From ca2a184401ea89db56108bd97ad3285da7150792 Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Fri, 26 Jun 2026 22:08:56 +0200 Subject: [PATCH 1/4] Add workflow for maual tests --- .github/workflows/safari-signing-test.yml | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/safari-signing-test.yml diff --git a/.github/workflows/safari-signing-test.yml b/.github/workflows/safari-signing-test.yml new file mode 100644 index 00000000..b023cd59 --- /dev/null +++ b/.github/workflows/safari-signing-test.yml @@ -0,0 +1,56 @@ +name: Safari Signing Test + +on: + workflow_dispatch: + inputs: + run_safari_signing_test: + description: Run the Safari signing and publish step + type: boolean + required: true + default: true + +permissions: + contents: read + +jobs: + safari-publish: + name: Safari Publish + runs-on: macos-26 + if: ${{ inputs.run_safari_signing_test }} + steps: + - name: Checkout + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + submodules: true + + - name: Install pnpm + uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 + + - name: Setup Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: 24.16.0 + cache: "pnpm" + + - name: Install dependencies + run: pnpm install + + - name: Build Safari project + run: make safari + + - name: Upload Safari to App Store Connect + uses: rxliuli/safari-webext-publish-action@624701331ecbeb38464a09d3cac6d246b6efb006 + with: + project-path: "dist/safari" + bundle-identifier: "org.jabref.JabRef-Browser-Extension" + team-id: ${{ secrets.APPLE_TEAM_ID }} + app-signing-identity: ${{ secrets.SAFARI_APP_SIGNING_IDENTITY }} + installer-signing-identity: ${{ secrets.SAFARI_INSTALLER_SIGNING_IDENTITY }} + env: + APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + APPLE_MACOS_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_MACOS_PROVISIONING_PROFILE_BASE64 }} + APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64 }} + APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} + APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} From afa584cbaa98af2a04b9b8d739fe49e9f91f5566 Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Fri, 17 Jul 2026 23:28:35 +0200 Subject: [PATCH 2/4] Split diagnostics UI and logging changes --- src/entrypoints/background.ts | 55 +++++++++++++++++++- src/entrypoints/content/index.js | 17 +++++- src/entrypoints/options/index.html | 12 +++++ src/entrypoints/options/main.js | 83 ++++++++++++++++++++++++------ src/entrypoints/options/style.css | 29 +++++++++++ src/entrypoints/popup/main.js | 5 ++ 6 files changed, 184 insertions(+), 17 deletions(-) diff --git a/src/entrypoints/background.ts b/src/entrypoints/background.ts index fda1a49a..77de5e53 100644 --- a/src/entrypoints/background.ts +++ b/src/entrypoints/background.ts @@ -7,6 +7,7 @@ export default defineBackground({ type: "module", main() { var tabInfo = new Map(); + const BIB_EXPORT_TIMEOUT_MS = 5000; /* Show/hide import button for all tabs (when add-on is loaded). @@ -145,6 +146,9 @@ export default defineBackground({ async function sendBibEntryHttp(bibtex) { const baseUrl = await getBaseUrl(); + await browser.runtime.sendMessage({ + popupLog: `Trying JabRef HTTP endpoint at ${baseUrl}`, + }); const health = await fetch(baseUrl, { method: "GET", cache: "no-store" }); if (!(health.ok || health.status === 404)) { @@ -161,13 +165,23 @@ export default defineBackground({ const body = await resp.text().catch(() => ""); throw new Error(`HTTP ${resp.status}${body ? `: ${body}` : ""}`); } + + await browser.runtime.sendMessage({ + popupLog: "JabRef accepted data over HTTP", + }); } async function sendBibEntryNative(bibtex) { + await browser.runtime.sendMessage({ + popupLog: "Trying native messaging to reach JabRef", + }); const response = await browser.runtime.sendNativeMessage("org.jabref.jabref", { text: bibtex, }); if (response?.message === "ok") { + await browser.runtime.sendMessage({ + popupLog: "JabRef accepted data over native messaging", + }); return; } @@ -190,13 +204,22 @@ export default defineBackground({ try { await sendBibEntryHttp(bibtex); + await browser.runtime.sendMessage({ + popupLog: "Send to JabRef finished", + }); await browser.runtime.sendMessage({ popupClose: "close" }); return; } catch (httpError) { console.warn("JabRef: HTTP send failed, falling back to native messaging", httpError); + await browser.runtime.sendMessage({ + popupLog: `HTTP send failed, falling back to native messaging: ${httpError}`, + }); } await sendBibEntryNative(bibtex); + await browser.runtime.sendMessage({ + popupLog: "Send to JabRef finished", + }); await browser.runtime.sendMessage({ popupClose: "close" }); } @@ -306,6 +329,18 @@ export default defineBackground({ return cfg.exportMode || "bibtex"; } + async function raceWithTimeout(promise, timeoutMs, label) { + return Promise.race([ + promise, + new Promise((_, reject) => { + setTimeout( + () => reject(new Error(`${label} timed out after ${timeoutMs} ms`)), + timeoutMs, + ); + }), + ]); + } + async function prepareForExport(items) { const { takeSnapshots } = await browser.storage.sync.get({ takeSnapshots: false }); @@ -387,11 +422,24 @@ export default defineBackground({ return; } const { url, items } = message; + await browser.runtime.sendMessage({ + popupLog: `Translator returned ${items.length} item(s) for ${url}`, + }); const conversionMode = await getConversionMode(); await prepareForExport(items); await browser.runtime.sendMessage({ onConvertToBibtex: "convertStarted" }); - const bib = await exportItems(items, conversionMode); + await browser.runtime.sendMessage({ + popupLog: `Starting BibTeX export in background for ${items.length} item(s)`, + }); + const bib = await raceWithTimeout( + exportItems(items, conversionMode), + BIB_EXPORT_TIMEOUT_MS, + "BibTeX export", + ); console.debug("JabRef: Exported BibTeX: %o", bib); + await browser.runtime.sendMessage({ + popupLog: `BibTeX export finished using mode ${conversionMode}`, + }); await sendBibTexToJabRef(bib); } else if (message.eval) { console.debug( @@ -411,6 +459,11 @@ export default defineBackground({ } } catch (e) { console.error("JabRef: Error handling message in background.js", e); + try { + await browser.runtime.sendMessage({ + popupLog: `Background error: ${e instanceof Error ? e.message : String(e)}`, + }); + } catch {} throw e; } }); diff --git a/src/entrypoints/content/index.js b/src/entrypoints/content/index.js index fc21230c..8252dbbe 100644 --- a/src/entrypoints/content/index.js +++ b/src/entrypoints/content/index.js @@ -6,6 +6,11 @@ export default defineContentScript({ matches: [], async main() { + if (globalThis.__JABREF_CONTENT_SCRIPT_INITIALIZED__) { + console.debug("[contentScript] already initialized"); + return; + } + globalThis.__JABREF_CONTENT_SCRIPT_INITIALIZED__ = true; console.debug("[contentScript] started"); browser.runtime.onMessage.addListener(async (msg, _sender, _sendResponse) => { @@ -76,7 +81,17 @@ export default defineContentScript({ ); const result = await translateEngine.translate(document, translators); console.debug("Content script obtained translation result %o", result); - await browser.runtime.sendMessage({ type: "offscreenResult", url, items: result.items }); + console.debug( + "Content script sending offscreenResult with %o item(s) for %o", + result.items?.length ?? 0, + url, + ); + const response = await browser.runtime.sendMessage({ + type: "offscreenResult", + url, + items: result.items, + }); + console.debug("Content script received background ack for offscreenResult %o", response); }); }, }); diff --git a/src/entrypoints/options/index.html b/src/entrypoints/options/index.html index e5c3f361..4e2c68d2 100644 --- a/src/entrypoints/options/index.html +++ b/src/entrypoints/options/index.html @@ -86,6 +86,18 @@

Connection Status

Testing connection... +
+
+

Diagnostics

+
+
+ +
+
+ +
No test run yet.
+
+
diff --git a/src/entrypoints/options/main.js b/src/entrypoints/options/main.js index f31775ce..6818380d 100644 --- a/src/entrypoints/options/main.js +++ b/src/entrypoints/options/main.js @@ -6,6 +6,50 @@ var ExportMode = Object.freeze({ }); const DEFAULT_PORT = 23119; +const NATIVE_MESSAGE_TIMEOUT_MS = 5000; +const NATIVE_MESSAGE_TEST_LOG_TAG = "JBE_NATIVE_TEST"; + +function raceWithTimeout(promise, timeoutMs, label) { + return Promise.race([ + promise, + new Promise((_, reject) => { + setTimeout(() => reject(new Error(`${label} timed out after ${timeoutMs} ms`)), timeoutMs); + }), + ]); +} + +function formatError(error) { + if (error instanceof Error) { + return error.message; + } + return String(error); +} + +async function sendNativeValidation() { + const requestId = `${NATIVE_MESSAGE_TEST_LOG_TAG}-${Date.now()}`; + console.log(`${NATIVE_MESSAGE_TEST_LOG_TAG} sending requestId=${requestId}`); + return raceWithTimeout( + browser.runtime.sendNativeMessage("org.jabref.jabref", { + status: "validate", + requestId, + }), + NATIVE_MESSAGE_TIMEOUT_MS, + "Native messaging", + ); +} + +function renderNativeStatus(statusElement, response) { + if (response.message === "jarNotFound") { + statusElement.setAttribute("class", "alert-error"); + statusElement.textContent = `Unable to locate JabRef at: ${response.path}`; + } else if (response.message === "jarFound") { + statusElement.setAttribute("class", "alert-positive"); + statusElement.textContent = "Communication to JabRef successful!"; + } else { + statusElement.setAttribute("class", "alert-error"); + statusElement.textContent = `Unexpected response: ${response.message}`; + } +} async function connectToJabRef(port) { const base = `http://localhost:${port}/`; @@ -24,25 +68,14 @@ async function connectToJabRef(port) { function checkConnections({ httpPort }) { let status = document.getElementById("connectionStatusNative"); - browser.runtime - .sendNativeMessage("org.jabref.jabref", { - status: "validate", - }) + status.textContent = "Testing connection..."; + sendNativeValidation() .then((response) => { - if (response.message === "jarNotFound") { - status.setAttribute("class", "alert-error"); - status.textContent = "Unable to locate JabRef at:
" + response.path; - } else if (response.message === "jarFound") { - status.setAttribute("class", "alert-positive"); - status.textContent = "Communication to JabRef successful!"; - } else { - status.setAttribute("class", "alert-error"); - status.innerHTML = "Unexpected response:
" + response.message; - } + renderNativeStatus(status, response); }) .catch((error) => { status.setAttribute("class", "alert-error"); - status.textContent = error.message; + status.textContent = formatError(error); }); let httpStatus = document.getElementById("connectionStatusHttp"); @@ -71,6 +104,25 @@ function checkConnections({ httpPort }) { }); } +function initDiagnostics() { + const testButton = document.getElementById("testNativeMessage"); + const result = document.getElementById("nativeMessageResult"); + + testButton.addEventListener("click", async () => { + testButton.disabled = true; + result.textContent = "Running native messaging test..."; + + try { + const response = await sendNativeValidation(); + result.textContent = JSON.stringify(response, null, 2); + } catch (error) { + result.textContent = formatError(error); + } finally { + testButton.disabled = false; + } + }); +} + async function restoreOptions() { const options = await browser.storage.sync.get({ exportMode: ExportMode.BibTeX, @@ -126,6 +178,7 @@ function saveOptions() { async function init() { const options = await restoreOptions(); checkConnections(options); + initDiagnostics(); document.getElementById("exportBiblatex").addEventListener("change", () => saveOptions()); document.getElementById("exportBibtex").addEventListener("change", () => saveOptions()); diff --git a/src/entrypoints/options/style.css b/src/entrypoints/options/style.css index 34506f4f..c3f29445 100644 --- a/src/entrypoints/options/style.css +++ b/src/entrypoints/options/style.css @@ -6,6 +6,11 @@ body { min-width: 500px; } +button { + font: inherit; + padding: 4px 10px; +} + label { cursor: default; margin-top: 1px; @@ -38,6 +43,30 @@ label { text-align: center; } +.diagnostics-cell { + display: flex; + flex-direction: column; + gap: 8px; + align-items: flex-start; +} + +#nativeMessageResult { + margin: 0; + padding: 8px; + width: min(100%, 42rem); + min-height: 6rem; + white-space: pre-wrap; + overflow-wrap: anywhere; + background: #f5f5f7; + border: 1px solid #d7d7db; + text-shadow: none; + font: + 12px/1.4 ui-monospace, + SFMono-Regular, + Menlo, + monospace; +} + input[type="radio"] { vertical-align: middle; -moz-appearance: none; diff --git a/src/entrypoints/popup/main.js b/src/entrypoints/popup/main.js index 7ca813e4..a51334d8 100644 --- a/src/entrypoints/popup/main.js +++ b/src/entrypoints/popup/main.js @@ -2,6 +2,9 @@ import "./style.css"; browser.runtime.onMessage.addListener(function (message, _sender, _sendResponse) { console.debug("JabRef: Received message in popup:", message); + if (message.popupLog) { + appendLog(message.popupLog, "info"); + } if (message.popupClose) { // The popup should be closed setTimeout(function () { @@ -10,8 +13,10 @@ browser.runtime.onMessage.addListener(function (message, _sender, _sendResponse) console.log("JabRef: Popup closed"); } else if (message.onConvertToBibtex) { document.getElementById("status").innerHTML = "Converting to BibTeX..."; + appendLog("Translation done, converting item data to BibTeX", "info"); } else if (message.onSendToJabRef) { document.getElementById("status").innerHTML = "Sending to JabRef..."; + appendLog("BibTeX ready, sending data to JabRef", "info"); } }); From 72affc85e18a7bb78999529e7675cdeced75c522 Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Sat, 18 Jul 2026 11:40:24 +0200 Subject: [PATCH 3/4] remove unrelated workflow --- .github/workflows/safari-signing-test.yml | 56 ----------------------- 1 file changed, 56 deletions(-) delete mode 100644 .github/workflows/safari-signing-test.yml diff --git a/.github/workflows/safari-signing-test.yml b/.github/workflows/safari-signing-test.yml deleted file mode 100644 index b023cd59..00000000 --- a/.github/workflows/safari-signing-test.yml +++ /dev/null @@ -1,56 +0,0 @@ -name: Safari Signing Test - -on: - workflow_dispatch: - inputs: - run_safari_signing_test: - description: Run the Safari signing and publish step - type: boolean - required: true - default: true - -permissions: - contents: read - -jobs: - safari-publish: - name: Safari Publish - runs-on: macos-26 - if: ${{ inputs.run_safari_signing_test }} - steps: - - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - submodules: true - - - name: Install pnpm - uses: pnpm/action-setup@0e279bb959325dab635dd2c09392533439d90093 # v6.0.8 - - - name: Setup Node.js - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - with: - node-version: 24.16.0 - cache: "pnpm" - - - name: Install dependencies - run: pnpm install - - - name: Build Safari project - run: make safari - - - name: Upload Safari to App Store Connect - uses: rxliuli/safari-webext-publish-action@624701331ecbeb38464a09d3cac6d246b6efb006 - with: - project-path: "dist/safari" - bundle-identifier: "org.jabref.JabRef-Browser-Extension" - team-id: ${{ secrets.APPLE_TEAM_ID }} - app-signing-identity: ${{ secrets.SAFARI_APP_SIGNING_IDENTITY }} - installer-signing-identity: ${{ secrets.SAFARI_INSTALLER_SIGNING_IDENTITY }} - env: - APPLE_CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE_BASE64 }} - APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - APPLE_MACOS_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_MACOS_PROVISIONING_PROFILE_BASE64 }} - APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64: ${{ secrets.APPLE_MACOS_EXTENSION_PROVISIONING_PROFILE_BASE64 }} - APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }} - APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} - APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }} From 96ca86f0eceeec59cca26ab55c9d4f2fadd7e0fa Mon Sep 17 00:00:00 2001 From: Siedlerchr Date: Tue, 11 Aug 2026 22:22:19 +0200 Subject: [PATCH 4/4] update namings --- src/entrypoints/options/main.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/entrypoints/options/main.js b/src/entrypoints/options/main.js index 6818380d..ef764a96 100644 --- a/src/entrypoints/options/main.js +++ b/src/entrypoints/options/main.js @@ -104,21 +104,21 @@ function checkConnections({ httpPort }) { }); } -function initDiagnostics() { - const testButton = document.getElementById("testNativeMessage"); - const result = document.getElementById("nativeMessageResult"); +function initializeNativeMessagingDiagnosticButton() { + const diagnosticButton = document.getElementById("testNativeMessage"); + const diagnosticResult = document.getElementById("nativeMessageResult"); - testButton.addEventListener("click", async () => { - testButton.disabled = true; - result.textContent = "Running native messaging test..."; + diagnosticButton.addEventListener("click", async () => { + diagnosticButton.disabled = true; + diagnosticResult.textContent = "Running native messaging diagnostic..."; try { const response = await sendNativeValidation(); - result.textContent = JSON.stringify(response, null, 2); + diagnosticResult.textContent = JSON.stringify(response, null, 2); } catch (error) { - result.textContent = formatError(error); + diagnosticResult.textContent = formatError(error); } finally { - testButton.disabled = false; + diagnosticButton.disabled = false; } }); } @@ -178,7 +178,7 @@ function saveOptions() { async function init() { const options = await restoreOptions(); checkConnections(options); - initDiagnostics(); + initializeNativeMessagingDiagnosticButton(); document.getElementById("exportBiblatex").addEventListener("change", () => saveOptions()); document.getElementById("exportBibtex").addEventListener("change", () => saveOptions());