Skip to content

Fix Zotero 8+ compatibility: file picker crash and ChromeUtils.import errors - #41

Merged
max3925vats merged 8 commits into
max3925vats:mainfrom
brianckeegan:main
Jun 11, 2026
Merged

Fix Zotero 8+ compatibility: file picker crash and ChromeUtils.import errors#41
max3925vats merged 8 commits into
max3925vats:mainfrom
brianckeegan:main

Conversation

@brianckeegan

Copy link
Copy Markdown
Contributor

Fixes two Zotero 8+/9 (Firefox 128/140) compatibility bugs, plus a README getting-started editing pass. One commit per concern. Branch: brianckeegan:claude/wizardly-bohr-hk6j7u, based on main at 43f910e (v0.3.1).

1. nsIFilePicker.init crash — BrowsingContext required

Fixes #39

Firefox 128+ (Zotero 8) changed nsIFilePicker.init() to take a BrowsingContext instead of a window, so the raw XPCOM file picker in promptSavePath() (src/modules/markdownZipExport.ts) threw NS_ERROR_XPC_BAD_CONVERT_JS on Zotero 8/9 — this is exactly the fp.init call at line 7021 of the built bundle named in the issue title.

Implemented the issue's recommended Option B: the dialog now goes through the toolkit's FilePickerHelper, which delegates to Zotero's own FilePicker module (chrome://zotero/content/modules/filePicker.mjs). Zotero's wrapper passes window.browsingContext on new platforms and still works on Zotero 7, so the plugin no longer touches the changing XPCOM signature. Behavior is preserved: same title/filter/default name, null on cancel, .zip extension forced.

2. ChromeUtils.import() removed — 16 console errors at startup

Fixes #40

The legacy import doesn't come from this plugin's code — it's in the bundled zotero-plugin-toolkit, and the bug is still present in the latest toolkit release (5.1.2), so upgrading the dependency can't fix it. The toolkit feature-detects the legacy loader with typeof ChromeUtils.import === "undefined"; on Firefox 140 ChromeUtils.import still exists but only throws "has been removed. Use importESModule()", so the toolkit rewrites Console.sys.mjs back to Console.jsm and the import throws once per helper constructed at startup (~16 errors).

Added an esbuild plugin in zotero-plugin.config.ts that patches the bundled toolkit's _importESModule to try ChromeUtils.importESModule first (available since Firefox 106, i.e. on every supported Zotero) with the legacy loader kept as a Zotero 7 fallback. The patch is an exact-match replacement that fails the build loudly if the toolkit source stops matching, so a future toolkit upgrade can't silently un-apply it — once the toolkit fixes its detection upstream, the patch block just gets deleted.

3. README: novice-friendly getting-started pass

  • New Quick start section spelling out the two-part setup (local docling-serve + plugin) end to end, with a heads-up about the first-run model download.
  • Install-guide links for uv/pipx, a browser-based sanity check (/ui), numbered plugin-install steps, a warning about Firefox auto-installing the .xpi, and the from-source path marked as development-oriented.
  • Fixed the "within seconds" promise that contradicted the documented 2–10 minute first-run download, and removed the emoji from the model-weights heading so section anchors are stable/linkable.

Verification

  • npm run build (esbuild bundle + tsc --noEmit) passes; the built bundle's _importESModule now prefers importESModule, and no raw nsIFilePicker usage remains in the bundle.
  • npm run lint:check (prettier + eslint) passes.
  • The scaffold's integration tests need a real Zotero binary, so they're left to CI.
  • Not run on a live Zotero 8/9 install — worth a smoke test of the .zip export dialog and a startup-console check before release.

https://claude.ai/code/session_01KUFXvTTsWqrPNDJ8Vi1oPL

claude and others added 6 commits June 10, 2026 02:26
Firefox 128+ (Zotero 8) changed nsIFilePicker.init() to take a
BrowsingContext instead of a window, so the raw nsIFilePicker code in
promptSavePath() threw NS_ERROR_XPC_BAD_CONVERT_JS on Zotero 8/9 and the
markdown .zip export never showed a save dialog.

Route the dialog through the toolkit's FilePickerHelper, which delegates
to Zotero's own FilePicker module (chrome://zotero/content/modules/
filePicker.mjs). Zotero's wrapper passes window.browsingContext on new
platforms and keeps working on Zotero 7, so the plugin no longer touches
the changing XPCOM signature directly.

Fixes max3925vats#39

https://claude.ai/code/session_01KUFXvTTsWqrPNDJ8Vi1oPL
…it patch

zotero-plugin-toolkit (through at least 5.1.2) detects the legacy JSM
loader with `typeof ChromeUtils.import === "undefined"`. On Zotero 8+
(Firefox 140) ChromeUtils.import still exists but only throws
"ChromeUtils.import() has been removed. Use importESModule()", so the
toolkit rewrote Console.sys.mjs back to Console.jsm and the import threw
once per helper constructed at startup — 16 console errors on every
launch.

