From 144838aa89e5f153067d2f070f203e434842bcb8 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Mon, 27 Apr 2026 04:51:18 +0100 Subject: [PATCH] feat(editor-ext): block accidental delete of selected base embed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add Backspace/Delete keyboard shortcuts on the BaseEmbed node that return true (handled, no-op) when the current selection is a NodeSelection of this node — the 'click the embed and hit delete' accidental path. Other deletion paths (range selections covering the node, programmatic transactions, sync) still go through normally. --- .../src/lib/base-embed/base-embed.ts | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/packages/editor-ext/src/lib/base-embed/base-embed.ts b/packages/editor-ext/src/lib/base-embed/base-embed.ts index 50cd0269a..90cd7584c 100644 --- a/packages/editor-ext/src/lib/base-embed/base-embed.ts +++ b/packages/editor-ext/src/lib/base-embed/base-embed.ts @@ -1,4 +1,5 @@ import { Node, mergeAttributes } from '@tiptap/core'; +import { NodeSelection } from '@tiptap/pm/state'; export interface BaseEmbedOptions { HTMLAttributes: Record; @@ -58,4 +59,24 @@ export const BaseEmbed = Node.create({ }), }; }, + + addKeyboardShortcuts() { + // Block Backspace / Delete when the base embed itself is the + // current selection — that's the "click on the embed and hit + // delete" accidental-delete path. Returning true tells TipTap + // we've handled the key, preventing the default removal. + // Other deletion paths (range selections covering the node, + // programmatic transactions) still go through. + const isThisNodeSelected = (): boolean => { + const { selection } = this.editor.state; + return ( + selection instanceof NodeSelection && + selection.node.type.name === this.name + ); + }; + return { + Backspace: () => isThisNodeSelected(), + Delete: () => isThisNodeSelected(), + }; + }, });