From d2de8a19a591ccbcf9c4decfe13abcd72f8cdb6c Mon Sep 17 00:00:00 2001 From: David Whetstone Date: Sun, 21 Dec 2025 09:53:31 -0800 Subject: [PATCH] fix zotero://select on cold start --- .../content/zotero/ZoteroProtocolHandler.mjs | 8 ++++++ chrome/content/zotero/collectionTree.jsx | 4 +++ chrome/content/zotero/itemTree.jsx | 6 +++++ .../zotero/xpcom/commandLineHandler.js | 5 +--- chrome/content/zotero/xpcom/zotero.js | 27 +++++++++++++++++++ chrome/content/zotero/zoteroPane.js | 5 +++- 6 files changed, 50 insertions(+), 5 deletions(-) diff --git a/chrome/content/zotero/ZoteroProtocolHandler.mjs b/chrome/content/zotero/ZoteroProtocolHandler.mjs index 29ca5afd8d..c13f096db2 100644 --- a/chrome/content/zotero/ZoteroProtocolHandler.mjs +++ b/chrome/content/zotero/ZoteroProtocolHandler.mjs @@ -637,6 +637,14 @@ export function ZoteroProtocolHandler() { } win.Zotero_Tabs.select('zotero-pane'); + if (zp.collectionsView?.waitForLoad) { + await zp.collectionsView.waitForLoad(); + } + // Wait for itemTreeView to be ready with a 10 second timeout + await Promise.race([ + zp.collectionsView.itemTreeViewReady, + Zotero.Promise.delay(10000).then(() => Promise.reject(new Error('itemTreeView ready timeout'))) + ]); if (params.objectType == 'collection') { return zp.collectionsView.selectCollection(results[0].id); } diff --git a/chrome/content/zotero/collectionTree.jsx b/chrome/content/zotero/collectionTree.jsx index f3278f3933..223dd6cea4 100644 --- a/chrome/content/zotero/collectionTree.jsx +++ b/chrome/content/zotero/collectionTree.jsx @@ -67,6 +67,10 @@ var CollectionTree = class CollectionTree extends LibraryTree { this.itemTreeView = null; this.itemToSelect = null; + // Promise that resolves when itemTreeView is set and ready + this._itemTreeViewReadyDeferred = Zotero.Promise.defer(); + this.itemTreeViewReady = this._itemTreeViewReadyDeferred.promise; + this.type = 'collection'; this.name = "CollectionTree"; this.id = "collection-tree"; diff --git a/chrome/content/zotero/itemTree.jsx b/chrome/content/zotero/itemTree.jsx index 799d452213..4a5e2d95c1 100644 --- a/chrome/content/zotero/itemTree.jsx +++ b/chrome/content/zotero/itemTree.jsx @@ -169,6 +169,9 @@ var ItemTree = class ItemTree extends LibraryTree { if (this.collectionTreeRow) { this.collectionTreeRow.view.itemTreeView = this; + // Resolve the promise when itemTreeView is set + this.collectionTreeRow.view._itemTreeViewReadyDeferred.resolve(); + } this._itemTreeLoadingDeferred = Zotero.Promise.defer(); @@ -1192,6 +1195,9 @@ var ItemTree = class ItemTree extends LibraryTree { this.collectionTreeRow = collectionTreeRow; this.selection.selectEventsSuppressed = true; this.collectionTreeRow.view.itemTreeView = this; + // Resolve the promise when itemTreeView is set + this.collectionTreeRow.view._itemTreeViewReadyDeferred.resolve(); + // Ensures that an up to date this._columns is set this._getColumns(); diff --git a/chrome/content/zotero/xpcom/commandLineHandler.js b/chrome/content/zotero/xpcom/commandLineHandler.js index 767ed37f0b..c40d529469 100644 --- a/chrome/content/zotero/xpcom/commandLineHandler.js +++ b/chrome/content/zotero/xpcom/commandLineHandler.js @@ -31,18 +31,15 @@ Zotero.CommandLineIngester = { ingest: async function () { const { CommandLineOptions } = ChromeUtils.importESModule("chrome://zotero/content/modules/commandLineOptions.mjs"); - var mainWindow = Zotero.getMainWindow(); + var mainWindow = await Zotero.getMainWindowPromise(); var fileToOpen; // Handle zotero:// and file URIs var uri = CommandLineOptions.url; if (uri) { if (uri.schemeIs("zotero")) { - // Check for existing window and focus it - if (mainWindow) { mainWindow.focus(); mainWindow.ZoteroPane.loadURI(uri.spec); } - } // See below else if (uri.schemeIs("file")) { fileToOpen = OS.Path.fromFileURI(uri.spec); diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js index 7bbeb0a692..9032f7b077 100644 --- a/chrome/content/zotero/xpcom/zotero.js +++ b/chrome/content/zotero/xpcom/zotero.js @@ -90,6 +90,33 @@ const { CommandLineOptions } = ChromeUtils.importESModule("chrome://zotero/conte return windows; }; + /** Deferred promise for main window readiness */ + let _mainWindowDeferred = null; + + this.getMainWindowPromise = function () { + let win = this.getMainWindow(); + if (win && !win.closed) { + return Promise.resolve(win); + } + + // Create deferred if not exists + if (!_mainWindowDeferred) { + _mainWindowDeferred = Zotero.Promise.defer(); + } + return _mainWindowDeferred.promise; + }; + + /** + * Called when main window is ready to signal waiters + * @param {ChromeWindow} win - The main window + */ + this.mainWindowReady = function (win) { + if (_mainWindowDeferred) { + _mainWindowDeferred.resolve(win); + _mainWindowDeferred = null; + } + }; + this.getActiveZoteroPane = function () { var win = Services.wm.getMostRecentWindow("navigator:browser"); return win ? win.ZoteroPane : null; diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js index 54d8c11511..457c77d441 100644 --- a/chrome/content/zotero/zoteroPane.js +++ b/chrome/content/zotero/zoteroPane.js @@ -796,7 +796,10 @@ var ZoteroPane = new function () { this.unserializePersist(); this.updateLayout(); this.initContainers(); - + + // Signal that main window is ready to handle loadURI() and other operations + Zotero.mainWindowReady(window); + // Focus the quicksearch on pane open var searchBar = document.getElementById('zotero-tb-search'); setTimeout(function () {