From 6ffcd55c944ba07c6a6dff33404f66d9fd44114a Mon Sep 17 00:00:00 2001 From: xfy Date: Tue, 23 Jun 2026 18:42:57 +0800 Subject: [PATCH] fix(tiptap): correct link command order + validate URL scheme in slash commands --- libs/tiptap-editor/src/slash-command.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/libs/tiptap-editor/src/slash-command.ts b/libs/tiptap-editor/src/slash-command.ts index 58c6d56..a34e21a 100644 --- a/libs/tiptap-editor/src/slash-command.ts +++ b/libs/tiptap-editor/src/slash-command.ts @@ -167,7 +167,7 @@ export const SlashCommand = Extension.create({ icon: '🖼', command: ({ editor, range }) => { const url = window.prompt('输入图片 URL') - if (url) { + if (url && isValidUrl(url)) { editor.chain().focus().deleteRange(range).setImage({ src: url }).run() } }, @@ -178,9 +178,17 @@ export const SlashCommand = Extension.create({ icon: '🔗', command: ({ editor, range }) => { const url = window.prompt('输入链接 URL') - if (url) { - editor.chain().focus().deleteRange(range).setLink({ href: url }).insertContent(url).run() - } + if (!url || !isValidUrl(url)) return + // deleteRange 后光标停在 range.to;先插入 URL 文本,再选中刚插入的范围设 link + // (setLink 需要非空选区才生效,原顺序 setLink 在空选区无效)。 + const insertFrom = range.to + editor.chain() + .focus() + .deleteRange(range) + .insertContent(url) + .setTextSelection({ from: insertFrom, to: insertFrom + url.length }) + .setLink({ href: url }) + .run() }, }, ) @@ -238,6 +246,11 @@ interface SlashPopup { destroy(): void } +/** 校验图片/链接 URL:只允许 http(s) 和 data:image。拒绝 javascript: 等。 */ +function isValidUrl(url: string): boolean { + return /^https?:\/\//i.test(url) || /^data:image\//i.test(url) +} + function createPopup(props: SuggestionProps): SlashPopup { const component = document.createElement('div') component.classList.add('slash-command')