add: support note link with section
This commit is contained in:
parent
22dbe5dbb3
commit
fa143e3c72
|
|
@ -91,7 +91,8 @@ 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-insertTemplate = Insert Template to Cursor Line
|
||||
editor-toolbar-settings-copyLink = Copy Note Link
|
||||
editor-toolbar-settings-copyLink = Copy Note Link at Line ({ $line })
|
||||
editor-toolbar-settings-copyLinkAtSection = Copy Note Link at Section ({ $section })
|
||||
editor-toolbar-settings-openParent = Open Attachment
|
||||
editor-toolbar-settings-export = Export Current Note...
|
||||
editor-toolbar-settings-refreshSyncing = Sync Now
|
||||
|
|
|
|||
|
|
@ -91,7 +91,8 @@ editor-toolbar-settings-openWorkspace=Открыть пространство з
|
|||
editor-toolbar-settings-setWorkspace=Установить как заметку раб. пространства
|
||||
editor-toolbar-settings-previewInWorkspace=Предпросмотр в рабочем пространстве
|
||||
editor-toolbar-settings-insertTemplate=Вставить шаблон в строку курсора
|
||||
editor-toolbar-settings-copyLink=Скопировать ссылку заметки
|
||||
editor-toolbar-settings-copyLink = Copy Note Link at Line ({ $line })
|
||||
editor-toolbar-settings-copyLinkAtSection = Copy Note Link at Section ({ $section })
|
||||
editor-toolbar-settings-openParent=Открыть вложение
|
||||
editor-toolbar-settings-export=Экспортировать текущую заметку...
|
||||
editor-toolbar-settings-refreshSyncing=Синхронизировать сейчас
|
||||
|
|
|
|||
|
|
@ -91,7 +91,8 @@ editor-toolbar-settings-openWorkspace=打开笔记工作区
|
|||
editor-toolbar-settings-setWorkspace=设为工作区笔记
|
||||
editor-toolbar-settings-previewInWorkspace=在工作区预览
|
||||
editor-toolbar-settings-insertTemplate=插入模板到光标行
|
||||
editor-toolbar-settings-copyLink=复制笔记链接
|
||||
editor-toolbar-settings-copyLink=复制当前行({ $line })笔记链接
|
||||
editor-toolbar-settings-copyLinkAtSection=复制当前节({ $section })笔记链接
|
||||
editor-toolbar-settings-openParent=打开附件
|
||||
editor-toolbar-settings-export=导出当前笔记...
|
||||
editor-toolbar-settings-refreshSyncing=立即同步
|
||||
|
|
|
|||
|
|
@ -51,8 +51,10 @@ import {
|
|||
insert,
|
||||
del,
|
||||
scroll,
|
||||
scrollToSection,
|
||||
getTextBetweenLines,
|
||||
getLineAtCursor,
|
||||
getSectionAtCursor,
|
||||
getPositionAtLine,
|
||||
getTextBetween,
|
||||
getRangeAtCursor,
|
||||
|
|
@ -124,8 +126,10 @@ const editor = {
|
|||
move,
|
||||
replace,
|
||||
scroll,
|
||||
scrollToSection,
|
||||
getRangeAtCursor,
|
||||
getLineAtCursor,
|
||||
getSectionAtCursor,
|
||||
getPositionAtLine,
|
||||
getTextBetween,
|
||||
getTextBetweenLines,
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ function onOpenNote(
|
|||
mode: "auto" | "preview" | "workspace" | "standalone" = "auto",
|
||||
options: {
|
||||
lineIndex?: number;
|
||||
sectionName?: string;
|
||||
} = {},
|
||||
) {
|
||||
const noteItem = Zotero.Items.get(noteId);
|
||||
|
|
@ -217,6 +218,7 @@ function onSetWorkspaceNote(
|
|||
type: "main" | "preview" = "main",
|
||||
options: {
|
||||
lineIndex?: number;
|
||||
sectionName?: string;
|
||||
} = {},
|
||||
) {
|
||||
if (type === "main") {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { config } from "../../../package.json";
|
||||
import { ICONS } from "../../utils/config";
|
||||
import { getLineAtCursor } from "../../utils/editor";
|
||||
import { getLineAtCursor, getSectionAtCursor } from "../../utils/editor";
|
||||
import { showHint } from "../../utils/hint";
|
||||
import { getNoteLink, getNoteLinkParams } from "../../utils/link";
|
||||
import { getString } from "../../utils/locale";
|
||||
|
|
@ -49,6 +49,8 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
|||
return;
|
||||
}
|
||||
|
||||
const currentLine = getLineAtCursor(editor);
|
||||
const currentSection = getSectionAtCursor(editor);
|
||||
const settingsMenuData: PopupData[] = [
|
||||
{
|
||||
id: makeId("settings-openWorkspace"),
|
||||
|
|
@ -72,71 +74,110 @@ export async function initEditorToolbar(editor: Zotero.EditorInstance) {
|
|||
addon.hooks.onSetWorkspaceNote(e.editor._item.id, "preview");
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "splitter",
|
||||
},
|
||||
{
|
||||
id: makeId("settings-export"),
|
||||
text: getString("editor.toolbar.settings.export"),
|
||||
callback: (e) => {
|
||||
if (addon.api.sync.isSyncNote(noteItem.id)) {
|
||||
addon.hooks.onShowSyncInfo(noteItem.id);
|
||||
} else {
|
||||
addon.hooks.onShowExportNoteOptions([noteItem.id]);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "splitter",
|
||||
},
|
||||
{
|
||||
id: makeId("settings-insertTemplate"),
|
||||
text: getString("editor.toolbar.settings.insertTemplate"),
|
||||
callback: (e) => {
|
||||
addon.hooks.onShowTemplatePicker("insert", {
|
||||
noteId: e.editor._item.id,
|
||||
lineIndex: getLineAtCursor(e.editor),
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "splitter",
|
||||
},
|
||||
{
|
||||
id: makeId("settings-copyLink"),
|
||||
text: getString("editor.toolbar.settings.copyLink"),
|
||||
callback: (e) => {
|
||||
const link =
|
||||
getNoteLink(e.editor._item, {
|
||||
lineIndex: getLineAtCursor(e.editor),
|
||||
}) || "";
|
||||
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`);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
if (currentLine >= 0) {
|
||||
settingsMenuData.push(
|
||||
...(<PopupData[]>[
|
||||
{
|
||||
type: "splitter",
|
||||
},
|
||||
{
|
||||
id: makeId("settings-export"),
|
||||
text: getString("editor.toolbar.settings.export"),
|
||||
callback: (e) => {
|
||||
if (addon.api.sync.isSyncNote(noteItem.id)) {
|
||||
addon.hooks.onShowSyncInfo(noteItem.id);
|
||||
} else {
|
||||
addon.hooks.onShowExportNoteOptions([noteItem.id]);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "splitter",
|
||||
},
|
||||
{
|
||||
id: makeId("settings-insertTemplate"),
|
||||
text: getString("editor.toolbar.settings.insertTemplate"),
|
||||
callback: (e) => {
|
||||
addon.hooks.onShowTemplatePicker("insert", {
|
||||
noteId: e.editor._item.id,
|
||||
lineIndex: currentLine,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "splitter",
|
||||
},
|
||||
{
|
||||
id: makeId("settings-copyLink"),
|
||||
text: getString("editor.toolbar.settings.copyLink", {
|
||||
args: {
|
||||
line: currentLine,
|
||||
},
|
||||
}),
|
||||
callback: (e) => {
|
||||
const link =
|
||||
getNoteLink(e.editor._item, {
|
||||
lineIndex: currentLine,
|
||||
}) || "";
|
||||
new ztoolkit.Clipboard()
|
||||
.addText(link, "text/unicode")
|
||||
.addText(
|
||||
`<a href="${link}">${
|
||||
e.editor._item.getNoteTitle().trim() || link
|
||||
}</a>`,
|
||||
"text/html",
|
||||
)
|
||||
.copy();
|
||||
showHint(`Link ${link} copied`);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: makeId("settings-copyLinkAtSection"),
|
||||
text: getString("editor.toolbar.settings.copyLinkAtSection", {
|
||||
args: {
|
||||
section: currentSection,
|
||||
},
|
||||
}),
|
||||
callback: (e) => {
|
||||
const link =
|
||||
getNoteLink(e.editor._item, {
|
||||
sectionName: currentSection,
|
||||
}) || "";
|
||||
new ztoolkit.Clipboard()
|
||||
.addText(link, "text/unicode")
|
||||
.addText(
|
||||
`<a href="${link}#${currentSection}">${
|
||||
e.editor._item.getNoteTitle().trim() || link
|
||||
}</a>`,
|
||||
"text/html",
|
||||
)
|
||||
.copy();
|
||||
showHint(`Link ${link} copied`);
|
||||
},
|
||||
},
|
||||
]),
|
||||
);
|
||||
}
|
||||
|
||||
const parentAttachment = await noteItem.parentItem?.getBestAttachment();
|
||||
if (parentAttachment) {
|
||||
settingsMenuData.push({
|
||||
type: "splitter",
|
||||
});
|
||||
settingsMenuData.push({
|
||||
id: makeId("settings-openParent"),
|
||||
text: getString("editor.toolbar.settings.openParent"),
|
||||
callback: (e) => {
|
||||
ZoteroPane.viewAttachment([parentAttachment.id]);
|
||||
Zotero.Notifier.trigger("open", "file", parentAttachment.id);
|
||||
},
|
||||
});
|
||||
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)) {
|
||||
|
|
|
|||
|
|
@ -7,7 +7,8 @@ export function registerNoteLinkProxyHandler() {
|
|||
const linkParams = getNoteLinkParams(uri.spec);
|
||||
if (linkParams.noteItem) {
|
||||
addon.hooks.onOpenNote(linkParams.noteItem.id, "auto", {
|
||||
lineIndex: linkParams.lineIndex || undefined,
|
||||
lineIndex: linkParams.lineIndex,
|
||||
sectionName: linkParams.sectionName,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { config } from "../../../package.json";
|
||||
import { ICONS } from "../../utils/config";
|
||||
import { scroll } from "../../utils/editor";
|
||||
import { getString } from "../../utils/locale";
|
||||
import { getNoteTreeFlattened } from "../../utils/note";
|
||||
import { getPref } from "../../utils/prefs";
|
||||
|
|
@ -227,6 +226,7 @@ export async function initWorkspaceEditor(
|
|||
noteId: number,
|
||||
options: {
|
||||
lineIndex?: number;
|
||||
sectionName?: string;
|
||||
} = {},
|
||||
) {
|
||||
const noteItem = Zotero.Items.get(noteId);
|
||||
|
|
@ -252,7 +252,13 @@ export async function initWorkspaceEditor(
|
|||
await waitUtilAsync(() => Boolean(editorElem._editorInstance));
|
||||
await editorElem._editorInstance._initPromise;
|
||||
if (typeof options.lineIndex === "number") {
|
||||
scroll(editorElem._editorInstance, options.lineIndex);
|
||||
addon.api.editor.scroll(editorElem._editorInstance, options.lineIndex);
|
||||
}
|
||||
if (typeof options.sectionName === "string") {
|
||||
addon.api.editor.scrollToSection(
|
||||
editorElem._editorInstance,
|
||||
options.sectionName,
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import {
|
||||
getEditorInstance,
|
||||
moveHeading,
|
||||
scroll,
|
||||
updateHeadingTextAtLine,
|
||||
} from "../../utils/editor";
|
||||
import { showHintWithLink } from "../../utils/hint";
|
||||
|
|
@ -19,7 +18,7 @@ export async function messageHandler(ev: MessageEvent) {
|
|||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
scroll(editor, ev.data.lineIndex);
|
||||
addon.api.editor.scroll(editor, ev.data.lineIndex);
|
||||
return;
|
||||
}
|
||||
case "openNote": {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import TreeModel = require("tree-model");
|
||||
import { TextSelection } from "prosemirror-state";
|
||||
import { getNoteTreeFlattened } from "./note";
|
||||
|
||||
export {
|
||||
insert,
|
||||
|
|
@ -7,12 +8,14 @@ export {
|
|||
move,
|
||||
replace,
|
||||
scroll,
|
||||
scrollToSection,
|
||||
getEditorInstance,
|
||||
moveHeading,
|
||||
updateHeadingTextAtLine,
|
||||
getEditorCore,
|
||||
getRangeAtCursor,
|
||||
getLineAtCursor,
|
||||
getSectionAtCursor,
|
||||
getPositionAtLine,
|
||||
getPositionAtCursor,
|
||||
getURLAtCursor,
|
||||
|
|
@ -130,6 +133,16 @@ function scroll(editor: Zotero.EditorInstance, lineIndex: number) {
|
|||
core.view.dom.parentElement?.scrollTo(0, offset);
|
||||
}
|
||||
|
||||
function scrollToSection(editor: Zotero.EditorInstance, sectionName: string) {
|
||||
const item = editor._item;
|
||||
const sectionTree = getNoteTreeFlattened(item);
|
||||
const sectionNode = sectionTree.find(
|
||||
(node) => node.model.name.trim() === sectionName.trim(),
|
||||
);
|
||||
if (!sectionNode) return;
|
||||
scroll(editor, sectionNode.model.lineIndex);
|
||||
}
|
||||
|
||||
function getEditorInstance(noteId: number) {
|
||||
const editor = Zotero.Notes._editorInstances.find(
|
||||
(e) =>
|
||||
|
|
@ -150,7 +163,11 @@ function getEditorAPI(editor: Zotero.EditorInstance) {
|
|||
|
||||
function getPositionAtCursor(editor: Zotero.EditorInstance) {
|
||||
const selection = getEditorCore(editor).view.state.selection;
|
||||
return selection.$anchor.after(selection.$anchor.depth);
|
||||
try {
|
||||
return selection.$anchor.after(selection.$anchor.depth);
|
||||
} catch (e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
function getRangeAtCursor(editor: Zotero.EditorInstance) {
|
||||
|
|
@ -163,6 +180,9 @@ function getRangeAtCursor(editor: Zotero.EditorInstance) {
|
|||
|
||||
function getLineAtCursor(editor: Zotero.EditorInstance) {
|
||||
const position = getPositionAtCursor(editor);
|
||||
if (position < 0) {
|
||||
return -1;
|
||||
}
|
||||
const lastPos = getEditorCore(editor).view.state.tr.doc.content.size;
|
||||
let i = 0;
|
||||
let currentPos = getPositionAtLine(editor, 0);
|
||||
|
|
@ -176,6 +196,27 @@ function getLineAtCursor(editor: Zotero.EditorInstance) {
|
|||
return i;
|
||||
}
|
||||
|
||||
function getSectionAtCursor(editor: Zotero.EditorInstance): string | undefined {
|
||||
const lineIndex = getLineAtCursor(editor);
|
||||
if (lineIndex < 0) return undefined;
|
||||
const item = editor._item;
|
||||
const sectionTree = getNoteTreeFlattened(item);
|
||||
let sectionNode;
|
||||
for (let i = 0; i < sectionTree.length; i++) {
|
||||
if (
|
||||
// Is before cursor
|
||||
sectionTree[i].model.lineIndex <= lineIndex &&
|
||||
// Is last node, or next node is after cursor
|
||||
(i === sectionTree.length - 1 ||
|
||||
sectionTree[i + 1].model.lineIndex > lineIndex)
|
||||
) {
|
||||
sectionNode = sectionTree[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sectionNode?.model.name;
|
||||
}
|
||||
|
||||
function getDOMAtLine(
|
||||
editor: Zotero.EditorInstance,
|
||||
lineIndex: number,
|
||||
|
|
|
|||
|
|
@ -18,8 +18,9 @@ export function getNoteLinkParams(link: string) {
|
|||
noteItem: Zotero.Items.getByLibraryAndKey(libraryID, noteKey || "") as
|
||||
| Zotero.Item
|
||||
| false,
|
||||
ignore: Boolean(url.searchParams.get("ignore")),
|
||||
lineIndex: typeof line === "string" ? parseInt(line) : null,
|
||||
ignore: Boolean(url.searchParams.get("ignore")) || undefined,
|
||||
lineIndex: typeof line === "string" ? parseInt(line) : undefined,
|
||||
sectionName: url.searchParams.get("section") || undefined,
|
||||
};
|
||||
} catch (e: unknown) {
|
||||
return {
|
||||
|
|
@ -27,8 +28,9 @@ export function getNoteLinkParams(link: string) {
|
|||
libraryID: -1,
|
||||
noteKey: undefined,
|
||||
noteItem: false as const,
|
||||
ignore: null,
|
||||
lineIndex: null,
|
||||
ignore: undefined,
|
||||
lineIndex: undefined,
|
||||
sectionName: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -36,8 +38,9 @@ export function getNoteLinkParams(link: string) {
|
|||
export function getNoteLink(
|
||||
noteItem: Zotero.Item,
|
||||
options: {
|
||||
ignore?: boolean | null;
|
||||
lineIndex?: number | null;
|
||||
ignore?: boolean;
|
||||
lineIndex?: number;
|
||||
sectionName?: string;
|
||||
} = {},
|
||||
) {
|
||||
const libraryID = noteItem.libraryID;
|
||||
|
|
@ -71,6 +74,12 @@ export function getNoteLink(
|
|||
if (options.lineIndex) {
|
||||
link = addParam(link, `line=${options.lineIndex}`);
|
||||
}
|
||||
if (options.sectionName) {
|
||||
link = addParam(
|
||||
link,
|
||||
`section=${encodeURIComponent(options.sectionName)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return link;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue