@llui/markdown-editor 0.2.13 → 0.3.0

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.
Files changed (83) hide show
  1. package/dist/editor.d.ts +7 -0
  2. package/dist/editor.d.ts.map +1 -1
  3. package/dist/editor.js +66 -11
  4. package/dist/editor.js.map +1 -1
  5. package/dist/effects.d.ts +5 -4
  6. package/dist/effects.d.ts.map +1 -1
  7. package/dist/effects.js +11 -6
  8. package/dist/effects.js.map +1 -1
  9. package/dist/format.js.map +1 -1
  10. package/dist/index.d.ts +3 -3
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +3 -3
  13. package/dist/index.js.map +1 -1
  14. package/dist/paste.d.ts.map +1 -1
  15. package/dist/paste.js +5 -0
  16. package/dist/paste.js.map +1 -1
  17. package/dist/plugins/_preview.js.map +1 -1
  18. package/dist/plugins/callout.d.ts.map +1 -1
  19. package/dist/plugins/callout.js +37 -45
  20. package/dist/plugins/callout.js.map +1 -1
  21. package/dist/plugins/context-menu.js.map +1 -1
  22. package/dist/plugins/core.js.map +1 -1
  23. package/dist/plugins/emoji.js.map +1 -1
  24. package/dist/plugins/floating-toolbar.js.map +1 -1
  25. package/dist/plugins/hr.d.ts.map +1 -1
  26. package/dist/plugins/hr.js +4 -7
  27. package/dist/plugins/hr.js.map +1 -1
  28. package/dist/plugins/image.d.ts.map +1 -1
  29. package/dist/plugins/image.js +28 -20
  30. package/dist/plugins/image.js.map +1 -1
  31. package/dist/plugins/inline.js.map +1 -1
  32. package/dist/plugins/link.d.ts.map +1 -1
  33. package/dist/plugins/link.js +16 -9
  34. package/dist/plugins/link.js.map +1 -1
  35. package/dist/plugins/math.d.ts.map +1 -1
  36. package/dist/plugins/math.js +25 -34
  37. package/dist/plugins/math.js.map +1 -1
  38. package/dist/plugins/mention.js.map +1 -1
  39. package/dist/plugins/mermaid.d.ts.map +1 -1
  40. package/dist/plugins/mermaid.js +25 -34
  41. package/dist/plugins/mermaid.js.map +1 -1
  42. package/dist/plugins/overlay.js.map +1 -1
  43. package/dist/plugins/single-block.js.map +1 -1
  44. package/dist/plugins/slash.js.map +1 -1
  45. package/dist/plugins/table.d.ts.map +1 -1
  46. package/dist/plugins/table.js +84 -19
  47. package/dist/plugins/table.js.map +1 -1
  48. package/dist/plugins/types.js.map +1 -1
  49. package/dist/plugins/ui.js.map +1 -1
  50. package/dist/security.d.ts +26 -0
  51. package/dist/security.d.ts.map +1 -0
  52. package/dist/security.js +87 -0
  53. package/dist/security.js.map +1 -0
  54. package/dist/state.js.map +1 -1
  55. package/dist/styles/editor.css +0 -6
  56. package/dist/surfaces/link-dialog.js.map +1 -1
  57. package/dist/surfaces/toolbar.js.map +1 -1
  58. package/dist/theme.d.ts +2 -5
  59. package/dist/theme.d.ts.map +1 -1
  60. package/dist/theme.js +15 -11
  61. package/dist/theme.js.map +1 -1
  62. package/dist/transformers/gfm.d.ts +9 -1
  63. package/dist/transformers/gfm.d.ts.map +1 -1
  64. package/dist/transformers/gfm.js +9 -2
  65. package/dist/transformers/gfm.js.map +1 -1
  66. package/dist/transformers/registry.js.map +1 -1
  67. package/package.json +10 -8
  68. package/src/editor.ts +92 -11
  69. package/src/effects.ts +11 -7
  70. package/src/index.ts +3 -8
  71. package/src/paste.ts +5 -0
  72. package/src/plugins/callout.ts +44 -59
  73. package/src/plugins/hr.ts +4 -14
  74. package/src/plugins/image.ts +32 -29
  75. package/src/plugins/link.ts +20 -10
  76. package/src/plugins/math.ts +27 -41
  77. package/src/plugins/mermaid.ts +26 -45
  78. package/src/plugins/table.ts +94 -17
  79. package/src/security.ts +89 -0
  80. package/src/styles/editor.css +0 -6
  81. package/src/theme.ts +15 -11
  82. package/src/transformers/gfm.ts +10 -2
  83. package/dist/__llui_deps.json +0 -274
package/dist/editor.d.ts CHANGED
@@ -62,6 +62,13 @@ export interface EditorParts {
62
62
  /** Reactive format signal for `connect`-style toolbars. */
63
63
  format: Signal<FormatState>;
64
64
  }
