From 7c08daffe88ccd5ab22df803efe732f746740345 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Mon, 24 Nov 2025 07:42:50 -0800 Subject: [PATCH] fix: Add editor.view null checks to prevent click errors Added defensive checks for editor.view across editor components to prevent "Cannot read properties of undefined (reading 'posAtCoords')" errors when clicking before editor is fully initialized. Fixed in: - focusEditor utility function - ComposerLinkManager dropdown handlers - bubble-menu event listeners and shouldShow - SearchAndReplace goToSelection --- .../admin/composer/ComposerLinkManager.svelte | 6 ++--- .../components/SearchAndReplace.svelte | 2 +- .../edra/headless/menus/bubble-menu.svelte | 24 ++++++++++--------- src/lib/components/edra/utils.ts | 2 +- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/lib/components/admin/composer/ComposerLinkManager.svelte b/src/lib/components/admin/composer/ComposerLinkManager.svelte index cd02487..c299faa 100644 --- a/src/lib/components/admin/composer/ComposerLinkManager.svelte +++ b/src/lib/components/admin/composer/ComposerLinkManager.svelte @@ -32,7 +32,7 @@ // URL convert handlers export function handleShowUrlConvertDropdown(pos: number, _url: string) { - if (!editor) return + if (!editor || !editor.view) return const coords = editor.view.coordsAtPos(pos) urlConvertDropdownPosition = { x: coords.left, y: coords.bottom + 5 } urlConvertPos = pos @@ -48,7 +48,7 @@ // Link context menu handlers export function handleShowLinkContextMenu(pos: number, url: string) { - if (!editor) return + if (!editor || !editor.view) return const coords = editor.view.coordsAtPos(pos) linkContextMenuPosition = { x: coords.left, y: coords.bottom + 5 } linkContextUrl = url @@ -65,7 +65,7 @@ } function handleEditLink() { - if (!editor || linkContextPos === null || !linkContextUrl) return + if (!editor || !editor.view || linkContextPos === null || !linkContextUrl) return const coords = editor.view.coordsAtPos(linkContextPos) linkEditDialogPosition = { x: coords.left, y: coords.bottom + 5 } linkEditUrl = linkContextUrl diff --git a/src/lib/components/edra/headless/components/SearchAndReplace.svelte b/src/lib/components/edra/headless/components/SearchAndReplace.svelte index 66bce00..a66be05 100644 --- a/src/lib/components/edra/headless/components/SearchAndReplace.svelte +++ b/src/lib/components/edra/headless/components/SearchAndReplace.svelte @@ -32,7 +32,7 @@ function goToSelection() { const { results, resultIndex } = editor.storage.searchAndReplace const position = results[resultIndex] - if (!position) return + if (!position || !editor.view) return editor.commands.setTextSelection(position) const { node } = editor.view.domAtPos(editor.state.selection.anchor) if (node instanceof HTMLElement) node.scrollIntoView({ behavior: 'smooth', block: 'center' }) diff --git a/src/lib/components/edra/headless/menus/bubble-menu.svelte b/src/lib/components/edra/headless/menus/bubble-menu.svelte index ceec1cd..b0a233a 100644 --- a/src/lib/components/edra/headless/menus/bubble-menu.svelte +++ b/src/lib/components/edra/headless/menus/bubble-menu.svelte @@ -15,18 +15,20 @@ let isDragging = $state(false) - editor.view.dom.addEventListener('dragstart', () => { - isDragging = true - }) + if (editor.view) { + editor.view.dom.addEventListener('dragstart', () => { + isDragging = true + }) - editor.view.dom.addEventListener('drop', () => { - isDragging = true + editor.view.dom.addEventListener('drop', () => { + isDragging = true - // Allow some time for the drop action to complete before re-enabling - setTimeout(() => { - isDragging = false - }, 100) // Adjust delay if needed - }) + // Allow some time for the drop action to complete before re-enabling + setTimeout(() => { + isDragging = false + }, 100) // Adjust delay if needed + }) + } const bubbleMenuCommands = [ ...commands['text-formatting'].commands, @@ -40,7 +42,7 @@ function shouldShow(props: ShouldShowProps) { if (!props.editor.isEditable) return false const { view, editor } = props - if (!view || editor.view.dragging) { + if (!view || !editor.view || editor.view.dragging) { return false } if (editor.isActive('link')) return false diff --git a/src/lib/components/edra/utils.ts b/src/lib/components/edra/utils.ts index 172f1a9..04ce04b 100644 --- a/src/lib/components/edra/utils.ts +++ b/src/lib/components/edra/utils.ts @@ -114,7 +114,7 @@ export function getHandlePaste(editor: Editor, maxSize: number = 2) { * @param event - Optional MouseEvent or KeyboardEvent triggering the focus */ export function focusEditor(editor: Editor | undefined, event?: MouseEvent | KeyboardEvent) { - if (!editor) return + if (!editor || !editor.view) return // Check if there is a text selection already (i.e. a non-empty selection) const selection = window.getSelection() if (selection && selection.toString().length > 0) {