@pixerate/editor 0.47.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.
Files changed (76) hide show
  1. package/dist/agent/index.cjs +246 -29
  2. package/dist/agent/index.cjs.map +1 -1
  3. package/dist/agent/index.d.cts +18 -6
  4. package/dist/agent/index.d.ts +18 -6
  5. package/dist/agent/index.js +226 -9
  6. package/dist/agent/index.js.map +1 -1
  7. package/dist/{builtins-xs7rK6ys.d.cts → builtins-1kqwLUnb.d.cts} +1 -1
  8. package/dist/{builtins-D2nMtw2W.d.ts → builtins-BKYlHH1s.d.ts} +1 -1
  9. package/dist/chart/index.cjs +214 -3541
  10. package/dist/chart/index.cjs.map +1 -1
  11. package/dist/chart/index.d.cts +4 -494
  12. package/dist/chart/index.d.ts +4 -494
  13. package/dist/chart/index.js +135 -3462
  14. package/dist/chart/index.js.map +1 -1
  15. package/dist/{chunk-5VXI5GED.js → chunk-2CJ3LPYQ.js} +3 -3
  16. package/dist/{chunk-ZFBBVGCI.js → chunk-EKYHKQPG.js} +243 -243
  17. package/dist/{chunk-ZFBBVGCI.js.map → chunk-EKYHKQPG.js.map} +1 -1
  18. package/dist/chunk-HXUL3LS7.js +113 -0
  19. package/dist/chunk-HXUL3LS7.js.map +1 -0
  20. package/dist/{chunk-REAGGLXS.js → chunk-JQ3BB3E6.js} +2 -2
  21. package/dist/chunk-LTUM52X6.js +4074 -0
  22. package/dist/chunk-LTUM52X6.js.map +1 -0
  23. package/dist/{chunk-J3R2OWOE.cjs → chunk-MUCYWI6W.cjs} +608 -15
  24. package/dist/chunk-MUCYWI6W.cjs.map +1 -0
  25. package/dist/chunk-PIMCHMZE.cjs +521 -0
  26. package/dist/chunk-PIMCHMZE.cjs.map +1 -0
  27. package/dist/{chunk-TK2XZK6Z.js → chunk-PKBWLEL7.js} +594 -1
  28. package/dist/chunk-PKBWLEL7.js.map +1 -0
  29. package/dist/chunk-PRSJJZBS.cjs +4074 -0
  30. package/dist/chunk-PRSJJZBS.cjs.map +1 -0
  31. package/dist/chunk-U6IZKC7B.cjs +113 -0
  32. package/dist/chunk-U6IZKC7B.cjs.map +1 -0
  33. package/dist/{chunk-SQC26VQV.cjs → chunk-UN33YOXW.cjs} +191 -191
  34. package/dist/{chunk-SQC26VQV.cjs.map → chunk-UN33YOXW.cjs.map} +1 -1
  35. package/dist/{chunk-PQFAHT5O.js → chunk-VKPEPWKZ.js} +27 -94
  36. package/dist/chunk-VKPEPWKZ.js.map +1 -0
  37. package/dist/{chunk-H4PICYNL.cjs → chunk-WXEHCVJQ.cjs} +113 -180
  38. package/dist/chunk-WXEHCVJQ.cjs.map +1 -0
  39. package/dist/{chunk-LYGEEDNR.cjs → chunk-XP5IUZUO.cjs} +242 -242
  40. package/dist/chunk-XP5IUZUO.cjs.map +1 -0
  41. package/dist/{chunk-C3NWJE3K.js → chunk-XUUD43OV.js} +11 -672
  42. package/dist/chunk-XUUD43OV.js.map +1 -0
  43. package/dist/{chunk-BBRK6MOI.cjs → chunk-YU4WQE5M.cjs} +100 -100
  44. package/dist/{chunk-BBRK6MOI.cjs.map → chunk-YU4WQE5M.cjs.map} +1 -1
  45. package/dist/commands/index.d.cts +1 -1
  46. package/dist/commands/index.d.ts +1 -1
  47. package/dist/controller-ChjCCMLl.d.cts +822 -0
  48. package/dist/controller-DtbEo3om.d.ts +822 -0
  49. package/dist/extensions/index.cjs +32 -4
  50. package/dist/extensions/index.cjs.map +1 -1
  51. package/dist/extensions/index.d.cts +4 -2
  52. package/dist/extensions/index.d.ts +4 -2
  53. package/dist/extensions/index.js +31 -3
  54. package/dist/{index-QrqMmZ75.d.cts → index-DoHqT1mq.d.cts} +203 -7
  55. package/dist/{index-CSUCQYAH.d.ts → index-YLZ0fKqE.d.ts} +203 -7
  56. package/dist/index.cjs +37 -12
  57. package/dist/index.cjs.map +1 -1
  58. package/dist/index.d.cts +9 -7
  59. package/dist/index.d.ts +9 -7
  60. package/dist/index.js +31 -6
  61. package/dist/index.js.map +1 -1
  62. package/dist/spreadsheet/index.cjs +4 -4
  63. package/dist/spreadsheet/index.js +3 -3
  64. package/package.json +1 -1
  65. package/dist/chunk-C3NWJE3K.js.map +0 -1
  66. package/dist/chunk-H4PICYNL.cjs.map +0 -1
  67. package/dist/chunk-J3R2OWOE.cjs.map +0 -1
  68. package/dist/chunk-LYGEEDNR.cjs.map +0 -1
  69. package/dist/chunk-NEOO6QJD.cjs +0 -1182
  70. package/dist/chunk-NEOO6QJD.cjs.map +0 -1
  71. package/dist/chunk-PQFAHT5O.js.map +0 -1
  72. package/dist/chunk-TK2XZK6Z.js.map +0 -1
  73. package/dist/types-BVqWm3d1.d.cts +0 -320
  74. package/dist/types-BVqWm3d1.d.ts +0 -320
  75. /package/dist/{chunk-5VXI5GED.js.map → chunk-2CJ3LPYQ.js.map} +0 -0
  76. /package/dist/{chunk-REAGGLXS.js.map → chunk-JQ3BB3E6.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-J3R2OWOE.cjs","../src/extensions/table/nesting.ts","../src/extensions/find-replace/pattern.ts","../src/extensions/find-replace/search.ts","../src/extensions/table/extension.ts","../src/extensions/table/actions.ts"],"names":["skip","exit","CellSelection"],"mappings":"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IAAA;AACK,IAAA;AACkD,MAAA;AAClD,IAAA;AACsD,MAAA;AACtD,IAAA;AACqF,MAAA;AAC/E,QAAA;AACT,MAAA;AAC8D,MAAA;AACvD,MAAA;AACJ,IAAA;AACwF,MAAA;AAClF,QAAA;AACT,MAAA;AACwE,MAAA;AACjE,MAAA;AACT,IAAA;AACkD,MAAA;AACpD,EAAA;AACF;AAgBW;AACwD,EAAA;AACtD,EAAA;AACe,EAAA;AACa,EAAA;AACvB,EAAA;AACT,IAAA;AACuB,MAAA;AACH,QAAA;AACA,QAAA;AACkB,QAAA;AACxC,MAAA;AACD,MAAA;AACG,IAAA;AAC8E,MAAA;AACjF,MAAA;AACG,IAAA;AACoF,MAAA;AACvF,MAAA;AACF,IAAA;AACwB,MAAA;AAC1B,EAAA;AACmB,EAAA;AACrB;AAkC0B;AAC6C,EAAA;AACvE;AAE2D;AAChD,EAAA;AACA,EAAA;AACM,EAAA;AACT,EAAA;AACG,EAAA;AACJ,EAAA;AACG,EAAA;AACG,EAAA;AACG,EAAA;AACF,EAAA;AACG,EAAA;AACC,EAAA;AACP,EAAA;AACV;AAG4E;AACjD,EAAA;AACH,EAAA;AACa,IAAA;AACiB,MAAA;AACuC,MAAA;AACzF,IAAA;AACH,EAAA;AACqB,EAAA;AACqE,EAAA;AAClC,EAAA;AACpD,EAAA;AAC6B,IAAA;AACR,IAAA;AACiE,IAAA;AAE/B,IAAA;AACwB,IAAA;AACI,IAAA;AAC9C,IAAA;AAChC,IAAA;AACF,MAAA;AACM,MAAA;AACiCC,MAAAA;AAChC,MAAA;AACG,MAAA;AACH,MAAA;AACG,MAAA;AACiB,MAAA;AACM,MAAA;AACJ,MAAA;AAC4B,MAAA;AACO,MAAA;AACrE,IAAA;AACM,EAAA;AAC0B,IAAA;AAClC,EAAA;AACF;AAE4D;AAcoB;AACxC,EAAA;AACH,EAAA;AACR,EAAA;AAEN,EAAA;AACc,IAAA;AACJ,IAAA;AACR,IAAA;AACV,IAAA;AACL,IAAA;AACoC,IAAA;AAC5C,EAAA;AACqB,EAAA;AACc,oBAAA;AACL,oBAAA;AACD,oBAAA;AACE,oBAAA;AAC/B,EAAA;AAEO,EAAA;AACc,IAAA;AACI,MAAA;AACc,QAAA;AACJ,QAAA;AACR,QAAA;AACR,UAAA;AACL,UAAA;AACR,QAAA;AACF,MAAA;AACO,MAAA;AACT,IAAA;AACoB,IAAA;AACa,MAAA;AACtB,QAAA;AACwB,QAAA;AAEL,QAAA;AACD,QAAA;AACE,QAAA;AAC7B,MAAA;AACsB,MAAA;AACT,MAAA;AACc,QAAA;AACG,QAAA;AAC9B,MAAA;AACF,IAAA;AACU,IAAA;AACD,MAAA;AACS,MAAA;AAClB,IAAA;AACF,EAAA;AACF;ALm/B8F;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-J3R2OWOE.cjs","sourcesContent":[null,"import { Fragment, Slice, type Node as ProsemirrorNode, type ResolvedPos } from \"@tiptap/pm/model\";\nimport { Plugin, PluginKey, Selection, type Transaction } from \"@tiptap/pm/state\";\nimport type { EditorView } from \"@tiptap/pm/view\";\nimport {\n CellSelection,\n __pastedCells,\n handlePaste as handleTablePaste,\n} from \"@tiptap/pm/tables\";\n\n/**\n * Set this meta on a transaction to let it create tables inside table cells\n * (the nesting guard leaves it alone), e.g. when loading legacy content\n * through a transaction that does not replace the whole document.\n */\nexport const ALLOW_NESTED_TABLES_META = \"allowNestedTables\";\n\nexport const tableNestingPluginKey = new PluginKey(\"tableNesting\");\n\nconst roleOf = (node: ProsemirrorNode) => node.type.spec.tableRole as string | undefined;\nconst isTableNode = (node: ProsemirrorNode) => roleOf(node) === \"table\";\n\n/** Depth of the outermost table around `$pos`, or `-1` outside tables. */\nexport function outermostTableDepth($pos: ResolvedPos): number {\n for (let d = 1; d <= $pos.depth; d++) if (isTableNode($pos.node(d))) return d;\n return -1;\n}\n\n/** Whether `$pos` lies inside a table cell or header cell. */\nexport function isInTableCell($pos: ResolvedPos): boolean {\n for (let d = $pos.depth; d > 0; d--) {\n const role = roleOf($pos.node(d));\n if (role === \"cell\" || role === \"header_cell\") return true;\n }\n return false;\n}\n\n/** Whether a slice or fragment contains a table node at any depth. */\nexport function sliceContainsTable(content: Slice | Fragment): boolean {\n const fragment = content instanceof Slice ? content.content : content;\n let found = false;\n fragment.descendants((node) => {\n if (found) return false;\n if (isTableNode(node)) found = true;\n return !found;\n });\n return found;\n}\n\n/**\n * Inserts `slice` right after the outermost table around `from` instead of\n * inside the cell, deleting `from`–`to` first when both lie in that table.\n * Returns the position right after the inserted content, or `null` (and\n * leaves `tr` unchanged) when `from` is not in a table.\n */\nexport function insertSliceAfterTable(tr: Transaction, from: number, to: number, slice: Slice): number | null {\n const $from = tr.doc.resolve(from);\n const depth = outermostTableDepth($from);\n if (depth < 0) return null;\n const tableStart = $from.before(depth);\n const tableEnd = $from.after(depth);\n if (to > from && to < tableEnd) {\n if (tr.selection instanceof CellSelection && tr.selection.from === from && tr.selection.to === to) {\n tr.deleteSelection();\n } else {\n tr.delete(from, to);\n }\n }\n const table = tr.doc.nodeAt(tableStart);\n if (!table) return null;\n const after = tableStart + table.nodeSize;\n const before = tr.doc.content.size;\n // A closed slice: the content becomes blocks of its own after the table\n // instead of merging into whatever follows it.\n tr.replace(after, after, new Slice(slice.content, 0, 0));\n return after + (tr.doc.content.size - before);\n}\n\n/**\n * Paste handling for slices that contain a table while the selection is in a\n * table: a cell selection or a pasted table (rows, cells) fills the cells from\n * the cursor (prosemirror-tables), and anything else that contains a table is\n * inserted after the outermost table, with the cursor at its end. Returns\n * `false` when the paste needs no special handling.\n */\nexport function handleTablePasteWithoutNesting(view: EditorView, event: ClipboardEvent | null, slice: Slice): boolean {\n const { state } = view;\n const { selection } = state;\n if (outermostTableDepth(selection.$from) < 0) return false;\n if (selection instanceof CellSelection || __pastedCells(slice)) {\n if (handleTablePaste(view, event as ClipboardEvent, slice)) return true;\n }\n if (!sliceContainsTable(slice)) return false;\n const tr = state.tr;\n const end = insertSliceAfterTable(tr, selection.from, selection.to, slice);\n if (end === null) return false;\n tr.setSelection(Selection.near(tr.doc.resolve(end), -1));\n view.dispatch(tr.scrollIntoView().setMeta(\"paste\", true).setMeta(\"uiEvent\", \"paste\"));\n return true;\n}\n\n/**\n * Ranges of the final document that `transactions` changed, leaving out the\n * changes of skipped transactions (their steps still map the other ranges).\n */\nfunction changedRanges(transactions: readonly Transaction[], skip: (tr: Transaction) => boolean): [number, number][] {\n let ranges: [number, number][] = [];\n for (const tr of transactions) {\n const skipped = skip(tr);\n for (const step of tr.steps) {\n const map = step.getMap();\n // Content that later (skipped) steps insert at a range's edge is not\n // part of it; pure deletions leave nothing to check.\n ranges = ranges\n .map(([from, to]): [number, number] => [map.map(from, 1), map.map(to, -1)])\n .filter(([from, to]) => to > from);\n if (skipped) continue;\n map.forEach((_oldStart, _oldEnd, newStart, newEnd) => {\n if (newEnd > newStart) ranges.push([newStart, newEnd]);\n });\n }\n }\n return ranges;\n}\n\n/** Tables starting inside `ranges` that sit in a table cell. */\nfunction findNestedTables(doc: ProsemirrorNode, ranges: [number, number][]): number[] {\n const found = new Set<number>();\n for (const [from, to] of ranges) {\n doc.nodesBetween(from, to, (node, pos) => {\n if (!isTableNode(node) || pos < from) return true;\n if (isInTableCell(doc.resolve(pos))) {\n found.add(pos);\n return false;\n }\n return true;\n });\n }\n // A table inside another found table moves with it.\n const sorted = [...found].sort((a, b) => a - b);\n const outer: number[] = [];\n for (const pos of sorted) {\n const last = outer[outer.length - 1];\n if (last === undefined || pos >= last + doc.nodeAt(last)!.nodeSize) outer.push(pos);\n }\n return outer;\n}\n\n/**\n * Moves tables that sit in table cells (at positions in `ranges`) to right\n * after their outermost table, keeping their content, and repeats for tables\n * nested in the moved ones. A cell left without content gets an empty\n * paragraph. Returns whether `tr` changed.\n */\nexport function unnestTables(tr: Transaction, ranges: [number, number][]): boolean {\n let changed = false;\n for (let round = 0; round < 32 && ranges.length; round++) {\n const nested = findNestedTables(tr.doc, ranges);\n if (!nested.length) break;\n // Group by outermost table; handle later tables first so earlier\n // positions stay valid.\n const groups = new Map<number, number[]>();\n for (const pos of nested) {\n const $pos = tr.doc.resolve(pos);\n const outer = $pos.before(outermostTableDepth($pos));\n groups.set(outer, [...(groups.get(outer) ?? []), pos]);\n }\n const next: [number, number][] = [];\n for (const outer of [...groups.keys()].sort((a, b) => b - a)) {\n const positions = groups.get(outer)!;\n const nodes = positions.map((pos) => tr.doc.nodeAt(pos)!);\n for (let i = positions.length - 1; i >= 0; i--) {\n const pos = positions[i];\n const node = nodes[i];\n const $pos = tr.doc.resolve(pos);\n const parent = $pos.parent;\n const filler = parent.childCount === 1 ? parent.type.contentMatch.defaultType?.createAndFill() : null;\n if (filler) tr.replaceWith(pos, pos + node.nodeSize, filler);\n else tr.delete(pos, pos + node.nodeSize);\n }\n const table = tr.doc.nodeAt(outer)!;\n const after = outer + table.nodeSize;\n const fragment = Fragment.fromArray(nodes);\n tr.insert(after, fragment);\n // Later groups are before this one, so this range stays valid.\n next.push([after, after + fragment.size]);\n changed = true;\n }\n ranges = next;\n }\n return changed;\n}\n\n/**\n * Whether a transaction replaces the whole document (`setContent`, loading):\n * existing documents keep their nested tables.\n */\nfunction replacesDocument(tr: Transaction): boolean {\n return tr.steps.some((step, i) => {\n const { from, to } = step as unknown as { from?: number; to?: number };\n return from === 0 && to !== undefined && to >= tr.docs[i].content.size;\n });\n}\n\nconst skip = (tr: Transaction) =>\n !tr.docChanged ||\n tr.getMeta(ALLOW_NESTED_TABLES_META) === true ||\n // Undo/redo and remote collaborative changes restore what was there.\n !!tr.getMeta(\"history$\") ||\n !!tr.getMeta(\"y-sync$\") ||\n // Other plugins' housekeeping, e.g. Track Changes putting a deleted\n // (nested) table back so the deletion can be reviewed.\n !!tr.getMeta(\"appendedTransaction\") ||\n replacesDocument(tr);\n\n/**\n * Keeps tables out of table cells: paste handling (see\n * `handleTablePasteWithoutNesting`) plus a guard that moves any table a\n * local edit puts into a cell (`insertContent`, `insertTable`, drops) to\n * right after the outermost table. Documents that already contain nested\n * tables (initial content, `setContent`, undo/redo, collaboration, Track\n * Changes restoring a deleted table) are left as they are.\n */\nexport function createTableNestingPlugin(): Plugin {\n return new Plugin({\n key: tableNestingPluginKey,\n props: {\n handlePaste: (view, event, slice) => handleTablePasteWithoutNesting(view, event, slice),\n },\n appendTransaction(transactions, _oldState, newState) {\n if (transactions.every(skip)) return null;\n const ranges = changedRanges(transactions, skip);\n if (!ranges.length) return null;\n const tr = newState.tr;\n if (!unnestTables(tr, ranges)) return null;\n return tr;\n },\n });\n}\n","// Portions ported from wordcraft/crates/engine/src/cmd/find_pattern.rs and crates/engine/src/cmd/edit.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n/**\n * Word's Find and Replace patterns: special characters (`^p`, `^t`, `^#` …),\n * the \"Use wildcards\" language (`?`, `*`, `[a-z]`, `[!x]`, `{n,m}`, `@`, `<`,\n * `>`, `( )`) and a raw regular-expression mode.\n *\n * Special and wildcard patterns are compiled to a small program run by a\n * Pike VM, which takes time linear in the text for a given pattern, so no\n * Find text can backtrack catastrophically (JavaScript's `RegExp` would). The\n * raw regex mode uses the native `RegExp` engine; it rejects nested\n * repetition and is bounded by the search budget between matches only.\n *\n * Pure TypeScript: no ProseMirror or DOM.\n */\n\n/** A paragraph mark in the searched text (`^p`). */\nexport const FIND_PARAGRAPH_MARK = \"\\r\";\n/** Longest Find what text, in characters. */\nexport const MAX_FIND_PATTERN_LENGTH = 1024;\n/** Largest count in `{n,m}`. */\nconst MAX_COUNT = 1000;\n/** Largest compiled program (instructions). Larger patterns are `too_complex`. */\nconst MAX_PROGRAM_SIZE = 20_000;\n\nexport interface FindOptions {\n matchCase?: boolean;\n /** Find whole words only (ignored with wildcards, like Word). */\n wholeWord?: boolean;\n /** The match must start a word. */\n matchPrefix?: boolean;\n /** The match must end a word. */\n matchSuffix?: boolean;\n /** Skip punctuation between the characters of the Find text. */\n ignorePunctuation?: boolean;\n /** Skip spaces and tabs between the characters of the Find text. */\n ignoreWhitespace?: boolean;\n /** Word's \"Use wildcards\". Always matches case. */\n wildcards?: boolean;\n /** A JavaScript regular expression (wins over `wildcards`). Replace text is literal. */\n regex?: boolean;\n /** Read the Find and Replace text literally: no `^` codes. */\n literal?: boolean;\n}\n\nexport type FindPatternErrorCode = \"too_long\" | \"invalid_pattern\" | \"too_complex\";\n\n/** Why a Find text cannot be used. Commands report it; they never throw it. */\nexport interface FindPatternError {\n code: FindPatternErrorCode;\n message: string;\n}\n\n/** One match in a searched string (UTF-16 offsets). */\nexport interface PatternMatch {\n start: number;\n end: number;\n /** Text of each group; index 0 is the whole match, unmatched groups are `undefined`. */\n groups: (string | undefined)[];\n}\n\n/** Shared limit on the work one search may do. */\nexport interface SearchBudget {\n /** `performance.now()`/`Date.now()` value after which the search stops. */\n deadline: number;\n /** VM steps left. */\n steps: number;\n /** Set once the budget ran out. */\n exhausted: boolean;\n}\n\nexport interface SearchBudgetOptions {\n /** Wall-clock limit in milliseconds (default 250). */\n timeLimitMs?: number;\n /** Pike VM thread steps (default 50 million). */\n maxSteps?: number;\n}\n\nconst now = (): number =>\n typeof performance !== \"undefined\" && typeof performance.now === \"function\"\n ? performance.now()\n : Date.now();\n\nexport function createSearchBudget(options: SearchBudgetOptions = {}): SearchBudget {\n return {\n deadline: now() + (options.timeLimitMs ?? 250),\n steps: options.maxSteps ?? 50_000_000,\n exhausted: false,\n };\n}\n\nexport interface CompiledFindPattern {\n mode: \"special\" | \"wildcards\" | \"regex\";\n /** The pattern can match a paragraph mark, so paragraphs must be read together. */\n spansParagraphs: boolean;\n caseInsensitive: boolean;\n /** Number of capture groups (not counting the whole match). */\n groupCount: number;\n /**\n * The leftmost match in `hay[start, end)`; text outside the range is\n * context for word assertions. `\"budget\"` when the budget ran out.\n */\n exec(hay: string, start: number, end: number, budget: SearchBudget): PatternMatch | null | \"budget\";\n}\n\nexport type CompileFindPatternResult =\n | { ok: true; pattern: CompiledFindPattern }\n | { ok: false; error: FindPatternError };\n\n// ---------------------------------------------------------------------------\n// Character tests\n// ---------------------------------------------------------------------------\n\n/** Tests a code point (and its case fold when matching ignores case). */\ntype CharTest = (cp: number, folded: number) => boolean;\n\nconst PARA = 13; // \"\\r\"\nconst ANY: CharTest = (cp) => cp !== PARA;\n\nconst LETTER = /\\p{L}/u;\nconst PUNCT = /\\p{P}/u;\nconst WORD = /[\\p{L}\\p{M}\\p{Nd}\\p{Pc}]/u;\n\nconst foldCache = new Map<number, number>();\n/** Simple (one code point to one code point) lower-case fold. */\nexport function foldCodePoint(cp: number): number {\n if (cp < 128) return cp >= 65 && cp <= 90 ? cp + 32 : cp;\n const cached = foldCache.get(cp);\n if (cached !== undefined) return cached;\n const lower = String.fromCodePoint(cp).toLowerCase();\n const folded = [...lower].length === 1 ? lower.codePointAt(0)! : cp;\n if (foldCache.size < 4096) foldCache.set(cp, folded);\n return folded;\n}\n\nfunction isWordChar(cp: number): boolean {\n if (cp < 0) return false;\n if (cp < 128) {\n return (cp >= 48 && cp <= 57) || (cp >= 65 && cp <= 90) || (cp >= 97 && cp <= 122) || cp === 95;\n }\n return WORD.test(String.fromCodePoint(cp));\n}\n\nconst isLetter = (cp: number) =>\n cp < 128 ? (cp >= 65 && cp <= 90) || (cp >= 97 && cp <= 122) : LETTER.test(String.fromCodePoint(cp));\n\nfunction isPunct(cp: number): boolean {\n if (cp < 128) {\n return (cp >= 33 && cp <= 47) || (cp >= 58 && cp <= 64) || (cp >= 91 && cp <= 96) || (cp >= 123 && cp <= 126);\n }\n return (cp >= 0x2010 && cp <= 0x2027) || cp === 0xa1 || cp === 0xbf || cp === 0xab || cp === 0xbb || PUNCT.test(String.fromCodePoint(cp));\n}\n\nconst isSpace = (cp: number) => cp === 32 || cp === 9 || cp === 0xa0;\n\n/** Code point before `pos` in `hay` (-1 at the start). */\nfunction cpBefore(hay: string, pos: number): number {\n if (pos <= 0) return -1;\n const low = hay.charCodeAt(pos - 1);\n if (low >= 0xdc00 && low <= 0xdfff && pos >= 2) {\n const high = hay.charCodeAt(pos - 2);\n if (high >= 0xd800 && high <= 0xdbff) return hay.codePointAt(pos - 2)!;\n }\n return low;\n}\n\nconst cpAt = (hay: string, pos: number): number => (pos < hay.length ? hay.codePointAt(pos)! : -1);\n\n// ---------------------------------------------------------------------------\n// Pattern AST\n// ---------------------------------------------------------------------------\n\ntype AssertKind = \"boundary\" | \"wordStart\" | \"wordEnd\";\n\ntype Node =\n | { k: \"char\"; test: CharTest }\n | { k: \"cat\"; items: Node[] }\n | { k: \"group\"; index: number; body: Node }\n | { k: \"rep\"; body: Node; min: number; max: number; lazy: boolean } // max -1 = unbounded\n | { k: \"assert\"; kind: AssertKind };\n\nclass PatternError extends Error {\n constructor(\n readonly code: FindPatternErrorCode,\n message: string,\n ) {\n super(message);\n }\n}\n\nconst charNode = (test: CharTest): Node => ({ k: \"char\", test });\nconst exact = (cp: number): Node => charNode((c) => c === cp);\nconst literalNode = (cp: number, caseInsensitive: boolean): Node => {\n if (!caseInsensitive) return exact(cp);\n const f = foldCodePoint(cp);\n return charNode((_c, folded) => folded === f);\n};\nconst star = (body: Node, lazy: boolean): Node => ({ k: \"rep\", body, min: 0, max: -1, lazy });\n\n/** What a `^` code stands for in Find what. */\nfunction findCode(c: string): Node | null {\n switch (c) {\n case \"p\": return exact(PARA);\n case \"t\": return exact(9);\n case \"?\": return charNode(ANY);\n case \"#\": return charNode((cp) => cp >= 48 && cp <= 57);\n case \"$\": return charNode((cp) => isLetter(cp));\n case \"^\": return exact(94);\n case \"l\": return exact(10);\n case \"m\": return exact(0x0c);\n case \"n\": return exact(0x0e);\n case \"+\": return exact(0x2014);\n case \"=\": return exact(0x2013);\n case \"s\": return exact(0xa0);\n case \"~\": return exact(0x2011);\n case \"-\": return exact(0xad);\n case \"w\": return { k: \"rep\", body: charNode((cp) => isSpace(cp)), min: 1, max: -1, lazy: false };\n case \"g\": return exact(0xfffc);\n case \"%\": return exact(0xa7);\n case \"v\": return exact(0xb6);\n default: return null;\n }\n}\n\n/** What a `^` code stands for in Replace with. */\nfunction replaceCode(c: string): string | null {\n switch (c) {\n case \"p\": return FIND_PARAGRAPH_MARK;\n case \"t\": return \"\\t\";\n case \"^\": return \"^\";\n case \"l\": return \"\\n\";\n case \"m\": return \"\\u000c\";\n case \"n\": return \"\\u000e\";\n case \"+\": return \"\\u2014\";\n case \"=\": return \"\\u2013\";\n case \"s\": return \"\\u00a0\";\n case \"~\": return \"\\u2011\";\n case \"-\": return \"\\u00ad\";\n case \"%\": return \"\\u00a7\";\n case \"v\": return \"\\u00b6\";\n default: return null;\n }\n}\n\n/** A character code (`^13`, `^0151`, `^u8212`) as the character it stands for. */\nfunction codeChar(n: number): number | null {\n switch (n) {\n case 13: return PARA;\n case 11: return 10;\n case 12: return 0x0c;\n case 14: return 0x0e;\n case 30: return 0x2011;\n case 31: return 0xad;\n }\n if (n > 0x10ffff || (n >= 0xd800 && n <= 0xdfff)) return null;\n // Control characters other than tab stand for nothing here.\n if ((n < 32 && n !== 9) || (n >= 0x7f && n < 0xa0)) return null;\n return n;\n}\n\n/** A numeric code after `^` starting at `chars[i]`: the code point and characters used. */\nfunction numericCode(chars: string[], i: number): { cp: number; used: number } | null {\n const unicode = chars[i] === \"u\";\n const start = unicode ? i + 1 : i;\n const max = unicode ? 5 : 4;\n let digits = \"\";\n for (let j = start; j < chars.length && digits.length < max && /[0-9]/.test(chars[j]); j++) digits += chars[j];\n if (!digits) return null;\n const cp = codeChar(parseInt(digits, 10));\n return cp === null ? null : { cp, used: start - i + digits.length };\n}\n\nconst toChars = (s: string): string[] => Array.from(s);\nconst cpOf = (ch: string): number => ch.codePointAt(0)!;\n\n/** Normal search: literal text with `^` codes, optionally skipping punctuation and white space. */\nfunction parseSpecial(query: string, o: FindOptions, caseInsensitive: boolean): { node: Node; spans: boolean } {\n const chars = toChars(query);\n const atoms: Node[] = [];\n let spans = false;\n let i = 0;\n while (i < chars.length) {\n const c = chars[i++];\n if (c === \"^\" && !o.literal && i < chars.length) {\n const n = chars[i];\n const code = findCode(n);\n if (code) {\n if (n === \"p\") spans = true;\n atoms.push(code);\n i++;\n continue;\n }\n const num = numericCode(chars, i);\n if (num) {\n if (num.cp === PARA) spans = true;\n atoms.push(literalNode(num.cp, caseInsensitive));\n i += num.used;\n continue;\n }\n // Not a code: the caret itself.\n }\n const cp = cpOf(c);\n if ((o.ignorePunctuation && isPunct(cp)) || (o.ignoreWhitespace && isSpace(cp))) continue;\n atoms.push(literalNode(cp, caseInsensitive));\n }\n const filler: CharTest | null =\n o.ignorePunctuation && o.ignoreWhitespace\n ? (cp) => isPunct(cp) || isSpace(cp)\n : o.ignorePunctuation\n ? (cp) => isPunct(cp)\n : o.ignoreWhitespace\n ? (cp) => isSpace(cp)\n : null;\n const items: Node[] = [];\n atoms.forEach((atom, index) => {\n if (index > 0 && filler) items.push(star(charNode(filler), false));\n items.push(atom);\n });\n return { node: { k: \"cat\", items }, spans };\n}\n\n/** `{n}`, `{n,}`, `{n,m}` (and `{,m}`). */\nfunction parseCount(body: string): { min: number; max: number } {\n const bad = () => new PatternError(\"invalid_pattern\", `{${body}} is not a valid count`);\n const num = (s: string): number | null => {\n const t = s.trim();\n if (!t) return null;\n if (!/^[0-9]+$/.test(t)) throw bad();\n const n = parseInt(t, 10);\n if (n > MAX_COUNT) throw new PatternError(\"invalid_pattern\", `counts go up to ${MAX_COUNT}`);\n return n;\n };\n const sep = body.search(/[,;]/);\n if (sep < 0) {\n const n = num(body);\n if (n === null) throw bad();\n return { min: n, max: n };\n }\n const a = num(body.slice(0, sep)) ?? 0;\n const b = num(body.slice(sep + 1));\n if (b !== null && b < a) throw bad();\n return { min: a, max: b ?? -1 };\n}\n\n/** `[abc]`, `[a-z]`, `[!x]`: the characters inside are literal. */\nfunction parseClass(inner: string[]): CharTest {\n let negated = false;\n if (inner[0] === \"!\") {\n negated = true;\n inner = inner.slice(1);\n }\n if (inner.length === 0) throw new PatternError(\"invalid_pattern\", \"[ ] needs at least one character\");\n const singles = new Set<number>();\n const ranges: [number, number][] = [];\n let i = 0;\n while (i < inner.length) {\n let c = inner[i];\n if (c === \"\\\\\" && i + 1 < inner.length) c = inner[++i];\n i++;\n if (inner[i] === \"-\" && i + 1 < inner.length) {\n const hi = inner[i + 1];\n if (cpOf(hi) < cpOf(c)) throw new PatternError(\"invalid_pattern\", `[${c}-${hi}] is not in ascending order`);\n ranges.push([cpOf(c), cpOf(hi)]);\n i += 2;\n } else {\n singles.add(cpOf(c));\n }\n }\n const inSet = (cp: number) => singles.has(cp) || ranges.some(([lo, hi]) => cp >= lo && cp <= hi);\n return negated ? (cp) => cp !== PARA && !inSet(cp) : (cp) => inSet(cp);\n}\n\n/** \"Use wildcards\" search. */\nfunction parseWildcards(query: string): { node: Node; spans: boolean; groups: number } {\n const chars = toChars(query);\n const stack: { items: Node[]; group: number }[] = [{ items: [], group: 0 }];\n let groups = 0;\n let spans = false;\n const top = () => stack[stack.length - 1].items;\n const quantify = (min: number, max: number, what: string) => {\n const items = top();\n const last = items[items.length - 1];\n if (!last || last.k === \"assert\" || last.k === \"rep\") {\n throw new PatternError(\"invalid_pattern\", `${what} needs something to repeat before it`);\n }\n items[items.length - 1] = { k: \"rep\", body: last, min, max, lazy: false };\n };\n let i = 0;\n while (i < chars.length) {\n const c = chars[i++];\n switch (c) {\n case \"?\":\n top().push(charNode(ANY));\n break;\n case \"*\":\n top().push(star(charNode(ANY), true));\n break;\n case \"@\":\n quantify(1, -1, \"@\");\n break;\n case \"<\":\n top().push({ k: \"assert\", kind: \"wordStart\" });\n break;\n case \">\":\n top().push({ k: \"assert\", kind: \"wordEnd\" });\n break;\n case \"(\":\n stack.push({ items: [], group: ++groups });\n break;\n case \")\": {\n if (stack.length === 1) throw new PatternError(\"invalid_pattern\", \"a ) has no opening (\");\n const frame = stack.pop()!;\n top().push({ k: \"group\", index: frame.group, body: { k: \"cat\", items: frame.items } });\n break;\n }\n case \"{\": {\n const end = chars.indexOf(\"}\", i);\n if (end < 0) throw new PatternError(\"invalid_pattern\", \"a { needs a closing }\");\n const { min, max } = parseCount(chars.slice(i, end).join(\"\"));\n quantify(min, max, `{${chars.slice(i, end).join(\"\")}}`);\n i = end + 1;\n break;\n }\n case \"[\": {\n const end = chars.indexOf(\"]\", i);\n if (end < 0) throw new PatternError(\"invalid_pattern\", \"a [ needs a closing ]\");\n top().push(charNode(parseClass(chars.slice(i, end))));\n i = end + 1;\n break;\n }\n case \"\\\\\": {\n if (i >= chars.length) {\n top().push(exact(92));\n } else if (/[0-9]/.test(chars[i])) {\n throw new PatternError(\"invalid_pattern\", \"back-references (\\\\1) work in Replace with, not in Find what\");\n } else {\n top().push(exact(cpOf(chars[i++])));\n }\n break;\n }\n case \"^\": {\n if (i >= chars.length) {\n top().push(exact(94));\n break;\n }\n const n = chars[i];\n const code = findCode(n);\n if (code) {\n if (n === \"p\") spans = true;\n top().push(code);\n i++;\n break;\n }\n const num = numericCode(chars, i);\n if (num) {\n if (num.cp === PARA) spans = true;\n top().push(exact(num.cp));\n i += num.used;\n } else {\n top().push(exact(94));\n }\n break;\n }\n default:\n top().push(exact(cpOf(c)));\n }\n }\n if (stack.length > 1) throw new PatternError(\"invalid_pattern\", \"a ( needs a closing )\");\n return { node: { k: \"cat\", items: stack[0].items }, spans, groups };\n}\n\n// ---------------------------------------------------------------------------\n// Program and Pike VM\n// ---------------------------------------------------------------------------\n\nenum Op {\n Char,\n Split,\n Jmp,\n Save,\n Assert,\n Match,\n}\n\ntype Inst =\n | { op: Op.Char; test: CharTest }\n | { op: Op.Split; x: number; y: number }\n | { op: Op.Jmp; x: number }\n | { op: Op.Save; n: number }\n | { op: Op.Assert; kind: AssertKind }\n | { op: Op.Match };\n\nfunction compileProgram(node: Node): Inst[] {\n const prog: Inst[] = [];\n const push = (inst: Inst): number => {\n if (prog.length >= MAX_PROGRAM_SIZE) {\n throw new PatternError(\"too_complex\", \"the pattern is too complex (too many repeats)\");\n }\n prog.push(inst);\n return prog.length - 1;\n };\n const emit = (n: Node): void => {\n switch (n.k) {\n case \"char\":\n push({ op: Op.Char, test: n.test });\n return;\n case \"cat\":\n n.items.forEach(emit);\n return;\n case \"group\":\n push({ op: Op.Save, n: 2 * n.index });\n emit(n.body);\n push({ op: Op.Save, n: 2 * n.index + 1 });\n return;\n case \"assert\":\n push({ op: Op.Assert, kind: n.kind });\n return;\n case \"rep\": {\n for (let k = 0; k < n.min; k++) emit(n.body);\n if (n.max < 0) {\n const split = push({ op: Op.Split, x: 0, y: 0 });\n emit(n.body);\n push({ op: Op.Jmp, x: split });\n const exit = prog.length;\n const inst = prog[split] as { op: Op.Split; x: number; y: number };\n inst.x = n.lazy ? exit : split + 1;\n inst.y = n.lazy ? split + 1 : exit;\n return;\n }\n const splits: number[] = [];\n for (let k = n.min; k < n.max; k++) {\n splits.push(push({ op: Op.Split, x: 0, y: 0 }));\n emit(n.body);\n }\n const exit = prog.length;\n for (const s of splits) {\n const inst = prog[s] as { op: Op.Split; x: number; y: number };\n inst.x = n.lazy ? exit : s + 1;\n inst.y = n.lazy ? s + 1 : exit;\n }\n return;\n }\n }\n };\n push({ op: Op.Save, n: 0 });\n emit(node);\n push({ op: Op.Save, n: 1 });\n push({ op: Op.Match });\n return prog;\n}\n\nfunction checkAssert(kind: AssertKind, hay: string, pos: number): boolean {\n const before = isWordChar(cpBefore(hay, pos));\n const after = isWordChar(cpAt(hay, pos));\n switch (kind) {\n case \"boundary\": return before !== after;\n case \"wordStart\": return !before && after;\n case \"wordEnd\": return before && !after;\n }\n}\n\n/**\n * Leftmost-first search (Perl/Rust priorities: greedy, lazy) with a Pike VM:\n * every position is visited once per live instruction, so time is\n * O(text × program) whatever the pattern.\n */\nfunction pikeExec(\n prog: Inst[],\n groupCount: number,\n caseInsensitive: boolean,\n hay: string,\n start: number,\n end: number,\n budget: SearchBudget,\n): PatternMatch | null | \"budget\" {\n const ncap = 2 * (groupCount + 1);\n const mark = new Int32Array(prog.length);\n let gen = 1;\n let clistPc: number[] = [];\n let clistCaps: Int32Array[] = [];\n let nlistPc: number[] = [];\n let nlistCaps: Int32Array[] = [];\n const stackPc: number[] = [];\n const stackCaps: Int32Array[] = [];\n let matched: Int32Array | null = null;\n const initial = new Int32Array(ncap).fill(-1);\n\n const add = (listPc: number[], listCaps: Int32Array[], pc0: number, caps0: Int32Array, pos: number) => {\n stackPc.push(pc0);\n stackCaps.push(caps0);\n while (stackPc.length) {\n const pc = stackPc.pop()!;\n const caps = stackCaps.pop()!;\n if (mark[pc] === gen) continue;\n mark[pc] = gen;\n const inst = prog[pc];\n switch (inst.op) {\n case Op.Jmp:\n stackPc.push(inst.x);\n stackCaps.push(caps);\n break;\n case Op.Split:\n stackPc.push(inst.y, inst.x);\n stackCaps.push(caps, caps);\n break;\n case Op.Save: {\n const next = caps.slice();\n next[inst.n] = pos;\n stackPc.push(pc + 1);\n stackCaps.push(next);\n break;\n }\n case Op.Assert:\n if (checkAssert(inst.kind, hay, pos)) {\n stackPc.push(pc + 1);\n stackCaps.push(caps);\n }\n break;\n default:\n listPc.push(pc);\n listCaps.push(caps);\n }\n }\n };\n\n let i = start;\n if (budget.steps <= 0 || now() > budget.deadline) {\n budget.exhausted = true;\n return \"budget\";\n }\n let steps = 0;\n for (;;) {\n if (!matched) add(clistPc, clistCaps, 0, initial, i);\n const cp = i < end ? hay.codePointAt(i)! : -1;\n const width = cp > 0xffff ? 2 : 1;\n if (clistPc.length === 0) {\n // Nothing alive here (e.g. an assertion failed at the start): move on.\n if (matched || i >= end) break;\n gen++;\n i += width;\n continue;\n }\n const folded = cp >= 0 && caseInsensitive ? foldCodePoint(cp) : cp;\n gen++;\n nlistPc = [];\n nlistCaps = [];\n for (let t = 0; t < clistPc.length; t++) {\n if (++steps >= 4096) {\n budget.steps -= steps;\n steps = 0;\n if (budget.steps <= 0 || now() > budget.deadline) {\n budget.exhausted = true;\n return \"budget\";\n }\n }\n const inst = prog[clistPc[t]];\n if (inst.op === Op.Char) {\n if (cp >= 0 && inst.test(cp, folded)) add(nlistPc, nlistCaps, clistPc[t] + 1, clistCaps[t], i + width);\n } else if (inst.op === Op.Match) {\n matched = clistCaps[t];\n // Lower-priority threads lose to this match.\n break;\n }\n }\n clistPc = nlistPc;\n clistCaps = nlistCaps;\n if (i >= end) break;\n i += width;\n }\n budget.steps -= steps;\n if (!matched) return null;\n const groups: (string | undefined)[] = [];\n for (let g = 0; g <= groupCount; g++) {\n const a = matched[2 * g];\n const b = matched[2 * g + 1];\n groups.push(a >= 0 && b >= 0 ? hay.slice(a, b) : undefined);\n }\n return { start: matched[0], end: matched[1], groups };\n}\n\n// ---------------------------------------------------------------------------\n// Raw regex mode\n// ---------------------------------------------------------------------------\n\nconst WORD_CLASS = \"[\\\\p{L}\\\\p{M}\\\\p{Nd}\\\\p{Pc}]\";\nconst REGEX_BOUNDARY = `(?:(?<=${WORD_CLASS})(?!${WORD_CLASS})|(?<!${WORD_CLASS})(?=${WORD_CLASS}))`;\n\n/**\n * Rejects a repeated group that itself contains a repeat (`(a+)+`, `(\\w*)*`),\n * the shape behind most catastrophic backtracking in `RegExp`.\n */\nfunction hasNestedRepeat(source: string): boolean {\n /** The quantifier at `i`: whether it repeats at all, and whether more than once. */\n const quantifierAt = (i: number): { repeats: boolean; many: boolean } => {\n const c = source[i];\n if (c === \"*\" || c === \"+\") return { repeats: true, many: true };\n if (c === \"{\") {\n const m = /^\\{(\\d*)(,?)(\\d*)\\}/.exec(source.slice(i));\n if (m) {\n const max = m[2] ? (m[3] ? Number(m[3]) : Infinity) : Number(m[1]);\n return { repeats: max > 1, many: max > 1 };\n }\n }\n return { repeats: false, many: false };\n };\n // For each open group: whether it contains a repeat.\n const stack: boolean[] = [];\n let inClass = false;\n for (let i = 0; i < source.length; i++) {\n const c = source[i];\n if (c === \"\\\\\") {\n i++;\n continue;\n }\n if (inClass) {\n if (c === \"]\") inClass = false;\n continue;\n }\n if (c === \"[\") {\n inClass = true;\n } else if (c === \"(\") {\n stack.push(false);\n } else if (c === \")\") {\n const inner = stack.pop() ?? false;\n const q = quantifierAt(i + 1);\n if (inner && q.many) return true;\n if (stack.length && (inner || q.repeats)) stack[stack.length - 1] = true;\n } else if (stack.length && quantifierAt(i).repeats) {\n stack[stack.length - 1] = true;\n }\n }\n return false;\n}\n\nfunction compileRegex(query: string, o: FindOptions): CompiledFindPattern {\n if (hasNestedRepeat(query)) {\n throw new PatternError(\n \"too_complex\",\n \"a repeated group containing a repeat, like (a+)+, can take exponential time; rewrite it without nesting\",\n );\n }\n let source = query;\n if (o.wholeWord || (o.matchPrefix && o.matchSuffix)) source = `${REGEX_BOUNDARY}(?:${query})${REGEX_BOUNDARY}`;\n else if (o.matchPrefix) source = `${REGEX_BOUNDARY}(?:${query})`;\n else if (o.matchSuffix) source = `(?:${query})${REGEX_BOUNDARY}`;\n let re: RegExp;\n try {\n re = new RegExp(source, o.matchCase ? \"gu\" : \"giu\");\n } catch (error) {\n throw new PatternError(\"invalid_pattern\", `bad pattern: ${error instanceof Error ? error.message : String(error)}`);\n }\n let slicedFor: { hay: string; end: number; text: string } | null = null;\n return {\n mode: \"regex\",\n spansParagraphs: false,\n caseInsensitive: !o.matchCase,\n groupCount: 0,\n exec(hay, start, end, budget) {\n if (now() > budget.deadline) {\n budget.exhausted = true;\n return \"budget\";\n }\n let text = hay;\n if (end < hay.length) {\n if (!slicedFor || slicedFor.hay !== hay || slicedFor.end !== end) {\n slicedFor = { hay, end, text: hay.slice(0, end) };\n }\n text = slicedFor.text;\n }\n re.lastIndex = start;\n const m = re.exec(text);\n if (!m) return null;\n return { start: m.index, end: m.index + m[0].length, groups: Array.from(m) };\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** Translates Word's Find what text. Never throws: invalid text returns `{ ok: false, error }`. */\nexport function compileFindPattern(query: string, options: FindOptions = {}): CompileFindPatternResult {\n try {\n if (Array.from(query).length > MAX_FIND_PATTERN_LENGTH) {\n throw new PatternError(\"too_long\", `the Find what text is longer than ${MAX_FIND_PATTERN_LENGTH} characters`);\n }\n if (options.regex) return { ok: true, pattern: compileRegex(query, options) };\n\n const wildcards = !!options.wildcards && !options.literal;\n // Word's wildcard search always matches case.\n const caseInsensitive = !options.matchCase && !wildcards;\n let node: Node;\n let spans: boolean;\n let groups = 0;\n if (wildcards) {\n ({ node, spans, groups } = parseWildcards(query));\n } else {\n ({ node, spans } = parseSpecial(query, options, caseInsensitive));\n const before = options.wholeWord || options.matchPrefix;\n const after = options.wholeWord || options.matchSuffix;\n if (before || after) {\n const items: Node[] = [];\n if (before) items.push({ k: \"assert\", kind: \"boundary\" });\n items.push(node);\n if (after) items.push({ k: \"assert\", kind: \"boundary\" });\n node = { k: \"cat\", items };\n }\n }\n const prog = compileProgram(node);\n return {\n ok: true,\n pattern: {\n mode: wildcards ? \"wildcards\" : \"special\",\n spansParagraphs: spans,\n caseInsensitive,\n groupCount: groups,\n exec: (hay, start, end, budget) => pikeExec(prog, groups, caseInsensitive, hay, start, end, budget),\n },\n };\n } catch (error) {\n if (error instanceof PatternError) return { ok: false, error: { code: error.code, message: error.message } };\n return { ok: false, error: { code: \"invalid_pattern\", message: error instanceof Error ? error.message : String(error) } };\n }\n}\n\nexport interface FindAllOptions {\n /**\n * Offsets where the live text breaks (e.g. where tracked-deleted text was\n * skipped): no match spans a cut.\n */\n cuts?: readonly number[];\n budget?: SearchBudget;\n /** Stop after this many matches (default 10 000). */\n maxMatches?: number;\n}\n\nexport interface FindAllResult {\n matches: PatternMatch[];\n /** The search stopped early (match cap or time budget). */\n truncated: boolean;\n}\n\n/**\n * Every non-empty match in `hay`, left to right, without overlaps. Empty\n * matches are stepped past so the search always moves on.\n */\nexport function findAllMatches(\n pattern: CompiledFindPattern,\n hay: string,\n options: FindAllOptions = {},\n): FindAllResult {\n const budget = options.budget ?? createSearchBudget();\n const maxMatches = options.maxMatches ?? 10_000;\n const cuts = [...(options.cuts ?? []), hay.length].sort((a, b) => a - b);\n const matches: PatternMatch[] = [];\n let from = 0;\n for (const cut of cuts) {\n if (cut <= from) continue;\n let pos = from;\n from = cut;\n for (;;) {\n const m = pattern.exec(hay, pos, cut, budget);\n if (m === \"budget\") return { matches, truncated: true };\n if (!m) break;\n const empty = m.end === m.start;\n const next = empty ? m.end + (cpAt(hay, m.end) > 0xffff ? 2 : 1) : m.end;\n if (next > cut) break;\n pos = next;\n if (empty) continue;\n matches.push(m);\n if (matches.length >= maxMatches) return { matches, truncated: true };\n }\n }\n return { matches, truncated: false };\n}\n\n/** A piece of Replace with text. */\nexport type ReplacePiece =\n | { type: \"text\"; text: string }\n /** `^&`: the text found. */\n | { type: \"found\" }\n /** `\\n` with wildcards: group `n`. */\n | { type: \"group\"; index: number }\n /** `^c`: the clipboard. */\n | { type: \"clipboard\" };\n\n/** Reads Replace with text: `^` codes (unless `literal`), and `\\1`–`\\9` with wildcards. Regex mode is literal. */\nexport function parseReplacement(replacement: string, options: FindOptions = {}): ReplacePiece[] {\n if (options.literal || options.regex) return replacement ? [{ type: \"text\", text: replacement }] : [];\n const chars = toChars(replacement);\n const out: ReplacePiece[] = [];\n let cur = \"\";\n const flush = () => {\n if (cur) out.push({ type: \"text\", text: cur });\n cur = \"\";\n };\n let i = 0;\n while (i < chars.length) {\n const c = chars[i++];\n if (c === \"\\\\\" && options.wildcards && i < chars.length) {\n const d = chars[i];\n if (/[1-9]/.test(d)) {\n flush();\n out.push({ type: \"group\", index: Number(d) });\n } else {\n cur += d;\n }\n i++;\n continue;\n }\n if (c === \"^\" && i < chars.length) {\n const n = chars[i];\n if (n === \"&\" || n === \"c\") {\n flush();\n out.push(n === \"&\" ? { type: \"found\" } : { type: \"clipboard\" });\n i++;\n continue;\n }\n const code = replaceCode(n);\n if (code !== null) {\n cur += code;\n i++;\n continue;\n }\n const num = numericCode(chars, i);\n if (num) {\n cur += String.fromCodePoint(num.cp);\n i += num.used;\n continue;\n }\n }\n cur += c;\n }\n flush();\n return out;\n}\n\n/** Fills in Replace with text for one match. Paragraph marks in the result are `\\r`. */\nexport function expandReplacement(pieces: readonly ReplacePiece[], match: PatternMatch, clipboardText = \"\"): string {\n let out = \"\";\n for (const piece of pieces) {\n switch (piece.type) {\n case \"text\":\n out += piece.text;\n break;\n case \"found\":\n out += match.groups[0] ?? \"\";\n break;\n case \"group\":\n out += match.groups[piece.index] ?? \"\";\n break;\n case \"clipboard\":\n out += clipboardText.replace(/\\r\\n/g, \"\\r\").replace(/\\n/g, \"\\r\");\n break;\n }\n }\n return out;\n}\n","// Portions ported from wordcraft/crates/engine/src/cmd/edit.rs (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\nimport type { Mark as ProsemirrorMark, Node as ProsemirrorNode, Schema } from \"@tiptap/pm/model\";\nimport type { Transaction } from \"@tiptap/pm/state\";\nimport {\n FIND_PARAGRAPH_MARK,\n compileFindPattern,\n createSearchBudget,\n expandReplacement,\n findAllMatches,\n parseReplacement,\n type FindOptions,\n type FindPatternError,\n} from \"./pattern\";\n\n/** Name of the tracked-deletion mark (see `track-changes.ts`); deleted text is not searched. */\nconst DELETION_MARK = \"deletion\";\n/** Review marks the replacement text does not inherit from the found text. */\nconst DROPPED_REPLACEMENT_MARKS = new Set([DELETION_MARK, \"formatChange\"]);\n/** Stands for an inline node other than text or a hard break (`^g`). */\nconst OBJECT_CHAR = \"\\ufffc\";\n\nexport interface FindMatch {\n /** Document position of the first matched character. */\n from: number;\n /** Document position after the last matched character. */\n to: number;\n /** The matched text (`\\r` for paragraph marks, `\\n` for hard breaks). */\n text: string;\n /** The Replace with text for this match (`\\r` splits paragraphs). */\n replacement: string;\n}\n\nexport interface DocumentSearchOptions {\n /** Replace with text (`^&`, `^c`, `\\1`…). */\n replacement?: string;\n /** Text of `^c` in the replacement. */\n clipboardText?: string;\n /** Wall-clock budget for the whole search (default 250 ms). */\n timeLimitMs?: number;\n /** Match cap (default 10 000). */\n maxMatches?: number;\n}\n\nexport interface DocumentSearchResult {\n matches: FindMatch[];\n /** The search stopped early at the match cap or time budget. */\n truncated: boolean;\n error: FindPatternError | null;\n}\n\ninterface Block {\n node: ProsemirrorNode;\n pos: number;\n parent: ProsemirrorNode | null;\n index: number;\n}\n\n/**\n * Finds `query` in a document. Each textblock is read as text without\n * tracked-deleted runs (a deletion \"cuts\" the text, so no match spans it, but\n * word boundaries see the live text on both sides); a pattern that can match\n * `^p` reads runs of sibling textblocks as one text with `\\r` between them.\n */\nexport function searchDocument(\n doc: ProsemirrorNode,\n query: string,\n options: FindOptions = {},\n searchOptions: DocumentSearchOptions = {},\n): DocumentSearchResult {\n if (!query) return { matches: [], truncated: false, error: null };\n const compiled = compileFindPattern(query, options);\n if (!compiled.ok) return { matches: [], truncated: false, error: compiled.error };\n const pattern = compiled.pattern;\n const pieces = parseReplacement(searchOptions.replacement ?? \"\", options);\n const budget = createSearchBudget({ timeLimitMs: searchOptions.timeLimitMs });\n const maxMatches = searchOptions.maxMatches ?? 10_000;\n\n const blocks: Block[] = [];\n doc.descendants((node, pos, parent, index) => {\n if (!node.isTextblock) return true;\n blocks.push({ node, pos, parent, index });\n return false;\n });\n\n // Textblocks read as one text: consecutive siblings when the pattern spans paragraphs.\n const chunks: Block[][] = [];\n blocks.forEach((block, i) => {\n const prev = blocks[i - 1];\n const joins =\n pattern.spansParagraphs && prev && prev.parent === block.parent && prev.index + 1 === block.index;\n if (joins) chunks[chunks.length - 1].push(block);\n else chunks.push([block]);\n });\n\n const matches: FindMatch[] = [];\n let truncated = false;\n for (const chunk of chunks) {\n let hay = \"\";\n // Document position of each hay offset, and the position after it.\n const posOf: number[] = [];\n const endOf: number[] = [];\n const cuts: number[] = [];\n chunk.forEach((block, k) => {\n const contentStart = block.pos + 1;\n block.node.forEach((child, offset) => {\n const at = contentStart + offset;\n if (child.marks.some((m) => m.type.name === DELETION_MARK)) {\n if (cuts[cuts.length - 1] !== hay.length) cuts.push(hay.length);\n return;\n }\n if (child.isText) {\n const text = child.text ?? \"\";\n for (let j = 0; j < text.length; j++) {\n posOf.push(at + j);\n endOf.push(at + j + 1);\n }\n hay += text;\n } else {\n posOf.push(at);\n endOf.push(at + child.nodeSize);\n hay += child.type.name === \"hardBreak\" ? \"\\n\" : OBJECT_CHAR;\n }\n });\n const next = chunk[k + 1];\n if (next) {\n posOf.push(contentStart + block.node.content.size);\n endOf.push(next.pos + 1);\n hay += FIND_PARAGRAPH_MARK;\n }\n });\n if (!hay) continue;\n const result = findAllMatches(pattern, hay, {\n cuts,\n budget,\n maxMatches: maxMatches - matches.length,\n });\n for (const m of result.matches) {\n matches.push({\n from: posOf[m.start],\n to: endOf[m.end - 1],\n text: m.groups[0] ?? hay.slice(m.start, m.end),\n replacement: expandReplacement(pieces, m, searchOptions.clipboardText ?? \"\"),\n });\n }\n if (result.truncated) {\n truncated = true;\n break;\n }\n }\n return { matches, truncated, error: null };\n}\n\n/** Marks the replacement for a match inherits: the found text's, minus tracked-change marks. */\nfunction replacementMarks(doc: ProsemirrorNode, from: number, to: number): readonly ProsemirrorMark[] {\n const $from = doc.resolve(from);\n const node = from < to ? $from.nodeAfter : null;\n const marks = node ? node.marks : $from.marks();\n return marks.filter((m) => !DROPPED_REPLACEMENT_MARKS.has(m.type.name));\n}\n\n/** Inline content for a piece of replacement text: `\\n` becomes a hard break when the schema has one. */\nfunction inlineContent(schema: Schema, text: string, marks: readonly ProsemirrorMark[]): ProsemirrorNode[] {\n const nodes: ProsemirrorNode[] = [];\n const hardBreak = schema.nodes.hardBreak;\n const parts = hardBreak ? text.split(\"\\n\") : [text];\n parts.forEach((part, i) => {\n if (i > 0) nodes.push(hardBreak.create(null, null, marks));\n if (part) nodes.push(schema.text(part, marks));\n });\n return nodes;\n}\n\n/**\n * Replaces matches in one transaction, last first so earlier positions stay\n * valid. A paragraph mark (`\\r` or U+2029) in a replacement splits the\n * paragraph. Returns the mapped end of each replacement (in match order).\n */\nexport function applyReplacements(tr: Transaction, matches: readonly FindMatch[]): number[] {\n const ends: number[] = new Array(matches.length);\n const schema = tr.doc.type.schema;\n for (let i = matches.length - 1; i >= 0; i--) {\n const { from, to, replacement } = matches[i];\n const marks = replacementMarks(tr.doc, from, to);\n const parts = replacement.split(/[\\r\\u2029]/);\n const stepsBefore = tr.steps.length;\n const first = inlineContent(schema, parts[0], marks);\n if (first.length) tr.replaceWith(from, to, first);\n else if (to > from) tr.delete(from, to);\n let pos = tr.mapping.slice(stepsBefore).map(to, 1);\n for (let k = 1; k < parts.length; k++) {\n tr.split(pos);\n pos += 2;\n const content = inlineContent(schema, parts[k], marks);\n if (content.length) {\n tr.insert(pos, content);\n pos += content.reduce((size, node) => size + node.nodeSize, 0);\n }\n }\n // Positions of later replacements were already mapped; record this one's end\n // and shift the later ends by what this replacement changed.\n const map = tr.mapping.slice(stepsBefore);\n for (let j = i + 1; j < matches.length; j++) ends[j] = map.map(ends[j], 1);\n ends[i] = pos;\n }\n return ends;\n}\n","import { getHTMLFromFragment, type Extensions } from \"@tiptap/core\";\nimport { Fragment, type Node as ProsemirrorNode } from \"@tiptap/pm/model\";\nimport * as _Table from \"@tiptap/extension-table\";\nimport * as _TableRow from \"@tiptap/extension-table-row\";\nimport * as _TableHeader from \"@tiptap/extension-table-header\";\nimport * as _TableCell from \"@tiptap/extension-table-cell\";\nimport { createTableNestingPlugin } from \"./nesting\";\n\nconst resolveExt = (mod: any, name: string) =>\n mod?.[name] || mod?.default?.[name] || mod?.default?.default || mod?.default || mod;\n\nconst BaseTable = resolveExt(_Table, \"Table\");\nconst BaseTableRow = resolveExt(_TableRow, \"TableRow\");\nconst BaseTableHeader = resolveExt(_TableHeader, \"TableHeader\");\nconst BaseTableCell = resolveExt(_TableCell, \"TableCell\");\n\n/** Horizontal alignment of a table cell (`align` attribute). */\nexport type TableCellAlignment = \"left\" | \"center\" | \"right\" | \"justify\";\n\nconst ALIGNMENTS = new Set<TableCellAlignment>([\"left\", \"center\", \"right\", \"justify\"]);\n\n/** Returns a valid cell alignment, or `null`. */\nexport function normalizeTableCellAlignment(value: unknown): TableCellAlignment | null {\n const v = typeof value === \"string\" ? value.trim().toLowerCase() : \"\";\n return ALIGNMENTS.has(v as TableCellAlignment) ? (v as TableCellAlignment) : null;\n}\n\n/**\n * Color values allowed as a cell background: hex, `rgb()/rgba()/hsl()/hsla()`\n * with numeric arguments, or a CSS color keyword. Anything else (e.g.\n * `url(...)`, `expression(...)`, `;`) is rejected because the value ends up in\n * a `style` attribute.\n */\nconst COLOR_REGEX =\n /^(?:#[0-9a-f]{3,8}|(?:rgb|rgba|hsl|hsla)\\(\\s*[-0-9.%\\s,/deg]+\\)|[a-z]{3,20})$/i;\n\n/** Returns a safe CSS color for a cell background, or `null`. */\nexport function normalizeTableCellColor(value: unknown): string | null {\n const v = typeof value === \"string\" ? value.trim() : \"\";\n if (!v || v.length > 64 || !COLOR_REGEX.test(v)) return null;\n if (/^(?:inherit|initial|unset|revert|transparent)$/i.test(v)) return null;\n return v;\n}\n\n/** Cell attributes added on top of TipTap's `colspan` / `rowspan` / `colwidth`. */\nconst cellAttributes = () => ({\n align: {\n default: null,\n parseHTML: (element: HTMLElement) =>\n normalizeTableCellAlignment(element.style?.textAlign || element.getAttribute(\"align\")),\n renderHTML: (attrs: Record<string, any>) => {\n const align = normalizeTableCellAlignment(attrs.align);\n return align ? { style: `text-align: ${align}` } : {};\n },\n },\n backgroundColor: {\n default: null,\n parseHTML: (element: HTMLElement) =>\n normalizeTableCellColor(\n element.getAttribute(\"data-background-color\") ||\n element.style?.backgroundColor ||\n element.getAttribute(\"bgcolor\"),\n ),\n renderHTML: (attrs: Record<string, any>) => {\n const color = normalizeTableCellColor(attrs.backgroundColor);\n return color ? { style: `background-color: ${color}`, \"data-background-color\": color } : {};\n },\n },\n});\n\n// ---------------------------------------------------------------------------\n// Markdown (tiptap-markdown)\n// ---------------------------------------------------------------------------\n\nconst children = (node: ProsemirrorNode) => {\n const list: ProsemirrorNode[] = [];\n node.forEach((child) => list.push(child));\n return list;\n};\n\nconst hasSpan = (cell: ProsemirrorNode) => (cell.attrs.colspan ?? 1) > 1 || (cell.attrs.rowspan ?? 1) > 1;\n\n/**\n * Whether a table can be written as a GFM pipe table: the first row is all\n * header cells, the other rows have none, no cell spans rows or columns, and\n * every cell holds a single paragraph.\n */\nexport function isGfmTable(node: ProsemirrorNode): boolean {\n const rows = children(node);\n if (rows.length === 0) return false;\n const width = rows[0].childCount;\n return rows.every(\n (row, r) =>\n row.childCount === width &&\n children(row).every(\n (cell) =>\n (r === 0 ? cell.type.name === \"tableHeader\" : cell.type.name === \"tableCell\") &&\n !hasSpan(cell) &&\n cell.childCount === 1 &&\n cell.firstChild!.type.name === \"paragraph\",\n ),\n );\n}\n\nconst DELIMITERS: Record<string, string> = { left: \":---\", center: \":---:\", right: \"---:\" };\n\n/**\n * tiptap-markdown serializer for tables: GFM pipe tables (with column\n * alignment from the header cells and `|` escaped) when `isGfmTable`, HTML\n * otherwise (spans, header columns, multi-paragraph cells).\n */\nfunction serializeTableMarkdown(state: any, node: ProsemirrorNode) {\n if (!isGfmTable(node)) {\n const html = getHTMLFromFragment(Fragment.from(node), node.type.schema);\n // An HTML block must start on its own line and contain no blank lines.\n state.write(html.replace(/\\n{2,}/g, \"\\n\"));\n state.closeBlock(node);\n return;\n }\n const previous = state.inTable;\n state.inTable = true;\n children(node).forEach((row, r) => {\n if (r) state.ensureNewLine();\n state.write(\"| \");\n children(row).forEach((cell, c) => {\n if (c) state.write(\" | \");\n const paragraph = cell.firstChild!;\n // Render in place (mid-line, so no block prefix is added), then escape\n // the cell's pipes and line breaks, which would end the cell or row.\n const start = state.out.length;\n if (paragraph.childCount) state.renderInline(paragraph);\n const text: string = state.out.slice(start);\n state.out = state.out.slice(0, start) + text.replace(/\\n/g, \" \").replace(/\\|/g, \"\\\\|\");\n });\n state.write(\" |\");\n if (r === 0) {\n const delimiter = children(row).map((cell) => DELIMITERS[cell.attrs.align] ?? \"---\");\n state.ensureNewLine();\n state.write(`| ${delimiter.join(\" | \")} |`);\n }\n });\n state.inTable = previous;\n state.closeBlock(node);\n}\n\n// ---------------------------------------------------------------------------\n// Extensions\n// ---------------------------------------------------------------------------\n\nexport interface RichTextTableOptions {\n /** Drag column borders to resize columns (stored as `colwidth`). Default `true`. */\n resizable: boolean;\n /** Width of the resize handle in pixels. Default 5. */\n handleWidth: number;\n /** Minimum cell width in pixels. Default 25. */\n cellMinWidth: number;\n /** Whether the last column can be resized. Default `true`. */\n lastColumnResizable: boolean;\n /** Allow selecting the whole table as a node (click its border, Esc from a cell selection). Default `true`. */\n allowTableNodeSelection: boolean;\n /** HTML attributes of the `<table>` element. */\n HTMLAttributes: Record<string, any>;\n /** HTML attributes of `<td>` elements. */\n cellHTMLAttributes: Record<string, any>;\n /** HTML attributes of `<th>` elements. */\n headerHTMLAttributes: Record<string, any>;\n /** HTML attributes of `<tr>` elements. */\n rowHTMLAttributes: Record<string, any>;\n}\n\nexport const DEFAULT_TABLE_OPTIONS: RichTextTableOptions = {\n resizable: true,\n handleWidth: 5,\n cellMinWidth: 25,\n lastColumnResizable: true,\n allowTableNodeSelection: true,\n HTMLAttributes: { class: \"pixerate-table\" },\n cellHTMLAttributes: {},\n headerHTMLAttributes: {},\n rowHTMLAttributes: {},\n};\n\n/**\n * TipTap's `Table` with the GFM-aware markdown serializer and the nesting\n * guard (`createTableNestingPlugin`): tables are never pasted, imported or\n * inserted into table cells.\n */\nexport const Table = BaseTable.extend({\n addStorage() {\n return { markdown: { serialize: serializeTableMarkdown } };\n },\n addProseMirrorPlugins() {\n return [...(this.parent?.() ?? []), createTableNestingPlugin()];\n },\n});\n\nexport const TableRow = BaseTableRow;\n\n/** TipTap's `TableCell` plus `align` and `backgroundColor` attributes. */\nexport const TableCell = BaseTableCell.extend({\n addAttributes() {\n return { ...this.parent?.(), ...cellAttributes() };\n },\n});\n\n/** TipTap's `TableHeader` plus `align` and `backgroundColor` attributes. */\nexport const TableHeader = BaseTableHeader.extend({\n addAttributes() {\n return { ...this.parent?.(), ...cellAttributes() };\n },\n});\n\n/**\n * The table extensions (`table`, `tableRow`, `tableHeader`, `tableCell`)\n * configured for rich documents: resizable columns, header rows, columns and\n * cells, `colspan` / `rowspan` from imported HTML, cell alignment and\n * background, table node selection and GFM markdown. `createRichTextPreset`\n * adds them unless `table: false`.\n */\nexport function createTableExtensions(options: Partial<RichTextTableOptions> = {}): Extensions {\n const o = { ...DEFAULT_TABLE_OPTIONS, ...options };\n return [\n Table.configure({\n resizable: o.resizable,\n handleWidth: o.handleWidth,\n cellMinWidth: o.cellMinWidth,\n lastColumnResizable: o.lastColumnResizable,\n allowTableNodeSelection: o.allowTableNodeSelection,\n HTMLAttributes: o.HTMLAttributes,\n }),\n TableRow.configure({ HTMLAttributes: o.rowHTMLAttributes }),\n TableHeader.configure({ HTMLAttributes: o.headerHTMLAttributes }),\n TableCell.configure({ HTMLAttributes: o.cellHTMLAttributes }),\n ];\n}\n","import type { Editor } from \"@tiptap/core\";\nimport type { EditorState } from \"@tiptap/pm/state\";\nimport { CellSelection, isInTable, selectedRect } from \"@tiptap/pm/tables\";\nimport { isTrackChangesEnabled } from \"../track-changes\";\nimport {\n normalizeTableCellAlignment,\n normalizeTableCellColor,\n type TableCellAlignment,\n} from \"./extension\";\n\n/** Table operations shared by the command registry, the toolbars and agent tools. */\nexport type TableAction =\n | \"insertTable\"\n | \"addRowBefore\"\n | \"addRowAfter\"\n | \"deleteRow\"\n | \"addColumnBefore\"\n | \"addColumnAfter\"\n | \"deleteColumn\"\n | \"deleteTable\"\n | \"mergeCells\"\n | \"splitCell\"\n | \"toggleHeaderRow\"\n | \"toggleHeaderColumn\"\n | \"toggleHeaderCell\"\n | \"goToNextCell\"\n | \"goToPreviousCell\"\n | \"setCellAlignment\"\n | \"setCellBackground\";\n\n/** Parameters of `insertTable`, `setCellAlignment` and `setCellBackground`. */\nexport interface TableActionParams {\n /** `insertTable`: number of rows (1–100, default 3). */\n rows?: number;\n /** `insertTable`: number of columns (1–50, default 3). */\n cols?: number;\n /** `insertTable`: make the first row a header row (default `true`). */\n withHeaderRow?: boolean;\n /** `setCellAlignment`: `left`, `center`, `right`, `justify`, or `null` to clear. */\n align?: TableCellAlignment | null;\n /** `setCellBackground`: a CSS color, or `null` to clear. */\n color?: string | null;\n}\n\nexport interface TableActionInfo {\n action: TableAction;\n label: string;\n description: string;\n keywords: readonly string[];\n}\n\n/** Every table action with its label, in toolbar order. */\nexport const TABLE_ACTIONS: readonly TableActionInfo[] = [\n { action: \"insertTable\", label: \"Insert Table\", description: \"Inserts a table (rows, cols, withHeaderRow) at the cursor.\", keywords: [\"table\", \"grid\", \"new\"] },\n { action: \"addRowBefore\", label: \"Insert Row Above\", description: \"Adds a row above the current row.\", keywords: [\"row\", \"add\", \"above\"] },\n { action: \"addRowAfter\", label: \"Insert Row Below\", description: \"Adds a row below the current row.\", keywords: [\"row\", \"add\", \"below\"] },\n { action: \"deleteRow\", label: \"Delete Row\", description: \"Deletes the selected rows.\", keywords: [\"row\", \"remove\"] },\n { action: \"addColumnBefore\", label: \"Insert Column Left\", description: \"Adds a column left of the current column.\", keywords: [\"column\", \"add\", \"left\", \"before\"] },\n { action: \"addColumnAfter\", label: \"Insert Column Right\", description: \"Adds a column right of the current column.\", keywords: [\"column\", \"add\", \"right\", \"after\"] },\n { action: \"deleteColumn\", label: \"Delete Column\", description: \"Deletes the selected columns.\", keywords: [\"column\", \"remove\"] },\n { action: \"deleteTable\", label: \"Delete Table\", description: \"Deletes the table around the cursor.\", keywords: [\"table\", \"remove\"] },\n { action: \"mergeCells\", label: \"Merge Cells\", description: \"Merges the selected cells into one.\", keywords: [\"combine\", \"join\", \"span\"] },\n { action: \"splitCell\", label: \"Split Cell\", description: \"Splits a merged cell back into single cells.\", keywords: [\"unmerge\", \"span\"] },\n { action: \"toggleHeaderRow\", label: \"Toggle Header Row\", description: \"Turns the first row into header cells, or back.\", keywords: [\"header\", \"row\", \"th\"] },\n { action: \"toggleHeaderColumn\", label: \"Toggle Header Column\", description: \"Turns the first column into header cells, or back.\", keywords: [\"header\", \"column\", \"th\"] },\n { action: \"toggleHeaderCell\", label: \"Toggle Header Cell\", description: \"Turns the selected cells into header cells, or back.\", keywords: [\"header\", \"cell\", \"th\"] },\n { action: \"goToNextCell\", label: \"Next Cell\", description: \"Moves the cursor to the next cell.\", keywords: [\"tab\", \"navigate\"] },\n { action: \"goToPreviousCell\", label: \"Previous Cell\", description: \"Moves the cursor to the previous cell.\", keywords: [\"shift tab\", \"navigate\"] },\n { action: \"setCellAlignment\", label: \"Align Cells…\", description: \"Sets the horizontal alignment (align: left | center | right | justify | null) of the selected cells.\", keywords: [\"align\", \"left\", \"center\", \"right\"] },\n { action: \"setCellBackground\", label: \"Cell Background…\", description: \"Sets the background color (color: CSS color or null) of the selected cells.\", keywords: [\"shading\", \"fill\", \"color\"] },\n];\n\n/** A button of the table toolbars (`TableToolbar` in React and Svelte). */\nexport interface TableToolbarItem {\n /** Unique id (also the button's `data-action`). */\n id: string;\n action: TableAction;\n params?: TableActionParams;\n /** Short button text. */\n label: string;\n /** Accessible name and tooltip. */\n title: string;\n /** Items of one group are separated from the next group. */\n group: string;\n /** Toggle buttons: whether the button shows as pressed. */\n active?: (state: TableState) => boolean;\n}\n\n/** The default table toolbar: rows, columns, cells, headers, alignment, delete. */\nexport const DEFAULT_TABLE_TOOLBAR: readonly TableToolbarItem[] = [\n { id: \"addRowBefore\", action: \"addRowBefore\", label: \"+ Row ↑\", title: \"Insert row above\", group: \"rows\" },\n { id: \"addRowAfter\", action: \"addRowAfter\", label: \"+ Row ↓\", title: \"Insert row below\", group: \"rows\" },\n { id: \"deleteRow\", action: \"deleteRow\", label: \"− Row\", title: \"Delete row\", group: \"rows\" },\n { id: \"addColumnBefore\", action: \"addColumnBefore\", label: \"+ Col ←\", title: \"Insert column left\", group: \"columns\" },\n { id: \"addColumnAfter\", action: \"addColumnAfter\", label: \"+ Col →\", title: \"Insert column right\", group: \"columns\" },\n { id: \"deleteColumn\", action: \"deleteColumn\", label: \"− Col\", title: \"Delete column\", group: \"columns\" },\n { id: \"mergeCells\", action: \"mergeCells\", label: \"Merge\", title: \"Merge cells\", group: \"cells\" },\n { id: \"splitCell\", action: \"splitCell\", label: \"Split\", title: \"Split cell\", group: \"cells\" },\n { id: \"toggleHeaderRow\", action: \"toggleHeaderRow\", label: \"Header row\", title: \"Toggle header row\", group: \"headers\", active: (s) => s.headerRow },\n { id: \"toggleHeaderColumn\", action: \"toggleHeaderColumn\", label: \"Header col\", title: \"Toggle header column\", group: \"headers\", active: (s) => s.headerColumn },\n { id: \"toggleHeaderCell\", action: \"toggleHeaderCell\", label: \"Header cell\", title: \"Toggle header cell\", group: \"headers\", active: (s) => s.headerCell },\n { id: \"alignLeft\", action: \"setCellAlignment\", params: { align: \"left\" }, label: \"Left\", title: \"Align cell left\", group: \"align\", active: (s) => s.cellAlignment === \"left\" },\n { id: \"alignCenter\", action: \"setCellAlignment\", params: { align: \"center\" }, label: \"Center\", title: \"Align cell center\", group: \"align\", active: (s) => s.cellAlignment === \"center\" },\n { id: \"alignRight\", action: \"setCellAlignment\", params: { align: \"right\" }, label: \"Right\", title: \"Align cell right\", group: \"align\", active: (s) => s.cellAlignment === \"right\" },\n { id: \"deleteTable\", action: \"deleteTable\", label: \"Delete table\", title: \"Delete table\", group: \"table\" },\n];\n\nexport const MAX_TABLE_ROWS = 100;\nexport const MAX_TABLE_COLUMNS = 50;\n\nconst live = (editor: Editor | null | undefined): editor is Editor => !!editor && !editor.isDestroyed;\n\n/** Whether the editor's schema has the table nodes (`createRichTextPreset({ table })`). */\nexport function isTableEnabled(editor: Editor | null | undefined): boolean {\n return live(editor) && !!editor.schema.nodes.table && !!editor.schema.nodes.tableRow;\n}\n\n/** Whether the selection (cursor, text or cell selection) is inside a table. */\nexport function isSelectionInTable(state: EditorState): boolean {\n if (!state.schema.nodes.table) return false;\n return state.selection instanceof CellSelection || isInTable(state);\n}\n\n/**\n * `shouldShow` for a table bubble menu (TipTap's `BubbleMenuPlugin`): shown\n * while an editable editor's selection is in a table, even a collapsed cursor.\n */\nexport function shouldShowTableBubbleMenu({ editor, state }: { editor: Editor; state: EditorState }): boolean {\n return editor.isEditable && isSelectionInTable(state);\n}\n\nconst isInt = (value: unknown, min: number, max: number) =>\n typeof value === \"number\" && Number.isInteger(value) && value >= min && value <= max;\n\nconst can = (editor: Editor, run: (chain: any) => any): boolean => {\n try {\n return !!run(editor.can().chain()).run();\n } catch {\n return false;\n }\n};\n\n/**\n * Whether a table action can run: `true`, or the reason it cannot (e.g.\n * \"Place the cursor in a table first.\").\n */\nexport function getTableActionState(\n editor: Editor | null | undefined,\n action: TableAction,\n params: TableActionParams = {},\n): true | string {\n if (!live(editor)) return \"The editor is not available.\";\n if (!isTableEnabled(editor)) return \"Tables are not enabled in this editor.\";\n if (!editor.isEditable) return \"The editor is read-only.\";\n const state = editor.state;\n const inTable = isSelectionInTable(state);\n\n if (action === \"insertTable\") {\n if (inTable) return \"Tables cannot be nested: move the cursor out of the table first.\";\n if (params.rows !== undefined && !isInt(params.rows, 1, MAX_TABLE_ROWS)) {\n return `Rows must be a whole number from 1 to ${MAX_TABLE_ROWS}.`;\n }\n if (params.cols !== undefined && !isInt(params.cols, 1, MAX_TABLE_COLUMNS)) {\n return `Columns must be a whole number from 1 to ${MAX_TABLE_COLUMNS}.`;\n }\n return can(editor, (c) => c.insertTable({ rows: 1, cols: 1 })) ? true : \"A table cannot be inserted here.\";\n }\n\n if (!inTable) return \"Place the cursor in a table first.\";\n\n switch (action) {\n case \"mergeCells\":\n case \"splitCell\": {\n if (isTrackChangesEnabled(editor)) {\n return `${action === \"mergeCells\" ? \"Merging\" : \"Splitting\"} cells cannot be tracked: turn off Track Changes first.`;\n }\n if (action === \"mergeCells\") {\n return can(editor, (c) => c.mergeCells()) ? true : \"Select two or more adjacent cells to merge.\";\n }\n return can(editor, (c) => c.splitCell()) ? true : \"Only a merged cell can be split.\";\n }\n case \"deleteRow\":\n case \"deleteColumn\": {\n // prosemirror-tables only refuses to delete every row/column when dispatching.\n const rect = selectedRect(state);\n const all = action === \"deleteRow\"\n ? rect.top === 0 && rect.bottom === rect.map.height\n : rect.left === 0 && rect.right === rect.map.width;\n const what = action === \"deleteRow\" ? \"rows\" : \"columns\";\n if (all || !can(editor, (c) => c[action]())) return `These are all the ${what}: delete the table instead.`;\n return true;\n }\n case \"goToNextCell\":\n return can(editor, (c) => c.goToNextCell()) ? true : \"This is the last cell.\";\n case \"goToPreviousCell\":\n return can(editor, (c) => c.goToPreviousCell()) ? true : \"This is the first cell.\";\n case \"setCellAlignment\":\n if (params.align !== undefined && params.align !== null && !normalizeTableCellAlignment(params.align)) {\n return 'Alignment must be \"left\", \"center\", \"right\", \"justify\" or null.';\n }\n if (!editor.schema.nodes.tableCell?.spec.attrs?.align) return \"Cell alignment is not supported by this schema.\";\n return true;\n case \"setCellBackground\":\n if (params.color !== undefined && params.color !== null && !normalizeTableCellColor(params.color)) {\n return \"Color must be a CSS color such as #fde68a, rgb(…) or a color name, or null.\";\n }\n if (!editor.schema.nodes.tableCell?.spec.attrs?.backgroundColor) return \"Cell backgrounds are not supported by this schema.\";\n return true;\n default:\n return can(editor, (c) => c[action]()) ? true : \"This table action is not available here.\";\n }\n}\n\nexport interface RunTableActionOptions {\n /** Focus the editor (toolbars). Default `false`. */\n focus?: boolean;\n}\n\n/**\n * Runs a table action. Returns `false` (and changes nothing) when it cannot\n * run; check `getTableActionState` for the reason.\n */\nexport function runTableAction(\n editor: Editor | null | undefined,\n action: TableAction,\n params: TableActionParams = {},\n options: RunTableActionOptions = {},\n): boolean {\n if (getTableActionState(editor, action, params) !== true) return false;\n const ed = editor as Editor;\n let chain: any = ed.chain();\n if (options.focus) chain = chain.focus();\n switch (action) {\n case \"insertTable\":\n chain = chain.insertTable({\n rows: params.rows ?? 3,\n cols: params.cols ?? 3,\n withHeaderRow: params.withHeaderRow ?? true,\n });\n break;\n case \"setCellAlignment\":\n chain = chain.setCellAttribute(\"align\", normalizeTableCellAlignment(params.align));\n break;\n case \"setCellBackground\":\n chain = chain.setCellAttribute(\"backgroundColor\", normalizeTableCellColor(params.color));\n break;\n default:\n chain = chain[action]();\n }\n return !!chain.run();\n}\n\nexport interface TableActionStatus {\n enabled: boolean;\n /** Why the action cannot run (only when `enabled` is false). */\n reason?: string;\n}\n\n/** The table at the selection and what can be done there. */\nexport interface TableState {\n /** The schema has tables. */\n enabled: boolean;\n /** The selection is inside a table. */\n inTable: boolean;\n /** Cells are selected (a `CellSelection`). */\n cellSelection: boolean;\n /** Size of the table (0 outside a table). */\n rows: number;\n columns: number;\n /** Row and column (0-based) of the selection's first cell, or -1. */\n row: number;\n column: number;\n /** The first row / first column are all header cells. */\n headerRow: boolean;\n headerColumn: boolean;\n /** The cell at the selection is a header cell. */\n headerCell: boolean;\n /** Alignment / background of the cell at the selection. */\n cellAlignment: TableCellAlignment | null;\n cellBackground: string | null;\n /** Enablement of every action (with the default params). */\n actions: Record<TableAction, TableActionStatus>;\n}\n\nconst NO_ACTIONS = Object.fromEntries(\n TABLE_ACTIONS.map(({ action }) => [action, { enabled: false, reason: \"The editor is not available.\" }]),\n) as Record<TableAction, TableActionStatus>;\n\nexport const EMPTY_TABLE_STATE: TableState = Object.freeze({\n enabled: false,\n inTable: false,\n cellSelection: false,\n rows: 0,\n columns: 0,\n row: -1,\n column: -1,\n headerRow: false,\n headerColumn: false,\n headerCell: false,\n cellAlignment: null,\n cellBackground: null,\n actions: NO_ACTIONS,\n}) as TableState;\n\n/** Computes the table state at the editor's selection. */\nexport function getTableState(editor: Editor | null | undefined): TableState {\n if (!live(editor)) return EMPTY_TABLE_STATE;\n const actions = Object.fromEntries(\n TABLE_ACTIONS.map(({ action }) => {\n const result = getTableActionState(editor, action);\n return [action, result === true ? { enabled: true } : { enabled: false, reason: result }];\n }),\n ) as Record<TableAction, TableActionStatus>;\n const state = editor.state;\n const base: TableState = { ...EMPTY_TABLE_STATE, enabled: isTableEnabled(editor), actions };\n if (!base.enabled || !isSelectionInTable(state)) return base;\n try {\n const rect = selectedRect(state);\n const { map, table } = rect;\n const cellAt = (row: number, col: number) => table.nodeAt(map.map[row * map.width + col]);\n const allHeaders = (positions: number[]) =>\n positions.length > 0 && positions.every((pos) => table.nodeAt(pos)?.type.name === \"tableHeader\");\n const firstRow = map.cellsInRect({ left: 0, right: map.width, top: 0, bottom: 1 });\n const firstColumn = map.cellsInRect({ left: 0, right: 1, top: 0, bottom: map.height });\n const cell = cellAt(rect.top, rect.left);\n return {\n ...base,\n inTable: true,\n cellSelection: state.selection instanceof CellSelection,\n rows: map.height,\n columns: map.width,\n row: rect.top,\n column: rect.left,\n headerRow: allHeaders(firstRow),\n headerColumn: allHeaders(firstColumn),\n headerCell: cell?.type.name === \"tableHeader\",\n cellAlignment: normalizeTableCellAlignment(cell?.attrs.align),\n cellBackground: normalizeTableCellColor(cell?.attrs.backgroundColor),\n };\n } catch {\n return { ...base, inTable: true };\n }\n}\n\nconst stateKey = (state: TableState) => JSON.stringify(state);\n\nexport interface TableStore {\n getSnapshot(): TableState;\n subscribe(listener: () => void): () => void;\n destroy(): void;\n}\n\n/**\n * An external store of `getTableState(editor)` for UI bindings (React's\n * `useSyncExternalStore`, Svelte runes). The snapshot object only changes\n * when the state does, and the store listens to the editor only while it\n * has subscribers.\n */\nexport function createTableStore(editor: Editor | null | undefined): TableStore {\n const listeners = new Set<() => void>();\n let snapshot = getTableState(editor);\n let key = stateKey(snapshot);\n\n const update = () => {\n const next = getTableState(editor);\n const nextKey = stateKey(next);\n if (nextKey === key) return;\n snapshot = next;\n key = nextKey;\n listeners.forEach((listener) => listener());\n };\n const detach = () => {\n editor?.off(\"transaction\", update);\n editor?.off(\"update\", update);\n editor?.off(\"focus\", update);\n editor?.off(\"destroy\", update);\n };\n\n return {\n getSnapshot: () => {\n if (!listeners.size) {\n const next = getTableState(editor);\n const nextKey = stateKey(next);\n if (nextKey !== key) {\n snapshot = next;\n key = nextKey;\n }\n }\n return snapshot;\n },\n subscribe(listener) {\n if (!listeners.size && editor) {\n update();\n editor.on(\"transaction\", update);\n // `setEditable` emits `update` without a transaction.\n editor.on(\"update\", update);\n editor.on(\"focus\", update);\n editor.on(\"destroy\", update);\n }\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n if (!listeners.size) detach();\n };\n },\n destroy() {\n detach();\n listeners.clear();\n },\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/editor/editor/packages/core/dist/chunk-LYGEEDNR.cjs","../src/spreadsheet/formula/values.ts","../src/spreadsheet/formula/parse-input.ts","../src/spreadsheet/numfmt/decimal.ts","../src/spreadsheet/numfmt/builtin.ts","../src/spreadsheet/numfmt/parse.ts","../src/spreadsheet/numfmt/render.ts","../src/spreadsheet/numfmt/index.ts"],"names":["pad","y","m","out","UNIX_1899_12_30","isDigitTok","serial"],"mappings":"AAAA;ACSO,IAAM,eAAA,kBAAiB,IAAI,GAAA,CAAI;AAAA,EACpC,SAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAC,CAAA;AAcM,SAAS,cAAA,CAAe,KAAA,EAAiC;AAC9D,EAAA,OAAO,OAAO,MAAA,IAAU,SAAA,GAAY,cAAA,CAAe,GAAA,CAAI,KAAK,CAAA;AAC9D;AAEO,IAAM,QAAA,EAAU,CAAC,KAAA,EAAA,GAA4B,MAAA,IAAU,GAAA,GAAM,MAAA,IAAU,KAAA,GAAQ,MAAA,IAAU,KAAA,CAAA;AAOzF,SAAS,QAAA,CAAS,KAAA,EAAiC;AACxD,EAAA,GAAA,CAAI,cAAA,CAAe,KAAK,CAAA,EAAG,OAAO,KAAA;AAClC,EAAA,GAAA,CAAI,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,CAAA;AAC3B,EAAA,GAAA,CAAI,OAAO,MAAA,IAAU,SAAA,EAAW,OAAO,MAAA,EAAQ,EAAA,EAAI,CAAA;AACnD,EAAA,GAAA,CAAI,OAAO,MAAA,IAAU,QAAA,EAAU,OAAO,KAAA;AACtC,EAAA,wBAAO,eAAA,CAAgB,MAAA,CAAO,KAAK,CAAC,CAAA,UAAK,WAAA;AAC3C;AAGO,SAAS,SAAA,CAAU,KAAA,EAAkC;AAC1D,EAAA,GAAA,CAAI,cAAA,CAAe,KAAK,CAAA,EAAG,OAAO,KAAA;AAClC,EAAA,GAAA,CAAI,OAAA,CAAQ,KAAK,CAAA,EAAG,OAAO,KAAA;AAC3B,EAAA,GAAA,CAAI,OAAO,MAAA,IAAU,SAAA,EAAW,OAAO,KAAA;AACvC,EAAA,GAAA,CAAI,OAAO,MAAA,IAAU,QAAA,EAAU,OAAO,MAAA,IAAU,CAAA;AAChD,EAAA,MAAM,MAAA,EAAQ,MAAA,CAAO,KAAK,CAAA,CAAE,IAAA,CAAK,CAAA,CAAE,WAAA,CAAY,CAAA;AAC/C,EAAA,GAAA,CAAI,MAAA,IAAU,MAAA,EAAQ,OAAO,IAAA;AAC7B,EAAA,GAAA,CAAI,MAAA,IAAU,OAAA,EAAS,OAAO,KAAA;AAC9B,EAAA,OAAO,SAAA;AACT;AAGO,IAAM,OAAA,EAAS,CAAC,KAAA,EAAA,GACrB,OAAA,CAAQ,KAAK,EAAA,EACT,GAAA,EACA,OAAO,MAAA,IAAU,UAAA,EACd,MAAA,EAAQ,OAAA,EAAS,QAAA,EAClB,OAAO,MAAA,IAAU,SAAA,EACf,YAAA,CAAa,KAAK,EAAA,EAClB,MAAA,CAAO,KAAK,CAAA;AAMf,SAAS,YAAA,CAAa,CAAA,EAAmB;AAC9C,EAAA,GAAA,CAAI,EAAA,IAAM,EAAA,GAAK,CAAC,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,EAAG,OAAO,GAAA;AAC3C,EAAA,MAAM,EAAA,EAAI,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA;AACpB,EAAA,GAAA,CAAA,CAAM,EAAA,EAAI,KAAA,GAAQ,EAAA,GAAK,IAAA,EAAA,GAAS,IAAA,CAAK,KAAA,CAAM,CAAC,EAAA,IAAM,EAAA,GAAM,EAAA,GAAK,IAAA,EAAM,OAAO,OAAA,CAAQ,CAAA,EAAG,EAAE,CAAA;AACvF,EAAA,IAAI,IAAA,EAAM,IAAA,CAAK,KAAA,CAAM,IAAA,CAAK,KAAA,CAAM,CAAC,CAAC,CAAA;AAClC,EAAA,GAAA,CAAI,EAAA,GAAK,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,IAAA,EAAM,CAAC,CAAA,EAAG,GAAA,EAAA;AAChC,EAAA,MAAM,SAAA,EAAW,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,GAAA,EAAK,GAAA,EAAK,CAAC,CAAA,EAAG,EAAE,CAAA;AACnD,EAAA,OAAO,WAAA,CAAY,CAAA,CAAE,OAAA,CAAQ,QAAQ,CAAC,CAAA;AACxC;AAGA,SAAS,WAAA,CAAY,CAAA,EAAmB;AACtC,EAAA,GAAA,CAAI,CAAC,CAAA,CAAE,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,CAAA;AAC7B,EAAA,MAAM,EAAA,EAAI,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,CAAE,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA;AAChD,EAAA,OAAO,EAAA,IAAM,KAAA,EAAO,IAAA,EAAM,CAAA;AAC5B;AAGA,SAAS,OAAA,CAAQ,CAAA,EAAW,GAAA,EAAqB;AAC/C,EAAA,MAAM,CAAC,QAAA,EAAU,GAAG,EAAA,EAAI,CAAA,CAAE,aAAA,CAAc,IAAA,CAAK,GAAA,CAAI,IAAA,EAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAE,KAAA,CAAM,GAAG,CAAA;AACvE,EAAA,MAAM,EAAA,EAAI,MAAA,CAAO,GAAG,CAAA;AACpB,EAAA,OAAO,CAAA,EAAA;AACT;AAMgB;AACV,EAAA;AACA,EAAA;AACE,EAAA;AACF,EAAA;AACE,EAAA;AACC,EAAA;AACT;AAGgB;AACR,EAAA;AACC,EAAA;AACT;AAGM;AAGG;AACH,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACG,EAAA;AACT;AAMgB;AACV,EAAA;AAGA,EAAA;AACE,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAGgB;AACR,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAUgB;AACR,EAAA;AACA,EAAA;AACF,EAAA;AACE,IAAA;AAEE,IAAA;AACA,IAAA;AACA,IAAA;AACF,IAAA;AACA,IAAA;AAAwD,IAAA;AACD,IAAA;AAEtD,IAAA;AACL,IAAA;AACF,EAAA;AACI,EAAA;AACI,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,IAAA;AACJ,IAAA;AACF,EAAA;AACK,EAAA;AACG,IAAA;AACF,IAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACA,EAAA;AACA,EAAA;AACG,EAAA;AACT;AAMgB;AACR,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AACG,EAAA;AACT;AAGgB;AACR,EAAA;AACA,EAAA;AACE,EAAA;AACV;AAGa;AAEG;AACV,EAAA;AACE,EAAA;AACN,EAAA;AACE,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAEgB;AACV,EAAA;AACA,EAAA;AACG,EAAA;AACC,IAAA;AACN,IAAA;AACM,IAAA;AACR,EAAA;AACO,EAAA;AACT;AAeM;AAGU;AACd,EAAA;AACO,EAAA;AACT;AAGgB;AACR,EAAA;AACF,EAAA;AACG,EAAA;AACT;AAEa;AAIG;AAOR,EAAA;AACN,EAAA;AACE,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAQM;AACM;AAGI;AACV,EAAA;AACA,EAAA;AACA,EAAA;AACE,EAAA;AACF,EAAA;AACE,EAAA;AACF,EAAA;AACI,IAAA;AACA,IAAA;AAEN,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AAEA,EAAA;AACI,IAAA;AACN,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAEM;AAOU;AACV,EAAA;AACE,EAAA;AACD,EAAA;AACE,EAAA;AACD,EAAA;AACC,EAAA;AACT;AAGa;AAEJ;AAEH,EAAA;AACG,EAAA;AACT;AAGgB;AACR,EAAA;AACC,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACP,EAAA;AACO,EAAA;AACT;AAEgB;AACT,EAAA;AACC,EAAA;AACC,EAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACN,EAAA;AACF;AAGa;AAIA;AAIA;AAEH,EAAA;AAGR;ADxJQ;AACA;AEtMG;AACX,EAAA;AAAW,EAAA;AAAY,EAAA;AAAS,EAAA;AAAS,EAAA;AAAO,EAAA;AAChD,EAAA;AAAQ,EAAA;AAAU,EAAA;AAAa,EAAA;AAAW,EAAA;AAAY,EAAA;AACxD;AAGgB;AACR,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAEa;AAEG;AACV,EAAA;AACI,EAAA;AACD,EAAA;AACT;AAMa;AAGP;AACA;AAOU;AACR,EAAA;AACE,EAAA;AACF,EAAA;AACD,EAAA;AACC,EAAA;AACE,EAAA;AAEF,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAGa;AAGP;AAqBU;AACV,EAAA;AACA,EAAA;AACE,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACE,EAAA;AACF,EAAA;AACE,IAAA;AACF,MAAA;AAEI,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAMgB;AACR,EAAA;AACD,EAAA;AACD,EAAA;AACI,EAAA;AACF,EAAA;AACC,EAAA;AACT;AAEM;AAEG;AACF,EAAA;AACD,EAAA;AACA,EAAA;AACG,EAAA;AACT;AAEMA;AAGG;AACH,EAAA;AACE,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACG,EAAA;AACT;AAES;AACH,EAAA;AAEE,EAAA;AACE,IAAA;AACD,IAAA;AACL,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AACE,EAAA;AACF,EAAA;AACG,EAAA;AACT;AAEM;AAGG;AACC,EAAA;AACJ,EAAA;AACA,EAAA;AACA,EAAA;AACE,EAAA;AACE,IAAA;AACA,IAAA;AACR,EAAA;AACQ,IAAA;AACR,EAAA;AACM,EAAA;AACJ,IAAA;AACM,IAAA;AACD,IAAA;AACH,MAAA;AACI,MAAA;AACN,IAAA;AACF,EAAA;AACM,EAAA;AACJ,IAAA;AACM,IAAA;AACR,EAAA;AACI,EAAA;AACE,EAAA;AACF,EAAA;AACA,EAAA;AAEI,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,IAAA;AACJ,IAAA;AACK,EAAA;AACL,IAAA;AACF,EAAA;AACK,EAAA;AAGA,EAAA;AACG,EAAA;AACH,EAAA;AACC,EAAA;AACF,EAAA;AACK,EAAA;AACH,EAAA;AACA,EAAA;AASC,EAAA;AACT;AAEM;AAIG;AACD,EAAA;AACF,EAAA;AACE,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACE,EAAA;AACA,EAAA;AACF,EAAA;AACE,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAGS;AACD,EAAA;AACF,EAAA;AACA,EAAA;AACJ,EAAA;AACM,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AACA,EAAA;AACE,EAAA;AACE,EAAA;AACF,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACE,IAAA;AACA,IAAA;AACA,IAAA;AACN,EAAA;AACE,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAEM;AAEG;AAEH,EAAA;AACA,EAAA;AACG,EAAA;AACT;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACI,IAAA;AAEA,IAAA;AACJ,MAAA;AACIC,MAAAA;AACN,IAAA;AACA,IAAA;AACE,MAAA;AACIC,MAAAA;AACN,IAAA;AAEM,IAAA;AACF,IAAA;AACN,EAAA;AACI,EAAA;AACI,IAAA;AACA,IAAA;AACF,IAAA;AAEE,MAAA;AAEA,MAAA;AACN,IAAA;AACM,IAAA;AACA,IAAA;AACF,IAAA;AACE,IAAA;AACA,IAAA;AACF,IAAA;AACN,EAAA;AAEM,EAAA;AACF,EAAA;AACI,IAAA;AACD,IAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACN,EAAA;AACO,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACI,IAAA;AAMN,IAAA;AACF,EAAA;AACM,EAAA;AACF,EAAA;AAEJ,EAAA;AACQ,IAAA;AACA,IAAA;AAEF,IAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;AFqHU;AACA;AGpfG;AAMJ;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACN,EAAA;AACQ,IAAA;AACF,IAAA;AACN,EAAA;AACO,EAAA;AACT;AAGS;AACD,EAAA;AACD,EAAA;AACC,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACF,IAAA;AACK,EAAA;AACL,IAAA;AACA,IAAA;AACF,EAAA;AACM,EAAA;AACC,EAAA;AACT;AAGS;AACD,EAAA;AAEC,EAAA;AACT;AAUgB;AACT,EAAA;AACG,EAAA;AACF,EAAA;AACC,EAAA;AACD,EAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AAEE,IAAA;AACN,IAAA;AACA,IAAA;AACF,EAAA;AACQ,EAAA;AACJ,EAAA;AACJ,EAAA;AACM,IAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACI,EAAA;AACF,IAAA;AACA,IAAA;AACK,IAAA;AACP,EAAA;AACO,EAAA;AACT;AAOgB;AACR,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AACE,IAAA;AACN,EAAA;AACM,IAAA;AACJ,IAAA;AACK,EAAA;AACD,IAAA;AACC,IAAA;AACC,MAAA;AACJ,MAAA;AACE,QAAA;AACE,UAAA;AACF,QAAA;AACE,UAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACF,MAAA;AACI,MAAA;AACN,IAAA;AACF,EAAA;AACI,EAAA;AACG,EAAA;AACD,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AACE,EAAA;AACE,IAAA;AACN,IAAA;AACK,EAAA;AACC,IAAA;AACA,IAAA;AACN,IAAA;AACF,EAAA;AACI,EAAA;AACI,EAAA;AACV;AAGgB;AACT,EAAA;AACG,EAAA;AACD,EAAA;AACT;AAEM;AAOU;AACT,EAAA;AACD,EAAA;AACE,EAAA;AACE,EAAA;AACF,EAAA;AAEF,EAAA;AACI,IAAA;AACF,IAAA;AACJ,IAAA;AACE,MAAA;AACA,MAAA;AACI,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACJ,MAAA;AACF,IAAA;AACF,EAAA;AACA,EAAA;AACM,IAAA;AACC,IAAA;AACD,IAAA;AACF,MAAA;AACC,MAAA;AACH,IAAA;AACK,IAAA;AACD,IAAA;AACA,IAAA;AACC,IAAA;AACD,IAAA;AACN,EAAA;AACO,EAAA;AACT;AHkdU;AACA;AIhpBJ;AACA,EAAA;AACG,EAAA;AACH,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACI,EAAA;AACH,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACG,EAAA;AACV;AAMgB;AACP,EAAA;AACT;AAGgB;AACR,EAAA;AACF,EAAA;AACE,EAAA;AACA,EAAA;AACC,EAAA;AACT;AJ2oBU;AACA;AKrrBJ;AAQO;AAsBG;AACN,EAAA;AACD,IAAA;AACA,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACF,IAAA;AACE,MAAA;AACJ,EAAA;AACF;AASgB;AACN,EAAA;AACD,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACG,IAAA;AACH,MAAA;AACJ,EAAA;AACF;AAqBM;AAQU;AACR,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AACI,EAAA;AACD,EAAA;AACC,IAAA;AACD,IAAA;AACC,IAAA;AACF,IAAA;AACE,MAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACI,MAAA;AACN,IAAA;AACE,MAAA;AACI,MAAA;AACN,IAAA;AAEM,MAAA;AACJ,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACN,IAAA;AACM,MAAA;AACA,MAAA;AACN,IAAA;AACM,MAAA;AACN,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAEM;AAEG;AACP,EAAA;AACQ,IAAA;AACF,IAAA;AACN,EAAA;AACO,EAAA;AACT;AAES;AACH,EAAA;AACE,EAAA;AACF,EAAA;AACC,EAAA;AACP;AAEM;AACA;AAEA;AAEG;AACH,EAAA;AACI,EAAA;AACF,EAAA;AACD,EAAA;AACC,EAAA;AACC,EAAA;AACT;AAES;AACD,EAAA;AACF,EAAA;AACA,EAAA;AACE,EAAA;AAAgD,EAAA;AACK,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AAEpD,EAAA;AACC,EAAA;AACD,EAAA;AACC,EAAA;AACC,EAAA;AACT;AAUS;AACD,EAAA;AACA,EAAA;AACF,EAAA;AACE,IAAA;AACJ,IAAA;AACF,EAAA;AACO,EAAA;AACC,IAAA;AACC,IAAA;AACP,IAAA;AACF,EAAA;AACI,EAAA;AACI,IAAA;AACF,IAAA;AACE,MAAA;AACJ,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AACM,EAAA;AACD,EAAA;AACG,IAAA;AACF,IAAA;AACN,EAAA;AAEF;AAES;AACD,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACE,EAAA;AACD,EAAA;AACC,IAAA;AACF,IAAA;AACE,MAAA;AACJ,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACE,MAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACI,MAAA;AACJ,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AACF,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACI,IAAA;AAAgE,IAAA;AACxB,IAAA;AACA,IAAA;AACE,IAAA;AACL,IAAA;AAEvC,MAAA;AACA,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACE,UAAA;AACA,UAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACE,MAAA;AACI,MAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AAYA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACM,MAAA;AACJ,MAAA;AACF,IAAA;AACK,IAAA;AACP,EAAA;AACO,EAAA;AACT;AAEM;AAEG;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACE,IAAA;AACN,IAAA;AACM,IAAA;AACA,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACK,IAAA;AACL,IAAA;AACA,IAAA;AACM,IAAA;AACR,EAAA;AACI,EAAA;AACE,IAAA;AAGE,IAAA;AACF,IAAA;AACJ,IAAA;AACE,MAAA;AACA,MAAA;AACI,MAAA;AACN,IAAA;AACM,IAAA;AACF,IAAA;AACJ,IAAA;AACE,MAAA;AACA,MAAA;AACI,MAAA;AACN,IAAA;AACA,IAAA;AACE,MAAA;AACI,MAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACI,MAAA;AACN,IAAA;AACMC,IAAAA;AACF,IAAA;AACJ,IAAA;AACE,MAAA;AACI,MAAA;AACF,QAAA;AACA,QAAA;AACAA,QAAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACI,MAAA;AACC,MAAA;AACL,MAAA;AACF,IAAA;AACI,IAAA;AACJ,IAAA;AACF,EAAA;AACI,EAAA;AACE,IAAA;AACEA,IAAAA;AACN,IAAA;AACM,MAAA;AACC,MAAA;AACP,IAAA;AACI,IAAA;AACJ,IAAA;AACF,EAAA;AACI,EAAA;AACI,EAAA;AACF,IAAA;AACEA,IAAAA;AACN,IAAA;AACM,MAAA;AACC,MAAA;AACP,IAAA;AACI,IAAA;AACJ,IAAA;AACF,EAAA;AAEI,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAIE,EAAA;AACA,EAAA;AACF,EAAA;AACA,EAAA;AACJ,EAAA;AACE,IAAA;AACA,IAAA;AACM,IAAA;AACA,IAAA;AACA,IAAA;AACR,EAAA;AACI,EAAA;AACJ,EAAA;AACM,IAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACM,EAAA;AACA,EAAA;AACN,EAAA;AACQ,IAAA;AACF,IAAA;AACJ,IAAA;AACE,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AAAuD,QAAA;AAEvD,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AAAmE,QAAA;AAEjE,UAAA;AACA,UAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACE,UAAA;AACA,UAAA;AACF,QAAA;AAGE,UAAA;AACF,QAAA;AACE,UAAA;AACA,UAAA;AACF,QAAA;AACE,UAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AAEE,QAAA;AAEA,QAAA;AACA,QAAA;AACE,UAAA;AACA,UAAA;AACF,QAAA;AACE,UAAA;AACF,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACJ,IAAA;AACI,IAAA;AACN,EAAA;AACQ,EAAA;AAEN,IAAA;AACF,EAAA;AAEM,EAAA;AACN,EAAA;AACQ,IAAA;AACD,IAAA;AACP,EAAA;AACI,EAAA;AACG,EAAA;AACT;AAGgB;AACP,EAAA;AACT;ALykBU;AACA;AM3mCG;AAEP;AACA;AACAC;AAGG;AACD,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACC,EAAA;AACT;AAOgB;AACR,EAAA;AACF,EAAA;AACA,EAAA;AACE,EAAA;AACE,EAAA;AACD,EAAA;AACT;AAGO;AACE,iBAAA;AACP,EAAA;AAAA;AAEO,kBAAA;AACe,EAAA;AACf,IAAA;AACP,EAAA;AACQ,EAAA;AACD,IAAA;AACP,EAAA;AACQ,EAAA;AACD,IAAA;AACD,IAAA;AACN,EAAA;AACF;AAGgB;AACR,EAAA;AACN,EAAA;AACQ,IAAA;AAAyB,IAAA;AAE1B,IAAA;AACP,EAAA;AACO,EAAA;AACT;AAGgB;AACR,EAAA;AACN,EAAA;AACQ,IAAA;AAAoG,IAAA;AAErG,IAAA;AACP,EAAA;AACO,EAAA;AACT;AAEM;AAGU;AACT,EAAA;AACD,EAAA;AACJ,EAAA;AACA,EAAA;AACM,EAAA;AACF,EAAA;AACA,EAAA;AACA,EAAA;AACI,IAAA;AACN,IAAA;AACF,EAAA;AACM,EAAA;AACA,EAAA;AACF,EAAA;AACE,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACN,EAAA;AACQ,IAAA;AACA,MAAA;AACJ,MAAA;AACF,IAAA;AACM,IAAA;AACA,MAAA;AACJ,MAAA;AACF,IAAA;AACM,IAAA;AACF,IAAA;AACJ,IAAA;AACE,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACJ,IAAA;AACI,IAAA;AACN,EAAA;AACO,EAAA;AACT;AAEM;AACE,EAAA;AACC,EAAA;AACT;AAQS;AACH,EAAA;AACA,EAAA;AACE,EAAA;AACN,EAAA;AACQ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACJ,MAAA;AACF,IAAA;AACM,IAAA;AACF,IAAA;AACE,MAAA;AAAyB,MAAA;AACe,MAAA;AAE1C,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACE,MAAA;AACE,QAAA;AACA,QAAA;AACF,MAAA;AACF,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAES;AACF,EAAA;AACC,EAAA;AACE,EAAA;AACR,EAAA;AACM,IAAA;AACC,IAAA;AACP,EAAA;AACO,EAAA;AACT;AAGS;AACH,EAAA;AACJ,EAAA;AACM,IAAA;AACF,MAAA;AACA,MAAA;AACF,IAAA;AACF,EAAA;AACK,EAAA;AACG,IAAA;AACN,IAAA;AACD,EAAA;AACH;AAES;AACD,EAAA;AACN,EAAA;AACO,EAAA;AACT;AAGgB;AACN,EAAA;AAEA,EAAA;AACA,EAAA;AACH,EAAA;AACC,EAAA;AACA,EAAA;AAGA,EAAA;AACA,EAAA;AACA,EAAA;AACF,EAAA;AACI,EAAA;AACN,IAAA;AACK,EAAA;AACC,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,IAAA;AACA,IAAA;AACA,IAAA;AACF,MAAA;AACA,MAAA;AACI,MAAA;AACA,MAAA;AAEF,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACA,QAAA;AACC,QAAA;AACD,QAAA;AACE,UAAA;AACA,UAAA;AACF,QAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACI,MAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AAGI,MAAA;AACF,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACE,QAAA;AACF,MAAA;AACF,IAAA;AACG,MAAA;AACH,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACF,IAAA;AACJ,IAAA;AACI,IAAA;AACN,EAAA;AACM,EAAA;AACN,EAAA;AACM,IAAA;AACE,IAAA;AACA,IAAA;AAAoC,IAAA;AACF,IAAA;AACA,IAAA;AAEnC,IAAA;AACP,EAAA;AACI,EAAA;AACG,EAAA;AACT;AAGgB;AACV,EAAA;AACA,EAAA;AACC,EAAA;AACG,EAAA;AACR,EAAA;AACQ,IAAA;AACF,IAAA;AACE,IAAA;AACF,IAAA;AACE,IAAA;AACD,IAAA;AACC,IAAA;AAC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","file":"/home/runner/work/editor/editor/packages/core/dist/chunk-LYGEEDNR.cjs","sourcesContent":[null,"// Portions ported from gridcraft/crates/core/src/value.rs (MIT OR Apache-2.0,\n// © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n/**\n * Value helpers shared by the formula evaluator and the built-in functions:\n * error values, spreadsheet coercion rules, comparisons, ranges and dates.\n */\nimport { parseNumberText } from './parse-input';\n\n/** Spreadsheet error values. They propagate through operators and functions. */\nexport const FORMULA_ERRORS = new Set([\n '#DIV/0!',\n '#VALUE!',\n '#REF!',\n '#NAME?',\n '#N/A',\n '#NUM!',\n '#CYCLE!',\n '#ERROR!',\n '#SPILL!',\n '#CALC!'\n]);\n\nexport type FormulaErrorCode =\n | '#DIV/0!'\n | '#VALUE!'\n | '#REF!'\n | '#NAME?'\n | '#N/A'\n | '#NUM!'\n | '#CYCLE!'\n | '#ERROR!'\n | '#SPILL!'\n | '#CALC!';\n\nexport function isFormulaError(value: unknown): value is string {\n return typeof value === 'string' && FORMULA_ERRORS.has(value);\n}\n\nexport const isBlank = (value: unknown): boolean => value === '' || value === null || value === undefined;\n\n/**\n * Coerces to a number like arithmetic does: blanks are 0, booleans 1/0,\n * numeric text parses (`\"1,000\"`, `\"50%\"`, `\"$3\"`, `\"(5)\"`, dates and times\n * as serial numbers), other text is #VALUE!.\n */\nexport function toNumber(value: unknown): number | string {\n if (isFormulaError(value)) return value;\n if (isBlank(value)) return 0;\n if (typeof value === 'boolean') return value ? 1 : 0;\n if (typeof value === 'number') return value;\n return parseNumberText(String(value)) ?? '#VALUE!';\n}\n\n/** Coerces to a boolean: numbers are non-zero, \"TRUE\"/\"FALSE\" text is parsed, other text is #VALUE!. */\nexport function toBoolean(value: unknown): boolean | string {\n if (isFormulaError(value)) return value;\n if (isBlank(value)) return false;\n if (typeof value === 'boolean') return value;\n if (typeof value === 'number') return value !== 0;\n const upper = String(value).trim().toUpperCase();\n if (upper === 'TRUE') return true;\n if (upper === 'FALSE') return false;\n return '#VALUE!';\n}\n\n/** Coerces to text like `&` does: numbers use up to 15 significant digits (`0.1+0.2` is \"0.3\"). */\nexport const toText = (value: unknown): string =>\n isBlank(value)\n ? ''\n : typeof value === 'boolean'\n ? (value ? 'TRUE' : 'FALSE')\n : typeof value === 'number'\n ? numberToText(value)\n : String(value);\n\n/**\n * Number to text as Excel converts it in `&` and General display: up to 15\n * significant digits, scientific for very large or very small magnitudes.\n */\nexport function numberToText(n: number): string {\n if (n === 0 || !Number.isFinite(n)) return '0';\n const a = Math.abs(n);\n if (((a < 1e-9 || a >= 1e15) && Math.round(a) !== a) || a >= 1e15) return sciText(n, 15);\n let mag = Math.floor(Math.log10(a));\n if (a >= Math.pow(10, mag + 1)) mag++; // log10 can land just below an exact power of ten\n const decimals = Math.min(Math.max(14 - mag, 0), 30);\n return trimDecimal(n.toFixed(decimals));\n}\n\n/** Removes trailing zeros (and a trailing point) from a decimal string. */\nfunction trimDecimal(s: string): string {\n if (!s.includes('.')) return s;\n const t = s.replace(/0+$/, '').replace(/\\.$/, '');\n return t === '-0' ? '0' : t;\n}\n\n/** Scientific text like `1.23456789012346E+15` with up to `sig` significant digits. */\nfunction sciText(n: number, sig: number): string {\n const [mantissa, exp] = n.toExponential(Math.max(sig - 1, 0)).split('e');\n const e = Number(exp);\n return `${trimDecimal(mantissa)}E${e < 0 ? '-' : '+'}${String(Math.abs(e)).padStart(2, '0')}`;\n}\n\n/**\n * Number held by a value, or null. Typed cell values are stored as text, so\n * numeric text (\"120\") counts as a number here; booleans and blanks do not.\n */\nexport function numericValue(value: unknown): number | null {\n if (typeof value === 'number') return Number.isFinite(value) ? value : null;\n if (typeof value !== 'string' || FORMULA_ERRORS.has(value)) return null;\n const trimmed = value.trim();\n if (trimmed === '') return null;\n const num = Number(trimmed);\n return Number.isFinite(num) ? num : null;\n}\n\n/** Normalizes a cell value for lookups and criteria: numeric text becomes a number. */\nexport function normalizeValue(value: unknown): unknown {\n const num = typeof value === 'string' ? numericValue(value) : null;\n return num === null ? value : num;\n}\n\n/** `ERROR.TYPE`-style order of error values, used when two errors are compared. */\nconst ERROR_ORDER = ['#NULL!', '#DIV/0!', '#VALUE!', '#REF!', '#NAME?', '#NUM!', '#N/A', '#SPILL!', '#CALC!', '#CYCLE!', '#ERROR!'];\n\n/** Type rank for comparison and sorting: numbers < text < booleans < errors; blanks rank last. */\nfunction valueRank(value: unknown): number {\n if (isBlank(value)) return 4;\n if (typeof value === 'number') return 0;\n if (typeof value === 'boolean') return 2;\n if (typeof value === 'string' && FORMULA_ERRORS.has(value)) return 3;\n return 1;\n}\n\n/**\n * Compares numbers the way Excel does: both are rounded to 15 significant\n * digits first, so binary noise such as `0.1+0.2` versus `0.3` counts as equal.\n */\nexport function compareNumbers(a: number, b: number): number {\n if (a === b) return 0;\n // Numbers further apart than the 15th digit can't round together, and\n // rounding keeps their order: skip the rounding on the sort hot path.\n if (Math.abs(a - b) > Math.max(Math.abs(a), Math.abs(b)) * 1e-13) return a < b ? -1 : 1;\n const round15 = (x: number) => (!Number.isFinite(x) || x === 0 ? x : Number(x.toExponential(14)));\n const x = round15(a);\n const y = round15(b);\n return x < y ? -1 : x > y ? 1 : 0;\n}\n\n/** Case-insensitive text order. */\nexport function compareText(a: string, b: string): number {\n const x = a.toLowerCase();\n const y = b.toLowerCase();\n return x < y ? -1 : x > y ? 1 : 0;\n}\n\n/**\n * Excel's comparison for the `=`, `<>`, `<`… operators: numbers < text <\n * booleans < errors, numbers compared at 15 significant digits, text\n * case-insensitively. A blank acts as 0, \"\" or FALSE depending on the other\n * side. Dates are ISO text: two ISO dates compare chronologically\n * (\"2026-1-5\" is before \"2026-01-10\"), and an ISO date against a number\n * compares as its serial number.\n */\nexport function compareValues(left: unknown, right: unknown): number {\n const lRank = valueRank(left);\n const rRank = valueRank(right);\n if (lRank === 4 || rRank === 4) {\n if (lRank === rRank) return 0;\n // A blank against a number, text or boolean takes that type's empty value.\n const blankFirst = lRank === 4;\n const other = blankFirst ? right : left;\n const otherRank = blankFirst ? rRank : lRank;\n let cmp: number;\n if (otherRank === 0) cmp = compareNumbers(0, other as number);\n else if (otherRank === 1) cmp = String(other) === '' ? 0 : -1;\n else if (otherRank === 2) cmp = other ? -1 : 0;\n else return lRank - rRank;\n return blankFirst ? cmp : -cmp;\n }\n if (lRank === 1 && rRank === 1) {\n const a = String(left);\n const b = String(right);\n const ta = isoDateTime(a);\n const tb = ta === null ? null : isoDateTime(b);\n if (ta !== null && tb !== null) return Math.sign(ta - tb);\n return compareText(a, b);\n }\n if ((lRank === 0 && rRank === 1) || (lRank === 1 && rRank === 0)) {\n const date = parseIsoDate(lRank === 1 ? left : right);\n if (date) {\n const serial = dateToSerial(date);\n return lRank === 0 ? compareNumbers(left as number, serial) : compareNumbers(serial, right as number);\n }\n }\n if (lRank !== rRank) return lRank - rRank;\n if (lRank === 0) return compareNumbers(left as number, right as number);\n if (lRank === 2) return Number(left) - Number(right);\n return Math.sign(ERROR_ORDER.indexOf(left as string) - ERROR_ORDER.indexOf(right as string));\n}\n\n/**\n * Sort order (ascending): numbers, text, booleans, errors, then blanks last.\n * Errors are equal to each other, so sorting keeps their order.\n */\nexport function sortCompareValues(left: unknown, right: unknown): number {\n const lBlank = isBlank(left);\n const rBlank = isBlank(right);\n if (lBlank || rBlank) return lBlank === rBlank ? 0 : lBlank ? 1 : -1;\n if (isFormulaError(left) && isFormulaError(right)) return 0;\n return compareValues(left, right);\n}\n\n/** Rounds half away from zero, compensating for binary floating point (ROUND(1.005, 2) = 1.01). */\nexport function roundHalfAwayFromZero(value: number, decimals: number): number {\n const factor = Math.pow(10, decimals);\n const scaled = Number((Math.abs(value) * factor).toPrecision(15));\n return (Math.sign(value) * Math.round(scaled)) / factor;\n}\n\n/** Strips binary floating point noise (0.1 * 3 = 0.30000000000000004 -> 0.3). */\nexport const cleanFloat = (value: number): number => Number(value.toPrecision(15));\n\nexport function colNameToIndex(col: string): number {\n let index = 0;\n const upper = col.toUpperCase();\n for (let i = 0; i < upper.length; i++) {\n index = index * 26 + (upper.charCodeAt(i) - 65 + 1);\n }\n return index - 1;\n}\n\nexport function indexToColName(index: number): string {\n let num = index + 1;\n let name = '';\n while (num > 0) {\n const mod = (num - 1) % 26;\n name = String.fromCharCode(65 + mod) + name;\n num = Math.floor((num - mod) / 26);\n }\n return name;\n}\n\n// --- Ranges ---\n\n/**\n * A rectangular block of values. `values` is column-major (the order in which\n * `EvaluationContext.resolveRange` returns cells): `values[col * rows + row]`.\n */\nexport interface FormulaRange {\n rows: number;\n cols: number;\n values: any[];\n}\n\n/** Shapes of the arrays produced by range references and range-returning functions. */\nconst rangeShapes = new WeakMap<any[], { rows: number; cols: number }>();\n\n/** Records the shape of a column-major array so functions can treat it as a 2D range. */\nexport function withRangeShape<T extends any[]>(values: T, rows: number, cols: number): T {\n rangeShapes.set(values, { rows, cols });\n return values;\n}\n\n/** Views an array as a range. Arrays without a known shape are a single column. */\nexport function toFormulaRange(values: any[]): FormulaRange {\n const shape = rangeShapes.get(values);\n if (shape && shape.rows * shape.cols === values.length) return { ...shape, values };\n return { rows: values.length, cols: 1, values };\n}\n\nexport const rangeGet = (range: FormulaRange, row: number, col: number): any =>\n row < range.rows && col < range.cols ? range.values[col * range.rows + row] : '';\n\n/** Builds a column-major array (with its shape recorded) from a sub-block of a range. */\nexport function sliceRange(\n range: FormulaRange,\n rowStart: number,\n rowCount: number,\n colStart: number,\n colCount: number,\n): any[] {\n const out: any[] = [];\n for (let c = colStart; c < colStart + colCount; c++) {\n for (let r = rowStart; r < rowStart + rowCount; r++) out.push(rangeGet(range, r, c));\n }\n return withRangeShape(out, rowCount, colCount);\n}\n\n// --- Dates ---\n//\n// Dates are ISO text (\"2026-01-31\", or \"2026-01-31 13:45:00\" with a time), as\n// returned by TODAY(). Numbers are read as spreadsheet serial dates (days since\n// 1899-12-30), so values pasted from Excel or Numbers still work.\n\nconst MS_PER_DAY = 24 * 60 * 60 * 1000;\nconst pad = (n: number, width = 2) => String(n).padStart(width, '0');\n\n/** Parses a date value (ISO text, serial number or Date) into a local Date, or null. */\nexport function toDate(value: unknown): Date | null {\n if (value instanceof Date) return isNaN(value.getTime()) ? null : value;\n if (typeof value === 'number') return serialToDate(value);\n if (typeof value !== 'string' || FORMULA_ERRORS.has(value)) return null;\n const text = value.trim();\n if (text === '') return null;\n const iso = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})(?:[ T](\\d{1,2}):(\\d{2})(?::(\\d{2}))?)?$/.exec(text);\n if (iso) {\n const [, y, m, d, hh = '0', mm = '0', ss = '0'] = iso;\n const date = new Date(Number(y), Number(m) - 1, Number(d), Number(hh), Number(mm), Number(ss));\n // Reject impossible dates such as 2026-02-30 instead of rolling them over.\n return date.getMonth() === Number(m) - 1 ? date : null;\n }\n const serial = numericValue(text);\n if (serial !== null) return serialToDate(serial);\n // Other textual dates (\"Jan 5, 2026\", \"1/5/2026\") use the platform parser.\n if (/[a-z/]/i.test(text)) {\n const parsed = Date.parse(text);\n return isNaN(parsed) ? null : new Date(parsed);\n }\n return null;\n}\n\nconst ISO_DATE = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})(?:[ T](\\d{1,2}):(\\d{2})(?::(\\d{2}))?)?$/;\n\n/**\n * Parses ISO date text (\"2026-01-31\" or \"2026-01-31 13:45[:00]\") into a\n * local Date, or null. Only ISO text counts: date arithmetic and date\n * comparisons never reinterpret other text or numbers as dates.\n */\nexport function parseIsoDate(value: unknown): Date | null {\n if (typeof value !== 'string' || value.length < 8 || value.length > 19) return null;\n const iso = ISO_DATE.exec(value.trim());\n if (!iso) return null;\n const [, y, m, d, hh = '0', mm = '0', ss = '0'] = iso;\n const date = new Date(Number(y), Number(m) - 1, Number(d), Number(hh), Number(mm), Number(ss));\n return date.getMonth() === Number(m) - 1 && date.getDate() === Number(d) ? date : null;\n}\n\n/** Whether ISO date text includes a time of day. */\nexport const isoDateHasTime = (value: string): boolean => /[ T]\\d/.test(value.trim());\n\nfunction isoDateTime(value: string): number | null {\n // Cheap pre-check: ISO dates start with four digits and a dash.\n if (value.length < 8 || value.charCodeAt(4) !== 45 /* - */) return null;\n return parseIsoDate(value)?.getTime() ?? null;\n}\n\n/** Spreadsheet serial number (days since 1899-12-30, fraction = time of day). */\nexport function dateToSerial(date: Date): number {\n const utc = Date.UTC(\n date.getFullYear(),\n date.getMonth(),\n date.getDate(),\n date.getHours(),\n date.getMinutes(),\n date.getSeconds(),\n );\n return cleanFloat((utc - Date.UTC(1899, 11, 30)) / MS_PER_DAY);\n}\n\nexport function serialToDate(serial: number): Date | null {\n if (!Number.isFinite(serial)) return null;\n const utc = new Date(Date.UTC(1899, 11, 30) + Math.round(serial * MS_PER_DAY));\n return new Date(\n utc.getUTCFullYear(),\n utc.getUTCMonth(),\n utc.getUTCDate(),\n utc.getUTCHours(),\n utc.getUTCMinutes(),\n utc.getUTCSeconds(),\n );\n}\n\n/** Formats a local date as \"YYYY-MM-DD\" (the format TODAY() returns). */\nexport const formatIsoDate = (date: Date): string =>\n `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;\n\n/** Formats a local date and time as \"YYYY-MM-DD HH:MM:SS\" (the format NOW() returns). */\nexport const formatIsoDateTime = (date: Date): string =>\n `${formatIsoDate(date)} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;\n\n/** Whole days from `start` to `end`, ignoring time of day and DST shifts. */\nexport const daysBetween = (start: Date, end: Date): number =>\n Math.round(\n (Date.UTC(end.getFullYear(), end.getMonth(), end.getDate()) -\n Date.UTC(start.getFullYear(), start.getMonth(), start.getDate())) /\n MS_PER_DAY,\n );\n","// Portions ported from gridcraft/crates/core/src/parse.rs and gridcraft/crates/core/src/date.rs\n// (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n/**\n * Parsing what people type into a cell (and numeric text in formulas).\n *\n * Recognises numbers with thousands separators, percentages, currency,\n * scientific notation, fractions (`1 1/2`), dates (`3/4/2026`, `2026-03-04`,\n * `4-Mar-2026`, `Mar 4, 2026`), times (`10:30`, `10:30:15 PM`) and\n * date-times. Locale: en-US (month/day/year).\n *\n * Representation: our engine keeps dates as ISO text (`2026-03-04`, or\n * `2026-03-04 18:00:00` with a time), so a typed date becomes ISO text, not a\n * serial number. A time typed on its own (`12:00`) has no date to anchor ISO\n * text to, so it becomes a fraction of a day (0.5), as in Excel.\n */\nimport type { CellFormat } from '../types';\nimport { FORMULA_ERRORS } from './values';\n\n/** Kind of automatic number format chosen for typed input. */\nexport type CellInputFormatKind = 'number' | 'currency' | 'percentage' | 'date' | 'time';\n\nexport interface ParsedCellInput {\n /** The typed value: number, boolean, ISO date text, error code or text. `''` for empty input. */\n value: string | number | boolean;\n type: 'empty' | 'text' | 'number' | 'boolean' | 'error' | 'date';\n /**\n * Number format code Excel would apply automatically (e.g. `\"$\"#,##0.00`,\n * `0%`, `#,##0`, `m/d/yyyy`, `h:mm`), or undefined for General.\n */\n numberFormat?: string;\n /** What kind of format that is, if any. */\n formatKind?: CellInputFormatKind;\n /** `formatKind` mapped onto `CellFormat` (times have no `CellFormat` and map to undefined). */\n cellFormat?: CellFormat;\n}\n\n// --- Date helpers (from date.rs) ---\n\nexport const MONTH_NAMES = [\n 'January', 'February', 'March', 'April', 'May', 'June',\n 'July', 'August', 'September', 'October', 'November', 'December',\n];\n\n/** Days since 1970-01-01 for a civil date (Howard Hinnant's algorithm). */\nexport function daysFromCivil(year: number, month: number, day: number): number {\n const y = month <= 2 ? year - 1 : year;\n const era = Math.floor((y >= 0 ? y : y - 399) / 400);\n const yoe = y - era * 400;\n const doy = Math.floor((153 * (month > 2 ? month - 3 : month + 9) + 2) / 5) + day - 1;\n const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;\n return era * 146097 + doe - 719468;\n}\n\nexport const isLeapYear = (y: number): boolean => (y % 4 === 0 && y % 100 !== 0) || y % 400 === 0;\n\nexport function daysInMonth(year: number, month: number): number {\n if (month === 2) return isLeapYear(year) ? 29 : 28;\n if ([4, 6, 9, 11].includes(month)) return 30;\n return month >= 1 && month <= 12 ? 31 : 0;\n}\n\n/**\n * Days in a month of Excel's 1900 date system: February 1900 has 29 days\n * (serial 60 is the fictitious 1900-02-29, kept for Lotus 1-2-3 compatibility).\n */\nexport const daysInMonth1900 = (year: number, month: number): number =>\n year === 1900 && month === 2 ? 29 : daysInMonth(year, month);\n\nconst UNIX_1899_12_30 = -25569;\nconst MAX_SERIAL_1900 = 2_958_465; // 9999-12-31\n\n/**\n * Excel 1900-system serial for a date (1 = 1900-01-01, 60 = the fictitious\n * 1900-02-29). Month and day overflow roll over like `DATE()`; years 0–1899\n * are added to 1900. Null before the epoch or after 9999-12-31.\n */\nexport function serialFromYmd(year: number, month: number, day: number): number | null {\n if (![year, month, day].every(Number.isSafeInteger)) return null;\n let y = year >= 0 && year < 1900 ? year + 1900 : year;\n const m0 = month - 1;\n y += Math.floor(m0 / 12);\n const m = (((m0 % 12) + 12) % 12) + 1;\n if (y < 1900 || y > 9999) return null;\n // Up to February 1900 the serial is one lower than the day count from 1899-12-30.\n const first = daysFromCivil(y, m, 1) - UNIX_1899_12_30 - (y === 1900 && m <= 2 ? 1 : 0);\n const serial = first + day - 1;\n return serial >= 0 && serial <= MAX_SERIAL_1900 ? serial : null;\n}\n\n/** Fraction of a day for a time. */\nexport const timeFraction = (h: number, m: number, s: number): number => (h * 3600 + m * 60 + s) / 86400;\n\n/** The year used for dates typed without one. */\nconst currentYear = (): number => new Date().getFullYear();\n\n// --- Parsing (from parse.rs) ---\n\ninterface DateParts {\n year: number;\n month: number;\n day: number;\n /** Fraction of a day, when a time was typed. */\n time?: number;\n}\n\ntype Numeric =\n | { kind: 'number'; value: number; format?: string }\n | { kind: 'date'; date: DateParts; format: string }\n | { kind: 'time'; value: number; format: string };\n\n/**\n * Interprets typed cell input (not formulas). Text that isn't a number,\n * date, time, boolean or error stays text; a leading apostrophe forces text.\n */\nexport function parseCellInput(input: string): ParsedCellInput {\n if (input === '') return { value: '', type: 'empty' };\n if (input.startsWith(\"'\")) return { value: input.slice(1), type: 'text' };\n const t = input.trim();\n const upper = t.toUpperCase();\n if (upper === 'TRUE') return { value: true, type: 'boolean' };\n if (upper === 'FALSE') return { value: false, type: 'boolean' };\n if (FORMULA_ERRORS.has(upper)) return { value: upper, type: 'error' };\n const numeric = parseNumeric(t);\n if (numeric) {\n if (numeric.kind === 'date') {\n const iso = isoText(numeric.date);\n // ISO text can't hold the fictitious 1900-02-29: it stays text.\n if (iso !== null) {\n return { value: iso, type: 'date', numberFormat: numeric.format, formatKind: 'date', cellFormat: 'date' };\n }\n } else {\n const formatKind = numeric.kind === 'time' ? 'time' : formatKindOf(numeric.format);\n return {\n value: normalizeZero(numeric.value),\n type: 'number',\n ...(numeric.format ? { numberFormat: numeric.format } : {}),\n ...(formatKind ? { formatKind } : {}),\n ...(formatKind && formatKind !== 'time' ? { cellFormat: formatKind } : {}),\n };\n }\n }\n return { value: input, type: 'text' };\n}\n\n/**\n * Numeric text as arithmetic coerces it (\"1,000\", \"50%\", \"$3\", \"1/2/2020\",\n * \"10:00\"); dates become 1900-system serial numbers. Null when it isn't numeric.\n */\nexport function parseNumberText(text: string): number | null {\n const numeric = parseNumeric(text.trim());\n if (!numeric) return null;\n if (numeric.kind !== 'date') return normalizeZero(numeric.value);\n const { year, month, day, time = 0 } = numeric.date;\n const serial = serialFromYmd(year, month, day);\n return serial === null ? null : serial + time;\n}\n\nconst normalizeZero = (n: number) => (n === 0 ? 0 : n);\n\nfunction formatKindOf(format: string | undefined): CellInputFormatKind | undefined {\n if (!format) return undefined;\n if (format.includes('$')) return 'currency';\n if (format.includes('%')) return 'percentage';\n return 'number';\n}\n\nconst pad = (n: number, width = 2) => String(n).padStart(width, '0');\n\n/** ISO text for parsed date parts, or null for the fictitious 1900-02-29. */\nfunction isoText({ year, month, day, time }: DateParts): string | null {\n if (day > daysInMonth(year, month)) return null;\n const date = `${pad(year, 4)}-${pad(month)}-${pad(day)}`;\n if (time === undefined) return date;\n let seconds = Math.round(time * 86400);\n if (seconds >= 86400) seconds = 86399; // never roll a typed time into the next day\n return `${date} ${pad(Math.floor(seconds / 3600))}:${pad(Math.floor(seconds / 60) % 60)}:${pad(seconds % 60)}`;\n}\n\nfunction parseNumeric(t: string): Numeric | null {\n if (t === '') return null;\n // Parenthesised negatives: (100) = -100.\n if (t.startsWith('(') && t.endsWith(')') && t.length >= 2) {\n const inner = parseNumeric(t.slice(1, -1).trim());\n if (!inner || inner.kind === 'date') return null;\n return { ...inner, value: -inner.value };\n }\n const plain = parsePlain(t);\n if (plain) return plain;\n const fraction = parseFraction(t);\n if (fraction !== null) return { kind: 'number', value: fraction, format: '# ?/?' };\n return parseDateTime(t);\n}\n\nconst startsWithDigitOrDot = (s: string) => /^[0-9.]/.test(s);\n\n/** Signs, currency, thousands separators, percent, exponent. */\nfunction parsePlain(t: string): Numeric | null {\n let s = t;\n let neg = false;\n let currency = false;\n let percent = false;\n if (s.startsWith('-')) {\n neg = true;\n s = s.slice(1).trimStart();\n } else if (s.startsWith('+')) {\n s = s.slice(1).trimStart();\n }\n if (s.startsWith('$')) {\n currency = true;\n s = s.slice(1).trimStart();\n if (!neg && s.startsWith('-')) {\n neg = true;\n s = s.slice(1);\n }\n }\n if (s.endsWith('%')) {\n percent = true;\n s = s.slice(0, -1).trimEnd();\n }\n if (s === '') return null;\n const hasSep = s.includes(',');\n let digits: string;\n if (hasSep) {\n // Thousands separators must be in groups of three before the decimal point.\n const dot = s.indexOf('.');\n const int = dot === -1 ? s : s.slice(0, dot);\n const frac = dot === -1 ? null : s.slice(dot + 1);\n const groups = int.split(',');\n const first = groups[0];\n if (first === '' || first.length > 3 || groups.slice(1).some((g) => g.length !== 3)) return null;\n digits = groups.join('') + (frac !== null ? `.${frac}` : '');\n } else {\n digits = s;\n }\n if (!/^[0-9.eE+-]+$/.test(digits) || !startsWithDigitOrDot(digits)) return null;\n // Rust's f64 parser accepts exactly decimal literals; JS `Number` is looser\n // (\"1.\" and \"1e\" differ), so check the shape too.\n if (!/^(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$/.test(digits)) return null;\n let n = Number(digits);\n if (!Number.isFinite(n)) return null;\n const sci = /[eE]/.test(digits);\n if (percent) n /= 100;\n if (neg) n = -n;\n const hasDot = digits.includes('.');\n const format = currency\n ? hasDot ? '\"$\"#,##0.00' : '\"$\"#,##0'\n : percent\n ? hasDot ? '0.00%' : '0%'\n : sci\n ? '0.00E+00'\n : hasSep\n ? hasDot ? '#,##0.00' : '#,##0'\n : undefined;\n return { kind: 'number', value: n, ...(format ? { format } : {}) };\n}\n\nconst parseDecimal = (s: string): number | null =>\n /^[+-]?(\\d+\\.?\\d*|\\.\\d+)([eE][+-]?\\d+)?$/.test(s) ? Number(s) : null;\n\n/** `1 1/2`, `0 3/4` (a bare `1/2` is a date). */\nfunction parseFraction(t: string): number | null {\n const space = t.indexOf(' ');\n if (space === -1) return null;\n const whole = t.slice(0, space);\n const frac = t.slice(space + 1).trim();\n const neg = whole.startsWith('-');\n const w = parseDecimal(whole.replace(/^-+/, ''));\n const slash = frac.indexOf('/');\n if (w === null || slash === -1) return null;\n const a = parseDecimal(frac.slice(0, slash));\n const b = parseDecimal(frac.slice(slash + 1));\n if (a === null || b === null || b === 0 || !Number.isFinite(w)) return null;\n const v = w + a / b;\n return neg ? -v : v;\n}\n\nfunction monthFromName(s: string): number | null {\n const l = s.toLowerCase();\n if (l.length < 3) return null;\n const index = MONTH_NAMES.findIndex((m) => m.toLowerCase().startsWith(l));\n return index === -1 ? null : index + 1;\n}\n\n/** `h:mm`, `h:mm:ss`, `h:mm:ss.000`, `h AM`, with optional AM/PM. */\nfunction parseTime(input: string): { fraction: number; seconds: boolean; ampm: boolean } | null {\n const lower = input.trim().toLowerCase();\n let body = lower;\n let pm: boolean | null = null;\n for (const [suffix, isPm] of [['am', false], ['a', false], ['pm', true], ['p', true]] as const) {\n if (lower.endsWith(suffix)) {\n body = lower.slice(0, -suffix.length).trim();\n pm = isPm;\n break;\n }\n }\n const parts = body.split(':');\n if (parts.length === 1 && pm === null) return null;\n if (parts.length > 3 || parts.some((p) => p === '')) return null;\n const num = (p: string | undefined) => (p === undefined ? 0 : parseDecimal(p));\n let h = num(parts[0]);\n const m = num(parts[1]);\n const s = num(parts[2]);\n if (h === null || m === null || s === null) return null;\n if (m >= 60 || s >= 60 || h < 0 || m < 0 || s < 0) return null;\n if (pm !== null) {\n if (h > 12) return null;\n if (h === 12) h = 0;\n if (pm) h += 12;\n } else if (h >= 10000) {\n return null;\n }\n return { fraction: timeFraction(h, m, s), seconds: parts.length === 3, ampm: pm !== null };\n}\n\nconst expandYear = (text: string, y: number) => (text.length <= 2 ? (y < 30 ? 2000 + y : 1900 + y) : y);\n\nfunction validYmd(year: number, month: number, day: number): DateParts | null {\n // Dates before 1900 stay text (`DATE()` would add 1900 to such a year).\n if (month < 1 || month > 12 || day < 1 || year < 1900 || year > 9999) return null;\n if (day > daysInMonth1900(year, month)) return null;\n return { year, month, day };\n}\n\nfunction parseDate(input: string): { date: DateParts; format: string } | null {\n const t = input.trim().replace(/,+$/, '');\n const num = (s: string) => (/^[+-]?\\d+$/.test(s.trim()) ? Number(s.trim()) : null);\n const withFormat = (date: DateParts | null, format: string) => (date ? { date, format } : null);\n const parts = t.split(/[/.-]/);\n if (parts.length === 3) {\n const [a, b, c] = parts.map((p) => p.trim());\n // ISO yyyy-mm-dd or yyyy/mm/dd\n if (a.length === 4) {\n const [y, m, d] = [num(a), num(b), num(c)];\n if (y !== null && m !== null && d !== null) return withFormat(validYmd(y, m, d), 'yyyy-mm-dd');\n }\n {\n const [m, d, y] = [num(a), num(b), num(c)];\n if (m !== null && d !== null && y !== null) return withFormat(validYmd(expandYear(c, y), m, d), 'm/d/yyyy');\n }\n // 4-Mar-2026\n const [d, m, y] = [num(a), monthFromName(b), num(c)];\n if (d !== null && m !== null && y !== null) return withFormat(validYmd(expandYear(c, y), m, d), 'd-mmm-yy');\n }\n if (parts.length === 2) {\n const [a, b] = parts.map((p) => p.trim());\n const [x, y] = [num(a), num(b)];\n if (x !== null && y !== null) {\n // m/d in the current year\n if (x >= 1 && x <= 12 && y >= 1 && y <= 31) return withFormat(validYmd(currentYear(), x, y), 'd-mmm');\n // m/yyyy\n if (x >= 1 && x <= 12 && b.length === 4) return withFormat(validYmd(y, x, 1), 'mmm-yy');\n }\n const dayA = num(a);\n const monthB = monthFromName(b);\n if (dayA !== null && monthB !== null) return withFormat(validYmd(currentYear(), monthB, dayA), 'd-mmm');\n const monthA = monthFromName(a);\n const yearB = num(b);\n if (monthA !== null && yearB !== null) return withFormat(validYmd(expandYear(b, yearB), monthA, 1), 'mmm-yy');\n }\n // \"Mar 4, 2026\" / \"March 4 2026\" / \"4 March 2026\"\n const words = t.split(/[ ,]/).filter((w) => w !== '');\n if (words.length === 3) {\n const [w0, w1, w2] = words;\n let [m, d, y] = [monthFromName(w0), num(w1), num(w2)];\n if (m !== null && d !== null && y !== null) return withFormat(validYmd(expandYear(w2, y), m, d), 'd-mmm-yy');\n [d, m, y] = [num(w0), monthFromName(w1), num(w2)];\n if (d !== null && m !== null && y !== null) return withFormat(validYmd(expandYear(w2, y), m, d), 'd-mmm-yy');\n }\n return null;\n}\n\nfunction parseDateTime(t: string): Numeric | null {\n const time = parseTime(t);\n if (time) {\n const format =\n time.fraction >= 1\n ? '[h]:mm:ss' // 24 hours or more: elapsed hours\n : time.seconds\n ? time.ampm ? 'h:mm:ss AM/PM' : 'h:mm:ss'\n : time.ampm ? 'h:mm AM/PM' : 'h:mm';\n return { kind: 'time', value: time.fraction, format };\n }\n const date = parseDate(t);\n if (date) return { kind: 'date', date: date.date, format: date.format };\n // date + time: split at a space before a time-looking tail.\n for (let i = t.indexOf(' '); i !== -1; i = t.indexOf(' ', i + 1)) {\n const d = parseDate(t.slice(0, i));\n const tm = d ? parseTime(t.slice(i + 1)) : null;\n // A date-time's time of day must stay within the day.\n if (d && tm && tm.fraction < 1) {\n return { kind: 'date', date: { ...d.date, time: tm.fraction }, format: 'm/d/yyyy h:mm' };\n }\n }\n return null;\n}\n","// Portions ported from gridcraft/crates/numfmt/src/decimal.rs (MIT OR Apache-2.0,\n// © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n/**\n * Decimal-string rounding and the General format.\n *\n * Excel works with 15 significant digits, so numbers are first written with\n * 15 significant digits and then rounded half away from zero on that decimal\n * string. That makes 2.675 with `0.00` show 2.68 and 1.005 show 1.01, as in\n * Excel, even though neither is exact in binary.\n */\n\n/** Most decimals ever computed; placeholders beyond this are padded with zeros. */\nexport const MAX_DECIMALS = 400;\n\n/**\n * `n` significant digits of a positive finite `a` (correctly rounded) and the\n * power of ten of the first digit.\n */\nfunction significantDigits(a: number, n: number): { digits: number[]; exp: number } {\n const s = a.toExponential(Math.max(0, Math.min(100, n - 1)));\n const e = s.indexOf('e');\n const digits: number[] = [];\n for (let i = 0; i < e; i++) {\n const c = s.charCodeAt(i);\n if (c >= 48 && c <= 57) digits.push(c - 48);\n }\n return { digits, exp: Number(s.slice(e + 1)) };\n}\n\n/** The exact decimal expansion of a positive finite double, as significant digits (no trailing zeros). */\nfunction exactDigits(a: number): string {\n const view = new DataView(new ArrayBuffer(8));\n view.setFloat64(0, a);\n const hi = view.getUint32(0);\n const lo = view.getUint32(4);\n const biased = (hi >>> 20) & 0x7ff;\n let mantissa = (BigInt(hi & 0xfffff) << 32n) | BigInt(lo);\n let exp2: number;\n if (biased === 0) {\n exp2 = -1074;\n } else {\n mantissa |= 1n << 52n;\n exp2 = biased - 1075;\n }\n const digits = exp2 >= 0 ? (mantissa << BigInt(exp2)).toString() : (mantissa * 5n ** BigInt(-exp2)).toString();\n return digits.replace(/0+$/, '');\n}\n\n/** True when `a`'s exact decimal expansion has a 5 as its 16th significant digit and only zeros after. */\nfunction isExactTie(a: number): boolean {\n const isTie = (ds: string) => ds.length >= 16 && ds[15] === '5' && /^0*$/.test(ds.slice(16));\n // A cheap screen first (30 digits), then the exact expansion.\n return isTie(significantDigits(a, 30).digits.join('')) && isTie(exactDigits(a).padEnd(16, '0'));\n}\n\n/**\n * The 15 significant digits of a positive finite number and the power of ten\n * of the first digit: `a ≈ d0.d1d2… × 10^exp`.\n *\n * The value is rounded to the nearest 15-digit decimal. An exact tie at the\n * 16th digit goes toward zero, as Excel does: `1234567890123455` shows\n * `1234567890123450`, not `…460`.\n */\nexport function decimalDigits(a: number): { digits: number[]; exp: number } {\n if (!Number.isFinite(a) || a <= 0) return { digits: new Array(15).fill(0), exp: 0 };\n const { digits: d16, exp } = significantDigits(a, 16);\n const digits = d16.slice(0, 15);\n while (digits.length < 15) digits.push(0);\n const sixteenth = d16[15] ?? 0;\n if (sixteenth <= 4) return { digits, exp };\n if (sixteenth === 5) {\n if (isExactTie(a)) return { digits, exp };\n // Not a tie: round the exact value (the 16-digit rounding may be a double rounding).\n const r = significantDigits(a, 15);\n while (r.digits.length < 15) r.digits.push(0);\n return r;\n }\n let e = exp;\n let carry = true;\n for (let i = 14; i >= 0; i--) {\n if (digits[i] === 9) {\n digits[i] = 0;\n } else {\n digits[i] += 1;\n carry = false;\n break;\n }\n }\n if (carry) {\n digits.fill(0);\n digits[0] = 1;\n e += 1;\n }\n return { digits, exp: e };\n}\n\n/**\n * Rounds the number `digits × 10^exp` (see `decimalDigits`) to `decimals`\n * places, half away from zero. Returns the integer digits (`''` when the\n * integer part is zero) and exactly `decimals` fraction digits.\n */\nexport function fixedFromDigits(digits: number[], exp: number, decimals: number): [string, string] {\n const decimalsCalc = Math.min(decimals, MAX_DECIMALS);\n const keep = exp + decimalsCalc + 1;\n let m: number[];\n if (keep <= 0) {\n m = keep === 0 && (digits[0] ?? 0) >= 5 ? [1] : [];\n } else if (keep >= digits.length) {\n m = digits.slice();\n while (m.length < keep) m.push(0);\n } else {\n m = digits.slice(0, keep);\n if ((digits[keep] ?? 0) >= 5) {\n let carry = true;\n for (let i = m.length - 1; i >= 0; i--) {\n if (m[i] === 9) {\n m[i] = 0;\n } else {\n m[i] += 1;\n carry = false;\n break;\n }\n }\n if (carry) m.unshift(1);\n }\n }\n let lead = 0;\n while (lead < m.length && m[lead] === 0) lead++;\n m = m.slice(lead);\n const toS = (ds: number[]) => ds.join('');\n let int: string;\n let frac: string;\n if (m.length <= decimalsCalc) {\n int = '';\n frac = '0'.repeat(decimalsCalc - m.length) + toS(m);\n } else {\n const split = m.length - decimalsCalc;\n int = toS(m.slice(0, split));\n frac = toS(m.slice(split));\n }\n if (decimals > decimalsCalc) frac += '0'.repeat(decimals - decimalsCalc);\n return [int, frac];\n}\n\n/** Rounds a non-negative number to `decimals` places (see `fixedFromDigits`). */\nexport function fixed(a: number, decimals: number): [string, string] {\n if (!Number.isFinite(a) || a <= 0) return ['', '0'.repeat(Math.min(decimals, MAX_DECIMALS * 4))];\n const { digits, exp } = decimalDigits(a);\n return fixedFromDigits(digits, exp, decimals);\n}\n\nconst charCount = (s: string) => Array.from(s).length;\n\n/**\n * The General format squeezed into `maxChars` characters, as Excel shows it\n * in a narrow column: fewer decimals, then scientific notation; `null` when\n * even that does not fit (the caller paints `####`).\n */\nexport function formatGeneralFit(n: number, maxChars: number): string | null {\n if (!Number.isFinite(n)) return null;\n if (n === 0) return maxChars >= 1 ? '0' : null;\n const sign = n < 0 ? '-' : '';\n const { digits, exp: x } = decimalDigits(Math.abs(n));\n const intLen = sign.length + Math.max(x, 0) + 1;\n // Fixed notation for magnitudes from 1E-4 up to what fits.\n if (x >= -4 && intLen <= maxChars) {\n const avail = maxChars - intLen;\n let decimals = avail >= 2 ? avail - 1 : 0;\n for (;;) {\n const [i, f0] = fixedFromDigits(digits, x, decimals);\n const f = f0.replace(/0+$/, '');\n if (i === '' && f === '') break; // rounded away to zero: use scientific\n let s = sign + (i === '' ? '0' : i);\n if (f !== '') s += `.${f}`;\n if (charCount(s) <= maxChars) return s;\n if (decimals === 0) break;\n decimals -= 1;\n }\n }\n for (let md = Math.min(maxChars, 14); md >= 0; md--) {\n let e = x;\n let [mi, mf] = fixedFromDigits(digits, 0, md);\n if (mi.length > 1) {\n e += 1;\n [mi, mf] = fixedFromDigits(digits, -1, md);\n }\n mf = mf.replace(/0+$/, '');\n let s = sign + (mi === '' ? '0' : mi);\n if (mf !== '') s += `.${mf}`;\n s += `E${e < 0 ? '-' : '+'}${String(Math.abs(e)).padStart(2, '0')}`;\n if (charCount(s) <= maxChars) return s;\n }\n return null;\n}\n","// Portions ported from gridcraft/crates/numfmt/src/builtin.rs (MIT OR Apache-2.0,\n// © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n/** Built-in number formats (ECMA-376 §18.8.30, en-US). */\n\nconst BUILTINS: ReadonlyArray<readonly [number, string]> = [\n [0, 'General'],\n [1, '0'],\n [2, '0.00'],\n [3, '#,##0'],\n [4, '#,##0.00'],\n [5, '\"$\"#,##0_);\\\\(\"$\"#,##0\\\\)'],\n [6, '\"$\"#,##0_);[Red]\\\\(\"$\"#,##0\\\\)'],\n [7, '\"$\"#,##0.00_);\\\\(\"$\"#,##0.00\\\\)'],\n [8, '\"$\"#,##0.00_);[Red]\\\\(\"$\"#,##0.00\\\\)'],\n [9, '0%'],\n [10, '0.00%'],\n [11, '0.00E+00'],\n [12, '# ?/?'],\n [13, '# ??/??'],\n [14, 'm/d/yyyy'],\n [15, 'd-mmm-yy'],\n [16, 'd-mmm'],\n [17, 'mmm-yy'],\n [18, 'h:mm AM/PM'],\n [19, 'h:mm:ss AM/PM'],\n [20, 'h:mm'],\n [21, 'h:mm:ss'],\n [22, 'm/d/yyyy h:mm'],\n [37, '#,##0 ;(#,##0)'],\n [38, '#,##0 ;[Red](#,##0)'],\n [39, '#,##0.00;(#,##0.00)'],\n [40, '#,##0.00;[Red](#,##0.00)'],\n [41, '_(* #,##0_);_(* \\\\(#,##0\\\\);_(* \"-\"_);_(@_)'],\n [42, '_(\"$\"* #,##0_);_(\"$\"* \\\\(#,##0\\\\);_(\"$\"* \"-\"_);_(@_)'],\n [43, '_(* #,##0.00_);_(* \\\\(#,##0.00\\\\);_(* \"-\"??_);_(@_)'],\n [44, '_(\"$\"* #,##0.00_);_(\"$\"* \\\\(#,##0.00\\\\);_(\"$\"* \"-\"??_);_(@_)'],\n [45, 'mm:ss'],\n [46, '[h]:mm:ss'],\n [47, 'mmss.0'],\n [48, '##0.0E+0'],\n [49, '@'],\n];\n\n/**\n * Built-in format code for an id (0–49, en-US), e.g. 14 → `m/d/yyyy`.\n * Locale-specific ids (23–36) and unknown ids return undefined.\n */\nexport function builtinNumberFormat(id: number): string | undefined {\n return BUILTINS.find(([i]) => i === id)?.[1];\n}\n\n/** Id of a built-in format code. Matching ignores case and backslash escapes. */\nexport function builtinNumberFormatId(code: string): number | undefined {\n const exact = BUILTINS.find(([, c]) => c === code);\n if (exact) return exact[0];\n const norm = (s: string) => s.replace(/\\\\/g, '').toLowerCase();\n const n = norm(code);\n return BUILTINS.find(([, c]) => norm(c) === n)?.[0];\n}\n","// Portions ported from gridcraft/crates/numfmt/src/parse.rs (MIT OR Apache-2.0,\n// © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n/** Number format code parsing: sections, bracket tags and tokens. */\n\n/** Colour from a `[Red]` or `[ColorN]` section tag (`color1`…`color56` = palette index). */\nexport type NumberFormatColor =\n | 'black'\n | 'blue'\n | 'cyan'\n | 'green'\n | 'magenta'\n | 'red'\n | 'white'\n | 'yellow'\n | `color${number}`;\n\n/** At most this many sections are honoured (`pos;neg;zero;text`); extra ones are ignored. */\nconst MAX_SECTIONS = 4;\n\nexport type DatePart =\n | { k: 'year' | 'month' | 'minute' | 'day' | 'hour' | 'second' | 'subsec' | 'eh' | 'em' | 'es'; n: number }\n | { k: 'ampm' }\n /** `A/P` with the letters as typed. */\n | { k: 'ap'; a: string; p: string };\n\nexport const isCalendarPart = (d: DatePart): boolean => d.k === 'year' || d.k === 'month' || d.k === 'day';\n\nexport type Tok =\n | { t: 'lit'; s: string }\n /** `0`, `#` or `?`. */\n | { t: 'digit'; c: string }\n /** The decimal point (only the first one in a number section). */\n | { t: 'point' }\n /** Unresolved comma (resolved during analysis into thousands/scale markers or a literal). */\n | { t: 'comma' }\n | { t: 'percent' }\n | { t: 'exp'; upper: boolean; plus: boolean }\n /** The fraction bar (only after analysis decides the section is a fraction). */\n | { t: 'slash' }\n /** A fixed fraction denominator such as the `8` in `?/8`. */\n | { t: 'denom'; s: string }\n | { t: 'fill'; c: string }\n | { t: 'at' }\n | { t: 'general' }\n | { t: 'date'; d: DatePart };\n\n/** The text a token shows when it has no special meaning in its section. */\nexport function literalText(tok: Tok): string {\n switch (tok.t) {\n case 'lit':\n case 'denom':\n return tok.s;\n case 'digit':\n return tok.c;\n case 'point':\n return '.';\n case 'comma':\n return ',';\n case 'percent':\n return '%';\n case 'exp':\n return `${tok.upper ? 'E' : 'e'}${tok.plus ? '+' : '-'}`;\n case 'slash':\n return '/';\n default:\n return '';\n }\n}\n\nexport type CondOp = '=' | '<>' | '<' | '<=' | '>' | '>=';\n\nexport interface Cond {\n op: CondOp;\n value: number;\n}\n\nexport function testCond(c: Cond, v: number): boolean {\n switch (c.op) {\n case '=':\n return v === c.value;\n case '<>':\n return v !== c.value;\n case '<':\n return v < c.value;\n case '<=':\n return v <= c.value;\n case '>':\n return v > c.value;\n case '>=':\n return v >= c.value;\n }\n}\n\nexport type SecKind = 'number' | 'date' | 'text' | 'general';\n\nexport interface Section {\n toks: Tok[];\n color?: NumberFormatColor;\n cond?: Cond;\n kind: SecKind;\n thousands: boolean;\n /** Number of scaling commas (each divides by 1000). */\n scale: number;\n percent: number;\n hasAt: boolean;\n hasDigits: boolean;\n exp: boolean;\n fraction: boolean;\n currency: boolean;\n fill: boolean;\n}\n\nconst isDigitTok = (t: Tok | undefined, c?: string): boolean => t?.t === 'digit' && (c === undefined || t.c === c);\n\n/**\n * Splits a code into sections at unquoted, unescaped, unbracketed semicolons.\n * A `[` opens a bracket only when a `]` follows it somewhere; the bracket then\n * runs to the next `]`. An unclosed `[` is a literal, so a later `;` still\n * separates sections. Linear in the code length.\n */\nexport function splitSections(code: string): string[] {\n const cs = Array.from(code);\n const lastClose = cs.lastIndexOf(']');\n const out: string[] = [''];\n let quoted = false;\n let escape = false;\n let i = 0;\n while (i < cs.length) {\n const c = cs[i];\n i += 1;\n const cur = out.length - 1;\n if (escape) {\n out[cur] += c;\n escape = false;\n continue;\n }\n if (c === '\"') {\n quoted = !quoted;\n out[cur] += c;\n } else if ((c === '\\\\' || c === '_' || c === '*') && !quoted) {\n escape = true;\n out[cur] += c;\n } else if (c === '[' && !quoted && lastClose >= i) {\n // `i` is already past the `[`; the closing bracket is at or after it.\n let s = c;\n while (i < cs.length) {\n const d = cs[i];\n i += 1;\n s += d;\n if (d === ']') break;\n }\n out[cur] += s;\n } else if (c === ';' && !quoted) {\n if (out.length >= MAX_SECTIONS) break;\n out.push('');\n } else {\n out[cur] += c;\n }\n }\n return out;\n}\n\nconst lowerAscii = (c: string) => (c >= 'A' && c <= 'Z' ? c.toLowerCase() : c);\n\nfunction startsWithCi(cs: string[], i: number, pat: string): boolean {\n for (let k = 0; k < pat.length; k++) {\n const c = cs[i + k];\n if (c === undefined || lowerAscii(c) !== pat[k]) return false;\n }\n return true;\n}\n\nfunction pushLit(toks: Tok[], s: string): void {\n if (s === '') return;\n const last = toks[toks.length - 1];\n if (last?.t === 'lit') last.s += s;\n else toks.push({ t: 'lit', s });\n}\n\nconst CURRENCY_CHARS = new Set(['$', '€', '£', '¥', '₹', '₩', '₽', '¢', '₪', '₫', '฿']);\nconst isCurrencyChar = (c: string) => CURRENCY_CHARS.has(c);\n\nconst NAMED_COLORS = new Set(['black', 'blue', 'cyan', 'green', 'magenta', 'red', 'white', 'yellow']);\n\nfunction parseColor(lc: string): NumberFormatColor | undefined {\n if (NAMED_COLORS.has(lc)) return lc as NumberFormatColor;\n if (!lc.startsWith('color')) return undefined;\n const rest = lc.slice(5).trim();\n if (!/^\\+?\\d{1,3}$/.test(rest)) return undefined;\n const n = Number(rest);\n return n >= 1 && n <= 56 ? `color${n}` : undefined;\n}\n\nfunction parseCond(s: string): Cond | undefined {\n const t = s.trim();\n let op: CondOp;\n let rest: string;\n if (t.startsWith('<=')) [op, rest] = ['<=', t.slice(2)];\n else if (t.startsWith('>=')) [op, rest] = ['>=', t.slice(2)];\n else if (t.startsWith('<>')) [op, rest] = ['<>', t.slice(2)];\n else if (t.startsWith('<')) [op, rest] = ['<', t.slice(1)];\n else if (t.startsWith('>')) [op, rest] = ['>', t.slice(1)];\n else if (t.startsWith('=')) [op, rest] = ['=', t.slice(1)];\n else return undefined;\n const r = rest.trim();\n if (!/^[+-]?(\\d+\\.?\\d*|\\.\\d+)(e[+-]?\\d+)?$/i.test(r)) return undefined;\n const value = Number(r);\n return Number.isFinite(value) ? { op, value } : undefined;\n}\n\ninterface Raw {\n toks: Tok[];\n color?: NumberFormatColor;\n cond?: Cond;\n currency: boolean;\n}\n\n/** Handles the content of a `[...]` tag. */\nfunction bracket(raw: Raw, content: string): void {\n const lc = content.trim().toLowerCase();\n const color = parseColor(lc);\n if (color) {\n raw.color = color;\n return;\n }\n if (lc.startsWith('<') || lc.startsWith('>') || lc.startsWith('=')) {\n const c = parseCond(lc);\n if (c) raw.cond = c;\n return;\n }\n if (content.startsWith('$')) {\n const sym = content.slice(1).split('-')[0];\n if (sym !== '') {\n raw.currency = true;\n pushLit(raw.toks, sym);\n }\n return;\n }\n const first = lc[0];\n if ((first === 'h' || first === 'm' || first === 's') && Array.from(lc).every((c) => c === first)) {\n const n = Math.min(Array.from(lc).length, 255);\n raw.toks.push({ t: 'date', d: { k: first === 'h' ? 'eh' : first === 'm' ? 'em' : 'es', n } });\n }\n // Anything else ([DBNum1], [natural], locale tags…) is ignored.\n}\n\nfunction tokenize(src: string): Raw {\n const cs = Array.from(src);\n const raw: Raw = { toks: [], currency: false };\n const toks = raw.toks;\n // With no `]` after a `[`, that `[` is a literal; checking this once keeps the scan linear.\n const lastClose = cs.lastIndexOf(']');\n let i = 0;\n while (i < cs.length) {\n const c = cs[i];\n if (c === '\"') {\n let s = '';\n i += 1;\n while (i < cs.length) {\n const d = cs[i];\n i += 1;\n if (d === '\"') break;\n s += d;\n }\n if (Array.from(s).some(isCurrencyChar)) raw.currency = true;\n pushLit(toks, s);\n continue;\n }\n if (c === '\\\\') {\n const d = cs[i + 1];\n if (d !== undefined) {\n if (isCurrencyChar(d)) raw.currency = true;\n pushLit(toks, d);\n }\n i += 2;\n continue;\n }\n if (c === '_') {\n if (cs[i + 1] !== undefined) pushLit(toks, ' ');\n i += 2;\n continue;\n }\n if (c === '*') {\n const d = cs[i + 1];\n if (d !== undefined) toks.push({ t: 'fill', c: d });\n i += 2;\n continue;\n }\n if (c === '[') {\n const close = lastClose > i ? cs.indexOf(']', i + 1) : -1;\n if (close !== -1) {\n bracket(raw, cs.slice(i + 1, close).join(''));\n i = close + 1;\n } else {\n pushLit(toks, '[');\n i += 1;\n }\n continue;\n }\n if (c === '0' || c === '#' || c === '?') toks.push({ t: 'digit', c });\n else if (c === '.') toks.push({ t: 'point' });\n else if (c === ',') toks.push({ t: 'comma' });\n else if (c === '%') toks.push({ t: 'percent' });\n else if (c === '@') toks.push({ t: 'at' });\n else if (c === '/') {\n toks.push({ t: 'slash' });\n i += 1;\n if (cs[i] !== undefined && cs[i] >= '1' && cs[i] <= '9') {\n let s = '';\n while (i < cs.length && cs[i] >= '0' && cs[i] <= '9') {\n s += cs[i];\n i += 1;\n }\n toks.push({ t: 'denom', s });\n }\n continue;\n } else if ((c === 'e' || c === 'E') && (cs[i + 1] === '+' || cs[i + 1] === '-')) {\n toks.push({ t: 'exp', upper: c === 'E', plus: cs[i + 1] === '+' });\n i += 2;\n continue;\n } else if ((c === 'G' || c === 'g') && startsWithCi(cs, i, 'general')) {\n toks.push({ t: 'general' });\n i += 7;\n continue;\n } else if ((c === 'A' || c === 'a') && startsWithCi(cs, i, 'am/pm')) {\n toks.push({ t: 'date', d: { k: 'ampm' } });\n i += 5;\n continue;\n } else if ((c === 'A' || c === 'a') && startsWithCi(cs, i, 'a/p')) {\n toks.push({ t: 'date', d: { k: 'ap', a: c, p: cs[i + 2] ?? 'P' } });\n i += 3;\n continue;\n } else if ('yYmMdDhHsSeE'.includes(c)) {\n const lc = lowerAscii(c);\n let n = 0;\n while (cs[i + n] !== undefined && lowerAscii(cs[i + n]) === lc) n += 1;\n const n8 = Math.min(n, 255);\n const d: DatePart =\n lc === 'y'\n ? { k: 'year', n: n8 }\n : lc === 'e'\n ? { k: 'year', n: 4 }\n : lc === 'm'\n ? { k: 'month', n: n8 }\n : lc === 'd'\n ? { k: 'day', n: n8 }\n : lc === 'h'\n ? { k: 'hour', n: n8 }\n : { k: 'second', n: n8 };\n toks.push({ t: 'date', d });\n i += Math.max(n, 1);\n continue;\n } else {\n if (isCurrencyChar(c)) raw.currency = true;\n pushLit(toks, c);\n }\n i += 1;\n }\n return raw;\n}\n\nconst datePartOf = (t: Tok | undefined): DatePart | undefined => (t?.t === 'date' ? t.d : undefined);\n\nfunction analyze(raw: Raw): Section {\n const toks = raw.toks;\n const isDate = toks.some((t) => t.t === 'date');\n const hasGeneral = toks.some((t) => t.t === 'general');\n const sec: Section = {\n toks: [],\n color: raw.color,\n cond: raw.cond,\n kind: 'number',\n thousands: false,\n scale: 0,\n percent: 0,\n hasAt: toks.some((t) => t.t === 'at'),\n hasDigits: false,\n exp: false,\n fraction: false,\n currency: raw.currency,\n fill: toks.some((t) => t.t === 'fill'),\n };\n if (isDate) {\n sec.kind = 'date';\n // m/mm after hours or before seconds means minutes. Scanning for the\n // nearest date part either side is linear overall (prev/next are tracked).\n const prevPart: Array<DatePart | undefined> = [];\n let last: DatePart | undefined;\n for (let i = 0; i < toks.length; i++) {\n prevPart.push(last);\n const d = datePartOf(toks[i]);\n if (d) last = d;\n }\n const nextPart: Array<DatePart | undefined> = new Array(toks.length);\n let next: DatePart | undefined;\n for (let i = toks.length - 1; i >= 0; i--) {\n nextPart[i] = next;\n const d = datePartOf(toks[i]);\n if (d) next = d;\n }\n for (let i = 0; i < toks.length; i++) {\n const t = toks[i];\n if (t.t !== 'date' || t.d.k !== 'month' || t.d.n > 2) continue;\n const prev = prevPart[i];\n const nxt = nextPart[i];\n const afterH = prev?.k === 'hour' || prev?.k === 'eh';\n const beforeS = nxt?.k === 'second' || nxt?.k === 'es';\n if (afterH || beforeS) toks[i] = { t: 'date', d: { k: 'minute', n: t.d.n } };\n }\n const out: Tok[] = [];\n let i = 0;\n while (i < toks.length) {\n const t = toks[i];\n if (t.t === 'point' && isDigitTok(toks[i + 1], '0')) {\n let n = 0;\n while (isDigitTok(toks[i + 1 + n], '0')) n += 1;\n out.push({ t: 'date', d: { k: 'subsec', n: Math.min(n, 255) } });\n i += 1 + n;\n continue;\n }\n if (t.t === 'date' || t.t === 'fill') out.push(t);\n else pushLit(out, literalText(t));\n i += 1;\n }\n sec.toks = out;\n return sec;\n }\n if (hasGeneral) {\n sec.kind = 'general';\n const out: Tok[] = [];\n for (const t of toks) {\n if (t.t === 'general' || t.t === 'fill') out.push(t);\n else pushLit(out, literalText(t));\n }\n sec.toks = out;\n return sec;\n }\n sec.hasDigits = toks.some((t) => t.t === 'digit');\n if (sec.hasAt && !sec.hasDigits) {\n sec.kind = 'text';\n const out: Tok[] = [];\n for (const t of toks) {\n if (t.t === 'at' || t.t === 'fill') out.push(t);\n else pushLit(out, literalText(t));\n }\n sec.toks = out;\n return sec;\n }\n // Number section.\n let seenPoint = false;\n let seenExp = false;\n let seenDigit = false;\n let fractionSlash = false;\n // Precomputed so each comma and slash is O(1): whether the first non-comma\n // token after a position is a digit, the first non-literal token after a\n // position, and where the last exponent marker is.\n const digitAfterCommas: boolean[] = new Array(toks.length).fill(false);\n const firstNonLitAfter: Array<Tok | undefined> = new Array(toks.length);\n let after = false;\n let nonLit: Tok | undefined;\n for (let j = toks.length - 1; j >= 0; j--) {\n digitAfterCommas[j] = after;\n firstNonLitAfter[j] = nonLit;\n const t = toks[j];\n if (t.t !== 'comma') after = t.t === 'digit';\n if (t.t !== 'lit') nonLit = t;\n }\n let lastExp = -1;\n for (let j = toks.length - 1; j >= 0; j--) {\n if (toks[j].t === 'exp') {\n lastExp = j;\n break;\n }\n }\n const firstDigit = toks.findIndex((t) => t.t === 'digit');\n const scientific = firstDigit !== -1 && toks.slice(firstDigit).some((t) => t.t === 'exp');\n for (let i = 0; i < toks.length; i++) {\n const t = toks[i];\n let replace: Tok | undefined;\n switch (t.t) {\n case 'digit':\n seenDigit = true;\n break;\n case 'point':\n if (seenPoint || seenExp) replace = { t: 'lit', s: '.' };\n else seenPoint = true;\n break;\n case 'percent':\n sec.percent += 1;\n break;\n case 'exp':\n if (seenExp || !seenDigit) replace = { t: 'lit', s: literalText(t) };\n else {\n seenExp = true;\n sec.exp = true;\n }\n break;\n case 'comma': {\n const nextDigit = digitAfterCommas[i];\n if (seenDigit && nextDigit && !seenPoint && !seenExp) {\n sec.thousands = true;\n replace = { t: 'lit', s: '' };\n } else if (seenDigit && scientific) {\n // Observed: a scientific section never scales; any other comma after\n // the first digit shows nothing (`#,e+#`, `#e+#,`, `#e+000,00`).\n replace = { t: 'lit', s: '' };\n } else if (seenDigit && !nextDigit) {\n sec.scale += 1;\n replace = { t: 'lit', s: '' };\n } else {\n replace = { t: 'lit', s: ',' };\n }\n break;\n }\n case 'slash': {\n // Literal text may sit between the bar and the denominator (`#/ #`, `# ?=/=?`).\n const nextOk = toks[i + 1]?.t === 'denom' || firstNonLitAfter[i]?.t === 'digit';\n // An exponent kept before this bar or any marker still ahead of it.\n const hasExp = seenExp || lastExp > i;\n if (!fractionSlash && seenDigit && nextOk && !seenPoint && !hasExp) {\n fractionSlash = true;\n sec.fraction = true;\n } else {\n replace = { t: 'lit', s: '/' };\n }\n break;\n }\n case 'denom':\n if (!(toks[i - 1]?.t === 'slash' && sec.fraction)) replace = { t: 'lit', s: t.s };\n break;\n default:\n break;\n }\n if (replace) toks[i] = replace;\n }\n if (sec.fraction) {\n // A decimal point in a fraction section is literal.\n for (let i = 0; i < toks.length; i++) if (toks[i].t === 'point') toks[i] = { t: 'lit', s: '.' };\n }\n // Merge literals (removed commas left empty ones).\n const out: Tok[] = [];\n for (const t of toks) {\n if (t.t === 'lit') pushLit(out, t.s);\n else out.push(t);\n }\n sec.toks = out;\n return sec;\n}\n\n/** Parses a whole format code into its sections. */\nexport function parseSections(code: string): Section[] {\n return splitSections(code).map((s) => analyze(tokenize(s)));\n}\n","// Portions ported from gridcraft/crates/numfmt/src/render.rs and gridcraft/crates/core/src/date.rs\n// (MIT OR Apache-2.0, © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n/** Rendering a value with a parsed section. */\nimport { decimalDigits, fixed, fixedFromDigits, formatGeneralFit } from './decimal';\nimport { isCalendarPart, literalText, type Section, type Tok } from './parse';\n\n/** Last serial that is a valid date in the 1900 date system: 9999-12-31. */\nexport const MAX_DATE_SERIAL = 2_958_465;\n\nconst MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];\nconst WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];\nconst UNIX_1899_12_30 = -25569;\n\n/** Civil date for days since 1970-01-01 (Howard Hinnant's algorithm). */\nfunction civilFromDays(z0: number): { y: number; m: number; d: number } {\n const z = z0 + 719468;\n const era = Math.floor(z / 146097);\n const doe = z - era * 146097;\n const yoe = Math.floor((doe - Math.floor(doe / 1460) + Math.floor(doe / 36524) - Math.floor(doe / 146096)) / 365);\n const doy = doe - (365 * yoe + Math.floor(yoe / 4) - Math.floor(yoe / 100));\n const mp = Math.floor((5 * doy + 2) / 153);\n const d = doy - Math.floor((153 * mp + 2) / 5) + 1;\n const m = mp < 10 ? mp + 3 : mp - 9;\n return { y: yoe + era * 400 + (m <= 2 ? 1 : 0), m, d };\n}\n\n/**\n * Year, month, day and weekday (0 = Sunday) of a whole 1900-system serial.\n * Serial 0 is the fictitious 1900-01-00 and serial 60 the fictitious\n * 1900-02-29; weekdays continue that calendar (serial 1 is a Sunday).\n */\nexport function dateFromSerial(day: number): { year: number; month: number; day: number; weekday: number } {\n const weekday = (((day + 6) % 7) + 7) % 7;\n if (day === 60) return { year: 1900, month: 2, day: 29, weekday };\n if (day === 0) return { year: 1900, month: 1, day: 0, weekday };\n const real = day < 60 ? day + 1 : day;\n const { y, m, d } = civilFromDays(real + UNIX_1899_12_30);\n return { year: y, month: m, day: d, weekday };\n}\n\n/** Text being built plus the first repeat-fill position. */\nexport class Out {\n text = '';\n fill: { char: string; index: number } | undefined;\n /** True when every digit shown is zero (a number section that rounded to zero). */\n zero = false;\n push(s: string): void {\n this.text += s;\n }\n setFill(c: string): void {\n if (!this.fill) this.fill = { char: c, index: this.text.length };\n }\n prepend(s: string): void {\n this.text = s + this.text;\n if (this.fill) this.fill.index += s.length;\n }\n}\n\n/** Renders a text value through a text section (`@` is replaced by the text). */\nexport function renderText(sec: Section, text: string): Out {\n const out = new Out();\n for (const t of sec.toks) {\n if (t.t === 'at') out.push(text);\n else if (t.t === 'fill') out.setFill(t.c);\n else out.push(literalText(t));\n }\n return out;\n}\n\n/** Renders a number through a General section (literals around the General keyword). */\nexport function renderGeneral(sec: Section, v: number, width: number): Out {\n const out = new Out();\n for (const t of sec.toks) {\n if (t.t === 'general') out.push(formatGeneralFit(v, width) ?? '#'.repeat(Math.min(Math.max(width, 1), 11)));\n else if (t.t === 'fill') out.setFill(t.c);\n else out.push(literalText(t));\n }\n return out;\n}\n\nconst padNum = (n: number, w: number) => (w >= 2 ? String(n).padStart(Math.min(w, 20), '0') : String(n));\n\n/** Renders a date/time. `null` when the serial is outside the supported range. */\nexport function renderDate(sec: Section, v: number): Out | null {\n if (!Number.isFinite(v) || v < 0 || v >= MAX_DATE_SERIAL + 1) return null;\n let subDigits = 0;\n for (const t of sec.toks) if (t.t === 'date' && t.d.k === 'subsec') subDigits = Math.max(subDigits, t.d.n);\n subDigits = Math.min(subDigits, 3);\n const unit = 10 ** (3 - subDigits);\n let day = Math.floor(v);\n let ms = Math.round(((v - Math.floor(v)) * 86_400_000) / unit) * unit;\n if (ms >= 86_400_000) {\n ms -= 86_400_000;\n day += 1;\n }\n const totalMs = day * 86_400_000 + ms;\n const needsDate = sec.toks.some((t) => t.t === 'date' && isCalendarPart(t.d));\n if (needsDate && day > MAX_DATE_SERIAL) return null;\n const dt = needsDate ? dateFromSerial(day) : dateFromSerial(0);\n const ampm = sec.toks.some((t) => t.t === 'date' && (t.d.k === 'ampm' || t.d.k === 'ap'));\n const hour = Math.floor(ms / 3_600_000);\n const minute = Math.floor(ms / 60_000) % 60;\n const second = Math.floor(ms / 1000) % 60;\n const milli = ms % 1000;\n const monthName = MONTHS[dt.month - 1] ?? '';\n const dayName = WEEKDAYS[dt.weekday] ?? '';\n const out = new Out();\n for (const t of sec.toks) {\n if (t.t === 'fill') {\n out.setFill(t.c);\n continue;\n }\n if (t.t !== 'date') {\n out.push(literalText(t));\n continue;\n }\n const d = t.d;\n let s: string;\n switch (d.k) {\n case 'year':\n s = d.n <= 2 ? String(((dt.year % 100) + 100) % 100).padStart(2, '0') : String(dt.year).padStart(4, '0');\n break;\n case 'month':\n s = d.n <= 2 ? padNum(dt.month, d.n) : d.n === 3 ? monthName.slice(0, 3) : d.n === 5 ? monthName.slice(0, 1) : monthName;\n break;\n case 'day':\n s = d.n <= 2 ? padNum(dt.day, d.n) : d.n === 3 ? dayName.slice(0, 3) : dayName;\n break;\n case 'hour':\n s = padNum(ampm ? hour % 12 || 12 : hour, d.n);\n break;\n case 'minute':\n s = padNum(minute, d.n);\n break;\n case 'second':\n s = padNum(second, d.n);\n break;\n case 'subsec': {\n s = `.${String(milli).padStart(3, '0')}`.slice(0, 1 + Math.min(d.n, 3));\n if (d.n > 3) s += '0'.repeat(d.n - 3);\n break;\n }\n case 'eh':\n s = padNum(Math.floor(totalMs / 3_600_000), d.n);\n break;\n case 'em':\n s = padNum(Math.floor(totalMs / 60_000), d.n);\n break;\n case 'es':\n s = padNum(Math.floor(totalMs / 1000), d.n);\n break;\n case 'ampm':\n s = hour >= 12 ? 'PM' : 'AM';\n break;\n case 'ap':\n s = hour >= 12 ? d.p : d.a;\n break;\n }\n out.push(s);\n }\n return out;\n}\n\nconst digitChar = (toks: Tok[], i: number): string => {\n const t = toks[i];\n return t?.t === 'digit' ? t.c : '0';\n};\n\n/**\n * Assigns integer digits right-to-left to the placeholders at `idxs`; the\n * leftmost placeholder takes all remaining digits. `forceZero` shows `0` in\n * the units placeholder when there are no digits. A blank `?` pads with a\n * space, and so does the thousands separator it would carry.\n */\nfunction fillInt(toks: Tok[], idxs: number[], digits: string, thousands: boolean, forceZero: boolean, pieces: string[]): void {\n let rem = digits.length;\n let pos = 0;\n const count = idxs.length;\n for (let k = 0; k < count; k++) {\n const ti = idxs[count - 1 - k];\n const leftmost = k + 1 === count;\n const ph = digitChar(toks, ti);\n const rev: string[] = [];\n const pushDigit = (c: string) => {\n if (thousands && pos > 0 && pos % 3 === 0) rev.push(',');\n rev.push(c);\n pos += 1;\n };\n const take = leftmost ? rem : Math.min(rem, 1);\n if (take === 0) {\n if (ph === '0') pushDigit('0');\n else if (forceZero && k === 0) pushDigit('0');\n else if (ph === '?') {\n if (thousands && pos > 0 && pos % 3 === 0) rev.push(' ');\n rev.push(' ');\n pos += 1;\n }\n } else {\n for (let n = 0; n < take; n++) {\n rem -= 1;\n pushDigit(digits[rem] ?? '0');\n }\n }\n pieces[ti] = rev.reverse().join('');\n }\n}\n\nfunction withCommas(digits: string, thousands: boolean): string {\n if (!thousands) return digits;\n const n = digits.length;\n let s = '';\n for (let i = 0; i < n; i++) {\n if (i > 0 && (n - i) % 3 === 0) s += ',';\n s += digits[i];\n }\n return s;\n}\n\n/** Fraction-digit placeholders: trailing zeros show as `0`, nothing (`#`) or a space (`?`). */\nfunction fillFrac(toks: Tok[], idxs: number[], frac: string, pieces: string[]): void {\n let lastSig = -1;\n for (let i = frac.length - 1; i >= 0; i--) {\n if (frac[i] !== '0') {\n lastSig = i;\n break;\n }\n }\n idxs.forEach((ti, i) => {\n const ph = digitChar(toks, ti);\n pieces[ti] = i <= lastSig ? (frac[i] ?? '0') : ph === '0' ? '0' : ph === '?' ? ' ' : '';\n });\n}\n\nfunction digitIndices(toks: Tok[], start: number, end: number): number[] {\n const out: number[] = [];\n for (let i = start; i < end; i++) if (toks[i]?.t === 'digit') out.push(i);\n return out;\n}\n\n/** Renders a non-negative number through a number section. */\nexport function renderNumber(sec: Section, v: number): Out {\n let x = Math.abs(v);\n // Observed: `%` in a scientific section is only a literal (`#%e+#` shows 123456.789 as `1%e+5`).\n if (sec.percent > 0 && !sec.exp) x *= 100 ** Math.min(sec.percent, 20);\n if (sec.scale > 0) x /= 1000 ** Math.min(sec.scale, 110);\n if (!Number.isFinite(x)) x = Number.MAX_VALUE;\n const toks = sec.toks;\n const n = toks.length;\n // Pieces replace the token's own output; `before` is inserted ahead of a\n // token; `lits` replaces a literal's text (fractions blank hidden parts).\n const pieces: string[] = new Array(n).fill('');\n const before: string[] = new Array(n + 1).fill('');\n const lits: Array<string | undefined> = new Array(n);\n let zero = false;\n if (sec.fraction) {\n renderFraction(sec, x, pieces, lits);\n } else {\n const pointIdx = toks.findIndex((t) => t.t === 'point');\n const expIdx = toks.findIndex((t) => t.t === 'exp');\n const point = pointIdx === -1 ? undefined : pointIdx;\n const exp = expIdx === -1 ? undefined : expIdx;\n const intEnd = point ?? exp ?? n;\n const fracEnd = exp ?? n;\n const intIdx = digitIndices(toks, 0, intEnd);\n const fracIdx = point === undefined ? [] : digitIndices(toks, point + 1, fracEnd);\n let intS: string;\n let fracS: string;\n if (exp !== undefined) {\n const expDigitIdx = digitIndices(toks, exp + 1, n);\n const step = Math.max(intIdx.length, 1);\n let e: number;\n if (x === 0) {\n // Observed: zero shows `0` in every integer placeholder, `#` and `?` too.\n e = 0;\n intS = '0'.repeat(intIdx.length);\n fracS = '0'.repeat(fracIdx.length);\n } else {\n const { digits, exp: xe } = decimalDigits(x);\n e = xe - (((xe % step) + step) % step);\n [intS, fracS] = fixedFromDigits(digits, xe - e, fracIdx.length);\n if (intS.length > step) {\n e += step;\n [intS, fracS] = fixedFromDigits(digits, xe - e, fracIdx.length);\n }\n }\n const et = toks[exp];\n const upper = et.t === 'exp' ? et.upper : true;\n const plus = et.t === 'exp' ? et.plus : true;\n if (intS === '' && /^0*$/.test(fracS)) e = 0;\n const head = upper ? 'E' : 'e';\n const sign = e < 0 ? '-' : plus ? '+' : '';\n const expDigits = String(Math.abs(e));\n // The sign sits right before the exponent digits, after any literal that\n // follows the `E`; `?` pads the digits with spaces.\n if (expDigitIdx.length > 0) {\n pieces[exp] = head;\n before[expDigitIdx[0]] = sign;\n fillInt(toks, expDigitIdx, expDigits, false, false, pieces);\n } else {\n pieces[exp] = `${head}${sign}${expDigits}`;\n }\n } else {\n [intS, fracS] = fixed(x, fracIdx.length);\n }\n zero = /^0*$/.test(intS + fracS);\n fillInt(toks, intIdx, intS, sec.thousands, false, pieces);\n const anchor = point ?? exp;\n if (intIdx.length === 0 && intS !== '' && anchor !== undefined) before[anchor] = withCommas(intS, sec.thousands);\n fillFrac(toks, fracIdx, fracS, pieces);\n if (point !== undefined) pieces[point] = '.';\n }\n const out = new Out();\n for (let i = 0; i < n; i++) {\n out.push(before[i]);\n const t = toks[i];\n if (t.t === 'lit') out.push(lits[i] ?? t.s);\n else if (t.t === 'percent') out.push('%');\n else if (t.t === 'fill') out.setFill(t.c);\n else if (t.t === 'at' || t.t === 'general' || t.t === 'date' || t.t === 'comma') continue;\n else out.push(pieces[i]);\n }\n out.zero = zero;\n return out;\n}\n\n/** Best rational approximation of `x` with denominator at most `maxDen` (continued fractions). */\nexport function approximate(x: number, maxDen: number): [number, number] {\n if (Number.isNaN(x) || x <= 0 || !Number.isFinite(x)) return [0, 1];\n if (x >= 1e15) return [Math.round(x), 1];\n let [p0, q0, p1, q1] = [0, 1, 1, 0];\n let r = x;\n for (let iter = 0; iter < 64; iter++) {\n const aF = Math.floor(r);\n if (aF > 1e15) break;\n const q2 = aF * q1 + q0;\n if (q2 > maxDen) break;\n const p2 = aF * p1 + p0;\n [p0, q0, p1, q1] = [p1, q1, p2, q2];\n const frac = r - aF;\n if (frac < 1e-12) break;\n r = 1 / frac;\n }\n if (q1 === 0) return [Math.round(x), 1];\n // Semiconvergent bound.\n const k = Math.floor((maxDen - q0) / q1);\n const pb = p0 + k * p1;\n const qb = q0 + k * q1;\n const errA = Math.abs(p1 / q1 - x);\n const errB = qb > 0 ? Math.abs(pb / qb - x) : Infinity;\n return errB < errA ? [pb, qb] : [p1, q1];\n}\n\n/** Display width of a token in a fraction section, for blanking a hidden part with spaces. */\nfunction shownWidth(t: Tok): number {\n if (t.t === 'lit' || t.t === 'denom') return Array.from(t.s).length;\n return t.t === 'digit' || t.t === 'slash' ? 1 : 0;\n}\n\n/** Replaces the literals, placeholders and bar in `[start, end)` with spaces (`spaces`) or nothing. */\nfunction blankRange(toks: Tok[], start: number, end: number, spaces: boolean, pieces: string[], lits: Array<string | undefined>): void {\n for (let i = start; i < end && i < toks.length; i++) {\n const t = toks[i];\n const s = spaces ? ' '.repeat(shownWidth(t)) : '';\n if (t.t === 'lit') lits[i] = s;\n else if (t.t === 'digit' || t.t === 'slash' || t.t === 'denom') pieces[i] = s;\n }\n}\n\n/**\n * A fraction section is `[whole] [sep] num [lit] / [lit] den`: the numerator\n * is the run of placeholders ending at the last one before the bar, the whole\n * part is every placeholder before it, `sep` the literals between them.\n *\n * Observed Excel behaviour: when the whole part shows no digit its separator\n * disappears (or becomes spaces when the whole part or the numerator has a\n * `?`); when the fraction is zero and the numerator has no `0` placeholder,\n * the separator, numerator, bar and denominator disappear (or become spaces\n * when any part has a `?`) and the whole part shows at least `0`.\n */\nfunction renderFraction(sec: Section, x: number, pieces: string[], lits: Array<string | undefined>): void {\n const toks = sec.toks;\n const isDigit = (i: number) => toks[i]?.t === 'digit';\n const slash = toks.findIndex((t) => t.t === 'slash');\n if (slash === -1) return;\n let numLast = -1;\n for (let i = slash - 1; i >= 0; i--) {\n if (isDigit(i)) {\n numLast = i;\n break;\n }\n }\n if (numLast === -1) return;\n let numStart = numLast;\n while (numStart > 0 && isDigit(numStart - 1)) numStart -= 1;\n const wholeIdx = digitIndices(toks, 0, numStart);\n const numIdx = digitIndices(toks, numStart, numLast + 1);\n const sepStart = wholeIdx.length > 0 ? wholeIdx[wholeIdx.length - 1] + 1 : numStart;\n let fixedDen: number | undefined;\n let denStart: number;\n const afterSlash = toks[slash + 1];\n if (afterSlash?.t === 'denom') {\n const parsed = Number(afterSlash.s);\n fixedDen = Math.min(Math.max(Number.isFinite(parsed) ? parsed : 1e9, 1), 1_000_000_000);\n denStart = slash + 1;\n } else {\n denStart = slash + 1;\n while (toks[denStart]?.t === 'lit') denStart += 1;\n }\n let denEnd = denStart;\n if (fixedDen !== undefined) denEnd += 1;\n else while (isDigit(denEnd)) denEnd += 1;\n const denIdx = digitIndices(toks, denStart, denEnd);\n const hasQ = (idx: number[]) => idx.some((i) => isDigitTok(toks[i], '?'));\n const hasWhole = wholeIdx.length > 0;\n let whole = hasWhole ? Math.floor(x) : 0;\n const f = hasWhole ? x - Math.floor(x) : x;\n let num: number;\n let den: number;\n if (fixedDen !== undefined) {\n num = Math.round(f * fixedDen);\n den = fixedDen;\n } else {\n const digits = Math.min(Math.max(denIdx.length, 1), 7);\n [num, den] = approximate(f, 10 ** digits - 1);\n }\n if (hasWhole && num >= den && den > 0) {\n whole += Math.floor(num / den);\n num %= den;\n }\n const wholeS = whole >= 1 ? fixed(whole, 0)[0] : '';\n const numZeroPh = numIdx.some((i) => isDigitTok(toks[i], '0'));\n if (hasWhole && num === 0 && !numZeroPh) {\n fillInt(toks, wholeIdx, wholeS, sec.thousands, true, pieces);\n const spaces = hasQ(wholeIdx) || hasQ(numIdx) || hasQ(denIdx);\n blankRange(toks, sepStart, denEnd, spaces, pieces, lits);\n return;\n }\n if (hasWhole) {\n // A zero value shows `0` in a `?` or `0` units place, nothing in a `#` one.\n const unitsHash = isDigitTok(toks[wholeIdx[wholeIdx.length - 1]], '#');\n const force = wholeS === '' && num === 0 && !unitsHash;\n fillInt(toks, wholeIdx, wholeS, sec.thousands, force, pieces);\n const wholeShown = wholeIdx.some((i) => /[0-9]/.test(pieces[i] ?? ''));\n if (!wholeShown) {\n const spaces = hasQ(wholeIdx) || hasQ(numIdx);\n blankRange(toks, sepStart, numStart, spaces, pieces, lits);\n }\n }\n fillInt(toks, numIdx, fixed(num, 0)[0] || '0', false, true, pieces);\n pieces[slash] = '/';\n const denS = fixed(den, 0)[0] || '0';\n if (fixedDen !== undefined) {\n pieces[denStart] = denS;\n } else {\n // Denominator digits are zero padded to the `0` placeholders, then left\n // aligned; `?` pads with spaces after them.\n const zeros = denIdx.filter((i) => isDigitTok(toks[i], '0')).length;\n const padded = '0'.repeat(Math.max(zeros - denS.length, 0)) + denS;\n const count = denIdx.length;\n denIdx.forEach((ti, k) => {\n let s = k + 1 === count ? padded.slice(k) : (padded[k] ?? '');\n if (s === '') s = isDigitTok(toks[ti], '?') ? ' ' : isDigitTok(toks[ti], '0') ? '0' : '';\n pieces[ti] = s;\n });\n }\n}\n\nfunction isDigitTok(t: Tok | undefined, c: string): boolean {\n return t?.t === 'digit' && t.c === c;\n}\n","// Portions ported from gridcraft/crates/numfmt/src/lib.rs (MIT OR Apache-2.0,\n// © ArtCraft Team and contributors). See THIRD_PARTY_NOTICES.md.\n/**\n * Excel number formats: format codes (`#,##0.00;[Red](#,##0.00)`, dates,\n * fractions, scientific…), the General format and the `TEXT()` worksheet\n * function. en-US only, 1900 date system.\n *\n * Our engine keeps dates as ISO text (`2026-03-04`, `2026-03-04 18:00:00`) and\n * times as day fractions; ISO text is converted to a 1900-system serial number\n * before formatting.\n */\nimport { parseNumberText, serialFromYmd, daysInMonth, timeFraction } from '../formula/parse-input';\nimport { FORMULA_ERRORS } from '../formula/values';\nimport { formatGeneralFit } from './decimal';\nimport { parseSections, testCond, type NumberFormatColor, type Section } from './parse';\nimport { Out, renderDate, renderGeneral, renderNumber, renderText } from './render';\n\nexport { formatGeneralFit } from './decimal';\nexport { builtinNumberFormat, builtinNumberFormatId } from './builtin';\nexport type { NumberFormatColor } from './parse';\n\n/** Width (in characters) of General output, as in a default-width cell. */\nconst GENERAL_WIDTH = 11;\n/** What Excel paints when a date is out of range. */\nconst OVERFLOW = '########';\n\n/**\n * Longest number format code, in characters, that is parsed (Excel's own\n * limit). A longer code displays its value as General and makes `TEXT`\n * return `#VALUE!`. The cap also bounds work that scales with the code.\n */\nexport const MAX_NUMBER_FORMAT_CODE_CHARS = 255;\n\n/** Best guess at the ribbon's number-format category. */\nexport type NumberFormatKind =\n | 'general'\n | 'number'\n | 'currency'\n | 'accounting'\n | 'date'\n | 'time'\n | 'percentage'\n | 'fraction'\n | 'scientific'\n | 'text'\n /** Zip codes, phone numbers, social security numbers. */\n | 'special'\n | 'custom';\n\n/** A value formatted with a number format, ready to paint. */\nexport interface FormattedNumberValue {\n text: string;\n /** Colour from a `[Red]` / `[ColorN]` tag of the section used. */\n color?: NumberFormatColor;\n /**\n * Repeat-fill character from `*x` and its position (UTF-16 index) in\n * `text`; a renderer may repeat it to fill the cell width.\n */\n fill?: { char: string; index: number };\n /** True for numbers (right-aligned by default); text, booleans and errors are false. */\n numeric: boolean;\n}\n\n/** True when `code` is longer than `MAX_NUMBER_FORMAT_CODE_CHARS` characters. */\nfunction tooLong(code: string): boolean {\n // A code of more than 255 UTF-16 units can still be short in characters; count only when needed.\n return code.length > MAX_NUMBER_FORMAT_CODE_CHARS && Array.from(code).length > MAX_NUMBER_FORMAT_CODE_CHARS;\n}\n\nconst SPECIAL_CODES = ['00000', '00000-0000', '[<=9999999]###-####;(###) ###-####', '000-00-0000'];\n\n/** A parsed number format code. Parsing never fails: malformed parts become literal text. */\nexport class NumberFormat {\n readonly code: string;\n private readonly general: boolean;\n private readonly sections: Section[];\n /** Sections that apply to numbers (a trailing text section is excluded). */\n private readonly numSections: number;\n /** Index of the section that applies to text, if any. */\n private readonly textSection: number | undefined;\n\n private constructor(code: string, uncapped: boolean) {\n this.code = code;\n const trimmed = code.trim();\n // `checkbox` marks a checkbox cell: any value displays as General.\n if (trimmed === '' || trimmed.toLowerCase() === 'general' || trimmed === 'checkbox' || (!uncapped && tooLong(code))) {\n this.general = true;\n this.sections = [];\n this.numSections = 0;\n this.textSection = undefined;\n return;\n }\n this.general = false;\n this.sections = parseSections(code);\n const len = this.sections.length;\n const lastIsText = this.sections[len - 1]?.kind === 'text';\n this.textSection = len >= 4 ? 3 : lastIsText ? len - 1 : undefined;\n this.numSections = len >= 4 ? 3 : lastIsText ? len - 1 : len;\n }\n\n /**\n * Parses a format code. Never throws: unknown or malformed parts degrade to\n * literal text, and a code longer than 255 characters degrades to General.\n */\n static parse(code: string): NumberFormat {\n return new NumberFormat(code, false);\n }\n\n /** `parse` without the 255-character cap (for tests that reach renderer edge cases). */\n static parseUncapped(code: string): NumberFormat {\n return new NumberFormat(code, true);\n }\n\n /** Like `parse`, but remembers recently parsed codes (bounded), so formatting many values parses a code once. */\n static parseCached(code: string): NumberFormat {\n if (code.length > CACHE_MAX_CODE_LENGTH) return NumberFormat.parse(code);\n let fmt = parseCache.get(code);\n if (!fmt) {\n fmt = NumberFormat.parse(code);\n if (parseCache.size >= CACHE_CAPACITY) parseCache.clear();\n parseCache.set(code, fmt);\n }\n return fmt;\n }\n\n static general(): NumberFormat {\n return NumberFormat.parse('General');\n }\n\n isGeneral(): boolean {\n return this.general;\n }\n\n private numberSections(): Section[] {\n return this.sections.slice(0, this.numSections);\n }\n\n /** True when the code contains date or time tokens. */\n isDate(): boolean {\n return this.numberSections().some((s) => s.kind === 'date');\n }\n\n isPercent(): boolean {\n return this.numberSections().some((s) => s.kind === 'number' && s.percent > 0);\n }\n\n /** True for a pure text format such as `@`. */\n isText(): boolean {\n return this.numSections === 0 && this.textSection !== undefined;\n }\n\n /** Best guess at the format category. */\n kind(): NumberFormatKind {\n if (this.general) return 'general';\n if (this.isText()) return 'text';\n if (SPECIAL_CODES.includes(this.code)) return 'special';\n const s = this.sections[0];\n if (!s) return 'custom';\n if (s.kind === 'general') return 'general';\n if (s.kind === 'text') return 'text';\n if (s.kind === 'date') {\n const calendar = s.toks.some((t) => t.t === 'date' && (t.d.k === 'year' || t.d.k === 'month' || t.d.k === 'day'));\n return calendar ? 'date' : 'time';\n }\n if (s.fraction) return 'fraction';\n if (s.exp) return 'scientific';\n if (s.percent > 0) return 'percentage';\n if (s.fill && s.hasDigits) return 'accounting';\n if (s.currency && s.hasDigits) return 'currency';\n if (s.hasDigits) return 'number';\n return 'custom';\n }\n\n /**\n * Picks the number section for `v`, and whether the minus sign is dropped\n * (a negative section shows the absolute value). Null means General.\n */\n private select(v: number): { sec: Section; dropSign: boolean } | null {\n const secs = this.numberSections();\n const k = secs.length;\n const s0 = secs[0];\n if (!s0) return null;\n const c0 = s0.cond;\n const c1 = secs[1]?.cond;\n if (c0 || c1) {\n let idx: number;\n if (c0 && testCond(c0, v)) idx = 0;\n else if (c1 && testCond(c1, v)) idx = 1;\n else if (c0 && c1) idx = 2;\n else if (!c0) idx = 0;\n else idx = 1;\n // Excel never adds a minus sign in a multi-section conditional format.\n const sec = secs[idx];\n return sec ? { sec, dropSign: k >= 2 } : null;\n }\n if (v < 0 && k >= 2) return { sec: secs[1], dropSign: true };\n if (v === 0 && k >= 3) return { sec: secs[2], dropSign: false };\n return { sec: s0, dropSign: false };\n }\n\n /**\n * Formats a number. `overflow` is true when the value can't be shown (a\n * date out of range, painted as `########`).\n */\n formatNumber(n: number): FormattedNumberValue & { overflow: boolean } {\n const general = (): FormattedNumberValue & { overflow: boolean } => ({\n text: formatGeneralFit(n, GENERAL_WIDTH) ?? OVERFLOW,\n numeric: true,\n overflow: false,\n });\n if (!Number.isFinite(n)) return { text: '#NUM!', numeric: false, overflow: true };\n if (this.general) return general();\n const picked = this.select(n);\n if (!picked) return general();\n const { sec, dropSign } = picked;\n const minus = n < 0 && !dropSign;\n let out: Out | null;\n switch (sec.kind) {\n case 'text':\n out = renderText(sec, formatGeneralFit(n, GENERAL_WIDTH) ?? '');\n break;\n case 'general':\n out = renderGeneral(sec, Math.abs(n), minus ? GENERAL_WIDTH - 1 : GENERAL_WIDTH);\n break;\n case 'date':\n out = minus ? null : renderDate(sec, Math.abs(n));\n break;\n default:\n out = renderNumber(sec, Math.abs(n));\n }\n if (!out) return withColor({ text: OVERFLOW, numeric: true, overflow: true }, sec.color);\n // Excel drops the minus sign when the section shows a negative number as all zeros.\n if (minus && sec.kind !== 'text' && !out.zero) out.prepend('-');\n return withColor({ text: out.text, numeric: true, overflow: false, ...(out.fill ? { fill: out.fill } : {}) }, sec.color);\n }\n\n /** Formats text through the text section (`@`), or returns it unchanged. */\n formatText(text: string): FormattedNumberValue {\n const sec = this.textSection === undefined ? undefined : this.sections[this.textSection];\n if (!sec) return { text, numeric: false };\n const out = renderText(sec, text);\n return withColor({ text: out.text, numeric: false, ...(out.fill ? { fill: out.fill } : {}) }, sec.color);\n }\n\n /**\n * Formats a cell value: numbers through the number sections, ISO date text\n * (`2026-03-04`, `2026-03-04 18:00:00`) and `Date`s as serial numbers, other\n * text through the text section; booleans show TRUE/FALSE and error values\n * themselves.\n */\n format(value: unknown): FormattedNumberValue {\n if (value === '' || value === null || value === undefined) return { text: '', numeric: false };\n if (typeof value === 'boolean') return { text: value ? 'TRUE' : 'FALSE', numeric: false };\n if (typeof value === 'number') {\n const { overflow: _overflow, ...rest } = this.formatNumber(value);\n return rest;\n }\n if (typeof value === 'string' && FORMULA_ERRORS.has(value)) return { text: value, numeric: false };\n const serial = dateValueToSerial(value);\n if (serial !== null) {\n const { overflow: _overflow, ...rest } = this.formatNumber(serial);\n return rest;\n }\n return this.formatText(String(value));\n }\n}\n\nfunction withColor<T extends FormattedNumberValue>(value: T, color: NumberFormatColor | undefined): T {\n if (color) value.color = color;\n return value;\n}\n\n// --- Parse cache ---\n\n/** Most codes kept. A full cache is emptied, so memory stays bounded. */\nconst CACHE_CAPACITY = 64;\n/** Longer codes are parsed every time and never stored. */\nconst CACHE_MAX_CODE_LENGTH = 256;\nconst parseCache = new Map<string, NumberFormat>();\n\n/** Empties the parse cache used by `NumberFormat.parseCached` (mainly for tests). */\nexport function clearNumberFormatCache(): void {\n parseCache.clear();\n}\n\n// --- Dates ---\n\nconst ISO_DATE_TIME = /^(\\d{4})-(\\d{1,2})-(\\d{1,2})(?:[ T](\\d{1,2}):(\\d{2})(?::(\\d{2}(?:\\.\\d+)?))?)?$/;\n\n/**\n * 1900-system serial number of a date value: ISO date text (with an optional\n * time) or a `Date` (its local date and time). Null for anything else.\n */\nexport function dateValueToSerial(value: unknown): number | null {\n if (value instanceof Date) {\n if (isNaN(value.getTime())) return null;\n const serial = serialFromYmd(value.getFullYear(), value.getMonth() + 1, value.getDate());\n return serial === null ? null : serial + timeFraction(value.getHours(), value.getMinutes(), value.getSeconds() + value.getMilliseconds() / 1000);\n }\n if (typeof value !== 'string' || value.length < 8 || value.length > 30) return null;\n const m = ISO_DATE_TIME.exec(value.trim());\n if (!m) return null;\n const [y, mo, d] = [Number(m[1]), Number(m[2]), Number(m[3])];\n if (mo < 1 || mo > 12 || d < 1 || d > daysInMonth(y, mo)) return null;\n const serial = serialFromYmd(y, mo, d);\n if (serial === null) return null;\n if (m[4] === undefined) return serial;\n const [hh, mm, ss] = [Number(m[4]), Number(m[5]), Number(m[6] ?? 0)];\n if (hh > 23 || mm > 59 || ss >= 60) return null;\n return serial + timeFraction(hh, mm, ss);\n}\n\n// --- Public helpers ---\n\n/** Formats a cell value with a format code (see `NumberFormat.format`). */\nexport function formatNumberFormatValue(value: unknown, code: string): FormattedNumberValue {\n return NumberFormat.parseCached(code).format(value);\n}\n\n/**\n * The `TEXT()` worksheet function: formats a value with a format code.\n * Blanks count as 0; numeric and date text (`\"5\"`, `\"3/4/2026\"`,\n * `\"2026-03-04\"`) is converted to a number first; other text and booleans\n * (as TRUE/FALSE) go through the text section, or are returned as is.\n * Errors: the value's own error, `#VALUE!` for a code over 255 characters or\n * a date out of range, `#NUM!` for a non-finite number.\n */\nexport function formatTextFunction(value: unknown, code: string): { text: string } | { error: string } {\n if (typeof value === 'string' && FORMULA_ERRORS.has(value)) return { error: value };\n if (code === '') return { text: '' };\n if (tooLong(code)) return { error: '#VALUE!' };\n const fmt = NumberFormat.parseCached(code);\n let n: number;\n if (value === '' || value === null || value === undefined) n = 0;\n else if (typeof value === 'number') n = value;\n else if (typeof value === 'boolean') return { text: fmt.formatText(value ? 'TRUE' : 'FALSE').text };\n else {\n const serial = dateValueToSerial(value);\n const parsed = serial ?? (typeof value === 'string' ? parseNumberText(value) : null);\n if (parsed === null) return { text: fmt.formatText(String(value)).text };\n n = parsed;\n }\n if (!Number.isFinite(n)) return { error: '#NUM!' };\n const f = fmt.formatNumber(n);\n return f.overflow ? { error: '#VALUE!' } : { text: f.text };\n}\n\n/** Excel's default 56-colour palette, for `[Color1]`…`[Color56]`. */\nconst INDEXED_COLORS = [\n '#000000', '#FFFFFF', '#FF0000', '#00FF00', '#0000FF', '#FFFF00', '#FF00FF', '#00FFFF',\n '#800000', '#008000', '#000080', '#808000', '#800080', '#008080', '#C0C0C0', '#808080',\n '#9999FF', '#993366', '#FFFFCC', '#CCFFFF', '#660066', '#FF8080', '#0066CC', '#CCCCFF',\n '#000080', '#FF00FF', '#FFFF00', '#00FFFF', '#800080', '#800000', '#008080', '#0000FF',\n '#00CCFF', '#CCFFFF', '#CCFFCC', '#FFFF99', '#99CCFF', '#FF99CC', '#CC99FF', '#FFCC99',\n '#3366FF', '#33CCCC', '#99CC00', '#FFCC00', '#FF9900', '#FF6600', '#666699', '#969696',\n '#003366', '#339966', '#003300', '#333300', '#993300', '#993366', '#333399', '#333333',\n];\n\nconst NAMED_CSS: Record<string, string> = {\n black: '#000000',\n blue: '#0000FF',\n cyan: '#00FFFF',\n green: '#00FF00',\n magenta: '#FF00FF',\n red: '#FF0000',\n white: '#FFFFFF',\n yellow: '#FFFF00',\n};\n\n/** CSS colour for a format colour (`red` → `#FF0000`, `color10` → `#008000`). */\nexport function numberFormatColorToCss(color: NumberFormatColor): string {\n const named = NAMED_CSS[color];\n if (named) return named;\n return INDEXED_COLORS[Number(color.slice(5)) - 1] ?? '#000000';\n}\n"]}