65
+ /** Swallow the underline text-format command. `registerRichText` wires Cmd+U to
66
+ * FORMAT_TEXT 'underline', but the GFM markdown dialect this editor serializes
67
+ * has no underline representation, so an applied underline would be silently
68
+ * stripped on save. Intercepting at CRITICAL priority (ahead of rich-text) keeps
69
+ * the WYSIWYG surface and the serialized dialect in lock-step: underline can be
70
+ * neither applied nor lost. Returns a disposer. */
71
+ export declare function blockUnderlineFormat(editor: LexicalEditor): () => void;
65
72
  /**
66
73
  * Build the markdown editor component. Embed it with `mountApp(el, markdownEditor(...))`
67
74
  * or compose it inside a larger component.
@@ -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;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"}
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"AAMA,OAAO,EAKL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,SAAS,CAAA;AAMhB,OAAO,EAAmC,KAAK,MAAM,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAYjG,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;;;;;mDAKmD;AACnD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,IAAI,CAMtE;AAeD;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,GAAE,YAAiB,GACxB,kBAAkB,CAAC,WAAW,EAAE,SAAS,EAAE,YAAY,CAAC,CAwO1D"}
package/dist/editor.js CHANGED
@@ -3,13 +3,14 @@
3
3
  // surfaces the format state for the chrome, routes command intents back to the
4
4
  // live editor through effects, and COMPOSES plugin UI extensions (each plugin's
5
5
  // state slice + reducer + view + effects) into the single component.
6
- import { $getRoot, $setSelection } from 'lexical';
6
+ import { $getRoot, $setSelection, COMMAND_PRIORITY_CRITICAL, FORMAT_TEXT_COMMAND, } from 'lexical';
7
7
  import { $convertFromMarkdownString, $convertToMarkdownString, registerMarkdownShortcuts, } from '@lexical/markdown';
8
8
  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
12
  import { registerMarkdownPaste } from './paste.js';
13
+ import { registerLinkSanitizer } from './security.js';
13
14
  import { toolbar as renderToolbar } from './surfaces/toolbar.js';
14
15
  import { buildTransformers } from './transformers/registry.js';
15
16
  import { mergeTheme } from './theme.js';
@@ -20,6 +21,15 @@ import { countWords, init, update, } from './state.js';
20
21
  function defaultPlugins() {
21
22
  return [corePlugin(), linkPlugin()];
22
23
  }
24
+ /** Swallow the underline text-format command. `registerRichText` wires Cmd+U to
25
+ * FORMAT_TEXT 'underline', but the GFM markdown dialect this editor serializes
26
+ * has no underline representation, so an applied underline would be silently
27
+ * stripped on save. Intercepting at CRITICAL priority (ahead of rich-text) keeps
28
+ * the WYSIWYG surface and the serialized dialect in lock-step: underline can be
29
+ * neither applied nor lost. Returns a disposer. */
30
+ export function blockUnderlineFormat(editor) {
31
+ return editor.registerCommand(FORMAT_TEXT_COMMAND, (payload) => payload === 'underline', COMMAND_PRIORITY_CRITICAL);
32
+ }
23
33
  /**
24
34
  * Build the markdown editor component. Embed it with `mountApp(el, markdownEditor(...))`
25
35
  * or compose it inside a larger component.
@@ -37,11 +47,26 @@ export function markdownEditor(config = {}) {
37
47
  .filter((p) => p.ui !== undefined)
38
48
  .map((p) => ({ name: p.name, ui: p.ui }));
39
49
  const pluginUIByName = new Map(pluginUIs.map((p) => [p.name, p.ui]));
40
- // The live editor, captured at mount; effects dispatch through it.
41
- let editorRef = null;
42
- const getEditor = () => editorRef;
43
- const baseOnEffect = makeOnEffect(getEditor, itemsById, {
44
- onChange: config.onChange,
50
+ // Per-MOUNT live-editor context, keyed by the mount's stable `send` closure
51
+ // the SAME object the runtime hands to both `view` and `onEffect`. A single
52
+ // definition can be mounted more than once (or re-mounted); each mount gets its
53
+ // own editor reference here, so effects and plugin handlers dispatch to their
54
+ // own editor and unmount clears only that mount's reference (no cross-wiring,
55
+ // no leaked disposed editor). Entries are collected with their `send` key.
56
+ const mountContexts = new WeakMap();
57
+ const contextFor = (send) => {
58
+ let ctx = mountContexts.get(send);
59
+ if (!ctx) {
60
+ ctx = { editor: null, lastChange: undefined };
61
+ mountContexts.set(send, ctx);
62
+ }
63
+ return ctx;
64
+ };
65
+ const baseOnEffect = makeOnEffect((api) => contextFor(api.send).editor, itemsById, {
66
+ // NB: consumer `onChange` is delivered DIRECTLY from the foreign onChange
67
+ // wrapper (see `view` below), NOT through this effect path — the dispose-time
68
+ // debounce flush runs after the TEA loop is disposed, and a `send` from a
69
+ // disposed loop is dropped, so effect-routed delivery loses the final edit.
45
70
  onFormatChange: config.onFormatChange,
46
71
  applyValue: (editor, value) => editor.update(() => {
47
72
  $convertFromMarkdownString(value, transformers);
@@ -85,9 +110,10 @@ export function markdownEditor(config = {}) {
85
110
  };
86
111
  const composedOnEffect = (effect, api) => {
87
112
  if (effect.type === 'pluginEffect') {
113
+ const mount = contextFor(api.send);
88
114
  const ui = pluginUIByName.get(effect.name);
89
115
  ui?.onEffect?.(effect.effect, {
90
- editor: getEditor,
116
+ editor: () => mount.editor,
91
117
  send: (msg) => api.send({ type: 'plugin', name: effect.name, msg }),
92
118
  emit: (msg) => api.send(msg),
93
119
  });
@@ -96,6 +122,9 @@ export function markdownEditor(config = {}) {
96
122
  baseOnEffect(effect, api);
97
123
  };
98
124
  const view = ({ state, send, }) => {
125
+ // This mount's live-editor slot (keyed by this mount's `send`). `onReady`
126
+ // fills it; effects/plugins read it through `contextFor(api.send)`.
127
+ const mount = contextFor(send);
99
128
  // Build the collab binding (once, at mount) from the consumer's factory,
100
129
  // injecting the markdown seed + status sinks that mirror into `state.collab`.
101
130
  const collabBinding = config.collab
@@ -129,7 +158,17 @@ export function markdownEditor(config = {}) {
129
158
  ? { changeDebounceMs: config.changeDebounceMs }
130
159
  : {}),
131
160
  register: (editor) => {
132
- const disposers = [registerMarkdownShortcuts(editor, transformers)];
161
+ const disposers = [
162
+ // Keep the WYSIWYG surface and the serialized GFM dialect in lock-step:
163
+ // underline (Cmd+U) has no markdown representation, so it is swallowed
164
+ // rather than applied-then-silently-stripped on save.
165
+ blockUnderlineFormat(editor),
166
+ registerMarkdownShortcuts(editor, transformers),
167
+ // Global scheme allowlist for links: the single backstop covering the
168
+ // link dialog, pasted/imported markdown, and the typed `[x](url)`
169
+ // shortcut. Image src is enforced in the image transformer.
170
+ registerLinkSanitizer(editor),
171
+ ];
133
172
  if (config.pasteMarkdown !== false)
134
173
  disposers.push(registerMarkdownPaste(editor, transformers));
135
174
  if (decorators.length > 0)
@@ -139,16 +178,32 @@ export function markdownEditor(config = {}) {
139
178
  return () => {
140
179
  for (const dispose of disposers)
141
180
  dispose();
181
+ // Release this mount's editor reference so a disposed editor never
182
+ // leaks and stale effects can't reach a torn-down editor.
183
+ mount.editor = null;
142
184
  };
143
185
  },
144
186
  onReady: (editor) => {
145
- editorRef = editor;
187
+ mount.editor = editor;
146
188
  if (config.placeholder) {
147
189
  editor.getRootElement()?.setAttribute('data-placeholder', config.placeholder);
148
190
  }
149
191
  config.onReady?.(editor);
150
192
  },
151
- onChange: (value) => send({ type: 'markdownChanged', value }),
193
+ onChange: (value) => {
194
+ // Deliver to the consumer DIRECTLY, independent of the TEA loop: the
195
+ // dispose-time debounce flush runs AFTER the component is disposed, and a
196
+ // `send` from a disposed loop is dropped — so routing consumer delivery
197
+ // through `send` would lose the last debounce window of typing on unmount.
198
+ // Deduped per mount so the same value isn't delivered twice.
199
+ if (value !== mount.lastChange) {
200
+ mount.lastChange = value;
201
+ config.onChange?.(value);
202
+ }
203
+ // Mirror into TEA state (value/dirty) while the loop is alive; dropped
204
+ // silently after dispose, which is fine — the consumer was already told.
205
+ send({ type: 'markdownChanged', value });
206
+ },
152
207
  onSelectionChange: (ctx) => {
153
208
  const format = computeFormatState(ctx.editor, ctx);
154
209
  const text = ctx.editor.getEditorState().read(() => $getRoot().getTextContent());
@@ -165,7 +220,7 @@ export function markdownEditor(config = {}) {
165
220
  const rendered = ui.view({
166
221
  state: state.at(`plugins.${name}`),
167
222
  send: (msg) => send({ type: 'plugin', name, msg }),
168
- editor: getEditor,
223
+ editor: () => mount.editor,
169
224
  });
170
225
  return Array.isArray(rendered) ? rendered : [rendered];
171
226
  });
@@ -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 { 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"]}
1
+ {"version":3,"file":"editor.js","sourceRoot":"","sources":["../src/editor.ts"],"names":[],"mappings":"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"}
package/dist/effects.d.ts CHANGED
@@ -6,12 +6,13 @@ export interface EffectApi {
6
6
  send: (msg: EditorMsg) => void;
7
7
  }
8
8
  export interface EffectConfig {
9
- onChange?: (markdown: string) => void;
10
9
  onFormatChange?: (format: FormatState) => void;
11
10
  /** Push markdown into the live editor (deserialize), without echoing onChange. */