Add an esbuild plugin that patches the bundled toolkit's _importESModule
to try ChromeUtils.importESModule first (available since Firefox 106, so
on every supported Zotero) and keep the legacy loader as a fallback for
Zotero 7. The patch is an exact-match string replacement that fails the
build loudly if the toolkit source changes, so a future toolkit upgrade
cannot silently un-apply it; once the toolkit fixes its feature
detection upstream, the plugin block can simply be deleted.

Fixes max3925vats#40

https://claude.ai/code/session_01KUFXvTTsWqrPNDJ8Vi1oPL
Getting-started editing pass aimed at first-time users:

- Add a Quick start section that spells out the two-part setup (local
  docling-serve + plugin) and walks the happy path end to end, with a
  heads-up about the first-run model download.
- Link installation guides for uv and pipx, and point readers without
  Python tooling at the container option.
- Add a browser-based sanity check (the /ui playground) alongside curl.
- Turn plugin installation into numbered steps, drop the stale "(when
  available)" hedge, and warn about Firefox auto-installing the .xpi.
- Mark the from-source path as development-oriented (Node 20+) and scope
  the macOS .env note to the npm start workflow.
- Fix the "within seconds" promise that contradicted the 2-10 minute
  first-run download, and de-emoji the model-weights heading so section
  anchors stay stable.

https://claude.ai/code/session_01KUFXvTTsWqrPNDJ8Vi1oPL
Fix Zotero 8+ compatibility: file picker crash and ChromeUtils.import errors
docling-serve defaults image_export_mode to "embedded", which inlines
every figure as a base64 data URI and can grow a paper's .md by tens of
megabytes. A new opt-in "Exclude images" checkbox in Settings → Output
sends image_export_mode=placeholder instead, so each image becomes a
tiny <!-- image --> comment and the markdown keeps only text and tables.

The plugin now always sends image_export_mode explicitly ("embedded"
when the box is unticked — identical to the previous server-default
behavior), and the advancedJson escape hatch still overrides it for
users who want "referenced" or other modes. Wired through prefs.js
defaults, generated pref typings, the preferences pane (+ en-US
strings), reset-to-defaults, buildConvertForm and its unit tests, and
the README feature list.

https://claude.ai/code/session_01KUFXvTTsWqrPNDJ8Vi1oPL
Add an Exclude images option for text-and-tables-only markdown
@brianckeegan

Copy link
Copy Markdown
Contributor Author

Add an Exclude images option for text-and-tables-only markdown docling-serve defaults image_export_mode to "embedded", which inlines every figure as a base64 data URI and can grow a paper's .md by tens of megabytes. A new opt-in "Exclude images" checkbox in Settings → Output sends image_export_mode=placeholder instead, so each image becomes a tiny comment and the markdown keeps only text and tables.

The plugin now always sends image_export_mode explicitly ("embedded" when the box is unticked — identical to the previous server-default behavior), and the advancedJson escape hatch still overrides it for users who want "referenced" or other modes. Wired through prefs.js defaults, generated pref typings, the preferences pane (+ en-US strings), reset-to-defaults, buildConvertForm and its unit tests, and the README feature list.

claude and others added 2 commits June 10, 2026 03:05
Markdown converted before the Exclude-images option (or with it off)
carries every figure as an embedded base64 data URI, so a single paper's
.md can be tens of megabytes. This adds a retroactive cleanup: select
items and right-click "Remove images from markdown" (or Tools → Docling:
Remove images from markdown…) to rewrite their .md attachments in place,
replacing each markdown image or raw <img> tag with the same
<!-- image --> placeholder docling emits in image_export_mode=placeholder.

The pure transform lives in utils/stripImages.ts: line-based so fenced
code blocks are skipped (image syntax shown in a code sample isn't an
image), inline-safe so images inside table cells keep the table shape,
and idempotent. The orchestrator in modules/removeImages.ts resolves
selections the same way the zip export does (md attachments directly,
PDFs via their matching .md sibling, parents via their PDF children),
guards the destructive rewrite behind a confirm dialog, refuses to run
while a conversion batch is in flight, and reports replaced-image count
and bytes saved (new formatBytes helper) in the completion toast.

Covered by unit tests for the transform (embedded/table/HTML/fence/
idempotence cases plus fast-check properties) and formatBytes.

https://claude.ai/code/session_01KUFXvTTsWqrPNDJ8Vi1oPL
Add a Remove-images utility for existing docling markdown
@max3925vats
max3925vats merged commit daf0775 into max3925vats:main Jun 11, 2026
3 checks passed
@github-actions

Copy link
Copy Markdown

🚀 This ticket has been resolved in v0.3.2. See Release v0.3.2 for release notes.

@github-actions github-actions Bot locked as resolved and limited conversation to collaborators Jul 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

3 participants