@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.
- package/dist/cli/create.d.ts.map +1 -1
- package/dist/cli/create.js +8 -1
- package/dist/cli/create.js.map +1 -1
- package/dist/cli/skills.d.ts +5 -4
- package/dist/cli/skills.d.ts.map +1 -1
- package/dist/cli/skills.js +450 -125
- package/dist/cli/skills.js.map +1 -1
- package/dist/client/blocks/BlockView.d.ts +13 -4
- package/dist/client/blocks/BlockView.d.ts.map +1 -1
- package/dist/client/blocks/BlockView.js +34 -13
- package/dist/client/blocks/BlockView.js.map +1 -1
- package/dist/client/blocks/SchemaBlockEditor.d.ts.map +1 -1
- package/dist/client/blocks/SchemaBlockEditor.js +96 -3
- package/dist/client/blocks/SchemaBlockEditor.js.map +1 -1
- package/dist/client/blocks/index.d.ts +18 -1
- package/dist/client/blocks/index.d.ts.map +1 -1
- package/dist/client/blocks/index.js +26 -1
- package/dist/client/blocks/index.js.map +1 -1
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts +6 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.js +135 -0
- package/dist/client/blocks/library/AnnotatedCodeBlock.js.map +1 -0
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts +20 -0
- package/dist/client/blocks/library/ApiEndpointBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/ApiEndpointBlock.js +131 -0
- package/dist/client/blocks/library/ApiEndpointBlock.js.map +1 -0
- package/dist/client/blocks/library/DataModelBlock.d.ts +28 -0
- package/dist/client/blocks/library/DataModelBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/DataModelBlock.js +222 -0
- package/dist/client/blocks/library/DataModelBlock.js.map +1 -0
- package/dist/client/blocks/library/DiffBlock.d.ts +6 -0
- package/dist/client/blocks/library/DiffBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/DiffBlock.js +293 -0
- package/dist/client/blocks/library/DiffBlock.js.map +1 -0
- package/dist/client/blocks/library/FileTreeBlock.d.ts +23 -0
- package/dist/client/blocks/library/FileTreeBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/FileTreeBlock.js +225 -0
- package/dist/client/blocks/library/FileTreeBlock.js.map +1 -0
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts +19 -0
- package/dist/client/blocks/library/JsonExplorerBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/JsonExplorerBlock.js +171 -0
- package/dist/client/blocks/library/JsonExplorerBlock.js.map +1 -0
- package/dist/client/blocks/library/MermaidBlock.d.ts +17 -0
- package/dist/client/blocks/library/MermaidBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/MermaidBlock.js +131 -0
- package/dist/client/blocks/library/MermaidBlock.js.map +1 -0
- package/dist/client/blocks/library/OpenApiSpecBlock.d.ts +19 -0
- package/dist/client/blocks/library/OpenApiSpecBlock.d.ts.map +1 -0
- package/dist/client/blocks/library/OpenApiSpecBlock.js +494 -0
- package/dist/client/blocks/library/OpenApiSpecBlock.js.map +1 -0
- package/dist/client/blocks/library/annotated-code.config.d.ts +58 -0
- package/dist/client/blocks/library/annotated-code.config.d.ts.map +1 -0
- package/dist/client/blocks/library/annotated-code.config.js +53 -0
- package/dist/client/blocks/library/annotated-code.config.js.map +1 -0
- package/dist/client/blocks/library/api-endpoint.config.d.ts +71 -0
- package/dist/client/blocks/library/api-endpoint.config.d.ts.map +1 -0
- package/dist/client/blocks/library/api-endpoint.config.js +91 -0
- package/dist/client/blocks/library/api-endpoint.config.js.map +1 -0
- package/dist/client/blocks/library/checklist.d.ts.map +1 -1
- package/dist/client/blocks/library/checklist.js +3 -1
- package/dist/client/blocks/library/checklist.js.map +1 -1
- package/dist/client/blocks/library/code-tabs.js +1 -1
- package/dist/client/blocks/library/code-tabs.js.map +1 -1
- package/dist/client/blocks/library/data-model.config.d.ts +72 -0
- package/dist/client/blocks/library/data-model.config.d.ts.map +1 -0
- package/dist/client/blocks/library/data-model.config.js +59 -0
- package/dist/client/blocks/library/data-model.config.js.map +1 -0
- package/dist/client/blocks/library/dev-doc-ui.d.ts +49 -0
- package/dist/client/blocks/library/dev-doc-ui.d.ts.map +1 -0
- package/dist/client/blocks/library/dev-doc-ui.js +50 -0
- package/dist/client/blocks/library/dev-doc-ui.js.map +1 -0
- package/dist/client/blocks/library/diff.config.d.ts +41 -0
- package/dist/client/blocks/library/diff.config.d.ts.map +1 -0
- package/dist/client/blocks/library/diff.config.js +34 -0
- package/dist/client/blocks/library/diff.config.js.map +1 -0
- package/dist/client/blocks/library/file-tree.config.d.ts +59 -0
- package/dist/client/blocks/library/file-tree.config.d.ts.map +1 -0
- package/dist/client/blocks/library/file-tree.config.js +45 -0
- package/dist/client/blocks/library/file-tree.config.js.map +1 -0
- package/dist/client/blocks/library/html.d.ts.map +1 -1
- package/dist/client/blocks/library/html.js +4 -1
- package/dist/client/blocks/library/html.js.map +1 -1
- package/dist/client/blocks/library/json-explorer.config.d.ts +46 -0
- package/dist/client/blocks/library/json-explorer.config.d.ts.map +1 -0
- package/dist/client/blocks/library/json-explorer.config.js +28 -0
- package/dist/client/blocks/library/json-explorer.config.js.map +1 -0
- package/dist/client/blocks/library/mermaid.config.d.ts +32 -0
- package/dist/client/blocks/library/mermaid.config.d.ts.map +1 -0
- package/dist/client/blocks/library/mermaid.config.js +24 -0
- package/dist/client/blocks/library/mermaid.config.js.map +1 -0
- package/dist/client/blocks/library/openapi-spec.config.d.ts +49 -0
- package/dist/client/blocks/library/openapi-spec.config.d.ts.map +1 -0
- package/dist/client/blocks/library/openapi-spec.config.js +24 -0
- package/dist/client/blocks/library/openapi-spec.config.js.map +1 -0
- package/dist/client/blocks/library/server-specs.d.ts +35 -0
- package/dist/client/blocks/library/server-specs.d.ts.map +1 -0
- package/dist/client/blocks/library/server-specs.js +171 -0
- package/dist/client/blocks/library/server-specs.js.map +1 -0
- package/dist/client/blocks/library/specs.d.ts +29 -0
- package/dist/client/blocks/library/specs.d.ts.map +1 -0
- package/dist/client/blocks/library/specs.js +229 -0
- package/dist/client/blocks/library/specs.js.map +1 -0
- package/dist/client/blocks/library/table.d.ts.map +1 -1
- package/dist/client/blocks/library/table.js +3 -1
- package/dist/client/blocks/library/table.js.map +1 -1
- package/dist/client/blocks/library/tabs.js +1 -1
- package/dist/client/blocks/library/tabs.js.map +1 -1
- package/dist/client/blocks/registry.d.ts +8 -0
- package/dist/client/blocks/registry.d.ts.map +1 -1
- package/dist/client/blocks/registry.js +15 -0
- package/dist/client/blocks/registry.js.map +1 -1
- package/dist/client/blocks/server.d.ts +9 -0
- package/dist/client/blocks/server.d.ts.map +1 -1
- package/dist/client/blocks/server.js +16 -0
- package/dist/client/blocks/server.js.map +1 -1
- package/dist/client/blocks/types.d.ts +40 -0
- package/dist/client/blocks/types.d.ts.map +1 -1
- package/dist/client/blocks/types.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +10 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/DragHandle.d.ts +52 -0
- package/dist/client/rich-markdown-editor/DragHandle.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/DragHandle.js +403 -0
- package/dist/client/rich-markdown-editor/DragHandle.js.map +1 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts +97 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js +214 -0
- package/dist/client/rich-markdown-editor/RegistryBlockNode.js.map +1 -0
- package/dist/client/rich-markdown-editor/RunId.d.ts +28 -0
- package/dist/client/rich-markdown-editor/RunId.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/RunId.js +60 -0
- package/dist/client/rich-markdown-editor/RunId.js.map +1 -0
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts +25 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/SharedRichEditor.js +14 -5
- package/dist/client/rich-markdown-editor/SharedRichEditor.js.map +1 -1
- package/dist/client/rich-markdown-editor/gfmDoc.d.ts +24 -0
- package/dist/client/rich-markdown-editor/gfmDoc.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/gfmDoc.js +83 -0
- package/dist/client/rich-markdown-editor/gfmDoc.js.map +1 -0
- package/dist/client/rich-markdown-editor/index.d.ts +5 -0
- package/dist/client/rich-markdown-editor/index.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/index.js +5 -0
- package/dist/client/rich-markdown-editor/index.js.map +1 -1
- package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts +46 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.d.ts.map +1 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.js +13 -0
- package/dist/client/rich-markdown-editor/registrySlashCommands.js.map +1 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.d.ts.map +1 -1
- package/dist/client/rich-markdown-editor/useCollabReconcile.js +33 -0
- package/dist/client/rich-markdown-editor/useCollabReconcile.js.map +1 -1
- package/docs/content/template-plan.md +19 -4
- package/docs/content/visual-plans.md +3 -1
- 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"}
|