add: hover to show note link preview

This commit is contained in:
windingwind 2024-10-11 11:49:54 +02:00
parent ef1bbe1d73
commit 13e172a96c
14 changed files with 156 additions and 26 deletions

View File

@ -1,3 +1,3 @@
{ {
"typescript.tsdk": "node_modules/typescript/lib" "typescript.tsdk": "node_modules/typescript/lib"
} }

View File

@ -38,11 +38,22 @@
native="true" native="true"
preference="__prefsPrefix__.workspace.outline.keepLinks" preference="__prefsPrefix__.workspace.outline.keepLinks"
/> />
<checkbox <hbox align="center">
data-l10n-id="editor-noteLinkPreview" <html:label
native="true" data-l10n-id="editor-noteLinkPreviewType"
preference="__prefsPrefix__.editor.noteLinkPreview" for="__addonRef__-editor-noteLinkPreviewType"
/> ></html:label>
<radiogroup
id="__addonRef__-editor-noteLinkPreviewType"
preference="__prefsPrefix__.editor.noteLinkPreviewType"
orient="horizontal"
oncommand="Zotero.__addonInstance__.api.utils.requireRestart();"
>
<radio data-l10n-id="editor-noteLinkPreview-hover" value="hover" />
<radio data-l10n-id="editor-noteLinkPreview-ctrl" value="ctrl" />
<radio data-l10n-id="editor-noteLinkPreview-disable" value="disable" />
</radiogroup>
</hbox>
</groupbox> </groupbox>
<groupbox> <groupbox>
<label><html:h2 data-l10n-id="sync-title"></html:h2></label> <label><html:h2 data-l10n-id="sync-title"></html:h2></label>
@ -59,6 +70,7 @@
placeholder="-1 for disable" placeholder="-1 for disable"
id="__addonRef__-sync-period" id="__addonRef__-sync-period"
preference="__prefsPrefix__.syncPeriodSeconds" preference="__prefsPrefix__.syncPeriodSeconds"
onchange="Zotero.__addonInstance__.api.utils.requireRestart();"
></html:input> ></html:input>
</hbox> </hbox>
<hbox align="center"> <hbox align="center">

View File

