Skip to content

Commit 0f62c5b

Browse files
committed
feat: send bibtex to JabRef via HTTP and Native Messaging as fallback
1 parent d3220c9 commit 0f62c5b

2 files changed

Lines changed: 82 additions & 95 deletions

File tree

background.js

Lines changed: 80 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -120,25 +120,99 @@ async function evalInTab(tabsId, code) {
120120
}
121121
}
122122

123+
function openErrorPage(message, details = "", stacktrace = "") {
124+
browser.tabs.create({
125+
url:
126+
"/data/error.html?message=" +
127+
encodeURIComponent(message) +
128+
"&details=" +
129+
encodeURIComponent(details ?? "") +
130+
"&stacktrace=" +
131+
encodeURIComponent(stacktrace ?? ""),
132+
});
133+
}
134+
135+
async function getBaseUrl() {
136+
const settings = await browser.storage.sync.get({ httpPort: 23119 });
137+
return `http://localhost:${settings.httpPort}/`;
138+
}
139+
140+
async function sendBibEntryHttp(bibtex) {
141+
const baseUrl = await getBaseUrl();
142+
143+
const health = await fetch(baseUrl, { method: "GET", cache: "no-store" });
144+
if (!(health.ok || health.status === 404)) {
145+
throw new Error(`JabRef HTTP endpoint unavailable (${health.status})`);
146+
}
147+
148+
const resp = await fetch(baseUrl + "libraries/current/entries", {
149+
method: "POST",
150+
headers: { "Content-Type": "application/x-bibtex" },
151+
body: bibtex,
152+
});
153+
154+
if (!resp.ok) {
155+
const body = await resp.text().catch(() => "");
156+
throw new Error(`HTTP ${resp.status}${body ? `: ${body}` : ""}`);
157+
}
158+
}
159+
160+
async function sendBibEntryNative(bibtex) {
161+
const response = await browser.runtime.sendNativeMessage("org.jabref.jabref", {
162+
text: bibtex,
163+
});
164+
if (response?.message === "ok") {
165+
return;
166+
}
167+
168+
if (response?.message === "error") {
169+
console.error(
170+
`JabRef: Error connecting to JabRef: '${response.output}' at '${response.stacktrace}'`,
171+
);
172+
handleError(response.output, "", response.stacktrace);
173+
}
174+
175+
console.error(
176+
`JabRef: Error connecting to JabRef: '${response.message}' with details '${response.output}' at '${response.stacktrace}'`,
177+
);
178+
handleError(response.message, response.output, response.stacktrace);
179+
}
180+
181+
async function sendBibTexToJabRef(bibtex) {
182+
await browser.runtime.sendMessage({ onSendToJabRef: "sendToJabRefStarted" });
183+
console.log("JabRef: Send BibTeX to JabRef: %o", bibtex);
184+
185+
try {
186+
await sendBibEntryHttp(bibtex);
187+
await browser.runtime.sendMessage({ popupClose: "close" });
188+
return;
189+
} catch (httpError) {
190+
console.warn("JabRef: HTTP send failed, falling back to native messaging", httpError);
191+
}
192+
193+
await sendBibEntryNative(bibtex);
194+
await browser.runtime.sendMessage({ popupClose: "close" });
195+
}
196+
123197
function saveAsWebpage(tab) {
124198
var title = tab.title;
125199
var url = tab.url;
126-
var date = new Date().toISODate();
200+
var date = new Date().toISOString();
127201

128202
// Construct a manual Bibtex Entry for the webpage
129203
var bibtexString = `@misc{,\
130204
title={${title}},\
131205
url = {${url}},\
132206
urlDate={${date}},\
133207
}`;
134-
Zotero.Connector.sendBibTexToJabRef(bibtexString);
208+
sendBibTexToJabRef(bibtexString);
135209
}
136210

137211
function savePdf(tab) {
138212
var title = tab.title.replace(".pdf", "");
139213
var url = tab.url;
140214
var urlEscaped = tab.url.replace(":", "\\:");
141-
var date = new Date().toISODate();
215+
var date = new Date().toISOString();
142216

143217
// Construct a manual Bibtex Entry for the PDF
144218
var bibtexString = `@misc{,\
@@ -147,7 +221,7 @@ function savePdf(tab) {
147221
url = {${url}},\
148222
urlDate={${date}},\
149223
}`;
150-
Zotero.Connector.sendBibTexToJabRef(bibtexString);
224+
sendBibTexToJabRef(bibtexString);
151225
}
152226

