@agent-native/core 0.38.0 → 0.39.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 (156) hide show
  1. package/dist/cli/create.d.ts.map +1 -1
  2. package/dist/cli/create.js +8 -1
  3. package/dist/cli/create.js.map +1 -1
  4. package/dist/cli/skills.d.ts +5 -4
  5. package/dist/cli/skills.d.ts.map +1 -1
  6. package/dist/cli/skills.js +450 -125
  7. package/dist/cli/skills.js.map +1 -1
  8. package/dist/client/blocks/BlockView.d.ts +13 -4
  9. package/dist/client/blocks/BlockView.d.ts.map +1 -1
  10. package/dist/client/blocks/BlockView.js +34 -13
  11. package/dist/client/blocks/BlockView.js.map +1 -1
  12. package/dist/client/blocks/SchemaBlockEditor.d.ts.map +1 -1
  13. package/dist/client/blocks/SchemaBlockEditor.js +96 -3
  14. package/dist/client/blocks/SchemaBlockEditor.js.map +1 -1
  15. package/dist/client/blocks/index.d.ts +18 -1
  16. package/dist/client/blocks/index.d.ts.map +1 -1
  17. package/dist/client/blocks/index.js +26 -1
  18. package/dist/client/blocks/index.js.map +1 -1
  19. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +6 -0
  20. package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -0
  21. package/dist/client/blocks/library/AnnotatedCodeBlock.js +135 -0
  22. package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -0
  23. package/dist/client/blocks/library/ApiEndpointBlock.d.ts +20 -0
  24. package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -0
  25. package/dist/client/blocks/library/ApiEndpointBlock.js +131 -0
  26. package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -0
  27. package/dist/client/blocks/library/DataModelBlock.d.ts +28 -0
  28. package/dist/client/blocks/library/DataModelBlock.d.ts.map +1 -0
  29. package/dist/client/blocks/library/DataModelBlock.js +222 -0
  30. package/dist/client/blocks/library/DataModelBlock.js.map +1 -0
  31. package/dist/client/blocks/library/DiffBlock.d.ts +6 -0
  32. package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -0
  33. package/dist/client/blocks/library/DiffBlock.js +293 -0
  34. package/dist/client/blocks/library/DiffBlock.js.map +1 -0
  35. package/dist/client/blocks/library/FileTreeBlock.d.ts +23 -0
  36. package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -0
  37. package/dist/client/blocks/library/FileTreeBlock.js +225 -0
  38. package/dist/client/blocks/library/FileTreeBlock.js.map +1 -0
  39. package/dist/client/blocks/library/JsonExplorerBlock.d.ts +19 -0
  40. package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -0
  41. package/dist/client/blocks/library/JsonExplorerBlock.js +171 -0
  42. package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -0
  43. package/dist/client/blocks/library/MermaidBlock.d.ts +17 -0
  44. package/dist/client/blocks/library/MermaidBlock.d.ts.map +1 -0
  45. package/dist/client/blocks/library/MermaidBlock.js +131 -0
  46. package/dist/client/blocks/library/MermaidBlock.js.map +1 -0
  47. package/dist/client/blocks/library/OpenApiSpecBlock.d.ts +19 -0
  48. package/dist/client/blocks/library/OpenApiSpecBlock.d.ts.map +1 -0
  49. package/dist/client/blocks/library/OpenApiSpecBlock.js +494 -0
  50. package/dist/client/blocks/library/OpenApiSpecBlock.js.map +1 -0
  51. package/dist/client/blocks/library/annotated-code.config.d.ts +58 -0
  52. package/dist/client/blocks/library/annotated-code.config.d.ts.map +1 -0
  53. package/dist/client/blocks/library/annotated-code.config.js +53 -0
  54. package/dist/client/blocks/library/annotated-code.config.js.map +1 -0
  55. package/dist/client/blocks/library/api-endpoint.config.d.ts +71 -0
  56. package/dist/client/blocks/library/api-endpoint.config.d.ts.map +1 -0
  57. package/dist/client/blocks/library/api-endpoint.config.js +91 -0
  58. package/dist/client/blocks/library/api-endpoint.config.js.map +1 -0
  59. package/dist/client/blocks/library/checklist.d.ts.map +1 -1
  60. package/dist/client/blocks/library/checklist.js +3 -1
  61. package/dist/client/blocks/library/checklist.js.map +1 -1
  62. package/dist/client/blocks/library/code-tabs.js +1 -1
  63. package/dist/client/blocks/library/code-tabs.js.map +1 -1
  64. package/dist/client/blocks/library/data-model.config.d.ts +72 -0
  65. package/dist/client/blocks/library/data-model.config.d.ts.map +1 -0
  66. package/dist/client/blocks/library/data-model.config.js +59 -0
  67. package/dist/client/blocks/library/data-model.config.js.map +1 -0
  68. package/dist/client/blocks/library/dev-doc-ui.d.ts +49 -0
  69. package/dist/client/blocks/library/dev-doc-ui.d.ts.map +1 -0
  70. package/dist/client/blocks/library/dev-doc-ui.js +50 -0
  71. package/dist/client/blocks/library/dev-doc-ui.js.map +1 -0
  72. package/dist/client/blocks/library/diff.config.d.ts +41 -0
  73. package/dist/client/blocks/library/diff.config.d.ts.map +1 -0
  74. package/dist/client/blocks/library/diff.config.js +34 -0
  75. package/dist/client/blocks/library/diff.config.js.map +1 -0
  76. package/dist/client/blocks/library/file-tree.config.d.ts +59 -0
  77. package/dist/client/blocks/library/file-tree.config.d.ts.map +1 -0
  78. package/dist/client/blocks/library/file-tree.config.js +45 -0
  79. package/dist/client/blocks/library/file-tree.config.js.map +1 -0
  80. package/dist/client/blocks/library/html.d.ts.map +1 -1
  81. package/dist/client/blocks/library/html.js +4 -1
  82. package/dist/client/blocks/library/html.js.map +1 -1
  83. package/dist/client/blocks/library/json-explorer.config.d.ts +46 -0
  84. package/dist/client/blocks/library/json-explorer.config.d.ts.map +1 -0
  85. package/dist/client/blocks/library/json-explorer.config.js +28 -0
  86. package/dist/client/blocks/library/json-explorer.config.js.map +1 -0
  87. package/dist/client/blocks/library/mermaid.config.d.ts +32 -0
  88. package/dist/client/blocks/library/mermaid.config.d.ts.map +1 -0
  89. package/dist/client/blocks/library/mermaid.config.js +24 -0
  90. package/dist/client/blocks/library/mermaid.config.js.map +1 -0
  91. package/dist/client/blocks/library/openapi-spec.config.d.ts +49 -0
  92. package/dist/client/blocks/library/openapi-spec.config.d.ts.map +1 -0
  93. package/dist/client/blocks/library/openapi-spec.config.js +24 -0
  94. package/dist/client/blocks/library/openapi-spec.config.js.map +1 -0
  95. package/dist/client/blocks/library/server-specs.d.ts +35 -0
  96. package/dist/client/blocks/library/server-specs.d.ts.map +1 -0
  97. package/dist/client/blocks/library/server-specs.js +171 -0
  98. package/dist/client/blocks/library/server-specs.js.map +1 -0
  99. package/dist/client/blocks/library/specs.d.ts +29 -0
  100. package/dist/client/blocks/library/specs.d.ts.map +1 -0
  101. package/dist/client/blocks/library/specs.js +229 -0
  102. package/dist/client/blocks/library/specs.js.map +1 -0
  103. package/dist/client/blocks/library/table.d.ts.map +1 -1
  104. package/dist/client/blocks/library/table.js +3 -1
  105. package/dist/client/blocks/library/table.js.map +1 -1
  106. package/dist/client/blocks/library/tabs.js +1 -1
  107. package/dist/client/blocks/library/tabs.js.map +1 -1
  108. package/dist/client/blocks/registry.d.ts +8 -0
  109. package/dist/client/blocks/registry.d.ts.map +1 -1
  110. package/dist/client/blocks/registry.js +15 -0
  111. package/dist/client/blocks/registry.js.map +1 -1
  112. package/dist/client/blocks/server.d.ts +9 -0
  113. package/dist/client/blocks/server.d.ts.map +1 -1
  114. package/dist/client/blocks/server.js +16 -0
  115. package/dist/client/blocks/server.js.map +1 -1
  116. package/dist/client/blocks/types.d.ts +40 -0
  117. package/dist/client/blocks/types.d.ts.map +1 -1
  118. package/dist/client/blocks/types.js.map +1 -1
  119. package/dist/client/index.d.ts +2 -1
  120. package/dist/client/index.d.ts.map +1 -1
  121. package/dist/client/index.js +10 -1
  122. package/dist/client/index.js.map +1 -1
  123. package/dist/client/rich-markdown-editor/DragHandle.d.ts +52 -0
  124. package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -0
  125. package/dist/client/rich-markdown-editor/DragHandle.js +403 -0
  126. package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -0
  127. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +97 -0
  128. package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -0
  129. package/dist/client/rich-markdown-editor/RegistryBlockNode.js +214 -0
  130. package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -0
  131. package/dist/client/rich-markdown-editor/RunId.d.ts +28 -0
  132. package/dist/client/rich-markdown-editor/RunId.d.ts.map +1 -0
  133. package/dist/client/rich-markdown-editor/RunId.js +60 -0
  134. package/dist/client/rich-markdown-editor/RunId.js.map +1 -0
  135. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +25 -1
  136. package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
  137. package/dist/client/rich-markdown-editor/SharedRichEditor.js +14 -5
  138. package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
  139. package/dist/client/rich-markdown-editor/gfmDoc.d.ts +24 -0
  140. package/dist/client/rich-markdown-editor/gfmDoc.d.ts.map +1 -0
  141. package/dist/client/rich-markdown-editor/gfmDoc.js +83 -0
  142. package/dist/client/rich-markdown-editor/gfmDoc.js.map +1 -0
  143. package/dist/client/rich-markdown-editor/index.d.ts +5 -0
  144. package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
  145. package/dist/client/rich-markdown-editor/index.js +5 -0
  146. package/dist/client/rich-markdown-editor/index.js.map +1 -1
  147. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts +46 -0
  148. package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +1 -0
  149. package/dist/client/rich-markdown-editor/registrySlashCommands.js +13 -0
  150. package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +1 -0
  151. package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
  152. package/dist/client/rich-markdown-editor/useCollabReconcile.js +33 -0
  153. package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
  154. package/docs/content/template-plan.md +19 -4
  155. package/docs/content/visual-plans.md +3 -1
  156. package/package.json +1 -1
