update: for Zotero 7.0.0-beta.70

introduce new design of BN
This commit is contained in:
windingwind 2024-04-09 15:38:06 +08:00 committed by GitHub
commit eaa28efbab
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
96 changed files with 3322 additions and 2281 deletions

View File

@ -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;
}

View File

@ -0,0 +1,6 @@
<svg t="1712588638016" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4980"
width="16" height="16">
<path
d="M848 128H608c-17.67 0-32 14.33-32 32s14.33 32 32 32h178.75L553.38 425.38c-12.5 12.5-12.5 32.75 0 45.25 6.25 6.25 14.44 9.38 22.62 9.38s16.38-3.12 22.62-9.38L832 237.25V416c0 17.67 14.33 32 32 32s32-14.33 32-32V176c0-26.47-21.53-48-48-48zM425.38 553.38L192 786.75V608c0-17.67-14.33-32-32-32s-32 14.33-32 32v240c0 26.47 21.53 48 48 48h240c17.67 0 32-14.33 32-32s-14.33-32-32-32H237.25l233.38-233.38c12.5-12.5 12.5-32.75 0-45.25s-32.75-12.49-45.25 0.01z"
fill="context-fill" p-id="4981"></path>
</svg>

After

Width:  |  Height:  |  Size: 676 B

View File

@ -0,0 +1,12 @@
<svg t="1710401595942" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4073"
width="20" height="20">
<path
d="M512 521.216c-24.064 0-45.568-4.608-65.024-9.216l-2.048-0.512-296.96-124.928c-31.232-13.824-50.176-39.424-50.176-66.56s19.456-52.736 50.176-66.56l1.024-0.512L445.44 131.072c39.424-15.872 96.768-15.872 136.192 0L877.056 250.88c31.232 13.824 50.176 39.424 50.176 66.56 0 28.672-19.456 54.272-51.2 66.56l-295.424 123.904c-21.504 8.704-46.592 13.312-68.608 13.312z m-48.128-68.096c14.336 3.584 30.72 7.168 48.128 7.168 14.336 0 31.744-3.072 46.08-8.704L852.48 327.68c8.704-3.584 12.288-7.68 12.8-9.728-1.024-2.048-5.12-6.656-13.312-10.752L558.08 188.416c-24.576-9.728-65.024-9.728-90.112 0L172.032 309.76c-7.68 3.584-11.776 8.192-13.312 10.24 1.024 2.048 5.632 6.656 13.312 10.24l291.84 122.88z"
p-id="4074" fill="context-fill"></path>
<path
d="M512 712.192c-26.624 0-50.176-4.608-69.632-9.216l-2.048-0.512-2.048-1.024-321.024-123.904c-15.872-6.144-23.552-24.064-17.408-39.936 6.144-15.872 24.064-23.552 39.936-17.408l318.976 122.88c15.872 3.584 34.304 7.168 53.76 7.168 16.384 0 35.84-3.072 52.224-8.704l320.512-123.392c15.872-6.144 33.792 1.536 39.936 17.408s-1.536 33.792-17.408 39.936l-322.048 123.904c-23.552 8.192-50.176 12.8-73.728 12.8z"
p-id="4075" fill="context-fill"></path>
<path
d="M512 889.856c-26.624 0-50.176-4.608-69.632-9.216l-2.048-0.512-2.048-1.024-321.024-123.904c-15.872-6.144-23.552-24.064-17.408-39.936 6.144-15.872 24.064-23.552 39.936-17.408l318.976 122.88c15.872 3.584 34.304 7.168 53.76 7.168 16.384 0 35.84-3.072 52.224-8.704l320.512-123.392c15.872-6.144 33.792 1.536 39.936 17.408s-1.536 33.792-17.408 39.936l-322.048 123.904c-23.552 8.704-50.176 12.8-73.728 12.8z"
p-id="4076" fill="context-fill"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,6 @@
<svg t="1712475235724" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6084"
width="16" height="16">
<path
d="M896.244622 765.400781c-32.538127 0-62.261685 12.153822-84.820885 32.154322l-212.542629-190.40988a222.180398 222.180398 0 0 0 39.74513-127.33794c0-35.267407-8.145193-69.340754-23.497389-100.045146l139.363827-114.416508a156.507113 156.507113 0 1 0-23.75326-32.154322l-136.890417 112.369548a226.658122 226.658122 0 0 0-20.725465-23.881194 222.308333 222.308333 0 0 0-158.212913-65.54535 222.095108 222.095108 0 0 0-133.819978 44.43608L218.658377 219.71549a127.807035 127.807035 0 1 0-32.580772 23.113584l65.246835 84.43708a222.180398 222.180398 0 0 0-60.044146 152.498484c0 58.850086 22.601845 114.331218 63.75426 156.379179L200.278387 710.729904a127.807035 127.807035 0 1 0 29.510333 27.292794l55.609067-75.780148a222.137753 222.137753 0 0 0 129.512834 41.237706c59.702986 0 115.909083-23.284165 158.170268-65.502705l0.127935-0.17058 212.798499 190.623105a127.807035 127.807035 0 1 0 110.194654-63.029295z m-28.742723-701.595374c51.429858 0 93.264593 41.79209 93.264593 93.221948s-41.79209 93.264593-93.264593 93.264593A93.307238 93.307238 0 0 1 774.279951 157.027355c0-51.429858 41.79209-93.221948 93.221948-93.221948zM65.520218 128.327276A63.967485 63.967485 0 0 1 129.359768 64.487726a63.967485 63.967485 0 0 1 63.92484 63.882195 63.967485 63.967485 0 0 1-63.92484 63.92484 63.967485 63.967485 0 0 1-63.83955-63.967485z m63.882195 752.641428A63.967485 63.967485 0 0 1 65.520218 817.043864a63.967485 63.967485 0 0 1 63.882195-63.882195 63.967485 63.967485 0 0 1 63.92484 63.882195 63.967485 63.967485 0 0 1-63.92484 63.92484z m285.593498-241.370643a159.961357 159.961357 0 0 1-159.790777-159.790778 159.961357 159.961357 0 0 1 159.790777-159.748132 159.961357 159.961357 0 0 1 159.748132 159.748132 159.961357 159.961357 0 0 1-159.748132 159.748133zM896.244622 957.047366a63.967485 63.967485 0 0 1-63.92484-63.882195 63.967485 63.967485 0 0 1 63.92484-63.92484 63.967485 63.967485 0 0 1 63.882195 63.92484 63.967485 63.967485 0 0 1-63.882195 63.882195z"
fill="#e8af59" p-id="6085"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,6 @@
<svg t="1712475235724" class="icon" viewBox="0 0 1025 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6084"
width="20" height="20">
<path
d="M896.244622 765.400781c-32.538127 0-62.261685 12.153822-84.820885 32.154322l-212.542629-190.40988a222.180398 222.180398 0 0 0 39.74513-127.33794c0-35.267407-8.145193-69.340754-23.497389-100.045146l139.363827-114.416508a156.507113 156.507113 0 1 0-23.75326-32.154322l-136.890417 112.369548a226.658122 226.658122 0 0 0-20.725465-23.881194 222.308333 222.308333 0 0 0-158.212913-65.54535 222.095108 222.095108 0 0 0-133.819978 44.43608L218.658377 219.71549a127.807035 127.807035 0 1 0-32.580772 23.113584l65.246835 84.43708a222.180398 222.180398 0 0 0-60.044146 152.498484c0 58.850086 22.601845 114.331218 63.75426 156.379179L200.278387 710.729904a127.807035 127.807035 0 1 0 29.510333 27.292794l55.609067-75.780148a222.137753 222.137753 0 0 0 129.512834 41.237706c59.702986 0 115.909083-23.284165 158.170268-65.502705l0.127935-0.17058 212.798499 190.623105a127.807035 127.807035 0 1 0 110.194654-63.029295z m-28.742723-701.595374c51.429858 0 93.264593 41.79209 93.264593 93.221948s-41.79209 93.264593-93.264593 93.264593A93.307238 93.307238 0 0 1 774.279951 157.027355c0-51.429858 41.79209-93.221948 93.221948-93.221948zM65.520218 128.327276A63.967485 63.967485 0 0 1 129.359768 64.487726a63.967485 63.967485 0 0 1 63.92484 63.882195 63.967485 63.967485 0 0 1-63.92484 63.92484 63.967485 63.967485 0 0 1-63.83955-63.967485z m63.882195 752.641428A63.967485 63.967485 0 0 1 65.520218 817.043864a63.967485 63.967485 0 0 1 63.882195-63.882195 63.967485 63.967485 0 0 1 63.92484 63.882195 63.967485 63.967485 0 0 1-63.92484 63.92484z m285.593498-241.370643a159.961357 159.961357 0 0 1-159.790777-159.790778 159.961357 159.961357 0 0 1 159.790777-159.748132 159.961357 159.961357 0 0 1 159.748132 159.748132 159.961357 159.961357 0 0 1-159.748132 159.748133zM896.244622 957.047366a63.967485 63.967485 0 0 1-63.92484-63.882195 63.967485 63.967485 0 0 1 63.92484-63.92484 63.967485 63.967485 0 0 1 63.882195 63.92484 63.967485 63.967485 0 0 1-63.882195 63.882195z"
fill="#e8af59" p-id="6085"></path>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,9 @@
<svg t="1710402054079" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
p-id="16029" width="20" height="20">
<path
d="M246.153846 194.166154h59.076923v111.064615a118.153846 118.153846 0 0 0 118.153846 118.153846h177.23077a118.153846 118.153846 0 0 0 118.153846-118.153846v-111.064615h59.076923a51.987692 51.987692 0 0 1 51.987692 51.987692v262.301539a33.083077 33.083077 0 1 0 66.166154 0v-262.301539a118.153846 118.153846 0 0 0-118.153846-118.153846h-531.692308a118.153846 118.153846 0 0 0-118.153846 118.153846v531.692308a118.153846 118.153846 0 0 0 118.153846 118.153846h203.224616a33.083077 33.083077 0 1 0 0-66.166154h-203.224616a51.987692 51.987692 0 0 1-51.987692-51.987692v-531.692308a51.987692 51.987692 0 0 1 51.987692-51.987692z m125.243077 111.064615v-111.064615h281.206154v111.064615a51.987692 51.987692 0 0 1-51.987692 51.987693h-177.23077a51.987692 51.987692 0 0 1-51.987692-51.987693z"
p-id="16030" fill="context-fill"></path>
<path
d="M478.916923 692.460308h290.500923l-45.449846-45.449846a33.083077 33.083077 0 1 1 46.788923-46.710154l97.437539 97.437538a39.384615 39.384615 0 0 1 0 55.689846l-97.437539 97.516308a33.083077 33.083077 0 0 1-46.788923-46.788923l45.449846-45.528615H478.916923a33.083077 33.083077 0 1 1 0-66.166154z"
p-id="16031" fill="context-fill"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

File diff suppressed because one or more lines are too long

View File

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

View File

@ -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 }, "*");
}
});

View File

@ -6,35 +6,43 @@
onload="Zotero.__addonInstance__.hooks.onPrefsEvent('load', {window})"
>
<groupbox>
<label><html:h2 data-l10n-id="workspace-title"></html:h2></label>
<hbox>
<label><html:h2 data-l10n-id="basic-title"></html:h2></label>
<checkbox
data-l10n-id="basic-openNote-takeover"
native="true"
preference="__prefsPrefix__.openNote.takeover"
/>
<checkbox
data-l10n-id="basic-related-takeover"
native="true"
preference="__prefsPrefix__.related.takeover"
/>
</groupbox>
<groupbox>
<label><html:h2 data-l10n-id="editor-title"></html:h2></label>
<hbox align="center">
<html:label
for="__addonRef__-workspace-expandLevel"
data-l10n-id="workspace-expandLevel-label"
for="__addonRef__-editor-expandLevel"
data-l10n-id="editor-expandLevel-label"
></html:label>
<html:input
type="number"
min="1"
max="6"
step="1"
id="__addonRef__-workspace-expandLevel"
id="__addonRef__-editor-expandLevel"
preference="__prefsPrefix__.workspace.outline.expandLevel"
></html:input>
</hbox>
<checkbox
data-l10n-id="workspace-keepLinks"
data-l10n-id="editor-keepLinks"
native="true"
preference="__prefsPrefix__.workspace.outline.keepLinks"
/>
<checkbox
data-l10n-id="workspace-autoUpdateRelatedNotes"
native="true"
preference="__prefsPrefix__.workspace.autoUpdateRelatedNotes"
/>
</groupbox>
<groupbox>
<label><html:h2 data-l10n-id="sync-title"></html:h2></label>
<hbox>
<hbox align="center">
<html:label
for="__addonRef__-sync-period"
data-l10n-id="sync-period-label"
@ -49,7 +57,7 @@
preference="__prefsPrefix__.syncPeriodSeconds"
></html:input>
</hbox>
<hbox>
<hbox align="center">
<html:label
for="__addonRef__-sync-attachmentFolder"
data-l10n-id="sync-attachmentFolder-label"
@ -59,7 +67,7 @@
preference="__prefsPrefix__.syncAttachmentFolder"
></html:input>
</hbox>
<hbox>
<hbox align="center">
<button
onclick="Zotero.__addonInstance__.hooks.onShowSyncManager()"
data-l10n-id="sync-manager"
@ -68,7 +76,7 @@
</groupbox>
<groupbox>
<label><html:h2 data-l10n-id="template-title"></html:h2></label>
<hbox>
<hbox align="center">
<button
onclick="Zotero.__addonInstance__.hooks.onShowTemplateEditor()"
data-l10n-id="template-editor"

