@agent-native/core 0.42.0 → 0.44.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +17 -56
  2. package/dist/chat-threads/store.d.ts.map +1 -1
  3. package/dist/chat-threads/store.js +71 -10
  4. package/dist/chat-threads/store.js.map +1 -1
  5. package/dist/cli/pr-visual-recap-workflow.d.ts +1 -1
  6. package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
  7. package/dist/cli/pr-visual-recap-workflow.js +1 -1
  8. package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
  9. package/dist/cli/recap.d.ts.map +1 -1
  10. package/dist/cli/recap.js +13 -13
  11. package/dist/cli/recap.js.map +1 -1
  12. package/dist/cli/skills.d.ts +2 -6
  13. package/dist/cli/skills.d.ts.map +1 -1
  14. package/dist/cli/skills.js +21 -79
  15. package/dist/cli/skills.js.map +1 -1
  16. package/dist/client/AssistantChat.d.ts.map +1 -1
  17. package/dist/client/AssistantChat.js +76 -18
  18. package/dist/client/AssistantChat.js.map +1 -1
  19. package/dist/client/blocks/index.d.ts +9 -0
  20. package/dist/client/blocks/index.d.ts.map +1 -1
  21. package/dist/client/blocks/index.js +9 -0
  22. package/dist/client/blocks/index.js.map +1 -1
  23. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -1
  24. package/dist/client/blocks/library/AnnotatedCodeBlock.js +3 -3
  25. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -1
  26. package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -1
  27. package/dist/client/blocks/library/ApiEndpointBlock.js +1 -1
  28. package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -1
  29. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -1
  30. package/dist/client/blocks/library/DiffBlock.js +128 -19
  31. package/dist/client/blocks/library/DiffBlock.js.map +1 -1
  32. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -1
  33. package/dist/client/blocks/library/FileTreeBlock.js +31 -4
  34. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -1
  35. package/dist/client/blocks/library/JsonExplorerBlock.js +1 -1
  36. package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -1
  37. package/dist/client/blocks/library/MermaidBlock.js +1 -1
  38. package/dist/client/blocks/library/MermaidBlock.js.map +1 -1
  39. package/dist/client/blocks/library/callout.config.d.ts +29 -0
  40. package/dist/client/blocks/library/callout.config.d.ts.map +1 -0
  41. package/dist/client/blocks/library/callout.config.js +33 -0
  42. package/dist/client/blocks/library/callout.config.js.map +1 -0
  43. package/dist/client/blocks/library/callout.d.ts +20 -0
  44. package/dist/client/blocks/library/callout.d.ts.map +1 -0
  45. package/dist/client/blocks/library/callout.js +61 -0
  46. package/dist/client/blocks/library/callout.js.map +1 -0
  47. package/dist/client/blocks/library/checklist.d.ts.map +1 -1
  48. package/dist/client/blocks/library/checklist.js +3 -3
  49. package/dist/client/blocks/library/checklist.js.map +1 -1
  50. package/dist/client/blocks/library/code-tabs.js +1 -1
  51. package/dist/client/blocks/library/code-tabs.js.map +1 -1
  52. package/dist/client/blocks/library/diagram.config.d.ts +64 -0
  53. package/dist/client/blocks/library/diagram.config.d.ts.map +1 -0
  54. package/dist/client/blocks/library/diagram.config.js +111 -0
  55. package/dist/client/blocks/library/diagram.config.js.map +1 -0
  56. package/dist/client/blocks/library/diagram.d.ts +16 -0
  57. package/dist/client/blocks/library/diagram.d.ts.map +1 -0
  58. package/dist/client/blocks/library/diagram.js +261 -0
  59. package/dist/client/blocks/library/diagram.js.map +1 -0
  60. package/dist/client/blocks/library/question-form.config.d.ts +69 -0
  61. package/dist/client/blocks/library/question-form.config.d.ts.map +1 -0
  62. package/dist/client/blocks/library/question-form.config.js +58 -0
  63. package/dist/client/blocks/library/question-form.config.js.map +1 -0
  64. package/dist/client/blocks/library/question-form.d.ts +20 -0
  65. package/dist/client/blocks/library/question-form.d.ts.map +1 -0
  66. package/dist/client/blocks/library/question-form.js +286 -0
  67. package/dist/client/blocks/library/question-form.js.map +1 -0
  68. package/dist/client/blocks/library/sanitize-html.d.ts +5 -0
  69. package/dist/client/blocks/library/sanitize-html.d.ts.map +1 -0
  70. package/dist/client/blocks/library/sanitize-html.js +240 -0
  71. package/dist/client/blocks/library/sanitize-html.js.map +1 -0
  72. package/dist/client/blocks/library/server-specs.d.ts.map +1 -1
  73. package/dist/client/blocks/library/server-specs.js +49 -0
  74. package/dist/client/blocks/library/server-specs.js.map +1 -1
  75. package/dist/client/blocks/library/specs.d.ts.map +1 -1
  76. package/dist/client/blocks/library/specs.js +9 -0
  77. package/dist/client/blocks/library/specs.js.map +1 -1
  78. package/dist/client/blocks/library/tabs.d.ts.map +1 -1
  79. package/dist/client/blocks/library/tabs.js +12 -12
  80. package/dist/client/blocks/library/tabs.js.map +1 -1
  81. package/dist/client/blocks/library/wireframe-kit.d.ts +260 -0
  82. package/dist/client/blocks/library/wireframe-kit.d.ts.map +1 -0
  83. package/dist/client/blocks/library/wireframe-kit.js +920 -0
  84. package/dist/client/blocks/library/wireframe-kit.js.map +1 -0
  85. package/dist/client/blocks/library/wireframe.config.d.ts +123 -0
  86. package/dist/client/blocks/library/wireframe.config.d.ts.map +1 -0
  87. package/dist/client/blocks/library/wireframe.config.js +311 -0
  88. package/dist/client/blocks/library/wireframe.config.js.map +1 -0
  89. package/dist/client/blocks/library/wireframe.d.ts +15 -0
  90. package/dist/client/blocks/library/wireframe.d.ts.map +1 -0
  91. package/dist/client/blocks/library/wireframe.js +206 -0
  92. package/dist/client/blocks/library/wireframe.js.map +1 -0
  93. package/dist/client/blocks/mdx.d.ts.map +1 -1
  94. package/dist/client/blocks/mdx.js +11 -0
  95. package/dist/client/blocks/mdx.js.map +1 -1
  96. package/dist/client/blocks/registry.d.ts +9 -0
  97. package/dist/client/blocks/registry.d.ts.map +1 -1
  98. package/dist/client/blocks/registry.js +12 -5
  99. package/dist/client/blocks/registry.js.map +1 -1
  100. package/dist/client/blocks/server.d.ts +1 -0
  101. package/dist/client/blocks/server.d.ts.map +1 -1
  102. package/dist/client/blocks/server.js +1 -0
  103. package/dist/client/blocks/server.js.map +1 -1
  104. package/dist/client/blocks/types.d.ts +8 -0
  105. package/dist/client/blocks/types.d.ts.map +1 -1
  106. package/dist/client/blocks/types.js.map +1 -1
  107. package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -1
  108. package/dist/client/rich-markdown-editor/DragHandle.js +112 -84
  109. package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -1
  110. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -1
  111. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +1 -1
  112. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -1
  113. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +9 -1
  114. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  115. package/dist/client/rich-markdown-editor/SharedRichEditor.js +3 -1
  116. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  117. package/dist/client/rich-markdown-editor/extensions.d.ts +13 -1
  118. package/dist/client/rich-markdown-editor/extensions.d.ts.map +1 -1
  119. package/dist/client/rich-markdown-editor/extensions.js +4 -2
  120. package/dist/client/rich-markdown-editor/extensions.js.map +1 -1
  121. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  122. package/dist/client/rich-markdown-editor/useCollabReconcile.js +11 -1
  123. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  124. package/dist/server/poll.d.ts.map +1 -1
  125. package/dist/server/poll.js +30 -14
  126. package/dist/server/poll.js.map +1 -1
  127. package/dist/styles/agent-native.css +1 -0
  128. package/dist/styles/blocks.css +1388 -0
  129. package/dist/templates/default/.agents/skills/storing-data/SKILL.md +2 -0
  130. package/dist/templates/workspace-core/.agents/skills/performance/SKILL.md +141 -0
  131. package/dist/templates/workspace-core/.agents/skills/storing-data/SKILL.md +2 -0
  132. package/docs/content/plan-plugin.md +8 -8
  133. package/docs/content/pr-visual-recap.md +2 -2
  134. package/docs/content/template-plan.md +94 -17
  135. package/package.json +2 -1
  136. package/src/templates/default/.agents/skills/storing-data/SKILL.md +2 -0
  137. package/src/templates/workspace-core/.agents/skills/performance/SKILL.md +141 -0
  138. package/src/templates/workspace-core/.agents/skills/storing-data/SKILL.md +2 -0
  139. package/docs/content/visual-plans.md +0 -82
