@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,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared context menu overlay for diagram nodes (C4, Mermaid).
|
|
3
|
+
* Provides "Copy image to clipboard" and optional diagram-specific actions.
|
|
4
|
+
*/
|
|
5
|
+
export interface DiagramContextMenuState {
|
|
6
|
+
visible: boolean;
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
containerRef: React.RefObject<HTMLElement | null> | null;
|
|
10
|
+
}
|
|
11
|
+
export interface DiagramContextMenuProps {
|
|
12
|
+
visible: boolean;
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
onCopyImage: () => void;
|
|
16
|
+
onClose: () => void;
|
|
17
|
+
/** Optional: enter DSL text editor (double-click equivalent) */
|
|
18
|
+
onEditText?: () => void;
|
|
19
|
+
/** Optional: toggle drag layout mode */
|
|
20
|
+
onEditLayout?: () => void;
|
|
21
|
+
/** Optional: reset to auto layout */
|
|
22
|
+
onResetLayout?: () => void;
|
|
23
|
+
/** Whether drag layout mode is currently active */
|
|
24
|
+
isEditingLayout?: boolean;
|
|
25
|
+
/** Whether manual layout exists (show reset option) */
|
|
26
|
+
hasManualLayout?: boolean;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Hook to manage diagram context menu state.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useDiagramContextMenu(): {
|
|
32
|
+
show: (e: React.MouseEvent, containerRef: React.RefObject<HTMLElement | null>) => void;
|
|
33
|
+
props: {
|
|
34
|
+
visible: boolean;
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
onCopyImage: () => Promise<void>;
|
|
38
|
+
onClose: () => void;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Context menu overlay component. Uses CSS variables for theming
|
|
43
|
+
* with light/dark hex fallbacks for environments without VS Code vars.
|
|
44
|
+
*/
|
|
45
|
+
export declare function DiagramContextMenu({ visible, x, y, onCopyImage, onClose, onEditText, onEditLayout, onResetLayout, isEditingLayout, hasManualLayout, }: DiagramContextMenuProps): JSX.Element | null;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/* eslint-disable react-refresh/only-export-components */
|
|
3
|
+
/**
|
|
4
|
+
* Shared context menu overlay for diagram nodes (C4, Mermaid).
|
|
5
|
+
* Provides "Copy image to clipboard" and optional diagram-specific actions.
|
|
6
|
+
*/
|
|
7
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
8
|
+
import { copyDiagramToClipboard } from './diagram-context-menu.js';
|
|
9
|
+
/**
|
|
10
|
+
* Hook to manage diagram context menu state.
|
|
11
|
+
*/
|
|
12
|
+
export function useDiagramContextMenu() {
|
|
13
|
+
const [state, setState] = useState({
|
|
14
|
+
visible: false,
|
|
15
|
+
x: 0,
|
|
16
|
+
y: 0,
|
|
17
|
+
containerRef: null,
|
|
18
|
+
});
|
|
19
|
+
const show = useCallback((e, containerRef) => {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
e.stopPropagation();
|
|
22
|
+
setState({
|
|
23
|
+
visible: true,
|
|
24
|
+
x: e.clientX,
|
|
25
|
+
y: e.clientY,
|
|
26
|
+
containerRef,
|
|
27
|
+
});
|
|
28
|
+
}, []);
|
|
29
|
+
const close = useCallback(() => {
|
|
30
|
+
setState((prev) => ({ ...prev, visible: false }));
|
|
31
|
+
}, []);
|
|
32
|
+
const handleCopyImage = useCallback(async () => {
|
|
33
|
+
if (state.containerRef?.current) {
|
|
34
|
+
await copyDiagramToClipboard(state.containerRef.current);
|
|
35
|
+
}
|
|
36
|
+
close();
|
|
37
|
+
}, [state.containerRef, close]);
|
|
38
|
+
return {
|
|
39
|
+
show,
|
|
40
|
+
props: {
|
|
41
|
+
visible: state.visible,
|
|
42
|
+
x: state.x,
|
|
43
|
+
y: state.y,
|
|
44
|
+
onCopyImage: handleCopyImage,
|
|
45
|
+
onClose: close,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const menuStyle = {
|
|
50
|
+
position: 'fixed',
|
|
51
|
+
zIndex: 10000,
|
|
52
|
+
borderRadius: '6px',
|
|
53
|
+
padding: '4px 0',
|
|
54
|
+
minWidth: '180px',
|
|
55
|
+
};
|
|
56
|
+
const itemStyle = {
|
|
57
|
+
display: 'block',
|
|
58
|
+
width: '100%',
|
|
59
|
+
padding: '6px 12px',
|
|
60
|
+
background: 'transparent',
|
|
61
|
+
border: 'none',
|
|
62
|
+
fontSize: '13px',
|
|
63
|
+
textAlign: 'left',
|
|
64
|
+
cursor: 'pointer',
|
|
65
|
+
fontFamily: 'system-ui, -apple-system, sans-serif',
|
|
66
|
+
};
|
|
67
|
+
const separatorStyle = {
|
|
68
|
+
height: '1px',
|
|
69
|
+
margin: '4px 8px',
|
|
70
|
+
};
|
|
71
|
+
/**
|
|
72
|
+
* Context menu overlay component. Uses CSS variables for theming
|
|
73
|
+
* with light/dark hex fallbacks for environments without VS Code vars.
|
|
74
|
+
*/
|
|
75
|
+
export function DiagramContextMenu({ visible, x, y, onCopyImage, onClose, onEditText, onEditLayout, onResetLayout, isEditingLayout, hasManualLayout, }) {
|
|
76
|
+
const menuRef = useRef(null);
|
|
77
|
+
const isDark = typeof document !== 'undefined' && document.documentElement.classList.contains('dark');
|
|
78
|
+
const bgColor = `var(--vscode-menu-background, ${isDark ? '#252526' : '#ffffff'})`;
|
|
79
|
+
const borderColor = `var(--vscode-menu-border, ${isDark ? '#454545' : '#d4d4d4'})`;
|
|
80
|
+
const textColor = `var(--vscode-menu-foreground, ${isDark ? '#cccccc' : '#333333'})`;
|
|
81
|
+
const hoverBg = `var(--vscode-menu-selectionBackground, ${isDark ? '#094771' : '#e8e8e8'})`;
|
|
82
|
+
const separatorColor = `var(--vscode-menu-separatorBackground, ${isDark ? '#454545' : '#d4d4d4'})`;
|
|
83
|
+
// Close on click outside or Escape
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (!visible)
|
|
86
|
+
return;
|
|
87
|
+
const handleClick = (e) => {
|
|
88
|
+
if (menuRef.current && !menuRef.current.contains(e.target)) {
|
|
89
|
+
onClose();
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
const handleKey = (e) => {
|
|
93
|
+
if (e.key === 'Escape')
|
|
94
|
+
onClose();
|
|
95
|
+
};
|
|
96
|
+
document.addEventListener('mousedown', handleClick);
|
|
97
|
+
document.addEventListener('keydown', handleKey);
|
|
98
|
+
return () => {
|
|
99
|
+
document.removeEventListener('mousedown', handleClick);
|
|
100
|
+
document.removeEventListener('keydown', handleKey);
|
|
101
|
+
};
|
|
102
|
+
}, [visible, onClose]);
|
|
103
|
+
if (!visible)
|
|
104
|
+
return null;
|
|
105
|
+
const handleHover = (e, entering) => {
|
|
106
|
+
e.currentTarget.style.background = entering ? hoverBg : 'transparent';
|
|
107
|
+
};
|
|
108
|
+
return (_jsxs("div", { ref: menuRef, style: {
|
|
109
|
+
...menuStyle,
|
|
110
|
+
left: x,
|
|
111
|
+
top: y,
|
|
112
|
+
background: bgColor,
|
|
113
|
+
border: `1px solid ${borderColor}`,
|
|
114
|
+
boxShadow: isDark ? '0 4px 12px rgba(0,0,0,0.3)' : '0 4px 12px rgba(0,0,0,0.12)',
|
|
115
|
+
}, children: [onEditText && (_jsx("button", { onClick: () => { onEditText(); onClose(); }, style: { ...itemStyle, color: textColor }, onMouseEnter: (e) => handleHover(e, true), onMouseLeave: (e) => handleHover(e, false), children: "Edit text\u2026" })), onEditLayout && (_jsx("button", { onClick: () => { onEditLayout(); onClose(); }, style: { ...itemStyle, color: textColor }, onMouseEnter: (e) => handleHover(e, true), onMouseLeave: (e) => handleHover(e, false), children: isEditingLayout ? 'Exit layout mode' : 'Edit layout' })), onResetLayout && hasManualLayout && (_jsx("button", { onClick: () => { onResetLayout(); onClose(); }, style: { ...itemStyle, color: textColor }, onMouseEnter: (e) => handleHover(e, true), onMouseLeave: (e) => handleHover(e, false), children: "Reset to auto layout" })), (onEditText || onEditLayout) && (_jsx("div", { style: { ...separatorStyle, background: separatorColor } })), _jsx("button", { onClick: onCopyImage, style: { ...itemStyle, color: textColor }, onMouseEnter: (e) => handleHover(e, true), onMouseLeave: (e) => handleHover(e, false), children: "Copy image to clipboard" })] }));
|
|
116
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EquationComponent - React component for rendering and editing LaTeX equations
|
|
3
|
+
* Ported from Lexical playground with modifications for SlashMD
|
|
4
|
+
*
|
|
5
|
+
* TODO: Block math equations should be centered but centering doesn't work properly.
|
|
6
|
+
* The issue is likely related to Shadow DOM style inheritance - the :host(.block)
|
|
7
|
+
* selector and text-align: center are set but not taking effect. May need to
|
|
8
|
+
* investigate the container element structure or use a different centering approach.
|
|
9
|
+
*/
|
|
10
|
+
import { NodeKey } from 'lexical';
|
|
11
|
+
interface EquationComponentProps {
|
|
12
|
+
equation: string;
|
|
13
|
+
inline: boolean;
|
|
14
|
+
nodeKey: NodeKey;
|
|
15
|
+
}
|
|
16
|
+
export default function EquationComponent({ equation, inline, nodeKey, }: EquationComponentProps): JSX.Element;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* EquationComponent - React component for rendering and editing LaTeX equations
|
|
4
|
+
* Ported from Lexical playground with modifications for SlashMD
|
|
5
|
+
*
|
|
6
|
+
* TODO: Block math equations should be centered but centering doesn't work properly.
|
|
7
|
+
* The issue is likely related to Shadow DOM style inheritance - the :host(.block)
|
|
8
|
+
* selector and text-align: center are set but not taking effect. May need to
|
|
9
|
+
* investigate the container element structure or use a different centering approach.
|
|
10
|
+
*/
|
|
11
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
12
|
+
import { mergeRegister } from '@lexical/utils';
|
|
13
|
+
import { $getNodeByKey, $getSelection, $isNodeSelection, COMMAND_PRIORITY_HIGH, KEY_ESCAPE_COMMAND, SELECTION_CHANGE_COMMAND, } from 'lexical';
|
|
14
|
+
import { useCallback, useEffect, useRef, useState, forwardRef } from 'react';
|
|
15
|
+
import { createBrowserAdaptorDocument } from '../../../mathjax-config.js';
|
|
16
|
+
import { $isEquationNode } from './EquationNode.js';
|
|
17
|
+
// Initialize MathJax once for the renderer process
|
|
18
|
+
const { adaptor: mathjaxAdaptor, document: mathjaxDocument } = createBrowserAdaptorDocument(document);
|
|
19
|
+
// MathJax SVG Renderer component — replaces KaTeX with MathJax for higher quality rendering
|
|
20
|
+
function MathJaxRenderer({ equation, inline, onDoubleClick, }) {
|
|
21
|
+
const containerRef = useRef(null);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const container = containerRef.current;
|
|
24
|
+
if (container === null)
|
|
25
|
+
return;
|
|
26
|
+
try {
|
|
27
|
+
// Normalize escaped backslashes from markdown sources
|
|
28
|
+
const renderEquation = equation.replace(/\\\\([a-zA-Z])/g, '\\$1');
|
|
29
|
+
// Clear previous render
|
|
30
|
+
container.innerHTML = '';
|
|
31
|
+
// Render with MathJax
|
|
32
|
+
const node = mathjaxDocument.convert(renderEquation, { display: !inline });
|
|
33
|
+
// MathJax returns an element — append it directly
|
|
34
|
+
if (node instanceof Node) {
|
|
35
|
+
container.appendChild(node);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
// Fallback: use innerHTML from adaptor
|
|
39
|
+
container.innerHTML = mathjaxAdaptor.innerHTML(node);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
container.textContent = equation;
|
|
44
|
+
}
|
|
45
|
+
}, [equation, inline]);
|
|
46
|
+
return (_jsx("span", { role: "button", tabIndex: -1, onDoubleClick: onDoubleClick, ref: containerRef, className: `equation-renderer ${inline ? 'inline' : 'block'}`, style: { display: inline ? 'inline' : 'block', textAlign: inline ? 'inherit' : 'center' } }));
|
|
47
|
+
}
|
|
48
|
+
const EquationEditor = forwardRef(function EquationEditor({ equation, setEquation, inline }, forwardedRef) {
|
|
49
|
+
const onChange = (event) => {
|
|
50
|
+
setEquation(event.target.value);
|
|
51
|
+
};
|
|
52
|
+
if (inline) {
|
|
53
|
+
return (_jsxs("span", { className: "equation-editor-background", children: [_jsx("span", { className: "equation-dollar-sign", children: "$" }), _jsx("input", { className: "equation-inline-editor", value: equation, onChange: onChange, autoFocus: true, ref: forwardedRef }), _jsx("span", { className: "equation-dollar-sign", children: "$" })] }));
|
|
54
|
+
}
|
|
55
|
+
return (_jsxs("div", { className: "equation-editor-background", children: [_jsx("span", { className: "equation-dollar-sign", children: '$$' }), _jsx("textarea", { className: "equation-block-editor", value: equation, onChange: onChange, autoFocus: true, ref: forwardedRef }), _jsx("span", { className: "equation-dollar-sign", children: '$$' })] }));
|
|
56
|
+
});
|
|
57
|
+
export default function EquationComponent({ equation, inline, nodeKey, }) {
|
|
58
|
+
const [editor] = useLexicalComposerContext();
|
|
59
|
+
const [isEditable, setIsEditable] = useState(() => editor.isEditable());
|
|
60
|
+
const [equationValue, setEquationValue] = useState(equation);
|
|
61
|
+
const [showEquationEditor, setShowEquationEditor] = useState(false);
|
|
62
|
+
const inputRef = useRef(null);
|
|
63
|
+
// Listen for editable changes
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
return editor.registerEditableListener((editable) => {
|
|
66
|
+
setIsEditable(editable);
|
|
67
|
+
});
|
|
68
|
+
}, [editor]);
|
|
69
|
+
const onHide = useCallback((restoreSelection) => {
|
|
70
|
+
setShowEquationEditor(false);
|
|
71
|
+
editor.update(() => {
|
|
72
|
+
const node = $getNodeByKey(nodeKey);
|
|
73
|
+
if ($isEquationNode(node)) {
|
|
74
|
+
node.setEquation(equationValue);
|
|
75
|
+
if (restoreSelection) {
|
|
76
|
+
node.selectNext(0, 0);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}, [editor, equationValue, nodeKey]);
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!showEquationEditor && equationValue !== equation) {
|
|
83
|
+
setEquationValue(equation);
|
|
84
|
+
}
|
|
85
|
+
}, [showEquationEditor, equation, equationValue]);
|
|
86
|
+
useEffect(() => {
|
|
87
|
+
if (!isEditable) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (showEquationEditor) {
|
|
91
|
+
return mergeRegister(editor.registerCommand(SELECTION_CHANGE_COMMAND, () => {
|
|
92
|
+
const activeElement = document.activeElement;
|
|
93
|
+
const inputElem = inputRef.current;
|
|
94
|
+
if (inputElem !== activeElement) {
|
|
95
|
+
onHide();
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}, COMMAND_PRIORITY_HIGH), editor.registerCommand(KEY_ESCAPE_COMMAND, () => {
|
|
99
|
+
const activeElement = document.activeElement;
|
|
100
|
+
const inputElem = inputRef.current;
|
|
101
|
+
if (inputElem === activeElement) {
|
|
102
|
+
onHide(true);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}, COMMAND_PRIORITY_HIGH));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
return editor.registerUpdateListener(({ editorState }) => {
|
|
110
|
+
const isSelected = editorState.read(() => {
|
|
111
|
+
const selection = $getSelection();
|
|
112
|
+
return ($isNodeSelection(selection) &&
|
|
113
|
+
selection.has(nodeKey) &&
|
|
114
|
+
selection.getNodes().length === 1);
|
|
115
|
+
});
|
|
116
|
+
if (isSelected) {
|
|
117
|
+
setShowEquationEditor(true);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}, [editor, nodeKey, onHide, showEquationEditor, isEditable]);
|
|
122
|
+
if (showEquationEditor && isEditable) {
|
|
123
|
+
return (_jsx(EquationEditor, { equation: equationValue, setEquation: setEquationValue, inline: inline, ref: inputRef }));
|
|
124
|
+
}
|
|
125
|
+
return (_jsx(MathJaxRenderer, { equation: equationValue, inline: inline, onDoubleClick: () => {
|
|
126
|
+
if (isEditable) {
|
|
127
|
+
setShowEquationEditor(true);
|
|
128
|
+
}
|
|
129
|
+
} }));
|
|
130
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EquationNode - Renders LaTeX math equations using KaTeX
|
|
3
|
+
* Ported from Lexical playground with modifications for SlashMD
|
|
4
|
+
*/
|
|
5
|
+
import { DecoratorNode, DOMConversionMap, DOMExportOutput, LexicalNode, NodeKey, SerializedLexicalNode, Spread, TextFormatType } from 'lexical';
|
|
6
|
+
export type SerializedEquationNode = Spread<{
|
|
7
|
+
equation: string;
|
|
8
|
+
inline: boolean;
|
|
9
|
+
format?: number;
|
|
10
|
+
strongMarker?: '_' | '*' | null;
|
|
11
|
+
emphasisMarker?: '_' | '*' | null;
|
|
12
|
+
}, SerializedLexicalNode>;
|
|
13
|
+
export declare class EquationNode extends DecoratorNode<JSX.Element> {
|
|
14
|
+
__equation: string;
|
|
15
|
+
__inline: boolean;
|
|
16
|
+
__format: number;
|
|
17
|
+
__strongMarker: '_' | '*' | null;
|
|
18
|
+
__emphasisMarker: '_' | '*' | null;
|
|
19
|
+
static getType(): string;
|
|
20
|
+
static clone(node: EquationNode): EquationNode;
|
|
21
|
+
constructor(equation: string, inline?: boolean, key?: NodeKey);
|
|
22
|
+
static importJSON(serializedNode: SerializedEquationNode): EquationNode;
|
|
23
|
+
exportJSON(): SerializedEquationNode;
|
|
24
|
+
getFormat(): number;
|
|
25
|
+
hasFormat(type: TextFormatType): boolean;
|
|
26
|
+
setFormat(format: number): this;
|
|
27
|
+
toggleFormat(type: TextFormatType): this;
|
|
28
|
+
getStrongMarker(): '_' | '*' | null;
|
|
29
|
+
setStrongMarker(marker: '_' | '*' | null): this;
|
|
30
|
+
getEmphasisMarker(): '_' | '*' | null;
|
|
31
|
+
setEmphasisMarker(marker: '_' | '*' | null): this;
|
|
32
|
+
createDOM(): HTMLElement;
|
|
33
|
+
exportDOM(): DOMExportOutput;
|
|
34
|
+
static importDOM(): DOMConversionMap | null;
|
|
35
|
+
updateDOM(prevNode: EquationNode): boolean;
|
|
36
|
+
getTextContent(): string;
|
|
37
|
+
getEquation(): string;
|
|
38
|
+
setEquation(equation: string): void;
|
|
39
|
+
isInline(): boolean;
|
|
40
|
+
decorate(): JSX.Element;
|
|
41
|
+
}
|
|
42
|
+
export declare function $createEquationNode(equation?: string, inline?: boolean): EquationNode;
|
|
43
|
+
export declare function $isEquationNode(node: LexicalNode | null | undefined): node is EquationNode;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/* eslint-disable react-refresh/only-export-components */
|
|
2
|
+
/**
|
|
3
|
+
* EquationNode - Renders LaTeX math equations using KaTeX
|
|
4
|
+
* Ported from Lexical playground with modifications for SlashMD
|
|
5
|
+
*/
|
|
6
|
+
import { DecoratorNode, TEXT_TYPE_TO_FORMAT, toggleTextFormatType, $applyNodeReplacement, } from 'lexical';
|
|
7
|
+
import { createElement, lazy, Suspense } from 'react';
|
|
8
|
+
import { createLiteAdaptorDocument } from '../../../mathjax-config.js';
|
|
9
|
+
// Lightweight MathJax instance for DOM export (copy/paste serialization)
|
|
10
|
+
const { adaptor: exportAdaptor, document: exportDocument } = createLiteAdaptorDocument({ fontCache: 'none' });
|
|
11
|
+
const EquationComponent = lazy(() => import('./EquationComponent.js'));
|
|
12
|
+
function $convertEquationElement(domNode) {
|
|
13
|
+
let equation = domNode.getAttribute('data-lexical-equation');
|
|
14
|
+
const inline = domNode.getAttribute('data-lexical-inline') === 'true';
|
|
15
|
+
// Decode the equation from base64
|
|
16
|
+
equation = atob(equation || '');
|
|
17
|
+
if (equation) {
|
|
18
|
+
const node = $createEquationNode(equation, inline);
|
|
19
|
+
return { node };
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
export class EquationNode extends DecoratorNode {
|
|
24
|
+
__equation;
|
|
25
|
+
__inline;
|
|
26
|
+
__format;
|
|
27
|
+
__strongMarker;
|
|
28
|
+
__emphasisMarker;
|
|
29
|
+
static getType() {
|
|
30
|
+
return 'equation';
|
|
31
|
+
}
|
|
32
|
+
static clone(node) {
|
|
33
|
+
const cloned = new EquationNode(node.__equation, node.__inline, node.__key);
|
|
34
|
+
cloned.__format = node.__format;
|
|
35
|
+
cloned.__strongMarker = node.__strongMarker;
|
|
36
|
+
cloned.__emphasisMarker = node.__emphasisMarker;
|
|
37
|
+
return cloned;
|
|
38
|
+
}
|
|
39
|
+
constructor(equation, inline, key) {
|
|
40
|
+
super(key);
|
|
41
|
+
this.__equation = equation;
|
|
42
|
+
this.__inline = inline ?? false;
|
|
43
|
+
this.__format = 0;
|
|
44
|
+
this.__strongMarker = null;
|
|
45
|
+
this.__emphasisMarker = null;
|
|
46
|
+
}
|
|
47
|
+
static importJSON(serializedNode) {
|
|
48
|
+
const node = $createEquationNode(serializedNode.equation, serializedNode.inline).setFormat(serializedNode.format ?? 0);
|
|
49
|
+
if (serializedNode.strongMarker) {
|
|
50
|
+
node.setStrongMarker(serializedNode.strongMarker);
|
|
51
|
+
}
|
|
52
|
+
if (serializedNode.emphasisMarker) {
|
|
53
|
+
node.setEmphasisMarker(serializedNode.emphasisMarker);
|
|
54
|
+
}
|
|
55
|
+
return node;
|
|
56
|
+
}
|
|
57
|
+
exportJSON() {
|
|
58
|
+
return {
|
|
59
|
+
type: 'equation',
|
|
60
|
+
version: 1,
|
|
61
|
+
equation: this.getEquation(),
|
|
62
|
+
inline: this.__inline,
|
|
63
|
+
format: this.__format,
|
|
64
|
+
strongMarker: this.__strongMarker,
|
|
65
|
+
emphasisMarker: this.__emphasisMarker,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// Mirrors TextNode's format bitmask API so this node can carry
|
|
69
|
+
// bold/italic/strikethrough state through the mdast<->Lexical round-trip.
|
|
70
|
+
getFormat() {
|
|
71
|
+
return this.getLatest().__format;
|
|
72
|
+
}
|
|
73
|
+
hasFormat(type) {
|
|
74
|
+
const formatFlag = TEXT_TYPE_TO_FORMAT[type];
|
|
75
|
+
return (this.getFormat() & formatFlag) !== 0;
|
|
76
|
+
}
|
|
77
|
+
setFormat(format) {
|
|
78
|
+
const self = this.getWritable();
|
|
79
|
+
self.__format = format;
|
|
80
|
+
return self;
|
|
81
|
+
}
|
|
82
|
+
toggleFormat(type) {
|
|
83
|
+
const format = this.getFormat();
|
|
84
|
+
const newFormat = toggleTextFormatType(format, type, null);
|
|
85
|
+
return this.setFormat(newFormat);
|
|
86
|
+
}
|
|
87
|
+
// Mirrors TextNode's --md-strong-marker/--md-emphasis-marker style hooks
|
|
88
|
+
// (via setMarkdownMarker/getMarkdownMarker in the mappers) so a bare
|
|
89
|
+
// `_$O(n)$_`/`__$O(n)__` span with no text sibling can still recover its
|
|
90
|
+
// original underscore-vs-asterisk marker on export.
|
|
91
|
+
getStrongMarker() {
|
|
92
|
+
return this.getLatest().__strongMarker;
|
|
93
|
+
}
|
|
94
|
+
setStrongMarker(marker) {
|
|
95
|
+
const self = this.getWritable();
|
|
96
|
+
self.__strongMarker = marker;
|
|
97
|
+
return self;
|
|
98
|
+
}
|
|
99
|
+
getEmphasisMarker() {
|
|
100
|
+
return this.getLatest().__emphasisMarker;
|
|
101
|
+
}
|
|
102
|
+
setEmphasisMarker(marker) {
|
|
103
|
+
const self = this.getWritable();
|
|
104
|
+
self.__emphasisMarker = marker;
|
|
105
|
+
return self;
|
|
106
|
+
}
|
|
107
|
+
createDOM() {
|
|
108
|
+
const element = document.createElement(this.__inline ? 'span' : 'div');
|
|
109
|
+
element.className = 'editor-equation';
|
|
110
|
+
return element;
|
|
111
|
+
}
|
|
112
|
+
exportDOM() {
|
|
113
|
+
const element = document.createElement(this.__inline ? 'span' : 'div');
|
|
114
|
+
// Encode the equation as base64 to avoid issues with special characters
|
|
115
|
+
const equation = btoa(this.__equation);
|
|
116
|
+
element.setAttribute('data-lexical-equation', equation);
|
|
117
|
+
element.setAttribute('data-lexical-inline', `${this.__inline}`);
|
|
118
|
+
try {
|
|
119
|
+
const node = exportDocument.convert(this.__equation, { display: !this.__inline });
|
|
120
|
+
element.innerHTML = exportAdaptor.innerHTML(node);
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
element.textContent = this.__equation;
|
|
124
|
+
}
|
|
125
|
+
return { element };
|
|
126
|
+
}
|
|
127
|
+
static importDOM() {
|
|
128
|
+
return {
|
|
129
|
+
div: (domNode) => {
|
|
130
|
+
if (!domNode.hasAttribute('data-lexical-equation')) {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
conversion: $convertEquationElement,
|
|
135
|
+
priority: 2,
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
span: (domNode) => {
|
|
139
|
+
if (!domNode.hasAttribute('data-lexical-equation')) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
conversion: $convertEquationElement,
|
|
144
|
+
priority: 1,
|
|
145
|
+
};
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
updateDOM(prevNode) {
|
|
150
|
+
return this.__inline !== prevNode.__inline;
|
|
151
|
+
}
|
|
152
|
+
getTextContent() {
|
|
153
|
+
return this.__equation;
|
|
154
|
+
}
|
|
155
|
+
getEquation() {
|
|
156
|
+
return this.__equation;
|
|
157
|
+
}
|
|
158
|
+
setEquation(equation) {
|
|
159
|
+
const writable = this.getWritable();
|
|
160
|
+
writable.__equation = equation;
|
|
161
|
+
}
|
|
162
|
+
isInline() {
|
|
163
|
+
return this.__inline;
|
|
164
|
+
}
|
|
165
|
+
decorate() {
|
|
166
|
+
return createElement(Suspense, { fallback: null }, createElement(EquationComponent, {
|
|
167
|
+
equation: this.__equation,
|
|
168
|
+
inline: this.__inline,
|
|
169
|
+
nodeKey: this.__key,
|
|
170
|
+
}));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export function $createEquationNode(equation = '', inline = false) {
|
|
174
|
+
const equationNode = new EquationNode(equation, inline);
|
|
175
|
+
return $applyNodeReplacement(equationNode);
|
|
176
|
+
}
|
|
177
|
+
export function $isEquationNode(node) {
|
|
178
|
+
return node instanceof EquationNode;
|
|
179
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
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, DOMConversionMap, DOMExportOutput, LexicalNode, NodeKey, SerializedLexicalNode, Spread, TextFormatType } from 'lexical';
|
|
7
|
+
export type SerializedFootnoteNode = Spread<{
|
|
8
|
+
footnoteId: string;
|
|
9
|
+
format?: number;
|
|
10
|
+
strongMarker?: '_' | '*' | null;
|
|
11
|
+
emphasisMarker?: '_' | '*' | null;
|
|
12
|
+
}, SerializedLexicalNode>;
|
|
13
|
+
export declare class FootnoteNode extends DecoratorNode<JSX.Element> {
|
|
14
|
+
__footnoteId: string;
|
|
15
|
+
__format: number;
|
|
16
|
+
__strongMarker: '_' | '*' | null;
|
|
17
|
+
__emphasisMarker: '_' | '*' | null;
|
|
18
|
+
static getType(): string;
|
|
19
|
+
static clone(node: FootnoteNode): FootnoteNode;
|
|
20
|
+
constructor(footnoteId: string, key?: NodeKey);
|
|
21
|
+
getFootnoteId(): string;
|
|
22
|
+
isInline(): boolean;
|
|
23
|
+
static importJSON(serializedNode: SerializedFootnoteNode): FootnoteNode;
|
|
24
|
+
exportJSON(): SerializedFootnoteNode;
|
|
25
|
+
getFormat(): number;
|
|
26
|
+
hasFormat(type: TextFormatType): boolean;
|
|
27
|
+
setFormat(format: number): this;
|
|
28
|
+
toggleFormat(type: TextFormatType): this;
|
|
29
|
+
getStrongMarker(): '_' | '*' | null;
|
|
30
|
+
setStrongMarker(marker: '_' | '*' | null): this;
|
|
31
|
+
getEmphasisMarker(): '_' | '*' | null;
|
|
32
|
+
setEmphasisMarker(marker: '_' | '*' | null): this;
|
|
33
|
+
createDOM(): HTMLElement;
|
|
34
|
+
updateDOM(): boolean;
|
|
35
|
+
exportDOM(): DOMExportOutput;
|
|
36
|
+
static importDOM(): DOMConversionMap | null;
|
|
37
|
+
decorate(): JSX.Element;
|
|
38
|
+
}
|
|
39
|
+
export declare function $createFootnoteNode(footnoteId: string): FootnoteNode;
|
|
40
|
+
export declare function $isFootnoteNode(node: LexicalNode | null | undefined): node is FootnoteNode;
|