From c06fdf9e0ade22aced253aa1a4ec720c65ea7fdb Mon Sep 17 00:00:00 2001 From: Bogdan Abaev Date: Mon, 27 Apr 2026 16:52:53 -0700 Subject: [PATCH] Add Go menu for better keyboard navigation Adds a top-level Go menu to jump to common UI elements without the mouse. Each entry refreshes lazily on popup show, and items reflect what's visible for the current tab. - Tabs Menu, Locate: available in any tab type. Locate expands the context pane first in reader/note tabs. - Library, Quick Search: library tab only. Display the shortcut configured by the respective pref. - Item-pane sections (Info, Abstract, Tags, etc.): listed dynamically for library, reader, and note tabs. Selecting one opens its collapsible section and focuses its header. Sections added later (built-in or via plugins) are picked up automatically. - Item Info submenu: in library/reader tabs with a regular item visible, display an additional menu with item's fields. --- chrome/content/zotero/contextPane.js | 9 ++ chrome/content/zotero/elements/abstractBox.js | 5 + chrome/content/zotero/elements/itemBox.js | 7 +- chrome/content/zotero/elements/itemPane.js | 11 ++ .../zotero/elements/itemPaneSection.js | 6 + chrome/content/zotero/elements/noteEditor.js | 19 ++- .../content/zotero/standalone/standalone.js | 135 +++++++++++++++- chrome/content/zotero/tabs.js | 12 ++ chrome/content/zotero/zoteroPane.xhtml | 38 ++++- chrome/locale/en-US/zotero/zotero.ftl | 9 ++ test/tests/zoteroPaneTest.js | 152 ++++++++++++++++++ 11 files changed, 395 insertions(+), 8 deletions(-) diff --git a/chrome/content/zotero/contextPane.js b/chrome/content/zotero/contextPane.js index d9ac85ba3f..3915ca91af 100644 --- a/chrome/content/zotero/contextPane.js +++ b/chrome/content/zotero/contextPane.js @@ -61,6 +61,15 @@ var ZoteroContextPane = new function () { } }); + Object.defineProperty(this, 'itemDetails', { + get: () => { + if (_contextPaneInner.mode === 'notes') { + return this.activeEditor?.querySelector('links-box'); + } + return document.getElementById('zotero-context-pane-item-deck').selectedPanel; + } + }); + this.focus = () => { return _contextPaneInner.handleFocus(); }; diff --git a/chrome/content/zotero/elements/abstractBox.js b/chrome/content/zotero/elements/abstractBox.js index 23779a065c..d0efa6633a 100644 --- a/chrome/content/zotero/elements/abstractBox.js +++ b/chrome/content/zotero/elements/abstractBox.js @@ -112,6 +112,11 @@ } } + focusSection() { + this.open = true; + this._abstractField?.focus({ focusVisible: true }); + } + render() { if (!this.item) return; if (this._isAlreadyRendered()) return; diff --git a/chrome/content/zotero/elements/itemBox.js b/chrome/content/zotero/elements/itemBox.js index 7016085b19..8feed7ab2b 100644 --- a/chrome/content/zotero/elements/itemBox.js +++ b/chrome/content/zotero/elements/itemBox.js @@ -2558,7 +2558,12 @@ } focusField(fieldName) { - this.querySelector(`editable-text[fieldname="${fieldName}"]`)?.focus(); + this.open = true; + if (fieldName === 'itemType') { + this.querySelector('#item-type-menu').focus({ focusVisible: true }); + return; + } + this.querySelector(`editable-text[fieldname="${fieldName}"]`).focus(); } _saveFieldFocus() { diff --git a/chrome/content/zotero/elements/itemPane.js b/chrome/content/zotero/elements/itemPane.js index 21aff91a44..8dc954002f 100644 --- a/chrome/content/zotero/elements/itemPane.js +++ b/chrome/content/zotero/elements/itemPane.js @@ -111,6 +111,17 @@ this.setAttribute("view-type", type); } + + get itemDetails() { + if (this.mode === 'note') { + return this._noteEditor?.querySelector('links-box'); + } + if (this.mode === 'item') { + return this._itemDetails; + } + return null; + } + get collapsed() { return isPaneCollapsed(this); } diff --git a/chrome/content/zotero/elements/itemPaneSection.js b/chrome/content/zotero/elements/itemPaneSection.js index 37d8aef37e..7549ba4d7e 100644 --- a/chrome/content/zotero/elements/itemPaneSection.js +++ b/chrome/content/zotero/elements/itemPaneSection.js @@ -85,6 +85,12 @@ class ItemPaneSectionElementBase extends XULElementBase { } } + focusSection() { + if (!this._section) return; + this._section.open = true; + this._section._head?.focus({ focusVisible: true }); + } + get collapsible() { return this._section.collapsible; } diff --git a/chrome/content/zotero/elements/noteEditor.js b/chrome/content/zotero/elements/noteEditor.js index bafe54dffc..79b0e8d008 100644 --- a/chrome/content/zotero/elements/noteEditor.js +++ b/chrome/content/zotero/elements/noteEditor.js @@ -408,9 +408,9 @@