View File

@ -0,0 +1,210 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>Force-Directed Tree</title>
<script src="chrome://__addonRef__/content/lib/js/d3.v7.min.js"></script>
<link rel="stylesheet" href="chrome://zotero-platform/content/zotero.css" />
<style>
html,
body {
overflow: hidden;
background: var(--material-background);
}
.tooltip {
position: absolute;
left: 0;
top: 0;
margin: 10px;
}
.node:hover {
cursor: pointer;
}
@media (prefers-color-scheme: light) {
:root {
--text-color: black;
}
}
@media (prefers-color-scheme: dark) {
:root {
--text-color: white;
}
}
</style>
<script>
window.addEventListener("message", (ev) => {
if (ev.data.type === "render") {
render(ev.data.graph);
}
});
function render(data) {
// Define the data for the nodes and links
// Specify the dimensions of the chart.
var width = window.innerWidth;
var height = window.innerHeight;
// 2: 100; 22: 50
function distance() {
if (data.nodes.length > 22) {
return 50;
}
return 105 - 2.5 * data.nodes.length;
}
// Specify the color scale.
const color = d3.scaleOrdinal([1, 2], ["grey", "#e8af59"]);
// The force simulation mutates links and nodes, so create a copy
// so that re-evaluating this cell produces the same result.
const links = data.links.map((d) => ({ ...d }));
const nodes = data.nodes.map((d) => ({ ...d }));
// Create a simulation with several forces.
const simulation = d3
.forceSimulation(nodes)
.force(
"link",
d3
.forceLink(links)
.id((d) => d.id)
.distance(distance()),
)
.force("charge", d3.forceManyBody().strength(-400))
.force("x", d3.forceX())
.force("y", d3.forceY());
// Create the SVG container.
const svg = d3
.create("svg")
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height])
.attr("style", "max-width: 100%; height: auto;");
// Add a line for each link, and a circle for each node.
const link = svg
.append("g")
.attr("stroke", "#999")
.attr("stroke-opacity", 0.6)
.selectAll("line")
.data(links)
.join("line")
.attr("stroke-width", (d) => Math.sqrt(d.value));
const node = svg
.append("g")
.attr("stroke", "#fff")
.attr("stroke-width", 1.5)
.selectAll("circle")
.data(nodes)
.join("g") // Append a 'g' element for each node
.attr("class", "node"); // Assign a class for styling if needed
node
.append("circle")
.attr("r", 7)
.attr("fill", (d) => color(d.group));
node
.append("text")
.attr("x", 0) // Center the text horizontally
.attr("y", 18) // Position the text below the circle
.attr("text-anchor", "middle") // Ensure the text is centered
.attr("fill", "var(--text-color)")
.attr("stroke", "none")
.text((d) => d.title);
node.append("title").text((d) => d.title);
// Add a drag behavior.
node.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended),
);
// Set the position attributes of links and nodes each time the simulation ticks.
simulation.on("tick", () => {
link
.attr("x1", (d) => d.source.x)
.attr("y1", (d) => d.source.y)
.attr("x2", (d) => d.target.x)
.attr("y2", (d) => d.target.y);
node.attr("transform", (d) => `translate(${d.x},${d.y})`);
});
// Reheat the simulation when drag starts, and fix the subject position.
function dragstarted(event) {
if (!event.active) simulation.alphaTarget(0.3).restart();
event.subject.fx = event.subject.x;
event.subject.fy = event.subject.y;
}
// Update the subject (dragged node) position during drag.
function dragged(event) {
event.subject.fx = event.x;
event.subject.fy = event.y;
}
// Restore the target alpha so the simulation cools after dragging ends.
// Unfix the subject position now that its no longer being dragged.
function dragended(event) {
if (!event.active) simulation.alphaTarget(0);
event.subject.fx = null;
event.subject.fy = null;
}
// Add the hover interaction
node
.on("mouseover", function (event, d) {
// Enlarge the node circle
d3.select(this)
.select("circle")
.transition()
.duration(200)
.attr("r", 10); // New, larger radius
d3.select(".tooltip").style("display", "").html(d.title);
})
.on("mouseout", function (event, d) {
// Shrink the node circle back to original size
d3.select(this)
.select("circle")
.transition()
.duration(500)
.attr("r", 7); // Original radius
d3.select(".tooltip").style("display", "none");
})
.on("click", function (event, d) {
window.postMessage(
{ type: "openNote", isShift: !!event.shiftKey, id: d.id },
"*",
);
});
document.body.replaceChildren(
svg.node(),
d3
.create("div")
.attr("class", "tooltip")
.style("display", "none")
.node(),
);
}
d3.select(window).on("resize", function () {
width = window.innerWidth;
height = window.innerHeight;
d3.select(document.querySelector("svg"))
.attr("width", width)
.attr("height", height)
.attr("viewBox", [-width / 2, -height / 2, width, height]);
});
</script>
</head>
<body></body>
</html>

View File

@ -0,0 +1,3 @@
bn-context {
min-width: 360px;
}

View File

@ -0,0 +1,3 @@
bn-details pane-header {
display: none;
}

View File

@ -0,0 +1,45 @@
.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;
}

View File

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

View File

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

View File

@ -0,0 +1,44 @@
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;
padding: 0px 4px 0px 4px;
fill: currentColor;
-moz-context-properties: fill, fill-opacity;
}
bn-outline .zotero-tb-button[type=menu] {
width: 40px;
}
#__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");
}

View File

@ -0,0 +1,61 @@
bn-related-box {
display: flex;
flex-direction: column;
gap: 2px;
}
bn-related-box[hidden] {
display: none;
}
bn-related-box[readonly] .add {
display: none;
}
bn-related-box .body {
display: flex;
flex-direction: column;
padding-inline-start: 12px;
}
bn-related-box .body .row {
display: flex;
gap: 4px;
align-items: flex-start;
}
[zoteroUIDensity="comfortable"] bn-related-box .body .row {
padding-block: 2px;
}
bn-related-box .body .row .box {
display: flex;
align-items: flex-start;
gap: 4px;
padding-inline-start: 4px;
overflow: hidden;
border-radius: 5px;
flex: 1;
}
bn-related-box .body .row .box:not([disabled]):hover {
background-color: var(--fill-quinary);
}
bn-related-box .body .row .box:not([disabled]):active {
background-color: var(--fill-quarternary);
}
bn-related-box .body .row .box .icon {
height: calc(1.3333333333 * var(--zotero-font-size));
}
bn-related-box .body .row .box .label {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 10;
width: 0;
flex: 1;
overflow: hidden;
}
bn-related-box .body .row .box .icon,
bn-related-box .body .row .box .label {
padding-block: 2px;
}
bn-related-box .body .row toolbarbutton {
margin-inline-start: auto;
visibility: hidden;
}
bn-related-box .body .row:is(:hover, :focus-within) toolbarbutton {
visibility: visible;
}

View File

@ -0,0 +1,5 @@
#bn-relation-graph {
width: 100%;
height: 250px;
border-radius: 8px;
}

View File

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

View File

@ -0,0 +1,17 @@
bn-workspace,
.container {
width: 100%;
height: 100%;
background: var(--material-sidepane);
flex-grow: 1;
}
bn-workspace #__addonRef__-editor-main #links-container,
.bn-note-preview #links-container {
display: none;
}
.bn-note-preview iframe {
height: var(--details-height, 450px);
border-radius: 8px;
}

View File

@ -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 = `<svg t="1652008007954" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10521" width="18" height="18"><defs><style>path{fill:currentColor;}</style></defs><path d="M574.3 896H159.7c-17.6 0-31.9-14.3-31.9-32V160c0-17.7 14.3-32 31.9-32h382.7v160c0 35.3 28.6 64 63.8 64h159.5v192c0 17.7 14.3 32 31.9 32 17.6 0 31.9-14.3 31.9-32V270.2c0-8.5-3.3-16.6-9.3-22.6L647.4 73.4c-6-6-14.1-9.4-22.6-9.4h-497C92.6 64 64 92.7 64 128v768c0 35.3 28.6 64 63.8 64h446.5c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM638.1 288c-17.6 0-31.9-14.3-31.9-32V128l159.5 160H638.1z" p-id="10522"></path><path d="M418.8 673H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32h193.3c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM608.2 481H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32h382.7c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32zM225.5 353h191.4c17.6 0 31.9-14.3 31.9-32s-14.3-32-31.9-32H225.5c-17.6 0-31.9 14.3-31.9 32s14.3 32 31.9 32zM862.7 959.4c-23.6 0-47-8.8-64.8-26.6l-24.4-24.4c-12.5-12.5-12.5-32.8 0-45.3s32.7-12.5 45.1 0l24.4 24.4c11.3 11.4 30.7 10.4 43.2-2.1 12.5-12.5 13.4-31.9 2.1-43.3L749.2 702.6c-11.3-11.4-30.7-10.4-43.2 2.1-6.2 6.3-9.8 14.4-10 22.8-0.2 7.9 2.6 15.1 7.9 20.4 12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.1 0c-36.2-36.3-35.2-96.3 2.1-133.8 37.4-37.5 97.2-38.4 133.4-2.1l139.1 139.5c36.2 36.3 35.2 96.3-2.1 133.8-19 19.2-43.9 28.8-68.6 28.8z" p-id="10523"></path><path d="M696.3 883.1c-23.6 0-47-8.8-64.8-26.6l-139-139.6c-17.7-17.8-27.2-41.7-26.6-67.2 0.6-25 10.8-48.6 28.7-66.6 17.9-17.9 41.5-28.2 66.4-28.8 25.5-0.6 49.3 8.9 67 26.6l24.4 24.4c12.5 12.5 12.5 32.8 0 45.3s-32.7 12.5-45.1 0l-24.4-24.4c-5.3-5.3-12.5-8.1-20.4-7.9-8.4 0.2-16.5 3.8-22.8 10-6.2 6.3-9.8 14.4-10 22.8-0.2 7.9 2.6 15.1 7.9 20.4L676.7 811c11.3 11.4 30.7 10.4 43.2-2.1 12.5-12.5 13.4-31.9 2.1-43.3-12.5-12.5-12.5-32.8 0-45.3s32.7-12.5 45.1 0c36.2 36.3 35.3 96.3-2.1 133.8-19.1 19.3-44 29-68.7 29z" p-id="10524"></path></svg>`;
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 }, "*");
}
});
</script>

View File

