@llui/markdown-editor 0.2.8 → 0.2.10
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/__llui_deps.json +1 -0
- package/dist/editor.d.ts +4 -0
- package/dist/editor.d.ts.map +1 -1
- package/dist/editor.js +3 -0
- package/dist/editor.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/paste.d.ts +21 -0
- package/dist/paste.d.ts.map +1 -0
- package/dist/paste.js +71 -0
- package/dist/paste.js.map +1 -0
- package/dist/plugins/_preview.d.ts +10 -0
- package/dist/plugins/_preview.d.ts.map +1 -0
- package/dist/plugins/_preview.js +41 -0
- package/dist/plugins/_preview.js.map +1 -0
- package/dist/plugins/math.d.ts +6 -1
- package/dist/plugins/math.d.ts.map +1 -1
- package/dist/plugins/math.js +3 -4
- package/dist/plugins/math.js.map +1 -1
- package/dist/plugins/mermaid.d.ts +6 -1
- package/dist/plugins/mermaid.d.ts.map +1 -1
- package/dist/plugins/mermaid.js +3 -4
- package/dist/plugins/mermaid.js.map +1 -1
- package/package.json +3 -3
package/dist/__llui_deps.json
CHANGED
package/dist/editor.d.ts
CHANGED
|
@@ -26,6 +26,10 @@ export interface EditorConfig {
|
|
|
26
26
|
onReady?: (editor: LexicalEditor) => void;
|
|
27
27
|
/** Render the built-in toolbar above the editor. Default false (minimal). */
|
|
28
28
|
toolbar?: boolean;
|
|
29
|
+
/** Convert plain-text Markdown to rich content on paste. Default true.
|
|
30
|
+
* Pastes that carry `text/html` are always left to Lexical's HTML import,
|
|
31
|
+
* regardless of this flag. Set false to paste Markdown as literal text. */
|
|
32
|
+
pasteMarkdown?: boolean;
|
|
29
33
|
/** Enable collaborative editing. The editor hands you a markdown `seed` and
|
|
30
34
|
* status sinks; return a binding (build it with `yjsCollab` from
|
|
31
35
|
* `@llui/lexical-collab`, wiring your own provider). Mutually exclusive with
|
package/dist/editor.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAMA,OAAO,EAA2B,KAAK,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAA;AAM9F,OAAO,EAAmC,KAAK,MAAM,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAMA,OAAO,EAA2B,KAAK,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,SAAS,CAAA;AAM9F,OAAO,EAAmC,KAAK,MAAM,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAWjG,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAMrE,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,SAAS,EAEd,KAAK,WAAW,EAChB,KAAK,WAAW,EACjB,MAAM,YAAY,CAAA;AAEnB,MAAM,WAAW,YAAY;IAC3B;2FACuF;IACvF,OAAO,CAAC,EAAE,SAAS,cAAc,EAAE,CAAA;IACnC,4CAA4C;IAC5C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IACtB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,+BAA+B;IAC/B,KAAK,CAAC,EAAE,kBAAkB,CAAA;IAC1B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,uEAAuE;IACvE,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;IAC9C,mFAAmF;IACnF,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAA;IACzC,6EAA6E;IAC7E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;+EAE2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;wEAIoE;IACpE,MAAM,CAAC,EAAE,aAAa,CAAA;CACvB;AAED;;iEAEiE;AACjE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,MAAM,IAAI,CAAA;CAChD;AAED;;;yEAGyE;AACzE,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAA;IACrC,QAAQ,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAA;IACtC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;IACjC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACjC;AAED,gEAAgE;AAChE,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,WAAW,KAAK,aAAa,CAAA;AAEjE,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,KAAK,EAAE,SAAS,WAAW,EAAE,CAAA;IAC7B,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,CAAA;CAC5B;AAOD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,GAAE,YAAiB,GACxB,kBAAkB,CAAC,WAAW,EAAE,SAAS,EAAE,YAAY,CAAC,CA0L1D"}
|
package/dist/editor.js
CHANGED
|
@@ -9,6 +9,7 @@ import { component, div } from '@llui/dom';
|
|
|
9
9
|
import { lexicalForeign, registerDecoratorBridges, PROGRAMMATIC_TAG, } from '@llui/lexical';
|
|
10
10
|
import { corePlugin } from './plugins/core.js';
|
|
11
11
|
import { linkPlugin } from './plugins/link.js';
|
|
12
|
+
import { registerMarkdownPaste } from './paste.js';
|
|
12
13
|
import { toolbar as renderToolbar } from './surfaces/toolbar.js';
|
|
13
14
|
import { buildTransformers } from './transformers/registry.js';
|
|
14
15
|
import { mergeTheme } from './theme.js';
|
|
@@ -129,6 +130,8 @@ export function markdownEditor(config = {}) {
|
|
|
129
130
|
: {}),
|
|
130
131
|
register: (editor) => {
|
|
131
132
|
const disposers = [registerMarkdownShortcuts(editor, transformers)];
|
|
133
|
+
if (config.pasteMarkdown !== false)
|
|
134
|
+
disposers.push(registerMarkdownPaste(editor, transformers));
|
|
132
135
|
if (decorators.length > 0)
|
|
133
136
|
disposers.push(registerDecoratorBridges(editor, decorators));
|
|
134
137
|
if (collabBinding)
|
package/dist/editor.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"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","sourcesContent":["// `markdownEditor(config)` — the high-level component. Lexical owns the live\n// document; this wires the foreign seam to the markdown transformer converters,\n// surfaces the format state for the chrome, routes command intents back to the\n// live editor through effects, and COMPOSES plugin UI extensions (each plugin's\n// state slice + reducer + view + effects) into the single component.\n\nimport { $getRoot, $setSelection, type EditorThemeClasses, type LexicalEditor } from 'lexical'\nimport {\n $convertFromMarkdownString,\n $convertToMarkdownString,\n registerMarkdownShortcuts,\n} from '@lexical/markdown'\nimport { component, div, type Renderable, type Signal, type SignalComponentDef } from '@llui/dom'\nimport {\n lexicalForeign,\n registerDecoratorBridges,\n PROGRAMMATIC_TAG,\n type DecoratorBridge,\n} from '@llui/lexical'\nimport { corePlugin } from './plugins/core.js'\nimport { linkPlugin } from './plugins/link.js'\nimport { toolbar as renderToolbar } from './surfaces/toolbar.js'\nimport type { CommandItem, MarkdownPlugin } from './plugins/types.js'\nimport type { PluginUI } from './plugins/ui.js'\nimport { buildTransformers } from './transformers/registry.js'\nimport { mergeTheme } from './theme.js'\nimport { computeFormatState } from './format.js'\nimport { makeOnEffect } from './effects.js'\nimport {\n countWords,\n init,\n update,\n type EditorEffect,\n type EditorMsg,\n type EditorOutMsg,\n type EditorState,\n type FormatState,\n} from './state.js'\n\nexport interface EditorConfig {\n /** Plugins composing the feature set; order defines transformer precedence.\n * Defaults to `[corePlugin(), linkPlugin()]` so the minimal editor has GFM + links. */\n plugins?: readonly MarkdownPlugin[]\n /** Initial markdown (uncontrolled seed). */\n defaultValue?: string\n /** Controlled: the consumer owns this signal; the editor follows it. */\n value?: Signal<string>\n /** Debounced markdown-emission window (ms). Default 300. */\n changeDebounceMs?: number\n placeholder?: string\n readonly?: boolean\n /** Lexical theme class map. */\n theme?: EditorThemeClasses\n /** Editor namespace (instance isolation). */\n namespace?: string\n /** Outbound markdown (after debounce). */\n onChange?: (markdown: string) => void\n /** Outbound format surface (for chrome built outside this package). */\n onFormatChange?: (format: FormatState) => void\n /** Receives the live Lexical editor at mount (imperative access, collab hooks). */\n onReady?: (editor: LexicalEditor) => void\n /** Render the built-in toolbar above the editor. Default false (minimal). */\n toolbar?: boolean\n /** Enable collaborative editing. The editor hands you a markdown `seed` and\n * status sinks; return a binding (build it with `yjsCollab` from\n * `@llui/lexical-collab`, wiring your own provider). Mutually exclusive with\n * `value` — the shared CRDT document, not a markdown signal, owns the content.\n * `defaultValue` becomes the seed the bootstrapping peer writes. */\n collab?: CollabFactory\n}\n\n/** Disposer-returning binding the collab layer installs on the live editor.\n * `@llui/lexical-collab`'s `YjsCollab` satisfies this structurally, so\n * `@llui/markdown-editor` needs no Yjs dependency of its own. */\nexport interface CollabBinding {\n register: (editor: LexicalEditor) => () => void\n}\n\n/** Hooks the editor injects into the {@link CollabFactory}: a markdown `seed`\n * (run once by the bootstrapping peer to fill an empty shared doc from\n * `defaultValue`) plus status sinks the editor mirrors into `state.collab`.\n * Spread straight into `yjsCollab({ id, provider, user, ...hooks })`. */\nexport interface CollabHooks {\n seed: (editor: LexicalEditor) => void\n onStatus: (connected: boolean) => void\n onSync: (synced: boolean) => void\n onPeers: (count: number) => void\n}\n\n/** Builds the collab binding from the editor-supplied hooks. */\nexport type CollabFactory = (hooks: CollabHooks) => CollabBinding\n\n/** Hooks the chrome layer (toolbar/menus) uses to compose around the editor. */\nexport interface EditorParts {\n /** The merged, surface-filtered command items. */\n items: readonly CommandItem[]\n /** Reactive format signal for `connect`-style toolbars. */\n format: Signal<FormatState>\n}\n\n/** Default plugin set when the consumer supplies none. */\nfunction defaultPlugins(): MarkdownPlugin[] {\n return [corePlugin(), linkPlugin()]\n}\n\n/**\n * Build the markdown editor component. Embed it with `mountApp(el, markdownEditor(...))`\n * or compose it inside a larger component.\n */\nexport function markdownEditor(\n config: EditorConfig = {},\n): SignalComponentDef<EditorState, EditorMsg, EditorEffect> {\n const plugins = config.plugins && config.plugins.length > 0 ? config.plugins : defaultPlugins()\n const transformers = buildTransformers(plugins)\n\n const items: CommandItem[] = plugins.flatMap((p) => p.items ?? [])\n const itemsById = new Map(items.map((i) => [i.id, i]))\n // Share the merged item list with plugins that want it (e.g. the slash menu).\n for (const plugin of plugins) plugin.onItems?.(items)\n const decorators: DecoratorBridge[] = plugins.flatMap((p) => p.decorators ?? [])\n const pluginUIs: Array<{ name: string; ui: PluginUI }> = plugins\n .filter((p): p is MarkdownPlugin & { ui: PluginUI } => p.ui !== undefined)\n .map((p) => ({ name: p.name, ui: p.ui }))\n const pluginUIByName = new Map(pluginUIs.map((p) => [p.name, p.ui]))\n\n // The live editor, captured at mount; effects dispatch through it.\n let editorRef: LexicalEditor | null = null\n const getEditor = (): LexicalEditor | null => editorRef\n\n const baseOnEffect = makeOnEffect(getEditor, itemsById, {\n onChange: config.onChange,\n onFormatChange: config.onFormatChange,\n applyValue: (editor, value) =>\n editor.update(\n () => {\n $convertFromMarkdownString(value, transformers)\n // Clear selection so the reconciler doesn't pull DOM focus into the\n // editor on an external push (e.g. typing in a bound source textarea).\n $setSelection(null)\n },\n { tag: PROGRAMMATIC_TAG },\n ),\n })\n\n if (config.collab && config.value) {\n throw new Error(\n 'markdownEditor: `collab` and `value` are mutually exclusive — in a collaborative ' +\n 'session the shared CRDT document owns the content, not a markdown signal. ' +\n 'Use `defaultValue` as the bootstrap seed instead.',\n )\n }\n\n const collabEnabled = !!config.collab\n const seedValue = config.value ? config.value.peek() : (config.defaultValue ?? '')\n\n // ── Composed TEA: core + plugin UI slices ──────────────────────────────────\n const composedInit = (): [EditorState, EditorEffect[]] => {\n const [core, effects] = init({\n value: seedValue,\n readonly: config.readonly ?? false,\n collab: collabEnabled,\n })\n const slices: Record<string, unknown> = {}\n for (const { name, ui } of pluginUIs) slices[name] = ui.init()\n return [{ ...core, plugins: slices }, effects]\n }\n\n const composedUpdate = (state: EditorState, msg: EditorMsg): [EditorState, EditorEffect[]] => {\n if (msg.type === 'plugin') {\n const ui = pluginUIByName.get(msg.name)\n if (!ui?.update) return [state, []]\n const result = ui.update(state.plugins[msg.name], msg.msg)\n const [slice, effects] = (Array.isArray(result) ? result : [result, []]) as [\n unknown,\n unknown[],\n ]\n return [\n { ...state, plugins: { ...state.plugins, [msg.name]: slice } },\n effects.map((effect) => ({ type: 'pluginEffect' as const, name: msg.name, effect })),\n ]\n }\n return update(state, msg)\n }\n\n const composedOnEffect = (\n effect: EditorEffect,\n api: { send: (msg: EditorMsg) => void; state: Signal<EditorState> },\n ): void => {\n if (effect.type === 'pluginEffect') {\n const ui = pluginUIByName.get(effect.name)\n ui?.onEffect?.(effect.effect, {\n editor: getEditor,\n send: (msg) => api.send({ type: 'plugin', name: effect.name, msg }),\n emit: (msg) => api.send(msg as EditorMsg),\n })\n return\n }\n baseOnEffect(effect, api)\n }\n\n const view = ({\n state,\n send,\n }: {\n state: Signal<EditorState>\n send: (msg: EditorMsg) => void\n }): Renderable => {\n // Build the collab binding (once, at mount) from the consumer's factory,\n // injecting the markdown seed + status sinks that mirror into `state.collab`.\n const collabBinding: CollabBinding | null = config.collab\n ? config.collab({\n seed: () => {\n $convertFromMarkdownString(seedValue, transformers)\n $setSelection(null)\n },\n onStatus: (connected) => send({ type: 'collabStatus', connected }),\n onSync: (synced) => send({ type: 'collabSync', synced }),\n onPeers: (peers) => send({ type: 'collabPeers', peers }),\n })\n : null\n\n const host = lexicalForeign<EditorOutMsg>({\n namespace: config.namespace ?? 'llui-markdown',\n theme: mergeTheme(config.theme),\n plugins,\n serialize: (editor) =>\n editor.getEditorState().read(() => $convertToMarkdownString(transformers)),\n deserialize: (_editor, value) => {\n $convertFromMarkdownString(value, transformers)\n $setSelection(null)\n },\n // In collab mode the shared CRDT owns the document: the local undo stack\n // and the boot-time seed are disabled — the binding supplies a scoped undo\n // manager and a sync-gated bootstrap instead.\n ...(collabBinding ? { history: false, seedMode: 'deferred' as const } : {}),\n defaultValue: collabBinding || config.value ? undefined : (config.defaultValue ?? ''),\n ...(config.value && !collabBinding ? { value: config.value } : {}),\n readonly: state.at('readonly'),\n ...(config.changeDebounceMs !== undefined\n ? { changeDebounceMs: config.changeDebounceMs }\n : {}),\n register: (editor) => {\n const disposers = [registerMarkdownShortcuts(editor, transformers)]\n if (decorators.length > 0) disposers.push(registerDecoratorBridges(editor, decorators))\n if (collabBinding) disposers.push(collabBinding.register(editor))\n return () => {\n for (const dispose of disposers) dispose()\n }\n },\n onReady: (editor) => {\n editorRef = editor\n if (config.placeholder) {\n editor.getRootElement()?.setAttribute('data-placeholder', config.placeholder)\n }\n config.onReady?.(editor)\n },\n onChange: (value) => send({ type: 'markdownChanged', value }),\n onSelectionChange: (ctx) => {\n const format = computeFormatState(ctx.editor, ctx)\n const text = ctx.editor.getEditorState().read(() => $getRoot().getTextContent())\n // Toggle an empty marker so CSS can show the placeholder.\n ctx.editor.getRootElement()?.setAttribute('data-empty', text === '' ? 'true' : 'false')\n send({ type: 'formatChanged', format, wordCount: countWords(text), charCount: text.length })\n },\n emit: (msg) => send(msg),\n })\n\n // Plugin view contributions (overlays/panels) — each gets its own slice + send.\n const pluginViews: Renderable = pluginUIs.flatMap(({ name, ui }) => {\n if (!ui.view) return []\n const rendered = ui.view({\n state: state.at(`plugins.${name}`),\n send: (msg) => send({ type: 'plugin', name, msg }),\n editor: getEditor,\n })\n return Array.isArray(rendered) ? rendered : [rendered]\n })\n\n if (!config.toolbar) return [host, ...pluginViews]\n return [\n div({ 'data-scope': 'md-editor', 'data-part': 'root' }, [\n renderToolbar({ format: state.at('format'), send, items, collab: state.at('collab') }),\n div({ 'data-scope': 'md-editor', 'data-part': 'surface' }, [host]),\n ]),\n ...pluginViews,\n ]\n }\n\n return component<EditorState, EditorMsg, EditorEffect>({\n name: 'MarkdownEditor',\n init: composedInit,\n update: composedUpdate,\n view,\n onEffect: composedOnEffect,\n })\n}\n"]}
|
|
1
|
+
{"version":3,"file":"editor.js","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"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","sourcesContent":["// `markdownEditor(config)` — the high-level component. Lexical owns the live\n// document; this wires the foreign seam to the markdown transformer converters,\n// surfaces the format state for the chrome, routes command intents back to the\n// live editor through effects, and COMPOSES plugin UI extensions (each plugin's\n// state slice + reducer + view + effects) into the single component.\n\nimport { $getRoot, $setSelection, type EditorThemeClasses, type LexicalEditor } from 'lexical'\nimport {\n $convertFromMarkdownString,\n $convertToMarkdownString,\n registerMarkdownShortcuts,\n} from '@lexical/markdown'\nimport { component, div, type Renderable, type Signal, type SignalComponentDef } from '@llui/dom'\nimport {\n lexicalForeign,\n registerDecoratorBridges,\n PROGRAMMATIC_TAG,\n type DecoratorBridge,\n} from '@llui/lexical'\nimport { corePlugin } from './plugins/core.js'\nimport { linkPlugin } from './plugins/link.js'\nimport { registerMarkdownPaste } from './paste.js'\nimport { toolbar as renderToolbar } from './surfaces/toolbar.js'\nimport type { CommandItem, MarkdownPlugin } from './plugins/types.js'\nimport type { PluginUI } from './plugins/ui.js'\nimport { buildTransformers } from './transformers/registry.js'\nimport { mergeTheme } from './theme.js'\nimport { computeFormatState } from './format.js'\nimport { makeOnEffect } from './effects.js'\nimport {\n countWords,\n init,\n update,\n type EditorEffect,\n type EditorMsg,\n type EditorOutMsg,\n type EditorState,\n type FormatState,\n} from './state.js'\n\nexport interface EditorConfig {\n /** Plugins composing the feature set; order defines transformer precedence.\n * Defaults to `[corePlugin(), linkPlugin()]` so the minimal editor has GFM + links. */\n plugins?: readonly MarkdownPlugin[]\n /** Initial markdown (uncontrolled seed). */\n defaultValue?: string\n /** Controlled: the consumer owns this signal; the editor follows it. */\n value?: Signal<string>\n /** Debounced markdown-emission window (ms). Default 300. */\n changeDebounceMs?: number\n placeholder?: string\n readonly?: boolean\n /** Lexical theme class map. */\n theme?: EditorThemeClasses\n /** Editor namespace (instance isolation). */\n namespace?: string\n /** Outbound markdown (after debounce). */\n onChange?: (markdown: string) => void\n /** Outbound format surface (for chrome built outside this package). */\n onFormatChange?: (format: FormatState) => void\n /** Receives the live Lexical editor at mount (imperative access, collab hooks). */\n onReady?: (editor: LexicalEditor) => void\n /** Render the built-in toolbar above the editor. Default false (minimal). */\n toolbar?: boolean\n /** Convert plain-text Markdown to rich content on paste. Default true.\n * Pastes that carry `text/html` are always left to Lexical's HTML import,\n * regardless of this flag. Set false to paste Markdown as literal text. */\n pasteMarkdown?: boolean\n /** Enable collaborative editing. The editor hands you a markdown `seed` and\n * status sinks; return a binding (build it with `yjsCollab` from\n * `@llui/lexical-collab`, wiring your own provider). Mutually exclusive with\n * `value` — the shared CRDT document, not a markdown signal, owns the content.\n * `defaultValue` becomes the seed the bootstrapping peer writes. */\n collab?: CollabFactory\n}\n\n/** Disposer-returning binding the collab layer installs on the live editor.\n * `@llui/lexical-collab`'s `YjsCollab` satisfies this structurally, so\n * `@llui/markdown-editor` needs no Yjs dependency of its own. */\nexport interface CollabBinding {\n register: (editor: LexicalEditor) => () => void\n}\n\n/** Hooks the editor injects into the {@link CollabFactory}: a markdown `seed`\n * (run once by the bootstrapping peer to fill an empty shared doc from\n * `defaultValue`) plus status sinks the editor mirrors into `state.collab`.\n * Spread straight into `yjsCollab({ id, provider, user, ...hooks })`. */\nexport interface CollabHooks {\n seed: (editor: LexicalEditor) => void\n onStatus: (connected: boolean) => void\n onSync: (synced: boolean) => void\n onPeers: (count: number) => void\n}\n\n/** Builds the collab binding from the editor-supplied hooks. */\nexport type CollabFactory = (hooks: CollabHooks) => CollabBinding\n\n/** Hooks the chrome layer (toolbar/menus) uses to compose around the editor. */\nexport interface EditorParts {\n /** The merged, surface-filtered command items. */\n items: readonly CommandItem[]\n /** Reactive format signal for `connect`-style toolbars. */\n format: Signal<FormatState>\n}\n\n/** Default plugin set when the consumer supplies none. */\nfunction defaultPlugins(): MarkdownPlugin[] {\n return [corePlugin(), linkPlugin()]\n}\n\n/**\n * Build the markdown editor component. Embed it with `mountApp(el, markdownEditor(...))`\n * or compose it inside a larger component.\n */\nexport function markdownEditor(\n config: EditorConfig = {},\n): SignalComponentDef<EditorState, EditorMsg, EditorEffect> {\n const plugins = config.plugins && config.plugins.length > 0 ? config.plugins : defaultPlugins()\n const transformers = buildTransformers(plugins)\n\n const items: CommandItem[] = plugins.flatMap((p) => p.items ?? [])\n const itemsById = new Map(items.map((i) => [i.id, i]))\n // Share the merged item list with plugins that want it (e.g. the slash menu).\n for (const plugin of plugins) plugin.onItems?.(items)\n const decorators: DecoratorBridge[] = plugins.flatMap((p) => p.decorators ?? [])\n const pluginUIs: Array<{ name: string; ui: PluginUI }> = plugins\n .filter((p): p is MarkdownPlugin & { ui: PluginUI } => p.ui !== undefined)\n .map((p) => ({ name: p.name, ui: p.ui }))\n const pluginUIByName = new Map(pluginUIs.map((p) => [p.name, p.ui]))\n\n // The live editor, captured at mount; effects dispatch through it.\n let editorRef: LexicalEditor | null = null\n const getEditor = (): LexicalEditor | null => editorRef\n\n const baseOnEffect = makeOnEffect(getEditor, itemsById, {\n onChange: config.onChange,\n onFormatChange: config.onFormatChange,\n applyValue: (editor, value) =>\n editor.update(\n () => {\n $convertFromMarkdownString(value, transformers)\n // Clear selection so the reconciler doesn't pull DOM focus into the\n // editor on an external push (e.g. typing in a bound source textarea).\n $setSelection(null)\n },\n { tag: PROGRAMMATIC_TAG },\n ),\n })\n\n if (config.collab && config.value) {\n throw new Error(\n 'markdownEditor: `collab` and `value` are mutually exclusive — in a collaborative ' +\n 'session the shared CRDT document owns the content, not a markdown signal. ' +\n 'Use `defaultValue` as the bootstrap seed instead.',\n )\n }\n\n const collabEnabled = !!config.collab\n const seedValue = config.value ? config.value.peek() : (config.defaultValue ?? '')\n\n // ── Composed TEA: core + plugin UI slices ──────────────────────────────────\n const composedInit = (): [EditorState, EditorEffect[]] => {\n const [core, effects] = init({\n value: seedValue,\n readonly: config.readonly ?? false,\n collab: collabEnabled,\n })\n const slices: Record<string, unknown> = {}\n for (const { name, ui } of pluginUIs) slices[name] = ui.init()\n return [{ ...core, plugins: slices }, effects]\n }\n\n const composedUpdate = (state: EditorState, msg: EditorMsg): [EditorState, EditorEffect[]] => {\n if (msg.type === 'plugin') {\n const ui = pluginUIByName.get(msg.name)\n if (!ui?.update) return [state, []]\n const result = ui.update(state.plugins[msg.name], msg.msg)\n const [slice, effects] = (Array.isArray(result) ? result : [result, []]) as [\n unknown,\n unknown[],\n ]\n return [\n { ...state, plugins: { ...state.plugins, [msg.name]: slice } },\n effects.map((effect) => ({ type: 'pluginEffect' as const, name: msg.name, effect })),\n ]\n }\n return update(state, msg)\n }\n\n const composedOnEffect = (\n effect: EditorEffect,\n api: { send: (msg: EditorMsg) => void; state: Signal<EditorState> },\n ): void => {\n if (effect.type === 'pluginEffect') {\n const ui = pluginUIByName.get(effect.name)\n ui?.onEffect?.(effect.effect, {\n editor: getEditor,\n send: (msg) => api.send({ type: 'plugin', name: effect.name, msg }),\n emit: (msg) => api.send(msg as EditorMsg),\n })\n return\n }\n baseOnEffect(effect, api)\n }\n\n const view = ({\n state,\n send,\n }: {\n state: Signal<EditorState>\n send: (msg: EditorMsg) => void\n }): Renderable => {\n // Build the collab binding (once, at mount) from the consumer's factory,\n // injecting the markdown seed + status sinks that mirror into `state.collab`.\n const collabBinding: CollabBinding | null = config.collab\n ? config.collab({\n seed: () => {\n $convertFromMarkdownString(seedValue, transformers)\n $setSelection(null)\n },\n onStatus: (connected) => send({ type: 'collabStatus', connected }),\n onSync: (synced) => send({ type: 'collabSync', synced }),\n onPeers: (peers) => send({ type: 'collabPeers', peers }),\n })\n : null\n\n const host = lexicalForeign<EditorOutMsg>({\n namespace: config.namespace ?? 'llui-markdown',\n theme: mergeTheme(config.theme),\n plugins,\n serialize: (editor) =>\n editor.getEditorState().read(() => $convertToMarkdownString(transformers)),\n deserialize: (_editor, value) => {\n $convertFromMarkdownString(value, transformers)\n $setSelection(null)\n },\n // In collab mode the shared CRDT owns the document: the local undo stack\n // and the boot-time seed are disabled — the binding supplies a scoped undo\n // manager and a sync-gated bootstrap instead.\n ...(collabBinding ? { history: false, seedMode: 'deferred' as const } : {}),\n defaultValue: collabBinding || config.value ? undefined : (config.defaultValue ?? ''),\n ...(config.value && !collabBinding ? { value: config.value } : {}),\n readonly: state.at('readonly'),\n ...(config.changeDebounceMs !== undefined\n ? { changeDebounceMs: config.changeDebounceMs }\n : {}),\n register: (editor) => {\n const disposers = [registerMarkdownShortcuts(editor, transformers)]\n if (config.pasteMarkdown !== false)\n disposers.push(registerMarkdownPaste(editor, transformers))\n if (decorators.length > 0) disposers.push(registerDecoratorBridges(editor, decorators))\n if (collabBinding) disposers.push(collabBinding.register(editor))\n return () => {\n for (const dispose of disposers) dispose()\n }\n },\n onReady: (editor) => {\n editorRef = editor\n if (config.placeholder) {\n editor.getRootElement()?.setAttribute('data-placeholder', config.placeholder)\n }\n config.onReady?.(editor)\n },\n onChange: (value) => send({ type: 'markdownChanged', value }),\n onSelectionChange: (ctx) => {\n const format = computeFormatState(ctx.editor, ctx)\n const text = ctx.editor.getEditorState().read(() => $getRoot().getTextContent())\n // Toggle an empty marker so CSS can show the placeholder.\n ctx.editor.getRootElement()?.setAttribute('data-empty', text === '' ? 'true' : 'false')\n send({ type: 'formatChanged', format, wordCount: countWords(text), charCount: text.length })\n },\n emit: (msg) => send(msg),\n })\n\n // Plugin view contributions (overlays/panels) — each gets its own slice + send.\n const pluginViews: Renderable = pluginUIs.flatMap(({ name, ui }) => {\n if (!ui.view) return []\n const rendered = ui.view({\n state: state.at(`plugins.${name}`),\n send: (msg) => send({ type: 'plugin', name, msg }),\n editor: getEditor,\n })\n return Array.isArray(rendered) ? rendered : [rendered]\n })\n\n if (!config.toolbar) return [host, ...pluginViews]\n return [\n div({ 'data-scope': 'md-editor', 'data-part': 'root' }, [\n renderToolbar({ format: state.at('format'), send, items, collab: state.at('collab') }),\n div({ 'data-scope': 'md-editor', 'data-part': 'surface' }, [host]),\n ]),\n ...pluginViews,\n ]\n }\n\n return component<EditorState, EditorMsg, EditorEffect>({\n name: 'MarkdownEditor',\n init: composedInit,\n update: composedUpdate,\n view,\n onEffect: composedOnEffect,\n })\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -16,6 +16,7 @@ export { type Mention, type MentionPluginOptions, mentionPlugin } from './plugin
|
|
|
16
16
|
export { type EmojiPluginOptions, DEFAULT_EMOJI, emojiPlugin } from './plugins/emoji.js';
|
|
17
17
|
export { type ImagePluginOptions, imagePlugin } from './plugins/image.js';
|
|
18
18
|
export { tablePlugin } from './plugins/table.js';
|
|
19
|
+
export { $insertMarkdownAtSelection, registerMarkdownPaste } from './paste.js';
|
|
19
20
|
export { GFM_NODES, GFM_TRANSFORMERS } from './transformers/gfm.js';
|
|
20
21
|
export { buildTransformers, orderTransformers } from './transformers/registry.js';
|
|
21
22
|
export { computeFormatState } from './format.js';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,cAAc,GACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,MAAM,EACN,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,cAAc,GACf,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,wBAAwB,EAC7B,iBAAiB,GAClB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,aAAa,EACb,cAAc,GACf,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,KAAK,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACxF,OAAO,EAAE,KAAK,kBAAkB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,6BAA6B,EAC7B,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,cAAc,EACd,cAAc,EACd,OAAO,GACR,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,cAAc,GACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EACL,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,MAAM,EACN,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EACL,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,mBAAmB,EACxB,cAAc,GACf,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,wBAAwB,EAC7B,iBAAiB,GAClB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,oBAAoB,EACzB,aAAa,EACb,cAAc,GACf,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,oBAAoB,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAAE,KAAK,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACxF,OAAO,EAAE,KAAK,kBAAkB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAE9E,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,6BAA6B,EAC7B,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,cAAc,EACd,cAAc,EACd,OAAO,GACR,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAAE,KAAK,iBAAiB,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,7 @@ export { mentionPlugin } from './plugins/mention.js';
|
|
|
17
17
|
export { DEFAULT_EMOJI, emojiPlugin } from './plugins/emoji.js';
|
|
18
18
|
export { imagePlugin } from './plugins/image.js';
|
|
19
19
|
export { tablePlugin } from './plugins/table.js';
|
|
20
|
+
export { $insertMarkdownAtSelection, registerMarkdownPaste } from './paste.js';
|
|
20
21
|
export { GFM_NODES, GFM_TRANSFORMERS } from './transformers/gfm.js';
|
|
21
22
|
export { buildTransformers, orderTransformers } from './transformers/registry.js';
|
|
22
23
|
export { computeFormatState } from './format.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAML,cAAc,GACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EAUL,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,MAAM,EACN,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EAKN,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAKL,cAAc,GACf,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAA0B,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAGL,iBAAiB,GAClB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAA0B,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAIL,aAAa,EACb,cAAc,GACf,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,EAA0B,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAA6B,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAA2C,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAA2B,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACxF,OAAO,EAA2B,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,6BAA6B,EAC7B,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EAIL,cAAc,EACd,cAAc,EACd,OAAO,GACR,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAA0B,UAAU,EAAE,MAAM,2BAA2B,CAAA","sourcesContent":["// `@llui/markdown-editor` — WYSIWYG Markdown editor for LLui, built on Lexical.\n\nexport {\n type EditorConfig,\n type EditorParts,\n type CollabBinding,\n type CollabHooks,\n type CollabFactory,\n markdownEditor,\n} from './editor.js'\n\nexport {\n type BlockType,\n type FormatState,\n type OverlayKind,\n type CollabStatus,\n type EditorState,\n type EditorMsg,\n type EditorOutMsg,\n type EditorEffect,\n type InitOptions,\n EMPTY_FORMAT,\n COLLAB_OFF,\n init,\n update,\n countWords,\n} from './state.js'\n\nexport {\n type ItemSurface,\n type CommandItem,\n type CommandContext,\n type MarkdownPlugin,\n} from './plugins/types.js'\n\nexport {\n type PluginUI,\n type PluginUISpec,\n type PluginViewArgs,\n type PluginEffectContext,\n definePluginUI,\n} from './plugins/ui.js'\n\nexport { type CorePluginOptions, corePlugin } from './plugins/core.js'\nexport {\n type InlineFormat,\n type SingleBlockPluginOptions,\n singleBlockPlugin,\n} from './plugins/single-block.js'\nexport { type LinkPluginOptions, linkPlugin } from './plugins/link.js'\nexport {\n type CalloutKind,\n type CalloutData,\n type CalloutPluginOptions,\n calloutPlugin,\n $insertCallout,\n} from './plugins/callout.js'\nexport { hrPlugin, $insertHorizontalRule } from './plugins/hr.js'\nexport { slashPlugin } from './plugins/slash.js'\nexport { contextMenuPlugin } from './plugins/context-menu.js'\nexport { floatingToolbarPlugin } from './plugins/floating-toolbar.js'\nexport { type MathPluginOptions, mathPlugin } from './plugins/math.js'\nexport { type MermaidPluginOptions, mermaidPlugin } from './plugins/mermaid.js'\nexport { type Mention, type MentionPluginOptions, mentionPlugin } from './plugins/mention.js'\nexport { type EmojiPluginOptions, DEFAULT_EMOJI, emojiPlugin } from './plugins/emoji.js'\nexport { type ImagePluginOptions, imagePlugin } from './plugins/image.js'\nexport { tablePlugin } from './plugins/table.js'\n\nexport { GFM_NODES, GFM_TRANSFORMERS } from './transformers/gfm.js'\nexport { buildTransformers, orderTransformers } from './transformers/registry.js'\n\nexport { computeFormatState } from './format.js'\n\nexport {\n STRIKETHROUGH_CLASS,\n UNDERLINE_CLASS,\n UNDERLINE_STRIKETHROUGH_CLASS,\n defaultTheme,\n mergeTheme,\n} from './theme.js'\n\nexport {\n type ToolbarItemParts,\n type ToolbarParts,\n type ToolbarOptions,\n DEFAULT_GLYPHS,\n connectToolbar,\n toolbar,\n} from './surfaces/toolbar.js'\n\nexport { type LinkDialogOptions, linkDialog } from './surfaces/link-dialog.js'\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAML,cAAc,GACf,MAAM,aAAa,CAAA;AAEpB,OAAO,EAUL,YAAY,EACZ,UAAU,EACV,IAAI,EACJ,MAAM,EACN,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EAKN,MAAM,oBAAoB,CAAA;AAE3B,OAAO,EAKL,cAAc,GACf,MAAM,iBAAiB,CAAA;AAExB,OAAO,EAA0B,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAGL,iBAAiB,GAClB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAA0B,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAIL,aAAa,EACb,cAAc,GACf,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAE,QAAQ,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAA;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAA;AACrE,OAAO,EAA0B,UAAU,EAAE,MAAM,mBAAmB,CAAA;AACtE,OAAO,EAA6B,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC/E,OAAO,EAA2C,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAC7F,OAAO,EAA2B,aAAa,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACxF,OAAO,EAA2B,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEhD,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAE9E,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACnE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,6BAA6B,EAC7B,YAAY,EACZ,UAAU,GACX,MAAM,YAAY,CAAA;AAEnB,OAAO,EAIL,cAAc,EACd,cAAc,EACd,OAAO,GACR,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAA0B,UAAU,EAAE,MAAM,2BAA2B,CAAA","sourcesContent":["// `@llui/markdown-editor` — WYSIWYG Markdown editor for LLui, built on Lexical.\n\nexport {\n type EditorConfig,\n type EditorParts,\n type CollabBinding,\n type CollabHooks,\n type CollabFactory,\n markdownEditor,\n} from './editor.js'\n\nexport {\n type BlockType,\n type FormatState,\n type OverlayKind,\n type CollabStatus,\n type EditorState,\n type EditorMsg,\n type EditorOutMsg,\n type EditorEffect,\n type InitOptions,\n EMPTY_FORMAT,\n COLLAB_OFF,\n init,\n update,\n countWords,\n} from './state.js'\n\nexport {\n type ItemSurface,\n type CommandItem,\n type CommandContext,\n type MarkdownPlugin,\n} from './plugins/types.js'\n\nexport {\n type PluginUI,\n type PluginUISpec,\n type PluginViewArgs,\n type PluginEffectContext,\n definePluginUI,\n} from './plugins/ui.js'\n\nexport { type CorePluginOptions, corePlugin } from './plugins/core.js'\nexport {\n type InlineFormat,\n type SingleBlockPluginOptions,\n singleBlockPlugin,\n} from './plugins/single-block.js'\nexport { type LinkPluginOptions, linkPlugin } from './plugins/link.js'\nexport {\n type CalloutKind,\n type CalloutData,\n type CalloutPluginOptions,\n calloutPlugin,\n $insertCallout,\n} from './plugins/callout.js'\nexport { hrPlugin, $insertHorizontalRule } from './plugins/hr.js'\nexport { slashPlugin } from './plugins/slash.js'\nexport { contextMenuPlugin } from './plugins/context-menu.js'\nexport { floatingToolbarPlugin } from './plugins/floating-toolbar.js'\nexport { type MathPluginOptions, mathPlugin } from './plugins/math.js'\nexport { type MermaidPluginOptions, mermaidPlugin } from './plugins/mermaid.js'\nexport { type Mention, type MentionPluginOptions, mentionPlugin } from './plugins/mention.js'\nexport { type EmojiPluginOptions, DEFAULT_EMOJI, emojiPlugin } from './plugins/emoji.js'\nexport { type ImagePluginOptions, imagePlugin } from './plugins/image.js'\nexport { tablePlugin } from './plugins/table.js'\n\nexport { $insertMarkdownAtSelection, registerMarkdownPaste } from './paste.js'\n\nexport { GFM_NODES, GFM_TRANSFORMERS } from './transformers/gfm.js'\nexport { buildTransformers, orderTransformers } from './transformers/registry.js'\n\nexport { computeFormatState } from './format.js'\n\nexport {\n STRIKETHROUGH_CLASS,\n UNDERLINE_CLASS,\n UNDERLINE_STRIKETHROUGH_CLASS,\n defaultTheme,\n mergeTheme,\n} from './theme.js'\n\nexport {\n type ToolbarItemParts,\n type ToolbarParts,\n type ToolbarOptions,\n DEFAULT_GLYPHS,\n connectToolbar,\n toolbar,\n} from './surfaces/toolbar.js'\n\nexport { type LinkDialogOptions, linkDialog } from './surfaces/link-dialog.js'\n"]}
|
package/dist/paste.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type LexicalEditor } from 'lexical';
|
|
2
|
+
import { type Transformer } from '@lexical/markdown';
|
|
3
|
+
/**
|
|
4
|
+
* Parse `markdown` with `transformers` and insert the produced nodes at the
|
|
5
|
+
* current range selection. The document is parsed into a detached scratch
|
|
6
|
+
* container (so the live root is never cleared) and the selection captured
|
|
7
|
+
* before the import — which moves the caret into the scratch node — is restored
|
|
8
|
+
* before the nodes are spliced in. Returns `true` only when nodes were actually
|
|
9
|
+
* inserted; `false` (a no-op) when there is no range selection to insert into or
|
|
10
|
+
* the markdown parsed to nothing — letting the caller fall back to the default
|
|
11
|
+
* paste behaviour instead of silently swallowing the event.
|
|
12
|
+
*/
|
|
13
|
+
export declare function $insertMarkdownAtSelection(markdown: string, transformers: Array<Transformer>): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Register the markdown-on-paste handler on `editor`. Returns a disposer.
|
|
16
|
+
*
|
|
17
|
+
* Plain-text pastes are converted as Markdown. Pastes that also carry
|
|
18
|
+
* `text/html` are ignored so Lexical's richer HTML import handles them.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerMarkdownPaste(editor: LexicalEditor, transformers: Array<Transformer>): () => void;
|
|
21
|
+
//# sourceMappingURL=paste.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paste.d.ts","sourceRoot":"","sources":["../src/paste.ts"],"names":[],"mappings":"AAMA,OAAO,EAUL,KAAK,aAAa,EACnB,MAAM,SAAS,CAAA;AAChB,OAAO,EAA8B,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAEhF;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,GAC/B,OAAO,CAiBT;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,aAAa,EACrB,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,GAC/B,MAAM,IAAI,CA6BZ"}
|
package/dist/paste.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
// Markdown-on-paste: when the user pastes plain text into the editor, parse it
|
|
2
|
+
// as Markdown and insert the resulting rich nodes at the caret instead of the
|
|
3
|
+
// literal source. Rich pastes (clipboards carrying `text/html`) are left to
|
|
4
|
+
// Lexical's own HTML import so copy-from-the-web keeps its formatting. Disabled
|
|
5
|
+
// per-editor via the `pasteMarkdown: false` config option.
|
|
6
|
+
import { $createParagraphNode, $getSelection, $insertNodes, $isRangeSelection, $setSelection, COMMAND_PRIORITY_HIGH, PASTE_COMMAND, isDOMNode, isSelectionCapturedInDecoratorInput, } from 'lexical';
|
|
7
|
+
import { $convertFromMarkdownString } from '@lexical/markdown';
|
|
8
|
+
/**
|
|
9
|
+
* Parse `markdown` with `transformers` and insert the produced nodes at the
|
|
10
|
+
* current range selection. The document is parsed into a detached scratch
|
|
11
|
+
* container (so the live root is never cleared) and the selection captured
|
|
12
|
+
* before the import — which moves the caret into the scratch node — is restored
|
|
13
|
+
* before the nodes are spliced in. Returns `true` only when nodes were actually
|
|
14
|
+
* inserted; `false` (a no-op) when there is no range selection to insert into or
|
|
15
|
+
* the markdown parsed to nothing — letting the caller fall back to the default
|
|
16
|
+
* paste behaviour instead of silently swallowing the event.
|
|
17
|
+
*/
|
|
18
|
+
export function $insertMarkdownAtSelection(markdown, transformers) {
|
|
19
|
+
const selection = $getSelection();
|
|
20
|
+
if (!$isRangeSelection(selection))
|
|
21
|
+
return false;
|
|
22
|
+
const saved = selection.clone();
|
|
23
|
+
// Import into a detached element so `$convertFromMarkdownString`'s `root.clear()`
|
|
24
|
+
// touches the scratch node, not the live document.
|
|
25
|
+
const scratch = $createParagraphNode();
|
|
26
|
+
$convertFromMarkdownString(markdown, transformers, scratch);
|
|
27
|
+
const nodes = scratch.getChildren();
|
|
28
|
+
if (nodes.length === 0)
|
|
29
|
+
return false;
|
|
30
|
+
// The import re-homes the selection to the scratch node's start; restore the
|
|
31
|
+
// real caret, then splice the parsed nodes in (splitting blocks as needed).
|
|
32
|
+
$setSelection(saved);
|
|
33
|
+
$insertNodes(nodes);
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Register the markdown-on-paste handler on `editor`. Returns a disposer.
|
|
38
|
+
*
|
|
39
|
+
* Plain-text pastes are converted as Markdown. Pastes that also carry
|
|
40
|
+
* `text/html` are ignored so Lexical's richer HTML import handles them.
|
|
41
|
+
*/
|
|
42
|
+
export function registerMarkdownPaste(editor, transformers) {
|
|
43
|
+
return editor.registerCommand(PASTE_COMMAND, (event) => {
|
|
44
|
+
// Let native inputs inside a DecoratorNode paste themselves (mirrors
|
|
45
|
+
// Lexical's own rich-text paste guard).
|
|
46
|
+
if (isDOMNode(event.target) && isSelectionCapturedInDecoratorInput(event.target))
|
|
47
|
+
return false;
|
|
48
|
+
const clipboard = event.clipboardData;
|
|
49
|
+
if (!clipboard)
|
|
50
|
+
return false;
|
|
51
|
+
// Defer to Lexical's HTML import when the source provides rich content.
|
|
52
|
+
if (clipboard.types.includes('text/html'))
|
|
53
|
+
return false;
|
|
54
|
+
const text = clipboard.getData('text/plain');
|
|
55
|
+
if (!text)
|
|
56
|
+
return false;
|
|
57
|
+
// Command listeners run inside a read context, so we can inspect the
|
|
58
|
+
// selection synchronously. Only claim the event when there is a range
|
|
59
|
+
// selection to insert into; otherwise fall through to Lexical's default
|
|
60
|
+
// paste handler (e.g. a selected decorator/NodeSelection) rather than
|
|
61
|
+
// swallowing the paste.
|
|
62
|
+
if (!$isRangeSelection($getSelection()))
|
|
63
|
+
return false;
|
|
64
|
+
event.preventDefault();
|
|
65
|
+
editor.update(() => {
|
|
66
|
+
$insertMarkdownAtSelection(text, transformers);
|
|
67
|
+
});
|
|
68
|
+
return true;
|
|
69
|
+
}, COMMAND_PRIORITY_HIGH);
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=paste.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paste.js","sourceRoot":"","sources":["../src/paste.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,gFAAgF;AAChF,2DAA2D;AAE3D,OAAO,EACL,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,SAAS,EACT,mCAAmC,GAEpC,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,0BAA0B,EAAoB,MAAM,mBAAmB,CAAA;AAEhF;;;;;;;;;GASG;AACH,MAAM,UAAU,0BAA0B,CACxC,QAAgB,EAChB,YAAgC;IAEhC,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;IACjC,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC;QAAE,OAAO,KAAK,CAAA;IAC/C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE,CAAA;IAE/B,kFAAkF;IAClF,mDAAmD;IACnD,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAA;IACtC,0BAA0B,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,CAAC,CAAA;IAC3D,MAAM,KAAK,GAAG,OAAO,CAAC,WAAW,EAAE,CAAA;IACnC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAEpC,6EAA6E;IAC7E,4EAA4E;IAC5E,aAAa,CAAC,KAAK,CAAC,CAAA;IACpB,YAAY,CAAC,KAAK,CAAC,CAAA;IACnB,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAqB,EACrB,YAAgC;IAEhC,OAAO,MAAM,CAAC,eAAe,CAC3B,aAAa,EACb,CAAC,KAAqB,EAAE,EAAE;QACxB,qEAAqE;QACrE,wCAAwC;QACxC,IAAI,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,mCAAmC,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,OAAO,KAAK,CAAA;QAC9F,MAAM,SAAS,GAAG,KAAK,CAAC,aAAa,CAAA;QACrC,IAAI,CAAC,SAAS;YAAE,OAAO,KAAK,CAAA;QAC5B,wEAAwE;QACxE,IAAI,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,WAAW,CAAC;YAAE,OAAO,KAAK,CAAA;QACvD,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,YAAY,CAAC,CAAA;QAC5C,IAAI,CAAC,IAAI;YAAE,OAAO,KAAK,CAAA;QAEvB,qEAAqE;QACrE,sEAAsE;QACtE,wEAAwE;QACxE,sEAAsE;QACtE,wBAAwB;QACxB,IAAI,CAAC,iBAAiB,CAAC,aAAa,EAAE,CAAC;YAAE,OAAO,KAAK,CAAA;QAErD,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE;YACjB,0BAA0B,CAAC,IAAI,EAAE,YAAY,CAAC,CAAA;QAChD,CAAC,CAAC,CAAA;QACF,OAAO,IAAI,CAAA;IACb,CAAC,EACD,qBAAqB,CACtB,CAAA;AACH,CAAC","sourcesContent":["// Markdown-on-paste: when the user pastes plain text into the editor, parse it\n// as Markdown and insert the resulting rich nodes at the caret instead of the\n// literal source. Rich pastes (clipboards carrying `text/html`) are left to\n// Lexical's own HTML import so copy-from-the-web keeps its formatting. Disabled\n// per-editor via the `pasteMarkdown: false` config option.\n\nimport {\n $createParagraphNode,\n $getSelection,\n $insertNodes,\n $isRangeSelection,\n $setSelection,\n COMMAND_PRIORITY_HIGH,\n PASTE_COMMAND,\n isDOMNode,\n isSelectionCapturedInDecoratorInput,\n type LexicalEditor,\n} from 'lexical'\nimport { $convertFromMarkdownString, type Transformer } from '@lexical/markdown'\n\n/**\n * Parse `markdown` with `transformers` and insert the produced nodes at the\n * current range selection. The document is parsed into a detached scratch\n * container (so the live root is never cleared) and the selection captured\n * before the import — which moves the caret into the scratch node — is restored\n * before the nodes are spliced in. Returns `true` only when nodes were actually\n * inserted; `false` (a no-op) when there is no range selection to insert into or\n * the markdown parsed to nothing — letting the caller fall back to the default\n * paste behaviour instead of silently swallowing the event.\n */\nexport function $insertMarkdownAtSelection(\n markdown: string,\n transformers: Array<Transformer>,\n): boolean {\n const selection = $getSelection()\n if (!$isRangeSelection(selection)) return false\n const saved = selection.clone()\n\n // Import into a detached element so `$convertFromMarkdownString`'s `root.clear()`\n // touches the scratch node, not the live document.\n const scratch = $createParagraphNode()\n $convertFromMarkdownString(markdown, transformers, scratch)\n const nodes = scratch.getChildren()\n if (nodes.length === 0) return false\n\n // The import re-homes the selection to the scratch node's start; restore the\n // real caret, then splice the parsed nodes in (splitting blocks as needed).\n $setSelection(saved)\n $insertNodes(nodes)\n return true\n}\n\n/**\n * Register the markdown-on-paste handler on `editor`. Returns a disposer.\n *\n * Plain-text pastes are converted as Markdown. Pastes that also carry\n * `text/html` are ignored so Lexical's richer HTML import handles them.\n */\nexport function registerMarkdownPaste(\n editor: LexicalEditor,\n transformers: Array<Transformer>,\n): () => void {\n return editor.registerCommand(\n PASTE_COMMAND,\n (event: ClipboardEvent) => {\n // Let native inputs inside a DecoratorNode paste themselves (mirrors\n // Lexical's own rich-text paste guard).\n if (isDOMNode(event.target) && isSelectionCapturedInDecoratorInput(event.target)) return false\n const clipboard = event.clipboardData\n if (!clipboard) return false\n // Defer to Lexical's HTML import when the source provides rich content.\n if (clipboard.types.includes('text/html')) return false\n const text = clipboard.getData('text/plain')\n if (!text) return false\n\n // Command listeners run inside a read context, so we can inspect the\n // selection synchronously. Only claim the event when there is a range\n // selection to insert into; otherwise fall through to Lexical's default\n // paste handler (e.g. a selected decorator/NodeSelection) rather than\n // swallowing the paste.\n if (!$isRangeSelection($getSelection())) return false\n\n event.preventDefault()\n editor.update(() => {\n $insertMarkdownAtSelection(text, transformers)\n })\n return true\n },\n COMMAND_PRIORITY_HIGH,\n )\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Mountable, type Signal } from '@llui/dom';
|
|
2
|
+
/** A preview renderer: source string → safe DOM node, or trusted HTML string. */
|
|
3
|
+
export type PreviewRender = (source: string) => string | Node;
|
|
4
|
+
/**
|
|
5
|
+
* Build a reactive `data-part="preview"` region that re-renders whenever
|
|
6
|
+
* `source` changes. String results are set as trusted HTML; Node results
|
|
7
|
+
* are mounted directly (no sanitization). See the module header.
|
|
8
|
+
*/
|
|
9
|
+
export declare function renderedPreview(source: Signal<string>, render: PreviewRender, tag?: string): Mountable;
|
|
10
|
+
//# sourceMappingURL=_preview.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_preview.d.ts","sourceRoot":"","sources":["../../src/plugins/_preview.ts"],"names":[],"mappings":"AAcA,OAAO,EAAW,KAAK,SAAS,EAAE,KAAK,MAAM,EAAE,MAAM,WAAW,CAAA;AAEhE,iFAAiF;AACjF,MAAM,MAAM,aAAa,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,CAAA;AAM7D;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EACtB,MAAM,EAAE,aAAa,EACrB,GAAG,SAAQ,GACV,SAAS,CAkBX"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// Shared rendered-preview seam for the math / mermaid decorator plugins.
|
|
2
|
+
//
|
|
3
|
+
// SECURITY: a consumer-supplied `render` turns editor-document content
|
|
4
|
+
// (TeX / mermaid source the user typed) into a preview. To avoid forcing
|
|
5
|
+
// that through an unsanitized HTML sink, `render` may return EITHER:
|
|
6
|
+
//
|
|
7
|
+
// - a DOM `Node` — mounted directly with no HTML round-trip and NO
|
|
8
|
+
// sanitization needed (prefer this: hand back the element KaTeX /
|
|
9
|
+
// mermaid already produced); or
|
|
10
|
+
// - a `string` — injected as **trusted HTML**. The renderer owns
|
|
11
|
+
// sanitization in this branch; document content flows into it, so a
|
|
12
|
+
// vulnerable/misconfigured renderer is an XSS sink. Return sanitized
|
|
13
|
+
// markup (e.g. via DOMPurify) or prefer the Node form.
|
|
14
|
+
import { foreign } from '@llui/dom';
|
|
15
|
+
/**
|
|
16
|
+
* Build a reactive `data-part="preview"` region that re-renders whenever
|
|
17
|
+
* `source` changes. String results are set as trusted HTML; Node results
|
|
18
|
+
* are mounted directly (no sanitization). See the module header.
|
|
19
|
+
*/
|
|
20
|
+
export function renderedPreview(source, render, tag = 'div') {
|
|
21
|
+
return foreign({
|
|
22
|
+
tag,
|
|
23
|
+
state: { source },
|
|
24
|
+
mount: ({ el, state }) => {
|
|
25
|
+
el.setAttribute('data-part', 'preview');
|
|
26
|
+
el.setAttribute('contenteditable', 'false');
|
|
27
|
+
const apply = (value) => {
|
|
28
|
+
const out = render(value);
|
|
29
|
+
if (typeof out === 'string')
|
|
30
|
+
el.innerHTML = out;
|
|
31
|
+
else
|
|
32
|
+
el.replaceChildren(out);
|
|
33
|
+
};
|
|
34
|
+
// `bind` fires immediately with the current value, then on change.
|
|
35
|
+
const unbind = state.source.bind(apply);
|
|
36
|
+
return { unbind };
|
|
37
|
+
},
|
|
38
|
+
unmount: (instance) => instance.unbind(),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=_preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_preview.js","sourceRoot":"","sources":["../../src/plugins/_preview.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,qEAAqE;AACrE,EAAE;AACF,qEAAqE;AACrE,sEAAsE;AACtE,oCAAoC;AACpC,mEAAmE;AACnE,wEAAwE;AACxE,yEAAyE;AACzE,2DAA2D;AAE3D,OAAO,EAAE,OAAO,EAA+B,MAAM,WAAW,CAAA;AAShE;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAsB,EACtB,MAAqB,EACrB,GAAG,GAAG,KAAK;IAEX,OAAO,OAAO,CAA8C;QAC1D,GAAG;QACH,KAAK,EAAE,EAAE,MAAM,EAAE;QACjB,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YACvB,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,SAAS,CAAC,CAAA;YACvC,EAAE,CAAC,YAAY,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAA;YAC3C,MAAM,KAAK,GAAG,CAAC,KAAa,EAAQ,EAAE;gBACpC,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;gBACzB,IAAI,OAAO,GAAG,KAAK,QAAQ;oBAAE,EAAE,CAAC,SAAS,GAAG,GAAG,CAAA;;oBAC1C,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,CAAA;YAC9B,CAAC,CAAA;YACD,mEAAmE;YACnE,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACvC,OAAO,EAAE,MAAM,EAAE,CAAA;QACnB,CAAC;QACD,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE;KACzC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["// Shared rendered-preview seam for the math / mermaid decorator plugins.\n//\n// SECURITY: a consumer-supplied `render` turns editor-document content\n// (TeX / mermaid source the user typed) into a preview. To avoid forcing\n// that through an unsanitized HTML sink, `render` may return EITHER:\n//\n// - a DOM `Node` — mounted directly with no HTML round-trip and NO\n// sanitization needed (prefer this: hand back the element KaTeX /\n// mermaid already produced); or\n// - a `string` — injected as **trusted HTML**. The renderer owns\n// sanitization in this branch; document content flows into it, so a\n// vulnerable/misconfigured renderer is an XSS sink. Return sanitized\n// markup (e.g. via DOMPurify) or prefer the Node form.\n\nimport { foreign, type Mountable, type Signal } from '@llui/dom'\n\n/** A preview renderer: source string → safe DOM node, or trusted HTML string. */\nexport type PreviewRender = (source: string) => string | Node\n\ninterface PreviewInstance {\n unbind: () => void\n}\n\n/**\n * Build a reactive `data-part=\"preview\"` region that re-renders whenever\n * `source` changes. String results are set as trusted HTML; Node results\n * are mounted directly (no sanitization). See the module header.\n */\nexport function renderedPreview(\n source: Signal<string>,\n render: PreviewRender,\n tag = 'div',\n): Mountable {\n return foreign<PreviewInstance, { source: Signal<string> }>({\n tag,\n state: { source },\n mount: ({ el, state }) => {\n el.setAttribute('data-part', 'preview')\n el.setAttribute('contenteditable', 'false')\n const apply = (value: string): void => {\n const out = render(value)\n if (typeof out === 'string') el.innerHTML = out\n else el.replaceChildren(out)\n }\n // `bind` fires immediately with the current value, then on change.\n const unbind = state.source.bind(apply)\n return { unbind }\n },\n unmount: (instance) => instance.unbind(),\n })\n}\n"]}
|
package/dist/plugins/math.d.ts
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { MarkdownPlugin } from './types.js';
|
|
2
|
+
import { type PreviewRender } from './_preview.js';
|
|
2
3
|
export interface MathPluginOptions {
|
|
3
4
|
/** Typeset TeX to an HTML string (e.g. via KaTeX). When omitted, the raw TeX is
|
|
4
5
|
* shown in a styled box. */
|
|
5
|
-
|
|
6
|
+
/** Render the TeX source to a preview. Return a DOM `Node` (mounted
|
|
7
|
+
* directly, no sanitization) or a **trusted HTML string** (injected as-is
|
|
8
|
+
* — sanitize it yourself, e.g. via DOMPurify, since it carries document
|
|
9
|
+
* content). See `renderedPreview`. */
|
|
10
|
+
render?: PreviewRender;
|
|
6
11
|
}
|
|
7
12
|
export declare function mathPlugin(opts?: MathPluginOptions): MarkdownPlugin;
|
|
8
13
|
//# sourceMappingURL=math.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../src/plugins/math.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"math.d.ts","sourceRoot":"","sources":["../../src/plugins/math.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAgBnE,MAAM,WAAW,iBAAiB;IAChC;gCAC4B;IAC5B;;;0CAGsC;IACtC,MAAM,CAAC,EAAE,aAAa,CAAA;CACvB;AAED,wBAAgB,UAAU,CAAC,IAAI,GAAE,iBAAsB,GAAG,cAAc,CA0EvE"}
|
package/dist/plugins/math.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
import {} from 'lexical';
|
|
5
5
|
import { $insertNodeToNearestRoot } from '@lexical/utils';
|
|
6
6
|
import { $createLLuiDecoratorNode, $isLLuiDecoratorNode, LLuiDecoratorNode, decoratorBridge, } from '@llui/lexical';
|
|
7
|
-
import { component, div, span, text
|
|
7
|
+
import { component, div, span, text } from '@llui/dom';
|
|
8
|
+
import { renderedPreview } from './_preview.js';
|
|
8
9
|
const BRIDGE_TYPE = 'math';
|
|
9
10
|
function isMathData(value) {
|
|
10
11
|
return typeof value === 'object' && value !== null && typeof value.tex === 'string';
|
|
@@ -37,9 +38,7 @@ export function mathPlugin(opts = {}) {
|
|
|
37
38
|
}, [text(state.at('tex'))]),
|
|
38
39
|
];
|
|
39
40
|
if (opts.render) {
|
|
40
|
-
children.push(
|
|
41
|
-
unsafeHtml(state.at('tex').map((tex) => opts.render(tex))),
|
|
42
|
-
]));
|
|
41
|
+
children.push(renderedPreview(state.at('tex'), opts.render, 'span'));
|
|
43
42
|
}
|
|
44
43
|
return [
|
|
45
44
|
div({ 'data-scope': 'md-math', 'data-part': 'root', contenteditable: 'false' }, children),
|
package/dist/plugins/math.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/plugins/math.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,kFAAkF;AAClF,0EAA0E;AAE1E,OAAO,EAAsC,MAAM,SAAS,CAAA;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAEzD,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"math.js","sourceRoot":"","sources":["../../src/plugins/math.ts"],"names":[],"mappings":"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","sourcesContent":["// Math plugin — a block math node ($$…$$) rendered via the decorator bridge. The\n// TeX source is editable inline (persists on blur); an optional `render` callback\n// (e.g. KaTeX) turns it into a typeset preview. Round-trips to `$$tex$$`.\n\nimport { type ElementNode, type LexicalNode } from 'lexical'\nimport { $insertNodeToNearestRoot } from '@lexical/utils'\nimport type { ElementTransformer } from '@lexical/markdown'\nimport {\n $createLLuiDecoratorNode,\n $isLLuiDecoratorNode,\n LLuiDecoratorNode,\n decoratorBridge,\n} from '@llui/lexical'\nimport { component, div, span, text, type Mountable, type Signal } from '@llui/dom'\nimport type { MarkdownPlugin } from './types.js'\nimport { renderedPreview, type PreviewRender } from './_preview.js'\n\nconst BRIDGE_TYPE = 'math'\n\ninterface MathData {\n tex: string\n}\n\nfunction isMathData(value: unknown): value is MathData {\n return typeof value === 'object' && value !== null && typeof (value as MathData).tex === 'string'\n}\n\ntype MathMsg = { type: 'commit'; tex: string }\n\nconst stop = (e: Event): void => e.stopPropagation()\n\nexport interface MathPluginOptions {\n /** Typeset TeX to an HTML string (e.g. via KaTeX). When omitted, the raw TeX is\n * shown in a styled box. */\n /** Render the TeX source to a preview. Return a DOM `Node` (mounted\n * directly, no sanitization) or a **trusted HTML string** (injected as-is\n * — sanitize it yourself, e.g. via DOMPurify, since it carries document\n * content). See `renderedPreview`. */\n render?: PreviewRender\n}\n\nexport function mathPlugin(opts: MathPluginOptions = {}): MarkdownPlugin {\n const bridge = decoratorBridge<MathData, MathData, MathMsg, never>(BRIDGE_TYPE, (data, api) =>\n component<MathData, MathMsg, never>({\n name: 'Math',\n init: () => ({ tex: data.tex }),\n update: (state, msg) => {\n if (msg.type === 'commit') {\n if (msg.tex === state.tex) return state\n api.update({ tex: msg.tex })\n return { tex: msg.tex }\n }\n return state\n },\n view: ({ state, send }) => {\n const children: Mountable[] = [\n span(\n {\n 'data-part': 'source',\n contenteditable: 'true',\n role: 'textbox',\n 'aria-label': 'TeX source',\n onKeyDown: stop,\n onBeforeInput: stop,\n onPaste: stop,\n onBlur: (e: FocusEvent) =>\n send({ type: 'commit', tex: (e.target as HTMLElement).textContent ?? '' }),\n },\n [text(state.at('tex') as Signal<string>)],\n ),\n ]\n if (opts.render) {\n children.push(renderedPreview(state.at('tex') as Signal<string>, opts.render, 'span'))\n }\n return [\n div({ 'data-scope': 'md-math', 'data-part': 'root', contenteditable: 'false' }, children),\n ]\n },\n }),\n )\n\n const transformer: ElementTransformer = {\n dependencies: [LLuiDecoratorNode],\n export: (node: LexicalNode): string | null => {\n if (!$isLLuiDecoratorNode(node) || node.getBridgeType() !== BRIDGE_TYPE) return null\n const data = node.getData()\n return isMathData(data) ? `$$${data.tex}$$` : null\n },\n regExp: /^\\$\\$(.+)\\$\\$$/,\n replace: (parentNode: ElementNode, _children, match): void => {\n parentNode.replace($createLLuiDecoratorNode(BRIDGE_TYPE, { tex: match[1] ?? '' }))\n },\n type: 'element',\n }\n\n return {\n name: 'math',\n nodes: [LLuiDecoratorNode],\n decorators: [bridge],\n transformers: [transformer],\n items: [\n {\n id: 'math',\n label: 'Math block',\n icon: 'math',\n group: 'insert',\n keywords: ['latex', 'tex', 'equation', 'formula'],\n run: (editor) =>\n editor.update(() =>\n $insertNodeToNearestRoot($createLLuiDecoratorNode(BRIDGE_TYPE, { tex: 'e = mc^2' })),\n ),\n surfaces: ['slash', 'context'],\n },\n ],\n }\n}\n"]}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import type { MarkdownPlugin } from './types.js';
|
|
2
|
+
import { type PreviewRender } from './_preview.js';
|
|
2
3
|
export interface MermaidPluginOptions {
|
|
3
4
|
/** Render the diagram source to an HTML string (e.g. mermaid). When omitted,
|
|
4
5
|
* the raw source is shown in a styled box. */
|
|
5
|
-
|
|
6
|
+
/** Render the mermaid source to a preview. Return a DOM `Node` (mounted
|
|
7
|
+
* directly, no sanitization) or a **trusted HTML string** (injected as-is
|
|
8
|
+
* — sanitize it yourself, e.g. via DOMPurify, since it carries document
|
|
9
|
+
* content). See `renderedPreview`. */
|
|
10
|
+
render?: PreviewRender;
|
|
6
11
|
}
|
|
7
12
|
export declare function mermaidPlugin(opts?: MermaidPluginOptions): MarkdownPlugin;
|
|
8
13
|
//# sourceMappingURL=mermaid.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../src/plugins/mermaid.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../../src/plugins/mermaid.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAChD,OAAO,EAAmB,KAAK,aAAa,EAAE,MAAM,eAAe,CAAA;AAkBnE,MAAM,WAAW,oBAAoB;IACnC;kDAC8C;IAC9C;;;0CAGsC;IACtC,MAAM,CAAC,EAAE,aAAa,CAAA;CACvB;AAED,wBAAgB,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,cAAc,CAsF7E"}
|
package/dist/plugins/mermaid.js
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
import {} from 'lexical';
|
|
9
9
|
import { $insertNodeToNearestRoot } from '@lexical/utils';
|
|
10
10
|
import { $createLLuiDecoratorNode, $isLLuiDecoratorNode, LLuiDecoratorNode, decoratorBridge, } from '@llui/lexical';
|
|
11
|
-
import { component, div, text
|
|
11
|
+
import { component, div, text } from '@llui/dom';
|
|
12
|
+
import { renderedPreview } from './_preview.js';
|
|
12
13
|
const BRIDGE_TYPE = 'mermaid';
|
|
13
14
|
function isMermaidData(value) {
|
|
14
15
|
return (typeof value === 'object' && value !== null && typeof value.code === 'string');
|
|
@@ -41,9 +42,7 @@ export function mermaidPlugin(opts = {}) {
|
|
|
41
42
|
}, [text(state.at('code'))]),
|
|
42
43
|
];
|
|
43
44
|
if (opts.render) {
|
|
44
|
-
children.push(
|
|
45
|
-
unsafeHtml(state.at('code').map((code) => opts.render(code))),
|
|
46
|
-
]));
|
|
45
|
+
children.push(renderedPreview(state.at('code'), opts.render));
|
|
47
46
|
}
|
|
48
47
|
return [
|
|
49
48
|
div({ 'data-scope': 'md-mermaid', 'data-part': 'root', contenteditable: 'false' }, children),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mermaid.js","sourceRoot":"","sources":["../../src/plugins/mermaid.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,mFAAmF;AACnF,+EAA+E;AAC/E,SAAS;AACT,EAAE;AACF,kFAAkF;AAClF,8EAA8E;AAE9E,OAAO,EAAoB,MAAM,SAAS,CAAA;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAEzD,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,iBAAiB,EACjB,eAAe,GAChB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,SAAS,EAAE,GAAG,EAAE,IAAI,
|
|
1
|
+
{"version":3,"file":"mermaid.js","sourceRoot":"","sources":["../../src/plugins/mermaid.ts"],"names":[],"mappings":"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","sourcesContent":["// Mermaid plugin — a fenced ```mermaid block rendered via the decorator bridge.\n// The diagram source is editable (persists on blur); an optional `render` callback\n// (e.g. the mermaid library) produces the diagram. Round-trips to a ```mermaid\n// fence.\n//\n// NOTE: place `mermaidPlugin()` BEFORE `corePlugin()` in the plugins array so its\n// multiline transformer matches ```mermaid before the generic code-block one.\n\nimport { type LexicalNode } from 'lexical'\nimport { $insertNodeToNearestRoot } from '@lexical/utils'\nimport type { MultilineElementTransformer } from '@lexical/markdown'\nimport {\n $createLLuiDecoratorNode,\n $isLLuiDecoratorNode,\n LLuiDecoratorNode,\n decoratorBridge,\n} from '@llui/lexical'\nimport { component, div, text, type Mountable, type Signal } from '@llui/dom'\nimport type { MarkdownPlugin } from './types.js'\nimport { renderedPreview, type PreviewRender } from './_preview.js'\n\nconst BRIDGE_TYPE = 'mermaid'\n\ninterface MermaidData {\n code: string\n}\n\nfunction isMermaidData(value: unknown): value is MermaidData {\n return (\n typeof value === 'object' && value !== null && typeof (value as MermaidData).code === 'string'\n )\n}\n\ntype MermaidMsg = { type: 'commit'; code: string }\n\nconst stop = (e: Event): void => e.stopPropagation()\n\nexport interface MermaidPluginOptions {\n /** Render the diagram source to an HTML string (e.g. mermaid). When omitted,\n * the raw source is shown in a styled box. */\n /** Render the mermaid source to a preview. Return a DOM `Node` (mounted\n * directly, no sanitization) or a **trusted HTML string** (injected as-is\n * — sanitize it yourself, e.g. via DOMPurify, since it carries document\n * content). See `renderedPreview`. */\n render?: PreviewRender\n}\n\nexport function mermaidPlugin(opts: MermaidPluginOptions = {}): MarkdownPlugin {\n const bridge = decoratorBridge<MermaidData, MermaidData, MermaidMsg, never>(\n BRIDGE_TYPE,\n (data, api) =>\n component<MermaidData, MermaidMsg, never>({\n name: 'Mermaid',\n init: () => ({ code: data.code }),\n update: (state, msg) => {\n if (msg.type === 'commit') {\n if (msg.code === state.code) return state\n api.update({ code: msg.code })\n return { code: msg.code }\n }\n return state\n },\n view: ({ state, send }) => {\n const children: Mountable[] = [\n div(\n {\n 'data-part': 'source',\n contenteditable: 'true',\n role: 'textbox',\n 'aria-label': 'Mermaid source',\n onKeyDown: stop,\n onBeforeInput: stop,\n onPaste: stop,\n onBlur: (e: FocusEvent) =>\n send({ type: 'commit', code: (e.target as HTMLElement).textContent ?? '' }),\n },\n [text(state.at('code') as Signal<string>)],\n ),\n ]\n if (opts.render) {\n children.push(renderedPreview(state.at('code') as Signal<string>, opts.render))\n }\n return [\n div(\n { 'data-scope': 'md-mermaid', 'data-part': 'root', contenteditable: 'false' },\n children,\n ),\n ]\n },\n }),\n )\n\n const transformer: MultilineElementTransformer = {\n dependencies: [LLuiDecoratorNode],\n export: (node: LexicalNode): string | null => {\n if (!$isLLuiDecoratorNode(node) || node.getBridgeType() !== BRIDGE_TYPE) return null\n const data = node.getData()\n return isMermaidData(data) ? '```mermaid\\n' + data.code + '\\n```' : null\n },\n regExpStart: /^```mermaid$/,\n regExpEnd: /^```$/,\n replace: (rootNode, _children, _startMatch, _endMatch, linesInBetween): boolean => {\n const lines = [...(linesInBetween ?? [])]\n while (lines.length > 0 && lines[0] === '') lines.shift()\n while (lines.length > 0 && lines[lines.length - 1] === '') lines.pop()\n rootNode.append($createLLuiDecoratorNode(BRIDGE_TYPE, { code: lines.join('\\n') }))\n return true\n },\n type: 'multiline-element',\n }\n\n return {\n name: 'mermaid',\n nodes: [LLuiDecoratorNode],\n decorators: [bridge],\n transformers: [transformer],\n items: [\n {\n id: 'mermaid',\n label: 'Diagram',\n icon: 'mermaid',\n group: 'insert',\n keywords: ['mermaid', 'diagram', 'flowchart', 'graph'],\n run: (editor) =>\n editor.update(() =>\n $insertNodeToNearestRoot(\n $createLLuiDecoratorNode(BRIDGE_TYPE, { code: 'graph TD\\n A --> B' }),\n ),\n ),\n surfaces: ['slash', 'context'],\n },\n ],\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llui/markdown-editor",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@llui/dom": "^0.11.4",
|
|
28
|
-
"@llui/lexical": "^0.2.
|
|
28
|
+
"@llui/lexical": "^0.2.6",
|
|
29
29
|
"@llui/components": "^0.12.1",
|
|
30
30
|
"lexical": "^0.45.0",
|
|
31
31
|
"@lexical/markdown": "^0.45.0",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"vitest": "^4.1.2",
|
|
52
52
|
"@lexical/code-core": "^0.45.0",
|
|
53
53
|
"@llui/dom": "0.11.4",
|
|
54
|
-
"@llui/lexical": "0.2.
|
|
54
|
+
"@llui/lexical": "0.2.6",
|
|
55
55
|
"@llui/components": "0.12.1"
|
|
56
56
|
},
|
|
57
57
|
"sideEffects": [
|