Preference to hide context annotation rows (#5315)

hideContextAnnotationRows pref, accessible via View > Hide Non-Matching Annotations,
will hide non-matching context annotation rows.
Enabled by default.

Fixes zotero#5264
This commit is contained in:
abaevbog 2026-02-05 21:51:01 -08:00 committed by GitHub
parent bbb39719f0
commit bd8d56e384
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 74 additions and 2 deletions

View file

@ -161,6 +161,10 @@ var ItemTree = class ItemTree extends LibraryTree {
this._rowCache = {};
this.tree.invalidate();
}),
Zotero.Prefs.registerObserver('hideContextAnnotationRows', async () => {
await this.refresh();
this.tree.invalidate();
})
];
this._itemsPaneMessage = null;
@ -391,6 +395,10 @@ var ItemTree = class ItemTree extends LibraryTree {
// a sorted list of items for a given column configuration and restore items from that.
await this.sort([...addedItemIDs]);
// Update search results before collapse/expand of containers so that
// if hideContextAnnotationRows pref is true, child rows appear/disappear properly
this._searchItemIDs = newSearchItemIDs; // items matching the search
this._searchMode = newSearchMode;
// Toggle all open containers closed and open to refresh child items
//
// This could be avoided by making sure that items in notify() that aren't present are always
@ -406,8 +414,6 @@ var ItemTree = class ItemTree extends LibraryTree {
this._refreshRowMap();
this._searchMode = newSearchMode;
this._searchItemIDs = newSearchItemIDs; // items matching the search
this._rowCache = {};
if (!this.collectionTreeRow.isPublications()) {
@ -1742,6 +1748,10 @@ var ItemTree = class ItemTree extends LibraryTree {
if (item.isFileAttachment()) {
annotations = item.getAnnotations();
}
// Optionally, only keep annotation rows that match the search query
if (Zotero.Prefs.get("hideContextAnnotationRows") && this._searchMode) {
annotations = annotations.filter(annotation => this._searchItemIDs.has(annotation.id));
}
var newRows = [];
if (attachments.length && notes.length) {
newRows = notes.concat(attachments);
@ -2117,6 +2127,10 @@ var ItemTree = class ItemTree extends LibraryTree {
var item = this.getRow(index).ref;
if (item.isFileAttachment()) {
// Consider attachments with non-matching annotation rows as empty when pref is set
if (Zotero.Prefs.get("hideContextAnnotationRows") && this._searchMode) {
return !item.getAnnotations().some(annotation => this._searchItemIDs.has(annotation.id));
}
return item.numAnnotations() == 0;
}
if (!item.isRegularItem()) {

View file

@ -517,6 +517,12 @@ const ZoteroStandalone = new function () {
Zotero.Prefs.get('recursiveCollections')
);
// Hide context rows
this.updateMenuItemCheckmark(
'view-menuitem-hide-context-annotation-rows',
Zotero.Prefs.get('hideContextAnnotationRows')
);
this.onUpdateCustomMenus(event, 'view');
};
@ -651,6 +657,10 @@ const ZoteroStandalone = new function () {
case 'recursive-collections':
this.toggleBooleanPref('recursiveCollections');
break;
case 'hide-context-annotation-rows':
this.toggleBooleanPref('hideContextAnnotationRows');
break;
}
};

View file

@ -693,6 +693,12 @@
oncommand="ZoteroStandalone.onViewMenuItemClick(event)"
type="checkbox"
/>
<menuitem id="view-menuitem-hide-context-annotation-rows"
class="menu-type-library"
data-l10n-id="menu-view-hide-context-annotation-rows"
oncommand="ZoteroStandalone.onViewMenuItemClick(event)"
type="checkbox"
/>
<menuseparator/>
<menuitem id="show-tabs-menu"
data-l10n-id="menu-show-tabs-menu"

View file

@ -135,6 +135,9 @@ menu-view-columns-move-left =
.label = Move Column Left
menu-view-columns-move-right =
.label = Move Column Right
menu-view-hide-context-annotation-rows =
.label = Hide Non-Matching Annotations
menu-view-note-font-size =
.label = Note Font Size

View file

@ -28,6 +28,7 @@ pref("extensions.zotero.downloadAssociatedFiles",true);
pref("extensions.zotero.findPDFs.resolvers", '[]');
pref("extensions.zotero.reportTranslationFailure",true);
pref("extensions.zotero.automaticTags",true);
pref("extensions.zotero.hideContextAnnotationRows", true);
pref("extensions.zotero.fontSize", "1.00");
pref("extensions.zotero.layout", "standard");
pref("extensions.zotero.recursiveCollections", false);

View file

@ -58,6 +58,7 @@ describe("Zotero.ItemTree", function () {
quicksearch.value = "";
quicksearch.doCommand();
await itemsView._refreshPromise;
Zotero.Prefs.set("hideContextAnnotationRows", false);
});
describe("when issuing a Select All command", function () {
@ -177,6 +178,43 @@ describe("Zotero.ItemTree", function () {
await itemsView._refreshPromise;
assert.equal(quicksearch.value, "test");
});
it("should hide context annotation rows if hideContextAnnotationRows=true", async function () {
Zotero.Prefs.set("hideContextAnnotationRows", true);
let item = await createDataObject('item', { title: "Item" });
// Ensure that non-file attachments that cannot have annotations do not cause any issues
await Zotero.Attachments.linkFromURL({
url: 'https://example.com',
title: 'Example',
parentItemID: item.id
});
let attachmentOne = await importFileAttachment('test.pdf', { title: 'PDF', parentItemID: item.id });
let highlightOne = await createAnnotation('highlight', attachmentOne, { comment: "Highlight te" });
let underlineOne = await createAnnotation('underline', attachmentOne, { comment: "Underline testing" });
let attachmentTwo = await importFileAttachment('test.pdf', { title: 'PDF test', parentItemID: item.id });
let highlightTwo = await createAnnotation('highlight', attachmentTwo, { comment: "Highlight te" });
// "te" search - all rows are visible
await zp.itemsView.setFilter('search', "te");
assert.isNumber(itemsView.getRowIndexByID(attachmentOne.id));
assert.isNumber(itemsView.getRowIndexByID(highlightOne.id));
assert.isNumber(itemsView.getRowIndexByID(underlineOne.id));
assert.isNumber(itemsView.getRowIndexByID(attachmentTwo.id));
assert.isNumber(itemsView.getRowIndexByID(highlightTwo.id));
// "test" search - only annotations with "testing" remain
await zp.itemsView.setFilter('search', "test");
assert.isNumber(itemsView.getRowIndexByID(attachmentOne.id));
assert.isNumber(itemsView.getRowIndexByID(underlineOne.id));
assert.isFalse(itemsView.getRowIndexByID(highlightOne.id));
assert.isNumber(itemsView.getRowIndexByID(attachmentTwo.id));
assert.isFalse(itemsView.getRowIndexByID(highlightTwo.id));
});
});
describe("#selectItem()", function () {