diff --git a/extension/background/service-worker.js b/extension/background/service-worker.js index 708c7db..09e0ef4 100644 --- a/extension/background/service-worker.js +++ b/extension/background/service-worker.js @@ -75,6 +75,22 @@ chrome.runtime.onInstalled.addListener(async () => { updateBaseUrls("https://margin.at"); } + await ensureContextMenus(); + + if (hasSidebarAction) { + try { + await browser.sidebarAction.close(); + } catch { + /* ignore */ + } + } +}); + +chrome.runtime.onStartup.addListener(async () => { + await ensureContextMenus(); +}); + +async function ensureContextMenus() { await chrome.contextMenus.removeAll(); chrome.contextMenus.create({ @@ -100,15 +116,7 @@ chrome.runtime.onInstalled.addListener(async () => { title: "Open Margin Sidebar", contexts: ["page", "selection", "link"], }); - - if (hasSidebarAction) { - try { - await browser.sidebarAction.close(); - } catch { - /* ignore */ - } - } -}); +} chrome.action.onClicked.addListener(async () => { const stored = await chrome.storage.local.get(["apiUrl"]); -- 2.51.2 From b43f78fc0c646db7077f65b83bdf3d0ea9d00d1a Mon Sep 17 00:00:00 2001 From: Isaac Corbrey Date: Wed, 21 Jan 2026 10:17:00 -0500 Subject: [PATCH 2/3] 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. --- extension/background/service-worker.js | 115 +++++++++++-------------- extension/content/content.js | 14 +++ 2 files changed, 66 insertions(+), 63 deletions(-) diff --git a/extension/background/service-worker.js b/extension/background/service-worker.js index 09e0ef4..4951a87 100644 --- a/extension/background/service-worker.js +++ b/extension/background/service-worker.js @@ -39,6 +39,38 @@ function showNotification(title, message) { } } +async function createHighlight(payload) { + if (!API_BASE) { + throw new Error("API URL not configured"); + } + + const cookie = await chrome.cookies.get({ + url: API_BASE, + name: "margin_session", + }); + + if (!cookie) { + throw new Error("Not authenticated"); + } + + const res = await fetch(`${API_BASE}/api/highlights`, { + method: "POST", + headers: { + "Content-Type": "application/json", + "X-Session-Token": cookie.value, + }, + credentials: "include", + body: JSON.stringify(payload), + }); + + if (!res.ok) { + const errText = await res.text(); + throw new Error(`Failed to create highlight: ${res.status} ${errText}`); + } + + return res.json(); +} + async function openAnnotationUI(tabId, windowId) { if (hasSidePanel) { try { @@ -218,50 +250,35 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { type: "GET_SELECTOR_FOR_HIGHLIGHT", selectionText: info.selectionText, }); + if (response?.selector) { + selector = response.selector; + } if (response && response.success) return; } catch { /* ignore */ } - if (info.selectionText) { + if (!selector && info.selectionText) { selector = { type: "TextQuoteSelector", exact: info.selectionText, }; + } + if (selector) { try { - const cookie = await chrome.cookies.get({ - url: API_BASE, - name: "margin_session", + await createHighlight({ + url: tab.url, + title: tab.title, + selector: selector, }); - - if (!cookie) { + showNotification("Margin", "Text highlighted!"); + } catch (err) { + console.error("Highlight API error:", err); + if (err?.message === "Not authenticated") { showNotification("Margin", "Please sign in to create highlights"); return; } - - const res = await fetch(`${API_BASE}/api/highlights`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - credentials: "include", - body: JSON.stringify({ - url: tab.url, - title: tab.title, - selector: selector, - }), - }); - - if (res.ok) { - showNotification("Margin", "Text highlighted!"); - } else { - const errText = await res.text(); - console.error("Highlight API error:", res.status, errText); - showNotification("Margin", "Failed to create highlight"); - } - } catch (err) { - console.error("Highlight API error:", err); showNotification("Margin", "Error creating highlight"); } } else { @@ -490,45 +507,17 @@ async function handleMessage(request, sender, sendResponse) { } case "CREATE_HIGHLIGHT": { - if (!API_BASE) { - sendResponse({ success: false, error: "API URL not configured" }); - return; - } - - const cookie = await chrome.cookies.get({ - url: API_BASE, - name: "margin_session", - }); - - if (!cookie) { - sendResponse({ success: false, error: "Not authenticated" }); - return; - } - - const highlightRes = await fetch(`${API_BASE}/api/highlights`, { - method: "POST", - credentials: "include", - headers: { - "Content-Type": "application/json", - "X-Session-Token": cookie.value, - }, - body: JSON.stringify({ + try { + const highlightData = await createHighlight({ url: request.data.url, title: request.data.title, selector: request.data.selector, color: request.data.color || "yellow", - }), - }); - - if (!highlightRes.ok) { - const errorText = await highlightRes.text(); - throw new Error( - `Failed to create highlight: ${highlightRes.status} ${errorText}`, - ); + }); + sendResponse({ success: true, data: highlightData }); + } catch (error) { + sendResponse({ success: false, error: error.message }); } - - const highlightData = await highlightRes.json(); - sendResponse({ success: true, data: highlightData }); break; } diff --git a/extension/content/content.js b/extension/content/content.js index 2848951..94bae60 100644 --- a/extension/content/content.js +++ b/extension/content/content.js @@ -972,6 +972,20 @@ return true; } + if (request.type === "GET_SELECTOR_FOR_HIGHLIGHT") { + const sel = window.getSelection(); + if (!sel || !sel.toString().trim()) { + sendResponse({ success: false, selector: null }); + return true; + } + const exact = sel.toString().trim(); + sendResponse({ + success: false, + selector: { type: "TextQuoteSelector", exact }, + }); + return true; + } + if (request.type === "UPDATE_OVERLAY_VISIBILITY") { if (sidebarHost) { sidebarHost.style.display = request.show ? "block" : "none"; -- 2.51.2 From bab7db3955ec2800bb95bec72bab8e45bfecfa70 Mon Sep 17 00:00:00 2001 From: Isaac Corbrey Date: Wed, 21 Jan 2026 10:17:37 -0500 Subject: [PATCH 3/3] firefox: Refresh highlights after create Fixes highlighted text not being underlined after a highlight is created until the page is refreshed. --- extension/background/service-worker.js | 9 +++++++++ extension/content/content.js | 6 ++++++ 2 files changed, 15 insertions(+) diff --git a/extension/background/service-worker.js b/extension/background/service-worker.js index 4951a87..bf6fae3 100644 --- a/extension/background/service-worker.js +++ b/extension/background/service-worker.js @@ -71,6 +71,13 @@ async function createHighlight(payload) { return res.json(); } +function refreshTabAnnotations(tabId) { + if (!tabId) return; + chrome.tabs.sendMessage(tabId, { type: "REFRESH_ANNOTATIONS" }).catch(() => { + /* ignore missing content script */ + }); +} + async function openAnnotationUI(tabId, windowId) { if (hasSidePanel) { try { @@ -273,6 +280,7 @@ chrome.contextMenus.onClicked.addListener(async (info, tab) => { selector: selector, }); showNotification("Margin", "Text highlighted!"); + refreshTabAnnotations(tab.id); } catch (err) { console.error("Highlight API error:", err); if (err?.message === "Not authenticated") { @@ -515,6 +523,7 @@ async function handleMessage(request, sender, sendResponse) { color: request.data.color || "yellow", }); sendResponse({ success: true, data: highlightData }); + refreshTabAnnotations(sender.tab?.id); } catch (error) { sendResponse({ success: false, error: error.message }); } diff --git a/extension/content/content.js b/extension/content/content.js index 94bae60..c7545f2 100644 --- a/extension/content/content.js +++ b/extension/content/content.js @@ -986,6 +986,12 @@ return true; } + if (request.type === "REFRESH_ANNOTATIONS") { + fetchAnnotations(); + sendResponse({ success: true }); + return true; + } + if (request.type === "UPDATE_OVERLAY_VISIBILITY") { if (sidebarHost) { sidebarHost.style.display = request.show ? "block" : "none";