@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,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FootnoteNode — Inline decorator node for footnote references ([^id]).
|
|
3
|
+
* Renders as a superscript label and preserves the footnote identifier
|
|
4
|
+
* for correct round-trip serialization back to markdown.
|
|
5
|
+
*/
|
|
6
|
+
import { DecoratorNode, TEXT_TYPE_TO_FORMAT, toggleTextFormatType, $applyNodeReplacement, } from 'lexical';
|
|
7
|
+
import { createElement } from 'react';
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// DOM conversion (copy/paste support)
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
function $convertFootnoteElement(domNode) {
|
|
12
|
+
const id = domNode.getAttribute('data-footnote-id');
|
|
13
|
+
if (id) {
|
|
14
|
+
return { node: $createFootnoteNode(id) };
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
// FootnoteNode
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
export class FootnoteNode extends DecoratorNode {
|
|
22
|
+
__footnoteId;
|
|
23
|
+
__format;
|
|
24
|
+
__strongMarker;
|
|
25
|
+
__emphasisMarker;
|
|
26
|
+
static getType() {
|
|
27
|
+
return 'footnote';
|
|
28
|
+
}
|
|
29
|
+
static clone(node) {
|
|
30
|
+
const cloned = new FootnoteNode(node.__footnoteId, node.__key);
|
|
31
|
+
cloned.__format = node.__format;
|
|
32
|
+
cloned.__strongMarker = node.__strongMarker;
|
|
33
|
+
cloned.__emphasisMarker = node.__emphasisMarker;
|
|
34
|
+
return cloned;
|
|
35
|
+
}
|
|
36
|
+
constructor(footnoteId, key) {
|
|
37
|
+
super(key);
|
|
38
|
+
this.__footnoteId = footnoteId;
|
|
39
|
+
this.__format = 0;
|
|
40
|
+
this.__strongMarker = null;
|
|
41
|
+
this.__emphasisMarker = null;
|
|
42
|
+
}
|
|
43
|
+
getFootnoteId() {
|
|
44
|
+
return this.__footnoteId;
|
|
45
|
+
}
|
|
46
|
+
// Inline node — sits within text flow
|
|
47
|
+
isInline() {
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
// Serialization
|
|
51
|
+
static importJSON(serializedNode) {
|
|
52
|
+
const node = $createFootnoteNode(serializedNode.footnoteId).setFormat(serializedNode.format ?? 0);
|
|
53
|
+
if (serializedNode.strongMarker) {
|
|
54
|
+
node.setStrongMarker(serializedNode.strongMarker);
|
|
55
|
+
}
|
|
56
|
+
if (serializedNode.emphasisMarker) {
|
|
57
|
+
node.setEmphasisMarker(serializedNode.emphasisMarker);
|
|
58
|
+
}
|
|
59
|
+
return node;
|
|
60
|
+
}
|
|
61
|
+
exportJSON() {
|
|
62
|
+
return {
|
|
63
|
+
type: 'footnote',
|
|
64
|
+
version: 1,
|
|
65
|
+
footnoteId: this.__footnoteId,
|
|
66
|
+
format: this.__format,
|
|
67
|
+
strongMarker: this.__strongMarker,
|
|
68
|
+
emphasisMarker: this.__emphasisMarker,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Mirrors TextNode's format bitmask API so this node can carry
|
|
72
|
+
// bold/italic/strikethrough state through the mdast<->Lexical round-trip.
|
|
73
|
+
getFormat() {
|
|
74
|
+
return this.getLatest().__format;
|
|
75
|
+
}
|
|
76
|
+
hasFormat(type) {
|
|
77
|
+
const formatFlag = TEXT_TYPE_TO_FORMAT[type];
|
|
78
|
+
return (this.getFormat() & formatFlag) !== 0;
|
|
79
|
+
}
|
|
80
|
+
setFormat(format) {
|
|
81
|
+
const self = this.getWritable();
|
|
82
|
+
self.__format = format;
|
|
83
|
+
return self;
|
|
84
|
+
}
|
|
85
|
+
toggleFormat(type) {
|
|
86
|
+
const format = this.getFormat();
|
|
87
|
+
const newFormat = toggleTextFormatType(format, type, null);
|
|
88
|
+
return this.setFormat(newFormat);
|
|
89
|
+
}
|
|
90
|
+
// Mirrors TextNode's --md-strong-marker/--md-emphasis-marker style hooks
|
|
91
|
+
// (via setMarkdownMarker/getMarkdownMarker in the mappers) so a bare
|
|
92
|
+
// `_[^1]_`/`__[^1]__` span with no text sibling can still recover its
|
|
93
|
+
// original underscore-vs-asterisk marker on export.
|
|
94
|
+
getStrongMarker() {
|
|
95
|
+
return this.getLatest().__strongMarker;
|
|
96
|
+
}
|
|
97
|
+
setStrongMarker(marker) {
|
|
98
|
+
const self = this.getWritable();
|
|
99
|
+
self.__strongMarker = marker;
|
|
100
|
+
return self;
|
|
101
|
+
}
|
|
102
|
+
getEmphasisMarker() {
|
|
103
|
+
return this.getLatest().__emphasisMarker;
|
|
104
|
+
}
|
|
105
|
+
setEmphasisMarker(marker) {
|
|
106
|
+
const self = this.getWritable();
|
|
107
|
+
self.__emphasisMarker = marker;
|
|
108
|
+
return self;
|
|
109
|
+
}
|
|
110
|
+
// DOM creation (editor view)
|
|
111
|
+
createDOM() {
|
|
112
|
+
const el = document.createElement('sup');
|
|
113
|
+
el.className = 'footnote-ref';
|
|
114
|
+
el.style.cursor = 'default';
|
|
115
|
+
el.style.color = '#3c87f7';
|
|
116
|
+
el.style.fontSize = '0.75em';
|
|
117
|
+
return el;
|
|
118
|
+
}
|
|
119
|
+
updateDOM() {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
// DOM export (copy/paste)
|
|
123
|
+
exportDOM() {
|
|
124
|
+
const el = document.createElement('sup');
|
|
125
|
+
el.setAttribute('data-footnote-id', this.__footnoteId);
|
|
126
|
+
el.textContent = `[^${this.__footnoteId}]`;
|
|
127
|
+
return { element: el };
|
|
128
|
+
}
|
|
129
|
+
static importDOM() {
|
|
130
|
+
return {
|
|
131
|
+
sup: (domNode) => {
|
|
132
|
+
if (!domNode.hasAttribute('data-footnote-id'))
|
|
133
|
+
return null;
|
|
134
|
+
return { conversion: $convertFootnoteElement, priority: 1 };
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// Render as React element
|
|
139
|
+
decorate() {
|
|
140
|
+
return createElement('span', null, `[${this.__footnoteId}]`);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Factory + type guard
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
export function $createFootnoteNode(footnoteId) {
|
|
147
|
+
return $applyNodeReplacement(new FootnoteNode(footnoteId));
|
|
148
|
+
}
|
|
149
|
+
export function $isFootnoteNode(node) {
|
|
150
|
+
return node instanceof FootnoteNode;
|
|
151
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FrontmatterNode - A CodeNode variant for YAML frontmatter
|
|
3
|
+
* Extends CodeNode to get all the code editing behavior, but serializes as --- delimited frontmatter
|
|
4
|
+
*/
|
|
5
|
+
import { DOMConversionMap, DOMExportOutput, EditorConfig, LexicalNode, NodeKey, Spread } from 'lexical';
|
|
6
|
+
import { CodeNode, SerializedCodeNode } from '@lexical/code';
|
|
7
|
+
export type SerializedFrontmatterNode = Spread<{
|
|
8
|
+
type: 'frontmatter';
|
|
9
|
+
}, SerializedCodeNode>;
|
|
10
|
+
export declare class FrontmatterNode extends CodeNode {
|
|
11
|
+
static getType(): string;
|
|
12
|
+
static clone(node: FrontmatterNode): FrontmatterNode;
|
|
13
|
+
constructor(key?: NodeKey);
|
|
14
|
+
getLanguage(): string;
|
|
15
|
+
setLanguage(_language: string | null | undefined): this;
|
|
16
|
+
createDOM(config: EditorConfig): HTMLElement;
|
|
17
|
+
updateDOM(prevNode: FrontmatterNode, dom: HTMLElement, config: EditorConfig): boolean;
|
|
18
|
+
exportDOM(): DOMExportOutput;
|
|
19
|
+
static importDOM(): DOMConversionMap | null;
|
|
20
|
+
static importJSON(serializedNode: SerializedFrontmatterNode): FrontmatterNode;
|
|
21
|
+
exportJSON(): SerializedFrontmatterNode;
|
|
22
|
+
}
|
|
23
|
+
export declare function $createFrontmatterNode(): FrontmatterNode;
|
|
24
|
+
export declare function $isFrontmatterNode(node: LexicalNode | null | undefined): node is FrontmatterNode;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FrontmatterNode - A CodeNode variant for YAML frontmatter
|
|
3
|
+
* Extends CodeNode to get all the code editing behavior, but serializes as --- delimited frontmatter
|
|
4
|
+
*/
|
|
5
|
+
import { $applyNodeReplacement, $createTextNode, } from 'lexical';
|
|
6
|
+
import { CodeNode } from '@lexical/code';
|
|
7
|
+
export class FrontmatterNode extends CodeNode {
|
|
8
|
+
static getType() {
|
|
9
|
+
return 'frontmatter';
|
|
10
|
+
}
|
|
11
|
+
static clone(node) {
|
|
12
|
+
return new FrontmatterNode(node.__key);
|
|
13
|
+
}
|
|
14
|
+
constructor(key) {
|
|
15
|
+
super('yaml', key);
|
|
16
|
+
}
|
|
17
|
+
// Override to always return yaml
|
|
18
|
+
getLanguage() {
|
|
19
|
+
return 'yaml';
|
|
20
|
+
}
|
|
21
|
+
// Prevent language from being changed
|
|
22
|
+
setLanguage(_language) {
|
|
23
|
+
// No-op - frontmatter is always yaml
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
createDOM(config) {
|
|
27
|
+
void config;
|
|
28
|
+
const dom = super.createDOM(config);
|
|
29
|
+
dom.classList.add('frontmatter-code');
|
|
30
|
+
return dom;
|
|
31
|
+
}
|
|
32
|
+
updateDOM(prevNode, dom, config) {
|
|
33
|
+
void config;
|
|
34
|
+
const updated = super.updateDOM(prevNode, dom, config);
|
|
35
|
+
dom.classList.add('frontmatter-code');
|
|
36
|
+
return updated;
|
|
37
|
+
}
|
|
38
|
+
exportDOM() {
|
|
39
|
+
const element = document.createElement('pre');
|
|
40
|
+
element.setAttribute('data-frontmatter', 'true');
|
|
41
|
+
const code = document.createElement('code');
|
|
42
|
+
code.className = 'language-yaml';
|
|
43
|
+
code.textContent = this.getTextContent();
|
|
44
|
+
element.appendChild(code);
|
|
45
|
+
return { element };
|
|
46
|
+
}
|
|
47
|
+
static importDOM() {
|
|
48
|
+
return {
|
|
49
|
+
pre: (node) => {
|
|
50
|
+
const element = node;
|
|
51
|
+
if (element.getAttribute('data-frontmatter') === 'true') {
|
|
52
|
+
return {
|
|
53
|
+
conversion: convertPreElement,
|
|
54
|
+
priority: 2, // Higher priority than CodeNode
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
static importJSON(serializedNode) {
|
|
62
|
+
void serializedNode;
|
|
63
|
+
const node = $createFrontmatterNode();
|
|
64
|
+
return node;
|
|
65
|
+
}
|
|
66
|
+
exportJSON() {
|
|
67
|
+
return {
|
|
68
|
+
...super.exportJSON(),
|
|
69
|
+
type: 'frontmatter',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function convertPreElement(domNode) {
|
|
74
|
+
const element = domNode;
|
|
75
|
+
const code = element.querySelector('code');
|
|
76
|
+
const value = code?.textContent || '';
|
|
77
|
+
const node = $createFrontmatterNode();
|
|
78
|
+
if (value) {
|
|
79
|
+
node.append($createTextNode(value));
|
|
80
|
+
}
|
|
81
|
+
return { node };
|
|
82
|
+
}
|
|
83
|
+
export function $createFrontmatterNode() {
|
|
84
|
+
return $applyNodeReplacement(new FrontmatterNode());
|
|
85
|
+
}
|
|
86
|
+
export function $isFrontmatterNode(node) {
|
|
87
|
+
return node instanceof FrontmatterNode;
|
|
88
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { DecoratorNode, DOMConversionMap, DOMExportOutput, LexicalNode, NodeKey, SerializedLexicalNode } from 'lexical';
|
|
2
|
+
export type SerializedHorizontalRuleNode = SerializedLexicalNode;
|
|
3
|
+
export declare class HorizontalRuleNode extends DecoratorNode<JSX.Element> {
|
|
4
|
+
static getType(): string;
|
|
5
|
+
static clone(node: HorizontalRuleNode): HorizontalRuleNode;
|
|
6
|
+
constructor(key?: NodeKey);
|
|
7
|
+
createDOM(): HTMLElement;
|
|
8
|
+
updateDOM(): boolean;
|
|
9
|
+
static importDOM(): DOMConversionMap | null;
|
|
10
|
+
exportDOM(): DOMExportOutput;
|
|
11
|
+
static importJSON(_serializedNode: SerializedHorizontalRuleNode): HorizontalRuleNode;
|
|
12
|
+
exportJSON(): SerializedHorizontalRuleNode;
|
|
13
|
+
decorate(): JSX.Element;
|
|
14
|
+
isInline(): boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function $createHorizontalRuleNode(): HorizontalRuleNode;
|
|
17
|
+
export declare function $isHorizontalRuleNode(node: LexicalNode | null | undefined): node is HorizontalRuleNode;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { DecoratorNode, } from 'lexical';
|
|
2
|
+
export class HorizontalRuleNode extends DecoratorNode {
|
|
3
|
+
static getType() {
|
|
4
|
+
return 'horizontalrule';
|
|
5
|
+
}
|
|
6
|
+
static clone(node) {
|
|
7
|
+
return new HorizontalRuleNode(node.__key);
|
|
8
|
+
}
|
|
9
|
+
constructor(key) {
|
|
10
|
+
super(key);
|
|
11
|
+
}
|
|
12
|
+
createDOM() {
|
|
13
|
+
const element = document.createElement('hr');
|
|
14
|
+
element.className = 'divider-block';
|
|
15
|
+
return element;
|
|
16
|
+
}
|
|
17
|
+
updateDOM() {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
static importDOM() {
|
|
21
|
+
return {
|
|
22
|
+
hr: () => ({
|
|
23
|
+
conversion: () => {
|
|
24
|
+
return { node: new HorizontalRuleNode() };
|
|
25
|
+
},
|
|
26
|
+
priority: 0,
|
|
27
|
+
}),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
exportDOM() {
|
|
31
|
+
const element = document.createElement('hr');
|
|
32
|
+
element.className = 'divider-block';
|
|
33
|
+
return { element };
|
|
34
|
+
}
|
|
35
|
+
static importJSON(_serializedNode) {
|
|
36
|
+
return new HorizontalRuleNode();
|
|
37
|
+
}
|
|
38
|
+
exportJSON() {
|
|
39
|
+
return {
|
|
40
|
+
type: 'horizontalrule',
|
|
41
|
+
version: 1,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
decorate() {
|
|
45
|
+
// This will be rendered by React
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
isInline() {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function $createHorizontalRuleNode() {
|
|
53
|
+
return new HorizontalRuleNode();
|
|
54
|
+
}
|
|
55
|
+
export function $isHorizontalRuleNode(node) {
|
|
56
|
+
return node instanceof HorizontalRuleNode;
|
|
57
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HtmlNode — Decorator node that carries raw HTML markup opaquely through
|
|
3
|
+
* the mdast <-> Lexical pipeline for round-trip fidelity. The content is
|
|
4
|
+
* never rendered as live DOM (never assigned via innerHTML) — it is always
|
|
5
|
+
* displayed as inert text, matching the "opaque preservation, not live
|
|
6
|
+
* rendering" scope decision for issue #909.
|
|
7
|
+
*/
|
|
8
|
+
import { DecoratorNode, DOMConversionMap, DOMExportOutput, LexicalNode, NodeKey, SerializedLexicalNode, Spread } from 'lexical';
|
|
9
|
+
export type SerializedHtmlNode = Spread<{
|
|
10
|
+
html: string;
|
|
11
|
+
inline: boolean;
|
|
12
|
+
}, SerializedLexicalNode>;
|
|
13
|
+
export declare class HtmlNode extends DecoratorNode<JSX.Element> {
|
|
14
|
+
__html: string;
|
|
15
|
+
__inline: boolean;
|
|
16
|
+
static getType(): string;
|
|
17
|
+
static clone(node: HtmlNode): HtmlNode;
|
|
18
|
+
constructor(html: string, inline?: boolean, key?: NodeKey);
|
|
19
|
+
static importJSON(serializedNode: SerializedHtmlNode): HtmlNode;
|
|
20
|
+
exportJSON(): SerializedHtmlNode;
|
|
21
|
+
createDOM(): HTMLElement;
|
|
22
|
+
exportDOM(): DOMExportOutput;
|
|
23
|
+
static importDOM(): DOMConversionMap | null;
|
|
24
|
+
updateDOM(prevNode: HtmlNode): boolean;
|
|
25
|
+
getTextContent(): string;
|
|
26
|
+
getHtml(): string;
|
|
27
|
+
setHtml(html: string): void;
|
|
28
|
+
isInline(): boolean;
|
|
29
|
+
decorate(): JSX.Element;
|
|
30
|
+
}
|
|
31
|
+
export declare function $createHtmlNode(html: string, inline?: boolean): HtmlNode;
|
|
32
|
+
export declare function $isHtmlNode(node: LexicalNode | null | undefined): node is HtmlNode;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HtmlNode — Decorator node that carries raw HTML markup opaquely through
|
|
3
|
+
* the mdast <-> Lexical pipeline for round-trip fidelity. The content is
|
|
4
|
+
* never rendered as live DOM (never assigned via innerHTML) — it is always
|
|
5
|
+
* displayed as inert text, matching the "opaque preservation, not live
|
|
6
|
+
* rendering" scope decision for issue #909.
|
|
7
|
+
*/
|
|
8
|
+
import { DecoratorNode, $applyNodeReplacement, } from 'lexical';
|
|
9
|
+
import { createElement, Fragment } from 'react';
|
|
10
|
+
// btoa/atob only support Latin1 — encode/decode through UTF-8 percent-escaping
|
|
11
|
+
// first so HTML containing non-Latin1 characters (emoji, accents, CJK, etc.)
|
|
12
|
+
// doesn't throw during copy/paste DOM export/import.
|
|
13
|
+
function encodeUnicodeBase64(str) {
|
|
14
|
+
return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))));
|
|
15
|
+
}
|
|
16
|
+
function decodeUnicodeBase64(str) {
|
|
17
|
+
return decodeURIComponent(atob(str)
|
|
18
|
+
.split('')
|
|
19
|
+
.map((c) => '%' + c.charCodeAt(0).toString(16).padStart(2, '0'))
|
|
20
|
+
.join(''));
|
|
21
|
+
}
|
|
22
|
+
function $convertHtmlElement(domNode) {
|
|
23
|
+
const encoded = domNode.getAttribute('data-lexical-html');
|
|
24
|
+
const inline = domNode.getAttribute('data-lexical-inline') === 'true';
|
|
25
|
+
if (encoded === null) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
// Malformed/hand-edited clipboard data can contain an invalid base64
|
|
29
|
+
// payload — decode defensively so a bad paste doesn't crash the whole
|
|
30
|
+
// paste operation.
|
|
31
|
+
try {
|
|
32
|
+
const html = decodeUnicodeBase64(encoded);
|
|
33
|
+
return { node: $createHtmlNode(html, inline) };
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export class HtmlNode extends DecoratorNode {
|
|
40
|
+
__html;
|
|
41
|
+
__inline;
|
|
42
|
+
static getType() {
|
|
43
|
+
return 'html';
|
|
44
|
+
}
|
|
45
|
+
static clone(node) {
|
|
46
|
+
return new HtmlNode(node.__html, node.__inline, node.__key);
|
|
47
|
+
}
|
|
48
|
+
constructor(html, inline, key) {
|
|
49
|
+
super(key);
|
|
50
|
+
this.__html = html;
|
|
51
|
+
this.__inline = inline ?? false;
|
|
52
|
+
}
|
|
53
|
+
static importJSON(serializedNode) {
|
|
54
|
+
return $createHtmlNode(serializedNode.html, serializedNode.inline);
|
|
55
|
+
}
|
|
56
|
+
exportJSON() {
|
|
57
|
+
return {
|
|
58
|
+
type: 'html',
|
|
59
|
+
version: 1,
|
|
60
|
+
html: this.getHtml(),
|
|
61
|
+
inline: this.__inline,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
createDOM() {
|
|
65
|
+
const element = document.createElement(this.__inline ? 'span' : 'div');
|
|
66
|
+
element.className = 'editor-raw-html';
|
|
67
|
+
return element;
|
|
68
|
+
}
|
|
69
|
+
exportDOM() {
|
|
70
|
+
const element = document.createElement(this.__inline ? 'span' : 'div');
|
|
71
|
+
// Encode as base64 to avoid attribute-escaping issues with copy/paste
|
|
72
|
+
element.setAttribute('data-lexical-html', encodeUnicodeBase64(this.__html));
|
|
73
|
+
element.setAttribute('data-lexical-inline', `${this.__inline}`);
|
|
74
|
+
element.textContent = this.__html;
|
|
75
|
+
return { element };
|
|
76
|
+
}
|
|
77
|
+
static importDOM() {
|
|
78
|
+
return {
|
|
79
|
+
div: (domNode) => {
|
|
80
|
+
if (!domNode.hasAttribute('data-lexical-html')) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return { conversion: $convertHtmlElement, priority: 2 };
|
|
84
|
+
},
|
|
85
|
+
span: (domNode) => {
|
|
86
|
+
if (!domNode.hasAttribute('data-lexical-html')) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return { conversion: $convertHtmlElement, priority: 1 };
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
updateDOM(prevNode) {
|
|
94
|
+
return this.__inline !== prevNode.__inline;
|
|
95
|
+
}
|
|
96
|
+
getTextContent() {
|
|
97
|
+
return this.__html;
|
|
98
|
+
}
|
|
99
|
+
getHtml() {
|
|
100
|
+
return this.__html;
|
|
101
|
+
}
|
|
102
|
+
setHtml(html) {
|
|
103
|
+
const writable = this.getWritable();
|
|
104
|
+
writable.__html = html;
|
|
105
|
+
}
|
|
106
|
+
isInline() {
|
|
107
|
+
return this.__inline;
|
|
108
|
+
}
|
|
109
|
+
decorate() {
|
|
110
|
+
// createDOM() already provides the wrapping element (with class
|
|
111
|
+
// 'editor-raw-html') that this content is portaled into — render only
|
|
112
|
+
// the text here, not another nested copy of the same wrapper.
|
|
113
|
+
return createElement(Fragment, null, this.__html);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
export function $createHtmlNode(html, inline = false) {
|
|
117
|
+
return $applyNodeReplacement(new HtmlNode(html, inline));
|
|
118
|
+
}
|
|
119
|
+
export function $isHtmlNode(node) {
|
|
120
|
+
return node instanceof HtmlNode;
|
|
121
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { NodeKey } from 'lexical';
|
|
2
|
+
interface ImageComponentProps {
|
|
3
|
+
src: string;
|
|
4
|
+
alt: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
width?: number;
|
|
7
|
+
height?: number;
|
|
8
|
+
nodeKey: NodeKey;
|
|
9
|
+
}
|
|
10
|
+
export declare function ImageComponent({ src, alt, title, width, height, nodeKey, }: ImageComponentProps): JSX.Element;
|
|
11
|
+
export {};
|