12
11
  applyValue: (editor: LexicalEditor, value: string) => void;
13
12
  }
14
- /** Build the component's `onEffect`. `getEditor` returns the live editor (set at
15
- * mount via the foreign `onReady`); `items` is the merged id command map. */
16
- export declare function makeOnEffect(getEditor: () => LexicalEditor | null, items: ReadonlyMap<string, CommandItem>, config: EffectConfig): (effect: EditorEffect, api: EffectApi) => void;
13
+ /** Build the component's `onEffect`. `resolveEditor` maps the per-mount effect
14
+ * `api` (whose `send` identifies the mount) to that mount's live editor — so two
15
+ * mounts of one definition dispatch to their own editors; `items` is the merged
16
+ * id → command map. */
17
+ export declare function makeOnEffect(resolveEditor: (api: EffectApi) => LexicalEditor | null, items: ReadonlyMap<string, CommandItem>, config: EffectConfig): (effect: EditorEffect, api: EffectApi) => void;
17
18
  //# sourceMappingURL=effects.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"effects.d.ts","sourceRoot":"","sources":["../src/effects.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEtE,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAA;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACrC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;IAC9C,kFAAkF;IAClF,UAAU,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAC3D;AAED;+EAC+E;AAC/E,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,aAAa,GAAG,IAAI,EACrC,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EACvC,MAAM,EAAE,YAAY,GACnB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,KAAK,IAAI,CAwBhD"}
