From 598a6adc02854c7143a9695ff63c96fbcce7b4e7 Mon Sep 17 00:00:00 2001 From: Cyril Date: Wed, 15 Apr 2026 09:54:19 +0200 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=90=9B(frontend)=20sanitize=20pasted?= =?UTF-8?q?=20toolbar=20links?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Strip < and > from pasted BlockNote links to keep valid external URLs. --- CHANGELOG.md | 7 ++-- .../doc-editor/components/BlockNoteEditor.tsx | 33 +++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 102c6b6025..7e6b377a42 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,17 +11,18 @@ and this project adheres to - 🚸(frontend) redirect on current url tab after 401 #2197 - 🐛(frontend) abort check media status unmount #2194 - ✨(backend) order pinned documents by last updated at #2028 +- 🐛(frontend) sanitize pasted toolbar links #2214 ### Changed - - ♿️(frontend) structure correctly 5xx error alerts #2128 +- ♿️(frontend) structure correctly 5xx error alerts #2128 ## [v4.8.6] - 2026-04-08 ### Added -- 🚸(frontend) allow opening "@page" links with -ctrl/command/middle-mouse click #2170 +- 🚸(frontend) allow opening "@page" links with + ctrl/command/middle-mouse click #2170 - ✅ E2E - Any instance friendly #2142 ### Changed diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index f4b19a99d6..a28922bc72 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -88,6 +88,38 @@ interface BlockNoteEditorProps { provider: HocuspocusProvider; } +/** + * Strips angle brackets wrapping URLs (e.g. `` → `https://example.com`). + * BlockNote copies links in Markdown autolink format; pasting into the link + * toolbar input keeps the brackets, producing broken hrefs. + */ +const stripAngleBrackets = (text: string): string => + text.replace(/^<(.+)>$/, '$1'); + +const handlePasteUrlBrackets = (e: React.ClipboardEvent) => { + const target = e.target; + if ( + !(target instanceof HTMLInputElement) && + !(target instanceof HTMLTextAreaElement) + ) { + return; + } + const text = e.clipboardData?.getData('text/plain') ?? ''; + const cleaned = stripAngleBrackets(text.trim()); + if (cleaned === text) { + return; + } + e.preventDefault(); + // Use the native value setter (input/textarea) so React-controlled fields pick up the pasted value change. + const proto = + target instanceof HTMLInputElement + ? HTMLInputElement.prototype + : HTMLTextAreaElement.prototype; + const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set; + setter?.call(target, cleaned); + target.dispatchEvent(new Event('input', { bubbles: true })); +}; + export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { const { user } = useAuth(); const { setEditor } = useEditorStore(); @@ -267,6 +299,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { return ( Date: Wed, 15 Apr 2026 10:11:26 +0200 Subject: [PATCH 2/2] =?UTF-8?q?fixup!=20=F0=9F=90=9B(frontend)=20sanitize?= =?UTF-8?q?=20pasted=20toolbar=20links?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/doc-editor/components/BlockNoteEditor.tsx | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index a28922bc72..dcd3e69fc4 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -93,8 +93,6 @@ interface BlockNoteEditorProps { * BlockNote copies links in Markdown autolink format; pasting into the link * toolbar input keeps the brackets, producing broken hrefs. */ -const stripAngleBrackets = (text: string): string => - text.replace(/^<(.+)>$/, '$1'); const handlePasteUrlBrackets = (e: React.ClipboardEvent) => { const target = e.target; @@ -105,18 +103,14 @@ const handlePasteUrlBrackets = (e: React.ClipboardEvent) => { return; } const text = e.clipboardData?.getData('text/plain') ?? ''; - const cleaned = stripAngleBrackets(text.trim()); + const cleaned = text.replace(/^\s*<([^<>]+)>\s*$/, '$1'); if (cleaned === text) { return; } e.preventDefault(); - // Use the native value setter (input/textarea) so React-controlled fields pick up the pasted value change. - const proto = - target instanceof HTMLInputElement - ? HTMLInputElement.prototype - : HTMLTextAreaElement.prototype; - const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set; - setter?.call(target, cleaned); + const start = target.selectionStart ?? target.value.length; + const end = target.selectionEnd ?? target.value.length; + target.setRangeText(cleaned, start, end, 'end'); target.dispatchEvent(new Event('input', { bubbles: true })); };