gesso-framework 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["normalize","measure","measure"],"sources":["../src/Component.ts","../src/service/ServiceRegistry.ts","../src/bind.ts","../src/bounds.ts","../src/themeTokenCell.ts","../src/controlled.ts","../src/decorators.ts","../src/channel/ChannelReplica.ts","../src/channel/ChannelRegistry.ts","../src/channel/createChannelRegistry.ts","../src/FunctionComponent.ts","../src/overlay/OverlayService.ts","../src/overlay/OverlayLayer.ts","../src/ComponentElement.ts","../src/ComponentHost.ts","../src/ComponentHostResolver.ts","../src/app/DevtoolsProtocol.ts","../src/app/ShellService.ts","../src/app/worker/RenderWorkerProtocol.ts","../src/app/AudioService.ts","../src/router/RouteState.ts","../src/router/RoutePath.ts","../src/router/RouterService.ts","../src/app/FindService.ts","../src/app/FocusService.ts","../src/app/MediaService.ts","../src/app/FontService.ts","../src/app/AnimationService.ts","../src/app/FrameService.ts","../src/app/InputLatency.ts","../src/app/SmoothScroller.ts","../src/app/GessoRuntime.ts","../src/app/AudioSink.ts","../src/app/EditingProxy.ts","../src/app/SemanticsMirror.ts","../src/app/shellStorage.ts","../src/app/mediaQuery.ts","../src/app/colorScheme.ts","../src/app/reducedMotion.ts","../src/app/shellHistory.ts","../src/app/worker/WorkerApp.ts","../src/app/GessoApp.ts","../src/app/GessoAppBuilder.ts","../src/app/createApp.ts","../src/undo/undoShortcuts.ts","../src/storage/ShellStorage.ts","../src/Presence.ts","../src/router/RouterOutlet.ts","../src/router/RouteDefinition.ts","../src/app/worker/renderRoot.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\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) {\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 has(ServiceClass: Function): boolean {\n return this.services.has(ServiceClass);\n }\n}\n","import type { ReadableCell } from './Input';\nimport type { InternalState } from './InternalState';\n\n/**\n * A two-way binding for the plain case: the cell is the control's value,\n * and the control's changes are written back into the cell.\n *\n * const volume = internalState(0.8);\n * <Slider {...bind(volume)} />\n *\n * Spread onto a control that takes `value` and `onChange`, or name the\n * pair for one that calls them something else:\n *\n * <Checkbox {...bind(muted, 'checked')} />\n *\n * A value that lives across the barrier is read from a channel key and\n * written with a command, which is two halves of one binding, so it\n * takes the same shape with the write said out loud:\n *\n * <TextInput {...bind(library.view.filter, library.send.setFilter)} />\n *\n * That is the form a real form wants. A screen's fields are almost\n * never a component's own state: they are the application's, they\n * survive the screen, and they are on a channel. The cell is read-only\n * there, as an input is, and the command is what writes it, so the\n * round trip through the application thread is visible at the call\n * site rather than hidden by a helper pretending the key is writable.\n *\n * Sugar over the controlled contract, not a replacement for it.\n */\nexport function bind<T>(cell: InternalState<T>): { value: InternalState<T>; onChange: (next: T) => void };\nexport function bind<T, V extends string, E extends string = 'onChange'>(\n cell: InternalState<T>,\n value: V,\n onChange?: E\n): { [K in V]: InternalState<T> } & { [K in E]: (next: T) => void };\nexport function bind<T>(\n cell: ReadableCell<T>,\n write: (next: T) => void\n): { value: ReadableCell<T>; onChange: (next: T) => void };\nexport function bind<T, V extends string, E extends string = 'onChange'>(\n cell: ReadableCell<T>,\n write: (next: T) => void,\n value: V,\n onChange?: E\n): { [K in V]: ReadableCell<T> } & { [K in E]: (next: T) => void };\nexport function bind<T>(\n cell: ReadableCell<T>,\n second?: ((next: T) => void) | string,\n third?: string,\n fourth?: string\n): Record<string, unknown> {\n const writing = typeof second === 'function';\n const write =\n second === undefined || !writing\n ? (next: T) => {\n (cell as InternalState<T>).value = next;\n }\n : second;\n const value = (writing ? third : (second as string | undefined)) ?? 'value';\n const onChange = (writing ? fourth : third) ?? 'onChange';\n return { [value]: cell, [onChange]: write };\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","import { distinctUntilChanged, map, type Observable } from 'rxjs';\n\nimport { themeTokens, type UiModifier, type UiThemeExtension } from 'gesso-core';\nimport { InternalState } from './InternalState';\n\n/**\n * A theme extension's tokens, as a cell, with the modifier that fills\n * it.\n *\n * The same shape as `ctx.bounds()`, and for the same reason: the value\n * lives on a node and a component body has no node yet, so the cell is\n * the value and the modifier is how it gets filled.\n *\n * const tokens = themeTokenCell(controlTokens);\n * return Button(\n * {\n * modifiers: [tokens.modifier, ...],\n * paddingX: tokens.select(t => t.button.medium.paddingX),\n * borderRadius: tokens.select(t => t.button.medium.radius)\n * },\n * Text({ text: label, color: tokens.select(t => t.button.filled.foreground) })\n * );\n *\n * It starts holding the extension's declared defaults, which is the\n * honest value for a node that is not in a tree: it is what\n * `themeExtension` would answer for a theme carrying nothing. The\n * modifier replaces it at attach, before the first frame is drawn, so\n * a component that is never restyled draws its defaults and never\n * flickers through them.\n *\n * Bind with `select`, not with `value`. The cell publishes a whole\n * group, so every binding taken straight off it would recompute when\n * any token in the group changed; `select` projects one token and\n * drops a repeat, which is what keeps a theme change that moved a\n * colour from re-writing every padding on the element.\n */\nexport class ThemeTokenCell<T extends object> extends InternalState<T> {\n /** Put this on the element whose inherited theme the cell should read. */\n readonly modifier: UiModifier;\n\n constructor(extension: UiThemeExtension<T>) {\n super(extension.defaults);\n this.modifier = themeTokens<T>({ extension, sink: this });\n }\n\n /** One token, as an Observable that drops a repeat. */\n select<R>(pick: (tokens: T) => R): Observable<R> {\n return this.pipe(map(pick), distinctUntilChanged());\n }\n}\n\n/**\n * A token cell outside a component, for a class component or a test.\n * Inside a function component this is the same thing, and it completes\n * with the component.\n */\nexport function themeTokenCell<T extends object>(extension: UiThemeExtension<T>, label?: string): ThemeTokenCell<T> {\n const cell = new ThemeTokenCell(extension);\n if (label !== undefined) {\n cell.label = label;\n }\n return cell;\n}\n","import { map, type Observable } from 'rxjs';\n\nimport type { InputCell, OutputCell } from './Input';\nimport { internalState, type InternalState } from './InternalState';\n\n/** A value a control shows and changes, whoever owns it. */\nexport interface ControlledValue<T> {\n readonly value: Observable<T>;\n current(): T;\n /** What the control calls when the person changes it. */\n change(next: T): void;\n}\n\nexport interface ControlledOptions<T> {\n /** The component's name, for the error when both forms are passed. */\n readonly component: string;\n /** The input's name: `value`, `checked`, `open`. */\n readonly name: string;\n /** The controlled form: the parent owns the value and hears every change. */\n readonly source: InputCell<T | undefined>;\n /** The self-managed form: a starting value the control then owns. */\n readonly initial: InputCell<T | undefined>;\n /** What the control shows when given neither. */\n readonly fallback: T;\n /** Fired on every change in either form. */\n readonly onChange: OutputCell<[next: T]>;\n}\n\n/**\n * Controlled by default, self-managing when given only an initial value.\n *\n * The one contract every stateful control in the library obeys, and the\n * one an application component should copy: pass `value` and the app\n * owns the state and hears every change through `onChange`; pass\n * `defaultValue` and the control owns it, still reporting changes;\n * pass both and it throws, because two owners is a bug. `bind(cell)` is\n * the spread for the first form when the owner is a local cell.\n */\nexport function controlled<T>(options: ControlledOptions<T>): ControlledValue<T> {\n const { component, name, source, initial, fallback, onChange } = options;\n const isControlled = source.value !== undefined;\n if (isControlled && initial.value !== undefined) {\n throw new Error(\n `${component} was given both '${name}' and 'default${capitalize(name)}'. ` +\n `Pass '${name}' for the app to own the value, or 'default${capitalize(name)}' for the control to.`\n );\n }\n if (isControlled) {\n return {\n value: source.pipe(map(value => (value === undefined ? fallback : value))),\n current: () => (source.value === undefined ? fallback : source.value),\n change: next => onChange.emit(next)\n };\n }\n const own: InternalState<T> = internalState(initial.value === undefined ? fallback : initial.value);\n return {\n value: own,\n current: () => own.value,\n change: next => {\n own.value = next;\n onChange.emit(next);\n }\n };\n}\n\nfunction capitalize(name: string): string {\n return name.charAt(0).toUpperCase() + name.slice(1);\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 });\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.\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 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 cell.next(applyPatches(cell.value, group));\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 } from 'rxjs';\n\nimport { workerHandle, type WorkerHandle } from '../worker/WorkerPorts';\nimport { ChannelRegistry } from './ChannelRegistry';\nimport type { ChannelPort } from './ChannelProtocol';\nimport { provide, ProvidedChannel, type ChannelSource } from './provide';\nimport type { ChannelToken, Command, CommandMap } from './ChannelToken';\n\n/**\n * A registration with its types erased.\n *\n * Erased structurally rather than as `ChannelToken<never, never>`: a\n * heterogeneous list of channels has no single generic instantiation,\n * and `never` made every caller cast. What the registry actually needs\n * is a name, an initial shape, observables by key and callables by\n * name — all of which a concrete registration satisfies on its own.\n */\nexport interface ChannelRegistration {\n token: { name: string; initial: object };\n /**\n * The worker that owns this channel's data.\n *\n * A `WorkerHandle` shared between registrations puts them in one\n * application worker, which is the arrangement the barrier design\n * exists for: api, store, domain and view models together, one\n * thread, several channels.\n */\n worker?: WorkerHandle | (() => Worker);\n /**\n * What feeds the channel, when it is owned by this thread.\n *\n * Still crosses a real `MessageChannel`, so the same diff, the same\n * patches and the same plain-data rule apply — a channel behaves\n * identically wherever it lives, which is what lets it be moved into\n * a worker later without touching a view.\n */\n source?: {\n view: Record<string, Observable<unknown>>;\n commands?: Record<string, Command>;\n };\n}\n\nexport interface ChannelRegistryHandle {\n registry: ChannelRegistry;\n dispose(): void;\n}\n\nfunction isWorkerHandle(worker: WorkerHandle | (() => Worker)): worker is WorkerHandle {\n return typeof worker === 'object';\n}\n\n/**\n * Attaches every registered channel, wherever its data lives.\n */\nexport function createChannelRegistry(\n registrations: readonly ChannelRegistration[],\n onError?: (channelName: string, message: string, stack?: string) => void\n): ChannelRegistryHandle {\n const registry = new ChannelRegistry();\n const handles = new Set<WorkerHandle>();\n const wrapped = new Map<() => Worker, WorkerHandle>();\n const local: ProvidedChannel[] = [];\n\n for (const registration of registrations) {\n const token = registration.token as unknown as ChannelToken<object, CommandMap>;\n let port: ChannelPort;\n\n if (registration.worker !== undefined) {\n let handle: WorkerHandle;\n if (isWorkerHandle(registration.worker)) {\n handle = registration.worker;\n } else {\n const factory = registration.worker;\n handle = wrapped.get(factory) ?? workerHandle(factory);\n wrapped.set(factory, handle);\n }\n handles.add(handle);\n port = handle.open(token.name) as unknown as ChannelPort;\n } else {\n if (registration.source === undefined) {\n throw new Error(\n `Channel '${token.name}' was registered with neither a worker nor a source, ` +\n `and no application-logic worker was supplied to serve it. Pass appLogicWorker to ` +\n `createApp to spawn one, source to feed the channel from this thread, or worker to ` +\n `name a worker of its own.`\n );\n }\n const pair = new MessageChannel();\n local.push(\n provide(\n token,\n registration.source as unknown as ChannelSource<object, CommandMap>,\n pair.port2 as unknown as ChannelPort\n )\n );\n port = pair.port1 as unknown as ChannelPort;\n }\n\n const replica = registry.attach(token, port);\n replica.onError((message, stack) => {\n if (onError !== undefined) {\n onError(token.name, message, stack);\n } else {\n console.error(`[gesso channel ${token.name}] ${message}`, stack);\n }\n });\n }\n\n return {\n registry,\n dispose: () => {\n for (const channel of local) {\n channel.dispose();\n }\n local.length = 0;\n registry.dispose();\n for (const handle of handles) {\n handle.terminate();\n }\n handles.clear();\n wrapped.clear();\n }\n };\n}\n","import type { Observable, Subscription } from 'rxjs';\n\nimport type { Reactive, UiChild } 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/**\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<P[K]>;\n};\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 type { UiChild, UiNode } from 'gesso-core';\nimport { internalState } from '../InternalState';\n\nexport type OverlayPlacement =\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\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 /** Default 'bottom'. */\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 * 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 /** 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","import { map } from 'rxjs';\n\nimport { Box, UiEnvironmentKeys, type UiNode, type UiChild, type UiElement } from 'gesso-core';\nimport { Component } from '../Component';\nimport { Define, Inject } from '../decorators';\nimport { OverlayService, type OverlayEntry } 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 closes the entry on pointer down or wheel.\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@Define('gesso-overlay-layer')\nexport class OverlayLayer extends Component {\n @Inject(OverlayService) overlays!: OverlayService;\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 if (entry.dismissOnOutsidePress) {\n elements.push(\n Box({\n key: `${entry.id}\\0backdrop`,\n position: 'absolute',\n inset: 0,\n zIndex: entry.zIndex,\n onPointerDown: () => this.overlays.close(entry.id),\n // A wheel over the backdrop is the user scrolling away; the\n // menu closes rather than swallowing the scroll.\n onWheel: () => this.overlays.close(entry.id)\n })\n );\n }\n elements.push(\n Box(\n {\n key: entry.id,\n position: 'absolute',\n anchor: entry.anchor ?? undefined,\n placement: entry.placement,\n anchorOffset: entry.offset,\n top: entry.top,\n right: entry.right,\n bottom: entry.bottom,\n left: entry.left,\n zIndex: entry.zIndex,\n ...centering(entry),\n ...inheritedFrom(entry.environment ?? entry.anchor ?? null)\n },\n entry.content as UiChild\n )\n );\n }\n return elements;\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.\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 ? { left: entry.left ?? 0, right: entry.right ?? 0, x: 'center' } : {}),\n ...(y ? { top: entry.top ?? 0, bottom: entry.bottom ?? 0, y: 'center' } : {})\n };\n}\n\n/**\n * The scoped values an entry's content should keep, re-provided on the\n * box that holds it.\n *\n * Read once, when the entry opens. An overlay that outlives a theme\n * change re-opens; nothing here watches, because an entry's content is\n * built once too.\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 contentColor: environment.get(UiEnvironmentKeys.contentColor)\n };\n}\n","import type { UiChild } from 'gesso-core';\nimport type { ComponentType } from './FunctionComponent';\n\n/**\n * Declarative representation of a component instance in the tree.\n *\n * ComponentElements are resolved by ComponentRenderer into plain\n * UiElement trees before being passed to UiGraphBuilder.\n */\nexport interface ComponentElement<P = Record<string, unknown>> {\n readonly kind: 'component';\n readonly tag: string;\n /** The class or function to mount. */\n readonly component: ComponentType;\n readonly props: P;\n readonly key?: string | number;\n}\n\nexport function isComponentElement(value: unknown): value is ComponentElement {\n return typeof value === 'object' && value !== null && (value as Partial<ComponentElement>).kind === 'component';\n}\n\n/**\n * Anything the framework accepts as a child: a runtime element, an\n * observable stream of elements, or a component definition.\n */\nexport type FrameworkChild = UiChild | ComponentElement;\n","import { 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 } from 'gesso-core';\nimport { 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';\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 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 ) {\n this.element = element;\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, props[name], true);\n }\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 withBodyOf(this.element.tag, () => component(this.createInputRecord(), context));\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.element.props as Record<string, unknown>)[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 };\n }\n\n private wireInputs(): void {\n const metadata = getComponentMetadata(this.element.component);\n const props = this.element.props as Record<string, unknown>;\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, props[inputName], false);\n }\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(inputName: string, cell: InputCell<unknown>, provided: unknown, resetWhenAbsent: boolean): void {\n if (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 const subscription = provided.subscribe(value => cell.next(value));\n this.inputSubscriptions.set(inputName, subscription);\n this.subscriptions.add(subscription);\n return;\n }\n\n cell.next(provided);\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","import type { ComponentResolver, ComponentLikeElement, UiChild } 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): 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);\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 { 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 { insetsEqual, noInsets, type UiInsets } from 'gesso-core';\n\nimport { internalState } from '../InternalState';\nimport type { ColorScheme } 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'; text: string }\n | { type: 'openUrl'; url: string }\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: 'history'; action: 'push' | 'replace'; url: string }\n | { type: 'history'; action: 'back' | 'forward'; url?: undefined };\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 private readonly scheme = internalState<ColorScheme>('light');\n private readonly insets = internalState<UiInsets>(noInsets);\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\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 /** 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 /** Installed by the runtime; a request with no handler is dropped. */\n setHandler(handler: ((request: ShellRequest) => void) | null): void {\n this.handler = handler;\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 /**\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 /** Puts text on the system clipboard. */\n copyText(text: string): void {\n this.handler?.({ type: 'clipboard', 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 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, and it can\n * 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","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} from 'gesso-core';\nimport type { AudioAction, AudioRequest, AudioSample } from '../AudioService';\nimport type { ColorScheme } from '../colorScheme';\nimport type { 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 | { 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 | { type: 'keyDown'; key: string; modifiers: UiKeyModifiers; at?: number }\n | { type: 'keyUp'; key: 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 | { type: 'paste'; text: 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 /**\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 /**\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 * 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 | { 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 * 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 | { 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 = 'message' | 'uncaught' | 'renderer' | 'channel' | 'listener';\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 at: number;\n inputLatencyMs: 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 /** Put text on the clipboard (ShellService.copyText). */\n | { type: 'clipboard'; text: string }\n /** Open a URL in a new tab (ShellService.openUrl). */\n | { type: 'openUrl'; url: string }\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 /** 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 * 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 { 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]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);\n const normalized = `/${pathSegments(path).join('/')}`;\n return pairs.length === 0 ? normalized : `${normalized}?${pairs.join('&')}`;\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 || !match.chain.includes(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 && match.chain.includes(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 this.url.value = settled;\n this.match.value = match;\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","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 { 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 */\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 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 const focusChange = manager.onFocusChange(node => {\n this.focused.value = node;\n });\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 /** Gives the node keyboard focus. Non-focusable nodes are ignored. */\n focus(node: UiNode): void {\n this.run(manager => manager.focus(node));\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 * 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.\n */\n releaseTrap(): void {\n this.run(manager => manager.popScope());\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.trapped.value = manager.trapped;\n }\n}\n","import {\n DefaultImageResolver,\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 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 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 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 { EMPTY, type Observable } from 'rxjs';\n\nimport {\n AnimationDriver,\n UiSpring,\n createTween,\n easings,\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} 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\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\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 }\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);\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 =\n typeof options.spring === 'string' || options.spring === undefined\n ? this.motionVocabulary.springs[options.spring ?? 'snappy']\n : 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 if (driver === null) {\n animation.snap();\n return animation.values;\n }\n return driver.start(animation);\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 typeof duration === 'number' ? duration : this.motionVocabulary.durations[duration];\n }\n\n private resolveEasing(easing: UiEasingChoice): UiEasing {\n if (typeof easing === 'function') {\n return easing;\n }\n return this.motionVocabulary.easings[easing] ?? easings.standard;\n }\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 /** 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","/**\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 { 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 /** The container's clamp, read fresh because content grows. */\n private readonly limitOf: (node: UiNode, axis: Axis) => 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 limit = this.limitOf(node, axis);\n const target = clamp((pending ?? from) + delta, 0, limit);\n if (pending !== undefined && target === pending) {\n return;\n }\n this.animations.spring(cell, target, { spring: SCROLL_SPRING });\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 });\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 UiNodeReport,\n type UiBeneathReport,\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 UiGraph,\n UiGraphBuilder,\n describeOverrides,\n formatExplanation,\n isComponentLikeElement,\n linkOf,\n textRunOfRecordId,\n type UiEnvironment,\n isObservable,\n type UiElement,\n Stack,\n scrollbarThumb,\n UiVirtualWindow,\n VIRTUAL_INDEX_PROP,\n VIRTUAL_LEAD_PROP,\n VIRTUAL_WINDOW_PROP,\n type VirtualItemMeasure,\n DirtyFlags,\n propertyEffects,\n setPerformanceMarks,\n resolveCursor,\n type UiNode,\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 diffSemantics,\n LayoutNotifier,\n type UiSemanticsAction,\n type UiSemanticsBox,\n type UiSemanticsMap,\n type UiSemanticsPatch,\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 RendererBackend,\n type UiRenderer,\n type WebGPUCanvasHost,\n UiScheduler,\n UiTimerFrameClock,\n type UiFrame,\n type UiFrameClockFactory\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 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 { FocusService } from './FocusService';\nimport { MediaService, type MediaOptions } from './MediaService';\nimport { FontService, type FontFamilyDeclaration } from './FontService';\nimport { AnimationService } from './AnimationService';\nimport { FrameService } from './FrameService';\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 `createApp(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 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 */\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 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 private constraints: Constraints;\n private pixelRatio: number;\n private lastFrameMs = 0;\n private frameListener: ((metrics: FrameMetrics) => void) | null = null;\n private rendererErrorListener: ((message: 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 private semantics: UiSemanticsMap = new Map();\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 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 /** 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 private audioListener: ((request: AudioRequest) => void) | null = null;\n private caretTimer: ReturnType<typeof setTimeout> | null = null;\n private scrollbarTimer: 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 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 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\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 => {\n this.focusManager.focus(node);\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 visible 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.\n box: node => (this.engine.recordFor(node) === undefined ? null : this.engine.visibleBox(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 onLayout: (node, listener) => this.layoutNotifier.add(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.shellListener?.(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 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 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 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 // 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 0;\n }\n return axis === 'scrollY'\n ? Math.max(0, record.contentHeight - record.height)\n : Math.max(0, record.contentWidth - record.width);\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 router.setHistory({\n push: url => this.shellListener?.({ type: 'history', action: 'push', url }),\n replace: url => this.shellListener?.({ type: 'history', action: 'replace', url }),\n back: () => this.shellListener?.({ type: 'history', action: 'back' }),\n forward: () => this.shellListener?.({ type: 'history', action: 'forward' })\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?.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\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(FocusService).setManager(this.input.focus);\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.\n this.input.focus.onFocusChange((node, source) => {\n if (node !== null && source !== 'pointer') {\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(() => this.scheduler.notifyDirty());\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 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 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 this.engine.layout(this.root, this.constraints);\n // Resizing the backing store clears whatever was drawn, and layout\n // marks nothing dirty on its own, so without this the canvas stays\n // blank until some unrelated change happens to schedule a frame.\n this.graph.markDirty(this.root, DirtyFlags.Paint);\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 * 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 /** `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 onShellRequest(listener: ((request: ShellRequest) => void) | null): void {\n this.shellListener = listener;\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 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 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 /**\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 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 /** 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 explanation: formatExplanation(this.engine.explain(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 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 owners.push({ name: getComponentMetadata(host.component).tag, anchorId: current.id });\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 /**\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 scrollIntoView(node: UiNode, padding = 8): void {\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. 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.scrollbarTimer !== null) {\n clearTimeout(this.scrollbarTimer);\n this.scrollbarTimer = 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.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.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 output = this.resolver.resolve(definition, `app:component:${depth}`);\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 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 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 wheel: new UiWheelController(hitTester, this.dispatcher, scrollSink, () => root),\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.\n activation: {\n clickAt: node => {\n const box = this.engine.visibleBox(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.semantics.values()].map(node => ({ op: 'add', node }) as const),\n boxes: this.collectSemanticsBoxes(),\n focused: this.focusManager.focusedNode?.id ?? null\n });\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.semantics = buildSemanticsTree(this.layoutRoot());\n this.semanticsStale = false;\n }\n return this.semantics;\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 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 if (action.action === 'focus') {\n this.focusManager.focus(node);\n return;\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.\n this.focusManager.focus(node);\n this.input.editing.replaceText(action.value ?? '');\n return;\n }\n this.focusManager.focus(node);\n const box = this.engine.visibleBox(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 }\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(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 const next = buildSemanticsTree(this.layoutRoot());\n patches = diffSemantics(this.semantics, next);\n this.semantics = next;\n this.semanticsStale = false;\n }\n }\n if (listener === null) {\n return;\n }\n const boxes = moved || patches.length > 0 ? this.collectSemanticsBoxes() : 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 /**\n * The mirrored nodes whose box differs from the one last sent.\n *\n * Bounded by the semantics tree, which is bounded by the *mounted*\n * nodes — so a 100k-row list costs the fifteen rows it has mounted,\n * the same bound the semantics tree walk has.\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 private collectSemanticsBoxes(): UiSemanticsBox[] {\n const changed: UiSemanticsBox[] = [];\n for (const id of this.semantics.keys()) {\n const node = this.graph.getNode(id);\n if (node === undefined || this.engine.recordFor(node) === undefined) {\n continue;\n }\n const box = this.engine.visibleBox(node);\n const last = this.semanticsBoxes.get(id);\n if (last !== undefined && boxesEqual(last, box)) {\n continue;\n }\n this.semanticsBoxes.set(id, box);\n changed.push({ id, box });\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 /** 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 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: record.scrollX,\n scrollY: record.scrollY,\n maxScrollX: Math.max(0, record.contentWidth - record.width),\n maxScrollY: Math.max(0, record.contentHeight - record.height),\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, record.scrollX);\n }\n if (dy !== 0) {\n this.smoothScroller.scrollBy(node, 'scrollY', dy, record.scrollY);\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', record.scrollX + dx);\n }\n if (dy !== 0) {\n node.setProperty('scrollY', record.scrollY + 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 /**\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.phaseTimings = emptyPhaseTimings();\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 // 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 }\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 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 scroll = typeof scrollProp === 'number' ? scrollProp : column ? rec.scrollY : rec.scrollX;\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 node.setProperty(axis, scroll + result.scrollAdjust);\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, result.scrollAdjust);\n }\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 private handleFrame(frame: 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 laidOut = frameNeedsLayout(frame);\n this.phaseTimings.layout = this.timePhase(\n () => laidOut,\n () => this.engine.layoutForFrame(frame, this.constraints, root)\n );\n if (laidOut) {\n this.inspector.recordLayout(started);\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. Nothing\n // listening means nothing walked.\n if (!this.layoutNotifier.isEmpty()) {\n this.layoutNotifier.notify(node => {\n const record = this.engine.recordFor(node);\n return {\n box: this.engine.visibleBox(node),\n scrollX: record?.scrollX ?? 0,\n scrollY: record?.scrollY ?? 0\n };\n });\n }\n\n // After the layout listeners rather than before, because\n // `autoFocus` is one of them: it takes focus on the first layout\n // of the node it is attached to, and every node in a subtree a\n // reload replaced is having its first layout on this frame.\n if (this.restoringFocus && laidOut) {\n this.restoringFocus = false;\n this.restoreFocusAfterReload();\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(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 falls back to Canvas2D and repaints.\n if (this.renderer.backend === 'webgpu' && (this.renderer as WebGPURenderer).isLost) {\n this.fallBackToCanvas2D(this.renderer);\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 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 })\n );\n if (overlay !== null) {\n if (laidOut) {\n this.sendInspection();\n }\n this.scheduleInspectorRepaint(overlay.nextChange);\n }\n this.sendDevtoolsUpdates(frame);\n\n const finished = now();\n const elapsed = finished - started;\n this.lastFrameMs = elapsed;\n this.scheduleScrollbarFade(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 measured: this.engine.stats.measured,\n relayoutRoots: this.engine.stats.fullLayout ? 0 : this.engine.stats.relayoutRoots,\n at: finished,\n inputLatencyMs: this.inputLatency.take(epochAt(finished)),\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 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. */\n private sendCursor(): void {\n const cursor = 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 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 /** 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\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.multiline === b.multiline &&\n a.composing === b.composing &&\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\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(DirtyFlags.Layout | DirtyFlags.Children | DirtyFlags.SubtreeLayout | DirtyFlags.Transform);\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. */\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\nfunction frameNeedsSemantics(frame: UiFrame): boolean {\n return frame.anyFlags(DirtyFlags.Semantics | DirtyFlags.Children);\n}\n","import type { AudioAction, AudioMetadata, AudioRequest, AudioSample, AudioStatus } from './AudioService';\nimport { epochNow } from './worker/RenderWorkerProtocol';\n\n/**\n * The part of `HTMLAudioElement` the sink drives. Narrow so a spec can\n * stand one in with a plain `EventTarget`.\n */\nexport interface AudioElementLike extends EventTarget {\n src: string;\n currentTime: number;\n volume: number;\n preload: string;\n readonly duration: number;\n readonly paused: boolean;\n readonly ended: boolean;\n readonly buffered: { readonly length: number; start(index: number): number; end(index: number): number };\n readonly error: { readonly code: number; readonly message?: string } | null;\n play(): Promise<void>;\n pause(): void;\n load(): void;\n}\n\n/** The part of the Media Session API the sink uses; absent on browsers without it. */\nexport interface MediaSessionLike {\n metadata: unknown;\n playbackState: 'none' | 'paused' | 'playing';\n setActionHandler(action: string, handler: ((details: { seekTime?: number }) => void) | null): void;\n}\n\nexport interface AudioSinkOutput {\n /** The element's state, whenever it changes and about once a second while playing. */\n sample(sample: AudioSample): void;\n /** What the platform's media controls asked for. */\n action(action: AudioAction): void;\n}\n\nexport interface AudioSinkOptions {\n /** Injectable for specs; `new Audio()` by default. */\n readonly createElement?: () => AudioElementLike;\n /** Injectable for specs; `navigator.mediaSession` by default, when the browser has one. */\n readonly mediaSession?: MediaSessionLike | null;\n /** Milliseconds between samples while playing. */\n readonly sampleEveryMs?: number;\n}\n\nconst DEFAULT_SAMPLE_EVERY_MS = 1000;\n\n/** The element events that change what a sample would say. */\nconst SAMPLED_EVENTS = [\n 'loadstart',\n 'loadedmetadata',\n 'durationchange',\n 'canplay',\n 'playing',\n 'play',\n 'pause',\n 'seeking',\n 'seeked',\n 'waiting',\n 'stalled',\n 'ended',\n 'error',\n 'progress',\n 'emptied'\n] as const;\n\n/**\n * The shell's audio element, behind the `AudioRequest` messages.\n *\n * This is the one piece of sound that genuinely cannot run anywhere\n * else: an element that plays needs a window. Everything that could be\n * a decision is not here. The sink loads what it is told, plays and\n * pauses when told, and reports what the element is doing; which track\n * that is, and what comes next, is the render thread's `AudioService`\n * and the application above it. The thread model holds the shell to\n * \"what genuinely cannot run anywhere else\", and this is the audio\n * equivalent of forwarding a pointer event.\n *\n * Media Session is here for the same reason: `navigator.mediaSession`\n * is main-thread only, and it is what puts the title on the OS overlay\n * and makes the hardware keys work. Play and pause from those keys act\n * on the element directly and come back as samples; next and previous\n * are forwarded as actions, because an element has no idea what a\n * playlist is.\n */\nexport class AudioSink {\n /**\n * The element that is playing, and a spare that may be holding the\n * next track.\n *\n * Two rather than one because a gapless change needs the next track\n * already buffered when the current one ends, and an element cannot\n * buffer a second source. `preload` fills the spare; a `load` of the\n * source the spare is holding swaps them, so the gap between tracks\n * is the browser's and not this app's.\n *\n * Not readonly: swapping is the whole mechanism.\n */\n private element: AudioElementLike;\n private spare: AudioElementLike;\n /** What the spare has been asked to hold, or '' when it holds nothing. */\n private prepared = '';\n private readonly session: MediaSessionLike | null;\n private readonly sampleEveryMs: number;\n private timer: ReturnType<typeof setInterval> | null = null;\n private waiting = false;\n /** Why the last play() was refused, reported on the next sample and then forgotten. */\n private refused: string | undefined;\n private readonly onEvent = (event: Event): void => this.handleEvent(event.type);\n\n constructor(\n private readonly out: AudioSinkOutput,\n options: AudioSinkOptions = {}\n ) {\n const create = options.createElement ?? (() => new Audio() as unknown as AudioElementLike);\n this.element = create();\n this.element.preload = 'auto';\n this.spare = create();\n this.spare.preload = 'auto';\n this.session =\n options.mediaSession !== undefined\n ? options.mediaSession\n : typeof navigator !== 'undefined' && 'mediaSession' in navigator\n ? (navigator.mediaSession as unknown as MediaSessionLike)\n : null;\n this.sampleEveryMs = options.sampleEveryMs ?? DEFAULT_SAMPLE_EVERY_MS;\n for (const type of SAMPLED_EVENTS) {\n this.element.addEventListener(type, this.onEvent);\n }\n this.bindSession();\n }\n\n handle(request: AudioRequest): void {\n switch (request.type) {\n case 'load':\n // The spare is holding exactly this: swap to it rather than\n // fetching the same bytes twice, which is what makes the change\n // gapless.\n if (this.prepared !== '' && this.prepared === request.src) {\n this.swap();\n } else {\n this.element.src = request.src;\n this.element.load();\n }\n this.waiting = true;\n if (request.autoplay) {\n this.play();\n } else {\n this.emit();\n }\n return;\n case 'preload':\n this.preload(request.src);\n return;\n case 'play':\n this.play();\n return;\n case 'pause':\n this.element.pause();\n return;\n case 'seek':\n this.element.currentTime = request.seconds;\n return;\n case 'volume':\n this.element.volume = request.level;\n return;\n case 'metadata':\n this.setMetadata(request.metadata);\n return;\n }\n }\n\n /**\n * Asks the spare element to start buffering a source.\n *\n * Idempotent, because the application worker resolves the next url\n * whenever the queue moves and will often ask for the same one twice.\n * An empty source clears the spare, which is what happens when the\n * queue is at its end.\n */\n private preload(src: string): void {\n if (src === this.prepared) {\n return;\n }\n this.prepared = src;\n if (src === '') {\n this.spare.pause();\n this.spare.src = '';\n return;\n }\n // The volume has to travel, or a swap is a jump in loudness.\n this.spare.volume = this.element.volume;\n this.spare.src = src;\n this.spare.load();\n }\n\n /**\n * Makes the spare the element that plays.\n *\n * The listeners move with the role rather than sitting on both, so a\n * spare quietly buffering never emits a sample and the application\n * hears one timeline. The element that steps aside is emptied so it\n * holds no bytes while it waits to be the spare again.\n */\n private swap(): void {\n for (const type of SAMPLED_EVENTS) {\n this.element.removeEventListener(type, this.onEvent);\n }\n const previous = this.element;\n this.element = this.spare;\n this.spare = previous;\n this.prepared = '';\n this.spare.pause();\n this.spare.src = '';\n for (const type of SAMPLED_EVENTS) {\n this.element.addEventListener(type, this.onEvent);\n }\n }\n\n dispose(): void {\n this.stopTimer();\n for (const type of SAMPLED_EVENTS) {\n this.element.removeEventListener(type, this.onEvent);\n }\n this.element.pause();\n this.element.src = '';\n this.spare.pause();\n this.spare.src = '';\n if (this.session !== null) {\n for (const action of ['play', 'pause', 'previoustrack', 'nexttrack', 'seekto']) {\n try {\n this.session.setActionHandler(action, null);\n } catch {\n // A browser that does not know the action throws; nothing to undo.\n }\n }\n this.session.metadata = null;\n this.session.playbackState = 'none';\n }\n }\n\n private play(): void {\n this.refused = undefined;\n const started = this.element.play();\n // A browser may refuse a play that no gesture authorised; the\n // element stays paused and the sample says why, so the screen can\n // show a paused player rather than one that claims to be playing.\n started?.catch((error: unknown) => {\n this.waiting = false;\n this.refused = error instanceof Error ? error.name : String(error);\n this.emit();\n });\n }\n\n private handleEvent(type: string): void {\n switch (type) {\n case 'loadstart':\n case 'waiting':\n case 'stalled':\n this.waiting = true;\n break;\n case 'canplay':\n case 'playing':\n case 'pause':\n case 'ended':\n case 'error':\n case 'emptied':\n this.waiting = false;\n break;\n default:\n break;\n }\n this.emit();\n }\n\n private status(): AudioStatus {\n const element = this.element;\n if (element.error !== null) {\n return 'error';\n }\n if (element.src === '' || element.src === undefined) {\n return 'idle';\n }\n if (element.ended) {\n return 'ended';\n }\n if (element.paused) {\n return this.waiting && this.refused === undefined ? 'loading' : 'paused';\n }\n return this.waiting ? 'loading' : 'playing';\n }\n\n private emit(): void {\n const element = this.element;\n const status = this.status();\n const error =\n status === 'error' ? (element.error?.message ?? `media error ${element.error?.code ?? ''}`.trim()) : this.refused;\n this.out.sample({\n status,\n position: element.currentTime,\n duration: element.duration,\n buffered: bufferedEnd(element),\n at: epochNow(),\n ...(error === undefined ? {} : { error })\n });\n if (this.session !== null) {\n this.session.playbackState = status === 'playing' ? 'playing' : status === 'idle' ? 'none' : 'paused';\n }\n if (status === 'playing') {\n this.startTimer();\n } else {\n this.stopTimer();\n }\n }\n\n private startTimer(): void {\n if (this.timer !== null) {\n return;\n }\n this.timer = setInterval(() => this.emit(), this.sampleEveryMs);\n }\n\n private stopTimer(): void {\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n private setMetadata(metadata: AudioMetadata | null): void {\n if (this.session === null) {\n return;\n }\n if (metadata === null || typeof MediaMetadata === 'undefined') {\n this.session.metadata = null;\n return;\n }\n this.session.metadata = new MediaMetadata({\n title: metadata.title,\n artist: metadata.artist,\n album: metadata.album ?? '',\n artwork: metadata.artwork === undefined ? [] : [{ src: metadata.artwork }]\n });\n }\n\n private bindSession(): void {\n const session = this.session;\n if (session === null) {\n return;\n }\n const bind = (action: string, handler: (details: { seekTime?: number }) => void): void => {\n try {\n session.setActionHandler(action, handler);\n } catch {\n // Not every browser knows every action.\n }\n };\n bind('play', () => {\n this.play();\n this.out.action('play');\n });\n bind('pause', () => {\n this.element.pause();\n this.out.action('pause');\n });\n bind('previoustrack', () => this.out.action('previous'));\n bind('nexttrack', () => this.out.action('next'));\n bind('seekto', details => {\n if (details.seekTime !== undefined) {\n this.element.currentTime = details.seekTime;\n }\n });\n }\n}\n\n/** The end of the buffered range the play head is in, or the head itself when nothing is buffered there. */\nfunction bufferedEnd(element: AudioElementLike): number {\n const ranges = element.buffered;\n const at = element.currentTime;\n for (let index = 0; index < ranges.length; index++) {\n if (ranges.start(index) <= at && at <= ranges.end(index)) {\n return ranges.end(index);\n }\n }\n return at;\n}\n","import type { EditingState, UiSemanticsRecord } from 'gesso-core';\n\n/**\n * Where the proxy sends what it hears. `WorkerApp` posts each call to\n * the render worker; `GessoApp` calls the runtime's editing controller\n * directly.\n */\nexport interface EditingProxySink {\n beforeInput(inputType: string, data: string | null): void;\n compositionStart(): void;\n compositionUpdate(text: string, caret: number): void;\n compositionEnd(text: string): void;\n paste(text: string): void;\n /** The proxy lost focus to something outside the app. */\n blur(): void;\n /**\n * Key events reach the proxy's element, not the canvas, while it has\n * focus. A shell that listens on the canvas forwards them from here;\n * one that listens on `window` sees them anyway and leaves these out.\n */\n keyDown?(event: KeyboardEvent): void;\n keyUp?(event: KeyboardEvent): void;\n}\n\n/** Composition-owned input types: the composition events carry these. */\nconst COMPOSITION_INPUT_TYPES = new Set(['insertCompositionText', 'insertFromComposition', 'deleteCompositionText']);\n/**\n * Input types the keys already carry. Backspace, Delete and Enter reach\n * the runtime as key presses and are applied there; the textarea fires\n * these too, but only when its mirror happens to have something to\n * delete at the caret, so they cannot be the path — and forwarding them\n * as well would apply each edit twice. Undo and redo likewise: the\n * browser has no history of ours to replay.\n */\nconst KEY_INPUT_TYPES = new Set([\n 'deleteContentBackward',\n 'deleteContentForward',\n 'deleteWordBackward',\n 'deleteWordForward',\n 'deleteSoftLineBackward',\n 'deleteSoftLineForward',\n 'deleteHardLineBackward',\n 'deleteHardLineForward',\n 'insertLineBreak',\n 'insertParagraph',\n 'historyUndo',\n 'historyRedo'\n]);\n\n/**\n * The main thread's half of text editing: a hidden `<textarea>`.\n *\n * A canvas cannot receive text. Keyboard events carry keys, not\n * characters: dead keys, the OS keyboard layout and above all an IME\n * resolve to text only inside an editable DOM element, through\n * `beforeinput` and the `composition*` events. So while the runtime\n * reports a focused editable, this element takes DOM focus and:\n *\n * - forwards every `beforeinput` as an edit intent and cancels it, so\n * the element's own content never diverges from the runtime's;\n * - empties itself when a composition starts, so the composition\n * text is the whole value and the IME candidate window opens at the\n * element's top-left — which is positioned at the runtime's caret;\n * - reports the composition through `input` events while it is open\n * and its result at `compositionend`;\n * - handles copy, cut and paste with the runtime's text, since the\n * clipboard is only reachable from a user gesture on this thread;\n * - mirrors the runtime's text and selection between edits, so native\n * copy, IME context and, later, assistive technology see the real\n * text.\n *\n * Nothing here reads the textarea's content as truth except the\n * composition string, which is the one thing only the browser knows.\n */\nexport class EditingProxy {\n private readonly textarea: HTMLTextAreaElement;\n private readonly doc: Document;\n private readonly view: Window | null;\n private state: EditingState | null = null;\n private composing = false;\n private disposed = false;\n /** Set while `raiseKeyboard` drops focus on purpose; see there. */\n private refocusing = false;\n private readonly detach: () => void;\n\n constructor(\n private readonly canvas: HTMLCanvasElement,\n private readonly sink: EditingProxySink\n ) {\n this.doc = canvas.ownerDocument;\n this.view = this.doc.defaultView;\n const textarea = this.doc.createElement('textarea');\n this.textarea = textarea;\n textarea.setAttribute('aria-hidden', 'true');\n textarea.setAttribute('autocomplete', 'off');\n textarea.setAttribute('autocorrect', 'off');\n textarea.setAttribute('autocapitalize', 'off');\n textarea.setAttribute('spellcheck', 'false');\n textarea.setAttribute('wrap', 'off');\n textarea.tabIndex = -1;\n Object.assign(textarea.style, {\n position: 'fixed',\n left: '0px',\n top: '0px',\n width: '1px',\n height: '1em',\n margin: '0',\n padding: '0',\n border: '0',\n outline: 'none',\n opacity: '0',\n overflow: 'hidden',\n resize: 'none',\n whiteSpace: 'pre',\n pointerEvents: 'none',\n zIndex: '2147483647'\n } as Partial<CSSStyleDeclaration>);\n this.doc.body.appendChild(textarea);\n this.detach = this.listen();\n }\n\n /** True while an editable in the runtime has focus. */\n get active(): boolean {\n return this.state !== null;\n }\n\n /** The hidden element, for tests. */\n get element(): HTMLTextAreaElement {\n return this.textarea;\n }\n\n /**\n * The runtime's editing state changed. Null means no editable has\n * focus: the element gives focus back to the canvas so keys keep\n * reaching the app.\n */\n update(state: EditingState | null): void {\n if (this.disposed) {\n return;\n }\n const wasActive = this.state !== null;\n this.state = state;\n if (state === null) {\n if (wasActive) {\n if (this.doc.activeElement === this.textarea) {\n this.textarea.blur();\n this.canvas.focus({ preventScroll: true });\n }\n }\n return;\n }\n this.position(state);\n this.mirror(state);\n // What the phone's Return key should say. A single-line field has\n // nothing to insert a newline into, and a keyboard offering one is\n // offering a key that does nothing.\n this.textarea.setAttribute('enterkeyhint', state.multiline ? 'enter' : 'done');\n if (!wasActive || this.doc.activeElement !== this.textarea) {\n const active = this.doc.activeElement;\n // Take focus only from the canvas or from nothing: an editable\n // gaining focus in the app must not steal it from page chrome.\n if (active === null || active === this.doc.body || active === this.canvas || active === this.textarea) {\n this.textarea.focus({ preventScroll: true });\n }\n }\n }\n\n /**\n * Takes DOM focus for the focused editable, unconditionally.\n *\n * `update()` is careful about where it takes focus from — page\n * chrome outside the app must keep it — but the accessibility\n * mirror calls this only when the runtime has just said an editable\n * holds focus, and the element it is taking focus from is the\n * mirror's own. Without it, moving focus from a button to a field\n * would leave DOM focus on the button's mirrored element and the\n * IME with nothing to compose into.\n */\n focus(): void {\n if (this.disposed || this.state === null || this.doc.activeElement === this.textarea) {\n return;\n }\n this.textarea.focus({ preventScroll: true });\n }\n\n /**\n * Re-takes DOM focus from inside a user gesture, so a phone opens its\n * keyboard.\n *\n * A phone raises the keyboard for a focus that a person's gesture\n * caused, and for no other. In the worker configuration the runtime's\n * answer to a press — *this* node is editable, it has focus now —\n * arrives as a message a frame later, and `update()` focuses the\n * element from inside that message handler. The task the gesture\n * belonged to is over by then, so iOS moves focus and leaves the\n * keyboard down: a field the person is demonstrably typing into, with\n * nothing to type on.\n *\n * The shell calls this from the `pointerup` of the press that opened\n * the editable, which is a gesture task, and only for a press that\n * turned editing on — re-asserting focus while the keyboard is\n * already up is what makes it flicker.\n *\n * Focus has to be dropped and retaken rather than simply retaken:\n * focusing an element that is already focused does nothing at all,\n * and it already is focused — that is the whole problem. The blur is\n * ours, so the listener that would report it to the runtime as the\n * person leaving the field is suppressed for its duration.\n *\n * Nothing calls it in the single-threaded configuration and nothing\n * should: there, `update()` runs inside the pointerdown's own call\n * stack and the browser sees the focus as the gesture's.\n */\n raiseKeyboard(): void {\n if (this.disposed || this.state === null) {\n return;\n }\n const textarea = this.textarea;\n if (this.doc.activeElement !== textarea) {\n textarea.focus({ preventScroll: true });\n return;\n }\n this.refocusing = true;\n try {\n textarea.blur();\n textarea.focus({ preventScroll: true });\n } finally {\n this.refocusing = false;\n }\n }\n\n /**\n * Describes the focused editable on this element, for the\n * accessibility mirror (`SemanticsMirror`).\n *\n * The textarea is `aria-hidden` by default because an unlabelled\n * text box floating over an application is noise; when the mirror\n * hands it a record it stops being unlabelled, and becomes the one\n * element that is both what the assistive technology reads and what\n * the person is really typing into. That the two cannot disagree is\n * the reason the field's semantics live here rather than on a second\n * element beside it.\n */\n describe(record: UiSemanticsRecord | null): void {\n if (this.disposed) {\n return;\n }\n const textarea = this.textarea;\n if (record === null) {\n textarea.setAttribute('aria-hidden', 'true');\n for (const attribute of [\n 'role',\n 'aria-label',\n 'aria-description',\n 'aria-required',\n 'aria-invalid',\n 'aria-readonly',\n 'aria-disabled'\n ]) {\n textarea.removeAttribute(attribute);\n }\n return;\n }\n textarea.removeAttribute('aria-hidden');\n setOrClear(textarea, 'role', record.role);\n setOrClear(textarea, 'aria-label', record.label);\n setOrClear(textarea, 'aria-description', record.description);\n const states = new Set(record.states ?? []);\n setOrClear(textarea, 'aria-required', states.has('required') ? 'true' : undefined);\n setOrClear(textarea, 'aria-invalid', states.has('invalid') ? 'true' : undefined);\n setOrClear(textarea, 'aria-readonly', states.has('readonly') ? 'true' : undefined);\n setOrClear(textarea, 'aria-disabled', record.disabled === true ? 'true' : undefined);\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.detach();\n this.state = null;\n this.textarea.remove();\n }\n\n /** Puts the element where the caret is, so the IME window opens there. */\n private position(state: EditingState): void {\n const rect = this.canvas.getBoundingClientRect();\n const height = Math.max(1, state.caret.height);\n const left = rect.left + Math.min(Math.max(0, state.caret.x), Math.max(0, rect.width - 1));\n const top = rect.top + Math.min(Math.max(0, state.caret.y), Math.max(0, rect.height - height));\n const style = this.textarea.style;\n style.left = `${left}px`;\n style.top = `${top}px`;\n style.height = `${height}px`;\n style.fontSize = `${Math.max(1, Math.round(height * 0.75))}px`;\n style.lineHeight = `${height}px`;\n }\n\n /** Mirrors the runtime's text and selection, except while the IME owns the element. */\n private mirror(state: EditingState): void {\n if (this.composing) {\n return;\n }\n const textarea = this.textarea;\n if (textarea.value !== state.text) {\n textarea.value = state.text;\n }\n if (textarea.selectionStart !== state.selectionStart || textarea.selectionEnd !== state.selectionEnd) {\n try {\n textarea.setSelectionRange(state.selectionStart, state.selectionEnd);\n } catch {\n // Some engines throw for an offset past the value; the next\n // state will be consistent.\n }\n }\n }\n\n private listen(): () => void {\n const textarea = this.textarea;\n\n const onBeforeInput = (event: Event): void => {\n const input = event as InputEvent;\n const type = input.inputType;\n if (COMPOSITION_INPUT_TYPES.has(type) || this.composing) {\n // Not cancelable, and carried by the composition events.\n return;\n }\n event.preventDefault();\n if (KEY_INPUT_TYPES.has(type) || type === 'insertFromPaste') {\n // Deletes, newlines and undo come from the keys; paste from the\n // paste event.\n return;\n }\n this.sink.beforeInput(type, input.data ?? null);\n };\n\n const onCompositionStart = (): void => {\n this.composing = true;\n // Empty, so the composition text is the whole value and the\n // element's caret — where the IME opens — is at its top-left.\n textarea.value = '';\n this.sink.compositionStart();\n };\n\n const onInput = (): void => {\n if (!this.composing) {\n return;\n }\n const text = textarea.value;\n const caret = textarea.selectionStart ?? text.length;\n this.sink.compositionUpdate(text, Math.max(0, Math.min(caret, text.length)));\n };\n\n const onCompositionEnd = (event: Event): void => {\n if (!this.composing) {\n return;\n }\n this.composing = false;\n const text = (event as CompositionEvent).data ?? textarea.value;\n this.sink.compositionEnd(text);\n if (this.state !== null) {\n this.mirror(this.state);\n }\n };\n\n const onPaste = (event: Event): void => {\n const clipboard = (event as ClipboardEvent).clipboardData;\n event.preventDefault();\n const text = clipboard?.getData('text/plain') ?? '';\n if (text.length > 0) {\n this.sink.paste(text);\n }\n };\n\n const onCopy = (event: Event): void => {\n const clipboard = (event as ClipboardEvent).clipboardData;\n const state = this.state;\n if (clipboard === null || state === null) {\n return;\n }\n event.preventDefault();\n clipboard.setData('text/plain', state.text.slice(state.selectionStart, state.selectionEnd));\n };\n\n const onCut = (event: Event): void => {\n const state = this.state;\n onCopy(event);\n if (state !== null && state.selectionEnd > state.selectionStart) {\n this.sink.beforeInput('deleteByCut', null);\n }\n };\n\n const onKeyDown = (event: Event): void => {\n this.sink.keyDown?.(event as KeyboardEvent);\n };\n const onKeyUp = (event: Event): void => {\n this.sink.keyUp?.(event as KeyboardEvent);\n };\n\n const onBlur = (event: Event): void => {\n if (this.refocusing) {\n // Our own blur, half of retaking focus inside a gesture. The\n // person has not left anything.\n return;\n }\n // Focus moved within the page (not the window losing focus, which\n // also blurs) to something other than the canvas: the app's\n // editable should stop showing a caret.\n const related = (event as FocusEvent).relatedTarget;\n if (this.state !== null && this.doc.hasFocus() && related !== this.canvas) {\n this.sink.blur();\n }\n };\n\n const onReposition = (): void => {\n if (this.state !== null) {\n this.position(this.state);\n }\n };\n\n textarea.addEventListener('beforeinput', onBeforeInput);\n textarea.addEventListener('compositionstart', onCompositionStart);\n textarea.addEventListener('input', onInput);\n textarea.addEventListener('compositionend', onCompositionEnd);\n textarea.addEventListener('paste', onPaste);\n textarea.addEventListener('copy', onCopy);\n textarea.addEventListener('cut', onCut);\n textarea.addEventListener('keydown', onKeyDown);\n textarea.addEventListener('keyup', onKeyUp);\n textarea.addEventListener('blur', onBlur);\n this.view?.addEventListener('scroll', onReposition, { capture: true, passive: true });\n this.view?.addEventListener('resize', onReposition);\n\n return () => {\n textarea.removeEventListener('beforeinput', onBeforeInput);\n textarea.removeEventListener('compositionstart', onCompositionStart);\n textarea.removeEventListener('input', onInput);\n textarea.removeEventListener('compositionend', onCompositionEnd);\n textarea.removeEventListener('paste', onPaste);\n textarea.removeEventListener('copy', onCopy);\n textarea.removeEventListener('cut', onCut);\n textarea.removeEventListener('keydown', onKeyDown);\n textarea.removeEventListener('keyup', onKeyUp);\n textarea.removeEventListener('blur', onBlur);\n this.view?.removeEventListener('scroll', onReposition, { capture: true });\n this.view?.removeEventListener('resize', onReposition);\n };\n }\n}\n\n/**\n * Writes text to the system clipboard from the main thread. The async\n * API needs a secure context and, in some browsers, a recent user\n * gesture; the `execCommand` fallback covers the rest.\n */\nexport function writeClipboard(text: string, doc: Document = document): void {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard !== undefined && typeof clipboard.writeText === 'function') {\n clipboard.writeText(text).catch(() => copyWithExecCommand(text, doc));\n return;\n }\n copyWithExecCommand(text, doc);\n}\n\nfunction copyWithExecCommand(text: string, doc: Document): void {\n const previous = doc.activeElement as HTMLElement | null;\n const scratch = doc.createElement('textarea');\n scratch.value = text;\n scratch.style.position = 'fixed';\n scratch.style.opacity = '0';\n doc.body.appendChild(scratch);\n scratch.select();\n try {\n doc.execCommand('copy');\n } finally {\n scratch.remove();\n previous?.focus?.({ preventScroll: true });\n }\n}\n\nfunction setOrClear(element: HTMLElement, attribute: string, value: string | undefined): void {\n if (value === undefined) {\n element.removeAttribute(attribute);\n } else {\n element.setAttribute(attribute, value);\n }\n}\n","import type { UiSemanticsAction, UiSemanticsRecord, UiSemanticsUpdate } from 'gesso-core';\n\n/**\n * Where the mirror sends what it hears.\n *\n * `WorkerApp` posts each call to the render worker; `GessoApp` calls\n * the runtime directly. The same split as `EditingProxySink`, and for\n * the same reason: nothing here knows whether the runtime is a\n * function call or a thread away.\n */\nexport interface SemanticsMirrorSink {\n /** An assistive technology pressed, focused or set the value of a node. */\n action(action: UiSemanticsAction): void;\n /**\n * Key events reach a mirrored element, not the canvas, whenever the\n * app has focus — because the element holding DOM focus *is* the\n * mirror's. A shell that listens on the canvas forwards them from\n * here; one that listens on `window` sees them anyway and leaves\n * these out.\n */\n keyDown?(event: KeyboardEvent): void;\n keyUp?(event: KeyboardEvent): void;\n}\n\n/**\n * The editing proxy, as the mirror needs it.\n *\n * A focused editable is the one node the mirror must not take DOM\n * focus for: the hidden textarea has it, because that is the only way\n * an IME can compose (F2). So the mirror hands that element the\n * record instead, and the assistive technology reads the field it is\n * really typing into.\n */\nexport interface EditingMirrorTarget {\n /** True while the proxy holds DOM focus for a focused editable. */\n readonly active: boolean;\n /** Describes the focused editable on the proxy's element, or clears it. */\n describe(record: UiSemanticsRecord | null): void;\n /** Takes DOM focus back for the focused editable. */\n focus(): void;\n}\n\n/** Which ARIA attribute each semantic state becomes, and with what value. */\nconst STATE_ATTRIBUTES: Record<string, [attribute: string, value: string]> = {\n checked: ['aria-checked', 'true'],\n mixed: ['aria-checked', 'mixed'],\n expanded: ['aria-expanded', 'true'],\n collapsed: ['aria-expanded', 'false'],\n selected: ['aria-selected', 'true'],\n pressed: ['aria-pressed', 'true'],\n busy: ['aria-busy', 'true'],\n invalid: ['aria-invalid', 'true'],\n required: ['aria-required', 'true'],\n readonly: ['aria-readonly', 'true'],\n modal: ['aria-modal', 'true']\n};\n\n/**\n * Roles whose checked state is required rather than optional.\n *\n * A component publishes `checked` when it is on and nothing when it is\n * off, which is the right shape for a state list. ARIA does not agree:\n * on these roles `aria-checked` is a required attribute, and one that\n * is absent means \"this is not a checkbox after all\" rather than \"this\n * checkbox is off\". A screen reader then has nothing to announce.\n *\n * Filled in here rather than in each component for the reason\n * The reason: what an assistive technology needs is\n * the mirror's business, and a rule in one file cannot be forgotten by\n * the next control somebody writes. Found on a `RadioGroup` in a native\n * window, where the unselected radio carried no `aria-checked` at all.\n */\nconst CHECKABLE_ROLES: ReadonlySet<string> = new Set([\n 'checkbox',\n 'radio',\n 'switch',\n 'menuitemcheckbox',\n 'menuitemradio'\n]);\n\n/** Every attribute a record can write, so clearing one is a fixed list. */\nconst RECORD_ATTRIBUTES: readonly string[] = [\n 'role',\n 'aria-label',\n 'aria-description',\n 'aria-live',\n 'aria-disabled',\n 'aria-valuenow',\n 'aria-valuemin',\n 'aria-valuemax',\n 'aria-valuetext',\n 'aria-posinset',\n 'aria-setsize',\n 'aria-level',\n 'aria-checked',\n 'aria-expanded',\n 'aria-selected',\n 'aria-pressed',\n 'aria-busy',\n 'aria-invalid',\n 'aria-required',\n 'aria-readonly',\n 'aria-modal'\n];\n\n/**\n * Roles whose accessible name comes from what the element contains\n * rather than from an `aria-label`, plus prose, which has no role at\n * all. Everything else is a control, and a control is named.\n */\nconst NAMED_BY_CONTENT: ReadonlySet<string> = new Set(['heading', 'paragraph']);\n\n/**\n * Live regions. Their name comes from an `aria-label` like any control's,\n * because the platform does not name a status from its content; but a\n * live region announces its *content* when it changes, and an\n * `aria-label` changing is silent, so the name is written as the text\n * as well. Chrome reports both, and reads the text out on change.\n */\nconst LIVE_ROLES: ReadonlySet<string> = new Set(['status', 'alert']);\n\n/**\n * Roles whose value the platform reads out of the element's content\n * rather than off an attribute — a text field, in other words, whose\n * `value` in the accessibility tree is the text inside it.\n */\nconst VALUE_IN_CONTENT: ReadonlySet<string> = new Set(['textbox', 'searchbox']);\n\ninterface MirrorEntry {\n readonly element: HTMLElement;\n record: UiSemanticsRecord;\n /** The node's box in canvas coordinates, once one has been sent. */\n box?: MirrorBox;\n /**\n * What `position` last wrote into this element's style, so a frame\n * that recomputes the same four numbers can leave the style alone.\n * Absent until the first write, which is also why the zeroes\n * `createElement` sets do not count as written: they belong to an\n * element that has no box yet, and the first real box must land.\n */\n written?: WrittenOffset;\n}\n\n/**\n * The rounded offset an element's style already carries.\n *\n * Deliberately the *written* value rather than the box it came from:\n * that is what makes the comparison safe across a reparent. `position`\n * recomputes the offset from whatever parent the record names now, so\n * the cache is only ever asked whether the style string it is about to\n * write is the one already there — a question whose answer cannot go\n * stale while `position` is the only writer of these four properties.\n */\ninterface WrittenOffset {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\ntype MirrorBox = UiSemanticsUpdate['boxes'][number]['box'];\n\n/**\n * The off-screen DOM an assistive technology reads.\n *\n * A canvas has no accessibility tree of its own: a screen reader, an\n * OS accessibility API and an automated testing tool all see one empty\n * element where the whole application is. So the shell keeps a DOM\n * tree over the canvas — one transparent element per semantics record,\n * carrying that record's ARIA — and the platform reads *that*. This is\n * the approach Flutter web takes, for the same reason: it is the only\n * one that works with assistive technology that already exists, on\n * every one of the three webviews Gesso targets.\n *\n * Three rules keep it honest:\n *\n * - **It is a mirror, not a source.** Nothing here decides anything.\n * Records arrive from the runtime's semantics tree, boxes from its\n * layout engine, focus from its focus manager, and an action taken\n * on an element becomes ordinary input on the way back.\n * - **It never takes the pointer.** The container is\n * `pointer-events: none`, so a mouse press goes to the canvas\n * underneath as it always did. The clicks that arrive here are the\n * ones an assistive technology synthesises, which is exactly the\n * set that has nowhere else to go.\n * - **Elements are generic.** A `<div role=\"button\">` is announced\n * as a button but has no behaviour of its own, so Enter on it\n * reaches the app's keymap once instead of also synthesising a\n * click the way a real `<button>` would. Every activation path\n * ends in one `click` action.\n */\nexport class SemanticsMirror {\n private readonly container: HTMLElement;\n private readonly doc: Document;\n private readonly entries = new Map<string, MirrorEntry>();\n /** Element back to record id, for the events an assistive technology sends. */\n private readonly ids = new WeakMap<HTMLElement, string>();\n private readonly detach: () => void;\n private resizeObserver: ResizeObserver | null = null;\n private stopTracking: (() => void) | null = null;\n /** True while this class is the one moving DOM focus. */\n private applying = false;\n private focusedId: string | null = null;\n private disposed = false;\n\n constructor(\n private readonly canvas: HTMLCanvasElement,\n private readonly sink: SemanticsMirrorSink,\n private readonly editing: EditingMirrorTarget | null = null\n ) {\n this.doc = canvas.ownerDocument;\n const container = this.doc.createElement('div');\n this.container = container;\n container.setAttribute('data-gesso-semantics', '');\n Object.assign(container.style, {\n position: 'fixed',\n left: '0px',\n top: '0px',\n width: '0px',\n height: '0px',\n // The canvas keeps every pointer event it ever had; only\n // synthesised activations land in here.\n pointerEvents: 'none',\n // Not `display: none`, `visibility: hidden` or zero opacity:\n // each of those takes the subtree out of the accessibility tree\n // as well as out of the picture, which is the whole content of\n // this element. Transparent and un-drawable is the combination\n // that stays readable.\n overflow: 'hidden',\n margin: '0',\n padding: '0',\n border: '0',\n color: 'transparent',\n background: 'transparent',\n font: '1px sans-serif',\n zIndex: '2147483646'\n } as Partial<CSSStyleDeclaration>);\n this.doc.body.appendChild(container);\n this.detach = this.listen();\n this.trackCanvas();\n }\n\n /** The container, for tests and for a shell that wants to inspect it. */\n get element(): HTMLElement {\n return this.container;\n }\n\n /** The element standing for a node, if the mirror has one. */\n elementFor(id: string): HTMLElement | undefined {\n return this.entries.get(id)?.element;\n }\n\n /**\n * Applies one frame's worth of change: what the tree means, where it\n * sits, and what has focus — in that order, because focus can name a\n * node the same update introduced.\n */\n apply(update: UiSemanticsUpdate): void {\n if (this.disposed) {\n return;\n }\n for (const patch of update.patches) {\n if (patch.op === 'remove') {\n this.remove(patch.id);\n } else {\n this.upsert(patch.node);\n }\n }\n // Every box lands before any style is written, in two passes over\n // the update. The second pass then sees final boxes whichever order\n // the worker sent them in, where a single pass positioned a child\n // against its parent's *new* box and its own *old* one whenever the\n // parent came first — a wrong offset that the child's own turn\n // happened to correct a moment later.\n const moved = new Set<MirrorEntry>();\n for (const { id, box } of update.boxes) {\n const entry = this.entries.get(id);\n if (entry === undefined) {\n continue;\n }\n entry.box = box;\n moved.add(entry);\n }\n for (const entry of moved) {\n this.position(entry);\n // The children sit inside this element, so their offsets are\n // measured from it: a parent that moved carries them with it in\n // the DOM, and their own left and top have to give that back. Only\n // the children this update left alone need that, though — a child\n // with a box of its own in here is positioned by its own turn in\n // this same loop, and repositioning it here as well is the work a\n // scroll does twice over, since a scroll moves a parent and all of\n // its children together.\n //\n // Indexed over the live `children` collection rather than a copy:\n // `position` writes four style properties and nothing else, so no\n // node is inserted, removed or reordered while this runs, and the\n // collection cannot shift under the index. (`place` is the one\n // thing that moves elements, and it has already finished above.)\n // The copy `Array.from` made was an allocation per moved parent,\n // which on a scrolling list is one per visible row.\n const children = entry.element.children;\n for (let index = 0; index < children.length; index += 1) {\n const childId = this.ids.get(children[index] as HTMLElement);\n const childEntry = childId === undefined ? undefined : this.entries.get(childId);\n if (childEntry !== undefined && childEntry.box !== undefined && !moved.has(childEntry)) {\n this.position(childEntry);\n }\n }\n }\n if (update.focused !== undefined) {\n this.applyFocus(update.focused);\n }\n }\n\n dispose(): void {\n this.disposed = true;\n this.detach();\n this.stopTracking?.();\n this.stopTracking = null;\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.entries.clear();\n this.container.remove();\n }\n\n // ---------------------------------------------------------------------------\n // Records\n // ---------------------------------------------------------------------------\n\n private upsert(record: UiSemanticsRecord): void {\n const existing = this.entries.get(record.id);\n const element = existing?.element ?? this.createElement();\n if (existing === undefined) {\n this.entries.set(record.id, { element, record });\n this.ids.set(element, record.id);\n } else {\n existing.record = record;\n }\n this.describe(element, record);\n this.place(element, record);\n if (existing?.box !== undefined) {\n // Under a different parent, the same box is a different offset —\n // and `existing.record` is the new record by now, so the offset\n // `position` recomputes is measured from the new parent. That is\n // what keeps the written-offset cache honest here: it is compared\n // against a value that already accounts for the move, so a\n // reparent that changes the offset writes, and one that does not\n // needs no write because `place` has already moved the element.\n this.position(existing);\n }\n if (record.id === this.focusedId) {\n // A record that changed while focused: the proxy's copy of it\n // has to change too, or a screen reader reads the old value.\n this.editing?.describe(this.editing.active ? record : null);\n }\n }\n\n private createElement(): HTMLElement {\n const element = this.doc.createElement('div');\n // Reachable by a programmatic or assistive focus, never by Tab:\n // the app owns its own focus order and moves DOM focus to follow\n // it, so the browser walking this tree would be a second, silent\n // focus model.\n element.tabIndex = -1;\n Object.assign(element.style, {\n position: 'absolute',\n left: '0px',\n top: '0px',\n width: '0px',\n height: '0px',\n margin: '0',\n padding: '0',\n border: '0',\n outline: 'none',\n overflow: 'hidden',\n // The container is `pointer-events: none`, but an element that\n // opted back in would take presses from the canvas.\n pointerEvents: 'none',\n color: 'transparent'\n } as Partial<CSSStyleDeclaration>);\n return element;\n }\n\n /** Writes a record onto its element as ARIA, clearing what it no longer says. */\n private describe(element: HTMLElement, record: UiSemanticsRecord): void {\n for (const attribute of RECORD_ATTRIBUTES) {\n element.removeAttribute(attribute);\n }\n const label = record.label;\n if (record.role !== undefined) {\n element.setAttribute('role', record.role);\n }\n if (label !== undefined && record.role !== undefined && !NAMED_BY_CONTENT.has(record.role)) {\n element.setAttribute('aria-label', label);\n // A text box's *value* is its content, and a screen reader reads\n // it from there: `aria-valuetext` is for a slider, not a field.\n // Without this a person could hear that a note's body exists and\n // never hear a word of it.\n setText(\n element,\n VALUE_IN_CONTENT.has(record.role) ? (record.valueText ?? '') : LIVE_ROLES.has(record.role) ? label : ''\n );\n } else {\n // Prose, a heading and a paragraph are named by what they\n // contain — and prose is most of what a screen reader reads, so\n // it has to be real text in the document rather than a label on\n // an empty box.\n setText(element, label ?? '');\n }\n if (record.description !== undefined) {\n element.setAttribute('aria-description', record.description);\n }\n if (record.live !== undefined) {\n element.setAttribute('aria-live', record.live);\n }\n if (record.disabled === true) {\n element.setAttribute('aria-disabled', 'true');\n }\n for (const state of record.states ?? []) {\n const attribute = STATE_ATTRIBUTES[state];\n if (attribute !== undefined) {\n element.setAttribute(attribute[0], attribute[1]);\n }\n }\n const saysChecked = (record.states ?? []).some(state => state === 'checked' || state === 'mixed');\n if (record.role !== undefined && CHECKABLE_ROLES.has(record.role) && !saysChecked) {\n element.setAttribute('aria-checked', 'false');\n }\n setNumber(element, 'aria-valuenow', record.valueNow);\n setNumber(element, 'aria-valuemin', record.valueMin);\n setNumber(element, 'aria-valuemax', record.valueMax);\n setNumber(element, 'aria-posinset', record.posInSet);\n setNumber(element, 'aria-setsize', record.setSize);\n setNumber(element, 'aria-level', record.level);\n if (record.valueText !== undefined) {\n element.setAttribute('aria-valuetext', record.valueText);\n }\n }\n\n /**\n * Puts an element under its parent, at the index the record gives.\n *\n * Document order is the order a screen reader reads in, so it is not\n * cosmetic: a row that moved up a list has to move up here too. Adds\n * arrive in document order, so a missing later sibling only ever\n * means \"not yet\", and appending is right.\n */\n private place(element: HTMLElement, record: UiSemanticsRecord): void {\n const parent = record.parent === null ? this.container : this.entries.get(record.parent)?.element;\n if (parent === undefined) {\n return;\n }\n const at = parent.children[record.index];\n if (at === element) {\n return;\n }\n parent.insertBefore(element, at ?? null);\n }\n\n /**\n * Writes an element's box as an offset from its parent's.\n *\n * Every element is absolutely positioned and nested under its parent,\n * so a child's `left` and `top` are read from the parent's padding\n * box, not from the canvas. Writing canvas coordinates into a nested\n * element added the parent's offset twice, and the rectangle an\n * assistive technology measured for a control inside a region stood\n * well below where the control was drawn. A parent whose box has not\n * arrived yet counts as sitting at the origin; the box loop above\n * repositions the children when it does.\n *\n * Rounded, because a subpixel box would make the style string differ\n * on frames where nothing an assistive technology can perceive has\n * changed.\n *\n * And rounding is why the four values are remembered and compared\n * before they are written. A scroll marks the container's transform,\n * which is a laid-out frame, so the worker sends every moved box every\n * scroll frame — and a style write invalidates style for that element\n * whether or not the value changed, on the one thread this whole\n * architecture exists to keep idle. The comparison has to be against\n * the rounded numbers rather than against the box: a subpixel change\n * survives the worker's exact comparison and still rounds to the pixel\n * that is already there. The win it buys is bigger than it looks,\n * because during a scroll a parent and its children move by the same\n * delta, so every child's offset *from its parent* is unchanged and a\n * scrolling list writes nothing at all for its rows.\n *\n * It is a claim about the style, not about the box, so a reparent\n * cannot make it lie: the offset is recomputed from the parent the\n * record names now, and if that offset is the same then the string\n * already on the element is still the right one — the element moving\n * in the DOM is what changes where it lands, not a style write. This\n * holds exactly as long as `position` is the only thing writing\n * `left`, `top`, `width` and `height` on a mirrored element.\n */\n private position(entry: MirrorEntry): void {\n const box = entry.box;\n if (box === undefined) {\n return;\n }\n const parent = entry.record.parent === null ? undefined : this.entries.get(entry.record.parent)?.box;\n const left = Math.round(box.x - (parent?.x ?? 0));\n const top = Math.round(box.y - (parent?.y ?? 0));\n const width = Math.round(box.width);\n const height = Math.round(box.height);\n const written = entry.written;\n if (\n written !== undefined &&\n written.left === left &&\n written.top === top &&\n written.width === width &&\n written.height === height\n ) {\n return;\n }\n if (written === undefined) {\n entry.written = { left, top, width, height };\n } else {\n // Overwritten in place rather than replaced: the elements that do\n // move, move every frame of a drag or a scroll, and this is the\n // one allocation on that path.\n written.left = left;\n written.top = top;\n written.width = width;\n written.height = height;\n }\n const { element } = entry;\n element.style.left = `${left}px`;\n element.style.top = `${top}px`;\n element.style.width = `${width}px`;\n element.style.height = `${height}px`;\n }\n\n private remove(id: string): void {\n const entry = this.entries.get(id);\n if (entry === undefined) {\n return;\n }\n this.entries.delete(id);\n entry.element.remove();\n if (this.focusedId === id) {\n this.focusedId = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Focus\n // ---------------------------------------------------------------------------\n\n /**\n * Moves DOM focus to follow the app's.\n *\n * The app's focus is the truth and this is the echo, which is why\n * every move here is flagged: the `focusin` it causes must not be\n * read back as the assistive technology having moved focus itself.\n */\n private applyFocus(id: string | null): void {\n this.focusedId = id;\n const entry = id === null ? undefined : this.entries.get(id);\n if (this.editing !== null && this.editing.active) {\n // The hidden textarea is where the person is really typing: it is\n // the only element an IME will compose into. Describing it is\n // what makes it the field rather than an anonymous text box, and\n // claiming focus is what moves the caret off whichever mirrored\n // element held it a moment ago.\n this.editing.describe(entry?.record ?? null);\n this.applying = true;\n try {\n this.editing.focus();\n } finally {\n this.applying = false;\n }\n return;\n }\n this.editing?.describe(null);\n this.applying = true;\n try {\n if (entry !== undefined) {\n entry.element.focus({ preventScroll: true });\n } else if (this.container.contains(this.doc.activeElement)) {\n // Focus left the app's tree — or landed on a node with no\n // semantics of its own. Either way the canvas takes it back,\n // so keys keep reaching the app.\n this.canvas.focus({ preventScroll: true });\n }\n } finally {\n this.applying = false;\n }\n }\n\n private listen(): () => void {\n const onClick = (event: Event): void => {\n const id = this.idOf(event.target);\n if (id !== null) {\n // Nothing on this thread knows whether the node handles a\n // press; the runtime routes it exactly as it routes a mouse's.\n this.sink.action({ id, action: 'click' });\n }\n };\n const onFocusIn = (event: FocusEvent): void => {\n if (this.applying) {\n return;\n }\n const id = this.idOf(event.target);\n if (id !== null && id !== this.focusedId) {\n // The assistive technology moved focus itself. The runtime\n // decides whether the node may have it — an open focus trap\n // refuses — and the answer comes back as the next update.\n this.sink.action({ id, action: 'focus' });\n }\n };\n const onKeyDown = (event: KeyboardEvent): void => this.sink.keyDown?.(event);\n const onKeyUp = (event: KeyboardEvent): void => this.sink.keyUp?.(event);\n this.container.addEventListener('click', onClick);\n this.container.addEventListener('focusin', onFocusIn);\n this.container.addEventListener('keydown', onKeyDown);\n this.container.addEventListener('keyup', onKeyUp);\n return () => {\n this.container.removeEventListener('click', onClick);\n this.container.removeEventListener('focusin', onFocusIn);\n this.container.removeEventListener('keydown', onKeyDown);\n this.container.removeEventListener('keyup', onKeyUp);\n };\n }\n\n /**\n * Which record an event landed on.\n *\n * A reverse index rather than an `instanceof HTMLElement` test and a\n * scan: the elements are this class's own, so identity is the whole\n * question, and an event on anything else is not ours.\n */\n private idOf(target: EventTarget | null): string | null {\n return this.ids.get(target as HTMLElement) ?? null;\n }\n\n // ---------------------------------------------------------------------------\n // Where the canvas is\n // ---------------------------------------------------------------------------\n\n /**\n * Keeps the container over the canvas.\n *\n * Boxes arrive in canvas pixels, so the container has to sit exactly\n * where the canvas does — and a `position: fixed` box moves relative\n * to the canvas whenever the page scrolls or the window resizes.\n * Reading the rect on those three signals rather than every frame is\n * deliberate: a `getBoundingClientRect()` per frame on the main\n * thread is the kind of forced layout the worker configuration\n * exists to avoid.\n */\n private trackCanvas(): void {\n const sync = (): void => {\n const rect = this.canvas.getBoundingClientRect();\n this.container.style.left = `${rect.left}px`;\n this.container.style.top = `${rect.top}px`;\n this.container.style.width = `${rect.width}px`;\n this.container.style.height = `${rect.height}px`;\n };\n sync();\n const view = this.doc.defaultView;\n if (view === null) {\n return;\n }\n view.addEventListener('resize', sync);\n view.addEventListener('scroll', sync, true);\n this.stopTracking = () => {\n view.removeEventListener('resize', sync);\n view.removeEventListener('scroll', sync, true);\n };\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(sync);\n this.resizeObserver.observe(this.canvas);\n }\n }\n}\n\nfunction setNumber(element: HTMLElement, attribute: string, value: number | undefined): void {\n if (value !== undefined) {\n element.setAttribute(attribute, String(value));\n }\n}\n\n/**\n * Writes an element's text without throwing away what is inside it.\n *\n * Assigning `textContent` replaces *every* child node, elements\n * included. That is what is wanted for prose and for a control named by\n * an `aria-label`, both of which are leaves, and it is exactly wrong\n * for a container: a `tablist` full of tabs, a labelled `group`, a\n * `region` holding a page. Describing one of those a second time — a\n * label that changed, a state that came and went — emptied it, and a\n * screen reader was told the container existed and nothing about what\n * was in it.\n *\n * Found on a tabbed page whose tabs were missing from the\n * accessibility tree while drawing correctly on screen, which is the\n * worst way for it to be wrong: no screenshot gate can see it. An\n * element with element children keeps them and takes its name from the\n * `aria-label` that was just written.\n */\nfunction setText(element: HTMLElement, text: string): void {\n if (element.children.length > 0) {\n return;\n }\n if (element.textContent !== text) {\n element.textContent = text;\n }\n}\n","import { classifyStorageError, storageErrorMessage } from '../storage/StorageAdapter';\nimport type { ShellStorageOp, ShellStorageResult } from './ShellService';\n\n/** What a shell needs of `localStorage`, and no more. */\nexport interface ShellLocalStore {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n key(index: number): string | null;\n readonly length: number;\n}\n\nconst DENIED: ShellStorageResult = {\n outcome: 'denied',\n value: null,\n keys: [],\n error: 'This window has no localStorage.'\n};\n\n/**\n * Performs one storage request on the shell's `localStorage`.\n *\n * The whole of what the shell does for `ShellStorage`, written once\n * and called from both configurations, because the shell's half of a\n * request should be identical whether the render side is a worker or\n * the same thread. It decides nothing: the key it is given is the key\n * it uses, and every outcome goes back to the thread that asked.\n *\n * `open` is a function rather than a store, because reading\n * `window.localStorage` is itself what throws when a browser has\n * blocked it. Asking for it inside the `try` is what turns that into a\n * `denied` answer rather than an exception in the shell's message\n * handler.\n */\nexport function performShellStorage(\n request: { readonly op: ShellStorageOp; readonly key: string; readonly value?: string },\n open: () => ShellLocalStore | null | undefined\n): ShellStorageResult {\n try {\n const store = open();\n if (store === null || store === undefined) {\n return DENIED;\n }\n switch (request.op) {\n case 'read':\n return { outcome: 'ok', value: store.getItem(request.key), keys: [], error: null };\n case 'write':\n store.setItem(request.key, request.value ?? '');\n return { outcome: 'ok', value: null, keys: [], error: null };\n case 'remove':\n store.removeItem(request.key);\n return { outcome: 'ok', value: null, keys: [], error: null };\n case 'keys': {\n const keys: string[] = [];\n for (let at = 0; at < store.length; at++) {\n const key = store.key(at);\n if (key !== null) {\n keys.push(key);\n }\n }\n return { outcome: 'ok', value: null, keys, error: null };\n }\n }\n } catch (error) {\n return { outcome: classifyStorageError(error), value: null, keys: [], error: storageErrorMessage(error) };\n }\n}\n\n/** The answer for a shell that has no window at all. */\nexport function shellStorageDenied(): ShellStorageResult {\n return DENIED;\n}\n","/**\n * Watching one media query, from the thread that has a window.\n *\n * Extracted from `reducedMotion.ts` when the colour scheme needed the\n * same thing: `matchMedia` needs a window, the runtime that cares may\n * be in a worker, and every such preference has to be reported *once*\n * immediately as well as on change — a person who already has the\n * preference set gets no `change` event to tell the app about it.\n *\n * Deliberately holds no reference to a runtime. `GessoApp` hands what\n * it reports straight to the runtime; `WorkerApp` posts it across.\n *\n * Returns a function that stops watching. In an environment with no\n * `matchMedia` — a Node test, an old webview — it reports `false` once\n * and stops, which is the answer the platform gives when nobody has\n * expressed the preference.\n */\nexport function observeMediaQuery(query: string, onChange: (matches: boolean) => void): () => void {\n const view = typeof globalThis === 'undefined' ? undefined : (globalThis as { matchMedia?: typeof matchMedia });\n if (typeof view?.matchMedia !== 'function') {\n onChange(false);\n return () => {};\n }\n const list = view.matchMedia(query);\n onChange(list.matches);\n const listener = (event: MediaQueryListEvent): void => onChange(event.matches);\n // `addEventListener` on a MediaQueryList is the modern form; Safari\n // below 14 only has `addListener`, and WKWebView is one of the three\n // webviews E1 has to work on.\n if (typeof list.addEventListener === 'function') {\n list.addEventListener('change', listener);\n return () => list.removeEventListener('change', listener);\n }\n const legacy = list as unknown as {\n addListener(fn: (event: MediaQueryListEvent) => void): void;\n removeListener(fn: (event: MediaQueryListEvent) => void): void;\n };\n legacy.addListener(listener);\n return () => legacy.removeListener(listener);\n}\n","import { observeMediaQuery } from './mediaQuery';\n\n/** Which of the two appearances the platform is asking for. */\nexport type ColorScheme = 'light' | 'dark';\n\n/**\n * What a shell is told to report.\n *\n * `auto` follows the platform and is the default; the other two are an\n * override, for a host that has its own control — a documentation site\n * with a light/dark toggle, or a desktop window with an app-level\n * appearance setting that is not the OS's.\n */\nexport type ColorSchemePreference = ColorScheme | 'auto';\n\n/**\n * Watches the platform's colour-scheme preference.\n *\n * Shell-side for the same reason `observeReducedMotion` is: the query\n * needs a window, and the runtime that reports it to the application\n * may be in a worker. Reported once immediately as well as on change,\n * so an app started by someone in dark mode does not paint a light\n * first frame.\n *\n * The scheme is resolved here rather than on the far side — the worker\n * hears `light` or `dark` and never `auto`, because \"what the platform\n * says\" is a question only the thread with a window can ask.\n */\nexport function observeColorScheme(onChange: (scheme: ColorScheme) => void): () => void {\n return observeMediaQuery('(prefers-color-scheme: dark)', dark => onChange(dark ? 'dark' : 'light'));\n}\n","import { observeMediaQuery } from './mediaQuery';\n\n/**\n * Watches the platform's reduced-motion preference.\n *\n * The preference is a main-thread fact — `matchMedia` needs a window —\n * and the runtime that owns the animations may be in a worker, so this\n * is deliberately a shell-side helper with no reference to a runtime.\n * `GessoApp` hands what it reports straight to `setReducedMotion`;\n * `WorkerApp` posts it across. Reporting once immediately matters as\n * much as reporting changes: an app started by someone who already has\n * the preference on must not animate its first screen.\n *\n * Returns a function that stops watching. See `observeMediaQuery` for\n * what happens where there is no `matchMedia` at all.\n */\nexport function observeReducedMotion(onChange: (reduced: boolean) => void): () => void {\n return observeMediaQuery('(prefers-reduced-motion: reduce)', onChange);\n}\n","/**\n * The window's address, as the only thing routing needs from a shell.\n *\n * A render worker has no `location` and no `history`, so a routed app\n * exchanges exactly one kind of value with the thread that does: a\n * url string, outward when the app navigates and inward when the back\n * button, the forward button or a typed address changes it. Everything\n * else about routing — patterns, params, guards, which screen — stays\n * in the worker, where the components are.\n *\n * Three modes, because a Gesso app runs in three kinds of window:\n *\n * - `path` — `pushState` against the document's path. What a web app\n * deployed at its own origin wants, and the default.\n * - `hash` — the app's url lives in the fragment, after an optional\n * `base`. For an app that shares a page with something else that\n * owns the path — the playground, whose own routes are hashes — and\n * for a static host that will not rewrite unknown paths onto the\n * app.\n * - `memory` — no window involvement at all. What a desktop window\n * wants, since it has no address bar to sync with, and\n * what a test wants.\n */\nexport type ShellHistoryMode = 'path' | 'hash' | 'memory';\n\nexport interface ShellHistoryOptions {\n readonly mode?: ShellHistoryMode;\n /**\n * `hash` mode only: what the app's url follows in the fragment.\n *\n * With `base: 'example-router'` the app's `/mail/2` is the whole\n * page's `#example-router/mail/2`, which leaves the first segment to\n * whatever else on the page is reading the hash.\n */\n readonly base?: string;\n /** `memory` mode only: where the app starts. Defaults to `/`. */\n readonly initialUrl?: string;\n}\n\nexport interface ShellHistory {\n /** The url the window is at now. */\n readonly url: string;\n push(url: string): void;\n replace(url: string): void;\n back(): void;\n forward(): void;\n /** Reports urls the person produced: back, forward, or a typed address. */\n onChange(listener: (url: string) => void): void;\n dispose(): void;\n}\n\n/**\n * Just the parts of a window this module uses.\n *\n * A parameter rather than the global so the browser modes can be\n * tested the way `EditingProxy` is: the contract with the DOM is\n * small, and stating it is what makes it assertable.\n */\nexport interface HistoryWindow {\n readonly location: { pathname: string; search: string; hash: string };\n readonly history: {\n pushState(data: unknown, title: string, url: string): void;\n replaceState(data: unknown, title: string, url: string): void;\n back(): void;\n forward(): void;\n };\n addEventListener(type: string, listener: () => void): void;\n removeEventListener(type: string, listener: () => void): void;\n}\n\nexport function createShellHistory(\n options: ShellHistoryOptions = {},\n host: HistoryWindow | undefined = typeof window === 'undefined' ? undefined : (window as unknown as HistoryWindow)\n): ShellHistory {\n const mode = options.mode ?? (host === undefined ? 'memory' : 'path');\n if (mode === 'memory' || host === undefined) {\n return new MemoryHistory(options.initialUrl ?? '/');\n }\n return new BrowserHistory(host, mode, options.base ?? '');\n}\n\nclass MemoryHistory implements ShellHistory {\n private readonly entries: string[];\n private index = 0;\n private listener: ((url: string) => void) | null = null;\n\n constructor(initialUrl: string) {\n this.entries = [initialUrl];\n }\n\n get url(): string {\n return this.entries[this.index]!;\n }\n\n push(url: string): void {\n this.entries.length = this.index + 1;\n this.entries.push(url);\n this.index = this.entries.length - 1;\n }\n\n replace(url: string): void {\n this.entries[this.index] = url;\n }\n\n back(): void {\n this.step(-1);\n }\n\n forward(): void {\n this.step(1);\n }\n\n onChange(listener: (url: string) => void): void {\n this.listener = listener;\n }\n\n dispose(): void {\n this.listener = null;\n }\n\n private step(delta: number): void {\n const next = this.index + delta;\n if (next < 0 || next >= this.entries.length) {\n return;\n }\n this.index = next;\n this.listener?.(this.url);\n }\n}\n\n/**\n * The two browser modes, which differ only in where the url is kept.\n *\n * Both write with `history.pushState`/`replaceState` — in hash mode\n * too, rather than by assigning `location.hash`, because assignment\n * cannot replace an entry and would leave every navigation in the back\n * stack whether the app asked for that or not.\n */\nclass BrowserHistory implements ShellHistory {\n private listener: ((url: string) => void) | null = null;\n private readonly onPopState: () => void;\n private readonly onHashChange: () => void;\n /**\n * The last url this class wrote.\n *\n * `pushState` fires neither `popstate` nor `hashchange`, so in a\n * browser nothing echoes; this is here for the hosts that do not\n * honour that — an embedded webview, a test — where an echo would\n * otherwise come back as a navigation the person never made. It is\n * cleared on the first event that is not the echo.\n */\n private written: string | null = null;\n\n constructor(\n private readonly host: HistoryWindow,\n private readonly mode: 'path' | 'hash',\n private readonly base: string\n ) {\n this.onPopState = () => this.report();\n this.onHashChange = () => this.report();\n host.addEventListener('popstate', this.onPopState);\n if (mode === 'hash') {\n // A fragment typed into the address bar changes no history entry,\n // so popstate alone would miss it.\n host.addEventListener('hashchange', this.onHashChange);\n }\n }\n\n get url(): string {\n const location = this.host.location;\n return this.mode === 'path' ? `${location.pathname}${location.search}` : this.fromHash();\n }\n\n push(url: string): void {\n this.written = url;\n this.host.history.pushState(null, '', this.toHref(url));\n }\n\n replace(url: string): void {\n this.written = url;\n this.host.history.replaceState(null, '', this.toHref(url));\n }\n\n back(): void {\n this.host.history.back();\n }\n\n forward(): void {\n this.host.history.forward();\n }\n\n onChange(listener: (url: string) => void): void {\n this.listener = listener;\n }\n\n dispose(): void {\n this.listener = null;\n this.host.removeEventListener('popstate', this.onPopState);\n this.host.removeEventListener('hashchange', this.onHashChange);\n }\n\n private report(): void {\n const url = this.url;\n if (url === this.written) {\n return;\n }\n this.written = null;\n this.listener?.(url);\n }\n\n private toHref(url: string): string {\n if (this.mode === 'path') {\n return url;\n }\n // The path and query the page was served at are kept: in hash mode\n // the app owns the fragment and nothing else.\n const location = this.host.location;\n return `${location.pathname}${location.search}#${this.base}${url}`;\n }\n\n private fromHash(): string {\n const hash = this.host.location.hash.replace(/^#/, '');\n if (this.base.length === 0) {\n return hash.length === 0 ? '/' : ensureLeadingSlash(hash);\n }\n if (hash === this.base) {\n return '/';\n }\n if (!hash.startsWith(`${this.base}/`)) {\n // The fragment belongs to something else on the page; the app is\n // at its own root rather than at a url it cannot read.\n return '/';\n }\n return ensureLeadingSlash(hash.slice(this.base.length));\n }\n}\n\nfunction ensureLeadingSlash(url: string): string {\n return url.startsWith('/') ? url : `/${url}`;\n}\n","import type { UiNodeReport } from '../NodeReport';\nimport type { DevtoolsEvent, DevtoolsRequest } from '../DevtoolsProtocol';\nimport { isConsoleEntryMessage, type ConsoleForwardingMessage } from '../../worker/captureConsole';\nimport type { FrameMetrics, RendererChoice } from '../GessoRuntime';\nimport {\n epochFromEvent,\n epochNow,\n isInputMessage,\n modifiersFrom,\n type RuntimeErrorSource,\n type RuntimeToShellMessage,\n type ShellToRuntimeMessage\n} from './RenderWorkerProtocol';\nimport {\n capturePointer,\n observeViewportInsets,\n pointerDeviceOf,\n prepareInputSurface,\n touchActionFor,\n wheelDeltaYOf,\n type UiScrollability\n} from 'gesso-core';\nimport { AudioSink } from '../AudioSink';\nimport { portHandle, type WorkerHandle } from '../../worker/WorkerPorts';\nimport { EditingProxy, writeClipboard } from '../EditingProxy';\nimport { SemanticsMirror } from '../SemanticsMirror';\nimport { performShellStorage } from '../shellStorage';\nimport { observeColorScheme, type ColorSchemePreference } from '../colorScheme';\nimport { observeReducedMotion } from '../reducedMotion';\nimport { createShellHistory, type ShellHistory, type ShellHistoryOptions } from '../shellHistory';\n\n/**\n * What the shell needs to spawn and drive a render worker.\n *\n * There is deliberately nothing here about media. A resolver is a\n * function and no function crosses a `postMessage`, so an option on\n * this side could only ever be a promise the shell could not keep;\n * the worker entry builds its own and declares it with\n * `renderRoot(AppRoot).useMedia(...)`.\n */\n/**\n * Somewhere to send the application handshake that is not a `Worker`.\n *\n * A `MessagePort` satisfies it, and so does anything else that can\n * carry a message and a transferred port. It exists because the\n * application layer does not always live in a worker in this page: in\n * a desktop window it lives in another process, and what the shell\n * holds is one end of a bridge to it (`gesso-electrobun`).\n *\n * The shell treats an endpoint exactly as it treats a worker it was\n * handed rather than one it spawned: it wires it up, and it never\n * closes it.\n */\nexport interface AppLogicEndpoint {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', listener: (event: MessageEvent<unknown>) => void): void;\n removeEventListener(type: 'message', listener: (event: MessageEvent<unknown>) => void): void;\n /** A `MessagePort` delivers nothing until this is called; a `Worker` has no such method. */\n start?: () => void;\n}\n\nexport interface WorkerAppOptions {\n /**\n * Spawns the render worker: the one that calls renderRoot(), and so\n * owns components, layout and drawing.\n *\n * Prefer the factory form. Bundlers only code-split a worker when\n * they can see `new Worker(new URL('./x.ts', import.meta.url))`\n * written out literally, and that cannot happen inside this file —\n * it has to appear in the calling module:\n *\n * renderWorker: () => new Worker(new URL('./app.render.worker.ts', import.meta.url), { type: 'module' })\n *\n * A URL is accepted for environments that resolve modules at\n * runtime, but a bundled build will not emit a chunk for it.\n */\n renderWorker: (() => Worker) | URL | string;\n /**\n * The rendering backend the worker draws with. Defaults to `auto`,\n * which is WebGPU where the worker has it and Canvas2D elsewhere;\n * `webgpu` and `auto` both fall back to Canvas2D when the browser\n * has no WebGPU in workers, and the frame metrics say which one is\n * drawing.\n */\n renderer?: RendererChoice;\n /**\n * Receives frame timings reported by the render worker.\n *\n * `FrameMetrics` itself rather than a structural copy of it: the\n * copy had already fallen a field behind the real thing once.\n */\n onFrame?: (metrics: FrameMetrics) => void;\n /**\n * Spawns the application-logic worker: api, persistence, domain and\n * view models, published as channels.\n *\n * The shell creates it, hands the render worker a port to it, and\n * then has nothing more to do with it — no patch ever crosses this\n * thread. Owning the spawn is bootstrap wiring, not a running\n * responsibility, and it buys two things: the application survives\n * the render worker being replaced, and nothing depends on a worker\n * being able to spawn a worker.\n *\n * A factory rather than a URL, for the same reason `renderWorker` is\n * one.\n *\n * Pass an already-running `Worker` to keep it across a remount. The\n * app is disposed and rebuilt whenever the *rendering* changes — the\n * playground's Canvas2D/WebGPU switch does exactly that — and an\n * application worker spawned here would go with it, discarding\n * application state for a reason that had nothing to do with the\n * application. What this class spawned, it terminates; what it was\n * handed, it leaves alone.\n */\n appLogicWorker?: Worker | AppLogicEndpoint | (() => Worker) | URL | string;\n /**\n * Opens a url the application asked for, in place of a new tab.\n *\n * A page wants `window.open`, which is the default. A desktop window\n * does not: `window.open` in a webview opens another webview or\n * nothing at all, and a link in a desktop application belongs in the\n * person's browser, which only the process outside the window can\n * reach. `gesso-electrobun`'s bridge is what goes here.\n */\n onOpenUrl?: (url: string) => void;\n /**\n * Receives errors thrown inside the render worker: while handling a\n * message, uncaught during a frame, from the renderer, or from a\n * channel — `source` says which, and `RuntimeErrorSource` says what\n * each one costs the running application.\n *\n * Defaults to `console.error`, which is a developer reading the\n * right thread in devtools at the right moment. `gesso-devtools`'s\n * error overlay is the same callback, drawn where the app is.\n */\n onError?: (message: string, stack: string | undefined, source: RuntimeErrorSource) => void;\n /**\n * Receives a report on the hovered node while the inspector is on\n * (see `setInspector`), and null when nothing is hovered.\n *\n * The report is built in the worker, where the tree is, and crosses\n * as plain data; `report.explanation` is the layout explanation this\n * used to carry on its own.\n */\n onInspect?: (report: UiNodeReport | null) => void;\n /**\n * Cancel the browser's Ctrl/Cmd+F on the canvas, so the app's own\n * find bar takes it. Off by default: the browser's find bar cannot\n * see a canvas, but taking the shortcut from an app that has no find\n * of its own would leave the user with neither.\n *\n * A flag rather than something the runtime decides, because the\n * worker's answer cannot come back in time to cancel a default —\n * unlike `GessoApp`, where the platform adapter cancels whatever the\n * app's own KeyDown listener claimed.\n */\n interceptFind?: boolean;\n /**\n * The appearance the application is told about: `auto` (the default)\n * follows `prefers-color-scheme`, `light` and `dark` override it.\n *\n * An option as well as a setter because a host with its own control\n * — a documentation site whose reader has already chosen dark —\n * otherwise starts on the platform's answer and corrects it a frame\n * later, which is a visible flash of the wrong appearance.\n */\n colorScheme?: ColorSchemePreference;\n /**\n * Set false to drop the off-screen DOM an assistive technology reads\n * (`SemanticsMirror`).\n *\n * On by default: an application that is accessible only when its\n * author remembered a flag is an application that is not accessible.\n * Turning it off also stops the render worker computing the geometry\n * the mirror needs, which is what makes the opt-out worth having for\n * a measurement.\n */\n accessibility?: boolean;\n /**\n * How the app's url is kept, for an app with routes: `path`\n * (pushState, the default), `hash`, or `memory`. See `shellHistory`.\n *\n * The shell's half of routing is this and nothing else. It holds no\n * routes, resolves nothing, and could not — a route names a\n * component class, which never leaves the worker. It reports the url\n * the window is at and performs the pushes the worker asks for.\n */\n history?: ShellHistoryOptions;\n}\n\n/**\n * Main-thread half of a worker-hosted Gesso application.\n *\n * Owns nothing but the canvas element and the event plumbing. It\n * creates the canvas, hands its drawing surface to the worker as an\n * OffscreenCanvas, and forwards pointer, wheel, keyboard and resize\n * into the worker. No component, node, layout record or render call\n * exists on this thread, so main-thread work cannot delay a frame.\n */\n/**\n * Which application layer the shell was given, and whether it is the\n * shell's to close.\n *\n * A factory, a URL or a path names a worker this class creates, and\n * what it creates it terminates. Anything else was handed over\n * already running: a `Worker` kept across a remount, or an endpoint\n * that is not a worker at all, which is how a desktop window reaches\n * an application layer living in another process\n * (`gesso-electrobun`). Neither is closed here, because a handle\n * that could kill something it did not start is the wrong handle.\n *\n * Exported for its spec: the ownership half is the part that goes\n * quietly wrong, by discarding an application the shell had merely\n * borrowed.\n */\n/**\n * Opens a url through the host's handler, or in a new tab.\n *\n * Exported for its spec: the default carries `noopener,noreferrer`,\n * which is the difference between opening a link and handing the\n * opener to whatever is on the other end of it.\n */\nexport function openUrlWith(handler: ((url: string) => void) | undefined, url: string): void {\n if (handler !== undefined) {\n handler(url);\n return;\n }\n window.open(url, '_blank', 'noopener,noreferrer');\n}\n\nexport function resolveAppLogic(spec: NonNullable<WorkerAppOptions['appLogicWorker']>): {\n endpoint: AppLogicEndpoint;\n owned: boolean;\n} {\n if (typeof spec === 'function') {\n return { endpoint: spec(), owned: true };\n }\n if (typeof spec === 'string' || spec instanceof URL) {\n return { endpoint: new Worker(spec, { type: 'module' }), owned: true };\n }\n return { endpoint: spec, owned: false };\n}\n\n/**\n * The one message this shell ever holds on to rather than posting the\n * moment it has it. See `flushPendingMove`.\n */\ntype PointerMoveMessage = Extract<ShellToRuntimeMessage, { type: 'pointerMove' }>;\n\nexport class WorkerApp {\n private readonly options: WorkerAppOptions;\n\n private renderWorker: Worker | undefined;\n private appLogicWorker: AppLogicEndpoint | undefined;\n private devtoolsListener: ((event: DevtoolsEvent) => void) | null = null;\n /** Whether a panel has asked for the workers' consoles, remembered across a remount. */\n private consoleForwarding = false;\n /** True only when this class spawned the application-logic worker. */\n private ownsAppLogicWorker = false;\n private canvas: HTMLCanvasElement | undefined;\n private host: HTMLElement | undefined;\n private resizeObserver: ResizeObserver | null = null;\n private detachInput: (() => void) | null = null;\n private proxy: EditingProxy | null = null;\n private mirror: SemanticsMirror | null = null;\n /** The one audio element, behind `AudioService`; see `AudioSink`. */\n private audio: AudioSink | null = null;\n /**\n * Which way the runtime could scroll under the pointer, as of the\n * last frame the worker reported.\n *\n * Starts out all false, which is the safe unknown: before the\n * worker has said anything the shell lets wheels through to the\n * page rather than swallowing them, so a canvas that fails to start\n * degrades to an inert picture instead of a hole that eats\n * scrolling.\n */\n private scrollability: UiScrollability = { up: false, down: false, left: false, right: false };\n private history: ShellHistory | null = null;\n /** True once the render worker has answered `ready` at least once. */\n private ready = false;\n /** The running `requestAnimationFrame` handle, when ticks are wanted. */\n private frameHandle: number | null = null;\n /** Stops watching `prefers-color-scheme`; null while overridden. */\n private detachColorScheme: (() => void) | null = null;\n /** Stops watching `visualViewport` for the safe area and the keyboard. */\n private detachViewportInsets: (() => void) | null = null;\n /** The appearance this shell reports, remembered across a remount. */\n private colorSchemePreference: ColorSchemePreference = 'auto';\n /**\n * Whether the render worker still owes an answer to a `resize`, and\n * the newest size that has gone unsent because it does.\n *\n * A resize is the one shell message whose handling costs the worker\n * a full layout and a paint, and `ResizeObserver` delivers one per\n * refresh while a window edge is dragged. A worker slower than the\n * display therefore accumulated a queue of sizes, every one of them\n * already wrong by the time it was laid out, and the lag grew for\n * the length of the drag instead of settling. So the shell keeps one\n * resize in flight and remembers only the latest size it has not\n * sent. Nothing anyone can see is dropped: the size held back is the\n * newest one, and it goes out as soon as the worker says it has\n * drained the last.\n *\n * The initial size travels in `init` rather than as a resize, so\n * nothing is in flight until the first notification arrives.\n */\n private resizeInFlight = false;\n private heldResize: { width: number; height: number; dpr: number } | null = null;\n /**\n * Where the canvas sits on the page, or null when that has to be\n * read from the DOM again. `attachInput` says why it is cached and\n * what the cache costs.\n */\n private canvasOrigin: { left: number; top: number } | null = null;\n /**\n * The hover move being held for this frame, and the frame holding\n * it. See `flushPendingMove`.\n */\n private pendingMove: PointerMoveMessage | null = null;\n private moveFrame: number | null = null;\n\n constructor(options: WorkerAppOptions) {\n this.options = options;\n this.colorSchemePreference = options.colorScheme ?? 'auto';\n }\n\n /**\n * Creates the canvas, starts the worker, and wires event forwarding.\n *\n * Returns a dispose function.\n */\n /**\n * A handle on the application-logic worker, for something on this\n * thread that wants its channels too: `createChannelRegistry` takes\n * it as a registration's `worker`. Undefined before `mount`, and\n * when no application-logic worker was given. The shell itself never\n * uses this; its own view of the application is nothing at all.\n */\n get appLogic(): WorkerHandle | undefined {\n return this.appLogicWorker === undefined ? undefined : portHandle(this.appLogicWorker);\n }\n\n mount(host: HTMLElement | string): () => void {\n const element = resolveHost(host);\n this.host = element;\n\n const canvas = document.createElement('canvas');\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n prepareInputSurface(canvas);\n canvas.tabIndex = 0;\n element.appendChild(canvas);\n this.canvas = canvas;\n\n if (typeof canvas.transferControlToOffscreen !== 'function') {\n throw new Error(\n 'OffscreenCanvas is unavailable, so the render worker cannot draw. ' +\n 'Use createApp(Root).mountSync(host) for the single-thread configuration.'\n );\n }\n\n const offscreen = canvas.transferControlToOffscreen();\n const spec = this.options.renderWorker;\n const worker = typeof spec === 'function' ? spec() : new Worker(spec, { type: 'module' });\n this.renderWorker = worker;\n worker.addEventListener('message', this.handleWorkerMessage);\n worker.addEventListener('error', this.handleWorkerFailure);\n\n // The canvas's own box, not the host's. `clientWidth`/`clientHeight`\n // include the host's padding, while the canvas is sized to its\n // content box — so a padded host (the playground's preview pane has\n // 16px) started the runtime with a viewport wider and taller than\n // the surface it draws on. Everything came out scaled, and pointer\n // coordinates, which `attachInput` takes from this same rect,\n // landed off by the same ratio. The ResizeObserver below reports\n // `contentRect` and so already agreed with this measurement; the\n // first frame was the only one that did not.\n const { width, height } = measure(canvas, element);\n const transfer: Transferable[] = [offscreen];\n let appPort: MessagePort | undefined;\n if (this.options.appLogicWorker !== undefined) {\n const resolved = resolveAppLogic(this.options.appLogicWorker);\n const application = resolved.endpoint;\n this.appLogicWorker = application;\n this.ownsAppLogicWorker = resolved.owned;\n application.addEventListener('message', this.handleAppWorkerMessage);\n // A port delivers nothing until it is started, and a worker has\n // no such method. Calling it here rather than asking the caller\n // to is what makes a port a drop-in for a worker.\n application.start?.();\n if (this.consoleForwarding) {\n // A panel asked before the worker existed (a remount), and the\n // new worker has not been told.\n application.postMessage({ type: 'gesso:console', enabled: true } as ConsoleForwardingMessage);\n }\n // One channel between the two workers. The shell holds neither\n // end afterwards, so it cannot be in the way of a patch even by\n // accident.\n const hub = new MessageChannel();\n application.postMessage({ type: 'gesso:hub' }, [hub.port2]);\n appPort = hub.port1;\n transfer.push(hub.port1);\n }\n worker.postMessage(\n {\n type: 'init',\n canvas: offscreen,\n width,\n height,\n dpr: window.devicePixelRatio || 1,\n renderer: this.options.renderer,\n // Text comes through the editing proxy below, IME and all.\n textInput: 'proxy',\n accessibility: this.options.accessibility !== false,\n appPort\n } as ShellToRuntimeMessage,\n transfer\n );\n\n this.observeResize(element);\n this.attachHistory();\n this.detachInput = this.attachInput(canvas);\n // Sound. The element has to live here, and only here; the worker\n // tells it what to do and hears what it did.\n this.audio = new AudioSink({\n sample: sample => this.post({ type: 'audioSample', sample }),\n action: action => this.post({ type: 'audioAction', action })\n });\n this.setColorScheme(this.colorSchemePreference);\n // The safe area and the soft keyboard, as four numbers. Read here\n // because `visualViewport` is the window's; what to do about them\n // is layout, and layout is in the worker. Reported once immediately\n // as well as on change, so a phone whose notch has been there all\n // along starts with the right numbers.\n this.detachViewportInsets = observeViewportInsets(insets => this.post({ type: 'viewportInsets', insets }));\n // The hidden textarea that turns keystrokes into text for the\n // worker. It has DOM focus while the worker reports a focused\n // editable, so its key events are forwarded like the canvas's.\n this.proxy = new EditingProxy(canvas, {\n beforeInput: (inputType, data) => this.post({ type: 'beforeInput', inputType, data }),\n compositionStart: () => this.post({ type: 'compositionStart' }),\n compositionUpdate: (text, caret) => this.post({ type: 'compositionUpdate', text, caret }),\n compositionEnd: text => this.post({ type: 'compositionEnd', text }),\n paste: text => this.post({ type: 'paste', text }),\n blur: () => this.post({ type: 'blur' }),\n keyDown: event => this.forwardKeyDown(event),\n keyUp: event => this.forwardKeyUp(event)\n });\n if (this.options.accessibility !== false) {\n // The off-screen DOM an assistive technology reads. Keys are\n // forwarded from it for the same reason they are forwarded from\n // the proxy: while the app has focus, the element holding it is\n // one of these and not the canvas.\n this.mirror = new SemanticsMirror(\n canvas,\n {\n action: action => this.post({ type: 'semanticsAction', action }),\n keyDown: event => this.forwardKeyDown(event),\n keyUp: event => this.forwardKeyUp(event)\n },\n this.proxy\n );\n }\n\n return () => this.dispose();\n }\n\n /**\n * An error the browser raised *at the worker object*, which is not\n * the same thing as the worker reporting one.\n *\n * The worker reports its own exceptions over the protocol, with a\n * stack and a source; this event carries neither. Left uncancelled,\n * the browser reports it a second time at this window — the same\n * failure with less information, which is what the error overlay\n * showed as a duplicate — so it is cancelled here.\n *\n * It is passed on in exactly one case: the worker never got as far\n * as saying `ready`, so its own handlers were never installed and\n * nothing else will ever report this. A module that fails to load,\n * or fails to parse, arrives this way and no other.\n */\n private handleWorkerFailure = (event: ErrorEvent): void => {\n event.preventDefault();\n if (this.ready) {\n return;\n }\n const where = event.filename === undefined || event.filename === '' ? '' : ` (${event.filename})`;\n this.report(`the render worker failed to start: ${event.message}${where}`, undefined, 'uncaught');\n };\n\n /** Reports an error, to `onError` or to the console it defaults to. */\n private report(message: string, stack: string | undefined, source: RuntimeErrorSource): void {\n // A panel hears every error the shell does, whatever the shell\n // does with it; the overlay and the panel are two readers, not\n // two sources.\n this.devtoolsListener?.({ kind: 'error', message, ...(stack === undefined ? {} : { stack }), source });\n const report =\n this.options.onError ?? ((text, trace, from) => console.error(`[gesso render worker: ${from}] ${text}`, trace));\n report(message, stack, source);\n }\n\n private forwardKeyDown(event: KeyboardEvent): void {\n // Whatever hover the shell is holding happened before this key\n // and has to be posted before it; see `flushPendingMove`. Keys\n // reach here from the canvas, the editing proxy and the semantics\n // mirror, so the flush belongs here rather than in one listener.\n this.flushPendingMove();\n if (this.options.interceptFind === true && isFind(event)) {\n event.preventDefault();\n }\n if (event.key === 'Tab' || isSelectAll(event)) {\n // The worker's answer cannot come back in time to cancel a\n // default, so the shell cancels the two that would be wrong\n // whatever it is: Tab moving focus out of the canvas, and\n // Ctrl/Cmd+A selecting the page around it while the app selects\n // its own text.\n event.preventDefault();\n }\n this.post({ type: 'keyDown', key: event.key, modifiers: modifiersFrom(event), at: epochFromEvent(event) });\n }\n\n private forwardKeyUp(event: KeyboardEvent): void {\n this.flushPendingMove();\n this.post({ type: 'keyUp', key: event.key, modifiers: modifiersFrom(event), at: epochFromEvent(event) });\n }\n\n /**\n * Turns the layout inspector on or off in the worker: hover boxes and\n * a measure heatmap over the scene, and explanations via `onInspect`.\n */\n setInspector(enabled: boolean): void {\n this.post({ type: 'inspector', enabled });\n }\n\n /**\n * Receives what a devtools panel asked for through `devtools`, and\n * updates to whatever it is watching. A method rather than an option\n * because a panel attaches to an application that is already\n * running, and detaches from one that keeps running.\n */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void {\n this.devtoolsListener = listener;\n }\n\n /**\n * Passes a devtools panel's request to the render worker, and a\n * console request to the application worker as well: each worker\n * forwards its own console, and the shell names the thread.\n */\n devtools(request: DevtoolsRequest): void {\n this.post({ type: 'devtools', request });\n if (request.kind === 'console') {\n this.consoleForwarding = request.enabled;\n this.appLogicWorker?.postMessage({ type: 'gesso:console', enabled: request.enabled } as ConsoleForwardingMessage);\n }\n }\n\n /**\n * Chooses what the application is told about the appearance.\n *\n * `auto` watches `prefers-color-scheme` and reports what it says;\n * `light` and `dark` stop watching and report themselves, for a host\n * with its own control — the reader of a documentation site who has\n * picked dark against a light system, or a desktop window with an\n * appearance setting of its own.\n *\n * Safe to call before `mount`: the preference is remembered and sent\n * when the worker starts.\n */\n setColorScheme(preference: ColorSchemePreference): void {\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.colorSchemePreference = preference;\n if (this.renderWorker === undefined) {\n return;\n }\n if (preference === 'auto') {\n // Reports once immediately as well as on change, for the reason\n // reduced motion does: nobody fires a `change` event at an app\n // that started in the appearance it is already in.\n this.detachColorScheme = observeColorScheme(scheme => this.post({ type: 'colorScheme', scheme }));\n return;\n }\n this.post({ type: 'colorScheme', scheme: preference });\n }\n\n /**\n * Runs the display's refresh loop on the runtime's behalf.\n *\n * The one piece of per-frame work the shell genuinely has to do:\n * `requestAnimationFrame` is tied to the compositor and does not\n * exist in a worker, so without this the render worker can only\n * guess at a cadence with a timer — a fixed sixty on a 165Hz\n * display, aligned to none of its refreshes.\n *\n * The loop is free-running while the worker wants frames rather than\n * armed per frame, because a request-per-frame costs a round trip\n * inside every frame and halves the rate whenever the request misses\n * that vsync's callback. Each tick is a bare timestamp, and the loop\n * stops the moment the worker says it is idle — so an app doing\n * nothing costs nothing here.\n */\n private setFrameLoop(running: boolean): void {\n if (!running) {\n if (this.frameHandle !== null) {\n cancelAnimationFrame(this.frameHandle);\n this.frameHandle = null;\n }\n return;\n }\n if (this.frameHandle !== null) {\n return;\n }\n const step = (time: number): void => {\n // Re-armed before posting, so a worker that keeps wanting frames\n // never waits a refresh for the shell to come back round.\n this.frameHandle = requestAnimationFrame(step);\n this.post({ type: 'tick', time });\n };\n this.frameHandle = requestAnimationFrame(step);\n }\n\n dispose(): void {\n this.ready = false;\n this.setFrameLoop(false);\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.detachViewportInsets?.();\n this.detachViewportInsets = null;\n this.proxy?.dispose();\n this.proxy = null;\n this.mirror?.dispose();\n this.mirror = null;\n this.history?.dispose();\n this.history = null;\n this.detachInput?.();\n this.detachInput = null;\n this.audio?.dispose();\n this.audio = null;\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n // A remount starts from nothing in flight. The new worker is told\n // its size in `init`, and an acknowledgement from the old one\n // would be about a canvas that no longer exists.\n this.resizeInFlight = false;\n this.heldResize = null;\n this.canvasOrigin = null;\n this.dropPendingMove();\n if (this.renderWorker !== undefined) {\n this.renderWorker.postMessage({ type: 'dispose' } as ShellToRuntimeMessage);\n this.renderWorker.removeEventListener('message', this.handleWorkerMessage);\n this.renderWorker.removeEventListener('error', this.handleWorkerFailure);\n this.renderWorker.terminate();\n this.renderWorker = undefined;\n }\n this.appLogicWorker?.removeEventListener('message', this.handleAppWorkerMessage);\n if (this.ownsAppLogicWorker) {\n (this.appLogicWorker as Worker | undefined)?.terminate();\n }\n this.appLogicWorker = undefined;\n this.ownsAppLogicWorker = false;\n if (this.canvas !== undefined && this.canvas.parentElement === this.host) {\n this.host?.removeChild(this.canvas);\n }\n this.canvas = undefined;\n this.host = undefined;\n }\n\n /**\n * The application worker talks to the render worker over the hub,\n * never to the shell; the one thing it says to the shell is a\n * forwarded console entry, which is the one thing the render worker\n * cannot say for it.\n */\n private readonly handleAppWorkerMessage = (event: MessageEvent<unknown>): void => {\n if (isConsoleEntryMessage(event.data)) {\n this.devtoolsListener?.({ kind: 'console', entry: { ...event.data.entry, thread: 'app' } });\n }\n };\n\n private readonly handleWorkerMessage = (event: MessageEvent<RuntimeToShellMessage>): void => {\n const message = event.data;\n if (message.type === 'frame') {\n this.options.onFrame?.({\n frame: message.frame,\n durationMs: message.durationMs,\n nodes: message.nodes,\n measured: message.measured,\n relayoutRoots: message.relayoutRoots,\n at: message.at,\n inputLatencyMs: message.inputLatencyMs,\n phases: message.phases,\n renderer: message.renderer,\n gpu: message.gpu\n });\n return;\n }\n if (message.type === 'error') {\n this.report(message.message, message.stack, message.source);\n return;\n }\n if (message.type === 'ready') {\n this.ready = true;\n return;\n }\n if (message.type === 'frameLoop') {\n this.setFrameLoop(message.running);\n return;\n }\n if (message.type === 'inspect') {\n this.options.onInspect?.(message.report);\n return;\n }\n if (message.type === 'devtools') {\n this.devtoolsListener?.(message.event);\n return;\n }\n if (message.type === 'cursor') {\n // The worker decided what the pointer is over; only the DOM can\n // show it. An empty string restores the stylesheet's cursor.\n if (this.canvas !== undefined) {\n this.canvas.style.cursor = message.cursor ?? '';\n }\n return;\n }\n if (message.type === 'scrollability') {\n // Cached, not acted on: the wheel handler reads it synchronously\n // when an event arrives, which is the whole reason the worker\n // pushes it ahead of time.\n this.scrollability = message.scrollability;\n if (this.canvas !== undefined) {\n this.canvas.style.touchAction = touchActionFor(message.scrollsAnything);\n }\n return;\n }\n if (message.type === 'resized') {\n this.handleResized(message);\n return;\n }\n if (message.type === 'editing') {\n this.proxy?.update(message.state);\n return;\n }\n if (message.type === 'clipboard') {\n writeClipboard(message.text);\n return;\n }\n if (message.type === 'openUrl') {\n openUrlWith(this.options.onOpenUrl, message.url);\n return;\n }\n if (message.type === 'popup') {\n this.openPopup(message);\n return;\n }\n if (message.type === 'storage') {\n // The one call in this file that reaches a browser API the\n // render worker cannot: `localStorage` is on the window. What\n // comes back is plain data, and the shell judges none of it.\n this.post({\n type: 'storageResult',\n id: message.id,\n result: performShellStorage(message, () => globalThis.localStorage)\n });\n return;\n }\n if (message.type === 'audio') {\n this.audio?.handle(message.request);\n return;\n }\n if (message.type === 'semantics') {\n this.mirror?.apply(message.update);\n return;\n }\n if (message.type === 'history') {\n this.applyHistory(message.action, message.url);\n }\n };\n\n /**\n * Reports the window's address to the worker, and keeps reporting it.\n *\n * Sent once at start-up as well as on change, for the same reason\n * `reducedMotion` is: an app opened directly at a url must start on\n * the screen that url names, not on its root.\n */\n private attachHistory(): void {\n const history = createShellHistory(this.options.history);\n this.history = history;\n history.onChange(url => this.post({ type: 'url', url }));\n this.post({ type: 'url', url: history.url });\n }\n\n /**\n * Opens a popup for the render worker and tells it what happened.\n *\n * `noopener` is deliberately absent, though the sibling `openUrl`\n * above sets it. `window.open` answers `null` whenever `noopener` is\n * given, whether the window appeared or was refused, so a popup\n * opened that way could not be reported on, and reporting is the\n * whole reason this request exists rather than another `openUrl`.\n * The page opened is a different origin, so the opener reference it\n * gains is the ordinary one every OAuth popup has.\n *\n * A reply is posted on every path, including the throwing one, so the\n * promise on the other side always settles.\n */\n private openPopup(request: { id: number; url: string; name: string; width: number; height: number }): void {\n let opened = false;\n try {\n const features = `popup,width=${request.width},height=${request.height}`;\n opened = window.open(request.url, request.name, features) !== null;\n } catch {\n // A sandboxed frame throws rather than returning null.\n opened = false;\n }\n this.post({ type: 'popupResult', id: request.id, opened });\n }\n\n private applyHistory(action: 'push' | 'replace' | 'back' | 'forward', url?: string): void {\n const history = this.history;\n if (history === null) {\n return;\n }\n if (action === 'back') {\n history.back();\n } else if (action === 'forward') {\n history.forward();\n } else if (url !== undefined) {\n if (action === 'push') {\n history.push(url);\n } else {\n history.replace(url);\n }\n }\n }\n\n private post(message: ShellToRuntimeMessage): void {\n // A backstop only. An input forwarded from a DOM event carries the\n // event's own timestamp (see `epochFromEvent`); this covers the\n // few that have no event behind them, and costs a listener that\n // forgot to stamp a reading that is late by however long the shell\n // took to get here.\n if (isInputMessage(message) && message.at === undefined) {\n (message as { at?: number }).at = epochNow();\n }\n this.renderWorker?.postMessage(message);\n }\n\n private observeResize(element: HTMLElement): void {\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n this.resizeObserver = new ResizeObserver(entries => {\n const entry = entries[0];\n if (entry === undefined) {\n return;\n }\n // The host has changed shape, so the canvas inside it has very\n // likely moved on the page as well as grown; whatever\n // `attachInput` cached about where it is cannot be trusted.\n this.canvasOrigin = null;\n const { width, height } = entry.contentRect;\n if (width > 0 && height > 0) {\n this.requestResize({ width, height, dpr: window.devicePixelRatio || 1 });\n }\n });\n this.resizeObserver.observe(element);\n }\n\n /**\n * Sends a size to the worker, or holds it back until the worker has\n * caught up with the last one.\n *\n * At most one resize is in flight, for the reason `resizeInFlight`\n * gives: the alternative is a queue of sizes that are all wrong,\n * each bought with a layout and a paint. Overwriting the held size\n * rather than queueing it is the whole trick — a drag of any length\n * costs the worker one layout per acknowledgement rather than one\n * per refresh, and the size that eventually arrives is the size the\n * window ended at.\n */\n private requestResize(size: { width: number; height: number; dpr: number }): void {\n if (this.resizeInFlight) {\n this.heldResize = size;\n return;\n }\n this.resizeInFlight = true;\n this.post({ type: 'resize', ...size });\n }\n\n /**\n * The worker has applied a resize, so the one being held can go.\n *\n * The dimensions are compared against what is held because the\n * drag usually ends on the size that was already in flight: the\n * worker has it, and posting it again would buy a layout that\n * changes nothing. A held size that differs is the last one the\n * observer reported, and it becomes the resize in flight.\n *\n * There is no timer behind this. The worker acknowledges every\n * `resize` it is sent, including one it decides to drop, so an\n * acknowledgement is owed for as long as the worker lives — and a\n * worker that has stopped answering has stopped painting too, which\n * is not a wrong canvas size but a dead renderer, and is reported\n * as one. A genuinely lost acknowledgement would leave the canvas at\n * the last size the worker applied until the shell is remounted;\n * that is a trade this takes knowingly, in exchange for not having\n * a heuristic timeout re-sending sizes at a worker that is merely\n * slow.\n */\n private handleResized(applied: { width: number; height: number; dpr: number }): void {\n this.resizeInFlight = false;\n const held = this.heldResize;\n this.heldResize = null;\n if (held === null) {\n return;\n }\n if (held.width === applied.width && held.height === applied.height && held.dpr === applied.dpr) {\n return;\n }\n this.resizeInFlight = true;\n this.post({ type: 'resize', ...held });\n }\n\n /**\n * Forwards DOM input into the worker.\n *\n * UiPlatformAdapter is deliberately not reused here. It decides\n * whether to call preventDefault() from the returned event's\n * flags, and that answer lives in the worker and cannot come back\n * synchronously. The shell instead prevents the defaults that\n * matter — focus stealing on Tab, and the page's own select-all —\n * and lets the worker route everything else.\n *\n * The wheel is the one place where \"prevent it and be done\" is\n * wrong in both directions, so it reads a cached answer the worker\n * pushed ahead of the event. See `wouldConsumeWheel`.\n */\n /**\n * Whether the runtime will take this wheel, decided from the cached\n * scrollability rather than by asking.\n *\n * `preventDefault()` has to be called synchronously, inside the DOM\n * handler, and the runtime is a `postMessage` away — so the honest\n * answer arrives a frame late or not at all. The shell therefore\n * answers from what the worker last reported about the pointer's\n * scroll chain, which is the same bet a browser makes when it\n * scrolls on the compositor thread.\n *\n * Both mistakes it can make are bounded and recoverable. One frame\n * after a container reaches its edge, one wheel notch may still be\n * swallowed; one frame after it leaves its edge, one may leak to\n * the page. Neither is the failure this replaced, which was every\n * wheel over the canvas dying whether or not there was anything to\n * scroll.\n *\n * See `wheelConsumedBy` for the rule itself.\n */\n private wouldConsumeWheel(event: WheelEvent): boolean {\n return wheelConsumedBy(this.scrollability, event.deltaX, event.deltaY);\n }\n\n /**\n * Reads where the canvas is and remembers it.\n *\n * Only left and top are kept. The size is the ResizeObserver's\n * business and is already reported as a resize; what an input needs\n * from this rect is the origin to subtract.\n */\n private readCanvasOrigin(canvas: HTMLCanvasElement): { left: number; top: number } {\n const box = canvas.getBoundingClientRect();\n const origin = { left: box.left, top: box.top };\n this.canvasOrigin = origin;\n return origin;\n }\n\n /**\n * Posts the hover move being held for this frame, if there is one.\n *\n * Every other input this shell sends calls this first, and the\n * ordering is the whole reason it is one function rather than a flag\n * each listener consults. A held move that went out *after* the\n * press, release or wheel that superseded it would leave the worker\n * hovering a position the pointer had already left, and the hover\n * state of a widget is exactly what decides how the next event is\n * drawn.\n *\n * Only hover moves are ever held; see `onPointerMove`.\n */\n private flushPendingMove(): void {\n const move = this.pendingMove;\n this.dropPendingMove();\n if (move !== null) {\n this.post(move);\n }\n }\n\n /**\n * Forgets a held hover move and the frame that was to send it,\n * posting nothing. For a detach or a dispose, where the surface the\n * move was measured against is going away.\n */\n private dropPendingMove(): void {\n if (this.moveFrame !== null) {\n cancelAnimationFrame(this.moveFrame);\n this.moveFrame = null;\n }\n this.pendingMove = null;\n }\n\n private attachInput(canvas: HTMLCanvasElement): () => void {\n /**\n * Where a pointer is, in the canvas's own coordinates.\n *\n * The origin is cached rather than measured per event.\n * `getBoundingClientRect` is a synchronous style and layout flush\n * whenever the document is dirty, and this ran one on every\n * pointermove, pointerdown, pointerup and wheel — on the one\n * thread this whole architecture exists to keep free. The cache is\n * dropped whenever something the shell can hear says the canvas\n * may have moved: the ResizeObserver fires, an ancestor scrolls,\n * or the window resizes.\n *\n * What that trades away is exactness under movement nothing\n * announces — a CSS transition on an ancestor, an element\n * animated by a library that touches no scroll position — where\n * coordinates come out shifted by however far the canvas went.\n * `pointerdown` therefore takes a fresh reading, so every gesture\n * starts from the truth and a single press re-syncs a stale\n * cache. A hover in the meantime can land in the wrong place; a\n * press, and the drag and click that follow it, cannot.\n */\n const toLocal = (clientX: number, clientY: number) => {\n const origin = this.canvasOrigin ?? this.readCanvasOrigin(canvas);\n return { x: clientX - origin.left, y: clientY - origin.top };\n };\n\n // Whether an editable already had focus when the press began. A\n // press that *starts* editing is the one that has to raise the\n // keyboard; one that lands on a field already being typed into must\n // not, since the keyboard is up and re-taking focus makes it blink.\n let editingAtPress = false;\n const onPointerDown = (event: PointerEvent): void => {\n // A press is the one event worth a layout flush: it starts a\n // gesture, it is rare next to a move, and it is the shell's only\n // chance to notice that something moved the canvas without\n // telling anyone.\n this.canvasOrigin = null;\n this.flushPendingMove();\n const { x, y } = toLocal(event.clientX, event.clientY);\n editingAtPress = this.proxy?.active ?? false;\n // While an editable has focus the proxy's textarea holds DOM\n // focus; the worker decides whether this press keeps it there.\n if (!(this.proxy?.active ?? false)) {\n canvas.focus();\n }\n // The canvas keeps this contact even once it leaves the element,\n // so a drag that runs off the edge is still delivered. A finger\n // is captured implicitly and a mouse is not; capturing both\n // makes the two behave the same.\n capturePointer(canvas, event.pointerId);\n this.post({\n type: 'pointerDown',\n x,\n y,\n buttons: event.buttons,\n modifiers: modifiersFrom(event),\n pointer: pointerDeviceOf(event),\n at: epochFromEvent(event)\n });\n };\n const onMouseDown = (event: MouseEvent): void => {\n if (this.proxy?.active ?? false) {\n // The default would move focus to the canvas before the worker\n // has said where the press landed.\n event.preventDefault();\n }\n };\n const onVisibilityChange = (): void => {\n this.post({ type: 'visibility', visible: document.visibilityState !== 'hidden' });\n };\n // Sent once here as well as on change, for the reason the reduced\n // motion listener below gives for itself: a tab that is *already*\n // hidden when it starts never fires `visibilitychange`, so without\n // this the runtime assumes it is on screen and lays out and paints\n // a canvas nobody can see. That is not hypothetical — a page opened\n // in a background tab, or behind another window, is exactly this.\n onVisibilityChange();\n // Sent once here as well as on change: someone who already has the\n // preference on must not watch the first screen animate.\n const detachReducedMotion = observeReducedMotion(reduced => {\n this.post({ type: 'reducedMotion', reduced });\n });\n const onPointerMove = (event: PointerEvent): void => {\n const { x, y } = toLocal(event.clientX, event.clientY);\n const move: PointerMoveMessage = {\n type: 'pointerMove',\n x,\n y,\n buttons: event.buttons,\n modifiers: modifiersFrom(event),\n pointer: pointerDeviceOf(event),\n at: epochFromEvent(event)\n };\n if (event.buttons !== 0) {\n // Something is pressed, so every point is forwarded. A drag, a\n // text selection, a scrollbar thumb and the touch scroller's\n // fling velocity are all computed from the stream itself, and\n // thinning it would quietly change what they do rather than\n // save work. Coalescing is only ever right for hover, where\n // the newest position is the whole of the information.\n this.flushPendingMove();\n this.post(move);\n return;\n }\n this.pendingMove = move;\n // One frame, one hover hit-test. A high-rate mouse reports\n // hundreds of moves a second and the worker answers each with a\n // hit-test whose result the next one discards, so the held move\n // is overwritten and a single frame sends the last of them. The\n // timestamp posted is that newest event's, not the frame's, so\n // the latency reading still measures from the input.\n this.moveFrame ??= requestAnimationFrame(() => {\n this.moveFrame = null;\n this.flushPendingMove();\n });\n };\n const onPointerUp = (event: PointerEvent): void => {\n this.flushPendingMove();\n const { x, y } = toLocal(event.clientX, event.clientY);\n this.post({\n type: 'pointerUp',\n x,\n y,\n buttons: event.buttons,\n modifiers: modifiersFrom(event),\n pointer: pointerDeviceOf(event),\n at: epochFromEvent(event)\n });\n // The worker's answer to the press has arrived by now — a round\n // trip is a frame and a tap is not — so the proxy already holds\n // DOM focus, taken from a message rather than from a gesture. A\n // phone ignores that one. This is the last gesture task of the\n // press, and the only chance to ask again.\n if (!editingAtPress && (this.proxy?.active ?? false)) {\n this.proxy?.raiseKeyboard();\n }\n };\n const onPointerCancel = (event: PointerEvent): void => {\n this.flushPendingMove();\n this.post({ type: 'pointerCancel', pointer: pointerDeviceOf(event) });\n };\n const onWheel = (event: WheelEvent): void => {\n this.flushPendingMove();\n if (this.wouldConsumeWheel(event)) {\n event.preventDefault();\n }\n const { x, y } = toLocal(event.clientX, event.clientY);\n this.post({\n type: 'wheel',\n x,\n y,\n deltaX: event.deltaX,\n deltaY: event.deltaY,\n // Forwarded, not converted: what a line is worth is a policy\n // question and policy lives in the runtime.\n deltaMode: event.deltaMode,\n wheelDeltaY: wheelDeltaYOf(event),\n modifiers: modifiersFrom(event),\n at: epochFromEvent(event)\n });\n };\n const onKeyDown = (event: KeyboardEvent): void => this.forwardKeyDown(event);\n const onKeyUp = (event: KeyboardEvent): void => this.forwardKeyUp(event);\n /**\n * The canvas has moved on the page without necessarily changing\n * size: an ancestor scrolled, or the window resized and the page\n * reflowed around it.\n *\n * The cached origin is dropped rather than re-read, so the cost is\n * paid by the next event that actually needs a position and not by\n * the scroll — and a scroll that nothing is pointing at costs\n * nothing at all.\n */\n const onCanvasMayHaveMoved = (): void => {\n this.canvasOrigin = null;\n };\n\n canvas.addEventListener('mousedown', onMouseDown);\n document.addEventListener('visibilitychange', onVisibilityChange);\n canvas.addEventListener('pointerdown', onPointerDown);\n canvas.addEventListener('pointermove', onPointerMove);\n canvas.addEventListener('pointerup', onPointerUp);\n canvas.addEventListener('pointercancel', onPointerCancel);\n canvas.addEventListener('wheel', onWheel, { passive: false });\n canvas.addEventListener('keydown', onKeyDown);\n canvas.addEventListener('keyup', onKeyUp);\n // Capturing, because a scroll only reaches the scrolled element\n // and its ancestors otherwise, and it is an *ancestor* of the\n // canvas scrolling that moves the canvas. Passive, because this\n // never prevents one and a non-passive scroll listener on the\n // window is exactly the thing that keeps a browser from scrolling\n // off the main thread.\n window.addEventListener('scroll', onCanvasMayHaveMoved, { capture: true, passive: true });\n window.addEventListener('resize', onCanvasMayHaveMoved);\n\n return () => {\n detachReducedMotion();\n canvas.removeEventListener('mousedown', onMouseDown);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n canvas.removeEventListener('pointerdown', onPointerDown);\n canvas.removeEventListener('pointermove', onPointerMove);\n canvas.removeEventListener('pointerup', onPointerUp);\n canvas.removeEventListener('pointercancel', onPointerCancel);\n canvas.removeEventListener('wheel', onWheel);\n canvas.removeEventListener('keydown', onKeyDown);\n canvas.removeEventListener('keyup', onKeyUp);\n window.removeEventListener('scroll', onCanvasMayHaveMoved, { capture: true });\n window.removeEventListener('resize', onCanvasMayHaveMoved);\n // A frame still holding a hover move would post it against a\n // surface that is no longer listening, so it is dropped rather\n // than flushed.\n this.dropPendingMove();\n this.canvasOrigin = null;\n };\n }\n}\n\nfunction resolveHost(host: HTMLElement | string): HTMLElement {\n if (typeof host !== 'string') {\n return host;\n }\n const element = document.querySelector<HTMLElement>(host);\n if (element === null) {\n throw new Error(`Mount host '${host}' was not found.`);\n }\n return element;\n}\n\n/** Ctrl+A, or Cmd+A on a Mac: select every selectable text in the app. */\nfunction isSelectAll(event: KeyboardEvent): boolean {\n return (event.ctrlKey || event.metaKey) && !event.altKey && (event.key === 'a' || event.key === 'A');\n}\n\n/** Ctrl+F, or Cmd+F on a Mac: open the app's find bar. */\nfunction isFind(event: KeyboardEvent): boolean {\n return (event.ctrlKey || event.metaKey) && !event.altKey && (event.key === 'f' || event.key === 'F');\n}\n\n/**\n * Whether the runtime will take a wheel with these deltas, given what\n * it last said about the pointer's scroll chain.\n *\n * The shell has to answer this synchronously, inside the DOM handler,\n * and the runtime is a `postMessage` away — so it answers from the\n * last frame's report. That is the same bet a browser makes when it\n * scrolls on the compositor thread, and both mistakes it allows are\n * one frame long: a notch swallowed just after a container reached\n * its edge, or one leaked to the page just after it left it. Neither\n * is the failure this replaced, which was every wheel over the canvas\n * dying whether or not there was anything to scroll.\n *\n * The dominant axis decides, matching the runtime's own rule that a\n * container scrolls on one axis at a time. A wheel with no delta in\n * that axis is never consumed.\n */\nexport function wheelConsumedBy(scrollability: UiScrollability, deltaX: number, deltaY: number): boolean {\n if (Math.abs(deltaY) >= Math.abs(deltaX)) {\n if (deltaY > 0) {\n return scrollability.down;\n }\n return deltaY < 0 ? scrollability.up : false;\n }\n if (deltaX > 0) {\n return scrollability.right;\n }\n return deltaX < 0 ? scrollability.left : false;\n}\n\n/**\n * The logical size to start a runtime at, in CSS pixels.\n *\n * The canvas's border box is what the runtime draws into and what\n * pointer coordinates are measured against, so it is the measurement\n * that matters. It is zero before the first layout — a host mounted\n * while detached, or a test double — so the host's content box is the\n * fallback, and a fixed default the last resort.\n */\nexport function measure(canvas: HTMLCanvasElement, host: HTMLElement): { width: number; height: number } {\n const box = canvas.getBoundingClientRect();\n if (box.width > 0 && box.height > 0) {\n return { width: box.width, height: box.height };\n }\n const style = typeof getComputedStyle === 'function' ? getComputedStyle(host) : undefined;\n const pad = (value: string | undefined): number => parseFloat(value ?? '0') || 0;\n const width = host.clientWidth - pad(style?.paddingLeft) - pad(style?.paddingRight);\n const height = host.clientHeight - pad(style?.paddingTop) - pad(style?.paddingBottom);\n return { width: width > 0 ? width : 600, height: height > 0 ? height : 600 };\n}\n","import type { FrameworkChild } from '../ComponentElement';\nimport {\n CanvasPlatformSurface,\n observeViewportInsets,\n prepareInputSurface,\n UiPlatformAdapter,\n type UiNode,\n type CanvasHost,\n UiAnimationFrameClock,\n type UiFrameClockFactory\n} from 'gesso-core';\nimport { GessoRuntime, type FrameMetrics, type PatchSource, type RendererChoice } from './GessoRuntime';\nimport type { UiNodeReport } from './NodeReport';\nimport type { DevtoolsEvent, DevtoolsRequest } from './DevtoolsProtocol';\nimport type { ShellRequest } from './ShellService';\nimport { AudioSink } from './AudioSink';\nimport { EditingProxy, writeClipboard } from './EditingProxy';\nimport { SemanticsMirror } from './SemanticsMirror';\nimport { performShellStorage, shellStorageDenied } from './shellStorage';\nimport { observeColorScheme, type ColorSchemePreference } from './colorScheme';\nimport { observeReducedMotion } from './reducedMotion';\nimport { createShellHistory, type ShellHistory, type ShellHistoryOptions } from './shellHistory';\nimport { measure } from './worker/WorkerApp';\nimport type { ChannelRegistry } from '../channel/ChannelRegistry';\nimport type { ServiceRegistry } from '../service/ServiceRegistry';\nimport type { RouterRoutes } from '../router/RouterService';\nimport type { MediaOptions } from './MediaService';\nimport type { FontFamilyDeclaration } from './FontService';\n\nexport interface GessoAppOptions {\n host: HTMLElement;\n root: FrameworkChild;\n /** A registry built elsewhere, when some stores live in data workers. */\n services?: ServiceRegistry;\n channels?: ChannelRegistry;\n /** The routes a `RouterOutlet` in the tree resolves against. */\n routes?: RouterRoutes;\n /**\n * How the app's url is kept: `path` (pushState, the default in a\n * browser), `hash`, or `memory`. See `shellHistory`.\n */\n history?: ShellHistoryOptions;\n canvas?: CanvasHost;\n /** The rendering backend; see RendererChoice. Defaults to `auto`. */\n renderer?: RendererChoice;\n clock?: UiFrameClockFactory;\n /**\n * Set false to build the tree without attaching DOM input.\n * Handlers declared with `on*` props are still registered, so they\n * can be driven directly through `app.input`.\n */\n input?: boolean;\n /**\n * Set false to drop the off-screen DOM an assistive technology reads\n * (`SemanticsMirror`).\n *\n * On by default, because an application that is accessible only when\n * its author remembered a flag is an application that is not\n * accessible. The opt-out is for a host that mirrors the tree itself,\n * and for measuring what the mirror costs.\n */\n accessibility?: boolean;\n /**\n * The appearance the application is told about: `auto` (the default)\n * follows `prefers-color-scheme`, `light` and `dark` override it.\n * See `WorkerApp` for why this is an option and not only a setter.\n */\n colorScheme?: ColorSchemePreference;\n /**\n * Where the app's pictures come from: the image resolver, the icon\n * rasteriser and the video decoder.\n *\n * An option rather than a call on `MediaService` because the tree is\n * built inside the runtime's constructor and an `Image` in it asks\n * for its bitmap at that moment, so a resolver installed once there\n * is an app to install it on has already missed the first screen.\n */\n media?: MediaOptions;\n /** The font families the app's text may name; see `FontService`. */\n fonts?: readonly FontFamilyDeclaration[];\n}\n\n/**\n * Single-thread Gesso application.\n *\n * A thin DOM shell over GessoRuntime: it creates and sizes a canvas,\n * observes the host element, and forwards browser events into the\n * runtime's input controllers. All UI work — components, graph,\n * layout, rendering — belongs to the runtime and never touches the\n * DOM, which is what lets the same runtime host a render worker.\n *\n * This is the single-thread configuration. It is supported for tests,\n * headless rendering, and environments without OffscreenCanvas, but it\n * puts UI work on the main thread and so is not the default for an\n * interactive app.\n */\nexport class GessoApp {\n private readonly runtime: GessoRuntime;\n private readonly canvas: CanvasHost;\n private readonly host: HTMLElement;\n private readonly inputEnabled: boolean;\n private readonly accessibilityEnabled: boolean;\n private readonly adapter: UiPlatformAdapter;\n private readonly historyOptions: ShellHistoryOptions | undefined;\n\n private running = false;\n private resizeObserver: ResizeObserver | null = null;\n private proxy: EditingProxy | null = null;\n /** The one audio element, behind `AudioService`; see `AudioSink`. */\n private audio: AudioSink | null = null;\n private mirror: SemanticsMirror | null = null;\n private history: ShellHistory | null = null;\n private detachVisibility: (() => void) | null = null;\n private detachReducedMotion: (() => void) | null = null;\n /** Stops watching `prefers-color-scheme`; null while overridden. */\n private detachColorScheme: (() => void) | null = null;\n /** Stops watching `visualViewport` for the safe area and the keyboard. */\n private detachViewportInsets: (() => void) | null = null;\n /** The appearance this shell reports; watched or overridden. */\n private colorSchemePreference: ColorSchemePreference = 'auto';\n\n constructor(options: GessoAppOptions) {\n this.host = options.host;\n this.canvas = options.canvas ?? createCanvasElement();\n this.inputEnabled = options.input ?? true;\n this.accessibilityEnabled = options.accessibility ?? true;\n this.historyOptions = options.history;\n this.colorSchemePreference = options.colorScheme ?? 'auto';\n\n this.runtime = new GessoRuntime({\n root: options.root,\n canvas: this.canvas,\n renderer: options.renderer,\n services: options.services,\n channels: options.channels,\n routes: options.routes,\n media: options.media,\n fonts: options.fonts,\n clock: options.clock ?? (callback => new UiAnimationFrameClock(callback)),\n dpr: devicePixelRatio()\n });\n\n const input = this.runtime.input;\n this.adapter = new UiPlatformAdapter({\n pointerController: input.pointer,\n wheelController: input.wheel,\n keyboardController: input.keyboard\n });\n }\n\n /** The runtime services a component in this app can inject. */\n get services(): ServiceRegistry {\n return this.runtime.services;\n }\n\n /**\n * The platform adapter, for tests and for callers driving input from\n * a non-DOM source.\n */\n get input(): UiPlatformAdapter {\n return this.adapter;\n }\n\n /**\n * Starts the app: appends the canvas to the host, sizes it,\n * attaches input, and arms the frame scheduler.\n */\n mount(): void {\n if (this.running) {\n return;\n }\n this.running = true;\n\n if (isCanvasElement(this.canvas) && this.canvas.parentElement !== this.host) {\n this.host.appendChild(this.canvas);\n this.canvas.style.display = 'block';\n this.canvas.style.width = '100%';\n this.canvas.style.height = '100%';\n }\n\n this.observeResize();\n // The canvas's box rather than the host's: clientWidth/clientHeight\n // include the host's padding, and starting the runtime a padding\n // wider than the surface it draws on leaves the first frame scaled\n // and every pointer coordinate off by the same ratio. See\n // `measure` in worker/WorkerApp for the same reasoning on the\n // worker path.\n const box = isCanvasElement(this.canvas) ? measure(this.canvas, this.host) : undefined;\n this.resize(box?.width ?? this.canvas.width ?? 600, box?.height ?? this.canvas.height ?? 600);\n this.attachInput();\n this.attachHistory();\n this.attachViewportInsets();\n this.runtime.onCursor(cursor => {\n if (isCanvasElement(this.canvas)) {\n this.canvas.style.cursor = cursor ?? '';\n }\n });\n this.runtime.start();\n }\n\n /** Aligns patch delivery from worker-owned stores to the frame. */\n deferPatchesFrom(sources: readonly PatchSource[]): void {\n this.runtime.deferPatchesFrom(sources);\n }\n\n /**\n * Receives per-frame timings, mirroring WorkerAppOptions.onFrame so\n * the two configurations can be compared on equal terms.\n */\n onFrame(listener: ((metrics: FrameMetrics) => void) | null): void {\n this.runtime.onFrame(listener);\n }\n\n /** Resolves with the backend that ended up drawing. */\n get rendererReady(): Promise<'canvas2d' | 'webgpu'> {\n return this.runtime.rendererReady;\n }\n\n /** Resizes the drawing surface and schedules a repaint. */\n resize(width: number, height: number): void {\n this.runtime.resize(width, height, devicePixelRatio());\n }\n\n /** Turns the layout inspector on or off; see GessoRuntime.setInspectorEnabled. */\n /**\n * Replaces the root and rebuilds the tree, for hot module\n * replacement. See `GessoRuntime.reload` for what survives.\n */\n reload(root: FrameworkChild, services: readonly (new () => object)[] = []): void {\n this.runtime.reload(root, services);\n }\n\n setInspector(enabled: boolean): void {\n this.runtime.setInspectorEnabled(enabled);\n }\n\n /**\n * Chooses what the application is told about the appearance: `auto`\n * follows `prefers-color-scheme`, `light` and `dark` override it.\n *\n * The same method `WorkerApp` has, doing the same thing without the\n * protocol in the middle — which is the point of the two shells\n * having one surface.\n */\n setColorScheme(preference: ColorSchemePreference): void {\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.colorSchemePreference = preference;\n if (preference === 'auto') {\n this.detachColorScheme = observeColorScheme(scheme => this.runtime.setColorScheme(scheme));\n return;\n }\n this.runtime.setColorScheme(preference);\n }\n\n /** Receives a report on the hovered node while the inspector is on. */\n onInspect(listener: ((report: UiNodeReport | null) => void) | null): void {\n this.runtime.onInspect(listener);\n }\n\n /** Receives a devtools panel's answers and updates; see `WorkerApp.onDevtools`. */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void {\n this.runtime.onDevtools(listener);\n }\n\n /**\n * Answers a devtools panel, with the runtime in this thread. A\n * `console` request does nothing here: the page's console is already\n * the one the developer is reading.\n */\n devtools(request: DevtoolsRequest): void {\n this.runtime.handleDevtools(request);\n }\n\n /**\n * Receives the errors this configuration would otherwise only log:\n * a renderer that could not draw, and an application listener that\n * threw and was caught so the dispatch could continue.\n *\n * Mirrors `WorkerAppOptions.onError`, minus the two sources that\n * cannot arise here — nothing crosses a message boundary, and an\n * exception nothing catches is an ordinary main-thread error that\n * reaches `window` on its own.\n */\n onError(\n listener: ((message: string, stack: string | undefined, source: 'renderer' | 'listener') => void) | null\n ): void {\n this.runtime.onRendererError(listener === null ? null : message => listener(message, undefined, 'renderer'));\n this.runtime.onListenerError(listener === null ? null : (message, stack) => listener(message, stack, 'listener'));\n }\n\n debugRoot(): UiNode {\n return this.runtime.debugRoot();\n }\n\n /**\n * Stops the scheduler, detaches input, and removes the canvas.\n */\n dispose(): void {\n this.running = false;\n this.audio?.dispose();\n this.audio = null;\n this.runtime.onAudioRequest(null);\n this.proxy?.dispose();\n this.proxy = null;\n this.mirror?.dispose();\n this.mirror = null;\n this.detachVisibility?.();\n this.detachVisibility = null;\n this.detachReducedMotion?.();\n this.detachReducedMotion = null;\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.detachViewportInsets?.();\n this.detachViewportInsets = null;\n this.history?.dispose();\n this.history = null;\n this.adapter.detach();\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n if (isCanvasElement(this.canvas) && this.canvas.parentElement === this.host) {\n this.host.removeChild(this.canvas);\n }\n this.runtime.dispose();\n }\n\n private attachInput(): void {\n if (!this.inputEnabled || !isCanvasElement(this.canvas)) {\n // A headless or offscreen canvas has no DOM events to forward.\n return;\n }\n this.canvas.tabIndex = 0;\n prepareInputSurface(this.canvas);\n this.adapter.attach(new CanvasPlatformSurface(this.canvas));\n\n // Text editing goes through a hidden textarea, exactly as in the\n // worker configuration, so the IME works the same on both. Keys are\n // not forwarded from it: the surface above listens on `window` and\n // already sees them.\n const editing = this.runtime.input.editing;\n const canvas = this.canvas;\n this.proxy = new EditingProxy(canvas, {\n beforeInput: (inputType, data) => editing.beforeInput(inputType, data),\n compositionStart: () => editing.compositionStart(),\n compositionUpdate: (text, caret) => editing.compositionUpdate(text, caret),\n compositionEnd: text => editing.compositionEnd(text),\n paste: text => editing.paste(text),\n blur: () => this.runtime.input.focus.blur()\n });\n this.runtime.setTextInputSource('proxy');\n this.runtime.onEditingState(state => this.proxy?.update(state));\n // Sound, driven directly instead of over the protocol: the same\n // sink the worker configuration uses, which is the point of it not\n // knowing where its requests come from.\n this.audio = new AudioSink({\n sample: sample => this.runtime.applyAudioSample(sample),\n action: action => this.runtime.applyAudioAction(action)\n });\n this.runtime.onAudioRequest(request => this.audio?.handle(request));\n this.attachSemanticsMirror(canvas);\n if (typeof document !== 'undefined') {\n const onVisibility = (): void => this.runtime.setVisible(document.visibilityState !== 'hidden');\n document.addEventListener('visibilitychange', onVisibility);\n this.detachVisibility = () => document.removeEventListener('visibilitychange', onVisibility);\n }\n this.detachReducedMotion = observeReducedMotion(reduced => this.runtime.setReducedMotion(reduced));\n this.setColorScheme(this.colorSchemePreference);\n }\n\n /**\n * Mounts the off-screen DOM an assistive technology reads.\n *\n * The same class the worker configuration uses, driven directly\n * instead of over the protocol — which is the point of it being a\n * DOM class that knows nothing about where its updates come from.\n * Keys are not forwarded from it: `CanvasPlatformSurface` listens on\n * `window` and already sees them, exactly as for the editing proxy.\n */\n private attachSemanticsMirror(canvas: HTMLCanvasElement): void {\n if (!this.accessibilityEnabled) {\n return;\n }\n const mirror = new SemanticsMirror(\n canvas,\n { action: action => this.runtime.applySemanticsAction(action) },\n this.proxy\n );\n this.mirror = mirror;\n this.runtime.onSemantics(update => mirror.apply(update));\n }\n\n /**\n * Connects the router to the window's address bar.\n *\n * Outside `attachInput` deliberately: an app mounted with\n * `input: false` — a test, a headless render — still routes, and a\n * router that silently stopped syncing in that configuration would\n * be a difference between the two hosts that nothing declared.\n */\n private attachHistory(): void {\n const history = createShellHistory(this.historyOptions);\n this.history = history;\n this.runtime.onShellRequest(request => this.handleShellRequest(request, history));\n history.onChange(url => this.runtime.setUrl(url));\n this.runtime.setUrl(history.url);\n }\n\n /**\n * Reports the platform's safe area and soft keyboard to the runtime.\n *\n * Outside `attachInput` for the reason `attachHistory` is: the insets\n * are a layout fact, and an app mounted with `input: false` still\n * lays out. Nothing is decided here. `observeViewportInsets` reads\n * `visualViewport` and hands over four numbers, and the runtime\n * publishes them into the application's inset registry, which is the\n * same route `WorkerApp` takes with a message in the middle.\n */\n private attachViewportInsets(): void {\n this.detachViewportInsets = observeViewportInsets(insets => this.runtime.setViewportInsets(insets));\n }\n\n private handleShellRequest(request: ShellRequest, history: ShellHistory): void {\n if (request.type === 'history') {\n if (request.action === 'push') {\n history.push(request.url);\n } else if (request.action === 'replace') {\n history.replace(request.url);\n } else if (request.action === 'back') {\n history.back();\n } else {\n history.forward();\n }\n return;\n }\n if (!isCanvasElement(this.canvas)) {\n // No document to write a clipboard through or open a window from.\n // A popup still has to be answered, because a promise nobody\n // settles is worse than a popup nobody opened, and a storage\n // request is answered for the same reason.\n if (request.type === 'popup') {\n this.runtime.settlePopup(request.id, false);\n } else if (request.type === 'storage') {\n this.runtime.settleStorage(request.id, shellStorageDenied());\n }\n return;\n }\n if (request.type === 'clipboard') {\n writeClipboard(request.text, this.canvas.ownerDocument);\n return;\n }\n const view = this.canvas.ownerDocument.defaultView;\n if (request.type === 'storage') {\n // Performed here rather than posted, because in this\n // configuration the shell and the render side are the same\n // thread; the answer is the same either way.\n this.runtime.settleStorage(\n request.id,\n performShellStorage(request, () => view?.localStorage)\n );\n return;\n }\n if (request.type === 'popup') {\n // Same shape as `WorkerApp.openPopup`, and `noopener` is absent\n // for the same reason: it would make the answer always `null`.\n let opened = false;\n try {\n const features = `popup,width=${request.width},height=${request.height}`;\n opened = (view?.open(request.url, request.name, features) ?? null) !== null;\n } catch {\n opened = false;\n }\n this.runtime.settlePopup(request.id, opened);\n return;\n }\n view?.open(request.url, '_blank', 'noopener,noreferrer');\n }\n\n private observeResize(): void {\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n this.resizeObserver = new ResizeObserver(entries => {\n const entry = entries[0];\n if (entry === undefined) {\n return;\n }\n // contentRect is the logical CSS size. The surface owns the\n // backing store, so the canvas attributes are never written here:\n // round-tripping the size through them once meant the second\n // resize read device pixels back as logical pixels.\n this.resize(entry.contentRect.width, entry.contentRect.height);\n });\n this.resizeObserver.observe(this.host);\n }\n}\n\nfunction createCanvasElement(): HTMLCanvasElement {\n return document.createElement('canvas');\n}\n\nfunction isCanvasElement(canvas: CanvasHost): canvas is HTMLCanvasElement {\n return typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement;\n}\n\nfunction devicePixelRatio(): number {\n return typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n}\n","import { createComponent } from '../createComponent';\nimport type { FrameworkChild } from '../ComponentElement';\nimport type { ComponentType } from '../FunctionComponent';\nimport type { WorkerHandle } from '../worker/WorkerPorts';\nimport { ServiceRegistry } from '../service/ServiceRegistry';\nimport { createChannelRegistry, type ChannelRegistration } from '../channel/createChannelRegistry';\nimport type { ChannelSource } from '../channel/provide';\nimport type { ChannelToken } from '../channel/ChannelToken';\nimport type { ColorSchemePreference } from './colorScheme';\nimport { GessoApp } from './GessoApp';\nimport type { RouterRoutes } from '../router/RouterService';\nimport type { ShellHistoryOptions } from './shellHistory';\nimport type { FrameMetrics, RendererChoice } from './GessoRuntime';\nimport type { MediaOptions } from './MediaService';\nimport type { FontFamilyDeclaration } from './FontService';\nimport type { UiNodeReport } from './NodeReport';\nimport type { DevtoolsEvent, DevtoolsRequest } from './DevtoolsProtocol';\n\n/**\n * Fluent builder for the single-thread configuration.\n */\nexport class GessoAppBuilder {\n private readonly channelRegistrations: ChannelRegistration[] = [];\n private readonly serviceRegistrations: (new () => object)[] = [];\n private frameListener: ((metrics: FrameMetrics) => void) | undefined;\n private inspectListener: ((report: UiNodeReport | null) => void) | undefined;\n private devtoolsListener: ((event: DevtoolsEvent) => void) | null = null;\n private errorListener:\n | ((message: string, stack: string | undefined, source: 'renderer' | 'listener') => void)\n | undefined;\n private rendererChoice: RendererChoice | undefined;\n private routes: RouterRoutes | undefined;\n private historyOptions: ShellHistoryOptions | undefined;\n private mediaOptions: MediaOptions | undefined;\n private fontDeclarations: readonly FontFamilyDeclaration[] | undefined;\n private app: GessoApp | undefined;\n private colorSchemePreference: ColorSchemePreference = 'auto';\n\n constructor(private root: FrameworkChild | ComponentType) {}\n\n /**\n * Registers a channel.\n *\n * With `worker`, the channel's data lives there — api, store, domain\n * and view models, all plain code the framework never sees. With\n * `source`, it is fed from this thread; either way the same patches\n * cross the same kind of port, so a channel can be moved into a\n * worker later without a view noticing.\n */\n useChannel<V extends object, C extends object>(\n token: ChannelToken<V, C>,\n options: { worker?: WorkerHandle | (() => Worker); source?: ChannelSource<V, C> }\n ): this {\n this.channelRegistrations.push({\n token: token as unknown as ChannelToken<never, never>,\n worker: options.worker,\n source: options.source as unknown as ChannelSource<never, never>\n });\n return this;\n }\n\n /**\n * Registers a runtime service: a plain class this thread constructs\n * once and hands to whoever injects it.\n *\n * For things that belong to the render thread and could not leave it\n * — something holding a `UiNode`, a decoded bitmap, or a generator\n * feeding bound props at frame rate. Application state goes through\n * `useChannel` instead, and the test is the usual one: if it\n * survives a reload or another screen cares about it, it is not a\n * service.\n */\n useService(ServiceClass: new () => object): this {\n this.serviceRegistrations.push(ServiceClass);\n return this;\n }\n\n /**\n * Declares the app's routes, which is all it takes to make a\n * `RouterOutlet` in the tree resolve them. Mirrors\n * `renderRoot().useRoutes`.\n */\n useRoutes(routes: RouterRoutes): this {\n this.routes = routes;\n return this;\n }\n\n /**\n * Declares where the app's pictures come from: the image resolver,\n * the icon rasteriser and the video decoder. Mirrors\n * `renderRoot().useMedia`.\n *\n * createApp(AppRoot).useMedia({ resolver: new CachingResolver() });\n *\n * Declared here rather than set on `MediaService` afterwards because\n * the tree is built inside the runtime's constructor and an `Image`\n * in it asks for its bitmap at that moment. Whatever is left out,\n * the runtime builds and owns; whatever is passed stays the\n * caller's, and the runtime will not dispose it.\n */\n useMedia(media: MediaOptions): this {\n this.mediaOptions = media;\n return this;\n }\n\n /**\n * Declares the font families the app's text may name; the\n * single-thread twin of `renderRoot().useFonts`, loading into\n * `document.fonts` instead of a worker's set.\n */\n useFonts(families: readonly FontFamilyDeclaration[]): this {\n this.fontDeclarations = families;\n return this;\n }\n\n /**\n * How the app's url is kept: `path` (pushState, the default in a\n * browser), `hash`, or `memory`. See `shellHistory`.\n */\n useHistory(history: ShellHistoryOptions): this {\n this.historyOptions = history;\n return this;\n }\n\n /**\n * Pins the rendering backend. Without this the app is on `auto`:\n * WebGPU where the browser has it, Canvas2D elsewhere.\n */\n renderer(choice: RendererChoice): this {\n this.rendererChoice = choice;\n return this;\n }\n\n /**\n * Receives per-frame timings, mirroring the worker configuration's\n * onFrame option.\n */\n onFrame(listener: (metrics: FrameMetrics) => void): this {\n this.frameListener = listener;\n return this;\n }\n\n /**\n * Receives the hovered node's layout explanation while the inspector\n * is on, mirroring WorkerAppOptions.onInspect.\n */\n onInspect(listener: (report: UiNodeReport | null) => void): this {\n this.inspectListener = listener;\n return this;\n }\n\n /**\n * Receives errors the runtime would otherwise only log, mirroring\n * WorkerAppOptions.onError. See `GessoApp.onError`.\n */\n onError(listener: (message: string, stack: string | undefined, source: 'renderer' | 'listener') => void): this {\n this.errorListener = listener;\n return this;\n }\n\n /**\n * Turns the layout inspector on or off on the mounted app, mirroring\n * WorkerApp.setInspector. A no-op before mountSync.\n */\n setInspector(enabled: boolean): void {\n this.app?.setInspector(enabled);\n }\n\n /**\n * Receives a devtools panel's answers and updates, mirroring\n * `WorkerApp.onDevtools`. Remembered before `mountSync`, so a panel\n * connected to the builder hears the app once there is one.\n */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void {\n this.devtoolsListener = listener;\n this.app?.onDevtools(listener);\n }\n\n /** Answers a devtools panel, mirroring `WorkerApp.devtools`. A no-op before mountSync. */\n devtools(request: DevtoolsRequest): void {\n this.app?.devtools(request);\n }\n\n /**\n * Replaces the root and rebuilds the tree, for hot module\n * replacement, mirroring `RenderWorkerApp.reload`.\n *\n * Before `mountSync` it changes which root will be built, so an\n * entry that accepts a replacement during startup is not a race.\n */\n reload(root: FrameworkChild | ComponentType, services: readonly (new () => object)[] = []): this {\n this.root = root;\n this.app?.reload(typeof root === 'function' ? createComponent(root as ComponentType) : root, services);\n return this;\n }\n\n /**\n * Chooses what the application is told about the appearance,\n * mirroring `WorkerApp.setColorScheme`.\n *\n * Unlike `setInspector`, this is remembered when it is called before\n * `mountSync`: the appearance decides what the first frame looks\n * like, so a host that already knows the reader's choice must be\n * able to say so before there is an app to tell.\n */\n setColorScheme(preference: ColorSchemePreference): this {\n this.colorSchemePreference = preference;\n this.app?.setColorScheme(preference);\n return this;\n }\n\n /**\n * Mounts the app on the calling thread.\n *\n * Named for what it costs: everything — components, layout and\n * rendering — runs here, so main-thread work delays frames. Use the\n * worker configuration for interactive apps.\n *\n * Returns a dispose function that tears the app down.\n */\n mountSync(host: HTMLElement | string): () => void {\n const element = typeof host === 'string' ? requireElement(host) : host;\n const rootElement = typeof this.root === 'function' ? createComponent(this.root as ComponentType) : this.root;\n const channels = createChannelRegistry(this.channelRegistrations);\n const services = new ServiceRegistry();\n for (const ServiceClass of this.serviceRegistrations) {\n services.register(ServiceClass);\n }\n const app = new GessoApp({\n host: element,\n root: rootElement,\n channels: channels.registry,\n services,\n routes: this.routes,\n media: this.mediaOptions,\n fonts: this.fontDeclarations,\n history: this.historyOptions,\n renderer: this.rendererChoice,\n colorScheme: this.colorSchemePreference\n });\n app.deferPatchesFrom(channels.registry.all());\n if (this.frameListener !== undefined) {\n app.onFrame(this.frameListener);\n }\n if (this.inspectListener !== undefined) {\n app.onInspect(this.inspectListener);\n }\n if (this.errorListener !== undefined) {\n app.onError(this.errorListener);\n }\n if (this.devtoolsListener !== null) {\n app.onDevtools(this.devtoolsListener);\n }\n this.app = app;\n app.mount();\n return () => {\n this.app = undefined;\n app.dispose();\n channels.dispose();\n };\n }\n}\n\nfunction requireElement(selector: string): HTMLElement {\n const element = document.querySelector<HTMLElement>(selector);\n if (element === null) {\n throw new Error(`Mount host '${selector}' was not found.`);\n }\n return element;\n}\n","import type { FrameworkChild } from '../ComponentElement';\nimport type { ComponentType } from '../FunctionComponent';\nimport { GessoAppBuilder } from './GessoAppBuilder';\nimport { WorkerApp, type WorkerAppOptions } from './worker/WorkerApp';\n\n/**\n * What a shell passes when `gesso-vite-plugin` supplies the workers.\n *\n * The only difference from `WorkerAppOptions` is that `renderWorker` is\n * optional, because the plugin writes it: it rewrites `createApp(...)`\n * so that its own factories are merged under whatever the author wrote,\n * and the literal `new Worker(new URL(...))` it emits is the same\n * expression an author would have written by hand. Writing it by hand\n * is still supported and still documented; what the\n * plugin removes is the obligation to.\n *\n * Without the plugin and without a `renderWorker`, this throws with the\n * two ways to fix it, rather than mounting an application that would\n * quietly never draw.\n */\nexport interface CreateAppOptions extends Omit<WorkerAppOptions, 'renderWorker'> {\n /** Spawns the render worker. Written by the plugin when it is absent. */\n renderWorker?: WorkerAppOptions['renderWorker'];\n /**\n * The `name` to give every worker the plugin constructs, which is\n * how a worker reads a flag that only the page's url carries: the\n * playground's `?still` and Segue's are the same trick.\n *\n * Ignored when `renderWorker` is written by hand, since the factory\n * then names the worker itself.\n */\n workerName?: string;\n}\n\n/**\n * Creates a Gesso application.\n *\n * Two configurations, because a class reference cannot cross\n * postMessage: the root component has to already be inside the worker\n * that renders it.\n *\n * Worker-hosted (the default for an interactive app) — all UI work\n * happens off the main thread. With `gesso-vite-plugin` in the Vite\n * config, the shell names no worker at all:\n *\n * // main.ts\n * createApp({ history: { mode: 'path' } }).mount('#app');\n *\n * // app.render.worker.ts\n * renderRoot(AppRoot).useChannel(Catalog);\n *\n * and without it, the same thing said out loud:\n *\n * createApp({\n * renderWorker: () => new Worker(new URL('./app.render.worker.ts', import.meta.url), { type: 'module' }),\n * appLogicWorker: () => new Worker(new URL('./app.logic.worker.ts', import.meta.url), { type: 'module' })\n * }).mount('#app');\n *\n * Single-thread — for tests, headless rendering, and environments\n * without OffscreenCanvas:\n *\n * createApp(AppRoot).useChannel(Catalog, { source }).mountSync('#app');\n */\nexport function createApp(options?: CreateAppOptions): WorkerApp;\nexport function createApp(root: FrameworkChild | ComponentType): GessoAppBuilder;\nexport function createApp(arg?: CreateAppOptions | FrameworkChild | ComponentType): WorkerApp | GessoAppBuilder {\n if (isWorkerAppOptions(arg)) {\n if (arg?.renderWorker === undefined) {\n throw new Error(\n 'createApp() was given no render worker. Add `gesso()` from gesso-vite-plugin to the Vite config, which ' +\n \"writes the construction, or pass one: renderWorker: () => new Worker(new URL('./worker.ts', \" +\n \"import.meta.url), { type: 'module' }).\"\n );\n }\n return new WorkerApp(arg as WorkerAppOptions);\n }\n return new GessoAppBuilder(arg);\n}\n\n/**\n * Whether the argument describes the worker configuration.\n *\n * It used to be the presence of `renderWorker`, which no longer\n * distinguishes anything: the plugin's whole point is that the option\n * is absent. So the test is the other way round — anything that is\n * recognisably a child is one, and everything else is options. A\n * `UiElement` carries `type`, a `ComponentElement` carries `kind`, an\n * observable child carries `subscribe`, and a component is a function;\n * `WorkerAppOptions` has none of those and never will, because each is\n * the identity of something that goes in a tree.\n */\nfunction isWorkerAppOptions(value: unknown): value is CreateAppOptions | undefined {\n if (value === undefined) {\n return true;\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n return !('type' in value) && !('kind' in value) && !('subscribe' in value);\n}\n","import { UiNodeType, type UiNode, type UiShortcutRegistry } from 'gesso-core';\n\nimport type { UndoStack } from './UndoStack';\n\nexport interface UndoShortcutOptions {\n /** The registry the `shortcuts` modifier feeds. */\n readonly registry: UiShortcutRegistry;\n readonly stack: UndoStack;\n /**\n * The node keyboard input is going to, usually\n * `() => ctx.inject(FocusService).focused.value`.\n *\n * Given one, Mod+Z is skipped while focus is inside something being\n * typed into, so a text field keeps its own undo. Leave it out only\n * in an application with no editable text: without it the\n * application's stack takes every Mod+Z, including the ones a person\n * pressed to unmake the last three letters they typed.\n */\n readonly focused?: () => UiNode | null;\n /** The heading a palette groups the two commands under. */\n readonly group?: string;\n /** Default `'Mod+Z'`. */\n readonly undoKeys?: string;\n /** Default `'Mod+Shift+Z'` and `'Mod+Y'`, the two spellings in use. */\n readonly redoKeys?: readonly string[];\n}\n\n/**\n * Registers undo and redo on the application's shortcut registry.\n *\n * One key path rather than two: the registry is the place a command's\n * keys are declared, so that a palette listing the\n * available commands and the handler that fires them ask the same\n * question. Undo is exactly the command that would otherwise grow a\n * second path, because every application reaches for a root\n * `onKeyDown` for it first.\n *\n * const undo = ctx.inject(UndoStack);\n * ctx.onUnmount(registerUndoShortcuts({ registry, stack: undo, focused: () => focus.focused.value }));\n *\n * The returned function unregisters both, so the pair's lifetime is\n * whatever registered them.\n *\n * `when` is what keeps the two honest: a stack with nothing on it\n * offers no undo, so the key falls through to whatever else wants it\n * and a palette does not list a command that would do nothing.\n */\nexport function registerUndoShortcuts(options: UndoShortcutOptions): () => void {\n const { registry, stack, focused, group } = options;\n const available = (): boolean => !typingInto(focused?.() ?? null);\n const undone = registry.register({\n keys: options.undoKeys ?? 'Mod+Z',\n label: 'Undo',\n ...(group === undefined ? {} : { group }),\n when: () => stack.canUndo.value && available(),\n run: () => void stack.undo()\n });\n const redone = (options.redoKeys ?? ['Mod+Shift+Z', 'Mod+Y']).map(keys =>\n registry.register({\n keys,\n label: 'Redo',\n ...(group === undefined ? {} : { group }),\n when: () => stack.canRedo.value && available(),\n run: () => void stack.redo()\n })\n );\n return () => {\n undone();\n for (const remove of redone) {\n remove();\n }\n };\n}\n\n/**\n * Whether keys reaching this node are being typed into something.\n *\n * The same walk `UiShortcutRegistry` makes for a shortcut with no\n * modifier at all, made here for a shortcut that has one. The\n * registry's own rule cannot cover this case: Mod+Z carries a\n * modifier, so by the registry's reckoning it is not a letter somebody\n * is trying to type, and it takes a key that\n * `EditableTextModel.undo` is waiting for. The keyboard controller\n * applies the editing keys **after** the event has been dispatched, so\n * a root shortcut sees Mod+Z first and would win every time.\n */\nfunction typingInto(node: UiNode | null): boolean {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current.type === UiNodeType.EditableText) {\n return true;\n }\n }\n return false;\n}\n","import type { ShellService, ShellStorageResult } from '../app/ShellService';\nimport type { StorageAdapter, StorageOutcome, StorageRead } from './StorageAdapter';\n\nexport interface ShellStorageOptions {\n /**\n * What every key is written under, so an application's records are\n * recognisable in devtools and cannot collide with another script's\n * on the same origin. Default `'gesso:'`.\n *\n * Applied on this side rather than on the shell's, because the shell\n * is asked to store a key and nothing more; deciding what the key\n * means is the application's work and belongs on the thread doing\n * the application's work.\n */\n readonly prefix?: string;\n}\n\n/**\n * `localStorage`, reached through the shell.\n *\n * The odd one of the three, and worth saying why it exists. It is\n * synchronous on the window and unreachable from a worker, so a render\n * thread asking for it pays a round trip; it holds a few megabytes at\n * most; and writing to it blocks the main thread, which is the thread\n * the framework works hardest to leave alone. None of that makes it a\n * good place for an application's state, and all of it is beside the\n * point for the thing it is actually good for: a small preference that\n * something outside the application also reads, or that has to be\n * there before the first frame of the *next* visit rather than the\n * next frame of this one.\n *\n * Use `OpfsStorage` or `IndexedDbStorage` for anything else.\n *\n * What happens on each failure:\n *\n * - **No shell** (a headless runtime, a spec): `denied`, at once.\n * - **The browser blocks `localStorage`**, which it does in some\n * private windows and under some site settings: `denied`. Reading\n * `window.localStorage` is what throws, so the failure arrives on\n * the first call rather than at start-up.\n * - **The quota is spent**: `full`. `localStorage` has the smallest\n * quota of the three and reaches it soonest, which is the other\n * reason not to keep an application's state here.\n * - **Anything else**: `failed`, with the message the shell reported.\n *\n * Every answer crosses the barrier as plain data: a string, a list of\n * strings, and one of four words.\n */\nexport class ShellStorage implements StorageAdapter {\n private readonly prefix: string;\n\n constructor(\n private readonly shell: ShellService,\n options: ShellStorageOptions = {}\n ) {\n this.prefix = options.prefix ?? 'gesso:';\n }\n\n async read(key: string): Promise<StorageRead> {\n const result = await this.ask('read', key);\n return { outcome: result.outcome, value: result.value, error: result.error };\n }\n\n async write(key: string, value: string): Promise<StorageOutcome> {\n return (await this.ask('write', key, value)).outcome;\n }\n\n async remove(key: string): Promise<StorageOutcome> {\n return (await this.ask('remove', key)).outcome;\n }\n\n async keys(): Promise<readonly string[]> {\n // The prefix is this store's namespace, so the keys it reports are\n // its own with the namespace taken back off. A listing that\n // included every other script's key would be a listing an\n // application could not act on.\n const result = await this.ask('keys', '');\n return result.keys.filter(key => key.startsWith(this.prefix)).map(key => key.slice(this.prefix.length));\n }\n\n private ask(op: 'read' | 'write' | 'remove' | 'keys', key: string, value?: string): Promise<ShellStorageResult> {\n return this.shell.requestStorage({\n op,\n key: op === 'keys' ? '' : `${this.prefix}${key}`,\n ...(value === undefined ? {} : { value })\n });\n }\n}\n","import { BehaviorSubject, type Observable } from 'rxjs';\n\nimport {\n Box,\n motion,\n percent,\n type MotionStateInput,\n type MotionTiming,\n type UiChild,\n type UiElement,\n type UiLength\n} from 'gesso-core';\nimport type { ComponentContext, Inputs } from './FunctionComponent';\n\n/**\n * Keeps a child on screen while it leaves.\n *\n * Animation closed with one thing deliberately open: *\"No exit\n * animation. A dialog still leaves on the frame it closes. An exit\n * needs the tree to keep a node that has logically left, which is a\n * component-lifetime question F4 does not settle.\"* This settles it,\n * and the answer is that the node is not kept — the **definition** is.\n *\n * **Why not in the reconciler.** The obvious move is to make\n * `UiGraphBuilder.removeSubtree` hold a node that wants to animate\n * out. It does not survive contact. The held node has no definition,\n * so every later reconcile has to be taught to look past it; its id is\n * still in the graph's index, so the same element returning collides\n * with it; and its component host must not be released, which means\n * the resolver has to learn about animation. That is three new\n * problems in the one part of the system whose correctness everything\n * else rests on, bought for one feature.\n *\n * Keeping the definition instead costs nothing anywhere else. This\n * component subscribes to a list of children, notices when one stops\n * appearing in it, and goes on rendering that one — as an ordinary\n * child, with an ordinary host — until its exit animation reports that\n * it is done. Then it stops, and the ordinary removal path runs,\n * unchanged and unaware that anything unusual happened. It is what\n * `AnimatePresence` does and what a Svelte `transition:` does, for\n * this reason.\n *\n * **It is a stack, and that is not incidental.** Each child is laid\n * out absolutely inside a positioned container, so a child on its way\n * out holds no space and the one arriving does not wait for it. That\n * is exactly what a screen transition wants — the two screens overlap\n * for as long as the transition lasts — and it is why this is the\n * right shape for a route outlet, a dialog and a toast, and the wrong\n * shape for a row leaving a list, where the neighbours must close the\n * gap behind it. A list wants `animateLayout` on the rows that stay.\n */\nexport interface PresenceProps {\n /**\n * The children, keyed. A child that stops appearing is animated out\n * rather than removed.\n *\n * Identity is the element's `key`. Without one a child is keyed by\n * its position, which for the single swapping child this is most\n * often used for is the same thing — but a list must key its\n * children, exactly as it must anywhere else in this framework.\n */\n children?: UiChild | readonly UiChild[];\n /** Where a child starts when it appears. Omitted means it just appears. */\n enter?: MotionStateInput;\n /**\n * Where a child goes as it leaves. Omitted means it just goes, on\n * the frame it stops being asked for, exactly as it did before this\n * component existed.\n *\n * An `enter` or `exit` that changes opacity dims everything inside\n * the child, including any `sharedElement` morphing across the\n * change — see `RouteTransition` for why the two do not compose and\n * what it looks like when you try.\n */\n exit?: MotionStateInput;\n /**\n * `together` (the default) — the arriving child enters while the\n * departing one leaves, and for a moment both are on screen. What a\n * shared-element transition needs, since the morph is measured off\n * the element that is still standing there.\n *\n * `wait` — the departing child finishes leaving before the arriving\n * one is built at all. Right when the two would read as clutter on\n * top of each other, and wrong whenever anything is shared.\n */\n mode?: 'together' | 'wait';\n /** How the enter and the exit are timed. One timing serves both. */\n timing?: MotionTiming;\n /** The container's size. Both default to filling whatever it is given. */\n width?: UiLength | number;\n height?: UiLength | number;\n /**\n * A ceiling on how long a child may take to leave, in milliseconds.\n *\n * Insurance rather than policy. An exit that never settles — a\n * spring given absurd numbers, an element unmounted before it ever\n * had a frame — would otherwise keep a whole screen mounted for\n * good, and a leak that looks like a rendering bug is the worst\n * kind. Two seconds by default, far longer than any exit anyone\n * should be writing.\n */\n exitTimeout?: number;\n}\n\n/** One child that is on screen: its definition, and whether it is going. */\ninterface PresenceEntry {\n readonly key: string;\n child: UiChild;\n leaving: boolean;\n /** Bumped when a child comes back before it finished leaving. */\n generation: number;\n}\n\nconst DEFAULT_EXIT_TIMEOUT_MS = 2000;\n\nexport function Presence(inputs: Inputs<PresenceProps>, ctx: ComponentContext): UiChild {\n /** What is on screen: departing children first, so arrivals paint over them. */\n let entries: PresenceEntry[] = [];\n /** In `wait` mode, the children held back until the screen is clear. */\n let held: PresenceEntry[] | null = null;\n const timers = new Map<string, ReturnType<typeof setTimeout>>();\n const output = new BehaviorSubject<readonly UiChild[]>([]);\n let disposed = false;\n\n const clearTimer = (key: string): void => {\n const timer = timers.get(key);\n if (timer !== undefined) {\n clearTimeout(timer);\n timers.delete(key);\n }\n };\n\n const layerFor = (entry: PresenceEntry): UiChild =>\n Box(\n {\n key: entry.key,\n // A stack: every child fills the container and none of them\n // holds space from another. See the note on this component.\n position: 'absolute',\n left: 0,\n top: 0,\n width: percent(100),\n height: percent(100),\n // Each layer is its own top layer. A `lift`ed element inside a\n // child escapes whatever clips it within that child, and stops\n // there: without this it would be painted over every layer,\n // and a screen on its way out would fly its morphing artwork\n // across the screen arriving behind it. See the `liftBoundary`\n // property.\n liftBoundary: true,\n modifiers: [\n motion({\n ...inputs.timing.value,\n initial: inputs.enter.value,\n state: entry.leaving ? (inputs.exit.value ?? null) : null,\n onSettled: entry.leaving ? () => settle(entry) : undefined\n })\n ]\n },\n entry.child\n );\n\n const emit = (): void => {\n if (!disposed) {\n output.next(entries.map(layerFor));\n }\n };\n\n /** A child has finished leaving, so it may finally be dropped. */\n const settle = (entry: PresenceEntry): void => {\n const index = entries.indexOf(entry);\n if (!entry.leaving || index === -1) {\n return;\n }\n clearTimer(entry.key);\n entries.splice(index, 1);\n releaseHeld();\n emit();\n };\n\n /**\n * `wait` mode lets its arrivals in once the last departure is over,\n * which is the only moment they can be built without overlapping\n * what they replace.\n */\n const releaseHeld = (): void => {\n if (held === null || entries.some(entry => entry.leaving)) {\n return;\n }\n entries.push(...held);\n held = null;\n };\n\n const armTimeout = (entry: PresenceEntry): void => {\n clearTimer(entry.key);\n timers.set(\n entry.key,\n setTimeout(() => {\n timers.delete(entry.key);\n settle(entry);\n }, inputs.exitTimeout.value ?? DEFAULT_EXIT_TIMEOUT_MS)\n );\n };\n\n const apply = (children: UiChild | readonly UiChild[] | undefined): void => {\n const next = normalize(children);\n const wanted = new Set(next.map(child => child.key));\n // Anything still wanted is refreshed in place, so a child that came\n // back mid-exit keeps its node — and its scroll position, and its\n // component's state — rather than being rebuilt from nothing.\n const staying: PresenceEntry[] = [];\n for (const { key, child } of next) {\n const existing = entries.find(entry => entry.key === key) ?? held?.find(entry => entry.key === key);\n if (existing === undefined) {\n staying.push({ key, child, leaving: false, generation: 0 });\n continue;\n }\n if (existing.leaving) {\n clearTimer(key);\n existing.leaving = false;\n existing.generation++;\n }\n existing.child = child;\n staying.push(existing);\n }\n const hasExit = inputs.exit.value !== undefined;\n const leaving = hasExit ? entries.filter(entry => entry.leaving || !wanted.has(entry.key)) : [];\n for (const entry of leaving) {\n if (!entry.leaving) {\n entry.leaving = true;\n armTimeout(entry);\n }\n }\n for (const entry of entries) {\n if (!wanted.has(entry.key) && !leaving.includes(entry)) {\n clearTimer(entry.key);\n }\n }\n if (inputs.mode.value === 'wait' && leaving.length > 0) {\n entries = leaving;\n held = staying;\n } else {\n entries = [...leaving, ...staying];\n held = null;\n }\n emit();\n };\n\n const subscription = inputs.children.subscribe(children => apply(children));\n\n ctx.onUnmount(() => {\n disposed = true;\n subscription.unsubscribe();\n for (const key of timers.keys()) {\n clearTimer(key);\n }\n output.complete();\n });\n\n return Box(\n {\n // Positioned, because its children are: without this they would\n // resolve against whichever positioned ancestor happened to be\n // above, which is usually the whole screen.\n position: 'relative',\n width: inputs.width.value ?? percent(100),\n height: inputs.height.value ?? percent(100)\n },\n output as Observable<readonly UiChild[]>\n );\n}\n\n/** A child list flattened and keyed, with the empties dropped. */\nfunction normalize(children: UiChild | readonly UiChild[] | undefined): readonly { key: string; child: UiChild }[] {\n if (children === undefined || children === null) {\n return [];\n }\n const list = Array.isArray(children) ? (children as readonly UiChild[]) : [children as UiChild];\n const keyed: { key: string; child: UiChild }[] = [];\n for (const [index, child] of list.entries()) {\n if (child === undefined || child === null) {\n continue;\n }\n keyed.push({ key: keyOf(child, index), child });\n }\n return keyed;\n}\n\n/**\n * A child's identity: its own `key` when it has one, its position\n * otherwise.\n *\n * Both shapes are read because both appear here — a plain element\n * carries its key in `props`, and a component element carries it\n * beside them.\n */\nfunction keyOf(child: UiChild, index: number): string {\n const element = child as Partial<UiElement> & { key?: unknown };\n const declared = (element.props as { key?: unknown } | undefined)?.key ?? element.key;\n return declared === undefined || declared === null ? `#${index}` : String(declared);\n}\n","import { distinctUntilChanged, map, type Observable } from 'rxjs';\n\nimport type { MotionStateInput, MotionTiming, UiChild } from 'gesso-core';\nimport { Component } from '../Component';\nimport { createComponent } from '../createComponent';\nimport { Define, Inject, Input } from '../decorators';\nimport { input, InputCell } from '../Input';\nimport { Presence } from '../Presence';\nimport type { RouteDefinition } from './RouteDefinition';\nimport { RouterService, type RouteMatch } from './RouterService';\n\n/**\n * How one screen gives way to the next.\n *\n * Every field is optional and the default is nothing at all, so an\n * outlet with no `transition` swaps screens on the frame the url\n * changes, exactly as it did before this existed. A shared-element\n * morph needs no entry here: `sharedElement` pairs elements by name\n * across whatever tree change is happening, and a route change is one.\n *\n * **Do not combine `enter`/`exit` with shared elements.** A screen's\n * opacity multiplies onto everything inside it, morphing elements\n * included — so during a cross-fade the departing copy has already\n * handed over and the arriving one is at a fraction of its opacity,\n * and for those frames neither is on screen and whatever is behind\n * them shows through. It is very visible, and it is not a bug that can\n * be fixed here: a browser's View Transitions API only composes the\n * two because it lifts its named elements out of the page snapshot\n * into layers of their own, which is a whole architecture rather than\n * a flag.\n *\n * So they are alternatives. Either the screens cross-fade — right when\n * nothing is shared and the two are simply different — or the shared\n * elements carry the change, in which case they *are* the transition\n * and the screens should swap under them. `example-transitions` is the\n * second kind and passes no transition at all.\n */\nexport interface RouteTransition {\n /** Where the arriving screen starts. */\n enter?: MotionStateInput;\n /** Where the departing screen goes. */\n exit?: MotionStateInput;\n /**\n * `together` (the default) — the screens overlap while they cross.\n * Required for a shared element, which is measured off the departing\n * screen while it is still standing there.\n *\n * `wait` — the departing screen finishes leaving first.\n */\n mode?: 'together' | 'wait';\n timing?: MotionTiming;\n}\n\n/**\n * Where the current route's screen appears.\n *\n * This is `FRAMEWORK_DESIGN.md` §7.6 made real, and it is the one\n * component in the framework whose `render()` returns an Observable:\n * a route change is a structural change, not a change to a prop, and\n * observable render is the mechanism that exists for exactly that.\n *\n * Column(header(), createComponent(RouterOutlet))\n *\n * It cannot be the app root — a root definition may not be an\n * Observable, since the layout root needs a box — so it goes inside\n * whatever the root renders, which is where an app wants it anyway.\n *\n * **Nesting.** The outlet renders the whole matched chain, not just\n * the leaf: a route with a `parent` is built inside its parent's\n * screen, which receives it as an `outlet` prop (`OutletProps`) and\n * places it wherever it belongs. So there is one `RouterOutlet` in an\n * app; depth comes from the routes, not from where outlets are\n * scattered. That also means a layout screen is mounted once and stays\n * mounted while its children come and go — its sidebar keeps its\n * scroll position, and its state survives.\n *\n * A navigation that changes only params re-emits nothing at all: the\n * chain is the same objects, so the screens stay mounted and read the\n * new params from `router.observeParams(route)`.\n *\n * **Transitions.** Given a `transition`, the chain is rendered through\n * `Presence`, which keeps the departing screen mounted until its exit\n * animation is over. That is the only reason a screen transition needs\n * anything from the router at all — the animation itself is\n * `gesso-core`'s `motion`, and a shared-element morph across the\n * change is `sharedElement`, which the outlet knows nothing about.\n */\n@Define('gesso-router-outlet')\nexport class RouterOutlet extends Component {\n @Inject(RouterService) router!: RouterService;\n @Input() transition = input<RouteTransition | undefined>(undefined);\n\n override render(): UiChild {\n const chain = this.router.match.pipe(\n map(match => chainOf(match)),\n distinctUntilChanged(sameChain),\n map(routes => buildChain(routes))\n );\n return withTransition(chain, this.transition);\n }\n}\n\n/**\n * Wraps the chain in a `Presence` when a transition is asked for, and\n * hands it back untouched when one is not.\n *\n * Untouched rather than \"a Presence with no enter and no exit\",\n * because the two are not the same thing: `Presence` positions its\n * children absolutely inside a container that fills its parent, and an\n * app that never asked for a transition should not have its layout\n * changed by an outlet that decided to add one.\n *\n * The transition is read once, here, rather than followed. `render()`\n * runs exactly once per instance and this decides the *shape* of what\n * it returns; an outlet is configured when it is placed, the same way\n * an `Image`'s source is, and an outlet whose transition should change\n * gets a `key`.\n */\nfunction withTransition(\n chain: Observable<readonly UiChild[]>,\n transition: InputCell<RouteTransition | undefined>\n): UiChild {\n const settings = transition.value;\n if (settings === undefined) {\n return chain as Observable<UiChild | readonly UiChild[]> as unknown as UiChild;\n }\n return createComponent(Presence, {\n children: chain as unknown as UiChild | readonly UiChild[],\n enter: settings.enter,\n exit: settings.exit,\n mode: settings.mode,\n timing: settings.timing\n }) as unknown as UiChild;\n}\n\nfunction chainOf(match: RouteMatch | null): readonly RouteDefinition[] {\n return match === null ? [] : match.chain;\n}\n\n/**\n * Builds the chain from the inside out, each screen becoming the\n * `outlet` of the one above it.\n *\n * Keyed by path so reconciliation keeps a layout's host when only the\n * leaf below it changed: same component, same key, same slot, so the\n * host is reused and only its `outlet` input is pushed a new value.\n * The key is also what `Presence` identifies a departing screen by,\n * which is why a route change that keeps the same outermost route\n * — walking from one message to the next — is not a transition at all.\n */\nfunction buildChain(chain: readonly RouteDefinition[]): readonly UiChild[] {\n if (chain.length === 0) {\n return [];\n }\n // The leaf's own outlet: an empty list, which an observable child\n // reconciles to no children at all. It is what lets a screen place\n // `props.outlet` unconditionally, whether or not anything nests in\n // it on this url.\n let outlet = EMPTY_OUTLET;\n for (let index = chain.length - 1; index >= 0; index--) {\n const route = chain[index]!;\n outlet = createComponent(route.component as never, { outlet } as never, route.path);\n }\n return [outlet];\n}\n\nconst EMPTY_OUTLET = [] as unknown as UiChild;\n\nfunction sameChain(a: readonly RouteDefinition[], b: readonly RouteDefinition[]): boolean {\n return a.length === b.length && a.every((route, index) => route === b[index]);\n}\n","import type { UiChild } from 'gesso-core';\nimport type { ComponentType } from '../FunctionComponent';\nimport { parsePattern, type HasNoParams, type PatternSegment, type RouteParams } from './RoutePath';\n\n/**\n * The prop a screen receives when other routes nest inside it.\n *\n * A route that is another route's `parent` renders that child through\n * this prop: it is the outlet, and the screen places it wherever it\n * belongs — beside a sidebar, inside a card, under a header. A screen\n * nobody nests inside never receives one.\n *\n * function MailLayout(inputs: Inputs<OutletProps>) {\n * return Row(Sidebar(), Box({ flexGrow: 1 }, inputs.outlet));\n * }\n *\n * It is an `Inputs` cell like any other prop, so binding it as a child\n * is what makes the leaf swap without the layout being rebuilt.\n *\n * Never optional, so that placing it needs no ceremony: when no child\n * route is showing — `/mail` with nothing selected — the router feeds\n * the cell an empty list, and the outlet renders nothing.\n */\nexport interface OutletProps {\n readonly outlet: UiChild;\n}\n\n/** Where a navigation is going: a route and the params that fill it. */\nexport interface RouteTarget {\n readonly route: RouteDefinition;\n readonly params: Readonly<Record<string, string>>;\n readonly query: Readonly<Record<string, string>>;\n}\n\n/** What a guard is told about the navigation it is being asked to allow. */\nexport interface RouteContext<Path extends string = string> {\n readonly params: RouteParams<Path>;\n readonly query: Readonly<Record<string, string>>;\n /** The whole url being navigated to, path and query. */\n readonly url: string;\n}\n\n/**\n * A guard: an action that runs before its route is shown.\n *\n * Returning `true` lets the navigation through, `false` cancels it and\n * leaves the current url alone, and a `RouteTarget` — built with\n * `to(route, params)` — redirects. Guards are synchronous by design.\n * An asynchronous guard has to leave the app somewhere while it waits,\n * and \"somewhere\" is a screen, which means the waiting belongs to a\n * route rather than to the router: navigate to a loading route and\n * navigate on from it.\n */\nexport type RouteGuard<Path extends string = string> = (context: RouteContext<Path>) => boolean | RouteTarget;\n\n/**\n * One route: a full path, the screen that renders it, and optionally a\n * parent it nests inside.\n *\n * Paths are full, not relative. A relative fragment would know only\n * its own segments, and the params a screen actually receives are its\n * parents' as well — so `RouteParams` could not be honest about them.\n * Nesting is declared by pointing at the parent route object instead\n * of by position, which also means there are no route ids to keep\n * unique and no string to misspell.\n */\nexport interface RouteDefinition<Path extends string = string> {\n readonly path: Path;\n readonly component: ComponentType;\n readonly parent?: RouteDefinition;\n readonly guard?: RouteGuard<Path>;\n /** Parsed once, here, so matching a url never parses a pattern. */\n readonly segments: readonly PatternSegment[];\n /** Every ancestor and then this route: the chain the outlets render. */\n readonly chain: readonly RouteDefinition[];\n}\n\nexport interface RouteOptions<Path extends string> {\n readonly path: Path;\n readonly component: ComponentType;\n /** The route this one renders inside; its screen receives an `outlet`. */\n readonly parent?: RouteDefinition;\n readonly guard?: RouteGuard<Path>;\n}\n\n/**\n * Declares a route.\n *\n * The path is inferred as a literal type, which is where typed params\n * come from: `route({ path: '/mail/:id', ... })` produces a\n * `RouteDefinition<'/mail/:id'>`, and everything downstream — the\n * guard's context, `router.go`, `router.params` — reads `{ id: string }`\n * off it.\n */\nexport function route<const Path extends string>(options: RouteOptions<Path>): RouteDefinition<Path> {\n const parent = options.parent;\n const segments = parsePattern(options.path);\n if (parent !== undefined && !extendsParent(parent.segments, segments)) {\n throw new Error(\n `Route '${options.path}' declares '${parent.path}' as its parent but does not extend it. ` +\n \"A nested route renders inside its parent, so its path must begin with the parent's segments.\"\n );\n }\n const chain: RouteDefinition[] = parent === undefined ? [] : [...parent.chain];\n const definition: RouteDefinition<Path> = {\n path: options.path,\n component: options.component,\n parent,\n guard: options.guard,\n segments,\n chain\n };\n chain.push(definition);\n return definition;\n}\n\n/**\n * A navigation target: this route, with these params.\n *\n * The params argument is typed from the route's path, so a redirect to\n * a route that needs an id cannot forget it. A route with no params\n * takes no second argument.\n */\nexport function to<Path extends string>(route: RouteDefinition<Path>, ...args: ToArgs<Path>): RouteTarget {\n const [params, options] = args as unknown as [Readonly<Record<string, string>>?, ToOptions?];\n return {\n route,\n params: params ?? {},\n query: options?.query ?? {}\n };\n}\n\ninterface ToOptions {\n readonly query?: Readonly<Record<string, string>>;\n}\n\n/**\n * What `to` takes after the route, on the same reasoning as `GoArgs`:\n * a route with no params must not silently accept some.\n */\ntype ToArgs<Path extends string> =\n HasNoParams<Path> extends true ? [] : [params: RouteParams<Path>, options?: ToOptions];\n\n/**\n * Whether `child` begins with `parent`, segment by segment.\n *\n * Segment-wise rather than by string prefix, so `/mailbox` is not\n * mistaken for a child of `/mail`.\n */\nfunction extendsParent(parent: readonly PatternSegment[], child: readonly PatternSegment[]): boolean {\n if (child.length < parent.length) {\n return false;\n }\n return parent.every((segment, index) => {\n const other = child[index]!;\n if (segment.kind !== other.kind) {\n return false;\n }\n if (segment.kind === 'static' && other.kind === 'static') {\n return segment.text === other.text;\n }\n if (segment.kind === 'param' && other.kind === 'param') {\n return segment.name === other.name;\n }\n return true;\n });\n}\n","import type { FrameworkChild } from '../../ComponentElement';\nimport { createComponent } from '../../createComponent';\nimport type { ComponentType } from '../../FunctionComponent';\nimport { APPLICATION_WORKER, portHandle, type WorkerHandle } from '../../worker/WorkerPorts';\nimport { captureConsole } from '../../worker/captureConsole';\nimport {\n createChannelRegistry,\n type ChannelRegistration,\n type ChannelRegistryHandle\n} from '../../channel/createChannelRegistry';\nimport type { ChannelSource } from '../../channel/provide';\nimport type { ChannelToken } from '../../channel/ChannelToken';\nimport { UiHostFrameClock } from 'gesso-core';\nimport { GessoRuntime, type RendererChoice } from '../GessoRuntime';\nimport { ServiceRegistry } from '../../service/ServiceRegistry';\nimport type { MediaOptions } from '../MediaService';\nimport type { FontFamilyDeclaration } from '../FontService';\nimport type { RouterRoutes } from '../../router/RouterService';\nimport { isInputMessage, type RuntimeToShellMessage, type ShellToRuntimeMessage } from './RenderWorkerProtocol';\n\n/**\n * An `error` event as this module needs it: the thrown value when the\n * engine kept it, and the location when it did not.\n */\ninterface WorkerErrorEvent {\n message?: string;\n error?: unknown;\n filename?: string;\n lineno?: number;\n colno?: number;\n}\n\n/** An `unhandledrejection` event, reduced to the value that was rejected. */\ninterface WorkerRejectionEvent {\n reason?: unknown;\n}\n\n/**\n * Minimal view of the worker global, so this module type-checks\n * against the DOM lib without pulling in the WebWorker lib.\n *\n * `addEventListener` is required rather than optional, because a host\n * without it is a host whose uncaught exceptions vanish, and that is\n * the failure this whole file exists to prevent. A test double states\n * how it wants to be told instead of quietly not being told.\n */\ninterface WorkerGlobal {\n onmessage: ((event: MessageEvent<ShellToRuntimeMessage>) => void) | null;\n postMessage(message: RuntimeToShellMessage): void;\n addEventListener(type: 'error', listener: (event: WorkerErrorEvent) => void): void;\n addEventListener(type: 'unhandledrejection', listener: (event: WorkerRejectionEvent) => void): void;\n}\n\n/**\n * Runs a Gesso application inside a render worker.\n *\n * Everything the user sees is built and drawn here: components,\n * the retained graph, layout, input routing, and rasterization to an\n * OffscreenCanvas. The main thread only forwards events and never\n * touches any of it, which is the entire point of the arrangement.\n *\n * Usage, in a module loaded as a worker:\n *\n * renderRoot(AppRoot).useChannel(Catalog);\n *\n * The message handler is installed synchronously, so chained\n * useChannel() calls always land before the shell's init message is\n * processed.\n */\nexport function renderRoot(root: FrameworkChild | ComponentType): RenderWorkerApp {\n return new RenderWorkerApp(root);\n}\n\nexport class RenderWorkerApp {\n private readonly channelRegistrations: ChannelRegistration[] = [];\n private readonly serviceRegistrations: (new () => object)[] = [];\n private routes: RouterRoutes | undefined;\n private media: MediaOptions | undefined;\n private fonts: readonly FontFamilyDeclaration[] | undefined;\n private root: FrameworkChild;\n private readonly host: WorkerGlobal;\n\n private runtime: GessoRuntime | undefined;\n /** Held so forwarded display refreshes can be handed to it. */\n private clock: UiHostFrameClock | undefined;\n private channels: ChannelRegistryHandle | undefined;\n /** The shell's port to the application-logic worker, if there is one. */\n private appLogicWorker: WorkerHandle | undefined;\n /** Undoes `captureConsole` while a devtools panel has the console forwarded. */\n private restoreConsole: (() => void) | null = null;\n\n constructor(root: FrameworkChild | ComponentType, host: WorkerGlobal = self as unknown as WorkerGlobal) {\n this.root = typeof root === 'function' ? createComponent(root as ComponentType) : root;\n this.host = host;\n this.host.onmessage = event => this.receive(event.data);\n // Everything `receive` cannot see. The usual frame is not in that\n // set: the shell forwards its `requestAnimationFrame` as a `tick`\n // message and the clock delivers it synchronously, so a component\n // that throws while rendering, laying out or painting throws\n // inside `receive`'s own try and is reported as `message`. What\n // lands here is the rest: module scope, a callback no shell\n // message drove, and the frames the clock paces from its own\n // timer, before the first tick arrives or while the shell's thread\n // is blocked. Their only witness is the worker's own console,\n // which a page cannot read and a person only finds by opening the\n // right thread in devtools. These two listeners are what make a\n // render worker's failures reach the shell at all.\n this.host.addEventListener('error', event => {\n // The location only when the engine kept no Error: with one, the\n // stack says where it was in more detail and the event's\n // `filename` is whichever bundle chunk the frame landed in,\n // which is not where anybody wrote anything.\n const error = event.error;\n this.reportUncaught(\n error ?? event.message ?? 'Unknown error',\n error === undefined ? locationOf(event) : undefined\n );\n });\n this.host.addEventListener('unhandledrejection', event => {\n this.reportUncaught(event.reason ?? 'Unhandled rejection');\n });\n }\n\n /**\n * Reports a value nothing caught.\n *\n * `where` is the fallback location the `error` event carries when\n * the engine did not keep the thrown object — a cross-origin script,\n * or a value thrown that was never an Error. Without it the report\n * would be a bare sentence with nothing to look up.\n */\n private reportUncaught(value: unknown, where?: string): void {\n const error = value instanceof Error ? value : undefined;\n const message = error !== undefined ? error.message : String(value);\n this.host.postMessage({\n type: 'error',\n message: where === undefined ? message : `${message} (${where})`,\n stack: error?.stack,\n source: 'uncaught'\n });\n }\n\n /**\n * Replaces the application's root and rebuilds its tree, for hot\n * module replacement.\n *\n * The framework knows nothing about any bundler. An entry module\n * that wants this asks its own HMR client for the new module and\n * hands the root over:\n *\n * const app = renderRoot(AppRoot).useService(Counter);\n * import.meta.hot?.accept('./AppRoot', module => {\n * app.reload(module.AppRoot, [module.Counter]);\n * });\n *\n * The services are the ones the replaced module defines. They have\n * to be named because a registry is keyed by the class object and a\n * replaced module produces a new one; the registry adopts them,\n * keeping their instances. Omit a service that lives in a module the\n * replacement did not touch.\n *\n * Everything that is not the tree survives, including the channels:\n * see `GessoRuntime.reload`. Called before the shell's `init`\n * message it simply changes which root will be built.\n */\n reload(root: FrameworkChild | ComponentType, services: readonly (new () => object)[] = []): void {\n this.root = typeof root === 'function' ? createComponent(root as ComponentType) : root;\n for (const ServiceClass of services) {\n // Kept for a later reload too: a second replacement is matched\n // against what the first one left, not against the original.\n const previous = this.serviceRegistrations.findIndex(existing => existing.name === ServiceClass.name);\n if (previous === -1) {\n this.serviceRegistrations.push(ServiceClass);\n } else {\n this.serviceRegistrations[previous] = ServiceClass;\n }\n }\n this.runtime?.reload(this.root, services);\n }\n\n /**\n * Registers a channel.\n *\n * With `worker`, the channel's data lives there — api, store, domain\n * and view models, all plain code the framework never sees. With\n * `source`, it is fed from this thread; either way the same patches\n * cross the same kind of port, so a channel can be moved into a\n * worker later without a view noticing.\n */\n useChannel<V extends object, C extends object>(\n token: ChannelToken<V, C>,\n options: { worker?: WorkerHandle | (() => Worker); source?: ChannelSource<V, C> } = {}\n ): this {\n if (this.runtime !== undefined) {\n throw new Error(`Channel '${token.name}' was registered after the runtime started.`);\n }\n this.channelRegistrations.push({\n token: token as unknown as ChannelToken<never, never>,\n worker: options.worker,\n source: options.source as unknown as ChannelSource<never, never>\n });\n return this;\n }\n\n /**\n * Registers a runtime service: a plain class this thread constructs\n * once and hands to whoever injects it.\n *\n * For things that belong to the render thread and could not leave it\n * — something holding a `UiNode`, a decoded bitmap, or a generator\n * feeding bound props at frame rate. Application state goes through\n * `useChannel` instead, and the test is the usual one: if it\n * survives a reload or another screen cares about it, it is not a\n * service.\n */\n useService(ServiceClass: new () => object): this {\n this.serviceRegistrations.push(ServiceClass);\n return this;\n }\n\n /**\n * Declares the app's routes, which is all it takes to make a\n * `RouterOutlet` in the tree resolve them.\n *\n * renderRoot(AppRoot).useRoutes({ routes: ROUTES, notFound: NotFound });\n *\n * They are declared here, in the render worker, because a route\n * holds a component class. The shell never sees one; the only thing\n * that crosses is the url.\n */\n useRoutes(routes: RouterRoutes): this {\n if (this.runtime !== undefined) {\n throw new Error('Routes were registered after the runtime started.');\n }\n this.routes = routes;\n return this;\n }\n\n /**\n * Declares where the app's pictures come from: the image resolver,\n * the icon rasteriser and the video decoder.\n *\n * renderRoot(AppRoot).useMedia({ resolver: new CachingResolver() });\n *\n * Declared in the worker, like the routes and for a related reason:\n * a resolver is a function, and no function crosses a `postMessage`.\n * The shell could not forward one it was given, so the thread that\n * will do the fetching is where it is built. That is also the right\n * thread for it: this one already has no main thread to block.\n *\n * Declared before `init` rather than set on `MediaService` later,\n * because the tree is built when `init` arrives and an `Image` in it\n * asks for its bitmap at that moment. Whatever is left out, the\n * runtime builds and owns; whatever is passed stays the caller's,\n * and the runtime will not dispose it.\n */\n useMedia(media: MediaOptions): this {\n if (this.runtime !== undefined) {\n throw new Error('A media resolver was registered after the runtime started.');\n }\n this.media = media;\n return this;\n }\n\n /**\n * Declares the font families the app's text may name, with their\n * faces and fallback stacks.\n *\n * renderRoot(AppRoot).useFonts([\n * {\n * family: 'Inter',\n * faces: [{ source: new URL('./Inter.woff2', import.meta.url).href, weight: '100 900' }],\n * fallback: ['system-ui', 'sans-serif']\n * }\n * ]);\n *\n * Declared in the worker because that is the thread whose font set\n * the canvas draws from: a font the page loaded is not in it. The\n * faces are fetched here, text draws in the fallback until each\n * arrives, and the tree is laid out again when it does. See\n * `FontService`.\n */\n useFonts(families: readonly FontFamilyDeclaration[]): this {\n if (this.runtime !== undefined) {\n throw new Error('Fonts were declared after the runtime started.');\n }\n this.fonts = families;\n return this;\n }\n\n /**\n * Handles one message from the shell.\n *\n * Exceptions are reported to the shell rather than left to vanish:\n * an uncaught throw inside a worker is invisible to the page, which\n * is the worst failure mode this architecture introduces.\n */\n receive(message: ShellToRuntimeMessage): void {\n try {\n this.dispatch(message);\n } catch (error) {\n this.host.postMessage({\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n source: 'message'\n });\n }\n }\n\n /**\n * Copies this worker's `console.*` to the shell while a devtools\n * panel asks for it. Here rather than in the runtime because the\n * console is the worker global's, and this class is what owns the\n * global.\n */\n private setConsoleForwarding(enabled: boolean): void {\n this.restoreConsole?.();\n this.restoreConsole = null;\n if (!enabled) {\n return;\n }\n this.restoreConsole = captureConsole(entry => {\n this.host.postMessage({ type: 'devtools', event: { kind: 'console', entry: { ...entry, thread: 'render' } } });\n });\n }\n\n private dispatch(message: ShellToRuntimeMessage): void {\n if (message.type === 'init') {\n // The shell's channel to the application-logic worker, when it spawned\n // one. Held before initialize, because the channels registered\n // without a worker of their own are opened over it there.\n this.appLogicWorker = message.appPort === undefined ? undefined : portHandle(message.appPort);\n this.initialize(\n message.canvas,\n message.width,\n message.height,\n message.dpr,\n message.renderer,\n message.accessibility !== false\n );\n this.runtime!.setTextInputSource(message.textInput ?? 'keys');\n return;\n }\n\n if (message.type === 'resize') {\n // Answered ahead of the runtime guard, and answered from a\n // `finally`, because the shell's resize backpressure is held\n // open by this one message: it keeps a single resize in flight\n // and sits on the latest size it has not sent until the worker\n // says the last one has drained. An unacknowledged resize is\n // therefore not a late frame but a canvas that stays the wrong\n // size for good, so every resize is acknowledged — the one that\n // outran `init` and reached no tree, the zero-sized one the\n // runtime ignores by design, and the one whose layout threw,\n // which `receive` reports separately and which is no reason to\n // strand the shell. `RuntimeToShellMessage` says why the\n // dimensions ride along.\n //\n // It promises only that the layout for that size has been done.\n // It is not a paint confirmation: nothing here can vouch for\n // what the compositor has actually shown, and the queue this\n // exists to bound is a queue of layouts.\n try {\n this.runtime?.resize(message.width, message.height, message.dpr);\n } finally {\n this.host.postMessage({\n type: 'resized',\n width: message.width,\n height: message.height,\n dpr: message.dpr\n });\n }\n return;\n }\n\n const runtime = this.runtime;\n if (runtime === undefined) {\n // Events can outrun init; dropping them is correct, since there\n // is no tree yet for them to reach.\n return;\n }\n\n switch (message.type) {\n case 'pointerDown':\n runtime.input.pointer.pointerDown(message.x, message.y, message.buttons, message.modifiers, message.pointer);\n break;\n case 'pointerMove':\n runtime.input.pointer.pointerMove(message.x, message.y, message.buttons, message.modifiers, message.pointer);\n break;\n case 'pointerUp':\n runtime.input.pointer.pointerUp(message.x, message.y, message.buttons, message.modifiers, message.pointer);\n break;\n case 'pointerCancel':\n runtime.input.pointer.pointerCancel(message.pointer);\n break;\n case 'wheel':\n runtime.input.wheel.wheel(\n message.x,\n message.y,\n message.deltaX,\n message.deltaY,\n message.modifiers,\n message.deltaMode,\n message.wheelDeltaY\n );\n break;\n case 'keyDown':\n runtime.input.keyboard.keyDown(message.key, message.modifiers);\n break;\n case 'keyUp':\n runtime.input.keyboard.keyUp(message.key, message.modifiers);\n break;\n case 'beforeInput':\n runtime.input.editing.beforeInput(message.inputType, message.data);\n break;\n case 'compositionStart':\n runtime.input.editing.compositionStart();\n break;\n case 'compositionUpdate':\n runtime.input.editing.compositionUpdate(message.text, message.caret);\n break;\n case 'compositionEnd':\n runtime.input.editing.compositionEnd(message.text);\n break;\n case 'paste':\n runtime.input.editing.paste(message.text);\n break;\n case 'blur':\n runtime.input.focus.blur();\n break;\n case 'reducedMotion':\n runtime.setReducedMotion(message.reduced);\n break;\n case 'colorScheme':\n runtime.setColorScheme(message.scheme);\n break;\n case 'viewportInsets':\n runtime.setViewportInsets(message.insets);\n break;\n case 'url':\n runtime.setUrl(message.url);\n break;\n case 'popupResult':\n runtime.settlePopup(message.id, message.opened);\n break;\n case 'storageResult':\n runtime.settleStorage(message.id, message.result);\n break;\n case 'tick':\n this.clock?.tick(message.time);\n break;\n case 'visibility':\n runtime.setVisible(message.visible);\n break;\n case 'inspector':\n runtime.setInspectorEnabled(message.enabled);\n break;\n case 'devtools':\n if (message.request.kind === 'console') {\n this.setConsoleForwarding(message.request.enabled);\n } else {\n runtime.handleDevtools(message.request);\n }\n break;\n case 'audioSample':\n runtime.applyAudioSample(message.sample);\n break;\n case 'audioAction':\n runtime.applyAudioAction(message.action);\n break;\n case 'semanticsAction':\n runtime.applySemanticsAction(message.action);\n break;\n case 'dispose':\n this.setConsoleForwarding(false);\n runtime.dispose();\n this.channels?.dispose();\n this.channels = undefined;\n this.runtime = undefined;\n break;\n }\n\n // After routing, not before: whether a frame is now pending is how\n // the runtime tells an input that caused work from one that hit\n // nothing. `dispose` cannot reach here with a live runtime, and no\n // lifecycle message is an input, so the guard is enough.\n if (this.runtime !== undefined && isInputMessage(message)) {\n this.runtime.noteInput(message.at);\n }\n }\n\n /**\n * Swaps the `APPLICATION_WORKER` placeholder for the shell's port.\n *\n * Registrations run before `init`, so a registration naming the\n * application worker can only name a stand-in; this is where it\n * becomes real. Anything else is left exactly as registered.\n */\n private resolveWorker<T extends { worker?: WorkerHandle | (() => Worker) }>(registration: T): T {\n if (registration.worker !== APPLICATION_WORKER) {\n return registration;\n }\n return { ...registration, worker: this.appLogicWorker };\n }\n\n private initialize(\n canvas: OffscreenCanvas,\n width: number,\n height: number,\n dpr: number,\n renderer: RendererChoice | undefined,\n accessibility: boolean\n ): void {\n this.runtime?.dispose();\n this.channels?.dispose();\n this.channels = createChannelRegistry(\n // A channel registered with neither a worker nor a source is\n // served by whatever the shell spawned. Naming no worker is the\n // common case: an application has one application-logic worker, and\n // repeating that at every registration says nothing.\n this.channelRegistrations.map(registration =>\n registration.worker === undefined && registration.source === undefined\n ? { ...registration, worker: this.appLogicWorker }\n : this.resolveWorker(registration)\n ),\n (channelName, message, stack) => {\n this.host.postMessage({\n type: 'error',\n message: `channel ${channelName}: ${message}`,\n stack,\n source: 'channel'\n });\n }\n );\n const services = new ServiceRegistry();\n for (const ServiceClass of this.serviceRegistrations) {\n services.register(ServiceClass);\n }\n this.runtime = new GessoRuntime({\n root: this.root,\n services,\n routes: this.routes,\n media: this.media,\n fonts: this.fonts,\n canvas,\n renderer,\n channels: this.channels.registry,\n // A worker has no requestAnimationFrame tied to the compositor,\n // so the shell forwards the display's own refresh and this clock\n // just delivers it. See FRAMEWORK_DESIGN section 13.\n clock: callback => {\n const clock = new UiHostFrameClock(callback, running => {\n this.host.postMessage({ type: 'frameLoop', running });\n });\n this.clock = clock;\n return clock;\n },\n width,\n height,\n dpr\n });\n this.runtime.deferPatchesFrom(this.channels.registry.all());\n this.runtime.onInspect(report => {\n this.host.postMessage({ type: 'inspect', report });\n });\n this.runtime.onDevtools(event => {\n this.host.postMessage({ type: 'devtools', event });\n });\n this.runtime.onCursor(cursor => {\n this.host.postMessage({ type: 'cursor', cursor });\n });\n this.runtime.onScrollability((scrollability, scrollsAnything) => {\n this.host.postMessage({ type: 'scrollability', scrollability, scrollsAnything });\n });\n this.runtime.onEditingState(state => {\n this.host.postMessage({ type: 'editing', state });\n });\n if (accessibility) {\n // Subscribing is what turns the geometry sweep on in the\n // runtime, so a shell without a mirror pays nothing for one.\n this.runtime.onSemantics(update => {\n this.host.postMessage({ type: 'semantics', update });\n });\n }\n this.runtime.onAudioRequest(request => {\n this.host.postMessage({ type: 'audio', request });\n });\n this.runtime.onShellRequest(request => {\n if (request.type === 'clipboard') {\n this.host.postMessage({ type: 'clipboard', text: request.text });\n } else if (request.type === 'openUrl') {\n this.host.postMessage({ type: 'openUrl', url: request.url });\n } else if (request.type === 'popup') {\n this.host.postMessage({\n type: 'popup',\n id: request.id,\n url: request.url,\n name: request.name,\n width: request.width,\n height: request.height\n });\n } else if (request.type === 'storage') {\n this.host.postMessage({\n type: 'storage',\n id: request.id,\n op: request.op,\n key: request.key,\n ...(request.value === undefined ? {} : { value: request.value })\n });\n } else {\n this.host.postMessage({ type: 'history', action: request.action, url: request.url });\n }\n });\n this.runtime.onRendererError(message => {\n this.host.postMessage({ type: 'error', message, source: 'renderer' });\n });\n this.runtime.onListenerError((message, stack) => {\n this.host.postMessage({ type: 'error', message, stack, source: 'listener' });\n });\n this.runtime.onFrame(metrics => {\n this.host.postMessage({\n type: 'frame',\n frame: metrics.frame,\n durationMs: metrics.durationMs,\n nodes: metrics.nodes,\n measured: metrics.measured,\n relayoutRoots: metrics.relayoutRoots,\n at: metrics.at,\n inputLatencyMs: metrics.inputLatencyMs,\n phases: metrics.phases,\n renderer: metrics.renderer,\n gpu: metrics.gpu\n });\n });\n this.runtime.start();\n this.host.postMessage({ type: 'ready' });\n }\n}\n\n/** `file:line:column` from an error event that carried no Error. */\nfunction locationOf(event: WorkerErrorEvent): string | undefined {\n if (event.filename === undefined || event.filename === '') {\n return undefined;\n }\n const line = event.lineno ?? 0;\n const column = event.colno ?? 0;\n return `${event.filename}:${line}:${column}`;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["normalize","measure","measure"],"sources":["../src/Component.ts","../src/service/ServiceRegistry.ts","../src/bind.ts","../src/bounds.ts","../src/themeTokenCell.ts","../src/controlled.ts","../src/decorators.ts","../src/channel/ChannelReplica.ts","../src/channel/ChannelRegistry.ts","../src/channel/createChannelRegistry.ts","../src/FunctionComponent.ts","../src/overlay/OverlayService.ts","../src/overlay/OverlayLayer.ts","../src/ComponentElement.ts","../src/ComponentHost.ts","../src/ComponentHostResolver.ts","../src/app/DevtoolsProtocol.ts","../src/app/ShellService.ts","../src/app/worker/RenderWorkerProtocol.ts","../src/app/AudioService.ts","../src/router/RouteState.ts","../src/router/RoutePath.ts","../src/router/RouterService.ts","../src/app/FindService.ts","../src/app/FocusService.ts","../src/app/MediaService.ts","../src/app/FontService.ts","../src/app/AnimationService.ts","../src/app/FrameService.ts","../src/app/InputLatency.ts","../src/app/SmoothScroller.ts","../src/app/GessoRuntime.ts","../src/app/AudioSink.ts","../src/app/EditingProxy.ts","../src/app/SemanticsMirror.ts","../src/app/shellStorage.ts","../src/app/mediaQuery.ts","../src/app/colorScheme.ts","../src/app/reducedMotion.ts","../src/app/fullscreen.ts","../src/app/shellHistory.ts","../src/app/worker/WorkerApp.ts","../src/app/GessoApp.ts","../src/app/GessoAppBuilder.ts","../src/app/createApp.ts","../src/undo/undoShortcuts.ts","../src/storage/ShellStorage.ts","../src/app/videoClock.ts","../src/Presence.ts","../src/router/RouterOutlet.ts","../src/router/RouteDefinition.ts","../src/app/worker/renderRoot.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\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) {\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 has(ServiceClass: Function): boolean {\n return this.services.has(ServiceClass);\n }\n}\n","import type { ReadableCell } from './Input';\nimport type { InternalState } from './InternalState';\n\n/**\n * A two-way binding for the plain case: the cell is the control's value,\n * and the control's changes are written back into the cell.\n *\n * const volume = internalState(0.8);\n * <Slider {...bind(volume)} />\n *\n * Spread onto a control that takes `value` and `onChange`, or name the\n * pair for one that calls them something else:\n *\n * <Checkbox {...bind(muted, 'checked')} />\n *\n * A value that lives across the barrier is read from a channel key and\n * written with a command, which is two halves of one binding, so it\n * takes the same shape with the write said out loud:\n *\n * <TextInput {...bind(library.view.filter, library.send.setFilter)} />\n *\n * That is the form a real form wants. A screen's fields are almost\n * never a component's own state: they are the application's, they\n * survive the screen, and they are on a channel. The cell is read-only\n * there, as an input is, and the command is what writes it, so the\n * round trip through the application thread is visible at the call\n * site rather than hidden by a helper pretending the key is writable.\n *\n * Sugar over the controlled contract, not a replacement for it.\n */\nexport function bind<T>(cell: InternalState<T>): { value: InternalState<T>; onChange: (next: T) => void };\nexport function bind<T, V extends string, E extends string = 'onChange'>(\n cell: InternalState<T>,\n value: V,\n onChange?: E\n): { [K in V]: InternalState<T> } & { [K in E]: (next: T) => void };\nexport function bind<T>(\n cell: ReadableCell<T>,\n write: (next: T) => void\n): { value: ReadableCell<T>; onChange: (next: T) => void };\nexport function bind<T, V extends string, E extends string = 'onChange'>(\n cell: ReadableCell<T>,\n write: (next: T) => void,\n value: V,\n onChange?: E\n): { [K in V]: ReadableCell<T> } & { [K in E]: (next: T) => void };\nexport function bind<T>(\n cell: ReadableCell<T>,\n second?: ((next: T) => void) | string,\n third?: string,\n fourth?: string\n): Record<string, unknown> {\n const writing = typeof second === 'function';\n const write =\n second === undefined || !writing\n ? (next: T) => {\n (cell as InternalState<T>).value = next;\n }\n : second;\n const value = (writing ? third : (second as string | undefined)) ?? 'value';\n const onChange = (writing ? fourth : third) ?? 'onChange';\n return { [value]: cell, [onChange]: write };\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","import { distinctUntilChanged, map, type Observable } from 'rxjs';\n\nimport { themeTokens, type UiModifier, type UiThemeExtension } from 'gesso-core';\nimport { InternalState } from './InternalState';\n\n/**\n * A theme extension's tokens, as a cell, with the modifier that fills\n * it.\n *\n * The same shape as `ctx.bounds()`, and for the same reason: the value\n * lives on a node and a component body has no node yet, so the cell is\n * the value and the modifier is how it gets filled.\n *\n * const tokens = themeTokenCell(controlTokens);\n * return Button(\n * {\n * modifiers: [tokens.modifier, ...],\n * paddingX: tokens.select(t => t.button.medium.paddingX),\n * borderRadius: tokens.select(t => t.button.medium.radius)\n * },\n * Text({ text: label, color: tokens.select(t => t.button.filled.foreground) })\n * );\n *\n * It starts holding the extension's declared defaults, which is the\n * honest value for a node that is not in a tree: it is what\n * `themeExtension` would answer for a theme carrying nothing. The\n * modifier replaces it at attach, before the first frame is drawn, so\n * a component that is never restyled draws its defaults and never\n * flickers through them.\n *\n * Bind with `select`, not with `value`. The cell publishes a whole\n * group, so every binding taken straight off it would recompute when\n * any token in the group changed; `select` projects one token and\n * drops a repeat, which is what keeps a theme change that moved a\n * colour from re-writing every padding on the element.\n */\nexport class ThemeTokenCell<T extends object> extends InternalState<T> {\n /** Put this on the element whose inherited theme the cell should read. */\n readonly modifier: UiModifier;\n\n constructor(extension: UiThemeExtension<T>) {\n super(extension.defaults);\n this.modifier = themeTokens<T>({ extension, sink: this });\n }\n\n /** One token, as an Observable that drops a repeat. */\n select<R>(pick: (tokens: T) => R): Observable<R> {\n return this.pipe(map(pick), distinctUntilChanged());\n }\n}\n\n/**\n * A token cell outside a component, for a class component or a test.\n * Inside a function component this is the same thing, and it completes\n * with the component.\n */\nexport function themeTokenCell<T extends object>(extension: UiThemeExtension<T>, label?: string): ThemeTokenCell<T> {\n const cell = new ThemeTokenCell(extension);\n if (label !== undefined) {\n cell.label = label;\n }\n return cell;\n}\n","import { map, type Observable } from 'rxjs';\n\nimport type { InputCell, OutputCell } from './Input';\nimport { internalState, type InternalState } from './InternalState';\n\n/** A value a control shows and changes, whoever owns it. */\nexport interface ControlledValue<T> {\n readonly value: Observable<T>;\n current(): T;\n /** What the control calls when the person changes it. */\n change(next: T): void;\n}\n\nexport interface ControlledOptions<T> {\n /** The component's name, for the error when both forms are passed. */\n readonly component: string;\n /** The input's name: `value`, `checked`, `open`. */\n readonly name: string;\n /** The controlled form: the parent owns the value and hears every change. */\n readonly source: InputCell<T | undefined>;\n /** The self-managed form: a starting value the control then owns. */\n readonly initial: InputCell<T | undefined>;\n /** What the control shows when given neither. */\n readonly fallback: T;\n /** Fired on every change in either form. */\n readonly onChange: OutputCell<[next: T]>;\n}\n\n/**\n * Controlled by default, self-managing when given only an initial value.\n *\n * The one contract every stateful control in the library obeys, and the\n * one an application component should copy: pass `value` and the app\n * owns the state and hears every change through `onChange`; pass\n * `defaultValue` and the control owns it, still reporting changes;\n * pass both and it throws, because two owners is a bug. `bind(cell)` is\n * the spread for the first form when the owner is a local cell.\n */\nexport function controlled<T>(options: ControlledOptions<T>): ControlledValue<T> {\n const { component, name, source, initial, fallback, onChange } = options;\n const isControlled = source.value !== undefined;\n if (isControlled && initial.value !== undefined) {\n throw new Error(\n `${component} was given both '${name}' and 'default${capitalize(name)}'. ` +\n `Pass '${name}' for the app to own the value, or 'default${capitalize(name)}' for the control to.`\n );\n }\n if (isControlled) {\n return {\n value: source.pipe(map(value => (value === undefined ? fallback : value))),\n current: () => (source.value === undefined ? fallback : source.value),\n change: next => onChange.emit(next)\n };\n }\n const own: InternalState<T> = internalState(initial.value === undefined ? fallback : initial.value);\n return {\n value: own,\n current: () => own.value,\n change: next => {\n own.value = next;\n onChange.emit(next);\n }\n };\n}\n\nfunction capitalize(name: string): string {\n return name.charAt(0).toUpperCase() + name.slice(1);\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 });\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.\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 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 cell.next(applyPatches(cell.value, group));\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 } from 'rxjs';\n\nimport { workerHandle, type WorkerHandle } from '../worker/WorkerPorts';\nimport { ChannelRegistry } from './ChannelRegistry';\nimport type { ChannelPort } from './ChannelProtocol';\nimport { provide, ProvidedChannel, type ChannelSource } from './provide';\nimport type { ChannelToken, Command, CommandMap } from './ChannelToken';\n\n/**\n * A registration with its types erased.\n *\n * Erased structurally rather than as `ChannelToken<never, never>`: a\n * heterogeneous list of channels has no single generic instantiation,\n * and `never` made every caller cast. What the registry actually needs\n * is a name, an initial shape, observables by key and callables by\n * name — all of which a concrete registration satisfies on its own.\n */\nexport interface ChannelRegistration {\n token: { name: string; initial: object };\n /**\n * The worker that owns this channel's data.\n *\n * A `WorkerHandle` shared between registrations puts them in one\n * application worker, which is the arrangement the barrier design\n * exists for: api, store, domain and view models together, one\n * thread, several channels.\n */\n worker?: WorkerHandle | (() => Worker);\n /**\n * What feeds the channel, when it is owned by this thread.\n *\n * Still crosses a real `MessageChannel`, so the same diff, the same\n * patches and the same plain-data rule apply — a channel behaves\n * identically wherever it lives, which is what lets it be moved into\n * a worker later without touching a view.\n */\n source?: {\n view: Record<string, Observable<unknown>>;\n commands?: Record<string, Command>;\n };\n}\n\nexport interface ChannelRegistryHandle {\n registry: ChannelRegistry;\n dispose(): void;\n}\n\nfunction isWorkerHandle(worker: WorkerHandle | (() => Worker)): worker is WorkerHandle {\n return typeof worker === 'object';\n}\n\n/**\n * Attaches every registered channel, wherever its data lives.\n */\nexport function createChannelRegistry(\n registrations: readonly ChannelRegistration[],\n onError?: (channelName: string, message: string, stack?: string) => void\n): ChannelRegistryHandle {\n const registry = new ChannelRegistry();\n const handles = new Set<WorkerHandle>();\n const wrapped = new Map<() => Worker, WorkerHandle>();\n const local: ProvidedChannel[] = [];\n\n for (const registration of registrations) {\n const token = registration.token as unknown as ChannelToken<object, CommandMap>;\n let port: ChannelPort;\n\n if (registration.worker !== undefined) {\n let handle: WorkerHandle;\n if (isWorkerHandle(registration.worker)) {\n handle = registration.worker;\n } else {\n const factory = registration.worker;\n handle = wrapped.get(factory) ?? workerHandle(factory);\n wrapped.set(factory, handle);\n }\n handles.add(handle);\n port = handle.open(token.name) as unknown as ChannelPort;\n } else {\n if (registration.source === undefined) {\n throw new Error(\n `Channel '${token.name}' was registered with neither a worker nor a source, ` +\n `and no application-logic worker was supplied to serve it. Pass appLogicWorker to ` +\n `createApp to spawn one, source to feed the channel from this thread, or worker to ` +\n `name a worker of its own.`\n );\n }\n const pair = new MessageChannel();\n local.push(\n provide(\n token,\n registration.source as unknown as ChannelSource<object, CommandMap>,\n pair.port2 as unknown as ChannelPort\n )\n );\n port = pair.port1 as unknown as ChannelPort;\n }\n\n const replica = registry.attach(token, port);\n replica.onError((message, stack) => {\n if (onError !== undefined) {\n onError(token.name, message, stack);\n } else {\n console.error(`[gesso channel ${token.name}] ${message}`, stack);\n }\n });\n }\n\n return {\n registry,\n dispose: () => {\n for (const channel of local) {\n channel.dispose();\n }\n local.length = 0;\n registry.dispose();\n for (const handle of handles) {\n handle.terminate();\n }\n handles.clear();\n wrapped.clear();\n }\n };\n}\n","import type { Observable, Subscription } from 'rxjs';\n\nimport type { Reactive, UiChild } 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/**\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<P[K]>;\n};\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 type { UiChild, UiNode } from 'gesso-core';\nimport { internalState } from '../InternalState';\n\nexport type OverlayPlacement =\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\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 /** Default 'bottom'. */\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 * 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 /** 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","import { map } from 'rxjs';\n\nimport { Box, UiEnvironmentKeys, type UiNode, type UiChild, type UiElement } from 'gesso-core';\nimport { Component } from '../Component';\nimport { Define, Inject } from '../decorators';\nimport { OverlayService, type OverlayEntry } 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 closes the entry on pointer down or wheel.\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@Define('gesso-overlay-layer')\nexport class OverlayLayer extends Component {\n @Inject(OverlayService) overlays!: OverlayService;\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 if (entry.dismissOnOutsidePress) {\n elements.push(\n Box({\n key: `${entry.id}\\0backdrop`,\n position: 'absolute',\n inset: 0,\n zIndex: entry.zIndex,\n onPointerDown: () => this.overlays.close(entry.id),\n // A wheel over the backdrop is the user scrolling away; the\n // menu closes rather than swallowing the scroll.\n onWheel: () => this.overlays.close(entry.id)\n })\n );\n }\n elements.push(\n Box(\n {\n key: entry.id,\n position: 'absolute',\n anchor: entry.anchor ?? undefined,\n placement: entry.placement,\n anchorOffset: entry.offset,\n top: entry.top,\n right: entry.right,\n bottom: entry.bottom,\n left: entry.left,\n zIndex: entry.zIndex,\n ...centering(entry),\n ...inheritedFrom(entry.environment ?? entry.anchor ?? null)\n },\n entry.content as UiChild\n )\n );\n }\n return elements;\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.\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 ? { left: entry.left ?? 0, right: entry.right ?? 0, x: 'center' } : {}),\n ...(y ? { top: entry.top ?? 0, bottom: entry.bottom ?? 0, y: 'center' } : {})\n };\n}\n\n/**\n * The scoped values an entry's content should keep, re-provided on the\n * box that holds it.\n *\n * Read once, when the entry opens. An overlay that outlives a theme\n * change re-opens; nothing here watches, because an entry's content is\n * built once too.\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 contentColor: environment.get(UiEnvironmentKeys.contentColor)\n };\n}\n","import type { UiChild } from 'gesso-core';\nimport type { ComponentType } from './FunctionComponent';\n\n/**\n * Declarative representation of a component instance in the tree.\n *\n * ComponentElements are resolved by ComponentRenderer into plain\n * UiElement trees before being passed to UiGraphBuilder.\n */\nexport interface ComponentElement<P = Record<string, unknown>> {\n readonly kind: 'component';\n readonly tag: string;\n /** The class or function to mount. */\n readonly component: ComponentType;\n readonly props: P;\n readonly key?: string | number;\n}\n\nexport function isComponentElement(value: unknown): value is ComponentElement {\n return typeof value === 'object' && value !== null && (value as Partial<ComponentElement>).kind === 'component';\n}\n\n/**\n * Anything the framework accepts as a child: a runtime element, an\n * observable stream of elements, or a component definition.\n */\nexport type FrameworkChild = UiChild | ComponentElement;\n","import { 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 } from 'gesso-core';\nimport { 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';\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 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 ) {\n this.element = element;\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, props[name], true);\n }\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 withBodyOf(this.element.tag, () => component(this.createInputRecord(), context));\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.element.props as Record<string, unknown>)[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 };\n }\n\n private wireInputs(): void {\n const metadata = getComponentMetadata(this.element.component);\n const props = this.element.props as Record<string, unknown>;\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, props[inputName], false);\n }\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(inputName: string, cell: InputCell<unknown>, provided: unknown, resetWhenAbsent: boolean): void {\n if (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 const subscription = provided.subscribe(value => cell.next(value));\n this.inputSubscriptions.set(inputName, subscription);\n this.subscriptions.add(subscription);\n return;\n }\n\n cell.next(provided);\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","import type { ComponentResolver, ComponentLikeElement, UiChild } 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): 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);\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 { 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 { insetsEqual, noInsets, type UiInsets } from 'gesso-core';\n\nimport { internalState } from '../InternalState';\nimport type { ColorScheme } 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'; text: string }\n | { type: 'openUrl'; url: string }\n | { type: 'fullscreen'; enter: boolean }\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: 'history'; action: 'push' | 'replace'; url: string }\n | { type: 'history'; action: 'back' | 'forward'; url?: undefined };\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 private readonly scheme = internalState<ColorScheme>('light');\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\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 /** 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 /** Installed by the runtime; a request with no handler is dropped. */\n setHandler(handler: ((request: ShellRequest) => void) | null): void {\n this.handler = handler;\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 /**\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 /** Puts text on the system clipboard. */\n copyText(text: string): void {\n this.handler?.({ type: 'clipboard', 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 * 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 /** 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, and it can\n * 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","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} from 'gesso-core';\nimport type { AudioAction, AudioRequest, AudioSample } from '../AudioService';\nimport type { ColorScheme } from '../colorScheme';\nimport type { 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 | { 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 | { type: 'keyDown'; key: string; modifiers: UiKeyModifiers; at?: number }\n | { type: 'keyUp'; key: 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 | { type: 'paste'; text: 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 /**\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 * 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 | { 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 * 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 | { 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 = 'message' | 'uncaught' | 'renderer' | 'channel' | 'listener';\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 at: number;\n inputLatencyMs: 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 /** Put text on the clipboard (ShellService.copyText). */\n | { type: 'clipboard'; text: string }\n /** Open a URL in a new tab (ShellService.openUrl). */\n | { type: 'openUrl'; url: string }\n | { type: 'fullscreen'; enter: boolean }\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 /** 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 * 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 { 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]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`);\n const normalized = `/${pathSegments(path).join('/')}`;\n return pairs.length === 0 ? normalized : `${normalized}?${pairs.join('&')}`;\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 || !match.chain.includes(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 && match.chain.includes(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 this.url.value = settled;\n this.match.value = match;\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","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 { 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 */\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 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 const focusChange = manager.onFocusChange(node => {\n this.focused.value = node;\n });\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 /** Gives the node keyboard focus. Non-focusable nodes are ignored. */\n focus(node: UiNode): void {\n this.run(manager => manager.focus(node));\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 * 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.\n */\n releaseTrap(): void {\n this.run(manager => manager.popScope());\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.trapped.value = manager.trapped;\n }\n}\n","import {\n DefaultImageResolver,\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 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 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 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 { EMPTY, type Observable } from 'rxjs';\n\nimport {\n AnimationDriver,\n UiSpring,\n createTween,\n easings,\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} 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\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\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 }\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);\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 =\n typeof options.spring === 'string' || options.spring === undefined\n ? this.motionVocabulary.springs[options.spring ?? 'snappy']\n : 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 if (driver === null) {\n animation.snap();\n return animation.values;\n }\n return driver.start(animation);\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 typeof duration === 'number' ? duration : this.motionVocabulary.durations[duration];\n }\n\n private resolveEasing(easing: UiEasingChoice): UiEasing {\n if (typeof easing === 'function') {\n return easing;\n }\n return this.motionVocabulary.easings[easing] ?? easings.standard;\n }\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 /** 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","/**\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 { 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 /** The container's clamp, read fresh because content grows. */\n private readonly limitOf: (node: UiNode, axis: Axis) => 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 limit = this.limitOf(node, axis);\n const target = clamp((pending ?? from) + delta, 0, limit);\n if (pending !== undefined && target === pending) {\n return;\n }\n this.animations.spring(cell, target, { spring: SCROLL_SPRING });\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 });\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 UiNodeReport,\n type UiBeneathReport,\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 UiGraph,\n UiGraphBuilder,\n describeOverrides,\n formatExplanation,\n isComponentLikeElement,\n linkOf,\n textRunOfRecordId,\n type UiEnvironment,\n isObservable,\n type UiElement,\n Stack,\n scrollbarThumb,\n UiVirtualWindow,\n VIRTUAL_INDEX_PROP,\n VIRTUAL_LEAD_PROP,\n VIRTUAL_WINDOW_PROP,\n type VirtualItemMeasure,\n DirtyFlags,\n propertyEffects,\n setPerformanceMarks,\n resolveCursor,\n type UiNode,\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 semanticsInertAbove,\n recordsEqual,\n TEXT_RUN_ID_SEPARATOR,\n diffSemantics,\n LayoutNotifier,\n type UiSemanticsAction,\n type UiSemanticsBox,\n type UiSemanticsMap,\n type UiSemanticsPatch,\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 RendererBackend,\n type UiRenderer,\n type WebGPUCanvasHost,\n UiScheduler,\n UiTimerFrameClock,\n type UiFrame,\n type UiFrameClockFactory\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 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 { FocusService } from './FocusService';\nimport { MediaService, type MediaOptions } from './MediaService';\nimport { FontService, type FontFamilyDeclaration } from './FontService';\nimport { AnimationService } from './AnimationService';\nimport { FrameService } from './FrameService';\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 `createApp(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 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 */\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 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 private constraints: Constraints;\n private pixelRatio: number;\n private lastFrameMs = 0;\n private frameListener: ((metrics: FrameMetrics) => void) | null = null;\n private rendererErrorListener: ((message: 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 private semantics = new Map<string, UiSemanticsRecord>();\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 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 /** 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 private audioListener: ((request: AudioRequest) => void) | null = null;\n private caretTimer: ReturnType<typeof setTimeout> | null = null;\n private scrollbarTimer: 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\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 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\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 => {\n this.focusManager.focus(node);\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 visible 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.\n box: node => (this.engine.recordFor(node) === undefined ? null : this.engine.visibleBox(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 },\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.shellListener?.(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 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 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 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 // 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 0;\n }\n return axis === 'scrollY'\n ? Math.max(0, record.contentHeight - record.height)\n : Math.max(0, record.contentWidth - record.width);\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 router.setHistory({\n push: url => this.shellListener?.({ type: 'history', action: 'push', url }),\n replace: url => this.shellListener?.({ type: 'history', action: 'replace', url }),\n back: () => this.shellListener?.({ type: 'history', action: 'back' }),\n forward: () => this.shellListener?.({ type: 'history', action: 'forward' })\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?.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\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(FocusService).setManager(this.input.focus);\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.\n this.input.focus.onFocusChange((node, source) => {\n if (node !== null && source !== 'pointer') {\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(() => this.scheduler.notifyDirty());\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 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 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 * 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 /** `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 onShellRequest(listener: ((request: ShellRequest) => void) | null): void {\n this.shellListener = listener;\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 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 /**\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 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 /** 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 explanation: formatExplanation(this.engine.explain(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 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 owners.push({ name: getComponentMetadata(host.component).tag, anchorId: current.id });\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 /**\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 scrollIntoView(node: UiNode, padding = 8): void {\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. 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.scrollbarTimer !== null) {\n clearTimeout(this.scrollbarTimer);\n this.scrollbarTimer = 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.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.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 output = this.resolver.resolve(definition, `app:component:${depth}`);\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 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 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 wheel: new UiWheelController(hitTester, this.dispatcher, scrollSink, () => root),\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.\n activation: {\n clickAt: node => {\n const box = this.engine.visibleBox(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.semantics.values()].map(node => ({ op: 'add', node }) as const),\n boxes: this.collectSemanticsBoxes(),\n focused: this.focusManager.focusedNode?.id ?? null\n });\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.semantics = buildSemanticsTree(this.layoutRoot());\n this.semanticsStale = false;\n }\n return this.semantics;\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 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 if (action.action === 'focus') {\n this.focusManager.focus(node);\n return;\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.\n this.focusManager.focus(node);\n this.input.editing.replaceText(action.value ?? '');\n return;\n }\n this.focusManager.focus(node);\n const box = this.engine.visibleBox(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 }\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.rebuildSemantics();\n }\n }\n if (listener === null) {\n return;\n }\n const boxes = moved || patches.length > 0 ? this.collectSemanticsBoxes() : 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 next = buildSemanticsTree(this.layoutRoot());\n const patches = diffSemantics(this.semantics, next);\n this.semantics = 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(owner, previous.parent, previous.index, semanticsInertAbove(owner));\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 const before = this.semantics.get(id)!;\n if (!recordsEqual(before, next)) {\n updates.push(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 the map keeps the position — and therefore the\n // document order — it already had.\n this.semantics.set(record.id, record);\n patches.push({ op: 'update', node: record });\n }\n return patches;\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 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 private collectSemanticsBoxes(): UiSemanticsBox[] {\n const changed: UiSemanticsBox[] = [];\n const focused = this.focusManager.focusedNode?.id;\n for (const id of this.semantics.keys()) {\n const node = this.graph.getNode(id);\n if (node === undefined || this.engine.recordFor(node) === undefined) {\n continue;\n }\n const box = this.engine.visibleBox(node);\n const last = this.semanticsBoxes.get(id);\n if (last !== undefined && boxesEqual(last, box)) {\n continue;\n }\n if (id !== focused && !this.onScreen(box)) {\n // Left where it was, deliberately: `semanticsBoxes` is what the\n // mirror has been told, and leaving the two in step is what\n // makes the box arrive on the frame this node returns.\n continue;\n }\n this.semanticsBoxes.set(id, box);\n changed.push({ id, box });\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 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: record.scrollX,\n scrollY: record.scrollY,\n maxScrollX: Math.max(0, record.contentWidth - record.width),\n maxScrollY: Math.max(0, record.contentHeight - record.height),\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, record.scrollX);\n }\n if (dy !== 0) {\n this.smoothScroller.scrollBy(node, 'scrollY', dy, record.scrollY);\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', record.scrollX + dx);\n }\n if (dy !== 0) {\n node.setProperty('scrollY', record.scrollY + 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 /**\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.phaseTimings = emptyPhaseTimings();\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 // 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 }\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 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 scroll = typeof scrollProp === 'number' ? scrollProp : column ? rec.scrollY : rec.scrollX;\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 node.setProperty(axis, scroll + result.scrollAdjust);\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, result.scrollAdjust);\n }\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 private handleFrame(frame: 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 laidOut = frameNeedsLayout(frame);\n this.phaseTimings.layout = this.timePhase(\n () => laidOut,\n () => this.engine.layoutForFrame(frame, this.constraints, root)\n );\n if (laidOut) {\n this.laidOutOnce = true;\n this.inspector.recordLayout(started);\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. Nothing\n // listening means nothing walked.\n if (!this.layoutNotifier.isEmpty()) {\n this.layoutNotifier.notify(node => {\n const record = this.engine.recordFor(node);\n return {\n box: this.engine.visibleBox(node),\n scrollX: record?.scrollX ?? 0,\n scrollY: record?.scrollY ?? 0\n };\n });\n }\n\n // After the layout listeners rather than before, because\n // `autoFocus` is one of them: it takes focus on the first layout\n // of the node it is attached to, and every node in a subtree a\n // reload replaced is having its first layout on this frame.\n if (this.restoringFocus && laidOut) {\n this.restoringFocus = false;\n this.restoreFocusAfterReload();\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 falls back to Canvas2D and repaints.\n if (this.renderer.backend === 'webgpu' && (this.renderer as WebGPURenderer).isLost) {\n this.fallBackToCanvas2D(this.renderer);\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 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 })\n );\n if (overlay !== null) {\n if (laidOut) {\n this.sendInspection();\n }\n this.scheduleInspectorRepaint(overlay.nextChange);\n }\n this.sendDevtoolsUpdates(frame);\n\n const finished = now();\n const elapsed = finished - started;\n this.lastFrameMs = elapsed;\n this.scheduleScrollbarFade(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 measured: this.engine.stats.measured,\n relayoutRoots: this.engine.stats.fullLayout ? 0 : this.engine.stats.relayoutRoots,\n at: finished,\n inputLatencyMs: this.inputLatency.take(epochAt(finished)),\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 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. */\n private sendCursor(): void {\n const cursor = 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 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 /** 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\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.multiline === b.multiline &&\n a.composing === b.composing &&\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\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(DirtyFlags.Layout | DirtyFlags.Children | DirtyFlags.SubtreeLayout | DirtyFlags.Transform);\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 * 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\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\nfunction frameNeedsSemantics(frame: UiFrame): boolean {\n return frame.anyFlags(DirtyFlags.Semantics | DirtyFlags.Children);\n}\n","import type { AudioAction, AudioMetadata, AudioRequest, AudioSample, AudioStatus } from './AudioService';\nimport { epochNow } from './worker/RenderWorkerProtocol';\n\n/**\n * The part of `HTMLAudioElement` the sink drives. Narrow so a spec can\n * stand one in with a plain `EventTarget`.\n */\nexport interface AudioElementLike extends EventTarget {\n src: string;\n currentTime: number;\n volume: number;\n preload: string;\n readonly duration: number;\n readonly paused: boolean;\n readonly ended: boolean;\n readonly buffered: { readonly length: number; start(index: number): number; end(index: number): number };\n readonly error: { readonly code: number; readonly message?: string } | null;\n play(): Promise<void>;\n pause(): void;\n load(): void;\n}\n\n/** The part of the Media Session API the sink uses; absent on browsers without it. */\nexport interface MediaSessionLike {\n metadata: unknown;\n playbackState: 'none' | 'paused' | 'playing';\n setActionHandler(action: string, handler: ((details: { seekTime?: number }) => void) | null): void;\n}\n\nexport interface AudioSinkOutput {\n /** The element's state, whenever it changes and about once a second while playing. */\n sample(sample: AudioSample): void;\n /** What the platform's media controls asked for. */\n action(action: AudioAction): void;\n}\n\nexport interface AudioSinkOptions {\n /** Injectable for specs; `new Audio()` by default. */\n readonly createElement?: () => AudioElementLike;\n /** Injectable for specs; `navigator.mediaSession` by default, when the browser has one. */\n readonly mediaSession?: MediaSessionLike | null;\n /** Milliseconds between samples while playing. */\n readonly sampleEveryMs?: number;\n}\n\nconst DEFAULT_SAMPLE_EVERY_MS = 1000;\n\n/** The element events that change what a sample would say. */\nconst SAMPLED_EVENTS = [\n 'loadstart',\n 'loadedmetadata',\n 'durationchange',\n 'canplay',\n 'playing',\n 'play',\n 'pause',\n 'seeking',\n 'seeked',\n 'waiting',\n 'stalled',\n 'ended',\n 'error',\n 'progress',\n 'emptied'\n] as const;\n\n/**\n * The shell's audio element, behind the `AudioRequest` messages.\n *\n * This is the one piece of sound that genuinely cannot run anywhere\n * else: an element that plays needs a window. Everything that could be\n * a decision is not here. The sink loads what it is told, plays and\n * pauses when told, and reports what the element is doing; which track\n * that is, and what comes next, is the render thread's `AudioService`\n * and the application above it. The thread model holds the shell to\n * \"what genuinely cannot run anywhere else\", and this is the audio\n * equivalent of forwarding a pointer event.\n *\n * Media Session is here for the same reason: `navigator.mediaSession`\n * is main-thread only, and it is what puts the title on the OS overlay\n * and makes the hardware keys work. Play and pause from those keys act\n * on the element directly and come back as samples; next and previous\n * are forwarded as actions, because an element has no idea what a\n * playlist is.\n */\nexport class AudioSink {\n /**\n * The element that is playing, and a spare that may be holding the\n * next track.\n *\n * Two rather than one because a gapless change needs the next track\n * already buffered when the current one ends, and an element cannot\n * buffer a second source. `preload` fills the spare; a `load` of the\n * source the spare is holding swaps them, so the gap between tracks\n * is the browser's and not this app's.\n *\n * Not readonly: swapping is the whole mechanism.\n */\n private element: AudioElementLike;\n private spare: AudioElementLike;\n /** What the spare has been asked to hold, or '' when it holds nothing. */\n private prepared = '';\n private readonly session: MediaSessionLike | null;\n private readonly sampleEveryMs: number;\n private timer: ReturnType<typeof setInterval> | null = null;\n private waiting = false;\n /** Why the last play() was refused, reported on the next sample and then forgotten. */\n private refused: string | undefined;\n private readonly onEvent = (event: Event): void => this.handleEvent(event.type);\n\n constructor(\n private readonly out: AudioSinkOutput,\n options: AudioSinkOptions = {}\n ) {\n const create = options.createElement ?? (() => new Audio() as unknown as AudioElementLike);\n this.element = create();\n this.element.preload = 'auto';\n this.spare = create();\n this.spare.preload = 'auto';\n this.session =\n options.mediaSession !== undefined\n ? options.mediaSession\n : typeof navigator !== 'undefined' && 'mediaSession' in navigator\n ? (navigator.mediaSession as unknown as MediaSessionLike)\n : null;\n this.sampleEveryMs = options.sampleEveryMs ?? DEFAULT_SAMPLE_EVERY_MS;\n for (const type of SAMPLED_EVENTS) {\n this.element.addEventListener(type, this.onEvent);\n }\n this.bindSession();\n }\n\n handle(request: AudioRequest): void {\n switch (request.type) {\n case 'load':\n // The spare is holding exactly this: swap to it rather than\n // fetching the same bytes twice, which is what makes the change\n // gapless.\n if (this.prepared !== '' && this.prepared === request.src) {\n this.swap();\n } else {\n this.element.src = request.src;\n this.element.load();\n }\n this.waiting = true;\n if (request.autoplay) {\n this.play();\n } else {\n this.emit();\n }\n return;\n case 'preload':\n this.preload(request.src);\n return;\n case 'play':\n this.play();\n return;\n case 'pause':\n this.element.pause();\n return;\n case 'seek':\n this.element.currentTime = request.seconds;\n return;\n case 'volume':\n this.element.volume = request.level;\n return;\n case 'metadata':\n this.setMetadata(request.metadata);\n return;\n }\n }\n\n /**\n * Asks the spare element to start buffering a source.\n *\n * Idempotent, because the application worker resolves the next url\n * whenever the queue moves and will often ask for the same one twice.\n * An empty source clears the spare, which is what happens when the\n * queue is at its end.\n */\n private preload(src: string): void {\n if (src === this.prepared) {\n return;\n }\n this.prepared = src;\n if (src === '') {\n this.spare.pause();\n this.spare.src = '';\n return;\n }\n // The volume has to travel, or a swap is a jump in loudness.\n this.spare.volume = this.element.volume;\n this.spare.src = src;\n this.spare.load();\n }\n\n /**\n * Makes the spare the element that plays.\n *\n * The listeners move with the role rather than sitting on both, so a\n * spare quietly buffering never emits a sample and the application\n * hears one timeline. The element that steps aside is emptied so it\n * holds no bytes while it waits to be the spare again.\n */\n private swap(): void {\n for (const type of SAMPLED_EVENTS) {\n this.element.removeEventListener(type, this.onEvent);\n }\n const previous = this.element;\n this.element = this.spare;\n this.spare = previous;\n this.prepared = '';\n this.spare.pause();\n this.spare.src = '';\n for (const type of SAMPLED_EVENTS) {\n this.element.addEventListener(type, this.onEvent);\n }\n }\n\n dispose(): void {\n this.stopTimer();\n for (const type of SAMPLED_EVENTS) {\n this.element.removeEventListener(type, this.onEvent);\n }\n this.element.pause();\n this.element.src = '';\n this.spare.pause();\n this.spare.src = '';\n if (this.session !== null) {\n for (const action of ['play', 'pause', 'previoustrack', 'nexttrack', 'seekto']) {\n try {\n this.session.setActionHandler(action, null);\n } catch {\n // A browser that does not know the action throws; nothing to undo.\n }\n }\n this.session.metadata = null;\n this.session.playbackState = 'none';\n }\n }\n\n private play(): void {\n this.refused = undefined;\n const started = this.element.play();\n // A browser may refuse a play that no gesture authorised; the\n // element stays paused and the sample says why, so the screen can\n // show a paused player rather than one that claims to be playing.\n started?.catch((error: unknown) => {\n this.waiting = false;\n this.refused = error instanceof Error ? error.name : String(error);\n this.emit();\n });\n }\n\n private handleEvent(type: string): void {\n switch (type) {\n case 'loadstart':\n case 'waiting':\n case 'stalled':\n this.waiting = true;\n break;\n case 'canplay':\n case 'playing':\n case 'pause':\n case 'ended':\n case 'error':\n case 'emptied':\n this.waiting = false;\n break;\n default:\n break;\n }\n this.emit();\n }\n\n private status(): AudioStatus {\n const element = this.element;\n if (element.error !== null) {\n return 'error';\n }\n if (element.src === '' || element.src === undefined) {\n return 'idle';\n }\n if (element.ended) {\n return 'ended';\n }\n if (element.paused) {\n return this.waiting && this.refused === undefined ? 'loading' : 'paused';\n }\n return this.waiting ? 'loading' : 'playing';\n }\n\n private emit(): void {\n const element = this.element;\n const status = this.status();\n const error =\n status === 'error' ? (element.error?.message ?? `media error ${element.error?.code ?? ''}`.trim()) : this.refused;\n this.out.sample({\n status,\n position: element.currentTime,\n duration: element.duration,\n buffered: bufferedEnd(element),\n at: epochNow(),\n ...(error === undefined ? {} : { error })\n });\n if (this.session !== null) {\n this.session.playbackState = status === 'playing' ? 'playing' : status === 'idle' ? 'none' : 'paused';\n }\n if (status === 'playing') {\n this.startTimer();\n } else {\n this.stopTimer();\n }\n }\n\n private startTimer(): void {\n if (this.timer !== null) {\n return;\n }\n this.timer = setInterval(() => this.emit(), this.sampleEveryMs);\n }\n\n private stopTimer(): void {\n if (this.timer !== null) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n private setMetadata(metadata: AudioMetadata | null): void {\n if (this.session === null) {\n return;\n }\n if (metadata === null || typeof MediaMetadata === 'undefined') {\n this.session.metadata = null;\n return;\n }\n this.session.metadata = new MediaMetadata({\n title: metadata.title,\n artist: metadata.artist,\n album: metadata.album ?? '',\n artwork: metadata.artwork === undefined ? [] : [{ src: metadata.artwork }]\n });\n }\n\n private bindSession(): void {\n const session = this.session;\n if (session === null) {\n return;\n }\n const bind = (action: string, handler: (details: { seekTime?: number }) => void): void => {\n try {\n session.setActionHandler(action, handler);\n } catch {\n // Not every browser knows every action.\n }\n };\n bind('play', () => {\n this.play();\n this.out.action('play');\n });\n bind('pause', () => {\n this.element.pause();\n this.out.action('pause');\n });\n bind('previoustrack', () => this.out.action('previous'));\n bind('nexttrack', () => this.out.action('next'));\n bind('seekto', details => {\n if (details.seekTime !== undefined) {\n this.element.currentTime = details.seekTime;\n }\n });\n }\n}\n\n/** The end of the buffered range the play head is in, or the head itself when nothing is buffered there. */\nfunction bufferedEnd(element: AudioElementLike): number {\n const ranges = element.buffered;\n const at = element.currentTime;\n for (let index = 0; index < ranges.length; index++) {\n if (ranges.start(index) <= at && at <= ranges.end(index)) {\n return ranges.end(index);\n }\n }\n return at;\n}\n","import type { EditingState, UiSemanticsRecord } from 'gesso-core';\n\n/**\n * Where the proxy sends what it hears. `WorkerApp` posts each call to\n * the render worker; `GessoApp` calls the runtime's editing controller\n * directly.\n */\nexport interface EditingProxySink {\n beforeInput(inputType: string, data: string | null): void;\n compositionStart(): void;\n compositionUpdate(text: string, caret: number): void;\n compositionEnd(text: string): void;\n paste(text: string): void;\n /** The proxy lost focus to something outside the app. */\n blur(): void;\n /**\n * Key events reach the proxy's element, not the canvas, while it has\n * focus. A shell that listens on the canvas forwards them from here;\n * one that listens on `window` sees them anyway and leaves these out.\n */\n keyDown?(event: KeyboardEvent): void;\n keyUp?(event: KeyboardEvent): void;\n}\n\n/** Composition-owned input types: the composition events carry these. */\nconst COMPOSITION_INPUT_TYPES = new Set(['insertCompositionText', 'insertFromComposition', 'deleteCompositionText']);\n/**\n * Input types the keys already carry. Backspace, Delete and Enter reach\n * the runtime as key presses and are applied there; the textarea fires\n * these too, but only when its mirror happens to have something to\n * delete at the caret, so they cannot be the path — and forwarding them\n * as well would apply each edit twice. Undo and redo likewise: the\n * browser has no history of ours to replay.\n */\nconst KEY_INPUT_TYPES = new Set([\n 'deleteContentBackward',\n 'deleteContentForward',\n 'deleteWordBackward',\n 'deleteWordForward',\n 'deleteSoftLineBackward',\n 'deleteSoftLineForward',\n 'deleteHardLineBackward',\n 'deleteHardLineForward',\n 'insertLineBreak',\n 'insertParagraph',\n 'historyUndo',\n 'historyRedo'\n]);\n\n/**\n * The main thread's half of text editing: a hidden `<textarea>`.\n *\n * A canvas cannot receive text. Keyboard events carry keys, not\n * characters: dead keys, the OS keyboard layout and above all an IME\n * resolve to text only inside an editable DOM element, through\n * `beforeinput` and the `composition*` events. So while the runtime\n * reports a focused editable, this element takes DOM focus and:\n *\n * - forwards every `beforeinput` as an edit intent and cancels it, so\n * the element's own content never diverges from the runtime's;\n * - empties itself when a composition starts, so the composition\n * text is the whole value and the IME candidate window opens at the\n * element's top-left — which is positioned at the runtime's caret;\n * - reports the composition through `input` events while it is open\n * and its result at `compositionend`;\n * - handles copy, cut and paste with the runtime's text, since the\n * clipboard is only reachable from a user gesture on this thread;\n * - mirrors the runtime's text and selection between edits, so native\n * copy, IME context and, later, assistive technology see the real\n * text.\n *\n * Nothing here reads the textarea's content as truth except the\n * composition string, which is the one thing only the browser knows.\n */\nexport class EditingProxy {\n private readonly textarea: HTMLTextAreaElement;\n private readonly doc: Document;\n private readonly view: Window | null;\n private state: EditingState | null = null;\n private composing = false;\n private disposed = false;\n /** Set while `raiseKeyboard` drops focus on purpose; see there. */\n private refocusing = false;\n private readonly detach: () => void;\n\n constructor(\n private readonly canvas: HTMLCanvasElement,\n private readonly sink: EditingProxySink\n ) {\n this.doc = canvas.ownerDocument;\n this.view = this.doc.defaultView;\n const textarea = this.doc.createElement('textarea');\n this.textarea = textarea;\n textarea.setAttribute('aria-hidden', 'true');\n textarea.setAttribute('autocomplete', 'off');\n textarea.setAttribute('autocorrect', 'off');\n textarea.setAttribute('autocapitalize', 'off');\n textarea.setAttribute('spellcheck', 'false');\n textarea.setAttribute('wrap', 'off');\n textarea.tabIndex = -1;\n Object.assign(textarea.style, {\n position: 'fixed',\n left: '0px',\n top: '0px',\n width: '1px',\n height: '1em',\n margin: '0',\n padding: '0',\n border: '0',\n outline: 'none',\n opacity: '0',\n overflow: 'hidden',\n resize: 'none',\n whiteSpace: 'pre',\n pointerEvents: 'none',\n zIndex: '2147483647'\n } as Partial<CSSStyleDeclaration>);\n this.doc.body.appendChild(textarea);\n this.detach = this.listen();\n }\n\n /** True while an editable in the runtime has focus. */\n get active(): boolean {\n return this.state !== null;\n }\n\n /** The hidden element, for tests. */\n get element(): HTMLTextAreaElement {\n return this.textarea;\n }\n\n /**\n * The runtime's editing state changed. Null means no editable has\n * focus: the element gives focus back to the canvas so keys keep\n * reaching the app.\n */\n update(state: EditingState | null): void {\n if (this.disposed) {\n return;\n }\n const wasActive = this.state !== null;\n this.state = state;\n if (state === null) {\n if (wasActive) {\n if (this.doc.activeElement === this.textarea) {\n this.textarea.blur();\n this.canvas.focus({ preventScroll: true });\n }\n }\n return;\n }\n this.position(state);\n this.mirror(state);\n // What the phone's Return key should say. A single-line field has\n // nothing to insert a newline into, and a keyboard offering one is\n // offering a key that does nothing.\n this.textarea.setAttribute('enterkeyhint', state.multiline ? 'enter' : 'done');\n if (!wasActive || this.doc.activeElement !== this.textarea) {\n const active = this.doc.activeElement;\n // Take focus only from the canvas or from nothing: an editable\n // gaining focus in the app must not steal it from page chrome.\n if (active === null || active === this.doc.body || active === this.canvas || active === this.textarea) {\n this.textarea.focus({ preventScroll: true });\n }\n }\n }\n\n /**\n * Takes DOM focus for the focused editable, unconditionally.\n *\n * `update()` is careful about where it takes focus from — page\n * chrome outside the app must keep it — but the accessibility\n * mirror calls this only when the runtime has just said an editable\n * holds focus, and the element it is taking focus from is the\n * mirror's own. Without it, moving focus from a button to a field\n * would leave DOM focus on the button's mirrored element and the\n * IME with nothing to compose into.\n */\n focus(): void {\n if (this.disposed || this.state === null || this.doc.activeElement === this.textarea) {\n return;\n }\n this.textarea.focus({ preventScroll: true });\n }\n\n /**\n * Re-takes DOM focus from inside a user gesture, so a phone opens its\n * keyboard.\n *\n * A phone raises the keyboard for a focus that a person's gesture\n * caused, and for no other. In the worker configuration the runtime's\n * answer to a press — *this* node is editable, it has focus now —\n * arrives as a message a frame later, and `update()` focuses the\n * element from inside that message handler. The task the gesture\n * belonged to is over by then, so iOS moves focus and leaves the\n * keyboard down: a field the person is demonstrably typing into, with\n * nothing to type on.\n *\n * The shell calls this from the `pointerup` of the press that opened\n * the editable, which is a gesture task, and only for a press that\n * turned editing on — re-asserting focus while the keyboard is\n * already up is what makes it flicker.\n *\n * Focus has to be dropped and retaken rather than simply retaken:\n * focusing an element that is already focused does nothing at all,\n * and it already is focused — that is the whole problem. The blur is\n * ours, so the listener that would report it to the runtime as the\n * person leaving the field is suppressed for its duration.\n *\n * Nothing calls it in the single-threaded configuration and nothing\n * should: there, `update()` runs inside the pointerdown's own call\n * stack and the browser sees the focus as the gesture's.\n */\n raiseKeyboard(): void {\n if (this.disposed || this.state === null) {\n return;\n }\n const textarea = this.textarea;\n if (this.doc.activeElement !== textarea) {\n textarea.focus({ preventScroll: true });\n return;\n }\n this.refocusing = true;\n try {\n textarea.blur();\n textarea.focus({ preventScroll: true });\n } finally {\n this.refocusing = false;\n }\n }\n\n /**\n * Describes the focused editable on this element, for the\n * accessibility mirror (`SemanticsMirror`).\n *\n * The textarea is `aria-hidden` by default because an unlabelled\n * text box floating over an application is noise; when the mirror\n * hands it a record it stops being unlabelled, and becomes the one\n * element that is both what the assistive technology reads and what\n * the person is really typing into. That the two cannot disagree is\n * the reason the field's semantics live here rather than on a second\n * element beside it.\n */\n describe(record: UiSemanticsRecord | null): void {\n if (this.disposed) {\n return;\n }\n const textarea = this.textarea;\n if (record === null) {\n textarea.setAttribute('aria-hidden', 'true');\n for (const attribute of [\n 'role',\n 'aria-label',\n 'aria-description',\n 'aria-required',\n 'aria-invalid',\n 'aria-readonly',\n 'aria-disabled'\n ]) {\n textarea.removeAttribute(attribute);\n }\n return;\n }\n textarea.removeAttribute('aria-hidden');\n setOrClear(textarea, 'role', record.role);\n setOrClear(textarea, 'aria-label', record.label);\n setOrClear(textarea, 'aria-description', record.description);\n const states = new Set(record.states ?? []);\n setOrClear(textarea, 'aria-required', states.has('required') ? 'true' : undefined);\n setOrClear(textarea, 'aria-invalid', states.has('invalid') ? 'true' : undefined);\n setOrClear(textarea, 'aria-readonly', states.has('readonly') ? 'true' : undefined);\n setOrClear(textarea, 'aria-disabled', record.disabled === true ? 'true' : undefined);\n }\n\n dispose(): void {\n if (this.disposed) {\n return;\n }\n this.disposed = true;\n this.detach();\n this.state = null;\n this.textarea.remove();\n }\n\n /** Puts the element where the caret is, so the IME window opens there. */\n private position(state: EditingState): void {\n const rect = this.canvas.getBoundingClientRect();\n const height = Math.max(1, state.caret.height);\n const left = rect.left + Math.min(Math.max(0, state.caret.x), Math.max(0, rect.width - 1));\n const top = rect.top + Math.min(Math.max(0, state.caret.y), Math.max(0, rect.height - height));\n const style = this.textarea.style;\n style.left = `${left}px`;\n style.top = `${top}px`;\n style.height = `${height}px`;\n style.fontSize = `${Math.max(1, Math.round(height * 0.75))}px`;\n style.lineHeight = `${height}px`;\n }\n\n /** Mirrors the runtime's text and selection, except while the IME owns the element. */\n private mirror(state: EditingState): void {\n if (this.composing) {\n return;\n }\n const textarea = this.textarea;\n if (textarea.value !== state.text) {\n textarea.value = state.text;\n }\n if (textarea.selectionStart !== state.selectionStart || textarea.selectionEnd !== state.selectionEnd) {\n try {\n textarea.setSelectionRange(state.selectionStart, state.selectionEnd);\n } catch {\n // Some engines throw for an offset past the value; the next\n // state will be consistent.\n }\n }\n }\n\n private listen(): () => void {\n const textarea = this.textarea;\n\n const onBeforeInput = (event: Event): void => {\n const input = event as InputEvent;\n const type = input.inputType;\n if (COMPOSITION_INPUT_TYPES.has(type) || this.composing) {\n // Not cancelable, and carried by the composition events.\n return;\n }\n event.preventDefault();\n if (KEY_INPUT_TYPES.has(type) || type === 'insertFromPaste') {\n // Deletes, newlines and undo come from the keys; paste from the\n // paste event.\n return;\n }\n this.sink.beforeInput(type, input.data ?? null);\n };\n\n const onCompositionStart = (): void => {\n this.composing = true;\n // Empty, so the composition text is the whole value and the\n // element's caret — where the IME opens — is at its top-left.\n textarea.value = '';\n this.sink.compositionStart();\n };\n\n const onInput = (): void => {\n if (!this.composing) {\n return;\n }\n const text = textarea.value;\n const caret = textarea.selectionStart ?? text.length;\n this.sink.compositionUpdate(text, Math.max(0, Math.min(caret, text.length)));\n };\n\n const onCompositionEnd = (event: Event): void => {\n if (!this.composing) {\n return;\n }\n this.composing = false;\n const text = (event as CompositionEvent).data ?? textarea.value;\n this.sink.compositionEnd(text);\n if (this.state !== null) {\n this.mirror(this.state);\n }\n };\n\n const onPaste = (event: Event): void => {\n const clipboard = (event as ClipboardEvent).clipboardData;\n event.preventDefault();\n const text = clipboard?.getData('text/plain') ?? '';\n if (text.length > 0) {\n this.sink.paste(text);\n }\n };\n\n const onCopy = (event: Event): void => {\n const clipboard = (event as ClipboardEvent).clipboardData;\n const state = this.state;\n if (clipboard === null || state === null) {\n return;\n }\n event.preventDefault();\n clipboard.setData('text/plain', state.text.slice(state.selectionStart, state.selectionEnd));\n };\n\n const onCut = (event: Event): void => {\n const state = this.state;\n onCopy(event);\n if (state !== null && state.selectionEnd > state.selectionStart) {\n this.sink.beforeInput('deleteByCut', null);\n }\n };\n\n const onKeyDown = (event: Event): void => {\n this.sink.keyDown?.(event as KeyboardEvent);\n };\n const onKeyUp = (event: Event): void => {\n this.sink.keyUp?.(event as KeyboardEvent);\n };\n\n const onBlur = (event: Event): void => {\n if (this.refocusing) {\n // Our own blur, half of retaking focus inside a gesture. The\n // person has not left anything.\n return;\n }\n // Focus moved within the page (not the window losing focus, which\n // also blurs) to something other than the canvas: the app's\n // editable should stop showing a caret.\n const related = (event as FocusEvent).relatedTarget;\n if (this.state !== null && this.doc.hasFocus() && related !== this.canvas) {\n this.sink.blur();\n }\n };\n\n const onReposition = (): void => {\n if (this.state !== null) {\n this.position(this.state);\n }\n };\n\n textarea.addEventListener('beforeinput', onBeforeInput);\n textarea.addEventListener('compositionstart', onCompositionStart);\n textarea.addEventListener('input', onInput);\n textarea.addEventListener('compositionend', onCompositionEnd);\n textarea.addEventListener('paste', onPaste);\n textarea.addEventListener('copy', onCopy);\n textarea.addEventListener('cut', onCut);\n textarea.addEventListener('keydown', onKeyDown);\n textarea.addEventListener('keyup', onKeyUp);\n textarea.addEventListener('blur', onBlur);\n this.view?.addEventListener('scroll', onReposition, { capture: true, passive: true });\n this.view?.addEventListener('resize', onReposition);\n\n return () => {\n textarea.removeEventListener('beforeinput', onBeforeInput);\n textarea.removeEventListener('compositionstart', onCompositionStart);\n textarea.removeEventListener('input', onInput);\n textarea.removeEventListener('compositionend', onCompositionEnd);\n textarea.removeEventListener('paste', onPaste);\n textarea.removeEventListener('copy', onCopy);\n textarea.removeEventListener('cut', onCut);\n textarea.removeEventListener('keydown', onKeyDown);\n textarea.removeEventListener('keyup', onKeyUp);\n textarea.removeEventListener('blur', onBlur);\n this.view?.removeEventListener('scroll', onReposition, { capture: true });\n this.view?.removeEventListener('resize', onReposition);\n };\n }\n}\n\n/**\n * Writes text to the system clipboard from the main thread. The async\n * API needs a secure context and, in some browsers, a recent user\n * gesture; the `execCommand` fallback covers the rest.\n */\nexport function writeClipboard(text: string, doc: Document = document): void {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard !== undefined && typeof clipboard.writeText === 'function') {\n clipboard.writeText(text).catch(() => copyWithExecCommand(text, doc));\n return;\n }\n copyWithExecCommand(text, doc);\n}\n\nfunction copyWithExecCommand(text: string, doc: Document): void {\n const previous = doc.activeElement as HTMLElement | null;\n const scratch = doc.createElement('textarea');\n scratch.value = text;\n scratch.style.position = 'fixed';\n scratch.style.opacity = '0';\n doc.body.appendChild(scratch);\n scratch.select();\n try {\n doc.execCommand('copy');\n } finally {\n scratch.remove();\n previous?.focus?.({ preventScroll: true });\n }\n}\n\nfunction setOrClear(element: HTMLElement, attribute: string, value: string | undefined): void {\n if (value === undefined) {\n element.removeAttribute(attribute);\n } else {\n element.setAttribute(attribute, value);\n }\n}\n","import type { UiSemanticsAction, UiSemanticsRecord, UiSemanticsUpdate } from 'gesso-core';\n\n/**\n * Where the mirror sends what it hears.\n *\n * `WorkerApp` posts each call to the render worker; `GessoApp` calls\n * the runtime directly. The same split as `EditingProxySink`, and for\n * the same reason: nothing here knows whether the runtime is a\n * function call or a thread away.\n */\nexport interface SemanticsMirrorSink {\n /** An assistive technology pressed, focused or set the value of a node. */\n action(action: UiSemanticsAction): void;\n /**\n * Key events reach a mirrored element, not the canvas, whenever the\n * app has focus — because the element holding DOM focus *is* the\n * mirror's. A shell that listens on the canvas forwards them from\n * here; one that listens on `window` sees them anyway and leaves\n * these out.\n */\n keyDown?(event: KeyboardEvent): void;\n keyUp?(event: KeyboardEvent): void;\n}\n\n/**\n * The editing proxy, as the mirror needs it.\n *\n * A focused editable is the one node the mirror must not take DOM\n * focus for: the hidden textarea has it, because that is the only way\n * an IME can compose (F2). So the mirror hands that element the\n * record instead, and the assistive technology reads the field it is\n * really typing into.\n */\nexport interface EditingMirrorTarget {\n /** True while the proxy holds DOM focus for a focused editable. */\n readonly active: boolean;\n /** Describes the focused editable on the proxy's element, or clears it. */\n describe(record: UiSemanticsRecord | null): void;\n /** Takes DOM focus back for the focused editable. */\n focus(): void;\n}\n\n/** Which ARIA attribute each semantic state becomes, and with what value. */\nconst STATE_ATTRIBUTES: Record<string, [attribute: string, value: string]> = {\n checked: ['aria-checked', 'true'],\n mixed: ['aria-checked', 'mixed'],\n expanded: ['aria-expanded', 'true'],\n collapsed: ['aria-expanded', 'false'],\n selected: ['aria-selected', 'true'],\n pressed: ['aria-pressed', 'true'],\n busy: ['aria-busy', 'true'],\n invalid: ['aria-invalid', 'true'],\n required: ['aria-required', 'true'],\n readonly: ['aria-readonly', 'true'],\n modal: ['aria-modal', 'true']\n};\n\n/**\n * Roles whose checked state is required rather than optional.\n *\n * A component publishes `checked` when it is on and nothing when it is\n * off, which is the right shape for a state list. ARIA does not agree:\n * on these roles `aria-checked` is a required attribute, and one that\n * is absent means \"this is not a checkbox after all\" rather than \"this\n * checkbox is off\". A screen reader then has nothing to announce.\n *\n * Filled in here rather than in each component for the reason\n * The reason: what an assistive technology needs is\n * the mirror's business, and a rule in one file cannot be forgotten by\n * the next control somebody writes. Found on a `RadioGroup` in a native\n * window, where the unselected radio carried no `aria-checked` at all.\n */\nconst CHECKABLE_ROLES: ReadonlySet<string> = new Set([\n 'checkbox',\n 'radio',\n 'switch',\n 'menuitemcheckbox',\n 'menuitemradio'\n]);\n\n/** Every attribute a record can write, so clearing one is a fixed list. */\nconst RECORD_ATTRIBUTES: readonly string[] = [\n 'role',\n 'aria-label',\n 'aria-description',\n 'aria-live',\n 'aria-disabled',\n 'aria-valuenow',\n 'aria-valuemin',\n 'aria-valuemax',\n 'aria-valuetext',\n 'aria-posinset',\n 'aria-setsize',\n 'aria-level',\n 'aria-checked',\n 'aria-expanded',\n 'aria-selected',\n 'aria-pressed',\n 'aria-busy',\n 'aria-invalid',\n 'aria-required',\n 'aria-readonly',\n 'aria-modal'\n];\n\n/**\n * Roles whose accessible name comes from what the element contains\n * rather than from an `aria-label`, plus prose, which has no role at\n * all. Everything else is a control, and a control is named.\n */\nconst NAMED_BY_CONTENT: ReadonlySet<string> = new Set(['heading', 'paragraph']);\n\n/**\n * Live regions. Their name comes from an `aria-label` like any control's,\n * because the platform does not name a status from its content; but a\n * live region announces its *content* when it changes, and an\n * `aria-label` changing is silent, so the name is written as the text\n * as well. Chrome reports both, and reads the text out on change.\n */\nconst LIVE_ROLES: ReadonlySet<string> = new Set(['status', 'alert']);\n\n/**\n * Roles whose value the platform reads out of the element's content\n * rather than off an attribute — a text field, in other words, whose\n * `value` in the accessibility tree is the text inside it.\n */\nconst VALUE_IN_CONTENT: ReadonlySet<string> = new Set(['textbox', 'searchbox']);\n\ninterface MirrorEntry {\n readonly element: HTMLElement;\n record: UiSemanticsRecord;\n /** The node's box in canvas coordinates, once one has been sent. */\n box?: MirrorBox;\n /**\n * What `position` last wrote into this element's style, so a frame\n * that recomputes the same four numbers can leave the style alone.\n * Absent until the first write, which is also why the zeroes\n * `createElement` sets do not count as written: they belong to an\n * element that has no box yet, and the first real box must land.\n */\n written?: WrittenOffset;\n}\n\n/**\n * The rounded offset an element's style already carries.\n *\n * Deliberately the *written* value rather than the box it came from:\n * that is what makes the comparison safe across a reparent. `position`\n * recomputes the offset from whatever parent the record names now, so\n * the cache is only ever asked whether the style string it is about to\n * write is the one already there — a question whose answer cannot go\n * stale while `position` is the only writer of these four properties.\n */\ninterface WrittenOffset {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\ntype MirrorBox = UiSemanticsUpdate['boxes'][number]['box'];\n\n/**\n * The off-screen DOM an assistive technology reads.\n *\n * A canvas has no accessibility tree of its own: a screen reader, an\n * OS accessibility API and an automated testing tool all see one empty\n * element where the whole application is. So the shell keeps a DOM\n * tree over the canvas — one transparent element per semantics record,\n * carrying that record's ARIA — and the platform reads *that*. This is\n * the approach Flutter web takes, for the same reason: it is the only\n * one that works with assistive technology that already exists, on\n * every one of the three webviews Gesso targets.\n *\n * Three rules keep it honest:\n *\n * - **It is a mirror, not a source.** Nothing here decides anything.\n * Records arrive from the runtime's semantics tree, boxes from its\n * layout engine, focus from its focus manager, and an action taken\n * on an element becomes ordinary input on the way back.\n * - **It never takes the pointer.** The container is\n * `pointer-events: none`, so a mouse press goes to the canvas\n * underneath as it always did. The clicks that arrive here are the\n * ones an assistive technology synthesises, which is exactly the\n * set that has nowhere else to go.\n * - **Elements are generic.** A `<div role=\"button\">` is announced\n * as a button but has no behaviour of its own, so Enter on it\n * reaches the app's keymap once instead of also synthesising a\n * click the way a real `<button>` would. Every activation path\n * ends in one `click` action.\n */\nexport class SemanticsMirror {\n private readonly container: HTMLElement;\n private readonly doc: Document;\n private readonly entries = new Map<string, MirrorEntry>();\n /** Element back to record id, for the events an assistive technology sends. */\n private readonly ids = new WeakMap<HTMLElement, string>();\n private readonly detach: () => void;\n private resizeObserver: ResizeObserver | null = null;\n private stopTracking: (() => void) | null = null;\n /** True while this class is the one moving DOM focus. */\n private applying = false;\n private focusedId: string | null = null;\n private disposed = false;\n\n constructor(\n private readonly canvas: HTMLCanvasElement,\n private readonly sink: SemanticsMirrorSink,\n private readonly editing: EditingMirrorTarget | null = null\n ) {\n this.doc = canvas.ownerDocument;\n const container = this.doc.createElement('div');\n this.container = container;\n container.setAttribute('data-gesso-semantics', '');\n Object.assign(container.style, {\n position: 'fixed',\n left: '0px',\n top: '0px',\n width: '0px',\n height: '0px',\n // The canvas keeps every pointer event it ever had; only\n // synthesised activations land in here.\n pointerEvents: 'none',\n // Not `display: none`, `visibility: hidden` or zero opacity:\n // each of those takes the subtree out of the accessibility tree\n // as well as out of the picture, which is the whole content of\n // this element. Transparent and un-drawable is the combination\n // that stays readable.\n overflow: 'hidden',\n margin: '0',\n padding: '0',\n border: '0',\n color: 'transparent',\n background: 'transparent',\n font: '1px sans-serif',\n zIndex: '2147483646'\n } as Partial<CSSStyleDeclaration>);\n this.doc.body.appendChild(container);\n this.detach = this.listen();\n this.trackCanvas();\n }\n\n /** The container, for tests and for a shell that wants to inspect it. */\n get element(): HTMLElement {\n return this.container;\n }\n\n /** The element standing for a node, if the mirror has one. */\n elementFor(id: string): HTMLElement | undefined {\n return this.entries.get(id)?.element;\n }\n\n /**\n * Applies one frame's worth of change: what the tree means, where it\n * sits, and what has focus — in that order, because focus can name a\n * node the same update introduced.\n */\n apply(update: UiSemanticsUpdate): void {\n if (this.disposed) {\n return;\n }\n for (const patch of update.patches) {\n if (patch.op === 'remove') {\n this.remove(patch.id);\n } else {\n this.upsert(patch.node);\n }\n }\n // Every box lands before any style is written, in two passes over\n // the update. The second pass then sees final boxes whichever order\n // the worker sent them in, where a single pass positioned a child\n // against its parent's *new* box and its own *old* one whenever the\n // parent came first — a wrong offset that the child's own turn\n // happened to correct a moment later.\n const moved = new Set<MirrorEntry>();\n for (const { id, box } of update.boxes) {\n const entry = this.entries.get(id);\n if (entry === undefined) {\n continue;\n }\n entry.box = box;\n moved.add(entry);\n }\n for (const entry of moved) {\n this.position(entry);\n // The children sit inside this element, so their offsets are\n // measured from it: a parent that moved carries them with it in\n // the DOM, and their own left and top have to give that back. Only\n // the children this update left alone need that, though — a child\n // with a box of its own in here is positioned by its own turn in\n // this same loop, and repositioning it here as well is the work a\n // scroll does twice over, since a scroll moves a parent and all of\n // its children together.\n //\n // Indexed over the live `children` collection rather than a copy:\n // `position` writes four style properties and nothing else, so no\n // node is inserted, removed or reordered while this runs, and the\n // collection cannot shift under the index. (`place` is the one\n // thing that moves elements, and it has already finished above.)\n // The copy `Array.from` made was an allocation per moved parent,\n // which on a scrolling list is one per visible row.\n const children = entry.element.children;\n for (let index = 0; index < children.length; index += 1) {\n const childId = this.ids.get(children[index] as HTMLElement);\n const childEntry = childId === undefined ? undefined : this.entries.get(childId);\n if (childEntry !== undefined && childEntry.box !== undefined && !moved.has(childEntry)) {\n this.position(childEntry);\n }\n }\n }\n if (update.focused !== undefined) {\n this.applyFocus(update.focused);\n }\n }\n\n dispose(): void {\n this.disposed = true;\n this.detach();\n this.stopTracking?.();\n this.stopTracking = null;\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.entries.clear();\n this.container.remove();\n }\n\n // ---------------------------------------------------------------------------\n // Records\n // ---------------------------------------------------------------------------\n\n private upsert(record: UiSemanticsRecord): void {\n const existing = this.entries.get(record.id);\n const element = existing?.element ?? this.createElement();\n if (existing === undefined) {\n this.entries.set(record.id, { element, record });\n this.ids.set(element, record.id);\n } else {\n existing.record = record;\n }\n this.describe(element, record);\n this.place(element, record);\n if (existing?.box !== undefined) {\n // Under a different parent, the same box is a different offset —\n // and `existing.record` is the new record by now, so the offset\n // `position` recomputes is measured from the new parent. That is\n // what keeps the written-offset cache honest here: it is compared\n // against a value that already accounts for the move, so a\n // reparent that changes the offset writes, and one that does not\n // needs no write because `place` has already moved the element.\n this.position(existing);\n }\n if (record.id === this.focusedId) {\n // A record that changed while focused: the proxy's copy of it\n // has to change too, or a screen reader reads the old value.\n this.editing?.describe(this.editing.active ? record : null);\n }\n }\n\n private createElement(): HTMLElement {\n const element = this.doc.createElement('div');\n // Reachable by a programmatic or assistive focus, never by Tab:\n // the app owns its own focus order and moves DOM focus to follow\n // it, so the browser walking this tree would be a second, silent\n // focus model.\n element.tabIndex = -1;\n Object.assign(element.style, {\n position: 'absolute',\n left: '0px',\n top: '0px',\n width: '0px',\n height: '0px',\n margin: '0',\n padding: '0',\n border: '0',\n outline: 'none',\n overflow: 'hidden',\n // The container is `pointer-events: none`, but an element that\n // opted back in would take presses from the canvas.\n pointerEvents: 'none',\n color: 'transparent'\n } as Partial<CSSStyleDeclaration>);\n return element;\n }\n\n /** Writes a record onto its element as ARIA, clearing what it no longer says. */\n private describe(element: HTMLElement, record: UiSemanticsRecord): void {\n for (const attribute of RECORD_ATTRIBUTES) {\n element.removeAttribute(attribute);\n }\n const label = record.label;\n if (record.role !== undefined) {\n element.setAttribute('role', record.role);\n }\n if (label !== undefined && record.role !== undefined && !NAMED_BY_CONTENT.has(record.role)) {\n element.setAttribute('aria-label', label);\n // A text box's *value* is its content, and a screen reader reads\n // it from there: `aria-valuetext` is for a slider, not a field.\n // Without this a person could hear that a note's body exists and\n // never hear a word of it.\n setText(\n element,\n VALUE_IN_CONTENT.has(record.role) ? (record.valueText ?? '') : LIVE_ROLES.has(record.role) ? label : ''\n );\n } else {\n // Prose, a heading and a paragraph are named by what they\n // contain — and prose is most of what a screen reader reads, so\n // it has to be real text in the document rather than a label on\n // an empty box.\n setText(element, label ?? '');\n }\n if (record.description !== undefined) {\n element.setAttribute('aria-description', record.description);\n }\n if (record.live !== undefined) {\n element.setAttribute('aria-live', record.live);\n }\n if (record.disabled === true) {\n element.setAttribute('aria-disabled', 'true');\n }\n for (const state of record.states ?? []) {\n const attribute = STATE_ATTRIBUTES[state];\n if (attribute !== undefined) {\n element.setAttribute(attribute[0], attribute[1]);\n }\n }\n const saysChecked = (record.states ?? []).some(state => state === 'checked' || state === 'mixed');\n if (record.role !== undefined && CHECKABLE_ROLES.has(record.role) && !saysChecked) {\n element.setAttribute('aria-checked', 'false');\n }\n setNumber(element, 'aria-valuenow', record.valueNow);\n setNumber(element, 'aria-valuemin', record.valueMin);\n setNumber(element, 'aria-valuemax', record.valueMax);\n setNumber(element, 'aria-posinset', record.posInSet);\n setNumber(element, 'aria-setsize', record.setSize);\n setNumber(element, 'aria-level', record.level);\n if (record.valueText !== undefined) {\n element.setAttribute('aria-valuetext', record.valueText);\n }\n }\n\n /**\n * Puts an element under its parent, at the index the record gives.\n *\n * Document order is the order a screen reader reads in, so it is not\n * cosmetic: a row that moved up a list has to move up here too. Adds\n * arrive in document order, so a missing later sibling only ever\n * means \"not yet\", and appending is right.\n */\n private place(element: HTMLElement, record: UiSemanticsRecord): void {\n const parent = record.parent === null ? this.container : this.entries.get(record.parent)?.element;\n if (parent === undefined) {\n return;\n }\n const at = parent.children[record.index];\n if (at === element) {\n return;\n }\n parent.insertBefore(element, at ?? null);\n }\n\n /**\n * Writes an element's box as an offset from its parent's.\n *\n * Every element is absolutely positioned and nested under its parent,\n * so a child's `left` and `top` are read from the parent's padding\n * box, not from the canvas. Writing canvas coordinates into a nested\n * element added the parent's offset twice, and the rectangle an\n * assistive technology measured for a control inside a region stood\n * well below where the control was drawn. A parent whose box has not\n * arrived yet counts as sitting at the origin; the box loop above\n * repositions the children when it does.\n *\n * Rounded, because a subpixel box would make the style string differ\n * on frames where nothing an assistive technology can perceive has\n * changed.\n *\n * And rounding is why the four values are remembered and compared\n * before they are written. A scroll marks the container's transform,\n * which is a laid-out frame, so the worker sends every moved box every\n * scroll frame — and a style write invalidates style for that element\n * whether or not the value changed, on the one thread this whole\n * architecture exists to keep idle. The comparison has to be against\n * the rounded numbers rather than against the box: a subpixel change\n * survives the worker's exact comparison and still rounds to the pixel\n * that is already there. The win it buys is bigger than it looks,\n * because during a scroll a parent and its children move by the same\n * delta, so every child's offset *from its parent* is unchanged and a\n * scrolling list writes nothing at all for its rows.\n *\n * It is a claim about the style, not about the box, so a reparent\n * cannot make it lie: the offset is recomputed from the parent the\n * record names now, and if that offset is the same then the string\n * already on the element is still the right one — the element moving\n * in the DOM is what changes where it lands, not a style write. This\n * holds exactly as long as `position` is the only thing writing\n * `left`, `top`, `width` and `height` on a mirrored element.\n */\n private position(entry: MirrorEntry): void {\n const box = entry.box;\n if (box === undefined) {\n return;\n }\n const parent = entry.record.parent === null ? undefined : this.entries.get(entry.record.parent)?.box;\n const left = Math.round(box.x - (parent?.x ?? 0));\n const top = Math.round(box.y - (parent?.y ?? 0));\n const width = Math.round(box.width);\n const height = Math.round(box.height);\n const written = entry.written;\n if (\n written !== undefined &&\n written.left === left &&\n written.top === top &&\n written.width === width &&\n written.height === height\n ) {\n return;\n }\n if (written === undefined) {\n entry.written = { left, top, width, height };\n } else {\n // Overwritten in place rather than replaced: the elements that do\n // move, move every frame of a drag or a scroll, and this is the\n // one allocation on that path.\n written.left = left;\n written.top = top;\n written.width = width;\n written.height = height;\n }\n const { element } = entry;\n element.style.left = `${left}px`;\n element.style.top = `${top}px`;\n element.style.width = `${width}px`;\n element.style.height = `${height}px`;\n }\n\n private remove(id: string): void {\n const entry = this.entries.get(id);\n if (entry === undefined) {\n return;\n }\n this.entries.delete(id);\n entry.element.remove();\n if (this.focusedId === id) {\n this.focusedId = null;\n }\n }\n\n // ---------------------------------------------------------------------------\n // Focus\n // ---------------------------------------------------------------------------\n\n /**\n * Moves DOM focus to follow the app's.\n *\n * The app's focus is the truth and this is the echo, which is why\n * every move here is flagged: the `focusin` it causes must not be\n * read back as the assistive technology having moved focus itself.\n */\n private applyFocus(id: string | null): void {\n this.focusedId = id;\n const entry = id === null ? undefined : this.entries.get(id);\n if (this.editing !== null && this.editing.active) {\n // The hidden textarea is where the person is really typing: it is\n // the only element an IME will compose into. Describing it is\n // what makes it the field rather than an anonymous text box, and\n // claiming focus is what moves the caret off whichever mirrored\n // element held it a moment ago.\n this.editing.describe(entry?.record ?? null);\n this.applying = true;\n try {\n this.editing.focus();\n } finally {\n this.applying = false;\n }\n return;\n }\n this.editing?.describe(null);\n this.applying = true;\n try {\n if (entry !== undefined) {\n entry.element.focus({ preventScroll: true });\n } else if (this.container.contains(this.doc.activeElement)) {\n // Focus left the app's tree — or landed on a node with no\n // semantics of its own. Either way the canvas takes it back,\n // so keys keep reaching the app.\n this.canvas.focus({ preventScroll: true });\n }\n } finally {\n this.applying = false;\n }\n }\n\n private listen(): () => void {\n const onClick = (event: Event): void => {\n const id = this.idOf(event.target);\n if (id !== null) {\n // Nothing on this thread knows whether the node handles a\n // press; the runtime routes it exactly as it routes a mouse's.\n this.sink.action({ id, action: 'click' });\n }\n };\n const onFocusIn = (event: FocusEvent): void => {\n if (this.applying) {\n return;\n }\n const id = this.idOf(event.target);\n if (id !== null && id !== this.focusedId) {\n // The assistive technology moved focus itself. The runtime\n // decides whether the node may have it — an open focus trap\n // refuses — and the answer comes back as the next update.\n this.sink.action({ id, action: 'focus' });\n }\n };\n const onKeyDown = (event: KeyboardEvent): void => this.sink.keyDown?.(event);\n const onKeyUp = (event: KeyboardEvent): void => this.sink.keyUp?.(event);\n this.container.addEventListener('click', onClick);\n this.container.addEventListener('focusin', onFocusIn);\n this.container.addEventListener('keydown', onKeyDown);\n this.container.addEventListener('keyup', onKeyUp);\n return () => {\n this.container.removeEventListener('click', onClick);\n this.container.removeEventListener('focusin', onFocusIn);\n this.container.removeEventListener('keydown', onKeyDown);\n this.container.removeEventListener('keyup', onKeyUp);\n };\n }\n\n /**\n * Which record an event landed on.\n *\n * A reverse index rather than an `instanceof HTMLElement` test and a\n * scan: the elements are this class's own, so identity is the whole\n * question, and an event on anything else is not ours.\n */\n private idOf(target: EventTarget | null): string | null {\n return this.ids.get(target as HTMLElement) ?? null;\n }\n\n // ---------------------------------------------------------------------------\n // Where the canvas is\n // ---------------------------------------------------------------------------\n\n /**\n * Keeps the container over the canvas.\n *\n * Boxes arrive in canvas pixels, so the container has to sit exactly\n * where the canvas does — and a `position: fixed` box moves relative\n * to the canvas whenever the page scrolls or the window resizes.\n * Reading the rect on those three signals rather than every frame is\n * deliberate: a `getBoundingClientRect()` per frame on the main\n * thread is the kind of forced layout the worker configuration\n * exists to avoid.\n */\n private trackCanvas(): void {\n const sync = (): void => {\n const rect = this.canvas.getBoundingClientRect();\n this.container.style.left = `${rect.left}px`;\n this.container.style.top = `${rect.top}px`;\n this.container.style.width = `${rect.width}px`;\n this.container.style.height = `${rect.height}px`;\n };\n sync();\n const view = this.doc.defaultView;\n if (view === null) {\n return;\n }\n view.addEventListener('resize', sync);\n view.addEventListener('scroll', sync, true);\n this.stopTracking = () => {\n view.removeEventListener('resize', sync);\n view.removeEventListener('scroll', sync, true);\n };\n if (typeof ResizeObserver !== 'undefined') {\n this.resizeObserver = new ResizeObserver(sync);\n this.resizeObserver.observe(this.canvas);\n }\n }\n}\n\nfunction setNumber(element: HTMLElement, attribute: string, value: number | undefined): void {\n if (value !== undefined) {\n element.setAttribute(attribute, String(value));\n }\n}\n\n/**\n * Writes an element's text without throwing away what is inside it.\n *\n * Assigning `textContent` replaces *every* child node, elements\n * included. That is what is wanted for prose and for a control named by\n * an `aria-label`, both of which are leaves, and it is exactly wrong\n * for a container: a `tablist` full of tabs, a labelled `group`, a\n * `region` holding a page. Describing one of those a second time — a\n * label that changed, a state that came and went — emptied it, and a\n * screen reader was told the container existed and nothing about what\n * was in it.\n *\n * Found on a tabbed page whose tabs were missing from the\n * accessibility tree while drawing correctly on screen, which is the\n * worst way for it to be wrong: no screenshot gate can see it. An\n * element with element children keeps them and takes its name from the\n * `aria-label` that was just written.\n */\nfunction setText(element: HTMLElement, text: string): void {\n if (element.children.length > 0) {\n return;\n }\n if (element.textContent !== text) {\n element.textContent = text;\n }\n}\n","import { classifyStorageError, storageErrorMessage } from '../storage/StorageAdapter';\nimport type { ShellStorageOp, ShellStorageResult } from './ShellService';\n\n/** What a shell needs of `localStorage`, and no more. */\nexport interface ShellLocalStore {\n getItem(key: string): string | null;\n setItem(key: string, value: string): void;\n removeItem(key: string): void;\n key(index: number): string | null;\n readonly length: number;\n}\n\nconst DENIED: ShellStorageResult = {\n outcome: 'denied',\n value: null,\n keys: [],\n error: 'This window has no localStorage.'\n};\n\n/**\n * Performs one storage request on the shell's `localStorage`.\n *\n * The whole of what the shell does for `ShellStorage`, written once\n * and called from both configurations, because the shell's half of a\n * request should be identical whether the render side is a worker or\n * the same thread. It decides nothing: the key it is given is the key\n * it uses, and every outcome goes back to the thread that asked.\n *\n * `open` is a function rather than a store, because reading\n * `window.localStorage` is itself what throws when a browser has\n * blocked it. Asking for it inside the `try` is what turns that into a\n * `denied` answer rather than an exception in the shell's message\n * handler.\n */\nexport function performShellStorage(\n request: { readonly op: ShellStorageOp; readonly key: string; readonly value?: string },\n open: () => ShellLocalStore | null | undefined\n): ShellStorageResult {\n try {\n const store = open();\n if (store === null || store === undefined) {\n return DENIED;\n }\n switch (request.op) {\n case 'read':\n return { outcome: 'ok', value: store.getItem(request.key), keys: [], error: null };\n case 'write':\n store.setItem(request.key, request.value ?? '');\n return { outcome: 'ok', value: null, keys: [], error: null };\n case 'remove':\n store.removeItem(request.key);\n return { outcome: 'ok', value: null, keys: [], error: null };\n case 'keys': {\n const keys: string[] = [];\n for (let at = 0; at < store.length; at++) {\n const key = store.key(at);\n if (key !== null) {\n keys.push(key);\n }\n }\n return { outcome: 'ok', value: null, keys, error: null };\n }\n }\n } catch (error) {\n return { outcome: classifyStorageError(error), value: null, keys: [], error: storageErrorMessage(error) };\n }\n}\n\n/** The answer for a shell that has no window at all. */\nexport function shellStorageDenied(): ShellStorageResult {\n return DENIED;\n}\n","/**\n * Watching one media query, from the thread that has a window.\n *\n * Extracted from `reducedMotion.ts` when the colour scheme needed the\n * same thing: `matchMedia` needs a window, the runtime that cares may\n * be in a worker, and every such preference has to be reported *once*\n * immediately as well as on change — a person who already has the\n * preference set gets no `change` event to tell the app about it.\n *\n * Deliberately holds no reference to a runtime. `GessoApp` hands what\n * it reports straight to the runtime; `WorkerApp` posts it across.\n *\n * Returns a function that stops watching. In an environment with no\n * `matchMedia` — a Node test, an old webview — it reports `false` once\n * and stops, which is the answer the platform gives when nobody has\n * expressed the preference.\n */\nexport function observeMediaQuery(query: string, onChange: (matches: boolean) => void): () => void {\n const view = typeof globalThis === 'undefined' ? undefined : (globalThis as { matchMedia?: typeof matchMedia });\n if (typeof view?.matchMedia !== 'function') {\n onChange(false);\n return () => {};\n }\n const list = view.matchMedia(query);\n onChange(list.matches);\n const listener = (event: MediaQueryListEvent): void => onChange(event.matches);\n // `addEventListener` on a MediaQueryList is the modern form; Safari\n // below 14 only has `addListener`, and WKWebView is one of the three\n // webviews E1 has to work on.\n if (typeof list.addEventListener === 'function') {\n list.addEventListener('change', listener);\n return () => list.removeEventListener('change', listener);\n }\n const legacy = list as unknown as {\n addListener(fn: (event: MediaQueryListEvent) => void): void;\n removeListener(fn: (event: MediaQueryListEvent) => void): void;\n };\n legacy.addListener(listener);\n return () => legacy.removeListener(listener);\n}\n","import { observeMediaQuery } from './mediaQuery';\n\n/** Which of the two appearances the platform is asking for. */\nexport type ColorScheme = 'light' | 'dark';\n\n/**\n * What a shell is told to report.\n *\n * `auto` follows the platform and is the default; the other two are an\n * override, for a host that has its own control — a documentation site\n * with a light/dark toggle, or a desktop window with an app-level\n * appearance setting that is not the OS's.\n */\nexport type ColorSchemePreference = ColorScheme | 'auto';\n\n/**\n * Watches the platform's colour-scheme preference.\n *\n * Shell-side for the same reason `observeReducedMotion` is: the query\n * needs a window, and the runtime that reports it to the application\n * may be in a worker. Reported once immediately as well as on change,\n * so an app started by someone in dark mode does not paint a light\n * first frame.\n *\n * The scheme is resolved here rather than on the far side — the worker\n * hears `light` or `dark` and never `auto`, because \"what the platform\n * says\" is a question only the thread with a window can ask.\n */\nexport function observeColorScheme(onChange: (scheme: ColorScheme) => void): () => void {\n return observeMediaQuery('(prefers-color-scheme: dark)', dark => onChange(dark ? 'dark' : 'light'));\n}\n","import { observeMediaQuery } from './mediaQuery';\n\n/**\n * Watches the platform's reduced-motion preference.\n *\n * The preference is a main-thread fact — `matchMedia` needs a window —\n * and the runtime that owns the animations may be in a worker, so this\n * is deliberately a shell-side helper with no reference to a runtime.\n * `GessoApp` hands what it reports straight to `setReducedMotion`;\n * `WorkerApp` posts it across. Reporting once immediately matters as\n * much as reporting changes: an app started by someone who already has\n * the preference on must not animate its first screen.\n *\n * Returns a function that stops watching. See `observeMediaQuery` for\n * what happens where there is no `matchMedia` at all.\n */\nexport function observeReducedMotion(onChange: (reduced: boolean) => void): () => void {\n return observeMediaQuery('(prefers-reduced-motion: reduce)', onChange);\n}\n","/**\n * Enters or leaves fullscreen on an element, across the prefixes that\n * are still out there.\n *\n * Deliberately fire-and-forget. `requestFullscreen` returns a promise\n * that rejects when the browser refuses -- it only grants fullscreen\n * during a gesture -- and there is nothing useful to do about that\n * here: whether it worked is reported back by the `fullscreenchange`\n * event, which is also the only thing that hears about the person\n * pressing Escape.\n */\nexport function setElementFullscreen(element: Element, enter: boolean): void {\n const document = documentOf(element);\n if (document === null) {\n return;\n }\n const anyDocument = document as Document & {\n webkitFullscreenElement?: Element | null;\n webkitExitFullscreen?: () => void;\n };\n if (enter) {\n const target = element as Element & { webkitRequestFullscreen?: () => void };\n try {\n if (typeof target.requestFullscreen === 'function') {\n void target.requestFullscreen().catch(() => {});\n } else {\n target.webkitRequestFullscreen?.();\n }\n } catch {\n // Refused, which the change event will confirm by not firing.\n }\n return;\n }\n try {\n if (typeof document.exitFullscreen === 'function') {\n void document.exitFullscreen().catch(() => {});\n } else {\n anyDocument.webkitExitFullscreen?.();\n }\n } catch {\n // Already out of it.\n }\n}\n\n/**\n * The document an element belongs to, or null where there is not one.\n *\n * A canvas double in a spec has no `ownerDocument`, and neither has an\n * `OffscreenCanvas`. Falling back to the ambient `document` covers the\n * first case where a real one exists, and answering null covers the\n * rest: fullscreen is a thing a page has, and somewhere without a page\n * should decline rather than throw.\n */\nfunction documentOf(element: Element): Document | null {\n const owner = (element as Partial<Element>).ownerDocument ?? null;\n if (owner !== null && typeof owner.addEventListener === 'function') {\n return owner;\n }\n const ambient = typeof globalThis.document === 'undefined' ? null : globalThis.document;\n return ambient !== null && typeof ambient.addEventListener === 'function' ? ambient : null;\n}\n\n/** Whether anything in the element's document is currently fullscreen. */\nexport function isDocumentFullscreen(element: Element): boolean {\n const document = documentOf(element) as (Document & { webkitFullscreenElement?: Element | null }) | null;\n if (document === null) {\n return false;\n }\n return (document.fullscreenElement ?? document.webkitFullscreenElement ?? null) !== null;\n}\n\n/** Calls back whenever the document enters or leaves fullscreen. */\nexport function observeFullscreen(element: Element, onChange: (active: boolean) => void): () => void {\n const document = documentOf(element);\n if (document === null) {\n return () => {};\n }\n const report = (): void => onChange(isDocumentFullscreen(element));\n document.addEventListener('fullscreenchange', report);\n document.addEventListener('webkitfullscreenchange', report);\n return () => {\n document.removeEventListener('fullscreenchange', report);\n document.removeEventListener('webkitfullscreenchange', report);\n };\n}\n\n/**\n * The box an application should lay itself out in, given whether its\n * canvas is currently filling the screen.\n *\n * **A `ResizeObserver` on the host does not see this happen**, and\n * that is the whole reason this exists. The fullscreen API lifts the\n * *canvas* out of the page and stretches it to the screen; the host\n * around it keeps the size it always had, so the observer never fires,\n * the runtime keeps laying out at the old size, and the browser scales\n * the result up. The picture looks right, and every coordinate is\n * wrong by the ratio between the two: a press near the bottom of a\n * fullscreen clip lands somewhere near the middle of the layout.\n *\n * So the size is read from the canvas while it is fullscreen and from\n * the host when it is not. On the way *out* the canvas has not been\n * put back yet when the event fires, which is why the host is the\n * right answer there rather than simply always asking the canvas.\n */\nexport function surfaceBox(canvas: Element, host: Element, active: boolean): { width: number; height: number } | null {\n const box = (active ? canvas : host).getBoundingClientRect();\n return box.width > 0 && box.height > 0 ? { width: box.width, height: box.height } : null;\n}\n\n/**\n * Runs a callback once the browser has actually applied the new\n * geometry.\n *\n * `fullscreenchange` fires *before* the new size is in the layout, so\n * reading a box from inside it gives the size the element had a moment\n * ago. Measured here: entering fullscreen reported the canvas as its\n * old preview size, the surface stayed that size, and the browser\n * stretched it to the screen, which is precisely the thing\n * `surfaceBox` exists to prevent. Two frames, because the first is the\n * one the change lands on.\n */\nexport function afterLayout(run: () => void): void {\n if (typeof globalThis.requestAnimationFrame !== 'function') {\n run();\n return;\n }\n globalThis.requestAnimationFrame(() => globalThis.requestAnimationFrame(run));\n}\n","/**\n * The window's address, as the only thing routing needs from a shell.\n *\n * A render worker has no `location` and no `history`, so a routed app\n * exchanges exactly one kind of value with the thread that does: a\n * url string, outward when the app navigates and inward when the back\n * button, the forward button or a typed address changes it. Everything\n * else about routing — patterns, params, guards, which screen — stays\n * in the worker, where the components are.\n *\n * Three modes, because a Gesso app runs in three kinds of window:\n *\n * - `path` — `pushState` against the document's path. What a web app\n * deployed at its own origin wants, and the default.\n * - `hash` — the app's url lives in the fragment, after an optional\n * `base`. For an app that shares a page with something else that\n * owns the path — the playground, whose own routes are hashes — and\n * for a static host that will not rewrite unknown paths onto the\n * app.\n * - `memory` — no window involvement at all. What a desktop window\n * wants, since it has no address bar to sync with, and\n * what a test wants.\n */\nexport type ShellHistoryMode = 'path' | 'hash' | 'memory';\n\nexport interface ShellHistoryOptions {\n readonly mode?: ShellHistoryMode;\n /**\n * `hash` mode only: what the app's url follows in the fragment.\n *\n * With `base: 'example-router'` the app's `/mail/2` is the whole\n * page's `#example-router/mail/2`, which leaves the first segment to\n * whatever else on the page is reading the hash.\n */\n readonly base?: string;\n /** `memory` mode only: where the app starts. Defaults to `/`. */\n readonly initialUrl?: string;\n}\n\nexport interface ShellHistory {\n /** The url the window is at now. */\n readonly url: string;\n push(url: string): void;\n replace(url: string): void;\n back(): void;\n forward(): void;\n /** Reports urls the person produced: back, forward, or a typed address. */\n onChange(listener: (url: string) => void): void;\n dispose(): void;\n}\n\n/**\n * Just the parts of a window this module uses.\n *\n * A parameter rather than the global so the browser modes can be\n * tested the way `EditingProxy` is: the contract with the DOM is\n * small, and stating it is what makes it assertable.\n */\nexport interface HistoryWindow {\n readonly location: { pathname: string; search: string; hash: string };\n readonly history: {\n pushState(data: unknown, title: string, url: string): void;\n replaceState(data: unknown, title: string, url: string): void;\n back(): void;\n forward(): void;\n };\n addEventListener(type: string, listener: () => void): void;\n removeEventListener(type: string, listener: () => void): void;\n}\n\nexport function createShellHistory(\n options: ShellHistoryOptions = {},\n host: HistoryWindow | undefined = typeof window === 'undefined' ? undefined : (window as unknown as HistoryWindow)\n): ShellHistory {\n const mode = options.mode ?? (host === undefined ? 'memory' : 'path');\n if (mode === 'memory' || host === undefined) {\n return new MemoryHistory(options.initialUrl ?? '/');\n }\n return new BrowserHistory(host, mode, options.base ?? '');\n}\n\nclass MemoryHistory implements ShellHistory {\n private readonly entries: string[];\n private index = 0;\n private listener: ((url: string) => void) | null = null;\n\n constructor(initialUrl: string) {\n this.entries = [initialUrl];\n }\n\n get url(): string {\n return this.entries[this.index]!;\n }\n\n push(url: string): void {\n this.entries.length = this.index + 1;\n this.entries.push(url);\n this.index = this.entries.length - 1;\n }\n\n replace(url: string): void {\n this.entries[this.index] = url;\n }\n\n back(): void {\n this.step(-1);\n }\n\n forward(): void {\n this.step(1);\n }\n\n onChange(listener: (url: string) => void): void {\n this.listener = listener;\n }\n\n dispose(): void {\n this.listener = null;\n }\n\n private step(delta: number): void {\n const next = this.index + delta;\n if (next < 0 || next >= this.entries.length) {\n return;\n }\n this.index = next;\n this.listener?.(this.url);\n }\n}\n\n/**\n * The two browser modes, which differ only in where the url is kept.\n *\n * Both write with `history.pushState`/`replaceState` — in hash mode\n * too, rather than by assigning `location.hash`, because assignment\n * cannot replace an entry and would leave every navigation in the back\n * stack whether the app asked for that or not.\n */\nclass BrowserHistory implements ShellHistory {\n private listener: ((url: string) => void) | null = null;\n private readonly onPopState: () => void;\n private readonly onHashChange: () => void;\n /**\n * The last url this class wrote.\n *\n * `pushState` fires neither `popstate` nor `hashchange`, so in a\n * browser nothing echoes; this is here for the hosts that do not\n * honour that — an embedded webview, a test — where an echo would\n * otherwise come back as a navigation the person never made. It is\n * cleared on the first event that is not the echo.\n */\n private written: string | null = null;\n\n constructor(\n private readonly host: HistoryWindow,\n private readonly mode: 'path' | 'hash',\n private readonly base: string\n ) {\n this.onPopState = () => this.report();\n this.onHashChange = () => this.report();\n host.addEventListener('popstate', this.onPopState);\n if (mode === 'hash') {\n // A fragment typed into the address bar changes no history entry,\n // so popstate alone would miss it.\n host.addEventListener('hashchange', this.onHashChange);\n }\n }\n\n get url(): string {\n const location = this.host.location;\n return this.mode === 'path' ? `${location.pathname}${location.search}` : this.fromHash();\n }\n\n push(url: string): void {\n this.written = url;\n this.host.history.pushState(null, '', this.toHref(url));\n }\n\n replace(url: string): void {\n this.written = url;\n this.host.history.replaceState(null, '', this.toHref(url));\n }\n\n back(): void {\n this.host.history.back();\n }\n\n forward(): void {\n this.host.history.forward();\n }\n\n onChange(listener: (url: string) => void): void {\n this.listener = listener;\n }\n\n dispose(): void {\n this.listener = null;\n this.host.removeEventListener('popstate', this.onPopState);\n this.host.removeEventListener('hashchange', this.onHashChange);\n }\n\n private report(): void {\n const url = this.url;\n if (url === this.written) {\n return;\n }\n this.written = null;\n this.listener?.(url);\n }\n\n private toHref(url: string): string {\n if (this.mode === 'path') {\n return url;\n }\n // The path and query the page was served at are kept: in hash mode\n // the app owns the fragment and nothing else.\n const location = this.host.location;\n return `${location.pathname}${location.search}#${this.base}${url}`;\n }\n\n private fromHash(): string {\n const hash = this.host.location.hash.replace(/^#/, '');\n if (this.base.length === 0) {\n return hash.length === 0 ? '/' : ensureLeadingSlash(hash);\n }\n if (hash === this.base) {\n return '/';\n }\n if (!hash.startsWith(`${this.base}/`)) {\n // The fragment belongs to something else on the page; the app is\n // at its own root rather than at a url it cannot read.\n return '/';\n }\n return ensureLeadingSlash(hash.slice(this.base.length));\n }\n}\n\nfunction ensureLeadingSlash(url: string): string {\n return url.startsWith('/') ? url : `/${url}`;\n}\n","import type { UiNodeReport } from '../NodeReport';\nimport type { DevtoolsEvent, DevtoolsRequest } from '../DevtoolsProtocol';\nimport { isConsoleEntryMessage, type ConsoleForwardingMessage } from '../../worker/captureConsole';\nimport type { FrameMetrics, RendererChoice } from '../GessoRuntime';\nimport {\n epochFromEvent,\n epochNow,\n isInputMessage,\n modifiersFrom,\n type RuntimeErrorSource,\n type RuntimeToShellMessage,\n type ShellToRuntimeMessage\n} from './RenderWorkerProtocol';\nimport {\n capturePointer,\n observeViewportInsets,\n pointerDeviceOf,\n prepareInputSurface,\n touchActionFor,\n wheelDeltaYOf,\n type UiScrollability\n} from 'gesso-core';\nimport { AudioSink } from '../AudioSink';\nimport { portHandle, type WorkerHandle } from '../../worker/WorkerPorts';\nimport { EditingProxy, writeClipboard } from '../EditingProxy';\nimport { SemanticsMirror } from '../SemanticsMirror';\nimport { performShellStorage } from '../shellStorage';\nimport { observeColorScheme, type ColorSchemePreference } from '../colorScheme';\nimport { observeReducedMotion } from '../reducedMotion';\nimport { createShellHistory, type ShellHistory, type ShellHistoryOptions } from '../shellHistory';\nimport { afterLayout, isDocumentFullscreen, observeFullscreen, setElementFullscreen, surfaceBox } from '../fullscreen';\n\n/**\n * What the shell needs to spawn and drive a render worker.\n *\n * There is deliberately nothing here about media. A resolver is a\n * function and no function crosses a `postMessage`, so an option on\n * this side could only ever be a promise the shell could not keep;\n * the worker entry builds its own and declares it with\n * `renderRoot(AppRoot).useMedia(...)`.\n */\n/**\n * Somewhere to send the application handshake that is not a `Worker`.\n *\n * A `MessagePort` satisfies it, and so does anything else that can\n * carry a message and a transferred port. It exists because the\n * application layer does not always live in a worker in this page: in\n * a desktop window it lives in another process, and what the shell\n * holds is one end of a bridge to it (`gesso-electrobun`).\n *\n * The shell treats an endpoint exactly as it treats a worker it was\n * handed rather than one it spawned: it wires it up, and it never\n * closes it.\n */\nexport interface AppLogicEndpoint {\n postMessage(message: unknown, transfer?: Transferable[]): void;\n addEventListener(type: 'message', listener: (event: MessageEvent<unknown>) => void): void;\n removeEventListener(type: 'message', listener: (event: MessageEvent<unknown>) => void): void;\n /** A `MessagePort` delivers nothing until this is called; a `Worker` has no such method. */\n start?: () => void;\n}\n\nexport interface WorkerAppOptions {\n /**\n * Spawns the render worker: the one that calls renderRoot(), and so\n * owns components, layout and drawing.\n *\n * Prefer the factory form. Bundlers only code-split a worker when\n * they can see `new Worker(new URL('./x.ts', import.meta.url))`\n * written out literally, and that cannot happen inside this file —\n * it has to appear in the calling module:\n *\n * renderWorker: () => new Worker(new URL('./app.render.worker.ts', import.meta.url), { type: 'module' })\n *\n * A URL is accepted for environments that resolve modules at\n * runtime, but a bundled build will not emit a chunk for it.\n */\n renderWorker: (() => Worker) | URL | string;\n /**\n * The rendering backend the worker draws with. Defaults to `auto`,\n * which is WebGPU where the worker has it and Canvas2D elsewhere;\n * `webgpu` and `auto` both fall back to Canvas2D when the browser\n * has no WebGPU in workers, and the frame metrics say which one is\n * drawing.\n */\n renderer?: RendererChoice;\n /**\n * Receives frame timings reported by the render worker.\n *\n * `FrameMetrics` itself rather than a structural copy of it: the\n * copy had already fallen a field behind the real thing once.\n */\n onFrame?: (metrics: FrameMetrics) => void;\n /**\n * Spawns the application-logic worker: api, persistence, domain and\n * view models, published as channels.\n *\n * The shell creates it, hands the render worker a port to it, and\n * then has nothing more to do with it — no patch ever crosses this\n * thread. Owning the spawn is bootstrap wiring, not a running\n * responsibility, and it buys two things: the application survives\n * the render worker being replaced, and nothing depends on a worker\n * being able to spawn a worker.\n *\n * A factory rather than a URL, for the same reason `renderWorker` is\n * one.\n *\n * Pass an already-running `Worker` to keep it across a remount. The\n * app is disposed and rebuilt whenever the *rendering* changes — the\n * playground's Canvas2D/WebGPU switch does exactly that — and an\n * application worker spawned here would go with it, discarding\n * application state for a reason that had nothing to do with the\n * application. What this class spawned, it terminates; what it was\n * handed, it leaves alone.\n */\n appLogicWorker?: Worker | AppLogicEndpoint | (() => Worker) | URL | string;\n /**\n * Opens a url the application asked for, in place of a new tab.\n *\n * A page wants `window.open`, which is the default. A desktop window\n * does not: `window.open` in a webview opens another webview or\n * nothing at all, and a link in a desktop application belongs in the\n * person's browser, which only the process outside the window can\n * reach. `gesso-electrobun`'s bridge is what goes here.\n */\n onOpenUrl?: (url: string) => void;\n /**\n * Receives errors thrown inside the render worker: while handling a\n * message, uncaught during a frame, from the renderer, or from a\n * channel — `source` says which, and `RuntimeErrorSource` says what\n * each one costs the running application.\n *\n * Defaults to `console.error`, which is a developer reading the\n * right thread in devtools at the right moment. `gesso-devtools`'s\n * error overlay is the same callback, drawn where the app is.\n */\n onError?: (message: string, stack: string | undefined, source: RuntimeErrorSource) => void;\n /**\n * Receives a report on the hovered node while the inspector is on\n * (see `setInspector`), and null when nothing is hovered.\n *\n * The report is built in the worker, where the tree is, and crosses\n * as plain data; `report.explanation` is the layout explanation this\n * used to carry on its own.\n */\n onInspect?: (report: UiNodeReport | null) => void;\n /**\n * Cancel the browser's Ctrl/Cmd+F on the canvas, so the app's own\n * find bar takes it. Off by default: the browser's find bar cannot\n * see a canvas, but taking the shortcut from an app that has no find\n * of its own would leave the user with neither.\n *\n * A flag rather than something the runtime decides, because the\n * worker's answer cannot come back in time to cancel a default —\n * unlike `GessoApp`, where the platform adapter cancels whatever the\n * app's own KeyDown listener claimed.\n */\n interceptFind?: boolean;\n /**\n * The appearance the application is told about: `auto` (the default)\n * follows `prefers-color-scheme`, `light` and `dark` override it.\n *\n * An option as well as a setter because a host with its own control\n * — a documentation site whose reader has already chosen dark —\n * otherwise starts on the platform's answer and corrects it a frame\n * later, which is a visible flash of the wrong appearance.\n */\n colorScheme?: ColorSchemePreference;\n /**\n * Set false to drop the off-screen DOM an assistive technology reads\n * (`SemanticsMirror`).\n *\n * On by default: an application that is accessible only when its\n * author remembered a flag is an application that is not accessible.\n * Turning it off also stops the render worker computing the geometry\n * the mirror needs, which is what makes the opt-out worth having for\n * a measurement.\n */\n accessibility?: boolean;\n /**\n * How the app's url is kept, for an app with routes: `path`\n * (pushState, the default), `hash`, or `memory`. See `shellHistory`.\n *\n * The shell's half of routing is this and nothing else. It holds no\n * routes, resolves nothing, and could not — a route names a\n * component class, which never leaves the worker. It reports the url\n * the window is at and performs the pushes the worker asks for.\n */\n history?: ShellHistoryOptions;\n}\n\n/**\n * Main-thread half of a worker-hosted Gesso application.\n *\n * Owns nothing but the canvas element and the event plumbing. It\n * creates the canvas, hands its drawing surface to the worker as an\n * OffscreenCanvas, and forwards pointer, wheel, keyboard and resize\n * into the worker. No component, node, layout record or render call\n * exists on this thread, so main-thread work cannot delay a frame.\n */\n/**\n * Which application layer the shell was given, and whether it is the\n * shell's to close.\n *\n * A factory, a URL or a path names a worker this class creates, and\n * what it creates it terminates. Anything else was handed over\n * already running: a `Worker` kept across a remount, or an endpoint\n * that is not a worker at all, which is how a desktop window reaches\n * an application layer living in another process\n * (`gesso-electrobun`). Neither is closed here, because a handle\n * that could kill something it did not start is the wrong handle.\n *\n * Exported for its spec: the ownership half is the part that goes\n * quietly wrong, by discarding an application the shell had merely\n * borrowed.\n */\n/**\n * Opens a url through the host's handler, or in a new tab.\n *\n * Exported for its spec: the default carries `noopener,noreferrer`,\n * which is the difference between opening a link and handing the\n * opener to whatever is on the other end of it.\n */\nexport function openUrlWith(handler: ((url: string) => void) | undefined, url: string): void {\n if (handler !== undefined) {\n handler(url);\n return;\n }\n window.open(url, '_blank', 'noopener,noreferrer');\n}\n\nexport function resolveAppLogic(spec: NonNullable<WorkerAppOptions['appLogicWorker']>): {\n endpoint: AppLogicEndpoint;\n owned: boolean;\n} {\n if (typeof spec === 'function') {\n return { endpoint: spec(), owned: true };\n }\n if (typeof spec === 'string' || spec instanceof URL) {\n return { endpoint: new Worker(spec, { type: 'module' }), owned: true };\n }\n return { endpoint: spec, owned: false };\n}\n\n/**\n * The one message this shell ever holds on to rather than posting the\n * moment it has it. See `flushPendingMove`.\n */\ntype PointerMoveMessage = Extract<ShellToRuntimeMessage, { type: 'pointerMove' }>;\n\nexport class WorkerApp {\n private readonly options: WorkerAppOptions;\n\n private renderWorker: Worker | undefined;\n private appLogicWorker: AppLogicEndpoint | undefined;\n private devtoolsListener: ((event: DevtoolsEvent) => void) | null = null;\n /** Whether a panel has asked for the workers' consoles, remembered across a remount. */\n private consoleForwarding = false;\n /** True only when this class spawned the application-logic worker. */\n private ownsAppLogicWorker = false;\n private canvas: HTMLCanvasElement | undefined;\n private host: HTMLElement | undefined;\n private resizeObserver: ResizeObserver | null = null;\n private detachInput: (() => void) | null = null;\n private proxy: EditingProxy | null = null;\n private mirror: SemanticsMirror | null = null;\n /** The one audio element, behind `AudioService`; see `AudioSink`. */\n private audio: AudioSink | null = null;\n /**\n * Which way the runtime could scroll under the pointer, as of the\n * last frame the worker reported.\n *\n * Starts out all false, which is the safe unknown: before the\n * worker has said anything the shell lets wheels through to the\n * page rather than swallowing them, so a canvas that fails to start\n * degrades to an inert picture instead of a hole that eats\n * scrolling.\n */\n private scrollability: UiScrollability = { up: false, down: false, left: false, right: false };\n private history: ShellHistory | null = null;\n /** True once the render worker has answered `ready` at least once. */\n private ready = false;\n /** The running `requestAnimationFrame` handle, when ticks are wanted. */\n private frameHandle: number | null = null;\n /** Stops watching `prefers-color-scheme`; null while overridden. */\n private detachColorScheme: (() => void) | null = null;\n /** Stops watching `visualViewport` for the safe area and the keyboard. */\n private detachViewportInsets: (() => void) | null = null;\n /** The appearance this shell reports, remembered across a remount. */\n private colorSchemePreference: ColorSchemePreference = 'auto';\n /**\n * Whether the render worker still owes an answer to a `resize`, and\n * the newest size that has gone unsent because it does.\n *\n * A resize is the one shell message whose handling costs the worker\n * a full layout and a paint, and `ResizeObserver` delivers one per\n * refresh while a window edge is dragged. A worker slower than the\n * display therefore accumulated a queue of sizes, every one of them\n * already wrong by the time it was laid out, and the lag grew for\n * the length of the drag instead of settling. So the shell keeps one\n * resize in flight and remembers only the latest size it has not\n * sent. Nothing anyone can see is dropped: the size held back is the\n * newest one, and it goes out as soon as the worker says it has\n * drained the last.\n *\n * The initial size travels in `init` rather than as a resize, so\n * nothing is in flight until the first notification arrives.\n */\n private resizeInFlight = false;\n private heldResize: { width: number; height: number; dpr: number } | null = null;\n /**\n * Where the canvas sits on the page, or null when that has to be\n * read from the DOM again. `attachInput` says why it is cached and\n * what the cache costs.\n */\n private canvasOrigin: { left: number; top: number } | null = null;\n /** Whether the canvas is filling the screen; see the resize observer. */\n private fullscreen = false;\n /**\n * The hover move being held for this frame, and the frame holding\n * it. See `flushPendingMove`.\n */\n private pendingMove: PointerMoveMessage | null = null;\n private moveFrame: number | null = null;\n\n constructor(options: WorkerAppOptions) {\n this.options = options;\n this.colorSchemePreference = options.colorScheme ?? 'auto';\n }\n\n /**\n * Creates the canvas, starts the worker, and wires event forwarding.\n *\n * Returns a dispose function.\n */\n /**\n * A handle on the application-logic worker, for something on this\n * thread that wants its channels too: `createChannelRegistry` takes\n * it as a registration's `worker`. Undefined before `mount`, and\n * when no application-logic worker was given. The shell itself never\n * uses this; its own view of the application is nothing at all.\n */\n get appLogic(): WorkerHandle | undefined {\n return this.appLogicWorker === undefined ? undefined : portHandle(this.appLogicWorker);\n }\n\n mount(host: HTMLElement | string): () => void {\n const element = resolveHost(host);\n this.host = element;\n\n const canvas = document.createElement('canvas');\n canvas.style.display = 'block';\n canvas.style.width = '100%';\n canvas.style.height = '100%';\n prepareInputSurface(canvas);\n canvas.tabIndex = 0;\n element.appendChild(canvas);\n this.canvas = canvas;\n\n if (typeof canvas.transferControlToOffscreen !== 'function') {\n throw new Error(\n 'OffscreenCanvas is unavailable, so the render worker cannot draw. ' +\n 'Use createApp(Root).mountSync(host) for the single-thread configuration.'\n );\n }\n\n const offscreen = canvas.transferControlToOffscreen();\n const spec = this.options.renderWorker;\n const worker = typeof spec === 'function' ? spec() : new Worker(spec, { type: 'module' });\n this.renderWorker = worker;\n worker.addEventListener('message', this.handleWorkerMessage);\n worker.addEventListener('error', this.handleWorkerFailure);\n\n // The canvas's own box, not the host's. `clientWidth`/`clientHeight`\n // include the host's padding, while the canvas is sized to its\n // content box — so a padded host (the playground's preview pane has\n // 16px) started the runtime with a viewport wider and taller than\n // the surface it draws on. Everything came out scaled, and pointer\n // coordinates, which `attachInput` takes from this same rect,\n // landed off by the same ratio. The ResizeObserver below reports\n // `contentRect` and so already agreed with this measurement; the\n // first frame was the only one that did not.\n const { width, height } = measure(canvas, element);\n const transfer: Transferable[] = [offscreen];\n let appPort: MessagePort | undefined;\n if (this.options.appLogicWorker !== undefined) {\n const resolved = resolveAppLogic(this.options.appLogicWorker);\n const application = resolved.endpoint;\n this.appLogicWorker = application;\n this.ownsAppLogicWorker = resolved.owned;\n application.addEventListener('message', this.handleAppWorkerMessage);\n // A port delivers nothing until it is started, and a worker has\n // no such method. Calling it here rather than asking the caller\n // to is what makes a port a drop-in for a worker.\n application.start?.();\n if (this.consoleForwarding) {\n // A panel asked before the worker existed (a remount), and the\n // new worker has not been told.\n application.postMessage({ type: 'gesso:console', enabled: true } as ConsoleForwardingMessage);\n }\n // One channel between the two workers. The shell holds neither\n // end afterwards, so it cannot be in the way of a patch even by\n // accident.\n const hub = new MessageChannel();\n application.postMessage({ type: 'gesso:hub' }, [hub.port2]);\n appPort = hub.port1;\n transfer.push(hub.port1);\n }\n worker.postMessage(\n {\n type: 'init',\n canvas: offscreen,\n width,\n height,\n dpr: window.devicePixelRatio || 1,\n renderer: this.options.renderer,\n // Text comes through the editing proxy below, IME and all.\n textInput: 'proxy',\n accessibility: this.options.accessibility !== false,\n appPort\n } as ShellToRuntimeMessage,\n transfer\n );\n\n this.observeResize(element);\n this.attachHistory();\n this.detachInput = this.attachInput(canvas);\n // Sound. The element has to live here, and only here; the worker\n // tells it what to do and hears what it did.\n this.audio = new AudioSink({\n sample: sample => this.post({ type: 'audioSample', sample }),\n action: action => this.post({ type: 'audioAction', action })\n });\n this.setColorScheme(this.colorSchemePreference);\n // The safe area and the soft keyboard, as four numbers. Read here\n // because `visualViewport` is the window's; what to do about them\n // is layout, and layout is in the worker. Reported once immediately\n // as well as on change, so a phone whose notch has been there all\n // along starts with the right numbers.\n this.detachViewportInsets = observeViewportInsets(insets => this.post({ type: 'viewportInsets', insets }));\n // The hidden textarea that turns keystrokes into text for the\n // worker. It has DOM focus while the worker reports a focused\n // editable, so its key events are forwarded like the canvas's.\n this.proxy = new EditingProxy(canvas, {\n beforeInput: (inputType, data) => this.post({ type: 'beforeInput', inputType, data }),\n compositionStart: () => this.post({ type: 'compositionStart' }),\n compositionUpdate: (text, caret) => this.post({ type: 'compositionUpdate', text, caret }),\n compositionEnd: text => this.post({ type: 'compositionEnd', text }),\n paste: text => this.post({ type: 'paste', text }),\n blur: () => this.post({ type: 'blur' }),\n keyDown: event => this.forwardKeyDown(event),\n keyUp: event => this.forwardKeyUp(event)\n });\n if (this.options.accessibility !== false) {\n // The off-screen DOM an assistive technology reads. Keys are\n // forwarded from it for the same reason they are forwarded from\n // the proxy: while the app has focus, the element holding it is\n // one of these and not the canvas.\n this.mirror = new SemanticsMirror(\n canvas,\n {\n action: action => this.post({ type: 'semanticsAction', action }),\n keyDown: event => this.forwardKeyDown(event),\n keyUp: event => this.forwardKeyUp(event)\n },\n this.proxy\n );\n }\n\n return () => this.dispose();\n }\n\n /**\n * An error the browser raised *at the worker object*, which is not\n * the same thing as the worker reporting one.\n *\n * The worker reports its own exceptions over the protocol, with a\n * stack and a source; this event carries neither. Left uncancelled,\n * the browser reports it a second time at this window — the same\n * failure with less information, which is what the error overlay\n * showed as a duplicate — so it is cancelled here.\n *\n * It is passed on in exactly one case: the worker never got as far\n * as saying `ready`, so its own handlers were never installed and\n * nothing else will ever report this. A module that fails to load,\n * or fails to parse, arrives this way and no other.\n */\n private handleWorkerFailure = (event: ErrorEvent): void => {\n event.preventDefault();\n if (this.ready) {\n return;\n }\n const where = event.filename === undefined || event.filename === '' ? '' : ` (${event.filename})`;\n this.report(`the render worker failed to start: ${event.message}${where}`, undefined, 'uncaught');\n };\n\n /** Reports an error, to `onError` or to the console it defaults to. */\n private report(message: string, stack: string | undefined, source: RuntimeErrorSource): void {\n // A panel hears every error the shell does, whatever the shell\n // does with it; the overlay and the panel are two readers, not\n // two sources.\n this.devtoolsListener?.({ kind: 'error', message, ...(stack === undefined ? {} : { stack }), source });\n const report =\n this.options.onError ?? ((text, trace, from) => console.error(`[gesso render worker: ${from}] ${text}`, trace));\n report(message, stack, source);\n }\n\n private forwardKeyDown(event: KeyboardEvent): void {\n // Whatever hover the shell is holding happened before this key\n // and has to be posted before it; see `flushPendingMove`. Keys\n // reach here from the canvas, the editing proxy and the semantics\n // mirror, so the flush belongs here rather than in one listener.\n this.flushPendingMove();\n if (this.options.interceptFind === true && isFind(event)) {\n event.preventDefault();\n }\n if (event.key === 'Tab' || isSelectAll(event)) {\n // The worker's answer cannot come back in time to cancel a\n // default, so the shell cancels the two that would be wrong\n // whatever it is: Tab moving focus out of the canvas, and\n // Ctrl/Cmd+A selecting the page around it while the app selects\n // its own text.\n event.preventDefault();\n }\n this.post({ type: 'keyDown', key: event.key, modifiers: modifiersFrom(event), at: epochFromEvent(event) });\n }\n\n private forwardKeyUp(event: KeyboardEvent): void {\n this.flushPendingMove();\n this.post({ type: 'keyUp', key: event.key, modifiers: modifiersFrom(event), at: epochFromEvent(event) });\n }\n\n /**\n * Turns the layout inspector on or off in the worker: hover boxes and\n * a measure heatmap over the scene, and explanations via `onInspect`.\n */\n setInspector(enabled: boolean): void {\n this.post({ type: 'inspector', enabled });\n }\n\n /**\n * Receives what a devtools panel asked for through `devtools`, and\n * updates to whatever it is watching. A method rather than an option\n * because a panel attaches to an application that is already\n * running, and detaches from one that keeps running.\n */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void {\n this.devtoolsListener = listener;\n }\n\n /**\n * Passes a devtools panel's request to the render worker, and a\n * console request to the application worker as well: each worker\n * forwards its own console, and the shell names the thread.\n */\n devtools(request: DevtoolsRequest): void {\n this.post({ type: 'devtools', request });\n if (request.kind === 'console') {\n this.consoleForwarding = request.enabled;\n this.appLogicWorker?.postMessage({ type: 'gesso:console', enabled: request.enabled } as ConsoleForwardingMessage);\n }\n }\n\n /**\n * Chooses what the application is told about the appearance.\n *\n * `auto` watches `prefers-color-scheme` and reports what it says;\n * `light` and `dark` stop watching and report themselves, for a host\n * with its own control — the reader of a documentation site who has\n * picked dark against a light system, or a desktop window with an\n * appearance setting of its own.\n *\n * Safe to call before `mount`: the preference is remembered and sent\n * when the worker starts.\n */\n setColorScheme(preference: ColorSchemePreference): void {\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.colorSchemePreference = preference;\n if (this.renderWorker === undefined) {\n return;\n }\n if (preference === 'auto') {\n // Reports once immediately as well as on change, for the reason\n // reduced motion does: nobody fires a `change` event at an app\n // that started in the appearance it is already in.\n this.detachColorScheme = observeColorScheme(scheme => this.post({ type: 'colorScheme', scheme }));\n return;\n }\n this.post({ type: 'colorScheme', scheme: preference });\n }\n\n /**\n * Runs the display's refresh loop on the runtime's behalf.\n *\n * The one piece of per-frame work the shell genuinely has to do:\n * `requestAnimationFrame` is tied to the compositor and does not\n * exist in a worker, so without this the render worker can only\n * guess at a cadence with a timer — a fixed sixty on a 165Hz\n * display, aligned to none of its refreshes.\n *\n * The loop is free-running while the worker wants frames rather than\n * armed per frame, because a request-per-frame costs a round trip\n * inside every frame and halves the rate whenever the request misses\n * that vsync's callback. Each tick is a bare timestamp, and the loop\n * stops the moment the worker says it is idle — so an app doing\n * nothing costs nothing here.\n */\n private setFrameLoop(running: boolean): void {\n if (!running) {\n if (this.frameHandle !== null) {\n cancelAnimationFrame(this.frameHandle);\n this.frameHandle = null;\n }\n return;\n }\n if (this.frameHandle !== null) {\n return;\n }\n const step = (time: number): void => {\n // Re-armed before posting, so a worker that keeps wanting frames\n // never waits a refresh for the shell to come back round.\n this.frameHandle = requestAnimationFrame(step);\n this.post({ type: 'tick', time });\n };\n this.frameHandle = requestAnimationFrame(step);\n }\n\n dispose(): void {\n this.ready = false;\n this.setFrameLoop(false);\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.detachViewportInsets?.();\n this.detachViewportInsets = null;\n this.proxy?.dispose();\n this.proxy = null;\n this.mirror?.dispose();\n this.mirror = null;\n this.history?.dispose();\n this.history = null;\n this.detachInput?.();\n this.detachInput = null;\n this.audio?.dispose();\n this.audio = null;\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n // A remount starts from nothing in flight. The new worker is told\n // its size in `init`, and an acknowledgement from the old one\n // would be about a canvas that no longer exists.\n this.resizeInFlight = false;\n this.heldResize = null;\n this.canvasOrigin = null;\n this.dropPendingMove();\n if (this.renderWorker !== undefined) {\n this.renderWorker.postMessage({ type: 'dispose' } as ShellToRuntimeMessage);\n this.renderWorker.removeEventListener('message', this.handleWorkerMessage);\n this.renderWorker.removeEventListener('error', this.handleWorkerFailure);\n this.renderWorker.terminate();\n this.renderWorker = undefined;\n }\n this.appLogicWorker?.removeEventListener('message', this.handleAppWorkerMessage);\n if (this.ownsAppLogicWorker) {\n (this.appLogicWorker as Worker | undefined)?.terminate();\n }\n this.appLogicWorker = undefined;\n this.ownsAppLogicWorker = false;\n if (this.canvas !== undefined && this.canvas.parentElement === this.host) {\n this.host?.removeChild(this.canvas);\n }\n this.canvas = undefined;\n this.host = undefined;\n }\n\n /**\n * The application worker talks to the render worker over the hub,\n * never to the shell; the one thing it says to the shell is a\n * forwarded console entry, which is the one thing the render worker\n * cannot say for it.\n */\n private readonly handleAppWorkerMessage = (event: MessageEvent<unknown>): void => {\n if (isConsoleEntryMessage(event.data)) {\n this.devtoolsListener?.({ kind: 'console', entry: { ...event.data.entry, thread: 'app' } });\n }\n };\n\n private readonly handleWorkerMessage = (event: MessageEvent<RuntimeToShellMessage>): void => {\n const message = event.data;\n if (message.type === 'frame') {\n this.options.onFrame?.({\n frame: message.frame,\n durationMs: message.durationMs,\n nodes: message.nodes,\n measured: message.measured,\n relayoutRoots: message.relayoutRoots,\n at: message.at,\n inputLatencyMs: message.inputLatencyMs,\n phases: message.phases,\n renderer: message.renderer,\n gpu: message.gpu\n });\n return;\n }\n if (message.type === 'error') {\n this.report(message.message, message.stack, message.source);\n return;\n }\n if (message.type === 'ready') {\n this.ready = true;\n return;\n }\n if (message.type === 'frameLoop') {\n this.setFrameLoop(message.running);\n return;\n }\n if (message.type === 'inspect') {\n this.options.onInspect?.(message.report);\n return;\n }\n if (message.type === 'devtools') {\n this.devtoolsListener?.(message.event);\n return;\n }\n if (message.type === 'cursor') {\n // The worker decided what the pointer is over; only the DOM can\n // show it. An empty string restores the stylesheet's cursor.\n if (this.canvas !== undefined) {\n this.canvas.style.cursor = message.cursor ?? '';\n }\n return;\n }\n if (message.type === 'scrollability') {\n // Cached, not acted on: the wheel handler reads it synchronously\n // when an event arrives, which is the whole reason the worker\n // pushes it ahead of time.\n this.scrollability = message.scrollability;\n if (this.canvas !== undefined) {\n this.canvas.style.touchAction = touchActionFor(message.scrollsAnything);\n }\n return;\n }\n if (message.type === 'resized') {\n this.handleResized(message);\n return;\n }\n if (message.type === 'editing') {\n this.proxy?.update(message.state);\n return;\n }\n if (message.type === 'clipboard') {\n writeClipboard(message.text);\n return;\n }\n if (message.type === 'openUrl') {\n openUrlWith(this.options.onOpenUrl, message.url);\n return;\n }\n if (message.type === 'fullscreen') {\n // The canvas rather than the document body: what the application\n // draws on is what should fill the screen.\n if (this.canvas !== undefined) {\n setElementFullscreen(this.canvas, message.enter);\n }\n return;\n }\n if (message.type === 'popup') {\n this.openPopup(message);\n return;\n }\n if (message.type === 'storage') {\n // The one call in this file that reaches a browser API the\n // render worker cannot: `localStorage` is on the window. What\n // comes back is plain data, and the shell judges none of it.\n this.post({\n type: 'storageResult',\n id: message.id,\n result: performShellStorage(message, () => globalThis.localStorage)\n });\n return;\n }\n if (message.type === 'audio') {\n this.audio?.handle(message.request);\n return;\n }\n if (message.type === 'semantics') {\n this.mirror?.apply(message.update);\n return;\n }\n if (message.type === 'history') {\n this.applyHistory(message.action, message.url);\n }\n };\n\n /**\n * Reports the window's address to the worker, and keeps reporting it.\n *\n * Sent once at start-up as well as on change, for the same reason\n * `reducedMotion` is: an app opened directly at a url must start on\n * the screen that url names, not on its root.\n */\n private attachHistory(): void {\n const history = createShellHistory(this.options.history);\n this.history = history;\n history.onChange(url => this.post({ type: 'url', url }));\n this.post({ type: 'url', url: history.url });\n }\n\n /**\n * Opens a popup for the render worker and tells it what happened.\n *\n * `noopener` is deliberately absent, though the sibling `openUrl`\n * above sets it. `window.open` answers `null` whenever `noopener` is\n * given, whether the window appeared or was refused, so a popup\n * opened that way could not be reported on, and reporting is the\n * whole reason this request exists rather than another `openUrl`.\n * The page opened is a different origin, so the opener reference it\n * gains is the ordinary one every OAuth popup has.\n *\n * A reply is posted on every path, including the throwing one, so the\n * promise on the other side always settles.\n */\n private openPopup(request: { id: number; url: string; name: string; width: number; height: number }): void {\n let opened = false;\n try {\n const features = `popup,width=${request.width},height=${request.height}`;\n opened = window.open(request.url, request.name, features) !== null;\n } catch {\n // A sandboxed frame throws rather than returning null.\n opened = false;\n }\n this.post({ type: 'popupResult', id: request.id, opened });\n }\n\n private applyHistory(action: 'push' | 'replace' | 'back' | 'forward', url?: string): void {\n const history = this.history;\n if (history === null) {\n return;\n }\n if (action === 'back') {\n history.back();\n } else if (action === 'forward') {\n history.forward();\n } else if (url !== undefined) {\n if (action === 'push') {\n history.push(url);\n } else {\n history.replace(url);\n }\n }\n }\n\n private post(message: ShellToRuntimeMessage): void {\n // A backstop only. An input forwarded from a DOM event carries the\n // event's own timestamp (see `epochFromEvent`); this covers the\n // few that have no event behind them, and costs a listener that\n // forgot to stamp a reading that is late by however long the shell\n // took to get here.\n if (isInputMessage(message) && message.at === undefined) {\n (message as { at?: number }).at = epochNow();\n }\n this.renderWorker?.postMessage(message);\n }\n\n private observeResize(element: HTMLElement): void {\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n this.resizeObserver = new ResizeObserver(entries => {\n const entry = entries[0];\n if (entry === undefined) {\n return;\n }\n // The host has changed shape, so the canvas inside it has very\n // likely moved on the page as well as grown; whatever\n // `attachInput` cached about where it is cannot be trusted.\n this.canvasOrigin = null;\n if (this.fullscreen) {\n // The canvas has been lifted out of the host and is filling\n // the screen, so the host's shape says nothing about how big\n // the surface should be. It reflows *because* the canvas\n // left, and taking that size would shrink the surface behind\n // a screen-sized canvas and have the browser stretch it back\n // up, which is what put every coordinate out.\n return;\n }\n const { width, height } = entry.contentRect;\n if (width > 0 && height > 0) {\n this.requestResize({ width, height, dpr: window.devicePixelRatio || 1 });\n }\n });\n this.resizeObserver.observe(element);\n }\n\n /**\n * Sends a size to the worker, or holds it back until the worker has\n * caught up with the last one.\n *\n * At most one resize is in flight, for the reason `resizeInFlight`\n * gives: the alternative is a queue of sizes that are all wrong,\n * each bought with a layout and a paint. Overwriting the held size\n * rather than queueing it is the whole trick — a drag of any length\n * costs the worker one layout per acknowledgement rather than one\n * per refresh, and the size that eventually arrives is the size the\n * window ended at.\n */\n private requestResize(size: { width: number; height: number; dpr: number }): void {\n if (this.resizeInFlight) {\n this.heldResize = size;\n return;\n }\n this.resizeInFlight = true;\n this.post({ type: 'resize', ...size });\n }\n\n /**\n * The worker has applied a resize, so the one being held can go.\n *\n * The dimensions are compared against what is held because the\n * drag usually ends on the size that was already in flight: the\n * worker has it, and posting it again would buy a layout that\n * changes nothing. A held size that differs is the last one the\n * observer reported, and it becomes the resize in flight.\n *\n * There is no timer behind this. The worker acknowledges every\n * `resize` it is sent, including one it decides to drop, so an\n * acknowledgement is owed for as long as the worker lives — and a\n * worker that has stopped answering has stopped painting too, which\n * is not a wrong canvas size but a dead renderer, and is reported\n * as one. A genuinely lost acknowledgement would leave the canvas at\n * the last size the worker applied until the shell is remounted;\n * that is a trade this takes knowingly, in exchange for not having\n * a heuristic timeout re-sending sizes at a worker that is merely\n * slow.\n */\n private handleResized(applied: { width: number; height: number; dpr: number }): void {\n this.resizeInFlight = false;\n const held = this.heldResize;\n this.heldResize = null;\n if (held === null) {\n return;\n }\n if (held.width === applied.width && held.height === applied.height && held.dpr === applied.dpr) {\n return;\n }\n this.resizeInFlight = true;\n this.post({ type: 'resize', ...held });\n }\n\n /**\n * Forwards DOM input into the worker.\n *\n * UiPlatformAdapter is deliberately not reused here. It decides\n * whether to call preventDefault() from the returned event's\n * flags, and that answer lives in the worker and cannot come back\n * synchronously. The shell instead prevents the defaults that\n * matter — focus stealing on Tab, and the page's own select-all —\n * and lets the worker route everything else.\n *\n * The wheel is the one place where \"prevent it and be done\" is\n * wrong in both directions, so it reads a cached answer the worker\n * pushed ahead of the event. See `wouldConsumeWheel`.\n */\n /**\n * Whether the runtime will take this wheel, decided from the cached\n * scrollability rather than by asking.\n *\n * `preventDefault()` has to be called synchronously, inside the DOM\n * handler, and the runtime is a `postMessage` away — so the honest\n * answer arrives a frame late or not at all. The shell therefore\n * answers from what the worker last reported about the pointer's\n * scroll chain, which is the same bet a browser makes when it\n * scrolls on the compositor thread.\n *\n * Both mistakes it can make are bounded and recoverable. One frame\n * after a container reaches its edge, one wheel notch may still be\n * swallowed; one frame after it leaves its edge, one may leak to\n * the page. Neither is the failure this replaced, which was every\n * wheel over the canvas dying whether or not there was anything to\n * scroll.\n *\n * See `wheelConsumedBy` for the rule itself.\n */\n private wouldConsumeWheel(event: WheelEvent): boolean {\n return wheelConsumedBy(this.scrollability, event.deltaX, event.deltaY);\n }\n\n /**\n * Reads where the canvas is and remembers it.\n *\n * Only left and top are kept. The size is the ResizeObserver's\n * business and is already reported as a resize; what an input needs\n * from this rect is the origin to subtract.\n */\n private readCanvasOrigin(canvas: HTMLCanvasElement): { left: number; top: number } {\n const box = canvas.getBoundingClientRect();\n const origin = { left: box.left, top: box.top };\n this.canvasOrigin = origin;\n return origin;\n }\n\n /**\n * Posts the hover move being held for this frame, if there is one.\n *\n * Every other input this shell sends calls this first, and the\n * ordering is the whole reason it is one function rather than a flag\n * each listener consults. A held move that went out *after* the\n * press, release or wheel that superseded it would leave the worker\n * hovering a position the pointer had already left, and the hover\n * state of a widget is exactly what decides how the next event is\n * drawn.\n *\n * Only hover moves are ever held; see `onPointerMove`.\n */\n private flushPendingMove(): void {\n const move = this.pendingMove;\n this.dropPendingMove();\n if (move !== null) {\n this.post(move);\n }\n }\n\n /**\n * Forgets a held hover move and the frame that was to send it,\n * posting nothing. For a detach or a dispose, where the surface the\n * move was measured against is going away.\n */\n private dropPendingMove(): void {\n if (this.moveFrame !== null) {\n cancelAnimationFrame(this.moveFrame);\n this.moveFrame = null;\n }\n this.pendingMove = null;\n }\n\n private attachInput(canvas: HTMLCanvasElement): () => void {\n /**\n * Where a pointer is, in the canvas's own coordinates.\n *\n * The origin is cached rather than measured per event.\n * `getBoundingClientRect` is a synchronous style and layout flush\n * whenever the document is dirty, and this ran one on every\n * pointermove, pointerdown, pointerup and wheel — on the one\n * thread this whole architecture exists to keep free. The cache is\n * dropped whenever something the shell can hear says the canvas\n * may have moved: the ResizeObserver fires, an ancestor scrolls,\n * or the window resizes.\n *\n * What that trades away is exactness under movement nothing\n * announces — a CSS transition on an ancestor, an element\n * animated by a library that touches no scroll position — where\n * coordinates come out shifted by however far the canvas went.\n * `pointerdown` therefore takes a fresh reading, so every gesture\n * starts from the truth and a single press re-syncs a stale\n * cache. A hover in the meantime can land in the wrong place; a\n * press, and the drag and click that follow it, cannot.\n */\n const toLocal = (clientX: number, clientY: number) => {\n const origin = this.canvasOrigin ?? this.readCanvasOrigin(canvas);\n return { x: clientX - origin.left, y: clientY - origin.top };\n };\n\n // Whether an editable already had focus when the press began. A\n // press that *starts* editing is the one that has to raise the\n // keyboard; one that lands on a field already being typed into must\n // not, since the keyboard is up and re-taking focus makes it blink.\n let editingAtPress = false;\n const onPointerDown = (event: PointerEvent): void => {\n // A press is the one event worth a layout flush: it starts a\n // gesture, it is rare next to a move, and it is the shell's only\n // chance to notice that something moved the canvas without\n // telling anyone.\n this.canvasOrigin = null;\n this.flushPendingMove();\n const { x, y } = toLocal(event.clientX, event.clientY);\n editingAtPress = this.proxy?.active ?? false;\n // While an editable has focus the proxy's textarea holds DOM\n // focus; the worker decides whether this press keeps it there.\n if (!(this.proxy?.active ?? false)) {\n canvas.focus();\n }\n // The canvas keeps this contact even once it leaves the element,\n // so a drag that runs off the edge is still delivered. A finger\n // is captured implicitly and a mouse is not; capturing both\n // makes the two behave the same.\n capturePointer(canvas, event.pointerId);\n this.post({\n type: 'pointerDown',\n x,\n y,\n buttons: event.buttons,\n modifiers: modifiersFrom(event),\n pointer: pointerDeviceOf(event),\n at: epochFromEvent(event)\n });\n };\n const onMouseDown = (event: MouseEvent): void => {\n if (this.proxy?.active ?? false) {\n // The default would move focus to the canvas before the worker\n // has said where the press landed.\n event.preventDefault();\n }\n };\n const onVisibilityChange = (): void => {\n this.post({ type: 'visibility', visible: document.visibilityState !== 'hidden' });\n };\n const detachFullscreen = observeFullscreen(canvas, active => {\n // Entering or leaving fullscreen moves the canvas and changes\n // its size without the host's `ResizeObserver` hearing anything;\n // see `surfaceBox`. Both halves matter: the cached origin makes\n // a hover land in the wrong place, and the stale size makes\n // every coordinate wrong by the ratio between the two.\n this.canvasOrigin = null;\n this.fullscreen = active;\n this.post({ type: 'fullscreenChanged', active });\n afterLayout(() => {\n this.canvasOrigin = null;\n const box = this.host === undefined ? null : surfaceBox(canvas, this.host, active);\n if (box !== null) {\n this.requestResize({ ...box, dpr: window.devicePixelRatio || 1 });\n }\n });\n });\n // Once here as well as on change, for the reason visibility is\n // sent once: a canvas mounted into a document that is already\n // fullscreen never fires the event.\n this.post({ type: 'fullscreenChanged', active: isDocumentFullscreen(canvas) });\n // Sent once here as well as on change, for the reason the reduced\n // motion listener below gives for itself: a tab that is *already*\n // hidden when it starts never fires `visibilitychange`, so without\n // this the runtime assumes it is on screen and lays out and paints\n // a canvas nobody can see. That is not hypothetical — a page opened\n // in a background tab, or behind another window, is exactly this.\n onVisibilityChange();\n // Sent once here as well as on change: someone who already has the\n // preference on must not watch the first screen animate.\n const detachReducedMotion = observeReducedMotion(reduced => {\n this.post({ type: 'reducedMotion', reduced });\n });\n const onPointerMove = (event: PointerEvent): void => {\n const { x, y } = toLocal(event.clientX, event.clientY);\n const move: PointerMoveMessage = {\n type: 'pointerMove',\n x,\n y,\n buttons: event.buttons,\n modifiers: modifiersFrom(event),\n pointer: pointerDeviceOf(event),\n at: epochFromEvent(event)\n };\n if (event.buttons !== 0) {\n // Something is pressed, so every point is forwarded. A drag, a\n // text selection, a scrollbar thumb and the touch scroller's\n // fling velocity are all computed from the stream itself, and\n // thinning it would quietly change what they do rather than\n // save work. Coalescing is only ever right for hover, where\n // the newest position is the whole of the information.\n this.flushPendingMove();\n this.post(move);\n return;\n }\n this.pendingMove = move;\n // One frame, one hover hit-test. A high-rate mouse reports\n // hundreds of moves a second and the worker answers each with a\n // hit-test whose result the next one discards, so the held move\n // is overwritten and a single frame sends the last of them. The\n // timestamp posted is that newest event's, not the frame's, so\n // the latency reading still measures from the input.\n this.moveFrame ??= requestAnimationFrame(() => {\n this.moveFrame = null;\n this.flushPendingMove();\n });\n };\n const onPointerUp = (event: PointerEvent): void => {\n this.flushPendingMove();\n const { x, y } = toLocal(event.clientX, event.clientY);\n this.post({\n type: 'pointerUp',\n x,\n y,\n buttons: event.buttons,\n modifiers: modifiersFrom(event),\n pointer: pointerDeviceOf(event),\n at: epochFromEvent(event)\n });\n // The worker's answer to the press has arrived by now — a round\n // trip is a frame and a tap is not — so the proxy already holds\n // DOM focus, taken from a message rather than from a gesture. A\n // phone ignores that one. This is the last gesture task of the\n // press, and the only chance to ask again.\n if (!editingAtPress && (this.proxy?.active ?? false)) {\n this.proxy?.raiseKeyboard();\n }\n };\n const onPointerCancel = (event: PointerEvent): void => {\n this.flushPendingMove();\n this.post({ type: 'pointerCancel', pointer: pointerDeviceOf(event) });\n };\n const onWheel = (event: WheelEvent): void => {\n this.flushPendingMove();\n if (this.wouldConsumeWheel(event)) {\n event.preventDefault();\n }\n const { x, y } = toLocal(event.clientX, event.clientY);\n this.post({\n type: 'wheel',\n x,\n y,\n deltaX: event.deltaX,\n deltaY: event.deltaY,\n // Forwarded, not converted: what a line is worth is a policy\n // question and policy lives in the runtime.\n deltaMode: event.deltaMode,\n wheelDeltaY: wheelDeltaYOf(event),\n modifiers: modifiersFrom(event),\n at: epochFromEvent(event)\n });\n };\n const onKeyDown = (event: KeyboardEvent): void => this.forwardKeyDown(event);\n const onKeyUp = (event: KeyboardEvent): void => this.forwardKeyUp(event);\n /**\n * The canvas has moved on the page without necessarily changing\n * size: an ancestor scrolled, or the window resized and the page\n * reflowed around it.\n *\n * The cached origin is dropped rather than re-read, so the cost is\n * paid by the next event that actually needs a position and not by\n * the scroll — and a scroll that nothing is pointing at costs\n * nothing at all.\n */\n const onCanvasMayHaveMoved = (): void => {\n this.canvasOrigin = null;\n };\n\n canvas.addEventListener('mousedown', onMouseDown);\n document.addEventListener('visibilitychange', onVisibilityChange);\n canvas.addEventListener('pointerdown', onPointerDown);\n canvas.addEventListener('pointermove', onPointerMove);\n canvas.addEventListener('pointerup', onPointerUp);\n canvas.addEventListener('pointercancel', onPointerCancel);\n canvas.addEventListener('wheel', onWheel, { passive: false });\n canvas.addEventListener('keydown', onKeyDown);\n canvas.addEventListener('keyup', onKeyUp);\n // Capturing, because a scroll only reaches the scrolled element\n // and its ancestors otherwise, and it is an *ancestor* of the\n // canvas scrolling that moves the canvas. Passive, because this\n // never prevents one and a non-passive scroll listener on the\n // window is exactly the thing that keeps a browser from scrolling\n // off the main thread.\n window.addEventListener('scroll', onCanvasMayHaveMoved, { capture: true, passive: true });\n window.addEventListener('resize', onCanvasMayHaveMoved);\n\n return () => {\n detachReducedMotion();\n detachFullscreen();\n canvas.removeEventListener('mousedown', onMouseDown);\n document.removeEventListener('visibilitychange', onVisibilityChange);\n canvas.removeEventListener('pointerdown', onPointerDown);\n canvas.removeEventListener('pointermove', onPointerMove);\n canvas.removeEventListener('pointerup', onPointerUp);\n canvas.removeEventListener('pointercancel', onPointerCancel);\n canvas.removeEventListener('wheel', onWheel);\n canvas.removeEventListener('keydown', onKeyDown);\n canvas.removeEventListener('keyup', onKeyUp);\n window.removeEventListener('scroll', onCanvasMayHaveMoved, { capture: true });\n window.removeEventListener('resize', onCanvasMayHaveMoved);\n // A frame still holding a hover move would post it against a\n // surface that is no longer listening, so it is dropped rather\n // than flushed.\n this.dropPendingMove();\n this.canvasOrigin = null;\n };\n }\n}\n\nfunction resolveHost(host: HTMLElement | string): HTMLElement {\n if (typeof host !== 'string') {\n return host;\n }\n const element = document.querySelector<HTMLElement>(host);\n if (element === null) {\n throw new Error(`Mount host '${host}' was not found.`);\n }\n return element;\n}\n\n/** Ctrl+A, or Cmd+A on a Mac: select every selectable text in the app. */\nfunction isSelectAll(event: KeyboardEvent): boolean {\n return (event.ctrlKey || event.metaKey) && !event.altKey && (event.key === 'a' || event.key === 'A');\n}\n\n/** Ctrl+F, or Cmd+F on a Mac: open the app's find bar. */\nfunction isFind(event: KeyboardEvent): boolean {\n return (event.ctrlKey || event.metaKey) && !event.altKey && (event.key === 'f' || event.key === 'F');\n}\n\n/**\n * Whether the runtime will take a wheel with these deltas, given what\n * it last said about the pointer's scroll chain.\n *\n * The shell has to answer this synchronously, inside the DOM handler,\n * and the runtime is a `postMessage` away — so it answers from the\n * last frame's report. That is the same bet a browser makes when it\n * scrolls on the compositor thread, and both mistakes it allows are\n * one frame long: a notch swallowed just after a container reached\n * its edge, or one leaked to the page just after it left it. Neither\n * is the failure this replaced, which was every wheel over the canvas\n * dying whether or not there was anything to scroll.\n *\n * The dominant axis decides, matching the runtime's own rule that a\n * container scrolls on one axis at a time. A wheel with no delta in\n * that axis is never consumed.\n */\nexport function wheelConsumedBy(scrollability: UiScrollability, deltaX: number, deltaY: number): boolean {\n if (Math.abs(deltaY) >= Math.abs(deltaX)) {\n if (deltaY > 0) {\n return scrollability.down;\n }\n return deltaY < 0 ? scrollability.up : false;\n }\n if (deltaX > 0) {\n return scrollability.right;\n }\n return deltaX < 0 ? scrollability.left : false;\n}\n\n/**\n * The logical size to start a runtime at, in CSS pixels.\n *\n * The canvas's border box is what the runtime draws into and what\n * pointer coordinates are measured against, so it is the measurement\n * that matters. It is zero before the first layout — a host mounted\n * while detached, or a test double — so the host's content box is the\n * fallback, and a fixed default the last resort.\n */\nexport function measure(canvas: HTMLCanvasElement, host: HTMLElement): { width: number; height: number } {\n const box = canvas.getBoundingClientRect();\n if (box.width > 0 && box.height > 0) {\n return { width: box.width, height: box.height };\n }\n const style = typeof getComputedStyle === 'function' ? getComputedStyle(host) : undefined;\n const pad = (value: string | undefined): number => parseFloat(value ?? '0') || 0;\n const width = host.clientWidth - pad(style?.paddingLeft) - pad(style?.paddingRight);\n const height = host.clientHeight - pad(style?.paddingTop) - pad(style?.paddingBottom);\n return { width: width > 0 ? width : 600, height: height > 0 ? height : 600 };\n}\n","import type { FrameworkChild } from '../ComponentElement';\nimport {\n CanvasPlatformSurface,\n observeViewportInsets,\n prepareInputSurface,\n UiPlatformAdapter,\n type UiNode,\n type CanvasHost,\n UiAnimationFrameClock,\n type UiFrameClockFactory\n} from 'gesso-core';\nimport { GessoRuntime, type FrameMetrics, type PatchSource, type RendererChoice } from './GessoRuntime';\nimport type { UiNodeReport } from './NodeReport';\nimport type { DevtoolsEvent, DevtoolsRequest } from './DevtoolsProtocol';\nimport type { ShellRequest } from './ShellService';\nimport { AudioSink } from './AudioSink';\nimport { EditingProxy, writeClipboard } from './EditingProxy';\nimport { SemanticsMirror } from './SemanticsMirror';\nimport { performShellStorage, shellStorageDenied } from './shellStorage';\nimport { observeColorScheme, type ColorSchemePreference } from './colorScheme';\nimport { observeReducedMotion } from './reducedMotion';\nimport { afterLayout, isDocumentFullscreen, observeFullscreen, setElementFullscreen, surfaceBox } from './fullscreen';\nimport { createShellHistory, type ShellHistory, type ShellHistoryOptions } from './shellHistory';\nimport { measure } from './worker/WorkerApp';\nimport type { ChannelRegistry } from '../channel/ChannelRegistry';\nimport type { ServiceRegistry } from '../service/ServiceRegistry';\nimport type { RouterRoutes } from '../router/RouterService';\nimport type { MediaOptions } from './MediaService';\nimport type { FontFamilyDeclaration } from './FontService';\n\nexport interface GessoAppOptions {\n host: HTMLElement;\n root: FrameworkChild;\n /** A registry built elsewhere, when some stores live in data workers. */\n services?: ServiceRegistry;\n channels?: ChannelRegistry;\n /** The routes a `RouterOutlet` in the tree resolves against. */\n routes?: RouterRoutes;\n /**\n * How the app's url is kept: `path` (pushState, the default in a\n * browser), `hash`, or `memory`. See `shellHistory`.\n */\n history?: ShellHistoryOptions;\n canvas?: CanvasHost;\n /** The rendering backend; see RendererChoice. Defaults to `auto`. */\n renderer?: RendererChoice;\n clock?: UiFrameClockFactory;\n /**\n * Set false to build the tree without attaching DOM input.\n * Handlers declared with `on*` props are still registered, so they\n * can be driven directly through `app.input`.\n */\n input?: boolean;\n /**\n * Set false to drop the off-screen DOM an assistive technology reads\n * (`SemanticsMirror`).\n *\n * On by default, because an application that is accessible only when\n * its author remembered a flag is an application that is not\n * accessible. The opt-out is for a host that mirrors the tree itself,\n * and for measuring what the mirror costs.\n */\n accessibility?: boolean;\n /**\n * The appearance the application is told about: `auto` (the default)\n * follows `prefers-color-scheme`, `light` and `dark` override it.\n * See `WorkerApp` for why this is an option and not only a setter.\n */\n colorScheme?: ColorSchemePreference;\n /**\n * Where the app's pictures come from: the image resolver, the icon\n * rasteriser and the video decoder.\n *\n * An option rather than a call on `MediaService` because the tree is\n * built inside the runtime's constructor and an `Image` in it asks\n * for its bitmap at that moment, so a resolver installed once there\n * is an app to install it on has already missed the first screen.\n */\n media?: MediaOptions;\n /** The font families the app's text may name; see `FontService`. */\n fonts?: readonly FontFamilyDeclaration[];\n}\n\n/**\n * Single-thread Gesso application.\n *\n * A thin DOM shell over GessoRuntime: it creates and sizes a canvas,\n * observes the host element, and forwards browser events into the\n * runtime's input controllers. All UI work — components, graph,\n * layout, rendering — belongs to the runtime and never touches the\n * DOM, which is what lets the same runtime host a render worker.\n *\n * This is the single-thread configuration. It is supported for tests,\n * headless rendering, and environments without OffscreenCanvas, but it\n * puts UI work on the main thread and so is not the default for an\n * interactive app.\n */\nexport class GessoApp {\n private readonly runtime: GessoRuntime;\n private readonly canvas: CanvasHost;\n private readonly host: HTMLElement;\n private readonly inputEnabled: boolean;\n private readonly accessibilityEnabled: boolean;\n private readonly adapter: UiPlatformAdapter;\n private readonly historyOptions: ShellHistoryOptions | undefined;\n\n private running = false;\n private resizeObserver: ResizeObserver | null = null;\n private proxy: EditingProxy | null = null;\n /** The one audio element, behind `AudioService`; see `AudioSink`. */\n private audio: AudioSink | null = null;\n private mirror: SemanticsMirror | null = null;\n private history: ShellHistory | null = null;\n private detachVisibility: (() => void) | null = null;\n private detachFullscreen: (() => void) | null = null;\n private fullscreen = false;\n private detachReducedMotion: (() => void) | null = null;\n /** Stops watching `prefers-color-scheme`; null while overridden. */\n private detachColorScheme: (() => void) | null = null;\n /** Stops watching `visualViewport` for the safe area and the keyboard. */\n private detachViewportInsets: (() => void) | null = null;\n /** The appearance this shell reports; watched or overridden. */\n private colorSchemePreference: ColorSchemePreference = 'auto';\n\n constructor(options: GessoAppOptions) {\n this.host = options.host;\n this.canvas = options.canvas ?? createCanvasElement();\n this.inputEnabled = options.input ?? true;\n this.accessibilityEnabled = options.accessibility ?? true;\n this.historyOptions = options.history;\n this.colorSchemePreference = options.colorScheme ?? 'auto';\n\n this.runtime = new GessoRuntime({\n root: options.root,\n canvas: this.canvas,\n renderer: options.renderer,\n services: options.services,\n channels: options.channels,\n routes: options.routes,\n media: options.media,\n fonts: options.fonts,\n clock: options.clock ?? (callback => new UiAnimationFrameClock(callback)),\n dpr: devicePixelRatio()\n });\n\n const input = this.runtime.input;\n this.adapter = new UiPlatformAdapter({\n pointerController: input.pointer,\n wheelController: input.wheel,\n keyboardController: input.keyboard\n });\n }\n\n /** The runtime services a component in this app can inject. */\n get services(): ServiceRegistry {\n return this.runtime.services;\n }\n\n /**\n * The platform adapter, for tests and for callers driving input from\n * a non-DOM source.\n */\n get input(): UiPlatformAdapter {\n return this.adapter;\n }\n\n /**\n * Starts the app: appends the canvas to the host, sizes it,\n * attaches input, and arms the frame scheduler.\n */\n mount(): void {\n if (this.running) {\n return;\n }\n this.running = true;\n\n if (isCanvasElement(this.canvas) && this.canvas.parentElement !== this.host) {\n this.host.appendChild(this.canvas);\n this.canvas.style.display = 'block';\n this.canvas.style.width = '100%';\n this.canvas.style.height = '100%';\n }\n\n this.observeResize();\n // The canvas's box rather than the host's: clientWidth/clientHeight\n // include the host's padding, and starting the runtime a padding\n // wider than the surface it draws on leaves the first frame scaled\n // and every pointer coordinate off by the same ratio. See\n // `measure` in worker/WorkerApp for the same reasoning on the\n // worker path.\n const box = isCanvasElement(this.canvas) ? measure(this.canvas, this.host) : undefined;\n this.resize(box?.width ?? this.canvas.width ?? 600, box?.height ?? this.canvas.height ?? 600);\n this.attachInput();\n this.attachHistory();\n this.attachViewportInsets();\n this.runtime.onCursor(cursor => {\n if (isCanvasElement(this.canvas)) {\n this.canvas.style.cursor = cursor ?? '';\n }\n });\n this.runtime.start();\n }\n\n /** Aligns patch delivery from worker-owned stores to the frame. */\n deferPatchesFrom(sources: readonly PatchSource[]): void {\n this.runtime.deferPatchesFrom(sources);\n }\n\n /**\n * Receives per-frame timings, mirroring WorkerAppOptions.onFrame so\n * the two configurations can be compared on equal terms.\n */\n onFrame(listener: ((metrics: FrameMetrics) => void) | null): void {\n this.runtime.onFrame(listener);\n }\n\n /** Resolves with the backend that ended up drawing. */\n get rendererReady(): Promise<'canvas2d' | 'webgpu'> {\n return this.runtime.rendererReady;\n }\n\n /** Resizes the drawing surface and schedules a repaint. */\n resize(width: number, height: number): void {\n this.runtime.resize(width, height, devicePixelRatio());\n }\n\n /** Turns the layout inspector on or off; see GessoRuntime.setInspectorEnabled. */\n /**\n * Replaces the root and rebuilds the tree, for hot module\n * replacement. See `GessoRuntime.reload` for what survives.\n */\n reload(root: FrameworkChild, services: readonly (new () => object)[] = []): void {\n this.runtime.reload(root, services);\n }\n\n setInspector(enabled: boolean): void {\n this.runtime.setInspectorEnabled(enabled);\n }\n\n /**\n * Chooses what the application is told about the appearance: `auto`\n * follows `prefers-color-scheme`, `light` and `dark` override it.\n *\n * The same method `WorkerApp` has, doing the same thing without the\n * protocol in the middle — which is the point of the two shells\n * having one surface.\n */\n setColorScheme(preference: ColorSchemePreference): void {\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.colorSchemePreference = preference;\n if (preference === 'auto') {\n this.detachColorScheme = observeColorScheme(scheme => this.runtime.setColorScheme(scheme));\n return;\n }\n this.runtime.setColorScheme(preference);\n }\n\n /** Receives a report on the hovered node while the inspector is on. */\n onInspect(listener: ((report: UiNodeReport | null) => void) | null): void {\n this.runtime.onInspect(listener);\n }\n\n /** Receives a devtools panel's answers and updates; see `WorkerApp.onDevtools`. */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void {\n this.runtime.onDevtools(listener);\n }\n\n /**\n * Answers a devtools panel, with the runtime in this thread. A\n * `console` request does nothing here: the page's console is already\n * the one the developer is reading.\n */\n devtools(request: DevtoolsRequest): void {\n this.runtime.handleDevtools(request);\n }\n\n /**\n * Receives the errors this configuration would otherwise only log:\n * a renderer that could not draw, and an application listener that\n * threw and was caught so the dispatch could continue.\n *\n * Mirrors `WorkerAppOptions.onError`, minus the two sources that\n * cannot arise here — nothing crosses a message boundary, and an\n * exception nothing catches is an ordinary main-thread error that\n * reaches `window` on its own.\n */\n onError(\n listener: ((message: string, stack: string | undefined, source: 'renderer' | 'listener') => void) | null\n ): void {\n this.runtime.onRendererError(listener === null ? null : message => listener(message, undefined, 'renderer'));\n this.runtime.onListenerError(listener === null ? null : (message, stack) => listener(message, stack, 'listener'));\n }\n\n debugRoot(): UiNode {\n return this.runtime.debugRoot();\n }\n\n /**\n * Stops the scheduler, detaches input, and removes the canvas.\n */\n dispose(): void {\n this.running = false;\n this.audio?.dispose();\n this.audio = null;\n this.runtime.onAudioRequest(null);\n this.proxy?.dispose();\n this.proxy = null;\n this.mirror?.dispose();\n this.mirror = null;\n this.detachVisibility?.();\n this.detachVisibility = null;\n this.detachFullscreen?.();\n this.detachFullscreen = null;\n this.detachReducedMotion?.();\n this.detachReducedMotion = null;\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.detachViewportInsets?.();\n this.detachViewportInsets = null;\n this.history?.dispose();\n this.history = null;\n this.adapter.detach();\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n if (isCanvasElement(this.canvas) && this.canvas.parentElement === this.host) {\n this.host.removeChild(this.canvas);\n }\n this.runtime.dispose();\n }\n\n private attachInput(): void {\n if (!this.inputEnabled || !isCanvasElement(this.canvas)) {\n // A headless or offscreen canvas has no DOM events to forward.\n return;\n }\n this.canvas.tabIndex = 0;\n prepareInputSurface(this.canvas);\n this.adapter.attach(new CanvasPlatformSurface(this.canvas));\n\n // Text editing goes through a hidden textarea, exactly as in the\n // worker configuration, so the IME works the same on both. Keys are\n // not forwarded from it: the surface above listens on `window` and\n // already sees them.\n const editing = this.runtime.input.editing;\n const canvas = this.canvas;\n this.proxy = new EditingProxy(canvas, {\n beforeInput: (inputType, data) => editing.beforeInput(inputType, data),\n compositionStart: () => editing.compositionStart(),\n compositionUpdate: (text, caret) => editing.compositionUpdate(text, caret),\n compositionEnd: text => editing.compositionEnd(text),\n paste: text => editing.paste(text),\n blur: () => this.runtime.input.focus.blur()\n });\n this.runtime.setTextInputSource('proxy');\n this.runtime.onEditingState(state => this.proxy?.update(state));\n // Sound, driven directly instead of over the protocol: the same\n // sink the worker configuration uses, which is the point of it not\n // knowing where its requests come from.\n this.audio = new AudioSink({\n sample: sample => this.runtime.applyAudioSample(sample),\n action: action => this.runtime.applyAudioAction(action)\n });\n this.runtime.onAudioRequest(request => this.audio?.handle(request));\n this.attachSemanticsMirror(canvas);\n if (typeof document !== 'undefined') {\n const onVisibility = (): void => this.runtime.setVisible(document.visibilityState !== 'hidden');\n document.addEventListener('visibilitychange', onVisibility);\n this.detachVisibility = () => document.removeEventListener('visibilitychange', onVisibility);\n this.detachFullscreen = observeFullscreen(canvas, active => {\n // The host's `ResizeObserver` does not see this; see\n // `surfaceBox` for why, and for why the size comes from the\n // canvas going in and from the host coming out.\n this.fullscreen = active;\n this.runtime.setFullscreen(active);\n afterLayout(() => {\n const box = surfaceBox(canvas, this.host, active);\n if (box !== null) {\n this.resize(box.width, box.height);\n }\n });\n });\n // Once at startup as well as on change: a canvas mounted into a\n // document that is already fullscreen never fires the event.\n this.runtime.setFullscreen(isDocumentFullscreen(canvas));\n }\n this.detachReducedMotion = observeReducedMotion(reduced => this.runtime.setReducedMotion(reduced));\n this.setColorScheme(this.colorSchemePreference);\n }\n\n /**\n * Mounts the off-screen DOM an assistive technology reads.\n *\n * The same class the worker configuration uses, driven directly\n * instead of over the protocol — which is the point of it being a\n * DOM class that knows nothing about where its updates come from.\n * Keys are not forwarded from it: `CanvasPlatformSurface` listens on\n * `window` and already sees them, exactly as for the editing proxy.\n */\n private attachSemanticsMirror(canvas: HTMLCanvasElement): void {\n if (!this.accessibilityEnabled) {\n return;\n }\n const mirror = new SemanticsMirror(\n canvas,\n { action: action => this.runtime.applySemanticsAction(action) },\n this.proxy\n );\n this.mirror = mirror;\n this.runtime.onSemantics(update => mirror.apply(update));\n }\n\n /**\n * Connects the router to the window's address bar.\n *\n * Outside `attachInput` deliberately: an app mounted with\n * `input: false` — a test, a headless render — still routes, and a\n * router that silently stopped syncing in that configuration would\n * be a difference between the two hosts that nothing declared.\n */\n private attachHistory(): void {\n const history = createShellHistory(this.historyOptions);\n this.history = history;\n this.runtime.onShellRequest(request => this.handleShellRequest(request, history));\n history.onChange(url => this.runtime.setUrl(url));\n this.runtime.setUrl(history.url);\n }\n\n /**\n * Reports the platform's safe area and soft keyboard to the runtime.\n *\n * Outside `attachInput` for the reason `attachHistory` is: the insets\n * are a layout fact, and an app mounted with `input: false` still\n * lays out. Nothing is decided here. `observeViewportInsets` reads\n * `visualViewport` and hands over four numbers, and the runtime\n * publishes them into the application's inset registry, which is the\n * same route `WorkerApp` takes with a message in the middle.\n */\n private attachViewportInsets(): void {\n this.detachViewportInsets = observeViewportInsets(insets => this.runtime.setViewportInsets(insets));\n }\n\n private handleShellRequest(request: ShellRequest, history: ShellHistory): void {\n if (request.type === 'history') {\n if (request.action === 'push') {\n history.push(request.url);\n } else if (request.action === 'replace') {\n history.replace(request.url);\n } else if (request.action === 'back') {\n history.back();\n } else {\n history.forward();\n }\n return;\n }\n if (!isCanvasElement(this.canvas)) {\n // No document to write a clipboard through or open a window from.\n // A popup still has to be answered, because a promise nobody\n // settles is worse than a popup nobody opened, and a storage\n // request is answered for the same reason.\n if (request.type === 'popup') {\n this.runtime.settlePopup(request.id, false);\n } else if (request.type === 'storage') {\n this.runtime.settleStorage(request.id, shellStorageDenied());\n }\n return;\n }\n if (request.type === 'clipboard') {\n writeClipboard(request.text, this.canvas.ownerDocument);\n return;\n }\n if (request.type === 'fullscreen') {\n // The canvas rather than the document body: what the application\n // draws on is what should fill the screen, and a host page with\n // chrome of its own around the canvas does not want that chrome\n // blown up with it.\n setElementFullscreen(this.canvas, request.enter);\n return;\n }\n const view = this.canvas.ownerDocument.defaultView;\n if (request.type === 'storage') {\n // Performed here rather than posted, because in this\n // configuration the shell and the render side are the same\n // thread; the answer is the same either way.\n this.runtime.settleStorage(\n request.id,\n performShellStorage(request, () => view?.localStorage)\n );\n return;\n }\n if (request.type === 'popup') {\n // Same shape as `WorkerApp.openPopup`, and `noopener` is absent\n // for the same reason: it would make the answer always `null`.\n let opened = false;\n try {\n const features = `popup,width=${request.width},height=${request.height}`;\n opened = (view?.open(request.url, request.name, features) ?? null) !== null;\n } catch {\n opened = false;\n }\n this.runtime.settlePopup(request.id, opened);\n return;\n }\n view?.open(request.url, '_blank', 'noopener,noreferrer');\n }\n\n private observeResize(): void {\n if (typeof ResizeObserver === 'undefined') {\n return;\n }\n this.resizeObserver = new ResizeObserver(entries => {\n const entry = entries[0];\n if (entry === undefined) {\n return;\n }\n // contentRect is the logical CSS size. The surface owns the\n // backing store, so the canvas attributes are never written here:\n // round-tripping the size through them once meant the second\n // resize read device pixels back as logical pixels.\n if (this.fullscreen) {\n // The canvas is filling the screen and is no longer in the\n // host's flow; see the fullscreen listener above.\n return;\n }\n this.resize(entry.contentRect.width, entry.contentRect.height);\n });\n this.resizeObserver.observe(this.host);\n }\n}\n\nfunction createCanvasElement(): HTMLCanvasElement {\n return document.createElement('canvas');\n}\n\nfunction isCanvasElement(canvas: CanvasHost): canvas is HTMLCanvasElement {\n return typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement;\n}\n\nfunction devicePixelRatio(): number {\n return typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1;\n}\n","import { createComponent } from '../createComponent';\nimport type { FrameworkChild } from '../ComponentElement';\nimport type { ComponentType } from '../FunctionComponent';\nimport type { WorkerHandle } from '../worker/WorkerPorts';\nimport { ServiceRegistry } from '../service/ServiceRegistry';\nimport { createChannelRegistry, type ChannelRegistration } from '../channel/createChannelRegistry';\nimport type { ChannelSource } from '../channel/provide';\nimport type { ChannelToken } from '../channel/ChannelToken';\nimport type { ColorSchemePreference } from './colorScheme';\nimport { GessoApp } from './GessoApp';\nimport type { RouterRoutes } from '../router/RouterService';\nimport type { ShellHistoryOptions } from './shellHistory';\nimport type { FrameMetrics, RendererChoice } from './GessoRuntime';\nimport type { MediaOptions } from './MediaService';\nimport type { FontFamilyDeclaration } from './FontService';\nimport type { UiNodeReport } from './NodeReport';\nimport type { DevtoolsEvent, DevtoolsRequest } from './DevtoolsProtocol';\n\n/**\n * Fluent builder for the single-thread configuration.\n */\nexport class GessoAppBuilder {\n private readonly channelRegistrations: ChannelRegistration[] = [];\n private readonly serviceRegistrations: (new () => object)[] = [];\n private frameListener: ((metrics: FrameMetrics) => void) | undefined;\n private inspectListener: ((report: UiNodeReport | null) => void) | undefined;\n private devtoolsListener: ((event: DevtoolsEvent) => void) | null = null;\n private errorListener:\n | ((message: string, stack: string | undefined, source: 'renderer' | 'listener') => void)\n | undefined;\n private rendererChoice: RendererChoice | undefined;\n private routes: RouterRoutes | undefined;\n private historyOptions: ShellHistoryOptions | undefined;\n private mediaOptions: MediaOptions | undefined;\n private fontDeclarations: readonly FontFamilyDeclaration[] | undefined;\n private app: GessoApp | undefined;\n private colorSchemePreference: ColorSchemePreference = 'auto';\n\n constructor(private root: FrameworkChild | ComponentType) {}\n\n /**\n * Registers a channel.\n *\n * With `worker`, the channel's data lives there — api, store, domain\n * and view models, all plain code the framework never sees. With\n * `source`, it is fed from this thread; either way the same patches\n * cross the same kind of port, so a channel can be moved into a\n * worker later without a view noticing.\n */\n useChannel<V extends object, C extends object>(\n token: ChannelToken<V, C>,\n options: { worker?: WorkerHandle | (() => Worker); source?: ChannelSource<V, C> }\n ): this {\n this.channelRegistrations.push({\n token: token as unknown as ChannelToken<never, never>,\n worker: options.worker,\n source: options.source as unknown as ChannelSource<never, never>\n });\n return this;\n }\n\n /**\n * Registers a runtime service: a plain class this thread constructs\n * once and hands to whoever injects it.\n *\n * For things that belong to the render thread and could not leave it\n * — something holding a `UiNode`, a decoded bitmap, or a generator\n * feeding bound props at frame rate. Application state goes through\n * `useChannel` instead, and the test is the usual one: if it\n * survives a reload or another screen cares about it, it is not a\n * service.\n */\n useService(ServiceClass: new () => object): this {\n this.serviceRegistrations.push(ServiceClass);\n return this;\n }\n\n /**\n * Declares the app's routes, which is all it takes to make a\n * `RouterOutlet` in the tree resolve them. Mirrors\n * `renderRoot().useRoutes`.\n */\n useRoutes(routes: RouterRoutes): this {\n this.routes = routes;\n return this;\n }\n\n /**\n * Declares where the app's pictures come from: the image resolver,\n * the icon rasteriser and the video decoder. Mirrors\n * `renderRoot().useMedia`.\n *\n * createApp(AppRoot).useMedia({ resolver: new CachingResolver() });\n *\n * Declared here rather than set on `MediaService` afterwards because\n * the tree is built inside the runtime's constructor and an `Image`\n * in it asks for its bitmap at that moment. Whatever is left out,\n * the runtime builds and owns; whatever is passed stays the\n * caller's, and the runtime will not dispose it.\n */\n useMedia(media: MediaOptions): this {\n this.mediaOptions = media;\n return this;\n }\n\n /**\n * Declares the font families the app's text may name; the\n * single-thread twin of `renderRoot().useFonts`, loading into\n * `document.fonts` instead of a worker's set.\n */\n useFonts(families: readonly FontFamilyDeclaration[]): this {\n this.fontDeclarations = families;\n return this;\n }\n\n /**\n * How the app's url is kept: `path` (pushState, the default in a\n * browser), `hash`, or `memory`. See `shellHistory`.\n */\n useHistory(history: ShellHistoryOptions): this {\n this.historyOptions = history;\n return this;\n }\n\n /**\n * Pins the rendering backend. Without this the app is on `auto`:\n * WebGPU where the browser has it, Canvas2D elsewhere.\n */\n renderer(choice: RendererChoice): this {\n this.rendererChoice = choice;\n return this;\n }\n\n /**\n * Receives per-frame timings, mirroring the worker configuration's\n * onFrame option.\n */\n onFrame(listener: (metrics: FrameMetrics) => void): this {\n this.frameListener = listener;\n return this;\n }\n\n /**\n * Receives the hovered node's layout explanation while the inspector\n * is on, mirroring WorkerAppOptions.onInspect.\n */\n onInspect(listener: (report: UiNodeReport | null) => void): this {\n this.inspectListener = listener;\n return this;\n }\n\n /**\n * Receives errors the runtime would otherwise only log, mirroring\n * WorkerAppOptions.onError. See `GessoApp.onError`.\n */\n onError(listener: (message: string, stack: string | undefined, source: 'renderer' | 'listener') => void): this {\n this.errorListener = listener;\n return this;\n }\n\n /**\n * Turns the layout inspector on or off on the mounted app, mirroring\n * WorkerApp.setInspector. A no-op before mountSync.\n */\n setInspector(enabled: boolean): void {\n this.app?.setInspector(enabled);\n }\n\n /**\n * Receives a devtools panel's answers and updates, mirroring\n * `WorkerApp.onDevtools`. Remembered before `mountSync`, so a panel\n * connected to the builder hears the app once there is one.\n */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void {\n this.devtoolsListener = listener;\n this.app?.onDevtools(listener);\n }\n\n /** Answers a devtools panel, mirroring `WorkerApp.devtools`. A no-op before mountSync. */\n devtools(request: DevtoolsRequest): void {\n this.app?.devtools(request);\n }\n\n /**\n * Replaces the root and rebuilds the tree, for hot module\n * replacement, mirroring `RenderWorkerApp.reload`.\n *\n * Before `mountSync` it changes which root will be built, so an\n * entry that accepts a replacement during startup is not a race.\n */\n reload(root: FrameworkChild | ComponentType, services: readonly (new () => object)[] = []): this {\n this.root = root;\n this.app?.reload(typeof root === 'function' ? createComponent(root as ComponentType) : root, services);\n return this;\n }\n\n /**\n * Chooses what the application is told about the appearance,\n * mirroring `WorkerApp.setColorScheme`.\n *\n * Unlike `setInspector`, this is remembered when it is called before\n * `mountSync`: the appearance decides what the first frame looks\n * like, so a host that already knows the reader's choice must be\n * able to say so before there is an app to tell.\n */\n setColorScheme(preference: ColorSchemePreference): this {\n this.colorSchemePreference = preference;\n this.app?.setColorScheme(preference);\n return this;\n }\n\n /**\n * Mounts the app on the calling thread.\n *\n * Named for what it costs: everything — components, layout and\n * rendering — runs here, so main-thread work delays frames. Use the\n * worker configuration for interactive apps.\n *\n * Returns a dispose function that tears the app down.\n */\n mountSync(host: HTMLElement | string): () => void {\n const element = typeof host === 'string' ? requireElement(host) : host;\n const rootElement = typeof this.root === 'function' ? createComponent(this.root as ComponentType) : this.root;\n const channels = createChannelRegistry(this.channelRegistrations);\n const services = new ServiceRegistry();\n for (const ServiceClass of this.serviceRegistrations) {\n services.register(ServiceClass);\n }\n const app = new GessoApp({\n host: element,\n root: rootElement,\n channels: channels.registry,\n services,\n routes: this.routes,\n media: this.mediaOptions,\n fonts: this.fontDeclarations,\n history: this.historyOptions,\n renderer: this.rendererChoice,\n colorScheme: this.colorSchemePreference\n });\n app.deferPatchesFrom(channels.registry.all());\n if (this.frameListener !== undefined) {\n app.onFrame(this.frameListener);\n }\n if (this.inspectListener !== undefined) {\n app.onInspect(this.inspectListener);\n }\n if (this.errorListener !== undefined) {\n app.onError(this.errorListener);\n }\n if (this.devtoolsListener !== null) {\n app.onDevtools(this.devtoolsListener);\n }\n this.app = app;\n app.mount();\n return () => {\n this.app = undefined;\n app.dispose();\n channels.dispose();\n };\n }\n}\n\nfunction requireElement(selector: string): HTMLElement {\n const element = document.querySelector<HTMLElement>(selector);\n if (element === null) {\n throw new Error(`Mount host '${selector}' was not found.`);\n }\n return element;\n}\n","import type { FrameworkChild } from '../ComponentElement';\nimport type { ComponentType } from '../FunctionComponent';\nimport { GessoAppBuilder } from './GessoAppBuilder';\nimport { WorkerApp, type WorkerAppOptions } from './worker/WorkerApp';\n\n/**\n * What a shell passes when `gesso-vite-plugin` supplies the workers.\n *\n * The only difference from `WorkerAppOptions` is that `renderWorker` is\n * optional, because the plugin writes it: it rewrites `createApp(...)`\n * so that its own factories are merged under whatever the author wrote,\n * and the literal `new Worker(new URL(...))` it emits is the same\n * expression an author would have written by hand. Writing it by hand\n * is still supported and still documented; what the\n * plugin removes is the obligation to.\n *\n * Without the plugin and without a `renderWorker`, this throws with the\n * two ways to fix it, rather than mounting an application that would\n * quietly never draw.\n */\nexport interface CreateAppOptions extends Omit<WorkerAppOptions, 'renderWorker'> {\n /** Spawns the render worker. Written by the plugin when it is absent. */\n renderWorker?: WorkerAppOptions['renderWorker'];\n /**\n * The `name` to give every worker the plugin constructs, which is\n * how a worker reads a flag that only the page's url carries: the\n * playground's `?still` and Segue's are the same trick.\n *\n * Ignored when `renderWorker` is written by hand, since the factory\n * then names the worker itself.\n */\n workerName?: string;\n}\n\n/**\n * Creates a Gesso application.\n *\n * Two configurations, because a class reference cannot cross\n * postMessage: the root component has to already be inside the worker\n * that renders it.\n *\n * Worker-hosted (the default for an interactive app) — all UI work\n * happens off the main thread. With `gesso-vite-plugin` in the Vite\n * config, the shell names no worker at all:\n *\n * // main.ts\n * createApp({ history: { mode: 'path' } }).mount('#app');\n *\n * // app.render.worker.ts\n * renderRoot(AppRoot).useChannel(Catalog);\n *\n * and without it, the same thing said out loud:\n *\n * createApp({\n * renderWorker: () => new Worker(new URL('./app.render.worker.ts', import.meta.url), { type: 'module' }),\n * appLogicWorker: () => new Worker(new URL('./app.logic.worker.ts', import.meta.url), { type: 'module' })\n * }).mount('#app');\n *\n * Single-thread — for tests, headless rendering, and environments\n * without OffscreenCanvas:\n *\n * createApp(AppRoot).useChannel(Catalog, { source }).mountSync('#app');\n */\nexport function createApp(options?: CreateAppOptions): WorkerApp;\nexport function createApp(root: FrameworkChild | ComponentType): GessoAppBuilder;\nexport function createApp(arg?: CreateAppOptions | FrameworkChild | ComponentType): WorkerApp | GessoAppBuilder {\n if (isWorkerAppOptions(arg)) {\n if (arg?.renderWorker === undefined) {\n throw new Error(\n 'createApp() was given no render worker. Add `gesso()` from gesso-vite-plugin to the Vite config, which ' +\n \"writes the construction, or pass one: renderWorker: () => new Worker(new URL('./worker.ts', \" +\n \"import.meta.url), { type: 'module' }).\"\n );\n }\n return new WorkerApp(arg as WorkerAppOptions);\n }\n return new GessoAppBuilder(arg);\n}\n\n/**\n * Whether the argument describes the worker configuration.\n *\n * It used to be the presence of `renderWorker`, which no longer\n * distinguishes anything: the plugin's whole point is that the option\n * is absent. So the test is the other way round — anything that is\n * recognisably a child is one, and everything else is options. A\n * `UiElement` carries `type`, a `ComponentElement` carries `kind`, an\n * observable child carries `subscribe`, and a component is a function;\n * `WorkerAppOptions` has none of those and never will, because each is\n * the identity of something that goes in a tree.\n */\nfunction isWorkerAppOptions(value: unknown): value is CreateAppOptions | undefined {\n if (value === undefined) {\n return true;\n }\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n return false;\n }\n return !('type' in value) && !('kind' in value) && !('subscribe' in value);\n}\n","import { UiNodeType, type UiNode, type UiShortcutRegistry } from 'gesso-core';\n\nimport type { UndoStack } from './UndoStack';\n\nexport interface UndoShortcutOptions {\n /** The registry the `shortcuts` modifier feeds. */\n readonly registry: UiShortcutRegistry;\n readonly stack: UndoStack;\n /**\n * The node keyboard input is going to, usually\n * `() => ctx.inject(FocusService).focused.value`.\n *\n * Given one, Mod+Z is skipped while focus is inside something being\n * typed into, so a text field keeps its own undo. Leave it out only\n * in an application with no editable text: without it the\n * application's stack takes every Mod+Z, including the ones a person\n * pressed to unmake the last three letters they typed.\n */\n readonly focused?: () => UiNode | null;\n /** The heading a palette groups the two commands under. */\n readonly group?: string;\n /** Default `'Mod+Z'`. */\n readonly undoKeys?: string;\n /** Default `'Mod+Shift+Z'` and `'Mod+Y'`, the two spellings in use. */\n readonly redoKeys?: readonly string[];\n}\n\n/**\n * Registers undo and redo on the application's shortcut registry.\n *\n * One key path rather than two: the registry is the place a command's\n * keys are declared, so that a palette listing the\n * available commands and the handler that fires them ask the same\n * question. Undo is exactly the command that would otherwise grow a\n * second path, because every application reaches for a root\n * `onKeyDown` for it first.\n *\n * const undo = ctx.inject(UndoStack);\n * ctx.onUnmount(registerUndoShortcuts({ registry, stack: undo, focused: () => focus.focused.value }));\n *\n * The returned function unregisters both, so the pair's lifetime is\n * whatever registered them.\n *\n * `when` is what keeps the two honest: a stack with nothing on it\n * offers no undo, so the key falls through to whatever else wants it\n * and a palette does not list a command that would do nothing.\n */\nexport function registerUndoShortcuts(options: UndoShortcutOptions): () => void {\n const { registry, stack, focused, group } = options;\n const available = (): boolean => !typingInto(focused?.() ?? null);\n const undone = registry.register({\n keys: options.undoKeys ?? 'Mod+Z',\n label: 'Undo',\n ...(group === undefined ? {} : { group }),\n when: () => stack.canUndo.value && available(),\n run: () => void stack.undo()\n });\n const redone = (options.redoKeys ?? ['Mod+Shift+Z', 'Mod+Y']).map(keys =>\n registry.register({\n keys,\n label: 'Redo',\n ...(group === undefined ? {} : { group }),\n when: () => stack.canRedo.value && available(),\n run: () => void stack.redo()\n })\n );\n return () => {\n undone();\n for (const remove of redone) {\n remove();\n }\n };\n}\n\n/**\n * Whether keys reaching this node are being typed into something.\n *\n * The same walk `UiShortcutRegistry` makes for a shortcut with no\n * modifier at all, made here for a shortcut that has one. The\n * registry's own rule cannot cover this case: Mod+Z carries a\n * modifier, so by the registry's reckoning it is not a letter somebody\n * is trying to type, and it takes a key that\n * `EditableTextModel.undo` is waiting for. The keyboard controller\n * applies the editing keys **after** the event has been dispatched, so\n * a root shortcut sees Mod+Z first and would win every time.\n */\nfunction typingInto(node: UiNode | null): boolean {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current.type === UiNodeType.EditableText) {\n return true;\n }\n }\n return false;\n}\n","import type { ShellService, ShellStorageResult } from '../app/ShellService';\nimport type { StorageAdapter, StorageOutcome, StorageRead } from './StorageAdapter';\n\nexport interface ShellStorageOptions {\n /**\n * What every key is written under, so an application's records are\n * recognisable in devtools and cannot collide with another script's\n * on the same origin. Default `'gesso:'`.\n *\n * Applied on this side rather than on the shell's, because the shell\n * is asked to store a key and nothing more; deciding what the key\n * means is the application's work and belongs on the thread doing\n * the application's work.\n */\n readonly prefix?: string;\n}\n\n/**\n * `localStorage`, reached through the shell.\n *\n * The odd one of the three, and worth saying why it exists. It is\n * synchronous on the window and unreachable from a worker, so a render\n * thread asking for it pays a round trip; it holds a few megabytes at\n * most; and writing to it blocks the main thread, which is the thread\n * the framework works hardest to leave alone. None of that makes it a\n * good place for an application's state, and all of it is beside the\n * point for the thing it is actually good for: a small preference that\n * something outside the application also reads, or that has to be\n * there before the first frame of the *next* visit rather than the\n * next frame of this one.\n *\n * Use `OpfsStorage` or `IndexedDbStorage` for anything else.\n *\n * What happens on each failure:\n *\n * - **No shell** (a headless runtime, a spec): `denied`, at once.\n * - **The browser blocks `localStorage`**, which it does in some\n * private windows and under some site settings: `denied`. Reading\n * `window.localStorage` is what throws, so the failure arrives on\n * the first call rather than at start-up.\n * - **The quota is spent**: `full`. `localStorage` has the smallest\n * quota of the three and reaches it soonest, which is the other\n * reason not to keep an application's state here.\n * - **Anything else**: `failed`, with the message the shell reported.\n *\n * Every answer crosses the barrier as plain data: a string, a list of\n * strings, and one of four words.\n */\nexport class ShellStorage implements StorageAdapter {\n private readonly prefix: string;\n\n constructor(\n private readonly shell: ShellService,\n options: ShellStorageOptions = {}\n ) {\n this.prefix = options.prefix ?? 'gesso:';\n }\n\n async read(key: string): Promise<StorageRead> {\n const result = await this.ask('read', key);\n return { outcome: result.outcome, value: result.value, error: result.error };\n }\n\n async write(key: string, value: string): Promise<StorageOutcome> {\n return (await this.ask('write', key, value)).outcome;\n }\n\n async remove(key: string): Promise<StorageOutcome> {\n return (await this.ask('remove', key)).outcome;\n }\n\n async keys(): Promise<readonly string[]> {\n // The prefix is this store's namespace, so the keys it reports are\n // its own with the namespace taken back off. A listing that\n // included every other script's key would be a listing an\n // application could not act on.\n const result = await this.ask('keys', '');\n return result.keys.filter(key => key.startsWith(this.prefix)).map(key => key.slice(this.prefix.length));\n }\n\n private ask(op: 'read' | 'write' | 'remove' | 'keys', key: string, value?: string): Promise<ShellStorageResult> {\n return this.shell.requestStorage({\n op,\n key: op === 'keys' ? '' : `${this.prefix}${key}`,\n ...(value === undefined ? {} : { value })\n });\n }\n}\n","import type { VideoClock } from 'gesso-core';\n\nimport type { AudioService } from './AudioService';\n\n/**\n * A clip's position, taken from the sound playing beside it.\n *\n * **This is the clock inversion, and it is worth being explicit about\n * why it exists.** Everywhere else in this framework the animation\n * driver owns time: a video is a pure function of a position and the\n * position comes from a tween, which is what lets a clip be scheduled,\n * paced and stopped by exactly the machinery every other animated\n * thing uses. That arrangement is correct right up until the clip has\n * sound, and then it is exactly backwards.\n *\n * The asymmetry is in what the two media can survive. Video drops\n * frames: an application that stalled for 50ms shows the picture it\n * should be showing now and throws away the ones it missed, and nobody\n * can tell. Audio cannot do either — a gap is audible, and resampling\n * to catch up changes the pitch — so the sound plays at its own rate\n * whatever else is happening, and anything that must agree with it has\n * to follow. Which means the picture follows the sound, and a tween\n * driving the picture independently would drift against it within\n * seconds. There is no arrangement in which both lead.\n *\n * **And the sound cannot be played here.** `AudioContext` does not\n * exist on a worker, which is where this framework decodes and draws.\n * So the sound is the shell's to play — `AudioService` already does\n * exactly that, reporting where it has got to about once a second and\n * extrapolating in between on the animation driver — and what crosses\n * the thread boundary is one number. That is the whole of this file:\n * `AudioService` is already the right shape, and this is the adapter\n * that lets `videoSource` read it.\n *\n * ```ts\n * // The same file, played twice: the shell for its sound, the worker\n * // for its pictures. A `<video>` element does this internally and\n * // calls it one thing.\n * audio.load('clip.mp4', { autoplay: false });\n * Video({ src: 'clip.mp4', clock: audioClock(audio, 'clip.mp4') });\n * ```\n *\n * `source` is checked on every read, and that check is not\n * decoration: `AudioService` is one playback for the whole\n * application, so a screen that starts a podcast while this clip is\n * still mounted would otherwise drive the picture from the podcast's\n * position. A clock whose source is no longer loaded reports that it\n * is not running and holds the position it last had, which shows a\n * still frame rather than a clip that has jumped somewhere absurd.\n */\nexport function audioClock(audio: AudioService, source: string): VideoClock {\n let held = 0;\n let listeners = new Set<() => void>();\n let wasRunning = false;\n let subscription: { unsubscribe(): void } | null = null;\n\n const mine = (): boolean => audio.state.value.src === source;\n\n const running = (): boolean => mine() && audio.state.value.status === 'playing';\n\n const announce = (): void => {\n const now = running();\n if (now === wasRunning) {\n // The position moves constantly and is read rather than pushed;\n // what a listener is waiting for is the clip starting or\n // stopping, which is rare.\n return;\n }\n wasRunning = now;\n for (const listener of listeners) {\n listener();\n }\n };\n\n return {\n positionSeconds(): number {\n if (!mine()) {\n return held;\n }\n held = audio.state.value.position;\n return held;\n },\n get running(): boolean {\n return running();\n },\n onChange(listener: () => void): () => void {\n listeners.add(listener);\n // Subscribed on the first listener rather than eagerly, because\n // `AudioService.state` is a `ComputedCell` and a computed with\n // no subscriber holds nothing and runs nothing.\n subscription ??= audio.state.subscribe(() => announce());\n return () => {\n listeners.delete(listener);\n if (listeners.size > 0) {\n return;\n }\n subscription?.unsubscribe();\n subscription = null;\n listeners = new Set();\n };\n }\n };\n}\n","import { BehaviorSubject, type Observable } from 'rxjs';\n\nimport {\n Box,\n motion,\n percent,\n type MotionStateInput,\n type MotionTiming,\n type UiChild,\n type UiElement,\n type UiLength\n} from 'gesso-core';\nimport type { ComponentContext, Inputs } from './FunctionComponent';\n\n/**\n * Keeps a child on screen while it leaves.\n *\n * Animation closed with one thing deliberately open: *\"No exit\n * animation. A dialog still leaves on the frame it closes. An exit\n * needs the tree to keep a node that has logically left, which is a\n * component-lifetime question F4 does not settle.\"* This settles it,\n * and the answer is that the node is not kept — the **definition** is.\n *\n * **Why not in the reconciler.** The obvious move is to make\n * `UiGraphBuilder.removeSubtree` hold a node that wants to animate\n * out. It does not survive contact. The held node has no definition,\n * so every later reconcile has to be taught to look past it; its id is\n * still in the graph's index, so the same element returning collides\n * with it; and its component host must not be released, which means\n * the resolver has to learn about animation. That is three new\n * problems in the one part of the system whose correctness everything\n * else rests on, bought for one feature.\n *\n * Keeping the definition instead costs nothing anywhere else. This\n * component subscribes to a list of children, notices when one stops\n * appearing in it, and goes on rendering that one — as an ordinary\n * child, with an ordinary host — until its exit animation reports that\n * it is done. Then it stops, and the ordinary removal path runs,\n * unchanged and unaware that anything unusual happened. It is what\n * `AnimatePresence` does and what a Svelte `transition:` does, for\n * this reason.\n *\n * **It is a stack, and that is not incidental.** Each child is laid\n * out absolutely inside a positioned container, so a child on its way\n * out holds no space and the one arriving does not wait for it. That\n * is exactly what a screen transition wants — the two screens overlap\n * for as long as the transition lasts — and it is why this is the\n * right shape for a route outlet, a dialog and a toast, and the wrong\n * shape for a row leaving a list, where the neighbours must close the\n * gap behind it. A list wants `animateLayout` on the rows that stay.\n */\nexport interface PresenceProps {\n /**\n * The children, keyed. A child that stops appearing is animated out\n * rather than removed.\n *\n * Identity is the element's `key`. Without one a child is keyed by\n * its position, which for the single swapping child this is most\n * often used for is the same thing — but a list must key its\n * children, exactly as it must anywhere else in this framework.\n */\n children?: UiChild | readonly UiChild[];\n /** Where a child starts when it appears. Omitted means it just appears. */\n enter?: MotionStateInput;\n /**\n * Where a child goes as it leaves. Omitted means it just goes, on\n * the frame it stops being asked for, exactly as it did before this\n * component existed.\n *\n * An `enter` or `exit` that changes opacity dims everything inside\n * the child, including any `sharedElement` morphing across the\n * change — see `RouteTransition` for why the two do not compose and\n * what it looks like when you try.\n */\n exit?: MotionStateInput;\n /**\n * `together` (the default) — the arriving child enters while the\n * departing one leaves, and for a moment both are on screen. What a\n * shared-element transition needs, since the morph is measured off\n * the element that is still standing there.\n *\n * `wait` — the departing child finishes leaving before the arriving\n * one is built at all. Right when the two would read as clutter on\n * top of each other, and wrong whenever anything is shared.\n */\n mode?: 'together' | 'wait';\n /** How the enter and the exit are timed. One timing serves both. */\n timing?: MotionTiming;\n /** The container's size. Both default to filling whatever it is given. */\n width?: UiLength | number;\n height?: UiLength | number;\n /**\n * A ceiling on how long a child may take to leave, in milliseconds.\n *\n * Insurance rather than policy. An exit that never settles — a\n * spring given absurd numbers, an element unmounted before it ever\n * had a frame — would otherwise keep a whole screen mounted for\n * good, and a leak that looks like a rendering bug is the worst\n * kind. Two seconds by default, far longer than any exit anyone\n * should be writing.\n */\n exitTimeout?: number;\n}\n\n/** One child that is on screen: its definition, and whether it is going. */\ninterface PresenceEntry {\n readonly key: string;\n child: UiChild;\n leaving: boolean;\n /** Bumped when a child comes back before it finished leaving. */\n generation: number;\n}\n\nconst DEFAULT_EXIT_TIMEOUT_MS = 2000;\n\nexport function Presence(inputs: Inputs<PresenceProps>, ctx: ComponentContext): UiChild {\n /** What is on screen: departing children first, so arrivals paint over them. */\n let entries: PresenceEntry[] = [];\n /** In `wait` mode, the children held back until the screen is clear. */\n let held: PresenceEntry[] | null = null;\n const timers = new Map<string, ReturnType<typeof setTimeout>>();\n const output = new BehaviorSubject<readonly UiChild[]>([]);\n let disposed = false;\n\n const clearTimer = (key: string): void => {\n const timer = timers.get(key);\n if (timer !== undefined) {\n clearTimeout(timer);\n timers.delete(key);\n }\n };\n\n const layerFor = (entry: PresenceEntry): UiChild =>\n Box(\n {\n key: entry.key,\n // A stack: every child fills the container and none of them\n // holds space from another. See the note on this component.\n position: 'absolute',\n left: 0,\n top: 0,\n width: percent(100),\n height: percent(100),\n // Each layer is its own top layer. A `lift`ed element inside a\n // child escapes whatever clips it within that child, and stops\n // there: without this it would be painted over every layer,\n // and a screen on its way out would fly its morphing artwork\n // across the screen arriving behind it. See the `liftBoundary`\n // property.\n liftBoundary: true,\n modifiers: [\n motion({\n ...inputs.timing.value,\n initial: inputs.enter.value,\n state: entry.leaving ? (inputs.exit.value ?? null) : null,\n onSettled: entry.leaving ? () => settle(entry) : undefined\n })\n ]\n },\n entry.child\n );\n\n const emit = (): void => {\n if (!disposed) {\n output.next(entries.map(layerFor));\n }\n };\n\n /** A child has finished leaving, so it may finally be dropped. */\n const settle = (entry: PresenceEntry): void => {\n const index = entries.indexOf(entry);\n if (!entry.leaving || index === -1) {\n return;\n }\n clearTimer(entry.key);\n entries.splice(index, 1);\n releaseHeld();\n emit();\n };\n\n /**\n * `wait` mode lets its arrivals in once the last departure is over,\n * which is the only moment they can be built without overlapping\n * what they replace.\n */\n const releaseHeld = (): void => {\n if (held === null || entries.some(entry => entry.leaving)) {\n return;\n }\n entries.push(...held);\n held = null;\n };\n\n const armTimeout = (entry: PresenceEntry): void => {\n clearTimer(entry.key);\n timers.set(\n entry.key,\n setTimeout(() => {\n timers.delete(entry.key);\n settle(entry);\n }, inputs.exitTimeout.value ?? DEFAULT_EXIT_TIMEOUT_MS)\n );\n };\n\n const apply = (children: UiChild | readonly UiChild[] | undefined): void => {\n const next = normalize(children);\n const wanted = new Set(next.map(child => child.key));\n // Anything still wanted is refreshed in place, so a child that came\n // back mid-exit keeps its node — and its scroll position, and its\n // component's state — rather than being rebuilt from nothing.\n const staying: PresenceEntry[] = [];\n for (const { key, child } of next) {\n const existing = entries.find(entry => entry.key === key) ?? held?.find(entry => entry.key === key);\n if (existing === undefined) {\n staying.push({ key, child, leaving: false, generation: 0 });\n continue;\n }\n if (existing.leaving) {\n clearTimer(key);\n existing.leaving = false;\n existing.generation++;\n }\n existing.child = child;\n staying.push(existing);\n }\n const hasExit = inputs.exit.value !== undefined;\n const leaving = hasExit ? entries.filter(entry => entry.leaving || !wanted.has(entry.key)) : [];\n for (const entry of leaving) {\n if (!entry.leaving) {\n entry.leaving = true;\n armTimeout(entry);\n }\n }\n for (const entry of entries) {\n if (!wanted.has(entry.key) && !leaving.includes(entry)) {\n clearTimer(entry.key);\n }\n }\n if (inputs.mode.value === 'wait' && leaving.length > 0) {\n entries = leaving;\n held = staying;\n } else {\n entries = [...leaving, ...staying];\n held = null;\n }\n emit();\n };\n\n const subscription = inputs.children.subscribe(children => apply(children));\n\n ctx.onUnmount(() => {\n disposed = true;\n subscription.unsubscribe();\n for (const key of timers.keys()) {\n clearTimer(key);\n }\n output.complete();\n });\n\n return Box(\n {\n // Positioned, because its children are: without this they would\n // resolve against whichever positioned ancestor happened to be\n // above, which is usually the whole screen.\n position: 'relative',\n width: inputs.width.value ?? percent(100),\n height: inputs.height.value ?? percent(100)\n },\n output as Observable<readonly UiChild[]>\n );\n}\n\n/** A child list flattened and keyed, with the empties dropped. */\nfunction normalize(children: UiChild | readonly UiChild[] | undefined): readonly { key: string; child: UiChild }[] {\n if (children === undefined || children === null) {\n return [];\n }\n const list = Array.isArray(children) ? (children as readonly UiChild[]) : [children as UiChild];\n const keyed: { key: string; child: UiChild }[] = [];\n for (const [index, child] of list.entries()) {\n if (child === undefined || child === null) {\n continue;\n }\n keyed.push({ key: keyOf(child, index), child });\n }\n return keyed;\n}\n\n/**\n * A child's identity: its own `key` when it has one, its position\n * otherwise.\n *\n * Both shapes are read because both appear here — a plain element\n * carries its key in `props`, and a component element carries it\n * beside them.\n */\nfunction keyOf(child: UiChild, index: number): string {\n const element = child as Partial<UiElement> & { key?: unknown };\n const declared = (element.props as { key?: unknown } | undefined)?.key ?? element.key;\n return declared === undefined || declared === null ? `#${index}` : String(declared);\n}\n","import { distinctUntilChanged, map, type Observable } from 'rxjs';\n\nimport type { MotionStateInput, MotionTiming, UiChild } from 'gesso-core';\nimport { Component } from '../Component';\nimport { createComponent } from '../createComponent';\nimport { Define, Inject, Input } from '../decorators';\nimport { input, InputCell } from '../Input';\nimport { Presence } from '../Presence';\nimport type { RouteDefinition } from './RouteDefinition';\nimport { RouterService, type RouteMatch } from './RouterService';\n\n/**\n * How one screen gives way to the next.\n *\n * Every field is optional and the default is nothing at all, so an\n * outlet with no `transition` swaps screens on the frame the url\n * changes, exactly as it did before this existed. A shared-element\n * morph needs no entry here: `sharedElement` pairs elements by name\n * across whatever tree change is happening, and a route change is one.\n *\n * **Do not combine `enter`/`exit` with shared elements.** A screen's\n * opacity multiplies onto everything inside it, morphing elements\n * included — so during a cross-fade the departing copy has already\n * handed over and the arriving one is at a fraction of its opacity,\n * and for those frames neither is on screen and whatever is behind\n * them shows through. It is very visible, and it is not a bug that can\n * be fixed here: a browser's View Transitions API only composes the\n * two because it lifts its named elements out of the page snapshot\n * into layers of their own, which is a whole architecture rather than\n * a flag.\n *\n * So they are alternatives. Either the screens cross-fade — right when\n * nothing is shared and the two are simply different — or the shared\n * elements carry the change, in which case they *are* the transition\n * and the screens should swap under them. `example-transitions` is the\n * second kind and passes no transition at all.\n */\nexport interface RouteTransition {\n /** Where the arriving screen starts. */\n enter?: MotionStateInput;\n /** Where the departing screen goes. */\n exit?: MotionStateInput;\n /**\n * `together` (the default) — the screens overlap while they cross.\n * Required for a shared element, which is measured off the departing\n * screen while it is still standing there.\n *\n * `wait` — the departing screen finishes leaving first.\n */\n mode?: 'together' | 'wait';\n timing?: MotionTiming;\n}\n\n/**\n * Where the current route's screen appears.\n *\n * This is `FRAMEWORK_DESIGN.md` §7.6 made real, and it is the one\n * component in the framework whose `render()` returns an Observable:\n * a route change is a structural change, not a change to a prop, and\n * observable render is the mechanism that exists for exactly that.\n *\n * Column(header(), createComponent(RouterOutlet))\n *\n * It cannot be the app root — a root definition may not be an\n * Observable, since the layout root needs a box — so it goes inside\n * whatever the root renders, which is where an app wants it anyway.\n *\n * **Nesting.** The outlet renders the whole matched chain, not just\n * the leaf: a route with a `parent` is built inside its parent's\n * screen, which receives it as an `outlet` prop (`OutletProps`) and\n * places it wherever it belongs. So there is one `RouterOutlet` in an\n * app; depth comes from the routes, not from where outlets are\n * scattered. That also means a layout screen is mounted once and stays\n * mounted while its children come and go — its sidebar keeps its\n * scroll position, and its state survives.\n *\n * A navigation that changes only params re-emits nothing at all: the\n * chain is the same objects, so the screens stay mounted and read the\n * new params from `router.observeParams(route)`.\n *\n * **Transitions.** Given a `transition`, the chain is rendered through\n * `Presence`, which keeps the departing screen mounted until its exit\n * animation is over. That is the only reason a screen transition needs\n * anything from the router at all — the animation itself is\n * `gesso-core`'s `motion`, and a shared-element morph across the\n * change is `sharedElement`, which the outlet knows nothing about.\n */\n@Define('gesso-router-outlet')\nexport class RouterOutlet extends Component {\n @Inject(RouterService) router!: RouterService;\n @Input() transition = input<RouteTransition | undefined>(undefined);\n\n override render(): UiChild {\n const chain = this.router.match.pipe(\n map(match => chainOf(match)),\n distinctUntilChanged(sameChain),\n map(routes => buildChain(routes))\n );\n return withTransition(chain, this.transition);\n }\n}\n\n/**\n * Wraps the chain in a `Presence` when a transition is asked for, and\n * hands it back untouched when one is not.\n *\n * Untouched rather than \"a Presence with no enter and no exit\",\n * because the two are not the same thing: `Presence` positions its\n * children absolutely inside a container that fills its parent, and an\n * app that never asked for a transition should not have its layout\n * changed by an outlet that decided to add one.\n *\n * The transition is read once, here, rather than followed. `render()`\n * runs exactly once per instance and this decides the *shape* of what\n * it returns; an outlet is configured when it is placed, the same way\n * an `Image`'s source is, and an outlet whose transition should change\n * gets a `key`.\n */\nfunction withTransition(\n chain: Observable<readonly UiChild[]>,\n transition: InputCell<RouteTransition | undefined>\n): UiChild {\n const settings = transition.value;\n if (settings === undefined) {\n return chain as Observable<UiChild | readonly UiChild[]> as unknown as UiChild;\n }\n return createComponent(Presence, {\n children: chain as unknown as UiChild | readonly UiChild[],\n enter: settings.enter,\n exit: settings.exit,\n mode: settings.mode,\n timing: settings.timing\n }) as unknown as UiChild;\n}\n\nfunction chainOf(match: RouteMatch | null): readonly RouteDefinition[] {\n return match === null ? [] : match.chain;\n}\n\n/**\n * Builds the chain from the inside out, each screen becoming the\n * `outlet` of the one above it.\n *\n * Keyed by path so reconciliation keeps a layout's host when only the\n * leaf below it changed: same component, same key, same slot, so the\n * host is reused and only its `outlet` input is pushed a new value.\n * The key is also what `Presence` identifies a departing screen by,\n * which is why a route change that keeps the same outermost route\n * — walking from one message to the next — is not a transition at all.\n */\nfunction buildChain(chain: readonly RouteDefinition[]): readonly UiChild[] {\n if (chain.length === 0) {\n return [];\n }\n // The leaf's own outlet: an empty list, which an observable child\n // reconciles to no children at all. It is what lets a screen place\n // `props.outlet` unconditionally, whether or not anything nests in\n // it on this url.\n let outlet = EMPTY_OUTLET;\n for (let index = chain.length - 1; index >= 0; index--) {\n const route = chain[index]!;\n outlet = createComponent(route.component as never, { outlet } as never, route.path);\n }\n return [outlet];\n}\n\nconst EMPTY_OUTLET = [] as unknown as UiChild;\n\nfunction sameChain(a: readonly RouteDefinition[], b: readonly RouteDefinition[]): boolean {\n return a.length === b.length && a.every((route, index) => route === b[index]);\n}\n","import type { UiChild } from 'gesso-core';\nimport type { ComponentType } from '../FunctionComponent';\nimport { parsePattern, type HasNoParams, type PatternSegment, type RouteParams } from './RoutePath';\n\n/**\n * The prop a screen receives when other routes nest inside it.\n *\n * A route that is another route's `parent` renders that child through\n * this prop: it is the outlet, and the screen places it wherever it\n * belongs — beside a sidebar, inside a card, under a header. A screen\n * nobody nests inside never receives one.\n *\n * function MailLayout(inputs: Inputs<OutletProps>) {\n * return Row(Sidebar(), Box({ flexGrow: 1 }, inputs.outlet));\n * }\n *\n * It is an `Inputs` cell like any other prop, so binding it as a child\n * is what makes the leaf swap without the layout being rebuilt.\n *\n * Never optional, so that placing it needs no ceremony: when no child\n * route is showing — `/mail` with nothing selected — the router feeds\n * the cell an empty list, and the outlet renders nothing.\n */\nexport interface OutletProps {\n readonly outlet: UiChild;\n}\n\n/** Where a navigation is going: a route and the params that fill it. */\nexport interface RouteTarget {\n readonly route: RouteDefinition;\n readonly params: Readonly<Record<string, string>>;\n readonly query: Readonly<Record<string, string>>;\n}\n\n/** What a guard is told about the navigation it is being asked to allow. */\nexport interface RouteContext<Path extends string = string> {\n readonly params: RouteParams<Path>;\n readonly query: Readonly<Record<string, string>>;\n /** The whole url being navigated to, path and query. */\n readonly url: string;\n}\n\n/**\n * A guard: an action that runs before its route is shown.\n *\n * Returning `true` lets the navigation through, `false` cancels it and\n * leaves the current url alone, and a `RouteTarget` — built with\n * `to(route, params)` — redirects. Guards are synchronous by design.\n * An asynchronous guard has to leave the app somewhere while it waits,\n * and \"somewhere\" is a screen, which means the waiting belongs to a\n * route rather than to the router: navigate to a loading route and\n * navigate on from it.\n */\nexport type RouteGuard<Path extends string = string> = (context: RouteContext<Path>) => boolean | RouteTarget;\n\n/**\n * One route: a full path, the screen that renders it, and optionally a\n * parent it nests inside.\n *\n * Paths are full, not relative. A relative fragment would know only\n * its own segments, and the params a screen actually receives are its\n * parents' as well — so `RouteParams` could not be honest about them.\n * Nesting is declared by pointing at the parent route object instead\n * of by position, which also means there are no route ids to keep\n * unique and no string to misspell.\n */\nexport interface RouteDefinition<Path extends string = string> {\n readonly path: Path;\n readonly component: ComponentType;\n readonly parent?: RouteDefinition;\n readonly guard?: RouteGuard<Path>;\n /** Parsed once, here, so matching a url never parses a pattern. */\n readonly segments: readonly PatternSegment[];\n /** Every ancestor and then this route: the chain the outlets render. */\n readonly chain: readonly RouteDefinition[];\n}\n\nexport interface RouteOptions<Path extends string> {\n readonly path: Path;\n readonly component: ComponentType;\n /** The route this one renders inside; its screen receives an `outlet`. */\n readonly parent?: RouteDefinition;\n readonly guard?: RouteGuard<Path>;\n}\n\n/**\n * Declares a route.\n *\n * The path is inferred as a literal type, which is where typed params\n * come from: `route({ path: '/mail/:id', ... })` produces a\n * `RouteDefinition<'/mail/:id'>`, and everything downstream — the\n * guard's context, `router.go`, `router.params` — reads `{ id: string }`\n * off it.\n */\nexport function route<const Path extends string>(options: RouteOptions<Path>): RouteDefinition<Path> {\n const parent = options.parent;\n const segments = parsePattern(options.path);\n if (parent !== undefined && !extendsParent(parent.segments, segments)) {\n throw new Error(\n `Route '${options.path}' declares '${parent.path}' as its parent but does not extend it. ` +\n \"A nested route renders inside its parent, so its path must begin with the parent's segments.\"\n );\n }\n const chain: RouteDefinition[] = parent === undefined ? [] : [...parent.chain];\n const definition: RouteDefinition<Path> = {\n path: options.path,\n component: options.component,\n parent,\n guard: options.guard,\n segments,\n chain\n };\n chain.push(definition);\n return definition;\n}\n\n/**\n * A navigation target: this route, with these params.\n *\n * The params argument is typed from the route's path, so a redirect to\n * a route that needs an id cannot forget it. A route with no params\n * takes no second argument.\n */\nexport function to<Path extends string>(route: RouteDefinition<Path>, ...args: ToArgs<Path>): RouteTarget {\n const [params, options] = args as unknown as [Readonly<Record<string, string>>?, ToOptions?];\n return {\n route,\n params: params ?? {},\n query: options?.query ?? {}\n };\n}\n\ninterface ToOptions {\n readonly query?: Readonly<Record<string, string>>;\n}\n\n/**\n * What `to` takes after the route, on the same reasoning as `GoArgs`:\n * a route with no params must not silently accept some.\n */\ntype ToArgs<Path extends string> =\n HasNoParams<Path> extends true ? [] : [params: RouteParams<Path>, options?: ToOptions];\n\n/**\n * Whether `child` begins with `parent`, segment by segment.\n *\n * Segment-wise rather than by string prefix, so `/mailbox` is not\n * mistaken for a child of `/mail`.\n */\nfunction extendsParent(parent: readonly PatternSegment[], child: readonly PatternSegment[]): boolean {\n if (child.length < parent.length) {\n return false;\n }\n return parent.every((segment, index) => {\n const other = child[index]!;\n if (segment.kind !== other.kind) {\n return false;\n }\n if (segment.kind === 'static' && other.kind === 'static') {\n return segment.text === other.text;\n }\n if (segment.kind === 'param' && other.kind === 'param') {\n return segment.name === other.name;\n }\n return true;\n });\n}\n","import type { FrameworkChild } from '../../ComponentElement';\nimport { createComponent } from '../../createComponent';\nimport type { ComponentType } from '../../FunctionComponent';\nimport { APPLICATION_WORKER, portHandle, type WorkerHandle } from '../../worker/WorkerPorts';\nimport { captureConsole } from '../../worker/captureConsole';\nimport {\n createChannelRegistry,\n type ChannelRegistration,\n type ChannelRegistryHandle\n} from '../../channel/createChannelRegistry';\nimport type { ChannelSource } from '../../channel/provide';\nimport type { ChannelToken } from '../../channel/ChannelToken';\nimport { UiHostFrameClock } from 'gesso-core';\nimport { GessoRuntime, type RendererChoice } from '../GessoRuntime';\nimport { ServiceRegistry } from '../../service/ServiceRegistry';\nimport type { MediaOptions } from '../MediaService';\nimport type { FontFamilyDeclaration } from '../FontService';\nimport type { RouterRoutes } from '../../router/RouterService';\nimport { isInputMessage, type RuntimeToShellMessage, type ShellToRuntimeMessage } from './RenderWorkerProtocol';\n\n/**\n * An `error` event as this module needs it: the thrown value when the\n * engine kept it, and the location when it did not.\n */\ninterface WorkerErrorEvent {\n message?: string;\n error?: unknown;\n filename?: string;\n lineno?: number;\n colno?: number;\n}\n\n/** An `unhandledrejection` event, reduced to the value that was rejected. */\ninterface WorkerRejectionEvent {\n reason?: unknown;\n}\n\n/**\n * Minimal view of the worker global, so this module type-checks\n * against the DOM lib without pulling in the WebWorker lib.\n *\n * `addEventListener` is required rather than optional, because a host\n * without it is a host whose uncaught exceptions vanish, and that is\n * the failure this whole file exists to prevent. A test double states\n * how it wants to be told instead of quietly not being told.\n */\ninterface WorkerGlobal {\n onmessage: ((event: MessageEvent<ShellToRuntimeMessage>) => void) | null;\n postMessage(message: RuntimeToShellMessage): void;\n addEventListener(type: 'error', listener: (event: WorkerErrorEvent) => void): void;\n addEventListener(type: 'unhandledrejection', listener: (event: WorkerRejectionEvent) => void): void;\n}\n\n/**\n * Runs a Gesso application inside a render worker.\n *\n * Everything the user sees is built and drawn here: components,\n * the retained graph, layout, input routing, and rasterization to an\n * OffscreenCanvas. The main thread only forwards events and never\n * touches any of it, which is the entire point of the arrangement.\n *\n * Usage, in a module loaded as a worker:\n *\n * renderRoot(AppRoot).useChannel(Catalog);\n *\n * The message handler is installed synchronously, so chained\n * useChannel() calls always land before the shell's init message is\n * processed.\n */\nexport function renderRoot(root: FrameworkChild | ComponentType): RenderWorkerApp {\n return new RenderWorkerApp(root);\n}\n\nexport class RenderWorkerApp {\n private readonly channelRegistrations: ChannelRegistration[] = [];\n private readonly serviceRegistrations: (new () => object)[] = [];\n private routes: RouterRoutes | undefined;\n private media: MediaOptions | undefined;\n private fonts: readonly FontFamilyDeclaration[] | undefined;\n private root: FrameworkChild;\n private readonly host: WorkerGlobal;\n\n private runtime: GessoRuntime | undefined;\n /** Held so forwarded display refreshes can be handed to it. */\n private clock: UiHostFrameClock | undefined;\n private channels: ChannelRegistryHandle | undefined;\n /** The shell's port to the application-logic worker, if there is one. */\n private appLogicWorker: WorkerHandle | undefined;\n /** Undoes `captureConsole` while a devtools panel has the console forwarded. */\n private restoreConsole: (() => void) | null = null;\n\n constructor(root: FrameworkChild | ComponentType, host: WorkerGlobal = self as unknown as WorkerGlobal) {\n this.root = typeof root === 'function' ? createComponent(root as ComponentType) : root;\n this.host = host;\n this.host.onmessage = event => this.receive(event.data);\n // Everything `receive` cannot see. The usual frame is not in that\n // set: the shell forwards its `requestAnimationFrame` as a `tick`\n // message and the clock delivers it synchronously, so a component\n // that throws while rendering, laying out or painting throws\n // inside `receive`'s own try and is reported as `message`. What\n // lands here is the rest: module scope, a callback no shell\n // message drove, and the frames the clock paces from its own\n // timer, before the first tick arrives or while the shell's thread\n // is blocked. Their only witness is the worker's own console,\n // which a page cannot read and a person only finds by opening the\n // right thread in devtools. These two listeners are what make a\n // render worker's failures reach the shell at all.\n this.host.addEventListener('error', event => {\n // The location only when the engine kept no Error: with one, the\n // stack says where it was in more detail and the event's\n // `filename` is whichever bundle chunk the frame landed in,\n // which is not where anybody wrote anything.\n const error = event.error;\n this.reportUncaught(\n error ?? event.message ?? 'Unknown error',\n error === undefined ? locationOf(event) : undefined\n );\n });\n this.host.addEventListener('unhandledrejection', event => {\n this.reportUncaught(event.reason ?? 'Unhandled rejection');\n });\n }\n\n /**\n * Reports a value nothing caught.\n *\n * `where` is the fallback location the `error` event carries when\n * the engine did not keep the thrown object — a cross-origin script,\n * or a value thrown that was never an Error. Without it the report\n * would be a bare sentence with nothing to look up.\n */\n private reportUncaught(value: unknown, where?: string): void {\n const error = value instanceof Error ? value : undefined;\n const message = error !== undefined ? error.message : String(value);\n this.host.postMessage({\n type: 'error',\n message: where === undefined ? message : `${message} (${where})`,\n stack: error?.stack,\n source: 'uncaught'\n });\n }\n\n /**\n * Replaces the application's root and rebuilds its tree, for hot\n * module replacement.\n *\n * The framework knows nothing about any bundler. An entry module\n * that wants this asks its own HMR client for the new module and\n * hands the root over:\n *\n * const app = renderRoot(AppRoot).useService(Counter);\n * import.meta.hot?.accept('./AppRoot', module => {\n * app.reload(module.AppRoot, [module.Counter]);\n * });\n *\n * The services are the ones the replaced module defines. They have\n * to be named because a registry is keyed by the class object and a\n * replaced module produces a new one; the registry adopts them,\n * keeping their instances. Omit a service that lives in a module the\n * replacement did not touch.\n *\n * Everything that is not the tree survives, including the channels:\n * see `GessoRuntime.reload`. Called before the shell's `init`\n * message it simply changes which root will be built.\n */\n reload(root: FrameworkChild | ComponentType, services: readonly (new () => object)[] = []): void {\n this.root = typeof root === 'function' ? createComponent(root as ComponentType) : root;\n for (const ServiceClass of services) {\n // Kept for a later reload too: a second replacement is matched\n // against what the first one left, not against the original.\n const previous = this.serviceRegistrations.findIndex(existing => existing.name === ServiceClass.name);\n if (previous === -1) {\n this.serviceRegistrations.push(ServiceClass);\n } else {\n this.serviceRegistrations[previous] = ServiceClass;\n }\n }\n this.runtime?.reload(this.root, services);\n }\n\n /**\n * Registers a channel.\n *\n * With `worker`, the channel's data lives there — api, store, domain\n * and view models, all plain code the framework never sees. With\n * `source`, it is fed from this thread; either way the same patches\n * cross the same kind of port, so a channel can be moved into a\n * worker later without a view noticing.\n */\n useChannel<V extends object, C extends object>(\n token: ChannelToken<V, C>,\n options: { worker?: WorkerHandle | (() => Worker); source?: ChannelSource<V, C> } = {}\n ): this {\n if (this.runtime !== undefined) {\n throw new Error(`Channel '${token.name}' was registered after the runtime started.`);\n }\n this.channelRegistrations.push({\n token: token as unknown as ChannelToken<never, never>,\n worker: options.worker,\n source: options.source as unknown as ChannelSource<never, never>\n });\n return this;\n }\n\n /**\n * Registers a runtime service: a plain class this thread constructs\n * once and hands to whoever injects it.\n *\n * For things that belong to the render thread and could not leave it\n * — something holding a `UiNode`, a decoded bitmap, or a generator\n * feeding bound props at frame rate. Application state goes through\n * `useChannel` instead, and the test is the usual one: if it\n * survives a reload or another screen cares about it, it is not a\n * service.\n */\n useService(ServiceClass: new () => object): this {\n this.serviceRegistrations.push(ServiceClass);\n return this;\n }\n\n /**\n * Declares the app's routes, which is all it takes to make a\n * `RouterOutlet` in the tree resolve them.\n *\n * renderRoot(AppRoot).useRoutes({ routes: ROUTES, notFound: NotFound });\n *\n * They are declared here, in the render worker, because a route\n * holds a component class. The shell never sees one; the only thing\n * that crosses is the url.\n */\n useRoutes(routes: RouterRoutes): this {\n if (this.runtime !== undefined) {\n throw new Error('Routes were registered after the runtime started.');\n }\n this.routes = routes;\n return this;\n }\n\n /**\n * Declares where the app's pictures come from: the image resolver,\n * the icon rasteriser and the video decoder.\n *\n * renderRoot(AppRoot).useMedia({ resolver: new CachingResolver() });\n *\n * Declared in the worker, like the routes and for a related reason:\n * a resolver is a function, and no function crosses a `postMessage`.\n * The shell could not forward one it was given, so the thread that\n * will do the fetching is where it is built. That is also the right\n * thread for it: this one already has no main thread to block.\n *\n * Declared before `init` rather than set on `MediaService` later,\n * because the tree is built when `init` arrives and an `Image` in it\n * asks for its bitmap at that moment. Whatever is left out, the\n * runtime builds and owns; whatever is passed stays the caller's,\n * and the runtime will not dispose it.\n */\n useMedia(media: MediaOptions): this {\n if (this.runtime !== undefined) {\n throw new Error('A media resolver was registered after the runtime started.');\n }\n this.media = media;\n return this;\n }\n\n /**\n * Declares the font families the app's text may name, with their\n * faces and fallback stacks.\n *\n * renderRoot(AppRoot).useFonts([\n * {\n * family: 'Inter',\n * faces: [{ source: new URL('./Inter.woff2', import.meta.url).href, weight: '100 900' }],\n * fallback: ['system-ui', 'sans-serif']\n * }\n * ]);\n *\n * Declared in the worker because that is the thread whose font set\n * the canvas draws from: a font the page loaded is not in it. The\n * faces are fetched here, text draws in the fallback until each\n * arrives, and the tree is laid out again when it does. See\n * `FontService`.\n */\n useFonts(families: readonly FontFamilyDeclaration[]): this {\n if (this.runtime !== undefined) {\n throw new Error('Fonts were declared after the runtime started.');\n }\n this.fonts = families;\n return this;\n }\n\n /**\n * Handles one message from the shell.\n *\n * Exceptions are reported to the shell rather than left to vanish:\n * an uncaught throw inside a worker is invisible to the page, which\n * is the worst failure mode this architecture introduces.\n */\n receive(message: ShellToRuntimeMessage): void {\n try {\n this.dispatch(message);\n } catch (error) {\n this.host.postMessage({\n type: 'error',\n message: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n source: 'message'\n });\n }\n }\n\n /**\n * Copies this worker's `console.*` to the shell while a devtools\n * panel asks for it. Here rather than in the runtime because the\n * console is the worker global's, and this class is what owns the\n * global.\n */\n private setConsoleForwarding(enabled: boolean): void {\n this.restoreConsole?.();\n this.restoreConsole = null;\n if (!enabled) {\n return;\n }\n this.restoreConsole = captureConsole(entry => {\n this.host.postMessage({ type: 'devtools', event: { kind: 'console', entry: { ...entry, thread: 'render' } } });\n });\n }\n\n private dispatch(message: ShellToRuntimeMessage): void {\n if (message.type === 'init') {\n // The shell's channel to the application-logic worker, when it spawned\n // one. Held before initialize, because the channels registered\n // without a worker of their own are opened over it there.\n this.appLogicWorker = message.appPort === undefined ? undefined : portHandle(message.appPort);\n this.initialize(\n message.canvas,\n message.width,\n message.height,\n message.dpr,\n message.renderer,\n message.accessibility !== false\n );\n this.runtime!.setTextInputSource(message.textInput ?? 'keys');\n return;\n }\n\n if (message.type === 'resize') {\n // Answered ahead of the runtime guard, and answered from a\n // `finally`, because the shell's resize backpressure is held\n // open by this one message: it keeps a single resize in flight\n // and sits on the latest size it has not sent until the worker\n // says the last one has drained. An unacknowledged resize is\n // therefore not a late frame but a canvas that stays the wrong\n // size for good, so every resize is acknowledged — the one that\n // outran `init` and reached no tree, the zero-sized one the\n // runtime ignores by design, and the one whose layout threw,\n // which `receive` reports separately and which is no reason to\n // strand the shell. `RuntimeToShellMessage` says why the\n // dimensions ride along.\n //\n // It promises only that the layout for that size has been done.\n // It is not a paint confirmation: nothing here can vouch for\n // what the compositor has actually shown, and the queue this\n // exists to bound is a queue of layouts.\n try {\n this.runtime?.resize(message.width, message.height, message.dpr);\n } finally {\n this.host.postMessage({\n type: 'resized',\n width: message.width,\n height: message.height,\n dpr: message.dpr\n });\n }\n return;\n }\n\n const runtime = this.runtime;\n if (runtime === undefined) {\n // Events can outrun init; dropping them is correct, since there\n // is no tree yet for them to reach.\n return;\n }\n\n switch (message.type) {\n case 'pointerDown':\n runtime.input.pointer.pointerDown(message.x, message.y, message.buttons, message.modifiers, message.pointer);\n break;\n case 'pointerMove':\n runtime.input.pointer.pointerMove(message.x, message.y, message.buttons, message.modifiers, message.pointer);\n break;\n case 'pointerUp':\n runtime.input.pointer.pointerUp(message.x, message.y, message.buttons, message.modifiers, message.pointer);\n break;\n case 'pointerCancel':\n runtime.input.pointer.pointerCancel(message.pointer);\n break;\n case 'wheel':\n runtime.input.wheel.wheel(\n message.x,\n message.y,\n message.deltaX,\n message.deltaY,\n message.modifiers,\n message.deltaMode,\n message.wheelDeltaY\n );\n break;\n case 'keyDown':\n runtime.input.keyboard.keyDown(message.key, message.modifiers);\n break;\n case 'keyUp':\n runtime.input.keyboard.keyUp(message.key, message.modifiers);\n break;\n case 'beforeInput':\n runtime.input.editing.beforeInput(message.inputType, message.data);\n break;\n case 'compositionStart':\n runtime.input.editing.compositionStart();\n break;\n case 'compositionUpdate':\n runtime.input.editing.compositionUpdate(message.text, message.caret);\n break;\n case 'compositionEnd':\n runtime.input.editing.compositionEnd(message.text);\n break;\n case 'paste':\n runtime.input.editing.paste(message.text);\n break;\n case 'blur':\n runtime.input.focus.blur();\n break;\n case 'reducedMotion':\n runtime.setReducedMotion(message.reduced);\n break;\n case 'colorScheme':\n runtime.setColorScheme(message.scheme);\n break;\n case 'viewportInsets':\n runtime.setViewportInsets(message.insets);\n break;\n case 'url':\n runtime.setUrl(message.url);\n break;\n case 'popupResult':\n runtime.settlePopup(message.id, message.opened);\n break;\n case 'storageResult':\n runtime.settleStorage(message.id, message.result);\n break;\n case 'tick':\n this.clock?.tick(message.time);\n break;\n case 'fullscreenChanged':\n runtime.setFullscreen(message.active);\n return;\n case 'visibility':\n runtime.setVisible(message.visible);\n break;\n case 'inspector':\n runtime.setInspectorEnabled(message.enabled);\n break;\n case 'devtools':\n if (message.request.kind === 'console') {\n this.setConsoleForwarding(message.request.enabled);\n } else {\n runtime.handleDevtools(message.request);\n }\n break;\n case 'audioSample':\n runtime.applyAudioSample(message.sample);\n break;\n case 'audioAction':\n runtime.applyAudioAction(message.action);\n break;\n case 'semanticsAction':\n runtime.applySemanticsAction(message.action);\n break;\n case 'dispose':\n this.setConsoleForwarding(false);\n runtime.dispose();\n this.channels?.dispose();\n this.channels = undefined;\n this.runtime = undefined;\n break;\n }\n\n // After routing, not before: whether a frame is now pending is how\n // the runtime tells an input that caused work from one that hit\n // nothing. `dispose` cannot reach here with a live runtime, and no\n // lifecycle message is an input, so the guard is enough.\n if (this.runtime !== undefined && isInputMessage(message)) {\n this.runtime.noteInput(message.at);\n }\n }\n\n /**\n * Swaps the `APPLICATION_WORKER` placeholder for the shell's port.\n *\n * Registrations run before `init`, so a registration naming the\n * application worker can only name a stand-in; this is where it\n * becomes real. Anything else is left exactly as registered.\n */\n private resolveWorker<T extends { worker?: WorkerHandle | (() => Worker) }>(registration: T): T {\n if (registration.worker !== APPLICATION_WORKER) {\n return registration;\n }\n return { ...registration, worker: this.appLogicWorker };\n }\n\n private initialize(\n canvas: OffscreenCanvas,\n width: number,\n height: number,\n dpr: number,\n renderer: RendererChoice | undefined,\n accessibility: boolean\n ): void {\n this.runtime?.dispose();\n this.channels?.dispose();\n this.channels = createChannelRegistry(\n // A channel registered with neither a worker nor a source is\n // served by whatever the shell spawned. Naming no worker is the\n // common case: an application has one application-logic worker, and\n // repeating that at every registration says nothing.\n this.channelRegistrations.map(registration =>\n registration.worker === undefined && registration.source === undefined\n ? { ...registration, worker: this.appLogicWorker }\n : this.resolveWorker(registration)\n ),\n (channelName, message, stack) => {\n this.host.postMessage({\n type: 'error',\n message: `channel ${channelName}: ${message}`,\n stack,\n source: 'channel'\n });\n }\n );\n const services = new ServiceRegistry();\n for (const ServiceClass of this.serviceRegistrations) {\n services.register(ServiceClass);\n }\n this.runtime = new GessoRuntime({\n root: this.root,\n services,\n routes: this.routes,\n media: this.media,\n fonts: this.fonts,\n canvas,\n renderer,\n channels: this.channels.registry,\n // A worker has no requestAnimationFrame tied to the compositor,\n // so the shell forwards the display's own refresh and this clock\n // just delivers it. See FRAMEWORK_DESIGN section 13.\n clock: callback => {\n const clock = new UiHostFrameClock(callback, running => {\n this.host.postMessage({ type: 'frameLoop', running });\n });\n this.clock = clock;\n return clock;\n },\n width,\n height,\n dpr\n });\n this.runtime.deferPatchesFrom(this.channels.registry.all());\n this.runtime.onInspect(report => {\n this.host.postMessage({ type: 'inspect', report });\n });\n this.runtime.onDevtools(event => {\n this.host.postMessage({ type: 'devtools', event });\n });\n this.runtime.onCursor(cursor => {\n this.host.postMessage({ type: 'cursor', cursor });\n });\n this.runtime.onScrollability((scrollability, scrollsAnything) => {\n this.host.postMessage({ type: 'scrollability', scrollability, scrollsAnything });\n });\n this.runtime.onEditingState(state => {\n this.host.postMessage({ type: 'editing', state });\n });\n if (accessibility) {\n // Subscribing is what turns the geometry sweep on in the\n // runtime, so a shell without a mirror pays nothing for one.\n this.runtime.onSemantics(update => {\n this.host.postMessage({ type: 'semantics', update });\n });\n }\n this.runtime.onAudioRequest(request => {\n this.host.postMessage({ type: 'audio', request });\n });\n this.runtime.onShellRequest(request => {\n if (request.type === 'clipboard') {\n this.host.postMessage({ type: 'clipboard', text: request.text });\n } else if (request.type === 'openUrl') {\n this.host.postMessage({ type: 'openUrl', url: request.url });\n } else if (request.type === 'fullscreen') {\n this.host.postMessage({ type: 'fullscreen', enter: request.enter });\n } else if (request.type === 'popup') {\n this.host.postMessage({\n type: 'popup',\n id: request.id,\n url: request.url,\n name: request.name,\n width: request.width,\n height: request.height\n });\n } else if (request.type === 'storage') {\n this.host.postMessage({\n type: 'storage',\n id: request.id,\n op: request.op,\n key: request.key,\n ...(request.value === undefined ? {} : { value: request.value })\n });\n } else {\n this.host.postMessage({ type: 'history', action: request.action, url: request.url });\n }\n });\n this.runtime.onRendererError(message => {\n this.host.postMessage({ type: 'error', message, source: 'renderer' });\n });\n this.runtime.onListenerError((message, stack) => {\n this.host.postMessage({ type: 'error', message, stack, source: 'listener' });\n });\n this.runtime.onFrame(metrics => {\n this.host.postMessage({\n type: 'frame',\n frame: metrics.frame,\n durationMs: metrics.durationMs,\n nodes: metrics.nodes,\n measured: metrics.measured,\n relayoutRoots: metrics.relayoutRoots,\n at: metrics.at,\n inputLatencyMs: metrics.inputLatencyMs,\n phases: metrics.phases,\n renderer: metrics.renderer,\n gpu: metrics.gpu\n });\n });\n this.runtime.start();\n this.host.postMessage({ type: 'ready' });\n }\n}\n\n/** `file:line:column` from an error event that carried no Error. */\nfunction locationOf(event: WorkerErrorEvent): string | undefined {\n if (event.filename === undefined || event.filename === '') {\n return undefined;\n }\n const line = event.lineno ?? 0;\n const column = event.colno ?? 0;\n return `${event.filename}:${line}:${column}`;\n}\n"],"mappings":"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"}