@ -4,7 +4,7 @@
<!-- prettier-ignore -->
<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
<!-- prettier-ignore -->
<?xml-stylesheet href="chrome://zotero-platform/content/zotero-react-client.css" type="text/css"?>
<?xml-stylesheet href="chrome://zotero-platform/content/zotero.css" type="text/css"?>
<!-- prettier-ignore -->
<!DOCTYPE html>
<html
@ -12,7 +12,7 @@
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
windowtype="__addonRef__-syncManager"
windowtype="__addonRef__-workspaceWindow"
sizemode="normal"
scrolling="false"
persist="screenX screenY width height sizemode"
@ -20,41 +20,16 @@
<head>
<title data-l10n-id="title"></title>
<meta charset="utf-8" />
<script>
document.addEventListener("DOMContentLoaded", (ev) => {
const { Services } = ChromeUtils.import(
"resource://gre/modules/Services.jsm",
);
Services.scriptloader.loadSubScript(
"chrome://zotero/content/include.js",
this,
);
Services.scriptloader.loadSubScript(
"resource://zotero/require.js",
this,
);
if (Zotero.platformMajorVersion >= 102) {
Services.scriptloader.loadSubScript(
"chrome://global/content/customElements.js",
this,
);
if (!customElements.get("note-editor")) {
Services.scriptloader.loadSubScript(
"chrome://zotero/content/elements/shadowAutocompleteInput.js",
this,
);
Services.scriptloader.loadSubScript(
"chrome://zotero/content/elements/noteEditor.js",
this,
);
}
}
window.arguments[0]._initPromise.resolve();
});
</script>
<script src="chrome://zotero/content/include.js"></script>
<script src="chrome://zotero/content/customElements.js"></script>
<script src="chrome://__addonRef__/content/scripts/customElements.js"></script>
<script src="chrome://__addonRef__/content/scripts/workspaceWindow.js"></script>
<xul:linkset>
<link rel="localization" href="browser/menubar.ftl" />
<link rel="localization" href="browser/browserSets.ftl" />
<link rel="localization" href="toolkit/global/textActions.ftl" />
<link rel="localization" href="zotero.ftl" />
</xul:linkset>
<style>
html,
body {
@ -76,6 +51,18 @@
<link rel="localization" href="__addonRef__-workspaceWindow.ftl" />
</head>
<body class="zotero-window">
<xul:commandset id="mainCommandSet">
<xul:command id="cmd_close" oncommand="window.close();" />
</xul:commandset>
<xul:keyset id="mainKeyset">
<xul:key
id="key_close"
data-l10n-id="close-shortcut"
command="cmd_close"
modifiers="accel"
reserved="true"
/>
</xul:keyset>
<div id="workspace-container"></div>
</body>
</html>

View File

@ -1,13 +1,7 @@
pref-title = BNotes
pref-title = Better Notes
menuItem-setMainNote = As Workspace Note
menuItem-exportNote = Export Note
menuFile-openMainNote = Choose and Open Workspace Note
menuFile-openMainNote-error = Choosen item is not a valid note item
menuFile-openRecent = Open Recent Workspace Note
menuFile-openRecent-empty = No Recent Workspace Note
menuEdit-insertTemplate = Insert Template to Workspace Note
menuEdit-exportTemplate = Export Template to File...
menuEdit-templateEditor = Template Editor
@ -15,11 +9,6 @@ menuEdit-importTemplate = New Template from Clipboard
menuTools-syncManager = Sync Manager
menuAddNote-newMainNote = New Workspace Note
menuAddNote-newMainNote-confirmHead = Will create a new note in collection
menuAddNote-newMainNote-confirmTail = and set it the workspace note. Continue?
menuAddNote-newMainNote-enterNoteTitle = Enter new note title:
menuAddNote-newMainNote-openWorkspaceTab = Open note workspace now?
menuAddNote-newTemplateStandaloneNote = New Standalone Note from Template
menuAddNote-newTemplateItemNote = New Item Note from Template
menuAddNote-importMD = Import MarkDown File as Note
@ -34,9 +23,6 @@ editor-resizeImage-title = Resize Image
editor-resizeImage-prompt = Resize image width to:
editor-previewImage-title = Preview Image
tab-name = Note Workspace
tab-openInWindow = Drag and drop here to open workspace in new window
syncManager-noteName = Note Name
syncManager-lastSync = Last Sync
syncManager-filePath = MarkDown Path
@ -63,7 +49,6 @@ sync-running-hint-finish = Finish
sync-running-hint-synced = Synced
sync-running-hint-upToDate = Up To Date
workspace-notesPane-hint = PDF NotePane is not accesible if no PDF files are opened.
workspace-switchOutline = Swith Outline Mode
workspace-saveOutlineImage = Save Image
workspace-saveOutlineFreeMind = Save MindMap
@ -72,21 +57,16 @@ workspace-emptyWorkspaceGuideOpen = Choose a note to open
workspace-emptyWorkspaceGuideOr = or
workspace-emptyWorkspaceGuideCreate = Create a new note
editor-toolbar-main = Workspace Note
editor-toolbar-settings-title = Workspace Settings
editor-toolbar-settings-openWorkspace = Open Note Workspace
editor-toolbar-settings-setWorkspace = Set as Workspace Note
editor-toolbar-settings-previewInWorkspace = Preview in Workspace
editor-toolbar-settings-openAsTab = Open as tab
editor-toolbar-settings-openAsWindow = Open as window
editor-toolbar-settings-showInLibrary = Show in Library
editor-toolbar-settings-insertTemplate = Insert Template to Cursor Line
editor-toolbar-settings-copyLink = Copy Note Link at Line ({ $line })
editor-toolbar-settings-copyLinkAtSection = Copy Note Link at Section ({ $section })
editor-toolbar-settings-insertTemplate = Insert template
editor-toolbar-settings-copyLink = Copy link (L{ $line })
editor-toolbar-settings-copyLinkAtSection = Copy link (Sec. { $section })
editor-toolbar-settings-openParent = Open Attachment
editor-toolbar-settings-export = Export Current Note...
editor-toolbar-settings-refreshSyncing = Sync Now
editor-toolbar-settings-updateRelatedNotes = Update Related Notes
editor-toolbar-link-title = Link current note to workspace
editor-toolbar-link-popup-nodata = Workspace note is invalid
templatePicker-itemData-info = are selected in library. Please choose the data source:
templatePicker-itemData-useLibrary = Use selected items in library
@ -95,5 +75,4 @@ templatePicker-itemData-title = Choose Item Template Data Source
alert-notValidCollectionError = Please select a valid collection.
alert-notValidParentItemError = No valid parent item.
alert-notValidWorkspaceNote = Workspace note is not set. Create one?
alert-syncImportedNotes = Keep imported notes in sync with MarkDown files?

View File

@ -0,0 +1,10 @@
note-preview-header =
.label = Note Preview
note-preview-sidenav =
.tooltiptext = {$title}
note-preview-open =
.tooltiptext = Open note
note-preview-close =
.tooltiptext = Close
note-preview-full =
.tooltiptext = Toggle full height

View File

@ -0,0 +1,6 @@
note-relation-header =
.label = Relation Graph
note-relation-sidenav =
.tooltiptext = Relation Graph
note-relation-refresh =
.tooltiptext = Refresh

View File

@ -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...

View File

@ -1,9 +1,14 @@
workspace-title = Workspace
workspace-expandLevel-label = Outline expand to heading level
workspace-keepLinks =
basic-title = Basic
basic-openNote-takeover =
.label = Take over opening note
basic-related-takeover =
.label = Take over note relation
editor-title = Note Editor
editor-expandLevel-label = Outline expand to heading level
editor-keepLinks =
.label = Show note links in outline
workspace-autoUpdateRelatedNotes =
.label = Automatically update related
sync-title = Sync
sync-period-label = Auto-sync period (seconds)
sync-attachmentFolder-label = Attachment folder

View File

@ -1,13 +1,7 @@
pref-title = BNotes
pref-title = Better Notes
menuItem-setMainNote = Come nota di lavoro
menuItem-exportNote = Esporta nota
menuFile-openMainNote = Scegli e apri nota di lavoro
menuFile-openMainNote-error = L'elemento selezionato non è un elemento nota valido
menuFile-openRecent = Apri nota di lavoro recente
menuFile-openRecent-empty = Nessuna nota di lavoro recente
menuEdit-insertTemplate = Inserisci template nella nota di lavoro
menuEdit-exportTemplate = Esporta il template su file...
menuEdit-templateEditor = Editor dei template
@ -15,11 +9,6 @@ menuEdit-importTemplate = Nuovo template dagli appunti
menuTools-syncManager = Manager di sincronizzazione
menuAddNote-newMainNote = Nuova nota di lavoro
menuAddNote-newMainNote-confirmHead = Verrà creata una nuova nota nella collezione
menuAddNote-newMainNote-confirmTail = e impostata come nota di lavoro. Continuare?
menuAddNote-newMainNote-enterNoteTitle = Inserire il titolo della nuova nota:
menuAddNote-newMainNote-openWorkspaceTab = Aprire la nota di lavoro?
menuAddNote-newTemplateStandaloneNote = Nuova nota indipendente da template
menuAddNote-newTemplateItemNote = Nuova nota dell'elemento da template
menuAddNote-importMD = Importa file MarkDown come nota
@ -30,9 +19,6 @@ menuEditor-resizeImage = Ridimensiona immagine
templateEditor-templateName = Nome template
tab-name = Spazio di lavoro della nota
tab-openInWindow = Trascina qui per aprire lo spazio di lavoro in una nuova finestra
syncManager-noteName = Nome nota
syncManager-lastSync = Ultima sincronizzazione
syncManager-filePath = Percorso MarkDown
@ -59,7 +45,6 @@ sync-running-hint-finish = Terminato
sync-running-hint-synced = Sincronizzato
sync-running-hint-upToDate = Aggiornato
workspace-notesPane-hint = Il pannello delle note dei PDF non è accesibile se non è aperto alcun file PDF.
workspace-switchOutline = Cambia modalità dello schema
workspace-saveOutlineImage = Salva immagine
workspace-saveOutlineFreeMind = Salva mappa mentale
@ -68,21 +53,16 @@ workspace-emptyWorkspaceGuideOpen = Scegli una nota da aprire
workspace-emptyWorkspaceGuideOr = o
workspace-emptyWorkspaceGuideCreate = Crea una nuova nota
editor-toolbar-main = Nota di lavoro
editor-toolbar-settings-title = Impostazioni dello spazio di lavoro
editor-toolbar-settings-openWorkspace = Apri nota di lavoro
editor-toolbar-settings-setWorkspace = Imposta come nota di lavoro
editor-toolbar-settings-previewInWorkspace = Anteprima nello spazio di lavoro
editor-toolbar-settings-openAsTab = Open as tab
editor-toolbar-settings-openAsWindow = Open as window
editor-toolbar-settings-showInLibrary = Show in Library
editor-toolbar-settings-insertTemplate = Inserisci template nella posizione del cursore
editor-toolbar-settings-copyLink = Copia link della nota alla riga ({ $line })
editor-toolbar-settings-copyLinkAtSection = Copia link della nota alla sezione ({ $section })
editor-toolbar-settings-insertTemplate = Inserisci template
editor-toolbar-settings-copyLink = Copia link (L{ $line })
editor-toolbar-settings-copyLinkAtSection = Copia link (Sec. { $section })
editor-toolbar-settings-openParent = Apri allegato
editor-toolbar-settings-export = Esporta nota corrente...
editor-toolbar-settings-refreshSyncing = Sincronizza ora
editor-toolbar-settings-updateRelatedNotes = Aggiorna note correlate
editor-toolbar-link-title = Collega la nota corrente alla nota di lavoro
editor-toolbar-link-popup-nodata = La nota di lavoro non è valida
templatePicker-itemData-info = sono in uso nella biblioteca. Si prega di selezionare la sorgente dei dati:
templatePicker-itemData-useLibrary = Usa gli elementi selezionati nella biblioteca
@ -91,5 +71,4 @@ templatePicker-itemData-title = Scegli la sorgente dei dati del template dell'el
alert-notValidCollectionError = Si prega di scegliere una collezione valida.
alert-notValidParentItemError = Nessun elemento genitore valido.
alert-notValidWorkspaceNote = La nota di lavoro non è impostata. Si desidera crearne una?
alert-syncImportedNotes = Si desidera sincronizzare le note importate con i file markdown?

View File

@ -0,0 +1,10 @@
note-preview-header =
.label = Note Preview
note-preview-sidenav =
.tooltiptext = {$title}
note-preview-open =
.tooltiptext = Open note
note-preview-close =
.tooltiptext = Close
note-preview-full =
.tooltiptext = Toggle full height

View File

@ -0,0 +1,6 @@
note-relation-header =
.label = Relation Graph
note-relation-sidenav =
.tooltiptext = Relation Graph
note-relation-refresh =
.tooltiptext = Refresh

View File

@ -1,9 +1,14 @@
workspace-title = Spazio di lavoro
workspace-expandLevel-label = Espansione dello schema al livello delle intestazioni
workspace-keepLinks =
basic-title = Basic
basic-openNote-takeover =
.label = Take over opening note
basic-related-takeover =
.label = Take over note relation
editor-title = Note Editor
editor-expandLevel-label = Espansione dello schema al livello delle intestazioni
editor-keepLinks =
.label = Mostra i collegamenti delle note nello schema
workspace-autoUpdateRelatedNotes =
.label = Aggiorna automaticamente le note correlate
sync-title = Sincronizzazione
sync-period-label = Intervallo della sincronizzazione automatica (secondi)
sync-attachmentFolder-label = Cartella degli allegati

View File

@ -1,13 +1,7 @@
pref-title=BNotes
pref-title=Better Notes
menuItem-setMainNote=Как заметка раб. пространства
menuItem-exportNote=Экспорт заметки
menuFile-openMainNote = Выбрать и Открыть Заметку раб. пространства
menuFile-openMainNote-error = Выбрана невалидная Заметка
menuFile-openRecent = Открыть недавнюю Заметку раб. пространства
menuFile-openRecent-empty = Нет недавних Заметок раб. пространства
menuEdit-insertTemplate=Вставить шаблон в Заметку раб. пространства
menuEdit-exportTemplate=Экспорт шаблона в файл...
menuEdit-templateEditor=Редактор шаблонов
@ -15,11 +9,6 @@ menuEdit-importTemplate=Новый шаблон из буфера обмена
menuTools-syncManager=Синк менеджер
menuAddNote-newMainNote=Новая Заметка раб. пространства
menuAddNote-newMainNote-confirmHead=Создать новую Заметку в коллекции
menuAddNote-newMainNote-confirmTail=и установить её как Заметку раб. пространства. Продолжить?
menuAddNote-newMainNote-enterNoteTitle=Ввести имя новой Заметки:
menuAddNote-newMainNote-openWorkspaceTab=Открыть Пространство заметок?
menuAddNote-newTemplateStandaloneNote=Новая отдельная Заметка из шаблона
menuAddNote-newTemplateItemNote=Новая элементная Заметка из шаблона
menuAddNote-importMD = Импорт файла MarkDown в качестве примечания
@ -34,9 +23,6 @@ editor-resizeImage-title = Resize Image
editor-resizeImage-prompt = Resize image width to:
editor-previewImage-title = Preview Image
tab-name=Рабочее пространство заметок
tab-openInWindow=Перетащите сюда для открытия раб. пространства в новом окне
syncManager-noteName=Имя заметки
syncManager-lastSync=Последний Синк
syncManager-filePath=MarkDown путь
@ -63,7 +49,6 @@ sync-running-hint-finish=Финиш
sync-running-hint-synced=Синхронизировано
sync-running-hint-upToDate=Обновить
workspace-notesPane-hint=PDF панель заметок недоступна если PDF файлы не открыты.
workspace-switchOutline=Переключить режим Набросок
workspace-saveOutlineImage=Сохранить изображение
workspace-saveOutlineFreeMind=Сохранить MindMap
@ -72,21 +57,16 @@ workspace-emptyWorkspaceGuideOpen = Choose a note to open
workspace-emptyWorkspaceGuideOr = or
workspace-emptyWorkspaceGuideCreate = Create a new note
editor-toolbar-main=Заметка рабочего пространства
editor-toolbar-settings-title=Настройки рабочего пространства
editor-toolbar-settings-openWorkspace=Открыть пространство заметок
editor-toolbar-settings-setWorkspace=Установить как Заметку раб. пространства
editor-toolbar-settings-previewInWorkspace=Предпросмотр в рабочем пространстве
editor-toolbar-settings-openAsTab = Open as tab
editor-toolbar-settings-openAsWindow = Open as window
editor-toolbar-settings-showInLibrary = Show in Library
editor-toolbar-settings-insertTemplate=Вставить шаблон в строку курсора
editor-toolbar-settings-copyLink = Копировать Ссылку на Заметку на строке ({ $line })
editor-toolbar-settings-copyLinkAtSection = Копировать Ссылку на Заметку в секции ({ $section })
editor-toolbar-settings-insertTemplate=Вставить шаблон
editor-toolbar-settings-copyLink = Копировать Ссылку (L{ $line })
editor-toolbar-settings-copyLinkAtSection = Копировать Ссылку (Sec. { $section })
editor-toolbar-settings-openParent=Открыть вложение
editor-toolbar-settings-export=Экспортировать текущую заметку...
editor-toolbar-settings-refreshSyncing=Синхронизировать сейчас
editor-toolbar-settings-updateRelatedNotes = Update Related Notes
editor-toolbar-link-title=Ссылка текущей заметки в рабочее пространство
editor-toolbar-link-popup-nodata=Невалидная Заметка раб. пространства
templatePicker-itemData-info=выбрано в библиотеке. Выберите источник данных:
templatePicker-itemData-useLibrary=Использовать выбранные записи в библиотеке
@ -95,5 +75,4 @@ templatePicker-itemData-title=Выбрать источник данных ша
alert-notValidCollectionError=Выберите валидную коллекцию.
alert-notValidParentItemError=Нет валидного родительского элемента.
alert-notValidWorkspaceNote=Заметка рабочего пространства не установлена. Создать?
alert-syncImportedNotes = Синхронизировать импортированные заметки с файлами MarkDown?

View File

@ -0,0 +1,10 @@
note-preview-header =
.label = Note Preview
note-preview-sidenav =
.tooltiptext = {$title}
note-preview-open =
.tooltiptext = Open note
note-preview-close =
.tooltiptext = Close
note-preview-full =
.tooltiptext = Toggle full height

View File

@ -0,0 +1,6 @@
note-relation-header =
.label = Relation Graph
note-relation-sidenav =
.tooltiptext = Relation Graph
note-relation-refresh =
.tooltiptext = Refresh

View File

@ -1,9 +1,14 @@
workspace-title = Рабочее пространство
workspace-expandLevel-label = Outline расширить до уровня заголовка
workspace-keepLinks =
basic-title = Basic
basic-openNote-takeover =
.label = Take over opening note
basic-related-takeover =
.label = Take over note relation
editor-title = Note Editor
editor-expandLevel-label = Outline расширить до уровня заголовка
editor-keepLinks =
.label = Сохранить ссылки
workspace-autoUpdateRelatedNotes =
.label = Автообновление связанных заметок
sync-title = Синк
sync-period-label = Авто-синк период (сек)
sync-attachmentFolder-label = Attachment folder

View File

@ -1,13 +1,7 @@
pref-title = BNotes
pref-title = Better Notes
menuItem-setMainNote = Çalışma Alanı Notu Olarak
menuItem-exportNote = Notu Dışa Aktar
menuFile-openMainNote = Çalışma Alanı Notunu Seç ve Aç
menuFile-openMainNote-error = Seçilen öge geçerli bir not ögesi değil
menuFile-openRecent = Önceki Çalışma Alanı Notunu Aç
menuFile-openRecent-empty = Geçmiş Çalışma Alanı Notu Yok
menuEdit-insertTemplate = Çalışma Alanı Notu İçerisine Şablon Yerleştir
menuEdit-exportTemplate = Şablonu Dosya Olarak Dışa Aktar...
menuEdit-templateEditor = Şablon Düzenleyici
@ -15,11 +9,6 @@ menuEdit-importTemplate = Panodan Yeni Şablon Al
menuTools-syncManager = Senkronizasyon Yönetimi
menuAddNote-newMainNote = Yeni Çalışma Alanı Notu
menuAddNote-newMainNote-confirmHead = Koleksiyon içerisinde yeni bir not oluşturulacak
menuAddNote-newMainNote-confirmTail = ve çalışma alanı notu olarak ayarlanacak. Devam edilsin mi?
menuAddNote-newMainNote-enterNoteTitle = Yeni bir not başlığı girin:
menuAddNote-newMainNote-openWorkspaceTab = Not Çalışma Alanıılsın mı?
menuAddNote-newTemplateStandaloneNote = Şablondan Yeni Bağımsız Not
menuAddNote-newTemplateItemNote = Şablondan Yeni Öge Notu
menuAddNote-importMD = Markdown Dosyasını Not Olarak İçe Aktar
@ -34,9 +23,6 @@ editor-resizeImage-title = Resmi Boyutlandır
editor-resizeImage-prompt = Resmi Şuna Boyutlandır:
editor-previewImage-title = Resmi Önizle
tab-name = Not Çalışma Alanı
tab-openInWindow = Çalışma alanını yeni bir pencerede açmak için buraya sürükleyip bırakın
syncManager-noteName = Not Adı
syncManager-lastSync = Son Senkronizasyon
syncManager-filePath = MarkDown Yolu
@ -63,7 +49,6 @@ sync-running-hint-finish = Bitir
sync-running-hint-synced = Senkronize Edildi
sync-running-hint-upToDate = Güncel
workspace-notesPane-hint = Hiçbir PDF Dosyasıık değilse PDF Not Paneline Erişilemez.
workspace-switchOutline = Anahat Moduna Geç
workspace-saveOutlineImage = Resmi Kaydet
workspace-saveOutlineFreeMind = Zihin Haritasını Kaydet
@ -72,21 +57,16 @@ workspace-emptyWorkspaceGuideOpen = Açmak için bir not seçin
workspace-emptyWorkspaceGuideOr = ya da
workspace-emptyWorkspaceGuideCreate = Yeni bir not oluşturun
editor-toolbar-main = Çalışma Alanı Notu
editor-toolbar-settings-title = Çalışma Alanı Ayarları
editor-toolbar-settings-openWorkspace = Not Çalışma Alanıın
editor-toolbar-settings-setWorkspace = Çalışma Alanı Notu Olarak Ayarla
editor-toolbar-settings-previewInWorkspace = Çalışma Alanında Ön İzle Preview in Workspace
editor-toolbar-settings-openAsTab = Open as tab
editor-toolbar-settings-openAsWindow = Open as window
editor-toolbar-settings-showInLibrary = Show in Library
editor-toolbar-settings-insertTemplate = İşaretçi Satırına Şablon Ekle
editor-toolbar-settings-copyLink = Satıra Not Linki Kopyala ({ $line })
editor-toolbar-settings-copyLinkAtSection = Bölüme Not Linki Kopyala ({ $section })
editor-toolbar-settings-insertTemplate = Insert template
editor-toolbar-settings-copyLink = Copy link (L{ $line })
editor-toolbar-settings-copyLinkAtSection =Copy link (Sec. { $section })
editor-toolbar-settings-openParent = Eki Aç
editor-toolbar-settings-export = Bu Notu Dışa Aktar...
editor-toolbar-settings-refreshSyncing = Senkronize Et
editor-toolbar-settings-updateRelatedNotes = Alakalı Notları Güncelle
editor-toolbar-link-title = Şuanki notu çalışma alanına ilintile
editor-toolbar-link-popup-nodata = Çalışma alanı notu geçersiz
templatePicker-itemData-info = kütphanede seçili. Lütfen veri kaynağı seçin:
templatePicker-itemData-useLibrary = Seçili ögeleri kütüphanede kullan
@ -95,5 +75,4 @@ templatePicker-itemData-title = Öge Şablonu İçin Veri Kaynağı Seç
alert-notValidCollectionError = Lütfen geçerli bir koleksiyon seçin.
alert-notValidParentItemError = Geçerli üst öge yok.
alert-notValidWorkspaceNote = Çalışma alanı notu yok. Yeni bir tane oluşturmak ister misiniz?
alert-syncImportedNotes = İçe aktarılmış notları Markdown dosyalarıyla senkron tutmak ister misiniz?

View File

@ -0,0 +1,10 @@
note-preview-header =
.label = Note Preview
note-preview-sidenav =
.tooltiptext = {$title}
note-preview-open =
.tooltiptext = Open note
note-preview-close =
.tooltiptext = Close
note-preview-full =
.tooltiptext = Toggle full height

View File

@ -0,0 +1,6 @@
note-relation-header =
.label = Relation Graph
note-relation-sidenav =
.tooltiptext = Relation Graph
note-relation-refresh =
.tooltiptext = Refresh

View File

@ -1,9 +1,14 @@
workspace-title = Çalışma Alanı
workspace-expandLevel-label = Anahat başlık seviyesine genişletildi
workspace-keepLinks =
basic-title = Basic
basic-openNote-takeover =
.label = Take over opening note
basic-related-takeover =
.label = Take over note relation
editor-title = Note Editor
editor-expandLevel-label = Anahat başlık seviyesine genişletildi
editor-keepLinks =
.label = Not linklerini anahatta göster
workspace-autoUpdateRelatedNotes =
.label = Alakalı olanları otomatik güncelle
sync-title = Senkronize Et
sync-period-label = Oto-Senkronize aralığı (saniye)
sync-attachmentFolder-label = Ek Klasörü

View File

@ -1,13 +1,7 @@
pref-title=BNotes
pref-title=Better Notes
menuItem-setMainNote=设为工作区主笔记
menuItem-exportNote=导出笔记
menuFile-openMainNote = 设置并打开工作区笔记
menuFile-openMainNote-error = 选择的条目不是有效的笔记条目
menuFile-openRecent = 打开最近使用的工作区笔记
menuFile-openRecent-empty = 无最近使用的工作区笔记
menuEdit-insertTemplate=插入模板到工作区笔记
menuEdit-exportTemplate=运行模板并导出为文件...
menuEdit-templateEditor=模板编辑器
@ -15,11 +9,6 @@ menuEdit-importTemplate=从剪贴板导入笔记模板
menuTools-syncManager=同步管理器
menuAddNote-newMainNote=新建工作区笔记
menuAddNote-newMainNote-confirmHead=将在当前分类
menuAddNote-newMainNote-confirmTail=下创建新笔记并将它设为工作区笔记。确认继续吗?
menuAddNote-newMainNote-enterNoteTitle=请输入新笔记的标题:
menuAddNote-newMainNote-openWorkspaceTab=现在打开笔记工作区吗?
menuAddNote-newTemplateStandaloneNote=从模板新建独立笔记
menuAddNote-newTemplateItemNote=从模板新建条目子笔记
menuAddNote-importMD = 导入MarkDown为笔记
@ -34,9 +23,6 @@ editor-resizeImage-title = 缩放图片
editor-resizeImage-prompt = 缩放图片宽度为:
editor-previewImage-title = 预览图片
tab-name=笔记工作区
tab-openInWindow=拖放到此处以在新窗口打开
syncManager-noteName=笔记名称
syncManager-lastSync=最近同步
syncManager-filePath=MarkDown路径
@ -63,7 +49,6 @@ sync-running-hint-upToDate=已最新
fileInterface-sync=同步到
workspace-notesPane-hint=PDF笔记侧栏在没有PDF文件打开时不可访问-
workspace-switchOutline=切换大纲模式
workspace-saveOutlineImage=保存图片
workspace-saveOutlineFreeMind=保存思维导图
@ -72,21 +57,16 @@ workspace-emptyWorkspaceGuideOpen = 打开现有笔记
workspace-emptyWorkspaceGuideOr = 或
workspace-emptyWorkspaceGuideCreate = 创建新笔记
editor-toolbar-main=工作区笔记
editor-toolbar-settings-title=工作区选项
editor-toolbar-settings-openWorkspace=打开笔记工作区
editor-toolbar-settings-setWorkspace=设为工作区笔记
editor-toolbar-settings-previewInWorkspace=在工作区预览
editor-toolbar-settings-openAsTab = 在标签页中打开
editor-toolbar-settings-openAsWindow = 在窗口中打开
editor-toolbar-settings-showInLibrary = 在文库中显示
editor-toolbar-settings-insertTemplate=插入模板到光标行
editor-toolbar-settings-copyLink=复制当前行({ $line })笔记链接
editor-toolbar-settings-copyLinkAtSection=复制当前节({ $section })笔记链接
editor-toolbar-settings-insertTemplate=插入模板
editor-toolbar-settings-copyLink=复制行(L{ $line })
editor-toolbar-settings-copyLinkAtSection=复制节(Sec. { $section })
editor-toolbar-settings-openParent=打开附件
editor-toolbar-settings-export=导出当前笔记...
editor-toolbar-settings-refreshSyncing=立即同步
editor-toolbar-settings-updateRelatedNotes = 更新关联笔记
editor-toolbar-link-title=链接当前笔记到工作区
editor-toolbar-link-popup-nodata=工作区笔记不可用
templatePicker-itemData-info=在文库中被选中。请选择模板数据源:
templatePicker-itemData-useLibrary=使用文库中选中的条目
@ -95,5 +75,4 @@ templatePicker-itemData-title=选择条目模板数据源
alert-notValidCollectionError=请选择一个有效的分类。
alert-notValidParentItemError=无效的父条目。
alert-notValidWorkspaceNote=工作区笔记未设置。创建一个吗?
alert-syncImportedNotes = 保持导入的笔记与 MarkDown 文件同步?

View File

@ -0,0 +1,10 @@
note-preview-header =
.label = 笔记预览
note-preview-sidenav =
.tooltiptext = {$title}
note-preview-open =
.tooltiptext = 打开笔记
note-preview-close =
.tooltiptext = 关闭
note-preview-full =
.tooltiptext = 切换全屏高度

View File

@ -0,0 +1,6 @@
note-relation-header =
.label = 关系图
note-relation-sidenav =
.tooltiptext = 关系图
note-relation-refresh =
.tooltiptext = 刷新

View File

@ -1,9 +1,14 @@
workspace-title = 工作区
workspace-expandLevel-label = 大纲展开至标题层级
workspace-keepLinks =
basic-title = 基本
basic-openNote-takeover =
.label = 接管打开笔记
basic-related-takeover =
.label = 接管笔记关联
editor-title = 笔记编辑器
editor-expandLevel-label = 大纲展开至标题层级
editor-keepLinks =
.label = 在大纲中显示笔记链接
workspace-autoUpdateRelatedNotes =
.label = 自动更新"相关"
sync-title = 同步
sync-period-label = 自动同步周期 (秒)
sync-attachmentFolder-label = 附件文件夹

View File

@ -13,7 +13,7 @@
"zotero": {
"id": "__addonID__",
"update_url": "__updateURL__",
"strict_min_version": "7.0.0-beta.55",
"strict_min_version": "7.0.0-beta.70",
"strict_max_version": "7.0.*"
}
}

