Skip to content

Commit 3d87164

Browse files
mjthoravalclaude
andcommitted
release: v0.8.5
Highlights since v0.8.4: - Reader "Related" popup redesigned to match the item-pane Related section: chain icon + grey "N Related" title, indented rows, inline plus.svg add button (replaces the chrome:// background-image approach that rendered blank in the reader window). - Zotero's native annotation preview popup now shows the related-items chain icon (next to the kebab) when the annotation has related items; click opens the relations popup, mirroring the sidebar. - zotero:// link handling: multi-select item resolution, and user-facing warnings when a linked item / collection is missing or points at an inaccessible group library; Trash and Recently-Read handled. - Context-menu cleanup: Weavero entries in the in-PDF annotation menu consolidated into one section (with annotation actions); item-pane annotation actions reworded ("Open in PDF in New Tab"); "Show Parent in Library" removed. - prefs.html copy tweaks. - typecheck: 0 errors; test suite: 56/56. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent f87f22a commit 3d87164

10 files changed

Lines changed: 1328 additions & 301 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "weavero",
3-
"version": "0.8.4",
3+
"version": "0.8.5",
44
"private": true,
55
"description": "Zotero 7+ plugin — clickable links in annotation comments + filter pane + tabs-menu overhaul.",
66
"homepage": "https://github.com/mjthoraval/Weavero",

src/index.ts

Lines changed: 326 additions & 35 deletions
Large diffs are not rendered by default.

src/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 2,
33
"name": "Weavero",
4-
"version": "0.8.4",
4+
"version": "0.8.5",
55
"description": "Adds clickable link buttons to Zotero annotation comments that contain URLs.",
66
"author": "mjthoraval",
77
"applications": {

src/modules/annotation.ts

Lines changed: 62 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,25 @@ class _AnnotationMixin {
336336
return svg;
337337
}
338338

339+
/** The "+" glyph from Zotero's chrome://zotero/skin/16/universal/plus.svg,
340+
* rebuilt inline with `fill="currentColor"` so it inherits the
341+
* surrounding CSS `color` — no chrome:// URL and no `context-fill`
342+
* keyword (that combination drew an invisible box in the reader-window
343+
* relations popup). Same path data as the icon Zotero shows on the
344+
* item pane's "Related" section add button, so the two match exactly. */
345+
_makePlusSvg(doc) {
346+
const NS = "http://www.w3.org/2000/svg";
347+
const svg = doc.createElementNS(NS, "svg");
348+
svg.setAttribute("class", "wv-plus-svg");
349+
svg.setAttribute("viewBox", "0 0 16 16");
350+
svg.setAttribute("aria-hidden", "true");
351+
const p = doc.createElementNS(NS, "path");
352+
p.setAttribute("fill", "currentColor");
353+
p.setAttribute("d", "M14 8H9V3H8V8H3V9H8V14H9V9H14V8Z");
354+
svg.appendChild(p);
355+
return svg;
356+
}
357+
339358
/** Stamp data-has-url on an icon element and (re)populate it with
340359
* the chain SVG when the comment has a URL. Markdown-only comments
341360
* no longer get a dedicated icon — markdown formatting is still
@@ -543,13 +562,13 @@ class _AnnotationMixin {
543562
* removes itself on `popuphidden`.
544563
*
545564
* Options listed (filtered by type at build time):
546-
* Annotation Open in Reader
547-
* Attachment Open in Reader / Open in New Window / Show File
548-
* Note Open Note
549-
* Regular Item Open Primary Attachment
565+
* Annotation Open in <PDF/EPUB/Snapshot> in New Tab / New Window
566+
* Attachment Open <Type> in New Tab / New Window / Show File
567+
* Note Open Note in New Tab / New Window
568+
* Regular Item Open <best attachment type> in New Tab / New Window / View Online
550569
* All Show in Library (unless opts.skipShowInLibrary),
551-
* Show Parent in Library (if has parent),
552-
* Copy Item Link
570+
* Copy Select Link, Copy Open Link (when one
571+
* applies for this item type), Add Related…
553572
*
554573
* `opts.skipShowInLibrary`: omit the "Show in Library" entry —
555574
* used by the right-pane annotation-row wiring where the user
@@ -589,8 +608,14 @@ class _AnnotationMixin {
589608
});
590609
popup.appendChild(mi);
591610
};
611+
// Add a separator — but never two in a row and never as the
612+
// first element (so groups that turn out empty don't leave a
613+
// dangling rule).
592614
const addSep = () => {
593-
popup.appendChild(doc.createXULElement("menuseparator"));
615+
const last = popup.lastElementChild as any;
616+
if (last && last.localName !== "menuseparator") {
617+
popup.appendChild(doc.createXULElement("menuseparator"));
618+
}
594619
};
595620

596621
const isAnnotation = !!(item.isAnnotation && item.isAnnotation());
@@ -742,8 +767,15 @@ class _AnnotationMixin {
742767
}
743768
} catch (e) {}
744769
if (parentID) {
745-
appendOpenPair(parentReaderType,
746-
readerTypeLabel(parentReaderType),
770+
// Phrase it as "Open in PDF in New Tab" — opening an
771+
// annotation means jumping to it *inside* its document,
772+
// not opening the doc as an object. (Unknown parent
773+
// type → "Open in Reader in New Tab".) The icon still
774+
// uses the parent's type so it reads as a PDF / EPUB /
775+
// snapshot.
776+
const tLabel = readerTypeLabel(parentReaderType);
777+
const openInLabel = "in " + (tLabel === "Attachment" ? "Reader" : tLabel);
778+
appendOpenPair(parentReaderType, openInLabel,
747779
(inWindow) => async () => {
748780
try {
749781
await Zotero.Reader.open(parentID,
@@ -845,61 +877,35 @@ class _AnnotationMixin {
845877

846878
addSep();
847879

848-
// ---- Universal options ----------------------------------------------
880+
// ---- Library navigation --------------------------------------------
849881
if (!opts.skipShowInLibrary) {
850882
append("Show in Library", () => this._navigateToItem(item),
851883
{ iconURL: ICON_LIBRARY });
852884
}
853-
// "Show Parent in Library" is meaningful only when the
854-
// parent is distinct from where "Show in Library" lands.
855-
// For annotations, `selectItem` already routes to the
856-
// parent attachment (annotations have no direct row in
857-
// the items tree), so this row would duplicate the one
858-
// above. Skip it for annotations.
859-
if (item.parentItemID && !isAnnotation) {
860-
append("Show Parent in Library", () => {
861-
try {
862-
const zp = win.ZoteroPane;
863-
if (zp && typeof zp.selectItem === "function") {
864-
zp.selectItem(item.parentItemID);
865-
}
866-
if (win.Zotero_Tabs && typeof win.Zotero_Tabs.select === "function") {
867-
win.Zotero_Tabs.select("zotero-pane");
868-
}
869-
win.focus();
870-
} catch (e) {
871-
Zotero.debug("[Weavero] show-parent err: " + e);
872-
}
873-
}, { iconURL: ICON_LIBRARY });
874-
}
875-
append("Copy Item Link", () => {
876-
try {
877-
const lib = item.libraryID;
878-
let prefix = "library";
879-
try {
880-
if (lib !== Zotero.Libraries.userLibraryID) {
881-
const gid = Zotero.Groups.getGroupIDFromLibraryID(lib);
882-
if (gid) prefix = "groups/" + gid;
883-
}
884-
} catch (e) {}
885-
const url = "zotero://select/" + prefix + "/items/" + item.key;
886-
Zotero.Utilities.Internal.copyTextToClipboard(url);
887-
} catch (e) {
888-
Zotero.debug("[Weavero] copy-link err: " + e);
889-
}
890-
// Plugin's needle icon — distinguishes a Weavero-provided
891-
// affordance ("copy a zotero:// URI for this item") from
892-
// the chain icons that mean "related items".
893-
}, { iconURL: this._menuItemIconURL });
894885

895886
addSep();
896887

888+
// ---- Copy links + Add Related (one group, separator above) ---------
889+
// The needle icon distinguishes the Weavero copy-link affordances
890+
// ("copy a zotero:// URI for this item") from the chain icon that
891+
// means "related items". "Copy Open Link" is only added when an
892+
// open link applies (this item is a file attachment, has a best
893+
// attachment, or — for an annotation — its parent attachment is
894+
// openable).
895+
append("Copy Select Link",
896+
() => this._copyItemLinks([item], "select"),
897+
{ iconURL: this._menuItemIconURL });
898+
if (this._buildOpenLink(item)) {
899+
append("Copy Open Link"
900+
+ (this._isExternalOpenTarget(item) ? " (external app)" : ""),
901+
() => this._copyItemLinks([item], "open"),
902+
{ iconURL: this._menuItemIconURL });
903+
}
897904
// "Add Related…" — opens Zotero's select-items dialog and adds
898-
// the chosen items as `dc:relation` peers of this one. Uses the
899-
// chain icon for visual consistency with the rest of the
900-
// related-item affordances (items-list `.wv-tree-rel-icon`,
901-
// sidebar `.wv-btn-relations`, PDF reader marker badge, the
902-
// "Add related item…" entry on the annotation context menu).
905+
// the chosen items as `dc:relation` peers of this one. Chain icon
906+
// for visual consistency with the rest of the related-item
907+
// affordances (items-list `.wv-tree-rel-icon`, sidebar
908+
// `.wv-btn-relations`, PDF reader marker badge).
903909
append("Add Related…", () => {
904910
try { this._addRelatedItemDialog([item]); }
905911
catch (e) {

src/modules/constants.ts

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,11 @@ export const SCHEME_SVG_TEMPLATE =
7979
// destroy-time pref cleanup).
8080

8181
export const MENU_LABEL_PREFIXES = [
82-
"Add Related", // covers both single ("Add Related…") and multi-select
83-
// ("Add Related… (N annotations)") label variants
82+
// Each prefix covers the single + multi-select label variants
83+
// ("Copy Select Link" / "Copy Select Links (N annotations)" etc.).
84+
"Add Related",
85+
"Copy Select Link",
86+
"Copy Open Link",
8487
];
8588

8689
export const PLUGIN_CSS = [
@@ -117,26 +120,69 @@ export const PLUGIN_CSS = [
117120
" width: 14px; height: 14px; display: block; flex-shrink: 0;",
118121
"}",
119122
":root.wv-ui-dark .wv-btn-relations { color: #ffb84d; }",
120-
// Relations popup — flat list of related items. Each row is a
121-
// type-icon + title that's clickable to navigate into the
122-
// library. No separator above the list (it's the only content
123-
// in this popup variant).
124-
".wv-relations-list { display: flex; flex-direction: column; gap: 2px; }",
123+
// Relations popup — laid out like the item pane's "Related"
124+
// section (scss/elements/_collapsibleSection.scss +
125+
// _notesBox.scss): a header [related icon] "<N> Related" + "+" add
126+
// button, then a 12px-indented list of type-icon-titled rows that
127+
// navigate into the library on click. Row titles WRAP (multi-line)
128+
// like the item pane's; header text + "+" use --fill-secondary
129+
// (Zotero's muted-grey UI colour); the "+" is the same 20px box /
130+
// 16px plus.svg as the item pane's section add button. No collapse
131+
// caret (it's a popup).
132+
".wv-relations-popup {",
133+
" padding: 5px 6px 6px; line-height: 1.4; white-space: normal;",
134+
"}",
135+
".wv-relations-header {",
136+
" display: flex; align-items: center; gap: 6px; padding: 3px 6px 5px;",
137+
" border-bottom: 1px solid rgba(127, 127, 127, 0.28);",
138+
" margin-bottom: 4px;",
139+
"}",
140+
".wv-relations-header-icon {",
141+
" width: 16px; height: 16px; flex-shrink: 0;",
142+
" -moz-context-properties: fill, fill-opacity, stroke, stroke-opacity;",
143+
" fill: currentColor; color: #7a4a00;",
144+
"}",
145+
":root.wv-ui-dark .wv-relations-header-icon { color: #ffb84d; }",
146+
// "<N> Related" — Zotero's section-header look: --fill-secondary
147+
// (a muted grey) at the inherited size, semibold.
148+
".wv-relations-header-title {",
149+
" flex: 1; min-width: 0; font-weight: 600;",
150+
" color: var(--fill-secondary, #5a5a5a);",
151+
" white-space: nowrap; overflow: hidden; text-overflow: ellipsis;",
152+
"}",
153+
":root.wv-ui-dark .wv-relations-header-title { color: var(--fill-secondary, #bdbdbd); }",
154+
// "+" — exactly the item-pane "Related" section add button: a 20px box
155+
// with 2px padding (16px icon area), border-radius 2px, --fill-secondary
156+
// tint. The glyph is an inline plus.svg with fill="currentColor" (see
157+
// _makePlusSvg) — the chrome:// background-image + context-fill approach
158+
// drew an invisible box in the reader-window popup (dev.34 regression).
159+
".wv-relations-add {",
160+
" flex-shrink: 0; cursor: pointer; user-select: none; box-sizing: border-box;",
161+
" width: 20px; height: 20px; padding: 2px; border-radius: 2px;",
162+
" color: var(--fill-secondary, #5a5a5a);",
163+
"}",
164+
".wv-relations-add svg { display: block; width: 100%; height: 100%; }",
165+
":root.wv-ui-dark .wv-relations-add { color: var(--fill-secondary, #bdbdbd); }",
166+
".wv-relations-add:hover { background-color: rgba(127, 127, 127, 0.2); }",
167+
".wv-relations-list {",
168+
" display: flex; flex-direction: column; gap: 2px;",
169+
" padding-inline-start: 12px;", // matches related-box .body's 12px indent
170+
"}",
125171
".wv-rel-row {",
126-
" display: flex; align-items: center; gap: 6px;",
127-
" padding: 4px 6px; border-radius: 3px;",
172+
" display: flex; align-items: flex-start; gap: 4px;",
173+
" padding: 3px 4px; border-radius: 3px;",
128174
" cursor: pointer; user-select: none;",
129175
"}",
130176
".wv-rel-row:hover { background: rgba(0, 0, 0, 0.07); }",
131177
":root.wv-ui-dark .wv-rel-row:hover { background: rgba(255, 255, 255, 0.08); }",
132178
".wv-rel-icon {",
133-
" width: 16px; height: 16px; flex-shrink: 0;",
179+
" width: 16px; height: 16px; flex-shrink: 0; margin-top: 1px;",
134180
" -moz-context-properties: fill, fill-opacity, stroke, stroke-opacity;",
135181
" fill: currentColor;",
136182
"}",
137183
".wv-rel-title {",
138184
" flex: 1; min-width: 0;",
139-
" white-space: nowrap; overflow: hidden; text-overflow: ellipsis;",
185+
" white-space: normal; overflow-wrap: anywhere; word-break: break-word;",
140186
"}",
141187
".wv-rel-empty {",
142188
" padding: 4px 6px; opacity: 0.6; font-style: italic;",

0 commit comments

Comments
 (0)