import { Node } from "@tiptap/core"; import { ReactNodeViewRenderer, type ReactNodeViewProps } from "@tiptap/react"; import { SCENE_BREAK } from "../constants.js"; /** * Scene break node view — renders a centered `• • •` divider. Clicking the * divider re-renders it; the underlying node is an empty block whose HTML * representation is `
`. */ export const SceneBreakNode = Node.create({ name: SCENE_BREAK, group: "block", selectable: true, draggable: true, atom: true, addAttributes() { return { break: { default: "***", parseHTML: (element) => element.getAttribute("data-break") ?? "***", renderHTML: (attributes) => ({ "data-break": attributes.break ?? "***" }), }, }; }, parseHTML() { // Higher priority than StarterKit's horizontalRule (`tag: "hr"`) so that // `
` resolves to a scene break, not a rule. return [{ tag: "hr.scene-break", priority: 2000 }]; }, renderHTML({ node }) { return ["hr", { class: "scene-break", "data-break": node.attrs.break ?? "***" }]; }, addNodeView() { return ReactNodeViewRenderer(SceneBreakComponent); }, }); /** * The React node view. TipTap's `ReactNodeViewRenderer` supplies the * contentDOM/selection handling; we just render the divider. */ export function SceneBreakComponent({ editor, node, selected }: ReactNodeViewProps) { return (
); } export default SceneBreakNode;