View File

@ -1,11 +1,11 @@
pref("__prefsPrefix__.recentMainNoteIds", "");
pref("__prefsPrefix__.syncNoteIds", "");
pref("__prefsPrefix__.syncPeriodSeconds", 30);
pref("__prefsPrefix__.syncAttachmentFolder", "attachments");
pref("__prefsPrefix__.autoAnnotation", false);
pref("__prefsPrefix__.insertLinkPosition", "end");
pref("__prefsPrefix__.embedLink", true);
pref("__prefsPrefix__.standaloneLink", false);
pref("__prefsPrefix__.keepLink", true);
@ -27,4 +27,6 @@ pref("__prefsPrefix__.OCRMathpix.APIKey", "");
pref("__prefsPrefix__.workspace.outline.expandLevel", 2);
pref("__prefsPrefix__.workspace.outline.keepLinks", true);
pref("__prefsPrefix__.workspace.autoUpdateRelatedNotes", false);
pref("__prefsPrefix__.openNote.takeover", true);
pref("__prefsPrefix__.related.takeover", true);

View File

@ -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.28"
},
"devDependencies": {
"@esbuild-plugins/node-globals-polyfill": "^0.2.3",
@ -89,7 +89,7 @@
"replace-in-file": "^7.0.2",
"typescript": "^5.3.3",
"xslt3": "^2.6.0",
"zotero-types": "^1.3.11"
"zotero-types": "^1.3.24"
},
"eslintConfig": {
"env": {

View File

@ -5,19 +5,9 @@ const buildDir = "build";
export async function main() {
await build({
entryPoints: ["src/extras/editorScript.ts"],
entryPoints: ["./src/extras/*.*"],
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"],
target: ["firefox115"],
}).catch(() => exit(1));
}

View File

@ -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`,
@ -193,7 +194,7 @@ export const esbuildOptions = {
__env__: `"${env.NODE_ENV}"`,
},
bundle: true,
target: "firefox102",
target: "firefox115",
outfile: path.join(
buildDir,
`addon/chrome/content/scripts/${config.addonRef}.js`,

View File

@ -7,7 +7,7 @@
"update_link": "__updateLink__",
"applications": {
"zotero": {
"strict_min_version": "7.0.0-beta.55"
"strict_min_version": "7.0.0-beta.70"
}
}
}

View File

@ -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";
@ -47,19 +46,7 @@ class Addon {
};
notify: Array<Parameters<_ZoteroTypes.Notifier.Notify>>;
workspace: {
mainId: number;
previewId: number;
tab: {
active: boolean;
id?: string;
container?: XUL.Box;
};
window: {
active: boolean;
window?: Window;
container?: XUL.Box;
};
outline: OutlineType;
instances: Record<string, WeakRef<HTMLElement>>;
};
imageViewer: {
window?: Window;
@ -106,36 +93,7 @@ class Addon {
},
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,
instances: {},
},
imageViewer: {
window: undefined,

View File

@ -9,7 +9,7 @@ import {
html2md,
annotations2html,
note2html,
} from "./modules/convert/api";
} from "./utils/convert";
import { exportNotes } from "./modules/export/api";
import { saveDocx } from "./modules/export/docx";
import { saveFreeMind } from "./modules/export/freemind";
@ -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,
@ -63,13 +62,12 @@ import {
} from "./utils/editor";
import {
addLineToNote,
updateRelatedNotes,
getRelatedNoteIds,
getNoteTreeFlattened,
getLinesInNote,
} from "./utils/note";
import { updateRelatedNotes, getRelatedNoteIds } from "./utils/related";
const workspace = {
getWorkspaceEditor,
};
const workspace = {};
const sync = {
isSyncNote,
@ -82,10 +80,6 @@ const sync = {
getMDStatus,
getMDStatusFromContent,
getMDFileName,
/**
* @deprecated Use `api.note.getRelatedNoteIds` instead
*/
getRelatedNoteIds,
};
const convert = {
@ -146,6 +140,11 @@ const editor = {
const note = {
insert: addLineToNote,
getLinesInNote,
getNoteTreeFlattened,
};
const related = {
updateRelatedNotes,
getRelatedNoteIds,
};
@ -159,4 +158,5 @@ export default {
$import,
editor,
note,
related,
};

60
src/elements/base.ts Normal file
View File

@ -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<string, string>
>;
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`;
}
}
}
}
}