1
+ {"version":3,"file":"effects.d.ts","sourceRoot":"","sources":["../src/effects.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEtE,iEAAiE;AACjE,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,IAAI,CAAA;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAA;IAC9C,kFAAkF;IAClF,UAAU,EAAE,CAAC,MAAM,EAAE,aAAa,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAC3D;AAED;;;uBAGuB;AACvB,wBAAgB,YAAY,CAC1B,aAAa,EAAE,CAAC,GAAG,EAAE,SAAS,KAAK,aAAa,GAAG,IAAI,EACvD,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,EACvC,MAAM,EAAE,YAAY,GACnB,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,EAAE,SAAS,KAAK,IAAI,CA2BhD"}
package/dist/effects.js CHANGED
@@ -1,26 +1,31 @@
1
1
  // Effect handler: the only place TEA reaches back into the live Lexical editor.
2
2
  // `execCommand` looks an id up in the merged command-item map and runs it on the
3
3
  // editor captured at mount; emit* forward to the consumer's callbacks.
4
- /** Build the component's `onEffect`. `getEditor` returns the live editor (set at
5
- * mount via the foreign `onReady`); `items` is the merged id command map. */
6
- export function makeOnEffect(getEditor, items, config) {
4
+ /** Build the component's `onEffect`. `resolveEditor` maps the per-mount effect
5
+ * `api` (whose `send` identifies the mount) to that mount's live editor — so two
6
+ * mounts of one definition dispatch to their own editors; `items` is the merged
7
+ * id → command map. */
8
+ export function makeOnEffect(resolveEditor, items, config) {
7
9
  return (effect, api) => {
8
10
  switch (effect.type) {
9
11
  case 'execCommand': {
10
- const editor = getEditor();
12
+ const editor = resolveEditor(api);
11
13
  const item = items.get(effect.id);
12
14
  if (editor && item)
13
15
  item.run(editor, { send: api.send });
14
16
  return;
15
17
  }
16
18
  case 'applyValue': {
17
- const editor = getEditor();
19
+ const editor = resolveEditor(api);
18
20
  if (editor)
19
21
  config.applyValue(editor, effect.value);
20
22
  return;
21
23
  }
22
24
  case 'emitChange': {
23
- config.onChange?.(effect.value);
25
+ // Consumer `onChange` delivery moved to the foreign onChange wrapper (see
26
+ // editor.ts) so it survives dispose — the loop is torn down before the
27
+ // dispose-time debounce flush runs, and a `send`-routed effect would be
28
+ // dropped. This effect now only signals that state changed; no side effect.
24
29
  return;
25
30
  }
26
31
  case 'emitFormat': {
@@ -1 +1 @@
1
- {"version":3,"file":"effects.js","sourceRoot":"","sources":["../src/effects.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AACjF,uEAAuE;AAkBvE;+EAC+E;AAC/E,MAAM,UAAU,YAAY,CAC1B,SAAqC,EACrC,KAAuC,EACvC,MAAoB;IAEpB,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;QACrB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;gBAC1B,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;gBACjC,IAAI,MAAM,IAAI,IAAI;oBAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;gBACxD,OAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;gBAC1B,IAAI,MAAM;oBAAE,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;gBACnD,OAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,MAAM,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;gBAC/B,OAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,MAAM,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;gBACtC,OAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC,CAAA;AACH,CAAC","sourcesContent":["// Effect handler: the only place TEA reaches back into the live Lexical editor.\n// `execCommand` looks an id up in the merged command-item map and runs it on the\n// editor captured at mount; emit* forward to the consumer's callbacks.\n\nimport type { LexicalEditor } from 'lexical'\nimport type { CommandItem } from './plugins/types.js'\nimport type { EditorEffect, EditorMsg, FormatState } from './state.js'\n\n/** The api the component passes to `onEffect` (send + state). */\nexport interface EffectApi {\n send: (msg: EditorMsg) => void\n}\n\nexport interface EffectConfig {\n onChange?: (markdown: string) => void\n onFormatChange?: (format: FormatState) => void\n /** Push markdown into the live editor (deserialize), without echoing onChange. */\n applyValue: (editor: LexicalEditor, value: string) => void\n}\n\n/** Build the component's `onEffect`. `getEditor` returns the live editor (set at\n * mount via the foreign `onReady`); `items` is the merged id → command map. */\nexport function makeOnEffect(\n getEditor: () => LexicalEditor | null,\n items: ReadonlyMap<string, CommandItem>,\n config: EffectConfig,\n): (effect: EditorEffect, api: EffectApi) => void {\n return (effect, api) => {\n switch (effect.type) {\n case 'execCommand': {\n const editor = getEditor()\n const item = items.get(effect.id)\n if (editor && item) item.run(editor, { send: api.send })\n return\n }\n case 'applyValue': {\n const editor = getEditor()\n if (editor) config.applyValue(editor, effect.value)\n return\n }\n case 'emitChange': {\n config.onChange?.(effect.value)\n return\n }\n case 'emitFormat': {\n config.onFormatChange?.(effect.format)\n return\n }\n }\n }\n}\n"]}
1
+ {"version":3,"file":"effects.js","sourceRoot":"","sources":["../src/effects.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,iFAAiF;AACjF,uEAAuE;AAiBvE;;;uBAGuB;AACvB,MAAM,UAAU,YAAY,CAC1B,aAAuD,EACvD,KAAuC,EACvC,MAAoB;IAEpB,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE;QACrB,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;gBACjC,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;gBACjC,IAAI,MAAM,IAAI,IAAI;oBAAE,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC,CAAA;gBACxD,OAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,MAAM,MAAM,GAAG,aAAa,CAAC,GAAG,CAAC,CAAA;gBACjC,IAAI,MAAM;oBAAE,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;gBACnD,OAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,0EAA0E;gBAC1E,uEAAuE;gBACvE,wEAAwE;gBACxE,4EAA4E;gBAC5E,OAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,MAAM,CAAC,cAAc,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;gBACtC,OAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC,CAAA;AACH,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,8EAA8E;AAC9E,mEAAmE;AAEnE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAsB,MAAM,SAAS,CAAA;AAC9E,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EAAE,YAAY,EAAoC,MAAM,YAAY,CAAA;AAE3E,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAChC,MAAqB,EACrB,OAAsD;IAEtD,OAAO,MAAM,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,eAAe,EAAE,CAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,OAAO,EAAE,GAAG,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAA;QAChF,CAAC;QAED,IAAI,SAAS,GAAc,IAAI,CAAC,SAAS,CAAA;QACzC,IAAI,IAAI,GAAG,KAAK,CAAA;QAEhB,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;QACjC,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,CAAA;YAC7C,IAAI,GAAG,mBAAmB,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAA;YAE5E,IAAI,IAAI,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;gBAC/B,MAAM,QAAQ,GAAG,mBAAmB,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAA;gBACjF,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,IAAI,GAAG,qBAAqB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;oBACxD,IAAI,IAAI;wBAAE,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;gBAC1C,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAA;oBACnF,IAAI,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC;wBAAE,SAAS,GAAG,MAAM,CAAA;gBACjD,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI;YACJ,SAAS;YACT,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC","sourcesContent":["// Compute the full toolbar FormatState from a Lexical selection — the base\n// rich-text format (from @llui/lexical) refined with list/code/link detection\n// (which needs the list/code/link packages this layer depends on).\n\nimport { $getSelection, $isRangeSelection, type LexicalEditor } from 'lexical'\nimport { $findMatchingParent, $getNearestNodeOfType } from '@lexical/utils'\nimport { ListNode, $isListItemNode } from '@lexical/list'\nimport { $isCodeNode } from '@lexical/code-core'\nimport { $isLinkNode } from '@lexical/link'\nimport { $readBaseFormat } from '@llui/lexical'\nimport type { SelectionContext } from '@llui/lexical'\nimport { EMPTY_FORMAT, type BlockType, type FormatState } from './state.js'\n\n/** Read the full format surface at the current selection (opens a read ctx). */\nexport function computeFormatState(\n editor: LexicalEditor,\n history: Pick<SelectionContext, 'canUndo' | 'canRedo'>,\n): FormatState {\n return editor.getEditorState().read(() => {\n const base = $readBaseFormat()\n if (!base.hasSelection) {\n return { ...EMPTY_FORMAT, canUndo: history.canUndo, canRedo: history.canRedo }\n }\n\n let blockType: BlockType = base.blockType\n let link = false\n\n const selection = $getSelection()\n if ($isRangeSelection(selection)) {\n const anchorNode = selection.anchor.getNode()\n link = $findMatchingParent(anchorNode, (node) => $isLinkNode(node)) !== null\n\n if (base.blockType === 'other') {\n const listItem = $findMatchingParent(anchorNode, (node) => $isListItemNode(node))\n if (listItem) {\n const list = $getNearestNodeOfType(anchorNode, ListNode)\n if (list) blockType = list.getListType()\n } else {\n const top = anchorNode.getKey() === 'root' ? null : anchorNode.getTopLevelElement()\n if (top && $isCodeNode(top)) blockType = 'code'\n }\n }\n }\n\n return {\n bold: base.bold,\n italic: base.italic,\n strikethrough: base.strikethrough,\n code: base.code,\n link,\n blockType,\n alignment: base.alignment,\n canUndo: history.canUndo,\n canRedo: history.canRedo,\n }\n })\n}\n"]}
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../src/format.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,8EAA8E;AAC9E,mEAAmE;AAEnE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAsB,MAAM,SAAS,CAAA;AAC9E,OAAO,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAC3E,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAE/C,OAAO,EAAE,YAAY,EAAoC,MAAM,YAAY,CAAA;AAE3E,gFAAgF;AAChF,MAAM,UAAU,kBAAkB,CAChC,MAAqB,EACrB,OAAsD;IAEtD,OAAO,MAAM,CAAC,cAAc,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,eAAe,EAAE,CAAA;QAC9B,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,OAAO,EAAE,GAAG,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAA;QAChF,CAAC;QAED,IAAI,SAAS,GAAc,IAAI,CAAC,SAAS,CAAA;QACzC,IAAI,IAAI,GAAG,KAAK,CAAA;QAEhB,MAAM,SAAS,GAAG,aAAa,EAAE,CAAA;QACjC,IAAI,iBAAiB,CAAC,SAAS,CAAC,EAAE,CAAC;YACjC,MAAM,UAAU,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,EAAE,CAAA;YAC7C,IAAI,GAAG,mBAAmB,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAA;YAE5E,IAAI,IAAI,CAAC,SAAS,KAAK,OAAO,EAAE,CAAC;gBAC/B,MAAM,QAAQ,GAAG,mBAAmB,CAAC,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,CAAA;gBACjF,IAAI,QAAQ,EAAE,CAAC;oBACb,MAAM,IAAI,GAAG,qBAAqB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;oBACxD,IAAI,IAAI;wBAAE,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;gBAC1C,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,kBAAkB,EAAE,CAAA;oBACnF,IAAI,GAAG,IAAI,WAAW,CAAC,GAAG,CAAC;wBAAE,SAAS,GAAG,MAAM,CAAA;gBACjD,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI;YACJ,SAAS;YACT,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,OAAO,EAAE,OAAO,CAAC,OAAO;SACzB,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { type EditorConfig, type EditorParts, type CollabBinding, type CollabHooks, type CollabFactory, markdownEditor, } from './editor.js';
1
+ export { type EditorConfig, type EditorParts, type CollabBinding, type CollabHooks, type CollabFactory, markdownEditor, blockUnderlineFormat, } from './editor.js';
2
2
  export { type BlockType, type FormatState, type OverlayKind, type CollabStatus, type EditorState, type EditorMsg, type EditorOutMsg, type EditorEffect, type InitOptions, EMPTY_FORMAT, COLLAB_OFF, init, update, countWords, } from './state.js';
3
3
  export { type ItemSurface, type CommandItem, type CommandContext, type MarkdownPlugin, } from './plugins/types.js';
4
4
  export { type PluginUI, type PluginUISpec, type PluginViewArgs, type PluginEffectContext, definePluginUI, } from './plugins/ui.js';
@@ -17,10 +17,10 @@ export { type EmojiPluginOptions, DEFAULT_EMOJI, emojiPlugin } from './plugins/e
17
17
  export { type ImagePluginOptions, imagePlugin } from './plugins/image.js';
18
18
  export { tablePlugin } from './plugins/table.js';
19
19
  export { $insertMarkdownAtSelection, registerMarkdownPaste } from './paste.js';
20
- export { GFM_NODES, GFM_TRANSFORMERS } from './transformers/gfm.js';
20
+ export { GFM_NODES, GFM_TRANSFORMERS, HIGHLIGHT_TRANSFORMER } from './transformers/gfm.js';
21
21
  export { buildTransformers, orderTransformers } from './transformers/registry.js';
22
22
  export { computeFormatState } from './format.js';
23
- export { STRIKETHROUGH_CLASS, UNDERLINE_CLASS, UNDERLINE_STRIKETHROUGH_CLASS, defaultTheme, mergeTheme, } from './theme.js';
23
+ export { STRIKETHROUGH_CLASS, defaultTheme, mergeTheme } from './theme.js';
24
24
  export { type ToolbarItemParts, type ToolbarParts, type ToolbarOptions, DEFAULT_GLYPHS, connectToolbar, toolbar, } from './surfaces/toolbar.js';
25
25
  export { type LinkDialogOptions, linkDialog } from './surfaces/link-dialog.js';
26
26
  //# sourceMappingURL=index.d.ts.map
@@ -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,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"}
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,EACd,oBAAoB,GACrB,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,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC1F,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE1E,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
@@ -1,5 +1,5 @@
1
1
  // `@llui/markdown-editor` — WYSIWYG Markdown editor for LLui, built on Lexical.
2
- export { markdownEditor, } from './editor.js';
2
+ export { markdownEditor, blockUnderlineFormat, } from './editor.js';
3
3
  export { EMPTY_FORMAT, COLLAB_OFF, init, update, countWords, } from './state.js';
4
4
  export {} from './plugins/types.js';
5
5
  export { definePluginUI, } from './plugins/ui.js';
@@ -18,10 +18,10 @@ 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
20
  export { $insertMarkdownAtSelection, registerMarkdownPaste } from './paste.js';
21
- export { GFM_NODES, GFM_TRANSFORMERS } from './transformers/gfm.js';
21
+ export { GFM_NODES, GFM_TRANSFORMERS, HIGHLIGHT_TRANSFORMER } from './transformers/gfm.js';
22
22
  export { buildTransformers, orderTransformers } from './transformers/registry.js';
23
23
  export { computeFormatState } from './format.js';
24
- export { STRIKETHROUGH_CLASS, UNDERLINE_CLASS, UNDERLINE_STRIKETHROUGH_CLASS, defaultTheme, mergeTheme, } from './theme.js';
24
+ export { STRIKETHROUGH_CLASS, defaultTheme, mergeTheme } from './theme.js';
25
25
  export { DEFAULT_GLYPHS, connectToolbar, toolbar, } from './surfaces/toolbar.js';
26
26
  export { linkDialog } from './surfaces/link-dialog.js';
27
27
  //# sourceMappingURL=index.js.map
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,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"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,OAAO,EAML,cAAc,EACd,oBAAoB,GACrB,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,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAC1F,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AAEjF,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAEhD,OAAO,EAAE,mBAAmB,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE1E,OAAO,EAIL,cAAc,EACd,cAAc,EACd,OAAO,GACR,MAAM,uBAAuB,CAAA;AAE9B,OAAO,EAA0B,UAAU,EAAE,MAAM,2BAA2B,CAAA"}
@@ -1 +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"}
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;AAGhF;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,KAAK,CAAC,WAAW,CAAC,GAC/B,OAAO,CAqBT;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 CHANGED
@@ -5,6 +5,7 @@
5
5
  // per-editor via the `pasteMarkdown: false` config option.
6
6
  import { $createParagraphNode, $getSelection, $insertNodes, $isRangeSelection, $setSelection, COMMAND_PRIORITY_HIGH, PASTE_COMMAND, isDOMNode, isSelectionCapturedInDecoratorInput, } from 'lexical';
7
7
  import { $convertFromMarkdownString } from '@lexical/markdown';
8
+ import { $sanitizeLinkNodes } from './security.js';
8
9
  /**
9
10
  * Parse `markdown` with `transformers` and insert the produced nodes at the
10
11
  * current range selection. The document is parsed into a detached scratch
@@ -24,6 +25,10 @@ export function $insertMarkdownAtSelection(markdown, transformers) {
24
25
  // touches the scratch node, not the live document.
25
26
  const scratch = $createParagraphNode();
26
27
  $convertFromMarkdownString(markdown, transformers, scratch);
28
+ // Untrusted source: neutralize any `javascript:`/`data:` link the markdown
29
+ // carried BEFORE the nodes are spliced into the live document. (Image src is
30
+ // enforced in the image transformer; links go through here.)
31
+ $sanitizeLinkNodes(scratch);
27
32
  const nodes = scratch.getChildren();
28
33
  if (nodes.length === 0)
29
34
  return false;
package/dist/paste.js.map CHANGED
@@ -1 +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"]}
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;AAChF,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAElD;;;;;;;;;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,2EAA2E;IAC3E,6EAA6E;IAC7E,6DAA6D;IAC7D,kBAAkB,CAAC,OAAO,CAAC,CAAA;IAC3B,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"}
@@ -1 +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"]}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"callout.d.ts","sourceRoot":"","sources":["../../src/plugins/callout.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,iBAAiB,EAElB,MAAM,eAAe,CAAA;AAEtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE/D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb;AA8GD,iFAAiF;AACjF,wBAAgB,cAAc,CAC5B,IAAI,GAAE,WAAoB,EAC1B,SAAS,SAAgB,GACxB,iBAAiB,CAEnB;AAyBD,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,wBAAgB,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,cAAc,CA0B7E"}
1
+ {"version":3,"file":"callout.d.ts","sourceRoot":"","sources":["../../src/plugins/callout.ts"],"names":[],"mappings":"AAQA,OAAO,EAGL,iBAAiB,EAElB,MAAM,eAAe,CAAA;AAEtB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAEhD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE/D,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,WAAW,CAAA;IACjB,IAAI,EAAE,MAAM,CAAA;CACb;AA+FD,iFAAiF;AACjF,wBAAgB,cAAc,CAC5B,IAAI,GAAE,WAAoB,EAC1B,SAAS,SAAgB,GACxB,iBAAiB,CAEnB;AAyBD,MAAM,WAAW,oBAAoB;IACnC,wDAAwD;IACxD,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B;AAED,wBAAgB,aAAa,CAAC,IAAI,GAAE,oBAAyB,GAAG,cAAc,CA0B7E"}
@@ -5,7 +5,7 @@
5
5
  import {} from 'lexical';
6
6
  import { $insertNodeToNearestRoot } from '@lexical/utils';
7
7
  import { $createLLuiDecoratorNode, $isLLuiDecoratorNode, LLuiDecoratorNode, decoratorBridge, } from '@llui/lexical';
8
- import { button, component, div, span, text } from '@llui/dom';
8
+ import { button, div, span, text } from '@llui/dom';
9
9
  const KIND_CYCLE = ['note', 'tip', 'warning', 'danger'];
10
10
  const KIND_LABEL = {
11
11
  note: 'Note',
@@ -29,50 +29,42 @@ function isCalloutData(value) {
29
29
  const stop = (e) => e.stopPropagation();
30
30
  /** The LLui sub-view rendered inside a callout DecoratorNode. The badge cycles
31
31
  * the kind; the text is an editable island that persists into the Lexical node
32
- * on blur (both round-trip to markdown). */
33
- const calloutBridge = decoratorBridge(BRIDGE_TYPE, (data, api) => component({
34
- name: 'Callout',
35
- init: () => ({ kind: data.kind, text: data.text }),
36
- update: (state, msg) => {
37
- if (msg.type === 'cycle') {
38
- const kind = nextKind(state.kind);
39
- api.update({ kind, text: state.text });
40
- return { ...state, kind };
41
- }
42
- if (msg.type === 'commitText') {
43
- if (msg.text === state.text)
44
- return state;
45
- api.update({ kind: state.kind, text: msg.text });
46
- return { ...state, text: msg.text };
47
- }
48
- return state;
49
- },
50
- view: ({ state, send }) => [
51
- div({
52
- 'data-scope': 'md-callout',
53
- 'data-part': 'root',
54
- 'data-kind': state.at('kind'),
55
- contenteditable: 'false',
56
- onKeyDown: stop,
57
- onBeforeInput: stop,
58
- onPaste: stop,
59
- }, [
60
- button({
61
- type: 'button',
62
- 'data-part': 'badge',
63
- 'aria-label': 'Change callout kind',
64
- onClick: () => send({ type: 'cycle' }),
65
- }, [text(state.at('kind').map((k) => KIND_LABEL[k]))]),
66
- span({
67
- 'data-part': 'text',
68
- contenteditable: 'true',
69
- role: 'textbox',
70
- 'aria-label': 'Callout text',
71
- onBlur: (e) => send({ type: 'commitText', text: e.target.textContent ?? '' }),
72
- }, [text(state.at('text'))]),
73
- ]),
74
- ],
75
- }));
32
+ * on blur (both round-trip to markdown). `data` is a reactive signal fed by the
33
+ * bridge, so a data commit updates the view IN PLACE — the editable island keeps
34
+ * its focus/caret instead of the whole sub-app remounting. */
35
+ const calloutBridge = decoratorBridge(BRIDGE_TYPE, (data, api) => [
36
+ div({
37
+ 'data-scope': 'md-callout',
38
+ 'data-part': 'root',
39
+ 'data-kind': data.at('kind'),
40
+ contenteditable: 'false',
41
+ onKeyDown: stop,
42
+ onBeforeInput: stop,
43
+ onPaste: stop,
44
+ }, [
45
+ button({
46
+ type: 'button',
47
+ 'data-part': 'badge',
48
+ 'aria-label': 'Change callout kind',
49
+ onClick: () => {
50
+ const cur = data.peek();
51
+ api.update({ kind: nextKind(cur.kind), text: cur.text });
52
+ },
53
+ }, [text(data.at('kind').map((k) => KIND_LABEL[k]))]),
54
+ span({
55
+ 'data-part': 'text',
56
+ contenteditable: 'true',
57
+ role: 'textbox',
58
+ 'aria-label': 'Callout text',
59
+ onBlur: (e) => {
60
+ const cur = data.peek();
61
+ const value = e.target.textContent ?? '';
62
+ if (value !== cur.text)
63
+ api.update({ kind: cur.kind, text: value });
64
+ },
65
+ }, [text(data.at('text'))]),
66
+ ]),
67
+ ]);
76
68
  /** `:::kind text` element transformer (single-line admonition). */
77
69
  const CALLOUT_TRANSFORMER = {
78
70
  dependencies: [LLuiDecoratorNode],