@@ -1 +1 @@
1
- {"version":3,"file":"RegistryBlockNode.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/RegistryBlockNode.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n useEffect,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n type MouseEvent as ReactMouseEvent,\n} from \"react\";\nimport { IconPencil } from \"@tabler/icons-react\";\nimport {\n Node,\n NodeViewWrapper,\n ReactNodeViewRenderer,\n mergeAttributes,\n type NodeViewProps,\n} from \"@tiptap/react\";\nimport {\n NodeSelection,\n Plugin,\n PluginKey,\n type EditorState,\n} from \"@tiptap/pm/state\";\nimport {\n blockEditSurface,\n useOptionalBlockRegistry,\n type BlockDataChangeMeta,\n type BlockRenderContext,\n} from \"../blocks/index.js\";\nimport { SchemaBlockEditor } from \"../blocks/SchemaBlockEditor.js\";\n\n/* -------------------------------------------------------------------------- */\n/* The generic registry-block side-map + Tiptap NodeView, lifted into core. */\n/* */\n/* This is the app-agnostic NodeView that renders registered block specs */\n/* inside a `SharedRichEditor` document. Hosts mount the node produced by */\n/* {@link createRegistryBlockNode} as an extra extension and wrap the editor */\n/* in a {@link RegistryBlockDataProvider}, sourcing the typed block `data` */\n/* from their own authoritative store (for example, PlanContent.blocks). The */\n/* node itself carries only lightweight identity attrs (type/id/title/summary) */\n/* plus an optional `__raw` verbatim-MDX attr for byte-stable source */\n/* round-trips; the heavy typed `data` is threaded through the side-map */\n/* context, keeping the doc small and the block data the single source of */\n/* truth. */\n/* -------------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------------- */\n/* C. Block-data side-map context */\n/* -------------------------------------------------------------------------- */\n\n/** The minimal block shape the NodeView renders through `<BlockView>`. */\nexport interface RegistryBlockSideMapBlock {\n id: string;\n title?: string;\n summary?: string;\n data: unknown;\n}\n\n/**\n * The side-map an editor host supplies so the registry NodeView can resolve a\n * block's full typed `data` (and commit edits) by its stable id, without ever\n * storing that data in the ProseMirror doc.\n */\nexport interface RegistryBlockDataValue<\n TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,\n> {\n /** Resolve a block's full record (incl. `data`) by its stable id. */\n getBlock: (blockId: string) => TBlock | undefined;\n /** Commit a new `data` value for a block (edit-mode only). */\n onBlockDataChange: (\n blockId: string,\n nextData: unknown,\n meta?: BlockDataChangeMeta,\n ) => void;\n /** Whether the document (and thus its blocks) is editable. */\n editable: boolean;\n /**\n * When true, blocks whose type has no Notion (NFM) analog are badged so the\n * author knows they won't sync. The host decides which types are incompatible\n * via {@link isNotionIncompatibleType}; this flag just toggles the badge on.\n */\n notionSync?: boolean;\n /**\n * Decide whether a block type is Notion-incompatible (no NFM analog). Only\n * consulted when {@link notionSync} is true. Injected by the host so the\n * single registry-level allowlist (plan's `isNotionCompatibleBlockType`, or\n * content's registry-derived gate) drives the badge — core stays policy-free.\n */\n isNotionIncompatibleType?: (blockType: string) => boolean;\n /**\n * Render a block whose type is NOT in the registry through the host's own\n * dispatcher (plan: `PlanBlockView` for decision / legacy visual-questions /\n * image; omitted in hosts with no legacy types), so every block type renders\n * in the document instead of a bare fallback.\n */\n renderLegacyBlock?: (\n block: TBlock,\n options: { editing: boolean },\n ) => ReactNode;\n /**\n * Optional: render a schema-driven (or otherwise custom) editor for a legacy\n * block in place of the raw-JSON fallback. Receives an `onChange` that commits\n * the block's new `data`. Return `null`/`undefined` to keep the JSON editor for\n * that block type. The returned editor is expected to autosave through\n * `onChange` (no Save button is shown), matching registered panel blocks.\n */\n renderLegacyBlockEditor?: (\n block: TBlock,\n args: { onChange: (nextData: unknown) => void },\n ) => ReactNode;\n /**\n * Return `true` for legacy block types that render their OWN edit affordance\n * inside `renderLegacyBlock` (e.g. an image block with its own hover toolbar).\n * For those types the node view renders the legacy block in edit mode and adds\n * NO separate corner edit surface (no pencil / JSON / form popover), so the\n * block owns a single, self-contained control overlay.\n */\n legacyBlockSelfEdits?: (blockType: string) => boolean;\n}\n\nconst RegistryBlockDataContext =\n createContext<RegistryBlockDataValue<any> | null>(null);\n\nexport function RegistryBlockDataProvider<\n TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,\n>({\n value,\n children,\n}: {\n value: RegistryBlockDataValue<TBlock>;\n children: ReactNode;\n}) {\n return (\n <RegistryBlockDataContext.Provider value={value}>\n {children}\n </RegistryBlockDataContext.Provider>\n );\n}\n\n/** Read the registry block side-map. Returns `null` outside a provider. */\nexport function useRegistryBlockData<\n TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,\n>(): RegistryBlockDataValue<TBlock> | null {\n return useContext(\n RegistryBlockDataContext,\n ) as RegistryBlockDataValue<TBlock> | null;\n}\n\nfunction clickedInteractiveChild(target: HTMLElement) {\n if (target.closest(\"button,input,textarea,select,a,[role='textbox']\")) {\n return true;\n }\n\n // The block drag-handle grip is a `role=\"button\"` div that lives in the editor\n // wrapper, and nested container blocks (columns/tabs) render their own inner\n // editors — each its own grip. A container block's `onMouseDownCapture`\n // selection handler runs in a SEPARATE React root (Tiptap mounts every node\n // view as its own root), so its `stopPropagation()` halts the NATIVE mousedown\n // right there, before it can reach an inner grip or inner editor. Treat the\n // grip and anything inside a nested editor region as interactive so the outer\n // container never hijacks a mousedown meant for inner machinery — this is what\n // makes dragging a block OUT of / BETWEEN columns (a nested grip) work at all.\n if (\n target.closest(\".drag-handle\") ||\n target.closest(\".plan-nested-document-editor-region\")\n ) {\n return true;\n }\n\n const blockNode = target.closest(\".plan-block-node\");\n const editable = target.closest(\"[contenteditable='true']\");\n return !!blockNode && !!editable && blockNode.contains(editable);\n}\n\n/* -------------------------------------------------------------------------- */\n/* B. RegistryBlockNodeView (React) */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Renders one registry-block atom. The block is non-editable as far as\n * ProseMirror is concerned (`contentEditable={false}`); all interaction happens\n * inside the registry-driven `<BlockView>`. Read vs edit is toggled by\n * `props.selected` (the node is \"selected\" in the editor) AND the document being\n * editable. `data-plan-interactive` keeps existing host click-guards from\n * treating clicks inside the block as document clicks.\n */\nexport function RegistryBlockNodeView(props: NodeViewProps) {\n const blockType = String(props.node.attrs.blockType ?? \"\");\n const blockId = String(props.node.attrs.blockId ?? \"\");\n const [panelOpen, setPanelOpen] = useState(false);\n const [shellHovered, setShellHovered] = useState(false);\n\n const registryValue = useOptionalBlockRegistry();\n const sideMap = useRegistryBlockData();\n\n // Optimistic edit override. `onBlockDataChange` commits into the host's own\n // store (a ref the side-map context can't observe), so an edit does NOT\n // re-render this node — the new data only reaches the view on the next full\n // document reconcile, which lands after the autosave round-trip (seconds\n // later) and is skipped entirely when the host treats the save as its own\n // echo. That left quick toggles like the callout tone buttons visually frozen\n // until reload. Holding the just-edited data locally re-renders this one node\n // immediately, then releases once the authoritative block catches up to (or\n // moves past) the value the edit was based on.\n const [pendingEdit, setPendingEdit] = useState<{\n data: unknown;\n base: unknown;\n } | null>(null);\n const liveBlock = sideMap?.getBlock(blockId);\n const liveData = liveBlock?.data;\n useEffect(() => {\n if (pendingEdit && !Object.is(liveData, pendingEdit.base)) {\n setPendingEdit(null);\n }\n }, [liveData, pendingEdit]);\n const block =\n liveBlock && pendingEdit && Object.is(liveData, pendingEdit.base)\n ? { ...liveBlock, data: pendingEdit.data }\n : liveBlock;\n const commitBlockData = (nextData: unknown, meta?: BlockDataChangeMeta) => {\n setPendingEdit({ data: nextData, base: liveData });\n sideMap?.onBlockDataChange(blockId, nextData, meta);\n };\n const editable = sideMap?.editable ?? false;\n // In Notion-sync mode, flag blocks that have no Notion (NFM) analog so the\n // author sees what won't push. Prose blocks aren't registry-block nodes, so\n // this only ever covers structured blocks.\n const incompatibleWithNotion =\n (sideMap?.notionSync ?? false) &&\n (sideMap?.isNotionIncompatibleType?.(blockType) ?? false);\n\n // The block data isn't in the side-map yet (e.g. a freshly inserted node whose\n // store entry hasn't been seeded). Render a graceful placeholder.\n if (!block) {\n return (\n <NodeViewWrapper className=\"plan-block-node\" data-block-id={blockId}>\n <div\n contentEditable={false}\n data-plan-interactive\n className=\"plan-block-node__placeholder rounded-md border border-dashed border-border px-3 py-2 text-sm text-muted-foreground\"\n >\n {blockType ? `Loading ${blockType} block…` : \"Loading block…\"}\n </div>\n </NodeViewWrapper>\n );\n }\n\n const spec = registryValue?.registry.get(blockType);\n const selectNode = (event: ReactMouseEvent<HTMLElement>) => {\n if (!editable) return;\n const target = event.target;\n if (target instanceof HTMLElement && clickedInteractiveChild(target))\n return;\n const pos = typeof props.getPos === \"function\" ? props.getPos() : null;\n if (typeof pos !== \"number\") return;\n try {\n event.preventDefault();\n event.stopPropagation();\n const { view } = props.editor;\n view.dispatch(\n view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos)),\n );\n view.focus();\n } catch {\n // Ignore stale positions during React/ProseMirror reconciliation.\n }\n };\n const updateShellHover = (event: ReactMouseEvent<HTMLElement>) => {\n const target = event.target;\n setShellHovered(\n target instanceof HTMLElement &&\n target.closest(\".plan-block-node__shell\") === event.currentTarget,\n );\n };\n\n // Choose how to render the block body:\n // 1. Registered spec → read view by default; direct-manipulation specs\n // (`editSurface: \"inline\" | \"container\"`) render their editor in place,\n // while artifact/config specs (`\"panel\"`) keep the read view plus a\n // corner edit button.\n // 2. No spec, but the side-map provides `renderLegacyBlock` → delegate to the\n // host's dispatcher (decision, legacy visual-questions, image, and any\n // other type rendered by a bespoke component rather than the registry), so\n // EVERY block type renders in the document exactly as it does in the\n // per-block reader — never a bare title fallback.\n // 3. Neither → a small non-crashing fallback.\n let body: ReactNode;\n let editSurface: ReactNode = null;\n if (registryValue && spec) {\n const blockData = (block as { data: unknown }).data;\n const Read = spec.Read;\n const readNode = (\n <Read\n data={blockData}\n blockId={block.id}\n title={block.title}\n summary={block.summary}\n ctx={registryValue.ctx}\n />\n );\n body = readNode;\n const canEditBlock =\n editable &&\n spec.placement.includes(\"block\") &&\n !!sideMap?.onBlockDataChange;\n if (canEditBlock) {\n const Edit = spec.Edit;\n const editorNode = Edit ? (\n <Edit\n data={blockData}\n onChange={commitBlockData}\n editable\n blockId={block.id}\n title={block.title}\n summary={block.summary}\n ctx={registryValue.ctx}\n />\n ) : (\n <SchemaBlockEditor\n data={blockData}\n onChange={(nextData) => commitBlockData(nextData)}\n schema={spec.schema}\n editable\n blockId={block.id}\n ctx={registryValue.ctx}\n />\n );\n const surface = blockEditSurface(spec);\n if (surface === \"panel\" && registryValue.ctx.renderEditSurface) {\n editSurface = registryValue.ctx.renderEditSurface({\n title: spec.label,\n open: panelOpen,\n onOpenChange: setPanelOpen,\n blockId: block.id,\n blockType,\n blockTitle: block.title,\n blockSummary: block.summary,\n blockData,\n trigger: (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={`Edit ${spec.label}`}\n onClick={() => setPanelOpen(true)}\n className=\"an-block-edit-trigger flex size-7 items-center justify-center rounded-md border border-border bg-background/85 text-muted-foreground opacity-0 shadow-sm backdrop-blur transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 data-[visible=true]:opacity-100\"\n data-visible={panelOpen || shellHovered}\n >\n <IconPencil className=\"size-4\" />\n </button>\n ),\n children: editorNode,\n });\n } else if (surface === \"panel\") {\n editSurface = props.selected ? (\n <div className=\"mt-3\">{editorNode}</div>\n ) : null;\n } else {\n body = editorNode;\n }\n }\n } else if (sideMap?.renderLegacyBlock) {\n // Self-editing legacy blocks (e.g. image) render their own edit affordance\n // inside their overlay, so render them in edit mode and add NO separate\n // corner edit surface — the block owns a single, self-contained overlay.\n const selfEdits =\n editable && Boolean(sideMap.legacyBlockSelfEdits?.(blockType));\n body = sideMap.renderLegacyBlock(block, { editing: selfEdits });\n if (editable && sideMap.onBlockDataChange && !selfEdits) {\n // Prefer a host-provided schema/custom editor (a real form) over the raw\n // JSON fallback when the host knows how to edit this legacy block type.\n const customEditor = sideMap.renderLegacyBlockEditor?.(block, {\n onChange: (nextData) => commitBlockData(nextData),\n });\n editSurface = (\n <LegacyJsonEditSurface\n block={block}\n open={panelOpen}\n onOpenChange={setPanelOpen}\n renderEditSurface={registryValue?.ctx.renderEditSurface}\n onChange={(nextBlock) => commitBlockData(nextBlock)}\n selected={shellHovered}\n customEditor={customEditor}\n />\n );\n }\n } else {\n body = (\n <div className=\"plan-block-node__fallback rounded-md border border-border px-3 py-2 text-sm text-muted-foreground\">\n {block.title || blockType || \"Unsupported block\"}\n </div>\n );\n }\n\n return (\n <NodeViewWrapper\n className=\"plan-block-node\"\n data-block-id={blockId}\n data-plan-block-selected={props.selected ? \"\" : undefined}\n data-notion-incompatible={incompatibleWithNotion ? \"\" : undefined}\n onMouseDownCapture={selectNode}\n >\n <div\n contentEditable={false}\n data-plan-interactive\n className=\"plan-block-node__shell relative\"\n onMouseEnter={updateShellHover}\n onMouseMove={updateShellHover}\n onMouseLeave={() => setShellHovered(false)}\n >\n {incompatibleWithNotion && (\n <span\n className=\"plan-block-notion-badge\"\n title=\"This block type has no Notion equivalent and won't sync to Notion.\"\n >\n Won't sync to Notion\n </span>\n )}\n {body}\n {editSurface && (\n <div className=\"plan-block-node__edit absolute right-2 top-2 z-20\">\n {editSurface}\n </div>\n )}\n </div>\n </NodeViewWrapper>\n );\n}\n\nexport function LegacyJsonEditSurface({\n block,\n open,\n onOpenChange,\n renderEditSurface,\n onChange,\n selected,\n customEditor,\n}: {\n block: RegistryBlockSideMapBlock;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n renderEditSurface?: BlockRenderContext[\"renderEditSurface\"];\n onChange: (nextData: unknown) => void;\n selected: boolean;\n /**\n * A host-provided form editor (e.g. {@link SchemaBlockEditor}). When present it\n * replaces the raw-JSON textarea + Save button; the form autosaves through its\n * own `onChange`.\n */\n customEditor?: ReactNode;\n}) {\n const serializedBlockData = useMemo(\n () => JSON.stringify(block.data, null, 2),\n [block.data],\n );\n const [draft, setDraft] = useState(serializedBlockData);\n const [parseError, setParseError] = useState<string | null>(null);\n\n useEffect(() => {\n setDraft(serializedBlockData);\n setParseError(null);\n }, [block.id, serializedBlockData]);\n\n const saveDraft = () => {\n try {\n const nextData = JSON.parse(draft) as unknown;\n setParseError(null);\n onChange(nextData);\n onOpenChange(false);\n } catch (error) {\n setParseError(\n error instanceof Error ? error.message : \"Invalid JSON data.\",\n );\n }\n };\n\n const trigger = (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={`Edit ${block.title ?? \"block\"}`}\n onClick={() => onOpenChange(true)}\n className=\"an-block-edit-trigger flex size-7 items-center justify-center rounded-md border border-border bg-background/85 text-muted-foreground opacity-0 shadow-sm backdrop-blur transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 data-[visible=true]:opacity-100\"\n data-visible={selected || open}\n >\n <IconPencil className=\"size-4\" />\n </button>\n );\n const jsonEditor = (\n <div className=\"grid gap-3\">\n <textarea\n data-plan-interactive\n className=\"min-h-64 w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs leading-5 text-foreground shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={draft}\n aria-invalid={parseError ? true : undefined}\n onChange={(event) => {\n setDraft(event.target.value);\n if (parseError) setParseError(null);\n }}\n />\n {parseError ? (\n <p className=\"text-xs text-destructive\" role=\"alert\">\n Invalid JSON: {parseError}\n </p>\n ) : null}\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n data-plan-interactive\n className=\"inline-flex h-8 items-center justify-center rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground\"\n onClick={saveDraft}\n >\n Save\n </button>\n </div>\n </div>\n );\n const editor = customEditor ?? jsonEditor;\n if (!renderEditSurface) return open ? editor : trigger;\n return renderEditSurface({\n title: block.title ?? \"Block\",\n open,\n onOpenChange,\n blockId: block.id,\n blockType:\n typeof (block as { type?: unknown }).type === \"string\"\n ? ((block as unknown as { type: string }).type ?? \"\")\n : \"legacy\",\n blockTitle: block.title,\n blockSummary: block.summary,\n blockData: block.data,\n trigger,\n children: editor,\n });\n}\n\n/* -------------------------------------------------------------------------- */\n/* A. registry-block Tiptap node factory */\n/* -------------------------------------------------------------------------- */\n\n/** Options for {@link createRegistryBlockNode}. */\nexport interface CreateRegistryBlockNodeOptions {\n /**\n * The Tiptap node name (e.g. `\"planBlock\"`). Hosts that serialize the doc by\n * node name (plan's `plan-doc.ts` keys off `\"planBlock\"`) must pass the exact\n * name their serializer expects.\n */\n nodeName: string;\n /**\n * The HTML data-attribute that marks a serialized registry block on copy/paste\n * round-trip (e.g. `\"data-plan-block\"`).\n */\n dataTag: string;\n /**\n * Mint a fresh, unique block id for a given block type. Used by the dedupe\n * plugin to re-mint duplicate / missing ids (paste/duplicate). Plan passes\n * `createPlanBlockId`.\n */\n mintId: (blockType: string) => string;\n /** Node group (default `\"block\"`). */\n group?: string;\n}\n\n/**\n * Build the generic registry-block Tiptap atom node. Returns a Tiptap `Node`\n * that:\n * - carries identity attrs `blockType` / `blockId` / `title` / `summary`, a\n * `sourceBlockId` (set when a duplicate is re-minted, so the host can copy the\n * original block's data), and an optional `__raw` verbatim-MDX attr for\n * byte-stable source round-trips;\n * - is an atom + isolating + draggable block that renders through\n * {@link RegistryBlockNodeView} (via `ReactNodeViewRenderer`);\n * - installs a dedupe `appendTransaction` plugin that re-mints any duplicate or\n * empty `blockId` (the classic paste/duplicate case), preserving the original\n * block's id + side-map data and tagging its own transaction so it never\n * loops.\n */\nexport function createRegistryBlockNode(\n options: CreateRegistryBlockNodeOptions,\n) {\n const { nodeName, dataTag, mintId, group = \"block\" } = options;\n const dedupeKey = new PluginKey(`${nodeName}DedupeIds`);\n const keyboardGuardKey = new PluginKey(`${nodeName}KeyboardGuard`);\n\n /**\n * Collect every `blockId` currently present on this node type in a doc, with\n * the position of each node, so duplicate ids (from paste/duplicate) can be\n * detected and re-minted.\n */\n function collectEntries(state: EditorState): Array<{\n pos: number;\n blockType: string;\n blockId: string;\n sourceBlockId?: string;\n }> {\n const found: Array<{\n pos: number;\n blockType: string;\n blockId: string;\n sourceBlockId?: string;\n }> = [];\n state.doc.descendants((node, pos) => {\n if (node.type.name === nodeName) {\n found.push({\n pos,\n blockType: String(node.attrs.blockType ?? \"\"),\n blockId: String(node.attrs.blockId ?? \"\"),\n sourceBlockId:\n typeof node.attrs.sourceBlockId === \"string\"\n ? node.attrs.sourceBlockId\n : undefined,\n });\n }\n return true;\n });\n return found;\n }\n\n /**\n * Build a transaction that re-mints any duplicate / missing ids in `state`, or\n * `null` when nothing needs changing. Only the *later* duplicate (and any node\n * with an empty id) is re-minted, so the original keeps its id and side-map\n * data.\n */\n function buildDedupeTransaction(state: EditorState) {\n const entries = collectEntries(state);\n if (entries.length === 0) return null;\n\n const seen = new Set<string>();\n let tr = state.tr;\n let changed = false;\n\n for (const entry of entries) {\n const needsNewId = !entry.blockId || seen.has(entry.blockId);\n if (needsNewId) {\n const freshId = mintId(entry.blockType || \"block\");\n const node = state.doc.nodeAt(entry.pos);\n if (node) {\n tr = tr.setNodeMarkup(entry.pos, undefined, {\n ...node.attrs,\n blockId: freshId,\n sourceBlockId: entry.sourceBlockId || entry.blockId || null,\n });\n changed = true;\n }\n seen.add(freshId);\n } else {\n seen.add(entry.blockId);\n }\n }\n\n return changed ? tr.setMeta(dedupeKey, true) : null;\n }\n\n const selectedRegistryBlock = (state: EditorState) =>\n state.selection instanceof NodeSelection &&\n state.selection.node.type.name === nodeName;\n\n const isMutatingKey = (event: KeyboardEvent) => {\n if (event.altKey || event.ctrlKey || event.metaKey) return false;\n if (event.key === \"Enter\") return true;\n return event.key.length === 1;\n };\n\n return Node.create({\n name: nodeName,\n group,\n atom: true,\n draggable: true,\n selectable: true,\n isolating: true,\n\n addAttributes() {\n return {\n blockType: { default: \"\" },\n blockId: { default: \"\" },\n title: { default: null },\n summary: { default: null },\n sourceBlockId: { default: null },\n // Optional verbatim source for hosts that need byte-identical\n // source-format round-trips without React (server pull, hashing). Plan\n // never sets this.\n __raw: { default: null, rendered: false },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: `div[${dataTag}]`,\n getAttrs: (element) => {\n const node = element as HTMLElement;\n return {\n blockType: node.getAttribute(\"data-block-type\") || \"\",\n blockId: node.getAttribute(\"data-block-id\") || \"\",\n title: node.getAttribute(\"data-title\") || null,\n summary: node.getAttribute(\"data-summary\") || null,\n sourceBlockId: node.getAttribute(\"data-source-block-id\") || null,\n };\n },\n },\n ];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n \"div\",\n mergeAttributes(HTMLAttributes, {\n [dataTag]: \"\",\n \"data-block-type\": HTMLAttributes.blockType ?? \"\",\n \"data-block-id\": HTMLAttributes.blockId ?? \"\",\n \"data-title\": HTMLAttributes.title ?? undefined,\n \"data-summary\": HTMLAttributes.summary ?? undefined,\n \"data-source-block-id\": HTMLAttributes.sourceBlockId ?? undefined,\n }),\n ];\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(RegistryBlockNodeView);\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: dedupeKey,\n appendTransaction(transactions, _oldState, newState) {\n // Ignore our own re-mint, and skip when nothing changed the doc.\n if (\n transactions.some((transaction) =>\n transaction.getMeta(dedupeKey),\n ) ||\n !transactions.some((transaction) => transaction.docChanged)\n ) {\n return null;\n }\n return buildDedupeTransaction(newState);\n },\n }),\n new Plugin({\n key: keyboardGuardKey,\n props: {\n handleClickOn(view, _pos, node, nodePos, event, direct) {\n if (node.type.name !== nodeName || !direct) return false;\n if (\n event.target instanceof HTMLElement &&\n clickedInteractiveChild(event.target)\n ) {\n return false;\n }\n event.preventDefault();\n view.dispatch(\n view.state.tr.setSelection(\n NodeSelection.create(view.state.doc, nodePos),\n ),\n );\n view.focus();\n return true;\n },\n handleKeyDown(view, event) {\n if (!selectedRegistryBlock(view.state) || !isMutatingKey(event))\n return false;\n event.preventDefault();\n return true;\n },\n handleTextInput(view) {\n return selectedRegistryBlock(view.state);\n },\n handlePaste(view, event) {\n if (!selectedRegistryBlock(view.state)) return false;\n event.preventDefault();\n return true;\n },\n handleDOMEvents: {\n beforeinput(view, event) {\n if (!selectedRegistryBlock(view.state)) return false;\n const inputEvent = event as InputEvent;\n if (\n !inputEvent.inputType ||\n (!inputEvent.inputType.startsWith(\"insert\") &&\n inputEvent.inputType !== \"formatSetBlockTextDirection\")\n ) {\n return false;\n }\n event.preventDefault();\n return true;\n },\n },\n },\n }),\n ];\n },\n });\n}\n"]}
1
+ {"version":3,"file":"RegistryBlockNode.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/RegistryBlockNode.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n createContext,\n useEffect,\n useContext,\n useMemo,\n useState,\n type ReactNode,\n type MouseEvent as ReactMouseEvent,\n} from \"react\";\nimport { IconPencil } from \"@tabler/icons-react\";\nimport {\n Node,\n NodeViewWrapper,\n ReactNodeViewRenderer,\n mergeAttributes,\n type NodeViewProps,\n} from \"@tiptap/react\";\nimport {\n NodeSelection,\n Plugin,\n PluginKey,\n type EditorState,\n} from \"@tiptap/pm/state\";\nimport {\n blockEditSurface,\n useOptionalBlockRegistry,\n type BlockDataChangeMeta,\n type BlockRenderContext,\n} from \"../blocks/index.js\";\nimport { SchemaBlockEditor } from \"../blocks/SchemaBlockEditor.js\";\n\n/* -------------------------------------------------------------------------- */\n/* The generic registry-block side-map + Tiptap NodeView, lifted into core. */\n/* */\n/* This is the app-agnostic NodeView that renders registered block specs */\n/* inside a `SharedRichEditor` document. Hosts mount the node produced by */\n/* {@link createRegistryBlockNode} as an extra extension and wrap the editor */\n/* in a {@link RegistryBlockDataProvider}, sourcing the typed block `data` */\n/* from their own authoritative store (for example, PlanContent.blocks). The */\n/* node itself carries only lightweight identity attrs (type/id/title/summary) */\n/* plus an optional `__raw` verbatim-MDX attr for byte-stable source */\n/* round-trips; the heavy typed `data` is threaded through the side-map */\n/* context, keeping the doc small and the block data the single source of */\n/* truth. */\n/* -------------------------------------------------------------------------- */\n\n/* -------------------------------------------------------------------------- */\n/* C. Block-data side-map context */\n/* -------------------------------------------------------------------------- */\n\n/** The minimal block shape the NodeView renders through `<BlockView>`. */\nexport interface RegistryBlockSideMapBlock {\n id: string;\n title?: string;\n summary?: string;\n data: unknown;\n}\n\n/**\n * The side-map an editor host supplies so the registry NodeView can resolve a\n * block's full typed `data` (and commit edits) by its stable id, without ever\n * storing that data in the ProseMirror doc.\n */\nexport interface RegistryBlockDataValue<\n TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,\n> {\n /** Resolve a block's full record (incl. `data`) by its stable id. */\n getBlock: (blockId: string) => TBlock | undefined;\n /** Commit a new `data` value for a block (edit-mode only). */\n onBlockDataChange: (\n blockId: string,\n nextData: unknown,\n meta?: BlockDataChangeMeta,\n ) => void;\n /** Whether the document (and thus its blocks) is editable. */\n editable: boolean;\n /**\n * When true, blocks whose type has no Notion (NFM) analog are badged so the\n * author knows they won't sync. The host decides which types are incompatible\n * via {@link isNotionIncompatibleType}; this flag just toggles the badge on.\n */\n notionSync?: boolean;\n /**\n * Decide whether a block type is Notion-incompatible (no NFM analog). Only\n * consulted when {@link notionSync} is true. Injected by the host so the\n * single registry-level allowlist (plan's `isNotionCompatibleBlockType`, or\n * content's registry-derived gate) drives the badge — core stays policy-free.\n */\n isNotionIncompatibleType?: (blockType: string) => boolean;\n /**\n * Render a block whose type is NOT in the registry through the host's own\n * dispatcher (plan: `PlanBlockView` for decision / legacy visual-questions /\n * image; omitted in hosts with no legacy types), so every block type renders\n * in the document instead of a bare fallback.\n */\n renderLegacyBlock?: (\n block: TBlock,\n options: { editing: boolean },\n ) => ReactNode;\n /**\n * Optional: render a schema-driven (or otherwise custom) editor for a legacy\n * block in place of the raw-JSON fallback. Receives an `onChange` that commits\n * the block's new `data`. Return `null`/`undefined` to keep the JSON editor for\n * that block type. The returned editor is expected to autosave through\n * `onChange` (no Save button is shown), matching registered panel blocks.\n */\n renderLegacyBlockEditor?: (\n block: TBlock,\n args: { onChange: (nextData: unknown) => void },\n ) => ReactNode;\n /**\n * Return `true` for legacy block types that render their OWN edit affordance\n * inside `renderLegacyBlock` (e.g. an image block with its own hover toolbar).\n * For those types the node view renders the legacy block in edit mode and adds\n * NO separate corner edit surface (no pencil / JSON / form popover), so the\n * block owns a single, self-contained control overlay.\n */\n legacyBlockSelfEdits?: (blockType: string) => boolean;\n}\n\nconst RegistryBlockDataContext =\n createContext<RegistryBlockDataValue<any> | null>(null);\n\nexport function RegistryBlockDataProvider<\n TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,\n>({\n value,\n children,\n}: {\n value: RegistryBlockDataValue<TBlock>;\n children: ReactNode;\n}) {\n return (\n <RegistryBlockDataContext.Provider value={value}>\n {children}\n </RegistryBlockDataContext.Provider>\n );\n}\n\n/** Read the registry block side-map. Returns `null` outside a provider. */\nexport function useRegistryBlockData<\n TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock,\n>(): RegistryBlockDataValue<TBlock> | null {\n return useContext(\n RegistryBlockDataContext,\n ) as RegistryBlockDataValue<TBlock> | null;\n}\n\nfunction clickedInteractiveChild(target: HTMLElement) {\n if (target.closest(\"button,input,textarea,select,a,[role='textbox']\")) {\n return true;\n }\n\n // The block drag-handle grip is a `role=\"button\"` div that lives in the editor\n // wrapper, and nested container blocks (columns/tabs) render their own inner\n // editors — each its own grip. A container block's `onMouseDownCapture`\n // selection handler runs in a SEPARATE React root (Tiptap mounts every node\n // view as its own root), so its `stopPropagation()` halts the NATIVE mousedown\n // right there, before it can reach an inner grip or inner editor. Treat the\n // grip and anything inside a nested editor region as interactive so the outer\n // container never hijacks a mousedown meant for inner machinery — this is what\n // makes dragging a block OUT of / BETWEEN columns (a nested grip) work at all.\n if (\n target.closest(\".drag-handle\") ||\n target.closest(\".plan-nested-document-editor-region\")\n ) {\n return true;\n }\n\n const blockNode = target.closest(\".plan-block-node\");\n const editable = target.closest(\"[contenteditable='true']\");\n return !!blockNode && !!editable && blockNode.contains(editable);\n}\n\n/* -------------------------------------------------------------------------- */\n/* B. RegistryBlockNodeView (React) */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Renders one registry-block atom. The block is non-editable as far as\n * ProseMirror is concerned (`contentEditable={false}`); all interaction happens\n * inside the registry-driven `<BlockView>`. Read vs edit is toggled by\n * `props.selected` (the node is \"selected\" in the editor) AND the document being\n * editable. `data-plan-interactive` keeps existing host click-guards from\n * treating clicks inside the block as document clicks.\n */\nexport function RegistryBlockNodeView(props: NodeViewProps) {\n const blockType = String(props.node.attrs.blockType ?? \"\");\n const blockId = String(props.node.attrs.blockId ?? \"\");\n const [panelOpen, setPanelOpen] = useState(false);\n const [shellHovered, setShellHovered] = useState(false);\n\n const registryValue = useOptionalBlockRegistry();\n const sideMap = useRegistryBlockData();\n\n // Optimistic edit override. `onBlockDataChange` commits into the host's own\n // store (a ref the side-map context can't observe), so an edit does NOT\n // re-render this node — the new data only reaches the view on the next full\n // document reconcile, which lands after the autosave round-trip (seconds\n // later) and is skipped entirely when the host treats the save as its own\n // echo. That left quick toggles like the callout tone buttons visually frozen\n // until reload. Holding the just-edited data locally re-renders this one node\n // immediately, then releases once the authoritative block catches up to (or\n // moves past) the value the edit was based on.\n const [pendingEdit, setPendingEdit] = useState<{\n data: unknown;\n base: unknown;\n } | null>(null);\n const liveBlock = sideMap?.getBlock(blockId);\n const liveData = liveBlock?.data;\n useEffect(() => {\n if (pendingEdit && !Object.is(liveData, pendingEdit.base)) {\n setPendingEdit(null);\n }\n }, [liveData, pendingEdit]);\n const block =\n liveBlock && pendingEdit && Object.is(liveData, pendingEdit.base)\n ? { ...liveBlock, data: pendingEdit.data }\n : liveBlock;\n const commitBlockData = (nextData: unknown, meta?: BlockDataChangeMeta) => {\n setPendingEdit({ data: nextData, base: liveData });\n sideMap?.onBlockDataChange(blockId, nextData, meta);\n };\n const editable = sideMap?.editable ?? false;\n // In Notion-sync mode, flag blocks that have no Notion (NFM) analog so the\n // author sees what won't push. Prose blocks aren't registry-block nodes, so\n // this only ever covers structured blocks.\n const incompatibleWithNotion =\n (sideMap?.notionSync ?? false) &&\n (sideMap?.isNotionIncompatibleType?.(blockType) ?? false);\n\n // The block data isn't in the side-map yet (e.g. a freshly inserted node whose\n // store entry hasn't been seeded). Render a graceful placeholder.\n if (!block) {\n return (\n <NodeViewWrapper className=\"plan-block-node\" data-block-id={blockId}>\n <div\n contentEditable={false}\n data-plan-interactive\n className=\"plan-block-node__placeholder rounded-md border border-dashed border-border px-3 py-2 text-sm text-muted-foreground\"\n >\n {blockType ? `Loading ${blockType} block…` : \"Loading block…\"}\n </div>\n </NodeViewWrapper>\n );\n }\n\n const spec = registryValue?.registry.get(blockType);\n const selectNode = (event: ReactMouseEvent<HTMLElement>) => {\n if (!editable) return;\n const target = event.target;\n if (target instanceof HTMLElement && clickedInteractiveChild(target))\n return;\n const pos = typeof props.getPos === \"function\" ? props.getPos() : null;\n if (typeof pos !== \"number\") return;\n try {\n event.preventDefault();\n event.stopPropagation();\n const { view } = props.editor;\n view.dispatch(\n view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos)),\n );\n view.focus();\n } catch {\n // Ignore stale positions during React/ProseMirror reconciliation.\n }\n };\n const updateShellHover = (event: ReactMouseEvent<HTMLElement>) => {\n const target = event.target;\n setShellHovered(\n target instanceof HTMLElement &&\n target.closest(\".plan-block-node__shell\") === event.currentTarget,\n );\n };\n\n // Choose how to render the block body:\n // 1. Registered spec → read view by default; direct-manipulation specs\n // (`editSurface: \"inline\" | \"container\"`) render their editor in place,\n // while artifact/config specs (`\"panel\"`) keep the read view plus a\n // corner edit button.\n // 2. No spec, but the side-map provides `renderLegacyBlock` → delegate to the\n // host's dispatcher (decision, legacy visual-questions, image, and any\n // other type rendered by a bespoke component rather than the registry), so\n // EVERY block type renders in the document exactly as it does in the\n // per-block reader — never a bare title fallback.\n // 3. Neither → a small non-crashing fallback.\n let body: ReactNode;\n let editSurface: ReactNode = null;\n if (registryValue && spec) {\n const blockData = (block as { data: unknown }).data;\n const Read = spec.Read;\n const readNode = (\n <Read\n data={blockData}\n blockId={block.id}\n title={block.title}\n summary={block.summary}\n ctx={registryValue.ctx}\n />\n );\n body = readNode;\n const canEditBlock =\n editable &&\n spec.placement.includes(\"block\") &&\n !!sideMap?.onBlockDataChange;\n if (canEditBlock) {\n const Edit = spec.Edit;\n const editorNode = Edit ? (\n <Edit\n data={blockData}\n onChange={commitBlockData}\n editable\n blockId={block.id}\n title={block.title}\n summary={block.summary}\n ctx={registryValue.ctx}\n />\n ) : (\n <SchemaBlockEditor\n data={blockData}\n onChange={(nextData) => commitBlockData(nextData)}\n schema={spec.schema}\n editable\n blockId={block.id}\n ctx={registryValue.ctx}\n />\n );\n const surface = blockEditSurface(spec);\n if (surface === \"panel\" && registryValue.ctx.renderEditSurface) {\n editSurface = registryValue.ctx.renderEditSurface({\n title: spec.label,\n open: panelOpen,\n onOpenChange: setPanelOpen,\n blockId: block.id,\n blockType,\n blockTitle: block.title,\n blockSummary: block.summary,\n blockData,\n trigger: (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={`Edit ${spec.label}`}\n onClick={() => setPanelOpen(true)}\n className=\"an-block-edit-trigger flex size-7 items-center justify-center rounded-md border border-border bg-background/85 text-muted-foreground opacity-0 shadow-sm backdrop-blur transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 data-[visible=true]:opacity-100\"\n data-visible={panelOpen || shellHovered}\n >\n <IconPencil className=\"size-4\" />\n </button>\n ),\n children: editorNode,\n });\n } else if (surface === \"panel\") {\n editSurface = props.selected ? (\n <div className=\"mt-3\">{editorNode}</div>\n ) : null;\n } else {\n body = editorNode;\n }\n }\n } else if (sideMap?.renderLegacyBlock) {\n // Self-editing legacy blocks (e.g. image) render their own edit affordance\n // inside their overlay, so render them in edit mode and add NO separate\n // corner edit surface — the block owns a single, self-contained overlay.\n const selfEdits =\n editable && Boolean(sideMap.legacyBlockSelfEdits?.(blockType));\n body = sideMap.renderLegacyBlock(block, { editing: selfEdits });\n if (editable && sideMap.onBlockDataChange && !selfEdits) {\n // Prefer a host-provided schema/custom editor (a real form) over the raw\n // JSON fallback when the host knows how to edit this legacy block type.\n const customEditor = sideMap.renderLegacyBlockEditor?.(block, {\n onChange: (nextData) => commitBlockData(nextData),\n });\n editSurface = (\n <LegacyJsonEditSurface\n block={block}\n open={panelOpen}\n onOpenChange={setPanelOpen}\n renderEditSurface={registryValue?.ctx.renderEditSurface}\n onChange={(nextBlock) => commitBlockData(nextBlock)}\n selected={shellHovered}\n customEditor={customEditor}\n />\n );\n }\n } else {\n body = (\n <div className=\"plan-block-node__fallback rounded-md border border-border px-3 py-2 text-sm text-muted-foreground\">\n {block.title || blockType || \"Unsupported block\"}\n </div>\n );\n }\n\n return (\n <NodeViewWrapper\n className=\"plan-block-node\"\n data-block-id={blockId}\n // Mirror the block type onto the wrapper so the document flow can detect a\n // RUN of consecutive blocks of the same type (e.g. api-endpoint) and\n // collapse the divider + gap between them, matching the read-only path.\n data-block-type={blockType || undefined}\n data-plan-block-selected={props.selected ? \"\" : undefined}\n data-notion-incompatible={incompatibleWithNotion ? \"\" : undefined}\n onMouseDownCapture={selectNode}\n >\n <div\n contentEditable={false}\n data-plan-interactive\n className=\"plan-block-node__shell relative\"\n onMouseEnter={updateShellHover}\n onMouseMove={updateShellHover}\n onMouseLeave={() => setShellHovered(false)}\n >\n {incompatibleWithNotion && (\n <span\n className=\"plan-block-notion-badge\"\n title=\"This block type has no Notion equivalent and won't sync to Notion.\"\n >\n Won't sync to Notion\n </span>\n )}\n {body}\n {editSurface && (\n <div className=\"plan-block-node__edit absolute right-2 top-2 z-20\">\n {editSurface}\n </div>\n )}\n </div>\n </NodeViewWrapper>\n );\n}\n\nexport function LegacyJsonEditSurface({\n block,\n open,\n onOpenChange,\n renderEditSurface,\n onChange,\n selected,\n customEditor,\n}: {\n block: RegistryBlockSideMapBlock;\n open: boolean;\n onOpenChange: (open: boolean) => void;\n renderEditSurface?: BlockRenderContext[\"renderEditSurface\"];\n onChange: (nextData: unknown) => void;\n selected: boolean;\n /**\n * A host-provided form editor (e.g. {@link SchemaBlockEditor}). When present it\n * replaces the raw-JSON textarea + Save button; the form autosaves through its\n * own `onChange`.\n */\n customEditor?: ReactNode;\n}) {\n const serializedBlockData = useMemo(\n () => JSON.stringify(block.data, null, 2),\n [block.data],\n );\n const [draft, setDraft] = useState(serializedBlockData);\n const [parseError, setParseError] = useState<string | null>(null);\n\n useEffect(() => {\n setDraft(serializedBlockData);\n setParseError(null);\n }, [block.id, serializedBlockData]);\n\n const saveDraft = () => {\n try {\n const nextData = JSON.parse(draft) as unknown;\n setParseError(null);\n onChange(nextData);\n onOpenChange(false);\n } catch (error) {\n setParseError(\n error instanceof Error ? error.message : \"Invalid JSON data.\",\n );\n }\n };\n\n const trigger = (\n <button\n type=\"button\"\n data-plan-interactive\n aria-label={`Edit ${block.title ?? \"block\"}`}\n onClick={() => onOpenChange(true)}\n className=\"an-block-edit-trigger flex size-7 items-center justify-center rounded-md border border-border bg-background/85 text-muted-foreground opacity-0 shadow-sm backdrop-blur transition-opacity hover:bg-muted hover:text-foreground focus-visible:opacity-100 data-[visible=true]:opacity-100\"\n data-visible={selected || open}\n >\n <IconPencil className=\"size-4\" />\n </button>\n );\n const jsonEditor = (\n <div className=\"grid gap-3\">\n <textarea\n data-plan-interactive\n className=\"min-h-64 w-full rounded-md border border-input bg-background px-3 py-2 font-mono text-xs leading-5 text-foreground shadow-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring\"\n value={draft}\n aria-invalid={parseError ? true : undefined}\n onChange={(event) => {\n setDraft(event.target.value);\n if (parseError) setParseError(null);\n }}\n />\n {parseError ? (\n <p className=\"text-xs text-destructive\" role=\"alert\">\n Invalid JSON: {parseError}\n </p>\n ) : null}\n <div className=\"flex justify-end\">\n <button\n type=\"button\"\n data-plan-interactive\n className=\"inline-flex h-8 items-center justify-center rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground\"\n onClick={saveDraft}\n >\n Save\n </button>\n </div>\n </div>\n );\n const editor = customEditor ?? jsonEditor;\n if (!renderEditSurface) return open ? editor : trigger;\n return renderEditSurface({\n title: block.title ?? \"Block\",\n open,\n onOpenChange,\n blockId: block.id,\n blockType:\n typeof (block as { type?: unknown }).type === \"string\"\n ? ((block as unknown as { type: string }).type ?? \"\")\n : \"legacy\",\n blockTitle: block.title,\n blockSummary: block.summary,\n blockData: block.data,\n trigger,\n children: editor,\n });\n}\n\n/* -------------------------------------------------------------------------- */\n/* A. registry-block Tiptap node factory */\n/* -------------------------------------------------------------------------- */\n\n/** Options for {@link createRegistryBlockNode}. */\nexport interface CreateRegistryBlockNodeOptions {\n /**\n * The Tiptap node name (e.g. `\"planBlock\"`). Hosts that serialize the doc by\n * node name (plan's `plan-doc.ts` keys off `\"planBlock\"`) must pass the exact\n * name their serializer expects.\n */\n nodeName: string;\n /**\n * The HTML data-attribute that marks a serialized registry block on copy/paste\n * round-trip (e.g. `\"data-plan-block\"`).\n */\n dataTag: string;\n /**\n * Mint a fresh, unique block id for a given block type. Used by the dedupe\n * plugin to re-mint duplicate / missing ids (paste/duplicate). Plan passes\n * `createPlanBlockId`.\n */\n mintId: (blockType: string) => string;\n /** Node group (default `\"block\"`). */\n group?: string;\n}\n\n/**\n * Build the generic registry-block Tiptap atom node. Returns a Tiptap `Node`\n * that:\n * - carries identity attrs `blockType` / `blockId` / `title` / `summary`, a\n * `sourceBlockId` (set when a duplicate is re-minted, so the host can copy the\n * original block's data), and an optional `__raw` verbatim-MDX attr for\n * byte-stable source round-trips;\n * - is an atom + isolating + draggable block that renders through\n * {@link RegistryBlockNodeView} (via `ReactNodeViewRenderer`);\n * - installs a dedupe `appendTransaction` plugin that re-mints any duplicate or\n * empty `blockId` (the classic paste/duplicate case), preserving the original\n * block's id + side-map data and tagging its own transaction so it never\n * loops.\n */\nexport function createRegistryBlockNode(\n options: CreateRegistryBlockNodeOptions,\n) {\n const { nodeName, dataTag, mintId, group = \"block\" } = options;\n const dedupeKey = new PluginKey(`${nodeName}DedupeIds`);\n const keyboardGuardKey = new PluginKey(`${nodeName}KeyboardGuard`);\n\n /**\n * Collect every `blockId` currently present on this node type in a doc, with\n * the position of each node, so duplicate ids (from paste/duplicate) can be\n * detected and re-minted.\n */\n function collectEntries(state: EditorState): Array<{\n pos: number;\n blockType: string;\n blockId: string;\n sourceBlockId?: string;\n }> {\n const found: Array<{\n pos: number;\n blockType: string;\n blockId: string;\n sourceBlockId?: string;\n }> = [];\n state.doc.descendants((node, pos) => {\n if (node.type.name === nodeName) {\n found.push({\n pos,\n blockType: String(node.attrs.blockType ?? \"\"),\n blockId: String(node.attrs.blockId ?? \"\"),\n sourceBlockId:\n typeof node.attrs.sourceBlockId === \"string\"\n ? node.attrs.sourceBlockId\n : undefined,\n });\n }\n return true;\n });\n return found;\n }\n\n /**\n * Build a transaction that re-mints any duplicate / missing ids in `state`, or\n * `null` when nothing needs changing. Only the *later* duplicate (and any node\n * with an empty id) is re-minted, so the original keeps its id and side-map\n * data.\n */\n function buildDedupeTransaction(state: EditorState) {\n const entries = collectEntries(state);\n if (entries.length === 0) return null;\n\n const seen = new Set<string>();\n let tr = state.tr;\n let changed = false;\n\n for (const entry of entries) {\n const needsNewId = !entry.blockId || seen.has(entry.blockId);\n if (needsNewId) {\n const freshId = mintId(entry.blockType || \"block\");\n const node = state.doc.nodeAt(entry.pos);\n if (node) {\n tr = tr.setNodeMarkup(entry.pos, undefined, {\n ...node.attrs,\n blockId: freshId,\n sourceBlockId: entry.sourceBlockId || entry.blockId || null,\n });\n changed = true;\n }\n seen.add(freshId);\n } else {\n seen.add(entry.blockId);\n }\n }\n\n return changed ? tr.setMeta(dedupeKey, true) : null;\n }\n\n const selectedRegistryBlock = (state: EditorState) =>\n state.selection instanceof NodeSelection &&\n state.selection.node.type.name === nodeName;\n\n const isMutatingKey = (event: KeyboardEvent) => {\n if (event.altKey || event.ctrlKey || event.metaKey) return false;\n if (event.key === \"Enter\") return true;\n return event.key.length === 1;\n };\n\n return Node.create({\n name: nodeName,\n group,\n atom: true,\n draggable: true,\n selectable: true,\n isolating: true,\n\n addAttributes() {\n return {\n blockType: { default: \"\" },\n blockId: { default: \"\" },\n title: { default: null },\n summary: { default: null },\n sourceBlockId: { default: null },\n // Optional verbatim source for hosts that need byte-identical\n // source-format round-trips without React (server pull, hashing). Plan\n // never sets this.\n __raw: { default: null, rendered: false },\n };\n },\n\n parseHTML() {\n return [\n {\n tag: `div[${dataTag}]`,\n getAttrs: (element) => {\n const node = element as HTMLElement;\n return {\n blockType: node.getAttribute(\"data-block-type\") || \"\",\n blockId: node.getAttribute(\"data-block-id\") || \"\",\n title: node.getAttribute(\"data-title\") || null,\n summary: node.getAttribute(\"data-summary\") || null,\n sourceBlockId: node.getAttribute(\"data-source-block-id\") || null,\n };\n },\n },\n ];\n },\n\n renderHTML({ HTMLAttributes }) {\n return [\n \"div\",\n mergeAttributes(HTMLAttributes, {\n [dataTag]: \"\",\n \"data-block-type\": HTMLAttributes.blockType ?? \"\",\n \"data-block-id\": HTMLAttributes.blockId ?? \"\",\n \"data-title\": HTMLAttributes.title ?? undefined,\n \"data-summary\": HTMLAttributes.summary ?? undefined,\n \"data-source-block-id\": HTMLAttributes.sourceBlockId ?? undefined,\n }),\n ];\n },\n\n addNodeView() {\n return ReactNodeViewRenderer(RegistryBlockNodeView);\n },\n\n addProseMirrorPlugins() {\n return [\n new Plugin({\n key: dedupeKey,\n appendTransaction(transactions, _oldState, newState) {\n // Ignore our own re-mint, and skip when nothing changed the doc.\n if (\n transactions.some((transaction) =>\n transaction.getMeta(dedupeKey),\n ) ||\n !transactions.some((transaction) => transaction.docChanged)\n ) {\n return null;\n }\n return buildDedupeTransaction(newState);\n },\n }),\n new Plugin({\n key: keyboardGuardKey,\n props: {\n handleClickOn(view, _pos, node, nodePos, event, direct) {\n if (node.type.name !== nodeName || !direct) return false;\n if (\n event.target instanceof HTMLElement &&\n clickedInteractiveChild(event.target)\n ) {\n return false;\n }\n event.preventDefault();\n view.dispatch(\n view.state.tr.setSelection(\n NodeSelection.create(view.state.doc, nodePos),\n ),\n );\n view.focus();\n return true;\n },\n handleKeyDown(view, event) {\n if (!selectedRegistryBlock(view.state) || !isMutatingKey(event))\n return false;\n event.preventDefault();\n return true;\n },\n handleTextInput(view) {\n return selectedRegistryBlock(view.state);\n },\n handlePaste(view, event) {\n if (!selectedRegistryBlock(view.state)) return false;\n event.preventDefault();\n return true;\n },\n handleDOMEvents: {\n beforeinput(view, event) {\n if (!selectedRegistryBlock(view.state)) return false;\n const inputEvent = event as InputEvent;\n if (\n !inputEvent.inputType ||\n (!inputEvent.inputType.startsWith(\"insert\") &&\n inputEvent.inputType !== \"formatSetBlockTextDirection\")\n ) {\n return false;\n }\n event.preventDefault();\n return true;\n },\n },\n },\n }),\n ];\n },\n });\n}\n"]}
@@ -42,6 +42,14 @@ export interface SharedRichEditorProps {
42
42
  awareness?: Awareness | null;
43
43
  /** Current user info for the collaborative cursor label. */
44
44
  user?: RichMarkdownCollabUser | null;
45
+ /**
46
+ * Disable StarterKit's built-in undo/redo for a controlled (non-collab)
47
+ * editor whose host owns its own undo authority (see
48
+ * {@link CreateSharedEditorExtensionsOptions.disableHistory}). Ignored when a
49
+ * `ydoc` is present (Yjs always owns history then). Default `false`, so every
50
+ * existing embedder is unchanged.
51
+ */
52
+ disableHistory?: boolean;
45
53
  /** Override the slash-menu block command list. */
46
54
  slashItems?: SlashCommandItem[];
47
55
  /** Override the bubble-toolbar item builder. */
@@ -89,5 +97,5 @@ export interface SharedRichEditorProps {
89
97
  * With a `ydoc` it binds the framework collaboration stack; markdown stays the
90
98
  * canonical saved representation while the Y.Doc is transient live state.
91
99
  */
92
- export declare function SharedRichEditor({ value, onChange, onBlur, contentUpdatedAt, editable, dialect, preset, features, onImageUpload, extraExtensions, placeholder, className, editorClassName, ariaLabel, interactive, ydoc, awareness, user, slashItems, buildBubbleItems, getMarkdown, setContent, normalizeValue, shouldSeed, initialAppliedUpdatedAt, wrapperClassName, onEditorReady, }: SharedRichEditorProps): import("react/jsx-runtime").JSX.Element;
100
+ export declare function SharedRichEditor({ value, onChange, onBlur, contentUpdatedAt, editable, dialect, preset, features, onImageUpload, extraExtensions, placeholder, className, editorClassName, ariaLabel, interactive, ydoc, awareness, user, disableHistory, slashItems, buildBubbleItems, getMarkdown, setContent, normalizeValue, shouldSeed, initialAppliedUpdatedAt, wrapperClassName, onEditorReady, }: SharedRichEditorProps): import("react/jsx-runtime").JSX.Element;
93
101
  //# sourceMappingURL=SharedRichEditor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SharedRichEditor.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/SharedRichEditor.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SharedRichEditor.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/SharedRichEditor.tsx"],"names":[],"mappings":"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"}
@@ -17,7 +17,7 @@ import { BubbleToolbar } from "./BubbleToolbar.js";
17
17
  * With a `ydoc` it binds the framework collaboration stack; markdown stays the
18
18
  * canonical saved representation while the Y.Doc is transient live state.
19
19
  */
20
- export function SharedRichEditor({ value, onChange, onBlur, contentUpdatedAt, editable = true, dialect = "gfm", preset = "plan", features, onImageUpload = null, extraExtensions, placeholder = "Type '/' for commands...", className, editorClassName, ariaLabel, interactive = editable, ydoc = null, awareness = null, user = null, slashItems, buildBubbleItems, getMarkdown, setContent, normalizeValue, shouldSeed, initialAppliedUpdatedAt, wrapperClassName, onEditorReady, }) {
20
+ export function SharedRichEditor({ value, onChange, onBlur, contentUpdatedAt, editable = true, dialect = "gfm", preset = "plan", features, onImageUpload = null, extraExtensions, placeholder = "Type '/' for commands...", className, editorClassName, ariaLabel, interactive = editable, ydoc = null, awareness = null, user = null, disableHistory = false, slashItems, buildBubbleItems, getMarkdown, setContent, normalizeValue, shouldSeed, initialAppliedUpdatedAt, wrapperClassName, onEditorReady, }) {
21
21
  const readMarkdown = getMarkdown ?? getEditorMarkdown;
22
22
  const onChangeRef = useRef(onChange);
23
23
  const onBlurRef = useRef(onBlur);
@@ -31,6 +31,7 @@ export function SharedRichEditor({ value, onChange, onBlur, contentUpdatedAt, ed
31
31
  extraExtensions,
32
32
  onImageUpload,
33
33
  collab: ydoc ? { ydoc, awareness, user } : null,
34
+ disableHistory,
34
35
  }),
35
36
  // `preset` is retained in the dependency list so future preset-specific
36
37
  // schema branches re-create the editor; it is currently schema-neutral. The
@@ -49,6 +50,7 @@ export function SharedRichEditor({ value, onChange, onBlur, contentUpdatedAt, ed
49
50
  user?.name,
50
51
  user?.email,
51
52
  user?.color,
53
+ disableHistory,
52
54
  ]);
53
55
  const collab = !!ydoc;
54
56
  // The collab hook needs the editor, but useEditor's `onUpdate` needs the
@@ -1 +1 @@
1
- {"version":3,"file":"SharedRichEditor.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/SharedRichEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport { EditorContent, useEditor } from \"@tiptap/react\";\nimport type { Extension, Node, Mark } from \"@tiptap/core\";\nimport type { Doc as YDoc } from \"yjs\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport { cn } from \"../utils.js\";\nimport {\n createSharedEditorExtensions,\n type RichMarkdownDialect,\n type RichMarkdownEditorPreset,\n type RichMarkdownCollabUser,\n type SharedEditorFeatures,\n} from \"./extensions.js\";\nimport type { ImageUploadFn } from \"./ImageExtension.js\";\nimport {\n useCollabReconcile,\n getEditorMarkdown,\n type UseCollabReconcileResult,\n} from \"./useCollabReconcile.js\";\nimport { SlashCommandMenu, type SlashCommandItem } from \"./SlashCommandMenu.js\";\nimport { BubbleToolbar, type BubbleToolbarItem } from \"./BubbleToolbar.js\";\n\nexport interface SharedRichEditorProps {\n value: string;\n onChange: (markdown: string) => void;\n onBlur?: () => void;\n contentUpdatedAt?: string | null;\n editable?: boolean;\n dialect?: RichMarkdownDialect;\n preset?: RichMarkdownEditorPreset;\n /** Toggle individual base extensions (tables/tasks/link/codeBlock/image). */\n features?: SharedEditorFeatures;\n /**\n * Injectable image uploader for the shared image block. Used only when\n * `features.image` is on. Pass `uploadEditorImage` (the framework\n * `upload-image` action) for a real uploading image block.\n */\n onImageUpload?: ImageUploadFn | null;\n /**\n * App-specific extensions (Notion nodes, media, drag handles, comment\n * anchors, …) appended after the shared base schema.\n */\n extraExtensions?: Array<Extension | Node | Mark>;\n placeholder?: string;\n className?: string;\n editorClassName?: string;\n ariaLabel?: string;\n interactive?: boolean;\n /**\n * Yjs document for real-time multi-user editing. When provided, prose is\n * authored against the shared Y.Doc and mirrored back to `value` as markdown\n * (still the source of truth). When omitted, the editor is a plain controlled\n * `value`/`onChange` editor — the existing, non-collaborative behavior.\n */\n ydoc?: YDoc | null;\n /** Shared awareness instance for live cursors/presence. */\n awareness?: Awareness | null;\n /** Current user info for the collaborative cursor label. */\n user?: RichMarkdownCollabUser | null;\n /** Override the slash-menu block command list. */\n slashItems?: SlashCommandItem[];\n /** Override the bubble-toolbar item builder. */\n buildBubbleItems?: (\n editor: import(\"@tiptap/react\").Editor,\n toggleLink: () => void,\n ) => BubbleToolbarItem[];\n /**\n * Override how the editor's content is read into the canonical `value`.\n * Defaults to the tiptap-markdown storage reader. Apps with a custom on-disk\n * format (Content's NFM, the plan's `blocks[]` JSON) pass their serializer so\n * seed/reconcile/onChange all speak the same value space.\n */\n getMarkdown?: (editor: import(\"@tiptap/react\").Editor) => string;\n /** Override how the canonical `value` is applied into the editor (seed + reconcile). */\n setContent?: (\n editor: import(\"@tiptap/react\").Editor,\n value: string,\n options: { emitUpdate?: boolean; addToHistory?: boolean },\n ) => void;\n /** Canonicalize `value` for the echo / already-in-sync equality checks. */\n normalizeValue?: (value: string) => string;\n /** Override the empty-doc seed predicate (see {@link useCollabReconcile}). */\n shouldSeed?: (info: {\n value: string;\n currentMarkdown: string;\n fragmentLength: number;\n }) => boolean;\n /** Initial \"applied\" watermark (see {@link useCollabReconcile}). */\n initialAppliedUpdatedAt?: string | null;\n /** Extra class on the editor wrapper (e.g. a drag-handle `wrapperSelector` hook). */\n wrapperClassName?: string;\n /**\n * Fires once the editor instance exists. Lets a host capture the root\n * `editor.view` — e.g. to repaint the WHOLE document after a structural block\n * move (a column emptied and its container dissolved) that a surgical\n * per-region patch can't express, since the change is in the root doc itself.\n */\n onEditorReady?: (editor: import(\"@tiptap/react\").Editor) => void;\n}\n\n/**\n * The single shared rich markdown editor surface. Combines\n * {@link createSharedEditorExtensions} (schema + dialect-keyed markdown +\n * optional collab + app extras), {@link useCollabReconcile} (seed / reconcile /\n * lead-client logic), and the shared {@link SlashCommandMenu} +\n * {@link BubbleToolbar}.\n *\n * With no `ydoc` it is a controlled `value`/`onChange` single-user editor.\n * With a `ydoc` it binds the framework collaboration stack; markdown stays the\n * canonical saved representation while the Y.Doc is transient live state.\n */\nexport function SharedRichEditor({\n value,\n onChange,\n onBlur,\n contentUpdatedAt,\n editable = true,\n dialect = \"gfm\",\n preset = \"plan\",\n features,\n onImageUpload = null,\n extraExtensions,\n placeholder = \"Type '/' for commands...\",\n className,\n editorClassName,\n ariaLabel,\n interactive = editable,\n ydoc = null,\n awareness = null,\n user = null,\n slashItems,\n buildBubbleItems,\n getMarkdown,\n setContent,\n normalizeValue,\n shouldSeed,\n initialAppliedUpdatedAt,\n wrapperClassName,\n onEditorReady,\n}: SharedRichEditorProps) {\n const readMarkdown = getMarkdown ?? getEditorMarkdown;\n const onChangeRef = useRef(onChange);\n const onBlurRef = useRef(onBlur);\n onChangeRef.current = onChange;\n onBlurRef.current = onBlur;\n\n const extensions = useMemo(\n () =>\n createSharedEditorExtensions({\n dialect,\n preset,\n placeholder,\n features,\n extraExtensions,\n onImageUpload,\n collab: ydoc ? { ydoc, awareness, user } : null,\n }),\n // `preset` is retained in the dependency list so future preset-specific\n // schema branches re-create the editor; it is currently schema-neutral. The\n // collab inputs are identity-stable per block (one Y.Doc per docId), so they\n // only change on a genuine doc switch — exactly when the editor must\n // re-create to rebind Collaboration.\n [\n dialect,\n placeholder,\n preset,\n features,\n extraExtensions,\n onImageUpload,\n ydoc,\n awareness,\n user?.name,\n user?.email,\n user?.color,\n ],\n );\n\n const collab = !!ydoc;\n\n // The collab hook needs the editor, but useEditor's `onUpdate` needs the\n // hook's guards. Break the cycle with a ref: `onUpdate` reads the guards\n // through `guardsRef`, which is populated right after the hook runs below.\n // `onUpdate` only ever fires after the editor exists, by which point the ref\n // holds the real guards.\n const guardsRef = useRef<UseCollabReconcileResult | null>(null);\n\n const editor = useEditor({\n extensions,\n // With Collaboration active the prose is owned by the shared Y.XmlFragment.\n // Seeding `content` here too would make the editor initialize from BOTH the\n // prop and the Y.Doc, firing a spurious initial update that could autosave a\n // stale value over newer SQL. The lead-client seed effect populates an empty\n // doc instead. Non-collab editors keep initializing from `value`.\n // With Collaboration the Y.Doc owns the prose (seeded by the lead client).\n // With a custom `setContent` the reconcile seeds the editor too (the raw\n // `value` is not directly settable content — e.g. the plan's blocks JSON),\n // so only the plain markdown path seeds from `value` here.\n content: collab || setContent ? undefined : value,\n editable,\n editorProps: {\n attributes: {\n class: cn(\"an-rich-md-prose\", editorClassName),\n role: ariaLabel ? \"textbox\" : undefined,\n \"aria-label\": ariaLabel,\n },\n },\n onUpdate: ({ editor, transaction }) => {\n const guards = guardsRef.current;\n if (!guards || guards.shouldIgnoreUpdate(transaction)) return;\n try {\n const markdown = readMarkdown(editor);\n if (!guards.registerEmitted(markdown)) return;\n queueMicrotask(() => onChangeRef.current(markdown));\n } catch (error) {\n console.error(\"Markdown serialization error:\", error);\n }\n },\n onBlur: () => {\n onBlurRef.current?.();\n },\n });\n\n const collabState = useCollabReconcile({\n editor,\n ydoc,\n awareness,\n value,\n contentUpdatedAt,\n editable,\n getMarkdown: readMarkdown,\n setContent,\n normalizeValue,\n shouldSeed,\n initialAppliedUpdatedAt,\n });\n guardsRef.current = collabState;\n\n useEffect(() => {\n if (!editor || editor.isDestroyed) return;\n editor.setEditable(editable);\n }, [editable, editor]);\n\n useEffect(() => {\n if (editor) onEditorReady?.(editor);\n }, [editor, onEditorReady]);\n\n useEffect(() => () => editor?.destroy(), [editor]);\n\n const handleWrapperClick = (event: React.MouseEvent<HTMLDivElement>) => {\n if (!editable || !editor || editor.isDestroyed) return;\n const target = event.target as HTMLElement;\n if (\n target.classList.contains(\"an-rich-md-wrapper\") ||\n target.classList.contains(\"an-rich-md-clickable\")\n ) {\n editor.chain().focus(\"end\").run();\n }\n };\n\n if (!editor) {\n return (\n <div\n className={cn(\"an-rich-md-wrapper an-rich-md-loading\", className)}\n data-plan-interactive={interactive ? true : undefined}\n />\n );\n }\n\n return (\n <div\n className={cn(\n \"an-rich-md-wrapper an-rich-md-clickable\",\n !editable && \"an-rich-md-wrapper--readonly\",\n wrapperClassName,\n className,\n )}\n onClick={handleWrapperClick}\n data-plan-interactive={interactive ? true : undefined}\n >\n {editable ? (\n <BubbleToolbar editor={editor} buildItems={buildBubbleItems} />\n ) : null}\n {editable ? (\n <SlashCommandMenu editor={editor} items={slashItems} />\n ) : null}\n <EditorContent editor={editor} />\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"SharedRichEditor.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/SharedRichEditor.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useMemo, useRef } from \"react\";\nimport { EditorContent, useEditor } from \"@tiptap/react\";\nimport type { Extension, Node, Mark } from \"@tiptap/core\";\nimport type { Doc as YDoc } from \"yjs\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport { cn } from \"../utils.js\";\nimport {\n createSharedEditorExtensions,\n type RichMarkdownDialect,\n type RichMarkdownEditorPreset,\n type RichMarkdownCollabUser,\n type SharedEditorFeatures,\n} from \"./extensions.js\";\nimport type { ImageUploadFn } from \"./ImageExtension.js\";\nimport {\n useCollabReconcile,\n getEditorMarkdown,\n type UseCollabReconcileResult,\n} from \"./useCollabReconcile.js\";\nimport { SlashCommandMenu, type SlashCommandItem } from \"./SlashCommandMenu.js\";\nimport { BubbleToolbar, type BubbleToolbarItem } from \"./BubbleToolbar.js\";\n\nexport interface SharedRichEditorProps {\n value: string;\n onChange: (markdown: string) => void;\n onBlur?: () => void;\n contentUpdatedAt?: string | null;\n editable?: boolean;\n dialect?: RichMarkdownDialect;\n preset?: RichMarkdownEditorPreset;\n /** Toggle individual base extensions (tables/tasks/link/codeBlock/image). */\n features?: SharedEditorFeatures;\n /**\n * Injectable image uploader for the shared image block. Used only when\n * `features.image` is on. Pass `uploadEditorImage` (the framework\n * `upload-image` action) for a real uploading image block.\n */\n onImageUpload?: ImageUploadFn | null;\n /**\n * App-specific extensions (Notion nodes, media, drag handles, comment\n * anchors, …) appended after the shared base schema.\n */\n extraExtensions?: Array<Extension | Node | Mark>;\n placeholder?: string;\n className?: string;\n editorClassName?: string;\n ariaLabel?: string;\n interactive?: boolean;\n /**\n * Yjs document for real-time multi-user editing. When provided, prose is\n * authored against the shared Y.Doc and mirrored back to `value` as markdown\n * (still the source of truth). When omitted, the editor is a plain controlled\n * `value`/`onChange` editor — the existing, non-collaborative behavior.\n */\n ydoc?: YDoc | null;\n /** Shared awareness instance for live cursors/presence. */\n awareness?: Awareness | null;\n /** Current user info for the collaborative cursor label. */\n user?: RichMarkdownCollabUser | null;\n /**\n * Disable StarterKit's built-in undo/redo for a controlled (non-collab)\n * editor whose host owns its own undo authority (see\n * {@link CreateSharedEditorExtensionsOptions.disableHistory}). Ignored when a\n * `ydoc` is present (Yjs always owns history then). Default `false`, so every\n * existing embedder is unchanged.\n */\n disableHistory?: boolean;\n /** Override the slash-menu block command list. */\n slashItems?: SlashCommandItem[];\n /** Override the bubble-toolbar item builder. */\n buildBubbleItems?: (\n editor: import(\"@tiptap/react\").Editor,\n toggleLink: () => void,\n ) => BubbleToolbarItem[];\n /**\n * Override how the editor's content is read into the canonical `value`.\n * Defaults to the tiptap-markdown storage reader. Apps with a custom on-disk\n * format (Content's NFM, the plan's `blocks[]` JSON) pass their serializer so\n * seed/reconcile/onChange all speak the same value space.\n */\n getMarkdown?: (editor: import(\"@tiptap/react\").Editor) => string;\n /** Override how the canonical `value` is applied into the editor (seed + reconcile). */\n setContent?: (\n editor: import(\"@tiptap/react\").Editor,\n value: string,\n options: { emitUpdate?: boolean; addToHistory?: boolean },\n ) => void;\n /** Canonicalize `value` for the echo / already-in-sync equality checks. */\n normalizeValue?: (value: string) => string;\n /** Override the empty-doc seed predicate (see {@link useCollabReconcile}). */\n shouldSeed?: (info: {\n value: string;\n currentMarkdown: string;\n fragmentLength: number;\n }) => boolean;\n /** Initial \"applied\" watermark (see {@link useCollabReconcile}). */\n initialAppliedUpdatedAt?: string | null;\n /** Extra class on the editor wrapper (e.g. a drag-handle `wrapperSelector` hook). */\n wrapperClassName?: string;\n /**\n * Fires once the editor instance exists. Lets a host capture the root\n * `editor.view` — e.g. to repaint the WHOLE document after a structural block\n * move (a column emptied and its container dissolved) that a surgical\n * per-region patch can't express, since the change is in the root doc itself.\n */\n onEditorReady?: (editor: import(\"@tiptap/react\").Editor) => void;\n}\n\n/**\n * The single shared rich markdown editor surface. Combines\n * {@link createSharedEditorExtensions} (schema + dialect-keyed markdown +\n * optional collab + app extras), {@link useCollabReconcile} (seed / reconcile /\n * lead-client logic), and the shared {@link SlashCommandMenu} +\n * {@link BubbleToolbar}.\n *\n * With no `ydoc` it is a controlled `value`/`onChange` single-user editor.\n * With a `ydoc` it binds the framework collaboration stack; markdown stays the\n * canonical saved representation while the Y.Doc is transient live state.\n */\nexport function SharedRichEditor({\n value,\n onChange,\n onBlur,\n contentUpdatedAt,\n editable = true,\n dialect = \"gfm\",\n preset = \"plan\",\n features,\n onImageUpload = null,\n extraExtensions,\n placeholder = \"Type '/' for commands...\",\n className,\n editorClassName,\n ariaLabel,\n interactive = editable,\n ydoc = null,\n awareness = null,\n user = null,\n disableHistory = false,\n slashItems,\n buildBubbleItems,\n getMarkdown,\n setContent,\n normalizeValue,\n shouldSeed,\n initialAppliedUpdatedAt,\n wrapperClassName,\n onEditorReady,\n}: SharedRichEditorProps) {\n const readMarkdown = getMarkdown ?? getEditorMarkdown;\n const onChangeRef = useRef(onChange);\n const onBlurRef = useRef(onBlur);\n onChangeRef.current = onChange;\n onBlurRef.current = onBlur;\n\n const extensions = useMemo(\n () =>\n createSharedEditorExtensions({\n dialect,\n preset,\n placeholder,\n features,\n extraExtensions,\n onImageUpload,\n collab: ydoc ? { ydoc, awareness, user } : null,\n disableHistory,\n }),\n // `preset` is retained in the dependency list so future preset-specific\n // schema branches re-create the editor; it is currently schema-neutral. The\n // collab inputs are identity-stable per block (one Y.Doc per docId), so they\n // only change on a genuine doc switch — exactly when the editor must\n // re-create to rebind Collaboration.\n [\n dialect,\n placeholder,\n preset,\n features,\n extraExtensions,\n onImageUpload,\n ydoc,\n awareness,\n user?.name,\n user?.email,\n user?.color,\n disableHistory,\n ],\n );\n\n const collab = !!ydoc;\n\n // The collab hook needs the editor, but useEditor's `onUpdate` needs the\n // hook's guards. Break the cycle with a ref: `onUpdate` reads the guards\n // through `guardsRef`, which is populated right after the hook runs below.\n // `onUpdate` only ever fires after the editor exists, by which point the ref\n // holds the real guards.\n const guardsRef = useRef<UseCollabReconcileResult | null>(null);\n\n const editor = useEditor({\n extensions,\n // With Collaboration active the prose is owned by the shared Y.XmlFragment.\n // Seeding `content` here too would make the editor initialize from BOTH the\n // prop and the Y.Doc, firing a spurious initial update that could autosave a\n // stale value over newer SQL. The lead-client seed effect populates an empty\n // doc instead. Non-collab editors keep initializing from `value`.\n // With Collaboration the Y.Doc owns the prose (seeded by the lead client).\n // With a custom `setContent` the reconcile seeds the editor too (the raw\n // `value` is not directly settable content — e.g. the plan's blocks JSON),\n // so only the plain markdown path seeds from `value` here.\n content: collab || setContent ? undefined : value,\n editable,\n editorProps: {\n attributes: {\n class: cn(\"an-rich-md-prose\", editorClassName),\n role: ariaLabel ? \"textbox\" : undefined,\n \"aria-label\": ariaLabel,\n },\n },\n onUpdate: ({ editor, transaction }) => {\n const guards = guardsRef.current;\n if (!guards || guards.shouldIgnoreUpdate(transaction)) return;\n try {\n const markdown = readMarkdown(editor);\n if (!guards.registerEmitted(markdown)) return;\n queueMicrotask(() => onChangeRef.current(markdown));\n } catch (error) {\n console.error(\"Markdown serialization error:\", error);\n }\n },\n onBlur: () => {\n onBlurRef.current?.();\n },\n });\n\n const collabState = useCollabReconcile({\n editor,\n ydoc,\n awareness,\n value,\n contentUpdatedAt,\n editable,\n getMarkdown: readMarkdown,\n setContent,\n normalizeValue,\n shouldSeed,\n initialAppliedUpdatedAt,\n });\n guardsRef.current = collabState;\n\n useEffect(() => {\n if (!editor || editor.isDestroyed) return;\n editor.setEditable(editable);\n }, [editable, editor]);\n\n useEffect(() => {\n if (editor) onEditorReady?.(editor);\n }, [editor, onEditorReady]);\n\n useEffect(() => () => editor?.destroy(), [editor]);\n\n const handleWrapperClick = (event: React.MouseEvent<HTMLDivElement>) => {\n if (!editable || !editor || editor.isDestroyed) return;\n const target = event.target as HTMLElement;\n if (\n target.classList.contains(\"an-rich-md-wrapper\") ||\n target.classList.contains(\"an-rich-md-clickable\")\n ) {\n editor.chain().focus(\"end\").run();\n }\n };\n\n if (!editor) {\n return (\n <div\n className={cn(\"an-rich-md-wrapper an-rich-md-loading\", className)}\n data-plan-interactive={interactive ? true : undefined}\n />\n );\n }\n\n return (\n <div\n className={cn(\n \"an-rich-md-wrapper an-rich-md-clickable\",\n !editable && \"an-rich-md-wrapper--readonly\",\n wrapperClassName,\n className,\n )}\n onClick={handleWrapperClick}\n data-plan-interactive={interactive ? true : undefined}\n >\n {editable ? (\n <BubbleToolbar editor={editor} buildItems={buildBubbleItems} />\n ) : null}\n {editable ? (\n <SlashCommandMenu editor={editor} items={slashItems} />\n ) : null}\n <EditorContent editor={editor} />\n </div>\n );\n}\n"]}
@@ -112,6 +112,18 @@ export interface CreateSharedEditorExtensionsOptions {
112
112
  extraExtensions?: Array<Extension | Node | Mark>;
113
113
  /** Optional collaborative-editing wiring. */
114
114
  collab?: SharedEditorCollab | null;
115
+ /**
116
+ * Disable StarterKit's built-in undo/redo (prosemirror-history) for a
117
+ * controlled (non-collab) editor whose host owns its own undo authority.
118
+ * Default `false`. When a `collab.ydoc` is present, undo/redo is ALWAYS
119
+ * disabled regardless of this flag (Yjs owns history); this flag is the
120
+ * non-collab equivalent. The plan editor sets it so a single app-level undo
121
+ * stack (over the authoritative `blocks[]` tree, which includes block data
122
+ * the ProseMirror doc never stores) is the sole cmd+z authority — otherwise
123
+ * PM history and the app stack would both fire, and PM history can't see
124
+ * block-option edits at all.
125
+ */
126
+ disableHistory?: boolean;
115
127
  /**
116
128
  * Injectable image uploader for the shared image block. Only used when
117
129
  * `features.image` is on. Turns a picked / pasted / dropped image File into a
@@ -162,5 +174,5 @@ export declare const MARKDOWN_DIALECT_CONFIG: Record<RichMarkdownDialect, Parame
162
174
  * `extraExtensions` — so it shares the StarterKit base + the collab wiring while
163
175
  * owning its byte-identical NFM serializer.
164
176
  */
165
- export declare function createSharedEditorExtensions({ dialect, preset: _preset, placeholder, features, starterKit, markdown, extraExtensions, collab, onImageUpload, }?: CreateSharedEditorExtensionsOptions): Array<Extension | Node | Mark>;
177
+ export declare function createSharedEditorExtensions({ dialect, preset: _preset, placeholder, features, starterKit, markdown, extraExtensions, collab, onImageUpload, disableHistory, }?: CreateSharedEditorExtensionsOptions): Array<Extension | Node | Mark>;
166
178
  //# sourceMappingURL=extensions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/extensions.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/extensions.ts"],"names":[],"mappings":"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"}
@@ -119,7 +119,7 @@ const DEFAULT_FEATURES = {
119
119
  export function createSharedEditorExtensions({ dialect = "gfm",
120
120
  // `preset` is accepted and forwarded for future preset-specific schema
121
121
  // branches; it is currently schema-neutral.
122
- preset: _preset = "plan", placeholder = "Type '/' for commands...", features, starterKit, markdown, extraExtensions = [], collab = null, onImageUpload = null, } = {}) {
122
+ preset: _preset = "plan", placeholder = "Type '/' for commands...", features, starterKit, markdown, extraExtensions = [], collab = null, onImageUpload = null, disableHistory = false, } = {}) {
123
123
  const feat = { ...DEFAULT_FEATURES, ...(features ?? {}) };
124
124
  const ydoc = collab?.ydoc ?? null;
125
125
  const awareness = collab?.awareness ?? null;
@@ -134,7 +134,9 @@ preset: _preset = "plan", placeholder = "Type '/' for commands...", features, st
134
134
  dropcursor: { color: "hsl(var(--ring))", width: 2 },
135
135
  // Yjs owns undo/redo when Collaboration is active; the StarterKit history
136
136
  // plugin and the CRDT cannot both track undo without corrupting state.
137
- ...(ydoc ? { undoRedo: false } : {}),
137
+ // `disableHistory` is the non-collab equivalent: a controlled editor whose
138
+ // host (e.g. the plan editor) owns a single app-level undo authority.
139
+ ...(ydoc || disableHistory ? { undoRedo: false } : {}),
138
140
  // App overrides last so embedders can disable replaced nodes (paragraph,
139
141
  // blockquote, code block) or swap the dropcursor while keeping the shared
140
142
  // base + the collab undo/redo gating above.
@@ -1 +1 @@
1
- {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/extensions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Extension, Node, Mark } from \"@tiptap/core\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport type { StarterKitOptions } from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport Link from \"@tiptap/extension-link\";\nimport TaskList from \"@tiptap/extension-task-list\";\nimport TaskItem from \"@tiptap/extension-task-item\";\nimport { Table } from \"@tiptap/extension-table\";\nimport { TableRow } from \"@tiptap/extension-table-row\";\nimport { TableCell } from \"@tiptap/extension-table-cell\";\nimport { TableHeader } from \"@tiptap/extension-table-header\";\nimport { Markdown } from \"tiptap-markdown\";\nimport { createLowlight } from \"lowlight\";\nimport bash from \"highlight.js/lib/languages/bash\";\nimport css from \"highlight.js/lib/languages/css\";\nimport javascript from \"highlight.js/lib/languages/javascript\";\nimport json from \"highlight.js/lib/languages/json\";\nimport markdown from \"highlight.js/lib/languages/markdown\";\nimport python from \"highlight.js/lib/languages/python\";\nimport sql from \"highlight.js/lib/languages/sql\";\nimport typescript from \"highlight.js/lib/languages/typescript\";\nimport xml from \"highlight.js/lib/languages/xml\";\nimport yaml from \"highlight.js/lib/languages/yaml\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCaret from \"@tiptap/extension-collaboration-caret\";\n\n/**\n * Shared lowlight instance for the editor's syntax-highlighted code blocks. A\n * curated grammar set (aliases like ts/tsx, js/jsx, html, sh, py, yml, md come\n * for free from each grammar) keeps the editor bundle lean while matching the\n * languages the read-side Shiki surfaces (`code-tabs`) support. highlight.js is\n * synchronous, which is what a live ProseMirror editor needs — Shiki is async\n * and only used for read-only render paths.\n */\nconst codeLowlight = createLowlight({\n bash,\n css,\n javascript,\n json,\n markdown,\n python,\n sql,\n typescript,\n xml,\n yaml,\n});\nimport type { Doc as YDoc } from \"yjs\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport { createImageExtension, type ImageUploadFn } from \"./ImageExtension.js\";\nimport { createCodeBlockNode } from \"./CodeBlockNode.js\";\n\n/**\n * Markdown dialect the editor parses/serializes.\n *\n * - `gfm` — GitHub-Flavored Markdown. No raw HTML passthrough. The byte-stable\n * serialization used by Plans (see RichMarkdownEditor.roundtrip.spec.ts).\n * - `nfm` — the Notion-Flavored Markdown superset used by the Content editor,\n * which opts into inline HTML so Notion-specific blocks round-trip.\n */\nexport type RichMarkdownDialect = \"gfm\" | \"nfm\";\n\n/**\n * Editor preset. Schema-neutral today (both presets share the base schema),\n * but threaded through so an app can branch schema/behavior per preset without\n * a new factory. The collab/markdown wiring is preset-independent.\n */\nexport type RichMarkdownEditorPreset = \"plan\" | \"content\";\n\n/** User info used to label this client's collaborative cursor. */\nexport interface RichMarkdownCollabUser {\n name: string;\n color: string;\n email?: string;\n}\n\n/** Optional collaborative-editing inputs for the shared editor. */\nexport interface SharedEditorCollab {\n /**\n * Yjs document for collaborative editing. When present the editor binds the\n * shared {@link Collaboration} (+ {@link CollaborationCaret} when awareness\n * is set) extensions and StarterKit's built-in undo/redo is disabled (Yjs\n * owns history). When absent the editor is a controlled `value`/`onChange`\n * editor.\n */\n ydoc?: YDoc | null;\n /** Shared awareness instance for live multi-user cursors. */\n awareness?: Awareness | null;\n /** Current user info for the collaborative cursor label. */\n user?: RichMarkdownCollabUser | null;\n}\n\n/** Toggle the optional base extensions on/off per app. All default to `true`. */\nexport interface SharedEditorFeatures {\n /** GFM pipe tables (Table + TableRow + TableHeader + TableCell). */\n tables?: boolean;\n /** Task / checklist lists (TaskList + TaskItem). */\n tasks?: boolean;\n /** Inline links (the `Link` mark). When off, links fall back to plain text. */\n link?: boolean;\n /** Fenced code blocks. Disabling lets an app inject its own code-block node. */\n codeBlock?: boolean;\n /**\n * The built-in {@link Placeholder} extension. Default `true`. Apps that need a\n * bespoke placeholder resolver (per-node-type labels, ancestor-aware text)\n * disable this and supply their own Placeholder via `extraExtensions`.\n */\n placeholder?: boolean;\n /**\n * The built-in dialect-keyed {@link Markdown} serializer. Default `true`.\n * Apps with a custom serializer (e.g. Content's NFM converter, which does NOT\n * round-trip through tiptap-markdown's storage) disable this and own the\n * serialize/parse pipeline themselves. The Markdown extension is still added\n * so paste/clipboard transforms work — disable it only when supplying your own\n * Markdown configuration via the {@link CreateSharedEditorExtensionsOptions.markdown}\n * option instead.\n */\n markdown?: boolean;\n /**\n * The shared block-level image node (`@tiptap/extension-image`). Default\n * `false` so existing embedders are unchanged. When `true`, images\n * serialize to GFM `![alt](src)` (source-syncable) and — when an\n * {@link CreateSharedEditorExtensionsOptions.onImageUpload} function is\n * supplied — paste / drop of local image files uploads through it. Content\n * leaves this off and injects its own richer image node via\n * `extraExtensions`, so the two never collide.\n */\n image?: boolean;\n}\n\nexport interface CreateSharedEditorExtensionsOptions {\n /** Markdown dialect; selects the keyed {@link Markdown} config. */\n dialect?: RichMarkdownDialect;\n /** Preset hook (schema-neutral today). */\n preset?: RichMarkdownEditorPreset;\n /** Empty-block placeholder text (headings get their own labels). */\n placeholder?: string;\n /** Toggle individual base extensions. */\n features?: SharedEditorFeatures;\n /**\n * Extra StarterKit options merged over the shared defaults. Lets an app turn\n * off StarterKit nodes it replaces (Content swaps in its own paragraph /\n * blockquote / code block) or pass a custom dropcursor, while still sharing\n * the rest of the StarterKit base + the collab undo/redo gating. The shared\n * defaults (`heading` levels 1-4, `link: false`, the default dropcursor, and\n * `undoRedo: false` in collab mode) are applied first and can be overridden\n * key-by-key here.\n */\n starterKit?: Partial<StarterKitOptions>;\n /**\n * Custom {@link Markdown} configuration. Replaces the dialect-keyed config from\n * {@link MARKDOWN_DIALECT_CONFIG} when provided. Only used when\n * `features.markdown !== false`; apps that own the whole markdown pipeline (no\n * tiptap-markdown serialization at all) should set `features.markdown: false`\n * and add their own configured Markdown extension via `extraExtensions`.\n */\n markdown?: Parameters<typeof Markdown.configure>[0];\n /**\n * App-specific extensions (Notion nodes, media, drag handles, comment\n * anchors, etc.) appended LAST so they bind over the shared base schema and\n * the optional Collaboration extensions still mount after them.\n */\n extraExtensions?: Array<Extension | Node | Mark>;\n /** Optional collaborative-editing wiring. */\n collab?: SharedEditorCollab | null;\n /**\n * Injectable image uploader for the shared image block. Only used when\n * `features.image` is on. Turns a picked / pasted / dropped image File into a\n * hosted `{ src, alt? }`. Plans pass `uploadEditorImage` (the framework\n * `upload-image` action). When omitted, the image block still renders and\n * round-trips `![alt](src)` markdown but cannot ingest local files.\n */\n onImageUpload?: ImageUploadFn | null;\n}\n\n/**\n * tiptap-markdown configuration, keyed by dialect. This is the single source of\n * truth for how each dialect parses/serializes markdown so the editor component\n * and the round-trip fidelity test can never drift apart.\n *\n * tiptap-markdown re-serializes the whole document on every edit, so the goal\n * for GFM is `serialize(parse(markdown)) === markdown` for the markdown plans\n * actually contain. We deliberately keep tiptap-markdown's own defaults\n * (`bulletListMarker: \"-\"`, `tightLists: true`, `linkify: false`,\n * `breaks: false`) because those produce the most byte-stable GFM. See\n * RichMarkdownEditor.roundtrip.spec.ts for the pinned corpus.\n *\n * NFM (Content) opts into inline HTML passthrough (`html: true`) so\n * Notion-specific blocks survive a markdown round-trip; the rest mirrors the\n * Content editor's existing `Markdown.configure` call.\n */\nexport const MARKDOWN_DIALECT_CONFIG: Record<\n RichMarkdownDialect,\n Parameters<typeof Markdown.configure>[0]\n> = {\n gfm: {\n // GFM plans are the common case and must never gain raw HTML as a second\n // representation.\n html: false,\n // Keep tiptap-markdown's defaults that minimise first-edit normalisation\n // churn (see roundtrip spec). Listed explicitly so the contract is\n // self-documenting rather than relying on the package defaults.\n bulletListMarker: \"-\",\n tightLists: true,\n linkify: false,\n breaks: false,\n transformPastedText: true,\n transformCopiedText: true,\n },\n nfm: {\n // NFM is a superset that allows inline HTML so Notion blocks round-trip.\n html: true,\n transformPastedText: true,\n transformCopiedText: true,\n },\n};\n\nconst DEFAULT_FEATURES: Required<SharedEditorFeatures> = {\n tables: true,\n tasks: true,\n link: true,\n codeBlock: true,\n placeholder: true,\n markdown: true,\n // Off by default: only Plans opt in today. Content injects its own richer\n // image node via `extraExtensions` and must not get a second `image` node.\n image: false,\n};\n\n/**\n * The ONE editor extension factory shared by every embedder (Plans today,\n * Content next). It assembles the base Tiptap schema (StarterKit + Placeholder\n * + Link + tasks + tables + code block), the dialect-keyed {@link Markdown}\n * serializer, the optional Collaboration stack, and finally any app-specific\n * `extraExtensions`.\n *\n * Ordering matters:\n * 1. Base schema (StarterKit first so its nodes/marks register; `starterKit`\n * overrides let an app disable replaced nodes / swap the dropcursor).\n * 2. dialect-keyed Markdown serializer (suppressible via `features.markdown`\n * for apps that own the whole serialize/parse pipeline, e.g. Content's NFM).\n * 3. `extraExtensions` (Notion/media/etc.) — appended before Collaboration so\n * apps can extend the schema and Collaboration still binds over the full\n * schema.\n * 4. Collaboration (+ CollaborationCaret) LAST so they bind over everything.\n *\n * Content (the NFM editor) drives this factory with `features.placeholder` and\n * `features.markdown` off, `features.tasks/tables/link` off where it ships its\n * own, a `starterKit` override disabling paragraph/blockquote/codeBlock, and all\n * Notion/media/fidelity nodes + its own Markdown(NFM)/Placeholder via\n * `extraExtensions` — so it shares the StarterKit base + the collab wiring while\n * owning its byte-identical NFM serializer.\n */\nexport function createSharedEditorExtensions({\n dialect = \"gfm\",\n // `preset` is accepted and forwarded for future preset-specific schema\n // branches; it is currently schema-neutral.\n preset: _preset = \"plan\",\n placeholder = \"Type '/' for commands...\",\n features,\n starterKit,\n markdown,\n extraExtensions = [],\n collab = null,\n onImageUpload = null,\n}: CreateSharedEditorExtensionsOptions = {}): Array<Extension | Node | Mark> {\n const feat = { ...DEFAULT_FEATURES, ...(features ?? {}) };\n const ydoc = collab?.ydoc ?? null;\n const awareness = collab?.awareness ?? null;\n const user = collab?.user ?? null;\n\n const exts: Array<Extension | Node | Mark> = [\n StarterKit.configure({\n heading: { levels: [1, 2, 3, 4] },\n link: false,\n // StarterKit's plain code block is always disabled; when enabled we add the\n // syntax-highlighting `CodeBlockLowlight` (same `codeBlock` node) below.\n codeBlock: false,\n dropcursor: { color: \"hsl(var(--ring))\", width: 2 },\n // Yjs owns undo/redo when Collaboration is active; the StarterKit history\n // plugin and the CRDT cannot both track undo without corrupting state.\n ...(ydoc ? { undoRedo: false } : {}),\n // App overrides last so embedders can disable replaced nodes (paragraph,\n // blockquote, code block) or swap the dropcursor while keeping the shared\n // base + the collab undo/redo gating above.\n ...(starterKit ?? {}),\n }),\n ];\n\n // Notion-style syntax-highlighted code block (replaces StarterKit's plain one)\n // only when the embedder opts in via `features.codeBlock`. The shared node adds\n // a language picker header (Auto-detects by default) over `CodeBlockLowlight`,\n // so fenced markdown still round-trips byte-identically. Content disables this\n // and ships its own code node, so this affects Plans (and future opt-in apps)\n // alone.\n if (feat.codeBlock) {\n exts.push(createCodeBlockNode({ lowlight: codeLowlight }));\n }\n\n if (feat.placeholder) {\n exts.push(\n Placeholder.configure({\n placeholder: ({ node }) => {\n if (node.type.name === \"heading\") {\n const level = node.attrs.level;\n if (level === 1) return \"Heading 1\";\n if (level === 2) return \"Heading 2\";\n if (level === 3) return \"Heading 3\";\n return \"Heading 4\";\n }\n return placeholder;\n },\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n }),\n );\n }\n\n if (feat.link) {\n exts.push(\n Link.configure({\n openOnClick: false,\n HTMLAttributes: { class: \"an-rich-md-link\" },\n }),\n );\n }\n\n if (feat.tasks) {\n exts.push(\n TaskList.configure({\n HTMLAttributes: { class: \"an-rich-md-task-list\" },\n }),\n TaskItem.configure({ nested: true }),\n );\n }\n\n if (feat.tables) {\n exts.push(\n Table.configure({\n resizable: false,\n HTMLAttributes: { class: \"an-rich-md-table\" },\n }),\n TableRow,\n TableHeader,\n TableCell,\n );\n }\n\n if (feat.markdown) {\n exts.push(Markdown.configure(markdown ?? MARKDOWN_DIALECT_CONFIG[dialect]));\n }\n\n // Shared block-level image node. The node is named `image`, so when\n // `features.markdown` is on, tiptap-markdown serializes it through its\n // built-in `defaultMarkdownSerializer.nodes.image` fallback → `![alt](src)`\n // (no width-as-HTML override here, so GFM stays byte-stable and\n // source-syncable). With an `onImageUpload` it accepts paste/drop uploads.\n if (feat.image) {\n exts.push(createImageExtension({ onImageUpload }));\n }\n\n // App-specific extensions (Notion/media/drag handles/comments). Appended\n // before Collaboration so they can extend the schema and Collaboration binds\n // over the full set.\n if (extraExtensions.length > 0) {\n exts.push(...extraExtensions);\n }\n\n // Collaborative editing via the shared Y.Doc. Markdown stays the canonical\n // saved representation (onChange serializes it); the Y.Doc is transient live\n // state only. Appended last so it binds over the configured schema above.\n if (ydoc) {\n exts.push(Collaboration.configure({ document: ydoc }));\n // Live multi-user cursors. Only mounted alongside a Y.Doc so the standalone\n // controlled editor (today's plan/content behavior) is untouched.\n if (awareness) {\n exts.push(\n CollaborationCaret.configure({\n provider: { awareness },\n user: user ?? { name: \"Anonymous\", color: \"#999\" },\n }),\n );\n }\n }\n\n return exts;\n}\n"]}
1
+ {"version":3,"file":"extensions.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/extensions.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Extension, Node, Mark } from \"@tiptap/core\";\nimport StarterKit from \"@tiptap/starter-kit\";\nimport type { StarterKitOptions } from \"@tiptap/starter-kit\";\nimport Placeholder from \"@tiptap/extension-placeholder\";\nimport Link from \"@tiptap/extension-link\";\nimport TaskList from \"@tiptap/extension-task-list\";\nimport TaskItem from \"@tiptap/extension-task-item\";\nimport { Table } from \"@tiptap/extension-table\";\nimport { TableRow } from \"@tiptap/extension-table-row\";\nimport { TableCell } from \"@tiptap/extension-table-cell\";\nimport { TableHeader } from \"@tiptap/extension-table-header\";\nimport { Markdown } from \"tiptap-markdown\";\nimport { createLowlight } from \"lowlight\";\nimport bash from \"highlight.js/lib/languages/bash\";\nimport css from \"highlight.js/lib/languages/css\";\nimport javascript from \"highlight.js/lib/languages/javascript\";\nimport json from \"highlight.js/lib/languages/json\";\nimport markdown from \"highlight.js/lib/languages/markdown\";\nimport python from \"highlight.js/lib/languages/python\";\nimport sql from \"highlight.js/lib/languages/sql\";\nimport typescript from \"highlight.js/lib/languages/typescript\";\nimport xml from \"highlight.js/lib/languages/xml\";\nimport yaml from \"highlight.js/lib/languages/yaml\";\nimport Collaboration from \"@tiptap/extension-collaboration\";\nimport CollaborationCaret from \"@tiptap/extension-collaboration-caret\";\n\n/**\n * Shared lowlight instance for the editor's syntax-highlighted code blocks. A\n * curated grammar set (aliases like ts/tsx, js/jsx, html, sh, py, yml, md come\n * for free from each grammar) keeps the editor bundle lean while matching the\n * languages the read-side Shiki surfaces (`code-tabs`) support. highlight.js is\n * synchronous, which is what a live ProseMirror editor needs — Shiki is async\n * and only used for read-only render paths.\n */\nconst codeLowlight = createLowlight({\n bash,\n css,\n javascript,\n json,\n markdown,\n python,\n sql,\n typescript,\n xml,\n yaml,\n});\nimport type { Doc as YDoc } from \"yjs\";\nimport type { Awareness } from \"y-protocols/awareness\";\nimport { createImageExtension, type ImageUploadFn } from \"./ImageExtension.js\";\nimport { createCodeBlockNode } from \"./CodeBlockNode.js\";\n\n/**\n * Markdown dialect the editor parses/serializes.\n *\n * - `gfm` — GitHub-Flavored Markdown. No raw HTML passthrough. The byte-stable\n * serialization used by Plans (see RichMarkdownEditor.roundtrip.spec.ts).\n * - `nfm` — the Notion-Flavored Markdown superset used by the Content editor,\n * which opts into inline HTML so Notion-specific blocks round-trip.\n */\nexport type RichMarkdownDialect = \"gfm\" | \"nfm\";\n\n/**\n * Editor preset. Schema-neutral today (both presets share the base schema),\n * but threaded through so an app can branch schema/behavior per preset without\n * a new factory. The collab/markdown wiring is preset-independent.\n */\nexport type RichMarkdownEditorPreset = \"plan\" | \"content\";\n\n/** User info used to label this client's collaborative cursor. */\nexport interface RichMarkdownCollabUser {\n name: string;\n color: string;\n email?: string;\n}\n\n/** Optional collaborative-editing inputs for the shared editor. */\nexport interface SharedEditorCollab {\n /**\n * Yjs document for collaborative editing. When present the editor binds the\n * shared {@link Collaboration} (+ {@link CollaborationCaret} when awareness\n * is set) extensions and StarterKit's built-in undo/redo is disabled (Yjs\n * owns history). When absent the editor is a controlled `value`/`onChange`\n * editor.\n */\n ydoc?: YDoc | null;\n /** Shared awareness instance for live multi-user cursors. */\n awareness?: Awareness | null;\n /** Current user info for the collaborative cursor label. */\n user?: RichMarkdownCollabUser | null;\n}\n\n/** Toggle the optional base extensions on/off per app. All default to `true`. */\nexport interface SharedEditorFeatures {\n /** GFM pipe tables (Table + TableRow + TableHeader + TableCell). */\n tables?: boolean;\n /** Task / checklist lists (TaskList + TaskItem). */\n tasks?: boolean;\n /** Inline links (the `Link` mark). When off, links fall back to plain text. */\n link?: boolean;\n /** Fenced code blocks. Disabling lets an app inject its own code-block node. */\n codeBlock?: boolean;\n /**\n * The built-in {@link Placeholder} extension. Default `true`. Apps that need a\n * bespoke placeholder resolver (per-node-type labels, ancestor-aware text)\n * disable this and supply their own Placeholder via `extraExtensions`.\n */\n placeholder?: boolean;\n /**\n * The built-in dialect-keyed {@link Markdown} serializer. Default `true`.\n * Apps with a custom serializer (e.g. Content's NFM converter, which does NOT\n * round-trip through tiptap-markdown's storage) disable this and own the\n * serialize/parse pipeline themselves. The Markdown extension is still added\n * so paste/clipboard transforms work — disable it only when supplying your own\n * Markdown configuration via the {@link CreateSharedEditorExtensionsOptions.markdown}\n * option instead.\n */\n markdown?: boolean;\n /**\n * The shared block-level image node (`@tiptap/extension-image`). Default\n * `false` so existing embedders are unchanged. When `true`, images\n * serialize to GFM `![alt](src)` (source-syncable) and — when an\n * {@link CreateSharedEditorExtensionsOptions.onImageUpload} function is\n * supplied — paste / drop of local image files uploads through it. Content\n * leaves this off and injects its own richer image node via\n * `extraExtensions`, so the two never collide.\n */\n image?: boolean;\n}\n\nexport interface CreateSharedEditorExtensionsOptions {\n /** Markdown dialect; selects the keyed {@link Markdown} config. */\n dialect?: RichMarkdownDialect;\n /** Preset hook (schema-neutral today). */\n preset?: RichMarkdownEditorPreset;\n /** Empty-block placeholder text (headings get their own labels). */\n placeholder?: string;\n /** Toggle individual base extensions. */\n features?: SharedEditorFeatures;\n /**\n * Extra StarterKit options merged over the shared defaults. Lets an app turn\n * off StarterKit nodes it replaces (Content swaps in its own paragraph /\n * blockquote / code block) or pass a custom dropcursor, while still sharing\n * the rest of the StarterKit base + the collab undo/redo gating. The shared\n * defaults (`heading` levels 1-4, `link: false`, the default dropcursor, and\n * `undoRedo: false` in collab mode) are applied first and can be overridden\n * key-by-key here.\n */\n starterKit?: Partial<StarterKitOptions>;\n /**\n * Custom {@link Markdown} configuration. Replaces the dialect-keyed config from\n * {@link MARKDOWN_DIALECT_CONFIG} when provided. Only used when\n * `features.markdown !== false`; apps that own the whole markdown pipeline (no\n * tiptap-markdown serialization at all) should set `features.markdown: false`\n * and add their own configured Markdown extension via `extraExtensions`.\n */\n markdown?: Parameters<typeof Markdown.configure>[0];\n /**\n * App-specific extensions (Notion nodes, media, drag handles, comment\n * anchors, etc.) appended LAST so they bind over the shared base schema and\n * the optional Collaboration extensions still mount after them.\n */\n extraExtensions?: Array<Extension | Node | Mark>;\n /** Optional collaborative-editing wiring. */\n collab?: SharedEditorCollab | null;\n /**\n * Disable StarterKit's built-in undo/redo (prosemirror-history) for a\n * controlled (non-collab) editor whose host owns its own undo authority.\n * Default `false`. When a `collab.ydoc` is present, undo/redo is ALWAYS\n * disabled regardless of this flag (Yjs owns history); this flag is the\n * non-collab equivalent. The plan editor sets it so a single app-level undo\n * stack (over the authoritative `blocks[]` tree, which includes block data\n * the ProseMirror doc never stores) is the sole cmd+z authority — otherwise\n * PM history and the app stack would both fire, and PM history can't see\n * block-option edits at all.\n */\n disableHistory?: boolean;\n /**\n * Injectable image uploader for the shared image block. Only used when\n * `features.image` is on. Turns a picked / pasted / dropped image File into a\n * hosted `{ src, alt? }`. Plans pass `uploadEditorImage` (the framework\n * `upload-image` action). When omitted, the image block still renders and\n * round-trips `![alt](src)` markdown but cannot ingest local files.\n */\n onImageUpload?: ImageUploadFn | null;\n}\n\n/**\n * tiptap-markdown configuration, keyed by dialect. This is the single source of\n * truth for how each dialect parses/serializes markdown so the editor component\n * and the round-trip fidelity test can never drift apart.\n *\n * tiptap-markdown re-serializes the whole document on every edit, so the goal\n * for GFM is `serialize(parse(markdown)) === markdown` for the markdown plans\n * actually contain. We deliberately keep tiptap-markdown's own defaults\n * (`bulletListMarker: \"-\"`, `tightLists: true`, `linkify: false`,\n * `breaks: false`) because those produce the most byte-stable GFM. See\n * RichMarkdownEditor.roundtrip.spec.ts for the pinned corpus.\n *\n * NFM (Content) opts into inline HTML passthrough (`html: true`) so\n * Notion-specific blocks survive a markdown round-trip; the rest mirrors the\n * Content editor's existing `Markdown.configure` call.\n */\nexport const MARKDOWN_DIALECT_CONFIG: Record<\n RichMarkdownDialect,\n Parameters<typeof Markdown.configure>[0]\n> = {\n gfm: {\n // GFM plans are the common case and must never gain raw HTML as a second\n // representation.\n html: false,\n // Keep tiptap-markdown's defaults that minimise first-edit normalisation\n // churn (see roundtrip spec). Listed explicitly so the contract is\n // self-documenting rather than relying on the package defaults.\n bulletListMarker: \"-\",\n tightLists: true,\n linkify: false,\n breaks: false,\n transformPastedText: true,\n transformCopiedText: true,\n },\n nfm: {\n // NFM is a superset that allows inline HTML so Notion blocks round-trip.\n html: true,\n transformPastedText: true,\n transformCopiedText: true,\n },\n};\n\nconst DEFAULT_FEATURES: Required<SharedEditorFeatures> = {\n tables: true,\n tasks: true,\n link: true,\n codeBlock: true,\n placeholder: true,\n markdown: true,\n // Off by default: only Plans opt in today. Content injects its own richer\n // image node via `extraExtensions` and must not get a second `image` node.\n image: false,\n};\n\n/**\n * The ONE editor extension factory shared by every embedder (Plans today,\n * Content next). It assembles the base Tiptap schema (StarterKit + Placeholder\n * + Link + tasks + tables + code block), the dialect-keyed {@link Markdown}\n * serializer, the optional Collaboration stack, and finally any app-specific\n * `extraExtensions`.\n *\n * Ordering matters:\n * 1. Base schema (StarterKit first so its nodes/marks register; `starterKit`\n * overrides let an app disable replaced nodes / swap the dropcursor).\n * 2. dialect-keyed Markdown serializer (suppressible via `features.markdown`\n * for apps that own the whole serialize/parse pipeline, e.g. Content's NFM).\n * 3. `extraExtensions` (Notion/media/etc.) — appended before Collaboration so\n * apps can extend the schema and Collaboration still binds over the full\n * schema.\n * 4. Collaboration (+ CollaborationCaret) LAST so they bind over everything.\n *\n * Content (the NFM editor) drives this factory with `features.placeholder` and\n * `features.markdown` off, `features.tasks/tables/link` off where it ships its\n * own, a `starterKit` override disabling paragraph/blockquote/codeBlock, and all\n * Notion/media/fidelity nodes + its own Markdown(NFM)/Placeholder via\n * `extraExtensions` — so it shares the StarterKit base + the collab wiring while\n * owning its byte-identical NFM serializer.\n */\nexport function createSharedEditorExtensions({\n dialect = \"gfm\",\n // `preset` is accepted and forwarded for future preset-specific schema\n // branches; it is currently schema-neutral.\n preset: _preset = \"plan\",\n placeholder = \"Type '/' for commands...\",\n features,\n starterKit,\n markdown,\n extraExtensions = [],\n collab = null,\n onImageUpload = null,\n disableHistory = false,\n}: CreateSharedEditorExtensionsOptions = {}): Array<Extension | Node | Mark> {\n const feat = { ...DEFAULT_FEATURES, ...(features ?? {}) };\n const ydoc = collab?.ydoc ?? null;\n const awareness = collab?.awareness ?? null;\n const user = collab?.user ?? null;\n\n const exts: Array<Extension | Node | Mark> = [\n StarterKit.configure({\n heading: { levels: [1, 2, 3, 4] },\n link: false,\n // StarterKit's plain code block is always disabled; when enabled we add the\n // syntax-highlighting `CodeBlockLowlight` (same `codeBlock` node) below.\n codeBlock: false,\n dropcursor: { color: \"hsl(var(--ring))\", width: 2 },\n // Yjs owns undo/redo when Collaboration is active; the StarterKit history\n // plugin and the CRDT cannot both track undo without corrupting state.\n // `disableHistory` is the non-collab equivalent: a controlled editor whose\n // host (e.g. the plan editor) owns a single app-level undo authority.\n ...(ydoc || disableHistory ? { undoRedo: false } : {}),\n // App overrides last so embedders can disable replaced nodes (paragraph,\n // blockquote, code block) or swap the dropcursor while keeping the shared\n // base + the collab undo/redo gating above.\n ...(starterKit ?? {}),\n }),\n ];\n\n // Notion-style syntax-highlighted code block (replaces StarterKit's plain one)\n // only when the embedder opts in via `features.codeBlock`. The shared node adds\n // a language picker header (Auto-detects by default) over `CodeBlockLowlight`,\n // so fenced markdown still round-trips byte-identically. Content disables this\n // and ships its own code node, so this affects Plans (and future opt-in apps)\n // alone.\n if (feat.codeBlock) {\n exts.push(createCodeBlockNode({ lowlight: codeLowlight }));\n }\n\n if (feat.placeholder) {\n exts.push(\n Placeholder.configure({\n placeholder: ({ node }) => {\n if (node.type.name === \"heading\") {\n const level = node.attrs.level;\n if (level === 1) return \"Heading 1\";\n if (level === 2) return \"Heading 2\";\n if (level === 3) return \"Heading 3\";\n return \"Heading 4\";\n }\n return placeholder;\n },\n showOnlyWhenEditable: true,\n showOnlyCurrent: true,\n }),\n );\n }\n\n if (feat.link) {\n exts.push(\n Link.configure({\n openOnClick: false,\n HTMLAttributes: { class: \"an-rich-md-link\" },\n }),\n );\n }\n\n if (feat.tasks) {\n exts.push(\n TaskList.configure({\n HTMLAttributes: { class: \"an-rich-md-task-list\" },\n }),\n TaskItem.configure({ nested: true }),\n );\n }\n\n if (feat.tables) {\n exts.push(\n Table.configure({\n resizable: false,\n HTMLAttributes: { class: \"an-rich-md-table\" },\n }),\n TableRow,\n TableHeader,\n TableCell,\n );\n }\n\n if (feat.markdown) {\n exts.push(Markdown.configure(markdown ?? MARKDOWN_DIALECT_CONFIG[dialect]));\n }\n\n // Shared block-level image node. The node is named `image`, so when\n // `features.markdown` is on, tiptap-markdown serializes it through its\n // built-in `defaultMarkdownSerializer.nodes.image` fallback → `![alt](src)`\n // (no width-as-HTML override here, so GFM stays byte-stable and\n // source-syncable). With an `onImageUpload` it accepts paste/drop uploads.\n if (feat.image) {\n exts.push(createImageExtension({ onImageUpload }));\n }\n\n // App-specific extensions (Notion/media/drag handles/comments). Appended\n // before Collaboration so they can extend the schema and Collaboration binds\n // over the full set.\n if (extraExtensions.length > 0) {\n exts.push(...extraExtensions);\n }\n\n // Collaborative editing via the shared Y.Doc. Markdown stays the canonical\n // saved representation (onChange serializes it); the Y.Doc is transient live\n // state only. Appended last so it binds over the configured schema above.\n if (ydoc) {\n exts.push(Collaboration.configure({ document: ydoc }));\n // Live multi-user cursors. Only mounted alongside a Y.Doc so the standalone\n // controlled editor (today's plan/content behavior) is untouched.\n if (awareness) {\n exts.push(\n CollaborationCaret.configure({\n provider: { awareness },\n user: user ?? { name: \"Anonymous\", color: \"#999\" },\n }),\n );\n }\n }\n\n return exts;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"useCollabReconcile.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/useCollabReconcile.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIvD,eAAO,MAAM,sCAAsC,wCACZ,CAAC;AAExC,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKxD;AAqBD,MAAM,WAAW,yBAAyB;IACxC,gDAAgD;IAChD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uEAAuE;IACvE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,qFAAqF;IACrF,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CACX,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,KACtD,IAAI,CAAC;IACV;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;KACxB,KAAK,OAAO,CAAC;IACd;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,wBAAwB;IACvC,iEAAiE;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,mBAAmB,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC/C;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC;IAC1D;;;;OAIG;IACH,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CAChD;AAoED,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,IAAW,EACX,SAAgB,EAChB,KAAK,EACL,gBAAgB,EAChB,QAAQ,EACR,WAA+B,EAC/B,UAA8B,EAC9B,cAAyB,EACzB,UAA8B,EAC9B,uBAAuB,GACxB,EAAE,yBAAyB,GAAG,wBAAwB,CA+StD"}
1
+ {"version":3,"file":"useCollabReconcile.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/useCollabReconcile.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,KAAK,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC3E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,GAAG,IAAI,IAAI,EAAE,MAAM,KAAK,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAIvD,eAAO,MAAM,sCAAsC,wCACZ,CAAC;AAExC,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAKxD;AAqBD,MAAM,WAAW,yBAAyB;IACxC,gDAAgD;IAChD,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uEAAuE;IACvE,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACnB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,0DAA0D;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,qFAAqF;IACrF,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACzC;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,CACX,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,YAAY,CAAC,EAAE,OAAO,CAAA;KAAE,KACtD,IAAI,CAAC;IACV;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC3C;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,eAAe,EAAE,MAAM,CAAC;QACxB,cAAc,EAAE,MAAM,CAAC;KACxB,KAAK,OAAO,CAAC;IACd;;;;;;;OAOG;IACH,uBAAuB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,wBAAwB;IACvC,iEAAiE;IACjE,MAAM,EAAE,OAAO,CAAC;IAChB;;;OAGG;IACH,mBAAmB,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC/C;;;;OAIG;IACH,kBAAkB,EAAE,CAAC,WAAW,EAAE,WAAW,KAAK,OAAO,CAAC;IAC1D;;;;OAIG;IACH,eAAe,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CAChD;AAoED,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,IAAW,EACX,SAAgB,EAChB,KAAK,EACL,gBAAgB,EAChB,QAAQ,EACR,WAA+B,EAC/B,UAA8B,EAC9B,cAAyB,EACzB,UAA8B,EAC9B,uBAAuB,GACxB,EAAE,yBAAyB,GAAG,wBAAwB,CAyTtD"}
@@ -281,7 +281,17 @@ export function useCollabReconcile({ editor, ydoc = null, awareness = null, valu
281
281
  }
282
282
  return;
283
283
  }
284
- if (!externalNewer && editor.isFocused)
284
+ // Older-or-equal content is a stale poll / lagging echo. Drop it while
285
+ // focused (a peer/agent edit would be NEWER and retries above). In
286
+ // NON-COLLAB mode there is no peer, so older-or-equal external content is
287
+ // ALWAYS stale — dropping it regardless of focus stops a lagging
288
+ // `get-visual-plan` poll from reverting a just-applied local structural
289
+ // change (drag-to-columns) while the editor is blurred (the drag grips the
290
+ // handle, not the prose, so `isFocused` is false at drop time). Gated on
291
+ // `lastAppliedSerializedRef` so the very first seed (nothing applied yet,
292
+ // also not-newer) still lands.
293
+ const seeded = lastAppliedSerializedRef.current !== null;
294
+ if (!externalNewer && (editor.isFocused || (!collab && seeded)))
285
295
  return;
286
296
  // Race guard: with peers present, let Yjs deliver a peer's edit first.
287
297
  // Defer once and re-check — a peer edit makes the equality check above