47
src/elements/context.ts Normal file
View File

@ -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(`
<linkset>
<html:link
rel="stylesheet"
href="chrome://${config.addonRef}/content/styles/context.css"
></html:link>
</linkset>
<bn-details id="container" class="container"></bn-details>
<item-pane-sidenav id="sidenav"></item-pane-sidenav>
`),
);
}
init(): void {
this._details = this._queryID("container");
this._sidenav = this._queryID("sidenav");
}
render() {
if (!this.item) return;
this._details.editable = this.item.isEditable();
this._details.item = this.item;
this._details.parentID = this.item.parentID;
this._details.sidenav = this._sidenav;
this._details.render();
this._sidenav.toggleDefaultStatus();
}
}

View File

@ -0,0 +1,36 @@
import { config } from "../../package.json";
const ItemDetails = customElements.get("item-details")! as any;
export class NoteDetails extends ItemDetails {
content = MozXULElement.parseXULToFragment(`
<linkset>
<html:link
rel="stylesheet"
href="chrome://${config.addonRef}/content/styles/details.css"
></html:link>
</linkset>
<hbox id="zotero-view-item-container" class="zotero-view-item-container" flex="1">
<html:div class="zotero-view-item-main">
<pane-header id="zotero-item-pane-header" />
<html:div id="zotero-view-item" class="zotero-view-item" tabindex="0">
<tags-box id="zotero-editpane-tags" class="zotero-editpane-tags" data-pane="tags" />
<bn-related-box id="zotero-editpane-related" class="zotero-editpane-related"
data-pane="related" />
</html:div>
</html:div>
</hbox>`);
init() {
MozXULElement.insertFTLIfNeeded(`${config.addonRef}-notePreview.ftl`);
MozXULElement.insertFTLIfNeeded(`${config.addonRef}-noteRelation.ftl`);
super.init();
}
forceUpdateSideNav() {
this._sidenav
.querySelectorAll("toolbarbutton")
.forEach((elem: HTMLElement) => (elem.parentElement!.hidden = true));
super.forceUpdateSideNav();
}
}

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

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

387
src/elements/outlinePane.ts Normal file
View File

@ -0,0 +1,387 @@
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(`
<linkset>
<html:link
rel="stylesheet"
href="chrome://${config.addonRef}/content/styles/outline.css"
></html:link>
</linkset>
<hbox id="left-toolbar">
<toolbarbutton
id="setOutline"
class="zotero-tb-button"
data-l10n-id="${config.addonRef}-setOutline"
type="menu"
wantdropmarker="true"
>
<menupopup id="setOutlinePopup">
<menuitem
id="useTreeView"
type="radio"
data-l10n-id="${config.addonRef}-useTreeView"
></menuitem>
<menuitem
id="useMindMap"
type="radio"
data-l10n-id="${config.addonRef}-useMindMap"
></menuitem>
<menuitem
id="useBubbleMap"
type="radio"
data-l10n-id="${config.addonRef}-useBubbleMap"
></menuitem>
</menupopup>
</toolbarbutton>
<toolbarbutton
id="saveOutline"
class="zotero-tb-button"
data-l10n-id="${config.addonRef}-saveOutline"
type="menu"
wantdropmarker="true"
>
<menupopup id="saveOutlinePopup">
<menuitem
id="saveImage"
data-l10n-id="${config.addonRef}-saveOutlineImage"
></menuitem>
<menuitem
id="saveSVG"
data-l10n-id="${config.addonRef}-saveOutlineSVG"
></menuitem>
<menuitem
id="saveFreeMind"
data-l10n-id="${config.addonRef}-saveOutlineFreeMind"
></menuitem>
<menuitem
id="saveMore"
data-l10n-id="${config.addonRef}-saveMore"
></menuitem>
</menupopup>
</toolbarbutton>
</hbox>
<iframe id="outline" class="container"></iframe>`),
);
}
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 (extraData.skipBN) return;
if (event === "modify" && type === "item") {
if ((ids as number[]).includes(this.item.id)) {
this.updateOutline();
if (getPref("related.takeover")) {
this._addon.api.related.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;
}
};
}

110
src/elements/related.ts Normal file
View File

@ -0,0 +1,110 @@
// @ts-nocheck
import { config } from "../../package.json";
import { getPref } from "../utils/prefs";
const RelatedBox = customElements.get("related-box")! as typeof XULElementBase;
const _require = window.require;
const { getCSSItemTypeIcon } = _require("components/icons");
export class NoteRelatedBox extends RelatedBox {
content = MozXULElement.parseXULToFragment(`
<linkset>
<html:link
rel="stylesheet"
href="chrome://${config.addonRef}/content/styles/related.css"
></html:link>
</linkset>
<collapsible-section
data-l10n-id="section-related"
data-pane="related"
extra-buttons="add"
>
<html:div class="body" />
</collapsible-section>`);
// Following code is from chrome/content/zotero/elements/relatedBox.js
render() {
if (!this.item) return;
if (this._isAlreadyRendered()) return;
const body = this.querySelector(".body");
body.replaceChildren();
if (this._item) {
const relatedKeys = this._item.relatedItems;
for (let i = 0; i < relatedKeys.length; i++) {
const key = relatedKeys[i];
const relatedItem = Zotero.Items.getByLibraryAndKey(
this._item.libraryID,
key,
);
if (!relatedItem) {
Zotero.debug(
`Related item ${this._item.libraryID}/${key} not found ` +
`for item ${this._item.libraryKey}`,
2,
);
continue;
}
const id = relatedItem.id;
const row = document.createElement("div");
row.className = "row";
const icon = getCSSItemTypeIcon(relatedItem.getItemTypeIconName());
const label = document.createElement("span");
label.className = "label";
label.append(relatedItem.getDisplayTitle());
const box = document.createElement("div");
box.addEventListener("click", () => this._handleShowItem(id));
box.setAttribute("tabindex", "0");
box.className = "box keyboard-clickable";
box.appendChild(icon);
box.appendChild(label);
row.append(box);
// Extra button to open note
if (relatedItem.isNote()) {
const note = document.createXULElement("toolbarbutton");
note.addEventListener("command", (event) => {
const position = event.shiftKey ? "window" : "tab";
Zotero[config.addonRef].hooks.onOpenNote(id, position);
});
note.className = "zotero-clicky zotero-clicky-open-link";
note.setAttribute("tabindex", "0");
row.append(note);
}
if (
this.editable &&
(!relatedItem.isNote() ||
!getPref("related.takeover"))
) {
const remove = document.createXULElement("toolbarbutton");
remove.addEventListener("command", () => this._handleRemove(id));
remove.className = "zotero-clicky zotero-clicky-minus";
remove.setAttribute("tabindex", "0");
row.append(remove);
}
body.append(row);
}
this._updateCount();
}
}
_handleShowItem(id: number) {
const item = Zotero.Items.get(id);
if (!item) return;
if (!item.isNote()) {
// @ts-ignore
return super._handleShowItem(id);
}
Zotero[config.addonRef].hooks.onOpenNote(item.id, "preview", {
workspaceUID: this.closest("bn-workspace")?.dataset.uid,
});
}
}