@ -153,13 +153,25 @@
color: var(--fill-primary); color: var(--fill-primary);
} }
.snippet.syntax { .snippet.syntax {
background-color: color-mix(in srgb, var(--accent-yellow) 50%, transparent 50%); background-color: color-mix(
in srgb,
var(--accent-yellow) 50%,
transparent 50%
);
} }
.snippet.expression { .snippet.expression {
background-color: color-mix(in srgb, var(--accent-green) 50%, transparent 50%); background-color: color-mix(
in srgb,
var(--accent-green) 50%,
transparent 50%
);
} }
.snippet.variable { .snippet.variable {
background-color: color-mix(in srgb, var(--accent-azure) 50%, transparent 50%); background-color: color-mix(
in srgb,
var(--accent-azure) 50%,
transparent 50%
);
} }
.snippet:hover { .snippet:hover {
background-color: var(--fill-quinary); background-color: var(--fill-quinary);

View File

@ -8,8 +8,16 @@ editor-title = Note Editor
editor-expandLevel-label = Outline expand to heading level editor-expandLevel-label = Outline expand to heading level
editor-keepLinks = editor-keepLinks =
.label = Show note links in outline .label = Show note links in outline
editor-noteLinkPreview = editor-noteLinkPreviewType = Show preview for note link when:
.label = Show note link preview on hover editor-noteLinkPreview-hover =
.label = Hover
editor-noteLinkPreview-ctrl =
.label = Press { PLATFORM() ->
[macos] ⌘
*[other] Ctrl
}
editor-noteLinkPreview-disable =
.label = Never
sync-title = Sync sync-title = Sync
sync-period-label = Auto-sync period (seconds) sync-period-label = Auto-sync period (seconds)

View File

@ -8,8 +8,16 @@ editor-title = Editor delle note
editor-expandLevel-label = Espansione dello schema al livello delle intestazioni editor-expandLevel-label = Espansione dello schema al livello delle intestazioni
editor-keepLinks = editor-keepLinks =
.label = Mostra i collegamenti delle note nello schema .label = Mostra i collegamenti delle note nello schema
editor-noteLinkPreview = editor-noteLinkPreviewType = Show preview for note link when:
.label = Mostra l'anteprima del link alla nota al passaggio del mouse editor-noteLinkPreview-hover =
.label = Hover
editor-noteLinkPreview-ctrl =
.label = Press { PLATFORM() ->
[macos] ⌘
*[other] Ctrl
}
editor-noteLinkPreview-disable =
.label = Never
sync-title = Sincronizzazione sync-title = Sincronizzazione
sync-period-label = Intervallo della sincronizzazione automatica (secondi) sync-period-label = Intervallo della sincronizzazione automatica (secondi)

View File

@ -8,8 +8,16 @@ editor-title = Note Editor
editor-expandLevel-label = Outline расширить до уровня заголовка editor-expandLevel-label = Outline расширить до уровня заголовка
editor-keepLinks = editor-keepLinks =
.label = Сохранить ссылки .label = Сохранить ссылки
editor-noteLinkPreview = editor-noteLinkPreviewType = Show preview for note link when:
.label = Show note link preview on hover editor-noteLinkPreview-hover =
.label = Hover
editor-noteLinkPreview-ctrl =
.label = Press { PLATFORM() ->
[macos] ⌘
*[other] Ctrl
}
editor-noteLinkPreview-disable =
.label = Never
sync-title = Синк sync-title = Синк
sync-period-label = Авто-синк период (сек) sync-period-label = Авто-синк период (сек)

View File

@ -8,8 +8,16 @@ editor-title = Not Düzenleyici
editor-expandLevel-label = Anahatta gösterilecek başlık düzeyleri editor-expandLevel-label = Anahatta gösterilecek başlık düzeyleri
editor-keepLinks = editor-keepLinks =
.label = Not bağlantılarını anahatta göster .label = Not bağlantılarını anahatta göster
editor-noteLinkPreview = editor-noteLinkPreviewType = Show preview for note link when:
.label = İmleci bağlantının üzerine getirdiğinde ön izlemeyi göster editor-noteLinkPreview-hover =
.label = Hover
editor-noteLinkPreview-ctrl =
.label = Press { PLATFORM() ->
[macos] ⌘
*[other] Ctrl
}
editor-noteLinkPreview-disable =
.label = Never
sync-title = Eşitle sync-title = Eşitle
sync-period-label = Otomatik Eşitleme Sıklığı (saniye) sync-period-label = Otomatik Eşitleme Sıklığı (saniye)

View File

@ -8,8 +8,16 @@ editor-title = 笔记编辑器
editor-expandLevel-label = 大纲展开至标题层级 editor-expandLevel-label = 大纲展开至标题层级
editor-keepLinks = editor-keepLinks =
.label = 在大纲中显示笔记链接 .label = 在大纲中显示笔记链接
editor-noteLinkPreview = editor-noteLinkPreviewType = 笔记链接预览触发方式:
.label = 鼠标悬停时显示笔记链接预览 editor-noteLinkPreview-hover =
.label = 鼠标悬停
editor-noteLinkPreview-ctrl =
.label = 按下 { PLATFORM() ->
[macos] ⌘
*[other] Ctrl
}
editor-noteLinkPreview-disable =
.label = 从不
sync-title = 同步 sync-title = 同步
sync-period-label = 自动同步周期 (秒) sync-period-label = 自动同步周期 (秒)

View File

@ -21,7 +21,7 @@ pref("__prefsPrefix__.exportNote", false);
pref("__prefsPrefix__.workspace.outline.expandLevel", 2); pref("__prefsPrefix__.workspace.outline.expandLevel", 2);
pref("__prefsPrefix__.workspace.outline.keepLinks", true); pref("__prefsPrefix__.workspace.outline.keepLinks", true);
pref("__prefsPrefix__.editor.noteLinkPreview", true); pref("__prefsPrefix__.editor.noteLinkPreviewType", "hover");
pref("__prefsPrefix__.openNote.takeover", true); pref("__prefsPrefix__.openNote.takeover", true);
pref("__prefsPrefix__.openNote.defaultAsWindow", false); pref("__prefsPrefix__.openNote.defaultAsWindow", false);

View File

@ -82,6 +82,7 @@ import {
} from "./utils/relation"; } from "./utils/relation";
import { getWorkspaceByTabID, getWorkspaceByUID } from "./utils/workspace"; import { getWorkspaceByTabID, getWorkspaceByUID } from "./utils/workspace";
import { getString } from "./utils/locale"; import { getString } from "./utils/locale";
import { showRestartHint } from "./utils/hint";
const workspace = { const workspace = {
getWorkspaceByTabID, getWorkspaceByTabID,
@ -181,6 +182,7 @@ const relation = {
const utils = { const utils = {
getString, getString,
requireRestart: showRestartHint,
}; };
export default { export default {

View File

@ -14,6 +14,8 @@ interface LinkPreviewOptions {
) => void; ) => void;
openURL: (url: string) => void; openURL: (url: string) => void;
requireCtrl: boolean;
} }
class LinkPreviewState { class LinkPreviewState {
@ -67,7 +69,7 @@ class LinkPreviewState {
this.node = target; this.node = target;
this.currentLink = href; this.currentLink = href;
this.hasHover = true; this.hasHover = true;
this.tryOpenPopup(); this.tryOpenPopupByHover();
} }
} }
} }
@ -79,7 +81,24 @@ class LinkPreviewState {
} }
}; };
tryOpenPopup() { handleKeydown = async (event: KeyboardEvent) => {
if (!this.options.requireCtrl) {
return;
}
if (!this.hasHover || !this.currentLink) {
return;
}
const isMac =
typeof navigator != "undefined" ? /Mac/.test(navigator.platform) : false;
if ((isMac && event.metaKey) || (!isMac && event.ctrlKey)) {
this.tryTogglePopupByKey();
}
};
tryOpenPopupByHover() {
if (this.options.requireCtrl) {
return;
}
const href = this.currentLink!; const href = this.currentLink!;
setTimeout(() => { setTimeout(() => {
if (this.currentLink === href) { if (this.currentLink === href) {
@ -88,6 +107,14 @@ class LinkPreviewState {
}, 300); }, 300);
} }
tryTogglePopupByKey() {
if (this._hasPopup()) {
this._closePopup();
} else {
this._openPopup();
}
}
_openPopup() { _openPopup() {
console.log("Enter Link Preview", this.currentLink, this.options); console.log("Enter Link Preview", this.currentLink, this.options);
document.querySelectorAll(".link-preview").forEach((el) => el.remove()); document.querySelectorAll(".link-preview").forEach((el) => el.remove());
@ -158,6 +185,10 @@ class LinkPreviewState {
document.querySelectorAll(".link-preview").forEach((el) => el.remove()); document.querySelectorAll(".link-preview").forEach((el) => el.remove());
this.popup = null; this.popup = null;
} }
_hasPopup() {
return !!document.querySelector(".link-preview");
}
} }
function initLinkPreviewPlugin(options: LinkPreviewOptions) { function initLinkPreviewPlugin(options: LinkPreviewOptions) {
@ -185,6 +216,10 @@ function initLinkPreviewPlugin(options: LinkPreviewOptions) {
pluginState.update(view.state); pluginState.update(view.state);
pluginState.handleMouseMove(event); pluginState.handleMouseMove(event);
}, },
keydown: (view, event) => {
const pluginState = key.getState(view.state) as LinkPreviewState;
pluginState.handleKeydown(event);
},
wheel: (view, event) => { wheel: (view, event) => {
const pluginState = key.getState(view.state) as LinkPreviewState; const pluginState = key.getState(view.state) as LinkPreviewState;
pluginState.popup?.layoutPopup(pluginState); pluginState.popup?.layoutPopup(pluginState);

View File

@ -30,7 +30,11 @@ import { showSyncDiff } from "./modules/sync/diffWindow";
import { showSyncInfo } from "./modules/sync/infoWindow"; import { showSyncInfo } from "./modules/sync/infoWindow";
import { showSyncManager } from "./modules/sync/managerWindow"; import { showSyncManager } from "./modules/sync/managerWindow";
import { showTemplateEditor } from "./modules/template/editorWindow"; import { showTemplateEditor } from "./modules/template/editorWindow";
import { createNoteFromTemplate, createNoteFromMD, createNote } from "./modules/createNote"; import {
createNoteFromTemplate,
createNoteFromMD,
createNote,
} from "./modules/createNote";
import { createZToolkit } from "./utils/ztoolkit"; import { createZToolkit } from "./utils/ztoolkit";
import { waitUtilAsync } from "./utils/wait"; import { waitUtilAsync } from "./utils/wait";
import { initSyncList } from "./modules/sync/api"; import { initSyncList } from "./modules/sync/api";

View File

@ -442,7 +442,8 @@ function getTextBetweenLines(
} }
function initLinkPreview(editor: Zotero.EditorInstance) { function initLinkPreview(editor: Zotero.EditorInstance) {
if (!getPref("editor.noteLinkPreview")) { const previewType = getPref("editor.noteLinkPreviewType") as string;
if (!["hover", "ctrl"].includes(previewType)) {
return; return;
} }
const EditorAPI = getEditorAPI(editor); const EditorAPI = getEditorAPI(editor);
@ -469,6 +470,7 @@ function initLinkPreview(editor: Zotero.EditorInstance) {
openURL: (url: string) => { openURL: (url: string) => {
Zotero.getActiveZoteroPane().loadURI(url); Zotero.getActiveZoteroPane().loadURI(url);
}, },
requireCtrl: previewType === "ctrl",
}, },
editor._iframeWindow, editor._iframeWindow,
{ wrapReflectors: true, cloneFunctions: true }, { wrapReflectors: true, cloneFunctions: true },

View File

@ -42,4 +42,27 @@ async function showHintWithLink(
return progress; return progress;
} }
export { showHint, showHintWithLink }; function showRestartHint() {
const ps = Services.prompt;
const buttonFlags =
ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING +
ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
const index = ps.confirmEx(
// @ts-ignore
null,
Zotero.getString("general.restartRequired"),
Zotero.getString("general.restartRequiredForChange", Zotero.appName),
buttonFlags,
Zotero.getString("general.restartNow"),
Zotero.getString("general.restartLater"),
null,
null,
{},
);
if (index == 0) {
Zotero.Utilities.Internal.quit(true);
}
}
export { showHint, showHintWithLink, showRestartHint };