153227
/*
@@ -308,7 +382,7 @@ browser.runtime.onMessage.addListener(async function (message, sender, _sendResp
308382
} else if (message.type === "offscreenResult") {
309383
console.debug("JabRef: offscreenResult in background.js: %o", message);
310384
if (message.error) {
311-
await browser.runtime.sendMessage({ type: "offscreenResult", url, error: message.error });
385+
console.error("JabRef: Error in offscreen translator execution", message.error);
312386
return;
313387
}
314388
const { url, items } = message;
@@ -317,6 +391,7 @@ browser.runtime.onMessage.addListener(async function (message, sender, _sendResp
317391
await browser.runtime.sendMessage({ onConvertToBibtex: "convertStarted" });
318392
const bib = await exportItems(items, conversionMode);
319393
console.debug("JabRef: Exported BibTeX: %o", bib);
394+
await sendBibTexToJabRef(bib);
320395
} else if (message.eval) {
321396
console.debug("JabRef: eval in background.js: %o", JSON.parse(JSON.stringify(message.eval)));
322397
return evalInTab(sender.tab.id, message.eval);

data/progressPanel.js

Lines changed: 2 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,8 @@ if (typeof browser === "undefined" && typeof chrome !== "undefined") {
33
globalThis.browser = chrome;
44
}
55

6-
var mainList = document.getElementById("itemList");
7-
8-
var jabrefBaseUrlPromise = null;
9-
10-
browser.runtime.onMessage.addListener(function (message, sender, sendResponse) {
6+
browser.runtime.onMessage.addListener(function (message, _sender, _sendResponse) {
7+
console.debug("JabRef: Received message in popup:", message);
118
if (message.popupClose) {
129
// The popup should be closed
1310
setTimeout(function () {
@@ -96,20 +93,6 @@ async function onPopupOpened() {
9693
}
9794
}
9895

99-
// Listen for offscreen results
100-
browser.runtime.onMessage.addListener((msg) => {
101-
if (!msg || msg.type !== "offscreenResult") return;
102-
const error = msg.error;
103-
const items = msg.items;
104-
if (error) {
105-
appendLog(`Error: ${error}`);
106-
return;
107-
}
108-
appendLog(`Received result for ${msg.url}`);
109-
// Send to JabRef automatically
110-
sendBibEntry(items);
111-
});
112-
11396
function appendLog(text) {
11497
const log = document.getElementById("log");
11598
if (!log) return;
@@ -135,80 +118,9 @@ function appendLog(text) {
135118
log.scrollTop = log.scrollHeight;
136119
}
137120

138-
async function getBaseUrl() {
139-
const settings = await browser.storage.sync.get({ httpPort: 23119 });
140-
return `http://localhost:${settings.httpPort}/`;
141-
}
142-
143-
/* Try to connect to JabRef via HTTP */
144-
async function connectToJabRef() {
145-
const url = await getBaseUrl();
146-
appendLog(`Checking JabRef at ${url}...`, "info");
147-
try {
148-
// Try a simple GET to the base URL to detect availability.
149-
const resp = await fetch(url, { method: "GET", cache: "no-store" });
150-
if (resp && (resp.ok || resp.status === 404)) {
151-
appendLog("JabRef reachable (HTTP)", "success");
152-
return url;
153-
} else {
154-
appendLog(`JabRef responded with status ${resp.status}`, "warning");
155-
return null;
156-
}
157-
} catch (error) {
158-
appendLog(`Connection failed: ${error && error.message ? error.message : error}`, "error");
159-
console.error("HTTP connection error:", error);
160-
return null;
161-
}
162-
}
163-
164-
// Send BibTeX entry to JabRef
165-
async function sendBibEntry(bibEntry) {
166-
if (!bibEntry) {
167-
appendLog("BibTeX entry is empty", "error");
168-
return;
169-
}
170-
const url = (await jabrefBaseUrlPromise) + "libraries/current/entries";
171-
appendLog(`Sending BibTeX entry to JabRef at ${url}...`, "info");
172-
173-
if (!bibEntry.startsWith("@")) {
174-
appendLog("BibTeX entry does not start with '@'", "error");
175-
return;
176-
}
177-
178-
try {
179-
console.log("Sending to JabRef (HTTP POST):", bibEntry);
180-
181-
const resp = await fetch(url, {
182-
method: "POST",
183-
headers: { "Content-Type": "application/x-bibtex" },
184-
body: bibEntry,
185-
});
186-
187-
if (resp.ok) {
188-
appendLog("BibTeX entry sent successfully!", "success");
189-
appendLog(`Sent: ${bibEntry.substring(0, 50)}...`, "info");
190-
} else {
191-
let text;
192-
try {
193-
text = await resp.text();
194-
} catch (e) {
195-
text = String(e);
196-
}
197-
appendLog(`Failed to send (HTTP ${resp.status}): ${text}`, "error");
198-
console.error("HTTP send failed", resp.status, text);
199-
}
200-
} catch (error) {
201-
appendLog(`Failed to send: ${error && error.message ? error.message : error}`, "error");
202-
console.error("Send error:", error);
203-
}
204-
}
205-
206121
document.addEventListener("DOMContentLoaded", async () => {
207122
console.log("JabRef: Popup opened");
208123

209-
// Try to auto-connect to JabRef via HTTP when popup opens
210-
jabrefBaseUrlPromise = connectToJabRef();
211-
212124
// Run translators for the active tab
213125
onPopupOpened();
214126
});

0 commit comments

Comments
 (0)