@motionscript/code 0.0.0-stage → 0.1.0-alpha.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 (103) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/LICENSE +201 -0
  3. package/README.md +76 -3
  4. package/dist/browser/chunks/chunk-HET2RCSD.js +2 -0
  5. package/dist/browser/chunks/chunk-HET2RCSD.js.map +7 -0
  6. package/dist/browser/chunks/chunk-SYZUZB3M.js +2 -0
  7. package/dist/browser/chunks/chunk-SYZUZB3M.js.map +7 -0
  8. package/dist/browser/chunks/dist-3F5TQHM6.js +2 -0
  9. package/dist/browser/chunks/dist-3F5TQHM6.js.map +7 -0
  10. package/dist/browser/chunks/dist-3SIR23P4.js +2 -0
  11. package/dist/browser/chunks/dist-3SIR23P4.js.map +7 -0
  12. package/dist/browser/chunks/dist-AQRNACF3.js +2 -0
  13. package/dist/browser/chunks/dist-AQRNACF3.js.map +7 -0
  14. package/dist/browser/chunks/dist-B46VH2YP.js +2 -0
  15. package/dist/browser/chunks/dist-B46VH2YP.js.map +7 -0
  16. package/dist/browser/chunks/dist-FH6BHJ6A.js +7 -0
  17. package/dist/browser/chunks/dist-FH6BHJ6A.js.map +7 -0
  18. package/dist/browser/chunks/dist-FQDUBZKU.js +2 -0
  19. package/dist/browser/chunks/dist-FQDUBZKU.js.map +7 -0
  20. package/dist/browser/chunks/dist-GPNEVCJI.js +2 -0
  21. package/dist/browser/chunks/dist-GPNEVCJI.js.map +7 -0
  22. package/dist/browser/chunks/dist-LRSXSDHT.js +2 -0
  23. package/dist/browser/chunks/dist-LRSXSDHT.js.map +7 -0
  24. package/dist/browser/chunks/dist-N4CNBNHL.js +2 -0
  25. package/dist/browser/chunks/dist-N4CNBNHL.js.map +7 -0
  26. package/dist/browser/chunks/dist-OXJDTPQ6.js +2 -0
  27. package/dist/browser/chunks/dist-OXJDTPQ6.js.map +7 -0
  28. package/dist/browser/chunks/dist-SH7H4MRK.js +2 -0
  29. package/dist/browser/chunks/dist-SH7H4MRK.js.map +7 -0
  30. package/dist/browser/chunks/dist-UG3NTKWL.js +2 -0
  31. package/dist/browser/chunks/dist-UG3NTKWL.js.map +7 -0
  32. package/dist/browser/chunks/dist-W5ZXIMV7.js +2 -0
  33. package/dist/browser/chunks/dist-W5ZXIMV7.js.map +7 -0
  34. package/dist/browser/index.js +8 -0
  35. package/dist/browser/index.js.map +7 -0
  36. package/dist/browser/manifest.json +11 -0
  37. package/dist/code-range.d.ts +69 -0
  38. package/dist/code-range.d.ts.map +1 -0
  39. package/dist/code-range.js +124 -0
  40. package/dist/code-range.js.map +1 -0
  41. package/dist/diff.d.ts +42 -0
  42. package/dist/diff.d.ts.map +1 -0
  43. package/dist/diff.js +179 -0
  44. package/dist/diff.js.map +1 -0
  45. package/dist/engine.d.ts +15 -0
  46. package/dist/engine.d.ts.map +1 -0
  47. package/dist/engine.js +17 -0
  48. package/dist/engine.js.map +1 -0
  49. package/dist/highlight.d.ts +43 -0
  50. package/dist/highlight.d.ts.map +1 -0
  51. package/dist/highlight.js +243 -0
  52. package/dist/highlight.js.map +1 -0
  53. package/dist/index.d.ts +4 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +4 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/layout.d.ts +77 -0
  58. package/dist/layout.d.ts.map +1 -0
  59. package/dist/layout.js +74 -0
  60. package/dist/layout.js.map +1 -0
  61. package/dist/measure-cache.d.ts +38 -0
  62. package/dist/measure-cache.d.ts.map +1 -0
  63. package/dist/measure-cache.js +0 -0
  64. package/dist/measure-cache.js.map +1 -0
  65. package/dist/node.d.ts +293 -0
  66. package/dist/node.d.ts.map +1 -0
  67. package/dist/node.js +733 -0
  68. package/dist/node.js.map +1 -0
  69. package/dist/nodes.d.ts +18 -0
  70. package/dist/nodes.d.ts.map +1 -0
  71. package/dist/nodes.js +18 -0
  72. package/dist/nodes.js.map +1 -0
  73. package/dist/render.d.ts +28 -0
  74. package/dist/render.d.ts.map +1 -0
  75. package/dist/render.js +223 -0
  76. package/dist/render.js.map +1 -0
  77. package/dist/style.d.ts +88 -0
  78. package/dist/style.d.ts.map +1 -0
  79. package/dist/style.js +203 -0
  80. package/dist/style.js.map +1 -0
  81. package/dist/tokens.d.ts +24 -0
  82. package/dist/tokens.d.ts.map +1 -0
  83. package/dist/tokens.js +50 -0
  84. package/dist/tokens.js.map +1 -0
  85. package/dist/transitions.d.ts +115 -0
  86. package/dist/transitions.d.ts.map +1 -0
  87. package/dist/transitions.js +85 -0
  88. package/dist/transitions.js.map +1 -0
  89. package/package.json +82 -3
  90. package/registry.json +6 -0
  91. package/src/code-range.ts +156 -0
  92. package/src/diff.ts +216 -0
  93. package/src/engine.ts +21 -0
  94. package/src/highlight.ts +291 -0
  95. package/src/index.ts +3 -0
  96. package/src/layout.ts +157 -0
  97. package/src/measure-cache.ts +0 -0
  98. package/src/node.ts +834 -0
  99. package/src/nodes.ts +18 -0
  100. package/src/render.ts +247 -0
  101. package/src/style.ts +251 -0
  102. package/src/tokens.ts +75 -0
  103. package/src/transitions.ts +184 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/node.ts", "../../src/transitions.ts", "../../src/layout.ts", "../../src/render.ts", "../../src/diff.ts", "../../src/style.ts", "../../src/highlight.ts", "../../src/tokens.ts", "../../src/measure-cache.ts", "../../src/code-range.ts", "../../src/nodes.ts"],