114
src/elements/workspace.ts Normal file
View File

@ -0,0 +1,114 @@
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(`
<linkset>
<html:link
rel="stylesheet"
href="chrome://${config.addonRef}/content/styles/workspace.css"
></html:link>
</linkset>
<hbox id="top-container" class="container">
<bn-outline id="left-container" class="container" zotero-persist="width">
</bn-outline>
<splitter
id="left-splitter"
collapse="before"
zotero-persist="state"
></splitter>
<vbox id="center-container" class="container" zotero-persist="width">
<note-editor id="editor-main" class="container"></note-editor>
</vbox>
<splitter
id="right-splitter"
collapse="after"
zotero-persist="state"
></splitter>
<bn-context id="right-container" class="container" zotero-persist="width"></bn-context>
</hbox>
`),
);
}
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`);
// For note preview section enabled decision
this.dataset.uid = Zotero.Utilities.randomString(8);
this._addon.data.workspace.instances[this.dataset.uid] = new WeakRef(this);
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() {
await waitUtilAsync(() => Boolean(this._editorElement._initialized));
if (!this._editorElement._initialized) {
throw new Error("initNoteEditor: waiting initialization failed");
}
this._editorElement.mode = "edit";
this._editorElement.viewMode = "library";
this._editorElement.parent = this.item?.parentItem;
this._editorElement.item = this.item;
await waitUtilAsync(() => Boolean(this._editorElement._editorInstance));
await this._editorElement._editorInstance._initPromise;
return;
}
scrollEditorTo(options: { lineIndex?: number; sectionName?: string } = {}) {
if (typeof options.lineIndex === "number") {
this._addon.api.editor.scroll(this.editor, options.lineIndex);
}
if (typeof options.sectionName === "string") {
this._addon.api.editor.scrollToSection(this.editor, options.sectionName);
}
}
}

View File

@ -0,0 +1,21 @@
import { ContextPane } from "../elements/context";
import { NoteDetails } from "../elements/detailsPane";
import { NotePicker } from "../elements/notePicker";
import { OutlinePane } from "../elements/outlinePane";
import { NoteRelatedBox } from "../elements/related";
import { Workspace } from "../elements/workspace";
const elements = {
"bn-context": ContextPane,
"bn-outline": OutlinePane,
"bn-details": NoteDetails as unknown as CustomElementConstructor,
"bn-workspace": Workspace,
"bn-note-picker": NotePicker,
"bn-related-box": NoteRelatedBox,
};
for (const [key, constructor] of Object.entries(elements)) {
if (!customElements.get(key)) {
customElements.define(key, constructor);
}
}

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

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

View File

@ -0,0 +1,37 @@
window.addEventListener("DOMContentLoaded", () => {
const registeredKey = Zotero.Notifier.registerObserver({
notify(action, type, ids, extraData) {
if (action === "modify" && type === "item") {
const item = getItem();
if ((ids as number[]).includes(item.id)) {
updateTitle();
}
}
},
});
window.addEventListener(
"unload",
() => {
Zotero.Notifier.unregisterObserver(registeredKey);
},
{ once: true },
);
// @ts-ignore
window.arguments[0]._initPromise.resolve();
});
function updateTitle() {
const item = getItem();
if (item?.isNote()) {
document.title = item.getNoteTitle();
}
}
function getItem() {
// @ts-ignore
return document.querySelector("bn-workspace")?.item as Zotero.Item;
}
window.updateTitle = updateTitle;

View File

@ -8,20 +8,16 @@ import {
initTemplates,
} from "./modules/template/controller";
import { registerMenus } from "./modules/menu";
import { initWorkspace } from "./modules/workspace/content";
import {
registerWorkspaceTab,
openWorkspaceTab,
onTabSelect,
restoreNoteTabs,
onUpdateNoteTabsTitle,
} from "./modules/workspace/tab";
import {
initWorkspace,
initWorkspaceEditor,
toggleNotesPane,
toggleOutlinePane,
togglePreviewPane,
updateOutline,
} from "./modules/workspace/content";
import { registerNotify } from "./modules/notify";
import { openWorkspaceWindow } from "./modules/workspace/window";
import { openNotePreview } from "./modules/workspace/preview";
import { registerNotify } from "./modules/notify";
import { registerReaderAnnotationButton } from "./modules/reader";
import { setSyncing, callSyncing } from "./modules/sync/hooks";
import {
@ -34,15 +30,14 @@ 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 { patchViewItems } from "./modules/viewItems";
import { onUpdateRelated } from "./modules/relatedNotes";
import { getFocusedWindow } from "./utils/window";
import { registerNoteRelation } from "./modules/workspace/relation";
import { getPref } from "./utils/prefs";
async function onStartup() {
@ -65,6 +60,8 @@ async function onStartup() {
registerReaderAnnotationButton();
registerNoteRelation();
initSyncList();
setSyncing();
@ -74,6 +71,11 @@ async function onStartup() {
async function onMainWindowLoad(win: Window): Promise<void> {
await waitUtilAsync(() => document.readyState === "complete");
Services.scriptloader.loadSubScript(
`chrome://${config.addonRef}/content/scripts/customElements.js`,
win,
);
// Create ztoolkit for every window
addon.data.ztoolkit = createZToolkit();
@ -81,9 +83,11 @@ async function onMainWindowLoad(win: Window): Promise<void> {
registerMenus();
registerWorkspaceTab(win);
initTemplates();
patchViewItems(win);
restoreNoteTabs();
}
async function onMainWindowUnload(win: Window): Promise<void> {
@ -102,25 +106,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());
@ -130,11 +125,9 @@ function onNotify(
skipActive: true,
reason: "item-modify",
});
addon.hooks.onUpdateRelated(modifiedNotes, { skipActive: true });
onUpdateNoteTabsTitle(modifiedNotes);
}
}
// Insert annotation when assigning tag starts with @
if (event === "add" && type === "item-tag") {
annotationTagAction(ids as number[], extraData);
} else {
return;
}
@ -158,37 +151,60 @@ async function onPrefsEvent(type: string, data: { [key: string]: any }) {
function onOpenNote(
noteId: number,
mode: "auto" | "preview" | "workspace" | "standalone" = "auto",
mode: "auto" | "preview" | "tab" | "window" | "builtin" = "auto",
options: {
workspaceUID?: string;
lineIndex?: number;
sectionName?: string;
} = {},
) {
if (!getPref("openNote.takeover")) {
ZoteroPane.openNoteWindow(noteId);
return;
}
let { workspaceUID } = options;
const noteItem = Zotero.Items.get(noteId);
if (!noteItem?.isNote()) {
ztoolkit.log(`onOpenNote: ${noteId} is not a note.`);
return;
}
if (mode === "auto") {
if (noteId === addon.data.workspace.mainId) {
mode = "workspace";
} else if (
addon.data.workspace.tab.active ||
addon.data.workspace.window.active
) {
const currentWindow = getFocusedWindow();
if ((currentWindow as any)?.Zotero_Tabs?.selectedType === "note") {
mode = "preview";
workspaceUID = (
document.querySelector(`#${Zotero_Tabs.selectedID} bn-workspace`) as
| HTMLElement
| undefined
)?.dataset.uid;
} else if (currentWindow?.document.querySelector("bn-workspace")) {
mode = "preview";
workspaceUID = (
currentWindow.document.querySelector("bn-workspace") as
| HTMLElement
| undefined
)?.dataset.uid;
} else {
mode = "standalone";
mode = "tab";
}
}
switch (mode) {
case "preview":
addon.hooks.onSetWorkspaceNote(noteId, "preview", options);
if (!workspaceUID) {
throw new Error(
"Better Notes onOpenNote mode=preview must have workspaceUID provided.",
);
}
openNotePreview(noteItem, workspaceUID, options);
break;
case "workspace":
addon.hooks.onSetWorkspaceNote(noteId, "main", options);
case "tab":
openWorkspaceTab(noteItem, options);
break;
case "standalone":
case "window":
openWorkspaceWindow(noteItem, options);
break;
case "builtin":
ZoteroPane.openNoteWindow(noteId);
break;
default:
@ -196,86 +212,8 @@ 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") {
if (type === "window") {
openWorkspaceWindow();
return;
}
if (type === "tab") {
openWorkspaceTab();
return;
}
}
const onInitWorkspace = initWorkspace;
function onToggleWorkspacePane(
type: "outline" | "preview" | "notes",
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;
}
}
const onSyncing = callSyncing;
const onShowTemplatePicker = showTemplatePicker;
@ -296,8 +234,6 @@ const onShowSyncDiff = showSyncDiff;
const onShowTemplateEditor = showTemplateEditor;
const onCreateWorkspaceNote = createWorkspaceNote;
const onCreateNoteFromTemplate = createNoteFromTemplate;
const onCreateNoteFromMD = createNoteFromMD;
@ -315,10 +251,8 @@ export default {
onPrefsEvent,
onOpenNote,
onInitWorkspace,
onSetWorkspaceNote,
onOpenWorkspace,
onToggleWorkspacePane,
onSyncing,
onUpdateRelated,
onShowTemplatePicker,
onUpdateTemplatePicker,
onImportTemplateFromClipboard,
@ -328,7 +262,7 @@ export default {
onShowSyncInfo,
onShowSyncManager,
onShowTemplateEditor,
onCreateWorkspaceNote,
onCreateNoteFromTemplate,
onCreateNoteFromMD,
restoreNoteTabs,
};

View File

@ -1,42 +0,0 @@
import { addLineToNote, getNoteTreeFlattened } from "../utils/note";
export { annotationTagAction };
async function annotationTagAction(
ids: Array<number | string>,
extraData: Record<string, any>,
) {
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,
);
}
}
}

View File

@ -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(
`<div data-schema-version="${config.dataSchemaVersion}"><h1>${header}</h1>\n</div>`,
);
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]

View File

@ -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,
},

View File

