add: linkNote dialog

This commit is contained in:
windingwind 2024-04-06 11:23:24 +08:00
parent d8e29cedc8
commit 9e2b4c583d
26 changed files with 1237 additions and 501 deletions

View File

@ -0,0 +1,98 @@
<?xml version="1.0"?>
<!-- prettier-ignore -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<!-- prettier-ignore -->
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
<!-- prettier-ignore -->
<?xml-stylesheet href="chrome://zotero-platform/content/zotero.css" type="text/css"?>
<!-- prettier-ignore -->
<?xml-stylesheet href="chrome://__addonRef__/content/styles/toolbar.css" type="text/css"?>
<!-- prettier-ignore -->
<?xml-stylesheet href="chrome://__addonRef__/content/styles/linkNote.css" type="text/css"?>
<!-- prettier-ignore -->
<!DOCTYPE window>
<window
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
id="bn-note-picker"
windowtype="__addonRef__-note-picker"
persist="screenX screenY width height sizemode"
style="min-width: 40em"
>
<script src="chrome://zotero/content/include.js"></script>
<script src="chrome://zotero/content/customElements.js"></script>
<script src="chrome://__addonRef__/content/scripts/customElements.js"></script>
<script src="chrome://__addonRef__/content/scripts/linkNote.js"></script>
<dialog buttons="accept, cancel">
<hbox id="top-container" class="container">
<bn-note-picker></bn-note-picker>
<vbox id="bn-select-note-outline-container" class="container">
<hbox class="toolbar">
<hbox class="toolbar-start">
<html:span class="toolbar-header content"
>Step 2. Insert to:
</html:span>
<html:span
id="selected-outline-title"
class="toolbar-header highlight"
></html:span>
</hbox>
<hbox class="toolbar-middle"></hbox>
<hbox class="toolbar-end"></hbox>
</hbox>
<vbox
id="bn-select-note-outline-content"
class="container virtualized-table-container"
>
<html:div id="bn-select-note-outline-tree"></html:div>
</vbox>
<hbox id="bn-link-insert-position-container">
<label>At section</label>
<radiogroup id="bn-link-insert-position" orient="horizontal">
<radio
id="bn-link-insert-position-top"
label="Start"
value="start"
></radio>
<radio
id="bn-link-insert-position-bottom"
label="End"
value="end"
></radio>
</radiogroup>
</hbox>
</vbox>
<vbox id="bn-note-preview-container" class="container">
<hbox class="toolbar">
<hbox class="toolbar-start">
<html:span class="toolbar-header content"
>Step 3. Preview:
</html:span>
<html:span
id="preview-note-from-title"
class="toolbar-header highlight"
></html:span>
<html:span
id="preview-note-middle-title"
class="toolbar-header content"
></html:span>
<html:span
id="preview-note-to-title"
class="toolbar-header highlight"
></html:span>
</hbox>
<hbox class="toolbar-middle"></hbox>
<hbox class="toolbar-end"></hbox>
</hbox>
<vbox id="bn-note-preview-content" class="container">
<iframe
id="bn-note-preview"
class="container"
type="content"
></iframe>
</vbox>
</vbox>
</hbox>
</dialog>
</window>

View File

@ -0,0 +1,33 @@
.container {
height: 100%;
margin: 0;
}
#top-container {
gap: 16px;
overflow: auto;
width: 800px;
padding: 2em;
}
bn-note-picker {
border: var(--material-border);
min-width: 600px;
height: 500px;
}
#bn-select-note-outline-container {
border: var(--material-border);
min-width: 300px;
height: 500px;
}
#bn-note-preview-container {
border: var(--material-border);
min-width: 450px;
height: 500px;
}
#bn-link-insert-position-container {
align-items: center;
}

View File

@ -0,0 +1,40 @@
bn-note-picker {
flex-direction: column;
}
.container {
height: 100%;
margin: 0;
}
#select-items-dialog #zotero-select-items-container {
gap: 0;
}
#collections-items-container {
display: flex;
height: 100%;
border-bottom: var(--material-border);
user-select: none;
}
#zotero-collections-tree-container {
border-right: var(--material-border);
}
#zotero-collections-tree {
background: var(--material-sidepane);
}
#select-items-dialog {
display: flex;
padding: 0;
}
#select-items-dialog #collections-items-container {
margin-bottom: 0;
}
#bn-select-opened-notes-container {
min-width: 200px;
}

View File

@ -0,0 +1,48 @@
.toolbar {
background: var(--material-toolbar);
border-bottom: var(--material-border);
padding: 6px;
align-items: center;
justify-content: space-between !important;
}
.toolbar-start,
.toolbar-middle,
.toolbar-end {
align-items: center;
}
.toolbar-header {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
color: var(--fill-secondary);
text-overflow: ellipsis;
overflow-wrap: anywhere;
line-break: anywhere;
font-size: calc(var(--zotero-font-size) * 1.2);
}
.toolbar-header.content {
flex-shrink: 0;
}
.toolbar-header.highlight {
font-size: var(--zotero-font-size);
padding: 4px;
border: var(--material-border);
box-shadow: 0 2px 5px
color-mix(in srgb, var(--material-background) 15%, transparent);
border-radius: 4px;
background: var(--material-background);
transition: all 0.3s ease;
}
.toolbar-header.highlight:hover {
box-shadow: 0 5px 15px
color-mix(in srgb, var(--material-background) 20%, transparent);
background: var(--color-background50);
}
.toolbar-header.highlight:empty {
display: none;
}

View File

@ -78,9 +78,9 @@ editor-toolbar-settings-openWorkspace = Open Note Workspace
editor-toolbar-settings-setWorkspace = Set as Workspace Note
editor-toolbar-settings-previewInWorkspace = Preview in Workspace
editor-toolbar-settings-showInLibrary = Show in Library
editor-toolbar-settings-insertTemplate = Insert Template to Cursor Line
editor-toolbar-settings-copyLink = Copy Note Link at Line ({ $line })
editor-toolbar-settings-copyLinkAtSection = Copy Note Link at Section ({ $section })
editor-toolbar-settings-insertTemplate = Insert template
editor-toolbar-settings-copyLink = Copy link (L{ $line })
editor-toolbar-settings-copyLinkAtSection = Copy link (Sec. { $section })
editor-toolbar-settings-openParent = Open Attachment
editor-toolbar-settings-export = Export Current Note...
editor-toolbar-settings-refreshSyncing = Sync Now

View File

@ -74,9 +74,9 @@ editor-toolbar-settings-openWorkspace = Apri nota di lavoro
editor-toolbar-settings-setWorkspace = Imposta come nota di lavoro
editor-toolbar-settings-previewInWorkspace = Anteprima nello spazio di lavoro
editor-toolbar-settings-showInLibrary = Show in Library
editor-toolbar-settings-insertTemplate = Inserisci template nella posizione del cursore
editor-toolbar-settings-copyLink = Copia link della nota alla riga ({ $line })
editor-toolbar-settings-copyLinkAtSection = Copia link della nota alla sezione ({ $section })
editor-toolbar-settings-insertTemplate = Inserisci template
editor-toolbar-settings-copyLink = Copia link (L{ $line })
editor-toolbar-settings-copyLinkAtSection = Copia link (Sec. { $section })
editor-toolbar-settings-openParent = Apri allegato
editor-toolbar-settings-export = Esporta nota corrente...
editor-toolbar-settings-refreshSyncing = Sincronizza ora

View File

@ -78,9 +78,9 @@ editor-toolbar-settings-openWorkspace=Открыть пространство з
editor-toolbar-settings-setWorkspace=Установить как Заметку раб. пространства
editor-toolbar-settings-previewInWorkspace=Предпросмотр в рабочем пространстве
editor-toolbar-settings-showInLibrary = Show in Library
editor-toolbar-settings-insertTemplate=Вставить шаблон в строку курсора
editor-toolbar-settings-copyLink = Копировать Ссылку на Заметку на строке ({ $line })
editor-toolbar-settings-copyLinkAtSection = Копировать Ссылку на Заметку в секции ({ $section })
editor-toolbar-settings-insertTemplate=Вставить шаблон
editor-toolbar-settings-copyLink = Копировать Ссылку (L{ $line })
editor-toolbar-settings-copyLinkAtSection = Копировать Ссылку (Sec. { $section })
editor-toolbar-settings-openParent=Открыть вложение
editor-toolbar-settings-export=Экспортировать текущую заметку...
editor-toolbar-settings-refreshSyncing=Синхронизировать сейчас

View File

@ -78,9 +78,9 @@ editor-toolbar-settings-openWorkspace = Not Çalışma Alanıın
editor-toolbar-settings-setWorkspace = Çalışma Alanı Notu Olarak Ayarla
editor-toolbar-settings-previewInWorkspace = Çalışma Alanında Ön İzle Preview in Workspace
editor-toolbar-settings-showInLibrary = Show in Library
editor-toolbar-settings-insertTemplate = İşaretçi Satırına Şablon Ekle
editor-toolbar-settings-copyLink = Satıra Not Linki Kopyala ({ $line })
editor-toolbar-settings-copyLinkAtSection = Bölüme Not Linki Kopyala ({ $section })
editor-toolbar-settings-insertTemplate = Insert template
editor-toolbar-settings-copyLink = Copy link (L{ $line })
editor-toolbar-settings-copyLinkAtSection =Copy link (Sec. { $section })
editor-toolbar-settings-openParent = Eki Aç
editor-toolbar-settings-export = Bu Notu Dışa Aktar...
editor-toolbar-settings-refreshSyncing = Senkronize Et

View File

@ -6,6 +6,8 @@ pref("__prefsPrefix__.syncAttachmentFolder", "attachments");
pref("__prefsPrefix__.autoAnnotation", false);
pref("__prefsPrefix__.insertLinkPosition", "end");
pref("__prefsPrefix__.embedLink", true);
pref("__prefsPrefix__.standaloneLink", false);
pref("__prefsPrefix__.keepLink", true);

View File

