@liminis/editor 0.1.0-rc.1
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/LICENSE +34 -0
- package/README.md +376 -0
- package/dist/annotations/anchor-align.d.ts +50 -0
- package/dist/annotations/anchor-align.js +74 -0
- package/dist/annotations/anchor-model.d.ts +81 -0
- package/dist/annotations/anchor-model.js +80 -0
- package/dist/annotations/anchor-resolver.d.ts +92 -0
- package/dist/annotations/anchor-resolver.js +231 -0
- package/dist/annotations/block-structure.d.ts +65 -0
- package/dist/annotations/block-structure.js +113 -0
- package/dist/annotations/types.d.ts +159 -0
- package/dist/annotations/types.js +50 -0
- package/dist/annotations.d.ts +25 -0
- package/dist/annotations.js +27 -0
- package/dist/app/App.d.ts +64 -0
- package/dist/app/App.js +180 -0
- package/dist/app/editor/AmbientCorrectionPlugin.d.ts +18 -0
- package/dist/app/editor/AmbientCorrectionPlugin.js +185 -0
- package/dist/app/editor/AnchorScrollPlugin.d.ts +16 -0
- package/dist/app/editor/AnchorScrollPlugin.js +134 -0
- package/dist/app/editor/AnnotationMarkerPlugin.d.ts +24 -0
- package/dist/app/editor/AnnotationMarkerPlugin.js +224 -0
- package/dist/app/editor/AnnotationPlugin.d.ts +42 -0
- package/dist/app/editor/AnnotationPlugin.js +135 -0
- package/dist/app/editor/AnnotationSurface.d.ts +34 -0
- package/dist/app/editor/AnnotationSurface.js +115 -0
- package/dist/app/editor/AssetContext.d.ts +20 -0
- package/dist/app/editor/AssetContext.js +81 -0
- package/dist/app/editor/BlockClickPlugin.d.ts +8 -0
- package/dist/app/editor/BlockClickPlugin.js +126 -0
- package/dist/app/editor/CodeBlockPlugin.d.ts +1 -0
- package/dist/app/editor/CodeBlockPlugin.js +219 -0
- package/dist/app/editor/CorrectionPanelPlugin.d.ts +14 -0
- package/dist/app/editor/CorrectionPanelPlugin.js +333 -0
- package/dist/app/editor/DragHandlePlugin.d.ts +1 -0
- package/dist/app/editor/DragHandlePlugin.js +300 -0
- package/dist/app/editor/Editor.d.ts +84 -0
- package/dist/app/editor/Editor.js +539 -0
- package/dist/app/editor/FrontmatterPlugin.d.ts +19 -0
- package/dist/app/editor/FrontmatterPlugin.js +301 -0
- package/dist/app/editor/ImageModal.d.ts +13 -0
- package/dist/app/editor/ImageModal.js +267 -0
- package/dist/app/editor/ImagePlugin.d.ts +3 -0
- package/dist/app/editor/ImagePlugin.js +187 -0
- package/dist/app/editor/LinkClickPlugin.d.ts +23 -0
- package/dist/app/editor/LinkClickPlugin.js +90 -0
- package/dist/app/editor/MarkdownShortcutsPlugin.d.ts +49 -0
- package/dist/app/editor/MarkdownShortcutsPlugin.js +373 -0
- package/dist/app/editor/OrderedTaskListPlugin.d.ts +21 -0
- package/dist/app/editor/OrderedTaskListPlugin.js +78 -0
- package/dist/app/editor/SearchPlugin.d.ts +1 -0
- package/dist/app/editor/SearchPlugin.js +379 -0
- package/dist/app/editor/SelectionContextMenuPlugin.d.ts +29 -0
- package/dist/app/editor/SelectionContextMenuPlugin.js +160 -0
- package/dist/app/editor/SlashMenu.d.ts +11 -0
- package/dist/app/editor/SlashMenu.js +494 -0
- package/dist/app/editor/SlashMenuPlugin.d.ts +1 -0
- package/dist/app/editor/SlashMenuPlugin.js +168 -0
- package/dist/app/editor/TableActionsPlugin.d.ts +1 -0
- package/dist/app/editor/TableActionsPlugin.js +398 -0
- package/dist/app/editor/TogglePlugin.d.ts +1 -0
- package/dist/app/editor/TogglePlugin.js +159 -0
- package/dist/app/editor/Toolbar.d.ts +10 -0
- package/dist/app/editor/Toolbar.js +263 -0
- package/dist/app/editor/WikiLinkExistencePlugin.d.ts +17 -0
- package/dist/app/editor/WikiLinkExistencePlugin.js +104 -0
- package/dist/app/editor/WikiLinkFormatPlugin.d.ts +17 -0
- package/dist/app/editor/WikiLinkFormatPlugin.js +203 -0
- package/dist/app/editor/annotation-marks.d.ts +177 -0
- package/dist/app/editor/annotation-marks.js +750 -0
- package/dist/app/editor/annotationCommands.d.ts +13 -0
- package/dist/app/editor/annotationCommands.js +11 -0
- package/dist/app/editor/c4/C4InteractiveRenderer.d.ts +35 -0
- package/dist/app/editor/c4/C4InteractiveRenderer.js +299 -0
- package/dist/app/editor/c4/edge-clipping.d.ts +24 -0
- package/dist/app/editor/c4/edge-clipping.js +139 -0
- package/dist/app/editor/c4/hooks/useC4DiagramDrag.d.ts +38 -0
- package/dist/app/editor/c4/hooks/useC4DiagramDrag.js +112 -0
- package/dist/app/editor/c4/layout.d.ts +25 -0
- package/dist/app/editor/c4/layout.js +839 -0
- package/dist/app/editor/c4/parser.d.ts +19 -0
- package/dist/app/editor/c4/parser.js +410 -0
- package/dist/app/editor/c4/render-to-string.d.ts +24 -0
- package/dist/app/editor/c4/render-to-string.js +34 -0
- package/dist/app/editor/c4/renderer.d.ts +64 -0
- package/dist/app/editor/c4/renderer.js +569 -0
- package/dist/app/editor/c4/types.d.ts +203 -0
- package/dist/app/editor/c4/types.js +43 -0
- package/dist/app/editor/correction-yaml.d.ts +10 -0
- package/dist/app/editor/correction-yaml.js +40 -0
- package/dist/app/editor/editorNodes.d.ts +9 -0
- package/dist/app/editor/editorNodes.js +36 -0
- package/dist/app/editor/index.d.ts +1 -0
- package/dist/app/editor/index.js +1 -0
- package/dist/app/editor/mark-overlap-resolver.d.ts +6 -0
- package/dist/app/editor/mark-overlap-resolver.js +45 -0
- package/dist/app/editor/nodes/C4Component.d.ts +14 -0
- package/dist/app/editor/nodes/C4Component.js +284 -0
- package/dist/app/editor/nodes/C4Node.d.ts +32 -0
- package/dist/app/editor/nodes/C4Node.js +112 -0
- package/dist/app/editor/nodes/CalloutNode.d.ts +23 -0
- package/dist/app/editor/nodes/CalloutNode.js +94 -0
- package/dist/app/editor/nodes/CustomLinkNode.d.ts +57 -0
- package/dist/app/editor/nodes/CustomLinkNode.js +209 -0
- package/dist/app/editor/nodes/CustomListItemNode.d.ts +46 -0
- package/dist/app/editor/nodes/CustomListItemNode.js +120 -0
- package/dist/app/editor/nodes/CustomListNode.d.ts +27 -0
- package/dist/app/editor/nodes/CustomListNode.js +54 -0
- package/dist/app/editor/nodes/DefinitionListNode.d.ts +44 -0
- package/dist/app/editor/nodes/DefinitionListNode.js +151 -0
- package/dist/app/editor/nodes/DiagramContextMenu.d.ts +45 -0
- package/dist/app/editor/nodes/DiagramContextMenu.js +116 -0
- package/dist/app/editor/nodes/EquationComponent.d.ts +17 -0
- package/dist/app/editor/nodes/EquationComponent.js +130 -0
- package/dist/app/editor/nodes/EquationNode.d.ts +43 -0
- package/dist/app/editor/nodes/EquationNode.js +179 -0
- package/dist/app/editor/nodes/FootnoteNode.d.ts +40 -0
- package/dist/app/editor/nodes/FootnoteNode.js +151 -0
- package/dist/app/editor/nodes/FrontmatterNode.d.ts +24 -0
- package/dist/app/editor/nodes/FrontmatterNode.js +88 -0
- package/dist/app/editor/nodes/HorizontalRuleNode.d.ts +17 -0
- package/dist/app/editor/nodes/HorizontalRuleNode.js +57 -0
- package/dist/app/editor/nodes/HtmlNode.d.ts +32 -0
- package/dist/app/editor/nodes/HtmlNode.js +121 -0
- package/dist/app/editor/nodes/ImageComponent.d.ts +11 -0
- package/dist/app/editor/nodes/ImageComponent.js +228 -0
- package/dist/app/editor/nodes/ImageNode.d.ts +53 -0
- package/dist/app/editor/nodes/ImageNode.js +183 -0
- package/dist/app/editor/nodes/ListItemParagraphBreakNode.d.ts +42 -0
- package/dist/app/editor/nodes/ListItemParagraphBreakNode.js +95 -0
- package/dist/app/editor/nodes/MermaidComponent.d.ts +11 -0
- package/dist/app/editor/nodes/MermaidComponent.js +216 -0
- package/dist/app/editor/nodes/MermaidNode.d.ts +27 -0
- package/dist/app/editor/nodes/MermaidNode.js +96 -0
- package/dist/app/editor/nodes/ToggleNode.d.ts +56 -0
- package/dist/app/editor/nodes/ToggleNode.js +237 -0
- package/dist/app/editor/nodes/diagram-context-menu.d.ts +11 -0
- package/dist/app/editor/nodes/diagram-context-menu.js +70 -0
- package/dist/app/editor/nodes/diagram-utils.d.ts +13 -0
- package/dist/app/editor/nodes/diagram-utils.js +29 -0
- package/dist/app/editor/nodes/index.d.ts +30 -0
- package/dist/app/editor/nodes/index.js +17 -0
- package/dist/app/editor/utils.d.ts +2 -0
- package/dist/app/editor/utils.js +17 -0
- package/dist/app/mapper/index.d.ts +3 -0
- package/dist/app/mapper/index.js +2 -0
- package/dist/app/mapper/lexicalToMdast.d.ts +62 -0
- package/dist/app/mapper/lexicalToMdast.js +1937 -0
- package/dist/app/mapper/mdastToLexical.d.ts +31 -0
- package/dist/app/mapper/mdastToLexical.js +1075 -0
- package/dist/contract.d.ts +8 -0
- package/dist/contract.js +8 -0
- package/dist/headless.d.ts +40 -0
- package/dist/headless.js +45 -0
- package/dist/host/context.d.ts +20 -0
- package/dist/host/context.js +24 -0
- package/dist/host/defaults.d.ts +21 -0
- package/dist/host/defaults.js +47 -0
- package/dist/host/messages.d.ts +26 -0
- package/dist/host/messages.js +47 -0
- package/dist/host/types.d.ts +75 -0
- package/dist/host/types.js +13 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +51 -0
- package/dist/markdown/annotate-sentinels.d.ts +42 -0
- package/dist/markdown/annotate-sentinels.js +86 -0
- package/dist/markdown/parse.d.ts +56 -0
- package/dist/markdown/parse.js +516 -0
- package/dist/markdown/stringify.d.ts +7 -0
- package/dist/markdown/stringify.js +439 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/LICENSE +21 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/README.md +41 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.d.ts +72 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/from-markdown.js +127 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/index.d.ts +8 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/index.js +6 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.d.ts +17 -0
- package/dist/markdown/vendor/mdast-util-wiki-link/to-markdown.js +33 -0
- package/dist/markdown.d.ts +28 -0
- package/dist/markdown.js +44 -0
- package/dist/mathjax-config.d.ts +89 -0
- package/dist/mathjax-config.js +133 -0
- package/dist/nodes.d.ts +34 -0
- package/dist/nodes.js +34 -0
- package/dist/stores/correctionStore.d.ts +15 -0
- package/dist/stores/correctionStore.js +12 -0
- package/dist/stores/editorStore.d.ts +16 -0
- package/dist/stores/editorStore.js +27 -0
- package/dist/styles.css +2477 -0
- package/dist/types.d.ts +296 -0
- package/dist/types.js +91 -0
- package/dist/utils/file-types.d.ts +10 -0
- package/dist/utils/file-types.js +46 -0
- package/docs/annotations.md +134 -0
- package/docs/decisions/README.md +72 -0
- package/docs/decisions/adr-002.md +27 -0
- package/docs/decisions/adr-007.md +27 -0
- package/docs/decisions/adr-010.md +26 -0
- package/docs/decisions/adr-024.md +124 -0
- package/docs/decisions/adr-024b.md +0 -0
- package/docs/decisions/adr-025.md +103 -0
- package/docs/decisions/adr-027.md +66 -0
- package/docs/decisions/adr-057.md +67 -0
- package/docs/decisions/adr-070.md +131 -0
- package/docs/decisions/adr-075.md +410 -0
- package/docs/decisions/adr-076.md +183 -0
- package/docs/decisions/adr-077.md +410 -0
- package/docs/decisions/adr-078.md +243 -0
- package/docs/decisions/adr-079.md +167 -0
- package/docs/decisions/adr-080.md +325 -0
- package/docs/decisions/adr-081.md +108 -0
- package/docs/decisions/adr-082.md +123 -0
- package/docs/decisions/adr-083.md +204 -0
- package/docs/decisions/adr-084.md +179 -0
- package/docs/editor-api.md +106 -0
- package/docs/markdown-pipeline.md +282 -0
- package/docs/provenance.md +106 -0
- package/docs/zusammen-editor-capability-map.md +232 -0
- package/package.json +175 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useRef, useState, useCallback } from 'react';
|
|
3
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
4
|
+
import { $getNodeByKey, $getSelection, $isRangeSelection, $createParagraphNode } from 'lexical';
|
|
5
|
+
// Find the top-level block element (direct child of editor root)
|
|
6
|
+
function getBlockElement(target, editorRoot) {
|
|
7
|
+
let element = target;
|
|
8
|
+
// Walk up until we find a direct child of the editor root
|
|
9
|
+
while (element && element.parentElement !== editorRoot) {
|
|
10
|
+
element = element.parentElement;
|
|
11
|
+
}
|
|
12
|
+
// Return the element if it's a valid block (direct child of editor root)
|
|
13
|
+
if (element?.parentElement === editorRoot) {
|
|
14
|
+
return element;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
// Find block element based on Y position
|
|
19
|
+
function getBlockElementAtY(y, editorRoot) {
|
|
20
|
+
for (const child of editorRoot.children) {
|
|
21
|
+
const rect = child.getBoundingClientRect();
|
|
22
|
+
if (y >= rect.top && y <= rect.bottom) {
|
|
23
|
+
return child;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
// Get the node key from a block element's internal Lexical property
|
|
29
|
+
function getNodeKeyFromBlockElement(element) {
|
|
30
|
+
// Lexical stores the key in __lexicalKey_[editorKey] property
|
|
31
|
+
const keys = Object.keys(element);
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
if (key.startsWith('__lexicalKey_')) {
|
|
34
|
+
return element[key] || null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
export function DragHandlePlugin() {
|
|
40
|
+
const [editor] = useLexicalComposerContext();
|
|
41
|
+
const [isEditable, setIsEditable] = useState(editor.isEditable());
|
|
42
|
+
const [dragState, setDragState] = useState({
|
|
43
|
+
isVisible: false,
|
|
44
|
+
position: { top: 0, left: 0 },
|
|
45
|
+
nodeKey: null,
|
|
46
|
+
blockElement: null,
|
|
47
|
+
});
|
|
48
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
49
|
+
const [dropIndicator, setDropIndicator] = useState(null);
|
|
50
|
+
const draggedNodeKey = useRef(null);
|
|
51
|
+
const dropTargetKey = useRef(null);
|
|
52
|
+
const dropPosition = useRef('after');
|
|
53
|
+
const handleRef = useRef(null);
|
|
54
|
+
// Use ref to access isDragging in event handlers without causing re-registration
|
|
55
|
+
const isDraggingRef = useRef(false);
|
|
56
|
+
// Sync isDragging state to ref (must be in effect, not during render)
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
isDraggingRef.current = isDragging;
|
|
59
|
+
}, [isDragging]);
|
|
60
|
+
// Track editable state changes
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
return editor.registerEditableListener((editable) => {
|
|
63
|
+
setIsEditable(editable);
|
|
64
|
+
});
|
|
65
|
+
}, [editor]);
|
|
66
|
+
// Listen for selection changes to hide handle when navigating via keyboard
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
return editor.registerUpdateListener(({ editorState }) => {
|
|
69
|
+
if (isDraggingRef.current)
|
|
70
|
+
return;
|
|
71
|
+
editorState.read(() => {
|
|
72
|
+
const selection = $getSelection();
|
|
73
|
+
if (!$isRangeSelection(selection)) {
|
|
74
|
+
setDragState((prev) => ({ ...prev, isVisible: false }));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Get the current anchor node and find its top-level block
|
|
78
|
+
const anchorNode = selection.anchor.getNode();
|
|
79
|
+
const topLevelElement = anchorNode.getTopLevelElement();
|
|
80
|
+
if (!topLevelElement) {
|
|
81
|
+
setDragState((prev) => ({ ...prev, isVisible: false }));
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const nodeKey = topLevelElement.getKey();
|
|
85
|
+
const editorElement = editor.getRootElement();
|
|
86
|
+
if (!editorElement)
|
|
87
|
+
return;
|
|
88
|
+
// Find the DOM element for this node
|
|
89
|
+
const blockElement = editor.getElementByKey(nodeKey);
|
|
90
|
+
if (!blockElement) {
|
|
91
|
+
setDragState((prev) => ({ ...prev, isVisible: false }));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const rect = blockElement.getBoundingClientRect();
|
|
95
|
+
const editorRect = editorElement.getBoundingClientRect();
|
|
96
|
+
setDragState({
|
|
97
|
+
isVisible: true,
|
|
98
|
+
position: {
|
|
99
|
+
top: rect.top - editorRect.top,
|
|
100
|
+
left: 0,
|
|
101
|
+
},
|
|
102
|
+
nodeKey,
|
|
103
|
+
blockElement,
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
}, [editor]);
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
const editorElement = editor.getRootElement();
|
|
110
|
+
if (!editorElement)
|
|
111
|
+
return;
|
|
112
|
+
// Get the .editor-inner container (parent of editor root)
|
|
113
|
+
const editorInner = editorElement.closest('.editor-inner');
|
|
114
|
+
if (!editorInner)
|
|
115
|
+
return;
|
|
116
|
+
const handleMouseMove = (event) => {
|
|
117
|
+
if (isDraggingRef.current)
|
|
118
|
+
return;
|
|
119
|
+
const mouseEvent = event;
|
|
120
|
+
const target = mouseEvent.target;
|
|
121
|
+
// Ignore if hovering over the drag handle itself
|
|
122
|
+
if (target.closest('.drag-handle')) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
// Try to find block element directly under cursor
|
|
126
|
+
let blockElement = getBlockElement(target, editorElement);
|
|
127
|
+
// If not found (e.g., in padding area), find by Y position
|
|
128
|
+
if (!blockElement) {
|
|
129
|
+
blockElement = getBlockElementAtY(mouseEvent.clientY, editorElement);
|
|
130
|
+
}
|
|
131
|
+
if (blockElement) {
|
|
132
|
+
const nodeKey = getNodeKeyFromBlockElement(blockElement);
|
|
133
|
+
if (nodeKey) {
|
|
134
|
+
const rect = blockElement.getBoundingClientRect();
|
|
135
|
+
const editorRect = editorElement.getBoundingClientRect();
|
|
136
|
+
setDragState({
|
|
137
|
+
isVisible: true,
|
|
138
|
+
position: {
|
|
139
|
+
top: rect.top - editorRect.top,
|
|
140
|
+
left: 0,
|
|
141
|
+
},
|
|
142
|
+
nodeKey,
|
|
143
|
+
blockElement,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
setDragState((prev) => ({ ...prev, isVisible: false }));
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const handleMouseLeave = () => {
|
|
152
|
+
if (isDraggingRef.current)
|
|
153
|
+
return;
|
|
154
|
+
setDragState((prev) => ({ ...prev, isVisible: false }));
|
|
155
|
+
};
|
|
156
|
+
// Native drag events for drop handling
|
|
157
|
+
const handleDragOver = (event) => {
|
|
158
|
+
const dragEvent = event;
|
|
159
|
+
if (!draggedNodeKey.current)
|
|
160
|
+
return;
|
|
161
|
+
dragEvent.preventDefault();
|
|
162
|
+
dragEvent.dataTransfer.dropEffect = 'move';
|
|
163
|
+
const target = dragEvent.target;
|
|
164
|
+
let blockElement = getBlockElement(target, editorElement);
|
|
165
|
+
if (!blockElement) {
|
|
166
|
+
blockElement = getBlockElementAtY(dragEvent.clientY, editorElement);
|
|
167
|
+
}
|
|
168
|
+
if (blockElement) {
|
|
169
|
+
const nodeKey = getNodeKeyFromBlockElement(blockElement);
|
|
170
|
+
if (nodeKey && nodeKey !== draggedNodeKey.current) {
|
|
171
|
+
dropTargetKey.current = nodeKey;
|
|
172
|
+
// Determine if dropping before or after based on cursor position
|
|
173
|
+
const rect = blockElement.getBoundingClientRect();
|
|
174
|
+
const midpoint = rect.top + rect.height / 2;
|
|
175
|
+
dropPosition.current = dragEvent.clientY < midpoint ? 'before' : 'after';
|
|
176
|
+
// Show drop indicator
|
|
177
|
+
const editorRect = editorElement.getBoundingClientRect();
|
|
178
|
+
const indicatorTop = dropPosition.current === 'before'
|
|
179
|
+
? rect.top - editorRect.top
|
|
180
|
+
: rect.bottom - editorRect.top;
|
|
181
|
+
setDropIndicator({ top: indicatorTop });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const handleDrop = (event) => {
|
|
186
|
+
const dragEvent = event;
|
|
187
|
+
dragEvent.preventDefault();
|
|
188
|
+
if (draggedNodeKey.current && dropTargetKey.current) {
|
|
189
|
+
editor.update(() => {
|
|
190
|
+
const draggedNode = $getNodeByKey(draggedNodeKey.current);
|
|
191
|
+
const targetNode = $getNodeByKey(dropTargetKey.current);
|
|
192
|
+
if (draggedNode && targetNode && draggedNode !== targetNode) {
|
|
193
|
+
if (dropPosition.current === 'before') {
|
|
194
|
+
targetNode.insertBefore(draggedNode);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
targetNode.insertAfter(draggedNode);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
// Reset state
|
|
203
|
+
draggedNodeKey.current = null;
|
|
204
|
+
dropTargetKey.current = null;
|
|
205
|
+
setIsDragging(false);
|
|
206
|
+
setDropIndicator(null);
|
|
207
|
+
};
|
|
208
|
+
const handleDragLeave = (event) => {
|
|
209
|
+
const dragEvent = event;
|
|
210
|
+
// Only hide indicator if leaving the editor entirely
|
|
211
|
+
const relatedTarget = dragEvent.relatedTarget;
|
|
212
|
+
if (!relatedTarget || !editorInner.contains(relatedTarget)) {
|
|
213
|
+
setDropIndicator(null);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
editorInner.addEventListener('mousemove', handleMouseMove);
|
|
217
|
+
editorInner.addEventListener('mouseleave', handleMouseLeave);
|
|
218
|
+
editorInner.addEventListener('dragover', handleDragOver);
|
|
219
|
+
editorInner.addEventListener('drop', handleDrop);
|
|
220
|
+
editorInner.addEventListener('dragleave', handleDragLeave);
|
|
221
|
+
return () => {
|
|
222
|
+
editorInner.removeEventListener('mousemove', handleMouseMove);
|
|
223
|
+
editorInner.removeEventListener('mouseleave', handleMouseLeave);
|
|
224
|
+
editorInner.removeEventListener('dragover', handleDragOver);
|
|
225
|
+
editorInner.removeEventListener('drop', handleDrop);
|
|
226
|
+
editorInner.removeEventListener('dragleave', handleDragLeave);
|
|
227
|
+
};
|
|
228
|
+
}, [editor]);
|
|
229
|
+
const handleDragStart = (event) => {
|
|
230
|
+
if (dragState.nodeKey) {
|
|
231
|
+
draggedNodeKey.current = dragState.nodeKey;
|
|
232
|
+
setIsDragging(true);
|
|
233
|
+
event.dataTransfer.effectAllowed = 'move';
|
|
234
|
+
event.dataTransfer.setData('text/plain', dragState.nodeKey);
|
|
235
|
+
// Set a drag image
|
|
236
|
+
if (dragState.blockElement) {
|
|
237
|
+
event.dataTransfer.setDragImage(dragState.blockElement, 0, 0);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
const handleDragEnd = () => {
|
|
242
|
+
setIsDragging(false);
|
|
243
|
+
setDropIndicator(null);
|
|
244
|
+
draggedNodeKey.current = null;
|
|
245
|
+
dropTargetKey.current = null;
|
|
246
|
+
};
|
|
247
|
+
const handleDelete = useCallback(() => {
|
|
248
|
+
if (!dragState.nodeKey)
|
|
249
|
+
return;
|
|
250
|
+
editor.update(() => {
|
|
251
|
+
const node = $getNodeByKey(dragState.nodeKey);
|
|
252
|
+
if (node) {
|
|
253
|
+
// If this is the only node, replace with empty paragraph
|
|
254
|
+
const parent = node.getParent();
|
|
255
|
+
if (parent?.getChildrenSize() === 1) {
|
|
256
|
+
const paragraph = $createParagraphNode();
|
|
257
|
+
node.replace(paragraph);
|
|
258
|
+
paragraph.selectStart();
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
// Select the next or previous node before removing
|
|
262
|
+
const nextSibling = node.getNextSibling();
|
|
263
|
+
const prevSibling = node.getPreviousSibling();
|
|
264
|
+
node.remove();
|
|
265
|
+
if (nextSibling) {
|
|
266
|
+
nextSibling.selectStart();
|
|
267
|
+
}
|
|
268
|
+
else if (prevSibling) {
|
|
269
|
+
prevSibling.selectEnd();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
setDragState((prev) => ({ ...prev, isVisible: false }));
|
|
275
|
+
}, [editor, dragState.nodeKey]);
|
|
276
|
+
// Don't render drag handle or delete button in read-only mode
|
|
277
|
+
if (!isEditable) {
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
return (_jsxs(_Fragment, { children: [dragState.isVisible && (_jsxs(_Fragment, { children: [_jsx("div", { ref: handleRef, className: `drag-handle ${isDragging ? 'dragging' : ''}`, style: {
|
|
281
|
+
position: 'absolute',
|
|
282
|
+
top: dragState.position.top,
|
|
283
|
+
left: dragState.position.left,
|
|
284
|
+
transform: 'translateX(-100%)',
|
|
285
|
+
}, draggable: true, onDragStart: handleDragStart, onDragEnd: handleDragEnd, "aria-label": "Drag to reorder block", role: "button", tabIndex: 0, children: _jsx("span", { className: "drag-handle-icon", children: "\u22EE\u22EE" }) }), _jsx("button", { className: "block-delete-button", style: {
|
|
286
|
+
position: 'absolute',
|
|
287
|
+
top: dragState.position.top,
|
|
288
|
+
right: 0,
|
|
289
|
+
transform: 'translateX(100%)',
|
|
290
|
+
}, onClick: handleDelete, "aria-label": "Delete block", title: "Delete block", tabIndex: 0, children: _jsx("span", { className: "block-delete-icon", children: "\u00D7" }) })] })), dropIndicator && (_jsx("div", { className: "drop-indicator", style: {
|
|
291
|
+
position: 'absolute',
|
|
292
|
+
top: dropIndicator.top,
|
|
293
|
+
left: 0,
|
|
294
|
+
right: 0,
|
|
295
|
+
height: 2,
|
|
296
|
+
background: 'var(--vscode-focusBorder, #007acc)',
|
|
297
|
+
pointerEvents: 'none',
|
|
298
|
+
zIndex: 100,
|
|
299
|
+
} }))] }));
|
|
300
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type MutableRefObject } from 'react';
|
|
2
|
+
import type { SelectionContextMenuEvent } from './SelectionContextMenuPlugin.js';
|
|
3
|
+
import { type SweepFn } from './AmbientCorrectionPlugin.js';
|
|
4
|
+
import type { Annotation, AnnotationEditorHandle, AnnotationKindConfigs } from '../../annotations/types.js';
|
|
5
|
+
import type { AnnotationCreateEvent } from './AnnotationPlugin.js';
|
|
6
|
+
import { type WikiLinkPromotionMode } from '../mapper/lexicalToMdast.js';
|
|
7
|
+
import type { ImagePathResolution } from '../../types.js';
|
|
8
|
+
import type { CursorState } from '../App.js';
|
|
9
|
+
interface EditorProps {
|
|
10
|
+
initialContent: string;
|
|
11
|
+
/**
|
|
12
|
+
* Whether the editor takes focus shortly after mounting. Defaults to
|
|
13
|
+
* `false`: an embedded editor does not know whether it is the surface the
|
|
14
|
+
* user is working in, and the host does.
|
|
15
|
+
*
|
|
16
|
+
* Opt in where opening the editor *is* the user's action — clicking a
|
|
17
|
+
* document to edit it. Do not opt in for an editor that mounts beside
|
|
18
|
+
* something else the user may be typing in, renders in a preview or a list,
|
|
19
|
+
* or remounts while focus belongs elsewhere.
|
|
20
|
+
*
|
|
21
|
+
* This defaulted to on until it was found to steal the caret out of
|
|
22
|
+
* whatever the user was actually using. Focus is taken on a timer after
|
|
23
|
+
* mount, so a host cannot reliably take it back afterwards — it has to be
|
|
24
|
+
* declined up front, which is why this is a prop and not something to work
|
|
25
|
+
* around at the call site.
|
|
26
|
+
*/
|
|
27
|
+
autoFocus?: boolean;
|
|
28
|
+
contentVersion?: number;
|
|
29
|
+
/** Ref to cursor state - read inside effects, not during render */
|
|
30
|
+
cursorToRestoreRef?: React.RefObject<CursorState | null>;
|
|
31
|
+
onChange: (markdown: string) => void;
|
|
32
|
+
onCursorChange?: (cursor: CursorState) => void;
|
|
33
|
+
onSelectionContextMenu?: (event: SelectionContextMenuEvent) => void;
|
|
34
|
+
assetBaseUri?: string;
|
|
35
|
+
documentDirUri?: string;
|
|
36
|
+
imagePathResolution?: ImagePathResolution;
|
|
37
|
+
/**
|
|
38
|
+
* Whether an untitled relative link (a `.md` path, a `.md` path with an
|
|
39
|
+
* anchor, a bare `#anchor`, or a directory-style path) is promoted to
|
|
40
|
+
* wiki-link syntax on export. Defaults to `'promote'`, today's only
|
|
41
|
+
* behavior — set to `'off'` for a host whose documents are rendered
|
|
42
|
+
* somewhere that doesn't understand wiki-link syntax (liminis#951). Never
|
|
43
|
+
* affects a genuine author-written `[[target]]` wiki-link, which always
|
|
44
|
+
* round-trips as one regardless of this setting.
|
|
45
|
+
*/
|
|
46
|
+
wikiLinkPromotion?: WikiLinkPromotionMode;
|
|
47
|
+
/** Resolve a workspace-relative file path to a data URL for display */
|
|
48
|
+
resolveLocalAsset?: (relativePath: string) => Promise<string | null>;
|
|
49
|
+
/** When false, the editor is read-only */
|
|
50
|
+
editable?: boolean;
|
|
51
|
+
/** Path to the file being edited (used for file-type-specific UI like .mdc) */
|
|
52
|
+
filePath?: string;
|
|
53
|
+
/** Called when a single-word substitution is detected after a debounce window. */
|
|
54
|
+
onSubstitutionDetected?: (oldTerm: string, newTerm: string) => void;
|
|
55
|
+
/** Ref populated with a sweep function by AmbientCorrectionPlugin when active. */
|
|
56
|
+
sweepRef?: MutableRefObject<SweepFn | null>;
|
|
57
|
+
/**
|
|
58
|
+
* Per-kind configuration. Supplying this is what turns the annotation
|
|
59
|
+
* mechanism on; the kinds themselves are the only difference between
|
|
60
|
+
* features built on it (a comment kind vs. a correction kind).
|
|
61
|
+
*/
|
|
62
|
+
annotationKinds?: AnnotationKindConfigs;
|
|
63
|
+
/** Host-supplied annotations to render markers for, already resolved by the host. */
|
|
64
|
+
annotations?: Annotation[];
|
|
65
|
+
/** Which annotation is currently active, for marker styling. */
|
|
66
|
+
activeAnnotationId?: string | null;
|
|
67
|
+
/** Host-driven scroll-to signal; `nonce` forces a re-scroll to the same id. */
|
|
68
|
+
scrollToAnnotation?: {
|
|
69
|
+
id: string;
|
|
70
|
+
nonce: number;
|
|
71
|
+
} | null;
|
|
72
|
+
/** Fires when a user creates an annotation via a kind's create affordance. */
|
|
73
|
+
onCreateAnnotation?: (event: AnnotationCreateEvent) => void;
|
|
74
|
+
/** Fires when a marker is activated; the host opens its own panel or thread. */
|
|
75
|
+
onActivateAnnotation?: (id: string) => void;
|
|
76
|
+
/** Populated with the imperative handle for the mounted editor's live marks. */
|
|
77
|
+
annotationEditorHandleRef?: MutableRefObject<AnnotationEditorHandle | null>;
|
|
78
|
+
/** Injected logger, so the package never imports a host logger (FR-010). */
|
|
79
|
+
annotationLogger?: {
|
|
80
|
+
warn: (message: string, ...args: unknown[]) => void;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
export declare function Editor({ initialContent, autoFocus, contentVersion, cursorToRestoreRef, onChange, onCursorChange, onSelectionContextMenu, assetBaseUri, documentDirUri, imagePathResolution, wikiLinkPromotion, resolveLocalAsset, editable, filePath, onSubstitutionDetected, sweepRef, annotationKinds, annotations, activeAnnotationId, scrollToAnnotation, onCreateAnnotation, onActivateAnnotation, annotationEditorHandleRef, annotationLogger, }: EditorProps): import("react").JSX.Element;
|
|
84
|
+
export {};
|