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 @@
-->
-
-
-
+
+
+
`, ['chrome://zotero/locale/zotero.dtd']);
}
@@ -433,6 +433,10 @@
this.destroy();
}
+ get item() {
+ return this._item;
+ }
+
set item(val) {
this._item = val;
this._id('related').item = this._item;
@@ -447,6 +451,15 @@
this.refresh();
}
+ // Mirror relevant methods from itemDetails so that Go menu generation treats LinksBox the same way
+ getEnabledPanes() {
+ return Array.from(this.querySelectorAll(':scope > [data-pane]:not([hidden])'));
+ }
+
+ getEnabledPane(id) {
+ return this.querySelector(`:scope > [data-pane="${CSS.escape(id)}"]:not([hidden])`);
+ }
+
set mode(val) {
this._mode = val;
this._id('related').editable = val == "edit";
diff --git a/chrome/content/zotero/standalone/standalone.js b/chrome/content/zotero/standalone/standalone.js
index e3cc1db434..2756c15b18 100644
--- a/chrome/content/zotero/standalone/standalone.js
+++ b/chrome/content/zotero/standalone/standalone.js
@@ -101,6 +101,10 @@ const ZoteroStandalone = new function () {
.setAttribute('key', Zotero.Keys.getKeyForCommand('copySelectedItemsToClipboard'));
document.getElementById('key_showTabsMenu')
.setAttribute('key', Zotero.Keys.getKeyForCommand('showTabsMenu'));
+ document.getElementById('key_library')
+ .setAttribute('key', Zotero.Keys.getKeyForCommand('library'));
+ document.getElementById('key_quicksearch')
+ .setAttribute('key', Zotero.Keys.getKeyForCommand('quicksearch'));
// Force menu to update with shortcut key at startup -- as of fx128, this is necessary
// to get the shortcut to reliably appear for the menu item without switching tabs
document.getElementById('show-tabs-menu').hidden = true;
@@ -390,6 +394,8 @@ const ZoteroStandalone = new function () {
this.onGoMenuOpen = function (event) {
+ if (event.target !== event.currentTarget) return;
+
var keyBack = document.getElementById('key_back');
var keyForward = document.getElementById('key_forward');
@@ -423,10 +429,135 @@ const ZoteroStandalone = new function () {
this.updateMenuItemEnabled('go-menuitem-forward', reader.canNavigateForward);
}
+ this._rebuildGoMenuSections(event.target);
+
this.onUpdateCustomMenus(event, 'go');
};
-
-
+
+ this._rebuildGoMenuSections = function (popup) {
+ let beforeSep = document.getElementById('go-menu-sep-sections-before');
+ let afterSep = document.getElementById('go-menu-sep-sections-after');
+
+ // Clear previously inserted dynamic entries
+ popup.querySelectorAll('.go-menu-section-dynamic').forEach(el => el.remove());
+
+ let itemDetails = Zotero_Tabs.currentItemPane?.itemDetails;
+ let hasSections = false;
+
+ for (let pane of (itemDetails?.getEnabledPanes() || [])) {
+ let paneID = pane.dataset.pane;
+ let section = pane.querySelector('collapsible-section');
+ if (!section) continue;
+
+ let label = Zotero.ftl.formatValueSync(`pane-${paneID}`)
+ || section.getAttribute('label')
+ || paneID;
+
+ let element;
+ if (paneID === 'info') {
+ element = document.createXULElement('menu');
+ element.setAttribute('label', label);
+ let submenu = document.createXULElement('menupopup');
+ element.appendChild(submenu);
+ this._populateItemInfoFields(submenu, itemDetails.item);
+ if (!submenu.hasChildNodes()) {
+ element.setAttribute('disabled', true);
+ }
+ }
+ else {
+ element = document.createXULElement('menuitem');
+ element.setAttribute('label', label);
+ element.addEventListener('command', () => {
+ Zotero_Tabs.currentItemPane.collapsed = false;
+ itemDetails.getEnabledPane(paneID).focusSection();
+ });
+ }
+ element.classList.add('go-menu-section-dynamic');
+ popup.insertBefore(element, afterSep);
+ hasSections = true;
+ }
+
+ // Only unhide separators the current tab type allows
+ let type = Zotero_Tabs.selectedType;
+ let beforeApplies = beforeSep.classList.contains(`menu-type-${type}`);
+ let afterApplies = afterSep.classList.contains(`menu-type-${type}`);
+ beforeSep.hidden = !(hasSections && beforeApplies);
+ afterSep.hidden = !(hasSections && afterApplies);
+ };
+
+
+ this._populateItemInfoFields = function (popup, item) {
+ let focusItemInfoField = function (fieldName) {
+ let pane = Zotero_Tabs.currentItemPane;
+ if (!pane) return;
+ pane.collapsed = false;
+ pane.itemDetails?.querySelector('info-box')?.focusField(fieldName);
+ };
+
+ let itemTypeMenuItem = document.createXULElement('menuitem');
+ itemTypeMenuItem.setAttribute('label', Zotero.getString('zotero.items.itemType'));
+ itemTypeMenuItem.addEventListener('command', () => focusItemInfoField('itemType'));
+ popup.appendChild(itemTypeMenuItem);
+
+ let fieldIDs = Zotero.ItemFields.getItemTypeFields(item.getField('itemTypeID'));
+ let titleFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(item.itemTypeID, 'title');
+ for (let fieldID of fieldIDs) {
+ let fieldName = Zotero.ItemFields.getName(fieldID);
+ // Skip abstract - it has its own section
+ if (fieldName === "abstractNote") continue;
+
+ let menuitem = document.createXULElement('menuitem');
+ menuitem.setAttribute('label', Zotero.ItemFields.getLocalizedString(fieldID));
+ menuitem.addEventListener('command', () => focusItemInfoField(fieldName));
+ popup.appendChild(menuitem);
+
+ if (fieldID === titleFieldID) {
+ let firstCreator = item.getCreators()[0];
+ let creatorTypeID;
+ if (firstCreator) {
+ creatorTypeID = firstCreator.creatorTypeID;
+ }
+ else if (item.library.editable
+ && Zotero.CreatorTypes.itemTypeHasCreators(item.itemTypeID)) {
+ creatorTypeID = Zotero.CreatorTypes.getPrimaryIDForType(item.itemTypeID);
+ }
+ if (creatorTypeID) {
+ let creatorMenuItem = document.createXULElement('menuitem');
+ creatorMenuItem.setAttribute('label',
+ Zotero.CreatorTypes.getLocalizedString(creatorTypeID));
+ creatorMenuItem.addEventListener('command', () => focusItemInfoField('creator-0-lastName'));
+ popup.appendChild(creatorMenuItem);
+ }
+ }
+ }
+ };
+
+
+ this.onGoMenuCommand = function (action) {
+ let target;
+ if (action === 'locate') {
+ let sidenavID = "zotero-view-item-sidenav";
+ // in non-library tabs the sidenav is hidden if context pane is collapsed
+ if (Zotero_Tabs.hasContextPane(Zotero_Tabs.selectedType)) {
+ ZoteroContextPane.collapsed = false;
+ sidenavID = "zotero-context-pane-sidenav";
+ }
+ target = document.querySelector(`#${sidenavID} toolbarbutton[data-action="locate"]`);
+ }
+ else {
+ let targetIDs = {
+ tabs: 'zotero-tb-tabs-menu',
+ library: 'collection-tree',
+ 'quick-search': 'zotero-tb-search-textbox',
+ };
+ target = document.getElementById(targetIDs[action]);
+ }
+ if (target) {
+ target.focus({ focusVisible: true });
+ }
+ };
+
+
this.onViewMenuOpen = function (event) {
// PDF Reader
var reader = Zotero.Reader.getByTabID(Zotero_Tabs.selectedID);
diff --git a/chrome/content/zotero/tabs.js b/chrome/content/zotero/tabs.js
index 43d1ad3b25..3166201ad5 100644
--- a/chrome/content/zotero/tabs.js
+++ b/chrome/content/zotero/tabs.js
@@ -92,6 +92,18 @@ var Zotero_Tabs = new function () {
return this._hasContextPaneTypes.includes(type);
};
+ Object.defineProperty(this, 'currentItemPane', {
+ get: () => {
+ if (this.selectedType === 'library') {
+ return ZoteroPane.itemPane;
+ }
+ if (this.hasContextPane(this.selectedType)) {
+ return ZoteroContextPane;
+ }
+ return null;
+ }
+ });
+
this.hasNoteContext = function (type) {
return this._hasNoteContextTypes.includes(type);
};
diff --git a/chrome/content/zotero/zoteroPane.xhtml b/chrome/content/zotero/zoteroPane.xhtml
index ba1049efab..9010490188 100644
--- a/chrome/content/zotero/zoteroPane.xhtml
+++ b/chrome/content/zotero/zoteroPane.xhtml
@@ -181,6 +181,10 @@
+
+