From 7a00947d09a12af1cab8583ecfb23465c8bb55aa Mon Sep 17 00:00:00 2001 From: mdzz2048 <2434206717@qq.com> Date: Sat, 16 May 2026 16:10:39 -0300 Subject: [PATCH 1/2] feat: :sparkles: Support configuring the way to open assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加本地配置 https://github.com/siyuan-note/siyuan/issues/10641 --- app/src/editor/openLink.ts | 81 +++++++++++++++++++++++++++----------- app/src/types/config.d.ts | 19 +++++++++ app/src/types/index.d.ts | 2 +- kernel/conf/editor.go | 9 +++++ kernel/model/conf.go | 3 ++ 5 files changed, 89 insertions(+), 25 deletions(-) diff --git a/app/src/editor/openLink.ts b/app/src/editor/openLink.ts index ecf0d033b63..ea04539ada2 100644 --- a/app/src/editor/openLink.ts +++ b/app/src/editor/openLink.ts @@ -96,8 +96,34 @@ export const processSYLink = (app: App, url: string) => { return false; }; +const resolveOpenAction = (ctrlIsPressed: boolean, event?: MouseEvent): Config.TLinkOpenAction => { + const config = window.siyuan.config.editor.openLink; + if (event && event.altKey) { + return config.altClick; + } + if (event && event.shiftKey) { + return config.shiftClick; + } + if (ctrlIsPressed) { + return config.ctrlClick; + } + return config.click; +}; + export const openLink = (protyle: IProtyle, aLink: string, event?: MouseEvent, ctrlIsPressed = false) => { let linkAddress = Lute.UnEscapeHTMLStr(aLink); + const action = resolveOpenAction(ctrlIsPressed, event); + + let preventDefaultCalled = false; + for (const plugin of protyle.app.plugins) { + if (!plugin.eventBus.emit("open-link", {url: linkAddress, action: action})) { + preventDefaultCalled = true; + } + } + if (preventDefaultCalled) { + return; + } + let pdfParams; if (isLocalPath(linkAddress) && !linkAddress.startsWith("file://") && linkAddress.indexOf(".pdf") > -1) { const pdfAddress = linkAddress.split("/"); @@ -120,33 +146,40 @@ export const openLink = (protyle: IProtyle, aLink: string, event?: MouseEvent, c (linkAddress.endsWith(".pdf") && linkAddress.startsWith("assets/")) ) ) { - if (event && event.altKey) { - openAsset(protyle.app, linkAddress, pdfParams); - } else if (event && event.shiftKey) { - /// #if !BROWSER - openBy(linkAddress, "app"); - /// #else - openByMobile(linkAddress); - /// #endif - } else if (ctrlIsPressed) { - /// #if !BROWSER - openBy(linkAddress, "folder"); - /// #else - openByMobile(linkAddress); - /// #endif - } else { - openAsset(protyle.app, linkAddress, pdfParams, !window.siyuan.config.fileTree.noSplitScreenWhenOpenTab ? "right" : null); + switch (action) { + case "current-tab": + openAsset(protyle.app, linkAddress, pdfParams); + break; + case "right-tab": + const isOpenRight = !window.siyuan.config.fileTree.noSplitScreenWhenOpenTab ? "right" : null + openAsset(protyle.app, linkAddress, pdfParams, isOpenRight); + break; + case "open-app": + /// #if !BROWSER + openBy(linkAddress, "app"); + break; + case "show-folder": + /// #if !BROWSER + openBy(linkAddress, "folder"); + break; + default: + openByMobile(linkAddress); + break; } } else { - /// #if !BROWSER - if (ctrlIsPressed) { - openBy(linkAddress, "folder"); - } else { - openBy(linkAddress, "app"); + switch (action) { + case "open-app": + /// #if !BROWSER + openBy(linkAddress, "app"); + break; + case "show-folder": + /// #if !BROWSER + openBy(linkAddress, "folder"); + break; + default: + openByMobile(linkAddress); + break; } - /// #else - openByMobile(linkAddress); - /// #endif } } else if (linkAddress) { if (0 > linkAddress.indexOf(":")) { diff --git a/app/src/types/config.d.ts b/app/src/types/config.d.ts index 60dee806491..eb23062c5b7 100644 --- a/app/src/types/config.d.ts +++ b/app/src/types/config.d.ts @@ -355,6 +355,21 @@ declare namespace Config { inlineMark: boolean; } + /** + * Link open action type + */ + type TLinkOpenAction = "current-tab" | "right-tab" | "open-app" | "show-folder"; + + /** + * Link open behavior configuration + */ + interface IOpenLink { + click: TLinkOpenAction; + ctrlClick: TLinkOpenAction; + altClick: TLinkOpenAction; + shiftClick: TLinkOpenAction; + } + /** * SiYuan editor related configuration */ @@ -504,6 +519,10 @@ declare namespace Config { * Whether to enable the `[[` symbol to search only for document blocks */ onlySearchForDoc: boolean; + /** + * Link open behavior configuration + */ + openLink: IOpenLink; /** * PlantUML rendering service address */ diff --git a/app/src/types/index.d.ts b/app/src/types/index.d.ts index dfe21280955..5767bdbefa4 100644 --- a/app/src/types/index.d.ts +++ b/app/src/types/index.d.ts @@ -82,7 +82,7 @@ type TEventBus = "ws-main" | "sync-start" | "sync-end" | "sync-fail" | "open-noneditableblock" | "open-menu-blockref" | "open-menu-fileannotationref" | "open-menu-tag" | "open-menu-link" | "open-menu-image" | "open-menu-av" | "open-menu-content" | "open-menu-breadcrumbmore" | "open-menu-doctree" | "open-menu-inbox" | - "open-siyuan-url-plugin" | "open-siyuan-url-block" | "opened-notebook" | + "open-siyuan-url-plugin" | "open-siyuan-url-block" | "open-link" | "opened-notebook" | "closed-notebook" | "paste" | "input-search" | diff --git a/kernel/conf/editor.go b/kernel/conf/editor.go index 0c32a78e65f..b8a94609e80 100644 --- a/kernel/conf/editor.go +++ b/kernel/conf/editor.go @@ -18,6 +18,13 @@ package conf import "github.com/siyuan-note/siyuan/kernel/util" +type OpenLink struct { + Click string `json:"click"` // 普通点击链接打开方式 + CtrlClick string `json:"ctrlClick"` // Ctrl+点击链接打开方式 + AltClick string `json:"altClick"` // Alt+点击链接打开方式 + ShiftClick string `json:"shiftClick"` // Shift+点击链接打开方式 +} + type Editor struct { AllowSVGScript bool `json:"allowSVGScript"` // 允许执行 SVG 内脚本 AllowHTMLBLockScript bool `json:"allowHTMLBLockScript"` // 允许执行 HTML 块内脚本 @@ -38,6 +45,7 @@ type Editor struct { VirtualBlockRefExclude string `json:"virtualBlockRefExclude"` // 虚拟引用关键字排除列表 VirtualBlockRefInclude string `json:"virtualBlockRefInclude"` // 虚拟引用关键字包含列表 BlockRefDynamicAnchorTextMaxLen int `json:"blockRefDynamicAnchorTextMaxLen"` // 块引动态锚文本最大长度 + OpenLink *OpenLink `json:"openLink"` // 链接打开行为配置 PlantUMLServePath string `json:"plantUMLServePath"` // PlantUML 伺服地址 FullWidth bool `json:"fullWidth"` // 是否使用最大宽度 KaTexMacros string `json:"katexMacros"` // KeTex 宏定义 @@ -82,6 +90,7 @@ func NewEditor() *Editor { Emoji: []string{}, VirtualBlockRef: false, BlockRefDynamicAnchorTextMaxLen: 96, + OpenLink: &OpenLink{Click: "right-tab", CtrlClick: "show-folder", AltClick: "current-tab", ShiftClick: "open-app"}, PlantUMLServePath: "https://www.plantuml.com/plantuml/svg/~1", FullWidth: true, KaTexMacros: "{}", diff --git a/kernel/model/conf.go b/kernel/model/conf.go index d1a3131b9bb..a8a73afafaf 100644 --- a/kernel/model/conf.go +++ b/kernel/model/conf.go @@ -290,6 +290,9 @@ func InitConf() { if 5120 < Conf.Editor.BlockRefDynamicAnchorTextMaxLen { Conf.Editor.BlockRefDynamicAnchorTextMaxLen = 5120 } + if nil == Conf.Editor.OpenLink { + Conf.Editor.OpenLink = defaultEditor.OpenLink + } if 1440 < Conf.Editor.GenerateHistoryInterval { Conf.Editor.GenerateHistoryInterval = 1440 } From ba24ab2785a7bbe0dda4c37b905abae263393e17 Mon Sep 17 00:00:00 2001 From: mdzz2048 <2434206717@qq.com> Date: Sat, 16 May 2026 16:23:24 -0300 Subject: [PATCH 2/2] fix: :bug: fix `#if without #endif` --- app/src/editor/openLink.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/app/src/editor/openLink.ts b/app/src/editor/openLink.ts index ea04539ada2..40aa3d3e885 100644 --- a/app/src/editor/openLink.ts +++ b/app/src/editor/openLink.ts @@ -157,27 +157,33 @@ export const openLink = (protyle: IProtyle, aLink: string, event?: MouseEvent, c case "open-app": /// #if !BROWSER openBy(linkAddress, "app"); + /// #else + openByMobile(linkAddress); + /// #endif break; case "show-folder": /// #if !BROWSER openBy(linkAddress, "folder"); - break; - default: + /// #else openByMobile(linkAddress); + /// #endif break; } } else { switch (action) { - case "open-app": - /// #if !BROWSER - openBy(linkAddress, "app"); - break; case "show-folder": /// #if !BROWSER openBy(linkAddress, "folder"); + /// #else + openByMobile(linkAddress); + /// #endif break; default: + /// #if !BROWSER + openBy(linkAddress, "app"); + /// #else openByMobile(linkAddress); + /// #endif break; } }