Write on the margins of the internet. Powered by the AT Protocol. margin.at
extension web atproto comments

firefox: Fix highlight context menu

Fixes the highlight option in the context menu doing nothing in Firefox.
The call to the service worker wasn't being handled, and the API call
the extension would have made wasn't being authenticated.

+66 -63
+52 -63
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 + 42 74 async function openAnnotationUI(tabId, windowId) { 43 75 if (hasSidePanel) { 44 76 try { ··· 218 250 type: "GET_SELECTOR_FOR_HIGHLIGHT", 219 251 selectionText: info.selectionText, 220 252 }); 253 + if (response?.selector) { 254 + selector = response.selector; 255 + } 221 256 if (response && response.success) return; 222 257 } catch { 223 258 /* ignore */ 224 259 } 225 260 226 - if (info.selectionText) { 261 + if (!selector && info.selectionText) { 227 262 selector = { 228 263 type: "TextQuoteSelector", 229 264 exact: info.selectionText, 230 265 }; 266 + } 231 267 268 + if (selector) { 232 269 try { 233 - const cookie = await chrome.cookies.get({ 234 - url: API_BASE, 235 - name: "margin_session", 270 + await createHighlight({ 271 + url: tab.url, 272 + title: tab.title, 273 + selector: selector, 236 274 }); 237 - 238 - if (!cookie) { 275 + showNotification("Margin", "Text highlighted!"); 276 + } catch (err) { 277 + console.error("Highlight API error:", err); 278 + if (err?.message === "Not authenticated") { 239 279 showNotification("Margin", "Please sign in to create highlights"); 240 280 return; 241 281 } 242 - 243 - const res = await fetch(`${API_BASE}/api/highlights`, { 244 - method: "POST", 245 - headers: { 246 - "Content-Type": "application/json", 247 - }, 248 - credentials: "include", 249 - body: JSON.stringify({ 250 - url: tab.url, 251 - title: tab.title, 252 - selector: selector, 253 - }), 254 - }); 255 - 256 - if (res.ok) { 257 - showNotification("Margin", "Text highlighted!"); 258 - } else { 259 - const errText = await res.text(); 260 - console.error("Highlight API error:", res.status, errText); 261 - showNotification("Margin", "Failed to create highlight"); 262 - } 263 - } catch (err) { 264 - console.error("Highlight API error:", err); 265 282 showNotification("Margin", "Error creating highlight"); 266 283 } 267 284 } else { ··· 490 507 } 491 508 492 509 case "CREATE_HIGHLIGHT": { 493 - if (!API_BASE) { 494 - sendResponse({ success: false, error: "API URL not configured" }); 495 - return; 496 - } 497 - 498 - const cookie = await chrome.cookies.get({ 499 - url: API_BASE, 500 - name: "margin_session", 501 - }); 502 - 503 - if (!cookie) { 504 - sendResponse({ success: false, error: "Not authenticated" }); 505 - return; 506 - } 507 - 508 - const highlightRes = await fetch(`${API_BASE}/api/highlights`, { 509 - method: "POST", 510 - credentials: "include", 511 - headers: { 512 - "Content-Type": "application/json", 513 - "X-Session-Token": cookie.value, 514 - }, 515 - body: JSON.stringify({ 510 + try { 511 + const highlightData = await createHighlight({ 516 512 url: request.data.url, 517 513 title: request.data.title, 518 514 selector: request.data.selector, 519 515 color: request.data.color || "yellow", 520 - }), 521 - }); 522 - 523 - if (!highlightRes.ok) { 524 - const errorText = await highlightRes.text(); 525 - throw new Error( 526 - `Failed to create highlight: ${highlightRes.status} ${errorText}`, 527 - ); 516 + }); 517 + sendResponse({ success: true, data: highlightData }); 518 + } catch (error) { 519 + sendResponse({ success: false, error: error.message }); 528 520 } 529 - 530 - const highlightData = await highlightRes.json(); 531 - sendResponse({ success: true, data: highlightData }); 532 521 break; 533 522 } 534 523
+14
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 + 975 989 if (request.type === "UPDATE_OVERLAY_VISIBILITY") { 976 990 if (sidebarHost) { 977 991 sidebarHost.style.display = request.show ? "block" : "none";