@ -137,7 +137,7 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
{
type: "click",
listener: async (e) => {
ZoteroPane.openNoteWindow(linkNote.id);
addon.hooks.onOpenNote(linkNote.id, "window", linkParams);
},
},
],
@ -146,42 +146,6 @@ async function updateEditorLinkPopup(editor: Zotero.EditorInstance) {
_window.document
.querySelector(".link-popup")
?.append(insertButton, updateButton, openButton);
// if (linkPopup) {
// if (Zotero.Prefs.get("Knowledge4Zotero.linkAction.preview") as boolean) {
// let previewContainer =
// _window.document.getElementById("note-link-preview");
// if (previewContainer) {
// previewContainer.remove();
// }
// previewContainer = ztoolkit.UI.createElement(
// _window.document,
// "div"
// ) as HTMLDivElement;
// previewContainer.id = "note-link-preview";
// previewContainer.className = "ProseMirror primary-editor";
// previewContainer.innerHTML =
// await this._Addon.NoteParse.parseNoteStyleHTML(linkNote);
// previewContainer.addEventListener("click", (e) => {
// this._Addon.WorkspaceWindow.setWorkspaceNote("preview", linkNote);
// });
// linkPopup.append(previewContainer);
// previewContainer.setAttribute(
// "style",
// `width: 98%;height: ${
// linkPopup ? Math.min(linkPopup.offsetTop, 300) : 300
// }px;position: absolute;background: white;bottom: 36px;overflow: hidden;box-shadow: 0 0 5px 5px rgba(0,0,0,0.2);border-radius: 5px;cursor: pointer;opacity: 0.9;`
// );
// previewContainer
// .querySelector("div[data-schema-version]")
// ?.childNodes.forEach((node) => {
// if ((node as Element).setAttribute) {
// (node as Element).setAttribute("style", "margin: 0");
// } else {
// node.remove();
// }
// });
// }
// }
} else {
Array.from(_window.document.querySelectorAll(".link-popup-extra")).forEach(
(elem) => elem.remove(),

View File

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

View File

@ -212,7 +212,7 @@ async function toFreeMind(noteItem: Zotero.Item) {
}
async function embedLinkedNotes(noteItem: Zotero.Item): Promise<string> {
const parser = ztoolkit.getDOMParser();
const parser = new DOMParser();
const globalCitationData = getNoteCitationData(noteItem as Zotero.Item);

View File

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

View File

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

View File

@ -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(
@ -112,109 +96,7 @@ export function registerMenus() {
const menuFileAnchor = document.querySelector(
"#menu_newCollection",
) as XUL.MenuItem;
const recentMainNotesMenuId = "zotero-recent-main-notes-menu";
const recentMainNotesMenuPopupId = "zotero-recent-main-notes-popup";
const removeChildren = (parent: Element) => {
while (parent.lastChild) {
parent.removeChild(parent.lastChild);
}
};
ztoolkit.Menu.register(
"menuFile",
{
tag: "menu",
id: recentMainNotesMenuId,
label: getString("menuFile-openRecent"),
icon: `chrome://${config.addonRef}/content/icons/favicon.png`,
children: [],
popupId: recentMainNotesMenuPopupId,
},
"after",
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,
},
},
];
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 +138,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"),

View File

@ -23,7 +23,7 @@ export function registerReaderAnnotationButton() {
createNoteFromAnnotation(
reader._item.libraryID,
annotationData.id,
(e as MouseEvent).shiftKey ? "standalone" : "auto",
(e as MouseEvent).shiftKey ? "builtin" : "auto",
);
e.preventDefault();
},
@ -54,7 +54,7 @@ export function registerReaderAnnotationButton() {
async function createNoteFromAnnotation(
libraryID: number,
itemKey: string,
openMode: "standalone" | "auto" = "auto",
openMode: "builtin" | "auto" = "auto",
) {
const annotationItem = Zotero.Items.getByLibraryAndKey(
libraryID,

View File

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

View File

@ -44,11 +44,12 @@ export async function showSyncInfo(noteId: number) {
})
.addButton(getString("syncInfo.unSync"), "unSync", {
callback: async (ev) => {
const allNoteIds = await addon.api.note.getRelatedNoteIds(noteId);
for (const itemId of allNoteIds) {
const { detectedIDSet } =
await addon.api.related.getRelatedNoteIds(noteId);
for (const itemId of Array.from(detectedIDSet)) {
addon.api.sync.removeSyncNote(itemId);
}
showHint(`Cancel sync of ${allNoteIds.length} notes.`);
showHint(`Cancel sync of ${detectedIDSet.size} notes.`);
},
})
.addButton(getString("syncInfo.reveal"), "reveal", {
@ -68,7 +69,7 @@ export async function showSyncInfo(noteId: number) {
addon.hooks.onShowExportNoteOptions([noteId]);
},
})
.addButton(getString("export.cancel"), "cancel")
.addButton(getString("syncInfo.cancel"), "cancel")
.open(getString("export.title"), {
resizable: true,
centerscreen: true,

View File

@ -91,7 +91,7 @@ export async function showSyncManager() {
.setProp("onActivate", (ev) => {
const noteIds = getSelectedNoteIds();
noteIds.forEach((noteId) =>
addon.hooks.onOpenNote(noteId, "standalone"),
addon.hooks.onOpenNote(noteId, "builtin"),
);
return true;
})
@ -99,7 +99,6 @@ export async function showSyncManager() {
"getRowString",
(index) => addon.data.prefs?.rows[index].title || "",
)
// @ts-ignore TODO: Fix type in zotero-plugin-toolkit
.setProp("onColumnSort", (columnIndex, ascending) => {
addon.data.sync.manager.columnIndex = columnIndex;
addon.data.sync.manager.columnAscending = ascending > 0;

View File

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

View File

@ -77,7 +77,7 @@ function getTemplatePromptHandler(name: string) {
async function insertTemplateCallback(name: string) {
const targetNoteItem = Zotero.Items.get(
addon.data.template.picker.data.noteId || addon.data.workspace.mainId,
addon.data.template.picker.data.noteId,
);
let html = "";
if (name.toLowerCase().startsWith("[item]")) {

View File

@ -78,7 +78,7 @@ async function renderTemplatePreview(
const link = getNoteLink(data);
const linkText = data.getNoteTitle().trim() || link;
const subNoteItem = data;
const noteItem = Zotero.Items.get(addon.data.workspace.mainId);
const noteItem = new Zotero.Item("note");
html = await addon.api.template.runTemplate(
templateName,
"link, linkText, subNoteItem, noteItem",
@ -95,7 +95,7 @@ async function renderTemplatePreview(
html = "<p>No note item selected</p>";
} else {
const link = getNoteLink(data);
const noteItem = Zotero.Items.get(addon.data.workspace.mainId);
const noteItem = new Zotero.Item("note");
const linkText = noteItem.getNoteTitle().trim() || "Workspace Note";
const subNoteItem = data;
html = await addon.api.template.runTemplate(
@ -114,7 +114,7 @@ async function renderTemplatePreview(
html = "<p>No note item selected</p>";
} else {
const link = getNoteLink(data);
const noteItem = Zotero.Items.get(addon.data.workspace.mainId);
const noteItem = new Zotero.Item("note");
html = await addon.api.template.runTemplate(
templateName,
"link, noteItem",

28
src/modules/viewItems.ts Normal file
View File

@ -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.onOpenNote(item.id, "tab");
continue;
}
otherItems.push(item);
}
// @ts-ignore
return origin.apply(this, [otherItems, event]);
},
enabled: true,
});
}

View File

@ -1,489 +1,19 @@
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 async 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";
await workspace.render();
return workspace;
}

View File

@ -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;
}
}

View File

@ -0,0 +1,152 @@
import { config } from "../../../package.json";
import { waitUtilAsync } from "../../utils/wait";
import { getWorkspaceByUID } from "../../utils/workspace";
export function openNotePreview(
noteItem: Zotero.Item,
workspaceUID: string,
options: {
lineIndex?: number;
sectionName?: string;
} = {},
) {
const key = Zotero.ItemPaneManager.registerSection({
paneID: `bn-note-preview-${workspaceUID}-${noteItem.id}`,
pluginID: config.addonID,
header: {
icon: "chrome://zotero/skin/16/universal/note.svg",
l10nID: `${config.addonRef}-note-preview-header`,
},
sidenav: {
icon: "chrome://zotero/skin/20/universal/note.svg",
l10nID: `${config.addonRef}-note-preview-sidenav`,
l10nArgs: JSON.stringify({ title: noteItem.getNoteTitle() }),
},
bodyXHTML: `<note-editor data-id="${noteItem.id}" class="bn-note-preview"></note-editor>`,
sectionButtons: [
{
type: "openNote",
icon: "chrome://zotero/skin/16/universal/open-link.svg",
l10nID: `${config.addonRef}-note-preview-open`,
onClick: ({ event }) => {
const position = (event as MouseEvent).shiftKey ? "window" : "tab";
Zotero[config.addonRef].hooks.onOpenNote(noteItem.id, position);
},
},
{
type: "closePreview",
icon: "chrome://zotero/skin/16/universal/minus.svg",
l10nID: `${config.addonRef}-note-preview-close`,
onClick: () => {
Zotero.ItemPaneManager.unregisterSection(key || "");
},
},
{
type: "fullHeight",
icon: `chrome://${config.addonRef}/content/icons/full-16.svg`,
l10nID: `${config.addonRef}-note-preview-full`,
onClick: ({ body }) => {
const iframe = body.querySelector("iframe");
const details = body.closest("bn-details");
const head = body
.closest("item-pane-custom-section")
?.querySelector(".head");
const heightKey = "--details-height";
if (iframe?.style.getPropertyValue(heightKey)) {
iframe.style.removeProperty(heightKey);
// @ts-ignore
if (details.pinnedPane === key) {
// @ts-ignore
details.pinnedPane = "";
}
} else {
iframe?.style.setProperty(
heightKey,
`${details!.clientHeight - head!.clientHeight - 8}px`,
);
// @ts-ignore
details.pinnedPane = key;
// @ts-ignore
details.scrollToPane(key);
}
},
},
],
onItemChange: ({ body, setEnabled }) => {
if (
(body.closest("bn-workspace") as HTMLElement | undefined)?.dataset
.uid !== workspaceUID
) {
setEnabled(false);
return;
}
setEnabled(true);
},
onRender: ({ setSectionSummary }) => {
setSectionSummary(noteItem.getNoteTitle());
},
onAsyncRender: async ({ body }) => {
const editorElement = body.querySelector("note-editor")! as EditorElement;
await waitUtilAsync(() => Boolean(editorElement._initialized));
if (!editorElement._initialized) {
throw new Error("initNoteEditor: waiting initialization failed");
}
editorElement.mode = "edit";
editorElement.viewMode = "library";
editorElement.parent = noteItem?.parentItem;
editorElement.item = noteItem;
await waitUtilAsync(() => Boolean(editorElement._editorInstance));
await editorElement._editorInstance._initPromise;
if (typeof options.lineIndex === "number") {
addon.api.editor.scroll(
editorElement._editorInstance,
options.lineIndex,
);
}
if (typeof options.sectionName === "string") {
addon.api.editor.scrollToSection(
editorElement._editorInstance,
options.sectionName,
);
}
},
});
const workspace = getWorkspaceByUID(workspaceUID);
setTimeout(
() =>
// @ts-ignore
workspace?.querySelector("bn-details")?.scrollToPane(key),
500,
);
if (!key) {
scrollPreviewEditorTo(noteItem, workspaceUID, options);
}
}
function scrollPreviewEditorTo(
item: Zotero.Item,
workspaceUID: string,
options: {
lineIndex?: number;
sectionName?: string;
} = {},
) {
const workspace = getWorkspaceByUID(workspaceUID);
if (!workspace) return;
const editor = workspace.querySelector(
`note-editor[data-id="${item.id}"]`,
) as EditorElement;
if (!editor) return;
if (typeof options.lineIndex === "number") {
addon.api.editor.scroll(editor._editorInstance, options.lineIndex);
}
if (typeof options.sectionName === "string") {
addon.api.editor.scrollToSection(
editor._editorInstance,
options.sectionName,
);
}
}

View File

@ -0,0 +1,139 @@
import { config } from "../../../package.json";
import { slice } from "../../utils/str";
import { waitUtilAsync } from "../../utils/wait";
export function registerNoteRelation() {
const key = Zotero.ItemPaneManager.registerSection({
paneID: `bn-note-relation`,
pluginID: config.addonID,
header: {
icon: `chrome://${config.addonRef}/content/icons/relation-16.svg`,
l10nID: `${config.addonRef}-note-relation-header`,
},
sidenav: {
icon: `chrome://${config.addonRef}/content/icons/relation-20.svg`,
l10nID: `${config.addonRef}-note-relation-sidenav`,
},
bodyXHTML: `
<linkset>
<html:link
rel="stylesheet"
href="chrome://${config.addonRef}/content/styles/relation.css"
></html:link>
</linkset>
<iframe
src="chrome://${config.addonRef}/content/relationGraph.html"
id="bn-relation-graph"
></iframe>`,
sectionButtons: [
{
type: "refreshGraph",
icon: "chrome://zotero/skin/16/universal/sync.svg",
l10nID: `${config.addonRef}-note-relation-refresh`,
onClick: ({ body, item }) => {
refresh(body, item);
},
},
],
onInit({ body }) {
body
.querySelector("iframe")!
.contentWindow?.addEventListener("message", (ev) => {
if (ev.data.type === "openNote") {
addon.hooks.onOpenNote(
ev.data.id,
ev.data.isShift ? "window" : "tab",
);
}
});
},
onItemChange: ({ body, setEnabled }) => {
if (body.closest("bn-workspace") as HTMLElement | undefined) {
setEnabled(true);
return;
}
setEnabled(false);
},
onRender: () => {},
onAsyncRender: async ({ body, item }) => {
await refresh(body, item);
},
});
}
async function refresh(body: HTMLElement, item: Zotero.Item) {
const data = await getRelationData(item);
await waitUtilAsync(
() =>
body.querySelector("iframe")!.contentDocument?.readyState === "complete",
);
body.querySelector("iframe")!.contentWindow?.postMessage(
{
type: "render",
graph: data,
},
"*",
);
}
async function getRelationData(note: Zotero.Item) {
if (!note) return;
const currentContent = note.getNote();
const currentLink = addon.api.convert.note2link(note);
const currentTitle = slice(note.getNoteTitle(), 15);
const { detectedIDSet, currentIDSet } =
await addon.api.related.getRelatedNoteIds(note.id);
if (!areSetsEqual(detectedIDSet, currentIDSet)) {
await addon.api.related.updateRelatedNotes(note.id);
}
const items = Zotero.Items.get(Array.from(detectedIDSet));
const nodes = [];
const links = [];
for (const item of items) {
const compareContent = item.getNote();
const compareLink = addon.api.convert.note2link(item);
const compareTitle = slice(item.getNoteTitle(), 15);
if (currentLink && compareContent.includes(currentLink)) {
links.push({
source: item.id,
target: note.id,
value: 1,
});
}
if (compareLink && currentContent.includes(compareLink)) {
links.push({
source: note.id,
target: item.id,
value: 1,
});
}
nodes.push({
id: item.id,
title: compareTitle,
group: 2,
});
}
nodes.push({
id: note.id,
title: currentTitle,
group: 1,
});
return { nodes, links };
}
function areSetsEqual(set1: Set<any>, set2: Set<any>): boolean {
if (set1.size !== set2.size) {
return false;
}
for (const item of set1) {
if (!set2.has(item)) {
return false;
}
}
return true;
}

View File

@ -1,400 +1,94 @@
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;
const spacer = doc.querySelector("#zotero-collections-toolbar > spacer");
if (!spacer) {
return;
}
const tabButton = ztoolkit.UI.insertElementBefore(
{
tag: "toolbarbutton",
classList: ["zotero-tb-button"],
styles: {
listStyleImage: `url("chrome://${config.addonRef}/content/icons/icon-linear-20.svg")`,
},
attributes: {
tooltiptext: "Open workspace",
},
listeners: [
{
type: "command",
listener: (ev) => {
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");
});
ob.observe(collectionSearch, {
attributes: true,
attributeFilter: ["class"],
});
win.addEventListener(
"unload",
() => {
ob.disconnect();
win.removeEventListener("message", messageHandler);
},
{ once: true },
export async function openWorkspaceTab(
item: Zotero.Item,
options: { select?: boolean; lineIndex?: number; sectionName?: string } = {
select: true,
},
) {
const { select, lineIndex, sectionName } = options;
if (!item) return;
const currentTab = Zotero_Tabs._tabs.find(
(tab) => tab.data?.itemID == item.id,
);
}
export async function openWorkspaceTab() {
if (addon.data.workspace.tab.active) {
Zotero_Tabs.select(addon.data.workspace.tab.id!);
if (currentTab) {
if (select ?? true) Zotero_Tabs.select(currentTab.id);
scrollTabEditorTo(item, options);
return;
}
const { id, container } = Zotero_Tabs.add({
type: TAB_TYPE,
title: getString("tab.name"),
index: 1,
title: item.getNoteTitle(),
data: {
itemID: addon.data.workspace.mainId,
},
select: false,
onClose: () => {
deActivateWorkspaceTab();
itemID: item.id,
},
select: select ?? true,
onClose: () => {},
});
const workspace = await initWorkspace(container, item);
workspace.scrollEditorTo({
lineIndex,
sectionName,
});
addon.data.workspace.tab.id = id;
container.setAttribute("workspace-type", "tab");
addon.data.workspace.tab.container = container;
await activateWorkspaceTab();
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.splitter;
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}`];
ZoteroContextPane.update();
}
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");
export function restoreNoteTabs() {
const tabsCache: _ZoteroTypes.TabInstance[] =
Zotero.Session.state.windows.find((x: any) => x.type == "pane")?.tabs;
for (const i in tabsCache) {
const tab = tabsCache[i];
if (tab.type !== TAB_TYPE) continue;
openWorkspaceTab(Zotero.Items.get(tab.data.itemID), {
select: tab.selected,
});
});
// 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";
});
}
export function onUpdateNoteTabsTitle(noteItems: Zotero.Item[]) {
const ids = noteItems.map((item) => item.id);
for (const tab of Zotero_Tabs._tabs) {
if (tab.type !== TAB_TYPE) continue;
if (ids.includes(tab.data.itemID)) {
const newTitle = Zotero.Items.get(tab.data.itemID).getNoteTitle();
if (tab.title === newTitle) {
continue;
}
Zotero_Tabs.rename(tab.id, newTitle);
}
}
}
function scrollTabEditorTo(
item: Zotero.Item,
options: {
lineIndex?: number;
sectionName?: string;
} = {},
) {
const tab = Zotero_Tabs._tabs.find((tab) => tab.data?.itemID == item.id);
if (!tab || tab.type !== TAB_TYPE) return;
const workspace = document.querySelector(`#${tab.id} > bn-workspace`);
if (!workspace) return;
// @ts-ignore
workspace.scrollEditorTo(options);
}

View File

@ -1,12 +1,9 @@
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,
options: { lineIndex?: number; sectionName?: string } = {},
) {
const windowArgs = {
_initPromise: Zotero.Promise.defer(),
};
@ -17,16 +14,15 @@ 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);
});
const workspace = await addon.hooks.onInitWorkspace(container, item);
workspace.scrollEditorTo(options);
win.focus();
// @ts-ignore
win.updateTitle();
return workspace;
}

View File

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

View File

@ -26,19 +26,19 @@ import {
formatPath,
jointPath,
randomString,
} from "../../utils/str";
} from "./str";
import {
copyEmbeddedImagesInHTML,
importImageToNote,
renderNoteHTML,
} from "../../utils/note";
} from "./note";
import {
getLinkedNotesRecursively,
getNoteLink,
getNoteLinkParams,
} from "../../utils/link";
import { parseAnnotationHTML } from "../../utils/annotation";
import { getPref } from "../../utils/prefs";
} from "./link";
import { parseAnnotationHTML } from "./annotation";
import { getPref } from "./prefs";
export {
md2note,
@ -175,7 +175,7 @@ async function note2noteDiff(noteItem: Zotero.Item) {
function note2link(
noteItem: Zotero.Item,
options: Parameters<typeof getNoteLink>[1],
options: Parameters<typeof getNoteLink>[1] = {},
) {
return getNoteLink(noteItem, options);
}
@ -236,7 +236,7 @@ function annotations2html(
async function note2html(
noteItems: Zotero.Item | Zotero.Item[],
options: { targetNoteItem?: Zotero.Item; html?: string },
options: { targetNoteItem?: Zotero.Item; html?: string } = {},
) {
if (!Array.isArray(noteItems)) {
noteItems = [noteItems];

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

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

View File

@ -4,22 +4,18 @@ import { getEditorInstance, getPositionAtLine, insert } from "./editor";
import { formatPath, getItemDataURL } from "./str";
import { showHint } from "./hint";
import { config } from "../../package.json";
import { getNoteLinkParams } from "./link";
export {
renderNoteHTML,
parseHTMLLines,
getLinesInNote,
addLineToNote,
getNoteType,
getNoteTree,
getNoteTreeFlattened,
getNoteTreeNodeById,
copyEmbeddedImagesFromNote,
copyEmbeddedImagesInHTML,
importImageToNote,
getRelatedNoteIds,
updateRelatedNotes,
};
function parseHTMLLines(html: string): string[] {
@ -112,11 +108,32 @@ function parseHTMLLines(html: string): string[] {
return parsedLines;
}
function getLinesInNote(note: Zotero.Item): string[] {
function getLinesInNote(note: Zotero.Item): string[];
async function getLinesInNote(
note: Zotero.Item,
options: {
convertToHTML?: true;
},
): Promise<string[]>;
function getLinesInNote(
note: Zotero.Item,
options?: {
convertToHTML?: boolean;
},
): string[] | Promise<string[]> {
if (!note) {
return [];
}
const noteText: string = note.getNote();
if (options?.convertToHTML) {
return new Promise((resolve) => {
addon.api.convert.note2html(note).then((html) => {
resolve(parseHTMLLines(html));
});
});
}
return parseHTMLLines(noteText);
}
@ -163,7 +180,7 @@ async function addLineToNote(
const editor = getEditorInstance(note.id);
if (editor && !forceMetadata) {
// The note is opened. Add line via note editor
const pos = getPositionAtLine(editor, lineIndex, "end");
const pos = getPositionAtLine(editor, lineIndex, "start");
ztoolkit.log("Add note line via note editor", pos);
insert(editor, html, pos);
// The selection is automatically moved to the next line
@ -197,7 +214,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 +286,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<NoteNodeData> {
const noteLines = getLinesInNote(note);
const parser = ztoolkit.getDOMParser();
const parser = new DOMParser();
const tree = new TreeModel();
const root = tree.parse({
id: -1,
@ -422,7 +429,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) {
@ -528,75 +535,3 @@ async function importImageToNote(
return attachment.key;
}
async function updateRelatedNotes(noteID: number) {
const noteItem = Zotero.Items.get(noteID);
if (!noteItem) {
ztoolkit.log(`updateRelatedNotes: ${noteID} is not a note.`);
return;
}
const relatedNoteIDs = await getRelatedNoteIds(noteID);
const relatedNotes = Zotero.Items.get(relatedNoteIDs);
const currentRelatedNotes = {} as Record<number, Zotero.Item>;
// Get current related items
for (const relItemKey of noteItem.relatedItems) {
try {
const relItem = (await Zotero.Items.getByLibraryAndKeyAsync(
noteItem.libraryID,
relItemKey,
)) as Zotero.Item;
if (relItem.isNote()) {
currentRelatedNotes[relItem.id] = relItem;
}
} catch (e) {
ztoolkit.log(e);
}
}
await Zotero.DB.executeTransaction(async () => {
const saveParams = {
skipDateModifiedUpdate: true,
skipSelect: true,
notifierData: {
skipBN: true,
},
};
for (const toAddNote of relatedNotes) {
if (toAddNote.id in currentRelatedNotes) {
// Remove existing notes from current dict for later process
delete currentRelatedNotes[toAddNote.id];
continue;
}
toAddNote.addRelatedItem(noteItem);
noteItem.addRelatedItem(toAddNote);
toAddNote.save(saveParams);
delete currentRelatedNotes[toAddNote.id];
}
for (const toRemoveNote of Object.values(currentRelatedNotes)) {
// Remove related notes that are not in the new list
toRemoveNote.removeRelatedItem(noteItem);
noteItem.removeRelatedItem(toRemoveNote);
toRemoveNote.save(saveParams);
}
noteItem.save(saveParams);
});
}
async function getRelatedNoteIds(noteId: number): Promise<number[]> {
let allNoteIds: number[] = [noteId];
const note = Zotero.Items.get(noteId);
const linkMatches = note.getNote().match(/zotero:\/\/note\/\w+\/\w+\//g);
if (!linkMatches) {
return allNoteIds;
}
const subNoteIds = (
await Promise.all(
linkMatches.map(async (link) => getNoteLinkParams(link).noteItem),
)
)
.filter((item) => item && item.isNote())
.map((item) => (item as Zotero.Item).id);
allNoteIds = allNoteIds.concat(subNoteIds);
allNoteIds = new Array(...new Set(allNoteIds));
return allNoteIds;
}

87
src/utils/related.ts Normal file
View File

@ -0,0 +1,87 @@
import { getNoteLink, getNoteLinkParams } from "./link";
export { getRelatedNoteIds, updateRelatedNotes };
async function updateRelatedNotes(noteID: number) {
const noteItem = Zotero.Items.get(noteID);
if (!noteItem) {
ztoolkit.log(`updateRelatedNotes: ${noteID} is not a note.`);
return;
}
const { detectedIDSet, currentIDSet } = await getRelatedNoteIds(noteID);
await Zotero.DB.executeTransaction(async () => {
const saveParams = {
skipDateModifiedUpdate: true,
skipSelect: true,
notifierData: {
skipBN: true,
},
};
for (const toAddNote of Zotero.Items.get(Array.from(detectedIDSet))) {
if (currentIDSet.has(toAddNote.id)) {
// Remove existing notes from current dict for later process
currentIDSet.delete(toAddNote.id);
continue;
}
toAddNote.addRelatedItem(noteItem);
noteItem.addRelatedItem(toAddNote);
toAddNote.save(saveParams);
currentIDSet.delete(toAddNote.id);
}
for (const toRemoveNote of Zotero.Items.get(Array.from(currentIDSet))) {
// Remove related notes that are not in the new list
toRemoveNote.removeRelatedItem(noteItem);
noteItem.removeRelatedItem(toRemoveNote);
toRemoveNote.save(saveParams);
}
noteItem.save(saveParams);
});
}
async function getRelatedNoteIds(noteId: number) {
let detectedIDs: number[] = [];
const note = Zotero.Items.get(noteId);
const linkMatches = note.getNote().match(/zotero:\/\/note\/\w+\/\w+\//g);
const currentIDs: number[] = [];
if (linkMatches) {
const subNoteIds = (
await Promise.all(
linkMatches.map(async (link) => getNoteLinkParams(link).noteItem),
)
)
.filter((item) => item && item.isNote())
.map((item) => (item as Zotero.Item).id);
detectedIDs = detectedIDs.concat(subNoteIds);
}
const currentNoteLink = getNoteLink(note);
if (currentNoteLink) {
// Get current related items
for (const relItemKey of note.relatedItems) {
try {
const relItem = (await Zotero.Items.getByLibraryAndKeyAsync(
note.libraryID,
relItemKey,
)) as Zotero.Item;
// If the related item is a note and contains the current note link
// Add it to the related note list
if (relItem.isNote()) {
if (relItem.getNote().includes(currentNoteLink)) {
detectedIDs.push(relItem.id);
}
currentIDs.push(relItem.id);
}
} catch (e) {
ztoolkit.log(e);
}
}
}
const detectedIDSet = new Set(detectedIDs);
detectedIDSet.delete(noteId);
const currentIDSet = new Set(currentIDs);
return { detectedIDSet, currentIDSet };
}

View File

@ -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<void>((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);

View File

@ -1,5 +1,14 @@
export { isWindowAlive };
export { isWindowAlive, getFocusedWindow };
function isWindowAlive(win?: Window) {
return win && !Components.utils.isDeadWrapper(win) && !win.closed;
}
function getFocusedWindow() {
const wins = Services.wm.getEnumerator(null) as Window[];
for (const win of wins) {
if (win.document.hasFocus()) {
return win;
}
}
}

View File

@ -4,3 +4,12 @@ export enum OutlineType {
mindMap,
bubbleMap,
}
export function getWorkspaceByUID(uid: string): HTMLElement | undefined {
const workspace = addon.data.workspace.instances[uid]?.deref();
if (!workspace?.ownerDocument) {
delete addon.data.workspace.instances[uid];
return undefined;
}
return workspace;
}

19
typings/global.d.ts vendored
View File

@ -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;
}

View File

@ -7,7 +7,7 @@
"update_link": "https://github.com/windingwind/zotero-better-notes/releases//download/v1.1.4-beta.57/zotero-better-notes.xpi",
"applications": {
"zotero": {
"strict_min_version": "7.0.0-beta.55"
"strict_min_version": "7.0.0-beta.70"
}
}
}