Write on the margins of the internet. Powered by the AT Protocol.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #12 from icorbrey-contrib/icorbrey/firefox-highlights

Fix the highlight context menu action in Firefox

authored by

Scan and committed by
GitHub
013e2373 31ab2e92

+98 -72
+78 -72
extension/background/service-worker.js
··· 39 39 } 40 40 } 41 41 42 + async function createHighlight(payload) { 43 + if (!API_BASE) { 44 + throw new Error("API URL not configured"); 45 + } 46 + 47 + const cookie = await chrome.cookies.get({ 48 + url: API_BASE, 49 + name: "margin_session", 50 + }); 51 + 52 + if (!cookie) { 53 + throw new Error("Not authenticated"); 54 + } 55 + 56 + const res = await fetch(`${API_BASE}/api/highlights`, { 57 + method: "POST", 58 + headers: { 59 + "Content-Type": "application/json", 60 + "X-Session-Token": cookie.value, 61 + }, 62 + credentials: "include", 63 + body: JSON.stringify(payload), 64 + }); 65 + 66 + if (!res.ok) { 67 + const errText = await res.text(); 68 + throw new Error(`Failed to create highlight: ${res.status} ${errText}`); 69 + } 70 + 71 + return res.json(); 72 + } 73 + 74 + function refreshTabAnnotations(tabId) { 75 + if (!tabId) return; 76 + chrome.tabs.sendMessage(tabId, { type: "REFRESH_ANNOTATIONS" }).catch(() => { 77 + /* ignore missing content script */ 78 + }); 79 + } 80 + 42 81 async function openAnnotationUI(tabId, windowId) { 43 82 if (hasSidePanel) { 44 83 try { ··· 75 114 updateBaseUrls("https://margin.at"); 76 115 } 77 116 117 + await ensureContextMenus(); 118 + 119 + if (hasSidebarAction) { 120 + try { 121 + await browser.sidebarAction.close(); 122 + } catch { 123 + /* ignore */ 124 + } 125 + } 126 + }); 127 + 128 + chrome.runtime.onStartup.addListener(async () => { 129 + await ensureContextMenus(); 130 + }); 131 + 132 + async function ensureContextMenus() { 78 133 await chrome.contextMenus.removeAll(); 79 134 80 135 chrome.contextMenus.create({ ··· 100 155 title: "Open Margin Sidebar", 101 156 contexts: ["page", "selection", "link"], 102 157 }); 103 - 104 - if (hasSidebarAction) { 105 - try { 106 - await browser.sidebarAction.close(); 107 - } catch { 108 - /* ignore */ 109 - } 110 - } 111 - }); 158 + } 112 159 113 160 chrome.action.onClicked.addListener(async () => { 114 161 const stored = await chrome.storage.local.get(["apiUrl"]); ··· 210 257 type: "GET_SELECTOR_FOR_HIGHLIGHT", 211 258 selectionText: info.selectionText, 212 259 }); 260 + if (response?.selector) { 261 + selector = response.selector; 262 + } 213 263 if (response && response.success) return; 214 264 } catch { 215 265 /* ignore */ 216 266 } 217 267 218 - if (info.selectionText) { 268 + if (!selector && info.selectionText) { 219 269 selector = { 220 270 type: "TextQuoteSelector", 221 271 exact: info.selectionText, 222 272 }; 273 + } 223 274 275 + if (selector) { 224 276 try { 225 - const cookie = await chrome.cookies.get({ 226 - url: API_BASE, 227 - name: "margin_session", 277 + await createHighlight({ 278 + url: tab.url, 279 + title: tab.title, 280 + selector: selector, 228 281 }); 229 - 230 - if (!cookie) { 282 + showNotification("Margin", "Text highlighted!"); 283 + refreshTabAnnotations(tab.id); 284 + } catch (err) { 285 + console.error("Highlight API error:", err); 286 + if (err?.message === "Not authenticated") { 231 287 showNotification("Margin", "Please sign in to create highlights"); 232 288 return; 233 289 } 234 - 235 - const res = await fetch(`${API_BASE}/api/highlights`, { 236 - method: "POST", 237 - headers: { 238 - "Content-Type": "application/json", 239 - }, 240 - credentials: "include", 241 - body: JSON.stringify({ 242 - url: tab.url, 243 - title: tab.title, 244 - selector: selector, 245 - }), 246 - }); 247 - 248 - if (res.ok) { 249 - showNotification("Margin", "Text highlighted!"); 250 - } else { 251 - const errText = await res.text(); 252 - console.error("Highlight API error:", res.status, errText); 253 - showNotification("Margin", "Failed to create highlight"); 254 - } 255 - } catch (err) { 256 - console.error("Highlight API error:", err); 257 290 showNotification("Margin", "Error creating highlight"); 258 291 } 259 292 } else { ··· 482 515 } 483 516 484 517 case "CREATE_HIGHLIGHT": { 485 - if (!API_BASE) { 486 - sendResponse({ success: false, error: "API URL not configured" }); 487 - return; 488 - } 489 - 490 - const cookie = await chrome.cookies.get({ 491 - url: API_BASE, 492 - name: "margin_session", 493 - }); 494 - 495 - if (!cookie) { 496 - sendResponse({ success: false, error: "Not authenticated" }); 497 - return; 498 - } 499 - 500 - const highlightRes = await fetch(`${API_BASE}/api/highlights`, { 501 - method: "POST", 502 - credentials: "include", 503 - headers: { 504 - "Content-Type": "application/json", 505 - "X-Session-Token": cookie.value, 506 - }, 507 - body: JSON.stringify({ 518 + try { 519 + const highlightData = await createHighlight({ 508 520 url: request.data.url, 509 521 title: request.data.title, 510 522 selector: request.data.selector, 511 523 color: request.data.color || "yellow", 512 - }), 513 - }); 514 - 515 - if (!highlightRes.ok) { 516 - const errorText = await highlightRes.text(); 517 - throw new Error( 518 - `Failed to create highlight: ${highlightRes.status} ${errorText}`, 519 - ); 524 + }); 525 + sendResponse({ success: true, data: highlightData }); 526 + refreshTabAnnotations(sender.tab?.id); 527 + } catch (error) { 528 + sendResponse({ success: false, error: error.message }); 520 529 } 521 - 522 - const highlightData = await highlightRes.json(); 523 - sendResponse({ success: true, data: highlightData }); 524 530 break; 525 531 } 526 532
+20
extension/content/content.js
··· 972 972 return true; 973 973 } 974 974 975 + if (request.type === "GET_SELECTOR_FOR_HIGHLIGHT") { 976 + const sel = window.getSelection(); 977 + if (!sel || !sel.toString().trim()) { 978 + sendResponse({ success: false, selector: null }); 979 + return true; 980 + } 981 + const exact = sel.toString().trim(); 982 + sendResponse({ 983 + success: false, 984 + selector: { type: "TextQuoteSelector", exact }, 985 + }); 986 + return true; 987 + } 988 + 989 + if (request.type === "REFRESH_ANNOTATIONS") { 990 + fetchAnnotations(); 991 + sendResponse({ success: true }); 992 + return true; 993 + } 994 + 975 995 if (request.type === "UPDATE_OVERLAY_VISIBILITY") { 976 996 if (sidebarHost) { 977 997 sidebarHost.style.display = request.show ? "block" : "none";