diff --git a/.changeset/modern-kangaroos-teach.md b/.changeset/modern-kangaroos-teach.md new file mode 100644 index 000000000..98151e69d --- /dev/null +++ b/.changeset/modern-kangaroos-teach.md @@ -0,0 +1,5 @@ +--- +'@tiptap/extension-horizontal-rule': patch +--- + +Fixed a bug that caused the horizontal rule's `setHorizontalRule` command to always return true when checked via `can()` diff --git a/.changeset/silver-roses-joke.md b/.changeset/silver-roses-joke.md new file mode 100644 index 000000000..695ef7a44 --- /dev/null +++ b/.changeset/silver-roses-joke.md @@ -0,0 +1,5 @@ +--- +'@tiptap/core': patch +--- + +Added new `canInsertNode` utility that checks if a node can be inserted into a specific position diff --git a/packages/core/src/utilities/canInsertNode.ts b/packages/core/src/utilities/canInsertNode.ts new file mode 100644 index 000000000..b797db0d5 --- /dev/null +++ b/packages/core/src/utilities/canInsertNode.ts @@ -0,0 +1,31 @@ +import type { NodeType } from '@tiptap/pm/model' +import { type EditorState, NodeSelection } from '@tiptap/pm/state' + +export function canInsertNode(state: EditorState, nodeType: NodeType): boolean { + const { selection } = state + const { $from } = selection + + // Special handling for NodeSelection + if (selection instanceof NodeSelection) { + const index = $from.index() + const parent = $from.parent + + // Can we replace the selected node with the horizontal rule? + return parent.canReplaceWith(index, index + 1, nodeType) + } + + // Default: check if we can insert at the current position + let depth = $from.depth + + while (depth >= 0) { + const index = $from.index(depth) + const parent = $from.node(depth) + const match = parent.contentMatchAt(index) + + if (match.matchType(nodeType)) { + return true + } + depth -= 1 + } + return false +} diff --git a/packages/core/src/utilities/index.ts b/packages/core/src/utilities/index.ts index 36e905372..74c824429 100644 --- a/packages/core/src/utilities/index.ts +++ b/packages/core/src/utilities/index.ts @@ -1,4 +1,5 @@ export * from './callOrReturn.js' +export * from './canInsertNode.js' export * from './createStyleTag.js' export * from './deleteProps.js' export * from './elementFromString.js' diff --git a/packages/extension-horizontal-rule/src/horizontal-rule.ts b/packages/extension-horizontal-rule/src/horizontal-rule.ts index ae1d56386..a55838166 100644 --- a/packages/extension-horizontal-rule/src/horizontal-rule.ts +++ b/packages/extension-horizontal-rule/src/horizontal-rule.ts @@ -1,5 +1,5 @@ import { - isNodeSelection, mergeAttributes, Node, nodeInputRule, + canInsertNode, isNodeSelection, mergeAttributes, Node, nodeInputRule, } from '@tiptap/core' import { NodeSelection, TextSelection } from '@tiptap/pm/state' @@ -51,6 +51,11 @@ export const HorizontalRule = Node.create({ return { setHorizontalRule: () => ({ chain, state }) => { + // Check if we can insert the node at the current selection + if (!canInsertNode(state, state.schema.nodes[this.name])) { + return false + } + const { selection } = state const { $from: $originFrom, $to: $originTo } = selection