Merge pull request #1 from windingwind/master

merge updates
This commit is contained in:
ezellohar 2023-11-07 08:49:27 +01:00 committed by GitHub
commit cd648d3352
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 135 additions and 194 deletions

View File

@ -49,6 +49,16 @@
preference="__prefsPrefix__.syncPeriodSeconds"
></html:input>
</hbox>
<hbox>
<html:label
for="__addonRef__-sync-attachmentFolder"
data-l10n-id="sync-attachmentFolder-label"
></html:label>
<html:input
id="__addonRef__-sync-attachmentFolder"
preference="__prefsPrefix__.syncAttachmentFolder"
></html:input>
</hbox>
<hbox>
<button
onclick="Zotero.__addonInstance__.hooks.onShowSyncManager()"

View File

@ -105,6 +105,7 @@ editor-toolbar-settings-copyLinkAtSection = Copy Note Link at Section ({ $sectio
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

View File

@ -6,6 +6,7 @@ workspace-autoUpdateRelatedNotes =
.label = Automatically update related
sync-title = Sync
sync-period-label = Auto-sync period (seconds)
sync-attachmentFolder-label = Attachment folder
sync-manager =
.label = Open Sync Manager
template-title = Template

View File

@ -101,6 +101,7 @@ editor-toolbar-settings-copyLinkAtSection = Copia link della nota alla sezione (
editor-toolbar-settings-openParent = Apri allegato
editor-toolbar-settings-export = Esporta nota corrente...
editor-toolbar-settings-refreshSyncing = Sincronizza ora
editor-toolbar-settings-updateRelatedNotes = Update Related Notes
editor-toolbar-link-title = Collega la nota corrente alla nota di lavoro
editor-toolbar-link-popup-nodata = La nota di lavoro non è valida

View File

@ -6,6 +6,7 @@ workspace-autoUpdateRelatedNotes =
.label = Aggiorna automaticamente le note correlate
sync-title = Sincronizzazione
sync-period-label = Intervallo della sincronizzazione automatica (secondi)
sync-attachmentFolder-label = Attachment folder
sync-manager =
.label = Apri Manager di sincronizzazione
template-title = Template

View File

@ -105,6 +105,7 @@ editor-toolbar-settings-copyLinkAtSection = Копировать Ссылку н
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=Невалидная Заметка раб. пространства

View File

@ -6,6 +6,7 @@ workspace-autoUpdateRelatedNotes =
.label = Автообновление связанных заметок
sync-title = Синк
sync-period-label = Авто-синк период (сек)
sync-attachmentFolder-label = Attachment folder
sync-manager =
.label = Открыть Синк менеджер
template-title = Шаблон

View File

@ -105,6 +105,7 @@ editor-toolbar-settings-copyLinkAtSection=复制当前节({ $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=工作区笔记不可用

View File

@ -6,6 +6,7 @@ workspace-autoUpdateRelatedNotes =
.label = 自动更新"相关"
sync-title = 同步
sync-period-label = 自动同步周期 (秒)
sync-attachmentFolder-label = 附件文件夹
sync-manager =
.label = 打开同步管理器
template-title = 模板

View File

@ -2,6 +2,7 @@ pref("__prefsPrefix__.recentMainNoteIds", "");
pref("__prefsPrefix__.syncNoteIds", "");
pref("__prefsPrefix__.syncPeriodSeconds", 30);
pref("__prefsPrefix__.syncAttachmentFolder", "attachments");
pref("__prefsPrefix__.autoAnnotation", false);

View File

@ -1,6 +1,6 @@
{
"name": "zotero-better-notes",
"version": "1.1.4-31",
"version": "1.1.4-36",
"description": "Everything about note management. All in Zotero.",
"config": {
"addonName": "Better Notes for Zotero",
@ -98,6 +98,6 @@
"release-it": "^16.1.5",
"replace-in-file": "^7.0.1",
"typescript": "^5.2.2",
"zotero-types": "^1.3.2"
"zotero-types": "^1.3.5"
}
}

View File

@ -26,9 +26,7 @@ import {
import { registerNotify } from "./modules/notify";
import { showWorkspaceWindow } from "./modules/workspace/window";
import {
checkReaderAnnotationButton,
registerReaderInitializer,
unregisterReaderInitializer,
registerReaderAnnotationButton,
} from "./modules/reader";
import { setSyncing, callSyncing } from "./modules/sync/hooks";
import {
@ -70,6 +68,8 @@ async function onStartup() {
registerPrefsWindow();
registerReaderAnnotationButton();
initSyncList();
setSyncing();
@ -88,19 +88,15 @@ async function onMainWindowLoad(win: Window): Promise<void> {
registerWorkspaceTab();
registerReaderInitializer();
initTemplates();
}
async function onMainWindowUnload(win: Window): Promise<void> {
ztoolkit.unregisterAll();
unregisterReaderInitializer();
}
function onShutdown(): void {
ztoolkit.unregisterAll();
unregisterReaderInitializer();
// Remove addon object
addon.data.alive = false;
unregisterWorkspaceTab();
@ -150,15 +146,6 @@ function onNotify(
});
}
}
// Reader annotation buttons update
if (event === "add" && type === "item") {
const annotationItems = Zotero.Items.get(ids as number[]).filter((item) =>
item.isAnnotation(),
);
if (annotationItems.length !== 0) {
checkReaderAnnotationButton(annotationItems);
}
}
// Insert annotation when assigning tag starts with @
if (event === "add" && type === "item-tag") {
annotationTagAction(ids as number[], extraData);

View File

@ -38,6 +38,7 @@ import {
getNoteLinkParams,
} from "../../utils/link";
import { parseAnnotationHTML } from "../../utils/annotation";
import { getPref } from "../../utils/prefs";
export {
md2note,
@ -79,7 +80,7 @@ async function note2md(
await processN2MRehypeImageNodes(
getN2MRehypeImageNodes(rehype),
noteItem.libraryID,
jointPath(dir, "attachments"),
jointPath(dir, getPref("syncAttachmentFolder") as string),
options.skipSavingImages,
false,
NodeMode.direct,
@ -134,7 +135,7 @@ async function md2note(
const _note = rehype2note(_rehype as HRoot);
const rehype = note2rehype(_note);
// Check if image already belongs to note
// Check if image citation already belongs to note
processM2NRehypeMetaImageNodes(getM2NRehypeImageNodes(rehype));
processM2NRehypeHighlightNodes(getM2NRehypeHighlightNodes(rehype));
@ -441,8 +442,7 @@ function md2remark(str: string) {
.replace(/!\[\[(.*)\]\]/g, (s: string) => `![](${s.slice(3, -2)})`)
.replace(
/!\[(.*)\]\((.*)\)/g,
(match, altText, imageURL) =>
`![${altText}](${encodeURI(imageURL)})`,
(match, altText, imageURL) => `![${altText}](${encodeURI(imageURL)})`,
);
const remark = unified()
.use(remarkGfm)
@ -945,7 +945,10 @@ async function processN2MRehypeImageNodes(
newFile = formatPath(
absolutePath
? newFile
: `attachments/${PathUtils.split(newFile).pop()}`,
: jointPath(
getPref("syncAttachmentFolder") as string,
PathUtils.split(newFile).pop() || "",
),
);
} catch (e) {
ztoolkit.log(e);
@ -1119,7 +1122,6 @@ function processM2NRehypeNoteLinkNodes(nodes: string | any[]) {
}
async function processM2NRehypeImageNodes(
this: any,
nodes: any[],
noteItem: Zotero.Item,
fileDir: string,
@ -1129,26 +1131,36 @@ async function processM2NRehypeImageNodes(
return;
}
let attKeys = [] as string[];
if (isImport) {
attKeys = Zotero.Items.get(noteItem.getAttachments()).map(
(item) => item.key,
);
}
for (const node of nodes) {
if (isImport) {
// We encode the src in md2remark and decode it here.
let src = formatPath(decodeURIComponent(node.properties.src));
const srcType = (src as string).startsWith("data:")
? "b64"
: (src as string).startsWith("http")
? "url"
: "file";
if (srcType === "file") {
if (!PathUtils.isAbsolute(src)) {
src = jointPath(fileDir, src);
}
if (!(await fileExists(src))) {
ztoolkit.log("parse image, path invalid", src);
continue;
// If image is already an attachment of note, skip import
if (!attKeys.includes(node.properties.dataAttachmentKey)) {
// We encode the src in md2remark and decode it here.
let src = formatPath(decodeURIComponent(node.properties.src));
const srcType = (src as string).startsWith("data:")
? "b64"
: (src as string).startsWith("http")
? "url"
: "file";
if (srcType === "file") {
if (!PathUtils.isAbsolute(src)) {
src = jointPath(fileDir, src);
}
if (!(await fileExists(src))) {
ztoolkit.log("parse image, path invalid", src);
continue;
}
}
const key = await importImageToNote(noteItem, src, srcType);
node.properties.dataAttachmentKey = key;
}
const key = await importImageToNote(noteItem, src, srcType);
node.properties.dataAttachmentKey = key;
}
delete node.properties.src;
node.properties.ztype && delete node.properties.ztype;

View File

@ -159,7 +159,7 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
},
{
id: makeId("settings-updateRelatedNotes"),
text: getString("editor.toolbar.settings.updateRelatedNotes"),
text: getString("editor-toolbar-settings-updateRelatedNotes"),
callback: (e) => {
addon.api.note.updateRelatedNotes(e.editor._item.id);
},

View File

@ -1,4 +1,5 @@
import { showHintWithLink } from "../../utils/hint";
import { getPref } from "../../utils/prefs";
import { formatPath, jointPath } from "../../utils/str";
export async function saveMD(
@ -10,12 +11,15 @@ export async function saveMD(
},
) {
const noteItem = Zotero.Items.get(noteId);
const dir = jointPath(
...PathUtils.split(formatPath(filename)).slice(0, -1),
);
const dir = jointPath(...PathUtils.split(formatPath(filename)).slice(0, -1));
await IOUtils.makeDirectory(dir);
const hasImage = noteItem.getNote().includes("<img");
if (hasImage) {
await Zotero.File.createDirectoryIfMissingAsync(dir);
const attachmentsDir = jointPath(
dir,
getPref("syncAttachmentFolder") as string,
);
await IOUtils.makeDirectory(attachmentsDir);
}
await Zotero.File.putContentsAsync(
filename,
@ -29,13 +33,16 @@ export async function saveMD(
export async function syncMDBatch(saveDir: string, noteIds: number[]) {
const noteItems = Zotero.Items.get(noteIds);
await Zotero.File.createDirectoryIfMissingAsync(saveDir);
const attachmentsDir = jointPath(saveDir, "attachments");
await IOUtils.makeDirectory(saveDir);
const attachmentsDir = jointPath(
saveDir,
getPref("syncAttachmentFolder") as string,
);
const hasImage = noteItems.some((noteItem) =>
noteItem.getNote().includes("<img"),
);
if (hasImage) {
await Zotero.File.createDirectoryIfMissingAsync(attachmentsDir);
await IOUtils.makeDirectory(attachmentsDir);
}
for (const noteItem of noteItems) {
const filename = await addon.api.sync.getMDFileName(noteItem.id, saveDir);

View File

@ -1,153 +1,72 @@
import { TagElementProps } from "zotero-plugin-toolkit/dist/tools/ui";
import { config } from "../../package.json";
import { ICONS } from "../utils/config";
import { getNoteLink, getNoteLinkParams } from "../utils/link";
import { addLineToNote } from "../utils/note";
export function registerReaderInitializer() {
ztoolkit.ReaderInstance.register(
"initialized",
`${config.addonRef}-annotationButtons`,
initializeReaderAnnotationButton,
export function registerReaderAnnotationButton() {
Zotero.Reader.registerEventListener(
"renderSidebarAnnotationHeader",
(event) => {
const { doc, append, params } = event;
const annotationData = params.annotation;
const annotationItem = Zotero.Items.getByLibraryAndKey(
annotationData.libraryID,
annotationData.id,
) as Zotero.Item;
if (!annotationItem) {
return;
}
const itemID = annotationItem.id;
append(
ztoolkit.UI.createElement(doc, "div", {
tag: "div",
classList: ["icon"],
properties: {
innerHTML: ICONS.readerQuickNote,
},
listeners: [
{
type: "click",
listener: (e) => {
createNoteFromAnnotation(
itemID,
(e as MouseEvent).shiftKey ? "standalone" : "auto",
);
e.preventDefault();
},
},
{
type: "mouseover",
listener: (e) => {
(e.target as HTMLElement).style.backgroundColor = "#F0F0F0";
},
},
{
type: "mouseout",
listener: (e) => {
(e.target as HTMLElement).style.removeProperty(
"background-color",
);
},
},
],
enableElementRecord: true,
}),
);
},
config.addonID,
);
// Force re-initialize
Zotero.Reader._readers.forEach((r) => {
initializeReaderAnnotationButton(r);
});
}
export function unregisterReaderInitializer() {
Zotero.Reader._readers.forEach((r) => {
unInitializeReaderAnnotationButton(r);
});
}
export async function checkReaderAnnotationButton(items: Zotero.Item[]) {
const hitSet = new Set<number>();
let t = 0;
const period = 100;
const wait = 5000;
while (items.length > hitSet.size && t < wait) {
for (const instance of Zotero.Reader._readers) {
const hitItems = await initializeReaderAnnotationButton(instance);
hitItems.forEach((item) => hitSet.add(item.id));
}
await Zotero.Promise.delay(period);
t += period;
}
}
async function initializeReaderAnnotationButton(
instance: _ZoteroTypes.ReaderInstance,
): Promise<Zotero.Item[]> {
if (!instance) {
return [];
}
await instance._initPromise;
await instance._waitForReader();
const _document = instance._iframeWindow?.document;
if (!_document) {
return [];
}
const hitItems: Zotero.Item[] = [];
for (const moreButton of Array.from(_document.querySelectorAll(".more"))) {
if (moreButton.getAttribute("_betternotesInitialized") === "true") {
continue;
}
moreButton.setAttribute("_betternotesInitialized", "true");
let annotationWrapper = moreButton;
while (!annotationWrapper.getAttribute("data-sidebar-annotation-id")) {
annotationWrapper = annotationWrapper.parentElement!;
}
const itemKey =
annotationWrapper.getAttribute("data-sidebar-annotation-id") || "";
if (!instance.itemID) {
continue;
}
const libraryID = Zotero.Items.get(instance.itemID).libraryID;
const annotationItem = (await Zotero.Items.getByLibraryAndKeyAsync(
libraryID,
itemKey,
)) as Zotero.Item;
if (!annotationItem) {
continue;
}
hitItems.push(annotationItem);
const annotationButtons: TagElementProps[] = [
{
tag: "div",
classList: ["icon"],
properties: {
innerHTML: ICONS.readerQuickNote,
},
listeners: [
{
type: "click",
listener: (e) => {
createNoteFromAnnotation(
annotationItem,
(e as MouseEvent).shiftKey ? "standalone" : "auto",
);
e.preventDefault();
},
},
{
type: "mouseover",
listener: (e) => {
(e.target as HTMLElement).style.backgroundColor = "#F0F0F0";
},
},
{
type: "mouseout",
listener: (e) => {
(e.target as HTMLElement).style.removeProperty(
"background-color",
);
},
},
],
enableElementRecord: true,
},
];
ztoolkit.UI.insertElementBefore(
{
tag: "fragment",
children: annotationButtons,
},
moreButton,
);
}
return hitItems;
}
async function unInitializeReaderAnnotationButton(
instance: _ZoteroTypes.ReaderInstance,
): Promise<void> {
if (!instance) {
return;
}
await instance._initPromise;
await instance._waitForReader();
const _document = instance._iframeWindow?.document;
if (!_document) {
return;
}
for (const moreButton of Array.from(_document.querySelectorAll(".more"))) {
if (moreButton.getAttribute("_betternotesInitialized") === "true") {
moreButton.removeAttribute("_betternotesInitialized");
}
}
}
async function createNoteFromAnnotation(
annotationItem: Zotero.Item,
itemID: number,
openMode: "standalone" | "auto" = "auto",
) {
const annotationItem = Zotero.Items.get(itemID);
if (!annotationItem) {
return;
}
const annotationTags = annotationItem.getTags().map((_) => _.tag);
const linkRegex = new RegExp("^zotero://note/(.*)$");
for (const tag of annotationTags) {

View File

@ -216,7 +216,7 @@ async function runItemTemplate(
async function getItemTemplateData() {
// If topItems are pre-defined, use it without asking
if (addon.data.template.picker.data.topItemIds?.length !== 0) {
if (addon.data.template.picker.data.topItemIds?.length > 0) {
return addon.data.template.picker.data.topItemIds;
}
const librarySelectedIds = addon.data.template.picker.data

View File

@ -91,7 +91,9 @@ export async function fileExists(path: string): Promise<boolean> {
export function jointPath(...paths: string[]) {
try {
return formatPath(pathHelper.join(...paths));
return formatPath(
pathHelper.join(...paths.map((p) => p.replaceAll("\\", "/"))),
);
} catch (e) {
ztoolkit.log("[jointPath]", e);
return "";

View File

@ -38,7 +38,6 @@ import { VirtualizedTableHelper } from "zotero-plugin-toolkit/dist/helpers/virtu
import { LibraryTabPanelManager } from "zotero-plugin-toolkit/dist/managers/libraryTabPanel";
import { MenuManager } from "zotero-plugin-toolkit/dist/managers/menu";
import { PromptManager } from "zotero-plugin-toolkit/dist/managers/prompt";
import { ReaderInstanceManager } from "zotero-plugin-toolkit/dist/managers/readerInstance";
import { ReaderTabPanelManager } from "zotero-plugin-toolkit/dist/managers/readerTabPanel";
import { LargePrefHelper } from "zotero-plugin-toolkit/dist/helpers/largePref";
@ -47,7 +46,6 @@ class MyToolkit extends BasicTool {
Prompt: PromptManager;
LibraryTabPanel: LibraryTabPanelManager;
ReaderTabPanel: ReaderTabPanelManager;
ReaderInstance: ReaderInstanceManager;
Menu: MenuManager;
PreferencePane: PreferencePaneManager;
Clipboard: typeof ClipboardHelper;
@ -63,7 +61,6 @@ class MyToolkit extends BasicTool {
this.Prompt = new PromptManager(this);
this.LibraryTabPanel = new LibraryTabPanelManager(this);
this.ReaderTabPanel = new ReaderTabPanelManager(this);
this.ReaderInstance = new ReaderInstanceManager(this);
this.Menu = new MenuManager(this);
this.PreferencePane = new PreferencePaneManager(this);
this.Clipboard = ClipboardHelper;

2
typings/global.d.ts vendored
View File

@ -32,6 +32,4 @@ declare const addon: import("../src/addon").default;
declare const __env__: "production" | "development";
declare const ChromeUtils: any;
declare class Localization {}