@lexical/html 0.44.1-nightly.20260518.0 → 0.45.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 (66) hide show
  1. package/{DOMRenderExtension.d.ts → dist/DOMRenderExtension.d.ts} +12 -1
  2. package/dist/DOMRenderRuntime.d.ts +51 -0
  3. package/dist/LexicalHtml.dev.js +3192 -0
  4. package/dist/LexicalHtml.dev.mjs +3146 -0
  5. package/{LexicalHtml.js.flow → dist/LexicalHtml.js.flow} +16 -16
  6. package/dist/LexicalHtml.mjs +56 -0
  7. package/dist/LexicalHtml.node.mjs +54 -0
  8. package/dist/LexicalHtml.prod.js +9 -0
  9. package/dist/LexicalHtml.prod.mjs +9 -0
  10. package/dist/RenderContext.d.ts +68 -0
  11. package/{compileDOMRenderConfigOverrides.d.ts → dist/compileDOMRenderConfigOverrides.d.ts} +1 -1
  12. package/{constants.d.ts → dist/constants.d.ts} +2 -0
  13. package/dist/domOverride.d.ts +23 -0
  14. package/dist/import/CoreImportExtension.d.ts +11 -0
  15. package/dist/import/DOMImportExtension.d.ts +82 -0
  16. package/dist/import/HorizontalRuleImportExtension.d.ts +27 -0
  17. package/dist/import/ImportContext.d.ts +208 -0
  18. package/dist/import/compileImportRules.d.ts +50 -0
  19. package/dist/import/coreImportRules.d.ts +25 -0
  20. package/dist/import/defineImportRule.d.ts +32 -0
  21. package/dist/import/defineOverlayRules.d.ts +66 -0
  22. package/dist/import/index.d.ts +38 -0
  23. package/dist/import/inlineStylesFromStyleSheets.d.ts +28 -0
  24. package/dist/import/parseCss.d.ts +18 -0
  25. package/dist/import/runImport.d.ts +19 -0
  26. package/dist/import/schemas.d.ts +91 -0
  27. package/dist/import/sel.d.ts +74 -0
  28. package/dist/import/types.d.ts +394 -0
  29. package/dist/index.d.ts +44 -0
  30. package/{types.d.ts → dist/types.d.ts} +96 -8
  31. package/package.json +33 -18
  32. package/src/ContextRecord.ts +243 -0
  33. package/src/DOMRenderExtension.ts +96 -0
  34. package/src/DOMRenderRuntime.ts +265 -0
  35. package/src/RenderContext.ts +168 -0
  36. package/src/compileDOMRenderConfigOverrides.ts +416 -0
  37. package/src/constants.ts +18 -0
  38. package/src/domOverride.ts +46 -0
  39. package/src/import/CoreImportExtension.ts +26 -0
  40. package/src/import/DOMImportExtension.ts +221 -0
  41. package/src/import/HorizontalRuleImportExtension.ts +53 -0
  42. package/src/import/ImportContext.ts +339 -0
  43. package/src/import/compileImportRules.ts +178 -0
  44. package/src/import/coreImportRules.ts +485 -0
  45. package/src/import/defineImportRule.ts +40 -0
  46. package/src/import/defineOverlayRules.ts +105 -0
  47. package/src/import/index.ts +96 -0
  48. package/src/import/inlineStylesFromStyleSheets.ts +104 -0
  49. package/src/import/parseCss.ts +219 -0
  50. package/src/import/runImport.ts +245 -0
  51. package/src/import/schemas.ts +236 -0
  52. package/src/import/sel.ts +314 -0
  53. package/src/import/types.ts +471 -0
  54. package/src/index.ts +555 -0
  55. package/src/types.ts +470 -0
  56. package/LexicalHtml.dev.js +0 -914
  57. package/LexicalHtml.dev.mjs +0 -900
  58. package/LexicalHtml.mjs +0 -24
  59. package/LexicalHtml.node.mjs +0 -22
  60. package/LexicalHtml.prod.js +0 -9
  61. package/LexicalHtml.prod.mjs +0 -9
  62. package/RenderContext.d.ts +0 -32
  63. package/domOverride.d.ts +0 -18
  64. package/index.d.ts +0 -32
  65. /package/{ContextRecord.d.ts → dist/ContextRecord.d.ts} +0 -0
  66. /package/{LexicalHtml.js → dist/LexicalHtml.js} +0 -0
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type {ContextRecord} from '../types';
9
+ import type {DOMImportRuleEntry} from './defineOverlayRules';
10
+ import type {
11
+ DOMImportExtensionOutput,
12
+ DOMPreprocessContext,
13
+ DOMPreprocessFn,
14
+ GenerateNodesFromDOMOptions,
15
+ ImportContextPairOrUpdater,
16
+ } from './types';
17
+
18
+ import {$getExtensionOutput} from '@lexical/extension';
19
+ import {defineExtension, type LexicalNode, shallowMergeConfig} from 'lexical';
20
+
21
+ import {DOMImportContextSymbol, DOMImportExtensionName} from '../constants';
22
+ import {$withFullContext, contextFromPairs} from '../ContextRecord';
23
+ import {type CompiledDispatch, compileImportRules} from './compileImportRules';
24
+ import {defineImportRule} from './defineImportRule';
25
+ import {flattenRuleEntries} from './defineOverlayRules';
26
+ import {ImportSessionImpl} from './ImportContext';
27
+ import {$inlineStylesFromStyleSheets} from './inlineStylesFromStyleSheets';
28
+ import {$runImport} from './runImport';
29
+ import {selBase} from './sel';
30
+
31
+ /**
32
+ * Configuration for {@link DOMImportExtension}.
33
+ *
34
+ * @experimental
35
+ */
36
+ export interface DOMImportConfig {
37
+ /**
38
+ * The set of rules contributed by this extension and its dependencies.
39
+ * Entries can be raw {@link DOMImportRule}s or a
40
+ * {@link CompiledOverlayRules} produced by {@link defineOverlayRules}
41
+ * (the latter is inlined in priority order — useful for libraries
42
+ * that already publish a compiled overlay).
43
+ *
44
+ * Rules are dispatched in priority order: rules contributed by
45
+ * extensions merged later (i.e. closer to the editor root) run first
46
+ * and may call `$next()` to delegate to lower-priority rules.
47
+ *
48
+ * `mergeConfig` prepends `partial.rules` to existing `rules`, so later
49
+ * configuration carries higher priority.
50
+ */
51
+ readonly rules: readonly DOMImportRuleEntry[];
52
+ /**
53
+ * Default context pairs applied to every `$generateNodesFromDOM` call.
54
+ * Per-call overrides can be supplied via
55
+ * {@link GenerateNodesFromDOMOptions.context}.
56
+ */
57
+ readonly contextDefaults: readonly ImportContextPairOrUpdater[];
58
+ /**
59
+ * Functions run in order on the DOM before walking begins, mutating in
60
+ * place. The default config registers
61
+ * {@link $inlineStylesFromStyleSheets} (resolves `<style>` rules to
62
+ * inline styles so the rules' style-driven matchers see them); apps
63
+ * append additional preprocessors (e.g. strip unsafe elements,
64
+ * normalize attributes, resolve relative URLs).
65
+ *
66
+ * `mergeConfig` appends, so each contributing extension's preprocessors
67
+ * run in dependency order. Per-call preprocessors registered via
68
+ * {@link GenerateNodesFromDOMOptions.preprocess} run AFTER these.
69
+ */
70
+ readonly preprocess: readonly DOMPreprocessFn[];
71
+ }
72
+
73
+ /**
74
+ * Drive a stack of {@link DOMPreprocessFn}s top-to-bottom: the highest-
75
+ * index fn runs first and may call `$next()` to defer to the next-lower
76
+ * one. Matches the export-side `callExportMimeTypeFunctionStack` shape.
77
+ */
78
+ function $runPreprocessStack(
79
+ stack: readonly DOMPreprocessFn[],
80
+ dom: Document | ParentNode,
81
+ ctx: DOMPreprocessContext,
82
+ ): void {
83
+ let i = stack.length - 1;
84
+ const $next = () => {
85
+ while (i >= 0) {
86
+ const cur = stack[i--];
87
+ cur(dom, ctx, $next);
88
+ return;
89
+ }
90
+ };
91
+ $next();
92
+ }
93
+
94
+ /**
95
+ * Lowest-priority catch-all rule used as the default `config.rules` entry
96
+ * for {@link DOMImportExtension}: descends into the element's children
97
+ * and returns whatever they produced. With no other matching rule, an
98
+ * element vanishes and its contents are inserted in its place — the
99
+ * legacy `$createNodesFromDOM` hoisting behavior, but now expressed as a
100
+ * regular rule that apps can override (e.g. with a `sel.any()` rule that
101
+ * captures and discards unknown elements).
102
+ *
103
+ * @experimental
104
+ */
105
+ export const DefaultHoistRule = defineImportRule({
106
+ $import: (ctx, el) => ctx.$importChildren(el),
107
+ match: selBase.any(),
108
+ name: '@lexical/html/default-hoist',
109
+ });
110
+
111
+ /**
112
+ * @experimental
113
+ *
114
+ * Extension-based replacement for the legacy `importDOM` / `DOMConversion`
115
+ * machinery. Rules are contributed via configuration (see
116
+ * {@link DOMImportConfig.rules}), compiled into a tag-bucketed dispatcher at
117
+ * editor build time, and consumed via the extension's
118
+ * {@link DOMImportExtensionOutput.$generateNodesFromDOM} output.
119
+ *
120
+ * The legacy `$generateNodesFromDOM` continues to work in parallel; the
121
+ * intent is to migrate node packages over to this extension incrementally.
122
+ */
123
+ export const DOMImportExtension = defineExtension<
124
+ DOMImportConfig,
125
+ typeof DOMImportExtensionName,
126
+ DOMImportExtensionOutput,
127
+ void
128
+ >({
129
+ build(editor, config) {
130
+ const dispatch: CompiledDispatch = compileImportRules(
131
+ flattenRuleEntries(config.rules),
132
+ );
133
+ const defaults = contextFromPairs(config.contextDefaults, undefined);
134
+ const configPreprocess = config.preprocess;
135
+ return {
136
+ $generateNodesFromDOM: (
137
+ dom: Document | ParentNode,
138
+ options?: GenerateNodesFromDOMOptions,
139
+ ) => {
140
+ // The session record IS the root layer of the walk's context.
141
+ // Start with per-call options.context applied on top of the
142
+ // editor's contextDefaults, then ensure we have a *fresh*
143
+ // mutable child (never the shared defaults record) so
144
+ // session.set writes never leak into the editor's config.
145
+ const fromOpts =
146
+ options && options.context
147
+ ? contextFromPairs(options.context, defaults)
148
+ : defaults;
149
+ const sessionRecord: ContextRecord<typeof DOMImportContextSymbol> =
150
+ fromOpts !== undefined && fromOpts !== defaults
151
+ ? fromOpts
152
+ : Object.create(defaults || null);
153
+ const session = new ImportSessionImpl(sessionRecord);
154
+ const preprocessCtx: DOMPreprocessContext = {session};
155
+ // Stack of preprocessors: config-level first, then per-call.
156
+ // Top of stack (last in array) runs first; `next()` defers to
157
+ // the next-lower one. Matches the GetClipboardDataExtension
158
+ // convention so app-registered preprocessors can wrap built-in
159
+ // ones via `next()`. Preprocess writes via `ctx.session.set`
160
+ // mutate the session record directly.
161
+ const stack: readonly DOMPreprocessFn[] =
162
+ options && options.preprocess
163
+ ? [...configPreprocess, ...options.preprocess]
164
+ : configPreprocess;
165
+ $runPreprocessStack(stack, dom, preprocessCtx);
166
+ return $withFullContext(
167
+ DOMImportContextSymbol,
168
+ sessionRecord,
169
+ () => $runImport(dispatch, editor, dom, session),
170
+ editor,
171
+ );
172
+ },
173
+ defaults,
174
+ };
175
+ },
176
+ config: {
177
+ contextDefaults: [],
178
+ preprocess: [$inlineStylesFromStyleSheets],
179
+ rules: [DefaultHoistRule],
180
+ },
181
+ mergeConfig(config, partial) {
182
+ return shallowMergeConfig(config, {
183
+ ...partial,
184
+ ...(partial.contextDefaults && {
185
+ contextDefaults: [
186
+ ...config.contextDefaults,
187
+ ...partial.contextDefaults,
188
+ ],
189
+ }),
190
+ ...(partial.preprocess && {
191
+ preprocess: [...config.preprocess, ...partial.preprocess],
192
+ }),
193
+ ...(partial.rules && {
194
+ rules: [...partial.rules, ...config.rules],
195
+ }),
196
+ });
197
+ },
198
+ name: DOMImportExtensionName,
199
+ });
200
+
201
+ /**
202
+ * Look up the editor's {@link DOMImportExtension} and run its
203
+ * `$generateNodesFromDOM`. Designed as a drop-in replacement for the
204
+ * legacy `$generateNodesFromDOM(editor, dom)` signature so it can be
205
+ * supplied to `ClipboardImportExtension.$generateNodesFromDOM` (or any
206
+ * other consumer that wants to route through the extension pipeline).
207
+ *
208
+ * Throws if the editor was not built with {@link DOMImportExtension} as a
209
+ * dependency.
210
+ *
211
+ * @experimental
212
+ */
213
+ export function $generateNodesFromDOMViaExtension(
214
+ dom: Document | ParentNode,
215
+ options?: GenerateNodesFromDOMOptions,
216
+ ): LexicalNode[] {
217
+ return $getExtensionOutput(DOMImportExtension).$generateNodesFromDOM(
218
+ dom,
219
+ options,
220
+ );
221
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+
9
+ import {
10
+ $createHorizontalRuleNode,
11
+ HorizontalRuleExtension,
12
+ } from '@lexical/extension';
13
+ import {configExtension, defineExtension} from 'lexical';
14
+
15
+ import {CoreImportExtension} from './CoreImportExtension';
16
+ import {defineImportRule} from './defineImportRule';
17
+ import {DOMImportExtension} from './DOMImportExtension';
18
+ import {selBase} from './sel';
19
+
20
+ const HorizontalRuleRule = defineImportRule({
21
+ $import: () => [$createHorizontalRuleNode()],
22
+ match: selBase.tag('hr'),
23
+ name: '@lexical/html/hr',
24
+ });
25
+
26
+ /**
27
+ * Import rules for {@link HorizontalRuleNode}.
28
+ *
29
+ * @experimental
30
+ */
31
+ export const HorizontalRuleImportRules = [HorizontalRuleRule];
32
+
33
+ /**
34
+ * Bundles {@link HorizontalRuleImportRules} (plus
35
+ * {@link CoreImportExtension}) into a single dependency. The legacy
36
+ * {@link HorizontalRuleExtension.importDOM} continues to work in parallel;
37
+ * depend on this extension to opt into the new pipeline.
38
+ *
39
+ * Lives in `@lexical/html` (not `@lexical/extension`) because
40
+ * {@link DOMImportExtension} itself is in `@lexical/html`, and
41
+ * `@lexical/extension` is upstream of `@lexical/html` in the dependency
42
+ * graph — same arrangement as {@link CoreImportExtension}.
43
+ *
44
+ * @experimental
45
+ */
46
+ export const HorizontalRuleImportExtension = defineExtension({
47
+ dependencies: [
48
+ CoreImportExtension,
49
+ HorizontalRuleExtension,
50
+ configExtension(DOMImportExtension, {rules: HorizontalRuleImportRules}),
51
+ ],
52
+ name: '@lexical/html/HorizontalRuleImport',
53
+ });
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type {ContextRecord} from '../types';
9
+ import type {CompiledOverlayRules} from './defineOverlayRules';
10
+ import type {DOMImportExtension} from './DOMImportExtension';
11
+ import type {
12
+ ImportContextPairOrUpdater,
13
+ ImportSession,
14
+ ImportStateConfig,
15
+ } from './types';
16
+
17
+ import {getPeerDependencyFromEditor} from '@lexical/extension';
18
+ import {
19
+ $getEditor,
20
+ isBlockDomNode,
21
+ isDOMTextNode,
22
+ isHTMLElement,
23
+ isInlineDomNode,
24
+ type LexicalEditor,
25
+ } from 'lexical';
26
+
27
+ import {DOMImportContextSymbol, DOMImportExtensionName} from '../constants';
28
+ import {
29
+ $withContext,
30
+ createContextState,
31
+ getContextRecord,
32
+ getContextValue,
33
+ } from '../ContextRecord';
34
+
35
+ type ImportContextRecord = ContextRecord<typeof DOMImportContextSymbol>;
36
+
37
+ /**
38
+ * Create an import context state. The phantom symbol prevents accidental
39
+ * use of a render-context state in an import context (and vice versa).
40
+ *
41
+ * Note: to support the value-or-updater pattern, `V` cannot be a function
42
+ * type; wrap it in an array or object if needed.
43
+ *
44
+ * `getDefaultValue` is called **once at state creation** and the result is
45
+ * shared between every session that reads the state without first writing
46
+ * a value. Defaults must therefore be immutable (primitives, frozen
47
+ * objects, or read-only arrays / records). If your state needs mutable
48
+ * per-session storage, lazily initialize it inside your rule (e.g.
49
+ * `if (!ctx.session.has(cfg)) ctx.session.set(cfg, new …())`).
50
+ *
51
+ * @experimental
52
+ * @__NO_SIDE_EFFECTS__
53
+ */
54
+ export function createImportState<V>(
55
+ name: string,
56
+ getDefaultValue: () => V,
57
+ isEqual?: (a: V, b: V) => boolean,
58
+ ): ImportStateConfig<V> {
59
+ return createContextState(
60
+ DOMImportContextSymbol,
61
+ name,
62
+ getDefaultValue,
63
+ isEqual,
64
+ );
65
+ }
66
+
67
+ /**
68
+ * The kind of operation that produced this import. Lets rules adapt
69
+ * their behavior (e.g. preserve more whitespace on `'paste'`).
70
+ * Defaults to `'unknown'`. Apps that need a different vocabulary can
71
+ * define their own {@link ImportStateConfig} with whatever value type
72
+ * they want.
73
+ *
74
+ * @experimental
75
+ */
76
+ export type ImportSourceKind = 'paste' | 'unknown';
77
+
78
+ /**
79
+ * Built-in import-context state identifying how this import was initiated.
80
+ * Callers of `$generateNodesFromDOM` should set it via the `context` option.
81
+ *
82
+ * @experimental
83
+ */
84
+ export const ImportSource: ImportStateConfig<ImportSourceKind> =
85
+ createImportState<ImportSourceKind>('importSource', () => 'unknown');
86
+
87
+ /**
88
+ * Built-in import-context state holding the {@link DataTransfer} the
89
+ * import was sourced from, if any. `null` outside paste/drop flows.
90
+ *
91
+ * The clipboard import pipeline passes the original `DataTransfer`
92
+ * through to its per-MIME-type handler stack (see
93
+ * {@link ImportMimeTypeFunction}); handlers that route HTML through
94
+ * the {@link DOMImportExtension} pipeline should forward it into the
95
+ * walk via `context: [contextValue(ImportSourceDataTransfer,
96
+ * dataTransfer)]` so rules and preprocessors can call
97
+ * `ctx.get(ImportSourceDataTransfer)` to inspect companion MIME types
98
+ * (e.g. an `'application/rtf'` alternative or an attached
99
+ * `'application/x-officedrawing'` payload), the file list, or any
100
+ * custom drag-and-drop slot.
101
+ *
102
+ * Use sparingly: the safer pattern is to decide *which* MIME-type
103
+ * payload to walk in the clipboard handler stack and hand a finalized
104
+ * DOM to the rules; only fall back to peeking at `ImportSourceDataTransfer`
105
+ * when the source-detection signal genuinely lives in a companion
106
+ * slot.
107
+ *
108
+ * @experimental
109
+ */
110
+ export const ImportSourceDataTransfer: ImportStateConfig<DataTransfer | null> =
111
+ createImportState<DataTransfer | null>(
112
+ 'importSourceDataTransfer',
113
+ () => null,
114
+ );
115
+
116
+ /**
117
+ * Built-in import-context state holding the bit-packed
118
+ * {@link TextFormatType} formats that should apply to {@link TextNode}s
119
+ * produced during the current subtree. Used by inline-format wrappers
120
+ * (`<b>`, `<i>`, `<u>`, …) to propagate formatting through the context
121
+ * record instead of via the legacy `forChild` chain.
122
+ *
123
+ * @experimental
124
+ */
125
+ export const ImportTextFormat: ImportStateConfig<number> = createImportState(
126
+ 'textFormat',
127
+ () => 0,
128
+ );
129
+
130
+ /**
131
+ * Built-in import-context state holding a parsed CSS-style record
132
+ * (the {@link getStyleObjectFromCSS} shape) that should apply to
133
+ * {@link TextNode}s produced during the current subtree. Mirrors the
134
+ * format-bit propagation in {@link ImportTextFormat} for properties
135
+ * that don't fit into the format bit mask — `color`, `font-family`,
136
+ * `font-size`, etc.
137
+ *
138
+ * Ancestor rules that contribute a style branch the context with a
139
+ * merged record; the core `#text` rule materializes the non-empty
140
+ * record to a CSS string and calls `setStyle` on the new TextNode.
141
+ * Once TextNode adopts a parsed style record, the materialization
142
+ * step will go away.
143
+ *
144
+ * @experimental
145
+ */
146
+ export const ImportTextStyle: ImportStateConfig<
147
+ Readonly<Record<string, string>>
148
+ > = createImportState<Readonly<Record<string, string>>>(
149
+ 'textStyle',
150
+ () => ({}),
151
+ );
152
+
153
+ /**
154
+ * Determines whether a given DOM element should be treated as preserving
155
+ * whitespace (i.e. text content under it is not collapsed and is split on
156
+ * `\n` / `\t` into `LineBreakNode` / `TabNode`). The default matches the
157
+ * legacy behavior: the element itself is `<pre>` or its inline
158
+ * `white-space` style begins with `'pre'`.
159
+ *
160
+ * @experimental
161
+ */
162
+ export type IsPreserveWhitespaceDom = (node: Node) => boolean;
163
+
164
+ /**
165
+ * Determines whether a given DOM node sits on the same visual line as its
166
+ * adjacent text siblings, governing whether leading/trailing whitespace in
167
+ * a `#text` is collapsed against neighbors. The default consults
168
+ * {@link isInlineDomNode} from `lexical` (style.display or a fixed inline
169
+ * tag-name set) and additionally treats elements with an explicit
170
+ * non-inline `display` style as block.
171
+ *
172
+ * @experimental
173
+ */
174
+ export type IsInlineForWhitespace = (node: Node) => boolean;
175
+
176
+ /**
177
+ * Configuration for the core text whitespace-collapse logic. Override via
178
+ * {@link ImportWhitespaceConfig} either as a `contextDefaults` entry on
179
+ * the {@link DOMImportExtension} or per-call on `$generateNodesFromDOM`'s
180
+ * `context` option.
181
+ *
182
+ * @experimental
183
+ */
184
+ export interface WhitespaceImportConfig {
185
+ /** See {@link IsPreserveWhitespaceDom}. */
186
+ readonly preservesWhitespace: IsPreserveWhitespaceDom;
187
+ /** See {@link IsInlineForWhitespace}. */
188
+ readonly isInline: IsInlineForWhitespace;
189
+ }
190
+
191
+ /**
192
+ * Default {@link WhitespaceImportConfig.preservesWhitespace}: matches
193
+ * `<pre>` and any element with `white-space: pre*`.
194
+ *
195
+ * @experimental
196
+ */
197
+ export function defaultPreservesWhitespace(node: Node): boolean {
198
+ if (!isHTMLElement(node)) {
199
+ return false;
200
+ }
201
+ if (node.nodeName === 'PRE') {
202
+ return true;
203
+ }
204
+ const ws = node.style.whiteSpace;
205
+ return typeof ws === 'string' && ws.startsWith('pre');
206
+ }
207
+
208
+ /**
209
+ * Default {@link WhitespaceImportConfig.isInline}: treats an element as
210
+ * inline iff its inline `display` style is `inline*` OR (no explicit
211
+ * non-inline display) its nodeName is a known inline tag (`isInlineDomNode`).
212
+ * Text nodes are always inline; comments and other non-elements are not.
213
+ *
214
+ * @experimental
215
+ */
216
+ export function defaultIsInline(node: Node): boolean {
217
+ if (isDOMTextNode(node)) {
218
+ return true;
219
+ }
220
+ if (!isHTMLElement(node)) {
221
+ return false;
222
+ }
223
+ const display = node.style.display;
224
+ if (display) {
225
+ return display.startsWith('inline');
226
+ }
227
+ if (isBlockDomNode(node)) {
228
+ return false;
229
+ }
230
+ return isInlineDomNode(node);
231
+ }
232
+
233
+ /**
234
+ * Built-in import-context state controlling text-node whitespace handling
235
+ * (collapse vs. preserve, what counts as an inline sibling). Override per
236
+ * editor via {@link DOMImportConfig.contextDefaults} or per call via
237
+ * {@link GenerateNodesFromDOMOptions.context}.
238
+ *
239
+ * @experimental
240
+ */
241
+ export const ImportWhitespaceConfig: ImportStateConfig<WhitespaceImportConfig> =
242
+ createImportState<WhitespaceImportConfig>('whitespaceConfig', () => ({
243
+ isInline: defaultIsInline,
244
+ preservesWhitespace: defaultPreservesWhitespace,
245
+ }));
246
+
247
+ /**
248
+ * Built-in session slot for runtime overlay rules that should be in
249
+ * effect for the entire walk. A preprocessor writes here when it wants
250
+ * to conditionally install handling for a particular paste source
251
+ * (e.g. "if the Microsoft Word generator meta tag is present, push the
252
+ * Word-paste overlay"). Each entry contributes an overlay dispatcher
253
+ * to the runtime's overlay stack; later array entries are higher
254
+ * priority. Use `ctx.session.update(ImportOverlays, prev => […])` to
255
+ * append.
256
+ *
257
+ * This is the walk-wide counterpart to
258
+ * `$importChildren({rules: …})` (which scopes an overlay to one
259
+ * subtree): write to {@link ImportOverlays} when the overlay should
260
+ * apply for the whole document; use `$importChildren`'s `rules` when
261
+ * the overlay should only apply for a deeper region.
262
+ *
263
+ * @experimental
264
+ */
265
+ export const ImportOverlays: ImportStateConfig<
266
+ readonly CompiledOverlayRules[]
267
+ > = createImportState<readonly CompiledOverlayRules[]>(
268
+ 'importOverlays',
269
+ () => [],
270
+ );
271
+
272
+ /**
273
+ * The session IS the root-layer {@link ContextRecord} of the walk. Reads
274
+ * fall through the prototype chain to the editor's `contextDefaults`,
275
+ * writes mutate the record's own properties, and any branch pushed by
276
+ * `$importChildren({context})` sits above this layer and can shadow
277
+ * (but does not overwrite) slots.
278
+ *
279
+ * @internal
280
+ */
281
+ export class ImportSessionImpl implements ImportSession {
282
+ constructor(readonly record: ImportContextRecord) {}
283
+ get<V>(cfg: ImportStateConfig<V>): V {
284
+ return getContextValue(this.record, cfg);
285
+ }
286
+ set<V>(cfg: ImportStateConfig<V>, value: V): void {
287
+ this.record[cfg.key] = value;
288
+ }
289
+ update<V>(cfg: ImportStateConfig<V>, updater: (prev: V) => V): void {
290
+ this.record[cfg.key] = updater(getContextValue(this.record, cfg));
291
+ }
292
+ has<V>(cfg: ImportStateConfig<V>): boolean {
293
+ return Object.prototype.hasOwnProperty.call(this.record, cfg.key);
294
+ }
295
+ }
296
+
297
+ function getDefaultImportContext(
298
+ editor: LexicalEditor,
299
+ ): undefined | ContextRecord<typeof DOMImportContextSymbol> {
300
+ const dep = getPeerDependencyFromEditor<typeof DOMImportExtension>(
301
+ editor,
302
+ DOMImportExtensionName,
303
+ );
304
+ return dep ? dep.output.defaults : undefined;
305
+ }
306
+
307
+ function getImportContext(
308
+ editor: LexicalEditor,
309
+ ): undefined | ContextRecord<typeof DOMImportContextSymbol> {
310
+ return (
311
+ getContextRecord(DOMImportContextSymbol, editor) ||
312
+ getDefaultImportContext(editor)
313
+ );
314
+ }
315
+
316
+ /**
317
+ * Read an import context value during an import operation.
318
+ * @experimental
319
+ */
320
+ export function $getImportContextValue<V>(
321
+ cfg: ImportStateConfig<V>,
322
+ editor: LexicalEditor = $getEditor(),
323
+ ): V {
324
+ return getContextValue(getImportContext(editor), cfg);
325
+ }
326
+
327
+ /**
328
+ * Run `f` with the given context pairs applied on top of the editor's
329
+ * current import context.
330
+ *
331
+ * @experimental
332
+ */
333
+ export const $withImportContext: (
334
+ cfg: readonly ImportContextPairOrUpdater[],
335
+ editor?: LexicalEditor,
336
+ ) => <T>(f: () => T) => T = $withContext(
337
+ DOMImportContextSymbol,
338
+ getDefaultImportContext,
339
+ );