@ -65,6 +65,7 @@ import {
updateRelatedNotes,
getRelatedNoteIds,
getNoteTreeFlattened,
getLinesInNote,
} from "./utils/note";
const workspace = {};
@ -144,6 +145,7 @@ const editor = {
const note = {
insert: addLineToNote,
getLinesInNote,
updateRelatedNotes,
getRelatedNoteIds,
getNoteTreeFlattened,

View File

@ -11,14 +11,14 @@ export class PluginCEBase extends XULElementBase {
_wrapID(key: string) {
if (key.startsWith(config.addonRef)) {
return key;
return key;
}
return `${config.addonRef}-${key}`;
}
_unwrapID(id: string) {
if (id.startsWith(config.addonRef)) {
return id.slice(config.addonRef.length + 1);
return id.slice(config.addonRef.length + 1);
}
return id;
}
@ -28,9 +28,9 @@ export class PluginCEBase extends XULElementBase {
}
_parseContentID(dom: DocumentFragment) {
dom.querySelectorAll("*[id]").forEach(elem => {
elem.id = this._wrapID(elem.id);
})
dom.querySelectorAll("*[id]").forEach((elem) => {
elem.id = this._wrapID(elem.id);
});
return dom;
}

304
src/elements/notePicker.ts Normal file
View File

@ -0,0 +1,304 @@
import { config } from "../../package.json";
import { VirtualizedTableHelper } from "zotero-plugin-toolkit/dist/helpers/virtualizedTable";
import { PluginCEBase } from "./base";
const _require = window.require;
const CollectionTree = _require("chrome://zotero/content/collectionTree.js");
const ItemTree = _require("chrome://zotero/content/itemTree.js");
const { getCSSItemTypeIcon } = _require("components/icons");
export class NotePicker extends PluginCEBase {
itemsView!: _ZoteroTypes.ItemTree;
collectionsView!: _ZoteroTypes.CollectionTree;
openedNotesView!: VirtualizedTableHelper;
openedNotes: Zotero.Item[] = [];
activeSelectionType: "library" | "tabs" | "none" = "none";
get content() {
return MozXULElement.parseXULToFragment(`
<linkset>
<html:link
rel="stylesheet"
href="chrome://${config.addonRef}/content/styles/notePicker.css"
></html:link>
</linkset>
<vbox id="select-items-dialog" class="container">
<vbox id="zotero-select-items-container" class="container" flex="1">
<hbox id="search-toolbar" class="toolbar">
<hbox class="toolbar-start"></hbox>
<hbox class="toolbar-middle"></hbox>
<hbox class="toolbar-end"></hbox>
</hbox>
<vbox class="container">
<hbox id="collections-items-container">
<vbox
id="zotero-collections-tree-container"
class="virtualized-table-container"
>
<html:div id="zotero-collections-tree"></html:div>
</vbox>
<hbox
id="zotero-items-pane-content"
class="container virtualized-table-container"
flex="1"
>
<html:div id="zotero-items-tree"></html:div>
</hbox>
</hbox>
<vbox id="bn-select-opened-notes-container" class="container">
<vbox
id="bn-select-opened-notes-content"
class="container virtualized-table-container"
>
<html:div id="bn-select-opened-notes-tree"></html:div>
</vbox>
</vbox>
</vbox>
</vbox>
</vbox>
`);
}
set openedNoteIDs(ids: number[]) {
this.openedNotes = Zotero.Items.get(ids).filter((item) => item.isNote());
if (this.openedNotesView) {
this.openedNotesView.render();
return;
}
this.loadOpenedNotes();
}
async init() {
await this.loadLibraryNotes();
this.loadQuickSearch();
await this.loadOpenedNotes();
window.addEventListener("unload", () => {
this.destroy();
});
}
destroy(): void {
this.collectionsView.unregister();
if (this.itemsView) this.itemsView.unregister();
}
async loadLibraryNotes() {
this.itemsView = await ItemTree.init(
this.querySelector("#zotero-items-tree"),
{
onSelectionChange: () => {
this.onItemSelected();
},
id: "select-items-dialog",
dragAndDrop: false,
persistColumns: true,
columnPicker: true,
emptyMessage: Zotero.getString("pane.items.loading"),
},
);
this.itemsView.isSelectable = (index: number, selectAll = false) => {
const row = this.itemsView.getRow(index);
if (!row) {
return false;
}
// @ts-ignore
if (!row.ref.isNote()) return false;
if (this.itemsView.collectionTreeRow.isTrash()) {
// @ts-ignore
return row.ref.deleted;
} else {
// @ts-ignore
return this.itemsView._searchItemIDs.has(row.id);
}
};
this.itemsView.setItemsPaneMessage(Zotero.getString("pane.items.loading"));
// Wait otherwise the collection tree will not be initialized
await Zotero.Promise.delay(10);
this.collectionsView = await CollectionTree.init(
this.querySelector("#zotero-collections-tree"),
{
onSelectionChange: Zotero.Utilities.debounce(
() => this.onCollectionSelected(),
100,
),
},
);
this.collectionsView.hideSources = ["duplicates", "trash", "feeds"];
await this.collectionsView.makeVisible();
}
loadQuickSearch() {
// @ts-ignore
const searchBox = document.createXULElement("quick-search-textbox");
searchBox.id = "zotero-tb-search";
searchBox.setAttribute("timeout", "250");
searchBox.setAttribute("dir", "reverse");
searchBox.addEventListener("command", this.onSearch);
this.querySelector("#search-toolbar > .toolbar-end")?.appendChild(
searchBox,
);
Zotero.updateQuickSearchBox(document);
}
async loadOpenedNotes() {
const renderLock = Zotero.Promise.defer();
this.openedNotesView = new VirtualizedTableHelper(window)
.setContainerId("bn-select-opened-notes-tree")
.setProp({
id: `bn-select-opened-notes-table`,
columns: [
{
dataKey: "title",
label: "Opened Notes",
flex: 1,
},
],
showHeader: true,
multiSelect: false,
staticColumns: true,
disableFontSizeScaling: true,
})
.setProp("getRowCount", () => this.openedNotes.length || 0)
.setProp("getRowData", (index) => {
const note = this.openedNotes[index];
return {
title: note.getNoteTitle(),
};
})
.setProp("onSelectionChange", (selection) => {
this.onOpenedNoteSelected();
})
// For find-as-you-type
.setProp(
"getRowString",
(index) => this.openedNotes[index].getNoteTitle() || "",
)
.setProp("renderItem", (index, selection, oldElem, columns) => {
let div;
if (oldElem) {
div = oldElem;
div.innerHTML = "";
} else {
div = document.createElement("div");
div.className = "row";
}
div.classList.toggle("selected", selection.isSelected(index));
div.classList.toggle("focused", selection.focused == index);
const rowData = this.openedNotes[index];
for (const column of columns) {
const span = document.createElement("span");
// @ts-ignore
span.className = `cell ${column?.className}`;
span.textContent = rowData.getNoteTitle();
const icon = getCSSItemTypeIcon("note");
icon.classList.add("cell-icon");
span.prepend(icon);
div.append(span);
}
return div;
})
.render(-1, () => {
renderLock.resolve();
});
await renderLock.promise;
if (this.openedNotes.length === 1) {
this.openedNotesView.treeInstance.selection.select(0);
}
}
onSearch() {
if (this.itemsView) {
const searchVal = (
this.querySelector("#zotero-tb-search-textbox") as HTMLInputElement
)?.value;
this.itemsView.setFilter("search", searchVal);
}
}
async onCollectionSelected() {
const collectionTreeRow = this.collectionsView.getRow(
this.collectionsView.selection.focused,
);
if (!this.collectionsView.selection.count) return;
// Collection not changed
if (
this.itemsView &&
this.itemsView.collectionTreeRow &&
this.itemsView.collectionTreeRow.id == collectionTreeRow.id
) {
return;
}
// @ts-ignore
if (!collectionTreeRow._bnPatched) {
// @ts-ignore
collectionTreeRow._bnPatched = true;
const getItems = collectionTreeRow.getItems.bind(collectionTreeRow);
// @ts-ignore
collectionTreeRow.getItems = async function () {
const items = (await getItems()) as Zotero.Item[];
return items.filter((item) => item.isNote()) as unknown[];
};
}
collectionTreeRow.setSearch("");
Zotero.Prefs.set("lastViewedFolder", collectionTreeRow.id);
this.itemsView.setItemsPaneMessage(Zotero.getString("pane.items.loading"));
// Load library data if necessary
const library = Zotero.Libraries.get(collectionTreeRow.ref.libraryID);
if (library) {
if (!library.getDataLoaded("item")) {
Zotero.debug(
"Waiting for items to load for library " + library.libraryID,
);
await library.waitForDataLoad("item");
}
}
await this.itemsView.changeCollectionTreeRow(collectionTreeRow);
this.itemsView.clearItemsPaneMessage();
this.collectionsView.runListeners("select");
}
onItemSelected() {
this.activeSelectionType = "library";
this.dispatchSelectionChange();
}
onOpenedNoteSelected() {
this.activeSelectionType = "tabs";
this.dispatchSelectionChange();
}
dispatchSelectionChange() {
this.dispatchEvent(
new CustomEvent("selectionChange", {
detail: {
selectedNote: this.getSelectedNotes()[0],
},
}),
);
}
getSelectedNotes(): Zotero.Item[] {
if (this.activeSelectionType == "none") {
return [];
} else if (this.activeSelectionType == "library") {
return this.itemsView.getSelectedItems();
}
return Array.from(this.openedNotesView.treeInstance.selection.selected).map(
(index) => this.openedNotes[index],
);
}
}

View File

@ -132,7 +132,9 @@ export class OutlinePane extends PluginCEBase {
init(): void {
MozXULElement.insertFTLIfNeeded(`${config.addonRef}-outline.ftl`);
this._outlineContainer = this._queryID("outline") as unknown as HTMLIFrameElement;
this._outlineContainer = this._queryID(
"outline",
) as unknown as HTMLIFrameElement;
this._queryID("left-toolbar")?.addEventListener(
"command",
@ -163,6 +165,7 @@ export class OutlinePane extends PluginCEBase {
extraData: { [key: string]: any },
) {
if (!this.item) return;
if (extraData.skipBN) return;
if (event === "modify" && type === "item") {
if ((ids as number[]).includes(this.item.id)) {
this.updateOutline();
@ -188,10 +191,15 @@ export class OutlinePane extends PluginCEBase {
this.messageHandler,
);
this._outlineContainer.setAttribute("src", OutlinePane.outlineSources[this.outlineType]);
this._outlineContainer.setAttribute(
"src",
OutlinePane.outlineSources[this.outlineType],
);
await waitUtilAsync(
() => this._outlineContainer.contentWindow?.document.readyState === "complete",
() =>
this._outlineContainer.contentWindow?.document.readyState ===
"complete",
);
this._outlineContainer.contentWindow?.addEventListener(
"message",

View File

@ -1,5 +1,6 @@
import { ContextPane } from "../elements/context";
import { NoteDetails } from "../elements/detailsPane";
import { NotePicker } from "../elements/notePicker";
import { OutlinePane } from "../elements/outlinePane";
import { Workspace } from "../elements/workspace";
@ -8,6 +9,7 @@ const elements = {
"bn-outline": OutlinePane,
"bn-details": NoteDetails as unknown as CustomElementConstructor,
"bn-workspace": Workspace,
"bn-note-picker": NotePicker,
};
for (const [key, constructor] of Object.entries(elements)) {

325
src/extras/linkNote.ts Normal file
View File

@ -0,0 +1,325 @@
import { VirtualizedTableHelper } from "zotero-plugin-toolkit/dist/helpers/virtualizedTable";
import { config } from "../../package.json";
import Addon from "../addon";
import { waitUtilAsync } from "../utils/wait";
import { getPref, setPref } from "../utils/prefs";
import { NotePicker } from "../elements/notePicker";
let initialized = false;
let notePicker: NotePicker;
let noteOutlineView: VirtualizedTableHelper;
let currentNote: Zotero.Item;
let targetNote: Zotero.Item | undefined;
let noteOutline: ReturnType<Addon["api"]["note"]["getNoteTreeFlattened"]> = [];
let positionData: NoteNodeData | undefined;
// @ts-ignore
window.addon = Zotero[config.addonRef];
let io: {
currentNoteID: number;
openedNoteIDs?: number[];
deferred: _ZoteroTypes.DeferredPromise<void>;
targetNoteID?: number;
content?: string;
lineIndex?: number;
};
window.onload = async function () {
// Set font size from pref
const sbc = document.getElementById("top-container");
Zotero.UIProperties.registerRoot(sbc);
// @ts-ignore
io = window.arguments[0];
loadNotePicker();
loadInsertPosition();
loadNoteOutline();
document.addEventListener("dialogaccept", doAccept);
currentNote = Zotero.Items.get(io.currentNoteID);
initialized = true;
scrollToSection("picker");
};
window.onunload = function () {
io.deferred && io.deferred.resolve();
};
function loadNotePicker() {
notePicker = document.querySelector("bn-note-picker") as NotePicker;
notePicker.openedNoteIDs = io.openedNoteIDs || [];
const content = document.createElement("span");
content.innerHTML = "Step 1. Choose target note:";
content.classList.add("toolbar-header", "content");
const title = document.createElement("span");
title.id = "selected-note-title";
title.classList.add("toolbar-header", "highlight");
notePicker
.querySelector("#search-toolbar .toolbar-start")
?.append(content, title);
notePicker.addEventListener("selectionChange", (event: any) => {
updateSelectedNotesTitle(event.detail.selectedNote);
updateNoteOutline(event.detail.selectedNote);
});
}
function loadInsertPosition() {
const insertPosition = document.getElementById(
"bn-link-insert-position",
) as HTMLSelectElement;
insertPosition.value = getPref("insertLinkPosition") as string;
insertPosition.addEventListener("command", () => {
setPref("insertLinkPosition", insertPosition.value);
updateNotePreview();
});
}
async function loadNoteOutline() {
const renderLock = Zotero.Promise.defer();
noteOutlineView = new VirtualizedTableHelper(window)
.setContainerId("bn-select-note-outline-tree")
.setProp({
id: `bn-select-note-outline-table`,
columns: [
{
dataKey: "level",
label: "Level",
width: 50,
staticWidth: true,
},
{
dataKey: "name",
label: "Table of Contents",
flex: 1,
},
],
showHeader: true,
multiSelect: false,
staticColumns: true,
disableFontSizeScaling: true,
})
.setProp("getRowCount", () => noteOutline.length || 0)
.setProp("getRowData", (index) => {
const model = noteOutline[index]?.model;
if (!model) return { level: 0, name: "**Unknown**" };
return {
level: model.level,
name: "··".repeat(model.level - 1) + model.name,
};
})
.setProp("onSelectionChange", (selection) => {
onOutlineSelected(selection);
})
// For find-as-you-type
.setProp("getRowString", (index) => noteOutline[index]?.model.name || "")
.render(-1, () => {
renderLock.resolve();
});
await renderLock.promise;
// if (openedNotes.length === 1) {
// openedNotesView.treeInstance.selection.select(0);
// }
}
function onOutlineSelected(selection: { selected: Set<number> }) {
positionData = noteOutline[selection.selected.values().next().value]?.model;
updateNotePreview();
updateSelectedOutlineTitle();
}
function updateSelectedNotesTitle(noteItem?: Zotero.Item) {
const title = noteItem ? noteItem.getNoteTitle() : "";
document.querySelector("#selected-note-title")!.textContent = title;
}
function updateSelectedOutlineTitle() {
const selectedOutline =
noteOutline[
noteOutlineView.treeInstance.selection.selected.values().next().value
];
const title = selectedOutline ? selectedOutline.model.name : "";
document.querySelector("#selected-outline-title")!.textContent = title;
}
function updatePreviewTitle() {
document.querySelector("#preview-note-from-title")!.textContent =
currentNote.getNoteTitle() || "No title";
document.querySelector("#preview-note-middle-title")!.textContent = "to";
document.querySelector("#preview-note-to-title")!.textContent =
targetNote?.getNoteTitle() || "No title";
}
async function updateNoteOutline(noteItem?: Zotero.Item) {
if (!noteItem) {
targetNote = undefined;
noteOutline = [];
} else {
targetNote = noteItem;
noteOutline = addon.api.note.getNoteTreeFlattened(targetNote);
}
noteOutlineView?.render(undefined);
// Set default line index to the end of the note
positionData = undefined;
if (targetNote) scrollToSection("outline");
}
async function updateNotePreview() {
if (!initialized || !targetNote) return;
const lines = await addon.api.note.getLinesInNote(targetNote, {
convertToHTML: true,
});
let index = getIndexToInsert();
if (index < 0) {
index = lines.length;
} else {
scrollToSection("preview");
}
const before = lines.slice(0, index).join("\n");
const after = lines.slice(index).join("\n");
// TODO: use index or section
const content = await getContentToInsert();
const iframe = document.querySelector(
"#bn-note-preview",
) as HTMLIFrameElement;
const activeElement = document.activeElement as HTMLElement; // 保存当前活动元素
iframe!.contentDocument!.documentElement.innerHTML = `<html>
<head>
<title></title>
<link
rel="stylesheet"
type="text/css"
href="chrome://zotero-platform/content/zotero.css"
/>
<link
rel="stylesheet"
type="text/css"
href="chrome://${config.addonRef}/content/lib/css/github-markdown.css"
/>
<link
rel="stylesheet"
href="chrome://${config.addonRef}/content/lib/css/katex.min.css"
crossorigin="anonymous"
/>
<style>
html {
color-scheme: light dark;
background: var(--material-sidepane);
}
body {
overflow-x: clip;
}
#inserted {
border: var(--material-border);
box-shadow: 0 2px 5px color-mix(in srgb, var(--material-background) 15%, transparent);
border-radius: 4px;
background: var(--material-background);
padding: 10px;
transition: all 0.3s ease;
}
#inserted:hover {
box-shadow: 0 5px 15px color-mix(in srgb, var(--material-background) 20%, transparent);
background: var(--color-background50);
}
</style>
</head>
<body>
<div>${before}</div>
<div id="inserted">${content}</div>
<div>${after}</div>
</body>
</html>
`;
activeElement?.focus();
await waitUtilAsync(() => iframe.contentDocument?.readyState === "complete");
// Scroll the inserted section into the center of the iframe
const inserted = iframe.contentDocument?.getElementById("inserted");
if (inserted) {
const rect = inserted.getBoundingClientRect();
const container = inserted.parentElement!;
container.scrollTo({
top:
container.scrollTop +
rect.top -
container.clientHeight / 2 +
rect.height,
behavior: "smooth",
});
}
updatePreviewTitle();
}
function scrollToSection(type: "picker" | "outline" | "preview") {
if (!initialized) return;
const querier = {
picker: "#zotero-select-items-container",
outline: "#bn-select-note-outline-container",
preview: "#bn-note-preview-container",
};
const container = document.querySelector(querier[type]);
if (!container) return;
container.scrollIntoView({
behavior: "smooth",
inline: "center",
});
}
async function getContentToInsert() {
const forwardLink = addon.api.convert.note2link(currentNote, {});
const content = await addon.api.template.runTemplate(
"[QuickInsertV2]",
"link, linkText, subNoteItem, noteItem",
[
forwardLink,
currentNote.getNoteTitle().trim() || forwardLink,
currentNote,
targetNote,
],
{
dryRun: true,
},
);
return content;
}
function getIndexToInsert() {
if (!positionData) return -1;
let position = getPref("insertLinkPosition") as string;
if (!["start", "end"].includes(position)) {
position = "end";
}
let index = {
start: positionData.lineIndex + 1,
end: positionData.endIndex + 1,
}[position];
if (index === undefined) {
index = -1;
}
return index;
}
async function doAccept() {
if (!targetNote) return;
const content = await getContentToInsert();
io.targetNoteID = targetNote.id;
io.content = content;
io.lineIndex = getIndexToInsert();
}

View File

@ -35,6 +35,7 @@ import { waitUtilAsync } from "./utils/wait";
import { initSyncList } from "./modules/sync/api";
import { getPref } from "./utils/prefs";
import { patchViewItems } from "./modules/viewItems";
import { onUpdateRelated } from "./modules/relatedNotes";
async function onStartup() {
await Promise.all([
@ -67,7 +68,7 @@ async function onMainWindowLoad(win: Window): Promise<void> {
await waitUtilAsync(() => document.readyState === "complete");
Services.scriptloader.loadSubScript(
`chrome://${config.addonRef}/content/scripts/workspace.js`,
`chrome://${config.addonRef}/content/scripts/customElements.js`,
win,
);
// Create ztoolkit for every window
@ -121,6 +122,7 @@ function onNotify(
skipActive: true,
reason: "item-modify",
});
addon.hooks.onUpdateRelated(modifiedNotes, { skipActive: true });
}
} else {
return;
@ -164,7 +166,7 @@ function onOpenNote(
// addon.hooks.onSetWorkspaceNote(noteId, "preview", options);
break;
case "workspace":
// addon.hooks.onSetWorkspaceNote(noteId, "main", options);
addon.hooks.onOpenWorkspace(noteItem, "tab");
break;
case "standalone":
ZoteroPane.openNoteWindow(noteId);
@ -247,6 +249,7 @@ export default {
onOpenWorkspace,
onToggleWorkspacePane,
onSyncing,
onUpdateRelated,
onShowTemplatePicker,
onUpdateTemplatePicker,
onImportTemplateFromClipboard,

View File

@ -175,7 +175,7 @@ async function note2noteDiff(noteItem: Zotero.Item) {
function note2link(
noteItem: Zotero.Item,
options: Parameters<typeof getNoteLink>[1],
options: Parameters<typeof getNoteLink>[1] = {},
) {
return getNoteLink(noteItem, options);
}
@ -236,7 +236,7 @@ function annotations2html(
async function note2html(
noteItems: Zotero.Item | Zotero.Item[],
options: { targetNoteItem?: Zotero.Item; html?: string },
options: { targetNoteItem?: Zotero.Item; html?: string } = {},
) {
if (!Array.isArray(noteItems)) {
noteItems = [noteItems];

View File

@ -2,431 +2,212 @@ import { config } from "../../../package.json";
import { ICONS } from "../../utils/config";
import { getLineAtCursor, getSectionAtCursor } from "../../utils/editor";
import { showHint } from "../../utils/hint";
import { getNoteLink, getNoteLinkParams } from "../../utils/link";
import { getNoteLink } from "../../utils/link";
import { getString } from "../../utils/locale";
import { addLineToNote, getNoteTreeFlattened } from "../../utils/note";
import { getPref } from "../../utils/prefs";
import { openLinkNoteDialog } from "../../utils/linkNote";
import { slice } from "../../utils/str";
export async function initEditorToolbar(editor: Zotero.EditorInstance) {
const noteItem = editor._item;
const toolbar = await registerEditorToolbar(editor, makeId("toolbar"));
// Settings
const settingsButton = await registerEditorToolbarDropdown(
const _document = editor._iframeWindow.document;
registerEditorToolbarElement(
editor,
toolbar,
makeId("settings"),
ICONS.settings,
getString("editor.toolbar.settings.title"),
"end",
(e) => {},
_document.querySelector(".toolbar") as HTMLDivElement,
"start",
ztoolkit.UI.createElement(_document, "button", {
classList: ["toolbar-button"],
properties: {
innerHTML: ICONS.addon,
title: "Link current note to another note",
},
listeners: [
{
type: "click",
listener: (e) => {
openLinkNoteDialog(noteItem);
},
},
],
}) as HTMLButtonElement,
);
settingsButton.addEventListener("click", async (ev) => {
ev.stopPropagation();
function removePopup() {
const popup = editor._iframeWindow.document.querySelector(
`#${makeId("settings-popup")}`,
);
if (popup) {
popup.remove();
settingsButton
.querySelector(".toolbar-button")
?.classList.remove("active");
editor._iframeWindow.document.removeEventListener("click", removePopup);
return true;
const settingsButton = editor._iframeWindow.document.querySelector(
".toolbar .end .dropdown .toolbar-button",
) as HTMLDivElement;
const MutationObserver = // @ts-ignore
editor._iframeWindow.MutationObserver as typeof window.MutationObserver;
const observer = new MutationObserver((mutations) => {
mutations.forEach(async (mutation) => {
if (
mutation.type === "attributes" &&
mutation.attributeName === "class" &&
mutation.target === settingsButton
) {
if (settingsButton.classList.contains("active")) {
const dropdown = settingsButton.parentElement!;
const popup = dropdown.querySelector(".popup") as HTMLDivElement;
ztoolkit.log(popup);
registerEditorToolbarPopup(editor, popup, await getMenuData(editor));
}
}
return false;
}
if (removePopup()) {
return;
}
const currentLine = getLineAtCursor(editor);
const currentSection = getSectionAtCursor(editor);
const settingsMenuData: PopupData[] = [
{
id: makeId("settings-openWorkspace"),
text: getString("editor.toolbar.settings.openWorkspace"),
callback: (e) => {
addon.hooks.onOpenWorkspace(noteItem, "tab");
},
},
{
id: makeId("settings-showInLibrary"),
text: getString("editor.toolbar.settings.showInLibrary"),
callback: (e) => {
ZoteroPane.selectItems([e.editor._item.id]);
},
},
];
if (currentLine >= 0) {
settingsMenuData.push(
...(<PopupData[]>[
{
type: "splitter",
},
{
id: makeId("settings-export"),
text: getString("editor.toolbar.settings.export"),
callback: (e) => {
if (addon.api.sync.isSyncNote(noteItem.id)) {
addon.hooks.onShowSyncInfo(noteItem.id);
} else {
addon.hooks.onShowExportNoteOptions([noteItem.id]);
}
},
},
{
type: "splitter",
},
{
id: makeId("settings-insertTemplate"),
text: getString("editor.toolbar.settings.insertTemplate"),
callback: (e) => {
addon.hooks.onShowTemplatePicker("insert", {
noteId: e.editor._item.id,
lineIndex: currentLine,
});
},
},
{
type: "splitter",
},
{
id: makeId("settings-copyLink"),
text: getString("editor.toolbar.settings.copyLink", {
args: {
line: currentLine,
},
}),
callback: (e) => {
const link =
getNoteLink(e.editor._item, {
lineIndex: currentLine,
}) || "";
new ztoolkit.Clipboard()
.addText(link, "text/unicode")
.addText(
`<a href="${link}">${
e.editor._item.getNoteTitle().trim() || link
}</a>`,
"text/html",
)
.copy();
showHint(`Link ${link} copied`);
},
},
{
id: makeId("settings-copyLinkAtSection"),
text: getString("editor.toolbar.settings.copyLinkAtSection", {
args: {
section: currentSection,
},
}),
callback: (e) => {
const link =
getNoteLink(e.editor._item, {
sectionName: currentSection,
}) || "";
new ztoolkit.Clipboard()
.addText(link, "text/unicode")
.addText(
`<a href="${link}#${currentSection}">${
e.editor._item.getNoteTitle().trim() || link
}</a>`,
"text/html",
)
.copy();
showHint(`Link ${link} copied`);
},
},
{
id: makeId("settings-updateRelatedNotes"),
text: getString("editor-toolbar-settings-updateRelatedNotes"),
callback: (e) => {
addon.api.note.updateRelatedNotes(e.editor._item.id);
},
},
]),
);
}
const parentAttachment = await noteItem.parentItem?.getBestAttachment();
if (parentAttachment) {
settingsMenuData.push(
...(<PopupData[]>[
{
type: "splitter",
},
{
id: makeId("settings-openParent"),
text: getString("editor.toolbar.settings.openParent"),
callback: (e) => {
ZoteroPane.viewAttachment([parentAttachment.id]);
Zotero.Notifier.trigger("open", "file", parentAttachment.id);
},
},
]),
);
}
if (addon.api.sync.isSyncNote(noteItem.id)) {
settingsMenuData.splice(5, 0, {
id: makeId("settings-refreshSyncing"),
text: getString("editor.toolbar.settings.refreshSyncing"),
callback: (e) => {
addon.hooks.onSyncing(undefined, {
quiet: false,
skipActive: false,
reason: "manual-editor",
});
},
});
}
registerEditorToolbarPopup(
editor,
settingsButton,
`${config.addonRef}-settings-popup`,
"right",
settingsMenuData,
).then((popup) => {
settingsButton.querySelector(".toolbar-button")?.classList.add("active");
editor._iframeWindow.document.addEventListener("click", removePopup);
});
});
// Center button
const onTriggerMenu = (ev: MouseEvent) => {
editor._iframeWindow.focus();
const linkMenu: PopupData[] = getLinkMenuData(editor);
editor._iframeWindow.document
.querySelector(`#${makeId("link")}`)!
.querySelector(".toolbar-button")!.innerHTML = ICONS.linkAfter;
const popup = registerEditorToolbarPopup(
editor,
linkButton,
`${config.addonRef}-link-popup`,
"middle",
linkMenu,
);
};
const onExitMenu = (ev: MouseEvent) => {
editor._iframeWindow.document
.querySelector(`#${makeId("link-popup")}`)
?.remove();
editor._iframeWindow.document
.querySelector(`#${makeId("link")}`)!
.querySelector(".toolbar-button")!.innerHTML = ICONS.addon;
};
const onClickMenu = async (ev: MouseEvent) => {
// TODO: fix link
return;
// const mainNote = Zotero.Items.get(addon.data.workspace.mainId) || null;
// if (!mainNote?.isNote()) {
// return;
// }
// const lineIndex = parseInt(
// (ev.target as HTMLDivElement).id.split("-").pop() || "-1",
// );
// const forwardLink = getNoteLink(noteItem);
// const backLink = getNoteLink(mainNote, { ignore: true, lineIndex });
// addLineToNote(
// mainNote,
// await addon.api.template.runTemplate(
// "[QuickInsertV2]",
// "link, linkText, subNoteItem, noteItem",
// [
// forwardLink,
// noteItem.getNoteTitle().trim() || forwardLink,
// noteItem,
// mainNote,
// ],
// ),
// lineIndex,
// );
// addLineToNote(
// noteItem,
// await addon.api.template.runTemplate(
// "[QuickBackLinkV2]",
// "link, linkText, subNoteItem, noteItem",
// [
// backLink,
// mainNote.getNoteTitle().trim() || "Workspace Note",
// noteItem,
// mainNote,
// "",
// ],
// ),
// );
// onExitMenu(ev);
// ev.stopPropagation();
};
const linkButton = await registerEditorToolbarDropdown(
editor,
toolbar,
makeId("link"),
ICONS.addon,
getString("editor.toolbar.link.title"),
"middle",
onClickMenu,
);
linkButton.addEventListener("mouseenter", onTriggerMenu);
linkButton.addEventListener("mouseleave", onExitMenu);
linkButton.addEventListener("mouseleave", onExitMenu);
linkButton.addEventListener("click", (ev) => {
if ((ev.target as HTMLElement).classList.contains("option")) {
onClickMenu(ev);
}
observer.observe(settingsButton, {
attributes: true,
attributeFilter: ["class"],
});
editor._iframeWindow.document.addEventListener("click", onExitMenu);
// Export
// const exportButton = await registerEditorToolbarDropdown(
// editor,
// toolbar,
// makeId("export"),
// ICONS.export,
// getString("editor.toolbar.export.title"),
// "end",
// (e) => {
// if (addon.api.sync.isSyncNote(noteItem.id)) {
// addon.hooks.onShowSyncInfo(noteItem.id);
// } else {
// addon.hooks.onShowExportNoteOptions([noteItem.id]);
// }
// }
// );
}
function getLinkMenuData(editor: Zotero.EditorInstance): PopupData[] {
return [];
// const workspaceNote = Zotero.Items.get(addon.data.workspace.mainId) || null;
// const currentNote = editor._item;
// if (!workspaceNote?.isNote()) {
// return [
// {
// id: makeId("link-popup-nodata"),
// text: getString("editor.toolbar.link.popup.nodata"),
// },
// ];
// }
// const nodes = getNoteTreeFlattened(workspaceNote, {
// keepLink: true,
// });
// const menuData: PopupData[] = [];
// for (const node of nodes) {
// if (node.model.level === 7) {
// const lastMenu =
// menuData.length > 0 ? menuData[menuData.length - 1] : null;
// const linkNote = getNoteLinkParams(node.model.link).noteItem;
// if (linkNote && linkNote.id === currentNote.id && lastMenu) {
// lastMenu.suffix = "🔗";
// }
// continue;
// }
// menuData.push({
// id: makeId(
// `link-popup-${
// getPref("editor.link.insertPosition")
// ? node.model.lineIndex - 1
// : node.model.endIndex
// }`,
// ),
// text: node.model.name,
// prefix: "·".repeat(node.model.level - 1),
// });
// }
// return menuData;
}
async function getMenuData(editor: Zotero.EditorInstance) {
const noteItem = editor._item;
async function registerEditorToolbar(
editor: Zotero.EditorInstance,
id: string,
) {
await editor._initPromise;
const _document = editor._iframeWindow.document;
const toolbar = ztoolkit.UI.createElement(_document, "div", {
attributes: {
id,
const currentLine = getLineAtCursor(editor);
const currentSection = slice(getSectionAtCursor(editor) || "", 10);
const settingsMenuData: PopupData[] = [
{
id: makeId("settings-openWorkspace"),
text: getString("editor.toolbar.settings.openWorkspace"),
callback: (e) => {
addon.hooks.onOpenWorkspace(noteItem, "tab");
},
},
classList: ["toolbar"],
children: [
{
tag: "div",
classList: ["start"],
{
id: makeId("settings-showInLibrary"),
text: getString("editor.toolbar.settings.showInLibrary"),
callback: (e) => {
ZoteroPane.selectItems([e.editor._item.id]);
},
{
tag: "div",
classList: ["middle"],
},
{
tag: "div",
classList: ["end"],
},
],
ignoreIfExists: true,
}) as HTMLDivElement;
_document.querySelector(".editor")?.childNodes[0].before(toolbar);
return toolbar;
}
async function registerEditorToolbarDropdown(
editor: Zotero.EditorInstance,
toolbar: HTMLDivElement,
id: string,
icon: string,
title: string,
position: "start" | "middle" | "end",
callback: (e: MouseEvent & { editor: Zotero.EditorInstance }) => any,
) {
await editor._initPromise;
const _document = editor._iframeWindow.document;
const dropdown = ztoolkit.UI.createElement(_document, "div", {
attributes: {
id,
title,
},
classList: ["dropdown", "more-dropdown"],
children: [
{
tag: "button",
attributes: {
title,
];
if (currentLine >= 0) {
settingsMenuData.push(
...(<PopupData[]>[
{
type: "splitter",
},
properties: {
innerHTML: icon,
},
classList: ["toolbar-button"],
listeners: [
{
type: "click",
listener: (e) => {
Object.assign(e, { editor });
if (callback) {
callback(
e as any as MouseEvent & { editor: Zotero.EditorInstance },
);
}
},
{
id: makeId("settings-export"),
text: getString("editor.toolbar.settings.export"),
callback: (e) => {
if (addon.api.sync.isSyncNote(noteItem.id)) {
addon.hooks.onShowSyncInfo(noteItem.id);
} else {
addon.hooks.onShowExportNoteOptions([noteItem.id]);
}
},
],
},
{
type: "splitter",
},
{
id: makeId("settings-insertTemplate"),
text: getString("editor.toolbar.settings.insertTemplate"),
callback: (e) => {
addon.hooks.onShowTemplatePicker("insert", {
noteId: e.editor._item.id,
lineIndex: currentLine,
});
},
},
{
type: "splitter",
},
{
id: makeId("settings-copyLink"),
text: getString("editor.toolbar.settings.copyLink", {
args: {
line: currentLine,
},
}),
callback: (e) => {
const link =
getNoteLink(e.editor._item, {
lineIndex: currentLine,
}) || "";
new ztoolkit.Clipboard()
.addText(link, "text/unicode")
.addText(
`<a href="${link}">${
e.editor._item.getNoteTitle().trim() || link
}</a>`,
"text/html",
)
.copy();
showHint(`Link ${link} copied`);
},
},
{
id: makeId("settings-copyLinkAtSection"),
text: getString("editor.toolbar.settings.copyLinkAtSection", {
args: {
section: currentSection,
},
}),
callback: (e) => {
const link =
getNoteLink(e.editor._item, {
sectionName: currentSection,
}) || "";
new ztoolkit.Clipboard()
.addText(link, "text/unicode")
.addText(
`<a href="${link}#${currentSection}">${
e.editor._item.getNoteTitle().trim() || link
}</a>`,
"text/html",
)
.copy();
showHint(`Link ${link} copied`);
},
},
{
id: makeId("settings-updateRelatedNotes"),
text: getString("editor-toolbar-settings-updateRelatedNotes"),
callback: (e) => {
addon.api.note.updateRelatedNotes(e.editor._item.id);
},
},
]),
);
}
const parentAttachment = await noteItem.parentItem?.getBestAttachment();
if (parentAttachment) {
settingsMenuData.push(
...(<PopupData[]>[
{
type: "splitter",
},
{
id: makeId("settings-openParent"),
text: getString("editor.toolbar.settings.openParent"),
callback: (e) => {
ZoteroPane.viewAttachment([parentAttachment.id]);
Zotero.Notifier.trigger("open", "file", parentAttachment.id);
},
},
]),
);
}
if (addon.api.sync.isSyncNote(noteItem.id)) {
settingsMenuData.splice(5, 0, {
id: makeId("settings-refreshSyncing"),
text: getString("editor.toolbar.settings.refreshSyncing"),
callback: (e) => {
addon.hooks.onSyncing(undefined, {
quiet: false,
skipActive: false,
reason: "manual-editor",
});
},
],
skipIfExists: true,
});
toolbar.querySelector(`.${position}`)?.append(dropdown);
return dropdown;
});
}
return settingsMenuData;
}
declare interface PopupData {
@ -440,21 +221,18 @@ declare interface PopupData {
async function registerEditorToolbarPopup(
editor: Zotero.EditorInstance,
dropdown: HTMLDivElement,
id: string,
align: "middle" | "left" | "right",
popup: HTMLDivElement,
popupLines: PopupData[],
) {
await editor._initPromise;
const popup = ztoolkit.UI.appendElement(
ztoolkit.UI.appendElement(
{
tag: "div",
classList: ["popup"],
id,
tag: "fragment",
children: popupLines.map((props) => {
return props.type === "splitter"
? {
tag: "hr",
tag: "div",
classList: ["separator"],
properties: {
id: props.id,
},
@ -485,20 +263,9 @@ async function registerEditorToolbarPopup(
],
};
}),
removeIfExists: true,
},
dropdown,
popup,
) as HTMLDivElement;
let style: string = "";
if (align === "middle") {
style = `right: -${popup.offsetWidth / 2 - 15}px;`;
} else if (align === "left") {
style = "left: 0; right: auto;";
} else if (align === "right") {
style = "right: 0;";
}
popup.setAttribute("style", style);
return popup;
}
async function registerEditorToolbarElement(

View File

@ -8,7 +8,7 @@ export async function saveMD(
options: {
keepNoteLink?: boolean;
withYAMLHeader?: boolean;
},
} = {},
) {
const noteItem = Zotero.Items.get(noteId);
const dir = jointPath(...PathUtils.split(formatPath(filename)).slice(0, -1));

View File

@ -23,7 +23,6 @@ export async function savePDF(noteId: number) {
}
function disablePrintFooterHeader() {
// @ts-ignore
Zotero.Prefs.resetBranch([], "print");
Zotero.Prefs.set("print.print_footercenter", "", true);
Zotero.Prefs.set("print.print_footerleft", "", true);

View File

@ -0,0 +1,31 @@
import { getPref } from "../utils/prefs";
export { onUpdateRelated };
function onUpdateRelated(
items: Zotero.Item[] = [],
{ skipActive } = {
skipActive: true,
},
) {
if (!getPref("workspace.autoUpdateRelatedNotes")) {
return;
}
if (skipActive) {
// Skip active note editors' targets
const activeNoteIds = Zotero.Notes._editorInstances
.filter(
(editor) =>
!Components.utils.isDeadWrapper(editor._iframeWindow) &&
editor._iframeWindow.document.hasFocus(),
)
.map((editor) => editor._item.id);
const filteredItems = items.filter(
(item) => !activeNoteIds.includes(item.id),
);
items = filteredItems;
}
for (const item of items) {
addon.api.note.updateRelatedNotes(item.id);
}
}

View File

@ -99,7 +99,6 @@ export async function showSyncManager() {
"getRowString",
(index) => addon.data.prefs?.rows[index].title || "",
)
// @ts-ignore TODO: Fix type in zotero-plugin-toolkit
.setProp("onColumnSort", (columnIndex, ascending) => {
addon.data.sync.manager.columnIndex = columnIndex;
addon.data.sync.manager.columnAscending = ascending > 0;

View File

@ -109,7 +109,7 @@ async function runTextTemplate(
options: {
targetNoteId?: number;
dryRun?: boolean;
},
} = {},
) {
const { targetNoteId, dryRun } = options;
const targetNoteItem = Zotero.Items.get(targetNoteId || -1);
@ -130,7 +130,7 @@ async function runItemTemplate(
itemIds?: number[];
targetNoteId?: number;
dryRun?: boolean;
},
} = {},
): Promise<string> {
/**
* args:

View File

@ -1,6 +1,6 @@
export const ICONS = {
settings: `<svg t="1679297692375" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1573" width="24" height="24"><path d="M913.94086 431.828053c-4.878101-25.783223-20.693298-42.455951-40.947598-42.455951l-3.511987 0c-54.731532 0-99.248422-44.515866-99.248422-99.262748 0-17.294898 8.299013-37.015032 8.619308-37.798884 10.097986-22.722514 2.349511-50.567699-18.078751-64.859193l-102.701057-57.183374-1.509377-0.738827c-20.545942-8.909927-48.667419-3.207042-63.987337 12.753465-11.086499 11.434423-49.306986 44.037982-78.471213 44.037982-29.543873 0-67.849294-33.257451-78.992075-44.908816-15.293311-16.077164-43.12417-22.112624-63.902402-13.218046l-106.35733 58.272171-1.596358 1.016143c-20.430308 14.234189-28.207435 42.078351-18.165732 64.713884 0.346901 0.827855 8.676613 20.387329 8.676613 37.914518 0 54.746882-44.51689 99.262748-99.247398 99.262748l-4.149507 0c-19.617803 0-35.434024 16.671705-40.309054 42.455951-0.363274 1.814322-8.590656 45.446052-8.590656 80.429821 0 34.938744 8.227382 78.555124 8.590656 80.355119 4.875031 25.799596 20.691251 42.48665 40.946574 42.48665l3.511987 0c54.730509 0 99.247398 44.51689 99.247398 99.247398 0 17.411555-8.328689 37.058011-8.647961 37.812187-10.069333 22.766516-2.349511 50.567699 18.021445 64.787562l100.756775 56.531528 1.538029 0.696872c20.836561 9.17087 49.01432 3.191692 64.250326-13.464663 14.07353-15.207353 52.207036-46.782489 80.20981-46.782489 30.354332 0 69.445652 35.347043 80.706113 47.76691 10.387581 11.376095 26.349111 18.22713 42.687218 18.22713 7.631818 0 14.857383-1.511423 21.474081-4.354168l104.4724-57.574277 1.538029-0.989537c20.428262-14.276145 28.206412-42.077328 18.138102-64.727187-0.348947-0.842181-8.677637-20.402679-8.677637-37.928844 0-54.730509 44.51689-99.247398 99.248422-99.247398l4.093225 0c19.644409 0 35.488259-16.687054 40.365336-42.48665 0.347924-1.799996 8.588609-45.416376 8.588609-80.355119C922.529469 477.274104 914.288784 433.642374 913.94086 431.828053M862.982258 512.257873c0 22.605857-4.498454 51.655474-6.559393 63.785745-82.09781 6.732331-145.738245 75.335802-145.738245 158.303422 0 23.419386 7.430226 45.851281 11.377118 56.169277l-89.12076 49.216935c-4.38282-4.584412-17.325597-17.644869-34.939767-30.762631-30.93557-22.925129-60.595077-34.635845-88.106664-34.635845-27.278273 0-56.703443 11.420097-87.493703 34.05563-17.528212 12.768815-30.296003 25.479301-34.765805 30.18037l-85.724407-47.997154c4.179183-10.839883 11.405771-32.982182 11.405771-56.226582 0-82.96762-63.640436-151.571091-145.70857-158.303422-2.089591-12.130272-6.588045-41.178865-6.588045-63.785745 0-22.650883 4.498454-51.713802 6.588045-63.844074 82.068134-6.718005 145.70857-75.335802 145.70857-158.303422 0-23.288402-7.429203-45.792952-11.376095-56.095599l91.325985-50.190099c3.975545 3.976568 17.005302 16.730033 34.82311 29.411867 30.355355 21.663392 59.260685 32.633235 86.016049 32.633235 26.494421 0 55.19509-10.766205 85.346807-32.009018 17.963117-12.623505 30.964222-25.203008 34.736129-28.757974l87.900979 48.825009c-3.975545 10.244318-11.405771 32.676214-11.405771 56.18258 0 82.96762 63.640436 151.585417 145.738245 158.303422C858.483804 460.5717 862.982258 489.7523 862.982258 512.257873" fill="currentColor" p-id="1574"></path><path d="M510.215866 365.633445c-80.530105 0-146.056494 65.527412-146.056494 146.057517 0 80.543408 65.527412 146.043191 146.056494 146.043191 80.530105 0 146.057517-65.499783 146.057517-146.043191C656.273383 431.160857 590.74597 365.633445 510.215866 365.633445M596.725148 511.690962c0 47.693232-38.799678 86.491887-86.509283 86.491887-47.708582 0-86.479607-38.798655-86.479607-86.491887 0-47.665603 38.771025-86.479607 86.479607-86.479607C557.925471 425.212378 596.725148 464.025359 596.725148 511.690962" fill="currentColor" p-id="1575"></path></svg>`,
addon: `<svg t="1651124422933" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3269" width="24" height="24"><path d="M896.00324 352c70.7 0 128-57.3 128-128 0-70.6-57.4-128-128-128-70.7 0-128 57.3-128 128 0 18.8 4.1 36.7 11.3 52.8 2.7 6 1.4 13.1-3.3 17.8l-24.2 24.2c-5.7 5.7-14.9 6.3-21.2 1.2-38.1-30.1-86.3-48-138.6-48-18.8 0-37.1 2.3-54.6 6.7-6.9 1.7-14.1-1.4-17.7-7.5l-6.6-11.4c-3.4-5.8-2.7-13.1 1.6-18.3 18.6-22.6 29.7-51.6 29.3-83.2C543.10324 89 486.30324 32.6 417.00324 32c-70.6-0.6-128.1 56.1-129 126.3-0.9 69.5 56.5 128.6 126 129.6 9.4 0.1 18.5-0.7 27.4-2.5 6.8-1.4 13.6 1.7 17.1 7.7l2.2 3.8c4 7 2.2 15.9-4.2 20.7-42.4 32.3-73 79.4-84 133.6-1.5 7.4-8.1 12.7-15.7 12.7h-94.1c-6.6 0-12.6-4-14.9-10.2-18.1-48-64.3-82.2-118.5-82.8C58.70324 370.3 0.50324 427.6 0.00324 498.1-0.49676 569.2 57.00324 627 128.00324 627c56.7 0 104.8-36.9 121.6-87.9 2.2-6.6 8.3-11.1 15.2-11.1h92c7.6 0 14.2 5.4 15.7 12.9 9.5 46.7 33.5 88 67 119.2 5.4 5 6.6 13.2 2.9 19.6l-21.7 37.6c-3.7 6.3-11.1 9.4-18.2 7.4-11.1-3.1-22.7-4.7-34.8-4.7-69.7 0.1-127 56.8-127.8 126.6-0.8 71.7 57.4 130 129.1 129.4 69.5-0.6 126.3-57.3 126.9-126.8 0.3-28-8.5-53.9-23.5-75.1-3.6-5.1-3.9-11.8-0.8-17.2l24.9-43.1c3.9-6.7 12-9.7 19.3-7 23.7 8.6 49.3 13.2 76 13.2 34.9 0 67.9-8 97.3-22.2 7.6-3.7 16.7-0.9 20.9 6.4l37 64c-26.3 23.5-43 57.7-43 95.8 0 70.9 58 128.5 128.9 128 69.7-0.5 126.2-56.7 127.1-126.3 0.9-70.1-57-129.3-127.1-129.7-6.2 0-12.3 0.4-18.3 1.2-6.5 0.9-12.8-2.2-16.1-7.8l-39.2-67.9c-3.4-5.9-2.7-13.3 1.7-18.4 34.2-39.3 54.9-90.7 54.9-147 0-38.9-9.9-75.5-27.4-107.4-3.4-6.2-2.2-13.9 2.8-18.9l28.4-28.4c4.9-4.9 12.4-6 18.7-2.9 17.4 8.6 36.9 13.5 57.6 13.5z m0-192c35.3 0 64 28.7 64 64s-28.7 64-64 64-64-28.7-64-64 28.7-64 64-64zM128.00324 563c-35.3 0-64-28.7-64-64s28.7-64 64-64 64 28.7 64 64-28.7 64-64 64z m240 349c-35.3 0-64-28.7-64-64s28.7-64 64-64 64 28.7 64 64-28.7 64-64 64z m464-112c35.3 0 64 28.7 64 64s-28.7 64-64 64-64-28.7-64-64 28.7-64 64-64zM416.00324 224c-35.3 0-64-28.7-64-64s28.7-64 64-64 64 28.7 64 64-28.7 64-64 64z m289.1 385.1C674.90324 639.4 634.70324 656 592.00324 656s-82.9-16.6-113.1-46.9C448.60324 578.9 432.00324 538.7 432.00324 496s16.6-82.9 46.9-113.1C509.10324 352.6 549.30324 336 592.00324 336s82.9 16.6 113.1 46.9C735.40324 413.1 752.00324 453.3 752.00324 496s-16.6 82.9-46.9 113.1z" p-id="3270" fill="currentColor"></path></svg>`,
settings: `<svg t="1679297692375" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1573" width="24" height="24"><path d="M913.94086 431.828053c-4.878101-25.783223-20.693298-42.455951-40.947598-42.455951l-3.511987 0c-54.731532 0-99.248422-44.515866-99.248422-99.262748 0-17.294898 8.299013-37.015032 8.619308-37.798884 10.097986-22.722514 2.349511-50.567699-18.078751-64.859193l-102.701057-57.183374-1.509377-0.738827c-20.545942-8.909927-48.667419-3.207042-63.987337 12.753465-11.086499 11.434423-49.306986 44.037982-78.471213 44.037982-29.543873 0-67.849294-33.257451-78.992075-44.908816-15.293311-16.077164-43.12417-22.112624-63.902402-13.218046l-106.35733 58.272171-1.596358 1.016143c-20.430308 14.234189-28.207435 42.078351-18.165732 64.713884 0.346901 0.827855 8.676613 20.387329 8.676613 37.914518 0 54.746882-44.51689 99.262748-99.247398 99.262748l-4.149507 0c-19.617803 0-35.434024 16.671705-40.309054 42.455951-0.363274 1.814322-8.590656 45.446052-8.590656 80.429821 0 34.938744 8.227382 78.555124 8.590656 80.355119 4.875031 25.799596 20.691251 42.48665 40.946574 42.48665l3.511987 0c54.730509 0 99.247398 44.51689 99.247398 99.247398 0 17.411555-8.328689 37.058011-8.647961 37.812187-10.069333 22.766516-2.349511 50.567699 18.021445 64.787562l100.756775 56.531528 1.538029 0.696872c20.836561 9.17087 49.01432 3.191692 64.250326-13.464663 14.07353-15.207353 52.207036-46.782489 80.20981-46.782489 30.354332 0 69.445652 35.347043 80.706113 47.76691 10.387581 11.376095 26.349111 18.22713 42.687218 18.22713 7.631818 0 14.857383-1.511423 21.474081-4.354168l104.4724-57.574277 1.538029-0.989537c20.428262-14.276145 28.206412-42.077328 18.138102-64.727187-0.348947-0.842181-8.677637-20.402679-8.677637-37.928844 0-54.730509 44.51689-99.247398 99.248422-99.247398l4.093225 0c19.644409 0 35.488259-16.687054 40.365336-42.48665 0.347924-1.799996 8.588609-45.416376 8.588609-80.355119C922.529469 477.274104 914.288784 433.642374 913.94086 431.828053M862.982258 512.257873c0 22.605857-4.498454 51.655474-6.559393 63.785745-82.09781 6.732331-145.738245 75.335802-145.738245 158.303422 0 23.419386 7.430226 45.851281 11.377118 56.169277l-89.12076 49.216935c-4.38282-4.584412-17.325597-17.644869-34.939767-30.762631-30.93557-22.925129-60.595077-34.635845-88.106664-34.635845-27.278273 0-56.703443 11.420097-87.493703 34.05563-17.528212 12.768815-30.296003 25.479301-34.765805 30.18037l-85.724407-47.997154c4.179183-10.839883 11.405771-32.982182 11.405771-56.226582 0-82.96762-63.640436-151.571091-145.70857-158.303422-2.089591-12.130272-6.588045-41.178865-6.588045-63.785745 0-22.650883 4.498454-51.713802 6.588045-63.844074 82.068134-6.718005 145.70857-75.335802 145.70857-158.303422 0-23.288402-7.429203-45.792952-11.376095-56.095599l91.325985-50.190099c3.975545 3.976568 17.005302 16.730033 34.82311 29.411867 30.355355 21.663392 59.260685 32.633235 86.016049 32.633235 26.494421 0 55.19509-10.766205 85.346807-32.009018 17.963117-12.623505 30.964222-25.203008 34.736129-28.757974l87.900979 48.825009c-3.975545 10.244318-11.405771 32.676214-11.405771 56.18258 0 82.96762 63.640436 151.585417 145.738245 158.303422C858.483804 460.5717 862.982258 489.7523 862.982258 512.257873" fill="currentColor" p-id="1574"></path><path d="M510.215866 365.633445c-80.530105 0-146.056494 65.527412-146.056494 146.057517 0 80.543408 65.527412 146.043191 146.056494 146.043191 80.530105 0 146.057517-65.499783 146.057517-146.043191C656.273383 431.160857 590.74597 365.633445 510.215866 365.633445M596.725148 511.690962c0 47.693232-38.799678 86.491887-86.509283 86.491887-47.708582 0-86.479607-38.798655-86.479607-86.491887 0-47.665603 38.771025-86.479607 86.479607-86.479607C557.925471 425.212378 596.725148 464.025359 596.725148 511.690962" fill="currentColor" p-id="1575"></path></svg>`,
addon: `<svg t="1651124422933" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3269" width="24" height="24"><path d="M896.00324 352c70.7 0 128-57.3 128-128 0-70.6-57.4-128-128-128-70.7 0-128 57.3-128 128 0 18.8 4.1 36.7 11.3 52.8 2.7 6 1.4 13.1-3.3 17.8l-24.2 24.2c-5.7 5.7-14.9 6.3-21.2 1.2-38.1-30.1-86.3-48-138.6-48-18.8 0-37.1 2.3-54.6 6.7-6.9 1.7-14.1-1.4-17.7-7.5l-6.6-11.4c-3.4-5.8-2.7-13.1 1.6-18.3 18.6-22.6 29.7-51.6 29.3-83.2C543.10324 89 486.30324 32.6 417.00324 32c-70.6-0.6-128.1 56.1-129 126.3-0.9 69.5 56.5 128.6 126 129.6 9.4 0.1 18.5-0.7 27.4-2.5 6.8-1.4 13.6 1.7 17.1 7.7l2.2 3.8c4 7 2.2 15.9-4.2 20.7-42.4 32.3-73 79.4-84 133.6-1.5 7.4-8.1 12.7-15.7 12.7h-94.1c-6.6 0-12.6-4-14.9-10.2-18.1-48-64.3-82.2-118.5-82.8C58.70324 370.3 0.50324 427.6 0.00324 498.1-0.49676 569.2 57.00324 627 128.00324 627c56.7 0 104.8-36.9 121.6-87.9 2.2-6.6 8.3-11.1 15.2-11.1h92c7.6 0 14.2 5.4 15.7 12.9 9.5 46.7 33.5 88 67 119.2 5.4 5 6.6 13.2 2.9 19.6l-21.7 37.6c-3.7 6.3-11.1 9.4-18.2 7.4-11.1-3.1-22.7-4.7-34.8-4.7-69.7 0.1-127 56.8-127.8 126.6-0.8 71.7 57.4 130 129.1 129.4 69.5-0.6 126.3-57.3 126.9-126.8 0.3-28-8.5-53.9-23.5-75.1-3.6-5.1-3.9-11.8-0.8-17.2l24.9-43.1c3.9-6.7 12-9.7 19.3-7 23.7 8.6 49.3 13.2 76 13.2 34.9 0 67.9-8 97.3-22.2 7.6-3.7 16.7-0.9 20.9 6.4l37 64c-26.3 23.5-43 57.7-43 95.8 0 70.9 58 128.5 128.9 128 69.7-0.5 126.2-56.7 127.1-126.3 0.9-70.1-57-129.3-127.1-129.7-6.2 0-12.3 0.4-18.3 1.2-6.5 0.9-12.8-2.2-16.1-7.8l-39.2-67.9c-3.4-5.9-2.7-13.3 1.7-18.4 34.2-39.3 54.9-90.7 54.9-147 0-38.9-9.9-75.5-27.4-107.4-3.4-6.2-2.2-13.9 2.8-18.9l28.4-28.4c4.9-4.9 12.4-6 18.7-2.9 17.4 8.6 36.9 13.5 57.6 13.5z m0-192c35.3 0 64 28.7 64 64s-28.7 64-64 64-64-28.7-64-64 28.7-64 64-64zM128.00324 563c-35.3 0-64-28.7-64-64s28.7-64 64-64 64 28.7 64 64-28.7 64-64 64z m240 349c-35.3 0-64-28.7-64-64s28.7-64 64-64 64 28.7 64 64-28.7 64-64 64z m464-112c35.3 0 64 28.7 64 64s-28.7 64-64 64-64-28.7-64-64 28.7-64 64-64zM416.00324 224c-35.3 0-64-28.7-64-64s28.7-64 64-64 64 28.7 64 64-28.7 64-64 64z m289.1 385.1C674.90324 639.4 634.70324 656 592.00324 656s-82.9-16.6-113.1-46.9C448.60324 578.9 432.00324 538.7 432.00324 496s16.6-82.9 46.9-113.1C509.10324 352.6 549.30324 336 592.00324 336s82.9 16.6 113.1 46.9C735.40324 413.1 752.00324 453.3 752.00324 496s-16.6 82.9-46.9 113.1z" p-id="3270" fill="currentColor"></path></svg>`,
linkAfter: `
<!-- Generator: Adobe Illustrator 26.5.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
@ -80,10 +80,10 @@ export const ICONS = {
<text transform="matrix(1 0 0 1 5.9615 16.5714)" class="st3 st4">...</text>
</svg>
`,
previewImage: `<svg t="1679666418098" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4921" width="16" height="16"><path d="M512 819.2c-118.784 0-235.52-55.296-346.112-147.456-38.912-32.768-71.68-65.536-102.4-100.352-10.24-12.288-20.48-22.528-28.672-32.768-4.096-6.144-8.192-10.24-10.24-12.288l-8.192-12.288 8.192-12.288c2.048-2.048 6.144-6.144 10.24-12.288 8.192-10.24 18.432-20.48 28.672-32.768 30.72-34.816 65.536-67.584 102.4-100.352 110.592-92.16 227.328-147.456 346.112-147.456s235.52 55.296 346.112 147.456c38.912 32.768 71.68 65.536 102.4 100.352 10.24 12.288 20.48 22.528 28.672 32.768 4.096 6.144 8.192 10.24 10.24 12.288l8.192 12.288-8.192 12.288c-2.048 2.048-6.144 6.144-10.24 12.288-8.192 10.24-18.432 20.48-28.672 32.768-30.72 34.816-65.536 67.584-102.4 100.352-110.592 92.16-227.328 147.456-346.112 147.456z m-417.792-276.48c28.672 32.768 61.44 65.536 98.304 96.256 102.4 86.016 210.944 139.264 319.488 139.264s217.088-51.2 319.488-139.264c36.864-30.72 69.632-63.488 98.304-96.256 10.24-12.288 18.432-22.528 26.624-30.72-8.192-10.24-16.384-20.48-26.624-30.72-28.672-32.768-61.44-65.536-98.304-96.256-102.4-86.016-210.944-139.264-319.488-139.264s-217.088 51.2-319.488 139.264c-36.864 30.72-69.632 63.488-98.304 96.256-10.24 12.288-18.432 22.528-26.624 30.72 6.144 10.24 16.384 20.48 26.624 30.72z m417.792 174.08c-112.64 0-204.8-92.16-204.8-204.8s92.16-204.8 204.8-204.8 204.8 92.16 204.8 204.8-92.16 204.8-204.8 204.8z m0-40.96c90.112 0 163.84-73.728 163.84-163.84s-73.728-163.84-163.84-163.84-163.84 73.728-163.84 163.84 73.728 163.84 163.84 163.84z" fill="currentColor" p-id="4922"></path></svg>`,
resizeImage: `<svg t="1679666293914" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3864" width="16" height="16"><path d="M1024 288h-64V64h-224V0h288v288z" fill="currentColor" p-id="3865"></path><path d="M551.264 427.904L969.6 9.536l45.248 45.248-418.368 418.4zM288 1024H0v-288h64v224h224v64z" fill="currentColor" p-id="3866"></path><path d="M9.28 969.216L427.424 551.04l45.248 45.248L54.528 1014.464zM192 352H128V128h224v64H192v160zM128 416h64v192H128zM416 128h192v64h-192zM896 896h-224v-64h160v-160h64v224zM832 416h64v192h-64zM416 832h192v64h-192z" fill="currentColor" p-id="3867"></path></svg>`,
imageViewerPin: `<svg t="1668685819555" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1445" width="18" height="18"><path d="M631.637333 178.432a64 64 0 0 1 19.84 13.504l167.616 167.786667a64 64 0 0 1-19.370666 103.744l-59.392 26.304-111.424 111.552-8.832 122.709333a64 64 0 0 1-109.098667 40.64l-108.202667-108.309333-184.384 185.237333-45.354666-45.162667 184.490666-185.344-111.936-112.021333a64 64 0 0 1 40.512-109.056l126.208-9.429333 109.44-109.568 25.706667-59.306667a64 64 0 0 1 84.181333-33.28z m-25.450666 58.730667l-30.549334 70.464-134.826666 135.04-149.973334 11.157333 265.408 265.6 10.538667-146.474667 136.704-136.874666 70.336-31.146667-167.637333-167.765333z" p-id="1446"></path></svg>`,
imageViewerPined: `<svg t="1668685864340" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1624" width="18" height="18"><path d="M631.637333 178.432a64 64 0 0 1 19.84 13.504l167.616 167.786667a64 64 0 0 1-19.370666 103.744l-59.392 26.304-111.424 111.552-8.832 122.709333a64 64 0 0 1-109.098667 40.64l-108.202667-108.309333-184.384 185.237333-45.354666-45.162667 184.490666-185.344-111.936-112.021333a64 64 0 0 1 40.512-109.056l126.208-9.429333 109.44-109.568 25.706667-59.306667a64 64 0 0 1 84.181333-33.28z" p-id="1625"></path></svg>`,
previewImage: `<svg t="1679666418098" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4921" width="16" height="16"><path d="M512 819.2c-118.784 0-235.52-55.296-346.112-147.456-38.912-32.768-71.68-65.536-102.4-100.352-10.24-12.288-20.48-22.528-28.672-32.768-4.096-6.144-8.192-10.24-10.24-12.288l-8.192-12.288 8.192-12.288c2.048-2.048 6.144-6.144 10.24-12.288 8.192-10.24 18.432-20.48 28.672-32.768 30.72-34.816 65.536-67.584 102.4-100.352 110.592-92.16 227.328-147.456 346.112-147.456s235.52 55.296 346.112 147.456c38.912 32.768 71.68 65.536 102.4 100.352 10.24 12.288 20.48 22.528 28.672 32.768 4.096 6.144 8.192 10.24 10.24 12.288l8.192 12.288-8.192 12.288c-2.048 2.048-6.144 6.144-10.24 12.288-8.192 10.24-18.432 20.48-28.672 32.768-30.72 34.816-65.536 67.584-102.4 100.352-110.592 92.16-227.328 147.456-346.112 147.456z m-417.792-276.48c28.672 32.768 61.44 65.536 98.304 96.256 102.4 86.016 210.944 139.264 319.488 139.264s217.088-51.2 319.488-139.264c36.864-30.72 69.632-63.488 98.304-96.256 10.24-12.288 18.432-22.528 26.624-30.72-8.192-10.24-16.384-20.48-26.624-30.72-28.672-32.768-61.44-65.536-98.304-96.256-102.4-86.016-210.944-139.264-319.488-139.264s-217.088 51.2-319.488 139.264c-36.864 30.72-69.632 63.488-98.304 96.256-10.24 12.288-18.432 22.528-26.624 30.72 6.144 10.24 16.384 20.48 26.624 30.72z m417.792 174.08c-112.64 0-204.8-92.16-204.8-204.8s92.16-204.8 204.8-204.8 204.8 92.16 204.8 204.8-92.16 204.8-204.8 204.8z m0-40.96c90.112 0 163.84-73.728 163.84-163.84s-73.728-163.84-163.84-163.84-163.84 73.728-163.84 163.84 73.728 163.84 163.84 163.84z" fill="currentColor" p-id="4922"></path></svg>`,
resizeImage: `<svg t="1679666293914" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3864" width="16" height="16"><path d="M1024 288h-64V64h-224V0h288v288z" fill="currentColor" p-id="3865"></path><path d="M551.264 427.904L969.6 9.536l45.248 45.248-418.368 418.4zM288 1024H0v-288h64v224h224v64z" fill="currentColor" p-id="3866"></path><path d="M9.28 969.216L427.424 551.04l45.248 45.248L54.528 1014.464zM192 352H128V128h224v64H192v160zM128 416h64v192H128zM416 128h192v64h-192zM896 896h-224v-64h160v-160h64v224zM832 416h64v192h-64zM416 832h192v64h-192z" fill="currentColor" p-id="3867"></path></svg>`,
imageViewerPin: `<svg t="1668685819555" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1445" width="18" height="18"><path d="M631.637333 178.432a64 64 0 0 1 19.84 13.504l167.616 167.786667a64 64 0 0 1-19.370666 103.744l-59.392 26.304-111.424 111.552-8.832 122.709333a64 64 0 0 1-109.098667 40.64l-108.202667-108.309333-184.384 185.237333-45.354666-45.162667 184.490666-185.344-111.936-112.021333a64 64 0 0 1 40.512-109.056l126.208-9.429333 109.44-109.568 25.706667-59.306667a64 64 0 0 1 84.181333-33.28z m-25.450666 58.730667l-30.549334 70.464-134.826666 135.04-149.973334 11.157333 265.408 265.6 10.538667-146.474667 136.704-136.874666 70.336-31.146667-167.637333-167.765333z" p-id="1446"></path></svg>`,
imageViewerPined: `<svg t="1668685864340" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1624" width="18" height="18"><path d="M631.637333 178.432a64 64 0 0 1 19.84 13.504l167.616 167.786667a64 64 0 0 1-19.370666 103.744l-59.392 26.304-111.424 111.552-8.832 122.709333a64 64 0 0 1-109.098667 40.64l-108.202667-108.309333-184.384 185.237333-45.354666-45.162667 184.490666-185.344-111.936-112.021333a64 64 0 0 1 40.512-109.056l126.208-9.429333 109.44-109.568 25.706667-59.306667a64 64 0 0 1 84.181333-33.28z" p-id="1625"></path></svg>`,
switchOutline: `<svg
t="1652006549395"
class="icon tool-icon"
@ -150,10 +150,10 @@ export const ICONS = {
workspace_preview_open: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g><rect x="2.38" y=".38" width="11.25" height="15.25" style="fill:#678ac5;"/><path d="M13.25,.75V15.25H2.75V.75H13.25m.75-.75H2V16H14V0h0Z" style="fill:#678ac5;"/></g><path d="M8,5.6c-2.4,0-4,2.4-4,2.4,0,0,1.6,2.4,4,2.4s4-2.4,4-2.4c0,0-1.6-2.4-4-2.4Zm0,3.98c-.88,0-1.59-.71-1.59-1.59s.71-1.59,1.59-1.59,1.59,.71,1.59,1.59c0,.88-.71,1.59-1.59,1.59Zm-.8-1.59c0,.21,.09,.42,.23,.56s.35,.23,.56,.23,.42-.09,.56-.23c.15-.15,.23-.35,.23-.56s-.09-.42-.23-.56c-.15-.15-.35-.23-.56-.23s-.42,.09-.56,.23c-.15,.15-.23,.35-.23,.56Z" style="fill:#fff;"/></svg>`,
workspace_notes_collapsed: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g><path d="M2.38,15.62V.38H12c.9,0,1.62,.73,1.62,1.62V14c0,.9-.73,1.62-1.62,1.62H2.38Z" style="fill:#fff;"/><path d="M12,.75c.69,0,1.25,.56,1.25,1.25V14c0,.69-.56,1.25-1.25,1.25H2.75V.75H12m0-.75H2V16H12c1.1,0,2-.9,2-2V2c0-1.1-.9-2-2-2h0Z" style="fill:#678ac5;"/></g><path d="M12.15,10.77l-1.34-1.75c.48-.45,.77-1.09,.77-1.8,0-1.37-1.12-2.49-2.49-2.49s-2.49,1.12-2.49,2.49,1.12,2.49,2.49,2.49c.44,0,.86-.12,1.22-.32l1.35,1.76c.06,.08,.15,.12,.25,.12,.07,0,.13-.02,.19-.06,.14-.1,.16-.3,.06-.43h0Zm-4.97-3.55c0-1.05,.86-1.91,1.92-1.91s1.92,.86,1.92,1.91-.86,1.91-1.92,1.91-1.92-.86-1.92-1.91h0Zm-1.21-1.25h-1.87c-.17,0-.31-.14-.31-.31s.14-.31,.31-.31h1.87c.17,0,.31,.14,.31,.31s-.14,.31-.31,.31h0Zm-.08,3.07h-1.79c-.17,0-.31-.14-.31-.31s.14-.31,.31-.31h1.79c.17,0,.31,.14,.31,.31s-.14,.31-.31,.31h0Zm-.37-1.54h-1.42c-.17,0-.31-.14-.31-.31s.14-.31,.31-.31h1.42c.17,0,.31,.14,.31,.31,0,.17-.14,.31-.31,.31h0Zm2,3.13h-3.43c-.17,0-.31-.14-.31-.31s.14-.31,.31-.31h3.43c.17,0,.31,.14,.31,.31,0,.17-.14,.31-.31,.31h0Z" style="fill:#678ac5;"/></svg>`,
workspace_notes_open: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g><path d="M2.38,15.62V.38H12c.9,0,1.62,.73,1.62,1.62V14c0,.9-.73,1.62-1.62,1.62H2.38Z" style="fill:#678ac5;"/><path d="M12,.75c.69,0,1.25,.56,1.25,1.25V14c0,.69-.56,1.25-1.25,1.25H2.75V.75H12m0-.75H2V16H12c1.1,0,2-.9,2-2V2c0-1.1-.9-2-2-2h0Z" style="fill:#678ac5;"/></g><path d="M12.15,10.77l-1.34-1.75c.48-.45,.77-1.09,.77-1.8,0-1.37-1.12-2.49-2.49-2.49s-2.49,1.12-2.49,2.49,1.12,2.49,2.49,2.49c.44,0,.86-.12,1.22-.32l1.35,1.76c.06,.08,.15,.12,.25,.12,.07,0,.13-.02,.19-.06,.14-.1,.16-.3,.06-.43h0Zm-4.97-3.55c0-1.05,.86-1.91,1.92-1.91s1.92,.86,1.92,1.91-.86,1.91-1.92,1.91-1.92-.86-1.92-1.91h0Zm-1.21-1.25h-1.87c-.17,0-.31-.14-.31-.31s.14-.31,.31-.31h1.87c.17,0,.31,.14,.31,.31s-.14,.31-.31,.31h0Zm-.08,3.07h-1.79c-.17,0-.31-.14-.31-.31s.14-.31,.31-.31h1.79c.17,0,.31,.14,.31,.31s-.14,.31-.31,.31h0Zm-.37-1.54h-1.42c-.17,0-.31-.14-.31-.31s.14-.31,.31-.31h1.42c.17,0,.31,.14,.31,.31,0,.17-.14,.31-.31,.31h0Zm2,3.13h-3.43c-.17,0-.31-.14-.31-.31s.14-.31,.31-.31h3.43c.17,0,.31,.14,.31,.31,0,.17-.14,.31-.31,.31h0Z" style="fill:#fff;"/></svg>`,
readerQuickNote: `<svg t="1651630304116" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14011" width="16" height="16"><path d="M791.30324 369.7c-5 5-6.2 12.7-2.8 18.9 17.5 31.9 27.4 68.5 27.4 107.4 0 56.2-20.7 107.6-54.9 147-4.5 5.1-5.1 12.6-1.8 18.4l39.2 67.9c3.3 5.7 9.6 8.7 16.1 7.8 6-0.8 12.1-1.2 18.3-1.2 70.1 0.5 128 59.7 127.1 129.7-0.9 69.7-57.4 125.9-127.1 126.4-70.9 0.5-128.9-57.1-128.9-128 0-38.1 16.7-72.3 43.1-95.8l-37-64c-4.2-7.3-13.3-10-20.9-6.4-29.3 14.2-62.3 22.2-97.2 22.2-26.7 0-52.3-4.7-76-13.2-7.3-2.6-15.4 0.3-19.3 7l-24.9 43.1c-3.1 5.4-2.8 12.1 0.8 17.2 15 21.2 23.7 47.1 23.5 75.1-0.7 69.5-57.5 126.2-127 126.8-71.6 0.6-129.8-57.7-129.1-129.4 0.8-69.7 58-126.5 127.8-126.6 12 0 23.7 1.6 34.8 4.7 7 2 14.5-1.1 18.2-7.4l21.7-37.6c3.7-6.4 2.5-14.6-2.9-19.6-33.6-31.2-57.5-72.6-67-119.2-1.5-7.5-8-12.9-15.7-12.9h-92c-6.9 0-13.1 4.5-15.2 11.1C232.80324 590.2 184.70324 627 128.00324 627 57.00324 627-0.49676 569.2 0.00324 498.1 0.40324 427.5 58.60324 370.3 129.20324 371c54.2 0.5 100.4 34.8 118.5 82.8C250.00324 460 256.00324 464 262.60324 464h94.1c7.6 0 14.2-5.3 15.7-12.7 11-54.2 41.5-101.3 84-133.6 6.4-4.9 8.2-13.8 4.2-20.8l-2.2-3.8c-3.5-6-10.3-9-17.1-7.7-8.8 1.8-18 2.7-27.4 2.5-69.5-1-126.9-60.1-126-129.6 0.9-70.3 58.4-126.9 129-126.3 69.3 0.6 126 57 127 126.2 0.4 31.6-10.6 60.7-29.3 83.2-4.3 5.2-5 12.5-1.6 18.3l6.6 11.4c3.6 6.2 10.8 9.3 17.7 7.5 17.5-4.4 35.8-6.7 54.6-6.7 52.3 0 100.4 17.9 138.6 48 6.4 5 15.5 4.5 21.2-1.2l24.2-24.2c4.7-4.7 6-11.8 3.3-17.8-7.3-16.1-11.3-34-11.3-52.8 0-70.7 57.3-128 128-128 70.6 0 128 57.4 128 128 0 70.7-57.3 128-128 128-20.7 0-40.2-4.9-57.5-13.6-6.2-3.1-13.7-2-18.7 2.9l-28.4 28.5z" p-id="14012" fill="#ffd400"></path></svg>`,
embedLinkContent: `<svg t="1652008007954" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10521" width="16" height="16"><path d="M574.3 896H159.7c-17.6 0-31.9-14.3-31.9-32V160c0-17.7 14.3-32 31.9-32h382.7v160c0 35.3 28.6 64 63.8 64h159.5v192c0 17.7 14.3 32 31.9 32 17.6 0 31.9-14.3 31.9-32V270.2c0-8.5-3.3-16.6-9.3-22.6L647.4 73.4c-6-6-14.1-9.4-22.6-9.4h-497C92.6 64 64 92.7 64 128v768c0 35.3 28.6 64 63.8 64h446.5c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM638.1 288c-17.6 0-31.9-14.3-31.9-32V128l159.5 160H638.1z" p-id="10522" style="fill: currentColor;"></path><path d="M418.8 673H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32h193.3c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM608.2 481H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32h382.7c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM225.5 353h191.4c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32zM862.7 959.4c-23.6 0-47-8.8-64.8-26.6l-24.4-24.4c-12.5-12.5-12.5-32.8 0-45.3s32.7-12.5 45.1 0l24.4 24.4c11.3 11.4 30.7 10.4 43.2-2.1 12.5-12.5 13.4-31.9 2.1-43.3L749.2 702.6c-11.3-11.4-30.7-10.4-43.2 2.1-6.2 6.3-9.8 14.4-10 22.8-0.2 7.9 2.6 15.1 7.9 20.4 12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.1 0c-36.2-36.3-35.2-96.3 2.1-133.8 37.4-37.5 97.2-38.4 133.4-2.1l139.1 139.5c36.2 36.3 35.2 96.3-2.1 133.8-19 19.2-43.9 28.8-68.6 28.8z" p-id="10523" style="fill: currentColor;"></path><path d="M696.3 883.1c-23.6 0-47-8.8-64.8-26.6l-139-139.6c-17.7-17.8-27.2-41.7-26.6-67.2 0.6-25 10.8-48.6 28.7-66.6 17.9-17.9 41.5-28.2 66.4-28.8 25.5-0.6 49.3 8.9 67 26.6l24.4 24.4c12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.1 0l-24.4-24.4c-5.3-5.3-12.5-8.1-20.4-7.9-8.4 0.2-16.5 3.8-22.8 10-6.2 6.3-9.8 14.4-10 22.8-0.2 7.9 2.6 15.1 7.9 20.4L676.7 811c11.3 11.4 30.7 10.4 43.2-2.1 12.5-12.5 13.4-31.9 2.1-43.3-12.5-12.5-12.5-32.8 0-45.3s32.7-12.5 45.1 0c36.2 36.3 35.3 96.3-2.1 133.8-19.1 19.3-44 29-68.7 29z" p-id="10524" style="fill: currentColor;"></path></svg>`,
updateLinkText: `<svg t="1652685521153" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7063" width="16" height="16"><path d="M271.914667 837.418667C182.314667 756.522667 128 637.653333 128 508.714667 128 304.896 263.338667 129.834667 450.986667 85.333333L469.333333 170.026667c-150.016 35.584-258.304 175.658667-258.304 338.688 0 106.069333 45.866667 203.562667 121.258667 268.373333L426.666667 682.666667v256H170.666667l101.248-101.248zM727.082667 168.917333C831.530667 249.045333 896 377.088 896 517.077333c0 202.922667-135.338667 377.258667-322.986667 421.589334L554.666667 854.357333c150.016-35.456 258.304-174.933333 258.304-337.322666 0-117.12-56.405333-223.786667-146.901334-287.146667L554.666667 341.333333V85.333333h256l-83.584 83.584z" p-id="7064" style="fill: currentColor;"></path></svg>`,
openInNewWindow: `<svg t="1665756319579" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9123" width="16" height="16"><path d="M808 896h-592c-48 0-88-41.6-88-92.8V220.8C128 169.6 168 128 216 128h353.6c17.6 0 32 14.4 32 32s-14.4 32-32 32H216c-12.8 0-24 12.8-24 28.8v580.8c0 16 11.2 28.8 24 28.8h592c12.8 0 24-12.8 24-28.8V480c0-17.6 14.4-32 32-32s32 14.4 32 32v323.2c0 51.2-40 92.8-88 92.8z" p-id="9124" style="fill: currentColor;"></path><path d="M416 704c-17.6 0-32-14.4-32-32 0-281.6 230.4-512 512-512 17.6 0 32 14.4 32 32s-14.4 32-32 32c-246.4 0-448 201.6-448 448 0 17.6-14.4 32-32 32z" p-id="9125" style="fill: currentColor;"></path><path d="M784 417.6c-4.8 0-11.2-1.6-16-4.8-16-9.6-20.8-28.8-11.2-43.2l96-166.4-166.4-96c-16-9.6-20.8-28.8-11.2-43.2 9.6-16 28.8-20.8 43.2-11.2l193.6 112c16 9.6 20.8 28.8 11.2 43.2l-112 193.6c-4.8 11.2-16 16-27.2 16z" p-id="9126" style="fill: currentColor;"></path></svg>`,
readerQuickNote: `<svg t="1651630304116" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14011" width="16" height="16"><path d="M791.30324 369.7c-5 5-6.2 12.7-2.8 18.9 17.5 31.9 27.4 68.5 27.4 107.4 0 56.2-20.7 107.6-54.9 147-4.5 5.1-5.1 12.6-1.8 18.4l39.2 67.9c3.3 5.7 9.6 8.7 16.1 7.8 6-0.8 12.1-1.2 18.3-1.2 70.1 0.5 128 59.7 127.1 129.7-0.9 69.7-57.4 125.9-127.1 126.4-70.9 0.5-128.9-57.1-128.9-128 0-38.1 16.7-72.3 43.1-95.8l-37-64c-4.2-7.3-13.3-10-20.9-6.4-29.3 14.2-62.3 22.2-97.2 22.2-26.7 0-52.3-4.7-76-13.2-7.3-2.6-15.4 0.3-19.3 7l-24.9 43.1c-3.1 5.4-2.8 12.1 0.8 17.2 15 21.2 23.7 47.1 23.5 75.1-0.7 69.5-57.5 126.2-127 126.8-71.6 0.6-129.8-57.7-129.1-129.4 0.8-69.7 58-126.5 127.8-126.6 12 0 23.7 1.6 34.8 4.7 7 2 14.5-1.1 18.2-7.4l21.7-37.6c3.7-6.4 2.5-14.6-2.9-19.6-33.6-31.2-57.5-72.6-67-119.2-1.5-7.5-8-12.9-15.7-12.9h-92c-6.9 0-13.1 4.5-15.2 11.1C232.80324 590.2 184.70324 627 128.00324 627 57.00324 627-0.49676 569.2 0.00324 498.1 0.40324 427.5 58.60324 370.3 129.20324 371c54.2 0.5 100.4 34.8 118.5 82.8C250.00324 460 256.00324 464 262.60324 464h94.1c7.6 0 14.2-5.3 15.7-12.7 11-54.2 41.5-101.3 84-133.6 6.4-4.9 8.2-13.8 4.2-20.8l-2.2-3.8c-3.5-6-10.3-9-17.1-7.7-8.8 1.8-18 2.7-27.4 2.5-69.5-1-126.9-60.1-126-129.6 0.9-70.3 58.4-126.9 129-126.3 69.3 0.6 126 57 127 126.2 0.4 31.6-10.6 60.7-29.3 83.2-4.3 5.2-5 12.5-1.6 18.3l6.6 11.4c3.6 6.2 10.8 9.3 17.7 7.5 17.5-4.4 35.8-6.7 54.6-6.7 52.3 0 100.4 17.9 138.6 48 6.4 5 15.5 4.5 21.2-1.2l24.2-24.2c4.7-4.7 6-11.8 3.3-17.8-7.3-16.1-11.3-34-11.3-52.8 0-70.7 57.3-128 128-128 70.6 0 128 57.4 128 128 0 70.7-57.3 128-128 128-20.7 0-40.2-4.9-57.5-13.6-6.2-3.1-13.7-2-18.7 2.9l-28.4 28.5z" p-id="14012" fill="#ffd400"></path></svg>`,
embedLinkContent: `<svg t="1652008007954" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10521" width="16" height="16"><path d="M574.3 896H159.7c-17.6 0-31.9-14.3-31.9-32V160c0-17.7 14.3-32 31.9-32h382.7v160c0 35.3 28.6 64 63.8 64h159.5v192c0 17.7 14.3 32 31.9 32 17.6 0 31.9-14.3 31.9-32V270.2c0-8.5-3.3-16.6-9.3-22.6L647.4 73.4c-6-6-14.1-9.4-22.6-9.4h-497C92.6 64 64 92.7 64 128v768c0 35.3 28.6 64 63.8 64h446.5c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM638.1 288c-17.6 0-31.9-14.3-31.9-32V128l159.5 160H638.1z" p-id="10522" style="fill: currentColor;"></path><path d="M418.8 673H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32h193.3c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM608.2 481H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32h382.7c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM225.5 353h191.4c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32zM862.7 959.4c-23.6 0-47-8.8-64.8-26.6l-24.4-24.4c-12.5-12.5-12.5-32.8 0-45.3s32.7-12.5 45.1 0l24.4 24.4c11.3 11.4 30.7 10.4 43.2-2.1 12.5-12.5 13.4-31.9 2.1-43.3L749.2 702.6c-11.3-11.4-30.7-10.4-43.2 2.1-6.2 6.3-9.8 14.4-10 22.8-0.2 7.9 2.6 15.1 7.9 20.4 12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.1 0c-36.2-36.3-35.2-96.3 2.1-133.8 37.4-37.5 97.2-38.4 133.4-2.1l139.1 139.5c36.2 36.3 35.2 96.3-2.1 133.8-19 19.2-43.9 28.8-68.6 28.8z" p-id="10523" style="fill: currentColor;"></path><path d="M696.3 883.1c-23.6 0-47-8.8-64.8-26.6l-139-139.6c-17.7-17.8-27.2-41.7-26.6-67.2 0.6-25 10.8-48.6 28.7-66.6 17.9-17.9 41.5-28.2 66.4-28.8 25.5-0.6 49.3 8.9 67 26.6l24.4 24.4c12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.1 0l-24.4-24.4c-5.3-5.3-12.5-8.1-20.4-7.9-8.4 0.2-16.5 3.8-22.8 10-6.2 6.3-9.8 14.4-10 22.8-0.2 7.9 2.6 15.1 7.9 20.4L676.7 811c11.3 11.4 30.7 10.4 43.2-2.1 12.5-12.5 13.4-31.9 2.1-43.3-12.5-12.5-12.5-32.8 0-45.3s32.7-12.5 45.1 0c36.2 36.3 35.3 96.3-2.1 133.8-19.1 19.3-44 29-68.7 29z" p-id="10524" style="fill: currentColor;"></path></svg>`,
updateLinkText: `<svg t="1652685521153" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7063" width="16" height="16"><path d="M271.914667 837.418667C182.314667 756.522667 128 637.653333 128 508.714667 128 304.896 263.338667 129.834667 450.986667 85.333333L469.333333 170.026667c-150.016 35.584-258.304 175.658667-258.304 338.688 0 106.069333 45.866667 203.562667 121.258667 268.373333L426.666667 682.666667v256H170.666667l101.248-101.248zM727.082667 168.917333C831.530667 249.045333 896 377.088 896 517.077333c0 202.922667-135.338667 377.258667-322.986667 421.589334L554.666667 854.357333c150.016-35.456 258.304-174.933333 258.304-337.322666 0-117.12-56.405333-223.786667-146.901334-287.146667L554.666667 341.333333V85.333333h256l-83.584 83.584z" p-id="7064" style="fill: currentColor;"></path></svg>`,
openInNewWindow: `<svg t="1665756319579" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9123" width="16" height="16"><path d="M808 896h-592c-48 0-88-41.6-88-92.8V220.8C128 169.6 168 128 216 128h353.6c17.6 0 32 14.4 32 32s-14.4 32-32 32H216c-12.8 0-24 12.8-24 28.8v580.8c0 16 11.2 28.8 24 28.8h592c12.8 0 24-12.8 24-28.8V480c0-17.6 14.4-32 32-32s32 14.4 32 32v323.2c0 51.2-40 92.8-88 92.8z" p-id="9124" style="fill: currentColor;"></path><path d="M416 704c-17.6 0-32-14.4-32-32 0-281.6 230.4-512 512-512 17.6 0 32 14.4 32 32s-14.4 32-32 32c-246.4 0-448 201.6-448 448 0 17.6-14.4 32-32 32z" p-id="9125" style="fill: currentColor;"></path><path d="M784 417.6c-4.8 0-11.2-1.6-16-4.8-16-9.6-20.8-28.8-11.2-43.2l96-166.4-166.4-96c-16-9.6-20.8-28.8-11.2-43.2 9.6-16 28.8-20.8 43.2-11.2l193.6 112c16 9.6 20.8 28.8 11.2 43.2l-112 193.6c-4.8 11.2-16 16-27.2 16z" p-id="9126" style="fill: currentColor;"></path></svg>`,
};
export const PROGRESS_TITLE = "Better Notes";

42
src/utils/linkNote.ts Normal file
View File

@ -0,0 +1,42 @@
import { config } from "../../package.json";
export { openLinkNoteDialog };
async function openLinkNoteDialog(currentNote: Zotero.Item) {
const io = {
openedNoteIDs: Zotero_Tabs._tabs
.map((tab) => tab.data?.itemID)
.filter((id) => id),
currentNoteID: currentNote.id,
deferred: Zotero.Promise.defer(),
} as any;
window.openDialog(
`chrome://${config.addonRef}/content/linkNote.xhtml`,
"_blank",
"chrome,modal,centerscreen,resizable=no",
io,
);
await io.deferred.promise;
const targetNote = Zotero.Items.get(io.targetNoteID);
const content = io.content;
const lineIndex = io.lineIndex;
if (!targetNote || !content) return;
await addon.api.note.insert(targetNote, content, lineIndex);
await Zotero.DB.executeTransaction(async () => {
const saveParams = {
skipDateModifiedUpdate: true,
skipSelect: true,
notifierData: {
skipBN: true,
},
};
targetNote.addRelatedItem(currentNote);
currentNote.addRelatedItem(targetNote);
targetNote.save(saveParams);
currentNote.save(saveParams);
});
}

View File

@ -4,7 +4,7 @@ import { getEditorInstance, getPositionAtLine, insert } from "./editor";
import { formatPath, getItemDataURL } from "./str";
import { showHint } from "./hint";
import { config } from "../../package.json";
import { getNoteLinkParams } from "./link";
import { getNoteLink, getNoteLinkParams } from "./link";
export {
renderNoteHTML,
@ -111,11 +111,32 @@ function parseHTMLLines(html: string): string[] {
return parsedLines;
}
function getLinesInNote(note: Zotero.Item): string[] {
function getLinesInNote(note: Zotero.Item): string[];
async function getLinesInNote(
note: Zotero.Item,
options: {
convertToHTML?: true;
},
): Promise<string[]>;
function getLinesInNote(
note: Zotero.Item,
options?: {
convertToHTML?: boolean;
},
): string[] | Promise<string[]> {
if (!note) {
return [];
}
const noteText: string = note.getNote();
if (options?.convertToHTML) {
return new Promise((resolve) => {
addon.api.convert.note2html(note).then((html) => {
resolve(parseHTMLLines(html));
});
});
}
return parseHTMLLines(noteText);
}
@ -162,7 +183,7 @@ async function addLineToNote(
const editor = getEditorInstance(note.id);
if (editor && !forceMetadata) {
// The note is opened. Add line via note editor
const pos = getPositionAtLine(editor, lineIndex, "end");
const pos = getPositionAtLine(editor, lineIndex, "start");
ztoolkit.log("Add note line via note editor", pos);
insert(editor, html, pos);
// The selection is automatically moved to the next line
@ -524,24 +545,8 @@ async function updateRelatedNotes(noteID: number) {
ztoolkit.log(`updateRelatedNotes: ${noteID} is not a note.`);
return;
}
const relatedNoteIDs = await getRelatedNoteIds(noteID);
const relatedNotes = Zotero.Items.get(relatedNoteIDs);
const currentRelatedNotes = {} as Record<number, Zotero.Item>;
const { detectedIDSet, currentIDSet } = await getRelatedNoteIds(noteID);
// Get current related items
for (const relItemKey of noteItem.relatedItems) {
try {
const relItem = (await Zotero.Items.getByLibraryAndKeyAsync(
noteItem.libraryID,
relItemKey,
)) as Zotero.Item;
if (relItem.isNote()) {
currentRelatedNotes[relItem.id] = relItem;
}
} catch (e) {
ztoolkit.log(e);
}
}
await Zotero.DB.executeTransaction(async () => {
const saveParams = {
skipDateModifiedUpdate: true,
@ -550,18 +555,18 @@ async function updateRelatedNotes(noteID: number) {
skipBN: true,
},
};
for (const toAddNote of relatedNotes) {
if (toAddNote.id in currentRelatedNotes) {
for (const toAddNote of Zotero.Items.get(Array.from(detectedIDSet))) {
if (currentIDSet.has(toAddNote.id)) {
// Remove existing notes from current dict for later process
delete currentRelatedNotes[toAddNote.id];
currentIDSet.delete(toAddNote.id);
continue;
}
toAddNote.addRelatedItem(noteItem);
noteItem.addRelatedItem(toAddNote);
toAddNote.save(saveParams);
delete currentRelatedNotes[toAddNote.id];
currentIDSet.delete(toAddNote.id);
}
for (const toRemoveNote of Object.values(currentRelatedNotes)) {
for (const toRemoveNote of Zotero.Items.get(Array.from(currentIDSet))) {
// Remove related notes that are not in the new list
toRemoveNote.removeRelatedItem(noteItem);
noteItem.removeRelatedItem(toRemoveNote);
@ -571,21 +576,49 @@ async function updateRelatedNotes(noteID: number) {
});
}
async function getRelatedNoteIds(noteId: number): Promise<number[]> {
let allNoteIds: number[] = [noteId];
async function getRelatedNoteIds(noteId: number) {
let detectedIDs: number[] = [];
const note = Zotero.Items.get(noteId);
const linkMatches = note.getNote().match(/zotero:\/\/note\/\w+\/\w+\//g);
if (!linkMatches) {
return allNoteIds;
}
const subNoteIds = (
await Promise.all(
linkMatches.map(async (link) => getNoteLinkParams(link).noteItem),
const currentIDs: number[] = [];
if (linkMatches) {
const subNoteIds = (
await Promise.all(
linkMatches.map(async (link) => getNoteLinkParams(link).noteItem),
)
)
)
.filter((item) => item && item.isNote())
.map((item) => (item as Zotero.Item).id);
allNoteIds = allNoteIds.concat(subNoteIds);
allNoteIds = new Array(...new Set(allNoteIds));
return allNoteIds;
.filter((item) => item && item.isNote())
.map((item) => (item as Zotero.Item).id);
detectedIDs = detectedIDs.concat(subNoteIds);
}
const currentNoteLink = getNoteLink(note);
if (currentNoteLink) {
// Get current related items
for (const relItemKey of note.relatedItems) {
try {
const relItem = (await Zotero.Items.getByLibraryAndKeyAsync(
note.libraryID,
relItemKey,
)) as Zotero.Item;
// If the related item is a note and contains the current note link
// Add it to the related note list
if (relItem.isNote()) {
if (relItem.getNote().includes(currentNoteLink)) {
detectedIDs.push(relItem.id);
}
currentIDs.push(relItem.id);
}
} catch (e) {
ztoolkit.log(e);
}
}
}
const detectedIDSet = new Set(detectedIDs);
detectedIDSet.delete(noteId);
const currentIDSet = new Set(currentIDs);
return { detectedIDSet, currentIDSet };
}