@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,539 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useRef, useMemo, useState, lazy, Suspense } from 'react';
|
|
3
|
+
import { LexicalComposer } from '@lexical/react/LexicalComposer';
|
|
4
|
+
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
|
|
5
|
+
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
|
|
6
|
+
import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
|
|
7
|
+
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
|
|
8
|
+
import { LexicalErrorBoundary } from '@lexical/react/LexicalErrorBoundary';
|
|
9
|
+
import { ListPlugin } from '@lexical/react/LexicalListPlugin';
|
|
10
|
+
import { CheckListPlugin } from '@lexical/react/LexicalCheckListPlugin';
|
|
11
|
+
import { TabIndentationPlugin } from '@lexical/react/LexicalTabIndentationPlugin';
|
|
12
|
+
import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
|
|
13
|
+
import { TablePlugin } from '@lexical/react/LexicalTablePlugin';
|
|
14
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
15
|
+
import { $isCodeNode } from '@lexical/code';
|
|
16
|
+
import { registerCodeHighlighting } from '@lexical/code-prism';
|
|
17
|
+
import { $createParagraphNode, $createRangeSelection, $getRoot, $getSelection, $isElementNode, $isLineBreakNode, $isRangeSelection, $isTextNode, $setSelection, COMMAND_PRIORITY_LOW, KEY_ENTER_COMMAND, } from 'lexical';
|
|
18
|
+
import { mergeRegister } from '@lexical/utils';
|
|
19
|
+
import { Toolbar } from './Toolbar.js';
|
|
20
|
+
import { SlashMenuPlugin } from './SlashMenuPlugin.js';
|
|
21
|
+
import { DragHandlePlugin } from './DragHandlePlugin.js';
|
|
22
|
+
import { MarkdownShortcutsPlugin } from './MarkdownShortcutsPlugin.js';
|
|
23
|
+
import { TableActionsPlugin } from './TableActionsPlugin.js';
|
|
24
|
+
import { CodeBlockPlugin } from './CodeBlockPlugin.js';
|
|
25
|
+
import { TogglePlugin } from './TogglePlugin.js';
|
|
26
|
+
import { ImagePlugin } from './ImagePlugin.js';
|
|
27
|
+
import { BlockClickPlugin } from './BlockClickPlugin.js';
|
|
28
|
+
import { LinkClickPlugin } from './LinkClickPlugin.js';
|
|
29
|
+
import { OrderedTaskListPlugin } from './OrderedTaskListPlugin.js';
|
|
30
|
+
import { SearchPlugin } from './SearchPlugin.js';
|
|
31
|
+
import { FrontmatterPlugin } from './FrontmatterPlugin.js';
|
|
32
|
+
import { WikiLinkExistencePlugin } from './WikiLinkExistencePlugin.js';
|
|
33
|
+
import { WikiLinkFormatPlugin } from './WikiLinkFormatPlugin.js';
|
|
34
|
+
import { AnchorScrollPlugin } from './AnchorScrollPlugin.js';
|
|
35
|
+
import { SelectionContextMenuPlugin } from './SelectionContextMenuPlugin.js';
|
|
36
|
+
import { CorrectionPanelPlugin } from './CorrectionPanelPlugin.js';
|
|
37
|
+
import { AmbientCorrectionPlugin } from './AmbientCorrectionPlugin.js';
|
|
38
|
+
import { AssetContext, createAssetContextValue } from './AssetContext.js';
|
|
39
|
+
import { editorNodes } from './editorNodes.js';
|
|
40
|
+
import { importMarkdownToLexicalInEditorStateWithOffsets, importMarkdownToLexicalWithOffsets, } from '../mapper/mdastToLexical.js';
|
|
41
|
+
import { exportLexicalToMdast } from '../mapper/lexicalToMdast.js';
|
|
42
|
+
import { parseMarkdown } from '../../markdown/parse.js';
|
|
43
|
+
import { stringifyMarkdown } from '../../markdown/stringify.js';
|
|
44
|
+
/**
|
|
45
|
+
* The annotation feature surface, loaded only when a host configures at least
|
|
46
|
+
* one kind. A static import here would defeat FR-004: every consumer would
|
|
47
|
+
* pull the annotation modules into its graph whether or not it uses them.
|
|
48
|
+
*/
|
|
49
|
+
const LazyAnnotationSurface = lazy(() => import('./AnnotationSurface.js'));
|
|
50
|
+
/** Stable identity, so an unconfigured `annotations` prop doesn't re-render the surface. */
|
|
51
|
+
const EMPTY_ANNOTATIONS = [];
|
|
52
|
+
const editorTheme = {
|
|
53
|
+
paragraph: 'editor-paragraph',
|
|
54
|
+
heading: {
|
|
55
|
+
h1: 'editor-heading-h1',
|
|
56
|
+
h2: 'editor-heading-h2',
|
|
57
|
+
h3: 'editor-heading-h3',
|
|
58
|
+
h4: 'editor-heading-h4',
|
|
59
|
+
h5: 'editor-heading-h5',
|
|
60
|
+
},
|
|
61
|
+
list: {
|
|
62
|
+
ul: 'editor-list-ul',
|
|
63
|
+
ol: 'editor-list-ol',
|
|
64
|
+
listitem: 'editor-listitem',
|
|
65
|
+
listitemChecked: 'editor-listitem-checked',
|
|
66
|
+
listitemUnchecked: 'editor-listitem-unchecked',
|
|
67
|
+
nested: {
|
|
68
|
+
listitem: 'editor-nested-listitem',
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
quote: 'editor-quote',
|
|
72
|
+
code: 'editor-code',
|
|
73
|
+
codeHighlight: {
|
|
74
|
+
atrule: 'editor-tokenAttr',
|
|
75
|
+
attr: 'editor-tokenAttr',
|
|
76
|
+
boolean: 'editor-tokenProperty',
|
|
77
|
+
builtin: 'editor-tokenSelector',
|
|
78
|
+
cdata: 'editor-tokenComment',
|
|
79
|
+
char: 'editor-tokenSelector',
|
|
80
|
+
class: 'editor-tokenFunction',
|
|
81
|
+
'class-name': 'editor-tokenFunction',
|
|
82
|
+
comment: 'editor-tokenComment',
|
|
83
|
+
constant: 'editor-tokenProperty',
|
|
84
|
+
deleted: 'editor-tokenProperty',
|
|
85
|
+
doctype: 'editor-tokenComment',
|
|
86
|
+
entity: 'editor-tokenOperator',
|
|
87
|
+
function: 'editor-tokenFunction',
|
|
88
|
+
important: 'editor-tokenVariable',
|
|
89
|
+
inserted: 'editor-tokenSelector',
|
|
90
|
+
keyword: 'editor-tokenAttr',
|
|
91
|
+
namespace: 'editor-tokenVariable',
|
|
92
|
+
number: 'editor-tokenProperty',
|
|
93
|
+
operator: 'editor-tokenOperator',
|
|
94
|
+
prolog: 'editor-tokenComment',
|
|
95
|
+
property: 'editor-tokenProperty',
|
|
96
|
+
punctuation: 'editor-tokenPunctuation',
|
|
97
|
+
regex: 'editor-tokenVariable',
|
|
98
|
+
selector: 'editor-tokenSelector',
|
|
99
|
+
string: 'editor-tokenSelector',
|
|
100
|
+
symbol: 'editor-tokenProperty',
|
|
101
|
+
tag: 'editor-tokenProperty',
|
|
102
|
+
url: 'editor-tokenOperator',
|
|
103
|
+
variable: 'editor-tokenVariable',
|
|
104
|
+
},
|
|
105
|
+
link: 'editor-link',
|
|
106
|
+
table: 'editor-table',
|
|
107
|
+
tableRow: 'editor-table-row',
|
|
108
|
+
tableCell: 'editor-table-cell',
|
|
109
|
+
tableCellHeader: 'editor-table-cell-header',
|
|
110
|
+
text: {
|
|
111
|
+
bold: 'editor-text-bold',
|
|
112
|
+
italic: 'editor-text-italic',
|
|
113
|
+
strikethrough: 'editor-text-strikethrough',
|
|
114
|
+
code: 'editor-text-code',
|
|
115
|
+
underline: 'editor-text-underline',
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
function editorOnError(error) {
|
|
119
|
+
console.error('Lexical error:', error);
|
|
120
|
+
}
|
|
121
|
+
// Mirror of $exitCodeNodeOnEnter in @lexical/code-core@0.44 (CodeExtension.register).
|
|
122
|
+
// CodePrismExtension requires LexicalExtensionComposer which is incompatible with
|
|
123
|
+
// LexicalComposer, so the Enter×3 escape logic is wired here instead.
|
|
124
|
+
// Re-verify against upstream when bumping @lexical/code past 0.44 (#8360).
|
|
125
|
+
function $exitCodeBlockOnEnter(selection) {
|
|
126
|
+
const { anchor } = selection;
|
|
127
|
+
if (!selection.isCollapsed() || anchor.type !== 'element')
|
|
128
|
+
return false;
|
|
129
|
+
const codeNode = anchor.getNode();
|
|
130
|
+
if (!$isCodeNode(codeNode))
|
|
131
|
+
return false;
|
|
132
|
+
const childrenSize = codeNode.getChildrenSize();
|
|
133
|
+
if (childrenSize < 2 || anchor.offset !== childrenSize)
|
|
134
|
+
return false;
|
|
135
|
+
const lastChild = codeNode.getLastChild();
|
|
136
|
+
if (!$isLineBreakNode(lastChild) || !$isLineBreakNode(lastChild.getPreviousSibling()))
|
|
137
|
+
return false;
|
|
138
|
+
const newElement = $createParagraphNode();
|
|
139
|
+
codeNode.splice(childrenSize - 2, 2, []).insertAfter(newElement, false);
|
|
140
|
+
newElement.select();
|
|
141
|
+
return true;
|
|
142
|
+
}
|
|
143
|
+
// Plugin to enable syntax highlighting in code blocks and wire the Enter×3 escape
|
|
144
|
+
// handler that moved to CodeExtension in Lexical 0.44 (#8360).
|
|
145
|
+
function CodeHighlightPlugin() {
|
|
146
|
+
const [editor] = useLexicalComposerContext();
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
return mergeRegister(registerCodeHighlighting(editor), editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
|
|
149
|
+
const selection = $getSelection();
|
|
150
|
+
if (!$isRangeSelection(selection) || !$exitCodeBlockOnEnter(selection))
|
|
151
|
+
return false;
|
|
152
|
+
event?.preventDefault();
|
|
153
|
+
return true;
|
|
154
|
+
}, COMMAND_PRIORITY_LOW));
|
|
155
|
+
}, [editor]);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
// Plugin to initialize editor with markdown content
|
|
159
|
+
function InitializePlugin({ content, onOffsets, }) {
|
|
160
|
+
const [editor] = useLexicalComposerContext();
|
|
161
|
+
const hasInitialized = useRef(false);
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
if (hasInitialized.current)
|
|
164
|
+
return;
|
|
165
|
+
hasInitialized.current = true;
|
|
166
|
+
if (content) {
|
|
167
|
+
const { root } = parseMarkdown(content);
|
|
168
|
+
onOffsets(importMarkdownToLexicalWithOffsets(editor, root), content);
|
|
169
|
+
}
|
|
170
|
+
}, [editor, content, onOffsets]);
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
// Plugin to auto-focus the editor when it mounts
|
|
174
|
+
function AutoFocusPlugin() {
|
|
175
|
+
const [editor] = useLexicalComposerContext();
|
|
176
|
+
const hasFocused = useRef(false);
|
|
177
|
+
useEffect(() => {
|
|
178
|
+
if (hasFocused.current)
|
|
179
|
+
return;
|
|
180
|
+
hasFocused.current = true;
|
|
181
|
+
// Small delay to ensure the editor is fully ready
|
|
182
|
+
const timeoutId = setTimeout(() => {
|
|
183
|
+
const rootElement = editor.getRootElement();
|
|
184
|
+
if (rootElement) {
|
|
185
|
+
// Focus without scrolling
|
|
186
|
+
rootElement.focus({ preventScroll: true });
|
|
187
|
+
}
|
|
188
|
+
}, 100);
|
|
189
|
+
return () => clearTimeout(timeoutId);
|
|
190
|
+
}, [editor]);
|
|
191
|
+
return null;
|
|
192
|
+
}
|
|
193
|
+
// Plugin to handle dynamic editable state changes
|
|
194
|
+
function EditablePlugin({ editable }) {
|
|
195
|
+
const [editor] = useLexicalComposerContext();
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
editor.setEditable(editable);
|
|
198
|
+
}, [editor, editable]);
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
// Plugin to track cursor position and report to parent
|
|
202
|
+
function CursorTrackingPlugin({ onCursorChange, }) {
|
|
203
|
+
const [editor] = useLexicalComposerContext();
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (!onCursorChange)
|
|
206
|
+
return;
|
|
207
|
+
return editor.registerUpdateListener(({ editorState }) => {
|
|
208
|
+
editorState.read(() => {
|
|
209
|
+
const selection = $getSelection();
|
|
210
|
+
if (!$isRangeSelection(selection))
|
|
211
|
+
return;
|
|
212
|
+
const offset = getGlobalOffsetForPoint(selection.anchor);
|
|
213
|
+
if (offset === null)
|
|
214
|
+
return;
|
|
215
|
+
const textContent = $getRoot().getTextContent();
|
|
216
|
+
const CONTEXT_CHARS = 50;
|
|
217
|
+
const contextBefore = textContent.slice(Math.max(0, offset - CONTEXT_CHARS), offset);
|
|
218
|
+
const contextAfter = textContent.slice(offset, Math.min(textContent.length, offset + CONTEXT_CHARS));
|
|
219
|
+
onCursorChange({
|
|
220
|
+
offset,
|
|
221
|
+
contextBefore,
|
|
222
|
+
contextAfter,
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
}, [editor, onCursorChange]);
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
// Plugin to restore cursor after content reload
|
|
230
|
+
function CursorRestorePlugin({ contentVersion, cursorToRestoreRef, }) {
|
|
231
|
+
const [editor] = useLexicalComposerContext();
|
|
232
|
+
const lastRestoredVersion = useRef(contentVersion);
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
// Only restore when contentVersion actually changes (content was reloaded)
|
|
235
|
+
if (contentVersion === lastRestoredVersion.current)
|
|
236
|
+
return;
|
|
237
|
+
lastRestoredVersion.current = contentVersion;
|
|
238
|
+
// Read ref value inside effect, not during render
|
|
239
|
+
const cursorToRestore = cursorToRestoreRef.current;
|
|
240
|
+
if (!cursorToRestore)
|
|
241
|
+
return;
|
|
242
|
+
console.log('[CursorRestorePlugin] Restoring cursor', {
|
|
243
|
+
contentVersion,
|
|
244
|
+
offset: cursorToRestore.offset,
|
|
245
|
+
});
|
|
246
|
+
// Delay slightly to ensure content has been imported
|
|
247
|
+
requestAnimationFrame(() => {
|
|
248
|
+
editor.update(() => {
|
|
249
|
+
const root = $getRoot();
|
|
250
|
+
const textContent = root.getTextContent();
|
|
251
|
+
// Try to find cursor position using context
|
|
252
|
+
let newOffset = findOffsetByContext(textContent, cursorToRestore.offset, cursorToRestore.contextBefore, cursorToRestore.contextAfter);
|
|
253
|
+
// Clamp to valid range
|
|
254
|
+
newOffset = Math.max(0, Math.min(textContent.length, newOffset));
|
|
255
|
+
const point = findTextPointAtOffset(root, newOffset);
|
|
256
|
+
if (point) {
|
|
257
|
+
const selection = $createRangeSelection();
|
|
258
|
+
selection.anchor.set(point.key, point.offset, 'text');
|
|
259
|
+
selection.focus.set(point.key, point.offset, 'text');
|
|
260
|
+
$setSelection(selection);
|
|
261
|
+
console.log('[CursorRestorePlugin] Selection restored at offset', newOffset);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
// Re-focus the editor
|
|
265
|
+
const rootElement = editor.getRootElement();
|
|
266
|
+
if (rootElement) {
|
|
267
|
+
rootElement.focus({ preventScroll: true });
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
}, [editor, contentVersion, cursorToRestoreRef]);
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
// Find best matching offset using context strings
|
|
274
|
+
function findOffsetByContext(text, fallbackOffset, contextBefore, contextAfter) {
|
|
275
|
+
// Try to find the exact junction of before+after context
|
|
276
|
+
if (contextBefore && contextAfter) {
|
|
277
|
+
const combined = contextBefore + contextAfter;
|
|
278
|
+
const idx = text.indexOf(combined);
|
|
279
|
+
if (idx !== -1) {
|
|
280
|
+
return idx + contextBefore.length;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
// Try to find just the before context (cursor at end of it)
|
|
284
|
+
if (contextBefore.length >= 10) {
|
|
285
|
+
const idx = text.indexOf(contextBefore);
|
|
286
|
+
if (idx !== -1) {
|
|
287
|
+
return idx + contextBefore.length;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Try to find just the after context (cursor at start of it)
|
|
291
|
+
if (contextAfter.length >= 10) {
|
|
292
|
+
const idx = text.indexOf(contextAfter);
|
|
293
|
+
if (idx !== -1) {
|
|
294
|
+
return idx;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
// Fallback to absolute offset (clamped)
|
|
298
|
+
return fallbackOffset;
|
|
299
|
+
}
|
|
300
|
+
// Plugin to handle content updates from extension host
|
|
301
|
+
// This should ONLY reload when content comes from external source (file open/reload)
|
|
302
|
+
// NOT when content flows back from editor's own onChange
|
|
303
|
+
function ExternalUpdatePlugin({ content, lastExternalLoadRef, currentContentRef, lastEditorChangeRef, onOffsets, }) {
|
|
304
|
+
const [editor] = useLexicalComposerContext();
|
|
305
|
+
useEffect(() => {
|
|
306
|
+
// Skip if content matches what we already have
|
|
307
|
+
if (content === currentContentRef.current) {
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
// Skip if this change likely came from the editor itself (via onChange callback)
|
|
311
|
+
// This prevents the feedback loop: type -> onChange -> prop change -> reload
|
|
312
|
+
const timeSinceEditorChange = Date.now() - lastEditorChangeRef.current;
|
|
313
|
+
if (timeSinceEditorChange < POST_LOAD_SUPPRESS_MS) {
|
|
314
|
+
// Still update the ref so we don't reload on next render either
|
|
315
|
+
currentContentRef.current = content;
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
console.log('[ExternalUpdatePlugin] Reloading content', {
|
|
319
|
+
contentLen: content.length,
|
|
320
|
+
currentLen: currentContentRef.current.length,
|
|
321
|
+
});
|
|
322
|
+
// Mark that we're loading external content
|
|
323
|
+
lastExternalLoadRef.current = Date.now();
|
|
324
|
+
currentContentRef.current = content;
|
|
325
|
+
// Defer the editor update to avoid React's flushSync warning
|
|
326
|
+
// This moves the Lexical update out of React's commit phase
|
|
327
|
+
queueMicrotask(() => {
|
|
328
|
+
// Double-check content still needs updating (user might have typed during microtask)
|
|
329
|
+
if (content !== currentContentRef.current) {
|
|
330
|
+
console.log('[ExternalUpdatePlugin] Skipping stale reload');
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const { root } = parseMarkdown(content);
|
|
334
|
+
let spans = [];
|
|
335
|
+
editor.update(() => {
|
|
336
|
+
spans = importMarkdownToLexicalInEditorStateWithOffsets(root);
|
|
337
|
+
}, { discrete: true });
|
|
338
|
+
onOffsets(spans, content);
|
|
339
|
+
});
|
|
340
|
+
}, [editor, content, lastExternalLoadRef, currentContentRef, lastEditorChangeRef, onOffsets]);
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
// Convert a Lexical selection point to a global character offset
|
|
344
|
+
function getGlobalOffsetForPoint(point) {
|
|
345
|
+
const root = $getRoot();
|
|
346
|
+
let currentOffset = 0;
|
|
347
|
+
let found = false;
|
|
348
|
+
const walk = (node) => {
|
|
349
|
+
if (found)
|
|
350
|
+
return;
|
|
351
|
+
if (node.getKey() === point.key) {
|
|
352
|
+
if ($isTextNode(node)) {
|
|
353
|
+
currentOffset += Math.min(point.offset, node.getTextContent().length);
|
|
354
|
+
found = true;
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if ($isElementNode(node)) {
|
|
358
|
+
const children = node.getChildren();
|
|
359
|
+
const limit = Math.min(point.offset, children.length);
|
|
360
|
+
for (let i = 0; i < limit; i += 1) {
|
|
361
|
+
currentOffset += children[i].getTextContent().length;
|
|
362
|
+
}
|
|
363
|
+
found = true;
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
if ($isTextNode(node)) {
|
|
368
|
+
currentOffset += node.getTextContent().length;
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
if ($isElementNode(node)) {
|
|
372
|
+
for (const child of node.getChildren()) {
|
|
373
|
+
walk(child);
|
|
374
|
+
if (found)
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
};
|
|
379
|
+
walk(root);
|
|
380
|
+
return found ? currentOffset : null;
|
|
381
|
+
}
|
|
382
|
+
// Find a text node and offset for a given global character offset
|
|
383
|
+
function findTextPointAtOffset(root, offset) {
|
|
384
|
+
const textNodes = root.getAllTextNodes();
|
|
385
|
+
if (textNodes.length === 0) {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
let remaining = Math.max(0, offset);
|
|
389
|
+
for (const node of textNodes) {
|
|
390
|
+
const length = node.getTextContent().length;
|
|
391
|
+
if (remaining <= length) {
|
|
392
|
+
return { key: node.getKey(), offset: remaining };
|
|
393
|
+
}
|
|
394
|
+
remaining -= length;
|
|
395
|
+
}
|
|
396
|
+
const lastNode = textNodes[textNodes.length - 1];
|
|
397
|
+
return { key: lastNode.getKey(), offset: lastNode.getTextContent().length };
|
|
398
|
+
}
|
|
399
|
+
// Debounce delay in ms - balances responsiveness with performance
|
|
400
|
+
const DEBOUNCE_DELAY = 100;
|
|
401
|
+
// Window after an external content load (file open/reload) or editor-initiated change
|
|
402
|
+
// during which we suppress onChange/reload to avoid feedback loops and normalization noise.
|
|
403
|
+
const POST_LOAD_SUPPRESS_MS = 500;
|
|
404
|
+
export function Editor({ initialContent, autoFocus = false, contentVersion = 0, cursorToRestoreRef, onChange, onCursorChange, onSelectionContextMenu, assetBaseUri, documentDirUri, imagePathResolution, wikiLinkPromotion, resolveLocalAsset, editable = true, filePath, onSubstitutionDetected, sweepRef, annotationKinds, annotations, activeAnnotationId = null, scrollToAnnotation, onCreateAnnotation, onActivateAnnotation, annotationEditorHandleRef, annotationLogger, }) {
|
|
405
|
+
// Annotations are on only when the host configured at least one kind
|
|
406
|
+
// (FR-004). Everything annotation-specific hangs off this flag, including
|
|
407
|
+
// the dynamic import below — with no kinds, none of those modules is ever
|
|
408
|
+
// loaded and no annotation command is registered.
|
|
409
|
+
const annotationsEnabled = !!annotationKinds && Object.keys(annotationKinds).length > 0;
|
|
410
|
+
// Derived, kind-agnostic affordance lists (FR-001/FR-004/FR-009/FR-010):
|
|
411
|
+
// one entry per configured kind on each surface, label falling back to the
|
|
412
|
+
// kind name (FR-003). Plain data only, so `Toolbar`/`SelectionContextMenuPlugin`
|
|
413
|
+
// never need to know about annotation types.
|
|
414
|
+
const toolbarAnnotationAffordances = useMemo(() => {
|
|
415
|
+
if (!annotationKinds)
|
|
416
|
+
return [];
|
|
417
|
+
return Object.entries(annotationKinds)
|
|
418
|
+
.filter(([, config]) => config.createAffordance?.surface === 'toolbar')
|
|
419
|
+
.map(([kind, config]) => ({ kind, label: config.createAffordance?.label ?? kind }));
|
|
420
|
+
}, [annotationKinds]);
|
|
421
|
+
const contextMenuAnnotationAffordances = useMemo(() => {
|
|
422
|
+
if (!annotationKinds)
|
|
423
|
+
return [];
|
|
424
|
+
return Object.entries(annotationKinds)
|
|
425
|
+
.filter(([, config]) => config.createAffordance?.surface === 'contextMenu')
|
|
426
|
+
.map(([kind, config]) => ({ kind, label: config.createAffordance?.label ?? kind }));
|
|
427
|
+
}, [annotationKinds]);
|
|
428
|
+
const offsetSpansRef = useRef([]);
|
|
429
|
+
const markdownTextRef = useRef(initialContent);
|
|
430
|
+
const [offsetsVersion, setOffsetsVersion] = useState(0);
|
|
431
|
+
// Read inside `handleOffsets` rather than closed over, so the callback's
|
|
432
|
+
// identity stays stable — it is an effect dependency in `InitializePlugin`
|
|
433
|
+
// and `ExternalUpdatePlugin`, and a new identity there would re-run them.
|
|
434
|
+
const annotationsEnabledRef = useRef(annotationsEnabled);
|
|
435
|
+
annotationsEnabledRef.current = annotationsEnabled;
|
|
436
|
+
/**
|
|
437
|
+
* Records the offset table each (re)parse produces.
|
|
438
|
+
*
|
|
439
|
+
* The two ref writes are unconditional on purpose. `InitializePlugin` runs
|
|
440
|
+
* its import exactly once, so gating span collection on `annotationsEnabled`
|
|
441
|
+
* left a host that loads its kinds asynchronously with a permanently empty
|
|
442
|
+
* table and silently placed nothing (an earlier review finding). The cost is
|
|
443
|
+
* one small record per text node on a walk that already visits every node.
|
|
444
|
+
*
|
|
445
|
+
* The `offsetsVersion` bump, on the other hand, is a state update that
|
|
446
|
+
* re-renders `Editor` — so it is gated. With annotations off nothing
|
|
447
|
+
* consumes the version, and a host that never passes `annotationKinds`
|
|
448
|
+
* should not re-render on every parse and external reload to maintain a
|
|
449
|
+
* counter nobody reads (review finding, @handarbeit-pruefer). When kinds
|
|
450
|
+
* arrive later, `AnnotationSurface` mounts and its placement effect runs
|
|
451
|
+
* against the refs, which are already current.
|
|
452
|
+
*/
|
|
453
|
+
const handleOffsets = useCallback((spans, markdownText) => {
|
|
454
|
+
offsetSpansRef.current = spans;
|
|
455
|
+
markdownTextRef.current = markdownText;
|
|
456
|
+
if (annotationsEnabledRef.current)
|
|
457
|
+
setOffsetsVersion((v) => v + 1);
|
|
458
|
+
}, []);
|
|
459
|
+
const lastExternalLoadRef = useRef(0);
|
|
460
|
+
const currentContentRef = useRef(initialContent);
|
|
461
|
+
const debounceTimerRef = useRef(null);
|
|
462
|
+
const pendingEditorRef = useRef(null);
|
|
463
|
+
// Track when editor-initiated changes happen to prevent feedback loops
|
|
464
|
+
const lastEditorChangeRef = useRef(0);
|
|
465
|
+
// Fallback ref for cursor restore when not provided
|
|
466
|
+
const fallbackCursorRef = useRef(null);
|
|
467
|
+
const effectiveCursorRef = cursorToRestoreRef ?? fallbackCursorRef;
|
|
468
|
+
const assetContextValue = useMemo(() => createAssetContextValue({ assetBaseUri, documentDirUri, imagePathResolution, resolveLocalAsset }), [assetBaseUri, documentDirUri, imagePathResolution, resolveLocalAsset]);
|
|
469
|
+
// Keep a ref to onChange so the unmount flush always calls the latest handler
|
|
470
|
+
// without causing the effect to re-run (and spuriously flush) on re-renders.
|
|
471
|
+
const onChangeRef = useRef(onChange);
|
|
472
|
+
onChangeRef.current = onChange;
|
|
473
|
+
// Same reasoning as onChangeRef: flushPendingChange must stay a stable
|
|
474
|
+
// ([]) callback, so the latest prop value is read through a ref rather than
|
|
475
|
+
// closed over.
|
|
476
|
+
const wikiLinkPromotionRef = useRef(wikiLinkPromotion);
|
|
477
|
+
wikiLinkPromotionRef.current = wikiLinkPromotion;
|
|
478
|
+
// Export pending editor state and propagate if changed.
|
|
479
|
+
// Shared by the debounce timer callback and unmount flush.
|
|
480
|
+
const flushPendingChange = useCallback(() => {
|
|
481
|
+
const pendingEditor = pendingEditorRef.current;
|
|
482
|
+
if (!pendingEditor)
|
|
483
|
+
return;
|
|
484
|
+
const timeSinceLoad = Date.now() - lastExternalLoadRef.current;
|
|
485
|
+
if (timeSinceLoad < POST_LOAD_SUPPRESS_MS)
|
|
486
|
+
return;
|
|
487
|
+
const mdast = exportLexicalToMdast(pendingEditor, { wikiLinkPromotion: wikiLinkPromotionRef.current });
|
|
488
|
+
const markdown = stringifyMarkdown(mdast);
|
|
489
|
+
if (markdown !== currentContentRef.current) {
|
|
490
|
+
currentContentRef.current = markdown;
|
|
491
|
+
lastEditorChangeRef.current = Date.now();
|
|
492
|
+
onChangeRef.current(markdown);
|
|
493
|
+
}
|
|
494
|
+
}, []);
|
|
495
|
+
// Flush pending debounced change on unmount so we don't lose edits
|
|
496
|
+
// (e.g. when switching from WYSIWYG to raw text view)
|
|
497
|
+
useEffect(() => {
|
|
498
|
+
return () => {
|
|
499
|
+
if (debounceTimerRef.current !== null) {
|
|
500
|
+
clearTimeout(debounceTimerRef.current);
|
|
501
|
+
debounceTimerRef.current = null;
|
|
502
|
+
flushPendingChange();
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
}, [flushPendingChange]);
|
|
506
|
+
const handleChange = useCallback((editorState, editor) => {
|
|
507
|
+
void editorState;
|
|
508
|
+
// Skip changes that happen right after external content load
|
|
509
|
+
// These are normalization diffs, not user edits
|
|
510
|
+
const timeSinceExternalLoad = Date.now() - lastExternalLoadRef.current;
|
|
511
|
+
if (timeSinceExternalLoad < POST_LOAD_SUPPRESS_MS) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
// Store the latest editor for debounced processing
|
|
515
|
+
pendingEditorRef.current = editor;
|
|
516
|
+
// Clear existing timer
|
|
517
|
+
if (debounceTimerRef.current !== null) {
|
|
518
|
+
clearTimeout(debounceTimerRef.current);
|
|
519
|
+
}
|
|
520
|
+
// Debounce the expensive mdast conversion
|
|
521
|
+
debounceTimerRef.current = window.setTimeout(() => {
|
|
522
|
+
debounceTimerRef.current = null;
|
|
523
|
+
flushPendingChange();
|
|
524
|
+
}, DEBOUNCE_DELAY);
|
|
525
|
+
}, [flushPendingChange]);
|
|
526
|
+
const initialConfig = {
|
|
527
|
+
namespace: 'SlashMD',
|
|
528
|
+
theme: editorTheme,
|
|
529
|
+
nodes: editorNodes,
|
|
530
|
+
onError: editorOnError,
|
|
531
|
+
editable,
|
|
532
|
+
};
|
|
533
|
+
return (_jsx(AssetContext.Provider, { value: assetContextValue, children: _jsx(LexicalComposer, { initialConfig: initialConfig, children: _jsx("div", { className: "relative max-w-[800px] ml-5 mr-1 pl-6 pr-6", children: _jsxs("div", { className: "editor-inner relative", children: [_jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: "editor-input", "aria-label": "Markdown editor" }), placeholder: _jsx("div", { className: "editor-placeholder", children: "Type '/' for commands..." }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, {}), _jsx(ListPlugin, {}), _jsx(CheckListPlugin, {}), _jsx(OrderedTaskListPlugin, {}), _jsx(TabIndentationPlugin, {}), _jsx(LinkPlugin, {}), _jsx(TablePlugin, {}), _jsx(CodeHighlightPlugin, {}), _jsx(OnChangePlugin, { onChange: handleChange, ignoreSelectionChange: true }), _jsx(InitializePlugin, { content: initialContent, onOffsets: handleOffsets }), _jsx(EditablePlugin, { editable: editable }), autoFocus && _jsx(AutoFocusPlugin, {}), _jsx(CursorTrackingPlugin, { onCursorChange: onCursorChange }), _jsx(CursorRestorePlugin, { contentVersion: contentVersion, cursorToRestoreRef: effectiveCursorRef }), _jsx(ExternalUpdatePlugin, { content: initialContent, lastExternalLoadRef: lastExternalLoadRef, currentContentRef: currentContentRef, lastEditorChangeRef: lastEditorChangeRef, onOffsets: handleOffsets }), _jsx(SlashMenuPlugin, {}), _jsx(DragHandlePlugin, {}), _jsx(MarkdownShortcutsPlugin, {}), _jsx(TableActionsPlugin, {}), _jsx(CodeBlockPlugin, {}), _jsx(TogglePlugin, {}), _jsx(ImagePlugin, {}), _jsx(BlockClickPlugin, {}), _jsx(LinkClickPlugin, { editable: editable }), _jsx(Toolbar, { annotationAffordances: toolbarAnnotationAffordances }), _jsx(SearchPlugin, {}), _jsx(FrontmatterPlugin, { filePath: filePath }), _jsx(WikiLinkExistencePlugin, {}), _jsx(WikiLinkFormatPlugin, {}), _jsx(AnchorScrollPlugin, {}), _jsx(SelectionContextMenuPlugin, { onSelectionContextMenu: onSelectionContextMenu,
|
|
534
|
+
// Only kinds that actually ask for a context-menu affordance:
|
|
535
|
+
// with none configured, or one on another surface, AnnotationPlugin
|
|
536
|
+
// would decline the command anyway, and dispatching from here would
|
|
537
|
+
// claim an entry point the kind never opted into.
|
|
538
|
+
annotationAffordances: contextMenuAnnotationAffordances }), _jsx(CorrectionPanelPlugin, {}), onSubstitutionDetected && (_jsx(AmbientCorrectionPlugin, { onSubstitutionDetected: onSubstitutionDetected, sweepRef: sweepRef })), annotationsEnabled && (_jsx(Suspense, { fallback: null, children: _jsx(LazyAnnotationSurface, { kinds: annotationKinds, annotations: annotations ?? EMPTY_ANNOTATIONS, activeAnnotationId: activeAnnotationId, scrollToAnnotation: scrollToAnnotation, onCreateAnnotation: onCreateAnnotation, onActivateAnnotation: onActivateAnnotation, editorHandleRef: annotationEditorHandleRef, offsetSpansRef: offsetSpansRef, markdownTextRef: markdownTextRef, offsetsVersion: offsetsVersion, logger: annotationLogger, wikiLinkPromotion: wikiLinkPromotion }) }))] }) }) }) }));
|
|
539
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FrontmatterPlugin - Shows frontmatter in a collapsible tray overlay
|
|
3
|
+
*
|
|
4
|
+
* The actual FrontmatterNode is hidden via CSS but remains in the Lexical tree.
|
|
5
|
+
* This plugin provides a UI to view/edit frontmatter in a sliding tray.
|
|
6
|
+
*
|
|
7
|
+
* The toggle button is rendered externally (in the editor header).
|
|
8
|
+
* This plugin communicates via Zustand store for state management.
|
|
9
|
+
*
|
|
10
|
+
* For .mdc files (Cursor rules), a specialized form UI is shown with:
|
|
11
|
+
* - alwaysApply dropdown (true/false)
|
|
12
|
+
* - description text input
|
|
13
|
+
* - globs array editor
|
|
14
|
+
*/
|
|
15
|
+
interface FrontmatterPluginProps {
|
|
16
|
+
filePath?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function FrontmatterPlugin({ filePath }: FrontmatterPluginProps): import("react").JSX.Element | null;
|
|
19
|
+
export {};
|