gesso-core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["describe","kind","isObservable","clamp","follow","isObservable","contains","MULTI_CLICK_MS","MULTI_CLICK_SLOP","isObservable","kind","kind","kind","controllers","EMPTY_OPTIONS","numberOr","numberOr","numberOr","isObservable","load","isObservable","MAX_CACHE_ENTRIES","DEFAULT_CAPACITY","defaultFetch","DEFAULT_CAPACITY","parseColor","parseColor","EMPTY"],"sources":["../src/animation/UiEasing.ts","../src/animation/Interpolate.ts","../src/animation/UiAnimation.ts","../src/environment/UiMotion.ts","../src/animation/UiTransition.ts","../src/animation/AnimationDriver.ts","../src/animation/UiSharedElements.ts","../src/animation/UiMotionState.ts","../src/bindings/UiChildrenBinding.ts","../src/bindings/UiEventBinding.ts","../src/composition/UiElement.ts","../src/composition/UiFactory.ts","../src/modifiers/UiModifier.ts","../src/modifiers/interaction.ts","../src/composition/UiComponents.ts","../src/composition/UiEventProps.ts","../src/modifiers/sameArgs.ts","../src/modifiers/UiModifierSet.ts","../src/graph/UiPropertyTransitions.ts","../src/composition/UiGraphBuilder.ts","../src/composition/UiVirtualWindow.ts","../src/composition/UiLazyList.ts","../src/modifiers/breakpoints.ts","../src/composition/UiResponsive.ts","../src/editing/EditingKeymap.ts","../src/environment/EnvironmentNotifier.ts","../src/environment/UiEnvironmentProvider.ts","../src/find/TextFind.ts","../src/find/UiFindController.ts","../src/input/FocusNotifier.ts","../src/input/UiShortcuts.ts","../src/input/UiDragSession.ts","../src/input/UiEditingController.ts","../src/layout/LayoutNotifier.ts","../src/modifiers/bundle.ts","../src/modifiers/decoration.ts","../src/modifiers/focus.ts","../src/modifiers/clickOutside.ts","../src/modifiers/drag.ts","../src/modifiers/drop.ts","../src/modifiers/pinch.ts","../src/modifiers/shortcuts.ts","../src/modifiers/animation.ts","../src/modifiers/media.ts","../src/modifiers/motion.ts","../src/modifiers/video.ts","../src/modifiers/scroll.ts","../src/modifiers/insets.ts","../src/modifiers/themeTokens.ts","../src/rendering/canvas2d/CanvasTextMeasurer.ts","../src/rendering/LayoutInspector.ts","../src/rendering/ImageResolver.ts","../src/rendering/IconRasterizer.ts","../src/rendering/webgpu/WebGPUError.ts","../src/rendering/webgpu/WebGPUDevice.ts","../src/rendering/webgpu/WebGPUShader.ts","../src/rendering/webgpu/WebGPUGlyphAtlas.ts","../src/rendering/BidiRuns.ts","../src/rendering/webgpu/WebGPUGlyphShaper.ts","../src/rendering/webgpu/WebGPUColor.ts","../src/rendering/webgpu/WebGPUSurface.ts","../src/rendering/webgpu/WebGPURenderData.ts","../src/rendering/webgpu/WebGPUPipeline.ts","../src/rendering/webgpu/WebGPUTextureCache.ts","../src/rendering/webgpu/WebGPUGlyphPages.ts","../src/rendering/webgpu/WebGPURenderer.ts","../src/media/Mp3Frames.ts","../src/media/Mp4Demuxer.ts","../src/media/VideoResolver.ts","../src/scheduler/PerformanceMarks.ts","../src/scheduler/UiFrame.ts","../src/scheduler/UiScheduler.ts","../src/scheduler/UiFrameClock.ts","../src/selection/UiSelectionController.ts","../src/semantics/UiSemanticsTree.ts","../src/semantics/UiSemanticsDiff.ts"],"sourcesContent":["/**\n * How a tween's progress maps onto its value.\n *\n * Takes normalised time in [0, 1] and answers normalised progress.\n * An easing may leave the range — an overshooting curve is a valid\n * easing — so nothing downstream clamps what it returns.\n */\nexport type UiEasing = (t: number) => number;\n\n/** Progress equals time. What a spinner and a sweep want. */\nexport const linear: UiEasing = t => t;\n\n/**\n * A cubic Bézier easing, in the CSS `cubic-bezier(x1, y1, x2, y2)`\n * sense: the curve runs from (0, 0) to (1, 1) through the two control\n * points, and the easing is y at the t where x equals the input.\n *\n * Solved by Newton's method with a bisection fallback, which is what\n * every browser engine does; eight iterations is well past the point\n * where the result stops moving in a float.\n */\nexport function cubicBezier(x1: number, y1: number, x2: number, y2: number): UiEasing {\n const ax = 3 * x1 - 3 * x2 + 1;\n const bx = 3 * x2 - 6 * x1;\n const cx = 3 * x1;\n const ay = 3 * y1 - 3 * y2 + 1;\n const by = 3 * y2 - 6 * y1;\n const cy = 3 * y1;\n\n const sampleX = (t: number): number => ((ax * t + bx) * t + cx) * t;\n const sampleY = (t: number): number => ((ay * t + by) * t + cy) * t;\n const slopeX = (t: number): number => (3 * ax * t + 2 * bx) * t + cx;\n\n return (input: number): number => {\n if (input <= 0) {\n return 0;\n }\n if (input >= 1) {\n return 1;\n }\n let t = input;\n for (let i = 0; i < 8; i++) {\n const error = sampleX(t) - input;\n if (Math.abs(error) < 1e-6) {\n return sampleY(t);\n }\n const slope = slopeX(t);\n if (Math.abs(slope) < 1e-6) {\n break;\n }\n t -= error / slope;\n }\n // Newton stalled on a flat segment; bisect instead, which cannot.\n let low = 0;\n let high = 1;\n t = input;\n for (let i = 0; i < 24; i++) {\n const x = sampleX(t);\n if (Math.abs(x - input) < 1e-6) {\n break;\n }\n if (x > input) {\n high = t;\n } else {\n low = t;\n }\n t = (low + high) / 2;\n }\n return sampleY(t);\n };\n}\n\n/**\n * An easing that only takes `count` distinct values.\n *\n * The reason it exists is the `Spinner`: eight blades have eight\n * positions promised the F4 version would keep a\n * turn at eight writes a second rather than sixty. Paired with an\n * animation's `stepMs`, this is how that promise is kept — the value\n * is stepped and the frames are too.\n */\nexport function steps(count: number): UiEasing {\n if (!(count >= 1)) {\n throw new Error(`steps() needs at least one step, got ${count}.`);\n }\n return t => Math.min(count - 1, Math.floor(t * count)) / count;\n}\n\n/**\n * The named curves a motion vocabulary is built from.\n *\n * `standard` is the one to reach for: something entering and leaving\n * at a natural pace. `decelerate` is for something arriving (it starts\n * fast and settles), `accelerate` for something leaving. `emphasized`\n * is `standard` with more of a lean, for the one movement on a screen\n * that should be noticed.\n */\nexport const easings = {\n linear,\n standard: cubicBezier(0.2, 0, 0, 1),\n decelerate: cubicBezier(0, 0, 0, 1),\n accelerate: cubicBezier(0.3, 0, 1, 1),\n emphasized: cubicBezier(0.05, 0.7, 0.1, 1)\n} as const;\n","import type { UiColor } from '../properties/UiColor';\nimport type { UiTransform } from '../properties/UiTransform';\n\n/**\n * Blends two values of one property.\n *\n * `t` is progress, normally in [0, 1] but not necessarily: a spring\n * and an overshooting easing both leave the range, and an interpolator\n * that clamped would flatten exactly the part of the motion that is\n * worth having.\n */\nexport type UiInterpolator<T> = (from: T, to: T, t: number) => T;\n\nexport const interpolateNumber: UiInterpolator<number> = (from, to, t) => from + (to - from) * t;\n\n/**\n * Blends two colours channel by channel, in the [0, 1] float space\n * `UiColor` already uses.\n *\n * Linear RGB rather than a perceptual space: both renderers hand\n * these channels straight to the GPU or to a `fillStyle`, so blending\n * anywhere else would mean converting twice a frame to be slightly\n * more correct about a midpoint nobody looks at for 200 ms.\n */\nexport const interpolateColor: UiInterpolator<UiColor> = (from, to, t) => ({\n r: from.r + (to.r - from.r) * t,\n g: from.g + (to.g - from.g) * t,\n b: from.b + (to.b - from.b) * t,\n a: from.a + (to.a - from.a) * t\n});\n\n/**\n * Blends two transforms field by field, filling identity for whatever\n * either side omits.\n *\n * `x` and `y` are the pivot, not a translation (see `UiTransform`), so\n * blending them moves where the rotation happens rather than moving\n * the node. `translateX` and `translateY` are the pair that moves it.\n */\nexport const interpolateTransform: UiInterpolator<Partial<UiTransform>> = (from, to, t) => ({\n x: interpolateNumber(from.x ?? 0, to.x ?? 0, t),\n y: interpolateNumber(from.y ?? 0, to.y ?? 0, t),\n translateX: interpolateNumber(from.translateX ?? 0, to.translateX ?? 0, t),\n translateY: interpolateNumber(from.translateY ?? 0, to.translateY ?? 0, t),\n scaleX: interpolateNumber(from.scaleX ?? 1, to.scaleX ?? 1, t),\n scaleY: interpolateNumber(from.scaleY ?? 1, to.scaleY ?? 1, t),\n rotation: interpolateNumber(from.rotation ?? 0, to.rotation ?? 0, t)\n});\n\nfunction isColor(value: object): boolean {\n const candidate = value as Partial<UiColor>;\n return (\n typeof candidate.r === 'number' &&\n typeof candidate.g === 'number' &&\n typeof candidate.b === 'number' &&\n typeof candidate.a === 'number'\n );\n}\n\nconst TRANSFORM_FIELDS = ['x', 'y', 'translateX', 'translateY', 'scaleX', 'scaleY', 'rotation'] as const;\n\nfunction isTransform(value: object): boolean {\n const candidate = value as Record<string, unknown>;\n let seen = 0;\n for (const key of Object.keys(candidate)) {\n if (!(TRANSFORM_FIELDS as readonly string[]).includes(key)) {\n return false;\n }\n if (typeof candidate[key] !== 'number') {\n return false;\n }\n seen++;\n }\n return seen > 0;\n}\n\n/**\n * The interpolator for a pair of values, or undefined when the type\n * cannot be blended.\n *\n * Three types, and the reason the list is short is that the rest are\n * not numbers in disguise. A `UiLength` may be `percent(50)`, `fr(1)`\n * or `auto`, and blending `auto` into `fr(1)` means nothing until the\n * layout engine has resolved both — an animation that took a length\n * would be choosing a frame's resolved value and writing it back as a\n * fixed one, which is a different layout. A colour that names a\n * palette entry (`'controlAccent'`) is not blendable either: it\n * resolves against the node at paint, and this code has a cell rather\n * than a node. Both cases fall back to writing the value, which is\n * the honest outcome — no animation rather than a wrong one.\n */\nexport function interpolatorFor(from: unknown, to: unknown): UiInterpolator<unknown> | undefined {\n if (typeof from === 'number' && typeof to === 'number') {\n return interpolateNumber as unknown as UiInterpolator<unknown>;\n }\n if (typeof from !== 'object' || from === null || typeof to !== 'object' || to === null) {\n return undefined;\n }\n if (isColor(from) && isColor(to)) {\n return interpolateColor as unknown as UiInterpolator<unknown>;\n }\n if (isTransform(from) && isTransform(to)) {\n return interpolateTransform as unknown as UiInterpolator<unknown>;\n }\n return undefined;\n}\n","import { Subject, type Observable } from 'rxjs';\n\nimport { interpolatorFor, type UiInterpolator } from './Interpolate';\nimport { easings, type UiEasing } from './UiEasing';\nimport type { UiSpringSpec } from '../environment/UiMotion';\n\n/**\n * Anything an animation can drive.\n *\n * A cell, not a node: `State<T>` from the framework satisfies this by\n * having a `value` getter and setter, and so does the small adapter a\n * `transition` prop puts in front of a node's property. Keeping the\n * driver on this side of the line is what lets one mechanism serve\n * `animate(cell, …)`, the declarative `transition` prop and the layout\n * modifier without any of them knowing about the others.\n */\nexport interface AnimatedCell<T> {\n value: T;\n}\n\n/**\n * What reduced motion does to one animation.\n *\n * `snap` — the default and the right answer nearly always: the cell\n * jumps to its target and the animation never runs, so the end state\n * is identical and no time passes.\n *\n * `keep` — run anyway, for the animations where the movement *is* the\n * information. A `Spinner` that stops turning is not a calmer spinner,\n * it is a spinner that says work has stopped; WCAG 2.3.3 is about\n * motion triggered by interaction, and a busy indicator is not that.\n * Reach for it only when standing still would state something false.\n */\nexport type UiReducedMotionPolicy = 'snap' | 'keep';\n\nexport interface UiAnimationOptions {\n /**\n * Sample this animation at most this often, in milliseconds. Zero,\n * the default, means every frame.\n *\n * This is what keeps the media tier's promise about the `Spinner`:\n * eight blades have eight positions, so with `stepMs` at an eighth\n * of the turn the runtime wakes eight times a second rather than\n * sixty, and the driver reports it through `nextTickAt` so the\n * scheduler sleeps in between rather than spinning.\n */\n stepMs?: number;\n /** What a reduced-motion preference does to it. Defaults to `snap`. */\n reducedMotion?: UiReducedMotionPolicy;\n /**\n * Wait this many milliseconds before the first sample.\n *\n * The cell is left alone while it waits — not written with its\n * starting value — so a delayed animation costs nothing until it\n * begins, and `nextTickAt` reports the moment it will, which is what\n * keeps the scheduler asleep in between rather than spinning through\n * the delay.\n *\n * This is where staggering comes from: a list that enters with\n * `delay: index * 40` is forty milliseconds of offset per row and no\n * orchestration mechanism at all.\n */\n delay?: number;\n}\n\nexport interface UiTweenOptions extends UiAnimationOptions {\n /** Milliseconds. Zero writes the target and completes without a frame. */\n duration: number;\n easing?: UiEasing;\n /** Restart from the beginning forever. A repeating tween is never done. */\n repeat?: boolean;\n}\n\nexport interface UiSpringOptions extends UiAnimationOptions {\n spring: UiSpringSpec;\n /** Units per second at the start; a gesture hands over the velocity it ended with. */\n velocity?: number;\n /** How close counts as arrived. */\n restDelta?: number;\n}\n\n/**\n * One running animation.\n *\n * Subclasses differ only in `sample`. Everything else — the cell, the\n * observable, when the next sample is due, and not writing a value\n * that has not changed — is the same for a tween and for a spring, and\n * is the part the driver depends on.\n */\n/** The most a sample may be taken early, in milliseconds. */\nconst MAX_DUE_SLACK_MS = 4;\n\nexport abstract class UiAnimation<T> {\n /** When the first tick reached it, on the frame clock. */\n protected startedAt = 0;\n private begun = false;\n private readonly subject = new Subject<T>();\n private lastSampledAt: number | null = null;\n private lastWritten: T | undefined;\n private finished = false;\n\n protected constructor(\n readonly cell: AnimatedCell<T>,\n private readonly stepMs: number,\n readonly reducedMotionPolicy: UiReducedMotionPolicy,\n private readonly delayMs = 0\n ) {}\n\n /**\n * Emits every value written to the cell, and completes when the\n * animation stops driving it — whether it arrived, was superseded by\n * another animation on the same cell, or was stopped. A caller that\n * needs to know which of the three reads the cell.\n */\n get values(): Observable<T> {\n return this.subject.asObservable();\n }\n\n get isFinished(): boolean {\n return this.finished;\n }\n\n /** When this animation next wants a frame. */\n /**\n * How early a sample may be taken and still count as on time.\n *\n * Frames arrive on the display's refreshes, not on demand, so a\n * cadence that does not divide the refresh interval can only ever be\n * served early or late. Insisting on \"not before due\" makes that a\n * catastrophe rather than a rounding error: 59.94fps video has a\n * 16.683ms frame and a 60Hz display refreshes every 16.667ms, so\n * every due time falls 0.016ms after a refresh, every refresh is\n * turned away, and the video plays at half rate.\n *\n * Half a step, capped, because being early by a fraction of your own\n * interval is imperceptible while being early by a whole one would\n * be a dropped frame's worth of drift.\n */\n get dueSlackMs(): number {\n return Math.min(this.stepMs / 2, MAX_DUE_SLACK_MS);\n }\n\n dueAt(now: number): number {\n // Still waiting out its delay: the next frame it has any use for is\n // the one the delay ends on, so the scheduler can sleep until then\n // rather than waking sixty times to decide it is not ready.\n if (this.begun && now - this.startedAt < this.delayMs) {\n return this.startedAt + this.delayMs;\n }\n return this.lastSampledAt === null ? now : this.lastSampledAt + this.stepMs;\n }\n\n /** Where it is at `now`, and whether that is the end of it. */\n protected abstract sample(now: number, elapsedMs: number): { value: T; done: boolean };\n\n /**\n * Begins at `now`, on the first frame that advances it.\n *\n * Not at the moment it was asked for: `animate()` is called from a\n * click handler or a reconcile, which run on `performance.now()`,\n * while sampling runs on the frame clock — and under a manual clock\n * in a spec those are different numbers entirely. Taking the start\n * time from the first tick means one clock decides everything, so an\n * animation cannot be born a thousand milliseconds old.\n */\n begin(now: number): void {\n if (this.begun) {\n return;\n }\n this.begun = true;\n this.startedAt = now;\n this.onBegin();\n }\n\n get hasBegun(): boolean {\n return this.begun;\n }\n\n protected onBegin(): void {}\n\n advance(now: number): void {\n if (this.finished) {\n return;\n }\n this.begin(now);\n const elapsed = now - this.startedAt;\n if (elapsed < this.delayMs) {\n // Waiting. The cell is deliberately not written: whoever asked\n // for the delay has already put the cell where it wants it, and\n // writing it again would dirty a node for nothing.\n this.lastSampledAt = now;\n return;\n }\n const { value, done } = this.sample(now, elapsed - this.delayMs);\n this.lastSampledAt = now;\n // A stepped easing holds one value across many frames, and a\n // spring at rest holds its target: writing an unchanged value\n // would emit through the cell and dirty a node for nothing.\n if (this.lastWritten === undefined || !this.valuesEqual(this.lastWritten, value)) {\n this.lastWritten = value;\n this.cell.value = value;\n this.subject.next(value);\n }\n if (done) {\n this.finish();\n }\n }\n\n /** Writes the target and completes, for reduced motion and for `duration: 0`. */\n snap(): void {\n if (this.finished) {\n return;\n }\n const value = this.target;\n this.lastWritten = value;\n this.cell.value = value;\n this.subject.next(value);\n this.finish();\n }\n\n /** Stops without reaching the target: superseded, or the node left. */\n cancel(): void {\n this.finish();\n }\n\n protected abstract get target(): T;\n\n protected valuesEqual(a: T, b: T): boolean {\n return Object.is(a, b);\n }\n\n private finish(): void {\n this.finished = true;\n this.subject.complete();\n }\n}\n\n/**\n * A value moving from where it is to where it was told, over a fixed\n * time, along a curve.\n *\n * `from` is read at construction rather than at the first frame, so an\n * animation that starts on the same turn as the write it reacts to\n * blends from the value that was on screen and not from the one that\n * has just replaced it.\n */\nexport class UiTween<T> extends UiAnimation<T> {\n private readonly from: T;\n private readonly to: T;\n private readonly duration: number;\n private readonly easing: UiEasing;\n private readonly repeat: boolean;\n private readonly interpolate: UiInterpolator<T>;\n\n constructor(cell: AnimatedCell<T>, to: T, options: UiTweenOptions, interpolate: UiInterpolator<T>) {\n super(cell, options.stepMs ?? 0, options.reducedMotion ?? 'snap', options.delay ?? 0);\n this.from = cell.value;\n this.to = to;\n this.duration = options.duration;\n this.easing = options.easing ?? easings.standard;\n this.repeat = options.repeat === true;\n this.interpolate = interpolate;\n }\n\n /** Whether it will ever finish on its own. */\n get isRepeating(): boolean {\n return this.repeat;\n }\n\n protected override get target(): T {\n return this.to;\n }\n\n protected override sample(_now: number, elapsedMs: number): { value: T; done: boolean } {\n if (this.duration <= 0) {\n return { value: this.to, done: !this.repeat };\n }\n if (this.repeat) {\n const progress = (elapsedMs % this.duration) / this.duration;\n return { value: this.interpolate(this.from, this.to, this.easing(progress)), done: false };\n }\n const progress = Math.min(1, Math.max(0, elapsedMs / this.duration));\n if (progress >= 1) {\n return { value: this.to, done: true };\n }\n return { value: this.interpolate(this.from, this.to, this.easing(progress)), done: false };\n }\n\n protected override valuesEqual(a: T, b: T): boolean {\n return valuesLookEqual(a, b);\n }\n}\n\n/** Sub-step for the spring integrator, in milliseconds. */\nconst SPRING_STEP_MS = 1000 / 240;\n/**\n * The most simulated time one frame may cover.\n *\n * A stall — a blocked main thread, a backgrounded tab — hands the next\n * frame a delta of seconds. Integrating all of it would either cost a\n * visible pause or, with a stiff spring, blow up; clamping means the\n * spring resumes from where it was, which is what a person sees as the\n * animation simply carrying on.\n */\nconst SPRING_MAX_FRAME_MS = 64;\n\n/**\n * A number pulled towards a target by a spring.\n *\n * Numbers only, deliberately. A spring integrates a position and a\n * velocity, and there is no honest velocity for a colour or for a\n * transform's five fields taken together; the usual dodge — springing\n * a scalar progress and interpolating along it — reintroduces the\n * duration a spring exists to be free of. Two springs on two numbers\n * is what a layout animation uses, and it is the correct model.\n *\n * Integrated at a fixed sub-step rather than with the frame delta, so\n * the same animation reaches the same values whether it was sampled at\n * 60 Hz or at 30, and so a spec can assert them.\n */\nexport class UiSpring extends UiAnimation<number> {\n private position: number;\n /** The position one sub-step back, for the interpolation below. */\n private previousPosition: number;\n private velocity: number;\n private readonly to: number;\n private readonly spec: UiSpringSpec;\n private readonly restDelta: number;\n private simulatedTo: number;\n\n constructor(cell: AnimatedCell<number>, to: number, options: UiSpringOptions) {\n super(cell, options.stepMs ?? 0, options.reducedMotion ?? 'snap', options.delay ?? 0);\n this.position = cell.value;\n this.previousPosition = this.position;\n this.velocity = options.velocity ?? 0;\n this.to = to;\n this.spec = options.spring;\n this.restDelta = options.restDelta ?? 0.01;\n this.simulatedTo = 0;\n }\n\n /** Where it is going, for a retarget that wants to know. */\n get destination(): number {\n return this.to;\n }\n\n /** Units per second right now, so a retarget can carry it over. */\n get currentVelocity(): number {\n return this.velocity;\n }\n\n protected override onBegin(): void {\n this.simulatedTo = 0;\n }\n\n protected override get target(): number {\n return this.to;\n }\n\n protected override sample(_now: number, elapsedMs: number): { value: number; done: boolean } {\n const until = Math.min(elapsedMs, this.simulatedTo + SPRING_MAX_FRAME_MS);\n const { stiffness, damping, mass } = this.spec;\n const seconds = SPRING_STEP_MS / 1000;\n // Whole sub-steps only, with the remainder carried into the next\n // frame. Integrating a partial step to land exactly on the frame's\n // time would make the result depend on the cadence, which is the\n // one thing this integrator exists to avoid — the spec asserts\n // that 60 Hz and 30 Hz agree to six places.\n while (this.simulatedTo + SPRING_STEP_MS <= until) {\n this.previousPosition = this.position;\n const acceleration = (-stiffness * (this.position - this.to) - damping * this.velocity) / mass;\n this.velocity += acceleration * seconds;\n this.position += this.velocity * seconds;\n this.simulatedTo += SPRING_STEP_MS;\n }\n // Rest is both halves: near the target *and* slow. A spring passing\n // through its target at speed is not finished.\n if (Math.abs(this.position - this.to) < this.restDelta && Math.abs(this.velocity) < this.restDelta * 10) {\n this.position = this.to;\n this.previousPosition = this.to;\n this.velocity = 0;\n return { value: this.to, done: true };\n }\n // Reported between the last two sub-steps, by however much of one\n // is left over.\n //\n // The simulation runs on whole steps and must: a partial step would\n // make the *trajectory* depend on the cadence, which is what this\n // integrator exists to avoid. But reporting the whole-step position\n // makes the value it hands out depend on the cadence in a different\n // and more visible way. A sub-step is 1/240s, so a 60Hz frame is\n // exactly four of them and every frame advances the same amount —\n // while a 165Hz frame is 1.4545 of them, so the loop above runs one\n // step, then one, then two, and the element moves by twice as much\n // on every third frame. That reads as judder on exactly the\n // displays fast enough to show it, which is the opposite of what a\n // high refresh rate is for.\n //\n // Interpolating the *output* keeps both: the simulation is\n // untouched and still agrees to six places across cadences, and the\n // value handed out advances by an equal amount every frame. It\n // trails the simulation by up to one sub-step, which is 4ms.\n const alpha = (until - this.simulatedTo) / SPRING_STEP_MS;\n return { value: this.previousPosition + (this.position - this.previousPosition) * alpha, done: false };\n }\n}\n\n/**\n * Whether two interpolated values are the same to a writer.\n *\n * Object.is is wrong for the compound ones: every sample of a\n * transform or a colour is a fresh object, so an identity comparison\n * would report a change on every frame of a stepped animation and undo\n * the reason `stepMs` exists.\n */\nfunction valuesLookEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {\n return false;\n }\n const keys = Object.keys(a as object);\n if (keys.length !== Object.keys(b as object).length) {\n return false;\n }\n for (const key of keys) {\n const left = (a as Record<string, unknown>)[key];\n const right = (b as Record<string, unknown>)[key];\n if (typeof left === 'number' && typeof right === 'number') {\n if (Math.abs(left - right) > 1e-6) {\n return false;\n }\n continue;\n }\n if (!Object.is(left, right)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Builds a tween, or undefined when the two values cannot be blended.\n *\n * Undefined is a real answer and not a failure: see `interpolatorFor`\n * for the two cases (typed lengths, palette names) where the honest\n * thing is to write the value rather than to invent a midpoint.\n */\nexport function createTween<T>(cell: AnimatedCell<T>, to: T, options: UiTweenOptions): UiTween<T> | undefined {\n const interpolate = interpolatorFor(cell.value, to) as UiInterpolator<T> | undefined;\n if (interpolate === undefined) {\n return undefined;\n }\n return new UiTween(cell, to, options, interpolate);\n}\n","import { easings, type UiEasing } from '../animation/UiEasing';\n\n/**\n * The named durations a component may ask for, in milliseconds.\n *\n * Named rather than numeric for the reason a colour is named: a\n * library whose components each pick their own 180 or 240 has no feel,\n * it has forty opinions. the rule for colour —\n * tokens only, never a literal in a component — applies to time.\n */\nexport type UiDurationToken = 'instant' | 'fast' | 'normal' | 'slow' | 'deliberate';\n\n/** The named curves; see `UiEasing.ts` for what each one is for. */\nexport type UiEasingToken = keyof typeof easings;\n\n/** The named springs, for a movement that follows a gesture. */\nexport type UiSpringToken = 'gentle' | 'snappy' | 'stiff';\n\n/**\n * A spring, in the physical sense: `stiffness` pulls towards the\n * target, `damping` resists velocity, `mass` resists both.\n *\n * Critical damping is `2 · sqrt(stiffness · mass)`; every preset below\n * sits a little under it, so a spring settles with one small overshoot\n * rather than creeping in. A spring has no duration, which is the\n * whole point of using one for a gesture: it starts from whatever\n * velocity the finger left behind.\n */\nexport interface UiSpringSpec {\n readonly stiffness: number;\n readonly damping: number;\n readonly mass: number;\n}\n\n/**\n * A vocabulary of motion, beside the palette and the type scale.\n *\n * It is deliberately **not** a field on `UiTheme`. A theme value is\n * resolved per node — `resolveColorValue` asks what the node inherits\n * — and an animation drives a *cell*, which has no node, so a motion\n * token on the theme would be a token nothing could resolve. That is\n * the mistake `visualState` made: a value\n * shipped where nothing reads it. An application that wants a\n * different feel installs one with `AnimationService.setMotion`, which\n * is per runtime, as everything else in front of the graph is.\n */\nexport interface UiMotion {\n readonly durations: Readonly<Record<UiDurationToken, number>>;\n readonly easings: Readonly<Record<UiEasingToken, UiEasing>>;\n readonly springs: Readonly<Record<UiSpringToken, UiSpringSpec>>;\n}\n\nexport const defaultMotion: UiMotion = {\n durations: {\n /** No motion. What `reducedMotion` turns everything else into. */\n instant: 0,\n /** A control acknowledging a press; too short to watch. */\n fast: 120,\n /** The default: something appearing, moving or changing colour. */\n normal: 200,\n /** A panel or a dialog, which is bigger and travels further. */\n slow: 320,\n /** A movement meant to be followed by the eye. Rare. */\n deliberate: 500\n },\n easings,\n springs: {\n gentle: { stiffness: 120, damping: 20, mass: 1 },\n snappy: { stiffness: 220, damping: 24, mass: 1 },\n stiff: { stiffness: 400, damping: 32, mass: 1 }\n }\n} as const;\n","import { defaultMotion, type UiSpringSpec, type UiSpringToken } from '../environment/UiMotion';\nimport type { UiEasing } from './UiEasing';\nimport type { UiReducedMotionPolicy } from './UiAnimation';\n\n/**\n * How one property gets from its old value to its new one, when the\n * element declares a `transition`.\n */\nexport type UiTransitionSpec =\n | {\n readonly kind: 'tween';\n readonly duration: number;\n readonly easing?: UiEasing;\n readonly stepMs?: number;\n readonly reducedMotion?: UiReducedMotionPolicy;\n }\n | {\n readonly kind: 'spring';\n readonly spring: UiSpringSpec;\n readonly restDelta?: number;\n readonly reducedMotion?: UiReducedMotionPolicy;\n };\n\n/**\n * What an element may write for one property of a `transition`.\n *\n * A bare number is a duration in milliseconds, which is what makes\n * `transition: { opacity: 200, transform: spring() }` read the way\n * That is how it was written.\n */\nexport type UiTransitionValue = number | UiTransitionSpec;\n\n/** A tween of `duration` milliseconds along `easing`. */\nexport function tween(\n duration: number,\n options: { easing?: UiEasing; stepMs?: number; reducedMotion?: UiReducedMotionPolicy } = {}\n): UiTransitionSpec {\n return { kind: 'tween', duration, ...options };\n}\n\n/**\n * A spring, named from the motion vocabulary or given outright.\n *\n * Springs apply to numbers only; see `UiSpring` for why springing a\n * colour or a whole transform is not a thing this offers.\n */\nexport function spring(\n spec: UiSpringToken | UiSpringSpec = 'snappy',\n options: { restDelta?: number; reducedMotion?: UiReducedMotionPolicy } = {}\n): UiTransitionSpec {\n return {\n kind: 'spring',\n spring: typeof spec === 'string' ? defaultMotion.springs[spec] : spec,\n ...options\n };\n}\n\nexport function normalizeTransition(value: UiTransitionValue): UiTransitionSpec {\n return typeof value === 'number' ? { kind: 'tween', duration: value } : value;\n}\n\n/**\n * Rejects a `transition` prop that is not a map of property names to\n * durations or specs, naming the node the way an unknown prop does.\n */\nexport function assertTransitionMap(\n nodeId: string,\n value: unknown,\n isKnownProperty: (name: string) => boolean\n): ReadonlyMap<string, UiTransitionSpec> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error(`The 'transition' prop on node '${nodeId}' must be an object, got ${describe(value)}.`);\n }\n const specs = new Map<string, UiTransitionSpec>();\n for (const [property, entry] of Object.entries(value as Record<string, unknown>)) {\n if (!isKnownProperty(property)) {\n throw new Error(\n `The 'transition' prop on node '${nodeId}' names '${property}', which is not a UI property. ` +\n `A transition names the properties it animates, so an unknown one is a typo the same way an unknown prop is.`\n );\n }\n if (typeof entry === 'number') {\n if (!Number.isFinite(entry) || entry < 0) {\n throw new Error(\n `The 'transition' for '${property}' on node '${nodeId}' must be a duration in ms, got ${entry}.`\n );\n }\n specs.set(property, { kind: 'tween', duration: entry });\n continue;\n }\n if (\n typeof entry === 'object' &&\n entry !== null &&\n ((entry as UiTransitionSpec).kind === 'tween' || (entry as UiTransitionSpec).kind === 'spring')\n ) {\n specs.set(property, entry as UiTransitionSpec);\n continue;\n }\n throw new Error(\n `The 'transition' for '${property}' on node '${nodeId}' must be a duration in milliseconds or a spec ` +\n `built by tween() or spring(), got ${describe(entry)}.`\n );\n }\n return specs;\n}\n\nfunction describe(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n return Array.isArray(value) ? 'an array' : `a ${typeof value}`;\n}\n","import type { Observable } from 'rxjs';\n\nimport type { UiAnimation, AnimatedCell } from './UiAnimation';\n\n/** An animation of some value, with the value's type erased. */\ntype AnyAnimation = UiAnimation<unknown>;\n\nfunction erase<T>(animation: UiAnimation<T>): AnyAnimation {\n return animation as unknown as AnyAnimation;\n}\n\n/**\n * The running animations of one runtime, and the thing the `ticks`\n * phase advances.\n *\n * **One animation per cell.** Starting a second one on a cell\n * supersedes the first, which completes without reaching its target.\n * That rule is not a simplification: it is what makes a gesture work\n * — every retarget while a finger moves is a new animation on the same\n * cell — and it removes the whole question of what two animations\n * writing one value would mean.\n *\n * The driver is per runtime for the reason `MediaService` is: the\n * playground runs several runtimes in one worker, and a module-level\n * set of animations would tick a disposed runtime's cells.\n */\nexport class AnimationDriver {\n private readonly running = new Map<object, AnyAnimation>();\n private reducedMotion = false;\n private hidden = false;\n private wake: (() => void) | null = null;\n\n /**\n * Called when an animation enters the running set on an idle driver.\n *\n * The runtime arms a frame here. Without it an animation started\n * from a click handler that changes nothing else would never get a\n * first frame: the scheduler arms only on dirt, the animation's dirt\n * is made inside a frame, and no frame would ever come. Re-arming\n * *between* frames is `scheduleAnimationTick`'s job; this is only\n * the first one.\n */\n setWakeListener(listener: (() => void) | null): void {\n this.wake = listener;\n }\n\n /** Whether anything is running, so the `ticks` phase can report 0. */\n get isRunning(): boolean {\n return this.running.size > 0;\n }\n\n get size(): number {\n return this.running.size;\n }\n\n /** Whether a person has asked for less motion; see `UiReducedMotionPolicy`. */\n get isReducedMotion(): boolean {\n return this.reducedMotion;\n }\n\n /**\n * Whether an animation should be put where it is going instead of\n * being run there.\n *\n * Two questions with one answer: a person who has asked for less\n * motion and a page nobody is looking at both want the end state and\n * not the journey to it, and in both cases `keep` is the way an\n * animation says its movement is the information rather than the\n * decoration.\n */\n private lands(policy: AnyAnimation['reducedMotionPolicy']): boolean {\n return policy === 'snap' && (this.reducedMotion || this.hidden);\n }\n\n /**\n * Starts an animation, replacing whatever was driving its cell.\n *\n * Returns the values it will write. Under reduced motion, or while\n * the document is hidden, an animation whose policy is `snap` never\n * enters the set at all: the cell takes its target here, in the\n * caller's turn, and the returned observable has already completed.\n * So a reduced-motion app runs no animation frames, rather than\n * running them and drawing the same thing sixty times.\n */\n start<T>(animation: UiAnimation<T>): Observable<T> {\n const cell = animation.cell as object;\n this.running.get(cell)?.cancel();\n this.running.delete(cell);\n if (this.lands(animation.reducedMotionPolicy)) {\n animation.snap();\n return animation.values;\n }\n const wasIdle = this.running.size === 0;\n this.running.set(cell, erase(animation));\n if (wasIdle) {\n this.wake?.();\n }\n return animation.values;\n }\n\n /** The animation currently driving a cell, if any. */\n animationFor<T>(cell: AnimatedCell<T>): UiAnimation<T> | undefined {\n return this.running.get(cell as object) as unknown as UiAnimation<T> | undefined;\n }\n\n /**\n * Stops whatever is driving a cell, leaving it where it stands.\n *\n * This is how a node leaving the tree releases its animations, which\n * is the leak the media tier names for image bitmaps and which\n * matters here too: a driver holding a cell holds every closure the\n * component that made it captured.\n */\n stop<T>(cell: AnimatedCell<T>): boolean {\n const animation = this.running.get(cell as object);\n if (animation === undefined) {\n return false;\n }\n this.running.delete(cell as object);\n animation.cancel();\n return true;\n }\n\n stopAll(): void {\n const animations = [...this.running.values()];\n this.running.clear();\n for (const animation of animations) {\n animation.cancel();\n }\n }\n\n /**\n * A person's motion preference changed.\n *\n * Turning it on finishes everything already in flight that is\n * willing to be finished, rather than leaving a dialog stuck\n * half-faded until the animation it was told not to run runs out.\n */\n setReducedMotion(reduced: boolean): void {\n if (this.reducedMotion === reduced) {\n return;\n }\n this.reducedMotion = reduced;\n if (!reduced) {\n return;\n }\n this.landRunning();\n }\n\n /**\n * The document went out of sight, or came back.\n *\n * A hidden page still paints. The runtime deliberately keeps giving\n * frames to a change that genuinely happened, so the canvas holds a\n * correct picture rather than whatever was on it when the tab went\n * away, and a route loaded hidden is drawn once its images have\n * decoded rather than being left half empty. What it does not do is\n * advance animations, because frames spent watching something move\n * that nobody can see are frames spent for nothing.\n *\n * Those two together leave an animation frozen at whatever value it\n * had reached, and painted there. For most animations that is\n * harmless: a colour halfway between two colours is still a colour.\n * For an **entrance** it is not, because an entrance begins at\n * `opacity: 0` and its frozen first value is an element that is not\n * there. Segue's track page, opened in a background tab, held sixty\n * two pixels of empty page where its play button belonged, and every\n * screen built from `motion({ initial })` did the same. It came back\n * the moment the tab did, which is exactly why it reads as a\n * rendering bug rather than as a tab that is not being drawn.\n *\n * So a hidden page lands its animations instead of freezing them, in\n * the same way and for the same reason a reduced-motion preference\n * does: what an animation has to say to nobody is nothing, and the\n * state it was going to end at is the one the page should be holding\n * while it waits to be looked at. An animation whose movement is the\n * information (`keep`) is left alone either way, so a spinner is\n * still a spinner when the tab comes back.\n */\n setHidden(hidden: boolean): void {\n if (this.hidden === hidden) {\n return;\n }\n this.hidden = hidden;\n if (!hidden) {\n return;\n }\n this.landRunning();\n }\n\n /** Puts everything that may be landed where it was going. */\n private landRunning(): void {\n // Deleting the current entry while iterating a Map is defined and\n // does not skip the next one.\n for (const [cell, animation] of this.running) {\n if (this.lands(animation.reducedMotionPolicy)) {\n this.running.delete(cell);\n animation.snap();\n }\n }\n }\n\n /**\n * When the earliest animation next wants a frame, or undefined when\n * nothing is running.\n *\n * This is the whole answer to \"how does an animation keep frames\n * coming on an idle app\". The runtime asks after every frame: an\n * answer at or before `now` means arm the next frame, a later one\n * means a timer, and undefined means arm nothing — which is why an\n * idle app's profile reads `ticks 0.00` for the strong reason (no\n * frames at all) rather than the weak one (frames that did nothing).\n */\n nextTickAt(now: number): number | undefined {\n let earliest: number | undefined;\n for (const animation of this.running.values()) {\n // Minus its slack, because this is the earliest moment a frame\n // would be *useful*, not the exact moment it is owed. Frames\n // arrive on the display's refreshes: a caller that waits for the\n // exact due time asks after the refresh that could have served\n // it and waits for the next one, which is how 59.94fps video on\n // a 60Hz display ends up playing at thirty. Asking a slack early\n // means the refresh at or just before due is the one that\n // serves it, and `advance` accepts it for the same reason.\n const due = animation.dueAt(now) - animation.dueSlackMs;\n if (earliest === undefined || due < earliest) {\n earliest = due;\n }\n }\n return earliest;\n }\n\n /**\n * Advances every animation whose next sample is due, writing each\n * one's cell. This is the `ticks` phase.\n *\n * It runs before the dirty set is snapshotted for exactly the reason\n * `patches` and `environment` do: the writes it makes have to belong\n * to the frame about to be collected, or a frame would draw the\n * previous tick's values and every animation would run one frame\n * behind.\n */\n advance(now: number): void {\n for (const [cell, animation] of this.running) {\n // Early by less than its own slack counts as on time — the same\n // allowance `nextTickAt` asks the scheduler for, and for the\n // same reason. See `UiAnimation.dueSlackMs`.\n if (animation.dueAt(now) - animation.dueSlackMs > now) {\n continue;\n }\n animation.advance(now);\n if (animation.isFinished && this.running.get(cell) === animation) {\n this.running.delete(cell);\n }\n }\n }\n}\n","import type { LayoutBox } from '../layout/LayoutTypes';\nimport type { UiNode } from '../graph/UiNode';\n\n/**\n * What an arriving element is told when it takes a name: where the\n * element it is replacing was standing, and how to make that element\n * step aside once the replacement is actually in place.\n */\nexport interface SharedClaim {\n /** The previous holder's last box, or null when nothing held the name. */\n readonly box: LayoutBox | null;\n /** Hides the previous holder. Idempotent, and a no-op when there was none. */\n yieldPrevious(): void;\n}\n\n/** What one name's current holder is, and where it last was. */\ninterface Holder {\n readonly node: UiNode;\n /** Its box as of the last layout that moved it. Null before the first. */\n box: LayoutBox | null;\n /** Told to get out of the way when another node takes the name. */\n yield: () => void;\n /** Told to come back, if this one outlives whatever displaced it. */\n restore: () => void;\n /**\n * Whoever held the name before this one and is still alive.\n *\n * Kept so the name can be handed back. Stepping aside is otherwise a\n * one-way door: an element that yields is hidden outright, and if the\n * element it yielded to leaves without anything replacing it, the\n * only copy left is an invisible one.\n *\n * That is not a corner case. A route change keeps the departing\n * screen alive for the length of its exit, so pressing Back part-way\n * through returns to a screen whose element has already yielded.\n */\n displaced: Holder | null;\n}\n\n/**\n * Who currently owns each shared-element name, and where they were.\n *\n * This is the whole of the shared-element mechanism's memory, and it\n * is deliberately tiny: a name, the node holding it, and that node's\n * last box. Everything the transition looks like is the modifier's\n * business; this only answers \"was something else called that a moment\n * ago, and where was it?\".\n *\n * **Why a registry and not a diff of two trees.** The browser's View\n * Transitions API snapshots the old document, applies the change, and\n * animates pseudo-elements between two rasters, because the DOM it\n * started with is gone by the time it can measure. Gesso has a\n * retained graph: both nodes are real, live, and measurable at the\n * same moment, so the transition can be FLIP on the real thing —\n * interruptible, continuous, and free of the snapshot.\n *\n * **Why it is order-independent.** `UiGraphBuilder.reconcile` creates\n * the new children of a parent before it removes the unmatched old\n * ones, so a naive \"the leaving node hands over to the arriving one\"\n * would be handing over in the wrong direction. Keying by name instead\n * means the arriving node asks *whoever holds this name* where it is,\n * and it does not matter at all whether that node has left yet.\n *\n * Per runtime, for the same reason `AnimationDriver` and the media\n * caches are: the playground runs several runtimes in one worker, and\n * a module-level registry would let one runtime's card hand its box to\n * another runtime's page.\n */\nexport class UiSharedElements {\n private readonly holders = new Map<string, Holder>();\n\n /**\n * Takes a name for a node, and answers where the previous holder is\n * standing — and how to tell it to get out of the way.\n *\n * **The yield is handed back rather than performed.** The previous\n * holder does have to disappear, because the arriving node is about\n * to be drawn over exactly where it stands and two copies of one\n * thing is a double image rather than a transition; the CSS version\n * of this effect says the same thing as\n * `::view-transition-old(name) { display: none }`.\n *\n * But it must not disappear *yet*. A claim happens during\n * reconciliation, and the arriving node has no box until the frame\n * lays out — so yielding here leaves a window in which neither\n * element is drawn. For a transform morph that window is closed\n * within the same frame; for a geometry morph, which cannot write a\n * box after layout has run, it is a whole frame long, and a whole\n * frame with neither the old card nor the new one is a flash of the\n * page behind them. It was exactly that visible.\n *\n * So the caller yields the previous holder at the moment it actually\n * takes its place. Until then both are on screen, in the same\n * position, which is indistinguishable from one.\n *\n * A null box is the ordinary case: an element that is simply\n * appearing has nothing to morph from and should enter instead.\n */\n claim(name: string, node: UiNode, onYield: () => void, onRestore: () => void = () => {}): SharedClaim {\n const previous = this.holders.get(name);\n const displaced = previous === undefined || previous.node === node ? null : previous;\n this.holders.set(name, { node, box: null, yield: onYield, restore: onRestore, displaced });\n if (previous === undefined || displaced === null) {\n return { box: previous?.box ?? null, yieldPrevious: () => {} };\n }\n let yielded = false;\n return {\n box: previous.box,\n yieldPrevious: () => {\n if (!yielded) {\n yielded = true;\n previous.yield();\n }\n }\n };\n }\n\n /** Records where a node is now, so whoever takes the name next knows. */\n report(name: string, node: UiNode, box: LayoutBox): void {\n const holder = this.holders.get(name);\n if (holder === undefined || holder.node !== node) {\n return;\n }\n holder.box = box;\n }\n\n /**\n * Gives up a name, if this node still holds it.\n *\n * Guarded on identity because detach order is not the reverse of\n * attach order across a tree change: the leaving node's modifier\n * detaches *after* the arriving one has already claimed the name,\n * and an unguarded release would delete the arriving node's entry.\n */\n release(name: string, node: UiNode): void {\n const holder = this.holders.get(name);\n if (holder === undefined) {\n return;\n }\n if (holder.node !== node) {\n // Something further back in the chain has gone. Forget it, so it\n // is never brought back and never measured: a removed node's last\n // box is a rectangle nothing occupies, and morphing from it is\n // worse than not morphing at all.\n for (let entry: Holder = holder; entry.displaced !== null; entry = entry.displaced) {\n if (entry.displaced.node === node) {\n entry.displaced = entry.displaced.displaced;\n break;\n }\n }\n return;\n }\n // The holder itself is going. If it displaced someone who is still\n // here, that one takes the name back and stops being hidden.\n const displaced = holder.displaced;\n if (displaced === null) {\n this.holders.delete(name);\n return;\n }\n this.holders.set(name, displaced);\n displaced.restore();\n }\n\n /** The names currently held, for specs and the inspector. */\n get names(): readonly string[] {\n return [...this.holders.keys()];\n }\n\n clear(): void {\n this.holders.clear();\n }\n}\n","import type { UiEasing } from './UiEasing';\nimport type { UiReducedMotionPolicy } from './UiAnimation';\nimport type { UiDurationToken, UiEasingToken, UiSpringSpec, UiSpringToken } from '../environment/UiMotion';\n\n/**\n * Where an element is, visually, relative to where the layout put it.\n *\n * Six numbers and nothing else, chosen because they are exactly the\n * ones that cost no layout: opacity, a translation, a scale per axis\n * and a rotation all resolve inside `PaintState`, so an element moving\n * through them marks Paint and the layout engine never runs. Anything\n * that would move the box — a width, a margin, a gap — is deliberately\n * not here; it belongs to the layout, and animating it is what the\n * declarative `transition` prop is for.\n *\n * Every field is an *offset from rest*. Omitting one means \"leave it\n * where it belongs\", which is what lets `{ opacity: 0 }` be a complete\n * description of a fade without also asserting a scale of 1.\n */\nexport interface MotionState {\n readonly opacity?: number;\n /** Logical pixels right of where the layout put it. */\n readonly x?: number;\n /** Logical pixels below where the layout put it. */\n readonly y?: number;\n /** Both axes at once; `scaleX`/`scaleY` override it per axis. */\n readonly scale?: number;\n readonly scaleX?: number;\n readonly scaleY?: number;\n /** Radians, about the node's centre. */\n readonly rotate?: number;\n}\n\n/**\n * One state, or several to be laid over each other left to right.\n *\n * `[fade, slideUp(16)]` is the idiom, and it reads as what it does.\n * An array rather than a `fade.and(...)` builder because a state is\n * data — a component may compute one, store one, or receive one as a\n * prop — and a plain object stays comparable, serialisable and\n * inspectable in a way a builder object does not.\n */\nexport type MotionStateInput = MotionState | readonly MotionState[];\n\n/** The six channels a motion state drives, all resolved. */\nexport interface ResolvedMotionState {\n readonly opacity: number;\n readonly x: number;\n readonly y: number;\n readonly scaleX: number;\n readonly scaleY: number;\n readonly rotate: number;\n}\n\n/** Where an element sits when nothing is animating it. */\nexport const MOTION_REST: ResolvedMotionState = {\n opacity: 1,\n x: 0,\n y: 0,\n scaleX: 1,\n scaleY: 1,\n rotate: 0\n};\n\n/** The channel names, for anything that has to walk them. */\nexport const MOTION_CHANNELS = ['opacity', 'x', 'y', 'scaleX', 'scaleY', 'rotate'] as const;\n\nexport type MotionChannel = (typeof MOTION_CHANNELS)[number];\n\n/**\n * Flattens a state (or a stack of them) onto rest.\n *\n * `scale` is expanded here rather than carried through, so everything\n * downstream deals in two independent axes and a shared-element morph\n * — which genuinely needs different numbers per axis — is not a\n * special case.\n */\nexport function resolveMotionState(input: MotionStateInput | null | undefined): ResolvedMotionState {\n if (input === null || input === undefined) {\n return MOTION_REST;\n }\n const states = Array.isArray(input) ? (input as readonly MotionState[]) : [input as MotionState];\n let opacity = MOTION_REST.opacity;\n let x = MOTION_REST.x;\n let y = MOTION_REST.y;\n let scaleX = MOTION_REST.scaleX;\n let scaleY = MOTION_REST.scaleY;\n let rotate = MOTION_REST.rotate;\n for (const state of states) {\n if (state === null || state === undefined) {\n continue;\n }\n opacity = state.opacity ?? opacity;\n x = state.x ?? x;\n y = state.y ?? y;\n // Per-axis wins over the shorthand, whichever order they appear in\n // the same object, because they are read in that order here.\n const uniform = state.scale;\n if (uniform !== undefined) {\n scaleX = uniform;\n scaleY = uniform;\n }\n scaleX = state.scaleX ?? scaleX;\n scaleY = state.scaleY ?? scaleY;\n rotate = state.rotate ?? rotate;\n }\n return { opacity, x, y, scaleX, scaleY, rotate };\n}\n\n/** Whether a resolved state is rest, to a pixel nobody can see. */\nexport function isMotionRest(state: ResolvedMotionState): boolean {\n return (\n Math.abs(state.opacity - 1) < 1e-4 &&\n Math.abs(state.x) < 0.01 &&\n Math.abs(state.y) < 0.01 &&\n Math.abs(state.scaleX - 1) < 1e-4 &&\n Math.abs(state.scaleY - 1) < 1e-4 &&\n Math.abs(state.rotate) < 1e-5\n );\n}\n\n// ---------------------------------------------------------------------------\n// How a state is reached\n// ---------------------------------------------------------------------------\n\n/**\n * How long a motion takes and what shape it has.\n *\n * A tween or a spring, never both: naming a `spring` and a `duration`\n * together is a contradiction, since a spring's whole point is that it\n * has no duration. The spring wins if both are given, and that is the\n * only reason this is not two types.\n */\nexport interface MotionTiming {\n /** Milliseconds, or a name from the motion vocabulary. Defaults to `normal`. */\n readonly duration?: number | UiDurationToken;\n /** A curve, or a name from the vocabulary. Defaults to `standard`. */\n readonly easing?: UiEasing | UiEasingToken;\n /** Use a spring instead of a tween. Named, or given outright. */\n readonly spring?: UiSpringToken | UiSpringSpec;\n /** Wait this long before starting; see `UiAnimationOptions.delay`. */\n readonly delay?: number;\n /** What a reduced-motion preference does to it. Defaults to `snap`. */\n readonly reducedMotion?: UiReducedMotionPolicy;\n}\n\n// ---------------------------------------------------------------------------\n// The presets\n// ---------------------------------------------------------------------------\n\n/**\n * Invisible. The state almost every enter and exit contains, and on\n * its own the most common animation there is.\n */\nexport const fade: MotionState = { opacity: 0 };\n\n/** Comes up from `distance` pixels below. */\nexport function slideUp(distance = 16): MotionState {\n return { y: distance };\n}\n\n/** Comes down from `distance` pixels above. */\nexport function slideDown(distance = 16): MotionState {\n return { y: -distance };\n}\n\n/**\n * Comes in from one edge.\n *\n * The edge names where it starts, which is the way a person describes\n * it — \"it slides in from the left\" — rather than which way it moves.\n */\nexport function slideFrom(edge: 'left' | 'right' | 'top' | 'bottom', distance = 24): MotionState {\n switch (edge) {\n case 'left':\n return { x: -distance };\n case 'right':\n return { x: distance };\n case 'top':\n return { y: -distance };\n case 'bottom':\n return { y: distance };\n }\n}\n\n/** Grows (or shrinks) into place from `scale`. */\nexport function scaleFrom(scale = 0.92): MotionState {\n return { scale };\n}\n\n/** Turned by `radians`. */\nexport function rotateFrom(radians: number): MotionState {\n return { rotate: radians };\n}\n\n/**\n * Small and invisible: what a dialog, a menu or a toast appears from.\n *\n * A named preset rather than `[fade, scaleFrom(0.9)]` written out\n * everywhere, for the reason the durations are named: a library whose\n * overlays each pick their own 0.9 or 0.94 has no feel.\n */\nexport function pop(scale = 0.9): MotionState {\n return { scale, opacity: 0 };\n}\n","import type { Observable, Subscription } from 'rxjs';\n\nimport { DirtyFlags } from '../graph/DirtyFlags';\nimport type { NodeId } from '../graph/UiNode';\nimport type { UiGraph } from '../graph/UiGraph';\nimport type { UiGraphBuilder } from '../composition/UiGraphBuilder';\nimport type { UiChild } from '../composition/UiElement';\n\nexport type ChildrenBindingId = number;\n\n/**\n * Binds an observable stream of child definitions to a fragment anchor.\n *\n * When the observable emits, the emitted UiElement(s) are reconciled\n * into the fragment's children. The fragment itself is invisible to\n * layout, rendering, and hit-testing; it exists only as a stable anchor\n * in the retained graph.\n */\nexport class UiChildrenBinding {\n private subscription: Subscription | null = null;\n\n constructor(\n public readonly id: ChildrenBindingId,\n public readonly parentId: NodeId,\n public readonly fragmentId: NodeId,\n public readonly observable: Observable<UiChild | readonly UiChild[]>,\n private readonly graph: UiGraph,\n private readonly builder: UiGraphBuilder\n ) {}\n\n /**\n * Start listening to the observable.\n */\n connect(): void {\n if (this.subscription !== null) {\n throw new Error(`Children binding ${this.id} is already connected.`);\n }\n this.subscription = this.observable.subscribe({\n next: value => this.handleValue(value),\n error: error => {\n console.error(`Children binding ${this.id} failed (${this.parentId} → ${this.fragmentId})`, error);\n this.disconnect();\n }\n });\n }\n\n /**\n * Stop listening and detach the subscription.\n */\n disconnect(): void {\n this.subscription?.unsubscribe();\n this.subscription = null;\n }\n\n private handleValue(value: UiChild | readonly UiChild[]): void {\n const definitions = Array.isArray(value) ? (value as readonly UiChild[]) : [value as UiChild];\n const fragment = this.graph.requireNode(this.fragmentId);\n this.builder.reconcileChildren(fragment, definitions);\n const parent = this.graph.requireNode(this.parentId);\n this.graph.markDirty(parent, DirtyFlags.Children);\n }\n}\n","import type { UiNode } from '../graph/UiNode';\nimport type { UiEventListener, UiInputDispatcher } from '../input/UiInputDispatcher';\nimport type { UiEventType } from '../input/UiInputEvent';\n\n/**\n * Binds a declarative `on*` prop to a listener on the input dispatcher.\n *\n * Event bindings sit alongside UiBinding (observable properties) and\n * UiChildrenBinding (observable children) so that all three are torn\n * down by the same subtree-removal path. A handler can therefore never\n * outlive the node it was declared on.\n */\nexport class UiEventBinding {\n constructor(\n public readonly node: UiNode,\n public readonly type: UiEventType,\n public readonly listener: UiEventListener,\n private readonly dispatcher: UiInputDispatcher\n ) {}\n\n connect(): void {\n this.dispatcher.addEventListener(this.node, this.type, this.listener);\n }\n\n disconnect(): void {\n this.dispatcher.removeEventListener(this.node, this.type, this.listener);\n }\n}\n","import type { Observable } from 'rxjs';\n\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { UiProps } from './UiProps';\n\n/**\n * Opaque marker for component definitions.\n *\n * The runtime does not know how to mount components; it only\n * recognizes this shape so factory functions can accept components\n * as children. The framework layer resolves ComponentLikeElements\n * into plain UiElements before passing them to UiGraphBuilder.\n */\nexport interface ComponentLikeElement {\n readonly kind: 'component';\n readonly tag: string;\n /** The class or function to mount; compared by identity across reconciles. */\n readonly component: Function;\n readonly props: Record<string, unknown>;\n readonly key?: string | number;\n}\n\n/**\n * A child definition may be a static element, a component definition,\n * or an observable stream of either (or of lists of them). Observable\n * children are reconciled dynamically by the framework without\n * re-rendering the parent component; components inside them are\n * mounted by the framework's resolver as the builder reaches them.\n */\nexport type UiChild = UiElement | ComponentLikeElement | Observable<UiChild | readonly UiChild[]>;\n\n/**\n * Declarative representation of a UI element.\n *\n * UiElement is NOT a runtime UiNode.\n *\n * It describes what the UI should look like.\n * UiGraphBuilder is responsible for turning this definition\n * into the runtime UiNode tree.\n */\nexport interface UiElement {\n /**\n * Runtime node type that should be created.\n */\n readonly type: UiNodeType;\n\n /**\n * Properties belonging to this element.\n */\n readonly props: UiProps;\n\n /**\n * Child elements, observable streams of elements, or component\n * definitions.\n */\n readonly children: readonly UiChild[];\n}\n\n/**\n * Returns true when the value is a component definition.\n */\nexport function isComponentLikeElement(value: unknown): value is ComponentLikeElement {\n return typeof value === 'object' && value !== null && (value as Partial<ComponentLikeElement>).kind === 'component';\n}\n\n/**\n * Returns true when the value is an RxJS Observable.\n *\n * Uses structural detection so Observable subclasses and instances\n * from different bundles are recognized.\n */\nexport function isObservable(value: unknown): value is Observable<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n !isComponentLikeElement(value) &&\n typeof (value as Partial<Observable<unknown>>).subscribe === 'function'\n );\n}\n\n/**\n * Returns true when the value is a UiChild (element, observable, or\n * component definition).\n */\nexport function isUiChild(value: unknown): value is UiChild {\n return isUiElement(value) || isObservable(value) || isComponentLikeElement(value);\n}\n\n/**\n * Returns true when the value structurally matches a UiElement.\n *\n * Used to distinguish a UiElement from a plain UiProps record at runtime.\n */\nexport function isUiElement(value: unknown): value is UiElement {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const candidate = value as Partial<UiElement>;\n return (\n (Object.values(UiNodeType) as unknown[]).includes(candidate.type) &&\n typeof candidate.props === 'object' &&\n candidate.props !== null &&\n !Array.isArray(candidate.props) &&\n Array.isArray(candidate.children)\n );\n}\n","import { UiNodeType } from '../graph/UiNodeType';\nimport { type UiChild, type UiElement, isUiElement } from './UiElement';\nimport type { UiProps } from './UiProps';\n\n/**\n * Creates a declarative UI element.\n *\n * This does not create a UiNode and does not interact with UiGraph.\n */\nexport function createElement(type: UiNodeType, props: UiProps = {}, children: readonly UiChild[] = []): UiElement {\n if (isUiElement(props)) {\n throw new Error(\n `createElement: props for '${type}' look like a UiElement. ` +\n `Did you forget to call the component with props before its children? ` +\n `Elements can only be passed as children, never as props.`\n );\n }\n return {\n type,\n props,\n children\n };\n}\n","import type { UiModifierHost } from './UiModifierHost';\n\n/**\n * A kind of behaviour that can be attached to an element.\n *\n * One kind per behaviour, not per use: `hoverable()` produces many\n * `UiModifier` values that all share the one `hoverableKind`. The kind\n * holds the lifecycle; the value holds the arguments.\n */\nexport interface UiModifierKind<A> {\n /** Identity for reconciliation and for the inspector. */\n readonly key: symbol;\n readonly name: string;\n attach(host: UiModifierHost, args: A): void;\n /**\n * Same kind, same slot, different arguments. Omitting it means a\n * change is a detach followed by an attach.\n */\n update?(host: UiModifierHost, args: A, previous: A): void;\n detach?(host: UiModifierHost): void;\n}\n\n/**\n * What an element carries: a kind and the arguments to give it.\n *\n * `key` distinguishes two instances of one kind on one element, as\n * `key` distinguishes two children; without one they match by the\n * order they appear in among their own kind.\n */\nexport interface UiModifier<A = unknown> {\n readonly kind: UiModifierKind<A>;\n readonly args: A;\n readonly key?: string | number;\n}\n\n/**\n * Defines a modifier kind and returns its factory.\n *\n * const hoverableKind = { name: 'hoverable', attach(host) { … } };\n * export const hoverable = defineModifier(hoverableKind);\n *\n * The symbol is created here so two modules cannot accidentally share\n * a kind identity, and so a kind is never compared by name.\n */\nexport function defineModifier<A>(\n kind: Omit<UiModifierKind<A>, 'key'>\n): ((args: A, key?: string | number) => UiModifier<A>) & { readonly kind: UiModifierKind<A> } {\n const full: UiModifierKind<A> = { ...kind, key: Symbol(kind.name) };\n const factory = (args: A, key?: string | number): UiModifier<A> => ({ kind: full, args, key });\n return Object.assign(factory, { kind: full });\n}\n\nexport function isUiModifier(value: unknown): value is UiModifier {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const kind = (value as Partial<UiModifier>).kind;\n return typeof kind === 'object' && kind !== null && typeof kind.key === 'symbol' && typeof kind.attach === 'function';\n}\n","import type { Subject } from 'rxjs';\n\nimport { UiEventType } from '../input/UiInputEvent';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport { UiVisualState, type UiVisualStateSet } from '../properties/UiVisualState';\nimport { defineModifier, type UiModifier } from './UiModifier';\n\n/**\n * Pointer state, as a property anything can read, and the property\n * writes that follow from it.\n *\n * `visualState` has been a registered, paint-affecting property since\n * the first renderer and **nothing has ever written it** — which is\n * why an app that wanted a row to light up on hover kept a\n * `state(false)` and two handlers per widget. This is the one writer.\n *\n * Neither renderer reads `visualState` either: nothing resolves a\n * colour from a state yet, and what a themed control looks like when\n * hovered is F3's theming decision, not this modifier's. So the state\n * is published for whoever reads it, and `hovered` / `pressed` carry\n * the property values to write while the state lasts. They go through\n * the override cascade, so leaving restores exactly what the element\n * declared — including nothing at all.\n *\n * Hover and press are one modifier rather than two on purpose: two\n * modifiers writing `visualState` would be a real conflict, with the\n * later one's set dropping the other's state, and the pair is wanted\n * together often enough that splitting them would mostly produce that\n * bug. `hoverable()` and `pressable()` are this kind with one half\n * switched off.\n */\nexport interface InteractiveOptions {\n readonly hover: boolean;\n readonly press: boolean;\n /** Properties to write while the pointer is over the node. */\n readonly hovered?: Readonly<Record<string, unknown>>;\n /** Properties to write while it is held down, over the hovered ones. */\n readonly pressed?: Readonly<Record<string, unknown>>;\n}\n\nconst BOTH: InteractiveOptions = Object.freeze({ hover: true, press: true });\nconst HOVER_ONLY: InteractiveOptions = Object.freeze({ hover: true, press: false });\nconst PRESS_ONLY: InteractiveOptions = Object.freeze({ hover: false, press: true });\n\n// #region kind\nconst kind = defineModifier<InteractiveOptions>({\n name: 'interactive',\n attach(host, options) {\n // Per-attachment state: one node's pointer, not the kind's.\n let hovered = false;\n let pressed = false;\n const styled = new Set([...Object.keys(options.hovered ?? {}), ...Object.keys(options.pressed ?? {})]);\n\n const sync = (): void => {\n const states = new Set<UiVisualState>();\n if (hovered) {\n states.add(UiVisualState.Hovered);\n }\n if (pressed) {\n states.add(UiVisualState.Pressed);\n }\n if (states.size === 0) {\n states.add(UiVisualState.Normal);\n }\n const current = host.get<UiVisualStateSet>('visualState');\n if (!sameStates(current, states)) {\n host.set('visualState', states);\n }\n for (const property of styled) {\n const value =\n (pressed ? options.pressed?.[property] : undefined) ?? (hovered ? options.hovered?.[property] : undefined);\n if (value === undefined) {\n host.clear(property);\n } else {\n host.set(property, value);\n }\n }\n };\n\n if (options.hover) {\n host.on(UiEventType.PointerEnter, () => {\n hovered = true;\n sync();\n });\n host.on(UiEventType.PointerLeave, () => {\n hovered = false;\n // A pointer that leaves during a press never sends the up.\n pressed = false;\n sync();\n });\n }\n if (options.press) {\n host.on(UiEventType.PointerDown, () => {\n pressed = true;\n sync();\n });\n host.on(UiEventType.PointerUp, () => {\n pressed = false;\n sync();\n });\n host.on(UiEventType.PointerCancel, () => {\n pressed = false;\n sync();\n });\n }\n }\n // No detach: the host restores every property this modifier wrote\n // and removes its listeners. There is nothing else it holds.\n});\n// #endregion kind\n\n/** Hover and press. */\nexport function interactive(options: InteractiveOptions = BOTH): UiModifier<InteractiveOptions> {\n return kind(options);\n}\n\n/** Hover only. */\nexport function hoverable(): UiModifier<InteractiveOptions> {\n return kind(HOVER_ONLY);\n}\n\n/** Press only. */\nexport function pressable(): UiModifier<InteractiveOptions> {\n return kind(PRESS_ONLY);\n}\n\n/**\n * What every Button carries. One shared value, so `sameArgs` matches it\n * on its first line and never walks the options: a button pays nothing\n * per render for this. `interactive` declares no `update`, so arguments\n * that genuinely differ do detach and re-attach it, losing the hover and\n * press state with them, which is the reason this one is a constant.\n */\nexport const BUTTON_INTERACTION: UiModifier<InteractiveOptions> = kind(BOTH);\n\nfunction sameStates(a: UiVisualStateSet, b: ReadonlySet<UiVisualState>): boolean {\n if (a.size !== b.size) {\n return false;\n }\n for (const state of b) {\n if (!a.has(state)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Reports the node's box whenever it moves: the `ResizeObserver` a\n * canvas does not have.\n *\n * The subject is the caller's, so a component can attach this to an\n * element it renders and read the box back — which is how a split pane\n * turns a pointer position into a fraction of its own track without\n * reaching into the layout engine.\n */\nexport const measure = defineModifier<Subject<LayoutBox>>({\n name: 'measure',\n attach(host, target) {\n const box = host.layoutBox();\n if (box !== null) {\n target.next(box);\n }\n host.onLayout(next => target.next(next));\n }\n});\n","import { UiNodeType } from '../graph/UiNodeType';\nimport { BUTTON_INTERACTION } from '../modifiers/interaction';\nimport { createElement } from './UiFactory';\nimport {\n type UiChild,\n type UiElement,\n isComponentLikeElement,\n isUiChild,\n isUiElement,\n isObservable\n} from './UiElement';\nimport type {\n BoxProps,\n ButtonProps,\n ColumnProps,\n EditableTextProps,\n GridProps,\n PaintElementProps,\n RowProps,\n ScrollViewProps,\n StackProps,\n TextProps\n} from './UiElementProps';\nimport type { UiProps } from './UiProps';\n\n/**\n * Creates a Text element.\n */\nexport function Text(props: TextProps = {}): UiElement {\n return createElement(UiNodeType.Text, props);\n}\n\n/**\n * Creates an EditableText element: text the user can type into, with\n * a caret, a selection and IME composition. The runtime owns the\n * editing; the app sets `value` and listens to `onInput`.\n */\nexport function EditableText(props: EditableTextProps = {}): UiElement {\n return createElement(UiNodeType.EditableText, props);\n}\n\n/**\n * Creates a Button element.\n *\n * Every button is interactive: it carries the modifier that publishes\n * hover and press as `visualState`, so a themed control can paint from\n * it without the app keeping a boolean per widget. A button that\n * declares its own modifiers keeps them, with the interaction one\n * first, so a caller's write of the same property wins.\n *\n * **Unless the caller brought their own interaction.** Two `interactive`\n * modifiers on one node is the conflict `modifiers/interaction.ts`\n * exists to avoid: both write `visualState`, so the later one's set\n * drops the earlier one's state and the two disagree about what the\n * pointer is doing. A caller who passes `interactive(...)` is asking\n * for their configuration rather than a second copy of the default,\n * so the default steps aside. This was found with the inspector, which\n * showed a playground button carrying `interactive, interactive`.\n */\nexport function Button(props: ButtonProps = {}, ...children: UiChild[]): UiElement {\n const declared = props.modifiers;\n const modifiers =\n declared === undefined\n ? DEFAULT_BUTTON_MODIFIERS\n : declared.some(modifier => modifier.kind === BUTTON_INTERACTION.kind)\n ? declared\n : [BUTTON_INTERACTION, ...declared];\n return createElement(UiNodeType.Button, { ...props, modifiers }, children);\n}\n\nconst DEFAULT_BUTTON_MODIFIERS = Object.freeze([BUTTON_INTERACTION]);\n\n/**\n * Creates a Box element.\n *\n * Boxes are leaves or plain stacked containers sized by\n * explicit width/height (or flex props).\n */\nexport function Box(props: BoxProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.Box, props, children);\n}\n\n/**\n * Creates a Stack element: a Box whose children overlap in one content\n * box, aligned by `x` / `y` (start, center, end, stretch) and per child\n * by `selfX` / `selfY`. Same runtime node as Box; the name says what\n * the children do.\n */\nexport function Stack(props: StackProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.Box, props, children);\n}\n\n/**\n * Creates a Paint element: a Box the application draws into.\n *\n * `paint` is a function given a `PaintSurface`, the resolved box and\n * the device scale; `path` is a static vector shape stated as a\n * property. A node may carry either or both, and children stack over\n * whatever it drew, so a gauge with a label in the middle is one\n * element with a text child.\n *\n * The drawing runs when the painter's `inputs` change and not once per\n * frame, which is the whole reason `UiPaint` has an `inputs` field.\n * See `rendering/PaintSurface.ts`.\n */\nexport function Paint(props: PaintElementProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.Paint, props, children);\n}\n\n/**\n * Creates a Grid element.\n *\n * Children fill cells of the tracks in `columns` / `rows` (numbers,\n * `percent()`, `auto`, `fr()`, `minmax()`), by `autoFlow` order or by\n * explicit `column` / `row` (1-based lines) and `columnSpan` / `rowSpan`.\n * `x` / `y` align items in their cells (stretch by default),\n * `justifyContent` / `alignContent` distribute the tracks.\n */\nexport function Grid(props: GridProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.Grid, props, children);\n}\n\n/**\n * Creates a Row element.\n *\n * Accepts either children only or props followed by children.\n */\nexport function Row(...children: UiChild[]): UiElement;\nexport function Row(props: RowProps, ...children: UiChild[]): UiElement;\nexport function Row(first: RowProps | UiChild = {}, ...rest: UiChild[]): UiElement {\n if (isUiChild(first) && !isPlainProps(first)) {\n return createElement(UiNodeType.Row, {}, [first, ...rest]);\n }\n return createElement(UiNodeType.Row, first as UiProps, rest);\n}\n\n/**\n * Creates a Column element.\n *\n * Accepts either children only or props followed by children.\n */\nexport function Column(...children: UiChild[]): UiElement;\nexport function Column(props: ColumnProps, ...children: UiChild[]): UiElement;\nexport function Column(first: ColumnProps | UiChild = {}, ...rest: UiChild[]): UiElement {\n if (isUiChild(first) && !isPlainProps(first)) {\n return createElement(UiNodeType.Column, {}, [first, ...rest]);\n }\n return createElement(UiNodeType.Column, first as UiProps, rest);\n}\n\n/**\n * Creates a ScrollView element.\n */\nexport function ScrollView(props: ScrollViewProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.ScrollView, props, children);\n}\n\n/**\n * Returns true when a value is a plain props record rather than a child.\n *\n * Props are plain objects. Children are UiElements or Observables.\n */\nfunction isPlainProps(value: unknown): value is UiProps {\n return (\n typeof value === 'object' &&\n value !== null &&\n !isUiElement(value) &&\n !isObservable(value) &&\n !isComponentLikeElement(value)\n );\n}\n","import { UiEventType } from '../input/UiInputEvent';\n\n/**\n * A prop is treated as an event handler when it is named `on` +\n * PascalCase and holds a function. Props matching that shape whose\n * value is not a function stay ordinary node properties.\n */\nconst EVENT_PROP_PATTERN = /^on[A-Z]/;\n\n/**\n * Declarative prop name for each input event.\n *\n * Spelled out rather than derived: UiEventType values are lowercase\n * concatenations ('pointerdown', 'longpress'), so the camelCase prop\n * name cannot be recovered from them. UiEventProps.spec asserts this\n * table stays exhaustive, so adding an event type without a prop name\n * fails the build rather than silently going unbindable.\n */\nconst EVENT_PROPS: Readonly<Record<string, UiEventType>> = {\n onPointerDown: UiEventType.PointerDown,\n onPointerUp: UiEventType.PointerUp,\n onPointerMove: UiEventType.PointerMove,\n onPointerCancel: UiEventType.PointerCancel,\n onPointerEnter: UiEventType.PointerEnter,\n onPointerLeave: UiEventType.PointerLeave,\n onWheel: UiEventType.Wheel,\n onKeyDown: UiEventType.KeyDown,\n onKeyUp: UiEventType.KeyUp,\n onFocus: UiEventType.Focus,\n onBlur: UiEventType.Blur,\n onBeforeInput: UiEventType.BeforeInput,\n onInput: UiEventType.Input,\n onClick: UiEventType.Click,\n onLongPress: UiEventType.LongPress,\n onDragStart: UiEventType.DragStart,\n onDragMove: UiEventType.DragMove,\n onDragEnd: UiEventType.DragEnd,\n onPanStart: UiEventType.PanStart,\n onPanMove: UiEventType.PanMove,\n onPanEnd: UiEventType.PanEnd,\n onContextMenu: UiEventType.ContextMenu,\n onPinchStart: UiEventType.PinchStart,\n onPinchMove: UiEventType.PinchMove,\n onPinchEnd: UiEventType.PinchEnd\n};\n\n/**\n * Whether a prop name/value pair declares an event handler.\n */\nexport function isEventProp(property: string, value: unknown): boolean {\n return typeof value === 'function' && EVENT_PROP_PATTERN.test(property);\n}\n\n/**\n * The event a handler prop binds to, or undefined when the name is\n * not a recognized event prop.\n */\nexport function eventTypeForProp(property: string): UiEventType | undefined {\n return EVENT_PROPS[property];\n}\n\n/**\n * Every accepted event prop name, sorted, for error messages.\n */\nexport function knownEventPropNames(): string[] {\n return Object.keys(EVENT_PROPS).sort();\n}\n","/**\n * Whether two modifier arguments mean the same thing.\n *\n * A modifier's arguments are compared when its element is rebuilt, and\n * a difference is a reason to update the modifier or, for a kind with no\n * `update`, to detach it and attach a fresh one. Identity was the\n * comparison, and it made `modifiers={[interactive({ hovered: ... })]}`\n * written inline a modifier that re-attached on every render, losing\n * its pointer state and paying its attach cost each time. Nothing warned\n * about it, and hoisting the value to module scope was folklore.\n *\n * So arguments are compared by what they hold: plain objects and arrays\n * recursively, primitives by value, and everything else, functions,\n * Observables, class instances, by identity, because a new callback or\n * a new stream is a genuinely new argument. An inline `interactive(...)`\n * with the same options is now the same modifier, as it reads.\n */\nexport function sameArgs(a: unknown, b: unknown, depth = 0): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n if (depth > 8 || typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {\n return false;\n }\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {\n return false;\n }\n for (let index = 0; index < a.length; index++) {\n if (!sameArgs(a[index], b[index], depth + 1)) {\n return false;\n }\n }\n return true;\n }\n if (!isPlain(a) || !isPlain(b)) {\n return false;\n }\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) {\n return false;\n }\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(b, key)) {\n return false;\n }\n if (!sameArgs((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key], depth + 1)) {\n return false;\n }\n }\n return true;\n}\n\nfunction isPlain(value: object): boolean {\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","import { EMPTY, isObservable, type Observable, type Subscription } from 'rxjs';\n\nimport {\n AnimationDriver,\n UiSharedElements,\n UiSpring,\n createTween,\n type AnimatedCell,\n type UiSpringOptions,\n type UiTweenOptions\n} from '../animation';\n\nimport { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiEnvironmentKey } from '../environment/UiEnvironmentKey';\nimport type { UiGraph } from '../graph/UiGraph';\nimport type { UiNode } from '../graph/UiNode';\nimport { clearOverrideProperty, writeOverrideProperty } from '../graph/UiPropertyOverrides';\nimport type { UiEventListener, UiEventListenerOptions, UiInputDispatcher } from '../input/UiInputDispatcher';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { UiEventType } from '../input/UiInputEvent';\nimport type { DecorationShape } from '../rendering/Decorations';\nimport { findPropertyDefinition, propertyEffects } from '../properties/UiPropertyRegistry';\nimport { resolveProperty } from '../properties/UiPropertyResolver';\nimport { isUiModifier, type UiModifier, type UiModifierKind } from './UiModifier';\nimport { sameArgs } from './sameArgs';\nimport type { UiModifierHost, UiModifierTeardown } from './UiModifierHost';\n\n/**\n * A modifier named a property that is not in the registry.\n *\n * Its own class because it is the one failure a modifier can raise\n * that must **not** be contained: an unknown property name is a typo,\n * caught the moment the modifier runs, and the registry's closedness\n * is what makes that a hard error rather than a silently ignored\n * write. Everything else a modifier throws is contained, reported and\n * detached; this is rethrown.\n */\nexport class UiUnknownModifierPropertyError extends Error {}\n\n/** One attached instance: its slot, its current arguments, its host. */\ninterface Attached {\n readonly kind: UiModifierKind<unknown>;\n readonly slot: string | number;\n readonly host: Host;\n args: unknown;\n}\n\n/** Where a modifier's layout access comes from; the runtime supplies it. */\nexport interface UiModifierLayout {\n box(node: UiNode): LayoutBox | null;\n /** The same node in pre-scroll layout coordinates; see `UiModifierHost.flowBox`. */\n flowBox(node: UiNode): LayoutBox | null;\n /** The node's own scroll offset; see `UiModifierHost.scrollOffset`. */\n scroll(node: UiNode): { x: number; y: number } | null;\n onLayout(node: UiNode, listener: (box: LayoutBox) => void): () => void;\n}\n\n/**\n * Where a modifier's focus access comes from.\n *\n * Supplied by the runtime, which owns the focus manager. Kept behind\n * an interface for the same reason layout is: `packages/core/src/composition`\n * must not depend on the input stack, and a headless build has\n * neither.\n */\nexport interface UiModifierFocus {\n isFocused(node: UiNode): boolean;\n /** Whether the focus `node` holds should be shown; see `UiFocusManager.focusVisible`. */\n isFocusVisible(node: UiNode): boolean;\n focus(node: UiNode): void;\n onFocusChange(node: UiNode, listener: (focused: boolean) => void): () => void;\n}\n\n/** Where a modifier's environment access comes from. */\nexport interface UiModifierEnvironment {\n read<T>(node: UiNode, key: UiEnvironmentKey<T>): T;\n onChange(node: UiNode, listener: () => void): () => void;\n}\n\n/**\n * The modifiers attached to one node, and their reconciliation.\n *\n * Matching is by `(kind, key ?? position among that kind)`, the same\n * rule keyed children follow: an instance survives a re-render when it\n * lands in the same slot, and its `update` is called only when the\n * arguments actually differ. A kind with no `update` is detached and\n * re-attached instead, which is the honest behaviour for one that\n * cannot describe a change.\n *\n * The set keeps list order, because order is semantic: B1's override\n * layer resolves a conflict in favour of whichever modifier is later.\n */\nexport class UiModifierSet {\n private attached: Attached[] = [];\n /** Built on the first `decorate`, so an undecorated node allocates nothing. */\n private decorations: NodeDecorations | null = null;\n\n constructor(\n private readonly node: UiNode,\n private readonly graph: UiGraph,\n private readonly dispatcher?: UiInputDispatcher,\n private readonly layout?: UiModifierLayout,\n private readonly focus?: UiModifierFocus,\n private readonly environment?: UiModifierEnvironment,\n private readonly animations?: AnimationDriver,\n private readonly sharedElements?: UiSharedElements\n ) {}\n\n /** The merged decoration list, created on demand. */\n private decorationsFor(): NodeDecorations {\n if (this.decorations === null) {\n this.decorations = new NodeDecorations(this.node, this.graph);\n }\n return this.decorations;\n }\n\n /** The names of what is attached, in order, for the inspector. */\n get names(): string[] {\n return this.attached.map(entry => entry.kind.name);\n }\n\n get size(): number {\n return this.attached.length;\n }\n\n /**\n * Brings the attached set in line with what the element declares.\n *\n * Runs after the node's declared props are written and before its\n * children are reconciled, so a modifier sees the element's own\n * values and may act on them before layout reads anything.\n */\n reconcile(modifiers: readonly UiModifier[]): void {\n const previous = this.attached;\n const taken = new Set<Attached>();\n const next: Attached[] = [];\n const ordinals = new Map<symbol, number>();\n\n for (const modifier of modifiers) {\n const kind = modifier.kind as UiModifierKind<unknown>;\n const ordinal = ordinals.get(kind.key) ?? 0;\n ordinals.set(kind.key, ordinal + 1);\n const slot = modifier.key ?? ordinal;\n const match = previous.find(entry => entry.kind.key === kind.key && entry.slot === slot && !taken.has(entry));\n if (match === undefined) {\n const attached = this.attach(kind, slot, modifier.args);\n if (attached !== null) {\n next.push(attached);\n }\n continue;\n }\n taken.add(match);\n const updated = this.update(match, modifier.args);\n if (updated !== null) {\n next.push(updated);\n }\n }\n\n for (const [order, entry] of next.entries()) {\n entry.host.setOrder(order);\n }\n // Order is semantic for decorations too: a node paints its\n // modifiers' shapes in the order the element listed them.\n this.decorations?.reorder();\n\n for (const entry of previous) {\n if (!taken.has(entry)) {\n this.detachOne(entry);\n }\n }\n this.attached = next;\n }\n\n /** Releases everything, for a node leaving the tree. */\n detach(): void {\n for (let index = this.attached.length - 1; index >= 0; index--) {\n this.detachOne(this.attached[index]);\n }\n this.attached = [];\n }\n\n /**\n * Attaches one modifier, or reports it and returns null.\n *\n * A modifier that throws while attaching had already been able to\n * write properties, register listeners and own teardowns, so the\n * half-attached host is released rather than dropped: without that,\n * the overrides it wrote would stay on the node with nothing left to\n * take them off again.\n */\n private attach(kind: UiModifierKind<unknown>, slot: string | number, args: unknown): Attached | null {\n const host = new Host(this.node, this.graph, kind.name, () => this.decorationsFor(), {\n dispatcher: this.dispatcher,\n layout: this.layout,\n focus: this.focus,\n environment: this.environment,\n animations: this.animations,\n sharedElements: this.sharedElements\n });\n const entry: Attached = { kind, slot, host, args };\n try {\n kind.attach(host, args);\n } catch (error) {\n host.release();\n if (error instanceof UiUnknownModifierPropertyError) {\n throw error;\n }\n this.graph.handleModifierError(kind.name, this.node, 'attach', error);\n return null;\n }\n return entry;\n }\n\n private update(entry: Attached, args: unknown): Attached | null {\n if (sameArgs(entry.args, args)) {\n // The same arguments, whether the same object or an equal one\n // written inline: nothing to tell the modifier, and nothing to\n // re-attach. See `sameArgs` for what counts as equal.\n entry.args = args;\n return entry;\n }\n if (entry.kind.update === undefined) {\n // Nothing to tell it, so it is a different instance: tear the old\n // one down before the new one attaches, so their resources never\n // overlap.\n this.detachOne(entry);\n return this.attach(entry.kind, entry.slot, args);\n }\n const previous = entry.args;\n entry.args = args;\n try {\n entry.kind.update(entry.host, args, previous);\n } catch (error) {\n this.detachOne(entry);\n if (error instanceof UiUnknownModifierPropertyError) {\n throw error;\n }\n this.graph.handleModifierError(entry.kind.name, this.node, 'update', error);\n return null;\n }\n return entry;\n }\n\n private detachOne(entry: Attached): void {\n try {\n entry.kind.detach?.(entry.host);\n } catch (error) {\n if (error instanceof UiUnknownModifierPropertyError) {\n entry.host.release();\n throw error;\n }\n this.graph.handleModifierError(entry.kind.name, this.node, 'detach', error);\n }\n // Outside the catch: the host's own release is the framework's, and\n // it is what restores the node. A modifier's `detach` failing must\n // not leave an override behind.\n entry.host.release();\n }\n}\n\n/**\n * The host handed to one attached modifier.\n *\n * Its `source` symbol is what the override cascade records, so two\n * instances of one kind on a node write independently and each\n * restores only its own.\n */\ninterface HostServices {\n dispatcher?: UiInputDispatcher;\n layout?: UiModifierLayout;\n focus?: UiModifierFocus;\n environment?: UiModifierEnvironment;\n animations?: AnimationDriver;\n sharedElements?: UiSharedElements;\n}\n\nclass Host implements UiModifierHost {\n private teardowns: UiModifierTeardown[] = [];\n private readonly source: symbol;\n private readonly written = new Set<string>();\n private readonly subscriptions = new Map<string, Subscription>();\n private order = 0;\n private decorated = false;\n /** Cells this modifier is driving, stopped when it detaches. */\n private readonly animated = new Set<AnimatedCell<unknown>>();\n\n constructor(\n readonly node: UiNode,\n private readonly graph: UiGraph,\n private readonly name: string,\n private readonly decorations: () => NodeDecorations,\n private readonly services: HostServices\n ) {\n this.source = Symbol(name);\n }\n\n /** The modifier's position in the element's list; later wins. */\n setOrder(order: number): void {\n if (order === this.order) {\n return;\n }\n this.order = order;\n for (const property of this.written) {\n this.write(property, this.node.properties.get(property));\n }\n if (this.decorated) {\n this.decorations().setOrder(this.source, order);\n }\n }\n\n get<T>(property: string): T {\n const definition = findPropertyDefinition<T>(property);\n if (definition === undefined) {\n throw new UiUnknownModifierPropertyError(\n `Modifier '${this.name}' read unknown property '${property}' on node '${this.node.id}'.`\n );\n }\n return resolveProperty(this.node, definition);\n }\n\n set(property: string, value: unknown): void {\n if (findPropertyDefinition(property) === undefined) {\n throw new UiUnknownModifierPropertyError(\n `Modifier '${this.name}' wrote unknown property '${property}' on node '${this.node.id}'.`\n );\n }\n this.subscriptions.get(property)?.unsubscribe();\n this.subscriptions.delete(property);\n if (isObservable(value)) {\n // Held for as long as the modifier is attached; each emission is\n // another write of the same override.\n this.subscriptions.set(\n property,\n (value as Observable<unknown>).subscribe(next => this.write(property, next))\n );\n this.written.add(property);\n return;\n }\n this.write(property, value);\n }\n\n clear(property: string): void {\n this.subscriptions.get(property)?.unsubscribe();\n this.subscriptions.delete(property);\n if (!this.written.delete(property)) {\n return;\n }\n clearOverrideProperty(this.graph, this.node, property, this.source, propertyEffects(property));\n }\n\n on(type: UiEventType, listener: UiEventListener, options?: UiEventListenerOptions): void {\n const dispatcher = this.services.dispatcher;\n if (dispatcher === undefined) {\n warnMissingDispatcher(this.name);\n return;\n }\n dispatcher.addEventListener(this.node, type, listener, options);\n this.own(() => dispatcher.removeEventListener(this.node, type, listener, options));\n }\n\n onRoot(type: UiEventType, listener: UiEventListener, options: UiEventListenerOptions = { capture: true }): void {\n const dispatcher = this.services.dispatcher;\n if (dispatcher === undefined) {\n warnMissingDispatcher(this.name);\n return;\n }\n // The graph's root, which is above the layout root and therefore\n // above both the application's tree and the overlay layer beside\n // it. A press on a menu opened over the page is dispatched into\n // the overlay layer, and a modifier on the page still has to hear\n // about it.\n const root = this.graph.root;\n dispatcher.addEventListener(root, type, listener, options);\n this.own(() => dispatcher.removeEventListener(root, type, listener, options));\n }\n\n layoutBox(): LayoutBox | null {\n return this.services.layout?.box(this.node) ?? null;\n }\n\n flowBox(): LayoutBox | null {\n return this.services.layout?.flowBox(this.node) ?? null;\n }\n\n onLayout(listener: (box: LayoutBox) => void): void {\n const layout = this.services.layout;\n if (layout === undefined) {\n warnMissingLayout(this.name);\n return;\n }\n this.own(layout.onLayout(this.node, listener));\n }\n\n environment<T>(key: UiEnvironmentKey<T>): T {\n const environment = this.services.environment;\n if (environment === undefined) {\n // The default is what an unprovided key resolves to anyway, so a\n // headless build reads the same value the tree would.\n return key.defaultValue;\n }\n return environment.read(this.node, key);\n }\n\n onEnvironment(listener: () => void): void {\n const environment = this.services.environment;\n if (environment === undefined) {\n warnMissing(this.name, 'follow its environment', 'environment');\n return;\n }\n this.own(environment.onChange(this.node, listener));\n }\n\n focus(): void {\n const focus = this.services.focus;\n if (focus === undefined) {\n warnMissing(this.name, 'move focus', 'focus');\n return;\n }\n focus.focus(this.node);\n }\n\n isFocused(): boolean {\n return this.services.focus?.isFocused(this.node) ?? false;\n }\n\n isFocusVisible(): boolean {\n return this.services.focus?.isFocusVisible(this.node) ?? false;\n }\n\n onFocusChange(listener: (focused: boolean) => void): void {\n const focus = this.services.focus;\n if (focus === undefined) {\n warnMissing(this.name, 'follow focus', 'focus');\n return;\n }\n this.own(focus.onFocusChange(this.node, listener));\n }\n\n decorate(shapes: readonly DecorationShape[] | null): void {\n this.decorated = shapes !== null;\n this.decorations().set(this.source, this.order, shapes);\n }\n\n animate<T>(cell: AnimatedCell<T>, to: T, options: UiTweenOptions): Observable<T> {\n const driver = this.services.animations;\n const tween = driver === undefined ? undefined : createTween(cell, to, options);\n if (driver === undefined || tween === undefined) {\n if (driver === undefined) {\n warnMissing(this.name, 'animate a value', 'animations');\n }\n cell.value = to;\n return EMPTY as Observable<T>;\n }\n this.animated.add(cell as AnimatedCell<unknown>);\n return driver.start(tween);\n }\n\n spring(cell: AnimatedCell<number>, to: number, options: UiSpringOptions): Observable<number> {\n const driver = this.services.animations;\n if (driver === undefined) {\n warnMissing(this.name, 'animate a value', 'animations');\n cell.value = to;\n return EMPTY as Observable<number>;\n }\n this.animated.add(cell as AnimatedCell<unknown>);\n // Retargeting a spring keeps the velocity it had, as\n // `AnimationService.spring` does: a spring that started again from\n // rest every time it was re-aimed would crawl, which is exactly\n // what a modifier re-aimed once a frame does.\n const previous = driver.animationFor(cell);\n const velocity = options.velocity ?? (previous instanceof UiSpring ? previous.currentVelocity : 0);\n return driver.start(new UiSpring(cell, to, { ...options, velocity }));\n }\n\n own(teardown: UiModifierTeardown): void {\n this.teardowns.push(teardown);\n }\n\n scrollOffset(): { x: number; y: number } | null {\n return this.services.layout?.scroll(this.node) ?? null;\n }\n\n stopAnimation<T>(cell: AnimatedCell<T>): void {\n this.services.animations?.stop(cell as AnimatedCell<unknown>);\n }\n\n get shared(): UiSharedElements | null {\n const registry = this.services.sharedElements;\n if (registry === undefined) {\n warnMissing(this.name, 'share an element across a tree change', 'sharedElements');\n return null;\n }\n return registry;\n }\n\n requestFrame(): void {\n this.graph.markDirty(this.node, DirtyFlags.Paint);\n }\n\n private write(property: string, value: unknown): void {\n this.written.add(property);\n writeOverrideProperty(\n this.graph,\n this.node,\n property,\n { source: this.source, name: this.name, order: this.order, value },\n propertyEffects(property)\n );\n }\n\n /**\n * Restores everything this modifier wrote, then runs its teardowns\n * in reverse, as a stack unwinds.\n */\n release(): void {\n // clear() deletes the entry it is given, which is the one being\n // visited; removing the current element of a Set mid-iteration is\n // defined and does not skip the next.\n for (const property of this.written) {\n this.clear(property);\n }\n if (this.decorated) {\n this.decorate(null);\n }\n for (const cell of this.animated) {\n this.services.animations?.stop(cell);\n }\n this.animated.clear();\n const teardowns = this.teardowns;\n this.teardowns = [];\n for (let index = teardowns.length - 1; index >= 0; index--) {\n const teardown = teardowns[index];\n if (typeof teardown === 'function') {\n teardown();\n } else {\n teardown.unsubscribe();\n }\n }\n }\n}\n\n/**\n * The merged decorations of one node.\n *\n * Every modifier that decorates contributes its own list; the node\n * paints the concatenation in modifier order, which is the same rule\n * the override cascade uses and for the same reason — order in the\n * element's list is the only thing that can settle a disagreement, and\n * it is the one a reader can see.\n *\n * `node.decorations` goes back to null when the last contributor\n * leaves, so a node that was decorated and is not any more costs\n * exactly what it did before.\n */\nclass NodeDecorations {\n private readonly bySource = new Map<symbol, { order: number; shapes: readonly DecorationShape[] }>();\n\n constructor(\n private readonly node: UiNode,\n private readonly graph: UiGraph\n ) {}\n\n set(source: symbol, order: number, shapes: readonly DecorationShape[] | null): void {\n if (shapes === null || shapes.length === 0) {\n if (!this.bySource.delete(source)) {\n return;\n }\n } else {\n this.bySource.set(source, { order, shapes });\n }\n this.rebuild();\n }\n\n setOrder(source: symbol, order: number): void {\n const entry = this.bySource.get(source);\n if (entry === undefined || entry.order === order) {\n return;\n }\n entry.order = order;\n this.rebuild();\n }\n\n reorder(): void {\n if (this.bySource.size > 1) {\n this.rebuild();\n }\n }\n\n private rebuild(): void {\n if (this.bySource.size === 0) {\n this.node.decorations = null;\n this.graph.markDirty(this.node, DirtyFlags.Paint);\n return;\n }\n const merged: DecorationShape[] = [];\n for (const entry of [...this.bySource.values()].sort((a, b) => a.order - b.order)) {\n merged.push(...entry.shapes);\n }\n this.node.decorations = merged;\n this.graph.markDirty(this.node, DirtyFlags.Paint);\n }\n}\n\nlet warnedAboutLayout = false;\n\nfunction warnMissingLayout(name: string): void {\n if (warnedAboutLayout) {\n return;\n }\n warnedAboutLayout = true;\n console.warn(\n `Modifier '${name}' asked to follow its node's box, but the UiGraphBuilder was constructed without layout access. ` +\n `Construct it with { layout } for onLayout to fire.`\n );\n}\n\nlet warnedAboutDispatcher = false;\n\nfunction warnMissingDispatcher(name: string): void {\n if (warnedAboutDispatcher) {\n return;\n }\n warnedAboutDispatcher = true;\n console.warn(\n `Modifier '${name}' asked to listen for input, but the UiGraphBuilder was constructed without a dispatcher. ` +\n `Construct it with { dispatcher } for modifiers to receive events.`\n );\n}\n\nconst warnedAbout = new Set<string>();\n\n/** One warning per missing service, as the two above do for theirs. */\nfunction warnMissing(name: string, what: string, option: string): void {\n if (warnedAbout.has(option)) {\n return;\n }\n warnedAbout.add(option);\n console.warn(\n `Modifier '${name}' asked to ${what}, but the UiGraphBuilder was constructed without ${option} access. ` +\n `Construct it with { ${option} }, as the runtime does.`\n );\n}\n\n/**\n * Rejects a `modifiers` prop that is not a list of modifiers, naming\n * the node the way an unknown prop does.\n */\nexport function assertModifierList(node: UiNode, value: unknown): readonly UiModifier[] {\n if (!Array.isArray(value)) {\n throw new Error(`The 'modifiers' prop on node '${node.id}' must be an array, got ${typeof value}.`);\n }\n for (const entry of value) {\n if (!isUiModifier(entry)) {\n throw new Error(\n `The 'modifiers' prop on node '${node.id}' must hold modifiers built by a factory from defineModifier.`\n );\n }\n }\n return value as readonly UiModifier[];\n}\n","import type { AnimatedCell, UiTransitionSpec } from '../animation';\nimport { AnimationDriver, UiSpring, createTween } from '../animation';\nimport type { DirtyFlags } from './DirtyFlags';\nimport type { UiGraph } from './UiGraph';\nimport type { NodeProperty, UiNode, UiNodeTransitions } from './UiNode';\n\n/**\n * The transitions declared on one node, and the animations they are\n * currently running.\n *\n * This is the same shape as the override cascade and for the same\n * reason that lives on the node: **both\n * the builder's static writes and a binding's emissions funnel through\n * `updateNodeProperty`**, so interposing there is the only place that\n * catches both. `node.transitions` is null until an element declares\n * one, so the cost to a tree without transitions is the second field\n * read on the write path.\n *\n * What it interposes is a write, not a binding. The Observable a\n * component wrote is still bound by the graph and still emits into the\n * same property; what changes is that the emission becomes a target\n * rather than a value. So nothing about component identity, binding\n * teardown or `getBindingForProperty` moves, and a `transition` can be\n * added to an element without touching anything else about it.\n */\nexport class NodeTransitions implements UiNodeTransitions {\n private specs: ReadonlyMap<NodeProperty, UiTransitionSpec> = new Map();\n private readonly cells = new Map<NodeProperty, AnimatedCell<unknown>>();\n private readonly targets = new Map<NodeProperty, unknown>();\n\n constructor(\n private readonly node: UiNode,\n private readonly graph: UiGraph,\n private readonly driver: AnimationDriver\n ) {}\n\n /** Replaces what the element declares; a property dropped from the list stops animating. */\n setSpecs(specs: ReadonlyMap<NodeProperty, UiTransitionSpec>): void {\n // Deleting the current key while iterating a Map is defined and\n // does not skip the next one.\n for (const property of this.cells.keys()) {\n if (!specs.has(property)) {\n this.stopProperty(property);\n }\n }\n this.specs = specs;\n }\n\n get size(): number {\n return this.specs.size;\n }\n\n /**\n * Takes over a write, or declines it.\n *\n * Declines when the property has no transition, when the node has no\n * value yet — a node's *first* value is not a change and animating\n * from nothing would mean animating from a default nobody asked for\n * — and when the two values cannot be blended (`interpolatorFor`\n * says which, and why).\n */\n write(property: NodeProperty, value: unknown, flags: DirtyFlags): boolean {\n const spec = this.specs.get(property);\n if (spec === undefined) {\n return false;\n }\n if (!this.node.properties.has(property)) {\n return false;\n }\n const current = this.node.properties.get(property);\n if (Object.is(current, value)) {\n return false;\n }\n // Already on its way there: a binding that re-emits the same target\n // must not restart the animation, or a spring would never settle.\n if (this.cells.has(property) && Object.is(this.targets.get(property), value)) {\n return true;\n }\n const cell = this.cellFor(property, flags);\n const started = this.startAnimation(cell, value, spec, property);\n if (!started) {\n this.stopProperty(property);\n return false;\n }\n this.targets.set(property, value);\n return true;\n }\n\n /** Cancels everything, for a node leaving the tree or dropping the prop. */\n release(): void {\n for (const cell of this.cells.values()) {\n this.driver.stop(cell);\n }\n this.cells.clear();\n this.targets.clear();\n this.specs = new Map();\n }\n\n private stopProperty(property: NodeProperty): void {\n const cell = this.cells.get(property);\n if (cell !== undefined) {\n this.driver.stop(cell);\n this.cells.delete(property);\n }\n this.targets.delete(property);\n }\n\n /**\n * The cell one property animates through, kept for the life of the\n * node: the driver keys its running set by cell identity, so a fresh\n * adapter per write would leave the previous animation running.\n *\n * Writing goes through `updateNodePropertyNow`, which is the path a\n * plain write takes minus this class — so a modifier's override still\n * sits on top of an animated value, exactly as it sits on top of a\n * bound one.\n */\n private cellFor(property: NodeProperty, flags: DirtyFlags): AnimatedCell<unknown> {\n let cell = this.cells.get(property);\n if (cell !== undefined) {\n return cell;\n }\n const node = this.node;\n const graph = this.graph;\n cell = {\n get value(): unknown {\n return node.properties.get(property);\n },\n set value(next: unknown) {\n graph.updateNodePropertyNow(node, property, next, flags);\n }\n };\n this.cells.set(property, cell);\n return cell;\n }\n\n private startAnimation(\n cell: AnimatedCell<unknown>,\n to: unknown,\n spec: UiTransitionSpec,\n property: NodeProperty\n ): boolean {\n if (spec.kind === 'spring') {\n if (typeof cell.value !== 'number' || typeof to !== 'number') {\n warnNotInterpolable(property);\n return false;\n }\n const previous = this.driver.animationFor(cell);\n const velocity = previous instanceof UiSpring ? previous.currentVelocity : 0;\n this.driver.start(\n new UiSpring(cell as AnimatedCell<number>, to, {\n spring: spec.spring,\n velocity,\n restDelta: spec.restDelta,\n reducedMotion: spec.reducedMotion\n })\n );\n return true;\n }\n const tween = createTween(cell, to, {\n duration: spec.duration,\n easing: spec.easing,\n stepMs: spec.stepMs,\n reducedMotion: spec.reducedMotion\n });\n if (tween === undefined) {\n warnNotInterpolable(property);\n return false;\n }\n this.driver.start(tween);\n return true;\n }\n}\n\nconst warnedProperties = new Set<string>();\n\n/**\n * One warning per property, then silence.\n *\n * Not an error: a `transition` on a property whose values cannot be\n * blended should still leave a working screen, and the value does get\n * written. But it must not be silent, because \"my transition does\n * nothing\" is otherwise indistinguishable from a typo.\n */\nfunction warnNotInterpolable(property: NodeProperty): void {\n if (warnedProperties.has(property)) {\n return;\n }\n warnedProperties.add(property);\n console.warn(\n `A 'transition' was declared for '${property}', but its values cannot be blended, so it is written directly. ` +\n `Numbers, UiColor objects and transforms can be animated; typed lengths (percent, fr, auto) and palette ` +\n `names cannot. See interpolatorFor in packages/core/src/animation/Interpolate.ts.`\n );\n}\n\n/** Test hook: forgets which properties have been reported. */\nexport function resetTransitionWarnings(): void {\n warnedProperties.clear();\n}\n","import { Observable } from 'rxjs';\n\nimport { UiChildrenBinding } from '../bindings/UiChildrenBinding';\nimport { UiEventBinding } from '../bindings/UiEventBinding';\nimport { DirtyFlags } from '../graph/DirtyFlags';\nimport { UiGraph } from '../graph/UiGraph';\nimport type { NodeProperty, UiNode } from '../graph/UiNode';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { UiEventListener, UiInputDispatcher } from '../input/UiInputDispatcher';\nimport {\n closestPropertyName,\n findPropertyDefinition,\n getPropertyNames,\n propertyEffects\n} from '../properties/UiPropertyRegistry';\nimport { eventTypeForProp, isEventProp, knownEventPropNames } from './UiEventProps';\nimport type { ComponentResolver } from './ComponentResolver';\nimport {\n type ComponentLikeElement,\n type UiChild,\n type UiElement,\n isComponentLikeElement,\n isObservable\n} from './UiElement';\nimport type { UiNodeRef } from './UiElementProps';\nimport type { UiProps } from './UiProps';\nimport {\n UiModifierSet,\n assertModifierList,\n type UiModifierEnvironment,\n type UiModifierFocus,\n type UiModifierLayout\n} from '../modifiers/UiModifierSet';\nimport { assertTransitionMap, type AnimationDriver, type UiSharedElements, type UiTransitionSpec } from '../animation';\nimport { NodeTransitions } from '../graph/UiPropertyTransitions';\n\n/**\n * Property reserved for reconciliation identity.\n *\n * The key never becomes a runtime property on the UiNode.\n */\nconst KEY_PROP = 'key';\n/**\n * `ref` receives the UiNode an element produced, and null when the\n * node is removed. It is how a component gets hold of a node to hand\n * to `anchor`, or to focus imperatively.\n */\nconst REF_PROP = 'ref';\n/**\n * `modifiers` attaches behaviour to an element without wrapping it.\n * Reconciled like children — matched by kind and slot — and detached\n * with the node. See `packages/core/src/modifiers`.\n */\nconst MODIFIERS_PROP = 'modifiers';\n/**\n * `transition` says how a property gets from one value to the next.\n *\n * The fourth reserved name, beside `key`, `ref` and `modifiers`, and\n * for the same reason those three are reserved rather than registered:\n * every one of them is about the *element* — its identity, who holds\n * its node, what is attached to it — rather than a value on the node.\n * Nothing in layout, paint, input or the environment ever reads a\n * `transition`; `propertyEffects('transition')` would have no meaning,\n * and registering it would make it bindable and overridable, which is\n * nonsense for a description of how other properties are written.\n * the rule that the registry's closedness is\n * load-bearing is exactly the argument for keeping it out.\n */\nconst TRANSITION_PROP = 'transition';\n\nexport type { UiNodeRef } from './UiElementProps';\n\nfunction knownPropertyNames(): string[] {\n return getPropertyNames();\n}\n\nfunction describeValueType(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (Array.isArray(value)) {\n return 'an array';\n }\n return typeof value === 'object' ? 'an object' : `a ${typeof value}`;\n}\n\nexport interface UiGraphBuilderOptions {\n /**\n * Mounts components encountered during reconciliation.\n *\n * Without a resolver the builder still reconciles plain elements\n * and observable children; encountering a component throws.\n */\n components?: ComponentResolver;\n\n /**\n * Receives listeners declared with `on*` props.\n *\n * Without a dispatcher those props are ignored (with one warning),\n * which keeps headless graph construction and non-interactive\n * renders working.\n */\n dispatcher?: UiInputDispatcher;\n\n /**\n * Lets a modifier read its node's box and follow it.\n *\n * Supplied by the runtime, which is where the layout engine lives.\n * Without it `host.onLayout` warns once and does nothing, as event\n * registration does without a dispatcher.\n */\n layout?: UiModifierLayout;\n\n /**\n * Lets a modifier move focus, read whether its node has it, and\n * follow it. Supplied by the runtime, which owns the focus manager.\n * Without it those calls warn once and do nothing, as `onLayout`\n * does without layout access.\n */\n focus?: UiModifierFocus;\n\n /**\n * Lets a modifier read the values its node inherits and follow them.\n * Without it `host.environment` answers with the key's default —\n * which is what an unprovided key resolves to anyway — and\n * `onEnvironment` warns once.\n */\n environment?: UiModifierEnvironment;\n\n /**\n * Drives the animations a `transition` prop asks for.\n *\n * Supplied by the runtime, which owns the driver and the `ticks`\n * phase that advances it. Without one a `transition` is still\n * validated — a typo in it throws whether or not anything animates —\n * and every write lands directly, so a headless build renders the\n * final values.\n */\n animations?: AnimationDriver;\n\n /**\n * Remembers which node currently answers to each shared-element\n * name, so a node arriving under a name can morph from where the\n * node leaving under it stood.\n *\n * Supplied by the runtime, one per runtime, for the reason the\n * driver is: several runtimes share a worker in the playground, and\n * a shared registry would let one runtime's element morph from\n * another's. Without one the `sharedElement` modifier warns once and\n * the element simply appears, which is the correct degraded\n * behaviour — no animation rather than a wrong one.\n */\n sharedElements?: UiSharedElements;\n}\n\n/**\n * Where one reconcile pass has got to in the parent's existing\n * children while matching the unkeyed definitions among them.\n *\n * An unkeyed definition has no identity of its own, so it takes the\n * first child that is still unclaimed and of the right type. Searching\n * for that from the start of the list every time is what made a flat\n * list of n unkeyed children cost n²/2 comparisons: definition i walks\n * past the i entries the definitions before it already took. A cursor\n * naming where the next search may begin walks that ground once across\n * the whole pass instead of once per definition.\n *\n * There is a cursor per node type rather than one for the pass, because\n * a single cursor can only be moved past entries that are *claimed*,\n * and a run of those is not the only thing a search wastes its time on.\n * A list of two interleaved types whose definitions all want one of\n * them has an unclaimed entry of the wrong type at the front for the\n * whole pass, which pins one shared cursor at zero and leaves every\n * definition rescanning the list. Per type, that entry is skipped for\n * good, and each type's searching adds up to one walk of the list.\n *\n * It is pass state rather than builder state on purpose. `reconcile`\n * re-enters itself, through `reconcileChildren` for every matched child\n * and through the deferred-children path for a fragment whose binding\n * emitted mid-pass, and each of those passes has its own `existing`\n * snapshot and its own `matched` set. Cursors held on the builder would\n * be shared between them and would point into the wrong list.\n */\ninterface UnkeyedMatchState {\n /** The parent's children when the pass began, in tree order. */\n readonly existing: readonly UiNode[];\n /**\n * Per node type, the index where the next search for that type may\n * begin. Built on the first unkeyed match rather than with the pass,\n * so the leaf nodes a pass recurses into, which have no children to\n * match at all, do not each pay for a map they never read.\n */\n cursors: Map<UiNodeType, number> | null;\n}\n\n/**\n * Converts declarative UiElements into runtime UiNodes.\n *\n * The builder is intentionally separated from UiGraph.\n *\n * UiGraph owns the runtime state.\n * UiGraphBuilder owns construction of that state from\n * declarative definitions.\n *\n * Calling build() again with a changed definition reconciles\n * the existing tree instead of destroying it:\n *\n * - matching node → update properties\n * - new node → create\n * - missing node → destroy\n * - keyed move → reorder\n * - observable child → fragment anchor + children binding\n * - component child → fragment anchor + mounted host\n */\nexport class UiGraphBuilder {\n private readonly refs = new Map<UiNode, UiNodeRef>();\n private readonly modifiers = new Map<UiNode, UiModifierSet>();\n private nextChildrenBindingId = 0;\n\n /**\n * Depth of the currently running reconcile pass.\n *\n * reconcileChildren recurses into itself and is also re-entered by\n * UiChildrenBinding when an observable emits. Only the outermost\n * pass flushes onMount hooks, so components observe a fully built\n * subtree rather than a half-reconciled one.\n */\n private reconcileDepth = 0;\n /** Fragments whose pass is on the stack, and the emissions they must run afterwards. */\n private readonly reconciling = new Set<UiNode>();\n private readonly deferredChildren = new Map<UiNode, readonly UiChild[]>();\n\n private readonly components: ComponentResolver | undefined;\n\n private readonly dispatcher: UiInputDispatcher | undefined;\n\n private readonly layout: UiModifierLayout | undefined;\n private readonly focus: UiModifierFocus | undefined;\n private readonly modifierEnvironment: UiModifierEnvironment | undefined;\n private readonly animations: AnimationDriver | undefined;\n private readonly sharedElements: UiSharedElements | undefined;\n\n /** Ensures the missing-dispatcher warning is emitted at most once. */\n private warnedAboutDispatcher = false;\n /** The same, for a `transition` with no driver behind it. */\n private warnedAboutAnimations = false;\n /** The same, for a list that arrived from an observable with no keys on it. */\n private warnedAboutIndexKeys = false;\n\n constructor(\n private readonly graph: UiGraph,\n options: UiGraphBuilderOptions = {}\n ) {\n this.components = options.components;\n this.dispatcher = options.dispatcher;\n this.layout = options.layout;\n this.focus = options.focus;\n this.modifierEnvironment = options.environment;\n this.animations = options.animations;\n this.sharedElements = options.sharedElements;\n }\n\n /**\n * Builds (or reconciles) a subtree.\n *\n * Returns the runtime UiNode corresponding to the root of\n * the supplied definition.\n *\n * When no parent is supplied the definition is rendered as\n * the child of the graph root, so repeated calls update the\n * previously rendered tree in place.\n *\n * Note that observable and component definitions produce a\n * transparent Fragment anchor, which is never a valid layout root.\n * Callers that need a layout root must supply a plain UiElement.\n */\n build(definition: UiChild, parentId?: string): UiNode {\n const parent = parentId === undefined ? this.graph.root : this.graph.requireNode(parentId);\n return this.reconcileChildren(parent, [definition]).nodes[0];\n }\n\n /**\n * Reconciles definitions against the existing children of\n * the parent node.\n *\n * Nodes are matched by key when one is supplied, otherwise\n * by position and type. Matched nodes are reused; missing\n * definitions are created, stale children destroyed, and\n * keyed children moved into definition order.\n *\n * Observable children are anchored by invisible Fragment nodes.\n * A UiChildrenBinding subscribes to the observable and reconciles\n * the fragment's children on each emission.\n */\n reconcileChildren(parent: UiNode, definitions: readonly UiChild[]): { nodes: UiNode[]; changed: boolean } {\n if (this.reconciling.has(parent)) {\n // A binding emitted into a fragment whose own pass is still on the\n // stack: a component body, run while its list was being\n // reconciled, wrote to the observable that holds the list. The\n // pass in progress works from a snapshot of the chain and would\n // continue over one a second pass had rewritten, leaving nodes\n // registered but unreachable. Keep the newest definitions and run\n // them once the pass returns; only the last emission matters.\n this.deferredChildren.set(parent, definitions);\n return { nodes: this.collectChildren(parent), changed: false };\n }\n this.reconciling.add(parent);\n this.reconcileDepth++;\n let result: { nodes: UiNode[]; changed: boolean };\n try {\n result = this.reconcile(parent, definitions);\n } finally {\n this.reconcileDepth--;\n this.reconciling.delete(parent);\n }\n const deferred = this.deferredChildren.get(parent);\n if (deferred !== undefined) {\n this.deferredChildren.delete(parent);\n return this.reconcileChildren(parent, deferred);\n }\n // Deliberately outside the finally: a pass that threw left the\n // tree half-built, and mounting components onto it would only\n // widen the damage.\n if (this.reconcileDepth === 0) {\n this.components?.flushMounts();\n }\n return result;\n }\n\n private reconcile(parent: UiNode, definitions: readonly UiChild[]): { nodes: UiNode[]; changed: boolean } {\n const existing = this.collectChildren(parent);\n const matched = new Set<UiNode>();\n const unkeyed: UnkeyedMatchState = { existing, cursors: null };\n if (!this.warnedAboutIndexKeys && definitions.length > 1 && parent.type === UiNodeType.Fragment) {\n // A list that arrived from an observable and carries no keys is\n // matched by position, so inserting a row anywhere but the end\n // shifts every row after it onto a different node: state moves\n // between rows and an animation runs on the wrong element. The\n // fallback is silent otherwise, and silence is the defect.\n // `each` supplies the key; this is the hand-written case.\n this.warnedAboutIndexKeys = definitions.every(child => this.isIndexKeyed(child));\n if (this.warnedAboutIndexKeys) {\n console.warn(\n `A list of ${definitions.length} children under '${parent.id}' arrived from an observable with no keys, ` +\n `so it is reconciled by index. Give each child a key, or build the list with \\`each\\`, ` +\n `which supplies one: each(items, 'id', item => ...).`\n );\n }\n }\n const result: UiNode[] = [];\n let changed = false;\n let cursor: UiNode | null = parent.firstChild;\n\n for (const [index, definition] of definitions.entries()) {\n if (isObservable(definition)) {\n const fragment = this.reconcileObservableChild(parent, index, definition, matched, cursor);\n if (fragment !== undefined) {\n if (this.moveBefore(parent, fragment, cursor)) {\n changed = true;\n }\n cursor = fragment.nextSibling;\n result.push(fragment);\n }\n continue;\n }\n\n if (isComponentLikeElement(definition)) {\n const anchor = this.reconcileComponentChild(parent, index, definition, matched, cursor);\n if (this.moveBefore(parent, anchor, cursor)) {\n changed = true;\n }\n cursor = anchor.nextSibling;\n result.push(anchor);\n continue;\n }\n\n let node = this.matchNode(parent, definition, unkeyed, matched);\n if (node === undefined) {\n const id = this.createNodeId(parent, definition, index);\n const stale = this.graph.getNode(id);\n if (stale !== undefined) {\n if (stale.parent !== parent) {\n throw new Error(`Node id '${id}' is already used outside parent '${parent.id}'.`);\n }\n // The stale child is being replaced; make sure the cursor\n // does not keep pointing at a detached node.\n if (cursor === stale) {\n cursor = stale.nextSibling;\n }\n this.removeSubtree(stale);\n // Prevent the final cleanup from removing it a second time.\n matched.add(stale);\n changed = true;\n }\n node = this.createNode(parent, definition, index);\n matched.add(node);\n changed = true;\n } else {\n matched.add(node);\n this.reconcileProps(node, definition.props);\n if (this.reconcileChildren(node, definition.children).changed) {\n changed = true;\n }\n }\n if (this.moveBefore(parent, node, cursor)) {\n changed = true;\n }\n cursor = node.nextSibling;\n result.push(node);\n }\n\n for (const node of existing) {\n if (!matched.has(node)) {\n this.removeSubtree(node);\n changed = true;\n }\n }\n if (changed) {\n this.graph.markDirty(parent, DirtyFlags.Children);\n }\n return { nodes: result, changed };\n }\n\n /**\n * Reconciles a single observable child definition.\n *\n * Creates or reuses a Fragment anchor under the parent and ensures\n * a UiChildrenBinding is subscribed to the observable.\n */\n private reconcileObservableChild(\n parent: UiNode,\n index: number,\n observable: Observable<UiChild | readonly UiChild[]>,\n matched: Set<UiNode>,\n cursor: UiNode | null\n ): UiNode {\n const fragmentId = this.createFragmentId(parent, index);\n let fragment = this.graph.getNode(fragmentId);\n\n if (fragment !== undefined && fragment.type !== UiNodeType.Fragment) {\n // A non-fragment node occupied this slot; remove it so the\n // fragment can take its place.\n if (cursor === fragment) {\n cursor = fragment.nextSibling;\n }\n this.removeSubtree(fragment);\n matched.add(fragment);\n fragment = undefined;\n }\n\n if (fragment === undefined) {\n fragment = this.graph.createNode(fragmentId, UiNodeType.Fragment);\n this.graph.insertBefore(parent, fragment, cursor);\n } else {\n matched.add(fragment);\n }\n\n const existingBinding = this.graph.getChildrenBindingForNode(fragment);\n if (existingBinding === undefined || existingBinding.observable !== observable) {\n if (existingBinding !== undefined) {\n this.graph.unbindChildren(fragment);\n }\n const bindingId = this.nextChildrenBindingId++;\n const binding = new UiChildrenBinding(bindingId, parent.id, fragment.id, observable, this.graph, this);\n this.graph.bindChildren(fragment, binding);\n }\n\n return fragment;\n }\n\n /**\n * Reconciles a single component child definition.\n *\n * Creates or reuses a Fragment anchor under the parent, asks the\n * resolver for the component's current output, and reconciles that\n * output as the anchor's children.\n *\n * The anchor — not the rendered node — is the component's identity.\n * That keeps identity stable when the component renders a different\n * root element type, lets the output be an Observable (which becomes\n * a children binding on the anchor for free), and makes host\n * teardown a plain consequence of the anchor being removed.\n */\n private reconcileComponentChild(\n parent: UiNode,\n index: number,\n element: ComponentLikeElement,\n matched: Set<UiNode>,\n cursor: UiNode | null\n ): UiNode {\n const resolver = this.components;\n if (resolver === undefined) {\n throw new Error(\n `Component '${element.tag}' was passed to a UiGraphBuilder with no ComponentResolver. ` +\n `Construct the builder with { components } to mount components.`\n );\n }\n if ((element.props as Record<string, unknown>)[MODIFIERS_PROP] !== undefined) {\n // A component's root may be a fragment or an observable, so\n // \"the host node\" a modifier would attach to is not well\n // defined. Put them on an element inside the component instead.\n throw new Error(\n `Component '${element.tag}' cannot take 'modifiers': a component's node is its anchor, which is a ` +\n `fragment with no box and no paint, so there is nothing for a modifier to attach to. Put them on an ` +\n `element the component renders, or, if the component offers it, pass 'rootModifiers', which is the ` +\n `convention for a component that places them on its own root element (see 'modifiersOf').`\n );\n }\n\n const anchorId = this.createComponentAnchorId(parent, element, index);\n let anchor = this.graph.getNode(anchorId);\n\n if (anchor !== undefined && anchor.type !== UiNodeType.Fragment) {\n // A non-fragment node occupied this slot; remove it so the\n // anchor can take its place.\n if (cursor === anchor) {\n cursor = anchor.nextSibling;\n }\n this.removeSubtree(anchor);\n matched.add(anchor);\n anchor = undefined;\n }\n\n if (anchor === undefined) {\n anchor = this.graph.createNode(anchorId, UiNodeType.Fragment);\n this.graph.insertBefore(parent, anchor, cursor);\n } else {\n matched.add(anchor);\n }\n\n this.reconcileChildren(anchor, [resolver.resolve(element, anchorId)]);\n return anchor;\n }\n\n /**\n * Removes a subtree, releasing any component hosts it anchors.\n *\n * Every node removal in this class goes through here so that a host\n * can never outlive the nodes it produced. Hosts are released from\n * the outside in: a parent component's onUnmount runs before its\n * children's, matching the order in which the subtree is leaving.\n */\n private removeSubtree(node: UiNode): void {\n const resolver = this.components;\n const stack: UiNode[] = [node];\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (resolver !== undefined && current.type === UiNodeType.Fragment) {\n resolver.release(current.id);\n }\n const ref = this.refs.get(current);\n if (ref !== undefined) {\n this.refs.delete(current);\n ref(null);\n }\n // Before graph.removeNode, so a modifier's teardown still sees an\n // intact node and its bindings.\n const modifiers = this.modifiers.get(current);\n if (modifiers !== undefined) {\n this.modifiers.delete(current);\n modifiers.detach();\n }\n // An animation outliving its node is the same leak a modifier\n // would be: the driver holds the cell, the cell holds the node.\n if (current.transitions !== null) {\n current.transitions.release();\n current.transitions = null;\n }\n for (let child = current.firstChild; child !== null; child = child.nextSibling) {\n stack.push(child);\n }\n }\n this.graph.removeNode(node);\n }\n\n /**\n * Hands the node to a `ref` callback once, and again only when the\n * callback identity changes. Removal calls the current one with null.\n */\n private reconcileRef(node: UiNode, value: unknown): void {\n const previous = this.refs.get(node);\n if (typeof value !== 'function') {\n if (previous !== undefined) {\n this.refs.delete(node);\n previous(null);\n }\n return;\n }\n const ref = value as UiNodeRef;\n if (previous === ref) {\n return;\n }\n if (previous !== undefined) {\n previous(null);\n }\n this.refs.set(node, ref);\n ref(node);\n }\n\n /**\n * Finds an existing child that should host a definition.\n *\n * Keyed definitions match by the id derived from the key, which is a\n * map lookup and needs no search. Unkeyed definitions match the first\n * unmatched child with the same type, searched for from the pass's\n * cursor rather than from the head of the list.\n */\n private matchNode(\n parent: UiNode,\n definition: UiElement,\n unkeyed: UnkeyedMatchState,\n matched: Set<UiNode>\n ): UiNode | undefined {\n const key = this.elementKey(definition);\n if (key !== undefined) {\n const candidate = this.graph.getNode(`${parent.id}:${key}`);\n if (candidate === undefined || candidate.parent !== parent || candidate.type !== definition.type) {\n return undefined;\n }\n if (matched.has(candidate)) {\n throw new Error(`Duplicate key '${key}' in parent '${parent.id}'.`);\n }\n return candidate;\n }\n\n const existing = unkeyed.existing;\n const type = definition.type;\n let cursors = unkeyed.cursors;\n if (cursors === null) {\n cursors = new Map<UiNodeType, number>();\n unkeyed.cursors = cursors;\n }\n\n // Walk this type's cursor to the first entry that could answer for\n // it, dropping everything passed over on the way. Both grounds for\n // dropping an entry are settled for the rest of the pass:\n //\n // Claimed. Nothing ever leaves `matched` within a pass and a\n // claimed entry is never returned twice, so an entry claimed now is\n // one every later search would skip anyway.\n //\n // Wrong type. This is the part worth pausing on, because it is only\n // permanent for a cursor that belongs to one type. A node's type is\n // fixed from the moment it is created, and a type change is a\n // different node replacing this one rather than this one changing,\n // so an entry that is not a T now will not be a T later either, and\n // no later definition of type T can want it. A cursor shared by\n // every type could make no such promise: the entry it stepped over\n // was merely the wrong type for the definition in hand, and the\n // next definition might be exactly the type it is.\n //\n // What the cursor may not do is move past the entry it lands on. A\n // keyed definition can claim an entry anywhere in the list, so the\n // free entries a cursor has already reached must stay reachable;\n // the walk resumes here next time and re-tests this entry then.\n let index = cursors.get(type) ?? 0;\n while (index < existing.length && (matched.has(existing[index]) || existing[index].type !== type)) {\n index++;\n }\n cursors.set(type, index);\n\n // The walk stopped either on an unclaimed entry of the right type,\n // which is the first such entry in the list and so the one `find`\n // over the whole array would have returned, or off the end.\n return index < existing.length ? existing[index] : undefined;\n }\n\n /**\n * Creates a runtime node for a new definition.\n *\n * The node id is derived from the parent and the definition\n * so that it stays stable across reconciles. The caller is\n * responsible for removing any stale node that already holds\n * the id.\n */\n private createNode(parent: UiNode, definition: UiElement, index: number): UiNode {\n const id = this.createNodeId(parent, definition, index);\n const node = this.graph.createNode(id, definition.type);\n this.reconcileProps(node, definition.props, parent);\n this.reconcileChildren(node, definition.children);\n return node;\n }\n\n /**\n * Positions a node immediately before the reference node.\n */\n private moveBefore(parent: UiNode, node: UiNode, reference: UiNode | null): boolean {\n if (node.parent !== parent) {\n this.graph.insertBefore(parent, node, reference);\n return true;\n }\n if (node === reference || node.nextSibling === reference) {\n return false;\n }\n this.graph.detachNode(node);\n this.graph.insertBefore(parent, node, reference);\n return true;\n }\n\n /**\n * Reconciles declarative properties onto a runtime node.\n *\n * Plain values are written directly. RxJS Observables become\n * UiGraph bindings. Bindings are kept when the Observable\n * instance is unchanged and torn down when a property stops\n * being reactive.\n */\n private reconcileProps(node: UiNode, props: UiProps, parent?: UiNode): void {\n // Installed before this pass writes anything, so a re-render that\n // both adds a transition and changes the value it covers animates\n // rather than jumping once and animating from then on. The prop is\n // read out of `props` directly because the loop below has not run\n // yet; it is skipped there, like `key`, `ref` and `modifiers`.\n this.reconcileTransitions(node, (props as Record<string, unknown>)[TRANSITION_PROP]);\n const present = new Set<string>();\n const presentEvents = new Set<string>();\n let declaredModifiers: unknown;\n\n for (const [property, value] of Object.entries(props)) {\n if (property === KEY_PROP) {\n continue;\n }\n if (property === REF_PROP) {\n this.reconcileRef(node, value);\n continue;\n }\n if (property === MODIFIERS_PROP) {\n declaredModifiers = value;\n continue;\n }\n if (property === TRANSITION_PROP) {\n continue;\n }\n if (isEventProp(property, value)) {\n this.reconcileEventProp(node, property, value as UiEventListener, presentEvents);\n continue;\n }\n this.assertKnownProp(node, property, value);\n present.add(property);\n this.assertValidValue(node, property, value);\n // The graph indexes bindings by node and property, so this asks\n // it directly rather than copying the node's whole binding set\n // into a lookup table on every reconcile.\n const existingBinding = this.graph.getBindingForProperty(node, property as NodeProperty);\n if (this.isObservable(value)) {\n if (existingBinding !== undefined && existingBinding.observable === value) {\n continue;\n }\n if (existingBinding !== undefined) {\n this.graph.unbind(existingBinding);\n }\n this.graph.bind(node, property as NodeProperty, value, propertyEffects(property));\n continue;\n }\n if (existingBinding !== undefined) {\n this.graph.unbind(existingBinding);\n }\n this.graph.updateNodeProperty(node, property as NodeProperty, value, propertyEffects(property));\n }\n\n // Tear down what this render stopped declaring. Both lists are\n // materialised up front because unbinding mutates the indexes they\n // come from.\n for (const binding of this.graph.getBindingsForNode(node)) {\n if (!present.has(binding.property)) {\n this.graph.unbind(binding);\n }\n }\n\n for (const binding of this.graph.getEventBindingsForNode(node)) {\n if (!presentEvents.has(binding.type)) {\n this.graph.unbindEvent(node, binding);\n }\n }\n\n if (node.environment === null && parent?.environment != null) {\n // A freshly built node is not in the tree yet — the builder\n // writes its props and builds its children before making the\n // edge — so what it inherits is not reachable from it. Seeding\n // it here, after the node's own provider props are written and\n // before its modifiers attach, is what makes `host.environment`\n // answer with the theme the node will actually be under rather\n // than with the default. `inheritEnvironment` recomputes at\n // attach and keeps this instance when it agrees, so nothing is\n // marked dirty twice.\n node.environment = this.graph.buildNodeEnvironment(node, parent);\n }\n\n this.reconcileModifiers(node, declaredModifiers);\n }\n\n /**\n * Attaches, updates and detaches the node's modifiers.\n *\n * Called once the declared props are written and before the\n * children are reconciled, so a modifier sees the element's own\n * values first. An element that stops declaring `modifiers` gets an\n * empty list, which detaches everything it had.\n */\n private reconcileModifiers(node: UiNode, declared: unknown): void {\n const existing = this.modifiers.get(node);\n if (declared === undefined) {\n if (existing !== undefined) {\n existing.detach();\n this.modifiers.delete(node);\n }\n return;\n }\n const list = assertModifierList(node, declared);\n const set =\n existing ??\n new UiModifierSet(\n node,\n this.graph,\n this.dispatcher,\n this.layout,\n this.focus,\n this.modifierEnvironment,\n this.animations,\n this.sharedElements\n );\n if (existing === undefined) {\n this.modifiers.set(node, set);\n }\n set.reconcile(list);\n }\n\n /**\n * Installs, updates or removes the node's transitions.\n *\n * An element that stops declaring `transition` loses it entirely,\n * cancelling whatever was in flight — the same rule `modifiers`\n * follows, and the same reason: what an element declares this render\n * is the whole truth about it.\n */\n private reconcileTransitions(node: UiNode, declared: unknown): void {\n if (declared === undefined) {\n if (node.transitions !== null) {\n node.transitions.release();\n node.transitions = null;\n }\n return;\n }\n const specs = assertTransitionMap(node.id, declared, name => findPropertyDefinition(name) !== undefined);\n const driver = this.animations;\n if (driver === undefined) {\n // Validated but not driven: a headless build writes final values.\n this.warnMissingAnimations();\n return;\n }\n let transitions = node.transitions;\n if (transitions === null) {\n transitions = new NodeTransitions(node, this.graph, driver);\n node.transitions = transitions;\n }\n (transitions as NodeTransitions).setSpecs(specs as ReadonlyMap<string, UiTransitionSpec>);\n }\n\n private warnMissingAnimations(): void {\n if (this.warnedAboutAnimations) {\n return;\n }\n this.warnedAboutAnimations = true;\n console.warn(\n `An element declared a 'transition', but the UiGraphBuilder was constructed without an animation driver. ` +\n `Values are written directly. Construct it with { animations }, as the runtime does.`\n );\n }\n\n /** The modifiers attached to a node, for the inspector and for tests. */\n modifiersFor(node: UiNode): UiModifierSet | undefined {\n return this.modifiers.get(node);\n }\n\n /**\n * Rejects a prop nothing will ever read.\n *\n * Every property layout, paint, input or the environment consults is\n * registered in UiProperties, so a name outside the registry is a\n * typo (`widht`) or a prop meant for a component rather than an\n * element. Either would otherwise be stored on the node and silently\n * ignored; `on*` typos have always thrown, and so does this now.\n */\n private assertKnownProp(node: UiNode, property: string, value: unknown): void {\n if (findPropertyDefinition(property) !== undefined) {\n return;\n }\n if (eventTypeForProp(property) !== undefined) {\n throw new Error(\n `Event prop '${property}' on node '${node.id}' must be a function, got ${describeValueType(value)}.`\n );\n }\n const suggestion = closestPropertyName(property, [...knownPropertyNames(), ...knownEventPropNames()]);\n throw new Error(\n `Unknown prop '${property}' on node '${node.id}'.` +\n (suggestion !== undefined ? ` Did you mean '${suggestion}'?` : '') +\n ` Props must be registered UI properties (width, padding, backgroundColor, …), 'key', 'ref',` +\n ` 'modifiers', 'transition', or on* event handlers.`\n );\n }\n\n /**\n * Rejects a value a registered property cannot hold.\n *\n * Only properties with a closed set of values declare a `validate`,\n * so this is one undefined check for the other ninety-odd. An\n * Observable is not checked here — its values arrive later, and a\n * bound value is the reader's to reject, as a bound length is\n * rejected at layout.\n */\n private assertValidValue(node: UiNode, property: string, value: unknown): void {\n const definition = findPropertyDefinition<unknown>(property);\n if (definition?.validate === undefined || this.isObservable(value)) {\n return;\n }\n const message = definition.validate(value);\n if (message !== undefined) {\n throw new Error(`Invalid '${property}' on node '${node.id}': ${message}`);\n }\n }\n\n /**\n * Binds one `on*` prop to the input dispatcher.\n *\n * An existing binding for the same event is kept when the handler\n * identity is unchanged, so re-reconciling a stable tree does not\n * churn listener registrations.\n */\n private reconcileEventProp(\n node: UiNode,\n property: string,\n handler: UiEventListener,\n presentEvents: Set<string>\n ): void {\n const type = eventTypeForProp(property);\n if (type === undefined) {\n throw new Error(\n `Unknown event prop '${property}' on node '${node.id}'. ` +\n `Expected one of: ${knownEventPropNames().join(', ')}.`\n );\n }\n presentEvents.add(type);\n\n const existing = this.graph.getEventBindingForType(node, type);\n if (existing !== undefined) {\n if (existing.listener === handler) {\n return;\n }\n this.graph.unbindEvent(node, existing);\n }\n\n if (this.dispatcher === undefined) {\n this.warnMissingDispatcher(property);\n return;\n }\n this.graph.bindEvent(node, new UiEventBinding(node, type, handler, this.dispatcher));\n }\n\n private warnMissingDispatcher(property: string): void {\n if (this.warnedAboutDispatcher) {\n return;\n }\n this.warnedAboutDispatcher = true;\n console.warn(\n `UiGraphBuilder received event prop '${property}' but was constructed without a dispatcher, ` +\n `so it and any further event props are ignored. Pass { dispatcher } to make the tree interactive.`\n );\n }\n\n /**\n * Determines whether a prop's value is an Observable to bind.\n *\n * The shared structural check rather than `instanceof Observable`,\n * for the reason `isObservable` in `UiElement.ts` already gives for\n * children: an application whose bundle holds a second copy of rxjs\n * produces observables that fail an `instanceof` against this one.\n * Children were structural and props were not, so an app-authored\n * `combineLatest` reached a child correctly and reached a prop as a\n * plain object, which is written once and never updates. It fails\n * silently, which is what makes it worth a named method: nothing\n * warns, nothing throws, and the element simply has no text.\n */\n private isObservable(value: unknown): value is Observable<unknown> {\n return isObservable(value);\n }\n\n /** Whether a child would fall back to its position for identity. */\n private isIndexKeyed(child: UiChild): boolean {\n if (isObservable(child)) {\n return true;\n }\n if (isComponentLikeElement(child)) {\n return child.key === undefined || child.key === null;\n }\n return this.elementKey(child) === undefined;\n }\n\n /**\n * The reconciliation key of a definition, if any.\n */\n private elementKey(definition: UiElement): string | undefined {\n const key = definition.props[KEY_PROP];\n if (key === undefined || key === null) {\n return undefined;\n }\n return String(key);\n }\n\n /**\n * Collects the current children of a node in tree order.\n */\n private collectChildren(parent: UiNode): UiNode[] {\n const children: UiNode[] = [];\n for (let child = parent.firstChild; child !== null; child = child.nextSibling) {\n children.push(child);\n }\n return children;\n }\n\n /**\n * Generates a stable runtime node id.\n *\n * Keyed nodes use `parent:key`. Unkeyed nodes use their\n * position within the parent, which stays stable as long as\n * the surrounding structure does not reorder.\n */\n private createNodeId(parent: UiNode, definition: UiElement, index: number): string {\n const key = this.elementKey(definition);\n if (key !== undefined) {\n return `${parent.id}:${key}`;\n }\n return `${parent.id}:${index}`;\n }\n\n /**\n * Generates a stable id for an observable-child fragment anchor.\n */\n private createFragmentId(parent: UiNode, index: number): string {\n return `${parent.id}:fragment:${index}`;\n }\n\n /**\n * Generates a stable id for a component's fragment anchor.\n *\n * Keyed components use their key, so a component keeps its instance\n * across reorders. Unkeyed components fall back to position, which\n * is stable only while the surrounding structure does not reorder —\n * the same trade-off unkeyed elements make.\n */\n private createComponentAnchorId(parent: UiNode, element: ComponentLikeElement, index: number): string {\n const key = element.key === undefined || element.key === null ? index : element.key;\n return `${parent.id}:component:${String(key)}`;\n }\n}\n","import { BehaviorSubject, isObservable } from 'rxjs';\n\nimport { UiNodeType } from '../graph/UiNodeType';\nimport { percent, type UiTrackSize } from '../layout/UiLength';\nimport { defineModifier } from '../modifiers/UiModifier';\nimport type { UiModifierHost } from '../modifiers/UiModifierHost';\nimport { Box, Column, Grid, Row } from './UiComponents';\nimport { isUiElement, type UiChild, type UiElement } from './UiElement';\nimport type { Reactive } from './UiElementProps';\n\nexport type LazyAxis = 'column' | 'row';\n\nexport interface LazyListOptions {\n /**\n * Number of items now. A list whose data changes takes an\n * Observable: `LazyColumn` binds it through the `lazySource`\n * modifier, so the window learns the new count with the same\n * lifetime as the node and without the caller holding the window.\n */\n readonly count: Reactive<number>;\n /**\n * Expected extent of an item along the scroll axis, used for items\n * that have not been measured yet. The closer it is, the less the\n * scrollbar and offsets adjust as items come into view.\n */\n readonly estimatedExtent: number;\n /** Items mounted beyond each edge of the viewport. Default 3. */\n readonly overscan?: number;\n /** Identity of an item, for reconciliation. Default: its index. */\n readonly key?: (index: number) => string | number;\n /**\n * Viewport extent assumed before the first layout, so the first\n * frame mounts a sensible number of items. Default 600.\n */\n readonly initialViewportExtent?: number;\n /**\n * Any value that changes when what an index *means* changes — a\n * sort, a filter, a page of data arriving. The mounted items are\n * re-rendered and their measurements dropped; the indices and their\n * keys do not move, so the rows are reconciled in place rather than\n * rebuilt.\n */\n readonly revision?: Reactive<unknown>;\n /**\n * Makes the list a **lazy grid**: every mounted row and the header\n * share one set of column tracks. Without it the list is a plain\n * column of wrapped items.\n */\n readonly grid?: LazyGridOptions;\n}\n\n/**\n * The tracks a lazy grid's rows share, and the header that shares them.\n *\n * The grid work left this here: a table whose virtualized\n * rows line up with its header needs the rows to be items of one grid,\n * because tracks cannot be sized across grids that cannot see each\n * other. So the scroll container holds a single Grid, and every row is\n * a `subgrid: 'columns'` item in it.\n */\nexport interface LazyGridOptions {\n /** The shared column tracks. */\n readonly columns: readonly UiTrackSize[];\n /** Space between the columns, in the header and in every row. */\n readonly columnGap?: number;\n /**\n * Drawn above the rows in the same tracks. Like a row it must be a\n * `Grid` with `subgrid: 'columns'`; unlike a row it is rendered once,\n * and `position: 'sticky'` on it holds it at the top while the rows\n * scroll under it.\n */\n readonly header?: UiChild;\n}\n\nexport type LazyItemRenderer = (index: number) => UiChild;\n\n/** What the host reports about the scroll container each frame. */\nexport interface VirtualViewport {\n /** Scroll offset along the list axis. */\n scroll: number;\n /** Visible extent along the list axis. */\n extent: number;\n /**\n * Content before the first item — a lazy grid's header. The window's\n * offsets are measured from the first item, so this is what makes\n * \"which row is at the top of the viewport\" the same question with a\n * header as without one. Zero by default.\n */\n lead?: number;\n}\n\nexport interface VirtualItemMeasure {\n index: number;\n /** Outer extent of the mounted item along the axis. */\n extent: number;\n}\n\nexport interface VirtualUpdate {\n /**\n * How far the scroll offset must move so that the first mounted item\n * stays where the user sees it, after measurements changed the size\n * of the content above it. Zero almost always.\n */\n scrollAdjust: number;\n}\n\n/** Marks an item wrapper with its index so the host can measure it. */\nexport const VIRTUAL_INDEX_PROP = 'virtualIndex';\n/**\n * Marks a lazy grid's header, whose extent sits between the top of the\n * scrolled content and the first item — so the host can tell the window\n * how far the items start down the page.\n */\nexport const VIRTUAL_LEAD_PROP = 'virtualLead';\n/** The window sits on the scroll container under this property. */\nexport const VIRTUAL_WINDOW_PROP = 'virtualWindow';\n\n/**\n * The windowing behind LazyColumn / LazyRow.\n *\n * Only the items in the viewport plus an overscan band are mounted. The\n * rest is two spacer boxes whose extents come from an estimate per item,\n * corrected by the real extent of every item that has been measured, so\n * the total extent (and the scrollbar) settle toward the truth as the\n * user scrolls. Corrections are sparse — one entry per measured item\n * whose extent differs from the estimate — so a 100k-row list costs\n * memory proportional to what was seen, and offsets are estimate ×\n * index plus the corrections before that index.\n *\n * Children are emitted through a BehaviorSubject and reconciled by the\n * graph builder like any observable child: item identity is the key,\n * and an item that stays in the window keeps its node.\n */\nexport class UiVirtualWindow {\n readonly axis: LazyAxis;\n readonly children$: BehaviorSubject<UiElement[]>;\n\n private count: number;\n private readonly estimate: number;\n private readonly overscan: number;\n private readonly keyOf: (index: number) => string | number;\n private readonly renderItem: LazyItemRenderer;\n private readonly grid: LazyGridOptions | undefined;\n\n private first = 0;\n private last = -1;\n private leadExtent = -1;\n private trailExtent = -1;\n private readonly measured = new Map<number, number>();\n /** Sorted by index: measured extent minus the estimate. */\n private corrections: Array<{ index: number; delta: number }> = [];\n /** Wrappers of the items currently mounted, so a row that stays is not re-rendered. */\n private readonly mountedItems = new Map<number, UiElement>();\n /**\n * The last viewport the host reported, so a count or a revision\n * arriving between frames recomputes the window against where the\n * list actually is rather than against the top of it.\n */\n private viewport: VirtualViewport;\n /** The last few content extents, and the viewport extent last seen; see `checkViewport`. */\n private readonly recentContentExtents: number[] = [];\n private lastViewportExtent = -1;\n private viewportLandedOnContent = 0;\n private warnedAboutViewport = false;\n\n constructor(axis: LazyAxis, options: LazyListOptions, renderItem: LazyItemRenderer) {\n const count = typeof options.count === 'number' ? options.count : 0;\n assertCount(count);\n if (!(options.estimatedExtent > 0)) {\n throw new Error(`LazyList estimatedExtent must be positive, got ${String(options.estimatedExtent)}.`);\n }\n this.axis = axis;\n this.count = count;\n this.estimate = options.estimatedExtent;\n this.overscan = options.overscan ?? 3;\n this.keyOf = options.key ?? (index => index);\n this.renderItem = renderItem;\n this.grid = options.grid;\n this.children$ = new BehaviorSubject<UiElement[]>([]);\n this.viewport = { scroll: 0, extent: options.initialViewportExtent ?? 600 };\n this.update(this.viewport, []);\n }\n\n /** How many items the list has, mounted or not. */\n get length(): number {\n return this.count;\n }\n\n /**\n * A new item count.\n *\n * L5 shipped `count` as a plain number and left an observable one to\n * this tier, because a table whose rows change cannot invalidate its\n * window without it. Measurements past the new end are dropped — a\n * shorter list must not keep corrections for items it no longer has,\n * or its scrollbar would claim space that is not there.\n *\n * The mounted items are re-rendered as well. A different count is a\n * different list, so an item cached against index 1 may be a\n * different item now; keeping it would show stale content, and with a\n * key derived from the data it would collide with the row that took\n * its place.\n */\n setCount(count: number): void {\n assertCount(count);\n if (count === this.count) {\n return;\n }\n this.count = count;\n for (const index of this.measured.keys()) {\n if (index >= count) {\n this.measured.delete(index);\n }\n }\n this.corrections = this.corrections.filter(correction => correction.index < count);\n this.mountedItems.clear();\n this.refresh();\n }\n\n /**\n * Re-renders the mounted items and forgets what they measured.\n *\n * For when the data behind the indices moved — a sort, a filter — so\n * the row at index 5 is a different row. Keys do not change, so the\n * rows are reconciled in place: the text of a cell is written, not a\n * node rebuilt.\n */\n invalidate(): void {\n this.measured.clear();\n this.corrections = [];\n this.mountedItems.clear();\n this.refresh();\n }\n\n /** Indices currently mounted, inclusive; `last < first` when none. */\n get range(): { first: number; last: number } {\n return { first: this.first, last: this.last };\n }\n\n /** Offset of an item's leading edge from the start of the content. */\n offsetOf(index: number): number {\n let offset = index * this.estimate;\n for (const correction of this.corrections) {\n if (correction.index >= index) {\n break;\n }\n offset += correction.delta;\n }\n return offset;\n }\n\n totalExtent(): number {\n return this.offsetOf(this.count);\n }\n\n extentOf(index: number): number {\n return this.measured.get(index) ?? this.estimate;\n }\n\n /** The item whose span contains `offset`; clamped to the list. */\n indexAt(offset: number): number {\n if (this.count === 0) {\n return 0;\n }\n let low = 0;\n let high = this.count - 1;\n while (low < high) {\n const mid = (low + high + 1) >> 1;\n if (this.offsetOf(mid) <= offset) {\n low = mid;\n } else {\n high = mid - 1;\n }\n }\n return low;\n }\n\n /**\n * One step, run by the host each frame before layout: take the\n * measurements of mounted items, keep the first mounted item anchored,\n * recompute the window for the viewport, and emit children when the\n * window or the spacers changed.\n */\n update(viewport: VirtualViewport, measures: Iterable<VirtualItemMeasure>): VirtualUpdate {\n this.viewport = viewport;\n const anchorBefore = this.last >= this.first ? this.offsetOf(this.first) : 0;\n for (const measure of measures) {\n this.record(measure.index, measure.extent);\n }\n const anchorAfter = this.last >= this.first ? this.offsetOf(this.first) : 0;\n const scrollAdjust = anchorAfter - anchorBefore;\n\n const total = this.totalExtent();\n const header = viewport.lead ?? 0;\n this.checkViewport(viewport.extent, total + header);\n const scrolled = clamp(viewport.scroll + scrollAdjust, 0, Math.max(0, total + header - viewport.extent));\n // Offsets are measured from the first item, so a header above them\n // shifts what the viewport is looking at by its own extent.\n const scroll = Math.max(0, scrolled - header);\n let first = 0;\n let last = -1;\n if (this.count > 0) {\n first = Math.max(0, this.indexAt(scroll) - this.overscan);\n last = Math.min(this.count - 1, this.indexAt(scroll + Math.max(0, viewport.extent)) + this.overscan);\n }\n const lead = first <= last ? this.offsetOf(first) : 0;\n const trail = first <= last ? total - this.offsetOf(last + 1) : total;\n if (first !== this.first || last !== this.last || lead !== this.leadExtent || trail !== this.trailExtent) {\n this.first = first;\n this.last = last;\n this.leadExtent = lead;\n this.trailExtent = trail;\n this.children$.next(this.buildChildren());\n }\n return { scrollAdjust };\n }\n\n /**\n * Warns, once, when the viewport is the content.\n *\n * A lazy list bounded by nothing takes the extent of its rows, and its\n * host then reports that extent back as the viewport: every row is in\n * view, every row is mounted, and nothing is virtualised. The list\n * looks right the whole time, because whatever holds it clips it; it\n * only gets slower with every row it gains. Found in Sluice, where a\n * wrapper box gave the feed's list the height of its rows and forty\n * posts a second re-laid and re-mirrored a thousand of them.\n *\n * The tell is the viewport moving with the content. The host reports\n * the extent it laid out last frame, so a viewport that is the content\n * lands on a content extent this window computed an update or two ago,\n * and lands on the next one when the content grows again. Two landings\n * are the signal: a viewport that changes for a real reason, a window\n * resized, hits one of those numbers only by coincidence, and not twice.\n */\n private checkViewport(extent: number, content: number): void {\n if (!this.warnedAboutViewport && extent !== this.lastViewportExtent) {\n if (this.recentContentExtents.some(recent => Math.abs(recent - extent) < 0.5)) {\n this.viewportLandedOnContent++;\n if (this.viewportLandedOnContent >= 2) {\n this.warnedAboutViewport = true;\n const name = this.grid !== undefined ? 'LazyGrid' : this.axis === 'column' ? 'LazyColumn' : 'LazyRow';\n const size = this.axis === 'column' ? 'height' : 'width';\n const minimum = this.axis === 'column' ? 'minHeight' : 'minWidth';\n console.warn(\n `A ${name} of ${this.count} items has a viewport the size of its content (${Math.round(extent)}px), ` +\n `so every item is mounted and nothing is virtualised. The list is taking its items' ${size} ` +\n `rather than being bounded by what holds it: give it a definite ${size}, or, when a wrapper ` +\n `between it and a flex parent is the one growing, put ${minimum}: 0 on that wrapper.`\n );\n }\n }\n this.lastViewportExtent = extent;\n }\n if (this.recentContentExtents[this.recentContentExtents.length - 1] !== content) {\n this.recentContentExtents.push(content);\n if (this.recentContentExtents.length > 4) {\n this.recentContentExtents.shift();\n }\n }\n }\n\n /**\n * Recomputes the window against the last viewport and emits, whatever\n * the arithmetic says. The sentinels force the emission: the window\n * may land on exactly the same range while the items in it mean\n * something different.\n */\n private refresh(): void {\n this.first = 0;\n this.last = -1;\n this.leadExtent = -1;\n this.trailExtent = -1;\n this.update(this.viewport, []);\n }\n\n private record(index: number, extent: number): void {\n if (index < 0 || index >= this.count || !(extent >= 0)) {\n return;\n }\n if (this.measured.get(index) === extent) {\n return;\n }\n this.measured.set(index, extent);\n const delta = extent - this.estimate;\n let position = 0;\n while (position < this.corrections.length && this.corrections[position].index < index) {\n position++;\n }\n const existing = this.corrections[position];\n if (existing !== undefined && existing.index === index) {\n if (delta === 0) {\n this.corrections.splice(position, 1);\n } else {\n existing.delta = delta;\n }\n } else if (delta !== 0) {\n this.corrections.splice(position, 0, { index, delta });\n }\n }\n\n private buildChildren(): UiElement[] {\n const children: UiElement[] = [];\n if (this.grid?.header !== undefined) {\n children.push(markRow(this.grid.header, { key: 'lazy:header', [VIRTUAL_LEAD_PROP]: true }, 'The header of'));\n }\n children.push(this.spacer('lazy:lead', this.leadExtent));\n for (const index of this.mountedItems.keys()) {\n if (index < this.first || index > this.last) {\n this.mountedItems.delete(index);\n }\n }\n for (let index = this.first; index <= this.last; index++) {\n let wrapper = this.mountedItems.get(index);\n if (wrapper === undefined) {\n wrapper = this.wrap(index, this.renderItem(index));\n this.mountedItems.set(index, wrapper);\n }\n children.push(wrapper);\n }\n children.push(this.spacer('lazy:trail', this.trailExtent));\n if (this.grid === undefined) {\n return children;\n }\n // One Grid holds the header, the spacers and every mounted row, so\n // the tracks are sized across all of them at once. The scroll\n // container's only child; its rows are contiguous, which is why the\n // grid takes no row gap — the window's offsets assume it.\n return [\n Grid(\n {\n key: 'lazy:grid',\n columns: this.grid.columns,\n columnGap: this.grid.columnGap,\n rowGap: 0,\n autoFlow: 'row',\n width: percent(100)\n },\n ...children\n )\n ];\n }\n\n private spacer(key: string, extent: number): UiElement {\n if (this.grid !== undefined) {\n // A spacer in a lazy grid is a grid item across every column, so\n // it occupies a whole row of its own rather than one cell.\n return Box({ key, height: extent, columnSpan: this.grid.columns.length });\n }\n return this.axis === 'column'\n ? Box({ key, height: extent, flexShrink: 0 })\n : Box({ key, width: extent, flexShrink: 0 });\n }\n\n /**\n * Marks an item so the host can find its index and measure it.\n *\n * A plain lazy list wraps the item in a flex container, so it\n * stretches across the list like a row in any column and its own\n * props are untouched. A lazy grid cannot: a row's cells have to be\n * items of the shared grid, so the row the renderer returns *is* the\n * wrapper, and the index is written onto it.\n */\n private wrap(index: number, item: UiChild): UiElement {\n const key = `lazy:${String(this.keyOf(index))}`;\n const props = { key, [VIRTUAL_INDEX_PROP]: index };\n if (this.grid !== undefined) {\n return markRow(item, props, `Row ${index} of`);\n }\n return this.axis === 'column' ? Column({ ...props, flexShrink: 0 }, item) : Row({ ...props, flexShrink: 0 }, item);\n }\n}\n\n/**\n * Copies the window's own props onto a row of a lazy grid.\n *\n * The row must be a `Grid` with `subgrid: 'columns'`, because that is\n * the only thing whose cells land in the tracks the header sized. The\n * check is here rather than in a comment: a row that is an ordinary\n * Row would line up by accident on the first frame and drift from the\n * header on the second.\n */\nfunction markRow(row: UiChild, props: Record<string, unknown>, subject: string): UiElement {\n if (!isUiElement(row) || row.type !== UiNodeType.Grid || row.props.subgrid !== 'columns') {\n throw new Error(`${subject} a lazy grid must be a Grid with subgrid: 'columns', so its cells share the tracks.`);\n }\n return { ...row, props: { ...row.props, ...props } };\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction assertCount(count: number): void {\n if (!(count >= 0) || !Number.isInteger(count)) {\n throw new Error(`LazyList count must be a non-negative integer, got ${String(count)}.`);\n }\n}\n\n/** What `lazySource` carries from a LazyColumn's props to its window. */\nexport interface LazySourceArgs {\n readonly window: UiVirtualWindow;\n readonly count: Reactive<number>;\n readonly revision?: Reactive<unknown>;\n /**\n * Makes the list a **lazy grid**: every mounted row and the header\n * share one set of column tracks. Without it the list is a plain\n * column of wrapped items.\n */\n readonly grid?: LazyGridOptions;\n}\n\n/**\n * The tracks a lazy grid's rows share, and the header that shares them.\n *\n * The grid work left this here: a table whose virtualized\n * rows line up with its header needs the rows to be items of one grid,\n * because tracks cannot be sized across grids that cannot see each\n * other. So the scroll container holds a single Grid, and every row is\n * a `subgrid: 'columns'` item in it.\n */\nexport interface LazyGridOptions {\n /** The shared column tracks. */\n readonly columns: readonly UiTrackSize[];\n /** Space between the columns, in the header and in every row. */\n readonly columnGap?: number;\n /**\n * Drawn above the rows in the same tracks. Like a row it must be a\n * `Grid` with `subgrid: 'columns'`; unlike a row it is rendered once,\n * and `position: 'sticky'` on it holds it at the top while the rows\n * scroll under it.\n */\n readonly header?: UiChild;\n}\n\n/**\n * Feeds a window the inputs that may change: the item count and the\n * revision of the data behind the indices.\n *\n * A modifier rather than a subscription taken in `LazyColumn`, because\n * a modifier's lifetime is exactly its node's — `own` releases the\n * subscription inside `removeSubtree`, before the node is gone — and\n * because attachment runs after the element's props and *before* its\n * children are reconciled, so a count that arrives with the tree is\n * already in the window when the first children are read from it.\n */\nexport const lazySource = defineModifier<LazySourceArgs>({\n name: 'lazySource',\n attach(host, args) {\n follow(host, args.count, value => args.window.setCount(value));\n if (args.revision !== undefined) {\n let first = true;\n follow(host, args.revision, () => {\n // The value the list was built with is not a change.\n if (first) {\n first = false;\n return;\n }\n args.window.invalidate();\n });\n }\n }\n});\n\nfunction follow<T>(host: UiModifierHost, source: Reactive<T>, apply: (value: T) => void): void {\n if (!isObservable(source)) {\n apply(source as T);\n return;\n }\n host.own(\n source.subscribe(value => {\n apply(value);\n host.requestFrame();\n })\n );\n}\n","import { UiNodeType } from '../graph/UiNodeType';\nimport { createElement } from './UiFactory';\nimport type { UiElement } from './UiElement';\nimport type { ScrollViewProps } from './UiElementProps';\nimport {\n lazySource,\n UiVirtualWindow,\n VIRTUAL_WINDOW_PROP,\n type LazyGridOptions,\n type LazyItemRenderer,\n type LazyListOptions\n} from './UiVirtualWindow';\n\n/**\n * A LazyColumn / LazyRow takes every ScrollView prop plus the window\n * options. The per-item key is `itemKey`, since `key` is the element's\n * own reconciliation key.\n */\nexport type LazyListProps = ScrollViewProps &\n Omit<LazyListOptions, 'key'> & {\n /** Stable identity per index, so a row keeps its node when the list shifts. */\n itemKey?: LazyListOptions['key'];\n /**\n * Hands the window to the caller, once, as the element is built.\n *\n * What it is for is `offsetOf(index)`: scrolling to a row that is\n * not mounted has no node to reveal, so the offset arithmetic is\n * the only way to ask for it. A component holding this must not\n * drive the window — the runtime owns that.\n */\n windowRef?: (window: UiVirtualWindow) => void;\n };\n\n/**\n * A vertical list that mounts only the rows in view.\n *\n * LazyColumn({ height: 400, count: 100000, estimatedExtent: 28 },\n * index => Row({ padding: 6 }, Text({ text: `Row ${index}` })))\n *\n * It is a ScrollView whose children are produced by a UiVirtualWindow:\n * a spacer for the rows above the window, the mounted rows (keyed, so a\n * row that stays in view keeps its node), and a spacer for the rows\n * below. The runtime advances the window every frame before layout, so\n * rows revealed by a scroll are laid out and painted on that frame.\n * Every other ScrollView property — size, padding, background — applies.\n */\nexport function LazyColumn(props: LazyListProps, renderItem: LazyItemRenderer): UiElement {\n return lazyList('column', props, renderItem);\n}\n\n/** The horizontal counterpart of LazyColumn. */\nexport function LazyRow(props: LazyListProps, renderItem: LazyItemRenderer): UiElement {\n return lazyList('row', props, renderItem);\n}\n\n/**\n * A vertical list whose rows share one set of column tracks with a\n * header, and of which only the visible rows exist.\n *\n * LazyGrid({ height: 400, count: 100000, estimatedExtent: 28,\n * columns: [auto, fr(1), 80], header: headerRow },\n * index => Grid({ subgrid: 'columns', role: 'row' }, …cells))\n *\n * The renderer returns the row itself, not its content: a row's cells\n * have to be items of the one grid that owns the tracks, so a row is a\n * `Grid` with `subgrid: 'columns'` and the window writes the index onto\n * it. The grid work left this to `DataTable`, and this\n * is it — the engine half, with the component built on top.\n */\nexport function LazyGrid(props: LazyGridProps, renderRow: LazyItemRenderer): UiElement {\n const { columns, columnGap, header, ...rest } = props;\n return lazyList('column', rest, renderRow, { columns, columnGap, header });\n}\n\nexport type LazyGridProps = LazyListProps & LazyGridOptions;\n\nfunction lazyList(\n axis: 'column' | 'row',\n props: LazyListProps,\n renderItem: LazyItemRenderer,\n grid?: LazyGridOptions\n): UiElement {\n const { count, estimatedExtent, overscan, itemKey, initialViewportExtent, revision, modifiers, windowRef, ...rest } =\n props;\n const window = new UiVirtualWindow(\n axis,\n { count, estimatedExtent, overscan, key: itemKey, initialViewportExtent, revision, grid },\n renderItem\n );\n windowRef?.(window);\n return createElement(\n UiNodeType.ScrollView,\n {\n ...rest,\n // The caller's modifiers first, so one of theirs writing the same\n // property still wins — the order `Button` already establishes.\n modifiers: [...(modifiers ?? []), lazySource({ window, count, revision })],\n direction: axis,\n [VIRTUAL_WINDOW_PROP]: window\n },\n [window.children$]\n );\n}\n","import { bandOf, type UiContainerSizeSource } from '../environment/UiContainerSize';\nimport { defineModifier } from './UiModifier';\n\nexport interface SizeContainerArgs {\n /**\n * The source this container feeds. The same object goes on the\n * node's `containerSize` property, which is what puts it in the\n * environment for the subtree.\n */\n readonly source: UiContainerSizeSource;\n}\n\n/**\n * Reports a node's content size to the source its subtree reads.\n *\n * The container-query half of custom layout, and built on\n * `host.onLayout` for the same reason `scrollPosition` is: \"this\n * node's box changed on this frame\" already has exactly one mechanism,\n * and a second one carrying the same fact would be a second thing to\n * keep right.\n *\n * The size reported is the **content** box: the border box less\n * padding, because the room a child has is the room inside the\n * padding, and a layout that switched arms at 600 px would otherwise\n * switch at 600 px of border box and lay itself out in 560.\n *\n * `Responsive` attaches this for you. Attach it by hand when the\n * source has to be reachable somewhere the helper's own children are\n * not, and put the same object on the node's `containerSize` prop.\n */\nexport const sizeContainer = defineModifier<SizeContainerArgs>({\n name: 'sizeContainer',\n attach(host, args) {\n const report = (): void => {\n const box = host.layoutBox();\n if (box === null) {\n return;\n }\n const inset = (name: string, axis: string): number => {\n const side = host.get<number | undefined>(name);\n return typeof side === 'number' ? side : (host.get<number | undefined>(axis) ?? 0);\n };\n const horizontal = inset('paddingLeft', 'paddingX') + inset('paddingRight', 'paddingX');\n const vertical = inset('paddingTop', 'paddingY') + inset('paddingBottom', 'paddingY');\n args.source.report(Math.max(0, box.width - horizontal), Math.max(0, box.height - vertical));\n };\n host.onLayout(report);\n // The first layout may already have happened, for a node mounted\n // into a tree that is on screen; `onLayout` only reports changes.\n report();\n }\n});\n\nexport interface BreakpointArgs {\n /**\n * Widths, in ascending order, at which the properties change. A band\n * runs from its own width up to the next one.\n */\n readonly at: readonly number[];\n /**\n * What to write in each band, keyed by the width that opens it. The\n * band below the first breakpoint is keyed `0`.\n *\n * Properties are written cumulatively from the narrowest band up, so\n * a band only names what it changes: `{ 0: { paddingX: 12 }, 900: {\n * paddingX: 32 } }` keeps everything else the narrow arm set.\n */\n readonly props: Readonly<Record<number, Readonly<Record<string, unknown>>>>;\n}\n\n/**\n * Changes a node's own properties as the room it has crosses a width.\n *\n * The small half of container queries, and the one to reach for first:\n * a screen whose gutter, column count or type size differs at 400 px\n * and 1400 px is a set of properties, not a different tree, and\n * changing properties costs a layout rather than a rebuild.\n * `Responsive` is for when the *children* genuinely differ.\n *\n * It measures the node it is attached to, which is the useful thing\n * about it and the thing a media query cannot do: a card in a sidebar\n * asks how wide the card is, not how wide the window is.\n *\n * Nothing is written until the node has been laid out once, so the\n * first frame uses the element's own values. Give the element the\n * narrowest band's values as its declared props if that matters.\n */\nexport const breakpoint = defineModifier<BreakpointArgs>({\n name: 'breakpoint',\n attach(host, args) {\n const bands = [0, ...args.at];\n let current = -1;\n const apply = (): void => {\n const box = host.layoutBox();\n if (box === null) {\n return;\n }\n const band = bandOf(box.width, args.at);\n if (band === current) {\n return;\n }\n current = band;\n // From the narrowest band up to this one, so a band that names\n // one property does not silently drop what the bands below set.\n const written = new Map<string, unknown>();\n for (const edge of bands) {\n if (edge > band) {\n break;\n }\n for (const [name, value] of Object.entries(args.props[edge] ?? {})) {\n written.set(name, value);\n }\n }\n for (const [name, value] of written) {\n host.set(name, value);\n }\n };\n host.onLayout(apply);\n apply();\n }\n});\n","import { map } from 'rxjs';\n\nimport { UiContainerSizeSource, containerBands } from '../environment/UiContainerSize';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { Size } from '../layout/LayoutTypes';\nimport { sizeContainer } from '../modifiers/breakpoints';\nimport { createElement } from './UiFactory';\nimport type { UiChild, UiElement } from './UiElement';\nimport type { ColumnProps } from './UiElementProps';\n\nexport interface ResponsiveProps extends ColumnProps {\n /**\n * The content widths, in ascending order, at which the children are\n * built again. A width of 900 with `at={[600, 1200]}` is in the 600\n * band, and so is every width from 600 to 1199.\n *\n * Required, and the reason this is not simply a subscription to the\n * width: children rebuilt on every pixel of a resize allocate a\n * subtree per frame of a drag. Naming the widths that matter is what\n * makes the rebuild happen once per band instead.\n */\n readonly at: readonly number[];\n /** Which container to be. A Column by default; a Row or a Box are the other two. */\n readonly as?: 'column' | 'row' | 'box';\n}\n\n/**\n * A container whose children are built from the room it has.\n *\n * The other half of container queries, for the case `breakpoint`\n * cannot cover: a screen that is a sidebar and a list at 1400 px and a\n * single stack at 400 px is not the same tree with different\n * properties, it is a different tree.\n *\n * Responsive({ at: [900], width: percent(100) }, size =>\n * size.width >= 900 ? [Sidebar(), List()] : [List()]\n * )\n *\n * `build` is called once per band entered, with the size the container\n * had when it entered that band, and its result replaces the children.\n * A resize inside a band changes nothing, which is the whole point:\n * dragging a window edge from 1000 px to 1399 px does no work at all.\n *\n * The container also **provides its size to the subtree** through\n * `UiEnvironmentKeys.containerSize`, so a descendant that needs the\n * number rather than a branch can read it without the tree threading a\n * prop down. What is provided is a source object whose identity never\n * changes, so nothing under it is rebuilt when the number moves; see\n * `environment/UiContainerSize.ts`.\n *\n * The first build happens before any layout, when the size is zero, so\n * `build` must return something sensible for a container whose room is\n * not yet known. Zero picks the narrowest arm, which is the right\n * guess and also what a phone gets.\n */\nexport function Responsive(props: ResponsiveProps, build: (size: Size) => UiChild | readonly UiChild[]): UiElement {\n const { at, as = 'column', modifiers, ...rest } = props;\n const source = new UiContainerSizeSource();\n const children = containerBands(source, at).pipe(\n map(() => {\n const built = build(source.current);\n return Array.isArray(built) ? (built as readonly UiChild[]) : [built as UiChild];\n })\n );\n const container = sizeContainer({ source });\n return createElement(\n as === 'row' ? UiNodeType.Row : as === 'box' ? UiNodeType.Box : UiNodeType.Column,\n {\n ...rest,\n containerSize: source,\n modifiers: modifiers === undefined ? [container] : [container, ...modifiers]\n },\n [children]\n );\n}\n","import type { UiKeyModifiers } from '../input/UiInputEvent';\nimport type { EditUnit } from './EditableTextModel';\n\n/** Which keyboard conventions apply: Command on a Mac, Control elsewhere. */\nexport type EditingPlatform = 'mac' | 'other';\n\n/**\n * What a key press asks an editable to do.\n *\n * `move` with unit 'vertical' is up/down a visual line and needs the\n * paragraph layout, so the model cannot do it alone; the controller\n * resolves it through `TextGeometry`.\n */\nexport type EditCommand =\n | { kind: 'move'; unit: EditUnit | 'vertical'; direction: -1 | 1; extend: boolean }\n | { kind: 'delete'; unit: EditUnit; direction: -1 | 1 }\n | { kind: 'newline' }\n | { kind: 'selectAll' }\n | { kind: 'undo' }\n | { kind: 'redo' }\n | { kind: 'insert'; text: string };\n\n/**\n * The platform the runtime is on, decided once. Available in a worker\n * too, since `navigator.userAgent` is; tests set it explicitly.\n */\nexport function detectEditingPlatform(): EditingPlatform {\n const agent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n return /Mac|iPhone|iPad|iPod/.test(agent) ? 'mac' : 'other';\n}\n\n/**\n * Maps a key press to an editing command, following the host's\n * conventions:\n *\n * Mac — Option steps by word, Command jumps to line and document\n * ends, Command+Backspace deletes to the line start.\n * Others — Control steps by word, Home/End and Control+Home/End are\n * the line and document ends.\n *\n * Shift extends every move. Enter is a newline (the controller drops\n * it in a single-line field). A printable key becomes `insert` only\n * when `textFromKeys` is set: a host with an editing proxy delivers\n * text through `beforeinput` instead, where the IME and dead keys\n * have already been resolved.\n */\nexport function commandForKey(\n key: string,\n modifiers: UiKeyModifiers,\n platform: EditingPlatform,\n textFromKeys: boolean\n): EditCommand | null {\n const mac = platform === 'mac';\n const primary = mac ? modifiers.meta : modifiers.ctrl;\n const word = mac ? modifiers.alt : modifiers.ctrl;\n const extend = modifiers.shift;\n\n switch (key) {\n case 'ArrowLeft':\n case 'ArrowRight': {\n const direction = key === 'ArrowLeft' ? -1 : 1;\n if (mac && modifiers.meta) {\n return { kind: 'move', unit: 'line', direction, extend };\n }\n return { kind: 'move', unit: word ? 'word' : 'grapheme', direction, extend };\n }\n case 'ArrowUp':\n case 'ArrowDown': {\n const direction = key === 'ArrowUp' ? -1 : 1;\n if (mac && modifiers.meta) {\n return { kind: 'move', unit: 'document', direction, extend };\n }\n return { kind: 'move', unit: 'vertical', direction, extend };\n }\n case 'Home':\n return { kind: 'move', unit: modifiers.ctrl ? 'document' : 'line', direction: -1, extend };\n case 'End':\n return { kind: 'move', unit: modifiers.ctrl ? 'document' : 'line', direction: 1, extend };\n case 'Backspace':\n if (mac && modifiers.meta) {\n return { kind: 'delete', unit: 'line', direction: -1 };\n }\n return { kind: 'delete', unit: word ? 'word' : 'grapheme', direction: -1 };\n case 'Delete':\n return { kind: 'delete', unit: word ? 'word' : 'grapheme', direction: 1 };\n case 'Enter':\n return primary ? null : { kind: 'newline' };\n }\n\n if (primary && !modifiers.alt) {\n switch (key.toLowerCase()) {\n case 'a':\n return { kind: 'selectAll' };\n case 'z':\n return modifiers.shift ? { kind: 'redo' } : { kind: 'undo' };\n case 'y':\n return mac ? null : { kind: 'redo' };\n }\n return null;\n }\n\n if (textFromKeys && isPrintable(key) && !modifiers.ctrl && !modifiers.meta) {\n return { kind: 'insert', text: key };\n }\n return null;\n}\n\n/** A key value that is the character it types: one grapheme, not a name like 'Shift'. */\nexport function isPrintable(key: string): boolean {\n if (key.length === 1) {\n return true;\n }\n if (key.length === 2) {\n const code = key.charCodeAt(0);\n return code >= 0xd800 && code <= 0xdbff;\n }\n return false;\n}\n","import type { UiNode } from '../graph/UiNode';\n\n/**\n * Tells a listener when a node's scoped environment changed under it.\n *\n * The graph already rebuilds environments in one place — at attach for\n * a freshly mounted subtree, and in the environment phase after a\n * provider changed — and marks the affected nodes dirty so their\n * inherited properties re-resolve. A modifier that read a value out of\n * the environment holds it in its own state instead, where no dirty\n * flag reaches it; this is the notification it needs.\n *\n * Registered per node and usually empty, so a provider change costs a\n * map lookup per node it already visits.\n */\nexport class EnvironmentNotifier {\n private readonly listeners = new Map<UiNode, Set<() => void>>();\n\n add(node: UiNode, listener: () => void): () => void {\n let forNode = this.listeners.get(node);\n if (forNode === undefined) {\n forNode = new Set();\n this.listeners.set(node, forNode);\n }\n forNode.add(listener);\n return () => {\n const set = this.listeners.get(node);\n if (set === undefined) {\n return;\n }\n set.delete(listener);\n if (set.size === 0) {\n this.listeners.delete(node);\n }\n };\n }\n\n isEmpty(): boolean {\n return this.listeners.size === 0;\n }\n\n /** Fed from the graph, for every node whose environment was reassigned. */\n handleEnvironmentChange(node: UiNode): void {\n const forNode = this.listeners.get(node);\n if (forNode === undefined) {\n return;\n }\n for (const listener of forNode) {\n listener();\n }\n }\n\n handleNodeRemoved(node: UiNode): void {\n this.listeners.delete(node);\n }\n}\n","import { UiEnvironment } from './UiEnvironment';\nimport type { UiEnvironmentKey } from './UiEnvironmentKey';\n\n/**\n * Describes a set of environment values provided by a node.\n *\n * Provider nodes are regular UiNodes; the builder recognizes them\n * by their type or by an explicit provider declaration and uses\n * this structure to attach a child environment to the subtree.\n */\nexport interface UiEnvironmentProvider {\n readonly environment: UiEnvironment;\n}\n\n/**\n * Creates an environment provider from a parent environment and\n * one or more explicit key/value overrides.\n */\nexport function provideEnvironment(\n parent: UiEnvironment | null,\n values: ReadonlyArray<{ key: UiEnvironmentKey<unknown>; value: unknown }>\n): UiEnvironmentProvider {\n let environment = parent ?? new UiEnvironment(null);\n for (const { key, value } of values) {\n environment = environment.set(key, value);\n }\n return { environment };\n}\n","import type { UiNode } from '../graph/UiNode';\n\n/** One occurrence of the query: a range of one node's text. */\nexport interface FindMatch {\n readonly node: UiNode;\n readonly start: number;\n readonly end: number;\n}\n\nexport interface FindOptions {\n /** Distinguish upper and lower case. Off by default, as a find bar is. */\n matchCase?: boolean;\n}\n\n/**\n * Where a query occurs in a run of text nodes.\n *\n * Matches are found within one node's text, never across two. A page of\n * HTML matches across inline elements — `hello <b>world</b>` finds\n * \"hello world\" — but a `Text` node is a paragraph, not a span, so\n * there is no inline seam to cross and every match is a range of one\n * node.\n *\n * Occurrences do not overlap: the scan resumes after each match, so\n * \"aa\" in \"aaa\" is one match, as it is in a browser's find bar.\n */\nexport function findMatchesIn(\n nodes: readonly UiNode[],\n textOf: (node: UiNode) => string | undefined,\n query: string,\n options: FindOptions = {}\n): FindMatch[] {\n const matches: FindMatch[] = [];\n if (query.length === 0) {\n return matches;\n }\n for (const node of nodes) {\n const text = textOf(node);\n if (text === undefined || text.length < query.length) {\n continue;\n }\n appendMatches(node, text, query, options.matchCase === true, matches);\n }\n return matches;\n}\n\nfunction appendMatches(node: UiNode, text: string, query: string, matchCase: boolean, out: FindMatch[]): void {\n let haystack = text;\n let needle = query;\n if (!matchCase) {\n const lowered = text.toLowerCase();\n const loweredQuery = query.toLowerCase();\n // Case folding is not always length-preserving ('İ' lowercases to\n // two code units), and a shifted offset would highlight the wrong\n // characters. Those rare strings are matched with case instead of\n // silently misplacing the highlight.\n if (lowered.length === text.length && loweredQuery.length === query.length) {\n haystack = lowered;\n needle = loweredQuery;\n }\n }\n for (let from = 0; from <= haystack.length - needle.length;) {\n const at = haystack.indexOf(needle, from);\n if (at < 0) {\n return;\n }\n out.push({ node, start: at, end: at + needle.length });\n from = at + needle.length;\n }\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { LayoutRecord } from '../layout/LayoutRecord';\nimport type { TextMeasurer } from '../layout/TextMeasurer';\nimport { createPaintState, resolvePaintState } from '../rendering/PaintState';\nimport { selectableTextNodes, selectableTextOf, type TextRange } from '../selection/UiSelectable';\nimport { paragraphGeometry, selectionRectsIn } from '../selection/TextSelectionGeometry';\nimport type { UiSelectionController } from '../selection/UiSelectionController';\nimport type { UiKeyModifiers } from '../input/UiInputEvent';\nimport { detectEditingPlatform, type EditingPlatform } from '../editing/EditingKeymap';\nimport { findMatchesIn, type FindMatch, type FindOptions } from './TextFind';\nimport { clearMatchRanges, setMatchRanges } from './UiTextMatches';\n\n/**\n * What the controller needs from the runtime around it: geometry,\n * measurement, and the two things it cannot do itself — mark a node\n * dirty and scroll a rectangle into view.\n */\nexport interface FindHost {\n recordFor(node: UiNode): LayoutRecord | undefined;\n readonly measurer: TextMeasurer;\n markDirty(node: UiNode, flags: DirtyFlags): void;\n /** Scrolls ancestors so a node-local box is visible. */\n reveal(node: UiNode, box: LayoutBox): void;\n /** The subtree a search runs over: the layout root. */\n root(): UiNode;\n /** Gives a node keyboard focus; used to put the caret in the app's query field. */\n focus(node: UiNode): void;\n}\n\nexport interface FindControllerOptions {\n /** Keyboard conventions; detected from the user agent by default. */\n platform?: EditingPlatform;\n}\n\n/** Room kept around the active match when scrolling it into view. */\nconst MATCH_REVEAL_PADDING = 8;\n\n/**\n * Finding text in the app's own content.\n *\n * The browser's find bar searches the DOM, and a canvas has none, so\n * Ctrl+F over a Gesso app has to be the app's.\n *\n * The corpus is the node tree — every string `selectableTextNodes`\n * finds, in reading order. That is the same reach a DOM mirror would\n * have and no more: a `LazyColumn` materialises only its visible\n * window, so rows that are not nodes are not searched. Closing that\n * gap means searching the data behind the list rather than the nodes\n * over it, which only the app can do; the engine is here, and a\n * supplementary source is the next thing to add to it.\n *\n * The active match is a real selection, through the selection\n * controller — so it looks like every other selection, copy takes it,\n * and revealing it uses the same scrolling. Every other match carries a\n * softer highlight on its node (see `UiTextMatches`). Both renderers\n * draw the matches under the selection, so the active one reads as the\n * strongest thing on the page.\n *\n * The UI is the app's: this is the engine a find bar drives, and\n * `isOpen` is the only thing it says about that UI — that a find\n * session is running, so Ctrl/Cmd+F and Escape mean find rather than\n * whatever they would otherwise. Whether the browser's own find bar is\n * left alone is the shell's call (`WorkerApp`'s `interceptFind`),\n * because the worker's answer cannot come back in time to cancel it.\n */\nexport class UiFindController {\n private readonly platform: EditingPlatform;\n private readonly paint = createPaintState();\n private readonly listeners = new Set<() => void>();\n private opened = false;\n private field: UiNode | null = null;\n private currentQuery = '';\n private options: FindOptions = {};\n private found: FindMatch[] = [];\n private active = -1;\n /** The nodes currently carrying match ranges, so they can be cleared. */\n private highlighted: UiNode[] = [];\n\n constructor(\n private readonly host: FindHost,\n private readonly selection: UiSelectionController,\n options: FindControllerOptions = {}\n ) {\n this.platform = options.platform ?? detectEditingPlatform();\n }\n\n // ---------------------------------------------------------------------------\n // The session\n // ---------------------------------------------------------------------------\n\n /** Whether a find session is running. */\n get isOpen(): boolean {\n return this.opened;\n }\n\n /** Notified whenever the query, the matches or `isOpen` change. */\n onChange(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * The app's query field, so opening a session can put the caret in\n * it. The app registers it through a `ref`; without one a session\n * still opens and the user clicks into the field themselves.\n */\n setField(node: UiNode | null): void {\n this.field = node;\n }\n\n /** Starts a find session; the app shows its bar for this. */\n open(): void {\n if (!this.opened) {\n this.opened = true;\n this.notify();\n }\n // After the notify, so the field the app renders for an open\n // session exists by the time it is focused.\n if (this.field !== null) {\n this.host.focus(this.field);\n }\n }\n\n /** Ends the session and drops the matches. */\n close(): void {\n if (!this.opened && this.currentQuery.length === 0) {\n return;\n }\n this.opened = false;\n this.clear();\n }\n\n /**\n * Default keyboard behaviour: Ctrl/Cmd+F starts a session, Escape\n * ends one. Returns true when the key was consumed — Escape only\n * while a session is running, so it still clears a selection\n * otherwise.\n */\n handleKey(key: string, modifiers: UiKeyModifiers): boolean {\n const primary = this.platform === 'mac' ? modifiers.meta : modifiers.ctrl;\n if (primary && !modifiers.alt && (key === 'f' || key === 'F')) {\n this.open();\n return true;\n }\n if (key === 'Escape' && this.opened) {\n this.close();\n return true;\n }\n return false;\n }\n\n // ---------------------------------------------------------------------------\n // Matches\n // ---------------------------------------------------------------------------\n\n /** The query the matches are for; empty when nothing is being searched. */\n get query(): string {\n return this.currentQuery;\n }\n\n /** Every match, in document order. */\n get matches(): readonly FindMatch[] {\n return this.found;\n }\n\n get matchCount(): number {\n return this.found.length;\n }\n\n /** The index of the active match, or -1 when there is none. */\n get activeIndex(): number {\n return this.active;\n }\n\n /**\n * Runs a query over the app's text and activates the first match.\n * Returns how many there are; an empty query clears everything.\n */\n search(query: string, options: FindOptions = {}): number {\n this.currentQuery = query;\n this.options = options;\n if (query.length === 0) {\n this.clear();\n return 0;\n }\n this.found = this.collect(query, options);\n this.highlight();\n this.activate(this.found.length > 0 ? 0 : -1);\n this.notify();\n return this.found.length;\n }\n\n /**\n * Re-runs the current query. The tree changed under it — content was\n * edited, a list scrolled new rows in — so the matches did too.\n */\n refresh(): void {\n if (this.currentQuery.length === 0) {\n return;\n }\n const previous = this.found[this.active];\n this.found = this.collect(this.currentQuery, this.options);\n this.highlight();\n // Stay on the same match when it survived, so refreshing under the\n // user does not send them back to the top of the page.\n const same =\n previous === undefined\n ? -1\n : this.found.findIndex(\n match => match.node === previous.node && match.start === previous.start && match.end === previous.end\n );\n this.activate(same >= 0 ? same : this.found.length > 0 ? 0 : -1);\n this.notify();\n }\n\n /** Moves to the next match, wrapping around. */\n next(): boolean {\n return this.step(1);\n }\n\n /** Moves to the previous match, wrapping around. */\n previous(): boolean {\n return this.step(-1);\n }\n\n /** Drops the query, the matches and their highlights. */\n clear(): void {\n this.currentQuery = '';\n this.found = [];\n this.active = -1;\n for (const node of this.highlighted) {\n if (clearMatchRanges(node)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n this.highlighted = [];\n this.notify();\n }\n\n /**\n * A node left the tree. Its matches went with it, so the search is\n * re-run rather than left pointing at something that is gone.\n */\n handleNodeRemoved(node: UiNode): void {\n if (this.currentQuery.length === 0) {\n return;\n }\n if (this.highlighted.includes(node) || this.found.some(match => match.node === node)) {\n this.clear();\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internals\n // ---------------------------------------------------------------------------\n\n private notify(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n\n private step(direction: 1 | -1): boolean {\n if (this.found.length === 0) {\n return false;\n }\n const from = this.active < 0 ? (direction > 0 ? -1 : 0) : this.active;\n const next = (from + direction + this.found.length) % this.found.length;\n this.activate(next);\n this.notify();\n return true;\n }\n\n /**\n * The matches of a query, dropped where the text they are in is not\n * drawn: a paragraph capped by `maxLines` shows part of itself, and a\n * match the user cannot see must not be counted or stepped through.\n * Geometry is only measured for the nodes that matched at all.\n */\n private collect(query: string, options: FindOptions): FindMatch[] {\n const nodes = selectableTextNodes(this.host.root());\n const matches = findMatchesIn(nodes, selectableTextOf, query, options);\n const drawnEnd = new Map<UiNode, number>();\n return matches.filter(match => {\n let end = drawnEnd.get(match.node);\n if (end === undefined) {\n end = this.geometryEndOf(match.node);\n drawnEnd.set(match.node, end);\n }\n return match.end <= end;\n });\n }\n\n /** Writes the match ranges onto the nodes that hold them. */\n private highlight(): void {\n const byNode = new Map<UiNode, TextRange[]>();\n for (const match of this.found) {\n const ranges = byNode.get(match.node);\n if (ranges === undefined) {\n byNode.set(match.node, [{ start: match.start, end: match.end }]);\n } else {\n ranges.push({ start: match.start, end: match.end });\n }\n }\n for (const [node, ranges] of byNode) {\n if (setMatchRanges(node, ranges)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n for (const node of this.highlighted) {\n if (!byNode.has(node) && clearMatchRanges(node)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n this.highlighted = Array.from(byNode.keys());\n }\n\n /** Selects the match at `index` and scrolls it into view. */\n private activate(index: number): void {\n this.active = index;\n if (index < 0) {\n this.selection.clear();\n return;\n }\n const match = this.found[index];\n this.selection.selectRange(match.node, match.start, match.end);\n this.revealMatch(match);\n }\n\n private revealMatch(match: FindMatch): void {\n const geometry = this.geometryOf(match.node);\n const rects = selectionRectsIn(geometry, match.start, match.end);\n if (rects.length === 0) {\n return;\n }\n const first = rects[0];\n this.host.reveal(match.node, {\n x: first.x - MATCH_REVEAL_PADDING,\n y: first.y - MATCH_REVEAL_PADDING,\n width: first.width + 2 * MATCH_REVEAL_PADDING,\n height: first.height + 2 * MATCH_REVEAL_PADDING\n });\n }\n\n private geometryEndOf(node: UiNode): number {\n return this.geometryOf(node).end;\n }\n\n /** The node's text laid out in its own coordinates; see UiSelectionController. */\n private geometryOf(node: UiNode) {\n const rec = this.host.recordFor(node);\n const state = resolvePaintState(node, this.paint);\n const box: LayoutBox =\n rec === undefined\n ? { x: 0, y: 0, width: 0, height: 0 }\n : {\n x: rec.paddingLeft,\n y: rec.paddingTop,\n width: Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight),\n height: Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom)\n };\n return paragraphGeometry(state.text ?? '', box, state, this.host.measurer);\n }\n}\n","import type { UiNode } from '../graph/UiNode';\n\n/**\n * Tells a listener when one particular node gains or loses focus.\n *\n * `UiFocusManager.onFocusChange` reports the node that now holds focus,\n * which is the right shape for the runtime (scroll it into view) and\n * the wrong one for a focus ring: with a ring on every control, every\n * listener would run on every focus change and all but two of them\n * would decide nothing happened.\n *\n * A focus change touches exactly two nodes — the one losing it and the\n * one gaining it — so the notifier keeps the previous node and fires\n * only those. The cost of a focus change is therefore independent of\n * how many rings exist.\n */\nexport class FocusNotifier {\n private readonly listeners = new Map<UiNode, Set<(focused: boolean) => void>>();\n private focused: UiNode | null = null;\n private visible = true;\n\n /** Registers a listener for one node; the returned function removes it. */\n add(node: UiNode, listener: (focused: boolean) => void): () => void {\n let forNode = this.listeners.get(node);\n if (forNode === undefined) {\n forNode = new Set();\n this.listeners.set(node, forNode);\n }\n forNode.add(listener);\n return () => {\n const set = this.listeners.get(node);\n if (set === undefined) {\n return;\n }\n set.delete(listener);\n if (set.size === 0) {\n this.listeners.delete(node);\n }\n };\n }\n\n /** Whether the node the notifier last saw focused is this one. */\n isFocused(node: UiNode): boolean {\n return this.focused === node;\n }\n\n /** Whether the focus `node` holds should be shown; false for any node without focus. */\n isFocusVisible(node: UiNode): boolean {\n return this.focused === node && this.visible;\n }\n\n /**\n * Fed from `UiFocusManager.onFocusChange`, which reports the new\n * holder and, through `visible`, whether its focus should be shown. A\n * change of visibility alone is reported to the holder's listeners as\n * a focus change, which is what lets a ring appear on a mouse-focused\n * control when the keyboard is next used.\n */\n handleFocusChange(node: UiNode | null, visible = true): void {\n const previous = this.focused;\n const wasVisible = this.visible;\n this.focused = node;\n this.visible = visible;\n if (previous === node) {\n if (node !== null && wasVisible !== visible) {\n this.notify(node, true);\n }\n return;\n }\n if (previous !== null) {\n this.notify(previous, false);\n }\n if (node !== null) {\n this.notify(node, true);\n }\n }\n\n /**\n * A removed node's listeners go with it. The modifier's own detach\n * removes them too; this is the case where the node leaves without\n * its modifiers being reconciled first.\n */\n handleNodeRemoved(node: UiNode): void {\n this.listeners.delete(node);\n if (this.focused === node) {\n this.focused = null;\n }\n }\n\n private notify(node: UiNode, focused: boolean): void {\n const forNode = this.listeners.get(node);\n if (forNode === undefined) {\n return;\n }\n for (const listener of forNode) {\n listener(focused);\n }\n }\n}\n","import { UiNodeType } from '../graph/UiNodeType';\nimport type { UiNode } from '../graph/UiNode';\nimport { isNodeInert } from './UiInteraction';\nimport type { UiKeyModifiers } from './UiInputEvent';\n\n/**\n * One press in a shortcut, after parsing.\n *\n * `mod` is deliberately not `ctrl` or `meta`. The framework runs in a\n * worker and has no reliable answer to which platform it is on, and the\n * answer would be wrong for a person on a Mac keyboard plugged into a\n * Linux machine anyway. So `Mod` matches **either** Control or Command,\n * and an application that wants the distinction spells out the one it\n * means. This is the same choice the DOM's own `metaKey`/`ctrlKey` pair\n * forces on every web application, made once here instead of in every\n * handler.\n */\nexport interface UiShortcutStep {\n readonly key: string;\n readonly mod: boolean;\n readonly ctrl: boolean;\n readonly meta: boolean;\n readonly shift: boolean;\n readonly alt: boolean;\n}\n\n/** What an application registers. */\nexport interface UiShortcut {\n /**\n * The keys, as `'Mod+K'`, `'Shift+?'`, or `'g d'` for a chord: a\n * sequence of presses, separated by spaces, that must arrive in\n * order and within the chord timeout.\n */\n readonly keys: string;\n /** What a palette lists it as. */\n readonly label: string;\n readonly run: () => void;\n /**\n * The subtree this shortcut belongs to, or null for the whole\n * application.\n *\n * A scoped shortcut is live only while focus is inside the node,\n * which is what makes \"Delete removes the selected row\" safe to\n * register: the table registers it, and it stops existing the moment\n * the person tabs into the search field.\n */\n readonly scope?: UiNode | null;\n /**\n * Higher wins when two live shortcuts want the same keys. Default 0.\n *\n * A scope is not a priority. Two shortcuts scoped to nested nodes are\n * separated by depth without either naming a number; this is for the\n * case where a route wants to take a key the application had.\n */\n readonly priority?: number;\n /** A last check before it runs, for a command that is not always available. */\n readonly when?: () => boolean;\n /** A heading a palette groups it under. */\n readonly group?: string;\n}\n\n/** A registered shortcut, with what the registry worked out about it. */\nexport interface UiShortcutBinding extends UiShortcut {\n /** The parsed presses, so a palette can render them without reparsing. */\n readonly steps: readonly UiShortcutStep[];\n /** `keys` written the way it should be shown. */\n readonly display: string;\n}\n\nexport interface ShortcutRegistryOptions {\n /**\n * How long the rest of a chord has to arrive, in milliseconds.\n *\n * Long enough to type a second key deliberately, short enough that a\n * `g` pressed into a page and then forgotten does not swallow a key\n * a minute later.\n */\n chordTimeout?: number;\n /** The clock, for specs that drive a chord without waiting. */\n now?: () => number;\n}\n\n/** The keys that never resolve a shortcut on their own. */\nconst MODIFIER_KEYS: ReadonlySet<string> = new Set(['Shift', 'Control', 'Alt', 'Meta', 'CapsLock']);\n\n/**\n * Every shortcut the application has, and which of them are live.\n *\n * Segue and Sluice each put one `onKeyDown` on the root and switch on\n * the current route, which has the three problems that shape this. A\n * screen cannot add a key without editing a file it does not own; two\n * screens that want the same key have no way to say which wins; and\n * nothing can list what is available, so neither application has a\n * palette or a help sheet and neither could grow one.\n *\n * A registry answers all three. A component registers what it can do\n * and gets a function back that unregisters it, so the shortcut's\n * lifetime is the component's. A shortcut names the subtree it belongs\n * to, so it is live exactly while focus is inside it. And `active`\n * returns what would run right now, which is what a palette lists and\n * what a help sheet prints.\n *\n * ## What it is not\n *\n * It does not listen to anything. The registry is fed by the\n * `shortcuts` modifier, which puts one root listener on the node it is\n * attached to and hands each key here; a runtime that wants shortcuts\n * without modifiers can call `handleKey` from its own listener. Keeping\n * the listening out means the registry is a plain object a spec can\n * drive directly, and means it does not need the dispatcher, the focus\n * manager, or anything else a `gesso-framework` service would have had\n * to inject.\n *\n * ## Typing is not a shortcut\n *\n * A shortcut with no modifier is skipped while a text field has focus.\n * Without that rule an application with a `n` for \"new note\" becomes an\n * application you cannot type the letter n into, which is the failure\n * every home-grown key handler eventually ships.\n */\nexport class UiShortcutRegistry {\n private readonly bindings: UiShortcutBinding[] = [];\n private readonly chordTimeout: number;\n private readonly now: () => number;\n\n /** The presses of a chord matched so far, and when the last arrived. */\n private pending: UiShortcutStep[] = [];\n private pendingAt = 0;\n\n constructor(options: ShortcutRegistryOptions = {}) {\n this.chordTimeout = options.chordTimeout ?? 1200;\n this.now = options.now ?? defaultClock;\n }\n\n /** Adds a shortcut. The returned function removes it again. */\n register(shortcut: UiShortcut): () => void {\n const steps = parseShortcut(shortcut.keys);\n const binding: UiShortcutBinding = { ...shortcut, steps, display: formatShortcut(steps) };\n this.bindings.push(binding);\n return () => {\n const index = this.bindings.indexOf(binding);\n if (index !== -1) {\n this.bindings.splice(index, 1);\n }\n };\n }\n\n /** Every registered shortcut, live or not, in registration order. */\n get all(): readonly UiShortcutBinding[] {\n return this.bindings;\n }\n\n /**\n * What would run right now, in the order the registry would try them.\n *\n * This is the palette's list, and it is the same computation the key\n * handler makes, so a palette cannot show a command that would not\n * fire and cannot hide one that would.\n *\n * `focused` is the node keyboard input is currently going to, which\n * for a dispatched KeyDown is the event's target: the keyboard\n * controller routes a key to the focused node and to the root when\n * nothing is focused, so the target is the answer without the\n * registry having to hold a focus manager.\n */\n active(focused: UiNode | null): readonly UiShortcutBinding[] {\n const live = this.bindings.filter(binding => this.isLive(binding, focused));\n // A stable sort, so registration order breaks a tie between two\n // shortcuts of equal priority and depth.\n return live\n .map((binding, index) => ({ binding, index }))\n .sort((a, b) => rank(b.binding, focused) - rank(a.binding, focused) || a.index - b.index)\n .map(entry => entry.binding);\n }\n\n /**\n * Offers a key press to the live shortcuts.\n *\n * True when one of them took it, which the caller turns into a\n * `preventDefault()` so the keyboard controller's own defaults (Tab\n * navigation, Enter on a button, an editable's typing) do not also\n * act on a key that has already been spent.\n */\n handleKey(key: string, modifiers: UiKeyModifiers, focused: UiNode | null): boolean {\n if (MODIFIER_KEYS.has(key)) {\n return false;\n }\n const step = stepFor(key, modifiers);\n const at = this.now();\n if (this.pending.length > 0 && at - this.pendingAt > this.chordTimeout) {\n this.pending = [];\n }\n const sequence = [...this.pending, step];\n\n let prefixed = false;\n for (const binding of this.active(focused)) {\n if (!matchesPrefix(binding.steps, sequence)) {\n continue;\n }\n if (binding.steps.length === sequence.length) {\n this.pending = [];\n binding.run();\n return true;\n }\n prefixed = true;\n }\n if (prefixed) {\n // The first key of a chord: held so the next press can complete\n // it, and reported as taken so nothing else acts on a `g` that\n // was the beginning of a command.\n this.pending = sequence;\n this.pendingAt = at;\n return true;\n }\n this.pending = [];\n return false;\n }\n\n /** Forgets a half-typed chord, for a route change or a lost focus. */\n reset(): void {\n this.pending = [];\n }\n\n /**\n * Whether a binding would fire for a key going to `focused`.\n *\n * Three things take a shortcut out: its scope does not contain the\n * focused node, its own `when` says no, and, for a shortcut with no\n * modifier at all, the focused node is somewhere text is being\n * typed. The last is the rule that keeps a single-letter shortcut\n * from making a text field unusable.\n */\n private isLive(binding: UiShortcutBinding, focused: UiNode | null): boolean {\n const scope = binding.scope ?? null;\n if (scope !== null && (focused === null || !containsNode(scope, focused))) {\n return false;\n }\n if (binding.when !== undefined && !binding.when()) {\n return false;\n }\n if (focused !== null && isTypingInto(focused) && binding.steps.every(step => isBare(step))) {\n return false;\n }\n return true;\n }\n}\n\n/**\n * Where a binding sits in the order, most specific first.\n *\n * Priority dominates, and depth of scope separates the rest: a\n * shortcut scoped to a row beats one scoped to the table it is in,\n * which beats one scoped to nothing, without either of them naming a\n * number.\n */\nfunction rank(binding: UiShortcutBinding, focused: UiNode | null): number {\n const priority = (binding.priority ?? 0) * 1000;\n const scope = binding.scope ?? null;\n if (scope === null || focused === null) {\n return priority;\n }\n let depth = 0;\n for (let node: UiNode | null = scope; node !== null; node = node.parent) {\n depth += 1;\n }\n return priority + depth;\n}\n\n/** Whether `ancestor` is `node` or one of its ancestors. */\nfunction containsNode(ancestor: UiNode, node: UiNode): boolean {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current === ancestor) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Whether keys reaching this node are being typed into something.\n *\n * An editable node, or anything inside one: the caret may be on a text\n * node inside a field rather than on the field itself.\n */\nfunction isTypingInto(node: UiNode): boolean {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current.type === UiNodeType.EditableText && !isNodeInert(current)) {\n return true;\n }\n }\n return false;\n}\n\n/** A press with no modifier at all: an ordinary character. */\nfunction isBare(step: UiShortcutStep): boolean {\n return !step.mod && !step.ctrl && !step.meta && !step.alt;\n}\n\n/**\n * Reads `'Mod+Shift+K'` or `'g d'` into presses.\n *\n * Space separates the presses of a chord and `+` separates the\n * modifiers of one press, so `'Mod++'` is Mod and the plus key: the\n * last segment is always the key, however it is spelled.\n */\nexport function parseShortcut(keys: string): readonly UiShortcutStep[] {\n return keys\n .trim()\n .split(/\\s+/)\n .filter(part => part.length > 0)\n .map(parseStep);\n}\n\nfunction parseStep(text: string): UiShortcutStep {\n const parts = text.split('+');\n // A trailing '+' means the plus key itself, which split leaves as an\n // empty last segment with the real key one before it.\n if (parts.length > 1 && parts[parts.length - 1] === '') {\n parts.splice(parts.length - 2, 2, '+');\n }\n const key = parts.pop() ?? '';\n let mod = false;\n let ctrl = false;\n let meta = false;\n let shift = false;\n let alt = false;\n for (const part of parts) {\n switch (part.toLowerCase()) {\n case 'mod':\n case 'cmdorctrl':\n mod = true;\n break;\n case 'ctrl':\n case 'control':\n ctrl = true;\n break;\n case 'meta':\n case 'cmd':\n case 'command':\n meta = true;\n break;\n case 'shift':\n shift = true;\n break;\n case 'alt':\n case 'option':\n alt = true;\n break;\n default:\n // An unknown word is a typo in a shortcut, and a shortcut that\n // silently never fires is worse than one that complains.\n console.warn(`Unknown shortcut modifier '${part}' in '${text}'.`);\n }\n }\n return { key: normalizeKey(key), mod, ctrl, meta, shift, alt };\n}\n\n/**\n * A key as the platform adapter reports it, case-folded for letters.\n *\n * `'Mod+K'` and `'Mod+k'` are the same shortcut, and the browser\n * reports whichever the shift state produced. Anything longer than one\n * character is a named key (`'Enter'`, `'ArrowUp'`) and keeps its case.\n */\nfunction normalizeKey(key: string): string {\n return key.length === 1 ? key.toLowerCase() : key;\n}\n\n/** Whether the presses so far could still complete this shortcut. */\nfunction matchesPrefix(steps: readonly UiShortcutStep[], sequence: readonly UiShortcutStep[]): boolean {\n if (sequence.length > steps.length) {\n return false;\n }\n return sequence.every((step, index) => sameStep(steps[index], step));\n}\n\nfunction sameStep(want: UiShortcutStep, got: UiShortcutStep): boolean {\n if (want.key !== got.key || want.shift !== got.shift || want.alt !== got.alt) {\n return false;\n }\n if (want.mod) {\n return got.ctrl || got.meta;\n }\n return want.ctrl === got.ctrl && want.meta === got.meta;\n}\n\n/** The press the person actually made. */\nfunction stepFor(key: string, modifiers: UiKeyModifiers): UiShortcutStep {\n return {\n key: normalizeKey(key),\n mod: false,\n ctrl: modifiers.ctrl,\n meta: modifiers.meta,\n shift: modifiers.shift,\n alt: modifiers.alt\n };\n}\n\n/**\n * The shortcut as a palette should print it.\n *\n * `Mod` is printed as `Ctrl`, not as a platform glyph: the render\n * worker cannot see the platform, and a wrong symbol is worse than a\n * plain word. An application that knows what it is running on can\n * format the `steps` itself.\n */\nexport function formatShortcut(steps: readonly UiShortcutStep[]): string {\n return steps.map(formatStep).join(' ');\n}\n\nfunction formatStep(step: UiShortcutStep): string {\n const parts: string[] = [];\n if (step.mod || step.ctrl) {\n parts.push('Ctrl');\n }\n if (step.meta) {\n parts.push('Meta');\n }\n if (step.alt) {\n parts.push('Alt');\n }\n if (step.shift) {\n parts.push('Shift');\n }\n parts.push(step.key.length === 1 ? step.key.toUpperCase() : step.key);\n return parts.join('+');\n}\n\nfunction defaultClock(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now();\n}\n","import type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from '../layout/LayoutTypes';\n\n/**\n * What is being carried, and what it is.\n *\n * `type` is the application's own vocabulary (`'queue/track'`,\n * `'board/card'`), and a drop target says which types it accepts.\n * Matching by a string rather than by a class keeps the payload plain\n * data, which is what it has to be: an OS file drop arrives as a shell\n * message and becomes a payload of exactly this shape, and a message\n * that crossed the barrier cannot carry a prototype.\n */\nexport interface UiDragPayload {\n readonly type: string;\n readonly data: unknown;\n}\n\n/**\n * What a drop would do, in the vocabulary of the platform drag APIs.\n *\n * The framework does none of it: the effect is what a zone reports so\n * the source and the cursor can say what will happen, and `'none'` is\n * a zone refusing the payload it is being offered.\n */\nexport type UiDropEffect = 'move' | 'copy' | 'link' | 'none';\n\n/** Where the drag is now, and what it is carrying. */\nexport interface UiDragState {\n readonly payload: UiDragPayload;\n readonly x: number;\n readonly y: number;\n /** The node the drag started from, or null for one from outside. */\n readonly source: UiNode | null;\n /** True for a drag that began outside the application, an OS file drop. */\n readonly external: boolean;\n}\n\n/**\n * A registered drop target.\n *\n * The zone answers where it is rather than being asked to hold a box,\n * because a list scrolls and a card moves and the answer is only ever\n * correct at the moment it is asked. `null` from `boxOf` (a node with\n * no layout record yet) takes the zone out of consideration for that\n * move rather than being an error.\n */\nexport interface UiDropZone {\n readonly node: UiNode;\n boxOf(): LayoutBox | null;\n accepts(payload: UiDragPayload): boolean;\n /** The pointer entered the zone with a payload it accepts. */\n enter(state: UiDragState): void;\n /** The pointer moved inside the zone. */\n over(state: UiDragState): void;\n /** The pointer left, or the drag ended somewhere else. */\n leave(): void;\n /** The drag was released over the zone. What it did, for the source. */\n drop(state: UiDragState): UiDropEffect;\n}\n\n/** The result a completed drag reports back to whatever started it. */\nexport interface UiDragResult {\n readonly node: UiNode;\n readonly effect: UiDropEffect;\n}\n\n/**\n * One file that arrived from outside the application.\n *\n * Plain data, every field of it, because this crosses the barrier:\n * recognition runs in the render worker, which leaves the\n * shell holding a `File` object it cannot send. The bytes are an\n * `ArrayBuffer` so a large file is transferred rather than copied, and\n * they are optional because the shell reads them only for a drop that\n * something accepted, not for every move of the pointer over the\n * window.\n */\nexport interface UiDroppedFile {\n readonly name: string;\n /** The MIME type the platform reported, or '' when it had none. */\n readonly mediaType: string;\n readonly size: number;\n readonly lastModified: number;\n readonly bytes?: ArrayBuffer;\n}\n\n/**\n * The shell's message for a drag that started outside the window.\n *\n * The same four phases a drop target sees from a drag inside it, so a\n * zone that accepts `EXTERNAL_FILES` needs no second code path: the\n * session turns this into an ordinary drag and every zone answers it\n * the way it answers any other. The phases mirror the DOM's\n * `dragenter`, `dragover`, `dragleave` and `drop`.\n *\n * Position is in the same canvas-space coordinates as a pointer event,\n * so the shell subtracts the surface's bounding rect exactly as it does\n * for a press.\n */\nexport interface UiFileDropMessage {\n readonly type: 'fileDrop';\n readonly phase: 'enter' | 'over' | 'leave' | 'drop';\n readonly x: number;\n readonly y: number;\n /**\n * What is being carried. On `enter` and `over` the platform allows\n * only the names and types to be read, so `bytes` is absent there\n * and present on `drop`.\n */\n readonly files: readonly UiDroppedFile[];\n}\n\n/** The payload type an OS file drop arrives under. */\nexport const EXTERNAL_FILES = 'gesso/files';\n\n/**\n * The drag in flight, and everywhere it could be put down.\n *\n * `draggable` moves a node's transform and stops, which is enough for a\n * card that goes back where it was and nothing at all for a card that\n * goes somewhere. What was missing is the other half: somewhere to\n * drop it, a way to say what it is, and an answer to \"is this over\n * anything that would take it\".\n *\n * The session is that answer and deliberately nothing more. It holds\n * no geometry of its own, subscribes to nothing and paints nothing: a\n * `dropTarget` modifier registers a zone, a `dragSource` modifier opens\n * a drag and moves it, and the session's whole job is to work out which\n * registered zone is under the point and to tell it so. Both halves are\n * modifiers because both are behaviour attached to an element, which is\n * the test for what belongs here.\n *\n * There is one session per graph root rather than one per process, so\n * two applications in one worker, and a test suite renders dozens,\n * cannot see each other's drops.\n *\n * ## Which zone the point is in\n *\n * The deepest zone whose box contains the point and that accepts the\n * payload, breaking a tie by registration order. Depth rather than\n * paint order because a drop target is a region rather than a drawing:\n * a row inside a list is inside the list, and dropping on the row is\n * unambiguously the more specific answer. A zone that does not accept\n * the payload is passed over rather than blocking the one behind it,\n * so a list of mixed rows behaves the way a person expects.\n */\nexport class UiDragSession {\n private readonly zones: UiDropZone[] = [];\n private current: UiDragState | null = null;\n private inside: UiDropZone | null = null;\n\n /** Registers a drop target. The returned function unregisters it. */\n addZone(zone: UiDropZone): () => void {\n this.zones.push(zone);\n return () => {\n const index = this.zones.indexOf(zone);\n if (index !== -1) {\n this.zones.splice(index, 1);\n }\n if (this.inside === zone) {\n this.inside = null;\n }\n };\n }\n\n /** The drag in flight, or null when nothing is being carried. */\n get state(): UiDragState | null {\n return this.current;\n }\n\n /** The zone the pointer is over, or null. */\n get overNode(): UiNode | null {\n return this.inside?.node ?? null;\n }\n\n /**\n * Whether a zone would take what is being carried, for a source that\n * wants to show the person that letting go will do something.\n */\n get accepted(): boolean {\n return this.inside !== null;\n }\n\n /** Starts a drag. Any drag already in flight is cancelled first. */\n begin(payload: UiDragPayload, x: number, y: number, source: UiNode | null, external = false): void {\n if (this.current !== null) {\n this.cancel();\n }\n this.current = { payload, x, y, source, external };\n this.resolve();\n }\n\n /** Moves the drag, entering and leaving zones as it crosses them. */\n move(x: number, y: number): void {\n if (this.current === null) {\n return;\n }\n this.current = { ...this.current, x, y };\n this.resolve();\n }\n\n /**\n * Releases the drag over whatever it is on, and reports what that did.\n *\n * Null when it was let go over nothing that would take it, which is\n * the case a source has to handle by putting the node back.\n */\n end(): UiDragResult | null {\n const state = this.current;\n const zone = this.inside;\n this.current = null;\n this.inside = null;\n if (state === null || zone === null) {\n zone?.leave();\n return null;\n }\n const effect = zone.drop(state);\n zone.leave();\n return { node: zone.node, effect };\n }\n\n /** Abandons the drag: the zone under it is told, and nothing is dropped. */\n cancel(): void {\n this.inside?.leave();\n this.inside = null;\n this.current = null;\n }\n\n /**\n * Turns a shell's file-drop message into an ordinary drag.\n *\n * Everything downstream of here cannot tell the difference, which is\n * the point: a zone written for `EXTERNAL_FILES` is a zone like any\n * other, and the four phases map onto the four things a drag does.\n */\n applyFileDrop(message: UiFileDropMessage): UiDragResult | null {\n const payload: UiDragPayload = { type: EXTERNAL_FILES, data: message.files };\n switch (message.phase) {\n case 'enter':\n this.begin(payload, message.x, message.y, null, true);\n return null;\n case 'over':\n if (this.current === null) {\n this.begin(payload, message.x, message.y, null, true);\n } else {\n this.move(message.x, message.y);\n }\n return null;\n case 'leave':\n this.cancel();\n return null;\n case 'drop':\n if (this.current === null) {\n this.begin(payload, message.x, message.y, null, true);\n } else {\n this.move(message.x, message.y);\n }\n return this.end();\n }\n }\n\n /**\n * Works out which zone the point is in and reports the crossings.\n *\n * Walks the registered zones once per move. There are as many of them\n * as the page has drop targets, which is a handful, so this is a\n * linear scan on purpose: an index would have to be invalidated by\n * every layout, and the layout engine already answers \"where is this\n * node\" in constant time.\n */\n private resolve(): void {\n const state = this.current;\n if (state === null) {\n return;\n }\n let best: UiDropZone | null = null;\n let bestDepth = -1;\n for (const zone of this.zones) {\n if (!zone.accepts(state.payload)) {\n continue;\n }\n const box = zone.boxOf();\n if (box === null || !contains(box, state.x, state.y)) {\n continue;\n }\n const depth = depthOf(zone.node);\n if (depth > bestDepth) {\n best = zone;\n bestDepth = depth;\n }\n }\n if (best !== this.inside) {\n this.inside?.leave();\n this.inside = best;\n best?.enter(state);\n return;\n }\n best?.over(state);\n }\n}\n\nfunction contains(box: LayoutBox, x: number, y: number): boolean {\n return x >= box.x && x < box.x + box.width && y >= box.y && y < box.y + box.height;\n}\n\nfunction depthOf(node: UiNode): number {\n let depth = 0;\n for (let current: UiNode | null = node.parent; current !== null; current = current.parent) {\n depth += 1;\n }\n return depth;\n}\n\nconst sessions = new WeakMap<UiNode, UiDragSession>();\n\n/**\n * The session for the graph a node belongs to, created on first ask.\n *\n * Keyed by the root rather than held in a module variable so that two\n * applications in one worker keep their drags apart; a spec that\n * renders several trees would otherwise have one drag visible in all\n * of them.\n */\nexport function dragSessionFor(node: UiNode): UiDragSession {\n let root = node;\n while (root.parent !== null) {\n root = root.parent;\n }\n let session = sessions.get(root);\n if (session === undefined) {\n session = new UiDragSession();\n sessions.set(root, session);\n }\n return session;\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { LayoutRecord } from '../layout/LayoutRecord';\nimport type { TextMeasurer } from '../layout/TextMeasurer';\nimport { createPaintState, resolvePaintState } from '../rendering/PaintState';\nimport { EditableLayout } from '../editing/EditableLayout';\nimport type { EditableTextModel, EditUnit } from '../editing/EditableTextModel';\nimport { commandForKey, detectEditingPlatform, type EditCommand, type EditingPlatform } from '../editing/EditingKeymap';\nimport { wordRangeAt, lineStartAt, lineEndAt } from '../editing/TextBoundaries';\nimport { editorFor, isEditableNode, isMultiline, isReadOnly, nextCaretToggle } from '../editing/UiEditable';\nimport type { UiInputDispatcher } from './UiInputDispatcher';\nimport type { UiFocusManager } from './UiFocusManager';\nimport { UiBeforeInputEvent, UiTextChangeEvent, type UiKeyModifiers } from './UiInputEvent';\n\n/**\n * What the controller needs from the runtime around it: geometry,\n * measurement, and the two things it cannot do itself — mark a node\n * dirty and scroll a rectangle into view.\n */\nexport interface EditingHost {\n recordFor(node: UiNode): LayoutRecord | undefined;\n /** Where the node is seen, scroll offsets applied; for the shell's caret rectangle. */\n visibleBox(node: UiNode): LayoutBox;\n /** A layout-root point in the node's own coordinates. */\n toLocal(node: UiNode, x: number, y: number): { x: number; y: number };\n readonly measurer: TextMeasurer;\n markDirty(node: UiNode, flags: DirtyFlags): void;\n /** Scrolls ancestors so a node-local box is visible. */\n reveal(node: UiNode, box: LayoutBox): void;\n now(): number;\n}\n\n/**\n * What a shell mirrors while an editable has focus: its text and\n * selection (so native copy, cut and IME context are right) and where\n * the caret is on the canvas (so the IME candidate window opens there).\n */\nexport interface EditingState {\n readonly text: string;\n readonly selectionStart: number;\n readonly selectionEnd: number;\n /** Caret box in canvas (layout root) pixels. */\n readonly caret: LayoutBox;\n readonly multiline: boolean;\n readonly composing: boolean;\n}\n\nexport interface EditingControllerOptions {\n /** Keyboard conventions; detected from the user agent by default. */\n platform?: EditingPlatform;\n}\n\n/** How long two presses may be apart and still count as a double click. */\nconst MULTI_CLICK_MS = 500;\nconst MULTI_CLICK_SLOP = 4;\n/** Room kept around the caret when scrolling it into view. */\nconst CARET_REVEAL_PADDING = 4;\n\n/**\n * Editing behaviour for EditableText nodes.\n *\n * The keyboard and pointer controllers call in here as their default\n * behaviour for an editable target — after the app's listeners have\n * run and only when none of them called preventDefault() — and the\n * host feeds it the shell's text input: `beforeinput` edits,\n * composition, paste. The model on the node is the single source of\n * truth; this class turns intents into model changes, marks the node\n * dirty so layout and paint follow, reports the change through an\n * `input` event, and keeps the caret in view.\n *\n * Text arrives one of two ways. A shell with an editing proxy sends\n * `beforeinput`, where the browser has already resolved the IME, dead\n * keys and the keyboard layout; then printable key presses are not\n * text and `textFromKeys` is off. Without a proxy — tests, a bare\n * runtime — printable keys become insertions.\n */\nexport class UiEditingController {\n /** Insert printable key presses as text; off when a shell delivers text through beforeinput. */\n textFromKeys = true;\n\n private readonly platform: EditingPlatform;\n private readonly paint = createPaintState();\n private focusedEditable: UiNode | null = null;\n private visible = true;\n /** x the current run of vertical moves keeps returning to. */\n private verticalGoalX: number | undefined = undefined;\n private dragging: UiNode | null = null;\n private lastPress: { node: UiNode; x: number; y: number; at: number; count: number } | null = null;\n private compositionOpen = false;\n\n constructor(\n private readonly host: EditingHost,\n private readonly dispatcher: UiInputDispatcher,\n private readonly focus: UiFocusManager,\n options: EditingControllerOptions = {}\n ) {\n this.platform = options.platform ?? detectEditingPlatform();\n this.focus.onFocusChange(node => this.handleFocusChange(node));\n }\n\n /** The editable that holds focus, or null. */\n get focused(): UiNode | null {\n return this.focusedEditable;\n }\n\n /** Whether the pointer controller should hand presses on the node to this controller. */\n isEditable(node: UiNode): boolean {\n return isEditableNode(node);\n }\n\n // ---------------------------------------------------------------------------\n // Keyboard\n // ---------------------------------------------------------------------------\n\n /**\n * Default keyboard behaviour for the focused editable. Returns true\n * when the key was an editing command, so the caller can mark the\n * event handled.\n */\n handleKey(node: UiNode, key: string, modifiers: UiKeyModifiers): boolean {\n if (!isEditableNode(node)) {\n return false;\n }\n const command = commandForKey(key, modifiers, this.platform, this.textFromKeys);\n if (command === null) {\n return false;\n }\n return this.execute(node, command);\n }\n\n private execute(node: UiNode, command: EditCommand): boolean {\n const model = editorFor(node);\n const readOnly = isReadOnly(node);\n switch (command.kind) {\n case 'move':\n this.move(node, model, command.unit, command.direction, command.extend);\n return true;\n case 'delete':\n if (readOnly) {\n return true;\n }\n return this.applyEdit(node, model, deleteInputType(command.unit, command.direction), null, () => {\n if (command.direction < 0) {\n model.deleteBackward(command.unit);\n } else {\n model.deleteForward(command.unit);\n }\n });\n case 'newline':\n if (!isMultiline(node)) {\n // Enter in a single-line field is the app's (submit), not ours.\n return false;\n }\n if (readOnly) {\n return true;\n }\n return this.applyEdit(node, model, 'insertLineBreak', '\\n', () => model.insertText('\\n'));\n case 'insert':\n if (readOnly) {\n return true;\n }\n return this.applyEdit(node, model, 'insertText', command.text, () => model.insertText(command.text));\n case 'selectAll':\n model.selectAll();\n this.afterSelectionChange(node, model);\n return true;\n case 'undo':\n if (readOnly) {\n return true;\n }\n this.history(node, model, 'historyUndo', () => model.undo());\n return true;\n case 'redo':\n if (readOnly) {\n return true;\n }\n this.history(node, model, 'historyRedo', () => model.redo());\n return true;\n }\n }\n\n private move(\n node: UiNode,\n model: EditableTextModel,\n unit: EditUnit | 'vertical',\n direction: -1 | 1,\n extend: boolean\n ): void {\n if (unit === 'vertical') {\n const layout = this.layoutOf(node);\n const target = layout.verticalMove(model.focus, direction, this.verticalGoalX);\n if (target === null) {\n // Off the first or last line: the caret goes to the text's edge.\n model.moveTo(direction < 0 ? 0 : model.text.length, extend);\n this.verticalGoalX = undefined;\n } else {\n model.moveTo(target.offset, extend);\n this.verticalGoalX = target.x;\n }\n this.afterSelectionChange(node, model, true);\n return;\n }\n if (unit === 'line') {\n // Home/End are the visual line, which needs the layout; the\n // model's 'line' is the hard line.\n const layout = this.layoutOf(node);\n const caret = layout.caretRect(model.focus);\n const line = layout.lines[caret.line];\n const hard = direction < 0 ? lineStartAt(model.text, model.focus) : lineEndAt(model.text, model.focus);\n const visual = direction < 0 ? line.start : line.end;\n model.moveTo(direction < 0 ? Math.max(hard, visual) : Math.min(hard, visual), extend);\n this.afterSelectionChange(node, model);\n return;\n }\n model.move(unit, direction, extend);\n this.afterSelectionChange(node, model);\n }\n\n // ---------------------------------------------------------------------------\n // Text input from the shell\n // ---------------------------------------------------------------------------\n\n /**\n * A `beforeinput` from the shell's editing proxy, in the DOM's\n * vocabulary. Returns true when it was applied to the focused\n * editable. Composition types are not handled here: the composition\n * methods carry them, and an engine that fires both must not insert\n * twice.\n */\n beforeInput(inputType: string, data: string | null): boolean {\n const node = this.focusedEditable;\n if (node === null) {\n return false;\n }\n const model = editorFor(node);\n const readOnly = isReadOnly(node);\n switch (inputType) {\n case 'insertText':\n case 'insertReplacementText':\n case 'insertFromDrop':\n case 'insertFromYank':\n if (data === null || data.length === 0) {\n return false;\n }\n return readOnly || this.insert(node, model, inputType, data);\n case 'insertFromPaste':\n return readOnly || (data !== null && this.insert(node, model, inputType, data));\n case 'insertLineBreak':\n case 'insertParagraph':\n if (!isMultiline(node)) {\n return false;\n }\n return readOnly || this.applyEdit(node, model, inputType, '\\n', () => model.insertText('\\n'));\n case 'deleteContentBackward':\n case 'deleteWordBackward':\n case 'deleteSoftLineBackward':\n case 'deleteHardLineBackward':\n case 'deleteContentForward':\n case 'deleteWordForward':\n case 'deleteSoftLineForward':\n case 'deleteHardLineForward':\n case 'deleteContent':\n case 'deleteByCut':\n case 'deleteByDrag': {\n if (readOnly) {\n return true;\n }\n const unit: EditUnit = /Word/.test(inputType) ? 'word' : /Line/.test(inputType) ? 'line' : 'grapheme';\n const forward = /Forward/.test(inputType);\n return this.applyEdit(node, model, inputType, null, () => {\n if (inputType === 'deleteContent' || inputType === 'deleteByCut' || inputType === 'deleteByDrag') {\n model.replaceRange(model.start, model.end, '');\n } else if (forward) {\n model.deleteForward(unit);\n } else {\n model.deleteBackward(unit);\n }\n });\n }\n case 'historyUndo':\n return readOnly || this.history(node, model, inputType, () => model.undo());\n case 'historyRedo':\n return readOnly || this.history(node, model, inputType, () => model.redo());\n default:\n return false;\n }\n }\n\n /** Inserts text at the selection of the focused editable, as typing would. */\n insertText(text: string): boolean {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return false;\n }\n return this.insert(node, editorFor(node), 'insertText', text);\n }\n\n /**\n * Replaces the focused editable's whole text, as an assistive\n * technology's \"set value\" does.\n *\n * `insertReplacementText` is the DOM's own input type for this, so an\n * `onBeforeInput` that vets edits sees the same vocabulary it sees\n * for typing, and an app that rejects the edit rejects this one too.\n * Selecting everything first is what makes it one undo step and one\n * `onInput` rather than a delete and an insert.\n */\n replaceText(text: string): boolean {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return false;\n }\n const model = editorFor(node);\n model.selectAll();\n return this.insert(node, model, 'insertReplacementText', text);\n }\n\n /** Pastes text: newlines are kept in a multiline field and become spaces in a single-line one. */\n paste(text: string): boolean {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return false;\n }\n return this.insert(node, editorFor(node), 'insertFromPaste', text);\n }\n\n private insert(node: UiNode, model: EditableTextModel, inputType: string, text: string): boolean {\n const data = isMultiline(node) ? text : text.replace(/\\r\\n|\\r|\\n/g, ' ');\n return this.applyEdit(node, model, inputType, data, () => model.insertText(data));\n }\n\n // ---------------------------------------------------------------------------\n // Composition\n // ---------------------------------------------------------------------------\n\n compositionStart(): void {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return;\n }\n const model = editorFor(node);\n model.beginComposition();\n this.compositionOpen = true;\n this.afterTextChange(node, model, false);\n }\n\n /** The composition text so far and the caret within it. */\n compositionUpdate(text: string, caret: number = text.length): void {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return;\n }\n const model = editorFor(node);\n model.updateComposition(text, caret);\n this.compositionOpen = true;\n this.afterTextChange(node, model, false);\n }\n\n /** The IME committed `text` (possibly empty: the composition was cancelled). */\n compositionEnd(text: string): void {\n const node = this.focusedEditable;\n this.compositionOpen = false;\n if (node === null || isReadOnly(node)) {\n return;\n }\n const model = editorFor(node);\n const data = isMultiline(node) ? text : text.replace(/\\r\\n|\\r|\\n/g, ' ');\n const before = model.text;\n model.commitComposition(data);\n this.afterTextChange(node, model, before !== model.text);\n }\n\n // ---------------------------------------------------------------------------\n // Pointer\n // ---------------------------------------------------------------------------\n\n /**\n * A press on an editable places the caret (Shift extends the\n * selection to it); a second press within the double-click window\n * selects the word, a third the line. Dragging afterwards extends the\n * selection from the anchor.\n */\n pointerDown(node: UiNode, x: number, y: number, modifiers: UiKeyModifiers): void {\n if (!isEditableNode(node)) {\n return;\n }\n const model = editorFor(node);\n const layout = this.layoutOf(node);\n const local = this.host.toLocal(node, x, y);\n const offset = layout.offsetAt(local.x, local.y);\n const now = this.host.now();\n const last = this.lastPress;\n // Shift+press extends the selection whatever came before it, and\n // does not count toward a double click.\n const count =\n !modifiers.shift &&\n last !== null &&\n last.node === node &&\n now - last.at <= MULTI_CLICK_MS &&\n Math.abs(last.x - x) <= MULTI_CLICK_SLOP &&\n Math.abs(last.y - y) <= MULTI_CLICK_SLOP\n ? last.count + 1\n : 1;\n this.lastPress = { node, x, y, at: now, count };\n this.dragging = node;\n\n if (modifiers.shift) {\n model.moveTo(offset, true);\n } else if (count === 2) {\n const word = wordRangeAt(model.text, offset);\n model.select(word.start, word.end);\n } else if (count >= 3) {\n model.select(lineStartAt(model.text, offset), lineEndAt(model.text, offset));\n } else {\n model.select(offset);\n }\n model.breakUndoGroup();\n this.afterSelectionChange(node, model);\n }\n\n pointerMove(node: UiNode, x: number, y: number): void {\n if (this.dragging !== node) {\n return;\n }\n const model = editorFor(node);\n const layout = this.layoutOf(node);\n const local = this.host.toLocal(node, x, y);\n const offset = layout.offsetAt(local.x, local.y);\n if (offset !== model.focus) {\n model.moveTo(offset, true);\n this.afterSelectionChange(node, model);\n }\n }\n\n pointerUp(): void {\n this.dragging = null;\n }\n\n // ---------------------------------------------------------------------------\n // State for the shell, caret blink\n // ---------------------------------------------------------------------------\n\n /** The focused editable's state for the shell to mirror, or null. */\n state(): EditingState | null {\n const node = this.focusedEditable;\n if (node === null || this.host.recordFor(node) === undefined) {\n return null;\n }\n const model = editorFor(node);\n const caret = this.layoutOf(node).caretRect();\n const visible = this.host.visibleBox(node);\n return {\n text: model.text,\n selectionStart: model.start,\n selectionEnd: model.end,\n caret: { x: visible.x + caret.x, y: visible.y + caret.y, width: 1, height: caret.height },\n multiline: isMultiline(node),\n composing: model.composing\n };\n }\n\n /**\n * When the focused caret next toggles, or undefined when nothing\n * blinks: no focused editable, a composition in progress (the caret\n * holds), or a hidden page.\n */\n nextCaretChange(now: number): number | undefined {\n const node = this.focusedEditable;\n if (node === null || !this.visible) {\n return undefined;\n }\n const model = editorFor(node);\n if (model.composing || !model.collapsed) {\n return undefined;\n }\n return nextCaretToggle(model, now);\n }\n\n /**\n * The page was hidden or shown. A hidden page stops the blink so a\n * background tab schedules no frames; showing it restarts the caret.\n */\n setVisible(visible: boolean): void {\n if (this.visible === visible) {\n return;\n }\n this.visible = visible;\n const node = this.focusedEditable;\n if (node !== null) {\n editorFor(node).blinkOrigin = this.host.now();\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n\n /** Whether the caret may be drawn at all (the page is visible). */\n get caretEnabled(): boolean {\n return this.visible;\n }\n\n // ---------------------------------------------------------------------------\n // Internals\n // ---------------------------------------------------------------------------\n\n private handleFocusChange(node: UiNode | null): void {\n const previous = this.focusedEditable;\n const next = node !== null && isEditableNode(node) ? node : null;\n if (previous === next) {\n return;\n }\n if (previous !== null) {\n const model = editorFor(previous);\n if (model.composing) {\n model.commitComposition(model.text.slice(model.composition!.start, model.composition!.end));\n }\n model.focused = false;\n this.host.markDirty(previous, DirtyFlags.Paint);\n }\n this.focusedEditable = next;\n this.compositionOpen = false;\n this.verticalGoalX = undefined;\n if (next !== null) {\n const model = editorFor(next);\n model.focused = true;\n model.blinkOrigin = this.host.now();\n this.host.markDirty(next, DirtyFlags.Paint);\n }\n }\n\n /**\n * Runs an edit unless a `beforeinput` listener cancels it, then\n * reports it. Returns true when the input was consumed either way.\n */\n private applyEdit(\n node: UiNode,\n model: EditableTextModel,\n inputType: string,\n data: string | null,\n run: () => void\n ): boolean {\n const before = new UiBeforeInputEvent(inputType, data);\n this.dispatcher.dispatch(before, node);\n if (before.defaultPrevented) {\n return true;\n }\n const text = model.text;\n run();\n this.afterTextChange(node, model, text !== model.text);\n return true;\n }\n\n private history(node: UiNode, model: EditableTextModel, inputType: string, run: () => boolean): boolean {\n const before = new UiBeforeInputEvent(inputType, null);\n this.dispatcher.dispatch(before, node);\n if (before.defaultPrevented) {\n return true;\n }\n const text = model.text;\n run();\n this.afterTextChange(node, model, text !== model.text);\n return true;\n }\n\n private afterTextChange(node: UiNode, model: EditableTextModel, changed: boolean): void {\n this.verticalGoalX = undefined;\n model.blinkOrigin = this.host.now();\n // The text is what layout measures, so a change is content and\n // layout; an unchanged text with a moved caret is paint only.\n this.host.markDirty(\n node,\n changed || this.compositionOpen ? DirtyFlags.Content | DirtyFlags.Layout : DirtyFlags.Paint\n );\n if (changed) {\n this.dispatcher.dispatch(new UiTextChangeEvent(model.text, model.start, model.end), node);\n }\n this.revealCaret(node);\n }\n\n private afterSelectionChange(node: UiNode, model: EditableTextModel, keepGoal = false): void {\n if (!keepGoal) {\n this.verticalGoalX = undefined;\n }\n model.blinkOrigin = this.host.now();\n this.host.markDirty(node, DirtyFlags.Paint);\n this.revealCaret(node);\n }\n\n /**\n * Asks the host to scroll the caret into view, when the node has been\n * laid out: the field's own text first, then any scroll container\n * around it.\n *\n * `layoutOf` places the text where it is seen, so the caret comes\n * back with the field's own scroll already taken off; the reveal is\n * asked for in the node's unscrolled coordinates, which is where the\n * offset it wants is measured from.\n */\n private revealCaret(node: UiNode): void {\n const rec = this.host.recordFor(node);\n if (rec === undefined) {\n return;\n }\n const caret = this.layoutOf(node).caretRect();\n this.host.reveal(node, {\n x: caret.x + rec.scrollX - CARET_REVEAL_PADDING,\n y: caret.y + rec.scrollY - CARET_REVEAL_PADDING,\n width: 1 + 2 * CARET_REVEAL_PADDING,\n height: caret.height + 2 * CARET_REVEAL_PADDING\n });\n }\n\n /**\n * The node's text laid out in node-local coordinates: the content box\n * starts at the padding and is shifted by the field's own scroll, as\n * it is for the renderers in the parent's space. Points from the\n * pointer and the caret box the shell is given are both in that same\n * seen space, so neither has to know the offset. Before the first\n * layout the record is empty and the lines fall at the origin, which\n * is still a valid place for a caret.\n */\n private layoutOf(node: UiNode): EditableLayout {\n const rec = this.host.recordFor(node);\n const state = resolvePaintState(node, this.paint);\n const box: LayoutBox =\n rec === undefined\n ? { x: 0, y: 0, width: 0, height: 0 }\n : {\n x: rec.paddingLeft - rec.scrollX,\n y: rec.paddingTop - rec.scrollY,\n width: Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight),\n height: Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom)\n };\n return new EditableLayout(state.editor!, box, state, this.host.measurer);\n }\n}\n\nfunction deleteInputType(unit: EditUnit, direction: -1 | 1): string {\n const side = direction < 0 ? 'Backward' : 'Forward';\n switch (unit) {\n case 'word':\n return `deleteWord${side}`;\n case 'line':\n case 'document':\n return `deleteSoftLine${side}`;\n default:\n return `deleteContent${side}`;\n }\n}\n","import type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from './LayoutTypes';\n\n/** A watched node's layout state: where it is, and how far it is scrolled. */\nexport interface NotifiedLayout {\n readonly box: LayoutBox;\n /** The container's own effective offset; zero for anything that does not scroll. */\n readonly scrollX: number;\n readonly scrollY: number;\n}\n\n/**\n * Tells listeners when a node's layout changed.\n *\n * Layout is readable at any moment — `worldBox(node)` answers \"where is\n * this\" — but nothing told a listener \"this changed on this frame\",\n * and a behaviour that follows a box (a split pane's divider, a drag\n * ghost, a `ResizeObserver` equivalent) needs exactly that.\n *\n * The cost is proportional to the number of registered listeners, not\n * to the size of the tree: a frame with none does nothing. A scroll\n * moves the world box of everything under the scroller, and reporting\n * that is correct — a thing that follows a box has to follow it when\n * the page scrolls too.\n *\n * **A container's own scroll counts as a change, even though its box\n * does not move.** That is the one case the box alone cannot describe:\n * scrolling a list moves everything inside it and leaves the list\n * itself exactly where it was, so a listener on the list heard\n * nothing. It is also the only fact an application has no other way to\n * learn — a wheel writes the offset from inside the runtime, and there\n * is no scroll event — which is why it is reported here rather than\n * through a second mechanism of its own. See `scrollPosition`.\n */\nexport class LayoutNotifier {\n private readonly listeners = new Map<UiNode, Set<(box: LayoutBox) => void>>();\n private readonly last = new Map<UiNode, NotifiedLayout>();\n\n /** Registers a listener; the returned function removes it. */\n add(node: UiNode, listener: (box: LayoutBox) => void): () => void {\n let forNode = this.listeners.get(node);\n if (forNode === undefined) {\n forNode = new Set();\n this.listeners.set(node, forNode);\n }\n forNode.add(listener);\n return () => {\n const set = this.listeners.get(node);\n if (set === undefined) {\n return;\n }\n set.delete(listener);\n if (set.size === 0) {\n this.listeners.delete(node);\n this.last.delete(node);\n }\n };\n }\n\n get size(): number {\n return this.listeners.size;\n }\n\n /** Whether anything is listening, so a frame can skip the walk. */\n isEmpty(): boolean {\n return this.listeners.size === 0;\n }\n\n /**\n * Calls the listeners of every watched node whose box or scroll\n * offset differs from what it was last told about.\n */\n notify(read: (node: UiNode) => NotifiedLayout): void {\n for (const [node, listeners] of this.listeners) {\n const current = read(node);\n const previous = this.last.get(node);\n if (previous !== undefined && sameLayout(previous, current)) {\n continue;\n }\n this.last.set(node, { box: { ...current.box }, scrollX: current.scrollX, scrollY: current.scrollY });\n for (const listener of listeners) {\n listener(current.box);\n }\n }\n }\n\n /** Drops a removed node's listeners, so nothing keeps it alive. */\n handleNodeRemoved(node: UiNode): void {\n this.listeners.delete(node);\n this.last.delete(node);\n }\n}\n\nfunction sameLayout(a: NotifiedLayout, b: NotifiedLayout): boolean {\n return a.scrollX === b.scrollX && a.scrollY === b.scrollY && sameBox(a.box, b.box);\n}\n\nfunction sameBox(a: LayoutBox, b: LayoutBox): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n","import { isUiModifier, type UiModifier } from './UiModifier';\n\n/**\n * A set of modifiers named once and attached as one.\n *\n * The analogue of a SwiftUI `ViewModifier` or a Compose `Modifier`\n * chain, and the answer to the seventy-two `modifiers={[INTERACTION,\n * ...RING]}` sites across the two applications. Behaviour, not paint:\n * a bundle carries modifiers, and the properties an element is drawn\n * with stay on the element.\n *\n * const ROW = bundle(ROW_INTERACTION, RING);\n * <row modifiers={ROW}>…</row>\n *\n * There are two reasons to name it rather than to write the array out.\n * The first is the ordinary one: the set has a meaning, and the\n * meaning is worth a name. The second is that the array literal is not\n * free. A `modifiers` list is read on every render of the element's\n * component, and `[INTERACTION, ...RING]` allocates a fresh array and\n * copies the ring into it each time; a bundle is built once at module\n * level and is the same frozen array on every render, which is what\n * `sameArgs` and the reconciler are comparing against.\n *\n * Nesting is flattened, so a bundle composes with another bundle the\n * way a `Modifier` chain composes: `bundle(BASE, focusRing())` and\n * `bundle(BASE, OTHER_BUNDLE)` both give one flat list. Order is kept,\n * because the override cascade resolves a conflict in favour of\n * whichever modifier is later.\n */\nexport type UiModifierBundle = readonly UiModifier[];\n\n/**\n * Names a set of modifiers. Call it at module level, once.\n *\n * The result is frozen: a bundle is a value, and a list that some\n * caller pushed onto would change what every element carrying it does,\n * on the next render and not on this one, which is the kind of bug\n * that takes an afternoon.\n */\nexport function bundle(...parts: readonly (UiModifier | UiModifierBundle)[]): UiModifierBundle {\n const flat: UiModifier[] = [];\n for (const part of parts) {\n if (isUiModifier(part)) {\n flat.push(part);\n continue;\n }\n for (const nested of part) {\n flat.push(nested);\n }\n }\n return Object.freeze(flat);\n}\n\n/** An empty bundle, for a branch that adds nothing. One shared value. */\nexport const noModifiers: UiModifierBundle = Object.freeze([]) as UiModifierBundle;\n","import { isObservable, type Observable, type Subscription } from 'rxjs';\n\nimport type { DecorationShape } from '../rendering/Decorations';\nimport type { UiColorValue } from '../properties/UiPropertyValues';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/** What `decorated` takes: a list, or one that changes over time. */\nexport type Decorations = readonly DecorationShape[] | Observable<readonly DecorationShape[]>;\n\n/**\n * Shapes on a node, for as long as the modifier is attached.\n *\n * The plain form of `host.decorate`: everything else in this file is a\n * behaviour that decides *when* to draw. Useful on its own for a\n * static mark — a drop target's outline, a fixture the renderer parity\n * check can diff — and it is the smallest thing that exercises the\n * decoration path end to end.\n *\n * The argument is compared by identity, as every modifier's arguments\n * are, so hoist the array rather than building it in a render body.\n *\n * **An Observable of shapes is also accepted**, and is the only way to\n * draw something whose shape changes while it is on screen. A\n * modifier list is static per element, so a component that recomputed\n * its shapes could not hand them over; it subscribes here instead, and\n * the subscription is released on detach. What this buys is a picture\n * made of many rectangles that would otherwise have to be many nodes:\n * Segue's waveform is four hundred columns on one node, redrawn as the\n * track's peaks arrive, with nothing to lay out and nothing to hit\n * test. Hoist the Observable for the same reason you would hoist the\n * array.\n */\nexport const decorated = defineModifier<Decorations>({\n name: 'decorated',\n attach(host, shapes) {\n draw(host, shapes);\n },\n update(host, shapes) {\n draw(host, shapes);\n }\n});\n\n/**\n * The live subscription per host, so a second `update` replaces the\n * first rather than drawing from both.\n *\n * `host.own` releases on detach, which is right for the last one and\n * not enough for the one before it. Keyed by the host, which is one\n * per node per modifier, so two decorations on the same node keep\n * their own.\n */\nconst live = new WeakMap<UiModifierHost, Subscription>();\n\nfunction draw(host: UiModifierHost, shapes: Decorations): void {\n live.get(host)?.unsubscribe();\n live.delete(host);\n if (isObservable(shapes)) {\n const subscription = shapes.subscribe(next => host.decorate(next));\n live.set(host, subscription);\n host.own(subscription);\n return;\n }\n host.decorate(shapes);\n}\n\n/**\n * A ring outside whatever holds keyboard focus.\n *\n * Visible focus is an accessibility requirement and, until this\n * existed, the library had no way to meet it: a control could only\n * bind its own `borderColor` to a focus observable, which recolours\n * the control rather than marking it, and which every one of\n * twenty-odd components had to repeat.\n *\n * Two things make this a decoration rather than an overlay, and they\n * are the reasons decorations exist at all:\n *\n * - **It is clipped with the node.** A ring on a row scrolled half out\n * of a `ScrollView` is cut off at the same edge the row is, because\n * it is painted inside the node's own paint pass under its\n * ancestors' clips. An overlay drawn over the finished scene would\n * float over the scroller's edge.\n * - **It is outside the node's own box.** The ring sits `offset`\n * pixels clear of the control, so it never covers the control's\n * border or its content, which is exactly what a bound\n * `borderColor` could not avoid doing.\n *\n * The colour names a palette entry and is resolved at paint against\n * whatever theme the node inherits, so a ring inside a dark card is\n * the dark palette's without this modifier reading the environment.\n *\n * The ring is `:focus-visible`, not `:focus`: `attach` draws it only\n * while the host reports the focus as visible, so a control a pointer\n * press focused stays unmarked until a key is pressed. A control that\n * also recoloured its border on focus would say the same thing twice,\n * and say it for the wrong focus, which is why no component does.\n */\nexport interface FocusRingOptions {\n /** A palette name or a literal colour. Defaults to the `focusRing` token. */\n readonly color?: UiColorValue;\n /** Thickness of the band. */\n readonly width?: number;\n /** Clear space between the node's box and the ring. */\n readonly offset?: number;\n /**\n * Ring radius. Omitted means the node's own corner radius grown by\n * the ring's distance from it, so the ring is concentric with the\n * control without the caller knowing its radius.\n */\n readonly radius?: number;\n /** Paints the ring over the node's content instead of under it. */\n readonly after?: 'children';\n}\n\nconst DEFAULT_WIDTH = 2;\nconst DEFAULT_OFFSET = 2;\n\n/** The ring as a single stroke, placed by `decorationRect`. */\nfunction ringShapes(options: FocusRingOptions): readonly DecorationShape[] {\n const width = options.width ?? DEFAULT_WIDTH;\n const offset = options.offset ?? DEFAULT_OFFSET;\n return [\n {\n kind: 'stroke',\n color: options.color ?? 'focusRing',\n lineWidth: width,\n // The band lies inside the outset box, so an outset of\n // offset + width puts it between `offset` and `offset + width`\n // pixels outside the node.\n outset: offset + width,\n radius: options.radius,\n after: options.after\n }\n ];\n}\n\n/**\n * No `update`: the shapes are computed in `attach` from the options,\n * and a change of options is a detach followed by an attach — which is\n * the honest behaviour for a kind that cannot describe a change, and\n * costs nothing here because the options of a focus ring do not\n * change while it is attached.\n */\nconst kind = defineModifier<FocusRingOptions>({\n name: 'focusRing',\n attach(host, options) {\n const shapes = ringShapes(options);\n // Drawn for focus the keyboard can see, not for every focus: a mouse\n // press focuses the control it lands on, and a ring there would mark\n // something nobody is about to drive with the keys. The host reports\n // the change when a key makes that focus visible, so the ring\n // appears then. This is `:focus-visible`, done where the focus is.\n const sync = (): void => {\n host.decorate(host.isFocused() && host.isFocusVisible() ? shapes : null);\n };\n // A node may already hold focus when its ring attaches: a dialog\n // that auto-focused its first field re-renders, and the modifier\n // that lands on the focused node would otherwise wait for a focus\n // change that has already happened.\n sync();\n host.onFocusChange(sync);\n }\n});\n\nconst DEFAULT_RING: UiModifier<FocusRingOptions> = kind(Object.freeze({}));\n\n/**\n * The ring, with the theme's own colour and spacing.\n *\n * Called with no options it returns one shared value, which `sameArgs`\n * matches without walking anything. Options are plain data and are\n * compared by what they hold, so a ring built inline in a render\n * survives it; hoisting saves the allocation and the walk, not the\n * attachment.\n */\nexport function focusRing(options?: FocusRingOptions): UiModifier<FocusRingOptions> {\n return options === undefined ? DEFAULT_RING : kind(options);\n}\n","import { defineModifier, type UiModifier } from './UiModifier';\n\n/**\n * Takes focus the first time the node is laid out.\n *\n * The one a dialog needs: an overlay that opens with the caret already\n * in its first field, without the component that opened it reaching\n * for a `ref` and a focus store.\n *\n * **Why the first layout and not the attach.** A modifier attaches\n * while its node is still being built, before the builder inserts it\n * into its parent, so at attach time the node has no parent and is not\n * under the focus manager's traversal root. `UiFocusManager.focus`\n * would accept it anyway (it asks whether the node is focusable, not\n * where it is), and the result would be a focused node that Tab cannot\n * reach and that no ancestor sees the focus event from. The first\n * layout is the first moment the node is demonstrably on screen, and\n * it is the same frame in every case that matters, because a node\n * mounted into a live tree is laid out on the frame that mounted it.\n *\n * It fires once. A node that is autofocused, loses focus to something\n * the person clicked, and is then laid out again does not steal the\n * focus back; that would make an autofocus a focus trap.\n *\n * A node that cannot take focus is left alone and no error is raised:\n * `focus()` refuses a node that is inert, not focusable, or outside an\n * active focus scope, which is exactly the set of cases where an\n * application would not want the focus moved either.\n */\nconst kind = defineModifier<void>({\n name: 'autoFocus',\n attach(host) {\n let done = false;\n host.onLayout(() => {\n if (done) {\n return;\n }\n done = true;\n host.focus();\n });\n }\n});\n\n/**\n * One shared instance, so the arguments keep their identity across\n * renders and the modifier is never detached and re-attached (which\n * would autofocus a second time).\n */\nconst AUTO_FOCUS: UiModifier<void> = kind(undefined);\n\n/** Takes focus the first time the node is laid out. */\nexport function autoFocus(): UiModifier<void> {\n return AUTO_FOCUS;\n}\n","import type { UiNode } from '../graph/UiNode';\nimport { UiEventType, type UiInputEvent } from '../input/UiInputEvent';\nimport { defineModifier, type UiModifier } from './UiModifier';\n\nexport interface ClickOutsideOptions {\n /** Called when a press lands anywhere but inside the node. */\n readonly onOutside: (event: UiInputEvent) => void;\n /**\n * Nodes that count as inside even though they are not in the\n * subtree. The one that always needs this is the control that opened\n * the thing: without it, pressing the button that opened a popover\n * closes it and reopens it in one press.\n */\n readonly except?: () => readonly (UiNode | null | undefined)[];\n /**\n * Whether a wheel outside the node counts too. Default true: a page\n * scrolled out from under a popover leaves it hanging beside nothing.\n *\n * It is read at each event rather than at attach, so a caller may\n * flip it between renders.\n */\n readonly wheel?: boolean;\n}\n\n/**\n * Calls back when a press lands outside the node.\n *\n * The listener is at the root, in the capture phase, because the event\n * this cares about is the one that never arrives: a press somewhere\n * else is not observable from the node it missed. Capture also means\n * it is heard before the node that was pressed acts on it, so a\n * popover closes on the same press that activates whatever is beneath\n * it, rather than a frame later.\n *\n * It does not close anything itself, and it is not a backdrop. A\n * backdrop is an element that swallows the press (`OverlayEntry`'s\n * `dismissOnOutsidePress` is one); this hears the press and lets it\n * through, which is the behaviour a menu wants when the next thing the\n * person does is press a button on the page.\n */\nconst kind = defineModifier<ClickOutsideOptions>({\n name: 'clickOutside',\n attach(host, options) {\n let current = options;\n handlers.set(host, next => (current = next));\n const outside = (event: UiInputEvent): void => {\n const target = event.target;\n if (target === null || contains(host.node, target)) {\n return;\n }\n if (event.type === UiEventType.Wheel && current.wheel === false) {\n return;\n }\n for (const node of current.except?.() ?? []) {\n if (node !== null && node !== undefined && contains(node, target)) {\n return;\n }\n }\n current.onOutside(event);\n };\n host.onRoot(UiEventType.PointerDown, outside);\n host.onRoot(UiEventType.Wheel, outside);\n },\n update(host, options) {\n // The listeners close over the latest options rather than being\n // re-registered, so a handler that changes every render does not\n // cost a pair of root listeners each time.\n handlers.get(host)?.(options);\n }\n});\n\nconst handlers = new WeakMap<object, (options: ClickOutsideOptions) => void>();\n\n/** Calls back when a press lands outside the node. */\nexport function clickOutside(options: ClickOutsideOptions): UiModifier<ClickOutsideOptions> {\n return kind(options);\n}\n\nfunction contains(ancestor: UiNode, node: UiNode): boolean {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current === ancestor) {\n return true;\n }\n }\n return false;\n}\n","import type { Subject } from 'rxjs';\n\nimport { UiEventType, type UiGestureEvent, type UiPointerEvent } from '../input/UiInputEvent';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/** How far the node has been moved from where the drag picked it up. */\nexport interface DragOffset {\n readonly x: number;\n readonly y: number;\n /**\n * How fast it was travelling when it was let go, in pixels per\n * second, along each axis.\n *\n * Zero everywhere but the offset handed to `onEnd`, because there is\n * no honest speed to report for a position half way through a drag\n * that the next move will replace. The unit is the one\n * `UiSpringOptions.velocity` takes, which is the whole reason it is\n * here: a spring given no velocity starts from rest, so a card\n * thrown across the screen used to stop dead the instant the finger\n * left it, and there was no way for the application to do better\n * because the drag never told it how fast the card was going.\n */\n readonly velocityX?: number;\n readonly velocityY?: number;\n}\n\nexport interface DraggableOptions {\n /** Which axes the node may move on. Default `'both'`. */\n readonly axis?: 'x' | 'y' | 'both';\n /**\n * Whether the drag begins on a press and a move, or only after a\n * long press. Default `'press'`.\n *\n * This modifier was first written as the `DragStart` gesture, and\n * `DragStart` is the long-press one: the recognizer resolves a press\n * to a Pan when it moves before the hold time and to a Drag only\n * after a LongPress. C5 found the same thing from the other side,\n * when a split pane's divider listened for `DragMove` and did not\n * move. A card the pointer picks up immediately is a Pan, so that is\n * the default here, and the long-press variety is an option rather\n * than the only behaviour.\n */\n readonly start?: 'press' | 'longPress';\n /** Every position while the drag runs, as an offset from the start. */\n readonly offset?: Subject<DragOffset>;\n readonly onStart?: () => void;\n readonly onEnd?: (offset: DragOffset) => void;\n /**\n * Properties to write while the drag runs, in the shape\n * `interactive` uses for hover and press: `{ opacity: 0.8, zIndex: 10 }`\n * puts the node over its siblings while it is carried.\n *\n * `zIndex` reaches no further than that, because it reorders the\n * children of one parent. A node carried out of its container and\n * over another one, a chip dragged from one tray into the next, is\n * still painted inside the container it left, and the container it\n * is over paints after that one and covers it. `{ lift: true }` is\n * the property for that case: the node is drawn after everything\n * else up to the nearest `liftBoundary`, and outside its ancestors'\n * clips, for as long as the drag runs.\n */\n readonly dragging?: Readonly<Record<string, unknown>>;\n /**\n * Whether the node stays where it was dropped. Default `true`.\n *\n * False is the right choice when the application moves the thing\n * itself: a list that reorders on drop wants the item back in the\n * flow at its new index, not held one row above it by a translation\n * nobody owns any more.\n */\n readonly keepOffset?: boolean;\n}\n\n/**\n * Moves a node with the pointer.\n *\n * It writes the movement as a translation on `transform`, through the\n * override cascade, so nothing about the node's layout changes: the\n * box stays where it was, its neighbours do not shuffle, and dropping\n * a node whose offset is released puts back exactly the transform the\n * element declared, including no transform at all. The declared\n * transform is read once at attach and composed with, so a node that\n * is already rotated goes on rotating while it is dragged.\n *\n * The gesture events are stopped from propagating while a drag is\n * running. `UiTouchScroller` listens for pans at the root, so without\n * that a card dragged inside a scroll view would move and scroll its\n * container at the same time; `SplitPane` stops them for the same\n * reason.\n *\n * Nothing here changes the cursor. That is a message to the shell\n *, and a node's `cursor` property already\n * sends it, so an application says `cursor: 'grab'` on the element and\n * this modifier stays inside the render thread.\n */\nconst kind = defineModifier<DraggableOptions>({\n name: 'draggable',\n attach(host, options) {\n controllers.set(host, new Draggable(host, options).attach());\n },\n update(host, options) {\n // In place rather than a detach and a re-attach, so options that\n // change while a card is in the air do not drop it.\n controllers.get(host)?.setOptions(options);\n }\n});\n\nconst controllers = new WeakMap<UiModifierHost, Draggable>();\n\n/**\n * Moves a node with the pointer.\n *\n * Hoist the options object: arguments are compared by identity, so a\n * fresh literal every render is a fresh set of arguments.\n */\nexport function draggable(options: DraggableOptions = EMPTY_OPTIONS): UiModifier<DraggableOptions> {\n return kind(options);\n}\n\nconst EMPTY_OPTIONS: DraggableOptions = Object.freeze({});\n\nclass Draggable {\n /** Where the pointer was when the gesture started. */\n private originX = 0;\n private originY = 0;\n /** The offset in force, which is what a later drag adds to. */\n private offsetX = 0;\n private offsetY = 0;\n /** The offset this gesture started from, so a second drag continues. */\n private startX = 0;\n private startY = 0;\n private dragging = false;\n\n /** What the element declared, which the translation is added to. */\n private basePivotX = 0;\n private basePivotY = 0;\n private baseScaleX = 1;\n private baseScaleY = 1;\n private baseRotation = 0;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: DraggableOptions\n ) {}\n\n attach(): this {\n const declared = this.host.get<Record<string, unknown> | null | undefined>('transform');\n if (declared !== undefined && declared !== null) {\n this.basePivotX = numberOr(declared.x, 0);\n this.basePivotY = numberOr(declared.y, 0);\n this.baseScaleX = numberOr(declared.scaleX, 1);\n this.baseScaleY = numberOr(declared.scaleY, 1);\n this.baseRotation = numberOr(declared.rotation, 0);\n }\n const longPress = this.options.start === 'longPress';\n // Every gesture event is a pointer event; the listener signature is\n // the base class because a listener may be registered for any type.\n this.host.on(longPress ? UiEventType.DragStart : UiEventType.PanStart, event =>\n this.begin(event as UiPointerEvent)\n );\n this.host.on(longPress ? UiEventType.DragMove : UiEventType.PanMove, event => this.move(event as UiPointerEvent));\n this.host.on(longPress ? UiEventType.DragEnd : UiEventType.PanEnd, event => this.end(event as UiPointerEvent));\n return this;\n }\n\n setOptions(options: DraggableOptions): void {\n // `start` is read when the listeners are registered, so changing it\n // mid-life would leave the modifier listening for the other\n // gesture. The kind has no `update` path for that; say so rather\n // than half-applying it.\n if ((options.start ?? 'press') !== (this.options.start ?? 'press')) {\n console.warn(\n `draggable on node '${this.host.node.id}' changed 'start' after it attached; the gesture it listens for is fixed at attach.`\n );\n }\n this.options = options;\n }\n\n private begin(event: UiPointerEvent): void {\n event.stopPropagation();\n this.dragging = true;\n this.originX = event.x;\n this.originY = event.y;\n this.startX = this.offsetX;\n this.startY = this.offsetY;\n for (const [property, value] of Object.entries(this.options.dragging ?? {})) {\n this.host.set(property, value);\n }\n this.options.onStart?.();\n }\n\n private move(event: UiPointerEvent): void {\n if (!this.dragging) {\n return;\n }\n event.stopPropagation();\n const axis = this.options.axis ?? 'both';\n const x = axis === 'y' ? this.startX : this.startX + (event.x - this.originX);\n const y = axis === 'x' ? this.startY : this.startY + (event.y - this.originY);\n this.setOffset(x, y);\n this.options.offset?.next({ x, y });\n }\n\n private end(event: UiPointerEvent): void {\n if (!this.dragging) {\n return;\n }\n event.stopPropagation();\n this.dragging = false;\n for (const property of Object.keys(this.options.dragging ?? {})) {\n this.host.clear(property);\n }\n const dropped: DragOffset = {\n x: this.offsetX,\n y: this.offsetY,\n velocityX: velocityOf(event, 'x'),\n velocityY: velocityOf(event, 'y')\n };\n if (this.options.keepOffset === false) {\n this.setOffset(0, 0);\n this.options.offset?.next({ x: 0, y: 0 });\n }\n this.options.onEnd?.(dropped);\n }\n\n /**\n * Writes the offset as the transform's translation, or takes it away.\n *\n * At rest the override is dropped rather than written as an identity\n * translation, so a node that was dragged and put back is\n * indistinguishable from one that never moved: no transform in its\n * paint state, and no matrix to multiply per frame.\n */\n private setOffset(x: number, y: number): void {\n this.offsetX = x;\n this.offsetY = y;\n if (x === 0 && y === 0) {\n this.host.clear('transform');\n return;\n }\n this.host.set('transform', {\n x: this.basePivotX,\n y: this.basePivotY,\n translateX: x,\n translateY: y,\n scaleX: this.baseScaleX,\n scaleY: this.baseScaleY,\n rotation: this.baseRotation\n });\n }\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\n/**\n * The release speed the recognizer measured, or none.\n *\n * `PanEnd` and `DragEnd` are `UiGestureEvent`s and carry it. The check\n * is a property test rather than an `instanceof` because a spec that\n * dispatches a plain `UiPointerEvent` at the modifier is a legitimate\n * way to drive it, and reporting no speed is the right answer there.\n */\nfunction velocityOf(event: UiPointerEvent, axis: 'x' | 'y'): number {\n const value = (event as Partial<UiGestureEvent>)[axis === 'x' ? 'velocityX' : 'velocityY'];\n return typeof value === 'number' && Number.isFinite(value) ? value : 0;\n}\n","import type { UiNode } from '../graph/UiNode';\nimport {\n dragSessionFor,\n type UiDragPayload,\n type UiDragResult,\n type UiDragSession,\n type UiDragState,\n type UiDropEffect,\n type UiDropZone\n} from '../input/UiDragSession';\nimport { UiEventType, type UiGestureEvent, type UiPointerEvent } from '../input/UiInputEvent';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/**\n * Keeps a zone registered with the session for the graph it is in.\n *\n * A modifier attaches before its node is linked into the tree: the\n * builder writes an element's props, attaches its modifiers and builds\n * its children, and only then makes the edge to its parent. A zone\n * that resolved its session once at attach would therefore register\n * with a session keyed on itself, and would be invisible to every drag\n * that started anywhere else. So the session is re-resolved whenever\n * there is a reason to think the answer changed: at attach, at the\n * node's first layout, and when a drag begins on the node. The\n * registration moves with it.\n */\nclass ZoneRegistration {\n private session: UiDragSession | null = null;\n private release: (() => void) | null = null;\n\n constructor(private readonly zone: UiDropZone) {}\n\n sync(): UiDragSession {\n const next = dragSessionFor(this.zone.node);\n if (next !== this.session) {\n this.release?.();\n this.release = next.addZone(this.zone);\n this.session = next;\n }\n return next;\n }\n\n dispose(): void {\n this.release?.();\n this.release = null;\n this.session = null;\n }\n}\n\n/** How fast a drag near a container's edge scrolls it, and from how far. */\nexport interface AutoScrollOptions {\n /** How close to the edge, in pixels, the pointer has to come. Default 48. */\n readonly edge?: number;\n /** Pixels per second at the very edge. Default 900. */\n readonly speed?: number;\n}\n\nexport interface DropTargetOptions {\n /**\n * Which payloads this zone will take: a type, several types, or a\n * predicate for a zone whose answer depends on the payload's data.\n */\n readonly accepts: string | readonly string[] | ((payload: UiDragPayload) => boolean);\n /**\n * The drag was let go here. What it did, so the source can say\n * whether the thing moved or was copied; returning nothing means\n * `'move'`, which is what almost every drop is.\n */\n readonly onDrop: (payload: UiDragPayload, at: { readonly x: number; readonly y: number }) => UiDropEffect | void;\n /** A payload this zone accepts arrived over it. */\n readonly onEnter?: (payload: UiDragPayload) => void;\n /** It moved while over the zone. */\n readonly onOver?: (payload: UiDragPayload, at: { readonly x: number; readonly y: number }) => void;\n /** It left, or the drag ended elsewhere. */\n readonly onLeave?: () => void;\n /**\n * Properties to write while an accepted payload is over the zone, in\n * the shape `interactive` uses for hover and press:\n * `{ borderColor: 'accent', backgroundColor: 'surfaceRaised' }`.\n *\n * A drop zone that does not light up is a drop zone nobody finds. It\n * is here rather than left to the application because the state is\n * the modifier's and an application cannot see it without keeping a\n * copy.\n */\n readonly over?: Readonly<Record<string, unknown>>;\n /**\n * Whether a drag held near this container's edge scrolls it.\n *\n * Only meaningful on a scroll container, and the reason a long list\n * is reorderable at all: without it the only rows you can reach are\n * the ones already on screen. Off by default, since a zone that is\n * not a scroller has nothing to scroll.\n */\n readonly autoScroll?: boolean | AutoScrollOptions;\n}\n\nexport interface DragSourceOptions {\n /**\n * What is being carried. A function when the payload depends on\n * state that changes between renders, which is most of them.\n */\n readonly payload: UiDragPayload | (() => UiDragPayload);\n /**\n * Whether the drag opens on a press and a move, or only after a long\n * press. Default `'press'`, matching `draggable`, and for the same\n * reason: the recognizer resolves a press that moves immediately to a\n * Pan and only a held press to a Drag.\n */\n readonly start?: 'press' | 'longPress';\n /** Properties to write while the drag runs. */\n readonly dragging?: Readonly<Record<string, unknown>>;\n /**\n * The drag ended. `result` is null when it was let go over nothing\n * that would take it, and `velocity` is the speed it was travelling\n * at, in pixels per second, ready for a spring that carries the node\n * back.\n */\n readonly onEnd?: (result: UiDragResult | null, velocity: { readonly x: number; readonly y: number }) => void;\n}\n\n/**\n * Somewhere a drag can be put down.\n *\n * `draggable` moves a node's transform and stops, and for a year that\n * was the whole of dragging: every application that needed a drag to\n * *mean* something wrote its own hit testing over its own coordinates.\n * Segue's queue did, over a row height constant and a rounded division.\n *\n * The zone registers itself with the session for its graph and answers\n * three questions: where am I, would I take this, and what did the drop\n * do. Everything else is the session's, so two applications cannot\n * disagree about it: which of several overlapping zones wins, when enter\n * and leave fire, and what a drop reports back to the source.\n *\n * The box it reports is `layoutBox()`, which is where the node is\n * *seen*: a row scrolled half off the top of a list is half a drop\n * target, exactly as it looks.\n */\nconst dropTargetKind = defineModifier<DropTargetOptions>({\n name: 'dropTarget',\n attach(host, options) {\n zones.set(host, new DropTarget(host, options).attach());\n },\n update(host, options) {\n zones.get(host)?.setOptions(options);\n }\n});\n\nconst zones = new WeakMap<UiModifierHost, DropTarget>();\n\nexport function dropTarget(options: DropTargetOptions): UiModifier<DropTargetOptions> {\n return dropTargetKind(options);\n}\n\nclass DropTarget implements UiDropZone {\n private readonly registration = new ZoneRegistration(this);\n private scrolling: ReturnType<typeof setInterval> | null = null;\n private toward = 0;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: DropTargetOptions\n ) {}\n\n get node(): UiNode {\n return this.host.node;\n }\n\n attach(): this {\n this.registration.sync();\n this.host.onLayout(() => this.registration.sync());\n this.host.own(() => {\n this.stopScrolling();\n this.registration.dispose();\n });\n return this;\n }\n\n setOptions(options: DropTargetOptions): void {\n this.options = options;\n }\n\n boxOf() {\n return this.host.layoutBox();\n }\n\n accepts(payload: UiDragPayload): boolean {\n const rule = this.options.accepts;\n if (typeof rule === 'function') {\n return rule(payload);\n }\n if (typeof rule === 'string') {\n return rule === payload.type;\n }\n return rule.includes(payload.type);\n }\n\n enter(state: UiDragState): void {\n for (const [property, value] of Object.entries(this.options.over ?? {})) {\n this.host.set(property, value);\n }\n this.options.onEnter?.(state.payload);\n this.over(state);\n }\n\n over(state: UiDragState): void {\n this.options.onOver?.(state.payload, { x: state.x, y: state.y });\n this.updateAutoScroll(state);\n }\n\n leave(): void {\n for (const property of Object.keys(this.options.over ?? {})) {\n this.host.clear(property);\n }\n this.stopScrolling();\n this.options.onLeave?.();\n }\n\n drop(state: UiDragState): UiDropEffect {\n return this.options.onDrop(state.payload, { x: state.x, y: state.y }) ?? 'move';\n }\n\n // ---------------------------------------------------------------------\n // Auto-scroll\n // ---------------------------------------------------------------------\n\n /**\n * Scrolls the container while the drag is held near one of its edges.\n *\n * The rate is driven by a timer rather than by pointer movement, and\n * it has to be: the gesture a person makes is to hold the card still\n * at the top of the list and wait, which produces no pointer events\n * at all. The interval is cleared when the drag leaves, when it ends,\n * and when the modifier detaches, so nothing survives the node.\n */\n private updateAutoScroll(state: UiDragState): void {\n const settings = this.options.autoScroll;\n if (settings === undefined || settings === false) {\n return;\n }\n const box = this.host.layoutBox();\n const offset = this.host.scrollOffset();\n if (box === null || offset === null) {\n return;\n }\n const edge = (settings === true ? undefined : settings.edge) ?? 48;\n const speed = (settings === true ? undefined : settings.speed) ?? 900;\n const fromTop = state.y - box.y;\n const fromBottom = box.y + box.height - state.y;\n let fraction = 0;\n if (fromTop < edge) {\n fraction = -(edge - Math.max(fromTop, 0)) / edge;\n } else if (fromBottom < edge) {\n fraction = (edge - Math.max(fromBottom, 0)) / edge;\n }\n this.toward = fraction * speed;\n if (this.toward === 0) {\n this.stopScrolling();\n return;\n }\n if (this.scrolling === null) {\n this.scrolling = setInterval(() => this.stepScroll(), SCROLL_TICK_MS);\n }\n }\n\n private stepScroll(): void {\n const offset = this.host.scrollOffset();\n if (offset === null || this.toward === 0) {\n this.stopScrolling();\n return;\n }\n const next = Math.max(0, offset.y + (this.toward * SCROLL_TICK_MS) / 1000);\n if (next === offset.y) {\n return;\n }\n // The property, not an override: the runtime scrolls a container by\n // writing `scrollY` and the engine clamps it to the content on the\n // next pass, so writing it here is the same scroll a wheel makes.\n this.host.node.setProperty('scrollY', next);\n this.host.requestFrame();\n }\n\n private stopScrolling(): void {\n if (this.scrolling !== null) {\n clearInterval(this.scrolling);\n this.scrolling = null;\n }\n this.toward = 0;\n }\n}\n\n/** How often an auto-scroll advances. One frame at 60 Hz. */\nconst SCROLL_TICK_MS = 16;\n\n/**\n * Opens a drag carrying a payload, and reports where it was put down.\n *\n * It moves nothing. `draggable` already moves a node with the pointer\n * and does it well, so this is the other half rather than a second copy\n * of the first: put both on the element and the card follows the\n * pointer and knows what it is. Splitting them is also what lets a drag\n * source be something that does *not* move, like a swatch that stays\n * where it is while a colour is carried off it.\n */\nconst dragSourceKind = defineModifier<DragSourceOptions>({\n name: 'dragSource',\n attach(host, options) {\n sources.set(host, new DragSource(host, options).attach());\n },\n update(host, options) {\n sources.get(host)?.setOptions(options);\n }\n});\n\nconst sources = new WeakMap<UiModifierHost, DragSource>();\n\nexport function dragSource(options: DragSourceOptions): UiModifier<DragSourceOptions> {\n return dragSourceKind(options);\n}\n\nclass DragSource {\n private session: UiDragSession | null = null;\n private dragging = false;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: DragSourceOptions\n ) {}\n\n attach(): this {\n const longPress = this.options.start === 'longPress';\n this.host.on(longPress ? UiEventType.DragStart : UiEventType.PanStart, event =>\n this.begin(event as UiPointerEvent)\n );\n this.host.on(longPress ? UiEventType.DragMove : UiEventType.PanMove, event => this.move(event as UiPointerEvent));\n this.host.on(longPress ? UiEventType.DragEnd : UiEventType.PanEnd, event => this.end(event as UiGestureEvent));\n this.host.own(() => {\n if (this.dragging) {\n this.session?.cancel();\n }\n });\n return this;\n }\n\n setOptions(options: DragSourceOptions): void {\n this.options = options;\n }\n\n private begin(event: UiPointerEvent): void {\n const payload = typeof this.options.payload === 'function' ? this.options.payload() : this.options.payload;\n this.dragging = true;\n // Resolved here rather than at attach: a modifier attaches before\n // its node is linked into the tree, and by the time a gesture\n // reaches it the node is where it belongs.\n this.session = dragSessionFor(this.host.node);\n this.session.begin(payload, event.x, event.y, this.host.node);\n for (const [property, value] of Object.entries(this.options.dragging ?? {})) {\n this.host.set(property, value);\n }\n }\n\n private move(event: UiPointerEvent): void {\n if (!this.dragging) {\n return;\n }\n this.session?.move(event.x, event.y);\n }\n\n private end(event: UiGestureEvent): void {\n if (!this.dragging) {\n return;\n }\n this.dragging = false;\n for (const property of Object.keys(this.options.dragging ?? {})) {\n this.host.clear(property);\n }\n const result = this.session?.end() ?? null;\n this.options.onEnd?.(result, { x: event.velocityX ?? 0, y: event.velocityY ?? 0 });\n }\n}\n\nexport interface ReorderableOptions {\n /**\n * Which list this row belongs to. Two lists on one page have\n * different names, so a row cannot be dropped into a list that does\n * not want it.\n */\n readonly list: string;\n /** Where this row currently sits. */\n readonly index: number;\n /**\n * Move the row, now.\n *\n * Called as the drag crosses each row rather than once at the end, so\n * what is on screen is always the real order rather than a preview of\n * one: a drag abandoned half way leaves something coherent behind,\n * and there is no separate \"committed\" and \"shown\" order to keep in\n * step.\n */\n readonly onMove: (from: number, to: number) => void;\n /** Which way the row is carried. Default `'y'`. */\n readonly axis?: 'x' | 'y' | 'both';\n /** Whether the row is picked up on a press or on a long press. Default `'press'`. */\n readonly start?: 'press' | 'longPress';\n /** Properties to write on the row while it is being carried. */\n readonly dragging?: Readonly<Record<string, unknown>>;\n /** Told when this row is picked up and put down, for a list that wants to know. */\n readonly onDragChange?: (dragging: boolean) => void;\n}\n\n/**\n * A row that can be dragged into a different place in its list.\n *\n * One modifier on the row rather than a component around the list,\n * because a reorderable list is not a widget: it is a list whose rows\n * happen to be draggable, and every application's rows look different.\n *\n * It is a drag source and a drop target on the same node, over the same\n * session, which is what makes the whole thing about twenty lines: the\n * session already answers \"which row is the pointer over\", and a row\n * that hears an accepted payload enter it knows both indices and can\n * call `onMove` immediately.\n *\n * ## Why the row is placed from its layout box\n *\n * The obvious way to carry a row is to translate it by how far the\n * pointer has moved since the press. That works until the first\n * reorder, at which point the row has *also* been moved by the layout,\n * and the two movements add up: the row runs away from the finger at\n * twice the speed. Segue's hand-written version fixed it by adding a\n * row height back onto the origin each time it crossed one, which is\n * correct only for rows of a fixed height that it had to name a\n * constant for.\n *\n * This holds the grab point instead: where in the row the person took\n * hold of it. The translation is then always `pointer - grab -\n * layoutBox().y`, recomputed on every move *and on every layout*, so a\n * reorder underneath the row corrects itself on the frame it happens\n * and rows of different heights need no arithmetic at all.\n */\nconst reorderableKind = defineModifier<ReorderableOptions>({\n name: 'reorderable',\n attach(host, options) {\n rows.set(host, new Reorderable(host, options).attach());\n },\n update(host, options) {\n rows.get(host)?.setOptions(options);\n }\n});\n\nconst rows = new WeakMap<UiModifierHost, Reorderable>();\n\nexport function reorderable(options: ReorderableOptions): UiModifier<ReorderableOptions> {\n return reorderableKind(options);\n}\n\n/** What a reorder drag carries: the list it came from, and where it is now. */\ninterface ReorderData {\n list: string;\n index: number;\n /**\n * Whether a move has been committed that the layout has not caught up\n * with yet.\n *\n * A crossing is decided by hit-testing the rows' boxes, and a move\n * changes every box below it, but not until the frame after. Two\n * pointer moves inside one frame would therefore have the second one\n * decided against the boxes the first one has already invalidated,\n * and the list reorders in a way nobody asked for. So a crossing\n * waits for the layout it caused: the flag is set here and cleared by\n * the carried row's own layout notification.\n */\n settling: boolean;\n}\n\n/** The payload type a reorder uses, so nothing else can be dropped on a row. */\nconst REORDER = 'gesso/reorder';\n\nclass Reorderable implements UiDropZone {\n private readonly registration = new ZoneRegistration(this);\n private session: UiDragSession | null = null;\n private carried: ReorderData | null = null;\n /** Where in the row the person took hold of it. */\n private grabX = 0;\n private grabY = 0;\n private pointerX = 0;\n private pointerY = 0;\n private dragging = false;\n\n /** What the element declared, which the translation is added to. */\n private basePivotX = 0;\n private basePivotY = 0;\n private baseScaleX = 1;\n private baseScaleY = 1;\n private baseRotation = 0;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: ReorderableOptions\n ) {}\n\n get node(): UiNode {\n return this.host.node;\n }\n\n attach(): this {\n const declared = this.host.get<Record<string, unknown> | null | undefined>('transform');\n if (declared !== undefined && declared !== null) {\n this.basePivotX = numberOr(declared.x, 0);\n this.basePivotY = numberOr(declared.y, 0);\n this.baseScaleX = numberOr(declared.scaleX, 1);\n this.baseScaleY = numberOr(declared.scaleY, 1);\n this.baseRotation = numberOr(declared.rotation, 0);\n }\n this.registration.sync();\n\n const longPress = this.options.start === 'longPress';\n if (longPress) {\n // A held finger is also how a touchscreen asks for a context\n // menu, and a row that is about to be picked up is not asking a\n // question about itself. Refusing the default on the LongPress is\n // the escape hatch the recognizer documents for exactly this.\n this.host.on(UiEventType.LongPress, event => event.preventDefault());\n }\n this.host.on(longPress ? UiEventType.DragStart : UiEventType.PanStart, event =>\n this.begin(event as UiPointerEvent)\n );\n this.host.on(longPress ? UiEventType.DragMove : UiEventType.PanMove, event => this.move(event as UiPointerEvent));\n this.host.on(longPress ? UiEventType.DragEnd : UiEventType.PanEnd, () => this.end());\n // A reorder underneath the row moves the row; the translation is\n // recomputed from the new box so it stays under the finger.\n this.host.onLayout(() => {\n this.registration.sync();\n if (this.carried !== null) {\n // The move this row is carrying has landed; the next crossing\n // is decided against boxes that are up to date.\n this.carried.settling = false;\n }\n this.place();\n });\n this.host.own(() => {\n this.registration.dispose();\n if (this.dragging) {\n this.session?.cancel();\n }\n });\n return this;\n }\n\n setOptions(options: ReorderableOptions): void {\n this.options = options;\n }\n\n // ---------------------------------------------------------------------\n // The row as a drop target\n // ---------------------------------------------------------------------\n\n boxOf() {\n return this.host.layoutBox();\n }\n\n accepts(payload: UiDragPayload): boolean {\n return payload.type === REORDER && (payload.data as ReorderData).list === this.options.list;\n }\n\n enter(state: UiDragState): void {\n const data = state.payload.data as ReorderData;\n if (data.index === this.options.index || data.settling) {\n return;\n }\n const from = data.index;\n const to = this.options.index;\n // The payload follows the row it belongs to, so the next crossing\n // is measured from where the row now is rather than from where it\n // started. Written *before* `onMove` is called, and from a copy of\n // the index, because an application that re-renders its list\n // synchronously inside `onMove` has already given this row its new\n // index by the time the call returns: read afterwards, `options.index`\n // is where this row went, which is where the carried row came from,\n // and the next crossing undoes the move. The two rows then swap on\n // every pointer move, which is the flicker that looked like one.\n data.index = to;\n data.settling = true;\n this.options.onMove(from, to);\n }\n\n over(): void {\n // Everything happens on the crossing; there is nothing to do while\n // the pointer stays inside one row.\n }\n\n leave(): void {\n // The row keeps whatever order the crossings gave it.\n }\n\n drop(): UiDropEffect {\n return 'move';\n }\n\n // ---------------------------------------------------------------------\n // The row as a drag source\n // ---------------------------------------------------------------------\n\n private begin(event: UiPointerEvent): void {\n // The list this row sits in scrolls, and a row being carried is not\n // a pan for it: `UiTouchScroller` listens at the root and would\n // scroll the list under the finger otherwise.\n event.stopPropagation();\n const box = this.host.layoutBox();\n this.grabX = box === null ? 0 : event.x - box.x;\n this.grabY = box === null ? 0 : event.y - box.y;\n this.pointerX = event.x;\n this.pointerY = event.y;\n this.dragging = true;\n this.carried = { list: this.options.list, index: this.options.index, settling: false };\n this.session = this.registration.sync();\n this.session.begin({ type: REORDER, data: this.carried }, event.x, event.y, this.host.node);\n for (const [property, value] of Object.entries(this.options.dragging ?? {})) {\n this.host.set(property, value);\n }\n this.options.onDragChange?.(true);\n this.place();\n }\n\n private move(event: UiPointerEvent): void {\n if (!this.dragging) {\n return;\n }\n event.stopPropagation();\n this.pointerX = event.x;\n this.pointerY = event.y;\n this.place();\n this.session?.move(event.x, event.y);\n }\n\n private end(): void {\n if (!this.dragging) {\n return;\n }\n this.dragging = false;\n this.carried = null;\n this.session?.end();\n for (const property of Object.keys(this.options.dragging ?? {})) {\n this.host.clear(property);\n }\n this.host.clear('transform');\n this.options.onDragChange?.(false);\n }\n\n /**\n * Puts the row where the pointer is holding it.\n *\n * The layout box is the row's place in the list *without* this\n * translation, because a modifier's override moves the paint and not\n * the layout, so subtracting it is what makes the placement absolute\n * rather than cumulative.\n */\n private place(): void {\n if (!this.dragging) {\n return;\n }\n const box = this.host.layoutBox();\n if (box === null) {\n return;\n }\n const axis = this.options.axis ?? 'y';\n const x = axis === 'y' ? 0 : this.pointerX - this.grabX - box.x;\n const y = axis === 'x' ? 0 : this.pointerY - this.grabY - box.y;\n if (x === 0 && y === 0) {\n this.host.clear('transform');\n return;\n }\n this.host.set('transform', {\n x: this.basePivotX,\n y: this.basePivotY,\n translateX: x,\n translateY: y,\n scaleX: this.baseScaleX,\n scaleY: this.baseScaleY,\n rotation: this.baseRotation\n });\n }\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n","import { UiEventType, type UiPinchEvent, type UiWheelEvent } from '../input/UiInputEvent';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/** Where a zoomable node has got to. */\nexport interface ZoomState {\n readonly scale: number;\n /** Degrees, matching the transform property's own units. */\n readonly rotation: number;\n readonly translateX: number;\n readonly translateY: number;\n}\n\nexport interface PinchableOptions {\n /** How far it may be zoomed out. Default 1, which is \"no smaller than declared\". */\n readonly minScale?: number;\n /** How far in. Default 8. */\n readonly maxScale?: number;\n /**\n * Whether two fingers twisting also rotate the node. Default false.\n *\n * Off by default because rotation is a gesture people make by\n * accident: a pinch on a photo is never perfectly parallel, and a\n * viewer that rotates two degrees every time it is zoomed feels\n * broken. A map or a canvas of freeform objects turns it on.\n */\n readonly rotate?: boolean;\n /**\n * Whether Ctrl (or Command) with the wheel zooms as well. Default\n * true.\n *\n * A trackpad pinch reaches the browser as exactly this, and it is\n * also the only way a mouse can zoom at all, so a zoomable node that\n * answers only to two fingers is a zoomable node most people cannot\n * zoom.\n */\n readonly wheel?: boolean;\n /** How much of a zoom one wheel notch is worth. Default 0.0015 per pixel of delta. */\n readonly wheelSensitivity?: number;\n /** Told whenever the zoom changes, for a readout or a reset button. */\n readonly onChange?: (state: ZoomState) => void;\n}\n\n/**\n * Makes a node zoomable by two fingers, or by Ctrl and the wheel.\n *\n * Pinch was deferred partly on the ground that \"what a\n * framework should do with a pinch when it owns no zoom is a design\n * question this work did not need to answer\". This is the answer: the\n * framework owns no zoom, and this modifier does. Nothing listens for a\n * pinch unless an element asks for it, exactly as nothing hovers unless\n * an element asks for it, which is the whole shape of\n * the modifier contract.\n *\n * ## It zooms about the gesture, not about the middle\n *\n * The point under the fingers stays under the fingers. That is the\n * difference between a photo viewer that feels like a photo viewer and\n * one that scales about its own centre and slides the thing you were\n * looking at off the edge. Each gesture holds the node-local point the\n * pinch started on and solves the translation that keeps it in place,\n * which is also why a two-finger pan and a pinch are one gesture here\n * rather than two that fight over the same transform.\n *\n * ## What it does to the transform\n *\n * It writes `scaleX`, `scaleY`, `rotation` and the translation as one\n * override, with the pivot at the node's own origin. A transform the\n * element declared is read once at attach and used as the **starting\n * state** rather than composed with: a photo that starts at 1.5x zooms\n * out to 1x, which is what an application that declared 1.5x meant,\n * and composing would have made its lower bound 1.5 without saying so.\n */\nconst kind = defineModifier<PinchableOptions>({\n name: 'pinchable',\n attach(host, options) {\n zoomers.set(host, new Pinchable(host, options).attach());\n },\n update(host, options) {\n zoomers.get(host)?.setOptions(options);\n }\n});\n\nconst zoomers = new WeakMap<UiModifierHost, Pinchable>();\n\n/** Makes a node zoomable. Hoist the options object, as every modifier asks. */\nexport function pinchable(options: PinchableOptions = EMPTY_OPTIONS): UiModifier<PinchableOptions> {\n return kind(options);\n}\n\nconst EMPTY_OPTIONS: PinchableOptions = Object.freeze({});\n\nclass Pinchable {\n private scale = 1;\n private rotation = 0;\n private translateX = 0;\n private translateY = 0;\n\n /** The gesture's own baseline, so its cumulative scale is relative to it. */\n private startScale = 1;\n private startRotation = 0;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: PinchableOptions\n ) {}\n\n attach(): this {\n const declared = this.host.get<Record<string, unknown> | null | undefined>('transform');\n if (declared !== undefined && declared !== null) {\n this.scale = numberOr(declared.scaleX, 1);\n this.rotation = numberOr(declared.rotation, 0);\n this.translateX = numberOr(declared.translateX, 0);\n this.translateY = numberOr(declared.translateY, 0);\n }\n this.host.on(UiEventType.PinchStart, event => this.start(event as UiPinchEvent));\n this.host.on(UiEventType.PinchMove, event => this.pinch(event as UiPinchEvent));\n this.host.on(UiEventType.Wheel, event => this.wheel(event as UiWheelEvent));\n return this;\n }\n\n setOptions(options: PinchableOptions): void {\n this.options = options;\n }\n\n /** Where the node has got to, for a reset button or a readout. */\n get state(): ZoomState {\n return {\n scale: this.scale,\n rotation: this.rotation,\n translateX: this.translateX,\n translateY: this.translateY\n };\n }\n\n private start(event: UiPinchEvent): void {\n event.stopPropagation();\n this.startScale = this.scale;\n this.startRotation = this.rotation;\n }\n\n private pinch(event: UiPinchEvent): void {\n event.stopPropagation();\n const rotation = this.options.rotate === true ? this.startRotation + event.rotation : this.rotation;\n this.applyAt(event.x, event.y, this.startScale * event.scale, rotation, event.translateX, event.translateY);\n }\n\n private wheel(event: UiWheelEvent): void {\n if (this.options.wheel === false) {\n return;\n }\n if (!event.modifiers.ctrl && !event.modifiers.meta) {\n return;\n }\n // The wheel was spent here, so the container underneath does not\n // scroll on the same notch.\n event.preventDefault();\n event.stopPropagation();\n const sensitivity = this.options.wheelSensitivity ?? 0.0015;\n // Exponential rather than linear: a fixed fraction per notch means\n // zooming in and back out by the same number of notches lands\n // exactly where it started.\n const factor = Math.exp(-event.deltaY * sensitivity);\n this.applyAt(event.x, event.y, this.scale * factor, this.rotation, 0, 0);\n }\n\n /**\n * Moves to a new scale and rotation while holding one point still.\n *\n * `anchorX`/`anchorY` are in canvas space, and the node's layout box\n * is where the node sits before this modifier's transform, so the\n * anchor is turned into a node-local point under the *current*\n * transform, then the translation that puts that same local point\n * back under the anchor at the *new* transform is solved directly.\n * Doing it in one step rather than accumulating deltas is what keeps\n * a long gesture from drifting.\n */\n private applyAt(anchorX: number, anchorY: number, scale: number, rotation: number, panX: number, panY: number): void {\n const box = this.host.layoutBox();\n if (box === null) {\n return;\n }\n const clamped = Math.min(Math.max(scale, this.options.minScale ?? 1), this.options.maxScale ?? 8);\n const localX = anchorX - box.x - this.translateX;\n const localY = anchorY - box.y - this.translateY;\n const [unrotatedX, unrotatedY] = unapply(localX, localY, this.scale, this.rotation);\n const [reappliedX, reappliedY] = apply(unrotatedX, unrotatedY, clamped, rotation);\n this.scale = clamped;\n this.rotation = rotation;\n this.translateX = anchorX + panX - box.x - reappliedX;\n this.translateY = anchorY + panY - box.y - reappliedY;\n this.write();\n }\n\n private write(): void {\n this.host.set('transform', {\n x: 0,\n y: 0,\n translateX: this.translateX,\n translateY: this.translateY,\n scaleX: this.scale,\n scaleY: this.scale,\n rotation: this.rotation\n });\n this.options.onChange?.(this.state);\n }\n}\n\n/** Rotates and scales a node-local point, the way the renderers do. */\nfunction apply(x: number, y: number, scale: number, degrees: number): [number, number] {\n const radians = (degrees * Math.PI) / 180;\n const cos = Math.cos(radians);\n const sin = Math.sin(radians);\n return [(x * cos - y * sin) * scale, (x * sin + y * cos) * scale];\n}\n\n/** The inverse of `apply`, for turning a screen offset back into a local point. */\nfunction unapply(x: number, y: number, scale: number, degrees: number): [number, number] {\n if (scale === 0) {\n return [0, 0];\n }\n const radians = (-degrees * Math.PI) / 180;\n const cos = Math.cos(radians);\n const sin = Math.sin(radians);\n const sx = x / scale;\n const sy = y / scale;\n return [sx * cos - sy * sin, sx * sin + sy * cos];\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n","import type { UiNode } from '../graph/UiNode';\nimport { UiEventType, type UiKeyboardEvent, type UiPointerEvent } from '../input/UiInputEvent';\nimport type { UiShortcut, UiShortcutRegistry } from '../input/UiShortcuts';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\nexport interface ShortcutsOptions {\n /** The registry every `shortcut` on this page registers into. */\n readonly registry: UiShortcutRegistry;\n}\n\n/**\n * Feeds keyboard input to a shortcut registry.\n *\n * Put it on the application's root element, once. It listens at the\n * graph root in the **bubble** phase, which is the whole of the design:\n * a key reaches the registry only after the focused node and everything\n * above it have had it, so a text field that handles its own Escape\n * keeps it and a dialog that takes Enter keeps that, with no list of\n * exceptions anywhere in the registry.\n *\n * A key a shortcut takes is marked `preventDefault()`, which is what\n * `UiKeyboardController` reads before applying its own defaults, so a\n * shortcut on Tab or on Enter is not also a tab navigation or a button\n * press.\n *\n * The focused node is the event's `target`: the keyboard controller\n * routes a key to the focused node, and to the root when nothing is\n * focused, so the registry learns the scope from the event rather than\n * holding a focus manager it would otherwise need injecting.\n */\nconst shortcutsKind = defineModifier<ShortcutsOptions>({\n name: 'shortcuts',\n attach(host, options) {\n let registry = options.registry;\n feeds.set(host, next => (registry = next.registry));\n host.onRoot(\n UiEventType.KeyDown,\n event => {\n if (event.defaultPrevented) {\n return;\n }\n const key = event as UiKeyboardEvent;\n if (registry.handleKey(key.key, key.modifiers, key.target)) {\n event.preventDefault();\n event.stopPropagation();\n }\n },\n { capture: false }\n );\n },\n update(host, options) {\n feeds.get(host)?.(options);\n }\n});\n\nconst feeds = new WeakMap<UiModifierHost, (options: ShortcutsOptions) => void>();\n\nexport function shortcuts(options: ShortcutsOptions): UiModifier<ShortcutsOptions> {\n return shortcutsKind(options);\n}\n\nexport interface ShortcutOptions extends Omit<UiShortcut, 'scope'> {\n readonly registry: UiShortcutRegistry;\n /**\n * Whether the shortcut is live only while focus is inside this node.\n * Default true, which is the point of registering it on an element\n * rather than calling `registry.register` from anywhere.\n *\n * False registers it application-wide with the element's lifetime,\n * which is what a route wants: the shortcut belongs to the screen and\n * dies with it, but works wherever the focus happens to be.\n */\n readonly scoped?: boolean;\n}\n\n/**\n * Registers one shortcut for as long as the element exists.\n *\n * The registration is undone on detach, so the lifetime of a shortcut\n * is the lifetime of the thing it acts on and a screen cannot leave a\n * command behind when it is replaced. That is the single thing a\n * root-level `onKeyDown` switching on the route cannot give you, and\n * the reason both applications' key handling grew into a function\n * nobody wanted to touch.\n */\nconst shortcutKind = defineModifier<ShortcutOptions>({\n name: 'shortcut',\n attach(host, options) {\n let current = options;\n entries.set(host, next => (current = next));\n const scope: UiNode | null = options.scoped === false ? null : host.node;\n host.own(\n options.registry.register({\n keys: options.keys,\n label: options.label,\n group: options.group,\n priority: options.priority,\n scope,\n // Read through the latest arguments, so a handler rebuilt every\n // render does not cost a re-registration.\n when: () => current.when?.() ?? true,\n run: () => current.run()\n })\n );\n },\n update(host, options) {\n entries.get(host)?.(options);\n }\n});\n\nconst entries = new WeakMap<UiModifierHost, (options: ShortcutOptions) => void>();\n\nexport function shortcut(options: ShortcutOptions): UiModifier<ShortcutOptions> {\n return shortcutKind(options);\n}\n\nexport interface ContextMenuOptions {\n /**\n * A menu was asked for on this node: the right button, or a finger\n * held on it. The point is where the pointer was, which is what a\n * `Menu`'s `at` prop takes.\n */\n readonly onOpen: (at: { readonly x: number; readonly y: number }) => void;\n /** Whether the request also stops here. Default true. */\n readonly stopPropagation?: boolean;\n}\n\n/**\n * Turns a context-menu request on this node into a callback.\n *\n * `Menu` has taken an `at` point and left the trigger to the caller\n * since it was written, with a comment saying a menu is opened by \"a\n * button, a right-click or a keyboard shortcut, and the component\n * should not care which\". Two of those three existed. This is the\n * third, and it is a modifier rather than a component because the thing\n * that needs the trigger is whatever the person right-clicked, not the\n * menu.\n *\n * The request stops here by default, so a row inside a list gets its\n * own menu rather than the list's; a node that wants both lets it\n * through.\n */\nconst contextMenuKind = defineModifier<ContextMenuOptions>({\n name: 'contextMenu',\n attach(host, options) {\n let current = options;\n menus.set(host, next => (current = next));\n host.on(UiEventType.ContextMenu, event => {\n const pointer = event as UiPointerEvent;\n if (current.stopPropagation !== false) {\n event.stopPropagation();\n }\n current.onOpen({ x: pointer.x, y: pointer.y });\n });\n },\n update(host, options) {\n menus.get(host)?.(options);\n }\n});\n\nconst menus = new WeakMap<UiModifierHost, (options: ContextMenuOptions) => void>();\n\nexport function contextMenu(options: ContextMenuOptions): UiModifier<ContextMenuOptions> {\n return contextMenuKind(options);\n}\n","import { defaultMotion, type UiSpringSpec, type UiSpringToken } from '../environment/UiMotion';\nimport type { AnimatedCell, UiEasing } from '../animation';\nimport type { UiNode } from '../graph/UiNode';\nimport { defineModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\nexport interface AnimateLayoutOptions {\n /** The spring that carries the node home. Named, or given outright. */\n spring?: UiSpringToken | UiSpringSpec;\n /** Use a tween of this many milliseconds instead of a spring. */\n duration?: number;\n easing?: UiEasing;\n /** Movements smaller than this are not worth animating. */\n threshold?: number;\n}\n\n/**\n * Animates a node from where it was to where layout has just put it.\n *\n * The classic FLIP: the node's box moved between two frames, so it is\n * drawn back at the old place and released. What is worth writing down\n * is where each half comes from, because both were already here.\n *\n * **The old box comes from `LayoutNotifier`, not from L7's slab\n * records.** Those are the prerequisite and they\n * do not exist — `LayoutRecord.ts` mentions slabs only in a comment\n * about a future Worker transfer. B2 built the thing this actually\n * needs: a per-node, opt-in notification after any frame that moved a\n * box, which already keeps the last box it reported. That is exactly\n * the old/new pair, it costs nothing when nothing is listening, and\n * building a second mechanism beside it would have been the mistake.\n *\n * **The offset is the transform's translation.** It used to be a\n * relative `left`/`top` on a `position: relative` node, and\n * the reason is this: `UiTransform.x`/`.y` are the\n * transform's *pivot*, both renderers compose `T(pivot)·R·S·T(-pivot)`,\n * and there was no translation field to use. The cost it recorded was\n * that a tick marked Layout rather than Paint, so the animating\n * subtree was re-placed from its relayout boundary on every frame of\n * every animation. `UiTransform` now has `translateX`/`translateY`,\n * which both renderers and the hit tester apply outside the pivot, so\n * that cost is simply gone: a reordering list marks Paint and the\n * layout engine does not run. It also means the modifier no longer\n * cares what an element's own `position`, `left` or `top` are, and the\n * two warnings that existed only to say so are gone with it.\n *\n * **It animates a reorder, and follows everything else.** The two look\n * identical to anything watching boxes and want opposite behaviour: a\n * node that changed places should be drawn back where it was and\n * released, while a node that moved because its neighbour grew should\n * simply move with it. Holding it still would be the neighbour growing\n * *through* it, which is what the first version of this did — the\n * example's expanding card was drawn over the card below it, in the\n * browser, on the first click. So the modifier absorbs a move only\n * when its parent's `childOrderVersion` has changed, which is exactly\n * \"something was inserted, moved or removed here\" and is one integer\n * comparison. A window resize is followed rather than animated for the\n * same reason.\n *\n * The listener also has to tell its own movement apart from the\n * layout's, because writing an offset moves the box the notifier\n * reports. It does that by subtracting the offset it wrote: what it\n * watches is the node's place in the *flow*. An interruption springs\n * from wherever the node had got to, which is what a list reordered\n * twice in quick succession should look like.\n */\nexport const animateLayout = defineModifier<AnimateLayoutOptions | undefined>({\n name: 'animateLayout',\n attach(host, args) {\n new LayoutAnimation(host, args ?? {}).attach();\n }\n});\n\nclass LayoutAnimation {\n /** Where the node sat in the flow when we last heard, or null before the first layout. */\n private flowX = 0;\n private flowY = 0;\n private seen = false;\n /** The parent and its child-order version when we last heard. */\n private parent: UiNode | null = null;\n private childOrder = -1;\n /** What we are currently offsetting by, and therefore what to subtract. */\n private offsetX = 0;\n private offsetY = 0;\n /** What the element declared, which our offset is added to. */\n private basePivotX = 0;\n private basePivotY = 0;\n private baseScaleX = 1;\n private baseScaleY = 1;\n private baseRotation = 0;\n\n private readonly cellX: AnimatedCell<number>;\n private readonly cellY: AnimatedCell<number>;\n\n constructor(\n private readonly host: UiModifierHost,\n private readonly options: AnimateLayoutOptions\n ) {\n // Each write is one override, which the cascade restores on detach.\n this.cellX = this.makeCell(\n () => this.offsetX,\n next => this.setOffset(next, this.offsetY)\n );\n this.cellY = this.makeCell(\n () => this.offsetY,\n next => this.setOffset(this.offsetX, next)\n );\n }\n\n attach(): void {\n // Read once, in the element's own values: what the modifier writes\n // is the element's transform with a translation added, so a node\n // that also rotates keeps rotating while it slides.\n const declared = this.host.get<Record<string, unknown> | undefined>('transform');\n if (declared !== undefined && declared !== null) {\n this.basePivotX = numberOr(declared.x, 0);\n this.basePivotY = numberOr(declared.y, 0);\n this.baseScaleX = numberOr(declared.scaleX, 1);\n this.baseScaleY = numberOr(declared.scaleY, 1);\n this.baseRotation = numberOr(declared.rotation, 0);\n }\n // The listener fires on any frame that moved the node's *visible*\n // box, which includes a scroll; what it reads is `flowBox`, which\n // does not. So a scroll wakes it and it correctly decides nothing\n // moved. This was found in the browser: with the visible box, every\n // row of the list lagged behind the page scroll and caught up.\n this.host.onLayout(() => this.handleLayout());\n }\n\n private makeCell(read: () => number, write: (offset: number) => void): AnimatedCell<number> {\n return {\n get value(): number {\n return read();\n },\n set value(next: number) {\n write(next);\n }\n };\n }\n\n /**\n * Writes the offset as the transform's translation, or takes it away.\n *\n * Back at zero the override is dropped rather than written as an\n * identity translation, so the node ends up with exactly what the\n * element declared — which for nearly every element is no transform\n * at all, and therefore no `hasTransform` in its paint state and no\n * matrix multiply per frame.\n */\n private setOffset(x: number, y: number): void {\n this.offsetX = x;\n this.offsetY = y;\n if (x === 0 && y === 0) {\n this.host.clear('transform');\n return;\n }\n this.host.set('transform', {\n x: this.basePivotX,\n y: this.basePivotY,\n translateX: x,\n translateY: y,\n scaleX: this.baseScaleX,\n scaleY: this.baseScaleY,\n rotation: this.baseRotation\n });\n }\n\n private handleLayout(): void {\n const box = this.host.flowBox();\n if (box === null) {\n return;\n }\n // The translation is paint-only, so the flow box the notifier\n // reports never contains it — but the subtraction is kept because\n // the node's place in the flow is still what this watches, and a\n // renderer that ever folded the transform into layout would break\n // silently without it.\n const flowX = box.x;\n const flowY = box.y;\n const parent = this.host.node.parent;\n const childOrder = parent === null ? -1 : parent.childOrderVersion;\n const reordered = parent !== this.parent || childOrder !== this.childOrder;\n this.parent = parent;\n this.childOrder = childOrder;\n if (!this.seen) {\n this.seen = true;\n this.flowX = flowX;\n this.flowY = flowY;\n return;\n }\n const dx = this.flowX - flowX;\n const dy = this.flowY - flowY;\n this.flowX = flowX;\n this.flowY = flowY;\n if (!reordered) {\n // Nothing changed places, so this is the layout itself moving —\n // a neighbour resizing, a window resizing, a container growing.\n // The node follows it, and whatever spring is already running\n // keeps decaying the offset it has. Absorbing this would hold\n // the node still while its neighbour grew into it.\n return;\n }\n const threshold = this.options.threshold ?? 0.5;\n if (Math.abs(dx) < threshold && Math.abs(dy) < threshold) {\n // Either nothing moved, or the only thing that moved was us.\n return;\n }\n // Keep it where it appears, then let it go. Adding to the current\n // offset rather than replacing it is what makes an interrupted\n // reorder continue from where it had got to.\n this.cellX.value = this.offsetX + dx;\n this.cellY.value = this.offsetY + dy;\n this.release(this.cellX);\n this.release(this.cellY);\n }\n\n private release(cell: AnimatedCell<number>): void {\n if (cell.value === 0) {\n return;\n }\n if (this.options.duration !== undefined) {\n this.host.animate(cell, 0, { duration: this.options.duration, easing: this.options.easing });\n return;\n }\n const named = this.options.spring ?? 'snappy';\n this.host.spring(cell, 0, {\n spring: typeof named === 'string' ? defaultMotion.springs[named] : named,\n // Half a pixel: closer than this is the same pixel on a 1× screen\n // and within one on a 2×, so continuing to schedule frames for it\n // would be paying for a movement nobody can see.\n restDelta: 0.5\n });\n }\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n","import { combineLatest, distinctUntilChanged, isObservable, of, type Observable } from 'rxjs';\n\nimport { UiEnvironmentKeys } from '../environment/UiEnvironmentKeys';\nimport type { Reactive } from '../composition/UiElementProps';\nimport type { UiColorValue } from '../properties/UiPropertyValues';\nimport { resolveColorValue } from '../properties/UiThemeColor';\nimport type { UiImage } from '../properties/UiImage';\nimport type { ImageResolver } from '../rendering/ImageResolver';\nimport type { IconRasterizer, IconSpec } from '../rendering/IconRasterizer';\nimport { defineModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/**\n * A value or an Observable of one, as one Observable. The media\n * modifiers take their arguments this way so an `Image` whose `src` is\n * bound to a cell follows it, rather than reading it once at attach.\n */\nfunction follow<T>(value: Reactive<T>): Observable<T> {\n return isObservable(value) ? (value as Observable<T>) : of(value);\n}\n\n/** One url, or several to try in order until one resolves. */\nexport type ImageSources = string | readonly string[];\n\nexport interface ImageSourceArgs {\n readonly resolver: ImageResolver;\n /**\n * The url to resolve, or a list of them tried in order: the same\n * picture on several mirrors, say. An Observable is followed, and each\n * new value replaces the last.\n */\n readonly source: Reactive<ImageSources>;\n readonly onState?: (state: 'loading' | 'loaded' | 'failed', error?: unknown) => void;\n}\n\n/**\n * Resolves a source to a bitmap and writes it onto the node's `image`.\n *\n * The source may be an Observable. Each distinct url it emits releases\n * the one before and starts a new load, so an `Image` bound to a cell\n * shows whatever the cell says rather than what it said when the node\n * was built. A load that has been superseded is dropped when it\n * arrives, never written.\n */\nexport const imageSource = defineModifier<ImageSourceArgs>({\n name: 'imageSource',\n attach(host, args) {\n let release: (() => void) | null = null;\n const subscription = follow(args.source)\n .pipe(distinctUntilChanged(sameSources))\n .subscribe(source => {\n release?.();\n release = load(host, args, typeof source === 'string' ? [source] : source);\n });\n host.own(() => {\n subscription.unsubscribe();\n release?.();\n release = null;\n });\n }\n // No `update`: a changed args object is a detach and an attach, which\n // is right, because the resolver or the whole source stream changed.\n // A changed *url* inside the stream is handled above.\n});\n\n/**\n * Starts loading the first candidate, falling through to the next when\n * one fails, and returns what undoes it: the in-flight result is\n * dropped and whichever source is held is released. A failed candidate\n * holds nothing, because the resolver does not cache failures.\n */\nfunction load(host: UiModifierHost, args: ImageSourceArgs, sources: readonly string[]): () => void {\n let live = true;\n let held: string | null = null;\n args.onState?.('loading');\n host.clear('image');\n const attempt = (index: number, lastError: unknown): void => {\n if (!live) {\n return;\n }\n const source = sources[index];\n if (source === undefined) {\n args.onState?.('failed', lastError ?? new Error('No image source was given.'));\n return;\n }\n held = source;\n // Already decoded: on the node in this frame, so a picture the\n // cache holds never paints as its placeholder first. Matters most\n // under a shared-element morph, where that placeholder would be a\n // blank tile where the same picture stood a frame ago.\n const ready = args.resolver.peek?.(source) ?? null;\n if (ready !== null) {\n host.set('image', ready);\n args.onState?.('loaded');\n return;\n }\n args.resolver\n .resolve(source)\n .then((bitmap: UiImage) => {\n if (!live) {\n return;\n }\n host.set('image', bitmap);\n args.onState?.('loaded');\n })\n .catch((error: unknown) => {\n if (held === source) {\n held = null;\n }\n attempt(index + 1, error);\n });\n };\n attempt(0, undefined);\n return () => {\n live = false;\n if (held !== null) {\n args.resolver.release(held);\n held = null;\n }\n };\n}\n\nfunction sameSources(a: ImageSources, b: ImageSources): boolean {\n if (typeof a === 'string' || typeof b === 'string') {\n return a === b;\n }\n return a.length === b.length && a.every((url, index) => url === b[index]);\n}\n\nexport interface IconSourceArgs {\n readonly rasterizer: IconRasterizer;\n readonly path: Reactive<string>;\n readonly viewBox: Reactive<number>;\n readonly size: Reactive<number>;\n /** A palette name or a literal; resolved against the theme the node inherits. */\n readonly color: Reactive<UiColorValue>;\n readonly style: Reactive<'fill' | 'stroke'>;\n readonly strokeWidth: Reactive<number>;\n /** How a filled path decides what is inside it; see `IconSpec.fillRule`. */\n readonly fillRule?: Reactive<'nonzero' | 'evenodd' | undefined>;\n}\n\n/**\n * Rasterises an icon path and writes it onto the node's `image`.\n *\n * The colour is the interesting part. Everything else in the library\n * names a palette entry and lets it resolve at paint, but a raster has\n * its colour baked in, so the icon has to be re-rasterised when the\n * theme under it changes. That is what `host.environment` and\n * `host.onEnvironment` — the half that had no consumer until now —\n * are for: the modifier reads the theme\n * the node inherits, and re-reads it when a provider above swaps it,\n * so an icon inside a card that turns dark turns with it.\n */\nexport const iconSource = defineModifier<IconSourceArgs>({\n name: 'iconSource',\n attach(host, args) {\n let current: IconSpec | null = null;\n let generation = 0;\n /** The arguments as last seen; each is followed, so a bound path or colour redraws the glyph. */\n let seen: {\n path: string;\n viewBox: number;\n size: number;\n color: UiColorValue;\n style: 'fill' | 'stroke';\n strokeWidth: number;\n fillRule: 'nonzero' | 'evenodd' | undefined;\n } | null = null;\n\n const render = (): void => {\n if (seen === null) {\n return;\n }\n const theme = host.environment(UiEnvironmentKeys.theme);\n const color = resolveColorValue(host.node, seen.color) ?? theme.colors.text;\n const spec: IconSpec = {\n path: seen.path,\n viewBox: seen.viewBox,\n size: seen.size,\n color,\n style: seen.style,\n strokeWidth: seen.strokeWidth,\n fillRule: seen.fillRule\n };\n if (current !== null && sameSpec(current, spec)) {\n return;\n }\n const previous = current;\n current = spec;\n const mine = ++generation;\n args.rasterizer\n .raster(spec)\n .then(bitmap => {\n if (generation === mine) {\n host.set('image', bitmap);\n }\n })\n .catch(() => {\n if (generation === mine) {\n host.clear('image');\n }\n });\n if (previous !== null) {\n args.rasterizer.release(previous);\n }\n };\n\n const subscription = combineLatest([\n follow(args.path),\n follow(args.viewBox),\n follow(args.size),\n follow(args.color),\n follow(args.style),\n follow(args.strokeWidth),\n follow(args.fillRule ?? undefined)\n ]).subscribe(([path, viewBox, size, color, style, strokeWidth, fillRule]) => {\n seen = { path, viewBox, size, color, style, strokeWidth, fillRule };\n render();\n });\n host.onEnvironment(render);\n host.own(() => {\n subscription.unsubscribe();\n generation++;\n if (current !== null) {\n args.rasterizer.release(current);\n current = null;\n }\n });\n }\n});\n\nfunction sameSpec(a: IconSpec, b: IconSpec): boolean {\n return (\n a.path === b.path &&\n a.viewBox === b.viewBox &&\n a.size === b.size &&\n a.style === b.style &&\n a.strokeWidth === b.strokeWidth &&\n (a.fillRule ?? 'nonzero') === (b.fillRule ?? 'nonzero') &&\n a.color.r === b.color.r &&\n a.color.g === b.color.g &&\n a.color.b === b.color.b &&\n a.color.a === b.color.a\n );\n}\n","import { isObservable, type Observable, type Subscription } from 'rxjs';\n\nimport { defaultMotion, type UiSpringSpec, type UiSpringToken } from '../environment/UiMotion';\nimport { easings, type UiEasing } from '../animation/UiEasing';\nimport type { AnimatedCell } from '../animation/UiAnimation';\nimport {\n MOTION_CHANNELS,\n MOTION_REST,\n isMotionRest,\n resolveMotionState,\n type MotionChannel,\n type MotionStateInput,\n type MotionTiming,\n type ResolvedMotionState\n} from '../animation/UiMotionState';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { UiNode } from '../graph/UiNode';\nimport { defineModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/**\n * Motion, as two modifiers over one mechanism.\n *\n * `motion` moves an element to a visual state, optionally in from\n * another; `sharedElement` makes an element that appears under a name\n * another element was using start from where that element stood.\n *\n * Both write the same two properties — `transform` and `opacity` —\n * through the same per-node composer below, and both drive their\n * numbers through `host.animate` / `host.spring`, so they are\n * cancelled with the node, honour reduced motion, and cost the runtime\n * exactly what one animation costs. Nothing here is a second animation\n * system beside `AnimationDriver`; it is a vocabulary in front of it.\n *\n * **Why none of it touches layout.** Every channel a motion state has\n * resolves in `PaintState`, so an element moving through them marks\n * Paint and the layout engine never runs for it. That is the whole\n * reason `UiTransform` gained `translateX`/`translateY`:\n * animation used to move a node with a relative `left`/`top` because\n * there was no translation to use, and paid a relayout per tick for\n * it. The one exception is `morph: 'geometry'`, which asks for a\n * relayout deliberately and says why.\n *\n * **Exit lives above this file.** An element that has logically left\n * has to be kept somewhere, and that is a question about who owns the\n * children — the framework's `Presence`, not a modifier. What\n * `Presence` needs from here is `motion`, which it points at an exit\n * state and waits on.\n */\n\n// ---------------------------------------------------------------------------\n// The per-node composer\n// ---------------------------------------------------------------------------\n\ninterface MotionLayerRecord {\n state: ResolvedMotionState;\n readonly host: UiModifierHost;\n}\n\n/**\n * Every motion layer on one node, composed into one write.\n *\n * More than one is normal: an element that is entering *and* morphing\n * from an element on the previous screen has two, and the two describe\n * different things about the same element. Composition is the obvious\n * arithmetic — translations and rotations add, scales and opacities\n * multiply — which is what makes \"fade it out while it also slides\"\n * mean what it looks like it means.\n *\n * The composed value is written through **every** participating host\n * rather than one. The override cascade resolves a conflict in favour\n * of whichever modifier is later in the element's list (see\n * `UiModifierSet`), so writing from a single host would make the\n * result depend on modifier order; writing the same value from all of\n * them makes last-wins land on the right answer whichever one is last,\n * and a layer detaching drops only its own override.\n */\nclass MotionComposer {\n private readonly layers = new Map<symbol, MotionLayerRecord>();\n /** The pivot: the node's own centre, so a scale grows from the middle. */\n private pivotX = 0;\n private pivotY = 0;\n\n add(key: symbol, host: UiModifierHost): void {\n this.layers.set(key, { state: MOTION_REST, host });\n }\n\n remove(key: symbol): void {\n const record = this.layers.get(key);\n if (record === undefined) {\n return;\n }\n this.layers.delete(key);\n // Its own overrides go with it; the rest re-state theirs, which is\n // how the element ends up with exactly what the remaining layers\n // say rather than with the last composite the departing one wrote.\n record.host.clear('transform');\n record.host.clear('opacity');\n this.flush();\n }\n\n set(key: symbol, state: ResolvedMotionState): void {\n const record = this.layers.get(key);\n if (record === undefined) {\n return;\n }\n record.state = state;\n this.flush();\n }\n\n /**\n * Re-reads the node's size and re-writes if the pivot moved.\n *\n * A pivot at the centre is what makes `scaleFrom(0.9)` grow from the\n * middle rather than out of the top-left corner, and the centre is a\n * function of the node's box — so a node that has not been laid out\n * yet, or that has just been resized, needs telling.\n */\n measure(box: LayoutBox | null): void {\n const pivotX = box === null ? 0 : box.width / 2;\n const pivotY = box === null ? 0 : box.height / 2;\n if (pivotX === this.pivotX && pivotY === this.pivotY) {\n return;\n }\n this.pivotX = pivotX;\n this.pivotY = pivotY;\n this.flush();\n }\n\n private flush(): void {\n let opacity = 1;\n let x = 0;\n let y = 0;\n let scaleX = 1;\n let scaleY = 1;\n let rotate = 0;\n for (const { state } of this.layers.values()) {\n opacity *= state.opacity;\n x += state.x;\n y += state.y;\n scaleX *= state.scaleX;\n scaleY *= state.scaleY;\n rotate += state.rotate;\n }\n const rest = isMotionRest({ opacity, x, y, scaleX, scaleY, rotate });\n for (const { host } of this.layers.values()) {\n if (rest) {\n // Back at rest, the overrides are dropped rather than written\n // as identity, so the element ends up with exactly what it\n // declared — which for nearly every element is no transform at\n // all, and therefore no `hasTransform` in its paint state and\n // no matrix multiply per frame.\n host.clear('transform');\n host.clear('opacity');\n continue;\n }\n host.set('transform', {\n x: this.pivotX,\n y: this.pivotY,\n translateX: x,\n translateY: y,\n scaleX,\n scaleY,\n rotation: rotate\n });\n host.set('opacity', opacity);\n }\n }\n}\n\n/** Keyed weakly, so a node that has gone takes its composer with it. */\nconst composers = new WeakMap<UiNode, MotionComposer>();\n\nfunction composerFor(node: UiNode): MotionComposer {\n let composer = composers.get(node);\n if (composer === undefined) {\n composer = new MotionComposer();\n composers.set(node, composer);\n }\n return composer;\n}\n\n// ---------------------------------------------------------------------------\n// One layer: six cells, driven by the runtime's driver\n// ---------------------------------------------------------------------------\n\n/**\n * One motion layer's six numbers, each an `AnimatedCell` the driver\n * owns.\n *\n * Six cells rather than one cell holding a state object, because the\n * driver's rule is one animation per cell — so six cells is six\n * independently retargetable channels, which is what lets an element\n * finish sliding while its fade is re-aimed, and is what makes a\n * spring per channel possible at all (`UiSpring` integrates a scalar\n * position and velocity, and there is no honest velocity for a whole\n * state taken together).\n */\nclass MotionLayer {\n private readonly key = Symbol('motion');\n private readonly cells: Record<MotionChannel, AnimatedCell<number>>;\n /**\n * Held in a box rather than in a field, because the six cells below\n * are property pairs on object literals — inside them `this` is the\n * literal, so they need somewhere to read and write that is not the\n * layer's own `this`.\n */\n private readonly holder: { state: ResolvedMotionState } = { state: MOTION_REST };\n /** Bumped on every retarget, so a superseded run does not report done. */\n private generation = 0;\n private outstanding = 0;\n private onSettled: (() => void) | null = null;\n\n constructor(private readonly host: UiModifierHost) {\n const composer = composerFor(host.node);\n const holder = this.holder;\n const key = this.key;\n const cellFor = (channel: MotionChannel): AnimatedCell<number> => ({\n get value(): number {\n return holder.state[channel];\n },\n set value(next: number) {\n holder.state = { ...holder.state, [channel]: next };\n composer.set(key, holder.state);\n }\n });\n this.cells = {\n opacity: cellFor('opacity'),\n x: cellFor('x'),\n y: cellFor('y'),\n scaleX: cellFor('scaleX'),\n scaleY: cellFor('scaleY'),\n rotate: cellFor('rotate')\n };\n composer.add(this.key, host);\n }\n\n get state(): ResolvedMotionState {\n return this.holder.state;\n }\n\n /** Puts the layer somewhere at once, cancelling whatever was driving it. */\n snapTo(state: ResolvedMotionState): void {\n this.generation++;\n this.onSettled = null;\n this.outstanding = 0;\n for (const channel of MOTION_CHANNELS) {\n this.host.stopAnimation(this.cells[channel]);\n }\n this.holder.state = state;\n composerFor(this.host.node).set(this.key, state);\n }\n\n /**\n * Moves every channel that is not already there, and calls back once\n * they have all arrived.\n *\n * `onDone` is what an exit waits on, so it must fire exactly once and\n * only for the run that is still current: a retarget mid-flight\n * completes the animations it supersedes, and reporting those as\n * \"arrived\" would drop an element off the screen halfway through its\n * replacement.\n */\n animateTo(target: ResolvedMotionState, timing: MotionTiming, onDone?: () => void): void {\n const generation = ++this.generation;\n this.onSettled = onDone ?? null;\n this.outstanding = 0;\n // Counted in two passes because an animation with no driver behind\n // it, or one that reduced motion snapped, completes synchronously\n // inside `subscribe` — before the loop has finished counting how\n // many there will be.\n let started = 0;\n let settled = 0;\n for (const channel of MOTION_CHANNELS) {\n if (Math.abs(this.holder.state[channel] - target[channel]) < channelEpsilon(channel)) {\n continue;\n }\n started++;\n this.drive(this.cells[channel], target[channel], timing).subscribe({\n complete: () => {\n if (generation !== this.generation) {\n return;\n }\n settled++;\n if (this.outstanding > 0 && settled === started) {\n this.report();\n }\n }\n });\n }\n this.outstanding = started - settled;\n if (this.outstanding <= 0) {\n // Either nothing had to move, or nothing could — no driver, or a\n // reduced-motion preference, both of which land on the target\n // without a frame passing.\n this.report();\n }\n }\n\n /** Drops the layer's contribution and its overrides. */\n release(): void {\n this.generation++;\n this.onSettled = null;\n for (const channel of MOTION_CHANNELS) {\n this.host.stopAnimation(this.cells[channel]);\n }\n composerFor(this.host.node).remove(this.key);\n }\n\n measure(box: LayoutBox | null): void {\n composerFor(this.host.node).measure(box);\n }\n\n private report(): void {\n this.outstanding = 0;\n const settled = this.onSettled;\n this.onSettled = null;\n settled?.();\n }\n\n private drive(cell: AnimatedCell<number>, to: number, timing: MotionTiming): Observable<number> {\n if (timing.spring !== undefined) {\n return this.host.spring(cell, to, {\n spring: springSpec(timing.spring),\n delay: timing.delay,\n reducedMotion: timing.reducedMotion,\n // These channels are opacities and scales as often as pixels,\n // so the default 0.01 would leave a scale visibly short.\n restDelta: 0.002\n });\n }\n return this.host.animate(cell, to, {\n duration: resolveDuration(timing.duration),\n easing: resolveEasing(timing.easing),\n delay: timing.delay,\n reducedMotion: timing.reducedMotion\n });\n }\n}\n\n/**\n * How close counts as \"already there\", per channel.\n *\n * A scale and an opacity live in [0, 1]-ish and a translation lives in\n * pixels, so one epsilon for all six would either animate a channel\n * that has not moved or refuse to animate one that has.\n */\nfunction channelEpsilon(channel: MotionChannel): number {\n return channel === 'x' || channel === 'y' ? 0.01 : 1e-4;\n}\n\nfunction springSpec(spring: UiSpringToken | UiSpringSpec): UiSpringSpec {\n return typeof spring === 'string' ? defaultMotion.springs[spring] : spring;\n}\n\nfunction resolveDuration(duration: MotionTiming['duration']): number {\n if (duration === undefined) {\n return defaultMotion.durations.normal;\n }\n return typeof duration === 'number' ? duration : defaultMotion.durations[duration];\n}\n\nfunction resolveEasing(easing: MotionTiming['easing']): UiEasing {\n if (easing === undefined) {\n return easings.standard;\n }\n return typeof easing === 'function' ? easing : (easings[easing] ?? easings.standard);\n}\n\n// ---------------------------------------------------------------------------\n// motion()\n// ---------------------------------------------------------------------------\n\nexport interface MotionArgs extends MotionTiming {\n /**\n * Where the element should be, now.\n *\n * A state, a stack of states, or an Observable of either. Null and\n * undefined both mean rest, so the common shape —\n * `hovered$.pipe(map(h => (h ? scaleFrom(0.97) : null)))` — needs no\n * ceremony to say \"and back to normal\".\n */\n readonly state?: MotionStateInput | Observable<MotionStateInput | null> | null;\n /**\n * Where it starts on the frame it appears, before moving to `state`.\n *\n * This is the enter animation, and it is a field on `motion` rather\n * than a modifier of its own because an element that enters and then\n * responds to a state is one element with one motion; two modifiers\n * writing the same six channels would be two layers arguing about\n * them.\n */\n readonly initial?: MotionStateInput;\n /**\n * Told when the element has finished arriving at `state`.\n *\n * `Presence` waits on this to know when an exit is over and the\n * element may finally be dropped. It fires once per target, and not\n * at all for a target that was superseded before it was reached.\n */\n readonly onSettled?: () => void;\n}\n\n/**\n * Moves an element to a visual state, and optionally in from another.\n *\n * ```tsx\n * <Box modifiers={[motion({ initial: [fade, slideUp(16)] })]} />\n * <Box modifiers={[motion({ state: pressed$, duration: 'fast' })]} />\n * ```\n *\n * `initial` is applied without animation when the modifier attaches —\n * during reconciliation, before the frame lays out or paints — so the\n * element is never seen at rest first, and released on the same frame,\n * which is what makes it an entrance rather than a jump.\n */\nexport const motion = defineModifier<MotionArgs>({\n name: 'motion',\n attach(host, args) {\n const controller = new MotionController(host, args);\n controllers.set(host, controller);\n controller.attach();\n host.own(() => {\n controllers.delete(host);\n controller.detach();\n });\n },\n update(host, args) {\n // Re-aimed rather than re-attached, because `initial` describes an\n // arrival and an element that is already here has already arrived.\n // Rebuilding the modifier when a duration changed would replay the\n // entrance, which is a bug you can see.\n controllers.get(host)?.update(args);\n }\n});\n\nconst controllers = new WeakMap<UiModifierHost, MotionController>();\n\nclass MotionController {\n private readonly layer: MotionLayer;\n private subscription: Subscription | null = null;\n\n constructor(\n private readonly host: UiModifierHost,\n private args: MotionArgs\n ) {\n this.layer = new MotionLayer(host);\n }\n\n attach(): void {\n this.layer.measure(this.host.layoutBox());\n if (this.args.initial !== undefined) {\n this.layer.snapTo(resolveMotionState(this.args.initial));\n }\n // The pivot is half the node's size, and the size is not known\n // until the first layout; until then a scale would grow out of the\n // corner. This also keeps the pivot right when the element resizes.\n this.host.onLayout(box => this.layer.measure(box));\n this.aim();\n }\n\n update(args: MotionArgs): void {\n const previousState = this.args.state;\n this.args = args;\n if (Object.is(previousState, args.state) && this.subscription !== null) {\n return;\n }\n this.subscription?.unsubscribe();\n this.subscription = null;\n this.aim();\n }\n\n detach(): void {\n this.subscription?.unsubscribe();\n this.subscription = null;\n this.layer.release();\n }\n\n private aim(): void {\n const state = this.args.state;\n if (isObservable(state)) {\n this.subscription = state.subscribe(next => this.animate(next));\n return;\n }\n this.animate(state ?? null);\n }\n\n private animate(state: MotionStateInput | null | undefined): void {\n this.layer.animateTo(resolveMotionState(state), this.args, this.args.onSettled);\n }\n}\n\n// ---------------------------------------------------------------------------\n// sharedElement()\n// ---------------------------------------------------------------------------\n\nexport interface SharedElementArgs extends MotionTiming {\n /**\n * The name both elements answer to.\n *\n * Two elements on two screens sharing a name is the whole\n * declaration: whichever appears second morphs from where the first\n * one stood. The name is global to the runtime, so it names a thing\n * — `'playlist-title'` — rather than a place.\n */\n readonly name: string;\n /**\n * How the morph is performed.\n *\n * `transform` — translate and scale the arriving element from the\n * departing one's box onto its own. Paint-only, and the right answer\n * for a picture, a line of text, a row of controls: anything whose\n * content should travel and stretch as one piece.\n *\n * `geometry` — animate the element's own `left`, `top`, `width` and\n * `height` instead. It costs a relayout per tick, and it is worth\n * paying for exactly one shape: a rounded rectangle whose aspect\n * ratio changes, because scaling one stretches its corners into\n * ellipses. A card expanding into a page is that shape, which is why\n * the CSS View Transitions version of this effect needs bespoke\n * `object-fit` rules for its card background and for nothing else.\n */\n readonly morph?: 'transform' | 'geometry';\n /** Fade the arriving element up from this opacity as it morphs. */\n readonly fadeFrom?: number;\n /**\n * How the two boxes' sizes become a scale.\n *\n * `'free'`, the default, takes each axis from its own ratio, which is\n * what a picture wants: a 4:3 thumbnail opening into a 16:9 banner\n * genuinely changes shape, and the morph should show that.\n *\n * `'uniform'` takes one scale for both axes, and is what **text**\n * wants. A line of text has no shape of its own to preserve — its box\n * is whatever the line breaks made it — while the type inside only\n * ever grows by its font size. Left free, a title that fits on one\n * line in a card and wraps to two on the page it opens into is scaled\n * 0.73 across and 0.34 down, and the letters are visibly squashed on\n * the way up. Measured exactly that, at a window width where the page\n * title wrapped and the card's did not.\n *\n * The width ratio is the one kept, because for a line of text it\n * tracks the font size closely — 0.73 against the 0.68 the font sizes\n * actually imply, in that same measurement — while the height ratio\n * is a count of lines and can be out by a whole multiple.\n */\n readonly scale?: 'free' | 'uniform';\n /**\n * Paints the element in the top layer for the length of the morph,\n * above the whole tree and outside every clip its ancestors impose.\n *\n * For the morph that ends *inside* something that clips. A picture\n * that lives in a scrolling row starts its journey back at the\n * middle of the page, which is nowhere near the row, so the row's\n * clip cuts most of the morph away: the slot is empty for the first\n * half of it and the picture appears out of nothing as it shrinks\n * back into the band. Measured in Segue, coming back from a track\n * page to the shelf it was opened from.\n *\n * `onMorph` cannot help with that, and neither can any `zIndex` an\n * ancestor writes: order decides who is drawn over whom, and this is\n * not a question of order. It is `overflow`.\n *\n * So it is the same question the browser answers by lifting its\n * named elements out of the page into a layer of their own, and the\n * same answer, held to the length of the morph: the element keeps\n * its place in the layout and every ancestor transform, scroll\n * offset and opacity, and loses their clipping and its place in\n * paint order. See the `lift` property.\n *\n * **On by default**, which it was not at first. The argument for\n * leaving it off was that most morphs do not need it and one that\n * does not is better off drawn where it belongs, since a lifted\n * element is over everything, a header and a now-playing bar\n * included. What that argument missed is who pays when it is wrong.\n * A morph that needed lifting and did not get it is not a little\n * worse, it is visibly broken — clipped in half, or drawn under the\n * thing it is travelling over — and the author cannot tell which\n * morphs need it by looking at the component, because the answer\n * lives in an ancestor's `overflow` three files away and changes\n * when a screen is relaid out. Segue found this twice: once when a\n * shelf's artwork flew home into a scrolling row, and again when the\n * Featured cards became a row and every one of their elements needed\n * the same flag.\n *\n * A browser lifts every named element for exactly this reason, and\n * nobody writing a view transition has to think about it. So the\n * default is the safe answer, and `lift: false` is there for the\n * morph that genuinely wants to stay in its layer.\n */\n readonly lift?: boolean;\n /**\n * Told `true` when this element starts morphing and `false` when it\n * has arrived — or when it leaves mid-flight, so nothing is left\n * holding a state that will never be cleared.\n *\n * The `true` comes the moment the element takes a name something\n * else held, which is during reconciliation and before the frame\n * that first draws it. That is deliberately earlier than the FLIP,\n * which needs the first layout: paint order is sorted during layout,\n * so a `zIndex` written in answer to this has to be in place before\n * that layout runs, or the first frame of the morph is drawn in the\n * old order.\n *\n * It exists for one thing, and stacking is the reason. A morphing\n * element is bigger than its resting self for most of the way, so it\n * overlaps whatever sits beside it, and it has to be drawn *over*\n * that rather than under it. Raising the element itself is rarely the\n * answer: what usually has to rise is an ancestor — the card the\n * morphing background belongs to, not the background — and a modifier\n * cannot reach an ancestor, nor should it, because which ancestor and\n * how far are questions about a layout that only the layout knows.\n *\n * const morphing = internalState(false);\n * <button zIndex={morphing.pipe(map(m => (m ? 1 : 0)))}>\n * <box modifiers={[sharedElement({ name, onMorph: at => (morphing.value = at) })]} />\n *\n * A browser has no equivalent because it does not need one: its\n * named elements are lifted out of the page into a layer above\n * everything, so nothing they might overlap is even in the picture.\n * Gesso morphs the real node, which stays exactly where it is in the\n * tree — the same trade that makes the morph interruptible and free\n * of a raster.\n */\n readonly onMorph?: (morphing: boolean) => void;\n}\n\n/**\n * Makes an element that appears under a name continue from wherever\n * the element that had that name was standing.\n *\n * ```tsx\n * <Image modifiers={[sharedElement({ name: 'playlist-image' })]} />\n * ```\n *\n * The departing element is hidden the moment the name changes hands,\n * because the arriving one is about to be drawn exactly over it, and\n * two copies of one thing reads as a double image rather than as a\n * transition.\n */\nexport const sharedElement = defineModifier<SharedElementArgs>({\n name: 'sharedElement',\n attach(host, args) {\n const controller = new SharedElementController(host, args);\n controller.attach();\n host.own(() => controller.detach());\n }\n});\n\nclass SharedElementController {\n private readonly layer: MotionLayer;\n /** Where the previous holder of the name stood, until it is used. */\n private claimed: LayoutBox | null = null;\n /**\n * Hides the element this one is replacing.\n *\n * Held rather than called on arrival: see `UiSharedElements.claim`.\n * Between claiming a name and taking the departing element's place\n * there is at least one layout, and for a geometry morph a whole\n * frame — and hiding it up front makes that a frame with neither\n * element on screen, which reads as a flash of the page behind them.\n */\n private yieldPrevious: (() => void) | null = null;\n private yielded = false;\n /** Whether a morph is running, so `onMorph` is never told twice. */\n private morphing = false;\n /** Set for the one frame a geometry morph spends being measured. */\n private geometryTarget: LayoutBox | null = null;\n\n constructor(\n private readonly host: UiModifierHost,\n private readonly args: SharedElementArgs\n ) {\n this.layer = new MotionLayer(host);\n }\n\n attach(): void {\n const registry = this.host.shared;\n if (registry === null) {\n return;\n }\n const claim = registry.claim(\n this.args.name,\n this.host.node,\n () => this.yieldName(),\n () => this.unyield()\n );\n this.claimed = claim.box;\n this.yieldPrevious = claim.yieldPrevious;\n if (claim.box !== null) {\n // Said now, during reconciliation, and not from the first layout\n // as the FLIP itself is. `onMorph` exists so an ancestor can lift\n // the morphing element over its neighbours with a `zIndex`, and\n // paint order is sorted *during* layout from that property: a\n // write made from the `onLayout` callback lands after the sort\n // and is not seen until the next frame lays out. Measured in the\n // transitions example on the frame after Back: the card whose\n // background was shrinking from the page still had `zIndex` 0\n // and painted at its tree position, so the card below it, painted\n // later, covered the background for that one frame while the FLIP\n // transform drew it over the whole page. The claim already says a\n // morph is coming, so this is the earliest it can be said, and it\n // gives the raise the same frame the FLIP has.\n this.beginMorph();\n }\n const box = this.host.layoutBox();\n if (box !== null) {\n registry.report(this.args.name, this.host.node, box);\n }\n this.host.onLayout(() => this.handleLayout());\n }\n\n detach(): void {\n // Whatever this element was replacing must not be left hidden\n // because this one went away before it ever took its place — and\n // whatever was raised for this morph must not be left raised\n // because the morph never finished.\n this.endMorph();\n this.takeOver();\n this.host.shared?.release(this.args.name, this.host.node);\n this.layer.release();\n }\n\n private beginMorph(): void {\n if (this.morphing) {\n return;\n }\n this.morphing = true;\n // Written here, at the claim, for the reason `onMorph` is told\n // here: `lift` is read while the tree is laid out, and this frame's\n // layout is the one the morph's first frame is drawn from.\n if (this.args.lift !== false) {\n this.host.set('lift', true);\n }\n // An element in flight is scenery, not a target. It is somewhere it\n // does not belong, it is there for a few hundred milliseconds, and\n // for most of that it is larger than its resting self and covering\n // its neighbours: a press that lands on it was aimed at whatever it\n // is passing over. Pressing a second card while the first was still\n // flying home opened the first one again, which is what this stops,\n // and it is why a browser's view-transition pseudo-elements take no\n // pointer events either.\n this.host.set('pointerEvents', 'none');\n this.args.onMorph?.(true);\n }\n\n private endMorph(): void {\n if (!this.morphing) {\n return;\n }\n this.morphing = false;\n if (this.args.lift !== false) {\n this.host.clear('lift');\n }\n this.host.clear('pointerEvents');\n this.args.onMorph?.(false);\n }\n\n /**\n * This element is now standing where the one it replaces was, so\n * that one may finally disappear.\n */\n private takeOver(): void {\n const yieldPrevious = this.yieldPrevious;\n this.yieldPrevious = null;\n yieldPrevious?.();\n }\n\n /**\n * Another node has taken the name. Get out of the way at once.\n *\n * Not an animation: the arriving element is about to be drawn from\n * this element's box, and anything but an instant disappearance puts\n * two of the same thing on screen at the same time.\n */\n private yieldName(): void {\n if (this.yielded) {\n return;\n }\n this.yielded = true;\n // Whatever this element was in the middle of is over: something\n // else holds the name now and this one is about to be invisible.\n // Said before the snap, because the snap is what cancels the\n // animation this morph was waiting on — `animateTo`'s callback\n // fires only for the run that is still current, so a morph\n // interrupted here would never report that it had finished, and\n // the raise (`onMorph`) and the `lift` it asked for would both be\n // left switched on for the life of the element. Visible as a card\n // from an earlier transition painting over the next screen.\n this.endMorph();\n this.layer.snapTo({ ...MOTION_REST, opacity: 0 });\n }\n\n /**\n * The name has come back, so this element does too.\n *\n * Stepping aside used to be a one-way door, and an interrupted\n * transition walked straight into it. A route change keeps the\n * departing screen alive for the length of its exit, so pressing Back\n * part-way through returns to a screen whose element has already\n * yielded; the arriving element then leaves without anything\n * replacing it, and the only copy left is the invisible one. The\n * picture simply disappeared.\n *\n * A snap rather than a fade, for the reason the yield is a snap: the\n * element it was standing aside for is going away this frame, and\n * anything gradual puts a gap between the two where the picture is\n * half there.\n */\n private unyield(): void {\n if (!this.yielded) {\n return;\n }\n this.yielded = false;\n this.layer.snapTo(MOTION_REST);\n }\n\n private handleLayout(): void {\n // The **visible** box, not the flow box — the opposite of what\n // `animateLayout` reads, and for the opposite reason. A layout\n // animation must not mistake a scroll for a move, so it watches\n // where a node sits in the flow. A morph is a visual continuation\n // of what a person is looking at: an element halfway down a\n // scrolled list should be picked up from where it appears on\n // screen, not from where it would be if the list were at the top.\n // With an unscrolled list the two are the same, which is exactly\n // why reading the wrong one looked correct for a while.\n const box = this.host.layoutBox();\n if (box === null) {\n return;\n }\n this.host.shared?.report(this.args.name, this.host.node, box);\n this.layer.measure(this.host.layoutBox());\n if (this.geometryTarget !== null) {\n // The frame after the geometry override landed: the element is\n // genuinely laid out at the departing element's box now, so the\n // transform that was standing in for it comes off. Both draw the\n // same rectangle, so there is nothing to see at the changeover.\n this.geometryTarget = null;\n this.layer.snapTo(MOTION_REST);\n return;\n }\n const from = this.claimed;\n if (from === null) {\n return;\n }\n // The first layout after arriving: both boxes are known now, so the\n // delta is known, and this is the only frame on which it is worth\n // computing.\n this.claimed = null;\n if (box.width <= 0 || box.height <= 0 || from.width <= 0 || from.height <= 0) {\n // Nothing to morph from or into, so whatever was raised at the\n // claim comes down again.\n this.layer.snapTo(MOTION_REST);\n this.takeOver();\n this.endMorph();\n return;\n }\n const timing = this.timing();\n // FLIP, with the pivot at the node's own centre — which is why the\n // offset is between centres and the scale is a plain ratio of\n // sizes, with nothing else to correct for.\n const scaleX = from.width / box.width;\n const scaleY = from.height / box.height;\n const uniform = this.args.scale === 'uniform';\n const flip: ResolvedMotionState = {\n opacity: this.args.fadeFrom ?? MOTION_REST.opacity,\n x: from.x + from.width / 2 - (box.x + box.width / 2),\n y: from.y + from.height / 2 - (box.y + box.height / 2),\n scaleX,\n scaleY: uniform ? scaleX : scaleY,\n rotate: 0\n };\n // The transform lands *this* frame — `onLayout` runs after the boxes\n // are final and before anything paints from them — so the element is\n // drawn over the departing one straight away and that one can step\n // aside with nothing in between. The morph itself was announced at\n // the claim (see `attach`), because a raise made in answer to it has\n // to be in this frame's layout, which has already run by now.\n this.layer.snapTo(flip);\n this.takeOver();\n if (this.args.morph === 'geometry') {\n // A geometry morph cannot land until the next frame, because it\n // writes `left`/`top`/`width`/`height` and this frame's layout has\n // already run. The transform above is what covers that frame: it\n // draws the element at exactly the box the geometry is about to\n // give it, and comes off as soon as it has. Hiding the element for\n // that frame instead — which is what this did first — is a frame\n // with neither the departing element nor the arriving one on\n // screen, and it reads as the page flashing through.\n this.morphGeometry(from, box, timing);\n return;\n }\n this.layer.animateTo(MOTION_REST, timing, () => this.endMorph());\n }\n\n /**\n * A duration by default, not a spring — which is the opposite of what\n * this started as, and a measurement is why.\n *\n * A spring's shape does not change with distance: it covers the same\n * *proportion* of the move in the same time whatever the move is. A\n * `snappy` morph over 676px was measured stepping 14, 44, 62, 69, 71,\n * 68 pixels a frame — half the distance in about 165ms, then the last\n * third at a crawl. A card near the middle of a list has a hundred\n * pixels to travel and that reads as a gentle expansion; the same\n * card near the edge of the screen has six hundred, and the identical\n * curve reads as a jump to the middle followed by a slow settle.\n *\n * A fixed duration spends the same time on the move however far it\n * goes, so the whole path is visible and the eye can follow it. It is\n * also what the browser's own View Transitions do, and why a morph\n * there looks like travel rather than a cut.\n *\n * A spring is still right for a movement that follows a gesture,\n * where the distance is whatever the finger did and there is a real\n * velocity to carry — so `spring` remains available per call site,\n * and naming one still turns the duration off.\n */\n private timing(): MotionTiming {\n const timed = this.args.spring === undefined;\n return {\n spring: this.args.spring,\n duration: timed ? (this.args.duration ?? 'slow') : this.args.duration,\n easing: timed ? (this.args.easing ?? 'standard') : this.args.easing,\n delay: this.args.delay,\n reducedMotion: this.args.reducedMotion\n };\n }\n\n /**\n * The exact morph: the element's own box animates, so its corners,\n * its border and everything laid out inside it are re-resolved every\n * frame rather than stretched.\n *\n * **It is offsets, not coordinates.** The measured boxes are in the\n * *world*, and `left`/`top` place an element inside its containing\n * block; writing one into the other puts the element wherever its\n * containing block happens to start, which is a bug that looks like a\n * layout bug. So what is animated is the difference between the two\n * boxes, added to whatever the element declared — the same arithmetic\n * `animateLayout` does, and correct whatever the containing block is.\n *\n * The difference is measured in *visible* coordinates while\n * `left`/`top` are pre-scroll, and those agree because a scroll is a\n * constant translation: a delta is the same number in either frame,\n * as long as the arriving element's own scroll offset does not change\n * while the morph runs. It does not — a screen arrives at whatever\n * offset it was restored to and stays there.\n *\n * **The element must place itself with `left`/`top` and size itself\n * with `width`/`height`.** An absolutely positioned element pinned\n * by insets (`left` *and* `right`) has no width of its own to\n * animate, and writing one on top of both is over-constrained: the\n * result depends on which of the three the engine chooses to ignore.\n * That is refused rather than guessed at.\n */\n private morphGeometry(from: LayoutBox, to: LayoutBox, timing: MotionTiming): void {\n const position = this.host.get<string | undefined>('position');\n if (position !== 'absolute' && position !== 'fixed') {\n warnGeometryNeedsPositioning(this.args.name, position, 'position');\n this.layer.snapTo(MOTION_REST);\n this.takeOver();\n this.endMorph();\n return;\n }\n const baseLeft = numberOrZero(this.host.get<unknown>('left'));\n const baseTop = numberOrZero(this.host.get<unknown>('top'));\n if (baseLeft === null || baseTop === null) {\n warnGeometryNeedsPositioning(this.args.name, position, 'offsets');\n this.layer.snapTo(MOTION_REST);\n this.takeOver();\n this.endMorph();\n return;\n }\n this.geometryTarget = to;\n const geometry = new GeometryMorph(this.host, {\n left: baseLeft + from.x - to.x,\n top: baseTop + from.y - to.y,\n width: from.width,\n height: from.height\n });\n const targets = { left: baseLeft, top: baseTop, width: to.width, height: to.height };\n let outstanding = 0;\n let settled = 0;\n const done = (): void => {\n settled++;\n if (settled === outstanding) {\n // Back where it belongs: the overrides are dropped rather than\n // left holding the values they arrived at, so the element goes\n // on being laid out by what it declared. Leaving them behind\n // would freeze a card at the size the window happened to be.\n geometry.release();\n this.endMorph();\n }\n };\n for (const field of GEOMETRY_FIELDS) {\n outstanding++;\n const cell = geometry.cell(field);\n const to = targets[field];\n const values =\n timing.spring !== undefined\n ? this.host.spring(cell, to, {\n spring: springSpec(timing.spring),\n delay: timing.delay,\n reducedMotion: timing.reducedMotion,\n // Half a pixel is the same pixel on a 1x screen;\n // scheduling frames past it buys a movement nobody can\n // see.\n restDelta: 0.5\n })\n : this.host.animate(cell, to, {\n duration: resolveDuration(timing.duration),\n easing: resolveEasing(timing.easing),\n delay: timing.delay,\n reducedMotion: timing.reducedMotion\n });\n values.subscribe({ complete: done });\n }\n }\n}\n\nconst GEOMETRY_FIELDS = ['left', 'top', 'width', 'height'] as const;\n\ntype GeometryField = (typeof GEOMETRY_FIELDS)[number];\n\n/**\n * The four numbers a geometry morph drives, written as one override\n * set.\n *\n * Written through `host.set` like every other modifier write, so\n * `release` — and detaching, and the node leaving — all hand the\n * element back exactly what it declared. That matters more here than\n * anywhere else, because what it declared is where the element\n * actually belongs.\n */\nclass GeometryMorph {\n constructor(\n private readonly host: UiModifierHost,\n private readonly state: Record<GeometryField, number>\n ) {\n this.write();\n }\n\n cell(field: GeometryField): AnimatedCell<number> {\n // Read and written through closures rather than through `this`,\n // because a cell is a property pair on an object literal and an\n // object literal's `this` is the literal.\n const read = (): number => this.state[field];\n const write = (next: number): void => {\n this.state[field] = next;\n this.write();\n };\n return {\n get value(): number {\n return read();\n },\n set value(next: number) {\n write(next);\n }\n };\n }\n\n release(): void {\n for (const field of GEOMETRY_FIELDS) {\n this.host.clear(field);\n }\n }\n\n private write(): void {\n for (const field of GEOMETRY_FIELDS) {\n this.host.set(field, this.state[field]);\n }\n }\n}\n\n/** A plain number, or null for a typed length this cannot add pixels to. */\nfunction numberOrZero(value: unknown): number | null {\n if (value === undefined || value === null) {\n return 0;\n }\n return typeof value === 'number' && Number.isFinite(value) ? value : null;\n}\n\nconst warnedGeometry = new Set<string>();\n\nfunction warnGeometryNeedsPositioning(\n name: string,\n position: string | undefined,\n reason: 'position' | 'offsets'\n): void {\n if (warnedGeometry.has(reason)) {\n return;\n }\n warnedGeometry.add(reason);\n if (reason === 'position') {\n console.warn(\n `sharedElement('${name}') asked for morph: 'geometry' on a '${position ?? 'static'}' element, and did ` +\n `nothing. A geometry morph animates left, top, width and height, which place an absolutely positioned ` +\n `element but only offset one in the flow. Position the element, or use the default morph: 'transform'.`\n );\n return;\n }\n console.warn(\n `sharedElement('${name}') asked for morph: 'geometry' on an element whose 'left' or 'top' is a typed length ` +\n `(percent, fr, auto), and did nothing. The morph adds pixels to what the element declared, and there is no ` +\n `pixel value to add to. Give it numeric offsets and a width and height, or use morph: 'transform'.`\n );\n}\n","import { linear } from '../animation/UiEasing';\nimport type { AnimatedCell } from '../animation/UiAnimation';\nimport type { VideoPlayback, VideoResolver } from '../media/VideoResolver';\nimport { defineModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\nexport interface VideoSourceArgs {\n readonly resolver: VideoResolver;\n readonly source: string;\n /** Start from the beginning again when it ends. Defaults to true. */\n readonly loop?: boolean;\n /** Start playing as soon as it is ready. Defaults to true. */\n readonly autoplay?: boolean;\n /** Told when the video loads, starts or fails. */\n readonly onState?: (state: 'loading' | 'playing' | 'failed', error?: unknown) => void;\n}\n\n/**\n * Plays a video onto the node's `video` property.\n *\n * A modifier rather than a subscription in a component body for the\n * reason `imageSource` is one, only more so: what it holds is a\n * decoder and a queue of decoded frames, which is the most expensive\n * thing in this framework to leak. `host.own` releases it inside\n * `removeSubtree`, so a node that leaves stops decoding on the frame\n * it goes.\n *\n * **Time comes from the animation driver, not from a timer.** The\n * playback is a pure function of a position, and the position is\n * driven by a repeating linear tween over the video's duration — an\n * ordinary animation on an ordinary cell. So a video is scheduled by\n * the same `ticks` phase as everything else, keeps its own frames\n * coming through the same `nextTickAt`, drops frames rather than\n * falling behind under load, and stops dead when the runtime does.\n * Nothing new had to be added to the runtime to play a video.\n *\n * **It keeps moving under reduced motion**, and that is deliberate.\n * The rule this framework applies (`UiReducedMotionPolicy`) is to stop\n * only when standing still would not state something false, and a\n * video that has frozen on its first frame is a video that has\n * finished loading badly. The media tier makes the same call for the\n * `Spinner`. An app that wants a still under reduced motion passes\n * `autoplay: false` and decides for itself.\n *\n * **Playback is shared by source, not by node.** The resolver\n * reference-counts, so two nodes pointed at one file watch one\n * playback — which is how a video survives a route change: the\n * arriving node resolves the source the departing node still holds and\n * picks it up mid-stream. The reference DOM implementation of this\n * effect has to physically move its `<video>` element into the new\n * document to get the same result.\n *\n * **Picking it up mid-stream needs the offset, and that took a bug to\n * learn.** The decoder is shared, but the tween that drives it belongs\n * to the node, and a node that has just been built starts its tween at\n * zero. During a route transition both screens are mounted at once, so\n * for the length of the exit two tweens drove one playback: one at\n * wherever the clip had got to, one at nearly zero. Each disagreement\n * larger than the seek tolerance is a seek backwards, which drops the\n * frame queue and reconfigures the decoder — about fourteen times\n * across a 240ms transition, on the same thread as layout and paint.\n * The video appeared to freeze, the morph juddered, and when the old\n * screen finally left, the clip carried on from the beginning.\n *\n * So the position this node presents is its own tween plus whatever\n * the playback had already reached when it resolved. Both holders then\n * agree to within a frame, no seek fires, and the continuity this\n * comment claims is actually true.\n */\nexport const videoSource = defineModifier<VideoSourceArgs>({\n name: 'videoSource',\n attach(host, args) {\n load(host, args);\n },\n update(host, args, previous) {\n if (args.resolver === previous.resolver && args.source === previous.source) {\n return;\n }\n previous.resolver.release(previous.source);\n load(host, args);\n }\n});\n\nfunction load(host: UiModifierHost, args: VideoSourceArgs): void {\n let live = true;\n let playback: VideoPlayback | null = null;\n let stopErrors: (() => void) | null = null;\n let positionMs = 0;\n /**\n * Where the playback already was when this element joined it, so the\n * tween's zero means \"here\" rather than \"the start of the file\".\n */\n let offsetMs = 0;\n /** The clip's length, known only once it has been read. */\n let durationMs = 0;\n args.onState?.('loading');\n host.clear('video');\n\n // The cell the driver writes: setting it presents whichever frame is\n // due at that position, and asks for a paint only when that actually\n // changed the picture. A video at 30 Hz inside an app at 60 therefore\n // paints thirty times a second, not sixty.\n const position: AnimatedCell<number> = {\n get value(): number {\n return positionMs;\n },\n set value(next: number) {\n positionMs = next;\n if (playback === null) {\n return;\n }\n // Wrapped rather than clamped: the tween runs 0 → duration and\n // repeats, and the offset slides where in the clip that lands.\n const at = durationMs > 0 ? (next + offsetMs) % durationMs : next;\n if (playback.present(at)) {\n host.requestFrame();\n }\n }\n };\n\n args.resolver\n .resolve(args.source)\n .then(resolved => {\n if (!live) {\n return;\n }\n playback = resolved;\n durationMs = Math.max(1, resolved.duration * 1000);\n // Whatever it had already reached, which is zero for a playback\n // nobody was holding and mid-clip for one this element is\n // joining part-way through a transition.\n offsetMs = resolved.positionMs;\n host.set('video', resolved.surface);\n stopErrors = resolved.onError(error => args.onState?.('failed', error));\n args.onState?.('playing');\n if (args.autoplay === false) {\n // Not playing, but the first frame is worth having: a paused\n // video showing nothing looks like one that failed. Its own\n // position rather than zero, so a still that joins a playing\n // holder does not seek it back to the start.\n resolved.present(offsetMs);\n host.requestFrame();\n return;\n }\n host.animate(position, durationMs, {\n duration: durationMs,\n easing: linear,\n repeat: args.loop !== false,\n reducedMotion: 'keep',\n // The video's own rate, which is what keeps a playing video\n // from pinning the whole application to the frame clock. A\n // tween with no `stepMs` asks to be sampled every frame; that\n // was survivable while frames were a fixed sixty and is not\n // once they follow the display, where a 30fps clip would wake\n // a 165Hz app 165 times a second to present the same picture\n // five times running. Declaring the rate instead lets\n // `AnimationDriver.nextTickAt` do what it is for: the earliest\n // moment *anybody* wants a frame, so an animation starting\n // beside this video raises the rate for as long as it runs and\n // the page falls back to the video's cadence when it settles.\n // Passed through rather than floored: a playback that reports\n // no interval is saying it does not know its own rate, and\n // zero already means \"every frame\" to the driver. The MP4 path\n // never reports zero — an unreadable sample table falls back to\n // sixty — so the floor only ever hid what a custom playback\n // meant.\n stepMs: resolved.frameDurationMs\n });\n })\n .catch((error: unknown) => {\n if (live) {\n args.onState?.('failed', error);\n }\n });\n\n host.own(() => {\n live = false;\n host.stopAnimation(position);\n stopErrors?.();\n args.resolver.release(args.source);\n });\n}\n","import { linear } from '../animation/UiEasing';\nimport type { AnimatedCell } from '../animation/UiAnimation';\nimport { defineModifier } from './UiModifier';\n\n/**\n * How long the offset has to hold still before a scroll counts as\n * over. Long enough to bridge a gap between two wheel notches, short\n * enough that a value persisted afterwards is not noticeably late.\n */\nconst SETTLE_MS = 120;\n\n/** How far a scroll container has been scrolled, in logical pixels. */\nexport interface ScrollOffset {\n readonly x: number;\n readonly y: number;\n}\n\nexport interface ScrollPositionArgs {\n /**\n * Told whenever this container's scroll offset changes, and only\n * then — not when the container merely moved.\n *\n * \"Not on every frame\" used to be part of this promise, and a\n * smoothed wheel ended it: an animated scroll changes the offset on\n * every frame it runs, so one notch is now ten or twenty calls\n * rather than one. That is the honest report of what happened, and\n * it is what a scroll-linked header or a position indicator wants.\n * A consumer that only cares where the scrolling *stopped* wants\n * `onSettled` instead.\n */\n readonly onChange?: (offset: ScrollOffset) => void;\n /**\n * Told once when the offset stops changing.\n *\n * What persisting a position wants — a route remembering where its\n * list was — since writing that on every frame of every notch is a\n * great deal of writing for one answer nobody reads until later.\n *\n * \"Stops\" is measured by a frame passing with the offset unchanged,\n * because this modifier is built on layout notifications and has no\n * view of whether an animation is running. That makes it a frame\n * late by construction, which for a value read after the fact is\n * free.\n */\n readonly onSettled?: (offset: ScrollOffset) => void;\n}\n\n/**\n * Reports a scroll container's offset whenever it changes.\n *\n * The counterpart of `measure`, and the answer to \"how does an\n * application find out where a list is scrolled to\". It exists because\n * nothing else could tell you: a wheel writes the container's offset\n * from inside the runtime, and so does a scrollbar drag, a trackpad\n * pan, and the `scrollIntoView` that keeps a focused row on screen.\n *\n * **Why this is not a scroll event.** Two reasons, and the second is\n * the one that decided it.\n *\n * An event would come from the input stack, and only some of the causes\n * above are input — a list scrolled because focus moved into a row\n * below the fold has scrolled just as much, and an `onScroll` that\n * stayed quiet for it would be a trap. To fire for all of them the\n * event would have to come from the layout phase, at which point it is\n * not an input event at all.\n *\n * And Gesso already has exactly one mechanism for \"this node's layout\n * changed on this frame\" — `LayoutNotifier`, reached through\n * `host.onLayout` — which this is built on. A second mechanism carrying\n * the same fact is the mistake `animateLayout` records not making when\n * it read `LayoutNotifier` rather than inventing a way to keep previous\n * boxes.\n *\n * **Restoring an offset is not this modifier's job**, and that\n * asymmetry is deliberate. `scrollY` is an ordinary property, so it is\n * put back the ordinary way — bind it, `<scrollview scrollY={cell}>`,\n * and a container built again after a route change is laid out where it\n * was left, before it paints. A modifier could not do it anyway: a\n * modifier writes through the override cascade, the runtime's own\n * scrolling writes the element's declared value, and an override would\n * shadow every wheel for the life of the node.\n *\n * const at = internalState(0);\n * <scrollview scrollY={at} modifiers={[scrollPosition({ onChange: o => (at.value = o.y) })]}>\n *\n * The offset reported is the container's **effective** one, taken from\n * the layout record: a wheel writes the property unclamped and the\n * engine clamps it to the content on the next layout, so the property\n * can name a place the list never actually went.\n */\nexport const scrollPosition = defineModifier<ScrollPositionArgs>({\n name: 'scrollPosition',\n attach(host, args) {\n let last: ScrollOffset | null = null;\n /**\n * The settle detector: a short animation restarted on every\n * change, whose completion means nothing changed while it ran.\n *\n * A frame that reports nothing cannot be observed here —\n * `onLayout` fires on *change*, which is the whole point of it —\n * and the last frame of a scroll is a frame where the offset\n * changed, with nothing armed after it. So the end of a scroll has\n * to be waited for rather than noticed, and this waits on the\n * framework's own clock rather than a timer: it is cancelled with\n * the node, it stops when the runtime does, and a reduced-motion\n * app snaps it, which reports the settle immediately and is\n * exactly right.\n */\n let settling = 0;\n const settleCell: AnimatedCell<number> = {\n get value(): number {\n return settling;\n },\n set value(next: number) {\n settling = next;\n }\n };\n const armSettle = (offset: ScrollOffset): void => {\n settling = 0;\n host.animate(settleCell, 1, { duration: SETTLE_MS, easing: linear }).subscribe({\n complete: () => {\n if (settling === 1) {\n args.onSettled?.(offset);\n }\n }\n });\n };\n host.onLayout(() => {\n const offset = host.scrollOffset();\n if (offset === null || (last !== null && last.x === offset.x && last.y === offset.y)) {\n return;\n }\n last = offset;\n args.onChange?.(offset);\n if (args.onSettled !== undefined) {\n armSettle(offset);\n }\n });\n }\n});\n","import { UiEnvironmentKeys } from '../environment/UiEnvironmentKeys';\nimport { UiInsetRegistry, type UiInsetEdge, type UiInsets } from '../environment/UiInsets';\nimport { defineModifier } from './UiModifier';\n\nexport interface PublishInsetArgs {\n /** Which edge this node is over. */\n readonly edge: UiInsetEdge;\n /**\n * How much space to claim, when the node's own box is not the\n * answer. Left out, the node publishes its measured height on a\n * horizontal edge and its width on a vertical one, which is what a\n * bar sitting across the bottom of a screen wants.\n */\n readonly extent?: number;\n}\n\n/**\n * Publishes the space this node occupies to the screens behind it.\n *\n * A floating bar, a toast rail, a docked player: anything drawn over\n * the content rather than in it takes room that the content has to be\n * kept clear of, and before this every screen worked that room out for\n * itself. In Segue seven screens each imported the queue channel, each\n * asked whether anything was playing, and each added the bar's height\n * from a constant, which is seven copies of a fact one component\n * already knew, and two more screens that used a different number.\n *\n * The bar publishes; the screens read. Neither knows about the other,\n * which is what makes it possible to add a second bar without editing\n * a screen.\n *\n * The contribution is retracted when the modifier detaches, so a bar\n * inside a `Presence` gives its room back as it leaves. It is\n * republished whenever the node's box changes, so a bar that grows\n * moves the content with it.\n *\n * Publishes into the nearest `UiInsetRegistry` the environment\n * carries. An application provides one on its root with the `insets`\n * prop; without one the registry the key defaults to is used, which\n * works for a single-window runtime and is what a spec gets.\n */\nexport const publishInset = defineModifier<PublishInsetArgs>({\n name: 'publishInset',\n attach(host, args) {\n const source = host.environment(UiEnvironmentKeys.insets);\n if (!(source instanceof UiInsetRegistry)) {\n // A source that is not a registry is somebody's read-only view of\n // one; there is nothing to publish into and nothing useful to do\n // about it beyond leaving the content where it is.\n return;\n }\n let write: ((next?: Partial<UiInsets>) => void) | null = null;\n const publish = (): void => {\n const box = host.layoutBox();\n if (box === null) {\n return;\n }\n const extent = args.extent ?? (args.edge === 'top' || args.edge === 'bottom' ? box.height : box.width);\n const insets = { [args.edge]: extent } as Partial<UiInsets>;\n if (write === null) {\n write = source.publish(insets);\n } else {\n write(insets);\n }\n };\n host.onLayout(publish);\n publish();\n host.own(() => {\n write?.();\n write = null;\n });\n }\n});\n\nexport interface InsetPaddingArgs {\n /**\n * Padding to keep on each edge in addition to whatever is published\n * there. An edge left out is not written at all, so the element's\n * own padding on it stands.\n */\n readonly top?: number;\n readonly right?: number;\n readonly bottom?: number;\n readonly left?: number;\n}\n\n/**\n * Keeps a scrolling column clear of whatever is floating over it.\n *\n * The reading half of `publishInset`. `insetPadding({ bottom: 40 })`\n * is forty pixels of padding plus however much the bars, the keyboard\n * and the platform's safe area are currently taking at the bottom, and\n * it follows all three as they change.\n *\n * It writes `paddingBottom` and friends, so it composes with an\n * element that also sets `paddingX`: only the edges named here are\n * written.\n *\n * Physical edges rather than logical ones, because these are physical\n * facts. A soft keyboard is at the bottom of the screen in every\n * language, and a notch does not move when the reading does.\n */\nexport const insetPadding = defineModifier<InsetPaddingArgs>({\n name: 'insetPadding',\n attach(host, args) {\n const edges = (['top', 'right', 'bottom', 'left'] as const).filter(edge => args[edge] !== undefined);\n if (edges.length === 0) {\n return;\n }\n const properties: Record<UiInsetEdge, string> = {\n top: 'paddingTop',\n right: 'paddingRight',\n bottom: 'paddingBottom',\n left: 'paddingLeft'\n };\n const apply = (insets: UiInsets): void => {\n for (const edge of edges) {\n host.set(properties[edge], (args[edge] ?? 0) + insets[edge]);\n }\n };\n const source = host.environment(UiEnvironmentKeys.insets);\n let subscription = source.changes.subscribe(apply);\n // The registry is an environment value, so a subtree moved under a\n // different provider reads a different one. Rare, and cheap to be\n // right about: `onEnvironment` is exactly the notification a\n // modifier holding a value out of the environment needs.\n host.onEnvironment(() => {\n subscription.unsubscribe();\n subscription = host.environment(UiEnvironmentKeys.insets).changes.subscribe(apply);\n });\n host.own(() => subscription.unsubscribe());\n }\n});\n","import type { Subject } from 'rxjs';\n\nimport { UiEnvironmentKeys } from '../environment/UiEnvironmentKeys';\nimport { themeExtension, type UiThemeExtension } from '../environment/UiThemeExtension';\nimport { defineModifier, type UiModifier } from './UiModifier';\n\n/**\n * What the modifier needs: which group to read, and where to put it.\n *\n * A plain object, so `sameArgs` compares it structurally and the same\n * extension with the same sink is the same argument. The sink is a\n * class instance and the extension's `equals` is a function, so both\n * compare by identity, which is what makes a cell built once per\n * component instance stable for that instance's life.\n */\nexport interface ThemeTokenTarget<T extends object> {\n readonly extension: UiThemeExtension<T>;\n readonly sink: Subject<T>;\n}\n\n/**\n * Publishes a theme extension's tokens into a cell, and keeps\n * publishing them as the theme above the element changes.\n *\n * This is the answer to the thing `Button`'s docstring has said all\n * along: a component's body runs once, before its node is\n * in a tree, so it cannot read the environment and anything it\n * computes from the theme is frozen at the default. A colour escapes\n * that by being a name resolved at paint, and a radius now does too\n * — but a padding, a gap, a variant's choice of\n * token and an interaction's opacity have nowhere to be resolved late.\n *\n * A modifier does have the environment, so it reads the group there\n * and feeds a cell the component already bound its properties to. The\n * shape is `measure` and `ctx.bounds()`: a cell, and the modifier that\n * fills it.\n *\n * const tokens = themeTokenCell(controlTokens);\n * Box({ modifiers: [tokens.modifier], paddingX: tokens.select(t => t.pad) });\n *\n * The cell is seeded with the extension's declared defaults, so the\n * first read is a real value rather than an empty one, and attach\n * replaces it with whatever the element actually inherits before the\n * first frame is drawn.\n *\n * Unlike `host.set`, this reaches a *child*: the cell is an ordinary\n * Observable, so a component may bind its label's colour to it as\n * easily as its own padding. That matters because most controls are a\n * box with something inside it, and `color` does not cascade from a\n * parent node the way it would in CSS.\n */\nconst themeTokensKind = defineModifier<ThemeTokenTarget<object>>({\n name: 'themeTokens',\n attach(host, target) {\n const publish = (): void => {\n target.sink.next(themeExtension(host.environment(UiEnvironmentKeys.theme), target.extension));\n };\n publish();\n host.onEnvironment(publish);\n }\n});\n\nexport function themeTokens<T extends object>(target: ThemeTokenTarget<T>): UiModifier<ThemeTokenTarget<T>> {\n // The kind is declared over `object` because a modifier kind is one\n // value for every use of it, and an extension's `equals` takes `T`\n // in argument position, so `UiThemeExtension<ControlTokens>` is not\n // assignable to `UiThemeExtension<object>`. The cast is sound in the\n // only direction that runs: `attach` reads the sink and the\n // extension and hands one to the other, never mixing two groups.\n return themeTokensKind(target as unknown as ThemeTokenTarget<object>) as unknown as UiModifier<ThemeTokenTarget<T>>;\n}\n\n/** The kind itself, for a test or an inspector that matches on it. */\nthemeTokens.kind = themeTokensKind.kind;\n","import type { FontMetrics, TextMeasureRequest } from '../../layout/TextMeasurer';\nimport { ParagraphTextMeasurer } from '../../layout/TextMeasurer';\nimport { proportionalFontMetrics } from '../../layout/ParagraphLayout';\nimport type { Canvas2DContext } from './Canvas2DContext';\nimport { applyCanvasTextStyle, createCanvasTextStyle, runCanvasStyleInto } from '../TextRenderer';\n\nconst MAX_CACHE_ENTRIES = 8192;\n\n/**\n * Canvas-backed measurer shared by the layout engine and the\n * renderers, so both always agree on text size.\n *\n * Only the two primitives live here — run width and font metrics —\n * and both are cached by font. Line breaking asks for a run width per\n * candidate line, so the width cache is what keeps wrapping cheap:\n * a stable paragraph re-laid out at the same width costs no\n * measureText calls at all.\n *\n * The cache is a flat map with a crude size cap: when the cap is\n * hit it is cleared wholesale. Revisit with an LRU only if profiling\n * shows measureText dominating.\n */\nexport class CanvasTextMeasurer extends ParagraphTextMeasurer {\n private readonly widths = new Map<string, number>();\n private readonly metrics = new Map<string, FontMetrics>();\n /** One style object per measurer, refilled per call; see `runCanvasStyleInto`. */\n private readonly style = createCanvasTextStyle();\n\n constructor(private readonly context: Canvas2DContext) {\n super();\n }\n\n measureRunWidth(text: string, request: TextMeasureRequest): number {\n // The font is built the one way drawing builds it, and its key is\n // the width cache's. The context is only touched on a miss:\n // assigning `font` re-parses the string, and this is the hottest\n // path in text.\n const key = `${runCanvasStyleInto(request, undefined, this.style)}\\0${text}`;\n const cached = this.widths.get(key);\n if (cached !== undefined) {\n return cached;\n }\n applyCanvasTextStyle(this.context, this.style);\n const width = this.context.measureText(text).width;\n if (this.widths.size >= MAX_CACHE_ENTRIES) {\n this.widths.clear();\n }\n this.widths.set(key, width);\n return width;\n }\n\n /** Drops every cached width, metric and paragraph; see `TextMeasurer.invalidate`. */\n override invalidate(): void {\n super.invalidate();\n this.widths.clear();\n this.metrics.clear();\n }\n\n fontMetrics(request: TextMeasureRequest): FontMetrics {\n // Metrics are the font's own; tracking moves glyphs apart and does\n // not change how tall they are.\n const font = runCanvasStyleInto(request, undefined, this.style);\n this.style.letterSpacing = 0;\n const cached = this.metrics.get(font);\n if (cached !== undefined) {\n return cached;\n }\n applyCanvasTextStyle(this.context, this.style);\n const measured = this.context.measureText('Mg') as Partial<TextMetrics>;\n // fontBoundingBox* is the em box the browser lays lines out with.\n // Test doubles and very old engines lack it; fall back to the\n // proportions the deterministic measurer uses.\n const metrics =\n typeof measured.fontBoundingBoxAscent === 'number' && typeof measured.fontBoundingBoxDescent === 'number'\n ? { ascent: measured.fontBoundingBoxAscent, descent: measured.fontBoundingBoxDescent }\n : proportionalFontMetrics(request.fontSize);\n this.metrics.set(font, metrics);\n return metrics;\n }\n}\n","import type { UiNode } from '../graph/UiNode';\nimport type { LayoutEngine } from '../layout/LayoutEngine';\nimport { formatExplanation, formatNumber, labelNode } from '../layout/LayoutExplanation';\nimport type { LayoutExplanation } from '../layout/LayoutExplanation';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { Canvas2DContext } from './canvas2d/Canvas2DContext';\nimport { drawOverlayShapes, type OverlayShape } from './OverlayShapes';\n\n/** How long a measured node stays warm in the heatmap. */\nexport const INSPECTOR_HEAT_MS = 1500;\n/** For this long after being measured a node is drawn hot; then warm until it expires. */\nexport const INSPECTOR_HOT_MS = 500;\n\nconst HEAT_HOT_FILL = 'rgba(229, 83, 75, 0.35)';\nconst HEAT_WARM_FILL = 'rgba(229, 83, 75, 0.14)';\nconst HEAT_HOT_STROKE = 'rgba(229, 83, 75, 0.9)';\nconst HEAT_WARM_STROKE = 'rgba(229, 83, 75, 0.45)';\n\nconst HOVER_STROKE = 'rgba(76, 141, 255, 0.95)';\nconst CONTENT_FILL = 'rgba(76, 141, 255, 0.18)';\nconst PADDING_FILL = 'rgba(46, 168, 138, 0.35)';\nconst MARGIN_FILL = 'rgba(217, 155, 58, 0.3)';\nconst RELAYOUT_STROKE = 'rgba(168, 85, 247, 0.9)';\nconst DECORATION_STROKE = 'rgba(46, 168, 138, 0.95)';\nconst LABEL_FILL = 'rgba(13, 17, 23, 0.92)';\nconst LABEL_TEXT = '#e6edf3';\nconst LABEL_FONT_SIZE = 10;\nconst LABEL_FONT_FAMILY = 'ui-monospace, SFMono-Regular, Menlo, monospace';\nconst LABEL_FONT = `${LABEL_FONT_SIZE}px ${LABEL_FONT_FAMILY}`;\nconst LABEL_HEIGHT = 16;\n\n/** The overlay for one frame: its shapes, and when the heatmap next changes. */\nexport interface InspectorOverlay {\n shapes: OverlayShape[];\n /** Milliseconds until a node cools or expires, or undefined when nothing is warm. */\n nextChange: number | undefined;\n}\n\n/**\n * The layout inspector: what a developer sees when they\n * hover a node with inspection on.\n *\n * It keeps two things and paints them over a finished frame, on the\n * same context the scene was drawn to, so it works identically on the\n * main thread and inside a render worker:\n *\n * - the hovered node's margin, border, padding and content boxes,\n * with a label, and the outline of the relayout root a change to\n * it would be laid out from;\n * - a heatmap of the nodes the recent layout passes measured — hot\n * for `INSPECTOR_HOT_MS`, warm until `INSPECTOR_HEAT_MS` — which is\n * the frame-by-frame picture of what the budgets in\n * `LayoutEngine.budget.spec` count. Leaves are filled; containers\n * are only outlined, because a container's measure runs whenever\n * anything inside it changes (a page's scroller is measured on\n * every tick of an animated child) and filling it would tint the\n * whole canvas and hide the leaves that actually changed.\n *\n * Heat changes in two steps rather than a continuous fade: a fade needs\n * a repaint every few tens of milliseconds while anything is warm, and\n * a repeatedly re-measured node then pulses as its alpha decays and\n * snaps back — over a large box that reads as flicker. With steps, a\n * repaint is needed only when some node's step is due, and `paint`\n * reports when that is.\n *\n * Enabling it turns on `LayoutEngine.trace`, which is what fills the\n * list of measured nodes; disabling it turns the trace back off so a\n * running app pays nothing for the inspector it is not using.\n *\n * The inspector reads records and never computes geometry: boxes come\n * from `visibleBox`, clipped by the scroll containers above the node,\n * so a heatmap row that scrolled out of its list is not drawn over the\n * content below it.\n */\nexport interface LayoutInspectorOptions {\n /**\n * The modifiers attached to a node, in list order, so the label can\n * name them.\n *\n * Supplied rather than read, because a node has no backpointer to\n * its modifiers: the set lives in the builder, keyed by node, and\n * `packages/core/src/rendering` must not depend on the builder to\n * paint a debugging label. The runtime passes\n * `node => builder.modifiersFor(node)?.names ?? []`; a caller that\n * does not pass it gets an inspector that says nothing about\n * modifiers, which is what it said before B5.\n */\n readonly modifierNames?: (node: UiNode) => readonly string[];\n}\n\nexport class LayoutInspector {\n private enabled = false;\n private hovered: UiNode | null = null;\n /**\n * A node picked from outside the canvas — a devtools panel's tree —\n * and outlined here so a person can find it on the screen.\n *\n * Separate from `hovered` and from `enabled`: the panel points at a\n * node without the pointer being anywhere near it, and without the\n * heatmap and the measure trace that the toggle turns on.\n */\n private highlighted: UiNode | null = null;\n /** When each node was last measured, for the heatmap. */\n private readonly heat = new Map<UiNode, number>();\n\n constructor(\n private readonly engine: LayoutEngine,\n private readonly options: LayoutInspectorOptions = {}\n ) {}\n\n get isEnabled(): boolean {\n return this.enabled;\n }\n\n get hoveredNode(): UiNode | null {\n return this.hovered;\n }\n\n get highlightedNode(): UiNode | null {\n return this.highlighted;\n }\n\n /** Whether `overlay` would draw anything: the toggle is on, or a node is highlighted. */\n get hasOverlay(): boolean {\n return this.enabled || this.highlighted !== null;\n }\n\n /** Turns the inspector, and the engine's measure trace, on or off. */\n setEnabled(enabled: boolean): void {\n if (this.enabled === enabled) {\n return;\n }\n this.enabled = enabled;\n this.engine.trace = enabled;\n if (!enabled) {\n this.heat.clear();\n this.hovered = null;\n }\n }\n\n /** Sets the node under the pointer. Returns true when it changed. */\n setHovered(node: UiNode | null): boolean {\n if (node === this.hovered) {\n return false;\n }\n this.hovered = node;\n return true;\n }\n\n /** Sets the node a panel is pointing at. Returns true when it changed. */\n setHighlighted(node: UiNode | null): boolean {\n if (node === this.highlighted) {\n return false;\n }\n this.highlighted = node;\n return true;\n }\n\n /**\n * Records the nodes the layout pass that just ran measured. Call once\n * after each layout, with the frame's time.\n */\n recordLayout(now: number): void {\n if (!this.enabled) {\n return;\n }\n for (const node of this.engine.stats.measuredNodes) {\n this.heat.set(node, now);\n }\n }\n\n /** Nodes currently warm in the heatmap. */\n get heatCount(): number {\n return this.heat.size;\n }\n\n /** The hovered node's explanation, or null with nothing hovered. */\n explainHovered(): LayoutExplanation | null {\n return this.hovered === null ? null : this.engine.explain(this.hovered);\n }\n\n /** The hovered node's explanation as text, or null with nothing hovered. */\n explainHoveredText(): string | null {\n const explanation = this.explainHovered();\n return explanation === null ? null : formatExplanation(explanation);\n }\n\n /**\n * The overlay for a finished frame, as shapes either renderer draws\n * after the scene. Returns them with how many milliseconds until the\n * heatmap next changes (a node cooling from hot to warm, or\n * expiring), or undefined when nothing is warm.\n */\n overlay(now: number): InspectorOverlay {\n const shapes: OverlayShape[] = [];\n if (!this.hasOverlay) {\n return { shapes, nextChange: undefined };\n }\n const nextChange = this.enabled ? this.heatShapes(shapes, now) : undefined;\n // The highlighted node is drawn exactly as a hovered one is: the\n // box model is what a person picking a node from a tree wants to\n // see, and a second vocabulary for the same boxes would be one more\n // thing to learn. Once when they coincide.\n if (this.highlighted !== null) {\n this.hoveredShapes(shapes, this.highlighted);\n }\n if (this.enabled && this.hovered !== null && this.hovered !== this.highlighted) {\n this.hoveredShapes(shapes, this.hovered);\n }\n return { shapes, nextChange };\n }\n\n /**\n * Paints the overlay onto a 2D context left in logical pixels, as the\n * Canvas2D renderer leaves it. Returns what `overlay` reports as the\n * next change.\n */\n paint(ctx: Canvas2DContext, now: number): number | undefined {\n const { shapes, nextChange } = this.overlay(now);\n drawOverlayShapes(ctx, shapes);\n return nextChange;\n }\n\n private heatShapes(out: OverlayShape[], now: number): number | undefined {\n let nextChange: number | undefined;\n for (const [node, at] of this.heat) {\n const age = now - at;\n if (age >= INSPECTOR_HEAT_MS) {\n this.heat.delete(node);\n continue;\n }\n const hot = age < INSPECTOR_HOT_MS;\n const due = (hot ? INSPECTOR_HOT_MS : INSPECTOR_HEAT_MS) - age;\n nextChange = nextChange === undefined ? due : Math.min(nextChange, due);\n const box = this.clippedVisibleBox(node);\n if (box === null) {\n continue;\n }\n if (!node.hasChildren()) {\n out.push({ kind: 'fill', ...box, color: hot ? HEAT_HOT_FILL : HEAT_WARM_FILL });\n }\n out.push({ kind: 'stroke', ...box, color: hot ? HEAT_HOT_STROKE : HEAT_WARM_STROKE, lineWidth: 1 });\n }\n return nextChange;\n }\n\n private hoveredShapes(out: OverlayShape[], node: UiNode): void {\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n return;\n }\n const explanation = this.engine.explain(node);\n const relayoutRoot = explanation.relayout.root;\n if (relayoutRoot !== node) {\n const rootBox = this.clippedVisibleBox(relayoutRoot);\n if (rootBox !== null) {\n out.push({ kind: 'stroke', ...rootBox, color: RELAYOUT_STROKE, lineWidth: 2 });\n }\n }\n\n const box = this.engine.visibleBox(node);\n const strip = (x: number, y: number, width: number, height: number, color: string): void => {\n if (width > 0 && height > 0) {\n out.push({ kind: 'fill', x, y, width, height, color });\n }\n };\n // Margin strips around the border box.\n const marginWidth = box.width + rec.marginLeft + rec.marginRight;\n strip(box.x - rec.marginLeft, box.y - rec.marginTop, marginWidth, rec.marginTop, MARGIN_FILL);\n strip(box.x - rec.marginLeft, box.y + box.height, marginWidth, rec.marginBottom, MARGIN_FILL);\n strip(box.x - rec.marginLeft, box.y, rec.marginLeft, box.height, MARGIN_FILL);\n strip(box.x + box.width, box.y, rec.marginRight, box.height, MARGIN_FILL);\n // Padding strips inside it.\n const innerHeight = box.height - rec.paddingTop - rec.paddingBottom;\n strip(box.x, box.y, box.width, rec.paddingTop, PADDING_FILL);\n strip(box.x, box.y + box.height - rec.paddingBottom, box.width, rec.paddingBottom, PADDING_FILL);\n strip(box.x, box.y + rec.paddingTop, rec.paddingLeft, innerHeight, PADDING_FILL);\n strip(box.x + box.width - rec.paddingRight, box.y + rec.paddingTop, rec.paddingRight, innerHeight, PADDING_FILL);\n // Content box.\n strip(\n box.x + rec.paddingLeft,\n box.y + rec.paddingTop,\n box.width - rec.paddingLeft - rec.paddingRight,\n innerHeight,\n CONTENT_FILL\n );\n // Border box outline, drawn even for a zero-sized node so it can be found.\n out.push({ kind: 'stroke', ...box, color: HOVER_STROKE, lineWidth: 1 });\n // Decorations in their own colour, because the whole point of one\n // is that it is not the box: a focus ring sits outside the border\n // box and would otherwise read as the node being bigger than it is.\n for (const shape of node.decorations ?? []) {\n const outset = shape.outset ?? 0;\n out.push({\n kind: 'stroke',\n x: box.x + (shape.x ?? 0) - outset,\n y: box.y + (shape.y ?? 0) - outset,\n width: (shape.width ?? box.width) + outset * 2,\n height: (shape.height ?? box.height) + outset * 2,\n color: DECORATION_STROKE,\n lineWidth: 1\n });\n }\n\n const modifiers = this.options.modifierNames?.(node) ?? [];\n out.push({\n kind: 'label',\n box,\n text: `${labelNode(node)} ${formatNumber(box.width)}×${formatNumber(box.height)}${\n modifiers.length === 0 ? '' : ` · ${modifiers.join(', ')}`\n }`,\n font: LABEL_FONT,\n fontSize: LABEL_FONT_SIZE,\n fontFamily: LABEL_FONT_FAMILY,\n textColor: LABEL_TEXT,\n background: LABEL_FILL,\n height: LABEL_HEIGHT\n });\n }\n\n /**\n * Where a node is seen, intersected with every clipping ancestor's\n * visible box; null when nothing of it is visible.\n */\n private clippedVisibleBox(node: UiNode): LayoutBox | null {\n const box = this.engine.visibleBox(node);\n let x = box.x;\n let y = box.y;\n let right = box.x + box.width;\n let bottom = box.y + box.height;\n let clipped = false;\n for (let current = node.parent; current !== null; current = current.parent) {\n const rec = this.engine.recordFor(current);\n if (rec === undefined || !rec.clips) {\n continue;\n }\n const clip = this.engine.visibleBox(current);\n x = Math.max(x, clip.x);\n y = Math.max(y, clip.y);\n right = Math.min(right, clip.x + clip.width);\n bottom = Math.min(bottom, clip.y + clip.height);\n clipped = true;\n if (right <= x || bottom <= y) {\n return null;\n }\n }\n // An unclipped box is returned as is, so its sizes are the record's\n // exactly rather than a sum and a difference of them.\n return clipped ? { x, y, width: right - x, height: bottom - y } : box;\n }\n}\n","import type { UiImage } from '../properties/UiImage';\n\n/**\n * Turns a source into something a renderer can draw.\n *\n * `UiImage` is `ImageBitmap`, which both backends already take — the\n * Canvas2D renderer draws it and the WebGPU one uploads it through\n * `WebGPUTextureCache` — so the Media tier adds no renderer work at\n * all. What was missing was everything in front of it: fetching,\n * decoding, not decoding the same thing twice, and releasing what is\n * no longer on screen.\n *\n * An implementation is free to fetch and decode wherever it likes; the\n * default one below does it wherever it is constructed, which for a\n * Gesso app is the render worker.\n */\nexport interface ImageResolver {\n /**\n * The decoded bitmap for a source, or a rejection.\n *\n * Calling twice with the same source must return the same bitmap\n * rather than decode twice, and two calls that overlap in time must\n * share one fetch.\n */\n resolve(source: string): Promise<UiImage>;\n /**\n * The bitmap for a source that is already decoded, held exactly as\n * `resolve` would hold it, or null when it is not decoded yet, in\n * which case nothing is held and the caller resolves as usual.\n *\n * Optional, because a resolver that decodes elsewhere may have no\n * synchronous answer. It exists for the frame that builds a node: a\n * promise settles a microtask after that frame has painted, so\n * without this a picture the cache already holds is drawn as its\n * placeholder for one frame, which under a shared-element morph is a\n * blank tile where the picture was a frame ago.\n */\n peek?(source: string): UiImage | null;\n /**\n * Says the caller no longer needs the source. The bitmap may be kept\n * (it is likely to be wanted again) but a resolver that closes\n * bitmaps must not close one another caller still holds.\n */\n release(source: string): void;\n /** Drops everything, for a runtime shutting down. */\n dispose(): void;\n}\n\nexport interface DefaultImageResolverOptions {\n /**\n * How many decoded bitmaps to keep after their last holder released\n * them. The live ones — those something on screen is still holding —\n * are never evicted, whatever this says.\n */\n capacity?: number;\n /** Injectable for specs, and for an app that fetches through its own stack. */\n fetch?: (source: string) => Promise<Blob>;\n /** Injectable for specs; the platform's `createImageBitmap` by default. */\n decode?: (blob: Blob) => Promise<UiImage>;\n}\n\ninterface Entry {\n readonly source: string;\n /** In flight, or settled. One per source, which is the de-duplication. */\n readonly bitmap: Promise<UiImage>;\n /** How many callers are holding it. Zero means it is evictable. */\n holders: number;\n /** Set once the promise settles, so eviction can close the bitmap. */\n settled: UiImage | null;\n}\n\nconst DEFAULT_CAPACITY = 32;\n\n/**\n * Fetch, decode, cache, evict.\n *\n * **Where the decode happens.** The decode belongs in a worker with\n * the bitmap transferred back. This resolver does not spawn one,\n * for two reasons that are worth stating rather than discovering\n * later. A Gesso runtime normally *is* a worker — the render worker of\n * `WorkerApp` — so a resolver constructed by it already fetches and\n * decodes off the main thread, and the bitmap never crosses a thread\n * at all, which is strictly better than transferring one. And\n * `createImageBitmap` is specified to decode in parallel, so even on a\n * main-thread runtime the decode is not on the main thread; only the\n * fetch bookkeeping is. A dedicated decode worker would therefore add\n * a nested worker to buy back something the platform already gives.\n * The seam is still open: `ImageStore.setResolver` takes any\n * `ImageResolver`, so an app that measures a reason for one can supply\n * it without touching a component.\n *\n * **What it cannot decode.** `createImageBitmap` takes a `Blob`, and\n * Chrome refuses an SVG one — \"The source image could not be decoded\".\n * So an `Image` pointed at an `.svg` fails on a browser where the same\n * file in an `<img>` would work, and a vector glyph belongs in `Icon`,\n * which draws a path rather than decoding a document. Found by\n * pointing the playground's Media card at an SVG data URL and getting\n * a placeholder.\n *\n * **Eviction.** Reference counting first, LRU second. A bitmap\n * something on screen is holding is never evicted, however long the\n * queue is; a released one stays cached until the capacity pushes it\n * out, so scrolling a list back and forth does not re-fetch. Evicting\n * closes the bitmap, because an `ImageBitmap` holds decoded pixels and\n * garbage collection is not prompt about them.\n */\nexport class DefaultImageResolver implements ImageResolver {\n private readonly entries = new Map<string, Entry>();\n /** Sources with no holders, least recently released first. */\n private readonly evictable: string[] = [];\n private readonly capacity: number;\n private readonly fetchBlob: (source: string) => Promise<Blob>;\n private readonly decodeBlob: (blob: Blob) => Promise<UiImage>;\n private disposed = false;\n\n constructor(options: DefaultImageResolverOptions = {}) {\n this.capacity = options.capacity ?? DEFAULT_CAPACITY;\n this.fetchBlob = options.fetch ?? defaultFetch;\n this.decodeBlob = options.decode ?? defaultDecode;\n }\n\n /** Decoded or in-flight sources, for specs and the inspector. */\n get size(): number {\n return this.entries.size;\n }\n\n resolve(source: string): Promise<UiImage> {\n if (this.disposed) {\n return Promise.reject(new Error('The image resolver has been disposed.'));\n }\n const existing = this.entries.get(source);\n if (existing !== undefined) {\n existing.holders++;\n const index = this.evictable.indexOf(source);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n return existing.bitmap;\n }\n const entry: Entry = {\n source,\n holders: 1,\n settled: null,\n bitmap: this.fetchBlob(source)\n .then(blob => this.decodeBlob(blob))\n .then(bitmap => {\n // Released while it was in flight, and evicted since: the\n // entry is gone, so nothing will ever close this one.\n if (this.entries.get(source) !== entry) {\n bitmap.close?.();\n throw new Error(`The image '${source}' was released before it finished decoding.`);\n }\n entry.settled = bitmap;\n return bitmap;\n })\n .catch((error: unknown) => {\n // A failed fetch is not cached: the next caller should try\n // again rather than inherit a rejection forever.\n if (this.entries.get(source) === entry) {\n this.entries.delete(source);\n const index = this.evictable.indexOf(source);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n }\n throw error;\n })\n };\n // Nothing else awaits `entry.bitmap`, so a rejection with no\n // consumer would be an unhandled rejection in the worker.\n entry.bitmap.catch(() => {});\n this.entries.set(source, entry);\n return entry.bitmap;\n }\n\n peek(source: string): UiImage | null {\n if (this.disposed) {\n return null;\n }\n const existing = this.entries.get(source);\n if (existing === undefined || existing.settled === null) {\n return null;\n }\n // A hold, exactly as `resolve` takes one, so the caller's `release`\n // balances it and the bitmap stays live while it is on screen.\n existing.holders++;\n const index = this.evictable.indexOf(source);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n return existing.settled;\n }\n\n release(source: string): void {\n const entry = this.entries.get(source);\n if (entry === undefined || entry.holders === 0) {\n return;\n }\n entry.holders--;\n if (entry.holders > 0) {\n return;\n }\n this.evictable.push(source);\n while (this.evictable.length > this.capacity) {\n const oldest = this.evictable.shift()!;\n const stale = this.entries.get(oldest);\n if (stale === undefined || stale.holders > 0) {\n continue;\n }\n this.entries.delete(oldest);\n stale.settled?.close?.();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n for (const entry of this.entries.values()) {\n entry.settled?.close?.();\n }\n this.entries.clear();\n this.evictable.length = 0;\n }\n}\n\nasync function defaultFetch(source: string): Promise<Blob> {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Fetching the image '${source}' failed with ${response.status} ${response.statusText}.`);\n }\n return response.blob();\n}\n\nfunction defaultDecode(blob: Blob): Promise<UiImage> {\n if (typeof createImageBitmap !== 'function') {\n return Promise.reject(new Error('createImageBitmap is unavailable, so images cannot be decoded here.'));\n }\n return createImageBitmap(blob);\n}\n","import type { UiColor } from '../properties/UiColor';\nimport type { UiImage } from '../properties/UiImage';\nimport { colorToCss } from './PaintState';\n\n/** What an icon is: a path in a square viewBox, at a size, in a colour. */\nexport interface IconSpec {\n /** SVG path data, in the coordinates of `viewBox`. */\n readonly path: string;\n /** The side of the square the path is drawn in. 24 is the usual icon grid. */\n readonly viewBox: number;\n /** The side of the box the icon occupies, in logical pixels. */\n readonly size: number;\n readonly color: UiColor;\n /** `'fill'` for a solid glyph, `'stroke'` for a line one. */\n readonly style: 'fill' | 'stroke';\n /** Line width for a stroked icon, in viewBox units. */\n readonly strokeWidth: number;\n /**\n * Which points a filled path encloses: `'nonzero'` (the canvas\n * default) or `'evenodd'`.\n *\n * It matters for any glyph with a hole in it — a clock face, a\n * circle-and-slash, a downward arrow inside a cloud — because a\n * subpath wound the same way as its container does not punch a hole\n * under `nonzero` and does under `evenodd`. Icon sets author for one\n * or the other and say which in the SVG's `fill-rule`; Heroicons'\n * solid set says `evenodd`, and a Gesso `Icon` given that path\n * without this renders a filled blob.\n */\n readonly fillRule?: 'nonzero' | 'evenodd';\n}\n\n/**\n * Draws an icon path into a bitmap, once per distinct icon.\n *\n * The icon atlas comes later; the first cut rasterises per icon, replaced later without an API change.\n * This is that first cut, and the API it fixes is `IconSpec` in and a\n * `UiImage` out — which is what an atlas would also produce, with a\n * sub-rectangle of one texture instead of a bitmap of its own.\n *\n * **What this costs today, so the atlas has a number to beat:** one\n * `OffscreenCanvas` and one `createImageBitmap` per distinct\n * (path, size, colour, style) — so a toolbar of ten icons in one\n * colour is ten small textures where an atlas would be one, and the\n * same icon in a hover colour is a second entry. Nothing re-rasterises\n * while the spec is unchanged, and a theme change re-rasterises only\n * the icons on screen.\n */\nexport interface IconRasterizerOptions {\n /**\n * Physical pixels per logical pixel to rasterise at. Two is crisp on\n * an ordinary and on a retina display and is what the runtime passes\n * when it does not know better; an atlas will take the real one.\n */\n scale?: number;\n /** How many released rasters to keep. */\n capacity?: number;\n /** Injectable for specs and for a host without `OffscreenCanvas`. */\n createCanvas?: (width: number, height: number) => IconCanvas;\n}\n\n/** The little that rasterising a path needs, so a spec can stand in for it. */\nexport interface IconCanvas {\n getContext2D(): IconContext | null;\n toBitmap(): Promise<UiImage>;\n}\n\nexport interface IconContext {\n scale(x: number, y: number): void;\n fillStyle: string;\n strokeStyle: string;\n lineWidth: number;\n lineCap: CanvasLineCap;\n lineJoin: CanvasLineJoin;\n fill(path: Path2D, fillRule?: 'nonzero' | 'evenodd'): void;\n stroke(path: Path2D): void;\n}\n\ninterface Raster {\n readonly key: string;\n readonly bitmap: Promise<UiImage>;\n holders: number;\n settled: UiImage | null;\n}\n\nconst DEFAULT_SCALE = 2;\nconst DEFAULT_CAPACITY = 64;\n\nexport function iconKey(spec: IconSpec): string {\n const { r, g, b, a } = spec.color;\n return `${spec.size}|${spec.viewBox}|${spec.style}|${spec.strokeWidth}|${spec.fillRule ?? 'nonzero'}|${r},${g},${b},${a}|${spec.path}`;\n}\n\nexport class IconRasterizer {\n private readonly rasters = new Map<string, Raster>();\n private readonly evictable: string[] = [];\n private readonly scale: number;\n private readonly capacity: number;\n private readonly createCanvas: (width: number, height: number) => IconCanvas;\n\n constructor(options: IconRasterizerOptions = {}) {\n this.scale = options.scale ?? DEFAULT_SCALE;\n this.capacity = options.capacity ?? DEFAULT_CAPACITY;\n this.createCanvas = options.createCanvas ?? offscreenCanvas;\n }\n\n get size(): number {\n return this.rasters.size;\n }\n\n raster(spec: IconSpec): Promise<UiImage> {\n const key = iconKey(spec);\n const existing = this.rasters.get(key);\n if (existing !== undefined) {\n existing.holders++;\n const index = this.evictable.indexOf(key);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n return existing.bitmap;\n }\n const raster: Raster = {\n key,\n holders: 1,\n settled: null,\n bitmap: this.draw(spec).then(bitmap => {\n if (this.rasters.get(key) !== raster) {\n bitmap.close?.();\n throw new Error('The icon was released before it finished rasterising.');\n }\n raster.settled = bitmap;\n return bitmap;\n })\n };\n raster.bitmap.catch(() => {});\n this.rasters.set(key, raster);\n return raster.bitmap;\n }\n\n release(spec: IconSpec): void {\n const key = iconKey(spec);\n const raster = this.rasters.get(key);\n if (raster === undefined || raster.holders === 0) {\n return;\n }\n raster.holders--;\n if (raster.holders > 0) {\n return;\n }\n this.evictable.push(key);\n while (this.evictable.length > this.capacity) {\n const oldest = this.evictable.shift()!;\n const stale = this.rasters.get(oldest);\n if (stale === undefined || stale.holders > 0) {\n continue;\n }\n this.rasters.delete(oldest);\n stale.settled?.close?.();\n }\n }\n\n dispose(): void {\n for (const raster of this.rasters.values()) {\n raster.settled?.close?.();\n }\n this.rasters.clear();\n this.evictable.length = 0;\n }\n\n private async draw(spec: IconSpec): Promise<UiImage> {\n const side = Math.max(1, Math.round(spec.size * this.scale));\n const canvas = this.createCanvas(side, side);\n const ctx = canvas.getContext2D();\n if (ctx === null) {\n throw new Error('An icon could not be rasterised: no 2D context.');\n }\n // The path is authored in viewBox units; one transform puts it in\n // the physical pixels of the bitmap, so nothing else has to scale.\n const unit = side / spec.viewBox;\n ctx.scale(unit, unit);\n const path = new Path2D(spec.path);\n if (spec.style === 'stroke') {\n ctx.strokeStyle = colorToCss(spec.color);\n ctx.lineWidth = spec.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.stroke(path);\n } else {\n ctx.fillStyle = colorToCss(spec.color);\n ctx.fill(path, spec.fillRule ?? 'nonzero');\n }\n return canvas.toBitmap();\n }\n}\n\n/**\n * `OffscreenCanvas` is available on the main thread and in a worker,\n * which is what lets an icon be rasterised wherever the runtime lives —\n * the same property that lets `UiImage` be an `ImageBitmap`.\n */\nfunction offscreenCanvas(width: number, height: number): IconCanvas {\n if (typeof OffscreenCanvas !== 'function') {\n throw new Error('OffscreenCanvas is unavailable, so icons cannot be rasterised here.');\n }\n const canvas = new OffscreenCanvas(width, height);\n return {\n getContext2D: () => canvas.getContext('2d') as unknown as IconContext | null,\n toBitmap: () => createImageBitmap(canvas)\n };\n}\n","/**\n * Base error for WebGPU renderer failures.\n *\n * Carries a `phase` tag so callers can distinguish initialization,\n * resize, shader compilation, pipeline creation, and device loss.\n */\nexport class WebGPUError extends Error {\n constructor(\n message: string,\n public readonly phase:\n | 'unavailable'\n | 'adapter'\n | 'device'\n | 'context'\n | 'configure'\n | 'shader'\n | 'pipeline'\n | 'render'\n | 'lost'\n | 'disposed',\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = 'WebGPUError';\n }\n}\n\n/**\n * Reports that the WebGPU renderer is not usable on this client.\n */\nexport function throwWebGPUUnavailable(reason: string): never {\n throw new WebGPUError(`WebGPU is unavailable: ${reason}`, 'unavailable');\n}\n","import { WebGPUError } from './WebGPUError';\n\nexport interface WebGPUDeviceInit {\n adapter: GPUAdapter;\n device: GPUDevice;\n format: GPUTextureFormat;\n}\n\n/**\n * Whether this thread has a WebGPU entry point at all.\n *\n * A synchronous answer, deliberately: it is what lets a runtime asked\n * for `auto` settle on Canvas2D during its constructor rather than\n * after a rejected promise, so a browser that never shipped WebGPU\n * draws its first frame with no blank period and no async state. It\n * says nothing about whether an adapter can actually be had, which\n * only `initializeWebGPU` can find out.\n */\nexport function isWebGPUAvailable(): boolean {\n return typeof navigator !== 'undefined' && navigator.gpu !== undefined;\n}\n\n/**\n * Acquires a WebGPU adapter and device.\n *\n * Fails cleanly with descriptive errors when navigator.gpu, the\n * adapter, or the device request is unavailable. The returned device\n * has a loss listener attached; callers should check `isDeviceLost`\n * or subscribe via `onDeviceLost` before rendering.\n */\nexport async function initializeWebGPU(): Promise<WebGPUDeviceInit> {\n if (typeof navigator === 'undefined' || navigator.gpu === undefined) {\n throw new WebGPUError('navigator.gpu is not present.', 'unavailable');\n }\n\n const adapter = await navigator.gpu.requestAdapter();\n if (adapter === null) {\n throw new WebGPUError('No WebGPU adapter found.', 'adapter');\n }\n\n const device = await adapter.requestDevice();\n device.lost.then(info => {\n onDeviceLostListeners.forEach(fn => fn(info));\n });\n\n const format = navigator.gpu.getPreferredCanvasFormat();\n return { adapter, device, format };\n}\n\nconst onDeviceLostListeners = new Set<(info: GPUDeviceLostInfo) => void>();\n\n/**\n * Subscribes to device loss events from any device created through\n * `initializeWebGPU`. Used by the renderer to enter a controlled\n * lost state.\n */\nexport function onDeviceLost(listener: (info: GPUDeviceLostInfo) => void): () => void {\n onDeviceLostListeners.add(listener);\n return () => {\n onDeviceLostListeners.delete(listener);\n };\n}\n","/**\n * WGSL sources for the two WebGPU pipelines.\n *\n * Both draw one unit quad per instance. An instance carries the\n * rectangle it covers in absolute layout coordinates and the affine\n * transform that maps those coordinates to logical screen pixels, so\n * every instance under one node shares the node's transform and the\n * rectangle is honest — a scrollbar thumb sits where its geometry says.\n *\n * Clipping is two-layered. The rectangular part is a scissor set per\n * draw command. The rounded part is a **clip chain**: a storage buffer\n * of clipping boxes with corner radii, each holding its parent's index\n * and the inverse of its own transform. An instance names its\n * innermost rounded clip; the fragment shader carries the pixel back\n * into each clip's space up the chain and multiplies their coverages,\n * so every rounded ancestor applies.\n *\n * Edges are anti-aliased by coverage rather than cut by `discard`: a\n * pixel's alpha is scaled by how far its centre sits inside the shape,\n * in physical pixels. A box on integer coordinates is still crisp —\n * every pixel centre is half a pixel from its edge — and rounded\n * corners match the anti-aliased paths Canvas2D draws.\n */\n\n/** Shared uniforms: logical viewport width and height, device pixel ratio. */\nexport const VIEW_UNIFORM_FLOATS = 4;\n\n/** Deepest rounded-clip nesting the shader will walk. */\nexport const MAX_CLIP_DEPTH = 16;\n\nconst SHARED = /* wgsl */ `\n// x: logical width, y: logical height, z: device pixel ratio.\n@group(0) @binding(0) var<uniform> uView: vec4f;\n\n// The frame's clip chain, four vec4s per node; see CLIP_STRIDE_FLOATS.\n@group(1) @binding(0) var<storage, read> uClips: array<vec4f>;\n\n// The frame's gradients, twelve vec4s each; see GRADIENT_STRIDE_FLOATS.\n// Declared in both pipelines so group 1 reads the same either way; only\n// the primitive pipeline samples it.\n@group(1) @binding(1) var<storage, read> uGradients: array<vec4f>;\n\n// Signed distance from a point to a rounded rectangle, negative inside.\nfn roundedRectDistance(point: vec2f, origin: vec2f, size: vec2f, radius: f32) -> f32 {\n let half = size * 0.5;\n let r = min(radius, min(half.x, half.y));\n let q = abs(point - (origin + half)) - (half - vec2f(r, r));\n return length(max(q, vec2f(0.0))) + min(max(q.x, q.y), 0.0) - r;\n}\n\n// How much of a pixel lies inside a shape, from the signed distance of\n// its centre in logical pixels.\nfn coverage(signedDistance: f32, dpr: f32) -> f32 {\n return clamp(0.5 - signedDistance * dpr, 0.0, 1.0);\n}\n\n// Coverage of the fragment at framebuffer position fragPx under every\n// rounded clip from clipIndex up to the root; 1.0 when unclipped.\nfn clipCoverage(fragPx: vec2f, dpr: f32, clipIndex: f32) -> f32 {\n var result = 1.0;\n var index = i32(clipIndex);\n var depth = 0;\n let screen = fragPx / dpr;\n loop {\n if (index < 0 || depth >= ${MAX_CLIP_DEPTH}) {\n break;\n }\n let base = u32(index) * 4u;\n let rect = uClips[base];\n let header = uClips[base + 1u];\n let inverse = uClips[base + 2u];\n let translation = uClips[base + 3u];\n let local = vec2f(\n screen.x * inverse.x + screen.y * inverse.z + translation.x,\n screen.x * inverse.y + screen.y * inverse.w + translation.y\n );\n result = min(result, coverage(roundedRectDistance(local, rect.xy, rect.zw, header.x), dpr));\n index = i32(header.y);\n depth = depth + 1;\n }\n return result;\n}\n\nfn toClipSpace(transformed: vec2f) -> vec4f {\n return vec4f((transformed.x / uView.x) * 2.0 - 1.0, 1.0 - (transformed.y / uView.y) * 2.0, 0.0, 1.0);\n}\n`;\n\nexport const PRIMITIVE_SHADER = /* wgsl */ `\n${SHARED}\n\n// Twelve vec4s per gradient; colours start at 2 and offsets at 10.\nconst GRADIENT_STRIDE = 12u;\nconst GRADIENT_COLORS = 2u;\nconst GRADIENT_OFFSETS = 10u;\n\n// One stop's position. The lane is picked with comparisons rather than\n// by indexing the vec4 with a runtime value, which is legal WGSL but\n// not worth depending on for four branches the compiler folds anyway.\nfn gradientStopOffset(base: u32, index: u32) -> f32 {\n let row = uGradients[base + GRADIENT_OFFSETS + (index >> 2u)];\n let lane = index & 3u;\n if (lane == 0u) {\n return row.x;\n }\n if (lane == 1u) {\n return row.y;\n }\n if (lane == 2u) {\n return row.z;\n }\n return row.w;\n}\n\nfn premultiply(color: vec4f) -> vec4f {\n return vec4f(color.rgb * color.a, color.a);\n}\n\nfn unpremultiply(color: vec4f) -> vec4f {\n if (color.a <= 0.0) {\n return vec4f(0.0, 0.0, 0.0, 0.0);\n }\n return vec4f(color.rgb / color.a, color.a);\n}\n\n// The gradient's colour at a point in the instance's own coordinates.\n//\n// Stops are interpolated with premultiplied alpha, which is what the\n// canvas specification says a CanvasGradient does; for the opaque\n// stops most gradients have it is the same answer either way, and for\n// a stop that fades out it is the difference between a ramp that goes\n// through grey and one that does not.\nfn gradientColor(index: u32, local: vec2f) -> vec4f {\n let base = index * GRADIENT_STRIDE;\n let header = uGradients[base];\n let geometry = uGradients[base + 1u];\n var t = 0.0;\n if (header.x < 0.5) {\n // Linear: how far along the gradient line the point projects.\n let line = geometry.zw - geometry.xy;\n let lengthSquared = dot(line, line);\n if (lengthSquared > 0.0) {\n t = dot(local - geometry.xy, line) / lengthSquared;\n }\n } else {\n // Radial: distance from the centre over the radius.\n if (geometry.z > 0.0) {\n t = length(local - geometry.xy) / geometry.z;\n }\n }\n t = clamp(t, 0.0, 1.0);\n\n let count = u32(header.y);\n var previousOffset = gradientStopOffset(base, 0u);\n var previousColor = premultiply(uGradients[base + GRADIENT_COLORS]);\n if (t <= previousOffset) {\n return unpremultiply(previousColor);\n }\n for (var i = 1u; i < count; i = i + 1u) {\n let offset = gradientStopOffset(base, i);\n let color = premultiply(uGradients[base + GRADIENT_COLORS + i]);\n if (t <= offset) {\n let span = offset - previousOffset;\n var f = 0.0;\n if (span > 0.0) {\n f = (t - previousOffset) / span;\n }\n return unpremultiply(mix(previousColor, color, f));\n }\n previousOffset = offset;\n previousColor = color;\n }\n return unpremultiply(previousColor);\n}\n\nstruct VertexOutput {\n @builtin(position) position: vec4f,\n @location(0) localPos: vec2f,\n @location(1) size: vec2f,\n @location(2) color: vec4f,\n @location(3) radiusOpacityBorder: vec3f,\n @location(4) @interpolate(flat) kind: u32,\n @location(5) @interpolate(flat) clipIndex: f32,\n @location(6) @interpolate(flat) gradientIndex: f32,\n};\n\n@vertex\nfn vs(\n @location(0) vertex: vec2f,\n @location(1) instancePos: vec2f,\n @location(2) instanceSize: vec2f,\n @location(3) instanceColor: vec4f,\n @location(4) radiusOpacityBorder: vec3f,\n @location(5) instanceKind: u32,\n @location(6) transformA: vec2f,\n @location(7) transformB: vec2f,\n @location(8) transformC: vec2f,\n @location(9) clipIndex: f32,\n @location(10) gradientIndex: f32,\n) -> VertexOutput {\n var out: VertexOutput;\n // The quad is inflated by one physical pixel on every side so the\n // fragments just outside a fractional or rounded edge exist to be\n // shaded: coverage decides the edge, not rasterisation, which only\n // produces fragments whose centres lie inside the geometry.\n let inflate = 1.0 / uView.z;\n let local = vertex * instanceSize + (vertex * 2.0 - 1.0) * inflate;\n let world = instancePos + local;\n let transformed = vec2f(\n world.x * transformA.x + world.y * transformB.x + transformC.x,\n world.x * transformA.y + world.y * transformB.y + transformC.y\n );\n out.position = toClipSpace(transformed);\n out.localPos = local;\n out.size = instanceSize;\n out.color = instanceColor;\n out.radiusOpacityBorder = radiusOpacityBorder;\n out.kind = instanceKind;\n out.clipIndex = clipIndex;\n out.gradientIndex = gradientIndex;\n return out;\n}\n\n@fragment\nfn fs(in: VertexOutput) -> @location(0) vec4f {\n let dpr = uView.z;\n let dist = roundedRectDistance(in.localPos, vec2f(0.0, 0.0), in.size, in.radiusOpacityBorder.x);\n\n var shape = coverage(dist, dpr);\n if (in.kind != 0u) {\n // Border: the band between the outer edge and the inner edge inset\n // by borderWidth.\n let bw = max(in.radiusOpacityBorder.z, 0.0);\n shape = min(shape, coverage(-(dist + bw), dpr));\n }\n let alpha = shape * clipCoverage(in.position.xy, dpr, in.clipIndex);\n if (alpha <= 0.0) {\n discard;\n }\n var base = in.color;\n if (in.gradientIndex >= 0.0) {\n base = gradientColor(u32(in.gradientIndex), in.localPos);\n }\n return vec4f(base.rgb, base.a * in.radiusOpacityBorder.y * alpha);\n}\n`;\n\nexport const TEXTURED_SHADER = /* wgsl */ `\n${SHARED}\n\n@group(0) @binding(1) var tSampler: sampler;\n@group(0) @binding(2) var tTexture: texture_2d<f32>;\n\nstruct VertexOutput {\n @builtin(position) position: vec4f,\n @location(0) uv: vec2f,\n @location(1) @interpolate(flat) opacity: f32,\n @location(2) @interpolate(flat) clipIndex: f32,\n};\n\n@vertex\nfn vs(\n @location(0) vertex: vec2f,\n @location(1) instancePos: vec2f,\n @location(2) instanceSize: vec2f,\n @location(3) instanceOpacity: f32,\n @location(4) clipIndex: f32,\n @location(5) transformA: vec2f,\n @location(6) transformB: vec2f,\n @location(7) transformC: vec2f,\n @location(8) uvOrigin: vec2f,\n @location(9) uvSize: vec2f,\n) -> VertexOutput {\n var out: VertexOutput;\n let world = instancePos + vertex * instanceSize;\n let transformed = vec2f(\n world.x * transformA.x + world.y * transformB.x + transformC.x,\n world.x * transformA.y + world.y * transformB.y + transformC.y\n );\n out.position = toClipSpace(transformed);\n // A glyph samples its cell in an atlas page; an image passes the\n // whole texture as (0,0)-(1,1) and this is the identity.\n out.uv = uvOrigin + vertex * uvSize;\n out.opacity = instanceOpacity;\n out.clipIndex = clipIndex;\n return out;\n}\n\n@fragment\nfn fs(in: VertexOutput) -> @location(0) vec4f {\n let alpha = clipCoverage(in.position.xy, uView.z, in.clipIndex);\n if (alpha <= 0.0) {\n discard;\n }\n let color = textureSample(tTexture, tSampler, in.uv);\n return vec4f(color.rgb, color.a * in.opacity * alpha);\n}\n`;\n","/**\n * The glyph atlas: one texture holding many glyphs, in place of one\n * texture per text run.\n *\n * A run-per-texture cache is bounded by the number of distinct runs,\n * which for a document view is the number of distinct lines — it has\n * no ceiling. A glyph cache is bounded by the number of distinct\n * glyphs a font is drawn at, which is a few hundred. The atlas is\n * what makes text memory a property of the fonts in use rather than\n * of how much text there is.\n *\n * Two halves, split so the packing is testable without a GPU:\n *\n * - **This file is pure CPU.** It packs cells into pages and hands\n * back a slot with the page index and the UV rectangle. Nothing\n * here touches a device, a canvas or a texture, so the render-list\n * builder can ask for slots inside vitest.\n * - **`WebGPUGlyphPages`** owns the GPU textures. It drains\n * `takePending()` after each build and rasterises those glyphs into\n * their cells.\n *\n * Cells are rasterised **in the run's colour**, not white-and-tinted.\n * Tinting would key the atlas on the glyph alone, but it also flattens\n * a colour emoji into a silhouette and forces a premultiplication\n * decision the rest of the pipeline does not make. Colour is instead\n * part of the key: an application draws text in a handful of colours,\n * so this multiplies the entry count by a small constant and keeps\n * the sampled pixels exactly what Canvas2D would have drawn.\n */\n\n/**\n * Horizontal subpixel positions a glyph is rasterised at.\n *\n * A cell maps one-to-one onto physical pixels, so its left edge has to\n * land on a whole pixel; the fraction of a pixel the pen actually sits\n * at is baked into the raster instead. Three phases keep the spacing\n * error under a third of a physical pixel, which at any device pixel\n * ratio is below what the eye resolves in a line of text. One phase\n * (plain rounding) is visibly uneven at dpr 1.\n */\nexport const GLYPH_SUBPIXEL_PHASES = 3;\n\n/**\n * Cell extents around the pen, in em.\n *\n * Generous rather than measured: the tight ink box of a glyph is only\n * known once it is rasterised, and allocation happens a step earlier,\n * in the builder. These cover the ascenders, descenders and side\n * bearings of any UI font — Chrome reports roughly 0.97 em of ascent\n * and 0.24 em of descent for the default system font — at the cost of\n * some empty space per cell. Tightening them needs font metrics at\n * allocation time; see the decision record.\n */\nconst GLYPH_ASCENT_EM = 1.1;\nconst GLYPH_DESCENT_EM = 0.4;\nconst GLYPH_SIDE_EM = 0.2;\n\n/** Physical-pixel edge of one atlas page. */\nconst DEFAULT_PAGE_SIZE = 1024;\n\n/**\n * Pages kept before the least recently used is cleared. Eight pages of\n * 1024² RGBA is 32 MB, half what the run-texture cache budgeted.\n */\nconst DEFAULT_MAX_PAGES = 8;\n\n/**\n * One font, colour and device pixel ratio, with the cells already\n * allocated for it.\n *\n * A run resolves its style once and then looks each cluster up by\n * string in this map. Keying the atlas on a single composite string\n * instead would build one key per glyph per frame — the frame loop's\n * largest allocation, for a value that is constant across a run.\n */\nexport interface GlyphStyle {\n readonly font: string;\n readonly color: string;\n readonly fontSize: number;\n readonly dpr: number;\n /**\n * The paragraph's base direction, which decides where a neutral\n * character inside a word cell lands when the cell is rasterised: an\n * Arabic word ending in a question mark puts it on the left in a\n * right-to-left paragraph and on the right otherwise.\n */\n readonly rtl: boolean;\n /** Cluster to its cell per subpixel phase; null where it cannot be packed. */\n readonly cells: Map<string, (GlyphSlot | null)[]>;\n}\n\n/** A cell in a page, and where the glyph sits inside it. */\nexport interface GlyphSlot {\n page: number;\n /** UV rectangle of the cell within its page. */\n u: number;\n v: number;\n uw: number;\n vh: number;\n /** Cell size in logical pixels; physical is this × dpr. */\n width: number;\n height: number;\n /** Cell top-left relative to the pen (left edge, baseline), logical. */\n offsetX: number;\n offsetY: number;\n}\n\n/** A newly allocated cell, waiting for its pixels. */\nexport interface GlyphUpload {\n slot: GlyphSlot;\n cluster: string;\n font: string;\n color: string;\n dpr: number;\n /** The direction the cell is rasterised in; see `GlyphStyle.rtl`. */\n rtl: boolean;\n /** Cell origin in the page, physical pixels. */\n pageX: number;\n pageY: number;\n /** Cell size in physical pixels. */\n pixelWidth: number;\n pixelHeight: number;\n /** Pen position inside the cell, physical pixels; the phase is in it. */\n penX: number;\n penY: number;\n}\n\ninterface Page {\n /** Next free x on the current shelf. */\n penX: number;\n /** Top of the current shelf. */\n shelfY: number;\n /** Height of the current shelf. */\n shelfHeight: number;\n /** Frame this page was last drawn from. */\n lastUsed: number;\n}\n\nexport interface GlyphAtlasOptions {\n /** Physical-pixel edge of a page. Default 1024. */\n pageSize?: number;\n /** Pages kept before the least recently used is cleared. Default 8. */\n maxPages?: number;\n}\n\n/**\n * Shelf-packed glyph cells across a small number of fixed-size pages.\n *\n * Shelf packing — fill a row left to right, start a new row when the\n * widest cell in the current one is passed — wastes a little height\n * per row and is right for glyphs, which arrive in a narrow range of\n * heights. A page that fills opens the next one; when every page is\n * full the least recently used is cleared wholesale and re-filled on\n * demand, which is cheap because the glyphs still on screen are\n * re-rasterised within a frame or two and everything else was cold.\n */\nexport class WebGPUGlyphAtlas {\n private readonly styles = new Map<string, GlyphStyle>();\n private readonly pages: Page[] = [];\n private pending: GlyphUpload[] = [];\n private frame = 0;\n private cells = 0;\n /** Pages cleared since the last drain, so the GPU side can zero them. */\n private clearedPages: number[] = [];\n readonly pageSize: number;\n readonly maxPages: number;\n\n constructor(options: GlyphAtlasOptions = {}) {\n this.pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;\n this.maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;\n }\n\n get pageCount(): number {\n return this.pages.length;\n }\n\n /** Cells allocated across every style and page. */\n get glyphCount(): number {\n return this.cells;\n }\n\n /** Marks the start of a frame, for page use tracking. */\n beginFrame(): void {\n this.frame++;\n }\n\n /** The style handle a run looks its clusters up in; created on first sight. */\n styleFor(font: string, color: string, fontSize: number, dpr: number, rtl = false): GlyphStyle {\n const key = `${font}\\0${color}\\0${dpr}\\0${rtl ? 'R' : 'L'}`;\n let style = this.styles.get(key);\n if (style === undefined) {\n style = { font, color, fontSize, dpr, rtl, cells: new Map() };\n this.styles.set(key, style);\n }\n return style;\n }\n\n /**\n * The cell for one glyph, allocating and queueing it for upload on\n * first sight. Null when the cluster cannot fit a page at all.\n */\n slotFor(style: GlyphStyle, cluster: string, advance: number, phase: number): GlyphSlot | null {\n let phases = style.cells.get(cluster);\n if (phases === undefined) {\n phases = Array.from<GlyphSlot | null>({ length: GLYPH_SUBPIXEL_PHASES }).fill(null);\n style.cells.set(cluster, phases);\n } else {\n const existing = phases[phase];\n if (existing !== null) {\n this.pages[existing.page].lastUsed = this.frame;\n return existing;\n }\n }\n\n const dpr = style.dpr;\n // Padding is a whole number of physical pixels so that the cell's\n // left edge stays on a pixel when the pen does.\n const sidePx = Math.max(1, Math.ceil(style.fontSize * GLYPH_SIDE_EM * dpr));\n const ascentPx = Math.max(1, Math.ceil(style.fontSize * GLYPH_ASCENT_EM * dpr));\n const descentPx = Math.max(1, Math.ceil(style.fontSize * GLYPH_DESCENT_EM * dpr));\n const pixelWidth = Math.ceil(advance * dpr) + 2 * sidePx;\n const pixelHeight = ascentPx + descentPx;\n if (pixelWidth > this.pageSize || pixelHeight > this.pageSize) {\n return null;\n }\n\n const placed = this.place(pixelWidth, pixelHeight);\n if (placed === null) {\n return null;\n }\n const slot: GlyphSlot = {\n page: placed.page,\n u: placed.x / this.pageSize,\n v: placed.y / this.pageSize,\n uw: pixelWidth / this.pageSize,\n vh: pixelHeight / this.pageSize,\n width: pixelWidth / dpr,\n height: pixelHeight / dpr,\n offsetX: -sidePx / dpr,\n offsetY: -ascentPx / dpr\n };\n phases[phase] = slot;\n this.cells++;\n this.pending.push({\n slot,\n cluster,\n font: style.font,\n color: style.color,\n dpr,\n rtl: style.rtl,\n pageX: placed.x,\n pageY: placed.y,\n pixelWidth,\n pixelHeight,\n penX: sidePx + phase / GLYPH_SUBPIXEL_PHASES,\n penY: ascentPx\n });\n return slot;\n }\n\n /** Cells allocated since the last call, for the GPU side to fill. */\n takePending(): GlyphUpload[] {\n if (this.pending.length === 0) {\n return [];\n }\n const pending = this.pending;\n this.pending = [];\n return pending;\n }\n\n /** Pages cleared since the last call, whose textures must be zeroed. */\n takeCleared(): number[] {\n if (this.clearedPages.length === 0) {\n return [];\n }\n const cleared = this.clearedPages;\n this.clearedPages = [];\n return cleared;\n }\n\n /** Drops every glyph and every page. */\n reset(): void {\n this.styles.clear();\n this.pages.length = 0;\n this.pending = [];\n this.clearedPages = [];\n this.cells = 0;\n }\n\n /**\n * Finds room for a cell: on the current shelf of an existing page,\n * on a new shelf, on a new page, or — when every page is full — on\n * the least recently used page after clearing it.\n */\n private place(width: number, height: number): { page: number; x: number; y: number } | null {\n for (let index = 0; index < this.pages.length; index++) {\n const spot = this.tryPlace(index, width, height);\n if (spot !== null) {\n return spot;\n }\n }\n if (this.pages.length < this.maxPages) {\n this.pages.push({ penX: 0, shelfY: 0, shelfHeight: 0, lastUsed: this.frame });\n return this.tryPlace(this.pages.length - 1, width, height);\n }\n const victim = this.leastRecentlyUsedPage();\n this.clearPage(victim);\n return this.tryPlace(victim, width, height);\n }\n\n private tryPlace(index: number, width: number, height: number): { page: number; x: number; y: number } | null {\n const page = this.pages[index];\n if (page.penX + width <= this.pageSize && height <= page.shelfHeight) {\n const x = page.penX;\n page.penX += width;\n page.lastUsed = this.frame;\n return { page: index, x, y: page.shelfY };\n }\n // A new shelf, either because the row is full or because this cell\n // is taller than the row was sized for.\n const shelfY = page.shelfY + page.shelfHeight;\n if (shelfY + height > this.pageSize) {\n return null;\n }\n page.shelfY = shelfY;\n page.shelfHeight = height;\n page.penX = width;\n page.lastUsed = this.frame;\n return { page: index, x: 0, y: shelfY };\n }\n\n private leastRecentlyUsedPage(): number {\n let victim = 0;\n for (let index = 1; index < this.pages.length; index++) {\n if (this.pages[index].lastUsed < this.pages[victim].lastUsed) {\n victim = index;\n }\n }\n return victim;\n }\n\n private clearPage(index: number): void {\n for (const style of this.styles.values()) {\n for (const phases of style.cells.values()) {\n for (let phase = 0; phase < phases.length; phase++) {\n if (phases[phase]?.page === index) {\n phases[phase] = null;\n this.cells--;\n }\n }\n }\n }\n this.pending = this.pending.filter(upload => upload.slot.page !== index);\n this.pages[index] = { penX: 0, shelfY: 0, shelfHeight: 0, lastUsed: this.frame };\n this.clearedPages.push(index);\n }\n}\n\n/**\n * The subpixel phase for a pen sitting `fraction` of a physical pixel\n * past a whole one.\n */\nexport function phaseFor(fraction: number): number {\n const phase = Math.floor(fraction * GLYPH_SUBPIXEL_PHASES);\n return Math.min(GLYPH_SUBPIXEL_PHASES - 1, Math.max(0, phase));\n}\n","/**\n * The visual order of a line's words.\n *\n * Canvas2D never needs this: `fillText` takes a whole line and Chrome\n * reorders it. The WebGPU glyph atlas draws a line a cell at a time,\n * and for a line holding Arabic or Hebrew that means two things the\n * per-cluster path cannot do. A cursive word has to be rasterised whole,\n * or every letter draws in its isolated form; and the words have to be\n * placed in visual order, or an Arabic sentence reads backwards and a\n * number inside it lands on the wrong side.\n *\n * This is the Unicode Bidirectional Algorithm at the granularity of\n * words rather than characters. A line is split into tokens, maximal\n * runs of blanks and of non-blanks, each token takes one bidi class\n * from its first strong character (or from its digits, or none), and\n * the algorithm's weak, neutral, implicit and reordering rules run over\n * the tokens (W2, W7, N1, N2, I1, I2, L1, L2). Inside a token, Chrome\n * shapes and orders the characters when the token is rasterised, so a\n * word that mixes classes internally, rare outside URLs, comes out as\n * Chrome would draw it alone.\n *\n * Not done, and stated: bracket pairing (N0), explicit embeddings and\n * isolates (the U+202A to U+2069 controls are treated as neutral),\n * and levels above two. The text conformance harness records Chrome's\n * per-cluster visual positions for every right-to-left fixture and\n * `WebGPUGlyphShaper.bidi.spec.ts` asserts these tokens land on them.\n */\n\nexport interface VisualToken {\n /** Offsets into the line's text, in logical order. */\n start: number;\n end: number;\n text: string;\n blank: boolean;\n}\n\n/** Whether a line needs visual ordering at all: a right-to-left paragraph, or a strong right-to-left character in it. */\nexport function needsVisualOrder(text: string, rtl: boolean): boolean {\n if (rtl) {\n return true;\n }\n for (const character of text) {\n const kind = strongKind(character.codePointAt(0)!);\n if (kind === 'R' || kind === 'AL') {\n return true;\n }\n }\n return false;\n}\n\n/** The line's tokens in the order they are drawn from left to right. */\nexport function visualOrder(text: string, rtl: boolean): VisualToken[] {\n const tokens = tokenize(text);\n if (tokens.length === 0) {\n return tokens;\n }\n const paragraph = rtl ? 1 : 0;\n const classes = tokens.map(token => (token.blank ? 'WS' : classify(token.text)));\n\n // W2: a European number after an Arabic letter is an Arabic number.\n // W7: a European number after a Latin letter (or at the start of a\n // left-to-right paragraph) is a letter.\n let lastStrong: 'L' | 'R' | 'AL' | null = paragraph === 0 ? 'L' : 'R';\n for (let i = 0; i < classes.length; i++) {\n const kind = classes[i];\n if (kind === 'L' || kind === 'R' || kind === 'AL') {\n lastStrong = kind;\n } else if (kind === 'EN') {\n if (lastStrong === 'AL') {\n classes[i] = 'AN';\n } else if (lastStrong === 'L') {\n classes[i] = 'L';\n }\n }\n }\n\n // N1 and N2: a run of neutrals between two strong characters of the\n // same direction takes it (numbers count as right-to-left); any other\n // neutral run takes the paragraph's direction.\n const directions: ('L' | 'R')[] = classes.map(kind => (kind === 'L' ? 'L' : 'R'));\n let i = 0;\n while (i < classes.length) {\n if (!isNeutral(classes[i])) {\n i++;\n continue;\n }\n let j = i;\n while (j < classes.length && isNeutral(classes[j])) {\n j++;\n }\n const before = i === 0 ? null : directions[i - 1];\n const after = j === classes.length ? null : directionOfStrong(classes[j]);\n const resolved: 'L' | 'R' = before !== null && before === after ? before : paragraph === 0 ? 'L' : 'R';\n for (let k = i; k < j; k++) {\n directions[k] = resolved;\n }\n i = j;\n }\n\n // I1 and I2: embedding levels from the resolved classes.\n const levels = classes.map((kind, index) => {\n if (kind === 'EN' || kind === 'AN') {\n return 2;\n }\n if (kind === 'R' || kind === 'AL') {\n return 1;\n }\n if (kind === 'L') {\n return paragraph === 0 ? 0 : 2;\n }\n return directions[index] === 'L' ? (paragraph === 0 ? 0 : 2) : 1;\n });\n\n // L1: blanks at the end of the line take the paragraph level.\n for (let k = levels.length - 1; k >= 0 && tokens[k].blank; k--) {\n levels[k] = paragraph;\n }\n\n // L2: from the highest level down to the lowest odd level, reverse\n // every run of tokens at that level or above.\n const highest = Math.max(...levels);\n const lowestOdd = Math.min(...levels.map(level => (level % 2 === 1 ? level : Infinity)));\n const order = tokens.map((_, index) => index);\n for (let level = highest; level >= Math.max(1, lowestOdd); level--) {\n let k = 0;\n while (k < order.length) {\n if (levels[order[k]] < level) {\n k++;\n continue;\n }\n let end = k;\n while (end < order.length && levels[order[end]] >= level) {\n end++;\n }\n reverse(order, k, end);\n k = end;\n }\n }\n return order.map(index => tokens[index]);\n}\n\ntype BidiClass = 'L' | 'R' | 'AL' | 'EN' | 'AN' | 'ON' | 'WS';\n\nfunction isNeutral(kind: BidiClass): boolean {\n return kind === 'ON' || kind === 'WS';\n}\n\nfunction directionOfStrong(kind: BidiClass): 'L' | 'R' | null {\n return kind === 'L' ? 'L' : kind === 'R' || kind === 'AL' || kind === 'EN' || kind === 'AN' ? 'R' : null;\n}\n\n/** A token's class: its first strong character's, else its digits', else neutral. */\nfunction classify(text: string): BidiClass {\n let digits: 'EN' | 'AN' | null = null;\n for (const character of text) {\n const code = character.codePointAt(0)!;\n const strong = strongKind(code);\n if (strong !== null) {\n return strong;\n }\n if (code >= 0x0660 && code <= 0x0669) {\n digits = 'AN';\n } else if (digits === null && DIGIT.test(character)) {\n digits = 'EN';\n }\n }\n return digits ?? 'ON';\n}\n\nconst DIGIT = /^\\p{Nd}$/u;\nconst LETTER = /^\\p{L}$/u;\n\n/** L, R or AL for a strong character; null for anything else. */\nfunction strongKind(code: number): 'L' | 'R' | 'AL' | null {\n if (\n (code >= 0x0600 && code <= 0x06ff && !(code >= 0x0660 && code <= 0x0669) && !(code >= 0x06f0 && code <= 0x06f9)) ||\n (code >= 0x0750 && code <= 0x077f) ||\n (code >= 0x0870 && code <= 0x08ff) ||\n (code >= 0x0700 && code <= 0x074f) || // Syriac\n (code >= 0x0780 && code <= 0x07bf) || // Thaana\n (code >= 0xfb50 && code <= 0xfdff) ||\n (code >= 0xfe70 && code <= 0xfeff)\n ) {\n return isMarkOrPunctuation(code) ? null : 'AL';\n }\n if (\n (code >= 0x0590 && code <= 0x05ff) || // Hebrew\n (code >= 0x07c0 && code <= 0x07ff) || // NKo\n (code >= 0xfb1d && code <= 0xfb4f)\n ) {\n return isMarkOrPunctuation(code) ? null : 'R';\n }\n return LETTER.test(String.fromCodePoint(code)) ? 'L' : null;\n}\n\n/** Combining marks and punctuation inside a right-to-left block are not strong. */\nfunction isMarkOrPunctuation(code: number): boolean {\n const character = String.fromCodePoint(code);\n return !LETTER.test(character);\n}\n\nfunction tokenize(text: string): VisualToken[] {\n const tokens: VisualToken[] = [];\n let start = 0;\n while (start < text.length) {\n const blank = isBlankCode(text.charCodeAt(start));\n let end = start + 1;\n while (end < text.length && isBlankCode(text.charCodeAt(end)) === blank) {\n end++;\n }\n tokens.push({ start, end, text: text.slice(start, end), blank });\n start = end;\n }\n return tokens;\n}\n\nfunction isBlankCode(code: number): boolean {\n return code === 0x20 || code === 0x09 || code === 0x3000 || code === 0xa0;\n}\n\nfunction reverse(order: number[], from: number, to: number): void {\n for (let a = from, b = to - 1; a < b; a++, b--) {\n const swap = order[a];\n order[a] = order[b];\n order[b] = swap;\n }\n}\n","import { graphemeBoundaries } from '../../editing/TextBoundaries';\nimport { needsVisualOrder, visualOrder } from '../BidiRuns';\n\n/**\n * Where each grapheme cluster of a line starts, and how wide it is.\n *\n * Positions come from **prefix widths** — the x of cluster `i` is the\n * measured width of the line's first `i` clusters — which is the same\n * rule caret and selection geometry use (`TextGeometry.ts`), from the\n * same measurer that produced the line. Two things follow from that\n * and neither is available from summing per-glyph advances: kerning is\n * honoured, because each prefix is measured in context; and the last\n * cluster's end is exactly the line width layout was sized with, so a\n * centred or right-aligned line ends where the box says it does.\n *\n * What it does not do is shape. Clusters are split by grapheme\n * boundary and drawn one at a time, so a font's ligatures do not form:\n * `fi` draws as an f and an i at the positions the ligature would have\n * occupied rather than as one glyph. Combining marks survive, because\n * a base and its marks are one cluster and rasterise together. This is\n * the cost of rasterising with a canvas instead of a shaping engine,\n * and it is the same trade the editing caret already makes.\n *\n * The exception is a line that holds a right-to-left script, or sits in\n * a right-to-left paragraph. There a cluster is a whole word, so Arabic\n * keeps its joining forms, and the words are placed in visual order by\n * `BidiRuns.ts`; see `shapeWords`.\n */\nexport interface ShapedCluster {\n text: string;\n /** Offset from the line's left edge, logical pixels. */\n x: number;\n /** Advance width, logical pixels. */\n advance: number;\n /** Whitespace only: it advances the pen and draws nothing. */\n blank: boolean;\n}\n\n/** The advance width of a run of text in the line's font. */\nexport type MeasureRun = (text: string) => number;\n\n/**\n * Entries kept before the cache is cleared wholesale.\n *\n * The same crude cap `CanvasTextMeasurer` uses, for the same reason:\n * an LRU costs more than it saves until profiling says otherwise. One\n * entry is a line's clusters, so a screen of a document is a few\n * hundred entries and a scrolled list re-uses every one of them.\n */\nconst MAX_CACHE_ENTRIES = 4096;\n\n/**\n * Splits lines into positioned clusters, once per distinct line.\n *\n * Prefix measurement costs one `measureText` per cluster, so a line is\n * measured n times the first time it is seen and never again. The\n * cache is keyed by font and text — not by position, size or colour —\n * so scrolling, moving or recolouring a line costs nothing.\n */\nexport class GlyphShaper {\n private readonly cache = new Map<string, ShapedCluster[]>();\n\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * The clusters of one line. `width` is the line's measured width,\n * used as the last cluster's end so the run closes exactly where\n * layout put it.\n */\n shape(text: string, font: string, width: number, measure: MeasureRun, rtl = false): readonly ShapedCluster[] {\n if (text.length === 0) {\n return [];\n }\n const key = `${font}\\0${rtl ? 'R' : 'L'}\\0${text}`;\n const cached = this.cache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n\n const clusters: ShapedCluster[] = needsVisualOrder(text, rtl)\n ? this.shapeWords(text, measure, rtl)\n : this.shapeClusters(text, width, measure);\n\n if (this.cache.size >= MAX_CACHE_ENTRIES) {\n this.cache.clear();\n }\n this.cache.set(key, clusters);\n return clusters;\n }\n\n /**\n * A line with a right-to-left script in it, or in a right-to-left\n * paragraph: one cell per word, placed in visual order. A cursive\n * word rasterised whole keeps its joining forms, and `visualOrder`\n * puts the words where Chrome's `fillText` would, so a number inside\n * an Arabic sentence lands on the side it belongs. Positions are\n * cumulative measured widths in visual order; kerning does not cross\n * a blank, so they are the prefix widths the other path uses, in a\n * different order.\n */\n private shapeWords(text: string, measure: MeasureRun, rtl: boolean): ShapedCluster[] {\n const clusters: ShapedCluster[] = [];\n let x = 0;\n for (const token of visualOrder(text, rtl)) {\n const advance = measure(token.text);\n clusters.push({ text: token.text, x, advance, blank: token.blank });\n x += advance;\n }\n return clusters;\n }\n\n /** The common path: one cell per grapheme cluster at its prefix width. */\n private shapeClusters(text: string, width: number, measure: MeasureRun): ShapedCluster[] {\n const boundaries = graphemeBoundaries(text);\n const clusters: ShapedCluster[] = [];\n let previous = 0;\n for (let i = 1; i < boundaries.length; i++) {\n const end = boundaries[i];\n // The final prefix is the whole line, whose width layout already\n // measured; taking it from the argument keeps the run's end and\n // the line box in exact agreement.\n const advanceEnd = i === boundaries.length - 1 ? width : measure(text.slice(0, end));\n const cluster = text.slice(boundaries[i - 1], end);\n clusters.push({\n text: cluster,\n x: previous,\n advance: Math.max(0, advanceEnd - previous),\n blank: isBlank(cluster)\n });\n previous = advanceEnd;\n }\n return clusters;\n }\n\n clear(): void {\n this.cache.clear();\n }\n}\n\n/**\n * Whether a cluster is whitespace. Decided once, when the line is\n * shaped, because the frame loop asks it of every cluster it draws and\n * `trim()` would allocate a string per glyph per frame.\n */\nfunction isBlank(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n const space = code === 32 || code === 9 || code === 10 || code === 13 || code === 12 || code === 11;\n if (!space && !(code >= 0x2000 && code <= 0x200a) && code !== 0x00a0 && code !== 0x3000) {\n return false;\n }\n }\n return true;\n}\n","import type { UiColor } from '../../properties/UiColor';\n\nexport interface RgbaColor {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\nfunction isUiColor(value: unknown): value is UiColor {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const c = value as Partial<UiColor>;\n return typeof c.r === 'number' && typeof c.g === 'number' && typeof c.b === 'number' && typeof c.a === 'number';\n}\n\n/**\n * Parses a color value into normalized RGBA.\n *\n * Accepts either a renderer-independent UiColor object or a CSS\n * color string. Supports:\n * #rgb, #rgba, #rrggbb, #rrggbbaa\n * rgb(r,g,b), rgba(r,g,b,a)\n * named colors: transparent, black, white, red, green, blue\n *\n * Returns undefined for unsupported values so callers can skip\n * drawing instead of crashing.\n */\nexport function parseColor(value: unknown): RgbaColor | undefined {\n if (isUiColor(value)) {\n return { r: value.r, g: value.g, b: value.b, a: value.a };\n }\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim().toLowerCase();\n if (trimmed.length === 0) {\n return undefined;\n }\n\n const named = NAMED_COLORS[trimmed];\n if (named !== undefined) {\n return named;\n }\n\n if (trimmed.startsWith('#')) {\n return parseHex(trimmed);\n }\n if (trimmed.startsWith('rgb')) {\n return parseRgb(trimmed);\n }\n return undefined;\n}\n\nfunction parseHex(hex: string): RgbaColor | undefined {\n let raw = hex.slice(1);\n if (raw.length === 3 || raw.length === 4) {\n raw = raw\n .split('')\n .map(c => c + c)\n .join('');\n }\n if (raw.length !== 6 && raw.length !== 8) {\n return undefined;\n }\n const num = Number.parseInt(raw, 16);\n if (!Number.isFinite(num)) {\n return undefined;\n }\n const r = (num >> 16) & 0xff;\n const g = (num >> 8) & 0xff;\n const b = num & 0xff;\n const a = raw.length === 8 ? (num >> 24) & 0xff : 0xff;\n return { r: r / 255, g: g / 255, b: b / 255, a: a / 255 };\n}\n\nfunction parseRgb(source: string): RgbaColor | undefined {\n const open = source.indexOf('(');\n const close = source.indexOf(')');\n if (open < 0 || close < 0) {\n return undefined;\n }\n const parts = source\n .slice(open + 1, close)\n .split(',')\n .map(s => s.trim());\n if (parts.length < 3) {\n return undefined;\n }\n const values: number[] = [];\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const num = Number(part.replace('%', ''));\n if (!Number.isFinite(num)) {\n return undefined;\n }\n if (part.includes('%')) {\n values.push(num / 100);\n } else if (i === 3) {\n values.push(num);\n } else {\n values.push(num / 255);\n }\n }\n const [r, g, b, a = 1] = values;\n return {\n r: clamp01(r),\n g: clamp01(g),\n b: clamp01(b),\n a: clamp01(a)\n };\n}\n\nfunction clamp01(v: number): number {\n return Math.min(Math.max(v, 0), 1);\n}\n\nconst NAMED_COLORS: Record<string, RgbaColor> = {\n transparent: { r: 0, g: 0, b: 0, a: 0 },\n black: { r: 0, g: 0, b: 0, a: 1 },\n white: { r: 1, g: 1, b: 1, a: 1 },\n red: { r: 1, g: 0, b: 0, a: 1 },\n green: { r: 0, g: 0.5, b: 0, a: 1 },\n blue: { r: 0, g: 0, b: 1, a: 1 },\n yellow: { r: 1, g: 1, b: 0, a: 1 },\n cyan: { r: 0, g: 1, b: 1, a: 1 },\n magenta: { r: 1, g: 0, b: 1, a: 1 },\n orange: { r: 1, g: 0.65, b: 0, a: 1 },\n gray: { r: 0.5, g: 0.5, b: 0.5, a: 1 },\n grey: { r: 0.5, g: 0.5, b: 0.5, a: 1 }\n};\n","import { WebGPUError } from './WebGPUError';\n\n/**\n * Converts a logical dimension to the physical pixel count of the\n * backing store.\n *\n * This is the single definition of that rule, and everything that\n * derives a physical rectangle — scissor rects above all — must go\n * through it. WebGPU rejects a scissor that extends past its\n * attachment by even one pixel, and the rejection discards the whole\n * command buffer, so a backing store rounded one way and a scissor\n * rounded another silently drops every draw in the frame.\n */\nexport function toPhysicalPixels(logical: number, dpr: number): number {\n return Math.max(1, Math.round(logical * dpr));\n}\n\n/**\n * Something that can host a WebGPU drawing surface.\n *\n * HTMLCanvasElement and OffscreenCanvas both satisfy this\n * structurally; the widening to concrete canvas types lives only at\n * the platform boundary.\n */\nexport interface WebGPUCanvasHost {\n width: number;\n height: number;\n getContext(contextId: 'webgpu'): GPUCanvasContext | null;\n}\n\n/**\n * Logical/physical viewport bookkeeping for a WebGPU canvas surface.\n *\n * Mirrors CanvasSurface but for WebGPU: layout works in logical\n * pixels, the configured GPU texture is physical = logical * dpr.\n */\nexport class WebGPUSurface {\n private logicalW = 0;\n private logicalH = 0;\n private pixelRatio = 1;\n private context: GPUCanvasContext | null = null;\n private configured = false;\n\n constructor(private readonly canvas: WebGPUCanvasHost) {}\n\n get logicalWidth(): number {\n return this.logicalW;\n }\n\n get logicalHeight(): number {\n return this.logicalH;\n }\n\n get dpr(): number {\n return this.pixelRatio;\n }\n\n get physicalWidth(): number {\n return this.canvas.width;\n }\n\n get physicalHeight(): number {\n return this.canvas.height;\n }\n\n /**\n * The WebGPU context for the backing store, acquired lazily.\n */\n getContext(): GPUCanvasContext {\n if (this.context === null) {\n this.context = this.acquireContext();\n }\n return this.context;\n }\n\n private acquireContext(): GPUCanvasContext {\n const context = this.canvas.getContext('webgpu');\n if (context === null) {\n throw new WebGPUError('Canvas webgpu context is unavailable.', 'context');\n }\n return context;\n }\n\n /**\n * Configures the context with the supplied device and format.\n *\n * Reconfiguration is skipped when the physical size has not changed\n * so that the existing GPU texture and swap chain stay valid.\n */\n configure(device: GPUDevice, format: GPUTextureFormat): void {\n const ctx = this.getContext();\n const renderAttachment = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.RENDER_ATTACHMENT : 0x10;\n const copySrc = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.COPY_SRC : 0x1;\n ctx.configure({\n device,\n format,\n alphaMode: 'premultiplied',\n // COPY_SRC lets a frame be read back for the parity check.\n usage: renderAttachment | copySrc\n });\n this.configured = true;\n }\n\n unconfigure(): void {\n if (this.context !== null && this.configured) {\n this.context.unconfigure();\n this.configured = false;\n }\n }\n\n /**\n * Resizes the backing store to logical * dpr.\n *\n * Does not reconfigure the GPU context here: the renderer checks\n * whether the physical size changed and reconfigures when needed.\n */\n setLogicalSize(width: number, height: number, dpr: number = 1): boolean {\n this.logicalW = width;\n this.logicalH = height;\n this.pixelRatio = dpr;\n const physicalWidth = toPhysicalPixels(width, dpr);\n const physicalHeight = toPhysicalPixels(height, dpr);\n const changed = this.canvas.width !== physicalWidth || this.canvas.height !== physicalHeight;\n if (changed) {\n this.canvas.width = physicalWidth;\n this.canvas.height = physicalHeight;\n }\n return changed;\n }\n\n getCurrentTexture(): GPUTexture {\n return this.getContext().getCurrentTexture();\n }\n}\n\nexport function createWebGPUSurface(host: WebGPUCanvasHost): WebGPUSurface {\n return new WebGPUSurface(host);\n}\n","import type { UiNode } from '../../graph/UiNode';\nimport { UiNodeType } from '../../graph/UiNodeType';\nimport { SCROLLBAR_FADE_MS } from '../../layout/LayoutEngine';\nimport { SCROLLBAR_THICKNESS, scrollbarThumbs } from '../../layout/Scrollbars';\nimport type { LayoutRecord } from '../../layout/LayoutRecord';\nimport type { LayoutBox } from '../../layout/LayoutTypes';\nimport type { TextMeasurer } from '../../layout/TextMeasurer';\nimport { resolvePaintState, createPaintState, computeObjectFitRect, isPaintVisible } from '../PaintState';\nimport { videoFrameSize } from '../../properties/UiVideo';\nimport type { TextureSource } from './WebGPUTextureCache';\nimport { colorToCss } from '../PaintState';\nimport {\n layoutTextLines,\n buildFontString,\n runFontString,\n spanOf,\n textMeasureRequest,\n textRunBackgrounds,\n textRunDecorations\n} from '../TextRenderer';\nimport type { SpanPaint, TextLinePlacement, TextRunRect } from '../TextRenderer';\nimport { spannedRunsFor } from '../../layout/TextMeasurer';\nimport type { TextMeasureRequest } from '../../layout/TextMeasurer';\nimport { WebGPUGlyphAtlas, phaseFor } from './WebGPUGlyphAtlas';\nimport type { GlyphStyle } from './WebGPUGlyphAtlas';\nimport { GlyphShaper } from './WebGPUGlyphShaper';\nimport type { MeasureRun } from './WebGPUGlyphShaper';\nimport { parseColor } from './WebGPUColor';\nimport type { RgbaColor } from './WebGPUColor';\nimport { borderRadiusIsZero, uniformBorderRadius } from '../../properties/UiBorderRadius';\nimport { gradientPaint, MAX_GRADIENT_STOPS, type ResolvedGradient } from '../../properties/UiGradient';\nimport { normalizeColor } from '../../properties/UiColor';\nimport { LABEL_PADDING_X, labelOrigin, type OverlayShape } from '../OverlayShapes';\nimport { decorationColor, decorationRect, hasDecorationPhase, type DecorationShape } from '../Decorations';\nimport { paintPictures } from '../PaintPicture';\nimport { toPhysicalPixels } from './WebGPUSurface';\nimport { EditableLayout } from '../../editing/EditableLayout';\nimport { lineIndexForOffset } from '../../editing/TextGeometry';\nimport { CARET_WIDTH, caretVisibleAt } from '../../editing/UiEditable';\nimport { paragraphGeometryFrom, selectionRectsIn } from '../../selection/TextSelectionGeometry';\n\n/**\n * Primitive instance layout, in floats:\n *\n * 0 pos.xy absolute layout coordinates of the rectangle\n * 2 size.xy\n * 4 color.rgba\n * 8 radius, opacity, borderWidth\n * 11 kind (u32: PrimitiveKind)\n * 12 transform 3x2 affine, absolute layout → logical screen\n * 18 clipIndex index into the clip chain, -1 for none\n * 19 gradientIndex index into the frame's gradients, -1 for none\n * 20 (end)\n */\nexport const INSTANCE_STRIDE_FLOATS = 20;\nexport const INSTANCE_STRIDE_BYTES = INSTANCE_STRIDE_FLOATS * 4;\n\n/**\n * Textured instance layout (glyphs and images), in floats:\n *\n * 0 pos.xy\n * 2 size.xy\n * 4 opacity\n * 5 clipIndex\n * 6 transform 3x2\n * 12 uvOrigin.xy top-left of the sampled region, 0..1\n * 14 uvSize.xy its extent; (0,0)-(1,1) is the whole texture\n * 16 (end)\n *\n * An image samples its whole texture; a glyph samples its cell in an\n * atlas page, which is what the UV rectangle is here for.\n */\nexport const TEXTURED_STRIDE_FLOATS = 16;\nexport const TEXTURED_STRIDE_BYTES = TEXTURED_STRIDE_FLOATS * 4;\n\n/**\n * Clip chain node layout, in floats (four vec4s):\n *\n * 0 rect.xywh the clipping box in its own layout space\n * 4 radius, parentIndex (-1 at the root), pad, pad\n * 8 inverse a, b, c, d 3x2 affine, logical screen → clip space\n * 12 inverse tx, ty, pad, pad\n * 16 (end)\n *\n * An instance names its innermost rounded clip; the fragment shader\n * walks parent links to the root, so every rounded ancestor applies.\n */\nexport const CLIP_STRIDE_FLOATS = 16;\nexport const CLIP_STRIDE_BYTES = CLIP_STRIDE_FLOATS * 4;\nexport const NO_CLIP_INDEX = -1;\n\n/**\n * Gradient record layout, in floats (twelve vec4s):\n *\n * 0 kind (0 linear, 1 radial), stopCount, pad, pad\n * 4 linear: the gradient line x0, y0, x1, y1\n * radial: centre x, centre y, radius, pad\n * 8 up to MAX_GRADIENT_STOPS colours, rgba each\n * 40 their offsets along the gradient, four to a vec4\n * 48 (end)\n *\n * Geometry is in the instance's own coordinates, with the origin at\n * the rectangle's top-left, which is exactly the `localPos` the\n * fragment shader already has. A fill instance names the gradient it\n * is painted with; the record is fixed-size so one buffer holds them\n * all and a stop is a lookup rather than a second indirection. That is\n * what the stop cap buys, and why exceeding it is an error rather than\n * a truncation: see `MAX_GRADIENT_STOPS`.\n */\nexport const GRADIENT_STRIDE_FLOATS = 48;\nexport const GRADIENT_STRIDE_BYTES = GRADIENT_STRIDE_FLOATS * 4;\n/** Float offset of the first stop colour within a record. */\nexport const GRADIENT_COLORS_OFFSET = 8;\n/** Float offset of the first stop position within a record. */\nexport const GRADIENT_OFFSETS_OFFSET = 40;\nexport const NO_GRADIENT_INDEX = -1;\n\nexport const enum PrimitiveKind {\n Fill = 0,\n Border = 1\n}\n\nexport const enum CommandKind {\n /** A contiguous range of primitive instances under one scissor. */\n Primitives = 0,\n /** A contiguous range of glyph instances sharing one atlas page. */\n Glyphs = 1,\n /** One textured instance drawn with an image. */\n Image = 2\n}\n\nexport type Affine = [number, number, number, number, number, number];\n\nexport interface ScissorRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface PrimitiveCommand {\n kind: CommandKind.Primitives;\n /** Inclusive start index in the primitive instance buffer. */\n start: number;\n /** Exclusive end index in the primitive instance buffer. */\n end: number;\n /** Scissor rectangle in physical pixels, or null for the full viewport. */\n scissor: ScissorRect | null;\n}\n\nexport interface GlyphCommand {\n kind: CommandKind.Glyphs;\n /** Inclusive start index in the textured instance buffer. */\n start: number;\n /** Exclusive end index. */\n end: number;\n /** Atlas page every instance in the range samples. */\n page: number;\n scissor: ScissorRect | null;\n}\n\nexport interface ImageCommand {\n kind: CommandKind.Image;\n instance: number;\n scissor: ScissorRect | null;\n /** A still, or a video's surface; the texture cache tells them apart. */\n source: TextureSource;\n /**\n * How many device pixels the quad covers, so a still can be uploaded\n * at the size it is drawn rather than the size it was decoded.\n *\n * Under the node's whole transform, not the device pixel ratio alone:\n * a node a morph is scaling covers a different number of pixels every\n * frame, which is the same reason `Canvas2DRenderer.scaledFor` reads\n * the current transform.\n */\n drawWidth: number;\n drawHeight: number;\n}\n\nexport type RenderCommand = PrimitiveCommand | GlyphCommand | ImageCommand;\n\n/**\n * One line of text the frame drew, as a whole.\n *\n * The GPU draws glyphs, not runs, so nothing in the pipeline reads\n * this — it is the record of *what text went where*, kept because the\n * question \"did this frame draw the right words in the right boxes\" is\n * the one parity tests and the profiler ask, and per-glyph instances\n * are a bad place to ask it. Building it is cheaper than the\n * per-run `TextRenderItem` it replaces, which mapped every line.\n */\nexport interface TextRunDraw {\n text: string;\n /** Canvas font shorthand. */\n font: string;\n /** CSS color string. */\n color: string;\n /** Line start and alphabetic baseline in logical screen pixels. */\n x: number;\n y: number;\n /** Measured line width and line-box height, logical pixels. */\n width: number;\n height: number;\n opacity: number;\n /** Index of the run's first glyph instance. */\n instance: number;\n /** Glyph instances emitted for it; zero when every cluster is blank. */\n glyphs: number;\n}\n\nexport interface RenderList {\n instanceData: Float32Array;\n instanceCount: number;\n texturedData: Float32Array;\n texturedCount: number;\n /** The frame's rounded clips, CLIP_STRIDE_FLOATS each; see the layout above. */\n clipData: Float32Array;\n clipCount: number;\n /** The frame's gradients, GRADIENT_STRIDE_FLOATS each. */\n gradientData: Float32Array;\n gradientCount: number;\n commands: RenderCommand[];\n /** The lines of text the frame drew, in paint order. */\n textRuns: TextRunDraw[];\n}\n\ninterface CullRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface RenderState {\n opacity: number;\n /**\n * Current transform from absolute layout coordinates to logical\n * screen pixels: ancestors' own transforms, sticky shifts and scroll\n * translations. Children inherit it.\n */\n ctm: Affine;\n /** Current rectangular clip as a physical-pixel scissor. */\n clip: ScissorRect | null;\n /** Innermost rounded clip in the chain, or NO_CLIP_INDEX. */\n rounded: number;\n /**\n * Visible region in the coordinate space children's records use,\n * or null while culling is suspended under a transform or a sticky\n * shift, which move what is drawn away from where records say it is.\n */\n cull: CullRect | null;\n}\n\nconst IDENTITY: Affine = [1, 0, 0, 1, 0, 0];\n\nconst paintScratch = createPaintState();\nconst contentBox: LayoutBox = { x: 0, y: 0, width: 0, height: 0 };\n\n/**\n * A growable float buffer for instance data.\n *\n * The builder used to collect instances in a plain `number[]` and\n * convert at the end. That is fine at a few hundred instances and it\n * is not fine at a few thousand: the atlas emits one instance per\n * letter on screen, and both the boxed pushes and the element-by-\n * element conversion showed up as the largest cost in the frame. This\n * writes floats where they are going to live, and the buffer is\n * module-level scratch — like `paintScratch` above — because a build\n * is not re-entrant and a per-frame allocation is the thing being\n * avoided.\n */\nclass FloatBuffer {\n data: Float32Array;\n length = 0;\n\n constructor(capacity: number) {\n this.data = new Float32Array(capacity);\n }\n\n reset(): void {\n this.length = 0;\n }\n\n /** Room for `count` more floats, doubling until there is. */\n ensure(count: number): void {\n const needed = this.length + count;\n if (needed <= this.data.length) {\n return;\n }\n let capacity = this.data.length * 2;\n while (capacity < needed) {\n capacity *= 2;\n }\n const grown = new Float32Array(capacity);\n grown.set(this.data.subarray(0, this.length));\n this.data = grown;\n }\n\n /** Appends four floats; the clip chain is written a vec4 at a time. */\n push4(a: number, b: number, c: number, d: number): void {\n this.ensure(4);\n const at = this.length;\n this.data[at] = a;\n this.data[at + 1] = b;\n this.data[at + 2] = c;\n this.data[at + 3] = d;\n this.length = at + 4;\n }\n\n /** A copy of what was written, for the frame to own. */\n take(): Float32Array {\n return this.data.slice(0, this.length);\n }\n}\n\nconst instanceScratch = new FloatBuffer(INSTANCE_STRIDE_FLOATS * 512);\nconst texturedScratch = new FloatBuffer(TEXTURED_STRIDE_FLOATS * 2048);\nconst clipScratch = new FloatBuffer(CLIP_STRIDE_FLOATS * 64);\nconst gradientScratch = new FloatBuffer(GRADIENT_STRIDE_FLOATS * 16);\n\n/**\n * Everything the builder keeps between frames: the glyph atlas it\n * allocates cells in and the shaper that positions clusters within a\n * line. Both are caches, and both are the renderer's to own — a\n * builder that made its own each frame would rasterise every glyph\n * again on every frame.\n */\nexport interface RenderTextCache {\n atlas: WebGPUGlyphAtlas;\n shaper: GlyphShaper;\n}\n\n/** A cache for one-shot builds — tests, and the first frame. */\nexport function createTextCache(): RenderTextCache {\n return { atlas: new WebGPUGlyphAtlas(), shaper: new GlyphShaper() };\n}\n\n/**\n * Builds a CPU-side render list from the retained UI tree.\n *\n * One ordered command list holds fills, borders, glyphs and images in\n * paint order — background, image, picture, border, decorations, children,\n * text, the decorations marked `after: 'children'`, then the node's\n * scrollbars — exactly as the Canvas2D renderer paints them.\n * Primitive instances are batched into one command until the scissor\n * changes, and glyph instances likewise until the scissor or the atlas\n * page changes; an image is one command, because it binds its own\n * texture.\n */\nexport function buildRenderList(\n root: UiNode,\n layout: { recordFor(node: UiNode): LayoutRecord | undefined },\n measurer: TextMeasurer,\n logicalWidth: number,\n logicalHeight: number,\n dpr: number,\n now: number = typeof performance !== 'undefined' ? performance.now() : Date.now(),\n overlay: readonly OverlayShape[] = [],\n textCache: RenderTextCache = createTextCache()\n): RenderList {\n const instanceData = instanceScratch;\n const texturedData = texturedScratch;\n const clipData = clipScratch;\n const gradientData = gradientScratch;\n instanceData.reset();\n texturedData.reset();\n clipData.reset();\n gradientData.reset();\n const commands: RenderCommand[] = [];\n const textRuns: TextRunDraw[] = [];\n const { atlas, shaper } = textCache;\n\n /** Primitive instances emitted since the last primitive command was closed. */\n let openStart = 0;\n let openScissor: ScissorRect | null = null;\n\n const state: RenderState = {\n opacity: 1,\n ctm: [...IDENTITY] as Affine,\n clip: null,\n rounded: NO_CLIP_INDEX,\n cull: { x: 0, y: 0, width: logicalWidth, height: logicalHeight }\n };\n\n /** The nodes asking for `lift`, met on the walk and drawn after it. */\n const liftedPass: { node: UiNode; opacity: number; ctm: Affine }[] = [];\n\n function closePrimitives(): void {\n const end = instanceData.length / INSTANCE_STRIDE_FLOATS;\n if (end > openStart) {\n commands.push({ kind: CommandKind.Primitives, start: openStart, end, scissor: openScissor });\n }\n openStart = end;\n }\n\n function beginPrimitives(scissor: ScissorRect | null): void {\n if (openStart === instanceData.length / INSTANCE_STRIDE_FLOATS) {\n openScissor = scissor;\n return;\n }\n if (!scissorsEqual(openScissor, scissor)) {\n closePrimitives();\n openScissor = scissor;\n }\n }\n\n /**\n * Emits one glyph instance per inked cluster of every line.\n *\n * The lines are the ones Canvas2D draws, in the coordinate space\n * `originX`/`originY` translates into absolute layout coordinates.\n * Each glyph is snapped so its cell lands on whole physical pixels —\n * the horizontal remainder becomes the atlas's subpixel phase rather\n * than being thrown away — and instances that share an atlas page\n * and a scissor are merged into one command, so a screen of text in\n * one font is one draw call.\n */\n function pushGlyphs(options: {\n lines: readonly TextLinePlacement[];\n font: string;\n color: string;\n fontSize: number;\n measureRun: MeasureRun;\n /** The paragraph's base direction; see `GlyphShaper.shape`. */\n rtl: boolean;\n originX: number;\n originY: number;\n ctm: Affine;\n opacity: number;\n rounded: number;\n scissor: ScissorRect | null;\n /** The paragraph's runs and their colours; absent for text in one style. */\n paint?: SpanPaint;\n /** The request the lines were measured with, for a run's own font. */\n request?: TextMeasureRequest;\n }): void {\n const { lines, font, color, fontSize, originX, originY, ctm, scissor, rtl } = options;\n const opacity = options.opacity;\n const rounded = options.rounded;\n const style = atlas.styleFor(font, color, fontSize, dpr, rtl);\n // A paragraph with runs draws one piece per run, each in its own\n // font, colour and size, at the x the paragraph measured for it.\n // The atlas is keyed by font and colour already, so a run is a new\n // style and nothing else; the rest of this walk is the one it was.\n const paint = options.paint;\n const spanned = options.request === undefined ? undefined : spannedRunsFor(options.request, measurer);\n let runRequest = options.request;\n const spanMeasure: MeasureRun = run =>\n run.length === 0 || runRequest === undefined ? 0 : measurer.measureRunWidth(run, runRequest);\n for (const line of lines) {\n if (line.text.length === 0) {\n continue;\n }\n const baseline = originY + line.baselineY;\n if (line.runs !== undefined && paint !== undefined && spanned !== undefined) {\n for (const piece of line.runs) {\n if (piece.text.length === 0) {\n continue;\n }\n const span = spanOf(paint, piece.span);\n const pieceFont = runFontString(options.request!, span);\n const pieceColor = colorToCss(span?.color ?? paint.textColor);\n runRequest = spanned.requestAt(piece.start);\n pushPiece(\n piece.text,\n originX + piece.x,\n baseline,\n piece.width,\n line.height,\n pieceFont,\n pieceColor,\n atlas.styleFor(pieceFont, pieceColor, span?.fontSize ?? fontSize, dpr, rtl),\n spanMeasure\n );\n }\n continue;\n }\n pushPiece(line.text, originX + line.x, baseline, line.width, line.height, font, color, style, options.measureRun);\n }\n\n /** One stretch of glyphs in one font and colour: a whole line, or a run of one. */\n function pushPiece(\n pieceText: string,\n lineX: number,\n baseline: number,\n width: number,\n height: number,\n pieceFont: string,\n pieceColor: string,\n pieceStyle: GlyphStyle,\n measure: MeasureRun\n ): void {\n const [runScreenX, runScreenY] = applyTransform(ctm, lineX, baseline);\n const run: TextRunDraw = {\n text: pieceText,\n font: pieceFont,\n color: pieceColor,\n x: runScreenX,\n y: runScreenY,\n width,\n height,\n opacity: options.opacity,\n instance: texturedData.length / TEXTURED_STRIDE_FLOATS,\n glyphs: 0\n };\n textRuns.push(run);\n\n for (const cluster of shaper.shape(pieceText, pieceFont, width, measure, rtl)) {\n if (cluster.blank) {\n continue;\n }\n const penX = lineX + cluster.x;\n // Inlined rather than via applyTransform: this loop runs once\n // per letter on screen, and a returned pair would be an\n // allocation per letter per frame.\n const screenX = penX * ctm[0] + baseline * ctm[2] + ctm[4];\n const screenY = penX * ctm[1] + baseline * ctm[3] + ctm[5];\n const physicalX = screenX * dpr;\n const wholeX = Math.floor(physicalX);\n const slot = atlas.slotFor(pieceStyle, cluster.text, cluster.advance, phaseFor(physicalX - wholeX));\n if (slot === null) {\n continue;\n }\n closePrimitives();\n // Shifting the translation rather than the rectangle keeps the\n // instance in layout coordinates, as every other instance is,\n // and lands the cell on whole physical pixels.\n const instance = pushTextured(\n texturedData,\n penX + slot.offsetX,\n baseline + slot.offsetY,\n slot.width,\n slot.height,\n opacity,\n ctm,\n rounded,\n slot,\n wholeX / dpr - screenX,\n Math.round(screenY * dpr) / dpr - screenY\n );\n const last = commands[commands.length - 1];\n if (\n last !== undefined &&\n last.kind === CommandKind.Glyphs &&\n last.page === slot.page &&\n last.end === instance &&\n scissorsEqual(last.scissor, scissor)\n ) {\n last.end = instance + 1;\n } else {\n commands.push({ kind: CommandKind.Glyphs, start: instance, end: instance + 1, page: slot.page, scissor });\n }\n run.glyphs++;\n }\n }\n }\n\n function intersectsCull(cull: CullRect, x: number, y: number, width: number, height: number): boolean {\n if (cull.width <= 0 || cull.height <= 0) {\n return false;\n }\n return x < cull.x + cull.width && cull.x < x + width && y < cull.y + cull.height && cull.y < y + height;\n }\n\n function visitChildren(node: UiNode, order: readonly UiNode[] | null): void {\n if (order !== null) {\n // zIndex reordered these children; fragments are already expanded.\n for (const child of order) {\n visit(child);\n }\n return;\n }\n let child = node.firstChild;\n while (child !== null) {\n if (child.type === UiNodeType.Fragment) {\n // Fragments are transparent anchors with no record of their own.\n visitChildren(child, null);\n } else {\n visit(child);\n }\n child = child.nextSibling;\n }\n }\n\n function visit(node: UiNode, lifted = false): void {\n const rec = layout.recordFor(node);\n if (rec === undefined) {\n return;\n }\n\n // Visibility from its own two properties rather than from the paint\n // state, and the cull test from the record alone, so that a node\n // this walk is about to discard never pays for the twenty-five or so\n // property resolutions the full state costs. A long list culls most\n // of what it walks, which is where that adds up.\n if (!isPaintVisible(node)) {\n return;\n }\n\n if (rec.lifted && !lifted) {\n // Held for the top layer, with the two things a lifted node keeps\n // of its ancestors: their accumulated transform, so it is drawn\n // where it belongs and scrolls with what it belongs to, and their\n // opacity. Their clips are exactly what it is escaping. Captured\n // before the cull test, because a lifted node is nearly always\n // somewhere its record box is not.\n liftedPass.push({ node, opacity: state.opacity, ctm: state.ctm });\n return;\n }\n\n const sticky = rec.stickyOffsetX !== 0 || rec.stickyOffsetY !== 0;\n if (\n state.cull !== null &&\n !intersectsCull(state.cull, rec.x + rec.stickyOffsetX, rec.y + rec.stickyOffsetY, rec.width, rec.height)\n ) {\n return;\n }\n\n const paint = resolvePaintState(node, paintScratch);\n const effectiveOpacity = state.opacity * paint.opacity;\n // A sticky node (and its children) is shifted to its scroll\n // container's edge; the record keeps the flow position.\n const baseCtm = sticky ? translateTransform(state.ctm, rec.stickyOffsetX, rec.stickyOffsetY) : state.ctm;\n const nodeCtm = paint.hasTransform ? multiplyTransform(baseCtm, buildOwnTransform(rec, paint.transform)) : baseCtm;\n // The node's own background, image, border and scrollbars are\n // clipped by its ancestors, as Canvas2D paints them outside the\n // node's own clip; children and the node's own text go under it.\n const ownScissor = state.clip;\n const ownRounded = state.rounded;\n\n // Clipping (overflow hidden/scroll/auto, ScrollView). The rectangle\n // becomes a scissor; a corner radius becomes the rounded clip the\n // shader evaluates for every descendant instance.\n let nextClip = state.clip;\n let nextRounded = state.rounded;\n if (rec.clips) {\n // The clip lives in the container's own (pre-scroll) space, so it\n // is transformed by the container's CTM, not the scrolled one its\n // children inherit.\n const viewportScissor = logicalClipToScissor(rec, nodeCtm, logicalWidth, logicalHeight, dpr);\n nextClip =\n viewportScissor === null\n ? emptyScissor()\n : state.clip === null\n ? viewportScissor\n : intersectScissors(state.clip, viewportScissor);\n if (!borderRadiusIsZero(paint.borderRadius)) {\n const inverse = invertTransform(nodeCtm);\n if (inverse !== null) {\n // A new node in the chain whose parent is the enclosing rounded\n // clip, so descendants are tested against both.\n nextRounded = clipData.length / CLIP_STRIDE_FLOATS;\n clipData.push4(rec.x, rec.y, rec.width, rec.height);\n clipData.push4(uniformBorderRadius(paint.borderRadius), state.rounded, 0, 0);\n clipData.push4(inverse[0], inverse[1], inverse[2], inverse[3]);\n clipData.push4(inverse[4], inverse[5], 0, 0);\n }\n }\n }\n\n const contentScissor = nextClip;\n const contentRounded = nextRounded;\n\n // Background fill.\n if (paint.backgroundColor !== undefined) {\n const color = parseColor(paint.backgroundColor);\n if (color !== undefined) {\n beginPrimitives(ownScissor);\n pushInstance(\n instanceData,\n rec.x,\n rec.y,\n rec.width,\n rec.height,\n color,\n uniformBorderRadius(paint.borderRadius),\n effectiveOpacity,\n 0,\n PrimitiveKind.Fill,\n nodeCtm,\n ownRounded\n );\n }\n }\n\n // Background gradient, over the colour and under the image, where\n // CSS paints a `background-image`. It is an ordinary fill instance\n // naming a record in the frame's gradient buffer; the fragment\n // shader evaluates the ramp at each pixel's place in the box, so\n // the same instance carries the same rounded corners and the same\n // clip chain as a flat one.\n if (paint.backgroundGradient !== undefined) {\n const index = pushGradient(gradientData, paint.backgroundGradient, rec.width, rec.height);\n if (index !== NO_GRADIENT_INDEX) {\n beginPrimitives(ownScissor);\n // The instance's colour is unread when it names a gradient; the\n // first stop makes a dumped instance buffer legible.\n const first = paint.backgroundGradient.stops[0].color;\n pushInstance(\n instanceData,\n rec.x,\n rec.y,\n rec.width,\n rec.height,\n first,\n uniformBorderRadius(paint.borderRadius),\n effectiveOpacity,\n 0,\n PrimitiveKind.Fill,\n nodeCtm,\n ownRounded,\n index\n );\n }\n }\n\n // Background image, fitted into the box and clipped to it as CSS\n // clips `object-fit`: `cover` and `none` overflow the box, and only\n // the box shows. The box is a scissor, plus a clip-chain node when\n // it has a radius.\n // A video draws through the same path as a still — same fit, same\n // clip, same textured quad — and wins on a node that has both.\n const textureSource: TextureSource | undefined =\n paint.video !== undefined && paint.video.frame !== null ? paint.video : paint.image;\n if (textureSource !== undefined) {\n const sourceSize =\n paint.video !== undefined && paint.video.frame !== null\n ? videoFrameSize(paint.video)\n : { width: paint.image!.width, height: paint.image!.height };\n const rect = computeObjectFitRect(paint.objectFit, sourceSize.width, sourceSize.height, rec);\n if (rect.width > 0 && rect.height > 0) {\n const overflows =\n rect.x < rec.x ||\n rect.y < rec.y ||\n rect.x + rect.width > rec.x + rec.width ||\n rect.y + rect.height > rec.y + rec.height;\n const rounded = !borderRadiusIsZero(paint.borderRadius);\n let imageScissor = ownScissor;\n let imageRounded = ownRounded;\n if (overflows || rounded) {\n const boxScissor = logicalClipToScissor(rec, nodeCtm, logicalWidth, logicalHeight, dpr);\n imageScissor =\n boxScissor === null\n ? emptyScissor()\n : ownScissor === null\n ? boxScissor\n : intersectScissors(ownScissor, boxScissor);\n }\n if (rounded) {\n const inverse = invertTransform(nodeCtm);\n if (inverse !== null) {\n imageRounded = clipData.length / CLIP_STRIDE_FLOATS;\n clipData.push4(rec.x, rec.y, rec.width, rec.height);\n clipData.push4(uniformBorderRadius(paint.borderRadius), ownRounded, 0, 0);\n clipData.push4(inverse[0], inverse[1], inverse[2], inverse[3]);\n clipData.push4(inverse[4], inverse[5], 0, 0);\n }\n }\n closePrimitives();\n const instance = pushTextured(\n texturedData,\n rect.x,\n rect.y,\n rect.width,\n rect.height,\n effectiveOpacity,\n nodeCtm,\n imageRounded,\n FULL_TEXTURE\n );\n // The quad's size in device pixels, from the node's transform.\n // `nodeCtm` is a 2x3 in column-major order, so the two column\n // lengths are the axis scales.\n const scaleX = Math.hypot(nodeCtm[0], nodeCtm[1]) * dpr;\n const scaleY = Math.hypot(nodeCtm[2], nodeCtm[3]) * dpr;\n commands.push({\n kind: CommandKind.Image,\n instance,\n scissor: imageScissor,\n source: textureSource,\n drawWidth: rect.width * scaleX,\n drawHeight: rect.height * scaleY\n });\n }\n }\n\n /**\n * A painted node's picture: one textured quad the size of the box,\n * where the Canvas2D backend puts its one `drawImage`.\n *\n * The bitmap is the same object that backend draws, from the same\n * cache, so the two cannot disagree about what a painter drew.\n * What remains for the parity gate to check is what a backend\n * could still get wrong on its own: the box, the place in paint\n * order, the clip and the opacity. See `PaintPicture.ts`.\n */\n if (node.type === UiNodeType.Paint) {\n const picture = paintPictures.pictureFor(node, rec, dpr);\n if (picture !== undefined) {\n closePrimitives();\n const instance = pushTextured(\n texturedData,\n rec.x,\n rec.y,\n rec.width,\n rec.height,\n effectiveOpacity,\n nodeCtm,\n ownRounded,\n FULL_TEXTURE\n );\n commands.push({\n kind: CommandKind.Image,\n instance,\n scissor: ownScissor,\n source: picture,\n // The picture was rasterised at exactly this size, so the\n // texture cache has nothing to gain by copying it smaller.\n drawWidth: rec.width * dpr,\n drawHeight: rec.height * dpr\n });\n }\n }\n\n // Border stroke.\n if (paint.borderWidth > 0 && paint.borderColor !== undefined) {\n const color = parseColor(paint.borderColor);\n if (color !== undefined) {\n beginPrimitives(ownScissor);\n pushInstance(\n instanceData,\n rec.x,\n rec.y,\n rec.width,\n rec.height,\n color,\n uniformBorderRadius(paint.borderRadius),\n effectiveOpacity,\n paint.borderWidth,\n PrimitiveKind.Border,\n nodeCtm,\n ownRounded\n );\n }\n }\n\n /**\n * The node's modifier decorations, as fill and border instances at\n * the node's own place in paint order.\n *\n * Under `ownScissor` / `ownRounded` — its ancestors' clips — and\n * not under its own, exactly where the background and border go,\n * because a ring with an outset lies entirely outside the box that\n * would clip it. The rectangle and the colour come from\n * `Decorations.ts`, which is the same arithmetic Canvas2D uses.\n */\n const pushDecorations = (shapes: readonly DecorationShape[], after: boolean): void => {\n const nodeRadius = uniformBorderRadius(paint.borderRadius);\n for (const shape of shapes) {\n if ((shape.after === 'children') !== after) {\n continue;\n }\n const resolved = decorationColor(node, shape);\n const color = resolved === undefined ? undefined : parseColor(resolved);\n if (color === undefined) {\n continue;\n }\n const rect = decorationRect(shape, rec, nodeRadius);\n if (rect.width <= 0 || rect.height <= 0) {\n continue;\n }\n if (shape.kind === 'stroke' && shape.lineWidth <= 0) {\n continue;\n }\n beginPrimitives(ownScissor);\n pushInstance(\n instanceData,\n rect.x,\n rect.y,\n rect.width,\n rect.height,\n color,\n rect.radius,\n effectiveOpacity,\n shape.kind === 'stroke' ? shape.lineWidth : 0,\n shape.kind === 'stroke' ? PrimitiveKind.Border : PrimitiveKind.Fill,\n nodeCtm,\n ownRounded\n );\n }\n };\n\n const decorations = node.decorations;\n if (decorations !== null && hasDecorationPhase(decorations, false)) {\n pushDecorations(decorations, false);\n }\n\n // Captured before children reuse the shared scratch below. An\n // editable always has foreground work: its caret and placeholder.\n const hasText = paint.editor !== undefined || (paint.text !== undefined && paint.text.length > 0);\n\n // Children. A boundary drains whatever they lifted once they are\n // done, so `lift` reaches the top of this subtree and no further.\n let liftedFrom = liftedPass.length;\n if (node.hasChildren()) {\n const saved: RenderState = {\n opacity: state.opacity,\n ctm: state.ctm,\n clip: state.clip,\n rounded: state.rounded,\n cull: state.cull\n };\n state.opacity = effectiveOpacity;\n state.ctm = nodeCtm;\n state.clip = nextClip;\n state.rounded = nextRounded;\n if (rec.clips && state.cull !== null) {\n state.cull = pushCull(state.cull, rec);\n }\n if (paint.hasTransform || sticky) {\n // Culling works in record coordinates; a transform or a sticky\n // shift moves what is drawn away from them.\n state.cull = null;\n }\n // Scroll containers translate their content by the scroll offset.\n if (rec.scrollable) {\n state.ctm = translateTransform(state.ctm, -rec.scrollX, -rec.scrollY);\n }\n\n liftedFrom = liftedPass.length;\n visitChildren(node, rec.paintOrder);\n\n state.opacity = saved.opacity;\n state.ctm = saved.ctm;\n state.clip = saved.clip;\n state.rounded = saved.rounded;\n state.cull = saved.cull;\n }\n\n // Foreground text, after children as Canvas2D paints it. Children\n // reused the paint scratch, so the node's own style is re-resolved.\n if (hasText) {\n const text = resolvePaintState(node, paintScratch);\n contentBox.x = 0;\n contentBox.y = 0;\n contentBox.width = Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight);\n contentBox.height = Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom);\n // A field scrolls its own text inside its box, as Canvas2D paints\n // it; the offset is zero for every other kind of text.\n const offsetX = rec.x + rec.paddingLeft - rec.scrollX;\n const offsetY = rec.y + rec.paddingTop - rec.scrollY;\n\n const font = buildFontString(text);\n // Prefix widths come from the measurer that produced the lines,\n // so cluster positions and the line width agree exactly. The\n // request's font is all `measureRunWidth` reads of it.\n const measureRequest = textMeasureRequest(text.text ?? '', text, contentBox);\n const measureRun: MeasureRun = run => (run.length === 0 ? 0 : measurer.measureRunWidth(run, measureRequest));\n\n /** One run of placed lines, in content-box coordinates. */\n const pushTextRun = (lines: readonly TextLinePlacement[], color: string): void => {\n pushGlyphs({\n lines,\n font,\n color,\n fontSize: text.fontSize,\n measureRun,\n rtl: text.rtl,\n originX: offsetX,\n originY: offsetY,\n ctm: nodeCtm,\n opacity: effectiveOpacity,\n rounded: contentRounded,\n scissor: contentScissor,\n paint: text.spans === undefined ? undefined : text,\n request: measureRequest\n });\n };\n\n /** The rectangles a paragraph's runs and decorations draw as fills. */\n const pushTextRects = (rects: readonly TextRunRect[]): void => {\n for (const rect of rects) {\n const color = parseColor(rect.color);\n if (color !== undefined) {\n pushContentRect(rect, color);\n }\n }\n };\n\n /** A plain rectangle in the content box, clipped like the text. */\n const pushContentRect = (box: LayoutBox, color: RgbaColor): void => {\n beginPrimitives(contentScissor);\n pushInstance(\n instanceData,\n offsetX + box.x,\n offsetY + box.y,\n box.width,\n box.height,\n color,\n 0,\n effectiveOpacity,\n 0,\n PrimitiveKind.Fill,\n nodeCtm,\n contentRounded\n );\n };\n\n if (text.editor !== undefined) {\n // An editable, in the order Canvas2D paints it: selection, text\n // or placeholder, composition underline, caret.\n const model = text.editor;\n const editable = new EditableLayout(model, contentBox, text, measurer);\n if (model.focused && !model.collapsed) {\n const selection = parseColor(text.selectionColor);\n if (selection !== undefined) {\n for (const box of editable.selectionBoxes()) {\n pushContentRect(box, selection);\n }\n }\n }\n if (editable.placeholderLines.length > 0) {\n pushTextRun(editable.placeholderLines, colorToCss(text.placeholderColor));\n } else {\n pushTextRun(editable.lines, colorToCss(text.textColor));\n }\n if (model.composing) {\n const underline = parseColor(text.textColor);\n if (underline !== undefined) {\n const line = editable.lines[lineIndexForOffset(editable.lines, model.composition!.start)];\n for (const box of editable.compositionBoxes()) {\n pushContentRect({ x: box.x, y: Math.round(line.baselineY + 1), width: box.width, height: 1 }, underline);\n }\n }\n }\n if (model.focused && model.collapsed && caretVisibleAt(model, now)) {\n const caretColor = parseColor(text.caretColor);\n if (caretColor !== undefined) {\n const caret = editable.caretRect();\n pushContentRect(\n { x: Math.round(caret.x), y: caret.y, width: CARET_WIDTH, height: caret.height },\n caretColor\n );\n }\n }\n } else {\n const lines = layoutTextLines(contentBox, text, measurer);\n // A run's own background is under the highlights, as a CSS\n // inline background is under a selection.\n pushTextRects(textRunBackgrounds(lines, text));\n if (text.textMatches !== undefined || text.textSelection !== undefined) {\n // Static text with part of it highlighted, in the order\n // Canvas2D paints it: find matches, the selection over them,\n // then the run. The lines are the ones laid out for the run.\n const geometry = paragraphGeometryFrom(lines, text.text!, contentBox, text, measurer);\n const match = text.textMatches === undefined ? undefined : parseColor(text.matchColor);\n if (match !== undefined) {\n for (const range of text.textMatches!) {\n for (const box of selectionRectsIn(geometry, range.start, range.end)) {\n pushContentRect(box, match);\n }\n }\n }\n const selection = text.textSelection === undefined ? undefined : parseColor(text.selectionColor);\n if (selection !== undefined) {\n for (const box of selectionRectsIn(geometry, text.textSelection!.start, text.textSelection!.end)) {\n pushContentRect(box, selection);\n }\n }\n }\n pushTextRun(lines, colorToCss(text.textColor));\n pushTextRects(textRunDecorations(lines, text));\n }\n }\n\n if (decorations !== null && hasDecorationPhase(decorations, true)) {\n // Children reused the paint scratch; the node's own radius and\n // opacity are read from it, so re-resolve before the second pass.\n resolvePaintState(node, paintScratch);\n pushDecorations(decorations, true);\n }\n\n if (rec.liftBoundary) {\n // Inside this node's own clip, over everything under it, and\n // still under whatever comes after this node.\n drawLifted(liftedFrom, nextClip, nextRounded);\n }\n\n if (rec.scrollable) {\n beginPrimitives(ownScissor);\n pushScrollbars(instanceData, rec, effectiveOpacity, nodeCtm, ownRounded, now);\n }\n }\n\n /**\n * The top layer, drawn once the tree is done: no clip, no scissor,\n * no cull, and after every command the walk emitted, which is the\n * whole of what `lift` promises. The list is walked by index rather\n * than iterated, so a lifted node inside a lifted subtree lands\n * after the one that carried it, as paint order says it should.\n */\n function drawLifted(from = 0, clip: ScissorRect | null = null, rounded: number = NO_CLIP_INDEX): void {\n if (liftedPass.length <= from) {\n return;\n }\n const saved: RenderState = {\n opacity: state.opacity,\n ctm: state.ctm,\n clip: state.clip,\n rounded: state.rounded,\n cull: state.cull\n };\n for (let i = from; i < liftedPass.length; i++) {\n const entry = liftedPass[i]!;\n state.opacity = entry.opacity;\n state.ctm = entry.ctm;\n // The boundary's own clip still applies; everything inside it\n // does not, which is the whole of what `lift` escapes.\n state.clip = clip;\n state.rounded = rounded;\n state.cull = null;\n visit(entry.node, true);\n }\n liftedPass.length = from;\n state.opacity = saved.opacity;\n state.ctm = saved.ctm;\n state.clip = saved.clip;\n state.rounded = saved.rounded;\n state.cull = saved.cull;\n }\n\n visit(root);\n drawLifted();\n closePrimitives();\n\n // The debugging overlay: unclipped, untransformed, over everything.\n for (const shape of overlay) {\n switch (shape.kind) {\n case 'fill': {\n const color = cssColor(shape.color);\n if (color !== undefined) {\n beginPrimitives(null);\n pushInstance(\n instanceData,\n shape.x,\n shape.y,\n shape.width,\n shape.height,\n color,\n 0,\n 1,\n 0,\n PrimitiveKind.Fill,\n IDENTITY,\n NO_CLIP_INDEX\n );\n }\n break;\n }\n case 'stroke': {\n const color = cssColor(shape.color);\n if (color !== undefined) {\n beginPrimitives(null);\n pushInstance(\n instanceData,\n shape.x,\n shape.y,\n shape.width,\n shape.height,\n color,\n 0,\n 1,\n shape.lineWidth,\n PrimitiveKind.Border,\n IDENTITY,\n NO_CLIP_INDEX\n );\n }\n break;\n }\n case 'label': {\n const labelBox = { x: 0, y: 0, width: 0, height: shape.height };\n const labelState = {\n ...createPaintState(),\n text: shape.text,\n fontSize: shape.fontSize,\n fontFamily: shape.fontFamily,\n lineHeight: shape.height,\n textWrap: 'none' as const,\n verticalAlign: 'middle' as const\n };\n const lines = layoutTextLines(labelBox, labelState, measurer);\n if (lines.length === 0) {\n break;\n }\n const width = lines[0].width + 2 * LABEL_PADDING_X;\n const origin = labelOrigin(shape);\n const background = cssColor(shape.background);\n if (background !== undefined) {\n beginPrimitives(null);\n pushInstance(\n instanceData,\n origin.x,\n origin.y,\n width,\n shape.height,\n background,\n 0,\n 1,\n 0,\n PrimitiveKind.Fill,\n IDENTITY,\n NO_CLIP_INDEX\n );\n }\n const labelRequest = textMeasureRequest(shape.text, labelState, labelBox);\n pushGlyphs({\n lines,\n font: shape.font,\n color: shape.textColor,\n // Overlay labels (the inspector's) are the framework's own text.\n rtl: false,\n fontSize: shape.fontSize,\n measureRun: run => (run.length === 0 ? 0 : measurer.measureRunWidth(run, labelRequest)),\n originX: origin.x + LABEL_PADDING_X,\n originY: origin.y,\n ctm: IDENTITY,\n opacity: 1,\n rounded: NO_CLIP_INDEX,\n scissor: null\n });\n break;\n }\n }\n }\n closePrimitives();\n\n return {\n instanceData: instanceData.take(),\n instanceCount: instanceData.length / INSTANCE_STRIDE_FLOATS,\n texturedData: texturedData.take(),\n texturedCount: texturedData.length / TEXTURED_STRIDE_FLOATS,\n clipData: clipData.take(),\n clipCount: clipData.length / CLIP_STRIDE_FLOATS,\n gradientData: gradientData.take(),\n gradientCount: gradientData.length / GRADIENT_STRIDE_FLOATS,\n commands,\n textRuns\n };\n}\n\nfunction cssColor(css: string): RgbaColor | undefined {\n const color = normalizeColor(css);\n return color === undefined ? undefined : parseColor(color);\n}\n\n/**\n * The lines of text a render list draws, in paint order. For tests and\n * the profiler; the renderer walks `commands` directly.\n */\nexport function textRuns(list: RenderList): readonly TextRunDraw[] {\n return list.textRuns;\n}\n\n/** Glyph instances a render list draws, across every glyph command. */\nexport function glyphCount(list: RenderList): number {\n let count = 0;\n for (const command of list.commands) {\n if (command.kind === CommandKind.Glyphs) {\n count += command.end - command.start;\n }\n }\n return count;\n}\n\n/**\n * Overlay scrollbars as fill primitives, from the shared geometry the\n * Canvas2D renderer draws and the hit tester grabs.\n */\nfunction pushScrollbars(\n out: FloatBuffer,\n rec: LayoutRecord,\n opacity: number,\n transform: Affine,\n rounded: number,\n now: number\n): void {\n const remaining = rec.scrollbarVisibleUntil - now;\n if (remaining <= 0) {\n return;\n }\n const alpha = Math.min(1, remaining / SCROLLBAR_FADE_MS) * 0.55;\n const color: RgbaColor = { r: 0.5, g: 0.5, b: 0.5, a: alpha };\n const { vertical, horizontal } = scrollbarThumbs(rec);\n for (const bar of [vertical, horizontal]) {\n if (bar === null) {\n continue;\n }\n const { x, y, width, height } = bar.thumb;\n pushInstance(\n out,\n x,\n y,\n width,\n height,\n color,\n SCROLLBAR_THICKNESS / 2,\n opacity,\n 0,\n PrimitiveKind.Fill,\n transform,\n rounded\n );\n }\n}\n\nfunction pushInstance(\n out: FloatBuffer,\n x: number,\n y: number,\n width: number,\n height: number,\n color: RgbaColor,\n radius: number,\n opacity: number,\n borderWidth: number,\n kind: PrimitiveKind,\n transform: Affine,\n clip: number,\n gradient: number = NO_GRADIENT_INDEX\n): void {\n out.ensure(INSTANCE_STRIDE_FLOATS);\n const data = out.data;\n const at = out.length;\n data[at] = x;\n data[at + 1] = y;\n data[at + 2] = width;\n data[at + 3] = height;\n data[at + 4] = color.r;\n data[at + 5] = color.g;\n data[at + 6] = color.b;\n data[at + 7] = color.a;\n data[at + 8] = radius;\n data[at + 9] = opacity;\n data[at + 10] = borderWidth;\n data[at + 11] = kind;\n data[at + 12] = transform[0];\n data[at + 13] = transform[1];\n data[at + 14] = transform[2];\n data[at + 15] = transform[3];\n data[at + 16] = transform[4];\n data[at + 17] = transform[5];\n data[at + 18] = clip;\n data[at + 19] = gradient;\n out.length = at + INSTANCE_STRIDE_FLOATS;\n}\n\n/**\n * Writes one gradient record for a rectangle of the given size and\n * returns its index, or NO_GRADIENT_INDEX when it would paint nothing.\n *\n * `gradientPaint` is the shared placement the Canvas2D renderer calls\n * too, so the gradient line and the stop positions are one piece of\n * arithmetic rather than two that have to agree.\n */\nfunction pushGradient(out: FloatBuffer, gradient: ResolvedGradient, width: number, height: number): number {\n const placed = gradientPaint(gradient, width, height);\n if (placed.kind === 'radial' && !(placed.radius > 0)) {\n return NO_GRADIENT_INDEX;\n }\n if (placed.stops.length > MAX_GRADIENT_STOPS) {\n // `resolveGradient` already rejected this; the guard is here so a\n // future caller cannot quietly write past the end of a record.\n throw new Error(`A gradient may carry at most ${MAX_GRADIENT_STOPS} stops, got ${placed.stops.length}.`);\n }\n out.ensure(GRADIENT_STRIDE_FLOATS);\n const data = out.data;\n const at = out.length;\n // The scratch is reused between frames, so the unwritten stop slots\n // hold whatever the last frame put there.\n data.fill(0, at, at + GRADIENT_STRIDE_FLOATS);\n data[at] = placed.kind === 'linear' ? 0 : 1;\n data[at + 1] = placed.stops.length;\n data[at + 4] = placed.x0;\n data[at + 5] = placed.y0;\n if (placed.kind === 'linear') {\n data[at + 6] = placed.x1;\n data[at + 7] = placed.y1;\n } else {\n data[at + 6] = placed.radius;\n }\n for (let i = 0; i < placed.stops.length; i++) {\n const stop = placed.stops[i];\n const color = at + GRADIENT_COLORS_OFFSET + i * 4;\n data[color] = stop.color.r;\n data[color + 1] = stop.color.g;\n data[color + 2] = stop.color.b;\n data[color + 3] = stop.color.a;\n data[at + GRADIENT_OFFSETS_OFFSET + i] = stop.offset;\n }\n out.length = at + GRADIENT_STRIDE_FLOATS;\n return at / GRADIENT_STRIDE_FLOATS;\n}\n\n/** The whole of a texture: what an image samples. */\nconst FULL_TEXTURE: UvRect = { u: 0, v: 0, uw: 1, vh: 1 };\n\ninterface UvRect {\n u: number;\n v: number;\n uw: number;\n vh: number;\n}\n\n/**\n * `dx`/`dy` shift the transform's translation, which is how a glyph\n * snaps its cell onto whole physical pixels without a transform array\n * of its own — one allocation per letter per frame, otherwise.\n */\nfunction pushTextured(\n out: FloatBuffer,\n x: number,\n y: number,\n width: number,\n height: number,\n opacity: number,\n transform: Affine,\n clip: number,\n uv: UvRect,\n dx = 0,\n dy = 0\n): number {\n out.ensure(TEXTURED_STRIDE_FLOATS);\n const data = out.data;\n const at = out.length;\n data[at] = x;\n data[at + 1] = y;\n data[at + 2] = width;\n data[at + 3] = height;\n data[at + 4] = opacity;\n data[at + 5] = clip;\n data[at + 6] = transform[0];\n data[at + 7] = transform[1];\n data[at + 8] = transform[2];\n data[at + 9] = transform[3];\n data[at + 10] = transform[4] + dx;\n data[at + 11] = transform[5] + dy;\n data[at + 12] = uv.u;\n data[at + 13] = uv.v;\n data[at + 14] = uv.uw;\n data[at + 15] = uv.vh;\n out.length = at + TEXTURED_STRIDE_FLOATS;\n return at / TEXTURED_STRIDE_FLOATS;\n}\n\nfunction buildOwnTransform(\n rec: LayoutBox,\n local: {\n x: number;\n y: number;\n translateX: number;\n translateY: number;\n scaleX: number;\n scaleY: number;\n rotation: number;\n }\n): Affine {\n const originX = rec.x + local.x;\n const originY = rec.y + local.y;\n\n const cos = Math.cos(local.rotation);\n const sin = Math.sin(local.rotation);\n\n // Own transform: T(translate) * T(origin) * R * S * T(-origin),\n // matching the Canvas2D renderer's translate sequence. Pre-composing\n // a translation only adds to the translation column, which is why it\n // costs two additions rather than a matrix multiply.\n const m00 = cos * local.scaleX;\n const m01 = sin * local.scaleX;\n const m10 = -sin * local.scaleY;\n const m11 = cos * local.scaleY;\n const tx = originX - originX * m00 - originY * m10 + local.translateX;\n const ty = originY - originX * m01 - originY * m11 + local.translateY;\n\n return [m00, m01, m10, m11, tx, ty];\n}\n\nexport function multiplyTransform(a: Affine, b: Affine): Affine {\n return [\n a[0] * b[0] + a[2] * b[1],\n a[1] * b[0] + a[3] * b[1],\n a[0] * b[2] + a[2] * b[3],\n a[1] * b[2] + a[3] * b[3],\n a[0] * b[4] + a[2] * b[5] + a[4],\n a[1] * b[4] + a[3] * b[5] + a[5]\n ];\n}\n\nexport function translateTransform(transform: Affine, dx: number, dy: number): Affine {\n return [\n transform[0],\n transform[1],\n transform[2],\n transform[3],\n transform[4] + transform[0] * dx + transform[2] * dy,\n transform[5] + transform[1] * dx + transform[3] * dy\n ];\n}\n\nexport function applyTransform(transform: Affine, x: number, y: number): [number, number] {\n return [x * transform[0] + y * transform[2] + transform[4], x * transform[1] + y * transform[3] + transform[5]];\n}\n\nexport function invertTransform(t: Affine): Affine | null {\n const det = t[0] * t[3] - t[1] * t[2];\n if (det === 0 || !Number.isFinite(det)) {\n return null;\n }\n const a = t[3] / det;\n const b = -t[1] / det;\n const c = -t[2] / det;\n const d = t[0] / det;\n // `+ 0` folds -0 into 0 so equal transforms compare equal.\n return [a + 0, b + 0, c + 0, d + 0, -(a * t[4] + c * t[5]) + 0, -(b * t[4] + d * t[5]) + 0];\n}\n\n/**\n * Intersects the visible region with a clipping box and shifts by its\n * scroll offset, so child boxes (absolute, pre-scroll) can be tested\n * against the region their screen pixels occupy.\n */\nfunction pushCull(cull: CullRect, rec: LayoutRecord): CullRect {\n const right = Math.min(cull.x + cull.width, rec.x + rec.width);\n const bottom = Math.min(cull.y + cull.height, rec.y + rec.height);\n const left = Math.max(cull.x, rec.x);\n const top = Math.max(cull.y, rec.y);\n return {\n x: left + rec.scrollX,\n y: top + rec.scrollY,\n width: Math.max(0, right - left),\n height: Math.max(0, bottom - top)\n };\n}\n\nfunction emptyScissor(): ScissorRect {\n return { x: 0, y: 0, width: 0, height: 0 };\n}\n\nfunction intersectScissors(a: ScissorRect, b: ScissorRect): ScissorRect {\n const left = Math.max(a.x, b.x);\n const top = Math.max(a.y, b.y);\n const right = Math.min(a.x + a.width, b.x + b.width);\n const bottom = Math.min(a.y + a.height, b.y + b.height);\n const width = Math.max(0, right - left);\n const height = Math.max(0, bottom - top);\n if (width <= 0 || height <= 0) {\n return emptyScissor();\n }\n return { x: left, y: top, width, height };\n}\n\nfunction logicalClipToScissor(\n clip: LayoutBox,\n transform: Affine,\n logicalWidth: number,\n logicalHeight: number,\n dpr: number\n): ScissorRect | null {\n if (clip.width <= 0 || clip.height <= 0) {\n return null;\n }\n\n // Transform the four corners to screen space and take the bounding box.\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const [lx, ly] of [\n [clip.x, clip.y],\n [clip.x + clip.width, clip.y],\n [clip.x, clip.y + clip.height],\n [clip.x + clip.width, clip.y + clip.height]\n ]) {\n const [sx, sy] = applyTransform(transform, lx, ly);\n minX = Math.min(minX, sx);\n minY = Math.min(minY, sy);\n maxX = Math.max(maxX, sx);\n maxY = Math.max(maxY, sy);\n }\n\n // Intersect with viewport.\n minX = Math.max(0, minX);\n minY = Math.max(0, minY);\n maxX = Math.min(logicalWidth, maxX);\n maxY = Math.min(logicalHeight, maxY);\n\n if (maxX - minX <= 0 || maxY - minY <= 0) {\n return null;\n }\n\n // Round the edges, never the extent. Rounding the origin and the\n // width independently lets x + width land a pixel past the\n // attachment whenever both products have a fractional part of a\n // half or more — and an out-of-bounds scissor is a validation error\n // that discards the entire command buffer, losing the whole frame\n // rather than this one rectangle. Clamping to the backing store,\n // derived through the same rule the surface sizes it with, keeps\n // the result inside the attachment for any size and dpr.\n const physicalWidth = toPhysicalPixels(logicalWidth, dpr);\n const physicalHeight = toPhysicalPixels(logicalHeight, dpr);\n const x0 = clamp(Math.round(minX * dpr), 0, physicalWidth);\n const x1 = clamp(Math.round(maxX * dpr), 0, physicalWidth);\n const y0 = clamp(Math.round(minY * dpr), 0, physicalHeight);\n const y1 = clamp(Math.round(maxY * dpr), 0, physicalHeight);\n if (x1 <= x0 || y1 <= y0) {\n return null;\n }\n\n return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n\n/**\n * The scissor rectangle covering the whole attachment.\n *\n * Callers that need to *undo* a scissor have to set one explicitly:\n * a render pass keeps the last rectangle it was given, so \"no clip\"\n * means the full viewport, not the absence of a call.\n */\nexport function viewportScissor(logicalWidth: number, logicalHeight: number, dpr: number): ScissorRect {\n return {\n x: 0,\n y: 0,\n width: toPhysicalPixels(logicalWidth, dpr),\n height: toPhysicalPixels(logicalHeight, dpr)\n };\n}\n\nexport function scissorsEqual(a: ScissorRect | null, b: ScissorRect | null): boolean {\n if (a === b) {\n return true;\n }\n if (a === null || b === null) {\n return false;\n }\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n","import { WebGPUError } from './WebGPUError';\nimport { PRIMITIVE_SHADER, TEXTURED_SHADER, VIEW_UNIFORM_FLOATS } from './WebGPUShader';\nimport { INSTANCE_STRIDE_BYTES, TEXTURED_STRIDE_BYTES } from './WebGPURenderData';\n\nexport interface PrimitivePipeline {\n pipeline: GPURenderPipeline;\n vertexBuffer: GPUBuffer;\n indexBuffer: GPUBuffer;\n indexCount: number;\n uniformBuffer: GPUBuffer;\n uniformBindGroup: GPUBindGroup;\n uniformLayout: GPUBindGroupLayout;\n}\n\n/**\n * The bind group layout for what the whole frame shares (group 1): the\n * clip chain and the frame's gradients, one bind group for the pass.\n *\n * Both are read-only storage rather than uniforms because both are\n * variable-length per frame, and both live in group 1 rather than in a\n * group of their own because a second bind group would be a second\n * `setBindGroup` per pipeline switch for data neither pipeline changes.\n * The textured pipeline never samples the gradients; a bind group\n * layout may declare more than a shader uses.\n */\nexport function createFrameBindGroupLayout(device: GPUDevice): GPUBindGroupLayout {\n return device.createBindGroupLayout({\n entries: [\n { binding: 0, visibility: fragmentStage(), buffer: { type: 'read-only-storage' } },\n { binding: 1, visibility: fragmentStage(), buffer: { type: 'read-only-storage' } }\n ]\n });\n}\n\nexport interface TexturedPipeline {\n pipeline: GPURenderPipeline;\n vertexBuffer: GPUBuffer;\n indexBuffer: GPUBuffer;\n indexCount: number;\n uniformBuffer: GPUBuffer;\n bindGroupLayout: GPUBindGroupLayout;\n /** Nearest sampling: a glyph cell maps one-to-one onto pixels. */\n textSampler: GPUSampler;\n /** Linear sampling: images are scaled by objectFit. */\n imageSampler: GPUSampler;\n}\n\nconst QUAD_VERTICES = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);\nconst QUAD_INDICES = new Uint16Array([0, 1, 2, 2, 1, 3]);\n\nconst vertexStage = (): number => (typeof GPUShaderStage !== 'undefined' ? GPUShaderStage.VERTEX : 0x1);\nconst fragmentStage = (): number => (typeof GPUShaderStage !== 'undefined' ? GPUShaderStage.FRAGMENT : 0x2);\nconst usage = {\n vertex: (): number => (typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.VERTEX : 0x20),\n index: (): number => (typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.INDEX : 0x10),\n uniform: (): number => (typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.UNIFORM : 0x40),\n copyDst: (): number => (typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.COPY_DST : 0x8)\n};\n\n/**\n * Source-over for a straight-alpha source onto a premultiplied target,\n * which is what the canvas is configured as. Colour is scaled by the\n * source alpha; alpha itself is not — with `src-alpha` there too, a\n * half-transparent fill over a transparent canvas came out a quarter\n * transparent, and every faint panel drew dimmer than on Canvas2D.\n */\nconst STRAIGHT_ALPHA_BLEND: GPUBlendState = {\n color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },\n alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' }\n};\n\n/**\n * Creates the primitive pipeline: rounded fills and borders.\n *\n * Static resources (quad geometry, pipeline, bind group layout) are\n * created once and reused for the lifetime of the renderer.\n */\nexport function createPrimitivePipeline(\n device: GPUDevice,\n format: GPUTextureFormat,\n frameLayout: GPUBindGroupLayout\n): PrimitivePipeline {\n const module = device.createShaderModule({ code: PRIMITIVE_SHADER });\n\n const uniformLayout = device.createBindGroupLayout({\n entries: [{ binding: 0, visibility: vertexStage() | fragmentStage(), buffer: { type: 'uniform' } }]\n });\n\n const pipeline = device.createRenderPipeline({\n layout: device.createPipelineLayout({ bindGroupLayouts: [uniformLayout, frameLayout] }),\n vertex: { module, entryPoint: 'vs', buffers: [quadVertexLayout(), primitiveInstanceLayout()] },\n fragment: { module, entryPoint: 'fs', targets: [{ format, blend: STRAIGHT_ALPHA_BLEND }] },\n primitive: { topology: 'triangle-list', cullMode: 'none' }\n });\n if (pipeline === undefined) {\n throw new WebGPUError('Failed to create primitive render pipeline.', 'pipeline');\n }\n\n const { vertexBuffer, indexBuffer } = createQuad(device);\n const uniformBuffer = device.createBuffer({\n size: VIEW_UNIFORM_FLOATS * 4,\n usage: usage.uniform() | usage.copyDst()\n });\n const uniformBindGroup = device.createBindGroup({\n layout: uniformLayout,\n entries: [{ binding: 0, resource: { buffer: uniformBuffer } }]\n });\n\n return {\n pipeline,\n vertexBuffer,\n indexBuffer,\n indexCount: QUAD_INDICES.length,\n uniformBuffer,\n uniformBindGroup,\n uniformLayout\n };\n}\n\n/**\n * Creates the textured pipeline: one quad per rasterised text run or\n * image, each command binding its own texture.\n */\nexport function createTexturedPipeline(\n device: GPUDevice,\n format: GPUTextureFormat,\n frameLayout: GPUBindGroupLayout\n): TexturedPipeline {\n const module = device.createShaderModule({ code: TEXTURED_SHADER });\n\n const bindGroupLayout = device.createBindGroupLayout({\n entries: [\n { binding: 0, visibility: vertexStage() | fragmentStage(), buffer: { type: 'uniform' } },\n { binding: 1, visibility: fragmentStage(), sampler: { type: 'filtering' } },\n { binding: 2, visibility: fragmentStage(), texture: {} }\n ]\n });\n\n const pipeline = device.createRenderPipeline({\n layout: device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout, frameLayout] }),\n vertex: { module, entryPoint: 'vs', buffers: [quadVertexLayout(), texturedInstanceLayout()] },\n fragment: { module, entryPoint: 'fs', targets: [{ format, blend: STRAIGHT_ALPHA_BLEND }] },\n primitive: { topology: 'triangle-list', cullMode: 'none' }\n });\n if (pipeline === undefined) {\n throw new WebGPUError('Failed to create textured render pipeline.', 'pipeline');\n }\n\n const { vertexBuffer, indexBuffer } = createQuad(device);\n const uniformBuffer = device.createBuffer({\n size: VIEW_UNIFORM_FLOATS * 4,\n usage: usage.uniform() | usage.copyDst()\n });\n\n return {\n pipeline,\n vertexBuffer,\n indexBuffer,\n indexCount: QUAD_INDICES.length,\n uniformBuffer,\n bindGroupLayout,\n textSampler: device.createSampler({ magFilter: 'nearest', minFilter: 'nearest' }),\n imageSampler: device.createSampler({ magFilter: 'linear', minFilter: 'linear' })\n };\n}\n\nfunction createQuad(device: GPUDevice): { vertexBuffer: GPUBuffer; indexBuffer: GPUBuffer } {\n const vertexBuffer = device.createBuffer({\n size: QUAD_VERTICES.byteLength,\n usage: usage.vertex(),\n mappedAtCreation: true\n });\n new Float32Array(vertexBuffer.getMappedRange()).set(QUAD_VERTICES);\n vertexBuffer.unmap();\n\n const indexBuffer = device.createBuffer({\n size: QUAD_INDICES.byteLength,\n usage: usage.index(),\n mappedAtCreation: true\n });\n new Uint16Array(indexBuffer.getMappedRange()).set(QUAD_INDICES);\n indexBuffer.unmap();\n return { vertexBuffer, indexBuffer };\n}\n\nfunction quadVertexLayout(): GPUVertexBufferLayout {\n return {\n arrayStride: 8,\n stepMode: 'vertex',\n attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x2' }]\n };\n}\n\n/** Mirrors the float layout documented on INSTANCE_STRIDE_FLOATS. */\nfunction primitiveInstanceLayout(): GPUVertexBufferLayout {\n return {\n arrayStride: INSTANCE_STRIDE_BYTES,\n stepMode: 'instance',\n attributes: [\n { shaderLocation: 1, offset: 0, format: 'float32x2' }, // pos\n { shaderLocation: 2, offset: 8, format: 'float32x2' }, // size\n { shaderLocation: 3, offset: 16, format: 'float32x4' }, // color\n { shaderLocation: 4, offset: 32, format: 'float32x3' }, // radius, opacity, borderWidth\n { shaderLocation: 5, offset: 44, format: 'uint32' }, // kind\n { shaderLocation: 6, offset: 48, format: 'float32x2' }, // transform a\n { shaderLocation: 7, offset: 56, format: 'float32x2' }, // transform b\n { shaderLocation: 8, offset: 64, format: 'float32x2' }, // transform c\n { shaderLocation: 9, offset: 72, format: 'float32' }, // clip index\n { shaderLocation: 10, offset: 76, format: 'float32' } // gradient index\n ]\n };\n}\n\n/** Mirrors the float layout documented on TEXTURED_STRIDE_FLOATS. */\nfunction texturedInstanceLayout(): GPUVertexBufferLayout {\n return {\n arrayStride: TEXTURED_STRIDE_BYTES,\n stepMode: 'instance',\n attributes: [\n { shaderLocation: 1, offset: 0, format: 'float32x2' }, // pos\n { shaderLocation: 2, offset: 8, format: 'float32x2' }, // size\n { shaderLocation: 3, offset: 16, format: 'float32' }, // opacity\n { shaderLocation: 4, offset: 20, format: 'float32' }, // clip index\n { shaderLocation: 5, offset: 24, format: 'float32x2' }, // transform a\n { shaderLocation: 6, offset: 32, format: 'float32x2' }, // transform b\n { shaderLocation: 7, offset: 40, format: 'float32x2' }, // transform c\n { shaderLocation: 8, offset: 48, format: 'float32x2' }, // uv origin\n { shaderLocation: 9, offset: 56, format: 'float32x2' } // uv size\n ]\n };\n}\n","import type { UiImage } from '../PaintState';\nimport { isVideoSurface, videoFrameSize, type UiVideoSurface } from '../../properties/UiVideo';\nimport { ScaledImageCache, type ScaledImageSurfaceFactory } from '../ScaledImageCache';\nimport type { TexturedPipeline } from './WebGPUPipeline';\n\n/** A GPU texture with the bind group that draws it, ready to reuse. */\nexport interface TextureEntry {\n texture: GPUTexture;\n bindGroup: GPUBindGroup;\n /** For a video surface: the frame this texture currently holds. */\n version?: number;\n width?: number;\n height?: number;\n}\n\n/** Anything the textured pipeline can be given: a still, or a video's surface. */\nexport type TextureSource = UiImage | UiVideoSurface;\n\n/**\n * Textures for the textured pipeline's images.\n *\n * An image is uploaded once per `ImageBitmap` and lives as long as the\n * bitmap does — a `WeakMap`, so a bitmap the application drops takes\n * its texture with it. Bind groups are created with their texture, so\n * a frame allocates nothing for an image it has drawn before.\n *\n * **A video is the same map with one more comparison.** Its pixels\n * change every frame while the thing on screen stays the same thing,\n * so keying on the frame — which is a fresh `VideoFrame` object each\n * time — would allocate a texture and a bind group sixty times a\n * second and free none of them until the garbage collector felt like\n * it. Keying on the *surface*, whose identity is stable for the life\n * of the playback, and comparing `version`, means one texture per\n * video and one `copyExternalImageToTexture` per frame into the\n * texture that is already there. See `UiVideo.ts`.\n *\n * Text used to live here too, one texture per run. It is now\n * `WebGPUGlyphPages`: the same drawing, cut into glyphs and packed\n * into a shared atlas, so its memory follows the fonts in use rather\n * than the amount of text on screen.\n */\nexport class WebGPUTextureCache {\n private readonly images = new WeakMap<TextureSource, TextureEntry>();\n /**\n * Copies of stills at the size they are drawn, shared with the other\n * backend.\n *\n * A texture is uploaded once and sampled every frame, so what the\n * source size costs here is not upload bandwidth but minification: a\n * 480px cover drawn into 56 logical pixels is sampled down by more\n * than four with `minFilter: 'linear'` and no mip chain under it,\n * which both aliases and reads texels far apart. Uploading the copy\n * instead makes the sampling one-to-one, and takes the memory with\n * it: the same cover is a fifty-sixth of the texture.\n *\n * The cache hands back the source until a copy is worth making and\n * ready, so the first frames draw exactly as they did before. The\n * `WeakMap` keys on whichever bitmap it returned, so the copy gets\n * its own texture and the full-size one is dropped when the source\n * is.\n */\n private readonly scaled: ScaledImageCache;\n\n constructor(\n private readonly device: GPUDevice,\n private readonly pipeline: TexturedPipeline,\n createSurface?: ScaledImageSurfaceFactory\n ) {\n this.scaled = new ScaledImageCache(createSurface);\n }\n\n /** The bind group for a still or a video, uploading it when it must. */\n imageBindGroup(source: TextureSource, drawWidth = 0, drawHeight = 0): GPUBindGroup | null {\n return isVideoSurface(source) ? this.videoBindGroup(source) : this.stillBindGroup(source, drawWidth, drawHeight);\n }\n\n private stillBindGroup(image: UiImage, drawWidth: number, drawHeight: number): GPUBindGroup | null {\n // A caller that does not know the size gets the source, which is\n // what this did for every still before.\n const drawn =\n drawWidth > 0 && drawHeight > 0 ? this.scaled.resolve(image as ImageBitmap, drawWidth, drawHeight) : image;\n const entry = this.images.get(drawn);\n if (entry !== undefined) {\n return entry.bindGroup;\n }\n return this.upload(drawn, drawn, drawn.width, drawn.height, undefined)?.bindGroup ?? null;\n }\n\n /**\n * The same, for a surface whose contents move.\n *\n * Three outcomes: the version is unchanged and the existing bind\n * group is handed straight back; the version moved but the size did\n * not, so the frame is copied into the texture that is already\n * there; or the size changed too — a stream that switched\n * resolution — and the texture is rebuilt, which is rare enough to\n * be worth nothing more than this comment.\n */\n private videoBindGroup(surface: UiVideoSurface): GPUBindGroup | null {\n const frame = surface.frame;\n if (frame === null) {\n return null;\n }\n const { width, height } = videoFrameSize(surface);\n const entry = this.images.get(surface);\n if (entry !== undefined) {\n if (entry.version === surface.version) {\n return entry.bindGroup;\n }\n if (entry.width === width && entry.height === height) {\n entry.version = surface.version;\n return this.copyInto(entry.texture, frame, width, height) ? entry.bindGroup : null;\n }\n entry.texture.destroy();\n this.images.delete(surface);\n }\n return this.upload(surface, frame, width, height, surface.version)?.bindGroup ?? null;\n }\n\n private upload(\n key: TextureSource,\n frame: ImageBitmap | VideoFrame,\n width: number,\n height: number,\n version: number | undefined\n ): TextureEntry | null {\n const w = Math.max(1, Math.round(width));\n const h = Math.max(1, Math.round(height));\n const texture = createTexture(this.device, w, h);\n if (!this.copyInto(texture, frame, w, h)) {\n texture.destroy();\n return null;\n }\n const bindGroup = createTexturedBindGroup(this.device, this.pipeline, texture, this.pipeline.imageSampler);\n const entry: TextureEntry = { texture, bindGroup, version, width: w, height: h };\n this.images.set(key, entry);\n return entry;\n }\n\n private copyInto(texture: GPUTexture, frame: ImageBitmap | VideoFrame, width: number, height: number): boolean {\n try {\n this.device.queue.copyExternalImageToTexture({ source: frame as ImageBitmap }, { texture }, [\n Math.max(1, Math.round(width)),\n Math.max(1, Math.round(height))\n ]);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Drops the scaled copies.\n *\n * The textures are keyed weakly and go when their bitmaps do, but a\n * copy is held strongly by the scaled cache until it is evicted, so\n * the renderer has to say when it is finished with them.\n */\n dispose(): void {\n this.scaled.dispose();\n }\n}\n\n/** A texture the textured pipeline can sample and be copied into. */\nexport function createTexture(device: GPUDevice, width: number, height: number): GPUTexture {\n const textureBinding = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.TEXTURE_BINDING : 0x4;\n const copyDst = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.COPY_DST : 0x8;\n const renderAttachment = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.RENDER_ATTACHMENT : 0x10;\n return device.createTexture({\n size: [width, height],\n format: 'rgba8unorm',\n usage: textureBinding | copyDst | renderAttachment\n });\n}\n\n/** The pipeline's group 0: view uniforms, a sampler and one texture. */\nexport function createTexturedBindGroup(\n device: GPUDevice,\n pipeline: TexturedPipeline,\n texture: GPUTexture,\n sampler: GPUSampler\n): GPUBindGroup {\n return device.createBindGroup({\n layout: pipeline.bindGroupLayout,\n entries: [\n { binding: 0, resource: { buffer: pipeline.uniformBuffer } },\n { binding: 1, resource: sampler },\n { binding: 2, resource: texture.createView() }\n ]\n });\n}\n","import type { TexturedPipeline } from './WebGPUPipeline';\nimport type { WebGPUGlyphAtlas, GlyphUpload } from './WebGPUGlyphAtlas';\nimport { createTexture, createTexturedBindGroup } from './WebGPUTextureCache';\n\n/**\n * The GPU half of the glyph atlas: one texture per page, and the\n * rasterisation that fills the cells the builder allocated.\n *\n * The builder runs before this and has already decided where every\n * glyph goes, so all that is left here is to draw each newly allocated\n * cluster into a scratch canvas and copy it into its cell. That copy\n * happens once per glyph for the life of the page — a scrolling list\n * of a thousand rows uploads nothing after its first frame, because\n * the second row's letters are the first row's.\n *\n * Cells are rasterised in the run's own colour, so what the sampler\n * reads is exactly what `fillText` would have put on the canvas.\n */\nexport class WebGPUGlyphPages {\n private readonly textures: (GPUTexture | null)[] = [];\n private readonly bindGroups: (GPUBindGroup | null)[] = [];\n private scratch: OffscreenCanvas | HTMLCanvasElement | null = null;\n private scratchContext: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null = null;\n /** Cells uploaded since the renderer was created; for tests and metrics. */\n uploads = 0;\n\n constructor(\n private readonly device: GPUDevice,\n private readonly pipeline: TexturedPipeline,\n private readonly atlas: WebGPUGlyphAtlas\n ) {}\n\n /**\n * Recycles the textures of cleared pages and rasterises every cell\n * the last build allocated. Call after building a render list and\n * before encoding the pass that draws it.\n */\n flush(): void {\n for (const page of this.atlas.takeCleared()) {\n this.textures[page]?.destroy();\n this.textures[page] = null;\n this.bindGroups[page] = null;\n }\n const pending = this.atlas.takePending();\n for (const upload of pending) {\n this.upload(upload);\n }\n }\n\n /** The bind group for a page, creating its texture on first use. */\n bindGroup(page: number): GPUBindGroup | null {\n const existing = this.bindGroups[page];\n if (existing !== undefined && existing !== null) {\n return existing;\n }\n const texture = this.textureFor(page);\n if (texture === null) {\n return null;\n }\n const bindGroup = createTexturedBindGroup(this.device, this.pipeline, texture, this.pipeline.textSampler);\n this.bindGroups[page] = bindGroup;\n return bindGroup;\n }\n\n dispose(): void {\n for (const texture of this.textures) {\n texture?.destroy();\n }\n this.textures.length = 0;\n this.bindGroups.length = 0;\n this.scratch = null;\n this.scratchContext = null;\n }\n\n private textureFor(page: number): GPUTexture | null {\n const existing = this.textures[page];\n if (existing !== undefined && existing !== null) {\n return existing;\n }\n const size = this.atlas.pageSize;\n const texture = createTexture(this.device, size, size);\n this.textures[page] = texture;\n return texture;\n }\n\n private upload(upload: GlyphUpload): void {\n const context = this.scratchFor(upload.pixelWidth, upload.pixelHeight);\n if (context === null) {\n return;\n }\n const texture = this.textureFor(upload.slot.page);\n if (texture === null) {\n return;\n }\n context.setTransform(1, 0, 0, 1, 0, 0);\n context.clearRect(0, 0, upload.pixelWidth, upload.pixelHeight);\n context.scale(upload.dpr, upload.dpr);\n context.font = upload.font;\n context.direction = upload.rtl ? 'rtl' : 'ltr';\n context.fillStyle = upload.color;\n context.textAlign = 'left';\n context.textBaseline = 'alphabetic';\n // The pen is in physical pixels — its subpixel phase is a fraction\n // of one — and the context is scaled to logical, so divide it back.\n context.fillText(upload.cluster, upload.penX / upload.dpr, upload.penY / upload.dpr);\n\n this.device.queue.copyExternalImageToTexture(\n { source: this.scratch as GPUImageCopyExternalImageSource },\n { texture, origin: [upload.pageX, upload.pageY] },\n [upload.pixelWidth, upload.pixelHeight]\n );\n this.uploads++;\n }\n\n /**\n * One scratch canvas, grown to the largest cell seen. Growing resets\n * the context, which costs nothing here: every draw sets its own\n * font, fill and transform.\n */\n private scratchFor(width: number, height: number): typeof this.scratchContext {\n const canvas = this.scratch;\n if (canvas !== null && canvas.width >= width && canvas.height >= height) {\n return this.scratchContext;\n }\n const nextWidth = Math.max(width, canvas?.width ?? 0);\n const nextHeight = Math.max(height, canvas?.height ?? 0);\n const created = createCanvas(nextWidth, nextHeight);\n if (created === null) {\n return null;\n }\n this.scratch = created;\n this.scratchContext = created.getContext('2d') as\n | OffscreenCanvasRenderingContext2D\n | CanvasRenderingContext2D\n | null;\n return this.scratchContext;\n }\n}\n\nfunction createCanvas(width: number, height: number): OffscreenCanvas | HTMLCanvasElement | null {\n if (typeof OffscreenCanvas !== 'undefined') {\n return new OffscreenCanvas(width, height);\n }\n if (typeof document !== 'undefined') {\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return canvas;\n }\n return null;\n}\n","import { UiNodeType } from '../../graph/UiNodeType';\nimport type { UiNode } from '../../graph/UiNode';\nimport type { RenderContext } from '../RenderContext';\nimport type { RendererBackend, UiRenderer } from '../UiRenderer';\nimport type { WebGPUSurface } from './WebGPUSurface';\nimport { initializeWebGPU, onDeviceLost } from './WebGPUDevice';\nimport { WebGPUError } from './WebGPUError';\nimport {\n createFrameBindGroupLayout,\n createPrimitivePipeline,\n createTexturedPipeline,\n type PrimitivePipeline,\n type TexturedPipeline\n} from './WebGPUPipeline';\nimport {\n buildRenderList,\n createTextCache,\n CLIP_STRIDE_BYTES,\n CommandKind,\n GRADIENT_STRIDE_BYTES,\n INSTANCE_STRIDE_BYTES,\n TEXTURED_STRIDE_BYTES,\n viewportScissor,\n type RenderTextCache,\n type ScissorRect\n} from './WebGPURenderData';\nimport { VIEW_UNIFORM_FLOATS } from './WebGPUShader';\nimport { WebGPUTextureCache } from './WebGPUTextureCache';\nimport { WebGPUGlyphPages } from './WebGPUGlyphPages';\n\nexport interface WebGPURendererOptions {\n /** The WebGPU surface this renderer draws into. */\n surface: WebGPUSurface;\n /** Optional hooks for benchmarking and profiling. */\n hooks?: RenderHooks;\n /**\n * Receives GPU errors that would otherwise only reach the console —\n * validation failures and device loss. Inside a render worker the\n * console is out of sight, so the runtime forwards these to the shell.\n */\n onError?: (message: string) => void;\n}\n\nexport interface RenderHooks {\n onPrepareStart?(): void;\n onPrepareEnd?(ms: number): void;\n onUploadStart?(): void;\n onUploadEnd?(ms: number): void;\n onEncodeStart?(): void;\n onEncodeEnd?(ms: number): void;\n}\n\nconst vertexBufferUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.VERTEX : 0x20;\nconst storageBufferUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.STORAGE : 0x80;\nconst copyDstBufferUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.COPY_DST : 0x8;\n\n/** A frame read back from the GPU: RGBA, straight alpha, row-major. */\nexport interface CapturedFrame {\n width: number;\n height: number;\n data: Uint8ClampedArray;\n}\n\ninterface PendingCapture {\n resolve: (frame: CapturedFrame) => void;\n reject: (error: Error) => void;\n}\n\nconst copyDstUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.COPY_DST : 0x8;\nconst mapReadUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.MAP_READ : 0x1;\nconst mapModeRead = typeof GPUMapMode !== 'undefined' ? GPUMapMode.READ : 0x1;\n\n/** Draw calls issued for one frame, by kind; for tests and the profiler. */\nexport interface DrawStats {\n primitiveDraws: number;\n texturedDraws: number;\n texturedInstances: number;\n}\n\n/**\n * WebGPU rendering backend for the retained UI tree.\n *\n * Consumes the same UiNode tree and LayoutRecords as the\n * Canvas2DRenderer. It owns no composition, bindings, layout, or\n * scheduling state. GPU resources are created during initialize() and\n * reused across frames; per-frame instance data is uploaded to two\n * growable vertex buffers, one per pipeline, and the frame's command\n * list is walked in paint order switching pipelines as it goes.\n */\nexport class WebGPURenderer implements UiRenderer {\n readonly backend: RendererBackend = 'webgpu';\n\n private readonly surface: WebGPUSurface;\n private hooks: RenderHooks;\n private device: GPUDevice | null = null;\n private format: GPUTextureFormat | null = null;\n private primitives: PrimitivePipeline | null = null;\n private textured: TexturedPipeline | null = null;\n private textures: WebGPUTextureCache | null = null;\n private glyphPages: WebGPUGlyphPages | null = null;\n /** The glyph atlas and line shaper, kept across frames. */\n private readonly textCache: RenderTextCache = createTextCache();\n private instanceBuffer: GPUBuffer | null = null;\n private texturedBuffer: GPUBuffer | null = null;\n private clipBuffer: GPUBuffer | null = null;\n private gradientBuffer: GPUBuffer | null = null;\n private frameLayout: GPUBindGroupLayout | null = null;\n private frameBindGroup: GPUBindGroup | null = null;\n private cachedInstanceData: Float32Array | null = null;\n /** Draw calls of the most recent frame. */\n readonly lastDraws: DrawStats = { primitiveDraws: 0, texturedDraws: 0, texturedInstances: 0 };\n private pendingCapture: PendingCapture | null = null;\n private lost = false;\n private disposed = false;\n private readonly removeLostListener: () => void;\n\n private readonly onError: (message: string) => void;\n\n constructor(options: WebGPURendererOptions) {\n this.surface = options.surface;\n this.hooks = options.hooks ?? {};\n // eslint-disable-next-line no-console\n this.onError = options.onError ?? (message => console.error(message));\n this.removeLostListener = onDeviceLost(info => {\n this.lost = true;\n this.onError(`WebGPU device lost: ${info.reason} ${info.message}`);\n });\n }\n\n /**\n * Replaces the render hooks used for profiling. Safe to call at any\n * time, including between frames.\n */\n setHooks(hooks: RenderHooks): void {\n this.hooks = hooks;\n }\n\n /**\n * Acquires the adapter, device and canvas context. Must be called\n * once before render(). The canvas is not touched until the device\n * exists, so a failure leaves it free for a Canvas2D fallback.\n */\n async initialize(): Promise<void> {\n if (this.disposed) {\n throw new WebGPUError('Renderer is disposed.', 'disposed');\n }\n const init = await initializeWebGPU();\n if (this.disposed) {\n return;\n }\n this.device = init.device;\n this.format = init.format;\n init.device.onuncapturederror = event => {\n this.onError(`WebGPU error: ${event.error.message}`);\n };\n this.surface.configure(init.device, init.format);\n this.frameLayout = createFrameBindGroupLayout(init.device);\n this.primitives = createPrimitivePipeline(init.device, init.format, this.frameLayout);\n this.textured = createTexturedPipeline(init.device, init.format, this.frameLayout);\n this.textures = new WebGPUTextureCache(init.device, this.textured);\n this.glyphPages = new WebGPUGlyphPages(init.device, this.textured, this.textCache.atlas);\n this.lost = false;\n }\n\n /**\n * True when the renderer has been initialized and the device is\n * still alive.\n */\n get isReady(): boolean {\n return (\n this.device !== null &&\n this.format !== null &&\n this.primitives !== null &&\n this.textured !== null &&\n !this.lost &&\n !this.disposed\n );\n }\n\n /** True once the device has been lost; frames are skipped from then on. */\n get isLost(): boolean {\n return this.lost;\n }\n\n /**\n * Resolves with the pixels of the next frame `render()` draws, read\n * back from the GPU rather than from the canvas, so the result does\n * not depend on when the compositor presents. For the parity check.\n */\n capture(): Promise<CapturedFrame> {\n return new Promise((resolve, reject) => {\n this.pendingCapture?.reject(new Error('Superseded by a later capture.'));\n this.pendingCapture = { resolve, reject };\n });\n }\n\n /**\n * Renders one frame.\n *\n * Builds a CPU-side render list from the tree, uploads it to the\n * GPU, and walks the command list: one instanced draw per run of\n * primitives under a scissor, one draw per text run or image.\n */\n render(root: UiNode, context: RenderContext): void {\n if (!this.isReady) {\n return;\n }\n if (this.disposed) {\n throw new WebGPUError('Renderer is disposed.', 'disposed');\n }\n if (root.type === UiNodeType.Root && !root.hasChildren()) {\n this.clearFrame();\n return;\n }\n\n // Each stage samples its start clock when either of its hooks is\n // set. Sampling only for the Start hook meant a caller that wants\n // durations alone — which the benchmark route does — measured\n // every stage from 0, so all three reported the absolute clock\n // instead of an elapsed time, and looked identical to each other.\n const prepareStart =\n this.hooks.onPrepareStart !== undefined || this.hooks.onPrepareEnd !== undefined ? performance.now() : 0;\n this.textCache.atlas.beginFrame();\n const list = buildRenderList(\n root,\n context.layout,\n context.text,\n this.surface.logicalWidth,\n this.surface.logicalHeight,\n this.surface.dpr,\n context.now,\n context.overlay ?? [],\n this.textCache\n );\n if (this.hooks.onPrepareEnd !== undefined) {\n this.hooks.onPrepareEnd(performance.now() - prepareStart);\n }\n\n const device = this.device!;\n const primitives = this.primitives!;\n const textured = this.textured!;\n const textures = this.textures!;\n const glyphPages = this.glyphPages!;\n // The build allocated atlas cells; fill the new ones before the\n // pass that samples them.\n glyphPages.flush();\n\n const uploadStart =\n this.hooks.onUploadStart !== undefined || this.hooks.onUploadEnd !== undefined ? performance.now() : 0;\n if (list.instanceCount > 0) {\n const buffer = this.ensureBuffer(this.instanceBuffer, list.instanceData.byteLength, INSTANCE_STRIDE_BYTES);\n if (buffer !== this.instanceBuffer) {\n this.instanceBuffer = buffer;\n // New GPU memory is undefined; force a full upload.\n this.cachedInstanceData = null;\n }\n this.uploadInstanceData(list.instanceData);\n }\n if (list.texturedCount > 0) {\n this.texturedBuffer = this.ensureBuffer(this.texturedBuffer, list.texturedData.byteLength, TEXTURED_STRIDE_BYTES);\n device.queue.writeBuffer(this.texturedBuffer, 0, list.texturedData.buffer, 0, list.texturedData.byteLength);\n }\n // The clip chain and the gradients are bound even when empty: the\n // shaders declare them and every instance names -1. The bind group\n // follows the buffers.\n const clipBuffer = this.ensureBuffer(\n this.clipBuffer,\n list.clipData.byteLength,\n CLIP_STRIDE_BYTES,\n storageBufferUsage\n );\n const gradientBuffer = this.ensureBuffer(\n this.gradientBuffer,\n list.gradientData.byteLength,\n GRADIENT_STRIDE_BYTES,\n storageBufferUsage\n );\n if (clipBuffer !== this.clipBuffer || gradientBuffer !== this.gradientBuffer || this.frameBindGroup === null) {\n this.clipBuffer = clipBuffer;\n this.gradientBuffer = gradientBuffer;\n this.frameBindGroup = device.createBindGroup({\n layout: this.frameLayout!,\n entries: [\n { binding: 0, resource: { buffer: clipBuffer } },\n { binding: 1, resource: { buffer: gradientBuffer } }\n ]\n });\n }\n if (list.clipCount > 0) {\n device.queue.writeBuffer(clipBuffer, 0, list.clipData.buffer, 0, list.clipData.byteLength);\n }\n if (list.gradientCount > 0) {\n device.queue.writeBuffer(gradientBuffer, 0, list.gradientData.buffer, 0, list.gradientData.byteLength);\n }\n const view = new Float32Array(VIEW_UNIFORM_FLOATS);\n view[0] = this.surface.logicalWidth || 1;\n view[1] = this.surface.logicalHeight || 1;\n view[2] = this.surface.dpr || 1;\n device.queue.writeBuffer(primitives.uniformBuffer, 0, view);\n device.queue.writeBuffer(textured.uniformBuffer, 0, view);\n if (this.hooks.onUploadEnd !== undefined) {\n this.hooks.onUploadEnd(performance.now() - uploadStart);\n }\n\n const encodeStart =\n this.hooks.onEncodeStart !== undefined || this.hooks.onEncodeEnd !== undefined ? performance.now() : 0;\n const commandEncoder = device.createCommandEncoder();\n const texture = this.surface.getCurrentTexture();\n const pass = commandEncoder.beginRenderPass({\n colorAttachments: [\n { view: texture.createView(), clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: 'clear', storeOp: 'store' }\n ]\n });\n\n // A pass retains the last scissor it was given, so an unclipped\n // command has to restore the full viewport rather than skip the\n // call: skipping left it drawing under whichever clip the\n // preceding command happened to install.\n const viewport = viewportScissor(this.surface.logicalWidth, this.surface.logicalHeight, this.surface.dpr);\n let currentScissor: ScissorRect | null = null;\n let currentKind: CommandKind | null = null;\n pass.setBindGroup(1, this.frameBindGroup!);\n this.lastDraws.primitiveDraws = 0;\n this.lastDraws.texturedDraws = 0;\n this.lastDraws.texturedInstances = 0;\n\n const commands = list.commands;\n for (let i = 0; i < commands.length; i++) {\n const command = commands[i];\n const scissor = command.scissor ?? viewport;\n if (scissor.width <= 0 || scissor.height <= 0) {\n // Everything under this clip is off screen; a zero scissor\n // draws nothing and some validators reject it.\n continue;\n }\n if (command.kind === CommandKind.Primitives) {\n if (command.end <= command.start) {\n continue;\n }\n if (currentKind !== CommandKind.Primitives) {\n pass.setPipeline(primitives.pipeline);\n pass.setVertexBuffer(0, primitives.vertexBuffer);\n pass.setVertexBuffer(1, this.instanceBuffer!);\n pass.setIndexBuffer(primitives.indexBuffer, 'uint16');\n pass.setBindGroup(0, primitives.uniformBindGroup);\n currentKind = CommandKind.Primitives;\n }\n currentScissor = setScissor(pass, currentScissor, scissor);\n pass.drawIndexed(primitives.indexCount, command.end - command.start, 0, 0, command.start);\n this.lastDraws.primitiveDraws++;\n continue;\n }\n\n const glyphs = command.kind === CommandKind.Glyphs;\n const bindGroup = glyphs\n ? glyphPages.bindGroup(command.page)\n : textures.imageBindGroup(command.source, command.drawWidth, command.drawHeight);\n if (bindGroup === null) {\n continue;\n }\n // Glyphs and images share one pipeline and one instance buffer;\n // only the sampled texture and the UV rectangle differ.\n if (currentKind !== CommandKind.Glyphs) {\n pass.setPipeline(textured.pipeline);\n pass.setVertexBuffer(0, textured.vertexBuffer);\n pass.setVertexBuffer(1, this.texturedBuffer!);\n pass.setIndexBuffer(textured.indexBuffer, 'uint16');\n currentKind = CommandKind.Glyphs;\n }\n currentScissor = setScissor(pass, currentScissor, scissor);\n pass.setBindGroup(0, bindGroup);\n // The builder already merged every run of glyphs sharing a page\n // and a scissor, so a screen of text in one font is one call.\n const count = glyphs ? command.end - command.start : 1;\n if (count <= 0) {\n continue;\n }\n pass.drawIndexed(textured.indexCount, count, 0, 0, glyphs ? command.start : command.instance);\n this.lastDraws.texturedDraws++;\n this.lastDraws.texturedInstances += count;\n }\n\n pass.end();\n const capture = this.takeCapture(device, commandEncoder, texture);\n device.queue.submit([commandEncoder.finish()]);\n capture?.();\n if (this.hooks.onEncodeEnd !== undefined) {\n this.hooks.onEncodeEnd(performance.now() - encodeStart);\n }\n }\n\n /**\n * When a capture is pending, encodes a copy of the frame into a\n * mappable buffer and returns the step that maps it after submit.\n */\n private takeCapture(device: GPUDevice, encoder: GPUCommandEncoder, texture: GPUTexture): (() => void) | null {\n const pending = this.pendingCapture;\n if (pending === null) {\n return null;\n }\n this.pendingCapture = null;\n const width = texture.width;\n const height = texture.height;\n // Rows are padded to 256 bytes, as copyTextureToBuffer requires.\n const bytesPerRow = Math.ceil((width * 4) / 256) * 256;\n const buffer = device.createBuffer({ size: bytesPerRow * height, usage: copyDstUsage | mapReadUsage });\n encoder.copyTextureToBuffer({ texture }, { buffer, bytesPerRow }, { width, height });\n const bgra = this.format === 'bgra8unorm' || this.format === 'bgra8unorm-srgb';\n return () => {\n buffer\n .mapAsync(mapModeRead)\n .then(() => {\n const mapped = new Uint8Array(buffer.getMappedRange());\n const data = new Uint8ClampedArray(width * height * 4);\n for (let y = 0; y < height; y++) {\n const row = y * bytesPerRow;\n const out = y * width * 4;\n for (let x = 0; x < width; x++) {\n const i = row + x * 4;\n const o = out + x * 4;\n data[o] = mapped[bgra ? i + 2 : i];\n data[o + 1] = mapped[i + 1];\n data[o + 2] = mapped[bgra ? i : i + 2];\n data[o + 3] = mapped[i + 3];\n }\n }\n buffer.unmap();\n buffer.destroy();\n pending.resolve({ width, height, data });\n })\n .catch((error: unknown) => {\n buffer.destroy();\n pending.reject(error instanceof Error ? error : new Error(String(error)));\n });\n };\n }\n\n /**\n * Clears the surface when there is nothing else to draw.\n */\n private clearFrame(): void {\n const device = this.device!;\n const commandEncoder = device.createCommandEncoder();\n const texture = this.surface.getCurrentTexture();\n const pass = commandEncoder.beginRenderPass({\n colorAttachments: [\n { view: texture.createView(), clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: 'clear', storeOp: 'store' }\n ]\n });\n pass.end();\n device.queue.submit([commandEncoder.finish()]);\n }\n\n /**\n * Updates the logical/physical size and reconfigures the canvas\n * context when the backing store changed.\n */\n resize(width: number, height: number, dpr: number = 1): void {\n if (this.disposed) {\n throw new WebGPUError('Renderer is disposed.', 'disposed');\n }\n const changed = this.surface.setLogicalSize(width, height, dpr);\n if (changed && this.device !== null && this.format !== null) {\n this.surface.configure(this.device, this.format);\n }\n }\n\n /**\n * Drops every shaped line and every atlas cell. The cells were\n * rasterised with whatever font the string resolved to at the time,\n * which was the fallback; the page textures go with them, since a\n * page index would otherwise be reused over stale pixels.\n */\n fontsChanged(): void {\n this.textCache.shaper.clear();\n this.textCache.atlas.reset();\n this.glyphPages?.dispose();\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.removeLostListener();\n this.surface.unconfigure();\n this.textures?.dispose();\n this.textures = null;\n this.glyphPages?.dispose();\n this.glyphPages = null;\n // The atlas outlives the device: its slots are plain packing, and\n // a re-initialized renderer re-uploads them into fresh textures.\n this.textCache.atlas.reset();\n this.instanceBuffer?.destroy();\n this.instanceBuffer = null;\n this.texturedBuffer?.destroy();\n this.texturedBuffer = null;\n this.clipBuffer?.destroy();\n this.clipBuffer = null;\n this.gradientBuffer?.destroy();\n this.gradientBuffer = null;\n this.frameBindGroup = null;\n this.frameLayout = null;\n this.primitives = null;\n this.textured = null;\n // The device is owned by the WebGPU initialization layer; the\n // renderer only borrows it. Do not destroy it here.\n this.device = null;\n }\n\n private uploadInstanceData(data: Float32Array): void {\n if (this.cachedInstanceData !== null && arraysEqual(this.cachedInstanceData, data)) {\n return;\n }\n this.device!.queue.writeBuffer(this.instanceBuffer!, 0, data.buffer, 0, data.byteLength);\n this.cachedInstanceData = new Float32Array(data);\n }\n\n /**\n * Returns a vertex buffer of at least `byteLength`, reusing the\n * current one when it is big enough and replacing it otherwise.\n */\n private ensureBuffer(\n current: GPUBuffer | null,\n byteLength: number,\n stride: number,\n usage: number = vertexBufferUsage\n ): GPUBuffer {\n if (current !== null && current.size >= byteLength) {\n return current;\n }\n current?.destroy();\n // Round up to whole instances, with headroom so a growing list does\n // not reallocate every frame.\n const size = Math.max(Math.ceil((byteLength * 1.5) / stride) * stride, stride * 16);\n return this.device!.createBuffer({ size, usage: usage | copyDstBufferUsage });\n }\n}\n\nfunction setScissor(pass: GPURenderPassEncoder, current: ScissorRect | null, next: ScissorRect): ScissorRect {\n if (\n current !== null &&\n current.x === next.x &&\n current.y === next.y &&\n current.width === next.width &&\n current.height === next.height\n ) {\n return current;\n }\n pass.setScissorRect(next.x, next.y, next.width, next.height);\n return next;\n}\n\nfunction arraysEqual(a: Float32Array, b: Float32Array): boolean {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n","/**\n * Just enough MPEG audio to feed an `AudioDecoder`, one arriving chunk\n * at a time.\n *\n * The sibling of `Mp4Demuxer`, and here for the same reason: WebCodecs\n * decodes and does not demux, so `AudioDecoder` takes\n * `EncodedAudioChunk`s and getting those out of an `.mp3` means\n * splitting the frames yourself. An `.mp3` has no container to walk,\n * only a run of self-describing frames, so this is a header parse\n * rather than a box tree.\n *\n * **It splits as the bytes arrive.** `Mp4Demuxer` parses a whole file\n * at once because a fifteen-second loop is small and its sample tables\n * live at the end. An `.mp3` is the opposite on both counts: a track\n * is megabytes, every frame describes itself, and\n * the bytes take longer to arrive than anything done with them takes\n * to run. So `push` takes whatever a stream reader just handed you,\n * returns the whole frames it can make from that and what it was\n * holding, and holds the partial tail for next time. Splitting the\n * whole of a five-minute track costs about 3 ms, so the incremental\n * shape is for latency rather than for throughput: it exists so the\n * first frames can be decoded while the rest is still on the wire.\n *\n * **What it understands.** MPEG-1, MPEG-2 and MPEG-2.5, layers I, II\n * and III, constant or variable bitrate, with an ID3v2 tag at the\n * front and junk anywhere. A Xing, Info or VBRI header frame is read\n * for what it knows about the file and then dropped, because it\n * carries no audio.\n *\n * **What it does not.** Free-format streams, whose frames declare no\n * bitrate and must be measured by finding the next header. They are\n * skipped as junk rather than mis-parsed into noise.\n */\n\n/** How a stream's frames are shaped, read from the first one. */\nexport interface Mp3Format {\n readonly version: 'mpeg1' | 'mpeg2' | 'mpeg2.5';\n /** 1, 2 or 3. Nearly always 3. */\n readonly layer: 1 | 2 | 3;\n readonly sampleRate: number;\n /** 1 for mono, 2 for every stereo mode. */\n readonly channels: number;\n /** The first frame's bitrate in bits per second; a variable bitrate stream's later frames differ. */\n readonly bitrate: number;\n /** What one frame decodes to: 1152, or 576 for layer III below MPEG-1, or 384 for layer I. */\n readonly samplesPerFrame: number;\n /**\n * Frames in the whole stream, when a Xing, Info or VBRI header said\n * so. The only way to know a variable bitrate file's duration\n * without reading all of it, and absent from most constant bitrate\n * files, which do not need one.\n */\n readonly totalFrames?: number;\n /** Bytes in the whole stream, when the same header said so. */\n readonly totalBytes?: number;\n}\n\n/** One frame, ready to become an `EncodedAudioChunk`. */\nexport interface Mp3Frame {\n /** The frame's own bytes, header included. A view into memory this splitter no longer touches. */\n readonly bytes: Uint8Array;\n /** Where the frame began, in bytes from the first byte pushed. */\n readonly offset: number;\n /** What this frame decodes to. */\n readonly samples: number;\n /** Where the frame begins, in samples from the first audio frame. */\n readonly sampleOffset: number;\n /** The same position in microseconds, which is what WebCodecs takes. */\n readonly timestampUs: number;\n readonly durationUs: number;\n}\n\n// ---------------------------------------------------------------------------\n// The header\n// ---------------------------------------------------------------------------\n\n/** Indexed by layer, then by the header's four bitrate bits, in kbps. */\nconst MPEG1_BITRATES: readonly (readonly number[])[] = [\n [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],\n [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],\n [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0]\n];\n\nconst MPEG2_BITRATES: readonly (readonly number[])[] = [\n [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],\n [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],\n [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0]\n];\n\n/** Indexed by the header's two version bits, then its two rate bits. */\nconst SAMPLE_RATES: Readonly<Record<number, readonly number[]>> = {\n 0: [11025, 12000, 8000],\n 2: [22050, 24000, 16000],\n 3: [44100, 48000, 32000]\n};\n\nconst VERSIONS: Readonly<Record<number, Mp3Format['version']>> = {\n 0: 'mpeg2.5',\n 2: 'mpeg2',\n 3: 'mpeg1'\n};\n\n/** The longest a frame can be: MPEG-1 layer II at 384 kbps and 32 kHz, with padding. */\nconst MAX_FRAME_BYTES = 1729;\n\n/** A frame's four header bytes, read. */\nexport interface Mp3Header {\n readonly version: Mp3Format['version'];\n readonly layer: 1 | 2 | 3;\n readonly sampleRate: number;\n readonly channels: number;\n readonly bitrate: number;\n readonly samples: number;\n /** The whole frame, header included. */\n readonly length: number;\n /** True when the mode bits say something other than mono, which is where a Xing header sits. */\n readonly stereo: boolean;\n}\n\n/**\n * The four bytes at `at`, read as a frame header, or null when they\n * are not one.\n *\n * Every reserved combination is a rejection rather than a guess: with\n * eleven sync bits, roughly one position in two thousand of arbitrary\n * data looks like the start of a header, and the reserved values are\n * most of what rules the impostors out.\n */\nexport function readMp3Header(bytes: Uint8Array, at: number): Mp3Header | null {\n if (at < 0 || at + 4 > bytes.length) {\n return null;\n }\n if (bytes[at] !== 0xff || (bytes[at + 1]! & 0xe0) !== 0xe0) {\n return null;\n }\n const versionBits = (bytes[at + 1]! >> 3) & 0x03;\n const layerBits = (bytes[at + 1]! >> 1) & 0x03;\n if (versionBits === 1 || layerBits === 0) {\n return null;\n }\n const rateBits = (bytes[at + 2]! >> 2) & 0x03;\n const bitrateBits = (bytes[at + 2]! >> 4) & 0x0f;\n // A bitrate of zero is the free format, whose frame length is not\n // declared anywhere; fifteen is reserved outright.\n if (rateBits === 3 || bitrateBits === 0 || bitrateBits === 15) {\n return null;\n }\n\n const version = VERSIONS[versionBits]!;\n const layer = (4 - layerBits) as 1 | 2 | 3;\n const mpeg1 = version === 'mpeg1';\n const bitrate = (mpeg1 ? MPEG1_BITRATES : MPEG2_BITRATES)[layer - 1]![bitrateBits]! * 1000;\n const sampleRate = SAMPLE_RATES[versionBits]![rateBits]!;\n const padding = (bytes[at + 2]! >> 1) & 0x01;\n const stereo = ((bytes[at + 3]! >> 6) & 0x03) !== 3;\n\n const samples = layer === 1 ? 384 : layer === 2 || mpeg1 ? 1152 : 576;\n const length =\n layer === 1\n ? (Math.floor((12 * bitrate) / sampleRate) + padding) * 4\n : Math.floor((samples / 8) * (bitrate / sampleRate)) + padding;\n if (length < 4 || length > MAX_FRAME_BYTES) {\n return null;\n }\n\n return { version, layer, sampleRate, channels: stereo ? 2 : 1, bitrate, samples, length, stereo };\n}\n\n/** Two headers describe the same stream when everything but the bitrate agrees. */\nfunction alike(one: Mp3Header, other: Mp3Header): boolean {\n return one.version === other.version && one.layer === other.layer && one.sampleRate === other.sampleRate;\n}\n\n// ---------------------------------------------------------------------------\n// Tags\n// ---------------------------------------------------------------------------\n\n/**\n * The length of an ID3v2 tag beginning at byte zero, or zero when\n * there is none, or -1 when the first ten bytes have not arrived and\n * the question cannot be answered yet.\n */\nfunction id3Length(bytes: Uint8Array): number {\n if (bytes.length < 3) {\n return bytes.length === 0 || bytes[0] === 0x49 ? -1 : 0;\n }\n if (bytes[0] !== 0x49 || bytes[1] !== 0x44 || bytes[2] !== 0x33) {\n return 0;\n }\n if (bytes.length < 10) {\n return -1;\n }\n // A syncsafe integer: seven bits per byte, so no byte can look like a sync word.\n const size =\n (bytes[6]! & 0x7f) * 0x200000 + (bytes[7]! & 0x7f) * 0x4000 + (bytes[8]! & 0x7f) * 0x80 + (bytes[9]! & 0x7f);\n // A footer, when the flags say there is one, is ten bytes more.\n return 10 + size + ((bytes[5]! & 0x10) !== 0 ? 10 : 0);\n}\n\nfunction ascii(bytes: Uint8Array, at: number, text: string): boolean {\n if (at + text.length > bytes.length) {\n return false;\n }\n for (let i = 0; i < text.length; i += 1) {\n if (bytes[at + i] !== text.charCodeAt(i)) {\n return false;\n }\n }\n return true;\n}\n\nfunction u32(bytes: Uint8Array, at: number): number {\n return bytes[at]! * 0x1000000 + bytes[at + 1]! * 0x10000 + bytes[at + 2]! * 0x100 + bytes[at + 3]!;\n}\n\n/**\n * What a Xing, Info or VBRI header in this frame says about the whole\n * stream, or null when the frame carries audio like any other.\n *\n * Encoders put one in the first frame of a variable bitrate file so\n * that a player can know the duration without reading to the end, and\n * many put one in a constant bitrate file too, where it is called Info\n * instead. The frame decodes to silence either way, so a caller wants\n * the numbers and not the frame.\n */\nfunction vbrHeader(frame: Uint8Array, head: Mp3Header): { totalFrames?: number; totalBytes?: number } | null {\n // Xing sits immediately after the side information, whose size is\n // fixed by the version and the channel mode.\n const sideInfo = head.version === 'mpeg1' ? (head.stereo ? 32 : 17) : head.stereo ? 17 : 9;\n const at = 4 + sideInfo;\n if (ascii(frame, at, 'Xing') || ascii(frame, at, 'Info')) {\n if (at + 8 > frame.length) {\n return {};\n }\n const flags = u32(frame, at + 4);\n let field = at + 8;\n const found: { totalFrames?: number; totalBytes?: number } = {};\n if ((flags & 0x01) !== 0) {\n if (field + 4 > frame.length) return found;\n found.totalFrames = u32(frame, field);\n field += 4;\n }\n if ((flags & 0x02) !== 0) {\n if (field + 4 > frame.length) return found;\n found.totalBytes = u32(frame, field);\n }\n return found;\n }\n // VBRI is Fraunhofer's, and always sits 32 bytes after the header.\n if (ascii(frame, 36, 'VBRI')) {\n if (frame.length < 54) {\n return {};\n }\n return { totalBytes: u32(frame, 44), totalFrames: u32(frame, 48) };\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// The splitter\n// ---------------------------------------------------------------------------\n\nconst EMPTY: readonly Mp3Frame[] = [];\n\n/**\n * A stream of MPEG audio, split into frames as it arrives.\n *\n * ```ts\n * const frames = new Mp3Frames();\n * const reader = response.body.getReader();\n * for (;;) {\n * const { done, value } = await reader.read();\n * if (done) break;\n * for (const frame of frames.push(value)) {\n * decoder.decode(new EncodedAudioChunk({ type: 'key', ...frame }));\n * }\n * }\n * ```\n *\n * Every frame of layer III is a key frame as WebCodecs counts them,\n * even though the bit reservoir means a frame may lean on up to 511\n * bytes of its predecessors. That is why decoding must begin at the\n * start of the stream rather than in the middle of it: the first\n * frames after an arbitrary offset decode to noise.\n */\nexport class Mp3Frames {\n /** Bytes pushed but not yet made into frames. */\n private carry: Uint8Array = new Uint8Array(0);\n /** Bytes consumed from the stream, which is what makes an offset absolute. */\n private consumed = 0;\n /** Still to skip from an ID3v2 tag that ran past the end of a chunk. */\n private skipping = 0;\n /** Nothing has been read yet, so the front of the stream may be a tag. */\n private atStart = true;\n /** A first frame has been confirmed by a second one behind it. */\n private locked = false;\n private shape: Mp3Format | null = null;\n private frames = 0;\n private samples = 0;\n\n /** How the stream is shaped, once its first frame has been read. */\n get format(): Mp3Format | null {\n return this.shape;\n }\n\n /** Audio frames returned so far, the dropped header frame not counted. */\n get frameCount(): number {\n return this.frames;\n }\n\n /** Samples those frames decode to, which is the duration read so far. */\n get sampleCount(): number {\n return this.samples;\n }\n\n /** Seconds of audio returned so far, or zero before the first frame. */\n get seconds(): number {\n return this.shape === null ? 0 : this.samples / this.shape.sampleRate;\n }\n\n /**\n * Take the next bytes of the stream and return every whole frame\n * they complete. The partial tail is held until the rest of it\n * arrives, so a caller may hand over chunks of any size, including\n * ones that end in the middle of a header.\n */\n push(bytes: Uint8Array): readonly Mp3Frame[] {\n if (bytes.length === 0) {\n return EMPTY;\n }\n this.append(bytes);\n return this.split(false);\n }\n\n /**\n * The stream has ended: return anything the tail still holds.\n *\n * Usually nothing, because `push` returns each frame the moment its\n * last byte arrives. It matters for a stream so short that no second\n * frame ever confirmed the first, which is otherwise held back\n * forever rather than risk locking onto an impostor.\n */\n flush(): readonly Mp3Frame[] {\n const rest = this.split(true);\n this.carry = new Uint8Array(0);\n return rest;\n }\n\n private append(bytes: Uint8Array): void {\n if (this.skipping > 0) {\n const skipped = Math.min(this.skipping, bytes.length);\n this.skipping -= skipped;\n this.consumed += skipped;\n bytes = bytes.subarray(skipped);\n if (bytes.length === 0) {\n return;\n }\n }\n if (this.carry.length === 0) {\n this.carry = bytes;\n return;\n }\n const joined = new Uint8Array(this.carry.length + bytes.length);\n joined.set(this.carry);\n joined.set(bytes, this.carry.length);\n this.carry = joined;\n }\n\n /**\n * Walk the carry, emitting frames, and leave behind whatever could\n * not be finished. `ending` allows a first frame to be taken on\n * trust, because there will be no later chunk to confirm it with.\n */\n private split(ending: boolean): readonly Mp3Frame[] {\n if (this.atStart) {\n const tag = id3Length(this.carry);\n if (tag === -1) {\n return EMPTY;\n }\n this.atStart = false;\n if (tag > 0) {\n const skipped = Math.min(tag, this.carry.length);\n this.skipping = tag - skipped;\n this.consumed += skipped;\n this.carry = this.carry.subarray(skipped);\n if (this.skipping > 0) {\n this.carry = new Uint8Array(0);\n return EMPTY;\n }\n }\n }\n\n const found: Mp3Frame[] = [];\n let at = 0;\n for (;;) {\n const head = readMp3Header(this.carry, at);\n if (head === null) {\n if (at + 4 > this.carry.length) {\n break;\n }\n at += 1;\n continue;\n }\n if (at + head.length > this.carry.length) {\n break;\n }\n if (!this.locked) {\n const next = readMp3Header(this.carry, at + head.length);\n if (next !== null && alike(head, next)) {\n this.locked = true;\n } else if (next === null && at + head.length + 4 > this.carry.length) {\n // Out of bytes rather than out of luck. Wait for the chunk\n // carrying the confirming header; only when the stream has\n // ended, and none is coming, is the frame taken on trust.\n if (!ending) {\n break;\n }\n this.locked = true;\n } else {\n at += 1;\n continue;\n }\n }\n\n const bytes = this.carry.subarray(at, at + head.length);\n const vbr = this.shape === null ? vbrHeader(bytes, head) : null;\n if (this.shape === null) {\n this.shape = {\n version: head.version,\n layer: head.layer,\n sampleRate: head.sampleRate,\n channels: head.channels,\n bitrate: head.bitrate,\n samplesPerFrame: head.samples,\n ...(vbr?.totalFrames === undefined ? {} : { totalFrames: vbr.totalFrames }),\n ...(vbr?.totalBytes === undefined ? {} : { totalBytes: vbr.totalBytes })\n };\n }\n if (vbr === null) {\n found.push({\n bytes,\n offset: this.consumed + at,\n samples: head.samples,\n sampleOffset: this.samples,\n timestampUs: Math.round((this.samples / head.sampleRate) * 1e6),\n durationUs: Math.round((head.samples / head.sampleRate) * 1e6)\n });\n this.frames += 1;\n this.samples += head.samples;\n }\n at += head.length;\n }\n\n if (at > 0) {\n this.consumed += at;\n this.carry = this.carry.subarray(at);\n }\n return found;\n }\n}\n\n/**\n * Split a whole `.mp3` at once, for a caller that already holds all of\n * it. The same walk `Mp3Frames` does, without the streaming.\n */\nexport function splitMp3Frames(bytes: Uint8Array): { format: Mp3Format | null; frames: readonly Mp3Frame[] } {\n const splitter = new Mp3Frames();\n const frames = [...splitter.push(bytes), ...splitter.flush()];\n return { format: splitter.format, frames };\n}\n","/**\n * Just enough ISO base media file format to feed a `VideoDecoder`.\n *\n * WebCodecs decodes; it does not demux. `VideoDecoder` takes\n * `EncodedVideoChunk`s and a `VideoDecoderConfig`, and getting those\n * out of an `.mp4` means walking the sample tables yourself. That is\n * what this does, and the reason it is here rather than a dependency\n * is that `gesso-core`'s only dependency is `@webgpu/types`: a\n * renderer that pulls in a general-purpose container parser to play a\n * demo video has bought a great deal it does not use.\n *\n * **What it understands.** Progressive (non-fragmented) MP4 with a\n * `moov` and a `mdat`: `stsd` (with `avcC` or `hvcC`), `stts`, `ctts`,\n * `stsc`, `stsz`/`stz2`, `stco`/`co64` and `stss`. That is what every\n * encoder writes by default and what the `-movflags +faststart` files\n * on the web are.\n *\n * **What it does not.** Fragmented MP4 (`moof`/`traf`/`trun`), which\n * is what DASH and HLS segments are, and which is a different parse\n * rather than a harder one. It is detected and named rather than\n * mis-parsed into silence — a demuxer that returns zero samples for a\n * file the browser plays fine is a bug that takes an afternoon to\n * find. Audio is skipped entirely: there is nothing on this side of\n * the framework that could play it.\n *\n * **The whole file is parsed at once**, from one `ArrayBuffer`. Byte\n * ranges and progressive parsing are what you want for an hour of\n * video and are pure cost for the fifteen-second loops this exists\n * for; the seam is `VideoResolver`, which decides what to fetch.\n */\n\n/** One encoded frame: where it is in the file, and when it is shown. */\nexport interface Mp4Sample {\n /** Byte offset into the file. */\n readonly offset: number;\n readonly size: number;\n /** Presentation time in microseconds, which is what WebCodecs takes. */\n readonly timestampUs: number;\n readonly durationUs: number;\n /** A sync sample: decoding may start here. */\n readonly isKey: boolean;\n}\n\nexport interface Mp4VideoTrack {\n readonly codec: string;\n readonly codedWidth: number;\n readonly codedHeight: number;\n /**\n * The codec's own configuration record (`avcC`, `hvcC`), which\n * `VideoDecoder.configure` needs for length-prefixed streams. Absent\n * for codecs that carry their configuration in-band.\n */\n readonly description?: Uint8Array;\n readonly samples: readonly Mp4Sample[];\n /** The whole track, in microseconds; where a loop wraps. */\n readonly durationUs: number;\n /**\n * The shortest frame interval in the track, in microseconds — the\n * fastest the picture ever changes.\n *\n * What paces playback: a caller that samples this often never misses\n * a frame, and one that samples faster only discovers that nothing\n * changed. For constant-rate video, which is nearly all of it, this\n * is simply the frame interval. For variable-rate video the shortest\n * is the safe choice over the average, because sampling too often\n * costs a wake-up and sampling too rarely drops a picture.\n */\n readonly frameDurationUs: number;\n}\n\nclass Reader {\n private at = 0;\n\n constructor(private readonly view: DataView) {}\n\n get offset(): number {\n return this.at;\n }\n\n set offset(value: number) {\n this.at = value;\n }\n\n get remaining(): number {\n return this.view.byteLength - this.at;\n }\n\n u8(): number {\n const value = this.view.getUint8(this.at);\n this.at += 1;\n return value;\n }\n\n u16(): number {\n const value = this.view.getUint16(this.at);\n this.at += 2;\n return value;\n }\n\n u32(): number {\n const value = this.view.getUint32(this.at);\n this.at += 4;\n return value;\n }\n\n i32(): number {\n const value = this.view.getInt32(this.at);\n this.at += 4;\n return value;\n }\n\n u64(): number {\n const high = this.u32();\n const low = this.u32();\n // Beyond 2^53 an MP4 is not a thing this plays, and Number keeps\n // every offset arithmetic below honest.\n return high * 0x1_0000_0000 + low;\n }\n\n ascii(length: number): string {\n let text = '';\n for (let index = 0; index < length; index++) {\n text += String.fromCharCode(this.view.getUint8(this.at + index));\n }\n this.at += length;\n return text;\n }\n\n bytes(length: number): Uint8Array {\n const start = this.view.byteOffset + this.at;\n this.at += length;\n return new Uint8Array(this.view.buffer.slice(start, start + length));\n }\n\n skip(length: number): void {\n this.at += length;\n }\n}\n\ninterface BoxHeader {\n readonly type: string;\n /** Where the box's payload starts. */\n readonly start: number;\n /** One past the box's last byte. */\n readonly end: number;\n}\n\nfunction readBoxHeader(reader: Reader, limit: number): BoxHeader | null {\n if (reader.remaining < 8 || reader.offset + 8 > limit) {\n return null;\n }\n const boxStart = reader.offset;\n let size = reader.u32();\n const type = reader.ascii(4);\n if (size === 1) {\n size = reader.u64();\n } else if (size === 0) {\n size = limit - boxStart;\n }\n const end = boxStart + size;\n if (size < 8 || end > limit) {\n return null;\n }\n return { type, start: reader.offset, end };\n}\n\n/** Walks the boxes directly inside `[from, to)`, calling back for each. */\nfunction walkBoxes(reader: Reader, from: number, to: number, visit: (box: BoxHeader) => void): void {\n reader.offset = from;\n for (;;) {\n const box = readBoxHeader(reader, to);\n if (box === null) {\n return;\n }\n const resume = box.end;\n visit(box);\n reader.offset = resume;\n if (resume >= to) {\n return;\n }\n }\n}\n\n/** Version and flags of a full box, which most of the sample tables are. */\nfunction readFullBoxVersion(reader: Reader): number {\n const version = reader.u8();\n reader.skip(3);\n return version;\n}\n\ninterface SampleTable {\n timescale: number;\n /** Decode deltas, expanded per sample. */\n deltas: number[];\n /** Composition offsets, per sample; empty when there is no `ctts`. */\n compositionOffsets: number[];\n sizes: number[];\n chunkOffsets: number[];\n /** `(firstChunk, samplesPerChunk)` runs. */\n chunkRuns: { firstChunk: number; samplesPerChunk: number }[];\n /** 1-based sample numbers that are sync samples; empty means all are. */\n syncSamples: number[];\n codec: string;\n description?: Uint8Array;\n codedWidth: number;\n codedHeight: number;\n}\n\n/**\n * Reads the first video track out of an MP4.\n *\n * Throws with a description of what it found rather than returning\n * something empty: a caller that asked for a video and got no samples\n * cannot tell \"this file has no video track\" from \"this parser does\n * not understand this file\", and those want different fixes.\n */\nexport function demuxMp4Video(data: ArrayBuffer): Mp4VideoTrack {\n const reader = new Reader(new DataView(data));\n const limit = data.byteLength;\n let moov: BoxHeader | null = null;\n let sawFragment = false;\n let sawFtyp = false;\n\n walkBoxes(reader, 0, limit, box => {\n if (box.type === 'ftyp') {\n sawFtyp = true;\n } else if (box.type === 'moov') {\n moov = box;\n } else if (box.type === 'moof' || box.type === 'sidx') {\n sawFragment = true;\n }\n });\n\n if (!sawFtyp && moov === null) {\n throw new Error('Not an MP4: no ftyp or moov box at the top level of the file.');\n }\n if (moov === null) {\n throw new Error('This MP4 has no moov box, so there is no sample table to read.');\n }\n\n const table = findVideoTrack(reader, moov);\n if (table === null) {\n if (sawFragment) {\n throw new Error(\n 'This is a fragmented MP4 (it has moof boxes), which this demuxer does not read. ' +\n 'Remux it to a progressive file: `ffmpeg -i in.mp4 -c copy -movflags +faststart out.mp4`.'\n );\n }\n throw new Error('This MP4 has no video track, or none this demuxer recognises.');\n }\n\n const samples = buildSamples(table);\n if (samples.length === 0) {\n throw new Error(\n sawFragment\n ? 'This MP4 has an empty sample table and moof boxes: it is fragmented, which this demuxer does not read.'\n : 'This MP4 video track has no samples.'\n );\n }\n // The furthest any frame is shown, not the last one submitted: with\n // B-frames those are different samples, and this is where a loop\n // wraps.\n let durationUs = 0;\n let frameDurationUs = Infinity;\n for (const sample of samples) {\n durationUs = Math.max(durationUs, sample.timestampUs + sample.durationUs);\n if (sample.durationUs > 0 && sample.durationUs < frameDurationUs) {\n frameDurationUs = sample.durationUs;\n }\n }\n return {\n codec: table.codec,\n codedWidth: table.codedWidth,\n codedHeight: table.codedHeight,\n description: table.description,\n samples,\n durationUs,\n // A track whose every sample claims zero duration says nothing\n // about its rate, so it is paced as sixty rather than as infinity.\n frameDurationUs: Number.isFinite(frameDurationUs) ? frameDurationUs : Math.round(1_000_000 / 60)\n };\n}\n\nfunction findVideoTrack(reader: Reader, moov: BoxHeader): SampleTable | null {\n let found: SampleTable | null = null;\n walkBoxes(reader, moov.start, moov.end, trak => {\n if (trak.type !== 'trak' || found !== null) {\n return;\n }\n const table = readTrack(reader, trak);\n if (table !== null) {\n found = table;\n }\n });\n return found;\n}\n\nfunction readTrack(reader: Reader, trak: BoxHeader): SampleTable | null {\n let mdia: BoxHeader | null = null;\n walkBoxes(reader, trak.start, trak.end, box => {\n if (box.type === 'mdia') {\n mdia = box;\n }\n });\n if (mdia === null) {\n return null;\n }\n const media = mdia as BoxHeader;\n let timescale = 0;\n let isVideo = false;\n let minf: BoxHeader | null = null;\n walkBoxes(reader, media.start, media.end, box => {\n if (box.type === 'mdhd') {\n const version = readFullBoxVersion(reader);\n if (version === 1) {\n reader.skip(16);\n timescale = reader.u32();\n } else {\n reader.skip(8);\n timescale = reader.u32();\n }\n return;\n }\n if (box.type === 'hdlr') {\n readFullBoxVersion(reader);\n reader.skip(4);\n isVideo = reader.ascii(4) === 'vide';\n return;\n }\n if (box.type === 'minf') {\n minf = box;\n }\n });\n if (!isVideo || minf === null || timescale <= 0) {\n return null;\n }\n let stbl: BoxHeader | null = null;\n walkBoxes(reader, (minf as BoxHeader).start, (minf as BoxHeader).end, box => {\n if (box.type === 'stbl') {\n stbl = box;\n }\n });\n if (stbl === null) {\n return null;\n }\n return readSampleTable(reader, stbl as BoxHeader, timescale);\n}\n\nfunction readSampleTable(reader: Reader, stbl: BoxHeader, timescale: number): SampleTable | null {\n const table: SampleTable = {\n timescale,\n deltas: [],\n compositionOffsets: [],\n sizes: [],\n chunkOffsets: [],\n chunkRuns: [],\n syncSamples: [],\n codec: '',\n codedWidth: 0,\n codedHeight: 0\n };\n\n walkBoxes(reader, stbl.start, stbl.end, box => {\n switch (box.type) {\n case 'stsd':\n readSampleDescription(reader, box, table);\n break;\n case 'stts': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n const count = reader.u32();\n const delta = reader.u32();\n for (let n = 0; n < count; n++) {\n table.deltas.push(delta);\n }\n }\n break;\n }\n case 'ctts': {\n const version = readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n const count = reader.u32();\n // Version 1 offsets are signed; version 0's are specified\n // unsigned but written signed by plenty of encoders, and\n // reading them signed is what every player does.\n const offset = version === 1 ? reader.i32() : reader.i32();\n for (let n = 0; n < count; n++) {\n table.compositionOffsets.push(offset);\n }\n }\n break;\n }\n case 'stsz': {\n readFullBoxVersion(reader);\n const uniform = reader.u32();\n const count = reader.u32();\n for (let index = 0; index < count; index++) {\n table.sizes.push(uniform === 0 ? reader.u32() : uniform);\n }\n break;\n }\n case 'stz2': {\n readFullBoxVersion(reader);\n reader.skip(3);\n const fieldSize = reader.u8();\n const count = reader.u32();\n for (let index = 0; index < count; index++) {\n if (fieldSize === 16) {\n table.sizes.push(reader.u16());\n } else if (fieldSize === 8) {\n table.sizes.push(reader.u8());\n } else {\n // 4-bit sizes pack two samples per byte.\n const pair = reader.u8();\n table.sizes.push(pair >> 4);\n if (index + 1 < count) {\n table.sizes.push(pair & 0x0f);\n index++;\n }\n }\n }\n break;\n }\n case 'stsc': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n const firstChunk = reader.u32();\n const samplesPerChunk = reader.u32();\n reader.skip(4);\n table.chunkRuns.push({ firstChunk, samplesPerChunk });\n }\n break;\n }\n case 'stco': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n table.chunkOffsets.push(reader.u32());\n }\n break;\n }\n case 'co64': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n table.chunkOffsets.push(reader.u64());\n }\n break;\n }\n case 'stss': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n table.syncSamples.push(reader.u32());\n }\n break;\n }\n default:\n break;\n }\n });\n\n return table.codec === '' ? null : table;\n}\n\n/**\n * The visual sample entry, and the codec configuration inside it.\n *\n * The 78 bytes skipped are `VisualSampleEntry`'s fixed fields, which\n * nothing here needs beyond the width and height: six reserved bytes\n * and a data reference index, then sixteen pre-defined/reserved, the\n * dimensions, two resolutions, a reserved word, a frame count, a\n * thirty-two byte compressor name, a depth and a pre-defined.\n */\nfunction readSampleDescription(reader: Reader, stsd: BoxHeader, table: SampleTable): void {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n if (entries === 0) {\n return;\n }\n const entry = readBoxHeader(reader, stsd.end);\n if (entry === null) {\n return;\n }\n reader.skip(6);\n reader.skip(2);\n reader.skip(16);\n table.codedWidth = reader.u16();\n table.codedHeight = reader.u16();\n reader.skip(4 + 4 + 4 + 2 + 32 + 2 + 2);\n const format = entry.type;\n walkBoxes(reader, reader.offset, entry.end, child => {\n if (child.type === 'avcC') {\n const record = reader.bytes(child.end - child.start);\n table.description = record;\n // `avc1.PPCCLL`: profile, constraint flags and level, straight\n // out of the record's second, third and fourth bytes.\n const profile = record[1] ?? 0;\n const compat = record[2] ?? 0;\n const level = record[3] ?? 0;\n table.codec = `${format}.${hex2(profile)}${hex2(compat)}${hex2(level)}`;\n return;\n }\n if (child.type === 'hvcC') {\n table.description = reader.bytes(child.end - child.start);\n // A full HEVC codec string needs the general profile space,\n // tier, compatibility flags and six constraint bytes assembled\n // in a particular order. Chrome accepts the bare format with a\n // description, and getting it subtly wrong is worse than not\n // claiming it.\n table.codec = format;\n return;\n }\n if (child.type === 'av1C' || child.type === 'vpcC') {\n table.description = reader.bytes(child.end - child.start);\n table.codec = format;\n }\n });\n if (table.codec === '') {\n // No configuration box: an in-band codec, or one this does not\n // know. The format alone is a legitimate codec string for some of\n // them, and `VideoDecoder.isConfigSupported` is the right judge.\n table.codec = format;\n }\n}\n\nfunction hex2(value: number): string {\n return value.toString(16).padStart(2, '0').toUpperCase();\n}\n\n/**\n * Walks the sample tables into a flat list.\n *\n * The one piece of real arithmetic in this file: `stsc` describes runs\n * of chunks that share a sample count, `stco` says where each chunk\n * begins, and a sample's offset is its chunk's offset plus the sizes\n * of the samples before it *in that chunk*. Everything else is\n * accumulating deltas.\n */\nfunction buildSamples(table: SampleTable): Mp4Sample[] {\n const { sizes, chunkOffsets, chunkRuns, deltas, compositionOffsets, syncSamples, timescale } = table;\n if (sizes.length === 0 || chunkOffsets.length === 0 || chunkRuns.length === 0) {\n return [];\n }\n const sync = syncSamples.length === 0 ? null : new Set(syncSamples);\n const samples: Mp4Sample[] = [];\n let sampleIndex = 0;\n let decodeTime = 0;\n\n for (const [runIndex, run] of chunkRuns.entries()) {\n const nextRun = chunkRuns[runIndex + 1];\n const lastChunk = nextRun === undefined ? chunkOffsets.length : nextRun.firstChunk - 1;\n for (let chunk = run.firstChunk; chunk <= lastChunk; chunk++) {\n let offset = chunkOffsets[chunk - 1];\n if (offset === undefined) {\n continue;\n }\n for (let n = 0; n < run.samplesPerChunk && sampleIndex < sizes.length; n++) {\n const size = sizes[sampleIndex]!;\n const delta = deltas[sampleIndex] ?? deltas[deltas.length - 1] ?? 0;\n const composition = compositionOffsets[sampleIndex] ?? 0;\n samples.push({\n offset,\n size,\n timestampUs: Math.round(((decodeTime + composition) * 1_000_000) / timescale),\n durationUs: Math.round((delta * 1_000_000) / timescale),\n // No `stss` at all means every sample is a sync sample, which\n // is what an all-intra track looks like.\n isKey: sync === null || sync.has(sampleIndex + 1)\n });\n offset += size;\n decodeTime += delta;\n sampleIndex++;\n }\n }\n }\n // Left in **decode** order, deliberately. A stream with B-frames\n // presents its frames out of the order they must be decoded in, and\n // `VideoDecoder` is fed in decode order and hands frames back in\n // presentation order — sorting here would submit a frame before the\n // one it references and the decoder would reject it.\n return samples;\n}\n","import type { UiVideoSurface } from '../properties/UiVideo';\nimport { demuxMp4Video, type Mp4Sample, type Mp4VideoTrack } from './Mp4Demuxer';\n\n/**\n * A playing video: one surface, and the controls over it.\n *\n * `surface` is the same object for the whole life of the playback, and\n * that is load-bearing — see `UiVideo.ts` for what the WebGPU texture\n * cache does with it.\n */\nexport interface VideoPlayback {\n readonly surface: UiVideoSurface;\n readonly width: number;\n readonly height: number;\n /** Seconds. Zero until the container has been read. */\n readonly duration: number;\n /**\n * How often the picture can change, in milliseconds.\n *\n * Playback is driven by whoever owns time, and this is what that\n * driver needs to pace itself: sampling this often never misses a\n * frame, and sampling faster only discovers that nothing changed.\n * Without it a playing video has to ask for *every* frame the clock\n * will give — which on a display-paced runtime means a 30fps clip\n * waking a 165Hz app 165 times a second to present the same picture\n * five times running.\n */\n readonly frameDurationMs: number;\n /**\n * How far into the video this playback has got, in milliseconds.\n *\n * What an arriving element needs in order to pick up where a\n * departing one left off. The reference counting keeps the decoder\n * alive across a navigation, but the *position* belongs to whoever\n * is driving, and a node that has just been built has no idea where\n * that is — so without this it starts at zero, which on a shared\n * playback is a seek backwards and a decoder reset.\n */\n readonly positionMs: number;\n /**\n * Shows the frame due at `positionMs` into the video, and says\n * whether that changed the picture.\n *\n * A pure function of a position rather than something that runs on a\n * clock of its own: whoever is driving owns time. In an app that is\n * the animation driver, through `videoSource`, which means a video\n * is scheduled and paced by exactly the machinery every other\n * animated thing here uses. Going backwards is a seek, and a seek to\n * zero is what looping is.\n */\n present(positionMs: number): boolean;\n /** Called when the video fails, with what went wrong. */\n onError(listener: (error: unknown) => void): () => void;\n}\n\n/**\n * Turns a source into something a renderer can draw, frame by frame.\n *\n * Deliberately the same shape as `ImageResolver`, including the\n * reference counting, and the reference counting is worth more here\n * than it is for a still. The demo this was written against keeps a\n * video playing across a navigation, and does it in the DOM by\n * physically moving the `<video>` element into the new document —\n * because a second `<video>` on the same source would start from the\n * beginning. Here the arriving node resolves the same source the\n * departing one is holding, gets the playback that is already running,\n * and picks it up mid-stream. There is no trick to reproduce.\n */\nexport interface VideoResolver {\n resolve(source: string): Promise<VideoPlayback>;\n release(source: string): void;\n dispose(): void;\n}\n\nexport interface DefaultVideoResolverOptions {\n /** How many finished playbacks to keep after their last holder let go. */\n capacity?: number;\n /** Injectable for specs, and for an app that fetches through its own stack. */\n fetch?: (source: string) => Promise<ArrayBuffer>;\n}\n\n/** Whether this thread can decode video at all. */\nexport function canDecodeVideo(): boolean {\n return typeof VideoDecoder !== 'undefined' && typeof EncodedVideoChunk !== 'undefined';\n}\n\nconst DEFAULT_CAPACITY = 4;\n/**\n * How many decoded frames to keep in flight, queued plus submitted.\n *\n * A `VideoFrame` holds decoded pixels — often in GPU memory — and a\n * decoder will happily run seconds ahead if you let it, so this is\n * deliberately small. But it cannot be *too* small, and the arithmetic\n * is worth writing down because the first value chosen was.\n *\n * The floor is three things multiplied out. A stream with B-frames\n * cannot emit its first picture until the decoder holds its reorder\n * depth — two for the clip this was built against, so three inputs\n * before one output. `fill` runs once per *presentation*, and a source\n * at 60 fps presented by a renderer running at 30 needs two frames\n * decoded per fill to keep up. And a frame or two of slack absorbs a\n * decode that arrives late without the queue ever reaching empty.\n *\n * Twelve covers that with room. Four — the first value here — did not:\n * it left barely more than the reorder depth, so delivery came in\n * bursts and `presentDue`, which shows only the newest frame that is\n * due, threw the rest away.\n */\nconst FRAME_QUEUE_LIMIT = 12;\n/**\n * How far the position may go backwards before it counts as a seek.\n *\n * A driver writing a position from a linear tween is monotonic within\n * a pass, but a frame's timing jitters by a millisecond either way and\n * a repeating tween wraps by a whole duration. A tenth of a second\n * tells those apart with room to spare.\n */\nconst SEEK_BACK_TOLERANCE_US = 100_000;\n\nasync function defaultFetch(source: string): Promise<ArrayBuffer> {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Fetching '${source}' failed with ${response.status} ${response.statusText}.`);\n }\n return response.arrayBuffer();\n}\n\ninterface Entry {\n readonly source: string;\n readonly playback: Promise<VideoPlayback>;\n holders: number;\n settled: Mp4Playback | null;\n}\n\n/**\n * Fetch, demux, decode, present, loop.\n *\n * Everything happens on the thread this is constructed on, which for a\n * Gesso app is the render worker: `VideoDecoder` is available there,\n * the demuxer is arithmetic, and a decoded `VideoFrame` is drawn where\n * it was produced rather than transferred anywhere. That is the whole\n * reason the decode is not on the main thread behind a `<video>`\n * element — the shell stays a shell.\n */\nexport class DefaultVideoResolver implements VideoResolver {\n private readonly entries = new Map<string, Entry>();\n private readonly evictable: string[] = [];\n private readonly capacity: number;\n private readonly fetchBuffer: (source: string) => Promise<ArrayBuffer>;\n private disposed = false;\n\n constructor(options: DefaultVideoResolverOptions = {}) {\n this.capacity = options.capacity ?? DEFAULT_CAPACITY;\n this.fetchBuffer = options.fetch ?? defaultFetch;\n }\n\n /** Live or in-flight sources, for specs and the inspector. */\n get size(): number {\n return this.entries.size;\n }\n\n resolve(source: string): Promise<VideoPlayback> {\n if (this.disposed) {\n return Promise.reject(new Error('The video resolver has been disposed.'));\n }\n const existing = this.entries.get(source);\n if (existing !== undefined) {\n existing.holders++;\n const index = this.evictable.indexOf(source);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n return existing.playback;\n }\n const entry: Entry = {\n source,\n holders: 1,\n settled: null,\n playback: this.open(source).then(playback => {\n const current = this.entries.get(source);\n if (current === undefined) {\n // Released and evicted while it was in flight.\n playback.close();\n return playback;\n }\n current.settled = playback;\n return playback;\n })\n };\n // A rejection must not become an unhandled one just because nobody\n // has awaited it yet; the caller's own `.catch` still sees it.\n entry.playback.catch(() => {});\n this.entries.set(source, entry);\n return entry.playback;\n }\n\n release(source: string): void {\n const entry = this.entries.get(source);\n if (entry === undefined) {\n return;\n }\n entry.holders--;\n if (entry.holders > 0) {\n return;\n }\n // Nobody is watching it, but keep it: the usual reason a source\n // loses its last holder is a navigation that is about to give it\n // another, and that is the whole trick that keeps a video running\n // across a route change. It costs nothing to hold — nothing is\n // driving its position, so no frame is decoded until someone asks\n // for one again.\n this.evictable.push(source);\n while (this.evictable.length > this.capacity) {\n const evicted = this.evictable.shift()!;\n const stale = this.entries.get(evicted);\n if (stale !== undefined && stale.holders === 0) {\n this.entries.delete(evicted);\n stale.settled?.close();\n }\n }\n }\n\n dispose(): void {\n this.disposed = true;\n for (const entry of this.entries.values()) {\n entry.settled?.close();\n }\n this.entries.clear();\n this.evictable.length = 0;\n }\n\n private async open(source: string): Promise<Mp4Playback> {\n if (!canDecodeVideo()) {\n throw new Error(\n 'This thread has no WebCodecs VideoDecoder, so video cannot be played here. ' +\n 'It is available in Chrome 94+ and in a worker; Safari and Firefox support varies.'\n );\n }\n const data = await this.fetchBuffer(source);\n const track = demuxMp4Video(data);\n const playback = new Mp4Playback(track, data);\n await playback.start();\n return playback;\n }\n}\n\n/**\n * One MP4, decoded and presented against the runtime's frame clock.\n *\n * The clock matters. Nothing here uses `setInterval` or the decoder's\n * own pacing: `present(now)` is called from the modifier once a frame,\n * and it advances to whichever decoded frame is due at that moment. So\n * a video runs at the speed of the app's frames, drops frames under\n * load rather than falling behind, and stops entirely when the runtime\n * stops — which is the same contract every other animated thing in\n * this framework has.\n */\nclass Mp4Playback implements VideoPlayback {\n readonly surface: MutableVideoSurface;\n private decoder: VideoDecoder;\n private config: VideoDecoderConfig | null = null;\n /** Decoded and waiting, in presentation order. */\n private readonly queue: VideoFrame[] = [];\n private nextSample = 0;\n /** Where the last `present` put us, in microseconds. */\n private positionUs = 0;\n private closed = false;\n private readonly errorListeners = new Set<(error: unknown) => void>();\n\n constructor(\n private readonly track: Mp4VideoTrack,\n private readonly data: ArrayBuffer\n ) {\n this.surface = new MutableVideoSurface(track.codedWidth, track.codedHeight);\n this.decoder = new VideoDecoder({\n output: frame => this.receive(frame),\n error: error => this.fail(error)\n });\n }\n\n get width(): number {\n return this.track.codedWidth;\n }\n\n get height(): number {\n return this.track.codedHeight;\n }\n\n get duration(): number {\n return this.track.durationUs / 1_000_000;\n }\n\n get frameDurationMs(): number {\n return this.track.frameDurationUs / 1000;\n }\n\n get positionMs(): number {\n return this.positionUs / 1000;\n }\n\n async start(): Promise<void> {\n const config: VideoDecoderConfig = {\n codec: this.track.codec,\n codedWidth: this.track.codedWidth,\n codedHeight: this.track.codedHeight,\n description: this.track.description,\n // `hardwareAcceleration` is deliberately not set, which means\n // 'no-preference' and lets the browser choose. Asking for\n // 'prefer-hardware' is a *requirement*, not a preference:\n // `isConfigSupported` answers false wherever there is no hardware\n // decoder for the codec — which on a Linux box without VA-API is\n // every codec — and the video then silently never plays. Found\n // exactly that way.\n optimizeForLatency: true\n };\n const support = await VideoDecoder.isConfigSupported(config);\n if (support.supported !== true) {\n throw new Error(\n `No decoder for '${this.track.codec}' at ${this.track.codedWidth}x${this.track.codedHeight} on this platform.`\n );\n }\n this.config = config;\n this.decoder.configure(config);\n this.fill();\n }\n\n /**\n * Shows whichever frame is due at `positionMs`, and says whether\n * that changed the picture.\n *\n * Frames already behind the position are dropped rather than shown,\n * so an app that stalled catches up instead of replaying the last\n * half second in fast-forward.\n */\n present(positionMs: number): boolean {\n if (this.closed) {\n return false;\n }\n const wanted = Math.max(0, positionMs * 1000);\n if (wanted === this.positionUs) {\n // Two elements can share one playback — a card and the page it\n // opens into, both on screen while a transition runs — and each\n // presents on its own tick. The second one to arrive on a given\n // position has nothing to do, and doing it anyway means a second\n // `fill()` and a second walk of the frame queue per frame.\n return false;\n }\n if (wanted + SEEK_BACK_TOLERANCE_US < this.positionUs) {\n // Backwards: a loop wrapping, or a deliberate seek. Either way\n // every frame in flight belongs to where we no longer are.\n this.rewind();\n }\n this.positionUs = wanted;\n const changed = this.presentDue();\n this.fill();\n return changed;\n }\n\n onError(listener: (error: unknown) => void): () => void {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n /** Frees the decoder and every frame still queued. */\n close(): void {\n if (this.closed) {\n return;\n }\n this.closed = true;\n for (const frame of this.queue) {\n frame.close();\n }\n this.queue.length = 0;\n this.surface.clear();\n if (this.decoder.state !== 'closed') {\n this.decoder.close();\n }\n this.errorListeners.clear();\n }\n\n private presentDue(): boolean {\n let shown = false;\n while (this.queue.length > 0) {\n const frame = this.queue[0]!;\n if (frame.timestamp > this.positionUs) {\n break;\n }\n this.queue.shift();\n // Everything still queued behind a frame that is also due is a\n // dropped frame: only the last one is worth uploading.\n const next = this.queue[0];\n if (next !== undefined && next.timestamp <= this.positionUs) {\n frame.close();\n continue;\n }\n this.surface.show(frame);\n shown = true;\n }\n return shown;\n }\n\n /** Keeps the decoder a few frames ahead, and no further. */\n private fill(): void {\n if (this.closed || this.decoder.state !== 'configured') {\n return;\n }\n while (\n this.nextSample < this.track.samples.length &&\n this.queue.length + this.decoder.decodeQueueSize < FRAME_QUEUE_LIMIT\n ) {\n const sample = this.track.samples[this.nextSample]!;\n this.nextSample++;\n try {\n this.decoder.decode(new EncodedVideoChunk(chunkOf(sample, this.data)));\n } catch (error) {\n this.fail(error);\n return;\n }\n }\n }\n\n /**\n * Back to the start of the stream.\n *\n * The decoder is reset and reconfigured rather than merely refilled,\n * because a decoder mid-GOP holds reference frames for where it was\n * and the first sample of the file is a keyframe for where it is\n * going. That costs one keyframe decode at the loop point — a\n * measurable hitch on a long GOP, invisible on the two-second ones\n * a looping clip is usually encoded with — and it is the honest\n * price of looping a progressive file rather than a fragmented one.\n */\n private rewind(): void {\n for (const frame of this.queue) {\n frame.close();\n }\n this.queue.length = 0;\n this.nextSample = 0;\n this.positionUs = 0;\n if (this.config !== null && this.decoder.state !== 'closed') {\n this.decoder.reset();\n this.decoder.configure(this.config);\n }\n }\n\n private receive(frame: VideoFrame): void {\n if (this.closed) {\n frame.close();\n return;\n }\n this.queue.push(frame);\n // The decoder emits in presentation order within a run, but a\n // frame may arrive after the position has already moved past it;\n // keeping the queue sorted means `presentDue` can stop at the\n // first frame that is not due yet.\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n }\n\n private fail(error: unknown): void {\n for (const listener of this.errorListeners) {\n listener(error);\n }\n this.close();\n }\n}\n\nfunction chunkOf(sample: Mp4Sample, data: ArrayBuffer): EncodedVideoChunkInit {\n return {\n type: sample.isKey ? 'key' : 'delta',\n timestamp: sample.timestampUs,\n duration: sample.durationUs,\n data: new Uint8Array(data, sample.offset, sample.size)\n };\n}\n\n/**\n * The surface the renderers see, written from inside.\n *\n * `UiVideoSurface` is readonly to everything downstream, which is the\n * point: a renderer must not be able to change what it is drawing. The\n * playback holds this side of it.\n */\nclass MutableVideoSurface implements UiVideoSurface {\n frame: VideoFrame | ImageBitmap | null = null;\n version = 0;\n\n constructor(\n readonly width: number,\n readonly height: number\n ) {}\n\n /**\n * Which conversion is the newest, so a slower one that finishes\n * after it can be thrown away rather than shown out of order.\n */\n private pending = 0;\n\n /**\n * Takes a decoded frame, and shows it once it is something cheap to\n * draw.\n *\n * A `VideoFrame` from a software decoder holds planar YUV in CPU\n * memory, and `drawImage` has to convert it to RGB before it can\n * put it anywhere. Whether that is visible depends on the engine:\n * Chromium uploads the frame and converts it in a shader, while\n * Gecko converts on whichever thread issued the draw, which is the\n * render worker, inside the frame. Measured on Firefox 154 in the\n * transitions example, one 1280x992 frame drawn into a 0.42\n * megapixel box cost **4.6ms**, against a whole-frame budget of\n * 6ms on a 165Hz display. It was two thirds of the frame, and it\n * was paid again on every draw.\n *\n * `createImageBitmap` is specified to do its work in parallel, so\n * converting here rather than at the draw takes that cost off the\n * render thread and pays it once per decoded frame instead of once\n * per drawn frame. The same measurement then reads **1.7ms**, and\n * a display faster than the video no longer multiplies it.\n *\n * The frame is closed as soon as the bitmap exists, and the bitmap\n * is what `UiVideoSurface.frame` was already allowed to be.\n */\n show(frame: VideoFrame): void {\n const conversion = ++this.pending;\n if (typeof createImageBitmap !== 'function') {\n this.replace(frame);\n return;\n }\n createImageBitmap(frame)\n .then(bitmap => {\n frame.close();\n if (conversion !== this.pending) {\n // A newer frame has already been shown. Presenting this one\n // would run the video backwards for a frame.\n bitmap.close();\n return;\n }\n this.replace(bitmap);\n })\n .catch(() => {\n // No `createImageBitmap` here, or it refused this frame. The\n // frame itself is still drawable, which is what every renderer\n // did before this conversion existed.\n if (conversion !== this.pending) {\n frame.close();\n return;\n }\n this.replace(frame);\n });\n }\n\n private replace(frame: VideoFrame | ImageBitmap): void {\n // What is being replaced is closed here rather than left to the\n // collector: a `VideoFrame` holds decoded pixels, often in GPU\n // memory, and Chrome warns loudly when one is collected unclosed.\n (this.frame as { close?: () => void } | null)?.close?.();\n this.frame = frame;\n this.version++;\n }\n\n clear(): void {\n // Also abandons any conversion still in flight: it will find its\n // sequence number stale and close what it made.\n this.pending++;\n (this.frame as { close?: () => void } | null)?.close?.();\n this.frame = null;\n this.version++;\n }\n}\n","/**\n * Gesso's work, in the browser's own profiler\n *.\n *\n * A performance recording of a Gesso application shows one long task\n * per frame and nothing inside it, because everything the framework\n * does happens under callbacks the browser cannot name. `performance\n * .measure` is the one way to put a name on a span that the DevTools\n * Performance panel, `PerformanceObserver` and a real-user monitor all\n * already read, so this is what the framework emits rather than a\n * timeline of its own.\n *\n * **It is off until something asks.** A production application that\n * never turns it on takes one boolean read per span and nothing else:\n * no clock reading, no string, no entry retained. That is the whole\n * reason the switch is a module-level flag rather than an option on\n * the scheduler — a span has to be able to ask \"is anyone recording\"\n * before it pays for the timestamp that would answer.\n *\n * It can be turned on in production, which is the point. Either call\n * `setPerformanceMarks(true)`, or set `globalThis.__GESSO_PERF__` to\n * true before the framework's first import: a page can then be\n * profiled as it shipped, with no development build and no devtools\n * attached.\n */\n\n/** Every measure this module emits starts with this, so a filter finds them all. */\nexport const MARK_PREFIX = 'gesso';\n\n/**\n * Read once, at module scope: a page that wants marks in a production\n * build sets the flag before the framework loads, and pays nothing for\n * the check afterwards.\n */\nlet recording = readInitialFlag();\n\n/** Whether `performance.measure` exists at all; a worker without it must not throw. */\nconst supported =\n typeof performance !== 'undefined' &&\n typeof performance.measure === 'function' &&\n typeof performance.now === 'function';\n\nfunction readInitialFlag(): boolean {\n return (globalThis as { __GESSO_PERF__?: unknown }).__GESSO_PERF__ === true;\n}\n\n/**\n * Turns the marks on or off.\n *\n * A devtools panel calls this; so may an application that wants a\n * profile of one interaction and nothing else.\n */\nexport function setPerformanceMarks(enabled: boolean): void {\n recording = enabled;\n}\n\n/**\n * Whether spans are being recorded.\n *\n * Call it before doing any work a span needs, including reading the\n * clock. This is the check that keeps the cost at zero when nobody is\n * listening.\n */\nexport function performanceMarksEnabled(): boolean {\n return recording && supported;\n}\n\n/**\n * A span between two `performance.now()` readings the caller already\n * took, named `gesso <what>`.\n *\n * Times are passed in rather than taken here because the caller is\n * timing the work anyway: the scheduler's frame span and the frame's\n * own duration must be the same two readings, or a profile and a\n * `FrameMetrics` would disagree about the same frame.\n *\n * `detail` rides along for the panels that show it (Chrome puts it on\n * the entry's tooltip) and costs nothing when marks are off, because\n * this is not called then.\n */\nexport function measureSpan(what: string, start: number, end: number, detail?: unknown): void {\n if (!recording || !supported) {\n return;\n }\n try {\n performance.measure(`${MARK_PREFIX} ${what}`, { start, end, ...(detail === undefined ? {} : { detail }) });\n } catch {\n // A measure can throw on a start time the browser has dropped from\n // its buffer. Instrumentation must never be the thing that breaks\n // the frame it is describing.\n }\n}\n\n/**\n * A point in time, named `gesso <what>`.\n *\n * For things with no duration: a command crossing the barrier, a patch\n * batch arriving. They land in the same track as the spans, so a\n * command and the frame that answered it are read off one timeline.\n */\nexport function markInstant(what: string, detail?: unknown): void {\n if (!recording || !supported || typeof performance.mark !== 'function') {\n return;\n }\n try {\n performance.mark(`${MARK_PREFIX} ${what}`, detail === undefined ? undefined : { detail });\n } catch {\n // As above: never throw out of instrumentation.\n }\n}\n\n/** `performance.now()`, or the epoch clock where there is no `performance`. */\nexport function markNow(): number {\n return supported ? performance.now() : Date.now();\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiFrameTime } from './UiFrameClock';\nimport type { UiNode } from '../graph/UiNode';\n\n/**\n * The unit of work produced by one scheduler frame.\n *\n * The dirty nodes and their flags are snapshotted when the\n * frame is collected, so a node dirtied again while the frame\n * is being processed belongs to the next frame.\n */\nexport class UiFrame {\n constructor(\n public readonly id: number,\n public readonly time: UiFrameTime,\n private readonly dirty: ReadonlyMap<UiNode, DirtyFlags>\n ) {}\n\n /**\n * The dirty nodes as an array.\n *\n * Copies, so it is the wrong thing to reach for on the frame path:\n * prefer `entries()` to walk them, and `anyFlags` to ask a question\n * about them. It stays because a caller that needs to hold the list\n * past the walk, or to index into it, wants a copy and should get an\n * honest one rather than a view that changes underneath it.\n */\n get nodes(): UiNode[] {\n return [...this.dirty.keys()];\n }\n\n /**\n * The dirty nodes with their flags, without copying either.\n *\n * The pairing matters as much as the absence of a copy. Walking\n * `nodes` and calling `dirtyFlagsFor` on each one asks the map for\n * something it just handed over, so a frame of a thousand dirty\n * nodes paid an array and a thousand lookups to learn what iterating\n * the entries says for free.\n */\n entries(): IterableIterator<[UiNode, DirtyFlags]> {\n return this.dirty.entries();\n }\n\n /**\n * Whether any node in the frame carries one of `flags`.\n *\n * The question the runtime actually asks three times a frame: does\n * this frame need layout, does it need semantics, did it change the\n * tree. Answering it here lets it stop at the first node that says\n * yes, which on a frame that does need layout is usually the first\n * node looked at.\n */\n anyFlags(flags: DirtyFlags): boolean {\n for (const value of this.dirty.values()) {\n if ((value & flags) !== 0) {\n return true;\n }\n }\n return false;\n }\n\n get size(): number {\n return this.dirty.size;\n }\n\n isEmpty(): boolean {\n return this.dirty.size === 0;\n }\n\n dirtyFlagsFor(node: UiNode): DirtyFlags {\n return this.dirty.get(node) ?? DirtyFlags.None;\n }\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport { DirtyNodeSet } from '../graph/DirtyNodeSet';\nimport { markNow, measureSpan, performanceMarksEnabled } from './PerformanceMarks';\nimport { UiFrame } from './UiFrame';\nimport type { UiFrameClock, UiFrameClockFactory, UiFrameTime } from './UiFrameClock';\nimport type { UiNode } from '../graph/UiNode';\n\nexport type UiFrameCallback = (frame: UiFrame) => void;\n\nexport interface UiSchedulerOptions {\n /**\n * Builds the frame clock for this scheduler.\n */\n clock: UiFrameClockFactory;\n /**\n * The shared dirty set. The graph marks into it and the\n * scheduler drains it on each frame.\n */\n dirty: DirtyNodeSet;\n /**\n * Receives one UiFrame per processed tick.\n */\n onFrame: UiFrameCallback;\n\n /**\n * Runs immediately before the dirty set is snapshotted.\n *\n * For work that produces dirt of its own — applying store patches,\n * propagating environment, advancing animations — so the nodes it\n * dirties belong to the frame about to be collected rather than the\n * one after it. Anything reading the dirty set after collection sees\n * an empty one, so this hook is the only place such work can go.\n *\n * It receives the frame's time, so a phase that advances with the\n * clock advances on *this* clock: under a manual clock in a spec\n * `performance.now()` is a different number entirely, and an\n * animation reading it would not be reproducible.\n */\n beforeCollect?: (time: UiFrameTime) => void;\n}\n\n/**\n * Decides when a dirty graph gets processed.\n *\n * No observable emission reaches this directly. Marking a node\n * dirty only calls notifyDirty(), which arms a frame at most\n * once. Additional marks are coalesced into the pending frame.\n *\n * The scheduler is timing agnostic: it drives whatever clock it\n * is given, so the same runtime works on the main thread, in a\n * Worker, and in tests.\n */\nexport class UiScheduler {\n private readonly clock: UiFrameClock;\n private readonly dirty: DirtyNodeSet;\n private readonly onFrame: UiFrameCallback;\n private readonly beforeCollect: ((time: UiFrameTime) => void) | undefined;\n\n private disposed = false;\n private active = true;\n private pending = false;\n /** True between a frame starting and its dirty set being taken. */\n private collecting = false;\n private frames = 0;\n\n /**\n * Scratch space for draining the dirty set.\n *\n * Kept for the life of the scheduler: this drain happens on every\n * frame, and the nodes are copied straight into the frame's map, so\n * nothing outside collectFrame ever sees the array.\n */\n private readonly drained: UiNode[] = [];\n\n constructor(options: UiSchedulerOptions) {\n this.dirty = options.dirty;\n this.onFrame = options.onFrame;\n this.beforeCollect = options.beforeCollect;\n this.clock = options.clock((time: UiFrameTime) => {\n this.handleFrame(time);\n });\n }\n\n /**\n * Called whenever a node becomes dirty.\n *\n * Arms a frame only when none is already pending — and not at all\n * while a frame is between starting and collecting its dirty set,\n * because that frame is about to collect this very node. The work\n * happens either way; what a frame armed here would add is a second\n * frame with nothing left to do.\n *\n * This is the difference between `notifyDirty` and `wake`, and it\n * only became visible when frames started following the display.\n * `beforeCollect` is where animations run, and an animation that\n * writes through the graph — `videoSource` presenting the frame due\n * at its position is the one that found this — marked a node dirty\n * from inside the frame that was going to draw it, and armed\n * another. Against a fixed 16ms timer the spare frame was simply the\n * next scheduled one and cost nothing; against a 165Hz display it\n * doubled the rate of everything a video was playing behind.\n */\n notifyDirty(): void {\n if (this.collecting) {\n return;\n }\n this.wake();\n }\n\n /**\n * Arms a frame for work that is not in the dirty set.\n *\n * `notifyDirty` is the graph's call and means \"a node changed\"; this\n * means \"run a frame anyway\", which is what an animation needs.\n * Nothing an animation writes can be what arms its own frame: its\n * writes happen in `beforeCollect`, inside a frame that has to exist\n * already. Something outside the graph has to ask, and this is the\n * asking. Safe to call from inside `beforeCollect` — `pending` is\n * cleared before that hook runs, so it arms the next frame and not\n * the one in progress.\n */\n wake(): void {\n if (this.disposed || !this.active || this.pending) {\n return;\n }\n this.pending = true;\n this.clock.requestFrame();\n }\n\n start(): void {\n if (this.disposed) {\n throw new Error('UiScheduler is disposed.');\n }\n if (this.active) {\n return;\n }\n this.active = true;\n if (!this.dirty.isEmpty()) {\n this.notifyDirty();\n }\n }\n\n stop(): void {\n if (!this.active) {\n return;\n }\n this.active = false;\n this.pending = false;\n this.clock.cancelFrame();\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.active = false;\n this.pending = false;\n this.clock.cancelFrame();\n }\n\n get running(): boolean {\n return this.active;\n }\n\n get framePending(): boolean {\n return this.pending;\n }\n\n get frameCount(): number {\n return this.frames;\n }\n\n /**\n * Processes the pending frame now, in the caller's task.\n *\n * For work that has already made the surface stale — a resize clears\n * the backing store — where waiting for the clock would present the\n * cleared surface for a frame.\n */\n flush(time: UiFrameTime): void {\n if (this.disposed || !this.active) {\n return;\n }\n this.clock.cancelFrame();\n this.pending = false;\n this.handleFrame(time);\n }\n\n /**\n * Runs one frame, and names its three parts for the browser's\n * profiler when anything is recording (`PerformanceMarks`).\n *\n * The three are the ones this class owns the boundaries of, and they\n * are exact rather than reconstructed: the work before the dirty set\n * is taken (animations, patches, environment, virtualisation), the\n * taking of it, and everything the frame callback does with it\n * (layout, semantics, render). The per-phase breakdown inside those\n * belongs to whoever runs the phases and reaches a panel as\n * `FrameMetrics.phases`.\n *\n * `measuring` is read once and the clock is read only when it is\n * true, so a frame nobody is profiling pays one boolean read.\n */\n private handleFrame(time: UiFrameTime): void {\n const measuring = performanceMarksEnabled();\n const started = measuring ? markNow() : 0;\n this.pending = false;\n this.collecting = true;\n let frame: UiFrame;\n let collected = 0;\n try {\n this.beforeCollect?.(time);\n if (measuring) {\n collected = markNow();\n measureSpan('before collect', started, collected);\n }\n frame = this.collectFrame(time);\n } finally {\n this.collecting = false;\n }\n let processed = 0;\n if (measuring) {\n processed = markNow();\n measureSpan('collect', collected, processed);\n }\n if (frame.size > 0) {\n this.onFrame(frame);\n }\n if (measuring) {\n const finished = markNow();\n const detail = { frame: frame.id, nodes: frame.size };\n measureSpan('process', processed, finished, detail);\n measureSpan('frame', started, finished, detail);\n }\n // Work may have arrived while the frame was being processed.\n // The dirty listener also arms a frame, but this covers a\n // scheduler used without a listener wired to the graph.\n if (!this.dirty.isEmpty()) {\n this.notifyDirty();\n }\n }\n\n private collectFrame(time: UiFrameTime): UiFrame {\n const count = this.dirty.drainInto(this.drained);\n const dirty = new Map<UiNode, DirtyFlags>();\n for (let i = 0; i < count; i++) {\n const node = this.drained[i];\n dirty.set(node, node.dirtyFlags);\n node.dirtyFlags = DirtyFlags.None;\n }\n const frame = new UiFrame(this.frames, time, dirty);\n if (dirty.size > 0) {\n this.frames++;\n }\n return frame;\n }\n}\n","/**\n * A time value, in milliseconds.\n *\n * The value is sourced from the frame clock so it stays\n * meaningful both in the browser main thread and in a Worker.\n */\nexport type UiFrameTime = number;\n\n/**\n * An abstract frame clock.\n *\n * The core runtime never touches requestAnimationFrame or\n * setTimeout directly. It only asks the clock to arm a frame\n * and to cancel one, which keeps the runtime usable in a\n * Worker or under a test clock.\n */\nexport interface UiFrameClock {\n requestFrame(): void;\n cancelFrame(): void;\n}\n\n/**\n * Builds a clock wired to a callback.\n *\n * A clock delivers ticks through the callback supplied to its\n * constructor, so a scheduler asks for a fresh clock through a\n * factory rather than holding a half-wired instance.\n */\nexport type UiFrameClockFactory = (onFrame: (time: UiFrameTime) => void) => UiFrameClock;\n\ninterface AnimationFrameHost {\n requestAnimationFrame?: (callback: (time: number) => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n}\n\n/**\n * Browser main-thread clock backed by requestAnimationFrame.\n *\n * Fails fast when requestAnimationFrame is unavailable (for\n * example in a Worker) instead of silently doing nothing.\n */\nexport class UiAnimationFrameClock implements UiFrameClock {\n private readonly requestAnimationFrame: (callback: (time: number) => void) => number;\n private readonly cancelAnimationFrame: (handle: number) => void;\n private handle: number | null = null;\n\n constructor(private readonly onFrame: (time: UiFrameTime) => void) {\n const host = globalThis as AnimationFrameHost;\n if (typeof host.requestAnimationFrame !== 'function' || typeof host.cancelAnimationFrame !== 'function') {\n throw new Error('requestAnimationFrame is not available in this environment.');\n }\n this.requestAnimationFrame = host.requestAnimationFrame.bind(host);\n this.cancelAnimationFrame = host.cancelAnimationFrame.bind(host);\n }\n\n requestFrame(): void {\n if (this.handle !== null) {\n return;\n }\n this.handle = this.requestAnimationFrame((time: number) => {\n this.handle = null;\n this.onFrame(time);\n });\n }\n\n cancelFrame(): void {\n if (this.handle === null) {\n return;\n }\n this.cancelAnimationFrame(this.handle);\n this.handle = null;\n }\n}\n\nexport interface UiTimerFrameClockOptions {\n /** Delay between requestFrame and the tick, in milliseconds. */\n intervalMs?: number;\n /** Time source used for the delivered tick. */\n now?: () => UiFrameTime;\n}\n\n/**\n * Refreshes to keep asking for after the last frame anybody wanted.\n *\n * Four is about a fortieth of a second at sixty and a fortieth at one\n * hundred and sixty-five, which covers the gap anything animating\n * leaves between frames without keeping an idle application awake for\n * a noticeable moment.\n */\n/** How many refresh intervals the watch is measured over. */\nconst CADENCE_SAMPLES = 5;\n\n/** Above this, a gap between ticks is a hiccup rather than a display's rate. */\nconst MAX_CADENCE_MS = 50;\n\n/**\n * How many refresh intervals the watchdog waits before calling a host\n * stalled. See `watchMs` for why one is not enough.\n */\nconst WATCH_REFRESHES = 2;\n\nconst IDLE_TICKS_BEFORE_STOP = 4;\n\nexport interface UiHostFrameClockOptions {\n /** How long to wait for a host tick before pacing a frame anyway. */\n fallbackMs?: number;\n /**\n * The longest a hosted clock will wait for a tick before deciding the\n * host has stopped answering and pacing itself again.\n *\n * A ceiling rather than the usual figure: once ticks have been\n * arriving the clock knows the display's own interval and watches at\n * twice that, so this value only applies before it has seen enough\n * refreshes to say. Waiting a tenth of a second on every stall would\n * be six dropped frames on a 60Hz panel, which is visible.\n */\n stallMs?: number;\n /**\n * The shortest that watch can be, whatever the measured cadence.\n *\n * A floor exists because the estimate is taken from timestamps that\n * jitter: watching at exactly one refresh would fire on any tick that\n * arrived a moment late and draw a frame nobody asked for.\n */\n minStallMs?: number;\n /** Time source for a fallback tick; host ticks carry their own. */\n now?: () => UiFrameTime;\n}\n\n/**\n * Timer-backed clock that works in the browser main thread,\n * in a Worker, and in Node.\n */\nexport class UiTimerFrameClock implements UiFrameClock {\n private readonly intervalMs: number;\n private readonly now: () => UiFrameTime;\n private handle: ReturnType<typeof setTimeout> | null = null;\n\n constructor(\n private readonly onFrame: (time: UiFrameTime) => void,\n options: UiTimerFrameClockOptions = {}\n ) {\n this.intervalMs = options.intervalMs ?? 16;\n this.now = options.now ?? (() => performance.now());\n }\n\n requestFrame(): void {\n if (this.handle !== null) {\n return;\n }\n this.handle = setTimeout(() => {\n this.handle = null;\n this.onFrame(this.now());\n }, this.intervalMs);\n }\n\n cancelFrame(): void {\n if (this.handle === null) {\n return;\n }\n clearTimeout(this.handle);\n this.handle = null;\n }\n}\n\n/**\n * A clock whose frames arrive from somewhere else.\n *\n * The one thing a worker cannot do for itself. `requestAnimationFrame`\n * is tied to the compositor and exists only on the main thread, so a\n * render worker's frames were paced by `UiTimerFrameClock` at a fixed\n * 16ms — roughly sixty a second on any display, aligned to none of\n * them. On a 165Hz monitor that is not merely slower than it could be:\n * an unaligned timer lands two frames inside one refresh, or none, so\n * the pacing is uneven as well as capped.\n *\n * This clock does no timing at all. It reports when it wants frames\n * and delivers whatever ticks it is handed, which lets the shell —\n * the one thread with a `requestAnimationFrame` — supply the display's\n * own cadence. That is the narrow exception to keeping the shell\n * uninvolved: not work moved back onto the main thread for\n * convenience, but the single fact a worker has no way to observe.\n *\n * **Ticks are free-running while frames are wanted, not requested one\n * at a time.** A tick per request would cost a round trip inside every\n * frame: if the request reaches the shell after that vsync's callback\n * has run, the tick waits for the next one and the rate halves. So\n * `onActive(true)` means \"keep ticking\" and `onActive(false)` means\n * \"stop\"; a tick arriving with nothing pending is dropped, which costs\n * one comparison and makes the timing robust to whichever side is\n * late.\n */\nexport class UiHostFrameClock implements UiFrameClock {\n private pending = false;\n private active = false;\n /** Set by the first real tick; until then the fallback timer paces. */\n private hosted = false;\n /**\n * True while a host that was ticking has gone quiet, and this clock\n * is pacing itself again until it comes back.\n */\n private stalled = false;\n /** Refreshes arrived with nothing pending, before the loop stops. */\n private idleTicks = 0;\n /** The time the last tick carried, for measuring the display's interval. */\n private lastTickTime: UiFrameTime | null = null;\n /**\n * The host's clock minus this thread's, or null before a tick has\n * shown what it is. See `hostTime`.\n */\n private hostOffset: number | null = null;\n /**\n * The last few intervals between ticks, in arrival order.\n *\n * The watch is set from the *median* of them. The largest was the\n * first attempt and it is too generous: one late refresh — a page\n * doing something expensive in its own rAF, which is exactly what\n * happens the moment before a demonstration blocks the thread —\n * widened the watch to twice that late gap and made the next stall\n * take a quarter of a second to notice. A median ignores one\n * outlier in four and still describes the display.\n */\n private readonly intervals: number[] = [];\n private handle: ReturnType<typeof setTimeout> | null = null;\n private readonly fallbackMs: number;\n private readonly stallMs: number;\n private readonly minStallMs: number;\n private readonly now: () => UiFrameTime;\n\n constructor(\n private readonly onFrame: (time: UiFrameTime) => void,\n /** Told when this starts and stops wanting ticks. */\n private readonly onActive: (active: boolean) => void,\n options: UiHostFrameClockOptions = {}\n ) {\n this.fallbackMs = options.fallbackMs ?? 16;\n this.stallMs = options.stallMs ?? 100;\n this.minStallMs = options.minStallMs ?? 12;\n this.now = options.now ?? (() => performance.now());\n }\n\n requestFrame(): void {\n this.pending = true;\n this.setActive(true);\n this.armTimer();\n }\n\n /**\n * Arms the timer that draws a frame the host did not ask for.\n *\n * It plays two roles with one mechanism. Before any tick has arrived\n * it *paces*: a host that does not forward refreshes is then merely\n * no better than the timer this clock replaced, rather than a frozen\n * application — which matters because the capability is not\n * negotiated, and the first real tick is the only evidence it exists.\n *\n * Once ticks are arriving it *watches*: set far beyond a refresh\n * interval, cleared by every tick, and therefore never fired while\n * the host is answering. When it does fire, the host has stopped —\n * a blocked main thread is the case that matters — and this clock\n * goes back to pacing itself until a tick says otherwise.\n *\n * Without that second role the render worker's frames stop dead\n * whenever the shell's thread is busy, which is the one thing the\n * whole architecture exists to prevent.\n */\n private armTimer(): void {\n if (this.handle !== null) {\n return;\n }\n const watching = this.hosted && !this.stalled;\n this.handle = setTimeout(\n () => {\n this.handle = null;\n if (!this.pending) {\n return;\n }\n // A tick would have cleared this timer, so its firing is the\n // evidence that none came.\n this.stalled = this.hosted;\n this.deliver(this.hostTime());\n },\n watching ? this.watchMs() : this.selfPaceMs()\n );\n }\n\n /**\n * How long to wait for a tick before concluding none is coming.\n *\n * Two refreshes: a live host always beats that, and a blocked one is\n * noticed a frame or two after it stops rather than a fixed tenth of\n * a second later. The ceiling applies until enough ticks have\n * arrived to say, which is three, because two timestamps make one\n * interval and one interval is not evidence of a cadence.\n *\n * One refresh was tried first and is the bug this constant exists to\n * record. The reasoning was that the timer is armed after the frame\n * the tick delivered has done its work, so the next tick is already\n * due by the deadline and wins the race whenever the host is alive.\n * It is not: `requestFrame` is called from the runtime's\n * `beforeCollect`, at the *start* of the frame, so the deadline\n * falls a few microseconds after the next tick is due rather than a\n * whole frame's work after it. The race is then decided by the\n * jitter on one `postMessage`, and the watchdog fires on a large\n * share of ordinary frames.\n *\n * It went unseen because the floor hides it on a fast display: at\n * 165Hz a 6ms cadence is raised to `minStallMs`, which is already\n * two refreshes, while at 60Hz a 16.7ms cadence is passed through\n * untouched. Scrolling was smooth on the one panel and ran at a\n * third of the rate on the other, which is exactly the asymmetry\n * this arithmetic produces.\n */\n private watchMs(): number {\n const cadence = this.cadenceMs();\n if (cadence === null) {\n return this.stallMs;\n }\n return Math.min(this.stallMs, Math.max(this.minStallMs, cadence * WATCH_REFRESHES));\n }\n\n /**\n * This thread's own clock, expressed on the host's.\n *\n * A tick carries the host's `requestAnimationFrame` timestamp, and a\n * self-paced frame has only `performance.now()` to offer. In a\n * worker those are not the same clock: `performance.now()` counts\n * from the worker's own creation, so it trails the shell's by the\n * page's age at the moment the worker was spawned, which is hundreds\n * of milliseconds in practice.\n *\n * Handing that raw reading to `onFrame` puts a timestamp from a\n * different origin into a stream of host ones, and everything\n * downstream reads the pair as a jump backwards through time. The\n * runtime's animation phase declines to sample, because every\n * running animation is due later than a `now` that far in the past,\n * and then arms its next wake-up for `next - now`, which is the\n * whole offset. One self-paced frame therefore silences the\n * animation clock for as long as the worker is young. A scroll\n * spring stops being advanced by the frame loop and only moves when\n * something else dirties the graph.\n *\n * So the offset is learned from every tick and added back here.\n * Before the first tick there is nothing to learn from and no host\n * timestamp has been delivered either, so the local reading is the\n * base and the first tick shifts it forward once.\n */\n private hostTime(): number {\n const local = this.now();\n return this.hostOffset === null ? local : local + this.hostOffset;\n }\n\n /** The median recent interval, or null before there are enough to say. */\n private cadenceMs(): number | null {\n if (this.intervals.length < 3) {\n return null;\n }\n const sorted = [...this.intervals].sort((a, b) => a - b);\n return sorted[Math.floor(sorted.length / 2)]!;\n }\n\n /** The interval to pace at while the host is absent: the display's, if it is known. */\n private selfPaceMs(): number {\n const cadence = this.cadenceMs();\n return cadence === null ? this.fallbackMs : Math.max(1, Math.min(this.fallbackMs, cadence));\n }\n\n cancelFrame(): void {\n this.pending = false;\n this.clearFallback();\n this.setActive(false);\n }\n\n private clearFallback(): void {\n if (this.handle !== null) {\n clearTimeout(this.handle);\n this.handle = null;\n }\n }\n\n private deliver(time: UiFrameTime): void {\n this.pending = false;\n this.onFrame(time);\n }\n\n /**\n * Delivers a tick from the host. Ignored when no frame is pending,\n * which is what lets the host keep a loop running a beat longer than\n * it is needed rather than negotiating every frame.\n */\n tick(time: UiFrameTime): void {\n this.hosted = true;\n this.stalled = false;\n // Every tick, not just the first: the two clocks drift, and a tab\n // that was suspended resumes with a different offset entirely.\n // Reading it here costs one subtraction and keeps a self-paced\n // frame on the same timeline as the ticks around it.\n this.hostOffset = time - this.now();\n this.recordCadence(time);\n this.clearFallback();\n if (!this.pending) {\n // Not wanted *yet* is not the same as not wanted. Something\n // animating slower than the display asks for its next frame a\n // moment after finishing this one, so stopping the loop the\n // instant a frame ends and restarting it milliseconds later\n // trades one message per frame for three and a cancelled\n // `requestAnimationFrame` in between. A few idle refreshes of\n // patience costs a dropped tick each and removes all of it.\n this.idleTicks++;\n if (this.idleTicks >= IDLE_TICKS_BEFORE_STOP) {\n this.setActive(false);\n }\n return;\n }\n this.idleTicks = 0;\n this.deliver(time);\n }\n\n /**\n * Keeps the last few gaps between ticks, which is where the watch\n * interval comes from.\n *\n * A gap wider than `MAX_CADENCE_MS` is not a refresh interval — no\n * display runs that slowly — so it is a hiccup, a stall ending, or a\n * tab coming back, and it is dropped rather than taught to the clock.\n * Only a gap wider than the ceiling clears what was learned, because\n * that is long enough that whatever cadence preceded it is stale.\n */\n private recordCadence(time: UiFrameTime): void {\n const previous = this.lastTickTime;\n this.lastTickTime = time;\n if (previous === null) {\n return;\n }\n const interval = time - previous;\n if (interval > this.stallMs) {\n this.intervals.length = 0;\n return;\n }\n if (interval <= 0 || interval > MAX_CADENCE_MS) {\n return;\n }\n this.intervals.push(interval);\n if (this.intervals.length > CADENCE_SAMPLES) {\n this.intervals.shift();\n }\n }\n\n private setActive(active: boolean): void {\n if (active) {\n this.idleTicks = 0;\n }\n if (this.active === active) {\n return;\n }\n this.active = active;\n this.onActive(active);\n }\n}\n\n/**\n * Deterministic clock for tests.\n *\n * Frames are delivered by calling tick(). tick() throws when\n * no frame is pending, which catches accidental double-arming.\n */\nexport class UiManualFrameClock implements UiFrameClock {\n private pending = false;\n\n constructor(private onFrame: (time: UiFrameTime) => void) {}\n\n setCallback(onFrame: (time: UiFrameTime) => void): void {\n this.onFrame = onFrame;\n }\n\n requestFrame(): void {\n this.pending = true;\n }\n\n cancelFrame(): void {\n this.pending = false;\n }\n\n get isPending(): boolean {\n return this.pending;\n }\n\n tick(time: UiFrameTime = 0): void {\n if (!this.pending) {\n throw new Error('No frame is pending.');\n }\n this.pending = false;\n this.onFrame(time);\n }\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { LayoutRecord } from '../layout/LayoutRecord';\nimport type { TextMeasurer } from '../layout/TextMeasurer';\nimport { createPaintState, resolvePaintState } from '../rendering/PaintState';\nimport { detectEditingPlatform, type EditingPlatform } from '../editing/EditingKeymap';\nimport { lineEndAt, lineStartAt } from '../editing/TextBoundaries';\nimport type { HitTester } from '../input/UiHitTester';\nimport type { UiKeyModifiers } from '../input/UiInputEvent';\nimport {\n clearSelectionRange,\n selectableTextNodes,\n selectableTextOf,\n selectionRangeOf,\n setSelectionRange\n} from './UiSelectable';\nimport { clearLinkHover, hasTextLinks, linkOf, linkRunAtPointIn, setLinkHover } from './UiTextLinks';\nimport {\n hasDrawnText,\n offsetAtPointIn,\n paragraphGeometry,\n wordRangeIn,\n type ParagraphGeometry\n} from './TextSelectionGeometry';\n\n/**\n * What the controller needs from the runtime around it: geometry,\n * measurement, and the three things it cannot do itself — mark a node\n * dirty, drop an editable's focus, and reach the clipboard.\n */\nexport interface SelectionHost {\n recordFor(node: UiNode): LayoutRecord | undefined;\n /** Where the node is seen, scroll offsets applied; for the nearest-node search. */\n visibleBox(node: UiNode): LayoutBox;\n readonly measurer: TextMeasurer;\n markDirty(node: UiNode, flags: DirtyFlags): void;\n /** The subtree a selection may range over: the layout root. */\n root(): UiNode;\n /** Hands text to the shell, which is the only thread with a clipboard. */\n copy(text: string): void;\n /**\n * Drops focus when it is on an editable. Pressing text outside a\n * field ends editing, as it does in a browser, so two selections are\n * never lit at once.\n */\n blurEditable(): void;\n now(): number;\n}\n\nexport interface SelectionControllerOptions {\n /** Keyboard conventions; detected from the user agent by default. */\n platform?: EditingPlatform;\n}\n\n/** One end of a selection: a text node and an offset into its text. */\ninterface TextPoint {\n node: UiNode;\n offset: number;\n}\n\n/** How long two presses may be apart and still count as a double click. */\nconst MULTI_CLICK_MS = 500;\nconst MULTI_CLICK_SLOP = 4;\n\n/**\n * Selecting text that nobody types into.\n *\n * `EditableText` carries its own selection in its model, because the\n * caret, the IME and undo all need it there. Everything else the\n * framework draws is glyphs on a canvas with no model at all, so this\n * controller is what lets a user drag across a heading and a paragraph\n * and copy what they highlighted — the thing a page of HTML gives for\n * free and a canvas gives not at all.\n *\n * Model: a selection is two `TextPoint`s, an anchor where the press\n * landed and a focus that follows the pointer. Ordering them needs\n * document order, so the selectable nodes under the root are collected\n * into a list when a drag starts; the range covers the tail of the\n * first node, all of every node between, and the head of the last. The\n * resolved per-node ranges are written onto the nodes themselves,\n * where both renderers read them (see `UiSelectable`).\n *\n * The pointer controller calls in here after the app's listeners have\n * run, and only when none of them called preventDefault().\n */\nexport class UiSelectionController {\n private readonly platform: EditingPlatform;\n private readonly paint = createPaintState();\n private anchor: TextPoint | null = null;\n private focus: TextPoint | null = null;\n private dragging = false;\n /** Document order for the drag in progress; rebuilt on each press. */\n private order: UiNode[] = [];\n /** The nodes currently carrying a range, so they can be cleared. */\n private painted: UiNode[] = [];\n private lastPress: { node: UiNode; x: number; y: number; at: number; count: number } | null = null;\n /** The link a press landed on, until it is released or dragged off. */\n private pendingLink: { node: UiNode; index: number; x: number; y: number } | null = null;\n /** The node carrying a lit link run, so it can be unlit when the pointer leaves. */\n private hoveredLink: UiNode | null = null;\n\n constructor(\n private readonly host: SelectionHost,\n private readonly hitTester: HitTester,\n options: SelectionControllerOptions = {}\n ) {\n this.platform = options.platform ?? detectEditingPlatform();\n }\n\n /** Whether anything is selected. */\n get hasSelection(): boolean {\n return this.painted.length > 0;\n }\n\n /** Whether a press on this node would start a selection. */\n isSelectable(node: UiNode): boolean {\n return selectableTextOf(node) !== undefined;\n }\n\n // ---------------------------------------------------------------------------\n // Pointer\n // ---------------------------------------------------------------------------\n\n /**\n * A press. On selectable text it places the anchor (Shift extends\n * from the existing one), and a second or third press within the\n * double-click window takes the word or the line. Anywhere else it\n * clears the selection, as pressing outside text does in a browser.\n *\n * Returns true when a selection is now in progress, so the caller\n * knows the press was consumed.\n */\n pointerDown(node: UiNode | null, x: number, y: number, modifiers: UiKeyModifiers): boolean {\n this.pendingLink = null;\n if (node === null) {\n this.clear();\n return false;\n }\n const geometry = this.geometryOf(node);\n const local = this.hitTester.toLocal(node, x, y);\n if (hasTextLinks(node)) {\n // A link is remembered rather than followed: a press that goes\n // on to drag is a selection, and only a press that is released\n // where it landed is a click. Held here and not in the pointer\n // controller because this is the one place that turns a point\n // inside a paragraph into a run.\n const index = linkRunAtPointIn(geometry, local.x, local.y);\n if (linkOf(node, index) !== undefined) {\n this.pendingLink = { node, index, x, y };\n }\n }\n if (!this.isSelectable(node) || !hasDrawnText(geometry)) {\n this.clear();\n return this.pendingLink !== null;\n }\n const offset = offsetAtPointIn(geometry, local.x, local.y);\n const now = this.host.now();\n const last = this.lastPress;\n const count =\n !modifiers.shift &&\n last !== null &&\n last.node === node &&\n now - last.at <= MULTI_CLICK_MS &&\n Math.abs(last.x - x) <= MULTI_CLICK_SLOP &&\n Math.abs(last.y - y) <= MULTI_CLICK_SLOP\n ? last.count + 1\n : 1;\n this.lastPress = { node, x, y, at: now, count };\n\n // Pressing text ends editing, so an editable's selection does not\n // stay lit beside this one.\n this.host.blurEditable();\n this.order = selectableTextNodes(this.host.root());\n this.dragging = true;\n\n if (modifiers.shift && this.anchor !== null) {\n this.focus = { node, offset };\n } else if (count === 2) {\n const word = wordRangeIn(geometry, offset);\n this.anchor = { node, offset: word.start };\n this.focus = { node, offset: word.end };\n } else if (count >= 3) {\n this.anchor = { node, offset: lineStartAt(geometry.text, offset) };\n this.focus = { node, offset: lineEndAt(geometry.text, offset) };\n } else {\n this.anchor = { node, offset };\n this.focus = { node, offset };\n }\n this.apply();\n return true;\n }\n\n /**\n * A drag. The focus follows the pointer across node boundaries, which\n * is why this hit-tests rather than using the pressed node: the\n * pointer controller captures a press to its target, and a selection\n * is the one gesture that has to leave it.\n */\n pointerMove(x: number, y: number): void {\n if (\n this.pendingLink !== null &&\n (Math.abs(x - this.pendingLink.x) > MULTI_CLICK_SLOP || Math.abs(y - this.pendingLink.y) > MULTI_CLICK_SLOP)\n ) {\n this.pendingLink = null;\n }\n if (!this.dragging || this.anchor === null) {\n return;\n }\n const point = this.pointAt(x, y);\n if (point === null) {\n return;\n }\n if (this.focus !== null && this.focus.node === point.node && this.focus.offset === point.offset) {\n return;\n }\n this.focus = point;\n this.apply();\n }\n\n pointerUp(): void {\n this.dragging = false;\n const pending = this.pendingLink;\n this.pendingLink = null;\n if (pending === null) {\n return;\n }\n // A press that selected something was a drag, however short.\n if (this.anchor !== null && this.focus !== null && this.anchor.offset !== this.focus.offset) {\n return;\n }\n linkOf(pending.node, pending.index)?.onClick?.();\n }\n\n /**\n * The pointer moved with nothing pressed.\n *\n * Lights the link run under it, which is what draws the wash and the\n * underline and what `resolveCursor` reads to show a pointer. The\n * state is written onto the node, as a selection and a find match\n * are, because paint has the node and nothing else.\n */\n pointerHover(node: UiNode | null, x: number, y: number): void {\n let target: UiNode | null = null;\n let index = -1;\n if (node !== null && hasTextLinks(node)) {\n const local = this.hitTester.toLocal(node, x, y);\n const at = linkRunAtPointIn(this.geometryOf(node), local.x, local.y);\n if (linkOf(node, at) !== undefined) {\n target = node;\n index = at;\n }\n }\n if (this.hoveredLink !== null && this.hoveredLink !== target && clearLinkHover(this.hoveredLink)) {\n this.host.markDirty(this.hoveredLink, DirtyFlags.Paint);\n }\n this.hoveredLink = target;\n if (target !== null && setLinkHover(target, index)) {\n this.host.markDirty(target, DirtyFlags.Paint);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Keyboard\n // ---------------------------------------------------------------------------\n\n /**\n * Default keyboard behaviour when no editable has focus: copy the\n * selection, select everything, or clear it. Returns true when the\n * key was consumed.\n */\n handleKey(key: string, modifiers: UiKeyModifiers): boolean {\n const primary = this.platform === 'mac' ? modifiers.meta : modifiers.ctrl;\n if (primary && (key === 'c' || key === 'C')) {\n const text = this.selectedText();\n if (text.length === 0) {\n return false;\n }\n this.host.copy(text);\n return true;\n }\n if (primary && (key === 'a' || key === 'A')) {\n return this.selectAll();\n }\n if (key === 'Escape' && this.hasSelection) {\n this.clear();\n return true;\n }\n return false;\n }\n\n /** Selects every selectable text node under the root, in document order. */\n selectAll(): boolean {\n const order = selectableTextNodes(this.host.root());\n if (order.length === 0) {\n return false;\n }\n const first = order[0];\n const last = order[order.length - 1];\n this.order = order;\n this.anchor = { node: first, offset: 0 };\n this.focus = { node: last, offset: this.geometryOf(last).end };\n this.apply();\n return true;\n }\n\n /**\n * Selects a range of one node's text, as if it had been dragged.\n * Used by find to light the active match: it is a real selection, so\n * copy takes it and it looks like every other one.\n */\n selectRange(node: UiNode, start: number, end: number): boolean {\n if (selectableTextOf(node) === undefined) {\n return false;\n }\n this.order = selectableTextNodes(this.host.root());\n if (!this.order.includes(node)) {\n return false;\n }\n this.anchor = { node, offset: start };\n this.focus = { node, offset: end };\n this.lastPress = null;\n this.apply();\n return this.hasSelection;\n }\n\n // ---------------------------------------------------------------------------\n // State\n // ---------------------------------------------------------------------------\n\n /**\n * The selected text, node by node in document order, one newline\n * between nodes — the paragraph breaks a reader would expect when\n * pasting a heading and the text under it.\n */\n selectedText(): string {\n const parts: string[] = [];\n for (const node of this.painted) {\n const range = selectionRangeOf(node);\n const text = selectableTextOf(node);\n if (range === undefined || text === undefined) {\n continue;\n }\n parts.push(text.slice(range.start, range.end));\n }\n return parts.join('\\n');\n }\n\n /** Drops the selection and repaints whatever was carrying it. */\n clear(): void {\n this.anchor = null;\n this.focus = null;\n this.dragging = false;\n if (this.painted.length === 0) {\n return;\n }\n for (const node of this.painted) {\n if (clearSelectionRange(node)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n this.painted = [];\n }\n\n /**\n * A node left the tree. Its range went with it, so the rest of the\n * selection no longer means anything: virtualization and route\n * changes both take nodes out from under a live selection.\n */\n handleNodeRemoved(node: UiNode): void {\n if (this.hoveredLink === node) {\n this.hoveredLink = null;\n }\n if (this.pendingLink?.node === node) {\n this.pendingLink = null;\n }\n if (this.painted.includes(node) || this.anchor?.node === node || this.focus?.node === node) {\n this.clear();\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internals\n // ---------------------------------------------------------------------------\n\n /**\n * The text position under a canvas point: the offset in the node hit,\n * or — when the pointer is over a gap, a margin or another widget —\n * the nearest position in the node whose visible box is closest. A\n * drag that leaves the text it started in keeps selecting, as it does\n * everywhere else.\n */\n private pointAt(x: number, y: number): TextPoint | null {\n const hit = this.hitTester.hitTest(x, y);\n if (hit !== null && this.isSelectable(hit.node)) {\n const geometry = this.geometryOf(hit.node);\n if (hasDrawnText(geometry)) {\n const local = this.hitTester.toLocal(hit.node, x, y);\n return { node: hit.node, offset: offsetAtPointIn(geometry, local.x, local.y) };\n }\n }\n const nearest = this.nearestNode(x, y);\n if (nearest === null) {\n return null;\n }\n const geometry = this.geometryOf(nearest);\n if (!hasDrawnText(geometry)) {\n return null;\n }\n const local = this.hitTester.toLocal(nearest, x, y);\n return { node: nearest, offset: offsetAtPointIn(geometry, local.x, local.y) };\n }\n\n /** The selectable node whose visible box is closest to a canvas point. */\n private nearestNode(x: number, y: number): UiNode | null {\n let best: UiNode | null = null;\n let bestDistance = Infinity;\n for (const node of this.order) {\n if (this.host.recordFor(node) === undefined) {\n continue;\n }\n const box = this.host.visibleBox(node);\n const dx = Math.max(box.x - x, 0, x - (box.x + box.width));\n const dy = Math.max(box.y - y, 0, y - (box.y + box.height));\n // Vertical distance dominates: a point beside a line belongs to\n // that line, not to a nearer one on the row above.\n const distance = dy * dy * 4 + dx * dx;\n if (distance < bestDistance) {\n bestDistance = distance;\n best = node;\n }\n }\n return best;\n }\n\n /**\n * Resolves the two ends into a range per node and writes them where\n * paint will find them.\n */\n private apply(): void {\n const anchor = this.anchor;\n const focus = this.focus;\n if (anchor === null || focus === null) {\n this.clear();\n return;\n }\n const anchorIndex = this.order.indexOf(anchor.node);\n const focusIndex = this.order.indexOf(focus.node);\n if (anchorIndex < 0 || focusIndex < 0) {\n // One end is no longer in the tree; nothing sensible spans them.\n this.clear();\n return;\n }\n const forward = anchorIndex < focusIndex || (anchorIndex === focusIndex && anchor.offset <= focus.offset);\n const from = forward ? anchor : focus;\n const to = forward ? focus : anchor;\n const first = Math.min(anchorIndex, focusIndex);\n const last = Math.max(anchorIndex, focusIndex);\n\n const next: UiNode[] = [];\n for (let i = first; i <= last; i++) {\n const node = this.order[i];\n const end = this.geometryOf(node).end;\n const start = i === first ? Math.min(from.offset, end) : 0;\n const stop = i === last ? Math.min(to.offset, end) : end;\n if (stop <= start) {\n continue;\n }\n if (setSelectionRange(node, start, stop)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n next.push(node);\n }\n for (const node of this.painted) {\n if (!next.includes(node) && clearSelectionRange(node)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n this.painted = next;\n }\n\n /**\n * The node's text laid out in its own coordinates: the content box\n * starts at the padding, as it does for the renderers in the parent's\n * space. Before the first layout the record is empty and there is\n * nothing drawn to select.\n */\n private geometryOf(node: UiNode): ParagraphGeometry {\n const rec = this.host.recordFor(node);\n const state = resolvePaintState(node, this.paint);\n const box: LayoutBox =\n rec === undefined\n ? { x: 0, y: 0, width: 0, height: 0 }\n : {\n x: rec.paddingLeft,\n y: rec.paddingTop,\n width: Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight),\n height: Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom)\n };\n return paragraphGeometry(state.text ?? '', box, state, this.host.measurer);\n }\n}\n","import type { UiNode } from '../graph/UiNode';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { UiRole, UiSemanticState, UiSemanticStates, UiLiveRegion } from '../properties/UiSemantics';\nimport { normalizeStates } from '../properties/UiSemantics';\nimport type { UiResolvedTextSpan, UiTextLink } from '../properties/UiTextStyle';\nimport { resolvedSpansOf, textContentOf } from '../properties/UiTextStyle';\n\n/**\n * One node of the semantics tree, flattened.\n *\n * Flat rather than nested because the consumer is a diff: a record\n * carries its parent and its position among its semantic siblings, so\n * two frames' maps can be compared key by key without walking two\n * trees in step. `parent` is null for a root-level record.\n *\n * Geometry is deliberately absent. A record changes when what the node\n * *means* changes; where it sits changes on every scrolled frame, and\n * F6b's mirror can read a box from the layout engine by id when it\n * needs one. Keeping them apart is what makes \"no semantics changed\"\n * a cheap and true statement.\n */\nexport interface UiSemanticsRecord {\n readonly id: string;\n readonly parent: string | null;\n /** Position among the records sharing this parent. */\n readonly index: number;\n readonly role?: UiRole;\n /** The accessible name: an explicit `label`, else the node's text. */\n readonly label?: string;\n readonly description?: string;\n /** Announced on change without focus; see `UiProperties.live`. */\n readonly live?: UiLiveRegion;\n /** Sorted and de-duplicated, so declaration order does not diff. */\n readonly states?: readonly UiSemanticState[];\n readonly disabled?: true;\n readonly valueNow?: number;\n readonly valueMin?: number;\n readonly valueMax?: number;\n readonly valueText?: string;\n readonly posInSet?: number;\n readonly setSize?: number;\n /** How deep a treeitem sits, 1 for a root. */\n readonly level?: number;\n}\n\n/** Insertion-ordered: iterating the map walks the tree in document order. */\nexport type UiSemanticsMap = ReadonlyMap<string, UiSemanticsRecord>;\n\n/**\n * The roles a node type carries without being asked.\n *\n * A `Button` is a button whether or not the component that built it\n * said so — which is what makes \"every component emits semantics\"\n * achievable rather than aspirational. Everything else states its own\n * role or is transparent.\n */\nconst IMPLICIT_ROLES: Partial<Record<UiNodeType, UiRole>> = {\n [UiNodeType.Button]: 'button',\n [UiNodeType.EditableText]: 'textbox'\n};\n\n/**\n * Roles whose descendants say nothing of their own.\n *\n * ARIA calls these \"children presentational\": the text inside a button\n * is how the button is named, not a paragraph next to it, and a screen\n * reader that announced both would say everything twice. The list is\n * ARIA's, narrowed to the roles `UiRole` has, plus `menuitem`: ARIA\n * names a menu item from its contents but stops short of marking its\n * children presentational, and the only difference that makes here is\n * a row whose label is on the tree twice.\n *\n * The other roles ARIA names from their contents are deliberately out.\n * A `listitem`, a `cell` or a `row` holds content rather than being\n * spelled by it, and a button inside one has to stay a record of its\n * own.\n */\nconst PRESENTATIONAL_CHILDREN: ReadonlySet<UiRole> = new Set<UiRole>([\n 'button',\n 'checkbox',\n 'image',\n 'menuitem',\n 'menuitemcheckbox',\n 'menuitemradio',\n 'option',\n 'progressbar',\n 'radio',\n 'separator',\n 'slider',\n 'switch',\n 'tab'\n]);\n\n/**\n * Builds the semantics tree under `root`.\n *\n * Three rules, in order:\n *\n * 1. A node is a **semantics node** when it declares a `role` or a\n * `label`, or its type has an implicit role, or it is a `Text`\n * with text that no ancestor has claimed as its name. Everything\n * else is transparent: its children attach to the nearest\n * semantics ancestor, so wrapping a button in three boxes for\n * layout does not put three nodes in front of a screen reader.\n * 2. A semantics node's name is its `label` if it has one, else the\n * text it and its non-semantic descendants draw. Text used that\n * way is *claimed*: it does not also become a record, so a\n * `Button(Text('Save'))` is one node named \"Save\", not two.\n * 3. An invisible subtree is not in the tree at all. A disabled one\n * is, carrying `disabled` — unavailable is a thing to announce,\n * hidden is not.\n */\nexport function buildSemanticsTree(root: UiNode): UiSemanticsMap {\n const records = new Map<string, UiSemanticsRecord>();\n const childCounts = new Map<string | null, number>();\n\n const nextIndex = (parent: string | null): number => {\n const index = childCounts.get(parent) ?? 0;\n childCounts.set(parent, index + 1);\n return index;\n };\n\n const visit = (node: UiNode, parent: string | null, inert: boolean): void => {\n if (node.properties.get('visible') === false) {\n return;\n }\n const disabled = inert || node.properties.get('disabled') === true;\n const record = describe(node, parent, disabled, nextIndex);\n if (record === null) {\n for (let child = node.firstChild; child !== null; child = child.nextSibling) {\n visit(child, parent, disabled);\n }\n return;\n }\n const runs = linkRunsOf(node);\n if (runs.length > 0) {\n // A paragraph with links is mirrored run by run: the prose\n // between the links and each link itself become records of their\n // own, in reading order, and the paragraph keeps no name. A\n // screen reader then reaches the link the way it reaches one in\n // a page, rather than hearing its words go by inside a sentence\n // it cannot act on.\n records.set(record.id, paragraphOf(record));\n appendTextRuns(node, record.id, runs, records);\n } else {\n records.set(record.id, record);\n }\n for (let child = node.firstChild; child !== null; child = child.nextSibling) {\n if (claimedByName(node, child)) {\n continue;\n }\n visit(child, record.id, disabled);\n }\n };\n\n visit(root, null, false);\n return records;\n}\n\n/**\n * The record for a node, or null when the node is transparent.\n */\nfunction describe(\n node: UiNode,\n parent: string | null,\n disabled: boolean,\n nextIndex: (parent: string | null) => number\n): UiSemanticsRecord | null {\n const role = (node.properties.get('role') as UiRole | undefined) ?? IMPLICIT_ROLES[node.type as UiNodeType];\n const declaredLabel = node.properties.get('label') as string | undefined;\n const name = declaredLabel ?? accessibleText(node);\n const isText = node.type === UiNodeType.Text;\n // Prose with nothing else to say is still a record: a screen reader\n // has to be able to read the page, not only operate it.\n if (role === undefined && declaredLabel === undefined && !(isText && name !== undefined)) {\n return null;\n }\n const states = node.properties.get('states') as UiSemanticStates | undefined;\n return prune({\n id: node.id,\n parent,\n index: nextIndex(parent),\n role,\n label: name,\n description: node.properties.get('description') as string | undefined,\n live: node.properties.get('live') as UiLiveRegion | undefined,\n states: states === undefined || states.length === 0 ? undefined : normalizeStates(states),\n disabled: disabled ? true : undefined,\n valueNow: node.properties.get('valueNow') as number | undefined,\n valueMin: node.properties.get('valueMin') as number | undefined,\n valueMax: node.properties.get('valueMax') as number | undefined,\n valueText: valueTextOf(node),\n posInSet: node.properties.get('posInSet') as number | undefined,\n setSize: node.properties.get('setSize') as number | undefined,\n level: node.properties.get('level') as number | undefined\n });\n}\n\n/**\n * An editable's content is its value, not its name, and it is held on\n * the node as `value` — the property the app sets and `onInput`\n * reports. An explicit `valueText` overrides it.\n */\nfunction valueTextOf(node: UiNode): string | undefined {\n const declared = node.properties.get('valueText') as string | undefined;\n if (declared !== undefined) {\n return declared;\n }\n if (node.type === UiNodeType.EditableText) {\n return (node.properties.get('value') as string | undefined) ?? '';\n }\n return undefined;\n}\n\n/**\n * The text a node draws, including the text of transparent\n * descendants, joined by spaces. Stops at a descendant that is a\n * semantics node of its own, which has its own name to say.\n */\nfunction accessibleText(node: UiNode): string | undefined {\n if (node.type === UiNodeType.EditableText) {\n return undefined;\n }\n const parts: string[] = [];\n const own = textContentOf(node);\n if (own.length > 0) {\n parts.push(own);\n }\n for (let child = node.firstChild; child !== null; child = child.nextSibling) {\n if (!namesItsParent(child)) {\n continue;\n }\n const text = accessibleText(child);\n if (text !== undefined) {\n parts.push(text);\n }\n }\n const joined = parts.join(' ').trim();\n return joined.length > 0 ? joined : undefined;\n}\n\n/**\n * Whether a child's text belongs to its parent's name rather than to\n * a record of its own: it must be visible and say nothing itself.\n */\nfunction namesItsParent(child: UiNode): boolean {\n if (child.properties.get('visible') === false) {\n return false;\n }\n if (child.properties.get('role') !== undefined || child.properties.get('label') !== undefined) {\n return false;\n }\n return IMPLICIT_ROLES[child.type as UiNodeType] === undefined;\n}\n\n/**\n * Whether a child is already spoken for by its parent.\n *\n * Either because the parent's name was built from its text, or because\n * the parent's role makes its whole subtree presentational — a labelled\n * button still hides the glyph inside it. A labelled *container*\n * (a form, a list, a dialog) claims nothing: its label names it, and\n * its children are the content that label introduces.\n */\nfunction claimedByName(parent: UiNode, child: UiNode): boolean {\n const role = (parent.properties.get('role') as UiRole | undefined) ?? IMPLICIT_ROLES[parent.type as UiNodeType];\n if (role !== undefined && PRESENTATIONAL_CHILDREN.has(role)) {\n return true;\n }\n if (parent.properties.get('label') !== undefined) {\n return false;\n }\n return namesItsParent(child) && accessibleText(child) !== undefined;\n}\n\n/** Drops the undefined members, so records compare and serialise small. */\nfunction prune(record: UiSemanticsRecord): UiSemanticsRecord {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(record)) {\n if (value !== undefined) {\n result[key] = value;\n }\n }\n return result as unknown as UiSemanticsRecord;\n}\n\n// ---------------------------------------------------------------------------\n// Runs\n// ---------------------------------------------------------------------------\n\n/**\n * Separates a run record's id from its node's.\n *\n * A run is not a node and has no id of its own, so it borrows its\n * paragraph's and adds its position. Nothing generates a node id\n * containing this character, and the shape is what lets the framework\n * route an action on a run back to the paragraph that owns it.\n */\nexport const TEXT_RUN_ID_SEPARATOR = '#run';\n\n/** The paragraph a run record belongs to, and which run it is; null for any other id. */\nexport function textRunOfRecordId(id: string): { nodeId: string; index: number } | null {\n const at = id.lastIndexOf(TEXT_RUN_ID_SEPARATOR);\n if (at < 0) {\n return null;\n }\n const index = Number(id.slice(at + TEXT_RUN_ID_SEPARATOR.length));\n return Number.isInteger(index) && index >= 0 ? { nodeId: id.slice(0, at), index } : null;\n}\n\n/** The runs of a node's paragraph that are links; empty when none are. */\nfunction linkRunsOf(node: UiNode): readonly UiResolvedTextSpan[] {\n const spans = resolvedSpansOf(node);\n for (const span of spans) {\n if (span.link !== undefined) {\n return spans;\n }\n }\n return EMPTY_SPANS;\n}\n\nconst EMPTY_SPANS: readonly UiResolvedTextSpan[] = [];\n\n/**\n * The paragraph a run-mirrored node becomes: a `paragraph` unless it\n * said what it was, and named by what it contains rather than by a\n * label, so its text is not read twice.\n */\nfunction paragraphOf(record: UiSemanticsRecord): UiSemanticsRecord {\n const { label: _label, ...rest } = record;\n return { ...rest, role: record.role ?? 'paragraph' };\n}\n\n/**\n * One record per stretch of the paragraph, in reading order: prose\n * where no link covers it, a `link` where one does.\n *\n * A link's name is its `label` when it has one, because \"here\" and\n * \"read more\" are links a screen reader user cannot tell apart, and\n * its text otherwise.\n */\nfunction appendTextRuns(\n node: UiNode,\n parent: string,\n spans: readonly UiResolvedTextSpan[],\n records: Map<string, UiSemanticsRecord>\n): void {\n const text = textContentOf(node);\n let index = 0;\n const push = (from: number, to: number, link: UiTextLink | undefined): void => {\n const value = text.slice(from, to);\n if (link === undefined && value.trim().length === 0) {\n // Whitespace between two links is not something to announce.\n return;\n }\n const id = `${node.id}${TEXT_RUN_ID_SEPARATOR}${index}`;\n records.set(id, {\n id,\n parent,\n index,\n role: link === undefined ? undefined : 'link',\n label: link?.label ?? value\n });\n index++;\n };\n let at = 0;\n for (const span of spans) {\n if (span.link === undefined) {\n continue;\n }\n push(at, span.start, undefined);\n push(span.start, span.end, span.link);\n at = span.end;\n }\n push(at, text.length, undefined);\n}\n","import type { UiSemanticsMap, UiSemanticsRecord } from './UiSemanticsTree';\n\n/**\n * What changed in the semantics tree between two frames.\n *\n * `add` and `update` carry the whole record rather than a delta: the\n * records are small, the consumer is a DOM mirror that sets attributes\n * from them, and a whole record cannot be applied out of order into\n * something half-updated. `remove` carries only the id, since a\n * removed subtree's records are each removed in their own patch.\n */\nexport type UiSemanticsPatch =\n | { readonly op: 'add'; readonly node: UiSemanticsRecord }\n | { readonly op: 'update'; readonly node: UiSemanticsRecord }\n | { readonly op: 'remove'; readonly id: string };\n\n/**\n * Diffs two semantics maps.\n *\n * Removals come first so a consumer never holds two records claiming\n * the same parent and index; adds and updates follow in document\n * order, which is the order `buildSemanticsTree` produced them in, so\n * a mirror can append as it reads.\n */\nexport function diffSemantics(previous: UiSemanticsMap, next: UiSemanticsMap): UiSemanticsPatch[] {\n const patches: UiSemanticsPatch[] = [];\n for (const id of previous.keys()) {\n if (!next.has(id)) {\n patches.push({ op: 'remove', id });\n }\n }\n for (const [id, record] of next) {\n const before = previous.get(id);\n if (before === undefined) {\n patches.push({ op: 'add', node: record });\n continue;\n }\n if (!recordsEqual(before, record)) {\n patches.push({ op: 'update', node: record });\n }\n }\n return patches;\n}\n\n/** Structural equality over the record's flat, pruned members. */\nexport function recordsEqual(a: UiSemanticsRecord, b: UiSemanticsRecord): boolean {\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) {\n return false;\n }\n for (const key of aKeys) {\n const left = (a as unknown as Record<string, unknown>)[key];\n const right = (b as unknown as Record<string, unknown>)[key];\n if (key === 'states') {\n const leftStates = left as readonly string[];\n const rightStates = right as readonly string[] | undefined;\n if (\n rightStates === undefined ||\n leftStates.length !== rightStates.length ||\n leftStates.some((state, index) => state !== rightStates[index])\n ) {\n return false;\n }\n continue;\n }\n if (!Object.is(left, right)) {\n return false;\n }\n }\n return true;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["describe","kind","isObservable","clamp","follow","isObservable","contains","MULTI_CLICK_MS","MULTI_CLICK_SLOP","isObservable","kind","kind","kind","controllers","EMPTY_OPTIONS","numberOr","numberOr","numberOr","isObservable","load","isObservable","MAX_CACHE_ENTRIES","DEFAULT_CAPACITY","defaultFetch","DEFAULT_CAPACITY","parseColor","parseColor","EMPTY"],"sources":["../src/animation/UiEasing.ts","../src/animation/Interpolate.ts","../src/animation/UiAnimation.ts","../src/environment/UiMotion.ts","../src/animation/UiTransition.ts","../src/animation/AnimationDriver.ts","../src/animation/UiSharedElements.ts","../src/animation/UiMotionState.ts","../src/bindings/UiChildrenBinding.ts","../src/bindings/UiEventBinding.ts","../src/composition/UiElement.ts","../src/composition/UiFactory.ts","../src/modifiers/UiModifier.ts","../src/modifiers/interaction.ts","../src/composition/UiComponents.ts","../src/composition/UiEventProps.ts","../src/modifiers/sameArgs.ts","../src/modifiers/UiModifierSet.ts","../src/graph/UiPropertyTransitions.ts","../src/composition/UiGraphBuilder.ts","../src/composition/UiVirtualWindow.ts","../src/composition/UiLazyList.ts","../src/modifiers/breakpoints.ts","../src/composition/UiResponsive.ts","../src/editing/EditingKeymap.ts","../src/environment/EnvironmentNotifier.ts","../src/environment/UiEnvironmentProvider.ts","../src/find/TextFind.ts","../src/find/UiFindController.ts","../src/input/FocusNotifier.ts","../src/input/UiShortcuts.ts","../src/input/UiDragSession.ts","../src/input/UiEditingController.ts","../src/layout/LayoutNotifier.ts","../src/modifiers/bundle.ts","../src/modifiers/decoration.ts","../src/modifiers/focus.ts","../src/modifiers/clickOutside.ts","../src/modifiers/drag.ts","../src/modifiers/drop.ts","../src/modifiers/pinch.ts","../src/modifiers/shortcuts.ts","../src/modifiers/animation.ts","../src/modifiers/media.ts","../src/modifiers/motion.ts","../src/modifiers/video.ts","../src/modifiers/scroll.ts","../src/modifiers/insets.ts","../src/modifiers/themeTokens.ts","../src/rendering/canvas2d/CanvasTextMeasurer.ts","../src/rendering/LayoutInspector.ts","../src/rendering/ImageResolver.ts","../src/rendering/IconRasterizer.ts","../src/rendering/webgpu/WebGPUError.ts","../src/rendering/webgpu/WebGPUDevice.ts","../src/rendering/webgpu/WebGPUShader.ts","../src/rendering/webgpu/WebGPUGlyphAtlas.ts","../src/rendering/BidiRuns.ts","../src/rendering/webgpu/WebGPUGlyphShaper.ts","../src/rendering/webgpu/WebGPUColor.ts","../src/rendering/webgpu/WebGPUSurface.ts","../src/rendering/webgpu/WebGPURenderData.ts","../src/rendering/webgpu/WebGPUPipeline.ts","../src/rendering/webgpu/WebGPUTextureCache.ts","../src/rendering/webgpu/WebGPUGlyphPages.ts","../src/rendering/webgpu/WebGPURenderer.ts","../src/media/Mp3Frames.ts","../src/media/Mp4Demuxer.ts","../src/media/ByteSource.ts","../src/media/VideoResolver.ts","../src/media/WebVtt.ts","../src/scheduler/PerformanceMarks.ts","../src/scheduler/UiFrame.ts","../src/scheduler/UiScheduler.ts","../src/scheduler/UiFrameClock.ts","../src/selection/UiSelectionController.ts","../src/semantics/UiSemanticsTree.ts","../src/semantics/UiSemanticsDiff.ts"],"sourcesContent":["/**\n * How a tween's progress maps onto its value.\n *\n * Takes normalised time in [0, 1] and answers normalised progress.\n * An easing may leave the range — an overshooting curve is a valid\n * easing — so nothing downstream clamps what it returns.\n */\nexport type UiEasing = (t: number) => number;\n\n/** Progress equals time. What a spinner and a sweep want. */\nexport const linear: UiEasing = t => t;\n\n/**\n * A cubic Bézier easing, in the CSS `cubic-bezier(x1, y1, x2, y2)`\n * sense: the curve runs from (0, 0) to (1, 1) through the two control\n * points, and the easing is y at the t where x equals the input.\n *\n * Solved by Newton's method with a bisection fallback, which is what\n * every browser engine does; eight iterations is well past the point\n * where the result stops moving in a float.\n */\nexport function cubicBezier(x1: number, y1: number, x2: number, y2: number): UiEasing {\n const ax = 3 * x1 - 3 * x2 + 1;\n const bx = 3 * x2 - 6 * x1;\n const cx = 3 * x1;\n const ay = 3 * y1 - 3 * y2 + 1;\n const by = 3 * y2 - 6 * y1;\n const cy = 3 * y1;\n\n const sampleX = (t: number): number => ((ax * t + bx) * t + cx) * t;\n const sampleY = (t: number): number => ((ay * t + by) * t + cy) * t;\n const slopeX = (t: number): number => (3 * ax * t + 2 * bx) * t + cx;\n\n return (input: number): number => {\n if (input <= 0) {\n return 0;\n }\n if (input >= 1) {\n return 1;\n }\n let t = input;\n for (let i = 0; i < 8; i++) {\n const error = sampleX(t) - input;\n if (Math.abs(error) < 1e-6) {\n return sampleY(t);\n }\n const slope = slopeX(t);\n if (Math.abs(slope) < 1e-6) {\n break;\n }\n t -= error / slope;\n }\n // Newton stalled on a flat segment; bisect instead, which cannot.\n let low = 0;\n let high = 1;\n t = input;\n for (let i = 0; i < 24; i++) {\n const x = sampleX(t);\n if (Math.abs(x - input) < 1e-6) {\n break;\n }\n if (x > input) {\n high = t;\n } else {\n low = t;\n }\n t = (low + high) / 2;\n }\n return sampleY(t);\n };\n}\n\n/**\n * An easing that only takes `count` distinct values.\n *\n * The reason it exists is the `Spinner`: eight blades have eight\n * positions promised the F4 version would keep a\n * turn at eight writes a second rather than sixty. Paired with an\n * animation's `stepMs`, this is how that promise is kept — the value\n * is stepped and the frames are too.\n */\nexport function steps(count: number): UiEasing {\n if (!(count >= 1)) {\n throw new Error(`steps() needs at least one step, got ${count}.`);\n }\n return t => Math.min(count - 1, Math.floor(t * count)) / count;\n}\n\n/**\n * The named curves a motion vocabulary is built from.\n *\n * `standard` is the one to reach for: something entering and leaving\n * at a natural pace. `decelerate` is for something arriving (it starts\n * fast and settles), `accelerate` for something leaving. `emphasized`\n * is `standard` with more of a lean, for the one movement on a screen\n * that should be noticed.\n */\nexport const easings = {\n linear,\n standard: cubicBezier(0.2, 0, 0, 1),\n decelerate: cubicBezier(0, 0, 0, 1),\n accelerate: cubicBezier(0.3, 0, 1, 1),\n emphasized: cubicBezier(0.05, 0.7, 0.1, 1)\n} as const;\n","import type { UiColor } from '../properties/UiColor';\nimport type { UiTransform } from '../properties/UiTransform';\n\n/**\n * Blends two values of one property.\n *\n * `t` is progress, normally in [0, 1] but not necessarily: a spring\n * and an overshooting easing both leave the range, and an interpolator\n * that clamped would flatten exactly the part of the motion that is\n * worth having.\n */\nexport type UiInterpolator<T> = (from: T, to: T, t: number) => T;\n\nexport const interpolateNumber: UiInterpolator<number> = (from, to, t) => from + (to - from) * t;\n\n/**\n * Blends two colours channel by channel, in the [0, 1] float space\n * `UiColor` already uses.\n *\n * Linear RGB rather than a perceptual space: both renderers hand\n * these channels straight to the GPU or to a `fillStyle`, so blending\n * anywhere else would mean converting twice a frame to be slightly\n * more correct about a midpoint nobody looks at for 200 ms.\n */\nexport const interpolateColor: UiInterpolator<UiColor> = (from, to, t) => ({\n r: from.r + (to.r - from.r) * t,\n g: from.g + (to.g - from.g) * t,\n b: from.b + (to.b - from.b) * t,\n a: from.a + (to.a - from.a) * t\n});\n\n/**\n * Blends two transforms field by field, filling identity for whatever\n * either side omits.\n *\n * `x` and `y` are the pivot, not a translation (see `UiTransform`), so\n * blending them moves where the rotation happens rather than moving\n * the node. `translateX` and `translateY` are the pair that moves it.\n */\nexport const interpolateTransform: UiInterpolator<Partial<UiTransform>> = (from, to, t) => ({\n x: interpolateNumber(from.x ?? 0, to.x ?? 0, t),\n y: interpolateNumber(from.y ?? 0, to.y ?? 0, t),\n translateX: interpolateNumber(from.translateX ?? 0, to.translateX ?? 0, t),\n translateY: interpolateNumber(from.translateY ?? 0, to.translateY ?? 0, t),\n scaleX: interpolateNumber(from.scaleX ?? 1, to.scaleX ?? 1, t),\n scaleY: interpolateNumber(from.scaleY ?? 1, to.scaleY ?? 1, t),\n rotation: interpolateNumber(from.rotation ?? 0, to.rotation ?? 0, t)\n});\n\nfunction isColor(value: object): boolean {\n const candidate = value as Partial<UiColor>;\n return (\n typeof candidate.r === 'number' &&\n typeof candidate.g === 'number' &&\n typeof candidate.b === 'number' &&\n typeof candidate.a === 'number'\n );\n}\n\nconst TRANSFORM_FIELDS = ['x', 'y', 'translateX', 'translateY', 'scaleX', 'scaleY', 'rotation'] as const;\n\nfunction isTransform(value: object): boolean {\n const candidate = value as Record<string, unknown>;\n let seen = 0;\n for (const key of Object.keys(candidate)) {\n if (!(TRANSFORM_FIELDS as readonly string[]).includes(key)) {\n return false;\n }\n if (typeof candidate[key] !== 'number') {\n return false;\n }\n seen++;\n }\n return seen > 0;\n}\n\n/**\n * The interpolator for a pair of values, or undefined when the type\n * cannot be blended.\n *\n * Three types, and the reason the list is short is that the rest are\n * not numbers in disguise. A `UiLength` may be `percent(50)`, `fr(1)`\n * or `auto`, and blending `auto` into `fr(1)` means nothing until the\n * layout engine has resolved both — an animation that took a length\n * would be choosing a frame's resolved value and writing it back as a\n * fixed one, which is a different layout. A colour that names a\n * palette entry (`'controlAccent'`) is not blendable either: it\n * resolves against the node at paint, and this code has a cell rather\n * than a node. Both cases fall back to writing the value, which is\n * the honest outcome — no animation rather than a wrong one.\n */\nexport function interpolatorFor(from: unknown, to: unknown): UiInterpolator<unknown> | undefined {\n if (typeof from === 'number' && typeof to === 'number') {\n return interpolateNumber as unknown as UiInterpolator<unknown>;\n }\n if (typeof from !== 'object' || from === null || typeof to !== 'object' || to === null) {\n return undefined;\n }\n if (isColor(from) && isColor(to)) {\n return interpolateColor as unknown as UiInterpolator<unknown>;\n }\n if (isTransform(from) && isTransform(to)) {\n return interpolateTransform as unknown as UiInterpolator<unknown>;\n }\n return undefined;\n}\n","import { Subject, type Observable } from 'rxjs';\n\nimport { interpolatorFor, type UiInterpolator } from './Interpolate';\nimport { easings, type UiEasing } from './UiEasing';\nimport type { UiSpringSpec } from '../environment/UiMotion';\n\n/**\n * Anything an animation can drive.\n *\n * A cell, not a node: `State<T>` from the framework satisfies this by\n * having a `value` getter and setter, and so does the small adapter a\n * `transition` prop puts in front of a node's property. Keeping the\n * driver on this side of the line is what lets one mechanism serve\n * `animate(cell, …)`, the declarative `transition` prop and the layout\n * modifier without any of them knowing about the others.\n */\nexport interface AnimatedCell<T> {\n value: T;\n}\n\n/**\n * What reduced motion does to one animation.\n *\n * `snap` — the default and the right answer nearly always: the cell\n * jumps to its target and the animation never runs, so the end state\n * is identical and no time passes.\n *\n * `keep` — run anyway, for the animations where the movement *is* the\n * information. A `Spinner` that stops turning is not a calmer spinner,\n * it is a spinner that says work has stopped; WCAG 2.3.3 is about\n * motion triggered by interaction, and a busy indicator is not that.\n * Reach for it only when standing still would state something false.\n */\nexport type UiReducedMotionPolicy = 'snap' | 'keep';\n\nexport interface UiAnimationOptions {\n /**\n * Sample this animation at most this often, in milliseconds. Zero,\n * the default, means every frame.\n *\n * This is what keeps the media tier's promise about the `Spinner`:\n * eight blades have eight positions, so with `stepMs` at an eighth\n * of the turn the runtime wakes eight times a second rather than\n * sixty, and the driver reports it through `nextTickAt` so the\n * scheduler sleeps in between rather than spinning.\n */\n stepMs?: number;\n /** What a reduced-motion preference does to it. Defaults to `snap`. */\n reducedMotion?: UiReducedMotionPolicy;\n /**\n * Wait this many milliseconds before the first sample.\n *\n * The cell is left alone while it waits — not written with its\n * starting value — so a delayed animation costs nothing until it\n * begins, and `nextTickAt` reports the moment it will, which is what\n * keeps the scheduler asleep in between rather than spinning through\n * the delay.\n *\n * This is where staggering comes from: a list that enters with\n * `delay: index * 40` is forty milliseconds of offset per row and no\n * orchestration mechanism at all.\n */\n delay?: number;\n}\n\nexport interface UiTweenOptions extends UiAnimationOptions {\n /** Milliseconds. Zero writes the target and completes without a frame. */\n duration: number;\n easing?: UiEasing;\n /** Restart from the beginning forever. A repeating tween is never done. */\n repeat?: boolean;\n}\n\nexport interface UiSpringOptions extends UiAnimationOptions {\n spring: UiSpringSpec;\n /** Units per second at the start; a gesture hands over the velocity it ended with. */\n velocity?: number;\n /** How close counts as arrived. */\n restDelta?: number;\n}\n\n/**\n * One running animation.\n *\n * Subclasses differ only in `sample`. Everything else — the cell, the\n * observable, when the next sample is due, and not writing a value\n * that has not changed — is the same for a tween and for a spring, and\n * is the part the driver depends on.\n */\n/** The most a sample may be taken early, in milliseconds. */\nconst MAX_DUE_SLACK_MS = 4;\n\nexport abstract class UiAnimation<T> {\n /** When the first tick reached it, on the frame clock. */\n protected startedAt = 0;\n private begun = false;\n private readonly subject = new Subject<T>();\n /**\n * When this animation next *wants* a sample, on the ideal timeline.\n *\n * The ideal one, and that word is carrying the whole fix. This used\n * to be derived at the point of asking, as `lastSampledAt + stepMs`,\n * which sounds equivalent and is not: frames arrive on the display's\n * refreshes, so a sample is nearly always served a little after it\n * was due, and measuring the next step from when it was *served*\n * rounds the period up to a whole refresh and keeps the rounding.\n *\n * The arithmetic is unforgiving. A 24fps clip wants 41.67ms; on a\n * 60Hz display the refresh at 33.3ms is too early and the one at\n * 50ms serves it, so the next step is measured from 50, wants 91.67,\n * and is served at 100. The period is not 41.67ms but 50ms, which is\n * 20fps: one frame in six dropped, for ever, on the commonest clip\n * rate there is. Measured, not reasoned about; see the spec beside\n * this file.\n *\n * Advancing the ideal time by exactly `stepMs` instead lets the\n * served times alternate between 33.3 and 50 and average out to the\n * rate that was asked for.\n */\n private nextDueAt: number | null = null;\n private lastWritten: T | undefined;\n private finished = false;\n\n protected constructor(\n readonly cell: AnimatedCell<T>,\n private readonly stepMs: number,\n readonly reducedMotionPolicy: UiReducedMotionPolicy,\n private readonly delayMs = 0\n ) {}\n\n /**\n * Emits every value written to the cell, and completes when the\n * animation stops driving it — whether it arrived, was superseded by\n * another animation on the same cell, or was stopped. A caller that\n * needs to know which of the three reads the cell.\n */\n get values(): Observable<T> {\n return this.subject.asObservable();\n }\n\n get isFinished(): boolean {\n return this.finished;\n }\n\n /** When this animation next wants a frame. */\n /**\n * How early a sample may be taken and still count as on time.\n *\n * Frames arrive on the display's refreshes, not on demand, so a\n * cadence that does not divide the refresh interval can only ever be\n * served early or late. Insisting on \"not before due\" makes that a\n * catastrophe rather than a rounding error: 59.94fps video has a\n * 16.683ms frame and a 60Hz display refreshes every 16.667ms, so\n * every due time falls 0.016ms after a refresh, every refresh is\n * turned away, and the video plays at half rate.\n *\n * Half a step, capped, because being early by a fraction of your own\n * interval is imperceptible while being early by a whole one would\n * be a dropped frame's worth of drift.\n */\n get dueSlackMs(): number {\n return Math.min(this.stepMs / 2, MAX_DUE_SLACK_MS);\n }\n\n dueAt(now: number): number {\n // Still waiting out its delay: the next frame it has any use for is\n // the one the delay ends on, so the scheduler can sleep until then\n // rather than waking sixty times to decide it is not ready.\n if (this.begun && now - this.startedAt < this.delayMs) {\n return this.startedAt + this.delayMs;\n }\n return this.nextDueAt ?? now;\n }\n\n /** Where it is at `now`, and whether that is the end of it. */\n protected abstract sample(now: number, elapsedMs: number): { value: T; done: boolean };\n\n /**\n * Begins at `now`, on the first frame that advances it.\n *\n * Not at the moment it was asked for: `animate()` is called from a\n * click handler or a reconcile, which run on `performance.now()`,\n * while sampling runs on the frame clock — and under a manual clock\n * in a spec those are different numbers entirely. Taking the start\n * time from the first tick means one clock decides everything, so an\n * animation cannot be born a thousand milliseconds old.\n */\n begin(now: number): void {\n if (this.begun) {\n return;\n }\n this.begun = true;\n this.startedAt = now;\n this.onBegin();\n }\n\n get hasBegun(): boolean {\n return this.begun;\n }\n\n protected onBegin(): void {}\n\n advance(now: number): void {\n if (this.finished) {\n return;\n }\n this.begin(now);\n const elapsed = now - this.startedAt;\n if (elapsed < this.delayMs) {\n // Waiting. The cell is deliberately not written: whoever asked\n // for the delay has already put the cell where it wants it, and\n // writing it again would dirty a node for nothing. The timeline\n // is left unanchored so that the first sample after the delay\n // starts it, rather than it starting somewhere inside the wait.\n return;\n }\n const { value, done } = this.sample(now, elapsed - this.delayMs);\n this.scheduleNext(now);\n // A stepped easing holds one value across many frames, and a\n // spring at rest holds its target: writing an unchanged value\n // would emit through the cell and dirty a node for nothing.\n if (this.lastWritten === undefined || !this.valuesEqual(this.lastWritten, value)) {\n this.lastWritten = value;\n this.cell.value = value;\n this.subject.next(value);\n }\n if (done) {\n this.finish();\n }\n }\n\n /**\n * Moves the ideal timeline on by one step.\n *\n * Resynchronised rather than caught up when a whole step has already\n * gone by: that means the application stalled, or the tab was\n * hidden, and the samples that were missed are not worth taking now.\n * For a video they are frames already past, and presenting is a pure\n * function of a position, so chasing the backlog would sample\n * several times in a row to arrive exactly where a single sample\n * arrives anyway.\n */\n private scheduleNext(now: number): void {\n if (this.stepMs <= 0) {\n // Every frame. There is no timeline to keep.\n this.nextDueAt = now;\n return;\n }\n const next = (this.nextDueAt ?? now) + this.stepMs;\n this.nextDueAt = next > now ? next : now + this.stepMs;\n }\n\n /** Writes the target and completes, for reduced motion and for `duration: 0`. */\n snap(): void {\n if (this.finished) {\n return;\n }\n const value = this.target;\n this.lastWritten = value;\n this.cell.value = value;\n this.subject.next(value);\n this.finish();\n }\n\n /** Stops without reaching the target: superseded, or the node left. */\n cancel(): void {\n this.finish();\n }\n\n protected abstract get target(): T;\n\n protected valuesEqual(a: T, b: T): boolean {\n return Object.is(a, b);\n }\n\n private finish(): void {\n this.finished = true;\n this.subject.complete();\n }\n}\n\n/**\n * A value moving from where it is to where it was told, over a fixed\n * time, along a curve.\n *\n * `from` is read at construction rather than at the first frame, so an\n * animation that starts on the same turn as the write it reacts to\n * blends from the value that was on screen and not from the one that\n * has just replaced it.\n */\nexport class UiTween<T> extends UiAnimation<T> {\n private readonly from: T;\n private readonly to: T;\n private readonly duration: number;\n private readonly easing: UiEasing;\n private readonly repeat: boolean;\n private readonly interpolate: UiInterpolator<T>;\n\n constructor(cell: AnimatedCell<T>, to: T, options: UiTweenOptions, interpolate: UiInterpolator<T>) {\n super(cell, options.stepMs ?? 0, options.reducedMotion ?? 'snap', options.delay ?? 0);\n this.from = cell.value;\n this.to = to;\n this.duration = options.duration;\n this.easing = options.easing ?? easings.standard;\n this.repeat = options.repeat === true;\n this.interpolate = interpolate;\n }\n\n /** Whether it will ever finish on its own. */\n get isRepeating(): boolean {\n return this.repeat;\n }\n\n protected override get target(): T {\n return this.to;\n }\n\n protected override sample(_now: number, elapsedMs: number): { value: T; done: boolean } {\n if (this.duration <= 0) {\n return { value: this.to, done: !this.repeat };\n }\n if (this.repeat) {\n const progress = (elapsedMs % this.duration) / this.duration;\n return { value: this.interpolate(this.from, this.to, this.easing(progress)), done: false };\n }\n const progress = Math.min(1, Math.max(0, elapsedMs / this.duration));\n if (progress >= 1) {\n return { value: this.to, done: true };\n }\n return { value: this.interpolate(this.from, this.to, this.easing(progress)), done: false };\n }\n\n protected override valuesEqual(a: T, b: T): boolean {\n return valuesLookEqual(a, b);\n }\n}\n\n/** Sub-step for the spring integrator, in milliseconds. */\nconst SPRING_STEP_MS = 1000 / 240;\n/**\n * The most simulated time one frame may cover.\n *\n * A stall — a blocked main thread, a backgrounded tab — hands the next\n * frame a delta of seconds. Integrating all of it would either cost a\n * visible pause or, with a stiff spring, blow up; clamping means the\n * spring resumes from where it was, which is what a person sees as the\n * animation simply carrying on.\n */\nconst SPRING_MAX_FRAME_MS = 64;\n\n/**\n * A number pulled towards a target by a spring.\n *\n * Numbers only, deliberately. A spring integrates a position and a\n * velocity, and there is no honest velocity for a colour or for a\n * transform's five fields taken together; the usual dodge — springing\n * a scalar progress and interpolating along it — reintroduces the\n * duration a spring exists to be free of. Two springs on two numbers\n * is what a layout animation uses, and it is the correct model.\n *\n * Integrated at a fixed sub-step rather than with the frame delta, so\n * the same animation reaches the same values whether it was sampled at\n * 60 Hz or at 30, and so a spec can assert them.\n */\nexport class UiSpring extends UiAnimation<number> {\n private position: number;\n /** The position one sub-step back, for the interpolation below. */\n private previousPosition: number;\n private velocity: number;\n private readonly to: number;\n private readonly spec: UiSpringSpec;\n private readonly restDelta: number;\n private simulatedTo: number;\n\n constructor(cell: AnimatedCell<number>, to: number, options: UiSpringOptions) {\n super(cell, options.stepMs ?? 0, options.reducedMotion ?? 'snap', options.delay ?? 0);\n this.position = cell.value;\n this.previousPosition = this.position;\n this.velocity = options.velocity ?? 0;\n this.to = to;\n this.spec = options.spring;\n this.restDelta = options.restDelta ?? 0.01;\n this.simulatedTo = 0;\n }\n\n /** Where it is going, for a retarget that wants to know. */\n get destination(): number {\n return this.to;\n }\n\n /** Units per second right now, so a retarget can carry it over. */\n get currentVelocity(): number {\n return this.velocity;\n }\n\n protected override onBegin(): void {\n this.simulatedTo = 0;\n }\n\n protected override get target(): number {\n return this.to;\n }\n\n protected override sample(_now: number, elapsedMs: number): { value: number; done: boolean } {\n const until = Math.min(elapsedMs, this.simulatedTo + SPRING_MAX_FRAME_MS);\n const { stiffness, damping, mass } = this.spec;\n const seconds = SPRING_STEP_MS / 1000;\n // Whole sub-steps only, with the remainder carried into the next\n // frame. Integrating a partial step to land exactly on the frame's\n // time would make the result depend on the cadence, which is the\n // one thing this integrator exists to avoid — the spec asserts\n // that 60 Hz and 30 Hz agree to six places.\n while (this.simulatedTo + SPRING_STEP_MS <= until) {\n this.previousPosition = this.position;\n const acceleration = (-stiffness * (this.position - this.to) - damping * this.velocity) / mass;\n this.velocity += acceleration * seconds;\n this.position += this.velocity * seconds;\n this.simulatedTo += SPRING_STEP_MS;\n }\n // Rest is both halves: near the target *and* slow. A spring passing\n // through its target at speed is not finished.\n if (Math.abs(this.position - this.to) < this.restDelta && Math.abs(this.velocity) < this.restDelta * 10) {\n this.position = this.to;\n this.previousPosition = this.to;\n this.velocity = 0;\n return { value: this.to, done: true };\n }\n // Reported between the last two sub-steps, by however much of one\n // is left over.\n //\n // The simulation runs on whole steps and must: a partial step would\n // make the *trajectory* depend on the cadence, which is what this\n // integrator exists to avoid. But reporting the whole-step position\n // makes the value it hands out depend on the cadence in a different\n // and more visible way. A sub-step is 1/240s, so a 60Hz frame is\n // exactly four of them and every frame advances the same amount —\n // while a 165Hz frame is 1.4545 of them, so the loop above runs one\n // step, then one, then two, and the element moves by twice as much\n // on every third frame. That reads as judder on exactly the\n // displays fast enough to show it, which is the opposite of what a\n // high refresh rate is for.\n //\n // Interpolating the *output* keeps both: the simulation is\n // untouched and still agrees to six places across cadences, and the\n // value handed out advances by an equal amount every frame. It\n // trails the simulation by up to one sub-step, which is 4ms.\n const alpha = (until - this.simulatedTo) / SPRING_STEP_MS;\n return { value: this.previousPosition + (this.position - this.previousPosition) * alpha, done: false };\n }\n}\n\n/**\n * Whether two interpolated values are the same to a writer.\n *\n * Object.is is wrong for the compound ones: every sample of a\n * transform or a colour is a fresh object, so an identity comparison\n * would report a change on every frame of a stepped animation and undo\n * the reason `stepMs` exists.\n */\nfunction valuesLookEqual(a: unknown, b: unknown): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {\n return false;\n }\n const keys = Object.keys(a as object);\n if (keys.length !== Object.keys(b as object).length) {\n return false;\n }\n for (const key of keys) {\n const left = (a as Record<string, unknown>)[key];\n const right = (b as Record<string, unknown>)[key];\n if (typeof left === 'number' && typeof right === 'number') {\n if (Math.abs(left - right) > 1e-6) {\n return false;\n }\n continue;\n }\n if (!Object.is(left, right)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Builds a tween, or undefined when the two values cannot be blended.\n *\n * Undefined is a real answer and not a failure: see `interpolatorFor`\n * for the two cases (typed lengths, palette names) where the honest\n * thing is to write the value rather than to invent a midpoint.\n */\nexport function createTween<T>(cell: AnimatedCell<T>, to: T, options: UiTweenOptions): UiTween<T> | undefined {\n const interpolate = interpolatorFor(cell.value, to) as UiInterpolator<T> | undefined;\n if (interpolate === undefined) {\n return undefined;\n }\n return new UiTween(cell, to, options, interpolate);\n}\n","import { easings, type UiEasing } from '../animation/UiEasing';\n\n/**\n * The named durations a component may ask for, in milliseconds.\n *\n * Named rather than numeric for the reason a colour is named: a\n * library whose components each pick their own 180 or 240 has no feel,\n * it has forty opinions. the rule for colour —\n * tokens only, never a literal in a component — applies to time.\n */\nexport type UiDurationToken = 'instant' | 'fast' | 'normal' | 'slow' | 'deliberate';\n\n/** The named curves; see `UiEasing.ts` for what each one is for. */\nexport type UiEasingToken = keyof typeof easings;\n\n/** The named springs, for a movement that follows a gesture. */\nexport type UiSpringToken = 'gentle' | 'snappy' | 'stiff';\n\n/**\n * A spring, in the physical sense: `stiffness` pulls towards the\n * target, `damping` resists velocity, `mass` resists both.\n *\n * Critical damping is `2 · sqrt(stiffness · mass)`; every preset below\n * sits a little under it, so a spring settles with one small overshoot\n * rather than creeping in. A spring has no duration, which is the\n * whole point of using one for a gesture: it starts from whatever\n * velocity the finger left behind.\n */\nexport interface UiSpringSpec {\n readonly stiffness: number;\n readonly damping: number;\n readonly mass: number;\n}\n\n/**\n * A vocabulary of motion, beside the palette and the type scale.\n *\n * It is deliberately **not** a field on `UiTheme`. A theme value is\n * resolved per node — `resolveColorValue` asks what the node inherits\n * — and an animation drives a *cell*, which has no node, so a motion\n * token on the theme would be a token nothing could resolve. That is\n * the mistake `visualState` made: a value\n * shipped where nothing reads it. An application that wants a\n * different feel installs one with `AnimationService.setMotion`, which\n * is per runtime, as everything else in front of the graph is.\n */\nexport interface UiMotion {\n readonly durations: Readonly<Record<UiDurationToken, number>>;\n readonly easings: Readonly<Record<UiEasingToken, UiEasing>>;\n readonly springs: Readonly<Record<UiSpringToken, UiSpringSpec>>;\n}\n\nexport const defaultMotion: UiMotion = {\n durations: {\n /** No motion. What `reducedMotion` turns everything else into. */\n instant: 0,\n /** A control acknowledging a press; too short to watch. */\n fast: 120,\n /** The default: something appearing, moving or changing colour. */\n normal: 200,\n /** A panel or a dialog, which is bigger and travels further. */\n slow: 320,\n /** A movement meant to be followed by the eye. Rare. */\n deliberate: 500\n },\n easings,\n springs: {\n gentle: { stiffness: 120, damping: 20, mass: 1 },\n snappy: { stiffness: 220, damping: 24, mass: 1 },\n stiff: { stiffness: 400, damping: 32, mass: 1 }\n }\n} as const;\n","import { defaultMotion, type UiSpringSpec, type UiSpringToken } from '../environment/UiMotion';\nimport type { UiEasing } from './UiEasing';\nimport type { UiReducedMotionPolicy } from './UiAnimation';\n\n/**\n * How one property gets from its old value to its new one, when the\n * element declares a `transition`.\n */\nexport type UiTransitionSpec =\n | {\n readonly kind: 'tween';\n readonly duration: number;\n readonly easing?: UiEasing;\n readonly stepMs?: number;\n readonly reducedMotion?: UiReducedMotionPolicy;\n }\n | {\n readonly kind: 'spring';\n readonly spring: UiSpringSpec;\n readonly restDelta?: number;\n readonly reducedMotion?: UiReducedMotionPolicy;\n };\n\n/**\n * What an element may write for one property of a `transition`.\n *\n * A bare number is a duration in milliseconds, which is what makes\n * `transition: { opacity: 200, transform: spring() }` read the way\n * That is how it was written.\n */\nexport type UiTransitionValue = number | UiTransitionSpec;\n\n/** A tween of `duration` milliseconds along `easing`. */\nexport function tween(\n duration: number,\n options: { easing?: UiEasing; stepMs?: number; reducedMotion?: UiReducedMotionPolicy } = {}\n): UiTransitionSpec {\n return { kind: 'tween', duration, ...options };\n}\n\n/**\n * A spring, named from the motion vocabulary or given outright.\n *\n * Springs apply to numbers only; see `UiSpring` for why springing a\n * colour or a whole transform is not a thing this offers.\n */\nexport function spring(\n spec: UiSpringToken | UiSpringSpec = 'snappy',\n options: { restDelta?: number; reducedMotion?: UiReducedMotionPolicy } = {}\n): UiTransitionSpec {\n return {\n kind: 'spring',\n spring: typeof spec === 'string' ? defaultMotion.springs[spec] : spec,\n ...options\n };\n}\n\nexport function normalizeTransition(value: UiTransitionValue): UiTransitionSpec {\n return typeof value === 'number' ? { kind: 'tween', duration: value } : value;\n}\n\n/**\n * Rejects a `transition` prop that is not a map of property names to\n * durations or specs, naming the node the way an unknown prop does.\n */\nexport function assertTransitionMap(\n nodeId: string,\n value: unknown,\n isKnownProperty: (name: string) => boolean\n): ReadonlyMap<string, UiTransitionSpec> {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error(`The 'transition' prop on node '${nodeId}' must be an object, got ${describe(value)}.`);\n }\n const specs = new Map<string, UiTransitionSpec>();\n for (const [property, entry] of Object.entries(value as Record<string, unknown>)) {\n if (!isKnownProperty(property)) {\n throw new Error(\n `The 'transition' prop on node '${nodeId}' names '${property}', which is not a UI property. ` +\n `A transition names the properties it animates, so an unknown one is a typo the same way an unknown prop is.`\n );\n }\n if (typeof entry === 'number') {\n if (!Number.isFinite(entry) || entry < 0) {\n throw new Error(\n `The 'transition' for '${property}' on node '${nodeId}' must be a duration in ms, got ${entry}.`\n );\n }\n specs.set(property, { kind: 'tween', duration: entry });\n continue;\n }\n if (\n typeof entry === 'object' &&\n entry !== null &&\n ((entry as UiTransitionSpec).kind === 'tween' || (entry as UiTransitionSpec).kind === 'spring')\n ) {\n specs.set(property, entry as UiTransitionSpec);\n continue;\n }\n throw new Error(\n `The 'transition' for '${property}' on node '${nodeId}' must be a duration in milliseconds or a spec ` +\n `built by tween() or spring(), got ${describe(entry)}.`\n );\n }\n return specs;\n}\n\nfunction describe(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n return Array.isArray(value) ? 'an array' : `a ${typeof value}`;\n}\n","import type { Observable } from 'rxjs';\n\nimport type { UiAnimation, AnimatedCell } from './UiAnimation';\n\n/** An animation of some value, with the value's type erased. */\ntype AnyAnimation = UiAnimation<unknown>;\n\nfunction erase<T>(animation: UiAnimation<T>): AnyAnimation {\n return animation as unknown as AnyAnimation;\n}\n\n/**\n * The running animations of one runtime, and the thing the `ticks`\n * phase advances.\n *\n * **One animation per cell.** Starting a second one on a cell\n * supersedes the first, which completes without reaching its target.\n * That rule is not a simplification: it is what makes a gesture work\n * — every retarget while a finger moves is a new animation on the same\n * cell — and it removes the whole question of what two animations\n * writing one value would mean.\n *\n * The driver is per runtime for the reason `MediaService` is: the\n * playground runs several runtimes in one worker, and a module-level\n * set of animations would tick a disposed runtime's cells.\n */\nexport class AnimationDriver {\n private readonly running = new Map<object, AnyAnimation>();\n private reducedMotion = false;\n private hidden = false;\n private wake: (() => void) | null = null;\n\n /**\n * Called when an animation enters the running set on an idle driver.\n *\n * The runtime arms a frame here. Without it an animation started\n * from a click handler that changes nothing else would never get a\n * first frame: the scheduler arms only on dirt, the animation's dirt\n * is made inside a frame, and no frame would ever come. Re-arming\n * *between* frames is `scheduleAnimationTick`'s job; this is only\n * the first one.\n */\n setWakeListener(listener: (() => void) | null): void {\n this.wake = listener;\n }\n\n /** Whether anything is running, so the `ticks` phase can report 0. */\n get isRunning(): boolean {\n return this.running.size > 0;\n }\n\n get size(): number {\n return this.running.size;\n }\n\n /** Whether a person has asked for less motion; see `UiReducedMotionPolicy`. */\n get isReducedMotion(): boolean {\n return this.reducedMotion;\n }\n\n /**\n * Whether an animation should be put where it is going instead of\n * being run there.\n *\n * Two questions with one answer: a person who has asked for less\n * motion and a page nobody is looking at both want the end state and\n * not the journey to it, and in both cases `keep` is the way an\n * animation says its movement is the information rather than the\n * decoration.\n */\n private lands(policy: AnyAnimation['reducedMotionPolicy']): boolean {\n return policy === 'snap' && (this.reducedMotion || this.hidden);\n }\n\n /**\n * Starts an animation, replacing whatever was driving its cell.\n *\n * Returns the values it will write. Under reduced motion, or while\n * the document is hidden, an animation whose policy is `snap` never\n * enters the set at all: the cell takes its target here, in the\n * caller's turn, and the returned observable has already completed.\n * So a reduced-motion app runs no animation frames, rather than\n * running them and drawing the same thing sixty times.\n */\n start<T>(animation: UiAnimation<T>): Observable<T> {\n const cell = animation.cell as object;\n this.running.get(cell)?.cancel();\n this.running.delete(cell);\n if (this.lands(animation.reducedMotionPolicy)) {\n animation.snap();\n return animation.values;\n }\n const wasIdle = this.running.size === 0;\n this.running.set(cell, erase(animation));\n if (wasIdle) {\n this.wake?.();\n }\n return animation.values;\n }\n\n /** The animation currently driving a cell, if any. */\n animationFor<T>(cell: AnimatedCell<T>): UiAnimation<T> | undefined {\n return this.running.get(cell as object) as unknown as UiAnimation<T> | undefined;\n }\n\n /**\n * Stops whatever is driving a cell, leaving it where it stands.\n *\n * This is how a node leaving the tree releases its animations, which\n * is the leak the media tier names for image bitmaps and which\n * matters here too: a driver holding a cell holds every closure the\n * component that made it captured.\n */\n stop<T>(cell: AnimatedCell<T>): boolean {\n const animation = this.running.get(cell as object);\n if (animation === undefined) {\n return false;\n }\n this.running.delete(cell as object);\n animation.cancel();\n return true;\n }\n\n stopAll(): void {\n const animations = [...this.running.values()];\n this.running.clear();\n for (const animation of animations) {\n animation.cancel();\n }\n }\n\n /**\n * A person's motion preference changed.\n *\n * Turning it on finishes everything already in flight that is\n * willing to be finished, rather than leaving a dialog stuck\n * half-faded until the animation it was told not to run runs out.\n */\n setReducedMotion(reduced: boolean): void {\n if (this.reducedMotion === reduced) {\n return;\n }\n this.reducedMotion = reduced;\n if (!reduced) {\n return;\n }\n this.landRunning();\n }\n\n /**\n * The document went out of sight, or came back.\n *\n * A hidden page still paints. The runtime deliberately keeps giving\n * frames to a change that genuinely happened, so the canvas holds a\n * correct picture rather than whatever was on it when the tab went\n * away, and a route loaded hidden is drawn once its images have\n * decoded rather than being left half empty. What it does not do is\n * advance animations, because frames spent watching something move\n * that nobody can see are frames spent for nothing.\n *\n * Those two together leave an animation frozen at whatever value it\n * had reached, and painted there. For most animations that is\n * harmless: a colour halfway between two colours is still a colour.\n * For an **entrance** it is not, because an entrance begins at\n * `opacity: 0` and its frozen first value is an element that is not\n * there. Segue's track page, opened in a background tab, held sixty\n * two pixels of empty page where its play button belonged, and every\n * screen built from `motion({ initial })` did the same. It came back\n * the moment the tab did, which is exactly why it reads as a\n * rendering bug rather than as a tab that is not being drawn.\n *\n * So a hidden page lands its animations instead of freezing them, in\n * the same way and for the same reason a reduced-motion preference\n * does: what an animation has to say to nobody is nothing, and the\n * state it was going to end at is the one the page should be holding\n * while it waits to be looked at. An animation whose movement is the\n * information (`keep`) is left alone either way, so a spinner is\n * still a spinner when the tab comes back.\n */\n setHidden(hidden: boolean): void {\n if (this.hidden === hidden) {\n return;\n }\n this.hidden = hidden;\n if (!hidden) {\n return;\n }\n this.landRunning();\n }\n\n /** Puts everything that may be landed where it was going. */\n private landRunning(): void {\n // Deleting the current entry while iterating a Map is defined and\n // does not skip the next one.\n for (const [cell, animation] of this.running) {\n if (this.lands(animation.reducedMotionPolicy)) {\n this.running.delete(cell);\n animation.snap();\n }\n }\n }\n\n /**\n * When the earliest animation next wants a frame, or undefined when\n * nothing is running.\n *\n * This is the whole answer to \"how does an animation keep frames\n * coming on an idle app\". The runtime asks after every frame: an\n * answer at or before `now` means arm the next frame, a later one\n * means a timer, and undefined means arm nothing — which is why an\n * idle app's profile reads `ticks 0.00` for the strong reason (no\n * frames at all) rather than the weak one (frames that did nothing).\n */\n nextTickAt(now: number): number | undefined {\n let earliest: number | undefined;\n for (const animation of this.running.values()) {\n // Minus its slack, because this is the earliest moment a frame\n // would be *useful*, not the exact moment it is owed. Frames\n // arrive on the display's refreshes: a caller that waits for the\n // exact due time asks after the refresh that could have served\n // it and waits for the next one, which is how 59.94fps video on\n // a 60Hz display ends up playing at thirty. Asking a slack early\n // means the refresh at or just before due is the one that\n // serves it, and `advance` accepts it for the same reason.\n const due = animation.dueAt(now) - animation.dueSlackMs;\n if (earliest === undefined || due < earliest) {\n earliest = due;\n }\n }\n return earliest;\n }\n\n /**\n * Advances every animation whose next sample is due, writing each\n * one's cell. This is the `ticks` phase.\n *\n * It runs before the dirty set is snapshotted for exactly the reason\n * `patches` and `environment` do: the writes it makes have to belong\n * to the frame about to be collected, or a frame would draw the\n * previous tick's values and every animation would run one frame\n * behind.\n */\n advance(now: number): void {\n for (const [cell, animation] of this.running) {\n // Early by less than its own slack counts as on time — the same\n // allowance `nextTickAt` asks the scheduler for, and for the\n // same reason. See `UiAnimation.dueSlackMs`.\n if (animation.dueAt(now) - animation.dueSlackMs > now) {\n continue;\n }\n animation.advance(now);\n if (animation.isFinished && this.running.get(cell) === animation) {\n this.running.delete(cell);\n }\n }\n }\n}\n","import type { LayoutBox } from '../layout/LayoutTypes';\nimport type { UiNode } from '../graph/UiNode';\n\n/**\n * What an arriving element is told when it takes a name: where the\n * element it is replacing was standing, and how to make that element\n * step aside once the replacement is actually in place.\n */\nexport interface SharedClaim {\n /** The previous holder's last box, or null when nothing held the name. */\n readonly box: LayoutBox | null;\n /** Hides the previous holder. Idempotent, and a no-op when there was none. */\n yieldPrevious(): void;\n}\n\n/** What one name's current holder is, and where it last was. */\ninterface Holder {\n readonly node: UiNode;\n /** Its box as of the last layout that moved it. Null before the first. */\n box: LayoutBox | null;\n /** Told to get out of the way when another node takes the name. */\n yield: () => void;\n /** Told to come back, if this one outlives whatever displaced it. */\n restore: () => void;\n /**\n * Whoever held the name before this one and is still alive.\n *\n * Kept so the name can be handed back. Stepping aside is otherwise a\n * one-way door: an element that yields is hidden outright, and if the\n * element it yielded to leaves without anything replacing it, the\n * only copy left is an invisible one.\n *\n * That is not a corner case. A route change keeps the departing\n * screen alive for the length of its exit, so pressing Back part-way\n * through returns to a screen whose element has already yielded.\n */\n displaced: Holder | null;\n}\n\n/**\n * Who currently owns each shared-element name, and where they were.\n *\n * This is the whole of the shared-element mechanism's memory, and it\n * is deliberately tiny: a name, the node holding it, and that node's\n * last box. Everything the transition looks like is the modifier's\n * business; this only answers \"was something else called that a moment\n * ago, and where was it?\".\n *\n * **Why a registry and not a diff of two trees.** The browser's View\n * Transitions API snapshots the old document, applies the change, and\n * animates pseudo-elements between two rasters, because the DOM it\n * started with is gone by the time it can measure. Gesso has a\n * retained graph: both nodes are real, live, and measurable at the\n * same moment, so the transition can be FLIP on the real thing —\n * interruptible, continuous, and free of the snapshot.\n *\n * **Why it is order-independent.** `UiGraphBuilder.reconcile` creates\n * the new children of a parent before it removes the unmatched old\n * ones, so a naive \"the leaving node hands over to the arriving one\"\n * would be handing over in the wrong direction. Keying by name instead\n * means the arriving node asks *whoever holds this name* where it is,\n * and it does not matter at all whether that node has left yet.\n *\n * Per runtime, for the same reason `AnimationDriver` and the media\n * caches are: the playground runs several runtimes in one worker, and\n * a module-level registry would let one runtime's card hand its box to\n * another runtime's page.\n */\nexport class UiSharedElements {\n private readonly holders = new Map<string, Holder>();\n\n /**\n * Takes a name for a node, and answers where the previous holder is\n * standing — and how to tell it to get out of the way.\n *\n * **The yield is handed back rather than performed.** The previous\n * holder does have to disappear, because the arriving node is about\n * to be drawn over exactly where it stands and two copies of one\n * thing is a double image rather than a transition; the CSS version\n * of this effect says the same thing as\n * `::view-transition-old(name) { display: none }`.\n *\n * But it must not disappear *yet*. A claim happens during\n * reconciliation, and the arriving node has no box until the frame\n * lays out — so yielding here leaves a window in which neither\n * element is drawn. For a transform morph that window is closed\n * within the same frame; for a geometry morph, which cannot write a\n * box after layout has run, it is a whole frame long, and a whole\n * frame with neither the old card nor the new one is a flash of the\n * page behind them. It was exactly that visible.\n *\n * So the caller yields the previous holder at the moment it actually\n * takes its place. Until then both are on screen, in the same\n * position, which is indistinguishable from one.\n *\n * A null box is the ordinary case: an element that is simply\n * appearing has nothing to morph from and should enter instead.\n */\n claim(name: string, node: UiNode, onYield: () => void, onRestore: () => void = () => {}): SharedClaim {\n const previous = this.holders.get(name);\n const displaced = previous === undefined || previous.node === node ? null : previous;\n this.holders.set(name, { node, box: null, yield: onYield, restore: onRestore, displaced });\n if (previous === undefined || displaced === null) {\n return { box: previous?.box ?? null, yieldPrevious: () => {} };\n }\n let yielded = false;\n return {\n box: previous.box,\n yieldPrevious: () => {\n if (!yielded) {\n yielded = true;\n previous.yield();\n }\n }\n };\n }\n\n /** Records where a node is now, so whoever takes the name next knows. */\n report(name: string, node: UiNode, box: LayoutBox): void {\n const holder = this.holders.get(name);\n if (holder === undefined || holder.node !== node) {\n return;\n }\n holder.box = box;\n }\n\n /**\n * Gives up a name, if this node still holds it.\n *\n * Guarded on identity because detach order is not the reverse of\n * attach order across a tree change: the leaving node's modifier\n * detaches *after* the arriving one has already claimed the name,\n * and an unguarded release would delete the arriving node's entry.\n */\n release(name: string, node: UiNode): void {\n const holder = this.holders.get(name);\n if (holder === undefined) {\n return;\n }\n if (holder.node !== node) {\n // Something further back in the chain has gone. Forget it, so it\n // is never brought back and never measured: a removed node's last\n // box is a rectangle nothing occupies, and morphing from it is\n // worse than not morphing at all.\n for (let entry: Holder = holder; entry.displaced !== null; entry = entry.displaced) {\n if (entry.displaced.node === node) {\n entry.displaced = entry.displaced.displaced;\n break;\n }\n }\n return;\n }\n // The holder itself is going. If it displaced someone who is still\n // here, that one takes the name back and stops being hidden.\n const displaced = holder.displaced;\n if (displaced === null) {\n this.holders.delete(name);\n return;\n }\n this.holders.set(name, displaced);\n displaced.restore();\n }\n\n /** The names currently held, for specs and the inspector. */\n get names(): readonly string[] {\n return [...this.holders.keys()];\n }\n\n clear(): void {\n this.holders.clear();\n }\n}\n","import type { UiEasing } from './UiEasing';\nimport type { UiReducedMotionPolicy } from './UiAnimation';\nimport type { UiDurationToken, UiEasingToken, UiSpringSpec, UiSpringToken } from '../environment/UiMotion';\n\n/**\n * Where an element is, visually, relative to where the layout put it.\n *\n * Six numbers and nothing else, chosen because they are exactly the\n * ones that cost no layout: opacity, a translation, a scale per axis\n * and a rotation all resolve inside `PaintState`, so an element moving\n * through them marks Paint and the layout engine never runs. Anything\n * that would move the box — a width, a margin, a gap — is deliberately\n * not here; it belongs to the layout, and animating it is what the\n * declarative `transition` prop is for.\n *\n * Every field is an *offset from rest*. Omitting one means \"leave it\n * where it belongs\", which is what lets `{ opacity: 0 }` be a complete\n * description of a fade without also asserting a scale of 1.\n */\nexport interface MotionState {\n readonly opacity?: number;\n /** Logical pixels right of where the layout put it. */\n readonly x?: number;\n /** Logical pixels below where the layout put it. */\n readonly y?: number;\n /** Both axes at once; `scaleX`/`scaleY` override it per axis. */\n readonly scale?: number;\n readonly scaleX?: number;\n readonly scaleY?: number;\n /** Radians, about the node's centre. */\n readonly rotate?: number;\n}\n\n/**\n * One state, or several to be laid over each other left to right.\n *\n * `[fade, slideUp(16)]` is the idiom, and it reads as what it does.\n * An array rather than a `fade.and(...)` builder because a state is\n * data — a component may compute one, store one, or receive one as a\n * prop — and a plain object stays comparable, serialisable and\n * inspectable in a way a builder object does not.\n */\nexport type MotionStateInput = MotionState | readonly MotionState[];\n\n/** The six channels a motion state drives, all resolved. */\nexport interface ResolvedMotionState {\n readonly opacity: number;\n readonly x: number;\n readonly y: number;\n readonly scaleX: number;\n readonly scaleY: number;\n readonly rotate: number;\n}\n\n/** Where an element sits when nothing is animating it. */\nexport const MOTION_REST: ResolvedMotionState = {\n opacity: 1,\n x: 0,\n y: 0,\n scaleX: 1,\n scaleY: 1,\n rotate: 0\n};\n\n/** The channel names, for anything that has to walk them. */\nexport const MOTION_CHANNELS = ['opacity', 'x', 'y', 'scaleX', 'scaleY', 'rotate'] as const;\n\nexport type MotionChannel = (typeof MOTION_CHANNELS)[number];\n\n/**\n * Flattens a state (or a stack of them) onto rest.\n *\n * `scale` is expanded here rather than carried through, so everything\n * downstream deals in two independent axes and a shared-element morph\n * — which genuinely needs different numbers per axis — is not a\n * special case.\n */\nexport function resolveMotionState(input: MotionStateInput | null | undefined): ResolvedMotionState {\n if (input === null || input === undefined) {\n return MOTION_REST;\n }\n const states = Array.isArray(input) ? (input as readonly MotionState[]) : [input as MotionState];\n let opacity = MOTION_REST.opacity;\n let x = MOTION_REST.x;\n let y = MOTION_REST.y;\n let scaleX = MOTION_REST.scaleX;\n let scaleY = MOTION_REST.scaleY;\n let rotate = MOTION_REST.rotate;\n for (const state of states) {\n if (state === null || state === undefined) {\n continue;\n }\n opacity = state.opacity ?? opacity;\n x = state.x ?? x;\n y = state.y ?? y;\n // Per-axis wins over the shorthand, whichever order they appear in\n // the same object, because they are read in that order here.\n const uniform = state.scale;\n if (uniform !== undefined) {\n scaleX = uniform;\n scaleY = uniform;\n }\n scaleX = state.scaleX ?? scaleX;\n scaleY = state.scaleY ?? scaleY;\n rotate = state.rotate ?? rotate;\n }\n return { opacity, x, y, scaleX, scaleY, rotate };\n}\n\n/** Whether a resolved state is rest, to a pixel nobody can see. */\nexport function isMotionRest(state: ResolvedMotionState): boolean {\n return (\n Math.abs(state.opacity - 1) < 1e-4 &&\n Math.abs(state.x) < 0.01 &&\n Math.abs(state.y) < 0.01 &&\n Math.abs(state.scaleX - 1) < 1e-4 &&\n Math.abs(state.scaleY - 1) < 1e-4 &&\n Math.abs(state.rotate) < 1e-5\n );\n}\n\n// ---------------------------------------------------------------------------\n// How a state is reached\n// ---------------------------------------------------------------------------\n\n/**\n * How long a motion takes and what shape it has.\n *\n * A tween or a spring, never both: naming a `spring` and a `duration`\n * together is a contradiction, since a spring's whole point is that it\n * has no duration. The spring wins if both are given, and that is the\n * only reason this is not two types.\n */\nexport interface MotionTiming {\n /** Milliseconds, or a name from the motion vocabulary. Defaults to `normal`. */\n readonly duration?: number | UiDurationToken;\n /** A curve, or a name from the vocabulary. Defaults to `standard`. */\n readonly easing?: UiEasing | UiEasingToken;\n /** Use a spring instead of a tween. Named, or given outright. */\n readonly spring?: UiSpringToken | UiSpringSpec;\n /** Wait this long before starting; see `UiAnimationOptions.delay`. */\n readonly delay?: number;\n /** What a reduced-motion preference does to it. Defaults to `snap`. */\n readonly reducedMotion?: UiReducedMotionPolicy;\n}\n\n// ---------------------------------------------------------------------------\n// The presets\n// ---------------------------------------------------------------------------\n\n/**\n * Invisible. The state almost every enter and exit contains, and on\n * its own the most common animation there is.\n */\nexport const fade: MotionState = { opacity: 0 };\n\n/** Comes up from `distance` pixels below. */\nexport function slideUp(distance = 16): MotionState {\n return { y: distance };\n}\n\n/** Comes down from `distance` pixels above. */\nexport function slideDown(distance = 16): MotionState {\n return { y: -distance };\n}\n\n/**\n * Comes in from one edge.\n *\n * The edge names where it starts, which is the way a person describes\n * it — \"it slides in from the left\" — rather than which way it moves.\n */\nexport function slideFrom(edge: 'left' | 'right' | 'top' | 'bottom', distance = 24): MotionState {\n switch (edge) {\n case 'left':\n return { x: -distance };\n case 'right':\n return { x: distance };\n case 'top':\n return { y: -distance };\n case 'bottom':\n return { y: distance };\n }\n}\n\n/** Grows (or shrinks) into place from `scale`. */\nexport function scaleFrom(scale = 0.92): MotionState {\n return { scale };\n}\n\n/** Turned by `radians`. */\nexport function rotateFrom(radians: number): MotionState {\n return { rotate: radians };\n}\n\n/**\n * Small and invisible: what a dialog, a menu or a toast appears from.\n *\n * A named preset rather than `[fade, scaleFrom(0.9)]` written out\n * everywhere, for the reason the durations are named: a library whose\n * overlays each pick their own 0.9 or 0.94 has no feel.\n */\nexport function pop(scale = 0.9): MotionState {\n return { scale, opacity: 0 };\n}\n","import type { Observable, Subscription } from 'rxjs';\n\nimport { DirtyFlags } from '../graph/DirtyFlags';\nimport type { NodeId } from '../graph/UiNode';\nimport type { UiGraph } from '../graph/UiGraph';\nimport type { UiGraphBuilder } from '../composition/UiGraphBuilder';\nimport type { UiChild } from '../composition/UiElement';\n\nexport type ChildrenBindingId = number;\n\n/**\n * Binds an observable stream of child definitions to a fragment anchor.\n *\n * When the observable emits, the emitted UiElement(s) are reconciled\n * into the fragment's children. The fragment itself is invisible to\n * layout, rendering, and hit-testing; it exists only as a stable anchor\n * in the retained graph.\n */\nexport class UiChildrenBinding {\n private subscription: Subscription | null = null;\n\n constructor(\n public readonly id: ChildrenBindingId,\n public readonly parentId: NodeId,\n public readonly fragmentId: NodeId,\n public readonly observable: Observable<UiChild | readonly UiChild[]>,\n private readonly graph: UiGraph,\n private readonly builder: UiGraphBuilder\n ) {}\n\n /**\n * Start listening to the observable.\n */\n connect(): void {\n if (this.subscription !== null) {\n throw new Error(`Children binding ${this.id} is already connected.`);\n }\n this.subscription = this.observable.subscribe({\n next: value => this.handleValue(value),\n error: error => {\n console.error(`Children binding ${this.id} failed (${this.parentId} → ${this.fragmentId})`, error);\n this.disconnect();\n }\n });\n }\n\n /**\n * Stop listening and detach the subscription.\n */\n disconnect(): void {\n this.subscription?.unsubscribe();\n this.subscription = null;\n }\n\n private handleValue(value: UiChild | readonly UiChild[]): void {\n const definitions = Array.isArray(value) ? (value as readonly UiChild[]) : [value as UiChild];\n const fragment = this.graph.requireNode(this.fragmentId);\n this.builder.reconcileChildren(fragment, definitions);\n const parent = this.graph.requireNode(this.parentId);\n this.graph.markDirty(parent, DirtyFlags.Children);\n }\n}\n","import type { UiNode } from '../graph/UiNode';\nimport type { UiEventListener, UiInputDispatcher } from '../input/UiInputDispatcher';\nimport type { UiEventType } from '../input/UiInputEvent';\n\n/**\n * Binds a declarative `on*` prop to a listener on the input dispatcher.\n *\n * Event bindings sit alongside UiBinding (observable properties) and\n * UiChildrenBinding (observable children) so that all three are torn\n * down by the same subtree-removal path. A handler can therefore never\n * outlive the node it was declared on.\n */\nexport class UiEventBinding {\n constructor(\n public readonly node: UiNode,\n public readonly type: UiEventType,\n public readonly listener: UiEventListener,\n private readonly dispatcher: UiInputDispatcher\n ) {}\n\n connect(): void {\n this.dispatcher.addEventListener(this.node, this.type, this.listener);\n }\n\n disconnect(): void {\n this.dispatcher.removeEventListener(this.node, this.type, this.listener);\n }\n}\n","import type { Observable } from 'rxjs';\n\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { UiProps } from './UiProps';\n\n/**\n * Opaque marker for component definitions.\n *\n * The runtime does not know how to mount components; it only\n * recognizes this shape so factory functions can accept components\n * as children. The framework layer resolves ComponentLikeElements\n * into plain UiElements before passing them to UiGraphBuilder.\n */\nexport interface ComponentLikeElement {\n readonly kind: 'component';\n readonly tag: string;\n /** The class or function to mount; compared by identity across reconciles. */\n readonly component: Function;\n readonly props: Record<string, unknown>;\n readonly key?: string | number;\n}\n\n/**\n * A child definition may be a static element, a component definition,\n * or an observable stream of either (or of lists of them). Observable\n * children are reconciled dynamically by the framework without\n * re-rendering the parent component; components inside them are\n * mounted by the framework's resolver as the builder reaches them.\n */\nexport type UiChild = UiElement | ComponentLikeElement | Observable<UiChild | readonly UiChild[]>;\n\n/**\n * Declarative representation of a UI element.\n *\n * UiElement is NOT a runtime UiNode.\n *\n * It describes what the UI should look like.\n * UiGraphBuilder is responsible for turning this definition\n * into the runtime UiNode tree.\n */\nexport interface UiElement {\n /**\n * Runtime node type that should be created.\n */\n readonly type: UiNodeType;\n\n /**\n * Properties belonging to this element.\n */\n readonly props: UiProps;\n\n /**\n * Child elements, observable streams of elements, or component\n * definitions.\n */\n readonly children: readonly UiChild[];\n}\n\n/**\n * Returns true when the value is a component definition.\n */\nexport function isComponentLikeElement(value: unknown): value is ComponentLikeElement {\n return typeof value === 'object' && value !== null && (value as Partial<ComponentLikeElement>).kind === 'component';\n}\n\n/**\n * Returns true when the value is an RxJS Observable.\n *\n * Uses structural detection so Observable subclasses and instances\n * from different bundles are recognized.\n */\nexport function isObservable(value: unknown): value is Observable<unknown> {\n return (\n typeof value === 'object' &&\n value !== null &&\n !isComponentLikeElement(value) &&\n typeof (value as Partial<Observable<unknown>>).subscribe === 'function'\n );\n}\n\n/**\n * Returns true when the value is a UiChild (element, observable, or\n * component definition).\n */\nexport function isUiChild(value: unknown): value is UiChild {\n return isUiElement(value) || isObservable(value) || isComponentLikeElement(value);\n}\n\n/**\n * Returns true when the value structurally matches a UiElement.\n *\n * Used to distinguish a UiElement from a plain UiProps record at runtime.\n */\nexport function isUiElement(value: unknown): value is UiElement {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const candidate = value as Partial<UiElement>;\n return (\n (Object.values(UiNodeType) as unknown[]).includes(candidate.type) &&\n typeof candidate.props === 'object' &&\n candidate.props !== null &&\n !Array.isArray(candidate.props) &&\n Array.isArray(candidate.children)\n );\n}\n","import { UiNodeType } from '../graph/UiNodeType';\nimport { type UiChild, type UiElement, isUiElement } from './UiElement';\nimport type { UiProps } from './UiProps';\n\n/**\n * Creates a declarative UI element.\n *\n * This does not create a UiNode and does not interact with UiGraph.\n */\nexport function createElement(type: UiNodeType, props: UiProps = {}, children: readonly UiChild[] = []): UiElement {\n if (isUiElement(props)) {\n throw new Error(\n `createElement: props for '${type}' look like a UiElement. ` +\n `Did you forget to call the component with props before its children? ` +\n `Elements can only be passed as children, never as props.`\n );\n }\n return {\n type,\n props,\n children\n };\n}\n","import type { UiModifierHost } from './UiModifierHost';\n\n/**\n * A kind of behaviour that can be attached to an element.\n *\n * One kind per behaviour, not per use: `hoverable()` produces many\n * `UiModifier` values that all share the one `hoverableKind`. The kind\n * holds the lifecycle; the value holds the arguments.\n */\nexport interface UiModifierKind<A> {\n /** Identity for reconciliation and for the inspector. */\n readonly key: symbol;\n readonly name: string;\n attach(host: UiModifierHost, args: A): void;\n /**\n * Same kind, same slot, different arguments. Omitting it means a\n * change is a detach followed by an attach.\n */\n update?(host: UiModifierHost, args: A, previous: A): void;\n detach?(host: UiModifierHost): void;\n}\n\n/**\n * What an element carries: a kind and the arguments to give it.\n *\n * `key` distinguishes two instances of one kind on one element, as\n * `key` distinguishes two children; without one they match by the\n * order they appear in among their own kind.\n */\nexport interface UiModifier<A = unknown> {\n readonly kind: UiModifierKind<A>;\n readonly args: A;\n readonly key?: string | number;\n}\n\n/**\n * Defines a modifier kind and returns its factory.\n *\n * const hoverableKind = { name: 'hoverable', attach(host) { … } };\n * export const hoverable = defineModifier(hoverableKind);\n *\n * The symbol is created here so two modules cannot accidentally share\n * a kind identity, and so a kind is never compared by name.\n */\nexport function defineModifier<A>(\n kind: Omit<UiModifierKind<A>, 'key'>\n): ((args: A, key?: string | number) => UiModifier<A>) & { readonly kind: UiModifierKind<A> } {\n const full: UiModifierKind<A> = { ...kind, key: Symbol(kind.name) };\n const factory = (args: A, key?: string | number): UiModifier<A> => ({ kind: full, args, key });\n return Object.assign(factory, { kind: full });\n}\n\nexport function isUiModifier(value: unknown): value is UiModifier {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const kind = (value as Partial<UiModifier>).kind;\n return typeof kind === 'object' && kind !== null && typeof kind.key === 'symbol' && typeof kind.attach === 'function';\n}\n","import type { Subject } from 'rxjs';\n\nimport { UiEventType } from '../input/UiInputEvent';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport { UiVisualState, type UiVisualStateSet } from '../properties/UiVisualState';\nimport { defineModifier, type UiModifier } from './UiModifier';\n\n/**\n * Pointer state, as a property anything can read, and the property\n * writes that follow from it.\n *\n * `visualState` has been a registered, paint-affecting property since\n * the first renderer and **nothing has ever written it** — which is\n * why an app that wanted a row to light up on hover kept a\n * `state(false)` and two handlers per widget. This is the one writer.\n *\n * Neither renderer reads `visualState` either: nothing resolves a\n * colour from a state yet, and what a themed control looks like when\n * hovered is F3's theming decision, not this modifier's. So the state\n * is published for whoever reads it, and `hovered` / `pressed` carry\n * the property values to write while the state lasts. They go through\n * the override cascade, so leaving restores exactly what the element\n * declared — including nothing at all.\n *\n * Hover and press are one modifier rather than two on purpose: two\n * modifiers writing `visualState` would be a real conflict, with the\n * later one's set dropping the other's state, and the pair is wanted\n * together often enough that splitting them would mostly produce that\n * bug. `hoverable()` and `pressable()` are this kind with one half\n * switched off.\n */\nexport interface InteractiveOptions {\n readonly hover: boolean;\n readonly press: boolean;\n /** Properties to write while the pointer is over the node. */\n readonly hovered?: Readonly<Record<string, unknown>>;\n /** Properties to write while it is held down, over the hovered ones. */\n readonly pressed?: Readonly<Record<string, unknown>>;\n}\n\nconst BOTH: InteractiveOptions = Object.freeze({ hover: true, press: true });\nconst HOVER_ONLY: InteractiveOptions = Object.freeze({ hover: true, press: false });\nconst PRESS_ONLY: InteractiveOptions = Object.freeze({ hover: false, press: true });\n\n// #region kind\nconst kind = defineModifier<InteractiveOptions>({\n name: 'interactive',\n attach(host, options) {\n // Per-attachment state: one node's pointer, not the kind's.\n let hovered = false;\n let pressed = false;\n const styled = new Set([...Object.keys(options.hovered ?? {}), ...Object.keys(options.pressed ?? {})]);\n\n const sync = (): void => {\n const states = new Set<UiVisualState>();\n if (hovered) {\n states.add(UiVisualState.Hovered);\n }\n if (pressed) {\n states.add(UiVisualState.Pressed);\n }\n if (states.size === 0) {\n states.add(UiVisualState.Normal);\n }\n const current = host.get<UiVisualStateSet>('visualState');\n if (!sameStates(current, states)) {\n host.set('visualState', states);\n }\n for (const property of styled) {\n const value =\n (pressed ? options.pressed?.[property] : undefined) ?? (hovered ? options.hovered?.[property] : undefined);\n if (value === undefined) {\n host.clear(property);\n } else {\n host.set(property, value);\n }\n }\n };\n\n if (options.hover) {\n host.on(UiEventType.PointerEnter, () => {\n hovered = true;\n sync();\n });\n host.on(UiEventType.PointerLeave, () => {\n hovered = false;\n // A pointer that leaves during a press never sends the up.\n pressed = false;\n sync();\n });\n }\n if (options.press) {\n host.on(UiEventType.PointerDown, () => {\n pressed = true;\n sync();\n });\n host.on(UiEventType.PointerUp, () => {\n pressed = false;\n sync();\n });\n host.on(UiEventType.PointerCancel, () => {\n pressed = false;\n sync();\n });\n }\n }\n // No detach: the host restores every property this modifier wrote\n // and removes its listeners. There is nothing else it holds.\n});\n// #endregion kind\n\n/** Hover and press. */\nexport function interactive(options: InteractiveOptions = BOTH): UiModifier<InteractiveOptions> {\n return kind(options);\n}\n\n/** Hover only. */\nexport function hoverable(): UiModifier<InteractiveOptions> {\n return kind(HOVER_ONLY);\n}\n\n/** Press only. */\nexport function pressable(): UiModifier<InteractiveOptions> {\n return kind(PRESS_ONLY);\n}\n\n/**\n * What every Button carries. One shared value, so `sameArgs` matches it\n * on its first line and never walks the options: a button pays nothing\n * per render for this. `interactive` declares no `update`, so arguments\n * that genuinely differ do detach and re-attach it, losing the hover and\n * press state with them, which is the reason this one is a constant.\n */\nexport const BUTTON_INTERACTION: UiModifier<InteractiveOptions> = kind(BOTH);\n\nfunction sameStates(a: UiVisualStateSet, b: ReadonlySet<UiVisualState>): boolean {\n if (a.size !== b.size) {\n return false;\n }\n for (const state of b) {\n if (!a.has(state)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Reports the node's box whenever it moves: the `ResizeObserver` a\n * canvas does not have.\n *\n * The subject is the caller's, so a component can attach this to an\n * element it renders and read the box back — which is how a split pane\n * turns a pointer position into a fraction of its own track without\n * reaching into the layout engine.\n */\nexport const measure = defineModifier<Subject<LayoutBox>>({\n name: 'measure',\n attach(host, target) {\n const box = host.layoutBox();\n if (box !== null) {\n target.next(box);\n }\n host.onLayout(next => target.next(next));\n }\n});\n","import { UiNodeType } from '../graph/UiNodeType';\nimport { BUTTON_INTERACTION } from '../modifiers/interaction';\nimport { createElement } from './UiFactory';\nimport {\n type UiChild,\n type UiElement,\n isComponentLikeElement,\n isUiChild,\n isUiElement,\n isObservable\n} from './UiElement';\nimport type {\n BoxProps,\n ButtonProps,\n ColumnProps,\n EditableTextProps,\n GridProps,\n PaintElementProps,\n RowProps,\n ScrollViewProps,\n StackProps,\n TextProps\n} from './UiElementProps';\nimport type { UiProps } from './UiProps';\n\n/**\n * Creates a Text element.\n */\nexport function Text(props: TextProps = {}): UiElement {\n return createElement(UiNodeType.Text, props);\n}\n\n/**\n * Creates an EditableText element: text the user can type into, with\n * a caret, a selection and IME composition. The runtime owns the\n * editing; the app sets `value` and listens to `onInput`.\n */\nexport function EditableText(props: EditableTextProps = {}): UiElement {\n return createElement(UiNodeType.EditableText, props);\n}\n\n/**\n * Creates a Button element.\n *\n * Every button is interactive: it carries the modifier that publishes\n * hover and press as `visualState`, so a themed control can paint from\n * it without the app keeping a boolean per widget. A button that\n * declares its own modifiers keeps them, with the interaction one\n * first, so a caller's write of the same property wins.\n *\n * **Unless the caller brought their own interaction.** Two `interactive`\n * modifiers on one node is the conflict `modifiers/interaction.ts`\n * exists to avoid: both write `visualState`, so the later one's set\n * drops the earlier one's state and the two disagree about what the\n * pointer is doing. A caller who passes `interactive(...)` is asking\n * for their configuration rather than a second copy of the default,\n * so the default steps aside. This was found with the inspector, which\n * showed a playground button carrying `interactive, interactive`.\n */\nexport function Button(props: ButtonProps = {}, ...children: UiChild[]): UiElement {\n const declared = props.modifiers;\n const modifiers =\n declared === undefined\n ? DEFAULT_BUTTON_MODIFIERS\n : declared.some(modifier => modifier.kind === BUTTON_INTERACTION.kind)\n ? declared\n : [BUTTON_INTERACTION, ...declared];\n return createElement(UiNodeType.Button, { ...props, modifiers }, children);\n}\n\nconst DEFAULT_BUTTON_MODIFIERS = Object.freeze([BUTTON_INTERACTION]);\n\n/**\n * Creates a Box element.\n *\n * Boxes are leaves or plain stacked containers sized by\n * explicit width/height (or flex props).\n */\nexport function Box(props: BoxProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.Box, props, children);\n}\n\n/**\n * Creates a Stack element: a Box whose children overlap in one content\n * box, aligned by `x` / `y` (start, center, end, stretch) and per child\n * by `selfX` / `selfY`. Same runtime node as Box; the name says what\n * the children do.\n */\nexport function Stack(props: StackProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.Box, props, children);\n}\n\n/**\n * Creates a Paint element: a Box the application draws into.\n *\n * `paint` is a function given a `PaintSurface`, the resolved box and\n * the device scale; `path` is a static vector shape stated as a\n * property. A node may carry either or both, and children stack over\n * whatever it drew, so a gauge with a label in the middle is one\n * element with a text child.\n *\n * The drawing runs when the painter's `inputs` change and not once per\n * frame, which is the whole reason `UiPaint` has an `inputs` field.\n * See `rendering/PaintSurface.ts`.\n */\nexport function Paint(props: PaintElementProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.Paint, props, children);\n}\n\n/**\n * Creates a Grid element.\n *\n * Children fill cells of the tracks in `columns` / `rows` (numbers,\n * `percent()`, `auto`, `fr()`, `minmax()`), by `autoFlow` order or by\n * explicit `column` / `row` (1-based lines) and `columnSpan` / `rowSpan`.\n * `x` / `y` align items in their cells (stretch by default),\n * `justifyContent` / `alignContent` distribute the tracks.\n */\nexport function Grid(props: GridProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.Grid, props, children);\n}\n\n/**\n * Creates a Row element.\n *\n * Accepts either children only or props followed by children.\n */\nexport function Row(...children: UiChild[]): UiElement;\nexport function Row(props: RowProps, ...children: UiChild[]): UiElement;\nexport function Row(first: RowProps | UiChild = {}, ...rest: UiChild[]): UiElement {\n if (isUiChild(first) && !isPlainProps(first)) {\n return createElement(UiNodeType.Row, {}, [first, ...rest]);\n }\n return createElement(UiNodeType.Row, first as UiProps, rest);\n}\n\n/**\n * Creates a Column element.\n *\n * Accepts either children only or props followed by children.\n */\nexport function Column(...children: UiChild[]): UiElement;\nexport function Column(props: ColumnProps, ...children: UiChild[]): UiElement;\nexport function Column(first: ColumnProps | UiChild = {}, ...rest: UiChild[]): UiElement {\n if (isUiChild(first) && !isPlainProps(first)) {\n return createElement(UiNodeType.Column, {}, [first, ...rest]);\n }\n return createElement(UiNodeType.Column, first as UiProps, rest);\n}\n\n/**\n * Creates a ScrollView element.\n */\nexport function ScrollView(props: ScrollViewProps = {}, ...children: UiChild[]): UiElement {\n return createElement(UiNodeType.ScrollView, props, children);\n}\n\n/**\n * Returns true when a value is a plain props record rather than a child.\n *\n * Props are plain objects. Children are UiElements or Observables.\n */\nfunction isPlainProps(value: unknown): value is UiProps {\n return (\n typeof value === 'object' &&\n value !== null &&\n !isUiElement(value) &&\n !isObservable(value) &&\n !isComponentLikeElement(value)\n );\n}\n","import { UiEventType } from '../input/UiInputEvent';\n\n/**\n * A prop is treated as an event handler when it is named `on` +\n * PascalCase and holds a function. Props matching that shape whose\n * value is not a function stay ordinary node properties.\n */\nconst EVENT_PROP_PATTERN = /^on[A-Z]/;\n\n/**\n * Declarative prop name for each input event.\n *\n * Spelled out rather than derived: UiEventType values are lowercase\n * concatenations ('pointerdown', 'longpress'), so the camelCase prop\n * name cannot be recovered from them. UiEventProps.spec asserts this\n * table stays exhaustive, so adding an event type without a prop name\n * fails the build rather than silently going unbindable.\n */\nconst EVENT_PROPS: Readonly<Record<string, UiEventType>> = {\n onPointerDown: UiEventType.PointerDown,\n onPointerUp: UiEventType.PointerUp,\n onPointerMove: UiEventType.PointerMove,\n onPointerCancel: UiEventType.PointerCancel,\n onPointerEnter: UiEventType.PointerEnter,\n onPointerLeave: UiEventType.PointerLeave,\n onWheel: UiEventType.Wheel,\n onKeyDown: UiEventType.KeyDown,\n onKeyUp: UiEventType.KeyUp,\n onFocus: UiEventType.Focus,\n onBlur: UiEventType.Blur,\n onBeforeInput: UiEventType.BeforeInput,\n onInput: UiEventType.Input,\n onClick: UiEventType.Click,\n onLongPress: UiEventType.LongPress,\n onDragStart: UiEventType.DragStart,\n onDragMove: UiEventType.DragMove,\n onDragEnd: UiEventType.DragEnd,\n onPanStart: UiEventType.PanStart,\n onPanMove: UiEventType.PanMove,\n onPanEnd: UiEventType.PanEnd,\n onContextMenu: UiEventType.ContextMenu,\n onPinchStart: UiEventType.PinchStart,\n onPinchMove: UiEventType.PinchMove,\n onPinchEnd: UiEventType.PinchEnd\n};\n\n/**\n * Whether a prop name/value pair declares an event handler.\n */\nexport function isEventProp(property: string, value: unknown): boolean {\n return typeof value === 'function' && EVENT_PROP_PATTERN.test(property);\n}\n\n/**\n * The event a handler prop binds to, or undefined when the name is\n * not a recognized event prop.\n */\nexport function eventTypeForProp(property: string): UiEventType | undefined {\n return EVENT_PROPS[property];\n}\n\n/**\n * Every accepted event prop name, sorted, for error messages.\n */\nexport function knownEventPropNames(): string[] {\n return Object.keys(EVENT_PROPS).sort();\n}\n","/**\n * Whether two modifier arguments mean the same thing.\n *\n * A modifier's arguments are compared when its element is rebuilt, and\n * a difference is a reason to update the modifier or, for a kind with no\n * `update`, to detach it and attach a fresh one. Identity was the\n * comparison, and it made `modifiers={[interactive({ hovered: ... })]}`\n * written inline a modifier that re-attached on every render, losing\n * its pointer state and paying its attach cost each time. Nothing warned\n * about it, and hoisting the value to module scope was folklore.\n *\n * So arguments are compared by what they hold: plain objects and arrays\n * recursively, primitives by value, and everything else, functions,\n * Observables, class instances, by identity, because a new callback or\n * a new stream is a genuinely new argument. An inline `interactive(...)`\n * with the same options is now the same modifier, as it reads.\n */\nexport function sameArgs(a: unknown, b: unknown, depth = 0): boolean {\n if (Object.is(a, b)) {\n return true;\n }\n if (depth > 8 || typeof a !== 'object' || typeof b !== 'object' || a === null || b === null) {\n return false;\n }\n if (Array.isArray(a) || Array.isArray(b)) {\n if (!Array.isArray(a) || !Array.isArray(b) || a.length !== b.length) {\n return false;\n }\n for (let index = 0; index < a.length; index++) {\n if (!sameArgs(a[index], b[index], depth + 1)) {\n return false;\n }\n }\n return true;\n }\n if (!isPlain(a) || !isPlain(b)) {\n return false;\n }\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n if (keysA.length !== keysB.length) {\n return false;\n }\n for (const key of keysA) {\n if (!Object.prototype.hasOwnProperty.call(b, key)) {\n return false;\n }\n if (!sameArgs((a as Record<string, unknown>)[key], (b as Record<string, unknown>)[key], depth + 1)) {\n return false;\n }\n }\n return true;\n}\n\nfunction isPlain(value: object): boolean {\n const prototype = Object.getPrototypeOf(value);\n return prototype === Object.prototype || prototype === null;\n}\n","import { EMPTY, isObservable, type Observable, type Subscription } from 'rxjs';\n\nimport {\n AnimationDriver,\n UiSharedElements,\n UiSpring,\n createTween,\n type AnimatedCell,\n type UiSpringOptions,\n type UiTweenOptions\n} from '../animation';\n\nimport { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiEnvironmentKey } from '../environment/UiEnvironmentKey';\nimport type { UiGraph } from '../graph/UiGraph';\nimport type { UiNode } from '../graph/UiNode';\nimport { clearOverrideProperty, writeOverrideProperty } from '../graph/UiPropertyOverrides';\nimport type { UiEventListener, UiEventListenerOptions, UiInputDispatcher } from '../input/UiInputDispatcher';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { UiEventType } from '../input/UiInputEvent';\nimport type { DecorationShape } from '../rendering/Decorations';\nimport { findPropertyDefinition, propertyEffects } from '../properties/UiPropertyRegistry';\nimport { resolveProperty } from '../properties/UiPropertyResolver';\nimport { isUiModifier, type UiModifier, type UiModifierKind } from './UiModifier';\nimport { sameArgs } from './sameArgs';\nimport type { UiModifierHost, UiModifierTeardown } from './UiModifierHost';\n\n/**\n * A modifier named a property that is not in the registry.\n *\n * Its own class because it is the one failure a modifier can raise\n * that must **not** be contained: an unknown property name is a typo,\n * caught the moment the modifier runs, and the registry's closedness\n * is what makes that a hard error rather than a silently ignored\n * write. Everything else a modifier throws is contained, reported and\n * detached; this is rethrown.\n */\nexport class UiUnknownModifierPropertyError extends Error {}\n\n/** One attached instance: its slot, its current arguments, its host. */\ninterface Attached {\n readonly kind: UiModifierKind<unknown>;\n readonly slot: string | number;\n readonly host: Host;\n args: unknown;\n}\n\n/** Where a modifier's layout access comes from; the runtime supplies it. */\nexport interface UiModifierLayout {\n box(node: UiNode): LayoutBox | null;\n /** The same node in pre-scroll layout coordinates; see `UiModifierHost.flowBox`. */\n flowBox(node: UiNode): LayoutBox | null;\n /** The node's own scroll offset; see `UiModifierHost.scrollOffset`. */\n scroll(node: UiNode): { x: number; y: number } | null;\n /**\n * The drawn surface, in the same coordinates `box` answers in; see\n * `UiModifierHost.viewportBox`.\n *\n * Optional, and absent means unknown. A layout double in a spec, and\n * a headless graph, have no surface to report — and a modifier that\n * gets null must carry on as though its node were visible, because\n * \"nobody is drawing\" and \"off screen\" are not the same answer.\n */\n viewport?(): LayoutBox | null;\n onLayout(node: UiNode, listener: (box: LayoutBox) => void): () => void;\n}\n\n/**\n * Where a modifier's focus access comes from.\n *\n * Supplied by the runtime, which owns the focus manager. Kept behind\n * an interface for the same reason layout is: `packages/core/src/composition`\n * must not depend on the input stack, and a headless build has\n * neither.\n */\nexport interface UiModifierFocus {\n isFocused(node: UiNode): boolean;\n /** Whether the focus `node` holds should be shown; see `UiFocusManager.focusVisible`. */\n isFocusVisible(node: UiNode): boolean;\n focus(node: UiNode): void;\n onFocusChange(node: UiNode, listener: (focused: boolean) => void): () => void;\n}\n\n/** Where a modifier's environment access comes from. */\nexport interface UiModifierEnvironment {\n read<T>(node: UiNode, key: UiEnvironmentKey<T>): T;\n onChange(node: UiNode, listener: () => void): () => void;\n}\n\n/**\n * The modifiers attached to one node, and their reconciliation.\n *\n * Matching is by `(kind, key ?? position among that kind)`, the same\n * rule keyed children follow: an instance survives a re-render when it\n * lands in the same slot, and its `update` is called only when the\n * arguments actually differ. A kind with no `update` is detached and\n * re-attached instead, which is the honest behaviour for one that\n * cannot describe a change.\n *\n * The set keeps list order, because order is semantic: B1's override\n * layer resolves a conflict in favour of whichever modifier is later.\n */\nexport class UiModifierSet {\n private attached: Attached[] = [];\n /** Built on the first `decorate`, so an undecorated node allocates nothing. */\n private decorations: NodeDecorations | null = null;\n\n constructor(\n private readonly node: UiNode,\n private readonly graph: UiGraph,\n private readonly dispatcher?: UiInputDispatcher,\n private readonly layout?: UiModifierLayout,\n private readonly focus?: UiModifierFocus,\n private readonly environment?: UiModifierEnvironment,\n private readonly animations?: AnimationDriver,\n private readonly sharedElements?: UiSharedElements\n ) {}\n\n /** The merged decoration list, created on demand. */\n private decorationsFor(): NodeDecorations {\n if (this.decorations === null) {\n this.decorations = new NodeDecorations(this.node, this.graph);\n }\n return this.decorations;\n }\n\n /** The names of what is attached, in order, for the inspector. */\n get names(): string[] {\n return this.attached.map(entry => entry.kind.name);\n }\n\n get size(): number {\n return this.attached.length;\n }\n\n /**\n * Brings the attached set in line with what the element declares.\n *\n * Runs after the node's declared props are written and before its\n * children are reconciled, so a modifier sees the element's own\n * values and may act on them before layout reads anything.\n */\n reconcile(modifiers: readonly UiModifier[]): void {\n const previous = this.attached;\n const taken = new Set<Attached>();\n const next: Attached[] = [];\n const ordinals = new Map<symbol, number>();\n\n for (const modifier of modifiers) {\n const kind = modifier.kind as UiModifierKind<unknown>;\n const ordinal = ordinals.get(kind.key) ?? 0;\n ordinals.set(kind.key, ordinal + 1);\n const slot = modifier.key ?? ordinal;\n const match = previous.find(entry => entry.kind.key === kind.key && entry.slot === slot && !taken.has(entry));\n if (match === undefined) {\n const attached = this.attach(kind, slot, modifier.args);\n if (attached !== null) {\n next.push(attached);\n }\n continue;\n }\n taken.add(match);\n const updated = this.update(match, modifier.args);\n if (updated !== null) {\n next.push(updated);\n }\n }\n\n for (const [order, entry] of next.entries()) {\n entry.host.setOrder(order);\n }\n // Order is semantic for decorations too: a node paints its\n // modifiers' shapes in the order the element listed them.\n this.decorations?.reorder();\n\n for (const entry of previous) {\n if (!taken.has(entry)) {\n this.detachOne(entry);\n }\n }\n this.attached = next;\n }\n\n /** Releases everything, for a node leaving the tree. */\n detach(): void {\n for (let index = this.attached.length - 1; index >= 0; index--) {\n this.detachOne(this.attached[index]);\n }\n this.attached = [];\n }\n\n /**\n * Attaches one modifier, or reports it and returns null.\n *\n * A modifier that throws while attaching had already been able to\n * write properties, register listeners and own teardowns, so the\n * half-attached host is released rather than dropped: without that,\n * the overrides it wrote would stay on the node with nothing left to\n * take them off again.\n */\n private attach(kind: UiModifierKind<unknown>, slot: string | number, args: unknown): Attached | null {\n const host = new Host(this.node, this.graph, kind.name, () => this.decorationsFor(), {\n dispatcher: this.dispatcher,\n layout: this.layout,\n focus: this.focus,\n environment: this.environment,\n animations: this.animations,\n sharedElements: this.sharedElements\n });\n const entry: Attached = { kind, slot, host, args };\n try {\n kind.attach(host, args);\n } catch (error) {\n host.release();\n if (error instanceof UiUnknownModifierPropertyError) {\n throw error;\n }\n this.graph.handleModifierError(kind.name, this.node, 'attach', error);\n return null;\n }\n return entry;\n }\n\n private update(entry: Attached, args: unknown): Attached | null {\n if (sameArgs(entry.args, args)) {\n // The same arguments, whether the same object or an equal one\n // written inline: nothing to tell the modifier, and nothing to\n // re-attach. See `sameArgs` for what counts as equal.\n entry.args = args;\n return entry;\n }\n if (entry.kind.update === undefined) {\n // Nothing to tell it, so it is a different instance: tear the old\n // one down before the new one attaches, so their resources never\n // overlap.\n this.detachOne(entry);\n return this.attach(entry.kind, entry.slot, args);\n }\n const previous = entry.args;\n entry.args = args;\n try {\n entry.kind.update(entry.host, args, previous);\n } catch (error) {\n this.detachOne(entry);\n if (error instanceof UiUnknownModifierPropertyError) {\n throw error;\n }\n this.graph.handleModifierError(entry.kind.name, this.node, 'update', error);\n return null;\n }\n return entry;\n }\n\n private detachOne(entry: Attached): void {\n try {\n entry.kind.detach?.(entry.host);\n } catch (error) {\n if (error instanceof UiUnknownModifierPropertyError) {\n entry.host.release();\n throw error;\n }\n this.graph.handleModifierError(entry.kind.name, this.node, 'detach', error);\n }\n // Outside the catch: the host's own release is the framework's, and\n // it is what restores the node. A modifier's `detach` failing must\n // not leave an override behind.\n entry.host.release();\n }\n}\n\n/**\n * The host handed to one attached modifier.\n *\n * Its `source` symbol is what the override cascade records, so two\n * instances of one kind on a node write independently and each\n * restores only its own.\n */\ninterface HostServices {\n dispatcher?: UiInputDispatcher;\n layout?: UiModifierLayout;\n focus?: UiModifierFocus;\n environment?: UiModifierEnvironment;\n animations?: AnimationDriver;\n sharedElements?: UiSharedElements;\n}\n\nclass Host implements UiModifierHost {\n private teardowns: UiModifierTeardown[] = [];\n private readonly source: symbol;\n private readonly written = new Set<string>();\n private readonly subscriptions = new Map<string, Subscription>();\n private order = 0;\n private decorated = false;\n /** Cells this modifier is driving, stopped when it detaches. */\n private readonly animated = new Set<AnimatedCell<unknown>>();\n\n constructor(\n readonly node: UiNode,\n private readonly graph: UiGraph,\n private readonly name: string,\n private readonly decorations: () => NodeDecorations,\n private readonly services: HostServices\n ) {\n this.source = Symbol(name);\n }\n\n /** The modifier's position in the element's list; later wins. */\n setOrder(order: number): void {\n if (order === this.order) {\n return;\n }\n this.order = order;\n for (const property of this.written) {\n this.write(property, this.node.properties.get(property));\n }\n if (this.decorated) {\n this.decorations().setOrder(this.source, order);\n }\n }\n\n get<T>(property: string): T {\n const definition = findPropertyDefinition<T>(property);\n if (definition === undefined) {\n throw new UiUnknownModifierPropertyError(\n `Modifier '${this.name}' read unknown property '${property}' on node '${this.node.id}'.`\n );\n }\n return resolveProperty(this.node, definition);\n }\n\n set(property: string, value: unknown): void {\n if (findPropertyDefinition(property) === undefined) {\n throw new UiUnknownModifierPropertyError(\n `Modifier '${this.name}' wrote unknown property '${property}' on node '${this.node.id}'.`\n );\n }\n this.subscriptions.get(property)?.unsubscribe();\n this.subscriptions.delete(property);\n if (isObservable(value)) {\n // Held for as long as the modifier is attached; each emission is\n // another write of the same override.\n this.subscriptions.set(\n property,\n (value as Observable<unknown>).subscribe(next => this.write(property, next))\n );\n this.written.add(property);\n return;\n }\n this.write(property, value);\n }\n\n clear(property: string): void {\n this.subscriptions.get(property)?.unsubscribe();\n this.subscriptions.delete(property);\n if (!this.written.delete(property)) {\n return;\n }\n clearOverrideProperty(this.graph, this.node, property, this.source, propertyEffects(property));\n }\n\n on(type: UiEventType, listener: UiEventListener, options?: UiEventListenerOptions): void {\n const dispatcher = this.services.dispatcher;\n if (dispatcher === undefined) {\n warnMissingDispatcher(this.name);\n return;\n }\n dispatcher.addEventListener(this.node, type, listener, options);\n this.own(() => dispatcher.removeEventListener(this.node, type, listener, options));\n }\n\n onRoot(type: UiEventType, listener: UiEventListener, options: UiEventListenerOptions = { capture: true }): void {\n const dispatcher = this.services.dispatcher;\n if (dispatcher === undefined) {\n warnMissingDispatcher(this.name);\n return;\n }\n // The graph's root, which is above the layout root and therefore\n // above both the application's tree and the overlay layer beside\n // it. A press on a menu opened over the page is dispatched into\n // the overlay layer, and a modifier on the page still has to hear\n // about it.\n const root = this.graph.root;\n dispatcher.addEventListener(root, type, listener, options);\n this.own(() => dispatcher.removeEventListener(root, type, listener, options));\n }\n\n layoutBox(): LayoutBox | null {\n return this.services.layout?.box(this.node) ?? null;\n }\n\n flowBox(): LayoutBox | null {\n return this.services.layout?.flowBox(this.node) ?? null;\n }\n\n viewportBox(): LayoutBox | null {\n return this.services.layout?.viewport?.() ?? null;\n }\n\n onLayout(listener: (box: LayoutBox) => void): void {\n const layout = this.services.layout;\n if (layout === undefined) {\n warnMissingLayout(this.name);\n return;\n }\n this.own(layout.onLayout(this.node, listener));\n }\n\n environment<T>(key: UiEnvironmentKey<T>): T {\n const environment = this.services.environment;\n if (environment === undefined) {\n // The default is what an unprovided key resolves to anyway, so a\n // headless build reads the same value the tree would.\n return key.defaultValue;\n }\n return environment.read(this.node, key);\n }\n\n onEnvironment(listener: () => void): void {\n const environment = this.services.environment;\n if (environment === undefined) {\n warnMissing(this.name, 'follow its environment', 'environment');\n return;\n }\n this.own(environment.onChange(this.node, listener));\n }\n\n focus(): void {\n const focus = this.services.focus;\n if (focus === undefined) {\n warnMissing(this.name, 'move focus', 'focus');\n return;\n }\n focus.focus(this.node);\n }\n\n isFocused(): boolean {\n return this.services.focus?.isFocused(this.node) ?? false;\n }\n\n isFocusVisible(): boolean {\n return this.services.focus?.isFocusVisible(this.node) ?? false;\n }\n\n onFocusChange(listener: (focused: boolean) => void): void {\n const focus = this.services.focus;\n if (focus === undefined) {\n warnMissing(this.name, 'follow focus', 'focus');\n return;\n }\n this.own(focus.onFocusChange(this.node, listener));\n }\n\n decorate(shapes: readonly DecorationShape[] | null): void {\n this.decorated = shapes !== null;\n this.decorations().set(this.source, this.order, shapes);\n }\n\n animate<T>(cell: AnimatedCell<T>, to: T, options: UiTweenOptions): Observable<T> {\n const driver = this.services.animations;\n const tween = driver === undefined ? undefined : createTween(cell, to, options);\n if (driver === undefined || tween === undefined) {\n if (driver === undefined) {\n warnMissing(this.name, 'animate a value', 'animations');\n }\n cell.value = to;\n return EMPTY as Observable<T>;\n }\n this.animated.add(cell as AnimatedCell<unknown>);\n return driver.start(tween);\n }\n\n spring(cell: AnimatedCell<number>, to: number, options: UiSpringOptions): Observable<number> {\n const driver = this.services.animations;\n if (driver === undefined) {\n warnMissing(this.name, 'animate a value', 'animations');\n cell.value = to;\n return EMPTY as Observable<number>;\n }\n this.animated.add(cell as AnimatedCell<unknown>);\n // Retargeting a spring keeps the velocity it had, as\n // `AnimationService.spring` does: a spring that started again from\n // rest every time it was re-aimed would crawl, which is exactly\n // what a modifier re-aimed once a frame does.\n const previous = driver.animationFor(cell);\n const velocity = options.velocity ?? (previous instanceof UiSpring ? previous.currentVelocity : 0);\n return driver.start(new UiSpring(cell, to, { ...options, velocity }));\n }\n\n own(teardown: UiModifierTeardown): void {\n this.teardowns.push(teardown);\n }\n\n scrollOffset(): { x: number; y: number } | null {\n return this.services.layout?.scroll(this.node) ?? null;\n }\n\n stopAnimation<T>(cell: AnimatedCell<T>): void {\n this.services.animations?.stop(cell as AnimatedCell<unknown>);\n }\n\n get shared(): UiSharedElements | null {\n const registry = this.services.sharedElements;\n if (registry === undefined) {\n warnMissing(this.name, 'share an element across a tree change', 'sharedElements');\n return null;\n }\n return registry;\n }\n\n requestFrame(): void {\n this.graph.markDirty(this.node, DirtyFlags.Paint);\n }\n\n private write(property: string, value: unknown): void {\n this.written.add(property);\n writeOverrideProperty(\n this.graph,\n this.node,\n property,\n { source: this.source, name: this.name, order: this.order, value },\n propertyEffects(property)\n );\n }\n\n /**\n * Restores everything this modifier wrote, then runs its teardowns\n * in reverse, as a stack unwinds.\n */\n release(): void {\n // clear() deletes the entry it is given, which is the one being\n // visited; removing the current element of a Set mid-iteration is\n // defined and does not skip the next.\n for (const property of this.written) {\n this.clear(property);\n }\n if (this.decorated) {\n this.decorate(null);\n }\n for (const cell of this.animated) {\n this.services.animations?.stop(cell);\n }\n this.animated.clear();\n const teardowns = this.teardowns;\n this.teardowns = [];\n for (let index = teardowns.length - 1; index >= 0; index--) {\n const teardown = teardowns[index];\n if (typeof teardown === 'function') {\n teardown();\n } else {\n teardown.unsubscribe();\n }\n }\n }\n}\n\n/**\n * The merged decorations of one node.\n *\n * Every modifier that decorates contributes its own list; the node\n * paints the concatenation in modifier order, which is the same rule\n * the override cascade uses and for the same reason — order in the\n * element's list is the only thing that can settle a disagreement, and\n * it is the one a reader can see.\n *\n * `node.decorations` goes back to null when the last contributor\n * leaves, so a node that was decorated and is not any more costs\n * exactly what it did before.\n */\nclass NodeDecorations {\n private readonly bySource = new Map<symbol, { order: number; shapes: readonly DecorationShape[] }>();\n\n constructor(\n private readonly node: UiNode,\n private readonly graph: UiGraph\n ) {}\n\n set(source: symbol, order: number, shapes: readonly DecorationShape[] | null): void {\n if (shapes === null || shapes.length === 0) {\n if (!this.bySource.delete(source)) {\n return;\n }\n } else {\n this.bySource.set(source, { order, shapes });\n }\n this.rebuild();\n }\n\n setOrder(source: symbol, order: number): void {\n const entry = this.bySource.get(source);\n if (entry === undefined || entry.order === order) {\n return;\n }\n entry.order = order;\n this.rebuild();\n }\n\n reorder(): void {\n if (this.bySource.size > 1) {\n this.rebuild();\n }\n }\n\n private rebuild(): void {\n if (this.bySource.size === 0) {\n this.node.decorations = null;\n this.graph.markDirty(this.node, DirtyFlags.Paint);\n return;\n }\n const merged: DecorationShape[] = [];\n for (const entry of [...this.bySource.values()].sort((a, b) => a.order - b.order)) {\n merged.push(...entry.shapes);\n }\n this.node.decorations = merged;\n this.graph.markDirty(this.node, DirtyFlags.Paint);\n }\n}\n\nlet warnedAboutLayout = false;\n\nfunction warnMissingLayout(name: string): void {\n if (warnedAboutLayout) {\n return;\n }\n warnedAboutLayout = true;\n console.warn(\n `Modifier '${name}' asked to follow its node's box, but the UiGraphBuilder was constructed without layout access. ` +\n `Construct it with { layout } for onLayout to fire.`\n );\n}\n\nlet warnedAboutDispatcher = false;\n\nfunction warnMissingDispatcher(name: string): void {\n if (warnedAboutDispatcher) {\n return;\n }\n warnedAboutDispatcher = true;\n console.warn(\n `Modifier '${name}' asked to listen for input, but the UiGraphBuilder was constructed without a dispatcher. ` +\n `Construct it with { dispatcher } for modifiers to receive events.`\n );\n}\n\nconst warnedAbout = new Set<string>();\n\n/** One warning per missing service, as the two above do for theirs. */\nfunction warnMissing(name: string, what: string, option: string): void {\n if (warnedAbout.has(option)) {\n return;\n }\n warnedAbout.add(option);\n console.warn(\n `Modifier '${name}' asked to ${what}, but the UiGraphBuilder was constructed without ${option} access. ` +\n `Construct it with { ${option} }, as the runtime does.`\n );\n}\n\n/**\n * Rejects a `modifiers` prop that is not a list of modifiers, naming\n * the node the way an unknown prop does.\n */\nexport function assertModifierList(node: UiNode, value: unknown): readonly UiModifier[] {\n if (!Array.isArray(value)) {\n throw new Error(`The 'modifiers' prop on node '${node.id}' must be an array, got ${typeof value}.`);\n }\n for (const entry of value) {\n if (!isUiModifier(entry)) {\n throw new Error(\n `The 'modifiers' prop on node '${node.id}' must hold modifiers built by a factory from defineModifier.`\n );\n }\n }\n return value as readonly UiModifier[];\n}\n","import type { AnimatedCell, UiTransitionSpec } from '../animation';\nimport { AnimationDriver, UiSpring, createTween } from '../animation';\nimport type { DirtyFlags } from './DirtyFlags';\nimport type { UiGraph } from './UiGraph';\nimport type { NodeProperty, UiNode, UiNodeTransitions } from './UiNode';\n\n/**\n * The transitions declared on one node, and the animations they are\n * currently running.\n *\n * This is the same shape as the override cascade and for the same\n * reason that lives on the node: **both\n * the builder's static writes and a binding's emissions funnel through\n * `updateNodeProperty`**, so interposing there is the only place that\n * catches both. `node.transitions` is null until an element declares\n * one, so the cost to a tree without transitions is the second field\n * read on the write path.\n *\n * What it interposes is a write, not a binding. The Observable a\n * component wrote is still bound by the graph and still emits into the\n * same property; what changes is that the emission becomes a target\n * rather than a value. So nothing about component identity, binding\n * teardown or `getBindingForProperty` moves, and a `transition` can be\n * added to an element without touching anything else about it.\n */\nexport class NodeTransitions implements UiNodeTransitions {\n private specs: ReadonlyMap<NodeProperty, UiTransitionSpec> = new Map();\n private readonly cells = new Map<NodeProperty, AnimatedCell<unknown>>();\n private readonly targets = new Map<NodeProperty, unknown>();\n\n constructor(\n private readonly node: UiNode,\n private readonly graph: UiGraph,\n private readonly driver: AnimationDriver\n ) {}\n\n /** Replaces what the element declares; a property dropped from the list stops animating. */\n setSpecs(specs: ReadonlyMap<NodeProperty, UiTransitionSpec>): void {\n // Deleting the current key while iterating a Map is defined and\n // does not skip the next one.\n for (const property of this.cells.keys()) {\n if (!specs.has(property)) {\n this.stopProperty(property);\n }\n }\n this.specs = specs;\n }\n\n get size(): number {\n return this.specs.size;\n }\n\n /**\n * Takes over a write, or declines it.\n *\n * Declines when the property has no transition, when the node has no\n * value yet — a node's *first* value is not a change and animating\n * from nothing would mean animating from a default nobody asked for\n * — and when the two values cannot be blended (`interpolatorFor`\n * says which, and why).\n */\n write(property: NodeProperty, value: unknown, flags: DirtyFlags): boolean {\n const spec = this.specs.get(property);\n if (spec === undefined) {\n return false;\n }\n if (!this.node.properties.has(property)) {\n return false;\n }\n const current = this.node.properties.get(property);\n if (Object.is(current, value)) {\n return false;\n }\n // Already on its way there: a binding that re-emits the same target\n // must not restart the animation, or a spring would never settle.\n if (this.cells.has(property) && Object.is(this.targets.get(property), value)) {\n return true;\n }\n const cell = this.cellFor(property, flags);\n const started = this.startAnimation(cell, value, spec, property);\n if (!started) {\n this.stopProperty(property);\n return false;\n }\n this.targets.set(property, value);\n return true;\n }\n\n /** Cancels everything, for a node leaving the tree or dropping the prop. */\n release(): void {\n for (const cell of this.cells.values()) {\n this.driver.stop(cell);\n }\n this.cells.clear();\n this.targets.clear();\n this.specs = new Map();\n }\n\n private stopProperty(property: NodeProperty): void {\n const cell = this.cells.get(property);\n if (cell !== undefined) {\n this.driver.stop(cell);\n this.cells.delete(property);\n }\n this.targets.delete(property);\n }\n\n /**\n * The cell one property animates through, kept for the life of the\n * node: the driver keys its running set by cell identity, so a fresh\n * adapter per write would leave the previous animation running.\n *\n * Writing goes through `updateNodePropertyNow`, which is the path a\n * plain write takes minus this class — so a modifier's override still\n * sits on top of an animated value, exactly as it sits on top of a\n * bound one.\n */\n private cellFor(property: NodeProperty, flags: DirtyFlags): AnimatedCell<unknown> {\n let cell = this.cells.get(property);\n if (cell !== undefined) {\n return cell;\n }\n const node = this.node;\n const graph = this.graph;\n cell = {\n get value(): unknown {\n return node.properties.get(property);\n },\n set value(next: unknown) {\n graph.updateNodePropertyNow(node, property, next, flags);\n }\n };\n this.cells.set(property, cell);\n return cell;\n }\n\n private startAnimation(\n cell: AnimatedCell<unknown>,\n to: unknown,\n spec: UiTransitionSpec,\n property: NodeProperty\n ): boolean {\n if (spec.kind === 'spring') {\n if (typeof cell.value !== 'number' || typeof to !== 'number') {\n warnNotInterpolable(property);\n return false;\n }\n const previous = this.driver.animationFor(cell);\n const velocity = previous instanceof UiSpring ? previous.currentVelocity : 0;\n this.driver.start(\n new UiSpring(cell as AnimatedCell<number>, to, {\n spring: spec.spring,\n velocity,\n restDelta: spec.restDelta,\n reducedMotion: spec.reducedMotion\n })\n );\n return true;\n }\n const tween = createTween(cell, to, {\n duration: spec.duration,\n easing: spec.easing,\n stepMs: spec.stepMs,\n reducedMotion: spec.reducedMotion\n });\n if (tween === undefined) {\n warnNotInterpolable(property);\n return false;\n }\n this.driver.start(tween);\n return true;\n }\n}\n\nconst warnedProperties = new Set<string>();\n\n/**\n * One warning per property, then silence.\n *\n * Not an error: a `transition` on a property whose values cannot be\n * blended should still leave a working screen, and the value does get\n * written. But it must not be silent, because \"my transition does\n * nothing\" is otherwise indistinguishable from a typo.\n */\nfunction warnNotInterpolable(property: NodeProperty): void {\n if (warnedProperties.has(property)) {\n return;\n }\n warnedProperties.add(property);\n console.warn(\n `A 'transition' was declared for '${property}', but its values cannot be blended, so it is written directly. ` +\n `Numbers, UiColor objects and transforms can be animated; typed lengths (percent, fr, auto) and palette ` +\n `names cannot. See interpolatorFor in packages/core/src/animation/Interpolate.ts.`\n );\n}\n\n/** Test hook: forgets which properties have been reported. */\nexport function resetTransitionWarnings(): void {\n warnedProperties.clear();\n}\n","import { Observable } from 'rxjs';\n\nimport { UiChildrenBinding } from '../bindings/UiChildrenBinding';\nimport { UiEventBinding } from '../bindings/UiEventBinding';\nimport { DirtyFlags } from '../graph/DirtyFlags';\nimport { UiGraph } from '../graph/UiGraph';\nimport type { NodeProperty, UiNode } from '../graph/UiNode';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { UiEventListener, UiInputDispatcher } from '../input/UiInputDispatcher';\nimport {\n closestPropertyName,\n findPropertyDefinition,\n getPropertyNames,\n propertyEffects\n} from '../properties/UiPropertyRegistry';\nimport { eventTypeForProp, isEventProp, knownEventPropNames } from './UiEventProps';\nimport type { ComponentResolver } from './ComponentResolver';\nimport {\n type ComponentLikeElement,\n type UiChild,\n type UiElement,\n isComponentLikeElement,\n isObservable\n} from './UiElement';\nimport type { UiNodeRef } from './UiElementProps';\nimport type { UiProps } from './UiProps';\nimport {\n UiModifierSet,\n assertModifierList,\n type UiModifierEnvironment,\n type UiModifierFocus,\n type UiModifierLayout\n} from '../modifiers/UiModifierSet';\nimport { assertTransitionMap, type AnimationDriver, type UiSharedElements, type UiTransitionSpec } from '../animation';\nimport { NodeTransitions } from '../graph/UiPropertyTransitions';\n\n/**\n * Property reserved for reconciliation identity.\n *\n * The key never becomes a runtime property on the UiNode.\n */\nconst KEY_PROP = 'key';\n/**\n * `ref` receives the UiNode an element produced, and null when the\n * node is removed. It is how a component gets hold of a node to hand\n * to `anchor`, or to focus imperatively.\n */\nconst REF_PROP = 'ref';\n/**\n * `modifiers` attaches behaviour to an element without wrapping it.\n * Reconciled like children — matched by kind and slot — and detached\n * with the node. See `packages/core/src/modifiers`.\n */\nconst MODIFIERS_PROP = 'modifiers';\n/**\n * `transition` says how a property gets from one value to the next.\n *\n * The fourth reserved name, beside `key`, `ref` and `modifiers`, and\n * for the same reason those three are reserved rather than registered:\n * every one of them is about the *element* — its identity, who holds\n * its node, what is attached to it — rather than a value on the node.\n * Nothing in layout, paint, input or the environment ever reads a\n * `transition`; `propertyEffects('transition')` would have no meaning,\n * and registering it would make it bindable and overridable, which is\n * nonsense for a description of how other properties are written.\n * the rule that the registry's closedness is\n * load-bearing is exactly the argument for keeping it out.\n */\nconst TRANSITION_PROP = 'transition';\n\nexport type { UiNodeRef } from './UiElementProps';\n\nfunction knownPropertyNames(): string[] {\n return getPropertyNames();\n}\n\nfunction describeValueType(value: unknown): string {\n if (value === null) {\n return 'null';\n }\n if (Array.isArray(value)) {\n return 'an array';\n }\n return typeof value === 'object' ? 'an object' : `a ${typeof value}`;\n}\n\nexport interface UiGraphBuilderOptions {\n /**\n * Mounts components encountered during reconciliation.\n *\n * Without a resolver the builder still reconciles plain elements\n * and observable children; encountering a component throws.\n */\n components?: ComponentResolver;\n\n /**\n * Receives listeners declared with `on*` props.\n *\n * Without a dispatcher those props are ignored (with one warning),\n * which keeps headless graph construction and non-interactive\n * renders working.\n */\n dispatcher?: UiInputDispatcher;\n\n /**\n * Lets a modifier read its node's box and follow it.\n *\n * Supplied by the runtime, which is where the layout engine lives.\n * Without it `host.onLayout` warns once and does nothing, as event\n * registration does without a dispatcher.\n */\n layout?: UiModifierLayout;\n\n /**\n * Lets a modifier move focus, read whether its node has it, and\n * follow it. Supplied by the runtime, which owns the focus manager.\n * Without it those calls warn once and do nothing, as `onLayout`\n * does without layout access.\n */\n focus?: UiModifierFocus;\n\n /**\n * Lets a modifier read the values its node inherits and follow them.\n * Without it `host.environment` answers with the key's default —\n * which is what an unprovided key resolves to anyway — and\n * `onEnvironment` warns once.\n */\n environment?: UiModifierEnvironment;\n\n /**\n * Drives the animations a `transition` prop asks for.\n *\n * Supplied by the runtime, which owns the driver and the `ticks`\n * phase that advances it. Without one a `transition` is still\n * validated — a typo in it throws whether or not anything animates —\n * and every write lands directly, so a headless build renders the\n * final values.\n */\n animations?: AnimationDriver;\n\n /**\n * Remembers which node currently answers to each shared-element\n * name, so a node arriving under a name can morph from where the\n * node leaving under it stood.\n *\n * Supplied by the runtime, one per runtime, for the reason the\n * driver is: several runtimes share a worker in the playground, and\n * a shared registry would let one runtime's element morph from\n * another's. Without one the `sharedElement` modifier warns once and\n * the element simply appears, which is the correct degraded\n * behaviour — no animation rather than a wrong one.\n */\n sharedElements?: UiSharedElements;\n}\n\n/**\n * Where one reconcile pass has got to in the parent's existing\n * children while matching the unkeyed definitions among them.\n *\n * An unkeyed definition has no identity of its own, so it takes the\n * first child that is still unclaimed and of the right type. Searching\n * for that from the start of the list every time is what made a flat\n * list of n unkeyed children cost n²/2 comparisons: definition i walks\n * past the i entries the definitions before it already took. A cursor\n * naming where the next search may begin walks that ground once across\n * the whole pass instead of once per definition.\n *\n * There is a cursor per node type rather than one for the pass, because\n * a single cursor can only be moved past entries that are *claimed*,\n * and a run of those is not the only thing a search wastes its time on.\n * A list of two interleaved types whose definitions all want one of\n * them has an unclaimed entry of the wrong type at the front for the\n * whole pass, which pins one shared cursor at zero and leaves every\n * definition rescanning the list. Per type, that entry is skipped for\n * good, and each type's searching adds up to one walk of the list.\n *\n * It is pass state rather than builder state on purpose. `reconcile`\n * re-enters itself, through `reconcileChildren` for every matched child\n * and through the deferred-children path for a fragment whose binding\n * emitted mid-pass, and each of those passes has its own `existing`\n * snapshot and its own `matched` set. Cursors held on the builder would\n * be shared between them and would point into the wrong list.\n */\ninterface UnkeyedMatchState {\n /** The parent's children when the pass began, in tree order. */\n readonly existing: readonly UiNode[];\n /**\n * Per node type, the index where the next search for that type may\n * begin. Built on the first unkeyed match rather than with the pass,\n * so the leaf nodes a pass recurses into, which have no children to\n * match at all, do not each pay for a map they never read.\n */\n cursors: Map<UiNodeType, number> | null;\n}\n\n/**\n * Converts declarative UiElements into runtime UiNodes.\n *\n * The builder is intentionally separated from UiGraph.\n *\n * UiGraph owns the runtime state.\n * UiGraphBuilder owns construction of that state from\n * declarative definitions.\n *\n * Calling build() again with a changed definition reconciles\n * the existing tree instead of destroying it:\n *\n * - matching node → update properties\n * - new node → create\n * - missing node → destroy\n * - keyed move → reorder\n * - observable child → fragment anchor + children binding\n * - component child → fragment anchor + mounted host\n */\nexport class UiGraphBuilder {\n private readonly refs = new Map<UiNode, UiNodeRef>();\n private readonly modifiers = new Map<UiNode, UiModifierSet>();\n private nextChildrenBindingId = 0;\n\n /**\n * Depth of the currently running reconcile pass.\n *\n * reconcileChildren recurses into itself and is also re-entered by\n * UiChildrenBinding when an observable emits. Only the outermost\n * pass flushes onMount hooks, so components observe a fully built\n * subtree rather than a half-reconciled one.\n */\n private reconcileDepth = 0;\n /** Fragments whose pass is on the stack, and the emissions they must run afterwards. */\n private readonly reconciling = new Set<UiNode>();\n private readonly deferredChildren = new Map<UiNode, readonly UiChild[]>();\n\n private readonly components: ComponentResolver | undefined;\n\n private readonly dispatcher: UiInputDispatcher | undefined;\n\n private readonly layout: UiModifierLayout | undefined;\n private readonly focus: UiModifierFocus | undefined;\n private readonly modifierEnvironment: UiModifierEnvironment | undefined;\n private readonly animations: AnimationDriver | undefined;\n private readonly sharedElements: UiSharedElements | undefined;\n\n /** Ensures the missing-dispatcher warning is emitted at most once. */\n private warnedAboutDispatcher = false;\n /** The same, for a `transition` with no driver behind it. */\n private warnedAboutAnimations = false;\n /** The same, for a list that arrived from an observable with no keys on it. */\n private warnedAboutIndexKeys = false;\n\n constructor(\n private readonly graph: UiGraph,\n options: UiGraphBuilderOptions = {}\n ) {\n this.components = options.components;\n this.dispatcher = options.dispatcher;\n this.layout = options.layout;\n this.focus = options.focus;\n this.modifierEnvironment = options.environment;\n this.animations = options.animations;\n this.sharedElements = options.sharedElements;\n }\n\n /**\n * Builds (or reconciles) a subtree.\n *\n * Returns the runtime UiNode corresponding to the root of\n * the supplied definition.\n *\n * When no parent is supplied the definition is rendered as\n * the child of the graph root, so repeated calls update the\n * previously rendered tree in place.\n *\n * Note that observable and component definitions produce a\n * transparent Fragment anchor, which is never a valid layout root.\n * Callers that need a layout root must supply a plain UiElement.\n */\n build(definition: UiChild, parentId?: string): UiNode {\n const parent = parentId === undefined ? this.graph.root : this.graph.requireNode(parentId);\n return this.reconcileChildren(parent, [definition]).nodes[0];\n }\n\n /**\n * Reconciles definitions against the existing children of\n * the parent node.\n *\n * Nodes are matched by key when one is supplied, otherwise\n * by position and type. Matched nodes are reused; missing\n * definitions are created, stale children destroyed, and\n * keyed children moved into definition order.\n *\n * Observable children are anchored by invisible Fragment nodes.\n * A UiChildrenBinding subscribes to the observable and reconciles\n * the fragment's children on each emission.\n */\n reconcileChildren(parent: UiNode, definitions: readonly UiChild[]): { nodes: UiNode[]; changed: boolean } {\n if (this.reconciling.has(parent)) {\n // A binding emitted into a fragment whose own pass is still on the\n // stack: a component body, run while its list was being\n // reconciled, wrote to the observable that holds the list. The\n // pass in progress works from a snapshot of the chain and would\n // continue over one a second pass had rewritten, leaving nodes\n // registered but unreachable. Keep the newest definitions and run\n // them once the pass returns; only the last emission matters.\n this.deferredChildren.set(parent, definitions);\n return { nodes: this.collectChildren(parent), changed: false };\n }\n this.reconciling.add(parent);\n this.reconcileDepth++;\n let result: { nodes: UiNode[]; changed: boolean };\n try {\n result = this.reconcile(parent, definitions);\n } finally {\n this.reconcileDepth--;\n this.reconciling.delete(parent);\n }\n const deferred = this.deferredChildren.get(parent);\n if (deferred !== undefined) {\n this.deferredChildren.delete(parent);\n return this.reconcileChildren(parent, deferred);\n }\n // Deliberately outside the finally: a pass that threw left the\n // tree half-built, and mounting components onto it would only\n // widen the damage.\n if (this.reconcileDepth === 0) {\n this.components?.flushMounts();\n }\n return result;\n }\n\n private reconcile(parent: UiNode, definitions: readonly UiChild[]): { nodes: UiNode[]; changed: boolean } {\n const existing = this.collectChildren(parent);\n const matched = new Set<UiNode>();\n const unkeyed: UnkeyedMatchState = { existing, cursors: null };\n if (!this.warnedAboutIndexKeys && definitions.length > 1 && parent.type === UiNodeType.Fragment) {\n // A list that arrived from an observable and carries no keys is\n // matched by position, so inserting a row anywhere but the end\n // shifts every row after it onto a different node: state moves\n // between rows and an animation runs on the wrong element. The\n // fallback is silent otherwise, and silence is the defect.\n // `each` supplies the key; this is the hand-written case.\n this.warnedAboutIndexKeys = definitions.every(child => this.isIndexKeyed(child));\n if (this.warnedAboutIndexKeys) {\n console.warn(\n `A list of ${definitions.length} children under '${parent.id}' arrived from an observable with no keys, ` +\n `so it is reconciled by index. Give each child a key, or build the list with \\`each\\`, ` +\n `which supplies one: each(items, 'id', item => ...).`\n );\n }\n }\n const result: UiNode[] = [];\n let changed = false;\n let cursor: UiNode | null = parent.firstChild;\n\n for (const [index, definition] of definitions.entries()) {\n if (isObservable(definition)) {\n const fragment = this.reconcileObservableChild(parent, index, definition, matched, cursor);\n if (fragment !== undefined) {\n if (this.moveBefore(parent, fragment, cursor)) {\n changed = true;\n }\n cursor = fragment.nextSibling;\n result.push(fragment);\n }\n continue;\n }\n\n if (isComponentLikeElement(definition)) {\n const anchor = this.reconcileComponentChild(parent, index, definition, matched, cursor);\n if (this.moveBefore(parent, anchor, cursor)) {\n changed = true;\n }\n cursor = anchor.nextSibling;\n result.push(anchor);\n continue;\n }\n\n let node = this.matchNode(parent, definition, unkeyed, matched);\n if (node === undefined) {\n const id = this.createNodeId(parent, definition, index);\n const stale = this.graph.getNode(id);\n if (stale !== undefined) {\n if (stale.parent !== parent) {\n throw new Error(`Node id '${id}' is already used outside parent '${parent.id}'.`);\n }\n // The stale child is being replaced; make sure the cursor\n // does not keep pointing at a detached node.\n if (cursor === stale) {\n cursor = stale.nextSibling;\n }\n this.removeSubtree(stale);\n // Prevent the final cleanup from removing it a second time.\n matched.add(stale);\n changed = true;\n }\n node = this.createNode(parent, definition, index);\n matched.add(node);\n changed = true;\n } else {\n matched.add(node);\n this.reconcileProps(node, definition.props);\n if (this.reconcileChildren(node, definition.children).changed) {\n changed = true;\n }\n }\n if (this.moveBefore(parent, node, cursor)) {\n changed = true;\n }\n cursor = node.nextSibling;\n result.push(node);\n }\n\n for (const node of existing) {\n if (!matched.has(node)) {\n this.removeSubtree(node);\n changed = true;\n }\n }\n if (changed) {\n this.graph.markDirty(parent, DirtyFlags.Children);\n }\n return { nodes: result, changed };\n }\n\n /**\n * Reconciles a single observable child definition.\n *\n * Creates or reuses a Fragment anchor under the parent and ensures\n * a UiChildrenBinding is subscribed to the observable.\n */\n private reconcileObservableChild(\n parent: UiNode,\n index: number,\n observable: Observable<UiChild | readonly UiChild[]>,\n matched: Set<UiNode>,\n cursor: UiNode | null\n ): UiNode {\n const fragmentId = this.createFragmentId(parent, index);\n let fragment = this.graph.getNode(fragmentId);\n\n if (fragment !== undefined && fragment.type !== UiNodeType.Fragment) {\n // A non-fragment node occupied this slot; remove it so the\n // fragment can take its place.\n if (cursor === fragment) {\n cursor = fragment.nextSibling;\n }\n this.removeSubtree(fragment);\n matched.add(fragment);\n fragment = undefined;\n }\n\n if (fragment === undefined) {\n fragment = this.graph.createNode(fragmentId, UiNodeType.Fragment);\n this.graph.insertBefore(parent, fragment, cursor);\n } else {\n matched.add(fragment);\n }\n\n const existingBinding = this.graph.getChildrenBindingForNode(fragment);\n if (existingBinding === undefined || existingBinding.observable !== observable) {\n if (existingBinding !== undefined) {\n this.graph.unbindChildren(fragment);\n }\n const bindingId = this.nextChildrenBindingId++;\n const binding = new UiChildrenBinding(bindingId, parent.id, fragment.id, observable, this.graph, this);\n this.graph.bindChildren(fragment, binding);\n }\n\n return fragment;\n }\n\n /**\n * Reconciles a single component child definition.\n *\n * Creates or reuses a Fragment anchor under the parent, asks the\n * resolver for the component's current output, and reconciles that\n * output as the anchor's children.\n *\n * The anchor — not the rendered node — is the component's identity.\n * That keeps identity stable when the component renders a different\n * root element type, lets the output be an Observable (which becomes\n * a children binding on the anchor for free), and makes host\n * teardown a plain consequence of the anchor being removed.\n */\n private reconcileComponentChild(\n parent: UiNode,\n index: number,\n element: ComponentLikeElement,\n matched: Set<UiNode>,\n cursor: UiNode | null\n ): UiNode {\n const resolver = this.components;\n if (resolver === undefined) {\n throw new Error(\n `Component '${element.tag}' was passed to a UiGraphBuilder with no ComponentResolver. ` +\n `Construct the builder with { components } to mount components.`\n );\n }\n if ((element.props as Record<string, unknown>)[MODIFIERS_PROP] !== undefined) {\n // A component's root may be a fragment or an observable, so\n // \"the host node\" a modifier would attach to is not well\n // defined. Put them on an element inside the component instead.\n throw new Error(\n `Component '${element.tag}' cannot take 'modifiers': a component's node is its anchor, which is a ` +\n `fragment with no box and no paint, so there is nothing for a modifier to attach to. Put them on an ` +\n `element the component renders, or, if the component offers it, pass 'rootModifiers', which is the ` +\n `convention for a component that places them on its own root element (see 'modifiersOf').`\n );\n }\n\n const anchorId = this.createComponentAnchorId(parent, element, index);\n let anchor = this.graph.getNode(anchorId);\n\n if (anchor !== undefined && anchor.type !== UiNodeType.Fragment) {\n // A non-fragment node occupied this slot; remove it so the\n // anchor can take its place.\n if (cursor === anchor) {\n cursor = anchor.nextSibling;\n }\n this.removeSubtree(anchor);\n matched.add(anchor);\n anchor = undefined;\n }\n\n if (anchor === undefined) {\n anchor = this.graph.createNode(anchorId, UiNodeType.Fragment);\n this.graph.insertBefore(parent, anchor, cursor);\n } else {\n matched.add(anchor);\n }\n\n this.reconcileChildren(anchor, [resolver.resolve(element, anchorId)]);\n return anchor;\n }\n\n /**\n * Removes a subtree, releasing any component hosts it anchors.\n *\n * Every node removal in this class goes through here so that a host\n * can never outlive the nodes it produced. Hosts are released from\n * the outside in: a parent component's onUnmount runs before its\n * children's, matching the order in which the subtree is leaving.\n */\n private removeSubtree(node: UiNode): void {\n const resolver = this.components;\n const stack: UiNode[] = [node];\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (resolver !== undefined && current.type === UiNodeType.Fragment) {\n resolver.release(current.id);\n }\n const ref = this.refs.get(current);\n if (ref !== undefined) {\n this.refs.delete(current);\n ref(null);\n }\n // Before graph.removeNode, so a modifier's teardown still sees an\n // intact node and its bindings.\n const modifiers = this.modifiers.get(current);\n if (modifiers !== undefined) {\n this.modifiers.delete(current);\n modifiers.detach();\n }\n // An animation outliving its node is the same leak a modifier\n // would be: the driver holds the cell, the cell holds the node.\n if (current.transitions !== null) {\n current.transitions.release();\n current.transitions = null;\n }\n for (let child = current.firstChild; child !== null; child = child.nextSibling) {\n stack.push(child);\n }\n }\n this.graph.removeNode(node);\n }\n\n /**\n * Hands the node to a `ref` callback once, and again only when the\n * callback identity changes. Removal calls the current one with null.\n */\n private reconcileRef(node: UiNode, value: unknown): void {\n const previous = this.refs.get(node);\n if (typeof value !== 'function') {\n if (previous !== undefined) {\n this.refs.delete(node);\n previous(null);\n }\n return;\n }\n const ref = value as UiNodeRef;\n if (previous === ref) {\n return;\n }\n if (previous !== undefined) {\n previous(null);\n }\n this.refs.set(node, ref);\n ref(node);\n }\n\n /**\n * Finds an existing child that should host a definition.\n *\n * Keyed definitions match by the id derived from the key, which is a\n * map lookup and needs no search. Unkeyed definitions match the first\n * unmatched child with the same type, searched for from the pass's\n * cursor rather than from the head of the list.\n */\n private matchNode(\n parent: UiNode,\n definition: UiElement,\n unkeyed: UnkeyedMatchState,\n matched: Set<UiNode>\n ): UiNode | undefined {\n const key = this.elementKey(definition);\n if (key !== undefined) {\n const candidate = this.graph.getNode(`${parent.id}:${key}`);\n if (candidate === undefined || candidate.parent !== parent || candidate.type !== definition.type) {\n return undefined;\n }\n if (matched.has(candidate)) {\n throw new Error(`Duplicate key '${key}' in parent '${parent.id}'.`);\n }\n return candidate;\n }\n\n const existing = unkeyed.existing;\n const type = definition.type;\n let cursors = unkeyed.cursors;\n if (cursors === null) {\n cursors = new Map<UiNodeType, number>();\n unkeyed.cursors = cursors;\n }\n\n // Walk this type's cursor to the first entry that could answer for\n // it, dropping everything passed over on the way. Both grounds for\n // dropping an entry are settled for the rest of the pass:\n //\n // Claimed. Nothing ever leaves `matched` within a pass and a\n // claimed entry is never returned twice, so an entry claimed now is\n // one every later search would skip anyway.\n //\n // Wrong type. This is the part worth pausing on, because it is only\n // permanent for a cursor that belongs to one type. A node's type is\n // fixed from the moment it is created, and a type change is a\n // different node replacing this one rather than this one changing,\n // so an entry that is not a T now will not be a T later either, and\n // no later definition of type T can want it. A cursor shared by\n // every type could make no such promise: the entry it stepped over\n // was merely the wrong type for the definition in hand, and the\n // next definition might be exactly the type it is.\n //\n // What the cursor may not do is move past the entry it lands on. A\n // keyed definition can claim an entry anywhere in the list, so the\n // free entries a cursor has already reached must stay reachable;\n // the walk resumes here next time and re-tests this entry then.\n let index = cursors.get(type) ?? 0;\n while (index < existing.length && (matched.has(existing[index]) || existing[index].type !== type)) {\n index++;\n }\n cursors.set(type, index);\n\n // The walk stopped either on an unclaimed entry of the right type,\n // which is the first such entry in the list and so the one `find`\n // over the whole array would have returned, or off the end.\n return index < existing.length ? existing[index] : undefined;\n }\n\n /**\n * Creates a runtime node for a new definition.\n *\n * The node id is derived from the parent and the definition\n * so that it stays stable across reconciles. The caller is\n * responsible for removing any stale node that already holds\n * the id.\n */\n private createNode(parent: UiNode, definition: UiElement, index: number): UiNode {\n const id = this.createNodeId(parent, definition, index);\n const node = this.graph.createNode(id, definition.type);\n this.reconcileProps(node, definition.props, parent);\n this.reconcileChildren(node, definition.children);\n return node;\n }\n\n /**\n * Positions a node immediately before the reference node.\n */\n private moveBefore(parent: UiNode, node: UiNode, reference: UiNode | null): boolean {\n if (node.parent !== parent) {\n this.graph.insertBefore(parent, node, reference);\n return true;\n }\n if (node === reference || node.nextSibling === reference) {\n return false;\n }\n this.graph.detachNode(node);\n this.graph.insertBefore(parent, node, reference);\n return true;\n }\n\n /**\n * Reconciles declarative properties onto a runtime node.\n *\n * Plain values are written directly. RxJS Observables become\n * UiGraph bindings. Bindings are kept when the Observable\n * instance is unchanged and torn down when a property stops\n * being reactive.\n */\n private reconcileProps(node: UiNode, props: UiProps, parent?: UiNode): void {\n // Installed before this pass writes anything, so a re-render that\n // both adds a transition and changes the value it covers animates\n // rather than jumping once and animating from then on. The prop is\n // read out of `props` directly because the loop below has not run\n // yet; it is skipped there, like `key`, `ref` and `modifiers`.\n this.reconcileTransitions(node, (props as Record<string, unknown>)[TRANSITION_PROP]);\n const present = new Set<string>();\n const presentEvents = new Set<string>();\n let declaredModifiers: unknown;\n\n for (const [property, value] of Object.entries(props)) {\n if (property === KEY_PROP) {\n continue;\n }\n if (property === REF_PROP) {\n this.reconcileRef(node, value);\n continue;\n }\n if (property === MODIFIERS_PROP) {\n declaredModifiers = value;\n continue;\n }\n if (property === TRANSITION_PROP) {\n continue;\n }\n if (isEventProp(property, value)) {\n this.reconcileEventProp(node, property, value as UiEventListener, presentEvents);\n continue;\n }\n this.assertKnownProp(node, property, value);\n present.add(property);\n this.assertValidValue(node, property, value);\n // The graph indexes bindings by node and property, so this asks\n // it directly rather than copying the node's whole binding set\n // into a lookup table on every reconcile.\n const existingBinding = this.graph.getBindingForProperty(node, property as NodeProperty);\n if (this.isObservable(value)) {\n if (existingBinding !== undefined && existingBinding.observable === value) {\n continue;\n }\n if (existingBinding !== undefined) {\n this.graph.unbind(existingBinding);\n }\n this.graph.bind(node, property as NodeProperty, value, propertyEffects(property));\n continue;\n }\n if (existingBinding !== undefined) {\n this.graph.unbind(existingBinding);\n }\n this.graph.updateNodeProperty(node, property as NodeProperty, value, propertyEffects(property));\n }\n\n // Tear down what this render stopped declaring. Both lists are\n // materialised up front because unbinding mutates the indexes they\n // come from.\n for (const binding of this.graph.getBindingsForNode(node)) {\n if (!present.has(binding.property)) {\n this.graph.unbind(binding);\n }\n }\n\n for (const binding of this.graph.getEventBindingsForNode(node)) {\n if (!presentEvents.has(binding.type)) {\n this.graph.unbindEvent(node, binding);\n }\n }\n\n if (node.environment === null && parent?.environment != null) {\n // A freshly built node is not in the tree yet — the builder\n // writes its props and builds its children before making the\n // edge — so what it inherits is not reachable from it. Seeding\n // it here, after the node's own provider props are written and\n // before its modifiers attach, is what makes `host.environment`\n // answer with the theme the node will actually be under rather\n // than with the default. `inheritEnvironment` recomputes at\n // attach and keeps this instance when it agrees, so nothing is\n // marked dirty twice.\n node.environment = this.graph.buildNodeEnvironment(node, parent);\n }\n\n this.reconcileModifiers(node, declaredModifiers);\n }\n\n /**\n * Attaches, updates and detaches the node's modifiers.\n *\n * Called once the declared props are written and before the\n * children are reconciled, so a modifier sees the element's own\n * values first. An element that stops declaring `modifiers` gets an\n * empty list, which detaches everything it had.\n */\n private reconcileModifiers(node: UiNode, declared: unknown): void {\n const existing = this.modifiers.get(node);\n if (declared === undefined) {\n if (existing !== undefined) {\n existing.detach();\n this.modifiers.delete(node);\n }\n return;\n }\n const list = assertModifierList(node, declared);\n const set =\n existing ??\n new UiModifierSet(\n node,\n this.graph,\n this.dispatcher,\n this.layout,\n this.focus,\n this.modifierEnvironment,\n this.animations,\n this.sharedElements\n );\n if (existing === undefined) {\n this.modifiers.set(node, set);\n }\n set.reconcile(list);\n }\n\n /**\n * Installs, updates or removes the node's transitions.\n *\n * An element that stops declaring `transition` loses it entirely,\n * cancelling whatever was in flight — the same rule `modifiers`\n * follows, and the same reason: what an element declares this render\n * is the whole truth about it.\n */\n private reconcileTransitions(node: UiNode, declared: unknown): void {\n if (declared === undefined) {\n if (node.transitions !== null) {\n node.transitions.release();\n node.transitions = null;\n }\n return;\n }\n const specs = assertTransitionMap(node.id, declared, name => findPropertyDefinition(name) !== undefined);\n const driver = this.animations;\n if (driver === undefined) {\n // Validated but not driven: a headless build writes final values.\n this.warnMissingAnimations();\n return;\n }\n let transitions = node.transitions;\n if (transitions === null) {\n transitions = new NodeTransitions(node, this.graph, driver);\n node.transitions = transitions;\n }\n (transitions as NodeTransitions).setSpecs(specs as ReadonlyMap<string, UiTransitionSpec>);\n }\n\n private warnMissingAnimations(): void {\n if (this.warnedAboutAnimations) {\n return;\n }\n this.warnedAboutAnimations = true;\n console.warn(\n `An element declared a 'transition', but the UiGraphBuilder was constructed without an animation driver. ` +\n `Values are written directly. Construct it with { animations }, as the runtime does.`\n );\n }\n\n /** The modifiers attached to a node, for the inspector and for tests. */\n modifiersFor(node: UiNode): UiModifierSet | undefined {\n return this.modifiers.get(node);\n }\n\n /**\n * Rejects a prop nothing will ever read.\n *\n * Every property layout, paint, input or the environment consults is\n * registered in UiProperties, so a name outside the registry is a\n * typo (`widht`) or a prop meant for a component rather than an\n * element. Either would otherwise be stored on the node and silently\n * ignored; `on*` typos have always thrown, and so does this now.\n */\n private assertKnownProp(node: UiNode, property: string, value: unknown): void {\n if (findPropertyDefinition(property) !== undefined) {\n return;\n }\n if (eventTypeForProp(property) !== undefined) {\n throw new Error(\n `Event prop '${property}' on node '${node.id}' must be a function, got ${describeValueType(value)}.`\n );\n }\n const suggestion = closestPropertyName(property, [...knownPropertyNames(), ...knownEventPropNames()]);\n throw new Error(\n `Unknown prop '${property}' on node '${node.id}'.` +\n (suggestion !== undefined ? ` Did you mean '${suggestion}'?` : '') +\n ` Props must be registered UI properties (width, padding, backgroundColor, …), 'key', 'ref',` +\n ` 'modifiers', 'transition', or on* event handlers.`\n );\n }\n\n /**\n * Rejects a value a registered property cannot hold.\n *\n * Only properties with a closed set of values declare a `validate`,\n * so this is one undefined check for the other ninety-odd. An\n * Observable is not checked here — its values arrive later, and a\n * bound value is the reader's to reject, as a bound length is\n * rejected at layout.\n */\n private assertValidValue(node: UiNode, property: string, value: unknown): void {\n const definition = findPropertyDefinition<unknown>(property);\n if (definition?.validate === undefined || this.isObservable(value)) {\n return;\n }\n const message = definition.validate(value);\n if (message !== undefined) {\n throw new Error(`Invalid '${property}' on node '${node.id}': ${message}`);\n }\n }\n\n /**\n * Binds one `on*` prop to the input dispatcher.\n *\n * An existing binding for the same event is kept when the handler\n * identity is unchanged, so re-reconciling a stable tree does not\n * churn listener registrations.\n */\n private reconcileEventProp(\n node: UiNode,\n property: string,\n handler: UiEventListener,\n presentEvents: Set<string>\n ): void {\n const type = eventTypeForProp(property);\n if (type === undefined) {\n throw new Error(\n `Unknown event prop '${property}' on node '${node.id}'. ` +\n `Expected one of: ${knownEventPropNames().join(', ')}.`\n );\n }\n presentEvents.add(type);\n\n const existing = this.graph.getEventBindingForType(node, type);\n if (existing !== undefined) {\n if (existing.listener === handler) {\n return;\n }\n this.graph.unbindEvent(node, existing);\n }\n\n if (this.dispatcher === undefined) {\n this.warnMissingDispatcher(property);\n return;\n }\n this.graph.bindEvent(node, new UiEventBinding(node, type, handler, this.dispatcher));\n }\n\n private warnMissingDispatcher(property: string): void {\n if (this.warnedAboutDispatcher) {\n return;\n }\n this.warnedAboutDispatcher = true;\n console.warn(\n `UiGraphBuilder received event prop '${property}' but was constructed without a dispatcher, ` +\n `so it and any further event props are ignored. Pass { dispatcher } to make the tree interactive.`\n );\n }\n\n /**\n * Determines whether a prop's value is an Observable to bind.\n *\n * The shared structural check rather than `instanceof Observable`,\n * for the reason `isObservable` in `UiElement.ts` already gives for\n * children: an application whose bundle holds a second copy of rxjs\n * produces observables that fail an `instanceof` against this one.\n * Children were structural and props were not, so an app-authored\n * `combineLatest` reached a child correctly and reached a prop as a\n * plain object, which is written once and never updates. It fails\n * silently, which is what makes it worth a named method: nothing\n * warns, nothing throws, and the element simply has no text.\n */\n private isObservable(value: unknown): value is Observable<unknown> {\n return isObservable(value);\n }\n\n /** Whether a child would fall back to its position for identity. */\n private isIndexKeyed(child: UiChild): boolean {\n if (isObservable(child)) {\n return true;\n }\n if (isComponentLikeElement(child)) {\n return child.key === undefined || child.key === null;\n }\n return this.elementKey(child) === undefined;\n }\n\n /**\n * The reconciliation key of a definition, if any.\n */\n private elementKey(definition: UiElement): string | undefined {\n const key = definition.props[KEY_PROP];\n if (key === undefined || key === null) {\n return undefined;\n }\n return String(key);\n }\n\n /**\n * Collects the current children of a node in tree order.\n */\n private collectChildren(parent: UiNode): UiNode[] {\n const children: UiNode[] = [];\n for (let child = parent.firstChild; child !== null; child = child.nextSibling) {\n children.push(child);\n }\n return children;\n }\n\n /**\n * Generates a stable runtime node id.\n *\n * Keyed nodes use `parent:key`. Unkeyed nodes use their\n * position within the parent, which stays stable as long as\n * the surrounding structure does not reorder.\n */\n private createNodeId(parent: UiNode, definition: UiElement, index: number): string {\n const key = this.elementKey(definition);\n if (key !== undefined) {\n return `${parent.id}:${key}`;\n }\n return `${parent.id}:${index}`;\n }\n\n /**\n * Generates a stable id for an observable-child fragment anchor.\n */\n private createFragmentId(parent: UiNode, index: number): string {\n return `${parent.id}:fragment:${index}`;\n }\n\n /**\n * Generates a stable id for a component's fragment anchor.\n *\n * Keyed components use their key, so a component keeps its instance\n * across reorders. Unkeyed components fall back to position, which\n * is stable only while the surrounding structure does not reorder —\n * the same trade-off unkeyed elements make.\n */\n private createComponentAnchorId(parent: UiNode, element: ComponentLikeElement, index: number): string {\n const key = element.key === undefined || element.key === null ? index : element.key;\n return `${parent.id}:component:${String(key)}`;\n }\n}\n","import { BehaviorSubject, isObservable } from 'rxjs';\n\nimport { UiNodeType } from '../graph/UiNodeType';\nimport { percent, type UiTrackSize } from '../layout/UiLength';\nimport { defineModifier } from '../modifiers/UiModifier';\nimport type { UiModifierHost } from '../modifiers/UiModifierHost';\nimport { Box, Column, Grid, Row } from './UiComponents';\nimport { isUiElement, type UiChild, type UiElement } from './UiElement';\nimport type { Reactive } from './UiElementProps';\n\nexport type LazyAxis = 'column' | 'row';\n\nexport interface LazyListOptions {\n /**\n * Number of items now. A list whose data changes takes an\n * Observable: `LazyColumn` binds it through the `lazySource`\n * modifier, so the window learns the new count with the same\n * lifetime as the node and without the caller holding the window.\n */\n readonly count: Reactive<number>;\n /**\n * Expected extent of an item along the scroll axis, used for items\n * that have not been measured yet. The closer it is, the less the\n * scrollbar and offsets adjust as items come into view.\n */\n readonly estimatedExtent: number;\n /** Items mounted beyond each edge of the viewport. Default 3. */\n readonly overscan?: number;\n /** Identity of an item, for reconciliation. Default: its index. */\n readonly key?: (index: number) => string | number;\n /**\n * Viewport extent assumed before the first layout, so the first\n * frame mounts a sensible number of items. Default 600.\n */\n readonly initialViewportExtent?: number;\n /**\n * Any value that changes when what an index *means* changes — a\n * sort, a filter, a page of data arriving. The mounted items are\n * re-rendered and their measurements dropped; the indices and their\n * keys do not move, so the rows are reconciled in place rather than\n * rebuilt.\n */\n readonly revision?: Reactive<unknown>;\n /**\n * Makes the list a **lazy grid**: every mounted row and the header\n * share one set of column tracks. Without it the list is a plain\n * column of wrapped items.\n */\n readonly grid?: LazyGridOptions;\n}\n\n/**\n * The tracks a lazy grid's rows share, and the header that shares them.\n *\n * The grid work left this here: a table whose virtualized\n * rows line up with its header needs the rows to be items of one grid,\n * because tracks cannot be sized across grids that cannot see each\n * other. So the scroll container holds a single Grid, and every row is\n * a `subgrid: 'columns'` item in it.\n */\nexport interface LazyGridOptions {\n /** The shared column tracks. */\n readonly columns: readonly UiTrackSize[];\n /** Space between the columns, in the header and in every row. */\n readonly columnGap?: number;\n /**\n * Drawn above the rows in the same tracks. Like a row it must be a\n * `Grid` with `subgrid: 'columns'`; unlike a row it is rendered once,\n * and `position: 'sticky'` on it holds it at the top while the rows\n * scroll under it.\n */\n readonly header?: UiChild;\n}\n\nexport type LazyItemRenderer = (index: number) => UiChild;\n\n/** What the host reports about the scroll container each frame. */\nexport interface VirtualViewport {\n /** Scroll offset along the list axis. */\n scroll: number;\n /** Visible extent along the list axis. */\n extent: number;\n /**\n * Content before the first item — a lazy grid's header. The window's\n * offsets are measured from the first item, so this is what makes\n * \"which row is at the top of the viewport\" the same question with a\n * header as without one. Zero by default.\n */\n lead?: number;\n}\n\nexport interface VirtualItemMeasure {\n index: number;\n /** Outer extent of the mounted item along the axis. */\n extent: number;\n}\n\nexport interface VirtualUpdate {\n /**\n * How far the scroll offset must move so that the first mounted item\n * stays where the user sees it, after measurements changed the size\n * of the content above it. Zero almost always.\n */\n scrollAdjust: number;\n}\n\n/** Marks an item wrapper with its index so the host can measure it. */\nexport const VIRTUAL_INDEX_PROP = 'virtualIndex';\n/**\n * Marks a lazy grid's header, whose extent sits between the top of the\n * scrolled content and the first item — so the host can tell the window\n * how far the items start down the page.\n */\nexport const VIRTUAL_LEAD_PROP = 'virtualLead';\n/** The window sits on the scroll container under this property. */\nexport const VIRTUAL_WINDOW_PROP = 'virtualWindow';\n\n/**\n * The windowing behind LazyColumn / LazyRow.\n *\n * Only the items in the viewport plus an overscan band are mounted. The\n * rest is two spacer boxes whose extents come from an estimate per item,\n * corrected by the real extent of every item that has been measured, so\n * the total extent (and the scrollbar) settle toward the truth as the\n * user scrolls. Corrections are sparse — one entry per measured item\n * whose extent differs from the estimate — so a 100k-row list costs\n * memory proportional to what was seen, and offsets are estimate ×\n * index plus the corrections before that index.\n *\n * Children are emitted through a BehaviorSubject and reconciled by the\n * graph builder like any observable child: item identity is the key,\n * and an item that stays in the window keeps its node.\n */\nexport class UiVirtualWindow {\n readonly axis: LazyAxis;\n readonly children$: BehaviorSubject<UiElement[]>;\n\n private count: number;\n private readonly estimate: number;\n private readonly overscan: number;\n private readonly keyOf: (index: number) => string | number;\n private readonly renderItem: LazyItemRenderer;\n private readonly grid: LazyGridOptions | undefined;\n\n private first = 0;\n private last = -1;\n private leadExtent = -1;\n private trailExtent = -1;\n private readonly measured = new Map<number, number>();\n /** Sorted by index: measured extent minus the estimate. */\n private corrections: Array<{ index: number; delta: number }> = [];\n /** Wrappers of the items currently mounted, so a row that stays is not re-rendered. */\n private readonly mountedItems = new Map<number, UiElement>();\n /**\n * The last viewport the host reported, so a count or a revision\n * arriving between frames recomputes the window against where the\n * list actually is rather than against the top of it.\n */\n private viewport: VirtualViewport;\n /** The last few content extents, and the viewport extent last seen; see `checkViewport`. */\n private readonly recentContentExtents: number[] = [];\n private lastViewportExtent = -1;\n private viewportLandedOnContent = 0;\n private warnedAboutViewport = false;\n\n constructor(axis: LazyAxis, options: LazyListOptions, renderItem: LazyItemRenderer) {\n const count = typeof options.count === 'number' ? options.count : 0;\n assertCount(count);\n if (!(options.estimatedExtent > 0)) {\n throw new Error(`LazyList estimatedExtent must be positive, got ${String(options.estimatedExtent)}.`);\n }\n this.axis = axis;\n this.count = count;\n this.estimate = options.estimatedExtent;\n this.overscan = options.overscan ?? 3;\n this.keyOf = options.key ?? (index => index);\n this.renderItem = renderItem;\n this.grid = options.grid;\n this.children$ = new BehaviorSubject<UiElement[]>([]);\n this.viewport = { scroll: 0, extent: options.initialViewportExtent ?? 600 };\n this.update(this.viewport, []);\n }\n\n /** How many items the list has, mounted or not. */\n get length(): number {\n return this.count;\n }\n\n /**\n * A new item count.\n *\n * L5 shipped `count` as a plain number and left an observable one to\n * this tier, because a table whose rows change cannot invalidate its\n * window without it. Measurements past the new end are dropped — a\n * shorter list must not keep corrections for items it no longer has,\n * or its scrollbar would claim space that is not there.\n *\n * The mounted items are re-rendered as well. A different count is a\n * different list, so an item cached against index 1 may be a\n * different item now; keeping it would show stale content, and with a\n * key derived from the data it would collide with the row that took\n * its place.\n */\n setCount(count: number): void {\n assertCount(count);\n if (count === this.count) {\n return;\n }\n this.count = count;\n for (const index of this.measured.keys()) {\n if (index >= count) {\n this.measured.delete(index);\n }\n }\n this.corrections = this.corrections.filter(correction => correction.index < count);\n this.mountedItems.clear();\n this.refresh();\n }\n\n /**\n * Re-renders the mounted items and forgets what they measured.\n *\n * For when the data behind the indices moved — a sort, a filter — so\n * the row at index 5 is a different row. Keys do not change, so the\n * rows are reconciled in place: the text of a cell is written, not a\n * node rebuilt.\n */\n invalidate(): void {\n this.measured.clear();\n this.corrections = [];\n this.mountedItems.clear();\n this.refresh();\n }\n\n /** Indices currently mounted, inclusive; `last < first` when none. */\n get range(): { first: number; last: number } {\n return { first: this.first, last: this.last };\n }\n\n /** Offset of an item's leading edge from the start of the content. */\n offsetOf(index: number): number {\n let offset = index * this.estimate;\n for (const correction of this.corrections) {\n if (correction.index >= index) {\n break;\n }\n offset += correction.delta;\n }\n return offset;\n }\n\n totalExtent(): number {\n return this.offsetOf(this.count);\n }\n\n extentOf(index: number): number {\n return this.measured.get(index) ?? this.estimate;\n }\n\n /** The item whose span contains `offset`; clamped to the list. */\n indexAt(offset: number): number {\n if (this.count === 0) {\n return 0;\n }\n let low = 0;\n let high = this.count - 1;\n while (low < high) {\n const mid = (low + high + 1) >> 1;\n if (this.offsetOf(mid) <= offset) {\n low = mid;\n } else {\n high = mid - 1;\n }\n }\n return low;\n }\n\n /**\n * One step, run by the host each frame before layout: take the\n * measurements of mounted items, keep the first mounted item anchored,\n * recompute the window for the viewport, and emit children when the\n * window or the spacers changed.\n */\n update(viewport: VirtualViewport, measures: Iterable<VirtualItemMeasure>): VirtualUpdate {\n this.viewport = viewport;\n const anchorBefore = this.last >= this.first ? this.offsetOf(this.first) : 0;\n for (const measure of measures) {\n this.record(measure.index, measure.extent);\n }\n const anchorAfter = this.last >= this.first ? this.offsetOf(this.first) : 0;\n const scrollAdjust = anchorAfter - anchorBefore;\n\n const total = this.totalExtent();\n const header = viewport.lead ?? 0;\n this.checkViewport(viewport.extent, total + header);\n const scrolled = clamp(viewport.scroll + scrollAdjust, 0, Math.max(0, total + header - viewport.extent));\n // Offsets are measured from the first item, so a header above them\n // shifts what the viewport is looking at by its own extent.\n const scroll = Math.max(0, scrolled - header);\n let first = 0;\n let last = -1;\n if (this.count > 0) {\n first = Math.max(0, this.indexAt(scroll) - this.overscan);\n last = Math.min(this.count - 1, this.indexAt(scroll + Math.max(0, viewport.extent)) + this.overscan);\n }\n const lead = first <= last ? this.offsetOf(first) : 0;\n const trail = first <= last ? total - this.offsetOf(last + 1) : total;\n if (first !== this.first || last !== this.last || lead !== this.leadExtent || trail !== this.trailExtent) {\n this.first = first;\n this.last = last;\n this.leadExtent = lead;\n this.trailExtent = trail;\n this.children$.next(this.buildChildren());\n }\n return { scrollAdjust };\n }\n\n /**\n * Warns, once, when the viewport is the content.\n *\n * A lazy list bounded by nothing takes the extent of its rows, and its\n * host then reports that extent back as the viewport: every row is in\n * view, every row is mounted, and nothing is virtualised. The list\n * looks right the whole time, because whatever holds it clips it; it\n * only gets slower with every row it gains. Found in Sluice, where a\n * wrapper box gave the feed's list the height of its rows and forty\n * posts a second re-laid and re-mirrored a thousand of them.\n *\n * The tell is the viewport moving with the content. The host reports\n * the extent it laid out last frame, so a viewport that is the content\n * lands on a content extent this window computed an update or two ago,\n * and lands on the next one when the content grows again. Two landings\n * are the signal: a viewport that changes for a real reason, a window\n * resized, hits one of those numbers only by coincidence, and not twice.\n */\n private checkViewport(extent: number, content: number): void {\n if (!this.warnedAboutViewport && extent !== this.lastViewportExtent) {\n if (this.recentContentExtents.some(recent => Math.abs(recent - extent) < 0.5)) {\n this.viewportLandedOnContent++;\n if (this.viewportLandedOnContent >= 2) {\n this.warnedAboutViewport = true;\n const name = this.grid !== undefined ? 'LazyGrid' : this.axis === 'column' ? 'LazyColumn' : 'LazyRow';\n const size = this.axis === 'column' ? 'height' : 'width';\n const minimum = this.axis === 'column' ? 'minHeight' : 'minWidth';\n console.warn(\n `A ${name} of ${this.count} items has a viewport the size of its content (${Math.round(extent)}px), ` +\n `so every item is mounted and nothing is virtualised. The list is taking its items' ${size} ` +\n `rather than being bounded by what holds it: give it a definite ${size}, or, when a wrapper ` +\n `between it and a flex parent is the one growing, put ${minimum}: 0 on that wrapper.`\n );\n }\n }\n this.lastViewportExtent = extent;\n }\n if (this.recentContentExtents[this.recentContentExtents.length - 1] !== content) {\n this.recentContentExtents.push(content);\n if (this.recentContentExtents.length > 4) {\n this.recentContentExtents.shift();\n }\n }\n }\n\n /**\n * Recomputes the window against the last viewport and emits, whatever\n * the arithmetic says. The sentinels force the emission: the window\n * may land on exactly the same range while the items in it mean\n * something different.\n */\n private refresh(): void {\n this.first = 0;\n this.last = -1;\n this.leadExtent = -1;\n this.trailExtent = -1;\n this.update(this.viewport, []);\n }\n\n private record(index: number, extent: number): void {\n if (index < 0 || index >= this.count || !(extent >= 0)) {\n return;\n }\n if (this.measured.get(index) === extent) {\n return;\n }\n this.measured.set(index, extent);\n const delta = extent - this.estimate;\n let position = 0;\n while (position < this.corrections.length && this.corrections[position].index < index) {\n position++;\n }\n const existing = this.corrections[position];\n if (existing !== undefined && existing.index === index) {\n if (delta === 0) {\n this.corrections.splice(position, 1);\n } else {\n existing.delta = delta;\n }\n } else if (delta !== 0) {\n this.corrections.splice(position, 0, { index, delta });\n }\n }\n\n private buildChildren(): UiElement[] {\n const children: UiElement[] = [];\n if (this.grid?.header !== undefined) {\n children.push(markRow(this.grid.header, { key: 'lazy:header', [VIRTUAL_LEAD_PROP]: true }, 'The header of'));\n }\n children.push(this.spacer('lazy:lead', this.leadExtent));\n for (const index of this.mountedItems.keys()) {\n if (index < this.first || index > this.last) {\n this.mountedItems.delete(index);\n }\n }\n for (let index = this.first; index <= this.last; index++) {\n let wrapper = this.mountedItems.get(index);\n if (wrapper === undefined) {\n wrapper = this.wrap(index, this.renderItem(index));\n this.mountedItems.set(index, wrapper);\n }\n children.push(wrapper);\n }\n children.push(this.spacer('lazy:trail', this.trailExtent));\n if (this.grid === undefined) {\n return children;\n }\n // One Grid holds the header, the spacers and every mounted row, so\n // the tracks are sized across all of them at once. The scroll\n // container's only child; its rows are contiguous, which is why the\n // grid takes no row gap — the window's offsets assume it.\n return [\n Grid(\n {\n key: 'lazy:grid',\n columns: this.grid.columns,\n columnGap: this.grid.columnGap,\n rowGap: 0,\n autoFlow: 'row',\n width: percent(100)\n },\n ...children\n )\n ];\n }\n\n private spacer(key: string, extent: number): UiElement {\n if (this.grid !== undefined) {\n // A spacer in a lazy grid is a grid item across every column, so\n // it occupies a whole row of its own rather than one cell.\n return Box({ key, height: extent, columnSpan: this.grid.columns.length });\n }\n return this.axis === 'column'\n ? Box({ key, height: extent, flexShrink: 0 })\n : Box({ key, width: extent, flexShrink: 0 });\n }\n\n /**\n * Marks an item so the host can find its index and measure it.\n *\n * A plain lazy list wraps the item in a flex container, so it\n * stretches across the list like a row in any column and its own\n * props are untouched. A lazy grid cannot: a row's cells have to be\n * items of the shared grid, so the row the renderer returns *is* the\n * wrapper, and the index is written onto it.\n */\n private wrap(index: number, item: UiChild): UiElement {\n const key = `lazy:${String(this.keyOf(index))}`;\n const props = { key, [VIRTUAL_INDEX_PROP]: index };\n if (this.grid !== undefined) {\n return markRow(item, props, `Row ${index} of`);\n }\n return this.axis === 'column' ? Column({ ...props, flexShrink: 0 }, item) : Row({ ...props, flexShrink: 0 }, item);\n }\n}\n\n/**\n * Copies the window's own props onto a row of a lazy grid.\n *\n * The row must be a `Grid` with `subgrid: 'columns'`, because that is\n * the only thing whose cells land in the tracks the header sized. The\n * check is here rather than in a comment: a row that is an ordinary\n * Row would line up by accident on the first frame and drift from the\n * header on the second.\n */\nfunction markRow(row: UiChild, props: Record<string, unknown>, subject: string): UiElement {\n if (!isUiElement(row) || row.type !== UiNodeType.Grid || row.props.subgrid !== 'columns') {\n throw new Error(`${subject} a lazy grid must be a Grid with subgrid: 'columns', so its cells share the tracks.`);\n }\n return { ...row, props: { ...row.props, ...props } };\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction assertCount(count: number): void {\n if (!(count >= 0) || !Number.isInteger(count)) {\n throw new Error(`LazyList count must be a non-negative integer, got ${String(count)}.`);\n }\n}\n\n/** What `lazySource` carries from a LazyColumn's props to its window. */\nexport interface LazySourceArgs {\n readonly window: UiVirtualWindow;\n readonly count: Reactive<number>;\n readonly revision?: Reactive<unknown>;\n /**\n * Makes the list a **lazy grid**: every mounted row and the header\n * share one set of column tracks. Without it the list is a plain\n * column of wrapped items.\n */\n readonly grid?: LazyGridOptions;\n}\n\n/**\n * The tracks a lazy grid's rows share, and the header that shares them.\n *\n * The grid work left this here: a table whose virtualized\n * rows line up with its header needs the rows to be items of one grid,\n * because tracks cannot be sized across grids that cannot see each\n * other. So the scroll container holds a single Grid, and every row is\n * a `subgrid: 'columns'` item in it.\n */\nexport interface LazyGridOptions {\n /** The shared column tracks. */\n readonly columns: readonly UiTrackSize[];\n /** Space between the columns, in the header and in every row. */\n readonly columnGap?: number;\n /**\n * Drawn above the rows in the same tracks. Like a row it must be a\n * `Grid` with `subgrid: 'columns'`; unlike a row it is rendered once,\n * and `position: 'sticky'` on it holds it at the top while the rows\n * scroll under it.\n */\n readonly header?: UiChild;\n}\n\n/**\n * Feeds a window the inputs that may change: the item count and the\n * revision of the data behind the indices.\n *\n * A modifier rather than a subscription taken in `LazyColumn`, because\n * a modifier's lifetime is exactly its node's — `own` releases the\n * subscription inside `removeSubtree`, before the node is gone — and\n * because attachment runs after the element's props and *before* its\n * children are reconciled, so a count that arrives with the tree is\n * already in the window when the first children are read from it.\n */\nexport const lazySource = defineModifier<LazySourceArgs>({\n name: 'lazySource',\n attach(host, args) {\n follow(host, args.count, value => args.window.setCount(value));\n if (args.revision !== undefined) {\n let first = true;\n follow(host, args.revision, () => {\n // The value the list was built with is not a change.\n if (first) {\n first = false;\n return;\n }\n args.window.invalidate();\n });\n }\n }\n});\n\nfunction follow<T>(host: UiModifierHost, source: Reactive<T>, apply: (value: T) => void): void {\n if (!isObservable(source)) {\n apply(source as T);\n return;\n }\n host.own(\n source.subscribe(value => {\n apply(value);\n host.requestFrame();\n })\n );\n}\n","import { UiNodeType } from '../graph/UiNodeType';\nimport { createElement } from './UiFactory';\nimport type { UiElement } from './UiElement';\nimport type { ScrollViewProps } from './UiElementProps';\nimport {\n lazySource,\n UiVirtualWindow,\n VIRTUAL_WINDOW_PROP,\n type LazyGridOptions,\n type LazyItemRenderer,\n type LazyListOptions\n} from './UiVirtualWindow';\n\n/**\n * A LazyColumn / LazyRow takes every ScrollView prop plus the window\n * options. The per-item key is `itemKey`, since `key` is the element's\n * own reconciliation key.\n */\nexport type LazyListProps = ScrollViewProps &\n Omit<LazyListOptions, 'key'> & {\n /** Stable identity per index, so a row keeps its node when the list shifts. */\n itemKey?: LazyListOptions['key'];\n /**\n * Hands the window to the caller, once, as the element is built.\n *\n * What it is for is `offsetOf(index)`: scrolling to a row that is\n * not mounted has no node to reveal, so the offset arithmetic is\n * the only way to ask for it. A component holding this must not\n * drive the window — the runtime owns that.\n */\n windowRef?: (window: UiVirtualWindow) => void;\n };\n\n/**\n * A vertical list that mounts only the rows in view.\n *\n * LazyColumn({ height: 400, count: 100000, estimatedExtent: 28 },\n * index => Row({ padding: 6 }, Text({ text: `Row ${index}` })))\n *\n * It is a ScrollView whose children are produced by a UiVirtualWindow:\n * a spacer for the rows above the window, the mounted rows (keyed, so a\n * row that stays in view keeps its node), and a spacer for the rows\n * below. The runtime advances the window every frame before layout, so\n * rows revealed by a scroll are laid out and painted on that frame.\n * Every other ScrollView property — size, padding, background — applies.\n */\nexport function LazyColumn(props: LazyListProps, renderItem: LazyItemRenderer): UiElement {\n return lazyList('column', props, renderItem);\n}\n\n/** The horizontal counterpart of LazyColumn. */\nexport function LazyRow(props: LazyListProps, renderItem: LazyItemRenderer): UiElement {\n return lazyList('row', props, renderItem);\n}\n\n/**\n * A vertical list whose rows share one set of column tracks with a\n * header, and of which only the visible rows exist.\n *\n * LazyGrid({ height: 400, count: 100000, estimatedExtent: 28,\n * columns: [auto, fr(1), 80], header: headerRow },\n * index => Grid({ subgrid: 'columns', role: 'row' }, …cells))\n *\n * The renderer returns the row itself, not its content: a row's cells\n * have to be items of the one grid that owns the tracks, so a row is a\n * `Grid` with `subgrid: 'columns'` and the window writes the index onto\n * it. The grid work left this to `DataTable`, and this\n * is it — the engine half, with the component built on top.\n */\nexport function LazyGrid(props: LazyGridProps, renderRow: LazyItemRenderer): UiElement {\n const { columns, columnGap, header, ...rest } = props;\n return lazyList('column', rest, renderRow, { columns, columnGap, header });\n}\n\nexport type LazyGridProps = LazyListProps & LazyGridOptions;\n\nfunction lazyList(\n axis: 'column' | 'row',\n props: LazyListProps,\n renderItem: LazyItemRenderer,\n grid?: LazyGridOptions\n): UiElement {\n const { count, estimatedExtent, overscan, itemKey, initialViewportExtent, revision, modifiers, windowRef, ...rest } =\n props;\n const window = new UiVirtualWindow(\n axis,\n { count, estimatedExtent, overscan, key: itemKey, initialViewportExtent, revision, grid },\n renderItem\n );\n windowRef?.(window);\n return createElement(\n UiNodeType.ScrollView,\n {\n ...rest,\n // The caller's modifiers first, so one of theirs writing the same\n // property still wins — the order `Button` already establishes.\n modifiers: [...(modifiers ?? []), lazySource({ window, count, revision })],\n direction: axis,\n [VIRTUAL_WINDOW_PROP]: window\n },\n [window.children$]\n );\n}\n","import { bandOf, type UiContainerSizeSource } from '../environment/UiContainerSize';\nimport { defineModifier } from './UiModifier';\n\nexport interface SizeContainerArgs {\n /**\n * The source this container feeds. The same object goes on the\n * node's `containerSize` property, which is what puts it in the\n * environment for the subtree.\n */\n readonly source: UiContainerSizeSource;\n}\n\n/**\n * Reports a node's content size to the source its subtree reads.\n *\n * The container-query half of custom layout, and built on\n * `host.onLayout` for the same reason `scrollPosition` is: \"this\n * node's box changed on this frame\" already has exactly one mechanism,\n * and a second one carrying the same fact would be a second thing to\n * keep right.\n *\n * The size reported is the **content** box: the border box less\n * padding, because the room a child has is the room inside the\n * padding, and a layout that switched arms at 600 px would otherwise\n * switch at 600 px of border box and lay itself out in 560.\n *\n * `Responsive` attaches this for you. Attach it by hand when the\n * source has to be reachable somewhere the helper's own children are\n * not, and put the same object on the node's `containerSize` prop.\n */\nexport const sizeContainer = defineModifier<SizeContainerArgs>({\n name: 'sizeContainer',\n attach(host, args) {\n const report = (): void => {\n const box = host.layoutBox();\n if (box === null) {\n return;\n }\n const inset = (name: string, axis: string): number => {\n const side = host.get<number | undefined>(name);\n return typeof side === 'number' ? side : (host.get<number | undefined>(axis) ?? 0);\n };\n const horizontal = inset('paddingLeft', 'paddingX') + inset('paddingRight', 'paddingX');\n const vertical = inset('paddingTop', 'paddingY') + inset('paddingBottom', 'paddingY');\n args.source.report(Math.max(0, box.width - horizontal), Math.max(0, box.height - vertical));\n };\n host.onLayout(report);\n // The first layout may already have happened, for a node mounted\n // into a tree that is on screen; `onLayout` only reports changes.\n report();\n }\n});\n\nexport interface BreakpointArgs {\n /**\n * Widths, in ascending order, at which the properties change. A band\n * runs from its own width up to the next one.\n */\n readonly at: readonly number[];\n /**\n * What to write in each band, keyed by the width that opens it. The\n * band below the first breakpoint is keyed `0`.\n *\n * Properties are written cumulatively from the narrowest band up, so\n * a band only names what it changes: `{ 0: { paddingX: 12 }, 900: {\n * paddingX: 32 } }` keeps everything else the narrow arm set.\n */\n readonly props: Readonly<Record<number, Readonly<Record<string, unknown>>>>;\n}\n\n/**\n * Changes a node's own properties as the room it has crosses a width.\n *\n * The small half of container queries, and the one to reach for first:\n * a screen whose gutter, column count or type size differs at 400 px\n * and 1400 px is a set of properties, not a different tree, and\n * changing properties costs a layout rather than a rebuild.\n * `Responsive` is for when the *children* genuinely differ.\n *\n * It measures the node it is attached to, which is the useful thing\n * about it and the thing a media query cannot do: a card in a sidebar\n * asks how wide the card is, not how wide the window is.\n *\n * Nothing is written until the node has been laid out once, so the\n * first frame uses the element's own values. Give the element the\n * narrowest band's values as its declared props if that matters.\n */\nexport const breakpoint = defineModifier<BreakpointArgs>({\n name: 'breakpoint',\n attach(host, args) {\n const bands = [0, ...args.at];\n let current = -1;\n const apply = (): void => {\n const box = host.layoutBox();\n if (box === null) {\n return;\n }\n const band = bandOf(box.width, args.at);\n if (band === current) {\n return;\n }\n current = band;\n // From the narrowest band up to this one, so a band that names\n // one property does not silently drop what the bands below set.\n const written = new Map<string, unknown>();\n for (const edge of bands) {\n if (edge > band) {\n break;\n }\n for (const [name, value] of Object.entries(args.props[edge] ?? {})) {\n written.set(name, value);\n }\n }\n for (const [name, value] of written) {\n host.set(name, value);\n }\n };\n host.onLayout(apply);\n apply();\n }\n});\n","import { map } from 'rxjs';\n\nimport { UiContainerSizeSource, containerBands } from '../environment/UiContainerSize';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { Size } from '../layout/LayoutTypes';\nimport { sizeContainer } from '../modifiers/breakpoints';\nimport { createElement } from './UiFactory';\nimport type { UiChild, UiElement } from './UiElement';\nimport type { ColumnProps } from './UiElementProps';\n\nexport interface ResponsiveProps extends ColumnProps {\n /**\n * The content widths, in ascending order, at which the children are\n * built again. A width of 900 with `at={[600, 1200]}` is in the 600\n * band, and so is every width from 600 to 1199.\n *\n * Required, and the reason this is not simply a subscription to the\n * width: children rebuilt on every pixel of a resize allocate a\n * subtree per frame of a drag. Naming the widths that matter is what\n * makes the rebuild happen once per band instead.\n */\n readonly at: readonly number[];\n /** Which container to be. A Column by default; a Row or a Box are the other two. */\n readonly as?: 'column' | 'row' | 'box';\n}\n\n/**\n * A container whose children are built from the room it has.\n *\n * The other half of container queries, for the case `breakpoint`\n * cannot cover: a screen that is a sidebar and a list at 1400 px and a\n * single stack at 400 px is not the same tree with different\n * properties, it is a different tree.\n *\n * Responsive({ at: [900], width: percent(100) }, size =>\n * size.width >= 900 ? [Sidebar(), List()] : [List()]\n * )\n *\n * `build` is called once per band entered, with the size the container\n * had when it entered that band, and its result replaces the children.\n * A resize inside a band changes nothing, which is the whole point:\n * dragging a window edge from 1000 px to 1399 px does no work at all.\n *\n * The container also **provides its size to the subtree** through\n * `UiEnvironmentKeys.containerSize`, so a descendant that needs the\n * number rather than a branch can read it without the tree threading a\n * prop down. What is provided is a source object whose identity never\n * changes, so nothing under it is rebuilt when the number moves; see\n * `environment/UiContainerSize.ts`.\n *\n * The first build happens before any layout, when the size is zero, so\n * `build` must return something sensible for a container whose room is\n * not yet known. Zero picks the narrowest arm, which is the right\n * guess and also what a phone gets.\n */\nexport function Responsive(props: ResponsiveProps, build: (size: Size) => UiChild | readonly UiChild[]): UiElement {\n const { at, as = 'column', modifiers, ...rest } = props;\n const source = new UiContainerSizeSource();\n const children = containerBands(source, at).pipe(\n map(() => {\n const built = build(source.current);\n return Array.isArray(built) ? (built as readonly UiChild[]) : [built as UiChild];\n })\n );\n const container = sizeContainer({ source });\n return createElement(\n as === 'row' ? UiNodeType.Row : as === 'box' ? UiNodeType.Box : UiNodeType.Column,\n {\n ...rest,\n containerSize: source,\n modifiers: modifiers === undefined ? [container] : [container, ...modifiers]\n },\n [children]\n );\n}\n","import type { UiKeyModifiers } from '../input/UiInputEvent';\nimport type { EditUnit } from './EditableTextModel';\n\n/** Which keyboard conventions apply: Command on a Mac, Control elsewhere. */\nexport type EditingPlatform = 'mac' | 'other';\n\n/**\n * What a key press asks an editable to do.\n *\n * `move` with unit 'vertical' is up/down a visual line and needs the\n * paragraph layout, so the model cannot do it alone; the controller\n * resolves it through `TextGeometry`.\n */\nexport type EditCommand =\n | { kind: 'move'; unit: EditUnit | 'vertical'; direction: -1 | 1; extend: boolean }\n | { kind: 'delete'; unit: EditUnit; direction: -1 | 1 }\n | { kind: 'newline' }\n | { kind: 'selectAll' }\n | { kind: 'undo' }\n | { kind: 'redo' }\n | { kind: 'insert'; text: string };\n\n/**\n * The platform the runtime is on, decided once. Available in a worker\n * too, since `navigator.userAgent` is; tests set it explicitly.\n */\nexport function detectEditingPlatform(): EditingPlatform {\n const agent = typeof navigator !== 'undefined' ? navigator.userAgent : '';\n return /Mac|iPhone|iPad|iPod/.test(agent) ? 'mac' : 'other';\n}\n\n/**\n * Maps a key press to an editing command, following the host's\n * conventions:\n *\n * Mac — Option steps by word, Command jumps to line and document\n * ends, Command+Backspace deletes to the line start.\n * Others — Control steps by word, Home/End and Control+Home/End are\n * the line and document ends.\n *\n * Shift extends every move. Enter is a newline (the controller drops\n * it in a single-line field). A printable key becomes `insert` only\n * when `textFromKeys` is set: a host with an editing proxy delivers\n * text through `beforeinput` instead, where the IME and dead keys\n * have already been resolved.\n */\nexport function commandForKey(\n key: string,\n modifiers: UiKeyModifiers,\n platform: EditingPlatform,\n textFromKeys: boolean\n): EditCommand | null {\n const mac = platform === 'mac';\n const primary = mac ? modifiers.meta : modifiers.ctrl;\n const word = mac ? modifiers.alt : modifiers.ctrl;\n const extend = modifiers.shift;\n\n switch (key) {\n case 'ArrowLeft':\n case 'ArrowRight': {\n const direction = key === 'ArrowLeft' ? -1 : 1;\n if (mac && modifiers.meta) {\n return { kind: 'move', unit: 'line', direction, extend };\n }\n return { kind: 'move', unit: word ? 'word' : 'grapheme', direction, extend };\n }\n case 'ArrowUp':\n case 'ArrowDown': {\n const direction = key === 'ArrowUp' ? -1 : 1;\n if (mac && modifiers.meta) {\n return { kind: 'move', unit: 'document', direction, extend };\n }\n return { kind: 'move', unit: 'vertical', direction, extend };\n }\n case 'Home':\n return { kind: 'move', unit: modifiers.ctrl ? 'document' : 'line', direction: -1, extend };\n case 'End':\n return { kind: 'move', unit: modifiers.ctrl ? 'document' : 'line', direction: 1, extend };\n case 'Backspace':\n if (mac && modifiers.meta) {\n return { kind: 'delete', unit: 'line', direction: -1 };\n }\n return { kind: 'delete', unit: word ? 'word' : 'grapheme', direction: -1 };\n case 'Delete':\n return { kind: 'delete', unit: word ? 'word' : 'grapheme', direction: 1 };\n case 'Enter':\n return primary ? null : { kind: 'newline' };\n }\n\n if (primary && !modifiers.alt) {\n switch (key.toLowerCase()) {\n case 'a':\n return { kind: 'selectAll' };\n case 'z':\n return modifiers.shift ? { kind: 'redo' } : { kind: 'undo' };\n case 'y':\n return mac ? null : { kind: 'redo' };\n }\n return null;\n }\n\n if (textFromKeys && isPrintable(key) && !modifiers.ctrl && !modifiers.meta) {\n return { kind: 'insert', text: key };\n }\n return null;\n}\n\n/** A key value that is the character it types: one grapheme, not a name like 'Shift'. */\nexport function isPrintable(key: string): boolean {\n if (key.length === 1) {\n return true;\n }\n if (key.length === 2) {\n const code = key.charCodeAt(0);\n return code >= 0xd800 && code <= 0xdbff;\n }\n return false;\n}\n","import type { UiNode } from '../graph/UiNode';\n\n/**\n * Tells a listener when a node's scoped environment changed under it.\n *\n * The graph already rebuilds environments in one place — at attach for\n * a freshly mounted subtree, and in the environment phase after a\n * provider changed — and marks the affected nodes dirty so their\n * inherited properties re-resolve. A modifier that read a value out of\n * the environment holds it in its own state instead, where no dirty\n * flag reaches it; this is the notification it needs.\n *\n * Registered per node and usually empty, so a provider change costs a\n * map lookup per node it already visits.\n */\nexport class EnvironmentNotifier {\n private readonly listeners = new Map<UiNode, Set<() => void>>();\n\n add(node: UiNode, listener: () => void): () => void {\n let forNode = this.listeners.get(node);\n if (forNode === undefined) {\n forNode = new Set();\n this.listeners.set(node, forNode);\n }\n forNode.add(listener);\n return () => {\n const set = this.listeners.get(node);\n if (set === undefined) {\n return;\n }\n set.delete(listener);\n if (set.size === 0) {\n this.listeners.delete(node);\n }\n };\n }\n\n isEmpty(): boolean {\n return this.listeners.size === 0;\n }\n\n /** Fed from the graph, for every node whose environment was reassigned. */\n handleEnvironmentChange(node: UiNode): void {\n const forNode = this.listeners.get(node);\n if (forNode === undefined) {\n return;\n }\n for (const listener of forNode) {\n listener();\n }\n }\n\n handleNodeRemoved(node: UiNode): void {\n this.listeners.delete(node);\n }\n}\n","import { UiEnvironment } from './UiEnvironment';\nimport type { UiEnvironmentKey } from './UiEnvironmentKey';\n\n/**\n * Describes a set of environment values provided by a node.\n *\n * Provider nodes are regular UiNodes; the builder recognizes them\n * by their type or by an explicit provider declaration and uses\n * this structure to attach a child environment to the subtree.\n */\nexport interface UiEnvironmentProvider {\n readonly environment: UiEnvironment;\n}\n\n/**\n * Creates an environment provider from a parent environment and\n * one or more explicit key/value overrides.\n */\nexport function provideEnvironment(\n parent: UiEnvironment | null,\n values: ReadonlyArray<{ key: UiEnvironmentKey<unknown>; value: unknown }>\n): UiEnvironmentProvider {\n let environment = parent ?? new UiEnvironment(null);\n for (const { key, value } of values) {\n environment = environment.set(key, value);\n }\n return { environment };\n}\n","import type { UiNode } from '../graph/UiNode';\n\n/** One occurrence of the query: a range of one node's text. */\nexport interface FindMatch {\n readonly node: UiNode;\n readonly start: number;\n readonly end: number;\n}\n\nexport interface FindOptions {\n /** Distinguish upper and lower case. Off by default, as a find bar is. */\n matchCase?: boolean;\n}\n\n/**\n * Where a query occurs in a run of text nodes.\n *\n * Matches are found within one node's text, never across two. A page of\n * HTML matches across inline elements — `hello <b>world</b>` finds\n * \"hello world\" — but a `Text` node is a paragraph, not a span, so\n * there is no inline seam to cross and every match is a range of one\n * node.\n *\n * Occurrences do not overlap: the scan resumes after each match, so\n * \"aa\" in \"aaa\" is one match, as it is in a browser's find bar.\n */\nexport function findMatchesIn(\n nodes: readonly UiNode[],\n textOf: (node: UiNode) => string | undefined,\n query: string,\n options: FindOptions = {}\n): FindMatch[] {\n const matches: FindMatch[] = [];\n if (query.length === 0) {\n return matches;\n }\n for (const node of nodes) {\n const text = textOf(node);\n if (text === undefined || text.length < query.length) {\n continue;\n }\n appendMatches(node, text, query, options.matchCase === true, matches);\n }\n return matches;\n}\n\nfunction appendMatches(node: UiNode, text: string, query: string, matchCase: boolean, out: FindMatch[]): void {\n let haystack = text;\n let needle = query;\n if (!matchCase) {\n const lowered = text.toLowerCase();\n const loweredQuery = query.toLowerCase();\n // Case folding is not always length-preserving ('İ' lowercases to\n // two code units), and a shifted offset would highlight the wrong\n // characters. Those rare strings are matched with case instead of\n // silently misplacing the highlight.\n if (lowered.length === text.length && loweredQuery.length === query.length) {\n haystack = lowered;\n needle = loweredQuery;\n }\n }\n for (let from = 0; from <= haystack.length - needle.length;) {\n const at = haystack.indexOf(needle, from);\n if (at < 0) {\n return;\n }\n out.push({ node, start: at, end: at + needle.length });\n from = at + needle.length;\n }\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { LayoutRecord } from '../layout/LayoutRecord';\nimport type { TextMeasurer } from '../layout/TextMeasurer';\nimport { createPaintState, resolvePaintState } from '../rendering/PaintState';\nimport { selectableTextNodes, selectableTextOf, type TextRange } from '../selection/UiSelectable';\nimport { paragraphGeometry, selectionRectsIn } from '../selection/TextSelectionGeometry';\nimport type { UiSelectionController } from '../selection/UiSelectionController';\nimport type { UiKeyModifiers } from '../input/UiInputEvent';\nimport { detectEditingPlatform, type EditingPlatform } from '../editing/EditingKeymap';\nimport { findMatchesIn, type FindMatch, type FindOptions } from './TextFind';\nimport { clearMatchRanges, setMatchRanges } from './UiTextMatches';\n\n/**\n * What the controller needs from the runtime around it: geometry,\n * measurement, and the two things it cannot do itself — mark a node\n * dirty and scroll a rectangle into view.\n */\nexport interface FindHost {\n recordFor(node: UiNode): LayoutRecord | undefined;\n readonly measurer: TextMeasurer;\n markDirty(node: UiNode, flags: DirtyFlags): void;\n /** Scrolls ancestors so a node-local box is visible. */\n reveal(node: UiNode, box: LayoutBox): void;\n /** The subtree a search runs over: the layout root. */\n root(): UiNode;\n /** Gives a node keyboard focus; used to put the caret in the app's query field. */\n focus(node: UiNode): void;\n}\n\nexport interface FindControllerOptions {\n /** Keyboard conventions; detected from the user agent by default. */\n platform?: EditingPlatform;\n}\n\n/** Room kept around the active match when scrolling it into view. */\nconst MATCH_REVEAL_PADDING = 8;\n\n/**\n * Finding text in the app's own content.\n *\n * The browser's find bar searches the DOM, and a canvas has none, so\n * Ctrl+F over a Gesso app has to be the app's.\n *\n * The corpus is the node tree — every string `selectableTextNodes`\n * finds, in reading order. That is the same reach a DOM mirror would\n * have and no more: a `LazyColumn` materialises only its visible\n * window, so rows that are not nodes are not searched. Closing that\n * gap means searching the data behind the list rather than the nodes\n * over it, which only the app can do; the engine is here, and a\n * supplementary source is the next thing to add to it.\n *\n * The active match is a real selection, through the selection\n * controller — so it looks like every other selection, copy takes it,\n * and revealing it uses the same scrolling. Every other match carries a\n * softer highlight on its node (see `UiTextMatches`). Both renderers\n * draw the matches under the selection, so the active one reads as the\n * strongest thing on the page.\n *\n * The UI is the app's: this is the engine a find bar drives, and\n * `isOpen` is the only thing it says about that UI — that a find\n * session is running, so Ctrl/Cmd+F and Escape mean find rather than\n * whatever they would otherwise. Whether the browser's own find bar is\n * left alone is the shell's call (`WorkerApp`'s `interceptFind`),\n * because the worker's answer cannot come back in time to cancel it.\n */\nexport class UiFindController {\n private readonly platform: EditingPlatform;\n private readonly paint = createPaintState();\n private readonly listeners = new Set<() => void>();\n private opened = false;\n private field: UiNode | null = null;\n private currentQuery = '';\n private options: FindOptions = {};\n private found: FindMatch[] = [];\n private active = -1;\n /** The nodes currently carrying match ranges, so they can be cleared. */\n private highlighted: UiNode[] = [];\n\n constructor(\n private readonly host: FindHost,\n private readonly selection: UiSelectionController,\n options: FindControllerOptions = {}\n ) {\n this.platform = options.platform ?? detectEditingPlatform();\n }\n\n // ---------------------------------------------------------------------------\n // The session\n // ---------------------------------------------------------------------------\n\n /** Whether a find session is running. */\n get isOpen(): boolean {\n return this.opened;\n }\n\n /** Notified whenever the query, the matches or `isOpen` change. */\n onChange(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n /**\n * The app's query field, so opening a session can put the caret in\n * it. The app registers it through a `ref`; without one a session\n * still opens and the user clicks into the field themselves.\n */\n setField(node: UiNode | null): void {\n this.field = node;\n }\n\n /** Starts a find session; the app shows its bar for this. */\n open(): void {\n if (!this.opened) {\n this.opened = true;\n this.notify();\n }\n // After the notify, so the field the app renders for an open\n // session exists by the time it is focused.\n if (this.field !== null) {\n this.host.focus(this.field);\n }\n }\n\n /** Ends the session and drops the matches. */\n close(): void {\n if (!this.opened && this.currentQuery.length === 0) {\n return;\n }\n this.opened = false;\n this.clear();\n }\n\n /**\n * Default keyboard behaviour: Ctrl/Cmd+F starts a session, Escape\n * ends one. Returns true when the key was consumed — Escape only\n * while a session is running, so it still clears a selection\n * otherwise.\n */\n handleKey(key: string, modifiers: UiKeyModifiers): boolean {\n const primary = this.platform === 'mac' ? modifiers.meta : modifiers.ctrl;\n if (primary && !modifiers.alt && (key === 'f' || key === 'F')) {\n this.open();\n return true;\n }\n if (key === 'Escape' && this.opened) {\n this.close();\n return true;\n }\n return false;\n }\n\n // ---------------------------------------------------------------------------\n // Matches\n // ---------------------------------------------------------------------------\n\n /** The query the matches are for; empty when nothing is being searched. */\n get query(): string {\n return this.currentQuery;\n }\n\n /** Every match, in document order. */\n get matches(): readonly FindMatch[] {\n return this.found;\n }\n\n get matchCount(): number {\n return this.found.length;\n }\n\n /** The index of the active match, or -1 when there is none. */\n get activeIndex(): number {\n return this.active;\n }\n\n /**\n * Runs a query over the app's text and activates the first match.\n * Returns how many there are; an empty query clears everything.\n */\n search(query: string, options: FindOptions = {}): number {\n this.currentQuery = query;\n this.options = options;\n if (query.length === 0) {\n this.clear();\n return 0;\n }\n this.found = this.collect(query, options);\n this.highlight();\n this.activate(this.found.length > 0 ? 0 : -1);\n this.notify();\n return this.found.length;\n }\n\n /**\n * Re-runs the current query. The tree changed under it — content was\n * edited, a list scrolled new rows in — so the matches did too.\n */\n refresh(): void {\n if (this.currentQuery.length === 0) {\n return;\n }\n const previous = this.found[this.active];\n this.found = this.collect(this.currentQuery, this.options);\n this.highlight();\n // Stay on the same match when it survived, so refreshing under the\n // user does not send them back to the top of the page.\n const same =\n previous === undefined\n ? -1\n : this.found.findIndex(\n match => match.node === previous.node && match.start === previous.start && match.end === previous.end\n );\n this.activate(same >= 0 ? same : this.found.length > 0 ? 0 : -1);\n this.notify();\n }\n\n /** Moves to the next match, wrapping around. */\n next(): boolean {\n return this.step(1);\n }\n\n /** Moves to the previous match, wrapping around. */\n previous(): boolean {\n return this.step(-1);\n }\n\n /** Drops the query, the matches and their highlights. */\n clear(): void {\n this.currentQuery = '';\n this.found = [];\n this.active = -1;\n for (const node of this.highlighted) {\n if (clearMatchRanges(node)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n this.highlighted = [];\n this.notify();\n }\n\n /**\n * A node left the tree. Its matches went with it, so the search is\n * re-run rather than left pointing at something that is gone.\n */\n handleNodeRemoved(node: UiNode): void {\n if (this.currentQuery.length === 0) {\n return;\n }\n if (this.highlighted.includes(node) || this.found.some(match => match.node === node)) {\n this.clear();\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internals\n // ---------------------------------------------------------------------------\n\n private notify(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n\n private step(direction: 1 | -1): boolean {\n if (this.found.length === 0) {\n return false;\n }\n const from = this.active < 0 ? (direction > 0 ? -1 : 0) : this.active;\n const next = (from + direction + this.found.length) % this.found.length;\n this.activate(next);\n this.notify();\n return true;\n }\n\n /**\n * The matches of a query, dropped where the text they are in is not\n * drawn: a paragraph capped by `maxLines` shows part of itself, and a\n * match the user cannot see must not be counted or stepped through.\n * Geometry is only measured for the nodes that matched at all.\n */\n private collect(query: string, options: FindOptions): FindMatch[] {\n const nodes = selectableTextNodes(this.host.root());\n const matches = findMatchesIn(nodes, selectableTextOf, query, options);\n const drawnEnd = new Map<UiNode, number>();\n return matches.filter(match => {\n let end = drawnEnd.get(match.node);\n if (end === undefined) {\n end = this.geometryEndOf(match.node);\n drawnEnd.set(match.node, end);\n }\n return match.end <= end;\n });\n }\n\n /** Writes the match ranges onto the nodes that hold them. */\n private highlight(): void {\n const byNode = new Map<UiNode, TextRange[]>();\n for (const match of this.found) {\n const ranges = byNode.get(match.node);\n if (ranges === undefined) {\n byNode.set(match.node, [{ start: match.start, end: match.end }]);\n } else {\n ranges.push({ start: match.start, end: match.end });\n }\n }\n for (const [node, ranges] of byNode) {\n if (setMatchRanges(node, ranges)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n for (const node of this.highlighted) {\n if (!byNode.has(node) && clearMatchRanges(node)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n this.highlighted = Array.from(byNode.keys());\n }\n\n /** Selects the match at `index` and scrolls it into view. */\n private activate(index: number): void {\n this.active = index;\n if (index < 0) {\n this.selection.clear();\n return;\n }\n const match = this.found[index];\n this.selection.selectRange(match.node, match.start, match.end);\n this.revealMatch(match);\n }\n\n private revealMatch(match: FindMatch): void {\n const geometry = this.geometryOf(match.node);\n const rects = selectionRectsIn(geometry, match.start, match.end);\n if (rects.length === 0) {\n return;\n }\n const first = rects[0];\n this.host.reveal(match.node, {\n x: first.x - MATCH_REVEAL_PADDING,\n y: first.y - MATCH_REVEAL_PADDING,\n width: first.width + 2 * MATCH_REVEAL_PADDING,\n height: first.height + 2 * MATCH_REVEAL_PADDING\n });\n }\n\n private geometryEndOf(node: UiNode): number {\n return this.geometryOf(node).end;\n }\n\n /** The node's text laid out in its own coordinates; see UiSelectionController. */\n private geometryOf(node: UiNode) {\n const rec = this.host.recordFor(node);\n const state = resolvePaintState(node, this.paint);\n const box: LayoutBox =\n rec === undefined\n ? { x: 0, y: 0, width: 0, height: 0 }\n : {\n x: rec.paddingLeft,\n y: rec.paddingTop,\n width: Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight),\n height: Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom)\n };\n return paragraphGeometry(state.text ?? '', box, state, this.host.measurer);\n }\n}\n","import type { UiNode } from '../graph/UiNode';\n\n/**\n * Tells a listener when one particular node gains or loses focus.\n *\n * `UiFocusManager.onFocusChange` reports the node that now holds focus,\n * which is the right shape for the runtime (scroll it into view) and\n * the wrong one for a focus ring: with a ring on every control, every\n * listener would run on every focus change and all but two of them\n * would decide nothing happened.\n *\n * A focus change touches exactly two nodes — the one losing it and the\n * one gaining it — so the notifier keeps the previous node and fires\n * only those. The cost of a focus change is therefore independent of\n * how many rings exist.\n */\nexport class FocusNotifier {\n private readonly listeners = new Map<UiNode, Set<(focused: boolean) => void>>();\n private focused: UiNode | null = null;\n private visible = true;\n\n /** Registers a listener for one node; the returned function removes it. */\n add(node: UiNode, listener: (focused: boolean) => void): () => void {\n let forNode = this.listeners.get(node);\n if (forNode === undefined) {\n forNode = new Set();\n this.listeners.set(node, forNode);\n }\n forNode.add(listener);\n return () => {\n const set = this.listeners.get(node);\n if (set === undefined) {\n return;\n }\n set.delete(listener);\n if (set.size === 0) {\n this.listeners.delete(node);\n }\n };\n }\n\n /** Whether the node the notifier last saw focused is this one. */\n isFocused(node: UiNode): boolean {\n return this.focused === node;\n }\n\n /** Whether the focus `node` holds should be shown; false for any node without focus. */\n isFocusVisible(node: UiNode): boolean {\n return this.focused === node && this.visible;\n }\n\n /**\n * Fed from `UiFocusManager.onFocusChange`, which reports the new\n * holder and, through `visible`, whether its focus should be shown. A\n * change of visibility alone is reported to the holder's listeners as\n * a focus change, which is what lets a ring appear on a mouse-focused\n * control when the keyboard is next used.\n */\n handleFocusChange(node: UiNode | null, visible = true): void {\n const previous = this.focused;\n const wasVisible = this.visible;\n this.focused = node;\n this.visible = visible;\n if (previous === node) {\n if (node !== null && wasVisible !== visible) {\n this.notify(node, true);\n }\n return;\n }\n if (previous !== null) {\n this.notify(previous, false);\n }\n if (node !== null) {\n this.notify(node, true);\n }\n }\n\n /**\n * A removed node's listeners go with it. The modifier's own detach\n * removes them too; this is the case where the node leaves without\n * its modifiers being reconciled first.\n */\n handleNodeRemoved(node: UiNode): void {\n this.listeners.delete(node);\n if (this.focused === node) {\n this.focused = null;\n }\n }\n\n private notify(node: UiNode, focused: boolean): void {\n const forNode = this.listeners.get(node);\n if (forNode === undefined) {\n return;\n }\n for (const listener of forNode) {\n listener(focused);\n }\n }\n}\n","import { UiNodeType } from '../graph/UiNodeType';\nimport type { UiNode } from '../graph/UiNode';\nimport { isNodeInert } from './UiInteraction';\nimport type { UiKeyModifiers } from './UiInputEvent';\n\n/**\n * One press in a shortcut, after parsing.\n *\n * `mod` is deliberately not `ctrl` or `meta`. The framework runs in a\n * worker and has no reliable answer to which platform it is on, and the\n * answer would be wrong for a person on a Mac keyboard plugged into a\n * Linux machine anyway. So `Mod` matches **either** Control or Command,\n * and an application that wants the distinction spells out the one it\n * means. This is the same choice the DOM's own `metaKey`/`ctrlKey` pair\n * forces on every web application, made once here instead of in every\n * handler.\n */\nexport interface UiShortcutStep {\n readonly key: string;\n readonly mod: boolean;\n readonly ctrl: boolean;\n readonly meta: boolean;\n readonly shift: boolean;\n readonly alt: boolean;\n}\n\n/** What an application registers. */\nexport interface UiShortcut {\n /**\n * The keys, as `'Mod+K'`, `'Shift+?'`, or `'g d'` for a chord: a\n * sequence of presses, separated by spaces, that must arrive in\n * order and within the chord timeout.\n */\n readonly keys: string;\n /** What a palette lists it as. */\n readonly label: string;\n readonly run: () => void;\n /**\n * The subtree this shortcut belongs to, or null for the whole\n * application.\n *\n * A scoped shortcut is live only while focus is inside the node,\n * which is what makes \"Delete removes the selected row\" safe to\n * register: the table registers it, and it stops existing the moment\n * the person tabs into the search field.\n */\n readonly scope?: UiNode | null;\n /**\n * Higher wins when two live shortcuts want the same keys. Default 0.\n *\n * A scope is not a priority. Two shortcuts scoped to nested nodes are\n * separated by depth without either naming a number; this is for the\n * case where a route wants to take a key the application had.\n */\n readonly priority?: number;\n /** A last check before it runs, for a command that is not always available. */\n readonly when?: () => boolean;\n /** A heading a palette groups it under. */\n readonly group?: string;\n}\n\n/** A registered shortcut, with what the registry worked out about it. */\nexport interface UiShortcutBinding extends UiShortcut {\n /** The parsed presses, so a palette can render them without reparsing. */\n readonly steps: readonly UiShortcutStep[];\n /** `keys` written the way it should be shown. */\n readonly display: string;\n}\n\nexport interface ShortcutRegistryOptions {\n /**\n * How long the rest of a chord has to arrive, in milliseconds.\n *\n * Long enough to type a second key deliberately, short enough that a\n * `g` pressed into a page and then forgotten does not swallow a key\n * a minute later.\n */\n chordTimeout?: number;\n /** The clock, for specs that drive a chord without waiting. */\n now?: () => number;\n}\n\n/** The keys that never resolve a shortcut on their own. */\nconst MODIFIER_KEYS: ReadonlySet<string> = new Set(['Shift', 'Control', 'Alt', 'Meta', 'CapsLock']);\n\n/**\n * Every shortcut the application has, and which of them are live.\n *\n * Segue and Sluice each put one `onKeyDown` on the root and switch on\n * the current route, which has the three problems that shape this. A\n * screen cannot add a key without editing a file it does not own; two\n * screens that want the same key have no way to say which wins; and\n * nothing can list what is available, so neither application has a\n * palette or a help sheet and neither could grow one.\n *\n * A registry answers all three. A component registers what it can do\n * and gets a function back that unregisters it, so the shortcut's\n * lifetime is the component's. A shortcut names the subtree it belongs\n * to, so it is live exactly while focus is inside it. And `active`\n * returns what would run right now, which is what a palette lists and\n * what a help sheet prints.\n *\n * ## What it is not\n *\n * It does not listen to anything. The registry is fed by the\n * `shortcuts` modifier, which puts one root listener on the node it is\n * attached to and hands each key here; a runtime that wants shortcuts\n * without modifiers can call `handleKey` from its own listener. Keeping\n * the listening out means the registry is a plain object a spec can\n * drive directly, and means it does not need the dispatcher, the focus\n * manager, or anything else a `gesso-framework` service would have had\n * to inject.\n *\n * ## Typing is not a shortcut\n *\n * A shortcut with no modifier is skipped while a text field has focus.\n * Without that rule an application with a `n` for \"new note\" becomes an\n * application you cannot type the letter n into, which is the failure\n * every home-grown key handler eventually ships.\n */\nexport class UiShortcutRegistry {\n private readonly bindings: UiShortcutBinding[] = [];\n private readonly chordTimeout: number;\n private readonly now: () => number;\n\n /** The presses of a chord matched so far, and when the last arrived. */\n private pending: UiShortcutStep[] = [];\n private pendingAt = 0;\n\n constructor(options: ShortcutRegistryOptions = {}) {\n this.chordTimeout = options.chordTimeout ?? 1200;\n this.now = options.now ?? defaultClock;\n }\n\n /** Adds a shortcut. The returned function removes it again. */\n register(shortcut: UiShortcut): () => void {\n const steps = parseShortcut(shortcut.keys);\n const binding: UiShortcutBinding = { ...shortcut, steps, display: formatShortcut(steps) };\n this.bindings.push(binding);\n return () => {\n const index = this.bindings.indexOf(binding);\n if (index !== -1) {\n this.bindings.splice(index, 1);\n }\n };\n }\n\n /** Every registered shortcut, live or not, in registration order. */\n get all(): readonly UiShortcutBinding[] {\n return this.bindings;\n }\n\n /**\n * What would run right now, in the order the registry would try them.\n *\n * This is the palette's list, and it is the same computation the key\n * handler makes, so a palette cannot show a command that would not\n * fire and cannot hide one that would.\n *\n * `focused` is the node keyboard input is currently going to, which\n * for a dispatched KeyDown is the event's target: the keyboard\n * controller routes a key to the focused node and to the root when\n * nothing is focused, so the target is the answer without the\n * registry having to hold a focus manager.\n */\n active(focused: UiNode | null): readonly UiShortcutBinding[] {\n const live = this.bindings.filter(binding => this.isLive(binding, focused));\n // A stable sort, so registration order breaks a tie between two\n // shortcuts of equal priority and depth.\n return live\n .map((binding, index) => ({ binding, index }))\n .sort((a, b) => rank(b.binding, focused) - rank(a.binding, focused) || a.index - b.index)\n .map(entry => entry.binding);\n }\n\n /**\n * Offers a key press to the live shortcuts.\n *\n * True when one of them took it, which the caller turns into a\n * `preventDefault()` so the keyboard controller's own defaults (Tab\n * navigation, Enter on a button, an editable's typing) do not also\n * act on a key that has already been spent.\n */\n handleKey(key: string, modifiers: UiKeyModifiers, focused: UiNode | null): boolean {\n if (MODIFIER_KEYS.has(key)) {\n return false;\n }\n const step = stepFor(key, modifiers);\n const at = this.now();\n if (this.pending.length > 0 && at - this.pendingAt > this.chordTimeout) {\n this.pending = [];\n }\n const sequence = [...this.pending, step];\n\n let prefixed = false;\n for (const binding of this.active(focused)) {\n if (!matchesPrefix(binding.steps, sequence)) {\n continue;\n }\n if (binding.steps.length === sequence.length) {\n this.pending = [];\n binding.run();\n return true;\n }\n prefixed = true;\n }\n if (prefixed) {\n // The first key of a chord: held so the next press can complete\n // it, and reported as taken so nothing else acts on a `g` that\n // was the beginning of a command.\n this.pending = sequence;\n this.pendingAt = at;\n return true;\n }\n this.pending = [];\n return false;\n }\n\n /** Forgets a half-typed chord, for a route change or a lost focus. */\n reset(): void {\n this.pending = [];\n }\n\n /**\n * Whether a binding would fire for a key going to `focused`.\n *\n * Three things take a shortcut out: its scope does not contain the\n * focused node, its own `when` says no, and, for a shortcut with no\n * modifier at all, the focused node is somewhere text is being\n * typed. The last is the rule that keeps a single-letter shortcut\n * from making a text field unusable.\n */\n private isLive(binding: UiShortcutBinding, focused: UiNode | null): boolean {\n const scope = binding.scope ?? null;\n if (scope !== null && (focused === null || !containsNode(scope, focused))) {\n return false;\n }\n if (binding.when !== undefined && !binding.when()) {\n return false;\n }\n if (focused !== null && isTypingInto(focused) && binding.steps.every(step => isBare(step))) {\n return false;\n }\n return true;\n }\n}\n\n/**\n * Where a binding sits in the order, most specific first.\n *\n * Priority dominates, and depth of scope separates the rest: a\n * shortcut scoped to a row beats one scoped to the table it is in,\n * which beats one scoped to nothing, without either of them naming a\n * number.\n */\nfunction rank(binding: UiShortcutBinding, focused: UiNode | null): number {\n const priority = (binding.priority ?? 0) * 1000;\n const scope = binding.scope ?? null;\n if (scope === null || focused === null) {\n return priority;\n }\n let depth = 0;\n for (let node: UiNode | null = scope; node !== null; node = node.parent) {\n depth += 1;\n }\n return priority + depth;\n}\n\n/** Whether `ancestor` is `node` or one of its ancestors. */\nfunction containsNode(ancestor: UiNode, node: UiNode): boolean {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current === ancestor) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Whether keys reaching this node are being typed into something.\n *\n * An editable node, or anything inside one: the caret may be on a text\n * node inside a field rather than on the field itself.\n */\nfunction isTypingInto(node: UiNode): boolean {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current.type === UiNodeType.EditableText && !isNodeInert(current)) {\n return true;\n }\n }\n return false;\n}\n\n/** A press with no modifier at all: an ordinary character. */\nfunction isBare(step: UiShortcutStep): boolean {\n return !step.mod && !step.ctrl && !step.meta && !step.alt;\n}\n\n/**\n * Reads `'Mod+Shift+K'` or `'g d'` into presses.\n *\n * Space separates the presses of a chord and `+` separates the\n * modifiers of one press, so `'Mod++'` is Mod and the plus key: the\n * last segment is always the key, however it is spelled.\n */\nexport function parseShortcut(keys: string): readonly UiShortcutStep[] {\n return keys\n .trim()\n .split(/\\s+/)\n .filter(part => part.length > 0)\n .map(parseStep);\n}\n\nfunction parseStep(text: string): UiShortcutStep {\n const parts = text.split('+');\n // A trailing '+' means the plus key itself, which split leaves as an\n // empty last segment with the real key one before it.\n if (parts.length > 1 && parts[parts.length - 1] === '') {\n parts.splice(parts.length - 2, 2, '+');\n }\n const key = parts.pop() ?? '';\n let mod = false;\n let ctrl = false;\n let meta = false;\n let shift = false;\n let alt = false;\n for (const part of parts) {\n switch (part.toLowerCase()) {\n case 'mod':\n case 'cmdorctrl':\n mod = true;\n break;\n case 'ctrl':\n case 'control':\n ctrl = true;\n break;\n case 'meta':\n case 'cmd':\n case 'command':\n meta = true;\n break;\n case 'shift':\n shift = true;\n break;\n case 'alt':\n case 'option':\n alt = true;\n break;\n default:\n // An unknown word is a typo in a shortcut, and a shortcut that\n // silently never fires is worse than one that complains.\n console.warn(`Unknown shortcut modifier '${part}' in '${text}'.`);\n }\n }\n return { key: normalizeKey(key), mod, ctrl, meta, shift, alt };\n}\n\n/**\n * A key as the platform adapter reports it, case-folded for letters.\n *\n * `'Mod+K'` and `'Mod+k'` are the same shortcut, and the browser\n * reports whichever the shift state produced. Anything longer than one\n * character is a named key (`'Enter'`, `'ArrowUp'`) and keeps its case.\n */\nfunction normalizeKey(key: string): string {\n return key.length === 1 ? key.toLowerCase() : key;\n}\n\n/** Whether the presses so far could still complete this shortcut. */\nfunction matchesPrefix(steps: readonly UiShortcutStep[], sequence: readonly UiShortcutStep[]): boolean {\n if (sequence.length > steps.length) {\n return false;\n }\n return sequence.every((step, index) => sameStep(steps[index], step));\n}\n\nfunction sameStep(want: UiShortcutStep, got: UiShortcutStep): boolean {\n if (want.key !== got.key || want.shift !== got.shift || want.alt !== got.alt) {\n return false;\n }\n if (want.mod) {\n return got.ctrl || got.meta;\n }\n return want.ctrl === got.ctrl && want.meta === got.meta;\n}\n\n/** The press the person actually made. */\nfunction stepFor(key: string, modifiers: UiKeyModifiers): UiShortcutStep {\n return {\n key: normalizeKey(key),\n mod: false,\n ctrl: modifiers.ctrl,\n meta: modifiers.meta,\n shift: modifiers.shift,\n alt: modifiers.alt\n };\n}\n\n/**\n * The shortcut as a palette should print it.\n *\n * `Mod` is printed as `Ctrl`, not as a platform glyph: the render\n * worker cannot see the platform, and a wrong symbol is worse than a\n * plain word. An application that knows what it is running on can\n * format the `steps` itself.\n */\nexport function formatShortcut(steps: readonly UiShortcutStep[]): string {\n return steps.map(formatStep).join(' ');\n}\n\nfunction formatStep(step: UiShortcutStep): string {\n const parts: string[] = [];\n if (step.mod || step.ctrl) {\n parts.push('Ctrl');\n }\n if (step.meta) {\n parts.push('Meta');\n }\n if (step.alt) {\n parts.push('Alt');\n }\n if (step.shift) {\n parts.push('Shift');\n }\n parts.push(step.key.length === 1 ? step.key.toUpperCase() : step.key);\n return parts.join('+');\n}\n\nfunction defaultClock(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now();\n}\n","import type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from '../layout/LayoutTypes';\n\n/**\n * What is being carried, and what it is.\n *\n * `type` is the application's own vocabulary (`'queue/track'`,\n * `'board/card'`), and a drop target says which types it accepts.\n * Matching by a string rather than by a class keeps the payload plain\n * data, which is what it has to be: an OS file drop arrives as a shell\n * message and becomes a payload of exactly this shape, and a message\n * that crossed the barrier cannot carry a prototype.\n */\nexport interface UiDragPayload {\n readonly type: string;\n readonly data: unknown;\n}\n\n/**\n * What a drop would do, in the vocabulary of the platform drag APIs.\n *\n * The framework does none of it: the effect is what a zone reports so\n * the source and the cursor can say what will happen, and `'none'` is\n * a zone refusing the payload it is being offered.\n */\nexport type UiDropEffect = 'move' | 'copy' | 'link' | 'none';\n\n/** Where the drag is now, and what it is carrying. */\nexport interface UiDragState {\n readonly payload: UiDragPayload;\n readonly x: number;\n readonly y: number;\n /** The node the drag started from, or null for one from outside. */\n readonly source: UiNode | null;\n /** True for a drag that began outside the application, an OS file drop. */\n readonly external: boolean;\n}\n\n/**\n * A registered drop target.\n *\n * The zone answers where it is rather than being asked to hold a box,\n * because a list scrolls and a card moves and the answer is only ever\n * correct at the moment it is asked. `null` from `boxOf` (a node with\n * no layout record yet) takes the zone out of consideration for that\n * move rather than being an error.\n */\nexport interface UiDropZone {\n readonly node: UiNode;\n boxOf(): LayoutBox | null;\n accepts(payload: UiDragPayload): boolean;\n /** The pointer entered the zone with a payload it accepts. */\n enter(state: UiDragState): void;\n /** The pointer moved inside the zone. */\n over(state: UiDragState): void;\n /** The pointer left, or the drag ended somewhere else. */\n leave(): void;\n /** The drag was released over the zone. What it did, for the source. */\n drop(state: UiDragState): UiDropEffect;\n}\n\n/** The result a completed drag reports back to whatever started it. */\nexport interface UiDragResult {\n readonly node: UiNode;\n readonly effect: UiDropEffect;\n}\n\n/**\n * One file that arrived from outside the application.\n *\n * Plain data, every field of it, because this crosses the barrier:\n * recognition runs in the render worker, which leaves the\n * shell holding a `File` object it cannot send. The bytes are an\n * `ArrayBuffer` so a large file is transferred rather than copied, and\n * they are optional because the shell reads them only for a drop that\n * something accepted, not for every move of the pointer over the\n * window.\n */\nexport interface UiDroppedFile {\n readonly name: string;\n /** The MIME type the platform reported, or '' when it had none. */\n readonly mediaType: string;\n readonly size: number;\n readonly lastModified: number;\n readonly bytes?: ArrayBuffer;\n}\n\n/**\n * The shell's message for a drag that started outside the window.\n *\n * The same four phases a drop target sees from a drag inside it, so a\n * zone that accepts `EXTERNAL_FILES` needs no second code path: the\n * session turns this into an ordinary drag and every zone answers it\n * the way it answers any other. The phases mirror the DOM's\n * `dragenter`, `dragover`, `dragleave` and `drop`.\n *\n * Position is in the same canvas-space coordinates as a pointer event,\n * so the shell subtracts the surface's bounding rect exactly as it does\n * for a press.\n */\nexport interface UiFileDropMessage {\n readonly type: 'fileDrop';\n readonly phase: 'enter' | 'over' | 'leave' | 'drop';\n readonly x: number;\n readonly y: number;\n /**\n * What is being carried. On `enter` and `over` the platform allows\n * only the names and types to be read, so `bytes` is absent there\n * and present on `drop`.\n */\n readonly files: readonly UiDroppedFile[];\n}\n\n/** The payload type an OS file drop arrives under. */\nexport const EXTERNAL_FILES = 'gesso/files';\n\n/**\n * The drag in flight, and everywhere it could be put down.\n *\n * `draggable` moves a node's transform and stops, which is enough for a\n * card that goes back where it was and nothing at all for a card that\n * goes somewhere. What was missing is the other half: somewhere to\n * drop it, a way to say what it is, and an answer to \"is this over\n * anything that would take it\".\n *\n * The session is that answer and deliberately nothing more. It holds\n * no geometry of its own, subscribes to nothing and paints nothing: a\n * `dropTarget` modifier registers a zone, a `dragSource` modifier opens\n * a drag and moves it, and the session's whole job is to work out which\n * registered zone is under the point and to tell it so. Both halves are\n * modifiers because both are behaviour attached to an element, which is\n * the test for what belongs here.\n *\n * There is one session per graph root rather than one per process, so\n * two applications in one worker, and a test suite renders dozens,\n * cannot see each other's drops.\n *\n * ## Which zone the point is in\n *\n * The deepest zone whose box contains the point and that accepts the\n * payload, breaking a tie by registration order. Depth rather than\n * paint order because a drop target is a region rather than a drawing:\n * a row inside a list is inside the list, and dropping on the row is\n * unambiguously the more specific answer. A zone that does not accept\n * the payload is passed over rather than blocking the one behind it,\n * so a list of mixed rows behaves the way a person expects.\n */\nexport class UiDragSession {\n private readonly zones: UiDropZone[] = [];\n private current: UiDragState | null = null;\n private inside: UiDropZone | null = null;\n\n /** Registers a drop target. The returned function unregisters it. */\n addZone(zone: UiDropZone): () => void {\n this.zones.push(zone);\n return () => {\n const index = this.zones.indexOf(zone);\n if (index !== -1) {\n this.zones.splice(index, 1);\n }\n if (this.inside === zone) {\n this.inside = null;\n }\n };\n }\n\n /** The drag in flight, or null when nothing is being carried. */\n get state(): UiDragState | null {\n return this.current;\n }\n\n /** The zone the pointer is over, or null. */\n get overNode(): UiNode | null {\n return this.inside?.node ?? null;\n }\n\n /**\n * Whether a zone would take what is being carried, for a source that\n * wants to show the person that letting go will do something.\n */\n get accepted(): boolean {\n return this.inside !== null;\n }\n\n /** Starts a drag. Any drag already in flight is cancelled first. */\n begin(payload: UiDragPayload, x: number, y: number, source: UiNode | null, external = false): void {\n if (this.current !== null) {\n this.cancel();\n }\n this.current = { payload, x, y, source, external };\n this.resolve();\n }\n\n /** Moves the drag, entering and leaving zones as it crosses them. */\n move(x: number, y: number): void {\n if (this.current === null) {\n return;\n }\n this.current = { ...this.current, x, y };\n this.resolve();\n }\n\n /**\n * Releases the drag over whatever it is on, and reports what that did.\n *\n * Null when it was let go over nothing that would take it, which is\n * the case a source has to handle by putting the node back.\n */\n end(): UiDragResult | null {\n const state = this.current;\n const zone = this.inside;\n this.current = null;\n this.inside = null;\n if (state === null || zone === null) {\n zone?.leave();\n return null;\n }\n const effect = zone.drop(state);\n zone.leave();\n return { node: zone.node, effect };\n }\n\n /** Abandons the drag: the zone under it is told, and nothing is dropped. */\n cancel(): void {\n this.inside?.leave();\n this.inside = null;\n this.current = null;\n }\n\n /**\n * Turns a shell's file-drop message into an ordinary drag.\n *\n * Everything downstream of here cannot tell the difference, which is\n * the point: a zone written for `EXTERNAL_FILES` is a zone like any\n * other, and the four phases map onto the four things a drag does.\n */\n applyFileDrop(message: UiFileDropMessage): UiDragResult | null {\n const payload: UiDragPayload = { type: EXTERNAL_FILES, data: message.files };\n switch (message.phase) {\n case 'enter':\n this.begin(payload, message.x, message.y, null, true);\n return null;\n case 'over':\n if (this.current === null) {\n this.begin(payload, message.x, message.y, null, true);\n } else {\n this.move(message.x, message.y);\n }\n return null;\n case 'leave':\n this.cancel();\n return null;\n case 'drop':\n if (this.current === null) {\n this.begin(payload, message.x, message.y, null, true);\n } else {\n this.move(message.x, message.y);\n }\n return this.end();\n }\n }\n\n /**\n * Works out which zone the point is in and reports the crossings.\n *\n * Walks the registered zones once per move. There are as many of them\n * as the page has drop targets, which is a handful, so this is a\n * linear scan on purpose: an index would have to be invalidated by\n * every layout, and the layout engine already answers \"where is this\n * node\" in constant time.\n */\n private resolve(): void {\n const state = this.current;\n if (state === null) {\n return;\n }\n let best: UiDropZone | null = null;\n let bestDepth = -1;\n for (const zone of this.zones) {\n if (!zone.accepts(state.payload)) {\n continue;\n }\n const box = zone.boxOf();\n if (box === null || !contains(box, state.x, state.y)) {\n continue;\n }\n const depth = depthOf(zone.node);\n if (depth > bestDepth) {\n best = zone;\n bestDepth = depth;\n }\n }\n if (best !== this.inside) {\n this.inside?.leave();\n this.inside = best;\n best?.enter(state);\n return;\n }\n best?.over(state);\n }\n}\n\nfunction contains(box: LayoutBox, x: number, y: number): boolean {\n return x >= box.x && x < box.x + box.width && y >= box.y && y < box.y + box.height;\n}\n\nfunction depthOf(node: UiNode): number {\n let depth = 0;\n for (let current: UiNode | null = node.parent; current !== null; current = current.parent) {\n depth += 1;\n }\n return depth;\n}\n\nconst sessions = new WeakMap<UiNode, UiDragSession>();\n\n/**\n * The session for the graph a node belongs to, created on first ask.\n *\n * Keyed by the root rather than held in a module variable so that two\n * applications in one worker keep their drags apart; a spec that\n * renders several trees would otherwise have one drag visible in all\n * of them.\n */\nexport function dragSessionFor(node: UiNode): UiDragSession {\n let root = node;\n while (root.parent !== null) {\n root = root.parent;\n }\n let session = sessions.get(root);\n if (session === undefined) {\n session = new UiDragSession();\n sessions.set(root, session);\n }\n return session;\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { LayoutRecord } from '../layout/LayoutRecord';\nimport type { TextMeasurer } from '../layout/TextMeasurer';\nimport { createPaintState, resolvePaintState } from '../rendering/PaintState';\nimport { EditableLayout } from '../editing/EditableLayout';\nimport type { EditableTextModel, EditUnit } from '../editing/EditableTextModel';\nimport { commandForKey, detectEditingPlatform, type EditCommand, type EditingPlatform } from '../editing/EditingKeymap';\nimport { wordRangeAt, lineStartAt, lineEndAt } from '../editing/TextBoundaries';\nimport { editorFor, isEditableNode, isMultiline, isReadOnly, nextCaretToggle } from '../editing/UiEditable';\nimport type { UiInputDispatcher } from './UiInputDispatcher';\nimport type { UiFocusManager } from './UiFocusManager';\nimport { UiBeforeInputEvent, UiTextChangeEvent, type UiKeyModifiers } from './UiInputEvent';\n\n/**\n * What the controller needs from the runtime around it: geometry,\n * measurement, and the two things it cannot do itself — mark a node\n * dirty and scroll a rectangle into view.\n */\nexport interface EditingHost {\n recordFor(node: UiNode): LayoutRecord | undefined;\n /** Where the node is seen, scroll offsets applied; for the shell's caret rectangle. */\n visibleBox(node: UiNode): LayoutBox;\n /** A layout-root point in the node's own coordinates. */\n toLocal(node: UiNode, x: number, y: number): { x: number; y: number };\n readonly measurer: TextMeasurer;\n markDirty(node: UiNode, flags: DirtyFlags): void;\n /** Scrolls ancestors so a node-local box is visible. */\n reveal(node: UiNode, box: LayoutBox): void;\n now(): number;\n}\n\n/**\n * What a shell mirrors while an editable has focus: its text and\n * selection (so native copy, cut and IME context are right) and where\n * the caret is on the canvas (so the IME candidate window opens there).\n */\nexport interface EditingState {\n readonly text: string;\n readonly selectionStart: number;\n readonly selectionEnd: number;\n /** Caret box in canvas (layout root) pixels. */\n readonly caret: LayoutBox;\n readonly multiline: boolean;\n readonly composing: boolean;\n}\n\nexport interface EditingControllerOptions {\n /** Keyboard conventions; detected from the user agent by default. */\n platform?: EditingPlatform;\n}\n\n/** How long two presses may be apart and still count as a double click. */\nconst MULTI_CLICK_MS = 500;\nconst MULTI_CLICK_SLOP = 4;\n/** Room kept around the caret when scrolling it into view. */\nconst CARET_REVEAL_PADDING = 4;\n\n/**\n * Editing behaviour for EditableText nodes.\n *\n * The keyboard and pointer controllers call in here as their default\n * behaviour for an editable target — after the app's listeners have\n * run and only when none of them called preventDefault() — and the\n * host feeds it the shell's text input: `beforeinput` edits,\n * composition, paste. The model on the node is the single source of\n * truth; this class turns intents into model changes, marks the node\n * dirty so layout and paint follow, reports the change through an\n * `input` event, and keeps the caret in view.\n *\n * Text arrives one of two ways. A shell with an editing proxy sends\n * `beforeinput`, where the browser has already resolved the IME, dead\n * keys and the keyboard layout; then printable key presses are not\n * text and `textFromKeys` is off. Without a proxy — tests, a bare\n * runtime — printable keys become insertions.\n */\nexport class UiEditingController {\n /** Insert printable key presses as text; off when a shell delivers text through beforeinput. */\n textFromKeys = true;\n\n private readonly platform: EditingPlatform;\n private readonly paint = createPaintState();\n private focusedEditable: UiNode | null = null;\n private visible = true;\n /** x the current run of vertical moves keeps returning to. */\n private verticalGoalX: number | undefined = undefined;\n private dragging: UiNode | null = null;\n private lastPress: { node: UiNode; x: number; y: number; at: number; count: number } | null = null;\n private compositionOpen = false;\n\n constructor(\n private readonly host: EditingHost,\n private readonly dispatcher: UiInputDispatcher,\n private readonly focus: UiFocusManager,\n options: EditingControllerOptions = {}\n ) {\n this.platform = options.platform ?? detectEditingPlatform();\n this.focus.onFocusChange(node => this.handleFocusChange(node));\n }\n\n /** The editable that holds focus, or null. */\n get focused(): UiNode | null {\n return this.focusedEditable;\n }\n\n /** Whether the pointer controller should hand presses on the node to this controller. */\n isEditable(node: UiNode): boolean {\n return isEditableNode(node);\n }\n\n // ---------------------------------------------------------------------------\n // Keyboard\n // ---------------------------------------------------------------------------\n\n /**\n * Default keyboard behaviour for the focused editable. Returns true\n * when the key was an editing command, so the caller can mark the\n * event handled.\n */\n handleKey(node: UiNode, key: string, modifiers: UiKeyModifiers): boolean {\n if (!isEditableNode(node)) {\n return false;\n }\n const command = commandForKey(key, modifiers, this.platform, this.textFromKeys);\n if (command === null) {\n return false;\n }\n return this.execute(node, command);\n }\n\n private execute(node: UiNode, command: EditCommand): boolean {\n const model = editorFor(node);\n const readOnly = isReadOnly(node);\n switch (command.kind) {\n case 'move':\n this.move(node, model, command.unit, command.direction, command.extend);\n return true;\n case 'delete':\n if (readOnly) {\n return true;\n }\n return this.applyEdit(node, model, deleteInputType(command.unit, command.direction), null, () => {\n if (command.direction < 0) {\n model.deleteBackward(command.unit);\n } else {\n model.deleteForward(command.unit);\n }\n });\n case 'newline':\n if (!isMultiline(node)) {\n // Enter in a single-line field is the app's (submit), not ours.\n return false;\n }\n if (readOnly) {\n return true;\n }\n return this.applyEdit(node, model, 'insertLineBreak', '\\n', () => model.insertText('\\n'));\n case 'insert':\n if (readOnly) {\n return true;\n }\n return this.applyEdit(node, model, 'insertText', command.text, () => model.insertText(command.text));\n case 'selectAll':\n model.selectAll();\n this.afterSelectionChange(node, model);\n return true;\n case 'undo':\n if (readOnly) {\n return true;\n }\n this.history(node, model, 'historyUndo', () => model.undo());\n return true;\n case 'redo':\n if (readOnly) {\n return true;\n }\n this.history(node, model, 'historyRedo', () => model.redo());\n return true;\n }\n }\n\n private move(\n node: UiNode,\n model: EditableTextModel,\n unit: EditUnit | 'vertical',\n direction: -1 | 1,\n extend: boolean\n ): void {\n if (unit === 'vertical') {\n const layout = this.layoutOf(node);\n const target = layout.verticalMove(model.focus, direction, this.verticalGoalX);\n if (target === null) {\n // Off the first or last line: the caret goes to the text's edge.\n model.moveTo(direction < 0 ? 0 : model.text.length, extend);\n this.verticalGoalX = undefined;\n } else {\n model.moveTo(target.offset, extend);\n this.verticalGoalX = target.x;\n }\n this.afterSelectionChange(node, model, true);\n return;\n }\n if (unit === 'line') {\n // Home/End are the visual line, which needs the layout; the\n // model's 'line' is the hard line.\n const layout = this.layoutOf(node);\n const caret = layout.caretRect(model.focus);\n const line = layout.lines[caret.line];\n const hard = direction < 0 ? lineStartAt(model.text, model.focus) : lineEndAt(model.text, model.focus);\n const visual = direction < 0 ? line.start : line.end;\n model.moveTo(direction < 0 ? Math.max(hard, visual) : Math.min(hard, visual), extend);\n this.afterSelectionChange(node, model);\n return;\n }\n model.move(unit, direction, extend);\n this.afterSelectionChange(node, model);\n }\n\n // ---------------------------------------------------------------------------\n // Text input from the shell\n // ---------------------------------------------------------------------------\n\n /**\n * A `beforeinput` from the shell's editing proxy, in the DOM's\n * vocabulary. Returns true when it was applied to the focused\n * editable. Composition types are not handled here: the composition\n * methods carry them, and an engine that fires both must not insert\n * twice.\n */\n beforeInput(inputType: string, data: string | null): boolean {\n const node = this.focusedEditable;\n if (node === null) {\n return false;\n }\n const model = editorFor(node);\n const readOnly = isReadOnly(node);\n switch (inputType) {\n case 'insertText':\n case 'insertReplacementText':\n case 'insertFromDrop':\n case 'insertFromYank':\n if (data === null || data.length === 0) {\n return false;\n }\n return readOnly || this.insert(node, model, inputType, data);\n case 'insertFromPaste':\n return readOnly || (data !== null && this.insert(node, model, inputType, data));\n case 'insertLineBreak':\n case 'insertParagraph':\n if (!isMultiline(node)) {\n return false;\n }\n return readOnly || this.applyEdit(node, model, inputType, '\\n', () => model.insertText('\\n'));\n case 'deleteContentBackward':\n case 'deleteWordBackward':\n case 'deleteSoftLineBackward':\n case 'deleteHardLineBackward':\n case 'deleteContentForward':\n case 'deleteWordForward':\n case 'deleteSoftLineForward':\n case 'deleteHardLineForward':\n case 'deleteContent':\n case 'deleteByCut':\n case 'deleteByDrag': {\n if (readOnly) {\n return true;\n }\n const unit: EditUnit = /Word/.test(inputType) ? 'word' : /Line/.test(inputType) ? 'line' : 'grapheme';\n const forward = /Forward/.test(inputType);\n return this.applyEdit(node, model, inputType, null, () => {\n if (inputType === 'deleteContent' || inputType === 'deleteByCut' || inputType === 'deleteByDrag') {\n model.replaceRange(model.start, model.end, '');\n } else if (forward) {\n model.deleteForward(unit);\n } else {\n model.deleteBackward(unit);\n }\n });\n }\n case 'historyUndo':\n return readOnly || this.history(node, model, inputType, () => model.undo());\n case 'historyRedo':\n return readOnly || this.history(node, model, inputType, () => model.redo());\n default:\n return false;\n }\n }\n\n /** Inserts text at the selection of the focused editable, as typing would. */\n insertText(text: string): boolean {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return false;\n }\n return this.insert(node, editorFor(node), 'insertText', text);\n }\n\n /**\n * Replaces the focused editable's whole text, as an assistive\n * technology's \"set value\" does.\n *\n * `insertReplacementText` is the DOM's own input type for this, so an\n * `onBeforeInput` that vets edits sees the same vocabulary it sees\n * for typing, and an app that rejects the edit rejects this one too.\n * Selecting everything first is what makes it one undo step and one\n * `onInput` rather than a delete and an insert.\n */\n replaceText(text: string): boolean {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return false;\n }\n const model = editorFor(node);\n model.selectAll();\n return this.insert(node, model, 'insertReplacementText', text);\n }\n\n /** Pastes text: newlines are kept in a multiline field and become spaces in a single-line one. */\n paste(text: string): boolean {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return false;\n }\n return this.insert(node, editorFor(node), 'insertFromPaste', text);\n }\n\n private insert(node: UiNode, model: EditableTextModel, inputType: string, text: string): boolean {\n const data = isMultiline(node) ? text : text.replace(/\\r\\n|\\r|\\n/g, ' ');\n return this.applyEdit(node, model, inputType, data, () => model.insertText(data));\n }\n\n // ---------------------------------------------------------------------------\n // Composition\n // ---------------------------------------------------------------------------\n\n compositionStart(): void {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return;\n }\n const model = editorFor(node);\n model.beginComposition();\n this.compositionOpen = true;\n this.afterTextChange(node, model, false);\n }\n\n /** The composition text so far and the caret within it. */\n compositionUpdate(text: string, caret: number = text.length): void {\n const node = this.focusedEditable;\n if (node === null || isReadOnly(node)) {\n return;\n }\n const model = editorFor(node);\n model.updateComposition(text, caret);\n this.compositionOpen = true;\n this.afterTextChange(node, model, false);\n }\n\n /** The IME committed `text` (possibly empty: the composition was cancelled). */\n compositionEnd(text: string): void {\n const node = this.focusedEditable;\n this.compositionOpen = false;\n if (node === null || isReadOnly(node)) {\n return;\n }\n const model = editorFor(node);\n const data = isMultiline(node) ? text : text.replace(/\\r\\n|\\r|\\n/g, ' ');\n const before = model.text;\n model.commitComposition(data);\n this.afterTextChange(node, model, before !== model.text);\n }\n\n // ---------------------------------------------------------------------------\n // Pointer\n // ---------------------------------------------------------------------------\n\n /**\n * A press on an editable places the caret (Shift extends the\n * selection to it); a second press within the double-click window\n * selects the word, a third the line. Dragging afterwards extends the\n * selection from the anchor.\n */\n pointerDown(node: UiNode, x: number, y: number, modifiers: UiKeyModifiers): void {\n if (!isEditableNode(node)) {\n return;\n }\n const model = editorFor(node);\n const layout = this.layoutOf(node);\n const local = this.host.toLocal(node, x, y);\n const offset = layout.offsetAt(local.x, local.y);\n const now = this.host.now();\n const last = this.lastPress;\n // Shift+press extends the selection whatever came before it, and\n // does not count toward a double click.\n const count =\n !modifiers.shift &&\n last !== null &&\n last.node === node &&\n now - last.at <= MULTI_CLICK_MS &&\n Math.abs(last.x - x) <= MULTI_CLICK_SLOP &&\n Math.abs(last.y - y) <= MULTI_CLICK_SLOP\n ? last.count + 1\n : 1;\n this.lastPress = { node, x, y, at: now, count };\n this.dragging = node;\n\n if (modifiers.shift) {\n model.moveTo(offset, true);\n } else if (count === 2) {\n const word = wordRangeAt(model.text, offset);\n model.select(word.start, word.end);\n } else if (count >= 3) {\n model.select(lineStartAt(model.text, offset), lineEndAt(model.text, offset));\n } else {\n model.select(offset);\n }\n model.breakUndoGroup();\n this.afterSelectionChange(node, model);\n }\n\n pointerMove(node: UiNode, x: number, y: number): void {\n if (this.dragging !== node) {\n return;\n }\n const model = editorFor(node);\n const layout = this.layoutOf(node);\n const local = this.host.toLocal(node, x, y);\n const offset = layout.offsetAt(local.x, local.y);\n if (offset !== model.focus) {\n model.moveTo(offset, true);\n this.afterSelectionChange(node, model);\n }\n }\n\n pointerUp(): void {\n this.dragging = null;\n }\n\n // ---------------------------------------------------------------------------\n // State for the shell, caret blink\n // ---------------------------------------------------------------------------\n\n /** The focused editable's state for the shell to mirror, or null. */\n state(): EditingState | null {\n const node = this.focusedEditable;\n if (node === null || this.host.recordFor(node) === undefined) {\n return null;\n }\n const model = editorFor(node);\n const caret = this.layoutOf(node).caretRect();\n const visible = this.host.visibleBox(node);\n return {\n text: model.text,\n selectionStart: model.start,\n selectionEnd: model.end,\n caret: { x: visible.x + caret.x, y: visible.y + caret.y, width: 1, height: caret.height },\n multiline: isMultiline(node),\n composing: model.composing\n };\n }\n\n /**\n * When the focused caret next toggles, or undefined when nothing\n * blinks: no focused editable, a composition in progress (the caret\n * holds), or a hidden page.\n */\n nextCaretChange(now: number): number | undefined {\n const node = this.focusedEditable;\n if (node === null || !this.visible) {\n return undefined;\n }\n const model = editorFor(node);\n if (model.composing || !model.collapsed) {\n return undefined;\n }\n return nextCaretToggle(model, now);\n }\n\n /**\n * The page was hidden or shown. A hidden page stops the blink so a\n * background tab schedules no frames; showing it restarts the caret.\n */\n setVisible(visible: boolean): void {\n if (this.visible === visible) {\n return;\n }\n this.visible = visible;\n const node = this.focusedEditable;\n if (node !== null) {\n editorFor(node).blinkOrigin = this.host.now();\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n\n /** Whether the caret may be drawn at all (the page is visible). */\n get caretEnabled(): boolean {\n return this.visible;\n }\n\n // ---------------------------------------------------------------------------\n // Internals\n // ---------------------------------------------------------------------------\n\n private handleFocusChange(node: UiNode | null): void {\n const previous = this.focusedEditable;\n const next = node !== null && isEditableNode(node) ? node : null;\n if (previous === next) {\n return;\n }\n if (previous !== null) {\n const model = editorFor(previous);\n if (model.composing) {\n model.commitComposition(model.text.slice(model.composition!.start, model.composition!.end));\n }\n model.focused = false;\n this.host.markDirty(previous, DirtyFlags.Paint);\n }\n this.focusedEditable = next;\n this.compositionOpen = false;\n this.verticalGoalX = undefined;\n if (next !== null) {\n const model = editorFor(next);\n model.focused = true;\n model.blinkOrigin = this.host.now();\n this.host.markDirty(next, DirtyFlags.Paint);\n }\n }\n\n /**\n * Runs an edit unless a `beforeinput` listener cancels it, then\n * reports it. Returns true when the input was consumed either way.\n */\n private applyEdit(\n node: UiNode,\n model: EditableTextModel,\n inputType: string,\n data: string | null,\n run: () => void\n ): boolean {\n const before = new UiBeforeInputEvent(inputType, data);\n this.dispatcher.dispatch(before, node);\n if (before.defaultPrevented) {\n return true;\n }\n const text = model.text;\n run();\n this.afterTextChange(node, model, text !== model.text);\n return true;\n }\n\n private history(node: UiNode, model: EditableTextModel, inputType: string, run: () => boolean): boolean {\n const before = new UiBeforeInputEvent(inputType, null);\n this.dispatcher.dispatch(before, node);\n if (before.defaultPrevented) {\n return true;\n }\n const text = model.text;\n run();\n this.afterTextChange(node, model, text !== model.text);\n return true;\n }\n\n private afterTextChange(node: UiNode, model: EditableTextModel, changed: boolean): void {\n this.verticalGoalX = undefined;\n model.blinkOrigin = this.host.now();\n // The text is what layout measures, so a change is content and\n // layout; an unchanged text with a moved caret is paint only.\n this.host.markDirty(\n node,\n changed || this.compositionOpen ? DirtyFlags.Content | DirtyFlags.Layout : DirtyFlags.Paint\n );\n if (changed) {\n this.dispatcher.dispatch(new UiTextChangeEvent(model.text, model.start, model.end), node);\n }\n this.revealCaret(node);\n }\n\n private afterSelectionChange(node: UiNode, model: EditableTextModel, keepGoal = false): void {\n if (!keepGoal) {\n this.verticalGoalX = undefined;\n }\n model.blinkOrigin = this.host.now();\n this.host.markDirty(node, DirtyFlags.Paint);\n this.revealCaret(node);\n }\n\n /**\n * Asks the host to scroll the caret into view, when the node has been\n * laid out: the field's own text first, then any scroll container\n * around it.\n *\n * `layoutOf` places the text where it is seen, so the caret comes\n * back with the field's own scroll already taken off; the reveal is\n * asked for in the node's unscrolled coordinates, which is where the\n * offset it wants is measured from.\n */\n private revealCaret(node: UiNode): void {\n const rec = this.host.recordFor(node);\n if (rec === undefined) {\n return;\n }\n const caret = this.layoutOf(node).caretRect();\n this.host.reveal(node, {\n x: caret.x + rec.scrollX - CARET_REVEAL_PADDING,\n y: caret.y + rec.scrollY - CARET_REVEAL_PADDING,\n width: 1 + 2 * CARET_REVEAL_PADDING,\n height: caret.height + 2 * CARET_REVEAL_PADDING\n });\n }\n\n /**\n * The node's text laid out in node-local coordinates: the content box\n * starts at the padding and is shifted by the field's own scroll, as\n * it is for the renderers in the parent's space. Points from the\n * pointer and the caret box the shell is given are both in that same\n * seen space, so neither has to know the offset. Before the first\n * layout the record is empty and the lines fall at the origin, which\n * is still a valid place for a caret.\n */\n private layoutOf(node: UiNode): EditableLayout {\n const rec = this.host.recordFor(node);\n const state = resolvePaintState(node, this.paint);\n const box: LayoutBox =\n rec === undefined\n ? { x: 0, y: 0, width: 0, height: 0 }\n : {\n x: rec.paddingLeft - rec.scrollX,\n y: rec.paddingTop - rec.scrollY,\n width: Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight),\n height: Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom)\n };\n return new EditableLayout(state.editor!, box, state, this.host.measurer);\n }\n}\n\nfunction deleteInputType(unit: EditUnit, direction: -1 | 1): string {\n const side = direction < 0 ? 'Backward' : 'Forward';\n switch (unit) {\n case 'word':\n return `deleteWord${side}`;\n case 'line':\n case 'document':\n return `deleteSoftLine${side}`;\n default:\n return `deleteContent${side}`;\n }\n}\n","import type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from './LayoutTypes';\n\n/** A watched node's layout state: where it is, and how far it is scrolled. */\nexport interface NotifiedLayout {\n readonly box: LayoutBox;\n /** The container's own effective offset; zero for anything that does not scroll. */\n readonly scrollX: number;\n readonly scrollY: number;\n}\n\n/**\n * Tells listeners when a node's layout changed.\n *\n * Layout is readable at any moment — `worldBox(node)` answers \"where is\n * this\" — but nothing told a listener \"this changed on this frame\",\n * and a behaviour that follows a box (a split pane's divider, a drag\n * ghost, a `ResizeObserver` equivalent) needs exactly that.\n *\n * The cost is proportional to the number of registered listeners, not\n * to the size of the tree: a frame with none does nothing. A scroll\n * moves the world box of everything under the scroller, and reporting\n * that is correct — a thing that follows a box has to follow it when\n * the page scrolls too.\n *\n * **A container's own scroll counts as a change, even though its box\n * does not move.** That is the one case the box alone cannot describe:\n * scrolling a list moves everything inside it and leaves the list\n * itself exactly where it was, so a listener on the list heard\n * nothing. It is also the only fact an application has no other way to\n * learn — a wheel writes the offset from inside the runtime, and there\n * is no scroll event — which is why it is reported here rather than\n * through a second mechanism of its own. See `scrollPosition`.\n */\nexport class LayoutNotifier {\n private readonly listeners = new Map<UiNode, Set<(box: LayoutBox) => void>>();\n private readonly last = new Map<UiNode, NotifiedLayout>();\n\n /** Registers a listener; the returned function removes it. */\n add(node: UiNode, listener: (box: LayoutBox) => void): () => void {\n let forNode = this.listeners.get(node);\n if (forNode === undefined) {\n forNode = new Set();\n this.listeners.set(node, forNode);\n }\n forNode.add(listener);\n return () => {\n const set = this.listeners.get(node);\n if (set === undefined) {\n return;\n }\n set.delete(listener);\n if (set.size === 0) {\n this.listeners.delete(node);\n this.last.delete(node);\n }\n };\n }\n\n get size(): number {\n return this.listeners.size;\n }\n\n /** Whether anything is listening, so a frame can skip the walk. */\n isEmpty(): boolean {\n return this.listeners.size === 0;\n }\n\n /**\n * Calls the listeners of every watched node whose box or scroll\n * offset differs from what it was last told about.\n */\n notify(read: (node: UiNode) => NotifiedLayout): void {\n for (const [node, listeners] of this.listeners) {\n const current = read(node);\n const previous = this.last.get(node);\n if (previous !== undefined && sameLayout(previous, current)) {\n continue;\n }\n this.last.set(node, { box: { ...current.box }, scrollX: current.scrollX, scrollY: current.scrollY });\n for (const listener of listeners) {\n listener(current.box);\n }\n }\n }\n\n /** Drops a removed node's listeners, so nothing keeps it alive. */\n handleNodeRemoved(node: UiNode): void {\n this.listeners.delete(node);\n this.last.delete(node);\n }\n}\n\nfunction sameLayout(a: NotifiedLayout, b: NotifiedLayout): boolean {\n return a.scrollX === b.scrollX && a.scrollY === b.scrollY && sameBox(a.box, b.box);\n}\n\nfunction sameBox(a: LayoutBox, b: LayoutBox): boolean {\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n","import { isUiModifier, type UiModifier } from './UiModifier';\n\n/**\n * A set of modifiers named once and attached as one.\n *\n * The analogue of a SwiftUI `ViewModifier` or a Compose `Modifier`\n * chain, and the answer to the seventy-two `modifiers={[INTERACTION,\n * ...RING]}` sites across the two applications. Behaviour, not paint:\n * a bundle carries modifiers, and the properties an element is drawn\n * with stay on the element.\n *\n * const ROW = bundle(ROW_INTERACTION, RING);\n * <row modifiers={ROW}>…</row>\n *\n * There are two reasons to name it rather than to write the array out.\n * The first is the ordinary one: the set has a meaning, and the\n * meaning is worth a name. The second is that the array literal is not\n * free. A `modifiers` list is read on every render of the element's\n * component, and `[INTERACTION, ...RING]` allocates a fresh array and\n * copies the ring into it each time; a bundle is built once at module\n * level and is the same frozen array on every render, which is what\n * `sameArgs` and the reconciler are comparing against.\n *\n * Nesting is flattened, so a bundle composes with another bundle the\n * way a `Modifier` chain composes: `bundle(BASE, focusRing())` and\n * `bundle(BASE, OTHER_BUNDLE)` both give one flat list. Order is kept,\n * because the override cascade resolves a conflict in favour of\n * whichever modifier is later.\n */\nexport type UiModifierBundle = readonly UiModifier[];\n\n/**\n * Names a set of modifiers. Call it at module level, once.\n *\n * The result is frozen: a bundle is a value, and a list that some\n * caller pushed onto would change what every element carrying it does,\n * on the next render and not on this one, which is the kind of bug\n * that takes an afternoon.\n */\nexport function bundle(...parts: readonly (UiModifier | UiModifierBundle)[]): UiModifierBundle {\n const flat: UiModifier[] = [];\n for (const part of parts) {\n if (isUiModifier(part)) {\n flat.push(part);\n continue;\n }\n for (const nested of part) {\n flat.push(nested);\n }\n }\n return Object.freeze(flat);\n}\n\n/** An empty bundle, for a branch that adds nothing. One shared value. */\nexport const noModifiers: UiModifierBundle = Object.freeze([]) as UiModifierBundle;\n","import { isObservable, type Observable, type Subscription } from 'rxjs';\n\nimport type { DecorationShape } from '../rendering/Decorations';\nimport type { UiColorValue } from '../properties/UiPropertyValues';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/** What `decorated` takes: a list, or one that changes over time. */\nexport type Decorations = readonly DecorationShape[] | Observable<readonly DecorationShape[]>;\n\n/**\n * Shapes on a node, for as long as the modifier is attached.\n *\n * The plain form of `host.decorate`: everything else in this file is a\n * behaviour that decides *when* to draw. Useful on its own for a\n * static mark — a drop target's outline, a fixture the renderer parity\n * check can diff — and it is the smallest thing that exercises the\n * decoration path end to end.\n *\n * The argument is compared by identity, as every modifier's arguments\n * are, so hoist the array rather than building it in a render body.\n *\n * **An Observable of shapes is also accepted**, and is the only way to\n * draw something whose shape changes while it is on screen. A\n * modifier list is static per element, so a component that recomputed\n * its shapes could not hand them over; it subscribes here instead, and\n * the subscription is released on detach. What this buys is a picture\n * made of many rectangles that would otherwise have to be many nodes:\n * Segue's waveform is four hundred columns on one node, redrawn as the\n * track's peaks arrive, with nothing to lay out and nothing to hit\n * test. Hoist the Observable for the same reason you would hoist the\n * array.\n */\nexport const decorated = defineModifier<Decorations>({\n name: 'decorated',\n attach(host, shapes) {\n draw(host, shapes);\n },\n update(host, shapes) {\n draw(host, shapes);\n }\n});\n\n/**\n * The live subscription per host, so a second `update` replaces the\n * first rather than drawing from both.\n *\n * `host.own` releases on detach, which is right for the last one and\n * not enough for the one before it. Keyed by the host, which is one\n * per node per modifier, so two decorations on the same node keep\n * their own.\n */\nconst live = new WeakMap<UiModifierHost, Subscription>();\n\nfunction draw(host: UiModifierHost, shapes: Decorations): void {\n live.get(host)?.unsubscribe();\n live.delete(host);\n if (isObservable(shapes)) {\n const subscription = shapes.subscribe(next => host.decorate(next));\n live.set(host, subscription);\n host.own(subscription);\n return;\n }\n host.decorate(shapes);\n}\n\n/**\n * A ring outside whatever holds keyboard focus.\n *\n * Visible focus is an accessibility requirement and, until this\n * existed, the library had no way to meet it: a control could only\n * bind its own `borderColor` to a focus observable, which recolours\n * the control rather than marking it, and which every one of\n * twenty-odd components had to repeat.\n *\n * Two things make this a decoration rather than an overlay, and they\n * are the reasons decorations exist at all:\n *\n * - **It is clipped with the node.** A ring on a row scrolled half out\n * of a `ScrollView` is cut off at the same edge the row is, because\n * it is painted inside the node's own paint pass under its\n * ancestors' clips. An overlay drawn over the finished scene would\n * float over the scroller's edge.\n * - **It is outside the node's own box.** The ring sits `offset`\n * pixels clear of the control, so it never covers the control's\n * border or its content, which is exactly what a bound\n * `borderColor` could not avoid doing.\n *\n * The colour names a palette entry and is resolved at paint against\n * whatever theme the node inherits, so a ring inside a dark card is\n * the dark palette's without this modifier reading the environment.\n *\n * The ring is `:focus-visible`, not `:focus`: `attach` draws it only\n * while the host reports the focus as visible, so a control a pointer\n * press focused stays unmarked until a key is pressed. A control that\n * also recoloured its border on focus would say the same thing twice,\n * and say it for the wrong focus, which is why no component does.\n */\nexport interface FocusRingOptions {\n /** A palette name or a literal colour. Defaults to the `focusRing` token. */\n readonly color?: UiColorValue;\n /** Thickness of the band. */\n readonly width?: number;\n /** Clear space between the node's box and the ring. */\n readonly offset?: number;\n /**\n * Ring radius. Omitted means the node's own corner radius grown by\n * the ring's distance from it, so the ring is concentric with the\n * control without the caller knowing its radius.\n */\n readonly radius?: number;\n /** Paints the ring over the node's content instead of under it. */\n readonly after?: 'children';\n}\n\nconst DEFAULT_WIDTH = 2;\nconst DEFAULT_OFFSET = 2;\n\n/** The ring as a single stroke, placed by `decorationRect`. */\nfunction ringShapes(options: FocusRingOptions): readonly DecorationShape[] {\n const width = options.width ?? DEFAULT_WIDTH;\n const offset = options.offset ?? DEFAULT_OFFSET;\n return [\n {\n kind: 'stroke',\n color: options.color ?? 'focusRing',\n lineWidth: width,\n // The band lies inside the outset box, so an outset of\n // offset + width puts it between `offset` and `offset + width`\n // pixels outside the node.\n outset: offset + width,\n radius: options.radius,\n after: options.after\n }\n ];\n}\n\n/**\n * No `update`: the shapes are computed in `attach` from the options,\n * and a change of options is a detach followed by an attach — which is\n * the honest behaviour for a kind that cannot describe a change, and\n * costs nothing here because the options of a focus ring do not\n * change while it is attached.\n */\nconst kind = defineModifier<FocusRingOptions>({\n name: 'focusRing',\n attach(host, options) {\n const shapes = ringShapes(options);\n // Drawn for focus the keyboard can see, not for every focus: a mouse\n // press focuses the control it lands on, and a ring there would mark\n // something nobody is about to drive with the keys. The host reports\n // the change when a key makes that focus visible, so the ring\n // appears then. This is `:focus-visible`, done where the focus is.\n const sync = (): void => {\n host.decorate(host.isFocused() && host.isFocusVisible() ? shapes : null);\n };\n // A node may already hold focus when its ring attaches: a dialog\n // that auto-focused its first field re-renders, and the modifier\n // that lands on the focused node would otherwise wait for a focus\n // change that has already happened.\n sync();\n host.onFocusChange(sync);\n }\n});\n\nconst DEFAULT_RING: UiModifier<FocusRingOptions> = kind(Object.freeze({}));\n\n/**\n * The ring, with the theme's own colour and spacing.\n *\n * Called with no options it returns one shared value, which `sameArgs`\n * matches without walking anything. Options are plain data and are\n * compared by what they hold, so a ring built inline in a render\n * survives it; hoisting saves the allocation and the walk, not the\n * attachment.\n */\nexport function focusRing(options?: FocusRingOptions): UiModifier<FocusRingOptions> {\n return options === undefined ? DEFAULT_RING : kind(options);\n}\n","import { defineModifier, type UiModifier } from './UiModifier';\n\n/**\n * Takes focus the first time the node is laid out.\n *\n * The one a dialog needs: an overlay that opens with the caret already\n * in its first field, without the component that opened it reaching\n * for a `ref` and a focus store.\n *\n * **Why the first layout and not the attach.** A modifier attaches\n * while its node is still being built, before the builder inserts it\n * into its parent, so at attach time the node has no parent and is not\n * under the focus manager's traversal root. `UiFocusManager.focus`\n * would accept it anyway (it asks whether the node is focusable, not\n * where it is), and the result would be a focused node that Tab cannot\n * reach and that no ancestor sees the focus event from. The first\n * layout is the first moment the node is demonstrably on screen, and\n * it is the same frame in every case that matters, because a node\n * mounted into a live tree is laid out on the frame that mounted it.\n *\n * It fires once. A node that is autofocused, loses focus to something\n * the person clicked, and is then laid out again does not steal the\n * focus back; that would make an autofocus a focus trap.\n *\n * A node that cannot take focus is left alone and no error is raised:\n * `focus()` refuses a node that is inert, not focusable, or outside an\n * active focus scope, which is exactly the set of cases where an\n * application would not want the focus moved either.\n */\nconst kind = defineModifier<void>({\n name: 'autoFocus',\n attach(host) {\n let done = false;\n host.onLayout(() => {\n if (done) {\n return;\n }\n done = true;\n host.focus();\n });\n }\n});\n\n/**\n * One shared instance, so the arguments keep their identity across\n * renders and the modifier is never detached and re-attached (which\n * would autofocus a second time).\n */\nconst AUTO_FOCUS: UiModifier<void> = kind(undefined);\n\n/** Takes focus the first time the node is laid out. */\nexport function autoFocus(): UiModifier<void> {\n return AUTO_FOCUS;\n}\n","import type { UiNode } from '../graph/UiNode';\nimport { UiEventType, type UiInputEvent } from '../input/UiInputEvent';\nimport { defineModifier, type UiModifier } from './UiModifier';\n\nexport interface ClickOutsideOptions {\n /** Called when a press lands anywhere but inside the node. */\n readonly onOutside: (event: UiInputEvent) => void;\n /**\n * Nodes that count as inside even though they are not in the\n * subtree. The one that always needs this is the control that opened\n * the thing: without it, pressing the button that opened a popover\n * closes it and reopens it in one press.\n */\n readonly except?: () => readonly (UiNode | null | undefined)[];\n /**\n * Whether a wheel outside the node counts too. Default true: a page\n * scrolled out from under a popover leaves it hanging beside nothing.\n *\n * It is read at each event rather than at attach, so a caller may\n * flip it between renders.\n */\n readonly wheel?: boolean;\n}\n\n/**\n * Calls back when a press lands outside the node.\n *\n * The listener is at the root, in the capture phase, because the event\n * this cares about is the one that never arrives: a press somewhere\n * else is not observable from the node it missed. Capture also means\n * it is heard before the node that was pressed acts on it, so a\n * popover closes on the same press that activates whatever is beneath\n * it, rather than a frame later.\n *\n * It does not close anything itself, and it is not a backdrop. A\n * backdrop is an element that swallows the press (`OverlayEntry`'s\n * `dismissOnOutsidePress` is one); this hears the press and lets it\n * through, which is the behaviour a menu wants when the next thing the\n * person does is press a button on the page.\n */\nconst kind = defineModifier<ClickOutsideOptions>({\n name: 'clickOutside',\n attach(host, options) {\n let current = options;\n handlers.set(host, next => (current = next));\n const outside = (event: UiInputEvent): void => {\n const target = event.target;\n if (target === null || contains(host.node, target)) {\n return;\n }\n if (event.type === UiEventType.Wheel && current.wheel === false) {\n return;\n }\n for (const node of current.except?.() ?? []) {\n if (node !== null && node !== undefined && contains(node, target)) {\n return;\n }\n }\n current.onOutside(event);\n };\n host.onRoot(UiEventType.PointerDown, outside);\n host.onRoot(UiEventType.Wheel, outside);\n },\n update(host, options) {\n // The listeners close over the latest options rather than being\n // re-registered, so a handler that changes every render does not\n // cost a pair of root listeners each time.\n handlers.get(host)?.(options);\n }\n});\n\nconst handlers = new WeakMap<object, (options: ClickOutsideOptions) => void>();\n\n/** Calls back when a press lands outside the node. */\nexport function clickOutside(options: ClickOutsideOptions): UiModifier<ClickOutsideOptions> {\n return kind(options);\n}\n\nfunction contains(ancestor: UiNode, node: UiNode): boolean {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current === ancestor) {\n return true;\n }\n }\n return false;\n}\n","import type { Subject } from 'rxjs';\n\nimport { UiEventType, type UiGestureEvent, type UiPointerEvent } from '../input/UiInputEvent';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/** How far the node has been moved from where the drag picked it up. */\nexport interface DragOffset {\n readonly x: number;\n readonly y: number;\n /**\n * How fast it was travelling when it was let go, in pixels per\n * second, along each axis.\n *\n * Zero everywhere but the offset handed to `onEnd`, because there is\n * no honest speed to report for a position half way through a drag\n * that the next move will replace. The unit is the one\n * `UiSpringOptions.velocity` takes, which is the whole reason it is\n * here: a spring given no velocity starts from rest, so a card\n * thrown across the screen used to stop dead the instant the finger\n * left it, and there was no way for the application to do better\n * because the drag never told it how fast the card was going.\n */\n readonly velocityX?: number;\n readonly velocityY?: number;\n}\n\nexport interface DraggableOptions {\n /** Which axes the node may move on. Default `'both'`. */\n readonly axis?: 'x' | 'y' | 'both';\n /**\n * Whether the drag begins on a press and a move, or only after a\n * long press. Default `'press'`.\n *\n * This modifier was first written as the `DragStart` gesture, and\n * `DragStart` is the long-press one: the recognizer resolves a press\n * to a Pan when it moves before the hold time and to a Drag only\n * after a LongPress. C5 found the same thing from the other side,\n * when a split pane's divider listened for `DragMove` and did not\n * move. A card the pointer picks up immediately is a Pan, so that is\n * the default here, and the long-press variety is an option rather\n * than the only behaviour.\n */\n readonly start?: 'press' | 'longPress';\n /** Every position while the drag runs, as an offset from the start. */\n readonly offset?: Subject<DragOffset>;\n readonly onStart?: () => void;\n readonly onEnd?: (offset: DragOffset) => void;\n /**\n * Properties to write while the drag runs, in the shape\n * `interactive` uses for hover and press: `{ opacity: 0.8, zIndex: 10 }`\n * puts the node over its siblings while it is carried.\n *\n * `zIndex` reaches no further than that, because it reorders the\n * children of one parent. A node carried out of its container and\n * over another one, a chip dragged from one tray into the next, is\n * still painted inside the container it left, and the container it\n * is over paints after that one and covers it. `{ lift: true }` is\n * the property for that case: the node is drawn after everything\n * else up to the nearest `liftBoundary`, and outside its ancestors'\n * clips, for as long as the drag runs.\n */\n readonly dragging?: Readonly<Record<string, unknown>>;\n /**\n * Whether the node stays where it was dropped. Default `true`.\n *\n * False is the right choice when the application moves the thing\n * itself: a list that reorders on drop wants the item back in the\n * flow at its new index, not held one row above it by a translation\n * nobody owns any more.\n */\n readonly keepOffset?: boolean;\n}\n\n/**\n * Moves a node with the pointer.\n *\n * It writes the movement as a translation on `transform`, through the\n * override cascade, so nothing about the node's layout changes: the\n * box stays where it was, its neighbours do not shuffle, and dropping\n * a node whose offset is released puts back exactly the transform the\n * element declared, including no transform at all. The declared\n * transform is read once at attach and composed with, so a node that\n * is already rotated goes on rotating while it is dragged.\n *\n * The gesture events are stopped from propagating while a drag is\n * running. `UiTouchScroller` listens for pans at the root, so without\n * that a card dragged inside a scroll view would move and scroll its\n * container at the same time; `SplitPane` stops them for the same\n * reason.\n *\n * Nothing here changes the cursor. That is a message to the shell\n *, and a node's `cursor` property already\n * sends it, so an application says `cursor: 'grab'` on the element and\n * this modifier stays inside the render thread.\n */\nconst kind = defineModifier<DraggableOptions>({\n name: 'draggable',\n attach(host, options) {\n controllers.set(host, new Draggable(host, options).attach());\n },\n update(host, options) {\n // In place rather than a detach and a re-attach, so options that\n // change while a card is in the air do not drop it.\n controllers.get(host)?.setOptions(options);\n }\n});\n\nconst controllers = new WeakMap<UiModifierHost, Draggable>();\n\n/**\n * Moves a node with the pointer.\n *\n * Hoist the options object: arguments are compared by identity, so a\n * fresh literal every render is a fresh set of arguments.\n */\nexport function draggable(options: DraggableOptions = EMPTY_OPTIONS): UiModifier<DraggableOptions> {\n return kind(options);\n}\n\nconst EMPTY_OPTIONS: DraggableOptions = Object.freeze({});\n\nclass Draggable {\n /** Where the pointer was when the gesture started. */\n private originX = 0;\n private originY = 0;\n /** The offset in force, which is what a later drag adds to. */\n private offsetX = 0;\n private offsetY = 0;\n /** The offset this gesture started from, so a second drag continues. */\n private startX = 0;\n private startY = 0;\n private dragging = false;\n\n /** What the element declared, which the translation is added to. */\n private basePivotX = 0;\n private basePivotY = 0;\n private baseScaleX = 1;\n private baseScaleY = 1;\n private baseRotation = 0;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: DraggableOptions\n ) {}\n\n attach(): this {\n const declared = this.host.get<Record<string, unknown> | null | undefined>('transform');\n if (declared !== undefined && declared !== null) {\n this.basePivotX = numberOr(declared.x, 0);\n this.basePivotY = numberOr(declared.y, 0);\n this.baseScaleX = numberOr(declared.scaleX, 1);\n this.baseScaleY = numberOr(declared.scaleY, 1);\n this.baseRotation = numberOr(declared.rotation, 0);\n }\n const longPress = this.options.start === 'longPress';\n // Every gesture event is a pointer event; the listener signature is\n // the base class because a listener may be registered for any type.\n this.host.on(longPress ? UiEventType.DragStart : UiEventType.PanStart, event =>\n this.begin(event as UiPointerEvent)\n );\n this.host.on(longPress ? UiEventType.DragMove : UiEventType.PanMove, event => this.move(event as UiPointerEvent));\n this.host.on(longPress ? UiEventType.DragEnd : UiEventType.PanEnd, event => this.end(event as UiPointerEvent));\n return this;\n }\n\n setOptions(options: DraggableOptions): void {\n // `start` is read when the listeners are registered, so changing it\n // mid-life would leave the modifier listening for the other\n // gesture. The kind has no `update` path for that; say so rather\n // than half-applying it.\n if ((options.start ?? 'press') !== (this.options.start ?? 'press')) {\n console.warn(\n `draggable on node '${this.host.node.id}' changed 'start' after it attached; the gesture it listens for is fixed at attach.`\n );\n }\n this.options = options;\n }\n\n private begin(event: UiPointerEvent): void {\n event.stopPropagation();\n this.dragging = true;\n this.originX = event.x;\n this.originY = event.y;\n this.startX = this.offsetX;\n this.startY = this.offsetY;\n for (const [property, value] of Object.entries(this.options.dragging ?? {})) {\n this.host.set(property, value);\n }\n this.options.onStart?.();\n }\n\n private move(event: UiPointerEvent): void {\n if (!this.dragging) {\n return;\n }\n event.stopPropagation();\n const axis = this.options.axis ?? 'both';\n const x = axis === 'y' ? this.startX : this.startX + (event.x - this.originX);\n const y = axis === 'x' ? this.startY : this.startY + (event.y - this.originY);\n this.setOffset(x, y);\n this.options.offset?.next({ x, y });\n }\n\n private end(event: UiPointerEvent): void {\n if (!this.dragging) {\n return;\n }\n event.stopPropagation();\n this.dragging = false;\n for (const property of Object.keys(this.options.dragging ?? {})) {\n this.host.clear(property);\n }\n const dropped: DragOffset = {\n x: this.offsetX,\n y: this.offsetY,\n velocityX: velocityOf(event, 'x'),\n velocityY: velocityOf(event, 'y')\n };\n if (this.options.keepOffset === false) {\n this.setOffset(0, 0);\n this.options.offset?.next({ x: 0, y: 0 });\n }\n this.options.onEnd?.(dropped);\n }\n\n /**\n * Writes the offset as the transform's translation, or takes it away.\n *\n * At rest the override is dropped rather than written as an identity\n * translation, so a node that was dragged and put back is\n * indistinguishable from one that never moved: no transform in its\n * paint state, and no matrix to multiply per frame.\n */\n private setOffset(x: number, y: number): void {\n this.offsetX = x;\n this.offsetY = y;\n if (x === 0 && y === 0) {\n this.host.clear('transform');\n return;\n }\n this.host.set('transform', {\n x: this.basePivotX,\n y: this.basePivotY,\n translateX: x,\n translateY: y,\n scaleX: this.baseScaleX,\n scaleY: this.baseScaleY,\n rotation: this.baseRotation\n });\n }\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n\n/**\n * The release speed the recognizer measured, or none.\n *\n * `PanEnd` and `DragEnd` are `UiGestureEvent`s and carry it. The check\n * is a property test rather than an `instanceof` because a spec that\n * dispatches a plain `UiPointerEvent` at the modifier is a legitimate\n * way to drive it, and reporting no speed is the right answer there.\n */\nfunction velocityOf(event: UiPointerEvent, axis: 'x' | 'y'): number {\n const value = (event as Partial<UiGestureEvent>)[axis === 'x' ? 'velocityX' : 'velocityY'];\n return typeof value === 'number' && Number.isFinite(value) ? value : 0;\n}\n","import type { UiNode } from '../graph/UiNode';\nimport {\n dragSessionFor,\n type UiDragPayload,\n type UiDragResult,\n type UiDragSession,\n type UiDragState,\n type UiDropEffect,\n type UiDropZone\n} from '../input/UiDragSession';\nimport { UiEventType, type UiGestureEvent, type UiPointerEvent } from '../input/UiInputEvent';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/**\n * Keeps a zone registered with the session for the graph it is in.\n *\n * A modifier attaches before its node is linked into the tree: the\n * builder writes an element's props, attaches its modifiers and builds\n * its children, and only then makes the edge to its parent. A zone\n * that resolved its session once at attach would therefore register\n * with a session keyed on itself, and would be invisible to every drag\n * that started anywhere else. So the session is re-resolved whenever\n * there is a reason to think the answer changed: at attach, at the\n * node's first layout, and when a drag begins on the node. The\n * registration moves with it.\n */\nclass ZoneRegistration {\n private session: UiDragSession | null = null;\n private release: (() => void) | null = null;\n\n constructor(private readonly zone: UiDropZone) {}\n\n sync(): UiDragSession {\n const next = dragSessionFor(this.zone.node);\n if (next !== this.session) {\n this.release?.();\n this.release = next.addZone(this.zone);\n this.session = next;\n }\n return next;\n }\n\n dispose(): void {\n this.release?.();\n this.release = null;\n this.session = null;\n }\n}\n\n/** How fast a drag near a container's edge scrolls it, and from how far. */\nexport interface AutoScrollOptions {\n /** How close to the edge, in pixels, the pointer has to come. Default 48. */\n readonly edge?: number;\n /** Pixels per second at the very edge. Default 900. */\n readonly speed?: number;\n}\n\nexport interface DropTargetOptions {\n /**\n * Which payloads this zone will take: a type, several types, or a\n * predicate for a zone whose answer depends on the payload's data.\n */\n readonly accepts: string | readonly string[] | ((payload: UiDragPayload) => boolean);\n /**\n * The drag was let go here. What it did, so the source can say\n * whether the thing moved or was copied; returning nothing means\n * `'move'`, which is what almost every drop is.\n */\n readonly onDrop: (payload: UiDragPayload, at: { readonly x: number; readonly y: number }) => UiDropEffect | void;\n /** A payload this zone accepts arrived over it. */\n readonly onEnter?: (payload: UiDragPayload) => void;\n /** It moved while over the zone. */\n readonly onOver?: (payload: UiDragPayload, at: { readonly x: number; readonly y: number }) => void;\n /** It left, or the drag ended elsewhere. */\n readonly onLeave?: () => void;\n /**\n * Properties to write while an accepted payload is over the zone, in\n * the shape `interactive` uses for hover and press:\n * `{ borderColor: 'accent', backgroundColor: 'surfaceRaised' }`.\n *\n * A drop zone that does not light up is a drop zone nobody finds. It\n * is here rather than left to the application because the state is\n * the modifier's and an application cannot see it without keeping a\n * copy.\n */\n readonly over?: Readonly<Record<string, unknown>>;\n /**\n * Whether a drag held near this container's edge scrolls it.\n *\n * Only meaningful on a scroll container, and the reason a long list\n * is reorderable at all: without it the only rows you can reach are\n * the ones already on screen. Off by default, since a zone that is\n * not a scroller has nothing to scroll.\n */\n readonly autoScroll?: boolean | AutoScrollOptions;\n}\n\nexport interface DragSourceOptions {\n /**\n * What is being carried. A function when the payload depends on\n * state that changes between renders, which is most of them.\n */\n readonly payload: UiDragPayload | (() => UiDragPayload);\n /**\n * Whether the drag opens on a press and a move, or only after a long\n * press. Default `'press'`, matching `draggable`, and for the same\n * reason: the recognizer resolves a press that moves immediately to a\n * Pan and only a held press to a Drag.\n */\n readonly start?: 'press' | 'longPress';\n /** Properties to write while the drag runs. */\n readonly dragging?: Readonly<Record<string, unknown>>;\n /**\n * The drag ended. `result` is null when it was let go over nothing\n * that would take it, and `velocity` is the speed it was travelling\n * at, in pixels per second, ready for a spring that carries the node\n * back.\n */\n readonly onEnd?: (result: UiDragResult | null, velocity: { readonly x: number; readonly y: number }) => void;\n}\n\n/**\n * Somewhere a drag can be put down.\n *\n * `draggable` moves a node's transform and stops, and for a year that\n * was the whole of dragging: every application that needed a drag to\n * *mean* something wrote its own hit testing over its own coordinates.\n * Segue's queue did, over a row height constant and a rounded division.\n *\n * The zone registers itself with the session for its graph and answers\n * three questions: where am I, would I take this, and what did the drop\n * do. Everything else is the session's, so two applications cannot\n * disagree about it: which of several overlapping zones wins, when enter\n * and leave fire, and what a drop reports back to the source.\n *\n * The box it reports is `layoutBox()`, which is where the node is\n * *seen*: a row scrolled half off the top of a list is half a drop\n * target, exactly as it looks.\n */\nconst dropTargetKind = defineModifier<DropTargetOptions>({\n name: 'dropTarget',\n attach(host, options) {\n zones.set(host, new DropTarget(host, options).attach());\n },\n update(host, options) {\n zones.get(host)?.setOptions(options);\n }\n});\n\nconst zones = new WeakMap<UiModifierHost, DropTarget>();\n\nexport function dropTarget(options: DropTargetOptions): UiModifier<DropTargetOptions> {\n return dropTargetKind(options);\n}\n\nclass DropTarget implements UiDropZone {\n private readonly registration = new ZoneRegistration(this);\n private scrolling: ReturnType<typeof setInterval> | null = null;\n private toward = 0;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: DropTargetOptions\n ) {}\n\n get node(): UiNode {\n return this.host.node;\n }\n\n attach(): this {\n this.registration.sync();\n this.host.onLayout(() => this.registration.sync());\n this.host.own(() => {\n this.stopScrolling();\n this.registration.dispose();\n });\n return this;\n }\n\n setOptions(options: DropTargetOptions): void {\n this.options = options;\n }\n\n boxOf() {\n return this.host.layoutBox();\n }\n\n accepts(payload: UiDragPayload): boolean {\n const rule = this.options.accepts;\n if (typeof rule === 'function') {\n return rule(payload);\n }\n if (typeof rule === 'string') {\n return rule === payload.type;\n }\n return rule.includes(payload.type);\n }\n\n enter(state: UiDragState): void {\n for (const [property, value] of Object.entries(this.options.over ?? {})) {\n this.host.set(property, value);\n }\n this.options.onEnter?.(state.payload);\n this.over(state);\n }\n\n over(state: UiDragState): void {\n this.options.onOver?.(state.payload, { x: state.x, y: state.y });\n this.updateAutoScroll(state);\n }\n\n leave(): void {\n for (const property of Object.keys(this.options.over ?? {})) {\n this.host.clear(property);\n }\n this.stopScrolling();\n this.options.onLeave?.();\n }\n\n drop(state: UiDragState): UiDropEffect {\n return this.options.onDrop(state.payload, { x: state.x, y: state.y }) ?? 'move';\n }\n\n // ---------------------------------------------------------------------\n // Auto-scroll\n // ---------------------------------------------------------------------\n\n /**\n * Scrolls the container while the drag is held near one of its edges.\n *\n * The rate is driven by a timer rather than by pointer movement, and\n * it has to be: the gesture a person makes is to hold the card still\n * at the top of the list and wait, which produces no pointer events\n * at all. The interval is cleared when the drag leaves, when it ends,\n * and when the modifier detaches, so nothing survives the node.\n */\n private updateAutoScroll(state: UiDragState): void {\n const settings = this.options.autoScroll;\n if (settings === undefined || settings === false) {\n return;\n }\n const box = this.host.layoutBox();\n const offset = this.host.scrollOffset();\n if (box === null || offset === null) {\n return;\n }\n const edge = (settings === true ? undefined : settings.edge) ?? 48;\n const speed = (settings === true ? undefined : settings.speed) ?? 900;\n const fromTop = state.y - box.y;\n const fromBottom = box.y + box.height - state.y;\n let fraction = 0;\n if (fromTop < edge) {\n fraction = -(edge - Math.max(fromTop, 0)) / edge;\n } else if (fromBottom < edge) {\n fraction = (edge - Math.max(fromBottom, 0)) / edge;\n }\n this.toward = fraction * speed;\n if (this.toward === 0) {\n this.stopScrolling();\n return;\n }\n if (this.scrolling === null) {\n this.scrolling = setInterval(() => this.stepScroll(), SCROLL_TICK_MS);\n }\n }\n\n private stepScroll(): void {\n const offset = this.host.scrollOffset();\n if (offset === null || this.toward === 0) {\n this.stopScrolling();\n return;\n }\n const next = Math.max(0, offset.y + (this.toward * SCROLL_TICK_MS) / 1000);\n if (next === offset.y) {\n return;\n }\n // The property, not an override: the runtime scrolls a container by\n // writing `scrollY` and the engine clamps it to the content on the\n // next pass, so writing it here is the same scroll a wheel makes.\n this.host.node.setProperty('scrollY', next);\n this.host.requestFrame();\n }\n\n private stopScrolling(): void {\n if (this.scrolling !== null) {\n clearInterval(this.scrolling);\n this.scrolling = null;\n }\n this.toward = 0;\n }\n}\n\n/** How often an auto-scroll advances. One frame at 60 Hz. */\nconst SCROLL_TICK_MS = 16;\n\n/**\n * Opens a drag carrying a payload, and reports where it was put down.\n *\n * It moves nothing. `draggable` already moves a node with the pointer\n * and does it well, so this is the other half rather than a second copy\n * of the first: put both on the element and the card follows the\n * pointer and knows what it is. Splitting them is also what lets a drag\n * source be something that does *not* move, like a swatch that stays\n * where it is while a colour is carried off it.\n */\nconst dragSourceKind = defineModifier<DragSourceOptions>({\n name: 'dragSource',\n attach(host, options) {\n sources.set(host, new DragSource(host, options).attach());\n },\n update(host, options) {\n sources.get(host)?.setOptions(options);\n }\n});\n\nconst sources = new WeakMap<UiModifierHost, DragSource>();\n\nexport function dragSource(options: DragSourceOptions): UiModifier<DragSourceOptions> {\n return dragSourceKind(options);\n}\n\nclass DragSource {\n private session: UiDragSession | null = null;\n private dragging = false;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: DragSourceOptions\n ) {}\n\n attach(): this {\n const longPress = this.options.start === 'longPress';\n this.host.on(longPress ? UiEventType.DragStart : UiEventType.PanStart, event =>\n this.begin(event as UiPointerEvent)\n );\n this.host.on(longPress ? UiEventType.DragMove : UiEventType.PanMove, event => this.move(event as UiPointerEvent));\n this.host.on(longPress ? UiEventType.DragEnd : UiEventType.PanEnd, event => this.end(event as UiGestureEvent));\n this.host.own(() => {\n if (this.dragging) {\n this.session?.cancel();\n }\n });\n return this;\n }\n\n setOptions(options: DragSourceOptions): void {\n this.options = options;\n }\n\n private begin(event: UiPointerEvent): void {\n const payload = typeof this.options.payload === 'function' ? this.options.payload() : this.options.payload;\n this.dragging = true;\n // Resolved here rather than at attach: a modifier attaches before\n // its node is linked into the tree, and by the time a gesture\n // reaches it the node is where it belongs.\n this.session = dragSessionFor(this.host.node);\n this.session.begin(payload, event.x, event.y, this.host.node);\n for (const [property, value] of Object.entries(this.options.dragging ?? {})) {\n this.host.set(property, value);\n }\n }\n\n private move(event: UiPointerEvent): void {\n if (!this.dragging) {\n return;\n }\n this.session?.move(event.x, event.y);\n }\n\n private end(event: UiGestureEvent): void {\n if (!this.dragging) {\n return;\n }\n this.dragging = false;\n for (const property of Object.keys(this.options.dragging ?? {})) {\n this.host.clear(property);\n }\n const result = this.session?.end() ?? null;\n this.options.onEnd?.(result, { x: event.velocityX ?? 0, y: event.velocityY ?? 0 });\n }\n}\n\nexport interface ReorderableOptions {\n /**\n * Which list this row belongs to. Two lists on one page have\n * different names, so a row cannot be dropped into a list that does\n * not want it.\n */\n readonly list: string;\n /** Where this row currently sits. */\n readonly index: number;\n /**\n * Move the row, now.\n *\n * Called as the drag crosses each row rather than once at the end, so\n * what is on screen is always the real order rather than a preview of\n * one: a drag abandoned half way leaves something coherent behind,\n * and there is no separate \"committed\" and \"shown\" order to keep in\n * step.\n */\n readonly onMove: (from: number, to: number) => void;\n /** Which way the row is carried. Default `'y'`. */\n readonly axis?: 'x' | 'y' | 'both';\n /** Whether the row is picked up on a press or on a long press. Default `'press'`. */\n readonly start?: 'press' | 'longPress';\n /** Properties to write on the row while it is being carried. */\n readonly dragging?: Readonly<Record<string, unknown>>;\n /** Told when this row is picked up and put down, for a list that wants to know. */\n readonly onDragChange?: (dragging: boolean) => void;\n}\n\n/**\n * A row that can be dragged into a different place in its list.\n *\n * One modifier on the row rather than a component around the list,\n * because a reorderable list is not a widget: it is a list whose rows\n * happen to be draggable, and every application's rows look different.\n *\n * It is a drag source and a drop target on the same node, over the same\n * session, which is what makes the whole thing about twenty lines: the\n * session already answers \"which row is the pointer over\", and a row\n * that hears an accepted payload enter it knows both indices and can\n * call `onMove` immediately.\n *\n * ## Why the row is placed from its layout box\n *\n * The obvious way to carry a row is to translate it by how far the\n * pointer has moved since the press. That works until the first\n * reorder, at which point the row has *also* been moved by the layout,\n * and the two movements add up: the row runs away from the finger at\n * twice the speed. Segue's hand-written version fixed it by adding a\n * row height back onto the origin each time it crossed one, which is\n * correct only for rows of a fixed height that it had to name a\n * constant for.\n *\n * This holds the grab point instead: where in the row the person took\n * hold of it. The translation is then always `pointer - grab -\n * layoutBox().y`, recomputed on every move *and on every layout*, so a\n * reorder underneath the row corrects itself on the frame it happens\n * and rows of different heights need no arithmetic at all.\n */\nconst reorderableKind = defineModifier<ReorderableOptions>({\n name: 'reorderable',\n attach(host, options) {\n rows.set(host, new Reorderable(host, options).attach());\n },\n update(host, options) {\n rows.get(host)?.setOptions(options);\n }\n});\n\nconst rows = new WeakMap<UiModifierHost, Reorderable>();\n\nexport function reorderable(options: ReorderableOptions): UiModifier<ReorderableOptions> {\n return reorderableKind(options);\n}\n\n/** What a reorder drag carries: the list it came from, and where it is now. */\ninterface ReorderData {\n list: string;\n index: number;\n /**\n * Whether a move has been committed that the layout has not caught up\n * with yet.\n *\n * A crossing is decided by hit-testing the rows' boxes, and a move\n * changes every box below it, but not until the frame after. Two\n * pointer moves inside one frame would therefore have the second one\n * decided against the boxes the first one has already invalidated,\n * and the list reorders in a way nobody asked for. So a crossing\n * waits for the layout it caused: the flag is set here and cleared by\n * the carried row's own layout notification.\n */\n settling: boolean;\n}\n\n/** The payload type a reorder uses, so nothing else can be dropped on a row. */\nconst REORDER = 'gesso/reorder';\n\nclass Reorderable implements UiDropZone {\n private readonly registration = new ZoneRegistration(this);\n private session: UiDragSession | null = null;\n private carried: ReorderData | null = null;\n /** Where in the row the person took hold of it. */\n private grabX = 0;\n private grabY = 0;\n private pointerX = 0;\n private pointerY = 0;\n private dragging = false;\n\n /** What the element declared, which the translation is added to. */\n private basePivotX = 0;\n private basePivotY = 0;\n private baseScaleX = 1;\n private baseScaleY = 1;\n private baseRotation = 0;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: ReorderableOptions\n ) {}\n\n get node(): UiNode {\n return this.host.node;\n }\n\n attach(): this {\n const declared = this.host.get<Record<string, unknown> | null | undefined>('transform');\n if (declared !== undefined && declared !== null) {\n this.basePivotX = numberOr(declared.x, 0);\n this.basePivotY = numberOr(declared.y, 0);\n this.baseScaleX = numberOr(declared.scaleX, 1);\n this.baseScaleY = numberOr(declared.scaleY, 1);\n this.baseRotation = numberOr(declared.rotation, 0);\n }\n this.registration.sync();\n\n const longPress = this.options.start === 'longPress';\n if (longPress) {\n // A held finger is also how a touchscreen asks for a context\n // menu, and a row that is about to be picked up is not asking a\n // question about itself. Refusing the default on the LongPress is\n // the escape hatch the recognizer documents for exactly this.\n this.host.on(UiEventType.LongPress, event => event.preventDefault());\n }\n this.host.on(longPress ? UiEventType.DragStart : UiEventType.PanStart, event =>\n this.begin(event as UiPointerEvent)\n );\n this.host.on(longPress ? UiEventType.DragMove : UiEventType.PanMove, event => this.move(event as UiPointerEvent));\n this.host.on(longPress ? UiEventType.DragEnd : UiEventType.PanEnd, () => this.end());\n // A reorder underneath the row moves the row; the translation is\n // recomputed from the new box so it stays under the finger.\n this.host.onLayout(() => {\n this.registration.sync();\n if (this.carried !== null) {\n // The move this row is carrying has landed; the next crossing\n // is decided against boxes that are up to date.\n this.carried.settling = false;\n }\n this.place();\n });\n this.host.own(() => {\n this.registration.dispose();\n if (this.dragging) {\n this.session?.cancel();\n }\n });\n return this;\n }\n\n setOptions(options: ReorderableOptions): void {\n this.options = options;\n }\n\n // ---------------------------------------------------------------------\n // The row as a drop target\n // ---------------------------------------------------------------------\n\n boxOf() {\n return this.host.layoutBox();\n }\n\n accepts(payload: UiDragPayload): boolean {\n return payload.type === REORDER && (payload.data as ReorderData).list === this.options.list;\n }\n\n enter(state: UiDragState): void {\n const data = state.payload.data as ReorderData;\n if (data.index === this.options.index || data.settling) {\n return;\n }\n const from = data.index;\n const to = this.options.index;\n // The payload follows the row it belongs to, so the next crossing\n // is measured from where the row now is rather than from where it\n // started. Written *before* `onMove` is called, and from a copy of\n // the index, because an application that re-renders its list\n // synchronously inside `onMove` has already given this row its new\n // index by the time the call returns: read afterwards, `options.index`\n // is where this row went, which is where the carried row came from,\n // and the next crossing undoes the move. The two rows then swap on\n // every pointer move, which is the flicker that looked like one.\n data.index = to;\n data.settling = true;\n this.options.onMove(from, to);\n }\n\n over(): void {\n // Everything happens on the crossing; there is nothing to do while\n // the pointer stays inside one row.\n }\n\n leave(): void {\n // The row keeps whatever order the crossings gave it.\n }\n\n drop(): UiDropEffect {\n return 'move';\n }\n\n // ---------------------------------------------------------------------\n // The row as a drag source\n // ---------------------------------------------------------------------\n\n private begin(event: UiPointerEvent): void {\n // The list this row sits in scrolls, and a row being carried is not\n // a pan for it: `UiTouchScroller` listens at the root and would\n // scroll the list under the finger otherwise.\n event.stopPropagation();\n const box = this.host.layoutBox();\n this.grabX = box === null ? 0 : event.x - box.x;\n this.grabY = box === null ? 0 : event.y - box.y;\n this.pointerX = event.x;\n this.pointerY = event.y;\n this.dragging = true;\n this.carried = { list: this.options.list, index: this.options.index, settling: false };\n this.session = this.registration.sync();\n this.session.begin({ type: REORDER, data: this.carried }, event.x, event.y, this.host.node);\n for (const [property, value] of Object.entries(this.options.dragging ?? {})) {\n this.host.set(property, value);\n }\n this.options.onDragChange?.(true);\n this.place();\n }\n\n private move(event: UiPointerEvent): void {\n if (!this.dragging) {\n return;\n }\n event.stopPropagation();\n this.pointerX = event.x;\n this.pointerY = event.y;\n this.place();\n this.session?.move(event.x, event.y);\n }\n\n private end(): void {\n if (!this.dragging) {\n return;\n }\n this.dragging = false;\n this.carried = null;\n this.session?.end();\n for (const property of Object.keys(this.options.dragging ?? {})) {\n this.host.clear(property);\n }\n this.host.clear('transform');\n this.options.onDragChange?.(false);\n }\n\n /**\n * Puts the row where the pointer is holding it.\n *\n * The layout box is the row's place in the list *without* this\n * translation, because a modifier's override moves the paint and not\n * the layout, so subtracting it is what makes the placement absolute\n * rather than cumulative.\n */\n private place(): void {\n if (!this.dragging) {\n return;\n }\n const box = this.host.layoutBox();\n if (box === null) {\n return;\n }\n const axis = this.options.axis ?? 'y';\n const x = axis === 'y' ? 0 : this.pointerX - this.grabX - box.x;\n const y = axis === 'x' ? 0 : this.pointerY - this.grabY - box.y;\n if (x === 0 && y === 0) {\n this.host.clear('transform');\n return;\n }\n this.host.set('transform', {\n x: this.basePivotX,\n y: this.basePivotY,\n translateX: x,\n translateY: y,\n scaleX: this.baseScaleX,\n scaleY: this.baseScaleY,\n rotation: this.baseRotation\n });\n }\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n","import { UiEventType, type UiPinchEvent, type UiWheelEvent } from '../input/UiInputEvent';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/** Where a zoomable node has got to. */\nexport interface ZoomState {\n readonly scale: number;\n /** Degrees, matching the transform property's own units. */\n readonly rotation: number;\n readonly translateX: number;\n readonly translateY: number;\n}\n\nexport interface PinchableOptions {\n /** How far it may be zoomed out. Default 1, which is \"no smaller than declared\". */\n readonly minScale?: number;\n /** How far in. Default 8. */\n readonly maxScale?: number;\n /**\n * Whether two fingers twisting also rotate the node. Default false.\n *\n * Off by default because rotation is a gesture people make by\n * accident: a pinch on a photo is never perfectly parallel, and a\n * viewer that rotates two degrees every time it is zoomed feels\n * broken. A map or a canvas of freeform objects turns it on.\n */\n readonly rotate?: boolean;\n /**\n * Whether Ctrl (or Command) with the wheel zooms as well. Default\n * true.\n *\n * A trackpad pinch reaches the browser as exactly this, and it is\n * also the only way a mouse can zoom at all, so a zoomable node that\n * answers only to two fingers is a zoomable node most people cannot\n * zoom.\n */\n readonly wheel?: boolean;\n /** How much of a zoom one wheel notch is worth. Default 0.0015 per pixel of delta. */\n readonly wheelSensitivity?: number;\n /** Told whenever the zoom changes, for a readout or a reset button. */\n readonly onChange?: (state: ZoomState) => void;\n}\n\n/**\n * Makes a node zoomable by two fingers, or by Ctrl and the wheel.\n *\n * Pinch was deferred partly on the ground that \"what a\n * framework should do with a pinch when it owns no zoom is a design\n * question this work did not need to answer\". This is the answer: the\n * framework owns no zoom, and this modifier does. Nothing listens for a\n * pinch unless an element asks for it, exactly as nothing hovers unless\n * an element asks for it, which is the whole shape of\n * the modifier contract.\n *\n * ## It zooms about the gesture, not about the middle\n *\n * The point under the fingers stays under the fingers. That is the\n * difference between a photo viewer that feels like a photo viewer and\n * one that scales about its own centre and slides the thing you were\n * looking at off the edge. Each gesture holds the node-local point the\n * pinch started on and solves the translation that keeps it in place,\n * which is also why a two-finger pan and a pinch are one gesture here\n * rather than two that fight over the same transform.\n *\n * ## What it does to the transform\n *\n * It writes `scaleX`, `scaleY`, `rotation` and the translation as one\n * override, with the pivot at the node's own origin. A transform the\n * element declared is read once at attach and used as the **starting\n * state** rather than composed with: a photo that starts at 1.5x zooms\n * out to 1x, which is what an application that declared 1.5x meant,\n * and composing would have made its lower bound 1.5 without saying so.\n */\nconst kind = defineModifier<PinchableOptions>({\n name: 'pinchable',\n attach(host, options) {\n zoomers.set(host, new Pinchable(host, options).attach());\n },\n update(host, options) {\n zoomers.get(host)?.setOptions(options);\n }\n});\n\nconst zoomers = new WeakMap<UiModifierHost, Pinchable>();\n\n/** Makes a node zoomable. Hoist the options object, as every modifier asks. */\nexport function pinchable(options: PinchableOptions = EMPTY_OPTIONS): UiModifier<PinchableOptions> {\n return kind(options);\n}\n\nconst EMPTY_OPTIONS: PinchableOptions = Object.freeze({});\n\nclass Pinchable {\n private scale = 1;\n private rotation = 0;\n private translateX = 0;\n private translateY = 0;\n\n /** The gesture's own baseline, so its cumulative scale is relative to it. */\n private startScale = 1;\n private startRotation = 0;\n\n constructor(\n private readonly host: UiModifierHost,\n private options: PinchableOptions\n ) {}\n\n attach(): this {\n const declared = this.host.get<Record<string, unknown> | null | undefined>('transform');\n if (declared !== undefined && declared !== null) {\n this.scale = numberOr(declared.scaleX, 1);\n this.rotation = numberOr(declared.rotation, 0);\n this.translateX = numberOr(declared.translateX, 0);\n this.translateY = numberOr(declared.translateY, 0);\n }\n this.host.on(UiEventType.PinchStart, event => this.start(event as UiPinchEvent));\n this.host.on(UiEventType.PinchMove, event => this.pinch(event as UiPinchEvent));\n this.host.on(UiEventType.Wheel, event => this.wheel(event as UiWheelEvent));\n return this;\n }\n\n setOptions(options: PinchableOptions): void {\n this.options = options;\n }\n\n /** Where the node has got to, for a reset button or a readout. */\n get state(): ZoomState {\n return {\n scale: this.scale,\n rotation: this.rotation,\n translateX: this.translateX,\n translateY: this.translateY\n };\n }\n\n private start(event: UiPinchEvent): void {\n event.stopPropagation();\n this.startScale = this.scale;\n this.startRotation = this.rotation;\n }\n\n private pinch(event: UiPinchEvent): void {\n event.stopPropagation();\n const rotation = this.options.rotate === true ? this.startRotation + event.rotation : this.rotation;\n this.applyAt(event.x, event.y, this.startScale * event.scale, rotation, event.translateX, event.translateY);\n }\n\n private wheel(event: UiWheelEvent): void {\n if (this.options.wheel === false) {\n return;\n }\n if (!event.modifiers.ctrl && !event.modifiers.meta) {\n return;\n }\n // The wheel was spent here, so the container underneath does not\n // scroll on the same notch.\n event.preventDefault();\n event.stopPropagation();\n const sensitivity = this.options.wheelSensitivity ?? 0.0015;\n // Exponential rather than linear: a fixed fraction per notch means\n // zooming in and back out by the same number of notches lands\n // exactly where it started.\n const factor = Math.exp(-event.deltaY * sensitivity);\n this.applyAt(event.x, event.y, this.scale * factor, this.rotation, 0, 0);\n }\n\n /**\n * Moves to a new scale and rotation while holding one point still.\n *\n * `anchorX`/`anchorY` are in canvas space, and the node's layout box\n * is where the node sits before this modifier's transform, so the\n * anchor is turned into a node-local point under the *current*\n * transform, then the translation that puts that same local point\n * back under the anchor at the *new* transform is solved directly.\n * Doing it in one step rather than accumulating deltas is what keeps\n * a long gesture from drifting.\n */\n private applyAt(anchorX: number, anchorY: number, scale: number, rotation: number, panX: number, panY: number): void {\n const box = this.host.layoutBox();\n if (box === null) {\n return;\n }\n const clamped = Math.min(Math.max(scale, this.options.minScale ?? 1), this.options.maxScale ?? 8);\n const localX = anchorX - box.x - this.translateX;\n const localY = anchorY - box.y - this.translateY;\n const [unrotatedX, unrotatedY] = unapply(localX, localY, this.scale, this.rotation);\n const [reappliedX, reappliedY] = apply(unrotatedX, unrotatedY, clamped, rotation);\n this.scale = clamped;\n this.rotation = rotation;\n this.translateX = anchorX + panX - box.x - reappliedX;\n this.translateY = anchorY + panY - box.y - reappliedY;\n this.write();\n }\n\n private write(): void {\n this.host.set('transform', {\n x: 0,\n y: 0,\n translateX: this.translateX,\n translateY: this.translateY,\n scaleX: this.scale,\n scaleY: this.scale,\n rotation: this.rotation\n });\n this.options.onChange?.(this.state);\n }\n}\n\n/** Rotates and scales a node-local point, the way the renderers do. */\nfunction apply(x: number, y: number, scale: number, degrees: number): [number, number] {\n const radians = (degrees * Math.PI) / 180;\n const cos = Math.cos(radians);\n const sin = Math.sin(radians);\n return [(x * cos - y * sin) * scale, (x * sin + y * cos) * scale];\n}\n\n/** The inverse of `apply`, for turning a screen offset back into a local point. */\nfunction unapply(x: number, y: number, scale: number, degrees: number): [number, number] {\n if (scale === 0) {\n return [0, 0];\n }\n const radians = (-degrees * Math.PI) / 180;\n const cos = Math.cos(radians);\n const sin = Math.sin(radians);\n const sx = x / scale;\n const sy = y / scale;\n return [sx * cos - sy * sin, sx * sin + sy * cos];\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n","import type { UiNode } from '../graph/UiNode';\nimport { UiEventType, type UiKeyboardEvent, type UiPointerEvent } from '../input/UiInputEvent';\nimport type { UiShortcut, UiShortcutRegistry } from '../input/UiShortcuts';\nimport { defineModifier, type UiModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\nexport interface ShortcutsOptions {\n /** The registry every `shortcut` on this page registers into. */\n readonly registry: UiShortcutRegistry;\n}\n\n/**\n * Feeds keyboard input to a shortcut registry.\n *\n * Put it on the application's root element, once. It listens at the\n * graph root in the **bubble** phase, which is the whole of the design:\n * a key reaches the registry only after the focused node and everything\n * above it have had it, so a text field that handles its own Escape\n * keeps it and a dialog that takes Enter keeps that, with no list of\n * exceptions anywhere in the registry.\n *\n * A key a shortcut takes is marked `preventDefault()`, which is what\n * `UiKeyboardController` reads before applying its own defaults, so a\n * shortcut on Tab or on Enter is not also a tab navigation or a button\n * press.\n *\n * The focused node is the event's `target`: the keyboard controller\n * routes a key to the focused node, and to the root when nothing is\n * focused, so the registry learns the scope from the event rather than\n * holding a focus manager it would otherwise need injecting.\n */\nconst shortcutsKind = defineModifier<ShortcutsOptions>({\n name: 'shortcuts',\n attach(host, options) {\n let registry = options.registry;\n feeds.set(host, next => (registry = next.registry));\n host.onRoot(\n UiEventType.KeyDown,\n event => {\n if (event.defaultPrevented) {\n return;\n }\n const key = event as UiKeyboardEvent;\n if (registry.handleKey(key.key, key.modifiers, key.target)) {\n event.preventDefault();\n event.stopPropagation();\n }\n },\n { capture: false }\n );\n },\n update(host, options) {\n feeds.get(host)?.(options);\n }\n});\n\nconst feeds = new WeakMap<UiModifierHost, (options: ShortcutsOptions) => void>();\n\nexport function shortcuts(options: ShortcutsOptions): UiModifier<ShortcutsOptions> {\n return shortcutsKind(options);\n}\n\nexport interface ShortcutOptions extends Omit<UiShortcut, 'scope'> {\n readonly registry: UiShortcutRegistry;\n /**\n * Whether the shortcut is live only while focus is inside this node.\n * Default true, which is the point of registering it on an element\n * rather than calling `registry.register` from anywhere.\n *\n * False registers it application-wide with the element's lifetime,\n * which is what a route wants: the shortcut belongs to the screen and\n * dies with it, but works wherever the focus happens to be.\n */\n readonly scoped?: boolean;\n}\n\n/**\n * Registers one shortcut for as long as the element exists.\n *\n * The registration is undone on detach, so the lifetime of a shortcut\n * is the lifetime of the thing it acts on and a screen cannot leave a\n * command behind when it is replaced. That is the single thing a\n * root-level `onKeyDown` switching on the route cannot give you, and\n * the reason both applications' key handling grew into a function\n * nobody wanted to touch.\n */\nconst shortcutKind = defineModifier<ShortcutOptions>({\n name: 'shortcut',\n attach(host, options) {\n let current = options;\n entries.set(host, next => (current = next));\n const scope: UiNode | null = options.scoped === false ? null : host.node;\n host.own(\n options.registry.register({\n keys: options.keys,\n label: options.label,\n group: options.group,\n priority: options.priority,\n scope,\n // Read through the latest arguments, so a handler rebuilt every\n // render does not cost a re-registration.\n when: () => current.when?.() ?? true,\n run: () => current.run()\n })\n );\n },\n update(host, options) {\n entries.get(host)?.(options);\n }\n});\n\nconst entries = new WeakMap<UiModifierHost, (options: ShortcutOptions) => void>();\n\nexport function shortcut(options: ShortcutOptions): UiModifier<ShortcutOptions> {\n return shortcutKind(options);\n}\n\nexport interface ContextMenuOptions {\n /**\n * A menu was asked for on this node: the right button, or a finger\n * held on it. The point is where the pointer was, which is what a\n * `Menu`'s `at` prop takes.\n */\n readonly onOpen: (at: { readonly x: number; readonly y: number }) => void;\n /** Whether the request also stops here. Default true. */\n readonly stopPropagation?: boolean;\n}\n\n/**\n * Turns a context-menu request on this node into a callback.\n *\n * `Menu` has taken an `at` point and left the trigger to the caller\n * since it was written, with a comment saying a menu is opened by \"a\n * button, a right-click or a keyboard shortcut, and the component\n * should not care which\". Two of those three existed. This is the\n * third, and it is a modifier rather than a component because the thing\n * that needs the trigger is whatever the person right-clicked, not the\n * menu.\n *\n * The request stops here by default, so a row inside a list gets its\n * own menu rather than the list's; a node that wants both lets it\n * through.\n */\nconst contextMenuKind = defineModifier<ContextMenuOptions>({\n name: 'contextMenu',\n attach(host, options) {\n let current = options;\n menus.set(host, next => (current = next));\n host.on(UiEventType.ContextMenu, event => {\n const pointer = event as UiPointerEvent;\n if (current.stopPropagation !== false) {\n event.stopPropagation();\n }\n current.onOpen({ x: pointer.x, y: pointer.y });\n });\n },\n update(host, options) {\n menus.get(host)?.(options);\n }\n});\n\nconst menus = new WeakMap<UiModifierHost, (options: ContextMenuOptions) => void>();\n\nexport function contextMenu(options: ContextMenuOptions): UiModifier<ContextMenuOptions> {\n return contextMenuKind(options);\n}\n","import { defaultMotion, type UiSpringSpec, type UiSpringToken } from '../environment/UiMotion';\nimport type { AnimatedCell, UiEasing } from '../animation';\nimport type { UiNode } from '../graph/UiNode';\nimport { defineModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\nexport interface AnimateLayoutOptions {\n /** The spring that carries the node home. Named, or given outright. */\n spring?: UiSpringToken | UiSpringSpec;\n /** Use a tween of this many milliseconds instead of a spring. */\n duration?: number;\n easing?: UiEasing;\n /** Movements smaller than this are not worth animating. */\n threshold?: number;\n}\n\n/**\n * Animates a node from where it was to where layout has just put it.\n *\n * The classic FLIP: the node's box moved between two frames, so it is\n * drawn back at the old place and released. What is worth writing down\n * is where each half comes from, because both were already here.\n *\n * **The old box comes from `LayoutNotifier`, not from L7's slab\n * records.** Those are the prerequisite and they\n * do not exist — `LayoutRecord.ts` mentions slabs only in a comment\n * about a future Worker transfer. B2 built the thing this actually\n * needs: a per-node, opt-in notification after any frame that moved a\n * box, which already keeps the last box it reported. That is exactly\n * the old/new pair, it costs nothing when nothing is listening, and\n * building a second mechanism beside it would have been the mistake.\n *\n * **The offset is the transform's translation.** It used to be a\n * relative `left`/`top` on a `position: relative` node, and\n * the reason is this: `UiTransform.x`/`.y` are the\n * transform's *pivot*, both renderers compose `T(pivot)·R·S·T(-pivot)`,\n * and there was no translation field to use. The cost it recorded was\n * that a tick marked Layout rather than Paint, so the animating\n * subtree was re-placed from its relayout boundary on every frame of\n * every animation. `UiTransform` now has `translateX`/`translateY`,\n * which both renderers and the hit tester apply outside the pivot, so\n * that cost is simply gone: a reordering list marks Paint and the\n * layout engine does not run. It also means the modifier no longer\n * cares what an element's own `position`, `left` or `top` are, and the\n * two warnings that existed only to say so are gone with it.\n *\n * **It animates a reorder, and follows everything else.** The two look\n * identical to anything watching boxes and want opposite behaviour: a\n * node that changed places should be drawn back where it was and\n * released, while a node that moved because its neighbour grew should\n * simply move with it. Holding it still would be the neighbour growing\n * *through* it, which is what the first version of this did — the\n * example's expanding card was drawn over the card below it, in the\n * browser, on the first click. So the modifier absorbs a move only\n * when its parent's `childOrderVersion` has changed, which is exactly\n * \"something was inserted, moved or removed here\" and is one integer\n * comparison. A window resize is followed rather than animated for the\n * same reason.\n *\n * The listener also has to tell its own movement apart from the\n * layout's, because writing an offset moves the box the notifier\n * reports. It does that by subtracting the offset it wrote: what it\n * watches is the node's place in the *flow*. An interruption springs\n * from wherever the node had got to, which is what a list reordered\n * twice in quick succession should look like.\n */\nexport const animateLayout = defineModifier<AnimateLayoutOptions | undefined>({\n name: 'animateLayout',\n attach(host, args) {\n new LayoutAnimation(host, args ?? {}).attach();\n }\n});\n\nclass LayoutAnimation {\n /** Where the node sat in the flow when we last heard, or null before the first layout. */\n private flowX = 0;\n private flowY = 0;\n private seen = false;\n /** The parent and its child-order version when we last heard. */\n private parent: UiNode | null = null;\n private childOrder = -1;\n /** What we are currently offsetting by, and therefore what to subtract. */\n private offsetX = 0;\n private offsetY = 0;\n /** What the element declared, which our offset is added to. */\n private basePivotX = 0;\n private basePivotY = 0;\n private baseScaleX = 1;\n private baseScaleY = 1;\n private baseRotation = 0;\n\n private readonly cellX: AnimatedCell<number>;\n private readonly cellY: AnimatedCell<number>;\n\n constructor(\n private readonly host: UiModifierHost,\n private readonly options: AnimateLayoutOptions\n ) {\n // Each write is one override, which the cascade restores on detach.\n this.cellX = this.makeCell(\n () => this.offsetX,\n next => this.setOffset(next, this.offsetY)\n );\n this.cellY = this.makeCell(\n () => this.offsetY,\n next => this.setOffset(this.offsetX, next)\n );\n }\n\n attach(): void {\n // Read once, in the element's own values: what the modifier writes\n // is the element's transform with a translation added, so a node\n // that also rotates keeps rotating while it slides.\n const declared = this.host.get<Record<string, unknown> | undefined>('transform');\n if (declared !== undefined && declared !== null) {\n this.basePivotX = numberOr(declared.x, 0);\n this.basePivotY = numberOr(declared.y, 0);\n this.baseScaleX = numberOr(declared.scaleX, 1);\n this.baseScaleY = numberOr(declared.scaleY, 1);\n this.baseRotation = numberOr(declared.rotation, 0);\n }\n // The listener fires on any frame that moved the node's *visible*\n // box, which includes a scroll; what it reads is `flowBox`, which\n // does not. So a scroll wakes it and it correctly decides nothing\n // moved. This was found in the browser: with the visible box, every\n // row of the list lagged behind the page scroll and caught up.\n this.host.onLayout(() => this.handleLayout());\n }\n\n private makeCell(read: () => number, write: (offset: number) => void): AnimatedCell<number> {\n return {\n get value(): number {\n return read();\n },\n set value(next: number) {\n write(next);\n }\n };\n }\n\n /**\n * Writes the offset as the transform's translation, or takes it away.\n *\n * Back at zero the override is dropped rather than written as an\n * identity translation, so the node ends up with exactly what the\n * element declared — which for nearly every element is no transform\n * at all, and therefore no `hasTransform` in its paint state and no\n * matrix multiply per frame.\n */\n private setOffset(x: number, y: number): void {\n this.offsetX = x;\n this.offsetY = y;\n if (x === 0 && y === 0) {\n this.host.clear('transform');\n return;\n }\n this.host.set('transform', {\n x: this.basePivotX,\n y: this.basePivotY,\n translateX: x,\n translateY: y,\n scaleX: this.baseScaleX,\n scaleY: this.baseScaleY,\n rotation: this.baseRotation\n });\n }\n\n private handleLayout(): void {\n const box = this.host.flowBox();\n if (box === null) {\n return;\n }\n // The translation is paint-only, so the flow box the notifier\n // reports never contains it — but the subtraction is kept because\n // the node's place in the flow is still what this watches, and a\n // renderer that ever folded the transform into layout would break\n // silently without it.\n const flowX = box.x;\n const flowY = box.y;\n const parent = this.host.node.parent;\n const childOrder = parent === null ? -1 : parent.childOrderVersion;\n const reordered = parent !== this.parent || childOrder !== this.childOrder;\n this.parent = parent;\n this.childOrder = childOrder;\n if (!this.seen) {\n this.seen = true;\n this.flowX = flowX;\n this.flowY = flowY;\n return;\n }\n const dx = this.flowX - flowX;\n const dy = this.flowY - flowY;\n this.flowX = flowX;\n this.flowY = flowY;\n if (!reordered) {\n // Nothing changed places, so this is the layout itself moving —\n // a neighbour resizing, a window resizing, a container growing.\n // The node follows it, and whatever spring is already running\n // keeps decaying the offset it has. Absorbing this would hold\n // the node still while its neighbour grew into it.\n return;\n }\n const threshold = this.options.threshold ?? 0.5;\n if (Math.abs(dx) < threshold && Math.abs(dy) < threshold) {\n // Either nothing moved, or the only thing that moved was us.\n return;\n }\n // Keep it where it appears, then let it go. Adding to the current\n // offset rather than replacing it is what makes an interrupted\n // reorder continue from where it had got to.\n this.cellX.value = this.offsetX + dx;\n this.cellY.value = this.offsetY + dy;\n this.release(this.cellX);\n this.release(this.cellY);\n }\n\n private release(cell: AnimatedCell<number>): void {\n if (cell.value === 0) {\n return;\n }\n if (this.options.duration !== undefined) {\n this.host.animate(cell, 0, { duration: this.options.duration, easing: this.options.easing });\n return;\n }\n const named = this.options.spring ?? 'snappy';\n this.host.spring(cell, 0, {\n spring: typeof named === 'string' ? defaultMotion.springs[named] : named,\n // Half a pixel: closer than this is the same pixel on a 1× screen\n // and within one on a 2×, so continuing to schedule frames for it\n // would be paying for a movement nobody can see.\n restDelta: 0.5\n });\n }\n}\n\nfunction numberOr(value: unknown, fallback: number): number {\n return typeof value === 'number' && Number.isFinite(value) ? value : fallback;\n}\n","import { combineLatest, distinctUntilChanged, isObservable, of, type Observable } from 'rxjs';\n\nimport { UiEnvironmentKeys } from '../environment/UiEnvironmentKeys';\nimport type { Reactive } from '../composition/UiElementProps';\nimport type { UiColorValue } from '../properties/UiPropertyValues';\nimport { resolveColorValue } from '../properties/UiThemeColor';\nimport type { UiImage } from '../properties/UiImage';\nimport type { ImageResolver } from '../rendering/ImageResolver';\nimport type { IconRasterizer, IconSpec } from '../rendering/IconRasterizer';\nimport { defineModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/**\n * A value or an Observable of one, as one Observable. The media\n * modifiers take their arguments this way so an `Image` whose `src` is\n * bound to a cell follows it, rather than reading it once at attach.\n */\nfunction follow<T>(value: Reactive<T>): Observable<T> {\n return isObservable(value) ? (value as Observable<T>) : of(value);\n}\n\n/** One url, or several to try in order until one resolves. */\nexport type ImageSources = string | readonly string[];\n\nexport interface ImageSourceArgs {\n readonly resolver: ImageResolver;\n /**\n * The url to resolve, or a list of them tried in order: the same\n * picture on several mirrors, say. An Observable is followed, and each\n * new value replaces the last.\n */\n readonly source: Reactive<ImageSources>;\n readonly onState?: (state: 'loading' | 'loaded' | 'failed', error?: unknown) => void;\n}\n\n/**\n * Resolves a source to a bitmap and writes it onto the node's `image`.\n *\n * The source may be an Observable. Each distinct url it emits releases\n * the one before and starts a new load, so an `Image` bound to a cell\n * shows whatever the cell says rather than what it said when the node\n * was built. A load that has been superseded is dropped when it\n * arrives, never written.\n */\nexport const imageSource = defineModifier<ImageSourceArgs>({\n name: 'imageSource',\n attach(host, args) {\n let release: (() => void) | null = null;\n const subscription = follow(args.source)\n .pipe(distinctUntilChanged(sameSources))\n .subscribe(source => {\n release?.();\n release = load(host, args, typeof source === 'string' ? [source] : source);\n });\n host.own(() => {\n subscription.unsubscribe();\n release?.();\n release = null;\n });\n }\n // No `update`: a changed args object is a detach and an attach, which\n // is right, because the resolver or the whole source stream changed.\n // A changed *url* inside the stream is handled above.\n});\n\n/**\n * Starts loading the first candidate, falling through to the next when\n * one fails, and returns what undoes it: the in-flight result is\n * dropped and whichever source is held is released. A failed candidate\n * holds nothing, because the resolver does not cache failures.\n */\nfunction load(host: UiModifierHost, args: ImageSourceArgs, sources: readonly string[]): () => void {\n let live = true;\n let held: string | null = null;\n args.onState?.('loading');\n host.clear('image');\n const attempt = (index: number, lastError: unknown): void => {\n if (!live) {\n return;\n }\n const source = sources[index];\n if (source === undefined) {\n args.onState?.('failed', lastError ?? new Error('No image source was given.'));\n return;\n }\n held = source;\n // Already decoded: on the node in this frame, so a picture the\n // cache holds never paints as its placeholder first. Matters most\n // under a shared-element morph, where that placeholder would be a\n // blank tile where the same picture stood a frame ago.\n const ready = args.resolver.peek?.(source) ?? null;\n if (ready !== null) {\n host.set('image', ready);\n args.onState?.('loaded');\n return;\n }\n args.resolver\n .resolve(source)\n .then((bitmap: UiImage) => {\n if (!live) {\n return;\n }\n host.set('image', bitmap);\n args.onState?.('loaded');\n })\n .catch((error: unknown) => {\n if (held === source) {\n held = null;\n }\n attempt(index + 1, error);\n });\n };\n attempt(0, undefined);\n return () => {\n live = false;\n if (held !== null) {\n args.resolver.release(held);\n held = null;\n }\n };\n}\n\nfunction sameSources(a: ImageSources, b: ImageSources): boolean {\n if (typeof a === 'string' || typeof b === 'string') {\n return a === b;\n }\n return a.length === b.length && a.every((url, index) => url === b[index]);\n}\n\nexport interface IconSourceArgs {\n readonly rasterizer: IconRasterizer;\n readonly path: Reactive<string>;\n readonly viewBox: Reactive<number>;\n readonly size: Reactive<number>;\n /** A palette name or a literal; resolved against the theme the node inherits. */\n readonly color: Reactive<UiColorValue>;\n readonly style: Reactive<'fill' | 'stroke'>;\n readonly strokeWidth: Reactive<number>;\n /** How a filled path decides what is inside it; see `IconSpec.fillRule`. */\n readonly fillRule?: Reactive<'nonzero' | 'evenodd' | undefined>;\n}\n\n/**\n * Rasterises an icon path and writes it onto the node's `image`.\n *\n * The colour is the interesting part. Everything else in the library\n * names a palette entry and lets it resolve at paint, but a raster has\n * its colour baked in, so the icon has to be re-rasterised when the\n * theme under it changes. That is what `host.environment` and\n * `host.onEnvironment` — the half that had no consumer until now —\n * are for: the modifier reads the theme\n * the node inherits, and re-reads it when a provider above swaps it,\n * so an icon inside a card that turns dark turns with it.\n */\nexport const iconSource = defineModifier<IconSourceArgs>({\n name: 'iconSource',\n attach(host, args) {\n let current: IconSpec | null = null;\n let generation = 0;\n /** The arguments as last seen; each is followed, so a bound path or colour redraws the glyph. */\n let seen: {\n path: string;\n viewBox: number;\n size: number;\n color: UiColorValue;\n style: 'fill' | 'stroke';\n strokeWidth: number;\n fillRule: 'nonzero' | 'evenodd' | undefined;\n } | null = null;\n\n const render = (): void => {\n if (seen === null) {\n return;\n }\n const theme = host.environment(UiEnvironmentKeys.theme);\n const color = resolveColorValue(host.node, seen.color) ?? theme.colors.text;\n const spec: IconSpec = {\n path: seen.path,\n viewBox: seen.viewBox,\n size: seen.size,\n color,\n style: seen.style,\n strokeWidth: seen.strokeWidth,\n fillRule: seen.fillRule\n };\n if (current !== null && sameSpec(current, spec)) {\n return;\n }\n const previous = current;\n current = spec;\n const mine = ++generation;\n args.rasterizer\n .raster(spec)\n .then(bitmap => {\n if (generation === mine) {\n host.set('image', bitmap);\n }\n })\n .catch(() => {\n if (generation === mine) {\n host.clear('image');\n }\n });\n if (previous !== null) {\n args.rasterizer.release(previous);\n }\n };\n\n const subscription = combineLatest([\n follow(args.path),\n follow(args.viewBox),\n follow(args.size),\n follow(args.color),\n follow(args.style),\n follow(args.strokeWidth),\n follow(args.fillRule ?? undefined)\n ]).subscribe(([path, viewBox, size, color, style, strokeWidth, fillRule]) => {\n seen = { path, viewBox, size, color, style, strokeWidth, fillRule };\n render();\n });\n host.onEnvironment(render);\n host.own(() => {\n subscription.unsubscribe();\n generation++;\n if (current !== null) {\n args.rasterizer.release(current);\n current = null;\n }\n });\n }\n});\n\nfunction sameSpec(a: IconSpec, b: IconSpec): boolean {\n return (\n a.path === b.path &&\n a.viewBox === b.viewBox &&\n a.size === b.size &&\n a.style === b.style &&\n a.strokeWidth === b.strokeWidth &&\n (a.fillRule ?? 'nonzero') === (b.fillRule ?? 'nonzero') &&\n a.color.r === b.color.r &&\n a.color.g === b.color.g &&\n a.color.b === b.color.b &&\n a.color.a === b.color.a\n );\n}\n","import { isObservable, type Observable, type Subscription } from 'rxjs';\n\nimport { defaultMotion, type UiSpringSpec, type UiSpringToken } from '../environment/UiMotion';\nimport { easings, type UiEasing } from '../animation/UiEasing';\nimport type { AnimatedCell } from '../animation/UiAnimation';\nimport {\n MOTION_CHANNELS,\n MOTION_REST,\n isMotionRest,\n resolveMotionState,\n type MotionChannel,\n type MotionStateInput,\n type MotionTiming,\n type ResolvedMotionState\n} from '../animation/UiMotionState';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { UiNode } from '../graph/UiNode';\nimport { defineModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/**\n * Motion, as two modifiers over one mechanism.\n *\n * `motion` moves an element to a visual state, optionally in from\n * another; `sharedElement` makes an element that appears under a name\n * another element was using start from where that element stood.\n *\n * Both write the same two properties — `transform` and `opacity` —\n * through the same per-node composer below, and both drive their\n * numbers through `host.animate` / `host.spring`, so they are\n * cancelled with the node, honour reduced motion, and cost the runtime\n * exactly what one animation costs. Nothing here is a second animation\n * system beside `AnimationDriver`; it is a vocabulary in front of it.\n *\n * **Why none of it touches layout.** Every channel a motion state has\n * resolves in `PaintState`, so an element moving through them marks\n * Paint and the layout engine never runs for it. That is the whole\n * reason `UiTransform` gained `translateX`/`translateY`:\n * animation used to move a node with a relative `left`/`top` because\n * there was no translation to use, and paid a relayout per tick for\n * it. The one exception is `morph: 'geometry'`, which asks for a\n * relayout deliberately and says why.\n *\n * **Exit lives above this file.** An element that has logically left\n * has to be kept somewhere, and that is a question about who owns the\n * children — the framework's `Presence`, not a modifier. What\n * `Presence` needs from here is `motion`, which it points at an exit\n * state and waits on.\n */\n\n// ---------------------------------------------------------------------------\n// The per-node composer\n// ---------------------------------------------------------------------------\n\ninterface MotionLayerRecord {\n state: ResolvedMotionState;\n readonly host: UiModifierHost;\n}\n\n/**\n * Every motion layer on one node, composed into one write.\n *\n * More than one is normal: an element that is entering *and* morphing\n * from an element on the previous screen has two, and the two describe\n * different things about the same element. Composition is the obvious\n * arithmetic — translations and rotations add, scales and opacities\n * multiply — which is what makes \"fade it out while it also slides\"\n * mean what it looks like it means.\n *\n * The composed value is written through **every** participating host\n * rather than one. The override cascade resolves a conflict in favour\n * of whichever modifier is later in the element's list (see\n * `UiModifierSet`), so writing from a single host would make the\n * result depend on modifier order; writing the same value from all of\n * them makes last-wins land on the right answer whichever one is last,\n * and a layer detaching drops only its own override.\n */\nclass MotionComposer {\n private readonly layers = new Map<symbol, MotionLayerRecord>();\n /** The pivot: the node's own centre, so a scale grows from the middle. */\n private pivotX = 0;\n private pivotY = 0;\n\n add(key: symbol, host: UiModifierHost): void {\n this.layers.set(key, { state: MOTION_REST, host });\n }\n\n remove(key: symbol): void {\n const record = this.layers.get(key);\n if (record === undefined) {\n return;\n }\n this.layers.delete(key);\n // Its own overrides go with it; the rest re-state theirs, which is\n // how the element ends up with exactly what the remaining layers\n // say rather than with the last composite the departing one wrote.\n record.host.clear('transform');\n record.host.clear('opacity');\n this.flush();\n }\n\n set(key: symbol, state: ResolvedMotionState): void {\n const record = this.layers.get(key);\n if (record === undefined) {\n return;\n }\n record.state = state;\n this.flush();\n }\n\n /**\n * Re-reads the node's size and re-writes if the pivot moved.\n *\n * A pivot at the centre is what makes `scaleFrom(0.9)` grow from the\n * middle rather than out of the top-left corner, and the centre is a\n * function of the node's box — so a node that has not been laid out\n * yet, or that has just been resized, needs telling.\n */\n measure(box: LayoutBox | null): void {\n const pivotX = box === null ? 0 : box.width / 2;\n const pivotY = box === null ? 0 : box.height / 2;\n if (pivotX === this.pivotX && pivotY === this.pivotY) {\n return;\n }\n this.pivotX = pivotX;\n this.pivotY = pivotY;\n this.flush();\n }\n\n private flush(): void {\n let opacity = 1;\n let x = 0;\n let y = 0;\n let scaleX = 1;\n let scaleY = 1;\n let rotate = 0;\n for (const { state } of this.layers.values()) {\n opacity *= state.opacity;\n x += state.x;\n y += state.y;\n scaleX *= state.scaleX;\n scaleY *= state.scaleY;\n rotate += state.rotate;\n }\n const rest = isMotionRest({ opacity, x, y, scaleX, scaleY, rotate });\n for (const { host } of this.layers.values()) {\n if (rest) {\n // Back at rest, the overrides are dropped rather than written\n // as identity, so the element ends up with exactly what it\n // declared — which for nearly every element is no transform at\n // all, and therefore no `hasTransform` in its paint state and\n // no matrix multiply per frame.\n host.clear('transform');\n host.clear('opacity');\n continue;\n }\n host.set('transform', {\n x: this.pivotX,\n y: this.pivotY,\n translateX: x,\n translateY: y,\n scaleX,\n scaleY,\n rotation: rotate\n });\n host.set('opacity', opacity);\n }\n }\n}\n\n/** Keyed weakly, so a node that has gone takes its composer with it. */\nconst composers = new WeakMap<UiNode, MotionComposer>();\n\nfunction composerFor(node: UiNode): MotionComposer {\n let composer = composers.get(node);\n if (composer === undefined) {\n composer = new MotionComposer();\n composers.set(node, composer);\n }\n return composer;\n}\n\n// ---------------------------------------------------------------------------\n// One layer: six cells, driven by the runtime's driver\n// ---------------------------------------------------------------------------\n\n/**\n * One motion layer's six numbers, each an `AnimatedCell` the driver\n * owns.\n *\n * Six cells rather than one cell holding a state object, because the\n * driver's rule is one animation per cell — so six cells is six\n * independently retargetable channels, which is what lets an element\n * finish sliding while its fade is re-aimed, and is what makes a\n * spring per channel possible at all (`UiSpring` integrates a scalar\n * position and velocity, and there is no honest velocity for a whole\n * state taken together).\n */\nclass MotionLayer {\n private readonly key = Symbol('motion');\n private readonly cells: Record<MotionChannel, AnimatedCell<number>>;\n /**\n * Held in a box rather than in a field, because the six cells below\n * are property pairs on object literals — inside them `this` is the\n * literal, so they need somewhere to read and write that is not the\n * layer's own `this`.\n */\n private readonly holder: { state: ResolvedMotionState } = { state: MOTION_REST };\n /** Bumped on every retarget, so a superseded run does not report done. */\n private generation = 0;\n private outstanding = 0;\n private onSettled: (() => void) | null = null;\n\n constructor(private readonly host: UiModifierHost) {\n const composer = composerFor(host.node);\n const holder = this.holder;\n const key = this.key;\n const cellFor = (channel: MotionChannel): AnimatedCell<number> => ({\n get value(): number {\n return holder.state[channel];\n },\n set value(next: number) {\n holder.state = { ...holder.state, [channel]: next };\n composer.set(key, holder.state);\n }\n });\n this.cells = {\n opacity: cellFor('opacity'),\n x: cellFor('x'),\n y: cellFor('y'),\n scaleX: cellFor('scaleX'),\n scaleY: cellFor('scaleY'),\n rotate: cellFor('rotate')\n };\n composer.add(this.key, host);\n }\n\n get state(): ResolvedMotionState {\n return this.holder.state;\n }\n\n /** Puts the layer somewhere at once, cancelling whatever was driving it. */\n snapTo(state: ResolvedMotionState): void {\n this.generation++;\n this.onSettled = null;\n this.outstanding = 0;\n for (const channel of MOTION_CHANNELS) {\n this.host.stopAnimation(this.cells[channel]);\n }\n this.holder.state = state;\n composerFor(this.host.node).set(this.key, state);\n }\n\n /**\n * Moves every channel that is not already there, and calls back once\n * they have all arrived.\n *\n * `onDone` is what an exit waits on, so it must fire exactly once and\n * only for the run that is still current: a retarget mid-flight\n * completes the animations it supersedes, and reporting those as\n * \"arrived\" would drop an element off the screen halfway through its\n * replacement.\n */\n animateTo(target: ResolvedMotionState, timing: MotionTiming, onDone?: () => void): void {\n const generation = ++this.generation;\n this.onSettled = onDone ?? null;\n this.outstanding = 0;\n // Counted in two passes because an animation with no driver behind\n // it, or one that reduced motion snapped, completes synchronously\n // inside `subscribe` — before the loop has finished counting how\n // many there will be.\n let started = 0;\n let settled = 0;\n for (const channel of MOTION_CHANNELS) {\n if (Math.abs(this.holder.state[channel] - target[channel]) < channelEpsilon(channel)) {\n continue;\n }\n started++;\n this.drive(this.cells[channel], target[channel], timing).subscribe({\n complete: () => {\n if (generation !== this.generation) {\n return;\n }\n settled++;\n if (this.outstanding > 0 && settled === started) {\n this.report();\n }\n }\n });\n }\n this.outstanding = started - settled;\n if (this.outstanding <= 0) {\n // Either nothing had to move, or nothing could — no driver, or a\n // reduced-motion preference, both of which land on the target\n // without a frame passing.\n this.report();\n }\n }\n\n /** Drops the layer's contribution and its overrides. */\n release(): void {\n this.generation++;\n this.onSettled = null;\n for (const channel of MOTION_CHANNELS) {\n this.host.stopAnimation(this.cells[channel]);\n }\n composerFor(this.host.node).remove(this.key);\n }\n\n measure(box: LayoutBox | null): void {\n composerFor(this.host.node).measure(box);\n }\n\n private report(): void {\n this.outstanding = 0;\n const settled = this.onSettled;\n this.onSettled = null;\n settled?.();\n }\n\n private drive(cell: AnimatedCell<number>, to: number, timing: MotionTiming): Observable<number> {\n if (timing.spring !== undefined) {\n return this.host.spring(cell, to, {\n spring: springSpec(timing.spring),\n delay: timing.delay,\n reducedMotion: timing.reducedMotion,\n // These channels are opacities and scales as often as pixels,\n // so the default 0.01 would leave a scale visibly short.\n restDelta: 0.002\n });\n }\n return this.host.animate(cell, to, {\n duration: resolveDuration(timing.duration),\n easing: resolveEasing(timing.easing),\n delay: timing.delay,\n reducedMotion: timing.reducedMotion\n });\n }\n}\n\n/**\n * How close counts as \"already there\", per channel.\n *\n * A scale and an opacity live in [0, 1]-ish and a translation lives in\n * pixels, so one epsilon for all six would either animate a channel\n * that has not moved or refuse to animate one that has.\n */\nfunction channelEpsilon(channel: MotionChannel): number {\n return channel === 'x' || channel === 'y' ? 0.01 : 1e-4;\n}\n\nfunction springSpec(spring: UiSpringToken | UiSpringSpec): UiSpringSpec {\n return typeof spring === 'string' ? defaultMotion.springs[spring] : spring;\n}\n\nfunction resolveDuration(duration: MotionTiming['duration']): number {\n if (duration === undefined) {\n return defaultMotion.durations.normal;\n }\n return typeof duration === 'number' ? duration : defaultMotion.durations[duration];\n}\n\nfunction resolveEasing(easing: MotionTiming['easing']): UiEasing {\n if (easing === undefined) {\n return easings.standard;\n }\n return typeof easing === 'function' ? easing : (easings[easing] ?? easings.standard);\n}\n\n// ---------------------------------------------------------------------------\n// motion()\n// ---------------------------------------------------------------------------\n\nexport interface MotionArgs extends MotionTiming {\n /**\n * Where the element should be, now.\n *\n * A state, a stack of states, or an Observable of either. Null and\n * undefined both mean rest, so the common shape —\n * `hovered$.pipe(map(h => (h ? scaleFrom(0.97) : null)))` — needs no\n * ceremony to say \"and back to normal\".\n */\n readonly state?: MotionStateInput | Observable<MotionStateInput | null> | null;\n /**\n * Where it starts on the frame it appears, before moving to `state`.\n *\n * This is the enter animation, and it is a field on `motion` rather\n * than a modifier of its own because an element that enters and then\n * responds to a state is one element with one motion; two modifiers\n * writing the same six channels would be two layers arguing about\n * them.\n */\n readonly initial?: MotionStateInput;\n /**\n * Told when the element has finished arriving at `state`.\n *\n * `Presence` waits on this to know when an exit is over and the\n * element may finally be dropped. It fires once per target, and not\n * at all for a target that was superseded before it was reached.\n */\n readonly onSettled?: () => void;\n}\n\n/**\n * Moves an element to a visual state, and optionally in from another.\n *\n * ```tsx\n * <Box modifiers={[motion({ initial: [fade, slideUp(16)] })]} />\n * <Box modifiers={[motion({ state: pressed$, duration: 'fast' })]} />\n * ```\n *\n * `initial` is applied without animation when the modifier attaches —\n * during reconciliation, before the frame lays out or paints — so the\n * element is never seen at rest first, and released on the same frame,\n * which is what makes it an entrance rather than a jump.\n */\nexport const motion = defineModifier<MotionArgs>({\n name: 'motion',\n attach(host, args) {\n const controller = new MotionController(host, args);\n controllers.set(host, controller);\n controller.attach();\n host.own(() => {\n controllers.delete(host);\n controller.detach();\n });\n },\n update(host, args) {\n // Re-aimed rather than re-attached, because `initial` describes an\n // arrival and an element that is already here has already arrived.\n // Rebuilding the modifier when a duration changed would replay the\n // entrance, which is a bug you can see.\n controllers.get(host)?.update(args);\n }\n});\n\nconst controllers = new WeakMap<UiModifierHost, MotionController>();\n\nclass MotionController {\n private readonly layer: MotionLayer;\n private subscription: Subscription | null = null;\n\n constructor(\n private readonly host: UiModifierHost,\n private args: MotionArgs\n ) {\n this.layer = new MotionLayer(host);\n }\n\n attach(): void {\n this.layer.measure(this.host.layoutBox());\n if (this.args.initial !== undefined) {\n this.layer.snapTo(resolveMotionState(this.args.initial));\n }\n // The pivot is half the node's size, and the size is not known\n // until the first layout; until then a scale would grow out of the\n // corner. This also keeps the pivot right when the element resizes.\n this.host.onLayout(box => this.layer.measure(box));\n this.aim();\n }\n\n update(args: MotionArgs): void {\n const previousState = this.args.state;\n this.args = args;\n if (Object.is(previousState, args.state) && this.subscription !== null) {\n return;\n }\n this.subscription?.unsubscribe();\n this.subscription = null;\n this.aim();\n }\n\n detach(): void {\n this.subscription?.unsubscribe();\n this.subscription = null;\n this.layer.release();\n }\n\n private aim(): void {\n const state = this.args.state;\n if (isObservable(state)) {\n this.subscription = state.subscribe(next => this.animate(next));\n return;\n }\n this.animate(state ?? null);\n }\n\n private animate(state: MotionStateInput | null | undefined): void {\n this.layer.animateTo(resolveMotionState(state), this.args, this.args.onSettled);\n }\n}\n\n// ---------------------------------------------------------------------------\n// sharedElement()\n// ---------------------------------------------------------------------------\n\nexport interface SharedElementArgs extends MotionTiming {\n /**\n * The name both elements answer to.\n *\n * Two elements on two screens sharing a name is the whole\n * declaration: whichever appears second morphs from where the first\n * one stood. The name is global to the runtime, so it names a thing\n * — `'playlist-title'` — rather than a place.\n */\n readonly name: string;\n /**\n * How the morph is performed.\n *\n * `transform` — translate and scale the arriving element from the\n * departing one's box onto its own. Paint-only, and the right answer\n * for a picture, a line of text, a row of controls: anything whose\n * content should travel and stretch as one piece.\n *\n * `geometry` — animate the element's own `left`, `top`, `width` and\n * `height` instead. It costs a relayout per tick, and it is worth\n * paying for exactly one shape: a rounded rectangle whose aspect\n * ratio changes, because scaling one stretches its corners into\n * ellipses. A card expanding into a page is that shape, which is why\n * the CSS View Transitions version of this effect needs bespoke\n * `object-fit` rules for its card background and for nothing else.\n */\n readonly morph?: 'transform' | 'geometry';\n /** Fade the arriving element up from this opacity as it morphs. */\n readonly fadeFrom?: number;\n /**\n * How the two boxes' sizes become a scale.\n *\n * `'free'`, the default, takes each axis from its own ratio, which is\n * what a picture wants: a 4:3 thumbnail opening into a 16:9 banner\n * genuinely changes shape, and the morph should show that.\n *\n * `'uniform'` takes one scale for both axes, and is what **text**\n * wants. A line of text has no shape of its own to preserve — its box\n * is whatever the line breaks made it — while the type inside only\n * ever grows by its font size. Left free, a title that fits on one\n * line in a card and wraps to two on the page it opens into is scaled\n * 0.73 across and 0.34 down, and the letters are visibly squashed on\n * the way up. Measured exactly that, at a window width where the page\n * title wrapped and the card's did not.\n *\n * The width ratio is the one kept, because for a line of text it\n * tracks the font size closely — 0.73 against the 0.68 the font sizes\n * actually imply, in that same measurement — while the height ratio\n * is a count of lines and can be out by a whole multiple.\n */\n readonly scale?: 'free' | 'uniform';\n /**\n * Paints the element in the top layer for the length of the morph,\n * above the whole tree and outside every clip its ancestors impose.\n *\n * For the morph that ends *inside* something that clips. A picture\n * that lives in a scrolling row starts its journey back at the\n * middle of the page, which is nowhere near the row, so the row's\n * clip cuts most of the morph away: the slot is empty for the first\n * half of it and the picture appears out of nothing as it shrinks\n * back into the band. Measured in Segue, coming back from a track\n * page to the shelf it was opened from.\n *\n * `onMorph` cannot help with that, and neither can any `zIndex` an\n * ancestor writes: order decides who is drawn over whom, and this is\n * not a question of order. It is `overflow`.\n *\n * So it is the same question the browser answers by lifting its\n * named elements out of the page into a layer of their own, and the\n * same answer, held to the length of the morph: the element keeps\n * its place in the layout and every ancestor transform, scroll\n * offset and opacity, and loses their clipping and its place in\n * paint order. See the `lift` property.\n *\n * **On by default**, which it was not at first. The argument for\n * leaving it off was that most morphs do not need it and one that\n * does not is better off drawn where it belongs, since a lifted\n * element is over everything, a header and a now-playing bar\n * included. What that argument missed is who pays when it is wrong.\n * A morph that needed lifting and did not get it is not a little\n * worse, it is visibly broken — clipped in half, or drawn under the\n * thing it is travelling over — and the author cannot tell which\n * morphs need it by looking at the component, because the answer\n * lives in an ancestor's `overflow` three files away and changes\n * when a screen is relaid out. Segue found this twice: once when a\n * shelf's artwork flew home into a scrolling row, and again when the\n * Featured cards became a row and every one of their elements needed\n * the same flag.\n *\n * A browser lifts every named element for exactly this reason, and\n * nobody writing a view transition has to think about it. So the\n * default is the safe answer, and `lift: false` is there for the\n * morph that genuinely wants to stay in its layer.\n */\n readonly lift?: boolean;\n /**\n * Told `true` when this element starts morphing and `false` when it\n * has arrived — or when it leaves mid-flight, so nothing is left\n * holding a state that will never be cleared.\n *\n * The `true` comes the moment the element takes a name something\n * else held, which is during reconciliation and before the frame\n * that first draws it. That is deliberately earlier than the FLIP,\n * which needs the first layout: paint order is sorted during layout,\n * so a `zIndex` written in answer to this has to be in place before\n * that layout runs, or the first frame of the morph is drawn in the\n * old order.\n *\n * It exists for one thing, and stacking is the reason. A morphing\n * element is bigger than its resting self for most of the way, so it\n * overlaps whatever sits beside it, and it has to be drawn *over*\n * that rather than under it. Raising the element itself is rarely the\n * answer: what usually has to rise is an ancestor — the card the\n * morphing background belongs to, not the background — and a modifier\n * cannot reach an ancestor, nor should it, because which ancestor and\n * how far are questions about a layout that only the layout knows.\n *\n * const morphing = internalState(false);\n * <button zIndex={morphing.pipe(map(m => (m ? 1 : 0)))}>\n * <box modifiers={[sharedElement({ name, onMorph: at => (morphing.value = at) })]} />\n *\n * A browser has no equivalent because it does not need one: its\n * named elements are lifted out of the page into a layer above\n * everything, so nothing they might overlap is even in the picture.\n * Gesso morphs the real node, which stays exactly where it is in the\n * tree — the same trade that makes the morph interruptible and free\n * of a raster.\n */\n readonly onMorph?: (morphing: boolean) => void;\n}\n\n/**\n * Makes an element that appears under a name continue from wherever\n * the element that had that name was standing.\n *\n * ```tsx\n * <Image modifiers={[sharedElement({ name: 'playlist-image' })]} />\n * ```\n *\n * The departing element is hidden the moment the name changes hands,\n * because the arriving one is about to be drawn exactly over it, and\n * two copies of one thing reads as a double image rather than as a\n * transition.\n */\nexport const sharedElement = defineModifier<SharedElementArgs>({\n name: 'sharedElement',\n attach(host, args) {\n const controller = new SharedElementController(host, args);\n controller.attach();\n host.own(() => controller.detach());\n }\n});\n\nclass SharedElementController {\n private readonly layer: MotionLayer;\n /** Where the previous holder of the name stood, until it is used. */\n private claimed: LayoutBox | null = null;\n /**\n * Hides the element this one is replacing.\n *\n * Held rather than called on arrival: see `UiSharedElements.claim`.\n * Between claiming a name and taking the departing element's place\n * there is at least one layout, and for a geometry morph a whole\n * frame — and hiding it up front makes that a frame with neither\n * element on screen, which reads as a flash of the page behind them.\n */\n private yieldPrevious: (() => void) | null = null;\n private yielded = false;\n /** Whether a morph is running, so `onMorph` is never told twice. */\n private morphing = false;\n /** Set for the one frame a geometry morph spends being measured. */\n private geometryTarget: LayoutBox | null = null;\n\n constructor(\n private readonly host: UiModifierHost,\n private readonly args: SharedElementArgs\n ) {\n this.layer = new MotionLayer(host);\n }\n\n attach(): void {\n const registry = this.host.shared;\n if (registry === null) {\n return;\n }\n const claim = registry.claim(\n this.args.name,\n this.host.node,\n () => this.yieldName(),\n () => this.unyield()\n );\n this.claimed = claim.box;\n this.yieldPrevious = claim.yieldPrevious;\n if (claim.box !== null) {\n // Said now, during reconciliation, and not from the first layout\n // as the FLIP itself is. `onMorph` exists so an ancestor can lift\n // the morphing element over its neighbours with a `zIndex`, and\n // paint order is sorted *during* layout from that property: a\n // write made from the `onLayout` callback lands after the sort\n // and is not seen until the next frame lays out. Measured in the\n // transitions example on the frame after Back: the card whose\n // background was shrinking from the page still had `zIndex` 0\n // and painted at its tree position, so the card below it, painted\n // later, covered the background for that one frame while the FLIP\n // transform drew it over the whole page. The claim already says a\n // morph is coming, so this is the earliest it can be said, and it\n // gives the raise the same frame the FLIP has.\n this.beginMorph();\n }\n const box = this.host.layoutBox();\n if (box !== null) {\n registry.report(this.args.name, this.host.node, box);\n }\n this.host.onLayout(() => this.handleLayout());\n }\n\n detach(): void {\n // Whatever this element was replacing must not be left hidden\n // because this one went away before it ever took its place — and\n // whatever was raised for this morph must not be left raised\n // because the morph never finished.\n this.endMorph();\n this.takeOver();\n this.host.shared?.release(this.args.name, this.host.node);\n this.layer.release();\n }\n\n private beginMorph(): void {\n if (this.morphing) {\n return;\n }\n this.morphing = true;\n // Written here, at the claim, for the reason `onMorph` is told\n // here: `lift` is read while the tree is laid out, and this frame's\n // layout is the one the morph's first frame is drawn from.\n if (this.args.lift !== false) {\n this.host.set('lift', true);\n }\n // An element in flight is scenery, not a target. It is somewhere it\n // does not belong, it is there for a few hundred milliseconds, and\n // for most of that it is larger than its resting self and covering\n // its neighbours: a press that lands on it was aimed at whatever it\n // is passing over. Pressing a second card while the first was still\n // flying home opened the first one again, which is what this stops,\n // and it is why a browser's view-transition pseudo-elements take no\n // pointer events either.\n this.host.set('pointerEvents', 'none');\n this.args.onMorph?.(true);\n }\n\n private endMorph(): void {\n if (!this.morphing) {\n return;\n }\n this.morphing = false;\n if (this.args.lift !== false) {\n this.host.clear('lift');\n }\n this.host.clear('pointerEvents');\n this.args.onMorph?.(false);\n }\n\n /**\n * This element is now standing where the one it replaces was, so\n * that one may finally disappear.\n */\n private takeOver(): void {\n const yieldPrevious = this.yieldPrevious;\n this.yieldPrevious = null;\n yieldPrevious?.();\n }\n\n /**\n * Another node has taken the name. Get out of the way at once.\n *\n * Not an animation: the arriving element is about to be drawn from\n * this element's box, and anything but an instant disappearance puts\n * two of the same thing on screen at the same time.\n */\n private yieldName(): void {\n if (this.yielded) {\n return;\n }\n this.yielded = true;\n // Whatever this element was in the middle of is over: something\n // else holds the name now and this one is about to be invisible.\n // Said before the snap, because the snap is what cancels the\n // animation this morph was waiting on — `animateTo`'s callback\n // fires only for the run that is still current, so a morph\n // interrupted here would never report that it had finished, and\n // the raise (`onMorph`) and the `lift` it asked for would both be\n // left switched on for the life of the element. Visible as a card\n // from an earlier transition painting over the next screen.\n this.endMorph();\n this.layer.snapTo({ ...MOTION_REST, opacity: 0 });\n }\n\n /**\n * The name has come back, so this element does too.\n *\n * Stepping aside used to be a one-way door, and an interrupted\n * transition walked straight into it. A route change keeps the\n * departing screen alive for the length of its exit, so pressing Back\n * part-way through returns to a screen whose element has already\n * yielded; the arriving element then leaves without anything\n * replacing it, and the only copy left is the invisible one. The\n * picture simply disappeared.\n *\n * A snap rather than a fade, for the reason the yield is a snap: the\n * element it was standing aside for is going away this frame, and\n * anything gradual puts a gap between the two where the picture is\n * half there.\n */\n private unyield(): void {\n if (!this.yielded) {\n return;\n }\n this.yielded = false;\n this.layer.snapTo(MOTION_REST);\n }\n\n private handleLayout(): void {\n // The **visible** box, not the flow box — the opposite of what\n // `animateLayout` reads, and for the opposite reason. A layout\n // animation must not mistake a scroll for a move, so it watches\n // where a node sits in the flow. A morph is a visual continuation\n // of what a person is looking at: an element halfway down a\n // scrolled list should be picked up from where it appears on\n // screen, not from where it would be if the list were at the top.\n // With an unscrolled list the two are the same, which is exactly\n // why reading the wrong one looked correct for a while.\n const box = this.host.layoutBox();\n if (box === null) {\n return;\n }\n this.host.shared?.report(this.args.name, this.host.node, box);\n this.layer.measure(this.host.layoutBox());\n if (this.geometryTarget !== null) {\n // The frame after the geometry override landed: the element is\n // genuinely laid out at the departing element's box now, so the\n // transform that was standing in for it comes off. Both draw the\n // same rectangle, so there is nothing to see at the changeover.\n this.geometryTarget = null;\n this.layer.snapTo(MOTION_REST);\n return;\n }\n const from = this.claimed;\n if (from === null) {\n return;\n }\n // The first layout after arriving: both boxes are known now, so the\n // delta is known, and this is the only frame on which it is worth\n // computing.\n this.claimed = null;\n if (box.width <= 0 || box.height <= 0 || from.width <= 0 || from.height <= 0) {\n // Nothing to morph from or into, so whatever was raised at the\n // claim comes down again.\n this.layer.snapTo(MOTION_REST);\n this.takeOver();\n this.endMorph();\n return;\n }\n const timing = this.timing();\n // FLIP, with the pivot at the node's own centre — which is why the\n // offset is between centres and the scale is a plain ratio of\n // sizes, with nothing else to correct for.\n const scaleX = from.width / box.width;\n const scaleY = from.height / box.height;\n const uniform = this.args.scale === 'uniform';\n const flip: ResolvedMotionState = {\n opacity: this.args.fadeFrom ?? MOTION_REST.opacity,\n x: from.x + from.width / 2 - (box.x + box.width / 2),\n y: from.y + from.height / 2 - (box.y + box.height / 2),\n scaleX,\n scaleY: uniform ? scaleX : scaleY,\n rotate: 0\n };\n // The transform lands *this* frame — `onLayout` runs after the boxes\n // are final and before anything paints from them — so the element is\n // drawn over the departing one straight away and that one can step\n // aside with nothing in between. The morph itself was announced at\n // the claim (see `attach`), because a raise made in answer to it has\n // to be in this frame's layout, which has already run by now.\n this.layer.snapTo(flip);\n this.takeOver();\n if (this.args.morph === 'geometry') {\n // A geometry morph cannot land until the next frame, because it\n // writes `left`/`top`/`width`/`height` and this frame's layout has\n // already run. The transform above is what covers that frame: it\n // draws the element at exactly the box the geometry is about to\n // give it, and comes off as soon as it has. Hiding the element for\n // that frame instead — which is what this did first — is a frame\n // with neither the departing element nor the arriving one on\n // screen, and it reads as the page flashing through.\n this.morphGeometry(from, box, timing);\n return;\n }\n this.layer.animateTo(MOTION_REST, timing, () => this.endMorph());\n }\n\n /**\n * A duration by default, not a spring — which is the opposite of what\n * this started as, and a measurement is why.\n *\n * A spring's shape does not change with distance: it covers the same\n * *proportion* of the move in the same time whatever the move is. A\n * `snappy` morph over 676px was measured stepping 14, 44, 62, 69, 71,\n * 68 pixels a frame — half the distance in about 165ms, then the last\n * third at a crawl. A card near the middle of a list has a hundred\n * pixels to travel and that reads as a gentle expansion; the same\n * card near the edge of the screen has six hundred, and the identical\n * curve reads as a jump to the middle followed by a slow settle.\n *\n * A fixed duration spends the same time on the move however far it\n * goes, so the whole path is visible and the eye can follow it. It is\n * also what the browser's own View Transitions do, and why a morph\n * there looks like travel rather than a cut.\n *\n * A spring is still right for a movement that follows a gesture,\n * where the distance is whatever the finger did and there is a real\n * velocity to carry — so `spring` remains available per call site,\n * and naming one still turns the duration off.\n */\n private timing(): MotionTiming {\n const timed = this.args.spring === undefined;\n return {\n spring: this.args.spring,\n duration: timed ? (this.args.duration ?? 'slow') : this.args.duration,\n easing: timed ? (this.args.easing ?? 'standard') : this.args.easing,\n delay: this.args.delay,\n reducedMotion: this.args.reducedMotion\n };\n }\n\n /**\n * The exact morph: the element's own box animates, so its corners,\n * its border and everything laid out inside it are re-resolved every\n * frame rather than stretched.\n *\n * **It is offsets, not coordinates.** The measured boxes are in the\n * *world*, and `left`/`top` place an element inside its containing\n * block; writing one into the other puts the element wherever its\n * containing block happens to start, which is a bug that looks like a\n * layout bug. So what is animated is the difference between the two\n * boxes, added to whatever the element declared — the same arithmetic\n * `animateLayout` does, and correct whatever the containing block is.\n *\n * The difference is measured in *visible* coordinates while\n * `left`/`top` are pre-scroll, and those agree because a scroll is a\n * constant translation: a delta is the same number in either frame,\n * as long as the arriving element's own scroll offset does not change\n * while the morph runs. It does not — a screen arrives at whatever\n * offset it was restored to and stays there.\n *\n * **The element must place itself with `left`/`top` and size itself\n * with `width`/`height`.** An absolutely positioned element pinned\n * by insets (`left` *and* `right`) has no width of its own to\n * animate, and writing one on top of both is over-constrained: the\n * result depends on which of the three the engine chooses to ignore.\n * That is refused rather than guessed at.\n */\n private morphGeometry(from: LayoutBox, to: LayoutBox, timing: MotionTiming): void {\n const position = this.host.get<string | undefined>('position');\n if (position !== 'absolute' && position !== 'fixed') {\n warnGeometryNeedsPositioning(this.args.name, position, 'position');\n this.layer.snapTo(MOTION_REST);\n this.takeOver();\n this.endMorph();\n return;\n }\n const baseLeft = numberOrZero(this.host.get<unknown>('left'));\n const baseTop = numberOrZero(this.host.get<unknown>('top'));\n if (baseLeft === null || baseTop === null) {\n warnGeometryNeedsPositioning(this.args.name, position, 'offsets');\n this.layer.snapTo(MOTION_REST);\n this.takeOver();\n this.endMorph();\n return;\n }\n this.geometryTarget = to;\n const geometry = new GeometryMorph(this.host, {\n left: baseLeft + from.x - to.x,\n top: baseTop + from.y - to.y,\n width: from.width,\n height: from.height\n });\n const targets = { left: baseLeft, top: baseTop, width: to.width, height: to.height };\n let outstanding = 0;\n let settled = 0;\n const done = (): void => {\n settled++;\n if (settled === outstanding) {\n // Back where it belongs: the overrides are dropped rather than\n // left holding the values they arrived at, so the element goes\n // on being laid out by what it declared. Leaving them behind\n // would freeze a card at the size the window happened to be.\n geometry.release();\n this.endMorph();\n }\n };\n for (const field of GEOMETRY_FIELDS) {\n outstanding++;\n const cell = geometry.cell(field);\n const to = targets[field];\n const values =\n timing.spring !== undefined\n ? this.host.spring(cell, to, {\n spring: springSpec(timing.spring),\n delay: timing.delay,\n reducedMotion: timing.reducedMotion,\n // Half a pixel is the same pixel on a 1x screen;\n // scheduling frames past it buys a movement nobody can\n // see.\n restDelta: 0.5\n })\n : this.host.animate(cell, to, {\n duration: resolveDuration(timing.duration),\n easing: resolveEasing(timing.easing),\n delay: timing.delay,\n reducedMotion: timing.reducedMotion\n });\n values.subscribe({ complete: done });\n }\n }\n}\n\nconst GEOMETRY_FIELDS = ['left', 'top', 'width', 'height'] as const;\n\ntype GeometryField = (typeof GEOMETRY_FIELDS)[number];\n\n/**\n * The four numbers a geometry morph drives, written as one override\n * set.\n *\n * Written through `host.set` like every other modifier write, so\n * `release` — and detaching, and the node leaving — all hand the\n * element back exactly what it declared. That matters more here than\n * anywhere else, because what it declared is where the element\n * actually belongs.\n */\nclass GeometryMorph {\n constructor(\n private readonly host: UiModifierHost,\n private readonly state: Record<GeometryField, number>\n ) {\n this.write();\n }\n\n cell(field: GeometryField): AnimatedCell<number> {\n // Read and written through closures rather than through `this`,\n // because a cell is a property pair on an object literal and an\n // object literal's `this` is the literal.\n const read = (): number => this.state[field];\n const write = (next: number): void => {\n this.state[field] = next;\n this.write();\n };\n return {\n get value(): number {\n return read();\n },\n set value(next: number) {\n write(next);\n }\n };\n }\n\n release(): void {\n for (const field of GEOMETRY_FIELDS) {\n this.host.clear(field);\n }\n }\n\n private write(): void {\n for (const field of GEOMETRY_FIELDS) {\n this.host.set(field, this.state[field]);\n }\n }\n}\n\n/** A plain number, or null for a typed length this cannot add pixels to. */\nfunction numberOrZero(value: unknown): number | null {\n if (value === undefined || value === null) {\n return 0;\n }\n return typeof value === 'number' && Number.isFinite(value) ? value : null;\n}\n\nconst warnedGeometry = new Set<string>();\n\nfunction warnGeometryNeedsPositioning(\n name: string,\n position: string | undefined,\n reason: 'position' | 'offsets'\n): void {\n if (warnedGeometry.has(reason)) {\n return;\n }\n warnedGeometry.add(reason);\n if (reason === 'position') {\n console.warn(\n `sharedElement('${name}') asked for morph: 'geometry' on a '${position ?? 'static'}' element, and did ` +\n `nothing. A geometry morph animates left, top, width and height, which place an absolutely positioned ` +\n `element but only offset one in the flow. Position the element, or use the default morph: 'transform'.`\n );\n return;\n }\n console.warn(\n `sharedElement('${name}') asked for morph: 'geometry' on an element whose 'left' or 'top' is a typed length ` +\n `(percent, fr, auto), and did nothing. The morph adds pixels to what the element declared, and there is no ` +\n `pixel value to add to. Give it numeric offsets and a width and height, or use morph: 'transform'.`\n );\n}\n","import { linear } from '../animation/UiEasing';\nimport type { AnimatedCell } from '../animation/UiAnimation';\nimport type { VideoPlayback, VideoResolver } from '../media/VideoResolver';\nimport { defineModifier } from './UiModifier';\nimport type { UiModifierHost } from './UiModifierHost';\n\n/**\n * Where a clip stands: loading it, showing it, or having failed.\n *\n * `playing` is about the *picture*, not about motion — it is the\n * moment a surface reaches the node, and it arrives for a clip built\n * with `autoplay: false` exactly as it does for one that runs. Whether\n * time is moving is `VideoTransport.paused`, which is a different\n * question and was never what this answered.\n */\nexport type VideoState = 'loading' | 'playing' | 'failed';\n\n/**\n * The clock a video's position comes from, when it is not the video's\n * own.\n *\n * The default is a tween on the animation driver, which is right for\n * a clip with no sound: nothing else in the application has an opinion\n * about where the clip should be, so the frame clock may as well\n * decide.\n *\n * It stops being right the moment there is audio. Audio cannot drop\n * samples the way video drops frames — a gap is audible and a\n * resample is worse — so where the two must agree, the audio is the\n * clock and the picture follows it. That inversion is the whole reason\n * this seam exists, and it is a *seam* rather than an implementation\n * because `AudioContext` does not exist on a worker: the sound is\n * played by the shell, and what crosses the thread boundary is this\n * one number. See `AudioService`, which already reports it.\n *\n * `positionSeconds` is called once per presentation and must be cheap.\n * Returning a position that goes backwards is a seek, exactly as it is\n * for the tween.\n */\nexport interface VideoClock {\n /** Where the clip should be now, in seconds. */\n positionSeconds(): number;\n /** Whether the clock is running. A paused clock presents no new frames. */\n readonly running: boolean;\n /** Told when `running` changes, so the modifier can stop asking for frames. */\n onChange(listener: () => void): () => void;\n}\n\n/**\n * The handle on a running clip: where it is, and the four things that\n * can be done to it.\n *\n * Handed out through `VideoSourceArgs.onReady` rather than returned,\n * because a playback does not exist until a file has been fetched and\n * a decoder configured, and a modifier attaches long before either.\n *\n * Nothing here is reactive. Core has no cells — `AnimatedCell` is an\n * interface it asks others to satisfy, not a thing it implements — so\n * this reports through `onChange` and the framework's tier wraps it in\n * whatever its own state primitive is. `Video` does exactly that.\n */\nexport interface VideoTransport {\n /** The clip's length in seconds. Zero until the container has been read. */\n readonly duration: number;\n /** Where the picture is now, in seconds. */\n readonly position: number;\n /** Whether time has stopped. A seek while paused still moves the picture. */\n readonly paused: boolean;\n /** How fast time runs. 1 is the clip's own rate; 0.5 is half speed. */\n readonly rate: number;\n /**\n * How loud, from 0 to 1, and whether it is silenced outright.\n *\n * **Nothing in this framework plays a clip's sound**, because\n * `AudioContext` does not exist on the thread that decodes. So this\n * is state rather than an effect: it is carried here, reported\n * through `onVolume`, and it does something only where an\n * application has wired that to whatever is actually making the\n * noise — `AudioService`, on the shell. A control over it is honest\n * for a clip whose sound is wired and a lie for one whose is not,\n * which is why `Video` decides whether to draw one rather than\n * always drawing it.\n *\n * `muted` is kept separate from a volume of zero, as every player\n * keeps it: unmuting has to return to the level it was at, and a\n * mute that wrote zero would have thrown that away.\n */\n readonly volume: number;\n readonly muted: boolean;\n /** Whether the decoder is still working towards the last position asked for. */\n readonly seeking: boolean;\n readonly state: VideoState;\n play(): void;\n pause(): void;\n /** Moves to a position in seconds, clamped to the clip, playing or not. */\n seek(seconds: number): void;\n /** Positive, and clamped to something a decoder can keep up with. */\n setRate(rate: number): void;\n /** Clamped to 0..1. Setting it above zero also unmutes, as a slider dragged up should. */\n setVolume(volume: number): void;\n setMuted(muted: boolean): void;\n /**\n * Fetches, demuxes and configures again, for a clip that failed.\n *\n * The resolver caches by source, so a clip that failed *inside* the\n * decoder is still held as a failed playback and this would resolve\n * straight back to it. Releasing first is what makes the retry a\n * real one.\n */\n retry(): void;\n /**\n * Told whenever any of the above changed, at most once a frame.\n *\n * Position is deliberately not part of that: it changes on every\n * frame of every clip, and a listener woken sixty times a second to\n * move a scrubber by a pixel is how a video costs an application its\n * frame budget. Read `position` on the frames you are already\n * drawing; this fires for the things that happen *to* a clip — it\n * started, it stopped, it arrived somewhere, it broke.\n */\n onChange(listener: () => void): () => void;\n}\n\nexport interface VideoSourceArgs {\n readonly resolver: VideoResolver;\n readonly source: string;\n /** Start from the beginning again when it ends. Defaults to true. */\n readonly loop?: boolean;\n /** Start playing as soon as it is ready. Defaults to true. */\n readonly autoplay?: boolean;\n /** How fast to play. Defaults to 1. */\n readonly rate?: number;\n /**\n * Stop decoding while the node is off screen. Defaults to true.\n *\n * See `pauseWhenHidden` in this file for what \"off screen\" means and\n * why the default is what it is.\n */\n readonly pauseWhenHidden?: boolean;\n /** Where the position comes from, when it is not this clip's own tween. */\n readonly clock?: VideoClock;\n /** Starting volume, 0 to 1. Defaults to 1. */\n readonly volume?: number;\n readonly muted?: boolean;\n /**\n * Told when the volume or the mute changed, so an application can\n * pass it to whatever is playing the sound. See `VideoTransport.volume`\n * for why this is a report rather than an effect.\n */\n readonly onVolume?: (volume: number, muted: boolean) => void;\n /** Told when the video loads, starts or fails. */\n readonly onState?: (state: VideoState, error?: unknown) => void;\n /**\n * Handed the transport once there is a playback to control.\n *\n * Called again after a successful `retry`, with a transport for the\n * new playback; the old one is inert from the moment it was retried.\n */\n readonly onReady?: (transport: VideoTransport) => void;\n}\n\n/**\n * The slowest and fastest a clip may be asked to run.\n *\n * Not a decoder limit — a decoder will run as fast as it is fed — but\n * a *sanity* one. Zero would be a pause spelled as a rate and would\n * divide by itself below; a negative rate is a request to run the\n * decoder backwards, which is not a thing it does; and past about four\n * the frame queue cannot stay ahead and the clip judders instead of\n * speeding up. Clamped rather than rejected, because a rate is the\n * kind of thing an application binds to a slider.\n */\nconst MIN_RATE = 0.0625;\nconst MAX_RATE = 4;\n\n/**\n * How close to the end counts as having reached it.\n *\n * A tween's last sample lands on its target exactly, but a clip whose\n * tween was cancelled a frame early — by a pause, by a seek — must not\n * be mistaken for one that finished and be looped back to the start.\n */\nconst END_TOLERANCE_MS = 1;\n\n/**\n * Plays a video onto the node's `video` property.\n *\n * A modifier rather than a subscription in a component body for the\n * reason `imageSource` is one, only more so: what it holds is a\n * decoder and a queue of decoded frames, which is the most expensive\n * thing in this framework to leak. `host.own` releases it inside\n * `removeSubtree`, so a node that leaves stops decoding on the frame\n * it goes.\n *\n * **Time comes from the animation driver, not from a timer.** The\n * playback is a pure function of a position, and the position is\n * driven by a linear tween over the video's duration — an ordinary\n * animation on an ordinary cell. So a video is scheduled by the same\n * `ticks` phase as everything else, keeps its own frames coming\n * through the same `nextTickAt`, drops frames rather than falling\n * behind under load, and stops dead when the runtime does. Nothing new\n * had to be added to the runtime to play a video.\n *\n * **Unless something else owns time.** A `clock` in the arguments\n * replaces the tween, and is how a clip keeps step with sound that is\n * being played somewhere this thread cannot reach. The rest of this\n * file does not care which of the two it is driven by: both end at the\n * same cell.\n *\n * **It keeps moving under reduced motion**, and that is deliberate for\n * a clip with no transport. The rule this framework applies\n * (`UiReducedMotionPolicy`) is to stop only when standing still would\n * not state something false, and a video that has frozen on its first\n * frame is a video that has finished loading badly. The media tier\n * makes the same call for the `Spinner`. A clip the reader can *drive*\n * is the other case — there the stillness is not a failure, it is a\n * clip waiting to be played — so `Video` declines to autoplay under\n * reduced motion when it has been given controls, and this modifier\n * is told so through `autoplay`.\n *\n * **Playback is shared by source, not by node.** The resolver\n * reference-counts, so two nodes pointed at one file watch one\n * playback — which is how a video survives a route change: the\n * arriving node resolves the source the departing node still holds and\n * picks it up mid-stream. The reference DOM implementation of this\n * effect has to physically move its `<video>` element into the new\n * document to get the same result.\n *\n * **Picking it up mid-stream needs the offset, and that took a bug to\n * learn.** The decoder is shared, but the tween that drives it belongs\n * to the node, and a node that has just been built starts its tween at\n * zero. During a route transition both screens are mounted at once, so\n * for the length of the exit two tweens drove one playback: one at\n * wherever the clip had got to, one at nearly zero. Each disagreement\n * larger than the seek tolerance is a seek backwards, which drops the\n * frame queue and reconfigures the decoder — about fourteen times\n * across a 240ms transition, on the same thread as layout and paint.\n * The video appeared to freeze, the morph juddered, and when the old\n * screen finally left, the clip carried on from the beginning.\n *\n * So a node starts its tween at whatever the playback had already\n * reached, and the cell holds an absolute position in the clip rather\n * than a distance from where this node joined. Both holders then agree\n * to within a frame, no seek fires, and the continuity this comment\n * claims is actually true. The earlier version of this kept a separate\n * offset and wrapped the sum; holding the position outright is the\n * same arithmetic with one fewer thing to get wrong, and it is what\n * makes `seek` a single assignment.\n */\nexport const videoSource = defineModifier<VideoSourceArgs>({\n name: 'videoSource',\n attach(host, args) {\n load(host, args);\n },\n update(host, args, previous) {\n if (args.resolver === previous.resolver && args.source === previous.source) {\n return;\n }\n previous.resolver.release(previous.source);\n load(host, args);\n }\n});\n\nfunction load(host: UiModifierHost, args: VideoSourceArgs): void {\n let live = true;\n let playback: VideoPlayback | null = null;\n let stopErrors: (() => void) | null = null;\n let stopFrames: (() => void) | null = null;\n let stopClock: (() => void) | null = null;\n let positionMs = 0;\n /** The clip's length, known only once it has been read. */\n let durationMs = 0;\n let paused = args.autoplay === false;\n let rate = clampRate(args.rate ?? 1);\n let volume = clampVolume(args.volume ?? 1);\n let muted = args.muted === true;\n let state: VideoState = 'loading';\n /**\n * Whether the node is somewhere a reader could see it. Assumed true\n * until layout says otherwise, so a clip is never held back waiting\n * for a measurement that a headless graph will never take.\n */\n let onScreen = true;\n const changeListeners = new Set<() => void>();\n\n args.onState?.('loading');\n host.clear('video');\n\n const announce = (next: VideoState, error?: unknown): void => {\n state = next;\n args.onState?.(next, error);\n changed();\n };\n\n const changed = (): void => {\n for (const listener of changeListeners) {\n listener();\n }\n };\n\n // The cell the driver writes: setting it presents whichever frame is\n // due at that position, and asks for a paint only when that actually\n // changed the picture. A video at 30 Hz inside an app at 60 therefore\n // paints thirty times a second, not sixty.\n const position: AnimatedCell<number> = {\n get value(): number {\n return positionMs;\n },\n set value(next: number) {\n positionMs = next;\n if (playback === null) {\n return;\n }\n if (playback.present(next)) {\n host.requestFrame();\n }\n }\n };\n\n /**\n * Runs the clip from where it stands to its end.\n *\n * A fresh tween each time rather than one repeating tween for the\n * life of the clip, which is what this used to be. A repeating tween\n * always starts from the value the cell held when it was built and\n * always runs the whole duration, so there is no way to begin one\n * part-way through — which is to say no way to seek, no way to\n * resume after a pause, and no way to change rate without the clip\n * jumping. Taking the loop apart into \"play to the end, then decide\"\n * costs one subscription per lap and buys all three.\n */\n const run = (): void => {\n if (playback === null || durationMs <= 0) {\n return;\n }\n const remaining = durationMs - positionMs;\n if (remaining <= END_TOLERANCE_MS) {\n finish();\n return;\n }\n host\n .animate(position, durationMs, {\n duration: remaining / rate,\n easing: linear,\n reducedMotion: 'keep',\n // The video's own rate, which is what keeps a playing video\n // from pinning the whole application to the frame clock. A\n // tween with no `stepMs` asks to be sampled every frame; that\n // was survivable while frames were a fixed sixty and is not\n // once they follow the display, where a 30fps clip would wake\n // a 165Hz app 165 times a second to present the same picture\n // five times running. Declaring the rate instead lets\n // `AnimationDriver.nextTickAt` do what it is for: the earliest\n // moment *anybody* wants a frame, so an animation starting\n // beside this video raises the rate for as long as it runs and\n // the page falls back to the video's cadence when it settles.\n // Divided by the rate, because a clip at half speed changes\n // its picture half as often and should wake the app half as\n // often to say so.\n stepMs: playback.frameDurationMs / rate\n })\n .subscribe({\n complete: () => {\n // A tween completes when it arrives *and* when it is\n // cancelled — by a pause, a seek, or the node leaving — and\n // the observable cannot tell those apart. The cell can: only\n // a tween that arrived left the position at the end.\n if (live && !paused && durationMs - positionMs <= END_TOLERANCE_MS) {\n finish();\n }\n }\n });\n };\n\n /** The clip reached its end: back to the start, or stop on the last frame. */\n const finish = (): void => {\n if (args.loop === false) {\n paused = true;\n changed();\n return;\n }\n position.value = 0;\n run();\n // A lap of a loop is something that happened to the clip, and\n // anything extrapolating the position — a scrubber, a caption\n // track — has just been left a whole duration ahead of reality.\n changed();\n };\n\n /** Whether time should be moving, given everything that can stop it. */\n const shouldRun = (): boolean => live && !paused && onScreen && playback !== null;\n\n /**\n * Brings the driver in line with `shouldRun`.\n *\n * Every one of pause, play, seek, rate and visibility ends here\n * rather than each starting and stopping the tween itself, because\n * the question they all really ask is the same one and the answer\n * has to survive them happening together: a clip scrolled off screen\n * while paused must not start playing when it comes back into view.\n */\n const sync = (): void => {\n host.stopAnimation(position);\n if (shouldRun()) {\n run();\n }\n };\n\n const transport: VideoTransport = {\n get duration(): number {\n return durationMs / 1000;\n },\n get position(): number {\n return positionMs / 1000;\n },\n get paused(): boolean {\n return paused;\n },\n get rate(): number {\n return rate;\n },\n get volume(): number {\n return volume;\n },\n get muted(): boolean {\n return muted;\n },\n get seeking(): boolean {\n return playback?.seeking ?? false;\n },\n get state(): VideoState {\n return state;\n },\n play(): void {\n if (!paused) {\n return;\n }\n paused = false;\n // A clip that ran to its end and stopped there is asked to play\n // again from the beginning, which is what every player does and\n // what the alternative — presenting the last frame forever —\n // would make look broken.\n if (durationMs > 0 && durationMs - positionMs <= END_TOLERANCE_MS) {\n position.value = 0;\n }\n sync();\n changed();\n },\n pause(): void {\n if (paused) {\n return;\n }\n paused = true;\n sync();\n changed();\n },\n seek(seconds: number): void {\n const wanted = Math.min(Math.max(0, seconds * 1000), durationMs);\n // Written before the driver is restarted, so the picture moves on\n // this frame rather than on the next one the tween samples: a\n // scrubber dragged while paused is the whole reason to care.\n position.value = wanted;\n sync();\n changed();\n },\n setRate(next: number): void {\n const clamped = clampRate(next);\n if (clamped === rate) {\n return;\n }\n rate = clamped;\n // The tween's duration is baked in at construction, so a rate\n // change is a new tween from wherever the old one had got to.\n sync();\n changed();\n },\n setVolume(next: number): void {\n const clamped = clampVolume(next);\n // Dragging a slider up from zero is a request to hear it, and a\n // player that stayed silent because it was also muted would look\n // broken. Dragging to zero does not mute: that is what the\n // button is for, and conflating them loses the level.\n const unmuted = clamped > 0 ? false : muted;\n if (clamped === volume && unmuted === muted) {\n return;\n }\n volume = clamped;\n muted = unmuted;\n args.onVolume?.(volume, muted);\n changed();\n },\n setMuted(next: boolean): void {\n if (next === muted) {\n return;\n }\n muted = next;\n args.onVolume?.(volume, muted);\n changed();\n },\n retry(): void {\n if (!live) {\n return;\n }\n // Tearing down this load rather than reaching into it: `load`\n // registers its own teardown with the host, and running it here\n // would leave that registration holding a released source. So the\n // release happens here and the dead load is fenced off with\n // `live`, exactly as the detach path does it.\n live = false;\n host.stopAnimation(position);\n stopErrors?.();\n stopFrames?.();\n stopClock?.();\n args.resolver.release(args.source);\n host.clear('video');\n load(host, args);\n },\n onChange(listener: () => void): () => void {\n changeListeners.add(listener);\n return () => changeListeners.delete(listener);\n }\n };\n\n args.resolver\n .resolve(args.source)\n .then(resolved => {\n if (!live) {\n return;\n }\n playback = resolved;\n durationMs = Math.max(1, resolved.duration * 1000);\n // Whatever it had already reached, which is zero for a playback\n // nobody was holding and mid-clip for one this element is\n // joining part-way through a transition. An absolute position in\n // the clip, which is what every other line here means by one.\n positionMs = Math.min(resolved.positionMs, durationMs);\n host.set('video', resolved.surface);\n stopErrors = resolved.onError(error => announce('failed', error));\n // A seek answers late — the frame for the position asked for has\n // to be decoded first — and a paused clip has no next frame to\n // answer on. Without this a scrub while paused moves the\n // scrubber and leaves the picture where it was.\n stopFrames = resolved.onFrame?.(() => host.requestFrame()) ?? null;\n announce('playing');\n // The first frame is worth having whether or not time is about to\n // move: a paused video showing nothing looks like one that\n // failed. Its own position rather than zero, so a still that\n // joins a playing holder does not seek it back to the start.\n resolved.present(positionMs);\n host.requestFrame();\n attachClock();\n sync();\n args.onReady?.(transport);\n })\n .catch((error: unknown) => {\n if (live) {\n announce('failed', error);\n }\n });\n\n /**\n * Follows an external clock, when there is one.\n *\n * The tween is never built in this case: `sync` still decides\n * whether time should be moving, but what moves it is the clock's\n * own progress, sampled once per frame through the same cell the\n * tween would have written.\n */\n function attachClock(): void {\n const clock = args.clock;\n if (clock === undefined) {\n return;\n }\n const followClock = (): void => {\n position.value = Math.min(Math.max(0, clock.positionSeconds() * 1000), durationMs);\n };\n stopClock = clock.onChange(() => {\n paused = !clock.running;\n followClock();\n changed();\n });\n paused = !clock.running;\n followClock();\n }\n\n host.own(() => {\n live = false;\n host.stopAnimation(position);\n stopErrors?.();\n stopFrames?.();\n stopClock?.();\n changeListeners.clear();\n args.resolver.release(args.source);\n });\n\n /**\n * Whether the node is somewhere worth decoding for.\n *\n * A clip scrolled out of the viewport still fetches, decodes,\n * converts to a bitmap and uploads a texture sixty times a second\n * for nobody, and the only thing that stops it today is the tab\n * being hidden — which the runtime already handles, at the driver.\n * Within a visible page nothing did, so a page of clips cost the sum\n * of all of them however few were on screen.\n *\n * The test is the node's box against the root's, with no margin: a\n * clip is either somewhere a reader could see it or it is not, and a\n * clip that starts a frame late coming into view is a far smaller\n * fault than one that never stops. What it must not do is *seek* —\n * the position is left exactly where it stopped, so coming back into\n * view resumes rather than reloads, and the decoder is not reset.\n */\n function watchVisibility(): void {\n if (args.pauseWhenHidden === false) {\n return;\n }\n const check = (): void => {\n const box = host.layoutBox();\n const viewport = host.viewportBox();\n if (box === null || viewport === null) {\n return;\n }\n if (box.width <= 0 || box.height <= 0) {\n // Laid out to nothing, which is what a clip sized by its\n // container looks like before the container has been sized.\n // Not a statement that it is off screen, and treating it as\n // one stops every video whose height comes from its content\n // from ever starting.\n return;\n }\n const visible =\n box.x < viewport.x + viewport.width &&\n box.x + box.width > viewport.x &&\n box.y < viewport.y + viewport.height &&\n box.y + box.height > viewport.y;\n if (visible === onScreen) {\n return;\n }\n onScreen = visible;\n sync();\n changed();\n };\n host.onLayout(check);\n check();\n }\n\n watchVisibility();\n}\n\nfunction clampVolume(volume: number): number {\n if (!Number.isFinite(volume)) {\n return 1;\n }\n return Math.min(1, Math.max(0, volume));\n}\n\nfunction clampRate(rate: number): number {\n if (!Number.isFinite(rate)) {\n return 1;\n }\n return Math.min(MAX_RATE, Math.max(MIN_RATE, rate));\n}\n","import { linear } from '../animation/UiEasing';\nimport type { AnimatedCell } from '../animation/UiAnimation';\nimport { defineModifier } from './UiModifier';\n\n/**\n * How long the offset has to hold still before a scroll counts as\n * over. Long enough to bridge a gap between two wheel notches, short\n * enough that a value persisted afterwards is not noticeably late.\n */\nconst SETTLE_MS = 120;\n\n/** How far a scroll container has been scrolled, in logical pixels. */\nexport interface ScrollOffset {\n readonly x: number;\n readonly y: number;\n}\n\nexport interface ScrollPositionArgs {\n /**\n * Told whenever this container's scroll offset changes, and only\n * then — not when the container merely moved.\n *\n * \"Not on every frame\" used to be part of this promise, and a\n * smoothed wheel ended it: an animated scroll changes the offset on\n * every frame it runs, so one notch is now ten or twenty calls\n * rather than one. That is the honest report of what happened, and\n * it is what a scroll-linked header or a position indicator wants.\n * A consumer that only cares where the scrolling *stopped* wants\n * `onSettled` instead.\n */\n readonly onChange?: (offset: ScrollOffset) => void;\n /**\n * Told once when the offset stops changing.\n *\n * What persisting a position wants — a route remembering where its\n * list was — since writing that on every frame of every notch is a\n * great deal of writing for one answer nobody reads until later.\n *\n * \"Stops\" is measured by a frame passing with the offset unchanged,\n * because this modifier is built on layout notifications and has no\n * view of whether an animation is running. That makes it a frame\n * late by construction, which for a value read after the fact is\n * free.\n */\n readonly onSettled?: (offset: ScrollOffset) => void;\n}\n\n/**\n * Reports a scroll container's offset whenever it changes.\n *\n * The counterpart of `measure`, and the answer to \"how does an\n * application find out where a list is scrolled to\". It exists because\n * nothing else could tell you: a wheel writes the container's offset\n * from inside the runtime, and so does a scrollbar drag, a trackpad\n * pan, and the `scrollIntoView` that keeps a focused row on screen.\n *\n * **Why this is not a scroll event.** Two reasons, and the second is\n * the one that decided it.\n *\n * An event would come from the input stack, and only some of the causes\n * above are input — a list scrolled because focus moved into a row\n * below the fold has scrolled just as much, and an `onScroll` that\n * stayed quiet for it would be a trap. To fire for all of them the\n * event would have to come from the layout phase, at which point it is\n * not an input event at all.\n *\n * And Gesso already has exactly one mechanism for \"this node's layout\n * changed on this frame\" — `LayoutNotifier`, reached through\n * `host.onLayout` — which this is built on. A second mechanism carrying\n * the same fact is the mistake `animateLayout` records not making when\n * it read `LayoutNotifier` rather than inventing a way to keep previous\n * boxes.\n *\n * **Restoring an offset is not this modifier's job**, and that\n * asymmetry is deliberate. `scrollY` is an ordinary property, so it is\n * put back the ordinary way — bind it, `<scrollview scrollY={cell}>`,\n * and a container built again after a route change is laid out where it\n * was left, before it paints. A modifier could not do it anyway: a\n * modifier writes through the override cascade, the runtime's own\n * scrolling writes the element's declared value, and an override would\n * shadow every wheel for the life of the node.\n *\n * const at = internalState(0);\n * <scrollview scrollY={at} modifiers={[scrollPosition({ onChange: o => (at.value = o.y) })]}>\n *\n * The offset reported is the container's **effective** one, taken from\n * the layout record: a wheel writes the property unclamped and the\n * engine clamps it to the content on the next layout, so the property\n * can name a place the list never actually went.\n */\nexport const scrollPosition = defineModifier<ScrollPositionArgs>({\n name: 'scrollPosition',\n attach(host, args) {\n let last: ScrollOffset | null = null;\n /**\n * The settle detector: a short animation restarted on every\n * change, whose completion means nothing changed while it ran.\n *\n * A frame that reports nothing cannot be observed here —\n * `onLayout` fires on *change*, which is the whole point of it —\n * and the last frame of a scroll is a frame where the offset\n * changed, with nothing armed after it. So the end of a scroll has\n * to be waited for rather than noticed, and this waits on the\n * framework's own clock rather than a timer: it is cancelled with\n * the node, it stops when the runtime does, and a reduced-motion\n * app snaps it, which reports the settle immediately and is\n * exactly right.\n */\n let settling = 0;\n const settleCell: AnimatedCell<number> = {\n get value(): number {\n return settling;\n },\n set value(next: number) {\n settling = next;\n }\n };\n const armSettle = (offset: ScrollOffset): void => {\n settling = 0;\n host.animate(settleCell, 1, { duration: SETTLE_MS, easing: linear }).subscribe({\n complete: () => {\n if (settling === 1) {\n args.onSettled?.(offset);\n }\n }\n });\n };\n host.onLayout(() => {\n const offset = host.scrollOffset();\n if (offset === null || (last !== null && last.x === offset.x && last.y === offset.y)) {\n return;\n }\n last = offset;\n args.onChange?.(offset);\n if (args.onSettled !== undefined) {\n armSettle(offset);\n }\n });\n }\n});\n","import { UiEnvironmentKeys } from '../environment/UiEnvironmentKeys';\nimport { UiInsetRegistry, type UiInsetEdge, type UiInsets } from '../environment/UiInsets';\nimport { defineModifier } from './UiModifier';\n\nexport interface PublishInsetArgs {\n /** Which edge this node is over. */\n readonly edge: UiInsetEdge;\n /**\n * How much space to claim, when the node's own box is not the\n * answer. Left out, the node publishes its measured height on a\n * horizontal edge and its width on a vertical one, which is what a\n * bar sitting across the bottom of a screen wants.\n */\n readonly extent?: number;\n}\n\n/**\n * Publishes the space this node occupies to the screens behind it.\n *\n * A floating bar, a toast rail, a docked player: anything drawn over\n * the content rather than in it takes room that the content has to be\n * kept clear of, and before this every screen worked that room out for\n * itself. In Segue seven screens each imported the queue channel, each\n * asked whether anything was playing, and each added the bar's height\n * from a constant, which is seven copies of a fact one component\n * already knew, and two more screens that used a different number.\n *\n * The bar publishes; the screens read. Neither knows about the other,\n * which is what makes it possible to add a second bar without editing\n * a screen.\n *\n * The contribution is retracted when the modifier detaches, so a bar\n * inside a `Presence` gives its room back as it leaves. It is\n * republished whenever the node's box changes, so a bar that grows\n * moves the content with it.\n *\n * Publishes into the nearest `UiInsetRegistry` the environment\n * carries. An application provides one on its root with the `insets`\n * prop; without one the registry the key defaults to is used, which\n * works for a single-window runtime and is what a spec gets.\n */\nexport const publishInset = defineModifier<PublishInsetArgs>({\n name: 'publishInset',\n attach(host, args) {\n const source = host.environment(UiEnvironmentKeys.insets);\n if (!(source instanceof UiInsetRegistry)) {\n // A source that is not a registry is somebody's read-only view of\n // one; there is nothing to publish into and nothing useful to do\n // about it beyond leaving the content where it is.\n return;\n }\n let write: ((next?: Partial<UiInsets>) => void) | null = null;\n const publish = (): void => {\n const box = host.layoutBox();\n if (box === null) {\n return;\n }\n const extent = args.extent ?? (args.edge === 'top' || args.edge === 'bottom' ? box.height : box.width);\n const insets = { [args.edge]: extent } as Partial<UiInsets>;\n if (write === null) {\n write = source.publish(insets);\n } else {\n write(insets);\n }\n };\n host.onLayout(publish);\n publish();\n host.own(() => {\n write?.();\n write = null;\n });\n }\n});\n\nexport interface InsetPaddingArgs {\n /**\n * Padding to keep on each edge in addition to whatever is published\n * there. An edge left out is not written at all, so the element's\n * own padding on it stands.\n */\n readonly top?: number;\n readonly right?: number;\n readonly bottom?: number;\n readonly left?: number;\n}\n\n/**\n * Keeps a scrolling column clear of whatever is floating over it.\n *\n * The reading half of `publishInset`. `insetPadding({ bottom: 40 })`\n * is forty pixels of padding plus however much the bars, the keyboard\n * and the platform's safe area are currently taking at the bottom, and\n * it follows all three as they change.\n *\n * It writes `paddingBottom` and friends, so it composes with an\n * element that also sets `paddingX`: only the edges named here are\n * written.\n *\n * Physical edges rather than logical ones, because these are physical\n * facts. A soft keyboard is at the bottom of the screen in every\n * language, and a notch does not move when the reading does.\n */\nexport const insetPadding = defineModifier<InsetPaddingArgs>({\n name: 'insetPadding',\n attach(host, args) {\n const edges = (['top', 'right', 'bottom', 'left'] as const).filter(edge => args[edge] !== undefined);\n if (edges.length === 0) {\n return;\n }\n const properties: Record<UiInsetEdge, string> = {\n top: 'paddingTop',\n right: 'paddingRight',\n bottom: 'paddingBottom',\n left: 'paddingLeft'\n };\n const apply = (insets: UiInsets): void => {\n for (const edge of edges) {\n host.set(properties[edge], (args[edge] ?? 0) + insets[edge]);\n }\n };\n const source = host.environment(UiEnvironmentKeys.insets);\n let subscription = source.changes.subscribe(apply);\n // The registry is an environment value, so a subtree moved under a\n // different provider reads a different one. Rare, and cheap to be\n // right about: `onEnvironment` is exactly the notification a\n // modifier holding a value out of the environment needs.\n host.onEnvironment(() => {\n subscription.unsubscribe();\n subscription = host.environment(UiEnvironmentKeys.insets).changes.subscribe(apply);\n });\n host.own(() => subscription.unsubscribe());\n }\n});\n","import type { Subject } from 'rxjs';\n\nimport { UiEnvironmentKeys } from '../environment/UiEnvironmentKeys';\nimport { themeExtension, type UiThemeExtension } from '../environment/UiThemeExtension';\nimport { defineModifier, type UiModifier } from './UiModifier';\n\n/**\n * What the modifier needs: which group to read, and where to put it.\n *\n * A plain object, so `sameArgs` compares it structurally and the same\n * extension with the same sink is the same argument. The sink is a\n * class instance and the extension's `equals` is a function, so both\n * compare by identity, which is what makes a cell built once per\n * component instance stable for that instance's life.\n */\nexport interface ThemeTokenTarget<T extends object> {\n readonly extension: UiThemeExtension<T>;\n readonly sink: Subject<T>;\n}\n\n/**\n * Publishes a theme extension's tokens into a cell, and keeps\n * publishing them as the theme above the element changes.\n *\n * This is the answer to the thing `Button`'s docstring has said all\n * along: a component's body runs once, before its node is\n * in a tree, so it cannot read the environment and anything it\n * computes from the theme is frozen at the default. A colour escapes\n * that by being a name resolved at paint, and a radius now does too\n * — but a padding, a gap, a variant's choice of\n * token and an interaction's opacity have nowhere to be resolved late.\n *\n * A modifier does have the environment, so it reads the group there\n * and feeds a cell the component already bound its properties to. The\n * shape is `measure` and `ctx.bounds()`: a cell, and the modifier that\n * fills it.\n *\n * const tokens = themeTokenCell(controlTokens);\n * Box({ modifiers: [tokens.modifier], paddingX: tokens.select(t => t.pad) });\n *\n * The cell is seeded with the extension's declared defaults, so the\n * first read is a real value rather than an empty one, and attach\n * replaces it with whatever the element actually inherits before the\n * first frame is drawn.\n *\n * Unlike `host.set`, this reaches a *child*: the cell is an ordinary\n * Observable, so a component may bind its label's colour to it as\n * easily as its own padding. That matters because most controls are a\n * box with something inside it, and `color` does not cascade from a\n * parent node the way it would in CSS.\n */\nconst themeTokensKind = defineModifier<ThemeTokenTarget<object>>({\n name: 'themeTokens',\n attach(host, target) {\n const publish = (): void => {\n target.sink.next(themeExtension(host.environment(UiEnvironmentKeys.theme), target.extension));\n };\n publish();\n host.onEnvironment(publish);\n }\n});\n\nexport function themeTokens<T extends object>(target: ThemeTokenTarget<T>): UiModifier<ThemeTokenTarget<T>> {\n // The kind is declared over `object` because a modifier kind is one\n // value for every use of it, and an extension's `equals` takes `T`\n // in argument position, so `UiThemeExtension<ControlTokens>` is not\n // assignable to `UiThemeExtension<object>`. The cast is sound in the\n // only direction that runs: `attach` reads the sink and the\n // extension and hands one to the other, never mixing two groups.\n return themeTokensKind(target as unknown as ThemeTokenTarget<object>) as unknown as UiModifier<ThemeTokenTarget<T>>;\n}\n\n/** The kind itself, for a test or an inspector that matches on it. */\nthemeTokens.kind = themeTokensKind.kind;\n","import type { FontMetrics, TextMeasureRequest } from '../../layout/TextMeasurer';\nimport { ParagraphTextMeasurer } from '../../layout/TextMeasurer';\nimport { proportionalFontMetrics } from '../../layout/ParagraphLayout';\nimport type { Canvas2DContext } from './Canvas2DContext';\nimport { applyCanvasTextStyle, createCanvasTextStyle, runCanvasStyleInto } from '../TextRenderer';\n\nconst MAX_CACHE_ENTRIES = 8192;\n\n/**\n * Canvas-backed measurer shared by the layout engine and the\n * renderers, so both always agree on text size.\n *\n * Only the two primitives live here — run width and font metrics —\n * and both are cached by font. Line breaking asks for a run width per\n * candidate line, so the width cache is what keeps wrapping cheap:\n * a stable paragraph re-laid out at the same width costs no\n * measureText calls at all.\n *\n * The cache is a flat map with a crude size cap: when the cap is\n * hit it is cleared wholesale. Revisit with an LRU only if profiling\n * shows measureText dominating.\n */\nexport class CanvasTextMeasurer extends ParagraphTextMeasurer {\n private readonly widths = new Map<string, number>();\n private readonly metrics = new Map<string, FontMetrics>();\n /** One style object per measurer, refilled per call; see `runCanvasStyleInto`. */\n private readonly style = createCanvasTextStyle();\n\n constructor(private readonly context: Canvas2DContext) {\n super();\n }\n\n measureRunWidth(text: string, request: TextMeasureRequest): number {\n // The font is built the one way drawing builds it, and its key is\n // the width cache's. The context is only touched on a miss:\n // assigning `font` re-parses the string, and this is the hottest\n // path in text.\n const key = `${runCanvasStyleInto(request, undefined, this.style)}\\0${text}`;\n const cached = this.widths.get(key);\n if (cached !== undefined) {\n return cached;\n }\n applyCanvasTextStyle(this.context, this.style);\n const width = this.context.measureText(text).width;\n if (this.widths.size >= MAX_CACHE_ENTRIES) {\n this.widths.clear();\n }\n this.widths.set(key, width);\n return width;\n }\n\n /** Drops every cached width, metric and paragraph; see `TextMeasurer.invalidate`. */\n override invalidate(): void {\n super.invalidate();\n this.widths.clear();\n this.metrics.clear();\n }\n\n fontMetrics(request: TextMeasureRequest): FontMetrics {\n // Metrics are the font's own; tracking moves glyphs apart and does\n // not change how tall they are.\n const font = runCanvasStyleInto(request, undefined, this.style);\n this.style.letterSpacing = 0;\n const cached = this.metrics.get(font);\n if (cached !== undefined) {\n return cached;\n }\n applyCanvasTextStyle(this.context, this.style);\n const measured = this.context.measureText('Mg') as Partial<TextMetrics>;\n // fontBoundingBox* is the em box the browser lays lines out with.\n // Test doubles and very old engines lack it; fall back to the\n // proportions the deterministic measurer uses.\n const metrics =\n typeof measured.fontBoundingBoxAscent === 'number' && typeof measured.fontBoundingBoxDescent === 'number'\n ? { ascent: measured.fontBoundingBoxAscent, descent: measured.fontBoundingBoxDescent }\n : proportionalFontMetrics(request.fontSize);\n this.metrics.set(font, metrics);\n return metrics;\n }\n}\n","import type { UiNode } from '../graph/UiNode';\nimport type { LayoutEngine } from '../layout/LayoutEngine';\nimport { formatExplanation, formatNumber, labelNode } from '../layout/LayoutExplanation';\nimport type { LayoutExplanation } from '../layout/LayoutExplanation';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { Canvas2DContext } from './canvas2d/Canvas2DContext';\nimport { drawOverlayShapes, type OverlayShape } from './OverlayShapes';\n\n/** How long a measured node stays warm in the heatmap. */\nexport const INSPECTOR_HEAT_MS = 1500;\n/** For this long after being measured a node is drawn hot; then warm until it expires. */\nexport const INSPECTOR_HOT_MS = 500;\n\nconst HEAT_HOT_FILL = 'rgba(229, 83, 75, 0.35)';\nconst HEAT_WARM_FILL = 'rgba(229, 83, 75, 0.14)';\nconst HEAT_HOT_STROKE = 'rgba(229, 83, 75, 0.9)';\nconst HEAT_WARM_STROKE = 'rgba(229, 83, 75, 0.45)';\n\nconst HOVER_STROKE = 'rgba(76, 141, 255, 0.95)';\nconst CONTENT_FILL = 'rgba(76, 141, 255, 0.18)';\nconst PADDING_FILL = 'rgba(46, 168, 138, 0.35)';\nconst MARGIN_FILL = 'rgba(217, 155, 58, 0.3)';\nconst RELAYOUT_STROKE = 'rgba(168, 85, 247, 0.9)';\nconst DECORATION_STROKE = 'rgba(46, 168, 138, 0.95)';\nconst LABEL_FILL = 'rgba(13, 17, 23, 0.92)';\nconst LABEL_TEXT = '#e6edf3';\nconst LABEL_FONT_SIZE = 10;\nconst LABEL_FONT_FAMILY = 'ui-monospace, SFMono-Regular, Menlo, monospace';\nconst LABEL_FONT = `${LABEL_FONT_SIZE}px ${LABEL_FONT_FAMILY}`;\nconst LABEL_HEIGHT = 16;\n\n/** The overlay for one frame: its shapes, and when the heatmap next changes. */\nexport interface InspectorOverlay {\n shapes: OverlayShape[];\n /** Milliseconds until a node cools or expires, or undefined when nothing is warm. */\n nextChange: number | undefined;\n}\n\n/**\n * The layout inspector: what a developer sees when they\n * hover a node with inspection on.\n *\n * It keeps two things and paints them over a finished frame, on the\n * same context the scene was drawn to, so it works identically on the\n * main thread and inside a render worker:\n *\n * - the hovered node's margin, border, padding and content boxes,\n * with a label, and the outline of the relayout root a change to\n * it would be laid out from;\n * - a heatmap of the nodes the recent layout passes measured — hot\n * for `INSPECTOR_HOT_MS`, warm until `INSPECTOR_HEAT_MS` — which is\n * the frame-by-frame picture of what the budgets in\n * `LayoutEngine.budget.spec` count. Leaves are filled; containers\n * are only outlined, because a container's measure runs whenever\n * anything inside it changes (a page's scroller is measured on\n * every tick of an animated child) and filling it would tint the\n * whole canvas and hide the leaves that actually changed.\n *\n * Heat changes in two steps rather than a continuous fade: a fade needs\n * a repaint every few tens of milliseconds while anything is warm, and\n * a repeatedly re-measured node then pulses as its alpha decays and\n * snaps back — over a large box that reads as flicker. With steps, a\n * repaint is needed only when some node's step is due, and `paint`\n * reports when that is.\n *\n * Enabling it turns on `LayoutEngine.trace`, which is what fills the\n * list of measured nodes; disabling it turns the trace back off so a\n * running app pays nothing for the inspector it is not using.\n *\n * The inspector reads records and never computes geometry: boxes come\n * from `visibleBox`, clipped by the scroll containers above the node,\n * so a heatmap row that scrolled out of its list is not drawn over the\n * content below it.\n */\nexport interface LayoutInspectorOptions {\n /**\n * The modifiers attached to a node, in list order, so the label can\n * name them.\n *\n * Supplied rather than read, because a node has no backpointer to\n * its modifiers: the set lives in the builder, keyed by node, and\n * `packages/core/src/rendering` must not depend on the builder to\n * paint a debugging label. The runtime passes\n * `node => builder.modifiersFor(node)?.names ?? []`; a caller that\n * does not pass it gets an inspector that says nothing about\n * modifiers, which is what it said before B5.\n */\n readonly modifierNames?: (node: UiNode) => readonly string[];\n}\n\nexport class LayoutInspector {\n private enabled = false;\n private hovered: UiNode | null = null;\n /**\n * A node picked from outside the canvas — a devtools panel's tree —\n * and outlined here so a person can find it on the screen.\n *\n * Separate from `hovered` and from `enabled`: the panel points at a\n * node without the pointer being anywhere near it, and without the\n * heatmap and the measure trace that the toggle turns on.\n */\n private highlighted: UiNode | null = null;\n /** When each node was last measured, for the heatmap. */\n private readonly heat = new Map<UiNode, number>();\n\n constructor(\n private readonly engine: LayoutEngine,\n private readonly options: LayoutInspectorOptions = {}\n ) {}\n\n get isEnabled(): boolean {\n return this.enabled;\n }\n\n get hoveredNode(): UiNode | null {\n return this.hovered;\n }\n\n get highlightedNode(): UiNode | null {\n return this.highlighted;\n }\n\n /** Whether `overlay` would draw anything: the toggle is on, or a node is highlighted. */\n get hasOverlay(): boolean {\n return this.enabled || this.highlighted !== null;\n }\n\n /** Turns the inspector, and the engine's measure trace, on or off. */\n setEnabled(enabled: boolean): void {\n if (this.enabled === enabled) {\n return;\n }\n this.enabled = enabled;\n this.engine.trace = enabled;\n if (!enabled) {\n this.heat.clear();\n this.hovered = null;\n }\n }\n\n /** Sets the node under the pointer. Returns true when it changed. */\n setHovered(node: UiNode | null): boolean {\n if (node === this.hovered) {\n return false;\n }\n this.hovered = node;\n return true;\n }\n\n /** Sets the node a panel is pointing at. Returns true when it changed. */\n setHighlighted(node: UiNode | null): boolean {\n if (node === this.highlighted) {\n return false;\n }\n this.highlighted = node;\n return true;\n }\n\n /**\n * Records the nodes the layout pass that just ran measured. Call once\n * after each layout, with the frame's time.\n */\n recordLayout(now: number): void {\n if (!this.enabled) {\n return;\n }\n for (const node of this.engine.stats.measuredNodes) {\n this.heat.set(node, now);\n }\n }\n\n /** Nodes currently warm in the heatmap. */\n get heatCount(): number {\n return this.heat.size;\n }\n\n /** The hovered node's explanation, or null with nothing hovered. */\n explainHovered(): LayoutExplanation | null {\n return this.hovered === null ? null : this.engine.explain(this.hovered);\n }\n\n /** The hovered node's explanation as text, or null with nothing hovered. */\n explainHoveredText(): string | null {\n const explanation = this.explainHovered();\n return explanation === null ? null : formatExplanation(explanation);\n }\n\n /**\n * The overlay for a finished frame, as shapes either renderer draws\n * after the scene. Returns them with how many milliseconds until the\n * heatmap next changes (a node cooling from hot to warm, or\n * expiring), or undefined when nothing is warm.\n */\n overlay(now: number): InspectorOverlay {\n const shapes: OverlayShape[] = [];\n if (!this.hasOverlay) {\n return { shapes, nextChange: undefined };\n }\n const nextChange = this.enabled ? this.heatShapes(shapes, now) : undefined;\n // The highlighted node is drawn exactly as a hovered one is: the\n // box model is what a person picking a node from a tree wants to\n // see, and a second vocabulary for the same boxes would be one more\n // thing to learn. Once when they coincide.\n if (this.highlighted !== null) {\n this.hoveredShapes(shapes, this.highlighted);\n }\n if (this.enabled && this.hovered !== null && this.hovered !== this.highlighted) {\n this.hoveredShapes(shapes, this.hovered);\n }\n return { shapes, nextChange };\n }\n\n /**\n * Paints the overlay onto a 2D context left in logical pixels, as the\n * Canvas2D renderer leaves it. Returns what `overlay` reports as the\n * next change.\n */\n paint(ctx: Canvas2DContext, now: number): number | undefined {\n const { shapes, nextChange } = this.overlay(now);\n drawOverlayShapes(ctx, shapes);\n return nextChange;\n }\n\n private heatShapes(out: OverlayShape[], now: number): number | undefined {\n let nextChange: number | undefined;\n for (const [node, at] of this.heat) {\n const age = now - at;\n if (age >= INSPECTOR_HEAT_MS) {\n this.heat.delete(node);\n continue;\n }\n const hot = age < INSPECTOR_HOT_MS;\n const due = (hot ? INSPECTOR_HOT_MS : INSPECTOR_HEAT_MS) - age;\n nextChange = nextChange === undefined ? due : Math.min(nextChange, due);\n const box = this.clippedVisibleBox(node);\n if (box === null) {\n continue;\n }\n if (!node.hasChildren()) {\n out.push({ kind: 'fill', ...box, color: hot ? HEAT_HOT_FILL : HEAT_WARM_FILL });\n }\n out.push({ kind: 'stroke', ...box, color: hot ? HEAT_HOT_STROKE : HEAT_WARM_STROKE, lineWidth: 1 });\n }\n return nextChange;\n }\n\n private hoveredShapes(out: OverlayShape[], node: UiNode): void {\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n return;\n }\n const explanation = this.engine.explain(node);\n const relayoutRoot = explanation.relayout.root;\n if (relayoutRoot !== node) {\n const rootBox = this.clippedVisibleBox(relayoutRoot);\n if (rootBox !== null) {\n out.push({ kind: 'stroke', ...rootBox, color: RELAYOUT_STROKE, lineWidth: 2 });\n }\n }\n\n const box = this.engine.visibleBox(node);\n const strip = (x: number, y: number, width: number, height: number, color: string): void => {\n if (width > 0 && height > 0) {\n out.push({ kind: 'fill', x, y, width, height, color });\n }\n };\n // Margin strips around the border box.\n const marginWidth = box.width + rec.marginLeft + rec.marginRight;\n strip(box.x - rec.marginLeft, box.y - rec.marginTop, marginWidth, rec.marginTop, MARGIN_FILL);\n strip(box.x - rec.marginLeft, box.y + box.height, marginWidth, rec.marginBottom, MARGIN_FILL);\n strip(box.x - rec.marginLeft, box.y, rec.marginLeft, box.height, MARGIN_FILL);\n strip(box.x + box.width, box.y, rec.marginRight, box.height, MARGIN_FILL);\n // Padding strips inside it.\n const innerHeight = box.height - rec.paddingTop - rec.paddingBottom;\n strip(box.x, box.y, box.width, rec.paddingTop, PADDING_FILL);\n strip(box.x, box.y + box.height - rec.paddingBottom, box.width, rec.paddingBottom, PADDING_FILL);\n strip(box.x, box.y + rec.paddingTop, rec.paddingLeft, innerHeight, PADDING_FILL);\n strip(box.x + box.width - rec.paddingRight, box.y + rec.paddingTop, rec.paddingRight, innerHeight, PADDING_FILL);\n // Content box.\n strip(\n box.x + rec.paddingLeft,\n box.y + rec.paddingTop,\n box.width - rec.paddingLeft - rec.paddingRight,\n innerHeight,\n CONTENT_FILL\n );\n // Border box outline, drawn even for a zero-sized node so it can be found.\n out.push({ kind: 'stroke', ...box, color: HOVER_STROKE, lineWidth: 1 });\n // Decorations in their own colour, because the whole point of one\n // is that it is not the box: a focus ring sits outside the border\n // box and would otherwise read as the node being bigger than it is.\n for (const shape of node.decorations ?? []) {\n const outset = shape.outset ?? 0;\n out.push({\n kind: 'stroke',\n x: box.x + (shape.x ?? 0) - outset,\n y: box.y + (shape.y ?? 0) - outset,\n width: (shape.width ?? box.width) + outset * 2,\n height: (shape.height ?? box.height) + outset * 2,\n color: DECORATION_STROKE,\n lineWidth: 1\n });\n }\n\n const modifiers = this.options.modifierNames?.(node) ?? [];\n out.push({\n kind: 'label',\n box,\n text: `${labelNode(node)} ${formatNumber(box.width)}×${formatNumber(box.height)}${\n modifiers.length === 0 ? '' : ` · ${modifiers.join(', ')}`\n }`,\n font: LABEL_FONT,\n fontSize: LABEL_FONT_SIZE,\n fontFamily: LABEL_FONT_FAMILY,\n textColor: LABEL_TEXT,\n background: LABEL_FILL,\n height: LABEL_HEIGHT\n });\n }\n\n /**\n * Where a node is seen, intersected with every clipping ancestor's\n * visible box; null when nothing of it is visible.\n */\n private clippedVisibleBox(node: UiNode): LayoutBox | null {\n const box = this.engine.visibleBox(node);\n let x = box.x;\n let y = box.y;\n let right = box.x + box.width;\n let bottom = box.y + box.height;\n let clipped = false;\n for (let current = node.parent; current !== null; current = current.parent) {\n const rec = this.engine.recordFor(current);\n if (rec === undefined || !rec.clips) {\n continue;\n }\n const clip = this.engine.visibleBox(current);\n x = Math.max(x, clip.x);\n y = Math.max(y, clip.y);\n right = Math.min(right, clip.x + clip.width);\n bottom = Math.min(bottom, clip.y + clip.height);\n clipped = true;\n if (right <= x || bottom <= y) {\n return null;\n }\n }\n // An unclipped box is returned as is, so its sizes are the record's\n // exactly rather than a sum and a difference of them.\n return clipped ? { x, y, width: right - x, height: bottom - y } : box;\n }\n}\n","import type { UiImage } from '../properties/UiImage';\n\n/**\n * Turns a source into something a renderer can draw.\n *\n * `UiImage` is `ImageBitmap`, which both backends already take — the\n * Canvas2D renderer draws it and the WebGPU one uploads it through\n * `WebGPUTextureCache` — so the Media tier adds no renderer work at\n * all. What was missing was everything in front of it: fetching,\n * decoding, not decoding the same thing twice, and releasing what is\n * no longer on screen.\n *\n * An implementation is free to fetch and decode wherever it likes; the\n * default one below does it wherever it is constructed, which for a\n * Gesso app is the render worker.\n */\nexport interface ImageResolver {\n /**\n * The decoded bitmap for a source, or a rejection.\n *\n * Calling twice with the same source must return the same bitmap\n * rather than decode twice, and two calls that overlap in time must\n * share one fetch.\n */\n resolve(source: string): Promise<UiImage>;\n /**\n * The bitmap for a source that is already decoded, held exactly as\n * `resolve` would hold it, or null when it is not decoded yet, in\n * which case nothing is held and the caller resolves as usual.\n *\n * Optional, because a resolver that decodes elsewhere may have no\n * synchronous answer. It exists for the frame that builds a node: a\n * promise settles a microtask after that frame has painted, so\n * without this a picture the cache already holds is drawn as its\n * placeholder for one frame, which under a shared-element morph is a\n * blank tile where the picture was a frame ago.\n */\n peek?(source: string): UiImage | null;\n /**\n * Says the caller no longer needs the source. The bitmap may be kept\n * (it is likely to be wanted again) but a resolver that closes\n * bitmaps must not close one another caller still holds.\n */\n release(source: string): void;\n /** Drops everything, for a runtime shutting down. */\n dispose(): void;\n}\n\nexport interface DefaultImageResolverOptions {\n /**\n * How many decoded bitmaps to keep after their last holder released\n * them. The live ones — those something on screen is still holding —\n * are never evicted, whatever this says.\n */\n capacity?: number;\n /** Injectable for specs, and for an app that fetches through its own stack. */\n fetch?: (source: string) => Promise<Blob>;\n /** Injectable for specs; the platform's `createImageBitmap` by default. */\n decode?: (blob: Blob) => Promise<UiImage>;\n}\n\ninterface Entry {\n readonly source: string;\n /** In flight, or settled. One per source, which is the de-duplication. */\n readonly bitmap: Promise<UiImage>;\n /** How many callers are holding it. Zero means it is evictable. */\n holders: number;\n /** Set once the promise settles, so eviction can close the bitmap. */\n settled: UiImage | null;\n}\n\nconst DEFAULT_CAPACITY = 32;\n\n/**\n * Fetch, decode, cache, evict.\n *\n * **Where the decode happens.** The decode belongs in a worker with\n * the bitmap transferred back. This resolver does not spawn one,\n * for two reasons that are worth stating rather than discovering\n * later. A Gesso runtime normally *is* a worker — the render worker of\n * `WorkerApp` — so a resolver constructed by it already fetches and\n * decodes off the main thread, and the bitmap never crosses a thread\n * at all, which is strictly better than transferring one. And\n * `createImageBitmap` is specified to decode in parallel, so even on a\n * main-thread runtime the decode is not on the main thread; only the\n * fetch bookkeeping is. A dedicated decode worker would therefore add\n * a nested worker to buy back something the platform already gives.\n * The seam is still open: `ImageStore.setResolver` takes any\n * `ImageResolver`, so an app that measures a reason for one can supply\n * it without touching a component.\n *\n * **What it cannot decode.** `createImageBitmap` takes a `Blob`, and\n * Chrome refuses an SVG one — \"The source image could not be decoded\".\n * So an `Image` pointed at an `.svg` fails on a browser where the same\n * file in an `<img>` would work, and a vector glyph belongs in `Icon`,\n * which draws a path rather than decoding a document. Found by\n * pointing the playground's Media card at an SVG data URL and getting\n * a placeholder.\n *\n * **Eviction.** Reference counting first, LRU second. A bitmap\n * something on screen is holding is never evicted, however long the\n * queue is; a released one stays cached until the capacity pushes it\n * out, so scrolling a list back and forth does not re-fetch. Evicting\n * closes the bitmap, because an `ImageBitmap` holds decoded pixels and\n * garbage collection is not prompt about them.\n */\nexport class DefaultImageResolver implements ImageResolver {\n private readonly entries = new Map<string, Entry>();\n /** Sources with no holders, least recently released first. */\n private readonly evictable: string[] = [];\n private readonly capacity: number;\n private readonly fetchBlob: (source: string) => Promise<Blob>;\n private readonly decodeBlob: (blob: Blob) => Promise<UiImage>;\n private disposed = false;\n\n constructor(options: DefaultImageResolverOptions = {}) {\n this.capacity = options.capacity ?? DEFAULT_CAPACITY;\n this.fetchBlob = options.fetch ?? defaultFetch;\n this.decodeBlob = options.decode ?? defaultDecode;\n }\n\n /** Decoded or in-flight sources, for specs and the inspector. */\n get size(): number {\n return this.entries.size;\n }\n\n resolve(source: string): Promise<UiImage> {\n if (this.disposed) {\n return Promise.reject(new Error('The image resolver has been disposed.'));\n }\n const existing = this.entries.get(source);\n if (existing !== undefined) {\n existing.holders++;\n const index = this.evictable.indexOf(source);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n return existing.bitmap;\n }\n const entry: Entry = {\n source,\n holders: 1,\n settled: null,\n bitmap: this.fetchBlob(source)\n .then(blob => this.decodeBlob(blob))\n .then(bitmap => {\n // Released while it was in flight, and evicted since: the\n // entry is gone, so nothing will ever close this one.\n if (this.entries.get(source) !== entry) {\n bitmap.close?.();\n throw new Error(`The image '${source}' was released before it finished decoding.`);\n }\n entry.settled = bitmap;\n return bitmap;\n })\n .catch((error: unknown) => {\n // A failed fetch is not cached: the next caller should try\n // again rather than inherit a rejection forever.\n if (this.entries.get(source) === entry) {\n this.entries.delete(source);\n const index = this.evictable.indexOf(source);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n }\n throw error;\n })\n };\n // Nothing else awaits `entry.bitmap`, so a rejection with no\n // consumer would be an unhandled rejection in the worker.\n entry.bitmap.catch(() => {});\n this.entries.set(source, entry);\n return entry.bitmap;\n }\n\n peek(source: string): UiImage | null {\n if (this.disposed) {\n return null;\n }\n const existing = this.entries.get(source);\n if (existing === undefined || existing.settled === null) {\n return null;\n }\n // A hold, exactly as `resolve` takes one, so the caller's `release`\n // balances it and the bitmap stays live while it is on screen.\n existing.holders++;\n const index = this.evictable.indexOf(source);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n return existing.settled;\n }\n\n release(source: string): void {\n const entry = this.entries.get(source);\n if (entry === undefined || entry.holders === 0) {\n return;\n }\n entry.holders--;\n if (entry.holders > 0) {\n return;\n }\n this.evictable.push(source);\n while (this.evictable.length > this.capacity) {\n const oldest = this.evictable.shift()!;\n const stale = this.entries.get(oldest);\n if (stale === undefined || stale.holders > 0) {\n continue;\n }\n this.entries.delete(oldest);\n stale.settled?.close?.();\n }\n }\n\n dispose(): void {\n this.disposed = true;\n for (const entry of this.entries.values()) {\n entry.settled?.close?.();\n }\n this.entries.clear();\n this.evictable.length = 0;\n }\n}\n\nasync function defaultFetch(source: string): Promise<Blob> {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Fetching the image '${source}' failed with ${response.status} ${response.statusText}.`);\n }\n return response.blob();\n}\n\nfunction defaultDecode(blob: Blob): Promise<UiImage> {\n if (typeof createImageBitmap !== 'function') {\n return Promise.reject(new Error('createImageBitmap is unavailable, so images cannot be decoded here.'));\n }\n return createImageBitmap(blob);\n}\n","import type { UiColor } from '../properties/UiColor';\nimport type { UiImage } from '../properties/UiImage';\nimport { colorToCss } from './PaintState';\n\n/** What an icon is: a path in a square viewBox, at a size, in a colour. */\nexport interface IconSpec {\n /** SVG path data, in the coordinates of `viewBox`. */\n readonly path: string;\n /** The side of the square the path is drawn in. 24 is the usual icon grid. */\n readonly viewBox: number;\n /** The side of the box the icon occupies, in logical pixels. */\n readonly size: number;\n readonly color: UiColor;\n /** `'fill'` for a solid glyph, `'stroke'` for a line one. */\n readonly style: 'fill' | 'stroke';\n /** Line width for a stroked icon, in viewBox units. */\n readonly strokeWidth: number;\n /**\n * Which points a filled path encloses: `'nonzero'` (the canvas\n * default) or `'evenodd'`.\n *\n * It matters for any glyph with a hole in it — a clock face, a\n * circle-and-slash, a downward arrow inside a cloud — because a\n * subpath wound the same way as its container does not punch a hole\n * under `nonzero` and does under `evenodd`. Icon sets author for one\n * or the other and say which in the SVG's `fill-rule`; Heroicons'\n * solid set says `evenodd`, and a Gesso `Icon` given that path\n * without this renders a filled blob.\n */\n readonly fillRule?: 'nonzero' | 'evenodd';\n}\n\n/**\n * Draws an icon path into a bitmap, once per distinct icon.\n *\n * The icon atlas comes later; the first cut rasterises per icon, replaced later without an API change.\n * This is that first cut, and the API it fixes is `IconSpec` in and a\n * `UiImage` out — which is what an atlas would also produce, with a\n * sub-rectangle of one texture instead of a bitmap of its own.\n *\n * **What this costs today, so the atlas has a number to beat:** one\n * `OffscreenCanvas` and one `createImageBitmap` per distinct\n * (path, size, colour, style) — so a toolbar of ten icons in one\n * colour is ten small textures where an atlas would be one, and the\n * same icon in a hover colour is a second entry. Nothing re-rasterises\n * while the spec is unchanged, and a theme change re-rasterises only\n * the icons on screen.\n */\nexport interface IconRasterizerOptions {\n /**\n * Physical pixels per logical pixel to rasterise at. Two is crisp on\n * an ordinary and on a retina display and is what the runtime passes\n * when it does not know better; an atlas will take the real one.\n */\n scale?: number;\n /** How many released rasters to keep. */\n capacity?: number;\n /** Injectable for specs and for a host without `OffscreenCanvas`. */\n createCanvas?: (width: number, height: number) => IconCanvas;\n}\n\n/** The little that rasterising a path needs, so a spec can stand in for it. */\nexport interface IconCanvas {\n getContext2D(): IconContext | null;\n toBitmap(): Promise<UiImage>;\n}\n\nexport interface IconContext {\n scale(x: number, y: number): void;\n fillStyle: string;\n strokeStyle: string;\n lineWidth: number;\n lineCap: CanvasLineCap;\n lineJoin: CanvasLineJoin;\n fill(path: Path2D, fillRule?: 'nonzero' | 'evenodd'): void;\n stroke(path: Path2D): void;\n}\n\ninterface Raster {\n readonly key: string;\n readonly bitmap: Promise<UiImage>;\n holders: number;\n settled: UiImage | null;\n}\n\nconst DEFAULT_SCALE = 2;\nconst DEFAULT_CAPACITY = 64;\n\nexport function iconKey(spec: IconSpec): string {\n const { r, g, b, a } = spec.color;\n return `${spec.size}|${spec.viewBox}|${spec.style}|${spec.strokeWidth}|${spec.fillRule ?? 'nonzero'}|${r},${g},${b},${a}|${spec.path}`;\n}\n\nexport class IconRasterizer {\n private readonly rasters = new Map<string, Raster>();\n private readonly evictable: string[] = [];\n private readonly scale: number;\n private readonly capacity: number;\n private readonly createCanvas: (width: number, height: number) => IconCanvas;\n\n constructor(options: IconRasterizerOptions = {}) {\n this.scale = options.scale ?? DEFAULT_SCALE;\n this.capacity = options.capacity ?? DEFAULT_CAPACITY;\n this.createCanvas = options.createCanvas ?? offscreenCanvas;\n }\n\n get size(): number {\n return this.rasters.size;\n }\n\n raster(spec: IconSpec): Promise<UiImage> {\n const key = iconKey(spec);\n const existing = this.rasters.get(key);\n if (existing !== undefined) {\n existing.holders++;\n const index = this.evictable.indexOf(key);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n return existing.bitmap;\n }\n const raster: Raster = {\n key,\n holders: 1,\n settled: null,\n bitmap: this.draw(spec).then(bitmap => {\n if (this.rasters.get(key) !== raster) {\n bitmap.close?.();\n throw new Error('The icon was released before it finished rasterising.');\n }\n raster.settled = bitmap;\n return bitmap;\n })\n };\n raster.bitmap.catch(() => {});\n this.rasters.set(key, raster);\n return raster.bitmap;\n }\n\n release(spec: IconSpec): void {\n const key = iconKey(spec);\n const raster = this.rasters.get(key);\n if (raster === undefined || raster.holders === 0) {\n return;\n }\n raster.holders--;\n if (raster.holders > 0) {\n return;\n }\n this.evictable.push(key);\n while (this.evictable.length > this.capacity) {\n const oldest = this.evictable.shift()!;\n const stale = this.rasters.get(oldest);\n if (stale === undefined || stale.holders > 0) {\n continue;\n }\n this.rasters.delete(oldest);\n stale.settled?.close?.();\n }\n }\n\n dispose(): void {\n for (const raster of this.rasters.values()) {\n raster.settled?.close?.();\n }\n this.rasters.clear();\n this.evictable.length = 0;\n }\n\n private async draw(spec: IconSpec): Promise<UiImage> {\n const side = Math.max(1, Math.round(spec.size * this.scale));\n const canvas = this.createCanvas(side, side);\n const ctx = canvas.getContext2D();\n if (ctx === null) {\n throw new Error('An icon could not be rasterised: no 2D context.');\n }\n // The path is authored in viewBox units; one transform puts it in\n // the physical pixels of the bitmap, so nothing else has to scale.\n const unit = side / spec.viewBox;\n ctx.scale(unit, unit);\n const path = new Path2D(spec.path);\n if (spec.style === 'stroke') {\n ctx.strokeStyle = colorToCss(spec.color);\n ctx.lineWidth = spec.strokeWidth;\n ctx.lineCap = 'round';\n ctx.lineJoin = 'round';\n ctx.stroke(path);\n } else {\n ctx.fillStyle = colorToCss(spec.color);\n ctx.fill(path, spec.fillRule ?? 'nonzero');\n }\n return canvas.toBitmap();\n }\n}\n\n/**\n * `OffscreenCanvas` is available on the main thread and in a worker,\n * which is what lets an icon be rasterised wherever the runtime lives —\n * the same property that lets `UiImage` be an `ImageBitmap`.\n */\nfunction offscreenCanvas(width: number, height: number): IconCanvas {\n if (typeof OffscreenCanvas !== 'function') {\n throw new Error('OffscreenCanvas is unavailable, so icons cannot be rasterised here.');\n }\n const canvas = new OffscreenCanvas(width, height);\n return {\n getContext2D: () => canvas.getContext('2d') as unknown as IconContext | null,\n toBitmap: () => createImageBitmap(canvas)\n };\n}\n","/**\n * Base error for WebGPU renderer failures.\n *\n * Carries a `phase` tag so callers can distinguish initialization,\n * resize, shader compilation, pipeline creation, and device loss.\n */\nexport class WebGPUError extends Error {\n constructor(\n message: string,\n public readonly phase:\n | 'unavailable'\n | 'adapter'\n | 'device'\n | 'context'\n | 'configure'\n | 'shader'\n | 'pipeline'\n | 'render'\n | 'lost'\n | 'disposed',\n public readonly cause?: unknown\n ) {\n super(message);\n this.name = 'WebGPUError';\n }\n}\n\n/**\n * Reports that the WebGPU renderer is not usable on this client.\n */\nexport function throwWebGPUUnavailable(reason: string): never {\n throw new WebGPUError(`WebGPU is unavailable: ${reason}`, 'unavailable');\n}\n","import { WebGPUError } from './WebGPUError';\n\nexport interface WebGPUDeviceInit {\n adapter: GPUAdapter;\n device: GPUDevice;\n format: GPUTextureFormat;\n}\n\n/**\n * Whether this thread has a WebGPU entry point at all.\n *\n * A synchronous answer, deliberately: it is what lets a runtime asked\n * for `auto` settle on Canvas2D during its constructor rather than\n * after a rejected promise, so a browser that never shipped WebGPU\n * draws its first frame with no blank period and no async state. It\n * says nothing about whether an adapter can actually be had, which\n * only `initializeWebGPU` can find out.\n */\nexport function isWebGPUAvailable(): boolean {\n return typeof navigator !== 'undefined' && navigator.gpu !== undefined;\n}\n\n/**\n * Acquires a WebGPU adapter and device.\n *\n * Fails cleanly with descriptive errors when navigator.gpu, the\n * adapter, or the device request is unavailable. The returned device\n * has a loss listener attached; callers should check `isDeviceLost`\n * or subscribe via `onDeviceLost` before rendering.\n */\nexport async function initializeWebGPU(): Promise<WebGPUDeviceInit> {\n if (typeof navigator === 'undefined' || navigator.gpu === undefined) {\n throw new WebGPUError('navigator.gpu is not present.', 'unavailable');\n }\n\n const adapter = await navigator.gpu.requestAdapter();\n if (adapter === null) {\n throw new WebGPUError('No WebGPU adapter found.', 'adapter');\n }\n\n const device = await adapter.requestDevice();\n device.lost.then(info => {\n onDeviceLostListeners.forEach(fn => fn(info));\n });\n\n const format = navigator.gpu.getPreferredCanvasFormat();\n return { adapter, device, format };\n}\n\nconst onDeviceLostListeners = new Set<(info: GPUDeviceLostInfo) => void>();\n\n/**\n * Subscribes to device loss events from any device created through\n * `initializeWebGPU`. Used by the renderer to enter a controlled\n * lost state.\n */\nexport function onDeviceLost(listener: (info: GPUDeviceLostInfo) => void): () => void {\n onDeviceLostListeners.add(listener);\n return () => {\n onDeviceLostListeners.delete(listener);\n };\n}\n","/**\n * WGSL sources for the two WebGPU pipelines.\n *\n * Both draw one unit quad per instance. An instance carries the\n * rectangle it covers in absolute layout coordinates and the affine\n * transform that maps those coordinates to logical screen pixels, so\n * every instance under one node shares the node's transform and the\n * rectangle is honest — a scrollbar thumb sits where its geometry says.\n *\n * Clipping is two-layered. The rectangular part is a scissor set per\n * draw command. The rounded part is a **clip chain**: a storage buffer\n * of clipping boxes with corner radii, each holding its parent's index\n * and the inverse of its own transform. An instance names its\n * innermost rounded clip; the fragment shader carries the pixel back\n * into each clip's space up the chain and multiplies their coverages,\n * so every rounded ancestor applies.\n *\n * Edges are anti-aliased by coverage rather than cut by `discard`: a\n * pixel's alpha is scaled by how far its centre sits inside the shape,\n * in physical pixels. A box on integer coordinates is still crisp —\n * every pixel centre is half a pixel from its edge — and rounded\n * corners match the anti-aliased paths Canvas2D draws.\n */\n\n/** Shared uniforms: logical viewport width and height, device pixel ratio. */\nexport const VIEW_UNIFORM_FLOATS = 4;\n\n/** Deepest rounded-clip nesting the shader will walk. */\nexport const MAX_CLIP_DEPTH = 16;\n\nconst SHARED = /* wgsl */ `\n// x: logical width, y: logical height, z: device pixel ratio.\n@group(0) @binding(0) var<uniform> uView: vec4f;\n\n// The frame's clip chain, four vec4s per node; see CLIP_STRIDE_FLOATS.\n@group(1) @binding(0) var<storage, read> uClips: array<vec4f>;\n\n// The frame's gradients, twelve vec4s each; see GRADIENT_STRIDE_FLOATS.\n// Declared in both pipelines so group 1 reads the same either way; only\n// the primitive pipeline samples it.\n@group(1) @binding(1) var<storage, read> uGradients: array<vec4f>;\n\n// Signed distance from a point to a rounded rectangle, negative inside.\nfn roundedRectDistance(point: vec2f, origin: vec2f, size: vec2f, radius: f32) -> f32 {\n let half = size * 0.5;\n let r = min(radius, min(half.x, half.y));\n let q = abs(point - (origin + half)) - (half - vec2f(r, r));\n return length(max(q, vec2f(0.0))) + min(max(q.x, q.y), 0.0) - r;\n}\n\n// How much of a pixel lies inside a shape, from the signed distance of\n// its centre in logical pixels.\nfn coverage(signedDistance: f32, dpr: f32) -> f32 {\n return clamp(0.5 - signedDistance * dpr, 0.0, 1.0);\n}\n\n// Coverage of the fragment at framebuffer position fragPx under every\n// rounded clip from clipIndex up to the root; 1.0 when unclipped.\nfn clipCoverage(fragPx: vec2f, dpr: f32, clipIndex: f32) -> f32 {\n var result = 1.0;\n var index = i32(clipIndex);\n var depth = 0;\n let screen = fragPx / dpr;\n loop {\n if (index < 0 || depth >= ${MAX_CLIP_DEPTH}) {\n break;\n }\n let base = u32(index) * 4u;\n let rect = uClips[base];\n let header = uClips[base + 1u];\n let inverse = uClips[base + 2u];\n let translation = uClips[base + 3u];\n let local = vec2f(\n screen.x * inverse.x + screen.y * inverse.z + translation.x,\n screen.x * inverse.y + screen.y * inverse.w + translation.y\n );\n result = min(result, coverage(roundedRectDistance(local, rect.xy, rect.zw, header.x), dpr));\n index = i32(header.y);\n depth = depth + 1;\n }\n return result;\n}\n\nfn toClipSpace(transformed: vec2f) -> vec4f {\n return vec4f((transformed.x / uView.x) * 2.0 - 1.0, 1.0 - (transformed.y / uView.y) * 2.0, 0.0, 1.0);\n}\n`;\n\nexport const PRIMITIVE_SHADER = /* wgsl */ `\n${SHARED}\n\n// Twelve vec4s per gradient; colours start at 2 and offsets at 10.\nconst GRADIENT_STRIDE = 12u;\nconst GRADIENT_COLORS = 2u;\nconst GRADIENT_OFFSETS = 10u;\n\n// One stop's position. The lane is picked with comparisons rather than\n// by indexing the vec4 with a runtime value, which is legal WGSL but\n// not worth depending on for four branches the compiler folds anyway.\nfn gradientStopOffset(base: u32, index: u32) -> f32 {\n let row = uGradients[base + GRADIENT_OFFSETS + (index >> 2u)];\n let lane = index & 3u;\n if (lane == 0u) {\n return row.x;\n }\n if (lane == 1u) {\n return row.y;\n }\n if (lane == 2u) {\n return row.z;\n }\n return row.w;\n}\n\nfn premultiply(color: vec4f) -> vec4f {\n return vec4f(color.rgb * color.a, color.a);\n}\n\nfn unpremultiply(color: vec4f) -> vec4f {\n if (color.a <= 0.0) {\n return vec4f(0.0, 0.0, 0.0, 0.0);\n }\n return vec4f(color.rgb / color.a, color.a);\n}\n\n// The gradient's colour at a point in the instance's own coordinates.\n//\n// Stops are interpolated with premultiplied alpha, which is what the\n// canvas specification says a CanvasGradient does; for the opaque\n// stops most gradients have it is the same answer either way, and for\n// a stop that fades out it is the difference between a ramp that goes\n// through grey and one that does not.\nfn gradientColor(index: u32, local: vec2f) -> vec4f {\n let base = index * GRADIENT_STRIDE;\n let header = uGradients[base];\n let geometry = uGradients[base + 1u];\n var t = 0.0;\n if (header.x < 0.5) {\n // Linear: how far along the gradient line the point projects.\n let line = geometry.zw - geometry.xy;\n let lengthSquared = dot(line, line);\n if (lengthSquared > 0.0) {\n t = dot(local - geometry.xy, line) / lengthSquared;\n }\n } else {\n // Radial: distance from the centre over the radius.\n if (geometry.z > 0.0) {\n t = length(local - geometry.xy) / geometry.z;\n }\n }\n t = clamp(t, 0.0, 1.0);\n\n let count = u32(header.y);\n var previousOffset = gradientStopOffset(base, 0u);\n var previousColor = premultiply(uGradients[base + GRADIENT_COLORS]);\n if (t <= previousOffset) {\n return unpremultiply(previousColor);\n }\n for (var i = 1u; i < count; i = i + 1u) {\n let offset = gradientStopOffset(base, i);\n let color = premultiply(uGradients[base + GRADIENT_COLORS + i]);\n if (t <= offset) {\n let span = offset - previousOffset;\n var f = 0.0;\n if (span > 0.0) {\n f = (t - previousOffset) / span;\n }\n return unpremultiply(mix(previousColor, color, f));\n }\n previousOffset = offset;\n previousColor = color;\n }\n return unpremultiply(previousColor);\n}\n\nstruct VertexOutput {\n @builtin(position) position: vec4f,\n @location(0) localPos: vec2f,\n @location(1) size: vec2f,\n @location(2) color: vec4f,\n @location(3) radiusOpacityBorder: vec3f,\n @location(4) @interpolate(flat) kind: u32,\n @location(5) @interpolate(flat) clipIndex: f32,\n @location(6) @interpolate(flat) gradientIndex: f32,\n};\n\n@vertex\nfn vs(\n @location(0) vertex: vec2f,\n @location(1) instancePos: vec2f,\n @location(2) instanceSize: vec2f,\n @location(3) instanceColor: vec4f,\n @location(4) radiusOpacityBorder: vec3f,\n @location(5) instanceKind: u32,\n @location(6) transformA: vec2f,\n @location(7) transformB: vec2f,\n @location(8) transformC: vec2f,\n @location(9) clipIndex: f32,\n @location(10) gradientIndex: f32,\n) -> VertexOutput {\n var out: VertexOutput;\n // The quad is inflated by one physical pixel on every side so the\n // fragments just outside a fractional or rounded edge exist to be\n // shaded: coverage decides the edge, not rasterisation, which only\n // produces fragments whose centres lie inside the geometry.\n let inflate = 1.0 / uView.z;\n let local = vertex * instanceSize + (vertex * 2.0 - 1.0) * inflate;\n let world = instancePos + local;\n let transformed = vec2f(\n world.x * transformA.x + world.y * transformB.x + transformC.x,\n world.x * transformA.y + world.y * transformB.y + transformC.y\n );\n out.position = toClipSpace(transformed);\n out.localPos = local;\n out.size = instanceSize;\n out.color = instanceColor;\n out.radiusOpacityBorder = radiusOpacityBorder;\n out.kind = instanceKind;\n out.clipIndex = clipIndex;\n out.gradientIndex = gradientIndex;\n return out;\n}\n\n@fragment\nfn fs(in: VertexOutput) -> @location(0) vec4f {\n let dpr = uView.z;\n let dist = roundedRectDistance(in.localPos, vec2f(0.0, 0.0), in.size, in.radiusOpacityBorder.x);\n\n var shape = coverage(dist, dpr);\n if (in.kind != 0u) {\n // Border: the band between the outer edge and the inner edge inset\n // by borderWidth.\n let bw = max(in.radiusOpacityBorder.z, 0.0);\n shape = min(shape, coverage(-(dist + bw), dpr));\n }\n let alpha = shape * clipCoverage(in.position.xy, dpr, in.clipIndex);\n if (alpha <= 0.0) {\n discard;\n }\n var base = in.color;\n if (in.gradientIndex >= 0.0) {\n base = gradientColor(u32(in.gradientIndex), in.localPos);\n }\n return vec4f(base.rgb, base.a * in.radiusOpacityBorder.y * alpha);\n}\n`;\n\nexport const TEXTURED_SHADER = /* wgsl */ `\n${SHARED}\n\n@group(0) @binding(1) var tSampler: sampler;\n@group(0) @binding(2) var tTexture: texture_2d<f32>;\n\nstruct VertexOutput {\n @builtin(position) position: vec4f,\n @location(0) uv: vec2f,\n @location(1) @interpolate(flat) opacity: f32,\n @location(2) @interpolate(flat) clipIndex: f32,\n};\n\n@vertex\nfn vs(\n @location(0) vertex: vec2f,\n @location(1) instancePos: vec2f,\n @location(2) instanceSize: vec2f,\n @location(3) instanceOpacity: f32,\n @location(4) clipIndex: f32,\n @location(5) transformA: vec2f,\n @location(6) transformB: vec2f,\n @location(7) transformC: vec2f,\n @location(8) uvOrigin: vec2f,\n @location(9) uvSize: vec2f,\n) -> VertexOutput {\n var out: VertexOutput;\n let world = instancePos + vertex * instanceSize;\n let transformed = vec2f(\n world.x * transformA.x + world.y * transformB.x + transformC.x,\n world.x * transformA.y + world.y * transformB.y + transformC.y\n );\n out.position = toClipSpace(transformed);\n // A glyph samples its cell in an atlas page; an image passes the\n // whole texture as (0,0)-(1,1) and this is the identity.\n out.uv = uvOrigin + vertex * uvSize;\n out.opacity = instanceOpacity;\n out.clipIndex = clipIndex;\n return out;\n}\n\n@fragment\nfn fs(in: VertexOutput) -> @location(0) vec4f {\n let alpha = clipCoverage(in.position.xy, uView.z, in.clipIndex);\n if (alpha <= 0.0) {\n discard;\n }\n let color = textureSample(tTexture, tSampler, in.uv);\n return vec4f(color.rgb, color.a * in.opacity * alpha);\n}\n`;\n","/**\n * The glyph atlas: one texture holding many glyphs, in place of one\n * texture per text run.\n *\n * A run-per-texture cache is bounded by the number of distinct runs,\n * which for a document view is the number of distinct lines — it has\n * no ceiling. A glyph cache is bounded by the number of distinct\n * glyphs a font is drawn at, which is a few hundred. The atlas is\n * what makes text memory a property of the fonts in use rather than\n * of how much text there is.\n *\n * Two halves, split so the packing is testable without a GPU:\n *\n * - **This file is pure CPU.** It packs cells into pages and hands\n * back a slot with the page index and the UV rectangle. Nothing\n * here touches a device, a canvas or a texture, so the render-list\n * builder can ask for slots inside vitest.\n * - **`WebGPUGlyphPages`** owns the GPU textures. It drains\n * `takePending()` after each build and rasterises those glyphs into\n * their cells.\n *\n * Cells are rasterised **in the run's colour**, not white-and-tinted.\n * Tinting would key the atlas on the glyph alone, but it also flattens\n * a colour emoji into a silhouette and forces a premultiplication\n * decision the rest of the pipeline does not make. Colour is instead\n * part of the key: an application draws text in a handful of colours,\n * so this multiplies the entry count by a small constant and keeps\n * the sampled pixels exactly what Canvas2D would have drawn.\n */\n\n/**\n * Horizontal subpixel positions a glyph is rasterised at.\n *\n * A cell maps one-to-one onto physical pixels, so its left edge has to\n * land on a whole pixel; the fraction of a pixel the pen actually sits\n * at is baked into the raster instead. Three phases keep the spacing\n * error under a third of a physical pixel, which at any device pixel\n * ratio is below what the eye resolves in a line of text. One phase\n * (plain rounding) is visibly uneven at dpr 1.\n */\nexport const GLYPH_SUBPIXEL_PHASES = 3;\n\n/**\n * Cell extents around the pen, in em.\n *\n * Generous rather than measured: the tight ink box of a glyph is only\n * known once it is rasterised, and allocation happens a step earlier,\n * in the builder. These cover the ascenders, descenders and side\n * bearings of any UI font — Chrome reports roughly 0.97 em of ascent\n * and 0.24 em of descent for the default system font — at the cost of\n * some empty space per cell. Tightening them needs font metrics at\n * allocation time; see the decision record.\n */\nconst GLYPH_ASCENT_EM = 1.1;\nconst GLYPH_DESCENT_EM = 0.4;\nconst GLYPH_SIDE_EM = 0.2;\n\n/** Physical-pixel edge of one atlas page. */\nconst DEFAULT_PAGE_SIZE = 1024;\n\n/**\n * Pages kept before the least recently used is cleared. Eight pages of\n * 1024² RGBA is 32 MB, half what the run-texture cache budgeted.\n */\nconst DEFAULT_MAX_PAGES = 8;\n\n/**\n * One font, colour and device pixel ratio, with the cells already\n * allocated for it.\n *\n * A run resolves its style once and then looks each cluster up by\n * string in this map. Keying the atlas on a single composite string\n * instead would build one key per glyph per frame — the frame loop's\n * largest allocation, for a value that is constant across a run.\n */\nexport interface GlyphStyle {\n readonly font: string;\n readonly color: string;\n readonly fontSize: number;\n readonly dpr: number;\n /**\n * The paragraph's base direction, which decides where a neutral\n * character inside a word cell lands when the cell is rasterised: an\n * Arabic word ending in a question mark puts it on the left in a\n * right-to-left paragraph and on the right otherwise.\n */\n readonly rtl: boolean;\n /** Cluster to its cell per subpixel phase; null where it cannot be packed. */\n readonly cells: Map<string, (GlyphSlot | null)[]>;\n}\n\n/** A cell in a page, and where the glyph sits inside it. */\nexport interface GlyphSlot {\n page: number;\n /** UV rectangle of the cell within its page. */\n u: number;\n v: number;\n uw: number;\n vh: number;\n /** Cell size in logical pixels; physical is this × dpr. */\n width: number;\n height: number;\n /** Cell top-left relative to the pen (left edge, baseline), logical. */\n offsetX: number;\n offsetY: number;\n}\n\n/** A newly allocated cell, waiting for its pixels. */\nexport interface GlyphUpload {\n slot: GlyphSlot;\n cluster: string;\n font: string;\n color: string;\n dpr: number;\n /** The direction the cell is rasterised in; see `GlyphStyle.rtl`. */\n rtl: boolean;\n /** Cell origin in the page, physical pixels. */\n pageX: number;\n pageY: number;\n /** Cell size in physical pixels. */\n pixelWidth: number;\n pixelHeight: number;\n /** Pen position inside the cell, physical pixels; the phase is in it. */\n penX: number;\n penY: number;\n}\n\ninterface Page {\n /** Next free x on the current shelf. */\n penX: number;\n /** Top of the current shelf. */\n shelfY: number;\n /** Height of the current shelf. */\n shelfHeight: number;\n /** Frame this page was last drawn from. */\n lastUsed: number;\n}\n\nexport interface GlyphAtlasOptions {\n /** Physical-pixel edge of a page. Default 1024. */\n pageSize?: number;\n /** Pages kept before the least recently used is cleared. Default 8. */\n maxPages?: number;\n}\n\n/**\n * Shelf-packed glyph cells across a small number of fixed-size pages.\n *\n * Shelf packing — fill a row left to right, start a new row when the\n * widest cell in the current one is passed — wastes a little height\n * per row and is right for glyphs, which arrive in a narrow range of\n * heights. A page that fills opens the next one; when every page is\n * full the least recently used is cleared wholesale and re-filled on\n * demand, which is cheap because the glyphs still on screen are\n * re-rasterised within a frame or two and everything else was cold.\n */\nexport class WebGPUGlyphAtlas {\n private readonly styles = new Map<string, GlyphStyle>();\n private readonly pages: Page[] = [];\n private pending: GlyphUpload[] = [];\n private frame = 0;\n private cells = 0;\n /** Pages cleared since the last drain, so the GPU side can zero them. */\n private clearedPages: number[] = [];\n readonly pageSize: number;\n readonly maxPages: number;\n\n constructor(options: GlyphAtlasOptions = {}) {\n this.pageSize = options.pageSize ?? DEFAULT_PAGE_SIZE;\n this.maxPages = options.maxPages ?? DEFAULT_MAX_PAGES;\n }\n\n get pageCount(): number {\n return this.pages.length;\n }\n\n /** Cells allocated across every style and page. */\n get glyphCount(): number {\n return this.cells;\n }\n\n /** Marks the start of a frame, for page use tracking. */\n beginFrame(): void {\n this.frame++;\n }\n\n /** The style handle a run looks its clusters up in; created on first sight. */\n styleFor(font: string, color: string, fontSize: number, dpr: number, rtl = false): GlyphStyle {\n const key = `${font}\\0${color}\\0${dpr}\\0${rtl ? 'R' : 'L'}`;\n let style = this.styles.get(key);\n if (style === undefined) {\n style = { font, color, fontSize, dpr, rtl, cells: new Map() };\n this.styles.set(key, style);\n }\n return style;\n }\n\n /**\n * The cell for one glyph, allocating and queueing it for upload on\n * first sight. Null when the cluster cannot fit a page at all.\n */\n slotFor(style: GlyphStyle, cluster: string, advance: number, phase: number): GlyphSlot | null {\n let phases = style.cells.get(cluster);\n if (phases === undefined) {\n phases = Array.from<GlyphSlot | null>({ length: GLYPH_SUBPIXEL_PHASES }).fill(null);\n style.cells.set(cluster, phases);\n } else {\n const existing = phases[phase];\n if (existing !== null) {\n this.pages[existing.page].lastUsed = this.frame;\n return existing;\n }\n }\n\n const dpr = style.dpr;\n // Padding is a whole number of physical pixels so that the cell's\n // left edge stays on a pixel when the pen does.\n const sidePx = Math.max(1, Math.ceil(style.fontSize * GLYPH_SIDE_EM * dpr));\n const ascentPx = Math.max(1, Math.ceil(style.fontSize * GLYPH_ASCENT_EM * dpr));\n const descentPx = Math.max(1, Math.ceil(style.fontSize * GLYPH_DESCENT_EM * dpr));\n const pixelWidth = Math.ceil(advance * dpr) + 2 * sidePx;\n const pixelHeight = ascentPx + descentPx;\n if (pixelWidth > this.pageSize || pixelHeight > this.pageSize) {\n return null;\n }\n\n const placed = this.place(pixelWidth, pixelHeight);\n if (placed === null) {\n return null;\n }\n const slot: GlyphSlot = {\n page: placed.page,\n u: placed.x / this.pageSize,\n v: placed.y / this.pageSize,\n uw: pixelWidth / this.pageSize,\n vh: pixelHeight / this.pageSize,\n width: pixelWidth / dpr,\n height: pixelHeight / dpr,\n offsetX: -sidePx / dpr,\n offsetY: -ascentPx / dpr\n };\n phases[phase] = slot;\n this.cells++;\n this.pending.push({\n slot,\n cluster,\n font: style.font,\n color: style.color,\n dpr,\n rtl: style.rtl,\n pageX: placed.x,\n pageY: placed.y,\n pixelWidth,\n pixelHeight,\n penX: sidePx + phase / GLYPH_SUBPIXEL_PHASES,\n penY: ascentPx\n });\n return slot;\n }\n\n /** Cells allocated since the last call, for the GPU side to fill. */\n takePending(): GlyphUpload[] {\n if (this.pending.length === 0) {\n return [];\n }\n const pending = this.pending;\n this.pending = [];\n return pending;\n }\n\n /** Pages cleared since the last call, whose textures must be zeroed. */\n takeCleared(): number[] {\n if (this.clearedPages.length === 0) {\n return [];\n }\n const cleared = this.clearedPages;\n this.clearedPages = [];\n return cleared;\n }\n\n /** Drops every glyph and every page. */\n reset(): void {\n this.styles.clear();\n this.pages.length = 0;\n this.pending = [];\n this.clearedPages = [];\n this.cells = 0;\n }\n\n /**\n * Finds room for a cell: on the current shelf of an existing page,\n * on a new shelf, on a new page, or — when every page is full — on\n * the least recently used page after clearing it.\n */\n private place(width: number, height: number): { page: number; x: number; y: number } | null {\n for (let index = 0; index < this.pages.length; index++) {\n const spot = this.tryPlace(index, width, height);\n if (spot !== null) {\n return spot;\n }\n }\n if (this.pages.length < this.maxPages) {\n this.pages.push({ penX: 0, shelfY: 0, shelfHeight: 0, lastUsed: this.frame });\n return this.tryPlace(this.pages.length - 1, width, height);\n }\n const victim = this.leastRecentlyUsedPage();\n this.clearPage(victim);\n return this.tryPlace(victim, width, height);\n }\n\n private tryPlace(index: number, width: number, height: number): { page: number; x: number; y: number } | null {\n const page = this.pages[index];\n if (page.penX + width <= this.pageSize && height <= page.shelfHeight) {\n const x = page.penX;\n page.penX += width;\n page.lastUsed = this.frame;\n return { page: index, x, y: page.shelfY };\n }\n // A new shelf, either because the row is full or because this cell\n // is taller than the row was sized for.\n const shelfY = page.shelfY + page.shelfHeight;\n if (shelfY + height > this.pageSize) {\n return null;\n }\n page.shelfY = shelfY;\n page.shelfHeight = height;\n page.penX = width;\n page.lastUsed = this.frame;\n return { page: index, x: 0, y: shelfY };\n }\n\n private leastRecentlyUsedPage(): number {\n let victim = 0;\n for (let index = 1; index < this.pages.length; index++) {\n if (this.pages[index].lastUsed < this.pages[victim].lastUsed) {\n victim = index;\n }\n }\n return victim;\n }\n\n private clearPage(index: number): void {\n for (const style of this.styles.values()) {\n for (const phases of style.cells.values()) {\n for (let phase = 0; phase < phases.length; phase++) {\n if (phases[phase]?.page === index) {\n phases[phase] = null;\n this.cells--;\n }\n }\n }\n }\n this.pending = this.pending.filter(upload => upload.slot.page !== index);\n this.pages[index] = { penX: 0, shelfY: 0, shelfHeight: 0, lastUsed: this.frame };\n this.clearedPages.push(index);\n }\n}\n\n/**\n * The subpixel phase for a pen sitting `fraction` of a physical pixel\n * past a whole one.\n */\nexport function phaseFor(fraction: number): number {\n const phase = Math.floor(fraction * GLYPH_SUBPIXEL_PHASES);\n return Math.min(GLYPH_SUBPIXEL_PHASES - 1, Math.max(0, phase));\n}\n","/**\n * The visual order of a line's words.\n *\n * Canvas2D never needs this: `fillText` takes a whole line and Chrome\n * reorders it. The WebGPU glyph atlas draws a line a cell at a time,\n * and for a line holding Arabic or Hebrew that means two things the\n * per-cluster path cannot do. A cursive word has to be rasterised whole,\n * or every letter draws in its isolated form; and the words have to be\n * placed in visual order, or an Arabic sentence reads backwards and a\n * number inside it lands on the wrong side.\n *\n * This is the Unicode Bidirectional Algorithm at the granularity of\n * words rather than characters. A line is split into tokens, maximal\n * runs of blanks and of non-blanks, each token takes one bidi class\n * from its first strong character (or from its digits, or none), and\n * the algorithm's weak, neutral, implicit and reordering rules run over\n * the tokens (W2, W7, N1, N2, I1, I2, L1, L2). Inside a token, Chrome\n * shapes and orders the characters when the token is rasterised, so a\n * word that mixes classes internally, rare outside URLs, comes out as\n * Chrome would draw it alone.\n *\n * Not done, and stated: bracket pairing (N0), explicit embeddings and\n * isolates (the U+202A to U+2069 controls are treated as neutral),\n * and levels above two. The text conformance harness records Chrome's\n * per-cluster visual positions for every right-to-left fixture and\n * `WebGPUGlyphShaper.bidi.spec.ts` asserts these tokens land on them.\n */\n\nexport interface VisualToken {\n /** Offsets into the line's text, in logical order. */\n start: number;\n end: number;\n text: string;\n blank: boolean;\n}\n\n/** Whether a line needs visual ordering at all: a right-to-left paragraph, or a strong right-to-left character in it. */\nexport function needsVisualOrder(text: string, rtl: boolean): boolean {\n if (rtl) {\n return true;\n }\n for (const character of text) {\n const kind = strongKind(character.codePointAt(0)!);\n if (kind === 'R' || kind === 'AL') {\n return true;\n }\n }\n return false;\n}\n\n/** The line's tokens in the order they are drawn from left to right. */\nexport function visualOrder(text: string, rtl: boolean): VisualToken[] {\n const tokens = tokenize(text);\n if (tokens.length === 0) {\n return tokens;\n }\n const paragraph = rtl ? 1 : 0;\n const classes = tokens.map(token => (token.blank ? 'WS' : classify(token.text)));\n\n // W2: a European number after an Arabic letter is an Arabic number.\n // W7: a European number after a Latin letter (or at the start of a\n // left-to-right paragraph) is a letter.\n let lastStrong: 'L' | 'R' | 'AL' | null = paragraph === 0 ? 'L' : 'R';\n for (let i = 0; i < classes.length; i++) {\n const kind = classes[i];\n if (kind === 'L' || kind === 'R' || kind === 'AL') {\n lastStrong = kind;\n } else if (kind === 'EN') {\n if (lastStrong === 'AL') {\n classes[i] = 'AN';\n } else if (lastStrong === 'L') {\n classes[i] = 'L';\n }\n }\n }\n\n // N1 and N2: a run of neutrals between two strong characters of the\n // same direction takes it (numbers count as right-to-left); any other\n // neutral run takes the paragraph's direction.\n const directions: ('L' | 'R')[] = classes.map(kind => (kind === 'L' ? 'L' : 'R'));\n let i = 0;\n while (i < classes.length) {\n if (!isNeutral(classes[i])) {\n i++;\n continue;\n }\n let j = i;\n while (j < classes.length && isNeutral(classes[j])) {\n j++;\n }\n const before = i === 0 ? null : directions[i - 1];\n const after = j === classes.length ? null : directionOfStrong(classes[j]);\n const resolved: 'L' | 'R' = before !== null && before === after ? before : paragraph === 0 ? 'L' : 'R';\n for (let k = i; k < j; k++) {\n directions[k] = resolved;\n }\n i = j;\n }\n\n // I1 and I2: embedding levels from the resolved classes.\n const levels = classes.map((kind, index) => {\n if (kind === 'EN' || kind === 'AN') {\n return 2;\n }\n if (kind === 'R' || kind === 'AL') {\n return 1;\n }\n if (kind === 'L') {\n return paragraph === 0 ? 0 : 2;\n }\n return directions[index] === 'L' ? (paragraph === 0 ? 0 : 2) : 1;\n });\n\n // L1: blanks at the end of the line take the paragraph level.\n for (let k = levels.length - 1; k >= 0 && tokens[k].blank; k--) {\n levels[k] = paragraph;\n }\n\n // L2: from the highest level down to the lowest odd level, reverse\n // every run of tokens at that level or above.\n const highest = Math.max(...levels);\n const lowestOdd = Math.min(...levels.map(level => (level % 2 === 1 ? level : Infinity)));\n const order = tokens.map((_, index) => index);\n for (let level = highest; level >= Math.max(1, lowestOdd); level--) {\n let k = 0;\n while (k < order.length) {\n if (levels[order[k]] < level) {\n k++;\n continue;\n }\n let end = k;\n while (end < order.length && levels[order[end]] >= level) {\n end++;\n }\n reverse(order, k, end);\n k = end;\n }\n }\n return order.map(index => tokens[index]);\n}\n\ntype BidiClass = 'L' | 'R' | 'AL' | 'EN' | 'AN' | 'ON' | 'WS';\n\nfunction isNeutral(kind: BidiClass): boolean {\n return kind === 'ON' || kind === 'WS';\n}\n\nfunction directionOfStrong(kind: BidiClass): 'L' | 'R' | null {\n return kind === 'L' ? 'L' : kind === 'R' || kind === 'AL' || kind === 'EN' || kind === 'AN' ? 'R' : null;\n}\n\n/** A token's class: its first strong character's, else its digits', else neutral. */\nfunction classify(text: string): BidiClass {\n let digits: 'EN' | 'AN' | null = null;\n for (const character of text) {\n const code = character.codePointAt(0)!;\n const strong = strongKind(code);\n if (strong !== null) {\n return strong;\n }\n if (code >= 0x0660 && code <= 0x0669) {\n digits = 'AN';\n } else if (digits === null && DIGIT.test(character)) {\n digits = 'EN';\n }\n }\n return digits ?? 'ON';\n}\n\nconst DIGIT = /^\\p{Nd}$/u;\nconst LETTER = /^\\p{L}$/u;\n\n/** L, R or AL for a strong character; null for anything else. */\nfunction strongKind(code: number): 'L' | 'R' | 'AL' | null {\n if (\n (code >= 0x0600 && code <= 0x06ff && !(code >= 0x0660 && code <= 0x0669) && !(code >= 0x06f0 && code <= 0x06f9)) ||\n (code >= 0x0750 && code <= 0x077f) ||\n (code >= 0x0870 && code <= 0x08ff) ||\n (code >= 0x0700 && code <= 0x074f) || // Syriac\n (code >= 0x0780 && code <= 0x07bf) || // Thaana\n (code >= 0xfb50 && code <= 0xfdff) ||\n (code >= 0xfe70 && code <= 0xfeff)\n ) {\n return isMarkOrPunctuation(code) ? null : 'AL';\n }\n if (\n (code >= 0x0590 && code <= 0x05ff) || // Hebrew\n (code >= 0x07c0 && code <= 0x07ff) || // NKo\n (code >= 0xfb1d && code <= 0xfb4f)\n ) {\n return isMarkOrPunctuation(code) ? null : 'R';\n }\n return LETTER.test(String.fromCodePoint(code)) ? 'L' : null;\n}\n\n/** Combining marks and punctuation inside a right-to-left block are not strong. */\nfunction isMarkOrPunctuation(code: number): boolean {\n const character = String.fromCodePoint(code);\n return !LETTER.test(character);\n}\n\nfunction tokenize(text: string): VisualToken[] {\n const tokens: VisualToken[] = [];\n let start = 0;\n while (start < text.length) {\n const blank = isBlankCode(text.charCodeAt(start));\n let end = start + 1;\n while (end < text.length && isBlankCode(text.charCodeAt(end)) === blank) {\n end++;\n }\n tokens.push({ start, end, text: text.slice(start, end), blank });\n start = end;\n }\n return tokens;\n}\n\nfunction isBlankCode(code: number): boolean {\n return code === 0x20 || code === 0x09 || code === 0x3000 || code === 0xa0;\n}\n\nfunction reverse(order: number[], from: number, to: number): void {\n for (let a = from, b = to - 1; a < b; a++, b--) {\n const swap = order[a];\n order[a] = order[b];\n order[b] = swap;\n }\n}\n","import { graphemeBoundaries } from '../../editing/TextBoundaries';\nimport { needsVisualOrder, visualOrder } from '../BidiRuns';\n\n/**\n * Where each grapheme cluster of a line starts, and how wide it is.\n *\n * Positions come from **prefix widths** — the x of cluster `i` is the\n * measured width of the line's first `i` clusters — which is the same\n * rule caret and selection geometry use (`TextGeometry.ts`), from the\n * same measurer that produced the line. Two things follow from that\n * and neither is available from summing per-glyph advances: kerning is\n * honoured, because each prefix is measured in context; and the last\n * cluster's end is exactly the line width layout was sized with, so a\n * centred or right-aligned line ends where the box says it does.\n *\n * What it does not do is shape. Clusters are split by grapheme\n * boundary and drawn one at a time, so a font's ligatures do not form:\n * `fi` draws as an f and an i at the positions the ligature would have\n * occupied rather than as one glyph. Combining marks survive, because\n * a base and its marks are one cluster and rasterise together. This is\n * the cost of rasterising with a canvas instead of a shaping engine,\n * and it is the same trade the editing caret already makes.\n *\n * The exception is a line that holds a right-to-left script, or sits in\n * a right-to-left paragraph. There a cluster is a whole word, so Arabic\n * keeps its joining forms, and the words are placed in visual order by\n * `BidiRuns.ts`; see `shapeWords`.\n */\nexport interface ShapedCluster {\n text: string;\n /** Offset from the line's left edge, logical pixels. */\n x: number;\n /** Advance width, logical pixels. */\n advance: number;\n /** Whitespace only: it advances the pen and draws nothing. */\n blank: boolean;\n}\n\n/** The advance width of a run of text in the line's font. */\nexport type MeasureRun = (text: string) => number;\n\n/**\n * Entries kept before the cache is cleared wholesale.\n *\n * The same crude cap `CanvasTextMeasurer` uses, for the same reason:\n * an LRU costs more than it saves until profiling says otherwise. One\n * entry is a line's clusters, so a screen of a document is a few\n * hundred entries and a scrolled list re-uses every one of them.\n */\nconst MAX_CACHE_ENTRIES = 4096;\n\n/**\n * Splits lines into positioned clusters, once per distinct line.\n *\n * Prefix measurement costs one `measureText` per cluster, so a line is\n * measured n times the first time it is seen and never again. The\n * cache is keyed by font and text — not by position, size or colour —\n * so scrolling, moving or recolouring a line costs nothing.\n */\nexport class GlyphShaper {\n private readonly cache = new Map<string, ShapedCluster[]>();\n\n get size(): number {\n return this.cache.size;\n }\n\n /**\n * The clusters of one line. `width` is the line's measured width,\n * used as the last cluster's end so the run closes exactly where\n * layout put it.\n */\n shape(text: string, font: string, width: number, measure: MeasureRun, rtl = false): readonly ShapedCluster[] {\n if (text.length === 0) {\n return [];\n }\n const key = `${font}\\0${rtl ? 'R' : 'L'}\\0${text}`;\n const cached = this.cache.get(key);\n if (cached !== undefined) {\n return cached;\n }\n\n const clusters: ShapedCluster[] = needsVisualOrder(text, rtl)\n ? this.shapeWords(text, measure, rtl)\n : this.shapeClusters(text, width, measure);\n\n if (this.cache.size >= MAX_CACHE_ENTRIES) {\n this.cache.clear();\n }\n this.cache.set(key, clusters);\n return clusters;\n }\n\n /**\n * A line with a right-to-left script in it, or in a right-to-left\n * paragraph: one cell per word, placed in visual order. A cursive\n * word rasterised whole keeps its joining forms, and `visualOrder`\n * puts the words where Chrome's `fillText` would, so a number inside\n * an Arabic sentence lands on the side it belongs. Positions are\n * cumulative measured widths in visual order; kerning does not cross\n * a blank, so they are the prefix widths the other path uses, in a\n * different order.\n */\n private shapeWords(text: string, measure: MeasureRun, rtl: boolean): ShapedCluster[] {\n const clusters: ShapedCluster[] = [];\n let x = 0;\n for (const token of visualOrder(text, rtl)) {\n const advance = measure(token.text);\n clusters.push({ text: token.text, x, advance, blank: token.blank });\n x += advance;\n }\n return clusters;\n }\n\n /** The common path: one cell per grapheme cluster at its prefix width. */\n private shapeClusters(text: string, width: number, measure: MeasureRun): ShapedCluster[] {\n const boundaries = graphemeBoundaries(text);\n const clusters: ShapedCluster[] = [];\n let previous = 0;\n for (let i = 1; i < boundaries.length; i++) {\n const end = boundaries[i];\n // The final prefix is the whole line, whose width layout already\n // measured; taking it from the argument keeps the run's end and\n // the line box in exact agreement.\n const advanceEnd = i === boundaries.length - 1 ? width : measure(text.slice(0, end));\n const cluster = text.slice(boundaries[i - 1], end);\n clusters.push({\n text: cluster,\n x: previous,\n advance: Math.max(0, advanceEnd - previous),\n blank: isBlank(cluster)\n });\n previous = advanceEnd;\n }\n return clusters;\n }\n\n clear(): void {\n this.cache.clear();\n }\n}\n\n/**\n * Whether a cluster is whitespace. Decided once, when the line is\n * shaped, because the frame loop asks it of every cluster it draws and\n * `trim()` would allocate a string per glyph per frame.\n */\nfunction isBlank(text: string): boolean {\n for (let i = 0; i < text.length; i++) {\n const code = text.charCodeAt(i);\n const space = code === 32 || code === 9 || code === 10 || code === 13 || code === 12 || code === 11;\n if (!space && !(code >= 0x2000 && code <= 0x200a) && code !== 0x00a0 && code !== 0x3000) {\n return false;\n }\n }\n return true;\n}\n","import type { UiColor } from '../../properties/UiColor';\n\nexport interface RgbaColor {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\nfunction isUiColor(value: unknown): value is UiColor {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const c = value as Partial<UiColor>;\n return typeof c.r === 'number' && typeof c.g === 'number' && typeof c.b === 'number' && typeof c.a === 'number';\n}\n\n/**\n * Parses a color value into normalized RGBA.\n *\n * Accepts either a renderer-independent UiColor object or a CSS\n * color string. Supports:\n * #rgb, #rgba, #rrggbb, #rrggbbaa\n * rgb(r,g,b), rgba(r,g,b,a)\n * named colors: transparent, black, white, red, green, blue\n *\n * Returns undefined for unsupported values so callers can skip\n * drawing instead of crashing.\n */\nexport function parseColor(value: unknown): RgbaColor | undefined {\n if (isUiColor(value)) {\n return { r: value.r, g: value.g, b: value.b, a: value.a };\n }\n if (typeof value !== 'string') {\n return undefined;\n }\n const trimmed = value.trim().toLowerCase();\n if (trimmed.length === 0) {\n return undefined;\n }\n\n const named = NAMED_COLORS[trimmed];\n if (named !== undefined) {\n return named;\n }\n\n if (trimmed.startsWith('#')) {\n return parseHex(trimmed);\n }\n if (trimmed.startsWith('rgb')) {\n return parseRgb(trimmed);\n }\n return undefined;\n}\n\nfunction parseHex(hex: string): RgbaColor | undefined {\n let raw = hex.slice(1);\n if (raw.length === 3 || raw.length === 4) {\n raw = raw\n .split('')\n .map(c => c + c)\n .join('');\n }\n if (raw.length !== 6 && raw.length !== 8) {\n return undefined;\n }\n const num = Number.parseInt(raw, 16);\n if (!Number.isFinite(num)) {\n return undefined;\n }\n const r = (num >> 16) & 0xff;\n const g = (num >> 8) & 0xff;\n const b = num & 0xff;\n const a = raw.length === 8 ? (num >> 24) & 0xff : 0xff;\n return { r: r / 255, g: g / 255, b: b / 255, a: a / 255 };\n}\n\nfunction parseRgb(source: string): RgbaColor | undefined {\n const open = source.indexOf('(');\n const close = source.indexOf(')');\n if (open < 0 || close < 0) {\n return undefined;\n }\n const parts = source\n .slice(open + 1, close)\n .split(',')\n .map(s => s.trim());\n if (parts.length < 3) {\n return undefined;\n }\n const values: number[] = [];\n for (let i = 0; i < parts.length; i++) {\n const part = parts[i];\n const num = Number(part.replace('%', ''));\n if (!Number.isFinite(num)) {\n return undefined;\n }\n if (part.includes('%')) {\n values.push(num / 100);\n } else if (i === 3) {\n values.push(num);\n } else {\n values.push(num / 255);\n }\n }\n const [r, g, b, a = 1] = values;\n return {\n r: clamp01(r),\n g: clamp01(g),\n b: clamp01(b),\n a: clamp01(a)\n };\n}\n\nfunction clamp01(v: number): number {\n return Math.min(Math.max(v, 0), 1);\n}\n\nconst NAMED_COLORS: Record<string, RgbaColor> = {\n transparent: { r: 0, g: 0, b: 0, a: 0 },\n black: { r: 0, g: 0, b: 0, a: 1 },\n white: { r: 1, g: 1, b: 1, a: 1 },\n red: { r: 1, g: 0, b: 0, a: 1 },\n green: { r: 0, g: 0.5, b: 0, a: 1 },\n blue: { r: 0, g: 0, b: 1, a: 1 },\n yellow: { r: 1, g: 1, b: 0, a: 1 },\n cyan: { r: 0, g: 1, b: 1, a: 1 },\n magenta: { r: 1, g: 0, b: 1, a: 1 },\n orange: { r: 1, g: 0.65, b: 0, a: 1 },\n gray: { r: 0.5, g: 0.5, b: 0.5, a: 1 },\n grey: { r: 0.5, g: 0.5, b: 0.5, a: 1 }\n};\n","import { WebGPUError } from './WebGPUError';\n\n/**\n * Converts a logical dimension to the physical pixel count of the\n * backing store.\n *\n * This is the single definition of that rule, and everything that\n * derives a physical rectangle — scissor rects above all — must go\n * through it. WebGPU rejects a scissor that extends past its\n * attachment by even one pixel, and the rejection discards the whole\n * command buffer, so a backing store rounded one way and a scissor\n * rounded another silently drops every draw in the frame.\n */\nexport function toPhysicalPixels(logical: number, dpr: number): number {\n return Math.max(1, Math.round(logical * dpr));\n}\n\n/**\n * Something that can host a WebGPU drawing surface.\n *\n * HTMLCanvasElement and OffscreenCanvas both satisfy this\n * structurally; the widening to concrete canvas types lives only at\n * the platform boundary.\n */\nexport interface WebGPUCanvasHost {\n width: number;\n height: number;\n getContext(contextId: 'webgpu'): GPUCanvasContext | null;\n}\n\n/**\n * Logical/physical viewport bookkeeping for a WebGPU canvas surface.\n *\n * Mirrors CanvasSurface but for WebGPU: layout works in logical\n * pixels, the configured GPU texture is physical = logical * dpr.\n */\nexport class WebGPUSurface {\n private logicalW = 0;\n private logicalH = 0;\n private pixelRatio = 1;\n private context: GPUCanvasContext | null = null;\n private configured = false;\n\n constructor(private readonly canvas: WebGPUCanvasHost) {}\n\n get logicalWidth(): number {\n return this.logicalW;\n }\n\n get logicalHeight(): number {\n return this.logicalH;\n }\n\n get dpr(): number {\n return this.pixelRatio;\n }\n\n get physicalWidth(): number {\n return this.canvas.width;\n }\n\n get physicalHeight(): number {\n return this.canvas.height;\n }\n\n /**\n * The WebGPU context for the backing store, acquired lazily.\n */\n getContext(): GPUCanvasContext {\n if (this.context === null) {\n this.context = this.acquireContext();\n }\n return this.context;\n }\n\n private acquireContext(): GPUCanvasContext {\n const context = this.canvas.getContext('webgpu');\n if (context === null) {\n throw new WebGPUError('Canvas webgpu context is unavailable.', 'context');\n }\n return context;\n }\n\n /**\n * Configures the context with the supplied device and format.\n *\n * Reconfiguration is skipped when the physical size has not changed\n * so that the existing GPU texture and swap chain stay valid.\n */\n configure(device: GPUDevice, format: GPUTextureFormat): void {\n const ctx = this.getContext();\n const renderAttachment = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.RENDER_ATTACHMENT : 0x10;\n const copySrc = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.COPY_SRC : 0x1;\n ctx.configure({\n device,\n format,\n alphaMode: 'premultiplied',\n // COPY_SRC lets a frame be read back for the parity check.\n usage: renderAttachment | copySrc\n });\n this.configured = true;\n }\n\n unconfigure(): void {\n if (this.context !== null && this.configured) {\n this.context.unconfigure();\n this.configured = false;\n }\n }\n\n /**\n * Resizes the backing store to logical * dpr.\n *\n * Does not reconfigure the GPU context here: the renderer checks\n * whether the physical size changed and reconfigures when needed.\n */\n setLogicalSize(width: number, height: number, dpr: number = 1): boolean {\n this.logicalW = width;\n this.logicalH = height;\n this.pixelRatio = dpr;\n const physicalWidth = toPhysicalPixels(width, dpr);\n const physicalHeight = toPhysicalPixels(height, dpr);\n const changed = this.canvas.width !== physicalWidth || this.canvas.height !== physicalHeight;\n if (changed) {\n this.canvas.width = physicalWidth;\n this.canvas.height = physicalHeight;\n }\n return changed;\n }\n\n getCurrentTexture(): GPUTexture {\n return this.getContext().getCurrentTexture();\n }\n}\n\nexport function createWebGPUSurface(host: WebGPUCanvasHost): WebGPUSurface {\n return new WebGPUSurface(host);\n}\n","import type { UiNode } from '../../graph/UiNode';\nimport { UiNodeType } from '../../graph/UiNodeType';\nimport { SCROLLBAR_FADE_MS } from '../../layout/LayoutEngine';\nimport { SCROLLBAR_THICKNESS, scrollbarThumbs } from '../../layout/Scrollbars';\nimport type { LayoutRecord } from '../../layout/LayoutRecord';\nimport type { LayoutBox } from '../../layout/LayoutTypes';\nimport type { TextMeasurer } from '../../layout/TextMeasurer';\nimport { resolvePaintState, createPaintState, computeObjectFitRect, isPaintVisible } from '../PaintState';\nimport { videoFrameSize } from '../../properties/UiVideo';\nimport type { TextureSource } from './WebGPUTextureCache';\nimport { colorToCss } from '../PaintState';\nimport {\n layoutTextLines,\n buildFontString,\n runFontString,\n spanOf,\n textMeasureRequest,\n textRunBackgrounds,\n textRunDecorations\n} from '../TextRenderer';\nimport type { SpanPaint, TextLinePlacement, TextRunRect } from '../TextRenderer';\nimport { spannedRunsFor } from '../../layout/TextMeasurer';\nimport type { TextMeasureRequest } from '../../layout/TextMeasurer';\nimport { WebGPUGlyphAtlas, phaseFor } from './WebGPUGlyphAtlas';\nimport type { GlyphStyle } from './WebGPUGlyphAtlas';\nimport { GlyphShaper } from './WebGPUGlyphShaper';\nimport type { MeasureRun } from './WebGPUGlyphShaper';\nimport { parseColor } from './WebGPUColor';\nimport type { RgbaColor } from './WebGPUColor';\nimport { borderRadiusIsZero, uniformBorderRadius } from '../../properties/UiBorderRadius';\nimport { gradientPaint, MAX_GRADIENT_STOPS, type ResolvedGradient } from '../../properties/UiGradient';\nimport { normalizeColor } from '../../properties/UiColor';\nimport { LABEL_PADDING_X, labelOrigin, type OverlayShape } from '../OverlayShapes';\nimport { decorationColor, decorationRect, hasDecorationPhase, type DecorationShape } from '../Decorations';\nimport { paintPictures } from '../PaintPicture';\nimport { toPhysicalPixels } from './WebGPUSurface';\nimport { EditableLayout } from '../../editing/EditableLayout';\nimport { lineIndexForOffset } from '../../editing/TextGeometry';\nimport { CARET_WIDTH, caretVisibleAt } from '../../editing/UiEditable';\nimport { paragraphGeometryFrom, selectionRectsIn } from '../../selection/TextSelectionGeometry';\n\n/**\n * Primitive instance layout, in floats:\n *\n * 0 pos.xy absolute layout coordinates of the rectangle\n * 2 size.xy\n * 4 color.rgba\n * 8 radius, opacity, borderWidth\n * 11 kind (u32: PrimitiveKind)\n * 12 transform 3x2 affine, absolute layout → logical screen\n * 18 clipIndex index into the clip chain, -1 for none\n * 19 gradientIndex index into the frame's gradients, -1 for none\n * 20 (end)\n */\nexport const INSTANCE_STRIDE_FLOATS = 20;\nexport const INSTANCE_STRIDE_BYTES = INSTANCE_STRIDE_FLOATS * 4;\n\n/**\n * Textured instance layout (glyphs and images), in floats:\n *\n * 0 pos.xy\n * 2 size.xy\n * 4 opacity\n * 5 clipIndex\n * 6 transform 3x2\n * 12 uvOrigin.xy top-left of the sampled region, 0..1\n * 14 uvSize.xy its extent; (0,0)-(1,1) is the whole texture\n * 16 (end)\n *\n * An image samples its whole texture; a glyph samples its cell in an\n * atlas page, which is what the UV rectangle is here for.\n */\nexport const TEXTURED_STRIDE_FLOATS = 16;\nexport const TEXTURED_STRIDE_BYTES = TEXTURED_STRIDE_FLOATS * 4;\n\n/**\n * Clip chain node layout, in floats (four vec4s):\n *\n * 0 rect.xywh the clipping box in its own layout space\n * 4 radius, parentIndex (-1 at the root), pad, pad\n * 8 inverse a, b, c, d 3x2 affine, logical screen → clip space\n * 12 inverse tx, ty, pad, pad\n * 16 (end)\n *\n * An instance names its innermost rounded clip; the fragment shader\n * walks parent links to the root, so every rounded ancestor applies.\n */\nexport const CLIP_STRIDE_FLOATS = 16;\nexport const CLIP_STRIDE_BYTES = CLIP_STRIDE_FLOATS * 4;\nexport const NO_CLIP_INDEX = -1;\n\n/**\n * Gradient record layout, in floats (twelve vec4s):\n *\n * 0 kind (0 linear, 1 radial), stopCount, pad, pad\n * 4 linear: the gradient line x0, y0, x1, y1\n * radial: centre x, centre y, radius, pad\n * 8 up to MAX_GRADIENT_STOPS colours, rgba each\n * 40 their offsets along the gradient, four to a vec4\n * 48 (end)\n *\n * Geometry is in the instance's own coordinates, with the origin at\n * the rectangle's top-left, which is exactly the `localPos` the\n * fragment shader already has. A fill instance names the gradient it\n * is painted with; the record is fixed-size so one buffer holds them\n * all and a stop is a lookup rather than a second indirection. That is\n * what the stop cap buys, and why exceeding it is an error rather than\n * a truncation: see `MAX_GRADIENT_STOPS`.\n */\nexport const GRADIENT_STRIDE_FLOATS = 48;\nexport const GRADIENT_STRIDE_BYTES = GRADIENT_STRIDE_FLOATS * 4;\n/** Float offset of the first stop colour within a record. */\nexport const GRADIENT_COLORS_OFFSET = 8;\n/** Float offset of the first stop position within a record. */\nexport const GRADIENT_OFFSETS_OFFSET = 40;\nexport const NO_GRADIENT_INDEX = -1;\n\nexport const enum PrimitiveKind {\n Fill = 0,\n Border = 1\n}\n\nexport const enum CommandKind {\n /** A contiguous range of primitive instances under one scissor. */\n Primitives = 0,\n /** A contiguous range of glyph instances sharing one atlas page. */\n Glyphs = 1,\n /** One textured instance drawn with an image. */\n Image = 2\n}\n\nexport type Affine = [number, number, number, number, number, number];\n\nexport interface ScissorRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface PrimitiveCommand {\n kind: CommandKind.Primitives;\n /** Inclusive start index in the primitive instance buffer. */\n start: number;\n /** Exclusive end index in the primitive instance buffer. */\n end: number;\n /** Scissor rectangle in physical pixels, or null for the full viewport. */\n scissor: ScissorRect | null;\n}\n\nexport interface GlyphCommand {\n kind: CommandKind.Glyphs;\n /** Inclusive start index in the textured instance buffer. */\n start: number;\n /** Exclusive end index. */\n end: number;\n /** Atlas page every instance in the range samples. */\n page: number;\n scissor: ScissorRect | null;\n}\n\nexport interface ImageCommand {\n kind: CommandKind.Image;\n instance: number;\n scissor: ScissorRect | null;\n /** A still, or a video's surface; the texture cache tells them apart. */\n source: TextureSource;\n /**\n * How many device pixels the quad covers, so a still can be uploaded\n * at the size it is drawn rather than the size it was decoded.\n *\n * Under the node's whole transform, not the device pixel ratio alone:\n * a node a morph is scaling covers a different number of pixels every\n * frame, which is the same reason `Canvas2DRenderer.scaledFor` reads\n * the current transform.\n */\n drawWidth: number;\n drawHeight: number;\n}\n\nexport type RenderCommand = PrimitiveCommand | GlyphCommand | ImageCommand;\n\n/**\n * One line of text the frame drew, as a whole.\n *\n * The GPU draws glyphs, not runs, so nothing in the pipeline reads\n * this — it is the record of *what text went where*, kept because the\n * question \"did this frame draw the right words in the right boxes\" is\n * the one parity tests and the profiler ask, and per-glyph instances\n * are a bad place to ask it. Building it is cheaper than the\n * per-run `TextRenderItem` it replaces, which mapped every line.\n */\nexport interface TextRunDraw {\n text: string;\n /** Canvas font shorthand. */\n font: string;\n /** CSS color string. */\n color: string;\n /** Line start and alphabetic baseline in logical screen pixels. */\n x: number;\n y: number;\n /** Measured line width and line-box height, logical pixels. */\n width: number;\n height: number;\n opacity: number;\n /** Index of the run's first glyph instance. */\n instance: number;\n /** Glyph instances emitted for it; zero when every cluster is blank. */\n glyphs: number;\n}\n\nexport interface RenderList {\n instanceData: Float32Array;\n instanceCount: number;\n texturedData: Float32Array;\n texturedCount: number;\n /** The frame's rounded clips, CLIP_STRIDE_FLOATS each; see the layout above. */\n clipData: Float32Array;\n clipCount: number;\n /** The frame's gradients, GRADIENT_STRIDE_FLOATS each. */\n gradientData: Float32Array;\n gradientCount: number;\n commands: RenderCommand[];\n /** The lines of text the frame drew, in paint order. */\n textRuns: TextRunDraw[];\n}\n\ninterface CullRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface RenderState {\n opacity: number;\n /**\n * Current transform from absolute layout coordinates to logical\n * screen pixels: ancestors' own transforms, sticky shifts and scroll\n * translations. Children inherit it.\n */\n ctm: Affine;\n /** Current rectangular clip as a physical-pixel scissor. */\n clip: ScissorRect | null;\n /** Innermost rounded clip in the chain, or NO_CLIP_INDEX. */\n rounded: number;\n /**\n * Visible region in the coordinate space children's records use,\n * or null while culling is suspended under a transform or a sticky\n * shift, which move what is drawn away from where records say it is.\n */\n cull: CullRect | null;\n}\n\nconst IDENTITY: Affine = [1, 0, 0, 1, 0, 0];\n\nconst paintScratch = createPaintState();\nconst contentBox: LayoutBox = { x: 0, y: 0, width: 0, height: 0 };\n\n/**\n * A growable float buffer for instance data.\n *\n * The builder used to collect instances in a plain `number[]` and\n * convert at the end. That is fine at a few hundred instances and it\n * is not fine at a few thousand: the atlas emits one instance per\n * letter on screen, and both the boxed pushes and the element-by-\n * element conversion showed up as the largest cost in the frame. This\n * writes floats where they are going to live, and the buffer is\n * module-level scratch — like `paintScratch` above — because a build\n * is not re-entrant and a per-frame allocation is the thing being\n * avoided.\n */\nclass FloatBuffer {\n data: Float32Array;\n length = 0;\n\n constructor(capacity: number) {\n this.data = new Float32Array(capacity);\n }\n\n reset(): void {\n this.length = 0;\n }\n\n /** Room for `count` more floats, doubling until there is. */\n ensure(count: number): void {\n const needed = this.length + count;\n if (needed <= this.data.length) {\n return;\n }\n let capacity = this.data.length * 2;\n while (capacity < needed) {\n capacity *= 2;\n }\n const grown = new Float32Array(capacity);\n grown.set(this.data.subarray(0, this.length));\n this.data = grown;\n }\n\n /** Appends four floats; the clip chain is written a vec4 at a time. */\n push4(a: number, b: number, c: number, d: number): void {\n this.ensure(4);\n const at = this.length;\n this.data[at] = a;\n this.data[at + 1] = b;\n this.data[at + 2] = c;\n this.data[at + 3] = d;\n this.length = at + 4;\n }\n\n /** A copy of what was written, for the frame to own. */\n take(): Float32Array {\n return this.data.slice(0, this.length);\n }\n}\n\nconst instanceScratch = new FloatBuffer(INSTANCE_STRIDE_FLOATS * 512);\nconst texturedScratch = new FloatBuffer(TEXTURED_STRIDE_FLOATS * 2048);\nconst clipScratch = new FloatBuffer(CLIP_STRIDE_FLOATS * 64);\nconst gradientScratch = new FloatBuffer(GRADIENT_STRIDE_FLOATS * 16);\n\n/**\n * Everything the builder keeps between frames: the glyph atlas it\n * allocates cells in and the shaper that positions clusters within a\n * line. Both are caches, and both are the renderer's to own — a\n * builder that made its own each frame would rasterise every glyph\n * again on every frame.\n */\nexport interface RenderTextCache {\n atlas: WebGPUGlyphAtlas;\n shaper: GlyphShaper;\n}\n\n/** A cache for one-shot builds — tests, and the first frame. */\nexport function createTextCache(): RenderTextCache {\n return { atlas: new WebGPUGlyphAtlas(), shaper: new GlyphShaper() };\n}\n\n/**\n * Builds a CPU-side render list from the retained UI tree.\n *\n * One ordered command list holds fills, borders, glyphs and images in\n * paint order — background, image, picture, border, decorations, children,\n * text, the decorations marked `after: 'children'`, then the node's\n * scrollbars — exactly as the Canvas2D renderer paints them.\n * Primitive instances are batched into one command until the scissor\n * changes, and glyph instances likewise until the scissor or the atlas\n * page changes; an image is one command, because it binds its own\n * texture.\n */\nexport function buildRenderList(\n root: UiNode,\n layout: { recordFor(node: UiNode): LayoutRecord | undefined },\n measurer: TextMeasurer,\n logicalWidth: number,\n logicalHeight: number,\n dpr: number,\n now: number = typeof performance !== 'undefined' ? performance.now() : Date.now(),\n overlay: readonly OverlayShape[] = [],\n textCache: RenderTextCache = createTextCache()\n): RenderList {\n const instanceData = instanceScratch;\n const texturedData = texturedScratch;\n const clipData = clipScratch;\n const gradientData = gradientScratch;\n instanceData.reset();\n texturedData.reset();\n clipData.reset();\n gradientData.reset();\n const commands: RenderCommand[] = [];\n const textRuns: TextRunDraw[] = [];\n const { atlas, shaper } = textCache;\n\n /** Primitive instances emitted since the last primitive command was closed. */\n let openStart = 0;\n let openScissor: ScissorRect | null = null;\n\n const state: RenderState = {\n opacity: 1,\n ctm: [...IDENTITY] as Affine,\n clip: null,\n rounded: NO_CLIP_INDEX,\n cull: { x: 0, y: 0, width: logicalWidth, height: logicalHeight }\n };\n\n /** The nodes asking for `lift`, met on the walk and drawn after it. */\n const liftedPass: { node: UiNode; opacity: number; ctm: Affine }[] = [];\n\n function closePrimitives(): void {\n const end = instanceData.length / INSTANCE_STRIDE_FLOATS;\n if (end > openStart) {\n commands.push({ kind: CommandKind.Primitives, start: openStart, end, scissor: openScissor });\n }\n openStart = end;\n }\n\n function beginPrimitives(scissor: ScissorRect | null): void {\n if (openStart === instanceData.length / INSTANCE_STRIDE_FLOATS) {\n openScissor = scissor;\n return;\n }\n if (!scissorsEqual(openScissor, scissor)) {\n closePrimitives();\n openScissor = scissor;\n }\n }\n\n /**\n * Emits one glyph instance per inked cluster of every line.\n *\n * The lines are the ones Canvas2D draws, in the coordinate space\n * `originX`/`originY` translates into absolute layout coordinates.\n * Each glyph is snapped so its cell lands on whole physical pixels —\n * the horizontal remainder becomes the atlas's subpixel phase rather\n * than being thrown away — and instances that share an atlas page\n * and a scissor are merged into one command, so a screen of text in\n * one font is one draw call.\n */\n function pushGlyphs(options: {\n lines: readonly TextLinePlacement[];\n font: string;\n color: string;\n fontSize: number;\n measureRun: MeasureRun;\n /** The paragraph's base direction; see `GlyphShaper.shape`. */\n rtl: boolean;\n originX: number;\n originY: number;\n ctm: Affine;\n opacity: number;\n rounded: number;\n scissor: ScissorRect | null;\n /** The paragraph's runs and their colours; absent for text in one style. */\n paint?: SpanPaint;\n /** The request the lines were measured with, for a run's own font. */\n request?: TextMeasureRequest;\n }): void {\n const { lines, font, color, fontSize, originX, originY, ctm, scissor, rtl } = options;\n const opacity = options.opacity;\n const rounded = options.rounded;\n const style = atlas.styleFor(font, color, fontSize, dpr, rtl);\n // A paragraph with runs draws one piece per run, each in its own\n // font, colour and size, at the x the paragraph measured for it.\n // The atlas is keyed by font and colour already, so a run is a new\n // style and nothing else; the rest of this walk is the one it was.\n const paint = options.paint;\n const spanned = options.request === undefined ? undefined : spannedRunsFor(options.request, measurer);\n let runRequest = options.request;\n const spanMeasure: MeasureRun = run =>\n run.length === 0 || runRequest === undefined ? 0 : measurer.measureRunWidth(run, runRequest);\n for (const line of lines) {\n if (line.text.length === 0) {\n continue;\n }\n const baseline = originY + line.baselineY;\n if (line.runs !== undefined && paint !== undefined && spanned !== undefined) {\n for (const piece of line.runs) {\n if (piece.text.length === 0) {\n continue;\n }\n const span = spanOf(paint, piece.span);\n const pieceFont = runFontString(options.request!, span);\n const pieceColor = colorToCss(span?.color ?? paint.textColor);\n runRequest = spanned.requestAt(piece.start);\n pushPiece(\n piece.text,\n originX + piece.x,\n baseline,\n piece.width,\n line.height,\n pieceFont,\n pieceColor,\n atlas.styleFor(pieceFont, pieceColor, span?.fontSize ?? fontSize, dpr, rtl),\n spanMeasure\n );\n }\n continue;\n }\n pushPiece(line.text, originX + line.x, baseline, line.width, line.height, font, color, style, options.measureRun);\n }\n\n /** One stretch of glyphs in one font and colour: a whole line, or a run of one. */\n function pushPiece(\n pieceText: string,\n lineX: number,\n baseline: number,\n width: number,\n height: number,\n pieceFont: string,\n pieceColor: string,\n pieceStyle: GlyphStyle,\n measure: MeasureRun\n ): void {\n const [runScreenX, runScreenY] = applyTransform(ctm, lineX, baseline);\n const run: TextRunDraw = {\n text: pieceText,\n font: pieceFont,\n color: pieceColor,\n x: runScreenX,\n y: runScreenY,\n width,\n height,\n opacity: options.opacity,\n instance: texturedData.length / TEXTURED_STRIDE_FLOATS,\n glyphs: 0\n };\n textRuns.push(run);\n\n for (const cluster of shaper.shape(pieceText, pieceFont, width, measure, rtl)) {\n if (cluster.blank) {\n continue;\n }\n const penX = lineX + cluster.x;\n // Inlined rather than via applyTransform: this loop runs once\n // per letter on screen, and a returned pair would be an\n // allocation per letter per frame.\n const screenX = penX * ctm[0] + baseline * ctm[2] + ctm[4];\n const screenY = penX * ctm[1] + baseline * ctm[3] + ctm[5];\n const physicalX = screenX * dpr;\n const wholeX = Math.floor(physicalX);\n const slot = atlas.slotFor(pieceStyle, cluster.text, cluster.advance, phaseFor(physicalX - wholeX));\n if (slot === null) {\n continue;\n }\n closePrimitives();\n // Shifting the translation rather than the rectangle keeps the\n // instance in layout coordinates, as every other instance is,\n // and lands the cell on whole physical pixels.\n const instance = pushTextured(\n texturedData,\n penX + slot.offsetX,\n baseline + slot.offsetY,\n slot.width,\n slot.height,\n opacity,\n ctm,\n rounded,\n slot,\n wholeX / dpr - screenX,\n Math.round(screenY * dpr) / dpr - screenY\n );\n const last = commands[commands.length - 1];\n if (\n last !== undefined &&\n last.kind === CommandKind.Glyphs &&\n last.page === slot.page &&\n last.end === instance &&\n scissorsEqual(last.scissor, scissor)\n ) {\n last.end = instance + 1;\n } else {\n commands.push({ kind: CommandKind.Glyphs, start: instance, end: instance + 1, page: slot.page, scissor });\n }\n run.glyphs++;\n }\n }\n }\n\n function intersectsCull(cull: CullRect, x: number, y: number, width: number, height: number): boolean {\n if (cull.width <= 0 || cull.height <= 0) {\n return false;\n }\n return x < cull.x + cull.width && cull.x < x + width && y < cull.y + cull.height && cull.y < y + height;\n }\n\n function visitChildren(node: UiNode, order: readonly UiNode[] | null): void {\n if (order !== null) {\n // zIndex reordered these children; fragments are already expanded.\n for (const child of order) {\n visit(child);\n }\n return;\n }\n let child = node.firstChild;\n while (child !== null) {\n if (child.type === UiNodeType.Fragment) {\n // Fragments are transparent anchors with no record of their own.\n visitChildren(child, null);\n } else {\n visit(child);\n }\n child = child.nextSibling;\n }\n }\n\n function visit(node: UiNode, lifted = false): void {\n const rec = layout.recordFor(node);\n if (rec === undefined) {\n return;\n }\n\n // Visibility from its own two properties rather than from the paint\n // state, and the cull test from the record alone, so that a node\n // this walk is about to discard never pays for the twenty-five or so\n // property resolutions the full state costs. A long list culls most\n // of what it walks, which is where that adds up.\n if (!isPaintVisible(node)) {\n return;\n }\n\n if (rec.lifted && !lifted) {\n // Held for the top layer, with the two things a lifted node keeps\n // of its ancestors: their accumulated transform, so it is drawn\n // where it belongs and scrolls with what it belongs to, and their\n // opacity. Their clips are exactly what it is escaping. Captured\n // before the cull test, because a lifted node is nearly always\n // somewhere its record box is not.\n liftedPass.push({ node, opacity: state.opacity, ctm: state.ctm });\n return;\n }\n\n const sticky = rec.stickyOffsetX !== 0 || rec.stickyOffsetY !== 0;\n if (\n state.cull !== null &&\n !intersectsCull(state.cull, rec.x + rec.stickyOffsetX, rec.y + rec.stickyOffsetY, rec.width, rec.height)\n ) {\n return;\n }\n\n const paint = resolvePaintState(node, paintScratch);\n const effectiveOpacity = state.opacity * paint.opacity;\n // A sticky node (and its children) is shifted to its scroll\n // container's edge; the record keeps the flow position.\n const baseCtm = sticky ? translateTransform(state.ctm, rec.stickyOffsetX, rec.stickyOffsetY) : state.ctm;\n const nodeCtm = paint.hasTransform ? multiplyTransform(baseCtm, buildOwnTransform(rec, paint.transform)) : baseCtm;\n // The node's own background, image, border and scrollbars are\n // clipped by its ancestors, as Canvas2D paints them outside the\n // node's own clip; children and the node's own text go under it.\n const ownScissor = state.clip;\n const ownRounded = state.rounded;\n\n // Clipping (overflow hidden/scroll/auto, ScrollView). The rectangle\n // becomes a scissor; a corner radius becomes the rounded clip the\n // shader evaluates for every descendant instance.\n let nextClip = state.clip;\n let nextRounded = state.rounded;\n if (rec.clips) {\n // The clip lives in the container's own (pre-scroll) space, so it\n // is transformed by the container's CTM, not the scrolled one its\n // children inherit.\n const viewportScissor = logicalClipToScissor(rec, nodeCtm, logicalWidth, logicalHeight, dpr);\n nextClip =\n viewportScissor === null\n ? emptyScissor()\n : state.clip === null\n ? viewportScissor\n : intersectScissors(state.clip, viewportScissor);\n if (!borderRadiusIsZero(paint.borderRadius)) {\n const inverse = invertTransform(nodeCtm);\n if (inverse !== null) {\n // A new node in the chain whose parent is the enclosing rounded\n // clip, so descendants are tested against both.\n nextRounded = clipData.length / CLIP_STRIDE_FLOATS;\n clipData.push4(rec.x, rec.y, rec.width, rec.height);\n clipData.push4(uniformBorderRadius(paint.borderRadius), state.rounded, 0, 0);\n clipData.push4(inverse[0], inverse[1], inverse[2], inverse[3]);\n clipData.push4(inverse[4], inverse[5], 0, 0);\n }\n }\n }\n\n const contentScissor = nextClip;\n const contentRounded = nextRounded;\n\n // Background fill.\n if (paint.backgroundColor !== undefined) {\n const color = parseColor(paint.backgroundColor);\n if (color !== undefined) {\n beginPrimitives(ownScissor);\n pushInstance(\n instanceData,\n rec.x,\n rec.y,\n rec.width,\n rec.height,\n color,\n uniformBorderRadius(paint.borderRadius),\n effectiveOpacity,\n 0,\n PrimitiveKind.Fill,\n nodeCtm,\n ownRounded\n );\n }\n }\n\n // Background gradient, over the colour and under the image, where\n // CSS paints a `background-image`. It is an ordinary fill instance\n // naming a record in the frame's gradient buffer; the fragment\n // shader evaluates the ramp at each pixel's place in the box, so\n // the same instance carries the same rounded corners and the same\n // clip chain as a flat one.\n if (paint.backgroundGradient !== undefined) {\n const index = pushGradient(gradientData, paint.backgroundGradient, rec.width, rec.height);\n if (index !== NO_GRADIENT_INDEX) {\n beginPrimitives(ownScissor);\n // The instance's colour is unread when it names a gradient; the\n // first stop makes a dumped instance buffer legible.\n const first = paint.backgroundGradient.stops[0].color;\n pushInstance(\n instanceData,\n rec.x,\n rec.y,\n rec.width,\n rec.height,\n first,\n uniformBorderRadius(paint.borderRadius),\n effectiveOpacity,\n 0,\n PrimitiveKind.Fill,\n nodeCtm,\n ownRounded,\n index\n );\n }\n }\n\n // Background image, fitted into the box and clipped to it as CSS\n // clips `object-fit`: `cover` and `none` overflow the box, and only\n // the box shows. The box is a scissor, plus a clip-chain node when\n // it has a radius.\n // A video draws through the same path as a still — same fit, same\n // clip, same textured quad — and wins on a node that has both.\n const textureSource: TextureSource | undefined =\n paint.video !== undefined && paint.video.frame !== null ? paint.video : paint.image;\n if (textureSource !== undefined) {\n const sourceSize =\n paint.video !== undefined && paint.video.frame !== null\n ? videoFrameSize(paint.video)\n : { width: paint.image!.width, height: paint.image!.height };\n const rect = computeObjectFitRect(paint.objectFit, sourceSize.width, sourceSize.height, rec);\n if (rect.width > 0 && rect.height > 0) {\n const overflows =\n rect.x < rec.x ||\n rect.y < rec.y ||\n rect.x + rect.width > rec.x + rec.width ||\n rect.y + rect.height > rec.y + rec.height;\n const rounded = !borderRadiusIsZero(paint.borderRadius);\n let imageScissor = ownScissor;\n let imageRounded = ownRounded;\n if (overflows || rounded) {\n const boxScissor = logicalClipToScissor(rec, nodeCtm, logicalWidth, logicalHeight, dpr);\n imageScissor =\n boxScissor === null\n ? emptyScissor()\n : ownScissor === null\n ? boxScissor\n : intersectScissors(ownScissor, boxScissor);\n }\n if (rounded) {\n const inverse = invertTransform(nodeCtm);\n if (inverse !== null) {\n imageRounded = clipData.length / CLIP_STRIDE_FLOATS;\n clipData.push4(rec.x, rec.y, rec.width, rec.height);\n clipData.push4(uniformBorderRadius(paint.borderRadius), ownRounded, 0, 0);\n clipData.push4(inverse[0], inverse[1], inverse[2], inverse[3]);\n clipData.push4(inverse[4], inverse[5], 0, 0);\n }\n }\n closePrimitives();\n const instance = pushTextured(\n texturedData,\n rect.x,\n rect.y,\n rect.width,\n rect.height,\n effectiveOpacity,\n nodeCtm,\n imageRounded,\n FULL_TEXTURE\n );\n // The quad's size in device pixels, from the node's transform.\n // `nodeCtm` is a 2x3 in column-major order, so the two column\n // lengths are the axis scales.\n const scaleX = Math.hypot(nodeCtm[0], nodeCtm[1]) * dpr;\n const scaleY = Math.hypot(nodeCtm[2], nodeCtm[3]) * dpr;\n commands.push({\n kind: CommandKind.Image,\n instance,\n scissor: imageScissor,\n source: textureSource,\n drawWidth: rect.width * scaleX,\n drawHeight: rect.height * scaleY\n });\n }\n }\n\n /**\n * A painted node's picture: one textured quad the size of the box,\n * where the Canvas2D backend puts its one `drawImage`.\n *\n * The bitmap is the same object that backend draws, from the same\n * cache, so the two cannot disagree about what a painter drew.\n * What remains for the parity gate to check is what a backend\n * could still get wrong on its own: the box, the place in paint\n * order, the clip and the opacity. See `PaintPicture.ts`.\n */\n if (node.type === UiNodeType.Paint) {\n const picture = paintPictures.pictureFor(node, rec, dpr);\n if (picture !== undefined) {\n closePrimitives();\n const instance = pushTextured(\n texturedData,\n rec.x,\n rec.y,\n rec.width,\n rec.height,\n effectiveOpacity,\n nodeCtm,\n ownRounded,\n FULL_TEXTURE\n );\n commands.push({\n kind: CommandKind.Image,\n instance,\n scissor: ownScissor,\n source: picture,\n // The picture was rasterised at exactly this size, so the\n // texture cache has nothing to gain by copying it smaller.\n drawWidth: rec.width * dpr,\n drawHeight: rec.height * dpr\n });\n }\n }\n\n // Border stroke.\n if (paint.borderWidth > 0 && paint.borderColor !== undefined) {\n const color = parseColor(paint.borderColor);\n if (color !== undefined) {\n beginPrimitives(ownScissor);\n pushInstance(\n instanceData,\n rec.x,\n rec.y,\n rec.width,\n rec.height,\n color,\n uniformBorderRadius(paint.borderRadius),\n effectiveOpacity,\n paint.borderWidth,\n PrimitiveKind.Border,\n nodeCtm,\n ownRounded\n );\n }\n }\n\n /**\n * The node's modifier decorations, as fill and border instances at\n * the node's own place in paint order.\n *\n * Under `ownScissor` / `ownRounded` — its ancestors' clips — and\n * not under its own, exactly where the background and border go,\n * because a ring with an outset lies entirely outside the box that\n * would clip it. The rectangle and the colour come from\n * `Decorations.ts`, which is the same arithmetic Canvas2D uses.\n */\n const pushDecorations = (shapes: readonly DecorationShape[], after: boolean): void => {\n const nodeRadius = uniformBorderRadius(paint.borderRadius);\n for (const shape of shapes) {\n if ((shape.after === 'children') !== after) {\n continue;\n }\n const resolved = decorationColor(node, shape);\n const color = resolved === undefined ? undefined : parseColor(resolved);\n if (color === undefined) {\n continue;\n }\n const rect = decorationRect(shape, rec, nodeRadius);\n if (rect.width <= 0 || rect.height <= 0) {\n continue;\n }\n if (shape.kind === 'stroke' && shape.lineWidth <= 0) {\n continue;\n }\n beginPrimitives(ownScissor);\n pushInstance(\n instanceData,\n rect.x,\n rect.y,\n rect.width,\n rect.height,\n color,\n rect.radius,\n effectiveOpacity,\n shape.kind === 'stroke' ? shape.lineWidth : 0,\n shape.kind === 'stroke' ? PrimitiveKind.Border : PrimitiveKind.Fill,\n nodeCtm,\n ownRounded\n );\n }\n };\n\n const decorations = node.decorations;\n if (decorations !== null && hasDecorationPhase(decorations, false)) {\n pushDecorations(decorations, false);\n }\n\n // Captured before children reuse the shared scratch below. An\n // editable always has foreground work: its caret and placeholder.\n const hasText = paint.editor !== undefined || (paint.text !== undefined && paint.text.length > 0);\n\n // Children. A boundary drains whatever they lifted once they are\n // done, so `lift` reaches the top of this subtree and no further.\n let liftedFrom = liftedPass.length;\n if (node.hasChildren()) {\n const saved: RenderState = {\n opacity: state.opacity,\n ctm: state.ctm,\n clip: state.clip,\n rounded: state.rounded,\n cull: state.cull\n };\n state.opacity = effectiveOpacity;\n state.ctm = nodeCtm;\n state.clip = nextClip;\n state.rounded = nextRounded;\n if (rec.clips && state.cull !== null) {\n state.cull = pushCull(state.cull, rec);\n }\n if (paint.hasTransform || sticky) {\n // Culling works in record coordinates; a transform or a sticky\n // shift moves what is drawn away from them.\n state.cull = null;\n }\n // Scroll containers translate their content by the scroll offset.\n if (rec.scrollable) {\n state.ctm = translateTransform(state.ctm, -rec.scrollX, -rec.scrollY);\n }\n\n liftedFrom = liftedPass.length;\n visitChildren(node, rec.paintOrder);\n\n state.opacity = saved.opacity;\n state.ctm = saved.ctm;\n state.clip = saved.clip;\n state.rounded = saved.rounded;\n state.cull = saved.cull;\n }\n\n // Foreground text, after children as Canvas2D paints it. Children\n // reused the paint scratch, so the node's own style is re-resolved.\n if (hasText) {\n const text = resolvePaintState(node, paintScratch);\n contentBox.x = 0;\n contentBox.y = 0;\n contentBox.width = Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight);\n contentBox.height = Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom);\n // A field scrolls its own text inside its box, as Canvas2D paints\n // it; the offset is zero for every other kind of text.\n const offsetX = rec.x + rec.paddingLeft - rec.scrollX;\n const offsetY = rec.y + rec.paddingTop - rec.scrollY;\n\n const font = buildFontString(text);\n // Prefix widths come from the measurer that produced the lines,\n // so cluster positions and the line width agree exactly. The\n // request's font is all `measureRunWidth` reads of it.\n const measureRequest = textMeasureRequest(text.text ?? '', text, contentBox);\n const measureRun: MeasureRun = run => (run.length === 0 ? 0 : measurer.measureRunWidth(run, measureRequest));\n\n /** One run of placed lines, in content-box coordinates. */\n const pushTextRun = (lines: readonly TextLinePlacement[], color: string): void => {\n pushGlyphs({\n lines,\n font,\n color,\n fontSize: text.fontSize,\n measureRun,\n rtl: text.rtl,\n originX: offsetX,\n originY: offsetY,\n ctm: nodeCtm,\n opacity: effectiveOpacity,\n rounded: contentRounded,\n scissor: contentScissor,\n paint: text.spans === undefined ? undefined : text,\n request: measureRequest\n });\n };\n\n /** The rectangles a paragraph's runs and decorations draw as fills. */\n const pushTextRects = (rects: readonly TextRunRect[]): void => {\n for (const rect of rects) {\n const color = parseColor(rect.color);\n if (color !== undefined) {\n pushContentRect(rect, color);\n }\n }\n };\n\n /** A plain rectangle in the content box, clipped like the text. */\n const pushContentRect = (box: LayoutBox, color: RgbaColor): void => {\n beginPrimitives(contentScissor);\n pushInstance(\n instanceData,\n offsetX + box.x,\n offsetY + box.y,\n box.width,\n box.height,\n color,\n 0,\n effectiveOpacity,\n 0,\n PrimitiveKind.Fill,\n nodeCtm,\n contentRounded\n );\n };\n\n if (text.editor !== undefined) {\n // An editable, in the order Canvas2D paints it: selection, text\n // or placeholder, composition underline, caret.\n const model = text.editor;\n const editable = new EditableLayout(model, contentBox, text, measurer);\n if (model.focused && !model.collapsed) {\n const selection = parseColor(text.selectionColor);\n if (selection !== undefined) {\n for (const box of editable.selectionBoxes()) {\n pushContentRect(box, selection);\n }\n }\n }\n if (editable.placeholderLines.length > 0) {\n pushTextRun(editable.placeholderLines, colorToCss(text.placeholderColor));\n } else {\n pushTextRun(editable.lines, colorToCss(text.textColor));\n }\n if (model.composing) {\n const underline = parseColor(text.textColor);\n if (underline !== undefined) {\n const line = editable.lines[lineIndexForOffset(editable.lines, model.composition!.start)];\n for (const box of editable.compositionBoxes()) {\n pushContentRect({ x: box.x, y: Math.round(line.baselineY + 1), width: box.width, height: 1 }, underline);\n }\n }\n }\n if (model.focused && model.collapsed && caretVisibleAt(model, now)) {\n const caretColor = parseColor(text.caretColor);\n if (caretColor !== undefined) {\n const caret = editable.caretRect();\n pushContentRect(\n { x: Math.round(caret.x), y: caret.y, width: CARET_WIDTH, height: caret.height },\n caretColor\n );\n }\n }\n } else {\n const lines = layoutTextLines(contentBox, text, measurer);\n // A run's own background is under the highlights, as a CSS\n // inline background is under a selection.\n pushTextRects(textRunBackgrounds(lines, text));\n if (text.textMatches !== undefined || text.textSelection !== undefined) {\n // Static text with part of it highlighted, in the order\n // Canvas2D paints it: find matches, the selection over them,\n // then the run. The lines are the ones laid out for the run.\n const geometry = paragraphGeometryFrom(lines, text.text!, contentBox, text, measurer);\n const match = text.textMatches === undefined ? undefined : parseColor(text.matchColor);\n if (match !== undefined) {\n for (const range of text.textMatches!) {\n for (const box of selectionRectsIn(geometry, range.start, range.end)) {\n pushContentRect(box, match);\n }\n }\n }\n const selection = text.textSelection === undefined ? undefined : parseColor(text.selectionColor);\n if (selection !== undefined) {\n for (const box of selectionRectsIn(geometry, text.textSelection!.start, text.textSelection!.end)) {\n pushContentRect(box, selection);\n }\n }\n }\n pushTextRun(lines, colorToCss(text.textColor));\n pushTextRects(textRunDecorations(lines, text));\n }\n }\n\n if (decorations !== null && hasDecorationPhase(decorations, true)) {\n // Children reused the paint scratch; the node's own radius and\n // opacity are read from it, so re-resolve before the second pass.\n resolvePaintState(node, paintScratch);\n pushDecorations(decorations, true);\n }\n\n if (rec.liftBoundary) {\n // Inside this node's own clip, over everything under it, and\n // still under whatever comes after this node.\n drawLifted(liftedFrom, nextClip, nextRounded);\n }\n\n if (rec.scrollable) {\n beginPrimitives(ownScissor);\n pushScrollbars(instanceData, rec, effectiveOpacity, nodeCtm, ownRounded, now);\n }\n }\n\n /**\n * The top layer, drawn once the tree is done: no clip, no scissor,\n * no cull, and after every command the walk emitted, which is the\n * whole of what `lift` promises. The list is walked by index rather\n * than iterated, so a lifted node inside a lifted subtree lands\n * after the one that carried it, as paint order says it should.\n */\n function drawLifted(from = 0, clip: ScissorRect | null = null, rounded: number = NO_CLIP_INDEX): void {\n if (liftedPass.length <= from) {\n return;\n }\n const saved: RenderState = {\n opacity: state.opacity,\n ctm: state.ctm,\n clip: state.clip,\n rounded: state.rounded,\n cull: state.cull\n };\n for (let i = from; i < liftedPass.length; i++) {\n const entry = liftedPass[i]!;\n state.opacity = entry.opacity;\n state.ctm = entry.ctm;\n // The boundary's own clip still applies; everything inside it\n // does not, which is the whole of what `lift` escapes.\n state.clip = clip;\n state.rounded = rounded;\n state.cull = null;\n visit(entry.node, true);\n }\n liftedPass.length = from;\n state.opacity = saved.opacity;\n state.ctm = saved.ctm;\n state.clip = saved.clip;\n state.rounded = saved.rounded;\n state.cull = saved.cull;\n }\n\n visit(root);\n drawLifted();\n closePrimitives();\n\n // The debugging overlay: unclipped, untransformed, over everything.\n for (const shape of overlay) {\n switch (shape.kind) {\n case 'fill': {\n const color = cssColor(shape.color);\n if (color !== undefined) {\n beginPrimitives(null);\n pushInstance(\n instanceData,\n shape.x,\n shape.y,\n shape.width,\n shape.height,\n color,\n 0,\n 1,\n 0,\n PrimitiveKind.Fill,\n IDENTITY,\n NO_CLIP_INDEX\n );\n }\n break;\n }\n case 'stroke': {\n const color = cssColor(shape.color);\n if (color !== undefined) {\n beginPrimitives(null);\n pushInstance(\n instanceData,\n shape.x,\n shape.y,\n shape.width,\n shape.height,\n color,\n 0,\n 1,\n shape.lineWidth,\n PrimitiveKind.Border,\n IDENTITY,\n NO_CLIP_INDEX\n );\n }\n break;\n }\n case 'label': {\n const labelBox = { x: 0, y: 0, width: 0, height: shape.height };\n const labelState = {\n ...createPaintState(),\n text: shape.text,\n fontSize: shape.fontSize,\n fontFamily: shape.fontFamily,\n lineHeight: shape.height,\n textWrap: 'none' as const,\n verticalAlign: 'middle' as const\n };\n const lines = layoutTextLines(labelBox, labelState, measurer);\n if (lines.length === 0) {\n break;\n }\n const width = lines[0].width + 2 * LABEL_PADDING_X;\n const origin = labelOrigin(shape);\n const background = cssColor(shape.background);\n if (background !== undefined) {\n beginPrimitives(null);\n pushInstance(\n instanceData,\n origin.x,\n origin.y,\n width,\n shape.height,\n background,\n 0,\n 1,\n 0,\n PrimitiveKind.Fill,\n IDENTITY,\n NO_CLIP_INDEX\n );\n }\n const labelRequest = textMeasureRequest(shape.text, labelState, labelBox);\n pushGlyphs({\n lines,\n font: shape.font,\n color: shape.textColor,\n // Overlay labels (the inspector's) are the framework's own text.\n rtl: false,\n fontSize: shape.fontSize,\n measureRun: run => (run.length === 0 ? 0 : measurer.measureRunWidth(run, labelRequest)),\n originX: origin.x + LABEL_PADDING_X,\n originY: origin.y,\n ctm: IDENTITY,\n opacity: 1,\n rounded: NO_CLIP_INDEX,\n scissor: null\n });\n break;\n }\n }\n }\n closePrimitives();\n\n return {\n instanceData: instanceData.take(),\n instanceCount: instanceData.length / INSTANCE_STRIDE_FLOATS,\n texturedData: texturedData.take(),\n texturedCount: texturedData.length / TEXTURED_STRIDE_FLOATS,\n clipData: clipData.take(),\n clipCount: clipData.length / CLIP_STRIDE_FLOATS,\n gradientData: gradientData.take(),\n gradientCount: gradientData.length / GRADIENT_STRIDE_FLOATS,\n commands,\n textRuns\n };\n}\n\nfunction cssColor(css: string): RgbaColor | undefined {\n const color = normalizeColor(css);\n return color === undefined ? undefined : parseColor(color);\n}\n\n/**\n * The lines of text a render list draws, in paint order. For tests and\n * the profiler; the renderer walks `commands` directly.\n */\nexport function textRuns(list: RenderList): readonly TextRunDraw[] {\n return list.textRuns;\n}\n\n/** Glyph instances a render list draws, across every glyph command. */\nexport function glyphCount(list: RenderList): number {\n let count = 0;\n for (const command of list.commands) {\n if (command.kind === CommandKind.Glyphs) {\n count += command.end - command.start;\n }\n }\n return count;\n}\n\n/**\n * Overlay scrollbars as fill primitives, from the shared geometry the\n * Canvas2D renderer draws and the hit tester grabs.\n */\nfunction pushScrollbars(\n out: FloatBuffer,\n rec: LayoutRecord,\n opacity: number,\n transform: Affine,\n rounded: number,\n now: number\n): void {\n const remaining = rec.scrollbarVisibleUntil - now;\n if (remaining <= 0) {\n return;\n }\n const alpha = Math.min(1, remaining / SCROLLBAR_FADE_MS) * 0.55;\n const color: RgbaColor = { r: 0.5, g: 0.5, b: 0.5, a: alpha };\n const { vertical, horizontal } = scrollbarThumbs(rec);\n for (const bar of [vertical, horizontal]) {\n if (bar === null) {\n continue;\n }\n const { x, y, width, height } = bar.thumb;\n pushInstance(\n out,\n x,\n y,\n width,\n height,\n color,\n SCROLLBAR_THICKNESS / 2,\n opacity,\n 0,\n PrimitiveKind.Fill,\n transform,\n rounded\n );\n }\n}\n\nfunction pushInstance(\n out: FloatBuffer,\n x: number,\n y: number,\n width: number,\n height: number,\n color: RgbaColor,\n radius: number,\n opacity: number,\n borderWidth: number,\n kind: PrimitiveKind,\n transform: Affine,\n clip: number,\n gradient: number = NO_GRADIENT_INDEX\n): void {\n out.ensure(INSTANCE_STRIDE_FLOATS);\n const data = out.data;\n const at = out.length;\n data[at] = x;\n data[at + 1] = y;\n data[at + 2] = width;\n data[at + 3] = height;\n data[at + 4] = color.r;\n data[at + 5] = color.g;\n data[at + 6] = color.b;\n data[at + 7] = color.a;\n data[at + 8] = radius;\n data[at + 9] = opacity;\n data[at + 10] = borderWidth;\n data[at + 11] = kind;\n data[at + 12] = transform[0];\n data[at + 13] = transform[1];\n data[at + 14] = transform[2];\n data[at + 15] = transform[3];\n data[at + 16] = transform[4];\n data[at + 17] = transform[5];\n data[at + 18] = clip;\n data[at + 19] = gradient;\n out.length = at + INSTANCE_STRIDE_FLOATS;\n}\n\n/**\n * Writes one gradient record for a rectangle of the given size and\n * returns its index, or NO_GRADIENT_INDEX when it would paint nothing.\n *\n * `gradientPaint` is the shared placement the Canvas2D renderer calls\n * too, so the gradient line and the stop positions are one piece of\n * arithmetic rather than two that have to agree.\n */\nfunction pushGradient(out: FloatBuffer, gradient: ResolvedGradient, width: number, height: number): number {\n const placed = gradientPaint(gradient, width, height);\n if (placed.kind === 'radial' && !(placed.radius > 0)) {\n return NO_GRADIENT_INDEX;\n }\n if (placed.stops.length > MAX_GRADIENT_STOPS) {\n // `resolveGradient` already rejected this; the guard is here so a\n // future caller cannot quietly write past the end of a record.\n throw new Error(`A gradient may carry at most ${MAX_GRADIENT_STOPS} stops, got ${placed.stops.length}.`);\n }\n out.ensure(GRADIENT_STRIDE_FLOATS);\n const data = out.data;\n const at = out.length;\n // The scratch is reused between frames, so the unwritten stop slots\n // hold whatever the last frame put there.\n data.fill(0, at, at + GRADIENT_STRIDE_FLOATS);\n data[at] = placed.kind === 'linear' ? 0 : 1;\n data[at + 1] = placed.stops.length;\n data[at + 4] = placed.x0;\n data[at + 5] = placed.y0;\n if (placed.kind === 'linear') {\n data[at + 6] = placed.x1;\n data[at + 7] = placed.y1;\n } else {\n data[at + 6] = placed.radius;\n }\n for (let i = 0; i < placed.stops.length; i++) {\n const stop = placed.stops[i];\n const color = at + GRADIENT_COLORS_OFFSET + i * 4;\n data[color] = stop.color.r;\n data[color + 1] = stop.color.g;\n data[color + 2] = stop.color.b;\n data[color + 3] = stop.color.a;\n data[at + GRADIENT_OFFSETS_OFFSET + i] = stop.offset;\n }\n out.length = at + GRADIENT_STRIDE_FLOATS;\n return at / GRADIENT_STRIDE_FLOATS;\n}\n\n/** The whole of a texture: what an image samples. */\nconst FULL_TEXTURE: UvRect = { u: 0, v: 0, uw: 1, vh: 1 };\n\ninterface UvRect {\n u: number;\n v: number;\n uw: number;\n vh: number;\n}\n\n/**\n * `dx`/`dy` shift the transform's translation, which is how a glyph\n * snaps its cell onto whole physical pixels without a transform array\n * of its own — one allocation per letter per frame, otherwise.\n */\nfunction pushTextured(\n out: FloatBuffer,\n x: number,\n y: number,\n width: number,\n height: number,\n opacity: number,\n transform: Affine,\n clip: number,\n uv: UvRect,\n dx = 0,\n dy = 0\n): number {\n out.ensure(TEXTURED_STRIDE_FLOATS);\n const data = out.data;\n const at = out.length;\n data[at] = x;\n data[at + 1] = y;\n data[at + 2] = width;\n data[at + 3] = height;\n data[at + 4] = opacity;\n data[at + 5] = clip;\n data[at + 6] = transform[0];\n data[at + 7] = transform[1];\n data[at + 8] = transform[2];\n data[at + 9] = transform[3];\n data[at + 10] = transform[4] + dx;\n data[at + 11] = transform[5] + dy;\n data[at + 12] = uv.u;\n data[at + 13] = uv.v;\n data[at + 14] = uv.uw;\n data[at + 15] = uv.vh;\n out.length = at + TEXTURED_STRIDE_FLOATS;\n return at / TEXTURED_STRIDE_FLOATS;\n}\n\nfunction buildOwnTransform(\n rec: LayoutBox,\n local: {\n x: number;\n y: number;\n translateX: number;\n translateY: number;\n scaleX: number;\n scaleY: number;\n rotation: number;\n }\n): Affine {\n const originX = rec.x + local.x;\n const originY = rec.y + local.y;\n\n const cos = Math.cos(local.rotation);\n const sin = Math.sin(local.rotation);\n\n // Own transform: T(translate) * T(origin) * R * S * T(-origin),\n // matching the Canvas2D renderer's translate sequence. Pre-composing\n // a translation only adds to the translation column, which is why it\n // costs two additions rather than a matrix multiply.\n const m00 = cos * local.scaleX;\n const m01 = sin * local.scaleX;\n const m10 = -sin * local.scaleY;\n const m11 = cos * local.scaleY;\n const tx = originX - originX * m00 - originY * m10 + local.translateX;\n const ty = originY - originX * m01 - originY * m11 + local.translateY;\n\n return [m00, m01, m10, m11, tx, ty];\n}\n\nexport function multiplyTransform(a: Affine, b: Affine): Affine {\n return [\n a[0] * b[0] + a[2] * b[1],\n a[1] * b[0] + a[3] * b[1],\n a[0] * b[2] + a[2] * b[3],\n a[1] * b[2] + a[3] * b[3],\n a[0] * b[4] + a[2] * b[5] + a[4],\n a[1] * b[4] + a[3] * b[5] + a[5]\n ];\n}\n\nexport function translateTransform(transform: Affine, dx: number, dy: number): Affine {\n return [\n transform[0],\n transform[1],\n transform[2],\n transform[3],\n transform[4] + transform[0] * dx + transform[2] * dy,\n transform[5] + transform[1] * dx + transform[3] * dy\n ];\n}\n\nexport function applyTransform(transform: Affine, x: number, y: number): [number, number] {\n return [x * transform[0] + y * transform[2] + transform[4], x * transform[1] + y * transform[3] + transform[5]];\n}\n\nexport function invertTransform(t: Affine): Affine | null {\n const det = t[0] * t[3] - t[1] * t[2];\n if (det === 0 || !Number.isFinite(det)) {\n return null;\n }\n const a = t[3] / det;\n const b = -t[1] / det;\n const c = -t[2] / det;\n const d = t[0] / det;\n // `+ 0` folds -0 into 0 so equal transforms compare equal.\n return [a + 0, b + 0, c + 0, d + 0, -(a * t[4] + c * t[5]) + 0, -(b * t[4] + d * t[5]) + 0];\n}\n\n/**\n * Intersects the visible region with a clipping box and shifts by its\n * scroll offset, so child boxes (absolute, pre-scroll) can be tested\n * against the region their screen pixels occupy.\n */\nfunction pushCull(cull: CullRect, rec: LayoutRecord): CullRect {\n const right = Math.min(cull.x + cull.width, rec.x + rec.width);\n const bottom = Math.min(cull.y + cull.height, rec.y + rec.height);\n const left = Math.max(cull.x, rec.x);\n const top = Math.max(cull.y, rec.y);\n return {\n x: left + rec.scrollX,\n y: top + rec.scrollY,\n width: Math.max(0, right - left),\n height: Math.max(0, bottom - top)\n };\n}\n\nfunction emptyScissor(): ScissorRect {\n return { x: 0, y: 0, width: 0, height: 0 };\n}\n\nfunction intersectScissors(a: ScissorRect, b: ScissorRect): ScissorRect {\n const left = Math.max(a.x, b.x);\n const top = Math.max(a.y, b.y);\n const right = Math.min(a.x + a.width, b.x + b.width);\n const bottom = Math.min(a.y + a.height, b.y + b.height);\n const width = Math.max(0, right - left);\n const height = Math.max(0, bottom - top);\n if (width <= 0 || height <= 0) {\n return emptyScissor();\n }\n return { x: left, y: top, width, height };\n}\n\nfunction logicalClipToScissor(\n clip: LayoutBox,\n transform: Affine,\n logicalWidth: number,\n logicalHeight: number,\n dpr: number\n): ScissorRect | null {\n if (clip.width <= 0 || clip.height <= 0) {\n return null;\n }\n\n // Transform the four corners to screen space and take the bounding box.\n let minX = Infinity;\n let minY = Infinity;\n let maxX = -Infinity;\n let maxY = -Infinity;\n for (const [lx, ly] of [\n [clip.x, clip.y],\n [clip.x + clip.width, clip.y],\n [clip.x, clip.y + clip.height],\n [clip.x + clip.width, clip.y + clip.height]\n ]) {\n const [sx, sy] = applyTransform(transform, lx, ly);\n minX = Math.min(minX, sx);\n minY = Math.min(minY, sy);\n maxX = Math.max(maxX, sx);\n maxY = Math.max(maxY, sy);\n }\n\n // Intersect with viewport.\n minX = Math.max(0, minX);\n minY = Math.max(0, minY);\n maxX = Math.min(logicalWidth, maxX);\n maxY = Math.min(logicalHeight, maxY);\n\n if (maxX - minX <= 0 || maxY - minY <= 0) {\n return null;\n }\n\n // Round the edges, never the extent. Rounding the origin and the\n // width independently lets x + width land a pixel past the\n // attachment whenever both products have a fractional part of a\n // half or more — and an out-of-bounds scissor is a validation error\n // that discards the entire command buffer, losing the whole frame\n // rather than this one rectangle. Clamping to the backing store,\n // derived through the same rule the surface sizes it with, keeps\n // the result inside the attachment for any size and dpr.\n const physicalWidth = toPhysicalPixels(logicalWidth, dpr);\n const physicalHeight = toPhysicalPixels(logicalHeight, dpr);\n const x0 = clamp(Math.round(minX * dpr), 0, physicalWidth);\n const x1 = clamp(Math.round(maxX * dpr), 0, physicalWidth);\n const y0 = clamp(Math.round(minY * dpr), 0, physicalHeight);\n const y1 = clamp(Math.round(maxY * dpr), 0, physicalHeight);\n if (x1 <= x0 || y1 <= y0) {\n return null;\n }\n\n return { x: x0, y: y0, width: x1 - x0, height: y1 - y0 };\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return value < min ? min : value > max ? max : value;\n}\n\n/**\n * The scissor rectangle covering the whole attachment.\n *\n * Callers that need to *undo* a scissor have to set one explicitly:\n * a render pass keeps the last rectangle it was given, so \"no clip\"\n * means the full viewport, not the absence of a call.\n */\nexport function viewportScissor(logicalWidth: number, logicalHeight: number, dpr: number): ScissorRect {\n return {\n x: 0,\n y: 0,\n width: toPhysicalPixels(logicalWidth, dpr),\n height: toPhysicalPixels(logicalHeight, dpr)\n };\n}\n\nexport function scissorsEqual(a: ScissorRect | null, b: ScissorRect | null): boolean {\n if (a === b) {\n return true;\n }\n if (a === null || b === null) {\n return false;\n }\n return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;\n}\n","import { WebGPUError } from './WebGPUError';\nimport { PRIMITIVE_SHADER, TEXTURED_SHADER, VIEW_UNIFORM_FLOATS } from './WebGPUShader';\nimport { INSTANCE_STRIDE_BYTES, TEXTURED_STRIDE_BYTES } from './WebGPURenderData';\n\nexport interface PrimitivePipeline {\n pipeline: GPURenderPipeline;\n vertexBuffer: GPUBuffer;\n indexBuffer: GPUBuffer;\n indexCount: number;\n uniformBuffer: GPUBuffer;\n uniformBindGroup: GPUBindGroup;\n uniformLayout: GPUBindGroupLayout;\n}\n\n/**\n * The bind group layout for what the whole frame shares (group 1): the\n * clip chain and the frame's gradients, one bind group for the pass.\n *\n * Both are read-only storage rather than uniforms because both are\n * variable-length per frame, and both live in group 1 rather than in a\n * group of their own because a second bind group would be a second\n * `setBindGroup` per pipeline switch for data neither pipeline changes.\n * The textured pipeline never samples the gradients; a bind group\n * layout may declare more than a shader uses.\n */\nexport function createFrameBindGroupLayout(device: GPUDevice): GPUBindGroupLayout {\n return device.createBindGroupLayout({\n entries: [\n { binding: 0, visibility: fragmentStage(), buffer: { type: 'read-only-storage' } },\n { binding: 1, visibility: fragmentStage(), buffer: { type: 'read-only-storage' } }\n ]\n });\n}\n\nexport interface TexturedPipeline {\n pipeline: GPURenderPipeline;\n vertexBuffer: GPUBuffer;\n indexBuffer: GPUBuffer;\n indexCount: number;\n uniformBuffer: GPUBuffer;\n bindGroupLayout: GPUBindGroupLayout;\n /** Nearest sampling: a glyph cell maps one-to-one onto pixels. */\n textSampler: GPUSampler;\n /** Linear sampling: images are scaled by objectFit. */\n imageSampler: GPUSampler;\n}\n\nconst QUAD_VERTICES = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);\nconst QUAD_INDICES = new Uint16Array([0, 1, 2, 2, 1, 3]);\n\nconst vertexStage = (): number => (typeof GPUShaderStage !== 'undefined' ? GPUShaderStage.VERTEX : 0x1);\nconst fragmentStage = (): number => (typeof GPUShaderStage !== 'undefined' ? GPUShaderStage.FRAGMENT : 0x2);\nconst usage = {\n vertex: (): number => (typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.VERTEX : 0x20),\n index: (): number => (typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.INDEX : 0x10),\n uniform: (): number => (typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.UNIFORM : 0x40),\n copyDst: (): number => (typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.COPY_DST : 0x8)\n};\n\n/**\n * Source-over for a straight-alpha source onto a premultiplied target,\n * which is what the canvas is configured as. Colour is scaled by the\n * source alpha; alpha itself is not — with `src-alpha` there too, a\n * half-transparent fill over a transparent canvas came out a quarter\n * transparent, and every faint panel drew dimmer than on Canvas2D.\n */\nconst STRAIGHT_ALPHA_BLEND: GPUBlendState = {\n color: { srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha', operation: 'add' },\n alpha: { srcFactor: 'one', dstFactor: 'one-minus-src-alpha', operation: 'add' }\n};\n\n/**\n * Creates the primitive pipeline: rounded fills and borders.\n *\n * Static resources (quad geometry, pipeline, bind group layout) are\n * created once and reused for the lifetime of the renderer.\n */\nexport function createPrimitivePipeline(\n device: GPUDevice,\n format: GPUTextureFormat,\n frameLayout: GPUBindGroupLayout\n): PrimitivePipeline {\n const module = device.createShaderModule({ code: PRIMITIVE_SHADER });\n\n const uniformLayout = device.createBindGroupLayout({\n entries: [{ binding: 0, visibility: vertexStage() | fragmentStage(), buffer: { type: 'uniform' } }]\n });\n\n const pipeline = device.createRenderPipeline({\n layout: device.createPipelineLayout({ bindGroupLayouts: [uniformLayout, frameLayout] }),\n vertex: { module, entryPoint: 'vs', buffers: [quadVertexLayout(), primitiveInstanceLayout()] },\n fragment: { module, entryPoint: 'fs', targets: [{ format, blend: STRAIGHT_ALPHA_BLEND }] },\n primitive: { topology: 'triangle-list', cullMode: 'none' }\n });\n if (pipeline === undefined) {\n throw new WebGPUError('Failed to create primitive render pipeline.', 'pipeline');\n }\n\n const { vertexBuffer, indexBuffer } = createQuad(device);\n const uniformBuffer = device.createBuffer({\n size: VIEW_UNIFORM_FLOATS * 4,\n usage: usage.uniform() | usage.copyDst()\n });\n const uniformBindGroup = device.createBindGroup({\n layout: uniformLayout,\n entries: [{ binding: 0, resource: { buffer: uniformBuffer } }]\n });\n\n return {\n pipeline,\n vertexBuffer,\n indexBuffer,\n indexCount: QUAD_INDICES.length,\n uniformBuffer,\n uniformBindGroup,\n uniformLayout\n };\n}\n\n/**\n * Creates the textured pipeline: one quad per rasterised text run or\n * image, each command binding its own texture.\n */\nexport function createTexturedPipeline(\n device: GPUDevice,\n format: GPUTextureFormat,\n frameLayout: GPUBindGroupLayout\n): TexturedPipeline {\n const module = device.createShaderModule({ code: TEXTURED_SHADER });\n\n const bindGroupLayout = device.createBindGroupLayout({\n entries: [\n { binding: 0, visibility: vertexStage() | fragmentStage(), buffer: { type: 'uniform' } },\n { binding: 1, visibility: fragmentStage(), sampler: { type: 'filtering' } },\n { binding: 2, visibility: fragmentStage(), texture: {} }\n ]\n });\n\n const pipeline = device.createRenderPipeline({\n layout: device.createPipelineLayout({ bindGroupLayouts: [bindGroupLayout, frameLayout] }),\n vertex: { module, entryPoint: 'vs', buffers: [quadVertexLayout(), texturedInstanceLayout()] },\n fragment: { module, entryPoint: 'fs', targets: [{ format, blend: STRAIGHT_ALPHA_BLEND }] },\n primitive: { topology: 'triangle-list', cullMode: 'none' }\n });\n if (pipeline === undefined) {\n throw new WebGPUError('Failed to create textured render pipeline.', 'pipeline');\n }\n\n const { vertexBuffer, indexBuffer } = createQuad(device);\n const uniformBuffer = device.createBuffer({\n size: VIEW_UNIFORM_FLOATS * 4,\n usage: usage.uniform() | usage.copyDst()\n });\n\n return {\n pipeline,\n vertexBuffer,\n indexBuffer,\n indexCount: QUAD_INDICES.length,\n uniformBuffer,\n bindGroupLayout,\n textSampler: device.createSampler({ magFilter: 'nearest', minFilter: 'nearest' }),\n imageSampler: device.createSampler({ magFilter: 'linear', minFilter: 'linear' })\n };\n}\n\nfunction createQuad(device: GPUDevice): { vertexBuffer: GPUBuffer; indexBuffer: GPUBuffer } {\n const vertexBuffer = device.createBuffer({\n size: QUAD_VERTICES.byteLength,\n usage: usage.vertex(),\n mappedAtCreation: true\n });\n new Float32Array(vertexBuffer.getMappedRange()).set(QUAD_VERTICES);\n vertexBuffer.unmap();\n\n const indexBuffer = device.createBuffer({\n size: QUAD_INDICES.byteLength,\n usage: usage.index(),\n mappedAtCreation: true\n });\n new Uint16Array(indexBuffer.getMappedRange()).set(QUAD_INDICES);\n indexBuffer.unmap();\n return { vertexBuffer, indexBuffer };\n}\n\nfunction quadVertexLayout(): GPUVertexBufferLayout {\n return {\n arrayStride: 8,\n stepMode: 'vertex',\n attributes: [{ shaderLocation: 0, offset: 0, format: 'float32x2' }]\n };\n}\n\n/** Mirrors the float layout documented on INSTANCE_STRIDE_FLOATS. */\nfunction primitiveInstanceLayout(): GPUVertexBufferLayout {\n return {\n arrayStride: INSTANCE_STRIDE_BYTES,\n stepMode: 'instance',\n attributes: [\n { shaderLocation: 1, offset: 0, format: 'float32x2' }, // pos\n { shaderLocation: 2, offset: 8, format: 'float32x2' }, // size\n { shaderLocation: 3, offset: 16, format: 'float32x4' }, // color\n { shaderLocation: 4, offset: 32, format: 'float32x3' }, // radius, opacity, borderWidth\n { shaderLocation: 5, offset: 44, format: 'uint32' }, // kind\n { shaderLocation: 6, offset: 48, format: 'float32x2' }, // transform a\n { shaderLocation: 7, offset: 56, format: 'float32x2' }, // transform b\n { shaderLocation: 8, offset: 64, format: 'float32x2' }, // transform c\n { shaderLocation: 9, offset: 72, format: 'float32' }, // clip index\n { shaderLocation: 10, offset: 76, format: 'float32' } // gradient index\n ]\n };\n}\n\n/** Mirrors the float layout documented on TEXTURED_STRIDE_FLOATS. */\nfunction texturedInstanceLayout(): GPUVertexBufferLayout {\n return {\n arrayStride: TEXTURED_STRIDE_BYTES,\n stepMode: 'instance',\n attributes: [\n { shaderLocation: 1, offset: 0, format: 'float32x2' }, // pos\n { shaderLocation: 2, offset: 8, format: 'float32x2' }, // size\n { shaderLocation: 3, offset: 16, format: 'float32' }, // opacity\n { shaderLocation: 4, offset: 20, format: 'float32' }, // clip index\n { shaderLocation: 5, offset: 24, format: 'float32x2' }, // transform a\n { shaderLocation: 6, offset: 32, format: 'float32x2' }, // transform b\n { shaderLocation: 7, offset: 40, format: 'float32x2' }, // transform c\n { shaderLocation: 8, offset: 48, format: 'float32x2' }, // uv origin\n { shaderLocation: 9, offset: 56, format: 'float32x2' } // uv size\n ]\n };\n}\n","import type { UiImage } from '../PaintState';\nimport { isVideoSurface, videoFrameSize, type UiVideoSurface } from '../../properties/UiVideo';\nimport { ScaledImageCache, type ScaledImageSurfaceFactory } from '../ScaledImageCache';\nimport type { TexturedPipeline } from './WebGPUPipeline';\n\n/** A GPU texture with the bind group that draws it, ready to reuse. */\nexport interface TextureEntry {\n texture: GPUTexture;\n bindGroup: GPUBindGroup;\n /** For a video surface: the frame this texture currently holds. */\n version?: number;\n width?: number;\n height?: number;\n}\n\n/** Anything the textured pipeline can be given: a still, or a video's surface. */\nexport type TextureSource = UiImage | UiVideoSurface;\n\n/**\n * Textures for the textured pipeline's images.\n *\n * An image is uploaded once per `ImageBitmap` and lives as long as the\n * bitmap does — a `WeakMap`, so a bitmap the application drops takes\n * its texture with it. Bind groups are created with their texture, so\n * a frame allocates nothing for an image it has drawn before.\n *\n * **A video is the same map with one more comparison.** Its pixels\n * change every frame while the thing on screen stays the same thing,\n * so keying on the frame — which is a fresh `VideoFrame` object each\n * time — would allocate a texture and a bind group sixty times a\n * second and free none of them until the garbage collector felt like\n * it. Keying on the *surface*, whose identity is stable for the life\n * of the playback, and comparing `version`, means one texture per\n * video and one `copyExternalImageToTexture` per frame into the\n * texture that is already there. See `UiVideo.ts`.\n *\n * Text used to live here too, one texture per run. It is now\n * `WebGPUGlyphPages`: the same drawing, cut into glyphs and packed\n * into a shared atlas, so its memory follows the fonts in use rather\n * than the amount of text on screen.\n */\nexport class WebGPUTextureCache {\n private readonly images = new WeakMap<TextureSource, TextureEntry>();\n /**\n * Copies of stills at the size they are drawn, shared with the other\n * backend.\n *\n * A texture is uploaded once and sampled every frame, so what the\n * source size costs here is not upload bandwidth but minification: a\n * 480px cover drawn into 56 logical pixels is sampled down by more\n * than four with `minFilter: 'linear'` and no mip chain under it,\n * which both aliases and reads texels far apart. Uploading the copy\n * instead makes the sampling one-to-one, and takes the memory with\n * it: the same cover is a fifty-sixth of the texture.\n *\n * The cache hands back the source until a copy is worth making and\n * ready, so the first frames draw exactly as they did before. The\n * `WeakMap` keys on whichever bitmap it returned, so the copy gets\n * its own texture and the full-size one is dropped when the source\n * is.\n */\n private readonly scaled: ScaledImageCache;\n\n constructor(\n private readonly device: GPUDevice,\n private readonly pipeline: TexturedPipeline,\n createSurface?: ScaledImageSurfaceFactory\n ) {\n this.scaled = new ScaledImageCache(createSurface);\n }\n\n /** The bind group for a still or a video, uploading it when it must. */\n imageBindGroup(source: TextureSource, drawWidth = 0, drawHeight = 0): GPUBindGroup | null {\n return isVideoSurface(source) ? this.videoBindGroup(source) : this.stillBindGroup(source, drawWidth, drawHeight);\n }\n\n private stillBindGroup(image: UiImage, drawWidth: number, drawHeight: number): GPUBindGroup | null {\n // A caller that does not know the size gets the source, which is\n // what this did for every still before.\n const drawn =\n drawWidth > 0 && drawHeight > 0 ? this.scaled.resolve(image as ImageBitmap, drawWidth, drawHeight) : image;\n const entry = this.images.get(drawn);\n if (entry !== undefined) {\n return entry.bindGroup;\n }\n return this.upload(drawn, drawn, drawn.width, drawn.height, undefined)?.bindGroup ?? null;\n }\n\n /**\n * The same, for a surface whose contents move.\n *\n * Three outcomes: the version is unchanged and the existing bind\n * group is handed straight back; the version moved but the size did\n * not, so the frame is copied into the texture that is already\n * there; or the size changed too — a stream that switched\n * resolution — and the texture is rebuilt, which is rare enough to\n * be worth nothing more than this comment.\n */\n private videoBindGroup(surface: UiVideoSurface): GPUBindGroup | null {\n const frame = surface.frame;\n if (frame === null) {\n return null;\n }\n const { width, height } = videoFrameSize(surface);\n const entry = this.images.get(surface);\n if (entry !== undefined) {\n if (entry.version === surface.version) {\n return entry.bindGroup;\n }\n if (entry.width === width && entry.height === height) {\n entry.version = surface.version;\n return this.copyInto(entry.texture, frame, width, height) ? entry.bindGroup : null;\n }\n entry.texture.destroy();\n this.images.delete(surface);\n }\n return this.upload(surface, frame, width, height, surface.version)?.bindGroup ?? null;\n }\n\n private upload(\n key: TextureSource,\n frame: ImageBitmap | VideoFrame,\n width: number,\n height: number,\n version: number | undefined\n ): TextureEntry | null {\n const w = Math.max(1, Math.round(width));\n const h = Math.max(1, Math.round(height));\n const texture = createTexture(this.device, w, h);\n if (!this.copyInto(texture, frame, w, h)) {\n texture.destroy();\n return null;\n }\n const bindGroup = createTexturedBindGroup(this.device, this.pipeline, texture, this.pipeline.imageSampler);\n const entry: TextureEntry = { texture, bindGroup, version, width: w, height: h };\n this.images.set(key, entry);\n return entry;\n }\n\n private copyInto(texture: GPUTexture, frame: ImageBitmap | VideoFrame, width: number, height: number): boolean {\n try {\n this.device.queue.copyExternalImageToTexture({ source: frame as ImageBitmap }, { texture }, [\n Math.max(1, Math.round(width)),\n Math.max(1, Math.round(height))\n ]);\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Drops the scaled copies.\n *\n * The textures are keyed weakly and go when their bitmaps do, but a\n * copy is held strongly by the scaled cache until it is evicted, so\n * the renderer has to say when it is finished with them.\n */\n dispose(): void {\n this.scaled.dispose();\n }\n}\n\n/** A texture the textured pipeline can sample and be copied into. */\nexport function createTexture(device: GPUDevice, width: number, height: number): GPUTexture {\n const textureBinding = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.TEXTURE_BINDING : 0x4;\n const copyDst = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.COPY_DST : 0x8;\n const renderAttachment = typeof GPUTextureUsage !== 'undefined' ? GPUTextureUsage.RENDER_ATTACHMENT : 0x10;\n return device.createTexture({\n size: [width, height],\n format: 'rgba8unorm',\n usage: textureBinding | copyDst | renderAttachment\n });\n}\n\n/** The pipeline's group 0: view uniforms, a sampler and one texture. */\nexport function createTexturedBindGroup(\n device: GPUDevice,\n pipeline: TexturedPipeline,\n texture: GPUTexture,\n sampler: GPUSampler\n): GPUBindGroup {\n return device.createBindGroup({\n layout: pipeline.bindGroupLayout,\n entries: [\n { binding: 0, resource: { buffer: pipeline.uniformBuffer } },\n { binding: 1, resource: sampler },\n { binding: 2, resource: texture.createView() }\n ]\n });\n}\n","import type { TexturedPipeline } from './WebGPUPipeline';\nimport type { WebGPUGlyphAtlas, GlyphUpload } from './WebGPUGlyphAtlas';\nimport { createTexture, createTexturedBindGroup } from './WebGPUTextureCache';\n\n/**\n * The GPU half of the glyph atlas: one texture per page, and the\n * rasterisation that fills the cells the builder allocated.\n *\n * The builder runs before this and has already decided where every\n * glyph goes, so all that is left here is to draw each newly allocated\n * cluster into a scratch canvas and copy it into its cell. That copy\n * happens once per glyph for the life of the page — a scrolling list\n * of a thousand rows uploads nothing after its first frame, because\n * the second row's letters are the first row's.\n *\n * Cells are rasterised in the run's own colour, so what the sampler\n * reads is exactly what `fillText` would have put on the canvas.\n */\nexport class WebGPUGlyphPages {\n private readonly textures: (GPUTexture | null)[] = [];\n private readonly bindGroups: (GPUBindGroup | null)[] = [];\n private scratch: OffscreenCanvas | HTMLCanvasElement | null = null;\n private scratchContext: OffscreenCanvasRenderingContext2D | CanvasRenderingContext2D | null = null;\n /** Cells uploaded since the renderer was created; for tests and metrics. */\n uploads = 0;\n\n constructor(\n private readonly device: GPUDevice,\n private readonly pipeline: TexturedPipeline,\n private readonly atlas: WebGPUGlyphAtlas\n ) {}\n\n /**\n * Recycles the textures of cleared pages and rasterises every cell\n * the last build allocated. Call after building a render list and\n * before encoding the pass that draws it.\n */\n flush(): void {\n for (const page of this.atlas.takeCleared()) {\n this.textures[page]?.destroy();\n this.textures[page] = null;\n this.bindGroups[page] = null;\n }\n const pending = this.atlas.takePending();\n for (const upload of pending) {\n this.upload(upload);\n }\n }\n\n /** The bind group for a page, creating its texture on first use. */\n bindGroup(page: number): GPUBindGroup | null {\n const existing = this.bindGroups[page];\n if (existing !== undefined && existing !== null) {\n return existing;\n }\n const texture = this.textureFor(page);\n if (texture === null) {\n return null;\n }\n const bindGroup = createTexturedBindGroup(this.device, this.pipeline, texture, this.pipeline.textSampler);\n this.bindGroups[page] = bindGroup;\n return bindGroup;\n }\n\n dispose(): void {\n for (const texture of this.textures) {\n texture?.destroy();\n }\n this.textures.length = 0;\n this.bindGroups.length = 0;\n this.scratch = null;\n this.scratchContext = null;\n }\n\n private textureFor(page: number): GPUTexture | null {\n const existing = this.textures[page];\n if (existing !== undefined && existing !== null) {\n return existing;\n }\n const size = this.atlas.pageSize;\n const texture = createTexture(this.device, size, size);\n this.textures[page] = texture;\n return texture;\n }\n\n private upload(upload: GlyphUpload): void {\n const context = this.scratchFor(upload.pixelWidth, upload.pixelHeight);\n if (context === null) {\n return;\n }\n const texture = this.textureFor(upload.slot.page);\n if (texture === null) {\n return;\n }\n context.setTransform(1, 0, 0, 1, 0, 0);\n context.clearRect(0, 0, upload.pixelWidth, upload.pixelHeight);\n context.scale(upload.dpr, upload.dpr);\n context.font = upload.font;\n context.direction = upload.rtl ? 'rtl' : 'ltr';\n context.fillStyle = upload.color;\n context.textAlign = 'left';\n context.textBaseline = 'alphabetic';\n // The pen is in physical pixels — its subpixel phase is a fraction\n // of one — and the context is scaled to logical, so divide it back.\n context.fillText(upload.cluster, upload.penX / upload.dpr, upload.penY / upload.dpr);\n\n this.device.queue.copyExternalImageToTexture(\n { source: this.scratch as GPUImageCopyExternalImageSource },\n { texture, origin: [upload.pageX, upload.pageY] },\n [upload.pixelWidth, upload.pixelHeight]\n );\n this.uploads++;\n }\n\n /**\n * One scratch canvas, grown to the largest cell seen. Growing resets\n * the context, which costs nothing here: every draw sets its own\n * font, fill and transform.\n */\n private scratchFor(width: number, height: number): typeof this.scratchContext {\n const canvas = this.scratch;\n if (canvas !== null && canvas.width >= width && canvas.height >= height) {\n return this.scratchContext;\n }\n const nextWidth = Math.max(width, canvas?.width ?? 0);\n const nextHeight = Math.max(height, canvas?.height ?? 0);\n const created = createCanvas(nextWidth, nextHeight);\n if (created === null) {\n return null;\n }\n this.scratch = created;\n this.scratchContext = created.getContext('2d') as\n | OffscreenCanvasRenderingContext2D\n | CanvasRenderingContext2D\n | null;\n return this.scratchContext;\n }\n}\n\nfunction createCanvas(width: number, height: number): OffscreenCanvas | HTMLCanvasElement | null {\n if (typeof OffscreenCanvas !== 'undefined') {\n return new OffscreenCanvas(width, height);\n }\n if (typeof document !== 'undefined') {\n const canvas = document.createElement('canvas');\n canvas.width = width;\n canvas.height = height;\n return canvas;\n }\n return null;\n}\n","import { UiNodeType } from '../../graph/UiNodeType';\nimport type { UiNode } from '../../graph/UiNode';\nimport type { RenderContext } from '../RenderContext';\nimport type { RendererBackend, UiRenderer } from '../UiRenderer';\nimport type { WebGPUSurface } from './WebGPUSurface';\nimport { initializeWebGPU, onDeviceLost } from './WebGPUDevice';\nimport { WebGPUError } from './WebGPUError';\nimport {\n createFrameBindGroupLayout,\n createPrimitivePipeline,\n createTexturedPipeline,\n type PrimitivePipeline,\n type TexturedPipeline\n} from './WebGPUPipeline';\nimport {\n buildRenderList,\n createTextCache,\n CLIP_STRIDE_BYTES,\n CommandKind,\n GRADIENT_STRIDE_BYTES,\n INSTANCE_STRIDE_BYTES,\n TEXTURED_STRIDE_BYTES,\n viewportScissor,\n type RenderTextCache,\n type ScissorRect\n} from './WebGPURenderData';\nimport { VIEW_UNIFORM_FLOATS } from './WebGPUShader';\nimport { WebGPUTextureCache } from './WebGPUTextureCache';\nimport { WebGPUGlyphPages } from './WebGPUGlyphPages';\n\nexport interface WebGPURendererOptions {\n /** The WebGPU surface this renderer draws into. */\n surface: WebGPUSurface;\n /** Optional hooks for benchmarking and profiling. */\n hooks?: RenderHooks;\n /**\n * Receives GPU errors that would otherwise only reach the console —\n * validation failures and device loss. Inside a render worker the\n * console is out of sight, so the runtime forwards these to the shell.\n */\n onError?: (message: string) => void;\n}\n\nexport interface RenderHooks {\n onPrepareStart?(): void;\n onPrepareEnd?(ms: number): void;\n onUploadStart?(): void;\n onUploadEnd?(ms: number): void;\n onEncodeStart?(): void;\n onEncodeEnd?(ms: number): void;\n}\n\nconst vertexBufferUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.VERTEX : 0x20;\nconst storageBufferUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.STORAGE : 0x80;\nconst copyDstBufferUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.COPY_DST : 0x8;\n\n/** A frame read back from the GPU: RGBA, straight alpha, row-major. */\nexport interface CapturedFrame {\n width: number;\n height: number;\n data: Uint8ClampedArray;\n}\n\ninterface PendingCapture {\n resolve: (frame: CapturedFrame) => void;\n reject: (error: Error) => void;\n}\n\nconst copyDstUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.COPY_DST : 0x8;\nconst mapReadUsage = typeof GPUBufferUsage !== 'undefined' ? GPUBufferUsage.MAP_READ : 0x1;\nconst mapModeRead = typeof GPUMapMode !== 'undefined' ? GPUMapMode.READ : 0x1;\n\n/** Draw calls issued for one frame, by kind; for tests and the profiler. */\nexport interface DrawStats {\n primitiveDraws: number;\n texturedDraws: number;\n texturedInstances: number;\n}\n\n/**\n * WebGPU rendering backend for the retained UI tree.\n *\n * Consumes the same UiNode tree and LayoutRecords as the\n * Canvas2DRenderer. It owns no composition, bindings, layout, or\n * scheduling state. GPU resources are created during initialize() and\n * reused across frames; per-frame instance data is uploaded to two\n * growable vertex buffers, one per pipeline, and the frame's command\n * list is walked in paint order switching pipelines as it goes.\n */\nexport class WebGPURenderer implements UiRenderer {\n readonly backend: RendererBackend = 'webgpu';\n\n private readonly surface: WebGPUSurface;\n private hooks: RenderHooks;\n private device: GPUDevice | null = null;\n private format: GPUTextureFormat | null = null;\n private primitives: PrimitivePipeline | null = null;\n private textured: TexturedPipeline | null = null;\n private textures: WebGPUTextureCache | null = null;\n private glyphPages: WebGPUGlyphPages | null = null;\n /** The glyph atlas and line shaper, kept across frames. */\n private readonly textCache: RenderTextCache = createTextCache();\n private instanceBuffer: GPUBuffer | null = null;\n private texturedBuffer: GPUBuffer | null = null;\n private clipBuffer: GPUBuffer | null = null;\n private gradientBuffer: GPUBuffer | null = null;\n private frameLayout: GPUBindGroupLayout | null = null;\n private frameBindGroup: GPUBindGroup | null = null;\n private cachedInstanceData: Float32Array | null = null;\n /** Draw calls of the most recent frame. */\n readonly lastDraws: DrawStats = { primitiveDraws: 0, texturedDraws: 0, texturedInstances: 0 };\n private pendingCapture: PendingCapture | null = null;\n private lost = false;\n private disposed = false;\n private readonly removeLostListener: () => void;\n\n private readonly onError: (message: string) => void;\n\n constructor(options: WebGPURendererOptions) {\n this.surface = options.surface;\n this.hooks = options.hooks ?? {};\n // eslint-disable-next-line no-console\n this.onError = options.onError ?? (message => console.error(message));\n this.removeLostListener = onDeviceLost(info => {\n this.lost = true;\n this.onError(`WebGPU device lost: ${info.reason} ${info.message}`);\n });\n }\n\n /**\n * Replaces the render hooks used for profiling. Safe to call at any\n * time, including between frames.\n */\n setHooks(hooks: RenderHooks): void {\n this.hooks = hooks;\n }\n\n /**\n * Acquires the adapter, device and canvas context. Must be called\n * once before render(). The canvas is not touched until the device\n * exists, so a failure leaves it free for a Canvas2D fallback.\n */\n async initialize(): Promise<void> {\n if (this.disposed) {\n throw new WebGPUError('Renderer is disposed.', 'disposed');\n }\n const init = await initializeWebGPU();\n if (this.disposed) {\n return;\n }\n this.device = init.device;\n this.format = init.format;\n init.device.onuncapturederror = event => {\n this.onError(`WebGPU error: ${event.error.message}`);\n };\n this.surface.configure(init.device, init.format);\n this.frameLayout = createFrameBindGroupLayout(init.device);\n this.primitives = createPrimitivePipeline(init.device, init.format, this.frameLayout);\n this.textured = createTexturedPipeline(init.device, init.format, this.frameLayout);\n this.textures = new WebGPUTextureCache(init.device, this.textured);\n this.glyphPages = new WebGPUGlyphPages(init.device, this.textured, this.textCache.atlas);\n this.lost = false;\n }\n\n /**\n * True when the renderer has been initialized and the device is\n * still alive.\n */\n get isReady(): boolean {\n return (\n this.device !== null &&\n this.format !== null &&\n this.primitives !== null &&\n this.textured !== null &&\n !this.lost &&\n !this.disposed\n );\n }\n\n /** True once the device has been lost; frames are skipped from then on. */\n get isLost(): boolean {\n return this.lost;\n }\n\n /**\n * Resolves with the pixels of the next frame `render()` draws, read\n * back from the GPU rather than from the canvas, so the result does\n * not depend on when the compositor presents. For the parity check.\n */\n capture(): Promise<CapturedFrame> {\n return new Promise((resolve, reject) => {\n this.pendingCapture?.reject(new Error('Superseded by a later capture.'));\n this.pendingCapture = { resolve, reject };\n });\n }\n\n /**\n * Renders one frame.\n *\n * Builds a CPU-side render list from the tree, uploads it to the\n * GPU, and walks the command list: one instanced draw per run of\n * primitives under a scissor, one draw per text run or image.\n */\n render(root: UiNode, context: RenderContext): void {\n if (!this.isReady) {\n return;\n }\n if (this.disposed) {\n throw new WebGPUError('Renderer is disposed.', 'disposed');\n }\n if (root.type === UiNodeType.Root && !root.hasChildren()) {\n this.clearFrame();\n return;\n }\n\n // Each stage samples its start clock when either of its hooks is\n // set. Sampling only for the Start hook meant a caller that wants\n // durations alone — which the benchmark route does — measured\n // every stage from 0, so all three reported the absolute clock\n // instead of an elapsed time, and looked identical to each other.\n const prepareStart =\n this.hooks.onPrepareStart !== undefined || this.hooks.onPrepareEnd !== undefined ? performance.now() : 0;\n this.textCache.atlas.beginFrame();\n const list = buildRenderList(\n root,\n context.layout,\n context.text,\n this.surface.logicalWidth,\n this.surface.logicalHeight,\n this.surface.dpr,\n context.now,\n context.overlay ?? [],\n this.textCache\n );\n if (this.hooks.onPrepareEnd !== undefined) {\n this.hooks.onPrepareEnd(performance.now() - prepareStart);\n }\n\n const device = this.device!;\n const primitives = this.primitives!;\n const textured = this.textured!;\n const textures = this.textures!;\n const glyphPages = this.glyphPages!;\n // The build allocated atlas cells; fill the new ones before the\n // pass that samples them.\n glyphPages.flush();\n\n const uploadStart =\n this.hooks.onUploadStart !== undefined || this.hooks.onUploadEnd !== undefined ? performance.now() : 0;\n if (list.instanceCount > 0) {\n const buffer = this.ensureBuffer(this.instanceBuffer, list.instanceData.byteLength, INSTANCE_STRIDE_BYTES);\n if (buffer !== this.instanceBuffer) {\n this.instanceBuffer = buffer;\n // New GPU memory is undefined; force a full upload.\n this.cachedInstanceData = null;\n }\n this.uploadInstanceData(list.instanceData);\n }\n if (list.texturedCount > 0) {\n this.texturedBuffer = this.ensureBuffer(this.texturedBuffer, list.texturedData.byteLength, TEXTURED_STRIDE_BYTES);\n device.queue.writeBuffer(this.texturedBuffer, 0, list.texturedData.buffer, 0, list.texturedData.byteLength);\n }\n // The clip chain and the gradients are bound even when empty: the\n // shaders declare them and every instance names -1. The bind group\n // follows the buffers.\n const clipBuffer = this.ensureBuffer(\n this.clipBuffer,\n list.clipData.byteLength,\n CLIP_STRIDE_BYTES,\n storageBufferUsage\n );\n const gradientBuffer = this.ensureBuffer(\n this.gradientBuffer,\n list.gradientData.byteLength,\n GRADIENT_STRIDE_BYTES,\n storageBufferUsage\n );\n if (clipBuffer !== this.clipBuffer || gradientBuffer !== this.gradientBuffer || this.frameBindGroup === null) {\n this.clipBuffer = clipBuffer;\n this.gradientBuffer = gradientBuffer;\n this.frameBindGroup = device.createBindGroup({\n layout: this.frameLayout!,\n entries: [\n { binding: 0, resource: { buffer: clipBuffer } },\n { binding: 1, resource: { buffer: gradientBuffer } }\n ]\n });\n }\n if (list.clipCount > 0) {\n device.queue.writeBuffer(clipBuffer, 0, list.clipData.buffer, 0, list.clipData.byteLength);\n }\n if (list.gradientCount > 0) {\n device.queue.writeBuffer(gradientBuffer, 0, list.gradientData.buffer, 0, list.gradientData.byteLength);\n }\n const view = new Float32Array(VIEW_UNIFORM_FLOATS);\n view[0] = this.surface.logicalWidth || 1;\n view[1] = this.surface.logicalHeight || 1;\n view[2] = this.surface.dpr || 1;\n device.queue.writeBuffer(primitives.uniformBuffer, 0, view);\n device.queue.writeBuffer(textured.uniformBuffer, 0, view);\n if (this.hooks.onUploadEnd !== undefined) {\n this.hooks.onUploadEnd(performance.now() - uploadStart);\n }\n\n const encodeStart =\n this.hooks.onEncodeStart !== undefined || this.hooks.onEncodeEnd !== undefined ? performance.now() : 0;\n const commandEncoder = device.createCommandEncoder();\n const texture = this.surface.getCurrentTexture();\n const pass = commandEncoder.beginRenderPass({\n colorAttachments: [\n { view: texture.createView(), clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: 'clear', storeOp: 'store' }\n ]\n });\n\n // A pass retains the last scissor it was given, so an unclipped\n // command has to restore the full viewport rather than skip the\n // call: skipping left it drawing under whichever clip the\n // preceding command happened to install.\n const viewport = viewportScissor(this.surface.logicalWidth, this.surface.logicalHeight, this.surface.dpr);\n let currentScissor: ScissorRect | null = null;\n let currentKind: CommandKind | null = null;\n pass.setBindGroup(1, this.frameBindGroup!);\n this.lastDraws.primitiveDraws = 0;\n this.lastDraws.texturedDraws = 0;\n this.lastDraws.texturedInstances = 0;\n\n const commands = list.commands;\n for (let i = 0; i < commands.length; i++) {\n const command = commands[i];\n const scissor = command.scissor ?? viewport;\n if (scissor.width <= 0 || scissor.height <= 0) {\n // Everything under this clip is off screen; a zero scissor\n // draws nothing and some validators reject it.\n continue;\n }\n if (command.kind === CommandKind.Primitives) {\n if (command.end <= command.start) {\n continue;\n }\n if (currentKind !== CommandKind.Primitives) {\n pass.setPipeline(primitives.pipeline);\n pass.setVertexBuffer(0, primitives.vertexBuffer);\n pass.setVertexBuffer(1, this.instanceBuffer!);\n pass.setIndexBuffer(primitives.indexBuffer, 'uint16');\n pass.setBindGroup(0, primitives.uniformBindGroup);\n currentKind = CommandKind.Primitives;\n }\n currentScissor = setScissor(pass, currentScissor, scissor);\n pass.drawIndexed(primitives.indexCount, command.end - command.start, 0, 0, command.start);\n this.lastDraws.primitiveDraws++;\n continue;\n }\n\n const glyphs = command.kind === CommandKind.Glyphs;\n const bindGroup = glyphs\n ? glyphPages.bindGroup(command.page)\n : textures.imageBindGroup(command.source, command.drawWidth, command.drawHeight);\n if (bindGroup === null) {\n continue;\n }\n // Glyphs and images share one pipeline and one instance buffer;\n // only the sampled texture and the UV rectangle differ.\n if (currentKind !== CommandKind.Glyphs) {\n pass.setPipeline(textured.pipeline);\n pass.setVertexBuffer(0, textured.vertexBuffer);\n pass.setVertexBuffer(1, this.texturedBuffer!);\n pass.setIndexBuffer(textured.indexBuffer, 'uint16');\n currentKind = CommandKind.Glyphs;\n }\n currentScissor = setScissor(pass, currentScissor, scissor);\n pass.setBindGroup(0, bindGroup);\n // The builder already merged every run of glyphs sharing a page\n // and a scissor, so a screen of text in one font is one call.\n const count = glyphs ? command.end - command.start : 1;\n if (count <= 0) {\n continue;\n }\n pass.drawIndexed(textured.indexCount, count, 0, 0, glyphs ? command.start : command.instance);\n this.lastDraws.texturedDraws++;\n this.lastDraws.texturedInstances += count;\n }\n\n pass.end();\n const capture = this.takeCapture(device, commandEncoder, texture);\n device.queue.submit([commandEncoder.finish()]);\n capture?.();\n if (this.hooks.onEncodeEnd !== undefined) {\n this.hooks.onEncodeEnd(performance.now() - encodeStart);\n }\n }\n\n /**\n * When a capture is pending, encodes a copy of the frame into a\n * mappable buffer and returns the step that maps it after submit.\n */\n private takeCapture(device: GPUDevice, encoder: GPUCommandEncoder, texture: GPUTexture): (() => void) | null {\n const pending = this.pendingCapture;\n if (pending === null) {\n return null;\n }\n this.pendingCapture = null;\n const width = texture.width;\n const height = texture.height;\n // Rows are padded to 256 bytes, as copyTextureToBuffer requires.\n const bytesPerRow = Math.ceil((width * 4) / 256) * 256;\n const buffer = device.createBuffer({ size: bytesPerRow * height, usage: copyDstUsage | mapReadUsage });\n encoder.copyTextureToBuffer({ texture }, { buffer, bytesPerRow }, { width, height });\n const bgra = this.format === 'bgra8unorm' || this.format === 'bgra8unorm-srgb';\n return () => {\n buffer\n .mapAsync(mapModeRead)\n .then(() => {\n const mapped = new Uint8Array(buffer.getMappedRange());\n const data = new Uint8ClampedArray(width * height * 4);\n for (let y = 0; y < height; y++) {\n const row = y * bytesPerRow;\n const out = y * width * 4;\n for (let x = 0; x < width; x++) {\n const i = row + x * 4;\n const o = out + x * 4;\n data[o] = mapped[bgra ? i + 2 : i];\n data[o + 1] = mapped[i + 1];\n data[o + 2] = mapped[bgra ? i : i + 2];\n data[o + 3] = mapped[i + 3];\n }\n }\n buffer.unmap();\n buffer.destroy();\n pending.resolve({ width, height, data });\n })\n .catch((error: unknown) => {\n buffer.destroy();\n pending.reject(error instanceof Error ? error : new Error(String(error)));\n });\n };\n }\n\n /**\n * Clears the surface when there is nothing else to draw.\n */\n private clearFrame(): void {\n const device = this.device!;\n const commandEncoder = device.createCommandEncoder();\n const texture = this.surface.getCurrentTexture();\n const pass = commandEncoder.beginRenderPass({\n colorAttachments: [\n { view: texture.createView(), clearValue: { r: 0, g: 0, b: 0, a: 0 }, loadOp: 'clear', storeOp: 'store' }\n ]\n });\n pass.end();\n device.queue.submit([commandEncoder.finish()]);\n }\n\n /**\n * Updates the logical/physical size and reconfigures the canvas\n * context when the backing store changed.\n */\n resize(width: number, height: number, dpr: number = 1): void {\n if (this.disposed) {\n throw new WebGPUError('Renderer is disposed.', 'disposed');\n }\n const changed = this.surface.setLogicalSize(width, height, dpr);\n if (changed && this.device !== null && this.format !== null) {\n this.surface.configure(this.device, this.format);\n }\n }\n\n /**\n * Drops every shaped line and every atlas cell. The cells were\n * rasterised with whatever font the string resolved to at the time,\n * which was the fallback; the page textures go with them, since a\n * page index would otherwise be reused over stale pixels.\n */\n fontsChanged(): void {\n this.textCache.shaper.clear();\n this.textCache.atlas.reset();\n this.glyphPages?.dispose();\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.removeLostListener();\n this.surface.unconfigure();\n this.textures?.dispose();\n this.textures = null;\n this.glyphPages?.dispose();\n this.glyphPages = null;\n // The atlas outlives the device: its slots are plain packing, and\n // a re-initialized renderer re-uploads them into fresh textures.\n this.textCache.atlas.reset();\n this.instanceBuffer?.destroy();\n this.instanceBuffer = null;\n this.texturedBuffer?.destroy();\n this.texturedBuffer = null;\n this.clipBuffer?.destroy();\n this.clipBuffer = null;\n this.gradientBuffer?.destroy();\n this.gradientBuffer = null;\n this.frameBindGroup = null;\n this.frameLayout = null;\n this.primitives = null;\n this.textured = null;\n // The device is owned by the WebGPU initialization layer; the\n // renderer only borrows it. Do not destroy it here.\n this.device = null;\n }\n\n private uploadInstanceData(data: Float32Array): void {\n if (this.cachedInstanceData !== null && arraysEqual(this.cachedInstanceData, data)) {\n return;\n }\n this.device!.queue.writeBuffer(this.instanceBuffer!, 0, data.buffer, 0, data.byteLength);\n this.cachedInstanceData = new Float32Array(data);\n }\n\n /**\n * Returns a vertex buffer of at least `byteLength`, reusing the\n * current one when it is big enough and replacing it otherwise.\n */\n private ensureBuffer(\n current: GPUBuffer | null,\n byteLength: number,\n stride: number,\n usage: number = vertexBufferUsage\n ): GPUBuffer {\n if (current !== null && current.size >= byteLength) {\n return current;\n }\n current?.destroy();\n // Round up to whole instances, with headroom so a growing list does\n // not reallocate every frame.\n const size = Math.max(Math.ceil((byteLength * 1.5) / stride) * stride, stride * 16);\n return this.device!.createBuffer({ size, usage: usage | copyDstBufferUsage });\n }\n}\n\nfunction setScissor(pass: GPURenderPassEncoder, current: ScissorRect | null, next: ScissorRect): ScissorRect {\n if (\n current !== null &&\n current.x === next.x &&\n current.y === next.y &&\n current.width === next.width &&\n current.height === next.height\n ) {\n return current;\n }\n pass.setScissorRect(next.x, next.y, next.width, next.height);\n return next;\n}\n\nfunction arraysEqual(a: Float32Array, b: Float32Array): boolean {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) {\n return false;\n }\n }\n return true;\n}\n","/**\n * Just enough MPEG audio to feed an `AudioDecoder`, one arriving chunk\n * at a time.\n *\n * The sibling of `Mp4Demuxer`, and here for the same reason: WebCodecs\n * decodes and does not demux, so `AudioDecoder` takes\n * `EncodedAudioChunk`s and getting those out of an `.mp3` means\n * splitting the frames yourself. An `.mp3` has no container to walk,\n * only a run of self-describing frames, so this is a header parse\n * rather than a box tree.\n *\n * **It splits as the bytes arrive.** `Mp4Demuxer` parses a whole file\n * at once because a fifteen-second loop is small and its sample tables\n * live at the end. An `.mp3` is the opposite on both counts: a track\n * is megabytes, every frame describes itself, and\n * the bytes take longer to arrive than anything done with them takes\n * to run. So `push` takes whatever a stream reader just handed you,\n * returns the whole frames it can make from that and what it was\n * holding, and holds the partial tail for next time. Splitting the\n * whole of a five-minute track costs about 3 ms, so the incremental\n * shape is for latency rather than for throughput: it exists so the\n * first frames can be decoded while the rest is still on the wire.\n *\n * **What it understands.** MPEG-1, MPEG-2 and MPEG-2.5, layers I, II\n * and III, constant or variable bitrate, with an ID3v2 tag at the\n * front and junk anywhere. A Xing, Info or VBRI header frame is read\n * for what it knows about the file and then dropped, because it\n * carries no audio.\n *\n * **What it does not.** Free-format streams, whose frames declare no\n * bitrate and must be measured by finding the next header. They are\n * skipped as junk rather than mis-parsed into noise.\n */\n\n/** How a stream's frames are shaped, read from the first one. */\nexport interface Mp3Format {\n readonly version: 'mpeg1' | 'mpeg2' | 'mpeg2.5';\n /** 1, 2 or 3. Nearly always 3. */\n readonly layer: 1 | 2 | 3;\n readonly sampleRate: number;\n /** 1 for mono, 2 for every stereo mode. */\n readonly channels: number;\n /** The first frame's bitrate in bits per second; a variable bitrate stream's later frames differ. */\n readonly bitrate: number;\n /** What one frame decodes to: 1152, or 576 for layer III below MPEG-1, or 384 for layer I. */\n readonly samplesPerFrame: number;\n /**\n * Frames in the whole stream, when a Xing, Info or VBRI header said\n * so. The only way to know a variable bitrate file's duration\n * without reading all of it, and absent from most constant bitrate\n * files, which do not need one.\n */\n readonly totalFrames?: number;\n /** Bytes in the whole stream, when the same header said so. */\n readonly totalBytes?: number;\n}\n\n/** One frame, ready to become an `EncodedAudioChunk`. */\nexport interface Mp3Frame {\n /** The frame's own bytes, header included. A view into memory this splitter no longer touches. */\n readonly bytes: Uint8Array;\n /** Where the frame began, in bytes from the first byte pushed. */\n readonly offset: number;\n /** What this frame decodes to. */\n readonly samples: number;\n /** Where the frame begins, in samples from the first audio frame. */\n readonly sampleOffset: number;\n /** The same position in microseconds, which is what WebCodecs takes. */\n readonly timestampUs: number;\n readonly durationUs: number;\n}\n\n// ---------------------------------------------------------------------------\n// The header\n// ---------------------------------------------------------------------------\n\n/** Indexed by layer, then by the header's four bitrate bits, in kbps. */\nconst MPEG1_BITRATES: readonly (readonly number[])[] = [\n [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],\n [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],\n [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0]\n];\n\nconst MPEG2_BITRATES: readonly (readonly number[])[] = [\n [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],\n [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],\n [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0]\n];\n\n/** Indexed by the header's two version bits, then its two rate bits. */\nconst SAMPLE_RATES: Readonly<Record<number, readonly number[]>> = {\n 0: [11025, 12000, 8000],\n 2: [22050, 24000, 16000],\n 3: [44100, 48000, 32000]\n};\n\nconst VERSIONS: Readonly<Record<number, Mp3Format['version']>> = {\n 0: 'mpeg2.5',\n 2: 'mpeg2',\n 3: 'mpeg1'\n};\n\n/** The longest a frame can be: MPEG-1 layer II at 384 kbps and 32 kHz, with padding. */\nconst MAX_FRAME_BYTES = 1729;\n\n/** A frame's four header bytes, read. */\nexport interface Mp3Header {\n readonly version: Mp3Format['version'];\n readonly layer: 1 | 2 | 3;\n readonly sampleRate: number;\n readonly channels: number;\n readonly bitrate: number;\n readonly samples: number;\n /** The whole frame, header included. */\n readonly length: number;\n /** True when the mode bits say something other than mono, which is where a Xing header sits. */\n readonly stereo: boolean;\n}\n\n/**\n * The four bytes at `at`, read as a frame header, or null when they\n * are not one.\n *\n * Every reserved combination is a rejection rather than a guess: with\n * eleven sync bits, roughly one position in two thousand of arbitrary\n * data looks like the start of a header, and the reserved values are\n * most of what rules the impostors out.\n */\nexport function readMp3Header(bytes: Uint8Array, at: number): Mp3Header | null {\n if (at < 0 || at + 4 > bytes.length) {\n return null;\n }\n if (bytes[at] !== 0xff || (bytes[at + 1]! & 0xe0) !== 0xe0) {\n return null;\n }\n const versionBits = (bytes[at + 1]! >> 3) & 0x03;\n const layerBits = (bytes[at + 1]! >> 1) & 0x03;\n if (versionBits === 1 || layerBits === 0) {\n return null;\n }\n const rateBits = (bytes[at + 2]! >> 2) & 0x03;\n const bitrateBits = (bytes[at + 2]! >> 4) & 0x0f;\n // A bitrate of zero is the free format, whose frame length is not\n // declared anywhere; fifteen is reserved outright.\n if (rateBits === 3 || bitrateBits === 0 || bitrateBits === 15) {\n return null;\n }\n\n const version = VERSIONS[versionBits]!;\n const layer = (4 - layerBits) as 1 | 2 | 3;\n const mpeg1 = version === 'mpeg1';\n const bitrate = (mpeg1 ? MPEG1_BITRATES : MPEG2_BITRATES)[layer - 1]![bitrateBits]! * 1000;\n const sampleRate = SAMPLE_RATES[versionBits]![rateBits]!;\n const padding = (bytes[at + 2]! >> 1) & 0x01;\n const stereo = ((bytes[at + 3]! >> 6) & 0x03) !== 3;\n\n const samples = layer === 1 ? 384 : layer === 2 || mpeg1 ? 1152 : 576;\n const length =\n layer === 1\n ? (Math.floor((12 * bitrate) / sampleRate) + padding) * 4\n : Math.floor((samples / 8) * (bitrate / sampleRate)) + padding;\n if (length < 4 || length > MAX_FRAME_BYTES) {\n return null;\n }\n\n return { version, layer, sampleRate, channels: stereo ? 2 : 1, bitrate, samples, length, stereo };\n}\n\n/** Two headers describe the same stream when everything but the bitrate agrees. */\nfunction alike(one: Mp3Header, other: Mp3Header): boolean {\n return one.version === other.version && one.layer === other.layer && one.sampleRate === other.sampleRate;\n}\n\n// ---------------------------------------------------------------------------\n// Tags\n// ---------------------------------------------------------------------------\n\n/**\n * The length of an ID3v2 tag beginning at byte zero, or zero when\n * there is none, or -1 when the first ten bytes have not arrived and\n * the question cannot be answered yet.\n */\nfunction id3Length(bytes: Uint8Array): number {\n if (bytes.length < 3) {\n return bytes.length === 0 || bytes[0] === 0x49 ? -1 : 0;\n }\n if (bytes[0] !== 0x49 || bytes[1] !== 0x44 || bytes[2] !== 0x33) {\n return 0;\n }\n if (bytes.length < 10) {\n return -1;\n }\n // A syncsafe integer: seven bits per byte, so no byte can look like a sync word.\n const size =\n (bytes[6]! & 0x7f) * 0x200000 + (bytes[7]! & 0x7f) * 0x4000 + (bytes[8]! & 0x7f) * 0x80 + (bytes[9]! & 0x7f);\n // A footer, when the flags say there is one, is ten bytes more.\n return 10 + size + ((bytes[5]! & 0x10) !== 0 ? 10 : 0);\n}\n\nfunction ascii(bytes: Uint8Array, at: number, text: string): boolean {\n if (at + text.length > bytes.length) {\n return false;\n }\n for (let i = 0; i < text.length; i += 1) {\n if (bytes[at + i] !== text.charCodeAt(i)) {\n return false;\n }\n }\n return true;\n}\n\nfunction u32(bytes: Uint8Array, at: number): number {\n return bytes[at]! * 0x1000000 + bytes[at + 1]! * 0x10000 + bytes[at + 2]! * 0x100 + bytes[at + 3]!;\n}\n\n/**\n * What a Xing, Info or VBRI header in this frame says about the whole\n * stream, or null when the frame carries audio like any other.\n *\n * Encoders put one in the first frame of a variable bitrate file so\n * that a player can know the duration without reading to the end, and\n * many put one in a constant bitrate file too, where it is called Info\n * instead. The frame decodes to silence either way, so a caller wants\n * the numbers and not the frame.\n */\nfunction vbrHeader(frame: Uint8Array, head: Mp3Header): { totalFrames?: number; totalBytes?: number } | null {\n // Xing sits immediately after the side information, whose size is\n // fixed by the version and the channel mode.\n const sideInfo = head.version === 'mpeg1' ? (head.stereo ? 32 : 17) : head.stereo ? 17 : 9;\n const at = 4 + sideInfo;\n if (ascii(frame, at, 'Xing') || ascii(frame, at, 'Info')) {\n if (at + 8 > frame.length) {\n return {};\n }\n const flags = u32(frame, at + 4);\n let field = at + 8;\n const found: { totalFrames?: number; totalBytes?: number } = {};\n if ((flags & 0x01) !== 0) {\n if (field + 4 > frame.length) return found;\n found.totalFrames = u32(frame, field);\n field += 4;\n }\n if ((flags & 0x02) !== 0) {\n if (field + 4 > frame.length) return found;\n found.totalBytes = u32(frame, field);\n }\n return found;\n }\n // VBRI is Fraunhofer's, and always sits 32 bytes after the header.\n if (ascii(frame, 36, 'VBRI')) {\n if (frame.length < 54) {\n return {};\n }\n return { totalBytes: u32(frame, 44), totalFrames: u32(frame, 48) };\n }\n return null;\n}\n\n// ---------------------------------------------------------------------------\n// The splitter\n// ---------------------------------------------------------------------------\n\nconst EMPTY: readonly Mp3Frame[] = [];\n\n/**\n * A stream of MPEG audio, split into frames as it arrives.\n *\n * ```ts\n * const frames = new Mp3Frames();\n * const reader = response.body.getReader();\n * for (;;) {\n * const { done, value } = await reader.read();\n * if (done) break;\n * for (const frame of frames.push(value)) {\n * decoder.decode(new EncodedAudioChunk({ type: 'key', ...frame }));\n * }\n * }\n * ```\n *\n * Every frame of layer III is a key frame as WebCodecs counts them,\n * even though the bit reservoir means a frame may lean on up to 511\n * bytes of its predecessors. That is why decoding must begin at the\n * start of the stream rather than in the middle of it: the first\n * frames after an arbitrary offset decode to noise.\n */\nexport class Mp3Frames {\n /** Bytes pushed but not yet made into frames. */\n private carry: Uint8Array = new Uint8Array(0);\n /** Bytes consumed from the stream, which is what makes an offset absolute. */\n private consumed = 0;\n /** Still to skip from an ID3v2 tag that ran past the end of a chunk. */\n private skipping = 0;\n /** Nothing has been read yet, so the front of the stream may be a tag. */\n private atStart = true;\n /** A first frame has been confirmed by a second one behind it. */\n private locked = false;\n private shape: Mp3Format | null = null;\n private frames = 0;\n private samples = 0;\n\n /** How the stream is shaped, once its first frame has been read. */\n get format(): Mp3Format | null {\n return this.shape;\n }\n\n /** Audio frames returned so far, the dropped header frame not counted. */\n get frameCount(): number {\n return this.frames;\n }\n\n /** Samples those frames decode to, which is the duration read so far. */\n get sampleCount(): number {\n return this.samples;\n }\n\n /** Seconds of audio returned so far, or zero before the first frame. */\n get seconds(): number {\n return this.shape === null ? 0 : this.samples / this.shape.sampleRate;\n }\n\n /**\n * Take the next bytes of the stream and return every whole frame\n * they complete. The partial tail is held until the rest of it\n * arrives, so a caller may hand over chunks of any size, including\n * ones that end in the middle of a header.\n */\n push(bytes: Uint8Array): readonly Mp3Frame[] {\n if (bytes.length === 0) {\n return EMPTY;\n }\n this.append(bytes);\n return this.split(false);\n }\n\n /**\n * The stream has ended: return anything the tail still holds.\n *\n * Usually nothing, because `push` returns each frame the moment its\n * last byte arrives. It matters for a stream so short that no second\n * frame ever confirmed the first, which is otherwise held back\n * forever rather than risk locking onto an impostor.\n */\n flush(): readonly Mp3Frame[] {\n const rest = this.split(true);\n this.carry = new Uint8Array(0);\n return rest;\n }\n\n private append(bytes: Uint8Array): void {\n if (this.skipping > 0) {\n const skipped = Math.min(this.skipping, bytes.length);\n this.skipping -= skipped;\n this.consumed += skipped;\n bytes = bytes.subarray(skipped);\n if (bytes.length === 0) {\n return;\n }\n }\n if (this.carry.length === 0) {\n this.carry = bytes;\n return;\n }\n const joined = new Uint8Array(this.carry.length + bytes.length);\n joined.set(this.carry);\n joined.set(bytes, this.carry.length);\n this.carry = joined;\n }\n\n /**\n * Walk the carry, emitting frames, and leave behind whatever could\n * not be finished. `ending` allows a first frame to be taken on\n * trust, because there will be no later chunk to confirm it with.\n */\n private split(ending: boolean): readonly Mp3Frame[] {\n if (this.atStart) {\n const tag = id3Length(this.carry);\n if (tag === -1) {\n return EMPTY;\n }\n this.atStart = false;\n if (tag > 0) {\n const skipped = Math.min(tag, this.carry.length);\n this.skipping = tag - skipped;\n this.consumed += skipped;\n this.carry = this.carry.subarray(skipped);\n if (this.skipping > 0) {\n this.carry = new Uint8Array(0);\n return EMPTY;\n }\n }\n }\n\n const found: Mp3Frame[] = [];\n let at = 0;\n for (;;) {\n const head = readMp3Header(this.carry, at);\n if (head === null) {\n if (at + 4 > this.carry.length) {\n break;\n }\n at += 1;\n continue;\n }\n if (at + head.length > this.carry.length) {\n break;\n }\n if (!this.locked) {\n const next = readMp3Header(this.carry, at + head.length);\n if (next !== null && alike(head, next)) {\n this.locked = true;\n } else if (next === null && at + head.length + 4 > this.carry.length) {\n // Out of bytes rather than out of luck. Wait for the chunk\n // carrying the confirming header; only when the stream has\n // ended, and none is coming, is the frame taken on trust.\n if (!ending) {\n break;\n }\n this.locked = true;\n } else {\n at += 1;\n continue;\n }\n }\n\n const bytes = this.carry.subarray(at, at + head.length);\n const vbr = this.shape === null ? vbrHeader(bytes, head) : null;\n if (this.shape === null) {\n this.shape = {\n version: head.version,\n layer: head.layer,\n sampleRate: head.sampleRate,\n channels: head.channels,\n bitrate: head.bitrate,\n samplesPerFrame: head.samples,\n ...(vbr?.totalFrames === undefined ? {} : { totalFrames: vbr.totalFrames }),\n ...(vbr?.totalBytes === undefined ? {} : { totalBytes: vbr.totalBytes })\n };\n }\n if (vbr === null) {\n found.push({\n bytes,\n offset: this.consumed + at,\n samples: head.samples,\n sampleOffset: this.samples,\n timestampUs: Math.round((this.samples / head.sampleRate) * 1e6),\n durationUs: Math.round((head.samples / head.sampleRate) * 1e6)\n });\n this.frames += 1;\n this.samples += head.samples;\n }\n at += head.length;\n }\n\n if (at > 0) {\n this.consumed += at;\n this.carry = this.carry.subarray(at);\n }\n return found;\n }\n}\n\n/**\n * Split a whole `.mp3` at once, for a caller that already holds all of\n * it. The same walk `Mp3Frames` does, without the streaming.\n */\nexport function splitMp3Frames(bytes: Uint8Array): { format: Mp3Format | null; frames: readonly Mp3Frame[] } {\n const splitter = new Mp3Frames();\n const frames = [...splitter.push(bytes), ...splitter.flush()];\n return { format: splitter.format, frames };\n}\n","/**\n * Just enough ISO base media file format to feed a `VideoDecoder`.\n *\n * WebCodecs decodes; it does not demux. `VideoDecoder` takes\n * `EncodedVideoChunk`s and a `VideoDecoderConfig`, and getting those\n * out of an `.mp4` means walking the sample tables yourself. That is\n * what this does, and the reason it is here rather than a dependency\n * is that `gesso-core`'s only dependency is `@webgpu/types`: a\n * renderer that pulls in a general-purpose container parser to play a\n * demo video has bought a great deal it does not use.\n *\n * **What it understands.** Progressive (non-fragmented) MP4 with a\n * `moov` and a `mdat`: `stsd` (with `avcC` or `hvcC`), `stts`, `ctts`,\n * `stsc`, `stsz`/`stz2`, `stco`/`co64` and `stss`. That is what every\n * encoder writes by default and what the `-movflags +faststart` files\n * on the web are. Video tracks through `demuxMp4Video`, and audio\n * tracks through `demuxMp4Audio` — which reads the same tables and\n * differs only in which sample entry it understands.\n *\n * **What it does not.** Fragmented MP4 (`moof`/`traf`/`trun`), which\n * is what DASH and HLS segments are, and which is a different parse\n * rather than a harder one. It is detected and named rather than\n * mis-parsed into silence — a demuxer that returns zero samples for a\n * file the browser plays fine is a bug that takes an afternoon to\n * find.\n *\n * **The whole file is parsed at once**, from one `ArrayBuffer`. Byte\n * ranges and progressive parsing are what you want for an hour of\n * video and are pure cost for the fifteen-second loops this exists\n * for; the seam is `VideoResolver`, which decides what to fetch.\n */\n\n/** One encoded frame: where it is in the file, and when it is shown. */\nexport interface Mp4Sample {\n /** Byte offset into the file. */\n readonly offset: number;\n readonly size: number;\n /** Presentation time in microseconds, which is what WebCodecs takes. */\n readonly timestampUs: number;\n readonly durationUs: number;\n /** A sync sample: decoding may start here. */\n readonly isKey: boolean;\n}\n\nexport interface Mp4VideoTrack {\n readonly codec: string;\n readonly codedWidth: number;\n readonly codedHeight: number;\n /**\n * The codec's own configuration record (`avcC`, `hvcC`), which\n * `VideoDecoder.configure` needs for length-prefixed streams. Absent\n * for codecs that carry their configuration in-band.\n */\n readonly description?: Uint8Array;\n readonly samples: readonly Mp4Sample[];\n /** The whole track, in microseconds; where a loop wraps. */\n readonly durationUs: number;\n /**\n * The shortest frame interval in the track, in microseconds — the\n * fastest the picture ever changes.\n *\n * What paces playback: a caller that samples this often never misses\n * a frame, and one that samples faster only discovers that nothing\n * changed. For constant-rate video, which is nearly all of it, this\n * is simply the frame interval. For variable-rate video the shortest\n * is the safe choice over the average, because sampling too often\n * costs a wake-up and sampling too rarely drops a picture.\n */\n readonly frameDurationUs: number;\n}\n\n/**\n * The audio track of an MP4, as far as this reads it.\n *\n * Deliberately the same shape as `Mp4VideoTrack` where the two agree,\n * because the sample tables they come out of are the same tables: only\n * the sample *entry* differs, and only in which fields it carries.\n *\n * **Nothing in this framework plays it, and that is not this file's\n * problem.** `AudioContext` does not exist on a worker, so sound is\n * the shell's to play — see `VideoClock` for the arrangement and why\n * it inverts which of the two owns time. What the demuxer is for here\n * is the question that comes *before* that one: whether this clip has\n * sound at all, and what it would take to decode it. A player that\n * cannot answer that has to guess whether to show a mute button.\n */\nexport interface Mp4AudioTrack {\n /** A codec string for `AudioDecoder.configure`, e.g. `mp4a.40.2`. */\n readonly codec: string;\n readonly sampleRate: number;\n readonly channels: number;\n /** Bits per sample as the container declares them; 16 for nearly everything. */\n readonly sampleSize: number;\n /**\n * The decoder configuration, where the codec keeps one out of band.\n * For AAC this is the `AudioSpecificConfig` out of the `esds`, which\n * `AudioDecoder` takes as its `description`.\n */\n readonly description?: Uint8Array;\n readonly samples: readonly Mp4Sample[];\n readonly durationUs: number;\n}\n\nclass Reader {\n private at = 0;\n\n constructor(private readonly view: DataView) {}\n\n get offset(): number {\n return this.at;\n }\n\n set offset(value: number) {\n this.at = value;\n }\n\n get remaining(): number {\n return this.view.byteLength - this.at;\n }\n\n u8(): number {\n const value = this.view.getUint8(this.at);\n this.at += 1;\n return value;\n }\n\n u16(): number {\n const value = this.view.getUint16(this.at);\n this.at += 2;\n return value;\n }\n\n u32(): number {\n const value = this.view.getUint32(this.at);\n this.at += 4;\n return value;\n }\n\n i32(): number {\n const value = this.view.getInt32(this.at);\n this.at += 4;\n return value;\n }\n\n u64(): number {\n const high = this.u32();\n const low = this.u32();\n // Beyond 2^53 an MP4 is not a thing this plays, and Number keeps\n // every offset arithmetic below honest.\n return high * 0x1_0000_0000 + low;\n }\n\n ascii(length: number): string {\n let text = '';\n for (let index = 0; index < length; index++) {\n text += String.fromCharCode(this.view.getUint8(this.at + index));\n }\n this.at += length;\n return text;\n }\n\n bytes(length: number): Uint8Array {\n const start = this.view.byteOffset + this.at;\n this.at += length;\n return new Uint8Array(this.view.buffer.slice(start, start + length));\n }\n\n skip(length: number): void {\n this.at += length;\n }\n}\n\ninterface BoxHeader {\n readonly type: string;\n /** Where the box's payload starts. */\n readonly start: number;\n /** One past the box's last byte. */\n readonly end: number;\n}\n\nfunction readBoxHeader(reader: Reader, limit: number): BoxHeader | null {\n if (reader.remaining < 8 || reader.offset + 8 > limit) {\n return null;\n }\n const boxStart = reader.offset;\n let size = reader.u32();\n const type = reader.ascii(4);\n if (size === 1) {\n size = reader.u64();\n } else if (size === 0) {\n size = limit - boxStart;\n }\n const end = boxStart + size;\n if (size < 8 || end > limit) {\n return null;\n }\n return { type, start: reader.offset, end };\n}\n\n/** Walks the boxes directly inside `[from, to)`, calling back for each. */\nfunction walkBoxes(reader: Reader, from: number, to: number, visit: (box: BoxHeader) => void): void {\n reader.offset = from;\n for (;;) {\n const box = readBoxHeader(reader, to);\n if (box === null) {\n return;\n }\n const resume = box.end;\n visit(box);\n reader.offset = resume;\n if (resume >= to) {\n return;\n }\n }\n}\n\n/** Version and flags of a full box, which most of the sample tables are. */\nfunction readFullBoxVersion(reader: Reader): number {\n const version = reader.u8();\n reader.skip(3);\n return version;\n}\n\n/** The same, for the boxes whose flags say which fields are present. */\nfunction readFullBox(reader: Reader): { version: number; flags: number } {\n const version = reader.u8();\n const flags = (reader.u8() << 16) | (reader.u8() << 8) | reader.u8();\n return { version, flags };\n}\n\n/**\n * The per-track defaults a fragmented file states once, in `trex`.\n *\n * A fragment that omits a sample's duration, size or flags means \"the\n * default\", and the default is here rather than in the fragment —\n * which is the whole economy of the format: a `trun` for two hundred\n * samples of constant-bitrate video can be a header and nothing else.\n */\ninterface TrackExtends {\n readonly trackId: number;\n readonly defaultSampleDuration: number;\n readonly defaultSampleSize: number;\n readonly defaultSampleFlags: number;\n}\n\ninterface SampleTable {\n timescale: number;\n /** Decode deltas, expanded per sample. */\n deltas: number[];\n /** Composition offsets, per sample; empty when there is no `ctts`. */\n compositionOffsets: number[];\n sizes: number[];\n chunkOffsets: number[];\n /** `(firstChunk, samplesPerChunk)` runs. */\n chunkRuns: { firstChunk: number; samplesPerChunk: number }[];\n /** 1-based sample numbers that are sync samples; empty means all are. */\n syncSamples: number[];\n codec: string;\n description?: Uint8Array;\n codedWidth: number;\n codedHeight: number;\n /** From `tkhd`; what a fragment's `tfhd` names to say which track it belongs to. */\n trackId: number;\n /** Audio only; zero on a video track. */\n sampleRate: number;\n channels: number;\n sampleSize: number;\n}\n\n/** Which handler a track declares: the one field that says what it is. */\ntype TrackKind = 'vide' | 'soun';\n\n/**\n * Reads the first video track out of an MP4.\n *\n * Throws with a description of what it found rather than returning\n * something empty: a caller that asked for a video and got no samples\n * cannot tell \"this file has no video track\" from \"this parser does\n * not understand this file\", and those want different fixes.\n */\nexport function demuxMp4Video(data: ArrayBuffer): Mp4VideoTrack {\n const { reader, moov, moofs, sawFtyp } = openMp4(data);\n if (!sawFtyp && moov === null) {\n throw new Error('Not an MP4: no ftyp or moov box at the top level of the file.');\n }\n if (moov === null) {\n throw new Error('This MP4 has no moov box, so there is no sample table to read.');\n }\n\n const table = findTrack(reader, moov, 'vide');\n if (table === null) {\n throw new Error('This MP4 has no video track, or none this demuxer recognises.');\n }\n\n const samples = samplesOf(reader, table, moov, moofs);\n if (samples.length === 0) {\n throw new Error(\n moofs.length > 0\n ? 'This fragmented MP4 has no samples for its video track. Its fragments may be in other files: ' +\n 'this reads the fragments in the buffer it was given, not an index of segments to fetch.'\n : 'This MP4 video track has no samples.'\n );\n }\n // The furthest any frame is shown, not the last one submitted: with\n // B-frames those are different samples, and this is where a loop\n // wraps.\n let durationUs = 0;\n let frameDurationUs = Infinity;\n for (const sample of samples) {\n durationUs = Math.max(durationUs, sample.timestampUs + sample.durationUs);\n if (sample.durationUs > 0 && sample.durationUs < frameDurationUs) {\n frameDurationUs = sample.durationUs;\n }\n }\n return {\n codec: table.codec,\n codedWidth: table.codedWidth,\n codedHeight: table.codedHeight,\n description: table.description,\n samples,\n durationUs,\n // A track whose every sample claims zero duration says nothing\n // about its rate, so it is paced as sixty rather than as infinity.\n frameDurationUs: Number.isFinite(frameDurationUs) ? frameDurationUs : Math.round(1_000_000 / 60)\n };\n}\n\n/** The top-level walk both entry points start from. */\nfunction openMp4(data: ArrayBuffer): {\n reader: Reader;\n moov: BoxHeader | null;\n moofs: BoxHeader[];\n sawFtyp: boolean;\n} {\n const reader = new Reader(new DataView(data));\n const limit = data.byteLength;\n let moov: BoxHeader | null = null;\n const moofs: BoxHeader[] = [];\n let sawFtyp = false;\n walkBoxes(reader, 0, limit, box => {\n if (box.type === 'ftyp') {\n sawFtyp = true;\n } else if (box.type === 'moov') {\n moov = box;\n } else if (box.type === 'moof') {\n moofs.push(box);\n }\n });\n return { reader, moov, moofs, sawFtyp };\n}\n\n/**\n * Moves a track's presentation times so the first picture is at zero.\n *\n * **A file's earliest presentation timestamp is not required to be\n * zero, and for anything encoded with B-frames it is not.** A `ctts`\n * shifts each sample's composition time forward of its decode time,\n * and the shift applied to the first sample is the reorder depth: two\n * frames, for the clips x264 writes by default. So a plain six second\n * clip at 24fps has its first picture at 83333us and its last ending\n * at 6.083s, and a player that believed those numbers would report a\n * duration 83ms too long and, far worse, have no picture at all to\n * show for position zero.\n *\n * That last part is not hypothetical. It is exactly what a paused\n * clip looked like before this existed: `present(0)` found nothing\n * due, because nothing *was* due, and since a paused clip never asks\n * again the still stayed blank forever. A playing one hid it by\n * advancing past 83ms within two frames.\n *\n * Subtracting a constant keeps every sample's order, duration and\n * relationship to its neighbours; all it changes is where the clock\n * starts. Which is what an edit list would usually be doing, and is\n * the part of one worth having.\n */\nfunction rebaseToZero(samples: Mp4Sample[]): Mp4Sample[] {\n if (samples.length === 0) {\n return samples;\n }\n let earliest = Infinity;\n for (const sample of samples) {\n earliest = Math.min(earliest, sample.timestampUs);\n }\n if (earliest <= 0 || !Number.isFinite(earliest)) {\n return samples;\n }\n return samples.map(sample => ({ ...sample, timestampUs: sample.timestampUs - earliest }));\n}\n\n/**\n * A track's samples, from wherever this file keeps them.\n *\n * The two shapes are not a spectrum: a progressive file's samples are\n * in tables in the `moov` and a fragmented file's are described inline\n * in each `moof`, and a file is one or the other. `mvex` is the\n * declaration — a file that has it is fragmented even if this buffer\n * happens to hold no fragments yet — and the sample tables are the\n * fallback, so a file with neither comes back empty rather than\n * half-read.\n */\nfunction samplesOf(reader: Reader, table: SampleTable, moov: BoxHeader, moofs: readonly BoxHeader[]): Mp4Sample[] {\n const defaults = readTrackExtends(reader, moov);\n const samples =\n defaults.size === 0 && moofs.length === 0\n ? buildSamples(table)\n : buildFragmentedSamples(reader, moofs, table, defaults);\n return rebaseToZero(samples);\n}\n\nfunction findTrack(reader: Reader, moov: BoxHeader, kind: TrackKind): SampleTable | null {\n let found: SampleTable | null = null;\n walkBoxes(reader, moov.start, moov.end, trak => {\n if (trak.type !== 'trak' || found !== null) {\n return;\n }\n const table = readTrack(reader, trak, kind);\n if (table !== null) {\n found = table;\n }\n });\n return found;\n}\n\nfunction readTrack(reader: Reader, trak: BoxHeader, kind: TrackKind): SampleTable | null {\n let mdia: BoxHeader | null = null;\n let trackId = 0;\n walkBoxes(reader, trak.start, trak.end, box => {\n if (box.type === 'tkhd') {\n const version = readFullBoxVersion(reader);\n // Creation and modification times, which are two words in\n // version 0 and two long words in version 1; the track id\n // follows them either way.\n reader.skip(version === 1 ? 16 : 8);\n trackId = reader.u32();\n return;\n }\n if (box.type === 'mdia') {\n mdia = box;\n }\n });\n if (mdia === null) {\n return null;\n }\n const media = mdia as BoxHeader;\n let timescale = 0;\n let matches = false;\n let minf: BoxHeader | null = null;\n walkBoxes(reader, media.start, media.end, box => {\n if (box.type === 'mdhd') {\n const version = readFullBoxVersion(reader);\n if (version === 1) {\n reader.skip(16);\n timescale = reader.u32();\n } else {\n reader.skip(8);\n timescale = reader.u32();\n }\n return;\n }\n if (box.type === 'hdlr') {\n readFullBoxVersion(reader);\n reader.skip(4);\n matches = reader.ascii(4) === kind;\n return;\n }\n if (box.type === 'minf') {\n minf = box;\n }\n });\n if (!matches || minf === null || timescale <= 0) {\n return null;\n }\n let stbl: BoxHeader | null = null;\n walkBoxes(reader, (minf as BoxHeader).start, (minf as BoxHeader).end, box => {\n if (box.type === 'stbl') {\n stbl = box;\n }\n });\n if (stbl === null) {\n return null;\n }\n const table = readSampleTable(reader, stbl as BoxHeader, timescale, kind);\n if (table !== null) {\n table.trackId = trackId;\n }\n return table;\n}\n\nfunction readSampleTable(reader: Reader, stbl: BoxHeader, timescale: number, kind: TrackKind): SampleTable | null {\n const table: SampleTable = {\n timescale,\n deltas: [],\n compositionOffsets: [],\n sizes: [],\n chunkOffsets: [],\n chunkRuns: [],\n syncSamples: [],\n codec: '',\n codedWidth: 0,\n codedHeight: 0,\n trackId: 0,\n sampleRate: 0,\n channels: 0,\n sampleSize: 0\n };\n\n walkBoxes(reader, stbl.start, stbl.end, box => {\n switch (box.type) {\n case 'stsd':\n if (kind === 'soun') {\n readAudioSampleDescription(reader, box, table);\n } else {\n readSampleDescription(reader, box, table);\n }\n break;\n case 'stts': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n const count = reader.u32();\n const delta = reader.u32();\n for (let n = 0; n < count; n++) {\n table.deltas.push(delta);\n }\n }\n break;\n }\n case 'ctts': {\n const version = readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n const count = reader.u32();\n // Version 1 offsets are signed; version 0's are specified\n // unsigned but written signed by plenty of encoders, and\n // reading them signed is what every player does.\n const offset = version === 1 ? reader.i32() : reader.i32();\n for (let n = 0; n < count; n++) {\n table.compositionOffsets.push(offset);\n }\n }\n break;\n }\n case 'stsz': {\n readFullBoxVersion(reader);\n const uniform = reader.u32();\n const count = reader.u32();\n for (let index = 0; index < count; index++) {\n table.sizes.push(uniform === 0 ? reader.u32() : uniform);\n }\n break;\n }\n case 'stz2': {\n readFullBoxVersion(reader);\n reader.skip(3);\n const fieldSize = reader.u8();\n const count = reader.u32();\n for (let index = 0; index < count; index++) {\n if (fieldSize === 16) {\n table.sizes.push(reader.u16());\n } else if (fieldSize === 8) {\n table.sizes.push(reader.u8());\n } else {\n // 4-bit sizes pack two samples per byte.\n const pair = reader.u8();\n table.sizes.push(pair >> 4);\n if (index + 1 < count) {\n table.sizes.push(pair & 0x0f);\n index++;\n }\n }\n }\n break;\n }\n case 'stsc': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n const firstChunk = reader.u32();\n const samplesPerChunk = reader.u32();\n reader.skip(4);\n table.chunkRuns.push({ firstChunk, samplesPerChunk });\n }\n break;\n }\n case 'stco': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n table.chunkOffsets.push(reader.u32());\n }\n break;\n }\n case 'co64': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n table.chunkOffsets.push(reader.u64());\n }\n break;\n }\n case 'stss': {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n for (let index = 0; index < entries; index++) {\n table.syncSamples.push(reader.u32());\n }\n break;\n }\n default:\n break;\n }\n });\n\n return table.codec === '' ? null : table;\n}\n\n/**\n * The visual sample entry, and the codec configuration inside it.\n *\n * The 78 bytes skipped are `VisualSampleEntry`'s fixed fields, which\n * nothing here needs beyond the width and height: six reserved bytes\n * and a data reference index, then sixteen pre-defined/reserved, the\n * dimensions, two resolutions, a reserved word, a frame count, a\n * thirty-two byte compressor name, a depth and a pre-defined.\n */\nfunction readSampleDescription(reader: Reader, stsd: BoxHeader, table: SampleTable): void {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n if (entries === 0) {\n return;\n }\n const entry = readBoxHeader(reader, stsd.end);\n if (entry === null) {\n return;\n }\n reader.skip(6);\n reader.skip(2);\n reader.skip(16);\n table.codedWidth = reader.u16();\n table.codedHeight = reader.u16();\n reader.skip(4 + 4 + 4 + 2 + 32 + 2 + 2);\n const format = entry.type;\n walkBoxes(reader, reader.offset, entry.end, child => {\n if (child.type === 'avcC') {\n const record = reader.bytes(child.end - child.start);\n table.description = record;\n // `avc1.PPCCLL`: profile, constraint flags and level, straight\n // out of the record's second, third and fourth bytes.\n const profile = record[1] ?? 0;\n const compat = record[2] ?? 0;\n const level = record[3] ?? 0;\n table.codec = `${format}.${hex2(profile)}${hex2(compat)}${hex2(level)}`;\n return;\n }\n if (child.type === 'hvcC') {\n table.description = reader.bytes(child.end - child.start);\n // A full HEVC codec string needs the general profile space,\n // tier, compatibility flags and six constraint bytes assembled\n // in a particular order. Chrome accepts the bare format with a\n // description, and getting it subtly wrong is worse than not\n // claiming it.\n table.codec = format;\n return;\n }\n if (child.type === 'av1C') {\n const record = reader.bytes(child.end - child.start);\n // Deliberately *not* kept as a description: AV1 carries its\n // sequence header in-band, and `VideoDecoder` rejects a config\n // that supplies one as well.\n table.codec = av1CodecString(format, record);\n return;\n }\n if (child.type === 'vpcC') {\n const record = reader.bytes(child.end - child.start);\n table.codec = vp9CodecString(format, record);\n }\n });\n if (table.codec === '') {\n // No configuration box: an in-band codec, or one this does not\n // know. The format alone is a legitimate codec string for some of\n // them, and `VideoDecoder.isConfigSupported` is the right judge.\n table.codec = format;\n }\n}\n\n/**\n * The audio sample entry, and the codec configuration inside it.\n *\n * The fixed fields are `AudioSampleEntry`'s: six reserved bytes and a\n * data reference index, then eight bytes that were a version, a\n * revision and a vendor in QuickTime and are reserved here, the\n * channel count, the sample size, two pre-defined and two reserved,\n * and a sample rate written as 16.16 fixed point — of which only the\n * whole part is ever meaningful, because no encoder writes a\n * fractional sample rate.\n *\n * **Version 1 and 2 entries are not read past their fixed fields.**\n * A version 1 `AudioSampleEntry` carries four extra fields and a\n * version 2 carries a different layout entirely; both are rare outside\n * QuickTime, and the child boxes this actually wants are found by\n * walking from the end of whichever header was read. Getting the\n * header length wrong would make that walk start mid-box and find\n * nothing, which is why the version is read rather than assumed.\n */\nfunction readAudioSampleDescription(reader: Reader, stsd: BoxHeader, table: SampleTable): void {\n readFullBoxVersion(reader);\n const entries = reader.u32();\n if (entries === 0) {\n return;\n }\n const entry = readBoxHeader(reader, stsd.end);\n if (entry === null) {\n return;\n }\n reader.skip(6); // reserved\n reader.skip(2); // data_reference_index\n const version = reader.u16();\n reader.skip(6); // revision level and vendor, both reserved here\n table.channels = reader.u16();\n table.sampleSize = reader.u16();\n reader.skip(2); // pre_defined (compression id)\n reader.skip(2); // reserved (packet size)\n // 16.16 fixed point; the fraction is always zero in practice.\n table.sampleRate = reader.u16();\n reader.skip(2);\n if (version === 1) {\n // samples_per_packet, bytes_per_packet, bytes_per_frame, bytes_per_sample.\n reader.skip(16);\n } else if (version === 2) {\n // A wholly different layout whose length is written into it.\n reader.skip(36);\n }\n const format = entry.type;\n table.codec = format;\n walkBoxes(reader, reader.offset, entry.end, child => {\n if (child.type === 'esds') {\n readElementaryStreamDescriptor(reader, child, table, format);\n return;\n }\n if (child.type === 'dOps' || child.type === 'alac' || child.type === 'dfLa') {\n // Opus, ALAC and FLAC keep their configuration in a box of their\n // own rather than in an ES descriptor, and `AudioDecoder` takes\n // it as the description unchanged.\n table.description = reader.bytes(child.end - child.start);\n }\n });\n}\n\n/**\n * The `esds` box, for the two numbers a codec string needs.\n *\n * An ES descriptor is a little tag-length-value tree, and this walks\n * exactly as far into it as `mp4a.40.2` requires: the\n * `DecoderConfigDescriptor` for the object type indication (`40` for\n * MPEG-4 audio), and the `DecoderSpecificInfo` inside it, whose first\n * five bits are the audio object type (`2` for AAC-LC). That inner\n * payload is also the `AudioSpecificConfig` that `AudioDecoder` wants\n * as its description, so it is kept whole.\n *\n * Lengths are written in a variable-length form where the top bit of\n * each byte says another follows — the same encoding MIDI uses for\n * delta times and MP4 inherited from MPEG-4 systems.\n */\nfunction readElementaryStreamDescriptor(reader: Reader, esds: BoxHeader, table: SampleTable, format: string): void {\n readFullBoxVersion(reader);\n const end = esds.end;\n\n const readLength = (): number => {\n let length = 0;\n for (let index = 0; index < 4; index++) {\n const byte = reader.u8();\n length = (length << 7) | (byte & 0x7f);\n if ((byte & 0x80) === 0) {\n break;\n }\n }\n return length;\n };\n\n while (reader.offset < end) {\n const tag = reader.u8();\n const length = readLength();\n const payloadEnd = reader.offset + length;\n if (payloadEnd > end) {\n return;\n }\n if (tag === 0x03) {\n // ES_Descriptor: an id, then flags that decide what follows.\n reader.skip(2);\n const flags = reader.u8();\n if ((flags & 0x80) !== 0) {\n reader.skip(2); // dependsOn_ES_ID\n }\n if ((flags & 0x40) !== 0) {\n reader.skip(reader.u8()); // a URL, length-prefixed\n }\n if ((flags & 0x20) !== 0) {\n reader.skip(2); // OCR_ES_Id\n }\n // Its children follow in place, so do not skip to payloadEnd.\n continue;\n }\n if (tag === 0x04) {\n const objectType = reader.u8();\n reader.skip(12); // stream type, buffer size, max and average bitrate\n table.codec = `${format}.${objectType.toString(16)}`;\n continue;\n }\n if (tag === 0x05) {\n const config = reader.bytes(length);\n table.description = config;\n const first = config[0];\n if (first !== undefined) {\n const audioObjectType = first >> 3;\n // Only the five-bit form: an object type of 31 means the real\n // one is six more bits further in, and nothing that writes one\n // of those is playing in a browser.\n if (audioObjectType !== 0 && audioObjectType !== 31) {\n table.codec = `${table.codec}.${audioObjectType}`;\n }\n }\n return;\n }\n reader.offset = payloadEnd;\n }\n}\n\n/**\n * Reads the first audio track out of an MP4.\n *\n * Null rather than a throw for a file that simply has no audio, which\n * is the ordinary case for the clips this framework plays and is not\n * an error in any sense. A file that *has* an audio track this cannot\n * read still throws, for the reason `demuxMp4Video` gives: \"no audio\n * track\" and \"an audio track I do not understand\" want different\n * fixes.\n */\nexport function demuxMp4Audio(data: ArrayBuffer): Mp4AudioTrack | null {\n const { reader, moov, moofs } = openMp4(data);\n if (moov === null) {\n return null;\n }\n const table = findTrack(reader, moov, 'soun');\n if (table === null) {\n return null;\n }\n const samples = samplesOf(reader, table, moov, moofs);\n if (samples.length === 0) {\n throw new Error('This MP4 has an audio track with no samples this demuxer can read.');\n }\n let durationUs = 0;\n for (const sample of samples) {\n durationUs = Math.max(durationUs, sample.timestampUs + sample.durationUs);\n }\n return {\n codec: table.codec,\n sampleRate: table.sampleRate,\n channels: table.channels,\n sampleSize: table.sampleSize,\n description: table.description,\n samples,\n durationUs\n };\n}\n\n/**\n * `av01.0.04M.08`, out of the `av1C` record.\n *\n * The bare format is not a codec string for AV1 — `VideoDecoder`\n * answers `supported: false` for `av01` alone — so a file whose codec\n * was reported as just the format never played at all. Every field\n * below is in the record's first three bytes, which is the whole of\n * the fixed part of an `AV1CodecConfigurationRecord`:\n *\n * byte 0: a marker bit and a version\n * byte 1: seq_profile (3 bits), seq_level_idx_0 (5 bits)\n * byte 2: seq_tier_0, high_bitdepth, twelve_bit, monochrome,\n * chroma subsampling and sample position\n *\n * The string wants the profile, the level as two decimal digits, the\n * tier as `M` or `H`, and the bit depth as two decimal digits. Falls\n * back to the format for a record too short to read, where claiming a\n * precise string would be worse than claiming none.\n */\nfunction av1CodecString(format: string, record: Uint8Array): string {\n const second = record[1];\n const third = record[2];\n if (second === undefined || third === undefined) {\n return format;\n }\n const profile = second >> 5;\n const level = second & 0x1f;\n const tier = (third & 0x80) !== 0 ? 'H' : 'M';\n const highBitDepth = (third & 0x40) !== 0;\n const twelveBit = (third & 0x20) !== 0;\n // Profile 2 is the only one where `high_bitdepth` can mean twelve;\n // everywhere else it means ten.\n const depth = twelveBit && profile === 2 ? 12 : highBitDepth ? 10 : 8;\n return `${format}.${profile}.${pad2(level)}${tier}.${pad2(depth)}`;\n}\n\n/**\n * `vp09.00.10.08`, out of the `vpcC` record.\n *\n * Here for the same reason the AV1 one is, and simpler: `vpcC` is a\n * full box, so a version and three flag bytes come first, and then the\n * profile, the level and a byte whose top four bits are the bit depth.\n */\nfunction vp9CodecString(format: string, record: Uint8Array): string {\n const profile = record[4];\n const level = record[5];\n const packed = record[6];\n if (profile === undefined || level === undefined || packed === undefined) {\n return format;\n }\n return `${format}.${pad2(profile)}.${pad2(level)}.${pad2(packed >> 4)}`;\n}\n\nfunction pad2(value: number): string {\n return value.toString(10).padStart(2, '0');\n}\n\nfunction hex2(value: number): string {\n return value.toString(16).padStart(2, '0').toUpperCase();\n}\n\n/**\n * The `trex` defaults, one per track, out of `moov`/`mvex`.\n *\n * `mvex`'s presence is also the honest test for a fragmented file. The\n * `moof` boxes are the fragments, but a file may legitimately have its\n * first fragment far enough in that a parser has not reached one yet,\n * whereas `mvex` is in the `moov` and says up front that fragments are\n * how this file is built.\n */\nfunction readTrackExtends(reader: Reader, moov: BoxHeader): Map<number, TrackExtends> {\n const defaults = new Map<number, TrackExtends>();\n walkBoxes(reader, moov.start, moov.end, box => {\n if (box.type !== 'mvex') {\n return;\n }\n walkBoxes(reader, box.start, box.end, child => {\n if (child.type !== 'trex') {\n return;\n }\n readFullBoxVersion(reader);\n const trackId = reader.u32();\n reader.skip(4); // default_sample_description_index\n defaults.set(trackId, {\n trackId,\n defaultSampleDuration: reader.u32(),\n defaultSampleSize: reader.u32(),\n defaultSampleFlags: reader.u32()\n });\n });\n });\n return defaults;\n}\n\n/**\n * Builds a track's samples out of the file's fragments.\n *\n * A fragmented MP4 says nothing up front about where its samples are:\n * there is no `stco`, no `stsz` and no `stts`, and the `stbl` in the\n * `moov` is a shell holding only the `stsd`. Instead each `moof`\n * carries a `traf` per track, and each `traf` a `trun` per run of\n * samples, describing sizes and durations inline — with anything\n * omitted falling back to a default from `tfhd`, and failing that from\n * `trex`. So this is a walk rather than a table lookup, and it is why\n * the two shapes could not share `buildSamples`.\n *\n * **Offsets are the part that goes wrong.** A sample's position is\n * `base_data_offset` plus the `trun`'s own `data_offset` plus the\n * sizes of the samples before it in that run, and `base_data_offset`\n * is one of three things: written into the `tfhd`, or the start of\n * the enclosing `moof` when `default-base-is-moof` is set, or — for\n * the first `traf` — the `moof` again by the specification's older\n * rule. The second of those is what every fragmenter writes today and\n * what makes a fragment self-contained; taking the first `traf` rule\n * as the general one puts every fragment after the first at the wrong\n * offset, which decodes as noise rather than as an error.\n *\n * **Times accumulate across fragments.** `tfdt` states the decode time\n * a fragment starts at, and where it is absent the fragment continues\n * from wherever the last one ended.\n */\nfunction buildFragmentedSamples(\n reader: Reader,\n moofs: readonly BoxHeader[],\n table: SampleTable,\n defaults: Map<number, TrackExtends>\n): Mp4Sample[] {\n const samples: Mp4Sample[] = [];\n const trex = defaults.get(table.trackId);\n let decodeTime = 0;\n\n for (const moof of moofs) {\n // `moof.start` is the payload; the box itself begins eight bytes\n // earlier, and that is what the offsets are relative to.\n const moofStart = moof.start - 8;\n walkBoxes(reader, moof.start, moof.end, traf => {\n if (traf.type !== 'traf') {\n return;\n }\n let trackId = 0;\n let baseDataOffset = moofStart;\n let defaultDuration = trex?.defaultSampleDuration ?? 0;\n let defaultSize = trex?.defaultSampleSize ?? 0;\n let defaultFlags = trex?.defaultSampleFlags ?? 0;\n const runs: { header: BoxHeader; at: number }[] = [];\n let fragmentTime: number | null = null;\n\n walkBoxes(reader, traf.start, traf.end, child => {\n if (child.type === 'tfhd') {\n const { flags } = readFullBox(reader);\n trackId = reader.u32();\n if ((flags & 0x000001) !== 0) {\n baseDataOffset = reader.u64();\n }\n if ((flags & 0x000002) !== 0) {\n reader.skip(4); // sample_description_index\n }\n if ((flags & 0x000008) !== 0) {\n defaultDuration = reader.u32();\n }\n if ((flags & 0x000010) !== 0) {\n defaultSize = reader.u32();\n }\n if ((flags & 0x000020) !== 0) {\n defaultFlags = reader.u32();\n }\n return;\n }\n if (child.type === 'tfdt') {\n const version = readFullBoxVersion(reader);\n fragmentTime = version === 1 ? reader.u64() : reader.u32();\n return;\n }\n if (child.type === 'trun') {\n runs.push({ header: child, at: child.start });\n }\n });\n\n if (trackId !== table.trackId) {\n return;\n }\n if (fragmentTime !== null) {\n decodeTime = fragmentTime;\n }\n\n for (const run of runs) {\n reader.offset = run.at;\n const { version, flags } = readFullBox(reader);\n const count = reader.u32();\n let offset = baseDataOffset;\n if ((flags & 0x000001) !== 0) {\n offset += reader.i32();\n }\n const firstFlags = (flags & 0x000004) !== 0 ? reader.u32() : null;\n for (let index = 0; index < count; index++) {\n const duration = (flags & 0x000100) !== 0 ? reader.u32() : defaultDuration;\n const size = (flags & 0x000200) !== 0 ? reader.u32() : defaultSize;\n const sampleFlags =\n (flags & 0x000400) !== 0 ? reader.u32() : index === 0 && firstFlags !== null ? firstFlags : defaultFlags;\n // Signed from version 1 on, and written signed by every\n // fragmenter even in version 0 — the same reading `ctts`\n // takes, and for the same reason.\n const composition = (flags & 0x000800) !== 0 ? (version === 0 ? reader.i32() : reader.i32()) : 0;\n samples.push({\n offset,\n size,\n timestampUs: Math.round(((decodeTime + composition) * 1_000_000) / table.timescale),\n durationUs: Math.round((duration * 1_000_000) / table.timescale),\n // Bit 16 of the sample flags is `sample_is_non_sync_sample`,\n // so a keyframe is the one with it clear.\n isKey: (sampleFlags & 0x00010000) === 0\n });\n offset += size;\n decodeTime += duration;\n }\n }\n });\n }\n return samples;\n}\n\n/**\n * Walks the sample tables into a flat list.\n *\n * The one piece of real arithmetic in this file: `stsc` describes runs\n * of chunks that share a sample count, `stco` says where each chunk\n * begins, and a sample's offset is its chunk's offset plus the sizes\n * of the samples before it *in that chunk*. Everything else is\n * accumulating deltas.\n */\nfunction buildSamples(table: SampleTable): Mp4Sample[] {\n const { sizes, chunkOffsets, chunkRuns, deltas, compositionOffsets, syncSamples, timescale } = table;\n if (sizes.length === 0 || chunkOffsets.length === 0 || chunkRuns.length === 0) {\n return [];\n }\n const sync = syncSamples.length === 0 ? null : new Set(syncSamples);\n const samples: Mp4Sample[] = [];\n let sampleIndex = 0;\n let decodeTime = 0;\n\n for (const [runIndex, run] of chunkRuns.entries()) {\n const nextRun = chunkRuns[runIndex + 1];\n const lastChunk = nextRun === undefined ? chunkOffsets.length : nextRun.firstChunk - 1;\n for (let chunk = run.firstChunk; chunk <= lastChunk; chunk++) {\n let offset = chunkOffsets[chunk - 1];\n if (offset === undefined) {\n continue;\n }\n for (let n = 0; n < run.samplesPerChunk && sampleIndex < sizes.length; n++) {\n const size = sizes[sampleIndex]!;\n const delta = deltas[sampleIndex] ?? deltas[deltas.length - 1] ?? 0;\n const composition = compositionOffsets[sampleIndex] ?? 0;\n samples.push({\n offset,\n size,\n timestampUs: Math.round(((decodeTime + composition) * 1_000_000) / timescale),\n durationUs: Math.round((delta * 1_000_000) / timescale),\n // No `stss` at all means every sample is a sync sample, which\n // is what an all-intra track looks like.\n isKey: sync === null || sync.has(sampleIndex + 1)\n });\n offset += size;\n decodeTime += delta;\n sampleIndex++;\n }\n }\n }\n // Left in **decode** order, deliberately. A stream with B-frames\n // presents its frames out of the order they must be decoded in, and\n // `VideoDecoder` is fed in decode order and hands frames back in\n // presentation order — sorting here would submit a frame before the\n // one it references and the decoder would reject it.\n return samples;\n}\n","/**\n * Where a demuxed file's bytes come from.\n *\n * The seam that lets one `Mp4Playback` serve both of the things a\n * video file can be. A fifteen-second loop is a few megabytes, is\n * wanted in its entirety within a second of starting, and is simplest\n * held in one `ArrayBuffer` — which is what this framework did, and\n * still does by default. An hour of video is none of those things: it\n * is a gigabyte, a viewer will watch four minutes of it, and holding\n * it in memory to do so is not a trade-off but a mistake.\n *\n * Both are the same demux. A sample table names byte offsets into a\n * file, and whether those bytes are already in memory or have to be\n * asked for is a question about *transport*, not about the format. So\n * the playback reads through this, and the two answers to that\n * question are `bufferSource` and `rangeSource`.\n *\n * **`read` is synchronous and may answer null**, which is the whole\n * design. The decoder is fed from inside a presentation, on the frame\n * clock, and a `fill` that awaited anything would turn every frame\n * into a microtask and every stall into a dropped frame. So `read`\n * answers immediately or not at all, and a caller that gets null\n * calls `request` and gets on with the frame it is in.\n */\nexport interface ByteSource {\n /** The whole file's length in bytes. */\n readonly size: number;\n /**\n * The bytes at `offset`, if they are already here. Null means they\n * are not, and says nothing about whether they ever will be.\n */\n read(offset: number, size: number): Uint8Array | null;\n /**\n * Asks for a region, resolving once `read` will answer for it.\n *\n * Calling it for a region already in flight joins that request\n * rather than making a second one.\n */\n request(offset: number, size: number): Promise<void>;\n /** Releases whatever is held. */\n close(): void;\n}\n\n/** A file already in memory, which is every file this framework read before. */\nexport function bufferSource(data: ArrayBuffer): ByteSource {\n return {\n size: data.byteLength,\n read(offset: number, size: number): Uint8Array | null {\n if (offset < 0 || offset + size > data.byteLength) {\n return null;\n }\n return new Uint8Array(data, offset, size);\n },\n request(): Promise<void> {\n return Promise.resolve();\n },\n close(): void {}\n };\n}\n\n/**\n * How much is fetched at a time.\n *\n * Big enough that a request is worth its round trip — a quarter of a\n * megabyte is a second or two of ordinary video, so a clip playing\n * forwards asks for one every second or so rather than every frame —\n * and small enough that a seek does not drag a great deal of video\n * nobody will watch.\n *\n * It is also what a reader's first request should be, so that the\n * bytes it read to find the header land on a block boundary and can\n * be kept; see `prefetched`. A megabyte, the first value here, was\n * larger than the whole of a short file, so the header probe could\n * never seed a block and the front of the file was always fetched\n * twice.\n */\nexport const DEFAULT_BLOCK_SIZE = 256 * 1024;\n\n/**\n * How many blocks to keep.\n *\n * Eight megabytes, which is enough that playing forwards never evicts\n * a block it is about to want, and a bound rather than a cache that\n * grows to the size of the file — which would make the whole exercise\n * pointless.\n */\nconst BLOCK_CAPACITY = 32;\n\nexport interface RangeSourceOptions {\n /** Fetches `[start, end)`. Inclusive-exclusive, like every other range in this codebase. */\n readonly fetchRange: (source: string, start: number, end: number) => Promise<ArrayBuffer>;\n /** The file's length, which a caller usually learns from the same response that told it the head. */\n readonly size: number;\n readonly blockSize?: number;\n readonly capacity?: number;\n /**\n * Bytes the caller has already fetched, starting at `start`.\n *\n * A reader almost always has some: finding the `moov` means reading\n * the front of the file, and without this that read is thrown away\n * and the first block is fetched all over again. On a short file\n * that duplicate was most of the transfer.\n *\n * Only the *whole* blocks inside it are kept. A partial block at\n * either end is dropped rather than stored short, because a block\n * held short would answer `read` for bytes it does not have.\n */\n readonly prefetched?: { readonly start: number; readonly data: ArrayBuffer };\n}\n\n/**\n * A file fetched a block at a time.\n *\n * Blocks rather than exact ranges, and aligned to their own size, so\n * that two samples in the same part of the file share one request and\n * so the cache can be keyed on a number. A sample straddling a block\n * boundary is copied out of the two blocks either side of it; every\n * other read is a view, which matters because the common case is a\n * frame's worth of bytes on the way to a decoder.\n *\n * **Eviction is by last use, not by position.** A viewer who seeks\n * backwards into what they just watched should find it still here, and\n * a strictly forward-looking policy would have thrown it away.\n */\nexport function rangeSource(source: string, options: RangeSourceOptions): ByteSource {\n const blockSize = options.blockSize ?? DEFAULT_BLOCK_SIZE;\n const capacity = options.capacity ?? BLOCK_CAPACITY;\n const blocks = new Map<number, Uint8Array>();\n const inFlight = new Map<number, Promise<void>>();\n let closed = false;\n\n const blockOf = (offset: number): number => Math.floor(offset / blockSize);\n\n /** Slices whatever the caller already had into blocks this can serve. */\n const seed = (start: number, data: ArrayBuffer): void => {\n const bytes = new Uint8Array(data);\n const end = start + bytes.length;\n // The first block boundary at or after `start`, and the last one\n // at or before `end` -- or the end of the file, whose final block\n // is legitimately short.\n for (let index = Math.ceil(start / blockSize); ; index++) {\n const blockStart = index * blockSize;\n const blockEnd = Math.min(blockStart + blockSize, options.size);\n if (blockEnd > end) {\n break;\n }\n blocks.set(index, bytes.subarray(blockStart - start, blockEnd - start));\n if (blockEnd === options.size) {\n break;\n }\n }\n };\n\n /** Marks a block as the most recently used, which a Map's order gives us for free. */\n const touch = (index: number, bytes: Uint8Array): void => {\n blocks.delete(index);\n blocks.set(index, bytes);\n while (blocks.size > capacity) {\n const oldest = blocks.keys().next();\n if (oldest.done === true) {\n break;\n }\n blocks.delete(oldest.value);\n }\n };\n\n const fetchBlock = (index: number): Promise<void> => {\n const existing = inFlight.get(index);\n if (existing !== undefined) {\n return existing;\n }\n const start = index * blockSize;\n const end = Math.min(start + blockSize, options.size);\n const request = options\n .fetchRange(source, start, end)\n .then(buffer => {\n if (closed) {\n return;\n }\n touch(index, new Uint8Array(buffer));\n })\n .finally(() => inFlight.delete(index));\n inFlight.set(index, request);\n return request;\n };\n\n if (options.prefetched !== undefined) {\n seed(options.prefetched.start, options.prefetched.data);\n }\n\n return {\n size: options.size,\n read(offset: number, size: number): Uint8Array | null {\n if (offset < 0 || size < 0 || offset + size > options.size) {\n return null;\n }\n const first = blockOf(offset);\n const last = blockOf(offset + Math.max(0, size - 1));\n const head = blocks.get(first);\n if (head === undefined) {\n return null;\n }\n if (first === last) {\n touch(first, head);\n return head.subarray(offset - first * blockSize, offset - first * blockSize + size);\n }\n // Straddling a boundary: every block it touches has to be here,\n // and the result is a copy rather than a view.\n const out = new Uint8Array(size);\n let written = 0;\n for (let index = first; index <= last; index++) {\n const block = blocks.get(index);\n if (block === undefined) {\n return null;\n }\n const blockStart = index * blockSize;\n const from = Math.max(0, offset - blockStart);\n const take = Math.min(block.length - from, size - written);\n out.set(block.subarray(from, from + take), written);\n written += take;\n }\n for (let index = first; index <= last; index++) {\n const block = blocks.get(index);\n if (block !== undefined) {\n touch(index, block);\n }\n }\n return written === size ? out : null;\n },\n async request(offset: number, size: number): Promise<void> {\n if (closed || offset < 0 || offset >= options.size) {\n return;\n }\n const first = blockOf(offset);\n const last = blockOf(Math.min(offset + Math.max(0, size - 1), options.size - 1));\n const wanted: Promise<void>[] = [];\n for (let index = first; index <= last; index++) {\n if (!blocks.has(index)) {\n wanted.push(fetchBlock(index));\n }\n }\n await Promise.all(wanted);\n },\n close(): void {\n closed = true;\n blocks.clear();\n inFlight.clear();\n }\n };\n}\n","import type { UiVideoSurface } from '../properties/UiVideo';\nimport { bufferSource, DEFAULT_BLOCK_SIZE, rangeSource, type ByteSource } from './ByteSource';\nimport { demuxMp4Video, type Mp4Sample, type Mp4VideoTrack } from './Mp4Demuxer';\n\n/**\n * A playing video: one surface, and the controls over it.\n *\n * `surface` is the same object for the whole life of the playback, and\n * that is load-bearing — see `UiVideo.ts` for what the WebGPU texture\n * cache does with it.\n */\nexport interface VideoPlayback {\n readonly surface: UiVideoSurface;\n readonly width: number;\n readonly height: number;\n /** Seconds. Zero until the container has been read. */\n readonly duration: number;\n /**\n * How often the picture can change, in milliseconds.\n *\n * Playback is driven by whoever owns time, and this is what that\n * driver needs to pace itself: sampling this often never misses a\n * frame, and sampling faster only discovers that nothing changed.\n * Without it a playing video has to ask for *every* frame the clock\n * will give — which on a display-paced runtime means a 30fps clip\n * waking a 165Hz app 165 times a second to present the same picture\n * five times running.\n */\n readonly frameDurationMs: number;\n /**\n * How far into the video this playback has got, in milliseconds.\n *\n * What an arriving element needs in order to pick up where a\n * departing one left off. The reference counting keeps the decoder\n * alive across a navigation, but the *position* belongs to whoever\n * is driving, and a node that has just been built has no idea where\n * that is — so without this it starts at zero, which on a shared\n * playback is a seek backwards and a decoder reset.\n */\n readonly positionMs: number;\n /**\n * Shows the frame due at `positionMs` into the video, and says\n * whether that changed the picture.\n *\n * A pure function of a position rather than something that runs on a\n * clock of its own: whoever is driving owns time. In an app that is\n * the animation driver, through `videoSource`, which means a video\n * is scheduled and paced by exactly the machinery every other\n * animated thing here uses. Going backwards is a seek, and a seek to\n * zero is what looping is.\n */\n present(positionMs: number): boolean;\n /**\n * Whether the decoder is still working towards the position it was\n * last given.\n *\n * A seek lands on the sync sample *before* where it was aimed, and\n * everything between the two has to be decoded and thrown away\n * before the wanted picture exists. On a two-second GOP that is a\n * handful of frames and invisible; on a long one it is long enough\n * that an interface wanting to say so — a scrubber that dims, a\n * spinner over the poster — needs to be told. False while playing\n * forwards, because then every decoded frame is one that will be\n * shown.\n *\n * Optional, and absent means false. A playback that produces its\n * pictures on demand — the generated ones the documentation is built\n * on, a canvas, a stream of bitmaps — never waits for a keyframe it\n * has no concept of, and should not have to say so.\n */\n readonly seeking?: boolean;\n /** Called when the video fails, with what went wrong. */\n onError(listener: (error: unknown) => void): () => void;\n /**\n * Called when a decoded frame arrived and changed the picture\n * without anyone having asked for it.\n *\n * Which sounds like a contradiction of everything `present` says\n * about who owns time, and is not: the position is still driven from\n * outside, and this fires only for a position that has *already*\n * been asked for and could not be answered yet. A seek is the case.\n * `present(3.5s)` resets the decoder and returns with nothing to\n * show, because the frame for 3.5s will not exist for another few\n * milliseconds; when it lands, the driver is not going to call again\n * — a paused clip has no next frame — so the picture would stay on\n * whatever was there before the seek, forever.\n *\n * So the playback presents it and says so, and a caller repaints.\n * Optional for the same reason `seeking` is: a playback that\n * produces its pictures on demand answers every `present` on the\n * spot and has nothing to announce.\n */\n onFrame?(listener: () => void): () => void;\n}\n\n/**\n * Turns a source into something a renderer can draw, frame by frame.\n *\n * Deliberately the same shape as `ImageResolver`, including the\n * reference counting, and the reference counting is worth more here\n * than it is for a still. The demo this was written against keeps a\n * video playing across a navigation, and does it in the DOM by\n * physically moving the `<video>` element into the new document —\n * because a second `<video>` on the same source would start from the\n * beginning. Here the arriving node resolves the same source the\n * departing one is holding, gets the playback that is already running,\n * and picks it up mid-stream. There is no trick to reproduce.\n */\nexport interface VideoResolver {\n resolve(source: string): Promise<VideoPlayback>;\n release(source: string): void;\n dispose(): void;\n}\n\n/** Part of a file, and how long the whole of it is. */\nexport interface RangeResponse {\n readonly data: ArrayBuffer;\n /**\n * The file's total length in bytes.\n *\n * Asked for alongside the bytes because an HTTP range response\n * already carries it, in `Content-Range`, and a separate `HEAD`\n * request to learn the same thing is a round trip for nothing.\n */\n readonly total: number;\n}\n\nexport interface DefaultVideoResolverOptions {\n /** How many finished playbacks to keep after their last holder let go. */\n capacity?: number;\n /** Injectable for specs, and for an app that fetches through its own stack. */\n fetch?: (source: string) => Promise<ArrayBuffer>;\n /**\n * Fetches `[start, end)` of a file, for playing one without holding\n * all of it.\n *\n * Given this, the resolver finds the `moov` with a few small\n * requests and then reads media as the decoder asks for it, which is\n * what makes an hour of video possible; given only `fetch`, it reads\n * the whole file at once, which is right for the looping clips this\n * framework is usually asked for and is what it has always done.\n * Neither is a default the other should be silently upgraded to — a\n * ranged read of a two megabyte loop is three round trips where one\n * would do — so an application says which it wants.\n */\n fetchRange?: (source: string, start: number, end: number) => Promise<RangeResponse>;\n}\n\n/** Whether this thread can decode video at all. */\nexport function canDecodeVideo(): boolean {\n return typeof VideoDecoder !== 'undefined' && typeof EncodedVideoChunk !== 'undefined';\n}\n\nconst DEFAULT_CAPACITY = 4;\n/**\n * How many decoded frames to keep in flight, queued plus submitted.\n *\n * A `VideoFrame` holds decoded pixels — often in GPU memory — and a\n * decoder will happily run seconds ahead if you let it, so this is\n * deliberately small. But it cannot be *too* small, and the arithmetic\n * is worth writing down because the first value chosen was.\n *\n * The floor is three things multiplied out. A stream with B-frames\n * cannot emit its first picture until the decoder holds its reorder\n * depth — two for the clip this was built against, so three inputs\n * before one output. `fill` runs once per *presentation*, and a source\n * at 60 fps presented by a renderer running at 30 needs two frames\n * decoded per fill to keep up. And a frame or two of slack absorbs a\n * decode that arrives late without the queue ever reaching empty.\n *\n * Twelve covers that with room. Four — the first value here — did not:\n * it left barely more than the reorder depth, so delivery came in\n * bursts and `presentDue`, which shows only the newest frame that is\n * due, threw the rest away.\n */\nconst FRAME_QUEUE_LIMIT = 12;\n/**\n * How far the position may go backwards before it counts as a seek.\n *\n * A driver writing a position from a linear tween is monotonic within\n * a pass, but a frame's timing jitters by a millisecond either way and\n * a repeating tween wraps by a whole duration. A tenth of a second\n * tells those apart with room to spare.\n */\nconst SEEK_BACK_TOLERANCE_US = 100_000;\n\nasync function defaultFetch(source: string): Promise<ArrayBuffer> {\n const response = await fetch(source);\n if (!response.ok) {\n throw new Error(`Fetching '${source}' failed with ${response.status} ${response.statusText}.`);\n }\n return response.arrayBuffer();\n}\n\ninterface Entry {\n readonly source: string;\n readonly playback: Promise<VideoPlayback>;\n holders: number;\n settled: Mp4Playback | null;\n}\n\n/**\n * Fetch, demux, decode, present, loop.\n *\n * Everything happens on the thread this is constructed on, which for a\n * Gesso app is the render worker: `VideoDecoder` is available there,\n * the demuxer is arithmetic, and a decoded `VideoFrame` is drawn where\n * it was produced rather than transferred anywhere. That is the whole\n * reason the decode is not on the main thread behind a `<video>`\n * element — the shell stays a shell.\n */\nexport class DefaultVideoResolver implements VideoResolver {\n private readonly entries = new Map<string, Entry>();\n private readonly evictable: string[] = [];\n private readonly capacity: number;\n private readonly fetchBuffer: (source: string) => Promise<ArrayBuffer>;\n private readonly fetchRange: DefaultVideoResolverOptions['fetchRange'];\n private disposed = false;\n\n constructor(options: DefaultVideoResolverOptions = {}) {\n this.capacity = options.capacity ?? DEFAULT_CAPACITY;\n this.fetchBuffer = options.fetch ?? defaultFetch;\n this.fetchRange = options.fetchRange;\n }\n\n /** Live or in-flight sources, for specs and the inspector. */\n get size(): number {\n return this.entries.size;\n }\n\n resolve(source: string): Promise<VideoPlayback> {\n if (this.disposed) {\n return Promise.reject(new Error('The video resolver has been disposed.'));\n }\n const existing = this.entries.get(source);\n if (existing !== undefined) {\n existing.holders++;\n const index = this.evictable.indexOf(source);\n if (index !== -1) {\n this.evictable.splice(index, 1);\n }\n return existing.playback;\n }\n const entry: Entry = {\n source,\n holders: 1,\n settled: null,\n playback: this.open(source).then(playback => {\n const current = this.entries.get(source);\n if (current === undefined) {\n // Released and evicted while it was in flight.\n playback.close();\n return playback;\n }\n current.settled = playback;\n return playback;\n })\n };\n // A rejection must not become an unhandled one just because nobody\n // has awaited it yet; the caller's own `.catch` still sees it.\n entry.playback.catch(() => {});\n this.entries.set(source, entry);\n return entry.playback;\n }\n\n release(source: string): void {\n const entry = this.entries.get(source);\n if (entry === undefined) {\n return;\n }\n entry.holders--;\n if (entry.holders > 0) {\n return;\n }\n // Nobody is watching it, but keep it: the usual reason a source\n // loses its last holder is a navigation that is about to give it\n // another, and that is the whole trick that keeps a video running\n // across a route change. It costs nothing to hold — nothing is\n // driving its position, so no frame is decoded until someone asks\n // for one again.\n this.evictable.push(source);\n while (this.evictable.length > this.capacity) {\n const evicted = this.evictable.shift()!;\n const stale = this.entries.get(evicted);\n if (stale !== undefined && stale.holders === 0) {\n this.entries.delete(evicted);\n stale.settled?.close();\n }\n }\n }\n\n dispose(): void {\n this.disposed = true;\n for (const entry of this.entries.values()) {\n entry.settled?.close();\n }\n this.entries.clear();\n this.evictable.length = 0;\n }\n\n private async open(source: string): Promise<Mp4Playback> {\n if (!canDecodeVideo()) {\n throw new Error(\n 'This thread has no WebCodecs VideoDecoder, so video cannot be played here. ' +\n 'It is available in Chrome 94+ and in a worker; Safari and Firefox support varies.'\n );\n }\n const bytes = this.fetchRange === undefined ? await this.openWhole(source) : await this.openRanged(source);\n const playback = new Mp4Playback(bytes.track, bytes.data);\n await playback.start();\n return playback;\n }\n\n private async openWhole(source: string): Promise<{ track: Mp4VideoTrack; data: ByteSource }> {\n const data = await this.fetchBuffer(source);\n return { track: demuxMp4Video(data), data: bufferSource(data) };\n }\n\n /**\n * Finds the `moov`, reads it, and leaves the media where it is.\n *\n * The top-level structure of an MP4 is a flat list of boxes, each of\n * which states its own length, so the whole of it can be walked by\n * reading eight bytes at a time and jumping — which means finding\n * the `moov` costs a couple of small requests whether it is at the\n * front of the file (`-movflags +faststart`, and every file served\n * for streaming) or at the back (the default, and every file written\n * by a camera).\n *\n * The `moov` is then demuxed *on its own*, which works because the\n * offsets in a sample table are absolute positions in the file\n * rather than relative to anything: the tables are as correct read\n * out of a two hundred kilobyte buffer as out of a gigabyte one.\n */\n private async openRanged(source: string): Promise<{ track: Mp4VideoTrack; data: ByteSource }> {\n const fetchRange = this.fetchRange!;\n const first = await fetchRange(source, 0, HEADER_PROBE_BYTES);\n const total = first.total;\n if (!Number.isFinite(total) || total <= 0) {\n throw new Error(`Fetching a range of '${source}' did not report how long the file is.`);\n }\n const plain = (start: number, end: number): Promise<ArrayBuffer> =>\n fetchRange(source, start, end).then(response => response.data);\n const bytes = rangeSource(source, {\n fetchRange: (_source, start, end) => plain(start, end),\n size: total,\n // The probe is not thrown away. Without this the front of the\n // file is fetched twice -- once to find the `moov` and once\n // again as the first block -- which on a short file is most of\n // the transfer, and makes a ranged read cost more than simply\n // fetching the whole thing.\n prefetched: { start: 0, data: first.data }\n });\n const moov = await findTopLevelBox(bytes, 'moov', total);\n if (moov === null) {\n throw new Error(\n `'${source}' has no moov box, so there is no sample table to read. ` +\n 'A ranged read walks the top-level boxes; a file this cannot walk is not an MP4.'\n );\n }\n // Already here, for a faststart file whose `moov` fits in the\n // probe, which is most of them. Copied rather than viewed because\n // the block it sits in can be evicted.\n const held = bytes.read(moov.start, moov.end - moov.start);\n const header = held === null ? await plain(moov.start, moov.end) : held.slice().buffer;\n return { track: demuxMp4Video(header), data: bytes };\n }\n}\n\n/**\n * How much of the front of the file to ask for first.\n *\n * Exactly one block, and that is the whole point of the constant\n * rather than a number here: a probe that does not land on a block\n * boundary cannot be kept, so the front of the file would be fetched\n * once to find the header and again as the first block. It is also\n * enough to hold the `ftyp` and, in a faststart file, the whole `moov`\n * for a clip of ordinary length, so the common case is a single\n * request for the whole header.\n */\nconst HEADER_PROBE_BYTES = DEFAULT_BLOCK_SIZE;\n\n/**\n * Walks the top-level boxes looking for one, reading only their\n * headers.\n *\n * A box states its own length, so the walk is a jump per box and the\n * bytes it touches are sixteen at a time. `size === 1` means the real\n * length is a 64-bit number after the type, and `size === 0` means the\n * box runs to the end of the file — both of which `mdat` is written\n * with often enough to matter.\n */\nasync function findTopLevelBox(\n bytes: ByteSource,\n wanted: string,\n total: number\n): Promise<{ start: number; end: number } | null> {\n let at = 0;\n while (at + 8 <= total) {\n await bytes.request(at, 16);\n const header = bytes.read(at, Math.min(16, total - at));\n if (header === null || header.length < 8) {\n return null;\n }\n const view = new DataView(header.buffer, header.byteOffset, header.byteLength);\n let size = view.getUint32(0);\n let payload = at + 8;\n if (size === 1) {\n if (header.length < 16) {\n return null;\n }\n size = view.getUint32(8) * 0x1_0000_0000 + view.getUint32(12);\n payload = at + 16;\n } else if (size === 0) {\n size = total - at;\n }\n if (size < 8 || at + size > total) {\n return null;\n }\n const type = String.fromCharCode(header[4]!, header[5]!, header[6]!, header[7]!);\n if (type === wanted) {\n return { start: payload - 8, end: at + size };\n }\n at += size;\n }\n return null;\n}\n\n/**\n * One MP4, decoded and presented against the runtime's frame clock.\n *\n * The clock matters. Nothing here uses `setInterval` or the decoder's\n * own pacing: `present(now)` is called from the modifier once a frame,\n * and it advances to whichever decoded frame is due at that moment. So\n * a video runs at the speed of the app's frames, drops frames under\n * load rather than falling behind, and stops entirely when the runtime\n * stops — which is the same contract every other animated thing in\n * this framework has.\n */\nclass Mp4Playback implements VideoPlayback {\n readonly surface: MutableVideoSurface;\n private decoder: VideoDecoder;\n private config: VideoDecoderConfig | null = null;\n /** Decoded and waiting, in presentation order. */\n private readonly queue: VideoFrame[] = [];\n private nextSample = 0;\n /** Where the last `present` put us, in microseconds. */\n private positionUs = 0;\n private closed = false;\n private readonly errorListeners = new Set<(error: unknown) => void>();\n private readonly frameListeners = new Set<() => void>();\n /**\n * The sync samples, by presentation time, so a seek can find where\n * to start decoding with a binary search rather than a scan.\n *\n * Built once, because `samples` never changes and a scrub drags\n * across it many times a second. Sorted by `timestampUs` rather than\n * left in decode order: a keyframe carries a composition offset like\n * any other sample, so the order they are *shown* in is the order a\n * seek searches, and it is not quite the order they are decoded in.\n */\n private readonly syncPoints: readonly SyncPoint[];\n /**\n * Where a seek is aiming, in microseconds, or null when playing\n * forwards.\n *\n * Frames that arrive before this are decoded only because the\n * pictures after them refer to them, and are closed on arrival\n * instead of being queued — see `receive`. Cleared as soon as a\n * frame at or past it has been decoded.\n */\n private seekTargetUs: number | null = null;\n /** Whether a fetch for missing bytes is already out; see `await`. */\n private pendingBytes = false;\n\n constructor(\n private readonly track: Mp4VideoTrack,\n private readonly data: ByteSource\n ) {\n // The surface tells us when a picture actually reaches it, which\n // is not the same moment as presenting one: converting a decoded\n // frame to a bitmap is asynchronous, so `present` can return\n // having chosen a frame that is not drawable yet. A playing clip\n // never notices, because the next tick repaints anyway. A paused\n // one has no next tick, so without this its first frame is chosen,\n // converted, and never drawn.\n this.surface = new MutableVideoSurface(track.codedWidth, track.codedHeight, () => this.announcePicture());\n this.syncPoints = buildSyncPoints(track.samples);\n this.decoder = new VideoDecoder({\n output: frame => this.receive(frame),\n error: error => this.fail(error)\n });\n }\n\n get width(): number {\n return this.track.codedWidth;\n }\n\n get height(): number {\n return this.track.codedHeight;\n }\n\n get duration(): number {\n return this.track.durationUs / 1_000_000;\n }\n\n get frameDurationMs(): number {\n return this.track.frameDurationUs / 1000;\n }\n\n get positionMs(): number {\n return this.positionUs / 1000;\n }\n\n get seeking(): boolean {\n return this.seekTargetUs !== null;\n }\n\n async start(): Promise<void> {\n const config: VideoDecoderConfig = {\n codec: this.track.codec,\n codedWidth: this.track.codedWidth,\n codedHeight: this.track.codedHeight,\n description: this.track.description,\n // `hardwareAcceleration` is deliberately not set, which means\n // 'no-preference' and lets the browser choose. Asking for\n // 'prefer-hardware' is a *requirement*, not a preference:\n // `isConfigSupported` answers false wherever there is no hardware\n // decoder for the codec — which on a Linux box without VA-API is\n // every codec — and the video then silently never plays. Found\n // exactly that way.\n optimizeForLatency: true\n };\n const support = await VideoDecoder.isConfigSupported(config);\n if (support.supported !== true) {\n throw new Error(\n `No decoder for '${this.track.codec}' at ${this.track.codedWidth}x${this.track.codedHeight} on this platform.`\n );\n }\n this.config = config;\n this.decoder.configure(config);\n this.fill();\n }\n\n /**\n * Shows whichever frame is due at `positionMs`, and says whether\n * that changed the picture.\n *\n * Frames already behind the position are dropped rather than shown,\n * so an app that stalled catches up instead of replaying the last\n * half second in fast-forward.\n */\n present(positionMs: number): boolean {\n if (this.closed) {\n return false;\n }\n const wanted = Math.max(0, positionMs * 1000);\n if (wanted === this.positionUs) {\n // Two elements can share one playback — a card and the page it\n // opens into, both on screen while a transition runs — and each\n // presents on its own tick. The second one to arrive on a given\n // position has nothing to do, and doing it anyway means a second\n // `fill()` and a second walk of the frame queue per frame.\n return false;\n }\n if (this.needsSeek(wanted)) {\n this.seekTo(wanted);\n }\n this.positionUs = wanted;\n const changed = this.presentDue();\n this.fill();\n return changed;\n }\n\n onError(listener: (error: unknown) => void): () => void {\n this.errorListeners.add(listener);\n return () => this.errorListeners.delete(listener);\n }\n\n onFrame(listener: () => void): () => void {\n this.frameListeners.add(listener);\n return () => this.frameListeners.delete(listener);\n }\n\n /** Frees the decoder and every frame still queued. */\n close(): void {\n if (this.closed) {\n return;\n }\n this.closed = true;\n for (const frame of this.queue) {\n frame.close();\n }\n this.queue.length = 0;\n this.surface.clear();\n if (this.decoder.state !== 'closed') {\n this.decoder.close();\n }\n this.errorListeners.clear();\n this.frameListeners.clear();\n this.data.close();\n }\n\n private presentDue(): boolean {\n let shown = false;\n while (this.queue.length > 0) {\n const frame = this.queue[0]!;\n if (frame.timestamp > this.positionUs) {\n break;\n }\n this.queue.shift();\n // Everything still queued behind a frame that is also due is a\n // dropped frame: only the last one is worth uploading.\n const next = this.queue[0];\n if (next !== undefined && next.timestamp <= this.positionUs) {\n frame.close();\n continue;\n }\n this.surface.show(frame);\n shown = true;\n }\n return shown;\n }\n\n /**\n * Keeps the decoder a few frames ahead, and no further.\n *\n * **Stops at the first sample whose bytes are not here yet**, which\n * is the whole of what a ranged source costs this loop. `read`\n * answers synchronously or not at all — see `ByteSource` for why it\n * must — so a sample that is still on the wire ends the fill, a\n * request goes out for the region around it, and that request\n * calls `fill` again when it lands. In the ordinary case, a file\n * already in memory, `read` never answers null and none of this\n * runs.\n */\n private fill(): void {\n if (this.closed || this.decoder.state !== 'configured') {\n return;\n }\n while (\n this.nextSample < this.track.samples.length &&\n this.queue.length + this.decoder.decodeQueueSize < FRAME_QUEUE_LIMIT\n ) {\n const sample = this.track.samples[this.nextSample]!;\n const bytes = this.data.read(sample.offset, sample.size);\n if (bytes === null) {\n this.await(sample);\n return;\n }\n this.nextSample++;\n try {\n this.decoder.decode(new EncodedVideoChunk(chunkOf(sample, bytes)));\n } catch (error) {\n this.fail(error);\n return;\n }\n }\n }\n\n /**\n * Asks for the bytes around a sample, and resumes when they arrive.\n *\n * The window is deliberately wider than the one sample: a request\n * per frame would be a request every sixteen milliseconds, and the\n * point of reading a file in pieces is to read it in *few* pieces.\n * So it asks for everything from this sample to the end of the\n * decoder's lookahead, which the block size below it will round up\n * to something worth a round trip anyway.\n */\n private await(sample: Mp4Sample): void {\n if (this.pendingBytes) {\n return;\n }\n const last = this.track.samples[Math.min(this.nextSample + FRAME_QUEUE_LIMIT, this.track.samples.length - 1)];\n const through = last === undefined ? sample.offset + sample.size : last.offset + last.size;\n this.pendingBytes = true;\n this.data\n .request(sample.offset, Math.max(sample.size, through - sample.offset))\n .then(() => {\n this.pendingBytes = false;\n if (this.closed) {\n return;\n }\n this.fill();\n // The position has not moved, but a frame that was missing\n // may now exist — a clip that stalled mid-seek, most of all.\n this.announceFrame();\n })\n .catch((error: unknown) => {\n this.pendingBytes = false;\n this.fail(error);\n });\n }\n\n /**\n * Whether reaching `wanted` means starting the decoder somewhere\n * else.\n *\n * Two cases, and they are not symmetric. **Backwards** past the\n * tolerance is always a seek: a decoder cannot run in reverse, so\n * every frame in flight belongs to where we no longer are. It is\n * also what a loop is, which is why looping costs a keyframe.\n *\n * **Forwards** is a seek only when the jump clears the samples\n * already submitted. During ordinary playback the decoder runs a few\n * frames ahead of the picture, so the keyframe covering the position\n * is always one it has long since passed — `sample <= nextSample`,\n * and nothing happens. A jump far enough ahead to land beyond the\n * decoder's own head is the only forward case worth a reset, and\n * without this test it would instead decode every frame in between\n * at playback speed, which is a scrub that crawls.\n */\n private needsSeek(wanted: number): boolean {\n if (wanted + SEEK_BACK_TOLERANCE_US < this.positionUs) {\n return true;\n }\n if (wanted <= this.positionUs) {\n return false;\n }\n return syncSampleFor(this.syncPoints, wanted) > this.nextSample;\n }\n\n /**\n * Starts the decoder again at the sync sample covering `wanted`.\n *\n * The decoder is reset and reconfigured rather than merely refilled,\n * because a decoder mid-GOP holds reference frames for where it was\n * and the sample it is about to be given is a keyframe for where it\n * is going.\n *\n * What lands is the keyframe *at or before* the wanted position,\n * never after: a seek that jumped forward to the next keyframe would\n * show a picture from later than the moment asked for, and a scrubber\n * that overshoots its own thumb is worse than one that is coarse.\n * The frames between the keyframe and the target are decoded — the\n * ones after it refer to them — and closed on arrival rather than\n * queued, which is what `seekTargetUs` is for. So the cost of a seek\n * is the GOP length, paid in decodes nobody sees, and the reason a\n * clip encoded with two-second keyframes scrubs well and one encoded\n * with ten-second keyframes does not.\n */\n private seekTo(wanted: number): void {\n for (const frame of this.queue) {\n frame.close();\n }\n this.queue.length = 0;\n this.nextSample = syncSampleFor(this.syncPoints, wanted);\n // Nothing to throw away when the seek lands on its own keyframe,\n // which is what a loop back to the start always does.\n this.seekTargetUs = wanted > 0 ? wanted : null;\n if (this.config !== null && this.decoder.state !== 'closed') {\n this.decoder.reset();\n this.decoder.configure(this.config);\n }\n }\n\n private receive(frame: VideoFrame): void {\n if (this.closed) {\n frame.close();\n return;\n }\n const target = this.seekTargetUs;\n if (target !== null) {\n if (frame.timestamp + this.track.frameDurationUs < target) {\n // Decoded only because the pictures after it refer to it.\n // Closing it here rather than queueing it is what keeps the\n // queue free for `fill` to keep submitting, so the gap between\n // the keyframe and the target is crossed as fast as the\n // decoder will go instead of one frame per presentation.\n frame.close();\n this.fill();\n return;\n }\n this.enqueue(frame);\n // Held, not shown. The frame *before* the target is kept because\n // it may well be the one the target position wants — a seek to\n // 3.45s is answered by the frame at 3.4s — but showing it the\n // moment it arrives would put the wrong picture up for a\n // millisecond and then correct it. So nothing is presented until\n // a frame at or past the target lands, or until the file runs\n // out, and `presentDue` then picks whichever of them is right.\n const arrived = frame.timestamp >= target || this.nextSample >= this.track.samples.length;\n if (!arrived) {\n this.fill();\n return;\n }\n this.seekTargetUs = null;\n this.announceFrame();\n return;\n }\n this.enqueue(frame);\n // Due already, which means the position asked for it before it\n // existed. During ordinary playback every frame here is ahead of\n // the position and this does nothing.\n this.announceFrame();\n }\n\n /** Queued in presentation order; see `presentDue` for why that matters. */\n private enqueue(frame: VideoFrame): void {\n this.queue.push(frame);\n // The decoder emits in presentation order within a run, but a\n // frame may arrive after the position has already moved past it;\n // keeping the queue sorted means `presentDue` can stop at the\n // first frame that is not due yet.\n this.queue.sort((a, b) => a.timestamp - b.timestamp);\n }\n\n /**\n * Presents whatever is due, and lets the surface say if that put a\n * picture up: see the constructor for why the two are not the same\n * moment.\n */\n private announceFrame(): void {\n this.presentDue();\n }\n\n private announcePicture(): void {\n for (const listener of this.frameListeners) {\n listener();\n }\n }\n\n private fail(error: unknown): void {\n for (const listener of this.errorListeners) {\n listener(error);\n }\n this.close();\n }\n}\n\n/** A sync sample: when it is shown, and where in the decode order it sits. */\ninterface SyncPoint {\n readonly timestampUs: number;\n /** Its index in the track's decode-ordered samples, which is where `fill` resumes. */\n readonly sample: number;\n}\n\n/**\n * The track's sync samples, in the order they are shown.\n *\n * `samples` is in decode order and stays that way — feeding a decoder\n * anything else is how you submit a frame before the one it refers to\n * — so the search a seek does needs its own view. Only the sync\n * samples are in it, which for a two-second GOP is one entry per two\n * seconds: a fifteen-minute film indexes in a few hundred numbers.\n */\nfunction buildSyncPoints(samples: readonly Mp4Sample[]): readonly SyncPoint[] {\n const points: SyncPoint[] = [];\n for (const [sample, entry] of samples.entries()) {\n if (entry.isKey) {\n points.push({ timestampUs: entry.timestampUs, sample });\n }\n }\n points.sort((a, b) => a.timestampUs - b.timestampUs);\n return points;\n}\n\n/**\n * Which sample to start decoding at to have a picture for `wanted`.\n *\n * The last sync sample at or before the position, by binary search.\n * Zero when the position is before the first one, or when the track\n * declared no sync samples at all — an all-intra track, where every\n * sample is one and `buildSyncPoints` returned all of them anyway.\n */\nfunction syncSampleFor(points: readonly SyncPoint[], wantedUs: number): number {\n let low = 0;\n let high = points.length - 1;\n let found = 0;\n while (low <= high) {\n const middle = (low + high) >> 1;\n const point = points[middle]!;\n if (point.timestampUs <= wantedUs) {\n found = point.sample;\n low = middle + 1;\n } else {\n high = middle - 1;\n }\n }\n return found;\n}\n\nfunction chunkOf(sample: Mp4Sample, bytes: Uint8Array): EncodedVideoChunkInit {\n return {\n type: sample.isKey ? 'key' : 'delta',\n timestamp: sample.timestampUs,\n duration: sample.durationUs,\n data: bytes\n };\n}\n\n/**\n * The surface the renderers see, written from inside.\n *\n * `UiVideoSurface` is readonly to everything downstream, which is the\n * point: a renderer must not be able to change what it is drawing. The\n * playback holds this side of it.\n */\nclass MutableVideoSurface implements UiVideoSurface {\n frame: VideoFrame | ImageBitmap | null = null;\n version = 0;\n\n constructor(\n readonly width: number,\n readonly height: number,\n /** Called once a picture is actually on the surface and drawable. */\n private readonly onPicture: () => void = () => {}\n ) {}\n\n /**\n * Which conversion is the newest, so a slower one that finishes\n * after it can be thrown away rather than shown out of order.\n */\n private pending = 0;\n\n /**\n * Takes a decoded frame, and shows it once it is something cheap to\n * draw.\n *\n * A `VideoFrame` from a software decoder holds planar YUV in CPU\n * memory, and `drawImage` has to convert it to RGB before it can\n * put it anywhere. Whether that is visible depends on the engine:\n * Chromium uploads the frame and converts it in a shader, while\n * Gecko converts on whichever thread issued the draw, which is the\n * render worker, inside the frame. Measured on Firefox 154 in the\n * transitions example, one 1280x992 frame drawn into a 0.42\n * megapixel box cost **4.6ms**, against a whole-frame budget of\n * 6ms on a 165Hz display. It was two thirds of the frame, and it\n * was paid again on every draw.\n *\n * `createImageBitmap` is specified to do its work in parallel, so\n * converting here rather than at the draw takes that cost off the\n * render thread and pays it once per decoded frame instead of once\n * per drawn frame. The same measurement then reads **1.7ms**, and\n * a display faster than the video no longer multiplies it.\n *\n * The frame is closed as soon as the bitmap exists, and the bitmap\n * is what `UiVideoSurface.frame` was already allowed to be.\n */\n show(frame: VideoFrame): void {\n const conversion = ++this.pending;\n if (typeof createImageBitmap !== 'function') {\n this.replace(frame);\n return;\n }\n createImageBitmap(frame)\n .then(bitmap => {\n frame.close();\n if (conversion !== this.pending) {\n // A newer frame has already been shown. Presenting this one\n // would run the video backwards for a frame.\n bitmap.close();\n return;\n }\n this.replace(bitmap);\n })\n .catch(() => {\n // No `createImageBitmap` here, or it refused this frame. The\n // frame itself is still drawable, which is what every renderer\n // did before this conversion existed.\n if (conversion !== this.pending) {\n frame.close();\n return;\n }\n this.replace(frame);\n });\n }\n\n private replace(frame: VideoFrame | ImageBitmap): void {\n // What is being replaced is closed here rather than left to the\n // collector: a `VideoFrame` holds decoded pixels, often in GPU\n // memory, and Chrome warns loudly when one is collected unclosed.\n (this.frame as { close?: () => void } | null)?.close?.();\n this.frame = frame;\n this.version++;\n this.onPicture();\n }\n\n clear(): void {\n // Also abandons any conversion still in flight: it will find its\n // sequence number stale and close what it made.\n this.pending++;\n (this.frame as { close?: () => void } | null)?.close?.();\n this.frame = null;\n this.version++;\n }\n}\n","/**\n * Just enough WebVTT to put words under a picture.\n *\n * Here for the reason `Mp4Demuxer` is: the alternative is a dependency\n * that brings a renderer, a positioning model and a CSS cascade with\n * it, and `gesso-core` has one dependency. What a caption track is,\n * stripped of the parts that only mean something in a document, is a\n * sorted list of (start, end, text) — and that is a parse of about two\n * hundred lines rather than a library.\n *\n * **What it reads.** The `WEBVTT` header and anything on that line, an\n * optional cue identifier, a timing line with `-->` and the two\n * timestamps around it, cue settings after the timings, and the\n * payload lines until a blank one. `NOTE` blocks and `STYLE` blocks\n * are skipped. Timestamps in both the `mm:ss.mmm` and `hh:mm:ss.mmm`\n * spellings, because both are written.\n *\n * **What it does not.** Cue settings are parsed and kept but nothing\n * here acts on them: `line`, `position`, `align`, `size` and `vertical`\n * describe a placement against a video box, and placement is the\n * caller's. They are carried rather than dropped so that a caller who\n * wants them is not made to parse the file again. Inline markup —\n * `<i>`, `<b>`, `<v Speaker>`, `<00:01.000>` — is **stripped to its\n * text**, which is a decision rather than an omission: this framework\n * draws a string, a voice span would have to become a style run, and a\n * caption that showed its own angle brackets would be worse than one\n * that lost its italics. `REGION` blocks are skipped for the same\n * reason the settings are inert.\n *\n * **Errors are not thrown.** A caption file is content, usually\n * someone else's, and a single malformed cue in a hundred is not a\n * reason to show none of them. A block that cannot be read is skipped\n * and counted; `VttTrack.skipped` is how a caller finds out. That is\n * the opposite of the demuxer's rule, and deliberately so: a video\n * that will not play is a failure worth reporting, and a caption that\n * is missing a line is not.\n */\n\n/** One caption: when it is on screen, and what it says. */\nexport interface VttCue {\n /** The cue's identifier, when it had one. Not required to be unique. */\n readonly id?: string;\n /** Seconds. */\n readonly start: number;\n readonly end: number;\n /** The payload with its markup stripped; newlines are kept as newlines. */\n readonly text: string;\n /**\n * The cue settings, unparsed beyond being split into pairs — `line`,\n * `position`, `align`, `size`, `vertical`. Empty for most cues.\n */\n readonly settings: Readonly<Record<string, string>>;\n}\n\nexport interface VttTrack {\n /** Cues in the order they are shown, which is not always the order they were written. */\n readonly cues: readonly VttCue[];\n /** Anything after `WEBVTT` on the first line; encoders put a title or a tool name there. */\n readonly header: string;\n /** How many blocks could not be read. Zero for a well-formed file. */\n readonly skipped: number;\n}\n\nconst EMPTY_SETTINGS: Readonly<Record<string, string>> = Object.freeze({});\n\n/**\n * Reads a `.vtt` file.\n *\n * Returns an empty track rather than throwing for a file that is not\n * WebVTT at all, for the reason the docblock gives: a caption track is\n * an enhancement, and an application that has to guard every one of\n * these with a `try` will end up not showing captions at all.\n */\nexport function parseWebVtt(source: string): VttTrack {\n // A byte order mark survives a `fetch().text()` and would otherwise\n // sit in front of the magic word and fail the check below.\n const text = source.replace(/^/, '');\n const lines = text.split(/\\r\\n|\\r|\\n/);\n const first = lines[0] ?? '';\n if (!first.startsWith('WEBVTT')) {\n return { cues: [], header: '', skipped: 0 };\n }\n const header = first.slice('WEBVTT'.length).trim();\n\n const cues: VttCue[] = [];\n let skipped = 0;\n let at = 1;\n\n while (at < lines.length) {\n // Blank lines between blocks, however many.\n while (at < lines.length && (lines[at] ?? '').trim() === '') {\n at++;\n }\n if (at >= lines.length) {\n break;\n }\n const blockStart = at;\n const block: string[] = [];\n while (at < lines.length && (lines[at] ?? '').trim() !== '') {\n block.push(lines[at]!);\n at++;\n }\n const cue = readBlock(block);\n if (cue === null) {\n // `NOTE`, `STYLE` and `REGION` are not failures — they are\n // blocks this deliberately ignores — so they must not be counted\n // against a file's health.\n if (!isIgnorableBlock(block)) {\n skipped++;\n }\n } else {\n cues.push(cue);\n }\n if (at === blockStart) {\n // A block that consumed nothing would spin here forever.\n at++;\n }\n }\n\n // WebVTT does not require cues to be written in order, and a file\n // that interleaves them is legal. Sorting once here means every\n // lookup afterwards can assume order — see `cueAt`.\n cues.sort((a, b) => a.start - b.start || a.end - b.end);\n return { cues, header, skipped };\n}\n\nfunction isIgnorableBlock(block: readonly string[]): boolean {\n const first = (block[0] ?? '').trim();\n return first === 'NOTE' || first.startsWith('NOTE ') || first === 'STYLE' || first === 'REGION';\n}\n\nfunction readBlock(block: readonly string[]): VttCue | null {\n if (block.length === 0 || isIgnorableBlock(block)) {\n return null;\n }\n // The timing line is either the first or the second: a cue may carry\n // an identifier above it.\n const timingIndex = block.findIndex(line => line.includes('-->'));\n if (timingIndex === -1 || timingIndex > 1) {\n return null;\n }\n const id = timingIndex === 1 ? block[0]!.trim() : undefined;\n const timing = block[timingIndex]!;\n const arrow = timing.indexOf('-->');\n const start = parseTimestamp(timing.slice(0, arrow));\n const rest = timing.slice(arrow + 3).trim();\n // The end timestamp runs to the first space; whatever follows is\n // settings.\n const space = rest.search(/\\s/);\n const end = parseTimestamp(space === -1 ? rest : rest.slice(0, space));\n if (start === null || end === null || end < start) {\n return null;\n }\n const settings = space === -1 ? EMPTY_SETTINGS : parseSettings(rest.slice(space + 1));\n const payload = block.slice(timingIndex + 1);\n if (payload.length === 0) {\n return null;\n }\n return { id, start, end, text: stripMarkup(payload.join('\\n')), settings };\n}\n\n/**\n * `hh:mm:ss.mmm` or `mm:ss.mmm`, into seconds.\n *\n * Null rather than NaN for anything else, so a caller cannot\n * accidentally propagate a number that compares false against\n * everything.\n */\nfunction parseTimestamp(text: string): number | null {\n const match = /^\\s*(?:(\\d+):)?([0-5]\\d):([0-5]\\d)[.,](\\d{1,3})\\s*$/.exec(text);\n if (match === null) {\n return null;\n }\n const hours = match[1] === undefined ? 0 : Number(match[1]);\n const minutes = Number(match[2]);\n const seconds = Number(match[3]);\n // One or two digits after the point mean tenths and hundredths, not\n // thousandths: `00:01.5` is a second and a half.\n const fraction = Number(match[4]!.padEnd(3, '0')) / 1000;\n return hours * 3600 + minutes * 60 + seconds + fraction;\n}\n\nfunction parseSettings(text: string): Readonly<Record<string, string>> {\n const settings: Record<string, string> = {};\n for (const pair of text.split(/\\s+/)) {\n const colon = pair.indexOf(':');\n if (colon <= 0) {\n continue;\n }\n settings[pair.slice(0, colon)] = pair.slice(colon + 1);\n }\n return Object.keys(settings).length === 0 ? EMPTY_SETTINGS : settings;\n}\n\n/**\n * Takes the tags out and leaves the words.\n *\n * Entities are decoded for the five WebVTT names only. There is no\n * general entity table here on purpose: the full set belongs to HTML,\n * a caption file that carries one is relying on a document context\n * this has none of, and the five below are the ones a caption actually\n * needs — a stray `<` in dialogue has to be escaped, and `&nbsp;` is\n * written by tools that think they are writing HTML.\n */\nfunction stripMarkup(text: string): string {\n return text\n .replace(/<[^>]*>/g, '')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&lrm;|&rlm;/g, '')\n .replace(/&nbsp;/g, ' ')\n .replace(/&amp;/g, '&')\n .trim();\n}\n\n/**\n * The cue to show at a position, or null between cues.\n *\n * A linear scan from a hint rather than a binary search, and that is\n * the right shape here rather than a concession: this is called once\n * per frame with a position that has almost always moved by one frame,\n * so the answer is nearly always the cue it was last time or the one\n * after it. `from` is that hint — pass back the index this returned —\n * and the scan is then O(1) for playback and O(n) only for a scrub,\n * where a caption is not what is costing anything.\n *\n * **Overlapping cues.** WebVTT permits them and a transcript of two\n * people talking over each other has them. This answers the one that\n * *starts* latest among those covering the position, which is what a\n * single-line caption should show: the newest thing said.\n */\nexport function cueAt(cues: readonly VttCue[], seconds: number, from = 0): { cue: VttCue | null; index: number } {\n if (cues.length === 0) {\n return { cue: null, index: 0 };\n }\n // Walk back if the position went backwards — a seek, or a loop.\n let index = Math.min(Math.max(0, from), cues.length - 1);\n while (index > 0 && cues[index]!.start > seconds) {\n index--;\n }\n let best: VttCue | null = null;\n let bestIndex = index;\n for (let n = index; n < cues.length; n++) {\n const cue = cues[n]!;\n if (cue.start > seconds) {\n break;\n }\n if (cue.end > seconds) {\n best = cue;\n bestIndex = n;\n }\n }\n // Nothing covers the position: keep the scan's starting point as the\n // hint, so the next call resumes from there rather than from zero.\n return { cue: best, index: best === null ? index : bestIndex };\n}\n","/**\n * Gesso's work, in the browser's own profiler\n *.\n *\n * A performance recording of a Gesso application shows one long task\n * per frame and nothing inside it, because everything the framework\n * does happens under callbacks the browser cannot name. `performance\n * .measure` is the one way to put a name on a span that the DevTools\n * Performance panel, `PerformanceObserver` and a real-user monitor all\n * already read, so this is what the framework emits rather than a\n * timeline of its own.\n *\n * **It is off until something asks.** A production application that\n * never turns it on takes one boolean read per span and nothing else:\n * no clock reading, no string, no entry retained. That is the whole\n * reason the switch is a module-level flag rather than an option on\n * the scheduler — a span has to be able to ask \"is anyone recording\"\n * before it pays for the timestamp that would answer.\n *\n * It can be turned on in production, which is the point. Either call\n * `setPerformanceMarks(true)`, or set `globalThis.__GESSO_PERF__` to\n * true before the framework's first import: a page can then be\n * profiled as it shipped, with no development build and no devtools\n * attached.\n */\n\n/** Every measure this module emits starts with this, so a filter finds them all. */\nexport const MARK_PREFIX = 'gesso';\n\n/**\n * Read once, at module scope: a page that wants marks in a production\n * build sets the flag before the framework loads, and pays nothing for\n * the check afterwards.\n */\nlet recording = readInitialFlag();\n\n/** Whether `performance.measure` exists at all; a worker without it must not throw. */\nconst supported =\n typeof performance !== 'undefined' &&\n typeof performance.measure === 'function' &&\n typeof performance.now === 'function';\n\nfunction readInitialFlag(): boolean {\n return (globalThis as { __GESSO_PERF__?: unknown }).__GESSO_PERF__ === true;\n}\n\n/**\n * Turns the marks on or off.\n *\n * A devtools panel calls this; so may an application that wants a\n * profile of one interaction and nothing else.\n */\nexport function setPerformanceMarks(enabled: boolean): void {\n recording = enabled;\n}\n\n/**\n * Whether spans are being recorded.\n *\n * Call it before doing any work a span needs, including reading the\n * clock. This is the check that keeps the cost at zero when nobody is\n * listening.\n */\nexport function performanceMarksEnabled(): boolean {\n return recording && supported;\n}\n\n/**\n * A span between two `performance.now()` readings the caller already\n * took, named `gesso <what>`.\n *\n * Times are passed in rather than taken here because the caller is\n * timing the work anyway: the scheduler's frame span and the frame's\n * own duration must be the same two readings, or a profile and a\n * `FrameMetrics` would disagree about the same frame.\n *\n * `detail` rides along for the panels that show it (Chrome puts it on\n * the entry's tooltip) and costs nothing when marks are off, because\n * this is not called then.\n */\nexport function measureSpan(what: string, start: number, end: number, detail?: unknown): void {\n if (!recording || !supported) {\n return;\n }\n try {\n performance.measure(`${MARK_PREFIX} ${what}`, { start, end, ...(detail === undefined ? {} : { detail }) });\n } catch {\n // A measure can throw on a start time the browser has dropped from\n // its buffer. Instrumentation must never be the thing that breaks\n // the frame it is describing.\n }\n}\n\n/**\n * A point in time, named `gesso <what>`.\n *\n * For things with no duration: a command crossing the barrier, a patch\n * batch arriving. They land in the same track as the spans, so a\n * command and the frame that answered it are read off one timeline.\n */\nexport function markInstant(what: string, detail?: unknown): void {\n if (!recording || !supported || typeof performance.mark !== 'function') {\n return;\n }\n try {\n performance.mark(`${MARK_PREFIX} ${what}`, detail === undefined ? undefined : { detail });\n } catch {\n // As above: never throw out of instrumentation.\n }\n}\n\n/** `performance.now()`, or the epoch clock where there is no `performance`. */\nexport function markNow(): number {\n return supported ? performance.now() : Date.now();\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiFrameTime } from './UiFrameClock';\nimport type { UiNode } from '../graph/UiNode';\n\n/**\n * The unit of work produced by one scheduler frame.\n *\n * The dirty nodes and their flags are snapshotted when the\n * frame is collected, so a node dirtied again while the frame\n * is being processed belongs to the next frame.\n */\nexport class UiFrame {\n constructor(\n public readonly id: number,\n public readonly time: UiFrameTime,\n private readonly dirty: ReadonlyMap<UiNode, DirtyFlags>\n ) {}\n\n /**\n * The dirty nodes as an array.\n *\n * Copies, so it is the wrong thing to reach for on the frame path:\n * prefer `entries()` to walk them, and `anyFlags` to ask a question\n * about them. It stays because a caller that needs to hold the list\n * past the walk, or to index into it, wants a copy and should get an\n * honest one rather than a view that changes underneath it.\n */\n get nodes(): UiNode[] {\n return [...this.dirty.keys()];\n }\n\n /**\n * The dirty nodes with their flags, without copying either.\n *\n * The pairing matters as much as the absence of a copy. Walking\n * `nodes` and calling `dirtyFlagsFor` on each one asks the map for\n * something it just handed over, so a frame of a thousand dirty\n * nodes paid an array and a thousand lookups to learn what iterating\n * the entries says for free.\n */\n entries(): IterableIterator<[UiNode, DirtyFlags]> {\n return this.dirty.entries();\n }\n\n /**\n * Whether any node in the frame carries one of `flags`.\n *\n * The question the runtime actually asks three times a frame: does\n * this frame need layout, does it need semantics, did it change the\n * tree. Answering it here lets it stop at the first node that says\n * yes, which on a frame that does need layout is usually the first\n * node looked at.\n */\n anyFlags(flags: DirtyFlags): boolean {\n for (const value of this.dirty.values()) {\n if ((value & flags) !== 0) {\n return true;\n }\n }\n return false;\n }\n\n get size(): number {\n return this.dirty.size;\n }\n\n isEmpty(): boolean {\n return this.dirty.size === 0;\n }\n\n dirtyFlagsFor(node: UiNode): DirtyFlags {\n return this.dirty.get(node) ?? DirtyFlags.None;\n }\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport { DirtyNodeSet } from '../graph/DirtyNodeSet';\nimport { markNow, measureSpan, performanceMarksEnabled } from './PerformanceMarks';\nimport { UiFrame } from './UiFrame';\nimport type { UiFrameClock, UiFrameClockFactory, UiFrameTime } from './UiFrameClock';\nimport type { UiNode } from '../graph/UiNode';\n\nexport type UiFrameCallback = (frame: UiFrame) => void;\n\nexport interface UiSchedulerOptions {\n /**\n * Builds the frame clock for this scheduler.\n */\n clock: UiFrameClockFactory;\n /**\n * The shared dirty set. The graph marks into it and the\n * scheduler drains it on each frame.\n */\n dirty: DirtyNodeSet;\n /**\n * Receives one UiFrame per processed tick.\n */\n onFrame: UiFrameCallback;\n\n /**\n * Runs immediately before the dirty set is snapshotted.\n *\n * For work that produces dirt of its own — applying store patches,\n * propagating environment, advancing animations — so the nodes it\n * dirties belong to the frame about to be collected rather than the\n * one after it. Anything reading the dirty set after collection sees\n * an empty one, so this hook is the only place such work can go.\n *\n * It receives the frame's time, so a phase that advances with the\n * clock advances on *this* clock: under a manual clock in a spec\n * `performance.now()` is a different number entirely, and an\n * animation reading it would not be reproducible.\n */\n beforeCollect?: (time: UiFrameTime) => void;\n}\n\n/**\n * Decides when a dirty graph gets processed.\n *\n * No observable emission reaches this directly. Marking a node\n * dirty only calls notifyDirty(), which arms a frame at most\n * once. Additional marks are coalesced into the pending frame.\n *\n * The scheduler is timing agnostic: it drives whatever clock it\n * is given, so the same runtime works on the main thread, in a\n * Worker, and in tests.\n */\nexport class UiScheduler {\n private readonly clock: UiFrameClock;\n private readonly dirty: DirtyNodeSet;\n private readonly onFrame: UiFrameCallback;\n private readonly beforeCollect: ((time: UiFrameTime) => void) | undefined;\n\n private disposed = false;\n private active = true;\n private pending = false;\n /** True between a frame starting and its dirty set being taken. */\n private collecting = false;\n private frames = 0;\n\n /**\n * Scratch space for draining the dirty set.\n *\n * Kept for the life of the scheduler: this drain happens on every\n * frame, and the nodes are copied straight into the frame's map, so\n * nothing outside collectFrame ever sees the array.\n */\n private readonly drained: UiNode[] = [];\n\n constructor(options: UiSchedulerOptions) {\n this.dirty = options.dirty;\n this.onFrame = options.onFrame;\n this.beforeCollect = options.beforeCollect;\n this.clock = options.clock((time: UiFrameTime) => {\n this.handleFrame(time);\n });\n }\n\n /**\n * Called whenever a node becomes dirty.\n *\n * Arms a frame only when none is already pending — and not at all\n * while a frame is between starting and collecting its dirty set,\n * because that frame is about to collect this very node. The work\n * happens either way; what a frame armed here would add is a second\n * frame with nothing left to do.\n *\n * This is the difference between `notifyDirty` and `wake`, and it\n * only became visible when frames started following the display.\n * `beforeCollect` is where animations run, and an animation that\n * writes through the graph — `videoSource` presenting the frame due\n * at its position is the one that found this — marked a node dirty\n * from inside the frame that was going to draw it, and armed\n * another. Against a fixed 16ms timer the spare frame was simply the\n * next scheduled one and cost nothing; against a 165Hz display it\n * doubled the rate of everything a video was playing behind.\n */\n notifyDirty(): void {\n if (this.collecting) {\n return;\n }\n this.wake();\n }\n\n /**\n * Arms a frame for work that is not in the dirty set.\n *\n * `notifyDirty` is the graph's call and means \"a node changed\"; this\n * means \"run a frame anyway\", which is what an animation needs.\n * Nothing an animation writes can be what arms its own frame: its\n * writes happen in `beforeCollect`, inside a frame that has to exist\n * already. Something outside the graph has to ask, and this is the\n * asking. Safe to call from inside `beforeCollect` — `pending` is\n * cleared before that hook runs, so it arms the next frame and not\n * the one in progress.\n */\n wake(): void {\n if (this.disposed || !this.active || this.pending) {\n return;\n }\n this.pending = true;\n this.clock.requestFrame();\n }\n\n start(): void {\n if (this.disposed) {\n throw new Error('UiScheduler is disposed.');\n }\n if (this.active) {\n return;\n }\n this.active = true;\n if (!this.dirty.isEmpty()) {\n this.notifyDirty();\n }\n }\n\n stop(): void {\n if (!this.active) {\n return;\n }\n this.active = false;\n this.pending = false;\n this.clock.cancelFrame();\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.active = false;\n this.pending = false;\n this.clock.cancelFrame();\n }\n\n get running(): boolean {\n return this.active;\n }\n\n get framePending(): boolean {\n return this.pending;\n }\n\n get frameCount(): number {\n return this.frames;\n }\n\n /**\n * Processes the pending frame now, in the caller's task.\n *\n * For work that has already made the surface stale — a resize clears\n * the backing store — where waiting for the clock would present the\n * cleared surface for a frame.\n */\n flush(time: UiFrameTime): void {\n if (this.disposed || !this.active) {\n return;\n }\n this.clock.cancelFrame();\n this.pending = false;\n this.handleFrame(time);\n }\n\n /**\n * Runs one frame, and names its three parts for the browser's\n * profiler when anything is recording (`PerformanceMarks`).\n *\n * The three are the ones this class owns the boundaries of, and they\n * are exact rather than reconstructed: the work before the dirty set\n * is taken (animations, patches, environment, virtualisation), the\n * taking of it, and everything the frame callback does with it\n * (layout, semantics, render). The per-phase breakdown inside those\n * belongs to whoever runs the phases and reaches a panel as\n * `FrameMetrics.phases`.\n *\n * `measuring` is read once and the clock is read only when it is\n * true, so a frame nobody is profiling pays one boolean read.\n */\n private handleFrame(time: UiFrameTime): void {\n const measuring = performanceMarksEnabled();\n const started = measuring ? markNow() : 0;\n this.pending = false;\n this.collecting = true;\n let frame: UiFrame;\n let collected = 0;\n try {\n this.beforeCollect?.(time);\n if (measuring) {\n collected = markNow();\n measureSpan('before collect', started, collected);\n }\n frame = this.collectFrame(time);\n } finally {\n this.collecting = false;\n }\n let processed = 0;\n if (measuring) {\n processed = markNow();\n measureSpan('collect', collected, processed);\n }\n if (frame.size > 0) {\n this.onFrame(frame);\n }\n if (measuring) {\n const finished = markNow();\n const detail = { frame: frame.id, nodes: frame.size };\n measureSpan('process', processed, finished, detail);\n measureSpan('frame', started, finished, detail);\n }\n // Work may have arrived while the frame was being processed.\n // The dirty listener also arms a frame, but this covers a\n // scheduler used without a listener wired to the graph.\n if (!this.dirty.isEmpty()) {\n this.notifyDirty();\n }\n }\n\n private collectFrame(time: UiFrameTime): UiFrame {\n const count = this.dirty.drainInto(this.drained);\n const dirty = new Map<UiNode, DirtyFlags>();\n for (let i = 0; i < count; i++) {\n const node = this.drained[i];\n dirty.set(node, node.dirtyFlags);\n node.dirtyFlags = DirtyFlags.None;\n }\n const frame = new UiFrame(this.frames, time, dirty);\n if (dirty.size > 0) {\n this.frames++;\n }\n return frame;\n }\n}\n","/**\n * A time value, in milliseconds.\n *\n * The value is sourced from the frame clock so it stays\n * meaningful both in the browser main thread and in a Worker.\n */\nexport type UiFrameTime = number;\n\n/**\n * An abstract frame clock.\n *\n * The core runtime never touches requestAnimationFrame or\n * setTimeout directly. It only asks the clock to arm a frame\n * and to cancel one, which keeps the runtime usable in a\n * Worker or under a test clock.\n */\nexport interface UiFrameClock {\n requestFrame(): void;\n cancelFrame(): void;\n}\n\n/**\n * Builds a clock wired to a callback.\n *\n * A clock delivers ticks through the callback supplied to its\n * constructor, so a scheduler asks for a fresh clock through a\n * factory rather than holding a half-wired instance.\n */\nexport type UiFrameClockFactory = (onFrame: (time: UiFrameTime) => void) => UiFrameClock;\n\ninterface AnimationFrameHost {\n requestAnimationFrame?: (callback: (time: number) => void) => number;\n cancelAnimationFrame?: (handle: number) => void;\n}\n\n/**\n * Browser main-thread clock backed by requestAnimationFrame.\n *\n * Fails fast when requestAnimationFrame is unavailable (for\n * example in a Worker) instead of silently doing nothing.\n */\nexport class UiAnimationFrameClock implements UiFrameClock {\n private readonly requestAnimationFrame: (callback: (time: number) => void) => number;\n private readonly cancelAnimationFrame: (handle: number) => void;\n private handle: number | null = null;\n\n constructor(private readonly onFrame: (time: UiFrameTime) => void) {\n const host = globalThis as AnimationFrameHost;\n if (typeof host.requestAnimationFrame !== 'function' || typeof host.cancelAnimationFrame !== 'function') {\n throw new Error('requestAnimationFrame is not available in this environment.');\n }\n this.requestAnimationFrame = host.requestAnimationFrame.bind(host);\n this.cancelAnimationFrame = host.cancelAnimationFrame.bind(host);\n }\n\n requestFrame(): void {\n if (this.handle !== null) {\n return;\n }\n this.handle = this.requestAnimationFrame((time: number) => {\n this.handle = null;\n this.onFrame(time);\n });\n }\n\n cancelFrame(): void {\n if (this.handle === null) {\n return;\n }\n this.cancelAnimationFrame(this.handle);\n this.handle = null;\n }\n}\n\nexport interface UiTimerFrameClockOptions {\n /** Delay between requestFrame and the tick, in milliseconds. */\n intervalMs?: number;\n /** Time source used for the delivered tick. */\n now?: () => UiFrameTime;\n}\n\n/**\n * Refreshes to keep asking for after the last frame anybody wanted.\n *\n * Four is about a fortieth of a second at sixty and a fortieth at one\n * hundred and sixty-five, which covers the gap anything animating\n * leaves between frames without keeping an idle application awake for\n * a noticeable moment.\n */\n/** How many refresh intervals the watch is measured over. */\nconst CADENCE_SAMPLES = 5;\n\n/** Above this, a gap between ticks is a hiccup rather than a display's rate. */\nconst MAX_CADENCE_MS = 50;\n\n/**\n * How many refresh intervals the watchdog waits before calling a host\n * stalled. See `watchMs` for why one is not enough.\n */\nconst WATCH_REFRESHES = 2;\n\nconst IDLE_TICKS_BEFORE_STOP = 4;\n\nexport interface UiHostFrameClockOptions {\n /**\n * This thread's own `requestAnimationFrame`, where it has one.\n *\n * Defaults to looking for it, which is the whole point: a dedicated\n * worker has one, and using it removes the host from the frame path\n * entirely. Pass `null` to force the hosted path, which specs do and\n * which is also the escape hatch if a browser's worker frames ever\n * turn out to be worse than its host's.\n */\n requestAnimationFrame?: ((callback: (time: number) => void) => number) | null;\n /** How long to wait for a host tick before pacing a frame anyway. */\n fallbackMs?: number;\n /**\n * The longest a hosted clock will wait for a tick before deciding the\n * host has stopped answering and pacing itself again.\n *\n * A ceiling rather than the usual figure: once ticks have been\n * arriving the clock knows the display's own interval and watches at\n * twice that, so this value only applies before it has seen enough\n * refreshes to say. Waiting a tenth of a second on every stall would\n * be six dropped frames on a 60Hz panel, which is visible.\n */\n stallMs?: number;\n /**\n * The shortest that watch can be, whatever the measured cadence.\n *\n * A floor exists because the estimate is taken from timestamps that\n * jitter: watching at exactly one refresh would fire on any tick that\n * arrived a moment late and draw a frame nobody asked for.\n */\n minStallMs?: number;\n /** Time source for a fallback tick; host ticks carry their own. */\n now?: () => UiFrameTime;\n}\n\n/**\n * Timer-backed clock that works in the browser main thread,\n * in a Worker, and in Node.\n */\nexport class UiTimerFrameClock implements UiFrameClock {\n private readonly intervalMs: number;\n private readonly now: () => UiFrameTime;\n private handle: ReturnType<typeof setTimeout> | null = null;\n\n constructor(\n private readonly onFrame: (time: UiFrameTime) => void,\n options: UiTimerFrameClockOptions = {}\n ) {\n this.intervalMs = options.intervalMs ?? 16;\n this.now = options.now ?? (() => performance.now());\n }\n\n requestFrame(): void {\n if (this.handle !== null) {\n return;\n }\n this.handle = setTimeout(() => {\n this.handle = null;\n this.onFrame(this.now());\n }, this.intervalMs);\n }\n\n cancelFrame(): void {\n if (this.handle === null) {\n return;\n }\n clearTimeout(this.handle);\n this.handle = null;\n }\n}\n\n/**\n * A clock whose frames arrive from somewhere else.\n *\n * A render worker's frames were once paced by `UiTimerFrameClock` at a\n * fixed 16ms — roughly sixty a second on any display, aligned to none\n * of them. On a 165Hz monitor that is not merely slower than it could\n * be: an unaligned timer lands two frames inside one refresh, or none,\n * so the pacing is uneven as well as capped.\n *\n * **A dedicated worker can ask the compositor itself**, and this clock\n * does so when it can. `DedicatedWorkerGlobalScope.requestAnimationFrame`\n * has been in Chrome since 69, Firefox since 99 and Safari since 16.4,\n * which makes it baseline as of March 2023; this file used to say it\n * \"exists only on the main thread\", which was true when it was written\n * and is not now. Measured in the render worker: a median interval of\n * 16.7ms with a spread of 0.2, which is vsync and not a timer. Its\n * timestamps are on this thread's own `performance.now()` timeline, so\n * nothing has to be translated.\n *\n * That path costs the shell nothing at all: no loop, and no message\n * per refresh. `onActive` is never called, so a host that was\n * forwarding ticks simply stops being asked to.\n *\n * **The hosted path remains for where it is not available**, which is\n * an older browser and, on Chromium, a *nested* worker. Then this\n * clock does no timing of its own: it reports when it wants frames and\n * delivers whatever ticks it is handed, which lets the shell supply\n * the display's cadence on its behalf. It is the same contract either\n * way, which is why the choice can be made at construction and\n * forgotten.\n *\n * Both paths stop for a hidden tab, and that is not a coincidence: a\n * worker's animation frames are serviced by its **owner window's**\n * rendering, and a window that is not rendering services none. So the\n * battery properties the runtime relies on hold whichever path runs.\n *\n * **Ticks are free-running while frames are wanted, not requested one\n * at a time.** A tick per request would cost a round trip inside every\n * frame: if the request reaches the shell after that vsync's callback\n * has run, the tick waits for the next one and the rate halves. So\n * `onActive(true)` means \"keep ticking\" and `onActive(false)` means\n * \"stop\"; a tick arriving with nothing pending is dropped, which costs\n * one comparison and makes the timing robust to whichever side is\n * late.\n */\n/**\n * This thread's `requestAnimationFrame`, if it has a real one.\n *\n * Checked for rather than assumed, because the answer differs by\n * thread and by browser: a window always has one, a dedicated worker\n * has had one for years, and a *nested* worker on Chromium has not.\n * The feature test is the whole guard; there is no version to compare.\n */\nfunction localAnimationFrame(): ((callback: (time: number) => void) => number) | null {\n const host = globalThis as { requestAnimationFrame?: (callback: (time: number) => void) => number };\n return typeof host.requestAnimationFrame === 'function' ? host.requestAnimationFrame.bind(host) : null;\n}\n\nexport class UiHostFrameClock implements UiFrameClock {\n private pending = false;\n private active = false;\n /** Set by the first real tick; until then the fallback timer paces. */\n private hosted = false;\n /**\n * True while a host that was ticking has gone quiet, and this clock\n * is pacing itself again until it comes back.\n */\n private stalled = false;\n /** Refreshes arrived with nothing pending, before the loop stops. */\n private idleTicks = 0;\n /** The time the last tick carried, for measuring the display's interval. */\n private lastTickTime: UiFrameTime | null = null;\n /**\n * The host's clock minus this thread's, or null before a tick has\n * shown what it is. See `hostTime`.\n */\n private hostOffset: number | null = null;\n /**\n * The last few intervals between ticks, in arrival order.\n *\n * The watch is set from the *median* of them. The largest was the\n * first attempt and it is too generous: one late refresh — a page\n * doing something expensive in its own rAF, which is exactly what\n * happens the moment before a demonstration blocks the thread —\n * widened the watch to twice that late gap and made the next stall\n * take a quarter of a second to notice. A median ignores one\n * outlier in four and still describes the display.\n */\n private readonly intervals: number[] = [];\n private handle: ReturnType<typeof setTimeout> | null = null;\n private readonly fallbackMs: number;\n private readonly stallMs: number;\n private readonly minStallMs: number;\n private readonly now: () => UiFrameTime;\n /** This thread's own `requestAnimationFrame`, or null where there is none. */\n private readonly local: ((callback: (time: number) => void) => number) | null;\n private localHandle: number | null = null;\n\n constructor(\n private readonly onFrame: (time: UiFrameTime) => void,\n /** Told when this starts and stops wanting ticks. */\n private readonly onActive: (active: boolean) => void,\n options: UiHostFrameClockOptions = {}\n ) {\n this.fallbackMs = options.fallbackMs ?? 16;\n this.stallMs = options.stallMs ?? 100;\n this.minStallMs = options.minStallMs ?? 12;\n this.now = options.now ?? (() => performance.now());\n this.local = options.requestAnimationFrame === undefined ? localAnimationFrame() : options.requestAnimationFrame;\n }\n\n /** Whether this clock is driving itself from the compositor. */\n get isLocal(): boolean {\n return this.local !== null;\n }\n\n requestFrame(): void {\n this.pending = true;\n if (this.local !== null) {\n // The compositor is answering directly, so there is no host to\n // tell and no timer to arm: no message per refresh, and no\n // fallback to pace, because a missing frame here would mean the\n // display itself had stopped.\n if (this.localHandle === null) {\n this.localHandle = this.local(time => {\n this.localHandle = null;\n if (this.pending) {\n this.deliver(time);\n }\n });\n }\n return;\n }\n this.setActive(true);\n this.armTimer();\n }\n\n /**\n * Arms the timer that draws a frame the host did not ask for.\n *\n * It plays two roles with one mechanism. Before any tick has arrived\n * it *paces*: a host that does not forward refreshes is then merely\n * no better than the timer this clock replaced, rather than a frozen\n * application — which matters because the capability is not\n * negotiated, and the first real tick is the only evidence it exists.\n *\n * Once ticks are arriving it *watches*: set far beyond a refresh\n * interval, cleared by every tick, and therefore never fired while\n * the host is answering. When it does fire, the host has stopped —\n * a blocked main thread is the case that matters — and this clock\n * goes back to pacing itself until a tick says otherwise.\n *\n * Without that second role the render worker's frames stop dead\n * whenever the shell's thread is busy, which is the one thing the\n * whole architecture exists to prevent.\n */\n private armTimer(): void {\n if (this.handle !== null) {\n return;\n }\n const watching = this.hosted && !this.stalled;\n this.handle = setTimeout(\n () => {\n this.handle = null;\n if (!this.pending) {\n return;\n }\n // A tick would have cleared this timer, so its firing is the\n // evidence that none came.\n this.stalled = this.hosted;\n this.deliver(this.hostTime());\n },\n watching ? this.watchMs() : this.selfPaceMs()\n );\n }\n\n /**\n * How long to wait for a tick before concluding none is coming.\n *\n * Two refreshes: a live host always beats that, and a blocked one is\n * noticed a frame or two after it stops rather than a fixed tenth of\n * a second later. The ceiling applies until enough ticks have\n * arrived to say, which is three, because two timestamps make one\n * interval and one interval is not evidence of a cadence.\n *\n * One refresh was tried first and is the bug this constant exists to\n * record. The reasoning was that the timer is armed after the frame\n * the tick delivered has done its work, so the next tick is already\n * due by the deadline and wins the race whenever the host is alive.\n * It is not: `requestFrame` is called from the runtime's\n * `beforeCollect`, at the *start* of the frame, so the deadline\n * falls a few microseconds after the next tick is due rather than a\n * whole frame's work after it. The race is then decided by the\n * jitter on one `postMessage`, and the watchdog fires on a large\n * share of ordinary frames.\n *\n * It went unseen because the floor hides it on a fast display: at\n * 165Hz a 6ms cadence is raised to `minStallMs`, which is already\n * two refreshes, while at 60Hz a 16.7ms cadence is passed through\n * untouched. Scrolling was smooth on the one panel and ran at a\n * third of the rate on the other, which is exactly the asymmetry\n * this arithmetic produces.\n */\n private watchMs(): number {\n const cadence = this.cadenceMs();\n if (cadence === null) {\n return this.stallMs;\n }\n return Math.min(this.stallMs, Math.max(this.minStallMs, cadence * WATCH_REFRESHES));\n }\n\n /**\n * This thread's own clock, expressed on the host's.\n *\n * A tick carries the host's `requestAnimationFrame` timestamp, and a\n * self-paced frame has only `performance.now()` to offer. In a\n * worker those are not the same clock: `performance.now()` counts\n * from the worker's own creation, so it trails the shell's by the\n * page's age at the moment the worker was spawned, which is hundreds\n * of milliseconds in practice.\n *\n * Handing that raw reading to `onFrame` puts a timestamp from a\n * different origin into a stream of host ones, and everything\n * downstream reads the pair as a jump backwards through time. The\n * runtime's animation phase declines to sample, because every\n * running animation is due later than a `now` that far in the past,\n * and then arms its next wake-up for `next - now`, which is the\n * whole offset. One self-paced frame therefore silences the\n * animation clock for as long as the worker is young. A scroll\n * spring stops being advanced by the frame loop and only moves when\n * something else dirties the graph.\n *\n * So the offset is learned from every tick and added back here.\n * Before the first tick there is nothing to learn from and no host\n * timestamp has been delivered either, so the local reading is the\n * base and the first tick shifts it forward once.\n */\n private hostTime(): number {\n const local = this.now();\n return this.hostOffset === null ? local : local + this.hostOffset;\n }\n\n /** The median recent interval, or null before there are enough to say. */\n private cadenceMs(): number | null {\n if (this.intervals.length < 3) {\n return null;\n }\n const sorted = [...this.intervals].sort((a, b) => a - b);\n return sorted[Math.floor(sorted.length / 2)]!;\n }\n\n /** The interval to pace at while the host is absent: the display's, if it is known. */\n private selfPaceMs(): number {\n const cadence = this.cadenceMs();\n return cadence === null ? this.fallbackMs : Math.max(1, Math.min(this.fallbackMs, cadence));\n }\n\n cancelFrame(): void {\n this.pending = false;\n // The local handle is deliberately left armed. There is no way to\n // cancel it without the matching `cancelAnimationFrame`, and one\n // refresh that finds nothing pending costs a comparison, which is\n // what `tick` already does for the hosted path.\n this.clearFallback();\n if (this.local === null) {\n this.setActive(false);\n }\n }\n\n private clearFallback(): void {\n if (this.handle !== null) {\n clearTimeout(this.handle);\n this.handle = null;\n }\n }\n\n private deliver(time: UiFrameTime): void {\n this.pending = false;\n this.onFrame(time);\n }\n\n /**\n * Delivers a tick from the host. Ignored when no frame is pending,\n * which is what lets the host keep a loop running a beat longer than\n * it is needed rather than negotiating every frame.\n */\n tick(time: UiFrameTime): void {\n this.hosted = true;\n this.stalled = false;\n // Every tick, not just the first: the two clocks drift, and a tab\n // that was suspended resumes with a different offset entirely.\n // Reading it here costs one subtraction and keeps a self-paced\n // frame on the same timeline as the ticks around it.\n this.hostOffset = time - this.now();\n this.recordCadence(time);\n this.clearFallback();\n if (!this.pending) {\n // Not wanted *yet* is not the same as not wanted. Something\n // animating slower than the display asks for its next frame a\n // moment after finishing this one, so stopping the loop the\n // instant a frame ends and restarting it milliseconds later\n // trades one message per frame for three and a cancelled\n // `requestAnimationFrame` in between. A few idle refreshes of\n // patience costs a dropped tick each and removes all of it.\n this.idleTicks++;\n if (this.idleTicks >= IDLE_TICKS_BEFORE_STOP) {\n this.setActive(false);\n }\n return;\n }\n this.idleTicks = 0;\n this.deliver(time);\n }\n\n /**\n * Keeps the last few gaps between ticks, which is where the watch\n * interval comes from.\n *\n * A gap wider than `MAX_CADENCE_MS` is not a refresh interval — no\n * display runs that slowly — so it is a hiccup, a stall ending, or a\n * tab coming back, and it is dropped rather than taught to the clock.\n * Only a gap wider than the ceiling clears what was learned, because\n * that is long enough that whatever cadence preceded it is stale.\n */\n private recordCadence(time: UiFrameTime): void {\n const previous = this.lastTickTime;\n this.lastTickTime = time;\n if (previous === null) {\n return;\n }\n const interval = time - previous;\n if (interval > this.stallMs) {\n this.intervals.length = 0;\n return;\n }\n if (interval <= 0 || interval > MAX_CADENCE_MS) {\n return;\n }\n this.intervals.push(interval);\n if (this.intervals.length > CADENCE_SAMPLES) {\n this.intervals.shift();\n }\n }\n\n private setActive(active: boolean): void {\n if (active) {\n this.idleTicks = 0;\n }\n if (this.active === active) {\n return;\n }\n this.active = active;\n this.onActive(active);\n }\n}\n\n/**\n * Deterministic clock for tests.\n *\n * Frames are delivered by calling tick(). tick() throws when\n * no frame is pending, which catches accidental double-arming.\n */\nexport class UiManualFrameClock implements UiFrameClock {\n private pending = false;\n\n constructor(private onFrame: (time: UiFrameTime) => void) {}\n\n setCallback(onFrame: (time: UiFrameTime) => void): void {\n this.onFrame = onFrame;\n }\n\n requestFrame(): void {\n this.pending = true;\n }\n\n cancelFrame(): void {\n this.pending = false;\n }\n\n get isPending(): boolean {\n return this.pending;\n }\n\n tick(time: UiFrameTime = 0): void {\n if (!this.pending) {\n throw new Error('No frame is pending.');\n }\n this.pending = false;\n this.onFrame(time);\n }\n}\n","import { DirtyFlags } from '../graph/DirtyFlags';\nimport type { UiNode } from '../graph/UiNode';\nimport type { LayoutBox } from '../layout/LayoutTypes';\nimport type { LayoutRecord } from '../layout/LayoutRecord';\nimport type { TextMeasurer } from '../layout/TextMeasurer';\nimport { createPaintState, resolvePaintState } from '../rendering/PaintState';\nimport { detectEditingPlatform, type EditingPlatform } from '../editing/EditingKeymap';\nimport { lineEndAt, lineStartAt } from '../editing/TextBoundaries';\nimport type { HitTester } from '../input/UiHitTester';\nimport type { UiKeyModifiers } from '../input/UiInputEvent';\nimport {\n clearSelectionRange,\n selectableTextNodes,\n selectableTextOf,\n selectionRangeOf,\n setSelectionRange\n} from './UiSelectable';\nimport { clearLinkHover, hasTextLinks, linkOf, linkRunAtPointIn, setLinkHover } from './UiTextLinks';\nimport {\n hasDrawnText,\n offsetAtPointIn,\n paragraphGeometry,\n wordRangeIn,\n type ParagraphGeometry\n} from './TextSelectionGeometry';\n\n/**\n * What the controller needs from the runtime around it: geometry,\n * measurement, and the three things it cannot do itself — mark a node\n * dirty, drop an editable's focus, and reach the clipboard.\n */\nexport interface SelectionHost {\n recordFor(node: UiNode): LayoutRecord | undefined;\n /** Where the node is seen, scroll offsets applied; for the nearest-node search. */\n visibleBox(node: UiNode): LayoutBox;\n readonly measurer: TextMeasurer;\n markDirty(node: UiNode, flags: DirtyFlags): void;\n /** The subtree a selection may range over: the layout root. */\n root(): UiNode;\n /** Hands text to the shell, which is the only thread with a clipboard. */\n copy(text: string): void;\n /**\n * Drops focus when it is on an editable. Pressing text outside a\n * field ends editing, as it does in a browser, so two selections are\n * never lit at once.\n */\n blurEditable(): void;\n now(): number;\n}\n\nexport interface SelectionControllerOptions {\n /** Keyboard conventions; detected from the user agent by default. */\n platform?: EditingPlatform;\n}\n\n/** One end of a selection: a text node and an offset into its text. */\ninterface TextPoint {\n node: UiNode;\n offset: number;\n}\n\n/** How long two presses may be apart and still count as a double click. */\nconst MULTI_CLICK_MS = 500;\nconst MULTI_CLICK_SLOP = 4;\n\n/**\n * Selecting text that nobody types into.\n *\n * `EditableText` carries its own selection in its model, because the\n * caret, the IME and undo all need it there. Everything else the\n * framework draws is glyphs on a canvas with no model at all, so this\n * controller is what lets a user drag across a heading and a paragraph\n * and copy what they highlighted — the thing a page of HTML gives for\n * free and a canvas gives not at all.\n *\n * Model: a selection is two `TextPoint`s, an anchor where the press\n * landed and a focus that follows the pointer. Ordering them needs\n * document order, so the selectable nodes under the root are collected\n * into a list when a drag starts; the range covers the tail of the\n * first node, all of every node between, and the head of the last. The\n * resolved per-node ranges are written onto the nodes themselves,\n * where both renderers read them (see `UiSelectable`).\n *\n * The pointer controller calls in here after the app's listeners have\n * run, and only when none of them called preventDefault().\n */\nexport class UiSelectionController {\n private readonly platform: EditingPlatform;\n private readonly paint = createPaintState();\n private anchor: TextPoint | null = null;\n private focus: TextPoint | null = null;\n private dragging = false;\n /** Document order for the drag in progress; rebuilt on each press. */\n private order: UiNode[] = [];\n /** The nodes currently carrying a range, so they can be cleared. */\n private painted: UiNode[] = [];\n private lastPress: { node: UiNode; x: number; y: number; at: number; count: number } | null = null;\n /** The link a press landed on, until it is released or dragged off. */\n private pendingLink: { node: UiNode; index: number; x: number; y: number } | null = null;\n /** The node carrying a lit link run, so it can be unlit when the pointer leaves. */\n private hoveredLink: UiNode | null = null;\n\n constructor(\n private readonly host: SelectionHost,\n private readonly hitTester: HitTester,\n options: SelectionControllerOptions = {}\n ) {\n this.platform = options.platform ?? detectEditingPlatform();\n }\n\n /** Whether anything is selected. */\n get hasSelection(): boolean {\n return this.painted.length > 0;\n }\n\n /** Whether a press on this node would start a selection. */\n isSelectable(node: UiNode): boolean {\n return selectableTextOf(node) !== undefined;\n }\n\n // ---------------------------------------------------------------------------\n // Pointer\n // ---------------------------------------------------------------------------\n\n /**\n * A press. On selectable text it places the anchor (Shift extends\n * from the existing one), and a second or third press within the\n * double-click window takes the word or the line. Anywhere else it\n * clears the selection, as pressing outside text does in a browser.\n *\n * Returns true when a selection is now in progress, so the caller\n * knows the press was consumed.\n */\n pointerDown(node: UiNode | null, x: number, y: number, modifiers: UiKeyModifiers): boolean {\n this.pendingLink = null;\n if (node === null) {\n this.clear();\n return false;\n }\n const geometry = this.geometryOf(node);\n const local = this.hitTester.toLocal(node, x, y);\n if (hasTextLinks(node)) {\n // A link is remembered rather than followed: a press that goes\n // on to drag is a selection, and only a press that is released\n // where it landed is a click. Held here and not in the pointer\n // controller because this is the one place that turns a point\n // inside a paragraph into a run.\n const index = linkRunAtPointIn(geometry, local.x, local.y);\n if (linkOf(node, index) !== undefined) {\n this.pendingLink = { node, index, x, y };\n }\n }\n if (!this.isSelectable(node) || !hasDrawnText(geometry)) {\n this.clear();\n return this.pendingLink !== null;\n }\n const offset = offsetAtPointIn(geometry, local.x, local.y);\n const now = this.host.now();\n const last = this.lastPress;\n const count =\n !modifiers.shift &&\n last !== null &&\n last.node === node &&\n now - last.at <= MULTI_CLICK_MS &&\n Math.abs(last.x - x) <= MULTI_CLICK_SLOP &&\n Math.abs(last.y - y) <= MULTI_CLICK_SLOP\n ? last.count + 1\n : 1;\n this.lastPress = { node, x, y, at: now, count };\n\n // Pressing text ends editing, so an editable's selection does not\n // stay lit beside this one.\n this.host.blurEditable();\n this.order = selectableTextNodes(this.host.root());\n this.dragging = true;\n\n if (modifiers.shift && this.anchor !== null) {\n this.focus = { node, offset };\n } else if (count === 2) {\n const word = wordRangeIn(geometry, offset);\n this.anchor = { node, offset: word.start };\n this.focus = { node, offset: word.end };\n } else if (count >= 3) {\n this.anchor = { node, offset: lineStartAt(geometry.text, offset) };\n this.focus = { node, offset: lineEndAt(geometry.text, offset) };\n } else {\n this.anchor = { node, offset };\n this.focus = { node, offset };\n }\n this.apply();\n return true;\n }\n\n /**\n * A drag. The focus follows the pointer across node boundaries, which\n * is why this hit-tests rather than using the pressed node: the\n * pointer controller captures a press to its target, and a selection\n * is the one gesture that has to leave it.\n */\n pointerMove(x: number, y: number): void {\n if (\n this.pendingLink !== null &&\n (Math.abs(x - this.pendingLink.x) > MULTI_CLICK_SLOP || Math.abs(y - this.pendingLink.y) > MULTI_CLICK_SLOP)\n ) {\n this.pendingLink = null;\n }\n if (!this.dragging || this.anchor === null) {\n return;\n }\n const point = this.pointAt(x, y);\n if (point === null) {\n return;\n }\n if (this.focus !== null && this.focus.node === point.node && this.focus.offset === point.offset) {\n return;\n }\n this.focus = point;\n this.apply();\n }\n\n pointerUp(): void {\n this.dragging = false;\n const pending = this.pendingLink;\n this.pendingLink = null;\n if (pending === null) {\n return;\n }\n // A press that selected something was a drag, however short.\n if (this.anchor !== null && this.focus !== null && this.anchor.offset !== this.focus.offset) {\n return;\n }\n linkOf(pending.node, pending.index)?.onClick?.();\n }\n\n /**\n * The pointer moved with nothing pressed.\n *\n * Lights the link run under it, which is what draws the wash and the\n * underline and what `resolveCursor` reads to show a pointer. The\n * state is written onto the node, as a selection and a find match\n * are, because paint has the node and nothing else.\n */\n pointerHover(node: UiNode | null, x: number, y: number): void {\n let target: UiNode | null = null;\n let index = -1;\n if (node !== null && hasTextLinks(node)) {\n const local = this.hitTester.toLocal(node, x, y);\n const at = linkRunAtPointIn(this.geometryOf(node), local.x, local.y);\n if (linkOf(node, at) !== undefined) {\n target = node;\n index = at;\n }\n }\n if (this.hoveredLink !== null && this.hoveredLink !== target && clearLinkHover(this.hoveredLink)) {\n this.host.markDirty(this.hoveredLink, DirtyFlags.Paint);\n }\n this.hoveredLink = target;\n if (target !== null && setLinkHover(target, index)) {\n this.host.markDirty(target, DirtyFlags.Paint);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Keyboard\n // ---------------------------------------------------------------------------\n\n /**\n * Default keyboard behaviour when no editable has focus: copy the\n * selection, select everything, or clear it. Returns true when the\n * key was consumed.\n */\n handleKey(key: string, modifiers: UiKeyModifiers): boolean {\n const primary = this.platform === 'mac' ? modifiers.meta : modifiers.ctrl;\n if (primary && (key === 'c' || key === 'C')) {\n const text = this.selectedText();\n if (text.length === 0) {\n return false;\n }\n this.host.copy(text);\n return true;\n }\n if (primary && (key === 'a' || key === 'A')) {\n return this.selectAll();\n }\n if (key === 'Escape' && this.hasSelection) {\n this.clear();\n return true;\n }\n return false;\n }\n\n /** Selects every selectable text node under the root, in document order. */\n selectAll(): boolean {\n const order = selectableTextNodes(this.host.root());\n if (order.length === 0) {\n return false;\n }\n const first = order[0];\n const last = order[order.length - 1];\n this.order = order;\n this.anchor = { node: first, offset: 0 };\n this.focus = { node: last, offset: this.geometryOf(last).end };\n this.apply();\n return true;\n }\n\n /**\n * Selects a range of one node's text, as if it had been dragged.\n * Used by find to light the active match: it is a real selection, so\n * copy takes it and it looks like every other one.\n */\n selectRange(node: UiNode, start: number, end: number): boolean {\n if (selectableTextOf(node) === undefined) {\n return false;\n }\n this.order = selectableTextNodes(this.host.root());\n if (!this.order.includes(node)) {\n return false;\n }\n this.anchor = { node, offset: start };\n this.focus = { node, offset: end };\n this.lastPress = null;\n this.apply();\n return this.hasSelection;\n }\n\n // ---------------------------------------------------------------------------\n // State\n // ---------------------------------------------------------------------------\n\n /**\n * The selected text, node by node in document order, one newline\n * between nodes — the paragraph breaks a reader would expect when\n * pasting a heading and the text under it.\n */\n selectedText(): string {\n const parts: string[] = [];\n for (const node of this.painted) {\n const range = selectionRangeOf(node);\n const text = selectableTextOf(node);\n if (range === undefined || text === undefined) {\n continue;\n }\n parts.push(text.slice(range.start, range.end));\n }\n return parts.join('\\n');\n }\n\n /** Drops the selection and repaints whatever was carrying it. */\n clear(): void {\n this.anchor = null;\n this.focus = null;\n this.dragging = false;\n if (this.painted.length === 0) {\n return;\n }\n for (const node of this.painted) {\n if (clearSelectionRange(node)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n this.painted = [];\n }\n\n /**\n * A node left the tree. Its range went with it, so the rest of the\n * selection no longer means anything: virtualization and route\n * changes both take nodes out from under a live selection.\n */\n handleNodeRemoved(node: UiNode): void {\n if (this.hoveredLink === node) {\n this.hoveredLink = null;\n }\n if (this.pendingLink?.node === node) {\n this.pendingLink = null;\n }\n if (this.painted.includes(node) || this.anchor?.node === node || this.focus?.node === node) {\n this.clear();\n }\n }\n\n // ---------------------------------------------------------------------------\n // Internals\n // ---------------------------------------------------------------------------\n\n /**\n * The text position under a canvas point: the offset in the node hit,\n * or — when the pointer is over a gap, a margin or another widget —\n * the nearest position in the node whose visible box is closest. A\n * drag that leaves the text it started in keeps selecting, as it does\n * everywhere else.\n */\n private pointAt(x: number, y: number): TextPoint | null {\n const hit = this.hitTester.hitTest(x, y);\n if (hit !== null && this.isSelectable(hit.node)) {\n const geometry = this.geometryOf(hit.node);\n if (hasDrawnText(geometry)) {\n const local = this.hitTester.toLocal(hit.node, x, y);\n return { node: hit.node, offset: offsetAtPointIn(geometry, local.x, local.y) };\n }\n }\n const nearest = this.nearestNode(x, y);\n if (nearest === null) {\n return null;\n }\n const geometry = this.geometryOf(nearest);\n if (!hasDrawnText(geometry)) {\n return null;\n }\n const local = this.hitTester.toLocal(nearest, x, y);\n return { node: nearest, offset: offsetAtPointIn(geometry, local.x, local.y) };\n }\n\n /** The selectable node whose visible box is closest to a canvas point. */\n private nearestNode(x: number, y: number): UiNode | null {\n let best: UiNode | null = null;\n let bestDistance = Infinity;\n for (const node of this.order) {\n if (this.host.recordFor(node) === undefined) {\n continue;\n }\n const box = this.host.visibleBox(node);\n const dx = Math.max(box.x - x, 0, x - (box.x + box.width));\n const dy = Math.max(box.y - y, 0, y - (box.y + box.height));\n // Vertical distance dominates: a point beside a line belongs to\n // that line, not to a nearer one on the row above.\n const distance = dy * dy * 4 + dx * dx;\n if (distance < bestDistance) {\n bestDistance = distance;\n best = node;\n }\n }\n return best;\n }\n\n /**\n * Resolves the two ends into a range per node and writes them where\n * paint will find them.\n */\n private apply(): void {\n const anchor = this.anchor;\n const focus = this.focus;\n if (anchor === null || focus === null) {\n this.clear();\n return;\n }\n const anchorIndex = this.order.indexOf(anchor.node);\n const focusIndex = this.order.indexOf(focus.node);\n if (anchorIndex < 0 || focusIndex < 0) {\n // One end is no longer in the tree; nothing sensible spans them.\n this.clear();\n return;\n }\n const forward = anchorIndex < focusIndex || (anchorIndex === focusIndex && anchor.offset <= focus.offset);\n const from = forward ? anchor : focus;\n const to = forward ? focus : anchor;\n const first = Math.min(anchorIndex, focusIndex);\n const last = Math.max(anchorIndex, focusIndex);\n\n const next: UiNode[] = [];\n for (let i = first; i <= last; i++) {\n const node = this.order[i];\n const end = this.geometryOf(node).end;\n const start = i === first ? Math.min(from.offset, end) : 0;\n const stop = i === last ? Math.min(to.offset, end) : end;\n if (stop <= start) {\n continue;\n }\n if (setSelectionRange(node, start, stop)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n next.push(node);\n }\n for (const node of this.painted) {\n if (!next.includes(node) && clearSelectionRange(node)) {\n this.host.markDirty(node, DirtyFlags.Paint);\n }\n }\n this.painted = next;\n }\n\n /**\n * The node's text laid out in its own coordinates: the content box\n * starts at the padding, as it does for the renderers in the parent's\n * space. Before the first layout the record is empty and there is\n * nothing drawn to select.\n */\n private geometryOf(node: UiNode): ParagraphGeometry {\n const rec = this.host.recordFor(node);\n const state = resolvePaintState(node, this.paint);\n const box: LayoutBox =\n rec === undefined\n ? { x: 0, y: 0, width: 0, height: 0 }\n : {\n x: rec.paddingLeft,\n y: rec.paddingTop,\n width: Math.max(0, rec.width - rec.paddingLeft - rec.paddingRight),\n height: Math.max(0, rec.height - rec.paddingTop - rec.paddingBottom)\n };\n return paragraphGeometry(state.text ?? '', box, state, this.host.measurer);\n }\n}\n","import type { UiNode } from '../graph/UiNode';\nimport { UiNodeType } from '../graph/UiNodeType';\nimport type { UiRole, UiSemanticState, UiSemanticStates, UiLiveRegion } from '../properties/UiSemantics';\nimport { normalizeStates } from '../properties/UiSemantics';\nimport type { UiResolvedTextSpan, UiTextLink } from '../properties/UiTextStyle';\nimport { resolvedSpansOf, textContentOf } from '../properties/UiTextStyle';\n\n/**\n * One node of the semantics tree, flattened.\n *\n * Flat rather than nested because the consumer is a diff: a record\n * carries its parent and its position among its semantic siblings, so\n * two frames' maps can be compared key by key without walking two\n * trees in step. `parent` is null for a root-level record.\n *\n * Geometry is deliberately absent. A record changes when what the node\n * *means* changes; where it sits changes on every scrolled frame, and\n * F6b's mirror can read a box from the layout engine by id when it\n * needs one. Keeping them apart is what makes \"no semantics changed\"\n * a cheap and true statement.\n */\nexport interface UiSemanticsRecord {\n readonly id: string;\n readonly parent: string | null;\n /** Position among the records sharing this parent. */\n readonly index: number;\n readonly role?: UiRole;\n /** The accessible name: an explicit `label`, else the node's text. */\n readonly label?: string;\n readonly description?: string;\n /** Announced on change without focus; see `UiProperties.live`. */\n readonly live?: UiLiveRegion;\n /** Sorted and de-duplicated, so declaration order does not diff. */\n readonly states?: readonly UiSemanticState[];\n readonly disabled?: true;\n readonly valueNow?: number;\n readonly valueMin?: number;\n readonly valueMax?: number;\n readonly valueText?: string;\n readonly posInSet?: number;\n readonly setSize?: number;\n /** How deep a treeitem sits, 1 for a root. */\n readonly level?: number;\n}\n\n/** Insertion-ordered: iterating the map walks the tree in document order. */\nexport type UiSemanticsMap = ReadonlyMap<string, UiSemanticsRecord>;\n\n/**\n * The roles a node type carries without being asked.\n *\n * A `Button` is a button whether or not the component that built it\n * said so — which is what makes \"every component emits semantics\"\n * achievable rather than aspirational. Everything else states its own\n * role or is transparent.\n */\nconst IMPLICIT_ROLES: Partial<Record<UiNodeType, UiRole>> = {\n [UiNodeType.Button]: 'button',\n [UiNodeType.EditableText]: 'textbox'\n};\n\n/**\n * Roles whose descendants say nothing of their own.\n *\n * ARIA calls these \"children presentational\": the text inside a button\n * is how the button is named, not a paragraph next to it, and a screen\n * reader that announced both would say everything twice. The list is\n * ARIA's, narrowed to the roles `UiRole` has, plus `menuitem`: ARIA\n * names a menu item from its contents but stops short of marking its\n * children presentational, and the only difference that makes here is\n * a row whose label is on the tree twice.\n *\n * The other roles ARIA names from their contents are deliberately out.\n * A `listitem`, a `cell` or a `row` holds content rather than being\n * spelled by it, and a button inside one has to stay a record of its\n * own.\n */\nconst PRESENTATIONAL_CHILDREN: ReadonlySet<UiRole> = new Set<UiRole>([\n 'button',\n 'checkbox',\n 'image',\n 'menuitem',\n 'menuitemcheckbox',\n 'menuitemradio',\n 'option',\n 'progressbar',\n 'radio',\n 'separator',\n 'slider',\n 'switch',\n 'tab'\n]);\n\n/**\n * Builds the semantics tree under `root`.\n *\n * Three rules, in order:\n *\n * 1. A node is a **semantics node** when it declares a `role` or a\n * `label`, or its type has an implicit role, or it is a `Text`\n * with text that no ancestor has claimed as its name. Everything\n * else is transparent: its children attach to the nearest\n * semantics ancestor, so wrapping a button in three boxes for\n * layout does not put three nodes in front of a screen reader.\n * 2. A semantics node's name is its `label` if it has one, else the\n * text it and its non-semantic descendants draw. Text used that\n * way is *claimed*: it does not also become a record, so a\n * `Button(Text('Save'))` is one node named \"Save\", not two.\n * 3. An invisible subtree is not in the tree at all. A disabled one\n * is, carrying `disabled` — unavailable is a thing to announce,\n * hidden is not.\n */\nexport function buildSemanticsTree(root: UiNode): Map<string, UiSemanticsRecord> {\n return walk(root, null, false);\n}\n\n/**\n * The records one subtree contributes, as if walked in place.\n *\n * For re-describing what changed without rebuilding the whole tree.\n * `node` keeps the `parent` and `index` its existing record has, and\n * everything below it is walked exactly as `buildSemanticsTree` would:\n * a descendant's index is counted among the semantic children of\n * whatever inside this subtree claims it, so the numbering is the same\n * one a full walk produces.\n *\n * **That only holds when `node` itself carries a record.** A\n * transparent node's children attach to its nearest semantic ancestor\n * and are numbered among that ancestor's *other* children too — nodes\n * outside this subtree — so a walk that started here would number them\n * from zero and disagree with the tree. Null says so, and the caller\n * has to rebuild the whole tree instead.\n *\n * `inert` is whether an ancestor above this subtree is disabled, which\n * only the caller can know.\n */\nexport function buildSemanticsSubtree(\n node: UiNode,\n parent: string | null,\n index: number,\n inert: boolean\n): Map<string, UiSemanticsRecord> | null {\n const records = walk(node, parent, inert);\n const own = records.get(node.id);\n if (own === undefined) {\n return null;\n }\n // The walk numbered it from zero, knowing nothing of its siblings.\n records.set(node.id, { ...own, index });\n return records;\n}\n\nfunction walk(root: UiNode, rootParent: string | null, rootInert: boolean): Map<string, UiSemanticsRecord> {\n const records = new Map<string, UiSemanticsRecord>();\n const childCounts = new Map<string | null, number>();\n\n const nextIndex = (parent: string | null): number => {\n const index = childCounts.get(parent) ?? 0;\n childCounts.set(parent, index + 1);\n return index;\n };\n\n const visit = (node: UiNode, parent: string | null, inert: boolean): void => {\n if (node.properties.get('visible') === false) {\n return;\n }\n const disabled = inert || node.properties.get('disabled') === true;\n const record = describe(node, parent, disabled, nextIndex);\n if (record === null) {\n for (let child = node.firstChild; child !== null; child = child.nextSibling) {\n visit(child, parent, disabled);\n }\n return;\n }\n const runs = linkRunsOf(node);\n if (runs.length > 0) {\n // A paragraph with links is mirrored run by run: the prose\n // between the links and each link itself become records of their\n // own, in reading order, and the paragraph keeps no name. A\n // screen reader then reaches the link the way it reaches one in\n // a page, rather than hearing its words go by inside a sentence\n // it cannot act on.\n records.set(record.id, paragraphOf(record));\n appendTextRuns(node, record.id, runs, records);\n } else {\n records.set(record.id, record);\n }\n for (let child = node.firstChild; child !== null; child = child.nextSibling) {\n if (claimedByName(node, child)) {\n continue;\n }\n visit(child, record.id, disabled);\n }\n };\n\n visit(root, rootParent, rootInert);\n return records;\n}\n\n/**\n * Whether an ancestor of `node` is disabled, which every record in its\n * subtree inherits.\n *\n * A full walk carries this down; a subtree walk has to look it up.\n */\nexport function semanticsInertAbove(node: UiNode): boolean {\n for (let current = node.parent; current !== null; current = current.parent) {\n if (current.properties.get('disabled') === true) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * The record for a node, or null when the node is transparent.\n */\nfunction describe(\n node: UiNode,\n parent: string | null,\n disabled: boolean,\n nextIndex: (parent: string | null) => number\n): UiSemanticsRecord | null {\n const role = (node.properties.get('role') as UiRole | undefined) ?? IMPLICIT_ROLES[node.type as UiNodeType];\n const declaredLabel = node.properties.get('label') as string | undefined;\n const isText = node.type === UiNodeType.Text;\n // Decided before the name is gathered, and that order is the whole\n // cost of this walk.\n //\n // `accessibleText` recurses through every descendant that names its\n // parent and joins what it finds, so on a plain container it reads\n // the entire subtree and allocates the joined string. A node with no\n // role, no label and no text of its own is transparent *whatever*\n // that string says — so computing it first meant every box in the\n // tree gathered the text of everything beneath it and then threw the\n // answer away. On the benchmark list that was the root joining two\n // thousand titles, each row joining its own, and so on down: the\n // walk was quadratic in the depth of transparent wrappers, and\n // rebuilding the tree for one changed label cost 3.2 ms.\n //\n // Asking the cheap question first — three property reads and a type\n // check — leaves the name to the nodes that can actually carry one.\n if (role === undefined && declaredLabel === undefined && !isText) {\n return null;\n }\n const name = declaredLabel ?? accessibleText(node);\n // Prose with nothing else to say is still a record: a screen reader\n // has to be able to read the page, not only operate it. A Text node\n // with no text left is not prose, and says nothing.\n if (role === undefined && declaredLabel === undefined && name === undefined) {\n return null;\n }\n const states = node.properties.get('states') as UiSemanticStates | undefined;\n return prune({\n id: node.id,\n parent,\n index: nextIndex(parent),\n role,\n label: name,\n description: node.properties.get('description') as string | undefined,\n live: node.properties.get('live') as UiLiveRegion | undefined,\n states: states === undefined || states.length === 0 ? undefined : normalizeStates(states),\n disabled: disabled ? true : undefined,\n valueNow: node.properties.get('valueNow') as number | undefined,\n valueMin: node.properties.get('valueMin') as number | undefined,\n valueMax: node.properties.get('valueMax') as number | undefined,\n valueText: valueTextOf(node),\n posInSet: node.properties.get('posInSet') as number | undefined,\n setSize: node.properties.get('setSize') as number | undefined,\n level: node.properties.get('level') as number | undefined\n });\n}\n\n/**\n * An editable's content is its value, not its name, and it is held on\n * the node as `value` — the property the app sets and `onInput`\n * reports. An explicit `valueText` overrides it.\n */\nfunction valueTextOf(node: UiNode): string | undefined {\n const declared = node.properties.get('valueText') as string | undefined;\n if (declared !== undefined) {\n return declared;\n }\n if (node.type === UiNodeType.EditableText) {\n return (node.properties.get('value') as string | undefined) ?? '';\n }\n return undefined;\n}\n\n/**\n * The text a node draws, including the text of transparent\n * descendants, joined by spaces. Stops at a descendant that is a\n * semantics node of its own, which has its own name to say.\n */\nfunction accessibleText(node: UiNode): string | undefined {\n if (node.type === UiNodeType.EditableText) {\n return undefined;\n }\n const parts: string[] = [];\n const own = textContentOf(node);\n if (own.length > 0) {\n parts.push(own);\n }\n for (let child = node.firstChild; child !== null; child = child.nextSibling) {\n if (!namesItsParent(child)) {\n continue;\n }\n const text = accessibleText(child);\n if (text !== undefined) {\n parts.push(text);\n }\n }\n const joined = parts.join(' ').trim();\n return joined.length > 0 ? joined : undefined;\n}\n\n/**\n * Whether a child's text belongs to its parent's name rather than to\n * a record of its own: it must be visible and say nothing itself.\n */\nfunction namesItsParent(child: UiNode): boolean {\n if (child.properties.get('visible') === false) {\n return false;\n }\n if (child.properties.get('role') !== undefined || child.properties.get('label') !== undefined) {\n return false;\n }\n return IMPLICIT_ROLES[child.type as UiNodeType] === undefined;\n}\n\n/**\n * Whether a child is already spoken for by its parent.\n *\n * Either because the parent's name was built from its text, or because\n * the parent's role makes its whole subtree presentational — a labelled\n * button still hides the glyph inside it. A labelled *container*\n * (a form, a list, a dialog) claims nothing: its label names it, and\n * its children are the content that label introduces.\n */\nfunction claimedByName(parent: UiNode, child: UiNode): boolean {\n const role = (parent.properties.get('role') as UiRole | undefined) ?? IMPLICIT_ROLES[parent.type as UiNodeType];\n if (role !== undefined && PRESENTATIONAL_CHILDREN.has(role)) {\n return true;\n }\n if (parent.properties.get('label') !== undefined) {\n return false;\n }\n return namesItsParent(child) && accessibleText(child) !== undefined;\n}\n\n/** Drops the undefined members, so records compare and serialise small. */\nfunction prune(record: UiSemanticsRecord): UiSemanticsRecord {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(record)) {\n if (value !== undefined) {\n result[key] = value;\n }\n }\n return result as unknown as UiSemanticsRecord;\n}\n\n// ---------------------------------------------------------------------------\n// Runs\n// ---------------------------------------------------------------------------\n\n/**\n * Separates a run record's id from its node's.\n *\n * A run is not a node and has no id of its own, so it borrows its\n * paragraph's and adds its position. Nothing generates a node id\n * containing this character, and the shape is what lets the framework\n * route an action on a run back to the paragraph that owns it.\n */\nexport const TEXT_RUN_ID_SEPARATOR = '#run';\n\n/** The paragraph a run record belongs to, and which run it is; null for any other id. */\nexport function textRunOfRecordId(id: string): { nodeId: string; index: number } | null {\n const at = id.lastIndexOf(TEXT_RUN_ID_SEPARATOR);\n if (at < 0) {\n return null;\n }\n const index = Number(id.slice(at + TEXT_RUN_ID_SEPARATOR.length));\n return Number.isInteger(index) && index >= 0 ? { nodeId: id.slice(0, at), index } : null;\n}\n\n/** The runs of a node's paragraph that are links; empty when none are. */\nfunction linkRunsOf(node: UiNode): readonly UiResolvedTextSpan[] {\n const spans = resolvedSpansOf(node);\n for (const span of spans) {\n if (span.link !== undefined) {\n return spans;\n }\n }\n return EMPTY_SPANS;\n}\n\nconst EMPTY_SPANS: readonly UiResolvedTextSpan[] = [];\n\n/**\n * The paragraph a run-mirrored node becomes: a `paragraph` unless it\n * said what it was, and named by what it contains rather than by a\n * label, so its text is not read twice.\n */\nfunction paragraphOf(record: UiSemanticsRecord): UiSemanticsRecord {\n const { label: _label, ...rest } = record;\n return { ...rest, role: record.role ?? 'paragraph' };\n}\n\n/**\n * One record per stretch of the paragraph, in reading order: prose\n * where no link covers it, a `link` where one does.\n *\n * A link's name is its `label` when it has one, because \"here\" and\n * \"read more\" are links a screen reader user cannot tell apart, and\n * its text otherwise.\n */\nfunction appendTextRuns(\n node: UiNode,\n parent: string,\n spans: readonly UiResolvedTextSpan[],\n records: Map<string, UiSemanticsRecord>\n): void {\n const text = textContentOf(node);\n let index = 0;\n const push = (from: number, to: number, link: UiTextLink | undefined): void => {\n const value = text.slice(from, to);\n if (link === undefined && value.trim().length === 0) {\n // Whitespace between two links is not something to announce.\n return;\n }\n const id = `${node.id}${TEXT_RUN_ID_SEPARATOR}${index}`;\n records.set(id, {\n id,\n parent,\n index,\n role: link === undefined ? undefined : 'link',\n label: link?.label ?? value\n });\n index++;\n };\n let at = 0;\n for (const span of spans) {\n if (span.link === undefined) {\n continue;\n }\n push(at, span.start, undefined);\n push(span.start, span.end, span.link);\n at = span.end;\n }\n push(at, text.length, undefined);\n}\n","import type { UiSemanticsMap, UiSemanticsRecord } from './UiSemanticsTree';\n\n/**\n * What changed in the semantics tree between two frames.\n *\n * `add` and `update` carry the whole record rather than a delta: the\n * records are small, the consumer is a DOM mirror that sets attributes\n * from them, and a whole record cannot be applied out of order into\n * something half-updated. `remove` carries only the id, since a\n * removed subtree's records are each removed in their own patch.\n */\nexport type UiSemanticsPatch =\n | { readonly op: 'add'; readonly node: UiSemanticsRecord }\n | { readonly op: 'update'; readonly node: UiSemanticsRecord }\n | { readonly op: 'remove'; readonly id: string };\n\n/**\n * Diffs two semantics maps.\n *\n * Removals come first so a consumer never holds two records claiming\n * the same parent and index; adds and updates follow in document\n * order, which is the order `buildSemanticsTree` produced them in, so\n * a mirror can append as it reads.\n */\nexport function diffSemantics(previous: UiSemanticsMap, next: UiSemanticsMap): UiSemanticsPatch[] {\n const patches: UiSemanticsPatch[] = [];\n for (const id of previous.keys()) {\n if (!next.has(id)) {\n patches.push({ op: 'remove', id });\n }\n }\n for (const [id, record] of next) {\n const before = previous.get(id);\n if (before === undefined) {\n patches.push({ op: 'add', node: record });\n continue;\n }\n if (!recordsEqual(before, record)) {\n patches.push({ op: 'update', node: record });\n }\n }\n return patches;\n}\n\n/** Structural equality over the record's flat, pruned members. */\nexport function recordsEqual(a: UiSemanticsRecord, b: UiSemanticsRecord): boolean {\n const aKeys = Object.keys(a);\n const bKeys = Object.keys(b);\n if (aKeys.length !== bKeys.length) {\n return false;\n }\n for (const key of aKeys) {\n const left = (a as unknown as Record<string, unknown>)[key];\n const right = (b as unknown as Record<string, unknown>)[key];\n if (key === 'states') {\n const leftStates = left as readonly string[];\n const rightStates = right as readonly string[] | undefined;\n if (\n rightStates === undefined ||\n leftStates.length !== rightStates.length ||\n leftStates.some((state, index) => state !== rightStates[index])\n ) {\n return false;\n }\n continue;\n }\n if (!Object.is(left, right)) {\n return false;\n }\n }\n return true;\n}\n"],"mappings":"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"}