import { MessageHelper } from "zotero-plugin-toolkit"; import { config } from "../../package.json"; import { getNoteLinkParams } from "./link"; import type { handlers } from "../extras/relationWorker"; function closeRelationServer() { if (addon.data.relation.server) { addon.data.relation.server.destroy(); addon.data.relation.server = undefined; } } async function getRelationServer() { if (!addon.data.relation.server) { const worker = new Worker( `chrome://${config.addonRef}/content/scripts/relationWorker.js`, { name: "relationWorker" }, ); const server = new MessageHelper({ canBeDestroyed: false, dev: __env__ === "development", name: "relationWorkerMain", target: worker, handlers: {}, }); server.start(); await server.exec("_ping"); addon.data.relation.server = server; } return addon.data.relation.server!; } export { getRelationServer, closeRelationServer }; export { updateNoteLinkRelation, getNoteLinkInboundRelation, getNoteLinkOutboundRelation, linkAnnotationToTarget, getLinkTargetByAnnotation, getAnnotationByLinkTarget, }; async function updateNoteLinkRelation(noteID: number) { ztoolkit.log("updateNoteLinkRelation", noteID); const note = Zotero.Items.get(noteID); const affectedNoteIDs = new Set([noteID]); const fromLibID = note.libraryID; const fromKey = note.key; const lines = await addon.api.note.getLinesInNote(note); const linkToData: LinkModel[] = []; for (let i = 0; i < lines.length; i++) { const linkMatches = lines[i].match(/href="zotero:\/\/note\/[^"]+"/g); if (!linkMatches) { continue; } for (const match of linkMatches) { const link = decodeHTMLEntities(match.slice(6, -1)); const { noteItem, libraryID, noteKey, lineIndex, sectionName } = getNoteLinkParams(link); if (noteItem && noteItem.isNote() && noteItem.id !== note.id) { affectedNoteIDs.add(noteItem.id); linkToData.push({ fromLibID, fromKey, toLibID: libraryID, toKey: noteKey!, fromLine: i, toLine: lineIndex ?? null, toSection: sectionName ?? null, url: link, }); } } } const result = await ( await getRelationServer() ).proxy.rebuildLinkForNote(fromLibID, fromKey, linkToData); for (const link of result.oldOutboundLinks as LinkModel[]) { const item = Zotero.Items.getByLibraryAndKey(link.toLibID, link.toKey); if (!item) { continue; } affectedNoteIDs.add(item.id); } Zotero.Notifier.trigger( // @ts-ignore "updateBNRelation", "item", Array.from(affectedNoteIDs), {}, true, ); } async function getNoteLinkOutboundRelation(noteID: number) { const note = Zotero.Items.get(noteID); const fromLibID = note.libraryID; const fromKey = note.key; return await ( await getRelationServer() ).proxy.getOutboundLinks(fromLibID, fromKey); } async function getNoteLinkInboundRelation(noteID: number) { const note = Zotero.Items.get(noteID); const toLibID = note.libraryID; const toKey = note.key; return await ( await getRelationServer() ).proxy.getInboundLinks(toLibID, toKey); } function decodeHTMLEntities(text: string) { return text .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">"); } interface LinkModel { fromLibID: number; fromKey: string; toLibID: number; toKey: string; fromLine: number; toLine: number | null; toSection: string | null; url: string; } async function linkAnnotationToTarget(model: AnnotationModel) { return await (await getRelationServer()).proxy.linkAnnotationToTarget(model); } async function getLinkTargetByAnnotation(fromLibID: number, fromKey: string) { return await ( await getRelationServer() ).proxy.getLinkTargetByAnnotation(fromLibID, fromKey); } async function getAnnotationByLinkTarget(toLibID: number, toKey: string) { return await ( await getRelationServer() ).proxy.getAnnotationByLinkTarget(toLibID, toKey); } interface AnnotationModel { fromLibID: number; fromKey: string; toLibID: number; toKey: string; url: string; }