@moldable-ai/editor 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/markdown-editor.d.ts +12 -3
- package/dist/components/markdown-editor.d.ts.map +1 -1
- package/dist/components/markdown-editor.js +23 -9
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -2
- package/dist/lib/lexical/editor-theme.d.ts +2 -0
- package/dist/lib/lexical/editor-theme.d.ts.map +1 -1
- package/dist/lib/lexical/editor-theme.js +2 -0
- package/dist/lib/lexical/file-reference-plugin.d.ts +9 -0
- package/dist/lib/lexical/file-reference-plugin.d.ts.map +1 -0
- package/dist/lib/lexical/file-reference-plugin.js +99 -0
- package/dist/lib/lexical/find-replace-plugin.d.ts +31 -0
- package/dist/lib/lexical/find-replace-plugin.d.ts.map +1 -0
- package/dist/lib/lexical/find-replace-plugin.js +205 -0
- package/dist/lib/lexical/headless-editor.d.ts.map +1 -1
- package/dist/lib/lexical/headless-editor.js +6 -0
- package/dist/lib/lexical/image-component.d.ts +8 -0
- package/dist/lib/lexical/image-component.d.ts.map +1 -0
- package/dist/lib/lexical/image-component.js +99 -0
- package/dist/lib/lexical/image-node.d.ts +36 -0
- package/dist/lib/lexical/image-node.d.ts.map +1 -0
- package/dist/lib/lexical/image-node.js +105 -0
- package/dist/lib/lexical/markdown-transformers.d.ts +4 -1
- package/dist/lib/lexical/markdown-transformers.d.ts.map +1 -1
- package/dist/lib/lexical/markdown-transformers.js +78 -0
- package/dist/lib/lexical/media-context.d.ts +7 -0
- package/dist/lib/lexical/media-context.d.ts.map +1 -0
- package/dist/lib/lexical/media-context.js +7 -0
- package/dist/lib/lexical/media-node.d.ts +32 -0
- package/dist/lib/lexical/media-node.d.ts.map +1 -0
- package/dist/lib/lexical/media-node.js +97 -0
- package/dist/lib/lexical/media-plugin.d.ts +12 -0
- package/dist/lib/lexical/media-plugin.d.ts.map +1 -0
- package/dist/lib/lexical/media-plugin.js +139 -0
- package/dist/lib/lexical/sync-plugin.d.ts +1 -0
- package/dist/lib/lexical/sync-plugin.d.ts.map +1 -1
- package/dist/lib/lexical/sync-plugin.js +13 -5
- package/package.json +19 -18
- package/src/styles/index.css +130 -0
- package/dist/lib/lexical/translation.test.d.ts +0 -2
- package/dist/lib/lexical/translation.test.d.ts.map +0 -1
- package/dist/lib/lexical/translation.test.js +0 -329
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
import { type MarkdownEditorHandle } from '../lib/lexical/find-replace-plugin';
|
|
2
|
+
import { type MarkdownReferenceOption } from '../lib/lexical/file-reference-plugin';
|
|
3
|
+
import { type MarkdownMediaUpload } from '../lib/lexical/media-plugin';
|
|
4
|
+
export interface MarkdownEditorProps {
|
|
2
5
|
value: string;
|
|
3
6
|
onChange: (value: string) => void;
|
|
4
7
|
placeholder?: string;
|
|
@@ -9,7 +12,13 @@ interface MarkdownEditorProps {
|
|
|
9
12
|
minHeight?: string;
|
|
10
13
|
maxHeight?: string;
|
|
11
14
|
hideMarkdownHint?: boolean;
|
|
15
|
+
findQuery?: string;
|
|
16
|
+
currentFindMatch?: number;
|
|
17
|
+
onFindMatchCountChange?: (count: number) => void;
|
|
18
|
+
onMediaUpload?: MarkdownMediaUpload;
|
|
19
|
+
resolveMediaUrl?: (src: string) => string;
|
|
20
|
+
referenceOptions?: MarkdownReferenceOption[];
|
|
12
21
|
}
|
|
13
|
-
export declare
|
|
14
|
-
export {};
|
|
22
|
+
export declare const MarkdownEditor: import("react").ForwardRefExoticComponent<MarkdownEditorProps & import("react").RefAttributes<MarkdownEditorHandle>>;
|
|
23
|
+
export type { MarkdownEditorHandle };
|
|
15
24
|
//# sourceMappingURL=markdown-editor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown-editor.d.ts","sourceRoot":"","sources":["../../src/components/markdown-editor.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"markdown-editor.d.ts","sourceRoot":"","sources":["../../src/components/markdown-editor.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAGL,KAAK,oBAAoB,EAC1B,MAAM,oCAAoC,CAAA;AAkB3C,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,sCAAsC,CAAA;AAG7C,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,6BAA6B,CAAA;AAOpC,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChD,aAAa,CAAC,EAAE,mBAAmB,CAAA;IACnC,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACzC,gBAAgB,CAAC,EAAE,uBAAuB,EAAE,CAAA;CAC7C;AAED,eAAO,MAAM,cAAc,sHAmJzB,CAAA;AAEF,YAAY,EAAE,oBAAoB,EAAE,CAAA"}
|
|
@@ -13,21 +13,28 @@ import { MarkdownShortcutPlugin } from '@lexical/react/LexicalMarkdownShortcutPl
|
|
|
13
13
|
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
|
|
14
14
|
import { RichTextPlugin } from '@lexical/react/LexicalRichTextPlugin';
|
|
15
15
|
import { TabIndentationPlugin } from '@lexical/react/LexicalTabIndentationPlugin';
|
|
16
|
-
import { useCallback, useRef } from 'react';
|
|
16
|
+
import { forwardRef, useCallback, useRef } from 'react';
|
|
17
17
|
import { cn } from '@moldable-ai/ui';
|
|
18
18
|
import { LINK_MATCHERS } from '../lib/lexical/auto-link-config';
|
|
19
19
|
import { ClickableLinkPlugin } from '../lib/lexical/clickable-link-plugin';
|
|
20
20
|
import { editorTheme } from '../lib/lexical/editor-theme';
|
|
21
|
+
import { FIND_REPLACE_UPDATE_TAG, FindReplacePlugin, } from '../lib/lexical/find-replace-plugin';
|
|
21
22
|
import { $convertFromMarkdownString, $convertToMarkdownString, markdownTransformers, } from '../lib/lexical/markdown-transformers';
|
|
22
|
-
import { SyncPlugin } from '../lib/lexical/sync-plugin';
|
|
23
|
+
import { EXTERNAL_MARKDOWN_SYNC_TAG, SyncPlugin, } from '../lib/lexical/sync-plugin';
|
|
23
24
|
import { FloatingToolbar } from './floating-toolbar';
|
|
24
25
|
import { CodeHighlightNode, CodeNode } from '@lexical/code';
|
|
25
26
|
import { HorizontalRuleNode } from '@lexical/extension';
|
|
26
27
|
import { AutoLinkNode, LinkNode } from '@lexical/link';
|
|
27
28
|
import { ListItemNode, ListNode } from '@lexical/list';
|
|
29
|
+
import { MarkNode } from '@lexical/mark';
|
|
28
30
|
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
|
|
29
31
|
import { $createParagraphNode, $getRoot } from 'lexical';
|
|
30
|
-
|
|
32
|
+
import { FileReferencePlugin, } from '../lib/lexical/file-reference-plugin';
|
|
33
|
+
import { ImageNode } from '../lib/lexical/image-node';
|
|
34
|
+
import { MediaNode } from '../lib/lexical/media-node';
|
|
35
|
+
import { MediaPasteDropPlugin, } from '../lib/lexical/media-plugin';
|
|
36
|
+
import { MediaProvider } from '../lib/lexical/media-context';
|
|
37
|
+
export const MarkdownEditor = forwardRef(function MarkdownEditor({ value, onChange, placeholder = 'Write something...', disabled = false, autoFocus = false, className, contentClassName, minHeight = '150px', maxHeight = '50vh', hideMarkdownHint = false, findQuery = '', currentFindMatch = 0, onFindMatchCountChange, onMediaUpload, resolveMediaUrl, referenceOptions, }, ref) {
|
|
31
38
|
const initialValueRef = useRef(value);
|
|
32
39
|
const initialConfig = {
|
|
33
40
|
namespace: 'MarkdownEditor',
|
|
@@ -38,8 +45,9 @@ export function MarkdownEditor({ value, onChange, placeholder = 'Write something
|
|
|
38
45
|
markdown: value,
|
|
39
46
|
transformers: markdownTransformers,
|
|
40
47
|
node: root,
|
|
41
|
-
shouldPreserveNewLines:
|
|
48
|
+
shouldPreserveNewLines: false,
|
|
42
49
|
});
|
|
50
|
+
root.selectEnd();
|
|
43
51
|
}
|
|
44
52
|
else {
|
|
45
53
|
root.append($createParagraphNode());
|
|
@@ -54,7 +62,10 @@ export function MarkdownEditor({ value, onChange, placeholder = 'Write something
|
|
|
54
62
|
CodeHighlightNode,
|
|
55
63
|
AutoLinkNode,
|
|
56
64
|
LinkNode,
|
|
65
|
+
MarkNode,
|
|
57
66
|
HorizontalRuleNode,
|
|
67
|
+
ImageNode,
|
|
68
|
+
MediaNode,
|
|
58
69
|
],
|
|
59
70
|
onError: (error) => {
|
|
60
71
|
console.error('MarkdownEditor error:', error);
|
|
@@ -62,12 +73,15 @@ export function MarkdownEditor({ value, onChange, placeholder = 'Write something
|
|
|
62
73
|
theme: editorTheme,
|
|
63
74
|
editable: !disabled,
|
|
64
75
|
};
|
|
65
|
-
const handleChange = useCallback((editorState) => {
|
|
76
|
+
const handleChange = useCallback((editorState, _editor, tags) => {
|
|
77
|
+
if (tags.has(FIND_REPLACE_UPDATE_TAG))
|
|
78
|
+
return;
|
|
79
|
+
if (tags.has(EXTERNAL_MARKDOWN_SYNC_TAG))
|
|
80
|
+
return;
|
|
66
81
|
editorState.read(() => {
|
|
67
|
-
// Use shouldPreserveNewLines: true to preserve empty paragraphs as extra newlines
|
|
68
82
|
const markdown = $convertToMarkdownString({
|
|
69
83
|
transformers: markdownTransformers,
|
|
70
|
-
shouldPreserveNewLines:
|
|
84
|
+
shouldPreserveNewLines: false,
|
|
71
85
|
});
|
|
72
86
|
// Only call onChange if the value actually changed
|
|
73
87
|
if (markdown !== initialValueRef.current) {
|
|
@@ -76,5 +90,5 @@ export function MarkdownEditor({ value, onChange, placeholder = 'Write something
|
|
|
76
90
|
}
|
|
77
91
|
});
|
|
78
92
|
}, [onChange]);
|
|
79
|
-
return (_jsx(LexicalComposer, { initialConfig: initialConfig, children: _jsxs("div", { className: cn('relative h-full', className), "data-lexical-editor-container": true, children: [_jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cn('prose prose-sm caret-primary dark:prose-invert max-w-none resize-none overflow-auto bg-transparent outline-none', disabled && 'cursor-not-allowed opacity-50', contentClassName), style: { minHeight, maxHeight }, "aria-placeholder": placeholder, "data-lexical-editor": "true", placeholder: _jsx("div", { className:
|
|
80
|
-
}
|
|
93
|
+
return (_jsx(LexicalComposer, { initialConfig: initialConfig, children: _jsx(MediaProvider, { value: { resolveMediaUrl }, children: _jsxs("div", { className: cn('relative h-full', className), "data-lexical-editor-container": true, children: [_jsx(RichTextPlugin, { contentEditable: _jsx(ContentEditable, { className: cn('prose prose-sm caret-primary dark:prose-invert max-w-none resize-none overflow-auto bg-transparent outline-none', disabled && 'cursor-not-allowed opacity-50', contentClassName), style: { minHeight, maxHeight }, "aria-placeholder": placeholder, "data-lexical-editor": "true", placeholder: _jsx("div", { className: cn('pointer-events-none absolute inset-x-0 top-1 overflow-hidden whitespace-nowrap', contentClassName, 'text-muted-foreground text-sm opacity-75'), children: placeholder }) }), ErrorBoundary: LexicalErrorBoundary }), _jsx(HistoryPlugin, {}), autoFocus && _jsx(AutoFocusPlugin, {}), _jsx(ListPlugin, {}), _jsx(CheckListPlugin, {}), _jsx(TabIndentationPlugin, {}), _jsx(LinkPlugin, {}), _jsx(AutoLinkPlugin, { matchers: LINK_MATCHERS }), _jsx(ClickableLinkPlugin, {}), _jsx(FloatingToolbar, {}), _jsx(MarkdownShortcutPlugin, { transformers: markdownTransformers }), _jsx(FileReferencePlugin, { references: referenceOptions }), _jsx(MediaPasteDropPlugin, { onMediaUpload: onMediaUpload }), _jsx(OnChangePlugin, { onChange: handleChange, ignoreSelectionChange: true }), _jsx(FindReplacePlugin, { query: findQuery, currentIndex: currentFindMatch, onMatchCountChange: onFindMatchCountChange, editorRef: ref }), _jsx(SyncPlugin, { value: value, initialValueRef: initialValueRef }), !hideMarkdownHint && (_jsx("div", { className: "mt-1 flex justify-end", children: _jsx("span", { className: "text-muted-foreground/60 text-xs", children: "Markdown supported" }) }))] }) }) }));
|
|
94
|
+
});
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
export { MarkdownEditor } from './components/markdown-editor';
|
|
1
|
+
export { MarkdownEditor, type MarkdownEditorHandle, type MarkdownEditorProps, } from './components/markdown-editor';
|
|
2
2
|
export { FloatingToolbar } from './components/floating-toolbar';
|
|
3
|
-
export { $convertFromMarkdownString, $convertToMarkdownString, markdownTransformers, HR, } from './lib/lexical/markdown-transformers';
|
|
3
|
+
export { $convertFromMarkdownString, $convertToMarkdownString, markdownTransformers, HR, IMAGE, OBSIDIAN_IMAGE_EMBED, MEDIA, } from './lib/lexical/markdown-transformers';
|
|
4
|
+
export { ImageNode, $createImageNode, $isImageNode, type ImagePayload, } from './lib/lexical/image-node';
|
|
5
|
+
export { MediaNode, $createMediaNode, $isMediaNode, type MediaKind, type MediaPayload, } from './lib/lexical/media-node';
|
|
6
|
+
export { MediaPasteDropPlugin, type MarkdownMediaKind, type MarkdownMediaUpload, type MarkdownMediaUploadResult, } from './lib/lexical/media-plugin';
|
|
7
|
+
export { FileReferencePlugin, type MarkdownReferenceOption, } from './lib/lexical/file-reference-plugin';
|
|
4
8
|
export { editorTheme } from './lib/lexical/editor-theme';
|
|
5
9
|
export { LINK_MATCHERS, URL_REGEX, EMAIL_REGEX, } from './lib/lexical/auto-link-config';
|
|
6
10
|
export { useFloatingToolbar, type FloatingToolbarState, } from './lib/lexical/floating-toolbar-plugin';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,GACzB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAA;AAG/D,OAAO,EACL,0BAA0B,EAC1B,wBAAwB,EACxB,oBAAoB,EACpB,EAAE,EACF,KAAK,EACL,oBAAoB,EACpB,KAAK,GACN,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EACL,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,KAAK,YAAY,GAClB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,KAAK,SAAS,EACd,KAAK,YAAY,GAClB,MAAM,0BAA0B,CAAA;AACjC,OAAO,EACL,oBAAoB,EACpB,KAAK,iBAAiB,EACtB,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,GAC/B,MAAM,4BAA4B,CAAA;AACnC,OAAO,EACL,mBAAmB,EACnB,KAAK,uBAAuB,GAC7B,MAAM,qCAAqC,CAAA;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AACxD,OAAO,EACL,aAAa,EACb,SAAS,EACT,WAAW,GACZ,MAAM,gCAAgC,CAAA;AACvC,OAAO,EACL,kBAAkB,EAClB,KAAK,oBAAoB,GAC1B,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AACtD,OAAO,EACL,mBAAmB,EACnB,SAAS,GACV,MAAM,qCAAqC,CAAA;AAG5C,OAAO,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
// Components
|
|
2
|
-
export { MarkdownEditor } from './components/markdown-editor';
|
|
2
|
+
export { MarkdownEditor, } from './components/markdown-editor';
|
|
3
3
|
export { FloatingToolbar } from './components/floating-toolbar';
|
|
4
4
|
// Lexical utilities (for advanced use cases)
|
|
5
|
-
export { $convertFromMarkdownString, $convertToMarkdownString, markdownTransformers, HR, } from './lib/lexical/markdown-transformers';
|
|
5
|
+
export { $convertFromMarkdownString, $convertToMarkdownString, markdownTransformers, HR, IMAGE, OBSIDIAN_IMAGE_EMBED, MEDIA, } from './lib/lexical/markdown-transformers';
|
|
6
|
+
export { ImageNode, $createImageNode, $isImageNode, } from './lib/lexical/image-node';
|
|
7
|
+
export { MediaNode, $createMediaNode, $isMediaNode, } from './lib/lexical/media-node';
|
|
8
|
+
export { MediaPasteDropPlugin, } from './lib/lexical/media-plugin';
|
|
9
|
+
export { FileReferencePlugin, } from './lib/lexical/file-reference-plugin';
|
|
6
10
|
export { editorTheme } from './lib/lexical/editor-theme';
|
|
7
11
|
export { LINK_MATCHERS, URL_REGEX, EMAIL_REGEX, } from './lib/lexical/auto-link-config';
|
|
8
12
|
export { useFloatingToolbar, } from './lib/lexical/floating-toolbar-plugin';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor-theme.d.ts","sourceRoot":"","sources":["../../../src/lib/lexical/editor-theme.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,WAAW
|
|
1
|
+
{"version":3,"file":"editor-theme.d.ts","sourceRoot":"","sources":["../../../src/lib/lexical/editor-theme.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCvB,CAAA"}
|
|
@@ -32,6 +32,8 @@ export const editorTheme = {
|
|
|
32
32
|
listitemUnchecked: 'lexical-listitem-unchecked',
|
|
33
33
|
},
|
|
34
34
|
link: 'text-primary underline hover:text-primary/80 cursor-pointer',
|
|
35
|
+
mark: 'lexical-mark',
|
|
36
|
+
markOverlap: 'lexical-mark-overlap',
|
|
35
37
|
quote: 'border-l-4 border-muted px-4 py-2 my-2 text-muted-foreground',
|
|
36
38
|
code: 'bg-muted p-3 my-4 rounded-md overflow-x-auto font-mono text-sm leading-loose text-muted-foreground',
|
|
37
39
|
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface MarkdownReferenceOption {
|
|
2
|
+
path: string;
|
|
3
|
+
title?: string;
|
|
4
|
+
kind?: 'note' | 'file';
|
|
5
|
+
}
|
|
6
|
+
export declare function FileReferencePlugin({ references, }: {
|
|
7
|
+
references?: MarkdownReferenceOption[];
|
|
8
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=file-reference-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-reference-plugin.d.ts","sourceRoot":"","sources":["../../../src/lib/lexical/file-reference-plugin.tsx"],"names":[],"mappings":"AAYA,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB;AAqDD,wBAAgB,mBAAmB,CAAC,EAClC,UAAU,GACX,EAAE;IACD,UAAU,CAAC,EAAE,uBAAuB,EAAE,CAAA;CACvC,2CAmGA"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { LexicalTypeaheadMenuPlugin, MenuOption, useBasicTypeaheadTriggerMatch, } from '@lexical/react/LexicalTypeaheadMenuPlugin';
|
|
4
|
+
import { $createTextNode } from 'lexical';
|
|
5
|
+
import { useMemo, useState } from 'react';
|
|
6
|
+
import { createPortal } from 'react-dom';
|
|
7
|
+
import { cn } from '@moldable-ai/ui';
|
|
8
|
+
class FileReferenceOption extends MenuOption {
|
|
9
|
+
path;
|
|
10
|
+
title;
|
|
11
|
+
isCreate;
|
|
12
|
+
constructor(path, title, isCreate = false) {
|
|
13
|
+
super(`${isCreate ? 'create' : 'reference'}:${path}`);
|
|
14
|
+
this.path = path;
|
|
15
|
+
this.title = title;
|
|
16
|
+
this.isCreate = isCreate;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function referenceTarget(path) {
|
|
20
|
+
return path.replace(/\.md$/, '');
|
|
21
|
+
}
|
|
22
|
+
function titleFromPath(path) {
|
|
23
|
+
return path.split('/').pop()?.replace(/\.md$/, '') ?? path;
|
|
24
|
+
}
|
|
25
|
+
function slugifyReference(value) {
|
|
26
|
+
const segments = value
|
|
27
|
+
.split('/')
|
|
28
|
+
.map((segment) => segment
|
|
29
|
+
.trim()
|
|
30
|
+
.toLowerCase()
|
|
31
|
+
.replace(/[^a-z0-9 -]/g, '')
|
|
32
|
+
.replace(/\s+/g, '-')
|
|
33
|
+
.replace(/^-+|-+$/g, ''))
|
|
34
|
+
.filter(Boolean);
|
|
35
|
+
return segments.join('/') || 'untitled';
|
|
36
|
+
}
|
|
37
|
+
function referenceScore(option, query) {
|
|
38
|
+
const needle = query.trim().toLowerCase();
|
|
39
|
+
if (!needle)
|
|
40
|
+
return 1;
|
|
41
|
+
const title = (option.title ?? titleFromPath(option.path)).toLowerCase();
|
|
42
|
+
const path = option.path.toLowerCase();
|
|
43
|
+
if (title === needle || path === needle || path === `${needle}.md`)
|
|
44
|
+
return 100;
|
|
45
|
+
if (title.startsWith(needle))
|
|
46
|
+
return 80;
|
|
47
|
+
if (path.startsWith(needle))
|
|
48
|
+
return 70;
|
|
49
|
+
if (title.includes(needle))
|
|
50
|
+
return 50;
|
|
51
|
+
if (path.includes(needle))
|
|
52
|
+
return 40;
|
|
53
|
+
return 0;
|
|
54
|
+
}
|
|
55
|
+
export function FileReferencePlugin({ references, }) {
|
|
56
|
+
const [query, setQuery] = useState(null);
|
|
57
|
+
const triggerFn = useBasicTypeaheadTriggerMatch('@', {
|
|
58
|
+
allowWhitespace: true,
|
|
59
|
+
maxLength: 80,
|
|
60
|
+
minLength: 0,
|
|
61
|
+
});
|
|
62
|
+
const options = useMemo(() => {
|
|
63
|
+
const matching = (references ?? [])
|
|
64
|
+
.map((reference) => ({
|
|
65
|
+
reference,
|
|
66
|
+
score: referenceScore(reference, query ?? ''),
|
|
67
|
+
}))
|
|
68
|
+
.filter(({ score }) => score > 0)
|
|
69
|
+
.sort((a, b) => b.score - a.score || a.reference.path.localeCompare(b.reference.path))
|
|
70
|
+
.slice(0, 8)
|
|
71
|
+
.map(({ reference }) => new FileReferenceOption(reference.path, reference.title ?? titleFromPath(reference.path)));
|
|
72
|
+
const trimmed = query?.trim();
|
|
73
|
+
if (!trimmed)
|
|
74
|
+
return matching;
|
|
75
|
+
const createPath = `${slugifyReference(trimmed)}.md`;
|
|
76
|
+
const hasExact = (references ?? []).some((reference) => reference.path.toLowerCase() === createPath.toLowerCase() ||
|
|
77
|
+
reference.path.replace(/\.md$/, '').toLowerCase() ===
|
|
78
|
+
createPath.replace(/\.md$/, '').toLowerCase());
|
|
79
|
+
return hasExact
|
|
80
|
+
? matching
|
|
81
|
+
: [
|
|
82
|
+
...matching,
|
|
83
|
+
new FileReferenceOption(createPath, `Create ${trimmed}`, true),
|
|
84
|
+
].slice(0, 9);
|
|
85
|
+
}, [query, references]);
|
|
86
|
+
return (_jsx(LexicalTypeaheadMenuPlugin, { triggerFn: triggerFn, options: options, onQueryChange: setQuery, preselectFirstItem: true, onSelectOption: (option, textNodeContainingQuery, closeMenu) => {
|
|
87
|
+
if (!textNodeContainingQuery)
|
|
88
|
+
return;
|
|
89
|
+
const node = $createTextNode(`[[${referenceTarget(option.path)}]]`);
|
|
90
|
+
textNodeContainingQuery.replace(node);
|
|
91
|
+
node.selectEnd();
|
|
92
|
+
closeMenu();
|
|
93
|
+
}, menuRenderFn: (anchorElementRef, { selectedIndex, selectOptionAndCleanUp, setHighlightedIndex }) => {
|
|
94
|
+
if (!anchorElementRef.current || options.length === 0)
|
|
95
|
+
return null;
|
|
96
|
+
return createPortal(_jsx("div", { className: "moldable-editor-reference-menu", children: options.map((option, index) => (_jsxs("button", { ref: option.setRefElement, type: "button", role: "option", "aria-selected": selectedIndex === index, className: cn('moldable-editor-reference-option', selectedIndex === index &&
|
|
97
|
+
'moldable-editor-reference-option-selected'), onMouseEnter: () => setHighlightedIndex(index), onMouseDown: (event) => event.preventDefault(), onClick: () => selectOptionAndCleanUp(option), children: [_jsx("span", { className: "moldable-editor-reference-title", children: option.title }), _jsx("span", { className: "moldable-editor-reference-path", children: option.isCreate ? 'New note' : option.path })] }, option.key))) }), anchorElementRef.current);
|
|
98
|
+
} }));
|
|
99
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ForwardedRef } from 'react';
|
|
2
|
+
import { type LexicalEditor } from 'lexical';
|
|
3
|
+
export interface MarkdownEditorHandle {
|
|
4
|
+
replaceCurrentFindMatch: (replacement: string) => boolean;
|
|
5
|
+
replaceAllFindMatches: (replacement: string) => number;
|
|
6
|
+
focus: () => void;
|
|
7
|
+
}
|
|
8
|
+
interface FindReplacePluginProps {
|
|
9
|
+
query: string;
|
|
10
|
+
currentIndex: number;
|
|
11
|
+
onMatchCountChange?: (count: number) => void;
|
|
12
|
+
editorRef: ForwardedRef<MarkdownEditorHandle>;
|
|
13
|
+
}
|
|
14
|
+
export interface FindMatch {
|
|
15
|
+
start: number;
|
|
16
|
+
end: number;
|
|
17
|
+
}
|
|
18
|
+
export declare const FIND_MATCH_MARK_ID = "moldable-find-match";
|
|
19
|
+
export declare const FIND_CURRENT_MARK_ID = "moldable-find-current";
|
|
20
|
+
export declare const FIND_REPLACE_UPDATE_TAG = "moldable-find-replace";
|
|
21
|
+
export declare function getTextMatches(text: string, query: string): FindMatch[];
|
|
22
|
+
export declare function $clearFindMarks(): void;
|
|
23
|
+
export declare function $getFindMatches(query: string): FindMatch[];
|
|
24
|
+
export declare function $applyFindHighlights(query: string, currentIndex: number): number;
|
|
25
|
+
export declare function updateFindHighlights(editor: LexicalEditor, query: string, currentIndex: number): number;
|
|
26
|
+
export declare function registerFindMarkResolver(editor: LexicalEditor): () => void;
|
|
27
|
+
export declare function replaceFindMatch(editor: LexicalEditor, query: string, replacement: string, currentIndex: number): boolean;
|
|
28
|
+
export declare function replaceAllFindMatches(editor: LexicalEditor, query: string, replacement: string): number;
|
|
29
|
+
export declare function FindReplacePlugin({ query, currentIndex, onMatchCountChange, editorRef, }: FindReplacePluginProps): null;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=find-replace-plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"find-replace-plugin.d.ts","sourceRoot":"","sources":["../../../src/lib/lexical/find-replace-plugin.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,YAAY,EAKlB,MAAM,OAAO,CAAA;AASd,OAAO,EAML,KAAK,aAAa,EAGnB,MAAM,SAAS,CAAA;AAEhB,MAAM,WAAW,oBAAoB;IACnC,uBAAuB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAA;IACzD,qBAAqB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAA;IACtD,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED,UAAU,sBAAsB;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5C,SAAS,EAAE,YAAY,CAAC,oBAAoB,CAAC,CAAA;CAC9C;AAaD,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,eAAO,MAAM,kBAAkB,wBAAwB,CAAA;AACvD,eAAO,MAAM,oBAAoB,0BAA0B,CAAA;AAC3D,eAAO,MAAM,uBAAuB,0BAA0B,CAAA;AAM9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,CAiBvE;AAwDD,wBAAgB,eAAe,SAgB9B;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,eAG5C;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAoBvE;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,UAcrB;AAeD,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,aAAa,cAa7D;AAED,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,WAsBrB;AAED,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,UAsBpB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,YAAY,EACZ,kBAAkB,EAClB,SAAS,GACV,EAAE,sBAAsB,QAqDxB"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
3
|
+
import { useCallback, useEffect, useImperativeHandle, useRef, } from 'react';
|
|
4
|
+
import { $createMarkNode, $isMarkNode, $unwrapMarkNode, $wrapSelectionInMarkNode, MarkNode, } from '@lexical/mark';
|
|
5
|
+
import { mergeRegister, registerNestedElementResolver } from '@lexical/utils';
|
|
6
|
+
import { $createRangeSelection, $getRoot, $getSelection, $nodesOfType, $setSelection, } from 'lexical';
|
|
7
|
+
export const FIND_MATCH_MARK_ID = 'moldable-find-match';
|
|
8
|
+
export const FIND_CURRENT_MARK_ID = 'moldable-find-current';
|
|
9
|
+
export const FIND_REPLACE_UPDATE_TAG = 'moldable-find-replace';
|
|
10
|
+
function getNormalizedQuery(query) {
|
|
11
|
+
return query.trim().toLocaleLowerCase();
|
|
12
|
+
}
|
|
13
|
+
export function getTextMatches(text, query) {
|
|
14
|
+
const normalizedQuery = getNormalizedQuery(query);
|
|
15
|
+
if (!normalizedQuery)
|
|
16
|
+
return [];
|
|
17
|
+
const normalizedText = text.toLocaleLowerCase();
|
|
18
|
+
const matches = [];
|
|
19
|
+
let searchFrom = 0;
|
|
20
|
+
while (searchFrom <= normalizedText.length - normalizedQuery.length) {
|
|
21
|
+
const start = normalizedText.indexOf(normalizedQuery, searchFrom);
|
|
22
|
+
if (start === -1)
|
|
23
|
+
break;
|
|
24
|
+
matches.push({ start, end: start + normalizedQuery.length });
|
|
25
|
+
searchFrom = start + normalizedQuery.length;
|
|
26
|
+
}
|
|
27
|
+
return matches;
|
|
28
|
+
}
|
|
29
|
+
function $collectTextEntries() {
|
|
30
|
+
const entries = [];
|
|
31
|
+
let text = '';
|
|
32
|
+
for (const node of $getRoot().getAllTextNodes()) {
|
|
33
|
+
const value = node.getTextContent();
|
|
34
|
+
const start = text.length;
|
|
35
|
+
text += value;
|
|
36
|
+
entries.push({ node, start, end: text.length });
|
|
37
|
+
}
|
|
38
|
+
return { entries, text };
|
|
39
|
+
}
|
|
40
|
+
function getTextPosition(entries, offset, bias) {
|
|
41
|
+
if (bias === 'next') {
|
|
42
|
+
for (const entry of entries) {
|
|
43
|
+
if (offset >= entry.start && offset < entry.end) {
|
|
44
|
+
return { node: entry.node, offset: offset - entry.start };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (offset > entry.start && offset <= entry.end) {
|
|
51
|
+
return { node: entry.node, offset: offset - entry.start };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const fallback = bias === 'next' ? entries[0] : entries.at(-1);
|
|
56
|
+
if (!fallback)
|
|
57
|
+
return null;
|
|
58
|
+
return {
|
|
59
|
+
node: fallback.node,
|
|
60
|
+
offset: bias === 'next' ? 0 : fallback.node.getTextContentSize(),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function $createSelectionForMatch(match) {
|
|
64
|
+
const { entries } = $collectTextEntries();
|
|
65
|
+
const start = getTextPosition(entries, match.start, 'next');
|
|
66
|
+
const end = getTextPosition(entries, match.end, 'previous');
|
|
67
|
+
if (!start || !end)
|
|
68
|
+
return null;
|
|
69
|
+
const selection = $createRangeSelection();
|
|
70
|
+
selection.setTextNodeRange(start.node, start.offset, end.node, end.offset);
|
|
71
|
+
return selection;
|
|
72
|
+
}
|
|
73
|
+
export function $clearFindMarks() {
|
|
74
|
+
for (const markNode of $nodesOfType(MarkNode)) {
|
|
75
|
+
let nextNode = markNode;
|
|
76
|
+
if (nextNode.hasID(FIND_MATCH_MARK_ID)) {
|
|
77
|
+
nextNode = nextNode.deleteID(FIND_MATCH_MARK_ID);
|
|
78
|
+
}
|
|
79
|
+
if (nextNode.hasID(FIND_CURRENT_MARK_ID)) {
|
|
80
|
+
nextNode = nextNode.deleteID(FIND_CURRENT_MARK_ID);
|
|
81
|
+
}
|
|
82
|
+
if (nextNode.getIDs().length === 0) {
|
|
83
|
+
$unwrapMarkNode(nextNode);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export function $getFindMatches(query) {
|
|
88
|
+
const { text } = $collectTextEntries();
|
|
89
|
+
return getTextMatches(text, query);
|
|
90
|
+
}
|
|
91
|
+
export function $applyFindHighlights(query, currentIndex) {
|
|
92
|
+
$clearFindMarks();
|
|
93
|
+
const matches = $getFindMatches(query);
|
|
94
|
+
for (let index = matches.length - 1; index >= 0; index -= 1) {
|
|
95
|
+
const selection = $createSelectionForMatch(matches[index]);
|
|
96
|
+
if (selection) {
|
|
97
|
+
$wrapSelectionInMarkNode(selection, false, FIND_MATCH_MARK_ID);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const currentMatch = matches[currentIndex] ?? matches[0];
|
|
101
|
+
if (currentMatch) {
|
|
102
|
+
const selection = $createSelectionForMatch(currentMatch);
|
|
103
|
+
if (selection) {
|
|
104
|
+
$wrapSelectionInMarkNode(selection, false, FIND_CURRENT_MARK_ID);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return matches.length;
|
|
108
|
+
}
|
|
109
|
+
export function updateFindHighlights(editor, query, currentIndex) {
|
|
110
|
+
let matchCount = 0;
|
|
111
|
+
editor.update(() => {
|
|
112
|
+
const previousSelection = $getSelection()?.clone() ?? null;
|
|
113
|
+
matchCount = $applyFindHighlights(query, currentIndex);
|
|
114
|
+
$setSelection(previousSelection);
|
|
115
|
+
}, { discrete: true, tag: FIND_REPLACE_UPDATE_TAG });
|
|
116
|
+
return matchCount;
|
|
117
|
+
}
|
|
118
|
+
function scrollCurrentMatchIntoView(editor) {
|
|
119
|
+
const editorRoot = editor.getRootElement();
|
|
120
|
+
const currentMatch = editorRoot?.querySelector('.lexical-mark-overlap');
|
|
121
|
+
currentMatch?.scrollIntoView({
|
|
122
|
+
behavior: 'smooth',
|
|
123
|
+
block: 'center',
|
|
124
|
+
inline: 'nearest',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
export function registerFindMarkResolver(editor) {
|
|
128
|
+
return registerNestedElementResolver(editor, MarkNode, (from) => $createMarkNode(from.getIDs()), (from, to) => {
|
|
129
|
+
if (!$isMarkNode(to))
|
|
130
|
+
return;
|
|
131
|
+
for (const id of from.getIDs()) {
|
|
132
|
+
to.addID(id);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
export function replaceFindMatch(editor, query, replacement, currentIndex) {
|
|
137
|
+
let replaced = false;
|
|
138
|
+
editor.update(() => {
|
|
139
|
+
$clearFindMarks();
|
|
140
|
+
const matches = $getFindMatches(query);
|
|
141
|
+
const match = matches[currentIndex] ?? matches[0];
|
|
142
|
+
if (!match)
|
|
143
|
+
return;
|
|
144
|
+
const selection = $createSelectionForMatch(match);
|
|
145
|
+
if (!selection)
|
|
146
|
+
return;
|
|
147
|
+
$setSelection(selection);
|
|
148
|
+
selection.insertText(replacement);
|
|
149
|
+
replaced = true;
|
|
150
|
+
}, { discrete: true });
|
|
151
|
+
return replaced;
|
|
152
|
+
}
|
|
153
|
+
export function replaceAllFindMatches(editor, query, replacement) {
|
|
154
|
+
let replacedCount = 0;
|
|
155
|
+
editor.update(() => {
|
|
156
|
+
$clearFindMarks();
|
|
157
|
+
const matches = $getFindMatches(query);
|
|
158
|
+
for (let index = matches.length - 1; index >= 0; index -= 1) {
|
|
159
|
+
const selection = $createSelectionForMatch(matches[index]);
|
|
160
|
+
if (!selection)
|
|
161
|
+
continue;
|
|
162
|
+
$setSelection(selection);
|
|
163
|
+
selection.insertText(replacement);
|
|
164
|
+
replacedCount += 1;
|
|
165
|
+
}
|
|
166
|
+
}, { discrete: true });
|
|
167
|
+
return replacedCount;
|
|
168
|
+
}
|
|
169
|
+
export function FindReplacePlugin({ query, currentIndex, onMatchCountChange, editorRef, }) {
|
|
170
|
+
const [editor] = useLexicalComposerContext();
|
|
171
|
+
const onMatchCountChangeRef = useRef(onMatchCountChange);
|
|
172
|
+
useEffect(() => {
|
|
173
|
+
onMatchCountChangeRef.current = onMatchCountChange;
|
|
174
|
+
}, [onMatchCountChange]);
|
|
175
|
+
useImperativeHandle(editorRef, () => ({
|
|
176
|
+
replaceCurrentFindMatch: (replacement) => replaceFindMatch(editor, query, replacement, currentIndex),
|
|
177
|
+
replaceAllFindMatches: (replacement) => replaceAllFindMatches(editor, query, replacement),
|
|
178
|
+
focus: () => editor.focus(),
|
|
179
|
+
}), [currentIndex, editor, query]);
|
|
180
|
+
const applyHighlights = useCallback(() => {
|
|
181
|
+
const matchCount = updateFindHighlights(editor, query, currentIndex);
|
|
182
|
+
onMatchCountChangeRef.current?.(matchCount);
|
|
183
|
+
if (getNormalizedQuery(query) && matchCount > 0) {
|
|
184
|
+
window.requestAnimationFrame(() => scrollCurrentMatchIntoView(editor));
|
|
185
|
+
}
|
|
186
|
+
}, [currentIndex, editor, query]);
|
|
187
|
+
useEffect(() => {
|
|
188
|
+
return registerFindMarkResolver(editor);
|
|
189
|
+
}, [editor]);
|
|
190
|
+
useEffect(() => {
|
|
191
|
+
applyHighlights();
|
|
192
|
+
const unregister = mergeRegister(editor.registerUpdateListener(({ tags }) => {
|
|
193
|
+
if (tags.has(FIND_REPLACE_UPDATE_TAG))
|
|
194
|
+
return;
|
|
195
|
+
applyHighlights();
|
|
196
|
+
}), () => {
|
|
197
|
+
editor.update(() => $clearFindMarks(), {
|
|
198
|
+
discrete: true,
|
|
199
|
+
tag: FIND_REPLACE_UPDATE_TAG,
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
return unregister;
|
|
203
|
+
}, [applyHighlights, editor]);
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"headless-editor.d.ts","sourceRoot":"","sources":["../../../src/lib/lexical/headless-editor.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"headless-editor.d.ts","sourceRoot":"","sources":["../../../src/lib/lexical/headless-editor.ts"],"names":[],"mappings":"AAWA;;;GAGG;AACH,wBAAgB,4BAA4B,oCAqB3C"}
|
|
@@ -4,7 +4,10 @@ import { HorizontalRuleNode } from '@lexical/extension';
|
|
|
4
4
|
import { createHeadlessEditor } from '@lexical/headless';
|
|
5
5
|
import { AutoLinkNode, LinkNode } from '@lexical/link';
|
|
6
6
|
import { ListItemNode, ListNode } from '@lexical/list';
|
|
7
|
+
import { MarkNode } from '@lexical/mark';
|
|
7
8
|
import { HeadingNode, QuoteNode } from '@lexical/rich-text';
|
|
9
|
+
import { ImageNode } from './image-node';
|
|
10
|
+
import { MediaNode } from './media-node';
|
|
8
11
|
/**
|
|
9
12
|
* Creates a headless Lexical editor for server-side operations.
|
|
10
13
|
* Use this for translation, content extraction, etc.
|
|
@@ -20,7 +23,10 @@ export function createMoldableHeadlessEditor() {
|
|
|
20
23
|
CodeHighlightNode,
|
|
21
24
|
AutoLinkNode,
|
|
22
25
|
LinkNode,
|
|
26
|
+
MarkNode,
|
|
23
27
|
HorizontalRuleNode,
|
|
28
|
+
ImageNode,
|
|
29
|
+
MediaNode,
|
|
24
30
|
],
|
|
25
31
|
theme: editorTheme,
|
|
26
32
|
onError: (error) => {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type NodeKey } from 'lexical';
|
|
2
|
+
export declare function ImageComponent({ altText, nodeKey, src, width, }: {
|
|
3
|
+
altText: string;
|
|
4
|
+
nodeKey: NodeKey;
|
|
5
|
+
src: string;
|
|
6
|
+
width?: number;
|
|
7
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
//# sourceMappingURL=image-component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-component.d.ts","sourceRoot":"","sources":["../../../src/lib/lexical/image-component.tsx"],"names":[],"mappings":"AAKA,OAAO,EAQL,KAAK,OAAO,EACb,MAAM,SAAS,CAAA;AAiBhB,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,OAAO,EACP,GAAG,EACH,KAAK,GACN,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,2CAoIA"}
|