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 } 40 } 41 42 async function openAnnotationUI(tabId, windowId) { 43 if (hasSidePanel) { 44 try { ··· 218 type: "GET_SELECTOR_FOR_HIGHLIGHT", 219 selectionText: info.selectionText, 220 }); 221 if (response && response.success) return; 222 } catch { 223 /* ignore */ 224 } 225 226 - if (info.selectionText) { 227 selector = { 228 type: "TextQuoteSelector", 229 exact: info.selectionText, 230 }; 231 232 try { 233 - const cookie = await chrome.cookies.get({ 234 - url: API_BASE, 235 - name: "margin_session", 236 }); 237 - 238 - if (!cookie) { 239 showNotification("Margin", "Please sign in to create highlights"); 240 return; 241 } 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 showNotification("Margin", "Error creating highlight"); 266 } 267 } else { ··· 490 } 491 492 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({ 516 url: request.data.url, 517 title: request.data.title, 518 selector: request.data.selector, 519 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 - ); 528 } 529 - 530 - const highlightData = await highlightRes.json(); 531 - sendResponse({ success: true, data: highlightData }); 532 break; 533 } 534
··· 39 } 40 } 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 async function openAnnotationUI(tabId, windowId) { 75 if (hasSidePanel) { 76 try { ··· 250 type: "GET_SELECTOR_FOR_HIGHLIGHT", 251 selectionText: info.selectionText, 252 }); 253 + if (response?.selector) { 254 + selector = response.selector; 255 + } 256 if (response && response.success) return; 257 } catch { 258 /* ignore */ 259 } 260 261 + if (!selector && info.selectionText) { 262 selector = { 263 type: "TextQuoteSelector", 264 exact: info.selectionText, 265 }; 266 + } 267 268 + if (selector) { 269 try { 270 + await createHighlight({ 271 + url: tab.url, 272 + title: tab.title, 273 + selector: selector, 274 }); 275 + showNotification("Margin", "Text highlighted!"); 276 + } catch (err) { 277 + console.error("Highlight API error:", err); 278 + if (err?.message === "Not authenticated") { 279 showNotification("Margin", "Please sign in to create highlights"); 280 return; 281 } 282 showNotification("Margin", "Error creating highlight"); 283 } 284 } else { ··· 507 } 508 509 case "CREATE_HIGHLIGHT": { 510 + try { 511 + const highlightData = await createHighlight({ 512 url: request.data.url, 513 title: request.data.title, 514 selector: request.data.selector, 515 color: request.data.color || "yellow", 516 + }); 517 + sendResponse({ success: true, data: highlightData }); 518 + } catch (error) { 519 + sendResponse({ success: false, error: error.message }); 520 } 521 break; 522 } 523
+14
extension/content/content.js
··· 972 return true; 973 } 974 975 if (request.type === "UPDATE_OVERLAY_VISIBILITY") { 976 if (sidebarHost) { 977 sidebarHost.style.display = request.show ? "block" : "none";
··· 972 return true; 973 } 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 === "UPDATE_OVERLAY_VISIBILITY") { 990 if (sidebarHost) { 991 sidebarHost.style.display = request.show ? "block" : "none";