gesso-devtools 0.3.0 → 0.4.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 +25 -0
- package/dist/index.d.ts +140 -2
- package/dist/index.js +264 -11
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["STYLES","STYLES","STYLES","STYLES"],"sources":["../src/codeFrame.ts","../src/sourceMap.ts","../src/stackTrace.ts","../src/ErrorOverlay.ts","../src/ActionLog.ts","../src/ActionLogPanel.ts","../src/RenderWorkerTap.ts","../src/NodePicker.ts","../src/NodeReportView.ts","../src/PanelProtocol.ts","../src/DevtoolsHook.ts","../src/FrameProfiler.ts","../src/TreeRows.ts","../src/DevtoolsPanel.ts","../src/NodeInspector.ts"],"sourcesContent":["/** One line of a code frame, with the offending one marked. */\nexport interface CodeFrameLine {\n number: number;\n text: string;\n /** True for the line the error was reported on. */\n target: boolean;\n}\n\n/** A few lines of original source around a mapped position. */\nexport interface CodeFrame {\n lines: CodeFrameLine[];\n /** 1-based column on the target line, for the caret under it. */\n column: number;\n}\n\n/**\n * Cuts `context` lines either side of a position out of a source file.\n *\n * This is the part of an overlay that a stack trace cannot replace: a\n * file and a line number send a person to their editor, while the line\n * itself is often the whole answer — a `.length` on something that is\n * undefined reads as the bug the moment it is on screen.\n *\n * Tabs are expanded to two spaces so the caret column below the line\n * lands where the character does. Nothing else is transformed; the text\n * reaches the DOM as text, never as markup.\n */\nexport function codeFrame(source: string, line: number, column: number, context = 2): CodeFrame | null {\n const lines = source.split('\\n');\n if (line < 1 || line > lines.length) {\n return null;\n }\n const first = Math.max(1, line - context);\n const last = Math.min(lines.length, line + context);\n const out: CodeFrameLine[] = [];\n for (let number = first; number <= last; number++) {\n out.push({ number, text: expandTabs(lines[number - 1]), target: number === line });\n }\n return { lines: out, column: expandedColumn(lines[line - 1], column) };\n}\n\nconst TAB_WIDTH = 2;\n\nfunction expandTabs(text: string): string {\n return text.replace(/\\t/g, ' '.repeat(TAB_WIDTH));\n}\n\n/** Where a column lands once the tabs before it have been expanded. */\nfunction expandedColumn(text: string, column: number): number {\n let expanded = 1;\n for (let i = 0; i < Math.min(column - 1, text.length); i++) {\n expanded += text[i] === '\\t' ? TAB_WIDTH : 1;\n }\n return expanded;\n}\n","/**\n * Enough of Source Map v3 to turn a compiled stack frame back into the\n * line somebody wrote.\n *\n * Written here rather than taken from npm for the reason the rest of\n * the repository's tooling gives (`scripts/gen-layout-fixtures.ts`,\n * `scripts/check-webgpu-parity.ts`): the whole of what this package\n * needs is a VLQ decoder and a binary search, and a dependency that\n * ships a `SourceMapConsumer` with a WASM payload is a heavier thing to\n * put in front of a developer trying to read an error than the error\n * was.\n *\n * Two deliberate omissions. Index maps (`sections`) are not read — no\n * bundler this project builds with emits one. Names are not read\n * either: a stack frame already carries the function name the engine\n * knew, and the mapped name is only occasionally better.\n */\n\n/** A source map as a bundler writes it. */\nexport interface SourceMapV3 {\n version: number;\n file?: string;\n sourceRoot?: string;\n sources: (string | null)[];\n sourcesContent?: (string | null)[];\n names?: string[];\n mappings: string;\n}\n\n/** Where a generated position came from, with 1-based line and column. */\nexport interface OriginalPosition {\n /** The source as the map names it, with `sourceRoot` already applied. */\n source: string;\n line: number;\n column: number;\n /**\n * The original text of the source, when the map inlined it.\n *\n * Carried on the position rather than fetched from the consumer\n * afterwards because a chained lookup ends in a consumer the caller\n * never sees: the map that knows the text is the last one in the\n * chain, not the one the caller started from. See\n * `SourceMapStore.originalFor`.\n */\n content?: string | null;\n}\n\n/**\n * One decoded mapping segment.\n *\n * Held as a flat tuple rather than an object because a map for a\n * medium application has hundreds of thousands of them, and they exist\n * only to be searched.\n */\ntype Segment = [generatedColumn: number, sourceIndex: number, sourceLine: number, sourceColumn: number];\n\nconst BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/** Character code → 6-bit value, with -1 for anything not base64. */\nconst BASE64_VALUE: Int8Array = (() => {\n const table = new Int8Array(128).fill(-1);\n for (let i = 0; i < BASE64.length; i++) {\n table[BASE64.charCodeAt(i)] = i;\n }\n return table;\n})();\n\n/**\n * Decodes the `mappings` string into one array of segments per\n * generated line.\n *\n * Segments carrying only a generated column — a run of output with no\n * original position, which is what a bundler emits for code it\n * synthesised — are dropped rather than kept with nulls. A lookup that\n * lands in one should fall back to the nearest earlier real mapping,\n * and dropping them is how that happens without a second case.\n */\nexport function decodeMappings(mappings: string): Segment[][] {\n const lines: Segment[][] = [];\n let line: Segment[] = [];\n let sourceIndex = 0;\n let sourceLine = 0;\n let sourceColumn = 0;\n let generatedColumn = 0;\n const fields: number[] = [];\n let index = 0;\n\n while (index < mappings.length) {\n const char = mappings[index];\n if (char === ';') {\n lines.push(line);\n line = [];\n generatedColumn = 0;\n index++;\n continue;\n }\n if (char === ',') {\n index++;\n continue;\n }\n\n fields.length = 0;\n while (index < mappings.length && mappings[index] !== ',' && mappings[index] !== ';') {\n let value = 0;\n let shift = 1;\n let digit: number;\n do {\n const code = mappings.charCodeAt(index);\n digit = code < 128 ? BASE64_VALUE[code] : -1;\n if (digit < 0) {\n // A malformed map is not worth a thrown error inside an error\n // reporter: the caller falls back to the raw stack.\n return lines;\n }\n index++;\n value += (digit & 31) * shift;\n shift *= 32;\n } while ((digit & 32) !== 0);\n fields.push(value % 2 === 1 ? -Math.floor(value / 2) : Math.floor(value / 2));\n }\n\n generatedColumn += fields[0];\n if (fields.length >= 4) {\n sourceIndex += fields[1];\n sourceLine += fields[2];\n sourceColumn += fields[3];\n line.push([generatedColumn, sourceIndex, sourceLine, sourceColumn]);\n }\n }\n lines.push(line);\n return lines;\n}\n\n/**\n * A decoded map, answering \"which line of which source is this?\".\n *\n * Decoding is done once in the constructor because a page that shows\n * one error usually shows several from the same file, and each of them\n * asks about a handful of positions.\n */\nexport class SourceMapConsumer {\n private readonly lines: Segment[][];\n private readonly sources: string[];\n private readonly contents: (string | null)[];\n\n constructor(map: SourceMapV3) {\n this.lines = decodeMappings(map.mappings);\n const root = map.sourceRoot ?? '';\n this.sources = map.sources.map(source => joinSourceRoot(root, source ?? '<unknown>'));\n this.contents = map.sourcesContent ?? [];\n }\n\n /**\n * Maps a generated position — 1-based line and column, as every\n * engine writes them in a stack — back to an original one.\n *\n * Returns the last mapping at or before the column, which is the\n * definition of a source map's coverage: a mapping holds until the\n * next one starts. Null when the line has no mappings at all.\n */\n lookup(line: number, column: number): OriginalPosition | null {\n const segments = this.lines[line - 1];\n if (segments === undefined || segments.length === 0) {\n return null;\n }\n const target = column - 1;\n let low = 0;\n let high = segments.length - 1;\n let found = -1;\n while (low <= high) {\n const middle = (low + high) >> 1;\n if (segments[middle][0] <= target) {\n found = middle;\n low = middle + 1;\n } else {\n high = middle - 1;\n }\n }\n // Before the first mapping on the line: the first one is still the\n // best answer available, and it is the right one for a frame whose\n // column the engine reported at the statement rather than the call.\n const segment = segments[found === -1 ? 0 : found];\n const source = this.sources[segment[1]];\n if (source === undefined) {\n return null;\n }\n return {\n source,\n line: segment[2] + 1,\n column: segment[3] + 1,\n content: this.contents[segment[1]] ?? null\n };\n }\n\n /** The original text of a source, when the map inlined it. */\n contentFor(source: string): string | null {\n const index = this.sources.indexOf(source);\n if (index === -1) {\n return null;\n }\n return this.contents[index] ?? null;\n }\n}\n\n/**\n * The `sourceMappingURL` a script declares, or null.\n *\n * The last one wins, and the search is a `lastIndexOf` over the whole\n * text rather than a regex over the tail: an inlined map is a single\n * comment megabytes long, so \"near the end\" is not where its opening\n * is.\n */\nexport function parseSourceMappingUrl(script: string): string | null {\n const marker = '//# sourceMappingURL=';\n const start = script.lastIndexOf(marker);\n if (start === -1) {\n return null;\n }\n const from = start + marker.length;\n const end = script.indexOf('\\n', from);\n const url = (end === -1 ? script.slice(from) : script.slice(from, end)).trim();\n return url === '' ? null : url;\n}\n\n/** Reads a `data:` URL's payload, base64 or percent-encoded. */\nfunction decodeDataUrl(url: string): string | null {\n const comma = url.indexOf(',');\n if (comma === -1) {\n return null;\n }\n const meta = url.slice(0, comma);\n const payload = url.slice(comma + 1);\n if (!meta.includes(';base64')) {\n return decodeURIComponent(payload);\n }\n const binary = atob(payload);\n // The map is JSON, and JSON is UTF-8: `atob` gives bytes, and a\n // source path with a non-ASCII character would otherwise come out\n // mojibake.\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/** Applies `sourceRoot`, which is a prefix and not a URL join. */\nfunction joinSourceRoot(root: string, source: string): string {\n if (root === '' || /^[a-z]+:\\/\\//i.test(source)) {\n return source;\n }\n return root.endsWith('/') ? `${root}${source}` : `${root}/${source}`;\n}\n\n/**\n * Fetches and caches the map for each script a stack mentions.\n *\n * Every method resolves rather than rejects: this runs while\n * something has already gone wrong, and an overlay that throws while\n * explaining a throw is worse than an overlay showing a raw stack.\n * A script with no map, a map that 404s and a map that is not JSON all\n * come back as null, and the caller shows what the engine gave it.\n */\nexport class SourceMapStore {\n private readonly cache = new Map<string, Promise<SourceMapConsumer | null>>();\n private readonly load: (url: string) => Promise<string>;\n\n constructor(load: (url: string) => Promise<string> = defaultLoad) {\n this.load = load;\n }\n\n /** The consumer for a script URL, fetched at most once per store. */\n consumerFor(scriptUrl: string): Promise<SourceMapConsumer | null> {\n const cached = this.cache.get(scriptUrl);\n if (cached !== undefined) {\n return cached;\n }\n const pending = this.resolve(scriptUrl).catch(() => null);\n this.cache.set(scriptUrl, pending);\n return pending;\n }\n\n /**\n * Where a position in a script was written, following the chain of\n * maps as far as it goes.\n *\n * One lookup is not enough, and the reason took a browser to find. A\n * frame inside `gesso-framework` names Vite's optimised dependency\n * bundle; that bundle's map points at the package's own\n * `dist/index.js`, because the optimiser does not chain to the map\n * the package ships; and it is the package's map that knows about\n * `src/app/worker/RenderWorkerApp.ts`. Stopping after one step gave\n * a frame in a bundled file with a five-figure line number, which is\n * exactly the thing shipping the maps was meant to prevent.\n *\n * So each answer is resolved against the map that gave it and asked\n * again, until a source has no map of its own — which is the source\n * somebody wrote. `depth` is a guard against a map that names\n * itself; four is more levels than any real toolchain stacks.\n */\n async originalFor(scriptUrl: string, line: number, column: number, depth = 4): Promise<OriginalPosition | null> {\n let url = scriptUrl;\n let position: OriginalPosition | null = null;\n for (let step = 0; step < depth; step++) {\n const consumer = await this.consumerFor(url);\n const next = consumer?.lookup(line, column) ?? null;\n if (next === null) {\n return position;\n }\n const resolved = absolute(next.source, url);\n position = { ...next, source: resolved };\n if (resolved === url) {\n return position;\n }\n url = resolved;\n line = next.line;\n column = next.column;\n }\n return position;\n }\n\n private async resolve(scriptUrl: string): Promise<SourceMapConsumer | null> {\n const script = await this.load(scriptUrl);\n const mapUrl = parseSourceMappingUrl(script);\n if (mapUrl === null) {\n return null;\n }\n const json = mapUrl.startsWith('data:')\n ? decodeDataUrl(mapUrl)\n : await this.load(new URL(mapUrl, scriptUrl).toString());\n if (json === null) {\n return null;\n }\n const map = JSON.parse(json) as SourceMapV3;\n if (typeof map.mappings !== 'string' || !Array.isArray(map.sources)) {\n return null;\n }\n return new SourceMapConsumer(map);\n }\n}\n\n/**\n * A source name resolved against the script whose map named it.\n *\n * A map's `sources` are relative to the map, and the map is beside the\n * script often enough that the script is the right base. When it is\n * not a URL at all — a webpack `webpack://` name, an absolute path —\n * `new URL` throws and the name is kept as it was, which still reads\n * correctly and simply ends the chain.\n */\nfunction absolute(source: string, base: string): string {\n try {\n return new URL(source, base).toString();\n } catch {\n return source;\n }\n}\n\nasync function defaultLoad(url: string): Promise<string> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`${response.status} ${response.statusText}`);\n }\n return response.text();\n}\n","import type { OriginalPosition, SourceMapStore } from './sourceMap';\n\n/** A position in a compiled file, 1-based as every engine reports it. */\nexport interface StackLocation {\n url: string;\n line: number;\n column: number;\n}\n\n/** One line of a stack, parsed as far as it could be. */\nexport interface StackFrame {\n /** The line exactly as the engine wrote it. */\n raw: string;\n /** The function name the engine knew, or null for an anonymous frame. */\n fn: string | null;\n /** Where it ran, or null when the line named no file. */\n location: StackLocation | null;\n /** Where it was written, once a source map has been consulted. */\n original: OriginalPosition | null;\n}\n\n/**\n * `at fn (url:line:col)`, `at url:line:col`, and the `fn@url:line:col`\n * that Firefox and Safari write.\n *\n * Three engines write three formats and there is no standard, so this\n * is deliberately loose: anything ending in `:digits:digits` is a\n * location, and whatever came before it is a name. A line that does\n * not match is kept as `raw` and shown unparsed rather than dropped —\n * a `[native code]` or `eval` frame is still evidence.\n */\nconst CHROME_FRAME = /^\\s*at\\s+(?:(?<fn>.*?)\\s+\\()?(?<url>.+?):(?<line>\\d+):(?<column>\\d+)\\)?\\s*$/;\nconst MOZILLA_FRAME = /^(?<fn>[^@]*)@(?<url>.+?):(?<line>\\d+):(?<column>\\d+)\\s*$/;\n\n/**\n * Splits a stack string into frames, dropping the message lines an\n * engine puts above them.\n *\n * The message is dropped rather than parsed because the caller already\n * has it: `onError` reports the message and the stack separately, and\n * a V8 stack repeats the message in its first line.\n */\nexport function parseStack(stack: string): StackFrame[] {\n const frames: StackFrame[] = [];\n for (const raw of stack.split('\\n')) {\n const line = raw.trimEnd();\n if (line.trim() === '') {\n continue;\n }\n const match = CHROME_FRAME.exec(line) ?? MOZILLA_FRAME.exec(line);\n if (match?.groups === undefined) {\n // Only once frames have started: everything above the first one\n // is the message, and repeating it under itself is noise.\n if (frames.length > 0) {\n frames.push({ raw: line.trim(), fn: null, location: null, original: null });\n }\n continue;\n }\n const { fn, url, line: lineNumber, column } = match.groups;\n const name = fn === undefined ? '' : fn.trim();\n frames.push({\n raw: line.trim(),\n fn: name === '' || name === 'Object.<anonymous>' ? null : name,\n location: { url, line: Number(lineNumber), column: Number(column) },\n original: null\n });\n }\n return frames;\n}\n\n/**\n * Fills in `original` for every frame whose script has a source map.\n *\n * Maps are fetched once per script and the frames of one stack usually\n * name two or three scripts, so this is a couple of requests. It never\n * rejects: a frame that cannot be mapped keeps its compiled location,\n * which is what the console would have shown anyway.\n *\n * `originalFor` rather than one `lookup`, because a frame inside a\n * published package is two maps deep: the bundler's map of the\n * dependency bundle, then the package's own. See its comment.\n */\nexport async function mapStack(frames: StackFrame[], store: SourceMapStore): Promise<StackFrame[]> {\n return Promise.all(\n frames.map(async frame => {\n if (frame.location === null) {\n return frame;\n }\n const original = await store.originalFor(frame.location.url, frame.location.line, frame.location.column);\n return original === null ? frame : { ...frame, original };\n })\n );\n}\n\n/**\n * The first frame worth putting a code frame under.\n *\n * Frames inside the framework are skipped while any application frame\n * remains, because an error thrown from a component surfaces through\n * several layers of runtime and the runtime is almost never where the\n * bug is. If every frame is a framework frame, the first one wins —\n * the framework is then genuinely the answer.\n */\nexport function primaryFrame(frames: StackFrame[]): StackFrame | null {\n const located = frames.filter(frame => frame.location !== null);\n if (located.length === 0) {\n return null;\n }\n const application = located.find(frame => {\n const path = frame.original?.source ?? frame.location?.url ?? '';\n return !/(^|\\/)(node_modules|packages\\/(core|framework|components))\\//.test(path);\n });\n return application ?? located[0];\n}\n\n/**\n * A path short enough to read in a header: same-origin prefix and\n * query string removed, `node_modules` collapsed to the package.\n *\n * The query matters more than it looks. A dev server rewrites imports\n * with cache-busting parameters — `/src/App.tsx?t=1724965201` — and a\n * stack full of those is unreadable for a reason that has nothing to\n * do with the error.\n */\nexport function shortenPath(url: string, origin?: string): string {\n let path = url;\n if (origin !== undefined && path.startsWith(origin)) {\n path = path.slice(origin.length);\n }\n const query = path.indexOf('?');\n if (query !== -1) {\n path = path.slice(0, query);\n }\n const modules = path.lastIndexOf('/node_modules/');\n if (modules !== -1) {\n path = path.slice(modules + '/node_modules/'.length);\n }\n return path.replace(/^\\/@fs/, '').replace(/^\\.\\//, '');\n}\n\n/** `path:line:column` for a frame, mapped when it could be. */\nexport function formatFrame(frame: StackFrame, origin?: string): string {\n if (frame.original !== null) {\n return `${shortenPath(frame.original.source, origin)}:${frame.original.line}:${frame.original.column}`;\n }\n if (frame.location !== null) {\n return `${shortenPath(frame.location.url, origin)}:${frame.location.line}:${frame.location.column}`;\n }\n return frame.raw;\n}\n","import type { RuntimeErrorSource } from 'gesso-framework';\nimport { codeFrame, type CodeFrame } from './codeFrame';\nimport { SourceMapStore } from './sourceMap';\nimport { formatFrame, mapStack, parseStack, primaryFrame, type StackFrame } from './stackTrace';\n\n/**\n * Where an error came from.\n *\n * The render worker's four sources, plus `window` for the thread the\n * overlay itself runs on — the single-thread configuration, and\n * anything the shell does around the app.\n */\nexport type ErrorOrigin = RuntimeErrorSource | 'window';\n\n/** What each origin is called, and the sentence under the message. */\nconst ORIGINS: Record<ErrorOrigin, { label: string; note: string }> = {\n uncaught: {\n label: 'render worker',\n note: 'Nothing caught this, so it ran outside any message from the shell: module scope, a callback the shell did not drive, or a frame the clock paced itself. If it was a frame, its work was already taken off the dirty set, so what you see may be stale.'\n },\n message: {\n label: 'render worker',\n note: 'Thrown while the runtime was handling a message from the shell. A frame usually arrives that way, so this is most often a frame that did not finish, and what you see may be stale. Where it was an input or a resize instead, that one message was dropped.'\n },\n renderer: {\n label: 'renderer',\n note: 'The backend refused to draw. Layout and state are unaffected; the surface is not being updated.'\n },\n listener: {\n label: 'event listener',\n note: 'The dispatcher caught this, so the event still reached the rest of the tree and the application is running. What did not happen is whatever this handler was for.'\n },\n frame: {\n label: 'frame',\n note: 'A frame threw and was abandoned. The clock is still running and the application is still drawing, so what you see is the last frame that finished: this is a report, not a stop. Whatever the broken frame was going to change has not changed.'\n },\n channel: {\n label: 'channel',\n note: 'A channel’s worker or its patch stream threw. The view is intact; the data behind it has stopped arriving.'\n },\n window: { label: 'main thread', note: '' }\n};\n\nexport interface ErrorOverlayOptions {\n /**\n * Also write every error to the console (default true).\n *\n * On by default because the overlay is a second place to see an\n * error, not a replacement for the first: the console keeps the live\n * object, its `cause`, and the \"expand to see the real frames\" that\n * no snapshot of a stack can offer.\n */\n echoToConsole?: boolean;\n /** Where source maps are fetched from. Injected by the specs. */\n sourceMaps?: SourceMapStore;\n}\n\n/** One distinct error, and how many times it has arrived. */\ninterface Entry {\n message: string;\n stack: string | undefined;\n origin: ErrorOrigin;\n repeats: number;\n frames: StackFrame[];\n /** Set once the source maps for this entry's frames have resolved. */\n mapped: boolean;\n code: CodeFrame | null;\n}\n\n/**\n * The error overlay: what a worker threw, drawn over the app that was\n * running when it threw.\n *\n * A canvas UI has no equivalent of a page that stops rendering. When a\n * render worker throws, the last good frame stays on screen — pixels\n * that look exactly like a working application — and the only witness\n * is a console message on a thread the developer has to know to\n * select. `WorkerApp` already forwards those errors to `onError`;\n * this is that callback, with the stack put back through the source\n * maps and the offending line quoted.\n *\n * It is a development tool and it makes a development tool's trade:\n * it fetches source maps, keeps every distinct error of the session,\n * and covers the application it is reporting on.\n */\nexport class ErrorOverlay {\n private readonly host: HTMLElement;\n /**\n * The document the host belongs to, rather than the global one.\n *\n * The same rule `SemanticsMirror` follows: everything this class\n * builds hangs off the element it was handed, so it works in a\n * second window and can be driven by a fake document in a spec —\n * which is the only way to test it in a suite that runs in Node.\n */\n private readonly doc: Document;\n private readonly view: (Window & typeof globalThis) | null;\n private readonly container: HTMLDivElement;\n private readonly root: ShadowRoot;\n private readonly panel: HTMLDivElement;\n private readonly maps: SourceMapStore;\n private readonly echo: boolean;\n private readonly entries: Entry[] = [];\n private readonly restoreHostPosition: string | null;\n\n private shown = -1;\n private disposed = false;\n private detachWindow: (() => void) | null = null;\n\n constructor(host: HTMLElement, options: ErrorOverlayOptions = {}) {\n this.host = host;\n this.doc = host.ownerDocument;\n this.view = this.doc.defaultView;\n this.maps = options.sourceMaps ?? new SourceMapStore();\n this.echo = options.echoToConsole !== false;\n\n // The overlay covers the element the app was mounted into rather\n // than the viewport, because a Gesso app is not necessarily the\n // whole page — the playground runs one in a pane. Covering the app\n // needs the app's element to be a containing block, which is the\n // one property this touches, and it is put back on dispose.\n const position = this.view?.getComputedStyle(host).position ?? 'static';\n this.restoreHostPosition = position === 'static' ? host.style.position : null;\n if (position === 'static') {\n host.style.position = 'relative';\n }\n\n this.container = this.doc.createElement('div');\n this.container.style.display = 'none';\n this.root = this.container.attachShadow({ mode: 'open' });\n const style = this.doc.createElement('style');\n style.textContent = STYLES;\n this.panel = this.doc.createElement('div');\n this.panel.className = 'panel';\n this.panel.tabIndex = -1;\n // `alert` and not `alertdialog`: the overlay traps nothing and\n // takes nothing over, and claiming a dialog would tell a screen\n // reader to expect a focus contract this does not honour.\n this.panel.setAttribute('role', 'alert');\n const scrim = this.doc.createElement('div');\n scrim.className = 'scrim';\n scrim.appendChild(this.panel);\n this.root.append(style, scrim);\n host.appendChild(this.container);\n }\n\n /** How many distinct errors have been reported. */\n get count(): number {\n return this.entries.length;\n }\n\n /** True while the overlay is covering the app. */\n get visible(): boolean {\n return this.container.style.display !== 'none';\n }\n\n /**\n * Reports an error, in the shape `WorkerApp`'s `onError` hands it\n * over, so the whole wiring is `onError: overlay.report`.\n *\n * Bound as a field rather than a method for exactly that: it is\n * passed as a callback far more often than it is called.\n */\n report = (message: string, stack?: string, origin: ErrorOrigin = 'window'): void => {\n if (this.disposed) {\n return;\n }\n if (this.echo) {\n console.error(`[gesso: ${origin}] ${message}`, stack ?? '');\n }\n const existing = this.entries.find(entry => entry.message === message && entry.stack === stack);\n if (existing !== undefined) {\n existing.repeats++;\n // A repeat does not reopen a dismissed overlay. An error thrown\n // every frame would otherwise be impossible to get out of the\n // way of, and getting it out of the way is how a developer looks\n // at the application underneath it.\n if (this.visible && this.entries[this.shown] === existing) {\n this.render();\n }\n return;\n }\n const entry: Entry = {\n message,\n stack,\n origin,\n repeats: 1,\n frames: stack === undefined ? [] : parseStack(stack),\n mapped: false,\n code: null\n };\n this.entries.push(entry);\n this.shown = this.entries.length - 1;\n this.show();\n void this.resolveSources(entry);\n };\n\n /** Reports a thrown value, which is usually but not always an Error. */\n reportError = (error: unknown, origin: ErrorOrigin = 'window'): void => {\n if (error instanceof Error) {\n this.report(error.message, error.stack, origin);\n return;\n }\n this.report(String(error), undefined, origin);\n };\n\n /**\n * Catches what this thread throws, too.\n *\n * The single-thread configuration runs components here, and even in\n * the worker configuration the shell around the app can throw. Returns\n * a function that stops listening; `dispose` calls it as well.\n */\n captureWindowErrors(target: Window | null = this.view): () => void {\n if (target === null) {\n return () => {};\n }\n const onError = (event: ErrorEvent): void => {\n this.reportError(event.error ?? event.message, 'window');\n };\n const onRejection = (event: PromiseRejectionEvent): void => {\n this.reportError(event.reason, 'window');\n };\n target.addEventListener('error', onError);\n target.addEventListener('unhandledrejection', onRejection);\n const detach = (): void => {\n target.removeEventListener('error', onError);\n target.removeEventListener('unhandledrejection', onRejection);\n this.detachWindow = null;\n };\n this.detachWindow = detach;\n return detach;\n }\n\n /** Hides the overlay. The errors are kept and can be shown again. */\n hide(): void {\n this.container.style.display = 'none';\n this.doc.removeEventListener('keydown', this.handleKeyDown, true);\n }\n\n /** Hides the overlay and forgets every error it was holding. */\n clear(): void {\n this.entries.length = 0;\n this.shown = -1;\n this.hide();\n }\n\n dispose(): void {\n this.disposed = true;\n this.detachWindow?.();\n this.doc.removeEventListener('keydown', this.handleKeyDown, true);\n this.container.remove();\n if (this.restoreHostPosition !== null) {\n this.host.style.position = this.restoreHostPosition;\n }\n }\n\n private show(): void {\n this.container.style.display = 'block';\n this.render();\n // Escape is taken at the document, in the capture phase, rather\n // than from the panel: focus is not the overlay's to hold. The\n // accessibility mirror moves DOM focus to follow the application's\n // own, and it does so on the frames either side of an error — so a\n // panel that had focus when it opened does not have it a moment\n // later, and Escape would reach nothing. Capturing also means the\n // canvas underneath does not also see the key, which it should not\n // while it is covered.\n this.doc.addEventListener('keydown', this.handleKeyDown, true);\n // Focus anyway, for the screen reader: it lands on the message\n // instead of announcing it from wherever the user happened to be.\n this.panel.focus();\n }\n\n private handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n this.hide();\n }\n };\n\n /**\n * Maps the entry's stack and quotes the line it points at.\n *\n * Deliberately after the first paint: the raw stack is on screen\n * within a frame of the error, and the mapped one replaces it when\n * the network answers. A developer looking at an error should never\n * be waiting on a fetch to see it.\n */\n private async resolveSources(entry: Entry): Promise<void> {\n if (entry.frames.length === 0) {\n return;\n }\n entry.frames = await mapStack(entry.frames, this.maps);\n entry.mapped = true;\n const frame = primaryFrame(entry.frames);\n // The text comes with the position rather than from a second\n // lookup: a frame inside a package is mapped through two maps, and\n // it is the last one in that chain that inlined the source.\n const content = frame?.original?.content ?? null;\n if (frame?.original != null && content !== null) {\n entry.code = codeFrame(content, frame.original.line, frame.original.column);\n }\n if (!this.disposed && this.visible && this.entries[this.shown] === entry) {\n this.render();\n }\n }\n\n /** `element`, bound to this overlay's document. */\n private el = <K extends keyof HTMLElementTagNameMap>(\n tag: K,\n options: { className?: string; text?: string } = {}\n ): HTMLElementTagNameMap[K] => element(this.doc, tag, options);\n\n /** A header button, bound to this overlay's document. */\n private button = (label: string, title: string, onClick: () => void): HTMLButtonElement =>\n button(this.doc, label, title, onClick);\n\n private render(): void {\n const entry = this.entries[this.shown];\n if (entry === undefined) {\n return;\n }\n const origin = ORIGINS[entry.origin];\n this.panel.replaceChildren();\n\n const header = this.el('header');\n header.append(this.el('span', { className: 'badge', text: origin.label }));\n if (entry.repeats > 1) {\n header.append(this.el('span', { className: 'repeats', text: `×${entry.repeats}` }));\n }\n const spacer = this.el('span', { className: 'spacer' });\n header.append(spacer);\n if (this.entries.length > 1) {\n header.append(\n this.button('‹', 'Previous error', () => this.step(-1)),\n this.el('span', { className: 'count', text: `${this.shown + 1} / ${this.entries.length}` }),\n this.button('›', 'Next error', () => this.step(1))\n );\n }\n header.append(\n this.button('Copy', 'Copy this error to the clipboard', () => this.copy(entry)),\n this.button('✕', 'Dismiss', () => this.hide())\n );\n this.panel.append(header, this.el('h1', { text: entry.message }));\n\n if (origin.note !== '') {\n this.panel.append(this.el('p', { className: 'note', text: origin.note }));\n }\n\n if (entry.code !== null) {\n this.panel.append(renderCodeFrame(this.doc, entry.code));\n }\n\n if (entry.frames.length > 0) {\n const list = this.el('ol', { className: 'stack' });\n for (const frame of entry.frames) {\n const item = this.el('li');\n if (frame.location === null) {\n item.append(this.el('span', { className: 'raw', text: frame.raw }));\n } else {\n item.append(\n this.el('span', { className: 'fn', text: frame.fn ?? '(anonymous)' }),\n this.el('span', { className: 'at', text: formatFrame(frame, this.view?.location.origin) })\n );\n }\n list.append(item);\n }\n this.panel.append(list);\n } else if (entry.stack !== undefined) {\n // Nothing parsed: show what the engine wrote rather than nothing.\n this.panel.append(this.el('pre', { className: 'stack raw', text: entry.stack }));\n }\n\n const footer = this.el('footer');\n footer.append(\n this.el('span', {\n text: entry.mapped || entry.frames.length === 0 ? 'Escape dismisses this.' : 'Resolving source maps…'\n })\n );\n this.panel.append(footer);\n }\n\n private step(delta: number): void {\n const next = this.shown + delta;\n if (next < 0 || next >= this.entries.length) {\n return;\n }\n this.shown = next;\n this.render();\n void this.resolveSources(this.entries[next]);\n }\n\n private copy(entry: Entry): void {\n const frames = entry.frames.map(frame =>\n frame.location === null ? frame.raw : ` at ${frame.fn ?? '(anonymous)'} (${formatFrame(frame)})`\n );\n const text = [entry.message, ...frames].join('\\n');\n void this.view?.navigator.clipboard?.writeText(text).catch(() => {\n // A clipboard permission the page does not have is not worth a\n // second error on top of the one being reported.\n });\n }\n}\n\n/**\n * Mounts an error overlay over an application's host element.\n *\n * const overlay = mountErrorOverlay(host);\n * createApp({ renderWorker, onError: overlay.report });\n * overlay.captureWindowErrors();\n */\nexport function mountErrorOverlay(host: HTMLElement, options: ErrorOverlayOptions = {}): ErrorOverlay {\n return new ErrorOverlay(host, options);\n}\n\nfunction renderCodeFrame(doc: Document, frame: CodeFrame): HTMLElement {\n const el = <K extends keyof HTMLElementTagNameMap>(\n tag: K,\n options: { className?: string; text?: string } = {}\n ): HTMLElementTagNameMap[K] => element(doc, tag, options);\n const width = String(frame.lines[frame.lines.length - 1].number).length;\n const block = el('pre', { className: 'code' });\n for (const line of frame.lines) {\n const row = el('div', { className: line.target ? 'line target' : 'line' });\n row.append(\n el('span', { className: 'gutter', text: String(line.number).padStart(width, ' ') }),\n el('span', { className: 'text', text: line.text })\n );\n block.append(row);\n if (line.target) {\n const caret = el('div', { className: 'line caret' });\n caret.append(\n el('span', { className: 'gutter', text: ' '.repeat(width) }),\n el('span', { className: 'text', text: `${' '.repeat(Math.max(0, frame.column - 1))}^` })\n );\n block.append(caret);\n }\n }\n return block;\n}\n\nfunction element<K extends keyof HTMLElementTagNameMap>(\n doc: Document,\n tag: K,\n options: { className?: string; text?: string } = {}\n): HTMLElementTagNameMap[K] {\n const node = doc.createElement(tag);\n if (options.className !== undefined) {\n node.className = options.className;\n }\n if (options.text !== undefined) {\n // Every string here came from a thrown error or a source file, so\n // it is set as text and never as markup.\n node.textContent = options.text;\n }\n return node;\n}\n\nfunction button(doc: Document, label: string, title: string, onClick: () => void): HTMLButtonElement {\n const node = element(doc, 'button', { text: label });\n node.type = 'button';\n node.title = title;\n node.setAttribute('aria-label', title);\n node.addEventListener('click', onClick);\n return node;\n}\n\n/**\n * The overlay's own styles, inside its shadow root.\n *\n * Shadow rather than a stylesheet in the page: an overlay that\n * inherited the application's CSS would be unreadable in exactly the\n * applications whose CSS is broken, and one that leaked its own rules\n * would be a development tool changing the thing being developed.\n *\n * Dark and monospace whatever the page is, because this is a tool\n * rather than a part of the app, and it should never be mistaken for\n * one.\n */\nconst STYLES = `\n:host { all: initial; }\n.scrim {\n position: absolute;\n inset: 0;\n z-index: 2147483000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n box-sizing: border-box;\n background: rgba(12, 12, 16, 0.72);\n backdrop-filter: blur(2px);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n line-height: 1.5;\n color: #e6e6ea;\n overflow: auto;\n}\n.panel {\n width: min(860px, 100%);\n max-height: 100%;\n overflow: auto;\n box-sizing: border-box;\n padding: 16px 18px 12px;\n border: 1px solid #7f1d1d;\n border-radius: 8px;\n background: #17171c;\n box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);\n outline: none;\n}\nheader { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }\n.spacer { flex: 1; }\n.badge {\n padding: 2px 8px;\n border-radius: 999px;\n background: #7f1d1d;\n color: #fee2e2;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n font-size: 10px;\n}\n.repeats { color: #fca5a5; }\n.count { color: #9a9aa5; }\nbutton {\n font: inherit;\n color: #cfcfd6;\n background: #24242c;\n border: 1px solid #34343e;\n border-radius: 5px;\n padding: 2px 8px;\n cursor: pointer;\n}\nbutton:hover { background: #2e2e38; color: #fff; }\nbutton:focus-visible { outline: 2px solid #f87171; outline-offset: 1px; }\nh1 {\n margin: 0 0 8px;\n font-size: 14px;\n font-weight: 600;\n color: #fca5a5;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.note { margin: 0 0 12px; color: #9a9aa5; max-width: 68ch; }\n.code {\n margin: 0 0 12px;\n padding: 10px 12px;\n background: #101015;\n border: 1px solid #26262f;\n border-radius: 6px;\n overflow-x: auto;\n}\n.code .line { white-space: pre; }\n.code .gutter { color: #55555f; margin-right: 12px; user-select: none; }\n.code .target { background: rgba(127, 29, 29, 0.35); }\n.code .caret .text { color: #f87171; }\nol.stack { margin: 0; padding: 0; list-style: none; }\nol.stack li { display: flex; gap: 10px; padding: 1px 0; }\nol.stack .fn { color: #d4d4dc; flex: 0 0 auto; }\nol.stack .at { color: #8b8b96; word-break: break-all; }\nol.stack .raw { color: #6f6f7a; }\npre.stack { margin: 0; white-space: pre-wrap; color: #8b8b96; }\nfooter { margin-top: 12px; color: #6f6f7a; }\n`;\n","import {\n applyPatch,\n isChannelClientMessage,\n isChannelHostMessage,\n markInstant,\n type ActionCause,\n type ActionEntry,\n type ChannelErrorEntry,\n type ChannelPort,\n type CommandEntry,\n type FrameEntry,\n type Patch,\n type PatchEntry,\n type WorkerHandle\n} from 'gesso-framework';\n\n/**\n * The entries are declared with the rest of the devtools vocabulary in\n * `gesso-framework`, for the reason `ConsoleEntry` is: they are plain\n * data that crosses a thread to reach a panel, and in the worker\n * configuration they cross on the framework's own devtools channel.\n * They are re-exported here because this is where a reader looks for\n * them.\n */\nexport type { ActionCause, ActionEntry, ChannelErrorEntry, CommandEntry, FrameEntry, PatchEntry };\n\n/**\n * The store action log: every command a view sent\n * across the barrier, every patch that came back, on one timeline,\n * with the view rewindable to any point on it.\n *\n * The seam is the port. A channel is two message types over a\n * `ChannelPort`, so a recorder that sits in the middle of one sees\n * both directions at their only crossing, needs nothing from the\n * framework beyond the two type guards the protocol already exports,\n * and can put a patch back on the wire, which is what makes time\n * travel a replay rather than a second write path into the replica.\n *\n * Nothing here touches the DOM. That is deliberate: the tap belongs\n * wherever the ports already are, which in the worker configuration is\n * the render worker, and a recorder that imported a document could not\n * go there. `ActionLogPanel` is the half that draws.\n *\n * **What time travel does.** It rewrites what the *view* holds. The\n * authoritative state lives on the other thread and is not rewound,\n * cannot be rewound from here, and does not know this happened. While\n * the log is pinned to a step, patches still arriving are recorded and\n * held rather than delivered, so the view stays where it was put;\n * going live delivers the state the application actually reached. A\n * command sent from the view while pinned is forwarded like any other,\n * because the application is still running and pretending otherwise\n * would be a lie about a button that visibly did something.\n */\nexport interface ActionLog {\n /**\n * Wraps a worker handle so every channel opened on it is recorded.\n *\n * `tokens` is read for one thing: the value both ends of a channel\n * start from. The first patch a provider sends is a diff against the\n * token's initial, so a recorder that started from nothing would\n * reconstruct an early step out of a patch whose base it never had.\n */\n tap(handle: WorkerHandle, tokens: readonly ActionLogToken[]): WorkerHandle;\n /**\n * Wraps one port, for a channel fed from this thread.\n *\n * `createChannelRegistry` makes the pair for a `source` registration\n * itself and hands out neither end, so a local channel is tapped by\n * providing it by hand instead: call `provide(token, source, port)`\n * on one end of a `MessageChannel` and register the other through\n * this.\n */\n tapPort(port: ChannelPort, token: ActionLogToken): ChannelPort;\n /**\n * Names what is being answered for as long as the returned function\n * has not been called, so everything recorded meanwhile carries the\n * same `ActionCause`.\n *\n * Called around the dispatch of one input, which is the only moment\n * where a cause is known rather than guessed: the command a click's\n * listener sends is sent synchronously inside it. Nothing is\n * allocated for an input that records nothing, so wrapping every\n * pointer move costs a function call and a null check.\n *\n * The patches that answer such a command are given the same cause,\n * which is an inference and the record says so: the barrier carries\n * no request id, so the recorder ties the next patch batch on that\n * channel to the command that preceded it, until a frame has drawn\n * one or another command replaces it.\n */\n cause(label: string): () => void;\n /**\n * Records the frame that drew whatever has been recorded since the\n * last one, closing the chain from click to command to patches to\n * pixels.\n *\n * Nothing is recorded for a frame with nothing to close, so an\n * application drawing sixty frames a second while its channels are\n * quiet adds nothing to the timeline.\n */\n frame(id: number): void;\n /** The timeline, oldest first. */\n readonly entries: readonly ActionEntry[];\n /** The channels this log is tapping, by name, in the order tapped. */\n readonly channels: readonly string[];\n /**\n * The entry the view is pinned to, or null when the view is live.\n *\n * A sequence number rather than an index, because entries fall off\n * the front of a bounded log and an index would then mean a\n * different entry than it did a moment ago.\n */\n readonly pinnedTo: number | null;\n /**\n * Rewinds every tapped channel's view to the state it held once\n * `seq` had been applied, or catches it up to the application when\n * `seq` is null.\n *\n * Each key is posted to the replica as one `{ op: 'set', path: [] }`,\n * which is the shape a reattaching client is already answered with,\n * so nothing downstream can tell a replay from a resync.\n */\n jumpTo(seq: number | null): void;\n /** Empties the timeline and goes live. Tapped channels stay tapped. */\n clear(): void;\n /** Called whenever the timeline or the pinned step changed. */\n subscribe(listener: () => void): () => void;\n /**\n * Stops recording, leaving every tapped channel working.\n *\n * The relay is not removed. A port handed to a replica cannot be\n * taken back out of the middle of it, so what this does is stop\n * listening: messages pass through, nothing is written down, and a\n * held patch could not be stranded by it.\n */\n dispose(): void;\n}\n\n/**\n * A channel's identity, structurally.\n *\n * The same erasure `ChannelRegistration` uses, and for the same\n * reason: a list of channels has no single generic instantiation, and\n * the only two things a recorder wants from a token are its name and\n * the value both ends start from.\n */\nexport interface ActionLogToken {\n readonly name: string;\n readonly initial: object;\n}\n\nexport interface ActionLogOptions {\n /**\n * Most entries kept. Default 200.\n *\n * A dropped entry is not forgotten, only unaddressable: its patches\n * are folded into the base state each channel is reconstructed from,\n * so a jump to a step that survived is still exact.\n */\n readonly limit?: number;\n}\n\nexport function createActionLog(options: ActionLogOptions = {}): ActionLog {\n return new Recorder(options.limit ?? 200);\n}\n\nfunction now(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\n/**\n * Sends each new entry as it is recorded, and returns a function that\n * stops.\n *\n * `subscribe` says only that something changed, so the sequence number\n * is what tells a new entry from the ones already sent; a `clear`\n * resets it, and a bounded log retiring old entries does not. Both\n * routes to a panel need exactly this: the hook, for a log in the\n * page, and the render worker tap, for one on the other side of a\n * thread.\n */\nexport function forwardNewEntries(log: ActionLog, send: (entry: ActionEntry) => void): () => void {\n // Below the first sequence number, which is zero.\n let lastSeq = -1;\n const flush = (): void => {\n const entries = log.entries;\n if (entries.length === 0) {\n lastSeq = -1;\n return;\n }\n for (const entry of entries) {\n if (entry.seq > lastSeq) {\n lastSeq = entry.seq;\n send(entry);\n }\n }\n };\n flush();\n return log.subscribe(flush);\n}\n\n/**\n * One channel with the recorder in the middle of it.\n *\n * It holds two states rather than one. `live` follows every patch that\n * arrives, whether or not the view was allowed to see it, so going\n * live is a single set per key instead of a backlog to drain. `base`\n * is the state as of the oldest entry still on the timeline, which is\n * what keeps a bounded log able to reconstruct exactly.\n */\nclass TappedChannel {\n readonly base = new Map<string, unknown>();\n readonly live = new Map<string, unknown>();\n private open = true;\n\n constructor(\n readonly name: string,\n initial: object,\n private readonly toProvider: (message: unknown) => void,\n private readonly toReplica: (message: unknown) => void,\n private readonly recorder: Recorder\n ) {\n for (const [key, value] of Object.entries(initial)) {\n this.base.set(key, value);\n this.live.set(key, value);\n }\n }\n\n /** The replica's end: commands out, and the sync that starts it all. */\n fromReplica(data: unknown): void {\n if (this.open && isChannelClientMessage(data) && data.type === 'channel:command') {\n // In the browser's own profiler too, on the same timeline as the\n // frame spans, so a command and the frame that answered it are\n // read off one recording rather than two panels.\n markInstant(`command ${this.name}.${data.command}`, data.payload);\n this.recorder.record({ kind: 'command', channel: this.name, command: data.command, payload: data.payload });\n }\n this.toProvider(data);\n }\n\n /** The owning thread's end: patches and errors. */\n fromProvider(data: unknown): void {\n if (this.open && isChannelHostMessage(data)) {\n if (data.type === 'channel:patch') {\n for (const patch of data.patches) {\n this.live.set(patch.projection, applyPatch(this.live.get(patch.projection), patch));\n }\n markInstant(`patch ${this.name}`, projectionsOf(data.patches));\n this.recorder.record({\n kind: 'patch',\n channel: this.name,\n patches: data.patches,\n keys: projectionsOf(data.patches)\n });\n if (this.recorder.pinnedTo !== null) {\n // Held, not dropped: `live` already has it, and delivering it\n // would drag the view off the step someone is reading.\n return;\n }\n } else {\n this.recorder.record({ kind: 'error', channel: this.name, message: data.message });\n }\n }\n this.toReplica(data);\n }\n\n /** Folds a dropped entry's patches into the reconstruction base. */\n absorb(patches: readonly Patch[]): void {\n for (const patch of patches) {\n this.base.set(patch.projection, applyPatch(this.base.get(patch.projection), patch));\n }\n }\n\n /** Puts a whole state on the wire, exactly as a resync is answered. */\n show(state: ReadonlyMap<string, unknown>): void {\n const patches: Patch[] = [];\n for (const [projection, value] of state) {\n patches.push({ op: 'set', projection, path: [], value });\n }\n if (patches.length > 0) {\n this.toReplica({ type: 'channel:patch', patches });\n }\n }\n\n close(): void {\n this.open = false;\n }\n}\n\nfunction projectionsOf(patches: readonly Patch[]): string[] {\n const keys: string[] = [];\n for (const patch of patches) {\n if (!keys.includes(patch.projection)) {\n keys.push(patch.projection);\n }\n }\n return keys;\n}\n\n/**\n * A command waiting to be answered, so the patches that answer it can\n * be given the cause the command had.\n *\n * `claimed` is what ends the wait: once a patch batch has taken this\n * cause, the next frame closes it, because that frame is the one that\n * drew the answer. An unclaimed one waits as long as it takes, which\n * is the case that matters — the playground's `heavy.compute()` takes\n * a second and a half, and a cause that timed out before then would\n * lose exactly the round trip a person most wants to see.\n */\ninterface PendingCause {\n readonly cause: ActionCause;\n claimed: boolean;\n}\n\n/** An entry before the recorder gives it its place on the timeline. */\ntype NewEntry =\n | Omit<CommandEntry, 'seq' | 'at'>\n | Omit<PatchEntry, 'seq' | 'at'>\n | Omit<ChannelErrorEntry, 'seq' | 'at'>\n | Omit<FrameEntry, 'seq' | 'at'>;\n\nclass Recorder implements ActionLog {\n private readonly tapped: TappedChannel[] = [];\n private readonly log: ActionEntry[] = [];\n private readonly listeners = new Set<() => void>();\n private seq = 0;\n private pinned: number | null = null;\n /** The input being dispatched, if one is, and the label it was opened with. */\n private openLabel: string | null = null;\n private openCause: ActionCause | null = null;\n private causeId = 0;\n /** Commands awaiting their patches, by channel. */\n private readonly awaiting = new Map<string, PendingCause>();\n /**\n * The cause shared by everything recorded since the last frame\n * entry: undefined when nothing has been, null when two causes have.\n */\n private frameCause: ActionCause | null | undefined = undefined;\n private recordedSinceFrame = false;\n\n constructor(private readonly limit: number) {}\n\n get entries(): readonly ActionEntry[] {\n return this.log;\n }\n\n get channels(): readonly string[] {\n return this.tapped.map(channel => channel.name);\n }\n\n get pinnedTo(): number | null {\n return this.pinned;\n }\n\n tap(handle: WorkerHandle, tokens: readonly ActionLogToken[]): WorkerHandle {\n const initials = new Map(tokens.map(token => [token.name, token.initial]));\n return {\n open: (key: string): MessagePort => {\n const real = handle.open(key);\n // A relay pair, so what the framework receives is a real\n // `MessagePort` and not something shaped like one. `open`\n // promises a port, and a channel registered through a tap has\n // to behave in every way like a channel that was not.\n const relay = new MessageChannel();\n const channel = new TappedChannel(\n key,\n initials.get(key) ?? {},\n message => real.postMessage(message),\n message => relay.port2.postMessage(message),\n this\n );\n real.onmessage = event => channel.fromProvider(event.data);\n relay.port2.onmessage = event => channel.fromReplica(event.data);\n this.tapped.push(channel);\n return relay.port1;\n },\n get spawned(): boolean {\n return handle.spawned;\n },\n terminate: (): void => {\n handle.terminate();\n }\n };\n }\n\n tapPort(port: ChannelPort, token: ActionLogToken): ChannelPort {\n let listener: ((event: { data: unknown }) => void) | null = null;\n const channel = new TappedChannel(\n token.name,\n token.initial,\n message => port.postMessage(message),\n message => listener?.({ data: message }),\n this\n );\n port.onmessage = event => channel.fromProvider(event.data);\n this.tapped.push(channel);\n return {\n postMessage(message: unknown): void {\n channel.fromReplica(message);\n },\n get onmessage(): ((event: { data: unknown }) => void) | null {\n return listener;\n },\n set onmessage(next: ((event: { data: unknown }) => void) | null) {\n listener = next;\n }\n };\n }\n\n cause(label: string): () => void {\n const previousLabel = this.openLabel;\n const previousCause = this.openCause;\n this.openLabel = label;\n // Not minted yet: an input that sends no command should not\n // consume an id, so a person reading the timeline sees the causes\n // that did something and not a counter jumping in tens.\n this.openCause = null;\n let closed = false;\n return () => {\n if (closed) {\n return;\n }\n closed = true;\n this.openLabel = previousLabel;\n this.openCause = previousCause;\n };\n }\n\n frame(id: number): void {\n if (!this.recordedSinceFrame) {\n return;\n }\n this.recordedSinceFrame = false;\n const cause = this.frameCause ?? undefined;\n this.frameCause = undefined;\n // A cause whose answer has been drawn is spent. One still waiting\n // for its first patch is left alone.\n for (const [channel, pending] of this.awaiting) {\n if (pending.claimed) {\n this.awaiting.delete(channel);\n }\n }\n this.append({ kind: 'frame', frame: id, ...(cause === undefined ? {} : { cause }) });\n }\n\n record(\n entry: Omit<CommandEntry, 'seq' | 'at'> | Omit<PatchEntry, 'seq' | 'at'> | Omit<ChannelErrorEntry, 'seq' | 'at'>\n ): void {\n const cause = this.causeFor(entry);\n this.append(cause === undefined ? entry : { ...entry, cause });\n }\n\n /**\n * Which cause an entry belongs to.\n *\n * A command belongs to the input being dispatched, which is known\n * rather than guessed. A patch belongs to the command it answers,\n * which is not: the barrier carries no request id, so what stands\n * for one is \"the last command on this channel that nothing has\n * answered yet\". It is right whenever a channel is answering one\n * thing at a time, which is what a channel driven by a person does,\n * and it is wrong for a channel that also emits on its own between\n * the command and its answer. The panel says `via` rather than\n * `from` for that reason.\n */\n private causeFor(entry: { kind: string; channel: string }): ActionCause | undefined {\n if (entry.kind === 'command') {\n if (this.openLabel === null) {\n this.awaiting.delete(entry.channel);\n return undefined;\n }\n this.openCause ??= { id: ++this.causeId, label: this.openLabel };\n this.awaiting.set(entry.channel, { cause: this.openCause, claimed: false });\n return this.openCause;\n }\n const pending = this.awaiting.get(entry.channel);\n // The open cause first: on a thread where the provider answers\n // synchronously, the patch arrives before the click's dispatch has\n // returned and is the same answer either way.\n const cause = this.openCause ?? pending?.cause;\n if (cause !== undefined && pending?.cause === cause) {\n pending.claimed = true;\n }\n return cause;\n }\n\n private append(entry: NewEntry): void {\n const complete = { ...entry, seq: this.seq++, at: now() } as ActionEntry;\n if (complete.kind !== 'frame') {\n this.recordedSinceFrame = true;\n const cause = complete.cause;\n if (cause !== undefined) {\n // Null means two causes met before a frame closed them, and\n // the frame then belongs to neither.\n this.frameCause = this.frameCause === undefined || this.frameCause === cause ? cause : null;\n }\n }\n this.log.push(complete);\n while (this.log.length > this.limit) {\n this.drop();\n }\n this.notify();\n }\n\n jumpTo(seq: number | null): void {\n this.pinned = seq;\n for (const channel of this.tapped) {\n channel.show(seq === null ? channel.live : this.stateAt(channel, seq));\n }\n this.notify();\n }\n\n clear(): void {\n this.jumpTo(null);\n while (this.log.length > 0) {\n this.drop();\n }\n // A command whose answer is still coming would otherwise hand its\n // cause to a patch on an empty timeline, naming a click nothing\n // shows any more.\n this.awaiting.clear();\n this.frameCause = undefined;\n this.recordedSinceFrame = false;\n this.notify();\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n dispose(): void {\n // Released first. A recorder that stopped while pinned would leave\n // the view frozen on a step with nothing left to unfreeze it.\n this.jumpTo(null);\n for (const channel of this.tapped) {\n channel.close();\n }\n this.tapped.length = 0;\n this.log.length = 0;\n this.pinned = null;\n this.awaiting.clear();\n this.openLabel = null;\n this.openCause = null;\n this.frameCause = undefined;\n this.recordedSinceFrame = false;\n this.listeners.clear();\n }\n\n /** Retires the oldest entry, leaving the reconstruction exact. */\n private drop(): void {\n const dropped = this.log.shift();\n if (dropped?.kind !== 'patch') {\n return;\n }\n for (const channel of this.tapped) {\n if (channel.name === dropped.channel) {\n channel.absorb(dropped.patches);\n }\n }\n }\n\n private stateAt(channel: TappedChannel, seq: number): Map<string, unknown> {\n const state = new Map(channel.base);\n for (const entry of this.log) {\n if (entry.seq > seq) {\n break;\n }\n if (entry.kind !== 'patch' || entry.channel !== channel.name) {\n continue;\n }\n for (const patch of entry.patches) {\n state.set(patch.projection, applyPatch(state.get(patch.projection), patch));\n }\n }\n return state;\n }\n\n private notify(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n","import type { ActionEntry, ActionLog } from './ActionLog';\n\n/**\n * The action log's panel: the timeline, and a click on any step to put\n * the view back where it was at that step.\n *\n * DOM in a shadow root over the canvas, for the reasons the error\n * overlay and the node inspector both give: the application owns the\n * canvas, and a panel drawn into the scene would be part of the scene\n * it is describing.\n *\n * It differs from the node inspector in one way that matters. The\n * inspector takes no pointer events because the person is hovering the\n * thing it describes; this panel is operated, so it takes them over\n * itself and nowhere else. That is also why it is behind a toggle\n * rather than always up: while it is showing, the canvas underneath it\n * is not reachable.\n */\nexport interface ActionLogPanel {\n /** Shows or hides the panel. A hidden panel stops rendering. */\n setVisible(visible: boolean): void;\n readonly visible: boolean;\n dispose(): void;\n}\n\nexport interface ActionLogPanelOptions {\n /** Which corner it floats in. Default `'bottom-right'`. */\n readonly corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n}\n\n/** Longest a payload or a value is printed before it is cut. */\nconst VALUE_CHARS = 60;\n\nexport function mountActionLogPanel(\n host: HTMLElement,\n log: ActionLog,\n options: ActionLogPanelOptions = {}\n): ActionLogPanel {\n const doc = host.ownerDocument;\n const view = doc.defaultView;\n const position = view?.getComputedStyle(host).position ?? 'static';\n const restore = position === 'static' ? host.style.position : null;\n if (position === 'static') {\n host.style.position = 'relative';\n }\n\n const container = doc.createElement('div');\n const root = container.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = STYLES;\n\n const panel = doc.createElement('div');\n panel.className = `panel ${options.corner ?? 'bottom-right'}`;\n panel.hidden = true;\n\n const header = doc.createElement('header');\n const title = doc.createElement('span');\n title.className = 'title';\n title.textContent = 'actions';\n const state = doc.createElement('span');\n state.className = 'state';\n const live = doc.createElement('button');\n live.type = 'button';\n live.textContent = 'Live';\n live.addEventListener('click', () => log.jumpTo(null));\n const clear = doc.createElement('button');\n clear.type = 'button';\n clear.textContent = 'Clear';\n clear.addEventListener('click', () => log.clear());\n header.append(title, state, live, clear);\n\n const list = doc.createElement('ol');\n list.className = 'entries';\n\n const note = doc.createElement('p');\n note.className = 'note';\n\n panel.append(header, list, note);\n root.append(style, panel);\n host.appendChild(container);\n\n let visible = false;\n let scheduled = 0;\n\n const render = (): void => {\n const entries = log.entries;\n const pinned = log.pinnedTo;\n // Was the list at the bottom before the rebuild? A log that\n // scrolled itself while someone was reading an older step would be\n // unusable, so it only follows when it was already following.\n const following = list.scrollHeight - list.scrollTop - list.clientHeight < 4;\n\n state.textContent = pinned === null ? 'live' : `step ${pinned}`;\n state.className = pinned === null ? 'state' : 'state pinned';\n live.disabled = pinned === null;\n\n list.textContent = '';\n const origin = entries[0]?.at ?? 0;\n for (const entry of entries) {\n list.append(row(doc, entry, origin, pinned, seq => log.jumpTo(seq)));\n }\n\n if (entries.length === 0) {\n note.textContent = 'Nothing has crossed a tapped channel yet.';\n } else if (pinned === null) {\n note.textContent = 'Click a step to put the view back to what it showed then.';\n } else {\n note.textContent =\n 'The view is showing an earlier step. The application worker is not rewound: it is still ' +\n 'running, its patches are being recorded and held, and Live catches the view up to it.';\n }\n\n if (following) {\n list.scrollTop = list.scrollHeight;\n }\n };\n\n const schedule = (): void => {\n if (!visible || scheduled !== 0) {\n return;\n }\n // Coalesced, because a chatty channel can push several entries\n // between two frames and each one would otherwise rebuild the list.\n const draw = (): void => {\n scheduled = 0;\n render();\n };\n // Called on the view rather than through a captured reference:\n // `requestAnimationFrame` refuses to run detached from its window.\n scheduled = view === null ? (setTimeout(draw, 16) as unknown as number) : view.requestAnimationFrame(draw);\n };\n\n const unsubscribe = log.subscribe(schedule);\n\n return {\n setVisible(next) {\n visible = next;\n panel.hidden = !next;\n if (next) {\n render();\n }\n },\n get visible() {\n return visible;\n },\n dispose() {\n unsubscribe();\n container.remove();\n if (restore !== null) {\n host.style.position = restore;\n }\n }\n };\n}\n\n/**\n * One step.\n *\n * The direction glyph is the first thing on the line because it is the\n * first thing anyone wants: `↑` is a command the view sent, `↓` is\n * what came back. A log that printed both the same way would need to\n * be read to be understood, and this one is meant to be skimmed.\n */\nfunction row(\n doc: Document,\n entry: ActionEntry,\n origin: number,\n pinned: number | null,\n jump: (seq: number) => void\n): HTMLElement {\n const item = doc.createElement('li');\n item.className = `entry ${entry.kind}${entry.seq === pinned ? ' at' : ''}`;\n item.tabIndex = 0;\n item.setAttribute('role', 'button');\n\n const time = doc.createElement('span');\n time.className = 'time';\n time.textContent = formatOffset(entry.at - origin);\n\n const arrow = doc.createElement('span');\n arrow.className = 'arrow';\n arrow.textContent = actionGlyph(entry);\n\n const text = doc.createElement('span');\n text.className = 'text';\n text.textContent = describeActionEntry(entry);\n\n item.append(time, arrow, text);\n if (entry.cause !== undefined) {\n // The thread through the timeline: every row the same click\n // produced carries the same number, so a round trip across three\n // threads is read by looking for one glyph rather than by\n // comparing timestamps.\n const cause = doc.createElement('span');\n cause.className = 'cause';\n cause.textContent = `#${entry.cause.id}`;\n cause.title = entry.cause.label;\n item.append(cause);\n }\n const activate = (): void => jump(entry.seq);\n item.addEventListener('click', activate);\n item.addEventListener('keydown', event => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n activate();\n }\n });\n return item;\n}\n\n/**\n * The direction, as one glyph: up for what the view sent, down for\n * what came back, a square for the frame that drew it.\n */\nexport function actionGlyph(entry: ActionEntry): string {\n switch (entry.kind) {\n case 'command':\n return '↑';\n case 'patch':\n return '↓';\n case 'frame':\n return '▣';\n case 'error':\n return '!';\n }\n}\n\n/** One entry as a line: the command and its payload, the patched keys, the frame, or the error. */\nexport function describeActionEntry(entry: ActionEntry): string {\n switch (entry.kind) {\n case 'command':\n return `${entry.channel}.${entry.command}(${entry.payload === undefined ? '' : print(entry.payload)})`;\n case 'patch':\n return `${entry.channel} ${entry.keys.join(', ')}`;\n case 'frame':\n return `frame ${entry.frame}`;\n case 'error':\n return `${entry.channel} ${entry.message}`;\n }\n}\n\nfunction print(value: unknown): string {\n let text: string;\n try {\n text = JSON.stringify(value) ?? String(value);\n } catch {\n text = String(value);\n }\n return text.length > VALUE_CHARS ? `${text.slice(0, VALUE_CHARS - 1)}…` : text;\n}\n\nfunction formatOffset(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nconst STYLES = `\n:host { all: initial; }\n.panel {\n position: absolute;\n z-index: 2147482000;\n display: flex;\n flex-direction: column;\n width: 320px;\n max-height: 60%;\n margin: 12px;\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(13, 17, 23, 0.94);\n color: #e6edf3;\n font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n.panel[hidden] { display: none; }\n.top-left { top: 0; left: 0; }\n.top-right { top: 0; right: 0; }\n.bottom-left { bottom: 0; left: 0; }\n.bottom-right { bottom: 0; right: 0; }\nheader {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n.title {\n flex: 1;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: #8b949e;\n}\n.state { color: #56d364; }\n.state.pinned { color: #e3b341; }\nbutton {\n cursor: pointer;\n padding: 2px 8px;\n border-radius: 5px;\n border: 1px solid rgba(255, 255, 255, 0.16);\n background: #21262d;\n color: #e6edf3;\n font: inherit;\n}\nbutton:hover:not(:disabled) { background: #30363d; color: #fff; }\nbutton:disabled { cursor: default; opacity: 0.45; }\n.entries {\n flex: 1;\n overflow: auto;\n margin: 0;\n padding: 4px 0;\n list-style: none;\n}\n.entry {\n display: flex;\n gap: 6px;\n padding: 1px 10px;\n cursor: pointer;\n overflow-wrap: anywhere;\n}\n.entry:hover { background: rgba(110, 118, 129, 0.25); }\n.entry:focus-visible { outline: 1px solid #79c0ff; outline-offset: -1px; }\n.entry.at { background: rgba(227, 179, 65, 0.22); }\n.time { width: 44px; flex: none; text-align: right; color: #6e7681; }\n.arrow { width: 8px; flex: none; }\n.command .arrow { color: #d2a8ff; }\n.patch .arrow { color: #79c0ff; }\n.frame .arrow { color: #7ee787; }\n.frame .text { color: #8b949e; }\n.error .arrow { color: #ff7b72; }\n.error .text { color: #ff7b72; }\n.text { flex: 1; }\n.cause { flex: none; color: #ffa657; }\n.note {\n margin: 0;\n padding: 6px 10px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n color: #8b949e;\n}\n`;\n","import {\n markNow,\n measureSpan,\n performanceMarksEnabled,\n portHandle,\n type ChannelPort,\n type ShellToRuntimeMessage,\n type WorkerHandle\n} from 'gesso-framework';\nimport { forwardNewEntries, type ActionLog, type ActionLogToken } from './ActionLog';\n\n/**\n * The action log in the worker configuration, and the click at the\n * head of every chain it records.\n *\n * The action log recorded the gap and the reason for it. A channel's\n * ports are made where the replicas are, which in the worker\n * configuration is the render worker; the shell holds neither end and\n * never sees a patch, deliberately. So the recorder was written to run\n * in a worker — it imports no DOM and its entries are plain data — and\n * then nothing ran it there, because the two ways to wire it up were\n * \"one line in the render worker's entry\" and \"route every patch\n * through the main thread\", and the second would falsify the very\n * thing the route exists to show.\n *\n * This is that one line, made general. It stands on the wire the way\n * the recorder itself does, which is the rule `0047` set: **the tap\n * goes where the messages already are, never somewhere new.** In a\n * render worker three kinds of message go past one object, the\n * worker's global:\n *\n * - the shell's port to the application worker, which arrives with\n * `init` and is what a tapped channel has to be opened over;\n * - every input the shell forwards, which is where a cause begins;\n * - every frame the runtime reports, which is where one ends.\n *\n * Nothing here reaches into the runtime, and nothing in the framework\n * knows it exists. Used after `renderRoot`, whose constructor installs\n * the handler this wraps:\n *\n * const app = renderRoot(AppRoot).useService(Counter);\n * const actions = createActionLog();\n * const tap = tapRenderWorker(actions);\n * const data = tap.applicationWorker([Catalog, Cart]);\n * app.useChannel(Catalog, { worker: data }).useChannel(Cart, { worker: data });\n *\n * Guard it with `import.meta.env.DEV` or the equivalent: a tap in a\n * production bundle is a recorder holding patch batches for a session\n * nobody is watching.\n */\n\n/** The worker global, as far as the tap is concerned. */\nexport interface RenderWorkerHost {\n onmessage: ((event: { data: unknown }) => void) | null;\n postMessage(message: unknown, transfer?: Transferable[]): void;\n}\n\nexport interface RenderWorkerTapOptions {\n /** Where to stand. Default: the worker's own global. */\n readonly host?: RenderWorkerHost;\n /**\n * Whether entries are posted to the shell as devtools events, so a\n * panel outside the page shows them. Default true.\n *\n * The route a log in the page does not need: there the devtools hook\n * has the log itself to read.\n */\n readonly forward?: boolean;\n}\n\nexport interface RenderWorkerTap {\n /**\n * A handle on the application worker the shell supplied, with every\n * channel opened over it recorded.\n *\n * Registrations run before `init` and the shell's port arrives with\n * it, which is why this is a handle rather than a port: it is opened\n * when the runtime starts, by which time the port is here. Passing\n * it to `useChannel` is what replaces the `APPLICATION_WORKER`\n * placeholder the render worker would otherwise swap in.\n */\n applicationWorker(tokens: readonly ActionLogToken[]): WorkerHandle;\n dispose(): void;\n}\n\nexport function tapRenderWorker(log: ActionLog, options: RenderWorkerTapOptions = {}): RenderWorkerTap {\n const host = options.host ?? (globalThis as unknown as RenderWorkerHost);\n const previousOnMessage = host.onmessage;\n // Kept twice over: bound, to call, since the worker global's own\n // `postMessage` needs its receiver; and unbound, to put back, so\n // dispose leaves the property the function it found rather than a\n // wrapper of it.\n const originalPostMessage = host.postMessage;\n const previousPostMessage = originalPostMessage.bind(host);\n let application: WorkerHandle | undefined;\n let disposed = false;\n\n host.onmessage = event => {\n const message = event.data as ShellToRuntimeMessage | undefined;\n if (message?.type === 'init' && message.appPort !== undefined) {\n // The shell's end of the hub to the application worker. Held as\n // a handle, not used: a tap that opened a port of its own would\n // be a second conversation, and the point is to be in the middle\n // of the one that was going to happen anyway.\n application = portHandle(message.appPort);\n }\n const label = inputLabel(message);\n if (label === null) {\n previousOnMessage?.(event);\n return;\n }\n // The one moment a cause is known rather than inferred: a click's\n // listener runs inside this call, and the command it sends is sent\n // before it returns.\n const close = log.cause(label);\n const measuring = performanceMarksEnabled();\n const started = measuring ? markNow() : 0;\n try {\n previousOnMessage?.(event);\n } finally {\n close();\n if (measuring) {\n measureSpan(`input ${label}`, started, markNow());\n }\n }\n };\n\n host.postMessage = (message: unknown, transfer?: Transferable[]): void => {\n const frame = (message as { type?: unknown; frame?: unknown } | null)?.type === 'frame';\n if (frame && typeof (message as { frame: unknown }).frame === 'number') {\n // Posted at the end of the frame, so everything this frame drew\n // has already been recorded and the entry closes them.\n log.frame((message as { frame: number }).frame);\n }\n if (transfer === undefined) {\n previousPostMessage(message);\n return;\n }\n previousPostMessage(message, transfer);\n };\n\n const stopForwarding =\n options.forward === false\n ? () => {}\n : forwardNewEntries(log, entry => {\n previousPostMessage({ type: 'devtools', event: { kind: 'action', entry } });\n });\n\n return {\n applicationWorker(tokens) {\n return {\n open(key: string): MessagePort {\n if (application === undefined) {\n throw new Error(\n `Channel '${key}' was opened before the shell sent its application worker port. ` +\n 'tapRenderWorker must be called before renderRoot handles `init`, and the shell must ' +\n 'have been given an appLogicWorker.'\n );\n }\n const real = application.open(key);\n const token = tokens.find(candidate => candidate.name === key);\n if (token === undefined) {\n // Untapped rather than refused: a channel the caller did\n // not name still has to work, and a missing token would\n // make the log's reconstruction wrong rather than absent.\n return real;\n }\n // `tapPort` rather than `tap`, and the difference decides\n // whether a cause is exact. `tap` puts a relay\n // `MessageChannel` between the replica and the recorder, so\n // a command crosses it a task later, by which time the click\n // that sent it has been dispatched and the cause closed.\n // `tapPort` hands the replica a port whose `postMessage` is\n // a function call, so the command is recorded inside the\n // listener that sent it. The real port to the application\n // worker is still a real port, and everything crossing a\n // thread still crosses it.\n //\n // The cast is safe for the one consumer there is: the\n // channel registry takes what `open` returns and uses it as\n // a `ChannelPort`, and nothing transfers it.\n return log.tapPort(real as unknown as ChannelPort, token) as unknown as MessagePort;\n },\n get spawned(): boolean {\n return application !== undefined;\n },\n terminate(): void {\n // The shell owns that worker's lifetime, as `portHandle`\n // says; a tap in the middle owns even less of it.\n }\n };\n },\n dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n stopForwarding();\n host.onmessage = previousOnMessage;\n host.postMessage = originalPostMessage;\n }\n };\n}\n\n/**\n * What to call the cause an input starts, or null for a message that\n * is not an input.\n *\n * Position is in the label because two presses in different places are\n * two different causes to the person who made them, and the ids alone\n * do not say which was which.\n */\nexport function inputLabel(message: ShellToRuntimeMessage | undefined): string | null {\n switch (message?.type) {\n case 'pointerDown':\n case 'pointerUp':\n case 'pointerMove':\n return `${message.type} (${Math.round(message.x)}, ${Math.round(message.y)})`;\n case 'pointerCancel':\n return 'pointerCancel';\n case 'wheel':\n return `wheel (${Math.round(message.deltaX)}, ${Math.round(message.deltaY)})`;\n case 'keyDown':\n return `keyDown ${message.key}`;\n case 'keyUp':\n return `keyUp ${message.key}`;\n case 'beforeInput':\n return `beforeInput ${message.inputType}`;\n case 'paste':\n return 'paste';\n case 'semanticsAction':\n return `semantics ${message.action.action}`;\n default:\n // Everything else the shell sends is lifecycle, not input:\n // a resize, a tick, a visibility change. None of them is\n // something a person did.\n return null;\n }\n}\n","/**\n * Click a node on the canvas to pin it in the panel\n * (the first of the two things the node inspector deferred).\n *\n * Picking was left out because the panel could not name a node back\n * to the runtime. The devtools panel built that half: `select` and\n * `highlight` address a node by id, and the inspector already reports\n * the node under the pointer while it is on. What was still missing is\n * the click, and a click is the one part of this that cannot happen in\n * the render thread. By the time the runtime has an event, the event\n * has been dispatched; taking it back would mean asking the\n * application to forget a press it may already have acted on.\n *\n * So the pick happens where the click arrives, in the capture phase,\n * over the element the canvas is in. That is the shell doing what the\n * shell does — forwarding input, or in this case declining to\n * — and it costs nothing while picking is off,\n * because the listeners are attached only then.\n *\n * What it pins is whatever the inspector last reported as hovered, so\n * a picker needs the inspector on; the panel turns it on with the same\n * toggle.\n */\n\nexport interface NodePickerOptions {\n /**\n * The element to take clicks over, in the capture phase. The one the\n * application is mounted in, so the canvas is inside it.\n */\n readonly host: EventTarget;\n /** The node under the pointer, from the runtime's `hover` reports. */\n hovered(): string | null;\n}\n\n/** What a panel drives: arm it, and hear what was picked. */\nexport interface DevtoolsPicker {\n /** Whether clicking the canvas pins a node instead of reaching the application. */\n setEnabled(enabled: boolean): void;\n readonly enabled: boolean;\n /** Called with the id of the node picked. Pass null to stop listening. */\n onPick(listener: ((id: string) => void) | null): void;\n dispose(): void;\n}\n\n/** The events a pick has to swallow for the application not to see it. */\nconst SWALLOWED = ['pointerdown', 'pointerup', 'mousedown', 'mouseup', 'click'] as const;\n\nexport function createNodePicker(options: NodePickerOptions): DevtoolsPicker {\n let listener: ((id: string) => void) | null = null;\n let armed = false;\n let attached = false;\n\n const swallow = (event: Event): void => {\n // Every one of them, and not only the one that carries the\n // decision. The shell forwards `pointerdown` and `pointerup`\n // separately, so taking one of the pair would leave the\n // application with half a press, which is worse than either\n // letting it through or taking it whole.\n event.preventDefault();\n event.stopImmediatePropagation();\n if (event.type !== 'pointerup') {\n return;\n }\n const id = options.hovered();\n if (id !== null) {\n listener?.(id);\n }\n };\n\n const attach = (): void => {\n if (attached) {\n return;\n }\n attached = true;\n for (const type of SWALLOWED) {\n options.host.addEventListener(type, swallow, true);\n }\n };\n const detach = (): void => {\n if (!attached) {\n return;\n }\n attached = false;\n for (const type of SWALLOWED) {\n options.host.removeEventListener(type, swallow, true);\n }\n };\n\n return {\n setEnabled(enabled) {\n armed = enabled;\n if (enabled) {\n attach();\n } else {\n detach();\n }\n },\n get enabled() {\n return armed;\n },\n onPick(next) {\n listener = next;\n },\n dispose() {\n detach();\n listener = null;\n armed = false;\n }\n };\n}\n","import { formatAge, type UiNodeReport, type UiPropReport } from 'gesso-framework';\n\nexport interface NodeReportViewOptions {\n /**\n * Makes the props editable, calling this with the new value when one\n * is committed; `null` means \"remove it\", which puts an inherited\n * value back.\n *\n * Absent for a read-only view. The corner inspector passes nothing,\n * because it sets `pointer-events: none` and could not be typed into\n * anyway.\n */\n onEditProp?(name: string, value: unknown): void;\n}\n\n/**\n * A `UiNodeReport` as DOM: the node inspector's body, shared with the\n * devtools panel so a node read in a corner of the canvas and a node\n * picked from a tree are described in the same words.\n */\nexport function renderNodeReport(\n doc: Document,\n report: UiNodeReport,\n options: NodeReportViewOptions = {}\n): HTMLElement[] {\n const out: HTMLElement[] = [\n heading(doc, `${report.type} ${report.id}`),\n line(\n doc,\n 'box',\n `${round(report.box.width)} × ${round(report.box.height)} at (${round(report.box.x)}, ${round(report.box.y)})`\n )\n ];\n if (report.owners.length > 0) {\n out.push(line(doc, 'rendered by', report.owners.map(owner => owner.name).join(' inside ')));\n }\n if (report.modifiers.length > 0) {\n out.push(line(doc, 'modifiers', report.modifiers.join(', ')));\n }\n if (report.listens.length > 0) {\n out.push(line(doc, 'listens', report.listens.join(', ')));\n }\n if (report.beneath.length > 0) {\n // The answer to a dead click: this node took the press, and these\n // are the nodes it covers at the pointer, topmost first.\n out.push(\n line(\n doc,\n 'beneath',\n report.beneath\n .map(under => {\n const owner = under.owner === undefined ? '' : ` (${under.owner})`;\n const listens = under.listens.length === 0 ? '' : ` · ${under.listens.join(', ')}`;\n return `${under.type}${owner}${listens}`;\n })\n .join('; ')\n )\n );\n }\n if (report.semantics !== undefined) {\n const parts = [\n report.semantics.role,\n report.semantics.label === undefined ? undefined : `\"${report.semantics.label}\"`,\n report.semantics.value,\n report.semantics.states?.join(', ')\n ].filter(part => part !== undefined && part !== '');\n if (parts.length > 0) {\n out.push(line(doc, 'semantics', parts.join(' · ')));\n }\n }\n if (report.props.length > 0) {\n out.push(section(doc, 'props'), propList(doc, report.props, options));\n }\n if (report.environment.length > 0) {\n const list = doc.createElement('dl');\n list.className = 'rows';\n for (const entry of report.environment) {\n const term = doc.createElement('dt');\n term.textContent = entry.key;\n term.className = entry.provided ? 'provided' : '';\n const value = doc.createElement('dd');\n value.textContent = entry.value;\n if (entry.provided) {\n value.append(note(doc, 'provided here'));\n }\n list.append(term, value);\n }\n out.push(section(doc, 'environment'), list);\n }\n const explanation = doc.createElement('pre');\n explanation.className = 'explanation';\n explanation.textContent = report.explanation;\n out.push(section(doc, 'layout'), explanation);\n return out;\n}\n\nfunction heading(doc: Document, text: string): HTMLElement {\n const element = doc.createElement('h1');\n element.textContent = text;\n return element;\n}\n\nfunction section(doc: Document, text: string): HTMLElement {\n const element = doc.createElement('h2');\n element.textContent = text;\n return element;\n}\n\nfunction line(doc: Document, label: string, value: string): HTMLElement {\n const element = doc.createElement('p');\n const name = doc.createElement('span');\n name.className = 'label';\n name.textContent = `${label} `;\n element.append(name, doc.createTextNode(value));\n return element;\n}\n\nfunction note(doc: Document, text: string): HTMLElement {\n const element = doc.createElement('span');\n element.className = 'note';\n element.textContent = ` ${text}`;\n return element;\n}\n\n/**\n * The props, with the ones a modifier or a binding is driving marked.\n *\n * The mark is the point of the list. A declared value and a value a\n * modifier wrote over it look identical on the node, and the second is\n * the one that surprises people: B1's cascade is invisible until\n * something says which of the two is on screen.\n */\nfunction propList(doc: Document, props: readonly UiPropReport[], options: NodeReportViewOptions): HTMLElement {\n const list = doc.createElement('dl');\n list.className = 'rows';\n for (const prop of props) {\n const term = doc.createElement('dt');\n term.textContent = prop.name;\n term.className = prop.origin;\n const value = doc.createElement('dd');\n value.textContent = prop.value;\n if (prop.source !== undefined) {\n value.append(note(doc, prop.source));\n }\n if (prop.stream !== undefined) {\n // The three things a \"bound\" mark never said: what the stream\n // last produced, how many values it has produced, and how long\n // ago. A prop that is not updating and a prop whose stream has\n // said nothing since the screen was built look identical\n // without them.\n const stream = doc.createElement('span');\n stream.className = 'stream';\n const emittedAt = prop.stream.emittedAt;\n const age = emittedAt === null ? 'no value yet' : `${formatAge(Math.max(0, Date.now() - emittedAt))} ago`;\n const count = `${prop.stream.emissions}×`;\n stream.textContent = `${count} · ${age}${prop.stream.connected ? '' : ' · disconnected'}`;\n stream.title = `${prop.stream.source} last emitted ${prop.stream.value}`;\n value.append(stream);\n }\n if (options.onEditProp !== undefined) {\n makeEditable(doc, value, prop, options.onEditProp);\n }\n list.append(term, value);\n }\n return list;\n}\n\n/**\n * Turns a prop's value into a field on a click, and writes it back on\n * Enter.\n *\n * Typed rather than stepped, because the values are of every kind a\n * property can hold and a spinner would only serve the numbers. The\n * text is read as JSON when it parses and as a plain string when it\n * does not, so `12`, `\"#ff0000\"`, `#ff0000` and `[8, 4]` all mean what\n * they look like. Empty removes the property.\n *\n * Escape and blur both cancel: this writes into a running application,\n * and a value committed by looking away is not what anyone meant.\n */\nfunction makeEditable(\n doc: Document,\n cell: HTMLElement,\n prop: UiPropReport,\n commit: (name: string, value: unknown) => void\n): void {\n cell.classList.add('editable');\n cell.title = 'Click to edit';\n cell.addEventListener('click', () => {\n if (cell.querySelector('input') !== null) {\n return;\n }\n const field = doc.createElement('input');\n field.type = 'text';\n field.className = 'edit';\n field.value = prop.value;\n cell.textContent = '';\n cell.append(field);\n field.focus();\n field.select();\n const cancel = (): void => {\n cell.textContent = prop.value;\n };\n field.addEventListener('keydown', event => {\n if (event.key === 'Escape') {\n cancel();\n return;\n }\n if (event.key !== 'Enter') {\n return;\n }\n const text = field.value.trim();\n commit(prop.name, text === '' ? null : parseValue(text));\n });\n field.addEventListener('blur', cancel);\n });\n}\n\n/** JSON where it parses, the text itself where it does not. */\nfunction parseValue(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction round(value: number): string {\n return Number.isInteger(value) ? String(value) : value.toFixed(2);\n}\n\n/**\n * The rules the report's elements use, for any stylesheet that shows one.\n *\n * Colours are the devtools panel's custom properties with the dark\n * palette as fallback, so the corner inspector, which defines none of\n * them, is unchanged, and the panel recolours the same report by\n * defining them.\n */\nexport const NODE_REPORT_STYLES = `\nh1 { margin: 0 0 6px; font-size: 12px; color: var(--gd-accent, #79c0ff); overflow-wrap: anywhere; }\nh2 {\n margin: 10px 0 4px;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--gd-muted, #8b949e);\n}\np { margin: 0 0 2px; }\n.label { color: var(--gd-muted, #8b949e); }\n.rows { display: grid; grid-template-columns: auto 1fr; gap: 0 8px; margin: 0; }\ndt { color: var(--gd-muted, #8b949e); overflow-wrap: anywhere; }\ndt.modifier { color: var(--gd-purple, #d2a8ff); }\ndt.binding { color: var(--gd-green, #7ee787); }\ndt.provided { color: var(--gd-orange, #ffa657); }\ndd { margin: 0; overflow-wrap: anywhere; }\ndd.editable { cursor: pointer; border-radius: 3px; }\ndd.editable:hover { background: var(--gd-bg-hover, #21262d); }\n.edit {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid var(--gd-accent, #79c0ff);\n border-radius: 3px;\n padding: 0 3px;\n background: var(--gd-bg, #0d1117);\n color: var(--gd-text, #e6edf3);\n font: inherit;\n}\n.note { color: var(--gd-faint, #6e7681); }\n.stream { display: block; color: var(--gd-green, #7ee787); }\n.explanation { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--gd-text-strong, #c9d1d9); }\n`;\n","import type { DevtoolsEvent, DevtoolsRequest } from 'gesso-framework';\nimport type { ActionEntry } from './ActionLog';\n\n/**\n * What a devtools panel and the page it inspects say to each other\n *.\n *\n * The framework's `DevtoolsRequest` and `DevtoolsEvent` are one\n * application's vocabulary. A page may run several (a documentation\n * site's examples), and a panel arrives after they started, so this\n * layer adds the two things the framework's protocol does not have: an\n * application id on every message, and a greeting that answers with\n * the list.\n *\n * Both sides speak through a `DevtoolsPort`, which is only `post` and\n * `onMessage`. Two ports are provided here: a pair joined in memory,\n * for a panel mounted in the same page and for tests, and one over\n * `window.postMessage`, which is how a browser extension's content\n * script reaches a page. The extension's own hop, from its content\n * script to its devtools page, is one more port of the same shape and\n * lives with the extension.\n */\n\nexport interface DevtoolsAppInfo {\n readonly id: string;\n readonly name: string;\n}\n\n/** Page to panel. */\nexport type PageMessage =\n /** The applications the page has connected; sent on `hello` and whenever the list changes. */\n | { type: 'apps'; apps: readonly DevtoolsAppInfo[] }\n | { type: 'event'; app: string; event: DevtoolsEvent }\n /** A store action log entry, from an `ActionLog` the application connected alongside itself. */\n | { type: 'action'; app: string; entry: ActionEntry }\n /**\n * A node the person clicked on the canvas while the panel was\n * picking. The panel selects it; the click never reaches the\n * application.\n */\n | { type: 'picked'; app: string; id: string };\n\n/** Panel to page. */\nexport type PanelMessage =\n /** \"Is anyone there\": answered with `apps`. */\n | { type: 'hello' }\n | { type: 'request'; app: string; request: DevtoolsRequest }\n /**\n * Turns click-to-pick on or off.\n *\n * Not a `DevtoolsRequest`, because it is not a question for the\n * runtime. Picking is a click that must be taken before the\n * application sees it, and the only place a click can be taken is\n * the page, where it arrives; the runtime is a thread away and would\n * have to be asked to un-dispatch something. So the page answers\n * this one, and the runtime is asked to `select` the node the page\n * names, which is a question it already answers.\n */\n | { type: 'pick'; app: string; enabled: boolean };\n\n/** One end of a conversation: what it hears and what it says. */\nexport interface DevtoolsPort<In, Out> {\n post(message: Out): void;\n /** Returns a function that stops listening. */\n onMessage(listener: (message: In) => void): () => void;\n /** Drops every listener and stops posting. */\n close(): void;\n}\n\n/** The page's end. */\nexport type PagePort = DevtoolsPort<PanelMessage, PageMessage>;\n/** The panel's end. */\nexport type PanelPort = DevtoolsPort<PageMessage, PanelMessage>;\n\n/**\n * Two ports joined in memory, delivering synchronously.\n *\n * For a panel mounted in the page it inspects, and for specs: the\n * hook and the panel are exercised end to end with nothing between\n * them but a function call.\n */\nexport function createDirectPorts(): { page: PagePort; panel: PanelPort } {\n const toPanel = new Set<(message: PageMessage) => void>();\n const toPage = new Set<(message: PanelMessage) => void>();\n let open = true;\n const page: PagePort = {\n post(message) {\n if (open) {\n for (const listener of Array.from(toPanel)) {\n listener(message);\n }\n }\n },\n onMessage(listener) {\n toPage.add(listener);\n return () => toPage.delete(listener);\n },\n close() {\n open = false;\n toPage.clear();\n }\n };\n const panel: PanelPort = {\n post(message) {\n if (open) {\n for (const listener of Array.from(toPage)) {\n listener(message);\n }\n }\n },\n onMessage(listener) {\n toPanel.add(listener);\n return () => toPanel.delete(listener);\n },\n close() {\n open = false;\n toPanel.clear();\n }\n };\n return { page, panel };\n}\n\n/** What a message carries over `window.postMessage`, so both sides can ignore everything else on the window. */\nexport interface Envelope<T> {\n readonly source: typeof ENVELOPE_SOURCE;\n /** Who it is for. A page ignores what it sent, and so does a panel. */\n readonly to: 'page' | 'panel';\n readonly message: T;\n}\n\nexport const ENVELOPE_SOURCE = 'gesso-devtools';\n\nexport function isEnvelope(value: unknown, to: 'page' | 'panel'): value is Envelope<unknown> {\n const envelope = value as { source?: unknown; to?: unknown; message?: unknown } | null;\n return (\n typeof envelope === 'object' &&\n envelope !== null &&\n envelope.source === ENVELOPE_SOURCE &&\n envelope.to === to &&\n 'message' in envelope\n );\n}\n\n/** The part of `Window` the transport uses, so a spec can hand it a plain object. */\nexport interface WindowLike {\n addEventListener(type: 'message', listener: (event: { data: unknown; source?: unknown }) => void): void;\n removeEventListener(type: 'message', listener: (event: { data: unknown; source?: unknown }) => void): void;\n postMessage(message: unknown, targetOrigin: string): void;\n readonly location?: { readonly origin: string };\n}\n\n/**\n * The page's end of a conversation over its own window.\n *\n * A content script shares the page's window but not its JavaScript, so\n * `window.postMessage` to the page's own origin is the one channel\n * they have. Messages are posted to that origin rather than to `*`,\n * and the page hears only envelopes addressed to it, so its own posts\n * come straight back through the same listener and are dropped.\n */\nexport function windowPagePort(win: WindowLike): PagePort {\n return windowPort<PanelMessage, PageMessage>(win, 'page', 'panel');\n}\n\n/** The other end, for whatever sits in the page beside it (an extension's content script). */\nexport function windowPanelPort(win: WindowLike): PanelPort {\n return windowPort<PageMessage, PanelMessage>(win, 'panel', 'page');\n}\n\nfunction windowPort<In, Out>(win: WindowLike, me: 'page' | 'panel', them: 'page' | 'panel'): DevtoolsPort<In, Out> {\n const listeners = new Set<(message: In) => void>();\n const origin = win.location?.origin;\n // `file:` and sandboxed pages have an opaque origin, which postMessage\n // refuses as a target; `*` is the only address they answer to.\n const target = origin === undefined || origin === 'null' || origin === '' ? '*' : origin;\n const handle = (event: { data: unknown }): void => {\n if (!isEnvelope(event.data, me)) {\n return;\n }\n const message = event.data.message as In;\n for (const listener of Array.from(listeners)) {\n listener(message);\n }\n };\n win.addEventListener('message', handle);\n let open = true;\n return {\n post(message) {\n if (open) {\n const envelope: Envelope<Out> = { source: ENVELOPE_SOURCE, to: them, message };\n win.postMessage(envelope, target);\n }\n },\n onMessage(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n close() {\n open = false;\n listeners.clear();\n win.removeEventListener('message', handle);\n }\n };\n}\n","import type { DevtoolsEvent, DevtoolsRequest } from 'gesso-framework';\nimport { forwardNewEntries, type ActionLog } from './ActionLog';\nimport type { DevtoolsPicker } from './NodePicker';\nimport {\n windowPagePort,\n type DevtoolsAppInfo,\n type PagePort,\n type PanelMessage,\n type WindowLike\n} from './PanelProtocol';\n\n/**\n * The page's side of the devtools panel.\n *\n * One object per page, kept on the window under a well-known name the\n * way React's devtools hook is, so that several bundles (a docs site's\n * examples, an application and its dependency) connect to the same\n * hook and a panel sees one list. Applications register with it;\n * panels attach ports to it; the hook routes requests to the right\n * application and fans every application's events out to every port.\n *\n * `connectDevtools(app)` is the one line an application adds. It also\n * installs the `window.postMessage` transport the first time, which is\n * how a browser extension's content script finds the page: nothing is\n * running in a page that has not connected, and a page that has\n * connected answers `hello`.\n */\n\n/** What an application must offer: both `WorkerApp` and `GessoApp` do. */\nexport interface DevtoolsApp {\n devtools(request: DevtoolsRequest): void;\n /**\n * Taken over by the hook while connected. An application that was\n * listening to its own devtools events has to choose: the panel or\n * itself. In practice the events only exist for panels.\n */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void;\n}\n\nexport interface ConnectDevtoolsOptions {\n /** What the panel calls this application. Default: the document title, else `app`. */\n readonly name?: string;\n /**\n * A store action log the panel should show alongside. The log stays\n * where it is (it taps ports in the page); the panel is sent each\n * entry as it is recorded.\n */\n readonly actions?: ActionLog;\n /**\n * Lets the panel pick a node by clicking the canvas.\n *\n * The page's half of picking, because a click has to be taken before\n * the application sees it and only the page has it in time. See\n * `createNodePicker`.\n */\n readonly picker?: DevtoolsPicker;\n /**\n * Where the hook lives and where the `postMessage` transport\n * listens. Default: the global window. `null` keeps the hook off any\n * window and installs no transport, for a panel mounted directly.\n */\n readonly window?: (WindowLike & HookHost) | null;\n}\n\nexport interface DevtoolsHook {\n /** The connected applications, in the order they connected. */\n readonly apps: readonly DevtoolsAppInfo[];\n /** Connects an application. Returns a function that disconnects it. */\n register(app: DevtoolsApp, options?: Omit<ConnectDevtoolsOptions, 'window'>): () => void;\n /** Attaches a panel's port. Returns a function that detaches it. */\n attach(port: PagePort): () => void;\n}\n\n/** The property the hook is kept under. */\nexport const HOOK_PROPERTY = '__GESSO_DEVTOOLS__';\n\n/** A window, as far as the hook is concerned: somewhere to keep itself. */\nexport interface HookHost {\n [HOOK_PROPERTY]?: DevtoolsHook;\n}\n\ninterface Registered {\n readonly info: DevtoolsAppInfo;\n readonly app: DevtoolsApp;\n readonly picker: DevtoolsPicker | undefined;\n readonly release: () => void;\n}\n\n/**\n * The page's hook, created on first use.\n *\n * With a window, the hook is stored on it and the `postMessage`\n * transport is attached once; without one (`null`), a fresh hook with\n * no transport, for specs and for a panel in the same page.\n */\nexport function getDevtoolsHook(win: (WindowLike & HookHost) | null = defaultWindow()): DevtoolsHook {\n if (win === null) {\n return createHook();\n }\n const existing = win[HOOK_PROPERTY];\n if (existing !== undefined) {\n return existing;\n }\n const hook = createHook();\n win[HOOK_PROPERTY] = hook;\n hook.attach(windowPagePort(win));\n return hook;\n}\n\n/**\n * Connects an application to the page's devtools hook, so a panel can\n * find it. Returns a function that disconnects it; call it when the\n * application is disposed.\n */\nexport function connectDevtools(app: DevtoolsApp, options: ConnectDevtoolsOptions = {}): () => void {\n const { window: win, ...rest } = options;\n return getDevtoolsHook(win).register(app, rest);\n}\n\nfunction createHook(): DevtoolsHook {\n const registered = new Map<string, Registered>();\n const ports = new Set<PagePort>();\n let nextId = 1;\n\n const infos = (): DevtoolsAppInfo[] => [...registered.values()].map(entry => entry.info);\n const broadcast = (message: Parameters<PagePort['post']>[0]): void => {\n for (const port of Array.from(ports)) {\n port.post(message);\n }\n };\n\n const handle = (message: PanelMessage, from: PagePort): void => {\n if (message.type === 'hello') {\n from.post({ type: 'apps', apps: infos() });\n return;\n }\n if (message.type === 'pick') {\n // An application registered without a picker simply cannot be\n // picked from; the panel's toggle then does nothing, which is\n // better than the panel refusing to show the toggle to some\n // applications and not others.\n registered.get(message.app)?.picker?.setEnabled(message.enabled);\n return;\n }\n // A request for an application that has gone is dropped: the panel\n // has been sent the new list and will catch up.\n registered.get(message.app)?.app.devtools(message.request);\n };\n\n return {\n get apps() {\n return infos();\n },\n register(app, options = {}) {\n const id = `app-${nextId++}`;\n const info: DevtoolsAppInfo = { id, name: options.name ?? defaultName() };\n app.onDevtools(event => broadcast({ type: 'event', app: id, event }));\n const stopActions = forwardActions(options.actions, entry => broadcast({ type: 'action', app: id, entry }));\n const picker = options.picker;\n picker?.onPick(picked => broadcast({ type: 'picked', app: id, id: picked }));\n const release = (): void => {\n app.onDevtools(null);\n stopActions();\n picker?.onPick(null);\n picker?.setEnabled(false);\n };\n registered.set(id, { info, app, picker, release });\n broadcast({ type: 'apps', apps: infos() });\n return () => {\n const entry = registered.get(id);\n if (entry === undefined) {\n return;\n }\n registered.delete(id);\n entry.release();\n broadcast({ type: 'apps', apps: infos() });\n };\n },\n attach(port) {\n ports.add(port);\n const stop = port.onMessage(message => handle(message, port));\n return () => {\n stop();\n ports.delete(port);\n };\n }\n };\n}\n\n/** Sends each new action log entry as it is recorded, when there is a log to read. */\nfunction forwardActions(log: ActionLog | undefined, send: (entry: ActionLog['entries'][number]) => void): () => void {\n return log === undefined ? () => {} : forwardNewEntries(log, send);\n}\n\nfunction defaultWindow(): (WindowLike & HookHost) | null {\n return typeof window === 'undefined' ? null : (window as unknown as WindowLike & HookHost);\n}\n\nfunction defaultName(): string {\n if (typeof document !== 'undefined' && document.title !== '') {\n return document.title;\n }\n return 'app';\n}\n","import { UI_FRAME_PHASES, type FrameMetrics, type UiFramePhase } from 'gesso-framework';\n\n/**\n * The frame profiler: where a frame's time went, over\n * the last few seconds, as a picture.\n *\n * The timings have existed since L7 and the playground has been showing\n * them as one clipped line of text, which is enough to read a number\n * off and not enough to see a shape. A stall, a phase that only wakes\n * on some frames, a render that grew when a route changed: all three\n * are obvious in a strip of bars and invisible in a running average.\n *\n * It draws into a small canvas rather than a div per bar, because at 60\n * frames a second a DOM per frame is more main-thread work than the\n * thing being profiled. For the same reason it redraws on a timer\n * rather than on every frame: the history is kept per frame, the\n * picture is repainted a few times a second.\n */\nexport interface FrameProfiler {\n /** Feed it every frame. Cheap: it appends and returns. */\n report(metrics: FrameMetrics): void;\n /** Shows or hides the panel. Hidden panels stop redrawing. */\n setVisible(visible: boolean): void;\n readonly visible: boolean;\n dispose(): void;\n}\n\nexport interface FrameProfilerOptions {\n /** How many frames the strip holds. Default 180, about three seconds. */\n readonly history?: number;\n /** How often the picture is repainted, in milliseconds. Default 250. */\n readonly redrawMs?: number;\n readonly corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n /**\n * `floating` (the default) sits in a corner over the application;\n * `docked` fills its host, for a panel that is not over anything.\n */\n readonly layout?: 'floating' | 'docked';\n}\n\n/** One bar's worth of history. */\nexport interface FrameSample {\n readonly phases: Readonly<Record<UiFramePhase, number>>;\n readonly total: number;\n /** Gap since the previous frame, on the rendering thread's clock. */\n readonly gap: number;\n readonly input: number | null;\n}\n\n/**\n * A colour per phase.\n *\n * Ordered as the phases run, and deliberately not a gradient: the\n * question a strip answers is \"which phase is that\", so the bands have\n * to be told apart at four pixels wide.\n */\nconst PHASE_COLORS: Record<UiFramePhase, string> = {\n ticks: '#d2a8ff',\n patches: '#79c0ff',\n environment: '#56d364',\n virtualize: '#e3b341',\n layout: '#f0883e',\n semantics: '#ff7b72',\n render: '#a5d6ff'\n};\n\n/** Above this, a frame missed a 60 Hz deadline. */\nconst BUDGET_MS = 16.7;\n\nexport function mountFrameProfiler(host: HTMLElement, options: FrameProfilerOptions = {}): FrameProfiler {\n const doc = host.ownerDocument;\n const view = doc.defaultView;\n const capacity = options.history ?? 180;\n const redrawMs = options.redrawMs ?? 250;\n\n const docked = options.layout === 'docked';\n const position = docked ? 'relative' : (view?.getComputedStyle(host).position ?? 'static');\n const restore = position === 'static' ? host.style.position : null;\n if (position === 'static') {\n host.style.position = 'relative';\n }\n\n const container = doc.createElement('div');\n const root = container.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = STYLES;\n const panel = doc.createElement('div');\n panel.className = docked ? 'panel docked' : `panel ${options.corner ?? 'top-right'}`;\n panel.hidden = true;\n const readout = doc.createElement('div');\n readout.className = 'readout';\n const strip = doc.createElement('canvas');\n strip.className = 'strip';\n strip.width = capacity * 2;\n strip.height = 96;\n const legend = doc.createElement('div');\n legend.className = 'legend';\n panel.append(readout, strip, legend);\n root.append(style, panel);\n host.appendChild(container);\n\n const samples: FrameSample[] = [];\n let previousAt: number | null = null;\n let visible = false;\n let timer: ReturnType<typeof setInterval> | null = null;\n\n const draw = (): void => {\n const ctx = strip.getContext('2d');\n if (ctx === null || samples.length === 0) {\n return;\n }\n const width = strip.width;\n const height = strip.height;\n ctx.clearRect(0, 0, width, height);\n // The scale follows the worst frame in view rather than the budget,\n // so a strip of 40 ms frames is still legible; the budget line says\n // where 60 Hz was.\n const peak = Math.max(BUDGET_MS, ...samples.map(sample => sample.total));\n const barWidth = width / capacity;\n samples.forEach((sample, index) => {\n const x = index * barWidth;\n let y = height;\n for (const phase of UI_FRAME_PHASES) {\n const ms = sample.phases[phase];\n if (ms <= 0) {\n continue;\n }\n const bar = (ms / peak) * height;\n ctx.fillStyle = PHASE_COLORS[phase];\n ctx.fillRect(x, y - bar, Math.max(1, barWidth - 0.5), bar);\n y -= bar;\n }\n });\n const budgetY = height - (BUDGET_MS / peak) * height;\n // The panel's token when docked in one, else the floating panel's own.\n ctx.strokeStyle =\n view?.getComputedStyle(panel).getPropertyValue('--gd-budget').trim() || 'rgba(255, 255, 255, 0.35)';\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(0, budgetY + 0.5);\n ctx.lineTo(width, budgetY + 0.5);\n ctx.stroke();\n\n const totals = summarize(samples);\n readout.textContent = '';\n readout.append(\n stat(doc, 'fps', totals.fps.toFixed(0)),\n stat(doc, 'frame', `${totals.meanTotal.toFixed(2)} ms`),\n stat(doc, 'worst', `${totals.worstTotal.toFixed(1)} ms`),\n stat(doc, 'gap', `${totals.worstGap.toFixed(0)} ms`),\n stat(doc, 'input', totals.worstInput === null ? '—' : `${totals.worstInput.toFixed(0)} ms`),\n stat(doc, 'peak', `${peak.toFixed(1)} ms`)\n );\n legend.textContent = '';\n for (const phase of UI_FRAME_PHASES) {\n const worst = totals.worstPhase[phase];\n // A phase that never had work in the whole window is dropped\n // rather than printed as a zero: `L7` made \"0 means it never ran\"\n // true, and a legend full of zeroes hides the two that matter.\n if (worst <= 0) {\n continue;\n }\n const entry = doc.createElement('span');\n entry.className = 'entry';\n const swatch = doc.createElement('i');\n swatch.style.background = PHASE_COLORS[phase];\n entry.append(swatch, doc.createTextNode(`${phase} ${worst.toFixed(2)}`));\n legend.append(entry);\n }\n };\n\n return {\n report(metrics) {\n const gap = previousAt === null ? 0 : metrics.at - previousAt;\n previousAt = metrics.at;\n samples.push({\n phases: metrics.phases,\n total: metrics.durationMs,\n gap,\n input: metrics.inputLatencyMs\n });\n if (samples.length > capacity) {\n samples.splice(0, samples.length - capacity);\n }\n },\n get visible() {\n return visible;\n },\n setVisible(next) {\n if (visible === next) {\n return;\n }\n visible = next;\n panel.hidden = !next;\n if (next) {\n draw();\n timer = setInterval(draw, redrawMs);\n } else if (timer !== null) {\n clearInterval(timer);\n timer = null;\n }\n },\n dispose() {\n if (timer !== null) {\n clearInterval(timer);\n }\n container.remove();\n if (restore !== null) {\n host.style.position = restore;\n }\n }\n };\n}\n\nexport interface FrameSummary {\n readonly fps: number;\n readonly meanTotal: number;\n readonly worstTotal: number;\n readonly worstGap: number;\n readonly worstInput: number | null;\n readonly worstPhase: Record<UiFramePhase, number>;\n}\n\n/**\n * The window's numbers.\n *\n * Peak rather than mean for the phases, for the reason the playground's\n * status line already gives: patches and environment run on a small\n * minority of frames, so a mean would report them as idle on exactly\n * the frames where they were the cost.\n */\nexport function summarize(samples: readonly FrameSample[]): FrameSummary {\n const worstPhase = { ticks: 0, patches: 0, environment: 0, virtualize: 0, layout: 0, semantics: 0, render: 0 };\n let totalSum = 0;\n let worstTotal = 0;\n let worstGap = 0;\n let worstInput: number | null = null;\n let gapSum = 0;\n let gaps = 0;\n for (const sample of samples) {\n totalSum += sample.total;\n worstTotal = Math.max(worstTotal, sample.total);\n if (sample.gap > 0) {\n worstGap = Math.max(worstGap, sample.gap);\n gapSum += sample.gap;\n gaps++;\n }\n if (sample.input !== null) {\n worstInput = Math.max(worstInput ?? 0, sample.input);\n }\n for (const phase of UI_FRAME_PHASES) {\n worstPhase[phase] = Math.max(worstPhase[phase], sample.phases[phase]);\n }\n }\n return {\n // From the gaps between frames rather than from a wall clock: the\n // profiler may be fed from a worker, where the only honest measure\n // of when a frame happened is the timestamp that frame carries.\n fps: gaps === 0 ? 0 : 1000 / (gapSum / gaps),\n meanTotal: samples.length === 0 ? 0 : totalSum / samples.length,\n worstTotal,\n worstGap,\n worstInput,\n worstPhase\n };\n}\n\nfunction stat(doc: Document, label: string, value: string): HTMLElement {\n const element = doc.createElement('span');\n element.className = 'stat';\n const name = doc.createElement('i');\n name.textContent = label;\n element.append(name, doc.createTextNode(value));\n return element;\n}\n\nconst STYLES = `\n:host { all: initial; }\n.panel {\n position: absolute;\n z-index: 2147482000;\n width: 300px;\n pointer-events: none;\n margin: 12px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(13, 17, 23, 0.94);\n color: #e6edf3;\n font: 10px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n.panel[hidden] { display: none; }\n.panel.docked { position: static; width: auto; margin: 0; pointer-events: auto; border: none; background: transparent; color: var(--gd-text, #e6edf3); }\n.panel.docked .stat i, .panel.docked .legend { color: var(--gd-muted, #8b949e); }\n.panel.docked .strip { height: 96px; }\n.top-left { top: 0; left: 0; }\n.top-right { top: 0; right: 0; }\n.bottom-left { bottom: 0; left: 0; }\n.bottom-right { bottom: 0; right: 0; }\n.strip { display: block; width: 100%; height: 48px; margin: 6px 0; }\n.readout { display: flex; flex-wrap: wrap; gap: 2px 10px; }\n.stat i { color: #8b949e; font-style: normal; margin-right: 4px; }\n.legend { display: flex; flex-wrap: wrap; gap: 2px 8px; color: #8b949e; }\n.entry { display: inline-flex; align-items: center; gap: 3px; }\n.entry i { width: 7px; height: 7px; border-radius: 2px; display: inline-block; }\n`;\n","import type { UiTreeNode } from 'gesso-framework';\n\n/**\n * A tree snapshot as the rows a panel draws.\n *\n * The panel keeps a set of expanded ids and asks for the rows; the\n * tree can be replaced by a new snapshot every frame while the set\n * stays, so a person's unfolding survives the application changing\n * under it. Ids are positional, which is what makes that work and also\n * what makes it approximate: a row that was a button can become a text\n * when a list reorders. The report says what it is now.\n */\nexport interface TreeRow {\n readonly node: UiTreeNode;\n readonly depth: number;\n readonly expandable: boolean;\n readonly expanded: boolean;\n /** The nearest component anchor at or above this node, if any. */\n readonly owner?: string;\n}\n\n/** The rows of the tree with `expanded` nodes unfolded, in document order. */\nexport function treeRows(root: UiTreeNode, expanded: ReadonlySet<string>): TreeRow[] {\n const rows: TreeRow[] = [];\n const visit = (node: UiTreeNode, depth: number, owner: string | undefined): void => {\n const own = node.component ?? owner;\n const expandable = node.children.length > 0;\n const isExpanded = expandable && expanded.has(node.id);\n rows.push({ node, depth, expandable, expanded: isExpanded, ...(own === undefined ? {} : { owner: own }) });\n if (isExpanded) {\n for (const child of node.children) {\n visit(child, depth + 1, own);\n }\n }\n };\n visit(root, 0, undefined);\n return rows;\n}\n\n/** The ids of every node with children down to `depth` levels, for a first unfolding. */\nexport function idsToDepth(root: UiTreeNode, depth: number): string[] {\n const ids: string[] = [];\n const visit = (node: UiTreeNode, level: number): void => {\n if (level >= depth || node.children.length === 0) {\n return;\n }\n ids.push(node.id);\n for (const child of node.children) {\n visit(child, level + 1);\n }\n };\n visit(root, 0);\n return ids;\n}\n\n/** The ids of the ancestors of `id`, root first, or null when the tree has no such node. */\nexport function pathTo(root: UiTreeNode, id: string): string[] | null {\n const path: string[] = [];\n const visit = (node: UiTreeNode): boolean => {\n if (node.id === id) {\n return true;\n }\n path.push(node.id);\n for (const child of node.children) {\n if (visit(child)) {\n return true;\n }\n }\n path.pop();\n return false;\n };\n return visit(root) ? path : null;\n}\n\n/** One row's label, as the tree prints it. */\nexport function rowLabel(row: TreeRow): string {\n const { node } = row;\n const parts = [node.type];\n if (node.component !== undefined) {\n parts.push(`<${node.component}>`);\n }\n if (node.text !== undefined) {\n parts.push(`\"${node.text}\"`);\n }\n return parts.join(' ');\n}\n","import type { ConsoleEntry, DevtoolsEvent, DevtoolsRequest, UiNodeReport, UiTreeSnapshot } from 'gesso-framework';\nimport type { ActionEntry } from './ActionLog';\nimport { describeActionEntry } from './ActionLogPanel';\nimport { mountFrameProfiler, type FrameProfiler } from './FrameProfiler';\nimport { NODE_REPORT_STYLES, renderNodeReport } from './NodeReportView';\nimport type { DevtoolsAppInfo, PageMessage, PanelPort } from './PanelProtocol';\nimport { idsToDepth, pathTo, rowLabel, treeRows } from './TreeRows';\n\n/**\n * The devtools panel: the tree, a node's\n * report, the workers' consoles, the frame profiler and the action log,\n * in one place, docked outside the canvas.\n *\n * It is plain DOM against a `PanelPort`, and knows nothing about where\n * it is mounted: a Chrome extension's devtools page, or a pane in the\n * page itself. That is what lets the same panel be both, and what lets\n * a spec drive it through a port joined in memory.\n *\n * Unlike the in-page inspector, this panel does not float over the\n * application, so it takes pointer events and can be as tall as its\n * host. The trade is that it cannot point at the canvas with the\n * pointer; it points with `highlight`, and the runtime draws the box.\n */\nexport interface DevtoolsPanel {\n /** The application the panel is showing, or null with none connected. */\n readonly app: DevtoolsAppInfo | null;\n /** Shows a different connected application. */\n show(appId: string): void;\n /** Changes the theme; see `DevtoolsPanelOptions.theme`. */\n setTheme(theme: DevtoolsPanelTheme): void;\n dispose(): void;\n}\n\n/**\n * `light` and `dark` are the panel's two palettes; `auto` follows the\n * viewer's `prefers-color-scheme`. A host that knows better than the\n * media query says so: Chrome's devtools has its own theme setting,\n * which the extension reads and passes here, and the playground is\n * dark whatever the system prefers.\n */\nexport type DevtoolsPanelTheme = 'light' | 'dark' | 'auto';\n\nexport interface DevtoolsPanelOptions {\n /** Which palette. Default `auto`. */\n readonly theme?: DevtoolsPanelTheme;\n /** How many levels of the tree open on the first snapshot. Default 4. */\n readonly openDepth?: number;\n /** How many console entries are kept. Default 500. */\n readonly consoleLimit?: number;\n /** How many action entries are kept. Default 300. */\n readonly actionLimit?: number;\n}\n\ntype Tab = 'tree' | 'streams' | 'console' | 'frames' | 'actions';\nconst TABS: readonly { readonly id: Tab; readonly label: string }[] = [\n { id: 'tree', label: 'Tree' },\n { id: 'streams', label: 'Streams' },\n { id: 'console', label: 'Console' },\n { id: 'frames', label: 'Frames' },\n { id: 'actions', label: 'Actions' }\n];\n\n/** Live subscriptions under one component, as the streams view lists them. */\ninterface ComponentStreams {\n readonly name: string;\n /** The component's anchor node, so a row selects the thing it names. */\n readonly anchorId: string;\n count: number;\n}\n\nexport function mountDevtoolsPanel(\n host: HTMLElement,\n port: PanelPort,\n options: DevtoolsPanelOptions = {}\n): DevtoolsPanel {\n const doc = host.ownerDocument;\n const openDepth = options.openDepth ?? 4;\n const consoleLimit = options.consoleLimit ?? 500;\n const actionLimit = options.actionLimit ?? 300;\n\n const container = doc.createElement('div');\n container.className = 'gesso-devtools';\n const applyTheme = (theme: DevtoolsPanelTheme): void => {\n // No attribute for `auto`, so the stylesheet's media query decides.\n if (theme === 'auto') {\n delete container.dataset['theme'];\n } else {\n container.dataset['theme'] = theme;\n }\n };\n applyTheme(options.theme ?? 'auto');\n const root = container.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = STYLES;\n root.append(style);\n host.appendChild(container);\n\n // ---- toolbar\n const toolbar = el(doc, 'div', 'toolbar');\n const picker = doc.createElement('select');\n picker.className = 'picker';\n picker.hidden = true;\n const tabs = el(doc, 'div', 'tabs');\n const tabButtons = new Map<Tab, HTMLButtonElement>();\n for (const tab of TABS) {\n const button = doc.createElement('button');\n button.type = 'button';\n button.textContent = tab.label;\n button.addEventListener('click', () => showTab(tab.id));\n tabButtons.set(tab.id, button);\n tabs.append(button);\n }\n const inspectToggle = doc.createElement('label');\n inspectToggle.className = 'toggle';\n const inspectBox = doc.createElement('input');\n inspectBox.type = 'checkbox';\n inspectToggle.append(inspectBox, doc.createTextNode(' Inspect layout'));\n const pickToggle = doc.createElement('label');\n pickToggle.className = 'toggle';\n const pickBox = doc.createElement('input');\n pickBox.type = 'checkbox';\n pickToggle.append(pickBox, doc.createTextNode(' Pick'));\n pickToggle.title = 'Click a node on the canvas to select it here. The click does not reach the application.';\n const marksToggle = doc.createElement('label');\n marksToggle.className = 'toggle';\n const marksBox = doc.createElement('input');\n marksBox.type = 'checkbox';\n marksToggle.append(marksBox, doc.createTextNode(' Marks'));\n marksToggle.title =\n \"Name the frame phases and the channel traffic for the browser's own profiler, \" +\n 'so a performance recording shows what Gesso was doing. Off by default: a frame ' +\n 'nobody is profiling pays nothing.';\n const status = el(doc, 'span', 'status');\n toolbar.append(picker, tabs, inspectToggle, pickToggle, marksToggle, status);\n root.append(toolbar);\n\n // ---- views\n const views = el(doc, 'div', 'views');\n const treeView = el(doc, 'div', 'view tree-view');\n const treePane = el(doc, 'div', 'tree');\n const reportPane = el(doc, 'div', 'report');\n treeView.append(treePane, reportPane);\n const consoleView = el(doc, 'div', 'view console-view');\n const consoleList = el(doc, 'ol', 'log');\n const consoleBar = el(doc, 'div', 'bar');\n const clearConsole = doc.createElement('button');\n clearConsole.type = 'button';\n clearConsole.textContent = 'Clear';\n consoleBar.append(clearConsole);\n consoleView.append(consoleBar, consoleList);\n const framesView = el(doc, 'div', 'view frames-view');\n const actionsView = el(doc, 'div', 'view actions-view');\n const actionsList = el(doc, 'ol', 'log');\n actionsView.append(actionsList);\n const streamsView = el(doc, 'div', 'view streams-view');\n const streamsBar = el(doc, 'div', 'bar');\n const streamsTotal = el(doc, 'span', 'total');\n const resetStreams = doc.createElement('button');\n resetStreams.type = 'button';\n resetStreams.textContent = 'Mark';\n resetStreams.title = 'Take the counts as they are now, so what follows reads as a change from here.';\n streamsBar.append(streamsTotal, resetStreams);\n const streamsList = el(doc, 'div', 'streams');\n streamsView.append(streamsBar, streamsList);\n views.append(treeView, streamsView, consoleView, framesView, actionsView);\n root.append(views);\n const viewFor: Record<Tab, HTMLElement> = {\n tree: treeView,\n streams: streamsView,\n console: consoleView,\n frames: framesView,\n actions: actionsView\n };\n\n // ---- state\n let apps: readonly DevtoolsAppInfo[] = [];\n let current: DevtoolsAppInfo | null = null;\n let tab: Tab = 'tree';\n let snapshot: UiTreeSnapshot | null = null;\n const expanded = new Set<string>();\n let openedOnce = false;\n let selectedId: string | null = null;\n let hoveredOnCanvas: string | null = null;\n let highlighted: string | null = null;\n let report: UiNodeReport | null = null;\n const consoleEntries: (\n | ConsoleEntry\n | { error: true; message: string; stack?: string; source: string; at: number }\n )[] = [];\n const actionEntries: ActionEntry[] = [];\n let actionOrigin: number | null = null;\n let profiler: FrameProfiler | null = null;\n /**\n * Counts as of the last \"Mark\", by component anchor.\n *\n * A leak is not a number, it is a number that climbs: forty\n * subscriptions under a list is either right or wrong depending on\n * the list, and forty more after navigating away and back is wrong\n * whatever the list. So the view shows the count and the change\n * since a moment the person chose.\n */\n let streamBaseline = new Map<string, number>();\n let baselineTotal: number | null = null;\n\n const request = (message: DevtoolsRequest): void => {\n if (current !== null) {\n port.post({ type: 'request', app: current.id, request: message });\n }\n };\n\n // What the current application is asked to keep sending, for as long\n // as it is the one shown. Frames only while the frames tab is up,\n // because a frame a second is a frame a second.\n const subscribe = (): void => {\n request({ kind: 'console', enabled: true });\n request({ kind: 'watchTree', enabled: true });\n request({ kind: 'watchFrames', enabled: tab === 'frames' });\n request({ kind: 'inspector', enabled: inspectBox.checked });\n };\n const unsubscribe = (): void => {\n request({ kind: 'console', enabled: false });\n request({ kind: 'watchTree', enabled: false });\n request({ kind: 'watchFrames', enabled: false });\n request({ kind: 'inspector', enabled: false });\n request({ kind: 'select', id: null });\n request({ kind: 'highlight', id: null });\n request({ kind: 'marks', enabled: false });\n if (current !== null && pickBox.checked) {\n port.post({ type: 'pick', app: current.id, enabled: false });\n }\n };\n\n const setStatus = (text: string): void => {\n status.textContent = text;\n };\n\n const showApp = (info: DevtoolsAppInfo | null): void => {\n if (current?.id === info?.id) {\n return;\n }\n if (current !== null) {\n unsubscribe();\n }\n current = info;\n snapshot = null;\n expanded.clear();\n openedOnce = false;\n selectedId = null;\n highlighted = null;\n hoveredOnCanvas = null;\n report = null;\n consoleEntries.length = 0;\n actionEntries.length = 0;\n actionOrigin = null;\n streamBaseline = new Map();\n baselineTotal = null;\n renderTree();\n renderReport();\n renderConsole();\n renderActions();\n renderStreams();\n if (info === null) {\n setStatus('No Gesso application connected. Call connectDevtools(app) in the page.');\n return;\n }\n setStatus('');\n picker.value = info.id;\n subscribe();\n };\n\n const showTab = (next: Tab): void => {\n tab = next;\n for (const [id, button] of tabButtons) {\n button.className = id === next ? 'active' : '';\n }\n for (const [id, view] of Object.entries(viewFor) as [Tab, HTMLElement][]) {\n view.hidden = id !== next;\n }\n request({ kind: 'watchFrames', enabled: next === 'frames' });\n if (next === 'frames') {\n profiler ??= mountFrameProfiler(framesView, { layout: 'docked' });\n profiler.setVisible(true);\n } else {\n profiler?.setVisible(false);\n }\n };\n\n // ---- tree\n const renderTree = (): void => {\n treePane.textContent = '';\n if (snapshot === null) {\n return;\n }\n if (!openedOnce) {\n openedOnce = true;\n for (const id of idsToDepth(snapshot.root, openDepth)) {\n expanded.add(id);\n }\n }\n const rows = treeRows(snapshot.root, expanded);\n for (const row of rows) {\n const line = el(doc, 'div', 'row');\n if (row.node.id === selectedId) {\n line.classList.add('selected');\n }\n if (row.node.id === hoveredOnCanvas) {\n line.classList.add('hovered');\n }\n line.style.paddingLeft = `${8 + row.depth * 12}px`;\n line.dataset['id'] = row.node.id;\n const disclosure = el(doc, 'span', 'disclosure');\n disclosure.textContent = row.expandable ? (row.expanded ? '▾' : '▸') : '';\n disclosure.addEventListener('click', event => {\n event.stopPropagation();\n if (expanded.has(row.node.id)) {\n expanded.delete(row.node.id);\n } else {\n expanded.add(row.node.id);\n }\n renderTree();\n });\n const type = el(doc, 'span', 'type');\n type.textContent = row.node.type;\n line.append(disclosure, type);\n if (row.node.component !== undefined) {\n const component = el(doc, 'span', 'component');\n component.textContent = `<${row.node.component}>`;\n line.append(component);\n }\n if (row.node.text !== undefined) {\n const text = el(doc, 'span', 'text');\n text.textContent = `\"${row.node.text}\"`;\n line.append(text);\n }\n const id = el(doc, 'span', 'id');\n id.textContent = row.node.id;\n line.append(id);\n line.title = rowLabel(row) + (row.owner === undefined ? '' : ` · rendered by ${row.owner}`);\n line.addEventListener('mouseenter', () => setHighlight(row.node.id));\n line.addEventListener('click', () => select(row.node.id));\n treePane.append(line);\n }\n const count = el(doc, 'div', 'count');\n count.textContent = `${snapshot.nodes} nodes`;\n treePane.append(count);\n };\n treePane.addEventListener('mouseleave', () => setHighlight(null));\n\n const setHighlight = (id: string | null): void => {\n if (highlighted === id) {\n return;\n }\n highlighted = id;\n request({ kind: 'highlight', id });\n };\n\n const select = (id: string | null): void => {\n selectedId = id;\n request({ kind: 'select', id });\n if (id === null) {\n report = null;\n renderReport();\n }\n renderTree();\n };\n\n const reveal = (id: string): void => {\n if (snapshot === null) {\n return;\n }\n const path = pathTo(snapshot.root, id);\n if (path === null) {\n return;\n }\n for (const ancestor of path) {\n expanded.add(ancestor);\n }\n renderTree();\n const row = [...treePane.children].find(child => (child as HTMLElement).dataset['id'] === id);\n (row as HTMLElement | undefined)?.scrollIntoView?.({ block: 'nearest' });\n };\n\n // ---- streams\n /**\n * Live subscriptions per component, from the tree.\n *\n * Aggregated here rather than reported per component by the runtime,\n * because the tree already says which component a node belongs to\n * and already arrives whenever the count changes. Nodes above every\n * component anchor are the runtime's own and are listed under the\n * root.\n */\n const componentStreams = (): ComponentStreams[] => {\n if (snapshot === null) {\n return [];\n }\n const totals = new Map<string, ComponentStreams>();\n const visit = (node: UiTreeSnapshot['root'], owner: ComponentStreams): void => {\n const here =\n node.component === undefined\n ? owner\n : (totals.get(node.id) ?? { name: node.component, anchorId: node.id, count: 0 });\n if (node.component !== undefined) {\n totals.set(node.id, here);\n }\n here.count += node.subscriptions ?? 0;\n for (const child of node.children) {\n visit(child, here);\n }\n };\n const root: ComponentStreams = { name: '(root)', anchorId: snapshot.root.id, count: 0 };\n totals.set(snapshot.root.id, root);\n visit(snapshot.root, root);\n return [...totals.values()].sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n };\n\n const renderStreams = (): void => {\n streamsList.textContent = '';\n if (snapshot === null) {\n streamsTotal.textContent = 'Waiting for a tree.';\n return;\n }\n const change = baselineTotal === null ? '' : ` (${signed(snapshot.subscriptions - baselineTotal)} since the mark)`;\n streamsTotal.textContent = `${snapshot.subscriptions} live subscriptions in the graph${change}`;\n const rows = componentStreams();\n if (rows.length === 0) {\n const empty = el(doc, 'p', 'empty');\n empty.textContent = 'Nothing is subscribed.';\n streamsList.append(empty);\n return;\n }\n const largest = rows[0]?.count ?? 0;\n for (const entry of rows) {\n const row = el(doc, 'div', 'stream-row');\n const name = el(doc, 'span', 'component');\n name.textContent = entry.name;\n const bar = el(doc, 'span', 'bar-track');\n const fill = el(doc, 'span', 'bar-fill');\n fill.style.width = `${largest === 0 ? 0 : Math.round((entry.count / largest) * 100)}%`;\n bar.append(fill);\n const count = el(doc, 'span', 'count');\n const before = streamBaseline.get(entry.anchorId);\n const delta = before === undefined || before === entry.count ? '' : ` ${signed(entry.count - before)}`;\n count.textContent = `${entry.count}${delta}`;\n if (delta !== '') {\n count.classList.add('changed');\n }\n row.append(name, bar, count);\n row.title = entry.anchorId;\n row.addEventListener('mouseenter', () => setHighlight(entry.anchorId));\n row.addEventListener('click', () => {\n showTab('tree');\n reveal(entry.anchorId);\n select(entry.anchorId);\n });\n streamsList.append(row);\n }\n };\n streamsList.addEventListener('mouseleave', () => setHighlight(null));\n resetStreams.addEventListener('click', () => {\n streamBaseline = new Map(componentStreams().map(entry => [entry.anchorId, entry.count]));\n baselineTotal = snapshot?.subscriptions ?? null;\n renderStreams();\n });\n\n const renderReport = (): void => {\n reportPane.textContent = '';\n if (report === null) {\n const empty = el(doc, 'p', 'empty');\n empty.textContent =\n selectedId === null ? 'Select a node in the tree to read its report.' : 'The selected node has left the tree.';\n reportPane.append(empty);\n return;\n }\n const id = report.id;\n reportPane.append(\n ...renderNodeReport(doc, report, {\n // The write half of the addressed channel: `select` names a\n // node to read, `setProp` names one to change. The frame the\n // write dirties sends the report back, so the value shown\n // afterwards is the value the node holds, not the one typed.\n onEditProp: (name, value) => request({ kind: 'setProp', id, name, value })\n })\n );\n };\n\n // ---- console\n const renderConsole = (): void => {\n consoleList.textContent = '';\n for (const entry of consoleEntries) {\n const item = doc.createElement('li');\n if ('error' in entry) {\n item.className = 'entry error';\n item.append(badge(doc, 'render'), badge(doc, entry.source, 'source'), doc.createTextNode(entry.message));\n if (entry.stack !== undefined) {\n const stack = doc.createElement('pre');\n stack.textContent = entry.stack;\n item.append(stack);\n }\n } else {\n item.className = `entry ${entry.level}`;\n item.append(badge(doc, entry.thread), doc.createTextNode(entry.args.join(' ')));\n }\n consoleList.append(item);\n }\n consoleList.scrollTop = consoleList.scrollHeight;\n };\n clearConsole.addEventListener('click', () => {\n consoleEntries.length = 0;\n renderConsole();\n });\n\n // ---- actions\n const renderActions = (): void => {\n actionsList.textContent = '';\n if (actionEntries.length === 0) {\n const empty = el(doc, 'li', 'empty');\n empty.textContent = 'No store actions. Pass an ActionLog to connectDevtools to see commands and patches here.';\n actionsList.append(empty);\n return;\n }\n for (const entry of actionEntries) {\n const item = doc.createElement('li');\n item.className = `entry ${entry.kind}`;\n const time = el(doc, 'span', 'time');\n time.textContent = `+${((entry.at - (actionOrigin ?? entry.at)) / 1000).toFixed(2)}s`;\n item.append(time, badge(doc, entry.kind), doc.createTextNode(describeActionEntry(entry)));\n if (entry.cause !== undefined) {\n // The one number that ties a click, the command it sent, the\n // patches that answered and the frame that drew them. Reading\n // it is the thing that used to be timestamp arithmetic across\n // three threads.\n const cause = el(doc, 'span', 'cause');\n cause.textContent = ` #${entry.cause.id} ${entry.cause.label}`;\n item.append(cause);\n }\n actionsList.append(item);\n }\n actionsList.scrollTop = actionsList.scrollHeight;\n };\n\n // ---- incoming\n const onEvent = (event: DevtoolsEvent): void => {\n switch (event.kind) {\n case 'tree':\n snapshot = event.tree;\n renderTree();\n renderStreams();\n break;\n case 'action':\n pushAction(event.entry);\n break;\n case 'report':\n if (event.id === selectedId) {\n report = event.report;\n if (report === null) {\n selectedId = null;\n }\n renderReport();\n renderTree();\n }\n break;\n case 'hover':\n hoveredOnCanvas = event.report?.id ?? null;\n if (hoveredOnCanvas !== null) {\n reveal(hoveredOnCanvas);\n } else {\n renderTree();\n }\n break;\n case 'console':\n consoleEntries.push(event.entry);\n if (consoleEntries.length > consoleLimit) {\n consoleEntries.splice(0, consoleEntries.length - consoleLimit);\n }\n renderConsole();\n break;\n case 'error':\n consoleEntries.push({\n error: true,\n message: event.message,\n source: event.source,\n at: Date.now(),\n ...(event.stack === undefined ? {} : { stack: event.stack })\n });\n renderConsole();\n break;\n case 'frame':\n profiler?.report(event.metrics);\n break;\n }\n };\n\n const onMessage = (message: PageMessage): void => {\n if (message.type === 'apps') {\n apps = message.apps;\n picker.textContent = '';\n for (const info of apps) {\n const option = doc.createElement('option');\n option.value = info.id;\n option.textContent = info.name;\n picker.append(option);\n }\n picker.hidden = apps.length < 2;\n const still = apps.find(info => info.id === current?.id);\n showApp(still ?? apps[0] ?? null);\n return;\n }\n if (message.app !== current?.id) {\n return;\n }\n if (message.type === 'event') {\n onEvent(message.event);\n return;\n }\n if (message.type === 'picked') {\n // The click never reached the application; this is what it meant.\n // The tree comes forward with it, because somebody who pointed\n // at a node meant to read it and not to keep watching the log.\n showTab('tree');\n reveal(message.id);\n select(message.id);\n return;\n }\n pushAction(message.entry);\n };\n\n /** One action log entry, from either route: the page's log or the render worker's. */\n const pushAction = (entry: ActionEntry): void => {\n actionOrigin ??= entry.at;\n actionEntries.push(entry);\n if (actionEntries.length > actionLimit) {\n actionEntries.splice(0, actionEntries.length - actionLimit);\n }\n renderActions();\n };\n\n picker.addEventListener('change', () => {\n const info = apps.find(candidate => candidate.id === picker.value);\n if (info !== undefined) {\n showApp(info);\n }\n });\n inspectBox.addEventListener('change', () => request({ kind: 'inspector', enabled: inspectBox.checked }));\n // Picking needs the inspector: what a click pins is whatever the\n // runtime last reported as hovered, and nothing is reported while\n // the inspector is off. Turning picking on therefore turns it on,\n // and turning picking off puts it back the way the person left it.\n pickBox.addEventListener('change', () => {\n if (current === null) {\n return;\n }\n port.post({ type: 'pick', app: current.id, enabled: pickBox.checked });\n request({ kind: 'inspector', enabled: pickBox.checked || inspectBox.checked });\n });\n // The browser's own profiler, and nothing else: the runtime names\n // its spans while this is on and pays one boolean read for the\n // question while it is off.\n marksBox.addEventListener('change', () => request({ kind: 'marks', enabled: marksBox.checked }));\n\n const stop = port.onMessage(onMessage);\n showTab('tree');\n showApp(null);\n port.post({ type: 'hello' });\n\n return {\n get app() {\n return current;\n },\n show(appId) {\n const info = apps.find(candidate => candidate.id === appId);\n if (info !== undefined) {\n showApp(info);\n }\n },\n setTheme: applyTheme,\n dispose() {\n if (current !== null) {\n unsubscribe();\n }\n stop();\n profiler?.dispose();\n container.remove();\n }\n };\n}\n\nfunction el(doc: Document, tag: string, className: string): HTMLElement {\n const element = doc.createElement(tag);\n element.className = className;\n return element;\n}\n\n/** A change with its sign, so a rise reads as one at a glance. */\nfunction signed(delta: number): string {\n return delta > 0 ? `+${delta}` : String(delta);\n}\n\nfunction badge(doc: Document, text: string, extra = ''): HTMLElement {\n const element = el(doc, 'span', `badge ${text} ${extra}`.trim());\n element.textContent = text;\n return element;\n}\n\n/**\n * The two palettes, as custom properties on the shadow host.\n *\n * Light is the default set and dark overrides it, twice: once for a\n * host that asked for dark, and once for a viewer who prefers it when\n * the host did not say. The report view and the docked profiler read\n * the same properties with the dark values as fallbacks, so the\n * in-page corner tools, which define none of them, look as they did.\n */\nconst DARK_TOKENS = `\n --gd-bg: #0d1117;\n --gd-bg-raised: #161b22;\n --gd-bg-hover: #21262d;\n --gd-border: #30363d;\n --gd-text: #e6edf3;\n --gd-text-strong: #c9d1d9;\n --gd-muted: #8b949e;\n --gd-faint: #6e7681;\n --gd-fainter: #484f58;\n --gd-accent: #79c0ff;\n --gd-info: #a5d6ff;\n --gd-purple: #d2a8ff;\n --gd-green: #7ee787;\n --gd-orange: #ffa657;\n --gd-red: #ff7b72;\n --gd-yellow: #e3b341;\n --gd-row-hovered: #1f2a3a;\n --gd-row-selected: #1f3b5c;\n --gd-budget: rgba(255, 255, 255, 0.35);\n`;\n\nconst LIGHT_TOKENS = `\n --gd-bg: #ffffff;\n --gd-bg-raised: #f1f3f4;\n --gd-bg-hover: #e8eaed;\n --gd-border: #dadce0;\n --gd-text: #202124;\n --gd-text-strong: #3c4043;\n --gd-muted: #5f6368;\n --gd-faint: #80868b;\n --gd-fainter: #9aa0a6;\n --gd-accent: #1a73e8;\n --gd-info: #185abc;\n --gd-purple: #8430ce;\n --gd-green: #188038;\n --gd-orange: #b06000;\n --gd-red: #c5221f;\n --gd-yellow: #a05f00;\n --gd-row-hovered: #e8f0fe;\n --gd-row-selected: #d2e3fc;\n --gd-budget: rgba(0, 0, 0, 0.35);\n`;\n\nconst STYLES = `\n:host { all: initial; display: block; height: 100%; ${LIGHT_TOKENS} }\n:host([data-theme=\"dark\"]) { ${DARK_TOKENS} }\n@media (prefers-color-scheme: dark) {\n :host(:not([data-theme=\"light\"])) { ${DARK_TOKENS} }\n}\n.toolbar {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 4px 8px;\n border-bottom: 1px solid var(--gd-border);\n background: var(--gd-bg-raised);\n color: var(--gd-text);\n font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n.tabs { display: flex; gap: 2px; }\n.tabs button, .bar button {\n border: 1px solid transparent;\n border-radius: 4px;\n padding: 2px 8px;\n background: transparent;\n color: var(--gd-muted);\n font: inherit;\n cursor: pointer;\n}\n.tabs button:hover, .bar button:hover { color: var(--gd-text); background: var(--gd-bg-hover); }\n.tabs button.active { color: var(--gd-text); border-color: var(--gd-border); background: var(--gd-bg); }\n.picker { font: inherit; background: var(--gd-bg); color: var(--gd-text); border: 1px solid var(--gd-border); border-radius: 4px; }\n.toggle { display: inline-flex; align-items: center; gap: 2px; color: var(--gd-muted); cursor: pointer; }\n.status { margin-left: auto; color: var(--gd-muted); }\n.views { height: calc(100% - 30px); background: var(--gd-bg); color: var(--gd-text); font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }\n.view { height: 100%; }\n.view[hidden] { display: none; }\n.tree-view { display: grid; grid-template-columns: minmax(200px, 1fr) minmax(240px, 1fr); }\n.tree { overflow: auto; border-right: 1px solid var(--gd-border); padding: 4px 0; }\n.row { display: flex; gap: 6px; white-space: nowrap; padding: 1px 8px; cursor: pointer; }\n.row:hover { background: var(--gd-bg-raised); }\n.row.hovered { background: var(--gd-row-hovered); }\n.row.selected { background: var(--gd-row-selected); }\n.disclosure { width: 10px; color: var(--gd-muted); }\n.type { color: var(--gd-accent); }\n.component { color: var(--gd-purple); }\n.text { color: var(--gd-info); overflow: hidden; text-overflow: ellipsis; max-width: 200px; }\n.id { color: var(--gd-fainter); }\n.count { padding: 4px 8px; color: var(--gd-faint); }\n.report { overflow: auto; padding: 8px 12px; }\n.empty { color: var(--gd-muted); margin: 0; padding: 4px 8px; }\n.console-view { display: flex; flex-direction: column; }\n.bar { padding: 4px 8px; border-bottom: 1px solid var(--gd-border); }\n.log { flex: 1; margin: 0; padding: 4px 0; list-style: none; overflow: auto; }\n.entry { padding: 1px 8px; border-bottom: 1px solid var(--gd-bg-raised); white-space: pre-wrap; overflow-wrap: anywhere; }\n.entry.warn { color: var(--gd-yellow); }\n.entry.error { color: var(--gd-red); }\n.entry.debug, .entry.info { color: var(--gd-muted); }\n.entry pre { margin: 2px 0 0 0; color: var(--gd-muted); white-space: pre-wrap; }\n.badge {\n display: inline-block;\n min-width: 36px;\n margin-right: 6px;\n padding: 0 4px;\n border-radius: 3px;\n background: var(--gd-bg-hover);\n color: var(--gd-muted);\n font-size: 10px;\n text-align: center;\n}\n.badge.app { color: var(--gd-green); }\n.badge.render { color: var(--gd-accent); }\n.badge.command { color: var(--gd-purple); }\n.badge.patch { color: var(--gd-accent); }\n.badge.frame { color: var(--gd-green); }\n.badge.source { color: var(--gd-orange); }\n.time { color: var(--gd-faint); margin-right: 6px; }\n.cause { color: var(--gd-orange); }\n.entry.frame { color: var(--gd-muted); }\n.frames-view { padding: 8px; }\n/* Column, so the list inside scrolls itself rather than overflowing\n the pane: without it the log grew past the bottom of the panel and\n the newest entries could not be reached. */\n.actions-view { display: flex; flex-direction: column; }\n.streams-view { display: flex; flex-direction: column; }\n.streams-view .bar { display: flex; align-items: center; gap: 8px; }\n.streams-view .total { flex: 1; color: var(--gd-muted); }\n.streams { flex: 1; overflow: auto; padding: 4px 0; }\n.stream-row { display: flex; align-items: center; gap: 8px; padding: 1px 8px; cursor: pointer; }\n.stream-row:hover { background: var(--gd-bg-raised); }\n.stream-row .component { color: var(--gd-purple); white-space: nowrap; }\n.bar-track { flex: 1; height: 6px; border-radius: 3px; background: var(--gd-bg-raised); overflow: hidden; }\n.bar-fill { display: block; height: 100%; background: var(--gd-accent); }\n.stream-row .count { width: 76px; text-align: right; color: var(--gd-muted); white-space: nowrap; }\n.stream-row .count.changed { color: var(--gd-orange); }\n${NODE_REPORT_STYLES}\n`;\n","import type { UiNodeReport } from 'gesso-framework';\nimport { NODE_REPORT_STYLES, renderNodeReport } from './NodeReportView';\n\n/**\n * The node inspector: what the thing under the\n * pointer is, and where every part of it came from.\n *\n * L8 already answered \"why is this box that size\" and printed it as\n * text. This is the rest of the question a person actually has, which\n * turned out to be four more: what did the element declare, what is a\n * modifier writing over it, what is it inheriting, and which component\n * rendered it. The layout explanation is still here, at the bottom,\n * because it is still the answer to the first one.\n *\n * It is DOM, in a shadow root, over the canvas, for the same reasons\n * the error overlay is: the application it is inspecting owns the\n * canvas, and a panel drawn inside the scene would be part of the\n * scene it is describing. It takes no pointer events at all, because\n * the person is hovering the canvas underneath it and a panel that\n * swallowed the pointer would erase the very thing it is showing.\n */\nexport interface NodeInspector {\n /** Shows the report, or hides the panel when null. */\n set(report: UiNodeReport | null): void;\n dispose(): void;\n}\n\nexport interface NodeInspectorOptions {\n /**\n * Which corner it floats in. Default `'bottom-left'`.\n *\n * A corner rather than a docked side, because the panel must not\n * change the size of the element the application is mounted in: a\n * readout that resized the canvas would relayout the scene it is\n * describing, and the boxes it is pointing at would move.\n */\n readonly corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n}\n\nexport function mountNodeInspector(host: HTMLElement, options: NodeInspectorOptions = {}): NodeInspector {\n const doc = host.ownerDocument;\n const view = doc.defaultView;\n const position = view?.getComputedStyle(host).position ?? 'static';\n const restore = position === 'static' ? host.style.position : null;\n if (position === 'static') {\n host.style.position = 'relative';\n }\n\n const container = doc.createElement('div');\n const root = container.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = STYLES;\n const panel = doc.createElement('div');\n panel.className = `panel ${options.corner ?? 'bottom-left'}`;\n panel.hidden = true;\n root.append(style, panel);\n host.appendChild(container);\n\n const render = (report: UiNodeReport): void => {\n panel.textContent = '';\n panel.append(...renderNodeReport(doc, report));\n };\n\n return {\n set(report) {\n if (report === null) {\n panel.hidden = true;\n panel.textContent = '';\n return;\n }\n render(report);\n panel.hidden = false;\n },\n dispose() {\n container.remove();\n if (restore !== null) {\n host.style.position = restore;\n }\n }\n };\n}\n\nconst STYLES = `\n:host { all: initial; }\n.panel {\n position: absolute;\n z-index: 2147482000;\n max-width: 380px;\n max-height: 70%;\n overflow: auto;\n /* The person is hovering the canvas underneath: the panel showing\n what is under the pointer must never be what is under the pointer. */\n pointer-events: none;\n margin: 12px;\n padding: 10px 12px;\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(13, 17, 23, 0.94);\n color: #e6edf3;\n font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n.panel[hidden] { display: none; }\n.top-left { top: 0; left: 0; }\n.top-right { top: 0; right: 0; }\n.bottom-left { bottom: 0; left: 0; }\n.bottom-right { bottom: 0; right: 0; }\n${NODE_REPORT_STYLES}\n`;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["STYLES","STYLES","STYLES","STYLES","STYLES"],"sources":["../src/codeFrame.ts","../src/sourceMap.ts","../src/stackTrace.ts","../src/ErrorOverlay.ts","../src/ActionLog.ts","../src/ActionLogPanel.ts","../src/RenderWorkerTap.ts","../src/NodePicker.ts","../src/NodeReportView.ts","../src/PanelProtocol.ts","../src/DevtoolsHook.ts","../src/FrameProfiler.ts","../src/TreeRows.ts","../src/DevtoolsPanel.ts","../src/NodeInspector.ts","../src/ProofPanel.ts"],"sourcesContent":["/** One line of a code frame, with the offending one marked. */\nexport interface CodeFrameLine {\n number: number;\n text: string;\n /** True for the line the error was reported on. */\n target: boolean;\n}\n\n/** A few lines of original source around a mapped position. */\nexport interface CodeFrame {\n lines: CodeFrameLine[];\n /** 1-based column on the target line, for the caret under it. */\n column: number;\n}\n\n/**\n * Cuts `context` lines either side of a position out of a source file.\n *\n * This is the part of an overlay that a stack trace cannot replace: a\n * file and a line number send a person to their editor, while the line\n * itself is often the whole answer — a `.length` on something that is\n * undefined reads as the bug the moment it is on screen.\n *\n * Tabs are expanded to two spaces so the caret column below the line\n * lands where the character does. Nothing else is transformed; the text\n * reaches the DOM as text, never as markup.\n */\nexport function codeFrame(source: string, line: number, column: number, context = 2): CodeFrame | null {\n const lines = source.split('\\n');\n if (line < 1 || line > lines.length) {\n return null;\n }\n const first = Math.max(1, line - context);\n const last = Math.min(lines.length, line + context);\n const out: CodeFrameLine[] = [];\n for (let number = first; number <= last; number++) {\n out.push({ number, text: expandTabs(lines[number - 1]), target: number === line });\n }\n return { lines: out, column: expandedColumn(lines[line - 1], column) };\n}\n\nconst TAB_WIDTH = 2;\n\nfunction expandTabs(text: string): string {\n return text.replace(/\\t/g, ' '.repeat(TAB_WIDTH));\n}\n\n/** Where a column lands once the tabs before it have been expanded. */\nfunction expandedColumn(text: string, column: number): number {\n let expanded = 1;\n for (let i = 0; i < Math.min(column - 1, text.length); i++) {\n expanded += text[i] === '\\t' ? TAB_WIDTH : 1;\n }\n return expanded;\n}\n","/**\n * Enough of Source Map v3 to turn a compiled stack frame back into the\n * line somebody wrote.\n *\n * Written here rather than taken from npm for the reason the rest of\n * the repository's tooling gives (`scripts/gen-layout-fixtures.ts`,\n * `scripts/check-webgpu-parity.ts`): the whole of what this package\n * needs is a VLQ decoder and a binary search, and a dependency that\n * ships a `SourceMapConsumer` with a WASM payload is a heavier thing to\n * put in front of a developer trying to read an error than the error\n * was.\n *\n * Two deliberate omissions. Index maps (`sections`) are not read — no\n * bundler this project builds with emits one. Names are not read\n * either: a stack frame already carries the function name the engine\n * knew, and the mapped name is only occasionally better.\n */\n\n/** A source map as a bundler writes it. */\nexport interface SourceMapV3 {\n version: number;\n file?: string;\n sourceRoot?: string;\n sources: (string | null)[];\n sourcesContent?: (string | null)[];\n names?: string[];\n mappings: string;\n}\n\n/** Where a generated position came from, with 1-based line and column. */\nexport interface OriginalPosition {\n /** The source as the map names it, with `sourceRoot` already applied. */\n source: string;\n line: number;\n column: number;\n /**\n * The original text of the source, when the map inlined it.\n *\n * Carried on the position rather than fetched from the consumer\n * afterwards because a chained lookup ends in a consumer the caller\n * never sees: the map that knows the text is the last one in the\n * chain, not the one the caller started from. See\n * `SourceMapStore.originalFor`.\n */\n content?: string | null;\n}\n\n/**\n * One decoded mapping segment.\n *\n * Held as a flat tuple rather than an object because a map for a\n * medium application has hundreds of thousands of them, and they exist\n * only to be searched.\n */\ntype Segment = [generatedColumn: number, sourceIndex: number, sourceLine: number, sourceColumn: number];\n\nconst BASE64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';\n\n/** Character code → 6-bit value, with -1 for anything not base64. */\nconst BASE64_VALUE: Int8Array = (() => {\n const table = new Int8Array(128).fill(-1);\n for (let i = 0; i < BASE64.length; i++) {\n table[BASE64.charCodeAt(i)] = i;\n }\n return table;\n})();\n\n/**\n * Decodes the `mappings` string into one array of segments per\n * generated line.\n *\n * Segments carrying only a generated column — a run of output with no\n * original position, which is what a bundler emits for code it\n * synthesised — are dropped rather than kept with nulls. A lookup that\n * lands in one should fall back to the nearest earlier real mapping,\n * and dropping them is how that happens without a second case.\n */\nexport function decodeMappings(mappings: string): Segment[][] {\n const lines: Segment[][] = [];\n let line: Segment[] = [];\n let sourceIndex = 0;\n let sourceLine = 0;\n let sourceColumn = 0;\n let generatedColumn = 0;\n const fields: number[] = [];\n let index = 0;\n\n while (index < mappings.length) {\n const char = mappings[index];\n if (char === ';') {\n lines.push(line);\n line = [];\n generatedColumn = 0;\n index++;\n continue;\n }\n if (char === ',') {\n index++;\n continue;\n }\n\n fields.length = 0;\n while (index < mappings.length && mappings[index] !== ',' && mappings[index] !== ';') {\n let value = 0;\n let shift = 1;\n let digit: number;\n do {\n const code = mappings.charCodeAt(index);\n digit = code < 128 ? BASE64_VALUE[code] : -1;\n if (digit < 0) {\n // A malformed map is not worth a thrown error inside an error\n // reporter: the caller falls back to the raw stack.\n return lines;\n }\n index++;\n value += (digit & 31) * shift;\n shift *= 32;\n } while ((digit & 32) !== 0);\n fields.push(value % 2 === 1 ? -Math.floor(value / 2) : Math.floor(value / 2));\n }\n\n generatedColumn += fields[0];\n if (fields.length >= 4) {\n sourceIndex += fields[1];\n sourceLine += fields[2];\n sourceColumn += fields[3];\n line.push([generatedColumn, sourceIndex, sourceLine, sourceColumn]);\n }\n }\n lines.push(line);\n return lines;\n}\n\n/**\n * A decoded map, answering \"which line of which source is this?\".\n *\n * Decoding is done once in the constructor because a page that shows\n * one error usually shows several from the same file, and each of them\n * asks about a handful of positions.\n */\nexport class SourceMapConsumer {\n private readonly lines: Segment[][];\n private readonly sources: string[];\n private readonly contents: (string | null)[];\n\n constructor(map: SourceMapV3) {\n this.lines = decodeMappings(map.mappings);\n const root = map.sourceRoot ?? '';\n this.sources = map.sources.map(source => joinSourceRoot(root, source ?? '<unknown>'));\n this.contents = map.sourcesContent ?? [];\n }\n\n /**\n * Maps a generated position — 1-based line and column, as every\n * engine writes them in a stack — back to an original one.\n *\n * Returns the last mapping at or before the column, which is the\n * definition of a source map's coverage: a mapping holds until the\n * next one starts. Null when the line has no mappings at all.\n */\n lookup(line: number, column: number): OriginalPosition | null {\n const segments = this.lines[line - 1];\n if (segments === undefined || segments.length === 0) {\n return null;\n }\n const target = column - 1;\n let low = 0;\n let high = segments.length - 1;\n let found = -1;\n while (low <= high) {\n const middle = (low + high) >> 1;\n if (segments[middle][0] <= target) {\n found = middle;\n low = middle + 1;\n } else {\n high = middle - 1;\n }\n }\n // Before the first mapping on the line: the first one is still the\n // best answer available, and it is the right one for a frame whose\n // column the engine reported at the statement rather than the call.\n const segment = segments[found === -1 ? 0 : found];\n const source = this.sources[segment[1]];\n if (source === undefined) {\n return null;\n }\n return {\n source,\n line: segment[2] + 1,\n column: segment[3] + 1,\n content: this.contents[segment[1]] ?? null\n };\n }\n\n /** The original text of a source, when the map inlined it. */\n contentFor(source: string): string | null {\n const index = this.sources.indexOf(source);\n if (index === -1) {\n return null;\n }\n return this.contents[index] ?? null;\n }\n}\n\n/**\n * The `sourceMappingURL` a script declares, or null.\n *\n * The last one wins, and the search is a `lastIndexOf` over the whole\n * text rather than a regex over the tail: an inlined map is a single\n * comment megabytes long, so \"near the end\" is not where its opening\n * is.\n */\nexport function parseSourceMappingUrl(script: string): string | null {\n const marker = '//# sourceMappingURL=';\n const start = script.lastIndexOf(marker);\n if (start === -1) {\n return null;\n }\n const from = start + marker.length;\n const end = script.indexOf('\\n', from);\n const url = (end === -1 ? script.slice(from) : script.slice(from, end)).trim();\n return url === '' ? null : url;\n}\n\n/** Reads a `data:` URL's payload, base64 or percent-encoded. */\nfunction decodeDataUrl(url: string): string | null {\n const comma = url.indexOf(',');\n if (comma === -1) {\n return null;\n }\n const meta = url.slice(0, comma);\n const payload = url.slice(comma + 1);\n if (!meta.includes(';base64')) {\n return decodeURIComponent(payload);\n }\n const binary = atob(payload);\n // The map is JSON, and JSON is UTF-8: `atob` gives bytes, and a\n // source path with a non-ASCII character would otherwise come out\n // mojibake.\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return new TextDecoder().decode(bytes);\n}\n\n/** Applies `sourceRoot`, which is a prefix and not a URL join. */\nfunction joinSourceRoot(root: string, source: string): string {\n if (root === '' || /^[a-z]+:\\/\\//i.test(source)) {\n return source;\n }\n return root.endsWith('/') ? `${root}${source}` : `${root}/${source}`;\n}\n\n/**\n * Fetches and caches the map for each script a stack mentions.\n *\n * Every method resolves rather than rejects: this runs while\n * something has already gone wrong, and an overlay that throws while\n * explaining a throw is worse than an overlay showing a raw stack.\n * A script with no map, a map that 404s and a map that is not JSON all\n * come back as null, and the caller shows what the engine gave it.\n */\nexport class SourceMapStore {\n private readonly cache = new Map<string, Promise<SourceMapConsumer | null>>();\n private readonly load: (url: string) => Promise<string>;\n\n constructor(load: (url: string) => Promise<string> = defaultLoad) {\n this.load = load;\n }\n\n /** The consumer for a script URL, fetched at most once per store. */\n consumerFor(scriptUrl: string): Promise<SourceMapConsumer | null> {\n const cached = this.cache.get(scriptUrl);\n if (cached !== undefined) {\n return cached;\n }\n const pending = this.resolve(scriptUrl).catch(() => null);\n this.cache.set(scriptUrl, pending);\n return pending;\n }\n\n /**\n * Where a position in a script was written, following the chain of\n * maps as far as it goes.\n *\n * One lookup is not enough, and the reason took a browser to find. A\n * frame inside `gesso-framework` names Vite's optimised dependency\n * bundle; that bundle's map points at the package's own\n * `dist/index.js`, because the optimiser does not chain to the map\n * the package ships; and it is the package's map that knows about\n * `src/app/worker/RenderWorkerApp.ts`. Stopping after one step gave\n * a frame in a bundled file with a five-figure line number, which is\n * exactly the thing shipping the maps was meant to prevent.\n *\n * So each answer is resolved against the map that gave it and asked\n * again, until a source has no map of its own — which is the source\n * somebody wrote. `depth` is a guard against a map that names\n * itself; four is more levels than any real toolchain stacks.\n */\n async originalFor(scriptUrl: string, line: number, column: number, depth = 4): Promise<OriginalPosition | null> {\n let url = scriptUrl;\n let position: OriginalPosition | null = null;\n for (let step = 0; step < depth; step++) {\n const consumer = await this.consumerFor(url);\n const next = consumer?.lookup(line, column) ?? null;\n if (next === null) {\n return position;\n }\n const resolved = absolute(next.source, url);\n position = { ...next, source: resolved };\n if (resolved === url) {\n return position;\n }\n url = resolved;\n line = next.line;\n column = next.column;\n }\n return position;\n }\n\n private async resolve(scriptUrl: string): Promise<SourceMapConsumer | null> {\n const script = await this.load(scriptUrl);\n const mapUrl = parseSourceMappingUrl(script);\n if (mapUrl === null) {\n return null;\n }\n const json = mapUrl.startsWith('data:')\n ? decodeDataUrl(mapUrl)\n : await this.load(new URL(mapUrl, scriptUrl).toString());\n if (json === null) {\n return null;\n }\n const map = JSON.parse(json) as SourceMapV3;\n if (typeof map.mappings !== 'string' || !Array.isArray(map.sources)) {\n return null;\n }\n return new SourceMapConsumer(map);\n }\n}\n\n/**\n * A source name resolved against the script whose map named it.\n *\n * A map's `sources` are relative to the map, and the map is beside the\n * script often enough that the script is the right base. When it is\n * not a URL at all — a webpack `webpack://` name, an absolute path —\n * `new URL` throws and the name is kept as it was, which still reads\n * correctly and simply ends the chain.\n */\nfunction absolute(source: string, base: string): string {\n try {\n return new URL(source, base).toString();\n } catch {\n return source;\n }\n}\n\nasync function defaultLoad(url: string): Promise<string> {\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`${response.status} ${response.statusText}`);\n }\n return response.text();\n}\n","import type { OriginalPosition, SourceMapStore } from './sourceMap';\n\n/** A position in a compiled file, 1-based as every engine reports it. */\nexport interface StackLocation {\n url: string;\n line: number;\n column: number;\n}\n\n/** One line of a stack, parsed as far as it could be. */\nexport interface StackFrame {\n /** The line exactly as the engine wrote it. */\n raw: string;\n /** The function name the engine knew, or null for an anonymous frame. */\n fn: string | null;\n /** Where it ran, or null when the line named no file. */\n location: StackLocation | null;\n /** Where it was written, once a source map has been consulted. */\n original: OriginalPosition | null;\n}\n\n/**\n * `at fn (url:line:col)`, `at url:line:col`, and the `fn@url:line:col`\n * that Firefox and Safari write.\n *\n * Three engines write three formats and there is no standard, so this\n * is deliberately loose: anything ending in `:digits:digits` is a\n * location, and whatever came before it is a name. A line that does\n * not match is kept as `raw` and shown unparsed rather than dropped —\n * a `[native code]` or `eval` frame is still evidence.\n */\nconst CHROME_FRAME = /^\\s*at\\s+(?:(?<fn>.*?)\\s+\\()?(?<url>.+?):(?<line>\\d+):(?<column>\\d+)\\)?\\s*$/;\nconst MOZILLA_FRAME = /^(?<fn>[^@]*)@(?<url>.+?):(?<line>\\d+):(?<column>\\d+)\\s*$/;\n\n/**\n * Splits a stack string into frames, dropping the message lines an\n * engine puts above them.\n *\n * The message is dropped rather than parsed because the caller already\n * has it: `onError` reports the message and the stack separately, and\n * a V8 stack repeats the message in its first line.\n */\nexport function parseStack(stack: string): StackFrame[] {\n const frames: StackFrame[] = [];\n for (const raw of stack.split('\\n')) {\n const line = raw.trimEnd();\n if (line.trim() === '') {\n continue;\n }\n const match = CHROME_FRAME.exec(line) ?? MOZILLA_FRAME.exec(line);\n if (match?.groups === undefined) {\n // Only once frames have started: everything above the first one\n // is the message, and repeating it under itself is noise.\n if (frames.length > 0) {\n frames.push({ raw: line.trim(), fn: null, location: null, original: null });\n }\n continue;\n }\n const { fn, url, line: lineNumber, column } = match.groups;\n const name = fn === undefined ? '' : fn.trim();\n frames.push({\n raw: line.trim(),\n fn: name === '' || name === 'Object.<anonymous>' ? null : name,\n location: { url, line: Number(lineNumber), column: Number(column) },\n original: null\n });\n }\n return frames;\n}\n\n/**\n * Fills in `original` for every frame whose script has a source map.\n *\n * Maps are fetched once per script and the frames of one stack usually\n * name two or three scripts, so this is a couple of requests. It never\n * rejects: a frame that cannot be mapped keeps its compiled location,\n * which is what the console would have shown anyway.\n *\n * `originalFor` rather than one `lookup`, because a frame inside a\n * published package is two maps deep: the bundler's map of the\n * dependency bundle, then the package's own. See its comment.\n */\nexport async function mapStack(frames: StackFrame[], store: SourceMapStore): Promise<StackFrame[]> {\n return Promise.all(\n frames.map(async frame => {\n if (frame.location === null) {\n return frame;\n }\n const original = await store.originalFor(frame.location.url, frame.location.line, frame.location.column);\n return original === null ? frame : { ...frame, original };\n })\n );\n}\n\n/**\n * The first frame worth putting a code frame under.\n *\n * Frames inside the framework are skipped while any application frame\n * remains, because an error thrown from a component surfaces through\n * several layers of runtime and the runtime is almost never where the\n * bug is. If every frame is a framework frame, the first one wins —\n * the framework is then genuinely the answer.\n */\nexport function primaryFrame(frames: StackFrame[]): StackFrame | null {\n const located = frames.filter(frame => frame.location !== null);\n if (located.length === 0) {\n return null;\n }\n const application = located.find(frame => {\n const path = frame.original?.source ?? frame.location?.url ?? '';\n return !/(^|\\/)(node_modules|packages\\/(core|framework|components))\\//.test(path);\n });\n return application ?? located[0];\n}\n\n/**\n * A path short enough to read in a header: same-origin prefix and\n * query string removed, `node_modules` collapsed to the package.\n *\n * The query matters more than it looks. A dev server rewrites imports\n * with cache-busting parameters — `/src/App.tsx?t=1724965201` — and a\n * stack full of those is unreadable for a reason that has nothing to\n * do with the error.\n */\nexport function shortenPath(url: string, origin?: string): string {\n let path = url;\n if (origin !== undefined && path.startsWith(origin)) {\n path = path.slice(origin.length);\n }\n const query = path.indexOf('?');\n if (query !== -1) {\n path = path.slice(0, query);\n }\n const modules = path.lastIndexOf('/node_modules/');\n if (modules !== -1) {\n path = path.slice(modules + '/node_modules/'.length);\n }\n return path.replace(/^\\/@fs/, '').replace(/^\\.\\//, '');\n}\n\n/** `path:line:column` for a frame, mapped when it could be. */\nexport function formatFrame(frame: StackFrame, origin?: string): string {\n if (frame.original !== null) {\n return `${shortenPath(frame.original.source, origin)}:${frame.original.line}:${frame.original.column}`;\n }\n if (frame.location !== null) {\n return `${shortenPath(frame.location.url, origin)}:${frame.location.line}:${frame.location.column}`;\n }\n return frame.raw;\n}\n","import type { RuntimeErrorSource } from 'gesso-framework';\nimport { codeFrame, type CodeFrame } from './codeFrame';\nimport { SourceMapStore } from './sourceMap';\nimport { formatFrame, mapStack, parseStack, primaryFrame, type StackFrame } from './stackTrace';\n\n/**\n * Where an error came from.\n *\n * The render worker's four sources, plus `window` for the thread the\n * overlay itself runs on — the single-thread configuration, and\n * anything the shell does around the app.\n */\nexport type ErrorOrigin = RuntimeErrorSource | 'window';\n\n/** What each origin is called, and the sentence under the message. */\nconst ORIGINS: Record<ErrorOrigin, { label: string; note: string }> = {\n uncaught: {\n label: 'render worker',\n note: 'Nothing caught this, so it ran outside any message from the shell: module scope, a callback the shell did not drive, or a frame the clock paced itself. If it was a frame, its work was already taken off the dirty set, so what you see may be stale.'\n },\n message: {\n label: 'render worker',\n note: 'Thrown while the runtime was handling a message from the shell. A frame usually arrives that way, so this is most often a frame that did not finish, and what you see may be stale. Where it was an input or a resize instead, that one message was dropped.'\n },\n renderer: {\n label: 'renderer',\n note: 'The backend refused to draw. Layout and state are unaffected; the surface is not being updated.'\n },\n listener: {\n label: 'event listener',\n note: 'The dispatcher caught this, so the event still reached the rest of the tree and the application is running. What did not happen is whatever this handler was for.'\n },\n frame: {\n label: 'frame',\n note: 'A frame threw and was abandoned. The clock is still running and the application is still drawing, so what you see is the last frame that finished: this is a report, not a stop. Whatever the broken frame was going to change has not changed.'\n },\n channel: {\n label: 'channel',\n note: 'A channel’s worker or its patch stream threw. The view is intact; the data behind it has stopped arriving.'\n },\n window: { label: 'main thread', note: '' }\n};\n\nexport interface ErrorOverlayOptions {\n /**\n * Also write every error to the console (default true).\n *\n * On by default because the overlay is a second place to see an\n * error, not a replacement for the first: the console keeps the live\n * object, its `cause`, and the \"expand to see the real frames\" that\n * no snapshot of a stack can offer.\n */\n echoToConsole?: boolean;\n /** Where source maps are fetched from. Injected by the specs. */\n sourceMaps?: SourceMapStore;\n}\n\n/** One distinct error, and how many times it has arrived. */\ninterface Entry {\n message: string;\n stack: string | undefined;\n origin: ErrorOrigin;\n repeats: number;\n frames: StackFrame[];\n /** Set once the source maps for this entry's frames have resolved. */\n mapped: boolean;\n code: CodeFrame | null;\n}\n\n/**\n * The error overlay: what a worker threw, drawn over the app that was\n * running when it threw.\n *\n * A canvas UI has no equivalent of a page that stops rendering. When a\n * render worker throws, the last good frame stays on screen — pixels\n * that look exactly like a working application — and the only witness\n * is a console message on a thread the developer has to know to\n * select. `WorkerApp` already forwards those errors to `onError`;\n * this is that callback, with the stack put back through the source\n * maps and the offending line quoted.\n *\n * It is a development tool and it makes a development tool's trade:\n * it fetches source maps, keeps every distinct error of the session,\n * and covers the application it is reporting on.\n */\nexport class ErrorOverlay {\n private readonly host: HTMLElement;\n /**\n * The document the host belongs to, rather than the global one.\n *\n * The same rule `SemanticsMirror` follows: everything this class\n * builds hangs off the element it was handed, so it works in a\n * second window and can be driven by a fake document in a spec —\n * which is the only way to test it in a suite that runs in Node.\n */\n private readonly doc: Document;\n private readonly view: (Window & typeof globalThis) | null;\n private readonly container: HTMLDivElement;\n private readonly root: ShadowRoot;\n private readonly panel: HTMLDivElement;\n private readonly maps: SourceMapStore;\n private readonly echo: boolean;\n private readonly entries: Entry[] = [];\n private readonly restoreHostPosition: string | null;\n\n private shown = -1;\n private disposed = false;\n private detachWindow: (() => void) | null = null;\n\n constructor(host: HTMLElement, options: ErrorOverlayOptions = {}) {\n this.host = host;\n this.doc = host.ownerDocument;\n this.view = this.doc.defaultView;\n this.maps = options.sourceMaps ?? new SourceMapStore();\n this.echo = options.echoToConsole !== false;\n\n // The overlay covers the element the app was mounted into rather\n // than the viewport, because a Gesso app is not necessarily the\n // whole page — the playground runs one in a pane. Covering the app\n // needs the app's element to be a containing block, which is the\n // one property this touches, and it is put back on dispose.\n const position = this.view?.getComputedStyle(host).position ?? 'static';\n this.restoreHostPosition = position === 'static' ? host.style.position : null;\n if (position === 'static') {\n host.style.position = 'relative';\n }\n\n this.container = this.doc.createElement('div');\n this.container.style.display = 'none';\n this.root = this.container.attachShadow({ mode: 'open' });\n const style = this.doc.createElement('style');\n style.textContent = STYLES;\n this.panel = this.doc.createElement('div');\n this.panel.className = 'panel';\n this.panel.tabIndex = -1;\n // `alert` and not `alertdialog`: the overlay traps nothing and\n // takes nothing over, and claiming a dialog would tell a screen\n // reader to expect a focus contract this does not honour.\n this.panel.setAttribute('role', 'alert');\n const scrim = this.doc.createElement('div');\n scrim.className = 'scrim';\n scrim.appendChild(this.panel);\n this.root.append(style, scrim);\n host.appendChild(this.container);\n }\n\n /** How many distinct errors have been reported. */\n get count(): number {\n return this.entries.length;\n }\n\n /** True while the overlay is covering the app. */\n get visible(): boolean {\n return this.container.style.display !== 'none';\n }\n\n /**\n * Reports an error, in the shape `WorkerApp`'s `onError` hands it\n * over, so the whole wiring is `onError: overlay.report`.\n *\n * Bound as a field rather than a method for exactly that: it is\n * passed as a callback far more often than it is called.\n */\n report = (message: string, stack?: string, origin: ErrorOrigin = 'window'): void => {\n if (this.disposed) {\n return;\n }\n if (this.echo) {\n console.error(`[gesso: ${origin}] ${message}`, stack ?? '');\n }\n const existing = this.entries.find(entry => entry.message === message && entry.stack === stack);\n if (existing !== undefined) {\n existing.repeats++;\n // A repeat does not reopen a dismissed overlay. An error thrown\n // every frame would otherwise be impossible to get out of the\n // way of, and getting it out of the way is how a developer looks\n // at the application underneath it.\n if (this.visible && this.entries[this.shown] === existing) {\n this.render();\n }\n return;\n }\n const entry: Entry = {\n message,\n stack,\n origin,\n repeats: 1,\n frames: stack === undefined ? [] : parseStack(stack),\n mapped: false,\n code: null\n };\n this.entries.push(entry);\n this.shown = this.entries.length - 1;\n this.show();\n void this.resolveSources(entry);\n };\n\n /** Reports a thrown value, which is usually but not always an Error. */\n reportError = (error: unknown, origin: ErrorOrigin = 'window'): void => {\n if (error instanceof Error) {\n this.report(error.message, error.stack, origin);\n return;\n }\n this.report(String(error), undefined, origin);\n };\n\n /**\n * Catches what this thread throws, too.\n *\n * The single-thread configuration runs components here, and even in\n * the worker configuration the shell around the app can throw. Returns\n * a function that stops listening; `dispose` calls it as well.\n */\n captureWindowErrors(target: Window | null = this.view): () => void {\n if (target === null) {\n return () => {};\n }\n const onError = (event: ErrorEvent): void => {\n this.reportError(event.error ?? event.message, 'window');\n };\n const onRejection = (event: PromiseRejectionEvent): void => {\n this.reportError(event.reason, 'window');\n };\n target.addEventListener('error', onError);\n target.addEventListener('unhandledrejection', onRejection);\n const detach = (): void => {\n target.removeEventListener('error', onError);\n target.removeEventListener('unhandledrejection', onRejection);\n this.detachWindow = null;\n };\n this.detachWindow = detach;\n return detach;\n }\n\n /** Hides the overlay. The errors are kept and can be shown again. */\n hide(): void {\n this.container.style.display = 'none';\n this.doc.removeEventListener('keydown', this.handleKeyDown, true);\n }\n\n /** Hides the overlay and forgets every error it was holding. */\n clear(): void {\n this.entries.length = 0;\n this.shown = -1;\n this.hide();\n }\n\n dispose(): void {\n this.disposed = true;\n this.detachWindow?.();\n this.doc.removeEventListener('keydown', this.handleKeyDown, true);\n this.container.remove();\n if (this.restoreHostPosition !== null) {\n this.host.style.position = this.restoreHostPosition;\n }\n }\n\n private show(): void {\n this.container.style.display = 'block';\n this.render();\n // Escape is taken at the document, in the capture phase, rather\n // than from the panel: focus is not the overlay's to hold. The\n // accessibility mirror moves DOM focus to follow the application's\n // own, and it does so on the frames either side of an error — so a\n // panel that had focus when it opened does not have it a moment\n // later, and Escape would reach nothing. Capturing also means the\n // canvas underneath does not also see the key, which it should not\n // while it is covered.\n this.doc.addEventListener('keydown', this.handleKeyDown, true);\n // Focus anyway, for the screen reader: it lands on the message\n // instead of announcing it from wherever the user happened to be.\n this.panel.focus();\n }\n\n private handleKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Escape') {\n event.preventDefault();\n event.stopPropagation();\n this.hide();\n }\n };\n\n /**\n * Maps the entry's stack and quotes the line it points at.\n *\n * Deliberately after the first paint: the raw stack is on screen\n * within a frame of the error, and the mapped one replaces it when\n * the network answers. A developer looking at an error should never\n * be waiting on a fetch to see it.\n */\n private async resolveSources(entry: Entry): Promise<void> {\n if (entry.frames.length === 0) {\n return;\n }\n entry.frames = await mapStack(entry.frames, this.maps);\n entry.mapped = true;\n const frame = primaryFrame(entry.frames);\n // The text comes with the position rather than from a second\n // lookup: a frame inside a package is mapped through two maps, and\n // it is the last one in that chain that inlined the source.\n const content = frame?.original?.content ?? null;\n if (frame?.original != null && content !== null) {\n entry.code = codeFrame(content, frame.original.line, frame.original.column);\n }\n if (!this.disposed && this.visible && this.entries[this.shown] === entry) {\n this.render();\n }\n }\n\n /** `element`, bound to this overlay's document. */\n private el = <K extends keyof HTMLElementTagNameMap>(\n tag: K,\n options: { className?: string; text?: string } = {}\n ): HTMLElementTagNameMap[K] => element(this.doc, tag, options);\n\n /** A header button, bound to this overlay's document. */\n private button = (label: string, title: string, onClick: () => void): HTMLButtonElement =>\n button(this.doc, label, title, onClick);\n\n private render(): void {\n const entry = this.entries[this.shown];\n if (entry === undefined) {\n return;\n }\n const origin = ORIGINS[entry.origin];\n this.panel.replaceChildren();\n\n const header = this.el('header');\n header.append(this.el('span', { className: 'badge', text: origin.label }));\n if (entry.repeats > 1) {\n header.append(this.el('span', { className: 'repeats', text: `×${entry.repeats}` }));\n }\n const spacer = this.el('span', { className: 'spacer' });\n header.append(spacer);\n if (this.entries.length > 1) {\n header.append(\n this.button('‹', 'Previous error', () => this.step(-1)),\n this.el('span', { className: 'count', text: `${this.shown + 1} / ${this.entries.length}` }),\n this.button('›', 'Next error', () => this.step(1))\n );\n }\n header.append(\n this.button('Copy', 'Copy this error to the clipboard', () => this.copy(entry)),\n this.button('✕', 'Dismiss', () => this.hide())\n );\n this.panel.append(header, this.el('h1', { text: entry.message }));\n\n if (origin.note !== '') {\n this.panel.append(this.el('p', { className: 'note', text: origin.note }));\n }\n\n if (entry.code !== null) {\n this.panel.append(renderCodeFrame(this.doc, entry.code));\n }\n\n if (entry.frames.length > 0) {\n const list = this.el('ol', { className: 'stack' });\n for (const frame of entry.frames) {\n const item = this.el('li');\n if (frame.location === null) {\n item.append(this.el('span', { className: 'raw', text: frame.raw }));\n } else {\n item.append(\n this.el('span', { className: 'fn', text: frame.fn ?? '(anonymous)' }),\n this.el('span', { className: 'at', text: formatFrame(frame, this.view?.location.origin) })\n );\n }\n list.append(item);\n }\n this.panel.append(list);\n } else if (entry.stack !== undefined) {\n // Nothing parsed: show what the engine wrote rather than nothing.\n this.panel.append(this.el('pre', { className: 'stack raw', text: entry.stack }));\n }\n\n const footer = this.el('footer');\n footer.append(\n this.el('span', {\n text: entry.mapped || entry.frames.length === 0 ? 'Escape dismisses this.' : 'Resolving source maps…'\n })\n );\n this.panel.append(footer);\n }\n\n private step(delta: number): void {\n const next = this.shown + delta;\n if (next < 0 || next >= this.entries.length) {\n return;\n }\n this.shown = next;\n this.render();\n void this.resolveSources(this.entries[next]);\n }\n\n private copy(entry: Entry): void {\n const frames = entry.frames.map(frame =>\n frame.location === null ? frame.raw : ` at ${frame.fn ?? '(anonymous)'} (${formatFrame(frame)})`\n );\n const text = [entry.message, ...frames].join('\\n');\n void this.view?.navigator.clipboard?.writeText(text).catch(() => {\n // A clipboard permission the page does not have is not worth a\n // second error on top of the one being reported.\n });\n }\n}\n\n/**\n * Mounts an error overlay over an application's host element.\n *\n * const overlay = mountErrorOverlay(host);\n * createApp({ renderWorker, onError: overlay.report });\n * overlay.captureWindowErrors();\n */\nexport function mountErrorOverlay(host: HTMLElement, options: ErrorOverlayOptions = {}): ErrorOverlay {\n return new ErrorOverlay(host, options);\n}\n\nfunction renderCodeFrame(doc: Document, frame: CodeFrame): HTMLElement {\n const el = <K extends keyof HTMLElementTagNameMap>(\n tag: K,\n options: { className?: string; text?: string } = {}\n ): HTMLElementTagNameMap[K] => element(doc, tag, options);\n const width = String(frame.lines[frame.lines.length - 1].number).length;\n const block = el('pre', { className: 'code' });\n for (const line of frame.lines) {\n const row = el('div', { className: line.target ? 'line target' : 'line' });\n row.append(\n el('span', { className: 'gutter', text: String(line.number).padStart(width, ' ') }),\n el('span', { className: 'text', text: line.text })\n );\n block.append(row);\n if (line.target) {\n const caret = el('div', { className: 'line caret' });\n caret.append(\n el('span', { className: 'gutter', text: ' '.repeat(width) }),\n el('span', { className: 'text', text: `${' '.repeat(Math.max(0, frame.column - 1))}^` })\n );\n block.append(caret);\n }\n }\n return block;\n}\n\nfunction element<K extends keyof HTMLElementTagNameMap>(\n doc: Document,\n tag: K,\n options: { className?: string; text?: string } = {}\n): HTMLElementTagNameMap[K] {\n const node = doc.createElement(tag);\n if (options.className !== undefined) {\n node.className = options.className;\n }\n if (options.text !== undefined) {\n // Every string here came from a thrown error or a source file, so\n // it is set as text and never as markup.\n node.textContent = options.text;\n }\n return node;\n}\n\nfunction button(doc: Document, label: string, title: string, onClick: () => void): HTMLButtonElement {\n const node = element(doc, 'button', { text: label });\n node.type = 'button';\n node.title = title;\n node.setAttribute('aria-label', title);\n node.addEventListener('click', onClick);\n return node;\n}\n\n/**\n * The overlay's own styles, inside its shadow root.\n *\n * Shadow rather than a stylesheet in the page: an overlay that\n * inherited the application's CSS would be unreadable in exactly the\n * applications whose CSS is broken, and one that leaked its own rules\n * would be a development tool changing the thing being developed.\n *\n * Dark and monospace whatever the page is, because this is a tool\n * rather than a part of the app, and it should never be mistaken for\n * one.\n */\nconst STYLES = `\n:host { all: initial; }\n.scrim {\n position: absolute;\n inset: 0;\n z-index: 2147483000;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 16px;\n box-sizing: border-box;\n background: rgba(12, 12, 16, 0.72);\n backdrop-filter: blur(2px);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 12px;\n line-height: 1.5;\n color: #e6e6ea;\n overflow: auto;\n}\n.panel {\n width: min(860px, 100%);\n max-height: 100%;\n overflow: auto;\n box-sizing: border-box;\n padding: 16px 18px 12px;\n border: 1px solid #7f1d1d;\n border-radius: 8px;\n background: #17171c;\n box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);\n outline: none;\n}\nheader { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }\n.spacer { flex: 1; }\n.badge {\n padding: 2px 8px;\n border-radius: 999px;\n background: #7f1d1d;\n color: #fee2e2;\n text-transform: uppercase;\n letter-spacing: 0.06em;\n font-size: 10px;\n}\n.repeats { color: #fca5a5; }\n.count { color: #9a9aa5; }\nbutton {\n font: inherit;\n color: #cfcfd6;\n background: #24242c;\n border: 1px solid #34343e;\n border-radius: 5px;\n padding: 2px 8px;\n cursor: pointer;\n}\nbutton:hover { background: #2e2e38; color: #fff; }\nbutton:focus-visible { outline: 2px solid #f87171; outline-offset: 1px; }\nh1 {\n margin: 0 0 8px;\n font-size: 14px;\n font-weight: 600;\n color: #fca5a5;\n white-space: pre-wrap;\n word-break: break-word;\n}\n.note { margin: 0 0 12px; color: #9a9aa5; max-width: 68ch; }\n.code {\n margin: 0 0 12px;\n padding: 10px 12px;\n background: #101015;\n border: 1px solid #26262f;\n border-radius: 6px;\n overflow-x: auto;\n}\n.code .line { white-space: pre; }\n.code .gutter { color: #55555f; margin-right: 12px; user-select: none; }\n.code .target { background: rgba(127, 29, 29, 0.35); }\n.code .caret .text { color: #f87171; }\nol.stack { margin: 0; padding: 0; list-style: none; }\nol.stack li { display: flex; gap: 10px; padding: 1px 0; }\nol.stack .fn { color: #d4d4dc; flex: 0 0 auto; }\nol.stack .at { color: #8b8b96; word-break: break-all; }\nol.stack .raw { color: #6f6f7a; }\npre.stack { margin: 0; white-space: pre-wrap; color: #8b8b96; }\nfooter { margin-top: 12px; color: #6f6f7a; }\n`;\n","import {\n applyPatch,\n isChannelClientMessage,\n isChannelHostMessage,\n markInstant,\n type ActionCause,\n type ActionEntry,\n type ChannelErrorEntry,\n type ChannelPort,\n type CommandEntry,\n type FrameEntry,\n type Patch,\n type PatchEntry,\n type WorkerHandle\n} from 'gesso-framework';\n\n/**\n * The entries are declared with the rest of the devtools vocabulary in\n * `gesso-framework`, for the reason `ConsoleEntry` is: they are plain\n * data that crosses a thread to reach a panel, and in the worker\n * configuration they cross on the framework's own devtools channel.\n * They are re-exported here because this is where a reader looks for\n * them.\n */\nexport type { ActionCause, ActionEntry, ChannelErrorEntry, CommandEntry, FrameEntry, PatchEntry };\n\n/**\n * The store action log: every command a view sent\n * across the barrier, every patch that came back, on one timeline,\n * with the view rewindable to any point on it.\n *\n * The seam is the port. A channel is two message types over a\n * `ChannelPort`, so a recorder that sits in the middle of one sees\n * both directions at their only crossing, needs nothing from the\n * framework beyond the two type guards the protocol already exports,\n * and can put a patch back on the wire, which is what makes time\n * travel a replay rather than a second write path into the replica.\n *\n * Nothing here touches the DOM. That is deliberate: the tap belongs\n * wherever the ports already are, which in the worker configuration is\n * the render worker, and a recorder that imported a document could not\n * go there. `ActionLogPanel` is the half that draws.\n *\n * **What time travel does.** It rewrites what the *view* holds. The\n * authoritative state lives on the other thread and is not rewound,\n * cannot be rewound from here, and does not know this happened. While\n * the log is pinned to a step, patches still arriving are recorded and\n * held rather than delivered, so the view stays where it was put;\n * going live delivers the state the application actually reached. A\n * command sent from the view while pinned is forwarded like any other,\n * because the application is still running and pretending otherwise\n * would be a lie about a button that visibly did something.\n */\nexport interface ActionLog {\n /**\n * Wraps a worker handle so every channel opened on it is recorded.\n *\n * `tokens` is read for one thing: the value both ends of a channel\n * start from. The first patch a provider sends is a diff against the\n * token's initial, so a recorder that started from nothing would\n * reconstruct an early step out of a patch whose base it never had.\n */\n tap(handle: WorkerHandle, tokens: readonly ActionLogToken[]): WorkerHandle;\n /**\n * Wraps one port, for a channel fed from this thread.\n *\n * `createChannelRegistry` makes the pair for a `source` registration\n * itself and hands out neither end, so a local channel is tapped by\n * providing it by hand instead: call `provide(token, source, port)`\n * on one end of a `MessageChannel` and register the other through\n * this.\n */\n tapPort(port: ChannelPort, token: ActionLogToken): ChannelPort;\n /**\n * Names what is being answered for as long as the returned function\n * has not been called, so everything recorded meanwhile carries the\n * same `ActionCause`.\n *\n * Called around the dispatch of one input, which is the only moment\n * where a cause is known rather than guessed: the command a click's\n * listener sends is sent synchronously inside it. Nothing is\n * allocated for an input that records nothing, so wrapping every\n * pointer move costs a function call and a null check.\n *\n * The patches that answer such a command are given the same cause,\n * which is an inference and the record says so: the barrier carries\n * no request id, so the recorder ties the next patch batch on that\n * channel to the command that preceded it, until a frame has drawn\n * one or another command replaces it.\n */\n cause(label: string): () => void;\n /**\n * Records the frame that drew whatever has been recorded since the\n * last one, closing the chain from click to command to patches to\n * pixels.\n *\n * Nothing is recorded for a frame with nothing to close, so an\n * application drawing sixty frames a second while its channels are\n * quiet adds nothing to the timeline.\n */\n frame(id: number): void;\n /** The timeline, oldest first. */\n readonly entries: readonly ActionEntry[];\n /** The channels this log is tapping, by name, in the order tapped. */\n readonly channels: readonly string[];\n /**\n * The entry the view is pinned to, or null when the view is live.\n *\n * A sequence number rather than an index, because entries fall off\n * the front of a bounded log and an index would then mean a\n * different entry than it did a moment ago.\n */\n readonly pinnedTo: number | null;\n /**\n * Rewinds every tapped channel's view to the state it held once\n * `seq` had been applied, or catches it up to the application when\n * `seq` is null.\n *\n * Each key is posted to the replica as one `{ op: 'set', path: [] }`,\n * which is the shape a reattaching client is already answered with,\n * so nothing downstream can tell a replay from a resync.\n */\n jumpTo(seq: number | null): void;\n /** Empties the timeline and goes live. Tapped channels stay tapped. */\n clear(): void;\n /** Called whenever the timeline or the pinned step changed. */\n subscribe(listener: () => void): () => void;\n /**\n * Stops recording, leaving every tapped channel working.\n *\n * The relay is not removed. A port handed to a replica cannot be\n * taken back out of the middle of it, so what this does is stop\n * listening: messages pass through, nothing is written down, and a\n * held patch could not be stranded by it.\n */\n dispose(): void;\n}\n\n/**\n * A channel's identity, structurally.\n *\n * The same erasure `ChannelRegistration` uses, and for the same\n * reason: a list of channels has no single generic instantiation, and\n * the only two things a recorder wants from a token are its name and\n * the value both ends start from.\n */\nexport interface ActionLogToken {\n readonly name: string;\n readonly initial: object;\n}\n\nexport interface ActionLogOptions {\n /**\n * Most entries kept. Default 200.\n *\n * A dropped entry is not forgotten, only unaddressable: its patches\n * are folded into the base state each channel is reconstructed from,\n * so a jump to a step that survived is still exact.\n */\n readonly limit?: number;\n}\n\nexport function createActionLog(options: ActionLogOptions = {}): ActionLog {\n return new Recorder(options.limit ?? 200);\n}\n\nfunction now(): number {\n return typeof performance === 'undefined' ? Date.now() : performance.now();\n}\n\n/**\n * Sends each new entry as it is recorded, and returns a function that\n * stops.\n *\n * `subscribe` says only that something changed, so the sequence number\n * is what tells a new entry from the ones already sent; a `clear`\n * resets it, and a bounded log retiring old entries does not. Both\n * routes to a panel need exactly this: the hook, for a log in the\n * page, and the render worker tap, for one on the other side of a\n * thread.\n */\nexport function forwardNewEntries(log: ActionLog, send: (entry: ActionEntry) => void): () => void {\n // Below the first sequence number, which is zero.\n let lastSeq = -1;\n const flush = (): void => {\n const entries = log.entries;\n if (entries.length === 0) {\n lastSeq = -1;\n return;\n }\n for (const entry of entries) {\n if (entry.seq > lastSeq) {\n lastSeq = entry.seq;\n send(entry);\n }\n }\n };\n flush();\n return log.subscribe(flush);\n}\n\n/**\n * One channel with the recorder in the middle of it.\n *\n * It holds two states rather than one. `live` follows every patch that\n * arrives, whether or not the view was allowed to see it, so going\n * live is a single set per key instead of a backlog to drain. `base`\n * is the state as of the oldest entry still on the timeline, which is\n * what keeps a bounded log able to reconstruct exactly.\n */\nclass TappedChannel {\n readonly base = new Map<string, unknown>();\n readonly live = new Map<string, unknown>();\n private open = true;\n\n constructor(\n readonly name: string,\n initial: object,\n private readonly toProvider: (message: unknown) => void,\n private readonly toReplica: (message: unknown) => void,\n private readonly recorder: Recorder\n ) {\n for (const [key, value] of Object.entries(initial)) {\n this.base.set(key, value);\n this.live.set(key, value);\n }\n }\n\n /** The replica's end: commands out, and the sync that starts it all. */\n fromReplica(data: unknown): void {\n if (this.open && isChannelClientMessage(data) && data.type === 'channel:command') {\n // In the browser's own profiler too, on the same timeline as the\n // frame spans, so a command and the frame that answered it are\n // read off one recording rather than two panels.\n markInstant(`command ${this.name}.${data.command}`, data.payload);\n this.recorder.record({ kind: 'command', channel: this.name, command: data.command, payload: data.payload });\n }\n this.toProvider(data);\n }\n\n /** The owning thread's end: patches and errors. */\n fromProvider(data: unknown): void {\n if (this.open && isChannelHostMessage(data)) {\n if (data.type === 'channel:patch') {\n for (const patch of data.patches) {\n this.live.set(patch.projection, applyPatch(this.live.get(patch.projection), patch));\n }\n markInstant(`patch ${this.name}`, projectionsOf(data.patches));\n this.recorder.record({\n kind: 'patch',\n channel: this.name,\n patches: data.patches,\n keys: projectionsOf(data.patches)\n });\n if (this.recorder.pinnedTo !== null) {\n // Held, not dropped: `live` already has it, and delivering it\n // would drag the view off the step someone is reading.\n return;\n }\n } else {\n this.recorder.record({ kind: 'error', channel: this.name, message: data.message });\n }\n }\n this.toReplica(data);\n }\n\n /** Folds a dropped entry's patches into the reconstruction base. */\n absorb(patches: readonly Patch[]): void {\n for (const patch of patches) {\n this.base.set(patch.projection, applyPatch(this.base.get(patch.projection), patch));\n }\n }\n\n /** Puts a whole state on the wire, exactly as a resync is answered. */\n show(state: ReadonlyMap<string, unknown>): void {\n const patches: Patch[] = [];\n for (const [projection, value] of state) {\n patches.push({ op: 'set', projection, path: [], value });\n }\n if (patches.length > 0) {\n this.toReplica({ type: 'channel:patch', patches });\n }\n }\n\n close(): void {\n this.open = false;\n }\n}\n\nfunction projectionsOf(patches: readonly Patch[]): string[] {\n const keys: string[] = [];\n for (const patch of patches) {\n if (!keys.includes(patch.projection)) {\n keys.push(patch.projection);\n }\n }\n return keys;\n}\n\n/**\n * A command waiting to be answered, so the patches that answer it can\n * be given the cause the command had.\n *\n * `claimed` is what ends the wait: once a patch batch has taken this\n * cause, the next frame closes it, because that frame is the one that\n * drew the answer. An unclaimed one waits as long as it takes, which\n * is the case that matters — the playground's `heavy.compute()` takes\n * a second and a half, and a cause that timed out before then would\n * lose exactly the round trip a person most wants to see.\n */\ninterface PendingCause {\n readonly cause: ActionCause;\n claimed: boolean;\n}\n\n/** An entry before the recorder gives it its place on the timeline. */\ntype NewEntry =\n | Omit<CommandEntry, 'seq' | 'at'>\n | Omit<PatchEntry, 'seq' | 'at'>\n | Omit<ChannelErrorEntry, 'seq' | 'at'>\n | Omit<FrameEntry, 'seq' | 'at'>;\n\nclass Recorder implements ActionLog {\n private readonly tapped: TappedChannel[] = [];\n private readonly log: ActionEntry[] = [];\n private readonly listeners = new Set<() => void>();\n private seq = 0;\n private pinned: number | null = null;\n /** The input being dispatched, if one is, and the label it was opened with. */\n private openLabel: string | null = null;\n private openCause: ActionCause | null = null;\n private causeId = 0;\n /** Commands awaiting their patches, by channel. */\n private readonly awaiting = new Map<string, PendingCause>();\n /**\n * The cause shared by everything recorded since the last frame\n * entry: undefined when nothing has been, null when two causes have.\n */\n private frameCause: ActionCause | null | undefined = undefined;\n private recordedSinceFrame = false;\n\n constructor(private readonly limit: number) {}\n\n get entries(): readonly ActionEntry[] {\n return this.log;\n }\n\n get channels(): readonly string[] {\n return this.tapped.map(channel => channel.name);\n }\n\n get pinnedTo(): number | null {\n return this.pinned;\n }\n\n tap(handle: WorkerHandle, tokens: readonly ActionLogToken[]): WorkerHandle {\n const initials = new Map(tokens.map(token => [token.name, token.initial]));\n return {\n open: (key: string): MessagePort => {\n const real = handle.open(key);\n // A relay pair, so what the framework receives is a real\n // `MessagePort` and not something shaped like one. `open`\n // promises a port, and a channel registered through a tap has\n // to behave in every way like a channel that was not.\n const relay = new MessageChannel();\n const channel = new TappedChannel(\n key,\n initials.get(key) ?? {},\n message => real.postMessage(message),\n message => relay.port2.postMessage(message),\n this\n );\n real.onmessage = event => channel.fromProvider(event.data);\n relay.port2.onmessage = event => channel.fromReplica(event.data);\n this.tapped.push(channel);\n return relay.port1;\n },\n get spawned(): boolean {\n return handle.spawned;\n },\n terminate: (): void => {\n handle.terminate();\n }\n };\n }\n\n tapPort(port: ChannelPort, token: ActionLogToken): ChannelPort {\n let listener: ((event: { data: unknown }) => void) | null = null;\n const channel = new TappedChannel(\n token.name,\n token.initial,\n message => port.postMessage(message),\n message => listener?.({ data: message }),\n this\n );\n port.onmessage = event => channel.fromProvider(event.data);\n this.tapped.push(channel);\n return {\n postMessage(message: unknown): void {\n channel.fromReplica(message);\n },\n get onmessage(): ((event: { data: unknown }) => void) | null {\n return listener;\n },\n set onmessage(next: ((event: { data: unknown }) => void) | null) {\n listener = next;\n }\n };\n }\n\n cause(label: string): () => void {\n const previousLabel = this.openLabel;\n const previousCause = this.openCause;\n this.openLabel = label;\n // Not minted yet: an input that sends no command should not\n // consume an id, so a person reading the timeline sees the causes\n // that did something and not a counter jumping in tens.\n this.openCause = null;\n let closed = false;\n return () => {\n if (closed) {\n return;\n }\n closed = true;\n this.openLabel = previousLabel;\n this.openCause = previousCause;\n };\n }\n\n frame(id: number): void {\n if (!this.recordedSinceFrame) {\n return;\n }\n this.recordedSinceFrame = false;\n const cause = this.frameCause ?? undefined;\n this.frameCause = undefined;\n // A cause whose answer has been drawn is spent. One still waiting\n // for its first patch is left alone.\n for (const [channel, pending] of this.awaiting) {\n if (pending.claimed) {\n this.awaiting.delete(channel);\n }\n }\n this.append({ kind: 'frame', frame: id, ...(cause === undefined ? {} : { cause }) });\n }\n\n record(\n entry: Omit<CommandEntry, 'seq' | 'at'> | Omit<PatchEntry, 'seq' | 'at'> | Omit<ChannelErrorEntry, 'seq' | 'at'>\n ): void {\n const cause = this.causeFor(entry);\n this.append(cause === undefined ? entry : { ...entry, cause });\n }\n\n /**\n * Which cause an entry belongs to.\n *\n * A command belongs to the input being dispatched, which is known\n * rather than guessed. A patch belongs to the command it answers,\n * which is not: the barrier carries no request id, so what stands\n * for one is \"the last command on this channel that nothing has\n * answered yet\". It is right whenever a channel is answering one\n * thing at a time, which is what a channel driven by a person does,\n * and it is wrong for a channel that also emits on its own between\n * the command and its answer. The panel says `via` rather than\n * `from` for that reason.\n */\n private causeFor(entry: { kind: string; channel: string }): ActionCause | undefined {\n if (entry.kind === 'command') {\n if (this.openLabel === null) {\n this.awaiting.delete(entry.channel);\n return undefined;\n }\n this.openCause ??= { id: ++this.causeId, label: this.openLabel };\n this.awaiting.set(entry.channel, { cause: this.openCause, claimed: false });\n return this.openCause;\n }\n const pending = this.awaiting.get(entry.channel);\n // The open cause first: on a thread where the provider answers\n // synchronously, the patch arrives before the click's dispatch has\n // returned and is the same answer either way.\n const cause = this.openCause ?? pending?.cause;\n if (cause !== undefined && pending?.cause === cause) {\n pending.claimed = true;\n }\n return cause;\n }\n\n private append(entry: NewEntry): void {\n const complete = { ...entry, seq: this.seq++, at: now() } as ActionEntry;\n if (complete.kind !== 'frame') {\n this.recordedSinceFrame = true;\n const cause = complete.cause;\n if (cause !== undefined) {\n // Null means two causes met before a frame closed them, and\n // the frame then belongs to neither.\n this.frameCause = this.frameCause === undefined || this.frameCause === cause ? cause : null;\n }\n }\n this.log.push(complete);\n while (this.log.length > this.limit) {\n this.drop();\n }\n this.notify();\n }\n\n jumpTo(seq: number | null): void {\n this.pinned = seq;\n for (const channel of this.tapped) {\n channel.show(seq === null ? channel.live : this.stateAt(channel, seq));\n }\n this.notify();\n }\n\n clear(): void {\n this.jumpTo(null);\n while (this.log.length > 0) {\n this.drop();\n }\n // A command whose answer is still coming would otherwise hand its\n // cause to a patch on an empty timeline, naming a click nothing\n // shows any more.\n this.awaiting.clear();\n this.frameCause = undefined;\n this.recordedSinceFrame = false;\n this.notify();\n }\n\n subscribe(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => this.listeners.delete(listener);\n }\n\n dispose(): void {\n // Released first. A recorder that stopped while pinned would leave\n // the view frozen on a step with nothing left to unfreeze it.\n this.jumpTo(null);\n for (const channel of this.tapped) {\n channel.close();\n }\n this.tapped.length = 0;\n this.log.length = 0;\n this.pinned = null;\n this.awaiting.clear();\n this.openLabel = null;\n this.openCause = null;\n this.frameCause = undefined;\n this.recordedSinceFrame = false;\n this.listeners.clear();\n }\n\n /** Retires the oldest entry, leaving the reconstruction exact. */\n private drop(): void {\n const dropped = this.log.shift();\n if (dropped?.kind !== 'patch') {\n return;\n }\n for (const channel of this.tapped) {\n if (channel.name === dropped.channel) {\n channel.absorb(dropped.patches);\n }\n }\n }\n\n private stateAt(channel: TappedChannel, seq: number): Map<string, unknown> {\n const state = new Map(channel.base);\n for (const entry of this.log) {\n if (entry.seq > seq) {\n break;\n }\n if (entry.kind !== 'patch' || entry.channel !== channel.name) {\n continue;\n }\n for (const patch of entry.patches) {\n state.set(patch.projection, applyPatch(state.get(patch.projection), patch));\n }\n }\n return state;\n }\n\n private notify(): void {\n for (const listener of this.listeners) {\n listener();\n }\n }\n}\n","import type { ActionEntry, ActionLog } from './ActionLog';\n\n/**\n * The action log's panel: the timeline, and a click on any step to put\n * the view back where it was at that step.\n *\n * DOM in a shadow root over the canvas, for the reasons the error\n * overlay and the node inspector both give: the application owns the\n * canvas, and a panel drawn into the scene would be part of the scene\n * it is describing.\n *\n * It differs from the node inspector in one way that matters. The\n * inspector takes no pointer events because the person is hovering the\n * thing it describes; this panel is operated, so it takes them over\n * itself and nowhere else. That is also why it is behind a toggle\n * rather than always up: while it is showing, the canvas underneath it\n * is not reachable.\n */\nexport interface ActionLogPanel {\n /** Shows or hides the panel. A hidden panel stops rendering. */\n setVisible(visible: boolean): void;\n readonly visible: boolean;\n dispose(): void;\n}\n\nexport interface ActionLogPanelOptions {\n /** Which corner it floats in. Default `'bottom-right'`. */\n readonly corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n}\n\n/** Longest a payload or a value is printed before it is cut. */\nconst VALUE_CHARS = 60;\n\nexport function mountActionLogPanel(\n host: HTMLElement,\n log: ActionLog,\n options: ActionLogPanelOptions = {}\n): ActionLogPanel {\n const doc = host.ownerDocument;\n const view = doc.defaultView;\n const position = view?.getComputedStyle(host).position ?? 'static';\n const restore = position === 'static' ? host.style.position : null;\n if (position === 'static') {\n host.style.position = 'relative';\n }\n\n const container = doc.createElement('div');\n const root = container.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = STYLES;\n\n const panel = doc.createElement('div');\n panel.className = `panel ${options.corner ?? 'bottom-right'}`;\n panel.hidden = true;\n\n const header = doc.createElement('header');\n const title = doc.createElement('span');\n title.className = 'title';\n title.textContent = 'actions';\n const state = doc.createElement('span');\n state.className = 'state';\n const live = doc.createElement('button');\n live.type = 'button';\n live.textContent = 'Live';\n live.addEventListener('click', () => log.jumpTo(null));\n const clear = doc.createElement('button');\n clear.type = 'button';\n clear.textContent = 'Clear';\n clear.addEventListener('click', () => log.clear());\n header.append(title, state, live, clear);\n\n const list = doc.createElement('ol');\n list.className = 'entries';\n\n const note = doc.createElement('p');\n note.className = 'note';\n\n panel.append(header, list, note);\n root.append(style, panel);\n host.appendChild(container);\n\n let visible = false;\n let scheduled = 0;\n\n const render = (): void => {\n const entries = log.entries;\n const pinned = log.pinnedTo;\n // Was the list at the bottom before the rebuild? A log that\n // scrolled itself while someone was reading an older step would be\n // unusable, so it only follows when it was already following.\n const following = list.scrollHeight - list.scrollTop - list.clientHeight < 4;\n\n state.textContent = pinned === null ? 'live' : `step ${pinned}`;\n state.className = pinned === null ? 'state' : 'state pinned';\n live.disabled = pinned === null;\n\n list.textContent = '';\n const origin = entries[0]?.at ?? 0;\n for (const entry of entries) {\n list.append(row(doc, entry, origin, pinned, seq => log.jumpTo(seq)));\n }\n\n if (entries.length === 0) {\n note.textContent = 'Nothing has crossed a tapped channel yet.';\n } else if (pinned === null) {\n note.textContent = 'Click a step to put the view back to what it showed then.';\n } else {\n note.textContent =\n 'The view is showing an earlier step. The application worker is not rewound: it is still ' +\n 'running, its patches are being recorded and held, and Live catches the view up to it.';\n }\n\n if (following) {\n list.scrollTop = list.scrollHeight;\n }\n };\n\n const schedule = (): void => {\n if (!visible || scheduled !== 0) {\n return;\n }\n // Coalesced, because a chatty channel can push several entries\n // between two frames and each one would otherwise rebuild the list.\n const draw = (): void => {\n scheduled = 0;\n render();\n };\n // Called on the view rather than through a captured reference:\n // `requestAnimationFrame` refuses to run detached from its window.\n scheduled = view === null ? (setTimeout(draw, 16) as unknown as number) : view.requestAnimationFrame(draw);\n };\n\n const unsubscribe = log.subscribe(schedule);\n\n return {\n setVisible(next) {\n visible = next;\n panel.hidden = !next;\n if (next) {\n render();\n }\n },\n get visible() {\n return visible;\n },\n dispose() {\n unsubscribe();\n container.remove();\n if (restore !== null) {\n host.style.position = restore;\n }\n }\n };\n}\n\n/**\n * One step.\n *\n * The direction glyph is the first thing on the line because it is the\n * first thing anyone wants: `↑` is a command the view sent, `↓` is\n * what came back. A log that printed both the same way would need to\n * be read to be understood, and this one is meant to be skimmed.\n */\nfunction row(\n doc: Document,\n entry: ActionEntry,\n origin: number,\n pinned: number | null,\n jump: (seq: number) => void\n): HTMLElement {\n const item = doc.createElement('li');\n item.className = `entry ${entry.kind}${entry.seq === pinned ? ' at' : ''}`;\n item.tabIndex = 0;\n item.setAttribute('role', 'button');\n\n const time = doc.createElement('span');\n time.className = 'time';\n time.textContent = formatOffset(entry.at - origin);\n\n const arrow = doc.createElement('span');\n arrow.className = 'arrow';\n arrow.textContent = actionGlyph(entry);\n\n const text = doc.createElement('span');\n text.className = 'text';\n text.textContent = describeActionEntry(entry);\n\n item.append(time, arrow, text);\n if (entry.cause !== undefined) {\n // The thread through the timeline: every row the same click\n // produced carries the same number, so a round trip across three\n // threads is read by looking for one glyph rather than by\n // comparing timestamps.\n const cause = doc.createElement('span');\n cause.className = 'cause';\n cause.textContent = `#${entry.cause.id}`;\n cause.title = entry.cause.label;\n item.append(cause);\n }\n const activate = (): void => jump(entry.seq);\n item.addEventListener('click', activate);\n item.addEventListener('keydown', event => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n activate();\n }\n });\n return item;\n}\n\n/**\n * The direction, as one glyph: up for what the view sent, down for\n * what came back, a square for the frame that drew it.\n */\nexport function actionGlyph(entry: ActionEntry): string {\n switch (entry.kind) {\n case 'command':\n return '↑';\n case 'patch':\n return '↓';\n case 'frame':\n return '▣';\n case 'error':\n return '!';\n }\n}\n\n/** One entry as a line: the command and its payload, the patched keys, the frame, or the error. */\nexport function describeActionEntry(entry: ActionEntry): string {\n switch (entry.kind) {\n case 'command':\n return `${entry.channel}.${entry.command}(${entry.payload === undefined ? '' : print(entry.payload)})`;\n case 'patch':\n return `${entry.channel} ${entry.keys.join(', ')}`;\n case 'frame':\n return `frame ${entry.frame}`;\n case 'error':\n return `${entry.channel} ${entry.message}`;\n }\n}\n\nfunction print(value: unknown): string {\n let text: string;\n try {\n text = JSON.stringify(value) ?? String(value);\n } catch {\n text = String(value);\n }\n return text.length > VALUE_CHARS ? `${text.slice(0, VALUE_CHARS - 1)}…` : text;\n}\n\nfunction formatOffset(ms: number): string {\n if (ms < 1000) {\n return `${Math.round(ms)}ms`;\n }\n return `${(ms / 1000).toFixed(1)}s`;\n}\n\nconst STYLES = `\n:host { all: initial; }\n.panel {\n position: absolute;\n z-index: 2147482000;\n display: flex;\n flex-direction: column;\n width: 320px;\n max-height: 60%;\n margin: 12px;\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(13, 17, 23, 0.94);\n color: #e6edf3;\n font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n.panel[hidden] { display: none; }\n.top-left { top: 0; left: 0; }\n.top-right { top: 0; right: 0; }\n.bottom-left { bottom: 0; left: 0; }\n.bottom-right { bottom: 0; right: 0; }\nheader {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 10px;\n border-bottom: 1px solid rgba(255, 255, 255, 0.1);\n}\n.title {\n flex: 1;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: #8b949e;\n}\n.state { color: #56d364; }\n.state.pinned { color: #e3b341; }\nbutton {\n cursor: pointer;\n padding: 2px 8px;\n border-radius: 5px;\n border: 1px solid rgba(255, 255, 255, 0.16);\n background: #21262d;\n color: #e6edf3;\n font: inherit;\n}\nbutton:hover:not(:disabled) { background: #30363d; color: #fff; }\nbutton:disabled { cursor: default; opacity: 0.45; }\n.entries {\n flex: 1;\n overflow: auto;\n margin: 0;\n padding: 4px 0;\n list-style: none;\n}\n.entry {\n display: flex;\n gap: 6px;\n padding: 1px 10px;\n cursor: pointer;\n overflow-wrap: anywhere;\n}\n.entry:hover { background: rgba(110, 118, 129, 0.25); }\n.entry:focus-visible { outline: 1px solid #79c0ff; outline-offset: -1px; }\n.entry.at { background: rgba(227, 179, 65, 0.22); }\n.time { width: 44px; flex: none; text-align: right; color: #6e7681; }\n.arrow { width: 8px; flex: none; }\n.command .arrow { color: #d2a8ff; }\n.patch .arrow { color: #79c0ff; }\n.frame .arrow { color: #7ee787; }\n.frame .text { color: #8b949e; }\n.error .arrow { color: #ff7b72; }\n.error .text { color: #ff7b72; }\n.text { flex: 1; }\n.cause { flex: none; color: #ffa657; }\n.note {\n margin: 0;\n padding: 6px 10px;\n border-top: 1px solid rgba(255, 255, 255, 0.1);\n color: #8b949e;\n}\n`;\n","import {\n markNow,\n measureSpan,\n performanceMarksEnabled,\n portHandle,\n type ChannelPort,\n type ShellToRuntimeMessage,\n type WorkerHandle\n} from 'gesso-framework';\nimport { forwardNewEntries, type ActionLog, type ActionLogToken } from './ActionLog';\n\n/**\n * The action log in the worker configuration, and the click at the\n * head of every chain it records.\n *\n * The action log recorded the gap and the reason for it. A channel's\n * ports are made where the replicas are, which in the worker\n * configuration is the render worker; the shell holds neither end and\n * never sees a patch, deliberately. So the recorder was written to run\n * in a worker — it imports no DOM and its entries are plain data — and\n * then nothing ran it there, because the two ways to wire it up were\n * \"one line in the render worker's entry\" and \"route every patch\n * through the main thread\", and the second would falsify the very\n * thing the route exists to show.\n *\n * This is that one line, made general. It stands on the wire the way\n * the recorder itself does, which is the rule `0047` set: **the tap\n * goes where the messages already are, never somewhere new.** In a\n * render worker three kinds of message go past one object, the\n * worker's global:\n *\n * - the shell's port to the application worker, which arrives with\n * `init` and is what a tapped channel has to be opened over;\n * - every input the shell forwards, which is where a cause begins;\n * - every frame the runtime reports, which is where one ends.\n *\n * Nothing here reaches into the runtime, and nothing in the framework\n * knows it exists. Used after `renderRoot`, whose constructor installs\n * the handler this wraps:\n *\n * const app = renderRoot(AppRoot).useService(Counter);\n * const actions = createActionLog();\n * const tap = tapRenderWorker(actions);\n * const data = tap.applicationWorker([Catalog, Cart]);\n * app.useChannel(Catalog, { worker: data }).useChannel(Cart, { worker: data });\n *\n * Guard it with `import.meta.env.DEV` or the equivalent: a tap in a\n * production bundle is a recorder holding patch batches for a session\n * nobody is watching.\n */\n\n/** The worker global, as far as the tap is concerned. */\nexport interface RenderWorkerHost {\n onmessage: ((event: { data: unknown }) => void) | null;\n postMessage(message: unknown, transfer?: Transferable[]): void;\n}\n\nexport interface RenderWorkerTapOptions {\n /** Where to stand. Default: the worker's own global. */\n readonly host?: RenderWorkerHost;\n /**\n * Whether entries are posted to the shell as devtools events, so a\n * panel outside the page shows them. Default true.\n *\n * The route a log in the page does not need: there the devtools hook\n * has the log itself to read.\n */\n readonly forward?: boolean;\n}\n\nexport interface RenderWorkerTap {\n /**\n * A handle on the application worker the shell supplied, with every\n * channel opened over it recorded.\n *\n * Registrations run before `init` and the shell's port arrives with\n * it, which is why this is a handle rather than a port: it is opened\n * when the runtime starts, by which time the port is here. Passing\n * it to `useChannel` is what replaces the `APPLICATION_WORKER`\n * placeholder the render worker would otherwise swap in.\n */\n applicationWorker(tokens: readonly ActionLogToken[]): WorkerHandle;\n dispose(): void;\n}\n\nexport function tapRenderWorker(log: ActionLog, options: RenderWorkerTapOptions = {}): RenderWorkerTap {\n const host = options.host ?? (globalThis as unknown as RenderWorkerHost);\n const previousOnMessage = host.onmessage;\n // Kept twice over: bound, to call, since the worker global's own\n // `postMessage` needs its receiver; and unbound, to put back, so\n // dispose leaves the property the function it found rather than a\n // wrapper of it.\n const originalPostMessage = host.postMessage;\n const previousPostMessage = originalPostMessage.bind(host);\n let application: WorkerHandle | undefined;\n let disposed = false;\n\n host.onmessage = event => {\n const message = event.data as ShellToRuntimeMessage | undefined;\n if (message?.type === 'init' && message.appPort !== undefined) {\n // The shell's end of the hub to the application worker. Held as\n // a handle, not used: a tap that opened a port of its own would\n // be a second conversation, and the point is to be in the middle\n // of the one that was going to happen anyway.\n application = portHandle(message.appPort);\n }\n const label = inputLabel(message);\n if (label === null) {\n previousOnMessage?.(event);\n return;\n }\n // The one moment a cause is known rather than inferred: a click's\n // listener runs inside this call, and the command it sends is sent\n // before it returns.\n const close = log.cause(label);\n const measuring = performanceMarksEnabled();\n const started = measuring ? markNow() : 0;\n try {\n previousOnMessage?.(event);\n } finally {\n close();\n if (measuring) {\n measureSpan(`input ${label}`, started, markNow());\n }\n }\n };\n\n host.postMessage = (message: unknown, transfer?: Transferable[]): void => {\n const frame = (message as { type?: unknown; frame?: unknown } | null)?.type === 'frame';\n if (frame && typeof (message as { frame: unknown }).frame === 'number') {\n // Posted at the end of the frame, so everything this frame drew\n // has already been recorded and the entry closes them.\n log.frame((message as { frame: number }).frame);\n }\n if (transfer === undefined) {\n previousPostMessage(message);\n return;\n }\n previousPostMessage(message, transfer);\n };\n\n const stopForwarding =\n options.forward === false\n ? () => {}\n : forwardNewEntries(log, entry => {\n previousPostMessage({ type: 'devtools', event: { kind: 'action', entry } });\n });\n\n return {\n applicationWorker(tokens) {\n return {\n open(key: string): MessagePort {\n if (application === undefined) {\n throw new Error(\n `Channel '${key}' was opened before the shell sent its application worker port. ` +\n 'tapRenderWorker must be called before renderRoot handles `init`, and the shell must ' +\n 'have been given an appLogicWorker.'\n );\n }\n const real = application.open(key);\n const token = tokens.find(candidate => candidate.name === key);\n if (token === undefined) {\n // Untapped rather than refused: a channel the caller did\n // not name still has to work, and a missing token would\n // make the log's reconstruction wrong rather than absent.\n return real;\n }\n // `tapPort` rather than `tap`, and the difference decides\n // whether a cause is exact. `tap` puts a relay\n // `MessageChannel` between the replica and the recorder, so\n // a command crosses it a task later, by which time the click\n // that sent it has been dispatched and the cause closed.\n // `tapPort` hands the replica a port whose `postMessage` is\n // a function call, so the command is recorded inside the\n // listener that sent it. The real port to the application\n // worker is still a real port, and everything crossing a\n // thread still crosses it.\n //\n // The cast is safe for the one consumer there is: the\n // channel registry takes what `open` returns and uses it as\n // a `ChannelPort`, and nothing transfers it.\n return log.tapPort(real as unknown as ChannelPort, token) as unknown as MessagePort;\n },\n get spawned(): boolean {\n return application !== undefined;\n },\n terminate(): void {\n // The shell owns that worker's lifetime, as `portHandle`\n // says; a tap in the middle owns even less of it.\n }\n };\n },\n dispose() {\n if (disposed) {\n return;\n }\n disposed = true;\n stopForwarding();\n host.onmessage = previousOnMessage;\n host.postMessage = originalPostMessage;\n }\n };\n}\n\n/**\n * What to call the cause an input starts, or null for a message that\n * is not an input.\n *\n * Position is in the label because two presses in different places are\n * two different causes to the person who made them, and the ids alone\n * do not say which was which.\n */\nexport function inputLabel(message: ShellToRuntimeMessage | undefined): string | null {\n switch (message?.type) {\n case 'pointerDown':\n case 'pointerUp':\n case 'pointerMove':\n return `${message.type} (${Math.round(message.x)}, ${Math.round(message.y)})`;\n case 'pointerCancel':\n return 'pointerCancel';\n case 'wheel':\n return `wheel (${Math.round(message.deltaX)}, ${Math.round(message.deltaY)})`;\n case 'keyDown':\n return `keyDown ${message.key}`;\n case 'keyUp':\n return `keyUp ${message.key}`;\n case 'beforeInput':\n return `beforeInput ${message.inputType}`;\n case 'paste':\n return 'paste';\n case 'semanticsAction':\n return `semantics ${message.action.action}`;\n default:\n // Everything else the shell sends is lifecycle, not input:\n // a resize, a tick, a visibility change. None of them is\n // something a person did.\n return null;\n }\n}\n","/**\n * Click a node on the canvas to pin it in the panel\n * (the first of the two things the node inspector deferred).\n *\n * Picking was left out because the panel could not name a node back\n * to the runtime. The devtools panel built that half: `select` and\n * `highlight` address a node by id, and the inspector already reports\n * the node under the pointer while it is on. What was still missing is\n * the click, and a click is the one part of this that cannot happen in\n * the render thread. By the time the runtime has an event, the event\n * has been dispatched; taking it back would mean asking the\n * application to forget a press it may already have acted on.\n *\n * So the pick happens where the click arrives, in the capture phase,\n * over the element the canvas is in. That is the shell doing what the\n * shell does — forwarding input, or in this case declining to\n * — and it costs nothing while picking is off,\n * because the listeners are attached only then.\n *\n * What it pins is whatever the inspector last reported as hovered, so\n * a picker needs the inspector on; the panel turns it on with the same\n * toggle.\n */\n\nexport interface NodePickerOptions {\n /**\n * The element to take clicks over, in the capture phase. The one the\n * application is mounted in, so the canvas is inside it.\n */\n readonly host: EventTarget;\n /** The node under the pointer, from the runtime's `hover` reports. */\n hovered(): string | null;\n}\n\n/** What a panel drives: arm it, and hear what was picked. */\nexport interface DevtoolsPicker {\n /** Whether clicking the canvas pins a node instead of reaching the application. */\n setEnabled(enabled: boolean): void;\n readonly enabled: boolean;\n /** Called with the id of the node picked. Pass null to stop listening. */\n onPick(listener: ((id: string) => void) | null): void;\n dispose(): void;\n}\n\n/** The events a pick has to swallow for the application not to see it. */\nconst SWALLOWED = ['pointerdown', 'pointerup', 'mousedown', 'mouseup', 'click'] as const;\n\nexport function createNodePicker(options: NodePickerOptions): DevtoolsPicker {\n let listener: ((id: string) => void) | null = null;\n let armed = false;\n let attached = false;\n\n const swallow = (event: Event): void => {\n // Every one of them, and not only the one that carries the\n // decision. The shell forwards `pointerdown` and `pointerup`\n // separately, so taking one of the pair would leave the\n // application with half a press, which is worse than either\n // letting it through or taking it whole.\n event.preventDefault();\n event.stopImmediatePropagation();\n if (event.type !== 'pointerup') {\n return;\n }\n const id = options.hovered();\n if (id !== null) {\n listener?.(id);\n }\n };\n\n const attach = (): void => {\n if (attached) {\n return;\n }\n attached = true;\n for (const type of SWALLOWED) {\n options.host.addEventListener(type, swallow, true);\n }\n };\n const detach = (): void => {\n if (!attached) {\n return;\n }\n attached = false;\n for (const type of SWALLOWED) {\n options.host.removeEventListener(type, swallow, true);\n }\n };\n\n return {\n setEnabled(enabled) {\n armed = enabled;\n if (enabled) {\n attach();\n } else {\n detach();\n }\n },\n get enabled() {\n return armed;\n },\n onPick(next) {\n listener = next;\n },\n dispose() {\n detach();\n listener = null;\n armed = false;\n }\n };\n}\n","import { formatAge, type UiNodeReport, type UiPropReport } from 'gesso-framework';\n\nexport interface NodeReportViewOptions {\n /**\n * Makes the props editable, calling this with the new value when one\n * is committed; `null` means \"remove it\", which puts an inherited\n * value back.\n *\n * Absent for a read-only view. The corner inspector passes nothing,\n * because it sets `pointer-events: none` and could not be typed into\n * anyway.\n */\n onEditProp?(name: string, value: unknown): void;\n}\n\n/**\n * A `UiNodeReport` as DOM: the node inspector's body, shared with the\n * devtools panel so a node read in a corner of the canvas and a node\n * picked from a tree are described in the same words.\n */\nexport function renderNodeReport(\n doc: Document,\n report: UiNodeReport,\n options: NodeReportViewOptions = {}\n): HTMLElement[] {\n const out: HTMLElement[] = [\n heading(doc, `${report.type} ${report.id}`),\n line(\n doc,\n 'box',\n `${round(report.box.width)} × ${round(report.box.height)} at (${round(report.box.x)}, ${round(report.box.y)})`\n )\n ];\n if (report.owners.length > 0) {\n out.push(line(doc, 'rendered by', report.owners.map(owner => owner.name).join(' inside ')));\n }\n if (report.modifiers.length > 0) {\n out.push(line(doc, 'modifiers', report.modifiers.join(', ')));\n }\n if (report.listens.length > 0) {\n out.push(line(doc, 'listens', report.listens.join(', ')));\n }\n if (report.beneath.length > 0) {\n // The answer to a dead click: this node took the press, and these\n // are the nodes it covers at the pointer, topmost first.\n out.push(\n line(\n doc,\n 'beneath',\n report.beneath\n .map(under => {\n const owner = under.owner === undefined ? '' : ` (${under.owner})`;\n const listens = under.listens.length === 0 ? '' : ` · ${under.listens.join(', ')}`;\n return `${under.type}${owner}${listens}`;\n })\n .join('; ')\n )\n );\n }\n if (report.semantics !== undefined) {\n const parts = [\n report.semantics.role,\n report.semantics.label === undefined ? undefined : `\"${report.semantics.label}\"`,\n report.semantics.value,\n report.semantics.states?.join(', ')\n ].filter(part => part !== undefined && part !== '');\n if (parts.length > 0) {\n out.push(line(doc, 'semantics', parts.join(' · ')));\n }\n }\n if (report.props.length > 0) {\n out.push(section(doc, 'props'), propList(doc, report.props, options));\n }\n if (report.environment.length > 0) {\n const list = doc.createElement('dl');\n list.className = 'rows';\n for (const entry of report.environment) {\n const term = doc.createElement('dt');\n term.textContent = entry.key;\n term.className = entry.provided ? 'provided' : '';\n const value = doc.createElement('dd');\n value.textContent = entry.value;\n if (entry.provided) {\n value.append(note(doc, 'provided here'));\n }\n list.append(term, value);\n }\n out.push(section(doc, 'environment'), list);\n }\n const explanation = doc.createElement('pre');\n explanation.className = 'explanation';\n explanation.textContent = report.explanation;\n out.push(section(doc, 'layout'), explanation);\n return out;\n}\n\nfunction heading(doc: Document, text: string): HTMLElement {\n const element = doc.createElement('h1');\n element.textContent = text;\n return element;\n}\n\nfunction section(doc: Document, text: string): HTMLElement {\n const element = doc.createElement('h2');\n element.textContent = text;\n return element;\n}\n\nfunction line(doc: Document, label: string, value: string): HTMLElement {\n const element = doc.createElement('p');\n const name = doc.createElement('span');\n name.className = 'label';\n name.textContent = `${label} `;\n element.append(name, doc.createTextNode(value));\n return element;\n}\n\nfunction note(doc: Document, text: string): HTMLElement {\n const element = doc.createElement('span');\n element.className = 'note';\n element.textContent = ` ${text}`;\n return element;\n}\n\n/**\n * The props, with the ones a modifier or a binding is driving marked.\n *\n * The mark is the point of the list. A declared value and a value a\n * modifier wrote over it look identical on the node, and the second is\n * the one that surprises people: B1's cascade is invisible until\n * something says which of the two is on screen.\n */\nfunction propList(doc: Document, props: readonly UiPropReport[], options: NodeReportViewOptions): HTMLElement {\n const list = doc.createElement('dl');\n list.className = 'rows';\n for (const prop of props) {\n const term = doc.createElement('dt');\n term.textContent = prop.name;\n term.className = prop.origin;\n const value = doc.createElement('dd');\n value.textContent = prop.value;\n if (prop.source !== undefined) {\n value.append(note(doc, prop.source));\n }\n if (prop.stream !== undefined) {\n // The three things a \"bound\" mark never said: what the stream\n // last produced, how many values it has produced, and how long\n // ago. A prop that is not updating and a prop whose stream has\n // said nothing since the screen was built look identical\n // without them.\n const stream = doc.createElement('span');\n stream.className = 'stream';\n const emittedAt = prop.stream.emittedAt;\n const age = emittedAt === null ? 'no value yet' : `${formatAge(Math.max(0, Date.now() - emittedAt))} ago`;\n const count = `${prop.stream.emissions}×`;\n stream.textContent = `${count} · ${age}${prop.stream.connected ? '' : ' · disconnected'}`;\n stream.title = `${prop.stream.source} last emitted ${prop.stream.value}`;\n value.append(stream);\n }\n if (options.onEditProp !== undefined) {\n makeEditable(doc, value, prop, options.onEditProp);\n }\n list.append(term, value);\n }\n return list;\n}\n\n/**\n * Turns a prop's value into a field on a click, and writes it back on\n * Enter.\n *\n * Typed rather than stepped, because the values are of every kind a\n * property can hold and a spinner would only serve the numbers. The\n * text is read as JSON when it parses and as a plain string when it\n * does not, so `12`, `\"#ff0000\"`, `#ff0000` and `[8, 4]` all mean what\n * they look like. Empty removes the property.\n *\n * Escape and blur both cancel: this writes into a running application,\n * and a value committed by looking away is not what anyone meant.\n */\nfunction makeEditable(\n doc: Document,\n cell: HTMLElement,\n prop: UiPropReport,\n commit: (name: string, value: unknown) => void\n): void {\n cell.classList.add('editable');\n cell.title = 'Click to edit';\n cell.addEventListener('click', () => {\n if (cell.querySelector('input') !== null) {\n return;\n }\n const field = doc.createElement('input');\n field.type = 'text';\n field.className = 'edit';\n field.value = prop.value;\n cell.textContent = '';\n cell.append(field);\n field.focus();\n field.select();\n const cancel = (): void => {\n cell.textContent = prop.value;\n };\n field.addEventListener('keydown', event => {\n if (event.key === 'Escape') {\n cancel();\n return;\n }\n if (event.key !== 'Enter') {\n return;\n }\n const text = field.value.trim();\n commit(prop.name, text === '' ? null : parseValue(text));\n });\n field.addEventListener('blur', cancel);\n });\n}\n\n/** JSON where it parses, the text itself where it does not. */\nfunction parseValue(text: string): unknown {\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction round(value: number): string {\n return Number.isInteger(value) ? String(value) : value.toFixed(2);\n}\n\n/**\n * The rules the report's elements use, for any stylesheet that shows one.\n *\n * Colours are the devtools panel's custom properties with the dark\n * palette as fallback, so the corner inspector, which defines none of\n * them, is unchanged, and the panel recolours the same report by\n * defining them.\n */\nexport const NODE_REPORT_STYLES = `\nh1 { margin: 0 0 6px; font-size: 12px; color: var(--gd-accent, #79c0ff); overflow-wrap: anywhere; }\nh2 {\n margin: 10px 0 4px;\n font-size: 10px;\n text-transform: uppercase;\n letter-spacing: 0.08em;\n color: var(--gd-muted, #8b949e);\n}\np { margin: 0 0 2px; }\n.label { color: var(--gd-muted, #8b949e); }\n.rows { display: grid; grid-template-columns: auto 1fr; gap: 0 8px; margin: 0; }\ndt { color: var(--gd-muted, #8b949e); overflow-wrap: anywhere; }\ndt.modifier { color: var(--gd-purple, #d2a8ff); }\ndt.binding { color: var(--gd-green, #7ee787); }\ndt.provided { color: var(--gd-orange, #ffa657); }\ndd { margin: 0; overflow-wrap: anywhere; }\ndd.editable { cursor: pointer; border-radius: 3px; }\ndd.editable:hover { background: var(--gd-bg-hover, #21262d); }\n.edit {\n width: 100%;\n box-sizing: border-box;\n border: 1px solid var(--gd-accent, #79c0ff);\n border-radius: 3px;\n padding: 0 3px;\n background: var(--gd-bg, #0d1117);\n color: var(--gd-text, #e6edf3);\n font: inherit;\n}\n.note { color: var(--gd-faint, #6e7681); }\n.stream { display: block; color: var(--gd-green, #7ee787); }\n.explanation { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: var(--gd-text-strong, #c9d1d9); }\n`;\n","import type { DevtoolsEvent, DevtoolsRequest } from 'gesso-framework';\nimport type { ActionEntry } from './ActionLog';\n\n/**\n * What a devtools panel and the page it inspects say to each other\n *.\n *\n * The framework's `DevtoolsRequest` and `DevtoolsEvent` are one\n * application's vocabulary. A page may run several (a documentation\n * site's examples), and a panel arrives after they started, so this\n * layer adds the two things the framework's protocol does not have: an\n * application id on every message, and a greeting that answers with\n * the list.\n *\n * Both sides speak through a `DevtoolsPort`, which is only `post` and\n * `onMessage`. Two ports are provided here: a pair joined in memory,\n * for a panel mounted in the same page and for tests, and one over\n * `window.postMessage`, which is how a browser extension's content\n * script reaches a page. The extension's own hop, from its content\n * script to its devtools page, is one more port of the same shape and\n * lives with the extension.\n */\n\nexport interface DevtoolsAppInfo {\n readonly id: string;\n readonly name: string;\n}\n\n/** Page to panel. */\nexport type PageMessage =\n /** The applications the page has connected; sent on `hello` and whenever the list changes. */\n | { type: 'apps'; apps: readonly DevtoolsAppInfo[] }\n | { type: 'event'; app: string; event: DevtoolsEvent }\n /** A store action log entry, from an `ActionLog` the application connected alongside itself. */\n | { type: 'action'; app: string; entry: ActionEntry }\n /**\n * A node the person clicked on the canvas while the panel was\n * picking. The panel selects it; the click never reaches the\n * application.\n */\n | { type: 'picked'; app: string; id: string };\n\n/** Panel to page. */\nexport type PanelMessage =\n /** \"Is anyone there\": answered with `apps`. */\n | { type: 'hello' }\n | { type: 'request'; app: string; request: DevtoolsRequest }\n /**\n * Turns click-to-pick on or off.\n *\n * Not a `DevtoolsRequest`, because it is not a question for the\n * runtime. Picking is a click that must be taken before the\n * application sees it, and the only place a click can be taken is\n * the page, where it arrives; the runtime is a thread away and would\n * have to be asked to un-dispatch something. So the page answers\n * this one, and the runtime is asked to `select` the node the page\n * names, which is a question it already answers.\n */\n | { type: 'pick'; app: string; enabled: boolean };\n\n/** One end of a conversation: what it hears and what it says. */\nexport interface DevtoolsPort<In, Out> {\n post(message: Out): void;\n /** Returns a function that stops listening. */\n onMessage(listener: (message: In) => void): () => void;\n /** Drops every listener and stops posting. */\n close(): void;\n}\n\n/** The page's end. */\nexport type PagePort = DevtoolsPort<PanelMessage, PageMessage>;\n/** The panel's end. */\nexport type PanelPort = DevtoolsPort<PageMessage, PanelMessage>;\n\n/**\n * Two ports joined in memory, delivering synchronously.\n *\n * For a panel mounted in the page it inspects, and for specs: the\n * hook and the panel are exercised end to end with nothing between\n * them but a function call.\n */\nexport function createDirectPorts(): { page: PagePort; panel: PanelPort } {\n const toPanel = new Set<(message: PageMessage) => void>();\n const toPage = new Set<(message: PanelMessage) => void>();\n let open = true;\n const page: PagePort = {\n post(message) {\n if (open) {\n for (const listener of Array.from(toPanel)) {\n listener(message);\n }\n }\n },\n onMessage(listener) {\n toPage.add(listener);\n return () => toPage.delete(listener);\n },\n close() {\n open = false;\n toPage.clear();\n }\n };\n const panel: PanelPort = {\n post(message) {\n if (open) {\n for (const listener of Array.from(toPage)) {\n listener(message);\n }\n }\n },\n onMessage(listener) {\n toPanel.add(listener);\n return () => toPanel.delete(listener);\n },\n close() {\n open = false;\n toPanel.clear();\n }\n };\n return { page, panel };\n}\n\n/** What a message carries over `window.postMessage`, so both sides can ignore everything else on the window. */\nexport interface Envelope<T> {\n readonly source: typeof ENVELOPE_SOURCE;\n /** Who it is for. A page ignores what it sent, and so does a panel. */\n readonly to: 'page' | 'panel';\n readonly message: T;\n}\n\nexport const ENVELOPE_SOURCE = 'gesso-devtools';\n\nexport function isEnvelope(value: unknown, to: 'page' | 'panel'): value is Envelope<unknown> {\n const envelope = value as { source?: unknown; to?: unknown; message?: unknown } | null;\n return (\n typeof envelope === 'object' &&\n envelope !== null &&\n envelope.source === ENVELOPE_SOURCE &&\n envelope.to === to &&\n 'message' in envelope\n );\n}\n\n/** The part of `Window` the transport uses, so a spec can hand it a plain object. */\nexport interface WindowLike {\n addEventListener(type: 'message', listener: (event: { data: unknown; source?: unknown }) => void): void;\n removeEventListener(type: 'message', listener: (event: { data: unknown; source?: unknown }) => void): void;\n postMessage(message: unknown, targetOrigin: string): void;\n readonly location?: { readonly origin: string };\n}\n\n/**\n * The page's end of a conversation over its own window.\n *\n * A content script shares the page's window but not its JavaScript, so\n * `window.postMessage` to the page's own origin is the one channel\n * they have. Messages are posted to that origin rather than to `*`,\n * and the page hears only envelopes addressed to it, so its own posts\n * come straight back through the same listener and are dropped.\n */\nexport function windowPagePort(win: WindowLike): PagePort {\n return windowPort<PanelMessage, PageMessage>(win, 'page', 'panel');\n}\n\n/** The other end, for whatever sits in the page beside it (an extension's content script). */\nexport function windowPanelPort(win: WindowLike): PanelPort {\n return windowPort<PageMessage, PanelMessage>(win, 'panel', 'page');\n}\n\nfunction windowPort<In, Out>(win: WindowLike, me: 'page' | 'panel', them: 'page' | 'panel'): DevtoolsPort<In, Out> {\n const listeners = new Set<(message: In) => void>();\n const origin = win.location?.origin;\n // `file:` and sandboxed pages have an opaque origin, which postMessage\n // refuses as a target; `*` is the only address they answer to.\n const target = origin === undefined || origin === 'null' || origin === '' ? '*' : origin;\n const handle = (event: { data: unknown }): void => {\n if (!isEnvelope(event.data, me)) {\n return;\n }\n const message = event.data.message as In;\n for (const listener of Array.from(listeners)) {\n listener(message);\n }\n };\n win.addEventListener('message', handle);\n let open = true;\n return {\n post(message) {\n if (open) {\n const envelope: Envelope<Out> = { source: ENVELOPE_SOURCE, to: them, message };\n win.postMessage(envelope, target);\n }\n },\n onMessage(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n close() {\n open = false;\n listeners.clear();\n win.removeEventListener('message', handle);\n }\n };\n}\n","import type { DevtoolsEvent, DevtoolsRequest } from 'gesso-framework';\nimport { forwardNewEntries, type ActionLog } from './ActionLog';\nimport type { DevtoolsPicker } from './NodePicker';\nimport {\n windowPagePort,\n type DevtoolsAppInfo,\n type PagePort,\n type PanelMessage,\n type WindowLike\n} from './PanelProtocol';\n\n/**\n * The page's side of the devtools panel.\n *\n * One object per page, kept on the window under a well-known name the\n * way React's devtools hook is, so that several bundles (a docs site's\n * examples, an application and its dependency) connect to the same\n * hook and a panel sees one list. Applications register with it;\n * panels attach ports to it; the hook routes requests to the right\n * application and fans every application's events out to every port.\n *\n * `connectDevtools(app)` is the one line an application adds. It also\n * installs the `window.postMessage` transport the first time, which is\n * how a browser extension's content script finds the page: nothing is\n * running in a page that has not connected, and a page that has\n * connected answers `hello`.\n */\n\n/** What an application must offer: both `WorkerApp` and `GessoApp` do. */\nexport interface DevtoolsApp {\n devtools(request: DevtoolsRequest): void;\n /**\n * Taken over by the hook while connected. An application that was\n * listening to its own devtools events has to choose: the panel or\n * itself. In practice the events only exist for panels.\n */\n onDevtools(listener: ((event: DevtoolsEvent) => void) | null): void;\n}\n\nexport interface ConnectDevtoolsOptions {\n /** What the panel calls this application. Default: the document title, else `app`. */\n readonly name?: string;\n /**\n * A store action log the panel should show alongside. The log stays\n * where it is (it taps ports in the page); the panel is sent each\n * entry as it is recorded.\n */\n readonly actions?: ActionLog;\n /**\n * Lets the panel pick a node by clicking the canvas.\n *\n * The page's half of picking, because a click has to be taken before\n * the application sees it and only the page has it in time. See\n * `createNodePicker`.\n */\n readonly picker?: DevtoolsPicker;\n /**\n * Where the hook lives and where the `postMessage` transport\n * listens. Default: the global window. `null` keeps the hook off any\n * window and installs no transport, for a panel mounted directly.\n */\n readonly window?: (WindowLike & HookHost) | null;\n}\n\nexport interface DevtoolsHook {\n /** The connected applications, in the order they connected. */\n readonly apps: readonly DevtoolsAppInfo[];\n /** Connects an application. Returns a function that disconnects it. */\n register(app: DevtoolsApp, options?: Omit<ConnectDevtoolsOptions, 'window'>): () => void;\n /** Attaches a panel's port. Returns a function that detaches it. */\n attach(port: PagePort): () => void;\n}\n\n/** The property the hook is kept under. */\nexport const HOOK_PROPERTY = '__GESSO_DEVTOOLS__';\n\n/** A window, as far as the hook is concerned: somewhere to keep itself. */\nexport interface HookHost {\n [HOOK_PROPERTY]?: DevtoolsHook;\n}\n\ninterface Registered {\n readonly info: DevtoolsAppInfo;\n readonly app: DevtoolsApp;\n readonly picker: DevtoolsPicker | undefined;\n readonly release: () => void;\n}\n\n/**\n * The page's hook, created on first use.\n *\n * With a window, the hook is stored on it and the `postMessage`\n * transport is attached once; without one (`null`), a fresh hook with\n * no transport, for specs and for a panel in the same page.\n */\nexport function getDevtoolsHook(win: (WindowLike & HookHost) | null = defaultWindow()): DevtoolsHook {\n if (win === null) {\n return createHook();\n }\n const existing = win[HOOK_PROPERTY];\n if (existing !== undefined) {\n return existing;\n }\n const hook = createHook();\n win[HOOK_PROPERTY] = hook;\n hook.attach(windowPagePort(win));\n return hook;\n}\n\n/**\n * Connects an application to the page's devtools hook, so a panel can\n * find it. Returns a function that disconnects it; call it when the\n * application is disposed.\n */\nexport function connectDevtools(app: DevtoolsApp, options: ConnectDevtoolsOptions = {}): () => void {\n const { window: win, ...rest } = options;\n return getDevtoolsHook(win).register(app, rest);\n}\n\nfunction createHook(): DevtoolsHook {\n const registered = new Map<string, Registered>();\n const ports = new Set<PagePort>();\n let nextId = 1;\n\n const infos = (): DevtoolsAppInfo[] => [...registered.values()].map(entry => entry.info);\n const broadcast = (message: Parameters<PagePort['post']>[0]): void => {\n for (const port of Array.from(ports)) {\n port.post(message);\n }\n };\n\n const handle = (message: PanelMessage, from: PagePort): void => {\n if (message.type === 'hello') {\n from.post({ type: 'apps', apps: infos() });\n return;\n }\n if (message.type === 'pick') {\n // An application registered without a picker simply cannot be\n // picked from; the panel's toggle then does nothing, which is\n // better than the panel refusing to show the toggle to some\n // applications and not others.\n registered.get(message.app)?.picker?.setEnabled(message.enabled);\n return;\n }\n // A request for an application that has gone is dropped: the panel\n // has been sent the new list and will catch up.\n registered.get(message.app)?.app.devtools(message.request);\n };\n\n return {\n get apps() {\n return infos();\n },\n register(app, options = {}) {\n const id = `app-${nextId++}`;\n const info: DevtoolsAppInfo = { id, name: options.name ?? defaultName() };\n app.onDevtools(event => broadcast({ type: 'event', app: id, event }));\n const stopActions = forwardActions(options.actions, entry => broadcast({ type: 'action', app: id, entry }));\n const picker = options.picker;\n picker?.onPick(picked => broadcast({ type: 'picked', app: id, id: picked }));\n const release = (): void => {\n app.onDevtools(null);\n stopActions();\n picker?.onPick(null);\n picker?.setEnabled(false);\n };\n registered.set(id, { info, app, picker, release });\n broadcast({ type: 'apps', apps: infos() });\n return () => {\n const entry = registered.get(id);\n if (entry === undefined) {\n return;\n }\n registered.delete(id);\n entry.release();\n broadcast({ type: 'apps', apps: infos() });\n };\n },\n attach(port) {\n ports.add(port);\n const stop = port.onMessage(message => handle(message, port));\n return () => {\n stop();\n ports.delete(port);\n };\n }\n };\n}\n\n/** Sends each new action log entry as it is recorded, when there is a log to read. */\nfunction forwardActions(log: ActionLog | undefined, send: (entry: ActionLog['entries'][number]) => void): () => void {\n return log === undefined ? () => {} : forwardNewEntries(log, send);\n}\n\nfunction defaultWindow(): (WindowLike & HookHost) | null {\n return typeof window === 'undefined' ? null : (window as unknown as WindowLike & HookHost);\n}\n\nfunction defaultName(): string {\n if (typeof document !== 'undefined' && document.title !== '') {\n return document.title;\n }\n return 'app';\n}\n","import { UI_FRAME_PHASES, type FrameMetrics, type UiFramePhase } from 'gesso-framework';\n\n/**\n * The frame profiler: where a frame's time went, over\n * the last few seconds, as a picture.\n *\n * The timings have existed since L7 and the playground has been showing\n * them as one clipped line of text, which is enough to read a number\n * off and not enough to see a shape. A stall, a phase that only wakes\n * on some frames, a render that grew when a route changed: all three\n * are obvious in a strip of bars and invisible in a running average.\n *\n * It draws into a small canvas rather than a div per bar, because at 60\n * frames a second a DOM per frame is more main-thread work than the\n * thing being profiled. For the same reason it redraws on a timer\n * rather than on every frame: the history is kept per frame, the\n * picture is repainted a few times a second.\n */\nexport interface FrameProfiler {\n /** Feed it every frame. Cheap: it appends and returns. */\n report(metrics: FrameMetrics): void;\n /** Shows or hides the panel. Hidden panels stop redrawing. */\n setVisible(visible: boolean): void;\n readonly visible: boolean;\n dispose(): void;\n}\n\nexport interface FrameProfilerOptions {\n /** How many frames the strip holds. Default 180, about three seconds. */\n readonly history?: number;\n /** How often the picture is repainted, in milliseconds. Default 250. */\n readonly redrawMs?: number;\n readonly corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n /**\n * `floating` (the default) sits in a corner over the application;\n * `docked` fills its host, for a panel that is not over anything.\n */\n readonly layout?: 'floating' | 'docked';\n}\n\n/** One bar's worth of history. */\nexport interface FrameSample {\n readonly phases: Readonly<Record<UiFramePhase, number>>;\n readonly total: number;\n /** Gap since the previous frame, on the rendering thread's clock. */\n readonly gap: number;\n readonly input: number | null;\n}\n\n/**\n * A colour per phase.\n *\n * Ordered as the phases run, and deliberately not a gradient: the\n * question a strip answers is \"which phase is that\", so the bands have\n * to be told apart at four pixels wide.\n */\nconst PHASE_COLORS: Record<UiFramePhase, string> = {\n ticks: '#d2a8ff',\n patches: '#79c0ff',\n environment: '#56d364',\n virtualize: '#e3b341',\n layout: '#f0883e',\n semantics: '#ff7b72',\n render: '#a5d6ff'\n};\n\n/** Above this, a frame missed a 60 Hz deadline. */\nconst BUDGET_MS = 16.7;\n\nexport function mountFrameProfiler(host: HTMLElement, options: FrameProfilerOptions = {}): FrameProfiler {\n const doc = host.ownerDocument;\n const view = doc.defaultView;\n const capacity = options.history ?? 180;\n const redrawMs = options.redrawMs ?? 250;\n\n const docked = options.layout === 'docked';\n const position = docked ? 'relative' : (view?.getComputedStyle(host).position ?? 'static');\n const restore = position === 'static' ? host.style.position : null;\n if (position === 'static') {\n host.style.position = 'relative';\n }\n\n const container = doc.createElement('div');\n const root = container.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = STYLES;\n const panel = doc.createElement('div');\n panel.className = docked ? 'panel docked' : `panel ${options.corner ?? 'top-right'}`;\n panel.hidden = true;\n const readout = doc.createElement('div');\n readout.className = 'readout';\n const strip = doc.createElement('canvas');\n strip.className = 'strip';\n strip.width = capacity * 2;\n strip.height = 96;\n const legend = doc.createElement('div');\n legend.className = 'legend';\n panel.append(readout, strip, legend);\n root.append(style, panel);\n host.appendChild(container);\n\n const samples: FrameSample[] = [];\n let previousAt: number | null = null;\n let visible = false;\n let timer: ReturnType<typeof setInterval> | null = null;\n\n const draw = (): void => {\n const ctx = strip.getContext('2d');\n if (ctx === null || samples.length === 0) {\n return;\n }\n const width = strip.width;\n const height = strip.height;\n ctx.clearRect(0, 0, width, height);\n // The scale follows the worst frame in view rather than the budget,\n // so a strip of 40 ms frames is still legible; the budget line says\n // where 60 Hz was.\n const peak = Math.max(BUDGET_MS, ...samples.map(sample => sample.total));\n const barWidth = width / capacity;\n samples.forEach((sample, index) => {\n const x = index * barWidth;\n let y = height;\n for (const phase of UI_FRAME_PHASES) {\n const ms = sample.phases[phase];\n if (ms <= 0) {\n continue;\n }\n const bar = (ms / peak) * height;\n ctx.fillStyle = PHASE_COLORS[phase];\n ctx.fillRect(x, y - bar, Math.max(1, barWidth - 0.5), bar);\n y -= bar;\n }\n });\n const budgetY = height - (BUDGET_MS / peak) * height;\n // The panel's token when docked in one, else the floating panel's own.\n ctx.strokeStyle =\n view?.getComputedStyle(panel).getPropertyValue('--gd-budget').trim() || 'rgba(255, 255, 255, 0.35)';\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(0, budgetY + 0.5);\n ctx.lineTo(width, budgetY + 0.5);\n ctx.stroke();\n\n const totals = summarize(samples);\n readout.textContent = '';\n readout.append(\n stat(doc, 'fps', totals.fps.toFixed(0)),\n stat(doc, 'frame', `${totals.meanTotal.toFixed(2)} ms`),\n stat(doc, 'worst', `${totals.worstTotal.toFixed(1)} ms`),\n stat(doc, 'gap', `${totals.worstGap.toFixed(0)} ms`),\n stat(doc, 'input', totals.worstInput === null ? '—' : `${totals.worstInput.toFixed(0)} ms`),\n stat(doc, 'peak', `${peak.toFixed(1)} ms`)\n );\n legend.textContent = '';\n for (const phase of UI_FRAME_PHASES) {\n const worst = totals.worstPhase[phase];\n // A phase that never had work in the whole window is dropped\n // rather than printed as a zero: `L7` made \"0 means it never ran\"\n // true, and a legend full of zeroes hides the two that matter.\n if (worst <= 0) {\n continue;\n }\n const entry = doc.createElement('span');\n entry.className = 'entry';\n const swatch = doc.createElement('i');\n swatch.style.background = PHASE_COLORS[phase];\n entry.append(swatch, doc.createTextNode(`${phase} ${worst.toFixed(2)}`));\n legend.append(entry);\n }\n };\n\n return {\n report(metrics) {\n const gap = previousAt === null ? 0 : metrics.at - previousAt;\n previousAt = metrics.at;\n samples.push({\n phases: metrics.phases,\n total: metrics.durationMs,\n gap,\n input: metrics.inputLatencyMs\n });\n if (samples.length > capacity) {\n samples.splice(0, samples.length - capacity);\n }\n },\n get visible() {\n return visible;\n },\n setVisible(next) {\n if (visible === next) {\n return;\n }\n visible = next;\n panel.hidden = !next;\n if (next) {\n draw();\n timer = setInterval(draw, redrawMs);\n } else if (timer !== null) {\n clearInterval(timer);\n timer = null;\n }\n },\n dispose() {\n if (timer !== null) {\n clearInterval(timer);\n }\n container.remove();\n if (restore !== null) {\n host.style.position = restore;\n }\n }\n };\n}\n\nexport interface FrameSummary {\n readonly fps: number;\n readonly meanTotal: number;\n readonly worstTotal: number;\n readonly worstGap: number;\n readonly worstInput: number | null;\n readonly worstPhase: Record<UiFramePhase, number>;\n}\n\n/**\n * The window's numbers.\n *\n * Peak rather than mean for the phases, for the reason the playground's\n * status line already gives: patches and environment run on a small\n * minority of frames, so a mean would report them as idle on exactly\n * the frames where they were the cost.\n */\nexport function summarize(samples: readonly FrameSample[]): FrameSummary {\n const worstPhase = { ticks: 0, patches: 0, environment: 0, virtualize: 0, layout: 0, semantics: 0, render: 0 };\n let totalSum = 0;\n let worstTotal = 0;\n let worstGap = 0;\n let worstInput: number | null = null;\n let gapSum = 0;\n let gaps = 0;\n for (const sample of samples) {\n totalSum += sample.total;\n worstTotal = Math.max(worstTotal, sample.total);\n if (sample.gap > 0) {\n worstGap = Math.max(worstGap, sample.gap);\n gapSum += sample.gap;\n gaps++;\n }\n if (sample.input !== null) {\n worstInput = Math.max(worstInput ?? 0, sample.input);\n }\n for (const phase of UI_FRAME_PHASES) {\n worstPhase[phase] = Math.max(worstPhase[phase], sample.phases[phase]);\n }\n }\n return {\n // From the gaps between frames rather than from a wall clock: the\n // profiler may be fed from a worker, where the only honest measure\n // of when a frame happened is the timestamp that frame carries.\n fps: gaps === 0 ? 0 : 1000 / (gapSum / gaps),\n meanTotal: samples.length === 0 ? 0 : totalSum / samples.length,\n worstTotal,\n worstGap,\n worstInput,\n worstPhase\n };\n}\n\nfunction stat(doc: Document, label: string, value: string): HTMLElement {\n const element = doc.createElement('span');\n element.className = 'stat';\n const name = doc.createElement('i');\n name.textContent = label;\n element.append(name, doc.createTextNode(value));\n return element;\n}\n\nconst STYLES = `\n:host { all: initial; }\n.panel {\n position: absolute;\n z-index: 2147482000;\n width: 300px;\n pointer-events: none;\n margin: 12px;\n padding: 8px 10px;\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(13, 17, 23, 0.94);\n color: #e6edf3;\n font: 10px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n.panel[hidden] { display: none; }\n.panel.docked { position: static; width: auto; margin: 0; pointer-events: auto; border: none; background: transparent; color: var(--gd-text, #e6edf3); }\n.panel.docked .stat i, .panel.docked .legend { color: var(--gd-muted, #8b949e); }\n.panel.docked .strip { height: 96px; }\n.top-left { top: 0; left: 0; }\n.top-right { top: 0; right: 0; }\n.bottom-left { bottom: 0; left: 0; }\n.bottom-right { bottom: 0; right: 0; }\n.strip { display: block; width: 100%; height: 48px; margin: 6px 0; }\n.readout { display: flex; flex-wrap: wrap; gap: 2px 10px; }\n.stat i { color: #8b949e; font-style: normal; margin-right: 4px; }\n.legend { display: flex; flex-wrap: wrap; gap: 2px 8px; color: #8b949e; }\n.entry { display: inline-flex; align-items: center; gap: 3px; }\n.entry i { width: 7px; height: 7px; border-radius: 2px; display: inline-block; }\n`;\n","import type { UiTreeNode } from 'gesso-framework';\n\n/**\n * A tree snapshot as the rows a panel draws.\n *\n * The panel keeps a set of expanded ids and asks for the rows; the\n * tree can be replaced by a new snapshot every frame while the set\n * stays, so a person's unfolding survives the application changing\n * under it. Ids are positional, which is what makes that work and also\n * what makes it approximate: a row that was a button can become a text\n * when a list reorders. The report says what it is now.\n */\nexport interface TreeRow {\n readonly node: UiTreeNode;\n readonly depth: number;\n readonly expandable: boolean;\n readonly expanded: boolean;\n /** The nearest component anchor at or above this node, if any. */\n readonly owner?: string;\n}\n\n/** The rows of the tree with `expanded` nodes unfolded, in document order. */\nexport function treeRows(root: UiTreeNode, expanded: ReadonlySet<string>): TreeRow[] {\n const rows: TreeRow[] = [];\n const visit = (node: UiTreeNode, depth: number, owner: string | undefined): void => {\n const own = node.component ?? owner;\n const expandable = node.children.length > 0;\n const isExpanded = expandable && expanded.has(node.id);\n rows.push({ node, depth, expandable, expanded: isExpanded, ...(own === undefined ? {} : { owner: own }) });\n if (isExpanded) {\n for (const child of node.children) {\n visit(child, depth + 1, own);\n }\n }\n };\n visit(root, 0, undefined);\n return rows;\n}\n\n/** The ids of every node with children down to `depth` levels, for a first unfolding. */\nexport function idsToDepth(root: UiTreeNode, depth: number): string[] {\n const ids: string[] = [];\n const visit = (node: UiTreeNode, level: number): void => {\n if (level >= depth || node.children.length === 0) {\n return;\n }\n ids.push(node.id);\n for (const child of node.children) {\n visit(child, level + 1);\n }\n };\n visit(root, 0);\n return ids;\n}\n\n/** The ids of the ancestors of `id`, root first, or null when the tree has no such node. */\nexport function pathTo(root: UiTreeNode, id: string): string[] | null {\n const path: string[] = [];\n const visit = (node: UiTreeNode): boolean => {\n if (node.id === id) {\n return true;\n }\n path.push(node.id);\n for (const child of node.children) {\n if (visit(child)) {\n return true;\n }\n }\n path.pop();\n return false;\n };\n return visit(root) ? path : null;\n}\n\n/** One row's label, as the tree prints it. */\nexport function rowLabel(row: TreeRow): string {\n const { node } = row;\n const parts = [node.type];\n if (node.component !== undefined) {\n parts.push(`<${node.component}>`);\n }\n if (node.text !== undefined) {\n parts.push(`\"${node.text}\"`);\n }\n return parts.join(' ');\n}\n","import type { ConsoleEntry, DevtoolsEvent, DevtoolsRequest, UiNodeReport, UiTreeSnapshot } from 'gesso-framework';\nimport type { ActionEntry } from './ActionLog';\nimport { describeActionEntry } from './ActionLogPanel';\nimport { mountFrameProfiler, type FrameProfiler } from './FrameProfiler';\nimport { NODE_REPORT_STYLES, renderNodeReport } from './NodeReportView';\nimport type { DevtoolsAppInfo, PageMessage, PanelPort } from './PanelProtocol';\nimport { idsToDepth, pathTo, rowLabel, treeRows } from './TreeRows';\n\n/**\n * The devtools panel: the tree, a node's\n * report, the workers' consoles, the frame profiler and the action log,\n * in one place, docked outside the canvas.\n *\n * It is plain DOM against a `PanelPort`, and knows nothing about where\n * it is mounted: a Chrome extension's devtools page, or a pane in the\n * page itself. That is what lets the same panel be both, and what lets\n * a spec drive it through a port joined in memory.\n *\n * Unlike the in-page inspector, this panel does not float over the\n * application, so it takes pointer events and can be as tall as its\n * host. The trade is that it cannot point at the canvas with the\n * pointer; it points with `highlight`, and the runtime draws the box.\n */\nexport interface DevtoolsPanel {\n /** The application the panel is showing, or null with none connected. */\n readonly app: DevtoolsAppInfo | null;\n /** Shows a different connected application. */\n show(appId: string): void;\n /** Changes the theme; see `DevtoolsPanelOptions.theme`. */\n setTheme(theme: DevtoolsPanelTheme): void;\n dispose(): void;\n}\n\n/**\n * `light` and `dark` are the panel's two palettes; `auto` follows the\n * viewer's `prefers-color-scheme`. A host that knows better than the\n * media query says so: Chrome's devtools has its own theme setting,\n * which the extension reads and passes here, and the playground is\n * dark whatever the system prefers.\n */\nexport type DevtoolsPanelTheme = 'light' | 'dark' | 'auto';\n\nexport interface DevtoolsPanelOptions {\n /** Which palette. Default `auto`. */\n readonly theme?: DevtoolsPanelTheme;\n /** How many levels of the tree open on the first snapshot. Default 4. */\n readonly openDepth?: number;\n /** How many console entries are kept. Default 500. */\n readonly consoleLimit?: number;\n /** How many action entries are kept. Default 300. */\n readonly actionLimit?: number;\n}\n\ntype Tab = 'tree' | 'streams' | 'console' | 'frames' | 'actions';\nconst TABS: readonly { readonly id: Tab; readonly label: string }[] = [\n { id: 'tree', label: 'Tree' },\n { id: 'streams', label: 'Streams' },\n { id: 'console', label: 'Console' },\n { id: 'frames', label: 'Frames' },\n { id: 'actions', label: 'Actions' }\n];\n\n/** Live subscriptions under one component, as the streams view lists them. */\ninterface ComponentStreams {\n readonly name: string;\n /** The component's anchor node, so a row selects the thing it names. */\n readonly anchorId: string;\n count: number;\n}\n\nexport function mountDevtoolsPanel(\n host: HTMLElement,\n port: PanelPort,\n options: DevtoolsPanelOptions = {}\n): DevtoolsPanel {\n const doc = host.ownerDocument;\n const openDepth = options.openDepth ?? 4;\n const consoleLimit = options.consoleLimit ?? 500;\n const actionLimit = options.actionLimit ?? 300;\n\n const container = doc.createElement('div');\n container.className = 'gesso-devtools';\n const applyTheme = (theme: DevtoolsPanelTheme): void => {\n // No attribute for `auto`, so the stylesheet's media query decides.\n if (theme === 'auto') {\n delete container.dataset['theme'];\n } else {\n container.dataset['theme'] = theme;\n }\n };\n applyTheme(options.theme ?? 'auto');\n const root = container.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = STYLES;\n root.append(style);\n host.appendChild(container);\n\n // ---- toolbar\n const toolbar = el(doc, 'div', 'toolbar');\n const picker = doc.createElement('select');\n picker.className = 'picker';\n picker.hidden = true;\n const tabs = el(doc, 'div', 'tabs');\n const tabButtons = new Map<Tab, HTMLButtonElement>();\n for (const tab of TABS) {\n const button = doc.createElement('button');\n button.type = 'button';\n button.textContent = tab.label;\n button.addEventListener('click', () => showTab(tab.id));\n tabButtons.set(tab.id, button);\n tabs.append(button);\n }\n const inspectToggle = doc.createElement('label');\n inspectToggle.className = 'toggle';\n const inspectBox = doc.createElement('input');\n inspectBox.type = 'checkbox';\n inspectToggle.append(inspectBox, doc.createTextNode(' Inspect layout'));\n const pickToggle = doc.createElement('label');\n pickToggle.className = 'toggle';\n const pickBox = doc.createElement('input');\n pickBox.type = 'checkbox';\n pickToggle.append(pickBox, doc.createTextNode(' Pick'));\n pickToggle.title = 'Click a node on the canvas to select it here. The click does not reach the application.';\n const marksToggle = doc.createElement('label');\n marksToggle.className = 'toggle';\n const marksBox = doc.createElement('input');\n marksBox.type = 'checkbox';\n marksToggle.append(marksBox, doc.createTextNode(' Marks'));\n marksToggle.title =\n \"Name the frame phases and the channel traffic for the browser's own profiler, \" +\n 'so a performance recording shows what Gesso was doing. Off by default: a frame ' +\n 'nobody is profiling pays nothing.';\n const status = el(doc, 'span', 'status');\n toolbar.append(picker, tabs, inspectToggle, pickToggle, marksToggle, status);\n root.append(toolbar);\n\n // ---- views\n const views = el(doc, 'div', 'views');\n const treeView = el(doc, 'div', 'view tree-view');\n const treePane = el(doc, 'div', 'tree');\n const reportPane = el(doc, 'div', 'report');\n treeView.append(treePane, reportPane);\n const consoleView = el(doc, 'div', 'view console-view');\n const consoleList = el(doc, 'ol', 'log');\n const consoleBar = el(doc, 'div', 'bar');\n const clearConsole = doc.createElement('button');\n clearConsole.type = 'button';\n clearConsole.textContent = 'Clear';\n consoleBar.append(clearConsole);\n consoleView.append(consoleBar, consoleList);\n const framesView = el(doc, 'div', 'view frames-view');\n const actionsView = el(doc, 'div', 'view actions-view');\n const actionsList = el(doc, 'ol', 'log');\n actionsView.append(actionsList);\n const streamsView = el(doc, 'div', 'view streams-view');\n const streamsBar = el(doc, 'div', 'bar');\n const streamsTotal = el(doc, 'span', 'total');\n const resetStreams = doc.createElement('button');\n resetStreams.type = 'button';\n resetStreams.textContent = 'Mark';\n resetStreams.title = 'Take the counts as they are now, so what follows reads as a change from here.';\n streamsBar.append(streamsTotal, resetStreams);\n const streamsList = el(doc, 'div', 'streams');\n streamsView.append(streamsBar, streamsList);\n views.append(treeView, streamsView, consoleView, framesView, actionsView);\n root.append(views);\n const viewFor: Record<Tab, HTMLElement> = {\n tree: treeView,\n streams: streamsView,\n console: consoleView,\n frames: framesView,\n actions: actionsView\n };\n\n // ---- state\n let apps: readonly DevtoolsAppInfo[] = [];\n let current: DevtoolsAppInfo | null = null;\n let tab: Tab = 'tree';\n let snapshot: UiTreeSnapshot | null = null;\n const expanded = new Set<string>();\n let openedOnce = false;\n let selectedId: string | null = null;\n let hoveredOnCanvas: string | null = null;\n let highlighted: string | null = null;\n let report: UiNodeReport | null = null;\n const consoleEntries: (\n | ConsoleEntry\n | { error: true; message: string; stack?: string; source: string; at: number }\n )[] = [];\n const actionEntries: ActionEntry[] = [];\n let actionOrigin: number | null = null;\n let profiler: FrameProfiler | null = null;\n /**\n * Counts as of the last \"Mark\", by component anchor.\n *\n * A leak is not a number, it is a number that climbs: forty\n * subscriptions under a list is either right or wrong depending on\n * the list, and forty more after navigating away and back is wrong\n * whatever the list. So the view shows the count and the change\n * since a moment the person chose.\n */\n let streamBaseline = new Map<string, number>();\n let baselineTotal: number | null = null;\n\n const request = (message: DevtoolsRequest): void => {\n if (current !== null) {\n port.post({ type: 'request', app: current.id, request: message });\n }\n };\n\n // What the current application is asked to keep sending, for as long\n // as it is the one shown. Frames only while the frames tab is up,\n // because a frame a second is a frame a second.\n const subscribe = (): void => {\n request({ kind: 'console', enabled: true });\n request({ kind: 'watchTree', enabled: true });\n request({ kind: 'watchFrames', enabled: tab === 'frames' });\n request({ kind: 'inspector', enabled: inspectBox.checked });\n };\n const unsubscribe = (): void => {\n request({ kind: 'console', enabled: false });\n request({ kind: 'watchTree', enabled: false });\n request({ kind: 'watchFrames', enabled: false });\n request({ kind: 'inspector', enabled: false });\n request({ kind: 'select', id: null });\n request({ kind: 'highlight', id: null });\n request({ kind: 'marks', enabled: false });\n if (current !== null && pickBox.checked) {\n port.post({ type: 'pick', app: current.id, enabled: false });\n }\n };\n\n const setStatus = (text: string): void => {\n status.textContent = text;\n };\n\n const showApp = (info: DevtoolsAppInfo | null): void => {\n if (current?.id === info?.id) {\n return;\n }\n if (current !== null) {\n unsubscribe();\n }\n current = info;\n snapshot = null;\n expanded.clear();\n openedOnce = false;\n selectedId = null;\n highlighted = null;\n hoveredOnCanvas = null;\n report = null;\n consoleEntries.length = 0;\n actionEntries.length = 0;\n actionOrigin = null;\n streamBaseline = new Map();\n baselineTotal = null;\n renderTree();\n renderReport();\n renderConsole();\n renderActions();\n renderStreams();\n if (info === null) {\n setStatus('No Gesso application connected. Call connectDevtools(app) in the page.');\n return;\n }\n setStatus('');\n picker.value = info.id;\n subscribe();\n };\n\n const showTab = (next: Tab): void => {\n tab = next;\n for (const [id, button] of tabButtons) {\n button.className = id === next ? 'active' : '';\n }\n for (const [id, view] of Object.entries(viewFor) as [Tab, HTMLElement][]) {\n view.hidden = id !== next;\n }\n request({ kind: 'watchFrames', enabled: next === 'frames' });\n if (next === 'frames') {\n profiler ??= mountFrameProfiler(framesView, { layout: 'docked' });\n profiler.setVisible(true);\n } else {\n profiler?.setVisible(false);\n }\n };\n\n // ---- tree\n const renderTree = (): void => {\n treePane.textContent = '';\n if (snapshot === null) {\n return;\n }\n if (!openedOnce) {\n openedOnce = true;\n for (const id of idsToDepth(snapshot.root, openDepth)) {\n expanded.add(id);\n }\n }\n const rows = treeRows(snapshot.root, expanded);\n for (const row of rows) {\n const line = el(doc, 'div', 'row');\n if (row.node.id === selectedId) {\n line.classList.add('selected');\n }\n if (row.node.id === hoveredOnCanvas) {\n line.classList.add('hovered');\n }\n line.style.paddingLeft = `${8 + row.depth * 12}px`;\n line.dataset['id'] = row.node.id;\n const disclosure = el(doc, 'span', 'disclosure');\n disclosure.textContent = row.expandable ? (row.expanded ? '▾' : '▸') : '';\n disclosure.addEventListener('click', event => {\n event.stopPropagation();\n if (expanded.has(row.node.id)) {\n expanded.delete(row.node.id);\n } else {\n expanded.add(row.node.id);\n }\n renderTree();\n });\n const type = el(doc, 'span', 'type');\n type.textContent = row.node.type;\n line.append(disclosure, type);\n if (row.node.component !== undefined) {\n const component = el(doc, 'span', 'component');\n component.textContent = `<${row.node.component}>`;\n line.append(component);\n }\n if (row.node.text !== undefined) {\n const text = el(doc, 'span', 'text');\n text.textContent = `\"${row.node.text}\"`;\n line.append(text);\n }\n const id = el(doc, 'span', 'id');\n id.textContent = row.node.id;\n line.append(id);\n line.title = rowLabel(row) + (row.owner === undefined ? '' : ` · rendered by ${row.owner}`);\n line.addEventListener('mouseenter', () => setHighlight(row.node.id));\n line.addEventListener('click', () => select(row.node.id));\n treePane.append(line);\n }\n const count = el(doc, 'div', 'count');\n count.textContent = `${snapshot.nodes} nodes`;\n treePane.append(count);\n };\n treePane.addEventListener('mouseleave', () => setHighlight(null));\n\n const setHighlight = (id: string | null): void => {\n if (highlighted === id) {\n return;\n }\n highlighted = id;\n request({ kind: 'highlight', id });\n };\n\n const select = (id: string | null): void => {\n selectedId = id;\n request({ kind: 'select', id });\n if (id === null) {\n report = null;\n renderReport();\n }\n renderTree();\n };\n\n const reveal = (id: string): void => {\n if (snapshot === null) {\n return;\n }\n const path = pathTo(snapshot.root, id);\n if (path === null) {\n return;\n }\n for (const ancestor of path) {\n expanded.add(ancestor);\n }\n renderTree();\n const row = [...treePane.children].find(child => (child as HTMLElement).dataset['id'] === id);\n (row as HTMLElement | undefined)?.scrollIntoView?.({ block: 'nearest' });\n };\n\n // ---- streams\n /**\n * Live subscriptions per component, from the tree.\n *\n * Aggregated here rather than reported per component by the runtime,\n * because the tree already says which component a node belongs to\n * and already arrives whenever the count changes. Nodes above every\n * component anchor are the runtime's own and are listed under the\n * root.\n */\n const componentStreams = (): ComponentStreams[] => {\n if (snapshot === null) {\n return [];\n }\n const totals = new Map<string, ComponentStreams>();\n const visit = (node: UiTreeSnapshot['root'], owner: ComponentStreams): void => {\n const here =\n node.component === undefined\n ? owner\n : (totals.get(node.id) ?? { name: node.component, anchorId: node.id, count: 0 });\n if (node.component !== undefined) {\n totals.set(node.id, here);\n }\n here.count += node.subscriptions ?? 0;\n for (const child of node.children) {\n visit(child, here);\n }\n };\n const root: ComponentStreams = { name: '(root)', anchorId: snapshot.root.id, count: 0 };\n totals.set(snapshot.root.id, root);\n visit(snapshot.root, root);\n return [...totals.values()].sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));\n };\n\n const renderStreams = (): void => {\n streamsList.textContent = '';\n if (snapshot === null) {\n streamsTotal.textContent = 'Waiting for a tree.';\n return;\n }\n const change = baselineTotal === null ? '' : ` (${signed(snapshot.subscriptions - baselineTotal)} since the mark)`;\n streamsTotal.textContent = `${snapshot.subscriptions} live subscriptions in the graph${change}`;\n const rows = componentStreams();\n if (rows.length === 0) {\n const empty = el(doc, 'p', 'empty');\n empty.textContent = 'Nothing is subscribed.';\n streamsList.append(empty);\n return;\n }\n const largest = rows[0]?.count ?? 0;\n for (const entry of rows) {\n const row = el(doc, 'div', 'stream-row');\n const name = el(doc, 'span', 'component');\n name.textContent = entry.name;\n const bar = el(doc, 'span', 'bar-track');\n const fill = el(doc, 'span', 'bar-fill');\n fill.style.width = `${largest === 0 ? 0 : Math.round((entry.count / largest) * 100)}%`;\n bar.append(fill);\n const count = el(doc, 'span', 'count');\n const before = streamBaseline.get(entry.anchorId);\n const delta = before === undefined || before === entry.count ? '' : ` ${signed(entry.count - before)}`;\n count.textContent = `${entry.count}${delta}`;\n if (delta !== '') {\n count.classList.add('changed');\n }\n row.append(name, bar, count);\n row.title = entry.anchorId;\n row.addEventListener('mouseenter', () => setHighlight(entry.anchorId));\n row.addEventListener('click', () => {\n showTab('tree');\n reveal(entry.anchorId);\n select(entry.anchorId);\n });\n streamsList.append(row);\n }\n };\n streamsList.addEventListener('mouseleave', () => setHighlight(null));\n resetStreams.addEventListener('click', () => {\n streamBaseline = new Map(componentStreams().map(entry => [entry.anchorId, entry.count]));\n baselineTotal = snapshot?.subscriptions ?? null;\n renderStreams();\n });\n\n const renderReport = (): void => {\n reportPane.textContent = '';\n if (report === null) {\n const empty = el(doc, 'p', 'empty');\n empty.textContent =\n selectedId === null ? 'Select a node in the tree to read its report.' : 'The selected node has left the tree.';\n reportPane.append(empty);\n return;\n }\n const id = report.id;\n reportPane.append(\n ...renderNodeReport(doc, report, {\n // The write half of the addressed channel: `select` names a\n // node to read, `setProp` names one to change. The frame the\n // write dirties sends the report back, so the value shown\n // afterwards is the value the node holds, not the one typed.\n onEditProp: (name, value) => request({ kind: 'setProp', id, name, value })\n })\n );\n };\n\n // ---- console\n const renderConsole = (): void => {\n consoleList.textContent = '';\n for (const entry of consoleEntries) {\n const item = doc.createElement('li');\n if ('error' in entry) {\n item.className = 'entry error';\n item.append(badge(doc, 'render'), badge(doc, entry.source, 'source'), doc.createTextNode(entry.message));\n if (entry.stack !== undefined) {\n const stack = doc.createElement('pre');\n stack.textContent = entry.stack;\n item.append(stack);\n }\n } else {\n item.className = `entry ${entry.level}`;\n item.append(badge(doc, entry.thread), doc.createTextNode(entry.args.join(' ')));\n }\n consoleList.append(item);\n }\n consoleList.scrollTop = consoleList.scrollHeight;\n };\n clearConsole.addEventListener('click', () => {\n consoleEntries.length = 0;\n renderConsole();\n });\n\n // ---- actions\n const renderActions = (): void => {\n actionsList.textContent = '';\n if (actionEntries.length === 0) {\n const empty = el(doc, 'li', 'empty');\n empty.textContent = 'No store actions. Pass an ActionLog to connectDevtools to see commands and patches here.';\n actionsList.append(empty);\n return;\n }\n for (const entry of actionEntries) {\n const item = doc.createElement('li');\n item.className = `entry ${entry.kind}`;\n const time = el(doc, 'span', 'time');\n time.textContent = `+${((entry.at - (actionOrigin ?? entry.at)) / 1000).toFixed(2)}s`;\n item.append(time, badge(doc, entry.kind), doc.createTextNode(describeActionEntry(entry)));\n if (entry.cause !== undefined) {\n // The one number that ties a click, the command it sent, the\n // patches that answered and the frame that drew them. Reading\n // it is the thing that used to be timestamp arithmetic across\n // three threads.\n const cause = el(doc, 'span', 'cause');\n cause.textContent = ` #${entry.cause.id} ${entry.cause.label}`;\n item.append(cause);\n }\n actionsList.append(item);\n }\n actionsList.scrollTop = actionsList.scrollHeight;\n };\n\n // ---- incoming\n const onEvent = (event: DevtoolsEvent): void => {\n switch (event.kind) {\n case 'tree':\n snapshot = event.tree;\n renderTree();\n renderStreams();\n break;\n case 'action':\n pushAction(event.entry);\n break;\n case 'report':\n if (event.id === selectedId) {\n report = event.report;\n if (report === null) {\n selectedId = null;\n }\n renderReport();\n renderTree();\n }\n break;\n case 'hover':\n hoveredOnCanvas = event.report?.id ?? null;\n if (hoveredOnCanvas !== null) {\n reveal(hoveredOnCanvas);\n } else {\n renderTree();\n }\n break;\n case 'console':\n consoleEntries.push(event.entry);\n if (consoleEntries.length > consoleLimit) {\n consoleEntries.splice(0, consoleEntries.length - consoleLimit);\n }\n renderConsole();\n break;\n case 'error':\n consoleEntries.push({\n error: true,\n message: event.message,\n source: event.source,\n at: Date.now(),\n ...(event.stack === undefined ? {} : { stack: event.stack })\n });\n renderConsole();\n break;\n case 'frame':\n profiler?.report(event.metrics);\n break;\n }\n };\n\n const onMessage = (message: PageMessage): void => {\n if (message.type === 'apps') {\n apps = message.apps;\n picker.textContent = '';\n for (const info of apps) {\n const option = doc.createElement('option');\n option.value = info.id;\n option.textContent = info.name;\n picker.append(option);\n }\n picker.hidden = apps.length < 2;\n const still = apps.find(info => info.id === current?.id);\n showApp(still ?? apps[0] ?? null);\n return;\n }\n if (message.app !== current?.id) {\n return;\n }\n if (message.type === 'event') {\n onEvent(message.event);\n return;\n }\n if (message.type === 'picked') {\n // The click never reached the application; this is what it meant.\n // The tree comes forward with it, because somebody who pointed\n // at a node meant to read it and not to keep watching the log.\n showTab('tree');\n reveal(message.id);\n select(message.id);\n return;\n }\n pushAction(message.entry);\n };\n\n /** One action log entry, from either route: the page's log or the render worker's. */\n const pushAction = (entry: ActionEntry): void => {\n actionOrigin ??= entry.at;\n actionEntries.push(entry);\n if (actionEntries.length > actionLimit) {\n actionEntries.splice(0, actionEntries.length - actionLimit);\n }\n renderActions();\n };\n\n picker.addEventListener('change', () => {\n const info = apps.find(candidate => candidate.id === picker.value);\n if (info !== undefined) {\n showApp(info);\n }\n });\n inspectBox.addEventListener('change', () => request({ kind: 'inspector', enabled: inspectBox.checked }));\n // Picking needs the inspector: what a click pins is whatever the\n // runtime last reported as hovered, and nothing is reported while\n // the inspector is off. Turning picking on therefore turns it on,\n // and turning picking off puts it back the way the person left it.\n pickBox.addEventListener('change', () => {\n if (current === null) {\n return;\n }\n port.post({ type: 'pick', app: current.id, enabled: pickBox.checked });\n request({ kind: 'inspector', enabled: pickBox.checked || inspectBox.checked });\n });\n // The browser's own profiler, and nothing else: the runtime names\n // its spans while this is on and pays one boolean read for the\n // question while it is off.\n marksBox.addEventListener('change', () => request({ kind: 'marks', enabled: marksBox.checked }));\n\n const stop = port.onMessage(onMessage);\n showTab('tree');\n showApp(null);\n port.post({ type: 'hello' });\n\n return {\n get app() {\n return current;\n },\n show(appId) {\n const info = apps.find(candidate => candidate.id === appId);\n if (info !== undefined) {\n showApp(info);\n }\n },\n setTheme: applyTheme,\n dispose() {\n if (current !== null) {\n unsubscribe();\n }\n stop();\n profiler?.dispose();\n container.remove();\n }\n };\n}\n\nfunction el(doc: Document, tag: string, className: string): HTMLElement {\n const element = doc.createElement(tag);\n element.className = className;\n return element;\n}\n\n/** A change with its sign, so a rise reads as one at a glance. */\nfunction signed(delta: number): string {\n return delta > 0 ? `+${delta}` : String(delta);\n}\n\nfunction badge(doc: Document, text: string, extra = ''): HTMLElement {\n const element = el(doc, 'span', `badge ${text} ${extra}`.trim());\n element.textContent = text;\n return element;\n}\n\n/**\n * The two palettes, as custom properties on the shadow host.\n *\n * Light is the default set and dark overrides it, twice: once for a\n * host that asked for dark, and once for a viewer who prefers it when\n * the host did not say. The report view and the docked profiler read\n * the same properties with the dark values as fallbacks, so the\n * in-page corner tools, which define none of them, look as they did.\n */\nconst DARK_TOKENS = `\n --gd-bg: #0d1117;\n --gd-bg-raised: #161b22;\n --gd-bg-hover: #21262d;\n --gd-border: #30363d;\n --gd-text: #e6edf3;\n --gd-text-strong: #c9d1d9;\n --gd-muted: #8b949e;\n --gd-faint: #6e7681;\n --gd-fainter: #484f58;\n --gd-accent: #79c0ff;\n --gd-info: #a5d6ff;\n --gd-purple: #d2a8ff;\n --gd-green: #7ee787;\n --gd-orange: #ffa657;\n --gd-red: #ff7b72;\n --gd-yellow: #e3b341;\n --gd-row-hovered: #1f2a3a;\n --gd-row-selected: #1f3b5c;\n --gd-budget: rgba(255, 255, 255, 0.35);\n`;\n\nconst LIGHT_TOKENS = `\n --gd-bg: #ffffff;\n --gd-bg-raised: #f1f3f4;\n --gd-bg-hover: #e8eaed;\n --gd-border: #dadce0;\n --gd-text: #202124;\n --gd-text-strong: #3c4043;\n --gd-muted: #5f6368;\n --gd-faint: #80868b;\n --gd-fainter: #9aa0a6;\n --gd-accent: #1a73e8;\n --gd-info: #185abc;\n --gd-purple: #8430ce;\n --gd-green: #188038;\n --gd-orange: #b06000;\n --gd-red: #c5221f;\n --gd-yellow: #a05f00;\n --gd-row-hovered: #e8f0fe;\n --gd-row-selected: #d2e3fc;\n --gd-budget: rgba(0, 0, 0, 0.35);\n`;\n\nconst STYLES = `\n:host { all: initial; display: block; height: 100%; ${LIGHT_TOKENS} }\n:host([data-theme=\"dark\"]) { ${DARK_TOKENS} }\n@media (prefers-color-scheme: dark) {\n :host(:not([data-theme=\"light\"])) { ${DARK_TOKENS} }\n}\n.toolbar {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 4px 8px;\n border-bottom: 1px solid var(--gd-border);\n background: var(--gd-bg-raised);\n color: var(--gd-text);\n font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n.tabs { display: flex; gap: 2px; }\n.tabs button, .bar button {\n border: 1px solid transparent;\n border-radius: 4px;\n padding: 2px 8px;\n background: transparent;\n color: var(--gd-muted);\n font: inherit;\n cursor: pointer;\n}\n.tabs button:hover, .bar button:hover { color: var(--gd-text); background: var(--gd-bg-hover); }\n.tabs button.active { color: var(--gd-text); border-color: var(--gd-border); background: var(--gd-bg); }\n.picker { font: inherit; background: var(--gd-bg); color: var(--gd-text); border: 1px solid var(--gd-border); border-radius: 4px; }\n.toggle { display: inline-flex; align-items: center; gap: 2px; color: var(--gd-muted); cursor: pointer; }\n.status { margin-left: auto; color: var(--gd-muted); }\n.views { height: calc(100% - 30px); background: var(--gd-bg); color: var(--gd-text); font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }\n.view { height: 100%; }\n.view[hidden] { display: none; }\n.tree-view { display: grid; grid-template-columns: minmax(200px, 1fr) minmax(240px, 1fr); }\n.tree { overflow: auto; border-right: 1px solid var(--gd-border); padding: 4px 0; }\n.row { display: flex; gap: 6px; white-space: nowrap; padding: 1px 8px; cursor: pointer; }\n.row:hover { background: var(--gd-bg-raised); }\n.row.hovered { background: var(--gd-row-hovered); }\n.row.selected { background: var(--gd-row-selected); }\n.disclosure { width: 10px; color: var(--gd-muted); }\n.type { color: var(--gd-accent); }\n.component { color: var(--gd-purple); }\n.text { color: var(--gd-info); overflow: hidden; text-overflow: ellipsis; max-width: 200px; }\n.id { color: var(--gd-fainter); }\n.count { padding: 4px 8px; color: var(--gd-faint); }\n.report { overflow: auto; padding: 8px 12px; }\n.empty { color: var(--gd-muted); margin: 0; padding: 4px 8px; }\n.console-view { display: flex; flex-direction: column; }\n.bar { padding: 4px 8px; border-bottom: 1px solid var(--gd-border); }\n.log { flex: 1; margin: 0; padding: 4px 0; list-style: none; overflow: auto; }\n.entry { padding: 1px 8px; border-bottom: 1px solid var(--gd-bg-raised); white-space: pre-wrap; overflow-wrap: anywhere; }\n.entry.warn { color: var(--gd-yellow); }\n.entry.error { color: var(--gd-red); }\n.entry.debug, .entry.info { color: var(--gd-muted); }\n.entry pre { margin: 2px 0 0 0; color: var(--gd-muted); white-space: pre-wrap; }\n.badge {\n display: inline-block;\n min-width: 36px;\n margin-right: 6px;\n padding: 0 4px;\n border-radius: 3px;\n background: var(--gd-bg-hover);\n color: var(--gd-muted);\n font-size: 10px;\n text-align: center;\n}\n.badge.app { color: var(--gd-green); }\n.badge.render { color: var(--gd-accent); }\n.badge.command { color: var(--gd-purple); }\n.badge.patch { color: var(--gd-accent); }\n.badge.frame { color: var(--gd-green); }\n.badge.source { color: var(--gd-orange); }\n.time { color: var(--gd-faint); margin-right: 6px; }\n.cause { color: var(--gd-orange); }\n.entry.frame { color: var(--gd-muted); }\n.frames-view { padding: 8px; }\n/* Column, so the list inside scrolls itself rather than overflowing\n the pane: without it the log grew past the bottom of the panel and\n the newest entries could not be reached. */\n.actions-view { display: flex; flex-direction: column; }\n.streams-view { display: flex; flex-direction: column; }\n.streams-view .bar { display: flex; align-items: center; gap: 8px; }\n.streams-view .total { flex: 1; color: var(--gd-muted); }\n.streams { flex: 1; overflow: auto; padding: 4px 0; }\n.stream-row { display: flex; align-items: center; gap: 8px; padding: 1px 8px; cursor: pointer; }\n.stream-row:hover { background: var(--gd-bg-raised); }\n.stream-row .component { color: var(--gd-purple); white-space: nowrap; }\n.bar-track { flex: 1; height: 6px; border-radius: 3px; background: var(--gd-bg-raised); overflow: hidden; }\n.bar-fill { display: block; height: 100%; background: var(--gd-accent); }\n.stream-row .count { width: 76px; text-align: right; color: var(--gd-muted); white-space: nowrap; }\n.stream-row .count.changed { color: var(--gd-orange); }\n${NODE_REPORT_STYLES}\n`;\n","import type { UiNodeReport } from 'gesso-framework';\nimport { NODE_REPORT_STYLES, renderNodeReport } from './NodeReportView';\n\n/**\n * The node inspector: what the thing under the\n * pointer is, and where every part of it came from.\n *\n * L8 already answered \"why is this box that size\" and printed it as\n * text. This is the rest of the question a person actually has, which\n * turned out to be four more: what did the element declare, what is a\n * modifier writing over it, what is it inheriting, and which component\n * rendered it. The layout explanation is still here, at the bottom,\n * because it is still the answer to the first one.\n *\n * It is DOM, in a shadow root, over the canvas, for the same reasons\n * the error overlay is: the application it is inspecting owns the\n * canvas, and a panel drawn inside the scene would be part of the\n * scene it is describing. It takes no pointer events at all, because\n * the person is hovering the canvas underneath it and a panel that\n * swallowed the pointer would erase the very thing it is showing.\n */\nexport interface NodeInspector {\n /** Shows the report, or hides the panel when null. */\n set(report: UiNodeReport | null): void;\n dispose(): void;\n}\n\nexport interface NodeInspectorOptions {\n /**\n * Which corner it floats in. Default `'bottom-left'`.\n *\n * A corner rather than a docked side, because the panel must not\n * change the size of the element the application is mounted in: a\n * readout that resized the canvas would relayout the scene it is\n * describing, and the boxes it is pointing at would move.\n */\n readonly corner?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';\n}\n\nexport function mountNodeInspector(host: HTMLElement, options: NodeInspectorOptions = {}): NodeInspector {\n const doc = host.ownerDocument;\n const view = doc.defaultView;\n const position = view?.getComputedStyle(host).position ?? 'static';\n const restore = position === 'static' ? host.style.position : null;\n if (position === 'static') {\n host.style.position = 'relative';\n }\n\n const container = doc.createElement('div');\n const root = container.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = STYLES;\n const panel = doc.createElement('div');\n panel.className = `panel ${options.corner ?? 'bottom-left'}`;\n panel.hidden = true;\n root.append(style, panel);\n host.appendChild(container);\n\n const render = (report: UiNodeReport): void => {\n panel.textContent = '';\n panel.append(...renderNodeReport(doc, report));\n };\n\n return {\n set(report) {\n if (report === null) {\n panel.hidden = true;\n panel.textContent = '';\n return;\n }\n render(report);\n panel.hidden = false;\n },\n dispose() {\n container.remove();\n if (restore !== null) {\n host.style.position = restore;\n }\n }\n };\n}\n\nconst STYLES = `\n:host { all: initial; }\n.panel {\n position: absolute;\n z-index: 2147482000;\n max-width: 380px;\n max-height: 70%;\n overflow: auto;\n /* The person is hovering the canvas underneath: the panel showing\n what is under the pointer must never be what is under the pointer. */\n pointer-events: none;\n margin: 12px;\n padding: 10px 12px;\n border-radius: 8px;\n border: 1px solid rgba(255, 255, 255, 0.12);\n background: rgba(13, 17, 23, 0.94);\n color: #e6edf3;\n font: 11px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;\n}\n.panel[hidden] { display: none; }\n.top-left { top: 0; left: 0; }\n.top-right { top: 0; right: 0; }\n.bottom-left { bottom: 0; left: 0; }\n.bottom-right { bottom: 0; right: 0; }\n${NODE_REPORT_STYLES}\n`;\n","import type { FrameMetrics, UiNodeReport, WorkerApp } from 'gesso-framework';\n\n/**\n * The proof strip: a bar of instruments above a worker application,\n * and the only part of it that is DOM on the main thread.\n *\n * On purpose. The claim a worker application makes is that the main\n * thread is not doing the work, and numbers the render worker prints on\n * its own canvas are numbers a stranger has no reason to believe. So the\n * readout lives where the doubt lives. Block this thread and the strip\n * stops dead — the pulse freezes, the button stays pressed — while the\n * two threads behind it carry on, because the application is not here.\n *\n * What it has:\n *\n * - **a pulse**, driven by this thread's own `requestAnimationFrame`,\n * which stops the moment the thread is busy;\n * - **a block button**, which holds this thread in a busy loop for a\n * few seconds and records when, on the render worker's clock, so\n * frames drawn *inside* the block can be told from those either side;\n * - **the render worker's readout**: its frame rate and worst gap over\n * the last second, and the median work a frame cost;\n * - **this thread's frame rate**, which drops to nothing in a block;\n * - with `layout`, **the engine's layout heatmap** and a count of the\n * nodes each frame re-measured, for an application whose claim is\n * about measuring less.\n *\n * And a recording for a script: `globalThis[options.global]` has the\n * last two thousand frames, a reset, and the last block's window. A\n * budget check reads the same numbers the strip is showing, rather than\n * a measurement it installed for itself.\n *\n * Made out of gessosheet's and gessologic's strips, which had been\n * copied from one to the other and changed a little. The one thing\n * gessologic's copy found that is worth knowing before writing a check\n * against this: count frames inside `lastBlock()`, not the whole\n * recording. A count of the whole recording takes in the frames drawn\n * just before and after a block, and passes a render worker that drew\n * nothing at all through it.\n *\n * const panel = proofPanel(host, { global: 'myAppProof' });\n * const app = createApp({ ...panel.options });\n * panel.attach(app);\n * app.mount(host);\n *\n * Built before `createApp`, because `onFrame` is a constructor option,\n * and inserted before the host so the canvas measures its final size.\n */\n\nexport interface ProofPanelOptions {\n /** The global the recording is published on. Default `'gessoProof'`. */\n readonly global?: string;\n /** How long the block button holds the thread, in milliseconds. Default 5,000. */\n readonly blockMs?: number;\n /** Add the layout heatmap toggle and the re-measured count. Default false. */\n readonly layout?: boolean;\n}\n\n/** One frame, as a budget check reads it. */\nexport interface ProofFrame {\n /** When the render worker finished it, on the render worker's clock. */\n readonly at: number;\n readonly durationMs: number;\n readonly measured: number;\n readonly nodes: number;\n readonly inputLatencyMs: number | null;\n /** Which backend drew, and where the frame's time went. */\n readonly renderer: string;\n readonly phases: Record<string, number>;\n readonly gpu: Record<string, number> | null;\n}\n\n/** What a script reads off the global. */\nexport interface ProofHandle {\n frames(): readonly ProofFrame[];\n reset(): void;\n /** The last block, on the render worker's clock; null before one. */\n lastBlock(): ProofBlock | null;\n}\n\nexport interface ProofBlock {\n readonly start: number;\n readonly end: number;\n}\n\n/** The rolling readout: over the last second of frames, or `idle` when there were none lately. */\nexport type ProofReadout =\n | { readonly idle: true }\n | { readonly idle: false; readonly fps: number; readonly worstGapMs: number; readonly medianWorkMs: number };\n\n/** How many frames the recording keeps for a script to read back. */\nexport const PROOF_RECORDING = 2_000;\n\n/**\n * How far back the rolling readout looks, in milliseconds.\n *\n * A time window and not a count of frames: an idle Gesso application\n * draws nothing at all, so a window of the last ninety frames held\n * frames from however long ago the last interaction was, and reported a\n * scroll running at sixty as three.\n */\nconst RECENT_MS = 1_000;\n\n/** Past this since the last frame, the readout says idle rather than a rate that reads as a stall. */\nconst IDLE_AFTER_MS = 400;\n\n/**\n * The strip's arithmetic, without the DOM: frames in, readout and\n * recording out.\n *\n * Every time is the render worker's. A frame is stamped when the worker\n * finished it, and those stamps survive a blocked main thread, which\n * receives the whole block's frames in one burst when it comes back; a\n * readout that timed its own arrivals would report a stall that never\n * happened, about the wrong thread. This thread's clock is translated\n * onto the worker's with an offset learned from every frame, because a\n * worker's `performance.now()` counts from the worker's creation.\n */\nexport class ProofRecording implements ProofHandle {\n private readonly recorded: ProofFrame[] = [];\n private readonly finishes: number[] = [];\n private readonly costs: number[] = [];\n private offset: number | null = null;\n private block: ProofBlock | null = null;\n private peak = 0;\n\n constructor(private readonly hostNow: () => number = () => performance.now()) {}\n\n /** Takes a frame from the runtime's `onFrame`. */\n frame(metrics: FrameMetrics): void {\n this.offset = this.hostNow() - metrics.at;\n this.recorded.push({\n at: metrics.at,\n durationMs: metrics.durationMs,\n measured: metrics.measured,\n nodes: metrics.nodes,\n inputLatencyMs: metrics.inputLatencyMs,\n renderer: metrics.renderer,\n phases: { ...metrics.phases },\n gpu: metrics.gpu === null ? null : { ...metrics.gpu }\n });\n if (this.recorded.length > PROOF_RECORDING) {\n this.recorded.shift();\n }\n this.finishes.push(metrics.at);\n this.costs.push(metrics.durationMs);\n while (this.finishes.length > 1 && metrics.at - this.finishes[0]! > RECENT_MS) {\n this.finishes.shift();\n this.costs.shift();\n }\n this.peak = Math.max(this.peak, metrics.measured);\n }\n\n /** Now, on the render worker's clock. */\n workerNow(): number {\n return this.hostNow() - (this.offset ?? 0);\n }\n\n /** Records a block this thread held from `start` to `end`, on its own clock. */\n blocked(start: number, end: number): void {\n const offset = this.offset ?? 0;\n this.block = { start: start - offset, end: end - offset };\n }\n\n readout(): ProofReadout {\n const last = this.finishes.at(-1);\n const first = this.finishes[0];\n if (last === undefined || first === undefined || last <= first || this.workerNow() - last > IDLE_AFTER_MS) {\n return { idle: true };\n }\n let worst = 0;\n for (let i = 1; i < this.finishes.length; i++) {\n worst = Math.max(worst, this.finishes[i]! - this.finishes[i - 1]!);\n }\n const sorted = [...this.costs].sort((a, b) => a - b);\n return {\n idle: false,\n fps: Math.round(((this.finishes.length - 1) / (last - first)) * 1000),\n worstGapMs: worst,\n medianWorkMs: sorted[Math.floor(sorted.length / 2)]!\n };\n }\n\n /** The most nodes any frame since the last reset re-measured. */\n peakMeasured(): number {\n return this.peak;\n }\n\n frames(): readonly ProofFrame[] {\n return this.recorded;\n }\n\n lastBlock(): ProofBlock | null {\n return this.block;\n }\n\n reset(): void {\n this.recorded.length = 0;\n this.finishes.length = 0;\n this.costs.length = 0;\n this.peak = 0;\n }\n}\n\n/**\n * Puts the strip before `host` and wires it; returns the options the\n * application has to be created with, and the function that attaches the\n * controls to it once it exists.\n */\nexport function proofPanel(\n host: HTMLElement,\n options: ProofPanelOptions = {}\n): {\n readonly recording: ProofRecording;\n readonly options: { onFrame: (metrics: FrameMetrics) => void; onInspect?: (report: UiNodeReport | null) => void };\n readonly attach: (app: WorkerApp) => void;\n} {\n const blockMs = options.blockMs ?? 5_000;\n const layout = options.layout === true;\n const doc = host.ownerDocument;\n const view = doc.defaultView ?? globalThis;\n build(host, blockMs, layout);\n const find = <T extends HTMLElement = HTMLElement>(id: string): T => {\n const found = doc.getElementById(`gesso-proof-${id}`);\n if (found === null) {\n throw new Error(`The proof strip has no #gesso-proof-${id} element.`);\n }\n return found as T;\n };\n const pulse = find('pulse');\n const block = find<HTMLButtonElement>('block');\n const fpsOut = find('fps');\n const gapOut = find('gap');\n const workOut = find('work');\n const mainOut = find('mainfps');\n\n const recording = new ProofRecording();\n (globalThis as Record<string, unknown>)[options.global ?? 'gessoProof'] = recording;\n\n const onFrame = (metrics: FrameMetrics): void => {\n recording.frame(metrics);\n if (layout) {\n find('measured').textContent = String(metrics.measured);\n find('peak').textContent = String(recording.peakMeasured());\n }\n };\n\n // The pulse, this thread's frame rate and the readout, all on this\n // thread's animation frame, so all three stop together when it is\n // blocked — in front of an application that has not.\n let mainFrames = 0;\n let sampledAt = view.performance.now();\n const tick = (now: number): void => {\n mainFrames++;\n pulse.style.opacity = String(0.35 + 0.65 * Math.abs(Math.sin(now / 350)));\n if (now - sampledAt >= 500) {\n mainOut.textContent = String(Math.round((mainFrames / (now - sampledAt)) * 1000));\n mainFrames = 0;\n sampledAt = now;\n const readout = recording.readout();\n fpsOut.textContent = readout.idle ? 'idle' : String(readout.fps);\n gapOut.textContent = readout.idle ? '—' : `${readout.worstGapMs.toFixed(1)}ms`;\n workOut.textContent = readout.idle ? '—' : `${readout.medianWorkMs.toFixed(1)}ms`;\n }\n view.requestAnimationFrame(tick);\n };\n view.requestAnimationFrame(tick);\n\n const attach = (app: WorkerApp): void => {\n // A busy loop and not a sleep: the point is that this thread has no\n // turn to give anybody. The label is repainted and a frame yielded\n // before the loop starts, or the only evidence would be a button\n // that never looked pressed.\n block.addEventListener('click', () => {\n block.disabled = true;\n block.textContent = `Blocking for ${blockMs / 1000}s…`;\n pulse.classList.add('blocked');\n view.requestAnimationFrame(() =>\n view.requestAnimationFrame(() => {\n const start = view.performance.now();\n const until = start + blockMs;\n while (view.performance.now() < until) {\n /* Holding the thread. That is the whole experiment. */\n }\n recording.blocked(start, view.performance.now());\n block.disabled = false;\n block.textContent = `Block the main thread for ${blockMs / 1000}s`;\n pulse.classList.remove('blocked');\n })\n );\n });\n if (layout) {\n const heatmap = find<HTMLInputElement>('heatmap');\n const explain = find('explain');\n heatmap.addEventListener('change', () => {\n app.setInspector(heatmap.checked);\n explain.hidden = !heatmap.checked;\n if (!heatmap.checked) {\n explain.textContent = '';\n }\n });\n }\n };\n\n const onInspect = (report: UiNodeReport | null): void => {\n const heatmap = find<HTMLInputElement>('heatmap');\n const explain = find('explain');\n if (!heatmap.checked) {\n return;\n }\n if (report === null) {\n explain.textContent = 'Point at something.';\n return;\n }\n const { x, y, width, height } = report.box;\n const label = report.semantics?.label;\n explain.textContent =\n `${report.type}${label === undefined ? '' : ` \"${label}\"`} ` +\n `— ${width.toFixed(1)}x${height.toFixed(1)} at ${x.toFixed(1)},${y.toFixed(1)}\\n\\n` +\n report.explanation;\n };\n\n return { recording, options: layout ? { onFrame, onInspect } : { onFrame }, attach };\n}\n\nconst STYLES = `\n body { display: flex; flex-direction: column; }\n body > .gesso-proof-host { flex: 1; min-height: 0; height: auto !important; }\n #gesso-proof {\n display: flex; align-items: center; gap: 12px; flex-wrap: wrap;\n padding: 6px 10px; background: #22242a; color: #d7d9e0;\n border-bottom: 1px solid #000; font: 13px system-ui, sans-serif;\n }\n #gesso-proof button {\n font: inherit; padding: 4px 9px; border-radius: 5px;\n border: 1px solid #4a4d57; background: #32353e; color: #e8eaf0; cursor: pointer;\n }\n #gesso-proof button:hover { background: #3d414c; }\n #gesso-proof label { display: flex; align-items: center; gap: 5px; cursor: pointer; }\n #gesso-proof .stat { font-variant-numeric: tabular-nums; white-space: nowrap; }\n #gesso-proof .stat b { color: #fff; font-weight: 600; }\n #gesso-proof .sep { width: 1px; align-self: stretch; background: #4a4d57; }\n #gesso-proof-pulse { width: 12px; height: 12px; border-radius: 50%; background: #5ec26a; }\n #gesso-proof-pulse.blocked { background: #d4564f; }\n #gesso-proof-explain {\n position: fixed; right: 12px; bottom: 12px; z-index: 1; margin: 0; padding: 9px 11px;\n max-width: min(680px, calc(100vw - 24px)); max-height: 42vh; overflow: auto;\n pointer-events: none; font: 11px/1.45 ui-monospace, SFMono-Regular, Menlo, monospace;\n white-space: pre-wrap; background: #16171bf2; color: #b9bdc7;\n border: 1px solid #3a3d46; border-radius: 6px; box-shadow: 0 6px 24px #0006;\n }\n #gesso-proof-explain[hidden] { display: none; }\n`;\n\n/** The strip, in the order a person reads it, before the host so the canvas measures its final size. */\nfunction build(host: HTMLElement, blockMs: number, layout: boolean): void {\n const doc = host.ownerDocument;\n const styles = doc.createElement('style');\n styles.textContent = STYLES;\n doc.head.append(styles);\n host.classList.add('gesso-proof-host');\n\n const strip = doc.createElement('div');\n strip.id = 'gesso-proof';\n strip.innerHTML = `\n <span id=\"gesso-proof-pulse\" title=\"The main thread’s own animation frame\"></span>\n <button id=\"gesso-proof-block\" type=\"button\">Block the main thread for ${blockMs / 1000}s</button>\n ${layout ? '<label><input id=\"gesso-proof-heatmap\" type=\"checkbox\" />Layout heatmap</label>' : ''}\n <span class=\"sep\"></span>\n <span class=\"stat\">render worker <b id=\"gesso-proof-fps\">—</b> fps</span>\n <span class=\"stat\">worst frame gap <b id=\"gesso-proof-gap\">—</b></span>\n <span class=\"stat\">frame work <b id=\"gesso-proof-work\">—</b> median</span>\n ${layout ? '<span class=\"stat\">re-measured <b id=\"gesso-proof-measured\">—</b> last frame, at most <b id=\"gesso-proof-peak\">—</b></span>' : ''}\n <span class=\"stat\">main thread <b id=\"gesso-proof-mainfps\">—</b> fps</span>\n `;\n host.before(strip);\n\n if (layout) {\n const explain = doc.createElement('pre');\n explain.id = 'gesso-proof-explain';\n explain.hidden = true;\n host.before(explain);\n }\n}\n"],"mappings":"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"}
|