gesso-framework 0.4.2 → 0.5.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/CHANGELOG.md +106 -0
- package/README.md +2 -0
- package/dist/ChannelProtocol-ByNoHujM.d.ts +247 -0
- package/dist/{FunctionComponent-CgwLKE5d.d.ts → FunctionComponent-fYMdePtH.d.ts} +3 -247
- package/dist/agent/index.d.ts +317 -0
- package/dist/agent/index.js +184 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/app-BvlIO1G9.js +326 -0
- package/dist/app-BvlIO1G9.js.map +1 -0
- package/dist/{index-C9FAI_Kt.d.ts → index-BDM_gzzZ.d.ts} +377 -268
- package/dist/index.d.ts +5 -3
- package/dist/index.js +971 -89
- package/dist/index.js.map +1 -1
- package/dist/jsx/jsx-runtime.d.ts +1 -1
- package/dist/{persisted-CsTPnjkc.js → persisted-pix1fS1D.js} +6 -1
- package/dist/{persisted-CsTPnjkc.js.map → persisted-pix1fS1D.js.map} +1 -1
- package/dist/remote-xxij8zXe.js +600 -0
- package/dist/remote-xxij8zXe.js.map +1 -0
- package/dist/rolldown-runtime-D7D4PA-g.js +13 -0
- package/dist/ui-U39HjNFA.d.ts +517 -0
- package/dist/webmcp-CJVFvAHe.js +77 -0
- package/dist/webmcp-CJVFvAHe.js.map +1 -0
- package/dist/worker/index.d.ts +4 -2
- package/dist/worker/index.js +1 -1
- package/package.json +12 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["measure","normalize","measure"],"sources":["../src/Component.ts","../src/service/ServiceRegistry.ts","../src/fanOut.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/worker/RenderWorkerProtocol.ts","../src/app/AudioSink.ts","../src/app/fileDrop.ts","../src/app/ShellService.ts","../src/app/shellFiles.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/fullscreen.ts","../src/app/worker/WorkerApp.ts","../src/app/createApp.ts","../src/app/DevtoolsProtocol.ts","../src/app/AudioService.ts","../src/router/RouteState.ts","../src/router/RoutePath.ts","../src/router/RouterService.ts","../src/app/FindService.ts","../src/app/EditingService.ts","../src/app/FocusService.ts","../src/app/MediaService.ts","../src/app/FontService.ts","../src/app/TextService.ts","../src/app/AnimationService.ts","../src/app/FrameService.ts","../src/app/InputLatency.ts","../src/app/SmoothScroller.ts","../src/app/GessoRuntime.ts","../src/app/GessoApp.ts","../src/app/GessoAppBuilder.ts","../src/app/createSyncApp.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 { BehaviorSubject, Observable, Subscription } from 'rxjs';\n\nimport { equalityOf, type Equality } from './derive';\nimport { trackRead, type ReadableCell } from './Input';\n\n/**\n * How many live keys with nothing ever released counts as a mistake.\n *\n * Generous: a grid's first window is hundreds of cells before a scroll\n * retires any of them, and a warning that fired there would be noise.\n */\nconst GROWING_WITHOUT_RELEASE = 2048;\n\n/** What a key is. A cell in a grid names itself `${row}:${column}`. */\nexport type FanKey = string | number;\n\n/**\n * What a key stands for, carried beside it.\n *\n * A key has to be a string or a number, because the registry looks one\n * up in a `Map` and identity is the wrong test for `${row}:${column}`.\n * That leaves a reader with a compound key parsing it back out on\n * every read, which is precisely what a grid cannot afford: gessosheet\n * keeps a cell's row and column beside its subjects for exactly this\n * reason, and its comment says so — \"parsing them back out of the key\n * was the only reason the key had a shape\".\n *\n * So the caller hands `for` whatever the key means, the registry\n * remembers it, and the reader is given it. `undefined` for a caller\n * whose key is already the whole answer.\n */\n\nexport interface FanOutOptions<S, V, D> {\n /**\n * What a key's cell holds before the source has said anything.\n *\n * Required, and not `undefined` by default, because the surfaces\n * this exists for mount their cells before the first patch arrives:\n * a grid that fills in as the window is served needs \"a cell waiting\"\n * to be a value it can draw rather than a hole. A function is given\n * the key, for a default that varies by position.\n */\n readonly initial: V | ((key: FanKey, datum: D) => V);\n /**\n * Which keys an emission could have changed, when the source knows.\n *\n * Without it every emission reads every live key and pushes the ones\n * that differ, which is correct and is what the natural spelling\n * costs too — so on its own the registry buys a cheaper mount and a\n * cheaper teardown and little else. This is where the frame goes.\n * A patch that touched one cell of ten thousand should cost one\n * read, and a source that already knows what it changed is the only\n * thing that can say so.\n *\n * Return `undefined` for \"I do not know\", which walks them all: a\n * scroll that replaces the window is honestly that case, and a\n * source that guessed narrow there would leave stale cells on the\n * screen. Keys that are not live are skipped, so the set may be as\n * loose as the source finds convenient.\n */\n readonly changed?: (next: S, previous: S | undefined) => Iterable<FanKey> | undefined;\n /**\n * How one key's new value is judged unchanged; `reference` by\n * default.\n *\n * The opposite default to `select` and `derive`, and the reason is\n * what this is for. Those project one value and run once per\n * emission, so comparing by content is cheap and usually right.\n * This runs the comparison once per live key per emission, on a\n * surface with thousands of them, which is the cost the registry\n * exists to remove — measured at 1.9 ms against 0.8 ms for ten\n * thousand keys on a source that republishes the lot, which is what\n * a scroll does.\n *\n * Pass `structural` when the reader *builds* a value rather than\n * passing one through: a projection that returns a fresh array or\n * object of the same shape is not a change, and a reference test\n * would push one to every cell on every emission. A reader that can\n * hand back the same object for an unchanged key — the sentinel\n * trick, one `EMPTY` for every cell with nothing in it — keeps the\n * cheap test and gets the right answer.\n */\n readonly equal?: Equality<V>;\n /** What to call these cells in a warning. */\n readonly label?: string;\n}\n\n/**\n * One key's value, as a cell.\n *\n * It is a cell and nothing else: bound to a prop it is an Observable\n * like any other, read with `.value` it is the current value, and it\n * composes inside a `computed`. What is different is where its value\n * comes from — it is *pushed* by the registry that made it rather than\n * pulled through a pipeline of its own, and that is the whole point.\n */\nexport class FanCell<V, D = unknown> extends Observable<V> implements ReadableCell<V> {\n /** @internal */\n readonly subject: BehaviorSubject<V>;\n\n constructor(\n initial: V,\n readonly key: FanKey,\n readonly datum: D,\n readonly label: string | undefined\n ) {\n const subject = new BehaviorSubject(initial);\n super(subscriber => subject.subscribe(subscriber));\n this.subject = subject;\n }\n\n get value(): V {\n trackRead(this);\n return this.subject.value;\n }\n}\n\n/**\n * Many cells from one source, with one subscription between them.\n *\n * `fanOut` is the answer to a shape that every large surface arrives\n * at and that `select`, `derive` and `computed` all get wrong: N\n * things on screen, each reading its own slice of one upstream value.\n *\n * const cells = fanOut(sheet.view.cells, (cells, key) => cells[key] ?? null);\n *\n * const cell = cells.for('12:4'); // a stable cell, made on demand\n * Text({ text: cell }); // bound like anything else\n * cells.release('12:4'); // when it leaves the screen\n *\n * **Why not a pipe per slice.** `source.pipe(map(v => v[key]))` per\n * cell is the natural spelling and it does not scale, in two ways that\n * compound. Every emission runs N pipelines, whatever changed; and\n * RxJS removes a subscriber from a Subject by scanning its observer\n * list, so tearing down a window of N cells is quadratic. Gessosheet\n * measured the first at 0.2 ms of median frame and five milliseconds\n * of input latency for one such binding per cell, and learned it three\n * separate times before writing the fan-out by hand.\n *\n * **What this does instead.** One subscription on the source for the\n * whole registry. Each emission walks the live keys — not the source's\n * keys, of which there may be millions — reads each one, and pushes\n * only the ones that changed. Each cell then has one or two observers\n * of its own, so its own teardown is the constant-time case that RxJS\n * is good at. Releasing a key is a map delete.\n *\n * **A registry is a resource.** It holds a key until `release`, and\n * the source until `close` or the last key goes. That is deliberate:\n * the surfaces that need this are the ones that mount and unmount\n * deliberately, and a cell that vanished when its last binding did\n * would be rebuilt on every scroll frame by exactly the code that\n * cannot afford it.\n */\nexport function fanOut<S, V, D = undefined>(\n source: Observable<S>,\n read: (snapshot: S, key: FanKey, datum: D) => V,\n options: FanOutOptions<S, V, D>\n): FanOut<S, V, D> {\n return new FanOut(source, read, options);\n}\n\n/** What `fanOut` returns. See there. */\nexport class FanOut<S, V, D = undefined> {\n private readonly cells = new Map<FanKey, FanCell<V, D>>();\n private readonly equal: (a: V, b: V) => boolean;\n private readonly initial: (key: FanKey, datum: D) => V;\n private readonly changed: ((next: S, previous: S | undefined) => Iterable<FanKey> | undefined) | undefined;\n private readonly label: string | undefined;\n /**\n * How many keys have ever been let go.\n *\n * The leak signature is not \"this registry is big\" — a grid holds\n * ten thousand cells and is right to. It is \"this registry has never\n * let go of anything\", which is what forgetting `release` looks like\n * from in here, and what a registry that mounts and unmounts\n * correctly can never look like once it has scrolled once.\n */\n private releases = 0;\n private warnedGrowing = false;\n private upstream: Subscription | null = null;\n private snapshot: S | undefined;\n private hasSnapshot = false;\n private closed = false;\n\n constructor(\n private readonly source: Observable<S>,\n private readonly read: (snapshot: S, key: FanKey, datum: D) => V,\n options: FanOutOptions<S, V, D>\n ) {\n this.equal = equalityOf(options.equal ?? 'reference');\n const initial = options.initial;\n this.initial = typeof initial === 'function' ? (initial as (key: FanKey, datum: D) => V) : () => initial;\n this.changed = options.changed;\n this.label = options.label;\n }\n\n /** How many keys are live. */\n get size(): number {\n return this.cells.size;\n }\n\n /**\n * The cell for one key, made on first ask and the same object after.\n *\n * The identity is the contract: a surface that memoises its elements\n * by key binds this once and is never re-bound, which is the reason\n * the registry hands out cells rather than values.\n */\n for(key: FanKey, datum: D = undefined as D): FanCell<V, D> {\n const existing = this.cells.get(key);\n if (existing !== undefined) {\n return existing;\n }\n if (this.closed) {\n throw new Error(\n `fanOut(${this.label ?? 'anonymous'}) was asked for '${String(key)}' after it was closed. A closed ` +\n 'registry has let go of its source and cannot answer.'\n );\n }\n // Taking the first key is what starts the subscription, so a\n // registry made in a component body costs nothing until something\n // is on screen. `subscribe` may emit synchronously, and the cell\n // is not in the map yet — so it is read directly here and the\n // walk below skips it.\n this.attach();\n const first = this.hasSnapshot ? this.read(this.snapshot as S, key, datum) : this.initial(key, datum);\n const cell = new FanCell(first, key, datum, this.label);\n this.cells.set(key, cell);\n this.warnIfOnlyGrowing();\n return cell;\n }\n\n /** The cell for a key if it is live, without making one. */\n peek(key: FanKey): FanCell<V, D> | undefined {\n return this.cells.get(key);\n }\n\n /**\n * Drops a key.\n *\n * The cell completes, so anything still bound to it is torn down\n * rather than left holding a value that has stopped being updated.\n * Letting go of the last key lets go of the source.\n */\n release(key: FanKey): void {\n const cell = this.cells.get(key);\n if (cell === undefined) {\n return;\n }\n this.cells.delete(key);\n this.releases++;\n cell.subject.complete();\n if (this.cells.size === 0) {\n this.detach();\n }\n }\n\n /** Drops every key, keeping the registry usable. */\n releaseAll(): void {\n for (const cell of this.cells.values()) {\n cell.subject.complete();\n }\n this.releases += this.cells.size;\n this.cells.clear();\n this.detach();\n }\n\n /** Drops every key and the source for good. */\n close(): void {\n this.releaseAll();\n this.closed = true;\n }\n\n /**\n * Pushes the latest source value into the keys that changed.\n *\n * Called for each emission, and callable by hand when the source has\n * not emitted but the *reading* has changed — a selection moving\n * over a grid whose values are the same is exactly that case, and it\n * is why the reader takes a key rather than the registry taking a\n * projection.\n */\n refresh(keys?: Iterable<FanKey>): void {\n if (!this.hasSnapshot) {\n return;\n }\n const snapshot = this.snapshot as S;\n for (const key of keys ?? this.cells.keys()) {\n const cell = this.cells.get(key);\n if (cell === undefined) {\n continue;\n }\n const next = this.read(snapshot, key, cell.datum);\n if (!this.equal(cell.subject.value, next)) {\n cell.subject.next(next);\n }\n }\n }\n\n /**\n * Says so, once, when a registry has grown large and released\n * nothing.\n *\n * A registry is a resource and `release` is the caller's, which is\n * the one thing about this that can be got wrong silently: a key\n * that is never released keeps its cell, its entry and its place in\n * every walk, for the life of the registry. There is no upper bound\n * to hit and nothing to see, which is exactly the kind of leak that\n * is found six months later by someone else.\n *\n * The threshold is generous on purpose. A grid mounting its first\n * window legitimately takes hundreds of keys before it has scrolled\n * far enough to retire one, and a warning that fired there would be\n * noise people learn to scroll past.\n */\n private warnIfOnlyGrowing(): void {\n if (this.warnedGrowing || this.releases > 0 || this.cells.size < GROWING_WITHOUT_RELEASE) {\n return;\n }\n this.warnedGrowing = true;\n console.warn(\n `fanOut(${this.label ?? 'anonymous'}) holds ${this.cells.size} keys and has released none. A registry ` +\n 'keeps a key until `release`, so a surface that mounts and unmounts has to say when a key has gone: ' +\n '`release(key)` as it leaves, `releaseAll()` when the screen does, `close()` when the registry does. ' +\n 'Ignore this if the keys really do all live as long as the registry.'\n );\n }\n\n private attach(): void {\n if (this.upstream !== null) {\n return;\n }\n this.upstream = this.source.subscribe(snapshot => {\n const previous = this.hasSnapshot ? (this.snapshot as S) : undefined;\n // `changed` is asked before the snapshot moves, so it can compare\n // the two, and its answer is used after — a source that returns a\n // view onto its own delta must not have it read out from under it.\n const keys = this.changed?.(snapshot, previous);\n this.snapshot = snapshot;\n this.hasSnapshot = true;\n this.refresh(keys);\n });\n }\n\n private detach(): void {\n this.upstream?.unsubscribe();\n this.upstream = null;\n this.hasSnapshot = false;\n this.snapshot = undefined;\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 * A point to open beside, relative to the viewport: where a context\n * menu was asked for. Placed as an anchor of no size would be, with\n * `placement`, so an entry that would run off an edge flips or shifts\n * to stay on screen — which `top` and `left` do not do.\n */\n readonly point?: { readonly x: number; readonly y: number };\n /**\n * Centre an unanchored entry in the viewport along an axis.\n *\n * The edges of that axis stop being a position and become the region\n * to centre within — unset they are the whole viewport, so a dialog\n * asking for `'x'` sits in the middle however wide the window is, and\n * one asking for `'both'` sits in the middle of the screen. The other\n * axis is untouched: `center: 'x'` with `bottom: 24` is a toast\n * centred along the bottom edge.\n *\n * Ignored on an anchored entry, which is placed beside its anchor.\n */\n readonly center?: 'x' | 'y' | 'both';\n /**\n * Close the entry when the pointer goes down, or the wheel turns,\n * anywhere outside it. A backdrop takes those events, so nothing\n * underneath scrolls while the entry is open. Without it the entry\n * stays open and follows its anchor through scrolling.\n */\n readonly dismissOnOutsidePress?: boolean;\n /** 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 anchorPoint: entry.anchor == null ? entry.point : 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 { DevtoolsEvent, DevtoolsRequest } from '../DevtoolsProtocol';\nimport type {\n UiKeyModifiers,\n EditingState,\n RendererBackend,\n UiInsets,\n UiPointerDevice,\n UiSemanticsAction,\n UiScrollability,\n UiSemanticsUpdate,\n UiFileDropMessage\n} from 'gesso-core';\nimport type { AudioAction, AudioRequest, AudioSample } from '../AudioService';\nimport type { ColorScheme } from '../colorScheme';\nimport type { ShellFileRequest, ShellFileResult, ShellStorageOp, ShellStorageResult } from '../ShellService';\nimport type { FramePhaseTimings, GpuStageTimings, RendererChoice } from '../GessoRuntime';\n\n/**\n * Messages the main-thread shell sends to the render worker.\n *\n * Deliberately small: input, size, and lifecycle. UiElements,\n * component instances, observables and UiNodes never cross the\n * boundary — they are constructed in the worker and stay there.\n */\nexport type ShellToRuntimeMessage =\n | {\n type: 'init';\n canvas: OffscreenCanvas;\n width: number;\n height: number;\n dpr: number;\n renderer?: RendererChoice;\n /**\n * How typed text reaches the runtime: `proxy` when the shell has\n * an editing proxy that sends `beforeInput` and composition (then\n * printable key presses are not text); `keys` (default) when key\n * presses are all there is.\n */\n textInput?: 'proxy' | 'keys';\n /**\n * One end of a channel to the application worker, when the shell\n * spawned one.\n *\n * The shell creates both workers and wires them together once,\n * then stays out of the way — it never sees a patch. Owning the\n * spawn rather than letting the render worker nest a worker\n * inside itself keeps the application alive across a render\n * worker being replaced (a renderer switch), and avoids\n * depending on nested worker support, which is not uniform\n * across the webviews this project targets.\n */\n appPort?: MessagePort;\n /**\n * Whether the shell has an accessibility mirror to feed. False\n * stops the runtime computing the geometry it would need, which\n * is the only per-frame cost the mirror has in here.\n */\n accessibility?: boolean;\n }\n | { type: 'resize'; width: number; height: number; dpr: number }\n /**\n * A devtools panel's request (`DevtoolsProtocol.ts`). The worker\n * answers with `devtools` messages; `console` is handled by the\n * worker host, which owns the global the calls are made on.\n */\n | { type: 'devtools'; request: DevtoolsRequest }\n /**\n * `pointer` is the contact: which device, and which of its\n * simultaneous contacts. Optional, so a shell written before touch\n * existed is read as the mouse it was — every touch behaviour in the\n * runtime is opt-in on this field saying `touch`.\n */\n | {\n type: 'pointerDown';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | {\n type: 'pointerMove';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | {\n type: 'pointerUp';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | { type: 'pointerCancel'; pointer?: UiPointerDevice; at?: number }\n /**\n * `deltaMode` is the DOM's own value, forwarded rather than\n * translated: a wheel delta is a distance in pixels only when it is\n * 0, and Firefox reports lines. Optional, so a shell that predates\n * it is read as pixels — which is what it was assumed to be.\n */\n | {\n type: 'wheel';\n x: number;\n y: number;\n deltaX: number;\n deltaY: number;\n modifiers: UiKeyModifiers;\n deltaMode?: number;\n /**\n * The legacy `wheelDeltaY`. Forwarded because a detented wheel\n * reports it in multiples of 120 and a precision device does\n * not, and only a detented wheel is worth animating.\n */\n wheelDeltaY?: number;\n at?: number;\n }\n /**\n * `textFollows`: whether the key reached the editing proxy, which\n * turns it into a `beforeinput` of its own. False means the key went\n * to the canvas and its text, if it has any, is the runtime's to make.\n */\n | { type: 'keyDown'; key: string; modifiers: UiKeyModifiers; at?: number; textFollows?: boolean }\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 /**\n * What the shell did with a file request (ShellService.openFiles and\n * its siblings), under the request's `id`. A file read comes back\n * with its bytes, transferred rather than copied.\n */\n | { type: 'fileResult'; id: number; result: ShellFileResult }\n | { type: 'inspector'; enabled: boolean }\n /**\n * What an assistive technology did to the accessibility mirror: a\n * press, a focus move, or a value set.\n *\n * The fourth preference-shaped asymmetry on this protocol, and the\n * only *input* on it that no device produced. It arrives by id\n * rather than by coordinate because that is what the mirror has: an\n * element standing for a node, with no idea where the person's\n * pointer is or whether there is one. `GessoRuntime.applySemanticsAction`\n * turns it back into the events a pointer and a keyboard produce.\n */\n | { type: 'semanticsAction'; action: UiSemanticsAction }\n /**\n * Files dragged over the canvas from outside the application, and\n * let go on it: `UiDragSession.applyFileDrop` turns the four phases\n * into a drag like any other. On `drop` the files' bytes ride along\n * and are transferred rather than copied; see `attachFileDrop`.\n */\n | UiFileDropMessage\n /**\n * What the shell's audio element is doing: on every state change and\n * about once a second while it plays. The element lives on the shell\n * because no worker can make a sound; `AudioService` is its client\n * and moves the position on between samples. See `AudioSink`.\n */\n | { type: 'audioSample'; sample: AudioSample }\n /**\n * What the platform's media controls asked for (the keyboard's media\n * keys, the OS overlay). Play and pause were already done to the\n * element and arrive as samples too; next and previous are the\n * application's to answer.\n */\n | { type: 'audioAction'; action: AudioAction }\n /**\n * One display refresh, forwarded from the shell's\n * `requestAnimationFrame`.\n *\n * The render worker's frames were paced by a fixed 16ms timer,\n * because `requestAnimationFrame` is tied to the compositor and does\n * not exist off the main thread. That capped every display at\n * roughly sixty and aligned to none of them. The shell runs the loop\n * and forwards the beat; `time` is the rAF timestamp, so the\n * runtime's frame times stay on the same clock the display is on.\n *\n * Sent only between `frameLoop` starting and stopping, so an idle\n * app exchanges nothing.\n */\n | { type: 'tick'; time: number }\n | { type: 'dispose' };\n\n/**\n * Where an error the worker reports came from.\n *\n * The shell cannot see a worker's exceptions, so this is the only\n * thing that tells a developer *what* is broken rather than only what\n * threw — and the four cases have genuinely different consequences:\n *\n * - `message` — thrown while handling a message from the shell.\n * Input or a resize was dropped; the app is otherwise intact.\n * - `uncaught` — an exception or a rejected promise nothing caught,\n * which is almost always a frame. The scheduler had already\n * drained the dirty set for that frame, so the work it held is\n * gone and the surface can be stale until something dirties those\n * nodes again. The most serious of the four.\n * - `renderer` — the backend refused to draw (a lost GPU device, a\n * surface it could not configure). Layout and state are fine.\n * - `channel` — a channel's worker or its patch stream threw. The\n * view is intact; the data behind it stopped.\n * - `listener` — one of the application's own event listeners threw.\n * The dispatcher caught it so the event still reached the rest of\n * the tree, so this is the one source that costs the running\n * application nothing but whatever the handler was supposed to do.\n */\nexport type RuntimeErrorSource =\n | 'message'\n | 'uncaught'\n | 'renderer'\n | 'channel'\n | 'listener'\n /**\n * A frame threw. The frame was abandoned and the clock kept\n * running, so the application is still drawing: this is a report,\n * not an obituary.\n *\n * Its own source because it used to arrive as `message` — the throw\n * escaped into whatever was forwarding the tick — and that both\n * mislabelled it and stopped the application, since nothing re-armed\n * the clock afterwards.\n */\n | 'frame';\n\n/**\n * Messages the render worker sends back.\n *\n * The shell owns no UI state, so this carries only observability:\n * readiness, frame timings, and errors that would otherwise be\n * invisible inside a worker.\n */\nexport type RuntimeToShellMessage =\n | { type: 'ready' }\n | {\n type: 'frame';\n frame: number;\n durationMs: number;\n nodes: number;\n measured: number;\n relayoutRoots: number;\n 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 /**\n * Open, save, reopen, list or forget files (ShellService.openFiles\n * and its siblings). Answered with `fileResult`. Sent at once, like\n * `popup`, because a picker is shown only while the click that asked\n * for it is fresh.\n */\n | { type: 'file'; id: number; request: ShellFileRequest }\n /** The router navigated; the shell owns the address bar (RouterService). */\n | { type: 'history'; action: 'push' | 'replace' | 'back' | 'forward'; url?: string }\n /** Load, play, pause, seek, set the volume or the OS metadata (AudioService). */\n | { type: 'audio'; request: AudioRequest }\n /**\n * What the accessibility mirror needs to keep up with this frame:\n * the semantics patches, the boxes that moved, and the focused node\n * when focus moved.\n *\n * Sent only while the shell has a mirror attached — a `SemanticsMirror`\n * subscribes by existing, and a runtime nobody is mirroring computes\n * no geometry at all. Records and boxes travel at different cadences\n * and are one message anyway; `UiSemanticsUpdate` says why.\n */\n /**\n * Whether the runtime currently wants display refreshes.\n *\n * The shell answers by running or stopping a `requestAnimationFrame`\n * loop that sends `tick`. It is a state rather than a per-frame\n * request because a request-per-frame costs a round trip inside\n * every frame: a request that reaches the shell after that vsync's\n * callback has run waits for the next one, and the frame rate\n * halves.\n */\n | { type: 'frameLoop'; running: boolean }\n /**\n * One `resize` has been applied, and the shell may send the next.\n *\n * A resize is the one shell message whose handling costs a full\n * layout, and `ResizeObserver` delivers one per refresh while a\n * window edge is dragged. A worker slower than the display therefore\n * accumulates a queue of sizes it must lay out and paint in turn,\n * every one of them already wrong, and the lag grows for as long as\n * the drag lasts rather than settling.\n *\n * So the shell keeps at most one resize in flight and remembers only\n * the latest size it has not sent. This message is what lets it: the\n * worker has drained the previous one, so the current size can go\n * now. Nothing is dropped that anyone can see — the last size always\n * gets sent, because it is the one held back.\n *\n * The dimensions ride along so the shell can tell an acknowledgement\n * of the size it is holding from one it has already superseded.\n */\n | { type: 'resized'; width: number; height: number; dpr: number }\n | { type: 'semantics'; update: UiSemanticsUpdate };\n\n/**\n * The set of shell messages that carry a user input.\n *\n * The shell stamps these with `at` and the runtime measures against\n * them; everything else in the protocol is a size, a preference or a\n * lifecycle signal and has no latency to speak of.\n */\nconst INPUT_MESSAGE_TYPES: ReadonlySet<string> = new Set([\n 'pointerDown',\n 'pointerMove',\n 'pointerUp',\n 'pointerCancel',\n 'wheel',\n 'keyDown',\n 'keyUp',\n 'beforeInput',\n 'compositionStart',\n 'compositionUpdate',\n 'compositionEnd',\n 'paste'\n]);\n\nexport function isInputMessage(message: ShellToRuntimeMessage): message is ShellToRuntimeMessage & { at?: number } {\n return INPUT_MESSAGE_TYPES.has(message.type);\n}\n\n/**\n * 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 { 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 { UiDroppedFile, UiFileDropMessage } from 'gesso-core';\n\n/**\n * The shell half of an OS file drop.\n *\n * `UiDragSession.applyFileDrop` has turned a `fileDrop` message into\n * an ordinary drag since drop targets arrived; what was missing was a\n * shell that posted one, so a zone accepting `EXTERNAL_FILES` could be\n * written, spec'd and never reached. This is that shell, for a browser:\n * the four DOM drag events on the surface, turned into the four phases\n * of the message.\n *\n * Shared by both configurations because it is the same four events\n * either way. The worker shell posts the message across and transfers\n * the bytes; the single-thread shell hands it straight to its runtime.\n * Neither decides anything about the files — whether a drop is taken,\n * and what it means, is the zone's.\n *\n * **Only drags that carry files.** A drag of selected text from\n * another page over the canvas is not a file drop, and answering it\n * would put a drop cursor over an application that cannot take it.\n */\n\nexport interface FileDropSink {\n /**\n * One phase of a drop. `transfer` holds the dropped files' buffers\n * on `drop`, for a sink that posts across a thread and can move them\n * rather than copy them; it is empty on every other phase.\n */\n (message: UiFileDropMessage, transfer: ArrayBuffer[]): void;\n}\n\n/** The slice of a DOM element this needs, so a spec can double it. */\nexport interface FileDropTarget {\n addEventListener(type: string, listener: (event: DragEvent) => void): void;\n removeEventListener(type: string, listener: (event: DragEvent) => void): void;\n}\n\n/**\n * Listens for files dragged over `target` and reports them to `sink`.\n *\n * `toLocal` is the shell's own client-to-surface conversion, so a drop\n * lands at the point a press at the same place would.\n *\n * `dragover` has its default prevented on every event, not only the\n * first: that is what tells the browser the drop is wanted, and the\n * browser asks again on each one. `drop` has it prevented too, or the\n * browser navigates away to show the file — which, over an\n * application, is the tab being replaced by a CSV.\n *\n * During the drag the platform lets a page read a file's type and not\n * its name or size, so `enter` and `over` carry what it allows; `drop`\n * carries all of it, bytes included. Reading the bytes is asynchronous,\n * so the `drop` message goes when they are read, and a drop whose files\n * cannot be read is reported as a `leave` so that the zone does not\n * stay lit waiting for a drop that will never arrive.\n *\n * Returns the function that stops listening.\n */\nexport function attachFileDrop(\n target: FileDropTarget,\n toLocal: (clientX: number, clientY: number) => { x: number; y: number },\n sink: FileDropSink\n): () => void {\n let inside = false;\n\n const post = (phase: UiFileDropMessage['phase'], event: DragEvent, files: readonly UiDroppedFile[]): void => {\n const { x, y } = toLocal(event.clientX, event.clientY);\n sink({ type: 'fileDrop', phase, x, y, files }, []);\n };\n\n const onEnterOrOver = (event: DragEvent): void => {\n if (!carriesFiles(event)) {\n return;\n }\n event.preventDefault();\n if (event.dataTransfer !== null) {\n event.dataTransfer.dropEffect = 'copy';\n }\n post(inside ? 'over' : 'enter', event, filesDuringDrag(event));\n inside = true;\n };\n\n const onLeave = (event: DragEvent): void => {\n if (!inside) {\n return;\n }\n inside = false;\n post('leave', event, []);\n };\n\n const onDrop = (event: DragEvent): void => {\n if (!carriesFiles(event)) {\n return;\n }\n event.preventDefault();\n inside = false;\n const { x, y } = toLocal(event.clientX, event.clientY);\n const files = Array.from(event.dataTransfer?.files ?? []);\n void Promise.all(files.map(file => file.arrayBuffer())).then(\n buffers => {\n const dropped: UiDroppedFile[] = files.map((file, at) => ({\n name: file.name,\n mediaType: file.type,\n size: file.size,\n lastModified: file.lastModified,\n bytes: buffers[at]\n }));\n sink({ type: 'fileDrop', phase: 'drop', x, y, files: dropped }, buffers);\n },\n () => sink({ type: 'fileDrop', phase: 'leave', x, y, files: [] }, [])\n );\n };\n\n target.addEventListener('dragenter', onEnterOrOver);\n target.addEventListener('dragover', onEnterOrOver);\n target.addEventListener('dragleave', onLeave);\n target.addEventListener('drop', onDrop);\n return () => {\n target.removeEventListener('dragenter', onEnterOrOver);\n target.removeEventListener('dragover', onEnterOrOver);\n target.removeEventListener('dragleave', onLeave);\n target.removeEventListener('drop', onDrop);\n };\n}\n\nfunction carriesFiles(event: DragEvent): boolean {\n const types = event.dataTransfer?.types;\n return types !== undefined && Array.from(types).includes('Files');\n}\n\n/** What a drag in flight lets a page see of its files: their types. */\nfunction filesDuringDrag(event: DragEvent): UiDroppedFile[] {\n const items = Array.from(event.dataTransfer?.items ?? []);\n return items\n .filter(item => item.kind === 'file')\n .map(item => ({ name: '', mediaType: item.type, size: 0, lastModified: 0 }));\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: 'file'; id: number; request: ShellFileRequest }\n | { type: 'history'; action: 'push' | 'replace'; url: string }\n | { type: 'history'; action: 'back' | 'forward'; url?: undefined };\n\n/**\n * A kind of file a picker offers, as the File System Access API and a\n * file input both understand it.\n *\n * `extensions` include the dot (`.csv`), because that is the form both\n * APIs take and a spelling this layer translated would be one more\n * place for the two to disagree.\n */\nexport interface ShellFileType {\n readonly description: string;\n readonly mediaType: string;\n readonly extensions: readonly string[];\n}\n\n/**\n * What the shell is asked to do with files.\n *\n * `handle` is a number the shell hands out for a file somebody picked\n * — the `FileSystemFileHandle` itself cannot leave the main thread,\n * since nothing that is not plain data crosses the barrier. The shell\n * keeps the handle and remembers it across reloads, so a number from\n * yesterday's session still names yesterday's file.\n */\nexport type ShellFileRequest =\n /** Show an open picker. */\n | { readonly op: 'open'; readonly accept: readonly ShellFileType[]; readonly multiple: boolean }\n /** Read a file the shell already has a handle to, asking permission again if it lapsed. */\n | { readonly op: 'reopen'; readonly handle: number }\n /**\n * Write a file: to `handle` when given, and otherwise to wherever a\n * save picker says — or, where there is no picker, as a download.\n */\n | {\n readonly op: 'save';\n readonly name: string;\n readonly mediaType: string;\n readonly text: string;\n /**\n * The file's bytes, for one that is not text — a zip, an image.\n * Written instead of `text` when given; bytes cross the barrier\n * as they do on the way in, in `ShellFile.bytes`.\n */\n readonly bytes?: Uint8Array<ArrayBuffer>;\n readonly handle?: number;\n readonly accept: readonly ShellFileType[];\n }\n /** The files the shell remembers, most recently used first. */\n | { readonly op: 'recent' }\n /** Stop remembering one. */\n | { readonly op: 'forget'; readonly handle: number };\n\n/** One file the shell read. */\nexport interface ShellFile {\n readonly name: string;\n readonly mediaType: string;\n readonly lastModified: number;\n readonly bytes: ArrayBuffer;\n /** The shell's number for it, or null where the platform gives none (a file input). */\n readonly handle: number | null;\n}\n\n/** A file the shell remembers. */\nexport interface ShellRecentFile {\n readonly handle: number;\n readonly name: string;\n /** When it was last opened or saved, in epoch milliseconds. */\n readonly used: number;\n}\n\n/**\n * What the shell made of a file request.\n *\n * One record with a field per shape of answer, as `ShellStorageResult`\n * is and for the same reason. `cancelled` is its own outcome because a\n * person closing a picker is a decision and not a failure, and an\n * application that reported it as one would be wrong every time.\n * `denied` is a permission refused; `unsupported` is a shell with no\n * way to do it at all.\n */\nexport interface ShellFileResult {\n readonly outcome: 'ok' | 'cancelled' | 'denied' | 'unsupported' | 'failed';\n /** What `open` and `reopen` read. */\n readonly files: readonly ShellFile[];\n /** Where `save` wrote: `'file'` through a handle, `'download'` without one. */\n readonly saved: { readonly name: string; readonly handle: number | null; readonly via: 'file' | 'download' } | null;\n /** What `recent` lists. */\n readonly recent: readonly ShellRecentFile[];\n /** Why it did not answer, as a message; null when it did. */\n readonly error: string | null;\n}\n\n/** The four things `localStorage` is asked for; see `ShellStorage`. */\nexport type ShellStorageOp = 'read' | 'write' | 'remove' | 'keys';\n\n/**\n * What the shell made of a storage request.\n *\n * One record with a field per shape of answer, rather than four reply\n * messages: it is plain data either way, and a single reply keeps the\n * pairing with `id` in one place. `value` is a read's, `keys` is a\n * listing's, and both are empty for a write.\n */\nexport interface ShellStorageResult {\n readonly outcome: 'ok' | 'denied' | 'full' | 'failed';\n readonly value: string | null;\n readonly keys: readonly string[];\n /** Why it did not answer, as a message; null when it did. */\n readonly error: string | null;\n}\n\n/**\n * The shell's services, as a store components can inject.\n *\n * A component in the render worker has no clipboard and no window.\n * Dispatching an action here hands the request to the runtime, which\n * forwards it to whichever host it has: `WorkerApp` posts it to the\n * main thread, `GessoApp` performs it directly. Every runtime registers\n * one, like `OverlayService`; being a store keeps the rule that\n * components reach the outside world through actions only, and gives a\n * desktop shell one place to bind native equivalents.\n */\nexport class ShellService {\n private handler: ((request: ShellRequest) => void) | null = null;\n 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 /** File requests asked for and not yet answered, by the id sent with each. */\n private readonly files = new Map<number, (result: ShellFileResult) => void>();\n private nextFileId = 1;\n\n /**\n * The appearance the platform is asking for, as the shell reports it:\n * once when the app starts, and again whenever it changes.\n *\n * Read-only to the application on purpose. Nothing in the framework\n * consumes this — no built-in theme switches on it, and nothing in\n * layout, paint or input reads it — so the only writer is the shell,\n * and a cell an application could also write would be a cell the next\n * media-query change silently overwrites.\n *\n * What it means is the application's to decide. A theme is an\n * ordinary prop that accepts an Observable, so the whole of following\n * the platform is:\n *\n * const theme = ctx.inject(ShellService).colorScheme.pipe(\n * map(scheme => (scheme === 'dark' ? darkTheme : lightTheme))\n * );\n *\n * An app with its own light/dark control keeps that choice as\n * application state — a channel or a store, like any other preference\n * that outlives a screen — and combines it with this. The framework\n * does not decide what dark looks like, and does not remember what\n * the person picked.\n */\n /**\n * A cell rather than a bare Observable, so `computed(() => ...)` can\n * read it beside a channel's view; its setter stays private here.\n */\n readonly colorScheme: ReadableCell<ColorScheme> = this.scheme;\n\n /** 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 /**\n * Shows an open picker and reads what was picked.\n *\n * A request, like a popup, and for the same reason: a picker is the\n * window's, and a browser shows one only while the click that asked\n * for it is fresh. Call this from the handler of that click and\n * before any slow work, and the round trip through a worker is fast\n * enough. With the File System Access API each file comes back with\n * a `handle` the shell remembers; through a file input, which is\n * what a browser without the API has, `handle` is null and the file\n * cannot be saved back to.\n */\n openFiles(\n options: { readonly accept?: readonly ShellFileType[]; readonly multiple?: boolean } = {}\n ): Promise<ShellFileResult> {\n return this.requestFile({ op: 'open', accept: options.accept ?? [], multiple: options.multiple ?? false });\n }\n\n /**\n * Reads a file the shell has a handle to — from `recentFiles`, or\n * from an earlier `openFiles` or `saveFile` — asking for permission\n * again when the browser has let it lapse, which it does across a\n * reload. Asking needs a gesture, so this belongs in a click handler\n * too.\n */\n reopenFile(handle: number): Promise<ShellFileResult> {\n return this.requestFile({ op: 'reopen', handle });\n }\n\n /**\n * Writes text, or bytes, to a file.\n *\n * With `handle`, to that file, which is Save; without one, to\n * wherever a save picker says, which is Save As. A browser with no\n * picker gets a download instead, and `saved.via` says which\n * happened, so an application can tell the person where the file\n * went — and knows it has no handle to save back to next time.\n */\n saveFile(options: {\n readonly name: string;\n /** What to write, for a text file. */\n readonly text?: string;\n /** What to write, for any other kind; taken over `text` when both are given. */\n readonly bytes?: Uint8Array<ArrayBuffer>;\n readonly mediaType?: string;\n readonly handle?: number;\n readonly accept?: readonly ShellFileType[];\n }): Promise<ShellFileResult> {\n return this.requestFile({\n op: 'save',\n name: options.name,\n text: options.text ?? '',\n ...(options.bytes === undefined ? {} : { bytes: options.bytes }),\n mediaType: options.mediaType ?? 'application/octet-stream',\n accept: options.accept ?? [],\n ...(options.handle === undefined ? {} : { handle: options.handle })\n });\n }\n\n /** The files the shell remembers, most recently used first. */\n recentFiles(): Promise<ShellFileResult> {\n return this.requestFile({ op: 'recent' });\n }\n\n /** Stops remembering a file. Its handle means nothing afterwards. */\n forgetFile(handle: number): Promise<ShellFileResult> {\n return this.requestFile({ op: 'forget', handle });\n }\n\n /**\n * The wire under the five above. With no shell installed the answer\n * is `unsupported`, for the reason a storage request's is `denied`:\n * a promise left unsettled would hang whatever was waiting on it.\n */\n requestFile(request: ShellFileRequest): Promise<ShellFileResult> {\n const handler = this.handler;\n if (handler === undefined || handler === null) {\n return Promise.resolve(shellFilesUnsupported());\n }\n const id = this.nextFileId++;\n const settled = new Promise<ShellFileResult>(resolve => {\n this.files.set(id, resolve);\n });\n handler({ type: 'file', id, request });\n return settled;\n }\n\n /**\n * Called by the runtime with what the shell did. Not for\n * applications. An unknown id is ignored, on `settlePopup`'s terms.\n */\n settleFile(id: number, result: ShellFileResult): void {\n const resolve = this.files.get(id);\n if (resolve === undefined) {\n return;\n }\n this.files.delete(id);\n resolve(result);\n }\n}\n\n/** The answer for a shell that cannot do anything with files. */\nexport function shellFilesUnsupported(error = 'There is no shell to reach files through.'): ShellFileResult {\n return { outcome: 'unsupported', files: [], saved: null, recent: [], error };\n}\n","import type { ShellFile, ShellFileRequest, ShellFileResult, ShellFileType, ShellRecentFile } from './ShellService';\nimport { shellFilesUnsupported } from './ShellService';\n\n/**\n * The shell's half of a file request: pickers, handles, and the files\n * it remembers.\n *\n * Written once and called from both configurations, as\n * `performShellStorage` is, because what the shell does with a file\n * should not depend on whether the render side is a worker. It decides\n * nothing about what a file *is*: it reads bytes, writes text, and\n * answers with plain data.\n *\n * ## Handles stay here\n *\n * A `FileSystemFileHandle` is the only thing that lets a page write\n * back to a file somebody chose, and it is not plain data, so it cannot\n * cross to the render worker. The shell keeps every handle it is given\n * and hands out a number for it instead. The handles are kept in\n * IndexedDB, which can hold them — so a number handed out yesterday\n * still names yesterday's file, and \"recent files\" is just the store\n * listed. What does not survive a reload is the *permission*: a browser\n * asks again, and asking needs a gesture, which is why `reopen` and\n * `save` to a handle both belong in a click handler.\n *\n * ## Without the File System Access API\n *\n * Firefox and Safari have no pickers. There, `open` falls back to a\n * file input and returns files with no handle, and `save` without a\n * handle falls back to a download. An application learns which from\n * the answer — `handle: null`, `via: 'download'` — rather than from a\n * feature test of its own, because the shell is the only thing that\n * knows.\n */\n\n/** The slice of a `FileSystemFileHandle` used here. */\nexport interface ShellFileHandle {\n readonly name: string;\n getFile(): Promise<File>;\n createWritable?(): Promise<{ write(data: string | Uint8Array<ArrayBuffer>): Promise<void>; close(): Promise<void> }>;\n queryPermission?(descriptor: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n requestPermission?(descriptor: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n isSameEntry?(other: ShellFileHandle): Promise<boolean>;\n}\n\n/** A remembered handle, as the store keeps it. */\nexport interface StoredFileHandle {\n readonly id: number;\n readonly handle: ShellFileHandle;\n readonly name: string;\n readonly used: number;\n}\n\n/** Where handles are remembered. IndexedDB in a browser; a map in a spec. */\nexport interface ShellHandleStore {\n all(): Promise<StoredFileHandle[]>;\n /** Stores an entry, allocating an id when it has none, and answers the id. */\n put(entry: {\n readonly id?: number;\n readonly handle: ShellFileHandle;\n readonly name: string;\n readonly used: number;\n }): Promise<number>;\n delete(id: number): Promise<void>;\n}\n\n/** What the shell can reach. Every member optional but the store and the clock. */\nexport interface ShellFilesHost {\n readonly store: ShellHandleStore;\n now(): number;\n showOpenFilePicker?(options: PickerOptions & { multiple: boolean }): Promise<ShellFileHandle[]>;\n showSaveFilePicker?(options: PickerOptions & { suggestedName: string }): Promise<ShellFileHandle>;\n /** A file input, clicked. Null when it was dismissed. */\n pickWithInput?(accept: string, multiple: boolean): Promise<File[] | null>;\n /** Hands a file to the browser's downloads. */\n download?(name: string, blob: Blob): void;\n}\n\ninterface PickerOptions {\n types?: { description: string; accept: Record<string, string[]> }[];\n excludeAcceptAllOption?: boolean;\n}\n\n/** How many files are remembered before the oldest is let go. */\nexport const RECENT_FILE_LIMIT = 50;\n\nexport class ShellFiles {\n constructor(private readonly host: ShellFilesHost) {}\n\n /**\n * Performs one request. Never rejects: every path, including a\n * browser throwing something nobody predicted, is an answer, because\n * a promise on the other side of the barrier is waiting for one.\n */\n async perform(request: ShellFileRequest): Promise<ShellFileResult> {\n try {\n switch (request.op) {\n case 'open':\n return await this.open(request.accept, request.multiple);\n case 'reopen':\n return await this.reopen(request.handle);\n case 'save':\n return await this.save(request);\n case 'recent':\n return ok({ recent: await this.recent() });\n case 'forget':\n await this.host.store.delete(request.handle);\n return ok({ recent: await this.recent() });\n }\n } catch (error) {\n return failure(error);\n }\n }\n\n private async open(accept: readonly ShellFileType[], multiple: boolean): Promise<ShellFileResult> {\n if (this.host.showOpenFilePicker !== undefined) {\n const handles = await this.host.showOpenFilePicker({ ...pickerTypes(accept), multiple });\n const files: ShellFile[] = [];\n for (const handle of handles) {\n const id = await this.remember(handle);\n files.push(await read(await handle.getFile(), id));\n }\n return ok({ files });\n }\n if (this.host.pickWithInput !== undefined) {\n const picked = await this.host.pickWithInput(inputAccept(accept), multiple);\n if (picked === null) {\n return cancelled();\n }\n return ok({ files: await Promise.all(picked.map(file => read(file, null))) });\n }\n return shellFilesUnsupported('This browser cannot open files.');\n }\n\n private async reopen(id: number): Promise<ShellFileResult> {\n const entry = await this.find(id);\n if (entry === null) {\n return { ...failure(null), error: 'The shell has no file under that handle.' };\n }\n if (!(await permitted(entry.handle, 'read'))) {\n return denied();\n }\n const file = await entry.handle.getFile();\n await this.host.store.put({ ...entry, used: this.host.now() });\n return ok({ files: [await read(file, id)] });\n }\n\n private async save(request: Extract<ShellFileRequest, { op: 'save' }>): Promise<ShellFileResult> {\n if (request.handle !== undefined) {\n const entry = await this.find(request.handle);\n if (entry === null) {\n return { ...failure(null), error: 'The shell has no file under that handle.' };\n }\n if (!(await permitted(entry.handle, 'readwrite'))) {\n return denied();\n }\n await write(entry.handle, request.bytes ?? request.text);\n await this.host.store.put({ ...entry, used: this.host.now() });\n return ok({ saved: { name: entry.handle.name, handle: entry.id, via: 'file' } });\n }\n if (this.host.showSaveFilePicker !== undefined) {\n const handle = await this.host.showSaveFilePicker({\n ...pickerTypes(request.accept),\n suggestedName: request.name\n });\n await write(handle, request.bytes ?? request.text);\n const id = await this.remember(handle);\n return ok({ saved: { name: handle.name, handle: id, via: 'file' } });\n }\n if (this.host.download !== undefined) {\n this.host.download(request.name, new Blob([request.bytes ?? request.text], { type: request.mediaType }));\n return ok({ saved: { name: request.name, handle: null, via: 'download' } });\n }\n return shellFilesUnsupported('This browser cannot save files.');\n }\n\n private async recent(): Promise<ShellRecentFile[]> {\n const all = await this.host.store.all();\n return all.sort((a, b) => b.used - a.used).map(entry => ({ handle: entry.id, name: entry.name, used: entry.used }));\n }\n\n private async find(id: number): Promise<StoredFileHandle | null> {\n return (await this.host.store.all()).find(entry => entry.id === id) ?? null;\n }\n\n /**\n * Keeps a handle and answers its number.\n *\n * The same file picked twice is one entry, not two: a picker hands\n * back a fresh handle object every time, so the comparison is\n * `isSameEntry` rather than identity. Past `RECENT_FILE_LIMIT` the\n * least recently used is let go.\n */\n private async remember(handle: ShellFileHandle): Promise<number> {\n const all = await this.host.store.all();\n const now = this.host.now();\n for (const entry of all) {\n if (entry.handle.isSameEntry !== undefined && (await entry.handle.isSameEntry(handle))) {\n return this.host.store.put({ id: entry.id, handle, name: handle.name, used: now });\n }\n }\n const id = await this.host.store.put({ handle, name: handle.name, used: now });\n const oldest = all.sort((a, b) => b.used - a.used).slice(RECENT_FILE_LIMIT - 1);\n for (const entry of oldest) {\n await this.host.store.delete(entry.id);\n }\n return id;\n }\n}\n\nasync function read(file: File, handle: number | null): Promise<ShellFile> {\n return {\n name: file.name,\n mediaType: file.type,\n lastModified: file.lastModified,\n bytes: await file.arrayBuffer(),\n handle\n };\n}\n\nasync function write(handle: ShellFileHandle, data: string | Uint8Array<ArrayBuffer>): Promise<void> {\n if (handle.createWritable === undefined) {\n throw new Error('This file cannot be written to.');\n }\n const stream = await handle.createWritable();\n await stream.write(data);\n await stream.close();\n}\n\n/**\n * Whether a handle may be used, asking when it may not yet.\n *\n * A handle from this session's picker is granted already; one from\n * yesterday's is `prompt`, and `requestPermission` is what shows the\n * browser's own question. A handle with neither method is from a\n * browser that does not ask, and is taken as granted.\n */\nasync function permitted(handle: ShellFileHandle, mode: 'read' | 'readwrite'): Promise<boolean> {\n if (handle.queryPermission === undefined) {\n return true;\n }\n if ((await handle.queryPermission({ mode })) === 'granted') {\n return true;\n }\n return handle.requestPermission !== undefined && (await handle.requestPermission({ mode })) === 'granted';\n}\n\nfunction pickerTypes(accept: readonly ShellFileType[]): PickerOptions {\n if (accept.length === 0) {\n return {};\n }\n return {\n types: accept.map(type => ({ description: type.description, accept: { [type.mediaType]: [...type.extensions] } })),\n excludeAcceptAllOption: false\n };\n}\n\nfunction inputAccept(accept: readonly ShellFileType[]): string {\n return accept.flatMap(type => [...type.extensions, type.mediaType]).join(',');\n}\n\nfunction ok(fields: Partial<Pick<ShellFileResult, 'files' | 'saved' | 'recent'>>): ShellFileResult {\n return { outcome: 'ok', files: [], saved: null, recent: [], error: null, ...fields };\n}\n\nfunction cancelled(): ShellFileResult {\n return { outcome: 'cancelled', files: [], saved: null, recent: [], error: null };\n}\n\nfunction denied(): ShellFileResult {\n return { outcome: 'denied', files: [], saved: null, recent: [], error: 'Permission to use the file was refused.' };\n}\n\n/**\n * An exception, as an outcome.\n *\n * A picker closed without choosing throws `AbortError`, which is the\n * person deciding and is `cancelled`. `NotAllowedError` and\n * `SecurityError` are the browser refusing — no gesture, or a frame\n * that may not — and are `denied`. Anything else failed.\n */\nfunction failure(error: unknown): ShellFileResult {\n const name = typeof error === 'object' && error !== null && 'name' in error ? String(error.name) : '';\n if (name === 'AbortError') {\n return cancelled();\n }\n const message = error instanceof Error ? error.message : error === null ? null : String(error);\n if (name === 'NotAllowedError' || name === 'SecurityError') {\n return { ...denied(), error: message };\n }\n return { outcome: 'failed', files: [], saved: null, recent: [], error: message };\n}\n\n/** Every buffer in an answer, for a shell that posts it across and can move them. */\nexport function fileResultTransfer(result: ShellFileResult): ArrayBuffer[] {\n return result.files.map(file => file.bytes);\n}\n\n// ---------------------------------------------------------------------------\n// The browser's host\n// ---------------------------------------------------------------------------\n\nconst DATABASE = 'gesso-shell-files';\nconst HANDLES = 'handles';\n\n/**\n * Handles kept in IndexedDB, which is one of the few places a\n * `FileSystemFileHandle` can be put: it is structured-cloneable, and a\n * store of them is what the platform intends \"recent files\" to be.\n * The database is opened on first use, so a page that never touches a\n * file never creates one.\n */\nexport class IndexedDbHandleStore implements ShellHandleStore {\n private opened: Promise<IDBDatabase> | null = null;\n\n constructor(private readonly factory: IDBFactory) {}\n\n async all(): Promise<StoredFileHandle[]> {\n const db = await this.database();\n return request(db.transaction(HANDLES, 'readonly').objectStore(HANDLES).getAll()) as Promise<StoredFileHandle[]>;\n }\n\n async put(entry: { id?: number; handle: ShellFileHandle; name: string; used: number }): Promise<number> {\n const db = await this.database();\n const key = await request(db.transaction(HANDLES, 'readwrite').objectStore(HANDLES).put(entry));\n return key as number;\n }\n\n async delete(id: number): Promise<void> {\n const db = await this.database();\n await request(db.transaction(HANDLES, 'readwrite').objectStore(HANDLES).delete(id));\n }\n\n private database(): Promise<IDBDatabase> {\n this.opened ??= new Promise((resolve, reject) => {\n const open = this.factory.open(DATABASE, 1);\n open.onupgradeneeded = () => open.result.createObjectStore(HANDLES, { keyPath: 'id', autoIncrement: true });\n open.onsuccess = () => resolve(open.result);\n open.onerror = () => reject(open.error);\n });\n return this.opened;\n }\n}\n\nfunction request<T>(req: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\n/** A store that forgets, for a window with no IndexedDB. */\nexport class MemoryHandleStore implements ShellHandleStore {\n private readonly entries = new Map<number, StoredFileHandle>();\n private next = 1;\n\n all(): Promise<StoredFileHandle[]> {\n return Promise.resolve([...this.entries.values()]);\n }\n\n put(entry: { id?: number; handle: ShellFileHandle; name: string; used: number }): Promise<number> {\n const id = entry.id ?? this.next++;\n this.entries.set(id, { id, handle: entry.handle, name: entry.name, used: entry.used });\n return Promise.resolve(id);\n }\n\n delete(id: number): Promise<void> {\n this.entries.delete(id);\n return Promise.resolve();\n }\n}\n\n/**\n * The host a real window gives: its pickers where it has them, a file\n * input and a download where it does not.\n */\nexport function browserFilesHost(view: Window & typeof globalThis): ShellFilesHost {\n const pickers = view as unknown as {\n showOpenFilePicker?: ShellFilesHost['showOpenFilePicker'];\n showSaveFilePicker?: ShellFilesHost['showSaveFilePicker'];\n };\n const document = view.document;\n return {\n store: view.indexedDB === undefined ? new MemoryHandleStore() : new IndexedDbHandleStore(view.indexedDB),\n now: () => Date.now(),\n ...(pickers.showOpenFilePicker === undefined\n ? {}\n : { showOpenFilePicker: options => pickers.showOpenFilePicker!.call(view, options) }),\n ...(pickers.showSaveFilePicker === undefined\n ? {}\n : { showSaveFilePicker: options => pickers.showSaveFilePicker!.call(view, options) }),\n pickWithInput: (accept, multiple) =>\n new Promise(resolve => {\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = accept;\n input.multiple = multiple;\n input.addEventListener('change', () => resolve(Array.from(input.files ?? [])), { once: true });\n // `cancel` is what a dismissed picker fires, where the browser\n // fires anything at all; without it a dismissal is a promise\n // that waits, which is what every file input has always done.\n input.addEventListener('cancel', () => resolve(null), { once: true });\n input.click();\n }),\n download: (name, blob) => {\n const url = view.URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = name;\n anchor.click();\n // Revoked after the click has been handled rather than at once:\n // a URL revoked synchronously is one some browsers never fetch.\n view.setTimeout(() => view.URL.revokeObjectURL(url), 60_000);\n }\n };\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 /**\n * A paste aimed at the canvas rather than at the field.\n *\n * When no editable has focus this proxy blurs and hands focus back\n * to the canvas, so the browser fires `paste` there — and the\n * listener above, which is on the textarea, never hears it. The\n * text was dropped before it reached the application at all, which\n * is half of why a grid could not be pasted into; the other half\n * was `UiEditingController.paste` having nowhere to send it.\n *\n * The two listeners cannot both fire for one paste: the event goes\n * to whichever element has focus, and only one of them ever does.\n */\n const onCanvasPaste = (event: Event): void => {\n if (this.doc.activeElement === textarea) {\n return;\n }\n onPaste(event);\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 this.canvas.addEventListener('paste', onCanvasPaste);\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 this.canvas.removeEventListener('paste', onCanvasPaste);\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 * Text pasted while a mirrored element holds focus.\n *\n * The same reasoning as the keys, and the same surprise: a paste\n * onto a grid lands on the mirror's element, not on the canvas and\n * not on the editing proxy — so a shell listening on either hears\n * nothing at all and the text is dropped before the application\n * sees it.\n */\n paste?(text: string): 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 const onPaste = (event: ClipboardEvent): void => {\n const text = event.clipboardData?.getData('text/plain') ?? '';\n if (text.length === 0) {\n return;\n }\n event.preventDefault();\n this.sink.paste?.(text);\n };\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 this.container.addEventListener('paste', onPaste);\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 this.container.removeEventListener('paste', onPaste);\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","/**\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","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 { attachFileDrop } from '../fileDrop';\nimport { browserFilesHost, fileResultTransfer, ShellFiles } from '../shellFiles';\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 * Cancel the browser's default for any key this answers true for —\n * Ctrl/Cmd+S for an app with a Save of its own, Ctrl/Cmd+O for one\n * with an Open. `interceptFind` for any shortcut, on its terms: the\n * shell has to decide before the worker has heard of the key, so the\n * decision is the application's, made here on the main thread, and a\n * predicate rather than a list so it can say \"Ctrl or Cmd\" in one\n * line.\n *\n * Called for every keydown that reaches the shell, so it should be a\n * comparison and nothing more.\n */\n interceptKey?: (event: KeyboardEvent) => 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 /** Pickers and remembered handles, made on the first file request. */\n private files: ShellFiles | 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 createSyncApp(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 // From the proxy, a printable key's text follows it as a\n // `beforeinput`; see `forwardKeyDown`.\n keyDown: event => this.forwardKeyDown(event, true),\n keyUp: event => this.forwardKeyUp(event)\n });\n if (this.options.accessibility !== false) {\n // The off-screen DOM an assistive technology reads. Keys and\n // pastes are forwarded from it for the same reason they are\n // forwarded from the proxy: while the app has focus, the element\n // holding it is 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 paste: text => this.post({ type: 'paste', text })\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 /**\n * A key, to the worker, saying whether its text will follow it.\n *\n * Only the proxy turns a key into text. The proxy takes DOM focus when\n * the worker reports a focused editable, which is a round trip — so a\n * key typed in the gap, after an editable has the focus in the worker\n * and before the proxy has it here, reaches the canvas, and the text\n * that key would have produced never exists. The worker is told, and\n * inserts the key itself. Typing quickly into a spreadsheet cell that\n * the first letter opens lives in exactly that gap.\n */\n private forwardKeyDown(event: KeyboardEvent, viaProxy = false): 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)) || this.options.interceptKey?.(event) === true) {\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({\n type: 'keyDown',\n key: event.key,\n modifiers: modifiersFrom(event),\n at: epochFromEvent(event),\n textFollows: viaProxy\n });\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 === 'file') {\n // Pickers, handles and IndexedDB are all the window's. The answer\n // is posted on every path — `perform` never rejects — and a file\n // read goes back with its buffer moved rather than copied.\n this.files ??= new ShellFiles(browserFilesHost(window));\n void this.files\n .perform(message.request)\n .then(result => this.post({ type: 'fileResult', id: message.id, result }, fileResultTransfer(result)));\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, transfer: Transferable[] = []): 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, transfer);\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 // Files dragged in from the desktop. Their bytes are moved to the\n // render worker rather than copied: a dropped file is the largest\n // thing this shell ever posts, and it has no further use for it.\n const detachFileDrop = attachFileDrop(canvas, toLocal, (message, transfer) => this.post(message, transfer));\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 detachFileDrop();\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 { 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 whose UI work happens off the main\n * thread.\n *\n * A class reference cannot cross postMessage, so the root component\n * has to already be inside the worker that renders it: this side names\n * the workers and nothing else. With `gesso-vite-plugin` in the Vite\n * config the shell names none of them either —\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 * The single-thread configuration is `createSyncApp`, and it is a\n * second function rather than a second overload because this one must\n * not be able to reach the engine — see there for what that is worth\n * in kilobytes.\n */\nexport function createApp(options?: CreateAppOptions): WorkerApp {\n if (!isWorkerAppOptions(options)) {\n throw new Error(\n 'createApp() builds the worker configuration and was given a component. Use createSyncApp(Root) from ' +\n 'gesso-framework for the single-thread one, or pass options: createApp({ renderWorker: () => ... }).'\n );\n }\n if (options?.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(options as WorkerAppOptions);\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 *\n * It survives the split into two functions because it is what tells\n * somebody who called the wrong one which one they wanted, and a\n * string costs nothing to bundle.\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 type { UiNodeReport } from './NodeReport';\nimport type { FrameMetrics } from './GessoRuntime';\nimport type { Patch } from '../channel/StorePatch';\nimport type { RuntimeErrorSource } from './worker/RenderWorkerProtocol';\n\n/**\n * What a devtools panel says to a running application and what it\n * hears back, as plain data.\n *\n * The node inspector answered \"what is the thing under the pointer\";\n * a panel docked outside the page asks the questions a DOM inspector\n * answers instead: what is the whole tree, what is this node I picked\n * from it, outline it for me, and what did the workers log. Every\n * answer here is built where the tree is and crosses a `postMessage`\n * as strings and numbers, for the same reason `UiNodeReport` does:\n * nothing in the render thread can be handed out, and a panel that\n * lives in an extension is two message hops away from it anyway.\n *\n * The same vocabulary serves both configurations. `WorkerApp` carries\n * it over the render worker protocol; `GessoApp`, with the runtime in\n * the same thread, answers the requests directly. A panel cannot tell\n * which it is talking to, which is what lets one panel serve both.\n */\n\n/** One node of the tree, as a panel lists it. */\nexport interface UiTreeNode {\n readonly id: string;\n /** `UiNodeType`, as its string value. */\n readonly type: string;\n /**\n * The component this node is the anchor of, when it is one.\n *\n * Only the anchor carries the name. A panel wanting \"which component\n * rendered this node\" reads it off the nearest ancestor that has\n * one, which is the same walk `UiNodeReport.owners` makes and\n * costs the snapshot nothing per node.\n */\n readonly component?: string;\n /** A text node's text, shortened, so the tree reads like the screen. */\n readonly text?: string;\n /**\n * Live subscriptions the node holds: bound properties, reactive\n * children, event handlers. Absent when it holds none, which is most\n * nodes.\n *\n * On the tree rather than in a report of its own because the answer\n * a subscription view needs is per component, and the tree is what\n * says which component a node belongs to. A panel adds them up\n * towards the nearest ancestor carrying a `component`, and a leak is\n * the count that climbs while the shape stays still.\n */\n readonly subscriptions?: number;\n readonly children: readonly UiTreeNode[];\n}\n\n/** The whole tree at one moment. */\nexport interface UiTreeSnapshot {\n readonly root: UiTreeNode;\n /** How many nodes the snapshot holds, so a panel can say so without counting. */\n readonly nodes: number;\n /**\n * Live subscriptions across the whole graph, including the runtime's\n * own nodes that the snapshot does not list. The number to watch\n * while doing nothing.\n */\n readonly subscriptions: number;\n}\n\n/**\n * What was being answered when an action was recorded\n *.\n *\n * The barrier carries no request id: a command goes up, patches come\n * down, and nothing in the protocol ties the second to the first. So\n * the tie is made where both are seen, by the recorder, and it starts\n * from the one thing that is unambiguous — the input being dispatched\n * when the command was sent. Everything the input caused carries the\n * same id, and a panel groups by it instead of comparing timestamps\n * across three threads.\n */\nexport interface ActionCause {\n /** Rising from 1, within one recorder. */\n readonly id: number;\n /** The input that started it: `pointerUp (412, 233)`, `keyDown Enter`. */\n readonly label: string;\n}\n\ninterface ActionEntryBase {\n /** Position on the timeline. Never reused, never renumbered. */\n readonly seq: number;\n /** `performance.now()` on the thread that recorded it. */\n readonly at: number;\n /** The input this entry belongs to, when it belongs to one. */\n readonly cause?: ActionCause;\n}\n\n/** A command a view sent across the barrier. */\nexport interface CommandEntry extends ActionEntryBase {\n readonly kind: 'command';\n readonly channel: string;\n readonly command: string;\n readonly payload: unknown;\n}\n\n/** A batch of patches the owning thread sent back. */\nexport interface PatchEntry extends ActionEntryBase {\n readonly kind: 'patch';\n readonly channel: string;\n readonly patches: readonly Patch[];\n /** The projections this batch touched, in the order first touched. */\n readonly keys: readonly string[];\n}\n\n/** A channel error, kept on the timeline so it has a position on it. */\nexport interface ChannelErrorEntry extends ActionEntryBase {\n readonly kind: 'error';\n readonly channel: string;\n readonly message: string;\n}\n\n/**\n * The frame that drew what came before it.\n *\n * The last link in the chain, and the reason it is an entry rather\n * than a field on the patches: a patch batch is recorded when it\n * arrives, which is before the frame that applies it exists. Stamping\n * it later would mean amending an entry a panel has already been sent.\n * A row on the timeline says the same thing by position, and says it\n * once.\n *\n * Recorded only for a frame that has something to close, so an idle\n * application's ticker does not fill the log with frames.\n */\nexport interface FrameEntry extends ActionEntryBase {\n readonly kind: 'frame';\n /** `UiFrame.id`, the same number `FrameMetrics.frame` carries. */\n readonly frame: number;\n}\n\n/** One line of a store action log. */\nexport type ActionEntry = CommandEntry | PatchEntry | ChannelErrorEntry | FrameEntry;\n\n/** One `console.*` call made in a worker, forwarded with its thread named. */\nexport interface ConsoleEntry {\n readonly thread: 'render' | 'app';\n readonly level: 'log' | 'info' | 'warn' | 'error' | 'debug';\n /** Each argument printed as one string; an Error carries its stack. */\n readonly args: readonly string[];\n /** `Date.now()` in the thread that logged. */\n readonly at: number;\n}\n\n/** What a panel asks. */\nexport type DevtoolsRequest =\n /** One tree snapshot, now. */\n | { kind: 'tree' }\n /**\n * A snapshot after every frame that changed the shape or the text\n * of the tree, until turned off. Frames that only moved boxes send\n * nothing, because the tree a panel lists did not change.\n */\n | { kind: 'watchTree'; enabled: boolean }\n /** A report on one node, now; `null` comes back for an id the tree no longer has. */\n | { kind: 'inspect'; id: string }\n /**\n * Keep one node's report fresh: a report now, and another after any\n * frame that changed it, until a different node (or null) is\n * selected. This is the panel's selection.\n */\n | { kind: 'select'; id: string | null }\n /**\n * Outline a node on the canvas, the way the DOM inspector outlines an\n * element hovered in its tree. Independent of the layout inspector's\n * own toggle, so a panel can point at a node without turning on the\n * heatmap.\n */\n | { kind: 'highlight'; id: string | null }\n /** Forward `console.*` from the workers, with the thread named. */\n | { kind: 'console'; enabled: boolean }\n /** A `frame` event per frame, until turned off: the profiler's feed. */\n | { kind: 'watchFrames'; enabled: boolean }\n /**\n * The layout inspector's own toggle: hover boxes and the measure\n * heatmap on the canvas, and a `hover` event for the node under the\n * pointer. The panel's \"pick from the canvas\".\n */\n | { kind: 'inspector'; enabled: boolean }\n /**\n * Writes a property on a node the panel picked, which is the half of\n * the addressed channel the inspector deferred: `select` names a\n * node to read, this one names a node to change.\n *\n * The write goes through the graph like any other, so the override\n * cascade, the equality check and the dirty marking all apply, and\n * the frame that follows is an ordinary frame. Two consequences to\n * be honest about: a bound property is overwritten by its stream's\n * next emission, and a value the element declares again on the next\n * rebuild comes back. This edits the tree, not the code.\n *\n * `value` is plain data. `null` removes the property, which is what\n * lets a value put back an inherited one.\n */\n | { kind: 'setProp'; id: string; name: string; value: unknown }\n /** Frame and channel spans in the browser's own profiler, until turned off. */\n | { kind: 'marks'; enabled: boolean };\n\n/** What the application answers, and volunteers while something is watched. */\nexport type DevtoolsEvent =\n | { kind: 'tree'; tree: UiTreeSnapshot }\n | { kind: 'report'; id: string; report: UiNodeReport | null }\n | { kind: 'console'; entry: ConsoleEntry }\n | { kind: 'frame'; metrics: FrameMetrics }\n /**\n * One line of a store action log recorded on the thread the ports\n * are on.\n *\n * A log in the page reaches a panel through the devtools hook, which\n * has the log itself to read. A log in the render worker has no such\n * route: the shell holds neither end of a channel there, by design\n *, so the entries come out the way every other\n * answer from that thread does, as plain data on the devtools\n * channel.\n */\n | { kind: 'action'; entry: ActionEntry }\n /** The node under the pointer while the inspector is on; null when none, or when it was turned off. */\n | { kind: 'hover'; report: UiNodeReport | null }\n /**\n * An error the render worker reported to its shell. Only a worker\n * shell sends these: with the runtime in the page, an error is an\n * ordinary page error and the page's console already has it.\n */\n | { kind: 'error'; message: string; stack?: string; source: RuntimeErrorSource };\n\n/** Longer text than this is cut in a tree snapshot; the report has the whole of it. */\nexport const TREE_TEXT_LIMIT = 40;\n\n/** A text node's text as the tree shows it. */\nexport function treeText(text: unknown): string | undefined {\n if (typeof text !== 'string' || text === '') {\n return undefined;\n }\n const flat = text.replace(/\\s+/g, ' ');\n return flat.length > TREE_TEXT_LIMIT ? `${flat.slice(0, TREE_TEXT_LIMIT - 1)}…` : flat;\n}\n","import { type Observable, Subject } from 'rxjs';\n\nimport { linear } from 'gesso-core';\n\nimport { computed, type ComputedCell } from '../computed';\nimport { internalState } from '../InternalState';\nimport type { AnimationService } from './AnimationService';\nimport { epochNow } from './worker/RenderWorkerProtocol';\n\n/**\n * Where a playback stands, as the shell last reported it.\n *\n * A *sample*, not a stream: the shell sends one whenever the element\n * changes state and otherwise about once a second, and `AudioService`\n * moves `position` forward between samples on the animation driver.\n * That keeps the message rate at one a second while a seek bar moves\n * every frame, the same arrangement video time uses.\n */\nexport interface AudioSample {\n readonly status: AudioStatus;\n /** Seconds into the track, as of `at`. */\n readonly position: number;\n /** Seconds, or `NaN` until the element knows. */\n readonly duration: number;\n /** Seconds: the end of the buffered range that holds `position`. */\n readonly buffered: number;\n /** When the shell took the sample, on the epoch clock both threads share (`epochNow`). */\n readonly at: number;\n /** What went wrong, for `error`, or why a play was refused, for `paused`. */\n readonly error?: string;\n}\n\n/**\n * - `idle`: nothing loaded.\n * - `loading`: a source is set and the element is fetching or\n * stalled; the position does not advance.\n * - `playing`, `paused`, `ended`: what they say.\n * - `error`: the element gave up on this source.\n */\nexport type AudioStatus = 'idle' | 'loading' | 'playing' | 'paused' | 'ended' | 'error';\n\n/** What the render thread asks the shell's audio element to do. */\nexport type AudioRequest =\n | { readonly type: 'load'; readonly src: string; readonly autoplay: boolean }\n /**\n * Start buffering the source that will play next, on a second\n * element, so the change when it arrives is gapless.\n *\n * A hint and not a command: nothing plays, nothing is reported, and a\n * `load` of some other source ignores it. A `load` of exactly this\n * source is what redeems it. An empty string clears it.\n */\n | { readonly type: 'preload'; readonly src: string }\n | { readonly type: 'play' }\n | { readonly type: 'pause' }\n | { readonly type: 'seek'; readonly seconds: number }\n | { readonly type: 'volume'; readonly level: number }\n | { readonly type: 'metadata'; readonly metadata: AudioMetadata | null };\n\n/**\n * What the platform's own controls asked for: the keyboard's media\n * keys, the lock screen, the OS media overlay. Play and pause are\n * carried out by the shell on the element itself and reported back as\n * a sample; `next` and `previous` mean nothing to an element, so they\n * come here for the application to answer.\n */\nexport type AudioAction = 'play' | 'pause' | 'next' | 'previous';\n\n/** What the OS shows for the playing track, through the Media Session API. */\nexport interface AudioMetadata {\n readonly title: string;\n readonly artist: string;\n readonly album?: string;\n /** A square picture's url. */\n readonly artwork?: string;\n}\n\n/** The sample, with the position moved to now and the source it is for. */\nexport interface AudioState {\n readonly status: AudioStatus;\n readonly position: number;\n readonly duration: number;\n readonly buffered: number;\n readonly src: string | null;\n readonly error?: string;\n}\n\nconst IDLE: AudioSample = { status: 'idle', position: 0, duration: NaN, buffered: 0, at: 0 };\n\n/** How often the extrapolated position is written between samples. Ten a second is smooth on a seek bar and cheap. */\nconst POSITION_STEP_MS = 100;\n/** How far to extrapolate a playback whose duration the element has not reported yet. */\nconst UNKNOWN_DURATION_HORIZON_S = 24 * 60 * 60;\n\n/**\n * Sound, as a component reaches it.\n *\n * No worker can make a sound: `HTMLAudioElement` and `AudioContext`\n * exist only on a thread with a window. So the element lives on the\n * shell, as a *sink* behind a handful of messages, in the way the shell\n * already answers `clipboard` and `openUrl`, and this store is its\n * client on the render thread. Every runtime registers one, like\n * `ShellService`; being a store keeps the rule that components reach\n * the outside world through actions only, and gives a desktop shell one\n * place to bind a native player.\n *\n * Time is the interesting part. The shell samples the element on state\n * changes and once a second; between samples `position` is driven by a\n * linear tween on the animation driver, so the seek bar moves at the\n * frame rate while the barrier carries one small message a second. A\n * sample stops the tween and restarts it from the truth, which is also\n * what corrects any drift. Nothing here decides what to play next: the\n * application does, from `state` and `actions`.\n */\nexport class AudioService {\n private handler: ((request: AudioRequest) => void) | null = null;\n private animations: AnimationService | null = null;\n private readonly sample = internalState<AudioSample>(IDLE);\n private readonly source = internalState<string | null>(null);\n private readonly position = internalState(0);\n private readonly actionSubject = new Subject<AudioAction>();\n\n /**\n * The playback, with the position moved to now.\n *\n * A cell rather than a stream, which is what lets a screen write\n * `computed(() => audio.state.value.status === 'playing')` instead of\n * listing it as a source of a `derive`. It is the\n * same expression `current` is, and the same three cells underneath;\n * being a `computed` is what gives it a current value as well as a\n * stream, without a second copy of the arithmetic.\n */\n readonly state: ComputedCell<AudioState> = computed(() => this.current, { label: 'AudioService.state' });\n\n /** What the platform's media controls asked for. */\n readonly actions: Observable<AudioAction> = this.actionSubject.asObservable();\n\n /** The current state, for code that needs it without subscribing. */\n get current(): AudioState {\n const sample = this.sample.value;\n return {\n status: sample.status,\n position: this.position.value,\n duration: sample.duration,\n buffered: sample.buffered,\n src: this.source.value,\n ...(sample.error === undefined ? {} : { error: sample.error })\n };\n }\n\n /** Installed by the runtime; a request with no handler is dropped. */\n setHandler(handler: ((request: AudioRequest) => void) | null): void {\n this.handler = handler;\n }\n\n /** Installed by the runtime: the driver `position` moves on between samples. */\n setAnimations(animations: AnimationService | null): void {\n this.animations = animations;\n }\n\n /**\n * Called by the runtime when the shell reports the element.\n *\n * Not for applications: the shell is the only thing that knows the\n * answer, and `state` is how an application hears about it.\n */\n applySample(sample: AudioSample): void {\n this.sample.value = sample;\n const elapsed = sample.status === 'playing' ? Math.max(0, (epochNow() - sample.at) / 1000) : 0;\n const known = Number.isFinite(sample.duration);\n const now = known ? Math.min(sample.position + elapsed, sample.duration) : sample.position + elapsed;\n this.animations?.stop(this.position);\n this.position.value = now;\n if (sample.status !== 'playing' || this.animations === null) {\n return;\n }\n const target = known ? sample.duration : now + UNKNOWN_DURATION_HORIZON_S;\n if (target <= now) {\n return;\n }\n // A readout, not motion: it keeps moving under reduced motion, as\n // the spinner does, because a seek bar that stood still would say\n // the music had stopped.\n this.animations.animate(this.position, target, {\n duration: (target - now) * 1000,\n easing: linear,\n stepMs: POSITION_STEP_MS,\n reducedMotion: 'keep'\n });\n }\n\n /** Called by the runtime when the platform's controls act. Not for applications. */\n applyAction(action: AudioAction): void {\n this.actionSubject.next(action);\n }\n\n /**\n * Loads a source and, by default, starts it.\n *\n * Reported as `loading` at once rather than waiting for the shell,\n * so a screen that pressed play shows something before the round\n * trip comes back.\n */\n load(src: string, options: { readonly autoplay?: boolean } = {}): void {\n this.animations?.stop(this.position);\n this.source.value = src;\n this.position.value = 0;\n this.sample.value = { status: 'loading', position: 0, duration: NaN, buffered: 0, at: epochNow() };\n this.handler?.({ type: 'load', src, autoplay: options.autoplay ?? true });\n }\n\n /**\n * Says what will play next, so the shell can have it buffered before\n * it is asked for.\n *\n * Nothing about the current playback changes, and nothing is\n * reported: this only speaks to a second element the shell keeps. A\n * later `load` of the same source is answered from what was\n * buffered, and a `load` of anything else discards it. Pass an empty\n * string when nothing follows.\n */\n preload(src: string): void {\n this.handler?.({ type: 'preload', src });\n }\n\n play(): void {\n this.handler?.({ type: 'play' });\n }\n\n pause(): void {\n this.handler?.({ type: 'pause' });\n }\n\n /**\n * Moves to a position. Written locally at once, so a dragged seek bar\n * follows the finger rather than the round trip; the element's own\n * `seeked` sample confirms it.\n */\n seek(seconds: number): void {\n this.animations?.stop(this.position);\n this.position.value = Math.max(0, seconds);\n this.handler?.({ type: 'seek', seconds: Math.max(0, seconds) });\n }\n\n /** 0 to 1. */\n setVolume(level: number): void {\n this.handler?.({ type: 'volume', level: Math.min(1, Math.max(0, level)) });\n }\n\n /** What the OS shows for the track; null clears it. */\n setMetadata(metadata: AudioMetadata | null): void {\n this.handler?.({ type: 'metadata', metadata });\n }\n}\n","import { internalState, type InternalState } from '../InternalState';\nimport type { RouteDefinition } from './RouteDefinition';\n\n/**\n * What a route remembers while its screen is not there.\n *\n * A screen is built when its route matches and destroyed when it stops\n * matching, so everything a screen keeps in its body goes with it: the\n * scroll offset of the list, which row the keyboard was on, the text in\n * the filter field. Coming back lands at the top of the list, which is\n * the wrong place, and it also breaks a shared element, because a morph\n * is measured from where the element is *seen* and a list at the top is\n * not where it was left.\n *\n * Both applications solved this by putting those cells in module scope,\n * with the same comment on each explaining why. This\n * is that store, with an owner. What it buys over a module:\n *\n * - **A lifetime that is stated.** A cell here lives as long as the\n * router does and is dropped by `forget`, rather than as long as the\n * module registry, which is until the tab closes.\n * - **One store per runtime.** A module's cells are shared by every\n * runtime in the process, which two windows of the same desktop app\n * and two tests in the same file both are.\n * - **Names that cannot collide.** A key is scoped to a route, so two\n * screens may both remember `scroll` and mean different lists.\n *\n * It is a facility and not an architecture. The framework does not own\n * an application's data: anything that matters after\n * a reload, or that another part of the application acts on, is still\n * application state on a channel. What belongs here is the small,\n * screen-shaped remainder that only exists to put a screen back where it\n * was.\n */\nexport class RouteState {\n /**\n * `null` is the router's own scope: what belongs to the navigation\n * rather than to one screen. A `Map` keyed on the route object needs\n * no ids and no path strings, and a route that is redeclared is a\n * different route.\n */\n private readonly byRoute = new Map<RouteDefinition | null, Map<string, InternalState<unknown>>>();\n\n /**\n * The cell a route keeps under `key`, created with `initial` the first\n * time it is asked for and handed back unchanged afterwards.\n *\n * `initial` is therefore read once. A screen built a second time gets\n * the value the first one left, which is the whole point.\n */\n cell<T>(route: RouteDefinition | null, key: string, initial: T): InternalState<T> {\n let keys = this.byRoute.get(route);\n if (keys === undefined) {\n keys = new Map<string, InternalState<unknown>>();\n this.byRoute.set(route, keys);\n }\n const held = keys.get(key);\n if (held !== undefined) {\n return held as InternalState<T>;\n }\n const cell = internalState(initial, `${route === null ? 'router' : route.path}.${key}`);\n keys.set(key, cell as InternalState<unknown>);\n return cell;\n }\n\n /** Whether a route has remembered anything under this key yet. */\n has(route: RouteDefinition | null, key: string): boolean {\n return this.byRoute.get(route)?.has(key) ?? false;\n }\n\n /**\n * Drops everything a route remembered, so the next screen starts as\n * the first one did: signing out, or a list whose contents are no\n * longer the ones the offset was measured against.\n */\n forget(route: RouteDefinition | null): void {\n this.byRoute.delete(route);\n }\n\n /** Drops every route's memory. */\n clear(): void {\n this.byRoute.clear();\n }\n}\n","/**\n * Paths, patterns and the params inside them.\n *\n * A pattern is a path with `:name` in the places that vary:\n * `/mail/:folder/:id`. Nothing else is special — no regular\n * expressions, no optional segments, no repeats. That is deliberate:\n * every extra form is one more thing the type below has to model, and\n * a pattern whose params the compiler cannot name is a pattern that\n * gives up the one thing this router is for.\n *\n * A trailing `/*` is the exception, and only because a layout route\n * has to be able to say \"and anything below me\". It captures the rest\n * of the path as `rest`.\n */\n\n/** One piece of a parsed pattern. */\nexport type PatternSegment =\n | { readonly kind: 'static'; readonly text: string }\n | { readonly kind: 'param'; readonly name: string }\n | { readonly kind: 'rest' };\n\n/**\n * The params a pattern declares, as a type.\n *\n * `RouteParams<'/mail/:folder/:id'>` is `{ folder: string; id: string }`,\n * so `router.go(MailItem, { folder: 'inbox', id: '2' })` is checked, a\n * misspelled param is a compile error, and a missing one is too. This\n * is the whole reason routes are declared with full paths rather than\n * relative fragments: a relative fragment knows only its own segments,\n * and the params a screen actually receives include its parents'.\n */\nexport type RouteParams<Path extends string> = Prettify<ParamsOf<Path>>;\n\ntype ParamsOf<Path extends string> = Path extends `${infer Head}/${infer Rest}`\n ? ParamOfSegment<Head> & ParamsOf<Rest>\n : ParamOfSegment<Path>;\n\ntype ParamOfSegment<Segment extends string> = Segment extends `:${infer Name}`\n ? { [K in Name]: string }\n : Segment extends '*'\n ? { rest: string }\n : {};\n\n/** Flattens an intersection so hovering a param object shows its keys. */\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\n/** True when the pattern declares no params, so `go()` may omit them. */\nexport type HasNoParams<Path extends string> = keyof ParamsOf<Path> extends never ? true : false;\n\n/**\n * Splits a path into its segments, ignoring leading, trailing and\n * doubled slashes so `/mail/`, `mail` and `//mail` are one path.\n */\nexport function pathSegments(path: string): string[] {\n return path.split('/').filter(segment => segment.length > 0);\n}\n\n/** Parses a pattern once, at declaration time. */\nexport function parsePattern(pattern: string): PatternSegment[] {\n return pathSegments(pattern).map((segment, index, all) => {\n if (segment === '*') {\n if (index !== all.length - 1) {\n throw new Error(`Route pattern '${pattern}' has '*' before its last segment; a rest may only end a pattern.`);\n }\n return { kind: 'rest' as const };\n }\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n if (name.length === 0) {\n throw new Error(`Route pattern '${pattern}' has an unnamed ':' segment.`);\n }\n return { kind: 'param' as const, name };\n }\n return { kind: 'static' as const, text: segment };\n });\n}\n\n/**\n * Matches a parsed pattern against a path's segments.\n *\n * Returns the captured params, or null when the pattern does not\n * describe this path. An exact match is required: a pattern with\n * three segments does not match a path with four unless it ends in a\n * rest.\n */\nexport function matchPattern(\n segments: readonly PatternSegment[],\n path: readonly string[]\n): Record<string, string> | null {\n const params: Record<string, string> = {};\n for (let index = 0; index < segments.length; index++) {\n const segment = segments[index]!;\n if (segment.kind === 'rest') {\n params.rest = path.slice(index).join('/');\n return params;\n }\n const value = path[index];\n if (value === undefined) {\n return null;\n }\n if (segment.kind === 'static') {\n if (segment.text !== value) {\n return null;\n }\n continue;\n }\n params[segment.name] = decodeURIComponent(value);\n }\n return path.length === segments.length ? params : null;\n}\n\n/**\n * Fills a pattern's params back in, which is what makes a link out of\n * a route and a params object.\n */\nexport function buildPath(pattern: string, params: Readonly<Record<string, string>> = {}): string {\n const parts = parsePattern(pattern).map(segment => {\n if (segment.kind === 'static') {\n return segment.text;\n }\n if (segment.kind === 'rest') {\n return params.rest ?? '';\n }\n const value = params[segment.name];\n if (value === undefined) {\n throw new Error(`Route '${pattern}' needs a '${segment.name}' param.`);\n }\n return encodeURIComponent(value);\n });\n const path = parts.filter(part => part.length > 0).join('/');\n return `/${path}`;\n}\n\n/** A url split into the path the routes match and the query they carry. */\nexport interface ParsedUrl {\n readonly path: string;\n readonly query: Readonly<Record<string, string>>;\n}\n\n/**\n * Splits a url into path and query.\n *\n * Any hash is dropped: the shell decides whether the fragment is where\n * the app's url lives (see `shellHistory`), and by the time a url\n * reaches the router that decision has already been unwound.\n */\nexport function parseUrl(url: string): ParsedUrl {\n const withoutHash = url.split('#')[0] ?? '';\n const [rawPath = '', rawQuery = ''] = splitOnce(withoutHash, '?');\n const query: Record<string, string> = {};\n for (const pair of rawQuery.split('&')) {\n if (pair.length === 0) {\n continue;\n }\n const [key = '', value = ''] = splitOnce(pair, '=');\n query[decodeURIComponent(key)] = decodeURIComponent(value.replace(/\\+/g, ' '));\n }\n return { path: `/${pathSegments(rawPath).join('/')}`, query };\n}\n\n/** Joins a path and a query back into the url the history stores. */\nexport function formatUrl(path: string, query: Readonly<Record<string, string>> = {}): string {\n const pairs = Object.entries(query).map(([key, value]) => `${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 { CaretRect, UiEditingController, UiNode } from 'gesso-core';\n\n/**\n * The part of a text field an application has to ask about.\n *\n * Almost everything about an editable is reachable already: its text\n * is a property, its model is on the node, and its changes arrive as\n * events. Its *geometry* is not, and cannot be — where the caret sits\n * depends on the paragraph as it was laid out, which needs the\n * measurer, the resolved paint and the layout record. An application\n * that re-measured the text to work it out would be a second measurer\n * that must never disagree with the engine's, which is the kind of\n * pair that agrees until the day it does not.\n *\n * So it asks. This is the asking, and it is a service rather than a\n * free function because the controller lives in the input stack and\n * is out of a component's reach without something in front of it —\n * the same reason `FocusService` exists.\n *\n * What it is for: putting something *at* the caret. An autocomplete\n * list under the word being typed, a hint about the argument being\n * filled in, an error shown against the character it is about.\n */\nexport class EditingService {\n private controller: UiEditingController | null = null;\n\n /** Installed by the runtime; without one there is nothing to ask. */\n setController(controller: UiEditingController | null): void {\n this.controller = controller;\n }\n\n /**\n * Where the caret is inside a field, in the field's own coordinates.\n *\n * Relative to the node's border-box origin, with the field's own\n * scroll already applied: a popup placed under the caret adds the\n * node's position on screen and nothing else.\n *\n * Null when the node is not an editable, when it has not been laid\n * out yet, or before the runtime has wired the controller — all\n * three of which are \"ask again next frame\" rather than errors, and\n * all three of which happen during the frame a field first appears.\n */\n caretRectOf(node: UiNode | null): CaretRect | null {\n if (node === null || this.controller === null) {\n return null;\n }\n return this.controller.caretRectOf(node);\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 type { Size, TextMeasureRequest, TextMeasurer } from 'gesso-core';\n\n/**\n * How big a string would be, if it were drawn.\n *\n * Layout measures text constantly and an application could not ask —\n * the measurer is the runtime's, built for the layout engine, and\n * nothing handed it out. So anything that needs a width before there\n * is a node to read it from had no answer: a column autofitted to its\n * contents, a popover sized to the longest option, an axis label\n * decided before a chart is drawn, a truncation chosen rather than\n * discovered.\n *\n * The alternative every application reaches for is mounting the text\n * invisibly and reading its box back a frame later, which is a frame\n * of latency, a node in the tree and a race with the layout it is\n * trying to inform.\n *\n * **It is the runtime's own measurer**, not a second one. A width\n * from here is the width the layout engine would use, including the\n * cache it has already warmed and the invalidation it gets when a\n * declared face finishes loading — a service with its own measurer\n * would answer with the fallback font long after the real one had\n * arrived.\n */\nexport class TextService {\n private measurer: TextMeasurer | null = null;\n\n /**\n * Called by the runtime with the measurer it lays out through.\n *\n * Not for applications, on the terms `ShellService.applyColorScheme`\n * sets: the thread that draws is the only thing that knows how, and\n * a measurer an application could also install would be one the\n * next layout disagrees with.\n */\n setMeasurer(measurer: TextMeasurer | null): void {\n this.measurer = measurer;\n }\n\n /** Whether there is a measurer yet. False before the first frame. */\n get ready(): boolean {\n return this.measurer !== null;\n }\n\n /**\n * The size a string would take.\n *\n * Zero when there is no measurer, which is what a caller asking\n * before the runtime has drawn anything gets — and the honest\n * answer, since the font it would be measured in is not resolved\n * yet.\n */\n measure(request: TextMeasureRequest): Size {\n return this.measurer?.measure(request) ?? { width: 0, height: 0 };\n }\n\n /**\n * The width of one line, which is what sizing to content asks for.\n *\n * No `maxWidth`, so nothing wraps: the question is how wide the\n * text *wants* to be, and a wrapped answer would be the width it\n * was given rather than the width it needs.\n */\n widthOf(text: string, style: Omit<TextMeasureRequest, 'text' | 'maxWidth'>): number {\n return this.measure({ ...style, text, wrap: 'none' }).width;\n }\n}\n","import { EMPTY, type Observable } from 'rxjs';\n\nimport {\n AnimationDriver,\n UiSpring,\n createTween,\n 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 scrollRange,\n UiVirtualWindow,\n UiVirtualSheet,\n VIRTUAL_INDEX_PROP,\n VIRTUAL_LEAD_PROP,\n VIRTUAL_SHEET_PROP,\n VIRTUAL_WINDOW_PROP,\n type VirtualItemMeasure,\n DirtyFlags,\n propertyEffects,\n setPerformanceMarks,\n resolveCursor,\n type UiNode,\n 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 UiFileDropMessage,\n dragSessionFor,\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 RenderChanges,\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 ShellFileResult, type ShellRequest, type ShellStorageResult } from './ShellService';\nimport { AudioService, type AudioAction, type AudioRequest, type AudioSample } from './AudioService';\nimport { RouterService, type RouterRoutes } from '../router/RouterService';\nimport { FindService } from './FindService';\nimport { EditingService } from './EditingService';\nimport { FocusService } from './FocusService';\nimport { MediaService, type MediaOptions } from './MediaService';\nimport { FontService, type FontFamilyDeclaration } from './FontService';\nimport { TextService } from './TextService';\nimport { AnimationService } from './AnimationService';\nimport { FrameService } from './FrameService';\nimport { InputLatencyTracker } from './InputLatency';\nimport { SmoothScroller } from './SmoothScroller';\nimport { ChannelRegistry } from '../channel/ChannelRegistry';\nimport { ServiceRegistry } from '../service/ServiceRegistry';\n\n/**\n * The ordered work of one frame.\n *\n * Only five of the seven stages the design document imagined are real\n * phases. Component reconciliation and input dispatch are driven by\n * events, not by the clock: an observable emission reconciles its\n * subtree immediately and a pointer event routes immediately, each\n * marking nodes dirty so the *effects* land in the next frame. Giving\n * them frame slots would add latency and describe the system falsely.\n *\n * `ticks`, `patches` and `environment` run before the dirty set is\n * snapshotted, because all three produce dirt that this frame must\n * see. `layout` and `render` run against the snapshot.\n *\n * `ticks` is first because an animation's writes are inputs to\n * everything after them: a tick that changed a width has to be the\n * width this frame's virtualization measures against and this frame's\n * layout places from, and a tick that ran after `patches` would draw\n * one frame late for the whole life of the animation.\n */\nexport const UI_FRAME_PHASES = [\n 'ticks',\n 'patches',\n 'environment',\n 'virtualize',\n 'layout',\n 'semantics',\n 'render'\n] as const;\n\nexport type UiFramePhase = (typeof UI_FRAME_PHASES)[number];\n\nexport type FramePhaseTimings = Record<UiFramePhase, number>;\n\n/**\n * Guard against a root component that only ever renders another\n * component, which would otherwise recurse until the stack gives out.\n */\nconst MAX_ROOT_COMPONENT_DEPTH = 32;\n\n/**\n * Input controllers over the built tree.\n *\n * Exposed because input arrives differently per thread: from DOM\n * events through a UiPlatformAdapter on the main thread, and from\n * forwarded messages in a render worker. Both end up calling these.\n */\nexport interface RuntimeInput {\n readonly dispatcher: UiInputDispatcher;\n readonly pointer: UiPointerController;\n readonly wheel: UiWheelController;\n readonly keyboard: UiKeyboardController;\n readonly focus: UiFocusManager;\n /** Text editing: the shell's beforeinput, composition and paste land here. */\n readonly editing: UiEditingController;\n /** Selecting and copying text nobody types into. */\n readonly selection: UiSelectionController;\n /** Finding text in the app's own content; the find bar's engine. */\n readonly find: UiFindController;\n /** Dragging a scroll container's contents with a finger. */\n readonly touchScroll: UiTouchScroller;\n}\n\n/**\n * Which backend draws.\n *\n * **`canvas2d` is the default, and that is not the obvious answer.** A\n * GPU backend sounds like the faster one and on a scene of shapes and\n * text it is. On a scene dense with pictures it is currently not, and\n * the reason is one asymmetry rather than anything fundamental:\n * Canvas2D keeps a copy of each still at the size it is drawn (see\n * `ScaledImageCache`), while `WebGPUTextureCache` uploads a still at\n * the source's own size. A 480px cover shown at 164 is therefore about\n * eight times the texture on WebGPU, and a screen holding ninety of\n * them feels it. Measured on Segue's home screen, where the difference\n * was plain enough to notice without instrumenting anything.\n *\n * So the default is the one that is fast everywhere today, and the\n * faster ceiling is opt-in until the gap is closed. When\n * `WebGPUTextureCache` learns the drawn size the way its video path\n * already has, this should flip back.\n *\n * `auto` picks WebGPU where the browser has it and Canvas2D everywhere\n * else. The choice is made synchronously on whether `navigator.gpu`\n * exists, so an engine that never shipped WebGPU (WKWebView, WebKitGTK)\n * is on Canvas2D from the first frame rather than after a rejected\n * adapter request. A browser that has the entry point but cannot\n * produce an adapter or a device still falls back, asynchronously, once\n * that request fails.\n *\n * `canvas2d` pins the portable backend and never asks for an adapter.\n * `webgpu` asks for it and falls back the same way `auto` does, but\n * reports the fallback to the console, because a caller that named the\n * backend wants to know it did not get it.\n */\nexport type RendererChoice = RendererBackend | 'auto';\n\nexport interface GessoRuntimeOptions {\n /** Root component or element. */\n root: FrameworkChild;\n /** Canvas to draw into: HTMLCanvasElement, OffscreenCanvas, or a test double. */\n canvas: CanvasHost;\n /** The rendering backend. Defaults to `canvas2d`; see RendererChoice. */\n renderer?: RendererChoice;\n /**\n * A canvas for text measurement when the draw canvas is WebGPU's — a\n * canvas holds one context, so the measurer needs its own. Defaults\n * to a 1×1 OffscreenCanvas; tests pass a double.\n */\n measureCanvas?: CanvasHost;\n /**\n * The measurer layout and the renderers share, when the caller wants\n * one that does not come from a canvas.\n *\n * Only a test supplies it. A canvas measurer is the right answer\n * everywhere a canvas is real, but a test double's `measureText`\n * answers the same width for every font size, so text laid out\n * against one is not text: a heading and its caption come out the\n * same height. `gesso-testing` passes `CharacterCountTextMeasurer`\n * instead, which is proportional to the font size and identical on\n * every machine.\n */\n textMeasurer?: TextMeasurer;\n /**\n * The image resolver, icon rasteriser and video decoder the\n * `MediaService` should use.\n *\n * Supplied here rather than through the store afterwards because the\n * tree is built inside this constructor, and an `Image` in it asks\n * for its bitmap at that moment: a resolver installed after the\n * runtime exists would already have missed the first screen. An app\n * that fetches through its own stack, or one that has measured a\n * reason to decode in a worker of its own, passes it here.\n *\n * An application reaches this through `createSyncApp(Root).useMedia()`\n * on the single thread and `renderRoot(Root).useMedia()` in a render\n * worker. It is declared in the worker rather than in the shell\n * because a resolver is a function and no function crosses a\n * `postMessage`.\n */\n media?: MediaOptions;\n /**\n * The font families this runtime's text may name, with their faces\n * and fallback stacks. Each face is loaded into this thread's font\n * set, and the tree is measured again as it arrives; until then text\n * draws in the fallback. See `FontService`.\n */\n fonts?: readonly FontFamilyDeclaration[];\n /**\n * The runtime services this runtime's components may reach.\n *\n * Supplied only by a test wanting to substitute one; a runtime\n * registers the six it owns itself, because they are part of what a\n * runtime *is* rather than something an application configures.\n */\n services?: ServiceRegistry;\n /**\n * The routes a `RouterOutlet` in this tree resolves against.\n *\n * Optional, like every other application-shaped thing here: a\n * runtime with no routes still registers a `RouterService`, and it\n * simply matches nothing. Given here rather than set afterwards for\n * the same reason `media` is — the tree is built inside this\n * constructor, and an outlet in it asks for the current match at\n * that moment.\n */\n routes?: RouterRoutes;\n /**\n * The channels this runtime's components may reach.\n *\n * Attached elsewhere and handed in, because where a channel's data\n * lives is the application's decision, not the runtime's.\n */\n channels?: ChannelRegistry;\n /** Defaults to a timer clock, which is the only option inside a worker. */\n clock?: UiFrameClockFactory;\n /** Initial logical size. Callers normally follow with resize(). */\n width?: number;\n height?: number;\n dpr?: number;\n}\n\n/**\n * The whole UI, with no reference to the DOM.\n *\n * Owns the component runtime, retained graph, layout engine, input\n * controllers, scheduler and renderer. Everything here runs happily\n * in a Worker: the only things it cannot do for itself are obtain a\n * canvas and learn about size and input, which is exactly the split\n * between this class and its two hosts — GessoApp on the main thread\n * and renderRoot() in a render worker.\n */\n/** How many shell requests are held for a listener that has not attached, before the rest are dropped. */\nconst HELD_SHELL_REQUESTS = 256;\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 private frameErrorListener: ((message: string, stack?: string) => void) | null = null;\n /** WebGPU stage timings of the frame being rendered; null on Canvas2D. */\n private gpuTimings: GpuStageTimings | null = null;\n private inspectListener: ((report: UiNodeReport | null) => void) | null = null;\n /** The last report's explanation text, which is what tells two reports apart. */\n private lastInspection: string | null = null;\n private devtoolsListener: ((event: DevtoolsEvent) => void) | null = null;\n /** Whether a panel wants a tree snapshot after every frame that changed the tree. */\n private watchingTree = false;\n /**\n * Live subscriptions as of the last snapshot sent.\n *\n * A leak that adds no nodes changes nothing `frameChangedTree` looks\n * at, so a panel watching for one would never be told. This is the\n * second reason to resend, and it costs a count only while a panel\n * is attached.\n */\n private lastSubscriptions = -1;\n /** Whether a panel wants every frame's metrics. */\n private watchingFrames = false;\n /** The node a panel has selected, whose report is kept fresh; null for none. */\n private selectedId: string | null = null;\n /** The selected node's last report, serialised, which is what tells two apart. */\n private lastSelectedReport: string | null = null;\n private cursorListener: ((cursor: string | null) => void) | null = null;\n private lastCursor: string | null = null;\n private scrollabilityListener: ((scrollability: UiScrollability, scrollsAnything: boolean) => void) | null = null;\n private lastScrollability: UiScrollability = { up: false, down: false, left: false, right: false };\n /**\n * Undefined until the first report, so that one is always sent.\n *\n * The values it takes are ordinary booleans; the third state exists\n * only to make \"nothing has been said yet\" different from \"nothing\n * scrolls\", which matters because those two need opposite\n * `touch-action` on the shell's canvas and an app with no scroll\n * container at all would otherwise never send either.\n */\n private lastScrollsAnything: boolean | undefined = undefined;\n private editingListener: ((state: EditingState | null) => void) | null = null;\n /**\n * The selection controller, reachable before `input` is assigned:\n * the graph's node-removed listener is installed in the constructor\n * and fires for nodes taken out from under a live selection.\n */\n private selectionController: UiSelectionController | null = null;\n private findController: UiFindController | null = null;\n /** Reachable before `input` is assigned, for the same reason. */\n private readonly focusManager: UiFocusManager;\n /** The input stack's hit tester, kept for the inspector's questions about what lies where. */\n private hitTester!: UiHitTester;\n private readonly layoutNotifier: LayoutNotifier;\n /** Animates a wheel scroll; see `SmoothScroller`. */\n private readonly smoothScroller: SmoothScroller;\n /**\n * Whether the document showing this runtime is on screen.\n *\n * Assumed true until the shell says otherwise, because a runtime with\n * no shell — a spec, a headless graph — is never told and must draw.\n */\n private visible = true;\n /**\n * The running animations. Built as a field rather than in the body\n * of the constructor because the builder, the services and `buildRoot`\n * all need it, and `buildRoot` is where a component's first\n * `animate()` can happen.\n */\n private readonly animations = new AnimationDriver();\n /**\n * Which node currently answers to each shared-element name.\n *\n * Per runtime for the same reason the driver above is: several\n * runtimes share a worker in the playground, and a shared registry\n * would let one runtime's element morph from another's.\n */\n private readonly sharedElements = new UiSharedElements();\n private readonly focusNotifier = new FocusNotifier();\n private readonly environmentNotifier = new EnvironmentNotifier();\n 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 /** Shell requests made before `onShellRequest` gave them somewhere to go; see there. */\n private heldShellRequests: ShellRequest[] = [];\n private audioListener: ((request: AudioRequest) => void) | null = null;\n private caretTimer: ReturnType<typeof setTimeout> | null = null;\n private scrollbarTimer: ReturnType<typeof setTimeout> | null = null;\n 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 => {\n if (this.shellListener !== null && this.shellListener !== undefined) {\n this.shellListener(request);\n } else if (this.heldShellRequests.length < HELD_SHELL_REQUESTS) {\n this.heldShellRequests.push(request);\n }\n });\n // And sound, which is the shell's element and this thread's client.\n if (!this.services.has(AudioService)) {\n this.services.register(AudioService);\n }\n this.services.get(AudioService).setHandler(request => this.audioListener?.(request));\n // And the find session, so a component can drive the search the\n // browser's own find bar cannot do over a canvas.\n if (!this.services.has(FindService)) {\n this.services.register(FindService);\n }\n // And focus, which lives in the input stack and is therefore out\n // of a component's reach without a store in front of it.\n if (!this.services.has(FocusService)) {\n this.services.register(FocusService);\n }\n // And the caret's geometry, which is in the input stack for the\n // same reason and just as far out of reach.\n if (!this.services.has(EditingService)) {\n this.services.register(EditingService);\n }\n // And 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 // And measurement, which is the same measurer layout uses: a\n // width an application asks for has to be the width the engine\n // would lay the text out at, cache and font-loading and all.\n if (!this.services.has(TextService)) {\n this.services.register(TextService);\n }\n this.services.get(TextService).setMeasurer(this.textMeasurer);\n if (options.fonts !== undefined) {\n this.services.get(FontService).declare(options.fonts);\n }\n // And animation, whose running set must be per runtime for the\n // same reason the media caches are: the playground has several\n // runtimes in one worker, and a shared driver would tick a\n // disposed runtime's cells.\n if (!this.services.has(AnimationService)) {\n this.services.register(AnimationService);\n }\n this.services.get(AnimationService).setDriver(this.animations);\n // 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 scrollRange(record, axis === 'scrollY' ? 'y' : 'x');\n }\n );\n // And the router, whose matches hold route definitions, which hold\n // component classes: it could not cross a worker boundary if it\n // wanted to. The one thing it needs from the shell is the address\n // bar, which it reaches the same way the clipboard does.\n if (!this.services.has(RouterService)) {\n this.services.register(RouterService);\n }\n const router = this.services.get(RouterService);\n 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 * A frame that threw is reported and the clock keeps running.\n *\n * Both halves matter. Letting it escape used to report it — the\n * throw unwound into whatever was forwarding the tick — and stop\n * the application dead, because nothing re-armed the clock. An\n * application that merely stops looks exactly like a hang: the\n * last frame stays on screen and every click lands in a surface\n * nobody is listening to.\n */\n onFrameError: error => this.reportFrameError(error)\n });\n\n // An animation started between frames — from a click handler that\n // changes nothing else — has to arm the frame that will run its\n // first tick. Nothing in the graph is dirty at that moment, so\n // nothing else would.\n this.animations.setWakeListener(() => this.scheduler.wake());\n this.graph.setDirtyListener(() => this.scheduler.notifyDirty());\n this.graph.setNodeRemovedListener(node => {\n this.engine.detachNode(node);\n // Neither a selection nor a set of find matches can outlive its\n // nodes: virtualization and route changes both take them out from\n // under one.\n this.selectionController?.handleNodeRemoved(node);\n this.findController?.handleNodeRemoved(node);\n // Nor can focus: a closed dialog or a recycled row takes the\n // focused node with it.\n this.focusManager.handleNodeRemoved(node);\n this.layoutNotifier.handleNodeRemoved(node);\n this.smoothScroller.handleNodeRemoved(node);\n this.focusNotifier.handleNodeRemoved(node);\n this.environmentNotifier.handleNodeRemoved(node);\n });\n\n this.buildRoot(options.root);\n this.input = this.createInput();\n this.services.get(FocusService).setManager(this.input.focus);\n this.services.get(EditingService).setController(this.input.editing);\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 /**\n * Told when a frame throws, with the clock still running.\n *\n * Separate from `onListenerError` because the two are different\n * failures: a listener that throws has broken one interaction, and\n * a frame that throws has broken the thing that draws. It is\n * reported rather than fatal — see the scheduler — so somebody has\n * to be able to hear it.\n */\n onFrameError(listener: ((message: string, stack?: string) => void) | null): void {\n this.frameErrorListener = listener;\n }\n\n private reportFrameError(error: unknown): void {\n const message = error instanceof Error ? error.message : String(error);\n const stack = error instanceof Error ? error.stack : undefined;\n if (this.frameErrorListener !== null) {\n this.frameErrorListener(message, stack);\n return;\n }\n // eslint-disable-next-line no-console\n console.error('A Gesso frame threw. The frame was abandoned and the clock kept running.', error);\n }\n\n onListenerError(listener: ((message: string, stack?: string) => void) | null): void {\n this.dispatcher.onListenerError(\n listener === null\n ? null\n : (error, node, type) => {\n const message = error instanceof Error ? error.message : String(error);\n listener(\n `${message} (listener: ${type} on ${this.pathOf(node)})`,\n error instanceof Error ? error.stack : undefined\n );\n }\n );\n }\n\n private reportRendererError(message: string): void {\n if (this.rendererErrorListener !== null) {\n this.rendererErrorListener(message);\n return;\n }\n // eslint-disable-next-line no-console\n console.error(message);\n }\n\n /**\n * Replaces a WebGPU renderer that could not start, or lost its\n * device, with Canvas2D on the same canvas. The WebGPU path does not\n * touch the canvas until it has a device, so the 2D context is free.\n */\n private fallBackToCanvas2D(failed: UiRenderer): void {\n if (this.renderer !== failed) {\n return;\n }\n failed.dispose();\n this.canvasSurface = createCanvasSurface(this.canvas);\n this.renderer = new Canvas2DRenderer({ surface: this.canvasSurface });\n this.rendererState = 'canvas2d';\n this.renderer.resize(this.width, this.height, this.pixelRatio);\n this.requestRepaint();\n }\n\n 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 // What was asked before anyone was listening, handed over now. The\n // root is built in the constructor, so a component that reads a\n // stored preference as it mounts asks before a shell *can* have\n // attached — and a storage or file request is a promise waiting for\n // its answer, which a dropped request never gets.\n if (listener !== null) {\n const held = this.heldShellRequests;\n this.heldShellRequests = [];\n for (const request of held) {\n listener(request);\n }\n }\n }\n\n /**\n * Receives what components ask of the shell's audio element through\n * `AudioService`. Without a listener they are dropped, and the app\n * plays nothing, which is what a headless runtime should do.\n */\n onAudioRequest(listener: ((request: AudioRequest) => void) | null): void {\n this.audioListener = listener;\n }\n\n /** The shell reports its audio element; passed straight through to `AudioService`. */\n applyAudioSample(sample: AudioSample): void {\n this.services.get(AudioService).applySample(sample);\n }\n\n /** The platform's media controls acted; passed straight through to `AudioService`. */\n applyAudioAction(action: AudioAction): void {\n this.services.get(AudioService).applyAction(action);\n }\n\n /**\n * Where typed text comes from. A shell with an editing proxy delivers\n * it through `input.editing.beforeInput` and the composition methods,\n * so printable key presses must not be inserted a second time; with\n * `keys` (the default) they are all there is.\n */\n setTextInputSource(source: 'proxy' | 'keys'): void {\n this.input.editing.textFromKeys = source === 'keys';\n }\n\n /**\n * The page was hidden or shown. A hidden page stops the caret blink,\n * so a background tab with a focused field schedules no frames.\n *\n * The driver is told as well, and it is the half that matters to\n * what the page looks like: a hidden page goes on painting but runs\n * no animation frames, so anything the driver is holding would be\n * drawn frozen at whatever value it had reached. See\n * `AnimationDriver.setHidden` for why an entrance frozen at its\n * first value is a hole in the page rather than a paused animation.\n */\n /**\n * The shell says the surface entered or left fullscreen.\n *\n * Reported rather than inferred from the request that asked for it,\n * because the person can leave with Escape and a browser can refuse\n * to enter at all; a control that tracked its own last press would\n * then point the wrong way.\n */\n setFullscreen(active: boolean): void {\n this.services.get(ShellService).applyFullscreen(active);\n }\n\n setVisible(visible: boolean): void {\n this.input.editing.setVisible(visible);\n if (visible === this.visible) {\n return;\n }\n this.visible = visible;\n this.animations.setHidden(!visible);\n if (!visible) {\n return;\n }\n if (!this.started) {\n return;\n }\n // One frame on the way back, whether or not anything is dirty: an\n // animation the driver kept running while hidden is still in it,\n // and `scheduleAnimationTick` only re-arms from inside a frame.\n 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 /**\n * Reports what the shell did with a file request, settling the\n * promise `ShellService.requestFile` returned.\n */\n settleFile(id: number, result: ShellFileResult): void {\n this.services.get(ShellService).settleFile(id, result);\n }\n\n /** Whether the runtime is currently honouring a reduced-motion preference. */\n get reducedMotion(): boolean {\n return this.animations.isReducedMotion;\n }\n\n /** The appearance the shell last reported; `light` until it says otherwise. */\n get colorScheme(): ColorScheme {\n return this.services.get(ShellService).currentColorScheme;\n }\n\n /** The platform's insets the shell last reported; zeroes until it says otherwise. */\n get viewportInsets(): UiInsets {\n return this.viewportInsetValue;\n }\n\n /** Which shared-element names are currently held, for specs and devtools. */\n get sharedElementNames(): readonly string[] {\n return this.sharedElements.names;\n }\n\n /** `engine.explain` for any node, for tests and devtools. */\n explain(node: UiNode): LayoutExplanation {\n return this.engine.explain(node);\n }\n\n /**\n * Everything the inspector shows about one node, as plain data\n *.\n *\n * Built here rather than in `gesso-devtools` because every source\n * it reads is private to the render thread and most of it cannot\n * cross a thread boundary at all: the graph's bindings, the\n * builder's modifier sets, the resolver's component hosts. The\n * report is strings, so it can.\n */\n inspectNode(node: UiNode): UiNodeReport {\n const box = this.engine.visibleBox(node);\n return {\n id: node.id,\n type: node.type,\n box: { x: box.x, y: box.y, width: box.width, height: box.height },\n owners: this.ownersOf(node),\n props: this.propsOf(node),\n environment: this.environmentOf(node),\n modifiers: this.builder.modifiersFor(node)?.names ?? [],\n listens: this.dispatcher.listenerTypes(node),\n beneath: this.beneathAtPointer(node),\n semantics: this.semanticsOf(node),\n 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 /** Where the node is seen: scroll offsets and sticky shifts applied. */\n debugVisibleBox(node: UiNode): { x: number; y: number; width: number; height: number } {\n return this.engine.visibleBox(node);\n }\n\n /**\n * The node layout, hit testing and painting start from: a stack that\n * stretches the app root over the viewport with the overlay layer on\n * top of it.\n */\n layoutRoot(): UiNode {\n if (this.root === undefined) {\n throw new Error('App root has not been built.');\n }\n return this.root;\n }\n\n /**\n * Scrolls every scroll container above `node` just enough that the\n * node is inside its viewport, `padding` pixels from the nearest edge.\n * Nothing moves when it is already visible.\n */\n 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.frameErrorListener = null;\n this.scheduler.stop();\n // An animation holds its cell, and a cell holds whatever the\n // component that made it captured. A disposed runtime must not.\n this.animations.stopAll();\n this.sharedElements.clear();\n this.animations.setWakeListener(null);\n this.services.get(AnimationService).setDriver(null);\n this.services.get(AudioService).setAnimations(null);\n this.services.get(FindService).setController(null);\n this.services.get(FocusService).setManager(null);\n this.services.get(RouterService).setHistory(null);\n // Decoded bitmaps hold pixels; garbage collection is not prompt\n // about them, so they are closed rather than dropped.\n this.services.get(MediaService).dispose();\n this.services.get(FontService).dispose();\n this.graph.setEnvironmentChangedListener(null);\n this.graph.setDirtyListener(null);\n this.graph.setNodeRemovedListener(null);\n this.frameListener = null;\n // Its listeners are on the root node, which the dispatcher holds by\n // reference; a disposed runtime must not keep either alive.\n this.input.touchScroll.dispose();\n this.resolver.dispose();\n this.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 // The application's root, not the layout root that wraps it, for\n // the reason the keyboard's below gives: `overscrollBehavior` set\n // on the app's own top element has to be the one that is read.\n wheel: new UiWheelController(hitTester, this.dispatcher, scrollSink, () => this.appRoot ?? 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 /**\n * Files from outside the application, dragged over it or dropped.\n *\n * Handed to the tree's drag session, which turns them into a drag\n * like any other, so a zone that accepts `EXTERNAL_FILES` answers\n * the drop exactly as it answers one from inside. A drop that\n * arrives before there is a tree has nowhere to land and is dropped.\n */\n applyFileDrop(message: UiFileDropMessage): void {\n if (this.root === undefined) {\n return;\n }\n dragSessionFor(this.root).applyFileDrop(message);\n }\n\n applySemanticsAction(action: UiSemanticsAction): void {\n if (this.applyTextRunAction(action)) {\n return;\n }\n const node = this.graph.getNode(action.id);\n if (node === undefined || !this.semantics.has(action.id)) {\n // A stale id: the mirror acted on a node this frame removed.\n return;\n }\n 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: scrollRange(record, 'x'),\n maxScrollY: scrollRange(record, 'y'),\n horizontal: node.getProperty('direction') === 'row' || node.type === UiNodeType.Row,\n viewportWidth: record.width,\n viewportHeight: record.height\n };\n },\n scrollBy: (node, dx, dy, behavior): void => {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n return;\n }\n if (behavior === 'smooth') {\n // The smooth path adds to where the container is *going*, so\n // it needs the effective offset only as a starting point.\n if (dx !== 0) {\n this.smoothScroller.scrollBy(node, 'scrollX', dx, 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 if (node.properties.get(VIRTUAL_SHEET_PROP) instanceof UiVirtualSheet) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Advances every lazy list's window: reports the container's scroll\n * offset and viewport, and the measured extent of each mounted item,\n * then applies any scroll adjustment the window asks for to keep its\n * first item anchored while estimates above it are corrected.\n */\n private updateVirtualWindows(): void {\n for (const node of this.engine.scrollContainers()) {\n this.updateVirtualSheet(node);\n const window = node.properties.get(VIRTUAL_WINDOW_PROP);\n if (!(window instanceof UiVirtualWindow)) {\n continue;\n }\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n continue;\n }\n const column = window.axis === 'column';\n // A wheel may have written a newer offset than the record holds.\n const scrollProp = node.properties.get(column ? 'scrollY' : 'scrollX');\n const 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 * Advances a two-axis window, which needs both scroll offsets and\n * neither a measurement nor an anchor.\n *\n * A `LazySheet` is told its row height and column width, so the\n * offset of every cell is exact and the window is arithmetic. There\n * is nothing here to correct and so nothing to adjust the scroll by:\n * the rows and columns a scroll reveals are placed correctly on the\n * frame that reveals them.\n */\n private updateVirtualSheet(node: UiNode): void {\n const sheet = node.properties.get(VIRTUAL_SHEET_PROP);\n if (!(sheet instanceof UiVirtualSheet)) {\n return;\n }\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n return;\n }\n // A wheel may have written newer offsets than the record holds.\n const x = node.properties.get('scrollX');\n const y = node.properties.get('scrollY');\n sheet.update({\n scrollX: typeof x === 'number' ? x : rec.scrollX,\n scrollY: typeof y === 'number' ? y : rec.scrollY,\n width: rec.width,\n height: rec.height\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 // What changed, for a renderer that keeps pixels across frames\n // (Canvas2D's scroll layers). Only offered when it is the whole\n // truth: a node dirtied since the frame was collected — by a layout\n // listener, a focus settling, anything in the phases above — may\n // already show in what is drawn and is not in `frame`, so the\n // renderer is told nothing and draws from scratch.\n const changes: RenderChanges | undefined = this.graph.getDirtyNodes().isEmpty()\n ? { frame, geometryVersion: this.engine.geometryVersion }\n : undefined;\n this.phaseTimings.render = this.timePhase(\n () => this.renderer.isReady,\n () =>\n this.renderer.render(root, {\n layout: this.engine,\n text: this.textMeasurer,\n now: started,\n overlay: overlay?.shapes,\n changes\n })\n );\n 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 { 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 { shellFilesUnsupported, type ShellRequest } from './ShellService';\nimport { AudioSink } from './AudioSink';\nimport { attachFileDrop } from './fileDrop';\nimport { browserFilesHost, ShellFiles } from './shellFiles';\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 detachFileDrop: (() => void) | null = null;\n /** Pickers and remembered handles, made on the first file request. */\n private files: ShellFiles | 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.detachFileDrop?.();\n this.detachFileDrop = 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 // Files dragged in from the desktop, handed straight to the\n // runtime: there is no thread to cross, so nothing to transfer.\n this.detachFileDrop = attachFileDrop(\n canvas,\n (clientX, clientY) => {\n const box = canvas.getBoundingClientRect();\n return { x: clientX - box.left, y: clientY - box.top };\n },\n message => this.runtime.applyFileDrop(message)\n );\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 {\n action: action => this.runtime.applySemanticsAction(action),\n // A paste onto something that is not a text field lands here,\n // because the element holding focus while the app has it is\n // one of the mirror's and not the canvas.\n paste: text => this.runtime.input.editing.paste(text)\n },\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 } else if (request.type === 'file') {\n this.runtime.settleFile(request.id, shellFilesUnsupported('There is no document to reach files through.'));\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 === 'file') {\n if (view === null) {\n this.runtime.settleFile(request.id, shellFilesUnsupported('There is no window to reach files through.'));\n return;\n }\n // The same shell half the worker configuration runs, with no\n // thread to cross: the answer settles the promise directly.\n this.files ??= new ShellFiles(browserFilesHost(view as Window & typeof globalThis));\n void this.files.perform(request.request).then(result => this.runtime.settleFile(request.id, result));\n return;\n }\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 * createSyncApp(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';\n\n/**\n * Creates a Gesso application that runs on the calling thread.\n *\n * createSyncApp(AppRoot).useChannel(Catalog, { source }).mountSync('#app');\n *\n * For tests, headless rendering, and environments without\n * `OffscreenCanvas`. Channels resolve in-process, so the same contract\n * runs with no ports.\n *\n * **It is a separate function from `createApp` for a reason that shows\n * up on the wire.** A single-thread application has to have the layout\n * engine, both renderers and the hit-tester on the thread it is\n * mounted from, and reaches them by importing them. A worker\n * application must not: its shell creates a canvas, forwards input and\n * holds neither. While one `createApp` served both, the reference to\n * this builder was in every shell's module graph whether or not it was\n * ever called, and a bundler cannot know that `createApp(options)` and\n * `createApp(root)` take different halves of the package — so every\n * worker application shipped the whole engine to the thread whose\n * entire point is not to run it. Measured on gessosheet's shell: 662.9\n * kB raw, 169.3 kB gzipped, against 196.6 kB and 49.5 kB for the same\n * shell reaching the worker configuration directly.\n *\n * Two names cost one line in a single-thread application and take 120\n * kB off the main thread of every other kind. `check-bundle-size.ts`\n * is what keeps it that way.\n */\nexport function createSyncApp(root: FrameworkChild | ComponentType): GessoAppBuilder {\n return new GessoAppBuilder(root);\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, message.textFollows);\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 'fileResult':\n runtime.settleFile(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 'fileDrop':\n runtime.applyFileDrop(message);\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 === 'file') {\n this.host.postMessage({ type: 'file', id: request.id, request: request.request });\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 /**\n * A frame that threw, which the shell has to hear about.\n *\n * It used to arrive as `message`: the throw escaped the frame,\n * unwound into `receive`, and was caught there. That reported it\n * and killed the application, because nothing re-armed the clock\n * afterwards. Now the scheduler keeps drawing and the failure\n * comes back under its own name, which is also the more honest\n * one — the frame broke, not the message that happened to be\n * forwarding the tick.\n */\n this.runtime.onFrameError((message, stack) => {\n this.host.postMessage({ type: 'error', message, stack, source: 'frame' });\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":["measure","normalize","measure"],"sources":["../src/Component.ts","../src/service/ServiceRegistry.ts","../src/fanOut.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/worker/RenderWorkerProtocol.ts","../src/app/AudioSink.ts","../src/app/fileDrop.ts","../src/app/ShellService.ts","../src/app/shellFiles.ts","../src/app/EditingProxy.ts","../src/app/SemanticsMirror.ts","../src/app/shellStorage.ts","../src/app/mediaQuery.ts","../src/app/colorScheme.ts","../src/app/contrast.ts","../src/app/reducedMotion.ts","../src/app/shellHistory.ts","../src/app/fullscreen.ts","../src/app/worker/WorkerApp.ts","../src/app/createApp.ts","../src/app/DevtoolsProtocol.ts","../src/app/AudioService.ts","../src/router/RouteState.ts","../src/router/RoutePath.ts","../src/router/RouterService.ts","../src/app/FindService.ts","../src/app/EditingService.ts","../src/app/ScrollService.ts","../src/app/FocusService.ts","../src/app/MediaService.ts","../src/app/FontService.ts","../src/app/TextService.ts","../src/app/AnimationService.ts","../src/app/FrameService.ts","../src/app/InputLatency.ts","../src/app/SmoothScroller.ts","../src/app/GessoRuntime.ts","../src/app/GessoApp.ts","../src/app/GessoAppBuilder.ts","../src/app/createSyncApp.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 { BehaviorSubject, Observable, Subscription } from 'rxjs';\n\nimport { equalityOf, type Equality } from './derive';\nimport { trackRead, type ReadableCell } from './Input';\n\n/**\n * How many live keys with nothing ever released counts as a mistake.\n *\n * Generous: a grid's first window is hundreds of cells before a scroll\n * retires any of them, and a warning that fired there would be noise.\n */\nconst GROWING_WITHOUT_RELEASE = 2048;\n\n/** What a key is. A cell in a grid names itself `${row}:${column}`. */\nexport type FanKey = string | number;\n\n/**\n * What a key stands for, carried beside it.\n *\n * A key has to be a string or a number, because the registry looks one\n * up in a `Map` and identity is the wrong test for `${row}:${column}`.\n * That leaves a reader with a compound key parsing it back out on\n * every read, which is precisely what a grid cannot afford: gessosheet\n * keeps a cell's row and column beside its subjects for exactly this\n * reason, and its comment says so — \"parsing them back out of the key\n * was the only reason the key had a shape\".\n *\n * So the caller hands `for` whatever the key means, the registry\n * remembers it, and the reader is given it. `undefined` for a caller\n * whose key is already the whole answer.\n */\n\nexport interface FanOutOptions<S, V, D> {\n /**\n * What a key's cell holds before the source has said anything.\n *\n * Required, and not `undefined` by default, because the surfaces\n * this exists for mount their cells before the first patch arrives:\n * a grid that fills in as the window is served needs \"a cell waiting\"\n * to be a value it can draw rather than a hole. A function is given\n * the key, for a default that varies by position.\n */\n readonly initial: V | ((key: FanKey, datum: D) => V);\n /**\n * Which keys an emission could have changed, when the source knows.\n *\n * Without it every emission reads every live key and pushes the ones\n * that differ, which is correct and is what the natural spelling\n * costs too — so on its own the registry buys a cheaper mount and a\n * cheaper teardown and little else. This is where the frame goes.\n * A patch that touched one cell of ten thousand should cost one\n * read, and a source that already knows what it changed is the only\n * thing that can say so.\n *\n * Return `undefined` for \"I do not know\", which walks them all: a\n * scroll that replaces the window is honestly that case, and a\n * source that guessed narrow there would leave stale cells on the\n * screen. Keys that are not live are skipped, so the set may be as\n * loose as the source finds convenient.\n */\n readonly changed?: (next: S, previous: S | undefined) => Iterable<FanKey> | undefined;\n /**\n * How one key's new value is judged unchanged; `reference` by\n * default.\n *\n * The opposite default to `select` and `derive`, and the reason is\n * what this is for. Those project one value and run once per\n * emission, so comparing by content is cheap and usually right.\n * This runs the comparison once per live key per emission, on a\n * surface with thousands of them, which is the cost the registry\n * exists to remove — measured at 1.9 ms against 0.8 ms for ten\n * thousand keys on a source that republishes the lot, which is what\n * a scroll does.\n *\n * Pass `structural` when the reader *builds* a value rather than\n * passing one through: a projection that returns a fresh array or\n * object of the same shape is not a change, and a reference test\n * would push one to every cell on every emission. A reader that can\n * hand back the same object for an unchanged key — the sentinel\n * trick, one `EMPTY` for every cell with nothing in it — keeps the\n * cheap test and gets the right answer.\n */\n readonly equal?: Equality<V>;\n /** What to call these cells in a warning. */\n readonly label?: string;\n}\n\n/**\n * One key's value, as a cell.\n *\n * It is a cell and nothing else: bound to a prop it is an Observable\n * like any other, read with `.value` it is the current value, and it\n * composes inside a `computed`. What is different is where its value\n * comes from — it is *pushed* by the registry that made it rather than\n * pulled through a pipeline of its own, and that is the whole point.\n */\nexport class FanCell<V, D = unknown> extends Observable<V> implements ReadableCell<V> {\n /** @internal */\n readonly subject: BehaviorSubject<V>;\n\n constructor(\n initial: V,\n readonly key: FanKey,\n readonly datum: D,\n readonly label: string | undefined\n ) {\n const subject = new BehaviorSubject(initial);\n super(subscriber => subject.subscribe(subscriber));\n this.subject = subject;\n }\n\n get value(): V {\n trackRead(this);\n return this.subject.value;\n }\n}\n\n/**\n * Many cells from one source, with one subscription between them.\n *\n * `fanOut` is the answer to a shape that every large surface arrives\n * at and that `select`, `derive` and `computed` all get wrong: N\n * things on screen, each reading its own slice of one upstream value.\n *\n * const cells = fanOut(sheet.view.cells, (cells, key) => cells[key] ?? null);\n *\n * const cell = cells.for('12:4'); // a stable cell, made on demand\n * Text({ text: cell }); // bound like anything else\n * cells.release('12:4'); // when it leaves the screen\n *\n * **Why not a pipe per slice.** `source.pipe(map(v => v[key]))` per\n * cell is the natural spelling and it does not scale, in two ways that\n * compound. Every emission runs N pipelines, whatever changed; and\n * RxJS removes a subscriber from a Subject by scanning its observer\n * list, so tearing down a window of N cells is quadratic. Gessosheet\n * measured the first at 0.2 ms of median frame and five milliseconds\n * of input latency for one such binding per cell, and learned it three\n * separate times before writing the fan-out by hand.\n *\n * **What this does instead.** One subscription on the source for the\n * whole registry. Each emission walks the live keys — not the source's\n * keys, of which there may be millions — reads each one, and pushes\n * only the ones that changed. Each cell then has one or two observers\n * of its own, so its own teardown is the constant-time case that RxJS\n * is good at. Releasing a key is a map delete.\n *\n * **A registry is a resource.** It holds a key until `release`, and\n * the source until `close` or the last key goes. That is deliberate:\n * the surfaces that need this are the ones that mount and unmount\n * deliberately, and a cell that vanished when its last binding did\n * would be rebuilt on every scroll frame by exactly the code that\n * cannot afford it.\n */\nexport function fanOut<S, V, D = undefined>(\n source: Observable<S>,\n read: (snapshot: S, key: FanKey, datum: D) => V,\n options: FanOutOptions<S, V, D>\n): FanOut<S, V, D> {\n return new FanOut(source, read, options);\n}\n\n/** What `fanOut` returns. See there. */\nexport class FanOut<S, V, D = undefined> {\n private readonly cells = new Map<FanKey, FanCell<V, D>>();\n private readonly equal: (a: V, b: V) => boolean;\n private readonly initial: (key: FanKey, datum: D) => V;\n private readonly changed: ((next: S, previous: S | undefined) => Iterable<FanKey> | undefined) | undefined;\n private readonly label: string | undefined;\n /**\n * How many keys have ever been let go.\n *\n * The leak signature is not \"this registry is big\" — a grid holds\n * ten thousand cells and is right to. It is \"this registry has never\n * let go of anything\", which is what forgetting `release` looks like\n * from in here, and what a registry that mounts and unmounts\n * correctly can never look like once it has scrolled once.\n */\n private releases = 0;\n private warnedGrowing = false;\n private upstream: Subscription | null = null;\n private snapshot: S | undefined;\n private hasSnapshot = false;\n private closed = false;\n\n constructor(\n private readonly source: Observable<S>,\n private readonly read: (snapshot: S, key: FanKey, datum: D) => V,\n options: FanOutOptions<S, V, D>\n ) {\n this.equal = equalityOf(options.equal ?? 'reference');\n const initial = options.initial;\n this.initial = typeof initial === 'function' ? (initial as (key: FanKey, datum: D) => V) : () => initial;\n this.changed = options.changed;\n this.label = options.label;\n }\n\n /** How many keys are live. */\n get size(): number {\n return this.cells.size;\n }\n\n /**\n * The cell for one key, made on first ask and the same object after.\n *\n * The identity is the contract: a surface that memoises its elements\n * by key binds this once and is never re-bound, which is the reason\n * the registry hands out cells rather than values.\n */\n for(key: FanKey, datum: D = undefined as D): FanCell<V, D> {\n const existing = this.cells.get(key);\n if (existing !== undefined) {\n return existing;\n }\n if (this.closed) {\n throw new Error(\n `fanOut(${this.label ?? 'anonymous'}) was asked for '${String(key)}' after it was closed. A closed ` +\n 'registry has let go of its source and cannot answer.'\n );\n }\n // Taking the first key is what starts the subscription, so a\n // registry made in a component body costs nothing until something\n // is on screen. `subscribe` may emit synchronously, and the cell\n // is not in the map yet — so it is read directly here and the\n // walk below skips it.\n this.attach();\n const first = this.hasSnapshot ? this.read(this.snapshot as S, key, datum) : this.initial(key, datum);\n const cell = new FanCell(first, key, datum, this.label);\n this.cells.set(key, cell);\n this.warnIfOnlyGrowing();\n return cell;\n }\n\n /** The cell for a key if it is live, without making one. */\n peek(key: FanKey): FanCell<V, D> | undefined {\n return this.cells.get(key);\n }\n\n /**\n * Drops a key.\n *\n * The cell completes, so anything still bound to it is torn down\n * rather than left holding a value that has stopped being updated.\n * Letting go of the last key lets go of the source.\n */\n release(key: FanKey): void {\n const cell = this.cells.get(key);\n if (cell === undefined) {\n return;\n }\n this.cells.delete(key);\n this.releases++;\n cell.subject.complete();\n if (this.cells.size === 0) {\n this.detach();\n }\n }\n\n /** Drops every key, keeping the registry usable. */\n releaseAll(): void {\n for (const cell of this.cells.values()) {\n cell.subject.complete();\n }\n this.releases += this.cells.size;\n this.cells.clear();\n this.detach();\n }\n\n /** Drops every key and the source for good. */\n close(): void {\n this.releaseAll();\n this.closed = true;\n }\n\n /**\n * Pushes the latest source value into the keys that changed.\n *\n * Called for each emission, and callable by hand when the source has\n * not emitted but the *reading* has changed — a selection moving\n * over a grid whose values are the same is exactly that case, and it\n * is why the reader takes a key rather than the registry taking a\n * projection.\n */\n refresh(keys?: Iterable<FanKey>): void {\n if (!this.hasSnapshot) {\n return;\n }\n const snapshot = this.snapshot as S;\n for (const key of keys ?? this.cells.keys()) {\n const cell = this.cells.get(key);\n if (cell === undefined) {\n continue;\n }\n const next = this.read(snapshot, key, cell.datum);\n if (!this.equal(cell.subject.value, next)) {\n cell.subject.next(next);\n }\n }\n }\n\n /**\n * Says so, once, when a registry has grown large and released\n * nothing.\n *\n * A registry is a resource and `release` is the caller's, which is\n * the one thing about this that can be got wrong silently: a key\n * that is never released keeps its cell, its entry and its place in\n * every walk, for the life of the registry. There is no upper bound\n * to hit and nothing to see, which is exactly the kind of leak that\n * is found six months later by someone else.\n *\n * The threshold is generous on purpose. A grid mounting its first\n * window legitimately takes hundreds of keys before it has scrolled\n * far enough to retire one, and a warning that fired there would be\n * noise people learn to scroll past.\n */\n private warnIfOnlyGrowing(): void {\n if (this.warnedGrowing || this.releases > 0 || this.cells.size < GROWING_WITHOUT_RELEASE) {\n return;\n }\n this.warnedGrowing = true;\n console.warn(\n `fanOut(${this.label ?? 'anonymous'}) holds ${this.cells.size} keys and has released none. A registry ` +\n 'keeps a key until `release`, so a surface that mounts and unmounts has to say when a key has gone: ' +\n '`release(key)` as it leaves, `releaseAll()` when the screen does, `close()` when the registry does. ' +\n 'Ignore this if the keys really do all live as long as the registry.'\n );\n }\n\n private attach(): void {\n if (this.upstream !== null) {\n return;\n }\n this.upstream = this.source.subscribe(snapshot => {\n const previous = this.hasSnapshot ? (this.snapshot as S) : undefined;\n // `changed` is asked before the snapshot moves, so it can compare\n // the two, and its answer is used after — a source that returns a\n // view onto its own delta must not have it read out from under it.\n const keys = this.changed?.(snapshot, previous);\n this.snapshot = snapshot;\n this.hasSnapshot = true;\n this.refresh(keys);\n });\n }\n\n private detach(): void {\n this.upstream?.unsubscribe();\n this.upstream = null;\n this.hasSnapshot = false;\n this.snapshot = undefined;\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 /**\n * The workers serving this registry's channels, so a thread can reach\n * them for something other than a channel: an agent asking what each\n * one serves.\n */\n readonly workers: ReadonlySet<WorkerHandle>;\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 workers: handles,\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 * A point to open beside, relative to the viewport: where a context\n * menu was asked for. Placed as an anchor of no size would be, with\n * `placement`, so an entry that would run off an edge flips or shifts\n * to stay on screen — which `top` and `left` do not do.\n */\n readonly point?: { readonly x: number; readonly y: number };\n /**\n * Centre an unanchored entry in the viewport along an axis.\n *\n * The edges of that axis stop being a position and become the region\n * to centre within — unset they are the whole viewport, so a dialog\n * asking for `'x'` sits in the middle however wide the window is, and\n * one asking for `'both'` sits in the middle of the screen. The other\n * axis is untouched: `center: 'x'` with `bottom: 24` is a toast\n * centred along the bottom edge.\n *\n * Ignored on an anchored entry, which is placed beside its anchor.\n */\n readonly center?: 'x' | 'y' | 'both';\n /**\n * Close the entry when the pointer goes down, or the wheel turns,\n * anywhere outside it. A backdrop takes those events, so nothing\n * underneath scrolls while the entry is open. Without it the entry\n * stays open and follows its anchor through scrolling.\n */\n readonly dismissOnOutsidePress?: boolean;\n /** 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 anchorPoint: entry.anchor == null ? entry.point : 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 => pushInput(cell, value));\n this.inputSubscriptions.set(inputName, subscription);\n this.subscriptions.add(subscription);\n return;\n }\n\n pushInput(cell, 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\n/**\n * Hands an input cell a value from its parent, unless it already holds\n * that very value.\n *\n * A parent that re-renders hands every child its props again, and a\n * prop that is an Observable built in the parent's render is a new\n * source each time, which re-subscribes and replays the current value.\n * Pushed through regardless, that replay re-ran every binding derived\n * from the input. In the issue tracker's editor, inserting one block\n * re-rendered the list of 2,868, each replay rebuilt its block's text\n * runs as a new array, and every editable on the page was re-measured:\n * 12,912 nodes for one Enter. Values that cross into a component are\n * treated as immutable everywhere else in the framework (bindings hold\n * on to them, patches share structure), so the same reference is the\n * same value and there is nothing to tell the child.\n */\nfunction pushInput(cell: InputCell<unknown>, value: unknown): void {\n // `getValue`, not `value`: the getter records a read for the\n // stale-read warning, and this is the framework looking, not a body.\n const current = cell.getValue();\n if (current !== undefined && Object.is(current, value)) {\n return;\n }\n cell.next(value);\n}\n","import type { ComponentResolver, ComponentLikeElement, UiChild } 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 { DevtoolsEvent, DevtoolsRequest } from '../DevtoolsProtocol';\nimport type {\n UiKeyModifiers,\n EditingState,\n RendererBackend,\n UiInsets,\n UiPointerDevice,\n UiSemanticsAction,\n UiScrollability,\n UiSemanticsUpdate,\n UiFileDropMessage,\n UiContrast\n} from 'gesso-core';\nimport type { AudioAction, AudioRequest, AudioSample } from '../AudioService';\nimport type { ColorScheme } from '../colorScheme';\nimport type { ShellFileRequest, ShellFileResult, ShellStorageOp, ShellStorageResult } from '../ShellService';\nimport type { FramePhaseTimings, GpuStageTimings, RendererChoice } from '../GessoRuntime';\n\n/**\n * Messages the main-thread shell sends to the render worker.\n *\n * Deliberately small: input, size, and lifecycle. UiElements,\n * component instances, observables and UiNodes never cross the\n * boundary — they are constructed in the worker and stay there.\n */\nexport type ShellToRuntimeMessage =\n | {\n type: 'init';\n canvas: OffscreenCanvas;\n width: number;\n height: number;\n dpr: number;\n renderer?: RendererChoice;\n /**\n * How typed text reaches the runtime: `proxy` when the shell has\n * an editing proxy that sends `beforeInput` and composition (then\n * printable key presses are not text); `keys` (default) when key\n * presses are all there is.\n */\n textInput?: 'proxy' | 'keys';\n /**\n * One end of a channel to the application worker, when the shell\n * spawned one.\n *\n * The shell creates both workers and wires them together once,\n * then stays out of the way — it never sees a patch. Owning the\n * spawn rather than letting the render worker nest a worker\n * inside itself keeps the application alive across a render\n * worker being replaced (a renderer switch), and avoids\n * depending on nested worker support, which is not uniform\n * across the webviews this project targets.\n */\n appPort?: MessagePort;\n /**\n * Whether the shell has an accessibility mirror to feed. False\n * stops the runtime computing the geometry it would need, which\n * is the only per-frame cost the mirror has in here.\n */\n accessibility?: boolean;\n }\n | { type: 'resize'; width: number; height: number; dpr: number }\n /**\n * A devtools panel's request (`DevtoolsProtocol.ts`). The worker\n * answers with `devtools` messages; `console` is handled by the\n * worker host, which owns the global the calls are made on.\n */\n | { type: 'devtools'; request: DevtoolsRequest }\n /**\n * `pointer` is the contact: which device, and which of its\n * simultaneous contacts. Optional, so a shell written before touch\n * existed is read as the mouse it was — every touch behaviour in the\n * runtime is opt-in on this field saying `touch`.\n */\n | {\n type: 'pointerDown';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | {\n type: 'pointerMove';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | {\n type: 'pointerUp';\n x: number;\n y: number;\n buttons: number;\n modifiers: UiKeyModifiers;\n pointer?: UiPointerDevice;\n at?: number;\n }\n | { type: 'pointerCancel'; pointer?: UiPointerDevice; at?: number }\n /**\n * `deltaMode` is the DOM's own value, forwarded rather than\n * translated: a wheel delta is a distance in pixels only when it is\n * 0, and Firefox reports lines. Optional, so a shell that predates\n * it is read as pixels — which is what it was assumed to be.\n */\n | {\n type: 'wheel';\n x: number;\n y: number;\n deltaX: number;\n deltaY: number;\n modifiers: UiKeyModifiers;\n deltaMode?: number;\n /**\n * The legacy `wheelDeltaY`. Forwarded because a detented wheel\n * reports it in multiples of 120 and a precision device does\n * not, and only a detented wheel is worth animating.\n */\n wheelDeltaY?: number;\n at?: number;\n }\n /**\n * `textFollows`: whether the key reached the editing proxy, which\n * turns it into a `beforeinput` of its own. False means the key went\n * to the canvas and its text, if it has any, is the runtime's to make.\n */\n | { type: 'keyDown'; key: string; modifiers: UiKeyModifiers; at?: number; textFollows?: boolean }\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 /** `html` is the clipboard's HTML, when it held some. */\n | { type: 'paste'; text: string; html?: string; at?: number }\n /** The editing proxy lost focus to something outside the app. */\n | { type: 'blur' }\n /** The page was hidden or shown (document.visibilityState). */\n | { type: 'visibility'; visible: boolean }\n /** The surface entered or left fullscreen, however that happened. */\n | { type: 'fullscreenChanged'; active: boolean }\n /**\n * The person's motion preference (`prefers-reduced-motion`), sent\n * once at start-up and again whenever it changes.\n *\n * The first thing this protocol has ever carried that is a\n * *preference* rather than an event or a size. It is inbound because\n * the query needs a window and the animations are in here; see\n * `GessoRuntime.setReducedMotion` for why it is not an environment\n * key.\n */\n | { type: 'reducedMotion'; reduced: boolean }\n /**\n * The appearance the shell is asking for\n * (`prefers-color-scheme`, or an override the host set), sent once at\n * start-up and again whenever it changes.\n *\n * The second preference-shaped message here, and inbound for the same\n * reason as the first — the query needs a window. It differs in who\n * consumes it: reduced motion reaches the animation driver, while\n * nothing in the framework reads this one. It is carried to `ShellService` and no further,\n * because what dark *looks* like is the application's, and a\n * framework that shipped an answer would be shipping a palette.\n *\n * Always resolved to one of the two appearances. `auto` is a thing a\n * host tells a shell, not a thing that crosses.\n */\n | { type: 'colorScheme'; scheme: ColorScheme }\n | { type: 'contrast'; contrast: UiContrast }\n /**\n * What the window's own chrome is covering on each edge: the safe\n * area a notch or a home indicator takes, and the strip a soft\n * keyboard covers, read from `visualViewport` and the\n * `env(safe-area-inset-*)` custom properties. Sent once at start-up\n * and again whenever they change, which on a phone is every frame of\n * a keyboard sliding up.\n *\n * Inbound for the reason the two above are: `visualViewport` needs a\n * window. Four plain numbers cross, and nothing else, because what to\n * do about a keyboard is a layout question and layout is in here.\n * `GessoRuntime.setViewportInsets` publishes them into the\n * application's inset registry, where they compose by maximum with\n * whatever the application's own floating bars publish; see\n * `UiInsetRegistry` for why by maximum.\n */\n | { type: 'viewportInsets'; insets: UiInsets }\n /**\n * Where the window's address is now: once at start-up, and again for\n * every back, forward or typed address afterwards.\n *\n * The third preference-shaped message on this protocol, and for the\n * same reason as `reducedMotion`: `location` and `history` are the\n * shell's and the routes are in here. A url is the whole of what\n * routing puts on the wire — patterns, params, guards and screens\n * never leave the render thread, because a route holds a component\n * class and a component class cannot be posted anywhere.\n */\n | { type: 'url'; url: string }\n /**\n * What became of a popup the render worker asked for: `opened` is\n * false when the browser refused it, which is a thing an application\n * must be able to route around rather than a failure to log.\n *\n * The only reply on this protocol to a request from the other side,\n * which is why it carries the request's `id` rather than standing on\n * its own like the preference messages above it.\n */\n | { type: 'popupResult'; id: number; opened: boolean }\n /**\n * Whether a `clipboard` request's text reached the clipboard, under\n * the request's `id`. False when the browser refused both ways of\n * writing it.\n */\n | { type: 'clipboardResult'; id: number; copied: boolean }\n /**\n * What the shell found in `localStorage` for a `storage` request\n * (ShellStorage). The second reply on this protocol, and it carries\n * its request's `id` for the same reason `popupResult` does.\n */\n | { type: 'storageResult'; id: number; result: ShellStorageResult }\n /**\n * What the shell did with a file request (ShellService.openFiles and\n * its siblings), under the request's `id`. A file read comes back\n * with its bytes, transferred rather than copied.\n */\n | { type: 'fileResult'; id: number; result: ShellFileResult }\n | { type: 'inspector'; enabled: boolean }\n /**\n * What an assistive technology did to the accessibility mirror: a\n * press, a focus move, or a value set.\n *\n * The fourth preference-shaped asymmetry on this protocol, and the\n * only *input* on it that no device produced. It arrives by id\n * rather than by coordinate because that is what the mirror has: an\n * element standing for a node, with no idea where the person's\n * pointer is or whether there is one. `GessoRuntime.applySemanticsAction`\n * turns it back into the events a pointer and a keyboard produce.\n */\n | { type: 'semanticsAction'; action: UiSemanticsAction }\n /**\n * Files dragged over the canvas from outside the application, and\n * let go on it: `UiDragSession.applyFileDrop` turns the four phases\n * into a drag like any other. On `drop` the files' bytes ride along\n * and are transferred rather than copied; see `attachFileDrop`.\n */\n | UiFileDropMessage\n /**\n * What the shell's audio element is doing: on every state change and\n * about once a second while it plays. The element lives on the shell\n * because no worker can make a sound; `AudioService` is its client\n * and moves the position on between samples. See `AudioSink`.\n */\n | { type: 'audioSample'; sample: AudioSample }\n /**\n * What the platform's media controls asked for (the keyboard's media\n * keys, the OS overlay). Play and pause were already done to the\n * element and arrive as samples too; next and previous are the\n * application's to answer.\n */\n | { type: 'audioAction'; action: AudioAction }\n /**\n * One display refresh, forwarded from the shell's\n * `requestAnimationFrame`.\n *\n * The render worker's frames were paced by a fixed 16ms timer,\n * because `requestAnimationFrame` is tied to the compositor and does\n * not exist off the main thread. That capped every display at\n * roughly sixty and aligned to none of them. The shell runs the loop\n * and forwards the beat; `time` is the rAF timestamp, so the\n * runtime's frame times stay on the same clock the display is on.\n *\n * Sent only between `frameLoop` starting and stopping, so an idle\n * app exchanges nothing.\n */\n | { type: 'tick'; time: number }\n | { type: 'dispose' };\n\n/**\n * Where an error the worker reports came from.\n *\n * The shell cannot see a worker's exceptions, so this is the only\n * thing that tells a developer *what* is broken rather than only what\n * threw — and the four cases have genuinely different consequences:\n *\n * - `message` — thrown while handling a message from the shell.\n * Input or a resize was dropped; the app is otherwise intact.\n * - `uncaught` — an exception or a rejected promise nothing caught,\n * which is almost always a frame. The scheduler had already\n * drained the dirty set for that frame, so the work it held is\n * gone and the surface can be stale until something dirties those\n * nodes again. The most serious of the four.\n * - `renderer` — the backend refused to draw (a lost GPU device, a\n * surface it could not configure). Layout and state are fine.\n * - `channel` — a channel's worker or its patch stream threw. The\n * view is intact; the data behind it stopped.\n * - `listener` — one of the application's own event listeners threw.\n * The dispatcher caught it so the event still reached the rest of\n * the tree, so this is the one source that costs the running\n * application nothing but whatever the handler was supposed to do.\n */\nexport type RuntimeErrorSource =\n | 'message'\n | 'uncaught'\n | 'renderer'\n | 'channel'\n | 'listener'\n /**\n * A frame threw. The frame was abandoned and the clock kept\n * running, so the application is still drawing: this is a report,\n * not an obituary.\n *\n * Its own source because it used to arrive as `message` — the throw\n * escaped into whatever was forwarding the tick — and that both\n * mislabelled it and stopped the application, since nothing re-armed\n * the clock afterwards.\n */\n | 'frame';\n\n/**\n * Messages the render worker sends back.\n *\n * The shell owns no UI state, so this carries only observability:\n * readiness, frame timings, and errors that would otherwise be\n * invisible inside a worker.\n */\nexport type RuntimeToShellMessage =\n | { type: 'ready' }\n | {\n type: 'frame';\n frame: number;\n durationMs: number;\n nodes: number;\n measured: number;\n relayoutRoots: number;\n layoutPasses: number;\n at: number;\n inputLatencyMs: number | null;\n phases: FramePhaseTimings;\n renderer: RendererBackend | 'pending';\n gpu: GpuStageTimings | null;\n }\n | { type: 'error'; message: string; stack?: string; source: RuntimeErrorSource }\n /** A report on the hovered node while the inspector is on; null when nothing is hovered. */\n | { type: 'inspect'; report: UiNodeReport | null }\n /** An answer to a devtools request, or an update to something a panel is watching. */\n | { type: 'devtools'; event: DevtoolsEvent }\n /** The CSS cursor the hovered node asks for; null for the default arrow. */\n | { type: 'cursor'; cursor: string | null }\n /**\n * Which way the runtime could scroll under the pointer, and whether\n * it has anything scrollable at all.\n *\n * Pushed ahead of the wheel it answers for, because the shell has\n * to decide `preventDefault()` synchronously and the runtime is a\n * message away. Without it the shell must either swallow every\n * wheel — making the canvas a scroll trap in the page around it —\n * or swallow none, and let one wheel scroll twice.\n *\n * `scrollsAnything` is the coarser tree-level answer, and drives\n * the canvas's `touch-action`: that is latched when a finger lands,\n * so there is no hover position it could have been derived from.\n */\n | { type: 'scrollability'; scrollability: UiScrollability; scrollsAnything: boolean }\n /**\n * The focused editable's text, selection and caret box for the\n * editing proxy to mirror; null when no editable has focus.\n */\n | { type: 'editing'; state: EditingState | null }\n /**\n * Put text on the clipboard (ShellService.copyText). `id` pairs it\n * with the `clipboardResult` that comes back, once.\n */\n | { type: 'clipboard'; id: number; text: string }\n /** 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 /**\n * Open, save, reopen, list or forget files (ShellService.openFiles\n * and its siblings). Answered with `fileResult`. Sent at once, like\n * `popup`, because a picker is shown only while the click that asked\n * for it is fresh.\n */\n | { type: 'file'; id: number; request: ShellFileRequest }\n /** The router navigated; the shell owns the address bar (RouterService). */\n | { type: 'history'; action: 'push' | 'replace' | 'back' | 'forward'; url?: string }\n /** Load, play, pause, seek, set the volume or the OS metadata (AudioService). */\n | { type: 'audio'; request: AudioRequest }\n /**\n * What the accessibility mirror needs to keep up with this frame:\n * the semantics patches, the boxes that moved, and the focused node\n * when focus moved.\n *\n * Sent only while the shell has a mirror attached — a `SemanticsMirror`\n * subscribes by existing, and a runtime nobody is mirroring computes\n * no geometry at all. Records and boxes travel at different cadences\n * and are one message anyway; `UiSemanticsUpdate` says why.\n */\n /**\n * Whether the runtime currently wants display refreshes.\n *\n * The shell answers by running or stopping a `requestAnimationFrame`\n * loop that sends `tick`. It is a state rather than a per-frame\n * request because a request-per-frame costs a round trip inside\n * every frame: a request that reaches the shell after that vsync's\n * callback has run waits for the next one, and the frame rate\n * halves.\n */\n | { type: 'frameLoop'; running: boolean }\n /**\n * One `resize` has been applied, and the shell may send the next.\n *\n * A resize is the one shell message whose handling costs a full\n * layout, and `ResizeObserver` delivers one per refresh while a\n * window edge is dragged. A worker slower than the display therefore\n * accumulates a queue of sizes it must lay out and paint in turn,\n * every one of them already wrong, and the lag grows for as long as\n * the drag lasts rather than settling.\n *\n * So the shell keeps at most one resize in flight and remembers only\n * the latest size it has not sent. This message is what lets it: the\n * worker has drained the previous one, so the current size can go\n * now. Nothing is dropped that anyone can see — the last size always\n * gets sent, because it is the one held back.\n *\n * The dimensions ride along so the shell can tell an acknowledgement\n * of the size it is holding from one it has already superseded.\n */\n | { type: 'resized'; width: number; height: number; dpr: number }\n | { type: 'semantics'; update: UiSemanticsUpdate };\n\n/**\n * The set of shell messages that carry a user input.\n *\n * The shell stamps these with `at` and the runtime measures against\n * them; everything else in the protocol is a size, a preference or a\n * lifecycle signal and has no latency to speak of.\n */\nconst INPUT_MESSAGE_TYPES: ReadonlySet<string> = new Set([\n 'pointerDown',\n 'pointerMove',\n 'pointerUp',\n 'pointerCancel',\n 'wheel',\n 'keyDown',\n 'keyUp',\n 'beforeInput',\n 'compositionStart',\n 'compositionUpdate',\n 'compositionEnd',\n 'paste'\n]);\n\nexport function isInputMessage(message: ShellToRuntimeMessage): message is ShellToRuntimeMessage & { at?: number } {\n return INPUT_MESSAGE_TYPES.has(message.type);\n}\n\n/**\n * 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 { 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 { UiDroppedFile, UiFileDropMessage } from 'gesso-core';\n\n/**\n * The shell half of an OS file drop.\n *\n * `UiDragSession.applyFileDrop` has turned a `fileDrop` message into\n * an ordinary drag since drop targets arrived; what was missing was a\n * shell that posted one, so a zone accepting `EXTERNAL_FILES` could be\n * written, spec'd and never reached. This is that shell, for a browser:\n * the four DOM drag events on the surface, turned into the four phases\n * of the message.\n *\n * Shared by both configurations because it is the same four events\n * either way. The worker shell posts the message across and transfers\n * the bytes; the single-thread shell hands it straight to its runtime.\n * Neither decides anything about the files — whether a drop is taken,\n * and what it means, is the zone's.\n *\n * **Only drags that carry files.** A drag of selected text from\n * another page over the canvas is not a file drop, and answering it\n * would put a drop cursor over an application that cannot take it.\n */\n\nexport interface FileDropSink {\n /**\n * One phase of a drop. `transfer` holds the dropped files' buffers\n * on `drop`, for a sink that posts across a thread and can move them\n * rather than copy them; it is empty on every other phase.\n */\n (message: UiFileDropMessage, transfer: ArrayBuffer[]): void;\n}\n\n/** The slice of a DOM element this needs, so a spec can double it. */\nexport interface FileDropTarget {\n addEventListener(type: string, listener: (event: DragEvent) => void): void;\n removeEventListener(type: string, listener: (event: DragEvent) => void): void;\n}\n\n/**\n * Listens for files dragged over `target` and reports them to `sink`.\n *\n * `toLocal` is the shell's own client-to-surface conversion, so a drop\n * lands at the point a press at the same place would.\n *\n * `dragover` has its default prevented on every event, not only the\n * first: that is what tells the browser the drop is wanted, and the\n * browser asks again on each one. `drop` has it prevented too, or the\n * browser navigates away to show the file — which, over an\n * application, is the tab being replaced by a CSV.\n *\n * During the drag the platform lets a page read a file's type and not\n * its name or size, so `enter` and `over` carry what it allows; `drop`\n * carries all of it, bytes included. Reading the bytes is asynchronous,\n * so the `drop` message goes when they are read, and a drop whose files\n * cannot be read is reported as a `leave` so that the zone does not\n * stay lit waiting for a drop that will never arrive.\n *\n * Returns the function that stops listening.\n */\nexport function attachFileDrop(\n target: FileDropTarget,\n toLocal: (clientX: number, clientY: number) => { x: number; y: number },\n sink: FileDropSink\n): () => void {\n let inside = false;\n\n const post = (phase: UiFileDropMessage['phase'], event: DragEvent, files: readonly UiDroppedFile[]): void => {\n const { x, y } = toLocal(event.clientX, event.clientY);\n sink({ type: 'fileDrop', phase, x, y, files }, []);\n };\n\n const onEnterOrOver = (event: DragEvent): void => {\n if (!carriesFiles(event)) {\n return;\n }\n event.preventDefault();\n if (event.dataTransfer !== null) {\n event.dataTransfer.dropEffect = 'copy';\n }\n post(inside ? 'over' : 'enter', event, filesDuringDrag(event));\n inside = true;\n };\n\n const onLeave = (event: DragEvent): void => {\n if (!inside) {\n return;\n }\n inside = false;\n post('leave', event, []);\n };\n\n const onDrop = (event: DragEvent): void => {\n if (!carriesFiles(event)) {\n return;\n }\n event.preventDefault();\n inside = false;\n const { x, y } = toLocal(event.clientX, event.clientY);\n const files = Array.from(event.dataTransfer?.files ?? []);\n void Promise.all(files.map(file => file.arrayBuffer())).then(\n buffers => {\n const dropped: UiDroppedFile[] = files.map((file, at) => ({\n name: file.name,\n mediaType: file.type,\n size: file.size,\n lastModified: file.lastModified,\n bytes: buffers[at]\n }));\n sink({ type: 'fileDrop', phase: 'drop', x, y, files: dropped }, buffers);\n },\n () => sink({ type: 'fileDrop', phase: 'leave', x, y, files: [] }, [])\n );\n };\n\n target.addEventListener('dragenter', onEnterOrOver);\n target.addEventListener('dragover', onEnterOrOver);\n target.addEventListener('dragleave', onLeave);\n target.addEventListener('drop', onDrop);\n return () => {\n target.removeEventListener('dragenter', onEnterOrOver);\n target.removeEventListener('dragover', onEnterOrOver);\n target.removeEventListener('dragleave', onLeave);\n target.removeEventListener('drop', onDrop);\n };\n}\n\nfunction carriesFiles(event: DragEvent): boolean {\n const types = event.dataTransfer?.types;\n return types !== undefined && Array.from(types).includes('Files');\n}\n\n/** What a drag in flight lets a page see of its files: their types. */\nfunction filesDuringDrag(event: DragEvent): UiDroppedFile[] {\n const items = Array.from(event.dataTransfer?.items ?? []);\n return items\n .filter(item => item.kind === 'file')\n .map(item => ({ name: '', mediaType: item.type, size: 0, lastModified: 0 }));\n}\n","import { insetsEqual, noInsets, type UiInsets } from 'gesso-core';\n\nimport { internalState } from '../InternalState';\nimport type { UiContrast } from 'gesso-core';\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'; id: number; 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: 'file'; id: number; request: ShellFileRequest }\n | { type: 'history'; action: 'push' | 'replace'; url: string }\n | { type: 'history'; action: 'back' | 'forward'; url?: undefined };\n\n/**\n * A kind of file a picker offers, as the File System Access API and a\n * file input both understand it.\n *\n * `extensions` include the dot (`.csv`), because that is the form both\n * APIs take and a spelling this layer translated would be one more\n * place for the two to disagree.\n */\nexport interface ShellFileType {\n readonly description: string;\n readonly mediaType: string;\n readonly extensions: readonly string[];\n}\n\n/**\n * What the shell is asked to do with files.\n *\n * `handle` is a number the shell hands out for a file somebody picked\n * — the `FileSystemFileHandle` itself cannot leave the main thread,\n * since nothing that is not plain data crosses the barrier. The shell\n * keeps the handle and remembers it across reloads, so a number from\n * yesterday's session still names yesterday's file.\n */\nexport type ShellFileRequest =\n /** Show an open picker. */\n | { readonly op: 'open'; readonly accept: readonly ShellFileType[]; readonly multiple: boolean }\n /** Read a file the shell already has a handle to, asking permission again if it lapsed. */\n | { readonly op: 'reopen'; readonly handle: number }\n /**\n * Write a file: to `handle` when given, and otherwise to wherever a\n * save picker says — or, where there is no picker, as a download.\n */\n | {\n readonly op: 'save';\n readonly name: string;\n readonly mediaType: string;\n readonly text: string;\n /**\n * The file's bytes, for one that is not text — a zip, an image.\n * Written instead of `text` when given; bytes cross the barrier\n * as they do on the way in, in `ShellFile.bytes`.\n */\n readonly bytes?: Uint8Array<ArrayBuffer>;\n readonly handle?: number;\n readonly accept: readonly ShellFileType[];\n }\n /** The files the shell remembers, most recently used first. */\n | { readonly op: 'recent' }\n /** Stop remembering one. */\n | { readonly op: 'forget'; readonly handle: number };\n\n/** One file the shell read. */\nexport interface ShellFile {\n readonly name: string;\n readonly mediaType: string;\n readonly lastModified: number;\n readonly bytes: ArrayBuffer;\n /** The shell's number for it, or null where the platform gives none (a file input). */\n readonly handle: number | null;\n}\n\n/** A file the shell remembers. */\nexport interface ShellRecentFile {\n readonly handle: number;\n readonly name: string;\n /** When it was last opened or saved, in epoch milliseconds. */\n readonly used: number;\n}\n\n/**\n * What the shell made of a file request.\n *\n * One record with a field per shape of answer, as `ShellStorageResult`\n * is and for the same reason. `cancelled` is its own outcome because a\n * person closing a picker is a decision and not a failure, and an\n * application that reported it as one would be wrong every time.\n * `denied` is a permission refused; `unsupported` is a shell with no\n * way to do it at all.\n */\nexport interface ShellFileResult {\n readonly outcome: 'ok' | 'cancelled' | 'denied' | 'unsupported' | 'failed';\n /** What `open` and `reopen` read. */\n readonly files: readonly ShellFile[];\n /** Where `save` wrote: `'file'` through a handle, `'download'` without one. */\n readonly saved: { readonly name: string; readonly handle: number | null; readonly via: 'file' | 'download' } | null;\n /** What `recent` lists. */\n readonly recent: readonly ShellRecentFile[];\n /** Why it did not answer, as a message; null when it did. */\n readonly error: string | null;\n}\n\n/** The four things `localStorage` is asked for; see `ShellStorage`. */\nexport type ShellStorageOp = 'read' | 'write' | 'remove' | 'keys';\n\n/**\n * What the shell made of a storage request.\n *\n * One record with a field per shape of answer, rather than four reply\n * messages: it is plain data either way, and a single reply keeps the\n * pairing with `id` in one place. `value` is a read's, `keys` is a\n * listing's, and both are empty for a write.\n */\nexport interface ShellStorageResult {\n readonly outcome: 'ok' | 'denied' | 'full' | 'failed';\n readonly value: string | null;\n readonly keys: readonly string[];\n /** Why it did not answer, as a message; null when it did. */\n readonly error: string | null;\n}\n\n/**\n * The shell's services, as a store components can inject.\n *\n * A component in the render worker has no clipboard and no window.\n * Dispatching an action here hands the request to the runtime, which\n * forwards it to whichever host it has: `WorkerApp` posts it to the\n * main thread, `GessoApp` performs it directly. Every runtime registers\n * one, like `OverlayService`; being a store keeps the rule that\n * components reach the outside world through actions only, and gives a\n * desktop shell one place to bind native equivalents.\n */\nexport class ShellService {\n private handler: ((request: ShellRequest) => void) | null = null;\n private readonly scheme = internalState<ColorScheme>('light');\n private readonly contrastState = internalState<UiContrast>('standard');\n private readonly insets = internalState<UiInsets>(noInsets);\n private readonly isFullscreen = internalState<boolean>(false);\n /** Popups asked for and not yet answered, by the id sent with each. */\n private readonly popups = new Map<number, (opened: boolean) => void>();\n private nextPopupId = 1;\n /** Storage requests asked for and not yet answered, by the id sent with each. */\n private readonly stores = new Map<number, (result: ShellStorageResult) => void>();\n private nextStorageId = 1;\n /** Clipboard writes asked for and not yet answered, by the id sent with each. */\n private readonly copies = new Map<number, (copied: boolean) => void>();\n private nextCopyId = 1;\n /** File requests asked for and not yet answered, by the id sent with each. */\n private readonly files = new Map<number, (result: ShellFileResult) => void>();\n private nextFileId = 1;\n\n /**\n * The appearance the platform is asking for, as the shell reports it:\n * once when the app starts, and again whenever it changes.\n *\n * Read-only to the application on purpose. Nothing in the framework\n * consumes this — no built-in theme switches on it, and nothing in\n * layout, paint or input reads it — so the only writer is the shell,\n * and a cell an application could also write would be a cell the next\n * media-query change silently overwrites.\n *\n * What it means is the application's to decide. A theme is an\n * ordinary prop that accepts an Observable, so the whole of following\n * the platform is:\n *\n * const theme = ctx.inject(ShellService).colorScheme.pipe(\n * map(scheme => (scheme === 'dark' ? darkTheme : lightTheme))\n * );\n *\n * An app with its own light/dark control keeps that choice as\n * application state — a channel or a store, like any other preference\n * that outlives a screen — and combines it with this. The framework\n * does not decide what dark looks like, and does not remember what\n * the person picked.\n */\n /**\n * A cell rather than a bare Observable, so `computed(() => ...)` can\n * read it beside a channel's view; its setter stays private here.\n */\n readonly colorScheme: ReadableCell<ColorScheme> = this.scheme;\n\n /**\n * The contrast the platform is asking for: `high` when the person has\n * turned up contrast or turned on a contrast theme, `standard`\n * otherwise. Reported by the shell like `colorScheme`, and as with it,\n * what it means is the application's: `withContrast(theme, contrast)`\n * is the theme that answers it.\n *\n * const theme = combineLatest([shell.colorScheme, shell.contrast]).pipe(\n * map(([scheme, contrast]) => withContrast(scheme === 'dark' ? darkTheme : lightTheme, contrast))\n * );\n */\n readonly contrast: ReadableCell<UiContrast> = this.contrastState;\n\n /** 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 /** Called by the runtime when the shell reports the contrast; not for applications. */\n applyContrast(contrast: UiContrast): void {\n if (this.contrastState.value !== contrast) {\n this.contrastState.value = contrast;\n }\n }\n\n /**\n * Called by the runtime when the shell reports the platform's insets.\n *\n * Not for applications, on the terms `applyColorScheme` sets. A\n * report that changes nothing is dropped here, so a `visualViewport`\n * scroll event that moved no edge does not wake every subscriber.\n */\n applyViewportInsets(insets: UiInsets): void {\n if (!insetsEqual(this.insets.value, insets)) {\n this.insets.value = insets;\n }\n }\n\n /**\n * Puts text on the system clipboard, and answers whether it got there.\n *\n * Most callers ignore the answer, and may: the text is on its way the\n * moment this returns. It is for an application that tells the person\n * what happened, a \"Copied\" toast after a menu command, say, which\n * would be a lie on the occasions the browser refused. It does refuse:\n * the clipboard wants a focused document, and some browsers a fresh\n * gesture, and a key pressed in a render worker reaches the window's\n * clipboard a message later than it reached the canvas.\n *\n * With no shell installed the answer is false, as `openPopup`'s is,\n * rather than a promise that never settles.\n */\n copyText(text: string): Promise<boolean> {\n const handler = this.handler;\n if (handler === undefined || handler === null) {\n return Promise.resolve(false);\n }\n const id = this.nextCopyId++;\n const settled = new Promise<boolean>(resolve => {\n this.copies.set(id, resolve);\n });\n handler({ type: 'clipboard', id, text });\n return settled;\n }\n\n /**\n * Called by the runtime when the shell reports whether a clipboard\n * write landed. Not for applications. An unknown id is ignored, on\n * `settlePopup`'s terms.\n */\n settleClipboard(id: number, copied: boolean): void {\n const resolve = this.copies.get(id);\n if (resolve === undefined) {\n return;\n }\n this.copies.delete(id);\n resolve(copied);\n }\n\n /** 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 /**\n * Shows an open picker and reads what was picked.\n *\n * A request, like a popup, and for the same reason: a picker is the\n * window's, and a browser shows one only while the click that asked\n * for it is fresh. Call this from the handler of that click and\n * before any slow work, and the round trip through a worker is fast\n * enough. With the File System Access API each file comes back with\n * a `handle` the shell remembers; through a file input, which is\n * what a browser without the API has, `handle` is null and the file\n * cannot be saved back to.\n */\n openFiles(\n options: { readonly accept?: readonly ShellFileType[]; readonly multiple?: boolean } = {}\n ): Promise<ShellFileResult> {\n return this.requestFile({ op: 'open', accept: options.accept ?? [], multiple: options.multiple ?? false });\n }\n\n /**\n * Reads a file the shell has a handle to — from `recentFiles`, or\n * from an earlier `openFiles` or `saveFile` — asking for permission\n * again when the browser has let it lapse, which it does across a\n * reload. Asking needs a gesture, so this belongs in a click handler\n * too.\n */\n reopenFile(handle: number): Promise<ShellFileResult> {\n return this.requestFile({ op: 'reopen', handle });\n }\n\n /**\n * Writes text, or bytes, to a file.\n *\n * With `handle`, to that file, which is Save; without one, to\n * wherever a save picker says, which is Save As. A browser with no\n * picker gets a download instead, and `saved.via` says which\n * happened, so an application can tell the person where the file\n * went — and knows it has no handle to save back to next time.\n */\n saveFile(options: {\n readonly name: string;\n /** What to write, for a text file. */\n readonly text?: string;\n /** What to write, for any other kind; taken over `text` when both are given. */\n readonly bytes?: Uint8Array<ArrayBuffer>;\n readonly mediaType?: string;\n readonly handle?: number;\n readonly accept?: readonly ShellFileType[];\n }): Promise<ShellFileResult> {\n return this.requestFile({\n op: 'save',\n name: options.name,\n text: options.text ?? '',\n ...(options.bytes === undefined ? {} : { bytes: options.bytes }),\n mediaType: options.mediaType ?? 'application/octet-stream',\n accept: options.accept ?? [],\n ...(options.handle === undefined ? {} : { handle: options.handle })\n });\n }\n\n /** The files the shell remembers, most recently used first. */\n recentFiles(): Promise<ShellFileResult> {\n return this.requestFile({ op: 'recent' });\n }\n\n /** Stops remembering a file. Its handle means nothing afterwards. */\n forgetFile(handle: number): Promise<ShellFileResult> {\n return this.requestFile({ op: 'forget', handle });\n }\n\n /**\n * The wire under the five above. With no shell installed the answer\n * is `unsupported`, for the reason a storage request's is `denied`:\n * a promise left unsettled would hang whatever was waiting on it.\n */\n requestFile(request: ShellFileRequest): Promise<ShellFileResult> {\n const handler = this.handler;\n if (handler === undefined || handler === null) {\n return Promise.resolve(shellFilesUnsupported());\n }\n const id = this.nextFileId++;\n const settled = new Promise<ShellFileResult>(resolve => {\n this.files.set(id, resolve);\n });\n handler({ type: 'file', id, request });\n return settled;\n }\n\n /**\n * Called by the runtime with what the shell did. Not for\n * applications. An unknown id is ignored, on `settlePopup`'s terms.\n */\n settleFile(id: number, result: ShellFileResult): void {\n const resolve = this.files.get(id);\n if (resolve === undefined) {\n return;\n }\n this.files.delete(id);\n resolve(result);\n }\n}\n\n/** The answer for a shell that cannot do anything with files. */\nexport function shellFilesUnsupported(error = 'There is no shell to reach files through.'): ShellFileResult {\n return { outcome: 'unsupported', files: [], saved: null, recent: [], error };\n}\n","import type { ShellFile, ShellFileRequest, ShellFileResult, ShellFileType, ShellRecentFile } from './ShellService';\nimport { shellFilesUnsupported } from './ShellService';\n\n/**\n * The shell's half of a file request: pickers, handles, and the files\n * it remembers.\n *\n * Written once and called from both configurations, as\n * `performShellStorage` is, because what the shell does with a file\n * should not depend on whether the render side is a worker. It decides\n * nothing about what a file *is*: it reads bytes, writes text, and\n * answers with plain data.\n *\n * ## Handles stay here\n *\n * A `FileSystemFileHandle` is the only thing that lets a page write\n * back to a file somebody chose, and it is not plain data, so it cannot\n * cross to the render worker. The shell keeps every handle it is given\n * and hands out a number for it instead. The handles are kept in\n * IndexedDB, which can hold them — so a number handed out yesterday\n * still names yesterday's file, and \"recent files\" is just the store\n * listed. What does not survive a reload is the *permission*: a browser\n * asks again, and asking needs a gesture, which is why `reopen` and\n * `save` to a handle both belong in a click handler.\n *\n * ## Without the File System Access API\n *\n * Firefox and Safari have no pickers. There, `open` falls back to a\n * file input and returns files with no handle, and `save` without a\n * handle falls back to a download. An application learns which from\n * the answer — `handle: null`, `via: 'download'` — rather than from a\n * feature test of its own, because the shell is the only thing that\n * knows.\n */\n\n/** The slice of a `FileSystemFileHandle` used here. */\nexport interface ShellFileHandle {\n readonly name: string;\n getFile(): Promise<File>;\n createWritable?(): Promise<{ write(data: string | Uint8Array<ArrayBuffer>): Promise<void>; close(): Promise<void> }>;\n queryPermission?(descriptor: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n requestPermission?(descriptor: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n isSameEntry?(other: ShellFileHandle): Promise<boolean>;\n}\n\n/** A remembered handle, as the store keeps it. */\nexport interface StoredFileHandle {\n readonly id: number;\n readonly handle: ShellFileHandle;\n readonly name: string;\n readonly used: number;\n}\n\n/** Where handles are remembered. IndexedDB in a browser; a map in a spec. */\nexport interface ShellHandleStore {\n all(): Promise<StoredFileHandle[]>;\n /** Stores an entry, allocating an id when it has none, and answers the id. */\n put(entry: {\n readonly id?: number;\n readonly handle: ShellFileHandle;\n readonly name: string;\n readonly used: number;\n }): Promise<number>;\n delete(id: number): Promise<void>;\n}\n\n/** What the shell can reach. Every member optional but the store and the clock. */\nexport interface ShellFilesHost {\n readonly store: ShellHandleStore;\n now(): number;\n showOpenFilePicker?(options: PickerOptions & { multiple: boolean }): Promise<ShellFileHandle[]>;\n showSaveFilePicker?(options: PickerOptions & { suggestedName: string }): Promise<ShellFileHandle>;\n /** A file input, clicked. Null when it was dismissed. */\n pickWithInput?(accept: string, multiple: boolean): Promise<File[] | null>;\n /** Hands a file to the browser's downloads. */\n download?(name: string, blob: Blob): void;\n}\n\ninterface PickerOptions {\n types?: { description: string; accept: Record<string, string[]> }[];\n excludeAcceptAllOption?: boolean;\n}\n\n/** How many files are remembered before the oldest is let go. */\nexport const RECENT_FILE_LIMIT = 50;\n\nexport class ShellFiles {\n constructor(private readonly host: ShellFilesHost) {}\n\n /**\n * Performs one request. Never rejects: every path, including a\n * browser throwing something nobody predicted, is an answer, because\n * a promise on the other side of the barrier is waiting for one.\n */\n async perform(request: ShellFileRequest): Promise<ShellFileResult> {\n try {\n switch (request.op) {\n case 'open':\n return await this.open(request.accept, request.multiple);\n case 'reopen':\n return await this.reopen(request.handle);\n case 'save':\n return await this.save(request);\n case 'recent':\n return ok({ recent: await this.recent() });\n case 'forget':\n await this.host.store.delete(request.handle);\n return ok({ recent: await this.recent() });\n }\n } catch (error) {\n return failure(error);\n }\n }\n\n private async open(accept: readonly ShellFileType[], multiple: boolean): Promise<ShellFileResult> {\n if (this.host.showOpenFilePicker !== undefined) {\n const handles = await this.host.showOpenFilePicker({ ...pickerTypes(accept), multiple });\n const files: ShellFile[] = [];\n for (const handle of handles) {\n const id = await this.remember(handle);\n files.push(await read(await handle.getFile(), id));\n }\n return ok({ files });\n }\n if (this.host.pickWithInput !== undefined) {\n const picked = await this.host.pickWithInput(inputAccept(accept), multiple);\n if (picked === null) {\n return cancelled();\n }\n return ok({ files: await Promise.all(picked.map(file => read(file, null))) });\n }\n return shellFilesUnsupported('This browser cannot open files.');\n }\n\n private async reopen(id: number): Promise<ShellFileResult> {\n const entry = await this.find(id);\n if (entry === null) {\n return { ...failure(null), error: 'The shell has no file under that handle.' };\n }\n if (!(await permitted(entry.handle, 'read'))) {\n return denied();\n }\n const file = await entry.handle.getFile();\n await this.host.store.put({ ...entry, used: this.host.now() });\n return ok({ files: [await read(file, id)] });\n }\n\n private async save(request: Extract<ShellFileRequest, { op: 'save' }>): Promise<ShellFileResult> {\n if (request.handle !== undefined) {\n const entry = await this.find(request.handle);\n if (entry === null) {\n return { ...failure(null), error: 'The shell has no file under that handle.' };\n }\n if (!(await permitted(entry.handle, 'readwrite'))) {\n return denied();\n }\n await write(entry.handle, request.bytes ?? request.text);\n await this.host.store.put({ ...entry, used: this.host.now() });\n return ok({ saved: { name: entry.handle.name, handle: entry.id, via: 'file' } });\n }\n if (this.host.showSaveFilePicker !== undefined) {\n const handle = await this.host.showSaveFilePicker({\n ...pickerTypes(request.accept),\n suggestedName: request.name\n });\n await write(handle, request.bytes ?? request.text);\n const id = await this.remember(handle);\n return ok({ saved: { name: handle.name, handle: id, via: 'file' } });\n }\n if (this.host.download !== undefined) {\n this.host.download(request.name, new Blob([request.bytes ?? request.text], { type: request.mediaType }));\n return ok({ saved: { name: request.name, handle: null, via: 'download' } });\n }\n return shellFilesUnsupported('This browser cannot save files.');\n }\n\n private async recent(): Promise<ShellRecentFile[]> {\n const all = await this.host.store.all();\n return all.sort((a, b) => b.used - a.used).map(entry => ({ handle: entry.id, name: entry.name, used: entry.used }));\n }\n\n private async find(id: number): Promise<StoredFileHandle | null> {\n return (await this.host.store.all()).find(entry => entry.id === id) ?? null;\n }\n\n /**\n * Keeps a handle and answers its number.\n *\n * The same file picked twice is one entry, not two: a picker hands\n * back a fresh handle object every time, so the comparison is\n * `isSameEntry` rather than identity. Past `RECENT_FILE_LIMIT` the\n * least recently used is let go.\n */\n private async remember(handle: ShellFileHandle): Promise<number> {\n const all = await this.host.store.all();\n const now = this.host.now();\n for (const entry of all) {\n if (entry.handle.isSameEntry !== undefined && (await entry.handle.isSameEntry(handle))) {\n return this.host.store.put({ id: entry.id, handle, name: handle.name, used: now });\n }\n }\n const id = await this.host.store.put({ handle, name: handle.name, used: now });\n const oldest = all.sort((a, b) => b.used - a.used).slice(RECENT_FILE_LIMIT - 1);\n for (const entry of oldest) {\n await this.host.store.delete(entry.id);\n }\n return id;\n }\n}\n\nasync function read(file: File, handle: number | null): Promise<ShellFile> {\n return {\n name: file.name,\n mediaType: file.type,\n lastModified: file.lastModified,\n bytes: await file.arrayBuffer(),\n handle\n };\n}\n\nasync function write(handle: ShellFileHandle, data: string | Uint8Array<ArrayBuffer>): Promise<void> {\n if (handle.createWritable === undefined) {\n throw new Error('This file cannot be written to.');\n }\n const stream = await handle.createWritable();\n await stream.write(data);\n await stream.close();\n}\n\n/**\n * Whether a handle may be used, asking when it may not yet.\n *\n * A handle from this session's picker is granted already; one from\n * yesterday's is `prompt`, and `requestPermission` is what shows the\n * browser's own question. A handle with neither method is from a\n * browser that does not ask, and is taken as granted.\n */\nasync function permitted(handle: ShellFileHandle, mode: 'read' | 'readwrite'): Promise<boolean> {\n if (handle.queryPermission === undefined) {\n return true;\n }\n if ((await handle.queryPermission({ mode })) === 'granted') {\n return true;\n }\n return handle.requestPermission !== undefined && (await handle.requestPermission({ mode })) === 'granted';\n}\n\nfunction pickerTypes(accept: readonly ShellFileType[]): PickerOptions {\n if (accept.length === 0) {\n return {};\n }\n return {\n types: accept.map(type => ({ description: type.description, accept: { [type.mediaType]: [...type.extensions] } })),\n excludeAcceptAllOption: false\n };\n}\n\nfunction inputAccept(accept: readonly ShellFileType[]): string {\n return accept.flatMap(type => [...type.extensions, type.mediaType]).join(',');\n}\n\nfunction ok(fields: Partial<Pick<ShellFileResult, 'files' | 'saved' | 'recent'>>): ShellFileResult {\n return { outcome: 'ok', files: [], saved: null, recent: [], error: null, ...fields };\n}\n\nfunction cancelled(): ShellFileResult {\n return { outcome: 'cancelled', files: [], saved: null, recent: [], error: null };\n}\n\nfunction denied(): ShellFileResult {\n return { outcome: 'denied', files: [], saved: null, recent: [], error: 'Permission to use the file was refused.' };\n}\n\n/**\n * An exception, as an outcome.\n *\n * A picker closed without choosing throws `AbortError`, which is the\n * person deciding and is `cancelled`. `NotAllowedError` and\n * `SecurityError` are the browser refusing — no gesture, or a frame\n * that may not — and are `denied`. Anything else failed.\n */\nfunction failure(error: unknown): ShellFileResult {\n const name = typeof error === 'object' && error !== null && 'name' in error ? String(error.name) : '';\n if (name === 'AbortError') {\n return cancelled();\n }\n const message = error instanceof Error ? error.message : error === null ? null : String(error);\n if (name === 'NotAllowedError' || name === 'SecurityError') {\n return { ...denied(), error: message };\n }\n return { outcome: 'failed', files: [], saved: null, recent: [], error: message };\n}\n\n/** Every buffer in an answer, for a shell that posts it across and can move them. */\nexport function fileResultTransfer(result: ShellFileResult): ArrayBuffer[] {\n return result.files.map(file => file.bytes);\n}\n\n// ---------------------------------------------------------------------------\n// The browser's host\n// ---------------------------------------------------------------------------\n\nconst DATABASE = 'gesso-shell-files';\nconst HANDLES = 'handles';\n\n/**\n * Handles kept in IndexedDB, which is one of the few places a\n * `FileSystemFileHandle` can be put: it is structured-cloneable, and a\n * store of them is what the platform intends \"recent files\" to be.\n * The database is opened on first use, so a page that never touches a\n * file never creates one.\n */\nexport class IndexedDbHandleStore implements ShellHandleStore {\n private opened: Promise<IDBDatabase> | null = null;\n\n constructor(private readonly factory: IDBFactory) {}\n\n async all(): Promise<StoredFileHandle[]> {\n const db = await this.database();\n return request(db.transaction(HANDLES, 'readonly').objectStore(HANDLES).getAll()) as Promise<StoredFileHandle[]>;\n }\n\n async put(entry: { id?: number; handle: ShellFileHandle; name: string; used: number }): Promise<number> {\n const db = await this.database();\n const key = await request(db.transaction(HANDLES, 'readwrite').objectStore(HANDLES).put(entry));\n return key as number;\n }\n\n async delete(id: number): Promise<void> {\n const db = await this.database();\n await request(db.transaction(HANDLES, 'readwrite').objectStore(HANDLES).delete(id));\n }\n\n private database(): Promise<IDBDatabase> {\n this.opened ??= new Promise((resolve, reject) => {\n const open = this.factory.open(DATABASE, 1);\n open.onupgradeneeded = () => open.result.createObjectStore(HANDLES, { keyPath: 'id', autoIncrement: true });\n open.onsuccess = () => resolve(open.result);\n open.onerror = () => reject(open.error);\n });\n return this.opened;\n }\n}\n\nfunction request<T>(req: IDBRequest<T>): Promise<T> {\n return new Promise((resolve, reject) => {\n req.onsuccess = () => resolve(req.result);\n req.onerror = () => reject(req.error);\n });\n}\n\n/** A store that forgets, for a window with no IndexedDB. */\nexport class MemoryHandleStore implements ShellHandleStore {\n private readonly entries = new Map<number, StoredFileHandle>();\n private next = 1;\n\n all(): Promise<StoredFileHandle[]> {\n return Promise.resolve([...this.entries.values()]);\n }\n\n put(entry: { id?: number; handle: ShellFileHandle; name: string; used: number }): Promise<number> {\n const id = entry.id ?? this.next++;\n this.entries.set(id, { id, handle: entry.handle, name: entry.name, used: entry.used });\n return Promise.resolve(id);\n }\n\n delete(id: number): Promise<void> {\n this.entries.delete(id);\n return Promise.resolve();\n }\n}\n\n/**\n * The host a real window gives: its pickers where it has them, a file\n * input and a download where it does not.\n */\nexport function browserFilesHost(view: Window & typeof globalThis): ShellFilesHost {\n const pickers = view as unknown as {\n showOpenFilePicker?: ShellFilesHost['showOpenFilePicker'];\n showSaveFilePicker?: ShellFilesHost['showSaveFilePicker'];\n };\n const document = view.document;\n return {\n store: view.indexedDB === undefined ? new MemoryHandleStore() : new IndexedDbHandleStore(view.indexedDB),\n now: () => Date.now(),\n ...(pickers.showOpenFilePicker === undefined\n ? {}\n : { showOpenFilePicker: options => pickers.showOpenFilePicker!.call(view, options) }),\n ...(pickers.showSaveFilePicker === undefined\n ? {}\n : { showSaveFilePicker: options => pickers.showSaveFilePicker!.call(view, options) }),\n pickWithInput: (accept, multiple) =>\n new Promise(resolve => {\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = accept;\n input.multiple = multiple;\n input.addEventListener('change', () => resolve(Array.from(input.files ?? [])), { once: true });\n // `cancel` is what a dismissed picker fires, where the browser\n // fires anything at all; without it a dismissal is a promise\n // that waits, which is what every file input has always done.\n input.addEventListener('cancel', () => resolve(null), { once: true });\n input.click();\n }),\n download: (name, blob) => {\n const url = view.URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = name;\n anchor.click();\n // Revoked after the click has been handled rather than at once:\n // a URL revoked synchronously is one some browsers never fetch.\n view.setTimeout(() => view.URL.revokeObjectURL(url), 60_000);\n }\n };\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 /** Pasted text, and the clipboard's HTML when it held some. */\n paste(text: string, html: string | null): 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, activeDescendant?: string): 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 'aria-expanded',\n 'aria-activedescendant'\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 // A field that opens a list (a combobox) keeps focus while a\n // highlight moves through the list; these say the list is open and\n // which option the highlight is on.\n setOrClear(\n textarea,\n 'aria-expanded',\n states.has('expanded') ? 'true' : states.has('collapsed') ? 'false' : undefined\n );\n setOrClear(textarea, 'aria-activedescendant', activeDescendant);\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 // The HTML as well, when the clipboard has it: a copy from a web\n // page or a document carries headings, lists and links there, and\n // an editor that keeps structure wants them.\n const html = clipboard?.getData('text/html') ?? '';\n if (text.length > 0 || html.length > 0) {\n this.sink.paste(text, html.length > 0 ? html : null);\n }\n };\n\n /**\n * A paste aimed at the canvas rather than at the field.\n *\n * When no editable has focus this proxy blurs and hands focus back\n * to the canvas, so the browser fires `paste` there — and the\n * listener above, which is on the textarea, never hears it. The\n * text was dropped before it reached the application at all, which\n * is half of why a grid could not be pasted into; the other half\n * was `UiEditingController.paste` having nowhere to send it.\n *\n * The two listeners cannot both fire for one paste: the event goes\n * to whichever element has focus, and only one of them ever does.\n */\n const onCanvasPaste = (event: Event): void => {\n if (this.doc.activeElement === textarea) {\n return;\n }\n onPaste(event);\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 // HTML too when the field's editing group gave some, so a copy\n // into a document or an email keeps its formatting. It describes\n // the selection the state carries, which is the one being copied.\n if (state.html !== undefined) {\n clipboard.setData('text/html', state.html);\n }\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 this.canvas.addEventListener('paste', onCanvasPaste);\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 this.canvas.removeEventListener('paste', onCanvasPaste);\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, and answers\n * whether it got there. The async API needs a secure context and, in\n * some browsers, a recent user gesture; the `execCommand` fallback\n * covers the rest. False means both refused. It never rejects.\n */\nexport function writeClipboard(text: string, doc: Document = document): Promise<boolean> {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard !== undefined && typeof clipboard.writeText === 'function') {\n return clipboard.writeText(text).then(\n () => true,\n () => copyWithExecCommand(text, doc)\n );\n }\n return Promise.resolve(copyWithExecCommand(text, doc));\n}\n\nfunction copyWithExecCommand(text: string, doc: Document): boolean {\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 // False when the browser refused, which it says rather than throws;\n // a document without the command at all throws.\n return doc.execCommand('copy');\n } catch {\n return false;\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 * Text pasted while a mirrored element holds focus.\n *\n * The same reasoning as the keys, and the same surprise: a paste\n * onto a grid lands on the mirror's element, not on the canvas and\n * not on the editing proxy — so a shell listening on either hears\n * nothing at all and the text is dropped before the application\n * sees it.\n */\n paste?(text: string, html: string | null): 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 /**\n * Describes the focused editable on the proxy's element, or clears it.\n * `activeDescendant` is the DOM id of the element its record's\n * `activeDescendant` names, for `aria-activedescendant`.\n */\n describe(record: UiSemanticsRecord | null, activeDescendant?: string): 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 multiselectable: ['aria-multiselectable', 'true'],\n current: ['aria-current', '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 'aria-multiselectable',\n 'aria-current',\n 'aria-activedescendant'\n];\n\nlet mirrors = 0;\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 * Prefixes every element's DOM id, which `aria-activedescendant`\n * refers to. Per mirror, because two apps on one page have nodes with\n * the same ids.\n */\n private readonly idPrefix = `gesso-${mirrors++}-`;\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 //\n // `clip` and not `hidden`: see `createElement`.\n overflow: 'clip',\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 element.id = this.idPrefix + 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.describeEditing(this.editing?.active === true ? record : null);\n }\n }\n\n /** Hands the proxy the focused editable's record, with its active descendant's DOM id. */\n private describeEditing(record: UiSemanticsRecord | null): void {\n const active = record?.activeDescendant;\n this.editing?.describe(record, active === undefined ? undefined : this.idPrefix + active);\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 // `clip` and not `hidden`. Every element is placed in canvas\n // pixels and the app scrolls its content by placing it again,\n // but a browser scrolls an `overflow: hidden` box to bring\n // something into view: Tab focus, a screen reader's cursor, an\n // automation tool clicking an element. A sidebar whose list\n // reached past its box was left scrolled, and every element in\n // it was described 49 pixels above where it is drawn and hit, so\n // activating one by its position activated its neighbour. A\n // `clip` box cannot be scrolled at all.\n overflow: 'clip',\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 if (record.activeDescendant !== undefined) {\n element.setAttribute('aria-activedescendant', this.idPrefix + record.activeDescendant);\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.describeEditing(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.describeEditing(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 const onPaste = (event: ClipboardEvent): void => {\n const text = event.clipboardData?.getData('text/plain') ?? '';\n const html = event.clipboardData?.getData('text/html') ?? '';\n if (text.length === 0 && html.length === 0) {\n return;\n }\n event.preventDefault();\n this.sink.paste?.(text, html.length > 0 ? html : null);\n };\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 this.container.addEventListener('paste', onPaste);\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 this.container.removeEventListener('paste', onPaste);\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 type { UiContrast } from 'gesso-core';\n\nimport { observeMediaQuery } from './mediaQuery';\n\n/**\n * Watches the platform's contrast preference.\n *\n * `high` when the person has asked for more contrast\n * (`prefers-contrast: more`, macOS's Increase contrast and its\n * counterparts) or forced colours are on (Windows' contrast themes). A\n * canvas gets no forced colours from the browser, which only repaints\n * the DOM, so the application has to hear about it and draw its own:\n * `withContrast(theme, contrast)` is the theme that does.\n *\n * Shell-side, reported once at once and again on change, for the reason\n * `observeColorScheme` gives.\n */\nexport function observeContrast(onChange: (contrast: UiContrast) => void): () => void {\n let more = false;\n let forced = false;\n let last: UiContrast | null = null;\n // Both queries are read before the first report, so a person whose\n // contrast comes from the second hears 'high' once, not 'standard' first.\n let ready = false;\n const report = (): void => {\n if (!ready) return;\n const contrast: UiContrast = more || forced ? 'high' : 'standard';\n if (contrast !== last) {\n last = contrast;\n onChange(contrast);\n }\n };\n const stopMore = observeMediaQuery('(prefers-contrast: more)', matches => {\n more = matches;\n report();\n });\n const stopForced = observeMediaQuery('(forced-colors: active)', matches => {\n forced = matches;\n report();\n });\n ready = true;\n report();\n return () => {\n stopMore();\n stopForced();\n };\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","/**\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","import type { UiNodeReport } from '../NodeReport';\nimport type { DevtoolsEvent, DevtoolsRequest } from '../DevtoolsProtocol';\nimport { isConsoleEntryMessage, type ConsoleForwardingMessage } from '../../worker/captureConsole';\nimport type { AgentConfirmation } from '../../agent/AgentSurface';\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 { attachFileDrop } from '../fileDrop';\nimport { browserFilesHost, fileResultTransfer, ShellFiles } from '../shellFiles';\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 { observeContrast } from '../contrast';\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 * Offer the application's channels to an AI agent in the browser,\n * through WebMCP (default false). Each channel's view and commands\n * become tools registered with `document.modelContext` once the app\n * mounts, removed when it is disposed. A browser without WebMCP\n * registers nothing. Pass `{ confirm }` to ask the person about a\n * `@confirm` command with the application's own dialog instead of\n * the browser's. `gesso-framework/agent` has the rest.\n */\n webmcp?: boolean | { confirm?: (request: AgentConfirmation) => boolean | Promise<boolean> };\n /**\n * The app is the page (default false): a key pressed while nothing\n * on the page has focus goes to the app, and the canvas takes focus.\n *\n * Keys reach the app through the canvas, which has focus only once\n * something put it there. A page that is all app loads with focus on\n * the body, so its shortcuts did nothing until the first click, and a\n * person who pressed `c` or Mod+K straight away got nothing. An app\n * embedded in a page with other things on it leaves this off: a key\n * pressed on that page isn't its.\n */\n pageKeys?: boolean;\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 * Cancel the browser's default for any key this answers true for —\n * Ctrl/Cmd+S for an app with a Save of its own, Ctrl/Cmd+O for one\n * with an Open. `interceptFind` for any shortcut, on its terms: the\n * shell has to decide before the worker has heard of the key, so the\n * decision is the application's, made here on the main thread, and a\n * predicate rather than a list so it can say \"Ctrl or Cmd\" in one\n * line.\n *\n * Called for every keydown that reaches the shell, so it should be a\n * comparison and nothing more.\n */\n interceptKey?: (event: KeyboardEvent) => 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 /** Pickers and remembered handles, made on the first file request. */\n private files: ShellFiles | 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 /**\n * Opens a port to whatever the render worker serves under `key`, or\n * returns undefined before `mount` has started it. The development\n * agent bridge asks for `gesso:agent` this way; nothing else in the\n * shell talks to the render worker except through its protocol.\n */\n openRenderPort(key: string): MessagePort | undefined {\n return this.renderWorker === undefined ? undefined : portHandle(this.renderWorker).open(key);\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 createSyncApp(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, html) => this.post({ type: 'paste', text, ...(html === null ? {} : { html }) }),\n blur: () => this.post({ type: 'blur' }),\n // From the proxy, a printable key's text follows it as a\n // `beforeinput`; see `forwardKeyDown`.\n keyDown: event => this.forwardKeyDown(event, true),\n keyUp: event => this.forwardKeyUp(event)\n });\n if (this.options.accessibility !== false) {\n // The off-screen DOM an assistive technology reads. Keys and\n // pastes are forwarded from it for the same reason they are\n // forwarded from the proxy: while the app has focus, the element\n // holding it is 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 paste: (text, html) => this.post({ type: 'paste', text, ...(html === null ? {} : { html }) })\n },\n this.proxy\n );\n }\n\n const webmcp = this.options.webmcp;\n if (webmcp !== undefined && webmcp !== false) {\n // Loaded on demand: the shell carries the agent code only for an\n // application that asked for it, and only once it is mounted.\n void import('../../agent/webmcp')\n .then(agent => agent.connectWebMcp(this, webmcp === true ? {} : webmcp))\n .then(\n disconnect => {\n if (this.renderWorker === worker) {\n this.disconnectWebMcp = disconnect;\n } else {\n disconnect();\n }\n },\n (error: unknown) => console.warn('[gesso] WebMCP could not register the channels:', error)\n );\n }\n\n return () => this.dispose();\n }\n\n /** Removes the WebMCP tools `webmcp` registered, if it did. */\n private disconnectWebMcp: (() => void) | null = null;\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 /**\n * A key, to the worker, saying whether its text will follow it.\n *\n * Only the proxy turns a key into text. The proxy takes DOM focus when\n * the worker reports a focused editable, which is a round trip — so a\n * key typed in the gap, after an editable has the focus in the worker\n * and before the proxy has it here, reaches the canvas, and the text\n * that key would have produced never exists. The worker is told, and\n * inserts the key itself. Typing quickly into a spreadsheet cell that\n * the first letter opens lives in exactly that gap.\n */\n private forwardKeyDown(event: KeyboardEvent, viaProxy = false): 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)) || this.options.interceptKey?.(event) === true) {\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({\n type: 'keyDown',\n key: event.key,\n modifiers: modifiersFrom(event),\n at: epochFromEvent(event),\n textFollows: viaProxy\n });\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.disconnectWebMcp?.();\n this.disconnectWebMcp = null;\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 layoutPasses: message.layoutPasses,\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 // Answered on every path: `writeClipboard` never rejects.\n void writeClipboard(message.text).then(copied => this.post({ type: 'clipboardResult', id: message.id, copied }));\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 === 'file') {\n // Pickers, handles and IndexedDB are all the window's. The answer\n // is posted on every path — `perform` never rejects — and a file\n // read goes back with its buffer moved rather than copied.\n this.files ??= new ShellFiles(browserFilesHost(window));\n void this.files\n .perform(message.request)\n .then(result => this.post({ type: 'fileResult', id: message.id, result }, fileResultTransfer(result)));\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, transfer: Transferable[] = []): 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, transfer);\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 detachContrast = observeContrast(contrast => this.post({ type: 'contrast', contrast }));\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 // Files dragged in from the desktop. Their bytes are moved to the\n // render worker rather than copied: a dropped file is the largest\n // thing this shell ever posts, and it has no further use for it.\n const detachFileDrop = attachFileDrop(canvas, toLocal, (message, transfer) => this.post(message, transfer));\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 // A key aimed at the page itself, when the app is the page: the\n // canvas takes focus, so the keys after it arrive the usual way.\n const aimedAtPage = (target: EventTarget | null): boolean =>\n target === null || target === document || target === document.body || target === document.documentElement;\n const onPageKeyDown = (event: KeyboardEvent): void => {\n if (aimedAtPage(event.target)) {\n canvas.focus({ preventScroll: true });\n this.forwardKeyDown(event);\n }\n };\n const onPageKeyUp = (event: KeyboardEvent): void => {\n if (aimedAtPage(event.target)) {\n this.forwardKeyUp(event);\n }\n };\n const pageKeys = this.options.pageKeys === true;\n if (pageKeys) {\n document.addEventListener('keydown', onPageKeyDown);\n document.addEventListener('keyup', onPageKeyUp);\n }\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 detachContrast();\n detachFullscreen();\n detachFileDrop();\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 if (pageKeys) {\n document.removeEventListener('keydown', onPageKeyDown);\n document.removeEventListener('keyup', onPageKeyUp);\n }\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 { 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 whose UI work happens off the main\n * thread.\n *\n * A class reference cannot cross postMessage, so the root component\n * has to already be inside the worker that renders it: this side names\n * the workers and nothing else. With `gesso-vite-plugin` in the Vite\n * config the shell names none of them either —\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 * The single-thread configuration is `createSyncApp`, and it is a\n * second function rather than a second overload because this one must\n * not be able to reach the engine — see there for what that is worth\n * in kilobytes.\n */\nexport function createApp(options?: CreateAppOptions): WorkerApp {\n if (!isWorkerAppOptions(options)) {\n throw new Error(\n 'createApp() builds the worker configuration and was given a component. Use createSyncApp(Root) from ' +\n 'gesso-framework for the single-thread one, or pass options: createApp({ renderWorker: () => ... }).'\n );\n }\n if (options?.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(options as WorkerAppOptions);\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 *\n * It survives the split into two functions because it is what tells\n * somebody who called the wrong one which one they wanted, and a\n * string costs nothing to bundle.\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 type { UiNodeReport } from './NodeReport';\nimport type { FrameMetrics } from './GessoRuntime';\nimport type { Patch } from '../channel/StorePatch';\nimport type { RuntimeErrorSource } from './worker/RenderWorkerProtocol';\n\n/**\n * What a devtools panel says to a running application and what it\n * hears back, as plain data.\n *\n * The node inspector answered \"what is the thing under the pointer\";\n * a panel docked outside the page asks the questions a DOM inspector\n * answers instead: what is the whole tree, what is this node I picked\n * from it, outline it for me, and what did the workers log. Every\n * answer here is built where the tree is and crosses a `postMessage`\n * as strings and numbers, for the same reason `UiNodeReport` does:\n * nothing in the render thread can be handed out, and a panel that\n * lives in an extension is two message hops away from it anyway.\n *\n * The same vocabulary serves both configurations. `WorkerApp` carries\n * it over the render worker protocol; `GessoApp`, with the runtime in\n * the same thread, answers the requests directly. A panel cannot tell\n * which it is talking to, which is what lets one panel serve both.\n */\n\n/** One node of the tree, as a panel lists it. */\nexport interface UiTreeNode {\n readonly id: string;\n /** `UiNodeType`, as its string value. */\n readonly type: string;\n /**\n * The component this node is the anchor of, when it is one.\n *\n * Only the anchor carries the name. A panel wanting \"which component\n * rendered this node\" reads it off the nearest ancestor that has\n * one, which is the same walk `UiNodeReport.owners` makes and\n * costs the snapshot nothing per node.\n */\n readonly component?: string;\n /** A text node's text, shortened, so the tree reads like the screen. */\n readonly text?: string;\n /**\n * Live subscriptions the node holds: bound properties, reactive\n * children, event handlers. Absent when it holds none, which is most\n * nodes.\n *\n * On the tree rather than in a report of its own because the answer\n * a subscription view needs is per component, and the tree is what\n * says which component a node belongs to. A panel adds them up\n * towards the nearest ancestor carrying a `component`, and a leak is\n * the count that climbs while the shape stays still.\n */\n readonly subscriptions?: number;\n readonly children: readonly UiTreeNode[];\n}\n\n/** The whole tree at one moment. */\nexport interface UiTreeSnapshot {\n readonly root: UiTreeNode;\n /** How many nodes the snapshot holds, so a panel can say so without counting. */\n readonly nodes: number;\n /**\n * Live subscriptions across the whole graph, including the runtime's\n * own nodes that the snapshot does not list. The number to watch\n * while doing nothing.\n */\n readonly subscriptions: number;\n}\n\n/**\n * What was being answered when an action was recorded\n *.\n *\n * The barrier carries no request id: a command goes up, patches come\n * down, and nothing in the protocol ties the second to the first. So\n * the tie is made where both are seen, by the recorder, and it starts\n * from the one thing that is unambiguous — the input being dispatched\n * when the command was sent. Everything the input caused carries the\n * same id, and a panel groups by it instead of comparing timestamps\n * across three threads.\n */\nexport interface ActionCause {\n /** Rising from 1, within one recorder. */\n readonly id: number;\n /** The input that started it: `pointerUp (412, 233)`, `keyDown Enter`. */\n readonly label: string;\n}\n\ninterface ActionEntryBase {\n /** Position on the timeline. Never reused, never renumbered. */\n readonly seq: number;\n /** `performance.now()` on the thread that recorded it. */\n readonly at: number;\n /** The input this entry belongs to, when it belongs to one. */\n readonly cause?: ActionCause;\n}\n\n/** A command a view sent across the barrier. */\nexport interface CommandEntry extends ActionEntryBase {\n readonly kind: 'command';\n readonly channel: string;\n readonly command: string;\n readonly payload: unknown;\n}\n\n/** A batch of patches the owning thread sent back. */\nexport interface PatchEntry extends ActionEntryBase {\n readonly kind: 'patch';\n readonly channel: string;\n readonly patches: readonly Patch[];\n /** The projections this batch touched, in the order first touched. */\n readonly keys: readonly string[];\n}\n\n/** A channel error, kept on the timeline so it has a position on it. */\nexport interface ChannelErrorEntry extends ActionEntryBase {\n readonly kind: 'error';\n readonly channel: string;\n readonly message: string;\n}\n\n/**\n * The frame that drew what came before it.\n *\n * The last link in the chain, and the reason it is an entry rather\n * than a field on the patches: a patch batch is recorded when it\n * arrives, which is before the frame that applies it exists. Stamping\n * it later would mean amending an entry a panel has already been sent.\n * A row on the timeline says the same thing by position, and says it\n * once.\n *\n * Recorded only for a frame that has something to close, so an idle\n * application's ticker does not fill the log with frames.\n */\nexport interface FrameEntry extends ActionEntryBase {\n readonly kind: 'frame';\n /** `UiFrame.id`, the same number `FrameMetrics.frame` carries. */\n readonly frame: number;\n}\n\n/** One line of a store action log. */\nexport type ActionEntry = CommandEntry | PatchEntry | ChannelErrorEntry | FrameEntry;\n\n/** One `console.*` call made in a worker, forwarded with its thread named. */\nexport interface ConsoleEntry {\n readonly thread: 'render' | 'app';\n readonly level: 'log' | 'info' | 'warn' | 'error' | 'debug';\n /** Each argument printed as one string; an Error carries its stack. */\n readonly args: readonly string[];\n /** `Date.now()` in the thread that logged. */\n readonly at: number;\n}\n\n/** What a panel asks. */\nexport type DevtoolsRequest =\n /** One tree snapshot, now. */\n | { kind: 'tree' }\n /**\n * A snapshot after every frame that changed the shape or the text\n * of the tree, until turned off. Frames that only moved boxes send\n * nothing, because the tree a panel lists did not change.\n */\n | { kind: 'watchTree'; enabled: boolean }\n /** A report on one node, now; `null` comes back for an id the tree no longer has. */\n | { kind: 'inspect'; id: string }\n /**\n * Keep one node's report fresh: a report now, and another after any\n * frame that changed it, until a different node (or null) is\n * selected. This is the panel's selection.\n */\n | { kind: 'select'; id: string | null }\n /**\n * Outline a node on the canvas, the way the DOM inspector outlines an\n * element hovered in its tree. Independent of the layout inspector's\n * own toggle, so a panel can point at a node without turning on the\n * heatmap.\n */\n | { kind: 'highlight'; id: string | null }\n /** Forward `console.*` from the workers, with the thread named. */\n | { kind: 'console'; enabled: boolean }\n /** A `frame` event per frame, until turned off: the profiler's feed. */\n | { kind: 'watchFrames'; enabled: boolean }\n /**\n * The layout inspector's own toggle: hover boxes and the measure\n * heatmap on the canvas, and a `hover` event for the node under the\n * pointer. The panel's \"pick from the canvas\".\n */\n | { kind: 'inspector'; enabled: boolean }\n /**\n * Writes a property on a node the panel picked, which is the half of\n * the addressed channel the inspector deferred: `select` names a\n * node to read, this one names a node to change.\n *\n * The write goes through the graph like any other, so the override\n * cascade, the equality check and the dirty marking all apply, and\n * the frame that follows is an ordinary frame. Two consequences to\n * be honest about: a bound property is overwritten by its stream's\n * next emission, and a value the element declares again on the next\n * rebuild comes back. This edits the tree, not the code.\n *\n * `value` is plain data. `null` removes the property, which is what\n * lets a value put back an inherited one.\n */\n | { kind: 'setProp'; id: string; name: string; value: unknown }\n /** Frame and channel spans in the browser's own profiler, until turned off. */\n | { kind: 'marks'; enabled: boolean };\n\n/** What the application answers, and volunteers while something is watched. */\nexport type DevtoolsEvent =\n | { kind: 'tree'; tree: UiTreeSnapshot }\n | { kind: 'report'; id: string; report: UiNodeReport | null }\n | { kind: 'console'; entry: ConsoleEntry }\n | { kind: 'frame'; metrics: FrameMetrics }\n /**\n * One line of a store action log recorded on the thread the ports\n * are on.\n *\n * A log in the page reaches a panel through the devtools hook, which\n * has the log itself to read. A log in the render worker has no such\n * route: the shell holds neither end of a channel there, by design\n *, so the entries come out the way every other\n * answer from that thread does, as plain data on the devtools\n * channel.\n */\n | { kind: 'action'; entry: ActionEntry }\n /** The node under the pointer while the inspector is on; null when none, or when it was turned off. */\n | { kind: 'hover'; report: UiNodeReport | null }\n /**\n * An error the render worker reported to its shell. Only a worker\n * shell sends these: with the runtime in the page, an error is an\n * ordinary page error and the page's console already has it.\n */\n | { kind: 'error'; message: string; stack?: string; source: RuntimeErrorSource };\n\n/** Longer text than this is cut in a tree snapshot; the report has the whole of it. */\nexport const TREE_TEXT_LIMIT = 40;\n\n/** A text node's text as the tree shows it. */\nexport function treeText(text: unknown): string | undefined {\n if (typeof text !== 'string' || text === '') {\n return undefined;\n }\n const flat = text.replace(/\\s+/g, ' ');\n return flat.length > TREE_TEXT_LIMIT ? `${flat.slice(0, TREE_TEXT_LIMIT - 1)}…` : flat;\n}\n","import { type Observable, Subject } from 'rxjs';\n\nimport { linear } from 'gesso-core';\n\nimport { computed, type ComputedCell } from '../computed';\nimport { internalState } from '../InternalState';\nimport type { AnimationService } from './AnimationService';\nimport { epochNow } from './worker/RenderWorkerProtocol';\n\n/**\n * Where a playback stands, as the shell last reported it.\n *\n * A *sample*, not a stream: the shell sends one whenever the element\n * changes state and otherwise about once a second, and `AudioService`\n * moves `position` forward between samples on the animation driver.\n * That keeps the message rate at one a second while a seek bar moves\n * every frame, the same arrangement video time uses.\n */\nexport interface AudioSample {\n readonly status: AudioStatus;\n /** Seconds into the track, as of `at`. */\n readonly position: number;\n /** Seconds, or `NaN` until the element knows. */\n readonly duration: number;\n /** Seconds: the end of the buffered range that holds `position`. */\n readonly buffered: number;\n /** When the shell took the sample, on the epoch clock both threads share (`epochNow`). */\n readonly at: number;\n /** What went wrong, for `error`, or why a play was refused, for `paused`. */\n readonly error?: string;\n}\n\n/**\n * - `idle`: nothing loaded.\n * - `loading`: a source is set and the element is fetching or\n * stalled; the position does not advance.\n * - `playing`, `paused`, `ended`: what they say.\n * - `error`: the element gave up on this source.\n */\nexport type AudioStatus = 'idle' | 'loading' | 'playing' | 'paused' | 'ended' | 'error';\n\n/** What the render thread asks the shell's audio element to do. */\nexport type AudioRequest =\n | { readonly type: 'load'; readonly src: string; readonly autoplay: boolean }\n /**\n * Start buffering the source that will play next, on a second\n * element, so the change when it arrives is gapless.\n *\n * A hint and not a command: nothing plays, nothing is reported, and a\n * `load` of some other source ignores it. A `load` of exactly this\n * source is what redeems it. An empty string clears it.\n */\n | { readonly type: 'preload'; readonly src: string }\n | { readonly type: 'play' }\n | { readonly type: 'pause' }\n | { readonly type: 'seek'; readonly seconds: number }\n | { readonly type: 'volume'; readonly level: number }\n | { readonly type: 'metadata'; readonly metadata: AudioMetadata | null };\n\n/**\n * What the platform's own controls asked for: the keyboard's media\n * keys, the lock screen, the OS media overlay. Play and pause are\n * carried out by the shell on the element itself and reported back as\n * a sample; `next` and `previous` mean nothing to an element, so they\n * come here for the application to answer.\n */\nexport type AudioAction = 'play' | 'pause' | 'next' | 'previous';\n\n/** What the OS shows for the playing track, through the Media Session API. */\nexport interface AudioMetadata {\n readonly title: string;\n readonly artist: string;\n readonly album?: string;\n /** A square picture's url. */\n readonly artwork?: string;\n}\n\n/** The sample, with the position moved to now and the source it is for. */\nexport interface AudioState {\n readonly status: AudioStatus;\n readonly position: number;\n readonly duration: number;\n readonly buffered: number;\n readonly src: string | null;\n readonly error?: string;\n}\n\nconst IDLE: AudioSample = { status: 'idle', position: 0, duration: NaN, buffered: 0, at: 0 };\n\n/** How often the extrapolated position is written between samples. Ten a second is smooth on a seek bar and cheap. */\nconst POSITION_STEP_MS = 100;\n/** How far to extrapolate a playback whose duration the element has not reported yet. */\nconst UNKNOWN_DURATION_HORIZON_S = 24 * 60 * 60;\n\n/**\n * Sound, as a component reaches it.\n *\n * No worker can make a sound: `HTMLAudioElement` and `AudioContext`\n * exist only on a thread with a window. So the element lives on the\n * shell, as a *sink* behind a handful of messages, in the way the shell\n * already answers `clipboard` and `openUrl`, and this store is its\n * client on the render thread. Every runtime registers one, like\n * `ShellService`; being a store keeps the rule that components reach\n * the outside world through actions only, and gives a desktop shell one\n * place to bind a native player.\n *\n * Time is the interesting part. The shell samples the element on state\n * changes and once a second; between samples `position` is driven by a\n * linear tween on the animation driver, so the seek bar moves at the\n * frame rate while the barrier carries one small message a second. A\n * sample stops the tween and restarts it from the truth, which is also\n * what corrects any drift. Nothing here decides what to play next: the\n * application does, from `state` and `actions`.\n */\nexport class AudioService {\n private handler: ((request: AudioRequest) => void) | null = null;\n private animations: AnimationService | null = null;\n private readonly sample = internalState<AudioSample>(IDLE);\n private readonly source = internalState<string | null>(null);\n private readonly position = internalState(0);\n private readonly actionSubject = new Subject<AudioAction>();\n\n /**\n * The playback, with the position moved to now.\n *\n * A cell rather than a stream, which is what lets a screen write\n * `computed(() => audio.state.value.status === 'playing')` instead of\n * listing it as a source of a `derive`. It is the\n * same expression `current` is, and the same three cells underneath;\n * being a `computed` is what gives it a current value as well as a\n * stream, without a second copy of the arithmetic.\n */\n readonly state: ComputedCell<AudioState> = computed(() => this.current, { label: 'AudioService.state' });\n\n /** What the platform's media controls asked for. */\n readonly actions: Observable<AudioAction> = this.actionSubject.asObservable();\n\n /** The current state, for code that needs it without subscribing. */\n get current(): AudioState {\n const sample = this.sample.value;\n return {\n status: sample.status,\n position: this.position.value,\n duration: sample.duration,\n buffered: sample.buffered,\n src: this.source.value,\n ...(sample.error === undefined ? {} : { error: sample.error })\n };\n }\n\n /** Installed by the runtime; a request with no handler is dropped. */\n setHandler(handler: ((request: AudioRequest) => void) | null): void {\n this.handler = handler;\n }\n\n /** Installed by the runtime: the driver `position` moves on between samples. */\n setAnimations(animations: AnimationService | null): void {\n this.animations = animations;\n }\n\n /**\n * Called by the runtime when the shell reports the element.\n *\n * Not for applications: the shell is the only thing that knows the\n * answer, and `state` is how an application hears about it.\n */\n applySample(sample: AudioSample): void {\n this.sample.value = sample;\n const elapsed = sample.status === 'playing' ? Math.max(0, (epochNow() - sample.at) / 1000) : 0;\n const known = Number.isFinite(sample.duration);\n const now = known ? Math.min(sample.position + elapsed, sample.duration) : sample.position + elapsed;\n this.animations?.stop(this.position);\n this.position.value = now;\n if (sample.status !== 'playing' || this.animations === null) {\n return;\n }\n const target = known ? sample.duration : now + UNKNOWN_DURATION_HORIZON_S;\n if (target <= now) {\n return;\n }\n // A readout, not motion: it keeps moving under reduced motion, as\n // the spinner does, because a seek bar that stood still would say\n // the music had stopped.\n this.animations.animate(this.position, target, {\n duration: (target - now) * 1000,\n easing: linear,\n stepMs: POSITION_STEP_MS,\n reducedMotion: 'keep'\n });\n }\n\n /** Called by the runtime when the platform's controls act. Not for applications. */\n applyAction(action: AudioAction): void {\n this.actionSubject.next(action);\n }\n\n /**\n * Loads a source and, by default, starts it.\n *\n * Reported as `loading` at once rather than waiting for the shell,\n * so a screen that pressed play shows something before the round\n * trip comes back.\n */\n load(src: string, options: { readonly autoplay?: boolean } = {}): void {\n this.animations?.stop(this.position);\n this.source.value = src;\n this.position.value = 0;\n this.sample.value = { status: 'loading', position: 0, duration: NaN, buffered: 0, at: epochNow() };\n this.handler?.({ type: 'load', src, autoplay: options.autoplay ?? true });\n }\n\n /**\n * Says what will play next, so the shell can have it buffered before\n * it is asked for.\n *\n * Nothing about the current playback changes, and nothing is\n * reported: this only speaks to a second element the shell keeps. A\n * later `load` of the same source is answered from what was\n * buffered, and a `load` of anything else discards it. Pass an empty\n * string when nothing follows.\n */\n preload(src: string): void {\n this.handler?.({ type: 'preload', src });\n }\n\n play(): void {\n this.handler?.({ type: 'play' });\n }\n\n pause(): void {\n this.handler?.({ type: 'pause' });\n }\n\n /**\n * Moves to a position. Written locally at once, so a dragged seek bar\n * follows the finger rather than the round trip; the element's own\n * `seeked` sample confirms it.\n */\n seek(seconds: number): void {\n this.animations?.stop(this.position);\n this.position.value = Math.max(0, seconds);\n this.handler?.({ type: 'seek', seconds: Math.max(0, seconds) });\n }\n\n /** 0 to 1. */\n setVolume(level: number): void {\n this.handler?.({ type: 'volume', level: Math.min(1, Math.max(0, level)) });\n }\n\n /** What the OS shows for the track; null clears it. */\n setMetadata(metadata: AudioMetadata | null): void {\n this.handler?.({ type: 'metadata', metadata });\n }\n}\n","import { internalState, type InternalState } from '../InternalState';\nimport type { RouteDefinition } from './RouteDefinition';\n\n/**\n * What a route remembers while its screen is not there.\n *\n * A screen is built when its route matches and destroyed when it stops\n * matching, so everything a screen keeps in its body goes with it: the\n * scroll offset of the list, which row the keyboard was on, the text in\n * the filter field. Coming back lands at the top of the list, which is\n * the wrong place, and it also breaks a shared element, because a morph\n * is measured from where the element is *seen* and a list at the top is\n * not where it was left.\n *\n * Both applications solved this by putting those cells in module scope,\n * with the same comment on each explaining why. This\n * is that store, with an owner. What it buys over a module:\n *\n * - **A lifetime that is stated.** A cell here lives as long as the\n * router does and is dropped by `forget`, rather than as long as the\n * module registry, which is until the tab closes.\n * - **One store per runtime.** A module's cells are shared by every\n * runtime in the process, which two windows of the same desktop app\n * and two tests in the same file both are.\n * - **Names that cannot collide.** A key is scoped to a route, so two\n * screens may both remember `scroll` and mean different lists.\n *\n * It is a facility and not an architecture. The framework does not own\n * an application's data: anything that matters after\n * a reload, or that another part of the application acts on, is still\n * application state on a channel. What belongs here is the small,\n * screen-shaped remainder that only exists to put a screen back where it\n * was.\n */\nexport class RouteState {\n /**\n * `null` is the router's own scope: what belongs to the navigation\n * rather than to one screen. A `Map` keyed on the route object needs\n * no ids and no path strings, and a route that is redeclared is a\n * different route.\n */\n private readonly byRoute = new Map<RouteDefinition | null, Map<string, InternalState<unknown>>>();\n\n /**\n * The cell a route keeps under `key`, created with `initial` the first\n * time it is asked for and handed back unchanged afterwards.\n *\n * `initial` is therefore read once. A screen built a second time gets\n * the value the first one left, which is the whole point.\n */\n cell<T>(route: RouteDefinition | null, key: string, initial: T): InternalState<T> {\n let keys = this.byRoute.get(route);\n if (keys === undefined) {\n keys = new Map<string, InternalState<unknown>>();\n this.byRoute.set(route, keys);\n }\n const held = keys.get(key);\n if (held !== undefined) {\n return held as InternalState<T>;\n }\n const cell = internalState(initial, `${route === null ? 'router' : route.path}.${key}`);\n keys.set(key, cell as InternalState<unknown>);\n return cell;\n }\n\n /** Whether a route has remembered anything under this key yet. */\n has(route: RouteDefinition | null, key: string): boolean {\n return this.byRoute.get(route)?.has(key) ?? false;\n }\n\n /**\n * Drops everything a route remembered, so the next screen starts as\n * the first one did: signing out, or a list whose contents are no\n * longer the ones the offset was measured against.\n */\n forget(route: RouteDefinition | null): void {\n this.byRoute.delete(route);\n }\n\n /** Drops every route's memory. */\n clear(): void {\n this.byRoute.clear();\n }\n}\n","/**\n * Paths, patterns and the params inside them.\n *\n * A pattern is a path with `:name` in the places that vary:\n * `/mail/:folder/:id`. Nothing else is special — no regular\n * expressions, no optional segments, no repeats. That is deliberate:\n * every extra form is one more thing the type below has to model, and\n * a pattern whose params the compiler cannot name is a pattern that\n * gives up the one thing this router is for.\n *\n * A trailing `/*` is the exception, and only because a layout route\n * has to be able to say \"and anything below me\". It captures the rest\n * of the path as `rest`.\n */\n\n/** One piece of a parsed pattern. */\nexport type PatternSegment =\n | { readonly kind: 'static'; readonly text: string }\n | { readonly kind: 'param'; readonly name: string }\n | { readonly kind: 'rest' };\n\n/**\n * The params a pattern declares, as a type.\n *\n * `RouteParams<'/mail/:folder/:id'>` is `{ folder: string; id: string }`,\n * so `router.go(MailItem, { folder: 'inbox', id: '2' })` is checked, a\n * misspelled param is a compile error, and a missing one is too. This\n * is the whole reason routes are declared with full paths rather than\n * relative fragments: a relative fragment knows only its own segments,\n * and the params a screen actually receives include its parents'.\n */\nexport type RouteParams<Path extends string> = Prettify<ParamsOf<Path>>;\n\ntype ParamsOf<Path extends string> = Path extends `${infer Head}/${infer Rest}`\n ? ParamOfSegment<Head> & ParamsOf<Rest>\n : ParamOfSegment<Path>;\n\ntype ParamOfSegment<Segment extends string> = Segment extends `:${infer Name}`\n ? { [K in Name]: string }\n : Segment extends '*'\n ? { rest: string }\n : {};\n\n/** Flattens an intersection so hovering a param object shows its keys. */\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\n/** True when the pattern declares no params, so `go()` may omit them. */\nexport type HasNoParams<Path extends string> = keyof ParamsOf<Path> extends never ? true : false;\n\n/**\n * Splits a path into its segments, ignoring leading, trailing and\n * doubled slashes so `/mail/`, `mail` and `//mail` are one path.\n */\nexport function pathSegments(path: string): string[] {\n return path.split('/').filter(segment => segment.length > 0);\n}\n\n/** Parses a pattern once, at declaration time. */\nexport function parsePattern(pattern: string): PatternSegment[] {\n return pathSegments(pattern).map((segment, index, all) => {\n if (segment === '*') {\n if (index !== all.length - 1) {\n throw new Error(`Route pattern '${pattern}' has '*' before its last segment; a rest may only end a pattern.`);\n }\n return { kind: 'rest' as const };\n }\n if (segment.startsWith(':')) {\n const name = segment.slice(1);\n if (name.length === 0) {\n throw new Error(`Route pattern '${pattern}' has an unnamed ':' segment.`);\n }\n return { kind: 'param' as const, name };\n }\n return { kind: 'static' as const, text: segment };\n });\n}\n\n/**\n * Matches a parsed pattern against a path's segments.\n *\n * Returns the captured params, or null when the pattern does not\n * describe this path. An exact match is required: a pattern with\n * three segments does not match a path with four unless it ends in a\n * rest.\n */\nexport function matchPattern(\n segments: readonly PatternSegment[],\n path: readonly string[]\n): Record<string, string> | null {\n const params: Record<string, string> = {};\n for (let index = 0; index < segments.length; index++) {\n const segment = segments[index]!;\n if (segment.kind === 'rest') {\n params.rest = path.slice(index).join('/');\n return params;\n }\n const value = path[index];\n if (value === undefined) {\n return null;\n }\n if (segment.kind === 'static') {\n if (segment.text !== value) {\n return null;\n }\n continue;\n }\n params[segment.name] = decodeURIComponent(value);\n }\n return path.length === segments.length ? params : null;\n}\n\n/**\n * Fills a pattern's params back in, which is what makes a link out of\n * a route and a params object.\n */\nexport function buildPath(pattern: string, params: Readonly<Record<string, string>> = {}): string {\n const parts = parsePattern(pattern).map(segment => {\n if (segment.kind === 'static') {\n return segment.text;\n }\n if (segment.kind === 'rest') {\n return params.rest ?? '';\n }\n const value = params[segment.name];\n if (value === undefined) {\n throw new Error(`Route '${pattern}' needs a '${segment.name}' param.`);\n }\n return encodeURIComponent(value);\n });\n const path = parts.filter(part => part.length > 0).join('/');\n return `/${path}`;\n}\n\n/** A url split into the path the routes match and the query they carry. */\nexport interface ParsedUrl {\n readonly path: string;\n readonly query: Readonly<Record<string, string>>;\n}\n\n/**\n * Splits a url into path and query.\n *\n * Any hash is dropped: the shell decides whether the fragment is where\n * the app's url lives (see `shellHistory`), and by the time a url\n * reaches the router that decision has already been unwound.\n */\nexport function parseUrl(url: string): ParsedUrl {\n const withoutHash = url.split('#')[0] ?? '';\n const [rawPath = '', rawQuery = ''] = splitOnce(withoutHash, '?');\n const query: Record<string, string> = {};\n for (const pair of rawQuery.split('&')) {\n if (pair.length === 0) {\n continue;\n }\n const [key = '', value = ''] = splitOnce(pair, '=');\n query[decodeURIComponent(key)] = decodeURIComponent(value.replace(/\\+/g, ' '));\n }\n return { path: `/${pathSegments(rawPath).join('/')}`, query };\n}\n\n/** Joins a path and a query back into the url the history stores. */\nexport function formatUrl(path: string, query: Readonly<Record<string, string>> = {}): string {\n const pairs = Object.entries(query).map(([key, value]) => `${encodeQueryPart(key)}=${encodeQueryPart(value)}`);\n const normalized = `/${pathSegments(path).join('/')}`;\n return pairs.length === 0 ? normalized : `${normalized}?${pairs.join('&')}`;\n}\n\n/**\n * Encodes a query key or value, leaving the characters a query may hold\n * as they are (`,` `:` `@` `/`), so a list of values reads as one:\n * `status=todo,done`, not `status=todo%2Cdone`. What would change how\n * the query parses (`&`, `=`, `+`, `#`) is still encoded.\n */\nfunction encodeQueryPart(text: string): string {\n return encodeURIComponent(text)\n .replace(/%2C/gi, ',')\n .replace(/%3A/gi, ':')\n .replace(/%40/gi, '@')\n .replace(/%2F/gi, '/');\n}\n\nfunction splitOnce(text: string, separator: string): [string, string] {\n const index = text.indexOf(separator);\n return index === -1 ? [text, ''] : [text.slice(0, index), text.slice(index + separator.length)];\n}\n","import type { Observable } from 'rxjs';\n\nimport { computed, type ComputedCell } from '../computed';\nimport { internalState, type InternalState } from '../InternalState';\nimport type { RouteDefinition, RouteTarget } from './RouteDefinition';\nimport { RouteState } from './RouteState';\nimport {\n buildPath,\n formatUrl,\n matchPattern,\n parseUrl,\n pathSegments,\n type HasNoParams,\n type RouteParams\n} from './RoutePath';\n\n/** What the router resolved a url to. */\nexport interface RouteMatch {\n /** The deepest route that matched: the one whose screen is the leaf. */\n readonly route: RouteDefinition;\n /** That route and its ancestors, outermost first — the outlets to render. */\n readonly chain: readonly RouteDefinition[];\n /** Every param captured along the chain, since paths are full. */\n readonly params: Readonly<Record<string, string>>;\n readonly query: Readonly<Record<string, string>>;\n readonly path: string;\n /** Path and query together: what the history stores. */\n readonly url: string;\n}\n\n/**\n * The half of navigation only the thread with an address bar can do.\n *\n * The runtime installs one; without it the router still works, it\n * simply keeps its history in memory. That is not a degraded mode — it\n * is exactly what a desktop window wants, and what a test wants.\n */\nexport interface RouterHistorySink {\n push(url: string): void;\n replace(url: string): void;\n back(): void;\n forward(): void;\n}\n\nexport interface RouterRoutes {\n readonly routes: readonly RouteDefinition[];\n /**\n * Shown when no route matches. Without one an unmatched url leaves\n * `match` null and the outlet renders nothing, which is a blank\n * screen — fine for a test, wrong for an app.\n */\n readonly notFound?: RouteDefinition;\n}\n\nexport interface NavigateOptions {\n readonly query?: Readonly<Record<string, string>>;\n /** Replace the current history entry instead of pushing a new one. */\n readonly replace?: boolean;\n}\n\n/**\n * What `go` takes after the route: its params, or nothing at all when\n * the path declares none.\n *\n * A rest tuple rather than an optional argument, because an optional\n * `RouteParams<Path>` for a path with no params is `{}`, and `{}`\n * accepts any object — so `go(Home, { id: '1' })` would compile and do\n * nothing. Making the argument absent is what makes it an error.\n */\nexport type GoArgs<Path extends string> =\n HasNoParams<Path> extends true ? [options?: NavigateOptions] : [params: RouteParams<Path>, options?: NavigateOptions];\n\n/** How many redirects a single navigation may take before it is a bug. */\nconst MAX_REDIRECTS = 10;\n\n/**\n * How a route's question and a shared slot's answer are compared, for\n * `answerFor`.\n *\n * Both sides return a string because both sides are naming the same\n * thing in the application's own words, and a string is the only shape\n * the router can compare without knowing what the thing is. Whatever\n * normalising the comparison needs (a case, a trailing slash) is done\n * in these two functions, where the application knows which it wants.\n */\nexport interface RouteAnswer<Path extends string, T> {\n /** What this route's params are asking for. */\n readonly asks: (params: RouteParams<Path>) => string;\n /** What a value from the slot is an answer about. */\n readonly answers: (value: NonNullable<T>) => string;\n}\n\n/**\n * Routing, as a service components inject.\n *\n * Every runtime registers one, and it is inert until routes are given\n * to it — through `renderRoot(App).useRoutes(...)`, the single-thread\n * builder's `useRoutes`, or `GessoRuntimeOptions.routes`.\n *\n * It is a service and not a channel, which is the same test everything\n * else on this thread passes: a match holds `RouteDefinition` objects,\n * which hold component classes, and a component class cannot cross a\n * worker boundary. What *can* cross — a url — is what the shell\n * exchanges with it, and that is the entire wire surface of routing.\n *\n * Named `RouterService` rather than `RouterStore`\n * because the store/service split of the thread model renamed all six\n * of its siblings; a `Store` here would be the only one left.\n */\nexport class RouterService {\n /** The current url, path and query. */\n readonly url = internalState('/');\n /** What that url resolved to, or null when nothing matched. */\n readonly match = internalState<RouteMatch | null>(null);\n /**\n * What each route remembers between the times its screen exists.\n *\n * Reached through `remember` and `forget`; exposed because a test and\n * a devtools panel both want to look at it whole.\n */\n readonly state = new RouteState();\n\n private routes: readonly RouteDefinition[] = [];\n private notFound: RouteDefinition | undefined;\n private history: RouterHistorySink | null = null;\n\n /** Installed by the runtime; without one, history is in memory. */\n setHistory(history: RouterHistorySink | null): void {\n this.history = history;\n }\n\n /**\n * Declares what routes exist. Re-resolves the current url, so\n * registering routes after a url has arrived is not a race.\n */\n setRoutes(routes: RouterRoutes): void {\n this.routes = routes.routes;\n this.notFound = routes.notFound;\n this.resolveInto(this.url.value, { push: false });\n }\n\n /** The routes this service is currently matching against. */\n get declaredRoutes(): readonly RouteDefinition[] {\n return this.routes;\n }\n\n /**\n * Navigates to a route, with the params its path declares.\n *\n * Typed from the route: `go(MailItem, { id: '2' })` will not compile\n * with the wrong param name, the wrong type, or none at all. A route\n * whose path has no params takes no second argument.\n */\n go<Path extends string>(route: RouteDefinition<Path>, ...args: GoArgs<Path>): void {\n const [params, options] = splitGoArgs(args);\n const path = buildPath(route.path, params);\n this.navigate(formatUrl(path, options.query), { replace: options.replace ?? false });\n }\n\n /** Navigates to a url, as a link would. */\n navigate(url: string, options: { replace?: boolean } = {}): void {\n this.resolveInto(url, { push: true, replace: options.replace ?? false });\n }\n\n /** Goes back through the shell's history; a no-op without a shell. */\n back(): void {\n this.history?.back();\n }\n\n /** Goes forward through the shell's history; a no-op without a shell. */\n forward(): void {\n this.history?.forward();\n }\n\n /**\n * The url the shell says the window is at: the first one at start-up,\n * and every one the back and forward buttons produce afterwards.\n *\n * Guards run on these too — a url typed into the address bar is\n * exactly the navigation a guard exists for — and a guard that\n * redirects replaces the entry rather than pushing, so Back does not\n * land on the url that was just refused.\n */\n applyUrl(url: string): void {\n this.resolveInto(url, { push: false });\n }\n\n /**\n * The params of the current match, typed from the route asked for,\n * or null when that route is not the one showing.\n *\n * A screen reads its own params with this rather than through a prop\n * so that it keeps them across a navigation that changes only the\n * params — the same instance stays mounted, and its props would have\n * had to be rebuilt to tell it.\n */\n params<Path extends string>(route: RouteDefinition<Path>): RouteParams<Path> | null {\n const match = this.match.value;\n if (match === null || !inChain(match.chain, route)) {\n return null;\n }\n return match.params as RouteParams<Path>;\n }\n\n /** The same, as a cell, for binding a screen's title or fields. */\n observeParams<Path extends string>(route: RouteDefinition<Path>): ComputedCell<RouteParams<Path> | null> {\n return computed(() => this.params(route), { equal: sameParams, label: 'RouterService.params' });\n }\n\n /**\n * A cell a route keeps while its screen does not exist: a scroll\n * offset, a keyboard cursor, the text in a filter field.\n *\n * A screen is built when its route matches and destroyed when it\n * stops matching, so a list rebuilt after Back starts at the top\n * unless somebody remembered where it was. This is where that is\n * kept, and it is why Back returns to the row a person left rather\n * than to the top of the list.\n *\n * const scroll = router.remember(Home, 'scroll', 0);\n * <scrollview scrollY={scroll} modifiers={[scrollPosition({ … })]}>\n *\n * `initial` is used the first time the key is asked for and ignored\n * afterwards. The cell is scoped to the route, so two screens may\n * both call their offset `scroll`.\n *\n * A `null` route is the router's own scope, for the handful of values\n * that belong to the *navigation* rather than to one screen: which\n * element the next transition should morph from is the case both\n * applications have, since the screen that was pressed is destroyed\n * before the screen that arrives is built.\n *\n * This is a facility, not an architecture. The thread model leaves an\n * application's data to the application, and that has not changed:\n * what belongs here is the screen-shaped remainder that exists only\n * to put a screen back where it was. Anything that must survive a\n * reload, or that another part of the application acts on, is still\n * state on a channel.\n */\n remember<T>(route: RouteDefinition | null, key: string, initial: T): InternalState<T> {\n return this.state.cell(route, key, initial);\n }\n\n /** Drops what a route remembered, so its next screen starts fresh. */\n forget(route: RouteDefinition | null): void {\n this.state.forget(route);\n }\n\n /**\n * A shared slot's value, but only while it is the answer to *this*\n * route's own parameters.\n *\n * The case it exists for is a page loaded across the barrier. A\n * channel key that holds \"the track page\" holds whichever track was\n * asked for last, and a screen arriving during a transition asks for\n * its own and is handed the previous one until the answer lands. That\n * is one or two frames of the wrong cover, and it is worse than it\n * looks: the artwork element mounts carrying the previous track's\n * shared name, claims it, and never claims its own, so a second trip\n * between two pages does not animate at all.\n *\n * const track = router.answerFor(Track, page.view.track, {\n * asks: params => `/${params.handle}/${params.slug}`.toLowerCase(),\n * answers: entry => entry.path.toLowerCase()\n * });\n *\n * Two things about it are the router's to know rather than the\n * screen's. It follows the *current* params, so a navigation from one\n * track to another, which keeps the same screen mounted because the\n * chain did not change, asks the new question rather than staying on\n * the one the body read once. And when the route stops matching the\n * cell **keeps what it last held** instead of emptying: a screen is\n * still on screen while it leaves, and a departing page whose artwork\n * blanks for the last frames of its own fade is the flicker this is\n * meant to remove, not a new one to add.\n */\n answerFor<Path extends string, T>(\n route: RouteDefinition<Path>,\n source: Observable<T>,\n keys: RouteAnswer<Path, T>\n ): ComputedCell<T | null> {\n let held: T | null = null;\n return computed(\n read => {\n const params = this.params(route);\n if (params === null) {\n // Not showing: either this screen is on its way out, in which\n // case it keeps its own page, or it was never in.\n return held;\n }\n const value = read(source);\n held =\n value === null || value === undefined\n ? null\n : keys.answers(value as NonNullable<T>) === keys.asks(params)\n ? value\n : null;\n return held;\n },\n { label: `RouterService.answerFor(${route.path})` }\n );\n }\n\n /**\n * Whether a route is in the current chain — true for a layout while\n * any of its children shows, which is what a nav item highlights on.\n */\n isActive(route: RouteDefinition): ComputedCell<boolean> {\n return computed(() => {\n const match = this.match.value;\n return match !== null && inChain(match.chain, route);\n });\n }\n\n /**\n * Resolves a url, runs the guards along the way, and publishes the\n * result.\n *\n * One method for both directions of travel, because a guard runs the\n * same either way — but what a redirect does to the history depends\n * on which direction it came from, and that is the whole of the\n * `replace` bookkeeping below.\n */\n private resolveInto(url: string, options: { push: boolean; replace?: boolean }): void {\n const push = options.push;\n let replace = options.replace ?? false;\n let target = url;\n for (let redirects = 0; redirects <= MAX_REDIRECTS; redirects++) {\n const match = this.resolve(target);\n const verdict = match === null ? true : this.runGuards(match);\n if (verdict === false) {\n // Refused with nowhere else to go: the current url stands. When\n // the refusal was of a url the shell already committed to, the\n // shell is put back where the app actually is.\n if (!push) {\n this.history?.replace(this.url.value);\n }\n return;\n }\n if (verdict === true) {\n this.publish(target, match, { push, replace });\n return;\n }\n target = urlOf(verdict);\n // Where a redirect leaves the history depends on whether the\n // refused url was ever an entry.\n //\n // From inside the app it was not: nothing is written until a\n // navigation settles, so the redirect pushes, and Back returns to\n // the screen the person left. Replacing here was a bug — it\n // overwrote the entry they were standing on, and Back walked out\n // of the app entirely.\n //\n // From the shell it was: the address bar already committed to the\n // refused url before the guard ever saw it, so the redirect\n // replaces, and Back does not land back on a url that will only\n // be refused again.\n replace = push ? replace : true;\n }\n throw new Error(`Navigating to '${url}' redirected more than ${MAX_REDIRECTS} times.`);\n }\n\n /**\n * Writes the result to the cells, and tells the shell what to do\n * with the address bar.\n *\n * A navigation that came from the app pushes; a redirect replaces,\n * so the refused url is not a Back target; and a url the shell\n * itself reported writes nothing back unless a guard changed it, in\n * which case the address bar would otherwise be lying.\n */\n private publish(url: string, match: RouteMatch | null, options: { push: boolean; replace?: boolean }): void {\n const settled = match?.url ?? normalize(url);\n if (options.replace === true) {\n this.history?.replace(settled);\n } else if (options.push) {\n this.history?.push(settled);\n }\n 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\n/**\n * Whether a route is in a matched chain: the same object, or a route\n * declared with the same path.\n *\n * Identity alone was the original test, and it is right for a table\n * loaded once. It fails silently under a dev server's hot replacement:\n * a route table that imports its screens, with screens that import the\n * table to read their params, is an import cycle, and after a hot edit\n * Vite can serve the two sides as `routes.tsx` and `routes.tsx?t=…`.\n * The table then loads twice, the router holds one copy's route objects\n * and the screens hold the other's, and every `params(route)` answers\n * null, so a team page quietly showed the default team. Matching is by\n * path already, so two routes with one path are one route to the url.\n */\nfunction inChain(chain: readonly RouteDefinition[], route: RouteDefinition): boolean {\n return chain.some(link => link === route || link.path === route.path);\n}\n","import type { UiNode, UiFindController } from 'gesso-core';\nimport { internalState } from '../InternalState';\n\n/**\n * The find session, as a store components can inject.\n *\n * `UiFindController` does the searching, but it lives in the runtime\n * and a component cannot reach it — components reach the world through\n * stores, as they do for the clipboard (`ShellService`) and overlays.\n * This is the reactive face of it: cells a find bar binds to, actions\n * it dispatches.\n *\n * The bar itself is the app's. The framework knows only that a session\n * is open, which is what makes Ctrl/Cmd+F and Escape mean find; what a\n * find bar looks like is a component, and belongs to F3.\n */\nexport class FindService {\n /** Whether a find session is running; a bar shows itself for this. */\n readonly open = internalState(false);\n /** The query the matches are for. */\n readonly query = internalState('');\n readonly matchCount = internalState(0);\n /** Which match is active, 1-based for display, or 0 when there is none. */\n readonly activeMatch = internalState(0);\n\n private controller: UiFindController | null = null;\n private detach: (() => void) | null = null;\n /**\n * The query field, kept here as well as on the controller: the app's\n * tree is built — and its refs fire — before the runtime installs the\n * controller, so the node has to wait for one.\n */\n private field: UiNode | null = null;\n\n /** Installed by the runtime; without one every action is a no-op. */\n setController(controller: UiFindController | null): void {\n this.detach?.();\n this.detach = null;\n this.controller = controller;\n if (controller === null) {\n return;\n }\n this.detach = controller.onChange(() => this.sync(controller));\n controller.setField(this.field);\n this.sync(controller);\n }\n\n /**\n * Registers the bar's query field, so opening a session puts the\n * caret in it. Pass it as the field's `ref`; null when it unmounts.\n */\n setField(node: UiNode | null): void {\n this.field = node;\n this.controller?.setField(node);\n }\n\n /** Starts a session. The app shows its bar; the field takes the caret. */\n openFind(): void {\n this.controller?.open();\n }\n\n /** Ends the session and drops the highlights. */\n close(): void {\n this.controller?.close();\n }\n\n /** Searches for `query`, activating its first match. */\n search(query: string, matchCase = false): void {\n this.controller?.search(query, { matchCase });\n }\n\n /** Moves to the next match, wrapping around. */\n next(): void {\n this.controller?.next();\n }\n\n /** Moves to the previous match, wrapping around. */\n previous(): void {\n this.controller?.previous();\n }\n\n /**\n * Re-runs the current query because the content changed under it.\n * An app that edits text while a find is open calls this.\n */\n refresh(): void {\n this.controller?.refresh();\n }\n\n private sync(controller: UiFindController): void {\n this.open.value = controller.isOpen;\n this.query.value = controller.query;\n this.matchCount.value = controller.matchCount;\n this.activeMatch.value = controller.activeIndex + 1;\n }\n}\n","import type { CaretRect, UiEditingController, UiNode, UiTextPosition } from 'gesso-core';\n\n/**\n * The part of a text field an application has to ask about.\n *\n * Almost everything about an editable is reachable already: its text\n * is a property, its model is on the node, and its changes arrive as\n * events. Its *geometry* is not, and cannot be — where the caret sits\n * depends on the paragraph as it was laid out, which needs the\n * measurer, the resolved paint and the layout record. An application\n * that re-measured the text to work it out would be a second measurer\n * that must never disagree with the engine's, which is the kind of\n * pair that agrees until the day it does not.\n *\n * So it asks. This is the asking, and it is a service rather than a\n * free function because the controller lives in the input stack and\n * is out of a component's reach without something in front of it —\n * the same reason `FocusService` exists.\n *\n * What it is for: putting something *at* the caret. An autocomplete\n * list under the word being typed, a hint about the argument being\n * filled in, an error shown against the character it is about.\n */\nexport class EditingService {\n private controller: UiEditingController | null = null;\n\n /** Installed by the runtime; without one there is nothing to ask. */\n setController(controller: UiEditingController | null): void {\n this.controller = controller;\n }\n\n /**\n * Where the caret is inside a field, in the field's own coordinates.\n *\n * Relative to the node's border-box origin, with the field's own\n * scroll already applied: a popup placed under the caret adds the\n * node's position on screen and nothing else.\n *\n * Null when the node is not an editable, when it has not been laid\n * out yet, or before the runtime has wired the controller — all\n * three of which are \"ask again next frame\" rather than errors, and\n * all three of which happen during the frame a field first appears.\n */\n caretRectOf(node: UiNode | null): CaretRect | null {\n if (node === null || this.controller === null) {\n return null;\n }\n return this.controller.caretRectOf(node);\n }\n\n /**\n * Selects from `anchor` to `focus` and focuses the field the focus is\n * in: within one field, or across the fields of an editing group (see\n * `UiEditingGroup`). What an editor does after a command over a\n * selection (making it bold, say) to leave it selected, since its\n * fields can only select their own text.\n *\n * False when the two ends aren't editables of one group, or before\n * the runtime has wired the controller.\n */\n select(anchor: UiTextPosition, focus: UiTextPosition): boolean {\n return this.controller?.select(anchor, focus) ?? false;\n }\n}\n","import type { UiNode } from 'gesso-core';\n\n/**\n * Scrolling a node into view, for a component that moves a highlight\n * rather than focus.\n *\n * Focus moved from the keyboard already brings its node into view. A\n * highlight doesn't move focus: a combobox's arrows walk its list while\n * the caret stays in the field, and a grid's cursor can do the same.\n * The list has to follow the highlight all the same, and only the\n * runtime knows where the scroll containers above a node are and how\n * far each has to move. This is the asking.\n */\nexport class ScrollService {\n private scroller: ((node: UiNode, padding: number) => void) | null = null;\n\n /** Installed by the runtime; without one nothing scrolls. */\n setScroller(scroller: ((node: UiNode, padding: number) => void) | null): void {\n this.scroller = scroller;\n }\n\n /**\n * Scrolls every container above `node` just enough that it's inside\n * the container's viewport, `padding` pixels from the nearest edge.\n * Nothing moves when it's visible already. Before the node has been\n * laid out there is nowhere to scroll it to, and nothing happens.\n */\n scrollIntoView(node: UiNode, padding = 8): void {\n this.scroller?.(node, padding);\n }\n}\n","import type { FocusOptions, UiNode, UiFocusManager } from 'gesso-core';\nimport { internalState } from '../InternalState';\n\n/**\n * Keyboard focus, as a store components can inject.\n *\n * `UiFocusManager` lives in the runtime and a component cannot reach\n * it — components reach the world through stores, as they do for the\n * clipboard (`ShellService`), overlays (`OverlayService`) and find\n * (`FindService`). Without this a component cannot autofocus a field,\n * trap the keyboard in a dialog, or put the caret in the input that\n * failed validation.\n *\n * Nodes come from a `ref` prop. A tree's refs fire before the runtime\n * installs the manager, so an action taken during the first build is\n * queued and replayed once there is one; that is the same problem\n * `FindService` solves for its query field, and the reason `autoFocus`\n * in a dialog works on the frame it mounts.\n *\n * It must stay on the render thread: its actions take `UiNode`s, which\n * never cross a worker boundary.\n */\nexport class FocusService {\n /** The node holding focus, or null. A control binds its focus ring to this. */\n readonly focused = internalState<UiNode | null>(null);\n /** Whether focus is confined to a subtree by an open trap. */\n readonly trapped = internalState(false);\n /**\n * Whether the focus held is focus the keyboard can see: reached by Tab\n * or an arrow key, or by code with no pointer since, rather than by a\n * press. CSS's `:focus-visible`, for something other than a ring that\n * should answer the keyboard and not the click that happened to focus\n * the same control: a tooltip, say.\n */\n readonly focusVisible = internalState(false);\n\n private manager: UiFocusManager | null = null;\n private detach: (() => void) | null = null;\n /** Actions taken before the runtime installed a manager, in order. */\n private queued: ((manager: UiFocusManager) => void)[] = [];\n\n /** Installed by the runtime; without one every action is queued. */\n setManager(manager: UiFocusManager | null): void {\n this.detach?.();\n this.detach = null;\n this.manager = manager;\n if (manager === null) {\n this.queued = [];\n return;\n }\n // A change of modality alone — a key pressed on a control a click\n // focused — is reported as a focus change on the same node, so this\n // one listener keeps both cells right.\n const focusChange = manager.onFocusChange(node => {\n this.focused.value = node;\n this.focusVisible.value = manager.focusVisible;\n });\n const scopeChange = manager.onScopeChange(() => {\n this.trapped.value = manager.trapped;\n });\n this.detach = () => {\n focusChange();\n scopeChange();\n };\n const pending = this.queued;\n this.queued = [];\n for (const action of pending) {\n action(manager);\n }\n this.sync(manager);\n }\n\n /**\n * Gives the node keyboard focus. Non-focusable nodes are ignored.\n *\n * The node is scrolled into view, unless `options.preventScroll` asks\n * for the page to stay where it is, as `element.focus({ preventScroll:\n * true })` does in a browser.\n */\n focus(node: UiNode, options?: FocusOptions): void {\n this.run(manager => manager.focus(node, 'program', options));\n }\n\n /** Drops focus without moving it anywhere. */\n blur(): void {\n this.run(manager => manager.blur());\n }\n\n /** Moves focus to the next focusable node, wrapping around. */\n focusNext(): void {\n this.run(manager => manager.focusNext());\n }\n\n /** Moves focus to the previous focusable node, wrapping around. */\n focusPrevious(): void {\n this.run(manager => manager.focusPrevious());\n }\n\n /**\n * 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.focusVisible.value = manager.focusVisible;\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 type { Size, TextMeasureRequest, TextMeasurer } from 'gesso-core';\n\n/**\n * How big a string would be, if it were drawn.\n *\n * Layout measures text constantly and an application could not ask —\n * the measurer is the runtime's, built for the layout engine, and\n * nothing handed it out. So anything that needs a width before there\n * is a node to read it from had no answer: a column autofitted to its\n * contents, a popover sized to the longest option, an axis label\n * decided before a chart is drawn, a truncation chosen rather than\n * discovered.\n *\n * The alternative every application reaches for is mounting the text\n * invisibly and reading its box back a frame later, which is a frame\n * of latency, a node in the tree and a race with the layout it is\n * trying to inform.\n *\n * **It is the runtime's own measurer**, not a second one. A width\n * from here is the width the layout engine would use, including the\n * cache it has already warmed and the invalidation it gets when a\n * declared face finishes loading — a service with its own measurer\n * would answer with the fallback font long after the real one had\n * arrived.\n */\nexport class TextService {\n private measurer: TextMeasurer | null = null;\n\n /**\n * Called by the runtime with the measurer it lays out through.\n *\n * Not for applications, on the terms `ShellService.applyColorScheme`\n * sets: the thread that draws is the only thing that knows how, and\n * a measurer an application could also install would be one the\n * next layout disagrees with.\n */\n setMeasurer(measurer: TextMeasurer | null): void {\n this.measurer = measurer;\n }\n\n /** Whether there is a measurer yet. False before the first frame. */\n get ready(): boolean {\n return this.measurer !== null;\n }\n\n /**\n * The size a string would take.\n *\n * Zero when there is no measurer, which is what a caller asking\n * before the runtime has drawn anything gets — and the honest\n * answer, since the font it would be measured in is not resolved\n * yet.\n */\n measure(request: TextMeasureRequest): Size {\n return this.measurer?.measure(request) ?? { width: 0, height: 0 };\n }\n\n /**\n * The width of one line, which is what sizing to content asks for.\n *\n * No `maxWidth`, so nothing wraps: the question is how wide the\n * text *wants* to be, and a wrapped answer would be the width it\n * was given rather than the width it needs.\n */\n widthOf(text: string, style: Omit<TextMeasureRequest, 'text' | 'maxWidth'>): number {\n return this.measure({ ...style, text, wrap: 'none' }).width;\n }\n}\n","import { EMPTY, type Observable } from 'rxjs';\n\nimport {\n AnimationDriver,\n UiSpring,\n createTween,\n 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 scrollRange,\n type LayoutRecord,\n UiVirtualWindow,\n UiVirtualSheet,\n VIRTUAL_INDEX_PROP,\n VIRTUAL_LEAD_PROP,\n VIRTUAL_SHEET_PROP,\n VIRTUAL_WINDOW_PROP,\n type VirtualItemMeasure,\n DirtyFlags,\n propertyEffects,\n setPerformanceMarks,\n resolveCursor,\n type UiNode,\n UiNodeType,\n UiInputDispatcher,\n UiPointerEvent,\n UiEventType,\n UiGestureRecognizer,\n UiHitTester,\n UiPointerController,\n UiTouchScroller,\n UiWheelController,\n type ScrollContainerState,\n type ScrollSink,\n type UiScrollability,\n UiFocusManager,\n FocusNotifier,\n EnvironmentNotifier,\n UiEnvironmentKeys,\n UiInsetRegistry,\n insetsEqual,\n noInsets,\n type UiInsets,\n UiKeyboardController,\n UiEditingController,\n type EditingState,\n UiSelectionController,\n UiFindController,\n AnimationDriver,\n UiSharedElements,\n buildSemanticsTree,\n buildSemanticsSubtree,\n rewalkSemantics,\n semanticsInertAbove,\n semanticsMemory,\n recordsEqual,\n TEXT_RUN_ID_SEPARATOR,\n diffSemantics,\n dropIndexShifts,\n LayoutNotifier,\n type UiSemanticsAction,\n type UiFileDropMessage,\n dragSessionFor,\n type UiSemanticsBox,\n type UiSemanticsMap,\n type UiSemanticsPatch,\n type SemanticsRewalk,\n type UiSemanticsRecord,\n type UiSemanticsUpdate,\n type LayoutBox,\n LayoutEngine,\n type LayoutExplanation,\n Constraints,\n Canvas2DRenderer,\n CanvasTextMeasurer,\n type TextMeasurer,\n createCanvasSurface,\n createWebGPUSurface,\n isWebGPUAvailable,\n LayoutInspector,\n WebGPURenderer,\n type CanvasHost,\n type CanvasSurface,\n type RenderChanges,\n type RendererBackend,\n type UiRenderer,\n type WebGPUCanvasHost,\n UiScheduler,\n UiTimerFrameClock,\n type UiFrame,\n type UiFrameClockFactory,\n type UiContrast\n} from 'gesso-core';\nimport { createComponent } from '../createComponent';\nimport { OverlayLayer } from '../overlay/OverlayLayer';\nimport { OverlayService } from '../overlay/OverlayService';\nimport type { ColorScheme } from './colorScheme';\nimport { ShellService, type ShellFileResult, type ShellRequest, type ShellStorageResult } from './ShellService';\nimport { AudioService, type AudioAction, type AudioRequest, type AudioSample } from './AudioService';\nimport { RouterService, type RouterRoutes } from '../router/RouterService';\nimport { FindService } from './FindService';\nimport { EditingService } from './EditingService';\nimport { ScrollService } from './ScrollService';\nimport { FocusService } from './FocusService';\nimport { MediaService, type MediaOptions } from './MediaService';\nimport { FontService, type FontFamilyDeclaration } from './FontService';\nimport { TextService } from './TextService';\nimport { AnimationService } from './AnimationService';\nimport { FrameService } from './FrameService';\nimport { InputLatencyTracker } from './InputLatency';\nimport { SmoothScroller } from './SmoothScroller';\nimport { ChannelRegistry } from '../channel/ChannelRegistry';\nimport { ServiceRegistry } from '../service/ServiceRegistry';\n\n/**\n * The ordered work of one frame.\n *\n * Only five of the seven stages the design document imagined are real\n * phases. Component reconciliation and input dispatch are driven by\n * events, not by the clock: an observable emission reconciles its\n * subtree immediately and a pointer event routes immediately, each\n * marking nodes dirty so the *effects* land in the next frame. Giving\n * them frame slots would add latency and describe the system falsely.\n *\n * `ticks`, `patches` and `environment` run before the dirty set is\n * snapshotted, because all three produce dirt that this frame must\n * see. `layout` and `render` run against the snapshot.\n *\n * `ticks` is first because an animation's writes are inputs to\n * everything after them: a tick that changed a width has to be the\n * width this frame's virtualization measures against and this frame's\n * layout places from, and a tick that ran after `patches` would draw\n * one frame late for the whole life of the animation.\n */\nexport const UI_FRAME_PHASES = [\n 'ticks',\n 'patches',\n 'environment',\n 'virtualize',\n 'layout',\n 'semantics',\n 'render'\n] as const;\n\nexport type UiFramePhase = (typeof UI_FRAME_PHASES)[number];\n\nexport type FramePhaseTimings = Record<UiFramePhase, number>;\n\n/**\n * Guard against a root component that only ever renders another\n * component, which would otherwise recurse until the stack gives out.\n */\nconst MAX_ROOT_COMPONENT_DEPTH = 32;\n\n/**\n * Input controllers over the built tree.\n *\n * Exposed because input arrives differently per thread: from DOM\n * events through a UiPlatformAdapter on the main thread, and from\n * forwarded messages in a render worker. Both end up calling these.\n */\nexport interface RuntimeInput {\n readonly dispatcher: UiInputDispatcher;\n readonly pointer: UiPointerController;\n readonly wheel: UiWheelController;\n readonly keyboard: UiKeyboardController;\n readonly focus: UiFocusManager;\n /** Text editing: the shell's beforeinput, composition and paste land here. */\n readonly editing: UiEditingController;\n /** Selecting and copying text nobody types into. */\n readonly selection: UiSelectionController;\n /** Finding text in the app's own content; the find bar's engine. */\n readonly find: UiFindController;\n /** Dragging a scroll container's contents with a finger. */\n readonly touchScroll: UiTouchScroller;\n}\n\n/**\n * Which backend draws.\n *\n * **`canvas2d` is the default, and that is not the obvious answer.** A\n * GPU backend sounds like the faster one and on a scene of shapes and\n * text it is. On a scene dense with pictures it is currently not, and\n * the reason is one asymmetry rather than anything fundamental:\n * Canvas2D keeps a copy of each still at the size it is drawn (see\n * `ScaledImageCache`), while `WebGPUTextureCache` uploads a still at\n * the source's own size. A 480px cover shown at 164 is therefore about\n * eight times the texture on WebGPU, and a screen holding ninety of\n * them feels it. Measured on Segue's home screen, where the difference\n * was plain enough to notice without instrumenting anything.\n *\n * So the default is the one that is fast everywhere today, and the\n * faster ceiling is opt-in until the gap is closed. When\n * `WebGPUTextureCache` learns the drawn size the way its video path\n * already has, this should flip back.\n *\n * `auto` picks WebGPU where the browser has it and Canvas2D everywhere\n * else. The choice is made synchronously on whether `navigator.gpu`\n * exists, so an engine that never shipped WebGPU (WKWebView, WebKitGTK)\n * is on Canvas2D from the first frame rather than after a rejected\n * adapter request. A browser that has the entry point but cannot\n * produce an adapter or a device still falls back, asynchronously, once\n * that request fails.\n *\n * `canvas2d` pins the portable backend and never asks for an adapter.\n * `webgpu` asks for it and falls back the same way `auto` does, but\n * reports the fallback to the console, because a caller that named the\n * backend wants to know it did not get it.\n */\nexport type RendererChoice = RendererBackend | 'auto';\n\nexport interface GessoRuntimeOptions {\n /** Root component or element. */\n root: FrameworkChild;\n /** Canvas to draw into: HTMLCanvasElement, OffscreenCanvas, or a test double. */\n canvas: CanvasHost;\n /** The rendering backend. Defaults to `canvas2d`; see RendererChoice. */\n renderer?: RendererChoice;\n /**\n * A canvas for text measurement when the draw canvas is WebGPU's — a\n * canvas holds one context, so the measurer needs its own. Defaults\n * to a 1×1 OffscreenCanvas; tests pass a double.\n */\n measureCanvas?: CanvasHost;\n /**\n * The measurer layout and the renderers share, when the caller wants\n * one that does not come from a canvas.\n *\n * Only a test supplies it. A canvas measurer is the right answer\n * everywhere a canvas is real, but a test double's `measureText`\n * answers the same width for every font size, so text laid out\n * against one is not text: a heading and its caption come out the\n * same height. `gesso-testing` passes `CharacterCountTextMeasurer`\n * instead, which is proportional to the font size and identical on\n * every machine.\n */\n textMeasurer?: TextMeasurer;\n /**\n * The image resolver, icon rasteriser and video decoder the\n * `MediaService` should use.\n *\n * Supplied here rather than through the store afterwards because the\n * tree is built inside this constructor, and an `Image` in it asks\n * for its bitmap at that moment: a resolver installed after the\n * runtime exists would already have missed the first screen. An app\n * that fetches through its own stack, or one that has measured a\n * reason to decode in a worker of its own, passes it here.\n *\n * An application reaches this through `createSyncApp(Root).useMedia()`\n * on the single thread and `renderRoot(Root).useMedia()` in a render\n * worker. It is declared in the worker rather than in the shell\n * because a resolver is a function and no function crosses a\n * `postMessage`.\n */\n media?: MediaOptions;\n /**\n * The font families this runtime's text may name, with their faces\n * and fallback stacks. Each face is loaded into this thread's font\n * set, and the tree is measured again as it arrives; until then text\n * draws in the fallback. See `FontService`.\n */\n fonts?: readonly FontFamilyDeclaration[];\n /**\n * The runtime services this runtime's components may reach.\n *\n * Supplied only by a test wanting to substitute one; a runtime\n * registers the six it owns itself, because they are part of what a\n * runtime *is* rather than something an application configures.\n */\n services?: ServiceRegistry;\n /**\n * The routes a `RouterOutlet` in this tree resolves against.\n *\n * Optional, like every other application-shaped thing here: a\n * runtime with no routes still registers a `RouterService`, and it\n * simply matches nothing. Given here rather than set afterwards for\n * the same reason `media` is — the tree is built inside this\n * constructor, and an outlet in it asks for the current match at\n * that moment.\n */\n routes?: RouterRoutes;\n /**\n * The channels this runtime's components may reach.\n *\n * Attached elsewhere and handed in, because where a channel's data\n * lives is the application's decision, not the runtime's.\n */\n channels?: ChannelRegistry;\n /** Defaults to a timer clock, which is the only option inside a worker. */\n clock?: UiFrameClockFactory;\n /** Initial logical size. Callers normally follow with resize(). */\n width?: number;\n height?: number;\n dpr?: number;\n}\n\n/**\n * The whole UI, with no reference to the DOM.\n *\n * Owns the component runtime, retained graph, layout engine, input\n * controllers, scheduler and renderer. Everything here runs happily\n * in a Worker: the only things it cannot do for itself are obtain a\n * canvas and learn about size and input, which is exactly the split\n * between this class and its two hosts — GessoApp on the main thread\n * and renderRoot() in a render worker.\n */\n/** How many shell requests are held for a listener that has not attached, before the rest are dropped. */\nconst HELD_SHELL_REQUESTS = 256;\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 private frameErrorListener: ((message: string, stack?: string) => void) | null = null;\n /** WebGPU stage timings of the frame being rendered; null on Canvas2D. */\n private gpuTimings: GpuStageTimings | null = null;\n private inspectListener: ((report: UiNodeReport | null) => void) | null = null;\n /** The last report's explanation text, which is what tells two reports apart. */\n private lastInspection: string | null = null;\n private devtoolsListener: ((event: DevtoolsEvent) => void) | null = null;\n /** Whether a panel wants a tree snapshot after every frame that changed the tree. */\n private watchingTree = false;\n /**\n * Live subscriptions as of the last snapshot sent.\n *\n * A leak that adds no nodes changes nothing `frameChangedTree` looks\n * at, so a panel watching for one would never be told. This is the\n * second reason to resend, and it costs a count only while a panel\n * is attached.\n */\n private lastSubscriptions = -1;\n /** Whether a panel wants every frame's metrics. */\n private watchingFrames = false;\n /** The node a panel has selected, whose report is kept fresh; null for none. */\n private selectedId: string | null = null;\n /** The selected node's last report, serialised, which is what tells two apart. */\n private lastSelectedReport: string | null = null;\n private cursorListener: ((cursor: string | null) => void) | null = null;\n private lastCursor: string | null = null;\n private scrollabilityListener: ((scrollability: UiScrollability, scrollsAnything: boolean) => void) | null = null;\n private lastScrollability: UiScrollability = { up: false, down: false, left: false, right: false };\n /**\n * Undefined until the first report, so that one is always sent.\n *\n * The values it takes are ordinary booleans; the third state exists\n * only to make \"nothing has been said yet\" different from \"nothing\n * scrolls\", which matters because those two need opposite\n * `touch-action` on the shell's canvas and an app with no scroll\n * container at all would otherwise never send either.\n */\n private lastScrollsAnything: boolean | undefined = undefined;\n private editingListener: ((state: EditingState | null) => void) | null = null;\n /**\n * The selection controller, reachable before `input` is assigned:\n * the graph's node-removed listener is installed in the constructor\n * and fires for nodes taken out from under a live selection.\n */\n private selectionController: UiSelectionController | null = null;\n private findController: UiFindController | null = null;\n /** Reachable before `input` is assigned, for the same reason. */\n private readonly focusManager: UiFocusManager;\n /** The input stack's hit tester, kept for the inspector's questions about what lies where. */\n private hitTester!: UiHitTester;\n private readonly layoutNotifier: LayoutNotifier;\n /** Animates a wheel scroll; see `SmoothScroller`. */\n private readonly smoothScroller: SmoothScroller;\n /**\n * Whether the document showing this runtime is on screen.\n *\n * Assumed true until the shell says otherwise, because a runtime with\n * no shell — a spec, a headless graph — is never told and must draw.\n */\n private visible = true;\n /**\n * The running animations. Built as a field rather than in the body\n * of the constructor because the builder, the services and `buildRoot`\n * all need it, and `buildRoot` is where a component's first\n * `animate()` can happen.\n */\n private readonly animations = new AnimationDriver();\n /**\n * Which node currently answers to each shared-element name.\n *\n * Per runtime for the same reason the driver above is: several\n * runtimes share a worker in the playground, and a shared registry\n * would let one runtime's element morph from another's.\n */\n private readonly sharedElements = new UiSharedElements();\n private readonly focusNotifier = new FocusNotifier();\n private readonly environmentNotifier = new EnvironmentNotifier();\n /**\n * The semantics tree: every record by id, and each record's children\n * in order.\n *\n * A record's own `index` is only kept current for the records that\n * are sent; the order is `semanticsChildren`. Renumbering every\n * record after an insertion cost a pass over the whole tree on every\n * structural change, for indices nothing would read until a record\n * was next sent or the tree was asked for (`semanticsTree`).\n */\n private semantics = new Map<string, UiSemanticsRecord>();\n private semanticsChildren = new Map<string | null, string[]>();\n /** The tree in document order with every index current, built when asked for. */\n private semanticsOrdered: Map<string, UiSemanticsRecord> | null = null;\n private semanticsListener: ((update: UiSemanticsUpdate) => void) | null = null;\n /**\n * The box last reported for each mirrored node, so a frame that\n * moved three rows of a list sends three boxes rather than all of\n * them. Only populated while a listener is attached.\n */\n private semanticsBoxes = new Map<string, LayoutBox>();\n /** What each semantics walk leaves for the next; see `SemanticsMemory`. */\n private readonly semanticsMemory = semanticsMemory();\n private lastFocusedId: string | null = null;\n /**\n * Where the caret was when `reload` replaced the tree, put back on\n * the frame that lays the new one out, and null when nothing had it.\n *\n * Null is restored as well as an id, which is the second half of\n * the hot-replacement gap: a rebuilt subtree runs `autoFocus` again,\n * and a dialog's first field taking the caret away from where the\n * person was is worse than a reload doing nothing at all.\n */\n private focusAfterReload: string | null = null;\n /** Whether `focusAfterReload` is waiting to be applied. */\n private restoringFocus = false;\n /**\n * Reveals asked for while the layout listeners run, kept until the\n * frame's boxes have settled; null outside `settleLayout`, where a\n * reveal is applied at once.\n */\n private deferredReveals: { node: UiNode; padding: number }[] | null = null;\n /** Whether a frame has run out of layout passes and been warned about; see `MAX_LAYOUT_PASSES`. */\n private warnedLayoutPasses = false;\n /** A frame changed semantics while nothing was listening; see `semanticsTree`. */\n private semanticsStale = false;\n private lastEditingState: EditingState | null = null;\n private shellListener: ((request: ShellRequest) => void) | null = null;\n /** Shell requests made before `onShellRequest` gave them somewhere to go; see there. */\n private heldShellRequests: ShellRequest[] = [];\n private audioListener: ((request: AudioRequest) => void) | null = null;\n private caretTimer: ReturnType<typeof setTimeout> | null = null;\n private scrollbarTimer: ReturnType<typeof setTimeout> | null = null;\n 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, options) => {\n this.focusManager.focus(node, 'program', options);\n },\n onFocusChange: (node, listener) => this.focusNotifier.add(node, listener)\n },\n environment: {\n read: (node, key) => (node.environment ?? this.graph.buildNodeEnvironment(node)).get(key),\n onChange: (node, listener) => this.environmentNotifier.add(node, listener)\n },\n layout: {\n // The 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 => {\n if (this.shellListener !== null && this.shellListener !== undefined) {\n this.shellListener(request);\n } else if (this.heldShellRequests.length < HELD_SHELL_REQUESTS) {\n this.heldShellRequests.push(request);\n }\n });\n // And sound, which is the shell's element and this thread's client.\n if (!this.services.has(AudioService)) {\n this.services.register(AudioService);\n }\n this.services.get(AudioService).setHandler(request => this.audioListener?.(request));\n // And the find session, so a component can drive the search the\n // browser's own find bar cannot do over a canvas.\n if (!this.services.has(FindService)) {\n this.services.register(FindService);\n }\n // And focus, which lives in the input stack and is therefore out\n // of a component's reach without a store in front of it.\n if (!this.services.has(FocusService)) {\n this.services.register(FocusService);\n }\n // And the caret's geometry, which is in the input stack for the\n // same reason and just as far out of reach.\n if (!this.services.has(EditingService)) {\n this.services.register(EditingService);\n }\n // And scrolling a node into view, for a highlight that moves\n // without focus.\n if (!this.services.has(ScrollService)) {\n this.services.register(ScrollService);\n }\n // And the frames themselves, for a screen that shows its own\n // frame gap or input latency.\n if (!this.services.has(FrameService)) {\n this.services.register(FrameService);\n }\n // And the image resolver and icon rasteriser, whose caches must be\n // per runtime: two runtimes in one worker must not share a bitmap\n // one of them is about to close.\n if (!this.services.has(MediaService)) {\n this.services.register(MediaService);\n }\n // And the fonts, which load into this thread's font set and re-lay\n // the tree out as they arrive.\n if (!this.services.has(FontService)) {\n this.services.register(FontService);\n }\n this.services.get(FontService).setListener(() => this.fontsChanged());\n // And measurement, which is the same measurer layout uses: a\n // width an application asks for has to be the width the engine\n // would lay the text out at, cache and font-loading and all.\n if (!this.services.has(TextService)) {\n this.services.register(TextService);\n }\n this.services.get(TextService).setMeasurer(this.textMeasurer);\n if (options.fonts !== undefined) {\n this.services.get(FontService).declare(options.fonts);\n }\n // And animation, whose running set must be per runtime for the\n // same reason the media caches are: the playground has several\n // runtimes in one worker, and a shared driver would tick a\n // disposed runtime's cells.\n if (!this.services.has(AnimationService)) {\n this.services.register(AnimationService);\n }\n this.services.get(AnimationService).setDriver(this.animations);\n // 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 scrollRange(record, axis === 'scrollY' ? 'y' : 'x');\n }\n );\n // And the router, whose matches hold route definitions, which hold\n // component classes: it could not cross a worker boundary if it\n // wanted to. The one thing it needs from the shell is the address\n // bar, which it reaches the same way the clipboard does.\n if (!this.services.has(RouterService)) {\n this.services.register(RouterService);\n }\n const router = this.services.get(RouterService);\n 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 * A frame that threw is reported and the clock keeps running.\n *\n * Both halves matter. Letting it escape used to report it — the\n * throw unwound into whatever was forwarding the tick — and stop\n * the application dead, because nothing re-armed the clock. An\n * application that merely stops looks exactly like a hang: the\n * last frame stays on screen and every click lands in a surface\n * nobody is listening to.\n */\n onFrameError: error => this.reportFrameError(error)\n });\n\n // An animation started between frames — from a click handler that\n // changes nothing else — has to arm the frame that will run its\n // first tick. Nothing in the graph is dirty at that moment, so\n // nothing else would.\n this.animations.setWakeListener(() => this.scheduler.wake());\n this.graph.setDirtyListener(() => this.scheduler.notifyDirty());\n this.graph.setNodeRemovedListener(node => {\n this.engine.detachNode(node);\n // Neither a selection nor a set of find matches can outlive its\n // nodes: virtualization and route changes both take them out from\n // under one.\n this.selectionController?.handleNodeRemoved(node);\n this.findController?.handleNodeRemoved(node);\n // Nor can focus: a closed dialog or a recycled row takes the\n // focused node with it.\n this.focusManager.handleNodeRemoved(node);\n this.layoutNotifier.handleNodeRemoved(node);\n this.smoothScroller.handleNodeRemoved(node);\n this.focusNotifier.handleNodeRemoved(node);\n this.environmentNotifier.handleNodeRemoved(node);\n });\n\n this.buildRoot(options.root);\n this.input = this.createInput();\n this.services.get(FocusService).setManager(this.input.focus);\n this.services.get(EditingService).setController(this.input.editing);\n this.services.get(ScrollService).setScroller((node, padding) => this.scrollIntoView(node, padding));\n // Keyboard navigation must keep the focused control visible — but\n // only keyboard navigation. A click has already shown the person\n // where they are, and revealing what they just pressed scrolls the\n // page out from under a pointer that is still resting on it: a\n // half-visible 562px card jumped 511px up the screen on the press,\n // before the transition it started had drawn a frame. A browser\n // draws the same line, and for the same reason. Focus from code that\n // asked for `preventScroll` is left where it is, as in a browser.\n this.input.focus.onFocusChange((node, source, options) => {\n if (node !== null && source !== 'pointer' && options.preventScroll !== true) {\n this.scrollIntoView(node);\n }\n });\n\n if (options.width !== undefined && options.height !== undefined) {\n this.resize(options.width, options.height, this.pixelRatio);\n }\n }\n\n /**\n * Aligns patch delivery from worker-owned channels to the frame.\n *\n * Without this a burst of patches rebuilds the bound subtree once per\n * patch, even though only the final state is ever drawn.\n */\n deferPatchesFrom(sources: readonly PatchSource[]): void {\n this.replicas = sources;\n for (const source of sources) {\n source.deferPatches(() => 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 /**\n * Told when a frame throws, with the clock still running.\n *\n * Separate from `onListenerError` because the two are different\n * failures: a listener that throws has broken one interaction, and\n * a frame that throws has broken the thing that draws. It is\n * reported rather than fatal — see the scheduler — so somebody has\n * to be able to hear it.\n */\n onFrameError(listener: ((message: string, stack?: string) => void) | null): void {\n this.frameErrorListener = listener;\n }\n\n private reportFrameError(error: unknown): void {\n const message = error instanceof Error ? error.message : String(error);\n const stack = error instanceof Error ? error.stack : undefined;\n if (this.frameErrorListener !== null) {\n this.frameErrorListener(message, stack);\n return;\n }\n // eslint-disable-next-line no-console\n console.error('A Gesso frame threw. The frame was abandoned and the clock kept running.', error);\n }\n\n onListenerError(listener: ((message: string, stack?: string) => void) | null): void {\n this.dispatcher.onListenerError(\n listener === null\n ? null\n : (error, node, type) => {\n const message = error instanceof Error ? error.message : String(error);\n listener(\n `${message} (listener: ${type} on ${this.pathOf(node)})`,\n error instanceof Error ? error.stack : undefined\n );\n }\n );\n }\n\n private reportRendererError(message: string): void {\n if (this.rendererErrorListener !== null) {\n this.rendererErrorListener(message);\n return;\n }\n // eslint-disable-next-line no-console\n console.error(message);\n }\n\n /**\n * Replaces a WebGPU renderer that could not start, or lost its\n * device, with Canvas2D on the same canvas. The WebGPU path does not\n * touch the canvas until it has a device, so the 2D context is free.\n */\n private fallBackToCanvas2D(failed: UiRenderer): void {\n if (this.renderer !== failed) {\n return;\n }\n failed.dispose();\n this.canvasSurface = createCanvasSurface(this.canvas);\n this.renderer = new Canvas2DRenderer({ surface: this.canvasSurface });\n this.rendererState = 'canvas2d';\n this.renderer.resize(this.width, this.height, this.pixelRatio);\n this.requestRepaint();\n }\n\n 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 // What was asked before anyone was listening, handed over now. The\n // root is built in the constructor, so a component that reads a\n // stored preference as it mounts asks before a shell *can* have\n // attached — and a storage or file request is a promise waiting for\n // its answer, which a dropped request never gets.\n if (listener !== null) {\n const held = this.heldShellRequests;\n this.heldShellRequests = [];\n for (const request of held) {\n listener(request);\n }\n }\n }\n\n /**\n * Receives what components ask of the shell's audio element through\n * `AudioService`. Without a listener they are dropped, and the app\n * plays nothing, which is what a headless runtime should do.\n */\n onAudioRequest(listener: ((request: AudioRequest) => void) | null): void {\n this.audioListener = listener;\n }\n\n /** The shell reports its audio element; passed straight through to `AudioService`. */\n applyAudioSample(sample: AudioSample): void {\n this.services.get(AudioService).applySample(sample);\n }\n\n /** The platform's media controls acted; passed straight through to `AudioService`. */\n applyAudioAction(action: AudioAction): void {\n this.services.get(AudioService).applyAction(action);\n }\n\n /**\n * Where typed text comes from. A shell with an editing proxy delivers\n * it through `input.editing.beforeInput` and the composition methods,\n * so printable key presses must not be inserted a second time; with\n * `keys` (the default) they are all there is.\n */\n setTextInputSource(source: 'proxy' | 'keys'): void {\n this.input.editing.textFromKeys = source === 'keys';\n }\n\n /**\n * The page was hidden or shown. A hidden page stops the caret blink,\n * so a background tab with a focused field schedules no frames.\n *\n * The driver is told as well, and it is the half that matters to\n * what the page looks like: a hidden page goes on painting but runs\n * no animation frames, so anything the driver is holding would be\n * drawn frozen at whatever value it had reached. See\n * `AnimationDriver.setHidden` for why an entrance frozen at its\n * first value is a hole in the page rather than a paused animation.\n */\n /**\n * The shell says the surface entered or left fullscreen.\n *\n * Reported rather than inferred from the request that asked for it,\n * because the person can leave with Escape and a browser can refuse\n * to enter at all; a control that tracked its own last press would\n * then point the wrong way.\n */\n setFullscreen(active: boolean): void {\n this.services.get(ShellService).applyFullscreen(active);\n }\n\n setVisible(visible: boolean): void {\n this.input.editing.setVisible(visible);\n if (visible === this.visible) {\n return;\n }\n this.visible = visible;\n this.animations.setHidden(!visible);\n if (!visible) {\n return;\n }\n if (!this.started) {\n return;\n }\n // One frame on the way back, whether or not anything is dirty: an\n // animation the driver kept running while hidden is still in it,\n // and `scheduleAnimationTick` only re-arms from inside a frame.\n 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 /** The contrast the platform asks for, passed straight to `ShellService`; see `ShellService.contrast`. */\n setContrast(contrast: UiContrast): void {\n this.services.get(ShellService).applyContrast(contrast);\n }\n\n /**\n * The platform's own insets, as the shell reports them: the safe area\n * under a notch or a home indicator and the strip a soft keyboard\n * covers, once at start-up and again whenever they change.\n *\n * Two things happen to them, and both are on this side of the\n * boundary because both are decisions. They reach `ShellService`,\n * where an application can read the raw numbers as `colorScheme` is\n * read. And they are published into the inset registry the app root's\n * environment carries, as one contributor beside the application's\n * own floating bars, so `insetPadding` on a screen keeps clear of the\n * keyboard without the application writing a line. The registry\n * composes by maximum, so a bar drawn across the home indicator and\n * the home indicator under it cost the content one strip, not two;\n * see `UiInsetRegistry` for the reasoning.\n *\n * The registry is the one at the app root rather than one this\n * runtime owns because an application provides its own with the\n * `insets` prop and every screen reads that one. Without a provider\n * the key's default registry is used, which is what every reader\n * under such a root resolves too. A registry provided below the root\n * is not found; the application feeds `ShellService.viewportInsets`\n * into it, which is why that cell exists.\n */\n setViewportInsets(insets: UiInsets): void {\n if (insetsEqual(this.viewportInsetValue, insets)) {\n return;\n }\n this.viewportInsetValue = insets;\n this.services.get(ShellService).applyViewportInsets(insets);\n this.publishViewportInsets();\n }\n\n /**\n * Writes the platform's insets into whichever registry the app root\n * resolves right now, moving the contribution when that changes.\n *\n * Called when the insets change, when the root is built or reloaded,\n * and when the root's environment is rebuilt, because the registry\n * is an environment value and a reload may provide a different one.\n * The old registry gets its room back before the new one is written,\n * exactly as a retracted bar would.\n */\n private publishViewportInsets(): void {\n const appRoot = this.appRoot;\n const source =\n appRoot === undefined\n ? null\n : (appRoot.environment ?? this.graph.buildNodeEnvironment(appRoot)).get(UiEnvironmentKeys.insets);\n const registry = source instanceof UiInsetRegistry ? source : null;\n if (registry !== this.viewportInsetRegistry) {\n this.viewportInsetWrite?.();\n this.viewportInsetWrite = null;\n this.viewportInsetRegistry = registry;\n }\n if (registry === null) {\n // A source that is not a registry is somebody's read-only view of\n // one, and there is nothing to publish into; `publishInset` makes\n // the same call.\n return;\n }\n if (this.viewportInsetWrite === null) {\n this.viewportInsetWrite = registry.publish(this.viewportInsetValue);\n } else {\n this.viewportInsetWrite(this.viewportInsetValue);\n }\n }\n\n /**\n * Reports what became of a popup a component asked for, settling the\n * promise `ShellService.openPopup` returned.\n *\n * The one inbound message that answers an outbound one, so unlike the\n * preference setters beside it this carries the id it is replying to.\n */\n settlePopup(id: number, opened: boolean): void {\n this.services.get(ShellService).settlePopup(id, opened);\n }\n\n /**\n * Reports whether a clipboard write landed, settling the promise\n * `ShellService.copyText` returned.\n */\n settleClipboard(id: number, copied: boolean): void {\n this.services.get(ShellService).settleClipboard(id, copied);\n }\n\n /**\n * Reports what the shell found in `localStorage`, settling the\n * promise `ShellService.requestStorage` returned.\n *\n * The second inbound message that answers an outbound one, and it\n * carries its request's id for the same reason the first does.\n */\n settleStorage(id: number, result: ShellStorageResult): void {\n this.services.get(ShellService).settleStorage(id, result);\n }\n\n /**\n * Reports what the shell did with a file request, settling the\n * promise `ShellService.requestFile` returned.\n */\n settleFile(id: number, result: ShellFileResult): void {\n this.services.get(ShellService).settleFile(id, result);\n }\n\n /** Whether the runtime is currently honouring a reduced-motion preference. */\n get reducedMotion(): boolean {\n return this.animations.isReducedMotion;\n }\n\n /** The appearance the shell last reported; `light` until it says otherwise. */\n get colorScheme(): ColorScheme {\n return this.services.get(ShellService).currentColorScheme;\n }\n\n /** The platform's insets the shell last reported; zeroes until it says otherwise. */\n get viewportInsets(): UiInsets {\n return this.viewportInsetValue;\n }\n\n /** Which shared-element names are currently held, for specs and devtools. */\n get sharedElementNames(): readonly string[] {\n return this.sharedElements.names;\n }\n\n /** `engine.explain` for any node, for tests and devtools. */\n explain(node: UiNode): LayoutExplanation {\n return this.engine.explain(node);\n }\n\n /**\n * Everything the inspector shows about one node, as plain data\n *.\n *\n * Built here rather than in `gesso-devtools` because every source\n * it reads is private to the render thread and most of it cannot\n * cross a thread boundary at all: the graph's bindings, the\n * builder's modifier sets, the resolver's component hosts. The\n * report is strings, so it can.\n */\n inspectNode(node: UiNode): UiNodeReport {\n const box = this.engine.visibleBox(node);\n return {\n id: node.id,\n type: node.type,\n box: { x: box.x, y: box.y, width: box.width, height: box.height },\n owners: this.ownersOf(node),\n props: this.propsOf(node),\n environment: this.environmentOf(node),\n modifiers: this.builder.modifiersFor(node)?.names ?? [],\n listens: this.dispatcher.listenerTypes(node),\n beneath: this.beneathAtPointer(node),\n semantics: this.semanticsOf(node),\n 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 /** Where the node is seen: scroll offsets and sticky shifts applied. */\n debugVisibleBox(node: UiNode): { x: number; y: number; width: number; height: number } {\n return this.engine.visibleBox(node);\n }\n\n /**\n * The node layout, hit testing and painting start from: a stack that\n * stretches the app root over the viewport with the overlay layer on\n * top of it.\n */\n layoutRoot(): UiNode {\n if (this.root === undefined) {\n throw new Error('App root has not been built.');\n }\n return this.root;\n }\n\n /**\n * Scrolls every scroll container above `node` just enough that the\n * node is inside its viewport, `padding` pixels from the nearest edge.\n * Nothing moves when it is already visible.\n *\n * Asked for from a layout listener — `autoFocus` taking focus is the\n * usual one — it waits until the frame's layout has settled, so it\n * reveals where the node ends up rather than where the first pass\n * put it (see `settleLayout`).\n */\n scrollIntoView(node: UiNode, padding = 8): void {\n if (this.deferredReveals !== null) {\n this.deferredReveals.push({ node, padding });\n return;\n }\n for (const adjustment of this.engine.revealAdjustments(node, padding)) {\n // A reveal knows exactly where the container has to be, and a\n // spring still running would overwrite that on its next tick.\n this.smoothScroller.stop(adjustment.container);\n adjustment.container.setProperty('scrollX', adjustment.scrollX);\n adjustment.container.setProperty('scrollY', adjustment.scrollY);\n this.graph.markDirty(adjustment.container, DirtyFlags.Transform);\n }\n }\n\n /**\n * Rebuilds the application's tree from a new root definition,\n * keeping the runtime and everything that is not the tree\n * (the HMR item).\n *\n * **Why a rebuild is the only story.** A Gesso component's `render`\n * runs once; there is no re-render pass to push new code through, so\n * replacing a module cannot be made to update a mounted component in\n * place. What can be done is to throw the tree away and build a new\n * one, and that is only useful if the things worth keeping do not\n * live in the tree. In this framework they do not: application state\n * is in a data worker behind a channel, and a replica's cells are\n * held by the runtime rather than by any component, so a freshly\n * built tree binds to values that are already there.\n *\n * That is why the \"snapshot projections as patches, replay\n * after mount\" is not here. It describes rebuilding a replica from\n * scratch, and nothing rebuilds one: the replica, the services, the\n * renderer, the canvas, the focus manager and the scheduler all\n * survive, because only the tree is replaced.\n *\n * **What is kept and what is lost.** The layout root node is the\n * same object, so input routing, the focus scope stack and every\n * listener registered on the root stay valid. Below it, the builder\n * reconciles rather than recreating, so a component whose class is\n * the same object as before keeps its host and its `internalState`;\n * one whose module was replaced is a different class, and its host\n * is disposed and mounted again. 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.frameErrorListener = null;\n this.scheduler.stop();\n // An animation holds its cell, and a cell holds whatever the\n // component that made it captured. A disposed runtime must not.\n this.animations.stopAll();\n this.sharedElements.clear();\n this.animations.setWakeListener(null);\n this.services.get(AnimationService).setDriver(null);\n this.services.get(AudioService).setAnimations(null);\n this.services.get(FindService).setController(null);\n this.services.get(FocusService).setManager(null);\n this.services.get(RouterService).setHistory(null);\n // Decoded bitmaps hold pixels; garbage collection is not prompt\n // about them, so they are closed rather than dropped.\n this.services.get(MediaService).dispose();\n this.services.get(FontService).dispose();\n this.graph.setEnvironmentChangedListener(null);\n this.graph.setDirtyListener(null);\n this.graph.setNodeRemovedListener(null);\n this.frameListener = null;\n // Its listeners are on the root node, which the dispatcher holds by\n // reference; a disposed runtime must not keep either alive.\n this.input.touchScroll.dispose();\n this.resolver.dispose();\n this.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 // The application's root, not the layout root that wraps it, for\n // the reason the keyboard's below gives: `overscrollBehavior` set\n // on the app's own top element has to be the one that is read.\n // Paced: a trackpad's steps are spread evenly over frames, which a\n // runtime can do because it advances the controller each frame.\n wheel: new UiWheelController(hitTester, this.dispatcher, scrollSink, () => this.appRoot ?? root, undefined, {\n pace: true\n }),\n // Unfocused keys land on the application's root, not the layout\n // root that wraps it: the wrapper is the runtime's, and an app\n // listening for Escape on its own top element would otherwise\n // never hear a key pressed with nothing focused. A getter, because\n // `reload` rebuilds the tree under a controller that lives on.\n keyboard: new UiKeyboardController(this.dispatcher, focus, () => this.appRoot ?? this.layoutRoot(), {\n editing,\n selection,\n find,\n // Enter on a focused button clicks its centre, as an assistive\n // technology's press does.\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.orderedSemantics().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 id of the node that holds focus, or null. The same id the\n * semantics tree keys its records by, so a reader of the tree can\n * say which control a key press would reach.\n */\n focusedNodeId(): string | null {\n return this.focusManager.focusedNode?.id ?? null;\n }\n\n /**\n * The semantics tree as of the last frame that changed it.\n *\n * Rebuilt here when no mirror was listening and a frame marked it\n * stale, so a test or a devtools panel sees the current tree without\n * every frame having paid to keep one nothing was reading.\n */\n semanticsTree(): UiSemanticsMap {\n if (this.semanticsStale) {\n this.storeSemantics(buildSemanticsTree(this.layoutRoot(), this.semanticsMemory));\n this.semanticsStale = false;\n }\n return this.orderedSemantics();\n }\n\n /** Replaces the tree with one built in full, which is in document order. */\n private storeSemantics(tree: Map<string, UiSemanticsRecord>): void {\n this.semantics = tree;\n this.semanticsOrdered = tree;\n this.semanticsChildren = new Map();\n for (const record of tree.values()) {\n let list = this.semanticsChildren.get(record.parent);\n if (list === undefined) {\n this.semanticsChildren.set(record.parent, (list = []));\n }\n list.push(record.id);\n }\n }\n\n /**\n * The tree in document order, every index current: a walk down\n * `semanticsChildren`, done when someone asks rather than per frame.\n * Records whose index had gone out of date are brought up to date in\n * the store as well.\n */\n private orderedSemantics(): Map<string, UiSemanticsRecord> {\n if (this.semanticsOrdered !== null) {\n return this.semanticsOrdered;\n }\n const ordered = new Map<string, UiSemanticsRecord>();\n const stack: [string, number][] = [];\n const push = (parent: string | null): void => {\n const list = this.semanticsChildren.get(parent) ?? [];\n const indices = semanticIndices(list);\n for (let i = list.length - 1; i >= 0; i--) {\n stack.push([list[i]!, indices[i]!]);\n }\n };\n push(null);\n while (stack.length > 0) {\n const [id, index] = stack.pop()!;\n let record = this.semantics.get(id)!;\n if (record.index !== index) {\n record = { ...record, index };\n this.semantics.set(id, record);\n }\n ordered.set(id, record);\n push(id);\n }\n this.semanticsOrdered = ordered;\n return ordered;\n }\n\n /** A record's index among its siblings now, which its own `index` may not say. */\n private semanticsIndexOf(record: UiSemanticsRecord): number {\n const list = this.semanticsChildren.get(record.parent) ?? [];\n const at = list.indexOf(record.id);\n return at < 0 ? record.index : semanticIndices(list)[at]!;\n }\n\n /**\n * Something an assistive technology did to a mirrored element,\n * turned back into ordinary input.\n *\n * Deliberately routed through the same controllers a pointer and a\n * keyboard use rather than into components directly: an AT press on\n * a `Checkbox` has to reach the `onClick` the mouse reaches, or the\n * two paths drift and only one of them is tested. `focus` goes\n * through the focus manager, which means an AT cannot escape an open\n * focus trap any more than Tab can.\n */\n /**\n * Files from outside the application, dragged over it or dropped.\n *\n * Handed to the tree's drag session, which turns them into a drag\n * like any other, so a zone that accepts `EXTERNAL_FILES` answers\n * the drop exactly as it answers one from inside. A drop that\n * arrives before there is a tree has nowhere to land and is dropped.\n */\n applyFileDrop(message: UiFileDropMessage): void {\n if (this.root === undefined) {\n return;\n }\n dragSessionFor(this.root).applyFileDrop(message);\n }\n\n applySemanticsAction(action: UiSemanticsAction): void {\n if (this.applyTextRunAction(action)) {\n return;\n }\n const node = this.graph.getNode(action.id);\n if (node === undefined || !this.semantics.has(action.id)) {\n // A stale id: the mirror acted on a node this frame removed.\n return;\n }\n 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.restructureSemantics(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 previous = this.orderedSemantics();\n const next = buildSemanticsTree(this.layoutRoot(), this.semanticsMemory);\n const patches = dropIndexShifts(previous, next, diffSemantics(previous, next));\n this.storeSemantics(next);\n this.semanticsStale = false;\n return patches;\n }\n\n /**\n * The patches for a frame that changed what some nodes *mean*\n * without changing which nodes there are — or null when that cannot\n * be established cheaply, and the whole tree has to be walked.\n *\n * This is the common case and it used to cost the uncommon one. A\n * bound label is `text`, `text` marks semantics dirty, and the phase\n * answered every one of them by rebuilding the tree from the root\n * and diffing two maps: on the benchmark list, 2 ms on any frame a\n * clock ticked, next to 0.03 ms for the layout of the same change.\n *\n * So the walk is rooted at what actually changed. For each dirty\n * node the nearest ancestor *carrying a record* is found — a\n * `Button`'s label lives on the button, not on the `Text` inside it\n * that a parent claimed — and only that node's subtree is walked.\n * `buildSemanticsSubtree` explains why the owner has to be a record\n * holder for the indices to come out the same.\n *\n * It gives up, and says so with null, whenever the *shape* of the\n * tree could have moved:\n *\n * - a frame that changed the tree's children, which is a structural\n * change by definition;\n * - a dirty node with no record-holding ancestor, whose records sit\n * at the top level and are numbered among everything else there;\n * - an owner that is no longer describable, whose children would\n * reattach further up;\n * - a subtree that gained or lost a record, because `this.semantics`\n * is insertion-ordered and the diff promises adds in document\n * order — splicing one into the middle of a Map cannot preserve\n * that, and a mirror appending a child before its parent is a\n * worse bug than a slow frame.\n *\n * Each of those falls back to the full rebuild, which is correct by\n * construction. Nothing here decides what a record says; it only\n * decides how much of the tree has to be asked.\n */\n private rescopeSemantics(frame: UiFrame): readonly UiSemanticsPatch[] | null {\n if (this.semantics.size === 0 || this.semanticsStale) {\n // No previous tree to scope against, or one already owed in full.\n return null;\n }\n const owners = new Set<UiNode>();\n for (const [node, flags] of frame.entries()) {\n if ((flags & DirtyFlags.Children) !== 0) {\n return null;\n }\n if ((flags & DirtyFlags.Semantics) === 0) {\n continue;\n }\n const owner = this.semanticsOwnerOf(node);\n if (owner === null) {\n return null;\n }\n owners.add(owner);\n if (owners.size > SCOPED_SEMANTICS_OWNERS) {\n // Enough of the tree is moving that one walk of all of it beats\n // this many walks of parts, and the parts may overlap besides.\n return null;\n }\n }\n if (owners.size === 0) {\n return null;\n }\n\n // Built completely before anything is committed, so a give-up part\n // way through leaves `this.semantics` as it was.\n const updates: UiSemanticsRecord[] = [];\n for (const owner of owners) {\n const previous = this.semantics.get(owner.id)!;\n const subtree = buildSemanticsSubtree(\n owner,\n previous.parent,\n this.semanticsIndexOf(previous),\n semanticsInertAbove(owner),\n this.semanticsMemory\n );\n if (subtree === null) {\n return null;\n }\n let covered = 0;\n for (const id of this.semanticIdsUnder(owner)) {\n const next = subtree.get(id);\n if (next === undefined) {\n return null;\n }\n covered++;\n // A stored index can be out of date (see `semantics`), and\n // nothing here moved, so only what the record says is compared.\n const before = this.semantics.get(id)!;\n if (!recordsEqual({ ...before, index: next.index }, next)) {\n updates.push(next);\n } else if (before.index !== next.index) {\n this.semantics.set(id, next);\n }\n }\n if (covered !== subtree.size) {\n // The subtree gained a record; see the note about ordering.\n return null;\n }\n }\n\n const patches: UiSemanticsPatch[] = [];\n for (const record of updates) {\n // Same key, so a map keeps the position, and therefore the\n // document order, it already had.\n this.semantics.set(record.id, record);\n this.semanticsOrdered?.set(record.id, record);\n patches.push({ op: 'update', node: record });\n }\n return patches;\n }\n\n /**\n * The patches for a frame that changed the shape of the tree, without\n * walking all of it, or null when the whole tree has to be rebuilt.\n *\n * Any frame that added or removed a child used to rebuild the tree\n * from the root, describing every node again. In a 5,000-line editor\n * that was 11 ms of every Enter, for one new paragraph. Now the walk\n * starts at the nearest record above each change, as\n * `rescopeSemantics` does for a change of meaning, and inside it a\n * subtree nothing touched keeps its records: renumbered if its\n * siblings moved, never described again (see\n * `rebuildSemanticsSubtree`). What is described is the path down to\n * each change, whatever is new, and everything under a node whose\n * own meaning changed.\n *\n * The result is spliced into `this.semantics` in place, since the map\n * is in document order and a record's subtree is a contiguous run of\n * it, so adds still arrive in document order. Removals come first.\n */\n private restructureSemantics(frame: UiFrame): readonly UiSemanticsPatch[] | null {\n if (this.semantics.size === 0 || this.semanticsStale) {\n return null;\n }\n const dirty: UiNode[] = [];\n /** Nodes whose own meaning may have changed: everything under them is described afresh. */\n const meaning = new Set<UiNode>();\n let structural = false;\n for (const [node, flags] of frame.entries()) {\n const children = (flags & DirtyFlags.Children) !== 0;\n const semantics = (flags & DirtyFlags.Semantics) !== 0;\n if (!children && !semantics) {\n continue;\n }\n if (!this.attached(node)) {\n // Removed this frame; its parent carries the change.\n continue;\n }\n structural ||= children;\n dirty.push(node);\n if (semantics) {\n meaning.add(node);\n }\n }\n if (!structural) {\n return null;\n }\n\n const owners = new Set<UiNode>();\n for (const node of dirty) {\n const owner = this.semanticsOwnerOf(node);\n if (owner === null) {\n return null;\n }\n owners.add(owner);\n }\n // One walk covers an owner inside another. Deleting the entry being\n // visited is safe mid-iteration; a Set only skips what is not yet reached.\n for (const owner of owners) {\n for (let above = owner.parent; above !== null; above = above.parent) {\n if (owners.has(above)) {\n owners.delete(owner);\n break;\n }\n }\n }\n if (owners.size > SCOPED_SEMANTICS_OWNERS) {\n return null;\n }\n\n // The path from each change up to its owner: the nodes that may now\n // hold different children, so none of them is taken back as it was.\n const path = new Set<UiNode>();\n for (const node of dirty) {\n for (let current: UiNode | null = node; current !== null && !path.has(current); current = current.parent) {\n path.add(current);\n if (owners.has(current)) {\n break;\n }\n }\n }\n const fresh = new Map<UiNode, boolean>();\n const underChangedMeaning = (node: UiNode): boolean => {\n const known = fresh.get(node);\n if (known !== undefined) {\n return known;\n }\n const answer =\n meaning.has(node) || (!owners.has(node) && node.parent !== null && underChangedMeaning(node.parent));\n fresh.set(node, answer);\n return answer;\n };\n const unchanged = (node: UiNode): boolean => !path.has(node) && (meaning.size === 0 || !underChangedMeaning(node));\n\n // Walked completely before anything is committed, so a give-up part\n // way through leaves the tree as it was.\n const positions = new Map<string | null, Map<string, number>>();\n const reuse = {\n previous: this.semantics,\n childrenOf: (parent: string | null): readonly string[] => this.semanticsChildren.get(parent) ?? [],\n positionOf: (parent: string | null, id: string): number => {\n let index = positions.get(parent);\n if (index === undefined) {\n index = new Map((this.semanticsChildren.get(parent) ?? []).map((child, i) => [child, i]));\n positions.set(parent, index);\n }\n return index.get(id) ?? -1;\n },\n memory: this.semanticsMemory,\n unchanged\n };\n const walks: SemanticsRewalk[] = [];\n for (const owner of owners) {\n const before = this.semantics.get(owner.id);\n if (before === undefined) {\n return null;\n }\n const walked = rewalkSemantics(\n owner,\n before.parent,\n this.semanticsIndexOf(before),\n semanticsInertAbove(owner),\n reuse\n );\n if (walked === null) {\n return null;\n }\n walks.push(walked);\n }\n\n // What left: the children a walked record had and no longer has,\n // anywhere in the walks, with everything under them.\n const present = new Set<string>();\n for (const walked of walks) {\n for (const list of walked.children.values()) {\n for (const id of list) {\n present.add(id);\n }\n }\n }\n const removals: UiSemanticsPatch[] = [];\n const remove = (id: string): void => {\n removals.push({ op: 'remove', id });\n for (const child of this.semanticsChildren.get(id) ?? []) {\n remove(child);\n }\n this.semanticsChildren.delete(id);\n this.semantics.delete(id);\n };\n const formerly = new Map<string | null, readonly string[]>();\n for (const walked of walks) {\n for (const parent of walked.children.keys()) {\n formerly.set(parent, this.semanticsChildren.get(parent) ?? []);\n }\n }\n for (const list of formerly.values()) {\n for (const id of list) {\n if (!present.has(id) && this.semantics.has(id)) {\n remove(id);\n }\n }\n }\n\n // Parents whose remaining children changed order among themselves:\n // there, every child's new index is sent. Anywhere else a removal\n // or an addition leaves the others where a mirror already has them.\n const reordered = new Set<string | null>();\n for (const walked of walks) {\n for (const [parent, list] of walked.children) {\n const old = formerly.get(parent) ?? [];\n if (old.length === 0) {\n continue;\n }\n const was = new Map(old.map((id, i) => [id, i]));\n let last = -1;\n for (const id of list) {\n const at = was.get(id);\n if (at === undefined) {\n continue;\n }\n if (at < last) {\n reordered.add(parent);\n break;\n }\n last = at;\n }\n }\n }\n\n const changes: UiSemanticsPatch[] = [];\n for (const walked of walks) {\n for (const { id, parent, index } of walked.placed) {\n const record = walked.records.get(id);\n const before = this.semantics.get(id);\n if (record !== undefined) {\n if (before === undefined) {\n changes.push({ op: 'add', node: record });\n } else if (\n before.parent !== record.parent ||\n reordered.has(parent) ||\n !recordsEqual({ ...before, index: record.index }, record)\n ) {\n changes.push({ op: 'update', node: record });\n }\n this.semantics.set(id, record);\n } else if (before === undefined) {\n // Taken back from a record removed by another walk this frame.\n return null;\n } else if (before.parent !== parent || reordered.has(parent)) {\n const moved = { ...before, parent, index };\n changes.push({ op: 'update', node: moved });\n this.semantics.set(id, moved);\n }\n }\n for (const [parent, list] of walked.children) {\n this.semanticsChildren.set(parent, list);\n }\n }\n this.semanticsOrdered = null;\n return [...removals, ...changes];\n }\n\n /** Whether a node is still in the tree being laid out. */\n private attached(node: UiNode): boolean {\n const root = this.layoutRoot();\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (current === root) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * The nearest node at or above `node` that holds a semantics record,\n * or null when nothing above it does.\n */\n private semanticsOwnerOf(node: UiNode): UiNode | null {\n for (let current: UiNode | null = node; current !== null; current = current.parent) {\n if (this.semantics.has(current.id)) {\n return current;\n }\n }\n return null;\n }\n\n /**\n * The ids in the current tree that belong to `node`'s subtree.\n *\n * Read off the graph rather than remembered, which is sound here\n * precisely because the caller has established that no node's\n * children changed this frame.\n *\n * A text run is not a node and has no id of its own: it borrows its\n * paragraph's and adds its position, numbered from zero without\n * gaps. So the runs of a node are found by counting up until one is\n * missing, rather than by scanning every key for the prefix.\n */\n private semanticIdsUnder(node: UiNode): string[] {\n const ids: string[] = [];\n const stack: UiNode[] = [node];\n while (stack.length > 0) {\n const current = stack.pop()!;\n if (this.semantics.has(current.id)) {\n ids.push(current.id);\n for (let run = 0; ; run++) {\n const id = `${current.id}${TEXT_RUN_ID_SEPARATOR}${run}`;\n if (!this.semantics.has(id)) {\n break;\n }\n ids.push(id);\n }\n }\n for (let child = current.firstChild; child !== null; child = child.nextSibling) {\n stack.push(child);\n }\n }\n return ids;\n }\n\n /**\n * The mirrored nodes whose box differs from the one last sent, and\n * which are on screen.\n *\n * This runs on every frame that laid out, and `frameNeedsLayout`\n * counts a transform — so it runs on every scrolled frame, where\n * every box beneath the scroll container has moved. Whatever it\n * returns crosses to the shell and is written as four inline styles\n * per element, on the main thread, before the next paint. That is\n * the one per-frame cost this architecture puts back on the thread\n * it exists to keep free, and it was proportional to the whole\n * mounted tree.\n *\n * So it is bounded by the viewport instead. A node scrolled out of\n * sight keeps the last box the mirror was told about; there is\n * nothing on screen for it to be wrong about, and the sweep picks it\n * up again on the frame it comes back, because the box it is\n * compared against is still the stale one. The focused node is\n * always included whether or not it is on screen: the focus ring is\n * drawn from its rectangle, and focus moving to something off screen\n * is answered by `scrollIntoView`, whose own frame reports it.\n *\n * Ids that have left the tree are dropped here rather than tracked,\n * since a removal patch has already told the mirror about them.\n *\n * **Found by walking what is on screen, not by checking everything.**\n * Bounding what was sent still computed every node's box to find out\n * whether it was on screen: a 5,000-line document has three thousand\n * fields, and the sweep cost 4 to 24 ms of every keystroke. The walk\n * goes down from the root carrying the scroll and sticky offsets that\n * `visibleBox` adds up per node, and passes over any subtree whose\n * bounds (the hit tester's, see `subtreeBoundsFor`) are off screen.\n * A node it passes over is one this skipped before anyway.\n */\n private collectSemanticsBoxes(): UiSemanticsBox[] {\n const changed: UiSemanticsBox[] = [];\n const focused = this.focusManager.focusedNode;\n let focusedSeen = false;\n const offer = (id: string, box: LayoutBox): void => {\n const last = this.semanticsBoxes.get(id);\n if (last !== undefined && boxesEqual(last, box)) {\n return;\n }\n this.semanticsBoxes.set(id, box);\n changed.push({ id, box });\n };\n const root = this.root;\n if (root !== undefined) {\n // Fills the bounds for every record, if anything moved since.\n this.engine.subtreeBoundsFor(root);\n const stack: [UiNode, number, number][] = [[root, 0, 0]];\n while (stack.length > 0) {\n const [node, dx, dy] = stack.pop()!;\n const rec = this.engine.recordFor(node);\n let childX = dx;\n let childY = dy;\n if (rec !== undefined) {\n const x = dx + rec.stickyOffsetX;\n const y = dy + rec.stickyOffsetY;\n if (\n !rec.boundsUnbounded &&\n node !== focused &&\n !this.onScreen({\n x: rec.boundsMinX + x,\n y: rec.boundsMinY + y,\n width: rec.boundsMaxX - rec.boundsMinX,\n height: rec.boundsMaxY - rec.boundsMinY\n })\n ) {\n // Left where it was, deliberately: `semanticsBoxes` is what\n // the mirror has been told, and leaving the two in step is\n // what makes the box arrive on the frame this node returns.\n continue;\n }\n if (this.semantics.has(node.id)) {\n const box = { x: rec.x + x, y: rec.y + y, width: rec.width, height: rec.height };\n if (node === focused) {\n focusedSeen = true;\n offer(node.id, box);\n } else if (this.onScreen(box)) {\n offer(node.id, box);\n }\n }\n childX = x - (rec.scrollable ? rec.scrollX : 0);\n childY = y - (rec.scrollable ? rec.scrollY : 0);\n }\n // Last child first, so the boxes come out in document order.\n for (let child = node.lastChild; child !== null; child = child.previousSibling) {\n stack.push([child, childX, childY]);\n }\n }\n }\n // The focused node always, on screen or not: the focus ring is drawn\n // from its rectangle.\n if (focused !== null && focused !== undefined && !focusedSeen && this.semantics.has(focused.id)) {\n if (this.engine.recordFor(focused) !== undefined) {\n offer(focused.id, this.engine.visibleBox(focused));\n }\n }\n if (this.semanticsBoxes.size > this.semantics.size) {\n for (const id of this.semanticsBoxes.keys()) {\n if (!this.semantics.has(id)) {\n this.semanticsBoxes.delete(id);\n }\n }\n }\n return changed;\n }\n\n /**\n * Whether a box in viewport coordinates overlaps the canvas at all.\n *\n * Generous by a margin, so that something a scroll is about to bring\n * in has its rectangle before it arrives rather than one frame after.\n */\n private onScreen(box: LayoutBox): boolean {\n return (\n box.x < this.width + SEMANTICS_VIEWPORT_MARGIN &&\n box.y < this.height + SEMANTICS_VIEWPORT_MARGIN &&\n box.x + box.width > -SEMANTICS_VIEWPORT_MARGIN &&\n box.y + box.height > -SEMANTICS_VIEWPORT_MARGIN\n );\n }\n\n /** Scrolls every scroll container above `node` so a node-local box is visible. */\n private revealBox(node: UiNode, box: { x: number; y: number; width: number; height: number }): void {\n for (const adjustment of this.engine.revealAdjustments(node, 0, box)) {\n // On every keystroke, so it must land at once: a spring here\n // would leave the caret trailing the text being typed.\n this.smoothScroller.stop(adjustment.container);\n adjustment.container.setProperty('scrollX', adjustment.scrollX);\n adjustment.container.setProperty('scrollY', adjustment.scrollY);\n this.graph.markDirty(adjustment.container, DirtyFlags.Transform);\n }\n }\n\n /**\n * With the inspector on, a hover change repaints (the overlay follows\n * the pointer) and re-explains the hovered node for the listener.\n */\n private handleHoverChange(node: UiNode | null): void {\n this.sendCursor();\n this.sendScrollability();\n if (!this.inspector.isEnabled || !this.inspector.setHovered(node)) {\n return;\n }\n this.sendInspection();\n if (this.root !== undefined) {\n this.graph.markDirty(this.root, DirtyFlags.Paint);\n }\n }\n\n /**\n * Scrolling backed directly by layout records and node properties.\n *\n * The layout engine clamps scrollX/scrollY against content size on\n * every pass, so writing the raw offset here is enough.\n */\n private createScrollSink(): ScrollSink {\n // Where a container is going, not where the last layout left it: a\n // scroll written since that layout is in the property, and several\n // wheel steps arrive between two frames whenever a device sends\n // faster than the display draws. Each was added to the record's\n // offset, so each overwrote the last and most of a flick was lost.\n // Clamped, so an overshoot past either end isn't banked.\n const going = (node: UiNode, record: LayoutRecord, axis: 'x' | 'y'): number => {\n const written = node.getProperty(axis === 'x' ? 'scrollX' : 'scrollY');\n const offset = typeof written === 'number' ? written : axis === 'x' ? record.scrollX : record.scrollY;\n return Math.min(Math.max(offset, 0), scrollRange(record, axis));\n };\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: going(node, record, 'x'),\n scrollY: going(node, record, 'y'),\n maxScrollX: scrollRange(record, 'x'),\n maxScrollY: scrollRange(record, 'y'),\n horizontal: node.getProperty('direction') === 'row' || node.type === UiNodeType.Row,\n viewportWidth: record.width,\n viewportHeight: record.height\n };\n },\n scrollBy: (node, dx, dy, behavior): void => {\n const record = this.engine.recordFor(node);\n if (record === undefined) {\n return;\n }\n if (behavior === 'smooth') {\n // The smooth path adds to where the container is *going*, so\n // it needs the effective offset only as a starting point.\n if (dx !== 0) {\n this.smoothScroller.scrollBy(node, 'scrollX', dx, going(node, record, 'x'));\n }\n if (dy !== 0) {\n this.smoothScroller.scrollBy(node, 'scrollY', dy, going(node, record, 'y'));\n }\n return;\n }\n // An instant scroll wins over one in flight rather than racing\n // it: a thumb drag reads the offset back on every pointer move\n // and would chase a moving value.\n this.smoothScroller.stop(node);\n if (dx !== 0) {\n node.setProperty('scrollX', going(node, record, 'x') + dx);\n }\n if (dy !== 0) {\n node.setProperty('scrollY', going(node, record, 'y') + dy);\n }\n this.graph.markDirty(node, DirtyFlags.Transform);\n },\n scrollContainers: () => this.engine.scrollContainers(),\n revealScrollbars: (node): void => {\n this.engine.revealScrollbars(node);\n this.graph.markDirty(node, DirtyFlags.Paint);\n },\n scrollbar: (node, axis) => {\n const record = this.engine.recordFor(node);\n return record === undefined ? null : scrollbarThumb(record, axis);\n }\n };\n }\n\n /**\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 // A trackpad's steps, paced over frames (see `UiWheelController.advance`).\n // Before the lazy lists read the offset, so the rows this frame's step\n // reveals are built on it.\n if (this.input.wheel.advance(time)) {\n this.scheduler.wake();\n }\n\n // Lazy lists decide which rows to mount from the scroll offset the\n // frame is about to lay out with, so rows a scroll reveals are built,\n // measured and painted on that same frame.\n this.phaseTimings.virtualize = this.timePhase(\n () => this.hasVirtualWindows(),\n () => this.updateVirtualWindows()\n );\n }\n\n private hasVirtualWindows(): boolean {\n for (const node of this.engine.scrollContainers()) {\n if (node.properties.get(VIRTUAL_WINDOW_PROP) instanceof UiVirtualWindow) {\n return true;\n }\n if (node.properties.get(VIRTUAL_SHEET_PROP) instanceof UiVirtualSheet) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Advances every lazy list's window: reports the container's scroll\n * offset and viewport, and the measured extent of each mounted item,\n * then applies any scroll adjustment the window asks for to keep its\n * first item anchored while estimates above it are corrected.\n */\n private updateVirtualWindows(): void {\n for (const node of this.engine.scrollContainers()) {\n this.updateVirtualSheet(node);\n const window = node.properties.get(VIRTUAL_WINDOW_PROP);\n if (!(window instanceof UiVirtualWindow)) {\n continue;\n }\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n continue;\n }\n const column = window.axis === 'column';\n // A wheel may have written a newer offset than the record holds.\n const scrollProp = node.properties.get(column ? 'scrollY' : 'scrollX');\n const 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 * Advances a two-axis window, which needs both scroll offsets and\n * neither a measurement nor an anchor.\n *\n * A `LazySheet` is told its row height and column width, so the\n * offset of every cell is exact and the window is arithmetic. There\n * is nothing here to correct and so nothing to adjust the scroll by:\n * the rows and columns a scroll reveals are placed correctly on the\n * frame that reveals them.\n */\n private updateVirtualSheet(node: UiNode): void {\n const sheet = node.properties.get(VIRTUAL_SHEET_PROP);\n if (!(sheet instanceof UiVirtualSheet)) {\n return;\n }\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n return;\n }\n // A wheel may have written newer offsets than the record holds.\n const x = node.properties.get('scrollX');\n const y = node.properties.get('scrollY');\n sheet.update({\n scrollX: typeof x === 'number' ? x : rec.scrollX,\n scrollY: typeof y === 'number' ? y : rec.scrollY,\n width: rec.width,\n height: rec.height\n });\n }\n\n /**\n * Measures the mounted items, and returns the extent of the content\n * above the first of them — a lazy grid's header.\n *\n * A lazy grid puts its rows inside one Grid, so that the header and\n * every row share its tracks; the rows are that grid's children\n * rather than the scroll container's, which is why the walk goes\n * through a Grid as it goes through a Fragment.\n */\n private collectVirtualMeasures(parent: UiNode, column: boolean, out: VirtualItemMeasure[]): number {\n let lead = 0;\n for (let child = parent.firstChild; child !== null; child = child.nextSibling) {\n const index = child.properties.get(VIRTUAL_INDEX_PROP);\n if (typeof index !== 'number') {\n if (child.type === UiNodeType.Fragment || child.type === UiNodeType.Grid) {\n lead += this.collectVirtualMeasures(child, column, out);\n continue;\n }\n if (child.properties.get(VIRTUAL_LEAD_PROP) === true) {\n lead += this.extentOf(child, column);\n }\n continue;\n }\n const rec = this.engine.recordFor(child);\n if (rec === undefined) {\n continue;\n }\n out.push({ index, extent: this.extentOf(child, column) });\n }\n return lead;\n }\n\n /** A node's outer extent along the list's axis. */\n private extentOf(node: UiNode, column: boolean): number {\n const rec = this.engine.recordFor(node);\n if (rec === undefined) {\n return 0;\n }\n return column\n ? rec.measuredHeight + rec.marginTop + rec.marginBottom\n : rec.measuredWidth + rec.marginLeft + rec.marginRight;\n }\n\n /**\n * Tells the layout listeners what moved, and lays out again for as\n * long as what they do in answer needs it, so the frame paints the\n * layout they settle on rather than the one they were told about.\n *\n * The browser's answer to the same problem. A `ResizeObserver`\n * callback runs after layout and before paint, and when it changes\n * layout the browser lays out again before it paints rather than\n * showing the stale boxes for a frame. The listeners here are\n * `breakpoint`, `sizeContainer` (and so `Responsive`),\n * `scrollPosition`, `autoFocus` and anything else on `host.onLayout`.\n * Before this a page whose breakpoint gave it wide padding was\n * painted with its narrow padding on the frame it appeared, and\n * jumped 16 px on the next.\n *\n * A pass after the first lays out only what the listeners dirtied,\n * the same incremental pass any frame runs, and then tells only the\n * listeners whose boxes it changed. A frame whose listeners wrote\n * nothing that lays out, which is almost every frame, runs no second\n * pass: it pays one look at the dirty set.\n *\n * **Bounded.** Listeners that keep moving each other's boxes would\n * otherwise never let the frame paint. ResizeObserver bounds its loop\n * by tree depth; this one is bounded by count, `MAX_LAYOUT_PASSES` in\n * all, which is easier to reason about and more than a real chain\n * needs (a breakpoint inside a `Responsive` inside a `Responsive` is\n * four passes). Past it the frame paints what it has, the rest waits\n * for the next frame as it always used to, and a warning says so\n * once, as the browser's \"ResizeObserver loop completed with\n * undelivered notifications\" does.\n *\n * **Reveals wait for the end.** `autoFocus` takes focus from a layout\n * listener, on its node's first layout, and focus reveals the node:\n * revealed from the first pass's boxes, it scrolled to where the node\n * was before a breakpoint moved it. A reveal asked for while the\n * listeners run is applied once they are quiet, from final boxes, and\n * its scroll laid out like any other write. So is the focus a hot\n * reload restores, after the listeners rather than before because\n * `autoFocus` is one of them and the restore has to be the last word:\n * every node in a subtree a reload replaced has its first layout on\n * this frame.\n */\n private settleLayout(frame: UiFrame, root: UiNode, laidOut: boolean): SettledLayout {\n const stats = this.engine.stats;\n const settled: SettledLayout = {\n frame,\n passes: laidOut ? 1 : 0,\n measured: laidOut ? stats.measured : 0,\n relayoutRoots: laidOut && !stats.fullLayout ? stats.relayoutRoots : 0\n };\n let fromRoot = laidOut && stats.fullLayout;\n const read = (node: UiNode) => {\n const record = this.engine.recordFor(node);\n return {\n box: this.engine.visibleBox(node),\n scrollX: record?.scrollX ?? 0,\n scrollY: record?.scrollY ?? 0\n };\n };\n this.deferredReveals = [];\n try {\n for (;;) {\n // Nothing listening means nothing walked.\n if (!this.layoutNotifier.isEmpty()) {\n this.layoutNotifier.notify(read);\n }\n if (!this.layoutPending()) {\n this.finishSettling(settled.passes > 0);\n if (!this.layoutPending()) {\n break;\n }\n }\n if (settled.passes >= MAX_LAYOUT_PASSES) {\n this.warnLayoutPasses();\n this.finishSettling(true);\n break;\n }\n // A listener's write can be inherited (a theme, a font size), so\n // what it changes for the subtree is worked out first, as the\n // frame does before its own layout.\n this.phaseTimings.environment += this.timePhase(\n () => this.graph.hasEnvironmentDirty(),\n () => this.graph.processEnvironmentDirty()\n );\n const more = this.scheduler.recollect(frame);\n settled.frame = settled.frame.merged(more);\n this.phaseTimings.layout += this.timePhase(\n () => true,\n () => this.engine.layoutForFrame(more, this.constraints, root)\n );\n settled.passes++;\n settled.measured += stats.measured;\n settled.relayoutRoots += stats.fullLayout ? 0 : stats.relayoutRoots;\n fromRoot ||= stats.fullLayout;\n }\n } finally {\n this.deferredReveals = null;\n }\n // Zero means the frame ran from the root, as one pass reports it.\n if (fromRoot) {\n settled.relayoutRoots = 0;\n }\n return settled;\n }\n\n /**\n * Whether something written since the frame was collected has to be\n * laid out before the frame paints: the same flags that make a frame\n * lay out, or an inherited value still to be handed down.\n */\n private layoutPending(): boolean {\n return this.graph.hasEnvironmentDirty() || this.graph.getDirtyNodes().anyFlags(LAYOUT_DIRT);\n }\n\n /** Applies what waits for settled boxes: a reload's focus, then the reveals asked for. */\n private finishSettling(laidOut: boolean): void {\n const reveals = this.deferredReveals ?? [];\n this.deferredReveals = null;\n if (this.restoringFocus && laidOut) {\n this.restoringFocus = false;\n this.restoreFocusAfterReload();\n }\n for (const { node, padding } of reveals) {\n // A node taken out by the listeners that asked has nothing to show.\n if (this.engine.recordFor(node) !== undefined) {\n this.scrollIntoView(node, padding);\n }\n }\n this.deferredReveals = [];\n }\n\n private warnLayoutPasses(): void {\n if (this.warnedLayoutPasses) {\n return;\n }\n this.warnedLayoutPasses = true;\n console.warn(\n `Gesso: a frame laid out ${MAX_LAYOUT_PASSES} times and its layout listeners were still changing it, so it ` +\n `was painted unsettled and the rest left for the next frame. Something on \\`host.onLayout\\` (a ` +\n `breakpoint, a sizeContainer, a Responsive, a modifier of your own) is writing a property that changes ` +\n `the box it is listening to, back and forth. Shown once.`\n );\n }\n\n private handleFrame(collected: UiFrame): void {\n const root = this.root;\n if (root === undefined) {\n return;\n }\n const started = now();\n\n // The tree for this frame exists now. A focus trap taken from a\n // `ref` — which fires before the node has children — enters its\n // subtree here, so a dialog opened this frame gets the caret in\n // it before the frame is laid out and revealed.\n this.focusManager.settleScope();\n\n const firstPass = frameNeedsLayout(collected);\n this.phaseTimings.layout = this.timePhase(\n () => firstPass,\n () => this.engine.layoutForFrame(collected, this.constraints, root)\n );\n // A modifier that follows its node's box hears about it here, after\n // the boxes are final and before anything paints from them; and\n // when what it does in answer moves boxes, they are laid out again\n // before anything paints. From here on `frame` is everything the\n // frame changed, the listeners' writes included.\n const settled = this.settleLayout(collected, root, firstPass);\n const frame = settled.frame;\n const laidOut = settled.passes > 0;\n if (laidOut) {\n this.laidOutOnce = true;\n this.inspector.recordLayout(started);\n }\n\n // Before the semantics phase, not after the frame: the mirror\n // decides whether to move DOM focus from what the editing proxy\n // reports, and a field that gains focus this frame must have been\n // reported by the time it does. Layout has run, so the caret box\n // it carries is this frame's.\n this.sendEditingState();\n\n // What the tree *means* changes far less often than where it sits,\n // so the tree is rebuilt only when a semantics property or the\n // shape of the tree moved. With an accessibility mirror attached\n // the phase also sweeps the mirrored boxes on any frame that laid\n // out, because an off-screen element that is not over its node\n // gives a screen reader's cursor the wrong rectangle — so this\n // reads 0.00 on an app with no mirror, and on a mirrored app only\n // on a frame that neither moved nor re-meant anything.\n const rebuildSemantics = frameNeedsSemantics(frame);\n this.phaseTimings.semantics = this.timePhase(\n () =>\n rebuildSemantics ||\n (this.semanticsListener !== null && (laidOut || this.focusManager.focusedNode?.id !== this.lastFocusedId)),\n () => this.updateSemantics(frame, rebuildSemantics, laidOut)\n );\n\n // Render is unconditional once the backend is ready: both backends\n // redraw the whole scene, so any frame that got this far changes\n // pixels. Before WebGPU has a device there is nothing to draw with;\n // a lost device 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 // What changed, for a renderer that keeps pixels across frames\n // (Canvas2D's scroll layers). Only offered when it is the whole\n // truth: a node dirtied since the frame was collected — by a layout\n // listener, a focus settling, anything in the phases above — may\n // already show in what is drawn and is not in `frame`, so the\n // renderer is told nothing and draws from scratch.\n const changes: RenderChanges | undefined = this.graph.getDirtyNodes().isEmpty()\n ? { frame, geometryVersion: this.engine.geometryVersion }\n : undefined;\n this.phaseTimings.render = this.timePhase(\n () => this.renderer.isReady,\n () =>\n this.renderer.render(root, {\n layout: this.engine,\n text: this.textMeasurer,\n now: started,\n overlay: overlay?.shapes,\n changes\n })\n );\n 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 // The engine's counters are reset when a layout pass starts, so\n // they are added up pass by pass (see `settleLayout`), and a frame\n // that skipped layout reports zeroes rather than the last pass's\n // numbers as its own: a caret blink claimed thousands of nodes.\n measured: settled.measured,\n relayoutRoots: settled.relayoutRoots,\n layoutPasses: settled.passes,\n at: finished,\n inputLatencyMs: this.inputLatency.take(epochAt(finished)),\n 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 /**\n * Times the frame laid out: 0 when it laid nothing out, 1 usually,\n * more when a layout listener (a `breakpoint`, a `sizeContainer`, an\n * `autoFocus`'s reveal) changed what the first pass laid out and the\n * frame laid it out again before painting. `measured` and\n * `relayoutRoots` count every pass.\n */\n layoutPasses: number;\n /** Milliseconds per phase. A phase with no work reports 0. */\n phases: FramePhaseTimings;\n /** The backend that drew this frame, or `pending` while WebGPU initialises. */\n renderer: RendererBackend | 'pending';\n /**\n * The WebGPU render phase split into its stages — building the render\n * list, uploading buffers, encoding and submitting — or null when\n * Canvas2D drew. Their sum is the render phase's cost on the GPU path.\n */\n gpu: GpuStageTimings | null;\n /**\n * When the frame finished, on the clock of the thread that rendered\n * it. Gaps between consecutive values are the only honest measure of\n * a stall: across a worker boundary the messages themselves queue up\n * behind a blocked main thread and all arrive at once, so arrival\n * times say nothing about when the work happened.\n */\n at: number;\n /**\n * How long the input this frame answers waited, from the moment the\n * shell received it to the moment this frame finished, or null when\n * the frame was not drawn for an input.\n *\n * The companion to `at`, and the measurement `at` cannot make: a\n * shell too busy to forward events costs the person a late response\n * while the render worker, with nothing new to draw, reports a\n * perfectly even frame gap. Null on a host that does not stamp its\n * input — see `GessoRuntime.noteInput`.\n */\n inputLatencyMs: number | null;\n}\n\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.html === b.html &&\n a.caret.x === b.caret.x &&\n a.caret.y === b.caret.y &&\n a.caret.width === b.caret.width &&\n a.caret.height === b.caret.height\n );\n}\n\n/** A canvas for the text measurer when the draw canvas is not a 2D one. */\nfunction createMeasureCanvas(): CanvasHost {\n if (typeof OffscreenCanvas !== 'undefined') {\n return new OffscreenCanvas(1, 1) as unknown as CanvasHost;\n }\n if (typeof document !== 'undefined') {\n const canvas = document.createElement('canvas');\n canvas.width = 1;\n canvas.height = 1;\n return canvas;\n }\n throw new Error('GessoRuntime: no canvas is available for text measurement; pass `measureCanvas`.');\n}\n\n/** Milliseconds per WebGPU stage of one frame. */\nexport interface GpuStageTimings {\n prepare: number;\n upload: number;\n encode: number;\n}\n\nfunction emptyGpuTimings(): GpuStageTimings {\n return { prepare: 0, upload: 0, encode: 0 };\n}\n\n/** What `settleLayout` leaves the rest of the frame. */\ninterface SettledLayout {\n /** Everything the frame changed, including what its layout listeners wrote. */\n frame: UiFrame;\n /** Layout passes run: 0 for a frame that laid nothing out, 1 for one whose listeners moved nothing. */\n passes: number;\n measured: number;\n relayoutRoots: number;\n}\n\n/**\n * The most layout passes one frame runs before it paints; see\n * `settleLayout`. One for the frame and up to seven more for what its\n * layout listeners change.\n */\nconst MAX_LAYOUT_PASSES = 8;\n\n/** Dirt that makes a frame lay out; `frameNeedsLayout` asks the same of a collected frame. */\nconst LAYOUT_DIRT = DirtyFlags.Layout | DirtyFlags.Children | DirtyFlags.SubtreeLayout | DirtyFlags.Transform;\n\nfunction emptyPhaseTimings(): FramePhaseTimings {\n return { ticks: 0, patches: 0, environment: 0, virtualize: 0, layout: 0, semantics: 0, render: 0 };\n}\n\n/**\n * Whether anything in the frame needs measuring or placing.\n *\n * Mirrors what LayoutEngine.layoutForFrame decides internally, so a\n * scroll-only frame is reported as skipping layout rather than\n * spending an immeasurable amount of time deciding to do nothing.\n */\nfunction frameNeedsLayout(frame: UiFrame): boolean {\n return frame.anyFlags(LAYOUT_DIRT);\n}\n\n/**\n * Semantics follow the properties that carry them and the shape of the\n * tree — a removed node marks its parent Children-dirty, which is how\n * a closed dialog leaves the tree.\n */\n/** Shared empties, so a frame that changed nothing allocates nothing. */\n/**\n * How many separate subtrees a scoped semantics update will walk\n * before it gives up and walks the whole tree once.\n *\n * A frame that re-meant a handful of nodes is the case this exists\n * for — a clock, a counter, a row whose label changed. A frame that\n * re-meant a hundred has almost certainly re-meant a shared ancestor's\n * worth of them, and the subtrees start to overlap, so one walk of\n * everything is both cheaper and simpler than many walks of parts.\n */\nconst SCOPED_SEMANTICS_OWNERS = 32;\n\n/**\n * The index each id in a parent's list of children has. A paragraph's\n * text runs are numbered among themselves and its other children among\n * themselves, as `buildSemanticsTree` numbers them.\n */\nfunction semanticIndices(list: readonly string[]): number[] {\n const out: number[] = [];\n let runs = 0;\n let others = 0;\n for (const id of list) {\n out.push(id.includes(TEXT_RUN_ID_SEPARATOR) ? runs++ : others++);\n }\n return out;\n}\n\n/**\n * How far outside the canvas a mirrored box is still worth reporting.\n *\n * One screenful of slack, so a fling that covers a lot of ground in\n * one frame still hands the mirror the rows it is about to show.\n */\nconst SEMANTICS_VIEWPORT_MARGIN = 400;\n\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 { FrameworkChild } from '../ComponentElement';\nimport {\n CanvasPlatformSurface,\n observeViewportInsets,\n prepareInputSurface,\n UiPlatformAdapter,\n type UiNode,\n type CanvasHost,\n UiAnimationFrameClock,\n type UiFrameClock,\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 { shellFilesUnsupported, type ShellRequest } from './ShellService';\nimport { AudioSink } from './AudioSink';\nimport { attachFileDrop } from './fileDrop';\nimport { browserFilesHost, ShellFiles } from './shellFiles';\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 { observeContrast } from './contrast';\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';\nimport type { UiHost } from '../agent/ui';\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 /**\n * The clock the runtime was given, its callback, and whether a frame\n * is waiting on it: what `uiHost().flush` needs to run that frame now,\n * since a background tab gets no animation frames to run it.\n */\n private clock: UiFrameClock | undefined;\n private frameCallback: ((time: number) => void) | undefined;\n private framePending = false;\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 detachFileDrop: (() => void) | null = null;\n /** Pickers and remembered handles, made on the first file request. */\n private files: ShellFiles | null = null;\n private detachFullscreen: (() => void) | null = null;\n private fullscreen = false;\n private detachReducedMotion: (() => void) | null = null;\n private detachContrast: (() => 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: callback => {\n const factory = options.clock ?? (onFrame => new UiAnimationFrameClock(onFrame));\n const clock = factory(time => {\n this.framePending = false;\n callback(time);\n });\n this.clock = clock;\n this.frameCallback = callback;\n return {\n requestFrame: () => {\n this.framePending = true;\n clock.requestFrame();\n },\n cancelFrame: () => {\n this.framePending = false;\n clock.cancelFrame();\n }\n };\n },\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 /**\n * The running app, as the agent's screen tools need it: its semantics\n * tree and focus, the mirror's actions, keys, and a way to run a\n * pending frame now. See `uiSurface` in `gesso-framework/agent`.\n */\n uiHost(): UiHost {\n const runtime = this.runtime;\n return {\n semanticsTree: () => runtime.semanticsTree(),\n focusedNodeId: () => runtime.focusedNodeId(),\n applySemanticsAction: action => runtime.applySemanticsAction(action),\n key: (key, modifiers) => {\n runtime.input.keyboard.keyDown(key, modifiers);\n runtime.input.keyboard.keyUp(key, modifiers);\n },\n flush: () => {\n if (this.framePending && this.clock !== undefined && this.frameCallback !== undefined) {\n this.framePending = false;\n this.clock.cancelFrame();\n this.frameCallback(performance.now());\n }\n }\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.detachFileDrop?.();\n this.detachFileDrop = null;\n this.detachFullscreen?.();\n this.detachFullscreen = null;\n this.detachReducedMotion?.();\n this.detachReducedMotion = null;\n this.detachContrast?.();\n this.detachContrast = 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, html) => editing.paste(text, html),\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 // Files dragged in from the desktop, handed straight to the\n // runtime: there is no thread to cross, so nothing to transfer.\n this.detachFileDrop = attachFileDrop(\n canvas,\n (clientX, clientY) => {\n const box = canvas.getBoundingClientRect();\n return { x: clientX - box.left, y: clientY - box.top };\n },\n message => this.runtime.applyFileDrop(message)\n );\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.detachContrast = observeContrast(contrast => this.runtime.setContrast(contrast));\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 {\n action: action => this.runtime.applySemanticsAction(action),\n // A paste onto something that is not a text field lands here,\n // because the element holding focus while the app has it is\n // one of the mirror's and not the canvas.\n paste: (text, html) => this.runtime.input.editing.paste(text, html)\n },\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 } else if (request.type === 'file') {\n this.runtime.settleFile(request.id, shellFilesUnsupported('There is no document to reach files through.'));\n } else if (request.type === 'clipboard') {\n this.runtime.settleClipboard(request.id, false);\n }\n return;\n }\n if (request.type === 'clipboard') {\n void writeClipboard(request.text, this.canvas.ownerDocument).then(copied =>\n this.runtime.settleClipboard(request.id, copied)\n );\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 === 'file') {\n if (view === null) {\n this.runtime.settleFile(request.id, shellFilesUnsupported('There is no window to reach files through.'));\n return;\n }\n // The same shell half the worker configuration runs, with no\n // thread to cross: the answer settles the promise directly.\n this.files ??= new ShellFiles(browserFilesHost(view as Window & typeof globalThis));\n void this.files.perform(request.request).then(result => this.runtime.settleFile(request.id, result));\n return;\n }\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 {\n createChannelRegistry,\n type ChannelRegistration,\n type ChannelRegistryHandle\n} from '../channel/createChannelRegistry';\nimport type { ServedChannel } from '../channel/serveChannels';\nimport { AGENT_PORT } from '../agent/remote';\nimport type { AgentConfirmation } from '../agent/AgentSurface';\n\n/** What `useWebMcp` takes besides a boolean: the app's own way to ask the person. */\nexport interface WebMcpChoice {\n confirm?: (request: AgentConfirmation) => boolean | Promise<boolean>;\n}\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 /** The mounted app's channel registry, for the workers an agent port asks. */\n private channelHandle: ChannelRegistryHandle | undefined;\n private webmcpChoice: boolean | WebMcpChoice = false;\n private disconnectWebMcp: (() => void) | null = null;\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 * createSyncApp(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 this.channelHandle = channels;\n app.mount();\n this.connectWebMcp(app);\n return () => {\n this.disconnectWebMcp?.();\n this.disconnectWebMcp = null;\n this.app = undefined;\n this.channelHandle = undefined;\n app.dispose();\n channels.dispose();\n };\n }\n\n /**\n * Offer the app's channels and screen to an AI agent in the browser,\n * through WebMCP (default false). The single-thread form of\n * `createApp({ webmcp })`: registered once `mountSync` has run,\n * removed when the app is unmounted. Call before `mountSync`.\n */\n useWebMcp(choice: boolean | WebMcpChoice = true): this {\n this.webmcpChoice = choice;\n return this;\n }\n\n /**\n * Opens a port to what this app serves under `key`, or returns\n * undefined before `mountSync`.\n *\n * The same question `WorkerApp.openRenderPort` answers, asked of the\n * page instead of a render worker, because in this configuration the\n * page is the thread that draws. It serves one thing, `gesso:agent`:\n * the channels fed here, the workers behind them, and the screen.\n * The agent code is loaded the first time one is opened.\n */\n openRenderPort(key: string): MessagePort | undefined {\n const app = this.app;\n if (app === undefined || key !== AGENT_PORT) {\n return undefined;\n }\n const pair = new MessageChannel();\n const registrations = this.channelRegistrations;\n void import('../agent/app').then(agent =>\n agent.serveApplicationAgent(pair.port2, {\n channels: () =>\n registrations\n .filter(registration => registration.source !== undefined)\n .map(\n registration => ({ token: registration.token, source: registration.source }) as unknown as ServedChannel\n ),\n workers: () => this.channelHandle?.workers ?? [],\n ui: () => (this.app === app ? app.uiHost() : undefined)\n })\n );\n return pair.port1;\n }\n\n private connectWebMcp(app: GessoApp): void {\n const choice = this.webmcpChoice;\n if (choice === false) {\n return;\n }\n void import('../agent/webmcp')\n .then(agent => agent.connectWebMcp(this, choice === true ? {} : choice))\n .then(\n disconnect => {\n if (this.app === app) {\n this.disconnectWebMcp = disconnect;\n } else {\n disconnect();\n }\n },\n (error: unknown) => console.warn('[gesso] WebMCP could not register the channels:', error)\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';\n\n/**\n * Creates a Gesso application that runs on the calling thread.\n *\n * createSyncApp(AppRoot).useChannel(Catalog, { source }).mountSync('#app');\n *\n * For tests, headless rendering, and environments without\n * `OffscreenCanvas`. Channels resolve in-process, so the same contract\n * runs with no ports.\n *\n * **It is a separate function from `createApp` for a reason that shows\n * up on the wire.** A single-thread application has to have the layout\n * engine, both renderers and the hit-tester on the thread it is\n * mounted from, and reaches them by importing them. A worker\n * application must not: its shell creates a canvas, forwards input and\n * holds neither. While one `createApp` served both, the reference to\n * this builder was in every shell's module graph whether or not it was\n * ever called, and a bundler cannot know that `createApp(options)` and\n * `createApp(root)` take different halves of the package — so every\n * worker application shipped the whole engine to the thread whose\n * entire point is not to run it. Measured on gessosheet's shell: 662.9\n * kB raw, 169.3 kB gzipped, against 196.6 kB and 49.5 kB for the same\n * shell reaching the worker configuration directly.\n *\n * Two names cost one line in a single-thread application and take 120\n * kB off the main thread of every other kind. `check-bundle-size.ts`\n * is what keeps it that way.\n */\nexport function createSyncApp(root: FrameworkChild | ComponentType): GessoAppBuilder {\n return new GessoAppBuilder(root);\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, servePorts, type PortHost, type WorkerHandle } from '../../worker/WorkerPorts';\nimport { AGENT_PORT } from '../../agent/remote';\nimport { serveApplicationAgent } from '../../agent/app';\nimport type { ServedChannel } from '../../channel/serveChannels';\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 // An agent asking, through the page, what this application serves.\n // Wrapped around `receive` rather than in it, because the question\n // arrives as the same port handshake every other worker answers.\n servePorts(\n (key, port) => {\n if (key !== AGENT_PORT) {\n return false;\n }\n this.serveAgent(port);\n return true;\n },\n () => [AGENT_PORT],\n host as unknown as PortHost\n );\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 /**\n * Answers an agent port with every channel this application can\n * reach: the ones fed from this thread, and whatever each worker\n * behind them serves, asked over a port of its own. Imported rather\n * than loaded on demand: a worker built as an IIFE, which is Vite's\n * default, cannot split off a chunk, and a dynamic import here failed\n * every such build. Nothing runs until an agent asks.\n */\n private serveAgent(port: MessagePort): void {\n serveApplicationAgent(port, {\n channels: () =>\n this.channelRegistrations\n .filter(registration => registration.source !== undefined)\n .map(\n registration => ({ token: registration.token, source: registration.source }) as unknown as ServedChannel\n ),\n workers: () => {\n const workers = new Set(this.channels?.workers ?? []);\n if (this.appLogicWorker !== undefined) {\n workers.add(this.appLogicWorker);\n }\n return workers;\n },\n ui: () => {\n const runtime = this.runtime;\n if (runtime === undefined) {\n return undefined;\n }\n return {\n semanticsTree: () => runtime.semanticsTree(),\n focusedNodeId: () => runtime.focusedNodeId(),\n applySemanticsAction: action => runtime.applySemanticsAction(action),\n key: (key, modifiers) => {\n runtime.input.keyboard.keyDown(key, modifiers);\n runtime.input.keyboard.keyUp(key, modifiers);\n },\n flush: () => this.clock?.tick(performance.now())\n };\n }\n });\n }\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 // The shell's epoch, as this thread's clock, which frames are on.\n message.at === undefined ? undefined : message.at - performance.timeOrigin\n );\n break;\n case 'keyDown':\n runtime.input.keyboard.keyDown(message.key, message.modifiers, message.textFollows);\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, message.html ?? null);\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 'contrast':\n runtime.setContrast(message.contrast);\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 'clipboardResult':\n runtime.settleClipboard(message.id, message.copied);\n break;\n case 'storageResult':\n runtime.settleStorage(message.id, message.result);\n break;\n case 'fileResult':\n runtime.settleFile(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 'fileDrop':\n runtime.applyFileDrop(message);\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', id: request.id, 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 === 'file') {\n this.host.postMessage({ type: 'file', id: request.id, request: request.request });\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 /**\n * A frame that threw, which the shell has to hear about.\n *\n * It used to arrive as `message`: the throw escaped the frame,\n * unwound into `receive`, and was caught there. That reported it\n * and killed the application, because nothing re-armed the clock\n * afterwards. Now the scheduler keeps drawing and the failure\n * comes back under its own name, which is also the more honest\n * one — the frame broke, not the message that happened to be\n * forwarding the tick.\n */\n this.runtime.onFrameError((message, stack) => {\n this.host.postMessage({ type: 'error', message, stack, source: 'frame' });\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 layoutPasses: metrics.layoutPasses,\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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"}
|