@mx-space/cli 0.7.8 → 0.9.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 (68) hide show
  1. package/README.md +30 -0
  2. package/ROADMAP.md +1 -0
  3. package/dist/{Lexical.dev-rcxzwpir.mjs → Lexical.dev-By5dOKBN.mjs} +1486 -356
  4. package/dist/{Lexical.node-BUsI9gWr.mjs → Lexical.node-fXM8Jp7p.mjs} +39 -17
  5. package/dist/{Lexical.prod-C8HtkSD6.mjs → Lexical.prod-BlCvXPGa.mjs} +2279 -1768
  6. package/dist/LexicalClipboard.dev--HxGoSkO.mjs +915 -0
  7. package/dist/{LexicalClipboard.node-CO-MCKx5.mjs → LexicalClipboard.node-C3mRbrmW.mjs} +10 -3
  8. package/dist/LexicalClipboard.prod-DNJzFMcT.mjs +371 -0
  9. package/dist/LexicalCodeCore.dev-DjSrd4A4.mjs +1109 -0
  10. package/dist/LexicalCodeCore.prod-DTDShkR3.mjs +642 -0
  11. package/dist/{LexicalDragon.dev-gBP8WSRS.mjs → LexicalDragon.dev-DwprRH0z.mjs} +3 -3
  12. package/dist/{LexicalDragon.node-OG6G55nl.mjs → LexicalDragon.node-BVhw4C7s.mjs} +2 -2
  13. package/dist/{LexicalDragon.prod-DP-_Q8cv.mjs → LexicalDragon.prod-nh4J-8vn.mjs} +3 -3
  14. package/dist/{LexicalExtension.dev-DVbjljrm.mjs → LexicalExtension.dev-BPNyvGqe.mjs} +335 -6
  15. package/dist/{LexicalExtension.node-CF3EAPN7.mjs → LexicalExtension.node-BVlcrgOx.mjs} +15 -8
  16. package/dist/{LexicalExtension.prod-DklHHErZ.mjs → LexicalExtension.prod-DFKbiNt-.mjs} +375 -265
  17. package/dist/{LexicalHeadless.dev-DdWLM7Nl.mjs → LexicalHeadless.dev-B-cj7Pr5.mjs} +2 -2
  18. package/dist/{LexicalHeadless.prod-CoiiTDzR.mjs → LexicalHeadless.prod-Byp1vNzT.mjs} +2 -2
  19. package/dist/LexicalHtml.dev-prBAwszN.mjs +2424 -0
  20. package/dist/LexicalHtml.node-BvNF96jZ.mjs +56 -0
  21. package/dist/LexicalHtml.prod-Sz3CW1ht.mjs +1207 -0
  22. package/dist/{LexicalLink.dev-BS1Bf-yD.mjs → LexicalLink.dev-NySTYhVR.mjs} +42 -8
  23. package/dist/{LexicalLink.prod-B_crkoHP.mjs → LexicalLink.prod-BuCM8gts.mjs} +209 -191
  24. package/dist/{LexicalList.dev-OMqmcnQ-.mjs → LexicalList.dev-DIMOvSFD.mjs} +252 -80
  25. package/dist/{LexicalList.prod-CpL1s8vS.mjs → LexicalList.prod-MVqfA6Vo.mjs} +364 -278
  26. package/dist/{LexicalMarkdown.dev-BHSdx_Gr.mjs → LexicalMarkdown.dev-C7aylvCG.mjs} +729 -552
  27. package/dist/LexicalMarkdown.prod-CfQSM406.mjs +868 -0
  28. package/dist/{LexicalRichText.dev-Bz9A_WbM.mjs → LexicalRichText.dev-DfICq6lU.mjs} +272 -77
  29. package/dist/{LexicalRichText.node-DDpwfE-P.mjs → LexicalRichText.node-Bg9jND3Q.mjs} +21 -8
  30. package/dist/LexicalRichText.prod-GsFzCK-o.mjs +550 -0
  31. package/dist/{LexicalSelection.dev-CQ380fnD.mjs → LexicalSelection.dev-DGjj12wW.mjs} +50 -38
  32. package/dist/{LexicalSelection.node-DesBMjIE.mjs → LexicalSelection.node-BU5L4193.mjs} +4 -4
  33. package/dist/{LexicalSelection.prod-D2jivCDB.mjs → LexicalSelection.prod-D-PwRbyK.mjs} +158 -140
  34. package/dist/{LexicalTable.dev-Ca7mQ7Jk.mjs → LexicalTable.dev-ChP7cugd.mjs} +215 -53
  35. package/dist/{LexicalTable.prod-DyPAcC7y.mjs → LexicalTable.prod-Bpr9YRXp.mjs} +668 -573
  36. package/dist/{LexicalUtils.dev-HxM8F0g6.mjs → LexicalUtils.dev-BiFU3789.mjs} +10 -47
  37. package/dist/{LexicalUtils.node-lCWjRjoG.mjs → LexicalUtils.node-j3Llm7Y3.mjs} +2 -2
  38. package/dist/{LexicalUtils.prod-Bti5w4ki.mjs → LexicalUtils.prod-DCvrA78p.mjs} +103 -105
  39. package/dist/bin/mxs.d.mts +1 -1
  40. package/dist/bin/mxs.mjs +1 -1
  41. package/dist/esm-By7trG9K.mjs +2 -0
  42. package/dist/{esm-CK9m5JKX.mjs → esm-Dg-WhPCe.mjs} +989 -989
  43. package/dist/index.d.mts +11 -2
  44. package/dist/index.mjs +1 -1
  45. package/dist/{mxs-BZt1cLW9.mjs → mxs-BR5Y5ash.mjs} +25141 -24672
  46. package/package.json +15 -14
  47. package/skills/commands-auth.md +1 -1
  48. package/skills/commands-category.md +1 -1
  49. package/skills/commands-comment.md +1 -1
  50. package/skills/commands-config.md +1 -1
  51. package/skills/commands-preview.md +1 -1
  52. package/skills/commands-profile.md +1 -1
  53. package/skills/commands-project.md +57 -0
  54. package/skills/commands-topic.md +1 -1
  55. package/skills/output-modes.md +1 -1
  56. package/skills/overview.md +2 -1
  57. package/skills/safety.md +10 -0
  58. package/dist/LexicalClipboard.dev-BpqRwTLP.mjs +0 -494
  59. package/dist/LexicalClipboard.prod-B_xdeiAC.mjs +0 -291
  60. package/dist/LexicalCodeCore.dev-7_cexm39.mjs +0 -537
  61. package/dist/LexicalCodeCore.prod-Bd6WFHcO.mjs +0 -381
  62. package/dist/LexicalHtml.dev-C3ZSWBFZ.mjs +0 -684
  63. package/dist/LexicalHtml.node-D0TD152E.mjs +0 -24
  64. package/dist/LexicalHtml.prod-CX47X0zl.mjs +0 -430
  65. package/dist/LexicalMarkdown.prod-C-S7Ii6-.mjs +0 -754
  66. package/dist/LexicalRichText.prod-C8qXACDL.mjs +0 -427
  67. package/dist/esm-DRbkkY3r.mjs +0 -2
  68. /package/dist/{mxs-CM8-6uAr.d.mts → mxs-iqJBopvE.d.mts} +0 -0
