diff --git a/apply-patches.sh b/apply-patches.sh new file mode 100644 index 0000000..137d582 --- /dev/null +++ b/apply-patches.sh @@ -0,0 +1,15 @@ +#!/bin/bash +# Apply Zotero 8 compatibility patches to zotero-plugin-toolkit +# These patches fix deprecated ChromeUtils.import() calls + +TOOLKIT_DIR="node_modules/zotero-plugin-toolkit/dist" + +# Patch basic.js - fix Console.jsm import +sed -i.bak 's/if (typeof globalThis.ChromeUtils?.import !== "undefined") {/try {/' "$TOOLKIT_DIR/basic.js" +sed -i.bak 's/ChromeUtils.import("resource:\/\/gre\/modules\/Console.jsm")/ChromeUtils.importESModule("resource:\/\/gre\/modules\/Console.sys.mjs")/' "$TOOLKIT_DIR/basic.js" +sed -i.bak 's/});$/});\n } catch (e) {\n \/\/ Console not available\n }/' "$TOOLKIT_DIR/basic.js" + +# Patch pluginBridge.js - fix AddonManager.jsm import +sed -i.bak 's/ChromeUtils.import("resource:\/\/gre\/modules\/AddonManager.jsm")/ChromeUtils.importESModule("resource:\/\/gre\/modules\/AddonManager.sys.mjs")/' "$TOOLKIT_DIR/utils/pluginBridge.js" + +echo "Patches applied for Zotero 8 compatibility" diff --git a/bootstrap.ts b/bootstrap.ts index 41a9c0c..47d4ddb 100644 --- a/bootstrap.ts +++ b/bootstrap.ts @@ -1,30 +1,19 @@ -import { log } from './content/debug' - -export function install() { - log.info('installed') -} -export function uninstall() { - log.info('uninstalled') -} +export function install() {} +export function uninstall() {} let chromeHandle export async function startup({ id, version, rootURI }) { - log.info('startup', id, version) - const aomStartup = Components.classes['@mozilla.org/addons/addon-manager-startup;1'].getService(Components.interfaces.amIAddonManagerStartup) const manifestURI = Services.io.newURI(`${rootURI}manifest.json`) chromeHandle = aomStartup.registerChrome(manifestURI, [ ['content', 'zotero-folder-import', 'content/'], - ['locale', 'zotero-folder-import', 'en-US', 'locale/en-US/'], ]) Services.scriptloader.loadSubScript(`${rootURI}content/folder-import.js`, { rootURI, Zotero }) await Zotero.FolderImport.startup() - log.info('startup', id, version, 'ready') } export async function shutdown() { - log.info('shutdown') await Zotero.FolderImport.shutdown() if (chromeHandle) { chromeHandle.destruct() @@ -34,12 +23,9 @@ export async function shutdown() { } export function onMainWindowLoad({ window }) { - log.info('onMainWindowLoad') - window.MozXULElement.insertFTLIfNeeded('folder-import.ftl') Zotero.FolderImport?.onMainWindowLoad(window) } export function onMainWindowUnload({ window }) { - log.info('onMainWindowUnload') Zotero.FolderImport?.onMainWindowUnload(window) } diff --git a/content/debug.ts b/content/debug.ts index 23f9e83..fa32d67 100644 --- a/content/debug.ts +++ b/content/debug.ts @@ -1,3 +1,9 @@ -import { Logger } from 'zotero-plugin/logger' +// Simple logger using Zotero.debug directly (Zotero 8 compatible) +declare var Zotero: any -export const log = new Logger('folder-import') +export const log = { + info: (...args: any[]) => Zotero.debug(`[folder-import] INFO: ${args.join(' ')}`), + debug: (...args: any[]) => Zotero.debug(`[folder-import] DEBUG: ${args.join(' ')}`), + error: (...args: any[]) => Zotero.debug(`[folder-import] ERROR: ${args.join(' ')}`), + warn: (...args: any[]) => Zotero.debug(`[folder-import] WARN: ${args.join(' ')}`), +} diff --git a/content/folder-import.ts b/content/folder-import.ts index ed5aa85..270747d 100644 --- a/content/folder-import.ts +++ b/content/folder-import.ts @@ -1,16 +1,10 @@ declare var Zotero: any // eslint-disable-line no-var -declare const FileUtils: any declare const Services: any - -declare const Components: any -Components.utils.import('resource://gre/modules/FileUtils.jsm') - declare const ChromeUtils: any import { FilePickerHelper, ZoteroToolkit } from 'zotero-plugin-toolkit' const ztoolkit = new ZoteroToolkit() -import { DebugLog as DebugLogSender } from 'zotero-plugin/debug-log' import { log } from './debug' declare const OS: { @@ -156,10 +150,11 @@ class FolderScanner { export class $FolderImport { private status: { total: number; done: number } + private menuRegisteredID: string | null = null public async startup() { await Zotero.initializationPromise - DebugLogSender.register('Folder import', []) + log.info('startup') for (const win of Zotero.getMainWindows()) { if (win.ZoteroPane) this.onMainWindowLoad(win) } @@ -172,17 +167,34 @@ export class $FolderImport { } public onMainWindowLoad(win: Window) { - log.debug('onMainWindowLoad') - - ztoolkit.Menu.register('menuFile', { - tag: 'menuitem', - label: 'Add Files from Folder…', - oncommand: 'Zotero.FolderImport.addAttachmentsFromFolder()', + const doc = win.document + const fileMenu = doc.getElementById('menu_FilePopup') + if (!fileMenu) return + + const menuItem = doc.createXULElement('menuitem') + menuItem.id = 'folder-import-menuitem' + menuItem.setAttribute('label', 'Add Files from Folder…') + menuItem.addEventListener('command', () => { + Zotero.FolderImport.addAttachmentsFromFolder() }) + + const importFromClipboard = doc.getElementById('menu_import_clipboard') + if (importFromClipboard && importFromClipboard.nextSibling) { + fileMenu.insertBefore(menuItem, importFromClipboard.nextSibling) + } else { + const exportLibrary = doc.getElementById('menu_exportLibrary') + if (exportLibrary) { + fileMenu.insertBefore(menuItem, exportLibrary) + } else { + fileMenu.appendChild(menuItem) + } + } } public onMainWindowUnload(win: Window) { - ztoolkit.Menu.unregisterAll() + const doc = win.document + const menuItem = doc.getElementById('folder-import-menuitem') + if (menuItem) menuItem.remove() } public update() { @@ -204,7 +216,7 @@ export class $FolderImport { const duplicates: string = PathUtils.join(Zotero.getTempDirectory().path as string, `rmlint${Zotero.Utilities.randomString()}.json`) try { - const cmd = new FileUtils.File(rmlint) + const cmd = Zotero.File.pathToFile(rmlint) if (!cmd.isExecutable()) return [] const proc = Components.classes['@mozilla.org/process/util;1'].createInstance(Components.interfaces.nsIProcess) diff --git a/esbuild.js b/esbuild.js index e8d15e8..3047a94 100644 --- a/esbuild.js +++ b/esbuild.js @@ -8,8 +8,8 @@ const pretty = require('pretty') rmrf.sync('gen') require('zotero-plugin/copy-assets') -require('zotero-plugin/rdf') -require('zotero-plugin/version') +require('zotero-plugin/make-manifest') +require('zotero-plugin/make-version') async function bundle(entry) { const outdir = path.join('build', path.basename(path.dirname(entry))) diff --git a/package.json b/package.json index 9e0e6dd..6946bd7 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,9 @@ { "name": "zotero-folder-import", - "version": "0.0.9", + "version": "0.1.0", "description": "Import folder of attachments into a collection hierarchy", "scripts": { + "postinstall": "patch-package || true", "lint": "dprint fmt bootstrap.ts content/*.ts && dprint check bootstrap.ts content/*.ts", "prebuild": "npm run lint", "build": "rm -rf build && tsc --noEmit && node esbuild.js", @@ -25,15 +26,16 @@ }, "homepage": "https://github.com/retorquere/zotero-folder-import", "devDependencies": { + "dprint": "^0.50.0", + "esbuild": "^0.25.5", + "patch-package": "^8.0.0", "pretty": "^2.0.0", "pug": "^3.0.3", - "esbuild": "^0.25.5", "rimraf": "^6.0.1", "ts-node": "^10.9.2", "typescript": "^5.8.3", - "zotero-types": "^4.0.3", - "dprint": "^0.50.0", - "zotero-plugin": "5.0.18" + "zotero-plugin": "^8.0.8", + "zotero-types": "^4.0.5" }, "xpi": { "name": "Folder Import for Zotero", @@ -41,6 +43,6 @@ "releaseURL": "https://github.com/retorquere/zotero-folder-import/releases/download/release/" }, "dependencies": { - "zotero-plugin-toolkit": "^5.0.0" + "zotero-plugin-toolkit": "5.1.0-beta.13" } } diff --git a/patches/zotero-plugin-toolkit+5.1.0-beta.13.patch b/patches/zotero-plugin-toolkit+5.1.0-beta.13.patch new file mode 100644 index 0000000..ecec102 --- /dev/null +++ b/patches/zotero-plugin-toolkit+5.1.0-beta.13.patch @@ -0,0 +1,29 @@ +--- a/node_modules/zotero-plugin-toolkit/dist/basic.js ++++ b/node_modules/zotero-plugin-toolkit/dist/basic.js +@@ -52,11 +52,12 @@ + _mainWindow: undefined, + _plugin: undefined, + }; +- if (typeof globalThis.ChromeUtils?.import !== "undefined") { +- const { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm"); ++ try { ++ const { ConsoleAPI } = ChromeUtils.importESModule("resource://gre/modules/Console.sys.mjs"); + this._console = new ConsoleAPI({ + consoleID: `${this._basicOptions.api.pluginID}-${Date.now()}`, + }); ++ } catch (e) { ++ // Console not available + } + this.updateOptions(data); + } +--- a/node_modules/zotero-plugin-toolkit/dist/utils/pluginBridge.js ++++ b/node_modules/zotero-plugin-toolkit/dist/utils/pluginBridge.js +@@ -26,7 +26,7 @@ + } + } + initializePluginBridge() { +- const { AddonManager } = ChromeUtils.import("resource://gre/modules/AddonManager.jsm"); ++ const { AddonManager } = ChromeUtils.importESModule("resource://gre/modules/AddonManager.sys.mjs"); + const Zotero = BasicTool.getZotero(); + const pluginBridgeExtension = { + noContent: true, diff --git a/tsconfig.json b/tsconfig.json index d5cadaa..3b584be 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,8 @@ "importHelpers": true, "target": "es2017", "disableSizeLimit": true, - "module": "commonjs", + "module": "ESNext", + "moduleResolution": "bundler", "noImplicitAny": false, "removeComments": false, "preserveConstEnums": false, @@ -16,6 +17,7 @@ "types": [ "node" ], "typeRoots": [ "./node_modules/@types" ], "skipDefaultLibCheck": true, + "skipLibCheck": true, }, "include": [ "bootstrap.ts", "content/folder-import.ts", "content/globals.d.ts", "node_modules/zotero-types" ], "exclude": [