From 415705a3bf7a30235fb991d84490797ff223518d Mon Sep 17 00:00:00 2001 From: windingwind <33902321+windingwind@users.noreply.github.com> Date: Thu, 14 Mar 2024 22:20:25 +0800 Subject: [PATCH] add: note tabs --- addon/chrome/content/bubbleMap.html | 19 +- addon/chrome/content/icons/outline-20.svg | 12 + addon/chrome/content/icons/save-20.svg | 9 + addon/chrome/content/mindMap.html | 21 +- addon/chrome/content/styles/context.css | 3 + addon/chrome/content/styles/details.css | 3 + addon/chrome/content/styles/editor.css | 49 +++ addon/chrome/content/styles/outline.css | 39 ++ addon/chrome/content/styles/workspace.css | 11 + addon/chrome/content/treeView.html | 23 +- addon/locale/en-US/outline.ftl | 20 + package.json | 2 +- scripts/build-extras.mjs | 14 +- scripts/build.mjs | 1 + src/addon.ts | 48 --- src/api.ts | 7 +- src/elements/base.ts | 60 +++ src/elements/context.ts | 47 +++ src/elements/detailsPane.ts | 30 ++ src/elements/outlinePane.ts | 379 +++++++++++++++++ src/elements/workspace.ts | 116 +++++ src/extras/workspace.ts | 17 + src/hooks.ts | 148 ++----- src/modules/annotationTagAction.ts | 42 -- src/modules/createNote.ts | 40 +- src/modules/editor/inject.ts | 38 +- src/modules/editor/toolbar.ts | 196 ++++----- src/modules/export/api.ts | 2 +- src/modules/menu.ts | 171 +++----- src/modules/viewItems.ts | 28 ++ src/modules/workspace/content.ts | 489 +--------------------- src/modules/workspace/message.ts | 84 ---- src/modules/workspace/tab.ts | 367 ++-------------- src/modules/workspace/window.ts | 21 +- src/utils/note.ts | 17 +- src/utils/wait.ts | 12 +- typings/global.d.ts | 19 + 37 files changed, 1116 insertions(+), 1488 deletions(-) create mode 100644 addon/chrome/content/icons/outline-20.svg create mode 100644 addon/chrome/content/icons/save-20.svg create mode 100644 addon/chrome/content/styles/context.css create mode 100644 addon/chrome/content/styles/details.css create mode 100644 addon/chrome/content/styles/editor.css create mode 100644 addon/chrome/content/styles/outline.css create mode 100644 addon/chrome/content/styles/workspace.css create mode 100644 addon/locale/en-US/outline.ftl create mode 100644 src/elements/base.ts create mode 100644 src/elements/context.ts create mode 100644 src/elements/detailsPane.ts create mode 100644 src/elements/outlinePane.ts create mode 100644 src/elements/workspace.ts create mode 100644 src/extras/workspace.ts delete mode 100644 src/modules/annotationTagAction.ts create mode 100644 src/modules/viewItems.ts delete mode 100644 src/modules/workspace/message.ts diff --git a/addon/chrome/content/bubbleMap.html b/addon/chrome/content/bubbleMap.html index d194737..74f350e 100644 --- a/addon/chrome/content/bubbleMap.html +++ b/addon/chrome/content/bubbleMap.html @@ -172,12 +172,12 @@ ), $(go.Panel, "Auto"), ); - window.parent.postMessage({ type: "ready" }, "*"); + window.postMessage({ type: "ready" }, "*"); getData(); } function getData() { - window.parent.postMessage({ type: "getMindMapData" }, "*"); + window.postMessage({ type: "getMindMapData" }, "*"); } function setData(nodes) { @@ -219,17 +219,16 @@ var oldnode = adorn.adornedPart; var olddata = oldnode.data; if (olddata.noteLink) { - window.parent.postMessage( + window.postMessage( { type: "openNote", link: olddata.noteLink, id: olddata.key }, "*", ); } else { - window.parent.postMessage( + window.postMessage( { type: "jumpNode", lineIndex: olddata.lineIndex, id: olddata.key, - workspaceType: window.workspaceType || "tab", }, "*", ); @@ -243,12 +242,11 @@ alert("Link cannot be edited in mind map"); return false; } - window.parent.postMessage( + window.postMessage( { type: "editNode", lineIndex: data.lineIndex, text: data.text, - workspaceType: window.workspaceType || "tab", }, "*", ); @@ -259,30 +257,31 @@ switch (e.data.type) { case "setMindMapData": setData(e.data.nodes); - window.workspaceType = e.data.workspaceType; break; case "saveImage": const imgString = Diagram.makeImageData({ scale: 1, }); - window.parent.postMessage( + window.postMessage( { type: "saveImageReturn", image: imgString, }, "*", ); + break; case "saveSVG": const svgElement = Diagram.makeSvg({ scale: 1, }); - window.parent.postMessage( + window.postMessage( { type: "saveSVGReturn", image: svgElement.outerHTML, }, "*", ); + break; default: break; } diff --git a/addon/chrome/content/icons/outline-20.svg b/addon/chrome/content/icons/outline-20.svg new file mode 100644 index 0000000..99e7236 --- /dev/null +++ b/addon/chrome/content/icons/outline-20.svg @@ -0,0 +1,12 @@ + + + + + \ No newline at end of file diff --git a/addon/chrome/content/icons/save-20.svg b/addon/chrome/content/icons/save-20.svg new file mode 100644 index 0000000..69edd92 --- /dev/null +++ b/addon/chrome/content/icons/save-20.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/addon/chrome/content/mindMap.html b/addon/chrome/content/mindMap.html index 3a65026..833de88 100644 --- a/addon/chrome/content/mindMap.html +++ b/addon/chrome/content/mindMap.html @@ -146,7 +146,7 @@ }); // read in the predefined graph using the JSON format data held in the "mySavedModel" textarea - window.parent.postMessage({ type: "ready" }, "*"); + window.postMessage({ type: "ready" }, "*"); getData(); } @@ -240,7 +240,7 @@ } function getData() { - window.parent.postMessage({ type: "getMindMapData" }, "*"); + window.postMessage({ type: "getMindMapData" }, "*"); } function setData(nodes) { @@ -281,17 +281,16 @@ var oldnode = adorn.adornedPart; var olddata = oldnode.data; if (olddata.noteLink) { - window.parent.postMessage( + window.postMessage( { type: "openNote", link: olddata.noteLink, id: olddata.key }, "*", ); } else { - window.parent.postMessage( + window.postMessage( { type: "jumpNode", lineIndex: olddata.lineIndex, id: olddata.key, - workspaceType: window.workspaceType || "tab", }, "*", ); @@ -305,12 +304,11 @@ alert("Link cannot be edited in mind map"); return false; } - window.parent.postMessage( + window.postMessage( { type: "editNode", lineIndex: data.lineIndex, text: data.text, - workspaceType: window.workspaceType || "tab", }, "*", ); @@ -321,30 +319,31 @@ switch (e.data.type) { case "setMindMapData": setData(e.data.nodes); - window.workspaceType = e.data.workspaceType; break; case "saveImage": const imgString = Diagram.makeImageData({ scale: 1, }); - window.parent.postMessage( + window.postMessage( { type: "saveImageReturn", image: imgString, }, "*", ); + break; case "saveSVG": const svgElement = Diagram.makeSvg({ scale: 1, }); - window.parent.postMessage( + window.postMessage( { type: "saveSVGReturn", image: svgElement.outerHTML, }, "*", ); + break; default: break; } @@ -354,7 +353,7 @@ try { init(); } catch (e) { - window.parent.postMessage({ type: "error", event: e }, "*"); + window.postMessage({ type: "error", event: e }, "*"); } }); diff --git a/addon/chrome/content/styles/context.css b/addon/chrome/content/styles/context.css new file mode 100644 index 0000000..253e369 --- /dev/null +++ b/addon/chrome/content/styles/context.css @@ -0,0 +1,3 @@ +bn-context { + min-width: 182px; +} \ No newline at end of file diff --git a/addon/chrome/content/styles/details.css b/addon/chrome/content/styles/details.css new file mode 100644 index 0000000..25f9fdb --- /dev/null +++ b/addon/chrome/content/styles/details.css @@ -0,0 +1,3 @@ +bn-details pane-header { + display: none; +} \ No newline at end of file diff --git a/addon/chrome/content/styles/editor.css b/addon/chrome/content/styles/editor.css new file mode 100644 index 0000000..797a4a0 --- /dev/null +++ b/addon/chrome/content/styles/editor.css @@ -0,0 +1,49 @@ +.primary-editor > h1::before { + margin-left: -64px !important; + padding-left: 40px !important; + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3E%E6%9C%AA%E6%A0%87%E9%A2%98-1%3C%2Ftitle%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M12.29%2C16.8H11.14V12.33H6.07V16.8H4.92V7H6.07v4.3h5.07V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M18.05%2C16.8H16.93V8.41a4%2C4%2C0%2C0%2C1-.9.53%2C6.52%2C6.52%2C0%2C0%2C1-1.14.44l-.32-1a8.2%2C8.2%2C0%2C0%2C0%2C1.67-.67%2C6.31%2C6.31%2C0%2C0%2C0%2C1.39-1h.42Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; +} +.primary-editor > h2::before { + margin-left: -64px !important; + padding-left: 40px !important; + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.a%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22a%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22a%22%20d%3D%22M14.14%2C16.8v-.48a4.1%2C4.1%2C0%2C0%2C1%2C.14-1.11%2C2.86%2C2.86%2C0%2C0%2C1%2C.45-.91%2C5.49%2C5.49%2C0%2C0%2C1%2C.83-.86c.33-.29.75-.61%2C1.24-1a7.43%2C7.43%2C0%2C0%2C0%2C.9-.73%2C3.9%2C3.9%2C0%2C0%2C0%2C.57-.7%2C2.22%2C2.22%2C0%2C0%2C0%2C.3-.66%2C2.87%2C2.87%2C0%2C0%2C0%2C.11-.77%2C1.89%2C1.89%2C0%2C0%2C0-.47-1.32%2C1.66%2C1.66%2C0%2C0%2C0-1.28-.5A3.17%2C3.17%2C0%2C0%2C0%2C15.7%2C8a3.49%2C3.49%2C0%2C0%2C0-1.08.76l-.68-.65a4.26%2C4.26%2C0%2C0%2C1%2C1.39-1A4%2C4%2C0%2C0%2C1%2C17%2C6.84a2.62%2C2.62%2C0%2C0%2C1%2C2.83%2C2.67%2C3.58%2C3.58%2C0%2C0%2C1-.15%2C1%2C3.09%2C3.09%2C0%2C0%2C1-.41.9%2C5.53%2C5.53%2C0%2C0%2C1-.67.81%2C9%2C9%2C0%2C0%2C1-.95.79c-.46.32-.84.59-1.13.82a4.68%2C4.68%2C0%2C0%2C0-.71.64%2C2%2C2%2C0%2C0%2C0-.38.6%2C2.08%2C2.08%2C0%2C0%2C0-.11.69h4.88v1Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22a%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; +} +.primary-editor > h3::before { + margin-left: -64px !important; + padding-left: 40px !important; + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M14%2C16.14l.51-.8a4.75%2C4.75%2C0%2C0%2C0%2C1.1.52%2C4.27%2C4.27%2C0%2C0%2C0%2C1.12.16%2C2.29%2C2.29%2C0%2C0%2C0%2C1.64-.52A1.77%2C1.77%2C0%2C0%2C0%2C19%2C14.17a1.7%2C1.7%2C0%2C0%2C0-.68-1.48%2C3.6%2C3.6%2C0%2C0%2C0-2.06-.48H15.4v-1h.77A3%2C3%2C0%2C0%2C0%2C18%2C10.81a1.65%2C1.65%2C0%2C0%2C0%2C.6-1.41%2C1.47%2C1.47%2C0%2C0%2C0-.47-1.19A1.67%2C1.67%2C0%2C0%2C0%2C17%2C7.79a3.33%2C3.33%2C0%2C0%2C0-2.08.73l-.59-.75a4.4%2C4.4%2C0%2C0%2C1%2C1.28-.71A4.35%2C4.35%2C0%2C0%2C1%2C17%2C6.84a2.84%2C2.84%2C0%2C0%2C1%2C2%2C.65%2C2.21%2C2.21%2C0%2C0%2C1%2C.74%2C1.78%2C2.35%2C2.35%2C0%2C0%2C1-.49%2C1.5%2C2.7%2C2.7%2C0%2C0%2C1-1.46.89v0a2.74%2C2.74%2C0%2C0%2C1%2C1.65.74%2C2.15%2C2.15%2C0%2C0%2C1%2C.66%2C1.65%2C2.64%2C2.64%2C0%2C0%2C1-.9%2C2.12%2C3.44%2C3.44%2C0%2C0%2C1-2.34.78%2C5.3%2C5.3%2C0%2C0%2C1-1.48-.2A5%2C5%2C0%2C0%2C1%2C14%2C16.14Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; +} +.primary-editor > h4::before { + margin-left: -64px !important; + padding-left: 40px !important; + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M19.43%2C6.92v6.59h1.05v1.05H19.43V16.9H18.31V14.56H13.66v-1c.43-.49.87-1%2C1.31-1.57s.87-1.13%2C1.27-1.7S17%2C9.14%2C17.36%2C8.57a16.51%2C16.51%2C0%2C0%2C0%2C.86-1.65Zm-4.49%2C6.59h3.37V8.63c-.34.61-.67%2C1.15-1%2C1.63s-.6.91-.87%2C1.3-.56.74-.81%2C1Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; +} +.primary-editor > h5::before { + margin-left: -64px !important; + padding-left: 40px !important; + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M14%2C16l.58-.76a3.67%2C3.67%2C0%2C0%2C0%2C1%2C.58A3.44%2C3.44%2C0%2C0%2C0%2C16.8%2C16a2.17%2C2.17%2C0%2C0%2C0%2C1.58-.6A2%2C2%2C0%2C0%2C0%2C19%2C13.88a1.85%2C1.85%2C0%2C0%2C0-.64-1.5%2C2.83%2C2.83%2C0%2C0%2C0-1.86-.54c-.27%2C0-.55%2C0-.86%2C0s-.58%2C0-.81.06L15.17%2C7H19.7V8H16.14l-.2%2C2.88.47%2C0h.43a3.5%2C3.5%2C0%2C0%2C1%2C2.43.79%2C2.74%2C2.74%2C0%2C0%2C1%2C.88%2C2.16%2C3%2C3%2C0%2C0%2C1-.94%2C2.3%2C3.41%2C3.41%2C0%2C0%2C1-2.4.87%2C4.45%2C4.45%2C0%2C0%2C1-1.5-.24A4.81%2C4.81%2C0%2C0%2C1%2C14%2C16Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; +} +.primary-editor > h6::before { + margin-left: -64px !important; + padding-left: 40px !important; + content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M20.18%2C13.7a3.24%2C3.24%2C0%2C0%2C1-.88%2C2.38%2C2.94%2C2.94%2C0%2C0%2C1-2.2.9%2C2.69%2C2.69%2C0%2C0%2C1-2.31-1.17A5.59%2C5.59%2C0%2C0%2C1%2C14%2C12.49a12.18%2C12.18%2C0%2C0%2C1%2C.2-2.14%2C5.16%2C5.16%2C0%2C0%2C1%2C.84-2A3.65%2C3.65%2C0%2C0%2C1%2C16.27%2C7.2%2C3.71%2C3.71%2C0%2C0%2C1%2C18%2C6.84%2C3.14%2C3.14%2C0%2C0%2C1%2C19%2C7a3.59%2C3.59%2C0%2C0%2C1%2C1%2C.5l-.56.77a2.3%2C2.3%2C0%2C0%2C0-1.49-.48A2.3%2C2.3%2C0%2C0%2C0%2C16.79%2C8a3%2C3%2C0%2C0%2C0-.92.85%2C3.79%2C3.79%2C0%2C0%2C0-.56%2C1.25%2C6.56%2C6.56%2C0%2C0%2C0-.19%2C1.65h0a2.61%2C2.61%2C0%2C0%2C1%2C1-.84%2C2.91%2C2.91%2C0%2C0%2C1%2C1.23-.28%2C2.63%2C2.63%2C0%2C0%2C1%2C2%2C.85A3.09%2C3.09%2C0%2C0%2C1%2C20.18%2C13.7ZM19%2C13.78a2.28%2C2.28%2C0%2C0%2C0-.5-1.62%2C1.67%2C1.67%2C0%2C0%2C0-1.29-.54%2C2%2C2%2C0%2C0%2C0-1.5.58%2C2%2C2%2C0%2C0%2C0-.56%2C1.4%2C2.65%2C2.65%2C0%2C0%2C0%2C.55%2C1.74%2C1.85%2C1.85%2C0%2C0%2C0%2C2.78.1A2.38%2C2.38%2C0%2C0%2C0%2C19%2C13.78Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; +} +.primary-editor > p, +.primary-editor h1, +.primary-editor h2, +.primary-editor h3, +.primary-editor h4, +.primary-editor h5, +.primary-editor h6, +.primary-editor pre, +.primary-editor blockquote, +.primary-editor table, +.primary-editor ul, +.primary-editor ol, +.primary-editor hr { + max-width: unset; +} + +body[no-bn-toolbar] #BetterNotes-toolbar { + display: none; +} diff --git a/addon/chrome/content/styles/outline.css b/addon/chrome/content/styles/outline.css new file mode 100644 index 0000000..4598945 --- /dev/null +++ b/addon/chrome/content/styles/outline.css @@ -0,0 +1,39 @@ +bn-outline, +.container { + width: 100%; + height: 100%; + background: var(--material-sidepane); +} + +bn-outline { + min-width: 100px; + flex-direction: column; +} + +@media (-moz-platform: macos) { + #__addonRef__-left-toolbar { + -moz-window-dragging: drag; + } +} + +#__addonRef__-left-toolbar { + background: var(--material-toolbar); + border-bottom: var(--material-panedivider); + padding: 6px 8px; +} + +bn-outline .zotero-tb-button { + width: 28px; + height: 28px; + margin: 0px 4px 0px 4px; + fill: currentColor; + -moz-context-properties: fill, fill-opacity; +} + +#__addonRef__-setOutline { + list-style-image: url("chrome://__addonRef__/content/icons/outline-20.svg"); +} + +#__addonRef__-saveOutline { + list-style-image: url("chrome://__addonRef__/content/icons/save-20.svg"); +} diff --git a/addon/chrome/content/styles/workspace.css b/addon/chrome/content/styles/workspace.css new file mode 100644 index 0000000..5ad9b5a --- /dev/null +++ b/addon/chrome/content/styles/workspace.css @@ -0,0 +1,11 @@ +bn-workspace, +.container { + width: 100%; + height: 100%; + background: var(--material-sidepane); + flex-grow: 1; +} + +bn-workspace #__addonRef__-editor-main #links-container { + display: none; +} diff --git a/addon/chrome/content/treeView.html b/addon/chrome/content/treeView.html index 3476800..e9b1cbe 100644 --- a/addon/chrome/content/treeView.html +++ b/addon/chrome/content/treeView.html @@ -43,7 +43,7 @@ } #treeview { - padding: 20px; + padding: 8px; } .drive-header { @@ -72,13 +72,18 @@ color: var(--fill-primary); } + .dx-treeview-item { + border-radius: 5px; + cursor: auto; + } + .dx-treeview-toggle-item-visibility { color: var(--fill-primary); } :not(.dx-state-focused) > .dx-treeview-item.dx-state-hover { color: var(--fill-primary); - background-color: var(--fill-quinary); + background-color: transparent; } .dx-state-focused > .dx-treeview-item { @@ -94,7 +99,7 @@ var cachedNodes = null; function init() { window.addEventListener("message", handler, false); - window.parent.postMessage({ type: "ready" }, "*"); + window.postMessage({ type: "ready" }, "*"); getData(); } @@ -151,7 +156,7 @@ return; } - window.parent.postMessage( + window.postMessage( { type: "moveNode", fromID: parseInt(fromNode.itemData.id), @@ -295,7 +300,7 @@ function jumpNode(e) { var itemData = e.itemData; if (itemData.noteLink) { - window.parent.postMessage( + window.postMessage( { type: "openNote", link: itemData.noteLink, @@ -304,12 +309,11 @@ "*", ); } else { - window.parent.postMessage( + window.postMessage( { type: "jumpNode", lineIndex: itemData.lineIndex, id: parseInt(itemData.id), - workspaceType: window.workspaceType || "tab", }, "*", ); @@ -319,7 +323,7 @@ const noteIcon = ``; function getData() { - window.parent.postMessage({ type: "getMindMapData" }, "*"); + window.postMessage({ type: "getMindMapData" }, "*"); } function setData(nodes, expandLevel) { @@ -384,7 +388,6 @@ console.log(e); if (e.data.type === "setMindMapData") { setData(e.data.nodes, e.data.expandLevel); - window.workspaceType = e.data.workspaceType; } } @@ -392,7 +395,7 @@ try { init(); } catch (e) { - window.parent.postMessage({ type: "error", event: e }, "*"); + window.postMessage({ type: "error", event: e }, "*"); } }); diff --git a/addon/locale/en-US/outline.ftl b/addon/locale/en-US/outline.ftl new file mode 100644 index 0000000..aa31812 --- /dev/null +++ b/addon/locale/en-US/outline.ftl @@ -0,0 +1,20 @@ +setOutline = + .tooltiptext = Change outline mode +useTreeView = + .label = Tree View +useMindMap = + .label = Mind Map +useBubbleMap = + .label = Bubble Map +saveOutline = + .tooltiptext = Save as... +saveOutlineImage = + .label = Outline image + .tooltiptext = Only in mind map/bubble map mode +saveOutlineSVG = + .label = Outline SVG + .tooltiptext = Only in mind map/bubble map mode +saveOutlineFreeMind = + .label = Outline FreeMind +saveMore = + .label = MarkDown, Docx, PDF... diff --git a/package.json b/package.json index 66f3ca5..d99b862 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.1", "yamljs": "^0.3.0", - "zotero-plugin-toolkit": "^2.3.23" + "zotero-plugin-toolkit": "^2.3.26" }, "devDependencies": { "@esbuild-plugins/node-globals-polyfill": "^0.2.3", diff --git a/scripts/build-extras.mjs b/scripts/build-extras.mjs index cefdd93..d00cda6 100644 --- a/scripts/build-extras.mjs +++ b/scripts/build-extras.mjs @@ -5,19 +5,9 @@ const buildDir = "build"; export async function main() { await build({ - entryPoints: ["src/extras/editorScript.ts"], + entryPoints: ["./src/extras/*.ts"], + outdir: path.join(buildDir, "addon/chrome/content/scripts"), bundle: true, - outfile: path.join( - buildDir, - "addon/chrome/content/scripts/editorScript.js", - ), - target: ["firefox102"], - }).catch(() => exit(1)); - - await build({ - entryPoints: ["src/extras/docxWorker.ts"], - bundle: true, - outfile: path.join(buildDir, "addon/chrome/content/scripts/docxWorker.js"), target: ["firefox102"], }).catch(() => exit(1)); } diff --git a/scripts/build.mjs b/scripts/build.mjs index 96bd4a1..495ef39 100644 --- a/scripts/build.mjs +++ b/scripts/build.mjs @@ -49,6 +49,7 @@ function replaceString(buildTime) { `${buildDir}/addon/**/*.xhtml`, `${buildDir}/addon/**/*.html`, `${buildDir}/addon/**/*.json`, + `${buildDir}/addon/**/*.css`, `${buildDir}/addon/prefs.js`, `${buildDir}/addon/manifest.json`, `${buildDir}/addon/bootstrap.js`, diff --git a/src/addon.ts b/src/addon.ts index c378645..fe9fbbe 100644 --- a/src/addon.ts +++ b/src/addon.ts @@ -7,7 +7,6 @@ import { LargePrefHelper } from "zotero-plugin-toolkit/dist/helpers/largePref"; import ToolkitGlobal from "zotero-plugin-toolkit/dist/managers/toolkitGlobal"; import { getPref, setPref } from "./utils/prefs"; -import { OutlineType } from "./utils/workspace"; import { SyncDataType } from "./modules/sync/managerWindow"; import hooks from "./hooks"; import api from "./api"; @@ -46,21 +45,6 @@ class Addon { }; }; notify: Array>; - workspace: { - mainId: number; - previewId: number; - tab: { - active: boolean; - id?: string; - container?: XUL.Box; - }; - window: { - active: boolean; - window?: Window; - container?: XUL.Box; - }; - outline: OutlineType; - }; imageViewer: { window?: Window; srcList: string[]; @@ -105,38 +89,6 @@ class Addon { diff: {}, }, notify: [], - workspace: { - get mainId(): number { - return parseInt(getPref("mainKnowledgeID") as string); - }, - set mainId(id: number) { - setPref("mainKnowledgeID", id); - const recentMainNoteIds = getPref("recentMainNoteIds") as string; - const recentMainNoteIdsArr = recentMainNoteIds - ? recentMainNoteIds.split(",").map((id) => parseInt(id)) - : []; - const idx = recentMainNoteIdsArr.indexOf(id); - if (idx !== -1) { - recentMainNoteIdsArr.splice(idx, 1); - } - recentMainNoteIdsArr.unshift(id); - setPref( - "recentMainNoteIds", - recentMainNoteIdsArr - .slice(0, 10) - .filter((id) => Zotero.Items.get(id).isNote()) - .join(","), - ); - }, - previewId: -1, - tab: { - active: false, - }, - window: { - active: false, - }, - outline: OutlineType.treeView, - }, imageViewer: { window: undefined, srcList: [], diff --git a/src/api.ts b/src/api.ts index 4922069..5bbabaf 100644 --- a/src/api.ts +++ b/src/api.ts @@ -44,7 +44,6 @@ import { DEFAULT_TEMPLATES, } from "./modules/template/data"; import { renderTemplatePreview } from "./modules/template/preview"; -import { getWorkspaceEditor } from "./modules/workspace/content"; import { parseCitationHTML } from "./utils/citation"; import { getEditorInstance, @@ -65,11 +64,10 @@ import { addLineToNote, updateRelatedNotes, getRelatedNoteIds, + getNoteTreeFlattened, } from "./utils/note"; -const workspace = { - getWorkspaceEditor, -}; +const workspace = {}; const sync = { isSyncNote, @@ -148,6 +146,7 @@ const note = { insert: addLineToNote, updateRelatedNotes, getRelatedNoteIds, + getNoteTreeFlattened, }; export default { diff --git a/src/elements/base.ts b/src/elements/base.ts new file mode 100644 index 0000000..76e10b7 --- /dev/null +++ b/src/elements/base.ts @@ -0,0 +1,60 @@ +import { config } from "../../package.json"; + +export class PluginCEBase extends XULElementBase { + _addon!: typeof addon; + + connectedCallback(): void { + this._addon = Zotero[config.addonInstance]; + Zotero.UIProperties.registerRoot(this); + super.connectedCallback(); + } + + _wrapID(key: string) { + if (key.startsWith(config.addonRef)) { + return key; + } + return `${config.addonRef}-${key}`; + } + + _unwrapID(id: string) { + if (id.startsWith(config.addonRef)) { + return id.slice(config.addonRef.length + 1); + } + return id; + } + + _queryID(key: string) { + return this.querySelector(`#${this._wrapID(key)}`) as XUL.Element | null; + } + + _parseContentID(dom: DocumentFragment) { + dom.querySelectorAll("*[id]").forEach(elem => { + elem.id = this._wrapID(elem.id); + }) + return dom; + } + + _loadPersist() { + const persistValues = Zotero.Prefs.get("pane.persist") as string; + if (!persistValues) return; + const serializedValues = JSON.parse(persistValues) as Record< + string, + Record + >; + + for (const id in serializedValues) { + const el = this.querySelector(`#${id}`) as HTMLElement; + if (!el) { + continue; + } + + const elValues = serializedValues[id]; + for (const attr in elValues) { + el.setAttribute(attr, elValues[attr]); + if (["width", "height"].includes(attr)) { + el.style[attr as any] = `${elValues[attr]}px`; + } + } + } + } +} diff --git a/src/elements/context.ts b/src/elements/context.ts new file mode 100644 index 0000000..febba36 --- /dev/null +++ b/src/elements/context.ts @@ -0,0 +1,47 @@ +import { config } from "../../package.json"; +import { PluginCEBase } from "./base"; + +export class ContextPane extends PluginCEBase { + _item?: Zotero.Item; + + _details!: any; + _sidenav: any; + + get item() { + return this._item; + } + + set item(val) { + this._item = val; + } + + get content() { + return this._parseContentID( + MozXULElement.parseXULToFragment(` + + + + + +`), + ); + } + + init(): void { + this._details = this._queryID("container"); + this._sidenav = this._queryID("sidenav"); + } + + render() { + if (!this.item) return; + this._details.mode = this.item.isEditable() ? null : "view"; + this._details.item = this.item; + this._details.parentID = this.item.parentID; + this._details.sidenav = this._sidenav; + this._details.render(); + this._sidenav.toggleDefaultStatus(); + } +} diff --git a/src/elements/detailsPane.ts b/src/elements/detailsPane.ts new file mode 100644 index 0000000..197197b --- /dev/null +++ b/src/elements/detailsPane.ts @@ -0,0 +1,30 @@ +import { config } from "../../package.json"; +const ItemDetails = customElements.get("item-details")! as any; + +export class NoteDetails extends ItemDetails { + content = MozXULElement.parseXULToFragment(` + + + + + + + + + + + + +`); + + forceUpdateSideNav() { + this._sidenav + .querySelectorAll("toolbarbutton") + .forEach((elem: HTMLElement) => (elem.parentElement!.hidden = true)); + super.forceUpdateSideNav(); + } +} diff --git a/src/elements/outlinePane.ts b/src/elements/outlinePane.ts new file mode 100644 index 0000000..81d816e --- /dev/null +++ b/src/elements/outlinePane.ts @@ -0,0 +1,379 @@ +import { FilePickerHelper } from "zotero-plugin-toolkit/dist/helpers/filePicker"; +import { config } from "../../package.json"; +import { showHintWithLink } from "../utils/hint"; +import { formatPath } from "../utils/str"; +import { waitUtilAsync } from "../utils/wait"; +import { OutlineType } from "../utils/workspace"; +import { PluginCEBase } from "./base"; +import { + getEditorInstance, + moveHeading, + updateHeadingTextAtLine, +} from "../utils/editor"; +import { getNoteLinkParams } from "../utils/link"; +import { getNoteTree, getNoteTreeNodeById } from "../utils/note"; +import { getPref } from "../utils/prefs"; + +export class OutlinePane extends PluginCEBase { + _outlineType: OutlineType = OutlineType.empty; + _item?: Zotero.Item; + _editorElement!: EditorElement; + + _outlineContainer!: HTMLIFrameElement; + _notifierID!: string; + + static outlineSources = [ + "", + `chrome://${config.addonRef}/content/treeView.html`, + `chrome://${config.addonRef}/content/mindMap.html`, + `chrome://${config.addonRef}/content/bubbleMap.html`, + ]; + + static outlineMenuIDs = { + "": OutlineType.empty, + useTreeView: OutlineType.treeView, + useMindMap: OutlineType.mindMap, + useBubbleMap: OutlineType.bubbleMap, + }; + + get content() { + return this._parseContentID( + MozXULElement.parseXULToFragment(` + + + + + + + + + + + + + + + + + + + + +`), + ); + } + + get outlineType() { + return this._outlineType; + } + + set outlineType(newType) { + if (newType === OutlineType.empty) { + newType = OutlineType.treeView; + } + if (newType > OutlineType.bubbleMap) { + newType = OutlineType.treeView; + } + + this._outlineType = newType; + } + + get item() { + return this._item; + } + + set item(val) { + this._item = val; + } + + get editor() { + return this._editorElement._editorInstance; + } + + init(): void { + MozXULElement.insertFTLIfNeeded(`${config.addonRef}-outline.ftl`); + + this._outlineContainer = this._queryID("outline") as unknown as HTMLIFrameElement; + + this._queryID("left-toolbar")?.addEventListener( + "command", + this.toolbarButtonCommandHandler, + ); + + this._notifierID = Zotero.Notifier.registerObserver( + this, + ["item"], + "attachmentsBox", + ); + + this._loadPersist(); + } + + destroy(): void { + Zotero.Notifier.unregisterObserver(this._notifierID); + this._outlineContainer.contentWindow?.removeEventListener( + "message", + this.messageHandler, + ); + } + + notify( + event: string, + type: string, + ids: number[] | string[], + extraData: { [key: string]: any }, + ) { + if (!this.item) return; + if (event === "modify" && type === "item") { + if ((ids as number[]).includes(this.item.id)) { + this.updateOutline(); + if (getPref("workspace.autoUpdateRelatedNotes")) { + this._addon.api.note.updateRelatedNotes(this.item.id); + } + } + } + } + + async render() { + if (this.outlineType === OutlineType.empty) { + this.outlineType = OutlineType.treeView; + } + await this.updateOutline(); + } + + async updateOutline() { + if (!this.item) return; + + this._outlineContainer.contentWindow?.removeEventListener( + "message", + this.messageHandler, + ); + + this._outlineContainer.setAttribute("src", OutlinePane.outlineSources[this.outlineType]); + + await waitUtilAsync( + () => this._outlineContainer.contentWindow?.document.readyState === "complete", + ); + this._outlineContainer.contentWindow?.addEventListener( + "message", + this.messageHandler, + ); + this._outlineContainer.contentWindow?.postMessage( + { + type: "setMindMapData", + nodes: this._addon.api.note.getNoteTreeFlattened(this.item, { + keepLink: !!getPref("workspace.outline.keepLinks"), + }), + expandLevel: getPref("workspace.outline.expandLevel"), + }, + "*", + ); + + // Update button hidden + const isTreeView = this.outlineType === OutlineType.treeView; + for (const key of ["saveImage", "saveSVG"]) { + const elem = this._queryID(key); + if (isTreeView) { + elem?.setAttribute("disabled", "true"); + } else { + elem?.removeAttribute("disabled"); + } + } + + // Update set outline menu + this._queryID("setOutlinePopup")?.childNodes.forEach((elem) => + (elem as XUL.MenuItem).removeAttribute("checked"), + ); + this._queryID( + Object.keys(OutlinePane.outlineMenuIDs)[this.outlineType], + )?.setAttribute("checked", "true"); + } + + saveImage(type: "saveSVG" | "saveImage") { + this._outlineContainer.contentWindow?.postMessage( + { + type, + }, + "*", + ); + } + + async saveFreeMind() { + if (!this.item?.id) return; + // TODO: uncouple this part + const filename = await new FilePickerHelper( + `${Zotero.getString("fileInterface.export")} FreeMind XML`, + "save", + [["FreeMind XML File(*.mm)", "*.mm"]], + `${this.item.getNoteTitle()}.mm`, + ).open(); + if (filename) { + await addon.api.$export.saveFreeMind(filename, this.item.id); + } + } + + toolbarButtonCommandHandler = async (ev: Event) => { + if (!this.item) return; + const type = this._unwrapID((ev.target as XUL.ToolBarButton).id); + switch (type) { + case "useTreeView": + case "useMindMap": + case "useBubbleMap": { + this.outlineType = OutlinePane.outlineMenuIDs[type]; + await this.updateOutline(); + break; + } + case "saveImage": + case "saveSVG": { + this.saveImage(type); + break; + } + case "saveFreeMind": { + this.saveFreeMind(); + break; + } + case "saveMore": { + this._addon.hooks.onShowExportNoteOptions([this.item.id]); + break; + } + default: { + break; + } + } + }; + + messageHandler = async (ev: MessageEvent) => { + switch (ev.data.type) { + case "jumpNode": { + if (!this.editor) { + return; + } + this._addon.api.editor.scroll(this.editor, ev.data.lineIndex); + return; + } + case "openNote": { + const linkParams = getNoteLinkParams(ev.data.link); + if (!linkParams.noteItem) { + return; + } + this._addon.hooks.onOpenNote(linkParams.noteItem.id, "preview", { + lineIndex: linkParams.lineIndex || undefined, + }); + return; + } + case "moveNode": { + if (!this.item) return; + const tree = getNoteTree(this.item); + const fromNode = getNoteTreeNodeById(this.item, ev.data.fromID, tree); + const toNode = getNoteTreeNodeById(this.item, ev.data.toID, tree); + moveHeading( + getEditorInstance(this.item.id), + fromNode!, + toNode!, + ev.data.moveType, + ); + return; + } + case "editNode": { + if (!this.editor) { + return; + } + updateHeadingTextAtLine( + this.editor, + ev.data.lineIndex, + ev.data.text.replace(/[\r\n]/g, ""), + ); + return; + } + case "saveSVGReturn": { + const filename = await new FilePickerHelper( + `${Zotero.getString("fileInterface.export")} SVG Image`, + "save", + [["SVG File(*.svg)", "*.svg"]], + `${this.item?.getNoteTitle()}.svg`, + ).open(); + if (filename) { + await Zotero.File.putContentsAsync( + formatPath(filename), + ev.data.image, + ); + showHintWithLink( + `Image Saved to ${filename}`, + "Show in Folder", + (ev) => { + Zotero.File.reveal(filename); + }, + ); + } + return; + } + case "saveImageReturn": { + const filename = await new FilePickerHelper( + `${Zotero.getString("fileInterface.export")} PNG Image`, + "save", + [["PNG File(*.png)", "*.png"]], + `${this.item?.getNoteTitle()}.png`, + ).open(); + if (filename) { + const parts = ev.data.image.split(","); + const bstr = atob(parts[1]); + let n = bstr.length; + const u8arr = new Uint8Array(n); + while (n--) { + u8arr[n] = bstr.charCodeAt(n); + } + await IOUtils.write(formatPath(filename), u8arr); + showHintWithLink( + `Image Saved to ${filename}`, + "Show in Folder", + (ev) => { + Zotero.File.reveal(filename); + }, + ); + } + return; + } + default: + return; + } + }; +} diff --git a/src/elements/workspace.ts b/src/elements/workspace.ts new file mode 100644 index 0000000..235b02e --- /dev/null +++ b/src/elements/workspace.ts @@ -0,0 +1,116 @@ +import { config } from "../../package.json"; +import { waitUtilAsync } from "../utils/wait"; +import { PluginCEBase } from "./base"; +import { ContextPane } from "./context"; +import { OutlinePane } from "./outlinePane"; + +export class Workspace extends PluginCEBase { + _item?: Zotero.Item; + + _editorElement!: EditorElement; + _outline!: OutlinePane; + _context!: ContextPane; + + get content() { + return this._parseContentID( + MozXULElement.parseXULToFragment(` + + + + + + + + + + + + + +`), + ); + } + + get containerType() { + return this.getAttribute("container-type") || ""; + } + + set containerType(val: string) { + this.setAttribute("container-type", val); + } + + get item() { + return this._item; + } + + set item(val) { + this._item = val; + this._outline.item = val; + this._context.item = val; + } + + get editor() { + return this._editorElement._editorInstance; + } + + init(): void { + // MozXULElement.insertFTLIfNeeded(`${config.addonRef}-workspace.ftl`); + + this._outline = this._queryID("left-container") as unknown as OutlinePane; + this._editorElement = this._queryID("editor-main") as EditorElement; + this._outline._editorElement = this._editorElement; + + this._context = this._queryID("right-container") as unknown as ContextPane; + + this._loadPersist(); + } + + destroy(): void {} + + async render() { + await this._outline.render(); + await this.updateEditor(); + await this._context.render(); + } + + async updateEditor() { + const editorElem = this._queryID("editor-main") as EditorElement; + await waitUtilAsync(() => Boolean(editorElem._initialized)); + if (!editorElem._initialized) { + throw new Error("initNoteEditor: waiting initialization failed"); + } + editorElem.mode = "edit"; + editorElem.viewMode = "library"; + editorElem.parent = this.item?.parentItem; + editorElem.item = this.item; + await waitUtilAsync(() => Boolean(editorElem._editorInstance)); + await editorElem._editorInstance._initPromise; + // Hide BN toolbar + editorElem._editorInstance._iframeWindow.document.body.setAttribute( + "no-bn-toolbar", + "true", + ); + // TODO: implement jump to + // if (typeof options.lineIndex === "number") { + // addon.api.editor.scroll(editorElem._editorInstance, options.lineIndex); + // } + // if (typeof options.sectionName === "string") { + // addon.api.editor.scrollToSection( + // editorElem._editorInstance, + // options.sectionName, + // ); + // } + return; + } +} diff --git a/src/extras/workspace.ts b/src/extras/workspace.ts new file mode 100644 index 0000000..2b134de --- /dev/null +++ b/src/extras/workspace.ts @@ -0,0 +1,17 @@ +import { ContextPane } from "../elements/context"; +import { NoteDetails } from "../elements/detailsPane"; +import { OutlinePane } from "../elements/outlinePane"; +import { Workspace } from "../elements/workspace"; + +const elements = { + "bn-context": ContextPane, + "bn-outline": OutlinePane, + "bn-details": NoteDetails, + "bn-workspace": Workspace, +}; + +for (const [key, constructor] of Object.entries(elements)) { + if (!customElements.get(key)) { + customElements.define(key, constructor); + } +} diff --git a/src/hooks.ts b/src/hooks.ts index a687c68..88fdcb7 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -11,15 +11,9 @@ import { registerMenus } from "./modules/menu"; import { registerWorkspaceTab, openWorkspaceTab, + onTabSelect, } from "./modules/workspace/tab"; -import { - initWorkspace, - initWorkspaceEditor, - toggleNotesPane, - toggleOutlinePane, - togglePreviewPane, - updateOutline, -} from "./modules/workspace/content"; +import { initWorkspace } from "./modules/workspace/content"; import { registerNotify } from "./modules/notify"; import { openWorkspaceWindow } from "./modules/workspace/window"; import { registerReaderAnnotationButton } from "./modules/reader"; @@ -34,16 +28,12 @@ import { showSyncDiff } from "./modules/sync/diffWindow"; import { showSyncInfo } from "./modules/sync/infoWindow"; import { showSyncManager } from "./modules/sync/managerWindow"; import { showTemplateEditor } from "./modules/template/editorWindow"; -import { - createNoteFromTemplate, - createWorkspaceNote, - createNoteFromMD, -} from "./modules/createNote"; -import { annotationTagAction } from "./modules/annotationTagAction"; +import { createNoteFromTemplate, createNoteFromMD } from "./modules/createNote"; import { createZToolkit } from "./utils/ztoolkit"; import { waitUtilAsync } from "./utils/wait"; import { initSyncList } from "./modules/sync/api"; import { getPref } from "./utils/prefs"; +import { patchViewItems } from "./modules/viewItems"; async function onStartup() { await Promise.all([ @@ -74,6 +64,11 @@ async function onStartup() { async function onMainWindowLoad(win: Window): Promise { await waitUtilAsync(() => document.readyState === "complete"); + + Services.scriptloader.loadSubScript( + `chrome://${config.addonRef}/content/scripts/workspace.js`, + win, + ); // Create ztoolkit for every window addon.data.ztoolkit = createZToolkit(); @@ -84,6 +79,8 @@ async function onMainWindowLoad(win: Window): Promise { registerWorkspaceTab(win); initTemplates(); + + patchViewItems(win); } async function onMainWindowUnload(win: Window): Promise { @@ -102,25 +99,16 @@ function onShutdown(): void { * Any operations should be placed in a function to keep this funcion clear. */ function onNotify( - event: string, - type: string, - ids: number[] | string[], - extraData: { [key: string]: any }, + event: Parameters<_ZoteroTypes.Notifier.Notify>["0"], + type: Parameters<_ZoteroTypes.Notifier.Notify>["1"], + ids: Parameters<_ZoteroTypes.Notifier.Notify>["2"], + extraData: Parameters<_ZoteroTypes.Notifier.Notify>["3"], ) { if (extraData.skipBN) { return; } - // Workspace main note update - if (event === "modify" && type === "item") { - if ((ids as number[]).includes(addon.data.workspace.mainId)) { - addon.data.workspace.tab.active && - updateOutline(addon.data.workspace.tab.container!); - addon.data.workspace.window.active && - updateOutline(addon.data.workspace.window.container!); - if (getPref("workspace.autoUpdateRelatedNotes")) { - addon.api.note.updateRelatedNotes(addon.data.workspace.mainId); - } - } + if (event === "select" && type === "tab") { + onTabSelect(extraData[ids[0]].type); } if (event === "modify" && type === "item") { const modifiedNotes = Zotero.Items.get(ids).filter((item) => item.isNote()); @@ -131,10 +119,6 @@ function onNotify( reason: "item-modify", }); } - } - // Insert annotation when assigning tag starts with @ - if (event === "add" && type === "item-tag") { - annotationTagAction(ids as number[], extraData); } else { return; } @@ -170,23 +154,14 @@ function onOpenNote( return; } if (mode === "auto") { - if (noteId === addon.data.workspace.mainId) { - mode = "workspace"; - } else if ( - addon.data.workspace.tab.active || - addon.data.workspace.window.active - ) { - mode = "preview"; - } else { - mode = "standalone"; - } + mode = "workspace"; } switch (mode) { case "preview": - addon.hooks.onSetWorkspaceNote(noteId, "preview", options); + // addon.hooks.onSetWorkspaceNote(noteId, "preview", options); break; case "workspace": - addon.hooks.onSetWorkspaceNote(noteId, "main", options); + // addon.hooks.onSetWorkspaceNote(noteId, "main", options); break; case "standalone": ZoteroPane.openNoteWindow(noteId); @@ -196,60 +171,13 @@ function onOpenNote( } } -function onSetWorkspaceNote( - noteId: number, - type: "main" | "preview" = "main", - options: { - lineIndex?: number; - sectionName?: string; - } = {}, -) { - if (type === "main") { - addon.data.workspace.mainId = noteId; - addon.data.workspace.tab.active && - updateOutline(addon.data.workspace.tab.container!); - addon.data.workspace.window.active && - updateOutline(addon.data.workspace.window.container!); - } - if (addon.data.workspace.window.active) { - initWorkspaceEditor( - addon.data.workspace.window.container!, - type, - noteId, - options, - ); - type === "preview" && - addon.hooks.onToggleWorkspacePane( - "preview", - true, - addon.data.workspace.window.container, - ); - addon.data.workspace.window.window?.focus(); - } - if (addon.data.workspace.tab.active) { - initWorkspaceEditor( - addon.data.workspace.tab.container!, - type, - noteId, - options, - ); - type === "preview" && - addon.hooks.onToggleWorkspacePane( - "preview", - true, - addon.data.workspace.tab.container, - ); - Zotero_Tabs.select(addon.data.workspace.tab.id!); - } -} - -function onOpenWorkspace(type: "tab" | "window" = "tab") { +function onOpenWorkspace(item: Zotero.Item, type: "tab" | "window" = "tab") { if (type === "window") { - openWorkspaceWindow(); + openWorkspaceWindow(item); return; } if (type === "tab") { - openWorkspaceTab(); + openWorkspaceTab(item); return; } } @@ -261,19 +189,19 @@ function onToggleWorkspacePane( visibility?: boolean, container?: XUL.Box, ) { - switch (type) { - case "outline": - toggleOutlinePane(visibility, container); - break; - case "preview": - togglePreviewPane(visibility, container); - break; - case "notes": - toggleNotesPane(visibility); - break; - default: - break; - } + // switch (type) { + // case "outline": + // toggleOutlinePane(visibility, container); + // break; + // case "preview": + // togglePreviewPane(visibility, container); + // break; + // case "notes": + // toggleNotesPane(visibility); + // break; + // default: + // break; + // } } const onSyncing = callSyncing; @@ -296,8 +224,6 @@ const onShowSyncDiff = showSyncDiff; const onShowTemplateEditor = showTemplateEditor; -const onCreateWorkspaceNote = createWorkspaceNote; - const onCreateNoteFromTemplate = createNoteFromTemplate; const onCreateNoteFromMD = createNoteFromMD; @@ -315,7 +241,6 @@ export default { onPrefsEvent, onOpenNote, onInitWorkspace, - onSetWorkspaceNote, onOpenWorkspace, onToggleWorkspacePane, onSyncing, @@ -328,7 +253,6 @@ export default { onShowSyncInfo, onShowSyncManager, onShowTemplateEditor, - onCreateWorkspaceNote, onCreateNoteFromTemplate, onCreateNoteFromMD, }; diff --git a/src/modules/annotationTagAction.ts b/src/modules/annotationTagAction.ts deleted file mode 100644 index 0293ba3..0000000 --- a/src/modules/annotationTagAction.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { addLineToNote, getNoteTreeFlattened } from "../utils/note"; - -export { annotationTagAction }; - -async function annotationTagAction( - ids: Array, - extraData: Record, -) { - const workspaceNote = Zotero.Items.get(addon.data.workspace.mainId); - if (!workspaceNote || !workspaceNote.isNote()) { - return; - } - const nodes = getNoteTreeFlattened(workspaceNote); - const headings: string[] = nodes.map((node) => node.model.name); - - for (const tagId of ids.filter((t) => - (extraData[t].tag as string).startsWith("@"), - )) { - const tagName = (extraData[tagId].tag as string).slice(1).trim(); - if (headings.includes(tagName) || tagName === "@") { - let lineIndex: number; - if (tagName === "@") { - lineIndex = -1; - } else { - const targetNode = nodes.find((node) => node.model.name === tagName); - lineIndex = targetNode?.model.endIndex; - } - - const annotationItem = Zotero.Items.get((tagId as string).split("-")[0]); - if (!annotationItem.isAnnotation()) { - continue; - } - await addLineToNote( - workspaceNote, - await addon.api.convert.annotations2html([annotationItem], { - noteItem: workspaceNote, - }), - lineIndex, - ); - } - } -} diff --git a/src/modules/createNote.ts b/src/modules/createNote.ts index 702db0c..5740bbe 100644 --- a/src/modules/createNote.ts +++ b/src/modules/createNote.ts @@ -1,45 +1,7 @@ import { getString } from "../utils/locale"; -import { config } from "../../package.json"; import { formatPath } from "../utils/str"; -export { createWorkspaceNote, createNoteFromTemplate, createNoteFromMD }; - -async function createWorkspaceNote() { - const currentCollection = ZoteroPane.getSelectedCollection(); - if (!currentCollection) { - window.alert(getString("alert.notValidCollectionError")); - return; - } - const confirmOperation = window.confirm( - `${getString( - "menuAddNote.newMainNote.confirmHead", - // @ts-ignore - )} '${currentCollection.getName()}' ${getString( - "menuAddNote.newMainNote.confirmTail", - )}`, - ); - if (!confirmOperation) { - return; - } - const header = window.prompt( - getString("menuAddNote.newMainNote.enterNoteTitle"), - `New Note ${new Date().toLocaleString()}`, - ); - const noteID = await ZoteroPane.newNote(); - const noteItem = Zotero.Items.get(noteID); - noteItem.setNote( - `

${header}

\n
`, - ); - await noteItem.saveTx(); - addon.hooks.onSetWorkspaceNote(noteID, "main"); - if ( - !addon.data.workspace.tab.active && - !addon.data.workspace.window.active && - window.confirm(getString("menuAddNote.newMainNote.openWorkspaceTab")) - ) { - addon.hooks.onOpenWorkspace("tab"); - } -} +export { createNoteFromTemplate, createNoteFromMD }; function getLibraryParentId() { return ZoteroPane.getSelectedItems().filter((item) => item.isRegularItem())[0] diff --git a/src/modules/editor/inject.ts b/src/modules/editor/inject.ts index eb4afea..379e061 100644 --- a/src/modules/editor/inject.ts +++ b/src/modules/editor/inject.ts @@ -16,47 +16,13 @@ export async function injectEditorScripts(win: Window) { ); } -export function injectEditorCSS(win: Window) { +export async function injectEditorCSS(win: Window) { ztoolkit.UI.appendElement( { tag: "style", id: "betternotes-style", properties: { - innerHTML: ` - .primary-editor > h1::before { - margin-left: -64px !important; - padding-left: 40px !important; - content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Ctitle%3E%E6%9C%AA%E6%A0%87%E9%A2%98-1%3C%2Ftitle%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M12.29%2C16.8H11.14V12.33H6.07V16.8H4.92V7H6.07v4.3h5.07V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M18.05%2C16.8H16.93V8.41a4%2C4%2C0%2C0%2C1-.9.53%2C6.52%2C6.52%2C0%2C0%2C1-1.14.44l-.32-1a8.2%2C8.2%2C0%2C0%2C0%2C1.67-.67%2C6.31%2C6.31%2C0%2C0%2C0%2C1.39-1h.42Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; - } - .primary-editor > h2::before { - margin-left: -64px !important; - padding-left: 40px !important; - content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.a%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22a%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22a%22%20d%3D%22M14.14%2C16.8v-.48a4.1%2C4.1%2C0%2C0%2C1%2C.14-1.11%2C2.86%2C2.86%2C0%2C0%2C1%2C.45-.91%2C5.49%2C5.49%2C0%2C0%2C1%2C.83-.86c.33-.29.75-.61%2C1.24-1a7.43%2C7.43%2C0%2C0%2C0%2C.9-.73%2C3.9%2C3.9%2C0%2C0%2C0%2C.57-.7%2C2.22%2C2.22%2C0%2C0%2C0%2C.3-.66%2C2.87%2C2.87%2C0%2C0%2C0%2C.11-.77%2C1.89%2C1.89%2C0%2C0%2C0-.47-1.32%2C1.66%2C1.66%2C0%2C0%2C0-1.28-.5A3.17%2C3.17%2C0%2C0%2C0%2C15.7%2C8a3.49%2C3.49%2C0%2C0%2C0-1.08.76l-.68-.65a4.26%2C4.26%2C0%2C0%2C1%2C1.39-1A4%2C4%2C0%2C0%2C1%2C17%2C6.84a2.62%2C2.62%2C0%2C0%2C1%2C2.83%2C2.67%2C3.58%2C3.58%2C0%2C0%2C1-.15%2C1%2C3.09%2C3.09%2C0%2C0%2C1-.41.9%2C5.53%2C5.53%2C0%2C0%2C1-.67.81%2C9%2C9%2C0%2C0%2C1-.95.79c-.46.32-.84.59-1.13.82a4.68%2C4.68%2C0%2C0%2C0-.71.64%2C2%2C2%2C0%2C0%2C0-.38.6%2C2.08%2C2.08%2C0%2C0%2C0-.11.69h4.88v1Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22a%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; - } - .primary-editor > h3::before { - margin-left: -64px !important; - padding-left: 40px !important; - content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M14%2C16.14l.51-.8a4.75%2C4.75%2C0%2C0%2C0%2C1.1.52%2C4.27%2C4.27%2C0%2C0%2C0%2C1.12.16%2C2.29%2C2.29%2C0%2C0%2C0%2C1.64-.52A1.77%2C1.77%2C0%2C0%2C0%2C19%2C14.17a1.7%2C1.7%2C0%2C0%2C0-.68-1.48%2C3.6%2C3.6%2C0%2C0%2C0-2.06-.48H15.4v-1h.77A3%2C3%2C0%2C0%2C0%2C18%2C10.81a1.65%2C1.65%2C0%2C0%2C0%2C.6-1.41%2C1.47%2C1.47%2C0%2C0%2C0-.47-1.19A1.67%2C1.67%2C0%2C0%2C0%2C17%2C7.79a3.33%2C3.33%2C0%2C0%2C0-2.08.73l-.59-.75a4.4%2C4.4%2C0%2C0%2C1%2C1.28-.71A4.35%2C4.35%2C0%2C0%2C1%2C17%2C6.84a2.84%2C2.84%2C0%2C0%2C1%2C2%2C.65%2C2.21%2C2.21%2C0%2C0%2C1%2C.74%2C1.78%2C2.35%2C2.35%2C0%2C0%2C1-.49%2C1.5%2C2.7%2C2.7%2C0%2C0%2C1-1.46.89v0a2.74%2C2.74%2C0%2C0%2C1%2C1.65.74%2C2.15%2C2.15%2C0%2C0%2C1%2C.66%2C1.65%2C2.64%2C2.64%2C0%2C0%2C1-.9%2C2.12%2C3.44%2C3.44%2C0%2C0%2C1-2.34.78%2C5.3%2C5.3%2C0%2C0%2C1-1.48-.2A5%2C5%2C0%2C0%2C1%2C14%2C16.14Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; - } - .primary-editor > h4::before { - margin-left: -64px !important; - padding-left: 40px !important; - content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M19.43%2C6.92v6.59h1.05v1.05H19.43V16.9H18.31V14.56H13.66v-1c.43-.49.87-1%2C1.31-1.57s.87-1.13%2C1.27-1.7S17%2C9.14%2C17.36%2C8.57a16.51%2C16.51%2C0%2C0%2C0%2C.86-1.65Zm-4.49%2C6.59h3.37V8.63c-.34.61-.67%2C1.15-1%2C1.63s-.6.91-.87%2C1.3-.56.74-.81%2C1Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; - } - .primary-editor > h5::before { - margin-left: -64px !important; - padding-left: 40px !important; - content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M14%2C16l.58-.76a3.67%2C3.67%2C0%2C0%2C0%2C1%2C.58A3.44%2C3.44%2C0%2C0%2C0%2C16.8%2C16a2.17%2C2.17%2C0%2C0%2C0%2C1.58-.6A2%2C2%2C0%2C0%2C0%2C19%2C13.88a1.85%2C1.85%2C0%2C0%2C0-.64-1.5%2C2.83%2C2.83%2C0%2C0%2C0-1.86-.54c-.27%2C0-.55%2C0-.86%2C0s-.58%2C0-.81.06L15.17%2C7H19.7V8H16.14l-.2%2C2.88.47%2C0h.43a3.5%2C3.5%2C0%2C0%2C1%2C2.43.79%2C2.74%2C2.74%2C0%2C0%2C1%2C.88%2C2.16%2C3%2C3%2C0%2C0%2C1-.94%2C2.3%2C3.41%2C3.41%2C0%2C0%2C1-2.4.87%2C4.45%2C4.45%2C0%2C0%2C1-1.5-.24A4.81%2C4.81%2C0%2C0%2C1%2C14%2C16Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; - } - .primary-editor > h6::before { - margin-left: -64px !important; - padding-left: 40px !important; - content: url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20width%3D%2218px%22%20height%3D%2218px%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20viewBox%3D%220%200%2024%2015.56%22%3E%3Cdefs%3E%3Cstyle%3E.cls-1%7Bfill%3A%23666%3B%7D%3C%2Fstyle%3E%3C%2Fdefs%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M11.17%2C16.8H10V12.33H5V16.8H3.8V7H5v4.3H10V7h1.15Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M20.18%2C13.7a3.24%2C3.24%2C0%2C0%2C1-.88%2C2.38%2C2.94%2C2.94%2C0%2C0%2C1-2.2.9%2C2.69%2C2.69%2C0%2C0%2C1-2.31-1.17A5.59%2C5.59%2C0%2C0%2C1%2C14%2C12.49a12.18%2C12.18%2C0%2C0%2C1%2C.2-2.14%2C5.16%2C5.16%2C0%2C0%2C1%2C.84-2A3.65%2C3.65%2C0%2C0%2C1%2C16.27%2C7.2%2C3.71%2C3.71%2C0%2C0%2C1%2C18%2C6.84%2C3.14%2C3.14%2C0%2C0%2C1%2C19%2C7a3.59%2C3.59%2C0%2C0%2C1%2C1%2C.5l-.56.77a2.3%2C2.3%2C0%2C0%2C0-1.49-.48A2.3%2C2.3%2C0%2C0%2C0%2C16.79%2C8a3%2C3%2C0%2C0%2C0-.92.85%2C3.79%2C3.79%2C0%2C0%2C0-.56%2C1.25%2C6.56%2C6.56%2C0%2C0%2C0-.19%2C1.65h0a2.61%2C2.61%2C0%2C0%2C1%2C1-.84%2C2.91%2C2.91%2C0%2C0%2C1%2C1.23-.28%2C2.63%2C2.63%2C0%2C0%2C1%2C2%2C.85A3.09%2C3.09%2C0%2C0%2C1%2C20.18%2C13.7ZM19%2C13.78a2.28%2C2.28%2C0%2C0%2C0-.5-1.62%2C1.67%2C1.67%2C0%2C0%2C0-1.29-.54%2C2%2C2%2C0%2C0%2C0-1.5.58%2C2%2C2%2C0%2C0%2C0-.56%2C1.4%2C2.65%2C2.65%2C0%2C0%2C0%2C.55%2C1.74%2C1.85%2C1.85%2C0%2C0%2C0%2C2.78.1A2.38%2C2.38%2C0%2C0%2C0%2C19%2C13.78Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3Cpath%20class%3D%22cls-1%22%20d%3D%22M21%2C5a2.25%2C2.25%2C0%2C0%2C1%2C2.25%2C2.25v9.56A2.25%2C2.25%2C0%2C0%2C1%2C21%2C19H3A2.25%2C2.25%2C0%2C0%2C1%2C.75%2C16.78V7.22A2.25%2C2.25%2C0%2C0%2C1%2C3%2C5H21m0-.75H3a3%2C3%2C0%2C0%2C0-3%2C3v9.56a3%2C3%2C0%2C0%2C0%2C3%2C3H21a3%2C3%2C0%2C0%2C0%2C3-3V7.22a3%2C3%2C0%2C0%2C0-3-3Z%22%20transform%3D%22translate(0%20-4.22)%22%2F%3E%3C%2Fsvg%3E") !important; - } - .primary-editor > p, .primary-editor h1, .primary-editor h2, .primary-editor h3, .primary-editor h4, .primary-editor h5, .primary-editor h6, .primary-editor pre, .primary-editor blockquote, .primary-editor table, .primary-editor ul, .primary-editor ol, .primary-editor hr{ - max-width: unset - } - `, + innerHTML: await getFileContent(rootURI + "chrome/content/styles/editor.css"), }, ignoreIfExists: true, }, diff --git a/src/modules/editor/toolbar.ts b/src/modules/editor/toolbar.ts index 373f959..a680b62 100644 --- a/src/modules/editor/toolbar.ts +++ b/src/modules/editor/toolbar.ts @@ -4,17 +4,12 @@ import { getLineAtCursor, getSectionAtCursor } from "../../utils/editor"; import { showHint } from "../../utils/hint"; import { getNoteLink, getNoteLinkParams } from "../../utils/link"; import { getString } from "../../utils/locale"; -import { - addLineToNote, - getNoteTreeFlattened, - getNoteType, -} from "../../utils/note"; +import { addLineToNote, getNoteTreeFlattened } from "../../utils/note"; import { getPref } from "../../utils/prefs"; import { slice } from "../../utils/str"; export async function initEditorToolbar(editor: Zotero.EditorInstance) { const noteItem = editor._item; - const noteType = getNoteType(noteItem.id); const toolbar = await registerEditorToolbar(editor, makeId("toolbar")); // Settings @@ -56,22 +51,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) { id: makeId("settings-openWorkspace"), text: getString("editor.toolbar.settings.openWorkspace"), callback: (e) => { - addon.hooks.onOpenWorkspace("tab"); - }, - }, - { - id: makeId("settings-setWorkspace"), - text: getString("editor.toolbar.settings.setWorkspace"), - callback: (e) => { - addon.hooks.onSetWorkspaceNote(e.editor._item.id, "main"); - }, - }, - { - id: makeId("settings-previewInWorkspace"), - text: getString("editor.toolbar.settings.previewInWorkspace"), - callback: (e) => { - addon.hooks.onOpenWorkspace("tab"); - addon.hooks.onSetWorkspaceNote(e.editor._item.id, "preview"); + addon.hooks.onOpenWorkspace(noteItem, "tab"); }, }, { @@ -221,103 +201,95 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) { }); // Center button - if (noteType === "main") { - registerEditorToolbarElement( + + 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, - toolbar, + linkButton, + `${config.addonRef}-link-popup`, "middle", - ztoolkit.UI.createElement(editor._iframeWindow.document, "div", { - properties: { innerHTML: getString("editor.toolbar.main") }, - }), + linkMenu, ); - } else { - 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 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) => { - 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, + 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(); + }; - 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); - } - }); - editor._iframeWindow.document.addEventListener("click", onExitMenu); - } + 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); + } + }); + editor._iframeWindow.document.addEventListener("click", onExitMenu); // Export // const exportButton = await registerEditorToolbarDropdown( diff --git a/src/modules/export/api.ts b/src/modules/export/api.ts index 19455c8..91c4c18 100644 --- a/src/modules/export/api.ts +++ b/src/modules/export/api.ts @@ -212,7 +212,7 @@ async function toFreeMind(noteItem: Zotero.Item) { } async function embedLinkedNotes(noteItem: Zotero.Item): Promise { - const parser = ztoolkit.getDOMParser(); + const parser = new DOMParser(); const globalCitationData = getNoteCitationData(noteItem as Zotero.Item); diff --git a/src/modules/menu.ts b/src/modules/menu.ts index aa8d78d..1c688a5 100644 --- a/src/modules/menu.ts +++ b/src/modules/menu.ts @@ -17,22 +17,6 @@ export function registerMenus() { ); }, }); - ztoolkit.Menu.register("item", { - tag: "menuitem", - label: getString("menuItem.setMainNote"), - icon: `chrome://${config.addonRef}/content/icons/favicon.png`, - commandListener: (ev) => { - addon.hooks.onSetWorkspaceNote(ZoteroPane.getSelectedItems()[0].id); - }, - getVisibility: (elem, ev) => { - const items = ZoteroPane.getSelectedItems(); - return ( - items.length == 1 && - items[0].isNote() && - items[0].id !== addon.data.workspace.mainId - ); - }, - }); // menuEdit const menuEditAnchor = document.querySelector( @@ -133,88 +117,66 @@ export function registerMenus() { menuFileAnchor, ); - document - .querySelector(`#${recentMainNotesMenuId}`) - ?.addEventListener("popupshowing", () => { - const popup = document.querySelector(`#${recentMainNotesMenuPopupId}`)!; - removeChildren(popup); - const children = Zotero.Items.get( - ((getPref("recentMainNoteIds") as string) || "") - .split(",") - .map((id) => parseInt(id)) - .filter((id) => id !== addon.data.workspace.mainId), - ) - .filter((item) => item.isNote()) - .map((item) => ({ - tag: "menuitem", - attributes: { - label: slice( - `${slice(item.getNoteTitle().trim() || item.key, 30)} - ${ - item.parentItem - ? "📄" + item.parentItem.getField("title") - : "📁" + - Zotero.Collections.get(item.getCollections()) - .map( - (collection) => (collection as Zotero.Collection).name, - ) - .join(", ") - }`, - 200, - ), - }, - listeners: [ - { - type: "command", - listener: () => { - addon.hooks.onSetWorkspaceNote(item.id, "main"); - addon.hooks.onOpenWorkspace(); - }, - }, - ], - })); - const defaultChildren = [ - { - tag: "menuitem", - attributes: { - label: getString("menuFile-openRecent-empty"), - disabled: true, - }, - }, - ]; + // document + // .querySelector(`#${recentMainNotesMenuId}`) + // ?.addEventListener("popupshowing", () => { + // const popup = document.querySelector(`#${recentMainNotesMenuPopupId}`)!; + // removeChildren(popup); + // const children = Zotero.Items.get( + // ((getPref("recentMainNoteIds") as string) || "") + // .split(",") + // .map((id) => parseInt(id)) + // .filter((id) => id !== addon.data.workspace.mainId), + // ) + // .filter((item) => item.isNote()) + // .map((item) => ({ + // tag: "menuitem", + // attributes: { + // label: slice( + // `${slice(item.getNoteTitle().trim() || item.key, 30)} - ${ + // item.parentItem + // ? "📄" + item.parentItem.getField("title") + // : "📁" + + // Zotero.Collections.get(item.getCollections()) + // .map( + // (collection) => (collection as Zotero.Collection).name, + // ) + // .join(", ") + // }`, + // 200, + // ), + // }, + // listeners: [ + // { + // type: "command", + // listener: () => { + // addon.hooks.onSetWorkspaceNote(item.id, "main"); + // addon.hooks.onOpenWorkspace(); + // }, + // }, + // ], + // })); + // const defaultChildren = [ + // { + // tag: "menuitem", + // attributes: { + // label: getString("menuFile-openRecent-empty"), + // disabled: true, + // }, + // }, + // ]; - ztoolkit.UI.appendElement( - { - tag: "fragment", - children: children.length === 0 ? defaultChildren : children, - enableElementRecord: false, - }, - popup, - ); - return true; - }); + // ztoolkit.UI.appendElement( + // { + // tag: "fragment", + // children: children.length === 0 ? defaultChildren : children, + // enableElementRecord: false, + // }, + // popup, + // ); + // return true; + // }); - ztoolkit.Menu.register( - "menuFile", - { - tag: "menuitem", - label: getString("menuFile-openMainNote"), - icon: `chrome://${config.addonRef}/content/icons/favicon.png`, - commandListener: async (ev) => { - const selectedIds = await itemPicker(); - if ( - selectedIds?.length === 1 && - Zotero.Items.get(selectedIds[0]).isNote() - ) { - addon.hooks.onSetWorkspaceNote(selectedIds[0], "main"); - addon.hooks.onOpenWorkspace(); - } else { - window.alert(getString("menuFile-openMainNote-error")); - } - }, - }, - "after", - menuFileAnchor, - ); ztoolkit.Menu.register( "menuFile", { tag: "menuseparator" }, @@ -256,28 +218,11 @@ export function registerMenus() { "after", menuFileAnchor, ); - ztoolkit.Menu.register( - "menuFile", - { - tag: "menuitem", - label: getString("menuAddNote.newMainNote"), - icon: `chrome://${config.addonRef}/content/icons/favicon.png`, - commandListener: addon.hooks.onCreateWorkspaceNote, - }, - "after", - menuFileAnchor, - ); // create note menu in library const newNoteMenu = document .querySelector("#zotero-tb-note-add") ?.querySelector("menupopup") as XUL.MenuPopup; - ztoolkit.Menu.register(newNoteMenu, { - tag: "menuitem", - label: getString("menuAddNote.newMainNote"), - icon: `chrome://${config.addonRef}/content/icons/favicon.png`, - commandListener: addon.hooks.onCreateWorkspaceNote, - }); ztoolkit.Menu.register(newNoteMenu, { tag: "menuitem", label: getString("menuAddNote.newTemplateStandaloneNote"), diff --git a/src/modules/viewItems.ts b/src/modules/viewItems.ts new file mode 100644 index 0000000..43e76f1 --- /dev/null +++ b/src/modules/viewItems.ts @@ -0,0 +1,28 @@ +import { PatchHelper } from "zotero-plugin-toolkit/dist/helpers/patch"; + +export function patchViewItems(win: Window) { + // @ts-ignore + const ZoteroPane = win.ZoteroPane; + new PatchHelper().setData({ + target: ZoteroPane, + funcSign: "viewItems", + patcher: (origin) => + function (items: Zotero.Item[], event?: KeyboardEvent) { + if (!addon.data.alive || event?.shiftKey) { + // @ts-ignore + return origin.apply(this, [items, event]); + } + const otherItems = []; + for (const item of items) { + if (item.isNote()) { + addon.hooks.onOpenWorkspace(item, "tab"); + continue; + } + otherItems.push(item); + } + // @ts-ignore + return origin.apply(this, [otherItems, event]); + }, + enabled: true, + }); +} diff --git a/src/modules/workspace/content.ts b/src/modules/workspace/content.ts index 9ca1ec9..97fb6f2 100644 --- a/src/modules/workspace/content.ts +++ b/src/modules/workspace/content.ts @@ -1,489 +1,20 @@ -import { config } from "../../../package.json"; -import { ICONS } from "../../utils/config"; -import { itemPicker } from "../../utils/itemPicker"; -import { getString } from "../../utils/locale"; -import { getNoteTreeFlattened } from "../../utils/note"; -import { getPref } from "../../utils/prefs"; -import { waitUtilAsync } from "../../utils/wait"; -import { OutlineType } from "../../utils/workspace"; import { saveFreeMind as _saveFreeMind } from "../export/freemind"; -function makeId(key: string) { - return `betternotes-workspace-${key}`; -} - -export function initWorkspace(container: XUL.Box | undefined) { +export function initWorkspace(container: XUL.Box, item: Zotero.Item) { if (!container) { return; } - function makeTooltipProp( - id: string, - content: string, - title: string, - callback: (ev: Event) => void, - ) { - return { - id, - tag: "button", - namespace: "html", - classList: ["tool-button"], - properties: { - innerHTML: content, - title, - }, - listeners: [ - { - type: "click", - listener: callback, - }, - ], - }; - } - ztoolkit.UI.appendElement( - { - tag: "hbox", - id: makeId("top-container"), - styles: { width: "100%", height: "100%" }, - properties: {}, - attributes: { - flex: "1", - }, - children: [ - { - tag: "vbox", - id: makeId("outline-container"), - styles: { - overflow: "hidden", - display: "flex", - flexDirection: "column", - }, - attributes: { - width: "330", - minwidth: "300", - flex: "1", - }, - children: [ - { - tag: "link", - properties: { - rel: "stylesheet", - href: `chrome://${config.addonRef}/content/toolbutton.css`, - }, - }, - { - tag: "div", - id: makeId("outline-content"), - styles: { - height: "100%", - }, - children: [ - { - tag: "iframe", - id: makeId("outline-iframe"), - properties: { - width: "100%", - }, - styles: { - border: "0", - height: "100%", - }, - }, - ], - }, - { - tag: "div", - id: makeId("outline-buttons"), - styles: { - height: "50px", - display: "flex", - flexDirection: "row", - justifyContent: "space-between", - margin: "10px 20px 10px 20px", - }, - children: [ - makeTooltipProp( - makeId("switchOutline"), - ICONS.switchOutline, - getString("workspace.switchOutline"), - (ev) => { - setOutline(container); - }, - ), - makeTooltipProp( - makeId("saveOutlineImage"), - ICONS.saveOutlineImage, - getString("workspace.saveOutlineImage"), - (ev) => { - saveImage(container); - }, - ), - makeTooltipProp( - makeId("saveOutlineFreeMind"), - ICONS.saveOutlineFreeMind, - getString("workspace.saveOutlineFreeMind"), - (ev) => { - saveFreeMind(); - }, - ), - ], - }, - ], - }, - { - tag: "splitter", - id: makeId("outline-splitter"), - attributes: { collapse: "before" }, - children: [ - { - tag: "grippy", - }, - ], - }, - { - tag: "vbox", - id: makeId("editor-main-container"), - attributes: { - flex: "1", - width: "700", - }, - styles: { - background: "var(--material-background50)", - }, - }, - { - tag: "splitter", - id: makeId("preview-splitter"), - attributes: { collapse: "after", state: "collapsed" }, - children: [ - { - tag: "grippy", - }, - ], - }, - { - tag: "vbox", - id: makeId("editor-preview-container"), - attributes: { - flex: "1", - width: "500", - }, - }, - ], - }, - container, - ); - // Manually add custom editor items in Zotero 7 + + container.style.minWidth = "0px"; + container.style.minHeight = "0px"; + // @ts-ignore const customElements = container.ownerGlobal .customElements as CustomElementRegistry; - const mainEditorContainer = container.querySelector( - `#${makeId("editor-main-container")}`, - ); - const previewEditorContainer = container.querySelector( - `#${makeId("editor-preview-container")}`, - ); - const mainEditor = new (customElements.get("note-editor")!)(); - mainEditor.id = makeId("editor-main"); - mainEditor.setAttribute("flex", "1"); - const previewEditor = new (customElements.get("note-editor")!)(); - previewEditor.id = makeId("editor-preview"); - previewEditor.setAttribute("flex", "1"); - mainEditorContainer?.append(mainEditor); - previewEditorContainer?.append(previewEditor); - const outlineContainer = container.querySelector( - `#${makeId("outline-container")}`, - ) as XUL.Box; - outlineContainer.style.background = "var(--material-sidepane)"; - const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))( - (mutations) => { - if (outlineContainer.getAttribute("collapsed") === "true") { - outlineContainer.style.removeProperty("display"); - } else { - outlineContainer.style.display = "flex"; - } - }, - ); - outlineMut.observe(outlineContainer, { - attributes: true, - attributeFilter: ["collapsed"], - }); - - setOutline(container, OutlineType.treeView); - initWorkspaceEditor(container, "main", addon.data.workspace.mainId); -} - -export async function initWorkspaceEditor( - container: XUL.Box, - type: "main" | "preview", - noteId: number, - options: { - lineIndex?: number; - sectionName?: string; - } = {}, -) { - const noteItem = Zotero.Items.get(noteId); - if (!noteItem || !noteItem.isNote()) { - ztoolkit.UI.appendElement( - { - tag: "div", - id: makeId("emptyWorkspaceGuide"), - styles: { - position: "absolute", - top: "0", - left: "0", - width: "100%", - height: "100%", - display: "flex", - flexDirection: "column", - justifyContent: "center", - alignItems: "center", - textAlign: "center", - backgroundColor: "rgba(255,255,255,0.8)", - zIndex: "100", - }, - children: [ - { - tag: "div", - properties: { - innerHTML: getString("workspace-emptyWorkspaceGuideInfo"), - }, - styles: { - fontSize: "20px", - fontWeight: "bold", - color: "gray", - }, - }, - { - tag: "button", - namespace: "xul", - properties: { - label: getString("workspace-emptyWorkspaceGuideOpen"), - }, - styles: { - fontSize: "16px", - }, - listeners: [ - { - type: "command", - listener: async (ev) => { - const selectedIds = await itemPicker(); - if ( - selectedIds?.length === 1 && - Zotero.Items.get(selectedIds[0]).isNote() - ) { - addon.hooks.onSetWorkspaceNote(selectedIds[0], "main"); - addon.hooks.onOpenWorkspace(); - } else { - window.alert(getString("menuFile-openMainNote-error")); - } - }, - }, - ], - }, - { - tag: "div", - properties: { - innerHTML: getString("workspace-emptyWorkspaceGuideOr"), - }, - styles: { - fontSize: "16px", - color: "gray", - }, - }, - { - tag: "button", - namespace: "xul", - properties: { - label: getString("workspace-emptyWorkspaceGuideCreate"), - }, - styles: { - fontSize: "16px", - }, - listeners: [ - { - type: "command", - listener: () => { - addon.hooks.onCreateWorkspaceNote(); - }, - }, - ], - }, - ], - }, - container, - ); - return; - } else { - container.querySelector(`#${makeId("emptyWorkspaceGuide")}`)?.remove(); - } - const editorElem = container.querySelector( - `#${makeId("editor-" + type)}`, - ) as EditorElement; - await waitUtilAsync(() => Boolean(editorElem._initialized)) - .then(() => ztoolkit.log("ok")) - .catch(() => ztoolkit.log("fail")); - if (!editorElem._initialized) { - throw new Error("initNoteEditor: waiting initialization failed"); - } - editorElem.mode = "edit"; - editorElem.viewMode = "library"; - editorElem.parent = undefined; - editorElem.item = noteItem; - await waitUtilAsync(() => Boolean(editorElem._editorInstance)); - await editorElem._editorInstance._initPromise; - if (typeof options.lineIndex === "number") { - addon.api.editor.scroll(editorElem._editorInstance, options.lineIndex); - } - if (typeof options.sectionName === "string") { - addon.api.editor.scrollToSection( - editorElem._editorInstance, - options.sectionName, - ); - } - return; -} - -function getContainerType( - container: XUL.Box | undefined, -): "tab" | "window" | "unknown" { - if (!container) { - return "unknown"; - } - return (container.getAttribute("workspace-type") || "unknown") as - | "tab" - | "window" - | "unknown"; -} - -export function toggleOutlinePane(visibility?: boolean, container?: XUL.Box) { - const splitter = container?.querySelector(`#${makeId("outline-splitter")}`); - if (typeof visibility === "undefined") { - visibility = splitter?.getAttribute("state") === "collapsed"; - } - splitter?.setAttribute("state", visibility ? "open" : "collapsed"); -} - -export function togglePreviewPane(visibility?: boolean, container?: XUL.Box) { - const splitter = container?.querySelector(`#${makeId("preview-splitter")}`); - if (typeof visibility === "undefined") { - visibility = splitter?.getAttribute("state") === "collapsed"; - } - splitter?.setAttribute("state", visibility ? "open" : "collapsed"); -} - -export function toggleNotesPane(visibility?: boolean) { - const splitter = document?.querySelector("#zotero-context-splitter"); - if (typeof visibility === "undefined") { - visibility = splitter?.getAttribute("state") === "collapsed"; - } - splitter?.setAttribute("state", visibility ? "open" : "collapsed"); -} - -export function getWorkspaceEditor( - workspaceType: "tab" | "window", - editorType: "main" | "preview" = "main", -) { - const container = - workspaceType === "tab" - ? addon.data.workspace.tab.container - : addon.data.workspace.window.container; - return ( - container?.querySelector(`#${makeId(`editor-${editorType}`)}`) as - | EditorElement - | undefined - )?._editorInstance; -} - -const SRC_LIST = [ - "", - `chrome://${config.addonRef}/content/treeView.html`, - `chrome://${config.addonRef}/content/mindMap.html`, - `chrome://${config.addonRef}/content/bubbleMap.html`, -]; - -function setOutline( - container: XUL.Box, - newType: OutlineType = OutlineType.empty, -) { - if (newType === OutlineType.empty) { - newType = addon.data.workspace.outline + 1; - } - if (newType > OutlineType.bubbleMap) { - newType = OutlineType.treeView; - } - addon.data.workspace.outline = newType; - ( - container.querySelector(`#${makeId("saveOutlineImage")}`) as HTMLDivElement - ).hidden = newType === OutlineType.treeView; - ( - container.querySelector( - `#${makeId("saveOutlineFreeMind")}`, - ) as HTMLDivElement - ).hidden = newType === OutlineType.treeView; - const iframe = container.querySelector( - `#${makeId("outline-iframe")}`, - ) as HTMLIFrameElement; - iframe.setAttribute("src", SRC_LIST[addon.data.workspace.outline]); - updateOutline(container); - updateOutlineButtons(container); -} - -export async function updateOutline(container: XUL.Box) { - const iframe = container.querySelector( - `#${makeId("outline-iframe")}`, - ) as HTMLIFrameElement; - await waitUtilAsync( - () => iframe.contentWindow?.document.readyState === "complete", - ); - iframe.contentWindow?.postMessage( - { - type: "setMindMapData", - nodes: getNoteTreeFlattened( - Zotero.Items.get(addon.data.workspace.mainId), - { keepLink: !!getPref("workspace.outline.keepLinks") }, - ), - workspaceType: getContainerType(container), - expandLevel: getPref("workspace.outline.expandLevel"), - }, - "*", - ); -} - -function updateOutlineButtons(container: XUL.Box) { - const outlineType = addon.data.workspace.outline; - const isTreeView = outlineType === OutlineType.treeView; - ( - container.querySelector(`#${makeId("saveOutlineImage")}`) as HTMLDivElement - ).style.visibility = isTreeView ? "hidden" : "visible"; - ( - container.querySelector( - `#${makeId("saveOutlineFreeMind")}`, - ) as HTMLDivElement - ).style.visibility = isTreeView ? "hidden" : "visible"; -} - -function saveImage(container: XUL.Box) { - const iframe = container.querySelector( - `#${makeId("outline-iframe")}`, - ) as HTMLIFrameElement; - iframe.contentWindow?.postMessage( - { - type: "saveSVG", - }, - "*", - ); -} - -async function saveFreeMind() { - // TODO: uncouple this part - const filename = await new ztoolkit.FilePicker( - `${Zotero.getString("fileInterface.export")} FreeMind XML`, - "save", - [["FreeMind XML File(*.mm)", "*.mm"]], - `${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.mm`, - ).open(); - if (filename) { - await _saveFreeMind(filename, addon.data.workspace.mainId); - } + const workspace = new (customElements.get("bn-workspace")!)() as any; + container.append(workspace); + workspace.item = item; + workspace.containerType = "tab"; + workspace.render(); } diff --git a/src/modules/workspace/message.ts b/src/modules/workspace/message.ts deleted file mode 100644 index 61ae324..0000000 --- a/src/modules/workspace/message.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - getEditorInstance, - moveHeading, - updateHeadingTextAtLine, -} from "../../utils/editor"; -import { showHintWithLink } from "../../utils/hint"; -import { getNoteLinkParams } from "../../utils/link"; -import { getNoteTree, getNoteTreeNodeById } from "../../utils/note"; -import { formatPath } from "../../utils/str"; - -export async function messageHandler(ev: MessageEvent) { - switch (ev.data.type) { - case "jumpNode": { - const editor = addon.api.workspace.getWorkspaceEditor( - ev.data.workspaceType, - "main", - ); - if (!editor) { - return; - } - addon.api.editor.scroll(editor, ev.data.lineIndex); - return; - } - case "openNote": { - const linkParams = getNoteLinkParams(ev.data.link); - if (!linkParams.noteItem) { - return; - } - addon.hooks.onOpenNote(linkParams.noteItem.id, "preview", { - lineIndex: linkParams.lineIndex || undefined, - }); - return; - } - case "moveNode": { - const noteItem = Zotero.Items.get(addon.data.workspace.mainId); - const tree = getNoteTree(noteItem); - const fromNode = getNoteTreeNodeById(noteItem, ev.data.fromID, tree); - const toNode = getNoteTreeNodeById(noteItem, ev.data.toID, tree); - moveHeading( - getEditorInstance(noteItem.id), - fromNode!, - toNode!, - ev.data.moveType, - ); - return; - } - case "editNode": { - const editor = addon.api.workspace.getWorkspaceEditor( - ev.data.workspaceType, - "main", - ); - if (!editor) { - return; - } - updateHeadingTextAtLine( - editor, - ev.data.lineIndex, - ev.data.text.replace(/[\r\n]/g, ""), - ); - return; - } - case "saveSVGReturn": { - const filename = await new ztoolkit.FilePicker( - `${Zotero.getString("fileInterface.export")} SVG Image`, - "save", - [["SVG File(*.svg)", "*.svg"]], - `${Zotero.Items.get(addon.data.workspace.mainId).getNoteTitle()}.svg`, - ).open(); - if (filename) { - await Zotero.File.putContentsAsync(formatPath(filename), ev.data.image); - showHintWithLink( - `Image Saved to ${filename}`, - "Show in Folder", - (ev) => { - Zotero.File.reveal(filename); - }, - ); - } - return; - } - default: - return; - } -} diff --git a/src/modules/workspace/tab.ts b/src/modules/workspace/tab.ts index 72e3004..d443f83 100644 --- a/src/modules/workspace/tab.ts +++ b/src/modules/workspace/tab.ts @@ -1,13 +1,7 @@ import { config } from "../../../package.json"; -import { ICONS } from "../../utils/config"; -import { showHint } from "../../utils/hint"; -import { getString } from "../../utils/locale"; -import { getPref, setPref } from "../../utils/prefs"; -import { waitUtilAsync } from "../../utils/wait"; -// TODO: uncouple these imports -import { messageHandler } from "./message"; +import { initWorkspace } from "./content"; -export const TAB_TYPE = "betternotes"; +export const TAB_TYPE = "note"; export function registerWorkspaceTab(win: Window) { const doc = win.document; @@ -29,18 +23,17 @@ export function registerWorkspaceTab(win: Window) { { type: "command", listener: (ev) => { - if ((ev as MouseEvent).shiftKey) { - addon.hooks.onOpenWorkspace("window"); - } else { - addon.hooks.onOpenWorkspace("tab"); - } + // if ((ev as MouseEvent).shiftKey) { + // addon.hooks.onOpenWorkspace("window"); + // } else { + // addon.hooks.onOpenWorkspace("tab"); + // } }, }, ], }, spacer, ) as XUL.ToolBarButton; - win.addEventListener("message", messageHandler, false); const collectionSearch = doc.querySelector("#zotero-collections-search")!; const ob = new (ztoolkit.getGlobal("MutationObserver"))((muts) => { tabButton.hidden = !!collectionSearch?.classList.contains("visible"); @@ -54,347 +47,47 @@ export function registerWorkspaceTab(win: Window) { "unload", () => { ob.disconnect(); - win.removeEventListener("message", messageHandler); }, { once: true }, ); } -export async function openWorkspaceTab() { - if (addon.data.workspace.tab.active) { - Zotero_Tabs.select(addon.data.workspace.tab.id!); +export async function openWorkspaceTab(item: Zotero.Item) { + const currentTab = Zotero_Tabs._tabs.find( + (tab) => tab.data?.itemID == item.id, + ); + if (currentTab) { + Zotero_Tabs.select(currentTab.id); return; } const { id, container } = Zotero_Tabs.add({ type: TAB_TYPE, - title: getString("tab.name"), + title: item.getNoteTitle(), index: 1, data: { - itemID: addon.data.workspace.mainId, + itemID: item.id, }, select: false, - onClose: () => { - deActivateWorkspaceTab(); - }, + onClose: () => {}, }); - addon.data.workspace.tab.id = id; - container.setAttribute("workspace-type", "tab"); - addon.data.workspace.tab.container = container; - - await activateWorkspaceTab(); + initWorkspace(container, item); Zotero_Tabs.select(id); } -function hoverWorkspaceTab(hovered: boolean) { - Array.from(document.querySelectorAll(".tab-toggle")).forEach((elem) => { - (elem as HTMLDivElement).style.visibility = hovered ? "visible" : "hidden"; - }); - const tabElem = document.querySelector( - `.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`, - ) as HTMLDivElement; - const content = tabElem.querySelector(".tab-name") as HTMLDivElement; - content.removeAttribute("style"); - if (hovered) { - content.style["-moz-box-pack" as any] = "start"; - } -} +let contextPaneOpen: boolean | undefined = undefined; -function updateWorkspaceTabToggleButton( - type: "outline" | "preview" | "notes", - state: "open" | "collapsed", -) { - const elem = document.querySelector( - `#betternotes-tab-toggle-${type}`, - ) as HTMLDivElement; - if (!elem) { +export function onTabSelect(tabType: string) { + const ZoteroContextPane = ztoolkit.getGlobal("ZoteroContextPane"); + const splitter = ZoteroContextPane.getSplitter(); + + if (tabType === TAB_TYPE) { + contextPaneOpen = splitter.getAttribute("state") != "collapsed"; + splitter.setAttribute("state", "collapsed"); + } else if (typeof contextPaneOpen !== "undefined") { + splitter.setAttribute("state", contextPaneOpen ? "open" : "collapsed"); + contextPaneOpen = undefined; + } else { return; } - if (state !== "collapsed") { - state = "open"; - } - elem.innerHTML = ICONS[`workspace_${type}_${state}`]; -} - -function registerWorkspaceTabPaneObserver() { - const outlineSplitter = document.querySelector( - "#betternotes-workspace-outline-splitter", - ); - const outlineMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => { - updateWorkspaceTabToggleButton( - "outline", - outlineSplitter!.getAttribute("state")! as "open" | "collapsed", - ); - }); - outlineMut.observe(outlineSplitter!, { - attributes: true, - attributeFilter: ["state"], - }); - const previewSplitter = document.querySelector( - "#betternotes-workspace-preview-splitter", - ); - const previeweMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => { - updateWorkspaceTabToggleButton( - "preview", - previewSplitter!.getAttribute("state")! as "open" | "collapsed", - ); - }); - previeweMut.observe(previewSplitter!, { - attributes: true, - attributeFilter: ["state"], - }); - const notesSplitter = document.querySelector("#zotero-context-splitter"); - const notesMut = new (ztoolkit.getGlobal("MutationObserver"))((muts) => { - updateWorkspaceTabToggleButton( - "notes", - notesSplitter!.getAttribute("state")! as "open" | "collapsed", - ); - }); - notesMut.observe(notesSplitter!, { - attributes: true, - attributeFilter: ["state"], - }); -} - -function isContextPaneInitialized() { - return ( - (document.querySelector(".notes-pane-deck")?.childElementCount || 0) > 0 - ); -} - -export async function activateWorkspaceTab() { - if (Zotero_Tabs.selectedType === TAB_TYPE && isContextPaneInitialized()) { - const tabToolbar = document.querySelector("#zotero-tab-toolbar") as XUL.Box; - tabToolbar && (tabToolbar.style.visibility = "collapse"); - const toolbar = document.querySelector( - "#zotero-context-toolbar-extension", - ) as XUL.Box; - if (toolbar) { - toolbar.style.visibility = "collapse"; - toolbar.nextElementSibling?.setAttribute("selectedIndex", "1"); - } - } - - if (addon.data.workspace.tab.active) { - ztoolkit.log("workspace tab is already active"); - return; - } - setWorkspaceTabStatus(true); - // reset tab style - await waitUtilAsync(() => - Boolean( - document.querySelector( - `.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`, - ), - ), - ); - const tabElem = document.querySelector( - `.tabs-wrapper .tab[data-id=${addon.data.workspace.tab.id}]`, - ) as HTMLDivElement; - tabElem.removeAttribute("style"); - const content = tabElem.querySelector(".tab-name") as HTMLDivElement; - const close = tabElem.querySelector(".tab-close") as HTMLDivElement; - content.removeAttribute("style"); - content.append(document.createTextNode(getString("tab.name"))); - close.style.removeProperty("visibility"); - ztoolkit.UI.insertElementBefore( - { - tag: "fragment", - children: [ - { - tag: "div", - id: "betternotes-tab-toggle-outline", - classList: ["tab-close", "tab-toggle"], - styles: { - right: "56px", - }, - properties: { - innerHTML: ICONS.workspace_outline_open, - }, - listeners: [ - { - type: "click", - listener: (ev) => { - addon.hooks.onToggleWorkspacePane( - "outline", - undefined, - addon.data.workspace.tab.container, - ); - }, - }, - ], - }, - { - tag: "div", - id: "betternotes-tab-toggle-preview", - classList: ["tab-close", "tab-toggle"], - styles: { - right: "40px", - }, - properties: { - innerHTML: ICONS.workspace_preview_collapsed, - }, - listeners: [ - { - type: "click", - listener: (ev) => { - addon.hooks.onToggleWorkspacePane( - "preview", - undefined, - addon.data.workspace.tab.container, - ); - }, - }, - ], - }, - { - tag: "div", - id: "betternotes-tab-toggle-notes", - classList: ["tab-close", "tab-toggle"], - styles: { - right: "24px", - }, - properties: { - innerHTML: - document - .querySelector("#zotero-context-splitter") - ?.getAttribute("state") === "open" - ? ICONS.workspace_notes_open - : ICONS.workspace_notes_collapsed, - }, - listeners: [ - { - type: "click", - listener: (ev) => { - if (isContextPaneInitialized()) { - addon.hooks.onToggleWorkspacePane("notes"); - return; - } - showHint(getString("workspace.notesPane.hint")); - }, - }, - ], - }, - ], - }, - close, - ); - hoverWorkspaceTab(false); - tabElem.addEventListener("mouseenter", () => { - if (Zotero_Tabs.selectedType !== "betternotes") { - return; - } - hoverWorkspaceTab(true); - }); - tabElem.addEventListener("mousedown", () => hoverWorkspaceTab(true)); - tabElem.addEventListener("mouseleave", () => hoverWorkspaceTab(false)); - tabElem.addEventListener("mousedown", async (ev) => { - if (ev.button !== 2) { - return; - } - await Zotero.Promise.delay(300); - const menu = document - .querySelector("#zotero-itemmenu") - ?.parentElement?.lastElementChild?.querySelector("menu") - ?.querySelector("menupopup")?.lastElementChild; - menu?.addEventListener("click", () => { - addon.hooks.onOpenWorkspace("window"); - }); - }); - // load workspace content - const container = addon.data.workspace.tab.container; - initWorkspaceTabDragDrop(container, tabElem); - addon.hooks.onInitWorkspace(container); - registerWorkspaceTabPaneObserver(); - setWorkspaceTabStatus(true); -} - -export function deActivateWorkspaceTab() { - const tabToolbar = document.querySelector("#zotero-tab-toolbar") as XUL.Box; - tabToolbar && tabToolbar.style.removeProperty("visibility"); - const toolbar = document.querySelector( - "#zotero-context-toolbar-extension", - ) as XUL.Box; - toolbar?.style.removeProperty("visibility"); - setWorkspaceTabStatus(false); -} - -function setWorkspaceTabStatus(status: boolean) { - addon.data.workspace.tab.active = status; - setPref("workspace.tab.active", status); -} - -function initWorkspaceTabDragDrop( - container?: XUL.Box, - tabElem?: HTMLDivElement, -) { - if (!container) { - return; - } - const rect = tabElem?.getBoundingClientRect(); - ztoolkit.UI.appendElement( - { - tag: "div", - id: "bn-workspace-tab-drop", - styles: { - background: "#252526", - opacity: "0.6", - width: "100%", - height: "100px", - position: "fixed", - left: "0px", - top: `${rect?.bottom}px`, - textAlign: "center", - display: "flex", - visibility: "hidden", - zIndex: "65535", - }, - properties: { - hidden: true, - ondrop: (ev: DragEvent) => { - addon.hooks.onOpenWorkspace("window"); - }, - ondragenter: (ev: DragEvent) => { - ev.preventDefault(); - ev.stopPropagation(); - dropElem.style.opacity = "0.9"; - if (ev.dataTransfer) { - ev.dataTransfer.dropEffect = "move"; - } - }, - ondragover: (ev: DragEvent) => { - ev.preventDefault(); - ev.stopPropagation(); - }, - ondragleave: (ev: DragEvent) => { - ev.preventDefault(); - ev.stopPropagation(); - dropElem.style.opacity = "0.6"; - }, - }, - children: [ - { - tag: "div", - styles: { - margin: "auto", - textAlign: "center", - color: "#fff", - }, - properties: { - innerHTML: getString("tab.openInWindow"), - }, - }, - ], - enableElementRecord: false, - }, - container, - ); - const dropElem = container.querySelector( - "#bn-workspace-tab-drop", - ) as HTMLDivElement; - tabElem?.addEventListener("dragstart", (ev) => { - dropElem.style.visibility = "visible"; - }); - tabElem?.addEventListener("dragend", (ev) => { - dropElem.style.visibility = "hidden"; - }); + ZoteroContextPane.update(); } diff --git a/src/modules/workspace/window.ts b/src/modules/workspace/window.ts index 855702b..382c83a 100644 --- a/src/modules/workspace/window.ts +++ b/src/modules/workspace/window.ts @@ -1,12 +1,6 @@ import { config } from "../../../package.json"; -import { isWindowAlive } from "../../utils/window"; -import { messageHandler } from "./message"; -export async function openWorkspaceWindow() { - if (isWindowAlive(addon.data.workspace.window.window)) { - addon.data.workspace.window.window?.focus(); - return; - } +export async function openWorkspaceWindow(item: Zotero.Item) { const windowArgs = { _initPromise: Zotero.Promise.defer(), }; @@ -17,16 +11,9 @@ export async function openWorkspaceWindow() { windowArgs, )!; await windowArgs._initPromise.promise; - addon.data.workspace.window.active = true; - addon.data.workspace.window.window = win; - addon.data.workspace.window.container = win.document.querySelector( + + const container = win.document.querySelector( "#workspace-container", ) as XUL.Box; - addon.hooks.onInitWorkspace(addon.data.workspace.window.container); - win.addEventListener("message", messageHandler, false); - win.addEventListener("unload", function onWindowUnload(ev) { - addon.data.workspace.window.active = false; - this.window.removeEventListener("unload", onWindowUnload, false); - this.window.removeEventListener("message", messageHandler, false); - }); + addon.hooks.onInitWorkspace(container, item); } diff --git a/src/utils/note.ts b/src/utils/note.ts index 4a7ad7c..b15a549 100644 --- a/src/utils/note.ts +++ b/src/utils/note.ts @@ -11,7 +11,6 @@ export { parseHTMLLines, getLinesInNote, addLineToNote, - getNoteType, getNoteTree, getNoteTreeFlattened, getNoteTreeNodeById, @@ -197,7 +196,7 @@ async function renderNoteHTML( refNotes = [noteItem]; } - const parser = ztoolkit.getDOMParser(); + const parser = new DOMParser(); const doc = parser.parseFromString(html, "text/html"); const imageAttachments = refNotes.reduce((acc, note) => { acc.push(...Zotero.Items.get(note.getAttachments())); @@ -269,22 +268,12 @@ async function renderNoteHTML( return doc.body.innerHTML; } -function getNoteType(id: number) { - if (id === addon.data.workspace.mainId) { - return "main"; - } else if (id === addon.data.workspace.previewId) { - return "preview"; - } else { - return "default"; - } -} - function getNoteTree( note: Zotero.Item, parseLink: boolean = true, ): TreeModel.Node { const noteLines = getLinesInNote(note); - const parser = ztoolkit.getDOMParser(); + const parser = new DOMParser(); const tree = new TreeModel(); const root = tree.parse({ id: -1, @@ -422,7 +411,7 @@ async function copyEmbeddedImagesInHTML( ztoolkit.log(attachments); - const doc = ztoolkit.getDOMParser().parseFromString(html, "text/html"); + const doc = new DOMParser().parseFromString(html, "text/html"); // Copy note image attachments and replace keys in the new note for (const attachment of attachments) { diff --git a/src/utils/wait.ts b/src/utils/wait.ts index 4183d14..f03b3f6 100644 --- a/src/utils/wait.ts +++ b/src/utils/wait.ts @@ -5,12 +5,12 @@ export function waitUntil( timeout = 10000, ) { const start = Date.now(); - const intervalId = ztoolkit.getGlobal("setInterval")(() => { + const intervalId = setInterval(() => { if (condition()) { - ztoolkit.getGlobal("clearInterval")(intervalId); + clearInterval(intervalId); callback(); } else if (Date.now() - start > timeout) { - ztoolkit.getGlobal("clearInterval")(intervalId); + clearInterval(intervalId); } }, interval); } @@ -22,12 +22,12 @@ export function waitUtilAsync( ) { return new Promise((resolve, reject) => { const start = Date.now(); - const intervalId = ztoolkit.getGlobal("setInterval")(() => { + const intervalId = setInterval(() => { if (condition()) { - ztoolkit.getGlobal("clearInterval")(intervalId); + clearInterval(intervalId); resolve(); } else if (Date.now() - start > timeout) { - ztoolkit.getGlobal("clearInterval")(intervalId); + clearInterval(intervalId); reject(); } }, interval); diff --git a/typings/global.d.ts b/typings/global.d.ts index 0cae16c..a659262 100644 --- a/typings/global.d.ts +++ b/typings/global.d.ts @@ -33,3 +33,22 @@ declare const addon: import("../src/addon").default; declare const __env__: "production" | "development"; declare class Localization {} + +declare class XULElementBase extends HTMLElement { + get content(): DocumentFragment; + init(): void; + destroy(): void; + connectedCallback(): void; + disconnectedCallback(): void; + attributeChangedCallback( + name: string, + oldValue: string, + newValue: string, + ): void; + static get observedAttributes(): string[]; +} + +declare class MozXULElement { + static parseXULToFragment(xul: string): Fragment; + static insertFTLIfNeeded(ftl: string): void; +}