Releases: cannolis/ZotRead
Release list
v0.1.9
Fix: sidenav / section-header / prefs icon was rendering as a flat white blob because Zotero masks those slots and our favicon was a raster wrapped in SVG. Add a single-path vector section-icon.svg (Z-shape, currentColor) for those slots; the full-colour logo stays for the add-ons list.
v0.1.8
v0.1.7
v0.1.6
ZotRead v0.1.6 — dark mode + reliable pane auto-follow
Two issues fixed
1. Text invisible in dark mode
The WhyRead sidebar pane hardcoded color: #333 / #555 / #666 everywhere — readable on white, near-invisible on a dark theme. All hardcoded greys are gone; the pane now inherits the parent's theme color and uses opacity: 0.6/0.7/0.8/0.95 to differentiate emphasis levels. Works on both light and dark Zotero.
2. Auto-follow ZotRead pane when navigating items
Earlier versions tried to detect "is the user looking at ZotRead?" by calling getBoundingClientRect synchronously inside onItemChange. That doesn't work on Zotero 9 because Zotero resets the item-pane scroll before firing onItemChange — so the snapshot always saw "not visible" and the auto-follow never fired.
Replaced with an IntersectionObserver attached to the section element on onRender. The observer's last-reported state reflects the user's actual most-recent visibility, not the post-reset state. So:
- Open ZotRead and read it →
userPrefersZotRead = true→ next item navigation auto-scrolls back to ZotRead - Scroll away to Info / Abstract / Tags → observer reports
isIntersecting = false→ next navigation doesn't drag you back - Collapse ZotRead section header → also drops the flag, observer is gated on
section.open
Install
Same routine — download zot-read.xpi from this release, Tools → Add-ons → gear icon → Install Add-on From File….
v0.1.5
ZotRead v0.1.5 — fix prefs pane unreachable after v0.1.4
What was wrong
v0.1.3 fixed blank labels by switching from <groupbox onload> to a top-of-document <script>. v0.1.4 then added await document.readyState === "complete" to fix scope dropdown emptiness from the script-runs-during-parse race. Neither pattern is robust on Zotero 9 prefs panes — the latter ended up deadlocking because the prefs sub-frame's readyState may never transition to "complete", leaving the prefs pane completely unreachable.
Fix
Switched to the pattern used by windingwind/zotero-pdf-translate (proven on Zotero 7 and 9): root the prefs pane in a <vbox id="..." onload="...">. onload fires reliably on <vbox> (unlike <groupbox>) after the DOM is fully parsed, so DOM lookups in registerPrefsScripts find every widget on the first try, and FTL labels render before the script runs.
-<script>
- Zotero.__addonInstance__.hooks.onPrefsEvent("load", { window });
-</script>
-<groupbox>
+<vbox
+ id="zotero-prefpane-__addonRef__-root"
+ onload="Zotero.__addonInstance__.hooks.onPrefsEvent('load', { window })"
+>
+<groupbox>
...
</groupbox>
+</vbox>The DOM-ready wait in registerPrefsScripts is no longer needed and is removed.
Install
Same as before — download zot-read.xpi and re-install via Tools → Add-ons → gear icon → Install Add-on From File….
v0.1.4
ZotRead v0.1.4 — fix scope dropdown empty after v0.1.3 prefs hook change
What was wrong
v0.1.3 fixed prefs blank labels by switching the load-hook trigger from <groupbox onload> to a top-of-document <script> block. That worked — labels show — but introduced a new issue: the <script> runs synchronously while the DOM is still being parsed, so by the time registerPrefsScripts calls getElementById('zotero-prefpane-zotread-scope-picker'), the <menulist> element below the script hasn't been parsed yet. Result: scope dropdown rendered as a blank widget with no options.
Fix
registerPrefsScripts now waits for document.readyState === "complete" before any DOM lookup. One short await; matches what zotero-better-notes does in its prefs script.
Note on "API key auto-filled after install"
Re-installing the plugin does NOT reset settings — Zotero/Firefox preserves user_pref() values across plugin uninstall/reinstall. The API key you see was your own paste from a previous install (v0.1, v0.1.1, v0.1.2 or v0.1.3) on that machine, not anything bundled with the xpi. Verified:
prefs.jsinside the xpi hasapiKeyas empty string""- The entire build output and git history contain zero
sk-strings
To wipe a saved key: about:config → search extensions.zotero.zotread.llm.apiKey → right-click → Reset.
Install
Same as before — download zot-read.xpi and re-install via Tools → Add-ons → gear icon → Install Add-on From File….
v0.1.3
ZotRead v0.1.3 — really, actually, fix prefs blank labels
After v0.1.1 and v0.1.2 both failed to fix the blank-prefs-on-first-install issue (one made it worse, the other had no effect), I dug into how proven Zotero plugins like zotero-better-notes and zotero-pdf-translate structure their preferences.xhtml. The root cause was finally clear:
What was wrong
Our preferences.xhtml triggered the load hook via <groupbox onload="...">. This pattern stopped working reliably in Zotero 9's prefs framework — the onload event on the groupbox doesn't fire in time (or at all) on a fresh-profile open, so registerPrefsScripts never ran, FTL never got translated, and labels stayed blank.
Zotero-better-notes (the most-used Zotero plugin) uses a <script> block directly inside the prefs document to invoke the load hook synchronously when the document parses. Switched to that pattern.
What changed
addon/content/preferences.xhtml:
<linkset>
<html:link rel="localization" href="__addonRef__-preferences.ftl" />
</linkset>
-<groupbox
- onload="Zotero.__addonInstance__.hooks.onPrefsEvent('load', { window })"
->
+<script>
+ Zotero.__addonInstance__.hooks.onPrefsEvent("load", { window });
+</script>
+<groupbox>The v0.1.2 preheat (insertFTLIfNeeded for prefs FTL on main window load) is kept as a belt-and-suspenders measure but is no longer the primary mechanism.
Install
Same as before — download the new zot-read.xpi and re-install via Tools → Add-ons → gear icon → Install Add-on From File…. Settings preserved.
If v0.1.3 still shows blank labels on a fresh profile, please open Zotero's Tools → Developer → Run JavaScript and paste:
Zotero.debug("[ZotRead] manual debug: " + JSON.stringify(Object.keys(Zotero.ZotRead || {})));Then send me the output of /tmp/zotero-win.log — that lets me see whether the prefs hook fired at all.
v0.1.2
ZotRead v0.1.2 — really fix prefs blank on first install
v0.1.1 was supposed to fix the blank-prefs-on-first-install issue, but the fix made it worse: the prefs panel's labels stayed blank permanently, and clicking a dropdown no longer brought them back. v0.1.2 corrects that.
What was wrong in v0.1.1
MozXULElement.insertFTLIfNeeded was called on the prefs window itself. But preferences.xhtml already declares the FTL via <linkset rel="localization">, so the bundle ended up registered twice — and Firefox's l10n engine then refused to translate any of the elements at all.
The actual fix
- Main-window preheat (
src/hooks.ts) — when Zotero's main window loads, the plugin now also pre-registerspreferences.ftlso Firefox's l10n cache knows about it before the user ever opens Settings. - Post-open re-translate (
src/modules/preferenceScript.ts) — when the prefs pane opens, the plugin asksdocument.l10nto re-translate the DOM fragment. This is a no-op when everything is already wired up, but rescues the rare case where the bundle finished loading after first paint.
No double-registration. Tested on a fresh dev profile: prefs labels appear immediately.
Install
If you have v0.1 or v0.1.1 installed: download the new zot-read.xpi below and re-install via Tools → Add-ons → gear icon → Install Add-on From File… — your existing settings, anchors, and cache are preserved.
What changed
src/hooks.ts—onMainWindowLoadnow also pre-registerszotread-preferences.ftlviainsertFTLIfNeededsrc/modules/preferenceScript.ts—registerPrefsScriptscallsdocument.l10n.translateFragment(documentElement)instead ofinsertFTLIfNeededpackage.json— version 0.1.1 → 0.1.2
v0.1.1
ZotRead v0.1.1 — bug fixes
Patch release fixing two issues reported on a fresh-profile install.
Fixes
-
Preferences pane labels blank on first install — On a fresh profile, opening Edit → Settings → ZotRead the very first time would show empty rows (just controls, no labels) until the user clicked anything. The plugin now force-loads its FTL into the preferences window on entry, so labels resolve immediately.
-
Cryptic TypeError when API key contains non-ASCII characters — If a pasted API key had a stray full-width space, smart quote, or IME-leftover Chinese character, "Rescore" would fail with
TypeError: Headers.append: Cannot convert argument 2 to ByteString. The plugin now trims the key and validates it's pure ASCII before calling the LLM, with a clear error pointing at the offending character and position.
Install
If you already have v0.1 installed: download the new zot-read.xpi below and re-install it via Tools → Add-ons → gear icon → Install Add-on From File… — your existing settings, anchors, and cache are preserved.
Fresh install: same process, no v0.1 needed first.
What changed
src/services/llm.ts—readConfig()trims the API key;chat()validates ASCII before fetchsrc/modules/preferenceScript.ts—registerPrefsScripts()callsMozXULElement.insertFTLIfNeededforzotread-preferences.ftlon entrypackage.json— version 0.1.0 → 0.1.1;authorreverted to npm string format
v0.1
ZotRead v0.1
First public release.
What it does
ZotRead scores papers in your Zotero library by relevance to your own
published work or to a research-idea text, using any OpenAI-compatible LLM.
Features
- Two anchor modes — mark your own papers as anchors, or write a research-idea text and rank against it
- Sortable Relevance column in the main item list
- WhyRead sidebar pane showing the rationale, referencing concrete methods and findings from both papers
- Auto-maintained "ZotRead Top" collection containing the highest-scoring papers
- Bring your own model — DeepSeek (default), OpenAI, OpenRouter, local Ollama, or any OpenAI-compatible endpoint
- Local SQLite cache — re-evaluation is nearly free; English and Chinese rationales cached independently
- Cost-aware — explicit scope selection, preview dialog showing the number of API calls, cancellable mid-run
- Manual score override with the original LLM rationale preserved
Install
Download the zot-read.xpi asset below, then in Zotero: Tools → Add-ons → gear icon → Install Add-on From File… and select the .xpi.
Requirements
- Zotero 7 or newer
- An OpenAI-compatible LLM API key (DeepSeek by default; configurable)
Notes for first run
Before the first rescore, open Edit → Settings → ZotRead and:
- Paste your API key
- Pick a Ranking scope (a collection or "My library") — required, no default
- Either mark a paper as "my paper" (right-click → ZotRead → Mark as my paper) or add a research idea
- Click Rescore all now — a preview dialog will show the number of API calls before anything runs
🤖 Built with zotero-plugin-template