@@ -0,0 +1,403 @@
1
+ import { Extension } from "@tiptap/core";
2
+ import { Plugin, PluginKey, NodeSelection } from "@tiptap/pm/state";
3
+ /**
4
+ * Default editor-wrapper CSS selector the drag handle scopes itself to.
5
+ *
6
+ * The handle, the drop indicator, and the `position: relative` anchor are all
7
+ * appended to / measured against the closest ancestor matching this selector.
8
+ * Content's editor wraps its ProseMirror DOM in a `.visual-editor-wrapper`
9
+ * element, so that is the historical default. Other apps (e.g. the plan editor)
10
+ * pass their own wrapper selector via {@link DragHandleOptions.wrapperSelector}.
11
+ */
12
+ export const DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR = ".visual-editor-wrapper";
13
+ const dragHandleKey = new PluginKey("dragHandle");
14
+ const HOVER_SIDE_OUTSET_REM = 8;
15
+ /**
16
+ * App-agnostic Tiptap extension providing a Notion-style left-margin drag grip
17
+ * (the `::` handle), block selection, and drag-to-reorder over top-level block
18
+ * nodes.
19
+ *
20
+ * Behavior:
21
+ * - On hover over any top-level block, a `.drag-handle` grip appears in the left
22
+ * margin (forgiving hit zone extends {@link HOVER_SIDE_OUTSET_REM}rem to the
23
+ * sides and into the gap above/between blocks).
24
+ * - `mousedown` on the grip selects the block as a `NodeSelection`; dragging past
25
+ * a small threshold starts a reorder, showing a floating clone preview
26
+ * (`.notion-drag-preview`) and a `.notion-drop-indicator` line. `Escape`
27
+ * cancels.
28
+ * - While dragging, the source block carries `.notion-block--dragging` and the
29
+ * document element carries `.notion-editor-is-dragging` so apps can style the
30
+ * in-flight state. Apps own all of these CSS class names.
31
+ * - Works for ANY top-level node ProseMirror renders as a direct child of the
32
+ * editor — including `group: "block"`, `draggable: true` atoms such as the
33
+ * plan editor's `planBlock`.
34
+ *
35
+ * The only app-specific coupling — the editor wrapper element the handle and
36
+ * drop indicator are anchored to — is configurable via
37
+ * {@link DragHandleOptions.wrapperSelector}, defaulting to
38
+ * {@link DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR} (`.visual-editor-wrapper`) so the
39
+ * Content editor keeps working byte-identically. The plan editor passes its own
40
+ * wrapper selector via `DragHandle.configure({ wrapperSelector })`.
41
+ */
42
+ export const DragHandle = Extension.create({
43
+ name: "dragHandle",
44
+ addOptions() {
45
+ return {
46
+ wrapperSelector: DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR,
47
+ };
48
+ },
49
+ addProseMirrorPlugins() {
50
+ const editor = this.editor;
51
+ const wrapperSelector = this.options.wrapperSelector;
52
+ let handle = null;
53
+ let currentBlock = null;
54
+ let dragStartPos = null;
55
+ let dragSession = null;
56
+ const getHoverSideOutset = () => {
57
+ const rootFontSize = Number.parseFloat(getComputedStyle(document.documentElement).fontSize);
58
+ return ((Number.isFinite(rootFontSize) ? rootFontSize : 16) *
59
+ HOVER_SIDE_OUTSET_REM);
60
+ };
61
+ const getTopLevelBlocks = (editorView) => {
62
+ const blocks = [];
63
+ editorView.state.doc.forEach((_node, offset) => {
64
+ const dom = editorView.nodeDOM(offset);
65
+ if (!(dom instanceof HTMLElement))
66
+ return;
67
+ blocks.push({
68
+ node: dom,
69
+ pmPos: offset,
70
+ rect: dom.getBoundingClientRect(),
71
+ });
72
+ });
73
+ return blocks;
74
+ };
75
+ const findForgivingBlock = (editorView, clientX, clientY) => {
76
+ const blocks = getTopLevelBlocks(editorView);
77
+ if (blocks.length === 0)
78
+ return null;
79
+ const sideOutset = getHoverSideOutset();
80
+ const pageLeft = 0;
81
+ const pageRight = window.visualViewport?.width ?? window.innerWidth;
82
+ for (let index = 0; index < blocks.length; index++) {
83
+ const block = blocks[index];
84
+ const nextBlock = blocks[index + 1];
85
+ const blockBottomGap = nextBlock
86
+ ? Math.max(0, nextBlock.rect.top - block.rect.bottom)
87
+ : 0;
88
+ const zoneLeft = Math.max(pageLeft, block.rect.left - sideOutset);
89
+ const zoneRight = Math.min(pageRight, block.rect.right + sideOutset);
90
+ const zoneTop = index === 0
91
+ ? Math.max(0, block.rect.top - blockBottomGap)
92
+ : block.rect.top;
93
+ const zoneBottom = nextBlock ? nextBlock.rect.top : block.rect.bottom;
94
+ if (clientX >= zoneLeft &&
95
+ clientX <= zoneRight &&
96
+ clientY >= zoneTop &&
97
+ clientY < zoneBottom) {
98
+ return block;
99
+ }
100
+ }
101
+ return null;
102
+ };
103
+ const showHandleForBlock = (editorView, block) => {
104
+ if (!handle)
105
+ return;
106
+ currentBlock = block.node;
107
+ dragStartPos = block.pmPos;
108
+ const wrapper = editorView.dom.closest(wrapperSelector);
109
+ if (!wrapper)
110
+ return;
111
+ // Lazily (re)attach the grip the first time a wrapper is actually
112
+ // available. At plugin `view()` init the editor DOM may not yet be mounted
113
+ // inside the wrapper (React mounts `EditorContent` after the EditorView is
114
+ // constructed), so the init-time append can silently no-op and leave the
115
+ // grip orphaned. Re-home it here once the wrapper exists.
116
+ if (handle.parentElement !== wrapper) {
117
+ wrapper.style.position = "relative";
118
+ wrapper.appendChild(handle);
119
+ }
120
+ const wrapperRect = wrapper.getBoundingClientRect();
121
+ handle.style.display = "flex";
122
+ handle.style.top = `${block.rect.top - wrapperRect.top + 2}px`;
123
+ handle.style.left = "-24px";
124
+ };
125
+ const selectCurrentBlock = (editorView) => {
126
+ if (dragStartPos === null)
127
+ return null;
128
+ try {
129
+ const sel = NodeSelection.create(editorView.state.doc, dragStartPos);
130
+ editorView.dispatch(editorView.state.tr.setSelection(sel));
131
+ editorView.focus();
132
+ return sel;
133
+ }
134
+ catch {
135
+ return null;
136
+ }
137
+ };
138
+ const cleanupDragVisuals = () => {
139
+ dragSession?.preview?.remove();
140
+ dragSession?.dropLine?.remove();
141
+ dragSession?.sourceBlock.classList.remove("notion-block--dragging");
142
+ document.documentElement.classList.remove("notion-editor-is-dragging");
143
+ };
144
+ const createDragPreview = (block) => {
145
+ const blockRect = block.getBoundingClientRect();
146
+ const preview = document.createElement("div");
147
+ const clone = block.cloneNode(true);
148
+ clone.classList.remove("ProseMirror-selectednode", "notion-block--dragging");
149
+ clone.removeAttribute("contenteditable");
150
+ clone.style.background = "transparent";
151
+ clone.style.backgroundColor = "transparent";
152
+ clone.querySelectorAll("[contenteditable]").forEach((node) => {
153
+ node.removeAttribute("contenteditable");
154
+ });
155
+ clone.querySelectorAll("*").forEach((node) => {
156
+ node.style.background = "transparent";
157
+ node.style.backgroundColor = "transparent";
158
+ });
159
+ preview.className = "notion-drag-preview";
160
+ preview.style.width = `${blockRect.width}px`;
161
+ preview.appendChild(clone);
162
+ document.body.appendChild(preview);
163
+ return preview;
164
+ };
165
+ const createDropLine = (view) => {
166
+ const wrapper = view.dom.closest(wrapperSelector);
167
+ if (!wrapper)
168
+ return null;
169
+ const line = document.createElement("div");
170
+ line.className = "notion-drop-indicator";
171
+ wrapper.appendChild(line);
172
+ return line;
173
+ };
174
+ const findDropTarget = (view, clientX, clientY) => {
175
+ const block = findForgivingBlock(view, clientX, clientY);
176
+ if (!block)
177
+ return null;
178
+ const node = view.state.doc.nodeAt(block.pmPos);
179
+ if (!node)
180
+ return null;
181
+ const dropBefore = clientY < block.rect.top ||
182
+ (clientY <= block.rect.bottom &&
183
+ clientY < block.rect.top + block.rect.height / 2);
184
+ return {
185
+ block: block.node,
186
+ before: dropBefore,
187
+ pos: dropBefore ? block.pmPos : block.pmPos + node.nodeSize,
188
+ rect: block.rect,
189
+ };
190
+ };
191
+ const positionDragPreview = (session, clientX, clientY) => {
192
+ if (!session.preview)
193
+ return;
194
+ session.preview.style.transform = `translate3d(${clientX + 12}px, ${clientY + 10}px, 0)`;
195
+ };
196
+ const updateDropLine = (session, target) => {
197
+ const sourceEnd = session.sourcePos + session.sourceNodeSize;
198
+ if (!target ||
199
+ target.pos === session.sourcePos ||
200
+ target.pos === sourceEnd ||
201
+ (target.pos > session.sourcePos && target.pos < sourceEnd)) {
202
+ session.dropPos = null;
203
+ session.dropLine?.remove();
204
+ session.dropLine = null;
205
+ return;
206
+ }
207
+ if (!session.dropLine)
208
+ session.dropLine = createDropLine(session.view);
209
+ if (!session.dropLine)
210
+ return;
211
+ const wrapper = session.view.dom.closest(wrapperSelector);
212
+ if (!wrapper)
213
+ return;
214
+ const wrapperRect = wrapper.getBoundingClientRect();
215
+ const editorRect = session.view.dom.getBoundingClientRect();
216
+ const top = target.before ? target.rect.top : target.rect.bottom;
217
+ session.dropPos = target.pos;
218
+ session.dropLine.style.left = `${editorRect.left - wrapperRect.left}px`;
219
+ session.dropLine.style.top = `${top - wrapperRect.top}px`;
220
+ session.dropLine.style.width = `${editorRect.width}px`;
221
+ };
222
+ const createHandle = () => {
223
+ const el = document.createElement("div");
224
+ el.className = "drag-handle";
225
+ el.contentEditable = "false";
226
+ el.draggable = false;
227
+ el.innerHTML = `<svg width="14" height="14" viewBox="0 0 16 16" fill="currentColor">
228
+ <circle cx="5.5" cy="3" r="1.5"/><circle cx="10.5" cy="3" r="1.5"/>
229
+ <circle cx="5.5" cy="8" r="1.5"/><circle cx="10.5" cy="8" r="1.5"/>
230
+ <circle cx="5.5" cy="13" r="1.5"/><circle cx="10.5" cy="13" r="1.5"/>
231
+ </svg>`;
232
+ return el;
233
+ };
234
+ const hideHandle = () => {
235
+ if (handle)
236
+ handle.style.display = "none";
237
+ currentBlock = null;
238
+ };
239
+ const removeDragListeners = () => {
240
+ document.removeEventListener("mousemove", handleDocumentMouseMove);
241
+ document.removeEventListener("mouseup", handleDocumentMouseUp);
242
+ document.removeEventListener("keydown", handleDocumentKeyDown);
243
+ };
244
+ const createDocumentHoverMove = (editorView) => {
245
+ return (event) => {
246
+ if (!handle || dragSession)
247
+ return;
248
+ if (!editor.isEditable) {
249
+ hideHandle();
250
+ return;
251
+ }
252
+ const block = findForgivingBlock(editorView, event.clientX, event.clientY);
253
+ if (!block) {
254
+ hideHandle();
255
+ return;
256
+ }
257
+ showHandleForBlock(editorView, block);
258
+ };
259
+ };
260
+ const finishDragSession = (commit) => {
261
+ const session = dragSession;
262
+ if (!session)
263
+ return;
264
+ removeDragListeners();
265
+ if (commit && session.dragging && session.dropPos !== null) {
266
+ const sourceStart = session.sourcePos;
267
+ const sourceEnd = session.sourcePos + session.sourceNodeSize;
268
+ const dropPos = session.dropPos;
269
+ if (dropPos !== sourceStart &&
270
+ dropPos !== sourceEnd &&
271
+ !(dropPos > sourceStart && dropPos < sourceEnd)) {
272
+ const sourceNode = session.view.state.doc.nodeAt(sourceStart);
273
+ if (sourceNode) {
274
+ const insertPos = dropPos > sourceStart ? dropPos - sourceNode.nodeSize : dropPos;
275
+ const tr = session.view.state.tr
276
+ .delete(sourceStart, sourceEnd)
277
+ .insert(insertPos, sourceNode);
278
+ tr.setSelection(NodeSelection.create(tr.doc, insertPos));
279
+ session.view.dispatch(tr.scrollIntoView());
280
+ session.view.focus();
281
+ }
282
+ }
283
+ }
284
+ else if (!session.dragging) {
285
+ selectCurrentBlock(session.view);
286
+ }
287
+ cleanupDragVisuals();
288
+ dragSession = null;
289
+ hideHandle();
290
+ };
291
+ const beginDragSession = (session, event) => {
292
+ session.dragging = true;
293
+ session.preview = createDragPreview(session.sourceBlock);
294
+ session.sourceBlock.classList.add("notion-block--dragging");
295
+ document.documentElement.classList.add("notion-editor-is-dragging");
296
+ positionDragPreview(session, event.clientX, event.clientY);
297
+ updateDropLine(session, findDropTarget(session.view, event.clientX, event.clientY));
298
+ };
299
+ function handleDocumentMouseMove(event) {
300
+ if (!dragSession)
301
+ return;
302
+ event.preventDefault();
303
+ const movedEnough = Math.hypot(event.clientX - dragSession.startX, event.clientY - dragSession.startY) > 4;
304
+ if (!dragSession.dragging && movedEnough) {
305
+ beginDragSession(dragSession, event);
306
+ }
307
+ if (!dragSession.dragging)
308
+ return;
309
+ positionDragPreview(dragSession, event.clientX, event.clientY);
310
+ updateDropLine(dragSession, findDropTarget(dragSession.view, event.clientX, event.clientY));
311
+ }
312
+ function handleDocumentMouseUp(event) {
313
+ event.preventDefault();
314
+ finishDragSession(true);
315
+ }
316
+ function handleDocumentKeyDown(event) {
317
+ if (event.key !== "Escape")
318
+ return;
319
+ event.preventDefault();
320
+ finishDragSession(false);
321
+ }
322
+ return [
323
+ new Plugin({
324
+ key: dragHandleKey,
325
+ view(editorView) {
326
+ handle = createHandle();
327
+ const handleDocumentHoverMove = createDocumentHoverMove(editorView);
328
+ const wrapper = editorView.dom.closest(wrapperSelector);
329
+ if (wrapper) {
330
+ wrapper.style.position = "relative";
331
+ wrapper.appendChild(handle);
332
+ }
333
+ document.addEventListener("mousemove", handleDocumentHoverMove);
334
+ handle.addEventListener("mousedown", (e) => {
335
+ e.stopPropagation();
336
+ if (!editor.isEditable) {
337
+ e.preventDefault();
338
+ return;
339
+ }
340
+ if (!currentBlock || dragStartPos === null)
341
+ return;
342
+ const sourceNode = editorView.state.doc.nodeAt(dragStartPos);
343
+ if (!sourceNode)
344
+ return;
345
+ e.preventDefault();
346
+ dragSession = {
347
+ view: editorView,
348
+ sourceBlock: currentBlock,
349
+ sourcePos: dragStartPos,
350
+ sourceNodeSize: sourceNode.nodeSize,
351
+ startX: e.clientX,
352
+ startY: e.clientY,
353
+ dragging: false,
354
+ preview: null,
355
+ dropLine: null,
356
+ dropPos: null,
357
+ };
358
+ document.addEventListener("mousemove", handleDocumentMouseMove);
359
+ document.addEventListener("mouseup", handleDocumentMouseUp);
360
+ document.addEventListener("keydown", handleDocumentKeyDown);
361
+ });
362
+ return {
363
+ destroy() {
364
+ document.removeEventListener("mousemove", handleDocumentHoverMove);
365
+ finishDragSession(false);
366
+ handle?.remove();
367
+ handle = null;
368
+ },
369
+ };
370
+ },
371
+ props: {
372
+ handleDOMEvents: {
373
+ mousemove(view, event) {
374
+ if (!handle)
375
+ return false;
376
+ if (!editor.isEditable) {
377
+ hideHandle();
378
+ return false;
379
+ }
380
+ if (dragSession)
381
+ return false;
382
+ const block = findForgivingBlock(view, event.clientX, event.clientY);
383
+ if (!block) {
384
+ hideHandle();
385
+ return false;
386
+ }
387
+ if (block.node === currentBlock)
388
+ return false;
389
+ showHandleForBlock(view, block);
390
+ return false;
391
+ },
392
+ drop() {
393
+ finishDragSession(false);
394
+ hideHandle();
395
+ return false;
396
+ },
397
+ },
398
+ },
399
+ }),
400
+ ];
401
+ },
402
+ });
403
+ //# sourceMappingURL=DragHandle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DragHandle.js","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/DragHandle.ts"],"names":[],"mappings":"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","sourcesContent":["import { Extension } from \"@tiptap/core\";\nimport { Plugin, PluginKey, NodeSelection } from \"@tiptap/pm/state\";\nimport { type EditorView } from \"@tiptap/pm/view\";\n\n/**\n * Default editor-wrapper CSS selector the drag handle scopes itself to.\n *\n * The handle, the drop indicator, and the `position: relative` anchor are all\n * appended to / measured against the closest ancestor matching this selector.\n * Content's editor wraps its ProseMirror DOM in a `.visual-editor-wrapper`\n * element, so that is the historical default. Other apps (e.g. the plan editor)\n * pass their own wrapper selector via {@link DragHandleOptions.wrapperSelector}.\n */\nexport const DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR = \".visual-editor-wrapper\";\n\nexport interface DragHandleOptions {\n /**\n * CSS selector for the editor wrapper element the handle is anchored to.\n *\n * Must match an ancestor of the ProseMirror editor DOM. The wrapper gets\n * `position: relative` so the absolutely-positioned grip and drop indicator\n * can be placed relative to it. Defaults to\n * {@link DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR} so Content keeps working\n * unchanged.\n */\n wrapperSelector: string;\n}\n\nconst dragHandleKey = new PluginKey(\"dragHandle\");\nconst HOVER_SIDE_OUTSET_REM = 8;\n\ntype DropTarget = {\n block: HTMLElement;\n before: boolean;\n pos: number;\n rect: DOMRect;\n};\n\ntype DragSession = {\n view: EditorView;\n sourceBlock: HTMLElement;\n sourcePos: number;\n sourceNodeSize: number;\n startX: number;\n startY: number;\n dragging: boolean;\n preview: HTMLElement | null;\n dropLine: HTMLElement | null;\n dropPos: number | null;\n};\n\ntype HoverBlock = {\n node: HTMLElement;\n pmPos: number;\n rect: DOMRect;\n};\n\n/**\n * App-agnostic Tiptap extension providing a Notion-style left-margin drag grip\n * (the `::` handle), block selection, and drag-to-reorder over top-level block\n * nodes.\n *\n * Behavior:\n * - On hover over any top-level block, a `.drag-handle` grip appears in the left\n * margin (forgiving hit zone extends {@link HOVER_SIDE_OUTSET_REM}rem to the\n * sides and into the gap above/between blocks).\n * - `mousedown` on the grip selects the block as a `NodeSelection`; dragging past\n * a small threshold starts a reorder, showing a floating clone preview\n * (`.notion-drag-preview`) and a `.notion-drop-indicator` line. `Escape`\n * cancels.\n * - While dragging, the source block carries `.notion-block--dragging` and the\n * document element carries `.notion-editor-is-dragging` so apps can style the\n * in-flight state. Apps own all of these CSS class names.\n * - Works for ANY top-level node ProseMirror renders as a direct child of the\n * editor — including `group: \"block\"`, `draggable: true` atoms such as the\n * plan editor's `planBlock`.\n *\n * The only app-specific coupling — the editor wrapper element the handle and\n * drop indicator are anchored to — is configurable via\n * {@link DragHandleOptions.wrapperSelector}, defaulting to\n * {@link DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR} (`.visual-editor-wrapper`) so the\n * Content editor keeps working byte-identically. The plan editor passes its own\n * wrapper selector via `DragHandle.configure({ wrapperSelector })`.\n */\nexport const DragHandle = Extension.create<DragHandleOptions>({\n name: \"dragHandle\",\n\n addOptions() {\n return {\n wrapperSelector: DEFAULT_DRAG_HANDLE_WRAPPER_SELECTOR,\n };\n },\n\n addProseMirrorPlugins() {\n const editor = this.editor;\n const wrapperSelector = this.options.wrapperSelector;\n let handle: HTMLElement | null = null;\n let currentBlock: HTMLElement | null = null;\n let dragStartPos: number | null = null;\n let dragSession: DragSession | null = null;\n\n const getHoverSideOutset = () => {\n const rootFontSize = Number.parseFloat(\n getComputedStyle(document.documentElement).fontSize,\n );\n return (\n (Number.isFinite(rootFontSize) ? rootFontSize : 16) *\n HOVER_SIDE_OUTSET_REM\n );\n };\n\n const getTopLevelBlocks = (editorView: EditorView): HoverBlock[] => {\n const blocks: HoverBlock[] = [];\n\n editorView.state.doc.forEach((_node, offset) => {\n const dom = editorView.nodeDOM(offset);\n if (!(dom instanceof HTMLElement)) return;\n\n blocks.push({\n node: dom,\n pmPos: offset,\n rect: dom.getBoundingClientRect(),\n });\n });\n\n return blocks;\n };\n\n const findForgivingBlock = (\n editorView: EditorView,\n clientX: number,\n clientY: number,\n ): HoverBlock | null => {\n const blocks = getTopLevelBlocks(editorView);\n if (blocks.length === 0) return null;\n\n const sideOutset = getHoverSideOutset();\n const pageLeft = 0;\n const pageRight = window.visualViewport?.width ?? window.innerWidth;\n\n for (let index = 0; index < blocks.length; index++) {\n const block = blocks[index];\n const nextBlock = blocks[index + 1];\n const blockBottomGap = nextBlock\n ? Math.max(0, nextBlock.rect.top - block.rect.bottom)\n : 0;\n const zoneLeft = Math.max(pageLeft, block.rect.left - sideOutset);\n const zoneRight = Math.min(pageRight, block.rect.right + sideOutset);\n const zoneTop =\n index === 0\n ? Math.max(0, block.rect.top - blockBottomGap)\n : block.rect.top;\n const zoneBottom = nextBlock ? nextBlock.rect.top : block.rect.bottom;\n\n if (\n clientX >= zoneLeft &&\n clientX <= zoneRight &&\n clientY >= zoneTop &&\n clientY < zoneBottom\n ) {\n return block;\n }\n }\n\n return null;\n };\n\n const showHandleForBlock = (editorView: EditorView, block: HoverBlock) => {\n if (!handle) return;\n currentBlock = block.node;\n dragStartPos = block.pmPos;\n\n const wrapper = editorView.dom.closest(wrapperSelector);\n if (!wrapper) return;\n\n // Lazily (re)attach the grip the first time a wrapper is actually\n // available. At plugin `view()` init the editor DOM may not yet be mounted\n // inside the wrapper (React mounts `EditorContent` after the EditorView is\n // constructed), so the init-time append can silently no-op and leave the\n // grip orphaned. Re-home it here once the wrapper exists.\n if (handle.parentElement !== wrapper) {\n (wrapper as HTMLElement).style.position = \"relative\";\n wrapper.appendChild(handle);\n }\n\n const wrapperRect = wrapper.getBoundingClientRect();\n\n handle.style.display = \"flex\";\n handle.style.top = `${block.rect.top - wrapperRect.top + 2}px`;\n handle.style.left = \"-24px\";\n };\n\n const selectCurrentBlock = (editorView: EditorView) => {\n if (dragStartPos === null) return null;\n\n try {\n const sel = NodeSelection.create(editorView.state.doc, dragStartPos);\n editorView.dispatch(editorView.state.tr.setSelection(sel));\n editorView.focus();\n return sel;\n } catch {\n return null;\n }\n };\n\n const cleanupDragVisuals = () => {\n dragSession?.preview?.remove();\n dragSession?.dropLine?.remove();\n dragSession?.sourceBlock.classList.remove(\"notion-block--dragging\");\n document.documentElement.classList.remove(\"notion-editor-is-dragging\");\n };\n\n const createDragPreview = (block: HTMLElement): HTMLElement => {\n const blockRect = block.getBoundingClientRect();\n const preview = document.createElement(\"div\");\n const clone = block.cloneNode(true) as HTMLElement;\n\n clone.classList.remove(\n \"ProseMirror-selectednode\",\n \"notion-block--dragging\",\n );\n clone.removeAttribute(\"contenteditable\");\n clone.style.background = \"transparent\";\n clone.style.backgroundColor = \"transparent\";\n clone.querySelectorAll(\"[contenteditable]\").forEach((node) => {\n node.removeAttribute(\"contenteditable\");\n });\n clone.querySelectorAll<HTMLElement>(\"*\").forEach((node) => {\n node.style.background = \"transparent\";\n node.style.backgroundColor = \"transparent\";\n });\n\n preview.className = \"notion-drag-preview\";\n preview.style.width = `${blockRect.width}px`;\n preview.appendChild(clone);\n document.body.appendChild(preview);\n\n return preview;\n };\n\n const createDropLine = (view: EditorView): HTMLElement | null => {\n const wrapper = view.dom.closest(wrapperSelector);\n if (!wrapper) return null;\n\n const line = document.createElement(\"div\");\n line.className = \"notion-drop-indicator\";\n wrapper.appendChild(line);\n return line;\n };\n\n const findDropTarget = (\n view: EditorView,\n clientX: number,\n clientY: number,\n ): DropTarget | null => {\n const block = findForgivingBlock(view, clientX, clientY);\n if (!block) return null;\n\n const node = view.state.doc.nodeAt(block.pmPos);\n if (!node) return null;\n\n const dropBefore =\n clientY < block.rect.top ||\n (clientY <= block.rect.bottom &&\n clientY < block.rect.top + block.rect.height / 2);\n\n return {\n block: block.node,\n before: dropBefore,\n pos: dropBefore ? block.pmPos : block.pmPos + node.nodeSize,\n rect: block.rect,\n };\n };\n\n const positionDragPreview = (\n session: DragSession,\n clientX: number,\n clientY: number,\n ) => {\n if (!session.preview) return;\n\n session.preview.style.transform = `translate3d(${clientX + 12}px, ${clientY + 10}px, 0)`;\n };\n\n const updateDropLine = (\n session: DragSession,\n target: DropTarget | null,\n ) => {\n const sourceEnd = session.sourcePos + session.sourceNodeSize;\n if (\n !target ||\n target.pos === session.sourcePos ||\n target.pos === sourceEnd ||\n (target.pos > session.sourcePos && target.pos < sourceEnd)\n ) {\n session.dropPos = null;\n session.dropLine?.remove();\n session.dropLine = null;\n return;\n }\n\n if (!session.dropLine) session.dropLine = createDropLine(session.view);\n if (!session.dropLine) return;\n\n const wrapper = session.view.dom.closest(wrapperSelector);\n if (!wrapper) return;\n\n const wrapperRect = wrapper.getBoundingClientRect();\n const editorRect = session.view.dom.getBoundingClientRect();\n const top = target.before ? target.rect.top : target.rect.bottom;\n\n session.dropPos = target.pos;\n session.dropLine.style.left = `${editorRect.left - wrapperRect.left}px`;\n session.dropLine.style.top = `${top - wrapperRect.top}px`;\n session.dropLine.style.width = `${editorRect.width}px`;\n };\n\n const createHandle = () => {\n const el = document.createElement(\"div\");\n el.className = \"drag-handle\";\n el.contentEditable = \"false\";\n el.draggable = false;\n el.innerHTML = `<svg width=\"14\" height=\"14\" viewBox=\"0 0 16 16\" fill=\"currentColor\">\n <circle cx=\"5.5\" cy=\"3\" r=\"1.5\"/><circle cx=\"10.5\" cy=\"3\" r=\"1.5\"/>\n <circle cx=\"5.5\" cy=\"8\" r=\"1.5\"/><circle cx=\"10.5\" cy=\"8\" r=\"1.5\"/>\n <circle cx=\"5.5\" cy=\"13\" r=\"1.5\"/><circle cx=\"10.5\" cy=\"13\" r=\"1.5\"/>\n </svg>`;\n return el;\n };\n\n const hideHandle = () => {\n if (handle) handle.style.display = \"none\";\n currentBlock = null;\n };\n\n const removeDragListeners = () => {\n document.removeEventListener(\"mousemove\", handleDocumentMouseMove);\n document.removeEventListener(\"mouseup\", handleDocumentMouseUp);\n document.removeEventListener(\"keydown\", handleDocumentKeyDown);\n };\n\n const createDocumentHoverMove = (editorView: EditorView) => {\n return (event: MouseEvent) => {\n if (!handle || dragSession) return;\n if (!editor.isEditable) {\n hideHandle();\n return;\n }\n\n const block = findForgivingBlock(\n editorView,\n event.clientX,\n event.clientY,\n );\n\n if (!block) {\n hideHandle();\n return;\n }\n\n showHandleForBlock(editorView, block);\n };\n };\n\n const finishDragSession = (commit: boolean) => {\n const session = dragSession;\n if (!session) return;\n\n removeDragListeners();\n\n if (commit && session.dragging && session.dropPos !== null) {\n const sourceStart = session.sourcePos;\n const sourceEnd = session.sourcePos + session.sourceNodeSize;\n const dropPos = session.dropPos;\n\n if (\n dropPos !== sourceStart &&\n dropPos !== sourceEnd &&\n !(dropPos > sourceStart && dropPos < sourceEnd)\n ) {\n const sourceNode = session.view.state.doc.nodeAt(sourceStart);\n if (sourceNode) {\n const insertPos =\n dropPos > sourceStart ? dropPos - sourceNode.nodeSize : dropPos;\n const tr = session.view.state.tr\n .delete(sourceStart, sourceEnd)\n .insert(insertPos, sourceNode);\n\n tr.setSelection(NodeSelection.create(tr.doc, insertPos));\n\n session.view.dispatch(tr.scrollIntoView());\n session.view.focus();\n }\n }\n } else if (!session.dragging) {\n selectCurrentBlock(session.view);\n }\n\n cleanupDragVisuals();\n dragSession = null;\n hideHandle();\n };\n\n const beginDragSession = (session: DragSession, event: MouseEvent) => {\n session.dragging = true;\n session.preview = createDragPreview(session.sourceBlock);\n session.sourceBlock.classList.add(\"notion-block--dragging\");\n document.documentElement.classList.add(\"notion-editor-is-dragging\");\n positionDragPreview(session, event.clientX, event.clientY);\n updateDropLine(\n session,\n findDropTarget(session.view, event.clientX, event.clientY),\n );\n };\n\n function handleDocumentMouseMove(event: MouseEvent) {\n if (!dragSession) return;\n event.preventDefault();\n\n const movedEnough =\n Math.hypot(\n event.clientX - dragSession.startX,\n event.clientY - dragSession.startY,\n ) > 4;\n\n if (!dragSession.dragging && movedEnough) {\n beginDragSession(dragSession, event);\n }\n\n if (!dragSession.dragging) return;\n\n positionDragPreview(dragSession, event.clientX, event.clientY);\n updateDropLine(\n dragSession,\n findDropTarget(dragSession.view, event.clientX, event.clientY),\n );\n }\n\n function handleDocumentMouseUp(event: MouseEvent) {\n event.preventDefault();\n finishDragSession(true);\n }\n\n function handleDocumentKeyDown(event: KeyboardEvent) {\n if (event.key !== \"Escape\") return;\n event.preventDefault();\n finishDragSession(false);\n }\n\n return [\n new Plugin({\n key: dragHandleKey,\n view(editorView) {\n handle = createHandle();\n const handleDocumentHoverMove = createDocumentHoverMove(editorView);\n const wrapper = editorView.dom.closest(wrapperSelector);\n if (wrapper) {\n (wrapper as HTMLElement).style.position = \"relative\";\n wrapper.appendChild(handle);\n }\n\n document.addEventListener(\"mousemove\", handleDocumentHoverMove);\n\n handle.addEventListener(\"mousedown\", (e) => {\n e.stopPropagation();\n if (!editor.isEditable) {\n e.preventDefault();\n return;\n }\n\n if (!currentBlock || dragStartPos === null) return;\n\n const sourceNode = editorView.state.doc.nodeAt(dragStartPos);\n if (!sourceNode) return;\n\n e.preventDefault();\n dragSession = {\n view: editorView,\n sourceBlock: currentBlock,\n sourcePos: dragStartPos,\n sourceNodeSize: sourceNode.nodeSize,\n startX: e.clientX,\n startY: e.clientY,\n dragging: false,\n preview: null,\n dropLine: null,\n dropPos: null,\n };\n\n document.addEventListener(\"mousemove\", handleDocumentMouseMove);\n document.addEventListener(\"mouseup\", handleDocumentMouseUp);\n document.addEventListener(\"keydown\", handleDocumentKeyDown);\n });\n\n return {\n destroy() {\n document.removeEventListener(\n \"mousemove\",\n handleDocumentHoverMove,\n );\n finishDragSession(false);\n handle?.remove();\n handle = null;\n },\n };\n },\n props: {\n handleDOMEvents: {\n mousemove(view, event) {\n if (!handle) return false;\n if (!editor.isEditable) {\n hideHandle();\n return false;\n }\n if (dragSession) return false;\n\n const block = findForgivingBlock(\n view,\n event.clientX,\n event.clientY,\n );\n if (!block) {\n hideHandle();\n return false;\n }\n\n if (block.node === currentBlock) return false;\n showHandleForBlock(view, block);\n\n return false;\n },\n drop() {\n finishDragSession(false);\n hideHandle();\n return false;\n },\n },\n },\n }),\n ];\n },\n});\n"]}
@@ -0,0 +1,97 @@
1
+ import { type ReactNode } from "react";
2
+ import { Node, type NodeViewProps } from "@tiptap/react";
3
+ /** The minimal block shape the NodeView renders through `<BlockView>`. */
4
+ export interface RegistryBlockSideMapBlock {
5
+ id: string;
6
+ title?: string;
7
+ summary?: string;
8
+ data: unknown;
9
+ }
10
+ /**
11
+ * The side-map an editor host supplies so the registry NodeView can resolve a
12
+ * block's full typed `data` (and commit edits) by its stable id, without ever
13
+ * storing that data in the ProseMirror doc.
14
+ */
15
+ export interface RegistryBlockDataValue<TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock> {
16
+ /** Resolve a block's full record (incl. `data`) by its stable id. */
17
+ getBlock: (blockId: string) => TBlock | undefined;
18
+ /** Commit a new `data` value for a block (edit-mode only). */
19
+ onBlockDataChange: (blockId: string, nextData: unknown) => void;
20
+ /** Whether the document (and thus its blocks) is editable. */
21
+ editable: boolean;
22
+ /**
23
+ * When true, blocks whose type has no Notion (NFM) analog are badged so the
24
+ * author knows they won't sync. The host decides which types are incompatible
25
+ * via {@link isNotionIncompatibleType}; this flag just toggles the badge on.
26
+ */
27
+ notionSync?: boolean;
28
+ /**
29
+ * Decide whether a block type is Notion-incompatible (no NFM analog). Only
30
+ * consulted when {@link notionSync} is true. Injected by the host so the
31
+ * single registry-level allowlist (plan's `isNotionCompatibleBlockType`, or
32
+ * content's registry-derived gate) drives the badge — core stays policy-free.
33
+ */
34
+ isNotionIncompatibleType?: (blockType: string) => boolean;
35
+ /**
36
+ * Render a block whose type is NOT in the registry through the host's own
37
+ * dispatcher (plan: `PlanBlockView` for decision / legacy visual-questions /
38
+ * image; omitted in hosts with no legacy types), so every block type renders
39
+ * in the document instead of a bare fallback.
40
+ */
41
+ renderLegacyBlock?: (block: TBlock, options: {
42
+ editing: boolean;
43
+ }) => ReactNode;
44
+ }
45
+ export declare function RegistryBlockDataProvider<TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock>({ value, children, }: {
46
+ value: RegistryBlockDataValue<TBlock>;
47
+ children: ReactNode;
48
+ }): import("react/jsx-runtime").JSX.Element;
49
+ /** Read the registry block side-map. Returns `null` outside a provider. */
50
+ export declare function useRegistryBlockData<TBlock extends RegistryBlockSideMapBlock = RegistryBlockSideMapBlock>(): RegistryBlockDataValue<TBlock> | null;
51
+ /**
52
+ * Renders one registry-block atom. The block is non-editable as far as
53
+ * ProseMirror is concerned (`contentEditable={false}`); all interaction happens
54
+ * inside the registry-driven `<BlockView>`. Read vs edit is toggled by
55
+ * `props.selected` (the node is "selected" in the editor) AND the document being
56
+ * editable. `data-plan-interactive` keeps existing host click-guards from
57
+ * treating clicks inside the block as document clicks.
58
+ */
59
+ export declare function RegistryBlockNodeView(props: NodeViewProps): import("react/jsx-runtime").JSX.Element;
60
+ /** Options for {@link createRegistryBlockNode}. */
61
+ export interface CreateRegistryBlockNodeOptions {
62
+ /**
63
+ * The Tiptap node name (e.g. `"planBlock"`). Hosts that serialize the doc by
64
+ * node name (plan's `plan-doc.ts` keys off `"planBlock"`) must pass the exact
65
+ * name their serializer expects.
66
+ */
67
+ nodeName: string;
68
+ /**
69
+ * The HTML data-attribute that marks a serialized registry block on copy/paste
70
+ * round-trip (e.g. `"data-plan-block"`).
71
+ */
72
+ dataTag: string;
73
+ /**
74
+ * Mint a fresh, unique block id for a given block type. Used by the dedupe
75
+ * plugin to re-mint duplicate / missing ids (paste/duplicate). Plan passes
76
+ * `createPlanBlockId`.
77
+ */
78
+ mintId: (blockType: string) => string;
79
+ /** Node group (default `"block"`). */
80
+ group?: string;
81
+ }
82
+ /**
83
+ * Build the generic registry-block Tiptap atom node. Returns a Tiptap `Node`
84
+ * that:
85
+ * - carries identity attrs `blockType` / `blockId` / `title` / `summary`, a
86
+ * `sourceBlockId` (set when a duplicate is re-minted, so the host can copy the
87
+ * original block's data), and an optional `__raw` verbatim-MDX attr for
88
+ * byte-stable source round-trips;
89
+ * - is an atom + isolating + draggable block that renders through
90
+ * {@link RegistryBlockNodeView} (via `ReactNodeViewRenderer`);
91
+ * - installs a dedupe `appendTransaction` plugin that re-mints any duplicate or
92
+ * empty `blockId` (the classic paste/duplicate case), preserving the original
93
+ * block's id + side-map data and tagging its own transaction so it never
94
+ * loops.
95
+ */
96
+ export declare function createRegistryBlockNode(options: CreateRegistryBlockNodeOptions): Node<any, any>;
97
+ //# sourceMappingURL=RegistryBlockNode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RegistryBlockNode.d.ts","sourceRoot":"","sources":["../../../src/client/rich-markdown-editor/RegistryBlockNode.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA6B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAClE,OAAO,EACL,IAAI,EAIJ,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAuBvB,0EAA0E;AAC1E,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB,CACrC,MAAM,SAAS,yBAAyB,GAAG,yBAAyB;IAEpE,qEAAqE;IACrE,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAClD,8DAA8D;IAC9D,iBAAiB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAChE,8DAA8D;IAC9D,QAAQ,EAAE,OAAO,CAAC;IAClB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;;OAKG;IACH,wBAAwB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC1D;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,CAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,KAC1B,SAAS,CAAC;CAChB;AAKD,wBAAgB,yBAAyB,CACvC,MAAM,SAAS,yBAAyB,GAAG,yBAAyB,EACpE,EACA,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC;IACtC,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAMA;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAClC,MAAM,SAAS,yBAAyB,GAAG,yBAAyB,KACjE,sBAAsB,CAAC,MAAM,CAAC,GAAG,IAAI,CAIzC;AAMD;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,aAAa,2CA2FzD;AAMD,mDAAmD;AACnD,MAAM,WAAW,8BAA8B;IAC7C;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IACtC,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,8BAA8B,kBAyJxC"}