4
+ "sourcesContent": ["import {\n node,\n RenderContext2D, RenderPass2D, Clip, EasingFunction, NodeConfig, Size2D, SizeConstraints,\n Node2D, Node2DProps, MeasureContext2D, InsetsResolved, InsetsProps,\n property, insetsOps, lerpNumber,\n command, driveCommand, type AssetScope, type Command, type CommandArgs, type Node, type TweenStepper, type Vector2,\n} from \"@motionscript/core\";\nimport {\n CodeRange,\n rangeToCharOffsets,\n charOffsetsToRange,\n TokenAdvanceCache,\n IdLine,\n tokenizeCodeToIdLines,\n defaultCodeDiff,\n type CodeDiffStrategy,\n CodeLayout,\n CodeMetrics,\n layoutCode,\n metricsSignature,\n AnimToken,\n CodeTransition,\n StructuralTransition,\n makeAnim,\n defaultCodePhases,\n type CodePhaseStrategy,\n resolveTokenStates,\n moveProgress,\n canHighlight,\n ensureHighlighter,\n CodeTheme,\n DefaultHighlightStyle,\n drawCode,\n} from \"./engine\";\n\n/**\n * A listing and nothing else — no paint slots, and no box behind the tokens.\n *\n * It did carry a `fill`/`overlay`/`stroke`/`shadow` set and the corner radius\n * that shaped them, so a block could draw its own panel. That was one node doing\n * two jobs, and the second one is a `Rect`'s: a listing set on a card is a `Code`\n * inside a hugging `Rect`, which tracks the block's size for the same reason\n * every other hugging container tracks its child's. What is left here is the\n * listing — its source, how it is coloured, the face it is set in, and the space\n * it keeps around itself.\n */\nexport interface CodeProps extends Node2DProps {\n code: string;\n language: string;\n fontSize: number;\n fontFamily: string;\n /** A built-in/registered theme name (e.g. `'github-dark'`), or a {@link CodeHighlightStyle} object. */\n theme: CodeTheme;\n lineHeight: number;\n letterSpacing: number;\n showLineNumbers: boolean;\n lineNumberGap: number;\n padding: InsetsProps;\n}\n\n/**\n * Construction-time-only options: pluggable engine behavior, set once when\n * the node is created. Deliberately *not* part of {@link CodeProps} — every\n * `@property` field accepts either a value or a zero-arg reactive binding\n * (`value | (() => value)`, see `PropInputs`), and the node's own reactive\n * write path (`_writeProp`) treats any function value as the latter. A\n * strategy *is* a function, so it can't be told apart from a binding that\n * computes one — it has to stay outside the reactive prop system entirely.\n */\nexport interface CodeStrategies {\n /** Decides which token became which across an edit. Defaults to `defaultCodeDiff`. */\n diffStrategy?: CodeDiffStrategy;\n /** Decides when (in normalized transition time) each part of an edit happens. Defaults to `defaultCodePhases`. */\n phaseStrategy?: CodePhaseStrategy;\n}\n\n@node({\n key: \"code\",\n parentKey: \"node\",\n forkable: true,\n layout: {\n defaultWidthMode: \"hug\",\n defaultHeightMode: \"hug\",\n acceptsChildren: false,\n },\n})\nexport class Code extends Node2D<CodeProps> {\n\n @property({ default: \"\" }) declare readonly code: string;\n @property({ default: \"typescript\" }) declare readonly language: string;\n @property({ default: \"Fira Mono\" }) declare readonly fontFamily: string;\n @property({ default: DefaultHighlightStyle.name }) declare readonly theme: CodeTheme;\n @property({ default: 16 }) declare readonly fontSize: number;\n @property({ default: 1.6 }) declare readonly lineHeight: number;\n // Extra horizontal space added after every glyph (in px), like CSS\n // letter-spacing. Folded into the advance a token is measured at, so\n // measurement and drawing stay in lockstep.\n @property({ default: 1.1 }) declare readonly letterSpacing: number;\n @property({ default: false }) declare readonly showLineNumbers: boolean;\n // Horizontal gap between the line-number column and the code text, expressed\n // in space-widths (so it scales with fontSize). Only applies when\n // showLineNumbers is on.\n @property({ default: 2 }) declare readonly lineNumberGap: number;\n @property({ default: 0, mapper: insetsOps.resolve, tween: insetsOps.lerp }) declare readonly padding: InsetsResolved;\n\n /** The settled structure. During an edit, the structure the edit lands on. */\n private tokenLines: IdLine[] = [];\n private tokenized: boolean = false;\n\n private transitions: CodeTransition[] = [];\n\n // Caches expensive scope.measureText() calls; cleared when the font\n // signature (fontSize|fontFamily) changes. See TokenAdvanceCache.\n private advanceCache = new TokenAdvanceCache();\n\n // Layout cache for *settled* frames. Bumped whenever tokenLines is\n // reassigned, so a cache hit only ever happens on identical content. Frames\n // inside a structural edit are served from that edit's own two cached\n // layouts instead (see StructuralTransition).\n private structureVersion = 0;\n private layoutCacheKey: string | null = null;\n private layoutCache: CodeLayout | null = null;\n\n // Persistent dim state set by highlight() — applied during render to all\n // tokens whose id is NOT in the highlight set. null means \"not highlighting\".\n private highlightDimOpacity: number | null = null;\n private highlightedIds: Set<number> = new Set();\n\n /**\n * Which token became which across an edit, and when each part of that\n * edit happens. Construction-time-only (see {@link CodeStrategies}) —\n * resolved once here rather than left as `?? defaultX` at every call site.\n */\n private readonly diffStrategy: CodeDiffStrategy;\n private readonly phaseStrategy: CodePhaseStrategy;\n\n constructor(props: NodeConfig<Code, CodeProps> & CodeStrategies) {\n super(props);\n this.diffStrategy = props.diffStrategy ?? defaultCodeDiff;\n this.phaseStrategy = props.phaseStrategy ?? defaultCodePhases;\n this.applyProp(\"width\", props.width ?? \"hug\");\n this.applyProp(\"height\", props.height ?? \"hug\");\n\n // Best-effort, for a highlighter constructed outside the asset pipeline\n // entirely (a bare `new Code(...)` in a test or a headless measure). On\n // the timeline the authoritative load is the one `declareAssets` declares,\n // which `AssetManager.loadAt` awaits *before* this node is laid out — so\n // there the tokens are right the first time rather than upgrading later.\n // (Theme is a synchronous color style; only the language parser loads.)\n ensureHighlighter(undefined, [this.language]).catch(() => { });\n this.tokenize();\n }\n\n set(props: { [K in keyof CodeProps]?: CodeProps[K] | (() => CodeProps[K]) }): void {\n super.set(props);\n if (props.code !== undefined || props.language !== undefined || props.theme !== undefined) {\n this.tokenized = false;\n }\n }\n\n private tokenize(): void {\n this.setLines(tokenizeCodeToIdLines(this.code, this.language, this.theme));\n // Only consider ourselves tokenized once we actually highlighted; while\n // the language is still loading we keep retrying on each render.\n this.tokenized = canHighlight(this.language, this.theme);\n }\n\n /**\n * Swap in a new settled structure, invalidating the layout cache.\n *\n * The one place `tokenLines` is written, because the cache key is a version\n * counter rather than a hash of the content — a write that skipped the bump\n * would keep serving the previous frame's geometry for the new listing.\n */\n private setLines(lines: IdLine[]): void {\n if (this.tokenLines === lines) return;\n this.tokenLines = lines;\n this.structureVersion++;\n this.layoutCache = null;\n this.layoutCacheKey = null;\n }\n\n /**\n * The monospaced face this block measures and draws with, and the syntax\n * grammar it tokenizes with.\n *\n * Both belong to *layout*: token x positions are measured against the face,\n * and how many tokens there are depends on the grammar. Declared here, the\n * grammar goes on the timeline as an ordinary asset: `AssetManager.loadAt`\n * waits for it before the frame is laid out, so the first measurement is the\n * right one. Nothing is freed on eviction — parsers are cheap to keep\n * resident. The loader touches no node: `renderContent` re-tokenizes once\n * the grammar is there.\n */\n override declareAssets(assets: AssetScope): void {\n super.declareAssets(assets);\n assets.font(this.fontFamily);\n assets.loader(`shiki:lang:${this.language}`, grammarLoader(this.language), { gate: \"layout\" });\n }\n\n // ── Geometry ────────────────────────────────────────────────────────────\n\n private metrics(): CodeMetrics {\n return {\n fontSize: this.fontSize,\n fontFamily: this.fontFamily,\n lineHeight: this.lineHeight,\n letterSpacing: this.letterSpacing,\n padding: this.padding,\n showLineNumbers: this.showLineNumbers,\n lineNumberGap: this.lineNumberGap,\n };\n }\n\n /** The structural edit currently in flight, if any. */\n private activeEdit(): StructuralTransition | null {\n for (let i = this.transitions.length - 1; i >= 0; i--) {\n const tr = this.transitions[i];\n if (tr.kind === \"structural\") return tr;\n }\n return null;\n }\n\n private settledLayout(m: CodeMetrics, scope: MeasureContext2D | RenderContext2D): CodeLayout {\n const key = `${this.structureVersion}|${metricsSignature(m)}|${this.advanceCache.signature(m.fontSize, m.fontFamily)}`;\n if (this.layoutCache && this.layoutCacheKey === key) return this.layoutCache;\n const layout = layoutCode(this.tokenLines, m, this.advanceCache, scope);\n this.layoutCache = layout;\n this.layoutCacheKey = key;\n return layout;\n }\n\n /**\n * The one or two layouts this frame is drawn from.\n *\n * A settled frame has one. A frame inside an edit has two — where every\n * token was, and where it is going — and the frame is a point between them.\n * That is the whole reason an insert no longer piles its glyphs at the left\n * margin: a token's destination is *computed*, not approximated by collapsing\n * the advance of everything around it.\n *\n * Both endpoints are fixed for the edit's duration, so they are built once\n * and cached on the transition rather than rebuilt per frame.\n */\n private frameLayout(scope: MeasureContext2D | RenderContext2D): {\n from: CodeLayout;\n to: CodeLayout;\n edit: StructuralTransition | null;\n } {\n const m = this.metrics();\n const edit = this.activeEdit();\n if (!edit) {\n const layout = this.settledLayout(m, scope);\n return { from: layout, to: layout, edit: null };\n }\n const key = `${metricsSignature(m)}|${this.advanceCache.signature(m.fontSize, m.fontFamily)}`;\n if (edit.layoutKey !== key || !edit.fromLayout || !edit.toLayout) {\n edit.fromLayout = layoutCode(edit.from, m, this.advanceCache, scope);\n edit.toLayout = layoutCode(edit.to, m, this.advanceCache, scope);\n edit.layoutKey = key;\n }\n return { from: edit.fromLayout, to: edit.toLayout, edit };\n }\n\n override measure(constraints: SizeConstraints, scope: MeasureContext2D): Partial<Size2D> {\n this.advanceCache.sync(this.advanceCache.signature(this.fontSize, this.fontFamily));\n const wm = this.width;\n const hm = this.height;\n\n const { from, to, edit } = this.frameLayout(scope);\n const t = moveProgress(edit);\n const innerW = edit ? lerpNumber(from.innerW, to.innerW, t) : to.innerW;\n const innerH = edit ? lerpNumber(from.innerH, to.innerH, t) : to.innerH;\n\n const resolvedW = typeof wm === \"number\"\n ? wm\n : wm === \"hug\"\n ? innerW + this.padding.left + this.padding.right\n : constraints.maxWidth ?? 0;\n\n const resolvedH = typeof hm === \"number\"\n ? hm\n : hm === \"hug\"\n ? innerH + this.padding.top + this.padding.bottom\n : constraints.maxHeight ?? 0;\n\n return { width: resolvedW, height: resolvedH };\n }\n\n protected override renderContent(ctx: RenderPass2D): void {\n // Refuse the ambient `<DefaultTextStyle>` / theme typography defaults for\n // everything drawn inside this node.\n //\n // Those defaults describe the *document's* prose — a display face, a\n // heading weight, a paragraph line-height — and a code block is not prose.\n // Its own props are the vocabulary: `fontFamily` defaults to a monospaced\n // face because column alignment depends on it, `letterSpacing` and\n // `lineHeight` are tuned against that face, and every token is laid out at\n // an x measured from those exact values. A scene-wide serif family or a\n // 700 weight arriving through the draw scope would shape glyphs the\n // geometry was never measured for, and the block would come apart\n // column-by-column rather than merely look different.\n ctx.pushTextStyle(null);\n try {\n // Keep retrying until the language+theme have actually loaded, so a frame\n // that rendered as plain text upgrades to full highlighting the moment the\n // asset loader resolves. Never mid-edit: re-tokenizing mints fresh ids,\n // and an in-flight transition is keyed by the ones it captured.\n if (!this.tokenized && !this.activeEdit() && canHighlight(this.language, this.theme)) {\n this.tokenize();\n }\n super.renderContent(ctx);\n } finally {\n ctx.popTextStyle();\n }\n }\n\n protected override renderSelf(ctx: RenderContext2D): void {\n this.drawSelf(ctx);\n }\n\n /** A listing holds a listing — `append`/`insert`/`erase` are how it grows. */\n protected override acceptsChild(_child: Node): boolean {\n return false;\n }\n\n /**\n * The node's laid-out rect as a clip outline, so `clip` and any backdrop\n * effect are confined to the block rather than to nothing — a listing set on\n * a card is exactly where a backdrop blur is asked for.\n */\n protected override clipSelf(): Clip {\n return new Clip().rect({\n width: this.layoutBounds.width,\n height: this.layoutBounds.height,\n });\n }\n\n /**\n * A listing is grabbed by its **text**, line by ragged line, rather than by\n * the rectangle the lines were laid out in.\n *\n * The default narrows a node's grab region to the outline it declares, and\n * this node's outline is a box it no longer paints anything into. Code is\n * mostly whitespace: a ten-line snippet with one long line is a rectangle\n * that is largely empty, and in an editor every one of those empty pixels\n * swallows a click meant for whatever sits behind or beneath it. Short of the\n * text itself there is nothing there to have clicked.\n *\n * So the region is the union of the lines' own extents — each a line-height\n * slot as wide as that line's ink — plus the gutter where line numbers are\n * showing, since those are drawn glyphs too.\n *\n * Answered from the **cached** layout, which is what the last frame drew\n * from, because a hit test arrives with no measurement scope and text cannot\n * be measured without one. Where there is no usable cache — before the first\n * frame, or mid-edit, when the geometry is an interpolation of two structures\n * held on the transition — the box is the honest answer rather than a\n * silhouette measured against stale metrics.\n */\n protected override hitTestSelf(local: Vector2, tolerance: number): boolean {\n const layout = this.hitLayout();\n if (!layout) return super.hitTestSelf(local, tolerance);\n\n // Never *wider* than the box: `hug` sizes to the ink, but a fixed width\n // narrower than the longest line would otherwise be hit outside itself.\n if (!super.hitTestSelf(local, tolerance)) return false;\n\n const half = (this.fontSize * this.lineHeight) / 2;\n // Line numbers sit in the gutter, to the left of every line's own start,\n // so a numbered blank line is still grabbable — by its number.\n const gutter = this.showLineNumbers ? layout.gutter : 0;\n const left = layout.startX - gutter - tolerance;\n for (let i = 0; i < layout.lineY.length; i++) {\n const width = (layout.lineW[i] ?? 0) + gutter;\n if (width <= 0) continue;\n if (Math.abs(local.y - layout.lineY[i]) > half + tolerance) continue;\n if (local.x >= left && local.x <= layout.startX + (layout.lineW[i] ?? 0) + tolerance) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * The layout a hit test may be answered from: the settled cache, and only\n * while it still matches the node's current metrics.\n *\n * The key is the one {@link settledLayout} writes minus its scope-dependent\n * half — every input to it (`metricsSignature`, the advance cache's own\n * signature) is readable without a measurer, which is precisely what makes\n * the check possible here.\n */\n private hitLayout(): CodeLayout | null {\n if (!this.layoutCache || this.activeEdit()) return null;\n const m = this.metrics();\n const key = `${this.structureVersion}|${metricsSignature(m)}|${this.advanceCache.signature(m.fontSize, m.fontFamily)}`;\n return this.layoutCacheKey === key ? this.layoutCache : null;\n }\n\n // ── Editing commands ────────────────────────────────────────────────────\n\n /**\n * Append `code` to the end of the listing.\n *\n * Stated as *what the source becomes*; {@link editTo} works out the rest —\n * which is why appending a line that closes a block re-highlights the lines\n * above it, instead of colouring the new text as its own program.\n */\n @command({ args: [{ key: \"code\", kind: \"text\", multiline: true }] })\n append(args: CommandArgs<{ code: string }> & { duration: number }): Command<Record<string, never>> {\n const { code } = args.data as { code: string };\n return this.editTo(this.joinedSource() + code, args.duration, args.easing);\n }\n\n /** Insert `code` above the listing. */\n @command({ args: [{ key: \"code\", kind: \"text\", multiline: true }] })\n prepend(args: CommandArgs<{ code: string }> & { duration: number }): Command<Record<string, never>> {\n const { code } = args.data as { code: string };\n return this.editTo(code + this.joinedSource(), args.duration, args.easing);\n }\n\n /**\n * Insert `code` at the given (line, col). Both are 1-indexed; col is the\n * column BEFORE which the new content is inserted (col=1 means start of\n * line). If `code` contains newlines, new lines are created in the middle\n * of the existing line.\n */\n @command({\n args: [\n // Two controls, one argument: the method takes a `[line, col]`\n // tuple, so the pair a host draws is declared *inside* the value it\n // composes into rather than flattened beside it.\n {\n key: \"position\", kind: \"point\", properties: {\n line: { kind: \"number\", default: 1, min: 1, step: 1 },\n col: { kind: \"number\", default: 1, min: 1, step: 1 },\n },\n },\n { key: \"code\", kind: \"text\", default: \"\", multiline: true },\n ],\n })\n insert(\n args: CommandArgs<{ position: [number, number]; code: string }> & { duration: number },\n ): Command<Record<string, never>> {\n const { position, code } = args.data as { position: [number, number]; code: string };\n const source = this.joinedSource();\n const offset = this.offsetAt(position);\n return this.editTo(source.slice(0, offset) + code + source.slice(offset), args.duration, args.easing);\n }\n\n /**\n * Erase the code in `codeRange`.\n *\n * Named `erase` rather than `remove` because a `Code` is also a node, and\n * `Node.remove(child)` takes a child out of the tree. Two methods spelled\n * the same on one object, doing entirely different things, is worse than one\n * of them having a slightly less obvious name.\n *\n * A range that covers whole lines takes their line breaks with it, so the\n * rows below close up; a range inside a line takes only the characters, and\n * the rest of the line reflows around the hole.\n */\n @command({ key: \"remove\", args: [{ key: \"target\", kind: \"selector\" }] })\n erase(\n args: CommandArgs<{ target: CodeRange }> & { duration: number },\n ): Command<Record<string, never>> {\n const { target: codeRange } = args.data as { target: CodeRange };\n const { duration, easing } = args;\n const source = this.joinedSource();\n let { start, end } = rangeToCharOffsets(codeRange, this.lineLengths());\n // `lines(2)` resolves to the *characters* of line 2, not to the row —\n // taking those alone would leave an empty line behind where an editor\n // would have closed the gap. This is also what makes a blank row\n // removable at all: it has no characters, so its range is empty, and\n // \"delete nothing\" is not what `lines(2)` was asking for.\n const atLineStart = start === 0 || source[start - 1] === \"\\n\";\n const atLineEnd = end === source.length || source[end] === \"\\n\";\n if (atLineStart && atLineEnd) {\n if (end < source.length) end += 1;\n else if (start > 0) start -= 1;\n } else if (end <= start) {\n return driveCommand(duration, () => { });\n }\n if (end <= start) return driveCommand(duration, () => { });\n return this.editTo(source.slice(0, start) + source.slice(end), duration, easing);\n }\n\n /** Replace the code in `codeRange` with `replacement`. */\n @command({ args: [{ key: \"target\", kind: \"selector\" }, { key: \"replacement\", kind: \"text\", default: \"\", multiline: true }] })\n replace(\n args: CommandArgs<{ target: CodeRange; replacement: string }> & { duration: number },\n ): Command<Record<string, never>> {\n const { target: codeRange, replacement } = args.data as { target: CodeRange; replacement: string };\n const source = this.joinedSource();\n const { start, end } = rangeToCharOffsets(codeRange, this.lineLengths());\n return this.editTo(source.slice(0, start) + replacement + source.slice(end), args.duration, args.easing);\n }\n\n /**\n * Highlight a range of code — or several at once (e.g.\n * `CodeRanges.lines(2).word(5, 1, 3)`) — highlighted as one set: tokens\n * within any of the ranges stay at opacity 1, tokens outside dim to\n * `dim`. Persistent — call resetHighlight() to undo, or call highlight()\n * again with a different range to cross-fade.\n */\n @command({ args: [{ key: \"target\", kind: \"selector\" }, { key: \"dim\", kind: \"number\", default: 0.4, min: 0, max: 1, step: 0.01, scale: 100, unit: \"%\" }] })\n highlight(\n args: CommandArgs<{ target: CodeRange | Iterable<CodeRange>; dim?: number }> & { duration: number },\n ): Command<Record<string, never>> {\n const { target: codeRange, dim } = args.data as { target: CodeRange | Iterable<CodeRange>; dim?: number };\n const { duration, easing } = args;\n const toDim = dim ?? 0.4;\n const matchIds = this.tokenIdsInRanges(normalizeCodeRanges(codeRange));\n if (matchIds.size === 0) return driveCommand(duration, () => { });\n\n const fromDim = this.highlightDimOpacity ?? 1;\n const hadPrevious = this.highlightedIds.size > 0;\n const previousIds = this.highlightedIds;\n\n const animTokens: AnimToken[] = [];\n for (const line of this.tokenLines) {\n for (const tok of line.tokens) {\n const wasHighlighted = !hadPrevious || previousIds.has(tok.id);\n const isHighlighted = matchIds.has(tok.id);\n const fromOp = wasHighlighted ? 1 : fromDim;\n const toOp = isHighlighted ? 1 : toDim;\n if (fromOp === toOp) continue;\n animTokens.push(makeAnim(tok.id, { keys: [0, 1], values: [fromOp, toOp] }));\n }\n }\n\n // Which tokens are dimmed is state the *next* highlight reads to know\n // what it is cross-fading from, so it is committed at the end and put\n // back below it — a `finally` could only ever have run forwards.\n return this.runDim(animTokens, duration, easing, (done) => {\n this.highlightDimOpacity = done ? toDim : (hadPrevious ? fromDim : null);\n this.highlightedIds = done ? matchIds : previousIds;\n });\n }\n\n /**\n * Fade all dimmed tokens back to opacity 1 and clear the persistent\n * highlight state.\n */\n @command()\n resetHighlight(args: CommandArgs<Record<string, never>> & { duration: number }): Command<Record<string, never>> {\n const { duration, easing } = args;\n if (this.highlightDimOpacity === null) return driveCommand(duration, () => { });\n\n const fromDim = this.highlightDimOpacity;\n const previousIds = this.highlightedIds;\n\n const animTokens: AnimToken[] = [];\n for (const line of this.tokenLines) {\n for (const tok of line.tokens) {\n const fromOp = previousIds.has(tok.id) ? 1 : fromDim;\n if (fromOp === 1) continue;\n animTokens.push(makeAnim(tok.id, { keys: [0, 1], values: [fromOp, 1] }));\n }\n }\n\n return this.runDim(animTokens, duration, easing, (done) => {\n this.highlightDimOpacity = done ? null : fromDim;\n this.highlightedIds = done ? new Set() : previousIds;\n });\n }\n\n /**\n * Animate the listing to a new source.\n *\n * The engine behind every editing command, and behind `to({ code })`. Every\n * edit is expressed as the source it produces, tokenized, and then *diffed*\n * against what is on screen — so a token that survived the edit keeps its\n * identity and simply travels to its new column, and only what genuinely\n * changed is faded.\n *\n * The result runs in three phases (see `phaseStrategy`): what is leaving\n * fades out, what stays reflows, and only then does what is new arrive.\n * Playing them together is what made a replacement read as a cross-dissolve\n * of two unrelated listings rather than as an edit being made.\n */\n private editTo(next: string, duration: number, easing?: EasingFunction): Command<Record<string, never>> {\n const from = this.tokenLines;\n const fromCode = this.joinedSource();\n if (next === fromCode) return driveCommand(duration, () => { });\n\n const edit = this.diffStrategy(from, tokenizeCodeToIdLines(next, this.language, this.theme));\n\n const transition: StructuralTransition = {\n kind: \"structural\",\n from,\n to: edit.lines,\n removedIds: edit.removedIds,\n addedIds: edit.addedIds,\n enteringLineIds: edit.newLineIds,\n fromColorById: edit.fromColorById,\n phases: this.phaseStrategy(edit.removedIds.size > 0, edit.addedIds.size > 0),\n progress: 0,\n layoutKey: null,\n fromLayout: null,\n toLayout: null,\n };\n\n // A tokenize triggered by the grammar landing mid-edit would mint fresh\n // ids underneath this transition, and the transition is keyed by the ones\n // it captured — so the retry in `onRender` is gated on there being no edit\n // in flight, which means this flag has to be honest now rather than once\n // the edit settles.\n this.tokenized = canHighlight(this.language, this.theme);\n\n return driveCommand(duration, (t) => {\n const running = t > 0 && t < 1;\n const index = this.transitions.indexOf(transition);\n if (running && index < 0) this.transitions.push(transition);\n else if (!running && index >= 0) this.transitions.splice(index, 1);\n\n transition.progress = easing ? easing(t) : t;\n\n // The settle, in both directions: asked for a time before the edit\n // starts, the listing is what preceded it; at or past the end, the\n // result. A `finally` could only ever have run forwards.\n if (t <= 0) {\n this.setLines(from);\n this._writeProp(\"code\", fromCode);\n } else {\n this.setLines(edit.lines);\n this._writeProp(\"code\", next);\n }\n });\n }\n\n /**\n * Put a dim transition on the stack, drive its progress, take it off and\n * commit at the end.\n *\n * A {@link Command} rather than a generator, which is what makes a listing\n * scrubbable: membership is a function of `t` (on the stack for `0 < t < 1`,\n * off it outside), `settle(done)` replaces the `finally` and takes *which\n * way*, and `progress` is assigned from `t` rather than accumulated.\n */\n private runDim(\n tokens: AnimToken[],\n duration: number,\n easing?: EasingFunction,\n settle?: (done: boolean) => void,\n ): Command<Record<string, never>> {\n const transition = { kind: \"dim\" as const, tokens, progress: 0 };\n\n return driveCommand(duration, (t) => {\n const running = t > 0 && t < 1;\n const index = this.transitions.indexOf(transition);\n if (running && index < 0) this.transitions.push(transition);\n else if (!running && index >= 0) this.transitions.splice(index, 1);\n\n transition.progress = easing ? easing(t) : t;\n settle?.(t >= 1);\n });\n }\n\n /**\n * Route a tweened `code` through the diff engine.\n *\n * `to({ code })` is the form an author reaches for first — it is how every\n * other prop is animated — and a bare string prop would otherwise land in the\n * generic tween's discrete-snap bucket and simply cut at the end. Intercepted\n * here it becomes the same three-phase edit the named commands produce, while\n * the rest of the step's props tween alongside it untouched.\n */\n override _prepareStep(\n to: Partial<CodeProps>,\n duration: number,\n easing?: EasingFunction,\n ): TweenStepper {\n const next = to.code;\n if (typeof next !== \"string\" || next === this.joinedSource()) {\n return super._prepareStep(to, duration, easing);\n }\n\n const rest: Partial<CodeProps> = { ...to };\n delete rest.code;\n // Prepared before the edit, so a step that also moves `fontSize` has\n // snapshotted its own start value against the listing that is still on\n // screen.\n const others = super._prepareStep(rest, duration, easing);\n const edit = this.editTo(next, duration, easing)._stepper();\n\n return {\n seek: (elapsed: number) => {\n others.seek(elapsed);\n edit.seek(elapsed);\n },\n advance: (dt: number): boolean => {\n const othersDone = others.advance(dt);\n const editDone = edit.advance(dt);\n return othersDone && editDone;\n },\n };\n }\n\n // ── Source queries ──────────────────────────────────────────────────────\n\n /**\n * Find every range matching the literal string `text` in the current\n * source. Multi-line matches are supported.\n */\n findAllRanges(text: string): CodeRange[] {\n const ranges: CodeRange[] = [];\n if (!text) return ranges;\n const source = this.joinedSource();\n const lineLens = this.lineLengths();\n let from = 0;\n while (true) {\n const idx = source.indexOf(text, from);\n if (idx === -1) break;\n ranges.push(charOffsetsToRange(idx, idx + text.length, lineLens));\n from = idx + Math.max(1, text.length);\n }\n return ranges;\n }\n\n /** Find the `index`th range matching `text`. Returns null if not found. */\n findRangeAt(text: string, index: number): CodeRange | null {\n const all = this.findAllRanges(text);\n return all[index] ?? null;\n }\n\n /** Find the first range matching `text`. Returns null if not found. */\n findFirstRange(text: string): CodeRange | null {\n return this.findRangeAt(text, 0);\n }\n\n private joinedSource(): string {\n return this.tokenLines\n .map(line => line.tokens.map(t => t.content).join(''))\n .join('\\n');\n }\n\n private lineLengths(): number[] {\n return this.tokenLines.map(line =>\n line.tokens.reduce((acc, t) => acc + t.content.length, 0),\n );\n }\n\n /** Character offset of a 1-indexed (line, col), clamped into the document. */\n private offsetAt(position: [number, number]): number {\n const lens = this.lineLengths();\n if (lens.length === 0) return 0;\n const [rawLine, rawCol] = position;\n const li = Math.max(0, Math.min(lens.length - 1, rawLine - 1));\n let offset = 0;\n for (let k = 0; k < li; k++) offset += lens[k] + 1;\n return offset + Math.max(0, Math.min(lens[li], rawCol - 1));\n }\n\n /**\n * Resolve one or more CodeRanges to the set of token ids whose content\n * overlaps any of them. Tokens that partially overlap are included.\n */\n private tokenIdsInRanges(codeRanges: CodeRange[]): Set<number> {\n const result = new Set<number>();\n const lineLens = this.lineLengths();\n if (lineLens.length === 0) return result;\n const offsets = codeRanges\n .map(r => rangeToCharOffsets(r, lineLens))\n .filter(({ start, end }) => end > start);\n if (offsets.length === 0) return result;\n\n // Walk tokens with running offsets in the joined string, testing each\n // token against every requested range's offsets.\n let off = 0;\n for (let li = 0; li < this.tokenLines.length; li++) {\n const line = this.tokenLines[li];\n for (const tok of line.tokens) {\n const tStart = off;\n const tEnd = off + tok.content.length;\n if (offsets.some(({ start, end }) => tEnd > start && tStart < end)) result.add(tok.id);\n off = tEnd;\n }\n if (li < this.tokenLines.length - 1) off += 1; // newline\n }\n return result;\n }\n\n // ── Drawing ─────────────────────────────────────────────────────────────\n\n /**\n * Thin delegator: everything about *how* a frame is painted (settled or\n * mid structural-edit, entry/exit motion, colour cross-fade) lives in\n * `drawCode` (the engine's `render.ts`) as a pure function over plain\n * state — this just gathers that state from the node's own instance\n * fields and caches.\n */\n protected drawSelf(draw: RenderContext2D): void {\n this.advanceCache.sync(this.advanceCache.signature(this.fontSize, this.fontFamily));\n drawCode(draw, {\n tokenLines: this.tokenLines,\n metrics: this.metrics(),\n advanceCache: this.advanceCache,\n ...this.frameLayout(draw),\n tokenStates: resolveTokenStates(this.tokenLines, this.transitions, this.highlightDimOpacity, this.highlightedIds),\n highlightActive: this.highlightDimOpacity !== null || this.transitions.some(tr => tr.kind === \"dim\"),\n });\n }\n}\n\n/**\n * Normalize `highlight()`'s `codeRange` argument to a plain array: a single\n * `CodeRange` (a plain object, not iterable) becomes a one-element array; a\n * `CodeRangeChain`/plain array of ranges spreads as-is. `CodeRange` and\n * `CodeRangeChain` are distinguished by iterability, not `instanceof` — a\n * bare array of `CodeRange`s (no chain) works the same way.\n */\nfunction normalizeCodeRanges(codeRange: CodeRange | Iterable<CodeRange>): CodeRange[] {\n const maybeIterable = codeRange as Partial<Iterable<CodeRange>>;\n if (typeof maybeIterable[Symbol.iterator] === \"function\") {\n return [...(codeRange as Iterable<CodeRange>)];\n }\n return [codeRange as CodeRange];\n}\n\nconst grammarLoaders = new Map<string, () => Promise<void>>();\n\n// One function per language: blocks sharing a grammar declare one key, and a\n// second load function for a key already declared is ignored with a warning.\nfunction grammarLoader(language: string): () => Promise<void> {\n let load = grammarLoaders.get(language);\n if (load === undefined) {\n load = () => ensureHighlighter(undefined, [language]);\n grammarLoaders.set(language, load);\n }\n return load;\n}\n\n", "import type { CodeLayout } from \"./layout\";\nimport { IdLine } from \"./tokens\";\n\n// A single animated token entry owned by one dim transition. Keyed by stable\n// token id rather than (line, tokenIndex) so concurrent animations don't clobber\n// each other when one shifts the indices of tokens the other is animating.\nexport interface AnimToken {\n id: number;\n opacity: number[];\n opacityKeys: number[];\n}\n\n/**\n * A highlight cross-fade: opacity, and nothing else.\n *\n * Structure is untouched, so several of these stack happily and a listing held\n * under a highlight still hits the static-layout cache.\n */\nexport interface DimTransition {\n kind: \"dim\";\n tokens: AnimToken[];\n progress: number;\n}\n\n/**\n * When each part of a structural edit happens, in normalized transition time.\n *\n * The order is what an edit *looks like* being made: what is going away leaves\n * first, what stays reflows into its new place, and only then does what is new\n * appear. Running them together is what made a replacement look like a\n * cross-dissolve of two unrelated listings.\n */\nexport interface Phases {\n /** Removed tokens fade out. Null when the edit removes nothing. */\n out: [number, number] | null;\n /** Surviving tokens travel, and the block resizes. Always present. */\n move: [number, number];\n /** Added tokens fade (and slide) in. Null when the edit adds nothing. */\n in: [number, number] | null;\n}\n\n/**\n * One edit in flight: the structure before, the structure after, and who is\n * doing what.\n *\n * Both endpoints are fixed for the transition's whole life, which is why the\n * two resolved layouts hang off it — they are computed once and reused for\n * every frame the edit spans, rather than rebuilt per frame from a structure\n * that is being mutated underneath.\n */\nexport interface StructuralTransition {\n kind: \"structural\";\n from: IdLine[];\n to: IdLine[];\n removedIds: Set<number>;\n addedIds: Set<number>;\n /**\n * Ids of rows in `to` that did not exist before, and so enter as a whole\n * row rather than as tokens spliced into one.\n *\n * They enter by fading in place, and that is the *only* thing they do. An\n * earlier version slid each one in from the edge the block had just grown\n * along — appended rows rising from below, prepended ones descending, ones\n * spliced into the middle arriving from the left. Every one of those is a\n * claim about where the line came from, and a listing has no such story to\n * tell: text is written where it belongs, it does not travel there. Sliding\n * also puts the row under the neighbours it is meant to sit between for the\n * length of its entrance, which reads as the listing redrawing itself.\n */\n enteringLineIds: Set<number>;\n fromColorById: Map<number, string | undefined>;\n phases: Phases;\n progress: number;\n /** @internal Layout cache — see {@link StructuralTransition}. */\n layoutKey: string | null;\n fromLayout: CodeLayout | null;\n toLayout: CodeLayout | null;\n}\n\nexport type CodeTransition = DimTransition | StructuralTransition;\n\n/**\n * Decides *when* (in normalized transition time) each part of a structural\n * edit happens, given only whether the edit removes/adds anything.\n * `phasesFor` below is the default implementation (exported as\n * `defaultCodePhases`) — pass a `phaseStrategy` of this shape to `<Code>` to\n * replace it with your own timing.\n */\nexport type CodePhaseStrategy = (hasRemoved: boolean, hasAdded: boolean) => Phases;\n\nexport interface TokenState {\n opacity: number;\n}\n\n/** Sample a piecewise-linear curve (keys → values) at progress `p` in [0, 1]. */\nexport function sampleCurve(keys: number[], values: number[], p: number): number {\n if (p <= keys[0]) return values[0];\n if (p >= keys[keys.length - 1]) return values[values.length - 1];\n for (let i = 0; i < keys.length - 1; i++) {\n if (p <= keys[i + 1]) {\n const span = keys[i + 1] - keys[i];\n const local = span === 0 ? 0 : (p - keys[i]) / span;\n return values[i] + (values[i + 1] - values[i]) * local;\n }\n }\n return values[values.length - 1];\n}\n\n/** Progress *within* a phase window, clamped to [0, 1]. A null window is complete. */\nexport function windowProgress(w: [number, number] | null, p: number): number {\n if (!w) return 1;\n const [a, b] = w;\n if (p <= a) return 0;\n if (p >= b) return 1;\n return b === a ? 1 : (p - a) / (b - a);\n}\n\n/** Smooth start and end, so a phase never begins or ends with a velocity step. */\nexport function smoothstep(t: number): number {\n return t * t * (3 - 2 * t);\n}\n\nexport function makeAnim(id: number, curve: { keys: number[]; values: number[] }): AnimToken {\n return { id, opacity: curve.values, opacityKeys: curve.keys };\n}\n\n/**\n * The phase schedule for an edit, shaped by what the edit actually contains.\n *\n * A fixed schedule wastes time it does not need: a pure append has nothing to\n * fade out, so holding the first 40% of the duration empty just makes the\n * animation feel late. Each shape gets the whole duration spent on the parts\n * that exist, with adjacent phases overlapping enough to read as one movement.\n */\nexport function phasesFor(hasRemoved: boolean, hasAdded: boolean): Phases {\n // The overlaps are deliberate and small. Butt-joining the phases leaves the\n // block sitting empty at its new size for a beat between the old content\n // leaving and the new content arriving; letting the arrival start while the\n // reflow's eased tail is still running closes that gap without blurring the\n // order the edit reads in.\n if (hasRemoved && hasAdded) return { out: [0, 0.38], move: [0.10, 0.68], in: [0.52, 1] };\n if (hasRemoved) return { out: [0, 0.45], move: [0.22, 1], in: null };\n if (hasAdded) return { out: null, move: [0, 0.55], in: [0.38, 1] };\n return { out: null, move: [0, 1], in: null };\n}\n\n/** {@link phasesFor} under the name a `phaseStrategy` default is looked up by. */\nexport const defaultCodePhases: CodePhaseStrategy = phasesFor;\n\n/** How far through the \"everything reflows\" phase a frame is, eased. A settled frame (no edit) is always fully moved. */\nexport function moveProgress(edit: { phases: Phases; progress: number } | null): number {\n return edit ? smoothstep(windowProgress(edit.phases.move, edit.progress)) : 1;\n}\n\n/**\n * Resolve the per-token opacity multiplier for the current frame from the\n * persistent highlight dim plus every active dim transition.\n */\nexport function resolveTokenStates(\n tokenLines: IdLine[],\n transitions: CodeTransition[],\n highlightDimOpacity: number | null,\n highlightedIds: Set<number>,\n): Map<number, TokenState> {\n const out = new Map<number, TokenState>();\n\n if (highlightDimOpacity !== null) {\n const dim = highlightDimOpacity;\n for (const line of tokenLines) {\n for (const tok of line.tokens) {\n out.set(tok.id, { opacity: highlightedIds.has(tok.id) ? 1 : dim });\n }\n }\n }\n\n for (const tr of transitions) {\n if (tr.kind !== \"dim\") continue;\n for (const at of tr.tokens) {\n out.set(at.id, { opacity: sampleCurve(at.opacityKeys, at.opacity, tr.progress) });\n }\n }\n\n return out;\n}\n", "import type { InsetsResolved, MeasureContext2D, RenderContext2D } from \"@motionscript/core\";\nimport type { TokenAdvanceCache } from \"./measure-cache\";\nimport type { IdLine } from \"./tokens\";\n\n/**\n * Everything about a listing's geometry that is *not* its content — the type,\n * the padding, whether there is a gutter. Split out from the node so a layout\n * can be computed for a token structure the node is no longer holding: during a\n * structural transition the frame is the interpolation of two of them, and the\n * one that is going away is not reachable through `this` any more.\n */\nexport interface CodeMetrics {\n fontSize: number;\n fontFamily: string;\n lineHeight: number;\n letterSpacing: number;\n padding: InsetsResolved;\n showLineNumbers: boolean;\n lineNumberGap: number;\n}\n\n/** Where one token sits: its LEFT edge, and the vertical CENTRE of its line's slot. */\nexport interface TokenBox {\n x: number;\n y: number;\n}\n\n/**\n * A resolved listing geometry: every token placed, in the node's own space\n * (origin at the block's centre, y-up).\n *\n * Positions are absolute rather than \"a running sum plus a width scale\", which\n * is the whole point. A token's place in the *next* structure is knowable\n * without drawing it, so a transition can move a token from where it was to\n * where it will be instead of faking the motion by collapsing the advance of\n * its neighbours — which is what made an insert look like a pile of glyphs at\n * the left margin springing apart.\n */\nexport interface CodeLayout {\n /** Token id → placement. */\n tokens: Map<number, TokenBox>;\n /** Vertical centre of each line's slot, by line index. */\n lineY: number[];\n /**\n * Width of each line's ink, by line index — the sum of its tokens' advances,\n * so a blank line is `0` and a short line is short.\n *\n * Returned rather than kept local because it is the only thing that knows a\n * listing is **ragged**: `innerW` is the longest line, and everything that\n * asks where the text actually is (hit testing, for one) would otherwise have\n * to treat the block as a rectangle the width of its longest row.\n */\n lineW: number[];\n /** Line id → its index in this structure (i.e. its line *number* minus one). */\n lineIndex: Map<number, number>;\n gutter: number;\n gutterGap: number;\n /** Content size, no padding. */\n innerW: number;\n innerH: number;\n /** Content size plus padding — what the block draws and measures as. */\n blockW: number;\n blockH: number;\n /** Left edge of the code column (after padding and gutter). */\n startX: number;\n}\n\n/**\n * Signature of everything a layout depends on other than the token structure\n * itself. A cached layout survives exactly as long as this is unchanged.\n */\nexport function metricsSignature(m: CodeMetrics): string {\n const p = m.padding;\n return [\n m.fontSize, m.fontFamily, m.lineHeight, m.letterSpacing,\n p.left, p.right, p.top, p.bottom,\n m.showLineNumbers ? 1 : 0, m.lineNumberGap,\n ].join(\"|\");\n}\n\n/**\n * Horizontal gap between the line-number column and the code text. Sized in\n * space-widths so it scales with the font, and measured with letterSpacing 0 —\n * line numbers and the gap don't carry the code's letter-spacing.\n */\nfunction gutterGapOf(scope: MeasureContext2D | RenderContext2D, m: CodeMetrics, cache: TokenAdvanceCache): number {\n return cache.advance(scope, \" \", m.fontSize, m.fontFamily, 0) * m.lineNumberGap;\n}\n\n/** Width of the line-number column for a structure of `lineCount` lines, gap included. */\nfunction gutterOf(\n scope: MeasureContext2D | RenderContext2D,\n m: CodeMetrics,\n cache: TokenAdvanceCache,\n lineCount: number,\n gap: number,\n): number {\n if (!m.showLineNumbers) return 0;\n const sample = String(Math.max(1, lineCount));\n return cache.advance(scope, sample, m.fontSize, m.fontFamily, 0) + gap;\n}\n\n/**\n * Place every token of `lines`.\n *\n * Pure in (lines, metrics, font measurements), which is what lets a caller cache\n * one per structure rather than recomputing it per frame — a transition's two\n * endpoints are both fixed for its whole duration.\n */\nexport function layoutCode(\n lines: IdLine[],\n m: CodeMetrics,\n cache: TokenAdvanceCache,\n scope: MeasureContext2D | RenderContext2D,\n): CodeLayout {\n const lineH = m.fontSize * m.lineHeight;\n const gutterGap = m.showLineNumbers ? gutterGapOf(scope, m, cache) : 0;\n const gutter = gutterOf(scope, m, cache, lines.length, gutterGap);\n\n const widths: number[] = new Array(lines.length);\n let maxLineWidth = 0;\n for (let i = 0; i < lines.length; i++) {\n let w = 0;\n for (const tok of lines[i].tokens) {\n w += cache.advance(scope, tok.content, m.fontSize, m.fontFamily, m.letterSpacing);\n }\n widths[i] = w;\n if (w > maxLineWidth) maxLineWidth = w;\n }\n\n const innerW = maxLineWidth + gutter;\n const innerH = lines.length * lineH;\n const blockW = innerW + m.padding.left + m.padding.right;\n const blockH = innerH + m.padding.top + m.padding.bottom;\n const startX = -blockW / 2 + m.padding.left + gutter;\n // y-up author space: the first line sits at the TOP of the block, so the\n // cursor starts at the top edge (+half height, minus top padding) and steps\n // DOWNWARD by subtracting a line height per row.\n const startY = blockH / 2 - m.padding.top;\n\n const tokens = new Map<number, TokenBox>();\n const lineY: number[] = new Array(lines.length);\n const lineIndex = new Map<number, number>();\n\n for (let i = 0; i < lines.length; i++) {\n const centerY = startY - i * lineH - lineH / 2;\n lineY[i] = centerY;\n lineIndex.set(lines[i].id, i);\n let x = startX;\n for (const tok of lines[i].tokens) {\n tokens.set(tok.id, { x, y: centerY });\n x += cache.advance(scope, tok.content, m.fontSize, m.fontFamily, m.letterSpacing);\n }\n }\n\n return { tokens, lineY, lineW: widths, lineIndex, gutter, gutterGap, innerW, innerH, blockW, blockH, startX };\n}\n", "import { RenderContext2D, Graphics2D, NormalizedColor, parseColor, lerpNumber } from \"@motionscript/core\";\nimport type { CodeLayout, CodeMetrics } from \"./layout\";\nimport type { IdLine } from \"./tokens\";\nimport { windowProgress, moveProgress } from \"./transitions\";\nimport type { TokenState, StructuralTransition } from \"./transitions\";\nimport type { TokenAdvanceCache } from \"./measure-cache\";\n\n/** Colour of a token the grammar had no opinion about. */\nconst DEFAULT_TOKEN_COLOR: NormalizedColor = [0.82, 0.84, 0.86, 1];\n/** Gutter line-number colour: a muted version of the standard text colour. */\nconst LINE_NUMBER_COLOR: NormalizedColor = [0.45, 0.5, 0.55, 1];\n\n// `parseColor` walks a CSS string; a listing asks for the same handful of theme\n// colours on every token of every frame, so the parse is memoized. Module-level,\n// because the answer depends only on the string.\nconst colorCache = new Map<string, NormalizedColor>();\nfunction tokenColor(css: string | undefined): NormalizedColor {\n if (!css) return DEFAULT_TOKEN_COLOR;\n let hit = colorCache.get(css);\n if (!hit) {\n hit = parseColor(css);\n colorCache.set(css, hit);\n }\n return hit;\n}\n\nfunction lerpColor(from: NormalizedColor, to: NormalizedColor, t: number): NormalizedColor {\n if (t <= 0) return from;\n if (t >= 1) return to;\n return [\n from[0] + (to[0] - from[0]) * t,\n from[1] + (to[1] - from[1]) * t,\n from[2] + (to[2] - from[2]) * t,\n from[3] + (to[3] - from[3]) * t,\n ];\n}\n\n// Opacity multiplier for a line's number under the active highlight. The\n// number stays bright only when the WHOLE line is highlighted, so we take the\n// min token opacity on the line. Returns 1 (no dimming) when no highlight is\n// active, so an edit's entries don't drag the number down.\nfunction lineHighlightOpacity(line: IdLine, stateById: Map<number, TokenState>, highlightActive: boolean): number {\n if (!highlightActive) return 1;\n let min = 1;\n for (const tok of line.tokens) {\n if (tok.content.length === 0) continue;\n const op = stateById.get(tok.id)?.opacity ?? 1;\n if (op < min) min = op;\n }\n return min;\n}\n\n/** Everything a `Code` node's own instance state supplies to draw one frame. */\nexport interface DrawCodeState {\n tokenLines: IdLine[];\n metrics: CodeMetrics;\n advanceCache: TokenAdvanceCache;\n /** The one or two layouts this frame draws from — see `frameLayout` in `layout.ts`. */\n from: CodeLayout;\n to: CodeLayout;\n edit: StructuralTransition | null;\n /** Per-token opacity from `resolveTokenStates` (persistent highlight + any active dim transition). */\n tokenStates: Map<number, TokenState>;\n /** Whether a highlight (persistent dim, or a highlight()/resetHighlight() cross-fade) is currently engaged. */\n highlightActive: boolean;\n}\n\n/**\n * Draw one frame of a code listing: the settled structure, or — mid\n * structural edit — the two structures it is interpolating between (what\n * stays reflows, what is leaving fades out where it was, what is arriving\n * fades/slides in where it lands). Pure over `state`; touches nothing but\n * `draw`.\n */\nexport function drawCode(draw: RenderContext2D, state: DrawCodeState): void {\n const { tokenLines, metrics, advanceCache, from, to, edit, tokenStates: dim } = state;\n\n const progress = edit ? edit.progress : 1;\n const pOut = edit ? windowProgress(edit.phases.out, progress) : 1;\n const pMove = moveProgress(edit);\n const pIn = edit ? windowProgress(edit.phases.in, progress) : 1;\n\n const blockW = edit ? lerpNumber(from.blockW, to.blockW, pMove) : to.blockW;\n const gutter = edit ? lerpNumber(from.gutter, to.gutter, pMove) : to.gutter;\n const gutterGap = to.gutterGap || from.gutterGap;\n // Right edge of the line-number column: one gap to the left of where the\n // code text begins.\n const numberRight = -blockW / 2 + metrics.padding.left + gutter - gutterGap;\n\n const drawNumber = (label: string, y: number, opacity: number): void => {\n if (opacity <= 0) return;\n const labelW = advanceCache.advance(draw, label, metrics.fontSize, metrics.fontFamily, 0);\n draw.draw(new Graphics2D()\n .text({\n text: label,\n fontSize: metrics.fontSize,\n fontFamily: metrics.fontFamily,\n lineHeight: metrics.lineHeight,\n x: numberRight - labelW / 2,\n y,\n textAlign: 'left',\n })\n .fill([{ type: \"solid\", color: LINE_NUMBER_COLOR, opacity }]));\n };\n\n const drawToken = (text: string, x: number, y: number, color: NormalizedColor, opacity: number): void => {\n if (opacity <= 0 || text.length === 0) return;\n const width = advanceCache.advance(draw, text, metrics.fontSize, metrics.fontFamily, metrics.letterSpacing);\n draw.draw(new Graphics2D()\n .text({\n text,\n fontSize: metrics.fontSize,\n fontFamily: metrics.fontFamily,\n lineHeight: metrics.lineHeight,\n letterSpacing: metrics.letterSpacing,\n // The renderer centres a single-token block on the (x, y) it is\n // given, so every token is anchored at the centre of its cell\n // rather than at its left edge. Passing lineHeight makes the\n // block's height deterministic (fontSize × lineHeight) rather\n // than the font's natural metrics, so the vertical centre\n // lands exactly on the slot centre.\n x: x + width / 2,\n y,\n textAlign: 'left',\n })\n .fill([{ type: \"solid\", color, opacity }]));\n };\n\n // What survived the edit, and what it brought with it — drawn from the\n // structure the edit lands on.\n for (let i = 0; i < tokenLines.length; i++) {\n const line = tokenLines[i];\n const toIndex = to.lineIndex.get(line.id);\n if (toIndex === undefined) continue;\n const toY = to.lineY[toIndex];\n\n const entering = edit?.enteringLineIds.has(line.id) ?? false;\n let rowY = toY;\n let rowAlpha = 1;\n if (edit && entering) {\n // A wholly new row fades as one piece, in the place it lands — the\n // line arrives, it does not travel to get there, and it is not a\n // spray of glyphs each finding its own way in. See\n // `StructuralTransition.enteringLineIds`.\n rowAlpha = pIn;\n } else if (edit) {\n const fromIndex = from.lineIndex.get(line.id);\n if (fromIndex !== undefined) rowY = lerpNumber(from.lineY[fromIndex], toY, pMove);\n }\n\n for (const token of line.tokens) {\n const box = to.tokens.get(token.id);\n if (!box) continue;\n\n let x = box.x;\n let alpha = rowAlpha;\n let color = tokenColor(token.color);\n\n if (edit && !entering) {\n if (edit.addedIds.has(token.id)) {\n // Spliced into a row that already existed: the reflow\n // already opened the space, so this only has to appear.\n alpha = pIn;\n } else {\n const was = from.tokens.get(token.id);\n if (was) x = lerpNumber(was.x, box.x, pMove);\n if (edit.fromColorById.has(token.id)) {\n color = lerpColor(tokenColor(edit.fromColorById.get(token.id)), color, pMove);\n }\n }\n }\n\n drawToken(token.content, x, rowY, color, alpha * (dim.get(token.id)?.opacity ?? 1));\n }\n }\n\n // What the edit takes away, drawn from the structure it is leaving — it\n // has no place in the new one, which is the whole reason it is fading.\n if (edit && pOut < 1) {\n for (let i = 0; i < edit.from.length; i++) {\n const line = edit.from[i];\n const fromIndex = from.lineIndex.get(line.id);\n if (fromIndex === undefined) continue;\n const toIndex = to.lineIndex.get(line.id);\n // A row that survived the edit is still moving, so its outgoing\n // tokens travel with it; a deleted row stays where it was and\n // dissolves while the rows below close over it.\n const y = toIndex === undefined\n ? from.lineY[fromIndex]\n : lerpNumber(from.lineY[fromIndex], to.lineY[toIndex], pMove);\n\n for (const token of line.tokens) {\n if (!edit.removedIds.has(token.id)) continue;\n const box = from.tokens.get(token.id);\n if (!box) continue;\n drawToken(token.content, box.x, y, tokenColor(token.color), 1 - pOut);\n }\n }\n }\n\n if (metrics.showLineNumbers) drawLineNumbers(state, drawNumber, pMove, pIn, pOut);\n}\n\n/**\n * Draw the gutter — one number per *slot*, not one per row.\n *\n * That distinction is the whole of it. A line number is an ordinal of the\n * block: \"3\" means the third line, and it is there for exactly as long as the\n * listing has a third line. Drawing it alongside the row it labels — the\n * obvious thing, since that is where it appears — tied it to the row's identity\n * instead, so a line rewritten past the point the diff could pair it took its\n * number down with it and brought an identical one back up. The number blinked\n * although it had never gone away, and every substantially rewritten line did\n * it at once.\n *\n * Numbering the slots means only the numbers at the end move at all: those the\n * block grew to reach fade in, those it shrank past fade out, and every number\n * both structures have simply travels to its new row height. Which is also the\n * honest reading of the edit — inserting a line in the middle does not make a\n * new number appear there, it makes the block one line longer.\n */\nfunction drawLineNumbers(\n state: DrawCodeState,\n drawNumber: (label: string, y: number, opacity: number) => void,\n pMove: number,\n pIn: number,\n pOut: number,\n): void {\n const { tokenLines, from, to, edit, tokenStates: dim, highlightActive } = state;\n\n const toCount = tokenLines.length;\n const fromCount = edit ? edit.from.length : toCount;\n\n for (let i = 0; i < Math.max(fromCount, toCount); i++) {\n const inFrom = i < fromCount;\n const inTo = i < toCount;\n // Highlight dimming follows whatever content occupies the slot, which\n // is the new listing wherever there is one.\n const line = inTo ? tokenLines[i] : edit?.from[i];\n const dimming = line ? lineHighlightOpacity(line, dim, highlightActive) : 1;\n\n if (!edit) drawNumber(String(i + 1), to.lineY[i], dimming);\n else if (inFrom && inTo) drawNumber(String(i + 1), lerpNumber(from.lineY[i], to.lineY[i], pMove), dimming);\n else if (inTo) drawNumber(String(i + 1), to.lineY[i], pIn * dimming);\n else drawNumber(String(i + 1), from.lineY[i], (1 - pOut) * dimming);\n }\n}\n", "import type { IdLine, IdToken } from \"./tokens\";\n\n/**\n * The result of diffing one listing structure against another: the *next*\n * structure, with token and line ids carried over from the previous one\n * wherever the content survived, plus the three sets a transition needs to know\n * which role each token plays.\n *\n * Identity is the whole product here. `const variable = 3` becoming\n * `const number = 3` should move `const` and `= 3` rather than dissolve and\n * rebuild them, and a token can only *move* if the frame after the edit still\n * knows it is the same token.\n */\n/**\n * How a `Code` node decides which token became which across an edit. Given\n * the old and new tokenized structures, return the carried-over structure\n * plus the added/removed/renamed-line bookkeeping a transition needs.\n * `diffCode` below is the default implementation (exported as\n * `defaultCodeDiff`) — pass a `diffStrategy` of this shape to `<Code>` to\n * replace it with your own.\n */\nexport type CodeDiffStrategy = (from: IdLine[], to: IdLine[]) => CodeEdit;\n\nexport interface CodeEdit {\n /** The new structure. Ids are rewritten in place onto the tokens handed in. */\n lines: IdLine[];\n /** Ids present only in the old structure — these fade out. */\n removedIds: Set<number>;\n /** Ids present only in the new structure — these fade in. */\n addedIds: Set<number>;\n /** Ids of {@link lines} that did not exist before — these enter as a whole row. */\n newLineIds: Set<number>;\n /** Previous colour of a surviving token whose highlighting changed, for the cross-fade. */\n fromColorById: Map<number, string | undefined>;\n}\n\n/**\n * How similar two lines must be before they are treated as *one line that was\n * edited* rather than as one line deleted and another inserted.\n *\n * Below this, a row's tokens have so little in common that carrying ids across\n * would animate glyphs sliding between unrelated words. Above it, the edit reads\n * the way it was made.\n */\nconst LINE_MATCH_FLOOR = 0.45;\n\n/** Weight a token carries when judging similarity: its visible characters. */\nfunction tokenWeight(tok: IdToken): number {\n return tok.content.trim().length;\n}\n\nfunction lineWeight(line: IdLine): number {\n let sum = 0;\n for (const tok of line.tokens) sum += tokenWeight(tok);\n return sum;\n}\n\n/**\n * Dice coefficient over the two lines' token multisets, weighted by visible\n * characters so `variable` counts for far more than a run of spaces.\n *\n * Two blank lines are identical (1); a blank line against a non-blank one shares\n * nothing (0), which keeps the blank rows a reformat introduces from anchoring\n * themselves onto real code.\n */\nfunction similarity(a: IdLine, b: IdLine): number {\n const wa = lineWeight(a);\n const wb = lineWeight(b);\n if (wa === 0 && wb === 0) return 1;\n if (wa === 0 || wb === 0) return 0;\n\n const bag = new Map<string, number>();\n for (const tok of a.tokens) {\n if (tokenWeight(tok) === 0) continue;\n bag.set(tok.content, (bag.get(tok.content) ?? 0) + 1);\n }\n let common = 0;\n for (const tok of b.tokens) {\n const w = tokenWeight(tok);\n if (w === 0) continue;\n const left = bag.get(tok.content) ?? 0;\n if (left > 0) {\n bag.set(tok.content, left - 1);\n common += w;\n }\n }\n return (2 * common) / (wa + wb);\n}\n\n/**\n * Align two sequences by maximising total `score`, skipping is free.\n *\n * A plain LCS would only ever pair *identical* elements, which is the wrong tool\n * twice over: two lines that differ by one word are the same line, and two\n * tokens that differ only in colour are the same token. Scoring the pairing and\n * letting anything that scores ≤ 0 fall out expresses that directly, and because\n * skips cost nothing the alignment stays in order.\n *\n * Returns the matched index pairs, in order.\n */\nfunction align(\n aLength: number,\n bLength: number,\n score: (i: number, j: number) => number,\n): Array<[number, number]> {\n // dp[i][j] = best score aligning a[0..i) with b[0..j).\n const width = bLength + 1;\n const dp = new Float64Array((aLength + 1) * width);\n const pairScore = new Float64Array(aLength * bLength);\n\n for (let i = 1; i <= aLength; i++) {\n for (let j = 1; j <= bLength; j++) {\n const s = score(i - 1, j - 1);\n pairScore[(i - 1) * bLength + (j - 1)] = s;\n const skipA = dp[(i - 1) * width + j];\n const skipB = dp[i * width + (j - 1)];\n let best = skipA > skipB ? skipA : skipB;\n if (s > 0) {\n const paired = dp[(i - 1) * width + (j - 1)] + s;\n if (paired > best) best = paired;\n }\n dp[i * width + j] = best;\n }\n }\n\n const pairs: Array<[number, number]> = [];\n let i = aLength;\n let j = bLength;\n while (i > 0 && j > 0) {\n const s = pairScore[(i - 1) * bLength + (j - 1)];\n if (s > 0 && dp[i * width + j] === dp[(i - 1) * width + (j - 1)] + s) {\n pairs.push([i - 1, j - 1]);\n i--; j--;\n } else if (dp[(i - 1) * width + j] >= dp[i * width + (j - 1)]) {\n i--;\n } else {\n j--;\n }\n }\n pairs.reverse();\n return pairs;\n}\n\n/**\n * Match the tokens of one line against another's.\n *\n * Scored by visible characters rather than token count, so an alignment that\n * keeps `items.reduce` together beats one that keeps four more spaces. A token\n * pairs only when its content is unchanged — a changed word is a different word,\n * and should be seen to be replaced.\n */\nfunction alignTokens(from: IdToken[], to: IdToken[]): Array<[number, number]> {\n return align(from.length, to.length, (i, j) => {\n if (from[i].content !== to[j].content) return 0;\n // Whitespace still pairs (it holds indentation together), just weakly\n // enough that it never outranks real content.\n return tokenWeight(to[j]) || 0.25;\n });\n}\n\n/**\n * Carry `from`'s identities onto `to`, and report what changed.\n *\n * `to` is freshly tokenized and owns ids nobody has seen yet, so rewriting them\n * in place is safe and is what makes the transition cheap: after this, the two\n * structures share an id for every token that survived the edit.\n */\nexport function diffCode(from: IdLine[], to: IdLine[]): CodeEdit {\n const removedIds = new Set<number>();\n const addedIds = new Set<number>();\n const newLineIds = new Set<number>();\n const fromColorById = new Map<number, string | undefined>();\n\n const linePairs = align(from.length, to.length, (i, j) => similarity(from[i], to[j]) - LINE_MATCH_FLOOR);\n\n const pairedTo = new Set<number>();\n const pairedFrom = new Set<number>();\n\n for (const [fi, ti] of linePairs) {\n pairedFrom.add(fi);\n pairedTo.add(ti);\n const fromLine = from[fi];\n const toLine = to[ti];\n toLine.id = fromLine.id;\n\n const matched = alignTokens(fromLine.tokens, toLine.tokens);\n const keptFrom = new Set<number>();\n const keptTo = new Set<number>();\n for (const [a, b] of matched) {\n keptFrom.add(a);\n keptTo.add(b);\n const oldTok = fromLine.tokens[a];\n const newTok = toLine.tokens[b];\n newTok.id = oldTok.id;\n if (oldTok.color !== newTok.color) fromColorById.set(oldTok.id, oldTok.color);\n }\n for (let a = 0; a < fromLine.tokens.length; a++) {\n if (!keptFrom.has(a)) removedIds.add(fromLine.tokens[a].id);\n }\n for (let b = 0; b < toLine.tokens.length; b++) {\n if (!keptTo.has(b)) addedIds.add(toLine.tokens[b].id);\n }\n }\n\n for (let i = 0; i < from.length; i++) {\n if (pairedFrom.has(i)) continue;\n for (const tok of from[i].tokens) removedIds.add(tok.id);\n }\n for (let j = 0; j < to.length; j++) {\n if (pairedTo.has(j)) continue;\n newLineIds.add(to[j].id);\n for (const tok of to[j].tokens) addedIds.add(tok.id);\n }\n\n return { lines: to, removedIds, addedIds, newLineIds, fromColorById };\n}\n", "import { Highlighter, Tag, tagHighlighter, tags as t } from \"@lezer/highlight\";\n\n/**\n * A single style rule: map one or more Lezer highlight {@link Tag}s to a color.\n * This mirrors a `@codemirror/language` HighlightStyle entry, but instead of\n * emitting CSS class names it carries the color directly — we render to a canvas,\n * not the DOM, so there is no stylesheet to resolve against.\n */\nexport interface CodeStyleRule {\n tag: Tag | readonly Tag[];\n color: string;\n}\n\n/**\n * A code theme: an ordered list of tag→color rules plus a fallback color for any\n * text the parser produces no highlight tag for (operators, punctuation, plain\n * identifiers in some grammars, etc.). Pass a custom one via the Code node's\n * `theme` prop to recolor highlighting without touching the highlighter wiring.\n *\n * @remarks\n * Later rules win when several match the same tag, matching how\n * `@lezer/highlight`'s {@link tagHighlighter} resolves precedence.\n */\nexport interface CodeHighlightStyle {\n /** Stable name, used as the highlighter cache key. */\n name: string;\n /** Color applied to text with no matching tag. */\n defaultColor: string;\n rules: CodeStyleRule[];\n}\n\n/**\n * Motion Canvas's default highlight palette (a Nord-derived theme), ported from\n * `@motion-canvas/2d`'s DefaultHighlightStyle so highlighting matches Motion\n * Canvas token-for-token. Colors and tag groupings are kept identical; only the\n * shape changes (color strings in place of CSS rules).\n */\nexport const DefaultHighlightStyle: CodeHighlightStyle = {\n name: \"motion-canvas-default\",\n defaultColor: \"#d8dee9\",\n rules: [\n { tag: t.keyword, color: \"#5e81ac\" },\n { tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName], color: \"#88c0d0\" },\n { tag: [t.variableName], color: \"#8fbcbb\" },\n { tag: [t.function(t.variableName)], color: \"#8fbcbb\" },\n { tag: [t.labelName], color: \"#81a1c1\" },\n { tag: [t.color, t.constant(t.name), t.standard(t.name)], color: \"#5e81ac\" },\n { tag: [t.definition(t.name), t.separator], color: \"#a3be8c\" },\n { tag: [t.brace], color: \"#8fbcbb\" },\n { tag: [t.annotation], color: \"#d30102\" },\n { tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: \"#b48ead\" },\n { tag: [t.typeName, t.className], color: \"#ECEFF4\" },\n { tag: [t.operator, t.operatorKeyword], color: \"#a3be8c\" },\n { tag: [t.tagName], color: \"#b48ead\" },\n { tag: [t.squareBracket], color: \"#ECEFF4\" },\n { tag: [t.angleBracket], color: \"#ECEFF4\" },\n { tag: [t.attributeName], color: \"#eceff4\" },\n { tag: [t.regexp], color: \"#5e81ac\" },\n { tag: [t.quote], color: \"#b48ead\" },\n { tag: [t.string], color: \"#a3be8c\" },\n { tag: t.link, color: \"#a3be8c\" },\n { tag: [t.url, t.escape, t.special(t.string)], color: \"#8fbcbb\" },\n { tag: [t.meta], color: \"#88c0d0\" },\n { tag: [t.monospace], color: \"#d8dee9\" },\n { tag: [t.comment], color: \"#4c566a\" },\n { tag: t.strong, color: \"#5e81ac\" },\n { tag: t.emphasis, color: \"#5e81ac\" },\n { tag: t.heading, color: \"#5e81ac\" },\n { tag: t.special(t.heading1), color: \"#5e81ac\" },\n { tag: t.heading1, color: \"#5e81ac\" },\n { tag: [t.heading2, t.heading3, t.heading4], color: \"#5e81ac\" },\n { tag: [t.heading5, t.heading6], color: \"#5e81ac\" },\n { tag: [t.atom, t.bool, t.special(t.variableName)], color: \"#d08770\" },\n { tag: [t.processingInstruction, t.inserted], color: \"#8fbcbb\" },\n { tag: [t.contentSeparator], color: \"#ebcb8b\" },\n { tag: t.invalid, color: \"#434c5e\" },\n ],\n};\n\n/**\n * GitHub Dark highlight palette, adapted from `@uiw/codemirror-theme-github`.\n * `defaultColor` is GitHub's editor foreground (`#c9d1d9`), used for any text the\n * parser tags don't cover. DOM-only style props (backgroundColor, textDecoration,\n * fontWeight, fontStyle) are dropped — we only color tokens on the canvas.\n */\nexport const GithubDarkStyle: CodeHighlightStyle = {\n name: \"github-dark\",\n defaultColor: \"#c9d1d9\",\n rules: [\n { tag: [t.standard(t.tagName), t.tagName], color: \"#7ee787\" },\n { tag: [t.comment, t.bracket], color: \"#8b949e\" },\n { tag: [t.className, t.propertyName], color: \"#d2a8ff\" },\n { tag: [t.variableName, t.attributeName, t.number, t.operator], color: \"#79c0ff\" },\n { tag: [t.keyword, t.typeName, t.typeOperator], color: \"#ff7b72\" },\n { tag: [t.string, t.meta, t.regexp], color: \"#a5d6ff\" },\n { tag: [t.name, t.quote], color: \"#7ee787\" },\n { tag: [t.heading, t.strong], color: \"#d2a8ff\" },\n { tag: [t.emphasis], color: \"#d2a8ff\" },\n { tag: [t.deleted], color: \"#ffdcd7\" },\n { tag: [t.atom, t.bool, t.special(t.variableName)], color: \"#ffab70\" },\n { tag: t.invalid, color: \"#f97583\" },\n ],\n};\n\n/**\n * GitHub Light highlight palette, adapted from `@uiw/codemirror-theme-github`.\n * `defaultColor` is GitHub's editor foreground (`#24292e`). See {@link GithubDarkStyle}\n * for the note on dropped DOM-only style props.\n */\nexport const GithubLightStyle: CodeHighlightStyle = {\n name: \"github-light\",\n defaultColor: \"#24292e\",\n rules: [\n { tag: [t.standard(t.tagName), t.tagName], color: \"#116329\" },\n { tag: [t.comment, t.bracket], color: \"#6a737d\" },\n { tag: [t.className, t.propertyName], color: \"#6f42c1\" },\n { tag: [t.variableName, t.attributeName, t.number, t.operator], color: \"#005cc5\" },\n { tag: [t.keyword, t.typeName, t.typeOperator], color: \"#d73a49\" },\n { tag: [t.string, t.meta, t.regexp], color: \"#032f62\" },\n { tag: [t.name, t.quote], color: \"#22863a\" },\n { tag: [t.heading, t.strong], color: \"#24292e\" },\n { tag: [t.emphasis], color: \"#24292e\" },\n { tag: [t.deleted], color: \"#b31d28\" },\n { tag: [t.atom, t.bool, t.special(t.variableName)], color: \"#e36209\" },\n { tag: [t.url, t.escape, t.regexp, t.link], color: \"#032f62\" },\n { tag: t.invalid, color: \"#cb2431\" },\n ],\n};\n\n/**\n * VS Code Dark+ highlight palette, adapted from the `@uiw/codemirror-theme-vscode`\n * snippet. `defaultColor` is VS Code's editor foreground (`#9cdcfe`). DOM-only\n * style props are dropped; later rules win, matching {@link tagHighlighter}.\n */\nexport const VscodeDarkStyle: CodeHighlightStyle = {\n name: \"vscode-dark\",\n defaultColor: \"#9cdcfe\",\n rules: [\n {\n tag: [\n t.keyword, t.operatorKeyword, t.modifier, t.color,\n t.constant(t.name), t.standard(t.name), t.standard(t.tagName),\n t.special(t.brace), t.atom, t.bool, t.special(t.variableName),\n ],\n color: \"#569cd6\",\n },\n { tag: [t.controlKeyword, t.moduleKeyword], color: \"#c586c0\" },\n {\n tag: [\n t.name, t.deleted, t.character, t.macroName, t.propertyName,\n t.variableName, t.labelName, t.definition(t.name),\n ],\n color: \"#9cdcfe\",\n },\n { tag: t.heading, color: \"#9cdcfe\" },\n { tag: [t.typeName, t.className, t.tagName, t.changed, t.annotation, t.self, t.namespace], color: \"#4ec9b0\" },\n { tag: [t.function(t.variableName), t.function(t.propertyName)], color: \"#dcdcaa\" },\n { tag: [t.number], color: \"#b5cea8\" },\n { tag: [t.operator, t.punctuation, t.separator, t.url, t.escape], color: \"#d4d4d4\" },\n { tag: [t.regexp], color: \"#d16969\" },\n { tag: [t.special(t.string), t.processingInstruction, t.string, t.inserted], color: \"#ce9178\" },\n { tag: [t.angleBracket], color: \"#808080\" },\n { tag: [t.meta, t.comment], color: \"#6a9955\" },\n { tag: t.link, color: \"#6a9955\" },\n { tag: t.invalid, color: \"#ff0000\" },\n ],\n};\n\n/**\n * VS Code Light+ highlight palette, adapted from the `@uiw/codemirror-theme-vscode`\n * snippet. `defaultColor` is VS Code's editor foreground (`#383a42`). See\n * {@link VscodeDarkStyle} for the note on dropped DOM-only style props.\n */\nexport const VscodeLightStyle: CodeHighlightStyle = {\n name: \"vscode-light\",\n defaultColor: \"#383a42\",\n rules: [\n {\n tag: [\n t.keyword, t.operatorKeyword, t.modifier, t.color,\n t.constant(t.name), t.standard(t.name), t.standard(t.tagName),\n t.special(t.brace), t.atom, t.bool, t.special(t.variableName),\n ],\n color: \"#0000ff\",\n },\n { tag: [t.moduleKeyword, t.controlKeyword], color: \"#af00db\" },\n {\n tag: [\n t.name, t.deleted, t.character, t.macroName, t.propertyName,\n t.variableName, t.labelName, t.definition(t.name),\n ],\n color: \"#0070c1\",\n },\n { tag: t.heading, color: \"#0070c1\" },\n { tag: [t.typeName, t.className, t.tagName, t.changed, t.annotation, t.self, t.namespace], color: \"#267f99\" },\n { tag: [t.function(t.variableName), t.function(t.propertyName)], color: \"#795e26\" },\n { tag: [t.number], color: \"#098658\" },\n { tag: [t.operator, t.punctuation, t.separator, t.url, t.escape], color: \"#383a42\" },\n { tag: [t.regexp], color: \"#af00db\" },\n { tag: [t.special(t.string), t.processingInstruction, t.string, t.inserted], color: \"#a31515\" },\n { tag: [t.angleBracket], color: \"#383a42\" },\n { tag: [t.meta, t.comment], color: \"#008000\" },\n { tag: t.link, color: \"#4078f2\" },\n { tag: t.invalid, color: \"#e45649\" },\n ],\n};\n\n/**\n * The built-in themes, keyed by their string name. {@link CodeThemeName} is the\n * union of these keys — pass one of those strings (or any {@link CodeHighlightStyle}\n * object) to a Code node's `theme` prop.\n */\nexport const BUILTIN_THEMES = {\n // Literal keys (not computed from `.name`) so `keyof` yields a string-literal\n // union for CodeThemeName; these must stay in sync with each style's `name`.\n \"motion-canvas-default\": DefaultHighlightStyle,\n \"github-dark\": GithubDarkStyle,\n \"github-light\": GithubLightStyle,\n \"vscode-dark\": VscodeDarkStyle,\n \"vscode-light\": VscodeLightStyle,\n} satisfies Record<string, CodeHighlightStyle>;\n\n/** Names of the built-in themes, e.g. `'github-dark' | 'github-light'`. */\nexport type CodeThemeName = keyof typeof BUILTIN_THEMES;\n\n/**\n * What a Code node's `theme` prop accepts: a built-in theme name (with\n * autocomplete), any other registered theme name, or a {@link CodeHighlightStyle}\n * object passed inline. The `string & {}` keeps the literal-name suggestions while\n * still allowing arbitrary registered names.\n */\nexport type CodeTheme = CodeThemeName | (string & {}) | CodeHighlightStyle;\n\n// Cache one compiled Highlighter per style name. Compiling builds a tag-rule\n// matcher; the parsed result is pure data so it's safe to share across Code nodes.\nconst compiledByName = new Map<string, Highlighter>();\n\n/**\n * Compile a {@link CodeHighlightStyle} into a `@lezer/highlight` {@link Highlighter}\n * whose \"class\" output is the literal color string for each tag. Cached by style\n * name so repeated lookups (every tokenize) don't rebuild the matcher.\n */\nexport function compileStyle(style: CodeHighlightStyle): Highlighter {\n const cached = compiledByName.get(style.name);\n if (cached) return cached;\n const highlighter = tagHighlighter(\n style.rules.map(rule => ({ tag: rule.tag, class: rule.color })),\n );\n compiledByName.set(style.name, highlighter);\n return highlighter;\n}\n", "import { Parser, Tree } from \"@lezer/common\";\nimport { highlightTree } from \"@lezer/highlight\";\nimport {\n BUILTIN_THEMES,\n CodeHighlightStyle,\n CodeTheme,\n DefaultHighlightStyle,\n compileStyle,\n} from \"./style\";\n\n/**\n * Lezer-backed syntax highlighting for the Code component, replacing the former\n * Shiki wiring. This mirrors Motion Canvas's `LezerHighlighter`:\n *\n * - {@link prepareHighlight} parses code once and builds a `from:to` → color map\n * by walking {@link highlightTree}.\n * - {@link tokenizeCode} splits code at parse-tree leaf boundaries.\n * - {@link highlightAt} resolves the color (and run length) for a character index.\n *\n * Parsers load lazily and asynchronously (dynamic import) so the bundle doesn't\n * pull in every grammar. The node's asset loader awaits {@link ensureHighlighter}\n * before tokenizing; until a parser resolves, callers fall back to plain text and\n * re-tokenize once {@link canHighlight} returns true.\n */\n\n/** Dynamic-import factories for each supported language → a Lezer {@link Parser}. */\nconst LANGUAGE_LOADERS: Record<string, () => Promise<Parser>> = {\n typescript: async () => (await import(\"@lezer/javascript\")).parser.configure({ dialect: \"ts\" }),\n tsx: async () => (await import(\"@lezer/javascript\")).parser.configure({ dialect: \"ts jsx\" }),\n javascript: async () => (await import(\"@lezer/javascript\")).parser,\n jsx: async () => (await import(\"@lezer/javascript\")).parser.configure({ dialect: \"jsx\" }),\n python: async () => (await import(\"@lezer/python\")).parser,\n json: async () => (await import(\"@lezer/json\")).parser,\n html: async () => (await import(\"@lezer/html\")).parser,\n css: async () => (await import(\"@lezer/css\")).parser,\n rust: async () => (await import(\"@lezer/rust\")).parser,\n go: async () => (await import(\"@lezer/go\")).parser,\n java: async () => (await import(\"@lezer/java\")).parser,\n cpp: async () => (await import(\"@lezer/cpp\")).parser,\n php: async () => (await import(\"@lezer/php\")).parser,\n markdown: async () => (await import(\"@lezer/markdown\")).parser,\n xml: async () => (await import(\"@lezer/xml\")).parser,\n yaml: async () => (await import(\"@lezer/yaml\")).parser,\n};\n\n// Aliases so common language strings resolve to a loader above.\nconst LANGUAGE_ALIASES: Record<string, string> = {\n ts: \"typescript\",\n js: \"javascript\",\n py: \"python\",\n \"c++\": \"cpp\",\n md: \"markdown\",\n yml: \"yaml\",\n shell: \"javascript\", // no shell grammar bundled; fall back rather than throw\n};\n\nfunction resolveLanguageKey(language: string): string {\n const key = language.toLowerCase();\n return LANGUAGE_ALIASES[key] ?? key;\n}\n\n// Loaded parsers and in-flight loads, keyed by resolved language key, so\n// concurrent Code nodes asking for the same language share one dynamic import.\nconst loadedParsers = new Map<string, Parser>();\nconst inFlightLangs = new Map<string, Promise<Parser | null>>();\n\nexport const DEFAULT_THEMES = [DefaultHighlightStyle.name];\nexport const DEFAULT_LANGS = [\"typescript\", \"javascript\", \"json\", \"python\"];\n\n// Theme registry, seeded with the built-ins (Motion Canvas default + GitHub\n// dark/light). The `theme` prop is a string name or a CodeHighlightStyle object;\n// apps can register additional styles by name and reference them by that name.\nconst themesByName = new Map<string, CodeHighlightStyle>(Object.entries(BUILTIN_THEMES));\n\n/** Register a custom {@link CodeHighlightStyle} so Code nodes can reference it by name. */\nexport function registerCodeTheme(style: CodeHighlightStyle): void {\n themesByName.set(style.name, style);\n}\n\n/**\n * Resolve a theme reference (a registered name, or a CodeHighlightStyle object)\n * to a concrete style. Unknown names fall back to the Motion Canvas default so a\n * stray theme string never throws.\n */\nexport function resolveTheme(theme: CodeTheme): CodeHighlightStyle {\n if (typeof theme !== \"string\") return theme;\n return themesByName.get(theme) ?? DefaultHighlightStyle;\n}\n\n/** Has `language` finished loading its parser? */\nfunction isLanguageLoaded(language: string): boolean {\n return loadedParsers.has(resolveLanguageKey(language));\n}\n\n/**\n * Load the parsers for `langs` (themes are synchronous, so they're a no-op here\n * but kept in the signature for API parity with the former Shiki implementation).\n * Resolves once every requested language's parser is available. Unknown languages\n * resolve without loading anything — {@link canHighlight} stays false for them and\n * the node renders them as plain text.\n */\nexport function ensureHighlighter(\n _themes: string[] = DEFAULT_THEMES,\n langs: string[] = DEFAULT_LANGS,\n): Promise<void> {\n const jobs: Promise<unknown>[] = [];\n\n for (const lang of langs) {\n const key = resolveLanguageKey(lang);\n if (loadedParsers.has(key)) continue;\n\n const loader = LANGUAGE_LOADERS[key];\n if (!loader) continue; // unsupported language; leave it unhighlighted\n\n let job = inFlightLangs.get(key);\n if (!job) {\n job = loader()\n .then(parser => {\n loadedParsers.set(key, parser);\n return parser;\n })\n .catch(() => null)\n .finally(() => inFlightLangs.delete(key));\n inFlightLangs.set(key, job);\n }\n jobs.push(job);\n }\n\n return jobs.length === 0 ? Promise.resolve() : Promise.all(jobs).then(() => undefined);\n}\n\n/**\n * Load a syntax-highlight language into the shared highlighter. Exposed so a Code\n * node's asset loader can run it on the timeline, and so apps can preload\n * languages explicitly. Returns once the parser is ready (or immediately for an\n * unsupported language). The `theme` arg is accepted for backwards compatibility.\n */\nexport async function loadCodeLanguage(language: string, _theme?: string): Promise<void> {\n await ensureHighlighter(DEFAULT_THEMES, [language]);\n}\n\nexport async function initSyntaxHighlighter(\n themes: string[] = DEFAULT_THEMES,\n langs: string[] = DEFAULT_LANGS,\n): Promise<void> {\n await ensureHighlighter(themes, langs);\n}\n\n/** True once `language` can be tokenized (its parser has loaded). */\nexport function canHighlight(language: string, _theme?: CodeTheme): boolean {\n return isLanguageLoaded(language);\n}\n\n/** A parsed-and-prepared highlight cache for one snapshot of code. */\ninterface HighlightCache {\n tree: Tree;\n code: string;\n // `${from}:${to}` node id → color, exactly as Motion Canvas keys it.\n colorLookup: Map<string, string>;\n}\n\nfunction nodeId(from: number, to: number): string {\n return `${from}:${to}`;\n}\n\n/**\n * Parse `code` and build the per-node color map. Mirrors Motion Canvas's\n * `LezerHighlighter.prepare`: for each highlighted range, walk the subtree and\n * record the color against every contained node's `from:to` id, so a later\n * {@link highlightAt} lookup by `resolveInner` index finds it.\n */\nfunction prepareHighlight(code: string, language: string, style: CodeHighlightStyle): HighlightCache | null {\n const parser = loadedParsers.get(resolveLanguageKey(language));\n if (!parser) return null;\n\n const tree = parser.parse(code);\n const highlighter = compileStyle(style);\n const colorLookup = new Map<string, string>();\n\n highlightTree(tree, highlighter, (from, to, classes) => {\n // tagHighlighter emits our color string as the \"class\".\n const color = classes;\n if (!color) return;\n const cursor = tree.cursorAt(from, 1);\n do {\n colorLookup.set(nodeId(cursor.from, cursor.to), color);\n } while (cursor.next() && cursor.to <= to);\n });\n\n return { tree, code, colorLookup };\n}\n\n/**\n * Resolve the color and run length for the token starting at `index`. Mirrors\n * Motion Canvas's `LezerHighlighter.highlight`: resolve the innermost node, look\n * up its color, and report how far the run extends (`skipAhead`).\n */\nfunction highlightAt(index: number, cache: HighlightCache): { color: string | null; skipAhead: number } {\n const node = cache.tree.resolveInner(index, 1);\n const color = cache.colorLookup.get(nodeId(node.from, node.to));\n if (color) {\n return { color, skipAhead: node.to - index };\n }\n // No color: skip past a childless (leaf) node so we don't re-resolve every char.\n const skipAhead = node.firstChild ? 0 : node.to - index;\n return { color: null, skipAhead };\n}\n\n/**\n * Split `code` into structural tokens at parse-tree leaf boundaries, including the\n * gaps between leaves (whitespace/punctuation). Mirrors Motion Canvas's\n * `LezerHighlighter.tokenize`; this segmentation is what the token-level\n * append/insert/erase/replace animations diff against, so matching it keeps the\n * animation behavior identical to Motion Canvas.\n */\nfunction tokenizeCode(code: string, language: string): string[] {\n const parser = loadedParsers.get(resolveLanguageKey(language));\n if (!parser) return [code];\n\n const tree = parser.parse(code);\n const cursor = tree.cursor();\n const tokens: string[] = [];\n let current = 0;\n\n do {\n if (!cursor.node.firstChild) {\n if (cursor.from > current) tokens.push(code.slice(current, cursor.from));\n if (cursor.from < cursor.to) tokens.push(code.slice(cursor.from, cursor.to));\n current = cursor.to;\n }\n } while (cursor.next());\n\n if (current < code.length) tokens.push(code.slice(current));\n return tokens;\n}\n\n/**\n * Tokenize `code` into per-line, colored tokens — the contract the rest of the\n * Code component consumes. Combines {@link tokenizeCode} (structural split) with\n * {@link highlightAt} (per-run color), then breaks tokens at newlines so each\n * output line is a row of single-line, single-color tokens.\n *\n * Callers must have checked {@link canHighlight} first; this returns a single\n * uncolored line per source line if the parser isn't loaded.\n */\nexport function highlightToTokens(\n code: string,\n language: string,\n theme: CodeTheme = DefaultHighlightStyle,\n): { content: string; color?: string }[][] {\n const style = resolveTheme(theme);\n const cache = prepareHighlight(code, language, style);\n\n const lines: { content: string; color?: string }[][] = [[]];\n const pushLine = () => lines.push([]);\n\n if (!cache) {\n // Parser unavailable: one uncolored token per source line.\n return code.split(\"\\n\").map(line => [{ content: line, color: style.defaultColor }]);\n }\n\n // Walk the structural tokens, coloring each by its starting index and\n // splitting on newlines so no token spans rows.\n let index = 0;\n for (const token of tokenizeCode(code, language)) {\n let offset = 0;\n while (offset < token.length) {\n const { color, skipAhead } = highlightAt(index + offset, cache);\n // Extend the run as far as the highlight says, but never past this\n // structural token and never across a newline (lines are drawn rows).\n let runEnd = offset + Math.max(skipAhead, 1);\n if (runEnd > token.length) runEnd = token.length;\n const newlineRel = token.indexOf(\"\\n\", offset);\n if (newlineRel !== -1 && newlineRel < runEnd) runEnd = newlineRel;\n\n const slice = token.slice(offset, runEnd);\n if (slice.length > 0) {\n lines[lines.length - 1].push({ content: slice, color: color ?? style.defaultColor });\n }\n\n if (newlineRel === runEnd) {\n pushLine();\n runEnd += 1; // consume the newline itself\n }\n offset = runEnd;\n }\n index += token.length;\n }\n\n return lines;\n}\n", "import { canHighlight, highlightToTokens } from \"./highlight\";\nimport { CodeTheme } from \"./style\";\n\n// IdToken is our internal wrapper around a highlighter token that gives every\n// token a stable identity across line insertions/removals. All animation state\n// is keyed by `id`, so concurrent edits at different positions don't clobber\n// each other while one is mid-animation.\nexport interface IdToken {\n id: number;\n content: string;\n color?: string;\n}\n\n// IdLine wraps a row of tokens with its own stable id, so per-line animations\n// (heightScale during insert/remove) survive line insertions/removals.\nexport interface IdLine {\n id: number;\n tokens: IdToken[];\n}\n\n// Module-global, monotonic id counters shared across all Code nodes — ids only\n// need to be unique within a node, but a global counter is the simplest way to\n// guarantee that and never collides.\nlet nextTokenId = 1;\nlet nextLineId = 1;\n\nexport function makeIdToken(content: string, color?: string): IdToken {\n return { id: nextTokenId++, content, color };\n}\n\nexport function makeIdLine(tokens: IdToken[]): IdLine {\n return { id: nextLineId++, tokens };\n}\n\nexport function tokenizeCodeToIdLines(code: string, language: string, theme: CodeTheme): IdLine[] {\n // Until the language parser has loaded (it streams in via the asset loader),\n // fall back to flat, uncolored lines instead of throwing on an unloaded\n // grammar. The node re-tokenizes once the load completes.\n if (!canHighlight(language, theme)) {\n return code.split('\\n').map(line => makeIdLine([makeIdToken(line, '#d1d5db')]));\n }\n return highlightToTokens(code, language, theme).map(line =>\n makeIdLine(line.map(tok => makeIdToken(tok.content, tok.color))),\n );\n}\n\n/**\n * Split `tokens` at character offset `col` (relative to the joined content of\n * the tokens). If `col` falls inside a token, that token is split into two new\n * tokens with fresh ids (the original id is discarded — it didn't exist as a\n * single unit on either side of the cut).\n */\nexport function splitTokensAt(\n tokens: IdToken[],\n col: number,\n): { before: IdToken[]; after: IdToken[] } {\n const before: IdToken[] = [];\n const after: IdToken[] = [];\n let off = 0;\n for (const tok of tokens) {\n const tStart = off;\n const tEnd = off + tok.content.length;\n if (tEnd <= col) {\n before.push(tok);\n } else if (tStart >= col) {\n after.push(tok);\n } else {\n const cut = col - tStart;\n before.push(makeIdToken(tok.content.slice(0, cut), tok.color));\n after.push(makeIdToken(tok.content.slice(cut), tok.color));\n }\n off = tEnd;\n }\n return { before, after };\n}\n", "import { MeasureContext2D, RenderContext2D } from \"@motionscript/core\";\r\n\r\n/**\r\n * Memoizes text-advance measurements for the Code node.\r\n *\r\n * `scope.measureText` runs a full CanvasKit/Skia paragraph layout (shaping plus\r\n * font object alloc/free) on every call — far too expensive to repeat for every\r\n * token on every frame. A token's advance only depends on (content, fontSize,\r\n * fontFamily, letterSpacing); content changes only at edit time and the font\r\n * inputs only change via props, so the same string measures to the same width\r\n * across the many static frames the player renders.\r\n *\r\n * fontSize + fontFamily fold into a `signature` checked once per frame: when it\r\n * changes the cache clears. letterSpacing folds into the per-entry key so the\r\n * letter-spaced token advances and the unspaced gutter advances coexist in one\r\n * map. The signature check (not the node's `set()`) is the invalidation point on\r\n * purpose — animated/bound font props (`to({ fontSize })`, `fontSize={() => …}`)\r\n * bypass `set()` but still flow through the per-frame signature comparison.\r\n *\r\n * NOTE: the two `measureText` implementations in packages/browser/src\r\n * (measure-scope.ts and render-context.ts) are uncached and shape+free fonts on\r\n * every call. A shared cache there would speed up all text nodes, but it touches\r\n * shared infra and cross-font correctness, so it's intentionally left out here.\r\n */\r\nexport class TokenAdvanceCache {\r\n private cache = new Map<string, number>();\r\n private sig = \"\";\r\n\r\n /** Signature of the font inputs that, when changed, invalidate every entry. */\r\n signature(fontSize: number, fontFamily: string): string {\r\n return `${fontSize}|${fontFamily}`;\r\n }\r\n\r\n /** Clear the cache if the font signature changed since the last frame. */\r\n sync(sig: string): void {\r\n if (sig !== this.sig) {\r\n this.cache.clear();\r\n this.sig = sig;\r\n }\r\n }\r\n\r\n /**\r\n * Advance width of `content`, measured once per unique (content,\r\n * letterSpacing) pair under the current font signature. Call `sync()` first\r\n * each frame so a font change is reflected.\r\n */\r\n advance(\r\n scope: MeasureContext2D | RenderContext2D,\r\n content: string,\r\n fontSize: number,\r\n fontFamily: string,\r\n letterSpacing: number,\r\n ): number {\r\n if (content.length === 0) return 0;\r\n const key = `${content}\u0000${letterSpacing}`;\r\n const hit = this.cache.get(key);\r\n if (hit !== undefined) return hit;\r\n // Only the advance width is cached: a code block's geometry is a column\r\n // of x offsets, and its line height comes from the node's own\r\n // `lineHeight` prop rather than from the shaper.\r\n const { width } = scope.measureText(content, fontSize, fontFamily, undefined, letterSpacing);\r\n this.cache.set(key, width);\r\n return width;\r\n }\r\n}\r\n", "/**\n * A range over a code document. Lines and columns are 1-indexed.\n *\n * `endLine`/`endCol` may be `Infinity` to mean \"to the end of the line\" or\n * \"to the end of the document\".\n */\nexport interface CodeRange {\n startLine: number;\n startCol: number;\n endLine: number;\n endCol: number;\n}\n\n/**\n * Create a range that spans `length` characters starting at (line, col).\n * If `length` is omitted, the range extends to the end of the line.\n *\n * Example:\n * word(1, 3, 3) // line 1, column 3, spans 3 characters\n * word(1, 3) // line 1, column 3, to end of line\n */\nexport function word(line: number, col: number, length?: number): CodeRange {\n return {\n startLine: line,\n startCol: col,\n endLine: line,\n endCol: length === undefined ? Infinity : col + length,\n };\n}\n\n/**\n * Create a range that covers entire lines from `from` to `to` (inclusive).\n * If `to` is omitted, only line `from` is covered.\n *\n * Example:\n * lines(1, 3) // lines 1 through 3\n * lines(2) // line 2 only\n */\nexport function lines(from: number, to?: number): CodeRange {\n return {\n startLine: from,\n startCol: 1,\n endLine: to ?? from,\n endCol: Infinity,\n };\n}\n\n\n\n/**\n * Immutable, chainable, iterable builder over multiple ranges — mirrors\n * {@link FillChain}/`Fills` in `@motionscript/core`. Every method returns a\n * *new* chain with one more range appended, so the next range-kind method is\n * called directly rather than through a separate combinator:\n *\n * @example\n * CodeRanges.lines(2).word(5, 1, 3) // a 2-range selection, one expression\n */\nexport class CodeRangeChain {\n constructor(public readonly list: CodeRange[] = []) { }\n\n lines(from: number, to?: number): CodeRangeChain {\n return new CodeRangeChain([...this.list, lines(from, to)]);\n }\n\n word(line: number, col: number, length?: number): CodeRangeChain {\n return new CodeRangeChain([...this.list, word(line, col, length)]);\n }\n\n *[Symbol.iterator](): Iterator<CodeRange> {\n yield* this.list;\n }\n}\n\n/**\n * Entry point for building multi-range selections fluently — an empty\n * {@link CodeRangeChain}, so `CodeRanges.lines(2)` *is* `new\n * CodeRangeChain().lines(2)`. `word`/`lines` stay directly importable and\n * usable standalone for a single range; reach for `CodeRanges` when a call\n * needs more than one.\n */\nexport const CodeRanges = new CodeRangeChain();\n\n/**\n * Convert a CodeRange to absolute character offsets [start, end) in the joined\n * source text, given the per-line lengths of that source. Lines/columns are\n * clamped into the document so out-of-bounds inputs produce a valid range.\n */\nexport function rangeToCharOffsets(\n r: CodeRange,\n lineLengths: number[],\n): { start: number; end: number } {\n if (lineLengths.length === 0) return { start: 0, end: 0 };\n\n // Cumulative line starts in the joined string (lines joined with '\\n').\n const lineStart: number[] = new Array(lineLengths.length);\n let acc = 0;\n for (let i = 0; i < lineLengths.length; i++) {\n lineStart[i] = acc;\n acc += lineLengths[i] + 1; // +1 for the joining newline\n }\n const docEnd = acc > 0 ? acc - 1 : 0; // strip trailing newline\n\n const clampLine = (n: number) =>\n Math.max(0, Math.min(lineLengths.length - 1, n - 1)); // to 0-indexed\n\n // A range read back from JSON holds `Infinity` as `null`; both mean \"to the end\".\n const endLine = r.endLine ?? Infinity;\n const endCol = r.endCol ?? Infinity;\n\n const sLine = clampLine(r.startLine);\n const eLine = clampLine(endLine);\n const sColMax = lineLengths[sLine];\n const eColMax = lineLengths[eLine];\n\n // Columns are 1-indexed; clamp to [1, lineLength+1] so endCol can sit past\n // the last char (one-past-end).\n const sCol = Math.max(0, Math.min(sColMax, r.startCol - 1));\n const eCol = endCol === Infinity\n ? eColMax\n : Math.max(0, Math.min(eColMax, endCol - 1));\n\n const start = Math.min(docEnd, lineStart[sLine] + sCol);\n const end = Math.min(docEnd, lineStart[eLine] + eCol);\n return { start: Math.min(start, end), end: Math.max(start, end) };\n}\n\n/**\n * Convert [start, end) character offsets in joined source to a CodeRange.\n * Lines/cols in the returned range are 1-indexed.\n */\nexport function charOffsetsToRange(\n start: number,\n end: number,\n lineLengths: number[],\n): CodeRange {\n let off = 0;\n let startLine = 1, startCol = 1, endLine = 1, endCol = 1;\n let foundStart = false;\n for (let li = 0; li < lineLengths.length; li++) {\n const lineStart = off;\n const lineEnd = off + lineLengths[li];\n if (!foundStart && start >= lineStart && start <= lineEnd) {\n startLine = li + 1;\n startCol = (start - lineStart) + 1;\n foundStart = true;\n }\n if (end >= lineStart && end <= lineEnd) {\n endLine = li + 1;\n endCol = (end - lineStart) + 1;\n break;\n }\n off = lineEnd + 1;\n }\n return { startLine, startCol, endLine, endCol };\n}\n", "import { Code } from \"./node\";\n\n/**\n * Every node type this package publishes.\n *\n * A host registers these by handing the array to an engine —\n * `new Engine(platform, { nodes: NODES })` — which reads each class's `@node()`\n * key. The decorator only *declares* that key: nothing is registered by the mere\n * act of importing a module, so a registry holds exactly what its owner asked\n * for and two engines in one process can differ about it.\n *\n * Listing class **values** is also what survives bundling. This package declares\n * `sideEffects: false`, which licenses a bundler to drop a module nothing\n * imports a value from, and a document names a node type by string — so\n * `import \"./node\"` for its side effect is exactly what gets shaken out, where an\n * array of bindings is a data dependency that cannot be.\n */\nexport const NODES = [Code];\n"],
5
+ "mappings": "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",
6
+ "names": ["node", "Clip", "Node2D", "property", "insetsOps", "lerpNumber", "command", "driveCommand", "sampleCurve", "keys", "values", "p", "span", "local", "windowProgress", "w", "a", "b", "smoothstep", "t", "makeAnim", "id", "curve", "phasesFor", "hasRemoved", "hasAdded", "defaultCodePhases", "moveProgress", "edit", "resolveTokenStates", "tokenLines", "transitions", "highlightDimOpacity", "highlightedIds", "out", "dim", "line", "tok", "tr", "at", "metricsSignature", "m", "p", "gutterGapOf", "scope", "cache", "gutterOf", "lineCount", "gap", "sample", "layoutCode", "lines", "lineH", "gutterGap", "gutter", "widths", "maxLineWidth", "i", "w", "tok", "innerW", "innerH", "blockW", "blockH", "startX", "startY", "tokens", "lineY", "lineIndex", "centerY", "x", "Graphics2D", "parseColor", "lerpNumber", "DEFAULT_TOKEN_COLOR", "LINE_NUMBER_COLOR", "colorCache", "tokenColor", "css", "hit", "parseColor", "lerpColor", "from", "to", "t", "lineHighlightOpacity", "line", "stateById", "highlightActive", "min", "tok", "op", "drawCode", "draw", "state", "tokenLines", "metrics", "advanceCache", "edit", "dim", "progress", "pOut", "windowProgress", "pMove", "moveProgress", "pIn", "blockW", "lerpNumber", "gutter", "gutterGap", "numberRight", "drawNumber", "label", "y", "opacity", "labelW", "Graphics2D", "drawToken", "text", "x", "color", "width", "i", "toIndex", "toY", "entering", "rowY", "rowAlpha", "fromIndex", "token", "box", "alpha", "was", "drawLineNumbers", "toCount", "fromCount", "inFrom", "inTo", "dimming", "tokenWeight", "tok", "lineWeight", "line", "sum", "similarity", "a", "b", "wa", "wb", "bag", "common", "w", "left", "align", "aLength", "bLength", "score", "width", "dp", "pairScore", "i", "j", "s", "skipA", "skipB", "best", "paired", "pairs", "alignTokens", "from", "to", "diffCode", "removedIds", "addedIds", "newLineIds", "fromColorById", "linePairs", "pairedTo", "pairedFrom", "fi", "ti", "fromLine", "toLine", "matched", "keptFrom", "keptTo", "oldTok", "newTok", "DefaultHighlightStyle", "tags", "GithubDarkStyle", "GithubLightStyle", "VscodeDarkStyle", "VscodeLightStyle", "BUILTIN_THEMES", "compiledByName", "compileStyle", "style", "cached", "highlighter", "tagHighlighter", "rule", "LANGUAGE_LOADERS", "LANGUAGE_ALIASES", "resolveLanguageKey", "language", "key", "loadedParsers", "inFlightLangs", "DEFAULT_THEMES", "DefaultHighlightStyle", "DEFAULT_LANGS", "themesByName", "BUILTIN_THEMES", "registerCodeTheme", "style", "resolveTheme", "theme", "isLanguageLoaded", "ensureHighlighter", "_themes", "langs", "jobs", "lang", "loader", "job", "parser", "loadCodeLanguage", "_theme", "initSyntaxHighlighter", "themes", "canHighlight", "nodeId", "from", "to", "prepareHighlight", "code", "tree", "highlighter", "compileStyle", "colorLookup", "highlightTree", "classes", "color", "cursor", "highlightAt", "index", "cache", "node", "tokenizeCode", "tokens", "current", "highlightToTokens", "lines", "pushLine", "line", "token", "offset", "skipAhead", "runEnd", "newlineRel", "slice", "nextTokenId", "nextLineId", "makeIdToken", "content", "color", "makeIdLine", "tokens", "tokenizeCodeToIdLines", "code", "language", "theme", "canHighlight", "highlightToTokens", "line", "tok", "TokenAdvanceCache", "fontSize", "fontFamily", "sig", "scope", "content", "letterSpacing", "key", "hit", "width", "word", "line", "col", "length", "lines", "from", "to", "CodeRangeChain", "_CodeRangeChain", "list", "CodeRanges", "rangeToCharOffsets", "r", "lineLengths", "lineStart", "acc", "i", "docEnd", "clampLine", "n", "endLine", "endCol", "sLine", "eLine", "sColMax", "eColMax", "sCol", "eCol", "start", "end", "charOffsetsToRange", "off", "startLine", "startCol", "foundStart", "li", "lineEnd", "Code", "Node2D", "TokenAdvanceCache", "props", "diffCode", "defaultCodePhases", "ensureHighlighter", "tokenizeCodeToIdLines", "canHighlight", "lines", "assets", "grammarLoader", "i", "tr", "m", "scope", "key", "metricsSignature", "layout", "layoutCode", "edit", "constraints", "wm", "hm", "from", "to", "t", "moveProgress", "innerW", "lerpNumber", "innerH", "resolvedW", "resolvedH", "ctx", "_child", "Clip", "local", "tolerance", "half", "gutter", "left", "args", "code", "position", "source", "offset", "codeRange", "duration", "easing", "start", "end", "rangeToCharOffsets", "atLineStart", "atLineEnd", "driveCommand", "replacement", "dim", "toDim", "matchIds", "normalizeCodeRanges", "fromDim", "hadPrevious", "previousIds", "animTokens", "line", "tok", "wasHighlighted", "isHighlighted", "fromOp", "toOp", "makeAnim", "done", "next", "fromCode", "transition", "running", "index", "tokens", "settle", "rest", "others", "elapsed", "dt", "othersDone", "editDone", "text", "ranges", "lineLens", "idx", "charOffsetsToRange", "acc", "lens", "rawLine", "rawCol", "li", "k", "codeRanges", "result", "offsets", "off", "tStart", "tEnd", "draw", "drawCode", "resolveTokenStates", "__decorate", "property", "DefaultHighlightStyle", "insetsOps", "command", "node", "normalizeCodeRanges", "codeRange", "grammarLoaders", "grammarLoader", "language", "load", "ensureHighlighter", "NODES", "Code"]
7
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@motionscript/code",
3
+ "version": "0.1.0-alpha.0",
4
+ "exports": {
5
+ ".": "dist/browser/index.js"
6
+ },
7
+ "peers": [
8
+ "@motionscript/core"
9
+ ],
10
+ "assets": {}
11
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * A range over a code document. Lines and columns are 1-indexed.
3
+ *
4
+ * `endLine`/`endCol` may be `Infinity` to mean "to the end of the line" or
5
+ * "to the end of the document".
6
+ */
7
+ export interface CodeRange {
8
+ startLine: number;
9
+ startCol: number;
10
+ endLine: number;
11
+ endCol: number;
12
+ }
13
+ /**
14
+ * Create a range that spans `length` characters starting at (line, col).
15
+ * If `length` is omitted, the range extends to the end of the line.
16
+ *
17
+ * Example:
18
+ * word(1, 3, 3) // line 1, column 3, spans 3 characters
19
+ * word(1, 3) // line 1, column 3, to end of line
20
+ */
21
+ export declare function word(line: number, col: number, length?: number): CodeRange;
22
+ /**
23
+ * Create a range that covers entire lines from `from` to `to` (inclusive).
24
+ * If `to` is omitted, only line `from` is covered.
25
+ *
26
+ * Example:
27
+ * lines(1, 3) // lines 1 through 3
28
+ * lines(2) // line 2 only
29
+ */
30
+ export declare function lines(from: number, to?: number): CodeRange;
31
+ /**
32
+ * Immutable, chainable, iterable builder over multiple ranges — mirrors
33
+ * {@link FillChain}/`Fills` in `@motionscript/core`. Every method returns a
34
+ * *new* chain with one more range appended, so the next range-kind method is
35
+ * called directly rather than through a separate combinator:
36
+ *
37
+ * @example
38
+ * CodeRanges.lines(2).word(5, 1, 3) // a 2-range selection, one expression
39
+ */
40
+ export declare class CodeRangeChain {
41
+ readonly list: CodeRange[];
42
+ constructor(list?: CodeRange[]);
43
+ lines(from: number, to?: number): CodeRangeChain;
44
+ word(line: number, col: number, length?: number): CodeRangeChain;
45
+ [Symbol.iterator](): Iterator<CodeRange>;
46
+ }
47
+ /**
48
+ * Entry point for building multi-range selections fluently — an empty
49
+ * {@link CodeRangeChain}, so `CodeRanges.lines(2)` *is* `new
50
+ * CodeRangeChain().lines(2)`. `word`/`lines` stay directly importable and
51
+ * usable standalone for a single range; reach for `CodeRanges` when a call
52
+ * needs more than one.
53
+ */
54
+ export declare const CodeRanges: CodeRangeChain;
55
+ /**
56
+ * Convert a CodeRange to absolute character offsets [start, end) in the joined
57
+ * source text, given the per-line lengths of that source. Lines/columns are
58
+ * clamped into the document so out-of-bounds inputs produce a valid range.
59
+ */
60
+ export declare function rangeToCharOffsets(r: CodeRange, lineLengths: number[]): {
61
+ start: number;
62
+ end: number;
63
+ };
64
+ /**
65
+ * Convert [start, end) character offsets in joined source to a CodeRange.
66
+ * Lines/cols in the returned range are 1-indexed.
67
+ */
68
+ export declare function charOffsetsToRange(start: number, end: number, lineLengths: number[]): CodeRange;
69
+ //# sourceMappingURL=code-range.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-range.d.ts","sourceRoot":"","sources":["../src/code-range.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,SAAS;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAO1E;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAO1D;AAID;;;;;;;;GAQG;AACH,qBAAa,cAAc;aACK,IAAI,EAAE,SAAS,EAAE;gBAAjB,IAAI,GAAE,SAAS,EAAO;IAElD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,GAAG,cAAc;IAIhD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,cAAc;IAI/D,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC;CAG5C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,gBAAuB,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAC9B,CAAC,EAAE,SAAS,EACZ,WAAW,EAAE,MAAM,EAAE,GACtB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAkChC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAC9B,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,EACX,WAAW,EAAE,MAAM,EAAE,GACtB,SAAS,CAoBX"}
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Create a range that spans `length` characters starting at (line, col).
3
+ * If `length` is omitted, the range extends to the end of the line.
4
+ *
5
+ * Example:
6
+ * word(1, 3, 3) // line 1, column 3, spans 3 characters
7
+ * word(1, 3) // line 1, column 3, to end of line
8
+ */
9
+ export function word(line, col, length) {
10
+ return {
11
+ startLine: line,
12
+ startCol: col,
13
+ endLine: line,
14
+ endCol: length === undefined ? Infinity : col + length,
15
+ };
16
+ }
17
+ /**
18
+ * Create a range that covers entire lines from `from` to `to` (inclusive).
19
+ * If `to` is omitted, only line `from` is covered.
20
+ *
21
+ * Example:
22
+ * lines(1, 3) // lines 1 through 3
23
+ * lines(2) // line 2 only
24
+ */
25
+ export function lines(from, to) {
26
+ return {
27
+ startLine: from,
28
+ startCol: 1,
29
+ endLine: to ?? from,
30
+ endCol: Infinity,
31
+ };
32
+ }
33
+ /**
34
+ * Immutable, chainable, iterable builder over multiple ranges — mirrors
35
+ * {@link FillChain}/`Fills` in `@motionscript/core`. Every method returns a
36
+ * *new* chain with one more range appended, so the next range-kind method is
37
+ * called directly rather than through a separate combinator:
38
+ *
39
+ * @example
40
+ * CodeRanges.lines(2).word(5, 1, 3) // a 2-range selection, one expression
41
+ */
42
+ export class CodeRangeChain {
43
+ list;
44
+ constructor(list = []) {
45
+ this.list = list;
46
+ }
47
+ lines(from, to) {
48
+ return new CodeRangeChain([...this.list, lines(from, to)]);
49
+ }
50
+ word(line, col, length) {
51
+ return new CodeRangeChain([...this.list, word(line, col, length)]);
52
+ }
53
+ *[Symbol.iterator]() {
54
+ yield* this.list;
55
+ }
56
+ }
57
+ /**
58
+ * Entry point for building multi-range selections fluently — an empty
59
+ * {@link CodeRangeChain}, so `CodeRanges.lines(2)` *is* `new
60
+ * CodeRangeChain().lines(2)`. `word`/`lines` stay directly importable and
61
+ * usable standalone for a single range; reach for `CodeRanges` when a call
62
+ * needs more than one.
63
+ */
64
+ export const CodeRanges = new CodeRangeChain();
65
+ /**
66
+ * Convert a CodeRange to absolute character offsets [start, end) in the joined
67
+ * source text, given the per-line lengths of that source. Lines/columns are
68
+ * clamped into the document so out-of-bounds inputs produce a valid range.
69
+ */
70
+ export function rangeToCharOffsets(r, lineLengths) {
71
+ if (lineLengths.length === 0)
72
+ return { start: 0, end: 0 };
73
+ // Cumulative line starts in the joined string (lines joined with '\n').
74
+ const lineStart = new Array(lineLengths.length);
75
+ let acc = 0;
76
+ for (let i = 0; i < lineLengths.length; i++) {
77
+ lineStart[i] = acc;
78
+ acc += lineLengths[i] + 1; // +1 for the joining newline
79
+ }
80
+ const docEnd = acc > 0 ? acc - 1 : 0; // strip trailing newline
81
+ const clampLine = (n) => Math.max(0, Math.min(lineLengths.length - 1, n - 1)); // to 0-indexed
82
+ // A range read back from JSON holds `Infinity` as `null`; both mean "to the end".
83
+ const endLine = r.endLine ?? Infinity;
84
+ const endCol = r.endCol ?? Infinity;
85
+ const sLine = clampLine(r.startLine);
86
+ const eLine = clampLine(endLine);
87
+ const sColMax = lineLengths[sLine];
88
+ const eColMax = lineLengths[eLine];
89
+ // Columns are 1-indexed; clamp to [1, lineLength+1] so endCol can sit past
90
+ // the last char (one-past-end).
91
+ const sCol = Math.max(0, Math.min(sColMax, r.startCol - 1));
92
+ const eCol = endCol === Infinity
93
+ ? eColMax
94
+ : Math.max(0, Math.min(eColMax, endCol - 1));
95
+ const start = Math.min(docEnd, lineStart[sLine] + sCol);
96
+ const end = Math.min(docEnd, lineStart[eLine] + eCol);
97
+ return { start: Math.min(start, end), end: Math.max(start, end) };
98
+ }
99
+ /**
100
+ * Convert [start, end) character offsets in joined source to a CodeRange.
101
+ * Lines/cols in the returned range are 1-indexed.
102
+ */
103
+ export function charOffsetsToRange(start, end, lineLengths) {
104
+ let off = 0;
105
+ let startLine = 1, startCol = 1, endLine = 1, endCol = 1;
106
+ let foundStart = false;
107
+ for (let li = 0; li < lineLengths.length; li++) {
108
+ const lineStart = off;
109
+ const lineEnd = off + lineLengths[li];
110
+ if (!foundStart && start >= lineStart && start <= lineEnd) {
111
+ startLine = li + 1;
112
+ startCol = (start - lineStart) + 1;
113
+ foundStart = true;
114
+ }
115
+ if (end >= lineStart && end <= lineEnd) {
116
+ endLine = li + 1;
117
+ endCol = (end - lineStart) + 1;
118
+ break;
119
+ }
120
+ off = lineEnd + 1;
121
+ }
122
+ return { startLine, startCol, endLine, endCol };
123
+ }
124
+ //# sourceMappingURL=code-range.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-range.js","sourceRoot":"","sources":["../src/code-range.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * A range over a code document. Lines and columns are 1-indexed.\n *\n * `endLine`/`endCol` may be `Infinity` to mean \"to the end of the line\" or\n * \"to the end of the document\".\n */\nexport interface CodeRange {\n startLine: number;\n startCol: number;\n endLine: number;\n endCol: number;\n}\n\n/**\n * Create a range that spans `length` characters starting at (line, col).\n * If `length` is omitted, the range extends to the end of the line.\n *\n * Example:\n * word(1, 3, 3) // line 1, column 3, spans 3 characters\n * word(1, 3) // line 1, column 3, to end of line\n */\nexport function word(line: number, col: number, length?: number): CodeRange {\n return {\n startLine: line,\n startCol: col,\n endLine: line,\n endCol: length === undefined ? Infinity : col + length,\n };\n}\n\n/**\n * Create a range that covers entire lines from `from` to `to` (inclusive).\n * If `to` is omitted, only line `from` is covered.\n *\n * Example:\n * lines(1, 3) // lines 1 through 3\n * lines(2) // line 2 only\n */\nexport function lines(from: number, to?: number): CodeRange {\n return {\n startLine: from,\n startCol: 1,\n endLine: to ?? from,\n endCol: Infinity,\n };\n}\n\n\n\n/**\n * Immutable, chainable, iterable builder over multiple ranges — mirrors\n * {@link FillChain}/`Fills` in `@motionscript/core`. Every method returns a\n * *new* chain with one more range appended, so the next range-kind method is\n * called directly rather than through a separate combinator:\n *\n * @example\n * CodeRanges.lines(2).word(5, 1, 3) // a 2-range selection, one expression\n */\nexport class CodeRangeChain {\n constructor(public readonly list: CodeRange[] = []) { }\n\n lines(from: number, to?: number): CodeRangeChain {\n return new CodeRangeChain([...this.list, lines(from, to)]);\n }\n\n word(line: number, col: number, length?: number): CodeRangeChain {\n return new CodeRangeChain([...this.list, word(line, col, length)]);\n }\n\n *[Symbol.iterator](): Iterator<CodeRange> {\n yield* this.list;\n }\n}\n\n/**\n * Entry point for building multi-range selections fluently — an empty\n * {@link CodeRangeChain}, so `CodeRanges.lines(2)` *is* `new\n * CodeRangeChain().lines(2)`. `word`/`lines` stay directly importable and\n * usable standalone for a single range; reach for `CodeRanges` when a call\n * needs more than one.\n */\nexport const CodeRanges = new CodeRangeChain();\n\n/**\n * Convert a CodeRange to absolute character offsets [start, end) in the joined\n * source text, given the per-line lengths of that source. Lines/columns are\n * clamped into the document so out-of-bounds inputs produce a valid range.\n */\nexport function rangeToCharOffsets(\n r: CodeRange,\n lineLengths: number[],\n): { start: number; end: number } {\n if (lineLengths.length === 0) return { start: 0, end: 0 };\n\n // Cumulative line starts in the joined string (lines joined with '\\n').\n const lineStart: number[] = new Array(lineLengths.length);\n let acc = 0;\n for (let i = 0; i < lineLengths.length; i++) {\n lineStart[i] = acc;\n acc += lineLengths[i] + 1; // +1 for the joining newline\n }\n const docEnd = acc > 0 ? acc - 1 : 0; // strip trailing newline\n\n const clampLine = (n: number) =>\n Math.max(0, Math.min(lineLengths.length - 1, n - 1)); // to 0-indexed\n\n // A range read back from JSON holds `Infinity` as `null`; both mean \"to the end\".\n const endLine = r.endLine ?? Infinity;\n const endCol = r.endCol ?? Infinity;\n\n const sLine = clampLine(r.startLine);\n const eLine = clampLine(endLine);\n const sColMax = lineLengths[sLine];\n const eColMax = lineLengths[eLine];\n\n // Columns are 1-indexed; clamp to [1, lineLength+1] so endCol can sit past\n // the last char (one-past-end).\n const sCol = Math.max(0, Math.min(sColMax, r.startCol - 1));\n const eCol = endCol === Infinity\n ? eColMax\n : Math.max(0, Math.min(eColMax, endCol - 1));\n\n const start = Math.min(docEnd, lineStart[sLine] + sCol);\n const end = Math.min(docEnd, lineStart[eLine] + eCol);\n return { start: Math.min(start, end), end: Math.max(start, end) };\n}\n\n/**\n * Convert [start, end) character offsets in joined source to a CodeRange.\n * Lines/cols in the returned range are 1-indexed.\n */\nexport function charOffsetsToRange(\n start: number,\n end: number,\n lineLengths: number[],\n): CodeRange {\n let off = 0;\n let startLine = 1, startCol = 1, endLine = 1, endCol = 1;\n let foundStart = false;\n for (let li = 0; li < lineLengths.length; li++) {\n const lineStart = off;\n const lineEnd = off + lineLengths[li];\n if (!foundStart && start >= lineStart && start <= lineEnd) {\n startLine = li + 1;\n startCol = (start - lineStart) + 1;\n foundStart = true;\n }\n if (end >= lineStart && end <= lineEnd) {\n endLine = li + 1;\n endCol = (end - lineStart) + 1;\n break;\n }\n off = lineEnd + 1;\n }\n return { startLine, startCol, endLine, endCol };\n}\n"]}