gesso-framework 0.7.0 → 0.8.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-DlhL5y0d.js","names":[],"sources":["../src/Component.ts","../src/service/ServiceRegistry.ts","../src/bounds.ts","../src/liveInput.ts","../src/decorators.ts","../src/channel/ChannelReplica.ts","../src/channel/ChannelRegistry.ts","../src/FunctionComponent.ts","../src/overlay/OverlayService.ts","../src/app/FocusService.ts","../src/overlay/OverlayLayer.ts","../src/startingInputsKey.ts","../src/ComponentHost.ts","../src/ComponentHostResolver.ts","../src/app/worker/RenderWorkerProtocol.ts","../src/app/ShellService.ts","../src/app/FrameService.ts","../src/app/DevtoolsProtocol.ts","../src/app/AudioService.ts","../src/router/RouteState.ts","../src/router/RoutePath.ts","../src/router/RouterService.ts","../src/app/FindService.ts","../src/app/EditingService.ts","../src/app/ScrollService.ts","../src/app/MediaService.ts","../src/app/FontService.ts","../src/app/TextService.ts","../src/app/AnimationService.ts","../src/app/InspectionService.ts","../src/app/InputLatency.ts","../src/app/SmoothScroller.ts","../src/app/GessoRuntime.ts","../src/app/claimedErrors.ts","../src/app/renderFrame.ts","../src/agent/render.ts"],"sourcesContent":["import type { UiChild } from 'gesso-core';\n\n/**\n * Base class for all Gesso framework components.\n *\n * Components are class-based. They declare reactive state with\n * an `internalState()` cell, inputs with `@Input()`, and return a UiElement tree\n * from `render()`. The framework calls `render()` once per mount;\n * after that, observable emissions in the returned tree drive updates.\n */\nexport abstract class Component {\n /**\n * Called once after the component's UiNode subtree has been created\n * and all observable bindings are connected.\n */\n onMount?(): void;\n\n /**\n * Called once when the component is being removed from the graph.\n */\n onUnmount?(): void;\n\n /**\n * Returns the component's UI definition.\n *\n * Called exactly once per component instance. Dynamic content is\n * expressed through Observable props and children, never by\n * re-invoking render().\n *\n * Returning an Observable is allowed for structural changes that\n * cannot be expressed as observable children (routing, for example).\n * The framework reconciles the component's root on each emission.\n */\n abstract render(): UiChild;\n}\n","/**\n * The runtime services a component may inject.\n *\n * A service is a plain class the runtime constructs once and hands to\n * whoever asks: the clipboard and window (`ShellService`), overlays,\n * focus, find, media, animation. They are not application state and\n * they never cross a thread — they hold a `UiNode`, an `ImageResolver`,\n * decoded bitmaps, a focus manager — so there is no replica, no patch\n * and no wire format anywhere near them.\n *\n * That is the whole difference from what used to be here. Application\n * state crosses the barrier and is declared as a channel; a service\n * stays on the render thread and is simply called. Both used to be\n * `Store`, and the single name hid which of the two rules applied.\n */\nexport class ServiceRegistry {\n private readonly services = new Map<Function, object>();\n private readonly parent: ServiceRegistry | undefined;\n\n /**\n * @param parent Where `get` looks for a service this registry does not\n * hold. A runtime drawing a frame off screen (`renderFrame`) gets one\n * of these over the application's registry: the services the\n * application registered are found there, and the runtime's own,\n * which it registers wherever `has` says none is, are its own.\n */\n constructor(parent?: ServiceRegistry) {\n this.parent = parent;\n }\n\n /** Constructs and registers a service, once. */\n register<T extends object>(ServiceClass: new () => T): T {\n if (this.services.has(ServiceClass)) {\n throw new Error(`Service '${ServiceClass.name}' is already registered.`);\n }\n const service = new ServiceClass();\n this.services.set(ServiceClass, service);\n return service;\n }\n\n /**\n * Points an existing service at a replacement class of the same\n * name, keeping the instance (the HMR).\n *\n * A registry is keyed by the class object, which is the right key\n * for every purpose but one: replacing a module produces a new class\n * object, so a component from the replaced module injects a class\n * the registry has never seen even though a service of that name is\n * sitting in it. Re-keying is what lets the tree be rebuilt from new\n * code while the service it depends on carries on holding what it\n * was holding.\n *\n * **The instance keeps the behaviour it was constructed with.** It\n * was built from the old class and its methods are the old code, so\n * a change to a service's own body needs a full reload to take\n * effect. State survives; behaviour does not. That is the trade\n * every hot-replacement system makes, and pretending otherwise would\n * be worse than saying it.\n *\n * Returns false when nothing of that name was registered, which\n * means the caller is looking at a genuinely new service rather than\n * a replacement.\n */\n adopt(ServiceClass: new () => object): boolean {\n if (this.services.has(ServiceClass)) {\n return true;\n }\n const matches = [...this.services.keys()].filter(key => (key as { name?: string }).name === ServiceClass.name);\n if (matches.length === 0) {\n return false;\n }\n if (matches.length > 1) {\n throw new Error(\n `Cannot adopt a replacement for '${ServiceClass.name}': ${matches.length} registered services share that name.`\n );\n }\n const previous = matches[0];\n const service = this.services.get(previous);\n this.services.delete(previous);\n this.services.set(ServiceClass, service as object);\n return true;\n }\n\n get<T extends object>(ServiceClass: new () => T): T {\n const service = this.services.get(ServiceClass);\n if (service === undefined && this.parent?.has(ServiceClass) === true) {\n return this.parent.get(ServiceClass);\n }\n if (service === undefined) {\n const names = [...this.services.keys()]\n .map(key => (key as { name?: string }).name ?? '?')\n .sort()\n .join(', ');\n // A same-named class that is a different object is nearly always\n // a hot replacement, and the plain message for it reads as a\n // contradiction: the service is right there in the list.\n const replaced = [...this.services.keys()].some(key => (key as { name?: string }).name === ServiceClass.name);\n throw new Error(\n `Service '${ServiceClass.name}' is not registered. ` +\n `Registered services: ${names.length > 0 ? names : '(none)'}.` +\n (replaced\n ? ` A different class of that name is registered, which usually means the module defining it was ` +\n `hot-replaced. Pass the replacement to reload() so the registry can adopt it.`\n : '')\n );\n }\n return service as T;\n }\n\n /** Whether this registry holds the service: not its parent, which `get` also asks. */\n has(ServiceClass: Function): boolean {\n return this.services.has(ServiceClass);\n }\n}\n","import type { Subject } from 'rxjs';\n\nimport { measure, type LayoutBox, type UiModifier } from 'gesso-core';\nimport { InternalState } from './InternalState';\n\n/** A box that has not been laid out yet: the value before the first frame. */\nconst NOWHERE: LayoutBox = { x: 0, y: 0, width: 0, height: 0 };\n\n/**\n * A node's box, as a cell.\n *\n * `ctx.bounds()` hands one out and the modifier on it fills it, so the\n * arithmetic a pointer position needs starts from a value rather than\n * from `new BehaviorSubject<LayoutBox>({ x: 0, y: 0, width: 0, height: 0 })`\n * and a `measure(subject)` beside it. It is an ordinary cell: read it\n * with `.value` in a handler, bind it, or derive from it.\n *\n * const track = ctx.bounds();\n * <box modifiers={[track.modifier]} onPointerMove={e => at(e.x - track.value.x)} />\n *\n * **It reports a move, and only a move.** `measure` is built on\n * `LayoutNotifier`, which fires for every layout fact a node's listeners\n * could care about, and one of those is a scroll offset that changed\n * while the box stayed exactly where it was. A\n * cell that emitted for those would wake everything derived from it on\n * every frame of every scroll, for a box that did not move, which is\n * the defect this exists to avoid. So an equal box is dropped here,\n * where the comparison is four numbers, rather than by a\n * `distinctUntilChanged` at each of the places that read it.\n *\n * There is no `ctx.bounds(ref)`. A box is reported by a modifier, which\n * is how a node is reached from outside the layout engine everywhere\n * else in this framework, and a ref would be a second way of naming the\n * same node with nothing else built on it.\n */\nexport class BoundsCell extends InternalState<LayoutBox> {\n /** Put this on the element whose box the cell should hold. */\n readonly modifier: UiModifier<Subject<LayoutBox>>;\n\n constructor() {\n super(NOWHERE);\n this.modifier = measure(this);\n }\n\n /** Writes a box, unless it is the one already held. */\n override next(box: LayoutBox): void {\n const held = super.getValue();\n if (held.x === box.x && held.y === box.y && held.width === box.width && held.height === box.height) {\n return;\n }\n super.next(box);\n }\n}\n\n/**\n * A bounds cell outside a component, for a class component or a test.\n * Inside a function component `ctx.bounds()` is the same thing, and it\n * completes with the component.\n */\nexport function bounds(label?: string): BoundsCell {\n const cell = new BoundsCell();\n if (label !== undefined) {\n cell.label = label;\n }\n return cell;\n}\n","/** Where the component whose body is running keeps its live inputs, while it runs. */\nlet target: Map<string, () => unknown> | null = null;\n\n/** Runs a component's body with its live inputs going to `into`; see `liveInput`. */\nexport function withLiveInputs<T>(into: Map<string, () => unknown>, run: () => T): T {\n const previous = target;\n target = into;\n try {\n return run();\n } finally {\n target = previous;\n }\n}\n\n/**\n * Says what an input of the component whose body is running holds now,\n * as the component keeps it, and how to read it again: a switch turned\n * on, a field typed in, a list scrolled to 600. The inspector reports it\n * on the component's owner report (`live`), and a design tool saves a\n * moment of a played screen from it, to start the screen there again\n * (`StartingInputs`). `controlled` calls it for its value, so a control\n * written by its contract reports itself; outside a component's body it\n * does nothing.\n */\nexport function liveInput(name: string, read: () => unknown): void {\n target?.set(name, read);\n}\n","import { getComponentMetadata } from './metadata';\n\n/**\n * Declares a class as a Gesso component.\n *\n * The tag is used for debugging and identity; it does not create\n * a DOM element.\n */\nexport function Define(tag: string): ClassDecorator {\n return target => {\n const metadata = getComponentMetadata(target as unknown as new () => unknown);\n metadata.tag = tag;\n };\n}\n\n/**\n * Declares a property as a component input.\n *\n * Input values are supplied by the parent through createComponent().\n */\nexport function Input(): PropertyDecorator {\n return (target, propertyKey) => {\n const constructor = target.constructor as unknown as new () => unknown;\n const metadata = getComponentMetadata(constructor);\n metadata.inputs.add(propertyKey as string);\n };\n}\n\n/**\n * Declares a property as an injected runtime service.\n *\n * The service must be registered with useService(), or be one of\n * the six a runtime registers itself.\n */\n/**\n * Marks a class component's output field:\n *\n * @Output() changed = output<[value: number]>();\n *\n * Wired exactly as an input is, because it is one: the cell holds the\n * handler the parent passed, and `this.changed.emit(next)` calls it.\n * The decorator exists so a reader can tell the two directions apart.\n */\nexport function Output(): PropertyDecorator {\n return (target, propertyKey) => {\n const constructor = target.constructor as unknown as new () => unknown;\n const metadata = getComponentMetadata(constructor);\n metadata.inputs.add(propertyKey as string);\n };\n}\n\nexport function Inject<T extends Function>(StoreClass: T): PropertyDecorator {\n return (target, propertyKey) => {\n const constructor = target.constructor as unknown as new () => unknown;\n const metadata = getComponentMetadata(constructor);\n metadata.injects.set(propertyKey as string, StoreClass);\n };\n}\n\n/**\n * Declares a property as a channel from across the barrier.\n *\n * The property becomes the channel's replica: `view` keys to read or\n * bind, and `send` to issue a command. It is the class counterpart of\n * `ctx.channel(token)`.\n *\n * @Channel(Catalog) catalog!: ChannelReplica<CatalogView, CatalogCommands>;\n */\nexport function Channel(token: { name: string }): PropertyDecorator {\n return (target, propertyKey) => {\n const constructor = target.constructor as unknown as new () => unknown;\n const metadata = getComponentMetadata(constructor);\n metadata.channels.set(propertyKey as string, token);\n };\n}\n","import { InputCell } from '../Input';\nimport { applyPatches, type Patch } from './StorePatch';\nimport { isChannelHostMessage, type ChannelClientMessage, type ChannelPort } from './ChannelProtocol';\nimport { viewKeys, type ChannelToken } from './ChannelToken';\nimport { isPortErrorMessage } from '../worker/WorkerPorts';\n\n/**\n * The render thread's end of a channel.\n *\n * It runs none of the application's logic. It holds the latest value\n * of each view key and forwards commands, and that asymmetry is the\n * point — the work stays on the thread that owns the data.\n *\n * Every key is an `InputCell`: a component reads it, binds it, and\n * cannot write it, which is the same contract a prop has. Whether a\n * value arrived from a parent or from across the barrier makes no\n * difference to the component that reads it, so it is not worth a\n * second name.\n */\nexport class ChannelReplica<View extends object, Commands extends object> {\n private readonly cells = new Map<string, InputCell<unknown>>();\n private readonly commandProxy: Commands;\n private errorListener: ((message: string, stack?: string) => void) | null = null;\n private pending: Patch[] | null = null;\n private scheduleFlush: (() => void) | null = null;\n\n constructor(\n private readonly token: ChannelToken<View, Commands>,\n private readonly port: ChannelPort\n ) {\n for (const key of viewKeys(token)) {\n // Seeded from the token, so nothing ever observes `undefined`\n // for a declared key. The provider starts from the same value,\n // so an application already in its initial state sends nothing.\n const cell = new InputCell((token.initial as Record<string, unknown>)[key]);\n cell.label = `${token.name}.${key}`;\n this.cells.set(key, cell);\n }\n this.commandProxy = this.createCommandProxy();\n this.port.onmessage = event => this.receive(event.data);\n // Asking rather than waiting to be pushed to keeps the two ends\n // independent of which finished starting up first.\n this.post({ type: 'channel:sync' });\n }\n\n /** The view keys, each an `InputCell` to read or bind. */\n get view(): { readonly [K in keyof View]: InputCell<View[K]> } {\n return this.viewProxy as { readonly [K in keyof View]: InputCell<View[K]> };\n }\n\n /**\n * The channel's commands, typed as declared.\n *\n * Fire and forget: the effect comes back as a patch, never a return\n * value. There is no synchronous answer across a thread, and\n * pretending otherwise would invite code that cannot work.\n */\n get send(): Commands {\n return this.commandProxy;\n }\n\n private readonly viewProxy = new Proxy({} as Record<string, InputCell<unknown>>, {\n get: (_target, property): unknown => {\n if (typeof property !== 'string') {\n return undefined;\n }\n const cell = this.cells.get(property);\n if (cell === undefined) {\n const names = [...this.cells.keys()].sort().join(', ');\n throw new Error(\n `'${property}' is not a view key on channel '${this.token.name}'. ` +\n `Declared keys: ${names.length > 0 ? names : '(none)'}.`\n );\n }\n return cell;\n },\n // Enumerable, so `Object.keys(view)` and a spread list the declared\n // keys. With only `get`, both silently found nothing: an app that\n // snapshotted a view by walking its keys got an empty object.\n has: (_target, property) => typeof property === 'string' && this.cells.has(property),\n ownKeys: () => [...this.cells.keys()],\n getOwnPropertyDescriptor: (_target, property) => {\n const cell = typeof property === 'string' ? this.cells.get(property) : undefined;\n return cell === undefined ? undefined : { value: cell, enumerable: true, configurable: true, writable: false };\n }\n });\n\n private createCommandProxy(): Commands {\n return new Proxy({} as Commands, {\n get: (_target, property): unknown => {\n if (typeof property !== 'string') {\n return undefined;\n }\n return (...args: unknown[]) => {\n // Every argument crosses. A second one used to be dropped on\n // the floor with a warning, so `move(from, to)` had to be\n // written `move({ from, to })` and taken apart again on the\n // other side; the first still travels as `payload` and the\n // rest beside it, which is what keeps the two ends able to\n // be different builds.\n this.post(\n args.length > 1\n ? { type: 'channel:command', command: property, payload: args[0], rest: args.slice(1) }\n : { type: 'channel:command', command: property, payload: args[0] }\n );\n };\n }\n }) as Commands;\n }\n\n /** Receives errors reported by the thread that owns the channel. */\n onError(listener: ((message: string, stack?: string) => void) | null): void {\n this.errorListener = listener;\n }\n\n private receive(data: unknown): void {\n if (isPortErrorMessage(data)) {\n // Nothing on the other thread serves this channel's name.\n // Reported like any other channel error, because from here it is\n // one: no patch will ever arrive.\n this.report(data.message);\n return;\n }\n if (!isChannelHostMessage(data)) {\n return;\n }\n if (data.type === 'channel:error') {\n this.report(data.message, data.stack);\n return;\n }\n if (this.pending === null) {\n this.applyPatches(data.patches);\n return;\n }\n this.pending.push(...data.patches);\n this.scheduleFlush?.();\n }\n\n private report(message: string, stack?: string): void {\n const listener =\n this.errorListener ?? ((text, trace) => console.error(`[gesso channel ${this.token.name}] ${text}`, trace));\n listener(message, stack);\n }\n\n /**\n * Defers patch application to the next frame.\n *\n * A chatty application thread can deliver many patches between two\n * frames. Applied on arrival each one pushes a value through the\n * bindings watching it, rebuilding a subtree once per patch when\n * only the last state is ever drawn. Queued, a burst costs one pass.\n */\n deferPatches(scheduleFlush: () => void): void {\n this.scheduleFlush = scheduleFlush;\n this.pending = [];\n }\n\n get hasPendingPatches(): boolean {\n return this.pending !== null && this.pending.length > 0;\n }\n\n flush(): void {\n if (this.pending === null || this.pending.length === 0) {\n return;\n }\n const batch = this.pending;\n this.pending = [];\n this.applyPatches(batch);\n }\n\n /**\n * Applies a batch, emitting once per affected key.\n *\n * Grouping matters: a batch touching one key three times must not\n * push three values through the bindings watching it. And every key's\n * patches are applied before any cell is told, so a batch that fails\n * part way tells nobody, rather than leaving the screen showing the\n * first keys of a change and not the rest.\n */\n applyPatches(patches: readonly Patch[]): void {\n const byKey = new Map<string, Patch[]>();\n for (const patch of patches) {\n const existing = byKey.get(patch.projection);\n if (existing === undefined) {\n byKey.set(patch.projection, [patch]);\n } else {\n existing.push(patch);\n }\n }\n const cells: InputCell<unknown>[] = [];\n const values: unknown[] = [];\n for (const [key, group] of byKey) {\n const cell = this.cells.get(key);\n if (cell === undefined) {\n // A key this build does not know about. Ignoring it lets a\n // newer application thread talk to an older view.\n continue;\n }\n cells.push(cell);\n values.push(applyPatches(cell.value, group));\n }\n for (let index = 0; index < cells.length; index++) {\n cells[index].next(values[index]);\n }\n }\n\n private post(message: ChannelClientMessage): void {\n this.port.postMessage(message);\n }\n\n dispose(): void {\n this.port.onmessage = null;\n }\n}\n","import { ChannelReplica } from './ChannelReplica';\nimport type { ChannelPort } from './ChannelProtocol';\nimport type { ChannelToken, CommandMap } from './ChannelToken';\n\n/**\n * The channels a runtime can hand to its components, by name.\n *\n * A replica is the only thing kept: whether the data lives in another\n * worker or on this very thread is settled by the port it was attached\n * with, and nothing above here can tell.\n */\nexport class ChannelRegistry {\n private readonly replicas = new Map<string, ChannelReplica<object, CommandMap>>();\n\n /** Attaches a channel over `port`. */\n attach<View extends object, Commands extends object>(\n token: ChannelToken<View, Commands>,\n port: ChannelPort\n ): ChannelReplica<View, Commands> {\n if (this.replicas.has(token.name)) {\n throw new Error(`Channel '${token.name}' is already attached.`);\n }\n const replica = new ChannelReplica(token, port);\n this.replicas.set(token.name, replica as unknown as ChannelReplica<object, CommandMap>);\n return replica;\n }\n\n get<View extends object, Commands extends object>(\n token: ChannelToken<View, Commands>\n ): ChannelReplica<View, Commands> {\n const replica = this.replicas.get(token.name);\n if (replica === undefined) {\n const names = [...this.replicas.keys()].sort().join(', ');\n throw new Error(\n `Channel '${token.name}' is not attached. Did you forget useChannel(...)? ` +\n `Attached channels: ${names.length > 0 ? names : '(none)'}.`\n );\n }\n return replica as unknown as ChannelReplica<View, Commands>;\n }\n\n has(token: ChannelToken<object, CommandMap>): boolean {\n return this.replicas.has(token.name);\n }\n\n /** Every attached replica, for frame-aligned patch flushing. */\n all(): ChannelReplica<object, CommandMap>[] {\n return [...this.replicas.values()];\n }\n\n dispose(): void {\n for (const replica of this.replicas.values()) {\n replica.dispose();\n }\n this.replicas.clear();\n }\n}\n","import type { Observable, Subscription } from 'rxjs';\n\nimport type { Reactive, UiChild, UiEnvironmentKey } from 'gesso-core';\nimport type { BoundsCell } from './bounds';\nimport { Component } from './Component';\nimport type { InputCell, OutputTarget } from './Input';\nimport type { ChannelReplica } from './channel/ChannelReplica';\nimport type { ChannelToken } from './channel/ChannelToken';\n\n/**\n * What a functional component can ask of the framework while its body\n * runs. It is the function's half of what `@Inject`, `onMount()` and\n * `onUnmount()` give a class.\n */\nexport interface ComponentContext {\n /**\n * The runtime service of this class: overlays, focus, find, the\n * clipboard, media, animation.\n *\n * Services stay on this thread and are simply called. Application\n * state comes through `channel` instead.\n */\n inject<S extends object>(ServiceClass: new () => S): S;\n\n /**\n * The channel declared by `token`: `view` keys to read or bind, and\n * `send` to issue a command.\n */\n channel<V extends object, C extends object>(token: ChannelToken<V, C>): ChannelReplica<V, C>;\n\n /**\n * Runs once after the component's nodes exist and its bindings are\n * connected. Must be called while the component function runs.\n */\n onMount(hook: () => void): void;\n\n /**\n * Runs once when the component leaves the tree, before its\n * subscriptions are torn down. Must be called while the component\n * function runs.\n */\n onUnmount(hook: () => void): void;\n\n /**\n * Follows a stream for as long as the component is in the tree.\n *\n * A component that has to *act* on a value rather than draw it,\n * telling the audio element to load a track, asking a channel for the\n * page a url names, writing a scroll offset somewhere, subscribes, and\n * something has to unsubscribe. Every screen in both applications\n * wrote that pair by hand, and one of them had grown a `Subscription`\n * bag to hold four of them.\n *\n * ctx.effect(queue.view.current, track => audio.load(track.stream));\n *\n * The subscription is the host's and is torn down with the component,\n * after `onUnmount` has run, in the order the host tears down every\n * subscription it opened on the component's behalf. It is handed back\n * for the rare case that wants to stop early; nothing has to hold it.\n *\n * Unlike `onMount` and `onUnmount` this may be called after the body,\n * from a callback the component registered, since what it registers\n * is a teardown rather than a hook that has already been run past.\n */\n effect<T>(source: Observable<T>, run: (value: T) => void): Subscription;\n\n /**\n * A cell holding a node's box, with the modifier that fills it.\n *\n * Turning a pointer position into a fraction of a track, or a drag\n * into a seek, starts with knowing where the element is, and until\n * now that meant declaring `new BehaviorSubject<LayoutBox>` with a\n * zero box in it and passing it to `measure`. This is that, named,\n * and it drops a report of a box that has not moved.\n *\n * const track = ctx.bounds();\n * <box modifiers={[track.modifier]} onPointerDown={e => seek(e.x - track.value.x)} />\n */\n bounds(label?: string): BoundsCell;\n\n /**\n * What the component is placed under for `key`: the nearest value a\n * node above it provides (`provide={[provided(key, value)]}`), else\n * the key's default.\n *\n * Read as the body runs, from where the component is placed, so it\n * is a value rather than a stream: a component that has to follow a\n * change is given a source to follow (a cell) rather than each value.\n * A design tool tells the screen it plays from the same screen drawn\n * on its board this way.\n */\n environment<T>(key: UiEnvironmentKey<T>): T;\n}\n\n/**\n * The inputs a functional component receives: one host-owned cell per\n * declared member, and for a member typed as a function, an output.\n *\n * The parent supplies values or Observables; the host feeds them into\n * these cells and keeps feeding them when the parent's values change,\n * so the function can run exactly once, like a class `render()`, and\n * still follow its parent. An output is fired with\n * `inputs.onChange.emit(next)`, and a parent may pass a handler or\n * `into(subject)` for it.\n *\n * Every declared input is a cell, whether or not the parent passed\n * anything: the record hands one out on first access and the host keeps\n * feeding it, so an optional input that was omitted is a live cell\n * holding `undefined`, and it takes a value if the parent starts\n * passing one. What it is *not* is a value, so binding it straight to a\n * property writes `undefined` there and the property draws as though it\n * had never been set. `input(inputs.name, fallback)` gives it a\n * default, and `select(inputs.name, ...)` projects one.\n * `Input.optional.spec.ts` is that case written down.\n */\nexport type Inputs<P> = {\n readonly [K in keyof P]-?: InputCell<InputValue<P[K]>>;\n};\n\n/**\n * What an input cell holds for a member declared as `P[K]`.\n *\n * The host subscribes to an Observable a parent passes and feeds the\n * cell its values, so a cell never holds an Observable. A member\n * declared as `Observable<T>` therefore holds `T`, and saying so here\n * turns `inputs.items.value.pipe(...)`, which used to compile and then\n * throw \"pipe is not a function\" on mount, into a type error. Declare\n * the member as `T`: a parent may pass a `T` or an `Observable<T>`\n * either way.\n *\n * Only a member that is nothing but an Observable (or one that may be\n * left out) is unwrapped. A union that mixes values and streams, such\n * as `UiChild` or `Reactive<T>`, is left as it was declared.\n */\nexport type InputValue<V> = [Exclude<V, undefined>] extends [Observable<infer T>] ? T | Extract<V, undefined> : V;\n\n/**\n * A component written as a function.\n *\n * function Counter(inputs: Inputs<{ label?: string }>, ctx: ComponentContext) {\n * const label = input(inputs.label, 'Count');\n * const count = state(0);\n * const store = ctx.inject(DemoStore);\n * return Row(Text({ text: label }), Button({ onClick: () => count.value++ }));\n * }\n *\n * The body is the component's `render()`: it runs once per instance,\n * and everything dynamic in the returned tree is an Observable. Local\n * state is `internalState()` cells created in the body.\n */\nexport type FunctionComponent<P = {}> = (inputs: Inputs<P>, context: ComponentContext) => UiChild;\n\nexport type ClassComponent = new () => Component;\n\n/**\n * Anything `createComponent` (and JSX) can mount. The function half is\n * loose on purpose: a function's own `Inputs<P>` parameter is what\n * `ComponentProps` reads its props from.\n */\nexport type ComponentType = ClassComponent | ((inputs: any, context: ComponentContext) => UiChild);\n\ntype IsAny<T> = 0 extends 1 & T ? true : false;\n\ntype CellValue<C> = C extends InputCell<infer T> ? T : never;\n\n/** The keys of a cell record whose value may be undefined; the parent may omit those. */\ntype OptionalCellKeys<I> = {\n [K in keyof I]: undefined extends CellValue<I[K]> ? K : never;\n}[keyof I];\n\n/**\n * The props a parent may pass for a record of input cells: each one a\n * value or an Observable of it. A cell that admits `undefined` is\n * optional; every other one is required.\n */\ntype PropsForCells<I> = [keyof I] extends [never]\n ? NoProps\n : {\n [K in OptionalCellKeys<I>]?: Passable<CellValue<I[K]>>;\n } & {\n [K in Exclude<keyof I, OptionalCellKeys<I>>]: Passable<CellValue<I[K]>>;\n };\n\n/**\n * What a parent may pass for one cell: a value or an Observable of it,\n * and for an output, the handler itself or an `into(subject)` target\n * that receives what the child emits.\n */\ntype Passable<T> =\n NonNullable<T> extends (first: infer V, ...rest: never[]) => void ? Reactive<T> | OutputTarget<V> : Reactive<T>;\n\n/**\n * A component that declares no props accepts none: `{}` passes, anything\n * else is an excess property error. A bare `{}` type would accept\n * anything, and an index signature would swallow JSX's `key`, so this\n * is an object type with one optional phantom member that can never be\n * set.\n */\ntype NoProps = { readonly __noProps?: never };\n\n/** The `input()` fields of a class component, as the props its parent may pass. */\ntype InputFields<I> = {\n [K in keyof I as I[K] extends InputCell<any> ? K : never]: I[K];\n};\n\n/**\n * The props a parent passes to a component.\n *\n * For a class, its `@Input() x = input(default)` fields, all optional\n * because each has a default. For a function, the cells of its first\n * parameter. A misspelled prop is an excess property error; a prop\n * whose cell holds a `string` rejects a `number` or an\n * `Observable<number>`.\n */\nexport type ComponentProps<C> = C extends ClassComponent\n ? Partial<PropsForCells<InputFields<InstanceType<C>>>>\n : C extends (inputs: infer I, ...rest: any[]) => UiChild\n ? IsAny<I> extends true\n ? Record<string, unknown>\n : unknown extends I\n ? NoProps\n : [I] extends [undefined]\n ? NoProps\n : PropsForCells<I>\n : never;\n\ntype RequiredKeys<T> = {\n [K in keyof T]-?: {} extends Pick<T, K> ? never : K;\n}[keyof T];\n\n/**\n * The trailing arguments of `createComponent`: props may be omitted\n * only when the component requires none of them.\n */\nexport type ComponentArgs<C> =\n RequiredKeys<ComponentProps<C>> extends never\n ? [inputs?: ComponentProps<C>, key?: string | number]\n : [inputs: ComponentProps<C>, key?: string | number];\n\n/**\n * Whether a component is a class (extends Component) rather than a\n * function. Arrow functions have no prototype; a plain function's\n * prototype is not a Component.\n */\nexport function isClassComponent(component: ComponentType): component is ClassComponent {\n const prototype = (component as { prototype?: unknown }).prototype;\n return prototype instanceof Component;\n}\n\n/** A value a parent passed for a prop, as the cell will hold it. */\nexport type Unwrapped<T> = T extends Observable<infer V> ? V : T;\n","import { createEnvironmentKey, type Reactive, type UiChild, type UiNode } from 'gesso-core';\nimport { internalState } from '../InternalState';\n\n/**\n * The side of the anchor an entry opens on and, after a dash, the edge\n * of the anchor it lines up with along that side; the bare side\n * centres.\n *\n * **`start` and `end` are the reading edges.** Along the top or the\n * bottom, `-start` lines the entry up with the anchor's start edge,\n * which is its left in left-to-right text and its right in\n * right-to-left text: a list under a field begins where the field's\n * text does. As a side, `start` and `end` open beside the anchor\n * toward those edges, so a submenu given `end-start` opens past the\n * end of its row whichever way the row reads. Along the left or right\n * side the alignment is up and down, which no reading direction turns.\n *\n * `left` and `right` stay physical, for a side that is genuinely one\n * edge of the screen whatever the language: an arrow drawn on a\n * diagram, a tooltip on a panel that is always on the left.\n *\n * `over` lays the entry over the anchor at its size, grown by the\n * offset on every side: the hole a spotlight leaves around what it\n * shows. No direction turns it.\n *\n * The direction is the entry's: the one its content reads in, which is\n * where its theme comes from too. See `OverlayEntry.environment`.\n */\nexport type OverlayPlacement =\n | 'over'\n | 'top'\n | 'top-start'\n | 'top-end'\n | 'bottom'\n | 'bottom-start'\n | 'bottom-end'\n | 'left'\n | 'left-start'\n | 'left-end'\n | 'right'\n | 'right-start'\n | 'right-end'\n | 'start'\n | 'start-start'\n | 'start-end'\n | 'end'\n | 'end-start'\n | 'end-end';\n\n/** A placement the layout engine takes: the side named physically, and the alignment along top and bottom too. */\nexport type OverlayPhysicalPlacement = Exclude<OverlayPlacement, `start${string}` | `end${string}`>;\n\n/**\n * A placement turned into the physical one the layout engine places by,\n * in a reading direction.\n *\n * The engine knows only screen edges: `bottom-start` to it is the\n * anchor's left edge. Resolving here, rather than teaching the engine\n * directions, keeps the engine's `placement` the plain property it is\n * for any absolutely positioned node, and puts the decision where the\n * direction is known: the overlay layer reads it from the tree that\n * declared the entry.\n */\nexport function physicalPlacement(placement: OverlayPlacement, rtl: boolean): OverlayPhysicalPlacement {\n if (placement === 'over') {\n return placement;\n }\n const [side, align] = placement.split('-') as [string, string | undefined];\n if (side === 'top' || side === 'bottom') {\n if (align === undefined || !rtl) {\n return placement as OverlayPhysicalPlacement;\n }\n return `${side}-${align === 'start' ? 'end' : 'start'}`;\n }\n if (side === 'left' || side === 'right') {\n return placement as OverlayPhysicalPlacement;\n }\n const physical = (side === 'start') !== rtl ? 'left' : 'right';\n return align === undefined ? physical : `${physical}-${align as 'start' | 'end'}`;\n}\n\n/** A rectangle inside an anchor, from its border box's top left. */\nexport interface OverlayRect {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\n/**\n * One thing floating above the app: a menu, a tooltip, a dialog.\n *\n * Anchored entries are placed beside `anchor` (a UiNode obtained from a\n * `ref` prop) by the layout engine, which flips them to the other side\n * when they would overflow and shifts them to stay on screen; they\n * follow the anchor when it scrolls. Unanchored entries use the edge\n * offsets, all relative to the viewport, or `center` for the axes that\n * should stay in the middle of it instead.\n */\nexport interface OverlayEntry {\n /** Stable identity: opening an id that is already open replaces it. */\n readonly id: string;\n readonly content: UiChild;\n readonly anchor?: UiNode | null;\n /**\n * The part of `anchor` to open beside, in the anchor's own\n * coordinates: a character in a field, from\n * `EditingService.caretRectOf(field, offset)`. The entry follows the\n * anchor as it would without it, through scrolling and layout. It can\n * be a stream, so a list under a word being typed moves with the word\n * when it wraps to the next line, without being opened again.\n */\n readonly anchorRect?: Reactive<OverlayRect | undefined>;\n /** Default 'bottom'. `start` and `end` follow the reading direction; see `OverlayPlacement`. */\n readonly placement?: OverlayPlacement;\n /** Gap between content and anchor. */\n readonly offset?: number;\n readonly top?: number;\n readonly right?: number;\n readonly bottom?: number;\n readonly left?: number;\n /**\n * The offset from the viewport's start edge: its left in\n * left-to-right text and its right in right-to-left text, in the\n * entry's direction. For something pinned to a corner a reader looks\n * for in a particular place, as a toast is. It wins over the physical\n * offset on the same edge.\n */\n readonly start?: number;\n /** The offset from the viewport's end edge, as `start` is from its start. */\n readonly end?: number;\n /**\n * A point to open beside, relative to the viewport: where a context\n * menu was asked for. Placed as an anchor of no size would be, with\n * `placement`, so an entry that would run off an edge flips or shifts\n * to stay on screen — which `top` and `left` do not do.\n */\n readonly point?: { readonly x: number; readonly y: number };\n /**\n * Centre an unanchored entry in the viewport along an axis.\n *\n * The edges of that axis stop being a position and become the region\n * to centre within — unset they are the whole viewport, so a dialog\n * asking for `'x'` sits in the middle however wide the window is, and\n * one asking for `'both'` sits in the middle of the screen. The other\n * axis is untouched: `center: 'x'` with `bottom: 24` is a toast\n * centred along the bottom edge.\n *\n * Ignored on an anchored entry, which is placed beside its anchor.\n */\n readonly center?: 'x' | 'y' | 'both';\n /**\n * Close the entry when the pointer goes down, or the wheel turns,\n * anywhere outside it. A backdrop takes those events, so nothing\n * underneath scrolls while the entry is open. Without it the entry\n * stays open and follows its anchor through scrolling.\n */\n readonly dismissOnOutsidePress?: boolean;\n /**\n * Put the backdrop underneath even when a press outside doesn't close\n * the entry, as a modal does: everything beneath it, positioned\n * panels with a zIndex included, is drawn under the backdrop and takes\n * no press or wheel through it, as the page under a browser's modal\n * `<dialog>` is inert. Without it, an entry that doesn't close on an\n * outside press has no backdrop, and a press beside it reaches the\n * page.\n */\n readonly modal?: boolean;\n /**\n * Dim the page under the backdrop with the theme's `scrim` colour, as\n * a browser draws `<dialog>::backdrop`. It fades in as the entry\n * opens, or appears at once under reduced motion.\n *\n * On by default for a modal entry, and only there: a menu or a list\n * of suggestions closes on a press outside it without saying the page\n * can't be used. False keeps a modal's backdrop clear.\n */\n readonly scrim?: boolean;\n /** Order among open entries; later entries paint on top by default. */\n readonly zIndex?: number;\n /**\n * A node whose environment the content should inherit.\n *\n * The layer is mounted above the app root, so an entry's content is\n * nowhere near the tree that opened it and inherits none of its\n * scoped values: a menu opened inside a dark-themed panel would come\n * out light. Passing a node from that tree — a trigger, or the\n * placeholder the component left where it was declared — carries the\n * theme across.\n */\n readonly environment?: UiNode | null;\n readonly onClose?: () => void;\n}\n\n/**\n * The open overlays, as a local store.\n *\n * Every runtime registers one. Components inject it and open or close\n * entries through actions; the OverlayLayer the runtime mounts above\n * the app root renders whatever is open. Being a store keeps the\n * framework's one rule intact — components never mutate shared state\n * directly — and gives devtools a log of what opened when.\n *\n * It must stay on the render thread: entries hold UiElements and\n * UiNodes, which never cross a worker boundary.\n */\nexport class OverlayService {\n readonly entries = internalState<readonly OverlayEntry[]>([]);\n open(entry: OverlayEntry): void {\n const others = this.entries.value.filter(existing => existing.id !== entry.id);\n this.entries.value = [...others, entry];\n }\n close(id: string): void {\n const closing = this.entries.value.find(entry => entry.id === id);\n if (closing === undefined) {\n return;\n }\n this.entries.value = this.entries.value.filter(entry => entry !== closing);\n closing.onClose?.();\n }\n closeAll(): void {\n const closing = this.entries.value;\n if (closing.length === 0) {\n return;\n }\n this.entries.value = [];\n for (const entry of closing) {\n entry.onClose?.();\n }\n }\n\n isOpen(id: string): boolean {\n return this.entries.value.some(entry => entry.id === id);\n }\n}\n\n/**\n * The overlays of the `OverlayScope` a node is in: where an entry\n * opened there goes instead of the runtime's layer. Null outside every\n * scope.\n */\nexport const OverlayScopeKey = createEnvironmentKey<OverlayService | null>({\n name: 'gesso.overlayScope',\n defaultValue: null\n});\n","import type { FocusOptions, UiNode, UiFocusManager } from 'gesso-core';\nimport { internalState } from '../InternalState';\n\n/**\n * Keyboard focus, as a store components can inject.\n *\n * `UiFocusManager` lives in the runtime and a component cannot reach\n * it — components reach the world through stores, as they do for the\n * clipboard (`ShellService`), overlays (`OverlayService`) and find\n * (`FindService`). Without this a component cannot autofocus a field,\n * trap the keyboard in a dialog, or put the caret in the input that\n * failed validation.\n *\n * Nodes come from a `ref` prop. A tree's refs fire before the runtime\n * installs the manager, so an action taken during the first build is\n * queued and replayed once there is one; that is the same problem\n * `FindService` solves for its query field, and the reason `autoFocus`\n * in a dialog works on the frame it mounts.\n *\n * It must stay on the render thread: its actions take `UiNode`s, which\n * never cross a worker boundary.\n */\n/** The nodes a trap taken inside is modal only within: each `OverlayScope`'s. */\nconst focusBounds = new WeakSet<UiNode>();\n\n/** Makes a node one, for `OverlayScope`. */\nexport function boundFocus(node: UiNode): void {\n focusBounds.add(node);\n}\n\nexport class FocusService {\n /** The node holding focus, or null. A control binds its focus ring to this. */\n readonly focused = internalState<UiNode | null>(null);\n /** Whether focus is confined to a subtree by an open trap. */\n readonly trapped = internalState(false);\n /**\n * Whether the focus held is focus the keyboard can see: reached by Tab\n * or an arrow key, or by code with no pointer since, rather than by a\n * press. CSS's `:focus-visible`, for something other than a ring that\n * should answer the keyboard and not the click that happened to focus\n * the same control: a tooltip, say.\n */\n readonly focusVisible = internalState(false);\n\n private manager: UiFocusManager | null = null;\n private detach: (() => void) | null = null;\n /** Actions taken before the runtime installed a manager, in order. */\n private queued: ((manager: UiFocusManager) => void)[] = [];\n\n /** Installed by the runtime; without one every action is queued. */\n setManager(manager: UiFocusManager | null): void {\n this.detach?.();\n this.detach = null;\n this.manager = manager;\n if (manager === null) {\n this.queued = [];\n return;\n }\n // A change of modality alone — a key pressed on a control a click\n // focused — is reported as a focus change on the same node, so this\n // one listener keeps both cells right.\n const focusChange = manager.onFocusChange(node => {\n this.focused.value = node;\n this.focusVisible.value = manager.focusVisible;\n // A trap inside an `OverlayScope` holds only focus inside it.\n this.trapped.value = manager.trapped;\n });\n manager.setFocusBounds(node => focusBounds.has(node));\n const scopeChange = manager.onScopeChange(() => {\n this.trapped.value = manager.trapped;\n });\n this.detach = () => {\n focusChange();\n scopeChange();\n };\n const pending = this.queued;\n this.queued = [];\n for (const action of pending) {\n action(manager);\n }\n this.sync(manager);\n }\n\n /**\n * Gives the node keyboard focus. Non-focusable nodes are ignored.\n *\n * The node is scrolled into view, unless `options.preventScroll` asks\n * for the page to stay where it is, as `element.focus({ preventScroll:\n * true })` does in a browser.\n */\n focus(node: UiNode, options?: FocusOptions): void {\n this.run(manager => manager.focus(node, 'program', options));\n }\n\n /** Drops focus without moving it anywhere. */\n blur(): void {\n this.run(manager => manager.blur());\n }\n\n /** Moves focus to the next focusable node, wrapping around. */\n focusNext(): void {\n this.run(manager => manager.focusNext());\n }\n\n /** Moves focus to the previous focusable node, wrapping around. */\n focusPrevious(): void {\n this.run(manager => manager.focusPrevious());\n }\n\n /**\n * The tab stops under `within` (the whole tree when not given), in\n * the order Tab visits them: what a design tool numbers to show a\n * screen's keyboard order. Empty before the runtime has a manager.\n */\n tabStops(within?: UiNode): UiNode[] {\n return this.manager?.tabStops(within) ?? [];\n }\n\n /**\n * Confines focus to `scope` until `releaseTrap`, moving it inside if\n * it was elsewhere. Traps nest: a dialog opened over a dialog traps\n * again, and each release restores its own opener.\n */\n trap(scope: UiNode): void {\n this.run(manager => manager.pushScope(scope));\n }\n\n /**\n * Ends the innermost trap and returns focus to whatever held it when\n * the trap was taken — the button that opened the dialog. Given the\n * node trapped in, only that trap ends, and nothing when it ended\n * already: a trap's subtree removed releases it, and a release after\n * that would otherwise end the trap under it.\n */\n releaseTrap(scope?: UiNode): void {\n this.run(manager => manager.popScope(scope));\n }\n\n private run(action: (manager: UiFocusManager) => void): void {\n const manager = this.manager;\n if (manager === null) {\n this.queued.push(action);\n return;\n }\n action(manager);\n this.sync(manager);\n }\n\n private sync(manager: UiFocusManager): void {\n this.focused.value = manager.focusedNode;\n this.focusVisible.value = manager.focusVisible;\n this.trapped.value = manager.trapped;\n }\n}\n","import { map } from 'rxjs';\n\nimport {\n Box,\n defineModifier,\n fade,\n findEnvironmentKey,\n motion,\n provided,\n UiEnvironmentKeys,\n type UiEnvironment,\n type UiNode,\n type UiChild,\n type UiElement,\n type UiModifier,\n type UiProvision\n} from 'gesso-core';\nimport { Component } from '../Component';\nimport { Define, Inject, Input } from '../decorators';\nimport { input } from '../Input';\nimport { OverlayService, physicalPlacement, type OverlayEntry, type OverlayPhysicalPlacement } from './OverlayService';\n\n/**\n * The layer every runtime mounts above the app root.\n *\n * An absolutely positioned box covering the viewport, above everything\n * (zIndex 1000) but not itself hit-testable, so with nothing open it\n * is invisible to input. Each open entry becomes an absolutely\n * positioned box the layout engine places — beside its anchor, by its\n * edge offsets, or centred in the viewport — and, when the entry asks\n * for it, a full-size\n * backdrop beneath it that takes every press and wheel outside the\n * entry, and closes the entry on them when it is dismissible. A modal\n * entry's backdrop is dimmed with the theme's `scrim` unless it asks\n * for none.\n * Entries without a backdrop stay open and follow their anchor when\n * the content underneath scrolls.\n *\n * Entries render in store order; a later entry is on top unless it\n * sets zIndex.\n *\n * `start` and `end`, in a placement or as edge offsets, are resolved to\n * physical edges here, in the reading direction of the node the entry\n * takes its environment from, and resolved again if that direction\n * changes while the entry is open. See `OverlayEntry.environment` for\n * why that node and not always the anchor.\n */\n@Define('gesso-overlay-layer')\nexport class OverlayLayer extends Component {\n @Inject(OverlayService) runtimeOverlays!: OverlayService;\n /** The entries to draw, when not the runtime's: an `OverlayScope`'s. */\n @Input() service = input<OverlayService | null>(null);\n\n private get overlays(): OverlayService {\n return this.service.value ?? this.runtimeOverlays;\n }\n\n override render(): UiElement {\n return Box(\n { position: 'absolute', inset: 0, zIndex: 1000, hitTestable: false },\n // Bound straight to the service's cell: it is on this thread,\n // so there is nothing for a selector to abstract over.\n this.overlays.entries.pipe(map(entries => this.renderEntries(entries)))\n );\n }\n\n private renderEntries(entries: readonly OverlayEntry[]): UiElement[] {\n const elements: UiElement[] = [];\n for (const entry of entries) {\n const environment = entry.environment ?? entry.anchor ?? null;\n if (entry.dismissOnOutsidePress === true || entry.modal === true) {\n // A modal's backdrop is there whether or not it closes anything:\n // it is what keeps a press beside the dialog from reaching the\n // page under it. One that doesn't close takes the press and the\n // wheel and does nothing with them.\n const dismiss = entry.dismissOnOutsidePress === true;\n elements.push(\n Box(\n {\n key: `${entry.id}\\0backdrop`,\n position: 'absolute',\n inset: 0,\n zIndex: entry.zIndex,\n // The theme the entry's content has, so the scrim is the\n // colour of the palette the dialog was opened in.\n ...inheritedFrom(environment),\n modifiers: followEnvironment(environment, null),\n ...(dismiss\n ? {\n onPointerDown: () => this.overlays.close(entry.id),\n // A wheel over the backdrop is the user scrolling away;\n // the menu closes rather than swallowing the scroll.\n onWheel: () => this.overlays.close(entry.id)\n }\n : {})\n },\n ...((entry.scrim ?? entry.modal === true) ? [scrim()] : [])\n )\n );\n }\n elements.push(\n Box(\n {\n key: entry.id,\n position: 'absolute',\n anchor: entry.anchor ?? undefined,\n anchorPoint: entry.anchor == null ? entry.point : undefined,\n anchorRect: entry.anchor == null ? undefined : entry.anchorRect,\n anchorOffset: entry.offset,\n top: entry.top,\n bottom: entry.bottom,\n zIndex: entry.zIndex,\n // Laid over its anchor, the entry's box covers it; what it\n // draws takes presses, and the anchor under the rest of it\n // keeps its own, as a spotlight's target does.\n ...(entry.placement === 'over' && entry.anchor != null ? { hitTestable: false } : {}),\n ...centering(entry),\n ...physicalEdges(entry, readsRightToLeft(environment)),\n ...inheritedFrom(environment),\n modifiers: followEnvironment(environment, entry)\n },\n entry.content as UiChild\n )\n );\n }\n return elements;\n }\n}\n\n/**\n * The dim over the page, inside the backdrop rather than on it.\n *\n * A node at opacity 0 takes no presses, so a backdrop fading in would\n * let a press through to the page on the frame it opens. The fade is\n * this child's; the backdrop under it is solid to input from the start,\n * and the child takes none of its own. It comes in at the pace a\n * `Dialog` does, and under reduced motion it is simply there.\n */\nfunction scrim(): UiElement {\n return Box({\n position: 'absolute',\n inset: 0,\n hitTestable: false,\n backgroundColor: 'scrim',\n modifiers: [motion({ initial: fade, duration: 'slow', easing: 'decelerate' })]\n });\n}\n\n/**\n * `center` as the layout engine takes it.\n *\n * Both edges of the axis are pinned, which stretches the entry's box\n * across the viewport, and a Box is a Stack — so `x`/`y` centre the\n * content inside that span. The edges default to 0 but an entry that\n * gave one keeps it, which is what centres a toast horizontally\n * without lifting it off the bottom. The horizontal edges are\n * `physicalEdges`', since an entry may give them as `start` and `end`.\n *\n * The stretched box is `hitTestable: false`, since it is now much\n * bigger than what it holds: a press in the space beside a centred\n * dialog is a press outside the dialog, and has to reach the backdrop\n * underneath. Children are still tested, so the content itself keeps\n * taking its own presses.\n */\nfunction centering(entry: OverlayEntry): Record<string, unknown> {\n if (entry.center === undefined || (entry.anchor ?? null) !== null) {\n return {};\n }\n const x = entry.center === 'x' || entry.center === 'both';\n const y = entry.center === 'y' || entry.center === 'both';\n return {\n hitTestable: false,\n ...(x ? { x: 'center' } : {}),\n ...(y ? { top: entry.top ?? 0, bottom: entry.bottom ?? 0, y: 'center' } : {})\n };\n}\n\n/** Whether the scoped text style at a node reads right to left, as the entry's content will. */\nfunction readsRightToLeft(node: UiNode | null): boolean {\n return node?.environment?.get(UiEnvironmentKeys.textStyle).textDirection === 'rtl';\n}\n\n/**\n * The entry's placement and horizontal edges, with `start` and `end`\n * turned into the physical ones the layout engine takes.\n *\n * A logical edge wins over the physical one it lands on, so an entry\n * that gives both says which it meant. A centred entry's edges are the\n * span it centres in, and default to the viewport's.\n */\nfunction physicalEdges(\n entry: OverlayEntry,\n rtl: boolean\n): { placement: OverlayPhysicalPlacement | undefined; left: number | undefined; right: number | undefined } {\n let left = (rtl ? entry.end : entry.start) ?? entry.left;\n let right = (rtl ? entry.start : entry.end) ?? entry.right;\n if ((entry.center === 'x' || entry.center === 'both') && (entry.anchor ?? null) === null) {\n left ??= 0;\n right ??= 0;\n }\n return {\n placement: entry.placement === undefined ? undefined : physicalPlacement(entry.placement, rtl),\n left,\n right\n };\n}\n\n/**\n * The scoped values an entry's content should keep, re-provided on the\n * box that holds it, as they are when the entry opens.\n */\nfunction inheritedFrom(node: UiNode | null): Record<string, unknown> {\n const environment = node?.environment;\n if (environment === undefined || environment === null) {\n return {};\n }\n return {\n theme: environment.get(UiEnvironmentKeys.theme),\n textStyle: environment.get(UiEnvironmentKeys.textStyle),\n // As well as textStyle, which on its own would reset it to the\n // style's: a menu's plain text inherits the colour of where it was\n // declared.\n color: environment.get(UiEnvironmentKeys.color),\n contentColor: environment.get(UiEnvironmentKeys.contentColor),\n provide: providedFrom(environment)\n };\n}\n\n/** The built-in keys an entry's box carries by props of its own, or leaves to where it is drawn. */\nconst CARRIED_BY_PROPS = new Set<string>(\n Object.values(UiEnvironmentKeys)\n .map(key => key.name)\n .filter(name => name !== UiEnvironmentKeys.clock.name && name !== UiEnvironmentKeys.motion.name)\n);\n\n/**\n * The rest of what the entry was opened under, re-provided: the clock\n * its animations run on, so a dialog opened on a settled frame is at\n * rest there and one opened in a slowed player slows, the motion its\n * names mean, and every key the\n * application provides itself, so a component in a dialog reads with\n * `ctx.environment` what the one that opened it reads.\n */\nfunction providedFrom(environment: UiEnvironment): UiProvision[] {\n const out: UiProvision[] = [];\n const seen = new Set<string>();\n for (let at: UiEnvironment | null = environment; at !== null; at = at.parent) {\n for (const name of at.providedKeys()) {\n if (seen.has(name) || CARRIED_BY_PROPS.has(name)) {\n continue;\n }\n seen.add(name);\n const key = findEnvironmentKey(name);\n if (key !== undefined) {\n out.push(provided(key, environment.get(key)));\n }\n }\n }\n return out;\n}\n\n/** The node an entry's environment is read from, and the entry to place again when it changes (none for a backdrop). */\ninterface Followed {\n readonly from: UiNode;\n readonly entry: OverlayEntry | null;\n}\n\n/**\n * The same values again whenever they change where the entry was\n * declared, for as long as it is open.\n *\n * Reading them once let a dialog open while the page went dark (the\n * system's setting changed, or the theme a person chose arrived from\n * another worker a moment after they opened it) stay light over a dark\n * page until it was closed. The reading direction is one of them, and\n * the edges it decides are placed again with it.\n */\nconst followEnvironmentKind = defineModifier<Followed>({\n name: 'overlayEnvironment',\n attach(host, { from, entry }) {\n host.onEnvironment(() => {\n host.set('theme', host.environment(UiEnvironmentKeys.theme, from));\n const textStyle = host.environment(UiEnvironmentKeys.textStyle, from);\n host.set('textStyle', textStyle);\n host.set('color', host.environment(UiEnvironmentKeys.color, from));\n host.set('contentColor', host.environment(UiEnvironmentKeys.contentColor, from));\n if (from.environment !== null) {\n host.set('provide', providedFrom(from.environment));\n }\n // The direction comes with the text style, and an entry placed by\n // `start` or `end` is placed again by it: one opened before its\n // declaring tree was mounted read the default, left to right.\n if (entry !== null) {\n const edges = physicalEdges(entry, textStyle.textDirection === 'rtl');\n host.set('placement', edges.placement);\n host.set('left', edges.left);\n host.set('right', edges.right);\n }\n }, from);\n }\n});\n\nfunction followEnvironment(node: UiNode | null, entry: OverlayEntry | null): UiModifier<Followed>[] {\n return node === null ? [] : [followEnvironmentKind({ from: node, entry })];\n}\n","import { createEnvironmentKey, type UiEnvironmentKey, type UiNode } from 'gesso-core';\n\n/** One component's starting inputs: input names to values. */\nexport type StartingInputsRecord = Readonly<Record<string, unknown>>;\n\n/**\n * Starting inputs by component name: one record for every instance of\n * it, or a list for each in the order they are made (`null` for one\n * left as written), so the second of three switches can start on.\n */\nexport type StartingInputsByName = Readonly<\n Record<string, StartingInputsRecord | readonly (StartingInputsRecord | null)[]>\n>;\n\n/**\n * The inputs components under a node start with, by component name,\n * over what their parents pass; see `StartingInputs`. Empty outside one.\n */\nexport const StartingInputsKey: UiEnvironmentKey<StartingInputsByName> = createEnvironmentKey<StartingInputsByName>({\n name: 'gesso.startingInputs',\n defaultValue: {}\n});\n\n/** How many of each name have been made under each set of starting inputs, for a list's next. */\nconst made = new WeakMap<StartingInputsByName, Map<string, number>>();\n\n/** What a component named `name`, made at `anchor`, starts with: null for nothing. */\nexport function startingInputsFor(anchor: UiNode | null, name: string): StartingInputsRecord | null {\n for (let at: UiNode | null = anchor; at !== null; at = at.parent) {\n if (at.environment !== null) {\n const all = at.environment.get(StartingInputsKey);\n const given = all[name];\n if (given === undefined) {\n return null;\n }\n if (!Array.isArray(given)) {\n return given as StartingInputsRecord;\n }\n let counts = made.get(all);\n if (counts === undefined) {\n counts = new Map();\n made.set(all, counts);\n }\n const index = counts.get(name) ?? 0;\n counts.set(name, index + 1);\n return (given as readonly (StartingInputsRecord | null)[])[index] ?? null;\n }\n }\n return null;\n}\n","import { concat, of, skip, Subscription, type Observable } from 'rxjs';\nimport { bounds, type BoundsCell } from './bounds';\nimport { ChannelRegistry } from './channel/ChannelRegistry';\nimport { ServiceRegistry } from './service/ServiceRegistry';\nimport type { ChannelReplica } from './channel/ChannelReplica';\nimport type { ChannelToken, CommandMap } from './channel/ChannelToken';\n\nimport { isObservable, type UiChild, type UiEnvironmentKey, type UiNode } from 'gesso-core';\nimport { forceInput, InputCell, isOutputTarget, outputTargetOf, withBodyOf } from './Input';\nimport type { Component } from './Component';\nimport { type ComponentElement } from './ComponentElement';\nimport {\n type ClassComponent,\n type ComponentContext,\n type ComponentType,\n type FunctionComponent,\n isClassComponent\n} from './FunctionComponent';\nimport { getComponentMetadata } from './metadata';\nimport { withLiveInputs } from './liveInput';\nimport { startingInputsFor } from './startingInputsKey';\n\n/**\n * Owns a single component instance and its lifecycle.\n *\n * The host is responsible for:\n * - instantiating the component class, or preparing a function's\n * input cells and context\n * - feeding parent props into input cells\n * - resolving injected services\n * - validating that @State fields are initialized\n * - calling render() (or the function) exactly once and caching its output\n * - invoking onMount / onUnmount hooks\n * - owning subscriptions that must not outlive the component\n *\n * A host is created and released by the ComponentHostResolver, which\n * in turn is driven by graph reconciliation. The host never decides\n * when it lives or dies.\n *\n * A functional component is a class component with its `render()` in\n * the function and its inputs in the props record: the record hands\n * out one InputCell per prop name, created on first access, and the\n * host keeps every cell it handed out fed from the parent's props.\n */\nexport class ComponentHost<P extends Record<string, unknown> = Record<string, unknown>> {\n /** The class instance; undefined for a functional component. */\n readonly instance: Component | undefined;\n readonly element: ComponentElement<P>;\n\n /**\n * Subscriptions the framework opened on the component's behalf.\n *\n * Anything added here is torn down in dispose(), after onUnmount()\n * has had a chance to run against a still-live component.\n */\n readonly subscriptions = new Subscription();\n\n private mounted = false;\n private output: UiChild | undefined;\n\n /**\n * What the parent most recently supplied for each input, so an\n * unchanged Observable is not resubscribed on every reconcile.\n */\n private readonly inputSources = new Map<string, unknown>();\n\n /** Live subscription per Observable-valued input. */\n private readonly inputSubscriptions = new Map<string, Subscription>();\n\n /** A functional component's cells, by prop name, as they were handed out. */\n private readonly functionalCells = new Map<string, InputCell<unknown>>();\n\n /**\n * Inputs it is drawn with over its parent's, from a `StartingInputs`\n * above it when it was made: a moment of a screen, a tour's second\n * step. A preview goes over these, and ending one comes back to them.\n */\n private started: Record<string, unknown> | null;\n /** What the parent passed for each started input at birth: a new value from it takes over. */\n private readonly startedOver = new Map<string, unknown>();\n /** A started input the parent passes as a stream: the start, then the stream's changes; one per input, so it is not resubscribed. */\n private readonly startedStreams = new Map<string, Observable<unknown>>();\n /** What the component says it holds now, by input; see `liveInput`. */\n private readonly live = new Map<string, () => unknown>();\n\n /** Inputs a tool draws the component with over its parent's, until it ends them; see `previewInputs`. */\n private previewed: Readonly<Record<string, unknown>> | null = null;\n\n /** A class's input values from before they were previewed, for the ones its parent does not pass. */\n private readonly beforePreview = new Map<string, unknown>();\n private readonly mountHooks: Array<() => void> = [];\n private readonly unmountHooks: Array<() => void> = [];\n /** True only while the component function runs; hooks may register then. */\n private rendering = false;\n\n constructor(\n element: ComponentElement<P>,\n private readonly services: ServiceRegistry = new ServiceRegistry(),\n private readonly channels: ChannelRegistry = new ChannelRegistry(),\n /** The slot's node, under its parent, for `ctx.environment`; null for a host made outside a tree. */\n private readonly anchor: UiNode | null = null\n ) {\n this.element = element;\n const started = startingInputsFor(anchor, element.tag);\n this.started = started === null ? null : { ...started };\n for (const name of Object.keys(this.started ?? {})) {\n this.startedOver.set(name, (element.props as Record<string, unknown>)[name]);\n }\n if (isClassComponent(element.component)) {\n this.instance = new element.component();\n this.validateInputs();\n this.wireInputs();\n this.wireInjects();\n this.wireChannels();\n } else {\n this.instance = undefined;\n }\n }\n\n /** The class or function this host mounts. */\n get component(): ComponentType {\n return this.element.component;\n }\n\n /**\n * Returns what the component renders.\n *\n * render() is invoked exactly once per instance. Subsequent calls\n * return the cached output, because updates are expressed through\n * observable props and children rather than by re-rendering.\n */\n render(): UiChild {\n if (this.output === undefined) {\n this.output = this.instance !== undefined ? this.instance.render() : this.renderFunction();\n }\n return this.output;\n }\n\n mount(): void {\n if (this.mounted) {\n return;\n }\n this.mounted = true;\n this.instance?.onMount?.();\n for (const hook of this.mountHooks) {\n hook();\n }\n }\n\n /**\n * Runs onUnmount() and tears down framework-owned subscriptions.\n */\n dispose(): void {\n if (this.mounted) {\n this.mounted = false;\n this.instance?.onUnmount?.();\n for (const hook of this.unmountHooks) {\n hook();\n }\n }\n this.subscriptions.unsubscribe();\n // Completing the cells ends anything derived from them, such as the\n // defaulted cells `input(props.x, fallback)` returns.\n for (const cell of this.functionalCells.values()) {\n cell.complete();\n }\n }\n\n /**\n * Feeds new parent props into the input cells.\n *\n * The rendered tree is not rebuilt. Because inputs are cells and\n * render() bound them into the tree, pushing a new value through the\n * cell is what updates the output.\n */\n updateProps(props: P): void {\n (this.element as { props: P }).props = props;\n if (this.instance !== undefined) {\n this.wireInputs();\n return;\n }\n for (const [name, cell] of this.functionalCells) {\n this.applyInput(name, cell, this.providedFor(name), true);\n }\n }\n\n /**\n * Feeds the inputs `values` names from them instead of from the\n * parent, until a call with null hands them back to the parent: a\n * design tool showing one of a component's states on the live tree.\n * A later call replaces the earlier one; a new prop from the parent\n * for a previewed input waits until the preview ends.\n *\n * Each input a preview starts or ends is handed its value even when\n * it holds that value already, as a new value from the parent is: a\n * control that owns what it was started at (`controlled`) starts\n * again from it, rather than keeping what a person turned it to.\n */\n previewInputs(values: Readonly<Record<string, unknown>> | null): void {\n const names = new Set([...Object.keys(this.previewed ?? {}), ...Object.keys(values ?? {})]);\n if (this.instance !== undefined) {\n // A class keeps its own default for an input its parent leaves\n // out, which a preview writes over: kept here, and given back.\n const cells = this.instance as unknown as Record<string, InputCell<unknown>>;\n const inputs = new Set(getComponentMetadata(this.element.component).inputs);\n for (const name of Object.keys(values ?? {})) {\n if (inputs.has(name) && !this.beforePreview.has(name)) {\n this.beforePreview.set(name, cells[name]!.getValue());\n }\n }\n this.previewed = values;\n this.wireInputs(names);\n for (const [name, value] of this.beforePreview) {\n if (values === null || !Object.hasOwn(values, name)) {\n this.beforePreview.delete(name);\n if (this.providedFor(name) === undefined) {\n cells[name]!.next(value);\n }\n }\n }\n return;\n }\n this.previewed = values;\n for (const name of names) {\n const cell = this.functionalCells.get(name);\n if (cell !== undefined) {\n this.applyInput(name, cell, this.providedFor(name), true, true);\n }\n }\n }\n\n /** What an input is fed from: a preview's value for it, else what it was started with, else the parent's prop. */\n private providedFor(name: string): unknown {\n if (this.previewed !== null && Object.hasOwn(this.previewed, name)) {\n return this.previewed[name];\n }\n const parent = (this.element.props as Record<string, unknown>)[name];\n if (this.started !== null && Object.hasOwn(this.started, name)) {\n // A start, not a hold: once the parent passes something new, it is\n // the parent's again, as a dialog saved open closes when its owner\n // closes it.\n if (parent !== this.startedOver.get(name)) {\n delete this.started[name];\n this.startedStreams.delete(name);\n return parent;\n }\n if (isObservable(parent)) {\n let stream = this.startedStreams.get(name);\n if (stream === undefined) {\n stream = concat(of(this.started[name]), parent.pipe(skip(1)));\n this.startedStreams.set(name, stream);\n }\n return stream;\n }\n return this.started[name];\n }\n return (this.element.props as Record<string, unknown>)[name];\n }\n\n private renderFunction(): UiChild {\n const component = this.element.component as FunctionComponent<Record<string, unknown>>;\n const context = this.createContext();\n this.rendering = true;\n try {\n return withLiveInputs(this.live, () =>\n withBodyOf(this.element.tag, () => component(this.createInputRecord(), context))\n );\n } finally {\n this.rendering = false;\n }\n }\n\n /**\n * The props record a functional component reads: a cell per name,\n * created when first asked for and fed from the parent's props.\n *\n * Creating cells on demand is what lets the parent omit a prop\n * without the function having to check for a missing cell, and lets\n * a cell asked for later — in an event handler, say — still be live.\n */\n private createInputRecord(): Record<string, InputCell<unknown>> {\n const cellFor = (name: string): InputCell<unknown> => {\n let cell = this.functionalCells.get(name);\n if (cell === undefined) {\n cell = new InputCell<unknown>(undefined);\n cell.label = `${this.element.tag}.${name}`;\n this.functionalCells.set(name, cell);\n this.applyInput(name, cell, this.providedFor(name), true);\n }\n return cell;\n };\n return new Proxy({} as Record<string, InputCell<unknown>>, {\n get: (_target, name) => (typeof name === 'string' ? cellFor(name) : undefined),\n has: (_target, name) => typeof name === 'string',\n ownKeys: () => Array.from(new Set([...Object.keys(this.element.props), ...this.functionalCells.keys()])),\n getOwnPropertyDescriptor: (_target, name) =>\n typeof name === 'string'\n ? { value: cellFor(name), enumerable: true, configurable: true, writable: false }\n : undefined,\n set: (_target, name) => {\n throw new Error(\n `Component '${this.element.tag}' tried to assign inputs.${String(name)}. ` +\n `Inputs are cells written by the host; read inputs.${String(name)}.value or bind the cell.`\n );\n }\n });\n }\n\n private createContext(): ComponentContext {\n const requireRendering = (method: string): void => {\n if (!this.rendering) {\n throw new Error(\n `Component '${this.element.tag}' called ctx.${method}() outside its function body. ` +\n `Register lifecycle hooks while the component function runs.`\n );\n }\n };\n return {\n inject: <S extends object>(ServiceClass: new () => S): S => this.services.get(ServiceClass),\n channel: <V extends object, C extends object>(token: ChannelToken<V, C>): ChannelReplica<V, C> =>\n this.channels.get(token),\n onMount: hook => {\n requireRendering('onMount');\n this.mountHooks.push(hook);\n },\n onUnmount: hook => {\n requireRendering('onUnmount');\n this.unmountHooks.push(hook);\n },\n effect: <T>(source: Observable<T>, run: (value: T) => void): Subscription => {\n // Not guarded by `requireRendering`: a teardown may be\n // registered at any point up to disposal, and adding one to a\n // Subscription that has already been torn down tears the new\n // one down at once, which is the right answer for a component\n // that has gone.\n const subscription = source.subscribe(value => run(value));\n this.subscriptions.add(subscription);\n return subscription;\n },\n bounds: (label?: string): BoundsCell => {\n const cell = bounds(label ?? `${this.element.tag}.bounds`);\n this.subscriptions.add(() => cell.complete());\n return cell;\n },\n environment: <T>(key: UiEnvironmentKey<T>): T => {\n // The anchor is under its parent before the body runs, and a\n // node the builder made in this pass already carries what it\n // will inherit, so the nearest environment up is the one.\n for (let at: UiNode | null = this.anchor; at !== null; at = at.parent) {\n if (at.environment !== null) {\n return at.environment.get(key);\n }\n }\n return key.defaultValue;\n }\n };\n }\n\n private wireInputs(forced: ReadonlySet<string> = new Set()): void {\n const metadata = getComponentMetadata(this.element.component);\n for (const inputName of metadata.inputs) {\n const cell = (this.instance as unknown as Record<string, InputCell<unknown>>)[inputName];\n cell.label ??= `${metadata.tag}.${inputName}`;\n this.applyInput(inputName, cell, this.providedFor(inputName), false, forced.has(inputName));\n }\n }\n\n /** What the component says each of its inputs holds now; see `liveInput`. */\n liveInputs(): ReadonlyMap<string, unknown> {\n return new Map([...this.live].map(([name, read]) => [name, read()]));\n }\n\n /**\n * What the parent most recently supplied for each input, as given:\n * a value, an element or a stream, whether or not the component has\n * read it yet. For a tool reading the tree, such as the inspector's\n * owner report; nothing here changes it.\n */\n suppliedInputs(): ReadonlyMap<string, unknown> {\n const props = this.element.props as Record<string, unknown>;\n return new Map(Object.entries(props).filter(([name]) => name !== 'key' && name !== 'ref'));\n }\n\n /**\n * Connects one input cell to whatever the parent supplied.\n *\n * For a class, an absent prop leaves the cell's default in place, so\n * a parent that does not mention an input never clobbers it. For a\n * function the cell has no default of its own — `input(cell,\n * fallback)` supplies one — so a prop the parent stops passing is\n * pushed through as `undefined`, which re-applies the fallback.\n */\n private applyInput(\n inputName: string,\n cell: InputCell<unknown>,\n provided: unknown,\n resetWhenAbsent: boolean,\n force = false\n ): void {\n if (!force && this.inputSources.has(inputName) && this.inputSources.get(inputName) === provided) {\n return;\n }\n\n const previous = this.inputSubscriptions.get(inputName);\n if (previous !== undefined) {\n previous.unsubscribe();\n this.subscriptions.remove(previous);\n this.inputSubscriptions.delete(inputName);\n }\n\n const hadSource = this.inputSources.has(inputName) && this.inputSources.get(inputName) !== undefined;\n this.inputSources.set(inputName, provided);\n\n if (provided === undefined) {\n if (resetWhenAbsent && hadSource) {\n cell.next(undefined);\n }\n return;\n }\n\n if (isOutputTarget(provided)) {\n // The parent wants the child's output as a stream: the cell holds\n // a handler that forwards to the target, and `emit` calls it.\n const target = outputTargetOf(provided);\n cell.next((value: unknown) => target.next(value));\n return;\n }\n\n if (isObservable(provided)) {\n // Forced, the first value goes through even when the cell holds it.\n let first = force;\n const subscription = provided.subscribe(value => {\n if (first) {\n first = false;\n forceInput(cell, value);\n } else {\n pushInput(cell, value);\n }\n });\n this.inputSubscriptions.set(inputName, subscription);\n this.subscriptions.add(subscription);\n return;\n }\n\n if (force) {\n forceInput(cell, provided);\n } else {\n pushInput(cell, provided);\n }\n }\n\n private validateInputs(): void {\n const metadata = getComponentMetadata(this.element.component);\n for (const inputName of metadata.inputs) {\n const value = (this.instance as unknown as Record<string, unknown>)[inputName];\n if (!(value instanceof InputCell)) {\n throw new Error(\n `Component '${metadata.tag}' declares @Input() '${inputName}' but it is not an input cell. ` +\n `Initialize it with input(defaultValue).`\n );\n }\n }\n }\n\n /**\n * Resolves `@Channel(token)` properties to their replicas.\n *\n * Alongside `wireInjects` rather than inside it: a store is a class\n * this thread owns, a channel is a name the other side answers to,\n * and only one of the two survives the barrier design.\n */\n private wireChannels(): void {\n const metadata = getComponentMetadata(this.element.component);\n for (const [propertyName, token] of metadata.channels) {\n (this.instance as unknown as Record<string, unknown>)[propertyName] = this.channels.get(\n token as ChannelToken<object, CommandMap>\n );\n }\n }\n\n private wireInjects(): void {\n const metadata = getComponentMetadata(this.element.component);\n for (const [propertyName, ServiceClass] of metadata.injects) {\n const service = this.services.get(ServiceClass as unknown as new () => object);\n (this.instance as unknown as Record<string, unknown>)[propertyName] = service;\n }\n }\n}\n\nexport type { ClassComponent };\n\n/**\n * Hands an input cell a value from its parent, unless it already holds\n * that very value.\n *\n * A parent that re-renders hands every child its props again, and a\n * prop that is an Observable built in the parent's render is a new\n * source each time, which re-subscribes and replays the current value.\n * Pushed through regardless, that replay re-ran every binding derived\n * from the input. In the issue tracker's editor, inserting one block\n * re-rendered the list of 2,868, each replay rebuilt its block's text\n * runs as a new array, and every editable on the page was re-measured:\n * 12,912 nodes for one Enter. Values that cross into a component are\n * treated as immutable everywhere else in the framework (bindings hold\n * on to them, patches share structure), so the same reference is the\n * same value and there is nothing to tell the child.\n */\nfunction pushInput(cell: InputCell<unknown>, value: unknown): void {\n // `getValue`, not `value`: the getter records a read for the\n // stale-read warning, and this is the framework looking, not a body.\n const current = cell.getValue();\n if (current !== undefined && Object.is(current, value)) {\n return;\n }\n cell.next(value);\n}\n","import type { ComponentResolver, ComponentLikeElement, UiChild, UiNode } from 'gesso-core';\nimport type { ComponentElement } from './ComponentElement';\nimport { ComponentHost } from './ComponentHost';\nimport { ChannelRegistry } from './channel/ChannelRegistry';\nimport { ServiceRegistry } from './service/ServiceRegistry';\n\n/**\n * Mounts framework components on behalf of UiGraphBuilder.\n *\n * This is the framework's half of the ComponentResolver contract. It\n * holds no opinion about tree structure or identity: the builder\n * assigns every component slot a stable anchor id and this class\n * simply keeps one ComponentHost per live id.\n *\n * Replaces the former ComponentRenderer pre-pass, which resolved the\n * whole tree before graph construction and therefore could not mount\n * a component that appeared inside an observable child.\n */\nexport class ComponentHostResolver implements ComponentResolver {\n private readonly hosts = new Map<string, ComponentHost>();\n private pendingMounts: ComponentHost[] = [];\n\n constructor(\n private readonly services: ServiceRegistry = new ServiceRegistry(),\n private readonly channels: ChannelRegistry = new ChannelRegistry()\n ) {}\n\n resolve(element: ComponentLikeElement, anchorId: string, anchor?: UiNode): UiChild {\n let host = this.hosts.get(anchorId);\n\n if (host !== undefined && host.component !== element.component) {\n // The slot changed component type. The old instance cannot be\n // reused, so retire it and mount a fresh one in its place.\n host.dispose();\n this.hosts.delete(anchorId);\n host = undefined;\n }\n\n if (host === undefined) {\n host = new ComponentHost(element as ComponentElement, this.services, this.channels, anchor ?? null);\n this.hosts.set(anchorId, host);\n this.pendingMounts.push(host);\n } else {\n host.updateProps(element.props);\n }\n\n return host.render();\n }\n\n release(anchorId: string): void {\n const host = this.hosts.get(anchorId);\n if (host === undefined) {\n // Not every Fragment is a component anchor; observable children\n // use them too.\n return;\n }\n this.hosts.delete(anchorId);\n // A host queued for mounting but removed within the same pass must\n // never receive onMount().\n this.pendingMounts = this.pendingMounts.filter(pending => pending !== host);\n host.dispose();\n }\n\n flushMounts(): void {\n if (this.pendingMounts.length === 0) {\n return;\n }\n const pending = this.pendingMounts;\n this.pendingMounts = [];\n for (const host of pending) {\n host.mount();\n }\n }\n\n /**\n * Releases every live host.\n *\n * Used when tearing down a whole app, where the graph is discarded\n * wholesale rather than reconciled down to nothing.\n */\n dispose(): void {\n const hosts = [...this.hosts.values()];\n this.hosts.clear();\n this.pendingMounts = [];\n for (const host of hosts) {\n host.dispose();\n }\n }\n\n /**\n * Number of live component instances. Intended for tests and devtools.\n */\n get size(): number {\n return this.hosts.size;\n }\n\n /**\n * The component mounted at an anchor id, for the inspector.\n *\n * The anchor is the component's identity (`UiGraphBuilder` gives\n * every component slot one), so this is the whole of the link\n * between a node and the component that rendered it. Nothing else\n * holds it, which is why the inspector has to ask here.\n */\n hostFor(anchorId: string): ComponentHost | undefined {\n return this.hosts.get(anchorId);\n }\n}\n","import type { UiNodeReport } from '../NodeReport';\nimport type { DevtoolsEvent, DevtoolsRequest } from '../DevtoolsProtocol';\nimport type {\n UiKeyModifiers,\n EditingState,\n RendererBackend,\n UiInsets,\n UiPointerDevice,\n UiSemanticsAction,\n UiScrollability,\n UiSemanticsUpdate,\n UiFileDropMessage,\n UiContrast,\n UiPointer\n} from 'gesso-core';\nimport type { AudioAction, AudioRequest, AudioSample } from '../AudioService';\nimport type { ColorScheme, ColorSchemePreference } from '../colorScheme';\nimport type { ShellFileRequest, ShellFileResult, ShellStorageOp, ShellStorageResult } from '../ShellService';\nimport type { FramePhaseTimings, GpuStageTimings, RendererChoice } from '../GessoRuntime';\n\n/**\n * Messages the main-thread shell sends to the render worker.\n *\n * Deliberately small: input, size, and lifecycle. UiElements,\n * component instances, observables and UiNodes never cross the\n * boundary — they are constructed in the worker and stay there.\n */\nexport type ShellToRuntimeMessage =\n | {\n type: 'init';\n canvas: OffscreenCanvas;\n width: number;\n height: number;\n dpr: number;\n renderer?: RendererChoice;\n /**\n * How typed text reaches the runtime: `proxy` when the shell has\n * an editing proxy that sends `beforeInput` and composition (then\n * printable key presses are not text); `keys` (default) when key\n * presses are all there is.\n */\n textInput?: 'proxy' | 'keys';\n /**\n * One end of a channel to the application worker, when the shell\n * spawned one.\n *\n * The shell creates both workers and wires them together once,\n * then stays out of the way — it never sees a patch. Owning the\n * spawn rather than letting the render worker nest a worker\n * inside itself keeps the application alive across a render\n * worker being replaced (a renderer switch), and avoids\n * depending on nested worker support, which is not uniform\n * across the webviews this project targets.\n */\n appPort?: MessagePort;\n /**\n * Whether the shell has an accessibility mirror to feed. False\n * stops the runtime computing the geometry it would need, which\n * is the only per-frame cost the mirror has in here.\n */\n accessibility?: boolean;\n /**\n * The window's address at start-up, so the first tree is built on\n * the screen it names. Optional for a shell that predates it,\n * which reports the address in a `url` message after this one\n * and so starts on the root route for a frame, as it always did.\n */\n url?: string;\n }\n | { type: 'resize'; width: number; height: number; dpr: number }\n /**\n * A devtools panel's request (`DevtoolsProtocol.ts`). The worker\n * answers with `devtools` messages; `console` is handled by the\n * worker host, which owns the global the calls are made on.\n */\n | { type: 'devtools'; request: DevtoolsRequest }\n /**\n * `pointer` is the contact: which device, and which of its\n * simultaneous contacts. Optional, so a shell written before touch\n * existed is read as the mouse it was — every touch behaviour in the\n * runtime is opt-in on this field saying `touch`.\n */\n | {\n type: 'pointerDown';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | {\n type: 'pointerMove';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | {\n type: 'pointerUp';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | { type: 'pointerCancel'; pointer?: UiPointerDevice; at?: number }\n /**\n * `deltaMode` is the DOM's own value, forwarded rather than\n * translated: a wheel delta is a distance in pixels only when it is\n * 0, and Firefox reports lines. Optional, so a shell that predates\n * it is read as pixels — which is what it was assumed to be.\n */\n | {\n type: 'wheel';\n x: number;\n y: number;\n deltaX: number;\n deltaY: number;\n modifiers: UiKeyModifiers;\n deltaMode?: number;\n /**\n * The legacy `wheelDeltaY`. Forwarded because a detented wheel\n * reports it in multiples of 120 and a precision device does\n * not, and only a detented wheel is worth animating.\n */\n wheelDeltaY?: number;\n at?: number;\n }\n /**\n * `textFollows`: whether the key reached the editing proxy, which\n * turns it into a `beforeinput` of its own. False means the key went\n * to the canvas and its text, if it has any, is the runtime's to make.\n */\n | { type: 'keyDown'; key: string; code?: string; modifiers: UiKeyModifiers; at?: number; textFollows?: boolean }\n | { type: 'keyUp'; key: string; code?: string; modifiers: UiKeyModifiers; at?: number }\n /** A `beforeinput` from the editing proxy, in the DOM's inputType vocabulary. */\n | { type: 'beforeInput'; inputType: string; data: string | null; at?: number }\n | { type: 'compositionStart'; at?: number }\n /** The composition text so far and the caret offset within it. */\n | { type: 'compositionUpdate'; text: string; caret: number; at?: number }\n /** The committed text; empty when the composition was cancelled. */\n | { type: 'compositionEnd'; text: string; at?: number }\n /** `html` is the clipboard's HTML, when it held some. */\n | { type: 'paste'; text: string; html?: string; at?: number }\n /** The editing proxy lost focus to something outside the app. */\n | { type: 'blur' }\n /** The page was hidden or shown (document.visibilityState). */\n | { type: 'visibility'; visible: boolean }\n /** The surface entered or left fullscreen, however that happened. */\n | { type: 'fullscreenChanged'; active: boolean }\n /**\n * The person's motion preference (`prefers-reduced-motion`), sent\n * once at start-up and again whenever it changes.\n *\n * The first thing this protocol has ever carried that is a\n * *preference* rather than an event or a size. It is inbound because\n * the query needs a window and the animations are in here; see\n * `GessoRuntime.setReducedMotion` for why it is not an environment\n * key.\n */\n | { type: 'reducedMotion'; reduced: boolean }\n /**\n * The appearance the shell is asking for\n * (`prefers-color-scheme`, or an override the host set), sent once at\n * start-up and again whenever it changes.\n *\n * The second preference-shaped message here, and inbound for the same\n * reason as the first — the query needs a window. It differs in who\n * consumes it: reduced motion reaches the animation driver, while\n * nothing in the framework reads this one. It is carried to `ShellService` and no further,\n * because what dark *looks* like is the application's, and a\n * framework that shipped an answer would be shipping a palette.\n *\n * Always resolved to one of the two appearances. `auto` is a thing a\n * host tells a shell, not a thing that crosses.\n */\n | { type: 'colorScheme'; scheme: ColorScheme }\n | { type: 'contrast'; contrast: UiContrast }\n | { type: 'pointer'; pointer: UiPointer }\n /** Whether the window is shown and holds the focus; once at start-up and on every change. See `ShellService.windowActive`. */\n | { type: 'windowActive'; active: boolean }\n /**\n * What the window's own chrome is covering on each edge: the safe\n * area a notch or a home indicator takes, and the strip a soft\n * keyboard covers, read from `visualViewport` and the\n * `env(safe-area-inset-*)` custom properties. Sent once at start-up\n * and again whenever they change, which on a phone is every frame of\n * a keyboard sliding up.\n *\n * Inbound for the reason the two above are: `visualViewport` needs a\n * window. Four plain numbers cross, and nothing else, because what to\n * do about a keyboard is a layout question and layout is in here.\n * `GessoRuntime.setViewportInsets` publishes them into the\n * application's inset registry, where they compose by maximum with\n * whatever the application's own floating bars publish; see\n * `UiInsetRegistry` for why by maximum.\n */\n | { type: 'viewportInsets'; insets: UiInsets }\n /**\n * Where the window's address is now: once at start-up, and again for\n * every back, forward or typed address afterwards.\n *\n * The third preference-shaped message on this protocol, and for the\n * same reason as `reducedMotion`: `location` and `history` are the\n * shell's and the routes are in here. A url is the whole of what\n * routing puts on the wire — patterns, params, guards and screens\n * never leave the render thread, because a route holds a component\n * class and a component class cannot be posted anywhere.\n */\n | { type: 'url'; url: string }\n /**\n * What became of a popup the render worker asked for: `opened` is\n * false when the browser refused it, which is a thing an application\n * must be able to route around rather than a failure to log.\n *\n * The only reply on this protocol to a request from the other side,\n * which is why it carries the request's `id` rather than standing on\n * its own like the preference messages above it.\n */\n | { type: 'popupResult'; id: number; opened: boolean }\n /**\n * Whether a `clipboard` request's text reached the clipboard, under\n * the request's `id`. False when the browser refused both ways of\n * writing it.\n */\n | { type: 'clipboardResult'; id: number; copied: boolean }\n /** What a `clipboardRead` request found, under its `id`: null when the browser would not say. */\n | { type: 'clipboardReadResult'; id: number; text: string | null }\n /**\n * What the shell found in `localStorage` for a `storage` request\n * (ShellStorage). The second reply on this protocol, and it carries\n * its request's `id` for the same reason `popupResult` does.\n */\n | { type: 'storageResult'; id: number; result: ShellStorageResult }\n /**\n * What the shell did with a file request (ShellService.openFiles and\n * its siblings), under the request's `id`. A file read comes back\n * with its bytes, transferred rather than copied.\n */\n | { type: 'fileResult'; id: number; result: ShellFileResult }\n | { type: 'inspector'; enabled: boolean }\n /**\n * What an assistive technology did to the accessibility mirror: a\n * press, a focus move, or a value set.\n *\n * The fourth preference-shaped asymmetry on this protocol, and the\n * only *input* on it that no device produced. It arrives by id\n * rather than by coordinate because that is what the mirror has: an\n * element standing for a node, with no idea where the person's\n * pointer is or whether there is one. `GessoRuntime.applySemanticsAction`\n * turns it back into the events a pointer and a keyboard produce.\n */\n | { type: 'semanticsAction'; action: UiSemanticsAction }\n /**\n * Files dragged over the canvas from outside the application, and\n * let go on it: `UiDragSession.applyFileDrop` turns the four phases\n * into a drag like any other. On `drop` the files' bytes ride along\n * and are transferred rather than copied; see `attachFileDrop`.\n */\n | UiFileDropMessage\n /**\n * What the shell's audio element is doing: on every state change and\n * about once a second while it plays. The element lives on the shell\n * because no worker can make a sound; `AudioService` is its client\n * and moves the position on between samples. See `AudioSink`.\n */\n | { type: 'audioSample'; sample: AudioSample }\n /**\n * What the platform's media controls asked for (the keyboard's media\n * keys, the OS overlay). Play and pause were already done to the\n * element and arrive as samples too; next and previous are the\n * application's to answer.\n */\n | { type: 'audioAction'; action: AudioAction }\n /**\n * One display refresh, forwarded from the shell's\n * `requestAnimationFrame`.\n *\n * The render worker's frames were paced by a fixed 16ms timer,\n * because `requestAnimationFrame` is tied to the compositor and does\n * not exist off the main thread. That capped every display at\n * roughly sixty and aligned to none of them. The shell runs the loop\n * and forwards the beat; `time` is the rAF timestamp, so the\n * runtime's frame times stay on the same clock the display is on.\n *\n * Sent only between `frameLoop` starting and stopping, so an idle\n * app exchanges nothing.\n */\n | { type: 'tick'; time: number }\n /**\n * The display's interval as the shell's \\`requestAnimationFrame\\` sees\n * it, sent once the worker is ready, with the shell's \\`timeOrigin\\` so\n * the worker can read the shell's frame times on its own clock.\n *\n * A worker's own refreshes can be no display's at all: WebKit's come\n * from a timer near 65 Hz, on no display's beat. The frame clock then\n * keeps time by the shell's refreshes instead; see\n * \\`UiHostFrameClock.displayInterval\\`.\n */\n | { type: 'displayInterval'; ms: number; origin: number }\n | { type: 'dispose' };\n\n/**\n * Where an error the worker reports came from.\n *\n * The shell cannot see a worker's exceptions, so this is the only\n * thing that tells a developer *what* is broken rather than only what\n * threw — and the four cases have genuinely different consequences:\n *\n * - `message` — thrown while handling a message from the shell.\n * Input or a resize was dropped; the app is otherwise intact.\n * - `uncaught` — an exception or a rejected promise nothing caught,\n * which is almost always a frame. The scheduler had already\n * drained the dirty set for that frame, so the work it held is\n * gone and the surface can be stale until something dirties those\n * nodes again. The most serious of the four.\n * - `renderer` — the backend refused to draw (a lost GPU device, a\n * surface it could not configure). Layout and state are fine.\n * - `channel` — a channel's worker or its patch stream threw. The\n * view is intact; the data behind it stopped.\n * - `listener` — one of the application's own event listeners threw.\n * The dispatcher caught it so the event still reached the rest of\n * the tree, so this is the one source that costs the running\n * application nothing but whatever the handler was supposed to do.\n */\nexport type RuntimeErrorSource =\n | 'message'\n | 'uncaught'\n | 'renderer'\n | 'channel'\n | 'listener'\n /**\n * A frame threw. The frame was abandoned and the clock kept\n * running, so the application is still drawing: this is a report,\n * not an obituary.\n *\n * Its own source because it used to arrive as `message` — the throw\n * escaped into whatever was forwarding the tick — and that both\n * mislabelled it and stopped the application, since nothing re-armed\n * the clock afterwards.\n */\n | 'frame';\n\n/**\n * Messages the render worker sends back.\n *\n * The shell owns no UI state, so this carries only observability:\n * readiness, frame timings, and errors that would otherwise be\n * invisible inside a worker.\n */\nexport type RuntimeToShellMessage =\n | { type: 'ready' }\n | {\n type: 'frame';\n frame: number;\n durationMs: number;\n nodes: number;\n measured: number;\n relayoutRoots: number;\n layoutPasses: number;\n at: number;\n inputLatencyMs: number | null;\n time: number | null;\n wheelScroll: { readonly x: number; readonly y: number } | null;\n phases: FramePhaseTimings;\n renderer: RendererBackend | 'pending';\n gpu: GpuStageTimings | null;\n }\n | { type: 'error'; message: string; stack?: string; source: RuntimeErrorSource }\n /** A report on the hovered node while the inspector is on; null when nothing is hovered. */\n | { type: 'inspect'; report: UiNodeReport | null }\n /** An answer to a devtools request, or an update to something a panel is watching. */\n | { type: 'devtools'; event: DevtoolsEvent }\n /** The CSS cursor the hovered node asks for; null for the default arrow. */\n | { type: 'cursor'; cursor: string | null }\n /**\n * Which way the runtime could scroll under the pointer, and whether\n * it has anything scrollable at all.\n *\n * Pushed ahead of the wheel it answers for, because the shell has\n * to decide `preventDefault()` synchronously and the runtime is a\n * message away. Without it the shell must either swallow every\n * wheel — making the canvas a scroll trap in the page around it —\n * or swallow none, and let one wheel scroll twice.\n *\n * `scrollsAnything` is the coarser tree-level answer, and drives\n * the canvas's `touch-action`: that is latched when a finger lands,\n * so there is no hover position it could have been derived from.\n */\n | { type: 'scrollability'; scrollability: UiScrollability; scrollsAnything: boolean }\n /**\n * The focused editable's text, selection and caret box for the\n * editing proxy to mirror; null when no editable has focus.\n */\n | { type: 'editing'; state: EditingState | null }\n /**\n * Put text on the clipboard (ShellService.copyText). `id` pairs it\n * with the `clipboardResult` that comes back, once.\n */\n | { type: 'clipboard'; id: number; text: string }\n /** Read the clipboard's text (ShellService.readText). `id` pairs it with the `clipboardReadResult` that comes back. */\n | { type: 'clipboardRead'; id: number }\n /** Open a URL in a new tab (ShellService.openUrl). */\n | { type: 'openUrl'; url: string }\n /**\n * Open the app at one of its own urls somewhere new\n * (ShellService.openRoute). `url` is the router's, not an address:\n * the shell turns it into one, through its `onOpenRoute` or its\n * history, or follows it in place when it has neither.\n */\n | { type: 'openRoute'; url: string }\n /**\n * Replace the page with a url, leaving the app (ShellService.redirect).\n * `url` is as the component gave it, relative or not; the shell\n * resolves it against the page and refuses a scheme that is not\n * http, https or the page's own.\n */\n | { type: 'redirect'; url: string }\n | { type: 'fullscreen'; enter: boolean }\n /**\n * Whether leaving the page would lose work (ShellService.warnOnLeave):\n * the shell listens for `beforeunload` while it is on, and not at all\n * otherwise.\n */\n | { type: 'leaveWarning'; on: boolean }\n /**\n * Report this appearance from now on (ShellService.preferColorScheme):\n * `light` or `dark`, or `auto` to follow `prefers-color-scheme` again.\n * The shell does what `WorkerApp.setColorScheme` does, and answers\n * with `colorScheme`.\n */\n | { type: 'preferColorScheme'; preference: ColorSchemePreference }\n /**\n * Open a sized, named window and report back whether the browser\n * allowed it (ShellService.openPopup).\n *\n * The only shell request that is answered. `id` pairs this with the\n * `popupResult` that comes back; the shell must send exactly one per\n * request, because the promise waiting on it settles once.\n *\n * Sent ahead of any slow work on purpose: the browser grants a window\n * only while the click that prompted it is still fresh, so a render\n * worker that resolves a url first will find the window refused.\n */\n | { type: 'popup'; id: number; url: string; name: string; width: number; height: number }\n /**\n * Read, write, remove or list in `localStorage`, which lives on the\n * window and nowhere else (ShellStorage).\n *\n * Answered, like `popup`, and `id` pairs the two. The shell performs\n * exactly the call it is given and decides nothing about the key,\n * which is what keeps it the dumb half of the thread model.\n */\n | { type: 'storage'; id: number; op: ShellStorageOp; key: string; value?: string }\n /**\n * Open, save, reopen, list or forget files (ShellService.openFiles\n * and its siblings). Answered with `fileResult`. Sent at once, like\n * `popup`, because a picker is shown only while the click that asked\n * for it is fresh.\n */\n | { type: 'file'; id: number; request: ShellFileRequest }\n /** The router navigated; the shell owns the address bar (RouterService). */\n | { type: 'history'; action: 'push' | 'replace' | 'back' | 'forward'; url?: string }\n /** Load, play, pause, seek, set the volume or the OS metadata (AudioService). */\n | { type: 'audio'; request: AudioRequest }\n /**\n * What the accessibility mirror needs to keep up with this frame:\n * the semantics patches, the boxes that moved, and the focused node\n * when focus moved.\n *\n * Sent only while the shell has a mirror attached — a `SemanticsMirror`\n * subscribes by existing, and a runtime nobody is mirroring computes\n * no geometry at all. Records and boxes travel at different cadences\n * and are one message anyway; `UiSemanticsUpdate` says why.\n */\n /**\n * Whether the runtime currently wants display refreshes.\n *\n * The shell answers by running or stopping a `requestAnimationFrame`\n * loop that sends `tick`. It is a state rather than a per-frame\n * request because a request-per-frame costs a round trip inside\n * every frame: a request that reaches the shell after that vsync's\n * callback has run waits for the next one, and the frame rate\n * halves.\n */\n | { type: 'frameLoop'; running: boolean }\n /**\n * One `resize` has been applied, and the shell may send the next.\n *\n * A resize is the one shell message whose handling costs a full\n * layout, and `ResizeObserver` delivers one per refresh while a\n * window edge is dragged. A worker slower than the display therefore\n * accumulates a queue of sizes it must lay out and paint in turn,\n * every one of them already wrong, and the lag grows for as long as\n * the drag lasts rather than settling.\n *\n * So the shell keeps at most one resize in flight and remembers only\n * the latest size it has not sent. This message is what lets it: the\n * worker has drained the previous one, so the current size can go\n * now. Nothing is dropped that anyone can see — the last size always\n * gets sent, because it is the one held back.\n *\n * The dimensions ride along so the shell can tell an acknowledgement\n * of the size it is holding from one it has already superseded.\n */\n | { type: 'resized'; width: number; height: number; dpr: number }\n | { type: 'semantics'; update: UiSemanticsUpdate };\n\n/**\n * The set of shell messages that carry a user input.\n *\n * The shell stamps these with `at` and the runtime measures against\n * them; everything else in the protocol is a size, a preference or a\n * lifecycle signal and has no latency to speak of.\n */\nconst INPUT_MESSAGE_TYPES: ReadonlySet<string> = new Set([\n 'pointerDown',\n 'pointerMove',\n 'pointerUp',\n 'pointerCancel',\n 'wheel',\n 'keyDown',\n 'keyUp',\n 'beforeInput',\n 'compositionStart',\n 'compositionUpdate',\n 'compositionEnd',\n 'paste'\n]);\n\nexport function isInputMessage(message: ShellToRuntimeMessage): message is ShellToRuntimeMessage & { at?: number } {\n return INPUT_MESSAGE_TYPES.has(message.type);\n}\n\n/**\n * The inputs a person makes one at a time, whose frame is drawn as soon\n * as they are routed (`GessoRuntime.answerInput`). Pointer moves and the\n * wheel come in streams, and wait for the refresh that coalesces them.\n */\nconst DISCRETE_INPUT_TYPES: ReadonlySet<string> = new Set([\n 'pointerDown',\n 'pointerUp',\n 'keyDown',\n 'beforeInput',\n 'compositionUpdate',\n 'compositionEnd',\n 'paste'\n]);\n\nexport function isDiscreteInputMessage(message: ShellToRuntimeMessage): boolean {\n return DISCRETE_INPUT_TYPES.has(message.type);\n}\n\n/**\n * Milliseconds since the Unix epoch, at `performance.now()`'s\n * resolution.\n *\n * Input latency is the one measurement in this protocol that spans two\n * threads, and `performance.now()` cannot span them: a worker's time\n * origin is its own creation, not the document's, so the shell's\n * reading and the worker's reading are counted from different\n * moments. Adding `timeOrigin` puts both on one clock.\n *\n * `FrameMetrics.at` deliberately does *not* use this — it is only ever\n * subtracted from another reading taken on the same thread, and its\n * docblock explains why that is the honest measure of a stall.\n */\nexport function epochNow(): number {\n if (typeof performance === 'undefined') {\n return Date.now();\n }\n return performance.timeOrigin + performance.now();\n}\n\n/**\n * When a DOM event actually happened, on the same epoch clock.\n *\n * `event.timeStamp` is set by the browser when it creates the event,\n * not when a listener runs, and that difference is the whole point of\n * this measurement: a shell busy for two seconds runs its listener two\n * seconds late, and stamping inside the listener would record the\n * delay as zero. Reading the event's own clock is what makes a blocked\n * shell visible.\n */\nexport function epochFromEvent(event: { timeStamp: number }): number {\n if (typeof performance === 'undefined') {\n return Date.now();\n }\n return performance.timeOrigin + event.timeStamp;\n}\n\nexport function modifiersFrom(event: {\n shiftKey: boolean;\n ctrlKey: boolean;\n altKey: boolean;\n metaKey: boolean;\n}): UiKeyModifiers {\n return { shift: event.shiftKey, ctrl: event.ctrlKey, alt: event.altKey, meta: event.metaKey };\n}\n","import { insetsEqual, noInsets, type UiInsets } from 'gesso-core';\n\nimport { internalState } from '../InternalState';\nimport type { UiContrast, UiPointer } from 'gesso-core';\nimport type { ColorScheme, ColorSchemePreference } from './colorScheme';\nimport type { ReadableCell } from '../Input';\n\n/**\n * Something only the shell — the thread with a window — can do.\n *\n * The `history` variant is the router's, not a component's: a\n * component navigates through `RouterService`, which turns the\n * navigation into one of these because the address bar is on the other\n * thread. It is here rather than in a protocol of its own because it\n * is the same kind of thing as the other two — a request the render\n * thread cannot serve itself — and `GessoApp` and `WorkerApp` already\n * have exactly one place that answers them.\n */\nexport type ShellRequest =\n | { type: 'clipboard'; id: number; text: string }\n | { type: 'clipboardRead'; id: number }\n | { type: 'openUrl'; url: string }\n | { type: 'openRoute'; url: string }\n | { type: 'redirect'; url: string }\n | { type: 'fullscreen'; enter: boolean }\n | { type: 'leaveWarning'; on: boolean }\n | { type: 'colorScheme'; preference: ColorSchemePreference }\n | { type: 'popup'; id: number; url: string; name: string; width: number; height: number }\n | { type: 'storage'; id: number; op: ShellStorageOp; key: string; value?: string }\n | { type: 'file'; id: number; request: ShellFileRequest }\n | { type: 'history'; action: 'push' | 'replace'; url: string }\n | { type: 'history'; action: 'back' | 'forward'; url?: undefined };\n\n/**\n * A kind of file a picker offers, as the File System Access API and a\n * file input both understand it.\n *\n * `extensions` include the dot (`.csv`), because that is the form both\n * APIs take and a spelling this layer translated would be one more\n * place for the two to disagree.\n */\nexport interface ShellFileType {\n readonly description: string;\n readonly mediaType: string;\n readonly extensions: readonly string[];\n}\n\n/**\n * What the shell is asked to do with files.\n *\n * `handle` is a number the shell hands out for a file somebody picked\n * — the `FileSystemFileHandle` itself cannot leave the main thread,\n * since nothing that is not plain data crosses the barrier. The shell\n * keeps the handle and remembers it across reloads, so a number from\n * yesterday's session still names yesterday's file.\n */\nexport type ShellFileRequest =\n /** Show an open picker. */\n | { readonly op: 'open'; readonly accept: readonly ShellFileType[]; readonly multiple: boolean }\n /**\n * Read a file the shell already has a handle to, asking permission\n * again if it lapsed; or, for a folder, ask again and read nothing.\n * `mode` is what a folder is asked for. Default `'read'`.\n */\n | { readonly op: 'reopen'; readonly handle: number; readonly mode?: 'read' | 'readwrite' }\n /**\n * Show a folder picker. The folder is remembered under a number like\n * a file; its contents are read by whoever needs them, with\n * `rememberedDirectory`, in any thread of the page.\n */\n | { readonly op: 'openDirectory'; readonly mode: 'read' | 'readwrite' }\n /**\n * Write a file: to `handle` when given, and otherwise to wherever a\n * save picker says — or, where there is no picker, as a download.\n */\n | {\n readonly op: 'save';\n readonly name: string;\n readonly mediaType: string;\n readonly text: string;\n /**\n * The file's bytes, for one that is not text — a zip, an image.\n * Written instead of `text` when given; bytes cross the barrier\n * as they do on the way in, in `ShellFile.bytes`.\n */\n readonly bytes?: Uint8Array<ArrayBuffer>;\n readonly handle?: number;\n readonly accept: readonly ShellFileType[];\n /**\n * Whether a file saved through the picker joins the recent files\n * and gets a handle to save back to. Absent is true; an export —\n * a picture, a report — is false, so it is not offered for\n * opening as though it were a document.\n */\n readonly remember?: boolean;\n }\n /** The files the shell remembers, most recently used first. */\n | { readonly op: 'recent' }\n /** Stop remembering one. */\n | { readonly op: 'forget'; readonly handle: number };\n\n/** One file the shell read. */\nexport interface ShellFile {\n readonly name: string;\n readonly mediaType: string;\n readonly lastModified: number;\n readonly bytes: ArrayBuffer;\n /** The shell's number for it, or null where the platform gives none (a file input). */\n readonly handle: number | null;\n}\n\n/** A file, or a folder, the shell remembers. */\nexport interface ShellRecentFile {\n readonly handle: number;\n readonly name: string;\n readonly kind: 'file' | 'directory';\n /** When it was last opened or saved, in epoch milliseconds. */\n readonly used: number;\n}\n\n/**\n * What the shell made of a file request.\n *\n * One record with a field per shape of answer, as `ShellStorageResult`\n * is and for the same reason. `cancelled` is its own outcome because a\n * person closing a picker is a decision and not a failure, and an\n * application that reported it as one would be wrong every time.\n * `denied` is a permission refused; `unsupported` is a shell with no\n * way to do it at all.\n */\nexport interface ShellFileResult {\n readonly outcome: 'ok' | 'cancelled' | 'denied' | 'unsupported' | 'failed';\n /** What `open` and `reopen` read. */\n readonly files: readonly ShellFile[];\n /** The folder `openDirectory` was given, or `reopen` was allowed to use again. */\n readonly directory: { readonly name: string; readonly handle: number } | null;\n /** Where `save` wrote: `'file'` through a handle, `'download'` without one. */\n readonly saved: { readonly name: string; readonly handle: number | null; readonly via: 'file' | 'download' } | null;\n /** What `recent` lists. */\n readonly recent: readonly ShellRecentFile[];\n /** Why it did not answer, as a message; null when it did. */\n readonly error: string | null;\n}\n\n/** The four things `localStorage` is asked for; see `ShellStorage`. */\nexport type ShellStorageOp = 'read' | 'write' | 'remove' | 'keys';\n\n/**\n * What the shell made of a storage request.\n *\n * One record with a field per shape of answer, rather than four reply\n * messages: it is plain data either way, and a single reply keeps the\n * pairing with `id` in one place. `value` is a read's, `keys` is a\n * listing's, and both are empty for a write.\n */\nexport interface ShellStorageResult {\n readonly outcome: 'ok' | 'denied' | 'full' | 'failed';\n readonly value: string | null;\n readonly keys: readonly string[];\n /** Why it did not answer, as a message; null when it did. */\n readonly error: string | null;\n}\n\n/**\n * The shell's services, as a store components can inject.\n *\n * A component in the render worker has no clipboard and no window.\n * Dispatching an action here hands the request to the runtime, which\n * forwards it to whichever host it has: `WorkerApp` posts it to the\n * main thread, `GessoApp` performs it directly. Every runtime registers\n * one, like `OverlayService`; being a store keeps the rule that\n * components reach the outside world through actions only, and gives a\n * desktop shell one place to bind native equivalents.\n */\nexport class ShellService {\n private handler: ((request: ShellRequest) => void) | null = null;\n /** What `warnOnLeave` last said. */\n private leaving = false;\n /** What `preferColorScheme` last asked for; null until it is asked. */\n private preferred: ColorSchemePreference | null = null;\n private readonly scheme = internalState<ColorScheme>('light');\n private readonly contrastState = internalState<UiContrast>('standard');\n private readonly pointerState = internalState<UiPointer>('fine');\n private readonly activeState = internalState(true);\n private readonly insets = internalState<UiInsets>(noInsets);\n private readonly isFullscreen = internalState<boolean>(false);\n /** Popups asked for and not yet answered, by the id sent with each. */\n private readonly popups = new Map<number, (opened: boolean) => void>();\n private nextPopupId = 1;\n /** Storage requests asked for and not yet answered, by the id sent with each. */\n private readonly stores = new Map<number, (result: ShellStorageResult) => void>();\n private nextStorageId = 1;\n /** Clipboard writes asked for and not yet answered, by the id sent with each. */\n private readonly copies = new Map<number, (copied: boolean) => void>();\n private nextCopyId = 1;\n /** Clipboard reads asked for and not yet answered, by the id sent with each. */\n private readonly reads = new Map<number, (text: string | null) => void>();\n private nextReadId = 1;\n /** File requests asked for and not yet answered, by the id sent with each. */\n private readonly files = new Map<number, (result: ShellFileResult) => void>();\n private nextFileId = 1;\n\n /**\n * The appearance the platform is asking for, as the shell reports it:\n * once when the app starts, and again whenever it changes.\n *\n * Read-only to the application on purpose. Nothing in the framework\n * consumes this — no built-in theme switches on it, and nothing in\n * layout, paint or input reads it — so the only writer is the shell,\n * and a cell an application could also write would be a cell the next\n * media-query change silently overwrites.\n *\n * What it means is the application's to decide. A theme is an\n * ordinary prop that accepts an Observable, so the whole of following\n * the platform is:\n *\n * const theme = ctx.inject(ShellService).colorScheme.pipe(\n * map(scheme => (scheme === 'dark' ? darkTheme : lightTheme))\n * );\n *\n * An app with its own light/dark control keeps that choice as\n * application state — a channel or a store, like any other preference\n * that outlives a screen — and combines it with this. The framework\n * does not decide what dark looks like, and does not remember what\n * the person picked.\n */\n /**\n * A cell rather than a bare Observable, so `computed(() => ...)` can\n * read it beside a channel's view; its setter stays private here.\n */\n readonly colorScheme: ReadableCell<ColorScheme> = this.scheme;\n\n /**\n * The contrast the platform is asking for: `high` when the person has\n * turned up contrast or turned on a contrast theme, `standard`\n * otherwise. Reported by the shell like `colorScheme`, and as with it,\n * what it means is the application's: `withContrast(theme, contrast)`\n * is the theme that answers it.\n *\n * const theme = combineLatest([shell.colorScheme, shell.contrast]).pipe(\n * map(([scheme, contrast]) => withContrast(scheme === 'dark' ? darkTheme : lightTheme, contrast))\n * );\n */\n readonly contrast: ReadableCell<UiContrast> = this.contrastState;\n\n /**\n * What the person points with: `coarse` on a touch screen, `fine` with\n * a mouse or a trackpad. Fine until a shell says otherwise. The\n * runtime also provides it at the root (`UiEnvironmentKeys.pointer`),\n * where controls read it; this is for the application's own choices.\n */\n readonly pointer: ReadableCell<UiPointer> = this.pointerState;\n\n /**\n * Whether the window is the one the person is using: shown, and holding\n * the keyboard focus. False while it is behind another window,\n * minimised, or in a background tab. True until a shell says otherwise.\n *\n * For work that should happen when the person comes back rather than\n * on a clock: reading again what may have changed while they were away.\n *\n * ctx.effect(shell.windowActive.pipe(filter(Boolean)), () => channel.send.refresh());\n */\n readonly windowActive: ReadableCell<boolean> = this.activeState;\n\n /** The current appearance, for code that needs it without subscribing. */\n get currentColorScheme(): ColorScheme {\n return this.scheme.value;\n }\n\n /**\n * What the platform itself is covering on each edge, as the shell\n * reports it: the safe area under a notch or a home indicator, and\n * the strip a soft keyboard has slid over. Zeroes on a desktop window\n * with neither, and zeroes until a shell has said otherwise.\n *\n * Read-only to the application for the same reason `colorScheme` is:\n * the shell is the only thing that knows, and a cell an application\n * could also write is a cell the next keyboard event overwrites.\n *\n * Most applications never read this. The runtime publishes the same\n * four numbers into the inset registry the root provides, so a\n * screen that keeps clear of the bars with `insetPadding` keeps\n * clear of the keyboard too without naming it. This is for the\n * application that provides its registry somewhere other than the\n * root, or wants the platform's numbers apart from its own bars'.\n */\n readonly viewportInsets: ReadableCell<UiInsets> = this.insets;\n\n /** The platform's current insets, for code that needs them without subscribing. */\n get currentViewportInsets(): UiInsets {\n return this.insets.value;\n }\n\n /**\n * Installed by the runtime; a request with no handler is dropped,\n * except the leave warning, which is a state rather than an event: a\n * shell that arrives after it was turned on is told.\n */\n setHandler(handler: ((request: ShellRequest) => void) | null): void {\n this.handler = handler;\n if (handler !== null && this.leaving) {\n handler({ type: 'leaveWarning', on: true });\n }\n if (handler !== null && this.preferred !== null) {\n handler({ type: 'colorScheme', preference: this.preferred });\n }\n }\n\n /**\n * Called by the runtime when the shell reports the appearance.\n *\n * Not for applications: the shell is the only thing that knows the\n * answer, and `colorScheme` is how an application hears about it.\n */\n applyColorScheme(scheme: ColorScheme): void {\n if (this.scheme.value !== scheme) {\n this.scheme.value = scheme;\n }\n }\n\n /** Called by the runtime when the shell reports whether the window is active; not for applications. */\n applyWindowActive(active: boolean): void {\n if (this.activeState.value !== active) {\n this.activeState.value = active;\n }\n }\n\n /** Called by the runtime when the shell reports the contrast; not for applications. */\n /** Called by the runtime when the shell reports what the person points with. */\n applyPointer(pointer: UiPointer): void {\n if (this.pointerState.value !== pointer) {\n this.pointerState.value = pointer;\n }\n }\n\n applyContrast(contrast: UiContrast): void {\n if (this.contrastState.value !== contrast) {\n this.contrastState.value = contrast;\n }\n }\n\n /**\n * Called by the runtime when the shell reports the platform's insets.\n *\n * Not for applications, on the terms `applyColorScheme` sets. A\n * report that changes nothing is dropped here, so a `visualViewport`\n * scroll event that moved no edge does not wake every subscriber.\n */\n applyViewportInsets(insets: UiInsets): void {\n if (!insetsEqual(this.insets.value, insets)) {\n this.insets.value = insets;\n }\n }\n\n /**\n * Puts text on the system clipboard, and answers whether it got there.\n *\n * Most callers ignore the answer, and may: the text is on its way the\n * moment this returns. It is for an application that tells the person\n * what happened, a \"Copied\" toast after a menu command, say, which\n * would be a lie on the occasions the browser refused. It does refuse:\n * the clipboard wants a focused document, and some browsers a fresh\n * gesture, and a key pressed in a render worker reaches the window's\n * clipboard a message later than it reached the canvas.\n *\n * With no shell installed the answer is false, as `openPopup`'s is,\n * rather than a promise that never settles.\n */\n copyText(text: string): Promise<boolean> {\n const handler = this.handler;\n if (handler === undefined || handler === null) {\n return Promise.resolve(false);\n }\n const id = this.nextCopyId++;\n const settled = new Promise<boolean>(resolve => {\n this.copies.set(id, resolve);\n });\n handler({ type: 'clipboard', id, text });\n return settled;\n }\n\n /**\n * Called by the runtime when the shell reports whether a clipboard\n * write landed. Not for applications. An unknown id is ignored, on\n * `settlePopup`'s terms.\n */\n settleClipboard(id: number, copied: boolean): void {\n const resolve = this.copies.get(id);\n if (resolve === undefined) {\n return;\n }\n this.copies.delete(id);\n resolve(copied);\n }\n\n /**\n * The text on the system clipboard, for a Paste chosen from a menu\n * rather than typed: a key's paste comes with its text, a menu's has\n * to ask. Null when there is none to give: the browser refused (it\n * wants a secure context, a focused document, and in some browsers\n * the person's say-so, which it asks for itself), or no shell is\n * installed. It never rejects.\n */\n readText(): Promise<string | null> {\n const handler = this.handler;\n if (handler === undefined || handler === null) {\n return Promise.resolve(null);\n }\n const id = this.nextReadId++;\n const settled = new Promise<string | null>(resolve => {\n this.reads.set(id, resolve);\n });\n handler({ type: 'clipboardRead', id });\n return settled;\n }\n\n /**\n * Called by the runtime with what a clipboard read found. Not for\n * applications. An unknown id is ignored, on `settlePopup`'s terms.\n */\n settleClipboardRead(id: number, text: string | null): void {\n const resolve = this.reads.get(id);\n if (resolve === undefined) {\n return;\n }\n this.reads.delete(id);\n resolve(text);\n }\n\n /** Opens a URL in the user's browser, in a new tab or window. */\n openUrl(url: string): void {\n this.handler?.({ type: 'openUrl', url });\n }\n\n /**\n * Opens this application at one of its own urls, somewhere new: what\n * a Cmd-click or a Ctrl-click on an in-app link asks for.\n *\n * Not `openUrl`, because the two urls are different kinds of thing.\n * `openUrl` takes an address that already means something to the\n * browser. This takes the router's url, `/epic/BUD-12?story=BUD-13`,\n * which means something only to this application, and only the\n * shell knows what address shows the application at it: a path on\n * the page's origin, a fragment on the page's own address, a tab in\n * the product the app is embedded in, or a new native window. The\n * render thread cannot know which, so it names the route and the\n * shell decides.\n *\n * What each shell does, in order: the host's `onOpenRoute` if it gave\n * one; otherwise a new tab at the address the history gives for the\n * url (`ShellHistory.href`); otherwise, where there is no address,\n * which is `memory` mode, the shell follows the url in place, exactly\n * as `RouterService.navigate` would, rather than doing nothing.\n *\n * The current screen does not navigate. Nothing comes back, as with\n * `openUrl`. With no shell installed the request is dropped.\n */\n openRoute(url: string): void {\n this.handler?.({ type: 'openRoute', url });\n }\n\n /**\n * Replaces the page with `url`, leaving the application.\n *\n * Not the router's `navigate`, which moves between screens *inside*\n * the app and keeps it running: this unloads the page, and whatever\n * the app held in memory goes with it. It is for the places a page\n * has to be handed to somebody else's server and wait to be sent\n * back — an OAuth sign-in that goes to `/api/auth/login`, is\n * redirected to an identity provider and returns with a session, or\n * a sign-out at `/api/auth/logout` that clears a cookie only the\n * server can clear. It is also where a sign-in falls back to when\n * `openPopup` reports the popup blocked.\n *\n * A request rather than a call because `window.location` belongs to\n * the main thread and a render worker cannot reach it. A relative\n * url resolves against the page, so `/api/auth/login` means the\n * app's own server. The shell refuses anything that is not http,\n * https or the page's own scheme — a `javascript:` url, above all,\n * which would run in the page rather than go anywhere — so a url\n * built from data cannot be turned into script.\n *\n * Nothing comes back, and there is nothing to wait for: if the\n * redirect happens, the code that would have read the answer is\n * gone. Leaving the page is not cancellable from here either, so a\n * component with unsaved work should settle it before asking.\n */\n redirect(url: string): void {\n this.handler?.({ type: 'redirect', url });\n }\n\n /**\n * Whether the application's surface is filling the screen.\n *\n * Read rather than assumed: the person can leave fullscreen with\n * Escape, which no request here hears about, and a button that\n * tracked its own last press would then point the wrong way. The\n * shell reports the real state and this follows it.\n */\n readonly fullscreen: ReadableCell<boolean> = this.isFullscreen;\n\n /**\n * Asks the shell to fill the screen, or to stop.\n *\n * A request rather than a call, for the reason the clipboard is one:\n * the Fullscreen API is the document's, and the thread this runs on\n * may not have a document. It can also be refused outright, because\n * browsers only grant it during a gesture, which is why nothing here\n * returns a promise pretending otherwise. Watch `fullscreen` for what\n * actually happened.\n */\n requestFullscreen(enter: boolean): void {\n this.handler?.({ type: 'fullscreen', enter });\n }\n\n /**\n * Whether leaving the page now would lose work: edits not yet saved,\n * an upload half done. While it is on, the shell has the browser ask\n * before the page is closed, reloaded or left (`beforeunload`), in the\n * browser's own words, which a page cannot set.\n *\n * A request rather than a call because `beforeunload` is the window's,\n * and the thread this runs on may not have one. Turn it off as soon as\n * the work is safe: the browser will not keep a page that has a\n * `beforeunload` listener in its back/forward cache, so the shell\n * listens only while this is on. Saying the same thing twice sends\n * nothing.\n */\n warnOnLeave(on: boolean): void {\n if (on === this.leaving) {\n return;\n }\n this.leaving = on;\n this.handler?.({ type: 'leaveWarning', on });\n }\n\n /**\n * Asks the shell to report this appearance from now on: `light` or\n * `dark` whatever the platform says, or `auto` to follow the platform\n * again. `colorScheme` then reports the answer, as it does when the\n * host calls `setColorScheme`, which is the same switch.\n *\n * For an application whose appearance setting is its own and stored:\n * the host reads the stored choice before the first frame and starts\n * with it (`createApp({ colorScheme })`), and the application, where\n * the person changes it, says so here. Without this, an application in\n * a render worker could not take back an override the host started it\n * with, and `auto` would keep reporting the stored choice instead of\n * the platform's. An application that stores nothing can combine its\n * choice with `colorScheme` instead and never call this.\n *\n * A request, because the media query is the window's; and a state,\n * like `warnOnLeave`, so a shell that arrives after it was asked is\n * told. Asking for the same thing twice sends nothing.\n */\n preferColorScheme(preference: ColorSchemePreference): void {\n if (preference === this.preferred) {\n return;\n }\n this.preferred = preference;\n this.handler?.({ type: 'colorScheme', preference });\n }\n\n /** Called by the runtime when the shell reports the state. Not for applications. */\n applyFullscreen(active: boolean): void {\n if (this.isFullscreen.value !== active) {\n this.isFullscreen.value = active;\n }\n }\n\n /**\n * Opens a sized window and answers whether the browser allowed it.\n *\n * Separate from `openUrl` because the two differ in three ways that\n * matter. A popup is a small window rather than a tab, so it carries\n * a size; it is named, so asking twice reuses one window rather than\n * littering the desktop; and the caller has to hear whether it opened,\n * because a blocked popup is a dead end an application must route\n * around rather than a request it can post and forget.\n *\n * The answer is a promise, the one place in the framework where a\n * shell request has a reply, because there is nothing useful an\n * application can do with a popup it cannot see the fate of. A sign-in\n * flow that is blocked falls back to a full-page redirect — `redirect`,\n * below — and it can only choose that if it is told.\n *\n * Two browser rules shape the contract and both were measured in\n * Chrome before this existed:\n *\n * - The window must be asked for while the click that prompted it is\n * still fresh, so the request travels ahead of any slow work. A\n * round trip through a worker is fast enough; resolving a url over\n * the network first is not, so build the url before calling this.\n * - One gesture buys one window. A second call on the same click is\n * refused by the browser and resolves `false`.\n *\n * The window is opened *with* an opener, unlike `openUrl`, which\n * passes `noopener`. That is not a relaxation for its own sake:\n * `window.open` returns `null` when `noopener` is set whether or not\n * the window appeared, so a popup asked for that way could never\n * report the one thing this method exists to report. The opened page\n * is a different origin, so what the opener reference grants it is\n * what any OAuth popup's does.\n */\n openPopup(request: {\n readonly url: string;\n readonly name?: string;\n readonly width?: number;\n readonly height?: number;\n }): Promise<boolean> {\n const handler = this.handler;\n if (handler === undefined || handler === null) {\n // No shell, so no window; a headless runtime says so rather than\n // leaving a promise that never settles.\n return Promise.resolve(false);\n }\n const id = this.nextPopupId++;\n const settled = new Promise<boolean>(resolve => {\n this.popups.set(id, resolve);\n });\n handler({\n type: 'popup',\n id,\n url: request.url,\n name: request.name ?? 'gesso-popup',\n width: request.width ?? 520,\n height: request.height ?? 680\n });\n return settled;\n }\n\n /**\n * Called by the runtime when the shell reports what became of a\n * popup. Not for applications.\n *\n * An id the map does not hold is ignored rather than thrown on: a\n * duplicate reply, or one arriving after the runtime was torn down,\n * is the shell being noisy and not the application being wrong.\n */\n settlePopup(id: number, opened: boolean): void {\n const resolve = this.popups.get(id);\n if (resolve === undefined) {\n return;\n }\n this.popups.delete(id);\n resolve(opened);\n }\n\n /**\n * Asks the shell to read, write, remove or list in `localStorage`.\n *\n * `localStorage` is on the window and nowhere else: a worker cannot\n * reach it, so a render thread that wants it has to ask, exactly as\n * it asks for the clipboard. What comes back is plain data, and the\n * shell decides nothing beyond performing the call, which is the\n * rule the thread model holds it to.\n *\n * `ShellStorage` is what an application uses; this is the wire under\n * it. With no shell installed the answer is `denied`, because a\n * headless runtime has no window and never will, and a promise left\n * unsettled would hang whatever was waiting on it.\n */\n requestStorage(request: {\n readonly op: ShellStorageOp;\n readonly key: string;\n readonly value?: string;\n }): Promise<ShellStorageResult> {\n const handler = this.handler;\n if (handler === undefined || handler === null) {\n return Promise.resolve({\n outcome: 'denied',\n value: null,\n keys: [],\n error: 'There is no shell to store through.'\n });\n }\n const id = this.nextStorageId++;\n const settled = new Promise<ShellStorageResult>(resolve => {\n this.stores.set(id, resolve);\n });\n handler({\n type: 'storage',\n id,\n op: request.op,\n key: request.key,\n ...(request.value === undefined ? {} : { value: request.value })\n });\n return settled;\n }\n\n /**\n * Called by the runtime with what the shell found. Not for\n * applications.\n *\n * An id the map does not hold is ignored, on the same terms as\n * `settlePopup`: a duplicate reply is the shell being noisy rather\n * than the application being wrong.\n */\n settleStorage(id: number, result: ShellStorageResult): void {\n const resolve = this.stores.get(id);\n if (resolve === undefined) {\n return;\n }\n this.stores.delete(id);\n resolve(result);\n }\n\n /**\n * Shows an open picker and reads what was picked.\n *\n * A request, like a popup, and for the same reason: a picker is the\n * window's, and a browser shows one only while the click that asked\n * for it is fresh. Call this from the handler of that click and\n * before any slow work, and the round trip through a worker is fast\n * enough. With the File System Access API each file comes back with\n * a `handle` the shell remembers; through a file input, which is\n * what a browser without the API has, `handle` is null and the file\n * cannot be saved back to.\n */\n openFiles(\n options: { readonly accept?: readonly ShellFileType[]; readonly multiple?: boolean } = {}\n ): Promise<ShellFileResult> {\n return this.requestFile({ op: 'open', accept: options.accept ?? [], multiple: options.multiple ?? false });\n }\n\n /**\n * Reads a file the shell has a handle to — from `recentFiles`, or\n * from an earlier `openFiles` or `saveFile` — asking for permission\n * again when the browser has let it lapse, which it does across a\n * reload. Asking needs a gesture, so this belongs in a click handler\n * too.\n */\n reopenFile(handle: number): Promise<ShellFileResult> {\n return this.requestFile({ op: 'reopen', handle });\n }\n\n /**\n * Writes text, or bytes, to a file.\n *\n * With `handle`, to that file, which is Save; without one, to\n * wherever a save picker says, which is Save As. A browser with no\n * picker gets a download instead, and `saved.via` says which\n * happened, so an application can tell the person where the file\n * went — and knows it has no handle to save back to next time.\n */\n saveFile(options: {\n readonly name: string;\n /** What to write, for a text file. */\n readonly text?: string;\n /** What to write, for any other kind; taken over `text` when both are given. */\n readonly bytes?: Uint8Array<ArrayBuffer>;\n readonly mediaType?: string;\n readonly handle?: number;\n readonly accept?: readonly ShellFileType[];\n /**\n * Whether the file joins the recent files, with a handle to save\n * back to. True unless said otherwise; false for a file made to\n * keep rather than to open again — an exported picture — whose\n * answer then has no handle.\n */\n readonly remember?: boolean;\n }): Promise<ShellFileResult> {\n return this.requestFile({\n op: 'save',\n name: options.name,\n text: options.text ?? '',\n ...(options.bytes === undefined ? {} : { bytes: options.bytes }),\n mediaType: options.mediaType ?? 'application/octet-stream',\n accept: options.accept ?? [],\n ...(options.handle === undefined ? {} : { handle: options.handle }),\n ...(options.remember === false ? { remember: false } : {})\n });\n }\n\n /** The files the shell remembers, most recently used first. */\n recentFiles(): Promise<ShellFileResult> {\n return this.requestFile({ op: 'recent' });\n }\n\n /**\n * Shows a folder picker and remembers the folder chosen, under a\n * number, as `openFiles` does a file. Nothing in it is read: a folder\n * can hold a hundred thousand files, and the worker that wants them\n * takes the handle itself with `rememberedDirectory` and reads what\n * it needs, rather than every byte crossing to the render worker.\n * `outcome` is `unsupported` in a browser with no folder picker. Call\n * it from a click handler, as `openFiles`.\n */\n openDirectory(options: { readonly mode?: 'read' | 'readwrite' } = {}): Promise<ShellFileResult> {\n return this.requestFile({ op: 'openDirectory', mode: options.mode ?? 'read' });\n }\n\n /**\n * Asks again for a folder remembered from an earlier session, which a\n * browser lets lapse across a reload. Asking needs a gesture: a click\n * handler.\n */\n reopenDirectory(handle: number, options: { readonly mode?: 'read' | 'readwrite' } = {}): Promise<ShellFileResult> {\n return this.requestFile({ op: 'reopen', handle, mode: options.mode ?? 'read' });\n }\n\n /** Stops remembering a file. Its handle means nothing afterwards. */\n forgetFile(handle: number): Promise<ShellFileResult> {\n return this.requestFile({ op: 'forget', handle });\n }\n\n /**\n * The wire under the methods above. With no shell installed the answer\n * is `unsupported`, for the reason a storage request's is `denied`:\n * a promise left unsettled would hang whatever was waiting on it.\n */\n requestFile(request: ShellFileRequest): Promise<ShellFileResult> {\n const handler = this.handler;\n if (handler === undefined || handler === null) {\n return Promise.resolve(shellFilesUnsupported());\n }\n const id = this.nextFileId++;\n const settled = new Promise<ShellFileResult>(resolve => {\n this.files.set(id, resolve);\n });\n handler({ type: 'file', id, request });\n return settled;\n }\n\n /**\n * Called by the runtime with what the shell did. Not for\n * applications. An unknown id is ignored, on `settlePopup`'s terms.\n */\n settleFile(id: number, result: ShellFileResult): void {\n const resolve = this.files.get(id);\n if (resolve === undefined) {\n return;\n }\n this.files.delete(id);\n resolve(result);\n }\n}\n\n/** The answer for a shell that cannot do anything with files. */\nexport function shellFilesUnsupported(error = 'There is no shell to reach files through.'): ShellFileResult {\n return { outcome: 'unsupported', files: [], directory: null, saved: null, recent: [], error };\n}\n","import { Observable, Subject } from 'rxjs';\n\nimport type { FrameMetrics } from './GessoRuntime';\n\n/**\n * Every frame the runtime draws, as a component can hear it.\n *\n * The shell already receives these as `onFrame`, for an FPS readout or\n * a profiler on the host thread. A screen that wants to show its own\n * frame gap or input latency, as a demo of the thread model does,\n * needs them on this thread, and this is where the runtime puts them.\n * Injected like any service: `ctx.inject(FrameService).frames`.\n */\nexport class FrameService {\n private readonly subject = new Subject<FrameMetrics>();\n private flusher: (() => void) | null = null;\n /** Emits after each frame finishes, with what it cost and what it answered. */\n readonly frames: Observable<FrameMetrics> = this.subject.asObservable();\n\n /** Called by the runtime; not for components. */\n publish(metrics: FrameMetrics): void {\n this.subject.next(metrics);\n }\n\n /** Called by the shell that drives the frames; not for components. */\n setFlush(flush: (() => void) | null): void {\n this.flusher = flush;\n }\n\n /**\n * Draws a pending frame now, rather than when the display next asks\n * for one; nothing when none is pending.\n *\n * A tab in the background is sent no frames. A design editor's tool,\n * called by an agent while the editor's window is behind another,\n * changes the screen and waits for it to be drawn before reading it:\n * without this, it waits for a frame that never comes. The agent's\n * own screen tools (`uiSurface`) do the same after each action.\n */\n flush(): void {\n this.flusher?.();\n }\n}\n","import type { UiNodeReport } from './NodeReport';\nimport type { FrameMetrics } from './GessoRuntime';\nimport type { Patch } from '../channel/StorePatch';\nimport type { RuntimeErrorSource } from './worker/RenderWorkerProtocol';\n\n/**\n * What a devtools panel says to a running application and what it\n * hears back, as plain data.\n *\n * The node inspector answered \"what is the thing under the pointer\";\n * a panel docked outside the page asks the questions a DOM inspector\n * answers instead: what is the whole tree, what is this node I picked\n * from it, outline it for me, and what did the workers log. Every\n * answer here is built where the tree is and crosses a `postMessage`\n * as strings and numbers, for the same reason `UiNodeReport` does:\n * nothing in the render thread can be handed out, and a panel that\n * lives in an extension is two message hops away from it anyway.\n *\n * The same vocabulary serves both configurations. `WorkerApp` carries\n * it over the render worker protocol; `GessoApp`, with the runtime in\n * the same thread, answers the requests directly. A panel cannot tell\n * which it is talking to, which is what lets one panel serve both.\n */\n\n/** One node of the tree, as a panel lists it. */\nexport interface UiTreeNode {\n readonly id: string;\n /** `UiNodeType`, as its string value. */\n readonly type: string;\n /**\n * The component this node is the anchor of, when it is one.\n *\n * Only the anchor carries the name. A panel wanting \"which component\n * rendered this node\" reads it off the nearest ancestor that has\n * one, which is the same walk `UiNodeReport.owners` makes and\n * costs the snapshot nothing per node.\n */\n readonly component?: string;\n /** A text node's text, shortened, so the tree reads like the screen. */\n readonly text?: string;\n /**\n * Live subscriptions the node holds: bound properties, reactive\n * children, event handlers. Absent when it holds none, which is most\n * nodes.\n *\n * On the tree rather than in a report of its own because the answer\n * a subscription view needs is per component, and the tree is what\n * says which component a node belongs to. A panel adds them up\n * towards the nearest ancestor carrying a `component`, and a leak is\n * the count that climbs while the shape stays still.\n */\n readonly subscriptions?: number;\n readonly children: readonly UiTreeNode[];\n}\n\n/** The whole tree at one moment. */\nexport interface UiTreeSnapshot {\n readonly root: UiTreeNode;\n /** How many nodes the snapshot holds, so a panel can say so without counting. */\n readonly nodes: number;\n /**\n * Live subscriptions across the whole graph, including the runtime's\n * own nodes that the snapshot does not list. The number to watch\n * while doing nothing.\n */\n readonly subscriptions: number;\n}\n\n/**\n * What was being answered when an action was recorded\n *.\n *\n * The barrier carries no request id: a command goes up, patches come\n * down, and nothing in the protocol ties the second to the first. So\n * the tie is made where both are seen, by the recorder, and it starts\n * from the one thing that is unambiguous — the input being dispatched\n * when the command was sent. Everything the input caused carries the\n * same id, and a panel groups by it instead of comparing timestamps\n * across three threads.\n */\nexport interface ActionCause {\n /** Rising from 1, within one recorder. */\n readonly id: number;\n /** The input that started it: `pointerUp (412, 233)`, `keyDown Enter`. */\n readonly label: string;\n}\n\ninterface ActionEntryBase {\n /** Position on the timeline. Never reused, never renumbered. */\n readonly seq: number;\n /** `performance.now()` on the thread that recorded it. */\n readonly at: number;\n /** The input this entry belongs to, when it belongs to one. */\n readonly cause?: ActionCause;\n}\n\n/** A command a view sent across the barrier. */\nexport interface CommandEntry extends ActionEntryBase {\n readonly kind: 'command';\n readonly channel: string;\n readonly command: string;\n readonly payload: unknown;\n}\n\n/** A batch of patches the owning thread sent back. */\nexport interface PatchEntry extends ActionEntryBase {\n readonly kind: 'patch';\n readonly channel: string;\n readonly patches: readonly Patch[];\n /** The projections this batch touched, in the order first touched. */\n readonly keys: readonly string[];\n}\n\n/** A channel error, kept on the timeline so it has a position on it. */\nexport interface ChannelErrorEntry extends ActionEntryBase {\n readonly kind: 'error';\n readonly channel: string;\n readonly message: string;\n}\n\n/**\n * The frame that drew what came before it.\n *\n * The last link in the chain, and the reason it is an entry rather\n * than a field on the patches: a patch batch is recorded when it\n * arrives, which is before the frame that applies it exists. Stamping\n * it later would mean amending an entry a panel has already been sent.\n * A row on the timeline says the same thing by position, and says it\n * once.\n *\n * Recorded only for a frame that has something to close, so an idle\n * application's ticker does not fill the log with frames.\n */\nexport interface FrameEntry extends ActionEntryBase {\n readonly kind: 'frame';\n /** `UiFrame.id`, the same number `FrameMetrics.frame` carries. */\n readonly frame: number;\n}\n\n/** One line of a store action log. */\nexport type ActionEntry = CommandEntry | PatchEntry | ChannelErrorEntry | FrameEntry;\n\n/** One `console.*` call made in a worker, forwarded with its thread named. */\nexport interface ConsoleEntry {\n readonly thread: 'render' | 'app';\n readonly level: 'log' | 'info' | 'warn' | 'error' | 'debug';\n /** Each argument printed as one string; an Error carries its stack. */\n readonly args: readonly string[];\n /** `Date.now()` in the thread that logged. */\n readonly at: number;\n}\n\n/** What a panel asks. */\nexport type DevtoolsRequest =\n /** One tree snapshot, now. */\n | { kind: 'tree' }\n /**\n * A snapshot after every frame that changed the shape or the text\n * of the tree, until turned off. Frames that only moved boxes send\n * nothing, because the tree a panel lists did not change.\n */\n | { kind: 'watchTree'; enabled: boolean }\n /** A report on one node, now; `null` comes back for an id the tree no longer has. */\n | { kind: 'inspect'; id: string }\n /**\n * Keep one node's report fresh: a report now, and another after any\n * frame that changed it, until a different node (or null) is\n * selected. This is the panel's selection.\n */\n | { kind: 'select'; id: string | null }\n /**\n * Outline a node on the canvas, the way the DOM inspector outlines an\n * element hovered in its tree. Independent of the layout inspector's\n * own toggle, so a panel can point at a node without turning on the\n * heatmap.\n */\n | { kind: 'highlight'; id: string | null }\n /** Forward `console.*` from the workers, with the thread named. */\n | { kind: 'console'; enabled: boolean }\n /** A `frame` event per frame, until turned off: the profiler's feed. */\n | { kind: 'watchFrames'; enabled: boolean }\n /**\n * The layout inspector's own toggle: hover boxes and the measure\n * heatmap on the canvas, and a `hover` event for the node under the\n * pointer. The panel's \"pick from the canvas\".\n */\n | { kind: 'inspector'; enabled: boolean }\n /**\n * Writes a property on a node the panel picked, which is the half of\n * the addressed channel the inspector deferred: `select` names a\n * node to read, this one names a node to change.\n *\n * The write goes through the graph like any other, so the override\n * cascade, the equality check and the dirty marking all apply, and\n * the frame that follows is an ordinary frame. Two consequences to\n * be honest about: a bound property is overwritten by its stream's\n * next emission, and a value the element declares again on the next\n * rebuild comes back. This edits the tree, not the code.\n *\n * `value` is plain data. `null` removes the property, which is what\n * lets a value put back an inherited one.\n */\n | { kind: 'setProp'; id: string; name: string; value: unknown }\n /** Frame and channel spans in the browser's own profiler, until turned off. */\n | { kind: 'marks'; enabled: boolean };\n\n/** What the application answers, and volunteers while something is watched. */\nexport type DevtoolsEvent =\n | { kind: 'tree'; tree: UiTreeSnapshot }\n | { kind: 'report'; id: string; report: UiNodeReport | null }\n | { kind: 'console'; entry: ConsoleEntry }\n | { kind: 'frame'; metrics: FrameMetrics }\n /**\n * One line of a store action log recorded on the thread the ports\n * are on.\n *\n * A log in the page reaches a panel through the devtools hook, which\n * has the log itself to read. A log in the render worker has no such\n * route: the shell holds neither end of a channel there, by design\n *, so the entries come out the way every other\n * answer from that thread does, as plain data on the devtools\n * channel.\n */\n | { kind: 'action'; entry: ActionEntry }\n /** The node under the pointer while the inspector is on; null when none, or when it was turned off. */\n | { kind: 'hover'; report: UiNodeReport | null }\n /**\n * An error the render worker reported to its shell. Only a worker\n * shell sends these: with the runtime in the page, an error is an\n * ordinary page error and the page's console already has it.\n */\n | { kind: 'error'; message: string; stack?: string; source: RuntimeErrorSource };\n\n/** Longer text than this is cut in a tree snapshot; the report has the whole of it. */\nexport const TREE_TEXT_LIMIT = 40;\n\n/** A text node's text as the tree shows it. */\nexport function treeText(text: unknown): string | undefined {\n if (typeof text !== 'string' || text === '') {\n return undefined;\n }\n const flat = text.replace(/\\s+/g, ' ');\n return flat.length > TREE_TEXT_LIMIT ? `${flat.slice(0, TREE_TEXT_LIMIT - 1)}…` : flat;\n}\n","import { type Observable, Subject } from 'rxjs';\n\nimport { linear } from 'gesso-core';\n\nimport { computed, type ComputedCell } from '../computed';\nimport { internalState } from '../InternalState';\nimport type { AnimationService } from './AnimationService';\nimport { epochNow } from './worker/RenderWorkerProtocol';\n\n/**\n * Where a playback stands, as the shell last reported it.\n *\n * A *sample*, not a stream: the shell sends one whenever the element\n * changes state and otherwise about once a second, and `AudioService`\n * moves `position` forward between samples on the animation driver.\n * That keeps the message rate at one a second while a seek bar moves\n * every frame, the same arrangement video time uses.\n */\nexport interface AudioSample {\n readonly status: AudioStatus;\n /** Seconds into the track, as of `at`. */\n readonly position: number;\n /** Seconds, or `NaN` until the element knows. */\n readonly duration: number;\n /** Seconds: the end of the buffered range that holds `position`. */\n readonly buffered: number;\n /** When the shell took the sample, on the epoch clock both threads share (`epochNow`). */\n readonly at: number;\n /** What went wrong, for `error`, or why a play was refused, for `paused`. */\n readonly error?: string;\n}\n\n/**\n * - `idle`: nothing loaded.\n * - `loading`: a source is set and the element is fetching or\n * stalled; the position does not advance.\n * - `playing`, `paused`, `ended`: what they say.\n * - `error`: the element gave up on this source.\n */\nexport type AudioStatus = 'idle' | 'loading' | 'playing' | 'paused' | 'ended' | 'error';\n\n/** What the render thread asks the shell's audio element to do. */\nexport type AudioRequest =\n | { readonly type: 'load'; readonly src: string; readonly autoplay: boolean }\n /**\n * Start buffering the source that will play next, on a second\n * element, so the change when it arrives is gapless.\n *\n * A hint and not a command: nothing plays, nothing is reported, and a\n * `load` of some other source ignores it. A `load` of exactly this\n * source is what redeems it. An empty string clears it.\n */\n | { readonly type: 'preload'; readonly src: string }\n | { readonly type: 'play' }\n | { readonly type: 'pause' }\n | { readonly type: 'seek'; readonly seconds: number }\n | { readonly type: 'volume'; readonly level: number }\n | { readonly type: 'metadata'; readonly metadata: AudioMetadata | null };\n\n/**\n * What the platform's own controls asked for: the keyboard's media\n * keys, the lock screen, the OS media overlay. Play and pause are\n * carried out by the shell on the element itself and reported back as\n * a sample; `next` and `previous` mean nothing to an element, so they\n * come here for the application to answer.\n */\nexport type AudioAction = 'play' | 'pause' | 'next' | 'previous';\n\n/** What the OS shows for the playing track, through the Media Session API. */\nexport interface AudioMetadata {\n readonly title: string;\n readonly artist: string;\n readonly album?: string;\n /** A square picture's url. */\n readonly artwork?: string;\n}\n\n/** The sample, with the position moved to now and the source it is for. */\nexport interface AudioState {\n readonly status: AudioStatus;\n readonly position: number;\n readonly duration: number;\n readonly buffered: number;\n readonly src: string | null;\n readonly error?: string;\n}\n\nconst IDLE: AudioSample = { status: 'idle', position: 0, duration: NaN, buffered: 0, at: 0 };\n\n/** How often the extrapolated position is written between samples. Ten a second is smooth on a seek bar and cheap. */\nconst POSITION_STEP_MS = 100;\n/** How far to extrapolate a playback whose duration the element has not reported yet. */\nconst UNKNOWN_DURATION_HORIZON_S = 24 * 60 * 60;\n\n/**\n * Sound, as a component reaches it.\n *\n * No worker can make a sound: `HTMLAudioElement` and `AudioContext`\n * exist only on a thread with a window. So the element lives on the\n * shell, as a *sink* behind a handful of messages, in the way the shell\n * already answers `clipboard` and `openUrl`, and this store is its\n * client on the render thread. Every runtime registers one, like\n * `ShellService`; being a store keeps the rule that components reach\n * the outside world through actions only, and gives a desktop shell one\n * place to bind a native player.\n *\n * Time is the interesting part. The shell samples the element on state\n * changes and once a second; between samples `position` is driven by a\n * linear tween on the animation driver, so the seek bar moves at the\n * frame rate while the barrier carries one small message a second. A\n * sample stops the tween and restarts it from the truth, which is also\n * what corrects any drift. Nothing here decides what to play next: the\n * application does, from `state` and `actions`.\n */\nexport class AudioService {\n private handler: ((request: AudioRequest) => void) | null = null;\n private animations: AnimationService | null = null;\n private readonly sample = internalState<AudioSample>(IDLE);\n private readonly source = internalState<string | null>(null);\n private readonly position = internalState(0);\n private readonly actionSubject = new Subject<AudioAction>();\n\n /**\n * The playback, with the position moved to now.\n *\n * A cell rather than a stream, which is what lets a screen write\n * `computed(() => audio.state.value.status === 'playing')` instead of\n * listing it as a source of a `derive`. It is the\n * same expression `current` is, and the same three cells underneath;\n * being a `computed` is what gives it a current value as well as a\n * stream, without a second copy of the arithmetic.\n */\n readonly state: ComputedCell<AudioState> = computed(() => this.current, { label: 'AudioService.state' });\n\n /** What the platform's media controls asked for. */\n readonly actions: Observable<AudioAction> = this.actionSubject.asObservable();\n\n /** The current state, for code that needs it without subscribing. */\n get current(): AudioState {\n const sample = this.sample.value;\n return {\n status: sample.status,\n position: this.position.value,\n duration: sample.duration,\n buffered: sample.buffered,\n src: this.source.value,\n ...(sample.error === undefined ? {} : { error: sample.error })\n };\n }\n\n /** Installed by the runtime; a request with no handler is dropped. */\n setHandler(handler: ((request: AudioRequest) => void) | null): void {\n this.handler = handler;\n }\n\n /** Installed by the runtime: the driver `position` moves on between samples. */\n setAnimations(animations: AnimationService | null): void {\n this.animations = animations;\n }\n\n /**\n * Called by the runtime when the shell reports the element.\n *\n * Not for applications: the shell is the only thing that knows the\n * answer, and `state` is how an application hears about it.\n */\n applySample(sample: AudioSample): void {\n this.sample.value = sample;\n const elapsed = sample.status === 'playing' ? Math.max(0, (epochNow() - sample.at) / 1000) : 0;\n const known = Number.isFinite(sample.duration);\n const now = known ? Math.min(sample.position + elapsed, sample.duration) : sample.position + elapsed;\n this.animations?.stop(this.position);\n this.position.value = now;\n if (sample.status !== 'playing' || this.animations === null) {\n return;\n }\n const target = known ? sample.duration : now + UNKNOWN_DURATION_HORIZON_S;\n if (target <= now) {\n return;\n }\n // A readout, not motion: it keeps moving under reduced motion, as\n // the spinner does, because a seek bar that stood still would say\n // the music had stopped.\n this.animations.animate(this.position, target, {\n duration: (target - now) * 1000,\n easing: linear,\n stepMs: POSITION_STEP_MS,\n reducedMotion: 'keep'\n });\n }\n\n /** Called by the runtime when the platform's controls act. Not for applications. */\n applyAction(action: AudioAction): void {\n this.actionSubject.next(action);\n }\n\n /**\n * Loads a source and, by default, starts it.\n *\n * Reported as `loading` at once rather than waiting for the shell,\n * so a screen that pressed play shows something before the round\n * trip comes back.\n */\n load(src: string, options: { readonly autoplay?: boolean } = {}): void {\n this.animations?.stop(this.position);\n this.source.value = src;\n this.position.value = 0;\n this.sample.value = { status: 'loading', position: 0, duration: NaN, buffered: 0, at: epochNow() };\n this.handler?.({ type: 'load', src, autoplay: options.autoplay ?? true });\n }\n\n /**\n * Says what will play next, so the shell can have it buffered before\n * it is asked for.\n *\n * Nothing about the current playback changes, and nothing is\n * reported: this only speaks to a second element the shell keeps. A\n * later `load` of the same source is answered from what was\n * buffered, and a `load` of anything else discards it. Pass an empty\n * string when nothing follows.\n */\n preload(src: string): void {\n this.handler?.({ type: 'preload', src });\n }\n\n play(): void {\n this.handler?.({ type: 'play' });\n }\n\n pause(): void {\n this.handler?.({ type: 'pause' });\n }\n\n /**\n * Moves to a position. Written locally at once, so a dragged seek bar\n * follows the finger rather than the round trip; the element's own\n * `seeked` sample confirms it.\n */\n seek(seconds: number): void {\n this.animations?.stop(this.position);\n this.position.value = Math.max(0, seconds);\n this.handler?.({ type: 'seek', seconds: Math.max(0, seconds) });\n }\n\n /** 0 to 1. */\n setVolume(level: number): void {\n this.handler?.({ type: 'volume', level: Math.min(1, Math.max(0, level)) });\n }\n\n /** What the OS shows for the track; null clears it. */\n setMetadata(metadata: AudioMetadata | null): void {\n this.handler?.({ type: 'metadata', metadata });\n }\n}\n","import { internalState, type InternalState } from '../InternalState';\nimport type { RouteDefinition } from './RouteDefinition';\n\n/**\n * What a route remembers while its screen is not there.\n *\n * A screen is built when its route matches and destroyed when it stops\n * matching, so everything a screen keeps in its body goes with it: the\n * scroll offset of the list, which row the keyboard was on, the text in\n * the filter field. Coming back lands at the top of the list, which is\n * the wrong place, and it also breaks a shared element, because a morph\n * is measured from where the element is *seen* and a list at the top is\n * not where it was left.\n *\n * Both applications solved this by putting those cells in module scope,\n * with the same comment on each explaining why. This\n * is that store, with an owner. What it buys over a module:\n *\n * - **A lifetime that is stated.** A cell here lives as long as the\n * router does and is dropped by `forget`, rather than as long as the\n * module registry, which is until the tab closes.\n * - **One store per runtime.** A module's cells are shared by every\n * runtime in the process, which two windows of the same desktop app\n * and two tests in the same file both are.\n * - **Names that cannot collide.** A key is scoped to a route, so two\n * screens may both remember `scroll` and mean different lists.\n *\n * It is a facility and not an architecture. The framework does not own\n * an application's data: anything that matters after\n * a reload, or that another part of the application acts on, is still\n * application state on a channel. What belongs here is the small,\n * screen-shaped remainder that only exists to put a screen back where it\n * was.\n */\nexport class RouteState {\n /**\n * `null` is the router's own scope: what belongs to the navigation\n * rather than to one screen. A `Map` keyed on the route object needs\n * no ids and no path strings, and a route that is redeclared is a\n * different route.\n */\n private readonly byRoute = new Map<RouteDefinition | null, Map<string, InternalState<unknown>>>();\n\n /**\n * The cell a route keeps under `key`, created with `initial` the first\n * time it is asked for and handed back unchanged afterwards.\n *\n * `initial` is therefore read once. A screen built a second time gets\n * the value the first one left, which is the whole point.\n */\n cell<T>(route: RouteDefinition | null, key: string, initial: T): InternalState<T> {\n let keys = this.byRoute.get(route);\n if (keys === undefined) {\n keys = new Map<string, InternalState<unknown>>();\n this.byRoute.set(route, keys);\n }\n const held = keys.get(key);\n if (held !== undefined) {\n return held as InternalState<T>;\n }\n const cell = internalState(initial, `${route === null ? 'router' : route.path}.${key}`);\n keys.set(key, cell as InternalState<unknown>);\n return cell;\n }\n\n /** Whether a route has remembered anything under this key yet. */\n has(route: RouteDefinition | null, key: string): boolean {\n return this.byRoute.get(route)?.has(key) ?? false;\n }\n\n /**\n * Drops everything a route remembered, so the next screen starts as\n * the first one did: signing out, or a list whose contents are no\n * longer the ones the offset was measured against.\n */\n forget(route: RouteDefinition | null): void {\n this.byRoute.delete(route);\n }\n\n /** Drops every route's memory. */\n clear(): void {\n this.byRoute.clear();\n }\n}\n","/**\n * Paths, patterns and the params inside them.\n *\n * A pattern is a path with `:name` in the places that vary:\n * `/mail/:folder/:id`. Nothing else is special — no regular\n * expressions, no optional segments, no repeats. That is deliberate:\n * every extra form is one more thing the type below has to model, and\n * a pattern whose params the compiler cannot name is a pattern that\n * gives up the one thing this router is for.\n *\n * A trailing `/*` is the exception, and only because a layout route\n * has to be able to say \"and anything below me\". It captures the rest\n * of the path as `rest`.\n */\n\n/** One piece of a parsed pattern. */\nexport type PatternSegment =\n | { readonly kind: 'static'; readonly text: string }\n | { readonly kind: 'param'; readonly name: string }\n | { readonly kind: 'rest' };\n\n/**\n * The params a pattern declares, as a type.\n *\n * `RouteParams<'/mail/:folder/:id'>` is `{ folder: string; id: string }`,\n * so `router.go(MailItem, { folder: 'inbox', id: '2' })` is checked, a\n * misspelled param is a compile error, and a missing one is too. This\n * is the whole reason routes are declared with full paths rather than\n * relative fragments: a relative fragment knows only its own segments,\n * and the params a screen actually receives include its parents'.\n */\nexport type RouteParams<Path extends string> = Prettify<ParamsOf<Path>>;\n\ntype ParamsOf<Path extends string> = Path extends `${infer Head}/${infer Rest}`\n ? ParamOfSegment<Head> & ParamsOf<Rest>\n : ParamOfSegment<Path>;\n\ntype ParamOfSegment<Segment extends string> = Segment extends `:${infer Name}`\n ? { [K in Name]: string }\n : Segment extends '*'\n ? { rest: string }\n : {};\n\n/** Flattens an intersection so hovering a param object shows its keys. */\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\n/** True when the pattern declares no params, so `go()` may omit them. */\nexport type HasNoParams<Path extends string> = keyof ParamsOf<Path> extends never ? true : false;\n\n/**\n * Splits a path into its segments, ignoring leading, trailing and\n * doubled slashes so `/mail/`, `mail` and `//mail` are one path.\n */\nexport function pathSegments(path: string): string[] {\n return path.split('/').filter(segment => segment.length > 0);\n}\n\n/** Parses a pattern once, at declaration time. */\nexport function parsePattern(pattern: string): PatternSegment[] {\n return pathSegments(pattern).map((segment, index, all) => {\n if (segment === '*') {\n if (index !== all.length - 1) {\n throw new Error(`Route pattern '${pattern}' has '*' before its last segment; a rest may only end a pattern.`);\n }\n return { kind: 'rest' as const };\n }\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n if (name.length === 0) {\n throw new Error(`Route pattern '${pattern}' has an unnamed ':' segment.`);\n }\n return { kind: 'param' as const, name };\n }\n return { kind: 'static' as const, text: segment };\n });\n}\n\n/**\n * Matches a parsed pattern against a path's segments.\n *\n * Returns the captured params, or null when the pattern does not\n * describe this path. An exact match is required: a pattern with\n * three segments does not match a path with four unless it ends in a\n * rest.\n */\nexport function matchPattern(\n segments: readonly PatternSegment[],\n path: readonly string[]\n): Record<string, string> | null {\n const params: Record<string, string> = {};\n for (let index = 0; index < segments.length; index++) {\n const segment = segments[index]!;\n if (segment.kind === 'rest') {\n params.rest = path.slice(index).join('/');\n return params;\n }\n const value = path[index];\n if (value === undefined) {\n return null;\n }\n if (segment.kind === 'static') {\n if (segment.text !== value) {\n return null;\n }\n continue;\n }\n params[segment.name] = decodeURIComponent(value);\n }\n return path.length === segments.length ? params : null;\n}\n\n/**\n * Fills a pattern's params back in, which is what makes a link out of\n * a route and a params object.\n */\nexport function buildPath(pattern: string, params: Readonly<Record<string, string>> = {}): string {\n const parts = parsePattern(pattern).map(segment => {\n if (segment.kind === 'static') {\n return segment.text;\n }\n if (segment.kind === 'rest') {\n return params.rest ?? '';\n }\n const value = params[segment.name];\n if (value === undefined) {\n throw new Error(`Route '${pattern}' needs a '${segment.name}' param.`);\n }\n return encodeURIComponent(value);\n });\n const path = parts.filter(part => part.length > 0).join('/');\n return `/${path}`;\n}\n\n/** A url split into the path the routes match and the query they carry. */\nexport interface ParsedUrl {\n readonly path: string;\n readonly query: Readonly<Record<string, string>>;\n}\n\n/**\n * Splits a url into path and query.\n *\n * Any hash is dropped: the shell decides whether the fragment is where\n * the app's url lives (see `shellHistory`), and by the time a url\n * reaches the router that decision has already been unwound.\n */\nexport function parseUrl(url: string): ParsedUrl {\n const withoutHash = url.split('#')[0] ?? '';\n const [rawPath = '', rawQuery = ''] = splitOnce(withoutHash, '?');\n const query: Record<string, string> = {};\n for (const pair of rawQuery.split('&')) {\n if (pair.length === 0) {\n continue;\n }\n const [key = '', value = ''] = splitOnce(pair, '=');\n query[decodeURIComponent(key)] = decodeURIComponent(value.replace(/\\+/g, ' '));\n }\n return { path: `/${pathSegments(rawPath).join('/')}`, query };\n}\n\n/** Joins a path and a query back into the url the history stores. */\nexport function formatUrl(path: string, query: Readonly<Record<string, string>> = {}): string {\n const pairs = Object.entries(query).map(([key, value]) => `${encodeQueryPart(key)}=${encodeQueryPart(value)}`);\n const normalized = `/${pathSegments(path).join('/')}`;\n return pairs.length === 0 ? normalized : `${normalized}?${pairs.join('&')}`;\n}\n\n/**\n * Encodes a query key or value, leaving the characters a query may hold\n * as they are (`,` `:` `@` `/`), so a list of values reads as one:\n * `status=todo,done`, not `status=todo%2Cdone`. What would change how\n * the query parses (`&`, `=`, `+`, `#`) is still encoded.\n */\nfunction encodeQueryPart(text: string): string {\n return encodeURIComponent(text)\n .replace(/%2C/gi, ',')\n .replace(/%3A/gi, ':')\n .replace(/%40/gi, '@')\n .replace(/%2F/gi, '/');\n}\n\nfunction splitOnce(text: string, separator: string): [string, string] {\n const index = text.indexOf(separator);\n return index === -1 ? [text, ''] : [text.slice(0, index), text.slice(index + separator.length)];\n}\n","import type { Observable } from 'rxjs';\n\nimport { computed, type ComputedCell } from '../computed';\nimport { internalState, type InternalState } from '../InternalState';\nimport type { RouteDefinition, RouteTarget } from './RouteDefinition';\nimport { RouteState } from './RouteState';\nimport {\n buildPath,\n formatUrl,\n matchPattern,\n parseUrl,\n pathSegments,\n type HasNoParams,\n type RouteParams\n} from './RoutePath';\n\n/** What the router resolved a url to. */\nexport interface RouteMatch {\n /** The deepest route that matched: the one whose screen is the leaf. */\n readonly route: RouteDefinition;\n /** That route and its ancestors, outermost first — the outlets to render. */\n readonly chain: readonly RouteDefinition[];\n /** Every param captured along the chain, since paths are full. */\n readonly params: Readonly<Record<string, string>>;\n readonly query: Readonly<Record<string, string>>;\n readonly path: string;\n /** Path and query together: what the history stores. */\n readonly url: string;\n}\n\n/**\n * The half of navigation only the thread with an address bar can do.\n *\n * The runtime installs one; without it the router still works, it\n * simply keeps its history in memory. That is not a degraded mode — it\n * is exactly what a desktop window wants, and what a test wants.\n */\nexport interface RouterHistorySink {\n push(url: string): void;\n replace(url: string): void;\n back(): void;\n forward(): void;\n}\n\nexport interface RouterRoutes {\n readonly routes: readonly RouteDefinition[];\n /**\n * Shown when no route matches. Without one an unmatched url leaves\n * `match` null and the outlet renders nothing, which is a blank\n * screen — fine for a test, wrong for an app.\n */\n readonly notFound?: RouteDefinition;\n}\n\nexport interface NavigateOptions {\n readonly query?: Readonly<Record<string, string>>;\n /** Replace the current history entry instead of pushing a new one. */\n readonly replace?: boolean;\n}\n\n/**\n * What `go` takes after the route: its params, or nothing at all when\n * the path declares none.\n *\n * A rest tuple rather than an optional argument, because an optional\n * `RouteParams<Path>` for a path with no params is `{}`, and `{}`\n * accepts any object — so `go(Home, { id: '1' })` would compile and do\n * nothing. Making the argument absent is what makes it an error.\n */\nexport type GoArgs<Path extends string> =\n HasNoParams<Path> extends true ? [options?: NavigateOptions] : [params: RouteParams<Path>, options?: NavigateOptions];\n\n/** How many redirects a single navigation may take before it is a bug. */\nconst MAX_REDIRECTS = 10;\n\n/**\n * How a route's question and a shared slot's answer are compared, for\n * `answerFor`.\n *\n * Both sides return a string because both sides are naming the same\n * thing in the application's own words, and a string is the only shape\n * the router can compare without knowing what the thing is. Whatever\n * normalising the comparison needs (a case, a trailing slash) is done\n * in these two functions, where the application knows which it wants.\n */\nexport interface RouteAnswer<Path extends string, T> {\n /** What this route's params are asking for. */\n readonly asks: (params: RouteParams<Path>) => string;\n /** What a value from the slot is an answer about. */\n readonly answers: (value: NonNullable<T>) => string;\n}\n\n/**\n * Routing, as a service components inject.\n *\n * Every runtime registers one, and it is inert until routes are given\n * to it — through `renderRoot(App).useRoutes(...)`, the single-thread\n * builder's `useRoutes`, or `GessoRuntimeOptions.routes`.\n *\n * It is a service and not a channel, which is the same test everything\n * else on this thread passes: a match holds `RouteDefinition` objects,\n * which hold component classes, and a component class cannot cross a\n * worker boundary. What *can* cross — a url — is what the shell\n * exchanges with it, and that is the entire wire surface of routing.\n *\n * Named `RouterService` rather than `RouterStore`\n * because the store/service split of the thread model renamed all six\n * of its siblings; a `Store` here would be the only one left.\n */\nexport class RouterService {\n /** The current url, path and query. */\n readonly url = internalState('/');\n /** What that url resolved to, or null when nothing matched. */\n readonly match = internalState<RouteMatch | null>(null);\n /**\n * What each route remembers between the times its screen exists.\n *\n * Reached through `remember` and `forget`; exposed because a test and\n * a devtools panel both want to look at it whole.\n */\n readonly state = new RouteState();\n\n private routes: readonly RouteDefinition[] = [];\n private notFound: RouteDefinition | undefined;\n private history: RouterHistorySink | null = null;\n\n /** Installed by the runtime; without one, history is in memory. */\n setHistory(history: RouterHistorySink | null): void {\n this.history = history;\n }\n\n /**\n * Declares what routes exist. Re-resolves the current url, so\n * registering routes after a url has arrived is not a race.\n */\n setRoutes(routes: RouterRoutes): void {\n this.routes = routes.routes;\n this.notFound = routes.notFound;\n this.resolveInto(this.url.value, { push: false });\n }\n\n /** The routes this service is currently matching against. */\n get declaredRoutes(): readonly RouteDefinition[] {\n return this.routes;\n }\n\n /**\n * Navigates to a route, with the params its path declares.\n *\n * Typed from the route: `go(MailItem, { id: '2' })` will not compile\n * with the wrong param name, the wrong type, or none at all. A route\n * whose path has no params takes no second argument.\n */\n go<Path extends string>(route: RouteDefinition<Path>, ...args: GoArgs<Path>): void {\n const [params, options] = splitGoArgs(args);\n const path = buildPath(route.path, params);\n this.navigate(formatUrl(path, options.query), { replace: options.replace ?? false });\n }\n\n /** Navigates to a url, as a link would. */\n navigate(url: string, options: { replace?: boolean } = {}): void {\n this.resolveInto(url, { push: true, replace: options.replace ?? false });\n }\n\n /** Goes back through the shell's history; a no-op without a shell. */\n back(): void {\n this.history?.back();\n }\n\n /** Goes forward through the shell's history; a no-op without a shell. */\n forward(): void {\n this.history?.forward();\n }\n\n /**\n * The url the shell says the window is at: the first one at start-up,\n * and every one the back and forward buttons produce afterwards.\n *\n * Guards run on these too — a url typed into the address bar is\n * exactly the navigation a guard exists for — and a guard that\n * redirects replaces the entry rather than pushing, so Back does not\n * land on the url that was just refused.\n */\n applyUrl(url: string): void {\n this.resolveInto(url, { push: false });\n }\n\n /**\n * The params of the current match, typed from the route asked for,\n * or null when that route is not the one showing.\n *\n * A screen reads its own params with this rather than through a prop\n * so that it keeps them across a navigation that changes only the\n * params — the same instance stays mounted, and its props would have\n * had to be rebuilt to tell it.\n */\n params<Path extends string>(route: RouteDefinition<Path>): RouteParams<Path> | null {\n const match = this.match.value;\n if (match === null || !inChain(match.chain, route)) {\n return null;\n }\n return match.params as RouteParams<Path>;\n }\n\n /** The same, as a cell, for binding a screen's title or fields. */\n observeParams<Path extends string>(route: RouteDefinition<Path>): ComputedCell<RouteParams<Path> | null> {\n return computed(() => this.params(route), { equal: sameParams, label: 'RouterService.params' });\n }\n\n /**\n * A cell a route keeps while its screen does not exist: a scroll\n * offset, a keyboard cursor, the text in a filter field.\n *\n * A screen is built when its route matches and destroyed when it\n * stops matching, so a list rebuilt after Back starts at the top\n * unless somebody remembered where it was. This is where that is\n * kept, and it is why Back returns to the row a person left rather\n * than to the top of the list.\n *\n * const scroll = router.remember(Home, 'scroll', 0);\n * <scrollview scrollY={scroll} modifiers={[scrollPosition({ … })]}>\n *\n * `initial` is used the first time the key is asked for and ignored\n * afterwards. The cell is scoped to the route, so two screens may\n * both call their offset `scroll`.\n *\n * A `null` route is the router's own scope, for the handful of values\n * that belong to the *navigation* rather than to one screen: which\n * element the next transition should morph from is the case both\n * applications have, since the screen that was pressed is destroyed\n * before the screen that arrives is built.\n *\n * This is a facility, not an architecture. The thread model leaves an\n * application's data to the application, and that has not changed:\n * what belongs here is the screen-shaped remainder that exists only\n * to put a screen back where it was. Anything that must survive a\n * reload, or that another part of the application acts on, is still\n * state on a channel.\n */\n remember<T>(route: RouteDefinition | null, key: string, initial: T): InternalState<T> {\n return this.state.cell(route, key, initial);\n }\n\n /** Drops what a route remembered, so its next screen starts fresh. */\n forget(route: RouteDefinition | null): void {\n this.state.forget(route);\n }\n\n /**\n * A shared slot's value, but only while it is the answer to *this*\n * route's own parameters.\n *\n * The case it exists for is a page loaded across the barrier. A\n * channel key that holds \"the track page\" holds whichever track was\n * asked for last, and a screen arriving during a transition asks for\n * its own and is handed the previous one until the answer lands. That\n * is one or two frames of the wrong cover, and it is worse than it\n * looks: the artwork element mounts carrying the previous track's\n * shared name, claims it, and never claims its own, so a second trip\n * between two pages does not animate at all.\n *\n * const track = router.answerFor(Track, page.view.track, {\n * asks: params => `/${params.handle}/${params.slug}`.toLowerCase(),\n * answers: entry => entry.path.toLowerCase()\n * });\n *\n * Two things about it are the router's to know rather than the\n * screen's. It follows the *current* params, so a navigation from one\n * track to another, which keeps the same screen mounted because the\n * chain did not change, asks the new question rather than staying on\n * the one the body read once. And when the route stops matching the\n * cell **keeps what it last held** instead of emptying: a screen is\n * still on screen while it leaves, and a departing page whose artwork\n * blanks for the last frames of its own fade is the flicker this is\n * meant to remove, not a new one to add.\n */\n answerFor<Path extends string, T>(\n route: RouteDefinition<Path>,\n source: Observable<T>,\n keys: RouteAnswer<Path, T>\n ): ComputedCell<T | null> {\n let held: T | null = null;\n return computed(\n read => {\n const params = this.params(route);\n if (params === null) {\n // Not showing: either this screen is on its way out, in which\n // case it keeps its own page, or it was never in.\n return held;\n }\n const value = read(source);\n held =\n value === null || value === undefined\n ? null\n : keys.answers(value as NonNullable<T>) === keys.asks(params)\n ? value\n : null;\n return held;\n },\n { label: `RouterService.answerFor(${route.path})` }\n );\n }\n\n /**\n * Whether a route is in the current chain — true for a layout while\n * any of its children shows, which is what a nav item highlights on.\n */\n isActive(route: RouteDefinition): ComputedCell<boolean> {\n return computed(() => {\n const match = this.match.value;\n return match !== null && inChain(match.chain, route);\n });\n }\n\n /**\n * Resolves a url, runs the guards along the way, and publishes the\n * result.\n *\n * One method for both directions of travel, because a guard runs the\n * same either way — but what a redirect does to the history depends\n * on which direction it came from, and that is the whole of the\n * `replace` bookkeeping below.\n */\n private resolveInto(url: string, options: { push: boolean; replace?: boolean }): void {\n const push = options.push;\n let replace = options.replace ?? false;\n let target = url;\n for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects++) {\n const match = this.resolve(target);\n const verdict = match === null ? true : this.runGuards(match);\n if (verdict === false) {\n // Refused with nowhere else to go: the current url stands. When\n // the refusal was of a url the shell already committed to, the\n // shell is put back where the app actually is.\n if (!push) {\n this.history?.replace(this.url.value);\n }\n return;\n }\n if (verdict === true) {\n this.publish(target, match, { push, replace });\n return;\n }\n target = urlOf(verdict);\n // Where a redirect leaves the history depends on whether the\n // refused url was ever an entry.\n //\n // From inside the app it was not: nothing is written until a\n // navigation settles, so the redirect pushes, and Back returns to\n // the screen the person left. Replacing here was a bug — it\n // overwrote the entry they were standing on, and Back walked out\n // of the app entirely.\n //\n // From the shell it was: the address bar already committed to the\n // refused url before the guard ever saw it, so the redirect\n // replaces, and Back does not land back on a url that will only\n // be refused again.\n replace = push ? replace : true;\n }\n throw new Error(`Navigating to '${url}' redirected more than ${MAX_REDIRECTS} times.`);\n }\n\n /**\n * Writes the result to the cells, and tells the shell what to do\n * with the address bar.\n *\n * A navigation that came from the app pushes; a redirect replaces,\n * so the refused url is not a Back target; and a url the shell\n * itself reported writes nothing back unless a guard changed it, in\n * which case the address bar would otherwise be lying.\n */\n private publish(url: string, match: RouteMatch | null, options: { push: boolean; replace?: boolean }): void {\n const settled = match?.url ?? normalize(url);\n if (options.replace === true) {\n this.history?.replace(settled);\n } else if (options.push) {\n this.history?.push(settled);\n }\n // The match first: a subscriber to `url` that reads `match` (to pick\n // a query param out of it, say) then sees the match for the url it\n // was handed. Written the other way round, it saw the previous one,\n // and an app that synced a selection to `?story=` reset it on every\n // click. A `match` subscriber has the url on the match itself.\n this.match.value = match;\n this.url.value = settled;\n }\n\n /** First declared route whose pattern describes this url wins. */\n private resolve(url: string): RouteMatch | null {\n const { path, query } = parseUrl(url);\n const segments = pathSegments(path);\n for (const route of this.routes) {\n const params = matchPattern(route.segments, segments);\n if (params !== null) {\n return { route, chain: route.chain, params, query, path, url: formatUrl(path, query) };\n }\n }\n if (this.notFound === undefined) {\n return null;\n }\n return {\n route: this.notFound,\n chain: this.notFound.chain,\n params: {},\n query,\n path,\n url: formatUrl(path, query)\n };\n }\n\n /** Guards run outermost first: a layout refuses before its children. */\n private runGuards(match: RouteMatch): boolean | RouteTarget {\n for (const route of match.chain) {\n if (route.guard === undefined) {\n continue;\n }\n const verdict = route.guard({ params: match.params as never, query: match.query, url: match.url });\n if (verdict !== true) {\n return verdict;\n }\n }\n return true;\n }\n}\n\n/**\n * Separates the params from the options, whichever shape the rest\n * tuple arrived in.\n */\nfunction splitGoArgs(args: readonly unknown[]): [Readonly<Record<string, string>>, NavigateOptions] {\n const first = args[0];\n if (first === undefined) {\n return [{}, {}];\n }\n if (isNavigateOptions(first)) {\n return [{}, first];\n }\n return [first as Readonly<Record<string, string>>, (args[1] as NavigateOptions | undefined) ?? {}];\n}\n\n/**\n * A params-less route's only argument is its options, and the two are\n * both plain objects at runtime, so they are told apart by their keys.\n * An empty object is either, and behaves the same as either.\n */\nfunction isNavigateOptions(value: unknown): value is NavigateOptions {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n return Object.keys(value).every(key => key === 'query' || key === 'replace');\n}\n\nfunction urlOf(target: RouteTarget): string {\n return formatUrl(buildPath(target.route.path, target.params), target.query);\n}\n\nfunction normalize(url: string): string {\n const { path, query } = parseUrl(url);\n return formatUrl(path, query);\n}\n\n/**\n * Params compared by value, so a navigation that lands on the same\n * route with the same params emits nothing.\n *\n * Typed as `object` because `RouteParams<Path>` is a mapped type and\n * not assignable to `Record<string, string>`; the values are strings\n * either way, since that is all a url segment can hold.\n */\nfunction sameParams(a: object | null, b: object | null): boolean {\n if (a === null || b === null) {\n return a === b;\n }\n const left = a as Record<string, string>;\n const right = b as Record<string, string>;\n const keys = Object.keys(left);\n return keys.length === Object.keys(right).length && keys.every(key => left[key] === right[key]);\n}\n\n/**\n * Whether a route is in a matched chain: the same object, or a route\n * declared with the same path.\n *\n * Identity alone was the original test, and it is right for a table\n * loaded once. It fails silently under a dev server's hot replacement:\n * a route table that imports its screens, with screens that import the\n * table to read their params, is an import cycle, and after a hot edit\n * Vite can serve the two sides as `routes.tsx` and `routes.tsx?t=…`.\n * The table then loads twice, the router holds one copy's route objects\n * and the screens hold the other's, and every `params(route)` answers\n * null, so a team page quietly showed the default team. Matching is by\n * path already, so two routes with one path are one route to the url.\n */\nfunction inChain(chain: readonly RouteDefinition[], route: RouteDefinition): boolean {\n return chain.some(link => link === route || link.path === route.path);\n}\n","import type { UiNode, UiFindController } from 'gesso-core';\nimport { internalState } from '../InternalState';\n\n/**\n * The find session, as a store components can inject.\n *\n * `UiFindController` does the searching, but it lives in the runtime\n * and a component cannot reach it — components reach the world through\n * stores, as they do for the clipboard (`ShellService`) and overlays.\n * This is the reactive face of it: cells a find bar binds to, actions\n * it dispatches.\n *\n * The bar itself is the app's. The framework knows only that a session\n * is open, which is what makes Ctrl/Cmd+F and Escape mean find; what a\n * find bar looks like is a component, and belongs to F3.\n */\nexport class FindService {\n /** Whether a find session is running; a bar shows itself for this. */\n readonly open = internalState(false);\n /** The query the matches are for. */\n readonly query = internalState('');\n readonly matchCount = internalState(0);\n /** Which match is active, 1-based for display, or 0 when there is none. */\n readonly activeMatch = internalState(0);\n\n private controller: UiFindController | null = null;\n private detach: (() => void) | null = null;\n /**\n * The query field, kept here as well as on the controller: the app's\n * tree is built — and its refs fire — before the runtime installs the\n * controller, so the node has to wait for one.\n */\n private field: UiNode | null = null;\n\n /** Installed by the runtime; without one every action is a no-op. */\n setController(controller: UiFindController | null): void {\n this.detach?.();\n this.detach = null;\n this.controller = controller;\n if (controller === null) {\n return;\n }\n this.detach = controller.onChange(() => this.sync(controller));\n controller.setField(this.field);\n this.sync(controller);\n }\n\n /**\n * Registers the bar's query field, so opening a session puts the\n * caret in it. Pass it as the field's `ref`; null when it unmounts.\n */\n setField(node: UiNode | null): void {\n this.field = node;\n this.controller?.setField(node);\n }\n\n /** Starts a session. The app shows its bar; the field takes the caret. */\n openFind(): void {\n this.controller?.open();\n }\n\n /** Ends the session and drops the highlights. */\n close(): void {\n this.controller?.close();\n }\n\n /** Searches for `query`, activating its first match. */\n search(query: string, matchCase = false): void {\n this.controller?.search(query, { matchCase });\n }\n\n /** Moves to the next match, wrapping around. */\n next(): void {\n this.controller?.next();\n }\n\n /** Moves to the previous match, wrapping around. */\n previous(): void {\n this.controller?.previous();\n }\n\n /**\n * Re-runs the current query because the content changed under it.\n * An app that edits text while a find is open calls this.\n */\n refresh(): void {\n this.controller?.refresh();\n }\n\n private sync(controller: UiFindController): void {\n this.open.value = controller.isOpen;\n this.query.value = controller.query;\n this.matchCount.value = controller.matchCount;\n this.activeMatch.value = controller.activeIndex + 1;\n }\n}\n","import type { CaretRect, UiEditingController, UiNode, UiTextPosition } from 'gesso-core';\n\n/**\n * The part of a text field an application has to ask about.\n *\n * Almost everything about an editable is reachable already: its text\n * is a property, its model is on the node, and its changes arrive as\n * events. Its *geometry* is not, and cannot be — where the caret sits\n * depends on the paragraph as it was laid out, which needs the\n * measurer, the resolved paint and the layout record. An application\n * that re-measured the text to work it out would be a second measurer\n * that must never disagree with the engine's, which is the kind of\n * pair that agrees until the day it does not.\n *\n * So it asks. This is the asking, and it is a service rather than a\n * free function because the controller lives in the input stack and\n * is out of a component's reach without something in front of it —\n * the same reason `FocusService` exists.\n *\n * What it is for: putting something *at* the caret. An autocomplete\n * list under the word being typed, a hint about the argument being\n * filled in, an error shown against the character it is about.\n */\nexport class EditingService {\n private controller: UiEditingController | null = null;\n\n /** Installed by the runtime; without one there is nothing to ask. */\n setController(controller: UiEditingController | null): void {\n this.controller = controller;\n }\n\n /**\n * Where the caret is inside a field, in the field's own coordinates.\n *\n * Relative to the node's border-box origin, with the field's own\n * scroll already applied: a popup placed under the caret adds the\n * node's position on screen and nothing else.\n *\n * `offset` asks about a caret at another character than the one the\n * caret is at: the start of the word being completed, so its list\n * stays put as the word grows. With an overlay's `anchorRect` and the\n * field as its anchor, the list follows the field from then on.\n *\n * Null when the node is not an editable, when it has not been laid\n * out yet, or before the runtime has wired the controller — all\n * three of which are \"ask again next frame\" rather than errors, and\n * all three of which happen during the frame a field first appears.\n */\n caretRectOf(node: UiNode | null, offset?: number): CaretRect | null {\n if (node === null || this.controller === null) {\n return null;\n }\n return this.controller.caretRectOf(node, offset);\n }\n\n /**\n * The caret position nearest a point in a field, the point given as a\n * pointer event gives it: where a press there would put the caret.\n *\n * What a field opened over text the person pointed at uses to start\n * the caret where they pointed: ask once the field is laid out, then\n * `select` that offset. Null when the node is not an editable, when it\n * has not been laid out yet, or before the runtime has wired the\n * controller: \"ask again next frame\", as for `caretRectOf`.\n */\n offsetNear(node: UiNode | null, x: number, y: number): number | null {\n if (node === null || this.controller === null) {\n return null;\n }\n return this.controller.offsetNear(node, x, y);\n }\n\n /**\n * Selects from `anchor` to `focus` and focuses the field the focus is\n * in: within one field, or across the fields of an editing group (see\n * `UiEditingGroup`). What an editor does after a command over a\n * selection (making it bold, say) to leave it selected, since its\n * fields can only select their own text.\n *\n * False when the two ends aren't editables of one group, or before\n * the runtime has wired the controller.\n */\n select(anchor: UiTextPosition, focus: UiTextPosition): boolean {\n return this.controller?.select(anchor, focus) ?? false;\n }\n}\n","import type { UiNode } from 'gesso-core';\n\n/**\n * Scrolling a node into view, for a component that moves a highlight\n * rather than focus.\n *\n * Focus moved from the keyboard already brings its node into view. A\n * highlight doesn't move focus: a combobox's arrows walk its list while\n * the caret stays in the field, and a grid's cursor can do the same.\n * The list has to follow the highlight all the same, and only the\n * runtime knows where the scroll containers above a node are and how\n * far each has to move. This is the asking.\n */\nexport class ScrollService {\n private scroller: ((node: UiNode, padding: number) => void) | null = null;\n\n /** Installed by the runtime; without one nothing scrolls. */\n setScroller(scroller: ((node: UiNode, padding: number) => void) | null): void {\n this.scroller = scroller;\n }\n\n /**\n * Scrolls every container above `node` just enough that it's inside\n * the container's viewport, `padding` pixels from the nearest edge.\n * Nothing moves when it's visible already. Before the node has been\n * laid out there is nowhere to scroll it to, and nothing happens.\n */\n scrollIntoView(node: UiNode, padding = 8): void {\n this.scroller?.(node, padding);\n }\n}\n","import {\n DefaultImageResolver,\n DefaultSvgResolver,\n type SvgResolver,\n DefaultVideoResolver,\n type ImageResolver,\n type VideoResolver,\n IconRasterizer\n} from 'gesso-core';\n\n/**\n * What an application hands a runtime so its pictures come from\n * somewhere other than the defaults.\n *\n * All three are optional and independent: an app that fetches its\n * images through its own stack still gets the default rasteriser and\n * the default decoder. Whatever is left out, the runtime builds and\n * owns; whatever is passed belongs to the caller, and the runtime will\n * not dispose it. See `MediaService` for that rule in full.\n */\nexport interface MediaOptions {\n /** Where an `Image` finds its bitmap. */\n resolver?: ImageResolver;\n /** Where an `Icon` finds its raster. */\n rasterizer?: IconRasterizer;\n /** Where an `Image` reads an SVG source. */\n vectors?: SvgResolver;\n /** Where a `Video` finds its decoder. */\n videoResolver?: VideoResolver;\n}\n\n/**\n * Where an `Image` finds its bitmap, an `Icon` its raster and a\n * `Video` its decoder.\n *\n * A store for the same reason `ShellService` is one: a component reaches\n * the world outside the graph through an injected store and never\n * through a module-level singleton. It matters more here than usual,\n * because the cache has to be per runtime — two runtimes in one worker\n * (the playground has several) must not share a bitmap that one of\n * them is about to close.\n *\n * There are no actions on it: resolving is a promise a modifier awaits,\n * not a state transition, and modelling a decode as a dispatched action\n * would put every thumbnail of a scrolling list into the patch stream\n * for no consumer.\n */\nexport class MediaService {\n private imageResolver: ImageResolver = new DefaultImageResolver();\n private iconRasterizer = new IconRasterizer();\n private svgResolver: SvgResolver = new DefaultSvgResolver();\n private videoResolver: VideoResolver | null = null;\n /**\n * Whether each of the three is the service's to dispose.\n *\n * True for the ones built here, false once an application has\n * supplied its own: a caller that passes a resolver usually keeps a\n * reference to it and may well share it between runtimes, so\n * closing it when one of them shuts down would take the other's\n * bitmaps with it. The same rule for all three, because the\n * question is the same for all three.\n */\n private ownsResolver = true;\n private ownsRasterizer = true;\n private ownsVideoResolver = true;\n\n get images(): ImageResolver {\n return this.imageResolver;\n }\n\n /** Where an `Image` reads an SVG source, which it draws as vectors rather than decoding. */\n get vectors(): SvgResolver {\n return this.svgResolver;\n }\n\n /** The same seam, for SVG. A read file holds no pixels, so there is nothing to dispose either way. */\n setSvgResolver(resolver: SvgResolver): void {\n this.svgResolver = resolver;\n }\n\n get icons(): IconRasterizer {\n return this.iconRasterizer;\n }\n\n /**\n * Built on first use rather than in the field, because a decoder is\n * the one thing here that an app which never plays a video should\n * not be paying for — and because `canDecodeVideo()` is false on\n * plenty of threads that render perfectly well.\n */\n get videos(): VideoResolver {\n if (this.videoResolver === null) {\n this.videoResolver = new DefaultVideoResolver();\n this.ownsVideoResolver = true;\n }\n return this.videoResolver;\n }\n\n /**\n * Replaces the resolver — for a test double, for an app that fetches\n * through its own stack, or for one that has measured a reason to\n * decode in a worker of its own. See `ImageResolver`'s docblock for\n * why the default does not spawn one.\n */\n setResolver(resolver: ImageResolver): void {\n if (this.ownsResolver) {\n this.imageResolver.dispose();\n }\n this.imageResolver = resolver;\n this.ownsResolver = false;\n }\n\n /** The same seam, for video. */\n setVideoResolver(resolver: VideoResolver): void {\n if (this.ownsVideoResolver) {\n this.videoResolver?.dispose();\n }\n this.videoResolver = resolver;\n this.ownsVideoResolver = false;\n }\n\n /** The same seam, for icons. */\n setRasterizer(rasterizer: IconRasterizer): void {\n if (this.ownsRasterizer) {\n this.iconRasterizer.dispose();\n }\n this.iconRasterizer = rasterizer;\n this.ownsRasterizer = false;\n }\n\n /** Releases every decoded bitmap, for a runtime shutting down. */\n dispose(): void {\n if (this.ownsResolver) {\n this.imageResolver.dispose();\n }\n if (this.ownsVideoResolver) {\n this.videoResolver?.dispose();\n }\n if (this.ownsRasterizer) {\n this.iconRasterizer.dispose();\n }\n }\n}\n","import { bumpFontStack, registerFontStack } from 'gesso-core';\n\n/**\n * One face of a family: where its bytes come from and what it covers.\n * The descriptors are CSS `@font-face` descriptors, in their CSS forms\n * (`weight: '100 900'` for a variable face).\n */\nexport interface FontFaceDeclaration {\n /** A URL the worker can fetch, or the bytes themselves. */\n source: string | ArrayBuffer;\n weight?: string | number;\n style?: 'normal' | 'italic' | 'oblique';\n stretch?: string;\n unicodeRange?: string;\n display?: 'auto' | 'block' | 'swap' | 'fallback' | 'optional';\n}\n\n/**\n * A family an application declares: its faces, and the families tried\n * for glyphs it lacks and drawn with until its files arrive.\n */\nexport interface FontFamilyDeclaration {\n family: string;\n faces: readonly FontFaceDeclaration[];\n /** Default `['sans-serif']`. */\n fallback?: readonly string[];\n}\n\n/** Where a family stands: `loading` until every face settled, then `loaded`, or `error` if any face failed. */\nexport type FontFamilyStatus = 'undeclared' | 'unavailable' | 'loading' | 'loaded' | 'error';\n\n/** The part of a `FontFace` this service uses; a test passes a double. */\nexport interface FontFaceLike {\n readonly family: string;\n load(): Promise<unknown>;\n}\n\n/** The part of the platform this service needs: a font set to add faces to, and a way to make one. */\nexport interface FontHost {\n /** `self.fonts` in a worker, `document.fonts` in a window; undefined where there is neither. */\n readonly fonts: { add(face: FontFaceLike): unknown } | undefined;\n createFace(family: string, source: string | ArrayBuffer, descriptors: FontFaceDescriptors): FontFaceLike;\n}\n\n/**\n * Loads an application's declared fonts into the thread that draws.\n *\n * A web font used by the DOM is loaded by the document, and a worker\n * shares none of it: the render worker has its own `FontFaceSet`, and a\n * canvas in it draws with a fallback until a face is added there. This\n * service is where that happens. It creates a `FontFace` per declared\n * face, adds it to the thread's set, and tells the runtime as each one\n * arrives so that every measurement made with the fallback is thrown\n * away and the tree is laid out again in the face that was meant.\n *\n * The fallback stack is registered with `gesso-core` at declaration,\n * before any face loads, so text names the family alone and draws in\n * the fallback meanwhile, as `font-display: swap` would. When a face\n * arrives the stack is bumped, which changes every font string built\n * from it: Chrome keeps a worker's resolution of a font string for\n * good, so only a string it has never seen picks the new face up.\n *\n * Faces are shared across runtimes on a thread, as the font set they\n * live in is: a face created once for a URL is reused by the next\n * runtime that declares it, and disposing a runtime never removes a\n * face, because another runtime in the same worker may be drawing with\n * it. Injected like any other runtime service, so a component can ask\n * `statusOf(family)` and hold its text until the face is in.\n */\nexport class FontService {\n private readonly statuses = new Map<string, FontFamilyStatus>();\n private readonly pending = new Map<string, number>();\n private readonly failed = new Set<string>();\n private readonly batches: Promise<void>[] = [];\n private listener: ((family: string) => void) | null = null;\n private disposed = false;\n\n /**\n * Declares families and starts loading their faces. Called by the\n * runtime with what `useFonts` was given; may be called again later\n * for more.\n */\n declare(families: readonly FontFamilyDeclaration[], host: FontHost = platformFontHost()): void {\n const loads: Promise<void>[] = [];\n for (const declaration of families) {\n registerFontStack(declaration.family, declaration.fallback ?? ['sans-serif']);\n if (host.fonts === undefined) {\n this.statuses.set(declaration.family, 'unavailable');\n continue;\n }\n this.statuses.set(declaration.family, 'loading');\n this.pending.set(declaration.family, (this.pending.get(declaration.family) ?? 0) + declaration.faces.length);\n if (declaration.faces.length === 0) {\n this.settle(declaration.family);\n }\n for (const face of declaration.faces) {\n loads.push(this.loadFace(declaration.family, face, host));\n }\n }\n this.batches.push(Promise.all(loads).then(() => undefined));\n }\n\n /** Resolves once every declared face has loaded or failed. */\n get ready(): Promise<void> {\n return Promise.all(this.batches).then(() => undefined);\n }\n\n statusOf(family: string): FontFamilyStatus {\n return this.statuses.get(family) ?? 'undeclared';\n }\n\n get families(): readonly string[] {\n return [...this.statuses.keys()];\n }\n\n /** Called with a family each time one of its faces finishes loading or failing. */\n setListener(listener: ((family: string) => void) | null): void {\n this.listener = listener;\n }\n\n /** Stops notifying. The faces stay in the thread's font set for whoever else draws with them. */\n dispose(): void {\n this.disposed = true;\n this.listener = null;\n }\n\n private async loadFace(family: string, declaration: FontFaceDeclaration, host: FontHost): Promise<void> {\n try {\n const face = faceFor(family, declaration, host);\n host.fonts!.add(face);\n await face.load();\n // The family's font strings must change now, or a worker that\n // measured in the fallback keeps doing so; see `bumpFontStack`.\n bumpFontStack(family);\n } catch {\n this.failed.add(family);\n }\n this.settle(family);\n }\n\n private settle(family: string): void {\n const left = (this.pending.get(family) ?? 1) - 1;\n this.pending.set(family, left);\n if (left <= 0) {\n this.statuses.set(family, this.failed.has(family) ? 'error' : 'loaded');\n }\n if (!this.disposed) {\n this.listener?.(family);\n }\n }\n}\n\n/**\n * Faces already created on this thread, by family and URL, so two\n * runtimes declaring the same font share one `FontFace` and the set\n * is not asked to hold it twice. Faces from bytes are not shared: two\n * buffers are two fonts.\n */\nconst createdFaces = new Map<string, FontFaceLike>();\n\nfunction faceFor(family: string, declaration: FontFaceDeclaration, host: FontHost): FontFaceLike {\n const descriptors = descriptorsOf(declaration);\n if (typeof declaration.source !== 'string') {\n return host.createFace(family, declaration.source, descriptors);\n }\n const key = `${family}\\0${declaration.source}\\0${JSON.stringify(descriptors)}`;\n let face = createdFaces.get(key);\n if (face === undefined) {\n face = host.createFace(family, `url(${JSON.stringify(declaration.source)})`, descriptors);\n createdFaces.set(key, face);\n }\n return face;\n}\n\nfunction descriptorsOf(declaration: FontFaceDeclaration): FontFaceDescriptors {\n const descriptors: FontFaceDescriptors = {};\n if (declaration.weight !== undefined) {\n descriptors.weight = String(declaration.weight);\n }\n if (declaration.style !== undefined) {\n descriptors.style = declaration.style;\n }\n if (declaration.stretch !== undefined) {\n descriptors.stretch = declaration.stretch;\n }\n if (declaration.unicodeRange !== undefined) {\n descriptors.unicodeRange = declaration.unicodeRange;\n }\n if (declaration.display !== undefined) {\n descriptors.display = declaration.display;\n }\n return descriptors;\n}\n\n/** The thread's own font set and `FontFace`, where it has them. */\nexport function platformFontHost(): FontHost {\n const scope = globalThis as {\n fonts?: FontFaceSet;\n document?: { fonts?: FontFaceSet };\n FontFace?: typeof FontFace;\n };\n const fonts = scope.fonts ?? scope.document?.fonts;\n return {\n fonts: fonts !== undefined && typeof scope.FontFace === 'function' ? fonts : undefined,\n createFace: (family, source, descriptors) => new scope.FontFace!(family, source, descriptors)\n };\n}\n","import type { Size, TextMeasureRequest, TextMeasurer } from 'gesso-core';\n\n/**\n * How big a string would be, if it were drawn.\n *\n * Layout measures text constantly and an application could not ask —\n * the measurer is the runtime's, built for the layout engine, and\n * nothing handed it out. So anything that needs a width before there\n * is a node to read it from had no answer: a column autofitted to its\n * contents, a popover sized to the longest option, an axis label\n * decided before a chart is drawn, a truncation chosen rather than\n * discovered.\n *\n * The alternative every application reaches for is mounting the text\n * invisibly and reading its box back a frame later, which is a frame\n * of latency, a node in the tree and a race with the layout it is\n * trying to inform.\n *\n * **It is the runtime's own measurer**, not a second one. A width\n * from here is the width the layout engine would use, including the\n * cache it has already warmed and the invalidation it gets when a\n * declared face finishes loading — a service with its own measurer\n * would answer with the fallback font long after the real one had\n * arrived.\n */\nexport class TextService {\n private measurer: TextMeasurer | null = null;\n\n /**\n * Called by the runtime with the measurer it lays out through.\n *\n * Not for applications, on the terms `ShellService.applyColorScheme`\n * sets: the thread that draws is the only thing that knows how, and\n * a measurer an application could also install would be one the\n * next layout disagrees with.\n */\n setMeasurer(measurer: TextMeasurer | null): void {\n this.measurer = measurer;\n }\n\n /** Whether there is a measurer yet. False before the first frame. */\n get ready(): boolean {\n return this.measurer !== null;\n }\n\n /**\n * The size a string would take.\n *\n * Zero when there is no measurer, which is what a caller asking\n * before the runtime has drawn anything gets — and the honest\n * answer, since the font it would be measured in is not resolved\n * yet.\n */\n measure(request: TextMeasureRequest): Size {\n return this.measurer?.measure(request) ?? { width: 0, height: 0 };\n }\n\n /**\n * The width of one line, which is what sizing to content asks for.\n *\n * No `maxWidth`, so nothing wraps: the question is how wide the\n * text *wants* to be, and a wrapped answer would be the width it\n * was given rather than the width it needs.\n */\n widthOf(text: string, style: Omit<TextMeasureRequest, 'text' | 'maxWidth'>): number {\n return this.measure({ ...style, text, wrap: 'none' }).width;\n }\n}\n","import { EMPTY, type Observable } from 'rxjs';\n\nimport {\n AnimationDriver,\n UiSpring,\n createTween,\n durationIn,\n easingIn,\n springIn,\n type AnimatedCell,\n type UiAnimation,\n type UiEasing,\n type UiReducedMotionPolicy,\n defaultMotion,\n type UiDurationToken,\n type UiEasingToken,\n type UiMotion,\n type UiSpringSpec,\n type UiSpringToken,\n type UiClock\n} from 'gesso-core';\nimport { internalState } from '../InternalState';\n\n/** A duration named from the motion vocabulary, or milliseconds outright. */\nexport type UiDuration = UiDurationToken | number;\n/** A curve named from the motion vocabulary, or a function outright. */\nexport type UiEasingChoice = UiEasingToken | UiEasing;\n\nexport interface AnimateOptions {\n /** Defaults to `normal`. */\n duration?: UiDuration;\n /** Defaults to `standard`. */\n easing?: UiEasingChoice;\n /** Sample no more often than this; see `UiAnimationOptions.stepMs`. */\n stepMs?: number;\n /** Run forever, restarting each time. A repeating animation never completes. */\n repeat?: boolean;\n /** What a reduced-motion preference does to it. Defaults to `snap`. */\n reducedMotion?: UiReducedMotionPolicy;\n /** Wait this long before the first sample; see `UiAnimationOptions.delay`. */\n delay?: number;\n /**\n * The clock it runs on, where it is drawn: `ctx.environment(UiEnvironmentKeys.clock)`\n * in a component, `clockOf(node)` from a node. Real time without one. See `UiClock`.\n */\n clock?: UiClock | null;\n}\n\nexport interface SpringOptions {\n /** A named spring, or one given outright. Defaults to `snappy`. */\n spring?: UiSpringToken | UiSpringSpec;\n /** Overrides on top of the chosen spring, so `{ stiffness, damping }` alone works. */\n stiffness?: number;\n damping?: number;\n mass?: number;\n /** Units per second at the start; a gesture hands over what it ended with. */\n velocity?: number;\n restDelta?: number;\n reducedMotion?: UiReducedMotionPolicy;\n /** Wait this long before the first sample; see `UiAnimationOptions.delay`. */\n delay?: number;\n /**\n * The clock it runs on, where it is drawn: `ctx.environment(UiEnvironmentKeys.clock)`\n * in a component, `clockOf(node)` from a node. Real time without one. See `UiClock`.\n */\n clock?: UiClock | null;\n}\n\n/**\n * Animation, as a store components can inject.\n *\n * A store for the reason `MediaService` is one, and it is the same\n * reason: the running set has to be **per runtime**. The playground\n * runs several runtimes in one worker, and a module-level driver would\n * tick a disposed runtime's cells — which is why `animate(cell, to)`\n * is a method here rather than the free function first\n * sketched. A free function has nowhere to find its driver, and the\n * framework's answer to \"where does a component reach the world\" has\n * been an injected store since `ShellService`.\n *\n * The driver itself lives in the runtime, beside the layout engine and\n * the focus manager, because the runtime is what advances it: the\n * `ticks` phase is the driver's only caller. This store is the handle\n * on it, exactly as `FocusService` is the handle on `UiFocusManager`.\n */\nexport class AnimationService {\n /**\n * Whether the person using this app has asked for less motion.\n *\n * Bindable, so a component can decide not to render a decorative\n * movement at all rather than running one that snaps. The runtime\n * sets it from the shell; see `GessoRuntime.setReducedMotion`.\n */\n readonly reducedMotion = internalState(false);\n\n private driver: AnimationDriver | null = null;\n private motionVocabulary: UiMotion = defaultMotion;\n\n /** Installed by the runtime. Without one, every animation snaps to its target. */\n setDriver(driver: AnimationDriver | null): void {\n this.driver = driver;\n if (driver !== null) {\n driver.setReducedMotion(this.reducedMotion.value);\n }\n }\n\n /**\n * Replaces the durations, easings and springs the tokens name.\n *\n * The seam for an application with its own feel. It is here rather\n * than on `UiTheme` because a theme value resolves per node and an\n * animation drives a cell, which has no node — see `UiMotion`.\n */\n setMotion(motion: UiMotion): void {\n this.motionVocabulary = motion;\n this.motionListener?.(motion);\n }\n\n private motionListener: ((motion: UiMotion) => void) | null = null;\n\n /**\n * Called by the runtime, which provides the vocabulary at its root so\n * a name in a `transition` or a `motion` state resolves to it too; a\n * part of the tree may provide its own over it (`UiEnvironmentKeys.motion`).\n */\n setMotionListener(listener: ((motion: UiMotion) => void) | null): void {\n this.motionListener = listener;\n }\n\n get motion(): UiMotion {\n return this.motionVocabulary;\n }\n\n /**\n * Turns reduced motion on or off for this runtime.\n *\n * The runtime calls it when the shell reports the platform's\n * `prefers-reduced-motion`, which is where the answer normally comes\n * from. It is public because an application may legitimately offer\n * its own motion setting — many do — and because a person who wants\n * less motion in *this* app should not have to change an OS\n * preference to get it. The last caller wins; there is no priority\n * between the platform's answer and the app's.\n */\n applyReducedMotion(reduced: boolean): void {\n if (this.reducedMotion.value === reduced) {\n return;\n }\n this.reducedMotion.value = reduced;\n this.driver?.setReducedMotion(reduced);\n }\n\n /**\n * Moves a cell to a value over time, and returns what it writes.\n *\n * The cell is driven whether or not anyone subscribes — it is the\n * animation's purpose, not a side effect of observation — so the\n * returned Observable is for watching and for knowing when it is\n * over. It completes when the animation stops driving the cell,\n * which includes being superseded by the next `animate` on the same\n * cell; a caller that needs to know whether it arrived reads the\n * cell.\n */\n animate<T>(cell: AnimatedCell<T>, to: T, options: AnimateOptions = {}): Observable<T> {\n const duration = this.resolveDuration(options.duration ?? 'normal');\n const tween = createTween(cell, to, {\n duration,\n easing: this.resolveEasing(options.easing ?? 'standard'),\n stepMs: options.stepMs,\n repeat: options.repeat,\n reducedMotion: options.reducedMotion,\n delay: options.delay\n });\n if (tween === undefined) {\n // Not blendable: write it and say so. See `interpolatorFor`.\n cell.value = to;\n return EMPTY as Observable<T>;\n }\n const driver = this.driver;\n if (driver === null) {\n tween.snap();\n return tween.values;\n }\n return driver.start(tween, options.clock ?? null);\n }\n\n /**\n * Pulls a number towards a value on a spring, with no duration.\n *\n * What a gesture wants: retargeting mid-flight keeps the velocity,\n * so a flick that changes direction bends rather than restarting.\n */\n spring(cell: AnimatedCell<number>, to: number, options: SpringOptions = {}): Observable<number> {\n const base = springIn(this.motionVocabulary, options.spring);\n const driver = this.driver;\n const previous = driver?.animationFor(cell);\n const animation = new UiSpring(cell, to, {\n spring: {\n stiffness: options.stiffness ?? base.stiffness,\n damping: options.damping ?? base.damping,\n mass: options.mass ?? base.mass\n },\n velocity: options.velocity ?? (previous instanceof UiSpring ? previous.currentVelocity : 0),\n restDelta: options.restDelta,\n reducedMotion: options.reducedMotion,\n delay: options.delay\n });\n // The motion goes on from where the one it replaces was, in time as\n // well as in place and speed; see `continueFrom`.\n if (previous instanceof UiSpring) {\n animation.continueFrom(previous);\n }\n if (driver === null) {\n animation.snap();\n return animation.values;\n }\n return driver.start(animation, options.clock ?? null);\n }\n\n /** Stops whatever is driving a cell, leaving it where it stands. */\n stop<T>(cell: AnimatedCell<T>): boolean {\n return this.driver?.stop(cell) ?? false;\n }\n\n /**\n * What is driving a cell, if anything.\n *\n * For a caller that needs to know where a movement is *going* rather\n * than where it is — a scroll adding a wheel notch has to add it to\n * the destination, or every notch after the first travels less than\n * it asked for. `spring` uses the same lookup internally to carry\n * velocity across a retarget.\n */\n animationFor<T>(cell: AnimatedCell<T>): UiAnimation<T> | undefined {\n return this.driver?.animationFor(cell);\n }\n\n private resolveDuration(duration: UiDuration): number {\n return durationIn(this.motionVocabulary, duration);\n }\n\n private resolveEasing(easing: UiEasingChoice): UiEasing {\n return easingIn(this.motionVocabulary, easing);\n }\n}\n","import type { LayoutExplanation, UiNode } from 'gesso-core';\n\nimport type { UiNodeReport } from './NodeReport';\n\n/**\n * What is on the screen, as a component can ask: the nodes under a\n * point, and what each one is.\n *\n * The inspector already answers both, for the devtools panel, from the\n * runtime's hit tester and its node report. A tool built in Gesso that\n * points at Gesso needs the same answers from inside the tree: a design\n * editor whose canvas holds live frames, where a click picks the box\n * under it instead of pressing it and says which line wrote it. So the\n * runtime puts them here, as it puts frames in `FrameService`.\n * Injected like any service: `ctx.inject(InspectionService)`.\n */\nexport class InspectionService {\n private host: InspectionHost | null = null;\n\n /** Called by the runtime; not for components. */\n attach(host: InspectionHost): void {\n this.host = host;\n }\n\n /**\n * Every node under a point, topmost first, in the coordinates a\n * pointer event carries: the one a press would reach, then each one\n * it is painted over. Empty before the first frame.\n */\n nodesAt(x: number, y: number): readonly UiNode[] {\n return this.host?.nodesAt(x, y) ?? [];\n }\n\n /**\n * What the node is, as the inspector reports it: its box, its props,\n * the components that rendered it and, compiled for development, the\n * line that wrote it (`source`) and where each of those components\n * was used (`owners[].usedAt`).\n */\n inspect(node: UiNode): UiNodeReport {\n if (this.host === null) {\n throw new Error('InspectionService has nothing to inspect before the runtime has built its tree.');\n }\n return this.host.inspect(node);\n }\n\n /**\n * Why the node has the size it has, as the layout decided it: for\n * each axis, the rule that fixed it (`decidedBy`: its own length, a\n * flex share, a stretch across its parent, its content, a clamp) and\n * the sentences that say so. What a design tool needs to tell an\n * element that fills its parent from one that only happens to be as\n * wide, which the box alone cannot. Null before the node is laid out.\n */\n explain(node: UiNode): LayoutExplanation | null {\n return this.host?.explain?.(node) ?? null;\n }\n\n /**\n * Where the node is drawn, as `inspect(node).box` reports it, without\n * the rest of the report: for an editor that reads the boxes of many\n * nodes as its view moves (a minimap of a board of frames), where\n * building each node's props, owners and source was most of the cost.\n */\n box(node: UiNode): { readonly x: number; readonly y: number; readonly width: number; readonly height: number } {\n if (this.host === null) {\n throw new Error('InspectionService has nothing to measure before the runtime has built its tree.');\n }\n return this.host.box?.(node) ?? this.host.inspect(node).box;\n }\n\n /**\n * Where the node is seen on the screen: `box` with every transform\n * above it applied, so a node in a frame drawn at half its size is\n * half its size, where it is drawn. What an overlay laid over the\n * live tree is placed by (an outline on the component under the\n * pointer). `box` keeps the untransformed box, which is where the\n * node is in the layout: what an editor reading a zoomed board in\n * the board's own coordinates (a minimap) needs.\n */\n screenBox(node: UiNode): { readonly x: number; readonly y: number; readonly width: number; readonly height: number } {\n if (this.host === null) {\n throw new Error('InspectionService has nothing to measure before the runtime has built its tree.');\n }\n return this.host.screenBox?.(node) ?? this.box(node);\n }\n\n /**\n * Draws a node as though it declared `value` for `property`, until\n * the preview is ended or the node leaves the tree.\n *\n * What a design editor shows while a value is being chosen: a padding\n * dragged or a colour hovered in a list is drawn at once, on the\n * live tree, and only written to the source when the designer lets go.\n * It wins over the element's own value and over its modifiers, and\n * ending it restores whichever of those was there, inheritance\n * included. The name and value are a prop's, as JSX would write them.\n */\n preview(node: UiNode, property: string, value: unknown): void {\n this.host?.preview?.(node, property, value);\n }\n\n /** Ends a preview of one property, or of every property previewed on the node. */\n endPreview(node: UiNode, property?: string): void {\n this.host?.endPreview?.(node, property);\n }\n\n /**\n * Draws a component as though its parent passed `inputs`, over what\n * the parent did pass, until the preview is ended or the component\n * leaves the tree. The component is named by its anchor, as\n * `inspect(node).owners[].anchorId` gives it.\n *\n * What a design editor shows when a component on a played screen is\n * jumped to one of its states (Filled, Error, Disabled): the same\n * instance, still live, given that state's inputs, and the inputs it\n * was written with back when the preview ends. A later preview of the\n * same component replaces this one. The names and values are props',\n * as JSX would write them. An anchor no component holds is ignored.\n */\n previewInputs(anchorId: string, inputs: Readonly<Record<string, unknown>>): void {\n this.host?.previewInputs?.(anchorId, inputs);\n }\n\n /** Ends a preview of a component's inputs: its parent's are its own again. */\n endInputPreview(anchorId: string): void {\n this.host?.previewInputs?.(anchorId, null);\n }\n\n /**\n * Presses a node, or replaces the text of the field it is, as a\n * person would: a click at the middle of where it is drawn, which\n * its handlers and every handler above it get, or the edit a\n * keyboard makes once it has focus. The path the accessibility\n * mirror's actions take, for any node in the tree, record or not.\n *\n * What a design editor's agent does to play a prototype in its live\n * frames: a row pressed, an email typed, by a node it found with\n * `nodesAt` or by walking the tree. False when it did not take: a\n * node no longer in the tree, or a field that could not have focus\n * because an open dialog's trap holds it, or takes no text.\n */\n act(node: UiNode, action: 'click' | 'setValue', value?: string): boolean {\n if (this.host?.act === undefined) {\n throw new Error('InspectionService cannot act before the runtime has built its tree.');\n }\n return this.host.act(node, action, value);\n }\n}\n\n/** What the runtime gives the service. */\nexport interface InspectionHost {\n nodesAt(x: number, y: number): readonly UiNode[];\n inspect(node: UiNode): UiNodeReport;\n /** Why the node has its size, as the layout engine explains it. */\n explain?(node: UiNode): LayoutExplanation | null;\n /** The node's box alone, as `inspect` reports it. */\n box?(node: UiNode): { readonly x: number; readonly y: number; readonly width: number; readonly height: number };\n /** The node's box with the transforms above it applied. */\n screenBox?(node: UiNode): { readonly x: number; readonly y: number; readonly width: number; readonly height: number };\n preview?(node: UiNode, property: string, value: unknown): void;\n endPreview?(node: UiNode, property?: string): void;\n /** Null ends the preview. */\n previewInputs?(anchorId: string, inputs: Readonly<Record<string, unknown>> | null): void;\n act?(node: UiNode, action: 'click' | 'setValue', value?: string): boolean;\n}\n","/**\n * Measures how long a user input waits for the frame that reflects it.\n *\n * The number this produces is the one thing `FrameMetrics` could not\n * already show. Frame *gap* — the distance between consecutive\n * `FrameMetrics.at` readings — is the honest measure of a stall on the\n * rendering thread, and it is deliberately blind to everything that\n * happens before an event reaches that thread: a shell too busy to\n * forward a pointer event costs the person a visibly late response\n * while the render worker, having nothing new to draw, reports a\n * perfectly even cadence.\n *\n * So latency is measured from the moment the shell received the event\n * to the moment the frame carrying its effect finished, on a clock\n * both threads agree on (see `epochNow` in `RenderWorkerProtocol`).\n */\nexport class InputLatencyTracker {\n private pendingAt: number | null = null;\n\n /**\n * Records an input that a frame will answer.\n *\n * `armedFrame` is the caller's reading of whether a frame is pending\n * once the input has been routed. An input that dirtied nothing —\n * a pointer move across empty space, a key the focused node ignored\n * — arms no frame, and there is no work whose latency could be\n * measured. Marking it anyway would attribute it to whatever\n * unrelated frame happened next, which at idle could be seconds\n * later and would make the metric read as a stall.\n *\n * The earliest pending stamp wins. A burst of pointer moves between\n * two frames coalesces into one frame's work, and the honest number\n * is how long the oldest of them waited, not the youngest.\n */\n mark(at: number | undefined, armedFrame: boolean): void {\n if (at === undefined || !armedFrame) {\n return;\n }\n this.pendingAt = this.pendingAt === null ? at : Math.min(this.pendingAt, at);\n }\n\n /**\n * Takes the latency for a frame that has just finished, in\n * milliseconds, or null when no input is waiting on this frame.\n *\n * Taking clears the mark, so a frame drawn for some other reason —\n * an animation tick, a patch — reports null rather than repeating\n * the last input's number.\n */\n take(frameEndEpoch: number): number | null {\n const markedAt = this.pendingAt;\n if (markedAt === null) {\n return null;\n }\n this.pendingAt = null;\n const elapsed = frameEndEpoch - markedAt;\n // A negative reading means the two threads disagreed about the\n // shared clock. Reporting nothing is better than reporting a\n // number that cannot be true.\n return elapsed >= 0 ? elapsed : null;\n }\n\n /** Whether an input is still waiting for its frame. */\n get hasPending(): boolean {\n return this.pendingAt !== null;\n }\n\n /** Forgets any pending mark, for a runtime being torn down or reset. */\n reset(): void {\n this.pendingAt = null;\n }\n}\n","import { clockOf, DirtyFlags, UiSpring, type AnimatedCell, type UiGraph, type UiNode } from 'gesso-core';\nimport type { AnimationService } from './AnimationService';\n\n/** Which offset is moving. */\ntype Axis = 'scrollX' | 'scrollY';\n\ninterface AxisState {\n readonly cell: AnimatedCell<number>;\n /**\n * The last value this wrote, so a write by anyone else is\n * detectable. See the setter in `cellFor`.\n */\n lastWritten: number | undefined;\n}\n\ninterface Axes {\n scrollX?: AxisState;\n scrollY?: AxisState;\n}\n\n/**\n * Animates a scroll container towards an offset instead of jumping to\n * it.\n *\n * One wheel notch is one large step and nothing in between, which is\n * the whole of what this smooths. Overflow scrolling deferred it in as\n * many words — \"momentum for discrete wheel ticks needs the animation\n * clock\" — and that clock now exists.\n *\n * **It owns no time.** Every offset is an ordinary `AnimatedCell`\n * driven by the runtime's `AnimationDriver` through `AnimationService`,\n * so a scroll is scheduled by the same `ticks` phase as everything\n * else, keeps its own frames coming through the same `nextTickAt`, and\n * stops arming them when the spring settles. Going through the service\n * rather than building a `UiSpring` directly buys two things: an\n * application's own motion vocabulary (`setMotion`), and the\n * velocity-carrying retarget, which is the difference between a second\n * notch extending the first and a second notch restarting it.\n *\n * **Reduced motion needs no code here.** A spring's default policy is\n * `snap`, and the driver snaps before the animation ever enters its\n * running set — synchronously, in the caller's turn, arming no frames.\n * So under reduced motion a wheel behaves exactly as it did before any\n * of this existed.\n */\n/**\n * The spring a scroll travels on, and why it is not one of the three in\n * the motion vocabulary.\n *\n * `snappy` (220/24) is the vocabulary's default and was what this used.\n * A spring starts from rest, and that one is slack enough that the rest\n * shows: sampled at 60Hz, one 120px notch on it had travelled a tenth\n * of the way after 50ms, half after 117ms and was not done until 283ms.\n * The page starts late and glides on after the hand has stopped, which\n * reads as an application that is behind rather than as smoothing.\n *\n * So scrolling gets a spring of its own, critically damped so it never\n * overshoots the place the wheel asked for. What is left is how long it\n * should take, and that was found by being wrong in both directions.\n * Sampled at 60Hz, one 120px notch travels:\n *\n * 220/24 a tenth by 50ms half by 117ms nine tenths by 217ms\n * 900/60 a tenth by 33ms half by 67ms nine tenths by 150ms\n * 2500/100 a tenth by 17ms half by 50ms nine tenths by 83ms\n *\n * The first is `snappy` and read as the page lagging behind the wheel.\n * The last was an attempt to match the length a browser's own wheel\n * animation is documented to have, and read as a jump: nine tenths of\n * the way in five frames is not a scroll anybody can see happening.\n * The middle one is what shipped. It starts in two frames and is over\n * in a quarter of a second, which is long enough to be motion and short\n * enough that nothing is waited for.\n *\n * Chrome's own curve is not in that table because it could not be\n * measured here: Chrome applies a wheel event the DevTools protocol\n * synthesises straight to the offset, with no animation, however it is\n * dispatched and with smooth scrolling explicitly on.\n *\n * A container that wants none of this still says `scrollBehavior:\n * 'instant'`, and a precision device never reaches this code at all —\n * `isNotchedWheel` sends a trackpad's already-smooth stream straight\n * through.\n *\n * The vocabulary is left alone because it is an application's to set,\n * and this is the runtime's scrolling rather than an application's\n * motion.\n */\nconst SCROLL_SPRING = { stiffness: 900, damping: 60, mass: 1 } as const;\n\nexport class SmoothScroller {\n /** One cell per (node, axis), kept for the node's life. */\n private readonly cells = new Map<UiNode, Axes>();\n\n constructor(\n private readonly graph: UiGraph,\n private readonly animations: AnimationService,\n /**\n * The offsets the container can be scrolled between, read fresh\n * because content grows. The low end is negative across a container\n * that scrolls from its right edge; see `scrollBounds`.\n */\n private readonly boundsOf: (node: UiNode, axis: Axis) => { readonly min: number; readonly max: number }\n ) {}\n\n /**\n * Moves the container by a delta, animating to the sum.\n *\n * `from` is the container's **effective** offset — the clamped one\n * the last layout settled on — and is used only when nothing is\n * already in flight. While a scroll is running the delta is added to\n * where it is *going*, not to where it currently is: a notch arriving\n * mid-scroll must add a whole notch to the journey, and adding it to\n * the moving position instead makes every notch after the first\n * travel less than it asked for.\n */\n scrollBy(node: UiNode, axis: Axis, delta: number, from: number): void {\n const cell = this.cellFor(node, axis).cell;\n const pending = this.destinationOf(cell);\n const { min, max } = this.boundsOf(node, axis);\n const target = clamp((pending ?? from) + delta, min, max);\n if (pending !== undefined && target === pending) {\n return;\n }\n this.animations.spring(cell, target, { spring: SCROLL_SPRING, clock: clockOf(node) });\n }\n\n /**\n * Shifts a running scroll by a coordinate correction.\n *\n * For virtualization, which holds a lazy list's anchor while the\n * estimated heights above it are replaced by measured ones. That\n * correction is not a scroll — the content moved under the viewport,\n * and the viewport has to move with it — so both ends have to shift:\n * the position, or the list slips by the correction on the next tick,\n * and the destination, or the scroll ends somewhere the anchor did\n * not put it.\n *\n * Expressed by moving the cell and re-aiming rather than by reaching\n * into the spring, because a fresh spring reads the cell at\n * construction and `AnimationService` seeds it with the outgoing\n * one's velocity — so the correction lands with no seam in the\n * motion.\n */\n adjust(node: UiNode, axis: Axis, delta: number): void {\n const state = this.cells.get(node)?.[axis];\n if (state === undefined || delta === 0) {\n return;\n }\n const pending = this.destinationOf(state.cell);\n if (pending === undefined) {\n return;\n }\n // The caller has already written the corrected offset to the\n // property, so adopt it as ours rather than reading it as someone\n // else's write and standing down.\n state.lastWritten = state.cell.value;\n this.animations.spring(state.cell, pending + delta, { spring: SCROLL_SPRING, clock: clockOf(node) });\n }\n\n /** Whether this container is being animated right now. */\n isScrolling(node: UiNode): boolean {\n const axes = this.cells.get(node);\n if (axes === undefined) {\n return false;\n }\n return (\n (axes.scrollX !== undefined && this.destinationOf(axes.scrollX.cell) !== undefined) ||\n (axes.scrollY !== undefined && this.destinationOf(axes.scrollY.cell) !== undefined)\n );\n }\n\n /**\n * Abandons any animation on this container, leaving the offset\n * wherever it had reached.\n *\n * What every *other* way of scrolling calls before it writes. A focus\n * reveal, a caret reveal and a scrollbar thumb drag each know exactly\n * where they want the container to be, and a spring still running\n * would overwrite that on its next tick — for the caret, on every\n * keystroke.\n */\n stop(node: UiNode): void {\n const axes = this.cells.get(node);\n if (axes === undefined) {\n return;\n }\n for (const state of [axes.scrollX, axes.scrollY]) {\n if (state !== undefined) {\n this.animations.stop(state.cell);\n state.lastWritten = undefined;\n }\n }\n }\n\n /**\n * Releases a removed subtree.\n *\n * The graph reports one removal per subtree *root*, so this sweeps\n * for descendants the way the layout engine's own detach does. An\n * animation outliving its node is the leak the driver's `stop`\n * docblock names: the driver holds the cell and the cell holds the\n * node.\n */\n handleNodeRemoved(node: UiNode): void {\n const gone: UiNode[] = [];\n for (const held of this.cells.keys()) {\n if (held === node || isDescendantOf(held, node)) {\n gone.push(held);\n }\n }\n for (const held of gone) {\n this.stop(held);\n this.cells.delete(held);\n }\n }\n\n /**\n * The cell one axis animates through, kept for the node's life.\n *\n * Kept, rather than made per notch, because the driver keys its\n * running set by cell **identity**: a fresh cell each time would\n * leave the previous animation running and two springs would fight\n * over one offset. `NodeTransitions.cellFor` memoises for the same\n * reason and says so.\n */\n private cellFor(node: UiNode, axis: Axis): AxisState {\n let axes = this.cells.get(node);\n if (axes === undefined) {\n axes = {};\n this.cells.set(node, axes);\n }\n const existing = axes[axis];\n if (existing !== undefined) {\n return existing;\n }\n const graph = this.graph;\n const animations = this.animations;\n const state: AxisState = { cell: undefined as unknown as AnimatedCell<number>, lastWritten: undefined };\n const cell: AnimatedCell<number> = {\n get value(): number {\n return node.getProperty<number>(axis) ?? 0;\n },\n set value(next: number) {\n // Someone else moved this container since the last tick — a\n // bound `scrollY` emitting, or a component revealing a row.\n // A programmatic write wins: stand down rather than dragging\n // the container back to a destination chosen before it.\n const current = node.getProperty<number>(axis) ?? 0;\n if (state.lastWritten !== undefined && current !== state.lastWritten) {\n state.lastWritten = undefined;\n animations.stop(cell);\n return;\n }\n state.lastWritten = next;\n // Deliberately `setProperty` and a Transform mark — exactly\n // what the instant path does. The plain write keeps the runtime\n // writing the element's *declared* offset and creates no\n // override, which is what `modifiers/scroll.ts` requires: an\n // override on `scrollY` would shadow every wheel for the life\n // of the node.\n node.setProperty(axis, next);\n graph.markDirty(node, DirtyFlags.Transform);\n }\n };\n (state as { cell: AnimatedCell<number> }).cell = cell;\n axes[axis] = state;\n return state;\n }\n\n /** Where the running spring is going, or undefined when at rest. */\n private destinationOf(cell: AnimatedCell<number>): number | undefined {\n const running = this.animations.animationFor(cell);\n return running instanceof UiSpring ? running.destination : undefined;\n }\n}\n\nfunction clamp(value: number, low: number, high: number): number {\n return Math.min(high, Math.max(low, value));\n}\n\nfunction isDescendantOf(node: UiNode, ancestor: UiNode): boolean {\n for (let current: UiNode | null = node.parent; current !== null; current = current.parent) {\n if (current === ancestor) {\n return true;\n }\n }\n return false;\n}\n","import type { FrameworkChild } from '../ComponentElement';\nimport { ComponentHostResolver } from '../ComponentHostResolver';\nimport { getComponentMetadata } from '../metadata';\nimport {\n describeStream,\n formatNodePath,\n printPropValue,\n type UiEnvironmentReport,\n type UiInteractionReport,\n type UiNodeReport,\n type UiBeneathReport,\n type UiOwnerInput,\n type UiOwnerReport,\n type UiPropReport,\n type UiSemanticsReport\n} from './NodeReport';\nimport {\n treeText,\n type DevtoolsEvent,\n type DevtoolsRequest,\n type UiTreeNode,\n type UiTreeSnapshot\n} from './DevtoolsProtocol';\nimport {\n provided,\n UiGraph,\n UiGraphBuilder,\n describeOverrides,\n formatExplanation,\n isComponentLikeElement,\n isUiElement,\n linkOf,\n textRunOfRecordId,\n type InteractiveOptions,\n type UiEnvironment,\n isObservable,\n type UiElement,\n Stack,\n scrollbarThumb,\n scrollBounds,\n scrollRange,\n scrollsFromRight,\n type LayoutRecord,\n UiVirtualWindow,\n UiVirtualSheet,\n VIRTUAL_INDEX_PROP,\n VIRTUAL_LEAD_PROP,\n VIRTUAL_SHEET_PROP,\n VIRTUAL_WINDOW_PROP,\n type VirtualItemMeasure,\n DirtyFlags,\n propertyEffects,\n setPerformanceMarks,\n resolveCursor,\n type UiNode,\n type UiHoldUnseen,\n UiNodeType,\n UiInputDispatcher,\n UiPointerEvent,\n UiEventType,\n UiGestureRecognizer,\n UiHitTester,\n UiPointerController,\n UiTouchScroller,\n UiWheelController,\n type ScrollContainerState,\n type ScrollSink,\n type UiScrollability,\n UiFocusManager,\n FocusNotifier,\n EnvironmentNotifier,\n UiEnvironmentKeys,\n UiInsetRegistry,\n insetsEqual,\n noInsets,\n type UiInsets,\n UiKeyboardController,\n UiEditingController,\n type EditingState,\n UiSelectionController,\n UiFindController,\n AnimationDriver,\n UiSharedElements,\n buildSemanticsTree,\n buildSemanticsSubtree,\n rewalkSemantics,\n semanticsInertAbove,\n semanticsMemory,\n recordsEqual,\n TEXT_RUN_ID_SEPARATOR,\n diffSemantics,\n dropIndexShifts,\n LayoutNotifier,\n type UiSemanticsAction,\n type UiSourceLocation,\n type UiTheme,\n type UiFileDropMessage,\n dragSessionFor,\n type UiSemanticsBox,\n type UiSemanticsMap,\n type UiSemanticsPatch,\n type SemanticsRewalk,\n type UiSemanticsRecord,\n type UiSemanticsUpdate,\n type LayoutBox,\n LayoutEngine,\n type LayoutExplanation,\n Constraints,\n Canvas2DRenderer,\n CanvasTextMeasurer,\n type TextMeasurer,\n createCanvasSurface,\n createWebGPUSurface,\n isWebGPUAvailable,\n LayoutInspector,\n WebGPURenderer,\n type CanvasHost,\n type CanvasSurface,\n type RenderChanges,\n type RendererBackend,\n type UiRenderer,\n type WebGPUCanvasHost,\n UiScheduler,\n UiTimerFrameClock,\n UiFrame,\n type DirtyNodeSet,\n type UiFrameClockFactory,\n type UiContrast,\n type UiPointer,\n writeOverrideProperty,\n clearOverrideProperty\n} from 'gesso-core';\nimport { createComponent } from '../createComponent';\nimport { OverlayLayer } from '../overlay/OverlayLayer';\nimport { OverlayService } from '../overlay/OverlayService';\nimport type { ColorScheme } from './colorScheme';\nimport { ShellService, type ShellFileResult, type ShellRequest, type ShellStorageResult } from './ShellService';\nimport { AudioService, type AudioAction, type AudioRequest, type AudioSample } from './AudioService';\nimport { RouterService, type RouterRoutes } from '../router/RouterService';\nimport { FindService } from './FindService';\nimport { EditingService } from './EditingService';\nimport { ScrollService } from './ScrollService';\nimport { FocusService } from './FocusService';\nimport { MediaService, type MediaOptions } from './MediaService';\nimport { FontService, type FontFamilyDeclaration } from './FontService';\nimport { TextService } from './TextService';\nimport { AnimationService } from './AnimationService';\nimport { FrameService } from './FrameService';\nimport { InspectionService } from './InspectionService';\nimport { InputLatencyTracker } from './InputLatency';\nimport { SmoothScroller } from './SmoothScroller';\nimport { ChannelRegistry } from '../channel/ChannelRegistry';\nimport { ServiceRegistry } from '../service/ServiceRegistry';\n\n/**\n * The ordered work of one frame.\n *\n * Only five of the seven stages the design document imagined are real\n * phases. Component reconciliation and input dispatch are driven by\n * events, not by the clock: an observable emission reconciles its\n * subtree immediately and a pointer event routes immediately, each\n * marking nodes dirty so the *effects* land in the next frame. Giving\n * them frame slots would add latency and describe the system falsely.\n *\n * `ticks`, `patches` and `environment` run before the dirty set is\n * snapshotted, because all three produce dirt that this frame must\n * see. `layout` and `render` run against the snapshot.\n *\n * `ticks` is first because an animation's writes are inputs to\n * everything after them: a tick that changed a width has to be the\n * width this frame's virtualization measures against and this frame's\n * layout places from, and a tick that ran after `patches` would draw\n * one frame late for the whole life of the animation.\n */\nexport const UI_FRAME_PHASES = [\n 'ticks',\n 'patches',\n 'environment',\n 'virtualize',\n 'layout',\n 'semantics',\n 'render'\n] as const;\n\nexport type UiFramePhase = (typeof UI_FRAME_PHASES)[number];\n\nexport type FramePhaseTimings = Record<UiFramePhase, number>;\n\n/**\n * Guard against a root component that only ever renders another\n * component, which would otherwise recurse until the stack gives out.\n */\nconst MAX_ROOT_COMPONENT_DEPTH = 32;\n\n/**\n * Input controllers over the built tree.\n *\n * Exposed because input arrives differently per thread: from DOM\n * events through a UiPlatformAdapter on the main thread, and from\n * forwarded messages in a render worker. Both end up calling these.\n */\nexport interface RuntimeInput {\n readonly dispatcher: UiInputDispatcher;\n readonly pointer: UiPointerController;\n readonly wheel: UiWheelController;\n readonly keyboard: UiKeyboardController;\n readonly focus: UiFocusManager;\n /** Text editing: the shell's beforeinput, composition and paste land here. */\n readonly editing: UiEditingController;\n /** Selecting and copying text nobody types into. */\n readonly selection: UiSelectionController;\n /** Finding text in the app's own content; the find bar's engine. */\n readonly find: UiFindController;\n /** Dragging a scroll container's contents with a finger. */\n readonly touchScroll: UiTouchScroller;\n}\n\n/**\n * Which backend draws.\n *\n * **`canvas2d` is the default, and that is not the obvious answer.** A\n * GPU backend sounds like the faster one and on a scene of shapes and\n * text it is. On a scene dense with pictures it is currently not, and\n * the reason is one asymmetry rather than anything fundamental:\n * Canvas2D keeps a copy of each still at the size it is drawn (see\n * `ScaledImageCache`), while `WebGPUTextureCache` uploads a still at\n * the source's own size. A 480px cover shown at 164 is therefore about\n * eight times the texture on WebGPU, and a screen holding ninety of\n * them feels it. Measured on Segue's home screen, where the difference\n * was plain enough to notice without instrumenting anything.\n *\n * So the default is the one that is fast everywhere today, and the\n * faster ceiling is opt-in until the gap is closed. When\n * `WebGPUTextureCache` learns the drawn size the way its video path\n * already has, this should flip back.\n *\n * `auto` picks WebGPU where the browser has it and Canvas2D everywhere\n * else. The choice is made synchronously on whether `navigator.gpu`\n * exists, so an engine that never shipped WebGPU (WKWebView, WebKitGTK)\n * is on Canvas2D from the first frame rather than after a rejected\n * adapter request. A browser that has the entry point but cannot\n * produce an adapter or a device still falls back, asynchronously, once\n * that request fails.\n *\n * `canvas2d` pins the portable backend and never asks for an adapter.\n * `webgpu` asks for it and falls back the same way `auto` does, but\n * reports the fallback to the console, because a caller that named the\n * backend wants to know it did not get it.\n */\nexport type RendererChoice = RendererBackend | 'auto';\n\nexport interface GessoRuntimeOptions {\n /** Root component or element. */\n root: FrameworkChild;\n /** Canvas to draw into: HTMLCanvasElement, OffscreenCanvas, or a test double. */\n canvas: CanvasHost;\n /** The rendering backend. Defaults to `canvas2d`; see RendererChoice. */\n renderer?: RendererChoice;\n /**\n * A canvas for text measurement when the draw canvas is WebGPU's — a\n * canvas holds one context, so the measurer needs its own. Defaults\n * to a 1×1 OffscreenCanvas; tests pass a double.\n */\n measureCanvas?: CanvasHost;\n /**\n * The measurer layout and the renderers share, when the caller wants\n * one that does not come from a canvas.\n *\n * Only a test supplies it. A canvas measurer is the right answer\n * everywhere a canvas is real, but a test double's `measureText`\n * answers the same width for every font size, so text laid out\n * against one is not text: a heading and its caption come out the\n * same height. `gesso-testing` passes `CharacterCountTextMeasurer`\n * instead, which is proportional to the font size and identical on\n * every machine.\n */\n textMeasurer?: TextMeasurer;\n /**\n * The image resolver, icon rasteriser and video decoder the\n * `MediaService` should use.\n *\n * Supplied here rather than through the store afterwards because the\n * tree is built inside this constructor, and an `Image` in it asks\n * for its bitmap at that moment: a resolver installed after the\n * runtime exists would already have missed the first screen. An app\n * that fetches through its own stack, or one that has measured a\n * reason to decode in a worker of its own, passes it here.\n *\n * An application reaches this through `createSyncApp(Root).useMedia()`\n * on the single thread and `renderRoot(Root).useMedia()` in a render\n * worker. It is declared in the worker rather than in the shell\n * because a resolver is a function and no function crosses a\n * `postMessage`.\n */\n media?: MediaOptions;\n /**\n * The font families this runtime's text may name, with their faces\n * and fallback stacks. Each face is loaded into this thread's font\n * set, and the tree is measured again as it arrives; until then text\n * draws in the fallback. See `FontService`.\n */\n fonts?: readonly FontFamilyDeclaration[];\n /**\n * The runtime services this runtime's components may reach.\n *\n * Supplied only by a test wanting to substitute one; a runtime\n * registers the six it owns itself, because they are part of what a\n * runtime *is* rather than something an application configures.\n */\n services?: ServiceRegistry;\n /**\n * The routes a `RouterOutlet` in this tree resolves against.\n *\n * Optional, like every other application-shaped thing here: a\n * runtime with no routes still registers a `RouterService`, and it\n * simply matches nothing. Given here rather than set afterwards for\n * the same reason `media` is — the tree is built inside this\n * constructor, and an outlet in it asks for the current match at\n * that moment.\n */\n routes?: RouterRoutes;\n /**\n * The window's address at start-up, resolved before the tree is\n * built.\n *\n * Without it the router starts at `/`, and the tree built in this\n * constructor is the root route's — its screen constructed, its\n * effects run — before the shell's `setUrl` moves it somewhere else.\n * An app opened at `/d/42` would mount its home screen first, and a\n * home screen that does something on mount (opens the last document,\n * say) does it, for a url nobody asked for. Both hosts pass the\n * address here; `setUrl` is for every one after.\n */\n url?: string;\n /**\n * The channels this runtime's components may reach.\n *\n * Attached elsewhere and handed in, because where a channel's data\n * lives is the application's decision, not the runtime's.\n */\n channels?: ChannelRegistry;\n /** Defaults to a timer clock, which is the only option inside a worker. */\n clock?: UiFrameClockFactory;\n /** Initial logical size. Callers normally follow with resize(). */\n width?: number;\n height?: number;\n dpr?: number;\n}\n\n/**\n * The whole UI, with no reference to the DOM.\n *\n * Owns the component runtime, retained graph, layout engine, input\n * controllers, scheduler and renderer. Everything here runs happily\n * in a Worker: the only things it cannot do for itself are obtain a\n * canvas and learn about size and input, which is exactly the split\n * between this class and its two hosts — GessoApp on the main thread\n * and renderRoot() in a render worker.\n */\n/** The source of `InspectionService.preview`'s writes, below every modifier's in the cascade. */\nconst PREVIEW = Symbol('preview');\n\n/** How many shell requests are held for a listener that has not attached, before the rest are dropped. */\nconst HELD_SHELL_REQUESTS = 256;\n/** The longest last frame after which an input's frame is still drawn early: half a 60 Hz refresh. See `GessoRuntime.answerInput`. */\nconst EARLY_FRAME_MAX_MS = 8;\n/** The least time between two early frames: about half a refresh at 60 Hz. See `GessoRuntime.answerInput`. */\nconst EARLY_FRAME_GAP_MS = 8;\n/** How long after a discrete input a channel's patch counts as its answer, and is drawn early. See `GessoRuntime.answerInput`. */\nconst ANSWER_WINDOW_MS = 500;\n\nexport class GessoRuntime {\n readonly services: ServiceRegistry;\n readonly channels: ChannelRegistry;\n readonly input: RuntimeInput;\n /**\n * The layout inspector: hover boxes, a heatmap of measured nodes and\n * `engine.explain` for the hovered node, painted over each frame\n * while enabled. Off by default; see `setInspectorEnabled`.\n */\n readonly inspector: LayoutInspector;\n\n private readonly resolver: ComponentHostResolver;\n private readonly graph = new UiGraph();\n /** The properties `InspectionService.preview` is drawing over, by node; gone with the node. */\n private readonly previewed = new WeakMap<UiNode, Set<string>>();\n private readonly engine: LayoutEngine;\n private readonly builder: UiGraphBuilder;\n private readonly scheduler: UiScheduler;\n private readonly inputLatency = new InputLatencyTracker();\n private readonly canvas: CanvasHost;\n private readonly textMeasurer: TextMeasurer;\n /** The 2D surface when Canvas2D draws; the inspector paints on it. */\n private canvasSurface: CanvasSurface | null = null;\n private renderer: UiRenderer;\n private rendererState: RendererBackend | 'pending';\n /** Resolves with the backend that ended up drawing. */\n readonly rendererReady: Promise<RendererBackend>;\n private readonly dispatcher = new UiInputDispatcher();\n private width: number;\n private height: number;\n\n /** The layout root: a stack holding the app root and the overlay layer. */\n private root: UiNode | undefined;\n /** The node the app's root definition produced. */\n private appRoot: UiNode | undefined;\n /** The platform's insets as the shell last reported them; see `setViewportInsets`. */\n private viewportInsetValue: UiInsets = noInsets;\n /** The registry those insets are currently published into, and the handle on them. */\n private viewportInsetRegistry: UiInsetRegistry | null = null;\n private viewportInsetWrite: ((next?: Partial<UiInsets>) => void) | null = null;\n /** Stops watching the app root's environment for a change of registry. */\n private detachViewportInsetEnvironment: (() => void) | null = null;\n /** A WebGPU device being asked for after the last one was lost. */\n private webgpuRecovery: Promise<void> | null = null;\n /** Stops listening for the browser handing back a canvas it had thrown away. */\n private detachContextRestored: (() => void) | null = null;\n private constraints: Constraints;\n private pixelRatio: number;\n private lastFrameMs = 0;\n /** The timer that draws an input's frame early; see `answerInput`. */\n private earlyFrame: ReturnType<typeof setTimeout> | null = null;\n /** When the last early frame was drawn, on this thread's clock. */\n private lastEarlyFrame = -Infinity;\n /** When the last discrete input was routed; see `answerInput`. */\n private lastDiscreteInput = -Infinity;\n /** The wheel's steps since the last frame, added together; see `wheel`. */\n private heldWheel: Parameters<UiWheelController['wheel']> | null = null;\n /** The display time the frame being drawn is for; see `FrameMetrics.time`. */\n private frameTime: number | null = null;\n /** When the frame's first phase began, before its changes were collected; see `FrameMetrics.durationMs`. */\n private frameBegan: number | null = null;\n private frameListener: ((metrics: FrameMetrics) => void) | null = null;\n private rendererErrorListener: ((message: string) => void) | null = null;\n private frameErrorListener: ((message: string, stack?: string) => void) | null = null;\n /** WebGPU stage timings of the frame being rendered; null on Canvas2D. */\n private gpuTimings: GpuStageTimings | null = null;\n private inspectListener: ((report: UiNodeReport | null) => void) | null = null;\n /** The last report's explanation text, which is what tells two reports apart. */\n private lastInspection: string | null = null;\n private devtoolsListener: ((event: DevtoolsEvent) => void) | null = null;\n /** Whether a panel wants a tree snapshot after every frame that changed the tree. */\n private watchingTree = false;\n /**\n * Live subscriptions as of the last snapshot sent.\n *\n * A leak that adds no nodes changes nothing `frameChangedTree` looks\n * at, so a panel watching for one would never be told. This is the\n * second reason to resend, and it costs a count only while a panel\n * is attached.\n */\n private lastSubscriptions = -1;\n /** Whether a panel wants every frame's metrics. */\n private watchingFrames = false;\n /** The node a panel has selected, whose report is kept fresh; null for none. */\n private selectedId: string | null = null;\n /** The selected node's last report, serialised, which is what tells two apart. */\n private lastSelectedReport: string | null = null;\n private cursorListener: ((cursor: string | null) => void) | null = null;\n private lastCursor: string | null = null;\n private scrollabilityListener: ((scrollability: UiScrollability, scrollsAnything: boolean) => void) | null = null;\n private lastScrollability: UiScrollability = { up: false, down: false, left: false, right: false };\n /**\n * Undefined until the first report, so that one is always sent.\n *\n * The values it takes are ordinary booleans; the third state exists\n * only to make \"nothing has been said yet\" different from \"nothing\n * scrolls\", which matters because those two need opposite\n * `touch-action` on the shell's canvas and an app with no scroll\n * container at all would otherwise never send either.\n */\n private lastScrollsAnything: boolean | undefined = undefined;\n private editingListener: ((state: EditingState | null) => void) | null = null;\n /**\n * The selection controller, reachable before `input` is assigned:\n * the graph's node-removed listener is installed in the constructor\n * and fires for nodes taken out from under a live selection.\n */\n private selectionController: UiSelectionController | null = null;\n private findController: UiFindController | null = null;\n /** Reachable before `input` is assigned, for the same reason. */\n private readonly focusManager: UiFocusManager;\n /** The input stack's hit tester, kept for the inspector's questions about what lies where. */\n private hitTester!: UiHitTester;\n private readonly layoutNotifier: LayoutNotifier;\n /** Animates a wheel scroll; see `SmoothScroller`. */\n private readonly smoothScroller: SmoothScroller;\n /**\n * Whether the document showing this runtime is on screen.\n *\n * Assumed true until the shell says otherwise, because a runtime with\n * no shell — a spec, a headless graph — is never told and must draw.\n */\n private visible = true;\n /**\n * The running animations. Built as a field rather than in the body\n * of the constructor because the builder, the services and `buildRoot`\n * all need it, and `buildRoot` is where a component's first\n * `animate()` can happen.\n */\n private readonly animations = new AnimationDriver();\n /**\n * Which node currently answers to each shared-element name.\n *\n * Per runtime for the same reason the driver above is: several\n * runtimes share a worker in the playground, and a shared registry\n * would let one runtime's element morph from another's.\n */\n private readonly sharedElements = new UiSharedElements();\n private readonly focusNotifier = new FocusNotifier();\n private readonly environmentNotifier = new EnvironmentNotifier();\n /**\n * The semantics tree: every record by id, and each record's children\n * in order.\n *\n * A record's own `index` is only kept current for the records that\n * are sent; the order is `semanticsChildren`. Renumbering every\n * record after an insertion cost a pass over the whole tree on every\n * structural change, for indices nothing would read until a record\n * was next sent or the tree was asked for (`semanticsTree`).\n */\n private semantics = new Map<string, UiSemanticsRecord>();\n private semanticsChildren = new Map<string | null, string[]>();\n /** The tree in document order with every index current, built when asked for. */\n private semanticsOrdered: Map<string, UiSemanticsRecord> | null = null;\n private semanticsListener: ((update: UiSemanticsUpdate) => void) | null = null;\n /**\n * The box last reported for each mirrored node, so a frame that\n * moved three rows of a list sends three boxes rather than all of\n * them. Only populated while a listener is attached.\n */\n private semanticsBoxes = new Map<string, LayoutBox>();\n /** When a frame last moved something, and when the boxes were last swept; see `updateSemantics`. */\n private semanticsMovedAt = -Infinity;\n private semanticsSweptAt = -Infinity;\n /** The sweep owed once a run of moving frames stops. */\n private semanticsSweepTimer: ReturnType<typeof setTimeout> | null = null;\n /** What each semantics walk leaves for the next; see `SemanticsMemory`. */\n private readonly semanticsMemory = semanticsMemory();\n private lastFocusedId: string | null = null;\n /**\n * Where the caret was when `reload` replaced the tree, put back on\n * the frame that lays the new one out, and null when nothing had it.\n *\n * Null is restored as well as an id, which is the second half of\n * the hot-replacement gap: a rebuilt subtree runs `autoFocus` again,\n * and a dialog's first field taking the caret away from where the\n * person was is worse than a reload doing nothing at all.\n */\n private focusAfterReload: string | null = null;\n /** Whether `focusAfterReload` is waiting to be applied. */\n private restoringFocus = false;\n /**\n * Reveals asked for while the layout listeners run, kept until the\n * frame's boxes have settled; null outside `settleLayout`, where a\n * reveal is applied at once.\n */\n private deferredReveals: { node: UiNode; padding: number }[] | null = null;\n /** Whether a frame has run out of layout passes and been warned about; see `MAX_LAYOUT_PASSES`. */\n private warnedLayoutPasses = false;\n /** A frame changed semantics while nothing was listening; see `semanticsTree`. */\n private semanticsStale = false;\n private lastEditingState: EditingState | null = null;\n private shellListener: ((request: ShellRequest) => void) | null = null;\n /** Shell requests made before `onShellRequest` gave them somewhere to go; see there. */\n private heldShellRequests: ShellRequest[] = [];\n private audioListener: ((request: AudioRequest) => void) | null = null;\n private caretTimer: ReturnType<typeof setTimeout> | null = null;\n private scrollbarTimer: ReturnType<typeof setTimeout> | null = null;\n /** Pending frame the renderer asked for though nothing changes; see \\`UiRenderer.nextRepaint\\`. */\n private rendererTimer: ReturnType<typeof setTimeout> | null = null;\n private inspectorTimer: ReturnType<typeof setTimeout> | null = null;\n /** Pending wake-up for an animation that does not want every frame. */\n private animationTimer: ReturnType<typeof setTimeout> | null = null;\n private replicas: readonly PatchSource[] = [];\n private phaseTimings: FramePhaseTimings = emptyPhaseTimings();\n private started = false;\n /**\n * Whether the engine holds a laid-out tree, so `resize` knows which\n * path it is on. Set by the first resize and by the first frame that\n * lays out, whichever comes first; see `resize`.\n */\n private laidOutOnce = false;\n /** Frames in a row that threw; see `reportFrameError`. */\n private framesFailed = 0;\n /** Nodes that declare `holdUnseen`, as frames have found them; see `updateHolds`. */\n private readonly holdable = new Set<UiNode>();\n\n constructor(options: GessoRuntimeOptions) {\n this.services = options.services ?? new ServiceRegistry();\n this.channels = options.channels ?? new ChannelRegistry();\n this.canvas = options.canvas;\n this.pixelRatio = options.dpr ?? 1;\n this.width = options.width ?? 600;\n this.height = options.height ?? 600;\n this.constraints = Constraints.loose(this.width, this.height);\n\n // `auto` decides here, in the constructor, rather than by letting\n // the WebGPU path fail: a browser with no `navigator.gpu` gets the\n // Canvas2D renderer immediately, with no `pending` frames and no\n // fallback to unwind. Only `webgpu` asked for by name goes to the\n // GPU path on an engine that has no entry point, so that it can\n // report what it could not have.\n const choice = options.renderer ?? 'canvas2d';\n const drawWithWebGPU = choice === 'webgpu' || (choice === 'auto' && isWebGPUAvailable());\n if (!drawWithWebGPU) {\n this.canvasSurface = createCanvasSurface(options.canvas);\n this.textMeasurer = options.textMeasurer ?? new CanvasTextMeasurer(this.canvasSurface.getContext2D());\n this.renderer = new Canvas2DRenderer({ surface: this.canvasSurface });\n this.rendererState = 'canvas2d';\n this.rendererReady = Promise.resolve('canvas2d');\n } else {\n // The draw canvas will hold the WebGPU context, so text is measured\n // on a canvas of its own. One measurer still serves layout and the\n // renderer, which is what keeps line breaks identical.\n const measureSurface = createCanvasSurface(options.measureCanvas ?? createMeasureCanvas());\n this.textMeasurer = options.textMeasurer ?? new CanvasTextMeasurer(measureSurface.getContext2D());\n const webgpu = new WebGPURenderer({\n surface: createWebGPUSurface(options.canvas as unknown as WebGPUCanvasHost),\n onError: message => this.reportRendererError(message),\n // A frame is what notices and starts the recovery, and a page\n // nobody is touching would otherwise not have one.\n onLost: () => this.requestRepaint(),\n hooks: {\n onPrepareEnd: ms => (this.gpuTimings = { ...(this.gpuTimings ?? emptyGpuTimings()), prepare: ms }),\n onUploadEnd: ms => (this.gpuTimings = { ...(this.gpuTimings ?? emptyGpuTimings()), upload: ms }),\n onEncodeEnd: ms => (this.gpuTimings = { ...(this.gpuTimings ?? emptyGpuTimings()), encode: ms })\n }\n });\n this.renderer = webgpu;\n this.rendererState = 'pending';\n this.rendererReady = webgpu\n .initialize()\n .then((): RendererBackend => {\n if (this.renderer !== webgpu) {\n return this.rendererState === 'pending' ? 'canvas2d' : this.rendererState;\n }\n this.rendererState = 'webgpu';\n this.renderer.resize(this.width, this.height, this.pixelRatio);\n this.requestRepaint();\n return 'webgpu';\n })\n .catch((error: unknown): RendererBackend => {\n if (choice === 'webgpu') {\n // eslint-disable-next-line no-console\n console.error('WebGPU was requested but is unavailable; drawing with Canvas2D.', error);\n }\n this.fallBackToCanvas2D(webgpu);\n return 'canvas2d';\n });\n }\n this.detachContextRestored = watchContextRestored(this.canvas, () => this.surfaceRestored());\n\n this.engine = new LayoutEngine(this.textMeasurer);\n this.inspector = new LayoutInspector(this.engine, {\n // Read through `this.builder`, which is assigned below: the\n // closure runs when a node is hovered, long after the\n // constructor.\n modifierNames: node => this.builder.modifiersFor(node)?.names ?? []\n });\n this.resolver = new ComponentHostResolver(this.services, this.channels);\n this.layoutNotifier = new LayoutNotifier();\n // Built before the tree, not with the rest of the input stack in\n // `createInput`: modifiers attach while `buildRoot` runs, and a\n // `focusRing()` on the first control asks whether its node has\n // focus at that moment. The traversal root arrives afterwards\n // through `setRoot`, which is the only thing the manager needs the\n // tree for.\n this.focusManager = new UiFocusManager(this.graph.root, this.dispatcher);\n this.focusManager.onFocusChange(node => {\n this.focusNotifier.handleFocusChange(node, this.focusManager.focusVisible);\n if (this.semanticsListener !== null) {\n // A mirror has to move DOM focus with the app's, and the frame\n // where it hears about it is the frame this arms. Most focus\n // changes dirty something anyway — a focus ring is a property\n // write — but a node with no visible focus state would\n // otherwise change nothing and schedule nothing.\n this.requestRepaint();\n }\n });\n this.graph.setEnvironmentChangedListener(node => this.environmentNotifier.handleEnvironmentChange(node));\n this.builder = new UiGraphBuilder(this.graph, {\n components: this.resolver,\n dispatcher: this.dispatcher,\n focus: {\n isFocused: node => this.focusNotifier.isFocused(node),\n isFocusVisible: node => this.focusNotifier.isFocusVisible(node),\n focus: (node, options) => {\n this.focusManager.focus(node, 'program', options);\n },\n onFocusChange: (node, listener) => this.focusNotifier.add(node, listener)\n },\n environment: {\n read: (node, key) => (node.environment ?? this.graph.buildNodeEnvironment(node)).get(key),\n onChange: (node, listener) => this.environmentNotifier.add(node, listener)\n },\n layout: {\n // The screen box, not the world box: a modifier that turns a\n // pointer position into a fraction of its node needs where the\n // node is *seen*, which is the world box after every scroll and\n // sticky offset above it and every transform. Without the\n // transforms a slider on a card under a zoomed camera box took\n // a press on its middle for one far along its record, because\n // the pointer is in canvas space and the record never moves.\n // Its size is the drawn one too, so a fraction stays a fraction;\n // a modifier that wants its own laid-out size reads `flowBox`.\n box: node => (this.engine.recordFor(node) === undefined ? null : this.engine.screenBox(node)),\n // And the pre-scroll box, for a modifier asking where the node\n // sits in the layout rather than where it is seen — a layout\n // animation, which must not mistake a scroll for a move.\n flowBox: node => (this.engine.recordFor(node) === undefined ? null : this.engine.worldBox(node)),\n // The container's *effective* offset, which is the record's and\n // not the property's: a wheel writes the property unclamped and\n // the engine clamps it to the content on the next layout, so the\n // property can name a place the list never went.\n scroll: node => {\n const record = this.engine.recordFor(node);\n return record === undefined ? null : { x: record.scrollX, y: record.scrollY };\n },\n // The canvas, which is what `box` and `flowBox` are measured\n // against. Not the graph root's record — the root has none —\n // and not the window's, which in an application that does not\n // fill its page is a different rectangle. It is the same\n // answer `onScreen` culls the semantics tree with, without\n // that one's margin: a rectangle a scroll is *about* to bring\n // in wants its accessibility record early, and wants its\n // decoder started when it actually arrives.\n viewport: () => ({ x: 0, y: 0, width: this.width, height: this.height }),\n onLayout: (node, listener) => this.layoutNotifier.add(node, listener),\n onFlowLayout: (node, listener) => this.layoutNotifier.addFlow(node, listener)\n },\n animations: this.animations,\n sharedElements: this.sharedElements\n });\n\n // Every runtime has an overlay layer; its entries hold elements and\n // nodes, so it could not leave this thread even if asked.\n if (!this.services.has(OverlayService)) {\n this.services.register(OverlayService);\n }\n // And the shell's services: clipboard and URLs, which only the host\n // thread can reach.\n if (!this.services.has(ShellService)) {\n this.services.register(ShellService);\n }\n this.services.get(ShellService).setHandler(request => this.toShell(request));\n // And sound, which is the shell's element and this thread's client.\n if (!this.services.has(AudioService)) {\n this.services.register(AudioService);\n }\n this.services.get(AudioService).setHandler(request => this.audioListener?.(request));\n // And the find session, so a component can drive the search the\n // browser's own find bar cannot do over a canvas.\n if (!this.services.has(FindService)) {\n this.services.register(FindService);\n }\n // And focus, which lives in the input stack and is therefore out\n // of a component's reach without a store in front of it.\n if (!this.services.has(FocusService)) {\n this.services.register(FocusService);\n }\n // And the caret's geometry, which is in the input stack for the\n // same reason and just as far out of reach.\n if (!this.services.has(EditingService)) {\n this.services.register(EditingService);\n }\n // And scrolling a node into view, for a highlight that moves\n // without focus.\n if (!this.services.has(ScrollService)) {\n this.services.register(ScrollService);\n }\n // And the frames themselves, for a screen that shows its own\n // frame gap or input latency.\n if (!this.services.has(FrameService)) {\n this.services.register(FrameService);\n }\n // And the inspector's answers, for a tool built in Gesso that\n // points at what is on the screen; attached once input exists.\n if (!this.services.has(InspectionService)) {\n this.services.register(InspectionService);\n }\n // And the image resolver and icon rasteriser, whose caches must be\n // per runtime: two runtimes in one worker must not share a bitmap\n // one of them is about to close.\n if (!this.services.has(MediaService)) {\n this.services.register(MediaService);\n }\n // And the fonts, which load into this thread's font set and re-lay\n // the tree out as they arrive.\n if (!this.services.has(FontService)) {\n this.services.register(FontService);\n }\n this.services.get(FontService).setListener(() => this.fontsChanged());\n // And measurement, which is the same measurer layout uses: a\n // width an application asks for has to be the width the engine\n // would lay the text out at, cache and font-loading and all.\n if (!this.services.has(TextService)) {\n this.services.register(TextService);\n }\n this.services.get(TextService).setMeasurer(this.textMeasurer);\n if (options.fonts !== undefined) {\n this.services.get(FontService).declare(options.fonts);\n }\n // And animation, whose running set must be per runtime for the\n // same reason the media caches are: the playground has several\n // runtimes in one worker, and a shared driver would tick a\n // disposed runtime's cells.\n if (!this.services.has(AnimationService)) {\n this.services.register(AnimationService);\n }\n this.services.get(AnimationService).setDriver(this.animations);\n // The app's motion vocabulary, provided at the root, so a name in a\n // transition or a motion state means what it means to AnimationService.\n this.services.get(AnimationService).setMotionListener(motion => {\n if (\n this.graph.updateNodePropertyNow(\n this.graph.root,\n 'provide',\n [provided(UiEnvironmentKeys.motion, motion)],\n DirtyFlags.Environment\n )\n ) {\n this.requestRepaint();\n }\n });\n // Sound reads the clock through the same driver, so a playback's\n // position moves between the shell's samples.\n this.services.get(AudioService).setAnimations(this.services.get(AnimationService));\n this.smoothScroller = new SmoothScroller(\n this.graph,\n this.services.get(AnimationService),\n // Read fresh on every notch rather than captured: content grows,\n // and a target clamped against yesterday's limit stops short.\n (node, axis) => {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n return { min: 0, max: 0 };\n }\n return scrollBounds(record, axis === 'scrollY' ? 'y' : 'x');\n }\n );\n // And the router, whose matches hold route definitions, which hold\n // component classes: it could not cross a worker boundary if it\n // wanted to. The one thing it needs from the shell is the address\n // bar, which it reaches the same way the clipboard does.\n if (!this.services.has(RouterService)) {\n this.services.register(RouterService);\n }\n const router = this.services.get(RouterService);\n // Held like any other request when nobody is listening yet: a guard\n // that redirects the address the runtime starts at does so in this\n // constructor, and the shell's address bar still has to hear it.\n router.setHistory({\n push: url => this.toShell({ type: 'history', action: 'push', url }),\n replace: url => this.toShell({ type: 'history', action: 'replace', url }),\n back: () => this.toShell({ type: 'history', action: 'back' }),\n forward: () => this.toShell({ type: 'history', action: 'forward' })\n });\n // The address before the routes, so the routes resolve it rather\n // than `/`: a guard on the root route has no business running, and\n // redirecting the shell, for a window that was never there.\n if (options.url !== undefined) {\n router.applyUrl(options.url);\n }\n if (options.routes !== undefined) {\n router.setRoutes(options.routes);\n }\n if (options.media?.resolver !== undefined) {\n this.services.get(MediaService).setResolver(options.media.resolver);\n }\n if (options.media?.rasterizer !== undefined) {\n this.services.get(MediaService).setRasterizer(options.media.rasterizer);\n }\n if (options.media?.vectors !== undefined) {\n this.services.get(MediaService).setSvgResolver(options.media.vectors);\n }\n if (options.media?.videoResolver !== undefined) {\n this.services.get(MediaService).setVideoResolver(options.media.videoResolver);\n }\n\n this.scheduler = new UiScheduler({\n clock: options.clock ?? (callback => new UiTimerFrameClock(callback)),\n dirty: this.graph.getDirtyNodes(),\n beforeCollect: time => this.runPreCollectPhases(time),\n onFrame: frame => this.handleFrame(frame),\n /**\n * A frame that threw is reported and the clock keeps running.\n *\n * Both halves matter. Letting it escape used to report it — the\n * throw unwound into whatever was forwarding the tick — and stop\n * the application dead, because nothing re-armed the clock. An\n * application that merely stops looks exactly like a hang: the\n * last frame stays on screen and every click lands in a surface\n * nobody is listening to.\n */\n onFrameError: error => this.reportFrameError(error)\n });\n\n // An animation started between frames — from a click handler that\n // changes nothing else — has to arm the frame that will run its\n // first tick. Nothing in the graph is dirty at that moment, so\n // nothing else would.\n this.animations.setWakeListener(() => this.scheduler.wake());\n this.graph.setDirtyListener(() => this.scheduler.notifyDirty());\n this.graph.setNodeRemovedListener(node => {\n this.engine.detachNode(node);\n // Neither a selection nor a set of find matches can outlive its\n // nodes: virtualization and route changes both take them out from\n // under one.\n this.selectionController?.handleNodeRemoved(node);\n this.findController?.handleNodeRemoved(node);\n // Nor can focus: a closed dialog or a recycled row takes the\n // focused node with it.\n this.focusManager.handleNodeRemoved(node);\n this.layoutNotifier.handleNodeRemoved(node);\n this.smoothScroller.handleNodeRemoved(node);\n this.focusNotifier.handleNodeRemoved(node);\n this.environmentNotifier.handleNodeRemoved(node);\n });\n\n this.buildRoot(options.root);\n this.input = this.createInput();\n this.services.get(InspectionService).attach({\n nodesAt: (x, y) => this.hitTester.hitStack(x, y),\n inspect: node => this.inspectNode(node),\n explain: node => this.engine.explain(node),\n box: node => {\n const box = this.engine.visibleBox(node);\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n },\n screenBox: node => {\n const box = this.engine.screenBox(node);\n return { x: box.x, y: box.y, width: box.width, height: box.height };\n },\n preview: (node, property, value) => this.previewProperty(node, property, value),\n endPreview: (node, property) => this.endPreviews(node, property),\n previewInputs: (anchorId, inputs) => this.resolver.hostFor(anchorId)?.previewInputs(inputs),\n // A node this frame removed is a stale press, as a stale id is the mirror's.\n act: (node, action, value) =>\n this.graph.getNode(node.id) === node && this.actOn(node, value === undefined ? { action } : { action, value })\n });\n this.services.get(FocusService).setManager(this.input.focus);\n this.services.get(EditingService).setController(this.input.editing);\n this.services.get(ScrollService).setScroller((node, padding) => this.scrollIntoView(node, padding));\n // Keyboard navigation must keep the focused control visible — but\n // only keyboard navigation. A click has already shown the person\n // where they are, and revealing what they just pressed scrolls the\n // page out from under a pointer that is still resting on it: a\n // half-visible 562px card jumped 511px up the screen on the press,\n // before the transition it started had drawn a frame. A browser\n // draws the same line, and for the same reason. Focus from code that\n // asked for `preventScroll` is left where it is, as in a browser.\n this.input.focus.onFocusChange((node, source, options) => {\n if (node !== null && source !== 'pointer' && options.preventScroll !== true) {\n this.scrollIntoView(node);\n }\n });\n\n if (options.width !== undefined && options.height !== undefined) {\n this.resize(options.width, options.height, this.pixelRatio);\n }\n }\n\n /**\n * Aligns patch delivery from worker-owned channels to the frame.\n *\n * Without this a burst of patches rebuilds the bound subtree once per\n * patch, even though only the final state is ever drawn.\n */\n deferPatchesFrom(sources: readonly PatchSource[]): void {\n this.replicas = sources;\n for (const source of sources) {\n source.deferPatches(() => {\n this.scheduler.notifyDirty();\n if (now() - this.lastDiscreteInput < ANSWER_WINDOW_MS) {\n this.drawEarly();\n }\n });\n }\n }\n\n /** Starts the frame scheduler. */\n start(): void {\n this.started = true;\n this.scheduler.start();\n }\n\n /** The backend drawing frames, or `pending` while WebGPU initialises. */\n get rendererBackend(): RendererBackend | 'pending' {\n return this.rendererState;\n }\n\n /**\n * Receives renderer errors — GPU validation failures, device loss —\n * that would otherwise only reach the console of whichever thread\n * renders. Without a listener they are logged.\n */\n onRendererError(listener: ((message: string) => void) | null): void {\n this.rendererErrorListener = listener;\n }\n\n /**\n * Receives exceptions thrown by the application's own event\n * listeners — an `onClick` that throws.\n *\n * The dispatcher catches those so that one broken listener cannot\n * stop an event reaching the rest of the tree, which means nothing\n * outside it can see them. Without this hook they are logged to the\n * console of whichever thread dispatched, and in the worker\n * configuration that console is not the page's.\n */\n /**\n * Told when a frame throws, with the clock still running.\n *\n * Separate from `onListenerError` because the two are different\n * failures: a listener that throws has broken one interaction, and\n * a frame that throws has broken the thing that draws. It is\n * reported rather than fatal — see the scheduler — so somebody has\n * to be able to hear it.\n */\n onFrameError(listener: ((message: string, stack?: string) => void) | null): void {\n this.frameErrorListener = listener;\n }\n\n private reportFrameError(error: unknown): void {\n // What it drew before it threw is on screen, and was all the frame\n // collected: nothing is left dirty to draw it whole again. One frame\n // is asked for, and one only, so a frame that throws every time is\n // not drawn again and again at the display's rate.\n if (this.framesFailed++ === 0) {\n this.requestRepaint();\n }\n const message = error instanceof Error ? error.message : String(error);\n const stack = error instanceof Error ? error.stack : undefined;\n if (this.frameErrorListener !== null) {\n this.frameErrorListener(message, stack);\n return;\n }\n // eslint-disable-next-line no-console\n console.error('A Gesso frame threw. The frame was abandoned and the clock kept running.', error);\n }\n\n onListenerError(listener: ((message: string, stack?: string) => void) | null): void {\n this.dispatcher.onListenerError(\n listener === null\n ? null\n : (error, node, type) => {\n const message = error instanceof Error ? error.message : String(error);\n listener(\n `${message} (listener: ${type} on ${this.pathOf(node)})`,\n error instanceof Error ? error.stack : undefined\n );\n }\n );\n }\n\n private reportRendererError(message: string): void {\n if (this.rendererErrorListener !== null) {\n this.rendererErrorListener(message);\n return;\n }\n // eslint-disable-next-line no-console\n console.error(message);\n }\n\n /**\n * Replaces a WebGPU renderer that could not start, or lost its\n * device, with Canvas2D on the same canvas. The WebGPU path does not\n * touch the canvas until it has a device, so the 2D context is free.\n */\n private fallBackToCanvas2D(failed: UiRenderer): void {\n if (this.renderer !== failed) {\n return;\n }\n failed.dispose();\n this.canvasSurface = createCanvasSurface(this.canvas);\n this.renderer = new Canvas2DRenderer({ surface: this.canvasSurface });\n this.rendererState = 'canvas2d';\n this.renderer.resize(this.width, this.height, this.pixelRatio);\n this.requestRepaint();\n }\n\n /**\n * Asks for a new device for a renderer whose device was lost.\n *\n * Not a fall back to Canvas2D, which is what this once did: the\n * canvas already holds a WebGPU context, `getContext('2d')` on it\n * answers null, and the fallback threw on every frame from then on.\n * One request at a time; a failed one is reported, and the next frame\n * asks again.\n */\n private recoverWebGPU(renderer: WebGPURenderer): void {\n if (this.webgpuRecovery !== null) {\n return;\n }\n this.webgpuRecovery = renderer\n .recover()\n .then(() => {\n if (this.renderer === renderer) {\n renderer.resize(this.width, this.height, this.pixelRatio);\n this.requestRepaint();\n }\n })\n .catch((error: unknown) => {\n this.reportRendererError(`WebGPU could not get a new device: ${String(error)}`);\n })\n .finally(() => {\n this.webgpuRecovery = null;\n });\n }\n\n /**\n * The browser threw the canvas's pixels away and has handed it back\n * empty. Chrome does this to a canvas in a tab left hidden long\n * enough to be asked for its memory back, and to every canvas when\n * the GPU process restarts — which is what a Mac waking from sleep\n * often does. Nothing in the tree changed, so nothing is dirty and\n * no frame would ever come: the app sat there as a blank rectangle\n * until something happened to repaint it. Any layer the renderer\n * keeps was emptied with it, and reports itself as healthy again.\n */\n private surfaceRestored(): void {\n this.renderer.surfaceRestored?.();\n this.requestRepaint();\n }\n\n private requestRepaint(): void {\n if (this.root !== undefined) {\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n }\n\n /**\n * A declared font face finished loading (or failed, which changes\n * nothing but is not worth telling apart here). Every width measured\n * so far was measured in the fallback, so the measurer's cache, the\n * renderer's glyphs and the engine's sizes are all dropped, and the\n * whole tree is laid out and painted again in the face that was\n * meant. Text moves at most once per face, as with `font-display:\n * swap`.\n */\n private fontsChanged(): void {\n this.textMeasurer.invalidate?.();\n this.renderer.fontsChanged?.();\n this.engine.invalidateMeasurements();\n if (this.root !== undefined) {\n this.graph.markDirty(this.root, DirtyFlags.SubtreeLayout | DirtyFlags.Paint);\n }\n }\n\n /**\n * Resizes the surface and schedules a repaint.\n *\n * Zero-sized reports are ignored. A hidden or detached host delivers\n * 0x0, and a zero logical size makes the renderer's cull rectangle\n * empty, which discards every node.\n */\n resize(width: number, height: number, dpr: number = this.pixelRatio): void {\n if (!(width > 0) || !(height > 0)) {\n return;\n }\n this.pixelRatio = dpr;\n this.width = width;\n this.height = height;\n this.renderer.resize(width, height, dpr);\n this.constraints = Constraints.loose(width, height);\n if (this.root === undefined) {\n return;\n }\n if (this.laidOutOnce) {\n // Marked, not laid out here. `engine.layout()` is the *mount*\n // path: it retires every record and hands each one back through\n // `LayoutRecord.reset()`, which restores `measureDirty` and\n // clears `lastConstraints` — so it discards the measure memo for\n // the whole tree and pays a cold pass. A resize does not change\n // the tree, and the memo is exactly what makes the new size\n // cheap: the frame below reaches `fullLayout` through\n // `markLayoutDirty`, which dirties the root and nothing beneath\n // it, so a subtree whose constraints came out unchanged returns\n // at the memo instead of measuring again.\n //\n // On the benchmark list (5,001 nodes) that is 28.5 ms and 11,001\n // nodes measured, against 1.8 ms and *one* node measured for a\n // height-only drag. A width drag still re-measures what genuinely\n // re-wraps; it does not re-measure everything else as well.\n this.graph.markDirty(this.root, DirtyFlags.Layout | DirtyFlags.Paint);\n } else {\n // The first size the runtime is given, before any frame has run.\n // The mount path is the right one exactly once: there is no memo\n // to preserve, and a host that resizes before `start()` must\n // still end with a laid-out tree, since nothing else will lay one\n // out until the scheduler runs.\n this.laidOutOnce = true;\n this.engine.layout(this.root, this.constraints);\n // Resizing the backing store clears whatever was drawn, and\n // layout marks nothing dirty on its own, so without this the\n // canvas stays blank until some unrelated change happens to\n // schedule a frame.\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n // Drawn here, in the task that cleared the surface, rather than on\n // the next tick: a cleared canvas is committed to the compositor at\n // the end of this task, so a deferred repaint shows one blank frame\n // per resize notification, which reads as flicker while dragging.\n // Before start() there is nothing on screen to protect, and the\n // host has not wired its frame listeners yet.\n if (this.started) {\n this.scheduler.flush(now());\n }\n }\n\n /**\n * Receives per-frame timings. Used to report metrics across a\n * worker boundary, and by tests asserting frames actually ran.\n */\n onFrame(listener: ((metrics: FrameMetrics) => void) | null): void {\n this.frameListener = listener;\n }\n\n /**\n * Reports when the shell received the input just routed, so the\n * frame answering it can say how long it waited.\n *\n * Called by the host *after* handing the event to the input\n * controllers, because whether a frame is now pending is the test\n * for whether the input caused any work at all. A host that does not\n * call this leaves `FrameMetrics.inputLatencyMs` null, which is why\n * it is a separate call rather than a parameter on every input\n * method: measurement must not be a condition of routing an event.\n */\n noteInput(at: number | undefined): void {\n this.inputLatency.mark(at, this.scheduler.framePending);\n }\n\n /**\n * Draws the frame a discrete input armed (a key, a character typed, a\n * paste, a press) as soon as the input has been routed, rather than at\n * the next refresh.\n *\n * Waiting for the refresh is what typing cost. With keys at random\n * points in the frame, a key waited half a refresh on average and\n * nearly a whole one at worst before its frame began, so keystroke to\n * frame was p50 12.5 ms and p95 20.7 ms for about 5 ms of work:\n * a quarter of all keys over a frame at 60 Hz. A frame drawn in its\n * own task is committed when the task ends, and shown at the next\n * refresh the compositor has not yet taken, which is never later than\n * the frame the refresh would have drawn.\n *\n * It runs in a task of its own, a zero timer, not in the input's: a\n * key arrives as `keyDown` and then `beforeInput`, two messages posted\n * together, and one frame for both is the point. Continuous input\n * (pointer moves, the wheel) is left to the refresh, which already\n * coalesces it. Two guards keep it from costing more than it saves:\n * nothing early while the last frame took more than half a refresh,\n * when a second frame in the same refresh would take time the next\n * one needs; and no early frame within `EARLY_FRAME_GAP_MS` of the\n * last, so a burst of keys is still one frame per refresh or so.\n *\n * The answer to an input often comes later, from another thread: quick\n * open's results, a completion list. A channel's patch that arrives\n * within `ANSWER_WINDOW_MS` of a discrete input is drawn early the same\n * way (see `deferPatchesFrom`), so the answer does not wait for a\n * refresh either.\n */\n answerInput(): void {\n this.lastDiscreteInput = now();\n this.drawEarly();\n }\n\n /**\n * A step of the wheel, or of a trackpad's two fingers or its pinch,\n * held until the frame and added to the others that arrive before it.\n *\n * A trackpad sends a step on every refresh, and a handler that takes\n * longer than a refresh over one (a design tool's board zoomed over\n * hundreds of frames, each of whose names it scales) fell behind for\n * good: each step waited for the one before it, the frame waited for\n * them all, and a pinch drew nothing until the fingers stopped, then\n * jumped. Held, the steps that queue while the handler runs are one\n * step at the next frame, so the handler runs once a frame however\n * many arrive, and what it draws follows the fingers a frame at a\n * time. A step with other modifiers or in another unit is not added\n * to the held one but follows it, and any other input lets the held\n * step go first (`releaseWheel`), so nothing is answered out of order.\n */\n wheel(...step: Parameters<UiWheelController['wheel']>): void {\n const held = this.heldWheel;\n if (held !== null && !sameWheel(held, step)) {\n this.releaseWheel();\n }\n if (this.heldWheel === null) {\n this.heldWheel = step;\n this.scheduler.wake();\n return;\n }\n const [, , deltaX, deltaY, modifiers, deltaMode, wheelDeltaY] = this.heldWheel;\n const [x, y, moreX, moreY, , , moreWheelY, at] = step;\n const legacy = wheelDeltaY === undefined || moreWheelY === undefined ? moreWheelY : wheelDeltaY + moreWheelY;\n this.heldWheel = [x, y, deltaX + moreX, deltaY + moreY, modifiers, deltaMode, legacy, at];\n }\n\n /** Hands the held wheel step to the input now, before what follows it; see `wheel`. */\n releaseWheel(): void {\n const held = this.heldWheel;\n if (held === null) {\n return;\n }\n this.heldWheel = null;\n this.input.wheel.wheel(...held);\n }\n\n /** Draws the pending frame in a task of its own, if the guards in `answerInput` allow. */\n private drawEarly(): void {\n if (!this.started || !this.visible || this.earlyFrame !== null || !this.scheduler.framePending) {\n return;\n }\n if (this.lastFrameMs > EARLY_FRAME_MAX_MS || now() - this.lastEarlyFrame < EARLY_FRAME_GAP_MS) {\n return;\n }\n this.earlyFrame = setTimeout(() => {\n this.earlyFrame = null;\n if (!this.started || !this.scheduler.framePending) {\n return;\n }\n this.lastEarlyFrame = now();\n this.scheduler.flush(now());\n }, 0);\n }\n\n /**\n * Turns the layout inspector on or off. While on, every frame paints\n * the hovered node's boxes and the measure heatmap over the scene, and\n * the inspect listener receives the hovered node's explanation.\n */\n setInspectorEnabled(enabled: boolean): void {\n if (this.inspector.isEnabled === enabled) {\n return;\n }\n this.inspector.setEnabled(enabled);\n if (enabled) {\n this.inspector.setHovered(this.input.pointer.hoveredNode);\n }\n // Always sent, so a listener learns the toggle even when the\n // report happens to match (null before and after).\n const report = this.hoveredReport();\n this.lastInspection = report?.explanation ?? null;\n this.inspectListener?.(report);\n this.devtoolsListener?.({ kind: 'hover', report });\n if (this.root !== undefined) {\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n }\n\n /**\n * Receives a report on the hovered node whenever it changes while the\n * inspector is on, and null when nothing is hovered or the inspector\n * is turned off.\n *\n * The report carries `explanation`, which is what this used to send\n * on its own. It is plain data so that the worker configuration can\n * post it to a shell that has no access to the tree.\n */\n onInspect(listener: ((report: UiNodeReport | null) => void) | null): void {\n this.inspectListener = listener;\n }\n\n /**\n * Receives what a devtools panel asked for and what it is watching:\n * tree snapshots, node reports, and nothing else from here (console\n * entries are added by whoever owns the worker global).\n *\n * One listener, like the others on this class: the host forwards\n * events to its shell, and the shell fans them out.\n */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void {\n this.devtoolsListener = listener;\n }\n\n /**\n * Answers a devtools panel (`DevtoolsProtocol.ts`).\n *\n * `console` is not handled here: the runtime does not own the\n * worker's global, and in the same-thread configuration there is\n * nothing to forward. The host that owns the global handles it\n * before the request gets this far.\n */\n handleDevtools(request: DevtoolsRequest): void {\n switch (request.kind) {\n case 'tree':\n this.sendTree();\n break;\n case 'watchTree':\n this.watchingTree = request.enabled;\n if (request.enabled) {\n this.sendTree();\n }\n break;\n case 'inspect':\n this.devtoolsListener?.({ kind: 'report', id: request.id, report: this.inspectNodeById(request.id) });\n break;\n case 'select':\n this.selectedId = request.id;\n this.lastSelectedReport = null;\n if (request.id !== null) {\n this.sendSelectedReport();\n }\n break;\n case 'highlight':\n this.setHighlightedNode(request.id);\n break;\n case 'watchFrames':\n this.watchingFrames = request.enabled;\n break;\n case 'inspector':\n this.setInspectorEnabled(request.enabled);\n break;\n case 'setProp':\n this.writeInspectedProperty(request.id, request.name, request.value);\n break;\n case 'marks':\n setPerformanceMarks(request.enabled);\n break;\n case 'console':\n break;\n }\n }\n\n /**\n * Writes a property from a panel, on a node named by id.\n *\n * Through the same call the builder makes, so the write is an\n * ordinary one: a declared transition animates towards it, the\n * override cascade decides whether a modifier is already writing\n * this property, the equality check drops a write that changes\n * nothing, and the registry says what the property invalidates. A\n * panel that reached into the node's own map instead would produce a\n * value the cascade does not know about and a screen that does not\n * redraw.\n *\n * `null` removes the property rather than writing null, which is how\n * an inherited value is put back; that one goes past the cascade,\n * because there is no value to cascade.\n *\n * The selected node's report is re-sent by the frame this dirties,\n * so nothing is echoed from here.\n */\n private writeInspectedProperty(id: string, name: string, value: unknown): void {\n const node = this.graph.getNode(id);\n if (node === undefined) {\n return;\n }\n const effects = propertyEffects(name);\n if (value === null) {\n this.graph.applyResolvedProperty(node, name, false, undefined, effects);\n return;\n }\n this.graph.updateNodeProperty(node, name, value, effects);\n }\n\n /**\n * The tree as a panel lists it, from the application's root.\n *\n * The runtime's own layout root and the overlay layer beside the app\n * root are left out: neither is something the application wrote,\n * and the layout root is exactly the node `beneathAtPointer` also\n * hides for being nobody's intent.\n */\n snapshotTree(): UiTreeSnapshot {\n const root = this.debugRoot();\n let count = 0;\n const visit = (node: UiNode): UiTreeNode => {\n count++;\n const children: UiTreeNode[] = [];\n for (let child = node.firstChild; child !== null; child = child.nextSibling) {\n children.push(visit(child));\n }\n const host = this.resolver.hostFor(node.id);\n const text = treeText(node.getProperty('text'));\n // Counted per node and added up by the panel towards the nearest\n // component anchor, because the tree is what knows which\n // component a node belongs to and the snapshot is already being\n // walked.\n const subscriptions = this.graph.subscriptionsForNode(node);\n return {\n id: node.id,\n type: node.type,\n ...(host === undefined ? {} : { component: getComponentMetadata(host.component).tag }),\n ...(text === undefined ? {} : { text }),\n ...(subscriptions === 0 ? {} : { subscriptions }),\n children\n };\n };\n return { root: visit(root), nodes: count, subscriptions: this.graph.subscriptionCount };\n }\n\n /**\n * Sends a snapshot, and remembers the subscription count that went\n * with it.\n *\n * Every route to a snapshot goes through here, so the count a later\n * frame compares against is the one a panel was last told, whichever\n * request produced it.\n */\n private sendTree(): void {\n const tree = this.snapshotTree();\n this.lastSubscriptions = tree.subscriptions;\n this.devtoolsListener?.({ kind: 'tree', tree });\n }\n\n /**\n * The theme a node sees, by id, or with none named the theme the\n * application's outermost content sees: from the application's root,\n * down through every node with exactly one child, which passes the\n * wrappers an application usually puts its theme on. Undefined before\n * the root is built, or for an id the tree does not hold.\n */\n themeAt(id?: string): UiTheme | undefined {\n let node = id === undefined ? this.appRoot : this.graph.getNode(id);\n if (node === undefined) {\n return undefined;\n }\n if (id === undefined) {\n while (node.firstChild !== null && node.firstChild === node.lastChild) {\n node = node.firstChild;\n }\n }\n return (node.environment ?? this.graph.buildNodeEnvironment(node)).get(UiEnvironmentKeys.theme);\n }\n\n /**\n * `InspectionService.preview`: a write over the node's own value, as\n * a modifier's is, from a source of its own placed after every\n * modifier, so it wins and ending it restores what was under it.\n */\n private previewProperty(node: UiNode, property: string, value: unknown): void {\n const previewed = this.previewed.get(node) ?? new Set<string>();\n previewed.add(property);\n this.previewed.set(node, previewed);\n writeOverrideProperty(\n this.graph,\n node,\n property,\n { source: PREVIEW, name: 'preview', order: Number.MAX_SAFE_INTEGER, value, snap: true },\n propertyEffects(property)\n );\n }\n\n private endPreviews(node: UiNode, property?: string): void {\n const previewed = this.previewed.get(node);\n if (previewed === undefined) {\n return;\n }\n for (const name of property === undefined ? [...previewed] : [property]) {\n if (previewed.delete(name)) {\n clearOverrideProperty(this.graph, node, name, PREVIEW, propertyEffects(name));\n }\n }\n if (previewed.size === 0) {\n this.previewed.delete(node);\n }\n }\n\n /** `inspectNode` for a node named by id, or null when the tree has no such node. */\n inspectNodeById(id: string): UiNodeReport | null {\n const node = this.graph.getNode(id);\n return node === undefined ? null : this.inspectNode(node);\n }\n\n /**\n * Outlines a node on the canvas for a panel that picked it from the\n * tree, or clears the outline with null. An id the tree does not\n * have clears it too, since there is nothing to point at.\n */\n setHighlightedNode(id: string | null): void {\n const node = id === null ? undefined : this.graph.getNode(id);\n if (this.inspector.setHighlighted(node ?? null) && this.root !== undefined) {\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n }\n\n /**\n * Receives the cursor the hovered node asks for (`cursor: 'pointer'`\n * on it or an ancestor) whenever it changes, and null when nothing\n * under the pointer sets one. The shell applies it to the canvas —\n * the runtime has no DOM, in a worker least of all.\n */\n onCursor(listener: ((cursor: string | null) => void) | null): void {\n this.cursorListener = listener;\n }\n\n /** The cursor currently reported to the shell; null is the default arrow. */\n get cursor(): string | null {\n return this.lastCursor;\n }\n\n /**\n * Receives which way the runtime could scroll under the pointer,\n * whenever that changes.\n *\n * This exists because `preventDefault()` is a synchronous decision\n * and this runtime may be a worker message away from the DOM event\n * that needs it. A shell that swallows every wheel makes the canvas\n * a scroll trap in the page around it; one that swallows none lets\n * a scroll happen twice. Neither is a guess it can make locally, so\n * the answer is pushed ahead of the event and read from a cache\n * when one arrives — at worst one frame stale, which is the same\n * trade a browser makes to scroll off the main thread.\n */\n onScrollability(listener: ((scrollability: UiScrollability, scrollsAnything: boolean) => void) | null): void {\n this.scrollabilityListener = listener;\n }\n\n /** Which way the pointer's scroll chain can currently move. */\n get scrollability(): UiScrollability {\n return this.lastScrollability;\n }\n\n /**\n * Receives the focused editable's text, selection and caret box after\n * any frame that changed them, and null when no editable has focus.\n * The shell's editing proxy mirrors it; see `EditingProxy`.\n */\n onEditingState(listener: ((state: EditingState | null) => void) | null): void {\n this.editingListener = listener;\n }\n\n /** The editing state last reported to the shell. */\n get editingState(): EditingState | null {\n return this.lastEditingState;\n }\n\n /**\n * Receives what components ask of the shell through `ShellService`:\n * clipboard writes and URLs to open. Without a listener they are\n * dropped.\n */\n /** A request for the shell: sent, or held until `onShellRequest` gives it somewhere to go. */\n private toShell(request: ShellRequest): void {\n if (this.shellListener !== null && this.shellListener !== undefined) {\n this.shellListener(request);\n } else if (this.heldShellRequests.length < HELD_SHELL_REQUESTS) {\n this.heldShellRequests.push(request);\n }\n }\n\n onShellRequest(listener: ((request: ShellRequest) => void) | null): void {\n this.shellListener = listener;\n // What was asked before anyone was listening, handed over now. The\n // root is built in the constructor, so a component that reads a\n // stored preference as it mounts asks before a shell *can* have\n // attached — and a storage or file request is a promise waiting for\n // its answer, which a dropped request never gets.\n if (listener !== null) {\n const held = this.heldShellRequests;\n this.heldShellRequests = [];\n for (const request of held) {\n listener(request);\n }\n }\n }\n\n /**\n * Receives what components ask of the shell's audio element through\n * `AudioService`. Without a listener they are dropped, and the app\n * plays nothing, which is what a headless runtime should do.\n */\n onAudioRequest(listener: ((request: AudioRequest) => void) | null): void {\n this.audioListener = listener;\n }\n\n /** The shell reports its audio element; passed straight through to `AudioService`. */\n applyAudioSample(sample: AudioSample): void {\n this.services.get(AudioService).applySample(sample);\n }\n\n /** The platform's media controls acted; passed straight through to `AudioService`. */\n applyAudioAction(action: AudioAction): void {\n this.services.get(AudioService).applyAction(action);\n }\n\n /**\n * Where typed text comes from. A shell with an editing proxy delivers\n * it through `input.editing.beforeInput` and the composition methods,\n * so printable key presses must not be inserted a second time; with\n * `keys` (the default) they are all there is.\n */\n setTextInputSource(source: 'proxy' | 'keys'): void {\n this.input.editing.textFromKeys = source === 'keys';\n }\n\n /**\n * The page was hidden or shown. A hidden page stops the caret blink,\n * so a background tab with a focused field schedules no frames.\n *\n * The driver is told as well, and it is the half that matters to\n * what the page looks like: a hidden page goes on painting but runs\n * no animation frames, so anything the driver is holding would be\n * drawn frozen at whatever value it had reached. See\n * `AnimationDriver.setHidden` for why an entrance frozen at its\n * first value is a hole in the page rather than a paused animation.\n */\n /**\n * The shell says the surface entered or left fullscreen.\n *\n * Reported rather than inferred from the request that asked for it,\n * because the person can leave with Escape and a browser can refuse\n * to enter at all; a control that tracked its own last press would\n * then point the wrong way.\n */\n setFullscreen(active: boolean): void {\n this.services.get(ShellService).applyFullscreen(active);\n }\n\n setVisible(visible: boolean): void {\n this.input.editing.setVisible(visible);\n if (visible === this.visible) {\n return;\n }\n this.visible = visible;\n this.animations.setHidden(!visible);\n if (!visible) {\n return;\n }\n if (!this.started) {\n return;\n }\n // One frame on the way back, whether or not anything is dirty: an\n // animation the driver kept running while hidden is still in it,\n // and `scheduleAnimationTick` only re-arms from inside a frame.\n // And a whole repaint rather than whatever is dirty, because a\n // hidden canvas is the one a browser takes back, and not every\n // engine says so with `contextrestored`.\n this.surfaceRestored();\n this.scheduler.wake();\n }\n\n /**\n * The person has asked for less motion, or stopped asking.\n *\n * `ShellRequest` is outbound only and nothing carried a preference\n * inbound before this, so honouring reduced motion is plumbing that\n * had to be built rather than a setting that had to be read: the\n * shell has the media query, the runtime has the animations, and in\n * a render worker there is a thread boundary between them. It\n * arrives the way `visibility` does, and lands on the driver, which\n * is the one place every animation passes through.\n *\n * Not an environment key. A theme is scoped because different parts\n * of a screen legitimately look different; a motion preference\n * belongs to the person, not to a region of the tree, and an\n * animation drives a cell, which has no node to resolve a scoped\n * value against.\n */\n setReducedMotion(reduced: boolean): void {\n this.services.get(AnimationService).applyReducedMotion(reduced);\n }\n\n /**\n * The window's address, as the shell reports it: once at start-up,\n * and again for every back, forward or typed address.\n *\n * Guards run on it — a url the person typed is the navigation a\n * guard exists for — so a refused url is corrected back through the\n * history sink rather than shown.\n */\n setUrl(url: string): void {\n this.services.get(RouterService).applyUrl(url);\n }\n\n /**\n * The appearance the shell reports: once at start-up, and again\n * whenever the platform's answer or the host's override changes.\n *\n * Passed straight through to `ShellService`, where an application\n * reads it. Nothing here acts on it — unlike `reducedMotion`, which\n * the animation driver consumes, no part of the framework knows what\n * dark should look like.\n */\n setColorScheme(scheme: ColorScheme): void {\n this.services.get(ShellService).applyColorScheme(scheme);\n }\n\n /** Whether the window is the one in use, passed straight to `ShellService`; see `ShellService.windowActive`. */\n setWindowActive(active: boolean): void {\n this.services.get(ShellService).applyWindowActive(active);\n }\n\n /**\n * What the person points with, as the shell reports it: kept on\n * `ShellService.pointer`, and provided at the root, so every theme in\n * the tree is turned for a finger on a touch device (`withPointer`)\n * without the application doing anything. A subtree that says\n * `pointer` itself keeps what it says.\n */\n setPointer(pointer: UiPointer): void {\n this.services.get(ShellService).applyPointer(pointer);\n if (this.graph.updateNodePropertyNow(this.graph.root, 'pointer', pointer, DirtyFlags.Environment)) {\n this.requestRepaint();\n }\n }\n\n /** The contrast the platform asks for, passed straight to `ShellService`; see `ShellService.contrast`. */\n setContrast(contrast: UiContrast): void {\n this.services.get(ShellService).applyContrast(contrast);\n }\n\n /**\n * The platform's own insets, as the shell reports them: the safe area\n * under a notch or a home indicator and the strip a soft keyboard\n * covers, once at start-up and again whenever they change.\n *\n * Two things happen to them, and both are on this side of the\n * boundary because both are decisions. They reach `ShellService`,\n * where an application can read the raw numbers as `colorScheme` is\n * read. And they are published into the inset registry the app root's\n * environment carries, as one contributor beside the application's\n * own floating bars, so `insetPadding` on a screen keeps clear of the\n * keyboard without the application writing a line. The registry\n * composes by maximum, so a bar drawn across the home indicator and\n * the home indicator under it cost the content one strip, not two;\n * see `UiInsetRegistry` for the reasoning.\n *\n * The registry is the one at the app root rather than one this\n * runtime owns because an application provides its own with the\n * `insets` prop and every screen reads that one. Without a provider\n * the key's default registry is used, which is what every reader\n * under such a root resolves too. A registry provided below the root\n * is not found; the application feeds `ShellService.viewportInsets`\n * into it, which is why that cell exists.\n */\n setViewportInsets(insets: UiInsets): void {\n if (insetsEqual(this.viewportInsetValue, insets)) {\n return;\n }\n this.viewportInsetValue = insets;\n this.services.get(ShellService).applyViewportInsets(insets);\n this.publishViewportInsets();\n }\n\n /**\n * Writes the platform's insets into whichever registry the app root\n * resolves right now, moving the contribution when that changes.\n *\n * Called when the insets change, when the root is built or reloaded,\n * and when the root's environment is rebuilt, because the registry\n * is an environment value and a reload may provide a different one.\n * The old registry gets its room back before the new one is written,\n * exactly as a retracted bar would.\n */\n private publishViewportInsets(): void {\n const appRoot = this.appRoot;\n const source =\n appRoot === undefined\n ? null\n : (appRoot.environment ?? this.graph.buildNodeEnvironment(appRoot)).get(UiEnvironmentKeys.insets);\n const registry = source instanceof UiInsetRegistry ? source : null;\n if (registry !== this.viewportInsetRegistry) {\n this.viewportInsetWrite?.();\n this.viewportInsetWrite = null;\n this.viewportInsetRegistry = registry;\n }\n if (registry === null) {\n // A source that is not a registry is somebody's read-only view of\n // one, and there is nothing to publish into; `publishInset` makes\n // the same call.\n return;\n }\n if (this.viewportInsetWrite === null) {\n this.viewportInsetWrite = registry.publish(this.viewportInsetValue);\n } else {\n this.viewportInsetWrite(this.viewportInsetValue);\n }\n }\n\n /**\n * Reports what became of a popup a component asked for, settling the\n * promise `ShellService.openPopup` returned.\n *\n * The one inbound message that answers an outbound one, so unlike the\n * preference setters beside it this carries the id it is replying to.\n */\n settlePopup(id: number, opened: boolean): void {\n this.services.get(ShellService).settlePopup(id, opened);\n }\n\n /**\n * Reports whether a clipboard write landed, settling the promise\n * `ShellService.copyText` returned.\n */\n settleClipboard(id: number, copied: boolean): void {\n this.services.get(ShellService).settleClipboard(id, copied);\n }\n\n /** Reports what a clipboard read found, settling the promise `ShellService.readText` returned. */\n settleClipboardRead(id: number, text: string | null): void {\n this.services.get(ShellService).settleClipboardRead(id, text);\n }\n\n /**\n * Reports what the shell found in `localStorage`, settling the\n * promise `ShellService.requestStorage` returned.\n *\n * The second inbound message that answers an outbound one, and it\n * carries its request's id for the same reason the first does.\n */\n settleStorage(id: number, result: ShellStorageResult): void {\n this.services.get(ShellService).settleStorage(id, result);\n }\n\n /**\n * Reports what the shell did with a file request, settling the\n * promise `ShellService.requestFile` returned.\n */\n settleFile(id: number, result: ShellFileResult): void {\n this.services.get(ShellService).settleFile(id, result);\n }\n\n /** Whether the runtime is currently honouring a reduced-motion preference. */\n get reducedMotion(): boolean {\n return this.animations.isReducedMotion;\n }\n\n /** The appearance the shell last reported; `light` until it says otherwise. */\n get colorScheme(): ColorScheme {\n return this.services.get(ShellService).currentColorScheme;\n }\n\n /** The platform's insets the shell last reported; zeroes until it says otherwise. */\n get viewportInsets(): UiInsets {\n return this.viewportInsetValue;\n }\n\n /** Which shared-element names are currently held, for specs and devtools. */\n get sharedElementNames(): readonly string[] {\n return this.sharedElements.names;\n }\n\n /** `engine.explain` for any node, for tests and devtools. */\n explain(node: UiNode): LayoutExplanation {\n return this.engine.explain(node);\n }\n\n /**\n * Everything the inspector shows about one node, as plain data\n *.\n *\n * Built here rather than in `gesso-devtools` because every source\n * it reads is private to the render thread and most of it cannot\n * cross a thread boundary at all: the graph's bindings, the\n * builder's modifier sets, the resolver's component hosts. The\n * report is strings, so it can.\n */\n inspectNode(node: UiNode): UiNodeReport {\n const box = this.engine.visibleBox(node);\n return {\n id: node.id,\n type: node.type,\n box: { x: box.x, y: box.y, width: box.width, height: box.height },\n owners: this.ownersOf(node),\n props: this.propsOf(node),\n environment: this.environmentOf(node),\n modifiers: this.builder.modifiersFor(node)?.names ?? [],\n listens: this.dispatcher.listenerTypes(node),\n beneath: this.beneathAtPointer(node),\n semantics: this.semanticsOf(node),\n ...this.interactionOf(node),\n explanation: formatExplanation(this.engine.explain(node)),\n ...this.sourceReport(node)\n };\n }\n\n /**\n * The nodes under the pointer that this one is painted over, topmost\n * first, when the pointer is over this node at all. This is how the\n * inspector explains a dead click: the hovered node took the press,\n * and the button the person meant is listed here beneath it.\n */\n private beneathAtPointer(node: UiNode): UiBeneathReport[] {\n const at = this.input.pointer.position;\n if (at === null) {\n return [];\n }\n const stack = this.hitTester.hitStack(at.x, at.y);\n const index = stack.indexOf(node);\n if (index === -1) {\n return [];\n }\n // The layout root is the runtime's own wrapper, under everything\n // and never what a person meant to press.\n return stack\n .slice(index + 1)\n .filter(under => under !== this.root)\n .map(under => {\n const owners = this.ownersOf(under);\n return {\n id: under.id,\n type: under.type,\n ...(owners.length === 0 ? {} : { owner: owners[0]!.name }),\n listens: this.dispatcher.listenerTypes(under)\n };\n });\n }\n\n /**\n * A node as a path a person reads, for an error message: `App >\n * TrackScreen > ActionRow > Button \"Like\"`.\n *\n * The owner chain and the accessible name are both already computed\n * for the inspector; the only new thing here is that an error is\n * worth spending them on. See `formatNodePath`.\n */\n private pathOf(node: UiNode): string {\n const label = this.semanticsOf(node)?.label;\n return formatNodePath(this.ownersOf(node), {\n type: node.type,\n id: node.id,\n ...(label === undefined ? {} : { label })\n });\n }\n\n /** The components that rendered a node, nearest first. */\n private sourceReport(node: UiNode): { source?: UiSourceLocation } {\n const source = this.builder.sourceOf(node);\n return source === undefined ? {} : { source };\n }\n\n /** The props the node's `interactive` modifiers write while hovered and pressed, later ones over earlier. */\n private interactionOf(node: UiNode): { interaction?: UiInteractionReport } {\n let hovered: Record<string, unknown> | undefined;\n let pressed: Record<string, unknown> | undefined;\n for (const modifier of this.builder.modifiersFor(node)?.modifiers ?? []) {\n if (modifier.kind.name !== 'interactive') {\n continue;\n }\n const options = modifier.args as InteractiveOptions;\n if (options.hovered !== undefined) {\n hovered = { ...hovered, ...options.hovered };\n }\n if (options.pressed !== undefined) {\n pressed = { ...pressed, ...options.pressed };\n }\n }\n return hovered === undefined && pressed === undefined\n ? {}\n : {\n interaction: { ...(hovered === undefined ? {} : { hovered }), ...(pressed === undefined ? {} : { pressed }) }\n };\n }\n\n private ownersOf(node: UiNode): UiOwnerReport[] {\n const owners: UiOwnerReport[] = [];\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n // Asking the resolver rather than matching the id against\n // `:component:`: it holds a host for exactly the anchors that are\n // components, and a key with a colon in it would defeat the\n // string test.\n const host = this.resolver.hostFor(current.id);\n if (host !== undefined) {\n const usedAt = this.builder.sourceOf(current);\n const inputs: Record<string, UiOwnerInput> = {};\n for (const [name, supplied] of host.suppliedInputs()) {\n const value = ownerInput(supplied);\n if (value !== undefined) {\n inputs[name] = value;\n }\n }\n const live: Record<string, string | number | boolean | null> = {};\n for (const [name, value] of host.liveInputs()) {\n if (value === null || typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n live[name] = value;\n }\n }\n owners.push({\n name: getComponentMetadata(host.component).tag,\n anchorId: current.id,\n ...(usedAt === undefined ? {} : { usedAt }),\n ...(Object.keys(inputs).length === 0 ? {} : { inputs }),\n ...(Object.keys(live).length === 0 ? {} : { live })\n });\n }\n }\n return owners;\n }\n\n private propsOf(node: UiNode): UiPropReport[] {\n const sources = describeOverrides(node);\n const out: UiPropReport[] = [];\n for (const [name, value] of node.properties) {\n const source = sources?.[name];\n if (source !== undefined) {\n out.push({ name, value: printPropValue(value), origin: 'modifier', source });\n continue;\n }\n const binding = this.graph.getBindingForProperty(node, name);\n if (binding !== undefined) {\n // Which stream, what it last said, and how long ago. A yes to\n // \"is this bound\" is not enough to debug with: a stream that stopped and one that has\n // not emitted since the screen was built look the same.\n const stream = describeStream(binding, timeOrigin());\n out.push({\n name,\n value: printPropValue(value),\n origin: 'binding',\n source: `bound to ${stream.source}`,\n stream\n });\n continue;\n }\n out.push({ name, value: printPropValue(value), origin: 'element' });\n }\n out.sort((a, b) => a.name.localeCompare(b.name));\n return out;\n }\n\n /**\n * Every environment value in force at the node, and whether the node\n * is the one providing it.\n *\n * Walks the chain rather than reading the node's own map, because\n * the question the inspector answers is \"what does this node see\",\n * and most of what it sees was provided by an ancestor.\n */\n private environmentOf(node: UiNode): UiEnvironmentReport[] {\n const environment = node.environment;\n if (environment === null) {\n return [];\n }\n // A node that provides nothing shares its parent's environment\n // object rather than getting one of its own, so \"does my\n // environment provide this\" is true of every node under a\n // provider. The question the inspector is asking is narrower:\n // whether this node is the one that provided it.\n const own = environment === (node.parent?.environment ?? null) ? null : environment;\n const seen = new Set<string>();\n const out: UiEnvironmentReport[] = [];\n for (let current: UiEnvironment | null = environment; current !== null; current = current.parent) {\n for (const name of current.providedKeys()) {\n if (seen.has(name)) {\n continue;\n }\n seen.add(name);\n out.push({\n key: name,\n value: printPropValue(current.getOwn(name)),\n provided: current === own && own.providesOwn(name)\n });\n }\n }\n out.sort((a, b) => a.key.localeCompare(b.key));\n return out;\n }\n\n private semanticsOf(node: UiNode): UiSemanticsReport | undefined {\n const record = this.semanticsTree().get(node.id);\n if (record === undefined) {\n return undefined;\n }\n return {\n role: record.role,\n label: record.label,\n value: record.valueText ?? (record.valueNow === undefined ? undefined : String(record.valueNow)),\n states: record.states\n };\n }\n\n /**\n * The UiNode the app's root definition produced.\n *\n * For tests and devtools that inspect the retained graph without\n * reaching into private state. It is the first child of the layout\n * root, whose only other child is the overlay layer.\n */\n debugRoot(): UiNode {\n if (this.appRoot === undefined) {\n throw new Error('App root has not been built.');\n }\n return this.appRoot;\n }\n\n /** The laid-out box of a node, for tests and devtools. */\n debugLayoutBox(node: UiNode): { x: number; y: number; width: number; height: number } {\n return this.engine.worldBox(node);\n }\n\n /** Where the node is seen: scroll offsets and sticky shifts applied. */\n debugVisibleBox(node: UiNode): { x: number; y: number; width: number; height: number } {\n return this.engine.visibleBox(node);\n }\n\n /**\n * The node layout, hit testing and painting start from: a stack that\n * stretches the app root over the viewport with the overlay layer on\n * top of it.\n */\n layoutRoot(): UiNode {\n if (this.root === undefined) {\n throw new Error('App root has not been built.');\n }\n return this.root;\n }\n\n /**\n * Scrolls every scroll container above `node` just enough that the\n * node is inside its viewport, `padding` pixels from the nearest edge.\n * Nothing moves when it is already visible.\n *\n * Asked for from a layout listener — `autoFocus` taking focus is the\n * usual one — it waits until the frame's layout has settled, so it\n * reveals where the node ends up rather than where the first pass\n * put it (see `settleLayout`).\n */\n scrollIntoView(node: UiNode, padding = 8): void {\n if (this.deferredReveals !== null) {\n this.deferredReveals.push({ node, padding });\n return;\n }\n for (const adjustment of this.engine.revealAdjustments(node, padding)) {\n // A reveal knows exactly where the container has to be, and a\n // spring still running would overwrite that on its next tick.\n this.smoothScroller.stop(adjustment.container);\n adjustment.container.setProperty('scrollX', adjustment.scrollX);\n adjustment.container.setProperty('scrollY', adjustment.scrollY);\n this.graph.markDirty(adjustment.container, DirtyFlags.Transform);\n }\n }\n\n /**\n * Rebuilds the application's tree from a new root definition,\n * keeping the runtime and everything that is not the tree\n * (the HMR item).\n *\n * **Why a rebuild is the only story.** A Gesso component's `render`\n * runs once; there is no re-render pass to push new code through, so\n * replacing a module cannot be made to update a mounted component in\n * place. What can be done is to throw the tree away and build a new\n * one, and that is only useful if the things worth keeping do not\n * live in the tree. In this framework they do not: application state\n * is in a data worker behind a channel, and a replica's cells are\n * held by the runtime rather than by any component, so a freshly\n * built tree binds to values that are already there.\n *\n * That is why the \"snapshot projections as patches, replay\n * after mount\" is not here. It describes rebuilding a replica from\n * scratch, and nothing rebuilds one: the replica, the services, the\n * renderer, the canvas, the focus manager and the scheduler all\n * survive, because only the tree is replaced.\n *\n * **What is kept and what is lost.** The layout root node is the\n * same object, so input routing, the focus scope stack and every\n * listener registered on the root stay valid. Below it, the builder\n * reconciles rather than recreating, so a component whose class is\n * the same object as before keeps its host and its `internalState`;\n * one whose module was replaced is a different class, and its host\n * is disposed and mounted again with everything it rendered: its body\n * made new objects for its children, and a child kept from before\n * would go on holding the old ones. Scroll offsets on containers that\n * survive are kept, because the node is kept. Component state in a\n * replaced module is lost, which is the honest cost and the reason\n * this is a development tool.\n *\n * **Services from the replaced module have to be handed over.** A\n * registry is keyed by the class object, so a service defined beside\n * the root in a module that was replaced is a new class that the\n * registry has never seen, even though the old one is still in it.\n * Pass the replacements as `services` and the registry adopts them,\n * keeping their instances and therefore their state. One that is\n * genuinely new is registered instead.\n */\n reload(rootDefinition: FrameworkChild, services: readonly (new () => object)[] = []): void {\n if (this.root === undefined) {\n throw new Error('App root has not been built.');\n }\n // Before the tree is rebuilt, because the components in it inject\n // these on the way up and would otherwise ask for a class the\n // registry has never seen.\n for (const ServiceClass of services) {\n if (!this.services.adopt(ServiceClass)) {\n this.services.register(ServiceClass);\n }\n }\n // The hovered node may be about to be removed, and the inspector\n // would go on explaining it until the pointer next moved. The\n // highlighted one is about to be removed for certain.\n this.inspector.setHovered(null);\n this.inspector.setHighlighted(null);\n // Read before the rebuild and applied after the frame that lays\n // the new tree out, because that is the frame `autoFocus` fires\n // on and this has to be the last word.\n this.focusAfterReload = this.focusManager.focusedNode?.id ?? null;\n this.restoringFocus = true;\n this.buildRoot(rootDefinition);\n this.graph.markDirty(this.root, DirtyFlags.Children | DirtyFlags.SubtreeLayout | DirtyFlags.Paint);\n }\n\n /**\n * Puts the caret back where it was before a reload.\n *\n * A node id is positional and the builder reconciles, so the field\n * that had focus keeps its id across a replacement of the module\n * that rendered it, and the same id in the new tree is the same\n * place on the screen. When it is not there any more — the edit\n * removed it — the focus is cleared rather than left wherever the\n * rebuild happened to put it.\n */\n private restoreFocusAfterReload(): void {\n const id = this.focusAfterReload;\n this.focusAfterReload = null;\n const node = id === null ? undefined : this.graph.getNode(id);\n if (node === undefined || !this.focusManager.focus(node)) {\n this.focusManager.blur();\n }\n }\n\n dispose(): void {\n if (this.earlyFrame !== null) {\n clearTimeout(this.earlyFrame);\n this.earlyFrame = null;\n }\n if (this.scrollbarTimer !== null) {\n clearTimeout(this.scrollbarTimer);\n this.scrollbarTimer = null;\n }\n if (this.rendererTimer !== null) {\n clearTimeout(this.rendererTimer);\n this.rendererTimer = null;\n }\n if (this.semanticsSweepTimer !== null) {\n clearTimeout(this.semanticsSweepTimer);\n this.semanticsSweepTimer = null;\n }\n // The room the platform's insets took is given back: the default\n // registry is shared, and a disposed runtime must not go on\n // reporting a keyboard into it.\n this.detachViewportInsetEnvironment?.();\n this.detachViewportInsetEnvironment = null;\n this.viewportInsetWrite?.();\n this.viewportInsetWrite = null;\n this.viewportInsetRegistry = null;\n if (this.caretTimer !== null) {\n clearTimeout(this.caretTimer);\n this.caretTimer = null;\n }\n this.editingListener = null;\n this.shellListener = null;\n this.semanticsListener = null;\n if (this.inspectorTimer !== null) {\n clearTimeout(this.inspectorTimer);\n this.inspectorTimer = null;\n }\n if (this.animationTimer !== null) {\n clearTimeout(this.animationTimer);\n this.animationTimer = null;\n }\n this.inspectListener = null;\n this.devtoolsListener = null;\n this.cursorListener = null;\n this.scrollabilityListener = null;\n this.rendererErrorListener = null;\n this.frameErrorListener = null;\n this.scheduler.stop();\n // An animation holds its cell, and a cell holds whatever the\n // component that made it captured. A disposed runtime must not.\n this.animations.stopAll();\n this.sharedElements.clear();\n this.animations.setWakeListener(null);\n this.services.get(AnimationService).setDriver(null);\n this.services.get(AudioService).setAnimations(null);\n this.services.get(FindService).setController(null);\n this.services.get(FocusService).setManager(null);\n this.services.get(RouterService).setHistory(null);\n // Decoded bitmaps hold pixels; garbage collection is not prompt\n // about them, so they are closed rather than dropped.\n this.services.get(MediaService).dispose();\n this.services.get(FontService).dispose();\n this.graph.setEnvironmentChangedListener(null);\n this.graph.setDirtyListener(null);\n this.graph.setNodeRemovedListener(null);\n this.frameListener = null;\n // Its listeners are on the root node, which the dispatcher holds by\n // reference; a disposed runtime must not keep either alive.\n this.input.touchScroll.dispose();\n this.resolver.dispose();\n this.detachContextRestored?.();\n this.detachContextRestored = null;\n this.renderer.dispose();\n }\n\n /**\n * Resolves the root definition down to a plain element and builds it.\n *\n * The root is the one component slot the builder cannot anchor for\n * us: anchors are transparent Fragments, and a Fragment is never a\n * valid layout root — it contributes no box, so the layout engine\n * would have nothing to size the tree against. The runtime therefore\n * mounts the root host itself and hands the builder real geometry.\n *\n * Root hosts are still mounted through the resolver, so their\n * onMount() fires from the build pass below, once their nodes exist.\n */\n private buildRoot(rootDefinition: FrameworkChild): void {\n const appElement = this.resolveRootElement(rootDefinition, 0);\n // The app root stretches over the viewport exactly as it did when\n // it was the layout root; the overlay layer floats above it.\n this.root = this.builder.build(\n Stack({ x: 'stretch', y: 'stretch', position: 'relative' }, appElement, createComponent(OverlayLayer))\n );\n const appRoot = this.root.firstChild;\n if (appRoot === null) {\n throw new Error('The app root produced no node.');\n }\n this.appRoot = appRoot;\n this.graph.propagateEnvironment(this.root);\n // The registry the platform's insets go into is an environment\n // value on this node, so a new root, or a root whose environment is\n // rebuilt, may resolve a different one.\n this.detachViewportInsetEnvironment?.();\n this.detachViewportInsetEnvironment = this.environmentNotifier.add(appRoot, () => this.publishViewportInsets());\n this.publishViewportInsets();\n }\n\n private resolveRootElement(definition: FrameworkChild, depth: number): UiElement {\n if (isObservable(definition)) {\n throw new Error('Root definition cannot be an Observable. Wrap it in a component or static element.');\n }\n if (isComponentLikeElement(definition)) {\n if (depth > MAX_ROOT_COMPONENT_DEPTH) {\n throw new Error(\n `Root component chain exceeded ${MAX_ROOT_COMPONENT_DEPTH} levels without producing an element.`\n );\n }\n const anchorId = `app:component:${depth}`;\n const before = this.resolver.hostFor(anchorId);\n if (before !== undefined && before.component !== definition.component && this.root !== undefined) {\n // A root component whose module was replaced. Its body runs again\n // and makes new objects for what it renders, so the tree it\n // rendered before cannot be reconciled into: a child of a class\n // that did not change would be kept, still holding the old\n // objects it read when it mounted. The tree goes first, before\n // the new host is made, so nothing mounts onto a tree being\n // cleared. Ids are positional, so the rebuilt tree has the same\n // ones and the focus still finds its way back.\n this.builder.reconcileChildren(this.root, []);\n }\n const output = this.resolver.resolve(definition, anchorId);\n return this.resolveRootElement(output as FrameworkChild, depth + 1);\n }\n return definition;\n }\n\n /**\n * Builds the input stack over the freshly built tree.\n *\n * Handlers are registered on the dispatcher by the builder as it\n * reconciles `on*` props; this wires the other half — hit-testing,\n * pointer/wheel/keyboard routing, and focus — so those handlers\n * actually receive events.\n */\n private createInput(): RuntimeInput {\n const root = this.layoutRoot();\n const hitTester = new UiHitTester(this.engine, root);\n this.hitTester = hitTester;\n const focus = this.focusManager;\n focus.setRoot(root);\n const scrollSink = this.createScrollSink();\n // Editing is a default behaviour of the pointer and keyboard\n // controllers for EditableText targets; the shell's text input\n // (beforeinput, composition, paste) reaches the controller directly.\n const editing = new UiEditingController(\n {\n recordFor: node => this.engine.recordFor(node),\n visibleBox: node => this.engine.visibleBox(node),\n screenBox: (node, part) => this.engine.screenBox(node, part),\n toLocal: (node, x, y) => hitTester.toLocal(node, x, y),\n measurer: this.textMeasurer,\n markDirty: (node, flags) => this.graph.markDirty(node, flags),\n reveal: (node, box) => this.revealBox(node, box),\n now\n },\n this.dispatcher,\n focus\n );\n // Text that is not an editable has no model of its own, so its\n // selection is the controller's; it reaches the clipboard through\n // the same ShellService request a component would use.\n const selection = new UiSelectionController(\n {\n recordFor: node => this.engine.recordFor(node),\n visibleBox: node => this.engine.visibleBox(node),\n screenBox: node => this.engine.screenBox(node),\n measurer: this.textMeasurer,\n markDirty: (node, flags) => this.graph.markDirty(node, flags),\n root: () => this.layoutRoot(),\n copy: text => this.services.get(ShellService).copyText(text),\n blurEditable: () => {\n if (editing.focused !== null) {\n focus.blur();\n }\n },\n now\n },\n hitTester\n );\n // Focus moving into a field ends a canvas selection, so only one of\n // the two is ever lit.\n focus.onFocusChange(node => {\n if (node !== null) {\n selection.clear();\n }\n });\n this.selectionController = selection;\n // The browser's find bar cannot see a canvas, so the app gets its\n // own; the active match is a selection, which is why this is built\n // on top of the selection controller rather than beside it.\n const find = new UiFindController(\n {\n recordFor: node => this.engine.recordFor(node),\n measurer: this.textMeasurer,\n markDirty: (node, flags) => this.graph.markDirty(node, flags),\n reveal: (node, box) => this.revealBox(node, box),\n root: () => this.layoutRoot(),\n focus: node => {\n focus.focus(node);\n }\n },\n selection\n );\n this.findController = find;\n this.services.get(FindService).setController(find);\n return {\n dispatcher: this.dispatcher,\n focus,\n editing,\n selection,\n find,\n pointer: new UiPointerController(hitTester, this.dispatcher, {\n // Without this the controller has no recognizer to feed, and\n // `onPan*` / `onDrag*` never fire anywhere in a real app: the\n // events exist, components declare handlers for them, and\n // nothing ever synthesizes one. A `SplitPane` could be moved\n // from the keyboard and not with the pointer.\n gestures: new UiGestureRecognizer(this.dispatcher),\n onPress: node => {\n if (node !== null) {\n focus.focusOnPress(node);\n } else {\n // Nothing under the press, but it was a press: the person is\n // on the pointer, and a ring elsewhere should go.\n focus.noteInput('pointer');\n }\n },\n scrollSink,\n onHoverChange: node => this.handleHoverChange(node),\n editing,\n selection\n }),\n // The application's root, not the layout root that wraps it, for\n // the reason the keyboard's below gives: `overscrollBehavior` set\n // on the app's own top element has to be the one that is read.\n // Paced: a trackpad's steps are spread evenly over frames, which a\n // runtime can do because it advances the controller each frame.\n wheel: new UiWheelController(hitTester, this.dispatcher, scrollSink, () => this.appRoot ?? root, undefined, {\n pace: true\n }),\n // Unfocused keys land on the application's root, not the layout\n // root that wraps it: the wrapper is the runtime's, and an app\n // listening for Escape on its own top element would otherwise\n // never hear a key pressed with nothing focused. A getter, because\n // `reload` rebuilds the tree under a controller that lives on.\n keyboard: new UiKeyboardController(this.dispatcher, focus, () => this.appRoot ?? this.layoutRoot(), {\n editing,\n selection,\n find,\n // Enter on a focused button clicks its centre, as an assistive\n // technology's press does: the centre it is drawn at, for the\n // reason `applySemanticsAction` gives.\n activation: {\n clickAt: node => {\n const box = this.engine.screenBox(node);\n return { x: box.x + box.width / 2, y: box.y + box.height / 2 };\n }\n }\n }),\n // Listens at the root, so a pan reaches it only when nothing\n // between the pressed node and here claimed the gesture. That is\n // the opt-out a `Slider` or a `SplitPane` already relies on.\n touchScroll: new UiTouchScroller(this.dispatcher, root, scrollSink)\n };\n }\n\n /**\n * Receives what an accessibility mirror needs after any frame that\n * changed it: the semantics patches, the boxes that moved, and the\n * focused node when focus moved.\n *\n * The consumer is `SemanticsMirror` — an off-screen DOM tree over the\n * canvas that the platform's assistive technology reads . Attaching one is what turns the geometry sweep on; without a\n * listener the runtime keeps the tree and diffs it, and looks at no\n * boxes at all.\n */\n onSemantics(listener: ((update: UiSemanticsUpdate) => void) | null): void {\n this.semanticsListener = listener;\n this.semanticsBoxes.clear();\n if (listener !== null) {\n // Frames that ran while nothing was listening left it stale, and\n // a mirror must not be handed a tree from before them.\n this.semanticsTree();\n }\n if (listener === null || this.semantics.size === 0) {\n // Nothing to catch up on: a listener attached before the first\n // frame hears about the tree when the frame builds it.\n return;\n }\n // A listener attached after the first frame needs the tree that\n // already exists, as one patch per record, with the geometry and\n // the focus that go with it.\n listener({\n patches: [...this.orderedSemantics().values()].map(node => ({ op: 'add', node }) as const),\n boxes: this.sweepSemanticsBoxes(),\n focused: this.focusManager.focusedNode?.id ?? null\n });\n }\n\n /**\n * The id of the node that holds focus, or null. The same id the\n * semantics tree keys its records by, so a reader of the tree can\n * say which control a key press would reach.\n */\n focusedNodeId(): string | null {\n return this.focusManager.focusedNode?.id ?? null;\n }\n\n /**\n * The semantics tree as of the last frame that changed it.\n *\n * Rebuilt here when no mirror was listening and a frame marked it\n * stale, so a test or a devtools panel sees the current tree without\n * every frame having paid to keep one nothing was reading.\n */\n semanticsTree(): UiSemanticsMap {\n if (this.semanticsStale) {\n this.storeSemantics(buildSemanticsTree(this.layoutRoot(), this.semanticsMemory));\n this.semanticsStale = false;\n }\n return this.orderedSemantics();\n }\n\n /** Replaces the tree with one built in full, which is in document order. */\n private storeSemantics(tree: Map<string, UiSemanticsRecord>): void {\n this.semantics = tree;\n this.semanticsOrdered = tree;\n this.semanticsChildren = new Map();\n for (const record of tree.values()) {\n let list = this.semanticsChildren.get(record.parent);\n if (list === undefined) {\n this.semanticsChildren.set(record.parent, (list = []));\n }\n list.push(record.id);\n }\n }\n\n /**\n * The tree in document order, every index current: a walk down\n * `semanticsChildren`, done when someone asks rather than per frame.\n * Records whose index had gone out of date are brought up to date in\n * the store as well.\n */\n private orderedSemantics(): Map<string, UiSemanticsRecord> {\n if (this.semanticsOrdered !== null) {\n return this.semanticsOrdered;\n }\n const ordered = new Map<string, UiSemanticsRecord>();\n const stack: [string, number][] = [];\n const push = (parent: string | null): void => {\n const list = this.semanticsChildren.get(parent) ?? [];\n const indices = semanticIndices(list);\n for (let i = list.length - 1; i >= 0; i--) {\n stack.push([list[i]!, indices[i]!]);\n }\n };\n push(null);\n while (stack.length > 0) {\n const [id, index] = stack.pop()!;\n let record = this.semantics.get(id)!;\n if (record.index !== index) {\n record = { ...record, index };\n this.semantics.set(id, record);\n }\n ordered.set(id, record);\n push(id);\n }\n this.semanticsOrdered = ordered;\n return ordered;\n }\n\n /** A record's index among its siblings now, which its own `index` may not say. */\n private semanticsIndexOf(record: UiSemanticsRecord): number {\n const list = this.semanticsChildren.get(record.parent) ?? [];\n const at = list.indexOf(record.id);\n return at < 0 ? record.index : semanticIndices(list)[at]!;\n }\n\n /**\n * Something an assistive technology did to a mirrored element,\n * turned back into ordinary input.\n *\n * Deliberately routed through the same controllers a pointer and a\n * keyboard use rather than into components directly: an AT press on\n * a `Checkbox` has to reach the `onClick` the mouse reaches, or the\n * two paths drift and only one of them is tested. `focus` goes\n * through the focus manager, which means an AT cannot escape an open\n * focus trap any more than Tab can.\n */\n /**\n * Files from outside the application, dragged over it or dropped.\n *\n * Handed to the tree's drag session, which turns them into a drag\n * like any other, so a zone that accepts `EXTERNAL_FILES` answers\n * the drop exactly as it answers one from inside. A drop that\n * arrives before there is a tree has nowhere to land and is dropped.\n */\n applyFileDrop(message: UiFileDropMessage): void {\n if (this.root === undefined) {\n return;\n }\n dragSessionFor(this.root).applyFileDrop(message);\n }\n\n applySemanticsAction(action: UiSemanticsAction): void {\n if (this.applyTextRunAction(action)) {\n return;\n }\n const node = this.graph.getNode(action.id);\n if (node === undefined || !this.semantics.has(action.id)) {\n // A stale id: the mirror acted on a node this frame removed.\n return;\n }\n this.actOn(node, action);\n }\n\n /**\n * A semantics action on a node: the mirror's, or `InspectionService.act`'s\n * on a node with no record of its own. False when it did not take: focus\n * refused (an open dialog's trap holds it elsewhere), or a field that\n * takes no text.\n */\n private actOn(node: UiNode, action: Pick<UiSemanticsAction, 'action' | 'value'>): boolean {\n if (action.action === 'focus') {\n return this.focusManager.focus(node);\n }\n if (action.action === 'setValue') {\n // The editing controller edits whatever holds focus, so focus is\n // part of the action rather than a precondition the caller has\n // to arrange. Refused, it is not: the text would go into whatever\n // does hold it, another field than the one named.\n return this.focusManager.focus(node) && this.input.editing.replaceText(action.value ?? '');\n }\n this.focusManager.focus(node);\n // The centre the node is drawn at, which is where a pointer press\n // on it would land and so what its handlers expect: a click is in\n // canvas space. Its record's centre is where it would be drawn with\n // no transform above it, so a press on a card under a zoomed map\n // camera arrived somewhere off the card, and a handler that turns\n // the point into a fraction of its `layoutBox` read nonsense.\n const box = this.engine.screenBox(node);\n this.dispatcher.dispatch(\n new UiPointerEvent(UiEventType.Click, box.x + box.width / 2, box.y + box.height / 2, 1),\n node\n );\n return true;\n }\n\n /**\n * An action on an inline link, which is a run and not a node.\n *\n * A run borrows its paragraph's id and adds its position, so the\n * lookup above would miss it and the action would be dropped as\n * stale. Activation calls the run's own `onClick`, which is the same\n * call a press makes in `UiSelectionController`, so a link opened\n * from the keyboard and a link opened with the pointer go through one\n * path rather than two.\n *\n * `focus` and `setValue` are not answered. A run cannot hold focus,\n * because focus is a node in `UiFocusManager`, and a run has no value\n * to set. Returning true for them anyway is deliberate: the id did\n * name a run, so falling through to the node lookup would only find\n * nothing and read as a stale id.\n */\n private applyTextRunAction(action: UiSemanticsAction): boolean {\n const run = textRunOfRecordId(action.id);\n if (run === null) {\n return false;\n }\n if (action.action === 'focus' || action.action === 'setValue') {\n return true;\n }\n const node = this.graph.getNode(run.nodeId);\n if (node !== undefined && this.semantics.has(action.id)) {\n linkOf(node, run.index)?.onClick?.();\n }\n return true;\n }\n\n /**\n * Rebuilds the semantics tree, and gathers what moved.\n *\n * The two halves have different triggers — meaning changes when a\n * semantics property or the shape of the tree does, position changes\n * whenever anything is laid out or scrolled — so each is asked for\n * separately and an update is sent only if one of them has something\n * to say. `UiSemanticsUpdate` explains why they travel together\n * anyway.\n */\n private updateSemantics(frame: UiFrame, rebuild: boolean, moved: boolean): void {\n let patches: readonly UiSemanticsPatch[] = EMPTY_PATCHES;\n const listener = this.semanticsListener;\n if (rebuild) {\n if (listener === null) {\n // Nobody is reading it. Building and diffing the tree would be\n // a walk of every node for no one, on frames that are now far\n // more common than they were: `text` marks semantics dirty, so\n // any bound label does this. The work moves to whoever asks.\n this.semanticsStale = true;\n } else {\n patches = this.rescopeSemantics(frame) ?? this.restructureSemantics(frame) ?? this.rebuildSemantics();\n }\n }\n if (listener === null) {\n return;\n }\n const boxes = patches.length > 0 ? this.sweepSemanticsBoxes() : moved ? this.movedSemanticsBoxes() : EMPTY_BOXES;\n const focused = this.focusManager.focusedNode?.id ?? null;\n const focusMoved = focused !== this.lastFocusedId;\n this.lastFocusedId = focused;\n if (patches.length === 0 && boxes.length === 0 && !focusMoved) {\n return;\n }\n listener(focusMoved ? { patches, boxes, focused } : { patches, boxes });\n }\n\n /** Walks the whole tree and diffs it against the last one. */\n private rebuildSemantics(): readonly UiSemanticsPatch[] {\n const previous = this.orderedSemantics();\n const next = buildSemanticsTree(this.layoutRoot(), this.semanticsMemory);\n const patches = dropIndexShifts(previous, next, diffSemantics(previous, next));\n this.storeSemantics(next);\n this.semanticsStale = false;\n return patches;\n }\n\n /**\n * The patches for a frame that changed what some nodes *mean*\n * without changing which nodes there are — or null when that cannot\n * be established cheaply, and the whole tree has to be walked.\n *\n * This is the common case and it used to cost the uncommon one. A\n * bound label is `text`, `text` marks semantics dirty, and the phase\n * answered every one of them by rebuilding the tree from the root\n * and diffing two maps: on the benchmark list, 2 ms on any frame a\n * clock ticked, next to 0.03 ms for the layout of the same change.\n *\n * So the walk is rooted at what actually changed. For each dirty\n * node the nearest ancestor *carrying a record* is found — a\n * `Button`'s label lives on the button, not on the `Text` inside it\n * that a parent claimed — and only that node's subtree is walked.\n * `buildSemanticsSubtree` explains why the owner has to be a record\n * holder for the indices to come out the same.\n *\n * It gives up, and says so with null, whenever the *shape* of the\n * tree could have moved:\n *\n * - a frame that changed the tree's children, which is a structural\n * change by definition;\n * - a dirty node with no record-holding ancestor, whose records sit\n * at the top level and are numbered among everything else there;\n * - an owner that is no longer describable, whose children would\n * reattach further up;\n * - a subtree that gained or lost a record, because `this.semantics`\n * is insertion-ordered and the diff promises adds in document\n * order — splicing one into the middle of a Map cannot preserve\n * that, and a mirror appending a child before its parent is a\n * worse bug than a slow frame.\n *\n * Each of those falls back to the full rebuild, which is correct by\n * construction. Nothing here decides what a record says; it only\n * decides how much of the tree has to be asked.\n */\n private rescopeSemantics(frame: UiFrame): readonly UiSemanticsPatch[] | null {\n if (this.semantics.size === 0 || this.semanticsStale) {\n // No previous tree to scope against, or one already owed in full.\n return null;\n }\n const owners = new Set<UiNode>();\n for (const [node, flags] of frame.entries()) {\n if ((flags & DirtyFlags.Children) !== 0) {\n return null;\n }\n if ((flags & DirtyFlags.Semantics) === 0) {\n continue;\n }\n const owner = this.semanticsOwnerOf(node);\n if (owner === null) {\n return null;\n }\n owners.add(owner);\n if (owners.size > SCOPED_SEMANTICS_OWNERS) {\n // Enough of the tree is moving that one walk of all of it beats\n // this many walks of parts, and the parts may overlap besides.\n return null;\n }\n }\n if (owners.size === 0) {\n return null;\n }\n\n // Built completely before anything is committed, so a give-up part\n // way through leaves `this.semantics` as it was.\n const updates: UiSemanticsRecord[] = [];\n for (const owner of owners) {\n const previous = this.semantics.get(owner.id)!;\n const subtree = buildSemanticsSubtree(\n owner,\n previous.parent,\n this.semanticsIndexOf(previous),\n semanticsInertAbove(owner),\n this.semanticsMemory\n );\n if (subtree === null) {\n return null;\n }\n let covered = 0;\n for (const id of this.semanticIdsUnder(owner)) {\n const next = subtree.get(id);\n if (next === undefined) {\n return null;\n }\n covered++;\n // A stored index can be out of date (see `semantics`), and\n // nothing here moved, so only what the record says is compared.\n const before = this.semantics.get(id)!;\n if (!recordsEqual({ ...before, index: next.index }, next)) {\n updates.push(next);\n } else if (before.index !== next.index) {\n this.semantics.set(id, next);\n }\n }\n if (covered !== subtree.size) {\n // The subtree gained a record; see the note about ordering.\n return null;\n }\n }\n\n const patches: UiSemanticsPatch[] = [];\n for (const record of updates) {\n // Same key, so a map keeps the position, and therefore the\n // document order, it already had.\n this.semantics.set(record.id, record);\n this.semanticsOrdered?.set(record.id, record);\n patches.push({ op: 'update', node: record });\n }\n return patches;\n }\n\n /**\n * The patches for a frame that changed the shape of the tree, without\n * walking all of it, or null when the whole tree has to be rebuilt.\n *\n * Any frame that added or removed a child used to rebuild the tree\n * from the root, describing every node again. In a 5,000-line editor\n * that was 11 ms of every Enter, for one new paragraph. Now the walk\n * starts at the nearest record above each change, as\n * `rescopeSemantics` does for a change of meaning, and inside it a\n * subtree nothing touched keeps its records: renumbered if its\n * siblings moved, never described again (see\n * `rebuildSemanticsSubtree`). What is described is the path down to\n * each change, whatever is new, and everything under a node whose\n * own meaning changed.\n *\n * The result is spliced into `this.semantics` in place, since the map\n * is in document order and a record's subtree is a contiguous run of\n * it, so adds still arrive in document order. Removals come first.\n */\n private restructureSemantics(frame: UiFrame): readonly UiSemanticsPatch[] | null {\n if (this.semantics.size === 0 || this.semanticsStale) {\n return null;\n }\n const dirty: UiNode[] = [];\n /** Nodes whose own meaning may have changed: everything under them is described afresh. */\n const meaning = new Set<UiNode>();\n let structural = false;\n for (const [node, flags] of frame.entries()) {\n const children = (flags & DirtyFlags.Children) !== 0;\n const semantics = (flags & DirtyFlags.Semantics) !== 0;\n if (!children && !semantics) {\n continue;\n }\n if (!this.attached(node)) {\n // Removed this frame; its parent carries the change.\n continue;\n }\n structural ||= children;\n dirty.push(node);\n if (semantics) {\n meaning.add(node);\n }\n }\n if (!structural) {\n return null;\n }\n\n const owners = new Set<UiNode>();\n for (const node of dirty) {\n const owner = this.semanticsOwnerOf(node);\n if (owner === null) {\n return null;\n }\n owners.add(owner);\n }\n // One walk covers an owner inside another. Deleting the entry being\n // visited is safe mid-iteration; a Set only skips what is not yet reached.\n for (const owner of owners) {\n for (let above = owner.parent; above !== null; above = above.parent) {\n if (owners.has(above)) {\n owners.delete(owner);\n break;\n }\n }\n }\n if (owners.size > SCOPED_SEMANTICS_OWNERS) {\n return null;\n }\n\n // The path from each change up to its owner: the nodes that may now\n // hold different children, so none of them is taken back as it was.\n const path = new Set<UiNode>();\n for (const node of dirty) {\n for (let current: UiNode | null = node; current !== null && !path.has(current); current = current.parent) {\n path.add(current);\n if (owners.has(current)) {\n break;\n }\n }\n }\n const fresh = new Map<UiNode, boolean>();\n const underChangedMeaning = (node: UiNode): boolean => {\n const known = fresh.get(node);\n if (known !== undefined) {\n return known;\n }\n const answer =\n meaning.has(node) || (!owners.has(node) && node.parent !== null && underChangedMeaning(node.parent));\n fresh.set(node, answer);\n return answer;\n };\n const unchanged = (node: UiNode): boolean => !path.has(node) && (meaning.size === 0 || !underChangedMeaning(node));\n\n // Walked completely before anything is committed, so a give-up part\n // way through leaves the tree as it was.\n const positions = new Map<string | null, Map<string, number>>();\n const reuse = {\n previous: this.semantics,\n childrenOf: (parent: string | null): readonly string[] => this.semanticsChildren.get(parent) ?? [],\n positionOf: (parent: string | null, id: string): number => {\n let index = positions.get(parent);\n if (index === undefined) {\n index = new Map((this.semanticsChildren.get(parent) ?? []).map((child, i) => [child, i]));\n positions.set(parent, index);\n }\n return index.get(id) ?? -1;\n },\n memory: this.semanticsMemory,\n unchanged\n };\n const walks: SemanticsRewalk[] = [];\n for (const owner of owners) {\n const before = this.semantics.get(owner.id);\n if (before === undefined) {\n return null;\n }\n const walked = rewalkSemantics(\n owner,\n before.parent,\n this.semanticsIndexOf(before),\n semanticsInertAbove(owner),\n reuse\n );\n if (walked === null) {\n return null;\n }\n walks.push(walked);\n }\n\n // What left: the children a walked record had and no longer has,\n // anywhere in the walks, with everything under them.\n const present = new Set<string>();\n for (const walked of walks) {\n for (const list of walked.children.values()) {\n for (const id of list) {\n present.add(id);\n }\n }\n }\n const removals: UiSemanticsPatch[] = [];\n const remove = (id: string): void => {\n removals.push({ op: 'remove', id });\n for (const child of this.semanticsChildren.get(id) ?? []) {\n remove(child);\n }\n this.semanticsChildren.delete(id);\n this.semantics.delete(id);\n };\n const formerly = new Map<string | null, readonly string[]>();\n for (const walked of walks) {\n for (const parent of walked.children.keys()) {\n formerly.set(parent, this.semanticsChildren.get(parent) ?? []);\n }\n }\n for (const list of formerly.values()) {\n for (const id of list) {\n if (!present.has(id) && this.semantics.has(id)) {\n remove(id);\n }\n }\n }\n\n // Parents whose remaining children changed order among themselves:\n // there, every child's new index is sent. Anywhere else a removal\n // or an addition leaves the others where a mirror already has them.\n const reordered = new Set<string | null>();\n for (const walked of walks) {\n for (const [parent, list] of walked.children) {\n const old = formerly.get(parent) ?? [];\n if (old.length === 0) {\n continue;\n }\n const was = new Map(old.map((id, i) => [id, i]));\n let last = -1;\n for (const id of list) {\n const at = was.get(id);\n if (at === undefined) {\n continue;\n }\n if (at < last) {\n reordered.add(parent);\n break;\n }\n last = at;\n }\n }\n }\n\n const changes: UiSemanticsPatch[] = [];\n for (const walked of walks) {\n for (const { id, parent, index } of walked.placed) {\n const record = walked.records.get(id);\n const before = this.semantics.get(id);\n if (record !== undefined) {\n if (before === undefined) {\n changes.push({ op: 'add', node: record });\n } else if (\n before.parent !== record.parent ||\n reordered.has(parent) ||\n !recordsEqual({ ...before, index: record.index }, record)\n ) {\n changes.push({ op: 'update', node: record });\n }\n this.semantics.set(id, record);\n } else if (before === undefined) {\n // Taken back from a record removed by another walk this frame.\n return null;\n } else if (before.parent !== parent || reordered.has(parent)) {\n const moved = { ...before, parent, index };\n changes.push({ op: 'update', node: moved });\n this.semantics.set(id, moved);\n }\n }\n for (const [parent, list] of walked.children) {\n this.semanticsChildren.set(parent, list);\n }\n }\n this.semanticsOrdered = null;\n return [...removals, ...changes];\n }\n\n /** Whether a node is still in the tree being laid out. */\n private attached(node: UiNode): boolean {\n const root = this.layoutRoot();\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current === root) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * The nearest node at or above `node` that holds a semantics record,\n * or null when nothing above it does.\n */\n private semanticsOwnerOf(node: UiNode): UiNode | null {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (this.semantics.has(current.id)) {\n return current;\n }\n }\n return null;\n }\n\n /**\n * The ids in the current tree that belong to `node`'s subtree.\n *\n * Read off the graph rather than remembered, which is sound here\n * precisely because the caller has established that no node's\n * children changed this frame.\n *\n * A text run is not a node and has no id of its own: it borrows its\n * paragraph's and adds its position, numbered from zero without\n * gaps. So the runs of a node are found by counting up until one is\n * missing, rather than by scanning every key for the prefix.\n */\n private semanticIdsUnder(node: UiNode): string[] {\n const ids: string[] = [];\n const stack: UiNode[] = [node];\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (this.semantics.has(current.id)) {\n ids.push(current.id);\n for (let run = 0; ; run++) {\n const id = `${current.id}${TEXT_RUN_ID_SEPARATOR}${run}`;\n if (!this.semantics.has(id)) {\n break;\n }\n ids.push(id);\n }\n }\n for (let child = current.firstChild; child !== null; child = child.nextSibling) {\n stack.push(child);\n }\n }\n return ids;\n }\n\n /**\n * The boxes for a frame that moved something but changed no meaning:\n * all of them when it is the first move after a rest, and otherwise\n * only the focused node's, with the sweep owed for when the moving\n * stops (`SEMANTICS_SETTLE_MS`), or for when the boxes have been stale\n * too long (`SEMANTICS_MOST_STALE_MS`).\n *\n * A pan or a zoom moves every box under the camera on every frame. In\n * Atelier, a board of three dozen screens panned at 15%, the sweep was\n * 3 to 4 ms of a 4 to 6 ms frame, with frames to 13 ms, past a 120 Hz\n * display's 8.3; and the main thread wrote four styles on each of 545\n * elements per frame, which in Chromium was 45% of that thread while\n * it also had the wheel events to deliver. Nobody reads the rectangle\n * of an element that is moving under a pan: a screen reader's cursor\n * is on what it rests on. The focused node is the exception, since its\n * focus ring is drawn from its element, so it moves every frame.\n */\n private movedSemanticsBoxes(): readonly UiSemanticsBox[] {\n const at = now();\n const resting = at - this.semanticsMovedAt > SEMANTICS_SETTLE_MS;\n this.semanticsMovedAt = at;\n if (resting || at - this.semanticsSweptAt > SEMANTICS_MOST_STALE_MS) {\n return this.sweepSemanticsBoxes();\n }\n if (this.semanticsSweepTimer === null) {\n this.scheduleSemanticsSweep(SEMANTICS_SETTLE_MS);\n }\n return this.focusedSemanticsBox();\n }\n\n /** Sweeps the boxes once the moving has stopped, and sends what changed. */\n private scheduleSemanticsSweep(wait: number): void {\n this.semanticsSweepTimer = setTimeout(\n () => {\n this.semanticsSweepTimer = null;\n const listener = this.semanticsListener;\n if (listener === null) {\n return;\n }\n const left = this.semanticsMovedAt + SEMANTICS_SETTLE_MS - now();\n if (left > 0) {\n this.scheduleSemanticsSweep(left);\n return;\n }\n const boxes = this.sweepSemanticsBoxes();\n if (boxes.length > 0) {\n listener({ patches: EMPTY_PATCHES, boxes });\n }\n },\n Math.max(1, wait)\n );\n }\n\n private sweepSemanticsBoxes(): UiSemanticsBox[] {\n this.semanticsSweptAt = now();\n if (this.semanticsSweepTimer !== null) {\n clearTimeout(this.semanticsSweepTimer);\n this.semanticsSweepTimer = null;\n }\n return this.collectSemanticsBoxes();\n }\n\n /** The focused node's box when it differs from the one last sent, as `collectSemanticsBoxes` would give it. */\n private focusedSemanticsBox(): readonly UiSemanticsBox[] {\n const focused = this.focusManager.focusedNode;\n if (\n focused === null ||\n focused === undefined ||\n !this.semantics.has(focused.id) ||\n this.engine.recordFor(focused) === undefined\n ) {\n return EMPTY_BOXES;\n }\n const box = this.engine.screenBox(focused);\n const last = this.semanticsBoxes.get(focused.id);\n if (last !== undefined && boxesEqual(last, box)) {\n return EMPTY_BOXES;\n }\n this.semanticsBoxes.set(focused.id, box);\n return [{ id: focused.id, box }];\n }\n\n /**\n * The mirrored nodes whose box differs from the one last sent, and\n * which are on screen.\n *\n * This runs on every frame that laid out, and `frameNeedsLayout`\n * counts a transform — so it runs on every scrolled frame, where\n * every box beneath the scroll container has moved. Whatever it\n * returns crosses to the shell and is written as four inline styles\n * per element, on the main thread, before the next paint. That is\n * the one per-frame cost this architecture puts back on the thread\n * it exists to keep free, and it was proportional to the whole\n * mounted tree.\n *\n * So it is bounded by the viewport instead. A node scrolled out of\n * sight keeps the last box the mirror was told about; there is\n * nothing on screen for it to be wrong about, and the sweep picks it\n * up again on the frame it comes back, because the box it is\n * compared against is still the stale one. The focused node is\n * always included whether or not it is on screen: the focus ring is\n * drawn from its rectangle, and focus moving to something off screen\n * is answered by `scrollIntoView`, whose own frame reports it.\n *\n * Ids that have left the tree are dropped here rather than tracked,\n * since a removal patch has already told the mirror about them.\n *\n * **Found by walking what is on screen, not by checking everything.**\n * Bounding what was sent still computed every node's box to find out\n * whether it was on screen: a 5,000-line document has three thousand\n * fields, and the sweep cost 4 to 24 ms of every keystroke. The walk\n * goes down from the root carrying the scroll and sticky offsets that\n * `visibleBox` adds up per node, and passes over any subtree whose\n * bounds (the hit tester's, see `subtreeBoundsFor`) are off screen.\n * A node it passes over is one this skipped before anyway. Below a\n * `transform` the walk asks `screenBox` instead, which carries the\n * box through it.\n */\n private collectSemanticsBoxes(): UiSemanticsBox[] {\n const changed: UiSemanticsBox[] = [];\n const focused = this.focusManager.focusedNode;\n let focusedSeen = false;\n const offer = (id: string, box: LayoutBox): void => {\n const last = this.semanticsBoxes.get(id);\n if (last !== undefined && boxesEqual(last, box)) {\n return;\n }\n this.semanticsBoxes.set(id, box);\n changed.push({ id, box });\n };\n const root = this.root;\n if (root !== undefined) {\n // Fills the bounds for every record, if anything moved since.\n this.engine.subtreeBoundsFor(root);\n const stack: [UiNode, number, number, boolean][] = [[root, 0, 0, false]];\n while (stack.length > 0) {\n const [node, dx, dy, inherited] = stack.pop()!;\n const rec = this.engine.recordFor(node);\n let childX = dx;\n let childY = dy;\n const own = node.properties.get('transform');\n const transformed = inherited || (typeof own === 'object' && own !== null);\n if (rec !== undefined && inherited) {\n // Under a transform the offsets carried down are not the whole\n // story: a panned, zoomed map draws a card far from its record,\n // and the mirror put the card's element at the record. The\n // engine projects such a node through every transform above\n // it, at a walk up per node. A node's own transform is not one\n // of them (see `screenBox`), so a spinner's turning does not\n // move its element every step.\n //\n // Its subtree's bounds are projected the same way, and a\n // subtree none of which is on screen is passed over whole, as\n // it is outside a transform: a design tool's board of hundreds\n // of frames under one camera walked every node of every frame\n // on every step of a pan.\n const reach = node === focused ? null : this.engine.screenBounds(node);\n if (reach !== null && !this.onScreen(reach)) {\n continue;\n }\n if (this.semantics.has(node.id)) {\n const box = this.engine.screenBox(node);\n if (node === focused) {\n focusedSeen = true;\n offer(node.id, box);\n } else if (this.onScreen(box)) {\n offer(node.id, box);\n }\n }\n } else if (rec !== undefined) {\n const x = dx + rec.stickyOffsetX;\n const y = dy + rec.stickyOffsetY;\n if (\n !rec.boundsUnbounded &&\n node !== focused &&\n !this.onScreen({\n x: rec.boundsMinX + x,\n y: rec.boundsMinY + y,\n width: rec.boundsMaxX - rec.boundsMinX,\n height: rec.boundsMaxY - rec.boundsMinY\n })\n ) {\n // Left where it was, deliberately: `semanticsBoxes` is what\n // the mirror has been told, and leaving the two in step is\n // what makes the box arrive on the frame this node returns.\n continue;\n }\n if (this.semantics.has(node.id)) {\n const box = { x: rec.x + x, y: rec.y + y, width: rec.width, height: rec.height };\n if (node === focused) {\n focusedSeen = true;\n offer(node.id, box);\n } else if (this.onScreen(box)) {\n offer(node.id, box);\n }\n }\n childX = x - (rec.scrollable ? rec.scrollX : 0);\n childY = y - (rec.scrollable ? rec.scrollY : 0);\n }\n // A held subtree is out of view or too small to read, and stays\n // where the mirror was last told it was until it is let go: a\n // board's hundreds of frames are its node alone.\n if (node.hold !== null && !(focused != null && focused.heldBy === node)) {\n continue;\n }\n // Last child first, so the boxes come out in document order.\n for (let child = node.lastChild; child !== null; child = child.previousSibling) {\n stack.push([child, childX, childY, transformed]);\n }\n }\n }\n // The focused node always, on screen or not: the focus ring is drawn\n // from its rectangle.\n if (focused !== null && focused !== undefined && !focusedSeen && this.semantics.has(focused.id)) {\n if (this.engine.recordFor(focused) !== undefined) {\n // Through any transform above it, as the walk would have.\n offer(focused.id, this.engine.screenBox(focused));\n }\n }\n if (this.semanticsBoxes.size > this.semantics.size) {\n for (const id of this.semanticsBoxes.keys()) {\n if (!this.semantics.has(id)) {\n this.semanticsBoxes.delete(id);\n }\n }\n }\n return changed;\n }\n\n /**\n * Whether a box in viewport coordinates overlaps the canvas at all.\n *\n * Generous by a margin, so that something a scroll is about to bring\n * in has its rectangle before it arrives rather than one frame after.\n */\n private onScreen(box: LayoutBox): boolean {\n return (\n box.x < this.width + SEMANTICS_VIEWPORT_MARGIN &&\n box.y < this.height + SEMANTICS_VIEWPORT_MARGIN &&\n box.x + box.width > -SEMANTICS_VIEWPORT_MARGIN &&\n box.y + box.height > -SEMANTICS_VIEWPORT_MARGIN\n );\n }\n\n /** Scrolls every scroll container above `node` so a node-local box is visible. */\n private revealBox(node: UiNode, box: { x: number; y: number; width: number; height: number }): void {\n for (const adjustment of this.engine.revealAdjustments(node, 0, box)) {\n // On every keystroke, so it must land at once: a spring here\n // would leave the caret trailing the text being typed.\n this.smoothScroller.stop(adjustment.container);\n adjustment.container.setProperty('scrollX', adjustment.scrollX);\n adjustment.container.setProperty('scrollY', adjustment.scrollY);\n this.graph.markDirty(adjustment.container, DirtyFlags.Transform);\n }\n }\n\n /**\n * With the inspector on, a hover change repaints (the overlay follows\n * the pointer) and re-explains the hovered node for the listener.\n */\n private handleHoverChange(node: UiNode | null): void {\n this.sendCursor();\n this.sendScrollability();\n if (!this.inspector.isEnabled || !this.inspector.setHovered(node)) {\n return;\n }\n this.sendInspection();\n if (this.root !== undefined) {\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n }\n\n /**\n * Scrolling backed directly by layout records and node properties.\n *\n * The layout engine clamps scrollX/scrollY against content size on\n * every pass, so writing the raw offset here is enough.\n */\n private createScrollSink(): ScrollSink {\n // Where a container is going, not where the last layout left it: a\n // scroll written since that layout is in the property, and several\n // wheel steps arrive between two frames whenever a device sends\n // faster than the display draws. Each was added to the record's\n // offset, so each overwrote the last and most of a flick was lost.\n // Clamped, so an overshoot past either end isn't banked.\n const going = (node: UiNode, record: LayoutRecord, axis: 'x' | 'y'): number => {\n const written = node.getProperty(axis === 'x' ? 'scrollX' : 'scrollY');\n const offset = typeof written === 'number' ? written : axis === 'x' ? record.scrollX : record.scrollY;\n const { min, max } = scrollBounds(record, axis);\n return Math.min(Math.max(offset, min), max);\n };\n // What the controllers are told is measured from the low end of the\n // bounds, so every container reads to them as [0, range] and their\n // arithmetic (room left, a paced target, a thumb drag) is the same\n // in both directions. They only ever write deltas back, which mean\n // the same in either frame. A right-to-left container's real offset\n // runs from minus its overflow up to 0; see `scrollBounds`.\n const fromLow = (node: UiNode, record: LayoutRecord, axis: 'x' | 'y'): number =>\n going(node, record, axis) - scrollBounds(record, axis).min;\n return {\n containerState: (node): ScrollContainerState | undefined => {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n return undefined;\n }\n return {\n scrollX: fromLow(node, record, 'x'),\n scrollY: fromLow(node, record, 'y'),\n maxScrollX: scrollRange(record, 'x'),\n maxScrollY: scrollRange(record, 'y'),\n horizontal: node.getProperty('direction') === 'row' || node.type === UiNodeType.Row,\n viewportWidth: record.width,\n viewportHeight: record.height\n };\n },\n scrollBy: (node, dx, dy, behavior): void => {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n return;\n }\n if (behavior === 'smooth') {\n // The smooth path adds to where the container is *going*, so\n // it needs the effective offset only as a starting point.\n if (dx !== 0) {\n this.smoothScroller.scrollBy(node, 'scrollX', dx, going(node, record, 'x'));\n }\n if (dy !== 0) {\n this.smoothScroller.scrollBy(node, 'scrollY', dy, going(node, record, 'y'));\n }\n return;\n }\n // An instant scroll wins over one in flight rather than racing\n // it: a thumb drag reads the offset back on every pointer move\n // and would chase a moving value.\n this.smoothScroller.stop(node);\n if (dx !== 0) {\n node.setProperty('scrollX', going(node, record, 'x') + dx);\n }\n if (dy !== 0) {\n node.setProperty('scrollY', going(node, record, 'y') + dy);\n }\n this.graph.markDirty(node, DirtyFlags.Transform);\n },\n scrollContainers: () => this.engine.scrollContainers(),\n revealScrollbars: (node): void => {\n this.engine.revealScrollbars(node);\n this.graph.markDirty(node, DirtyFlags.Paint);\n },\n scrollbar: (node, axis) => {\n const record = this.engine.recordFor(node);\n return record === undefined ? null : scrollbarThumb(record, axis);\n }\n };\n }\n\n /** Where the container the wheel last scrolled is; see `FrameMetrics.wheelScroll`. */\n private wheelScroll(): { readonly x: number; readonly y: number } | null {\n const node = this.input.wheel.lastScrolled;\n const record = node === null ? undefined : this.engine.recordFor(node);\n return record === undefined ? null : { x: record.scrollX, y: record.scrollY };\n }\n\n /**\n * Work that must happen before the frame's dirty set is snapshotted.\n *\n * Environment propagation belongs here and nowhere else: rebuilding a\n * node's environment marks its descendants dirty, and those nodes\n * have to be in the frame that is about to be collected. Run after\n * collection it saw an already-drained set and silently did nothing,\n * so a theme change never reached descendants at all.\n */\n /**\n * The phases that run before the frame's dirty set is snapshotted.\n *\n * Both produce dirt of their own — applying a patch updates bound\n * properties, rebuilding an environment marks descendants — and those\n * nodes have to belong to the frame about to be collected. Run after\n * collection, the environment phase saw an already-drained set and\n * silently did nothing, so a theme change never reached descendants.\n */\n private runPreCollectPhases(time: number): void {\n this.frameBegan = now();\n this.phaseTimings = emptyPhaseTimings();\n this.frameTime = time;\n // The wheel's steps since the last frame, as one (see `wheel`), before\n // anything reads what they change: a smooth scroll they start ticks below.\n this.releaseWheel();\n\n // Animation first: every phase after this one reads values a tick\n // may have just written. `hasWork` is a set's size, so an app with\n // nothing running reports 0 without so much as reading the clock —\n // and, because `scheduleAnimationTick` arms nothing when the\n // driver is empty, an idle app runs no frames for this to be\n // reported on at all.\n this.phaseTimings.ticks = this.timePhase(\n () => this.visible && this.animations.isRunning,\n () => this.animations.advance(time)\n );\n // Asked after the tick, not before: an animation that finished\n // just now must not arm a frame nothing will use, and one that\n // started during it must.\n this.scheduleAnimationTick(time);\n\n this.phaseTimings.patches = this.timePhase(\n () => this.replicas.some(replica => replica.hasPendingPatches),\n () => {\n for (const replica of this.replicas) {\n replica.flush();\n }\n }\n );\n\n this.phaseTimings.environment = this.timePhase(\n () => this.graph.hasEnvironmentDirty(),\n () => this.graph.processEnvironmentDirty()\n );\n\n // A trackpad's steps, paced over frames (see `UiWheelController.advance`).\n // Before the lazy lists read the offset, so the rows this frame's step\n // reveals are built on it.\n if (this.input.wheel.advance(time)) {\n this.scheduler.wake();\n }\n\n // Lazy lists decide which rows to mount from the scroll offset the\n // frame is about to lay out with, so rows a scroll reveals are built,\n // measured and painted on that same frame.\n this.phaseTimings.virtualize = this.timePhase(\n () => this.hasVirtualWindows(),\n () => this.updateVirtualWindows()\n );\n }\n\n private hasVirtualWindows(): boolean {\n for (const node of this.engine.scrollContainers()) {\n if (node.properties.get(VIRTUAL_WINDOW_PROP) instanceof UiVirtualWindow) {\n return true;\n }\n if (node.properties.get(VIRTUAL_SHEET_PROP) instanceof UiVirtualSheet) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Advances every lazy list's window: reports the container's scroll\n * offset and viewport, and the measured extent of each mounted item,\n * then applies any scroll adjustment the window asks for to keep its\n * first item anchored while estimates above it are corrected.\n */\n private updateVirtualWindows(): void {\n for (const node of this.engine.scrollContainers()) {\n this.updateVirtualSheet(node);\n const window = node.properties.get(VIRTUAL_WINDOW_PROP);\n if (!(window instanceof UiVirtualWindow)) {\n continue;\n }\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n continue;\n }\n const column = window.axis === 'column';\n // A wheel may have written a newer offset than the record holds.\n const scrollProp = node.properties.get(column ? 'scrollY' : 'scrollX');\n const offset = typeof scrollProp === 'number' ? scrollProp : column ? rec.scrollY : rec.scrollX;\n // The window counts from the list's start. A row that reads right\n // to left starts at its right edge and scrolls there with a\n // negative offset, so the distance from its start is the offset\n // negated, and so is any correction the window hands back.\n const sign = !column && scrollsFromRight(rec) ? -1 : 1;\n const scroll = sign * offset;\n const measures: VirtualItemMeasure[] = [];\n const lead = this.collectVirtualMeasures(node, column, measures);\n const result = window.update({ scroll, extent: column ? rec.height : rec.width, lead }, measures);\n if (result.scrollAdjust !== 0) {\n const axis = column ? 'scrollY' : 'scrollX';\n const adjust = sign * result.scrollAdjust;\n node.setProperty(axis, offset + adjust);\n this.graph.markDirty(node, DirtyFlags.Transform);\n // This is a coordinate correction, not a scroll: the content\n // above moved, so the viewport moves with it to keep the same\n // row under the eye. A running scroll therefore has to have\n // *both* ends shifted — the write above would otherwise be\n // overwritten by the animation's next tick and the list would\n // slip by this much every frame, which is the jitter anchoring\n // exists to prevent.\n this.smoothScroller.adjust(node, axis, adjust);\n }\n // A row scrolled to before it was measured is put right once it\n // has been, which takes the frame after this one: wake for it,\n // since nothing else may.\n if (window.holding) {\n this.scheduler.wake();\n }\n }\n }\n\n /**\n * Advances a two-axis window, which needs both scroll offsets and\n * neither a measurement nor an anchor.\n *\n * A `LazySheet` is told its row height and column width, so the\n * offset of every cell is exact and the window is arithmetic. There\n * is nothing here to correct and so nothing to adjust the scroll by:\n * the rows and columns a scroll reveals are placed correctly on the\n * frame that reveals them.\n */\n private updateVirtualSheet(node: UiNode): void {\n const sheet = node.properties.get(VIRTUAL_SHEET_PROP);\n if (!(sheet instanceof UiVirtualSheet)) {\n return;\n }\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n return;\n }\n // A wheel may have written newer offsets than the record holds.\n const x = node.properties.get('scrollX');\n const y = node.properties.get('scrollY');\n // Counted from the sheet's start, as a lazy row's window is: a\n // sheet that reads right to left starts at its right edge and\n // scrolls there with a negative offset.\n const sign = scrollsFromRight(rec) ? -1 : 1;\n sheet.update({\n scrollX: sign * (typeof x === 'number' ? x : rec.scrollX),\n scrollY: typeof y === 'number' ? y : rec.scrollY,\n width: rec.width,\n height: rec.height,\n // So a point it is asked about is read from the right edge too.\n fromRight: sign < 0\n });\n }\n\n /**\n * Measures the mounted items, and returns the extent of the content\n * above the first of them — a lazy grid's header.\n *\n * A lazy grid puts its rows inside one Grid, so that the header and\n * every row share its tracks; the rows are that grid's children\n * rather than the scroll container's, which is why the walk goes\n * through a Grid as it goes through a Fragment.\n */\n private collectVirtualMeasures(parent: UiNode, column: boolean, out: VirtualItemMeasure[]): number {\n let lead = 0;\n for (let child = parent.firstChild; child !== null; child = child.nextSibling) {\n const index = child.properties.get(VIRTUAL_INDEX_PROP);\n if (typeof index !== 'number') {\n if (child.type === UiNodeType.Fragment || child.type === UiNodeType.Grid) {\n lead += this.collectVirtualMeasures(child, column, out);\n continue;\n }\n if (child.properties.get(VIRTUAL_LEAD_PROP) === true) {\n lead += this.extentOf(child, column);\n }\n continue;\n }\n const rec = this.engine.recordFor(child);\n if (rec === undefined) {\n continue;\n }\n out.push({ index, extent: this.extentOf(child, column) });\n }\n return lead;\n }\n\n /** A node's outer extent along the list's axis. */\n private extentOf(node: UiNode, column: boolean): number {\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n return 0;\n }\n return column\n ? rec.measuredHeight + rec.marginTop + rec.marginBottom\n : rec.measuredWidth + rec.marginLeft + rec.marginRight;\n }\n\n /**\n * Tells the layout listeners what moved, and lays out again for as\n * long as what they do in answer needs it, so the frame paints the\n * layout they settle on rather than the one they were told about.\n *\n * The browser's answer to the same problem. A `ResizeObserver`\n * callback runs after layout and before paint, and when it changes\n * layout the browser lays out again before it paints rather than\n * showing the stale boxes for a frame. The listeners here are\n * `breakpoint`, `sizeContainer` (and so `Responsive`),\n * `scrollPosition`, `autoFocus` and anything else on `host.onLayout`.\n * Before this a page whose breakpoint gave it wide padding was\n * painted with its narrow padding on the frame it appeared, and\n * jumped 16 px on the next.\n *\n * A pass after the first lays out only what the listeners dirtied,\n * the same incremental pass any frame runs, and then tells only the\n * listeners whose boxes it changed. A frame whose listeners wrote\n * nothing that lays out, which is almost every frame, runs no second\n * pass: it pays one look at the dirty set.\n *\n * **Bounded.** Listeners that keep moving each other's boxes would\n * otherwise never let the frame paint. ResizeObserver bounds its loop\n * by tree depth; this one is bounded by count, `MAX_LAYOUT_PASSES` in\n * all, which is easier to reason about and more than a real chain\n * needs (a breakpoint inside a `Responsive` inside a `Responsive` is\n * four passes). Past it the frame paints what it has, the rest waits\n * for the next frame as it always used to, and a warning says so\n * once, as the browser's \"ResizeObserver loop completed with\n * undelivered notifications\" does.\n *\n * **Reveals wait for the end.** `autoFocus` takes focus from a layout\n * listener, on its node's first layout, and focus reveals the node:\n * revealed from the first pass's boxes, it scrolled to where the node\n * was before a breakpoint moved it. A reveal asked for while the\n * listeners run is applied once they are quiet, from final boxes, and\n * its scroll laid out like any other write. So is the focus a hot\n * reload restores, after the listeners rather than before because\n * `autoFocus` is one of them and the restore has to be the last word:\n * every node in a subtree a reload replaced has its first layout on\n * this frame.\n */\n private settleLayout(frame: UiFrame, root: UiNode, laidOut: boolean): SettledLayout {\n const stats = this.engine.stats;\n const settled: SettledLayout = {\n frame,\n passes: laidOut ? 1 : 0,\n measured: laidOut ? stats.measured : 0,\n relayoutRoots: laidOut && !stats.fullLayout ? stats.relayoutRoots : 0\n };\n let fromRoot = laidOut && stats.fullLayout;\n const read = (node: UiNode) => {\n const record = this.engine.recordFor(node);\n return {\n // The box `layoutBox` answers, so an `onLayout` listener is\n // told when a transform above its node moves it on screen, as\n // a scroll already does.\n box: this.engine.screenBox(node),\n scrollX: record?.scrollX ?? 0,\n scrollY: record?.scrollY ?? 0\n };\n };\n // The node's place in the layout, read off its record: what a pan\n // or a scroll above it leaves alone (see `LayoutNotifier.addFlow`).\n const readFlow = (node: UiNode) => {\n const record = this.engine.recordFor(node);\n return {\n box: record === undefined ? NO_BOX : this.engine.worldBox(node),\n scrollX: record?.scrollX ?? 0,\n scrollY: record?.scrollY ?? 0\n };\n };\n this.deferredReveals = [];\n try {\n for (;;) {\n // Nothing listening means nothing walked.\n if (!this.layoutNotifier.isEmpty()) {\n this.layoutNotifier.notify(read, readFlow);\n }\n if (!this.layoutPending()) {\n this.finishSettling(settled.passes > 0);\n if (!this.layoutPending()) {\n break;\n }\n }\n if (settled.passes >= MAX_LAYOUT_PASSES) {\n this.warnLayoutPasses();\n this.finishSettling(true);\n break;\n }\n // A listener's write can be inherited (a theme, a font size), so\n // what it changes for the subtree is worked out first, as the\n // frame does before its own layout.\n this.phaseTimings.environment += this.timePhase(\n () => this.graph.hasEnvironmentDirty(),\n () => this.graph.processEnvironmentDirty()\n );\n const more = this.scheduler.recollect(frame);\n settled.frame = settled.frame.merged(more);\n this.phaseTimings.layout += this.timePhase(\n () => true,\n () => this.engine.layoutForFrame(more, this.constraints, root)\n );\n settled.passes++;\n settled.measured += stats.measured;\n settled.relayoutRoots += stats.fullLayout ? 0 : stats.relayoutRoots;\n fromRoot ||= stats.fullLayout;\n }\n } finally {\n this.deferredReveals = null;\n }\n // Zero means the frame ran from the root, as one pass reports it.\n if (fromRoot) {\n settled.relayoutRoots = 0;\n }\n return settled;\n }\n\n /**\n * Whether something written since the frame was collected has to be\n * laid out before the frame paints: the same flags that make a frame\n * lay out, or an inherited value still to be handed down.\n */\n private layoutPending(): boolean {\n return this.graph.hasEnvironmentDirty() || this.graph.getDirtyNodes().anyFlags(LAYOUT_DIRT);\n }\n\n /** Applies what waits for settled boxes: a reload's focus, then the reveals asked for. */\n private finishSettling(laidOut: boolean): void {\n const reveals = this.deferredReveals ?? [];\n this.deferredReveals = null;\n if (this.restoringFocus && laidOut) {\n this.restoringFocus = false;\n this.restoreFocusAfterReload();\n }\n for (const { node, padding } of reveals) {\n // A node taken out by the listeners that asked has nothing to show.\n if (this.engine.recordFor(node) !== undefined) {\n this.scrollIntoView(node, padding);\n }\n }\n this.deferredReveals = [];\n }\n\n private warnLayoutPasses(): void {\n if (this.warnedLayoutPasses) {\n return;\n }\n this.warnedLayoutPasses = true;\n console.warn(\n `Gesso: a frame laid out ${MAX_LAYOUT_PASSES} times and its layout listeners were still changing it, so it ` +\n `was painted unsettled and the rest left for the next frame. Something on \\`host.onLayout\\` (a ` +\n `breakpoint, a sizeContainer, a Responsive, a modifier of your own) is writing a property that changes ` +\n `the box it is listening to, back and forth. Shown once.`\n );\n }\n\n /**\n * Holds each subtree that declares `holdUnseen` while it cannot be\n * read, and lets it go on the frame it can (see `UiGraph.hold`).\n *\n * Decided after the frame is drawn, from where it was drawn: a subtree\n * is only ever held once it has been laid out and painted, so what is\n * kept of it is what it showed, and one let go catches up on the next\n * frame, which its own marks arm. Only frames that moved or laid\n * something out can change the answer, so the rest pay nothing.\n */\n private updateHolds(frame: UiFrame, laidOut: boolean): void {\n let found = false;\n // `holdUnseen` is written with `Properties`, so only a node marked so can have just declared it.\n for (const [node, flags] of frame.entries()) {\n if (\n (flags & DirtyFlags.Properties) !== 0 &&\n node.properties.get('holdUnseen') !== undefined &&\n !this.holdable.has(node)\n ) {\n this.holdable.add(node);\n found = true;\n }\n }\n if (this.holdable.size === 0 || (!laidOut && !found)) {\n return;\n }\n for (const node of this.holdable) {\n const how = node.properties.get('holdUnseen') as UiHoldUnseen | undefined;\n if (how === undefined || this.graph.getNode(node.id) !== node) {\n this.graph.release(node);\n this.holdable.delete(node);\n continue;\n }\n const rec = this.engine.recordFor(node);\n if (rec === undefined || !(rec.width > 0) || !(rec.height > 0)) {\n continue;\n }\n const box = this.engine.screenBox(node);\n const margin = how === true ? 0 : (how.margin ?? 0);\n const below = how === true ? 0 : (how.belowScale ?? 0);\n const seen =\n box.x + box.width > -margin &&\n box.y + box.height > -margin &&\n box.x < this.width + margin &&\n box.y < this.height + margin &&\n box.width / rec.width >= below;\n if (seen) {\n this.graph.release(node);\n } else {\n this.graph.hold(node);\n }\n }\n }\n\n private handleFrame(collected: UiFrame): void {\n const root = this.root;\n if (root === undefined) {\n return;\n }\n const started = now();\n\n // The tree for this frame exists now. A focus trap taken from a\n // `ref` — which fires before the node has children — enters its\n // subtree here, so a dialog opened this frame gets the caret in\n // it before the frame is laid out and revealed.\n this.focusManager.settleScope();\n\n const firstPass = frameNeedsLayout(collected);\n this.phaseTimings.layout = this.timePhase(\n () => firstPass,\n () => this.engine.layoutForFrame(collected, this.constraints, root)\n );\n // A modifier that follows its node's box hears about it here, after\n // the boxes are final and before anything paints from them; and\n // when what it does in answer moves boxes, they are laid out again\n // before anything paints. From here on `frame` is everything the\n // frame changed, the listeners' writes included.\n const settled = this.settleLayout(collected, root, firstPass);\n const frame = settled.frame;\n const laidOut = settled.passes > 0;\n if (laidOut) {\n this.laidOutOnce = true;\n this.inspector.recordLayout(started);\n }\n\n // Before the semantics phase, not after the frame: the mirror\n // decides whether to move DOM focus from what the editing proxy\n // reports, and a field that gains focus this frame must have been\n // reported by the time it does. Layout has run, so the caret box\n // it carries is this frame's.\n this.sendEditingState();\n\n // What the tree *means* changes far less often than where it sits,\n // so the tree is rebuilt only when a semantics property or the\n // shape of the tree moved. With an accessibility mirror attached\n // the phase also sweeps the mirrored boxes on any frame that laid\n // out, because an off-screen element that is not over its node\n // gives a screen reader's cursor the wrong rectangle — so this\n // reads 0.00 on an app with no mirror, and on a mirrored app only\n // on a frame that neither moved nor re-meant anything.\n const rebuildSemantics = frameNeedsSemantics(frame);\n this.phaseTimings.semantics = this.timePhase(\n () =>\n rebuildSemantics ||\n (this.semanticsListener !== null && (laidOut || this.focusManager.focusedNode?.id !== this.lastFocusedId)),\n () => this.updateSemantics(frame, rebuildSemantics, laidOut)\n );\n\n // Render is unconditional once the backend is ready: both backends\n // redraw the whole scene, so any frame that got this far changes\n // pixels. Before WebGPU has a device there is nothing to draw with;\n // a lost device is replaced, and the frame after it repaints.\n if (this.renderer.backend === 'webgpu' && (this.renderer as WebGPURenderer).isLost) {\n this.recoverWebGPU(this.renderer as WebGPURenderer);\n }\n // The inspector's overlay rides along with the frame, so either\n // backend draws it over the finished scene. The hovered node's\n // explanation only changes with layout, so it is re-read then and\n // sent when it differs from what the listener already has.\n const overlay = this.inspector.hasOverlay ? this.inspector.overlay(started) : null;\n this.gpuTimings = null;\n // What changed, for a renderer that keeps pixels across frames\n // (Canvas2D's scroll layers). A node dirtied since the frame was\n // collected (by a layout listener, a focus settling, a scroll\n // listener writing a cell) may already show in what is drawn and is\n // not in \\`frame\\`, so it is added to what the renderer is told, with\n // its flags as they stand: drawn again now, and again next frame,\n // when it is collected. Leaving the frame unvouched instead dropped\n // every kept layer on every frame of an editor's scroll, whose\n // scroll listener writes on each one.\n const changes: RenderChanges = {\n frame: withPending(frame, this.graph.getDirtyNodes()),\n geometryVersion: this.engine.geometryVersion\n };\n this.phaseTimings.render = this.timePhase(\n () => this.renderer.isReady,\n () =>\n this.renderer.render(root, {\n layout: this.engine,\n text: this.textMeasurer,\n now: started,\n overlay: overlay?.shapes,\n changes\n })\n );\n this.updateHolds(frame, laidOut);\n if (overlay !== null) {\n if (laidOut) {\n this.sendInspection();\n }\n this.scheduleInspectorRepaint(overlay.nextChange);\n }\n this.sendDevtoolsUpdates(frame);\n\n this.framesFailed = 0;\n const finished = now();\n // From the frame's first phase, before collection: the ticks, the\n // patches, the trackpad's pacing and the lazy lists' mounting are the\n // frame's too. Measured from collection, a frame that mounted rows for\n // four milliseconds reported two.\n const elapsed = finished - (this.frameBegan ?? started);\n this.frameBegan = null;\n this.lastFrameMs = elapsed;\n this.scheduleScrollbarFade(finished);\n this.scheduleRendererRepaint(finished);\n this.scheduleCaretBlink(finished);\n // A frame can change the cursor without the pointer moving: the\n // hovered node's `cursor` prop, or the node itself, may have changed.\n this.sendCursor();\n // The same is true of the scroll chain — a container that reached\n // its end, or content that grew under a still cursor.\n this.sendScrollability();\n const metrics: FrameMetrics = {\n frame: frame.id,\n durationMs: elapsed,\n nodes: frame.size,\n // The engine's counters are reset when a layout pass starts, so\n // they are added up pass by pass (see `settleLayout`), and a frame\n // that skipped layout reports zeroes rather than the last pass's\n // numbers as its own: a caret blink claimed thousands of nodes.\n measured: settled.measured,\n relayoutRoots: settled.relayoutRoots,\n layoutPasses: settled.passes,\n at: finished,\n inputLatencyMs: this.inputLatency.take(epochAt(finished)),\n time: this.frameTime,\n wheelScroll: this.wheelScroll(),\n phases: this.phaseTimings,\n renderer: this.rendererState,\n gpu: this.gpuTimings\n };\n this.frameListener?.(metrics);\n if (this.watchingFrames) {\n this.devtoolsListener?.({ kind: 'frame', metrics });\n }\n // And to any component on this thread that asked to hear frames.\n this.services.get(FrameService).publish(metrics);\n }\n\n /**\n * Keeps frames coming while something is animating.\n *\n * This is the one problem in F4 with no precedent to copy. The\n * scheduler arms a frame only when a node is marked dirty, and an\n * animation's dirt is made *inside* `beforeCollect` — so by the time\n * a frame ends the set is empty again and nothing would arm the\n * next. Every other \"frames nothing asks for\" in this class (the\n * caret blink, the scrollbar fade, the heatmap) answers that with a\n * timer, and so does this; what is new is only that the driver is\n * asked how long to wait.\n *\n * Three answers, and each one matters:\n *\n * - **undefined** — nothing is running, so nothing is armed. An idle\n * app schedules no frames at all, which is the strong reading of\n * §F4's \"`ticks 0.00` when idle\": not frames that do nothing, but\n * no frames.\n * - **now or earlier** — something wants every frame, so the next\n * one is armed directly on the scheduler. Under\n * `requestAnimationFrame` that is the display's cadence; a timer\n * clock gets its own interval, which is what a render worker has.\n * - **later** — nobody wants a frame until then, so one timer waits.\n * This is what keeps the media tier's promise about the\n * `Spinner`: eight positions means eight wake-ups a second, not\n * sixty frames drawing seven identical pictures.\n */\n private scheduleAnimationTick(now: number): void {\n if (!this.visible) {\n // A hidden document arms nothing on its own account. This is the\n // whole of \"do not draw for nobody\", and it is deliberately\n // narrower than stopping the scheduler: a change that genuinely\n // happened — an image finishing its decode, a patch arriving from\n // the application thread — still marks a node dirty and still\n // gets a frame, so the canvas holds a correct picture rather than\n // whatever was on it when the tab went away.\n //\n // Stopping outright was tried first and was wrong in a way worth\n // recording: the first frame is not the first *useful* paint.\n // A route loaded hidden drew once, before its images had decoded,\n // and then stopped — so it held a picture with every photograph\n // missing until something woke it.\n return;\n }\n const next = this.animations.nextTickAt(now);\n if (next === undefined) {\n return;\n }\n if (next <= now) {\n this.scheduler.wake();\n return;\n }\n if (this.animationTimer !== null) {\n return;\n }\n this.animationTimer = setTimeout(\n () => {\n this.animationTimer = null;\n this.scheduler.wake();\n },\n Math.max(1, next - now)\n );\n }\n\n /**\n * Overlay scrollbars fade after scrolling stops, which needs frames no\n * property change asks for. The engine says when the next change is\n * due; one pending timer marks a repaint for it.\n */\n /**\n * The renderer wants a frame though nothing changes: Canvas2D's kept\n * subtrees are drawn sharp once a zoom settles. One pending timer\n * marks the root for paint then, which leaves every kept thing as it\n * is; a frame before it that is still not settled asks again.\n */\n private scheduleRendererRepaint(now: number): void {\n const next = this.renderer.nextRepaint?.();\n if (next === undefined || this.rendererTimer !== null) {\n return;\n }\n this.rendererTimer = setTimeout(\n () => {\n this.rendererTimer = null;\n if (this.root !== undefined) {\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n },\n Math.max(16, next - now)\n );\n }\n\n private scheduleScrollbarFade(now: number): void {\n const next = this.engine.nextScrollbarChange(now);\n if (next === undefined || this.scrollbarTimer !== null) {\n return;\n }\n this.scrollbarTimer = setTimeout(\n () => {\n this.scrollbarTimer = null;\n if (this.root !== undefined) {\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n },\n Math.max(16, next - now)\n );\n }\n\n /**\n * The caret blinks, which needs frames no property change asks for.\n * The editing controller says when it next toggles; one pending timer\n * repaints the focused editable then. Nothing is scheduled while no\n * editable has focus, a composition holds the caret steady, or the\n * page is hidden.\n */\n private scheduleCaretBlink(now: number): void {\n const next = this.input.editing.nextCaretChange(now);\n if (next === undefined || this.caretTimer !== null) {\n return;\n }\n this.caretTimer = setTimeout(\n () => {\n this.caretTimer = null;\n const focused = this.input.editing.focused;\n if (focused !== null) {\n this.graph.markDirty(focused, DirtyFlags.Paint);\n }\n },\n Math.max(16, next - now)\n );\n }\n\n /** Hands the listener the focused editable's state, when it changed. */\n private sendEditingState(): void {\n const state = this.input.editing.state();\n if (editingStatesEqual(state, this.lastEditingState)) {\n return;\n }\n this.lastEditingState = state;\n this.editingListener?.(state);\n }\n\n /**\n * The heatmap cools in steps, which needs frames nothing else asks\n * for; one pending timer marks a repaint for the next step due.\n */\n private scheduleInspectorRepaint(nextChange: number | undefined): void {\n if (nextChange === undefined || this.inspectorTimer !== null) {\n return;\n }\n this.inspectorTimer = setTimeout(\n () => {\n this.inspectorTimer = null;\n if (this.root !== undefined && this.inspector.isEnabled) {\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n },\n Math.max(16, nextChange)\n );\n }\n\n /** Hands the listener the hovered node's cursor, when it changed: the arrow over a scrollbar, as a native one shows. */\n private sendCursor(): void {\n const cursor = this.input.pointer.overScrollbar ? null : resolveCursor(this.input.pointer.hoveredNode);\n if (cursor === this.lastCursor) {\n return;\n }\n this.lastCursor = cursor;\n this.cursorListener?.(cursor);\n }\n\n /**\n * Hands the listener the pointer's scroll chain, when it changed.\n *\n * Sent from the same two places as the cursor and for the same\n * reason: a hover change moves the chain, and a frame can change it\n * without the pointer moving — a list that reached its end, or\n * content that grew under a still cursor.\n */\n private sendScrollability(): void {\n // Hover first, and the last wheel's own target when nothing is\n // hovered. Scrolling a page slides a canvas under a cursor that\n // never moved, so on that path a wheel is the only evidence the\n // runtime gets that the pointer is over it at all.\n const target = this.input.pointer.hoveredNode ?? this.input.wheel.lastWheelTarget;\n const next = this.input.wheel.scrollabilityOf(target);\n const anything = this.input.wheel.scrollsAnything();\n const last = this.lastScrollability;\n const unchanged =\n next.up === last.up && next.down === last.down && next.left === last.left && next.right === last.right;\n if (unchanged && anything === this.lastScrollsAnything) {\n return;\n }\n this.lastScrollability = next;\n this.lastScrollsAnything = anything;\n this.scrollabilityListener?.(next, anything);\n }\n\n /** Hands the listener a report on the hovered node, when it changed. */\n private sendInspection(): void {\n // Compared on the explanation rather than on the whole report,\n // because the explanation already changes whenever anything about\n // the node's geometry does and a deep compare of the report would\n // cost more than building it.\n const text = this.inspector.explainHoveredText();\n if (text === this.lastInspection) {\n return;\n }\n this.lastInspection = text;\n const report = this.hoveredReport();\n this.inspectListener?.(report);\n this.devtoolsListener?.({ kind: 'hover', report });\n }\n\n /** A report on whatever the inspector says is hovered, or null. */\n private hoveredReport(): UiNodeReport | null {\n const node = this.inspector.hoveredNode;\n return node === null ? null : this.inspectNode(node);\n }\n\n /**\n * What a devtools panel is watching, after a frame: the tree when\n * its shape or text changed, and the selected node's report when\n * anything about it did. Nothing when no panel is attached.\n */\n private sendDevtoolsUpdates(frame: UiFrame): void {\n if (this.devtoolsListener === null) {\n return;\n }\n // A highlighted node that left the tree would keep its last box\n // drawn over whatever took its place.\n const highlighted = this.inspector.highlightedNode;\n if (highlighted !== null && this.graph.getNode(highlighted.id) !== highlighted) {\n this.inspector.setHighlighted(null);\n }\n if (this.watchingTree && (frameChangedTree(frame) || this.graph.subscriptionCount !== this.lastSubscriptions)) {\n this.sendTree();\n }\n if (this.selectedId !== null) {\n this.sendSelectedReport();\n }\n }\n\n /**\n * The selected node's report, when it differs from the last one\n * sent. A node that has gone is reported as null once, and then the\n * selection is dropped so the panel is not told again.\n */\n private sendSelectedReport(): void {\n const id = this.selectedId;\n if (id === null) {\n return;\n }\n const report = this.inspectNodeById(id);\n if (report === null) {\n this.selectedId = null;\n this.lastSelectedReport = null;\n this.devtoolsListener?.({ kind: 'report', id, report: null });\n return;\n }\n // Serialised rather than deep-compared: the report is plain data\n // by contract, and only one node's worth of it per frame.\n const text = JSON.stringify(report);\n if (text === this.lastSelectedReport) {\n return;\n }\n this.lastSelectedReport = text;\n this.devtoolsListener?.({ kind: 'report', id, report });\n }\n\n /**\n * Runs a phase when it has work, returning what it cost.\n *\n * A skipped phase reports 0, which is what makes the breakdown\n * useful: a frame doing nothing but scrolling should show zeroes\n * everywhere but render.\n */\n private timePhase(hasWork: () => boolean, run: () => void): number {\n if (!hasWork()) {\n return 0;\n }\n const started = now();\n run();\n return now() - started;\n }\n\n /** Duration of the most recent frame, in milliseconds. */\n get lastFrameDurationMs(): number {\n return this.lastFrameMs;\n }\n}\n\n/**\n * Anything that queues incoming state and applies it on a frame.\n *\n * Structural rather than a base class: a store replica and a channel\n * replica have nothing else in common, and the frame's first phase\n * only ever needs these three members.\n */\nexport interface PatchSource {\n readonly hasPendingPatches: boolean;\n flush(): void;\n deferPatches(scheduleFlush: () => void): void;\n}\n\nexport interface FrameMetrics {\n frame: number;\n /**\n * How long the frame took, from its first phase (the ticks, the\n * patches, the lazy lists' mounting, which run before its changes are\n * collected) to its end.\n */\n durationMs: number;\n nodes: number;\n /** Nodes the layout phase measured (memo hits excluded). */\n measured: number;\n /** Relayout boundaries the layout phase started from; 0 when it ran from the root or not at all. */\n relayoutRoots: number;\n /**\n * Times the frame laid out: 0 when it laid nothing out, 1 usually,\n * more when a layout listener (a `breakpoint`, a `sizeContainer`, an\n * `autoFocus`'s reveal) changed what the first pass laid out and the\n * frame laid it out again before painting. `measured` and\n * `relayoutRoots` count every pass.\n */\n layoutPasses: number;\n /** Milliseconds per phase. A phase with no work reports 0. */\n phases: FramePhaseTimings;\n /** The backend that drew this frame, or `pending` while WebGPU initialises. */\n renderer: RendererBackend | 'pending';\n /**\n * The WebGPU render phase split into its stages — building the render\n * list, uploading buffers, encoding and submitting — or null when\n * Canvas2D drew. Their sum is the render phase's cost on the GPU path.\n */\n gpu: GpuStageTimings | null;\n /**\n * When the frame finished, on the clock of the thread that rendered\n * it. Gaps between consecutive values are the only honest measure of\n * a stall: across a worker boundary the messages themselves queue up\n * behind a blocked main thread and all arrive at once, so arrival\n * times say nothing about when the work happened.\n */\n at: number;\n /**\n * How long the input this frame answers waited, from the moment the\n * shell received it to the moment this frame finished, or null when\n * the frame was not drawn for an input.\n *\n * The companion to `at`, and the measurement `at` cannot make: a\n * shell too busy to forward events costs the person a late response\n * while the render worker, with nothing new to draw, reports a\n * perfectly even frame gap. Null on a host that does not stamp its\n * input — see `GessoRuntime.noteInput`.\n */\n inputLatencyMs: number | null;\n /**\n * The display time the frame was drawn for: the refresh's timestamp,\n * on the same clock as `at`, or null for a frame with none.\n */\n time: number | null;\n /**\n * Where the container the wheel last scrolled is scrolled to after this\n * frame, or null before the wheel has scrolled anything. Read with\n * `time`, it says how evenly scrolling moves from frame to frame.\n */\n wheelScroll: { readonly x: number; readonly y: number } | null;\n}\n\nfunction now(): number {\n return typeof performance !== 'undefined' ? performance.now() : Date.now();\n}\n\n/**\n * Converts a `now()` reading to the cross-thread epoch clock.\n *\n * `performance.now()` counts from this thread's time origin, which in\n * a worker is the worker's own creation; the shell's stamps are\n * epoch-based so that the two can be subtracted. When there is no\n * `performance`, `now()` already returned `Date.now()` and the reading\n * is an epoch already.\n */\nfunction epochAt(reading: number): number {\n return timeOrigin() + reading;\n}\n\n/**\n * What to add to a `now()` reading to get an epoch one: zero when\n * `now()` was already `Date.now()`.\n */\nfunction timeOrigin(): number {\n return typeof performance !== 'undefined' ? performance.timeOrigin : 0;\n}\n\nfunction editingStatesEqual(a: EditingState | null, b: EditingState | null): boolean {\n if (a === null || b === null) {\n return a === b;\n }\n return (\n a.text === b.text &&\n a.selectionStart === b.selectionStart &&\n a.selectionEnd === b.selectionEnd &&\n a.selectionDirection === b.selectionDirection &&\n a.multiline === b.multiline &&\n a.composing === b.composing &&\n a.html === b.html &&\n a.clipboard === b.clipboard &&\n a.caret.x === b.caret.x &&\n a.caret.y === b.caret.y &&\n a.caret.width === b.caret.width &&\n a.caret.height === b.caret.height\n );\n}\n\n/** A canvas for the text measurer when the draw canvas is not a 2D one. */\nfunction createMeasureCanvas(): CanvasHost {\n if (typeof OffscreenCanvas !== 'undefined') {\n return new OffscreenCanvas(1, 1) as unknown as CanvasHost;\n }\n if (typeof document !== 'undefined') {\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n return canvas;\n }\n throw new Error('GessoRuntime: no canvas is available for text measurement; pass `measureCanvas`.');\n}\n\n/** Milliseconds per WebGPU stage of one frame. */\nexport interface GpuStageTimings {\n prepare: number;\n upload: number;\n encode: number;\n}\n\nfunction emptyGpuTimings(): GpuStageTimings {\n return { prepare: 0, upload: 0, encode: 0 };\n}\n\n/** What `settleLayout` leaves the rest of the frame. */\ninterface SettledLayout {\n /** Everything the frame changed, including what its layout listeners wrote. */\n frame: UiFrame;\n /** Layout passes run: 0 for a frame that laid nothing out, 1 for one whose listeners moved nothing. */\n passes: number;\n measured: number;\n relayoutRoots: number;\n}\n\n/**\n * The most layout passes one frame runs before it paints; see\n * `settleLayout`. One for the frame and up to seven more for what its\n * layout listeners change.\n */\nconst MAX_LAYOUT_PASSES = 8;\n\n/** Dirt that makes a frame lay out; `frameNeedsLayout` asks the same of a collected frame. */\nconst LAYOUT_DIRT = DirtyFlags.Layout | DirtyFlags.Children | DirtyFlags.SubtreeLayout | DirtyFlags.Transform;\n\nfunction emptyPhaseTimings(): FramePhaseTimings {\n return { ticks: 0, patches: 0, environment: 0, virtualize: 0, layout: 0, semantics: 0, render: 0 };\n}\n\n/**\n * Whether anything in the frame needs measuring or placing.\n *\n * Mirrors what LayoutEngine.layoutForFrame decides internally, so a\n * scroll-only frame is reported as skipping layout rather than\n * spending an immeasurable amount of time deciding to do nothing.\n */\nfunction frameNeedsLayout(frame: UiFrame): boolean {\n return frame.anyFlags(LAYOUT_DIRT);\n}\n\n/**\n * Semantics follow the properties that carry them and the shape of the\n * tree — a removed node marks its parent Children-dirty, which is how\n * a closed dialog leaves the tree.\n */\n/** Shared empties, so a frame that changed nothing allocates nothing. */\n/**\n * How many separate subtrees a scoped semantics update will walk\n * before it gives up and walks the whole tree once.\n *\n * A frame that re-meant a handful of nodes is the case this exists\n * for — a clock, a counter, a row whose label changed. A frame that\n * re-meant a hundred has almost certainly re-meant a shared ancestor's\n * worth of them, and the subtrees start to overlap, so one walk of\n * everything is both cheaper and simpler than many walks of parts.\n */\nconst SCOPED_SEMANTICS_OWNERS = 32;\n\n/**\n * The index each id in a parent's list of children has. A paragraph's\n * text runs are numbered among themselves and its other children among\n * themselves, as `buildSemanticsTree` numbers them.\n */\nfunction semanticIndices(list: readonly string[]): number[] {\n const out: number[] = [];\n let runs = 0;\n let others = 0;\n for (const id of list) {\n out.push(id.includes(TEXT_RUN_ID_SEPARATOR) ? runs++ : others++);\n }\n return out;\n}\n\n/**\n * How far outside the canvas a mirrored box is still worth reporting.\n *\n * One screenful of slack, so a fling that covers a lot of ground in\n * one frame still hands the mirror the rows it is about to show.\n */\nconst SEMANTICS_VIEWPORT_MARGIN = 400;\n\n/**\n * How long a run of moving frames has to pause before the mirrored\n * boxes are swept again; see `movedSemanticsBoxes`. Longer than the gap\n * between a trackpad's wheel events, so a pan is one run.\n */\nconst SEMANTICS_SETTLE_MS = 120;\n\n/** The longest the mirrored boxes go unswept while something keeps moving: an endless animation still gets them. */\nconst SEMANTICS_MOST_STALE_MS = 1000;\n\nconst EMPTY_PATCHES: readonly UiSemanticsPatch[] = [];\nconst EMPTY_BOXES: readonly UiSemanticsBox[] = [];\n\nfunction boxesEqual(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\n/**\n * Whether the frame changed what a tree snapshot shows: a node added,\n * removed or moved, or a text node's text. The same flags the\n * semantics tree rebuilds on, for the same reason: a box that only\n * moved changed neither.\n */\nfunction frameChangedTree(frame: UiFrame): boolean {\n return frameNeedsSemantics(frame);\n}\n\n/** The box a node with no record reports. */\nconst NO_BOX = { x: 0, y: 0, width: 0, height: 0 };\n\n/** Whether two wheel steps can be added together: the same keys held, in the same unit. */\nfunction sameWheel(a: Parameters<UiWheelController['wheel']>, b: Parameters<UiWheelController['wheel']>): boolean {\n const [, , , , keysA, unitA] = a;\n const [, , , , keysB, unitB] = b;\n return (\n unitA === unitB &&\n (keysA?.ctrl ?? false) === (keysB?.ctrl ?? false) &&\n (keysA?.shift ?? false) === (keysB?.shift ?? false) &&\n (keysA?.alt ?? false) === (keysB?.alt ?? false) &&\n (keysA?.meta ?? false) === (keysB?.meta ?? false)\n );\n}\n\nfunction frameNeedsSemantics(frame: UiFrame): boolean {\n return frame.anyFlags(DirtyFlags.Semantics | DirtyFlags.Children);\n}\n\n/**\n * Calls `restored` whenever the browser hands the canvas back after\n * losing it. Both canvas types are event targets; a test double that is\n * not one simply never hears.\n */\nfunction watchContextRestored(canvas: CanvasHost, restored: () => void): () => void {\n const target = canvas as Partial<EventTarget>;\n if (typeof target.addEventListener !== 'function' || typeof target.removeEventListener !== 'function') {\n return () => {};\n }\n target.addEventListener('contextrestored', restored);\n return () => target.removeEventListener?.('contextrestored', restored);\n}\n\n/** A frame's changes and those made since it was collected, together. */\nfunction withPending(frame: UiFrame, pending: DirtyNodeSet): UiFrame {\n if (pending.isEmpty()) {\n return frame;\n }\n const merged = new Map<UiNode, DirtyFlags>(frame.entries());\n for (const node of pending.values()) {\n merged.set(node, (merged.get(node) ?? DirtyFlags.None) | node.dirtyFlags);\n }\n return new UiFrame(frame.id, frame.time, merged);\n}\n\n/**\n * An input's supplied value as an owner report gives it: plain values\n * as they are, a stream by its current value, an element or a list of\n * them as a filled slot, and anything else (a handler, a data object)\n * not at all.\n */\nfunction ownerInput(value: unknown): UiOwnerInput | undefined {\n if (value === null || typeof value === 'string' || typeof value === 'boolean') {\n return value;\n }\n if (typeof value === 'number') {\n return Number.isFinite(value) ? value : undefined;\n }\n if (isObservable(value)) {\n const current = (value as { value?: unknown }).value;\n return current === undefined || isObservable(current) ? undefined : ownerInput(current);\n }\n const isElement = (item: unknown) => isUiElement(item) || isComponentLikeElement(item);\n if (isElement(value) || (Array.isArray(value) && value.length > 0 && value.every(isElement))) {\n return { element: true };\n }\n return undefined;\n}\n","/**\n * Errors a piece of work has said are its own, so the thread's error\n * handling leaves them to it.\n *\n * `renderFrame` draws a tree an agent wrote, in the thread that draws\n * the person's screen. When that tree throws from a subscription, the\n * error is uncaught in the thread, and the handlers that report the\n * thread's failures would put it over the person's screen: an agent's\n * draft, failing, in front of somebody using the application. The\n * frame cannot get ahead of those handlers, because on a global object\n * listeners run in the order they were added, capture or not. So the\n * handlers ask instead: each checks `isClaimedError` and leaves alone\n * what a frame claimed, and the frame reports it to the agent.\n *\n * The claims live under a `Symbol.for` key on the global object rather\n * than in this module, so `gesso-devtools`' overlay can honour them\n * without importing the framework.\n */\n\ntype Claim = (error: unknown) => boolean;\n\nconst KEY = Symbol.for('gesso.claimedErrors');\n\nfunction claims(): Set<Claim> {\n const scope = globalThis as { [KEY]?: Set<Claim> };\n return (scope[KEY] ??= new Set());\n}\n\n/** Claims the errors `test` accepts until the returned function is called. */\nexport function claimErrors(test: Claim): () => void {\n claims().add(test);\n return () => claims().delete(test);\n}\n\n/** Whether some work in progress has claimed this error as its own. */\nexport function isClaimedError(error: unknown): boolean {\n for (const test of claims()) {\n if (test(error)) {\n return true;\n }\n }\n return false;\n}\n","import {\n Column,\n UiManualFrameClock,\n lightTheme,\n mediaLoadsInFlight,\n type CanvasHost,\n type UiChild,\n type UiTheme\n} from 'gesso-core';\n\nimport type { ChannelRegistry } from '../channel/ChannelRegistry';\nimport type { FrameworkChild } from '../ComponentElement';\nimport { ServiceRegistry } from '../service/ServiceRegistry';\nimport { claimErrors } from './claimedErrors';\nimport { GessoRuntime } from './GessoRuntime';\n\n/**\n * A tree drawn once, off screen, to a PNG.\n *\n * What an agent designing a screen needs most is to see it. The screen\n * in front of the person is one size and one state; a mock is wanted at\n * a phone's width and a desktop's, and before anything has mounted it.\n * So the tree gets a runtime of its own, on a canvas nobody sees, with\n * a clock this drives by hand: it runs frames until the tree stops\n * asking for them and no picture is still on its way (a picture being\n * fetched asks for no frames until it arrives), sizes the canvas to the\n * content, draws, and is thrown away.\n *\n * The runtime is the same one the application uses, with Canvas2D, so\n * the picture is the application's pixels and not an approximation of\n * them: the same layout engine, text shaping and paint.\n */\n\n/** A canvas this can read a PNG back from: an `OffscreenCanvas`, in a worker or a page. */\nexport interface PictureCanvas extends CanvasHost {\n convertToBlob(options?: { type?: string }): Promise<Blob>;\n}\n\nexport interface RenderFrameOptions {\n /** Its width, in logical pixels. */\n readonly width: number;\n /**\n * Its height, which content that fills its parent fills: give one\n * for a whole screen. Left out, the frame is as tall as its content,\n * up to `maxHeight`, which is right for a component or a section; a\n * screen that fills the window would collapse to what is in it.\n */\n readonly height?: number;\n /** Device pixels per logical pixel (default 1). */\n readonly scale?: number;\n /** The theme it is drawn in (default the light theme). */\n readonly theme?: UiTheme;\n /** The tallest a frame grows to fit its content (default 4000). */\n readonly maxHeight?: number;\n /** Channels for the components in it to replicate: the running application's, to show its data. */\n readonly channels?: ChannelRegistry;\n /**\n * Services for the components in it to inject: the running\n * application's. The frame's runtime registers its own over them\n * rather than borrowing them, since those belong to the window.\n */\n readonly services?: ServiceRegistry;\n /** The longest to wait for the tree to stop asking for frames, in milliseconds (default 2000). */\n readonly settleMs?: number;\n /**\n * Whether an error thrown while the frame is drawn is the frame's own,\n * to keep it from the application's error handling: a draft an agent\n * got wrong should not put an error over the person's screen (see\n * `claimedErrors.ts`). One it does not claim is still listed in\n * `errors`, and still reported.\n */\n readonly claims?: (error: unknown) => boolean;\n /** Makes the canvas (default `new OffscreenCanvas`). */\n readonly createCanvas?: (width: number, height: number) => PictureCanvas;\n}\n\nexport interface RenderedFrame {\n readonly png: Uint8Array;\n /** Logical pixels; the PNG is these times `scale`. */\n readonly width: number;\n readonly height: number;\n /** Whether the content was taller than `maxHeight`, and is cut off there. */\n readonly clipped: boolean;\n /** Whether the content had no height of its own: a closed popup, say, or one waiting on inputs. */\n readonly empty: boolean;\n /**\n * What was thrown and not caught in this thread while the frame was\n * drawn. A component reading an input it was not given throws from a\n * subscription, which no frame catches, and the picture alone would\n * only show that nothing was drawn. The thread is shared, so an error\n * here is likely the frame's and not certainly.\n */\n readonly errors: readonly string[];\n}\n\nexport async function renderFrame(root: FrameworkChild, options: RenderFrameOptions): Promise<RenderedFrame> {\n const width = Math.round(options.width);\n if (!(width > 0)) {\n throw new Error(`A frame needs a width above zero, not ${options.width}.`);\n }\n const scale = options.scale ?? 1;\n const maxHeight = options.maxHeight ?? 4000;\n const probe = options.height ?? maxHeight;\n const createCanvas =\n options.createCanvas ?? ((w: number, h: number) => new OffscreenCanvas(w, h) as unknown as PictureCanvas);\n const canvas = createCanvas(width * scale, probe * scale);\n\n // The layout root stretches the app root over the canvas. The frame\n // inside it fills that when a height was asked for, so a screen fills\n // it too, and otherwise keeps its content's height, which is then\n // read off it.\n const frame = Column(options.height === undefined ? {} : { flex: 1 }, root as UiChild);\n let clock!: UiManualFrameClock;\n const runtime = new GessoRuntime({\n // Painted in the theme's background, as a window is: a canvas left\n // transparent shows dark-theme content on whatever the viewer's\n // ground is, which is usually white.\n root: Column({ theme: options.theme ?? lightTheme, backgroundColor: 'background' }, frame),\n canvas,\n renderer: 'canvas2d',\n width,\n height: probe,\n dpr: scale,\n ...(options.channels === undefined ? {} : { channels: options.channels }),\n services: new ServiceRegistry(options.services),\n clock: callback => (clock = new UiManualFrameClock(callback))\n });\n let failure: string | null = null;\n runtime.onFrameError((message, stack) => {\n failure ??= stack ?? message;\n });\n const errors: string[] = [];\n const uncaught = (event: Event) => {\n const reason =\n (event as ErrorEvent).error ?? (event as PromiseRejectionEvent).reason ?? (event as ErrorEvent).message;\n errors.push(reason instanceof Error ? reason.message : String(reason));\n if (options.claims?.(reason) === true) {\n // Handled: a worker's failure goes no further than the worker.\n event.preventDefault();\n }\n };\n const scope = globalThis as {\n addEventListener?: EventTarget['addEventListener'];\n removeEventListener?: EventTarget['removeEventListener'];\n };\n scope.addEventListener?.('error', uncaught);\n scope.addEventListener?.('unhandledrejection', uncaught);\n const release = options.claims === undefined ? () => {} : claimErrors(options.claims);\n try {\n runtime.start();\n let now = 0;\n const settle = async () => {\n const deadline = Date.now() + (options.settleMs ?? 2000);\n let quiet = 0;\n while (quiet < 3 && Date.now() < deadline) {\n if (clock.isPending) {\n clock.tick((now += 16));\n quiet = 0;\n } else if (mediaLoadsInFlight() > 0) {\n // Quiet only because a picture is still being fetched or\n // decoded: it asks for a frame when it lands.\n quiet = 0;\n } else {\n quiet++;\n }\n await new Promise(resolve => setTimeout(resolve, 16));\n }\n if (failure !== null) {\n throw new Error(`The frame threw while it was drawn:\\n${failure}`);\n }\n };\n await settle();\n\n let height = probe;\n let clipped = false;\n let empty = false;\n if (options.height === undefined) {\n const node = runtime.layoutRoot().firstChild?.firstChild ?? null;\n const content = node === null ? probe : Math.ceil(runtime.debugLayoutBox(node).height);\n clipped = content > probe;\n empty = content === 0;\n height = Math.max(1, Math.min(content, probe));\n runtime.resize(width, height, scale);\n await settle();\n }\n\n const blob = await canvas.convertToBlob({ type: 'image/png' });\n return { png: new Uint8Array(await blob.arrayBuffer()), width, height, clipped, empty, errors };\n } finally {\n runtime.dispose();\n // RxJS rethrows what a subscriber did not catch from a timer, so an\n // error from the last frame, or from taking the tree down, arrives\n // after this would otherwise have stopped listening.\n await new Promise(resolve => setTimeout(resolve, 20));\n release();\n scope.removeEventListener?.('error', uncaught);\n scope.removeEventListener?.('unhandledrejection', uncaught);\n }\n}\n","import { Column, isUiElement, Text, type UiElement, type UiTheme } from 'gesso-core';\n\nimport type { ChannelRegistry } from '../channel/ChannelRegistry';\nimport type { ServiceRegistry } from '../service/ServiceRegistry';\nimport { createComponent } from '../createComponent';\nimport type { ComponentType } from '../FunctionComponent';\nimport { renderFrame, type RenderFrameOptions } from '../app/renderFrame';\nimport type { AgentToolResult } from './AgentSurface';\nimport { designedTheme, type AgentThemeName, type DesignThemes } from './designedTheme';\n\n/**\n * `render_frame`: a module of the project, drawn to a PNG an agent can\n * look at.\n *\n * An agent that writes a screen and cannot see it is designing blind;\n * the outline `ui_snapshot` gives says what is there, not how it looks.\n * So this draws one: a mock, or a component with inputs, at the width\n * asked for, in the theme the agent is designing in by default (see\n * `designedTheme`), through the same runtime the application draws with\n * (see `renderFrame`).\n *\n * The module is loaded by path from where the application is served,\n * which in development is the dev server: the same file the agent just\n * wrote, as Vite serves it.\n */\n\nexport interface RenderRequest {\n /** The module, from the project root, with or without its extension: `src/mocks/Settings`. */\n readonly module: string;\n /** The export to draw. By default the module's default export, else its only component. */\n readonly export?: string;\n /** Inputs for a component, by name. */\n readonly inputs?: Readonly<Record<string, unknown>>;\n readonly width: number;\n readonly height?: number;\n readonly scale?: number;\n /** The theme to draw in; see `designedTheme` for what each means and which is the default. */\n readonly theme?: AgentThemeName;\n}\n\nexport interface RenderDependencies {\n /** Loads a module by its URL path, `/src/mocks/Settings.tsx`: `import()` where it is served. */\n load(path: string): Promise<Record<string, unknown>>;\n /** The theme the running application is drawn in. */\n appTheme(): UiTheme | undefined;\n /** The running application's channels, so a component that replicates one shows its data. */\n readonly channels?: ChannelRegistry;\n /** The running application's services, so a component that injects one finds it. */\n readonly services?: ServiceRegistry;\n /** The themes the application designs in, if it has any; see `designedTheme`. */\n readonly themes?: () => DesignThemes;\n /** Passed to `renderFrame`, for a test that has no `OffscreenCanvas`. */\n readonly frame?: Pick<RenderFrameOptions, 'createCanvas' | 'settleMs'>;\n}\n\n/** The extensions tried, in order, for a module named without one. */\nconst EXTENSIONS = ['.tsx', '.ts', '.jsx', '.js', '/index.tsx', '/index.ts'];\n\nexport async function renderModule(request: RenderRequest, deps: RenderDependencies): Promise<AgentToolResult> {\n const module = await loadModule(request.module, deps.load);\n if (typeof module === 'string') {\n return failure(module);\n }\n const chosen = chooseExport(module.exports, request.export, module.path);\n if (typeof chosen === 'string') {\n return failure(chosen);\n }\n const root = isUiElement(chosen.value)\n ? chosen.value\n : createComponent(chosen.value as ComponentType, (request.inputs ?? {}) as never);\n const { theme, name: themeName } = designedTheme(request.theme, deps.themes?.() ?? {}, deps.appTheme);\n const scale = Math.min(3, Math.max(1, request.scale ?? 1));\n let frame;\n try {\n frame = await renderFrame(root, {\n width: request.width,\n ...(request.height === undefined ? {} : { height: request.height }),\n scale,\n theme,\n ...(deps.channels === undefined ? {} : { channels: deps.channels }),\n ...(deps.services === undefined ? {} : { services: deps.services }),\n // Its own when the stack runs through the module drawn.\n claims: error => error instanceof Error && (error.stack ?? '').includes(`/${module.path}`),\n ...deps.frame\n });\n } catch (error) {\n return failure(\n `${chosen.name} from ${module.path} could not be drawn: ${String((error as Error).message ?? error)}`\n );\n }\n const data = base64(frame.png);\n const thrown =\n frame.errors.length === 0\n ? ''\n : ` While it was drawn, this was thrown: ${frame.errors.join('; ')}. It is likely the frame's own: check the ` +\n 'inputs it was given against library_get.';\n const note = frame.empty\n ? ' It drew nothing: it has no height of its own. A popup or dialog may be closed until an input opens it, or ' +\n 'it may fill its parent; give inputs that show it, or a height.'\n : frame.clipped\n ? ` It is taller than that and is cut off; give a height, or draw a part of it.`\n : request.height === undefined\n ? ' As tall as its content; give a height to draw a whole screen that fills the window.'\n : '';\n return {\n content: [\n { type: 'image', data, mimeType: 'image/png' },\n {\n type: 'text',\n text: `${chosen.name} from ${module.path}, ${frame.width}×${frame.height} at ${scale}x, in the ${themeName} theme.${note}${thrown}`\n }\n ],\n structuredContent: {\n module: module.path,\n export: chosen.name,\n width: frame.width,\n height: frame.height,\n scale,\n clipped: frame.clipped,\n empty: frame.empty,\n errors: frame.errors,\n mimeType: 'image/png',\n data\n },\n isError: false\n };\n}\n\nasync function loadModule(\n module: string,\n load: RenderDependencies['load']\n): Promise<{ path: string; exports: Record<string, unknown> } | string> {\n const path = `/${module.replace(/^\\.?\\/+/, '')}`;\n const candidates = /\\.[cm]?[jt]sx?$/.test(path) ? [path] : EXTENSIONS.map(extension => `${path}${extension}`);\n let last: unknown;\n for (const candidate of candidates) {\n try {\n return { path: candidate.slice(1), exports: await load(candidate) };\n } catch (error) {\n last = error;\n }\n }\n return (\n `No module loads from ${module}: tried ${candidates.map(candidate => candidate.slice(1)).join(', ')}. ` +\n `Name it from the project root, as library_overview does. (${String((last as Error)?.message ?? last)})`\n );\n}\n\n/** The export to draw, or why none can be chosen. */\nfunction chooseExport(\n exports: Record<string, unknown>,\n wanted: string | undefined,\n module: string\n): { name: string; value: unknown } | string {\n const drawable = (value: unknown) => typeof value === 'function' || isUiElement(value) || examplesOf(value) !== null;\n if (wanted !== undefined) {\n // `Button.Primary`: one of the examples an object of them holds.\n const [head, ...rest] = wanted.split('.');\n let value: unknown = exports[head!];\n for (const key of rest) {\n value = typeof value === 'object' && value !== null ? (value as Record<string, unknown>)[key] : undefined;\n }\n if (!drawable(value)) {\n return `${module} has no component or element exported as ${wanted}. It exports ${list(exports, drawable)}.`;\n }\n const examples = examplesOf(value);\n return { name: wanted, value: examples === null ? value : examplesSheet(examples) };\n }\n const asDrawn = (value: unknown) => {\n const examples = examplesOf(value);\n return examples === null ? value : examplesSheet(examples);\n };\n if (drawable(exports.default)) {\n return { name: 'default', value: asDrawn(exports.default) };\n }\n // A component's name is capitalised, which a helper's usually is not.\n const candidates = Object.keys(exports).filter(name => /^[A-Z]/.test(name) && drawable(exports[name]));\n if (candidates.length === 1) {\n return { name: candidates[0]!, value: asDrawn(exports[candidates[0]!]) };\n }\n return candidates.length === 0\n ? `${module} exports nothing to draw: no component, and no element.`\n : `${module} exports ${candidates.join(', ')}; say which with export.`;\n}\n\n/**\n * An object of examples, as a component's states are kept for a design\n * tool (`export const Button = { Primary: <Button … />, … }`): each\n * element by its name, or null for anything else.\n */\nfunction examplesOf(value: unknown): readonly [string, UiElement][] | null {\n if (typeof value !== 'object' || value === null || isUiElement(value) || Array.isArray(value)) {\n return null;\n }\n const entries = Object.entries(value as Record<string, unknown>).filter((entry): entry is [string, UiElement] =>\n isUiElement(entry[1])\n );\n return entries.length === 0 ? null : entries;\n}\n\n/** Every example, each under its name, down the frame. */\nfunction examplesSheet(examples: readonly [string, UiElement][]): UiElement {\n return Column(\n { gap: 24, padding: 16 },\n ...examples.map(([name, element]) =>\n Column({ key: name, gap: 8 }, Text({ text: name, fontSize: 12, fontWeight: '600', color: 'textMuted' }), element)\n )\n );\n}\n\nfunction list(exports: Record<string, unknown>, keep: (value: unknown) => boolean): string {\n const names = Object.keys(exports).filter(name => keep(exports[name]));\n return names.length === 0 ? 'nothing that can be drawn' : names.join(', ');\n}\n\nfunction base64(bytes: Uint8Array): string {\n let binary = '';\n for (let at = 0; at < bytes.length; at += 0x8000) {\n binary += String.fromCharCode(...bytes.subarray(at, at + 0x8000));\n }\n return btoa(binary);\n}\n\nfunction failure(text: string): AgentToolResult {\n return { content: [{ type: 'text', text }], isError: true };\n}\n"],"mappings":"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"}