@@ -0,0 +1,1109 @@
1
+ import { d as getPeerDependencyFromEditor, f as namedSignals, l as effect } from "./LexicalExtension.node-BVlcrgOx.mjs";
2
+ import { $ as $isElementNode, $t as ElementNode, An as KEY_ARROW_UP_COMMAND, Ar as removeClassNamesFromElement, Bn as OUTDENT_CONTENT_COMMAND, Bt as COMMAND_PRIORITY_LOW, D as $getCaretRangeInDirection, Dn as KEY_ARROW_DOWN_COMMAND, E as $getCaretRange, F as $getEditor, In as KEY_TAB_COMMAND, J as $insertNodes, K as $getTextPointCaret, Mr as setDOMStyleFromCSS, Nn as KEY_ENTER_COMMAND, Or as mergeRegister, Rn as MOVE_TO_END, Sr as isHTMLElement, Tt as $setSelectionFromCaretRange, U as $getSelection, W as $getSiblingCaret, _r as isDOMDocumentNode, cr as defineExtension, dt as $isTabNode, er as TextNode, f as $createParagraphNode, ft as $isTextNode, g as $createTabNode, ht as $normalizeCaret, it as $isLineBreakNode, jr as safeCast, l as $create, ln as INSERT_TAB_COMMAND, mr as getTextDirection, n as $applyNodeReplacement, on as INDENT_CONTENT_COMMAND, p as $createPoint, rr as configExtension, st as $isRangeSelection, tr as addClassNamesToElement, u as $createLineBreakNode, x as $generateNodesFromRawText, yr as isDOMTextNode, zn as MOVE_TO_START } from "./Lexical.node-fXM8Jp7p.mjs";
3
+ import { a as CoreImportExtension, g as sel, m as defineOverlayRules, o as DOMImportExtension, p as defineImportRule, s as ImportOverlays } from "./LexicalHtml.node-BvNF96jZ.mjs";
4
+ //#region ../../node_modules/.pnpm/@lexical+code-core@0.45.0/node_modules/@lexical/code-core/dist/LexicalCodeCore.dev.mjs
5
+ /**
6
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
7
+ *
8
+ * This source code is licensed under the MIT license found in the
9
+ * LICENSE file in the root directory of this source tree.
10
+ *
11
+ */
12
+ /**
13
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14
+ *
15
+ * This source code is licensed under the MIT license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ *
18
+ */
19
+ function warnOnlyOnce(message) {
20
+ {
21
+ let run = false;
22
+ return () => {
23
+ if (!run) console.warn(message);
24
+ run = true;
25
+ };
26
+ }
27
+ }
28
+ /**
29
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
30
+ *
31
+ * This source code is licensed under the MIT license found in the
32
+ * LICENSE file in the root directory of this source tree.
33
+ *
34
+ */
35
+ function formatDevErrorMessage(message) {
36
+ throw new Error(message);
37
+ }
38
+ function $getLastMatchingCodeNode(anchor, direction) {
39
+ let matchingNode = anchor;
40
+ for (let caret = $getSiblingCaret(anchor, direction); caret && ($isCodeHighlightNode(caret.origin) || $isTabNode(caret.origin)); caret = caret.getAdjacentCaret()) matchingNode = caret.origin;
41
+ return matchingNode;
42
+ }
43
+ function $getFirstCodeNodeOfLine(anchor) {
44
+ return $getLastMatchingCodeNode(anchor, "previous");
45
+ }
46
+ function $getLastCodeNodeOfLine(anchor) {
47
+ return $getLastMatchingCodeNode(anchor, "next");
48
+ }
49
+ /**
50
+ * Determines the visual writing direction of a code line.
51
+ *
52
+ * Scans the line segments (CodeHighlightNode/TabNode) from start to end
53
+ * and returns the first strong direction found ("ltr" or "rtl").
54
+ * If no strong character is found, falls back to the parent element's
55
+ * direction. Returns null if indeterminate.
56
+ */
57
+ function $getCodeLineDirection(anchor) {
58
+ const start = $getFirstCodeNodeOfLine(anchor);
59
+ const end = $getLastCodeNodeOfLine(anchor);
60
+ let node = start;
61
+ while (node !== null) {
62
+ if ($isCodeHighlightNode(node)) {
63
+ const direction = getTextDirection(node.getTextContent());
64
+ if (direction !== null) return direction;
65
+ }
66
+ if (node === end) break;
67
+ node = node.getNextSibling();
68
+ }
69
+ const parent = start.getParent();
70
+ if ($isElementNode(parent)) {
71
+ const parentDirection = parent.getDirection();
72
+ if (parentDirection === "ltr" || parentDirection === "rtl") return parentDirection;
73
+ }
74
+ return null;
75
+ }
76
+ function $getStartOfCodeInLine(anchor, offset) {
77
+ let last = null;
78
+ let lastNonBlank = null;
79
+ let node = anchor;
80
+ let nodeOffset = offset;
81
+ let nodeTextContent = anchor.getTextContent();
82
+ while (true) {
83
+ if (nodeOffset === 0) {
84
+ node = node.getPreviousSibling();
85
+ if (node === null) break;
86
+ if (!($isCodeHighlightNode(node) || $isTabNode(node) || $isLineBreakNode(node))) formatDevErrorMessage(`Expected a valid Code Node: CodeHighlightNode, TabNode, LineBreakNode`);
87
+ if ($isLineBreakNode(node)) {
88
+ last = {
89
+ node,
90
+ offset: 1
91
+ };
92
+ break;
93
+ }
94
+ nodeOffset = Math.max(0, node.getTextContentSize() - 1);
95
+ nodeTextContent = node.getTextContent();
96
+ } else nodeOffset--;
97
+ const character = nodeTextContent[nodeOffset];
98
+ if ($isCodeHighlightNode(node) && character !== " ") lastNonBlank = {
99
+ node,
100
+ offset: nodeOffset
101
+ };
102
+ }
103
+ if (lastNonBlank !== null) return lastNonBlank;
104
+ let codeCharacterAtAnchorOffset = null;
105
+ if (offset < anchor.getTextContentSize()) {
106
+ if ($isCodeHighlightNode(anchor)) codeCharacterAtAnchorOffset = anchor.getTextContent()[offset];
107
+ } else {
108
+ const nextSibling = anchor.getNextSibling();
109
+ if ($isCodeHighlightNode(nextSibling)) codeCharacterAtAnchorOffset = nextSibling.getTextContent()[0];
110
+ }
111
+ if (codeCharacterAtAnchorOffset !== null && codeCharacterAtAnchorOffset !== " ") return last;
112
+ else {
113
+ const nextNonBlank = findNextNonBlankInLine(anchor, offset);
114
+ if (nextNonBlank !== null) return nextNonBlank;
115
+ else return last;
116
+ }
117
+ }
118
+ function findNextNonBlankInLine(anchor, offset) {
119
+ let node = anchor;
120
+ let nodeOffset = offset;
121
+ let nodeTextContent = anchor.getTextContent();
122
+ let nodeTextContentSize = anchor.getTextContentSize();
123
+ while (true) {
124
+ if (!$isCodeHighlightNode(node) || nodeOffset === nodeTextContentSize) {
125
+ node = node.getNextSibling();
126
+ if (node === null || $isLineBreakNode(node)) return null;
127
+ if ($isCodeHighlightNode(node)) {
128
+ nodeOffset = 0;
129
+ nodeTextContent = node.getTextContent();
130
+ nodeTextContentSize = node.getTextContentSize();
131
+ }
132
+ }
133
+ if ($isCodeHighlightNode(node)) {
134
+ if (nodeTextContent[nodeOffset] !== " ") return {
135
+ node,
136
+ offset: nodeOffset
137
+ };
138
+ nodeOffset++;
139
+ }
140
+ }
141
+ }
142
+ function $getEndOfCodeInLine(anchor) {
143
+ const lastNode = $getLastCodeNodeOfLine(anchor);
144
+ if (!!$isLineBreakNode(lastNode)) formatDevErrorMessage(`Unexpected lineBreakNode in getEndOfCodeInLine`);
145
+ return lastNode;
146
+ }
147
+ /**
148
+ * Plain split of code text into CodeHighlightNodes (with no highlight
149
+ * type) + LineBreakNodes + TabNodes. Used when the tokenizer opts out
150
+ * of a default language so a previously highlighted block still
151
+ * renders its `\n` / `\t` as real line breaks / tabs, while staying
152
+ * compatible with the indent / shift-lines handlers that only accept
153
+ * CodeHighlightNode + TabNode + LineBreakNode inside a CodeNode.
154
+ */
155
+ function $plainifyCodeContent(text) {
156
+ const out = [];
157
+ text.split("\n").forEach((line, lineIdx) => {
158
+ if (lineIdx > 0) out.push($createLineBreakNode());
159
+ line.split(" ").forEach((part, partIdx) => {
160
+ if (partIdx > 0) out.push($createTabNode());
161
+ if (part.length > 0) out.push($createCodeHighlightNode(part));
162
+ });
163
+ });
164
+ return out;
165
+ }
166
+ /**
167
+ * Strip up to `tabSize` leading spaces from a {@link CodeHighlightNode} that
168
+ * starts a code line, to support outdenting space-indented code lines (e.g.
169
+ * code formatted with prettier). Returns true if any spaces were stripped.
170
+ *
171
+ * Best-effort: a line with fewer than `tabSize` leading spaces has all of
172
+ * them stripped, matching VS Code / IntelliJ behavior.
173
+ *
174
+ * Selection is preserved relative to line content. Anchor/focus offsets
175
+ * pointing into `node` shift left by the number of stripped characters
176
+ * (clamped to 0). The underlying TextNode mutation does not adjust
177
+ * selection offsets that already point into the old text, so we patch
178
+ * them up explicitly.
179
+ */
180
+ function $outdentLeadingSpaces(node, tabSize, selection) {
181
+ if (!Number.isInteger(tabSize) || tabSize <= 0) return false;
182
+ const text = node.getTextContent();
183
+ const leading = /^ +/.exec(text);
184
+ if (!leading) return false;
185
+ const stripCount = Math.min(tabSize, leading[0].length);
186
+ const lineKey = node.getKey();
187
+ const oldAnchorOffset = selection.anchor.key === lineKey && selection.anchor.type === "text" ? selection.anchor.offset : null;
188
+ const oldFocusOffset = selection.focus.key === lineKey && selection.focus.type === "text" ? selection.focus.offset : null;
189
+ node.spliceText(0, stripCount, "");
190
+ if (oldAnchorOffset !== null) selection.anchor.set(lineKey, Math.max(0, oldAnchorOffset - stripCount), "text");
191
+ if (oldFocusOffset !== null) selection.focus.set(lineKey, Math.max(0, oldFocusOffset - stripCount), "text");
192
+ return true;
193
+ }
194
+ /**
195
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
196
+ *
197
+ * This source code is licensed under the MIT license found in the
198
+ * LICENSE file in the root directory of this source tree.
199
+ *
200
+ */
201
+ const DEFAULT_CODE_LANGUAGE = "javascript";
202
+ /** @internal Configurable through the extensions. */
203
+ const getDefaultCodeLanguage = () => DEFAULT_CODE_LANGUAGE;
204
+ function hasChildDOMNodeTag(node, tagName) {
205
+ for (const child of node.childNodes) {
206
+ if (isHTMLElement(child) && child.tagName === tagName) return true;
207
+ if (hasChildDOMNodeTag(child, tagName)) return true;
208
+ }
209
+ return false;
210
+ }
211
+ const LANGUAGE_DATA_ATTRIBUTE$1 = "data-language";
212
+ const HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE = "data-highlight-language";
213
+ const THEME_DATA_ATTRIBUTE = "data-theme";
214
+ const noExtensionDeprecation = warnOnlyOnce("Using CodeNode without CodeExtension is deprecated");
215
+ /** @noInheritDoc */
216
+ var CodeNode = class CodeNode extends ElementNode {
217
+ /** @internal */
218
+ __language;
219
+ /** @internal */
220
+ __theme;
221
+ /** @internal */
222
+ __isSyntaxHighlightSupported;
223
+ static getType() {
224
+ return "code";
225
+ }
226
+ static clone(node) {
227
+ return new CodeNode(node.__language, node.__key);
228
+ }
229
+ constructor(language, key) {
230
+ super(key);
231
+ this.__language = language || void 0;
232
+ this.__isSyntaxHighlightSupported = false;
233
+ this.__theme = void 0;
234
+ }
235
+ afterCloneFrom(prevNode) {
236
+ super.afterCloneFrom(prevNode);
237
+ this.__language = prevNode.__language;
238
+ this.__theme = prevNode.__theme;
239
+ this.__isSyntaxHighlightSupported = prevNode.__isSyntaxHighlightSupported;
240
+ }
241
+ createDOM(config) {
242
+ const element = document.createElement("code");
243
+ addClassNamesToElement(element, config.theme.code);
244
+ element.setAttribute("spellcheck", "false");
245
+ const language = this.getLanguage();
246
+ if (language) {
247
+ element.setAttribute(LANGUAGE_DATA_ATTRIBUTE$1, language);
248
+ if (this.getIsSyntaxHighlightSupported()) element.setAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE, language);
249
+ }
250
+ const theme = this.getTheme();
251
+ if (theme) element.setAttribute(THEME_DATA_ATTRIBUTE, theme);
252
+ const style = this.getStyle();
253
+ if (style) setDOMStyleFromCSS(element.style, style);
254
+ return element;
255
+ }
256
+ updateDOM(prevNode, dom, config) {
257
+ const language = this.__language;
258
+ const prevLanguage = prevNode.__language;
259
+ if (language) {
260
+ if (language !== prevLanguage) dom.setAttribute(LANGUAGE_DATA_ATTRIBUTE$1, language);
261
+ } else if (prevLanguage) dom.removeAttribute(LANGUAGE_DATA_ATTRIBUTE$1);
262
+ const isSyntaxHighlightSupported = this.__isSyntaxHighlightSupported;
263
+ if (prevNode.__isSyntaxHighlightSupported && prevLanguage) if (isSyntaxHighlightSupported && language) {
264
+ if (language !== prevLanguage) dom.setAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE, language);
265
+ } else dom.removeAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE);
266
+ else if (isSyntaxHighlightSupported && language) dom.setAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE, language);
267
+ const theme = this.__theme;
268
+ const prevTheme = prevNode.__theme;
269
+ if (theme) {
270
+ if (theme !== prevTheme) dom.setAttribute(THEME_DATA_ATTRIBUTE, theme);
271
+ } else if (prevTheme) dom.removeAttribute(THEME_DATA_ATTRIBUTE);
272
+ const style = this.__style;
273
+ const prevStyle = prevNode.__style;
274
+ if (style !== prevStyle) setDOMStyleFromCSS(dom.style, style, prevStyle);
275
+ return false;
276
+ }
277
+ exportDOM(editor) {
278
+ const element = document.createElement("pre");
279
+ addClassNamesToElement(element, editor._config.theme.code);
280
+ element.setAttribute("spellcheck", "false");
281
+ const language = this.getLanguage();
282
+ if (language) {
283
+ element.setAttribute(LANGUAGE_DATA_ATTRIBUTE$1, language);
284
+ if (this.getIsSyntaxHighlightSupported()) element.setAttribute(HIGHLIGHT_LANGUAGE_DATA_ATTRIBUTE, language);
285
+ }
286
+ const theme = this.getTheme();
287
+ if (theme) element.setAttribute(THEME_DATA_ATTRIBUTE, theme);
288
+ const style = this.getStyle();
289
+ if (style) setDOMStyleFromCSS(element.style, style);
290
+ return { element };
291
+ }
292
+ static importDOM() {
293
+ return {
294
+ code: (node) => {
295
+ return node.textContent != null && (/\r?\n/.test(node.textContent) || hasChildDOMNodeTag(node, "BR")) ? {
296
+ conversion: $convertPreElement,
297
+ priority: 1
298
+ } : null;
299
+ },
300
+ div: () => ({
301
+ conversion: $convertDivElement,
302
+ priority: 1
303
+ }),
304
+ pre: () => ({
305
+ conversion: $convertPreElement,
306
+ priority: 0
307
+ }),
308
+ table: (node) => {
309
+ if (isGitHubCodeTable(node)) return {
310
+ conversion: $convertTableElement,
311
+ priority: 3
312
+ };
313
+ return null;
314
+ },
315
+ td: (node) => {
316
+ const td = node;
317
+ const table = td.closest("table");
318
+ if (isGitHubCodeCell(td) || table && isGitHubCodeTable(table)) return {
319
+ conversion: convertCodeNoop,
320
+ priority: 3
321
+ };
322
+ return null;
323
+ },
324
+ tr: (node) => {
325
+ const table = node.closest("table");
326
+ if (table && isGitHubCodeTable(table)) return {
327
+ conversion: convertCodeNoop,
328
+ priority: 3
329
+ };
330
+ return null;
331
+ }
332
+ };
333
+ }
334
+ static importJSON(serializedNode) {
335
+ return $createCodeNode().updateFromJSON(serializedNode);
336
+ }
337
+ updateFromJSON(serializedNode) {
338
+ return super.updateFromJSON(serializedNode).setLanguage(serializedNode.language).setTheme(serializedNode.theme);
339
+ }
340
+ exportJSON() {
341
+ return {
342
+ ...super.exportJSON(),
343
+ language: this.getLanguage(),
344
+ theme: this.getTheme()
345
+ };
346
+ }
347
+ insertNewAfter(selection, restoreSelection = true) {
348
+ if (!getPeerDependencyFromEditor($getEditor(), "@lexical/code")) {
349
+ noExtensionDeprecation();
350
+ const el = $exitCodeNodeOnEnter(selection);
351
+ if (el) return el;
352
+ }
353
+ const { anchor, focus } = selection;
354
+ const firstSelectionNode = (anchor.isBefore(focus) ? anchor : focus).getNode();
355
+ if ($isTextNode(firstSelectionNode)) {
356
+ let node = $getFirstCodeNodeOfLine(firstSelectionNode);
357
+ const insertNodes = [];
358
+ while (true) if ($isTabNode(node)) {
359
+ insertNodes.push($createTabNode());
360
+ node = node.getNextSibling();
361
+ } else if ($isCodeHighlightNode(node)) {
362
+ let spaces = 0;
363
+ const text = node.getTextContent();
364
+ const textSize = node.getTextContentSize();
365
+ while (spaces < textSize && text[spaces] === " ") spaces++;
366
+ if (spaces !== 0) insertNodes.push($createCodeHighlightNode(" ".repeat(spaces)));
367
+ if (spaces !== textSize) break;
368
+ node = node.getNextSibling();
369
+ } else break;
370
+ const split = firstSelectionNode.splitText(anchor.offset)[0];
371
+ const x = anchor.offset === 0 ? 0 : 1;
372
+ const index = split.getIndexWithinParent() + x;
373
+ const codeNode = firstSelectionNode.getParentOrThrow();
374
+ const nodesToInsert = [$createLineBreakNode(), ...insertNodes];
375
+ codeNode.splice(index, 0, nodesToInsert);
376
+ const last = insertNodes[insertNodes.length - 1];
377
+ if (last) last.select();
378
+ else if (anchor.offset === 0) split.selectPrevious();
379
+ else split.getNextSibling().selectNext(0, 0);
380
+ }
381
+ if ($isCodeNode(firstSelectionNode)) {
382
+ const { offset } = selection.anchor;
383
+ firstSelectionNode.splice(offset, 0, [$createLineBreakNode()]);
384
+ firstSelectionNode.select(offset + 1, offset + 1);
385
+ }
386
+ return null;
387
+ }
388
+ canIndent() {
389
+ return false;
390
+ }
391
+ collapseAtStart() {
392
+ const paragraph = $createParagraphNode();
393
+ this.getChildren().forEach((child) => paragraph.append(child));
394
+ this.replace(paragraph);
395
+ return true;
396
+ }
397
+ setLanguage(language) {
398
+ const writable = this.getWritable();
399
+ writable.__language = language || void 0;
400
+ return writable;
401
+ }
402
+ getLanguage() {
403
+ return this.getLatest().__language;
404
+ }
405
+ setIsSyntaxHighlightSupported(isSupported) {
406
+ const writable = this.getWritable();
407
+ writable.__isSyntaxHighlightSupported = isSupported;
408
+ return writable;
409
+ }
410
+ getIsSyntaxHighlightSupported() {
411
+ return this.getLatest().__isSyntaxHighlightSupported;
412
+ }
413
+ setTheme(theme) {
414
+ const writable = this.getWritable();
415
+ writable.__theme = theme || void 0;
416
+ return writable;
417
+ }
418
+ getTheme() {
419
+ return this.getLatest().__theme;
420
+ }
421
+ };
422
+ function $createCodeNode(language, theme) {
423
+ return $create(CodeNode).setLanguage(language).setTheme(theme);
424
+ }
425
+ function $isCodeNode(node) {
426
+ return node instanceof CodeNode;
427
+ }
428
+ function $convertPreElement(domNode) {
429
+ return { node: $createCodeNode(domNode.getAttribute(LANGUAGE_DATA_ATTRIBUTE$1)) };
430
+ }
431
+ function $convertDivElement(domNode) {
432
+ const div = domNode;
433
+ const isCode = isCodeElement(div);
434
+ if (!isCode && !isCodeChildElement(div)) return { node: null };
435
+ return { node: isCode ? $createCodeNode() : null };
436
+ }
437
+ function $convertTableElement() {
438
+ return { node: $createCodeNode() };
439
+ }
440
+ function convertCodeNoop() {
441
+ return { node: null };
442
+ }
443
+ function isCodeElement(div) {
444
+ return div.style.fontFamily.match("monospace") !== null;
445
+ }
446
+ function isCodeChildElement(node) {
447
+ let parent = node.parentElement;
448
+ while (parent !== null) {
449
+ if (isCodeElement(parent)) return true;
450
+ parent = parent.parentElement;
451
+ }
452
+ return false;
453
+ }
454
+ function isGitHubCodeCell(cell) {
455
+ return cell.classList.contains("js-file-line");
456
+ }
457
+ function isGitHubCodeTable(table) {
458
+ return table.classList.contains("js-file-line-container");
459
+ }
460
+ function $exitCodeNodeOnEnter(selection) {
461
+ const { anchor } = selection;
462
+ if (selection.isCollapsed() && anchor.type === "element") {
463
+ const codeNode = anchor.getNode();
464
+ if ($isCodeNode(codeNode)) {
465
+ const childrenSize = codeNode.getChildrenSize();
466
+ if (childrenSize >= 2 && anchor.offset === childrenSize) {
467
+ const lastChild = codeNode.getLastChild();
468
+ if ($isLineBreakNode(lastChild) && $isLineBreakNode(lastChild.getPreviousSibling())) {
469
+ const newElement = $createParagraphNode();
470
+ codeNode.splice(childrenSize - 2, 2, []).insertAfter(newElement, false);
471
+ newElement.select();
472
+ return newElement;
473
+ }
474
+ }
475
+ }
476
+ }
477
+ return null;
478
+ }
479
+ /**
480
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
481
+ *
482
+ * This source code is licensed under the MIT license found in the
483
+ * LICENSE file in the root directory of this source tree.
484
+ *
485
+ */
486
+ /** @noInheritDoc */
487
+ var CodeHighlightNode = class CodeHighlightNode extends TextNode {
488
+ /** @internal */
489
+ __highlightType;
490
+ constructor(text = "", highlightType, key) {
491
+ super(text, key);
492
+ this.__highlightType = highlightType;
493
+ }
494
+ static getType() {
495
+ return "code-highlight";
496
+ }
497
+ static clone(node) {
498
+ return new CodeHighlightNode(node.__text, node.__highlightType || void 0, node.__key);
499
+ }
500
+ afterCloneFrom(prevNode) {
501
+ super.afterCloneFrom(prevNode);
502
+ this.__highlightType = prevNode.__highlightType;
503
+ }
504
+ getHighlightType() {
505
+ return this.getLatest().__highlightType;
506
+ }
507
+ setHighlightType(highlightType) {
508
+ const self = this.getWritable();
509
+ self.__highlightType = highlightType || void 0;
510
+ return self;
511
+ }
512
+ canHaveFormat() {
513
+ return false;
514
+ }
515
+ createDOM(config) {
516
+ const element = super.createDOM(config);
517
+ addClassNamesToElement(element, getHighlightThemeClass(config.theme, this.__highlightType));
518
+ return element;
519
+ }
520
+ updateDOM(prevNode, dom, config) {
521
+ const update = super.updateDOM(prevNode, dom, config);
522
+ const prevClassName = getHighlightThemeClass(config.theme, prevNode.__highlightType);
523
+ const nextClassName = getHighlightThemeClass(config.theme, this.__highlightType);
524
+ if (prevClassName !== nextClassName) {
525
+ if (prevClassName) removeClassNamesFromElement(dom, prevClassName);
526
+ if (nextClassName) addClassNamesToElement(dom, nextClassName);
527
+ }
528
+ return update;
529
+ }
530
+ static importJSON(serializedNode) {
531
+ return $createCodeHighlightNode().updateFromJSON(serializedNode);
532
+ }
533
+ updateFromJSON(serializedNode) {
534
+ return super.updateFromJSON(serializedNode).setHighlightType(serializedNode.highlightType);
535
+ }
536
+ exportJSON() {
537
+ return {
538
+ ...super.exportJSON(),
539
+ highlightType: this.getHighlightType()
540
+ };
541
+ }
542
+ setFormat(format) {
543
+ return this;
544
+ }
545
+ isParentRequired() {
546
+ return true;
547
+ }
548
+ createParentElementNode() {
549
+ return $createCodeNode();
550
+ }
551
+ };
552
+ function getHighlightThemeClass(theme, highlightType) {
553
+ return highlightType && theme && theme.codeHighlight && theme.codeHighlight[highlightType];
554
+ }
555
+ function $createCodeHighlightNode(text = "", highlightType) {
556
+ return $applyNodeReplacement(new CodeHighlightNode(text, highlightType));
557
+ }
558
+ function $isCodeHighlightNode(node) {
559
+ return node instanceof CodeHighlightNode;
560
+ }
561
+ /**
562
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
563
+ *
564
+ * This source code is licensed under the MIT license found in the
565
+ * LICENSE file in the root directory of this source tree.
566
+ *
567
+ */
568
+ /**
569
+ * Add code blocks to the editor (syntax highlighting provided separately)
570
+ */
571
+ const CodeExtension = defineExtension({
572
+ name: "@lexical/code",
573
+ nodes: () => [CodeNode, CodeHighlightNode],
574
+ register(editor) {
575
+ return editor.registerCommand(KEY_ENTER_COMMAND, (event) => {
576
+ const selection = $getSelection();
577
+ if ($isRangeSelection(selection) && $exitCodeNodeOnEnter(selection)) {
578
+ event.preventDefault();
579
+ return true;
580
+ }
581
+ return false;
582
+ }, COMMAND_PRIORITY_LOW);
583
+ }
584
+ });
585
+ /**
586
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
587
+ *
588
+ * This source code is licensed under the MIT license found in the
589
+ * LICENSE file in the root directory of this source tree.
590
+ *
591
+ */
592
+ const LANGUAGE_DATA_ATTRIBUTE = "data-language";
593
+ /**
594
+ * True for elements whose `font-family` mentions `monospace` — the
595
+ * heuristic the legacy `<div>` rule uses to spot copy-pasted code blocks
596
+ * (e.g. Google Docs serializes a code block as a styled `<div>`).
597
+ */
598
+ function isMonospaceElement(el) {
599
+ return el.style.fontFamily.match("monospace") !== null;
600
+ }
601
+ function isMonospaceDescendant(node) {
602
+ let parent = node.parentElement;
603
+ while (parent !== null) {
604
+ if (isMonospaceElement(parent)) return true;
605
+ parent = parent.parentElement;
606
+ }
607
+ return false;
608
+ }
609
+ /**
610
+ * Overlay rules active only while {@link GitHubCodeTableRule} is
611
+ * processing its children. Inside the code-table subtree, every `<tr>`
612
+ * and `<td>` unwraps unconditionally — they never become table-row /
613
+ * table-cell nodes (even when `@lexical/table` registers its rules for
614
+ * those tags). Outside the subtree, this overlay isn't installed, so
615
+ * the cost of these rules is never paid against unrelated `<tr>` /
616
+ * `<td>` pastes.
617
+ */
618
+ const GitHubCodeTableOverlayRules = defineOverlayRules([defineImportRule({
619
+ $import: (ctx, el) => ctx.$importChildren(el),
620
+ match: sel.tag("tr", "td"),
621
+ name: "@lexical/code/github-code-table/unwrap"
622
+ })]);
623
+ const PreRule = defineImportRule({
624
+ $import: (ctx, el) => [$createCodeNode(el.getAttribute(LANGUAGE_DATA_ATTRIBUTE)).splice(0, 0, ctx.$importChildren(el))],
625
+ match: sel.tag("pre"),
626
+ name: "@lexical/code/pre"
627
+ });
628
+ /**
629
+ * Multi-line `<code>` (containing newlines or `<br>`) is treated as a
630
+ * block code element — mirrors the legacy behavior. Single-line `<code>`
631
+ * defers to the inline-format rule from `CoreImportExtension` so it
632
+ * becomes a TextNode with IS_CODE.
633
+ */
634
+ const MultilineCodeRule = defineImportRule({
635
+ $import: (ctx, el, $next) => {
636
+ const text = el.textContent || "";
637
+ if (!(/\r?\n/.test(text) || el.querySelector("br") !== null)) return $next();
638
+ return [$createCodeNode(el.getAttribute(LANGUAGE_DATA_ATTRIBUTE)).splice(0, 0, ctx.$importChildren(el))];
639
+ },
640
+ match: sel.tag("code"),
641
+ name: "@lexical/code/code-multiline"
642
+ });
643
+ /**
644
+ * True for elements carrying BOTH `font-family: …monospace…` and
645
+ * `white-space: pre*` inline — the shape VS Code uses for every line
646
+ * of a copied code block (on every per-line `<div>` on Safari, on
647
+ * the single outer wrapper on Chrome).
648
+ */
649
+ function isMonospacePreElement(el) {
650
+ if (!isHTMLElement(el)) return false;
651
+ const ff = el.style.fontFamily;
652
+ const ws = el.style.whiteSpace;
653
+ return typeof ff === "string" && /monospace/i.test(ff) && typeof ws === "string" && ws.startsWith("pre");
654
+ }
655
+ /**
656
+ * Split a monospace-pre wrapper element into logical code lines:
657
+ * `<div>` children contribute their text content as one line,
658
+ * `<br>` children contribute an empty line, inline children (spans
659
+ * and bare text) accumulate into the current line until the next
660
+ * block child.
661
+ *
662
+ * Returns `null` if `el` has no block children (i.e. it's a leaf
663
+ * line, not a wrapper) so the caller can leave it to the
664
+ * sibling-run pass.
665
+ */
666
+ function splitMonospaceWrapperLines(el) {
667
+ let hasBlockChild = false;
668
+ const lines = [];
669
+ let acc = "";
670
+ let hasAcc = false;
671
+ const flush = () => {
672
+ if (hasAcc) {
673
+ lines.push(acc);
674
+ acc = "";
675
+ hasAcc = false;
676
+ }
677
+ };
678
+ for (const child of Array.from(el.childNodes)) if (isHTMLElement(child)) if (child.tagName === "DIV") {
679
+ flush();
680
+ lines.push(child.textContent || "");
681
+ hasBlockChild = true;
682
+ } else if (child.tagName === "BR") {
683
+ flush();
684
+ lines.push("");
685
+ hasBlockChild = true;
686
+ } else {
687
+ acc += child.textContent || "";
688
+ hasAcc = true;
689
+ }
690
+ else if (isDOMTextNode(child)) {
691
+ const t = child.textContent || "";
692
+ if (t.length > 0) {
693
+ acc += t;
694
+ hasAcc = true;
695
+ }
696
+ }
697
+ flush();
698
+ return hasBlockChild ? lines : null;
699
+ }
700
+ /**
701
+ * Returns `true` if `root` contains the structural signature of a
702
+ * VS Code code-block paste:
703
+ *
704
+ * - a monospace+pre `<div>` wrapper with at least one block (`<div>` /
705
+ * `<br>`) child — the Chrome shape, or
706
+ * - two or more consecutive monospace+pre siblings — the Safari shape.
707
+ *
708
+ * Walked once in preprocess; the matching overlay is only installed
709
+ * when this returns `true` so an unrelated paste doesn't pay for the
710
+ * detection or rule cost.
711
+ */
712
+ function looksLikeVscodePaste(root) {
713
+ for (const child of Array.from(root.children)) {
714
+ if (isHTMLElement(child) && isMonospacePreElement(child)) {
715
+ if (splitMonospaceWrapperLines(child) !== null) return true;
716
+ const next = child.nextElementSibling;
717
+ if (next && isMonospacePreElement(next)) return true;
718
+ continue;
719
+ }
720
+ if (looksLikeVscodePaste(child)) return true;
721
+ }
722
+ return false;
723
+ }
724
+ const VscodeCodePasteOverlay = defineOverlayRules([defineImportRule({
725
+ $import: (_ctx, el, $next) => {
726
+ if (!isMonospacePreElement(el) || isMonospaceDescendant(el)) return $next();
727
+ const lines = splitMonospaceWrapperLines(el);
728
+ if (lines === null || lines.length === 0) return $next();
729
+ return [$createCodeNode().splice(0, 0, $generateNodesFromRawText(lines.join("\n")))];
730
+ },
731
+ match: sel.tag("div"),
732
+ name: "@lexical/code/vscode-wrapper"
733
+ }), defineImportRule({
734
+ $import: (_ctx, el, $next) => {
735
+ if (!isMonospacePreElement(el) || isMonospaceDescendant(el)) return $next();
736
+ const prev = el.previousElementSibling;
737
+ if (prev && isMonospacePreElement(prev)) return [];
738
+ const lines = [];
739
+ let cur = el;
740
+ while (cur && isMonospacePreElement(cur)) {
741
+ lines.push(cur.tagName === "BR" ? "" : cur.textContent || "");
742
+ cur = cur.nextElementSibling;
743
+ }
744
+ if (lines.length < 2) return $next();
745
+ return [$createCodeNode().splice(0, 0, $generateNodesFromRawText(lines.join("\n")))];
746
+ },
747
+ match: sel.tag("div", "br"),
748
+ name: "@lexical/code/vscode-line-run"
749
+ })]);
750
+ /**
751
+ * VS Code → browser code-block pastes ship the block as either:
752
+ *
753
+ * - **Chrome**: one outer
754
+ * `<div style="font-family: …monospace…; white-space: pre">…</div>`
755
+ * wrapping per-line `<div>`s and `<br>`s.
756
+ * - **Safari**: a flat run of sibling
757
+ * `<div style="…monospace…; white-space: pre">…</div>` and
758
+ * `<br style="…monospace…; …">` elements with no wrapping
759
+ * monospace ancestor (the styles are duplicated onto every
760
+ * element).
761
+ *
762
+ * The legacy `<div>` rule (and {@link DivRule}) produces one CodeNode
763
+ * per `<div>` on Safari and concatenates inner divs without
764
+ * separating `\n`s on Chrome. This preprocess scans once for the
765
+ * structural signature and, only when it matches, pushes
766
+ * {@link VscodeCodePasteOverlay} onto {@link ImportOverlays} so the
767
+ * VS Code-specific rules participate in the walk. Pastes from other
768
+ * sources pay only the detection cost.
769
+ *
770
+ * @experimental
771
+ */
772
+ const $installVscodeCodePasteOverlay = (dom, ctx, $next) => {
773
+ if (looksLikeVscodePaste(isDOMDocumentNode(dom) ? dom.body : dom)) ctx.session.update(ImportOverlays, (prev) => [...prev, VscodeCodePasteOverlay]);
774
+ $next();
775
+ };
776
+ /**
777
+ * A `<div style="font-family: …monospace…">` (Google-Docs-style code
778
+ * block) creates a CodeNode. Descendant elements inside a monospace
779
+ * wrapper just unwrap so their text content flows into the surrounding
780
+ * CodeNode.
781
+ */
782
+ const DivRule = defineImportRule({
783
+ $import: (ctx, el, $next) => {
784
+ if (isMonospaceElement(el)) return [$createCodeNode().splice(0, 0, ctx.$importChildren(el))];
785
+ if (isMonospaceDescendant(el)) return ctx.$importChildren(el);
786
+ return $next();
787
+ },
788
+ match: sel.tag("div"),
789
+ name: "@lexical/code/div"
790
+ });
791
+ /**
792
+ * Import rules for {@link CodeNode}.
793
+ *
794
+ * Specific class-restricted rules (GitHub raw-file-view detectors) are
795
+ * registered before the generic `<table>` / `<tr>` / `<td>` rules so
796
+ * they win dispatch.
797
+ *
798
+ * @experimental
799
+ */
800
+ const CodeImportRules = [
801
+ defineImportRule({
802
+ $import: (ctx, el) => [$createCodeNode().splice(0, 0, ctx.$importChildren(el, { rules: GitHubCodeTableOverlayRules }))],
803
+ match: sel.tag("table").classAll("js-file-line-container"),
804
+ name: "@lexical/code/github-code-table"
805
+ }),
806
+ defineImportRule({
807
+ $import: (ctx, el) => ctx.$importChildren(el),
808
+ match: sel.tag("td").classAll("js-file-line"),
809
+ name: "@lexical/code/github-code-cell-by-class"
810
+ }),
811
+ MultilineCodeRule,
812
+ PreRule,
813
+ DivRule
814
+ ];
815
+ /**
816
+ * Bundles {@link CodeImportRules} (plus {@link CoreImportExtension}) into
817
+ * a single dependency. The legacy {@link CodeNode.importDOM} continues to
818
+ * work in parallel; depend on this extension to opt into the new
819
+ * pipeline.
820
+ *
821
+ * @experimental
822
+ */
823
+ const CodeImportExtension = defineExtension({
824
+ dependencies: [
825
+ CoreImportExtension,
826
+ CodeExtension,
827
+ configExtension(DOMImportExtension, {
828
+ preprocess: [$installVscodeCodePasteOverlay],
829
+ rules: CodeImportRules
830
+ })
831
+ ],
832
+ name: "@lexical/code/Import"
833
+ });
834
+ function $isSelectionInCode(selection) {
835
+ if (!$isRangeSelection(selection)) return false;
836
+ const anchorNode = selection.anchor.getNode();
837
+ const maybeAnchorCodeNode = $isCodeNode(anchorNode) ? anchorNode : anchorNode.getParent();
838
+ const focusNode = selection.focus.getNode();
839
+ const maybeFocusCodeNode = $isCodeNode(focusNode) ? focusNode : focusNode.getParent();
840
+ return $isCodeNode(maybeAnchorCodeNode) && maybeAnchorCodeNode.is(maybeFocusCodeNode);
841
+ }
842
+ /**
843
+ * Returns an Array of code lines
844
+ * Take the sequence of LineBreakNode | TabNode | CodeHighlightNode forming
845
+ * the selection and split it by LineBreakNode.
846
+ * If the selection ends at the start of the last line, it is considered empty.
847
+ * Empty lines are discarded.
848
+ */
849
+ function $getCodeLines(selection) {
850
+ const nodes = selection.getNodes();
851
+ const lines = [];
852
+ if (nodes.length === 1 && $isCodeNode(nodes[0])) return lines;
853
+ let lastLine = [];
854
+ for (let i = 0; i < nodes.length; i++) {
855
+ const node = nodes[i];
856
+ if (!($isCodeHighlightNode(node) || $isTabNode(node) || $isLineBreakNode(node))) formatDevErrorMessage(`Expected selection to be inside CodeBlock and consisting of CodeHighlightNode, TabNode and LineBreakNode`);
857
+ if ($isLineBreakNode(node)) {
858
+ if (lastLine.length > 0) {
859
+ lines.push(lastLine);
860
+ lastLine = [];
861
+ }
862
+ } else lastLine.push(node);
863
+ }
864
+ if (lastLine.length > 0) {
865
+ const selectionEnd = selection.isBackward() ? selection.anchor : selection.focus;
866
+ const lastPoint = $createPoint(lastLine[0].getKey(), 0, "text");
867
+ if (!selectionEnd.is(lastPoint)) lines.push(lastLine);
868
+ }
869
+ return lines;
870
+ }
871
+ function $handleTab(shiftKey) {
872
+ const selection = $getSelection();
873
+ if (!$isRangeSelection(selection) || !$isSelectionInCode(selection)) return null;
874
+ const indentOrOutdent = !shiftKey ? INDENT_CONTENT_COMMAND : OUTDENT_CONTENT_COMMAND;
875
+ const tabOrOutdent = !shiftKey ? INSERT_TAB_COMMAND : OUTDENT_CONTENT_COMMAND;
876
+ const anchor = selection.anchor;
877
+ const focus = selection.focus;
878
+ if (anchor.is(focus)) return tabOrOutdent;
879
+ const codeLines = $getCodeLines(selection);
880
+ if (codeLines.length !== 1) return indentOrOutdent;
881
+ const codeLine = codeLines[0];
882
+ if (!(codeLine.length !== 0)) formatDevErrorMessage(`$getCodeLines only extracts non-empty lines`);
883
+ let selectionFirst;
884
+ let selectionLast;
885
+ if (selection.isBackward()) {
886
+ selectionFirst = focus;
887
+ selectionLast = anchor;
888
+ } else {
889
+ selectionFirst = anchor;
890
+ selectionLast = focus;
891
+ }
892
+ const firstOfLine = $getFirstCodeNodeOfLine(codeLine[0]);
893
+ const lastOfLine = $getLastCodeNodeOfLine(codeLine[0]);
894
+ const anchorOfLine = $createPoint(firstOfLine.getKey(), 0, "text");
895
+ const focusOfLine = $createPoint(lastOfLine.getKey(), lastOfLine.getTextContentSize(), "text");
896
+ if (selectionFirst.isBefore(anchorOfLine)) return indentOrOutdent;
897
+ if (focusOfLine.isBefore(selectionLast)) return indentOrOutdent;
898
+ if (anchorOfLine.isBefore(selectionFirst) || selectionLast.isBefore(focusOfLine)) return tabOrOutdent;
899
+ return indentOrOutdent;
900
+ }
901
+ function $handleMultilineIndent(type, tabSize) {
902
+ const selection = $getSelection();
903
+ if (!$isRangeSelection(selection) || !$isSelectionInCode(selection)) return false;
904
+ const codeLines = $getCodeLines(selection);
905
+ const codeLinesLength = codeLines.length;
906
+ if (codeLinesLength === 0 && selection.isCollapsed()) {
907
+ if (type === INDENT_CONTENT_COMMAND) selection.insertNodes([$createTabNode()]);
908
+ return true;
909
+ }
910
+ if (codeLinesLength === 0 && type === INDENT_CONTENT_COMMAND && selection.getTextContent() === "\n") {
911
+ const tabNode = $createTabNode();
912
+ const lineBreakNode = $createLineBreakNode();
913
+ const direction = selection.isBackward() ? "previous" : "next";
914
+ selection.insertNodes([tabNode, lineBreakNode]);
915
+ $setSelectionFromCaretRange($getCaretRangeInDirection($getCaretRange($getTextPointCaret(tabNode, "next", 0), $normalizeCaret($getSiblingCaret(lineBreakNode, "next"))), direction));
916
+ return true;
917
+ }
918
+ for (let i = 0; i < codeLinesLength; i++) {
919
+ const line = codeLines[i];
920
+ if (line.length > 0) {
921
+ let firstOfLine = line[0];
922
+ if (i === 0) firstOfLine = $getFirstCodeNodeOfLine(firstOfLine);
923
+ if (type === INDENT_CONTENT_COMMAND) {
924
+ const tabNode = $createTabNode();
925
+ firstOfLine.insertBefore(tabNode);
926
+ if (i === 0) {
927
+ const anchorKey = selection.isBackward() ? "focus" : "anchor";
928
+ const anchorLine = $createPoint(firstOfLine.getKey(), 0, "text");
929
+ if (selection[anchorKey].is(anchorLine)) selection[anchorKey].set(tabNode.getKey(), 0, "text");
930
+ }
931
+ } else if ($isTabNode(firstOfLine)) firstOfLine.remove();
932
+ else if (tabSize !== void 0 && $isCodeHighlightNode(firstOfLine)) $outdentLeadingSpaces(firstOfLine, tabSize, selection);
933
+ }
934
+ }
935
+ return true;
936
+ }
937
+ function $handleShiftLines(type, event) {
938
+ const selection = $getSelection();
939
+ if (!$isRangeSelection(selection)) return false;
940
+ const { anchor, focus } = selection;
941
+ const anchorOffset = anchor.offset;
942
+ const focusOffset = focus.offset;
943
+ const anchorNode = anchor.getNode();
944
+ const focusNode = focus.getNode();
945
+ const arrowIsUp = type === KEY_ARROW_UP_COMMAND;
946
+ if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || $isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || $isTabNode(focusNode))) return false;
947
+ if (!event.altKey) {
948
+ if (selection.isCollapsed()) {
949
+ const codeNode = anchorNode.getParentOrThrow();
950
+ if (arrowIsUp && anchorOffset === 0 && anchorNode.getPreviousSibling() === null) {
951
+ if (codeNode.getPreviousSibling() === null) {
952
+ codeNode.selectPrevious();
953
+ event.preventDefault();
954
+ return true;
955
+ }
956
+ } else if (!arrowIsUp && anchorOffset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null) {
957
+ if (codeNode.getNextSibling() === null) {
958
+ codeNode.selectNext();
959
+ event.preventDefault();
960
+ return true;
961
+ }
962
+ }
963
+ }
964
+ return false;
965
+ }
966
+ let start;
967
+ let end;
968
+ if (anchorNode.isBefore(focusNode)) {
969
+ start = $getFirstCodeNodeOfLine(anchorNode);
970
+ end = $getLastCodeNodeOfLine(focusNode);
971
+ } else {
972
+ start = $getFirstCodeNodeOfLine(focusNode);
973
+ end = $getLastCodeNodeOfLine(anchorNode);
974
+ }
975
+ if (start == null || end == null) return false;
976
+ const range = start.getNodesBetween(end);
977
+ for (let i = 0; i < range.length; i++) {
978
+ const node = range[i];
979
+ if (!$isCodeHighlightNode(node) && !$isTabNode(node) && !$isLineBreakNode(node)) return false;
980
+ }
981
+ event.preventDefault();
982
+ event.stopPropagation();
983
+ const linebreak = arrowIsUp ? start.getPreviousSibling() : end.getNextSibling();
984
+ if (!$isLineBreakNode(linebreak)) return true;
985
+ const sibling = arrowIsUp ? linebreak.getPreviousSibling() : linebreak.getNextSibling();
986
+ if (sibling == null) return true;
987
+ const maybeInsertionPoint = $isCodeHighlightNode(sibling) || $isTabNode(sibling) || $isLineBreakNode(sibling) ? arrowIsUp ? $getFirstCodeNodeOfLine(sibling) : $getLastCodeNodeOfLine(sibling) : null;
988
+ let insertionPoint = maybeInsertionPoint != null ? maybeInsertionPoint : sibling;
989
+ linebreak.remove();
990
+ range.forEach((node) => node.remove());
991
+ if (type === KEY_ARROW_UP_COMMAND) {
992
+ range.forEach((node) => insertionPoint.insertBefore(node));
993
+ insertionPoint.insertBefore(linebreak);
994
+ } else {
995
+ insertionPoint.insertAfter(linebreak);
996
+ insertionPoint = linebreak;
997
+ range.forEach((node) => {
998
+ insertionPoint.insertAfter(node);
999
+ insertionPoint = node;
1000
+ });
1001
+ }
1002
+ selection.setTextNodeRange(anchorNode, anchorOffset, focusNode, focusOffset);
1003
+ return true;
1004
+ }
1005
+ function $handleMoveTo(type, event) {
1006
+ const selection = $getSelection();
1007
+ if (!$isRangeSelection(selection)) return false;
1008
+ const { anchor, focus } = selection;
1009
+ const anchorNode = anchor.getNode();
1010
+ const focusNode = focus.getNode();
1011
+ const isMoveToStart = type === MOVE_TO_START;
1012
+ if (!$isSelectionInCode(selection) || !($isCodeHighlightNode(anchorNode) || $isTabNode(anchorNode)) || !($isCodeHighlightNode(focusNode) || $isTabNode(focusNode))) return false;
1013
+ const focusLineNode = focusNode;
1014
+ const moveToStart = $getCodeLineDirection(focusLineNode) === "rtl" ? !isMoveToStart : isMoveToStart;
1015
+ const originalAnchorKey = anchor.key;
1016
+ const originalAnchorOffset = anchor.offset;
1017
+ const originalAnchorType = anchor.type;
1018
+ if (moveToStart) {
1019
+ const start = $getStartOfCodeInLine(focusLineNode, focus.offset);
1020
+ if (start !== null) {
1021
+ const { node, offset } = start;
1022
+ if ($isLineBreakNode(node)) node.selectNext(0, 0);
1023
+ else selection.setTextNodeRange(node, offset, node, offset);
1024
+ } else focusLineNode.getParentOrThrow().selectStart();
1025
+ } else $getEndOfCodeInLine(focusLineNode).select();
1026
+ if (event.shiftKey) selection.anchor.set(originalAnchorKey, originalAnchorOffset, originalAnchorType);
1027
+ event.preventDefault();
1028
+ event.stopPropagation();
1029
+ return true;
1030
+ }
1031
+ /**
1032
+ * @internal
1033
+ * Register the keyboard and command handlers that drive code-block
1034
+ * indentation: Tab / Shift+Tab, INDENT/OUTDENT_CONTENT_COMMAND,
1035
+ * INSERT_TAB_COMMAND, alt+arrow line shifting, and Home/End movement.
1036
+ *
1037
+ * Both `@lexical/code-shiki` and `@lexical/code-prism` use this via
1038
+ * {@link CodeIndentExtension}; callers using `registerCodeHighlighting`
1039
+ * will implicitly call this with tabSize of `undefined`.
1040
+ *
1041
+ * @param editor The editor to register on.
1042
+ * @param tabSize When set, OUTDENT_CONTENT_COMMAND (Shift+Tab) also strips
1043
+ * up to that many leading spaces from a code line. See
1044
+ * {@link $outdentLeadingSpaces}.
1045
+ */
1046
+ function registerCodeIndentation(editor, tabSize) {
1047
+ return mergeRegister(editor.registerCommand(KEY_TAB_COMMAND, (event) => {
1048
+ const command = $handleTab(event.shiftKey);
1049
+ if (command === null) return false;
1050
+ event.preventDefault();
1051
+ editor.dispatchCommand(command, void 0);
1052
+ return true;
1053
+ }, COMMAND_PRIORITY_LOW), editor.registerCommand(INSERT_TAB_COMMAND, () => {
1054
+ if (!$isSelectionInCode($getSelection())) return false;
1055
+ $insertNodes([$createTabNode()]);
1056
+ return true;
1057
+ }, COMMAND_PRIORITY_LOW), editor.registerCommand(INDENT_CONTENT_COMMAND, () => $handleMultilineIndent(INDENT_CONTENT_COMMAND), COMMAND_PRIORITY_LOW), editor.registerCommand(OUTDENT_CONTENT_COMMAND, () => $handleMultilineIndent(OUTDENT_CONTENT_COMMAND, tabSize), COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_UP_COMMAND, (event) => {
1058
+ const selection = $getSelection();
1059
+ if (!$isRangeSelection(selection)) return false;
1060
+ const { anchor } = selection;
1061
+ const anchorNode = anchor.getNode();
1062
+ if (!$isSelectionInCode(selection)) return false;
1063
+ if (selection.isCollapsed() && anchor.offset === 0 && anchorNode.getPreviousSibling() === null && $isCodeNode(anchorNode.getParentOrThrow())) {
1064
+ event.preventDefault();
1065
+ return true;
1066
+ }
1067
+ return $handleShiftLines(KEY_ARROW_UP_COMMAND, event);
1068
+ }, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ARROW_DOWN_COMMAND, (event) => {
1069
+ const selection = $getSelection();
1070
+ if (!$isRangeSelection(selection)) return false;
1071
+ const { anchor } = selection;
1072
+ const anchorNode = anchor.getNode();
1073
+ if (!$isSelectionInCode(selection)) return false;
1074
+ if (selection.isCollapsed() && anchor.offset === anchorNode.getTextContentSize() && anchorNode.getNextSibling() === null && $isCodeNode(anchorNode.getParentOrThrow())) {
1075
+ event.preventDefault();
1076
+ return true;
1077
+ }
1078
+ return $handleShiftLines(KEY_ARROW_DOWN_COMMAND, event);
1079
+ }, COMMAND_PRIORITY_LOW), editor.registerCommand(MOVE_TO_START, (event) => $handleMoveTo(MOVE_TO_START, event), COMMAND_PRIORITY_LOW), editor.registerCommand(MOVE_TO_END, (event) => $handleMoveTo(MOVE_TO_END, event), COMMAND_PRIORITY_LOW));
1080
+ }
1081
+ /**
1082
+ * Adds keyboard-driven indentation to code blocks (Tab / Shift+Tab,
1083
+ * alt+arrow line shifts, Home/End within a line). Both
1084
+ * {@link "@lexical/code-shiki".CodeShikiExtension} and
1085
+ * {@link "@lexical/code-prism".CodePrismExtension} declare this as a
1086
+ * dependency, so it is activated automatically alongside either
1087
+ * highlighter.
1088
+ *
1089
+ * Code blocks without syntax highlighting can use this extension on its
1090
+ * own.
1091
+ */
1092
+ const CodeIndentExtension = defineExtension({
1093
+ build: (editor, config) => namedSignals(config),
1094
+ config: safeCast({
1095
+ disabled: false,
1096
+ tabSize: void 0
1097
+ }),
1098
+ dependencies: [CodeExtension],
1099
+ name: "@lexical/code-indent",
1100
+ register: (editor, config, state) => {
1101
+ const stores = state.getOutput();
1102
+ return effect(() => {
1103
+ if (stores.disabled.value) return;
1104
+ return registerCodeIndentation(editor, stores.tabSize.value);
1105
+ });
1106
+ }
1107
+ });
1108
+ //#endregion
1109
+ export { $createCodeHighlightNode, $createCodeNode, $getCodeLineDirection, $getEndOfCodeInLine, $getFirstCodeNodeOfLine, $getLastCodeNodeOfLine, $getStartOfCodeInLine, $isCodeHighlightNode, $isCodeNode, $outdentLeadingSpaces, $plainifyCodeContent, CodeExtension, CodeHighlightNode, CodeImportExtension, CodeImportRules, CodeIndentExtension, CodeNode, DEFAULT_CODE_LANGUAGE, getDefaultCodeLanguage, registerCodeIndentation };