gesso-framework 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/agent/index.js +1 -1
- package/dist/{index-DocVauCS.d.ts → index-C08h2Jze.d.ts} +76 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +15 -3
- package/dist/index.js.map +1 -1
- package/dist/remote/index.d.ts +1 -1
- package/dist/{render-W2XO444K.js → render-DlhL5y0d.js} +108 -3
- package/dist/render-DlhL5y0d.js.map +1 -0
- package/dist/worker/index.d.ts +1 -1
- package/package.json +2 -2
- package/dist/render-W2XO444K.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["measure","measure"],"sources":["../src/fanOut.ts","../src/bind.ts","../src/themeTokenCell.ts","../src/controlled.ts","../src/sharedValue.ts","../src/channel/createChannelRegistry.ts","../src/overlay/OverlayScope.ts","../src/StartingInputs.ts","../src/ComponentElement.ts","../src/app/AudioSink.ts","../src/app/fileDrop.ts","../src/app/shellFiles.ts","../src/app/shellLeaveWarning.ts","../src/app/shellRedirect.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/pointer.ts","../src/app/windowActive.ts","../src/app/reducedMotion.ts","../src/app/shellHistory.ts","../src/app/fullscreen.ts","../src/app/pixelRatio.ts","../src/app/worker/WorkerApp.ts","../src/app/createApp.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 { 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 /** @internal What the cell reads with; changed only by `FanOut.move`. */\n held: D;\n\n constructor(\n initial: V,\n readonly key: FanKey,\n 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 this.held = datum;\n }\n\n /** What the reader is given beside the key: where the cell is, for a grid. */\n get datum(): D {\n return this.held;\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 * Points a live key at another datum, and pushes what it reads there.\n *\n * For a surface that keeps its cells by place on screen rather than\n * by what they show: a grid that reuses the cell in a row's slot for\n * whichever row is in that slot now. The cell keeps its identity, so\n * everything bound to it stays bound, and its value is read again\n * from the latest snapshot and pushed only if it differs. Releasing\n * the key and taking a new one instead would complete the cell and\n * tear its bindings down, which is the cost this exists to avoid.\n *\n * A key that is not live is made, as `for` would make it.\n */\n move(key: FanKey, datum: D): FanCell<V, D> {\n const cell = this.cells.get(key);\n if (cell === undefined) {\n return this.for(key, datum);\n }\n cell.held = datum;\n const next = this.hasSnapshot ? this.read(this.snapshot as S, key, datum) : this.initial(key, datum);\n if (!this.equal(cell.subject.value, next)) {\n cell.subject.next(next);\n }\n return cell;\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.held);\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 *\n * A renamed value with the handler left as `onChange` is an overload of\n * its own rather than a default on the event name's type parameter. A\n * parameter with no argument to infer from is inferred from where the\n * result goes, and a spread into JSX goes into the props around it, so\n * the default never applied: the name became every prop beside the\n * spread, and `<Switch label=\"Sync\" {...bind(on, 'checked')} />` said\n * `label` was given twice.\n */\nexport function bind<T>(cell: InternalState<T>): { value: InternalState<T>; onChange: (next: T) => void };\nexport function bind<T, V extends string>(\n cell: InternalState<T>,\n value: V\n): { [K in V]: InternalState<T> } & { onChange: (next: T) => void };\nexport function bind<T, V extends string, E extends string>(\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>(\n cell: ReadableCell<T>,\n write: (next: T) => void,\n value: V\n): { [K in V]: ReadableCell<T> } & { onChange: (next: T) => void };\nexport function bind<T, V extends string, E extends string>(\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 { 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, skip, type Observable } from 'rxjs';\n\nimport type { InputCell, OutputCell } from './Input';\nimport { internalState, type InternalState } from './InternalState';\nimport { liveInput } from './liveInput';\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 * Other outputs a parent may hear the change through instead: a\n * drawer's `onClose` beside its `onOpenChange`. A parent listening on\n * any of them owns the value, as one passing `onChange` does.\n */\n readonly heardBy?: readonly OutputCell<never>[];\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 *\n * A `value` with no `onChange` beside it has nobody to own it, so it is\n * where the control starts, and the control owns it from there; a new\n * value from the parent starts it again. A screen written as a picture\n * of a moment, `<Switch checked={true} />`, is then one a person can\n * use: the design tool's Play mode runs mocks written exactly so, and a\n * value that only the parent may change is what `readOnly` and\n * `disabled` say, not a missing handler.\n */\nexport function controlled<T>(options: ControlledOptions<T>): ControlledValue<T> {\n const { component, name, source, initial, fallback, onChange, heardBy = [] } = 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 // `getValue`, not `value`: a parent's handler is often a new arrow on\n // each render, and this look is not a body's snapshot of it.\n const heard = [onChange, ...heardBy].some(output => output.getValue() !== undefined);\n if (isControlled && heard) {\n liveInput(name, () => (source.getValue() === undefined ? fallback : source.getValue()));\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 start = isControlled ? source.value : initial.value;\n const own: InternalState<T> = internalState(start === undefined ? fallback : start);\n // What a person turned it to, for a tool saving the moment (`liveInput`).\n liveInput(name, () => own.getValue());\n if (isControlled) {\n // The parent's later values, each a new start. The cell completes\n // with the component, which ends this with it; the host already\n // drops a value that is the one it holds.\n source.pipe(skip(1)).subscribe(value => (own.value = value === undefined ? fallback : value));\n }\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 { createEnvironmentKey } from 'gesso-core';\n\nimport type { ComponentContext } from './FunctionComponent';\nimport { internalState, type InternalState } from './InternalState';\n\n/**\n * Named values the components under one node share, with no\n * application behind them: what one screen of a prototype was given\n * (an email typed on Forgot password), shown by the next (Check email),\n * or a wizard's steps passing on what was filled in.\n *\n * A node provides a store to what it holds,\n * `provide={[provided(SharedValuesKey, values)]}`, and a component asks\n * for a value by name with `sharedValue(ctx, name, start)`. Under a\n * store, every component asking for the same name has the same cell;\n * with none above, each has its own, at its own start. So a design tool\n * can draw a screen on its board as written, each frame alone, and play\n * it with the screens before it.\n *\n * It is not where an application's state goes. That survives a reload\n * and is on a channel, written by a command; these are cells, written\n * by whatever holds one, and gone with the store.\n */\nexport class SharedValues {\n private readonly cells = new Map<string, { readonly cell: InternalState<unknown>; readonly start: unknown }>();\n\n /** Every value by name, as it is now, in the order each was first asked for. */\n readonly values = internalState<ReadonlyMap<string, unknown>>(new Map());\n\n /** The value named `name`: made at `start` by the first to ask, and the same cell for each after. */\n cell<T>(name: string, start: T): InternalState<T> {\n let found = this.cells.get(name);\n if (found === undefined) {\n const cell = internalState<unknown>(start);\n cell.label = `shared.${name}`;\n found = { cell, start };\n this.cells.set(name, found);\n cell.subscribe(() => this.publish());\n }\n return found.cell as InternalState<T>;\n }\n\n /** Writes a value by name, as a component holding it would; a name nobody has asked for is ignored. */\n set(name: string, value: unknown): void {\n const found = this.cells.get(name);\n if (found !== undefined) {\n found.cell.value = value;\n }\n }\n\n /** Every value back at the start it was made with. */\n reset(): void {\n for (const { cell, start } of this.cells.values()) {\n cell.value = start;\n }\n }\n\n private publish(): void {\n this.values.value = new Map([...this.cells].map(([name, { cell }]) => [name, cell.getValue()]));\n }\n}\n\n/** Where a node provides a `SharedValues` to what it holds. */\nexport const SharedValuesKey = createEnvironmentKey<SharedValues | null>({\n name: 'gesso.sharedValues',\n defaultValue: null\n});\n\n/**\n * A value by name: the cell the nearest `SharedValues` above holds for\n * it, shared with every component asking for it there; with none\n * above, a cell of this component's own, at `start`. Bind it as any\n * cell: `<TextInput {...bind(email)} />`.\n */\nexport function sharedValue<T>(ctx: ComponentContext, name: string, start: T): InternalState<T> {\n const store = ctx.environment(SharedValuesKey);\n return store === null ? internalState(start) : store.cell(name, start);\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 { map } from 'rxjs';\n\nimport { percent, provided, Stack, type UiChild, type UiNode } from 'gesso-core';\n\nimport { boundFocus } from '../app/FocusService';\nimport { createComponent } from '../createComponent';\nimport type { ComponentContext, Inputs } from '../FunctionComponent';\nimport { OverlayLayer } from './OverlayLayer';\nimport { OverlayScopeKey, OverlayService } from './OverlayService';\n\nexport interface OverlayScopeProps {\n children?: UiChild;\n}\n\n/**\n * A part of the tree that holds its own overlays: a dialog, menu or\n * tooltip opened inside it is drawn inside it, over its content and\n * nowhere else.\n *\n * What a design tool's player needs from the screen it plays: a dialog\n * on that screen dims the screen, centres in it and is clipped by it,\n * is scaled with it when the screen is drawn smaller than its own size,\n * and leaves the tool around it pressable. A menu is placed and flipped\n * within it, since an anchored entry stays inside the box its layer\n * covers. Outside a scope, entries open in the runtime's own layer\n * above the whole application, as they always have.\n *\n * It fills what holds it, and is the containing block of what it\n * holds. `useOverlay` finds the nearest scope above the component that\n * opens the entry; with none, it uses the runtime's `OverlayService`.\n *\n * A dialog's trap inside it holds the keyboard inside it only: the\n * tool around it, and every other scope, keep their focus, and none is\n * taken from them when it opens or handed to them when it closes.\n */\nexport function OverlayScope(inputs: Inputs<OverlayScopeProps>, _ctx: ComponentContext): UiChild {\n const overlays = new OverlayService();\n return Stack(\n {\n position: 'relative',\n width: percent(100),\n height: percent(100),\n x: 'stretch',\n y: 'stretch',\n provide: [provided(OverlayScopeKey, overlays)],\n ref: (node: UiNode | null) => {\n if (node !== null) {\n boundFocus(node);\n }\n }\n },\n inputs.children.pipe(map(children => children ?? [])),\n createComponent(OverlayLayer, { service: overlays })\n );\n}\n","import { map } from 'rxjs';\n\nimport { percent, provided, Stack, type UiChild } from 'gesso-core';\n\nimport type { ComponentContext, Inputs } from './FunctionComponent';\nimport { StartingInputsKey, type StartingInputsByName } from './startingInputsKey';\n\nexport interface StartingInputsProps {\n /**\n * Inputs by component name: `{ ScrollArea: { scrollY: 600 }, Tour: { step: 1 } }`;\n * or a list for each instance in the order they are made, `null` for\n * one left as written: `{ Toggle: [null, { on: true }] }`.\n */\n of?: StartingInputsByName;\n children?: UiChild;\n}\n\n/**\n * Draws what it holds with the components in it started from given\n * inputs, named by component, over what their parents pass: every\n * `ScrollArea` in it scrolled to 600, every `Tour` at its second step.\n *\n * What a design tool draws a moment of a screen with, beside the\n * screen as written, without a copy of the screen to drift from it: a\n * list scrolled far enough for its back-to-top button, a dialog open,\n * a tour on its second step. A component reads them as it would its\n * parent's props, so one that takes a value without a handler beside\n * it starts there and is then the person's to change, in a design\n * tool's player as anywhere. A preview of inputs (`InspectionService.\n * previewInputs`) goes over them, and ending it comes back to them.\n *\n * They are read as each component is made, so they start a component\n * rather than steer one: a new `of` reaches components made after it,\n * and once the parent passes something new for an input, it is the\n * parent's again, as a dialog saved open closes when its owner closes\n * it.\n * Nested, the outer one wins for an input both give a component, and\n * the inner one's other inputs and names still reach in: what draws a\n * screen at a moment decides over what the screen starts itself with,\n * so a moment saved from a moment is the later one. A list per instance\n * is taken whole. It fills what holds it.\n */\nexport function StartingInputs(inputs: Inputs<StartingInputsProps>, ctx: ComponentContext): UiChild {\n const outer = ctx.environment(StartingInputsKey);\n return Stack(\n {\n width: percent(100),\n height: percent(100),\n x: 'stretch',\n y: 'stretch',\n provide: inputs.of.pipe(map(of => [provided(StartingInputsKey, merged(of ?? {}, outer))]))\n },\n inputs.children.pipe(map(children => children ?? []))\n );\n}\n\n/** `inner` with `outer` over it, input by input for a component both give one record. */\nfunction merged(inner: StartingInputsByName, outer: StartingInputsByName): StartingInputsByName {\n const out: Record<string, StartingInputsByName[string]> = { ...inner };\n for (const [name, given] of Object.entries(outer)) {\n const under = out[name];\n out[name] = under !== undefined && !Array.isArray(under) && !Array.isArray(given) ? { ...under, ...given } : given;\n }\n return out;\n}\n","import type { UiChild, UiSourceLocation } 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 /** Where the component was used, in development; see `UiSourceLocation`. */\n readonly source?: UiSourceLocation;\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 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 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 kind?: 'file';\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/** The slice of a `FileSystemDirectoryHandle` used here. */\nexport interface ShellDirectoryHandle {\n readonly kind: 'directory';\n readonly name: string;\n queryPermission?(descriptor: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n requestPermission?(descriptor: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n isSameEntry?(other: ShellFileHandle | ShellDirectoryHandle): Promise<boolean>;\n}\n\ntype AnyHandle = ShellFileHandle | ShellDirectoryHandle;\n\n/** A remembered handle, as the store keeps it. */\nexport interface StoredFileHandle {\n readonly id: number;\n readonly handle: AnyHandle;\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: AnyHandle;\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 showDirectoryPicker?(options: { mode: 'read' | 'readwrite' }): Promise<ShellDirectoryHandle>;\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, request.mode ?? 'read');\n case 'openDirectory':\n return await this.openDirectory(request.mode);\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.rememberIfPossible(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 openDirectory(mode: 'read' | 'readwrite'): Promise<ShellFileResult> {\n if (this.host.showDirectoryPicker === undefined) {\n return shellFilesUnsupported('This browser cannot open folders.');\n }\n const handle = await this.host.showDirectoryPicker({ mode });\n // Not `rememberIfPossible`: a folder is only reachable by the number\n // it is remembered under, so a folder that cannot be remembered\n // cannot be opened, and that is a failure.\n const id = await this.remember(handle);\n return ok({ directory: { name: handle.name, handle: id } });\n }\n\n private async reopen(id: number, mode: 'read' | 'readwrite'): 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, entry.handle.kind === 'directory' ? mode : 'read'))) {\n return denied();\n }\n if (entry.handle.kind === 'directory') {\n await this.touch(entry);\n return ok({ directory: { name: entry.handle.name, handle: id } });\n }\n const file = await entry.handle.getFile();\n await this.touch(entry);\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 (entry.handle.kind === 'directory') {\n return { ...failure(null), error: 'That handle is a folder, which cannot be written to as a file.' };\n }\n if (!(await permitted(entry.handle, 'readwrite'))) {\n return denied();\n }\n await write(entry.handle, request.bytes ?? request.text);\n await this.touch(entry);\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 = request.remember === false ? null : await this.rememberIfPossible(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\n .sort((a, b) => b.used - a.used)\n .map(entry => ({\n handle: entry.id,\n name: entry.name,\n kind: entry.handle.kind === 'directory' ? 'directory' : 'file',\n used: entry.used\n }));\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 * `remember`, for a file that has already been read or written.\n *\n * Remembering is what the recent files and a later Save are made of,\n * and a store can refuse it for reasons that have nothing to do with\n * the file: storage blocked for the site, a spent quota, a private\n * window. The file was opened or saved all the same, so that is the\n * answer, with no handle, exactly as a file input's file has none.\n */\n private async rememberIfPossible(handle: AnyHandle): Promise<number | null> {\n try {\n return await this.remember(handle);\n } catch {\n return null;\n }\n }\n\n /** Notes that a remembered file was used, for the recent list's order. Best effort, as above. */\n private async touch(entry: StoredFileHandle): Promise<void> {\n try {\n await this.host.store.put({ ...entry, used: this.host.now() });\n } catch {\n // The order of the recent files is the only thing lost.\n }\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: AnyHandle): Promise<number> {\n const all = await this.host.store.all();\n const now = this.host.now();\n for (const entry of all) {\n if (\n entry.handle.isSameEntry !== undefined &&\n (await (entry.handle.isSameEntry as (other: AnyHandle) => Promise<boolean>)(handle))\n ) {\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: AnyHandle, 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' | 'directory' | 'saved' | 'recent'>>): ShellFileResult {\n return { outcome: 'ok', files: [], directory: null, saved: null, recent: [], error: null, ...fields };\n}\n\nfunction cancelled(): ShellFileResult {\n return { outcome: 'cancelled', files: [], directory: null, saved: null, recent: [], error: null };\n}\n\nfunction denied(): ShellFileResult {\n return {\n outcome: 'denied',\n files: [],\n directory: null,\n saved: null,\n recent: [],\n error: 'Permission to use the file was refused.'\n };\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: [], directory: null, 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: AnyHandle; 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: AnyHandle; 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 showDirectoryPicker?: ShellFilesHost['showDirectoryPicker'];\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 ...(pickers.showDirectoryPicker === undefined\n ? {}\n : { showDirectoryPicker: options => pickers.showDirectoryPicker!.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\n/**\n * A folder the shell remembers, by its number, for the thread that reads\n * it: null when there is no such folder, or IndexedDB is not here.\n *\n * The shell keeps handles on the page's own thread and hands out\n * numbers, which suits a file read once. A folder is different: an\n * editor or a photo library reads it a file at a time, a hundred\n * thousand times, and each read crossing to the page and back would be\n * the whole cost. IndexedDB is shared by every thread of an origin, so\n * the worker that owns the work takes the handle from the shell's store\n * and reads the folder itself, with the permission the picker (or\n * `reopenDirectory`) granted.\n */\nexport async function rememberedDirectory(\n handle: number,\n factory: IDBFactory | undefined = globalThis.indexedDB\n): Promise<FileSystemDirectoryHandle | null> {\n if (factory === undefined) {\n return null;\n }\n const entry = (await new IndexedDbHandleStore(factory).all()).find(stored => stored.id === handle);\n return entry !== undefined && entry.handle.kind === 'directory'\n ? (entry.handle as unknown as FileSystemDirectoryHandle)\n : null;\n}\n","/**\n * The shell's half of `ShellService.warnOnLeave`: a `beforeunload`\n * listener on the window while the application says leaving would lose\n * work, and none otherwise.\n *\n * Both hosts answer the request — `WorkerApp` when it arrives as a\n * message, `GessoApp` when the runtime hands it over on the same\n * thread — through here. None otherwise matters as much as the\n * listener: a browser does not keep a page that has a `beforeunload`\n * listener in its back/forward cache, so a page that only ever turned\n * the warning on would lose the cache for good.\n */\ntype LeaveTarget = Pick<EventTarget, 'addEventListener' | 'removeEventListener'>;\n\nconst listening = new WeakMap<LeaveTarget, (event: Event) => void>();\n\nexport function setLeaveWarning(view: LeaveTarget | null | undefined, on: boolean): void {\n if (view === null || view === undefined) {\n return;\n }\n const listener = listening.get(view);\n if (on && listener === undefined) {\n const warn = (event: Event): void => {\n // `preventDefault` is the standard; `returnValue` is what browsers\n // before it asked for. The text is the browser's own either way.\n event.preventDefault();\n (event as BeforeUnloadEvent).returnValue = '';\n };\n listening.set(view, warn);\n view.addEventListener('beforeunload', warn);\n } else if (!on && listener !== undefined) {\n listening.delete(view);\n view.removeEventListener('beforeunload', listener);\n }\n}\n","/**\n * The shell's half of `ShellService.redirect`: the page is replaced\n * with a url a component asked for.\n *\n * Both hosts answer the request — `WorkerApp` when it arrives as a\n * message, `GessoApp` when the runtime hands it over on the same\n * thread — and both go through here, so the one rule that matters is\n * written once.\n *\n * That rule is the scheme. `location.assign('javascript:…')` does not\n * go anywhere; it runs the script in the page, with everything the\n * page can reach. A component builds the url it redirects to, often\n * from data — a `returnTo` from the address bar, a link from the\n * server — and a shell that assigned whatever it was given would make\n * every such url a way to run script. So a url is resolved against\n * the page first, the way the browser would resolve it, and only an\n * http or https result, or one in the page's own scheme, is followed.\n * The page's own scheme is there for a page that is not served over\n * http at all — an Electrobun window loads from `views://` — where a\n * relative url resolves to that scheme and is no less the app's own.\n */\n\n/** Schemes a redirect may always go to, whatever the page was served from. */\nconst WEB_SCHEMES = new Set(['http:', 'https:']);\n\n/**\n * Schemes refused even when the page itself has one of them.\n *\n * A page served from `data:` or `blob:` is unusual but real — a\n * preview, a sandboxed frame — and \"the page's own scheme\" must not\n * then admit a `data:` url built from data, which is script by\n * another name.\n */\nconst REFUSED_SCHEMES = new Set(['javascript:', 'data:', 'blob:', 'about:']);\n\n/**\n * The absolute url `url` names, resolved against `base` (the page's\n * address), or null when it is not one a redirect may follow.\n *\n * Null as well for a url that does not parse, or a base that does\n * not: a redirect nobody can resolve is refused rather than guessed.\n */\nexport function redirectTarget(url: string, base: string): string | null {\n let page: URL;\n let target: URL;\n try {\n page = new URL(base);\n target = new URL(url, page);\n } catch {\n return null;\n }\n if (WEB_SCHEMES.has(target.protocol)) {\n return target.href;\n }\n if (target.protocol === page.protocol && !REFUSED_SCHEMES.has(target.protocol)) {\n return target.href;\n }\n return null;\n}\n\n/**\n * Where a redirect goes once it is admitted: the host's handler when\n * it gave one, and the page's own `location.assign` otherwise.\n */\nexport interface RedirectLocation {\n readonly href: string;\n assign(url: string): void;\n}\n\n/**\n * Follows a redirect, or refuses it, and says which.\n *\n * The url is checked before the handler is called as well as before\n * the default: a desktop host's `onRedirect` navigates a webview, and\n * a `javascript:` url is no safer there. The handler is given the\n * resolved, absolute url, so it never has to know what page the\n * request came from.\n *\n * A refusal is a warning on the console and nothing else. There is no\n * one to report it to — the request carries no answer — and throwing\n * on the main thread would take down a message handler over a url\n * that was simply ignored.\n */\nexport function redirectWith(\n handler: ((url: string) => void) | undefined,\n url: string,\n location: RedirectLocation\n): boolean {\n const target = redirectTarget(url, location.href);\n if (target === null) {\n console.warn(\n `[gesso] ShellService.redirect refused ${JSON.stringify(url)}: only an http, https or relative url can replace the page.`\n );\n return false;\n }\n if (handler !== undefined) {\n handler(target);\n } else {\n location.assign(target);\n }\n return true;\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 *\n * A secret field (`EditingState.secret`) is edited through an\n * `<input type=\"password\">` in the textarea's place, made the first\n * time one takes focus. Only a password input makes the browser and the\n * OS treat what is typed as a password: macOS turns on secure input, so\n * no other application can read the keys; nothing autocorrects, learns\n * or spell-checks it; a screen reader calls it a secure text field and\n * does not echo the characters. Every listener the textarea has, the\n * input has too. Its value is the state's text, which for a secret\n * field is already the mask, so the value reaches no element.\n */\nexport class EditingProxy {\n private readonly textarea: HTMLTextAreaElement;\n /** The password input, once a secret field has had focus. */\n private password: HTMLInputElement | null = null;\n /** Whichever of the two the focused editable is edited through. */\n private field: HTMLTextAreaElement | HTMLInputElement;\n /** What `describe` was last given, so a swap of element carries it across. */\n private described: Parameters<EditingProxy['describe']> = [null];\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 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 this.field = textarea;\n textarea.setAttribute('wrap', 'off');\n const detachTextarea = this.prepare(textarea);\n const detachWindow = this.listenWindow();\n this.detach = () => {\n detachTextarea();\n detachWindow();\n };\n }\n\n /** Hides an element, puts it over the canvas and listens to it. */\n private prepare(field: HTMLTextAreaElement | HTMLInputElement): () => void {\n field.setAttribute('aria-hidden', 'true');\n field.setAttribute('autocomplete', 'off');\n field.setAttribute('autocorrect', 'off');\n field.setAttribute('autocapitalize', 'off');\n field.setAttribute('spellcheck', 'false');\n field.tabIndex = -1;\n Object.assign(field.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(field);\n return this.listen(field);\n }\n\n /** The password input, made the first time a secret field needs it. */\n private passwordField(): HTMLInputElement {\n if (this.password === null) {\n const input = this.doc.createElement('input');\n input.setAttribute('type', 'password');\n const detach = this.prepare(input);\n // `off`, not `current-password`: a password manager filling it\n // would set a value the runtime never hears (no `beforeinput`),\n // and offering to save one would offer to save the mask. The data\n // attributes ask the managers that ignore `autocomplete` on a\n // password input not to put their icon on a one-pixel element.\n input.setAttribute('data-1p-ignore', '');\n input.setAttribute('data-lpignore', 'true');\n this.password = input;\n const previous = this.detach;\n this.detach = () => {\n previous();\n detach();\n };\n }\n return this.password;\n }\n\n /** Whether an element is one this proxy edits through. */\n private owns(element: unknown): boolean {\n return element !== null && (element === this.textarea || element === this.password);\n }\n\n /**\n * Moves editing to the other element: the focused editable became, or\n * stopped being, a secret one. Focus follows if the old element had\n * it; the blur that causes is ours, not the person leaving the field.\n */\n private swapTo(next: HTMLTextAreaElement | HTMLInputElement): void {\n const previous = this.field;\n const hadFocus = this.doc.activeElement === previous;\n this.field = next;\n // A composition open in the old element ends with it.\n this.composing = false;\n this.write(previous, null);\n this.write(next, ...this.described);\n if (previous === this.password) {\n previous.value = '';\n }\n if (hadFocus) {\n this.refocusing = true;\n try {\n next.focus({ preventScroll: true });\n } finally {\n this.refocusing = false;\n }\n }\n }\n\n /** True while an editable in the runtime has focus. */\n get active(): boolean {\n return this.state !== null;\n }\n\n /**\n * The hidden element editing goes through: the textarea, or the\n * password input while a secret field has focus. For tests.\n */\n get element(): HTMLTextAreaElement | HTMLInputElement {\n return this.field;\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.field) {\n this.field.blur();\n this.canvas.focus({ preventScroll: true });\n }\n if (this.password !== null) {\n this.password.value = '';\n }\n }\n return;\n }\n const wanted = state.secret === true ? this.passwordField() : this.textarea;\n if (wanted !== this.field) {\n this.swapTo(wanted);\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.field.setAttribute('enterkeyhint', state.multiline ? 'enter' : 'done');\n if (!wasActive || this.doc.activeElement !== this.field) {\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 || this.owns(active)) {\n this.field.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.field) {\n return;\n }\n this.field.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 field = this.field;\n if (this.doc.activeElement !== field) {\n field.focus({ preventScroll: true });\n return;\n }\n this.refocusing = true;\n try {\n field.blur();\n field.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 * Nothing of the record's value is written: the element's own value\n * is the field's text (a secret field's mask), and that is what a\n * screen reader reads.\n */\n describe(record: UiSemanticsRecord | null, activeDescendant?: string, controls?: string): void {\n if (this.disposed) {\n return;\n }\n this.described = [record, activeDescendant, controls];\n this.write(this.field, record, activeDescendant, controls);\n }\n\n private write(\n textarea: HTMLTextAreaElement | HTMLInputElement,\n record: UiSemanticsRecord | null,\n activeDescendant?: string,\n controls?: string\n ): void {\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 'aria-controls',\n 'aria-autocomplete'\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,\n // which list it is, that it suggests, and which option the\n // 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 setOrClear(textarea, 'aria-controls', controls);\n setOrClear(textarea, 'aria-autocomplete', record.autocomplete);\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 this.password?.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.field.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.field;\n if (textarea.value !== state.text) {\n textarea.value = state.text;\n }\n const direction = state.selectionDirection ?? 'forward';\n if (\n textarea.selectionStart !== state.selectionStart ||\n textarea.selectionEnd !== state.selectionEnd ||\n (state.selectionEnd > state.selectionStart && textarea.selectionDirection !== direction)\n ) {\n try {\n textarea.setSelectionRange(state.selectionStart, state.selectionEnd, direction);\n } catch {\n // Some engines throw for an offset past the value; the next\n // state will be consistent.\n }\n }\n }\n\n /** Listens to one of the two elements; each has the same listeners. */\n private listen(textarea: HTMLTextAreaElement | HTMLInputElement): () => void {\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 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 if (state.secret === true) {\n // A secret field gives the clipboard nothing, and leaves what\n // was on it there, as a password input does.\n return;\n }\n // A surface may say what a copy takes (its whole line, with\n // nothing selected; several selections joined); else the\n // selected text.\n clipboard.setData('text/plain', state.clipboard ?? 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 (\n state !== null &&\n state.secret !== true &&\n (state.clipboard !== undefined ? state.clipboard.length > 0 : state.selectionEnd > state.selectionStart)\n ) {\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 && !this.owns(related)) {\n this.sink.blur();\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', this.onPaste);\n textarea.addEventListener('copy', onCopy);\n textarea.addEventListener('cut', onCut);\n textarea.addEventListener('keydown', onKeyDown);\n textarea.addEventListener('keyup', onKeyUp);\n textarea.addEventListener('blur', onBlur);\n\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', this.onPaste);\n textarea.removeEventListener('copy', onCopy);\n textarea.removeEventListener('cut', onCut);\n textarea.removeEventListener('keydown', onKeyDown);\n textarea.removeEventListener('keyup', onKeyUp);\n textarea.removeEventListener('blur', onBlur);\n };\n }\n\n private readonly 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 private listenWindow(): () => void {\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 on the field's element 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.owns(this.doc.activeElement)) {\n return;\n }\n this.onPaste(event);\n };\n\n const onReposition = (): void => {\n if (this.state !== null) {\n this.position(this.state);\n }\n };\n this.view?.addEventListener('scroll', onReposition, { capture: true, passive: true });\n this.view?.addEventListener('resize', onReposition);\n this.canvas.addEventListener('paste', onCanvasPaste);\n return () => {\n this.canvas.removeEventListener('paste', onCanvasPaste);\n this.view?.removeEventListener('scroll', onReposition, { capture: true });\n this.view?.removeEventListener('resize', onReposition);\n };\n }\n}\n\n/**\n * Reads the system clipboard's text from the main thread; null where\n * the browser has no async clipboard or refuses (no secure context, no\n * focus, or the person said no to its prompt). It never rejects.\n */\nexport function readClipboard(): Promise<string | null> {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard === undefined || typeof clipboard.readText !== 'function') {\n return Promise.resolve(null);\n }\n return clipboard.readText().then(\n text => text,\n () => null\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` and `controls` are the DOM ids of the elements\n * its record's `activeDescendant` and `controls` name, for\n * `aria-activedescendant` and `aria-controls`.\n */\n describe(record: UiSemanticsRecord | null, activeDescendant?: string, controls?: 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-rowindex',\n 'aria-colindex',\n 'aria-rowcount',\n 'aria-colcount',\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 'aria-controls',\n 'aria-autocomplete',\n 'aria-orientation'\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 the DOM ids of its active descendant and what it controls. */\n private describeEditing(record: UiSemanticsRecord | null): void {\n const active = record?.activeDescendant;\n const controls = record?.controls;\n this.editing?.describe(\n record,\n active === undefined ? undefined : this.idPrefix + active,\n controls === undefined ? undefined : this.idPrefix + controls\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 // `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 setNumber(element, 'aria-rowindex', record.rowIndex);\n setNumber(element, 'aria-colindex', record.columnIndex);\n setNumber(element, 'aria-rowcount', record.rowCount);\n setNumber(element, 'aria-colcount', record.columnCount);\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 // A relation, like the active descendant: the id of another record's element.\n if (record.controls !== undefined) {\n element.setAttribute('aria-controls', this.idPrefix + record.controls);\n }\n if (record.orientation !== undefined) {\n element.setAttribute('aria-orientation', record.orientation);\n }\n if (record.autocomplete !== undefined) {\n element.setAttribute('aria-autocomplete', record.autocomplete);\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 // Its own words, written before its children arrived, are its first\n // text node: kept in step, or a label that changed went on being read\n // as the first one. A list's header, a row with no role, said\n // \"Changes, 0 files\" over eighteen while drawing 18.\n const own = element.firstChild;\n if (own !== null && own.nodeType === TEXT_NODE && own.nodeValue !== text) {\n own.nodeValue = text;\n }\n return;\n }\n if (element.textContent !== text) {\n element.textContent = text;\n }\n}\n\n/** `Node.TEXT_NODE`, which a worker-free page has but a spec's fake document does not. */\nconst TEXT_NODE = 3;\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 type { UiPointer } from 'gesso-core';\n\nimport { observeMediaQuery } from './mediaQuery';\n\n/**\n * Watches what the person points with: `coarse` when the primary\n * pointer is a finger (CSS's `pointer: coarse`), `fine` for a mouse or a\n * trackpad. Shell-side, reported once at once and again on change, for\n * the reason `observeColorScheme` gives; the runtime provides it at the\n * root, where it turns every theme for a finger (`withPointer`).\n */\nexport function observePointer(onChange: (pointer: UiPointer) => void): () => void {\n return observeMediaQuery('(pointer: coarse)', matches => onChange(matches ? 'coarse' : 'fine'));\n}\n","/**\n * Watches whether the window is the one the person is using: shown, and\n * holding the keyboard focus. A window behind another, minimised, or in\n * a background tab is not.\n *\n * Shell-side, reported once at once and again on change, for the reason\n * `observeColorScheme` gives: a window that starts in the background\n * fires nothing until it is brought forward.\n */\nexport function observeWindowActive(onChange: (active: boolean) => void): () => void {\n let last: boolean | null = null;\n const report = (): void => {\n // A document that cannot say where the focus is (some embedded webviews, a test's stand-in) is taken to have it.\n const focused = typeof document.hasFocus === 'function' ? document.hasFocus() : true;\n const active = document.visibilityState === 'visible' && focused;\n if (active !== last) {\n last = active;\n onChange(active);\n }\n };\n window.addEventListener('focus', report);\n window.addEventListener('blur', report);\n document.addEventListener('visibilitychange', report);\n report();\n return () => {\n window.removeEventListener('focus', report);\n window.removeEventListener('blur', report);\n document.removeEventListener('visibilitychange', report);\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\n/**\n * A url kept somewhere, which the shell reads, writes and listens to.\n *\n * `createShellHistory` makes one from `ShellHistoryOptions`, and that\n * covers every window the app owns the address of. An app embedded in\n * a page whose address belongs to something else does not own it: an\n * Atlassian Forge Custom UI app runs in an iframe inside Jira, and the\n * only way to Jira's address bar is the history object Forge's bridge\n * hands out. Such a host implements this interface over whatever it\n * has, and passes the object as the app's `history` option in place of\n * options, so the router and the host's address bar stay one url.\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 /**\n * Reports urls the person produced: back, forward, or a typed address.\n *\n * There is one listener, and a second call replaces the first. An\n * app that was handed its history and is being taken down relies on\n * that to stop listening, since disposing a history it did not make\n * is not its decision.\n */\n onChange(listener: (url: string) => void): void;\n dispose(): void;\n /**\n * The address a new tab would open to show the app at `url`, or null\n * when this history has none to give.\n *\n * Asked when a link is followed somewhere new (`ShellService.openRoute`,\n * a Cmd-click or a Ctrl-click). The browser modes answer with the\n * document address that holds `url`, which is what `push` would have\n * written; `memory` answers null, because a url kept in memory names\n * no page anything else could load. Optional, because a history an\n * embedder wrote may have no such answer either, and leaving it out\n * says exactly that: the shell then follows the link in place. A\n * host that can open its own kind of tab (Jira's, through Forge's\n * `router.open`) says so with the shell's `onOpenRoute` instead.\n */\n href?(url: string): string | null;\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\n/**\n * The history an app runs against, and whether the app may dispose it.\n *\n * Options are turned into a history here, and that history is the\n * app's to dispose with everything else it made. A history passed in\n * ready-made is used as it is and left alone at the end, by the rule\n * `appLogicWorker` already follows: what the app made, it disposes;\n * what it was handed, it leaves to whoever handed it. That is also\n * what keeps a host's history alive across a remount, which disposes\n * the app and builds it again around the same object.\n */\nexport function resolveShellHistory(source: ShellHistoryOptions | ShellHistory | undefined): {\n readonly history: ShellHistory;\n readonly owned: boolean;\n} {\n if (isShellHistory(source)) {\n return { history: source, owned: false };\n }\n return { history: createShellHistory(source), owned: true };\n}\n\n/**\n * Tells a ready-made history from options by its methods.\n *\n * Options are plain data and never carry a function, so the presence of\n * `push` and `onChange` as functions is unambiguous. It is checked by\n * behaviour rather than by `instanceof` because the histories worth\n * passing are adapters an embedder wrote, not classes from here.\n */\nfunction isShellHistory(value: ShellHistoryOptions | ShellHistory | undefined): value is ShellHistory {\n return (\n value !== undefined &&\n typeof (value as Partial<ShellHistory>).push === 'function' &&\n typeof (value as Partial<ShellHistory>).onChange === 'function'\n );\n}\n\n/**\n * Opens the app at `url` somewhere new, as the shell's answer to\n * `ShellService.openRoute`.\n *\n * In this order, and the order is the decision. A host's own handler\n * wins, because an app embedded in another product or in a desktop\n * window knows what a new tab means there and a browser default would\n * be wrong in both. Without one, the history is asked for the address\n * that holds `url`, and that address is opened. Without an address,\n * which is `memory` mode and any handed-in history with no `href`, the\n * link is followed in place: a Cmd-click that did nothing at all would\n * read as a broken link, and arriving at the destination in the window\n * already open is what a browser does with a modifier it cannot honour.\n *\n * Shared by `WorkerApp` and `GessoApp`, which differ only in how a url\n * reaches the router, so it is passed in.\n */\nexport function openRouteWith(\n handler: ((url: string) => void) | undefined,\n url: string,\n history: ShellHistory | null,\n open: (address: string) => void,\n followInPlace: (url: string) => void\n): void {\n if (handler !== undefined) {\n handler(url);\n return;\n }\n const address = history?.href?.(url) ?? null;\n if (address !== null) {\n open(address);\n return;\n }\n followInPlace(url);\n}\n\n/**\n * Stops an app hearing about a history it is done with.\n *\n * A history the app made is disposed, which drops its listeners and\n * its window events with it. One it was handed is not the app's to\n * dispose, so the listener is replaced with one that does nothing:\n * the host's history goes on working, and a back button pressed after\n * the app is gone no longer reaches it.\n */\nexport function releaseShellHistory(history: ShellHistory, owned: boolean): void {\n if (owned) {\n history.dispose();\n } else {\n history.onChange(() => {});\n }\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 /** No address: a url kept here names no page another tab could load. */\n href(): string | null {\n return 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 /**\n * The address `push` would write for `url`, for a new tab to open.\n *\n * Relative, as `pushState` takes it, and resolved against the page by\n * whatever opens it: `/epic/2` in `path` mode, and the page's own\n * path and query with the fragment set in `hash` mode, so the new tab\n * loads the same document and starts on the same screen.\n */\n href(url: string): string | null {\n return this.toHref(url);\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","/**\n * Hears the window's device pixel ratio change while its size does not.\n *\n * A window dragged from a laptop's screen to an external monitor keeps\n * its size in CSS pixels, so no `ResizeObserver` fires, but each CSS\n * pixel is now a different number of device pixels. A canvas sized for\n * the old ratio is drawn blurred, or scaled, until something else\n * happens to resize it. The browser says when the ratio changes only\n * through a media query for the ratio it had, which stops matching; so\n * each change is heard once and the query is made again for the new\n * ratio.\n *\n * @returns A function that stops listening.\n */\nexport function watchPixelRatio(\n onChange: (dpr: number) => void,\n view: Pick<Window, 'matchMedia' | 'devicePixelRatio'> | undefined = globalThis.window\n): () => void {\n if (view === undefined || typeof view.matchMedia !== 'function') {\n return () => {};\n }\n let query: MediaQueryList | null = null;\n let stopped = false;\n const listen = (): void => {\n query = view.matchMedia(`(resolution: ${view.devicePixelRatio || 1}dppx)`);\n query.addEventListener('change', changed, { once: true });\n };\n function changed(): void {\n if (stopped) {\n return;\n }\n onChange(view!.devicePixelRatio || 1);\n listen();\n }\n listen();\n return () => {\n stopped = true;\n query?.removeEventListener('change', changed);\n };\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 isImeKey,\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 { setLeaveWarning } from '../shellLeaveWarning';\nimport { redirectWith } from '../shellRedirect';\nimport { portHandle, type WorkerHandle } from '../../worker/WorkerPorts';\nimport { EditingProxy, readClipboard, writeClipboard } from '../EditingProxy';\nimport { SemanticsMirror } from '../SemanticsMirror';\nimport { performShellStorage } from '../shellStorage';\nimport { observeColorScheme, type ColorSchemePreference } from '../colorScheme';\nimport { observeContrast } from '../contrast';\nimport { observePointer } from '../pointer';\nimport { observeWindowActive } from '../windowActive';\nimport { observeReducedMotion } from '../reducedMotion';\nimport {\n openRouteWith,\n releaseShellHistory,\n resolveShellHistory,\n type ShellHistory,\n type ShellHistoryOptions\n} from '../shellHistory';\nimport { afterLayout, isDocumentFullscreen, observeFullscreen, setElementFullscreen, surfaceBox } from '../fullscreen';\nimport { watchPixelRatio } from '../pixelRatio';\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 * Opens the app at one of its own urls somewhere new\n * (`ShellService.openRoute`, a Cmd-click or a Ctrl-click on an in-app\n * `Link`), in place of a new browser tab.\n *\n * `url` is the router's, `/epic/BUD-12?story=BUD-13`, not an address.\n * Without this the shell opens a new tab at the address its history\n * gives for it (`ShellHistory.href`): the origin and the path in\n * `path` mode, the page with that fragment in `hash` mode. With no\n * address to give, in `memory` mode or with a history that has no\n * `href`, it follows the url in place.\n *\n * A host whose idea of a new tab is not the browser's goes here: an\n * app embedded in another product opens one of that product's tabs\n * (an Atlassian Forge app, Forge's `router.open`), and a desktop\n * window asks for another window (`gesso-electrobun`'s bridge,\n * `openRoute`).\n */\n onOpenRoute?: (url: string) => void;\n /**\n * Replaces the page with a url the application asked for\n * (`ShellService.redirect`), in place of `window.location.assign`.\n *\n * A page wants `location.assign`, which is the default, and it is\n * usually right in a desktop window too: the webview navigates, the\n * way a page would, and comes back when the server redirects it back.\n * A host that has to do something else — reload the bundled app\n * rather than leave it, or send a sign-in to the person's browser and\n * wait for a deep link — handles it here.\n *\n * Called with the url resolved against the page, and only once the\n * shell has admitted it: a `javascript:` url, or any scheme that is\n * not http, https or the page's own, is refused before this is\n * consulted, so a handler need not check again.\n */\n onRedirect?: (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 and keyup that reaches the shell, so it\n * should be a comparison and nothing more. The keyup matters for a key\n * whose default comes on release: a lone Alt opens a Windows browser's\n * menu when it comes up, so a `MenuBar` that answers to Alt claims\n * both.\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 * Or a `ShellHistory` made elsewhere, for a page whose address the\n * app does not own. An app embedded in another product — an\n * Atlassian Forge Custom UI app is an iframe inside Jira — reaches\n * the address bar only through the history object its host hands\n * out, so it adapts that object to `ShellHistory` and passes it here,\n * and the router and the host's address stay one url. A history\n * passed this way is used as it is and survives the app: unmount\n * stops listening to it but does not dispose it, by the rule\n * `appLogicWorker` follows, and a remount picks the same one up.\n */\n history?: ShellHistoryOptions | ShellHistory;\n}\n\n/** Refreshes timed to tell the worker the display's interval. */\nconst DISPLAY_SAMPLES = 12;\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\n/**\n * What to say when the browser fails a worker at the worker object,\n * before the worker could say anything itself.\n *\n * Two different failures arrive this way. An exception thrown while the\n * worker's modules were evaluating is an `ErrorEvent` with a message,\n * and the message is the useful part. A script that never loaded at all\n * is a plain `Event` with no message and no filename, and the browser\n * says nothing more about it anywhere a script can read: the reason is\n * only in the network panel. That case used to be reported as \"failed\n * to start: undefined\", so it now says where to look and what the\n * usual reasons are. The last one is easy to hit: turn on\n * `Cross-Origin-Embedder-Policy` and a worker script the browser cached\n * before is refused, because a dev server's 304 does not repeat the\n * header.\n */\nexport function workerFailureMessage(worker: 'render' | 'app', event: Event): string {\n const name = worker === 'render' ? 'the render worker' : 'the app worker';\n if (hasMessage(event)) {\n const filename = (event as Partial<ErrorEvent>).filename;\n const where = filename === undefined || filename === '' ? '' : ` (${filename})`;\n return `${name} failed to start: ${(event as ErrorEvent).message}${where}`;\n }\n return (\n `${name} failed to start: its script did not load. The browser's network panel has the reason; ` +\n 'usually the file is missing (404), is served with a MIME type that is not JavaScript, or was refused ' +\n \"under the page's Cross-Origin-Embedder-Policy (including a copy cached before the policy was turned on; \" +\n 'a hard reload clears it).'\n );\n}\n\nfunction hasMessage(event: Event): boolean {\n const message = (event as Partial<ErrorEvent>).message;\n return typeof message === 'string' && message !== '';\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 /** The host's last size, for a pixel ratio that changes on its own; see `watchPixelRatio`. */\n private hostSize: { width: number; height: number } | null = null;\n private stopPixelRatio: (() => void) | 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 only when the history was made here from options; see `resolveShellHistory`. */\n private ownsHistory = false;\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 // Before `init`, which carries the address; see `attachHistory`.\n const url = this.attachHistory();\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 if (resolved.owned) {\n (application as Worker).addEventListener('error', this.handleAppWorkerFailure);\n }\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 url\n } as ShellToRuntimeMessage,\n transfer\n );\n\n this.observeResize(element);\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: Event): void => {\n event.preventDefault();\n if (this.ready) {\n return;\n }\n this.report(workerFailureMessage('render', event), undefined, 'uncaught');\n };\n\n /**\n * The app worker's counterpart, for the one failure nothing else\n * reports: a script that never loaded. Without this, an app worker\n * blocked by a 404 or by the page's embedder policy left the screen\n * waiting on channels that would never arrive, and said nothing.\n *\n * An exception inside the app worker arrives here too, with a\n * message, and is left alone: the browser reports it from inside the\n * worker with its stack, which is better than anything said here.\n */\n private handleAppWorkerFailure = (event: Event): void => {\n if (hasMessage(event)) {\n return;\n }\n event.preventDefault();\n this.report(workerFailureMessage('app', event), 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 if (isImeKey(event)) {\n // The IME's, not the app's: see `isImeKey`.\n return;\n }\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 code: event.code,\n modifiers: modifiersFrom(event),\n at: epochFromEvent(event),\n textFollows: viaProxy\n });\n }\n\n private forwardKeyUp(event: KeyboardEvent): void {\n if (isImeKey(event)) {\n return;\n }\n this.flushPendingMove();\n if (this.options.interceptKey?.(event) === true) {\n event.preventDefault();\n }\n this.post({\n type: 'keyUp',\n key: event.key,\n code: event.code,\n modifiers: modifiersFrom(event),\n at: epochFromEvent(event)\n });\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 /**\n * Measures the display's interval on this thread's refreshes and tells\n * the worker, whose own may keep no display's time; see\n * `displayInterval` in the protocol. A dozen refreshes, once, at start.\n */\n private measureDisplay(): void {\n if (typeof requestAnimationFrame !== 'function') {\n return;\n }\n const times: number[] = [];\n const step = (time: number): void => {\n times.push(time);\n if (times.length < DISPLAY_SAMPLES) {\n requestAnimationFrame(step);\n return;\n }\n const gaps = times\n .slice(1)\n .map((value, index) => value - times[index]!)\n .sort((a, b) => a - b);\n this.post({ type: 'displayInterval', ms: gaps[Math.floor(gaps.length / 2)]!, origin: performance.timeOrigin });\n };\n requestAnimationFrame(step);\n }\n\n dispose(): void {\n this.disconnectWebMcp?.();\n this.disconnectWebMcp = null;\n // An app that is gone has no work to lose.\n if (typeof window !== 'undefined') {\n setLeaveWarning(window, false);\n }\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 if (this.history !== null) {\n releaseShellHistory(this.history, this.ownsHistory);\n }\n this.history = null;\n this.ownsHistory = false;\n this.detachInput?.();\n this.detachInput = null;\n this.audio?.dispose();\n this.audio = null;\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.stopPixelRatio?.();\n this.stopPixelRatio = 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)?.removeEventListener('error', this.handleAppWorkerFailure);\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 time: message.time,\n wheelScroll: message.wheelScroll,\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 this.measureDisplay();\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 === 'clipboardRead') {\n // Answered on every path: `readClipboard` never rejects.\n void readClipboard().then(text => this.post({ type: 'clipboardReadResult', id: message.id, text }));\n return;\n }\n if (message.type === 'openUrl') {\n openUrlWith(this.options.onOpenUrl, message.url);\n return;\n }\n if (message.type === 'openRoute') {\n openRouteWith(\n this.options.onOpenRoute,\n message.url,\n this.history,\n // `noopener` for the reason `openUrl` has it, and no\n // `noreferrer`: the tab is this application's own page, and\n // its server is owed the referrer as much as any of its pages.\n address => window.open(address, '_blank', 'noopener'),\n // No address to open, so the link is followed here: written to\n // the history as the router would have, and reported back as\n // the url the window is now at, which the router resolves.\n url => {\n this.history?.push(url);\n this.post({ type: 'url', url });\n }\n );\n return;\n }\n if (message.type === 'redirect') {\n redirectWith(this.options.onRedirect, message.url, window.location);\n return;\n }\n if (message.type === 'leaveWarning') {\n setLeaveWarning(window, message.on);\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 * Keeps the worker told of the window's address, and answers the one\n * it starts at.\n *\n * That first one is not posted from here: `mount` puts it on `init`,\n * because an app opened directly at a url must start on the screen\n * that url names. Posted after `init`, as it used to be, it reached a\n * render worker that had already built its tree on the root route\n * and run that screen's effects — an app that opened its last\n * document on `/` did so for a tab opened at `/d/new`.\n */\n private attachHistory(): string {\n const { history, owned } = resolveShellHistory(this.options.history);\n this.history = history;\n this.ownsHistory = owned;\n history.onChange(url => this.post({ type: 'url', url }));\n return 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.hostSize = { width, height };\n this.requestResize({ width, height, dpr: window.devicePixelRatio || 1 });\n }\n });\n this.resizeObserver.observe(element);\n // Moved to a display of another density: the same size, in more or fewer device pixels.\n this.stopPixelRatio = watchPixelRatio(dpr => {\n if (this.hostSize !== null && !this.fullscreen) {\n this.requestResize({ ...this.hostSize, dpr });\n }\n });\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 detachPointer = observePointer(pointer => this.post({ type: 'pointer', pointer }));\n const detachWindowActive = observeWindowActive(active => this.post({ type: 'windowActive', active }));\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 // A trackpad pinch, as WebKit reports it. Chromium and Firefox send\n // a pinch as a wheel with ctrlKey set, deltaY in hundredths of the\n // scale's natural log, and a canvas that zooms on the wheel takes\n // it with no more than that. WebKit sends gesture events instead,\n // and a web view that magnifies zooms the whole page on them, the\n // application's chrome along with the canvas. So the shell turns\n // each step of the gesture into the wheel a Chromium pinch would\n // have been, and keeps it from the page by the same rule as a\n // wheel: when the chain under the pointer would take that wheel.\n let pinchScale = 1;\n const onGestureStart = (event: Event): void => {\n pinchScale = 1;\n if (this.scrollability.up || this.scrollability.down) {\n event.preventDefault();\n }\n };\n const onGestureChange = (event: Event): void => {\n const pinch = event as PinchGestureEvent;\n const deltaY = pinchWheelDelta(pinchScale, pinch.scale);\n pinchScale = pinch.scale;\n if (deltaY === 0) {\n return;\n }\n this.flushPendingMove();\n if (wheelConsumedBy(this.scrollability, 0, deltaY)) {\n event.preventDefault();\n }\n const { x, y } = toLocal(pinch.clientX, pinch.clientY);\n this.post({\n type: 'wheel',\n x,\n y,\n deltaX: 0,\n deltaY,\n deltaMode: 0,\n modifiers: { ...modifiersFrom(pinch), ctrl: true },\n at: epochFromEvent(pinch)\n });\n };\n const onGestureEnd = (event: Event): void => {\n if (this.scrollability.up || this.scrollability.down) {\n event.preventDefault();\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('gesturestart', onGestureStart);\n canvas.addEventListener('gesturechange', onGestureChange);\n canvas.addEventListener('gestureend', onGestureEnd);\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 detachPointer();\n detachWindowActive();\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('gesturestart', onGestureStart);\n canvas.removeEventListener('gesturechange', onGestureChange);\n canvas.removeEventListener('gestureend', onGestureEnd);\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 * WebKit's `GestureEvent`, as far as a pinch uses it. Not in the DOM\n * typings, since no other engine has it.\n */\ninterface PinchGestureEvent extends Event {\n readonly scale: number;\n readonly clientX: number;\n readonly clientY: number;\n readonly shiftKey: boolean;\n readonly ctrlKey: boolean;\n readonly altKey: boolean;\n readonly metaKey: boolean;\n}\n\n/**\n * The wheel a Chromium pinch from one scale to the next would have\n * been: `deltaY` is -100·ln of the step, so spreading the fingers\n * scrolls up and zooms in, and a handler written for Chromium's pinch\n * takes WebKit's unchanged. Zero for a step that did not move, or a\n * scale that is not a scale.\n */\nexport function pinchWheelDelta(from: number, to: number): number {\n if (!(from > 0) || !(to > 0) || from === to) {\n return 0;\n }\n return -100 * Math.log(to / from);\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 { 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 { FrameService } from './FrameService';\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 { setLeaveWarning } from './shellLeaveWarning';\nimport { redirectWith } from './shellRedirect';\nimport { EditingProxy, readClipboard, 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 { observeWindowActive } from './windowActive';\nimport { afterLayout, isDocumentFullscreen, observeFullscreen, setElementFullscreen, surfaceBox } from './fullscreen';\nimport {\n openRouteWith,\n releaseShellHistory,\n resolveShellHistory,\n type ShellHistory,\n type ShellHistoryOptions\n} from './shellHistory';\nimport { measure } from './worker/WorkerApp';\nimport type { ChannelRegistry } from '../channel/ChannelRegistry';\nimport type { ServiceRegistry } from '../service/ServiceRegistry';\nimport { RouterService, type RouterRoutes } from '../router/RouterService';\nimport type { MediaOptions } from './MediaService';\nimport type { FontFamilyDeclaration } from './FontService';\nimport { renderModule } from '../agent/render';\nimport type { UiHost } from '../agent/ui';\nimport { watchPixelRatio } from './pixelRatio';\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 * Or a `ShellHistory` made elsewhere, for a page whose address the\n * app does not own: an iframe inside another product, where the\n * host's own history object is the only way to its address bar. It\n * is used as it is, and left undisposed when the app is disposed,\n * because it belongs to whoever passed it.\n */\n history?: ShellHistoryOptions | ShellHistory;\n /**\n * Opens the app at one of its own urls somewhere new\n * (`ShellService.openRoute`), in place of a new browser tab. The\n * same option, and the same fallbacks, as `WorkerAppOptions.onOpenRoute`.\n */\n onOpenRoute?: (url: string) => void;\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 onOpenRoute: ((url: string) => void) | undefined;\n\n private running = false;\n private resizeObserver: ResizeObserver | null = null;\n /** The host's last size, for a pixel ratio that changes on its own; see `watchPixelRatio`. */\n private hostSize: { width: number; height: number } | null = null;\n private stopPixelRatio: (() => void) | 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 /** True only when the history was made here from options; see `resolveShellHistory`. */\n private ownsHistory = false;\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 private detachWindowActive: (() => 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.onOpenRoute = options.onOpenRoute;\n this.colorSchemePreference = options.colorScheme ?? 'auto';\n // Here rather than at `mount`, because the runtime below builds its\n // tree at once and has to know the address to build the right one;\n // see `GessoRuntimeOptions.url`. Released in `dispose`, mounted or not.\n const { history, owned } = resolveShellHistory(options.history);\n this.history = history;\n this.ownsHistory = owned;\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 url: history.url,\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 this.runtime.services.get(FrameService).setFlush(() => this.flushFrame());\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 /** Runs a pending frame now: see `FrameService.flush`. */\n private flushFrame(): void {\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 * 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: () => this.flushFrame(),\n theme: at => runtime.themeAt(at),\n render: request =>\n renderModule(request, {\n load: path => import(/* @vite-ignore */ path),\n appTheme: () => runtime.themeAt(),\n channels: runtime.channels,\n services: runtime.services\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 // An app that is gone has no work to lose.\n if (isCanvasElement(this.canvas)) {\n setLeaveWarning(this.canvas.ownerDocument.defaultView, false);\n }\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.detachWindowActive?.();\n this.detachWindowActive = null;\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.detachViewportInsets?.();\n this.detachViewportInsets = null;\n if (this.history !== null) {\n releaseShellHistory(this.history, this.ownsHistory);\n }\n this.history = null;\n this.ownsHistory = false;\n this.adapter.detach();\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.stopPixelRatio?.();\n this.stopPixelRatio = 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.detachWindowActive = observeWindowActive(active => this.runtime.setWindowActive(active));\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 = this.history;\n if (history === null) {\n return;\n }\n this.runtime.onShellRequest(request => this.handleShellRequest(request, history));\n history.onChange(url => this.runtime.setUrl(url));\n // The runtime started at this address. Reported again only if it\n // moved between construction and mount, so a mount does not\n // republish a match nothing changed.\n if (history.url !== this.runtime.services.get(RouterService).url.value) {\n this.runtime.setUrl(history.url);\n }\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 (request.type === 'openRoute') {\n // Before the canvas check: a host's handler needs no document,\n // and an app with no window to open a tab from still follows the\n // link in place rather than dropping it.\n const view = isCanvasElement(this.canvas) ? this.canvas.ownerDocument.defaultView : null;\n openRouteWith(\n this.onOpenRoute,\n request.url,\n view === null ? null : history,\n address => view?.open(address, '_blank', 'noopener'),\n url => {\n history.push(url);\n this.runtime.setUrl(url);\n }\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 } else if (request.type === 'clipboardRead') {\n this.runtime.settleClipboardRead(request.id, null);\n }\n return;\n }\n if (request.type === 'clipboardRead') {\n void readClipboard().then(text => this.runtime.settleClipboardRead(request.id, text));\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 if (request.type === 'leaveWarning') {\n setLeaveWarning(view, request.on);\n return;\n }\n if (request.type === 'redirect') {\n // The same rule the worker configuration's shell applies, and\n // through the same function: the scheme is checked before the\n // page is replaced.\n if (view !== null) {\n redirectWith(undefined, request.url, view.location);\n }\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.hostSize = { width: entry.contentRect.width, height: entry.contentRect.height };\n this.resize(entry.contentRect.width, entry.contentRect.height);\n });\n this.resizeObserver.observe(this.host);\n // Moved to a display of another density: the same size, in more or fewer device pixels.\n this.stopPixelRatio = watchPixelRatio(() => {\n if (this.hostSize !== null && !this.fullscreen) {\n this.resize(this.hostSize.width, this.hostSize.height);\n }\n });\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';\nimport type { AgentToolDefinition } from '../agent/tools';\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 { ShellHistory, 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 /** The application's own agent tools (`useAgentTools`). */\n private readonly agentTools: AgentToolDefinition[] = [];\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 | ShellHistory | 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 /** Offers an agent tools of the application's own; see `renderRoot(...).useAgentTools`. */\n useAgentTools(tools: readonly AgentToolDefinition[]): this {\n this.agentTools.push(...tools);\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 * Or a `ShellHistory` made elsewhere, for an app embedded in a page\n * whose address belongs to its host; see `GessoAppOptions.history`.\n */\n useHistory(history: ShellHistoryOptions | ShellHistory): 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 tools: () => this.agentTools\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 { renderModule } from '../../agent/render';\nimport type { AgentToolDefinition } from '../../agent/tools';\nimport type { DesignThemes } from '../../agent/designedTheme';\nimport { isClaimedError } from '../claimedErrors';\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 { FrameService } from '../FrameService';\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 {\n isDiscreteInputMessage,\n isInputMessage,\n type RuntimeToShellMessage,\n type ShellToRuntimeMessage,\n type RuntimeErrorSource\n} 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 */\n/** An error the render worker reported, as an application's `onError` hears it. */\nexport interface RenderWorkerError {\n readonly message: string;\n readonly stack?: string;\n /** Where it came from: a frame, a listener, the renderer, a channel, a message, or nothing that caught it. */\n readonly source: RuntimeErrorSource;\n}\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 /** See `renderThemesWith`. */\n private renderThemes: () => DesignThemes = () => ({});\n /** See `loadModulesWith`. */\n private loadModule: (path: string) => Promise<Record<string, unknown>> = path => import(/* @vite-ignore */ path);\n /** The application's own agent tools (`useAgentTools`). */\n private readonly agentTools: AgentToolDefinition[] = [];\n /** What the application asked to hear of the errors this worker reports (`onError`). */\n private readonly errorListeners: ((error: RenderWorkerError) => void)[] = [];\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 // A frame an agent asked for, failing; it tells the agent.\n if (isClaimedError(event.error)) {\n return;\n }\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 if (isClaimedError(event.reason)) {\n return;\n }\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.reportError(where === undefined ? message : `${message} (${where})`, error?.stack, 'uncaught');\n }\n\n /**\n * Tells the shell about an error, which shows it (the development\n * overlay), and every listener the application gave `onError`.\n */\n private reportError(message: string, stack: string | undefined, source: RuntimeErrorSource): void {\n this.host.postMessage({ type: 'error', message, ...(stack === undefined ? {} : { stack }), source });\n for (const listener of this.errorListeners) {\n try {\n listener({ message, ...(stack === undefined ? {} : { stack }), source });\n } catch {\n // A listener that throws is not reported through itself.\n }\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 * How `render_frame` loads a module an agent names, given its path from\n * the project root with a leading slash (`/mocks/Settings.tsx`). By\n * default it is imported from where the application is served, which\n * suits an application drawing its own modules. One whose modules are\n * served from somewhere else says where: a design editor serving the\n * project it designs from another folder.\n *\n * renderRoot(Editor).loadModulesWith(path => import(`/@fs${projectRoot}${path}`))\n */\n loadModulesWith(load: (path: string) => Promise<Record<string, unknown>>): this {\n this.loadModule = load;\n return this;\n }\n\n /**\n * The themes an application designs in, asked on each call, when they\n * are not the one it is drawn in: a design editor's project's. They\n * are what `render_frame` draws in and `tokens_view` reports unless an\n * agent asks for the application's own; see `designedTheme`.\n */\n renderThemesWith(themes: () => DesignThemes): this {\n this.renderThemes = themes;\n return this;\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 * Offers an agent tools of the application's own, beside its\n * channels' and its screen's: ones that answer with something other\n * than a view, such as a measurement or a composed picture. See\n * `AgentToolDefinition`. Called again, it adds to them.\n *\n * renderRoot(Editor).useAgentTools(editorTools);\n */\n useAgentTools(tools: readonly AgentToolDefinition[]): this {\n this.agentTools.push(...tools);\n return this;\n }\n\n /**\n * Hears every error this worker reports to the shell: a frame that\n * threw, a listener, the renderer, a channel, anything uncaught. For\n * an application that shows errors itself, where they happen: a design\n * editor listing what is wrong with a mock rather than an overlay over\n * the editor, with the plugin's `overlay: false`.\n */\n onError(listener: (error: RenderWorkerError) => void): this {\n this.errorListeners.push(listener);\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.reportError(\n error instanceof Error ? error.message : String(error),\n error instanceof Error ? error.stack : undefined,\n '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 tools: () => this.agentTools,\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 theme: at => runtime.themeAt(at),\n designThemes: () => this.renderThemes(),\n render: request =>\n renderModule(request, {\n load: this.loadModule,\n themes: this.renderThemes,\n appTheme: () => runtime.themeAt(),\n channels: runtime.channels,\n services: runtime.services\n })\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 message.url\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 // The wheel's held step goes before any other input (see `GessoRuntime.wheel`).\n if (message.type !== 'wheel' && isInputMessage(message)) {\n runtime.releaseWheel();\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.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, message.code);\n break;\n case 'keyUp':\n runtime.input.keyboard.keyUp(message.key, message.modifiers, message.code);\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 'pointer':\n runtime.setPointer(message.pointer);\n break;\n case 'contrast':\n runtime.setContrast(message.contrast);\n break;\n case 'windowActive':\n runtime.setWindowActive(message.active);\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 'clipboardReadResult':\n runtime.settleClipboardRead(message.id, message.text);\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 'displayInterval':\n this.clock?.displayInterval(message.ms, message.origin);\n break;\n case 'fullscreenChanged':\n runtime.setFullscreen(message.active);\n return;\n case 'visibility':\n this.clock?.setHidden(!message.visible);\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 if (isDiscreteInputMessage(message)) {\n this.runtime.answerInput();\n }\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 url: string | undefined\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.reportError(`channel ${channelName}: ${message}`, stack, 'channel');\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 url,\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.services.get(FrameService).setFlush(() => this.clock?.tick(performance.now()));\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 === 'clipboardRead') {\n this.host.postMessage({ type: 'clipboardRead', id: request.id });\n } else if (request.type === 'openUrl') {\n this.host.postMessage({ type: 'openUrl', url: request.url });\n } else if (request.type === 'openRoute') {\n this.host.postMessage({ type: 'openRoute', url: request.url });\n } else if (request.type === 'redirect') {\n this.host.postMessage({ type: 'redirect', url: request.url });\n } else if (request.type === 'fullscreen') {\n this.host.postMessage({ type: 'fullscreen', enter: request.enter });\n } else if (request.type === 'leaveWarning') {\n this.host.postMessage({ type: 'leaveWarning', on: request.on });\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.reportError(message, undefined, 'renderer');\n });\n this.runtime.onListenerError((message, stack) => {\n this.reportError(message, stack, '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.reportError(message, stack, '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 time: metrics.time,\n wheelScroll: metrics.wheelScroll,\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","measure"],"sources":["../src/fanOut.ts","../src/bind.ts","../src/themeTokenCell.ts","../src/controlled.ts","../src/sharedValue.ts","../src/channel/createChannelRegistry.ts","../src/overlay/OverlayScope.ts","../src/StartingInputs.ts","../src/ComponentElement.ts","../src/app/AudioSink.ts","../src/app/fileDrop.ts","../src/app/shellFiles.ts","../src/app/shellLeaveWarning.ts","../src/app/shellRedirect.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/pointer.ts","../src/app/windowActive.ts","../src/app/reducedMotion.ts","../src/app/shellHistory.ts","../src/app/fullscreen.ts","../src/app/pixelRatio.ts","../src/app/worker/WorkerApp.ts","../src/app/createApp.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 { 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 /** @internal What the cell reads with; changed only by `FanOut.move`. */\n held: D;\n\n constructor(\n initial: V,\n readonly key: FanKey,\n 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 this.held = datum;\n }\n\n /** What the reader is given beside the key: where the cell is, for a grid. */\n get datum(): D {\n return this.held;\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 * Points a live key at another datum, and pushes what it reads there.\n *\n * For a surface that keeps its cells by place on screen rather than\n * by what they show: a grid that reuses the cell in a row's slot for\n * whichever row is in that slot now. The cell keeps its identity, so\n * everything bound to it stays bound, and its value is read again\n * from the latest snapshot and pushed only if it differs. Releasing\n * the key and taking a new one instead would complete the cell and\n * tear its bindings down, which is the cost this exists to avoid.\n *\n * A key that is not live is made, as `for` would make it.\n */\n move(key: FanKey, datum: D): FanCell<V, D> {\n const cell = this.cells.get(key);\n if (cell === undefined) {\n return this.for(key, datum);\n }\n cell.held = datum;\n const next = this.hasSnapshot ? this.read(this.snapshot as S, key, datum) : this.initial(key, datum);\n if (!this.equal(cell.subject.value, next)) {\n cell.subject.next(next);\n }\n return cell;\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.held);\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 *\n * A renamed value with the handler left as `onChange` is an overload of\n * its own rather than a default on the event name's type parameter. A\n * parameter with no argument to infer from is inferred from where the\n * result goes, and a spread into JSX goes into the props around it, so\n * the default never applied: the name became every prop beside the\n * spread, and `<Switch label=\"Sync\" {...bind(on, 'checked')} />` said\n * `label` was given twice.\n */\nexport function bind<T>(cell: InternalState<T>): { value: InternalState<T>; onChange: (next: T) => void };\nexport function bind<T, V extends string>(\n cell: InternalState<T>,\n value: V\n): { [K in V]: InternalState<T> } & { onChange: (next: T) => void };\nexport function bind<T, V extends string, E extends string>(\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>(\n cell: ReadableCell<T>,\n write: (next: T) => void,\n value: V\n): { [K in V]: ReadableCell<T> } & { onChange: (next: T) => void };\nexport function bind<T, V extends string, E extends string>(\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 { 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, skip, type Observable } from 'rxjs';\n\nimport type { InputCell, OutputCell } from './Input';\nimport { internalState, type InternalState } from './InternalState';\nimport { liveInput } from './liveInput';\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 * Other outputs a parent may hear the change through instead: a\n * drawer's `onClose` beside its `onOpenChange`. A parent listening on\n * any of them owns the value, as one passing `onChange` does.\n */\n readonly heardBy?: readonly OutputCell<never>[];\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 *\n * A `value` with no `onChange` beside it has nobody to own it, so it is\n * where the control starts, and the control owns it from there; a new\n * value from the parent starts it again. A screen written as a picture\n * of a moment, `<Switch checked={true} />`, is then one a person can\n * use: the design tool's Play mode runs mocks written exactly so, and a\n * value that only the parent may change is what `readOnly` and\n * `disabled` say, not a missing handler.\n */\nexport function controlled<T>(options: ControlledOptions<T>): ControlledValue<T> {\n const { component, name, source, initial, fallback, onChange, heardBy = [] } = 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 // `getValue`, not `value`: a parent's handler is often a new arrow on\n // each render, and this look is not a body's snapshot of it.\n const heard = [onChange, ...heardBy].some(output => output.getValue() !== undefined);\n if (isControlled && heard) {\n liveInput(name, () => (source.getValue() === undefined ? fallback : source.getValue()));\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 start = isControlled ? source.value : initial.value;\n const own: InternalState<T> = internalState(start === undefined ? fallback : start);\n // What a person turned it to, for a tool saving the moment (`liveInput`).\n liveInput(name, () => own.getValue());\n if (isControlled) {\n // The parent's later values, each a new start. The cell completes\n // with the component, which ends this with it; the host already\n // drops a value that is the one it holds.\n source.pipe(skip(1)).subscribe(value => (own.value = value === undefined ? fallback : value));\n }\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 { createEnvironmentKey } from 'gesso-core';\n\nimport type { ComponentContext } from './FunctionComponent';\nimport { internalState, type InternalState } from './InternalState';\n\n/**\n * Named values the components under one node share, with no\n * application behind them: what one screen of a prototype was given\n * (an email typed on Forgot password), shown by the next (Check email),\n * or a wizard's steps passing on what was filled in.\n *\n * A node provides a store to what it holds,\n * `provide={[provided(SharedValuesKey, values)]}`, and a component asks\n * for a value by name with `sharedValue(ctx, name, start)`. Under a\n * store, every component asking for the same name has the same cell;\n * with none above, each has its own, at its own start. So a design tool\n * can draw a screen on its board as written, each frame alone, and play\n * it with the screens before it.\n *\n * It is not where an application's state goes. That survives a reload\n * and is on a channel, written by a command; these are cells, written\n * by whatever holds one, and gone with the store.\n */\nexport class SharedValues {\n private readonly cells = new Map<string, { readonly cell: InternalState<unknown>; readonly start: unknown }>();\n\n /** Every value by name, as it is now, in the order each was first asked for. */\n readonly values = internalState<ReadonlyMap<string, unknown>>(new Map());\n\n /** The value named `name`: made at `start` by the first to ask, and the same cell for each after. */\n cell<T>(name: string, start: T): InternalState<T> {\n let found = this.cells.get(name);\n if (found === undefined) {\n const cell = internalState<unknown>(start);\n cell.label = `shared.${name}`;\n found = { cell, start };\n this.cells.set(name, found);\n cell.subscribe(() => this.publish());\n }\n return found.cell as InternalState<T>;\n }\n\n /** Writes a value by name, as a component holding it would; a name nobody has asked for is ignored. */\n set(name: string, value: unknown): void {\n const found = this.cells.get(name);\n if (found !== undefined) {\n found.cell.value = value;\n }\n }\n\n /** Every value back at the start it was made with. */\n reset(): void {\n for (const { cell, start } of this.cells.values()) {\n cell.value = start;\n }\n }\n\n private publish(): void {\n this.values.value = new Map([...this.cells].map(([name, { cell }]) => [name, cell.getValue()]));\n }\n}\n\n/** Where a node provides a `SharedValues` to what it holds. */\nexport const SharedValuesKey = createEnvironmentKey<SharedValues | null>({\n name: 'gesso.sharedValues',\n defaultValue: null\n});\n\n/**\n * A value by name: the cell the nearest `SharedValues` above holds for\n * it, shared with every component asking for it there; with none\n * above, a cell of this component's own, at `start`. Bind it as any\n * cell: `<TextInput {...bind(email)} />`.\n */\nexport function sharedValue<T>(ctx: ComponentContext, name: string, start: T): InternalState<T> {\n const store = ctx.environment(SharedValuesKey);\n return store === null ? internalState(start) : store.cell(name, start);\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 { map } from 'rxjs';\n\nimport { percent, provided, Stack, type UiChild, type UiNode } from 'gesso-core';\n\nimport { boundFocus } from '../app/FocusService';\nimport { createComponent } from '../createComponent';\nimport type { ComponentContext, Inputs } from '../FunctionComponent';\nimport { OverlayLayer } from './OverlayLayer';\nimport { OverlayScopeKey, OverlayService } from './OverlayService';\n\nexport interface OverlayScopeProps {\n children?: UiChild;\n}\n\n/**\n * A part of the tree that holds its own overlays: a dialog, menu or\n * tooltip opened inside it is drawn inside it, over its content and\n * nowhere else.\n *\n * What a design tool's player needs from the screen it plays: a dialog\n * on that screen dims the screen, centres in it and is clipped by it,\n * is scaled with it when the screen is drawn smaller than its own size,\n * and leaves the tool around it pressable. A menu is placed and flipped\n * within it, since an anchored entry stays inside the box its layer\n * covers. Outside a scope, entries open in the runtime's own layer\n * above the whole application, as they always have.\n *\n * It fills what holds it, and is the containing block of what it\n * holds. `useOverlay` finds the nearest scope above the component that\n * opens the entry; with none, it uses the runtime's `OverlayService`.\n *\n * A dialog's trap inside it holds the keyboard inside it only: the\n * tool around it, and every other scope, keep their focus, and none is\n * taken from them when it opens or handed to them when it closes.\n */\nexport function OverlayScope(inputs: Inputs<OverlayScopeProps>, _ctx: ComponentContext): UiChild {\n const overlays = new OverlayService();\n return Stack(\n {\n position: 'relative',\n width: percent(100),\n height: percent(100),\n x: 'stretch',\n y: 'stretch',\n provide: [provided(OverlayScopeKey, overlays)],\n ref: (node: UiNode | null) => {\n if (node !== null) {\n boundFocus(node);\n }\n }\n },\n inputs.children.pipe(map(children => children ?? [])),\n createComponent(OverlayLayer, { service: overlays })\n );\n}\n","import { map } from 'rxjs';\n\nimport { percent, provided, Stack, type UiChild } from 'gesso-core';\n\nimport type { ComponentContext, Inputs } from './FunctionComponent';\nimport { StartingInputsKey, type StartingInputsByName } from './startingInputsKey';\n\nexport interface StartingInputsProps {\n /**\n * Inputs by component name: `{ ScrollArea: { scrollY: 600 }, Tour: { step: 1 } }`;\n * or a list for each instance in the order they are made, `null` for\n * one left as written: `{ Toggle: [null, { on: true }] }`.\n */\n of?: StartingInputsByName;\n children?: UiChild;\n}\n\n/**\n * Draws what it holds with the components in it started from given\n * inputs, named by component, over what their parents pass: every\n * `ScrollArea` in it scrolled to 600, every `Tour` at its second step.\n *\n * What a design tool draws a moment of a screen with, beside the\n * screen as written, without a copy of the screen to drift from it: a\n * list scrolled far enough for its back-to-top button, a dialog open,\n * a tour on its second step. A component reads them as it would its\n * parent's props, so one that takes a value without a handler beside\n * it starts there and is then the person's to change, in a design\n * tool's player as anywhere. A preview of inputs (`InspectionService.\n * previewInputs`) goes over them, and ending it comes back to them.\n *\n * They are read as each component is made, so they start a component\n * rather than steer one: a new `of` reaches components made after it,\n * and once the parent passes something new for an input, it is the\n * parent's again, as a dialog saved open closes when its owner closes\n * it.\n * Nested, the outer one wins for an input both give a component, and\n * the inner one's other inputs and names still reach in: what draws a\n * screen at a moment decides over what the screen starts itself with,\n * so a moment saved from a moment is the later one. A list per instance\n * is taken whole. It fills what holds it.\n */\nexport function StartingInputs(inputs: Inputs<StartingInputsProps>, ctx: ComponentContext): UiChild {\n const outer = ctx.environment(StartingInputsKey);\n return Stack(\n {\n width: percent(100),\n height: percent(100),\n x: 'stretch',\n y: 'stretch',\n provide: inputs.of.pipe(map(of => [provided(StartingInputsKey, merged(of ?? {}, outer))]))\n },\n inputs.children.pipe(map(children => children ?? []))\n );\n}\n\n/** `inner` with `outer` over it, input by input for a component both give one record. */\nfunction merged(inner: StartingInputsByName, outer: StartingInputsByName): StartingInputsByName {\n const out: Record<string, StartingInputsByName[string]> = { ...inner };\n for (const [name, given] of Object.entries(outer)) {\n const under = out[name];\n out[name] = under !== undefined && !Array.isArray(under) && !Array.isArray(given) ? { ...under, ...given } : given;\n }\n return out;\n}\n","import type { UiChild, UiSourceLocation } 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 /** Where the component was used, in development; see `UiSourceLocation`. */\n readonly source?: UiSourceLocation;\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 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 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 kind?: 'file';\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/** The slice of a `FileSystemDirectoryHandle` used here. */\nexport interface ShellDirectoryHandle {\n readonly kind: 'directory';\n readonly name: string;\n queryPermission?(descriptor: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n requestPermission?(descriptor: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;\n isSameEntry?(other: ShellFileHandle | ShellDirectoryHandle): Promise<boolean>;\n}\n\ntype AnyHandle = ShellFileHandle | ShellDirectoryHandle;\n\n/** A remembered handle, as the store keeps it. */\nexport interface StoredFileHandle {\n readonly id: number;\n readonly handle: AnyHandle;\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: AnyHandle;\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 showDirectoryPicker?(options: { mode: 'read' | 'readwrite' }): Promise<ShellDirectoryHandle>;\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, request.mode ?? 'read');\n case 'openDirectory':\n return await this.openDirectory(request.mode);\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.rememberIfPossible(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 openDirectory(mode: 'read' | 'readwrite'): Promise<ShellFileResult> {\n if (this.host.showDirectoryPicker === undefined) {\n return shellFilesUnsupported('This browser cannot open folders.');\n }\n const handle = await this.host.showDirectoryPicker({ mode });\n // Not `rememberIfPossible`: a folder is only reachable by the number\n // it is remembered under, so a folder that cannot be remembered\n // cannot be opened, and that is a failure.\n const id = await this.remember(handle);\n return ok({ directory: { name: handle.name, handle: id } });\n }\n\n private async reopen(id: number, mode: 'read' | 'readwrite'): 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, entry.handle.kind === 'directory' ? mode : 'read'))) {\n return denied();\n }\n if (entry.handle.kind === 'directory') {\n await this.touch(entry);\n return ok({ directory: { name: entry.handle.name, handle: id } });\n }\n const file = await entry.handle.getFile();\n await this.touch(entry);\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 (entry.handle.kind === 'directory') {\n return { ...failure(null), error: 'That handle is a folder, which cannot be written to as a file.' };\n }\n if (!(await permitted(entry.handle, 'readwrite'))) {\n return denied();\n }\n await write(entry.handle, request.bytes ?? request.text);\n await this.touch(entry);\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 = request.remember === false ? null : await this.rememberIfPossible(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\n .sort((a, b) => b.used - a.used)\n .map(entry => ({\n handle: entry.id,\n name: entry.name,\n kind: entry.handle.kind === 'directory' ? 'directory' : 'file',\n used: entry.used\n }));\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 * `remember`, for a file that has already been read or written.\n *\n * Remembering is what the recent files and a later Save are made of,\n * and a store can refuse it for reasons that have nothing to do with\n * the file: storage blocked for the site, a spent quota, a private\n * window. The file was opened or saved all the same, so that is the\n * answer, with no handle, exactly as a file input's file has none.\n */\n private async rememberIfPossible(handle: AnyHandle): Promise<number | null> {\n try {\n return await this.remember(handle);\n } catch {\n return null;\n }\n }\n\n /** Notes that a remembered file was used, for the recent list's order. Best effort, as above. */\n private async touch(entry: StoredFileHandle): Promise<void> {\n try {\n await this.host.store.put({ ...entry, used: this.host.now() });\n } catch {\n // The order of the recent files is the only thing lost.\n }\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: AnyHandle): Promise<number> {\n const all = await this.host.store.all();\n const now = this.host.now();\n for (const entry of all) {\n if (\n entry.handle.isSameEntry !== undefined &&\n (await (entry.handle.isSameEntry as (other: AnyHandle) => Promise<boolean>)(handle))\n ) {\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: AnyHandle, 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' | 'directory' | 'saved' | 'recent'>>): ShellFileResult {\n return { outcome: 'ok', files: [], directory: null, saved: null, recent: [], error: null, ...fields };\n}\n\nfunction cancelled(): ShellFileResult {\n return { outcome: 'cancelled', files: [], directory: null, saved: null, recent: [], error: null };\n}\n\nfunction denied(): ShellFileResult {\n return {\n outcome: 'denied',\n files: [],\n directory: null,\n saved: null,\n recent: [],\n error: 'Permission to use the file was refused.'\n };\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: [], directory: null, 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: AnyHandle; 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: AnyHandle; 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 showDirectoryPicker?: ShellFilesHost['showDirectoryPicker'];\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 ...(pickers.showDirectoryPicker === undefined\n ? {}\n : { showDirectoryPicker: options => pickers.showDirectoryPicker!.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\n/**\n * A folder the shell remembers, by its number, for the thread that reads\n * it: null when there is no such folder, or IndexedDB is not here.\n *\n * The shell keeps handles on the page's own thread and hands out\n * numbers, which suits a file read once. A folder is different: an\n * editor or a photo library reads it a file at a time, a hundred\n * thousand times, and each read crossing to the page and back would be\n * the whole cost. IndexedDB is shared by every thread of an origin, so\n * the worker that owns the work takes the handle from the shell's store\n * and reads the folder itself, with the permission the picker (or\n * `reopenDirectory`) granted.\n */\nexport async function rememberedDirectory(\n handle: number,\n factory: IDBFactory | undefined = globalThis.indexedDB\n): Promise<FileSystemDirectoryHandle | null> {\n if (factory === undefined) {\n return null;\n }\n const entry = (await new IndexedDbHandleStore(factory).all()).find(stored => stored.id === handle);\n return entry !== undefined && entry.handle.kind === 'directory'\n ? (entry.handle as unknown as FileSystemDirectoryHandle)\n : null;\n}\n","/**\n * The shell's half of `ShellService.warnOnLeave`: a `beforeunload`\n * listener on the window while the application says leaving would lose\n * work, and none otherwise.\n *\n * Both hosts answer the request — `WorkerApp` when it arrives as a\n * message, `GessoApp` when the runtime hands it over on the same\n * thread — through here. None otherwise matters as much as the\n * listener: a browser does not keep a page that has a `beforeunload`\n * listener in its back/forward cache, so a page that only ever turned\n * the warning on would lose the cache for good.\n */\ntype LeaveTarget = Pick<EventTarget, 'addEventListener' | 'removeEventListener'>;\n\nconst listening = new WeakMap<LeaveTarget, (event: Event) => void>();\n\nexport function setLeaveWarning(view: LeaveTarget | null | undefined, on: boolean): void {\n if (view === null || view === undefined) {\n return;\n }\n const listener = listening.get(view);\n if (on && listener === undefined) {\n const warn = (event: Event): void => {\n // `preventDefault` is the standard; `returnValue` is what browsers\n // before it asked for. The text is the browser's own either way.\n event.preventDefault();\n (event as BeforeUnloadEvent).returnValue = '';\n };\n listening.set(view, warn);\n view.addEventListener('beforeunload', warn);\n } else if (!on && listener !== undefined) {\n listening.delete(view);\n view.removeEventListener('beforeunload', listener);\n }\n}\n","/**\n * The shell's half of `ShellService.redirect`: the page is replaced\n * with a url a component asked for.\n *\n * Both hosts answer the request — `WorkerApp` when it arrives as a\n * message, `GessoApp` when the runtime hands it over on the same\n * thread — and both go through here, so the one rule that matters is\n * written once.\n *\n * That rule is the scheme. `location.assign('javascript:…')` does not\n * go anywhere; it runs the script in the page, with everything the\n * page can reach. A component builds the url it redirects to, often\n * from data — a `returnTo` from the address bar, a link from the\n * server — and a shell that assigned whatever it was given would make\n * every such url a way to run script. So a url is resolved against\n * the page first, the way the browser would resolve it, and only an\n * http or https result, or one in the page's own scheme, is followed.\n * The page's own scheme is there for a page that is not served over\n * http at all — an Electrobun window loads from `views://` — where a\n * relative url resolves to that scheme and is no less the app's own.\n */\n\n/** Schemes a redirect may always go to, whatever the page was served from. */\nconst WEB_SCHEMES = new Set(['http:', 'https:']);\n\n/**\n * Schemes refused even when the page itself has one of them.\n *\n * A page served from `data:` or `blob:` is unusual but real — a\n * preview, a sandboxed frame — and \"the page's own scheme\" must not\n * then admit a `data:` url built from data, which is script by\n * another name.\n */\nconst REFUSED_SCHEMES = new Set(['javascript:', 'data:', 'blob:', 'about:']);\n\n/**\n * The absolute url `url` names, resolved against `base` (the page's\n * address), or null when it is not one a redirect may follow.\n *\n * Null as well for a url that does not parse, or a base that does\n * not: a redirect nobody can resolve is refused rather than guessed.\n */\nexport function redirectTarget(url: string, base: string): string | null {\n let page: URL;\n let target: URL;\n try {\n page = new URL(base);\n target = new URL(url, page);\n } catch {\n return null;\n }\n if (WEB_SCHEMES.has(target.protocol)) {\n return target.href;\n }\n if (target.protocol === page.protocol && !REFUSED_SCHEMES.has(target.protocol)) {\n return target.href;\n }\n return null;\n}\n\n/**\n * Where a redirect goes once it is admitted: the host's handler when\n * it gave one, and the page's own `location.assign` otherwise.\n */\nexport interface RedirectLocation {\n readonly href: string;\n assign(url: string): void;\n}\n\n/**\n * Follows a redirect, or refuses it, and says which.\n *\n * The url is checked before the handler is called as well as before\n * the default: a desktop host's `onRedirect` navigates a webview, and\n * a `javascript:` url is no safer there. The handler is given the\n * resolved, absolute url, so it never has to know what page the\n * request came from.\n *\n * A refusal is a warning on the console and nothing else. There is no\n * one to report it to — the request carries no answer — and throwing\n * on the main thread would take down a message handler over a url\n * that was simply ignored.\n */\nexport function redirectWith(\n handler: ((url: string) => void) | undefined,\n url: string,\n location: RedirectLocation\n): boolean {\n const target = redirectTarget(url, location.href);\n if (target === null) {\n console.warn(\n `[gesso] ShellService.redirect refused ${JSON.stringify(url)}: only an http, https or relative url can replace the page.`\n );\n return false;\n }\n if (handler !== undefined) {\n handler(target);\n } else {\n location.assign(target);\n }\n return true;\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 *\n * A secret field (`EditingState.secret`) is edited through an\n * `<input type=\"password\">` in the textarea's place, made the first\n * time one takes focus. Only a password input makes the browser and the\n * OS treat what is typed as a password: macOS turns on secure input, so\n * no other application can read the keys; nothing autocorrects, learns\n * or spell-checks it; a screen reader calls it a secure text field and\n * does not echo the characters. Every listener the textarea has, the\n * input has too. Its value is the state's text, which for a secret\n * field is already the mask, so the value reaches no element.\n */\nexport class EditingProxy {\n private readonly textarea: HTMLTextAreaElement;\n /** The password input, once a secret field has had focus. */\n private password: HTMLInputElement | null = null;\n /** Whichever of the two the focused editable is edited through. */\n private field: HTMLTextAreaElement | HTMLInputElement;\n /** What `describe` was last given, so a swap of element carries it across. */\n private described: Parameters<EditingProxy['describe']> = [null];\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 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 this.field = textarea;\n textarea.setAttribute('wrap', 'off');\n const detachTextarea = this.prepare(textarea);\n const detachWindow = this.listenWindow();\n this.detach = () => {\n detachTextarea();\n detachWindow();\n };\n }\n\n /** Hides an element, puts it over the canvas and listens to it. */\n private prepare(field: HTMLTextAreaElement | HTMLInputElement): () => void {\n field.setAttribute('aria-hidden', 'true');\n field.setAttribute('autocomplete', 'off');\n field.setAttribute('autocorrect', 'off');\n field.setAttribute('autocapitalize', 'off');\n field.setAttribute('spellcheck', 'false');\n field.tabIndex = -1;\n Object.assign(field.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(field);\n return this.listen(field);\n }\n\n /** The password input, made the first time a secret field needs it. */\n private passwordField(): HTMLInputElement {\n if (this.password === null) {\n const input = this.doc.createElement('input');\n input.setAttribute('type', 'password');\n const detach = this.prepare(input);\n // `off`, not `current-password`: a password manager filling it\n // would set a value the runtime never hears (no `beforeinput`),\n // and offering to save one would offer to save the mask. The data\n // attributes ask the managers that ignore `autocomplete` on a\n // password input not to put their icon on a one-pixel element.\n input.setAttribute('data-1p-ignore', '');\n input.setAttribute('data-lpignore', 'true');\n this.password = input;\n const previous = this.detach;\n this.detach = () => {\n previous();\n detach();\n };\n }\n return this.password;\n }\n\n /** Whether an element is one this proxy edits through. */\n private owns(element: unknown): boolean {\n return element !== null && (element === this.textarea || element === this.password);\n }\n\n /**\n * Moves editing to the other element: the focused editable became, or\n * stopped being, a secret one. Focus follows if the old element had\n * it; the blur that causes is ours, not the person leaving the field.\n */\n private swapTo(next: HTMLTextAreaElement | HTMLInputElement): void {\n const previous = this.field;\n const hadFocus = this.doc.activeElement === previous;\n this.field = next;\n // A composition open in the old element ends with it.\n this.composing = false;\n this.write(previous, null);\n this.write(next, ...this.described);\n if (previous === this.password) {\n previous.value = '';\n }\n if (hadFocus) {\n this.refocusing = true;\n try {\n next.focus({ preventScroll: true });\n } finally {\n this.refocusing = false;\n }\n }\n }\n\n /** True while an editable in the runtime has focus. */\n get active(): boolean {\n return this.state !== null;\n }\n\n /**\n * The hidden element editing goes through: the textarea, or the\n * password input while a secret field has focus. For tests.\n */\n get element(): HTMLTextAreaElement | HTMLInputElement {\n return this.field;\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.field) {\n this.field.blur();\n this.canvas.focus({ preventScroll: true });\n }\n if (this.password !== null) {\n this.password.value = '';\n }\n }\n return;\n }\n const wanted = state.secret === true ? this.passwordField() : this.textarea;\n if (wanted !== this.field) {\n this.swapTo(wanted);\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.field.setAttribute('enterkeyhint', state.multiline ? 'enter' : 'done');\n if (!wasActive || this.doc.activeElement !== this.field) {\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 || this.owns(active)) {\n this.field.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.field) {\n return;\n }\n this.field.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 field = this.field;\n if (this.doc.activeElement !== field) {\n field.focus({ preventScroll: true });\n return;\n }\n this.refocusing = true;\n try {\n field.blur();\n field.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 * Nothing of the record's value is written: the element's own value\n * is the field's text (a secret field's mask), and that is what a\n * screen reader reads.\n */\n describe(record: UiSemanticsRecord | null, activeDescendant?: string, controls?: string): void {\n if (this.disposed) {\n return;\n }\n this.described = [record, activeDescendant, controls];\n this.write(this.field, record, activeDescendant, controls);\n }\n\n private write(\n textarea: HTMLTextAreaElement | HTMLInputElement,\n record: UiSemanticsRecord | null,\n activeDescendant?: string,\n controls?: string\n ): void {\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 'aria-controls',\n 'aria-autocomplete'\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,\n // which list it is, that it suggests, and which option the\n // 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 setOrClear(textarea, 'aria-controls', controls);\n setOrClear(textarea, 'aria-autocomplete', record.autocomplete);\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 this.password?.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.field.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.field;\n if (textarea.value !== state.text) {\n textarea.value = state.text;\n }\n const direction = state.selectionDirection ?? 'forward';\n if (\n textarea.selectionStart !== state.selectionStart ||\n textarea.selectionEnd !== state.selectionEnd ||\n (state.selectionEnd > state.selectionStart && textarea.selectionDirection !== direction)\n ) {\n try {\n textarea.setSelectionRange(state.selectionStart, state.selectionEnd, direction);\n } catch {\n // Some engines throw for an offset past the value; the next\n // state will be consistent.\n }\n }\n }\n\n /** Listens to one of the two elements; each has the same listeners. */\n private listen(textarea: HTMLTextAreaElement | HTMLInputElement): () => void {\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 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 if (state.secret === true) {\n // A secret field gives the clipboard nothing, and leaves what\n // was on it there, as a password input does.\n return;\n }\n // A surface may say what a copy takes (its whole line, with\n // nothing selected; several selections joined); else the\n // selected text.\n clipboard.setData('text/plain', state.clipboard ?? 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 (\n state !== null &&\n state.secret !== true &&\n (state.clipboard !== undefined ? state.clipboard.length > 0 : state.selectionEnd > state.selectionStart)\n ) {\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 && !this.owns(related)) {\n this.sink.blur();\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', this.onPaste);\n textarea.addEventListener('copy', onCopy);\n textarea.addEventListener('cut', onCut);\n textarea.addEventListener('keydown', onKeyDown);\n textarea.addEventListener('keyup', onKeyUp);\n textarea.addEventListener('blur', onBlur);\n\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', this.onPaste);\n textarea.removeEventListener('copy', onCopy);\n textarea.removeEventListener('cut', onCut);\n textarea.removeEventListener('keydown', onKeyDown);\n textarea.removeEventListener('keyup', onKeyUp);\n textarea.removeEventListener('blur', onBlur);\n };\n }\n\n private readonly 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 private listenWindow(): () => void {\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 on the field's element 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.owns(this.doc.activeElement)) {\n return;\n }\n this.onPaste(event);\n };\n\n const onReposition = (): void => {\n if (this.state !== null) {\n this.position(this.state);\n }\n };\n this.view?.addEventListener('scroll', onReposition, { capture: true, passive: true });\n this.view?.addEventListener('resize', onReposition);\n this.canvas.addEventListener('paste', onCanvasPaste);\n return () => {\n this.canvas.removeEventListener('paste', onCanvasPaste);\n this.view?.removeEventListener('scroll', onReposition, { capture: true });\n this.view?.removeEventListener('resize', onReposition);\n };\n }\n}\n\n/**\n * Reads the system clipboard's text from the main thread; null where\n * the browser has no async clipboard or refuses (no secure context, no\n * focus, or the person said no to its prompt). It never rejects.\n */\nexport function readClipboard(): Promise<string | null> {\n const clipboard = typeof navigator !== 'undefined' ? navigator.clipboard : undefined;\n if (clipboard === undefined || typeof clipboard.readText !== 'function') {\n return Promise.resolve(null);\n }\n return clipboard.readText().then(\n text => text,\n () => null\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` and `controls` are the DOM ids of the elements\n * its record's `activeDescendant` and `controls` name, for\n * `aria-activedescendant` and `aria-controls`.\n */\n describe(record: UiSemanticsRecord | null, activeDescendant?: string, controls?: 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-rowindex',\n 'aria-colindex',\n 'aria-rowcount',\n 'aria-colcount',\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 'aria-controls',\n 'aria-autocomplete',\n 'aria-orientation'\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 the DOM ids of its active descendant and what it controls. */\n private describeEditing(record: UiSemanticsRecord | null): void {\n const active = record?.activeDescendant;\n const controls = record?.controls;\n this.editing?.describe(\n record,\n active === undefined ? undefined : this.idPrefix + active,\n controls === undefined ? undefined : this.idPrefix + controls\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 // `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 setNumber(element, 'aria-rowindex', record.rowIndex);\n setNumber(element, 'aria-colindex', record.columnIndex);\n setNumber(element, 'aria-rowcount', record.rowCount);\n setNumber(element, 'aria-colcount', record.columnCount);\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 // A relation, like the active descendant: the id of another record's element.\n if (record.controls !== undefined) {\n element.setAttribute('aria-controls', this.idPrefix + record.controls);\n }\n if (record.orientation !== undefined) {\n element.setAttribute('aria-orientation', record.orientation);\n }\n if (record.autocomplete !== undefined) {\n element.setAttribute('aria-autocomplete', record.autocomplete);\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 // Its own words, written before its children arrived, are its first\n // text node: kept in step, or a label that changed went on being read\n // as the first one. A list's header, a row with no role, said\n // \"Changes, 0 files\" over eighteen while drawing 18.\n const own = element.firstChild;\n if (own !== null && own.nodeType === TEXT_NODE && own.nodeValue !== text) {\n own.nodeValue = text;\n }\n return;\n }\n if (element.textContent !== text) {\n element.textContent = text;\n }\n}\n\n/** `Node.TEXT_NODE`, which a worker-free page has but a spec's fake document does not. */\nconst TEXT_NODE = 3;\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 type { UiPointer } from 'gesso-core';\n\nimport { observeMediaQuery } from './mediaQuery';\n\n/**\n * Watches what the person points with: `coarse` when the primary\n * pointer is a finger (CSS's `pointer: coarse`), `fine` for a mouse or a\n * trackpad. Shell-side, reported once at once and again on change, for\n * the reason `observeColorScheme` gives; the runtime provides it at the\n * root, where it turns every theme for a finger (`withPointer`).\n */\nexport function observePointer(onChange: (pointer: UiPointer) => void): () => void {\n return observeMediaQuery('(pointer: coarse)', matches => onChange(matches ? 'coarse' : 'fine'));\n}\n","/**\n * Watches whether the window is the one the person is using: shown, and\n * holding the keyboard focus. A window behind another, minimised, or in\n * a background tab is not.\n *\n * Shell-side, reported once at once and again on change, for the reason\n * `observeColorScheme` gives: a window that starts in the background\n * fires nothing until it is brought forward.\n */\nexport function observeWindowActive(onChange: (active: boolean) => void): () => void {\n let last: boolean | null = null;\n const report = (): void => {\n // A document that cannot say where the focus is (some embedded webviews, a test's stand-in) is taken to have it.\n const focused = typeof document.hasFocus === 'function' ? document.hasFocus() : true;\n const active = document.visibilityState === 'visible' && focused;\n if (active !== last) {\n last = active;\n onChange(active);\n }\n };\n window.addEventListener('focus', report);\n window.addEventListener('blur', report);\n document.addEventListener('visibilitychange', report);\n report();\n return () => {\n window.removeEventListener('focus', report);\n window.removeEventListener('blur', report);\n document.removeEventListener('visibilitychange', report);\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\n/**\n * A url kept somewhere, which the shell reads, writes and listens to.\n *\n * `createShellHistory` makes one from `ShellHistoryOptions`, and that\n * covers every window the app owns the address of. An app embedded in\n * a page whose address belongs to something else does not own it: an\n * Atlassian Forge Custom UI app runs in an iframe inside Jira, and the\n * only way to Jira's address bar is the history object Forge's bridge\n * hands out. Such a host implements this interface over whatever it\n * has, and passes the object as the app's `history` option in place of\n * options, so the router and the host's address bar stay one url.\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 /**\n * Reports urls the person produced: back, forward, or a typed address.\n *\n * There is one listener, and a second call replaces the first. An\n * app that was handed its history and is being taken down relies on\n * that to stop listening, since disposing a history it did not make\n * is not its decision.\n */\n onChange(listener: (url: string) => void): void;\n dispose(): void;\n /**\n * The address a new tab would open to show the app at `url`, or null\n * when this history has none to give.\n *\n * Asked when a link is followed somewhere new (`ShellService.openRoute`,\n * a Cmd-click or a Ctrl-click). The browser modes answer with the\n * document address that holds `url`, which is what `push` would have\n * written; `memory` answers null, because a url kept in memory names\n * no page anything else could load. Optional, because a history an\n * embedder wrote may have no such answer either, and leaving it out\n * says exactly that: the shell then follows the link in place. A\n * host that can open its own kind of tab (Jira's, through Forge's\n * `router.open`) says so with the shell's `onOpenRoute` instead.\n */\n href?(url: string): string | null;\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\n/**\n * The history an app runs against, and whether the app may dispose it.\n *\n * Options are turned into a history here, and that history is the\n * app's to dispose with everything else it made. A history passed in\n * ready-made is used as it is and left alone at the end, by the rule\n * `appLogicWorker` already follows: what the app made, it disposes;\n * what it was handed, it leaves to whoever handed it. That is also\n * what keeps a host's history alive across a remount, which disposes\n * the app and builds it again around the same object.\n */\nexport function resolveShellHistory(source: ShellHistoryOptions | ShellHistory | undefined): {\n readonly history: ShellHistory;\n readonly owned: boolean;\n} {\n if (isShellHistory(source)) {\n return { history: source, owned: false };\n }\n return { history: createShellHistory(source), owned: true };\n}\n\n/**\n * Tells a ready-made history from options by its methods.\n *\n * Options are plain data and never carry a function, so the presence of\n * `push` and `onChange` as functions is unambiguous. It is checked by\n * behaviour rather than by `instanceof` because the histories worth\n * passing are adapters an embedder wrote, not classes from here.\n */\nfunction isShellHistory(value: ShellHistoryOptions | ShellHistory | undefined): value is ShellHistory {\n return (\n value !== undefined &&\n typeof (value as Partial<ShellHistory>).push === 'function' &&\n typeof (value as Partial<ShellHistory>).onChange === 'function'\n );\n}\n\n/**\n * Opens the app at `url` somewhere new, as the shell's answer to\n * `ShellService.openRoute`.\n *\n * In this order, and the order is the decision. A host's own handler\n * wins, because an app embedded in another product or in a desktop\n * window knows what a new tab means there and a browser default would\n * be wrong in both. Without one, the history is asked for the address\n * that holds `url`, and that address is opened. Without an address,\n * which is `memory` mode and any handed-in history with no `href`, the\n * link is followed in place: a Cmd-click that did nothing at all would\n * read as a broken link, and arriving at the destination in the window\n * already open is what a browser does with a modifier it cannot honour.\n *\n * Shared by `WorkerApp` and `GessoApp`, which differ only in how a url\n * reaches the router, so it is passed in.\n */\nexport function openRouteWith(\n handler: ((url: string) => void) | undefined,\n url: string,\n history: ShellHistory | null,\n open: (address: string) => void,\n followInPlace: (url: string) => void\n): void {\n if (handler !== undefined) {\n handler(url);\n return;\n }\n const address = history?.href?.(url) ?? null;\n if (address !== null) {\n open(address);\n return;\n }\n followInPlace(url);\n}\n\n/**\n * Stops an app hearing about a history it is done with.\n *\n * A history the app made is disposed, which drops its listeners and\n * its window events with it. One it was handed is not the app's to\n * dispose, so the listener is replaced with one that does nothing:\n * the host's history goes on working, and a back button pressed after\n * the app is gone no longer reaches it.\n */\nexport function releaseShellHistory(history: ShellHistory, owned: boolean): void {\n if (owned) {\n history.dispose();\n } else {\n history.onChange(() => {});\n }\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 /** No address: a url kept here names no page another tab could load. */\n href(): string | null {\n return 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 /**\n * The address `push` would write for `url`, for a new tab to open.\n *\n * Relative, as `pushState` takes it, and resolved against the page by\n * whatever opens it: `/epic/2` in `path` mode, and the page's own\n * path and query with the fragment set in `hash` mode, so the new tab\n * loads the same document and starts on the same screen.\n */\n href(url: string): string | null {\n return this.toHref(url);\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","/**\n * Hears the window's device pixel ratio change while its size does not.\n *\n * A window dragged from a laptop's screen to an external monitor keeps\n * its size in CSS pixels, so no `ResizeObserver` fires, but each CSS\n * pixel is now a different number of device pixels. A canvas sized for\n * the old ratio is drawn blurred, or scaled, until something else\n * happens to resize it. The browser says when the ratio changes only\n * through a media query for the ratio it had, which stops matching; so\n * each change is heard once and the query is made again for the new\n * ratio.\n *\n * @returns A function that stops listening.\n */\nexport function watchPixelRatio(\n onChange: (dpr: number) => void,\n view: Pick<Window, 'matchMedia' | 'devicePixelRatio'> | undefined = globalThis.window\n): () => void {\n if (view === undefined || typeof view.matchMedia !== 'function') {\n return () => {};\n }\n let query: MediaQueryList | null = null;\n let stopped = false;\n const listen = (): void => {\n query = view.matchMedia(`(resolution: ${view.devicePixelRatio || 1}dppx)`);\n query.addEventListener('change', changed, { once: true });\n };\n function changed(): void {\n if (stopped) {\n return;\n }\n onChange(view!.devicePixelRatio || 1);\n listen();\n }\n listen();\n return () => {\n stopped = true;\n query?.removeEventListener('change', changed);\n };\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 isImeKey,\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 { setLeaveWarning } from '../shellLeaveWarning';\nimport { redirectWith } from '../shellRedirect';\nimport { portHandle, type WorkerHandle } from '../../worker/WorkerPorts';\nimport { EditingProxy, readClipboard, writeClipboard } from '../EditingProxy';\nimport { SemanticsMirror } from '../SemanticsMirror';\nimport { performShellStorage } from '../shellStorage';\nimport { observeColorScheme, type ColorSchemePreference } from '../colorScheme';\nimport { observeContrast } from '../contrast';\nimport { observePointer } from '../pointer';\nimport { observeWindowActive } from '../windowActive';\nimport { observeReducedMotion } from '../reducedMotion';\nimport {\n openRouteWith,\n releaseShellHistory,\n resolveShellHistory,\n type ShellHistory,\n type ShellHistoryOptions\n} from '../shellHistory';\nimport { afterLayout, isDocumentFullscreen, observeFullscreen, setElementFullscreen, surfaceBox } from '../fullscreen';\nimport { watchPixelRatio } from '../pixelRatio';\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 * Opens the app at one of its own urls somewhere new\n * (`ShellService.openRoute`, a Cmd-click or a Ctrl-click on an in-app\n * `Link`), in place of a new browser tab.\n *\n * `url` is the router's, `/epic/BUD-12?story=BUD-13`, not an address.\n * Without this the shell opens a new tab at the address its history\n * gives for it (`ShellHistory.href`): the origin and the path in\n * `path` mode, the page with that fragment in `hash` mode. With no\n * address to give, in `memory` mode or with a history that has no\n * `href`, it follows the url in place.\n *\n * A host whose idea of a new tab is not the browser's goes here: an\n * app embedded in another product opens one of that product's tabs\n * (an Atlassian Forge app, Forge's `router.open`), and a desktop\n * window asks for another window (`gesso-electrobun`'s bridge,\n * `openRoute`).\n */\n onOpenRoute?: (url: string) => void;\n /**\n * Replaces the page with a url the application asked for\n * (`ShellService.redirect`), in place of `window.location.assign`.\n *\n * A page wants `location.assign`, which is the default, and it is\n * usually right in a desktop window too: the webview navigates, the\n * way a page would, and comes back when the server redirects it back.\n * A host that has to do something else — reload the bundled app\n * rather than leave it, or send a sign-in to the person's browser and\n * wait for a deep link — handles it here.\n *\n * Called with the url resolved against the page, and only once the\n * shell has admitted it: a `javascript:` url, or any scheme that is\n * not http, https or the page's own, is refused before this is\n * consulted, so a handler need not check again.\n */\n onRedirect?: (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 and keyup that reaches the shell, so it\n * should be a comparison and nothing more. The keyup matters for a key\n * whose default comes on release: a lone Alt opens a Windows browser's\n * menu when it comes up, so a `MenuBar` that answers to Alt claims\n * both.\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 * Or a `ShellHistory` made elsewhere, for a page whose address the\n * app does not own. An app embedded in another product — an\n * Atlassian Forge Custom UI app is an iframe inside Jira — reaches\n * the address bar only through the history object its host hands\n * out, so it adapts that object to `ShellHistory` and passes it here,\n * and the router and the host's address stay one url. A history\n * passed this way is used as it is and survives the app: unmount\n * stops listening to it but does not dispose it, by the rule\n * `appLogicWorker` follows, and a remount picks the same one up.\n */\n history?: ShellHistoryOptions | ShellHistory;\n}\n\n/** Refreshes timed to tell the worker the display's interval. */\nconst DISPLAY_SAMPLES = 12;\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\n/**\n * What to say when the browser fails a worker at the worker object,\n * before the worker could say anything itself.\n *\n * Two different failures arrive this way. An exception thrown while the\n * worker's modules were evaluating is an `ErrorEvent` with a message,\n * and the message is the useful part. A script that never loaded at all\n * is a plain `Event` with no message and no filename, and the browser\n * says nothing more about it anywhere a script can read: the reason is\n * only in the network panel. That case used to be reported as \"failed\n * to start: undefined\", so it now says where to look and what the\n * usual reasons are. The last one is easy to hit: turn on\n * `Cross-Origin-Embedder-Policy` and a worker script the browser cached\n * before is refused, because a dev server's 304 does not repeat the\n * header.\n */\nexport function workerFailureMessage(worker: 'render' | 'app', event: Event): string {\n const name = worker === 'render' ? 'the render worker' : 'the app worker';\n if (hasMessage(event)) {\n const filename = (event as Partial<ErrorEvent>).filename;\n const where = filename === undefined || filename === '' ? '' : ` (${filename})`;\n return `${name} failed to start: ${(event as ErrorEvent).message}${where}`;\n }\n return (\n `${name} failed to start: its script did not load. The browser's network panel has the reason; ` +\n 'usually the file is missing (404), is served with a MIME type that is not JavaScript, or was refused ' +\n \"under the page's Cross-Origin-Embedder-Policy (including a copy cached before the policy was turned on; \" +\n 'a hard reload clears it).'\n );\n}\n\nfunction hasMessage(event: Event): boolean {\n const message = (event as Partial<ErrorEvent>).message;\n return typeof message === 'string' && message !== '';\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 /** The host's last size, for a pixel ratio that changes on its own; see `watchPixelRatio`. */\n private hostSize: { width: number; height: number } | null = null;\n private stopPixelRatio: (() => void) | 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 only when the history was made here from options; see `resolveShellHistory`. */\n private ownsHistory = false;\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 // Before `init`, which carries the address; see `attachHistory`.\n const url = this.attachHistory();\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 if (resolved.owned) {\n (application as Worker).addEventListener('error', this.handleAppWorkerFailure);\n }\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 url\n } as ShellToRuntimeMessage,\n transfer\n );\n\n this.observeResize(element);\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: Event): void => {\n event.preventDefault();\n if (this.ready) {\n return;\n }\n this.report(workerFailureMessage('render', event), undefined, 'uncaught');\n };\n\n /**\n * The app worker's counterpart, for the one failure nothing else\n * reports: a script that never loaded. Without this, an app worker\n * blocked by a 404 or by the page's embedder policy left the screen\n * waiting on channels that would never arrive, and said nothing.\n *\n * An exception inside the app worker arrives here too, with a\n * message, and is left alone: the browser reports it from inside the\n * worker with its stack, which is better than anything said here.\n */\n private handleAppWorkerFailure = (event: Event): void => {\n if (hasMessage(event)) {\n return;\n }\n event.preventDefault();\n this.report(workerFailureMessage('app', event), 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 if (isImeKey(event)) {\n // The IME's, not the app's: see `isImeKey`.\n return;\n }\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 code: event.code,\n modifiers: modifiersFrom(event),\n at: epochFromEvent(event),\n textFollows: viaProxy\n });\n }\n\n private forwardKeyUp(event: KeyboardEvent): void {\n if (isImeKey(event)) {\n return;\n }\n this.flushPendingMove();\n if (this.options.interceptKey?.(event) === true) {\n event.preventDefault();\n }\n this.post({\n type: 'keyUp',\n key: event.key,\n code: event.code,\n modifiers: modifiersFrom(event),\n at: epochFromEvent(event)\n });\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 /**\n * Measures the display's interval on this thread's refreshes and tells\n * the worker, whose own may keep no display's time; see\n * `displayInterval` in the protocol. A dozen refreshes, once, at start.\n */\n private measureDisplay(): void {\n if (typeof requestAnimationFrame !== 'function') {\n return;\n }\n const times: number[] = [];\n const step = (time: number): void => {\n times.push(time);\n if (times.length < DISPLAY_SAMPLES) {\n requestAnimationFrame(step);\n return;\n }\n const gaps = times\n .slice(1)\n .map((value, index) => value - times[index]!)\n .sort((a, b) => a - b);\n this.post({ type: 'displayInterval', ms: gaps[Math.floor(gaps.length / 2)]!, origin: performance.timeOrigin });\n };\n requestAnimationFrame(step);\n }\n\n dispose(): void {\n this.disconnectWebMcp?.();\n this.disconnectWebMcp = null;\n // An app that is gone has no work to lose.\n if (typeof window !== 'undefined') {\n setLeaveWarning(window, false);\n }\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 if (this.history !== null) {\n releaseShellHistory(this.history, this.ownsHistory);\n }\n this.history = null;\n this.ownsHistory = false;\n this.detachInput?.();\n this.detachInput = null;\n this.audio?.dispose();\n this.audio = null;\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.stopPixelRatio?.();\n this.stopPixelRatio = 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)?.removeEventListener('error', this.handleAppWorkerFailure);\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 time: message.time,\n wheelScroll: message.wheelScroll,\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 this.measureDisplay();\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 === 'clipboardRead') {\n // Answered on every path: `readClipboard` never rejects.\n void readClipboard().then(text => this.post({ type: 'clipboardReadResult', id: message.id, text }));\n return;\n }\n if (message.type === 'openUrl') {\n openUrlWith(this.options.onOpenUrl, message.url);\n return;\n }\n if (message.type === 'openRoute') {\n openRouteWith(\n this.options.onOpenRoute,\n message.url,\n this.history,\n // `noopener` for the reason `openUrl` has it, and no\n // `noreferrer`: the tab is this application's own page, and\n // its server is owed the referrer as much as any of its pages.\n address => window.open(address, '_blank', 'noopener'),\n // No address to open, so the link is followed here: written to\n // the history as the router would have, and reported back as\n // the url the window is now at, which the router resolves.\n url => {\n this.history?.push(url);\n this.post({ type: 'url', url });\n }\n );\n return;\n }\n if (message.type === 'redirect') {\n redirectWith(this.options.onRedirect, message.url, window.location);\n return;\n }\n if (message.type === 'leaveWarning') {\n setLeaveWarning(window, message.on);\n return;\n }\n if (message.type === 'preferColorScheme') {\n this.setColorScheme(message.preference);\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 * Keeps the worker told of the window's address, and answers the one\n * it starts at.\n *\n * That first one is not posted from here: `mount` puts it on `init`,\n * because an app opened directly at a url must start on the screen\n * that url names. Posted after `init`, as it used to be, it reached a\n * render worker that had already built its tree on the root route\n * and run that screen's effects — an app that opened its last\n * document on `/` did so for a tab opened at `/d/new`.\n */\n private attachHistory(): string {\n const { history, owned } = resolveShellHistory(this.options.history);\n this.history = history;\n this.ownsHistory = owned;\n history.onChange(url => this.post({ type: 'url', url }));\n return 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.hostSize = { width, height };\n this.requestResize({ width, height, dpr: window.devicePixelRatio || 1 });\n }\n });\n this.resizeObserver.observe(element);\n // Moved to a display of another density: the same size, in more or fewer device pixels.\n this.stopPixelRatio = watchPixelRatio(dpr => {\n if (this.hostSize !== null && !this.fullscreen) {\n this.requestResize({ ...this.hostSize, dpr });\n }\n });\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 detachPointer = observePointer(pointer => this.post({ type: 'pointer', pointer }));\n const detachWindowActive = observeWindowActive(active => this.post({ type: 'windowActive', active }));\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 // A trackpad pinch, as WebKit reports it. Chromium and Firefox send\n // a pinch as a wheel with ctrlKey set, deltaY in hundredths of the\n // scale's natural log, and a canvas that zooms on the wheel takes\n // it with no more than that. WebKit sends gesture events instead,\n // and a web view that magnifies zooms the whole page on them, the\n // application's chrome along with the canvas. So the shell turns\n // each step of the gesture into the wheel a Chromium pinch would\n // have been, and keeps it from the page by the same rule as a\n // wheel: when the chain under the pointer would take that wheel.\n let pinchScale = 1;\n const onGestureStart = (event: Event): void => {\n pinchScale = 1;\n if (this.scrollability.up || this.scrollability.down) {\n event.preventDefault();\n }\n };\n const onGestureChange = (event: Event): void => {\n const pinch = event as PinchGestureEvent;\n const deltaY = pinchWheelDelta(pinchScale, pinch.scale);\n pinchScale = pinch.scale;\n if (deltaY === 0) {\n return;\n }\n this.flushPendingMove();\n if (wheelConsumedBy(this.scrollability, 0, deltaY)) {\n event.preventDefault();\n }\n const { x, y } = toLocal(pinch.clientX, pinch.clientY);\n this.post({\n type: 'wheel',\n x,\n y,\n deltaX: 0,\n deltaY,\n deltaMode: 0,\n modifiers: { ...modifiersFrom(pinch), ctrl: true },\n at: epochFromEvent(pinch)\n });\n };\n const onGestureEnd = (event: Event): void => {\n if (this.scrollability.up || this.scrollability.down) {\n event.preventDefault();\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('gesturestart', onGestureStart);\n canvas.addEventListener('gesturechange', onGestureChange);\n canvas.addEventListener('gestureend', onGestureEnd);\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 detachPointer();\n detachWindowActive();\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('gesturestart', onGestureStart);\n canvas.removeEventListener('gesturechange', onGestureChange);\n canvas.removeEventListener('gestureend', onGestureEnd);\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 * WebKit's `GestureEvent`, as far as a pinch uses it. Not in the DOM\n * typings, since no other engine has it.\n */\ninterface PinchGestureEvent extends Event {\n readonly scale: number;\n readonly clientX: number;\n readonly clientY: number;\n readonly shiftKey: boolean;\n readonly ctrlKey: boolean;\n readonly altKey: boolean;\n readonly metaKey: boolean;\n}\n\n/**\n * The wheel a Chromium pinch from one scale to the next would have\n * been: `deltaY` is -100·ln of the step, so spreading the fingers\n * scrolls up and zooms in, and a handler written for Chromium's pinch\n * takes WebKit's unchanged. Zero for a step that did not move, or a\n * scale that is not a scale.\n */\nexport function pinchWheelDelta(from: number, to: number): number {\n if (!(from > 0) || !(to > 0) || from === to) {\n return 0;\n }\n return -100 * Math.log(to / from);\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 { 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 { FrameService } from './FrameService';\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 { setLeaveWarning } from './shellLeaveWarning';\nimport { redirectWith } from './shellRedirect';\nimport { EditingProxy, readClipboard, 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 { observeWindowActive } from './windowActive';\nimport { afterLayout, isDocumentFullscreen, observeFullscreen, setElementFullscreen, surfaceBox } from './fullscreen';\nimport {\n openRouteWith,\n releaseShellHistory,\n resolveShellHistory,\n type ShellHistory,\n type ShellHistoryOptions\n} from './shellHistory';\nimport { measure } from './worker/WorkerApp';\nimport type { ChannelRegistry } from '../channel/ChannelRegistry';\nimport type { ServiceRegistry } from '../service/ServiceRegistry';\nimport { RouterService, type RouterRoutes } from '../router/RouterService';\nimport type { MediaOptions } from './MediaService';\nimport type { FontFamilyDeclaration } from './FontService';\nimport { renderModule } from '../agent/render';\nimport type { UiHost } from '../agent/ui';\nimport { watchPixelRatio } from './pixelRatio';\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 * Or a `ShellHistory` made elsewhere, for a page whose address the\n * app does not own: an iframe inside another product, where the\n * host's own history object is the only way to its address bar. It\n * is used as it is, and left undisposed when the app is disposed,\n * because it belongs to whoever passed it.\n */\n history?: ShellHistoryOptions | ShellHistory;\n /**\n * Opens the app at one of its own urls somewhere new\n * (`ShellService.openRoute`), in place of a new browser tab. The\n * same option, and the same fallbacks, as `WorkerAppOptions.onOpenRoute`.\n */\n onOpenRoute?: (url: string) => void;\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 onOpenRoute: ((url: string) => void) | undefined;\n\n private running = false;\n private resizeObserver: ResizeObserver | null = null;\n /** The host's last size, for a pixel ratio that changes on its own; see `watchPixelRatio`. */\n private hostSize: { width: number; height: number } | null = null;\n private stopPixelRatio: (() => void) | 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 /** True only when the history was made here from options; see `resolveShellHistory`. */\n private ownsHistory = false;\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 private detachWindowActive: (() => 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.onOpenRoute = options.onOpenRoute;\n this.colorSchemePreference = options.colorScheme ?? 'auto';\n // Here rather than at `mount`, because the runtime below builds its\n // tree at once and has to know the address to build the right one;\n // see `GessoRuntimeOptions.url`. Released in `dispose`, mounted or not.\n const { history, owned } = resolveShellHistory(options.history);\n this.history = history;\n this.ownsHistory = owned;\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 url: history.url,\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 this.runtime.services.get(FrameService).setFlush(() => this.flushFrame());\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 /** Runs a pending frame now: see `FrameService.flush`. */\n private flushFrame(): void {\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 * 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: () => this.flushFrame(),\n theme: at => runtime.themeAt(at),\n render: request =>\n renderModule(request, {\n load: path => import(/* @vite-ignore */ path),\n appTheme: () => runtime.themeAt(),\n channels: runtime.channels,\n services: runtime.services\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 // An app that is gone has no work to lose.\n if (isCanvasElement(this.canvas)) {\n setLeaveWarning(this.canvas.ownerDocument.defaultView, false);\n }\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.detachWindowActive?.();\n this.detachWindowActive = null;\n this.detachColorScheme?.();\n this.detachColorScheme = null;\n this.detachViewportInsets?.();\n this.detachViewportInsets = null;\n if (this.history !== null) {\n releaseShellHistory(this.history, this.ownsHistory);\n }\n this.history = null;\n this.ownsHistory = false;\n this.adapter.detach();\n this.resizeObserver?.disconnect();\n this.resizeObserver = null;\n this.stopPixelRatio?.();\n this.stopPixelRatio = 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.detachWindowActive = observeWindowActive(active => this.runtime.setWindowActive(active));\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 = this.history;\n if (history === null) {\n return;\n }\n this.runtime.onShellRequest(request => this.handleShellRequest(request, history));\n history.onChange(url => this.runtime.setUrl(url));\n // The runtime started at this address. Reported again only if it\n // moved between construction and mount, so a mount does not\n // republish a match nothing changed.\n if (history.url !== this.runtime.services.get(RouterService).url.value) {\n this.runtime.setUrl(history.url);\n }\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 (request.type === 'colorScheme') {\n // Before the canvas check too: the preference is the host's\n // switch, and an app with no window still reports what it was told.\n this.setColorScheme(request.preference);\n return;\n }\n if (request.type === 'openRoute') {\n // Before the canvas check: a host's handler needs no document,\n // and an app with no window to open a tab from still follows the\n // link in place rather than dropping it.\n const view = isCanvasElement(this.canvas) ? this.canvas.ownerDocument.defaultView : null;\n openRouteWith(\n this.onOpenRoute,\n request.url,\n view === null ? null : history,\n address => view?.open(address, '_blank', 'noopener'),\n url => {\n history.push(url);\n this.runtime.setUrl(url);\n }\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 } else if (request.type === 'clipboardRead') {\n this.runtime.settleClipboardRead(request.id, null);\n }\n return;\n }\n if (request.type === 'clipboardRead') {\n void readClipboard().then(text => this.runtime.settleClipboardRead(request.id, text));\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 if (request.type === 'leaveWarning') {\n setLeaveWarning(view, request.on);\n return;\n }\n if (request.type === 'redirect') {\n // The same rule the worker configuration's shell applies, and\n // through the same function: the scheme is checked before the\n // page is replaced.\n if (view !== null) {\n redirectWith(undefined, request.url, view.location);\n }\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.hostSize = { width: entry.contentRect.width, height: entry.contentRect.height };\n this.resize(entry.contentRect.width, entry.contentRect.height);\n });\n this.resizeObserver.observe(this.host);\n // Moved to a display of another density: the same size, in more or fewer device pixels.\n this.stopPixelRatio = watchPixelRatio(() => {\n if (this.hostSize !== null && !this.fullscreen) {\n this.resize(this.hostSize.width, this.hostSize.height);\n }\n });\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';\nimport type { AgentToolDefinition } from '../agent/tools';\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 { ShellHistory, 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 /** The application's own agent tools (`useAgentTools`). */\n private readonly agentTools: AgentToolDefinition[] = [];\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 | ShellHistory | 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 /** Offers an agent tools of the application's own; see `renderRoot(...).useAgentTools`. */\n useAgentTools(tools: readonly AgentToolDefinition[]): this {\n this.agentTools.push(...tools);\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 * Or a `ShellHistory` made elsewhere, for an app embedded in a page\n * whose address belongs to its host; see `GessoAppOptions.history`.\n */\n useHistory(history: ShellHistoryOptions | ShellHistory): 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 tools: () => this.agentTools\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 { renderModule } from '../../agent/render';\nimport type { AgentToolDefinition } from '../../agent/tools';\nimport type { DesignThemes } from '../../agent/designedTheme';\nimport { isClaimedError } from '../claimedErrors';\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 { FrameService } from '../FrameService';\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 {\n isDiscreteInputMessage,\n isInputMessage,\n type RuntimeToShellMessage,\n type ShellToRuntimeMessage,\n type RuntimeErrorSource\n} 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 */\n/** An error the render worker reported, as an application's `onError` hears it. */\nexport interface RenderWorkerError {\n readonly message: string;\n readonly stack?: string;\n /** Where it came from: a frame, a listener, the renderer, a channel, a message, or nothing that caught it. */\n readonly source: RuntimeErrorSource;\n}\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 /** See `renderThemesWith`. */\n private renderThemes: () => DesignThemes = () => ({});\n /** See `loadModulesWith`. */\n private loadModule: (path: string) => Promise<Record<string, unknown>> = path => import(/* @vite-ignore */ path);\n /** The application's own agent tools (`useAgentTools`). */\n private readonly agentTools: AgentToolDefinition[] = [];\n /** What the application asked to hear of the errors this worker reports (`onError`). */\n private readonly errorListeners: ((error: RenderWorkerError) => void)[] = [];\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 // A frame an agent asked for, failing; it tells the agent.\n if (isClaimedError(event.error)) {\n return;\n }\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 if (isClaimedError(event.reason)) {\n return;\n }\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.reportError(where === undefined ? message : `${message} (${where})`, error?.stack, 'uncaught');\n }\n\n /**\n * Tells the shell about an error, which shows it (the development\n * overlay), and every listener the application gave `onError`.\n */\n private reportError(message: string, stack: string | undefined, source: RuntimeErrorSource): void {\n this.host.postMessage({ type: 'error', message, ...(stack === undefined ? {} : { stack }), source });\n for (const listener of this.errorListeners) {\n try {\n listener({ message, ...(stack === undefined ? {} : { stack }), source });\n } catch {\n // A listener that throws is not reported through itself.\n }\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 * How `render_frame` loads a module an agent names, given its path from\n * the project root with a leading slash (`/mocks/Settings.tsx`). By\n * default it is imported from where the application is served, which\n * suits an application drawing its own modules. One whose modules are\n * served from somewhere else says where: a design editor serving the\n * project it designs from another folder.\n *\n * renderRoot(Editor).loadModulesWith(path => import(`/@fs${projectRoot}${path}`))\n */\n loadModulesWith(load: (path: string) => Promise<Record<string, unknown>>): this {\n this.loadModule = load;\n return this;\n }\n\n /**\n * The themes an application designs in, asked on each call, when they\n * are not the one it is drawn in: a design editor's project's. They\n * are what `render_frame` draws in and `tokens_view` reports unless an\n * agent asks for the application's own; see `designedTheme`.\n */\n renderThemesWith(themes: () => DesignThemes): this {\n this.renderThemes = themes;\n return this;\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 * Offers an agent tools of the application's own, beside its\n * channels' and its screen's: ones that answer with something other\n * than a view, such as a measurement or a composed picture. See\n * `AgentToolDefinition`. Called again, it adds to them.\n *\n * renderRoot(Editor).useAgentTools(editorTools);\n */\n useAgentTools(tools: readonly AgentToolDefinition[]): this {\n this.agentTools.push(...tools);\n return this;\n }\n\n /**\n * Hears every error this worker reports to the shell: a frame that\n * threw, a listener, the renderer, a channel, anything uncaught. For\n * an application that shows errors itself, where they happen: a design\n * editor listing what is wrong with a mock rather than an overlay over\n * the editor, with the plugin's `overlay: false`.\n */\n onError(listener: (error: RenderWorkerError) => void): this {\n this.errorListeners.push(listener);\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.reportError(\n error instanceof Error ? error.message : String(error),\n error instanceof Error ? error.stack : undefined,\n '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 tools: () => this.agentTools,\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?.flush(),\n theme: at => runtime.themeAt(at),\n designThemes: () => this.renderThemes(),\n render: request =>\n renderModule(request, {\n load: this.loadModule,\n themes: this.renderThemes,\n appTheme: () => runtime.themeAt(),\n channels: runtime.channels,\n services: runtime.services\n })\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 message.url\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 // The wheel's held step goes before any other input (see `GessoRuntime.wheel`).\n if (message.type !== 'wheel' && isInputMessage(message)) {\n runtime.releaseWheel();\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.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, message.code);\n break;\n case 'keyUp':\n runtime.input.keyboard.keyUp(message.key, message.modifiers, message.code);\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 'pointer':\n runtime.setPointer(message.pointer);\n break;\n case 'contrast':\n runtime.setContrast(message.contrast);\n break;\n case 'windowActive':\n runtime.setWindowActive(message.active);\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 'clipboardReadResult':\n runtime.settleClipboardRead(message.id, message.text);\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 'displayInterval':\n this.clock?.displayInterval(message.ms, message.origin);\n break;\n case 'fullscreenChanged':\n runtime.setFullscreen(message.active);\n return;\n case 'visibility':\n this.clock?.setHidden(!message.visible);\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 if (isDiscreteInputMessage(message)) {\n this.runtime.answerInput();\n }\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 url: string | undefined\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.reportError(`channel ${channelName}: ${message}`, stack, 'channel');\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 url,\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.services.get(FrameService).setFlush(() => this.clock?.flush());\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 === 'clipboardRead') {\n this.host.postMessage({ type: 'clipboardRead', id: request.id });\n } else if (request.type === 'openUrl') {\n this.host.postMessage({ type: 'openUrl', url: request.url });\n } else if (request.type === 'openRoute') {\n this.host.postMessage({ type: 'openRoute', url: request.url });\n } else if (request.type === 'redirect') {\n this.host.postMessage({ type: 'redirect', url: request.url });\n } else if (request.type === 'fullscreen') {\n this.host.postMessage({ type: 'fullscreen', enter: request.enter });\n } else if (request.type === 'leaveWarning') {\n this.host.postMessage({ type: 'leaveWarning', on: request.on });\n } else if (request.type === 'colorScheme') {\n this.host.postMessage({ type: 'preferColorScheme', preference: request.preference });\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.reportError(message, undefined, 'renderer');\n });\n this.runtime.onListenerError((message, stack) => {\n this.reportError(message, stack, '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.reportError(message, stack, '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 time: metrics.time,\n wheelScroll: metrics.wheelScroll,\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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"}
|