@lexical/mdast 0.47.1-nightly.20260716.0 → 0.48.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.
package/README.md CHANGED
@@ -63,6 +63,7 @@ Behavior and convenience bundles:
63
63
  | `MdastExportExtension` | serialization back to Markdown (`$convertToMarkdownString`) |
64
64
  | `MdastExtension` | bundle of `MdastImportExtension` + `MdastExportExtension` |
65
65
  | `MdastShadowRootQuoteExtension` | opt-in: blockquotes as block containers (full-fidelity nested content) |
66
+ | `MdastHtmlExtension` | opt-in: raw HTML routed through the `@lexical/html` DOM import rules; HTML-encoded export via `$exportViaDOM` / `rawHtmlBlock` |
66
67
  | `MdastShortcutsExtension` | streaming keyboard shortcuts |
67
68
 
68
69
  Everything composes granularly and degrades gracefully: an editor with only
@@ -183,6 +184,85 @@ configExtension(MdastImportExtension, {
183
184
  });
184
185
  ```
185
186
 
187
+ ### Raw HTML (`MdastHtmlExtension`)
188
+
189
+ Markdown passes raw HTML through — GitHub-style `<details>` blocks, inline
190
+ `<kbd>` runs — and by default it imports as literal text. The opt-in
191
+ `MdastHtmlExtension` routes it through the editor's `@lexical/html`
192
+ `DOMImportExtension` rules instead, so **any HTML the editor can already
193
+ import works from Markdown**, and Markdown *inside* the construct keeps
194
+ working in both directions, the way it does on GitHub:
195
+
196
+ ```md
197
+ <details><summary>
198
+ The *summary* line
199
+ </summary>
200
+
201
+ The **body** blocks
202
+ </details>
203
+ ```
204
+
205
+ - **Import**: raw HTML block sequences and inline tag runs are reassembled
206
+ by tag balance (CommonMark splits blocks on blank lines), parsed with
207
+ `DOMParser`, and dispatched through the DOM import rule registry.
208
+ Markdown between the tags is parsed with the document's own grammar and
209
+ substituted back in with the surrounding formatting context. Unclosed
210
+ tags — including the `<p` / `<details` prefixes typing passes through —
211
+ stay literal text.
212
+ - **Export**: register `$exportViaDOM` for a node type and its `exportDOM`
213
+ becomes the single source of truth for the Markdown encoding too: the
214
+ shell is rendered, the children channel and named slots (marked with
215
+ `data-lexical-slot`, which is stripped from the output) are substituted
216
+ with embedded Markdown, boolean attributes are normalized, and custom
217
+ element tags get their own lines where CommonMark requires it to
218
+ re-parse. For hand-written encodings, `rawHtmlBlock(...parts)` builds
219
+ the same kind of node from a template of raw tag strings, embedded
220
+ Markdown phrasing, and `{flow}` block runs.
221
+ - **Context states**: the Markdown pipeline runs under the same
222
+ context-record mechanism as the `@lexical/html` import/render pipelines.
223
+ The whole import walk runs with `ImportContextMarkdown` set (so a DOM
224
+ rule can distinguish Markdown import from HTML paste); mdast import
225
+ handlers read ambient state with `$getImportContextValue` and layer
226
+ state for their subtree with `$withImportContext` around
227
+ `ctx.importChildren` — visible to nested handlers and to DOM-rule
228
+ sessions opened for raw HTML in that subtree, and to nothing outside
229
+ it. On export, `RenderContextMarkdownExport` lets `exportDOM` diverge
230
+ per destination (Markdown vs the HTML clipboard), and selection exports
231
+ (`$convertSelectionToMarkdownString`) run under
232
+ `RenderContextMarkdownSelection` carrying the selection, so a
233
+ contributed to-markdown handler that appends end-of-document data can
234
+ scope its output to a clipboard copy.
235
+
236
+ A complete HTML-encoded construct is one DOM import rule (which then also
237
+ serves HTML paste) plus one export rule:
238
+
239
+ ```ts
240
+ import {$exportViaDOM, MdastHtmlExtension, MdastImportExtension} from '@lexical/mdast';
241
+ import {defineImportRule, DOMImportExtension, sel} from '@lexical/html';
242
+ import {configExtension, defineExtension} from 'lexical';
243
+
244
+ export const MdastCollapsibleExtension = defineExtension({
245
+ name: 'collapsible-markdown',
246
+ nodes: [CollapsibleNode],
247
+ dependencies: [
248
+ MdastHtmlExtension,
249
+ configExtension(DOMImportExtension, {
250
+ // sel.tag('details') -> CollapsibleNode; serves Markdown and paste.
251
+ rules: [DetailsImportRule],
252
+ }),
253
+ configExtension(MdastImportExtension, {
254
+ // exportDOM is the single source of truth for the encoding.
255
+ exportRules: [{$export: $exportViaDOM, type: 'collapsible'}],
256
+ }),
257
+ ],
258
+ });
259
+ ```
260
+
261
+ The [mdast-editor dev example](https://github.com/facebook/lexical/tree/main/dev-examples/mdast-editor)
262
+ demonstrates the block path (a `<details><summary>` collapsible with a
263
+ named summary slot), the inline path (`<kbd>` keys), and inline HTML text
264
+ formats (`<u>`, `<mark>`, `<sub>`/`<sup>`, `style="color: …"` spans).
265
+
186
266
  ### Custom mappings
187
267
 
188
268
  Because extensions are the unit of configuration, you add or override behavior