@wildwinter/app-shell 0.57.0 → 0.58.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/README.md +195 -0
- package/dist/chunk-4JQWNC6Z.js +319 -0
- package/dist/chunk-4JQWNC6Z.js.map +1 -0
- package/dist/chunk-ICYPXUNA.js +8 -0
- package/dist/chunk-ICYPXUNA.js.map +1 -0
- package/dist/{chunk-XJM733E6.js → chunk-OO6O4LEL.js} +6 -2
- package/dist/chunk-OO6O4LEL.js.map +1 -0
- package/dist/{chunk-CPNTROWD.js → chunk-OZQ2BYXH.js} +13 -4
- package/dist/chunk-OZQ2BYXH.js.map +1 -0
- package/dist/{chunk-5BKGDTYD.js → chunk-RMQNBWLZ.js} +3 -3
- package/dist/{chunk-5BKGDTYD.js.map → chunk-RMQNBWLZ.js.map} +1 -1
- package/dist/index.cjs +334 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/property-list.cjs.map +1 -1
- package/dist/property-list.js +2 -2
- package/dist/tool-window.cjs +11 -2
- package/dist/tool-window.cjs.map +1 -1
- package/dist/tool-window.d.cts +10 -4
- package/dist/tool-window.d.ts +10 -4
- package/dist/tool-window.js +1 -1
- package/dist/updater.cjs.map +1 -1
- package/dist/updater.js +1 -1
- package/dist/value-control.cjs +4 -0
- package/dist/value-control.cjs.map +1 -1
- package/dist/value-control.d.cts +4 -4
- package/dist/value-control.d.ts +4 -4
- package/dist/value-control.js +1 -1
- package/dist/value-rows.cjs +8 -1
- package/dist/value-rows.cjs.map +1 -1
- package/dist/value-rows.js +5 -2
- package/dist/value-rows.js.map +1 -1
- package/dist/view-client.cjs +172 -0
- package/dist/view-client.cjs.map +1 -0
- package/dist/view-client.d.cts +46 -0
- package/dist/view-client.d.ts +46 -0
- package/dist/view-client.js +165 -0
- package/dist/view-client.js.map +1 -0
- package/dist/view-host-web.cjs +327 -0
- package/dist/view-host-web.cjs.map +1 -0
- package/dist/view-host-web.d.cts +61 -0
- package/dist/view-host-web.d.ts +61 -0
- package/dist/view-host-web.js +4 -0
- package/dist/view-host-web.js.map +1 -0
- package/dist/view-host.cjs +532 -0
- package/dist/view-host.cjs.map +1 -0
- package/dist/view-host.d.cts +178 -0
- package/dist/view-host.d.ts +178 -0
- package/dist/view-host.js +426 -0
- package/dist/view-host.js.map +1 -0
- package/dist/view-preload.cjs +16 -0
- package/dist/view-preload.cjs.map +1 -0
- package/dist/view-preload.js +8 -0
- package/dist/view-preload.js.map +1 -0
- package/dist/view-protocol.cjs +12 -0
- package/dist/view-protocol.cjs.map +1 -0
- package/dist/view-protocol.d.cts +257 -0
- package/dist/view-protocol.d.ts +257 -0
- package/dist/view-protocol.js +3 -0
- package/dist/view-protocol.js.map +1 -0
- package/package.json +25 -4
- package/dist/chunk-CPNTROWD.js.map +0 -1
- package/dist/chunk-XJM733E6.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/colour.ts","../src/pane-shell.ts","../src/tool-window-web.ts","../src/confirm.ts","../src/about.ts","../src/anchored.ts","../src/ids.ts","../src/id-editor.ts","../src/vc-view.ts","../src/identity.ts","../src/notes-editor.ts","../src/comments.ts","../src/job-view.ts","../src/save.ts","../src/save-view.ts","../src/stale.ts","../src/toast.ts","../src/stepper.ts","../src/nav-history.ts"],"names":["el","label","thread","timer","node","current"],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The per-entity colour model (design-language.md section 4): a stable hash of\n// a name selects an INDEX into the curated palette - repeatable, storage-free.\n// The hues live in the token layer as `--char-0 .. --char-(N-1)` per theme, so\n// a colour reference adapts to light/dark with zero JS. Each app defines the\n// `--char-*` values in its own palette; this module only picks the slot.\n//\n// Storylet Studio uses it for dimension-value / zone chips; Patterpad for\n// character cues.\n// ---------------------------------------------------------------------------\n\n/** Curated colour slots; must match the `--char-*` set defined per theme. */\nexport const PALETTE_SIZE = 12;\n\nexport const PALETTE = Array.from({ length: PALETTE_SIZE }, (_, i) => `var(--char-${i})`) as readonly string[];\n\n/** Hash a name to a palette slot: FNV-1a + an fmix32 avalanche, well\n * distributed even for short, similar names. */\nexport function colourIndex(name: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < name.length; i++) { h ^= name.charCodeAt(i); h = Math.imul(h, 0x01000193); }\n h ^= h >>> 16; h = Math.imul(h, 0x85ebca6b); h ^= h >>> 13; h = Math.imul(h, 0xc2b2ae35); h ^= h >>> 16;\n return (h >>> 0) % PALETTE_SIZE;\n}\n\n/** The theme-aware CSS variable reference for a name's slot. */\nexport function colourFor(name: string): string {\n return PALETTE[colourIndex(name)]!;\n}\n","// ---------------------------------------------------------------------------\n// mountPaneShell: the app-frame both editors share - a top bar with compact\n// pane toggles, a nav | centre | inspector grid whose side panes collapse and\n// resize, and width/open-state persistence via a host callback. The host fills\n// the pane bodies + top-bar slots and owns its own keyboard accelerators\n// (call togglePane); the frame owns layout, collapse, drag-resize, and state.\n//\n// The frame CSS ships alongside as pane-shell.css (import it once at the app\n// root). It reads the shared token grammar (tokens.css).\n// ---------------------------------------------------------------------------\n\nimport { iconNode } from \"./icons.js\";\nimport { isKeyCombo, tipWithKey } from \"./keys.js\";\nimport { ensureTooltipHost } from \"./tooltip.js\";\n\nexport type PaneSide = \"nav\" | \"inspector\";\n\nexport interface PaneSideConfig {\n /** The open width as a CSS length, e.g. \"14rem\" or \"224px\". */\n defaultWidth: string;\n /** Drag floor in px (default 160). */\n minWidth?: number;\n /** Drag ceiling in px from the window width (default w => min(640, w - 420)). */\n maxWidth?: (innerWidth: number) => number;\n /** Whether a drag seam is offered (default true). */\n resizable?: boolean;\n /** Noun for the toggle's tooltip/aria, e.g. \"navigator\" / \"inspector\". */\n label?: string;\n /** The key that toggles the pane, bracketed after the tooltip. A combo in\n * the portable spelling (\"Mod+1\") is drawn for the platform through\n * `tipWithKey`: \"(⌘1)\" on a Mac, \"(Ctrl+1)\" elsewhere. Anything else is a\n * literal and goes in verbatim, as it always has (\"Cmd+1\" stays \"Cmd+1\"). */\n shortcutHint?: string;\n /** false = the pane is not offered at all: no toggle, togglePane ignored,\n * forced closed whatever the remembered state says. For a slot a host has\n * retired but keeps mounted (Storyletter's inspector) - the shell used to\n * offer a chevron that opened a paneful of nothing. Default true. */\n offered?: boolean;\n}\n\nexport interface PaneShellState {\n open: Record<PaneSide, boolean>;\n /** Dragged widths in px; absent = the config default. */\n width: Partial<Record<PaneSide, number>>;\n}\n\nexport interface PaneShellOptions {\n nav: PaneSideConfig;\n inspector: PaneSideConfig;\n /** Initial open + width state (from the host's store). */\n initial?: Partial<PaneShellState>;\n /** Persist state after any toggle or resize (host wires it to its store). */\n onChange?: (state: PaneShellState) => void;\n /** The toggle's rollover for a side in a given state. Default: \"Show\n * navigator\" / \"Hide navigator\" from the side's `label`, with the\n * `shortcutHint` in brackets (platform-true when it is a combo). Written as\n * `data-tip` (the themed tooltip) and as the accessible name; never as\n * `title`. */\n tipFor?: (side: PaneSide, open: boolean) => string;\n /** An extra class per side, toggled on the `.panes` grid whenever that side\n * is collapsed, beside the shell's own `no-nav` / `no-inspector`. For an\n * app whose stylesheet already keys on its own name for the state\n * (Patterpad's `props-doc-open`), so its rules ride the shell's collapse\n * rather than a second copy of it. */\n collapseAlso?: Partial<Record<PaneSide, string>>;\n}\n\nexport interface PaneShell {\n /** The frame element (top bar + panes); mount it into the app container. */\n readonly root: HTMLElement;\n readonly topbar: HTMLElement;\n /** Top-bar host slots: leading (title) and trailing (status), the host fills. */\n readonly topbarLead: HTMLElement;\n readonly topbarTrail: HTMLElement;\n /** Pane bodies the host fills (the fixed-width inner that clips on collapse). */\n readonly nav: HTMLElement;\n readonly centre: HTMLElement;\n readonly inspector: HTMLElement;\n togglePane(side: PaneSide): void;\n setPaneOpen(side: PaneSide, open: boolean): void;\n /** Whether the pane is showing: open, and not held closed. */\n isOpen(side: PaneSide): boolean;\n /** Fold a side away WITHOUT changing what the person chose: a mode that has\n * no use for the inspector (Patterpad's properties page) holds it closed,\n * and releasing the hold restores whatever was remembered. Not persisted;\n * the toggle keeps reporting the remembered state. */\n holdClosed(side: PaneSide, held: boolean): void;\n /** Clear dragged widths back to the config defaults. */\n resetWidths(): void;\n /** A snapshot of the current state (open + widths). */\n state(): PaneShellState;\n destroy(): void;\n}\n\nconst DEFAULT_MAX = (w: number): number => Math.min(640, w - 420);\nconst openVar: Record<PaneSide, string> = { nav: \"--nav-open-w\", inspector: \"--insp-open-w\" };\nconst collapseClass: Record<PaneSide, string> = { nav: \"no-nav\", inspector: \"no-inspector\" };\n\nexport function mountPaneShell(host: HTMLElement, opts: PaneShellOptions): PaneShell {\n const cfg: Record<PaneSide, PaneSideConfig> = { nav: opts.nav, inspector: opts.inspector };\n const offered = (side: PaneSide): boolean => cfg[side].offered !== false;\n const open: Record<PaneSide, boolean> = {\n nav: offered(\"nav\") && (opts.initial?.open?.nav ?? true),\n inspector: offered(\"inspector\") && (opts.initial?.open?.inspector ?? true),\n };\n const width: Partial<Record<PaneSide, number>> = { ...(opts.initial?.width ?? {}) };\n const held: Record<PaneSide, boolean> = { nav: false, inspector: false };\n const showing = (side: PaneSide): boolean => open[side] && !held[side];\n\n const el = <K extends keyof HTMLElementTagNameMap>(tag: K, cls?: string): HTMLElementTagNameMap[K] => {\n const n = document.createElement(tag);\n if (cls) n.className = cls;\n return n;\n };\n\n // --- top bar ---------------------------------------------------------------\n const navToggle = el(\"button\", \"pane-toggle\");\n navToggle.type = \"button\";\n const inspToggle = el(\"button\", \"pane-toggle\");\n inspToggle.type = \"button\";\n const topbarLead = el(\"div\", \"topbar-lead\");\n const topbarTrail = el(\"div\", \"topbar-trail\");\n const spacer = el(\"span\", \"topbar-spacer\");\n const topbar = el(\"header\", \"topbar\");\n topbar.append(navToggle, topbarLead, spacer, topbarTrail, inspToggle);\n\n // --- panes -----------------------------------------------------------------\n const panes = el(\"main\", \"panes\");\n const navPane = el(\"nav\", \"pane pane-nav\");\n const navInner = el(\"div\", \"pane-inner\");\n navPane.append(navInner);\n const centre = el(\"section\", \"pane-centre\");\n const inspPane = el(\"aside\", \"pane pane-inspector\");\n const inspInner = el(\"div\", \"pane-inner\");\n inspPane.append(inspInner);\n const navResizer = el(\"div\", \"pane-resizer pane-resizer-nav\");\n const inspResizer = el(\"div\", \"pane-resizer pane-resizer-insp\");\n panes.append(navPane, centre, inspPane, navResizer, inspResizer);\n\n const root = el(\"div\", \"pane-shell\");\n root.append(topbar, panes);\n host.append(root);\n // An unoffered pane's toggle is not hidden but ABSENT: nothing to\n // discover, nothing to tab to.\n if (!offered(\"nav\")) navToggle.remove();\n if (!offered(\"inspector\")) inspToggle.remove();\n\n // --- state application -----------------------------------------------------\n const defaultTip = (side: PaneSide, isOpen: boolean): string => {\n const text = `${isOpen ? \"Hide\" : \"Show\"} ${cfg[side].label ?? side}`;\n const hint = cfg[side].shortcutHint;\n if (!hint) return text;\n // A portable combo is spelled for the platform at draw time; a literal\n // is the caller's own words and goes in as written.\n return isKeyCombo(hint) ? tipWithKey(text, hint) : `${text} (${hint})`;\n };\n const tipFor = opts.tipFor ?? defaultTip;\n\n function applyToggleGlyph(side: PaneSide, button: HTMLButtonElement): void {\n const isOpen = open[side];\n // Chevron points toward where the pane collapses to.\n button.replaceChildren(iconNode(side === \"nav\" ? (isOpen ? \"back\" : \"forward\") : (isOpen ? \"forward\" : \"back\")));\n // `data-tip`, not `title`: the themed tooltip, never the OS bubble on the\n // platform's own delay. The tip is also the accessible name, since the\n // button has no text of its own.\n const tip = tipFor(side, isOpen);\n button.dataset[\"tip\"] = tip;\n button.setAttribute(\"aria-label\", tip);\n button.setAttribute(\"aria-pressed\", String(isOpen));\n }\n // Writing data-tip mounts the renderer, as `el` does, so a window whose host\n // never called initTooltips() still draws the rollover.\n ensureTooltipHost();\n\n function apply(): void {\n for (const side of [\"nav\", \"inspector\"] as const) {\n const collapsed = !showing(side);\n panes.classList.toggle(collapseClass[side], collapsed);\n const also = opts.collapseAlso?.[side];\n if (also) panes.classList.toggle(also, collapsed);\n }\n // The open widths live on the grid: the track and each .pane-inner read them.\n panes.style.setProperty(openVar.nav, width.nav !== undefined ? `${width.nav}px` : cfg.nav.defaultWidth);\n panes.style.setProperty(openVar.inspector, width.inspector !== undefined ? `${width.inspector}px` : cfg.inspector.defaultWidth);\n applyToggleGlyph(\"nav\", navToggle);\n applyToggleGlyph(\"inspector\", inspToggle);\n navResizer.style.display = cfg.nav.resizable === false || !offered(\"nav\") ? \"none\" : \"\";\n inspResizer.style.display = cfg.inspector.resizable === false || !offered(\"inspector\") ? \"none\" : \"\";\n }\n\n const snapshot = (): PaneShellState => ({ open: { ...open }, width: { ...width } });\n const persist = (): void => opts.onChange?.(snapshot());\n\n function setPaneOpen(side: PaneSide, next: boolean): void {\n if (open[side] === next) return;\n open[side] = next;\n apply();\n persist();\n }\n function togglePane(side: PaneSide): void { if (offered(side)) setPaneOpen(side, !open[side]); }\n function holdClosed(side: PaneSide, next: boolean): void {\n if (held[side] === next) return;\n held[side] = next;\n apply(); // not persisted: the hold is the mode's, the remembered state is the person's\n }\n function resetWidths(): void { delete width.nav; delete width.inspector; apply(); persist(); }\n\n navToggle.addEventListener(\"click\", () => togglePane(\"nav\"));\n inspToggle.addEventListener(\"click\", () => togglePane(\"inspector\"));\n\n // --- drag resize -----------------------------------------------------------\n function beginResize(side: PaneSide, startEvent: PointerEvent): void {\n if (cfg[side].resizable === false || !showing(side)) return;\n startEvent.preventDefault();\n const pane = side === \"nav\" ? navPane : inspPane;\n const seam = side === \"nav\" ? navResizer : inspResizer;\n seam.classList.add(\"dragging\");\n const min = cfg[side].minWidth ?? 160;\n const max = cfg[side].maxWidth ?? DEFAULT_MAX;\n const startX = startEvent.clientX;\n const startW = pane.getBoundingClientRect().width;\n document.body.classList.add(\"pane-resizing\");\n let latest = startW;\n const onMove = (e: PointerEvent): void => {\n const delta = side === \"nav\" ? e.clientX - startX : startX - e.clientX;\n latest = Math.max(min, Math.min(max(window.innerWidth), startW + delta));\n panes.style.setProperty(openVar[side], `${latest}px`);\n };\n const onUp = (): void => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n document.body.classList.remove(\"pane-resizing\");\n seam.classList.remove(\"dragging\");\n width[side] = latest;\n persist();\n };\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n }\n navResizer.addEventListener(\"pointerdown\", (e) => beginResize(\"nav\", e));\n inspResizer.addEventListener(\"pointerdown\", (e) => beginResize(\"inspector\", e));\n navResizer.addEventListener(\"dblclick\", () => { delete width.nav; apply(); persist(); });\n inspResizer.addEventListener(\"dblclick\", () => { delete width.inspector; apply(); persist(); });\n\n apply();\n\n return {\n root, topbar, topbarLead, topbarTrail,\n nav: navInner, centre, inspector: inspInner,\n togglePane, setPaneOpen, isOpen: showing, holdClosed, resetWidths, state: snapshot,\n destroy: () => { root.remove(); },\n };\n}\n","// ---------------------------------------------------------------------------\n// The tool-window kit, RENDERER half: the always-on-top pin button every\n// helper window carries (previously pasted verbatim in five places across\n// the two apps). The matching chrome CSS - .swin-head drag bar, .swin-pin,\n// .swin-close - ships as tool-window.css.\n//\n// The head bar (toolWindowHead) and the follow toggle (followButton) live here\n// too since 0.39. The frameless drag bar itself needs no JS at all: it is markup + the\n// -webkit-app-region rules in tool-window.css.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\nimport { isEditableTarget } from \"./util.js\";\n\nexport interface PinButtonOptions {\n pinned: boolean;\n /** Persist the choice. The button reflects the click itself. */\n onToggle: (on: boolean) => void;\n}\n\n/** A pin and the handle to drive it, the same shape `saveIndicator` returns. */\nexport interface PinButton {\n el: HTMLButtonElement;\n /**\n * Reflect a pin state decided ELSEWHERE, without calling `onToggle`.\n *\n * The state is not always the button's to choose: main re-pins a helper window\n * on Reset View, and the window is told after the fact. Before this existed a\n * host had to reach in and set the class, the aria and the title by hand,\n * which is three chances to disagree with the button about what it is showing.\n */\n set: (pinned: boolean) => void;\n}\n\n/** The always-on-top pin: `.swin-pin`, aria-pressed, and a tooltip that says\n * what a click will do (the richest of the family's variants, kept). */\nexport function pinButton(opts: PinButtonOptions): PinButton {\n const b = el(\"button\", \"swin-pin\");\n b.type = \"button\";\n // The vocabulary's pin (it used to be a hand-drawn copy of the same shape,\n // pasted in five places across the two apps before it came here).\n b.append(iconNode(\"pin\"));\n let pinned = opts.pinned;\n const reflect = (): void => {\n b.classList.toggle(\"on\", pinned);\n b.setAttribute(\"aria-pressed\", String(pinned));\n // The THEMED rollover, not the platform's. 0.21.0 swept `title` out of this\n // package and missed this one, so every helper window in the family had a\n // single control waiting on the OS delay and then drawing the OS bubble,\n // sitting beside controls that did not. `title` was also the button's only\n // accessible name (the glyph is an aria-hidden SVG), so the label has to go\n // to `aria-label` as it moves, or dropping `title` would take the name too.\n const label = pinned ? \"Pinned on top. Click to unpin.\" : \"Keep on top\";\n b.dataset[\"tip\"] = label;\n b.setAttribute(\"aria-label\", label);\n };\n reflect();\n b.addEventListener(\"click\", () => { pinned = !pinned; reflect(); opts.onToggle(pinned); });\n return { el: b, set: (next: boolean) => { pinned = next; reflect(); } };\n}\n\n// --- the head ------------------------------------------------------------------\n\nexport interface ToolWindowHeadOptions {\n /** The window's own name. Constant: it says which window this is, so it must\n * not change as projects open and close. Omitted by a window whose mode\n * tabs stand where the title would be (Find): pass them as `tabs`. */\n title?: string;\n /** Mode tabs in the title's place (Find's bar: modes left, pin and close right). */\n tabs?: HTMLElement;\n /** A pin BUILT ALREADY, for a window that mounts its chrome once and drives\n * the button with `pin.set(on)` rather than rebuilding the bar. That is the\n * better shape (no re-entrancy question, survives a partial repaint), so the\n * helper takes either. */\n pin?: { el: HTMLElement };\n pinned?: boolean;\n onPin?: (on: boolean) => void;\n onClose: () => void;\n /** Between the title and the spacer: a subtitle, the project name. */\n lead?: (Node | string | null)[];\n /** Between the spacer and the pin: this window's own controls. */\n trail?: (Node | string | null)[];\n /** An extra class beside `swin-head`, for a window that styles its own bar. */\n className?: string;\n /**\n * Escape closes the window (default true): the family's rule, written once\n * here rather than per window.\n *\n * The listener stands aside on its own for a field's Escape (an input,\n * textarea, select or contenteditable target: clearing, cancelling an edit)\n * and for an Escape a deeper handler has already `preventDefault()`ed.\n * `\"always\"` drops the first of those: a window whose focus LIVES in a\n * field (Find, whose query box is where the typing is) still closes on\n * Escape from it. `false` turns the listener off for a window that owns\n * Escape entirely; it calls `onClose` itself when nothing smaller is left.\n */\n esc?: boolean | \"always\";\n /**\n * A window that layers Escape (a selection to clear, a pending outcome to\n * drop, a sweep that must not be closed out from under) says so here:\n * return true when the key was consumed and the window stays open, false\n * to let it close. Innermost first; the window is last.\n */\n onEscape?: () => boolean;\n}\n\ninterface EscSlot {\n onClose: () => void;\n onEscape?: (() => boolean) | undefined;\n fromFields: boolean;\n}\n\n/** The one Escape listener per window. The head may be rebuilt on every render\n * (a play surface does), so the listener is installed once and reads the\n * latest close through this slot. */\nlet escSlot: EscSlot | undefined;\nlet escWired = false;\nfunction wireEsc(): void {\n if (escWired) return;\n escWired = true;\n window.addEventListener(\"keydown\", (e) => {\n if (e.key !== \"Escape\" || !escSlot) return;\n // A deeper handler that claimed the key (a popover closing, a picker\n // dismissing) has said so already.\n if (e.defaultPrevented) return;\n // A field's own Escape (clearing, cancelling an edit) comes first, unless\n // the window asked to close from its fields too.\n if (!escSlot.fromFields && isEditableTarget(e.target)) return;\n // The window's own layers, innermost first; true means one of them took it.\n if (escSlot.onEscape?.() === true) return;\n escSlot.onClose();\n });\n}\n\n/** The `.swin-head` bar every tool window wears: title (or tabs), lead, spacer,\n * trail, pin, close. The close button says \"Close (Esc)\" once, here, rather\n * than in every window. */\nexport function toolWindowHead(opts: ToolWindowHeadOptions): HTMLElement {\n const keep = (parts: (Node | string | null)[] | undefined): (Node | string)[] =>\n (parts ?? []).filter((p): p is Node | string => p !== null);\n const pin = opts.pin\n ?? pinButton({ pinned: opts.pinned ?? true, onToggle: opts.onPin ?? (() => {}) });\n if (opts.esc !== false) {\n escSlot = { onClose: opts.onClose, onEscape: opts.onEscape, fromFields: opts.esc === \"always\" };\n wireEsc();\n }\n return el(\"header\", { className: `swin-head${opts.className ? ` ${opts.className}` : \"\"}` },\n ...(opts.title !== undefined ? [el(\"span\", { className: \"swin-title\", text: opts.title })] : []),\n ...(opts.tabs ? [opts.tabs] : []),\n ...keep(opts.lead),\n el(\"span\", { className: \"swin-spacer\" }),\n ...keep(opts.trail),\n pin.el,\n el(\"button\", { className: \"swin-close\", tip: \"Close (Esc)\", onClick: opts.onClose }, iconNode(\"close\")),\n );\n}\n\n// --- follow in the editor -------------------------------------------------------\n\nexport interface FollowButtonOptions {\n on: boolean;\n /** Persist the choice. The button reflects the click itself. */\n onToggle: (on: boolean) => void;\n /** What the editor opens as the run goes: \"line\" (Patterpad), \"card\"\n * (Storyletter). Default \"line\". */\n what?: string;\n}\n\n/** A toggle and the handle to drive it, the same shape `pinButton` returns. */\nexport interface FollowButton {\n el: HTMLButtonElement;\n /** Reflect a state decided elsewhere, without calling `onToggle`. */\n set: (on: boolean) => void;\n}\n\n/**\n * \"Follow in the editor\": the play surface's toggle that makes the EDITOR\n * follow the run, opening each line / card as it plays. Both play surfaces\n * built one (patterpad play.ts, studio table.ts) with the same label and\n * paraphrased tips; this is the one wording.\n *\n * The LABEL is the careful part. \"Follow\" alone would match the vocabulary of\n * a window that follows the editor; this is the other direction, the editor\n * following the run. Off by default and remembered by the host: following is\n * the author asking for it.\n */\nexport function followButton(opts: FollowButtonOptions): FollowButton {\n const what = opts.what ?? \"line\";\n const b = el(\"button\", \"btn swin-follow\", \"Follow in the editor\");\n b.type = \"button\";\n let on = opts.on;\n const reflect = (): void => {\n b.classList.toggle(\"on\", on);\n b.setAttribute(\"aria-pressed\", String(on));\n const label = on\n ? `The editor opens each ${what} as it plays. Click to stop.`\n : `Open each ${what} in the editor as it plays`;\n b.dataset[\"tip\"] = label;\n };\n reflect();\n b.addEventListener(\"click\", () => { on = !on; reflect(); opts.onToggle(on); });\n return { el: b, set: (next: boolean) => { on = next; reflect(); } };\n}\n\nexport interface SelectionFollowButtonOptions {\n on: boolean;\n /** Persist the choice. The button reflects the click itself. */\n onToggle: (on: boolean) => void;\n /** What the window stays on when it stops following: \"line\" (Patterpad), \"card\" (Storyletter). */\n what?: string;\n}\n\n/**\n * \"Follow selection\": the other direction from `followButton`. A helper window\n * that shows whatever the editor has selected (Patterpad's fields window, after\n * Pattershot's brief H1) moves along as the author moves; off, it stays on the\n * node it shows. On by default in such a window: following is what it is for.\n */\nexport function selectionFollowButton(opts: SelectionFollowButtonOptions): FollowButton {\n const what = opts.what ?? \"line\";\n const b = el(\"button\", \"btn swin-follow\", \"Follow selection\");\n b.type = \"button\";\n let on = opts.on;\n const reflect = (): void => {\n b.classList.toggle(\"on\", on);\n b.setAttribute(\"aria-pressed\", String(on));\n b.dataset[\"tip\"] = on\n ? `Shows whatever is selected in the editor. Click to stay on this ${what}.`\n : `Stays on this ${what}. Click to follow the selection.`;\n };\n reflect();\n b.addEventListener(\"click\", () => { on = !on; reflect(); opts.onToggle(on); });\n return { el: b, set: (next: boolean) => { on = next; reflect(); } };\n}\n","// ---------------------------------------------------------------------------\n// A themed in-app confirmation modal (design-language \"coherent to the\n// edges\": never a stock OS dialog). The promise API is Patterpad's surface\n// confirm; the build sits on the family's dialog frame (dialog.ts), so the\n// focus trap, Esc, the scrim and the exit motion are the frame's. Two\n// deliberate corrections over that original, kept when Patterpad migrates\n// here: the destructive button wears --danger (a required host token; the\n// surface one mistakenly used --accent), and initial focus lands on Cancel so\n// a stray Enter cannot destroy anything.\n//\n// Styles: dialog.css + controls.css for the frame and the buttons, confirm.css\n// for what is confirm's own (its width, its body copy). Callers keep the\n// elide-the-dialog rule: when nothing is at stake, don't ask.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\nexport interface ConfirmOptions {\n title: string;\n /** One plain sentence, ending with what undo does. */\n body?: string;\n /**\n * Content that is evidence rather than a sentence: a list of the scenes that\n * refer to the thing, a warning line. Rendered after `body` when both are\n * given. The caller styles it; the frame's body is a column with a gap.\n */\n bodyNode?: HTMLElement;\n /** The destructive button's label, which names its object (\"Delete scene\",\n * \"Remove language\"). */\n confirmLabel: string;\n}\n\n/** Resolves true on confirm; false on Cancel / Esc / backdrop. */\nexport function confirmDialog(opts: ConfirmOptions): Promise<boolean> {\n return new Promise((resolve) => {\n let done = false;\n // Resolve on the gesture, not after the exit motion: the caller acts on the\n // answer at once, and the fade plays over whatever it does.\n const finish = (ok: boolean): void => {\n if (done) return;\n done = true;\n resolve(ok);\n frame.close();\n };\n const frame = dialogFrame({\n title: opts.title, className: \"confirm-dialog\",\n onClose: () => finish(false), // Esc, or closed by someone else\n });\n const cancel = el(\"button\", \"btn confirm-btn cancel\", \"Cancel\");\n const danger = el(\"button\", \"btn danger confirm-btn\", opts.confirmLabel);\n cancel.type = \"button\";\n danger.type = \"button\";\n if (opts.body !== undefined) frame.body.append(el(\"div\", \"confirm-body\", opts.body));\n if (opts.bodyNode) frame.body.append(opts.bodyNode);\n frame.actions.append(cancel, danger);\n\n cancel.addEventListener(\"click\", () => finish(false));\n danger.addEventListener(\"click\", () => finish(true));\n // Esc arrives as the dialog's cancel event; the frame routes it through the\n // exit motion and onClose. A mousedown that lands on the dialog element\n // itself is the backdrop.\n frame.dialog.addEventListener(\"mousedown\", (e) => { if (e.target === frame.dialog) finish(false); });\n\n frame.open();\n cancel.focus();\n });\n}\n","// ---------------------------------------------------------------------------\n// The About surface: every app in the family has one, and it should never be\n// the grey OS panel (design-language, \"coherent to the edges\").\n//\n// Patterpad's, generalised. Theirs reuses its updater-dialog chrome and hard\n// codes the PatterKit wordmark; the shell takes the wordmark as markup from the\n// app, because that is the one part that is genuinely per-product. Everything\n// else - the layout, the type, the link handling, the single Close - is the\n// family's, so two apps' About boxes are recognisably siblings.\n//\n// The host opens links, because a renderer cannot: `onOpenLink` goes through\n// the app's own IPC to `shell.openExternal`. Without it the links are shown as\n// plain text rather than as buttons that do nothing.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\nexport interface AboutOptions {\n /** The product's name, large. */\n appName: string;\n /** \"1.4.2\" - shown as \"Version 1.4.2\". */\n version: string;\n /** One or two sentences: what this app is for. */\n blurb?: string;\n /** Licence, authorship, the suite it belongs to. Newlines are respected. */\n credits?: string;\n links?: { label: string; url: string }[];\n /** Inline SVG for the product wordmark, if it has one. */\n wordmark?: string;\n /** Open a URL in the real browser. Omit and the links render as text. */\n onOpenLink?: (url: string) => void;\n}\n\n/** Show the About dialog. Resolves when it closes. */\nexport function showAbout(opts: AboutOptions): Promise<void> {\n return new Promise((resolve) => {\n // On the family's frame: the product name is the frame's title, centred by\n // about.css, and the wordmark sits above it.\n const frame = dialogFrame({ title: opts.appName, className: \"shell-about\", onClose: () => resolve() });\n frame.dialog.setAttribute(\"aria-label\", `About ${opts.appName}`);\n if (opts.wordmark !== undefined) {\n const mark = el(\"div\", \"shell-about-mark\");\n mark.innerHTML = opts.wordmark;\n frame.dialog.prepend(mark);\n }\n frame.body.append(el(\"div\", \"shell-about-version\", `Version ${opts.version}`));\n if (opts.blurb !== undefined) frame.body.append(el(\"p\", \"shell-about-blurb\", opts.blurb));\n if (opts.credits !== undefined) frame.body.append(el(\"p\", \"shell-about-credits\", opts.credits));\n\n if (opts.links?.length) {\n const row = el(\"div\", \"shell-about-links\");\n for (const link of opts.links) {\n if (!opts.onOpenLink) { row.append(el(\"span\", \"shell-about-link\", link.label)); continue; }\n const a = el(\"button\", \"shell-about-link\", link.label);\n a.type = \"button\";\n a.addEventListener(\"click\", () => opts.onOpenLink?.(link.url));\n row.append(a);\n }\n frame.body.append(row);\n }\n\n const close = el(\"button\", \"btn shell-about-close\", \"Close\");\n close.type = \"button\";\n close.addEventListener(\"click\", () => frame.close());\n frame.actions.append(close);\n frame.open();\n close.focus();\n });\n}\n","// ---------------------------------------------------------------------------\n// A floating panel anchored to the thing it is about: Patterpad's\n// `openAnchoredPanel`, lifted and generalised.\n//\n// Patterpad's condition, effects, jump and gameId editors all use one, and so do\n// Storyletter's comment threads. Two implementations of one idea existed for\n// about a day, which is exactly the drift the shell is for - and Patterpad's was\n// the mature one, so this is that, not the one written second.\n//\n// The behaviours are all learnt from use, and each is a bug somebody hit:\n//\n// - Re-clicking the SAME anchor toggles the panel off and returns null,\n// rather than closing and reopening with a flicker. Callers must handle the\n// null.\n// - A DIFFERENT anchor swaps rather than stacking: only one is ever open.\n// - The outside-click listener is armed a tick late, or the very click that\n// opened the panel would close it again.\n// - `ignoreDown` spares body-level inner popovers, which are outside the\n// panel in the DOM but inside it as far as the author is concerned.\n// - `deferEscape` leaves Escape to an inner popover when one is open, so one\n// key does not close two things.\n//\n// Placement is the part worth reading. It flips to whichever side of the anchor\n// has MORE room, keeps clear of any bottom bars the app names, caps at 70% of\n// the window and scrolls its body. A panel that opens cramped against a status\n// bar with empty space above it is the failure this avoids.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface AnchoredPanel {\n panel: HTMLElement;\n /** The content area to populate: the header is built for you. */\n body: HTMLElement;\n /** Idempotent; fires the caller's `onClose` exactly once. */\n close: () => void;\n}\n\nexport interface AnchoredPanelOptions {\n anchor: HTMLElement;\n /** Extra class(es) on the panel, for app-specific sizing or colour. */\n className?: string;\n title: string;\n width: number;\n /**\n * Which side to try first.\n *\n * `\"left\"` suits a panel hung off a row in a right-hand pane (Patterpad's\n * inspector); `\"below\"` suits one hung off a button in a document's header\n * (Storyletter's topline). Either way it flips when the room is not there.\n */\n prefer?: \"left\" | \"below\" | \"centre\";\n /** Selectors for chrome the panel must not cover - a problems bar, a review\n * bar - so those stay usable while it is open. */\n keepClear?: string[];\n /** A pointerdown inside something matching this does NOT close the panel. */\n ignoreDown?: string;\n /** While something matching this exists, Escape belongs to it, not to us. */\n deferEscape?: string;\n /** Where the panel is appended. Default <body>. A panel opened from inside a\n * modal <dialog> passes the dialog: a body-level node sits behind the scrim,\n * inert, while one inside the dialog paints in its top layer. Placement is\n * viewport-relative either way (`position: fixed`). */\n host?: HTMLElement;\n onClose?: () => void;\n}\n\n/** The one open panel. Only ever one, which is what makes toggling meaningful. */\nlet current: { anchor: HTMLElement; panel: HTMLElement; close: () => void } | null = null;\n\nexport function openAnchoredPanel(opts: AnchoredPanelOptions): AnchoredPanel | null {\n if (current) {\n const prev = current;\n current = null;\n prev.close();\n if (prev.anchor === opts.anchor) return null; // a toggle, not a reopen\n }\n\n const panel = el(\"div\", `shell-anchored ${opts.className ?? \"\"}`.trim());\n const closeBtn = el(\"button\", \"shell-anchored-close\", iconNode(\"close\", 12));\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n const body = el(\"div\", \"shell-anchored-body\");\n panel.append(\n el(\"div\", \"shell-anchored-head\", el(\"span\", \"shell-anchored-title\", opts.title), closeBtn),\n body,\n );\n (opts.host ?? document.body).append(panel);\n placeAnchored(panel, opts.anchor, opts.width, opts.prefer ?? \"below\", opts.keepClear ?? []);\n\n let closed = false;\n const close = (): void => {\n if (closed) return;\n closed = true;\n if (current?.panel === panel) current = null;\n document.removeEventListener(\"pointerdown\", onDown, true);\n document.removeEventListener(\"keydown\", onKey, true);\n // Play the exit, THEN tear down. The order matters: `onClose` is where a\n // caller unmounts whatever it put in the body, so running it first would\n // leave an empty panel fading out. Listeners come off above rather than\n // here, because a panel mid-fade must not still be taking clicks.\n closeWithExit(panel, () => { opts.onClose?.(); panel.remove(); });\n };\n closeBtn.addEventListener(\"click\", close);\n\n const onDown = (e: PointerEvent): void => {\n const target = e.target as Node;\n if (panel.contains(target) || opts.anchor.contains(target)) return;\n if (opts.ignoreDown !== undefined && target instanceof Element && target.closest(opts.ignoreDown)) return;\n close();\n };\n const onKey = (e: KeyboardEvent): void => {\n if (e.key !== \"Escape\") return;\n if (opts.deferEscape !== undefined && document.querySelector(opts.deferEscape)) return;\n close();\n };\n // Next tick: the click that opened this is still travelling.\n setTimeout(() => {\n document.addEventListener(\"pointerdown\", onDown, true);\n document.addEventListener(\"keydown\", onKey, true);\n }, 0);\n\n current = { anchor: opts.anchor, panel, close };\n return { panel, body, close };\n}\n\n/** Close whatever is open. For a navigation or a re-render: a panel anchored to\n * an element that no longer exists would hang in the air. */\nexport function closeAnchoredPanel(): void {\n const open = current;\n current = null;\n open?.close();\n}\n\n/** Patterpad's placement, with the side as a parameter. */\nexport function placeAnchored(\n panel: HTMLElement, anchor: HTMLElement, width: number,\n prefer: \"left\" | \"below\" | \"centre\", keepClear: string[],\n): void {\n const a = anchor.getBoundingClientRect();\n const GAP = 10;\n const CAP = window.innerHeight * 0.7;\n const MIN_H = 140;\n panel.style.width = `${width}px`;\n\n // \"centre\" is for an anchor that is a POINT rather than a control: a zero-size\n // proxy dropped where somebody clicked on a canvas. Aligning a panel's edge to\n // a point puts the whole panel up and to one side of the spot the person just\n // chose, which reads as the panel having appeared somewhere else. Centred on\n // the point, it appears where they pointed.\n if (prefer === \"centre\") {\n const centred = a.left + a.width / 2 - width / 2;\n panel.style.left = `${Math.round(Math.min(Math.max(8, centred), window.innerWidth - width - 8))}px`;\n } else {\n // Horizontally: left of the anchor when asked and there is room, otherwise\n // aligned to it and nudged back inside the window.\n const leftOf = a.left - width - GAP;\n const alignedRight = Math.min(a.right - width, window.innerWidth - width - 8);\n panel.style.left = `${Math.round(prefer === \"left\" && leftOf >= 8\n ? leftOf\n : Math.max(8, prefer === \"left\" ? a.left : alignedRight))}px`;\n }\n\n // Vertically: whichever side has more room, staying off the app's bottom bars.\n let bottomLimit = window.innerHeight - 8;\n for (const selector of keepClear) {\n const bar = document.querySelector(selector);\n if (!bar) continue;\n const r = bar.getBoundingClientRect();\n if (r.height > 0) bottomLimit = Math.min(bottomLimit, r.top - 6);\n }\n // Centred on a point vertically as well, for the same reason, but only while\n // the panel actually fits: past that it falls back to the more-room-wins rule\n // below rather than hanging off the top of the window.\n if (prefer === \"centre\") {\n const height = Math.min(CAP, Math.max(MIN_H, panel.offsetHeight));\n const mid = a.top + a.height / 2 - height / 2;\n if (mid >= 8 && mid + height <= bottomLimit) {\n panel.style.top = `${Math.round(mid)}px`;\n panel.style.bottom = \"auto\";\n panel.style.maxHeight = `${Math.round(CAP)}px`;\n return;\n }\n }\n const roomBelow = bottomLimit - (a.bottom + GAP);\n const roomAbove = (a.top - GAP) - 8;\n if (roomAbove > roomBelow) {\n panel.style.top = \"auto\";\n panel.style.bottom = `${Math.round(window.innerHeight - (a.top - GAP))}px`;\n panel.style.maxHeight = `${Math.round(Math.max(MIN_H, Math.min(CAP, roomAbove)))}px`;\n } else {\n panel.style.bottom = \"auto\";\n panel.style.top = `${Math.round(Math.max(8, a.bottom + GAP))}px`;\n panel.style.maxHeight = `${Math.round(Math.max(MIN_H, Math.min(CAP, roomBelow)))}px`;\n }\n}\n","// ---------------------------------------------------------------------------\n// Game IDs: the host-facing ADDRESS of a thing.\n//\n// This belongs to the shell because it follows from the SHAPE these apps share\n// rather than from any one subject: a project of data files that compiles to\n// artefacts for something else to consume. \"Something else consumes it\" is the\n// whole reason content needs a stable name a program can call it by. Nothing\n// here needs the words card, beat, hand or scene to describe.\n//\n// It arrived here as two byte-identical copies, in Storyletter's model and\n// Patterpad's, each with its own editor popover on top - and the two editors had\n// grown DIFFERENT manners for the same job, which is how the duplication was\n// noticed at all.\n//\n// DEPENDENCY-FREE on purpose. The apps' domain layers (compilers, CLIs, engine\n// runtimes) need these rules far from any UI, and they keep their own copies\n// rather than taking a dependency on a UI kit; each asserts parity with these in\n// a test, so the copies cannot drift in silence. A NEW app starts from these.\n// ---------------------------------------------------------------------------\n\n/**\n * Slugify a name into hyphen form: lower case, apostrophes dropped, runs of\n * anything else to a single hyphen, no leading or trailing hyphen.\n *\n * May return \"\", when there was nothing usable in the input. Callers treat that\n * as \"no pinned address\" rather than as an address.\n */\nexport function gameIdify(text: string): string {\n return text.toLowerCase().replace(/['’]/g, \"\")\n .replace(/[^a-z0-9-]+/g, \"-\").replace(/-+/g, \"-\").replace(/^-+|-+$/g, \"\");\n}\n\n/** Is this a legal address? Lower case, digits and hyphens, starting and ending\n * with a letter or a digit. Note \"\" is NOT legal: an empty pinned address means\n * \"derive one\", which is a different thing from an address. */\nexport function isValidGameId(gameId: string): boolean {\n return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(gameId);\n}\n","// ---------------------------------------------------------------------------\n// The gameId editor: a small anchored popover for setting a thing's address.\n//\n// Patterpad's `id-editor.ts`, lifted into the shell so both apps have the same\n// manners. They did not: Storyletter's accepted anything and let the save\n// quietly rewrite it, Patterpad's refused an illegal address and offered Tab as\n// an opt-in slugify. Patterpad's is the right half of that argument and is what\n// this is - the address is what a host will call the content by, and an app that\n// silently renames it has made a decision that was not its to make.\n//\n// THE SEAM: this knows nothing about what it is addressing. It takes the pinned\n// value, the name-derived fallback to show as a placeholder, and hands back a\n// string to persist (\"\" = go back to deriving one).\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { openAnchoredPanel } from \"./anchored.js\";\nimport { gameIdify as defaultSlugify, isValidGameId as defaultValid } from \"./ids.js\";\n\nexport interface GameIdEditorOptions {\n anchor: HTMLElement;\n /** The pinned address, or \"\" when it is being derived. */\n value: string;\n /** What it derives to, shown as the placeholder. */\n derived: string;\n /** Persist. \"\" means \"stop pinning, go back to deriving\". */\n onCommit: (gameId: string) => void;\n /** The panel's title. Both apps say \"Game ID\"; a third may not. */\n title?: string;\n /** Override the rules. Defaults are the shell's, which is what a new app\n * should use; an app whose domain layer owns them can pass its own. */\n slugify?: (text: string) => string;\n validate?: (gameId: string) => boolean;\n /** Where the panel goes, for hosts that anchor it somewhere tight. */\n prefer?: \"left\" | \"below\" | \"centre\";\n /** Chrome the panel must not cover, passed through to the anchored panel: an\n * app with a problems bar along the bottom wants it usable while an address\n * is being edited. Without this the editor is the one panel in a host that\n * can land on top of its own bars. */\n keepClear?: string[];\n}\n\n/**\n * Open it. Returns false when that click TOGGLED the panel shut, which is the\n * anchored panel's contract and worth passing on: a caller that repaints on open\n * needs to know it did not.\n */\nexport function openGameIdEditor(opts: GameIdEditorOptions): boolean {\n const slugify = opts.slugify ?? defaultSlugify;\n const valid = opts.validate ?? defaultValid;\n\n const panel = openAnchoredPanel({\n anchor: opts.anchor, className: \"shell-id-editor\", title: opts.title ?? \"Game ID\", width: 260,\n ...(opts.prefer !== undefined ? { prefer: opts.prefer } : {}),\n ...(opts.keepClear !== undefined ? { keepClear: opts.keepClear } : {}),\n });\n if (!panel) return false;\n\n const input = el(\"input\", \"shell-id-input\");\n input.type = \"text\";\n input.value = opts.value;\n input.placeholder = opts.derived || \"game-id\";\n input.spellcheck = false;\n\n const hint = el(\"div\", \"shell-id-hint\", \"Lower case letters, digits, and hyphens. Leave it empty to derive one from the name.\");\n const actions = el(\"div\", \"shell-id-actions\");\n const reset = el(\"button\", \"shell-id-reset\", \"Reset to auto\");\n const set = el(\"button\", \"shell-id-set\", \"Set\");\n reset.type = \"button\";\n set.type = \"button\";\n actions.append(reset, set);\n panel.body.append(input, hint, actions);\n\n // \"\" is allowed and means reset; anything else has to be a legal address.\n const legal = (): boolean => { const v = input.value.trim(); return v === \"\" || valid(v); };\n const sync = (): void => {\n set.disabled = !legal();\n hint.classList.toggle(\"bad\", !legal());\n };\n const commit = (): void => {\n if (!legal()) return;\n opts.onCommit(input.value.trim());\n panel.close();\n };\n\n input.addEventListener(\"input\", sync);\n input.addEventListener(\"keydown\", (e) => {\n // Stop keys reaching the app underneath: a canvas listening for Delete must\n // not act on one typed into an address.\n e.stopPropagation();\n if (e.key === \"Enter\") { e.preventDefault(); commit(); }\n // The opt-in coercion. Nothing is rewritten unless it is asked for, and when\n // it is, it happens in the field where it can be seen and undone.\n if (e.key === \"Tab\" && input.value.trim() !== \"\") {\n e.preventDefault();\n input.value = slugify(input.value);\n sync();\n }\n });\n set.addEventListener(\"click\", commit);\n reset.addEventListener(\"click\", () => { opts.onCommit(\"\"); panel.close(); });\n\n sync();\n setTimeout(() => input.focus(), 0);\n return true;\n}\n","// ---------------------------------------------------------------------------\n// The version-control surface's pure DOM half: folding a snapshot over the\n// shard key(s) an item stands for, the one badge that item flies, painting\n// those badges onto rows that already exist, and turning a document's editing\n// controls off when somebody else holds its shard.\n//\n// The family's, for the same reason the main half is: it follows from the app\n// SHAPE. Patterpad's #145 grammar, by way of Storyletter's port.\n//\n// Render-FREE by design, and that is load-bearing: a poll repaints badges\n// without rebuilding anything, so a mid-edit document is never disturbed.\n//\n// The one thing an app must supply is WHAT STAYS LIVE in a locked document\n// (`staysLive`): reading a shard somebody else holds must remain fully\n// possible, so navigation, disclosure and view switches keep working while the\n// writing stops - and only the app knows what those look like in its own\n// markup.\n// ---------------------------------------------------------------------------\n\nimport { icon, iconNode, type IconName } from \"./icons.js\";\nimport { el } from \"./dom.js\";\n\n/** One shard's state as the renderer sees it. The app's own DTO will be this\n * shape (it crosses its own bridge); this is what the view needs. */\nexport interface ShardVc {\n key: string;\n writable: boolean;\n lockedBy?: string[];\n outOfDate?: boolean;\n /** WE hold it: still ours to edit. */\n checkedOutByMe?: boolean;\n /** Tracked, with uncommitted local changes. */\n dirty?: boolean;\n /** Not in version control yet. */\n untracked?: boolean;\n}\n\n/** A snapshot, keyed by shard key. Absent = clean, writable, up to date. */\nexport type VcMap = ReadonlyMap<string, ShardVc>;\n\n/** Fold the state of the shard key(s) an item stands for (a box row spans its\n * box + tags + hands shards), most actionable winning: any holder locks it,\n * any stale shard makes it stale, any read-only shard makes it read-only. */\nexport function foldVc(shards: VcMap, keys: string | undefined): ShardVc | undefined {\n if (!keys) return undefined;\n let out: ShardVc | undefined;\n for (const key of keys.split(\" \")) {\n const s = shards.get(key);\n if (!s) continue;\n out ??= { key, writable: true };\n if (!s.writable) out.writable = false;\n if (s.lockedBy?.length) out.lockedBy = [...new Set([...(out.lockedBy ?? []), ...s.lockedBy])];\n if (s.outOfDate) out.outOfDate = true;\n if (s.checkedOutByMe) out.checkedOutByMe = true;\n if (s.dirty) out.dirty = true;\n if (s.untracked) out.untracked = true;\n }\n return out;\n}\n\n/** What a row flies: the word to draw (`name`), the class that colours it,\n * and the rollover. `glyph` is the deprecated typed character for a caller\n * still setting text; it goes when `icon` does. */\nexport interface VcBadge {\n name: IconName;\n /** @deprecated Draw `name` with `iconNode` instead. */\n glyph: string;\n cls: string;\n title: string;\n}\n\n/** The one badge an item flies, by priority, or null when there is nothing to\n * say. Drawn monochrome (never colour emoji), so each inherits its themed\n * colour and reads the same in Linen and Baize. */\nexport function vcBadgeFor(s: ShardVc | undefined): VcBadge | null {\n const badge = (name: IconName, cls: string, title: string): VcBadge => ({ name, glyph: icon[name as keyof typeof icon] ?? \"\", cls, title });\n if (s?.lockedBy?.length) return badge(\"locked\", \"vc-locked\", `Locked by ${s.lockedBy.join(\", \")}`);\n if (s?.outOfDate) return badge(\"down\", \"vc-stale\", \"Out of date. A newer version is on the server.\");\n // The three states of a file that is YOURS, most actionable first. Ordered so\n // the badge answers \"what would I do about this?\" rather than describing the\n // file: a checkout you are holding matters more than the edits inside it, and\n // both matter more than a file the VCS has never seen.\n if (s?.checkedOutByMe) return badge(\"checkedOut\", \"vc-mine\", \"Checked out by you\");\n if (s?.dirty) return badge(\"modified\", \"vc-dirty\", \"Modified. Local changes aren't committed yet.\");\n if (s?.untracked) return badge(\"untracked\", \"vc-new\", \"New. Not in version control yet.\");\n // Read-only on disk with NO other holder is still editable: the save checks\n // it out. Muted, and last, because under a lock-based VCS this is the\n // resting state of everything the author has not touched yet.\n if (s && !s.writable) return badge(\"readOnly\", \"vc-frozen\", \"Read-only on disk. Saving checks it out.\");\n return null;\n}\n\n/** Repaint the badge on every item under `root` that names a shard (its\n * `data-vc`). In place: no re-render, so nothing loses focus or scroll. */\nexport function paintVcBadges(root: ParentNode, shards: VcMap): void {\n root.querySelectorAll<HTMLElement>(\"[data-vc]\").forEach((host) => {\n host.querySelector(\":scope > .vc-badge\")?.remove();\n const badge = vcBadgeFor(foldVc(shards, host.dataset[\"vc\"]));\n if (!badge) return;\n // `tip` carries the accessible name too, so no separate aria-label. 12px:\n // the badge sits beside a row title at 0.78rem and must not outweigh it.\n const span = el(\"span\", { className: `vc-badge ${badge.cls}`, tip: badge.title }, iconNode(badge.name, 12));\n host.append(span);\n });\n}\n\ntype Disableable = HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\n\n/** The shell's own controls that only change what is shown, so a lock never\n * turns them off: an extension section's fold (value-fold.ts). */\nconst ALWAYS_LIVE = \".shell-fold-cap\";\n\n/** Turn a region's editing controls off - or back on, but only the ones WE\n * turned off, so a control disabled for its own reasons (the first card's\n * \"previous\", a redraw-turns box on \"always\") stays disabled. `staysLive` is\n * the app's own reading controls; the shell's (ALWAYS_LIVE) stay live too. */\nexport function lockControls(host: HTMLElement, off: boolean, staysLive: string): void {\n host.classList.toggle(\"vc-readonly\", off);\n if (off) {\n host.querySelectorAll<Disableable>(\"input, textarea, select, button\").forEach((c) => {\n if (c.disabled || c.matches(ALWAYS_LIVE) || (staysLive !== \"\" && c.matches(staysLive))) return;\n c.disabled = true;\n c.classList.add(\"vc-off\");\n });\n } else {\n host.querySelectorAll<Disableable>(\".vc-off\").forEach((c) => { c.disabled = false; c.classList.remove(\"vc-off\"); });\n }\n}\n\n/** The notice a locked document opens with: who holds it, and why nothing types. */\nexport function lockNotice(holders: string[]): HTMLElement {\n return el(\"div\", { className: \"vc-lock\" },\n el(\"span\", { className: \"vc-lock-glyph\" }, iconNode(\"locked\")),\n el(\"span\", { text: `Locked by ${holders.join(\", \")}. Read-only until they release it.` }));\n}\n","// ---------------------------------------------------------------------------\n// \"Who is working here?\" - Patterpad's first-run identity, as the family's.\n//\n// `{ name, email? }`, asked ONCE at first startup and skippable, editable later\n// from User Information… in the menu. It belongs to the person at the keyboard,\n// never to the project, which is why the app stores it in ITS OWN state and not\n// in a shard: a project carrying a name would hand it to whoever opened the file\n// next.\n//\n// The shell asks and returns; PERSISTING is the app's, because each app has its\n// own state file. `suggested` is there for the day simple-vc-lib can say who the\n// version control thinks you are (see patterkit from-storylets/vc-current-user),\n// so the box opens with a sensible answer rather than empty.\n//\n// Two occasions, one dialog (ui-review-2026-09, Patterpad adoption step 5): the\n// first run is a welcome, the menu item is an edit, and the title and the\n// primary button say which. Without a `mode` it is the plain ask it always was.\n// ---------------------------------------------------------------------------\n\nimport { el, labelled } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\nexport interface Identity { name: string; email?: string }\n\nexport interface IdentityOptions {\n /** What it already says, if anything. */\n current?: Identity;\n /** A name to offer when there is none: the VCS's, one day. */\n suggested?: Identity;\n /**\n * `\"welcome\"` is the first run (\"Welcome to <appName>\", Continue); `\"edit\"`\n * is the menu item (\"User information\", Save). Absent, the title is the\n * shell's \"Who is working here?\" and the button is Save.\n */\n mode?: \"welcome\" | \"edit\";\n /** Names the app in the welcome title. Without it the title is \"Welcome\". */\n appName?: string;\n /**\n * By default a skip resolves `undefined` and the app asks again next launch.\n * With this on, a skip resolves `{ name: \"\" }`: the app stores the blank and\n * never asks again (Patterpad's first run, where main fills in a default).\n * A Save with an empty name counts as a skip either way.\n */\n neverAskAgainOnSkip?: boolean;\n}\n\n/** Ask, and record the answer. Resolves when the dialog closes, either way. */\nexport function askIdentity(opts: IdentityOptions = {}): Promise<Identity | undefined> {\n return new Promise<Identity | undefined>((resolve) => {\n const seed = opts.current ?? opts.suggested;\n const name = el(\"input\", \"field shell-ident-input\") as HTMLInputElement;\n name.placeholder = \"Your name\";\n name.value = seed?.name ?? \"\";\n const email = el(\"input\", \"field shell-ident-input\") as HTMLInputElement;\n email.placeholder = \"Optional\";\n email.type = \"email\";\n email.value = seed?.email ?? \"\";\n\n const skip = el(\"button\", \"btn shell-ident-btn\", \"Skip\");\n const save = el(\"button\", \"btn primary shell-ident-btn\", opts.mode === \"welcome\" ? \"Continue\" : \"Save\");\n skip.type = \"button\"; save.type = \"button\";\n\n let done = false;\n const finish = (keep: boolean): void => {\n if (done) return;\n done = true;\n const typed = name.value.trim();\n const value = email.value.trim();\n const answer: Identity | undefined = keep && typed !== \"\"\n ? (value === \"\" ? { name: typed } : { name: typed, email: value })\n : (opts.neverAskAgainOnSkip === true ? { name: \"\" } : undefined);\n resolve(answer);\n frame.close();\n };\n\n // The frame owns the panel, the scrim, Escape and the exit motion; this\n // owns the two fields and the two answers.\n const frame = dialogFrame({\n title: titleFor(opts),\n sub: subFor(opts),\n className: \"shell-ident\",\n onClose: () => finish(false), // Escape, or closed by someone else: a skip\n });\n // Captions above the fields, not placeholders alone: a placeholder is gone\n // the moment there is a value in the box, and the VCS's suggestion is one.\n frame.body.append(el(\"div\", \"shell-ident-fields\", labelled(\"Name\", name), labelled(\"Email\", email)));\n frame.actions.append(skip, save);\n\n skip.addEventListener(\"click\", () => finish(false));\n save.addEventListener(\"click\", () => finish(true));\n // Enter saves, Escape skips: the two answers the dialog has.\n name.addEventListener(\"keydown\", (e) => { if (e.key === \"Enter\") finish(true); });\n email.addEventListener(\"keydown\", (e) => { if (e.key === \"Enter\") finish(true); });\n\n frame.open();\n name.focus();\n });\n}\n\nfunction titleFor(opts: IdentityOptions): string {\n if (opts.mode === \"welcome\") return opts.appName ? `Welcome to ${opts.appName}` : \"Welcome\";\n if (opts.mode === \"edit\") return \"User information\";\n return \"Who is working here?\";\n}\n\n/** The one sub line, minus its last sentence when this IS the later change. */\nfunction subFor(opts: IdentityOptions): string {\n const why = \"Your name goes on the comments you write. It's kept in this app, not in the project.\";\n return opts.mode === \"edit\" ? why : `${why} You can change it later.`;\n}\n","// ---------------------------------------------------------------------------\n// The documentation-notes editor: Patterpad's, as the family's (Patter spec 18).\n//\n// Documentation states the reason; comments are the conversation. A note says\n// why a card is here, who it is for, what a translator needs to know. It has no\n// author, no timestamp, and nobody replies to it.\n//\n// A MODAL, and that is a decision somebody already paid for: Patterpad tried an\n// anchored popover first and removed it as too cramped. Notes are prose, and\n// prose needs room.\n//\n// One text area PER CLASS, each non-blank line becoming a note of that class.\n// The alternative - a list of lines each with its own class dropdown - is more\n// granular and, as their proposal puts it, fiddlier: you would fill in a form to\n// write a sentence.\n//\n// THE SEAM: this knows nothing about what a note is attached to. The app hands\n// it the notes that apply, the ancestors they came from and the classes on\n// offer, and takes back a list. Whether the levels are box -> deck -> card or\n// scene -> block -> line never reaches here.\n//\n// Showing INHERITED notes read-only is Storyletter's addition, and it belongs in\n// the family: Patterpad deferred it because its stacked inspector already shows\n// each ancestor's Notes row, but an app whose editor has no such stack must show\n// them somewhere or an author will write the box's note again on the card.\n//\n// Patterpad moves back onto this one (the 2026-10 review, ruling I). Its own\n// copy had kept two things this had lost, the \"editor\" class (notes for an\n// outside editor, carried by the editable script) and the \"Needs re-record\"\n// prompt that opens straight on the Voice class, so both are here\n// (`focus`). It committed on every keystroke; this commits once, on close,\n// and the ruling takes that: opening a note to read it costs nothing.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\n/** One line of documentation. `type` names its class; absent = editor-only. */\nexport interface DocLine { type?: string; text: string }\n\n/** What applies to one thing: its own notes, and the ancestors' that reach it.\n * Inherited notes are READ-ONLY - they are edited where they live, which is\n * what stops a note being copied down and then disagreeing with itself. */\nexport interface Notes {\n own: DocLine[];\n /** Outermost first. Absent or empty in an app whose own UI already shows\n * each ancestor's notes (Patterpad's stacked inspector). */\n inherited?: { id: string; label: string; lines: DocLine[] }[];\n /** The classes this app offers, in the order they should appear. */\n classes: string[];\n}\n\nconst EDITOR_ONLY = \"\";\nconst UNTYPED_KEY = \"__untyped\";\n\n/** The classes the family already has words for; anything else is Title Cased,\n * so a studio can add one without touching this. Patterpad's labels. A class\n * is named for who READS the note, so \"editor\" is \"Editors\": the outside\n * editor the editable script goes to, not this app. */\nconst CLASS_LABEL: Record<string, string> = {\n everyone: \"Everyone\", writing: \"Writing\", vo: \"Voice (VO)\", loc: \"Localisers\", editor: \"Editors\",\n};\nconst label = (cls: string): string =>\n cls === EDITOR_ONLY ? \"Note (editor-only)\"\n : CLASS_LABEL[cls] ?? (cls[0]!.toUpperCase() + cls.slice(1));\n\nconst PLACEHOLDER: Record<string, string> = {\n vo: \"Direction for the voice actor or director\",\n loc: \"Context for translators\",\n editor: \"Context for an outside editor\",\n};\nconst placeholder = (cls: string): string =>\n cls === EDITOR_ONLY ? \"An internal note, never exported\"\n : PLACEHOLDER[cls] ?? \"Why this is here\";\n\nexport interface NotesEditorOptions {\n /** What this is about, for the dialog's title (\"Notes: Arrive at the gate\").\n * Absent, the title is \"Notes\". */\n subject?: string;\n /** One line under the title, for what the classes are FOR in this app\n * (Patterpad's \"A VO note travels with the voice script…\"): the dialog\n * frame's own sub line. */\n sub?: string;\n notes: Notes;\n /** Open on this class: its text area is shown even when empty, and the\n * caret is in it. Patterpad's \"Needs re-record\" asks why the line needs\n * another take, and the answer is a VO note, so it opens on \"vo\". Ignored\n * when the class is neither offered (`notes.classes`) nor already written\n * on, so a prompt cannot add a class the thing does not take. \"\" is the\n * editor-only bucket. */\n focus?: string;\n /** Commit. Called once, on close, and only when something changed. */\n save: (lines: DocLine[]) => void;\n}\n\n/** Open the notes modal. Returns at once; `save` runs when it closes. */\nexport function openNotesEditor(opts: NotesEditorOptions): void {\n const { notes } = opts;\n const inherited = notes.inherited ?? [];\n\n // Seed one text area per class that already has notes; a fresh thing gets the\n // first class in the vocabulary, so the commonest act (write a note) is typing\n // rather than choosing.\n const seed = new Map<string, string>();\n for (const line of notes.own) {\n const key = line.type ?? EDITOR_ONLY;\n seed.set(key, seed.has(key) ? `${seed.get(key)!}\\n${line.text}` : line.text);\n }\n const shown: string[] = [...seed.keys()];\n const focus = opts.focus !== undefined\n && (opts.focus === EDITOR_ONLY || notes.classes.includes(opts.focus) || seed.has(opts.focus))\n ? opts.focus : undefined;\n if (focus !== undefined && !shown.includes(focus)) shown.push(focus);\n if (shown.length === 0) shown.push(notes.classes[0] ?? EDITOR_ONLY);\n const areas = new Map<string, HTMLTextAreaElement>();\n\n const value = (): DocLine[] => {\n const out: DocLine[] = [];\n for (const cls of shown) {\n const area = areas.get(cls);\n if (!area) continue;\n for (const raw of area.value.split(\"\\n\")) {\n const text = raw.trim();\n if (text !== \"\") out.push(cls === EDITOR_ONLY ? { text } : { type: cls, text });\n }\n }\n return out;\n };\n\n const body = el(\"div\", \"shell-notes-body\");\n\n const render = (): void => {\n body.replaceChildren();\n\n // The inherited context first, because it is what an author needs BEFORE\n // deciding whether to write anything: outermost first, muted, and labelled\n // with where it lives so the answer to \"why can I not edit this\" is on\n // screen.\n if (inherited.length > 0) {\n const from = el(\"div\", { className: \"shell-notes-inherited\" },\n el(\"div\", \"shell-notes-caption\", \"Inherited\"));\n for (const level of inherited) {\n from.append(el(\"div\", { className: \"shell-notes-level\" },\n el(\"span\", \"shell-notes-level-label\", level.label),\n ...level.lines.map((line) => el(\"div\", { className: \"shell-notes-line\" },\n ...(line.type !== undefined && line.type !== \"everyone\"\n ? [el(\"span\", \"shell-notes-cls\", label(line.type))] : []),\n el(\"span\", \"\", line.text))),\n ));\n }\n body.append(from);\n }\n\n for (const cls of shown) {\n const block = el(\"div\", { className: \"shell-notes-class\" },\n el(\"label\", \"shell-notes-caption\", label(cls)));\n const area = el(\"textarea\", \"field shell-notes-text\");\n area.value = areas.get(cls)?.value ?? seed.get(cls) ?? \"\";\n area.rows = 4;\n area.placeholder = placeholder(cls);\n area.spellcheck = true;\n areas.set(cls, area);\n block.append(area);\n body.append(block);\n }\n\n // One line per note, said once where somebody is about to type.\n body.append(el(\"p\", \"shell-notes-hint\", \"One note per line.\"));\n\n const remaining = [...notes.classes, EDITOR_ONLY].filter((c) => !shown.includes(c));\n if (remaining.length > 0) {\n const add = el(\"select\", \"shell-notes-add\");\n add.append(new Option(\"Add a note for…\", \"\"));\n for (const cls of remaining) add.append(new Option(label(cls), cls === EDITOR_ONLY ? UNTYPED_KEY : cls));\n add.addEventListener(\"change\", () => {\n if (add.value === \"\") return;\n const cls = add.value === UNTYPED_KEY ? EDITOR_ONLY : add.value;\n shown.push(cls);\n render();\n areas.get(cls)?.focus();\n });\n body.append(add);\n }\n };\n render();\n\n const done = el(\"button\", \"btn primary shell-notes-btn\", \"Done\");\n done.type = \"button\";\n\n const before = JSON.stringify(notes.own);\n // Done and Escape both end up here (the frame's onClose runs for every way\n // out), so the guard is what stops one gesture saving twice.\n let closed = false;\n const commit = (): void => {\n if (closed) return;\n closed = true;\n const next = value();\n // Only when something changed: opening a note to read it must not cost an\n // undo step, and must not touch the file (or the version control status).\n if (JSON.stringify(next) !== before) opts.save(next);\n };\n // Escape closes the dialog by itself, and commits: this editor has no Cancel,\n // because a note is prose and losing a paragraph to a stray key is worse than\n // an unwanted note somebody can delete.\n const frame = dialogFrame({\n title: opts.subject !== undefined ? `Notes: ${opts.subject}` : \"Notes\",\n ...(opts.sub !== undefined ? { sub: opts.sub } : {}),\n className: \"shell-notes\", onClose: commit,\n });\n frame.body.append(body);\n frame.actions.append(done);\n done.addEventListener(\"click\", () => { commit(); frame.close(); });\n\n frame.open();\n areas.get(focus ?? shown[0] ?? EDITOR_ONLY)?.focus();\n}\n","// ---------------------------------------------------------------------------\n// The comment thread popover: Patterpad's (#148), as the family's.\n//\n// Comments are the conversation; documentation states the reason. Anchored\n// rather than modal, which is the deliberate contrast with the notes editor: a\n// comment is had BESIDE the thing, and a modal covering the card would hide\n// what is being discussed.\n//\n// Everything here that looks like a small decision is one somebody already\n// made: author and timestamp on every message (a comment without a \"who\" is an\n// anonymous complaint); Enter posts and closes while Shift+Enter is a newline;\n// the button reads Comment on a new thread and Reply on an old one; focus goes\n// to the composer ONLY for a new thread, because opening an existing one to\n// read must not yank the cursor out of the work; and a thread does not exist\n// until its first message is posted, so a composer somebody thought better of\n// leaves nothing behind.\n//\n// THE SEAM: an anchor is an opaque id. What it points at - a card, a beat, a\n// span of characters - is the app's, and so is where the threads are stored.\n// This takes a list and gives back \"post this\" and \"resolve that\".\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\nimport { openAnchoredPanel } from \"./anchored.js\";\n\n/** One message. The author is a NAME stamped at posting time, not a reference:\n * people leave projects and their words stay. */\nexport interface CommentMessage {\n author: string; ts: string; body: string;\n /** A TOMBSTONE: the words are gone, the turn in the conversation is not.\n * Deleting a reply out of a thread would renumber the argument around it, so\n * what is left says who spoke and when and that they withdrew it. The body is\n * emptied on the way out, so \"deleted\" means deleted from the file too. */\n deleted?: boolean;\n}\n\n/** A thread, anchored to the id of whatever it is about. */\nexport interface Comment {\n id: string;\n anchor: string;\n resolved?: boolean;\n messages: CommentMessage[];\n}\n\nconst stamp = (ts: string): string => {\n const when = new Date(ts);\n if (Number.isNaN(when.getTime())) return ts;\n return when.toLocaleString(undefined, { dateStyle: \"medium\", timeStyle: \"short\" });\n};\n\n/** A preview of a thread, for the list: its first message, shortened. The first\n * message that still SAYS something - a thread whose opener was withdrawn is\n * previewed by whatever carried on, not by its tombstone. */\nconst gist = (thread: Comment): string => {\n const first = thread.messages.find((m) => m.deleted !== true)?.body ?? \"\";\n return first.length > 60 ? `${first.slice(0, 57)}…` : first;\n};\n\nexport interface CommentsOptions {\n anchor: HTMLElement;\n /** What is being discussed, for the panel's title. */\n subject: string;\n /** Every thread on this thing, resolved ones included. */\n threads: Comment[];\n /** Review ▸ Show Resolved Comments. */\n showResolved: boolean;\n /** A fresh thread id, minted by the caller so main never has to. */\n newThreadId: () => string;\n post: (threadId: string, body: string) => void;\n setResolved: (threadId: string, resolved: boolean) => void;\n /**\n * Withdraw one message. Absent = the host does not offer deletion.\n *\n * By INDEX, because a message has no id of its own and giving every message\n * one would be a format change to every stored thread for the sake of one\n * verb. The index is read off the list the reader is looking at, and these\n * files are edited by one person at a time with a VCS underneath.\n *\n * The host decides what deleting MEANS: the agreed rule is that the message\n * becomes a tombstone, and the whole thread goes when nothing readable would\n * be left (which covers the common case of withdrawing a comment nobody had\n * replied to).\n */\n deleteMessage?: (threadId: string, index: number) => void;\n /** Where the panel sits: \"centre\" for a point on a canvas (see anchored.ts). */\n prefer?: \"left\" | \"below\" | \"centre\";\n /**\n * Chrome the panel must not cover. Defaults to `[\".problembar\"]`, which was\n * hardcoded here and should not have been: one app's class name in shared\n * code, and it misses any OTHER bar a host has. A second app arrived with two.\n */\n keepClear?: string[];\n /**\n * The text a thread is pinned to, shown above it as a quotation.\n *\n * A callback rather than a field on `Comment`, because resolving an anchor is\n * the APP's half of the seam and this is only that resolution rendered: an app\n * that pins threads to a span of prose can quote it, one that pins them to a\n * whole card has nothing to quote and passes nothing. Keeping it out of the\n * thread type also means no change to what is written to disk, which matters\n * for files living in version control.\n *\n * Return undefined (or \"\") for a thread with no quotable anchor.\n */\n quoteFor?: (thread: Comment) => string | undefined;\n}\n\nexport function openComments(opts: CommentsOptions): void {\n const visible = opts.threads.filter((t) => opts.showResolved || t.resolved !== true);\n const panel = openAnchoredPanel({\n anchor: opts.anchor, className: \"comments\", title: \"Comments\", width: 320,\n // Below its button (the topline), unlike Patterpad's inspector rows which\n // want it to the left; and never over the problems bar, which an author may\n // be stepping through while reading the thread.\n prefer: opts.prefer ?? \"below\",\n keepClear: opts.keepClear ?? [\".problembar\"],\n });\n if (!panel) return; // the same anchor again: that click closed it\n const { body, close } = panel;\n\n // Which thread is on screen. A brand-new one until it is posted: it exists\n // only here, which is why nothing is written when the panel closes unposted.\n let open: Comment | undefined = visible.length === 1 ? visible[0] : undefined;\n let composing = visible.length === 0;\n\n const render = (): void => {\n body.replaceChildren();\n\n if (!composing && open === undefined) {\n // More than one: pick.\n for (const thread of visible) {\n const row = el(\"button\", { className: `shell-cmt-row${thread.resolved === true ? \" resolved\" : \"\"}` },\n el(\"span\", { className: \"shell-cmt-row-author\", text: thread.messages[0]?.author ?? \"Someone\" }),\n el(\"span\", { className: \"shell-cmt-row-gist\", text: gist(thread) }),\n ...(thread.messages.length > 1\n ? [el(\"span\", { className: \"shell-cmt-row-count\", text: `${thread.messages.length}` })] : []),\n );\n row.addEventListener(\"click\", () => { open = thread; render(); });\n body.append(row);\n }\n const fresh = el(\"button\", { className: \"shell-cmt-new\", text: \"New comment\" });\n fresh.addEventListener(\"click\", () => { open = undefined; composing = true; render(); });\n body.append(fresh);\n return;\n }\n\n const thread = open;\n if (thread) {\n // The span this thread is pinned to, quoted above it. Without it a comment\n // on a phrase reads as a comment on the whole thing, and the reader has to\n // remember what was selected.\n const quote = opts.quoteFor?.(thread);\n if (quote !== undefined && quote !== \"\") {\n body.append(el(\"blockquote\", { className: \"shell-cmt-quote\", text: quote }));\n }\n const list = el(\"div\", { className: `shell-cmt-list${thread.resolved === true ? \" resolved\" : \"\"}` });\n thread.messages.forEach((message, index) => {\n const head = el(\"div\", { className: \"shell-cmt-msg-head\" },\n el(\"span\", { className: \"shell-cmt-author\", text: message.author === \"\" ? \"Someone\" : message.author }),\n el(\"span\", { className: \"shell-cmt-ts\", text: stamp(message.ts) }));\n // Rollover-revealed, at the end of the head: the family's grip grammar,\n // and a delete that is always visible invites the accident it is.\n // Nothing is offered on a tombstone: there is nothing left to withdraw.\n if (opts.deleteMessage && message.deleted !== true) {\n const bin = el(\"button\", { className: \"shell-cmt-del\" }, iconNode(\"close\", 12));\n bin.dataset.tip = \"Delete this comment\";\n bin.setAttribute(\"aria-label\", \"Delete this comment\");\n bin.addEventListener(\"click\", () => opts.deleteMessage?.(thread.id, index));\n head.append(bin);\n }\n list.append(el(\"div\", { className: `shell-cmt-msg${message.deleted === true ? \" gone\" : \"\"}` },\n head,\n el(\"div\", { className: \"shell-cmt-body\", text: message.deleted === true ? \"Comment deleted\" : message.body })));\n });\n body.append(list);\n }\n\n const input = el(\"textarea\", { className: \"shell-cmt-input\" });\n input.rows = 2;\n input.placeholder = thread ? \"Reply…\" : \"Comment…\";\n input.addEventListener(\"keydown\", (e) => {\n e.stopPropagation();\n if (e.key !== \"Enter\" || e.shiftKey) return;\n e.preventDefault();\n submit(true);\n });\n body.append(input);\n\n const submit = (andClose: boolean): void => {\n const text = input.value.trim();\n if (text === \"\") return;\n const id = thread?.id ?? opts.newThreadId();\n opts.post(id, text);\n if (andClose) close(); else { input.value = \"\"; }\n };\n\n const actions = el(\"div\", { className: \"shell-cmt-actions\" });\n const post = el(\"button\", { className: \"shell-cmt-btn primary\", text: thread ? \"Reply\" : \"Comment\" });\n post.addEventListener(\"click\", () => submit(false));\n actions.append(post);\n\n if (thread) {\n const resolved = thread.resolved === true;\n const toggle = el(\"button\", { className: \"shell-cmt-btn\", text: resolved ? \"Reopen\" : \"Mark complete\" });\n toggle.addEventListener(\"click\", () => {\n opts.setResolved(thread.id, !resolved);\n // A resolved thread hides, so there is nothing left to look at.\n if (!resolved) close(); else render();\n });\n actions.append(toggle);\n }\n body.append(actions);\n\n // Only for a new thread: reading an old one must not steal the cursor.\n if (!thread) input.focus();\n };\n render();\n}\n","// ---------------------------------------------------------------------------\n// The long-job kit (renderer): the progress strip the main-side JobHost feeds.\n// A determinate bar, a count, elapsed and estimated remaining, and Cancel.\n//\n// A STRIP, NOT A MODAL. The window that started the job is the one that shows\n// it, and the rest of the app stays usable: these are tool windows, and taking\n// the whole editor hostage while a sweep runs was the old system's mistake.\n// The strip is hidden until a job starts and hides itself when one ends.\n//\n// TWO MODES, ONE STRIP. A job that knows its size (a coverage sweep: N runs)\n// gets the determinate bar, the count and the time readout. A job that does\n// not (a publish, a pull, a pack: one write of unknown length) gets an\n// INDETERMINATE band that sweeps the bar, no count, and the elapsed time\n// alone. The host's progress message already says which: `total` 0 is \"no\n// idea yet\" (job.ts), so the strip follows the data and flips to the\n// determinate bar the moment a total arrives. A host that knows its jobs\n// never count pins the mode with `indeterminate: true`; a host whose jobs\n// cannot be stopped mid-write drops Cancel with `cancellable: false` (or by\n// passing no `onCancel` at all).\n//\n// The time readout only appears once there is enough evidence for it. An ETA\n// that swings wildly for the first second reads as a broken app, so the\n// estimate waits until a second of work and a tenth of the job are behind it.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\n\n/** Per-run overrides for a strip that serves several kinds of job (the main\n * window's: a countable export beside an uncountable pull). Each begin()\n * starts from the mount options and applies these on top. */\nexport interface JobBeginOptions {\n /** Pin the band regardless of what the job reports. */\n indeterminate?: boolean;\n /** Show Cancel for this run. */\n cancellable?: boolean;\n}\n\nexport interface JobProgressView {\n /** The strip; mount it where the job belongs. */\n readonly element: HTMLElement;\n /** Show the strip, reset to zero, and (optionally) name what is running. */\n begin(label?: string, run?: JobBeginOptions): void;\n /** Feed it a progress message from the main-side host. A `total` of 0\n * (the host's \"no idea yet\") draws the indeterminate band; the first\n * positive total flips the strip to the determinate bar. */\n update(done: number, total: number, elapsedMs: number): void;\n /** Hide the strip (the job finished, failed, or was cancelled). */\n end(): void;\n readonly visible: boolean;\n /** True while the strip is drawing the band rather than the bar. */\n readonly indeterminate: boolean;\n}\n\nexport interface JobProgressOptions {\n /** Called when Cancel is clicked. The button disables itself and says so;\n * the job stops at its next yield, and the host then calls end(). Omit it\n * for a strip whose jobs cannot be stopped: Cancel is then not drawn. */\n onCancel?: () => void;\n /** The word for the units, for the count readout (default \"runs\"). */\n units?: string;\n /** Always draw the band and never the count, whatever the job reports.\n * Default: follow the data (band until a total arrives). */\n indeterminate?: boolean;\n /** Draw Cancel. Default: only when `onCancel` was given. */\n cancellable?: boolean;\n}\n\nconst clock = (ms: number): string => {\n const total = Math.round(ms / 1000);\n const m = Math.floor(total / 60);\n const s = total % 60;\n return m > 0 ? `${m}m ${String(s).padStart(2, \"0\")}s` : `${s}s`;\n};\n\n/** Enough of the job done, and enough time passed, for a remaining-time\n * estimate to be worth showing rather than misleading. */\nconst estimable = (done: number, total: number, elapsedMs: number): boolean =>\n done > 0 && total > 0 && elapsedMs > 1000 && done / total > 0.1;\n\nexport function mountJobProgress(host: HTMLElement, opts: JobProgressOptions): JobProgressView {\n const units = opts.units ?? \"runs\";\n const pinned = opts.indeterminate === true;\n const cancellableByDefault = opts.cancellable ?? (opts.onCancel !== undefined);\n\n const fill = el(\"i\", { className: \"job-fill\" });\n const bar = el(\"div\", { className: \"job-bar\" }, fill);\n bar.setAttribute(\"role\", \"progressbar\");\n bar.setAttribute(\"aria-valuemin\", \"0\");\n bar.setAttribute(\"aria-valuemax\", \"100\");\n const what = el(\"span\", { className: \"job-label\" });\n const count = el(\"span\", { className: \"job-count\" });\n const time = el(\"span\", { className: \"job-time\" });\n const cancel = el(\"button\", { className: \"job-cancel\", text: \"Cancel\" });\n cancel.type = \"button\";\n\n const strip = el(\"div\", { className: \"job-strip\" }, what, bar, count, time, cancel);\n strip.hidden = true;\n host.append(strip);\n\n // The run's settings: reset by begin(), read by update().\n let runPinned = pinned;\n let indeterminate = false;\n\n /** Draw the band (no count, no value) or the bar (both). Every write is\n * idempotent, so update() may call it each time and only a flip shows. */\n const setMode = (band: boolean): void => {\n indeterminate = band;\n strip.classList.toggle(\"indeterminate\", band);\n count.hidden = band;\n if (band) {\n // The band's width and motion are the stylesheet's: clear the inline\n // width the bar set, or it wins over the class.\n fill.style.width = \"\";\n bar.removeAttribute(\"aria-valuenow\");\n }\n };\n\n const view: JobProgressView = {\n element: strip,\n get visible() { return !strip.hidden; },\n get indeterminate() { return indeterminate; },\n\n begin(label?: string, run?: JobBeginOptions) {\n runPinned = run?.indeterminate ?? pinned;\n const cancellable = run?.cancellable ?? cancellableByDefault;\n what.textContent = label ?? \"Running…\";\n count.textContent = \"\";\n time.textContent = \"\";\n cancel.hidden = !cancellable;\n cancel.disabled = false;\n cancel.textContent = \"Cancel\";\n // Every run opens on the band: nothing is known until the first report.\n setMode(true);\n strip.hidden = false;\n },\n\n update(done: number, total: number, elapsedMs: number) {\n if (strip.hidden) strip.hidden = false;\n const band = runPinned || !(total > 0);\n setMode(band);\n if (!band) {\n const pct = Math.min(100, (done / total) * 100);\n fill.style.width = `${pct}%`;\n bar.setAttribute(\"aria-valuenow\", String(Math.round(pct)));\n count.textContent = `${done} / ${total} ${units}`.trimEnd();\n }\n if (!band && estimable(done, total, elapsedMs)) {\n const remaining = (elapsedMs / done) * (total - done);\n time.textContent = `${clock(elapsedMs)} elapsed, about ${clock(remaining)} left`;\n } else {\n time.textContent = clock(elapsedMs);\n }\n },\n\n end() {\n strip.hidden = true;\n },\n };\n\n cancel.addEventListener(\"click\", () => {\n // Cancellation lands at the job's next yield, which may be a moment away:\n // say so, rather than leaving a dead-looking button.\n cancel.disabled = true;\n cancel.textContent = \"Stopping…\";\n opts.onCancel?.();\n });\n\n // Before the first begin(): the band, and Cancel per the mount options, so\n // a host that only ever calls update() (the strip appearing on the first\n // progress message) still gets a coherent strip.\n cancel.hidden = !cancellableByDefault;\n setMode(true);\n\n return view;\n}\n","// ---------------------------------------------------------------------------\n// The save/dirty controller: the little machine that turns \"the author typed\n// something\" into \"the bytes are on disk\", and says so on screen.\n//\n// Both apps wrote one. Neither is complete, and the two are incomplete in\n// OPPOSITE directions, which is the clearest argument for a shared one that\n// this review has produced:\n//\n// - Patterpad runs an INTERVAL (one timer for the app's life, saving any\n// dirty scene every N seconds). Continuous typing is saved throughout, but\n// a single edit can sit unwritten for the whole period.\n// - Storyletter DEBOUNCES (write 700ms after edits settle). A single edit is\n// written promptly, but an author typing steadily is never written at all\n// until they pause - which on a long paragraph is a long time.\n//\n// So this does both: debounce for promptness, with a MAXIMUM AGE so a run of\n// edits cannot outrun the writer. Neither app had the pair; each had half.\n//\n// The other thing neither had is re-entrancy. Storyletter's flush cleared its\n// pending queue and then awaited the write, so a flush arriving mid-write saw\n// nothing pending and reported \"saved\" while bytes were still in the air. Here\n// a write in flight is a thing you can await, and the status is only \"saved\"\n// when nothing arrived while it was running.\n//\n// WHAT THIS DOES NOT OWN is what a save IS. The controller never touches the\n// document: it calls the app's `write` and reads its boolean. What is pending,\n// how it coalesces, what to repaint afterwards - all of that stays in the app,\n// because all of it is about the app's model.\n//\n// DOM-free, so a test can drive it with fake timers and no window.\n// ---------------------------------------------------------------------------\n\n/**\n * Three states, not a boolean.\n *\n * \"saving\" earns its place: on a slow disk or a slow VC hook a save takes long\n * enough to see, and a status that flips straight from unsaved to saved leaves\n * the author wondering whether the click did anything.\n */\nexport type SaveStatus = \"saved\" | \"unsaved\" | \"saving\";\n\nexport interface SaveControllerOptions {\n /**\n * Write whatever is pending. Return false if it failed: the controller stays\n * \"unsaved\" and will try again on the next touch or flush, rather than\n * quietly reporting success over a write that did not happen.\n *\n * Called only when something is pending, and never twice at once.\n */\n write: () => Promise<boolean> | boolean;\n /** Told every time the status changes, and only when it changes. */\n onStatus?: (status: SaveStatus) => void;\n /** How long after the last edit to write. Storyletter's 700ms by default. */\n delayMs?: number;\n /**\n * The longest an edit may go unwritten while more keep arriving.\n *\n * This is the interval half of the two apps put back in: without it, steady\n * typing defers the write for as long as the typing lasts.\n */\n maxWaitMs?: number;\n}\n\nexport interface SaveController {\n /** What the indicator should say. */\n readonly status: SaveStatus;\n /** Is anything unwritten? For a close guard, or an updater asking whether it\n * may restart. True while a write is in flight, too: it is not on disk yet. */\n readonly pending: boolean;\n /** Something changed. Starts (or extends) the countdown. */\n touch(): void;\n /**\n * Write now, and wait for it.\n *\n * Call on every transition that must see current bytes: switching what is\n * being edited, opening a tool window that reads the disk, closing the\n * window, Cmd+S. Safe to call when nothing is pending (it does nothing) and\n * safe to call during a write (it waits for it, then writes again if more\n * arrived while it ran).\n */\n flush(): Promise<void>;\n /** Give up on what is pending without writing it. For a project close after\n * the author has said to discard, and for teardown. */\n cancel(): void;\n /**\n * Autosave on or off (Patterpad's per-project setting).\n *\n * Off does not mean \"never write\": touch() still marks the work unsaved, and\n * flush() still writes on transitions and on Cmd+S. It only stops the clock.\n */\n setAuto(on: boolean): void;\n}\n\nexport function createSaveController(opts: SaveControllerOptions): SaveController {\n const delayMs = opts.delayMs ?? 700;\n const maxWaitMs = opts.maxWaitMs ?? 5000;\n\n let status: SaveStatus = \"saved\";\n let auto = true;\n let dirty = false;\n /** When the oldest unwritten edit arrived, for the maximum age. */\n let dirtySince = 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n /** The write in flight, so a second flush awaits it instead of racing it. */\n let inFlight: Promise<void> | undefined;\n\n const setStatus = (next: SaveStatus): void => {\n if (next === status) return;\n status = next;\n opts.onStatus?.(status);\n };\n\n const clearTimer = (): void => {\n if (timer === undefined) return;\n clearTimeout(timer);\n timer = undefined;\n };\n\n const arm = (): void => {\n clearTimer();\n if (!auto) return;\n // Never later than maxWaitMs after the first unwritten edit, however much\n // longer the author keeps typing.\n const deadline = dirtySince + maxWaitMs;\n const wait = Math.max(0, Math.min(delayMs, deadline - Date.now()));\n timer = setTimeout(() => { timer = undefined; void run(); }, wait);\n };\n\n const run = async (): Promise<void> => {\n if (inFlight) return inFlight; // one writer at a time\n if (!dirty) { setStatus(\"saved\"); return; }\n clearTimer();\n dirty = false;\n setStatus(\"saving\");\n inFlight = (async () => {\n let ok = false;\n try {\n ok = await opts.write();\n } catch {\n // A thrown write is a failed write. The app has already said whatever\n // it wants to say about it; here it only means \"still unsaved\".\n ok = false;\n }\n if (!ok) {\n // Put the dirt back so the next touch or flush retries, and keep the\n // oldest timestamp: a failing save must not reset the maximum age.\n if (!dirty) { dirty = true; dirtySince = dirtySince || Date.now(); }\n }\n setStatus(dirty ? \"unsaved\" : \"saved\");\n if (dirty) arm(); // more arrived while we wrote, or it failed\n })();\n try {\n await inFlight;\n } finally {\n inFlight = undefined;\n }\n };\n\n return {\n get status() { return status; },\n get pending() { return dirty || inFlight !== undefined; },\n touch() {\n if (!dirty) dirtySince = Date.now();\n dirty = true;\n // An edit arriving mid-write leaves the status alone: a write really is\n // running, and it will settle to \"unsaved\" on its own when it lands.\n if (!inFlight) setStatus(\"unsaved\");\n arm();\n },\n async flush() {\n // Await a write already running, then write again if this call's work (or\n // anything that arrived mid-write) is still outstanding.\n if (inFlight) await inFlight;\n if (dirty) await run();\n else setStatus(\"saved\");\n },\n cancel() {\n clearTimer();\n dirty = false;\n setStatus(inFlight ? \"saving\" : \"saved\");\n },\n setAuto(on) {\n auto = on;\n if (on) { if (dirty) arm(); } else clearTimer();\n },\n };\n}\n","// ---------------------------------------------------------------------------\n// The save indicator: the six lines that draw what `createSaveController`\n// already computes.\n//\n// The controller has been shared since 0.14.0 and its three states with it, but\n// the DRAWING stayed in each app - so both apps hand-rendered the output of one\n// machine and promptly drifted. Patterpad's was `<span id=\"dirty\" hidden>●\n// unsaved</span>`: present only when there was something to worry about.\n// Storyletter's always paints one of Saved / Saving… / Unsaved. (Design review\n// 2026-08, A4.)\n//\n// STORYLETTER'S WON, and the reasoning is worth keeping because it is not\n// \"newer is better\". Three states are what the shared controller models, and an\n// app that autosaves without being asked owes the author the reassurance as\n// well as the warning: a dot that only ever appears to worry you leaves \"did\n// that get written?\" unanswered at exactly the moment it is asked. \"Saving…\"\n// earns its place separately - on a slow disk or a slow version-control hook a\n// write takes long enough to see.\n//\n// It cannot drift again, which is the actual point of putting it here.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport type { SaveStatus } from \"./save.js\";\n\nexport interface SaveIndicator {\n /** Put it in the topbar. */\n readonly el: HTMLElement;\n /** Wire straight to the controller's `onStatus`. */\n set(status: SaveStatus): void;\n}\n\n/** What each state says. Not an option: two apps saying it differently is the\n * drift this exists to end. */\nconst WORDS: Record<SaveStatus, string> = {\n saved: \"Saved\",\n saving: \"Saving…\",\n unsaved: \"Unsaved\",\n};\n\nexport function saveIndicator(initial: SaveStatus = \"saved\"): SaveIndicator {\n const node = el(\"span\");\n const set = (status: SaveStatus): void => {\n node.className = `savestat ${status}`;\n node.textContent = WORDS[status];\n };\n set(initial);\n return { el: node, set };\n}\n","// ---------------------------------------------------------------------------\n// The stale-session bar: \"what you are playing is older than the project\".\n//\n// Both apps grew one without copying the other, and landed within a word of the\n// same sentence - Patterpad's \"Scene changed in the editor: restart to play the\n// new version\", Storyletter's \"The project changed in the editor. Restart to\n// play the new version.\" That is the strongest evidence a thing is shape-level\n// rather than a taste: any app that compiles a document into something you then\n// PLAY has a running session that can fall behind its source, and the only two\n// things it can say are what went out of date and how to start again.\n//\n// So the app supplies the NOUN and the callback, and nothing else. Everything\n// left over - the warn-toned banner, the sentence, the restart glyph - is the\n// shape, and having it in one place is what stops the two apps drifting to two\n// wordings for one situation.\n//\n// WHOSE DRAWING WON, since the two differed here where the words did not.\n// Patterpad's was a line of centred grey text in the choice tray; Storyletter's\n// is a bordered warn-toned bar with the button in it. The bar, because this is\n// a condition rather than a remark: the session is frozen until you act, and a\n// note that reads like the end of a passage does not say so.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\n\nexport interface StaleBarOptions {\n /**\n * What went out of date, as a capitalised noun phrase: \"The project\", \"The\n * scene\". Written into \"<subject> changed in the editor.\"\n */\n subject: string;\n /**\n * The whole first sentence, for when \"changed in the editor\" is not what\n * happened: another project was opened, or the project was closed. Replaces\n * \"<subject> changed in the editor.\"; the advice still follows it.\n */\n message?: string;\n /** Start again from the current source. */\n onRestart: () => void;\n /** The button, when \"Restart\" is not the app's word for it. */\n restartLabel?: string;\n /**\n * The rest of the sentence, for an app whose session is not a PLAY: the\n * default is \"Restart to play the new version.\"\n */\n advice?: string;\n}\n\nexport function staleBar(opts: StaleBarOptions): HTMLElement {\n const label = opts.restartLabel ?? \"Restart\";\n return el(\"div\", { className: \"stale-bar\" },\n el(\"span\", { className: \"stale-bar-msg\",\n text: `${opts.message ?? `${opts.subject} changed in the editor.`} ${opts.advice ?? \"Restart to play the new version.\"}` }),\n // The icon is a node beside the word, not a character inside it; the\n // button's flex gap sets the space (stale.css).\n el(\"button\", { className: \"stale-bar-go\", onClick: () => opts.onRestart() },\n iconNode(\"restart\", 12), label),\n );\n}\n","// ---------------------------------------------------------------------------\n// The toast: a transient remark from the app - \"Bundle published\", \"Save\n// refused: ...\", \"Spell-check is off\" - that shows itself, waits, and leaves.\n//\n// Both apps grew one without copying the other, and drifted on everything a\n// toast can drift on. Patterpad's sat bottom-right, an outlined card on the\n// surface colour, entered and left on the shared panel motion, lingered longer\n// for an error, and announced itself to a screen reader. Storyletter's sat\n// bottom-centre, a solid --danger or --ok block with surface-coloured text, had\n// no motion, one duration, and no ARIA. Same three sentences either side.\n//\n// WHOSE DRAWING WON, and why, since the two differed here in every particular:\n//\n// PLACE: bottom-right. A toast is a REMARK, not a condition (that is the stale\n// bar's job), and a remark must not sit over what the author is reading or\n// typing. Bottom-centre lands on the centre column in the editor and on the\n// choice tray in Play; the corner is out of the reading measure, and it is\n// where the platforms put their own notifications, so the eye already knows\n// to glance there and back.\n//\n// COLOUR: an outlined card on --surface, with the KIND carried by the border\n// and the text colour. A solid block of --danger shouts, and the design\n// language asks for restraint at the seams; the card also keeps its text as\n// --ink, which reads on every palette without each one having to tune a\n// contrast pair for white-on-red.\n//\n// TIME: 4s, 7s for an error. Something went wrong is worth an extra glance.\n//\n// MOTION: the shared panel vocabulary in tokens.css, in and out, so a toast\n// moves the way every other transient surface in the family moves.\n//\n// One toast at a time: a new one replaces the old, never stacks. Rapid events\n// (a burst of saves) would otherwise pile up a column of remarks about one\n// moment. The dismiss checks it is still dismissing the CURRENT toast, so a\n// replacement is never hidden by its predecessor's timer.\n//\n// Mounted by the module, not by host markup: it appends its own node to\n// document.body on first use, so a second window gets the whole thing by\n// importing it (multi-window-rules.md section 1). Its CSS travels beside it.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { closeWithExit } from \"./exit.js\";\n\n/** What the remark is about. `info` is the plain case; `ok` says a thing you\n * asked for happened; `error` says it did not. */\nexport type ToastKind = \"info\" | \"ok\" | \"error\";\n\nexport interface ToastOptions {\n /** How long it stays, in ms, before the exit plays. Defaults to 4000, or\n * 7000 for an error. */\n duration?: number;\n}\n\nconst DURATION: Record<ToastKind, number> = { info: 4000, ok: 4000, error: 7000 };\n\nlet node: HTMLElement | undefined;\nlet timer = 0;\nlet seq = 0;\n\nfunction mount(): HTMLElement {\n if (node && node.isConnected) return node;\n // role=status + aria-live=polite: announced without interrupting, which is\n // exactly a toast's manners.\n node = el(\"div\", { className: \"shell-toast\" });\n node.setAttribute(\"role\", \"status\");\n node.setAttribute(\"aria-live\", \"polite\");\n node.hidden = true;\n document.body.append(node);\n return node;\n}\n\n/**\n * Show a toast. Replaces any toast already showing.\n *\n * `message` is shown as written; a newline breaks the line (\"Bundle\n * published\\n<path>\"), and a long unbroken token such as a path wraps inside\n * the card rather than spilling past it.\n */\nexport function toast(message: string, kind: ToastKind = \"info\", opts: ToastOptions = {}): void {\n const t = mount();\n const id = ++seq;\n t.textContent = message;\n t.className = `shell-toast ${kind}`; // also clears a `.closing` left by a prior dismiss\n t.hidden = false;\n window.clearTimeout(timer);\n timer = window.setTimeout(() => {\n // Only hide if this is still the toast that set the timer: a replacement\n // that arrived meanwhile has its own.\n closeWithExit(t, () => { if (seq === id) t.hidden = true; });\n }, opts.duration ?? DURATION[kind]);\n}\n\n/** Dismiss the current toast now, if any. Plays the exit. */\nexport function dismissToast(): void {\n if (!node || node.hidden) return;\n window.clearTimeout(timer);\n const id = seq;\n const t = node;\n closeWithExit(t, () => { if (seq === id) t.hidden = true; });\n}\n","// ---------------------------------------------------------------------------\n// The stepper bar: a bottom bar that walks you through a list of things to\n// attend to, one at a time.\n//\n// Count, previous, position, next, the current thing as a button, and a slot at\n// the right for whatever acts on it. That is the whole shape, and it is already\n// four bars across two apps: Patterpad's problems bar and its review walk,\n// Storyletter's problems bar and its Review Feedback walk. Patterpad drew its\n// two with parallel class sets (`.problembar-*` and `.reviewbar-*`) for one\n// idea; Storyletter drew its second as the first plus one modifier, which is\n// the better rule and the reason this ended up here (design review 2026-08,\n// section 5 items 2 and 3).\n//\n// NO DOMAIN WORD anywhere in it, which is the test that says it belongs in the\n// shell rather than in either app. It knows about a list, an index, and three\n// pieces of text per entry; it does not know what a problem or a comment is.\n// Severity, resolvedness and quick fixes all arrive as strings and elements the\n// app made.\n//\n// TWO THINGS PATTERPAD'S BARS LACK and gain by adopting this: the position\n// readout (`3/11`, so the walk has a length and an end rather than just a next),\n// and the `where` segment naming what the entry is about before you go there.\n//\n// STEPPING NAVIGATES, or does not: that is the CALLER's decision and this bar\n// takes no view. It reports `onStep` and `onGo` and nothing more. The rule that\n// settles it lives in design-language.md - an ambient surface moves the view and\n// never the focus; a mode you entered may take you somewhere - and the guard for\n// an uncommitted edit belongs to the app, because only the app knows what is\n// unsaved.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\n\nexport interface StepperItem {\n /**\n * One word for what kind of thing this is: \"error\", \"comment\", \"resolved\".\n * Absent when the bar carries one kind of thing only.\n */\n kind?: string;\n /** An extra class on the kind chip, so an app can colour its own kinds\n * (Storyletter's `sev-error`, and `done` for a resolved thread). */\n kindClass?: string;\n /** Where it is. A string is the app's own address, set in the mono face; a\n * Node is a made element, a `breadcrumb([\"Box\", \"Deck\", \"Card\"])` say,\n * drawn as it is. Give the bar plain crumbs: the whole entry is already\n * the button that goes there, so a crumb with its own click has nowhere\n * else to take you. */\n where?: string | Node;\n /** The entry itself, in one line. Overflow is clipped, not wrapped: the bar\n * is one row high and stays that way however long the sentence is. */\n text: string;\n}\n\n/** How loud the count is. The app picks; the palette supplies the value. */\nexport type StepperTone = \"danger\" | \"warn\" | \"accent\" | \"muted\";\n\nexport interface StepperBarOptions {\n items: StepperItem[];\n /** Which one is current. Clamped, so a shrinking list cannot point off the end. */\n at: number;\n tone?: StepperTone;\n /** Go to another index. The bar wraps, so this is always in range. */\n onStep: (next: number) => void;\n /** Take me to what the current entry is about. */\n onGo: (index: number) => void;\n /** What the three controls say they do. Keyboard shortcuts belong in here. */\n tips?: { prev?: string; next?: string; go?: string };\n /**\n * What to say when the list is empty.\n *\n * Absent hides the bar, which is right for an AMBIENT bar: no problems, no\n * problems bar. Present keeps it up, which is right for a MODE: entering the\n * walk and seeing nothing at all reads as a broken command rather than as\n * \"there is no feedback\".\n */\n empty?: string;\n /** Leave the mode. Drawn as the last control, hard right. */\n onClose?: () => void;\n closeTip?: string;\n /** The right-hand end before the close: a quick fix, say. Nulls are skipped,\n * so a caller can pass a conditional straight in. */\n actions?: (HTMLElement | null | undefined)[];\n}\n\nconst STEP_ICON = 12;\n\n/** Draw (or hide) the bar in `host`. Idempotent: call it again with new state. */\nexport function renderStepperBar(host: HTMLElement, opts: StepperBarOptions): void {\n const { items } = opts;\n // 12px inside the bar's 20px control: the bar is set at 0.8rem.\n const close = opts.onClose === undefined ? null : el(\"button\", {\n className: \"stepbar-nav stepbar-close\",\n tip: opts.closeTip ?? \"Close\", onClick: opts.onClose,\n }, iconNode(\"close\", STEP_ICON));\n const actions = (opts.actions ?? []).filter((a): a is HTMLElement => a !== null && a !== undefined);\n\n if (items.length === 0) {\n if (opts.empty === undefined) { host.hidden = true; host.replaceChildren(); return; }\n host.hidden = false;\n host.replaceChildren(\n el(\"span\", { className: \"stepbar-count muted\", text: \"0\" }),\n el(\"span\", { className: \"stepbar-empty\", text: opts.empty }),\n ...actions, ...(close ? [close] : []),\n );\n return;\n }\n\n host.hidden = false;\n const index = Math.min(Math.max(opts.at, 0), items.length - 1);\n const item = items[index]!;\n const step = (delta: number): void => opts.onStep((index + delta + items.length) % items.length);\n\n // The steppers are absent for a list of one: two arrows that both come back\n // to where you are is furniture pretending to be a control.\n const nav = items.length > 1\n ? [\n el(\"button\", { className: \"stepbar-nav\",\n tip: opts.tips?.prev ?? \"Previous\", onClick: () => step(-1) }, iconNode(\"back\", STEP_ICON)),\n el(\"span\", { className: \"stepbar-of\", text: `${index + 1}/${items.length}` }),\n el(\"button\", { className: \"stepbar-nav\",\n tip: opts.tips?.next ?? \"Next\", onClick: () => step(1) }, iconNode(\"forward\", STEP_ICON)),\n ]\n : [];\n\n host.replaceChildren(\n el(\"span\", { className: `stepbar-count ${opts.tone ?? \"muted\"}`, text: String(items.length) }),\n ...nav,\n el(\"button\", { className: \"stepbar-cur\", tip: opts.tips?.go ?? \"Go to this\", onClick: () => opts.onGo(index) },\n item.kind === undefined ? null\n : el(\"span\", { className: `stepbar-cat${item.kindClass ? ` ${item.kindClass}` : \"\"}`, text: item.kind }),\n item.where === undefined ? null\n : typeof item.where === \"string\" ? el(\"span\", { className: \"stepbar-where\", text: item.where })\n : el(\"span\", \"stepbar-where\", item.where),\n el(\"span\", { className: \"stepbar-msg\", text: item.text }),\n ),\n ...actions, ...(close ? [close] : []),\n );\n}\n","// ---------------------------------------------------------------------------\n// Navigation history - the VS Code back/forward pair, family-wide.\n//\n// The shell owns the STACK and the ARROWS; each app owns what a place is.\n// `Place` is opaque here for the same reason the app-store's `places` is:\n// \"where you are\" means a focus + document + tab in Storyletter and a scene +\n// position in Patterpad, and the shell has no business knowing either. The app\n// supplies two judgements - `same` (two places that are the same DOCUMENT, so\n// tab and selection twitches coalesce instead of filling the stack) and\n// `usable` (a place can still be restored: the card it names may have been\n// deleted since) - and the mechanics cannot drift between the apps.\n//\n// The model is the browser's: `visit` records the place being LEFT on an\n// ordinary navigation and clears the forward list (navigating anew after\n// going back branches history, and the abandoned branch is gone); `back` and\n// `forward` exchange the current place for the adjacent one. Entries that\n// fail `usable` are DISCARDED on the way past, never restored and never moved\n// to the other side.\n//\n// One duty stays with the app, documented rather than latched: restoring a\n// place re-dispatches the app's own navigation actions, and those call\n// `visit`. The app must not `visit` while it is travelling (Storyletter's\n// `arriving` flag is the local idiom), or every Back would push what it just\n// left and Forward would never reach anything.\n// ---------------------------------------------------------------------------\n\nimport { iconNode, type IconName } from \"./icons.js\";\n\nexport interface NavHistoryOptions<Place> {\n /** Same DOCUMENT: a visit whose place is `same` as the newest entry\n * REPLACES it (keeping the freshest tab) instead of stacking. */\n same(a: Place, b: Place): boolean;\n /** Still restorable? Checked as an entry is stepped onto; a stale one is\n * dropped and the step continues past it. Default: everything is. */\n usable?(p: Place): boolean;\n /** Entries beyond this age off the far end (default 100). */\n cap?: number;\n}\n\nexport interface NavHistory<Place> {\n /** Record the place being left, before an ordinary navigation. */\n visit(place: Place): void;\n /** Step back: hands over the current place (it becomes the forward entry)\n * and returns the place to restore, or undefined at the far end. */\n back(current: Place): Place | undefined;\n forward(current: Place): Place | undefined;\n /** Whether the arrows light. Optimistic: a side holding only stale entries\n * still reports true, and the press quietly discards them (the alternative\n * is validating the whole stack on every paint). */\n canBack(): boolean;\n canForward(): boolean;\n /** Forget both sides, for when the places stop meaning anything: a\n * different project opened, or the welcome screen with none. Without it\n * Back walks into the project just closed (Patterpad, 2026-10-07: on the\n * welcome screen the arrows looked live and did nothing). The caller\n * re-`set`s its arrows afterwards, as after any other change. */\n clear(): void;\n}\n\nexport function createNavHistory<Place>(opts: NavHistoryOptions<Place>): NavHistory<Place> {\n const cap = opts.cap ?? 100;\n const usable = opts.usable ?? ((): boolean => true);\n let past: Place[] = [];\n let ahead: Place[] = [];\n\n const step = (from: Place[], to: Place[], current: Place): Place | undefined => {\n for (;;) {\n const p = from.pop();\n if (p === undefined) return undefined;\n if (!usable(p)) continue; // its document is gone: drop, keep walking\n if (opts.same(p, current)) continue; // a step must MOVE: a self-visit (a\n // tab click routed through a navigation action) is not a journey, and\n // restoring it reads as a dead button\n to.push(current);\n return p;\n }\n };\n\n return {\n visit(place) {\n ahead = [];\n const top = past[past.length - 1];\n if (top !== undefined && opts.same(top, place)) past[past.length - 1] = place;\n else past.push(place);\n if (past.length > cap) past = past.slice(past.length - cap);\n },\n back(current) { return step(past, ahead, current); },\n forward(current) { return step(ahead, past, current); },\n canBack() { return past.length > 0; },\n canForward() { return ahead.length > 0; },\n clear() { past = []; ahead = []; },\n };\n}\n\n/**\n * The quiet arrow pair, VS Code's shape: greyed where there is nowhere to\n * go, one component so it looks and sits the same in every app of the\n * family; the caller re-`set`s it whenever a navigation may have changed\n * what the arrows can do.\n *\n * ARROWS, not chevrons, and the distinction is the family's glyph grammar\n * (the author's ruling, 2026-08-28): a chevron says STRUCTURE - the pane\n * toggles, a labelled up-crumb, a tree row's disclosure - and an arrow says\n * TIME. The first cut used chevrons, and sitting on the topbar two seats\n * from the pane toggle's chevron the pair read as more of the same thing;\n * it is how every browser, editor and file manager already split the two.\n *\n * The pair's HOME is the topbar's lead, immediately after the nav toggle\n * (Patterpad placed it there first, and the ruling ratified it).\n */\nexport function historyNav(onBack: () => void, onForward: () => void): {\n el: HTMLElement;\n set(canBack: boolean, canForward: boolean): void;\n} {\n const btn = (name: IconName, tip: string, onClick: () => void): HTMLButtonElement => {\n const b = document.createElement(\"button\");\n b.type = \"button\";\n b.className = \"shell-histnav-btn\";\n b.append(iconNode(name));\n b.dataset[\"tip\"] = tip;\n b.setAttribute(\"aria-label\", tip);\n b.addEventListener(\"click\", onClick);\n return b;\n };\n const back = btn(\"arrowLeft\", \"Back\", onBack);\n const forward = btn(\"arrowRight\", \"Forward\", onForward);\n const el = document.createElement(\"span\");\n el.className = \"shell-histnav\";\n el.append(back, forward);\n return {\n el,\n set(canBack, canForward) { back.disabled = !canBack; forward.disabled = !canForward; },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/colour.ts","../src/pane-shell.ts","../src/tool-window-web.ts","../src/confirm.ts","../src/about.ts","../src/anchored.ts","../src/ids.ts","../src/id-editor.ts","../src/vc-view.ts","../src/identity.ts","../src/notes-editor.ts","../src/comments.ts","../src/job-view.ts","../src/save.ts","../src/save-view.ts","../src/stale.ts","../src/toast.ts","../src/stepper.ts","../src/nav-history.ts"],"names":["el","label","thread","timer","node","current"],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The per-entity colour model (design-language.md section 4): a stable hash of\n// a name selects an INDEX into the curated palette - repeatable, storage-free.\n// The hues live in the token layer as `--char-0 .. --char-(N-1)` per theme, so\n// a colour reference adapts to light/dark with zero JS. Each app defines the\n// `--char-*` values in its own palette; this module only picks the slot.\n//\n// Storylet Studio uses it for dimension-value / zone chips; Patterpad for\n// character cues.\n// ---------------------------------------------------------------------------\n\n/** Curated colour slots; must match the `--char-*` set defined per theme. */\nexport const PALETTE_SIZE = 12;\n\nexport const PALETTE = Array.from({ length: PALETTE_SIZE }, (_, i) => `var(--char-${i})`) as readonly string[];\n\n/** Hash a name to a palette slot: FNV-1a + an fmix32 avalanche, well\n * distributed even for short, similar names. */\nexport function colourIndex(name: string): number {\n let h = 0x811c9dc5;\n for (let i = 0; i < name.length; i++) { h ^= name.charCodeAt(i); h = Math.imul(h, 0x01000193); }\n h ^= h >>> 16; h = Math.imul(h, 0x85ebca6b); h ^= h >>> 13; h = Math.imul(h, 0xc2b2ae35); h ^= h >>> 16;\n return (h >>> 0) % PALETTE_SIZE;\n}\n\n/** The theme-aware CSS variable reference for a name's slot. */\nexport function colourFor(name: string): string {\n return PALETTE[colourIndex(name)]!;\n}\n","// ---------------------------------------------------------------------------\n// mountPaneShell: the app-frame both editors share - a top bar with compact\n// pane toggles, a nav | centre | inspector grid whose side panes collapse and\n// resize, and width/open-state persistence via a host callback. The host fills\n// the pane bodies + top-bar slots and owns its own keyboard accelerators\n// (call togglePane); the frame owns layout, collapse, drag-resize, and state.\n//\n// The frame CSS ships alongside as pane-shell.css (import it once at the app\n// root). It reads the shared token grammar (tokens.css).\n// ---------------------------------------------------------------------------\n\nimport { iconNode } from \"./icons.js\";\nimport { isKeyCombo, tipWithKey } from \"./keys.js\";\nimport { ensureTooltipHost } from \"./tooltip.js\";\n\nexport type PaneSide = \"nav\" | \"inspector\";\n\nexport interface PaneSideConfig {\n /** The open width as a CSS length, e.g. \"14rem\" or \"224px\". */\n defaultWidth: string;\n /** Drag floor in px (default 160). */\n minWidth?: number;\n /** Drag ceiling in px from the window width (default w => min(640, w - 420)). */\n maxWidth?: (innerWidth: number) => number;\n /** Whether a drag seam is offered (default true). */\n resizable?: boolean;\n /** Noun for the toggle's tooltip/aria, e.g. \"navigator\" / \"inspector\". */\n label?: string;\n /** The key that toggles the pane, bracketed after the tooltip. A combo in\n * the portable spelling (\"Mod+1\") is drawn for the platform through\n * `tipWithKey`: \"(⌘1)\" on a Mac, \"(Ctrl+1)\" elsewhere. Anything else is a\n * literal and goes in verbatim, as it always has (\"Cmd+1\" stays \"Cmd+1\"). */\n shortcutHint?: string;\n /** false = the pane is not offered at all: no toggle, togglePane ignored,\n * forced closed whatever the remembered state says. For a slot a host has\n * retired but keeps mounted (Storyletter's inspector) - the shell used to\n * offer a chevron that opened a paneful of nothing. Default true. */\n offered?: boolean;\n}\n\nexport interface PaneShellState {\n open: Record<PaneSide, boolean>;\n /** Dragged widths in px; absent = the config default. */\n width: Partial<Record<PaneSide, number>>;\n}\n\nexport interface PaneShellOptions {\n nav: PaneSideConfig;\n inspector: PaneSideConfig;\n /** Initial open + width state (from the host's store). */\n initial?: Partial<PaneShellState>;\n /** Persist state after any toggle or resize (host wires it to its store). */\n onChange?: (state: PaneShellState) => void;\n /** The toggle's rollover for a side in a given state. Default: \"Show\n * navigator\" / \"Hide navigator\" from the side's `label`, with the\n * `shortcutHint` in brackets (platform-true when it is a combo). Written as\n * `data-tip` (the themed tooltip) and as the accessible name; never as\n * `title`. */\n tipFor?: (side: PaneSide, open: boolean) => string;\n /** An extra class per side, toggled on the `.panes` grid whenever that side\n * is collapsed, beside the shell's own `no-nav` / `no-inspector`. For an\n * app whose stylesheet already keys on its own name for the state\n * (Patterpad's `props-doc-open`), so its rules ride the shell's collapse\n * rather than a second copy of it. */\n collapseAlso?: Partial<Record<PaneSide, string>>;\n}\n\nexport interface PaneShell {\n /** The frame element (top bar + panes); mount it into the app container. */\n readonly root: HTMLElement;\n readonly topbar: HTMLElement;\n /** Top-bar host slots: leading (title) and trailing (status), the host fills. */\n readonly topbarLead: HTMLElement;\n readonly topbarTrail: HTMLElement;\n /** Pane bodies the host fills (the fixed-width inner that clips on collapse). */\n readonly nav: HTMLElement;\n readonly centre: HTMLElement;\n readonly inspector: HTMLElement;\n togglePane(side: PaneSide): void;\n setPaneOpen(side: PaneSide, open: boolean): void;\n /** Whether the pane is showing: open, and not held closed. */\n isOpen(side: PaneSide): boolean;\n /** Fold a side away WITHOUT changing what the person chose: a mode that has\n * no use for the inspector (Patterpad's properties page) holds it closed,\n * and releasing the hold restores whatever was remembered. Not persisted;\n * the toggle keeps reporting the remembered state. */\n holdClosed(side: PaneSide, held: boolean): void;\n /** Clear dragged widths back to the config defaults. */\n resetWidths(): void;\n /** A snapshot of the current state (open + widths). */\n state(): PaneShellState;\n destroy(): void;\n}\n\nconst DEFAULT_MAX = (w: number): number => Math.min(640, w - 420);\nconst openVar: Record<PaneSide, string> = { nav: \"--nav-open-w\", inspector: \"--insp-open-w\" };\nconst collapseClass: Record<PaneSide, string> = { nav: \"no-nav\", inspector: \"no-inspector\" };\n\nexport function mountPaneShell(host: HTMLElement, opts: PaneShellOptions): PaneShell {\n const cfg: Record<PaneSide, PaneSideConfig> = { nav: opts.nav, inspector: opts.inspector };\n const offered = (side: PaneSide): boolean => cfg[side].offered !== false;\n const open: Record<PaneSide, boolean> = {\n nav: offered(\"nav\") && (opts.initial?.open?.nav ?? true),\n inspector: offered(\"inspector\") && (opts.initial?.open?.inspector ?? true),\n };\n const width: Partial<Record<PaneSide, number>> = { ...(opts.initial?.width ?? {}) };\n const held: Record<PaneSide, boolean> = { nav: false, inspector: false };\n const showing = (side: PaneSide): boolean => open[side] && !held[side];\n\n const el = <K extends keyof HTMLElementTagNameMap>(tag: K, cls?: string): HTMLElementTagNameMap[K] => {\n const n = document.createElement(tag);\n if (cls) n.className = cls;\n return n;\n };\n\n // --- top bar ---------------------------------------------------------------\n const navToggle = el(\"button\", \"pane-toggle\");\n navToggle.type = \"button\";\n const inspToggle = el(\"button\", \"pane-toggle\");\n inspToggle.type = \"button\";\n const topbarLead = el(\"div\", \"topbar-lead\");\n const topbarTrail = el(\"div\", \"topbar-trail\");\n const spacer = el(\"span\", \"topbar-spacer\");\n const topbar = el(\"header\", \"topbar\");\n topbar.append(navToggle, topbarLead, spacer, topbarTrail, inspToggle);\n\n // --- panes -----------------------------------------------------------------\n const panes = el(\"main\", \"panes\");\n const navPane = el(\"nav\", \"pane pane-nav\");\n const navInner = el(\"div\", \"pane-inner\");\n navPane.append(navInner);\n const centre = el(\"section\", \"pane-centre\");\n const inspPane = el(\"aside\", \"pane pane-inspector\");\n const inspInner = el(\"div\", \"pane-inner\");\n inspPane.append(inspInner);\n const navResizer = el(\"div\", \"pane-resizer pane-resizer-nav\");\n const inspResizer = el(\"div\", \"pane-resizer pane-resizer-insp\");\n panes.append(navPane, centre, inspPane, navResizer, inspResizer);\n\n const root = el(\"div\", \"pane-shell\");\n root.append(topbar, panes);\n host.append(root);\n // An unoffered pane's toggle is not hidden but ABSENT: nothing to\n // discover, nothing to tab to.\n if (!offered(\"nav\")) navToggle.remove();\n if (!offered(\"inspector\")) inspToggle.remove();\n\n // --- state application -----------------------------------------------------\n const defaultTip = (side: PaneSide, isOpen: boolean): string => {\n const text = `${isOpen ? \"Hide\" : \"Show\"} ${cfg[side].label ?? side}`;\n const hint = cfg[side].shortcutHint;\n if (!hint) return text;\n // A portable combo is spelled for the platform at draw time; a literal\n // is the caller's own words and goes in as written.\n return isKeyCombo(hint) ? tipWithKey(text, hint) : `${text} (${hint})`;\n };\n const tipFor = opts.tipFor ?? defaultTip;\n\n function applyToggleGlyph(side: PaneSide, button: HTMLButtonElement): void {\n const isOpen = open[side];\n // Chevron points toward where the pane collapses to.\n button.replaceChildren(iconNode(side === \"nav\" ? (isOpen ? \"back\" : \"forward\") : (isOpen ? \"forward\" : \"back\")));\n // `data-tip`, not `title`: the themed tooltip, never the OS bubble on the\n // platform's own delay. The tip is also the accessible name, since the\n // button has no text of its own.\n const tip = tipFor(side, isOpen);\n button.dataset[\"tip\"] = tip;\n button.setAttribute(\"aria-label\", tip);\n button.setAttribute(\"aria-pressed\", String(isOpen));\n }\n // Writing data-tip mounts the renderer, as `el` does, so a window whose host\n // never called initTooltips() still draws the rollover.\n ensureTooltipHost();\n\n function apply(): void {\n for (const side of [\"nav\", \"inspector\"] as const) {\n const collapsed = !showing(side);\n panes.classList.toggle(collapseClass[side], collapsed);\n const also = opts.collapseAlso?.[side];\n if (also) panes.classList.toggle(also, collapsed);\n }\n // The open widths live on the grid: the track and each .pane-inner read them.\n panes.style.setProperty(openVar.nav, width.nav !== undefined ? `${width.nav}px` : cfg.nav.defaultWidth);\n panes.style.setProperty(openVar.inspector, width.inspector !== undefined ? `${width.inspector}px` : cfg.inspector.defaultWidth);\n applyToggleGlyph(\"nav\", navToggle);\n applyToggleGlyph(\"inspector\", inspToggle);\n navResizer.style.display = cfg.nav.resizable === false || !offered(\"nav\") ? \"none\" : \"\";\n inspResizer.style.display = cfg.inspector.resizable === false || !offered(\"inspector\") ? \"none\" : \"\";\n }\n\n const snapshot = (): PaneShellState => ({ open: { ...open }, width: { ...width } });\n const persist = (): void => opts.onChange?.(snapshot());\n\n function setPaneOpen(side: PaneSide, next: boolean): void {\n if (open[side] === next) return;\n open[side] = next;\n apply();\n persist();\n }\n function togglePane(side: PaneSide): void { if (offered(side)) setPaneOpen(side, !open[side]); }\n function holdClosed(side: PaneSide, next: boolean): void {\n if (held[side] === next) return;\n held[side] = next;\n apply(); // not persisted: the hold is the mode's, the remembered state is the person's\n }\n function resetWidths(): void { delete width.nav; delete width.inspector; apply(); persist(); }\n\n navToggle.addEventListener(\"click\", () => togglePane(\"nav\"));\n inspToggle.addEventListener(\"click\", () => togglePane(\"inspector\"));\n\n // --- drag resize -----------------------------------------------------------\n function beginResize(side: PaneSide, startEvent: PointerEvent): void {\n if (cfg[side].resizable === false || !showing(side)) return;\n startEvent.preventDefault();\n const pane = side === \"nav\" ? navPane : inspPane;\n const seam = side === \"nav\" ? navResizer : inspResizer;\n seam.classList.add(\"dragging\");\n const min = cfg[side].minWidth ?? 160;\n const max = cfg[side].maxWidth ?? DEFAULT_MAX;\n const startX = startEvent.clientX;\n const startW = pane.getBoundingClientRect().width;\n document.body.classList.add(\"pane-resizing\");\n let latest = startW;\n const onMove = (e: PointerEvent): void => {\n const delta = side === \"nav\" ? e.clientX - startX : startX - e.clientX;\n latest = Math.max(min, Math.min(max(window.innerWidth), startW + delta));\n panes.style.setProperty(openVar[side], `${latest}px`);\n };\n const onUp = (): void => {\n window.removeEventListener(\"pointermove\", onMove);\n window.removeEventListener(\"pointerup\", onUp);\n document.body.classList.remove(\"pane-resizing\");\n seam.classList.remove(\"dragging\");\n width[side] = latest;\n persist();\n };\n window.addEventListener(\"pointermove\", onMove);\n window.addEventListener(\"pointerup\", onUp);\n }\n navResizer.addEventListener(\"pointerdown\", (e) => beginResize(\"nav\", e));\n inspResizer.addEventListener(\"pointerdown\", (e) => beginResize(\"inspector\", e));\n navResizer.addEventListener(\"dblclick\", () => { delete width.nav; apply(); persist(); });\n inspResizer.addEventListener(\"dblclick\", () => { delete width.inspector; apply(); persist(); });\n\n apply();\n\n return {\n root, topbar, topbarLead, topbarTrail,\n nav: navInner, centre, inspector: inspInner,\n togglePane, setPaneOpen, isOpen: showing, holdClosed, resetWidths, state: snapshot,\n destroy: () => { root.remove(); },\n };\n}\n","// ---------------------------------------------------------------------------\n// The tool-window kit, RENDERER half: the always-on-top pin button every\n// helper window carries (previously pasted verbatim in five places across\n// the two apps). The matching chrome CSS - .swin-head drag bar, .swin-pin,\n// .swin-close - ships as tool-window.css.\n//\n// The head bar (toolWindowHead) and the follow toggle (followButton) live here\n// too since 0.39. The frameless drag bar itself needs no JS at all: it is markup + the\n// -webkit-app-region rules in tool-window.css.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\nimport { isEditableTarget } from \"./util.js\";\n\nexport interface PinButtonOptions {\n pinned: boolean;\n /** Persist the choice. The button reflects the click itself. */\n onToggle: (on: boolean) => void;\n}\n\n/** A pin and the handle to drive it, the same shape `saveIndicator` returns. */\nexport interface PinButton {\n el: HTMLButtonElement;\n /**\n * Reflect a pin state decided ELSEWHERE, without calling `onToggle`.\n *\n * The state is not always the button's to choose: main re-pins a helper window\n * on Reset View, and the window is told after the fact. Before this existed a\n * host had to reach in and set the class, the aria and the title by hand,\n * which is three chances to disagree with the button about what it is showing.\n */\n set: (pinned: boolean) => void;\n}\n\n/** The always-on-top pin: `.swin-pin`, aria-pressed, and a tooltip that says\n * what a click will do (the richest of the family's variants, kept). */\nexport function pinButton(opts: PinButtonOptions): PinButton {\n const b = el(\"button\", \"swin-pin\");\n b.type = \"button\";\n // The vocabulary's pin (it used to be a hand-drawn copy of the same shape,\n // pasted in five places across the two apps before it came here).\n b.append(iconNode(\"pin\"));\n let pinned = opts.pinned;\n const reflect = (): void => {\n b.classList.toggle(\"on\", pinned);\n b.setAttribute(\"aria-pressed\", String(pinned));\n // The THEMED rollover, not the platform's. 0.21.0 swept `title` out of this\n // package and missed this one, so every helper window in the family had a\n // single control waiting on the OS delay and then drawing the OS bubble,\n // sitting beside controls that did not. `title` was also the button's only\n // accessible name (the glyph is an aria-hidden SVG), so the label has to go\n // to `aria-label` as it moves, or dropping `title` would take the name too.\n const label = pinned ? \"Pinned on top. Click to unpin.\" : \"Keep on top\";\n b.dataset[\"tip\"] = label;\n b.setAttribute(\"aria-label\", label);\n };\n reflect();\n b.addEventListener(\"click\", () => { pinned = !pinned; reflect(); opts.onToggle(pinned); });\n return { el: b, set: (next: boolean) => { pinned = next; reflect(); } };\n}\n\n// --- the head ------------------------------------------------------------------\n\nexport interface ToolWindowHeadOptions {\n /** The window's own name. Constant: it says which window this is, so it must\n * not change as projects open and close. Omitted by a window whose mode\n * tabs stand where the title would be (Find): pass them as `tabs`. */\n title?: string;\n /** Mode tabs in the title's place (Find's bar: modes left, pin and close right). */\n tabs?: HTMLElement;\n /** A pin BUILT ALREADY, for a window that mounts its chrome once and drives\n * the button with `pin.set(on)` rather than rebuilding the bar. That is the\n * better shape (no re-entrancy question, survives a partial repaint), so the\n * helper takes either. */\n pin?: { el: HTMLElement };\n pinned?: boolean;\n onPin?: (on: boolean) => void;\n onClose: () => void;\n /** Between the title and the spacer: a subtitle, the project name. */\n lead?: (Node | string | null)[];\n /** Between the spacer and the pin: this window's own controls. */\n trail?: (Node | string | null)[];\n /** An extra class beside `swin-head`, for a window that styles its own bar. */\n className?: string;\n /**\n * Escape closes the window (default true): the family's rule, written once\n * here rather than per window.\n *\n * The listener stands aside on its own for a field's Escape (an input,\n * textarea, select or contenteditable target: clearing, cancelling an edit)\n * and for an Escape a deeper handler has already `preventDefault()`ed.\n * `\"always\"` drops the first of those: a window whose focus LIVES in a\n * field (Find, whose query box is where the typing is) still closes on\n * Escape from it. `false` turns the listener off for a window that owns\n * Escape entirely; it calls `onClose` itself when nothing smaller is left.\n */\n esc?: boolean | \"always\";\n /**\n * A window that layers Escape (a selection to clear, a pending outcome to\n * drop, a sweep that must not be closed out from under) says so here:\n * return true when the key was consumed and the window stays open, false\n * to let it close. Innermost first; the window is last.\n */\n onEscape?: () => boolean;\n}\n\ninterface EscSlot {\n onClose: () => void;\n onEscape?: (() => boolean) | undefined;\n fromFields: boolean;\n}\n\n/** The one Escape listener per window. The head may be rebuilt on every render\n * (a play surface does), so the listener is installed once and reads the\n * latest close through this slot. */\nlet escSlot: EscSlot | undefined;\nlet escWired = false;\nfunction wireEsc(): void {\n if (escWired) return;\n escWired = true;\n window.addEventListener(\"keydown\", (e) => {\n if (e.key !== \"Escape\" || !escSlot) return;\n // A deeper handler that claimed the key (a popover closing, a picker\n // dismissing) has said so already.\n if (e.defaultPrevented) return;\n // A field's own Escape (clearing, cancelling an edit) comes first, unless\n // the window asked to close from its fields too.\n if (!escSlot.fromFields && isEditableTarget(e.target)) return;\n // The window's own layers, innermost first; true means one of them took it.\n if (escSlot.onEscape?.() === true) return;\n escSlot.onClose();\n });\n}\n\n/** The `.swin-head` bar every tool window wears: title (or tabs), lead, spacer,\n * trail, pin, close. The close button says \"Close (Esc)\" once, here, rather\n * than in every window. */\nexport function toolWindowHead(opts: ToolWindowHeadOptions): HTMLElement {\n const keep = (parts: (Node | string | null)[] | undefined): (Node | string)[] =>\n (parts ?? []).filter((p): p is Node | string => p !== null);\n const pin = opts.pin\n ?? pinButton({ pinned: opts.pinned ?? true, onToggle: opts.onPin ?? (() => {}) });\n if (opts.esc !== false) {\n escSlot = { onClose: opts.onClose, onEscape: opts.onEscape, fromFields: opts.esc === \"always\" };\n wireEsc();\n }\n return el(\"header\", { className: `swin-head${opts.className ? ` ${opts.className}` : \"\"}` },\n ...(opts.title !== undefined ? [el(\"span\", { className: \"swin-title\", text: opts.title })] : []),\n ...(opts.tabs ? [opts.tabs] : []),\n ...keep(opts.lead),\n el(\"span\", { className: \"swin-spacer\" }),\n ...keep(opts.trail),\n pin.el,\n el(\"button\", { className: \"swin-close\", tip: \"Close (Esc)\", onClick: opts.onClose }, iconNode(\"close\")),\n );\n}\n\n// --- follow in the editor -------------------------------------------------------\n\nexport interface FollowButtonOptions {\n on: boolean;\n /** Persist the choice. The button reflects the click itself. */\n onToggle: (on: boolean) => void;\n /** What the editor opens as the run goes: \"line\" (Patterpad), \"card\"\n * (Storyletter). Default \"line\". */\n what?: string;\n}\n\n/** A toggle and the handle to drive it, the same shape `pinButton` returns. */\nexport interface FollowButton {\n el: HTMLButtonElement;\n /** Reflect a state decided elsewhere, without calling `onToggle`. */\n set: (on: boolean) => void;\n}\n\n/**\n * \"Follow in the editor\": the play surface's toggle that makes the EDITOR\n * follow the run, opening each line / card as it plays. Both play surfaces\n * built one (patterpad play.ts, studio table.ts) with the same label and\n * paraphrased tips; this is the one wording.\n *\n * The LABEL is the careful part. \"Follow\" alone would match the vocabulary of\n * a window that follows the editor; this is the other direction, the editor\n * following the run. Off by default and remembered by the host: following is\n * the author asking for it.\n */\nexport function followButton(opts: FollowButtonOptions): FollowButton {\n const what = opts.what ?? \"line\";\n const b = el(\"button\", \"btn swin-follow\", \"Follow in the editor\");\n b.type = \"button\";\n let on = opts.on;\n const reflect = (): void => {\n b.classList.toggle(\"on\", on);\n b.setAttribute(\"aria-pressed\", String(on));\n const label = on\n ? `The editor opens each ${what} as it plays. Click to stop.`\n : `Open each ${what} in the editor as it plays`;\n b.dataset[\"tip\"] = label;\n };\n reflect();\n b.addEventListener(\"click\", () => { on = !on; reflect(); opts.onToggle(on); });\n return { el: b, set: (next: boolean) => { on = next; reflect(); } };\n}\n\nexport interface SelectionFollowButtonOptions {\n on: boolean;\n /** Persist the choice. The button reflects the click itself. */\n onToggle: (on: boolean) => void;\n /** What the window stays on when it stops following: \"line\" (Patterpad), \"card\" (Storyletter). */\n what?: string;\n}\n\n/**\n * \"Follow selection\": the other direction from `followButton`. A helper window\n * that shows whatever the editor has selected (Patterpad's fields window, after\n * Pattershot's brief H1) moves along as the author moves; off, it stays on the\n * node it shows. On by default in such a window: following is what it is for.\n */\nexport function selectionFollowButton(opts: SelectionFollowButtonOptions): FollowButton {\n const what = opts.what ?? \"line\";\n const b = el(\"button\", \"btn swin-follow\", \"Follow selection\");\n b.type = \"button\";\n let on = opts.on;\n const reflect = (): void => {\n b.classList.toggle(\"on\", on);\n b.setAttribute(\"aria-pressed\", String(on));\n b.dataset[\"tip\"] = on\n ? `Shows whatever is selected in the editor. Click to stay on this ${what}.`\n : `Stays on this ${what}. Click to follow the selection.`;\n };\n reflect();\n b.addEventListener(\"click\", () => { on = !on; reflect(); opts.onToggle(on); });\n return { el: b, set: (next: boolean) => { on = next; reflect(); } };\n}\n","// ---------------------------------------------------------------------------\n// A themed in-app confirmation modal (design-language \"coherent to the\n// edges\": never a stock OS dialog). The promise API is Patterpad's surface\n// confirm; the build sits on the family's dialog frame (dialog.ts), so the\n// focus trap, Esc, the scrim and the exit motion are the frame's. Two\n// deliberate corrections over that original, kept when Patterpad migrates\n// here: the destructive button wears --danger (a required host token; the\n// surface one mistakenly used --accent), and initial focus lands on Cancel so\n// a stray Enter cannot destroy anything.\n//\n// Styles: dialog.css + controls.css for the frame and the buttons, confirm.css\n// for what is confirm's own (its width, its body copy). Callers keep the\n// elide-the-dialog rule: when nothing is at stake, don't ask.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\nexport interface ConfirmOptions {\n title: string;\n /** One plain sentence, ending with what undo does. */\n body?: string;\n /**\n * Content that is evidence rather than a sentence: a list of the scenes that\n * refer to the thing, a warning line. Rendered after `body` when both are\n * given. The caller styles it; the frame's body is a column with a gap.\n */\n bodyNode?: HTMLElement;\n /** The destructive button's label, which names its object (\"Delete scene\",\n * \"Remove language\"). */\n confirmLabel: string;\n}\n\n/** Resolves true on confirm; false on Cancel / Esc / backdrop. */\nexport function confirmDialog(opts: ConfirmOptions): Promise<boolean> {\n return new Promise((resolve) => {\n let done = false;\n // Resolve on the gesture, not after the exit motion: the caller acts on the\n // answer at once, and the fade plays over whatever it does.\n const finish = (ok: boolean): void => {\n if (done) return;\n done = true;\n resolve(ok);\n frame.close();\n };\n const frame = dialogFrame({\n title: opts.title, className: \"confirm-dialog\",\n onClose: () => finish(false), // Esc, or closed by someone else\n });\n const cancel = el(\"button\", \"btn confirm-btn cancel\", \"Cancel\");\n const danger = el(\"button\", \"btn danger confirm-btn\", opts.confirmLabel);\n cancel.type = \"button\";\n danger.type = \"button\";\n if (opts.body !== undefined) frame.body.append(el(\"div\", \"confirm-body\", opts.body));\n if (opts.bodyNode) frame.body.append(opts.bodyNode);\n frame.actions.append(cancel, danger);\n\n cancel.addEventListener(\"click\", () => finish(false));\n danger.addEventListener(\"click\", () => finish(true));\n // Esc arrives as the dialog's cancel event; the frame routes it through the\n // exit motion and onClose. A mousedown that lands on the dialog element\n // itself is the backdrop.\n frame.dialog.addEventListener(\"mousedown\", (e) => { if (e.target === frame.dialog) finish(false); });\n\n frame.open();\n cancel.focus();\n });\n}\n","// ---------------------------------------------------------------------------\n// The About surface: every app in the family has one, and it should never be\n// the grey OS panel (design-language, \"coherent to the edges\").\n//\n// Patterpad's, generalised. Theirs reuses its updater-dialog chrome and hard\n// codes the PatterKit wordmark; the shell takes the wordmark as markup from the\n// app, because that is the one part that is genuinely per-product. Everything\n// else - the layout, the type, the link handling, the single Close - is the\n// family's, so two apps' About boxes are recognisably siblings.\n//\n// The host opens links, because a renderer cannot: `onOpenLink` goes through\n// the app's own IPC to `shell.openExternal`. Without it the links are shown as\n// plain text rather than as buttons that do nothing.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\nexport interface AboutOptions {\n /** The product's name, large. */\n appName: string;\n /** \"1.4.2\" - shown as \"Version 1.4.2\". */\n version: string;\n /** One or two sentences: what this app is for. */\n blurb?: string;\n /** Licence, authorship, the suite it belongs to. Newlines are respected. */\n credits?: string;\n links?: { label: string; url: string }[];\n /** Inline SVG for the product wordmark, if it has one. */\n wordmark?: string;\n /** Open a URL in the real browser. Omit and the links render as text. */\n onOpenLink?: (url: string) => void;\n}\n\n/** Show the About dialog. Resolves when it closes. */\nexport function showAbout(opts: AboutOptions): Promise<void> {\n return new Promise((resolve) => {\n // On the family's frame: the product name is the frame's title, centred by\n // about.css, and the wordmark sits above it.\n const frame = dialogFrame({ title: opts.appName, className: \"shell-about\", onClose: () => resolve() });\n frame.dialog.setAttribute(\"aria-label\", `About ${opts.appName}`);\n if (opts.wordmark !== undefined) {\n const mark = el(\"div\", \"shell-about-mark\");\n mark.innerHTML = opts.wordmark;\n frame.dialog.prepend(mark);\n }\n frame.body.append(el(\"div\", \"shell-about-version\", `Version ${opts.version}`));\n if (opts.blurb !== undefined) frame.body.append(el(\"p\", \"shell-about-blurb\", opts.blurb));\n if (opts.credits !== undefined) frame.body.append(el(\"p\", \"shell-about-credits\", opts.credits));\n\n if (opts.links?.length) {\n const row = el(\"div\", \"shell-about-links\");\n for (const link of opts.links) {\n if (!opts.onOpenLink) { row.append(el(\"span\", \"shell-about-link\", link.label)); continue; }\n const a = el(\"button\", \"shell-about-link\", link.label);\n a.type = \"button\";\n a.addEventListener(\"click\", () => opts.onOpenLink?.(link.url));\n row.append(a);\n }\n frame.body.append(row);\n }\n\n const close = el(\"button\", \"btn shell-about-close\", \"Close\");\n close.type = \"button\";\n close.addEventListener(\"click\", () => frame.close());\n frame.actions.append(close);\n frame.open();\n close.focus();\n });\n}\n","// ---------------------------------------------------------------------------\n// A floating panel anchored to the thing it is about: Patterpad's\n// `openAnchoredPanel`, lifted and generalised.\n//\n// Patterpad's condition, effects, jump and gameId editors all use one, and so do\n// Storyletter's comment threads. Two implementations of one idea existed for\n// about a day, which is exactly the drift the shell is for - and Patterpad's was\n// the mature one, so this is that, not the one written second.\n//\n// The behaviours are all learnt from use, and each is a bug somebody hit:\n//\n// - Re-clicking the SAME anchor toggles the panel off and returns null,\n// rather than closing and reopening with a flicker. Callers must handle the\n// null.\n// - A DIFFERENT anchor swaps rather than stacking: only one is ever open.\n// - The outside-click listener is armed a tick late, or the very click that\n// opened the panel would close it again.\n// - `ignoreDown` spares body-level inner popovers, which are outside the\n// panel in the DOM but inside it as far as the author is concerned.\n// - `deferEscape` leaves Escape to an inner popover when one is open, so one\n// key does not close two things.\n//\n// Placement is the part worth reading. It flips to whichever side of the anchor\n// has MORE room, keeps clear of any bottom bars the app names, caps at 70% of\n// the window and scrolls its body. A panel that opens cramped against a status\n// bar with empty space above it is the failure this avoids.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface AnchoredPanel {\n panel: HTMLElement;\n /** The content area to populate: the header is built for you. */\n body: HTMLElement;\n /** Idempotent; fires the caller's `onClose` exactly once. */\n close: () => void;\n}\n\nexport interface AnchoredPanelOptions {\n anchor: HTMLElement;\n /** Extra class(es) on the panel, for app-specific sizing or colour. */\n className?: string;\n title: string;\n width: number;\n /**\n * Which side to try first.\n *\n * `\"left\"` suits a panel hung off a row in a right-hand pane (Patterpad's\n * inspector); `\"below\"` suits one hung off a button in a document's header\n * (Storyletter's topline). Either way it flips when the room is not there.\n */\n prefer?: \"left\" | \"below\" | \"centre\";\n /** Selectors for chrome the panel must not cover - a problems bar, a review\n * bar - so those stay usable while it is open. */\n keepClear?: string[];\n /** A pointerdown inside something matching this does NOT close the panel. */\n ignoreDown?: string;\n /** While something matching this exists, Escape belongs to it, not to us. */\n deferEscape?: string;\n /** Where the panel is appended. Default <body>. A panel opened from inside a\n * modal <dialog> passes the dialog: a body-level node sits behind the scrim,\n * inert, while one inside the dialog paints in its top layer. Placement is\n * viewport-relative either way (`position: fixed`). */\n host?: HTMLElement;\n onClose?: () => void;\n}\n\n/** The one open panel. Only ever one, which is what makes toggling meaningful. */\nlet current: { anchor: HTMLElement; panel: HTMLElement; close: () => void } | null = null;\n\nexport function openAnchoredPanel(opts: AnchoredPanelOptions): AnchoredPanel | null {\n if (current) {\n const prev = current;\n current = null;\n prev.close();\n if (prev.anchor === opts.anchor) return null; // a toggle, not a reopen\n }\n\n const panel = el(\"div\", `shell-anchored ${opts.className ?? \"\"}`.trim());\n const closeBtn = el(\"button\", \"shell-anchored-close\", iconNode(\"close\", 12));\n closeBtn.type = \"button\";\n closeBtn.setAttribute(\"aria-label\", \"Close\");\n const body = el(\"div\", \"shell-anchored-body\");\n panel.append(\n el(\"div\", \"shell-anchored-head\", el(\"span\", \"shell-anchored-title\", opts.title), closeBtn),\n body,\n );\n (opts.host ?? document.body).append(panel);\n placeAnchored(panel, opts.anchor, opts.width, opts.prefer ?? \"below\", opts.keepClear ?? []);\n\n let closed = false;\n const close = (): void => {\n if (closed) return;\n closed = true;\n if (current?.panel === panel) current = null;\n document.removeEventListener(\"pointerdown\", onDown, true);\n document.removeEventListener(\"keydown\", onKey, true);\n // Play the exit, THEN tear down. The order matters: `onClose` is where a\n // caller unmounts whatever it put in the body, so running it first would\n // leave an empty panel fading out. Listeners come off above rather than\n // here, because a panel mid-fade must not still be taking clicks.\n closeWithExit(panel, () => { opts.onClose?.(); panel.remove(); });\n };\n closeBtn.addEventListener(\"click\", close);\n\n const onDown = (e: PointerEvent): void => {\n const target = e.target as Node;\n if (panel.contains(target) || opts.anchor.contains(target)) return;\n if (opts.ignoreDown !== undefined && target instanceof Element && target.closest(opts.ignoreDown)) return;\n close();\n };\n const onKey = (e: KeyboardEvent): void => {\n if (e.key !== \"Escape\") return;\n if (opts.deferEscape !== undefined && document.querySelector(opts.deferEscape)) return;\n close();\n };\n // Next tick: the click that opened this is still travelling.\n setTimeout(() => {\n document.addEventListener(\"pointerdown\", onDown, true);\n document.addEventListener(\"keydown\", onKey, true);\n }, 0);\n\n current = { anchor: opts.anchor, panel, close };\n return { panel, body, close };\n}\n\n/** Close whatever is open. For a navigation or a re-render: a panel anchored to\n * an element that no longer exists would hang in the air. */\nexport function closeAnchoredPanel(): void {\n const open = current;\n current = null;\n open?.close();\n}\n\n/** Patterpad's placement, with the side as a parameter. */\nexport function placeAnchored(\n panel: HTMLElement, anchor: HTMLElement, width: number,\n prefer: \"left\" | \"below\" | \"centre\", keepClear: string[],\n): void {\n const a = anchor.getBoundingClientRect();\n const GAP = 10;\n const CAP = window.innerHeight * 0.7;\n const MIN_H = 140;\n panel.style.width = `${width}px`;\n\n // \"centre\" is for an anchor that is a POINT rather than a control: a zero-size\n // proxy dropped where somebody clicked on a canvas. Aligning a panel's edge to\n // a point puts the whole panel up and to one side of the spot the person just\n // chose, which reads as the panel having appeared somewhere else. Centred on\n // the point, it appears where they pointed.\n if (prefer === \"centre\") {\n const centred = a.left + a.width / 2 - width / 2;\n panel.style.left = `${Math.round(Math.min(Math.max(8, centred), window.innerWidth - width - 8))}px`;\n } else {\n // Horizontally: left of the anchor when asked and there is room, otherwise\n // aligned to it and nudged back inside the window.\n const leftOf = a.left - width - GAP;\n const alignedRight = Math.min(a.right - width, window.innerWidth - width - 8);\n panel.style.left = `${Math.round(prefer === \"left\" && leftOf >= 8\n ? leftOf\n : Math.max(8, prefer === \"left\" ? a.left : alignedRight))}px`;\n }\n\n // Vertically: whichever side has more room, staying off the app's bottom bars.\n let bottomLimit = window.innerHeight - 8;\n for (const selector of keepClear) {\n const bar = document.querySelector(selector);\n if (!bar) continue;\n const r = bar.getBoundingClientRect();\n if (r.height > 0) bottomLimit = Math.min(bottomLimit, r.top - 6);\n }\n // Centred on a point vertically as well, for the same reason, but only while\n // the panel actually fits: past that it falls back to the more-room-wins rule\n // below rather than hanging off the top of the window.\n if (prefer === \"centre\") {\n const height = Math.min(CAP, Math.max(MIN_H, panel.offsetHeight));\n const mid = a.top + a.height / 2 - height / 2;\n if (mid >= 8 && mid + height <= bottomLimit) {\n panel.style.top = `${Math.round(mid)}px`;\n panel.style.bottom = \"auto\";\n panel.style.maxHeight = `${Math.round(CAP)}px`;\n return;\n }\n }\n const roomBelow = bottomLimit - (a.bottom + GAP);\n const roomAbove = (a.top - GAP) - 8;\n if (roomAbove > roomBelow) {\n panel.style.top = \"auto\";\n panel.style.bottom = `${Math.round(window.innerHeight - (a.top - GAP))}px`;\n panel.style.maxHeight = `${Math.round(Math.max(MIN_H, Math.min(CAP, roomAbove)))}px`;\n } else {\n panel.style.bottom = \"auto\";\n panel.style.top = `${Math.round(Math.max(8, a.bottom + GAP))}px`;\n panel.style.maxHeight = `${Math.round(Math.max(MIN_H, Math.min(CAP, roomBelow)))}px`;\n }\n}\n","// ---------------------------------------------------------------------------\n// Game IDs: the host-facing ADDRESS of a thing.\n//\n// This belongs to the shell because it follows from the SHAPE these apps share\n// rather than from any one subject: a project of data files that compiles to\n// artefacts for something else to consume. \"Something else consumes it\" is the\n// whole reason content needs a stable name a program can call it by. Nothing\n// here needs the words card, beat, hand or scene to describe.\n//\n// It arrived here as two byte-identical copies, in Storyletter's model and\n// Patterpad's, each with its own editor popover on top - and the two editors had\n// grown DIFFERENT manners for the same job, which is how the duplication was\n// noticed at all.\n//\n// DEPENDENCY-FREE on purpose. The apps' domain layers (compilers, CLIs, engine\n// runtimes) need these rules far from any UI, and they keep their own copies\n// rather than taking a dependency on a UI kit; each asserts parity with these in\n// a test, so the copies cannot drift in silence. A NEW app starts from these.\n// ---------------------------------------------------------------------------\n\n/**\n * Slugify a name into hyphen form: lower case, apostrophes dropped, runs of\n * anything else to a single hyphen, no leading or trailing hyphen.\n *\n * May return \"\", when there was nothing usable in the input. Callers treat that\n * as \"no pinned address\" rather than as an address.\n */\nexport function gameIdify(text: string): string {\n return text.toLowerCase().replace(/['’]/g, \"\")\n .replace(/[^a-z0-9-]+/g, \"-\").replace(/-+/g, \"-\").replace(/^-+|-+$/g, \"\");\n}\n\n/** Is this a legal address? Lower case, digits and hyphens, starting and ending\n * with a letter or a digit. Note \"\" is NOT legal: an empty pinned address means\n * \"derive one\", which is a different thing from an address. */\nexport function isValidGameId(gameId: string): boolean {\n return /^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/.test(gameId);\n}\n","// ---------------------------------------------------------------------------\n// The gameId editor: a small anchored popover for setting a thing's address.\n//\n// Patterpad's `id-editor.ts`, lifted into the shell so both apps have the same\n// manners. They did not: Storyletter's accepted anything and let the save\n// quietly rewrite it, Patterpad's refused an illegal address and offered Tab as\n// an opt-in slugify. Patterpad's is the right half of that argument and is what\n// this is - the address is what a host will call the content by, and an app that\n// silently renames it has made a decision that was not its to make.\n//\n// THE SEAM: this knows nothing about what it is addressing. It takes the pinned\n// value, the name-derived fallback to show as a placeholder, and hands back a\n// string to persist (\"\" = go back to deriving one).\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { openAnchoredPanel } from \"./anchored.js\";\nimport { gameIdify as defaultSlugify, isValidGameId as defaultValid } from \"./ids.js\";\n\nexport interface GameIdEditorOptions {\n anchor: HTMLElement;\n /** The pinned address, or \"\" when it is being derived. */\n value: string;\n /** What it derives to, shown as the placeholder. */\n derived: string;\n /** Persist. \"\" means \"stop pinning, go back to deriving\". */\n onCommit: (gameId: string) => void;\n /** The panel's title. Both apps say \"Game ID\"; a third may not. */\n title?: string;\n /** Override the rules. Defaults are the shell's, which is what a new app\n * should use; an app whose domain layer owns them can pass its own. */\n slugify?: (text: string) => string;\n validate?: (gameId: string) => boolean;\n /** Where the panel goes, for hosts that anchor it somewhere tight. */\n prefer?: \"left\" | \"below\" | \"centre\";\n /** Chrome the panel must not cover, passed through to the anchored panel: an\n * app with a problems bar along the bottom wants it usable while an address\n * is being edited. Without this the editor is the one panel in a host that\n * can land on top of its own bars. */\n keepClear?: string[];\n}\n\n/**\n * Open it. Returns false when that click TOGGLED the panel shut, which is the\n * anchored panel's contract and worth passing on: a caller that repaints on open\n * needs to know it did not.\n */\nexport function openGameIdEditor(opts: GameIdEditorOptions): boolean {\n const slugify = opts.slugify ?? defaultSlugify;\n const valid = opts.validate ?? defaultValid;\n\n const panel = openAnchoredPanel({\n anchor: opts.anchor, className: \"shell-id-editor\", title: opts.title ?? \"Game ID\", width: 260,\n ...(opts.prefer !== undefined ? { prefer: opts.prefer } : {}),\n ...(opts.keepClear !== undefined ? { keepClear: opts.keepClear } : {}),\n });\n if (!panel) return false;\n\n const input = el(\"input\", \"shell-id-input\");\n input.type = \"text\";\n input.value = opts.value;\n input.placeholder = opts.derived || \"game-id\";\n input.spellcheck = false;\n\n const hint = el(\"div\", \"shell-id-hint\", \"Lower case letters, digits, and hyphens. Leave it empty to derive one from the name.\");\n const actions = el(\"div\", \"shell-id-actions\");\n const reset = el(\"button\", \"shell-id-reset\", \"Reset to auto\");\n const set = el(\"button\", \"shell-id-set\", \"Set\");\n reset.type = \"button\";\n set.type = \"button\";\n actions.append(reset, set);\n panel.body.append(input, hint, actions);\n\n // \"\" is allowed and means reset; anything else has to be a legal address.\n const legal = (): boolean => { const v = input.value.trim(); return v === \"\" || valid(v); };\n const sync = (): void => {\n set.disabled = !legal();\n hint.classList.toggle(\"bad\", !legal());\n };\n const commit = (): void => {\n if (!legal()) return;\n opts.onCommit(input.value.trim());\n panel.close();\n };\n\n input.addEventListener(\"input\", sync);\n input.addEventListener(\"keydown\", (e) => {\n // Stop keys reaching the app underneath: a canvas listening for Delete must\n // not act on one typed into an address.\n e.stopPropagation();\n if (e.key === \"Enter\") { e.preventDefault(); commit(); }\n // The opt-in coercion. Nothing is rewritten unless it is asked for, and when\n // it is, it happens in the field where it can be seen and undone.\n if (e.key === \"Tab\" && input.value.trim() !== \"\") {\n e.preventDefault();\n input.value = slugify(input.value);\n sync();\n }\n });\n set.addEventListener(\"click\", commit);\n reset.addEventListener(\"click\", () => { opts.onCommit(\"\"); panel.close(); });\n\n sync();\n setTimeout(() => input.focus(), 0);\n return true;\n}\n","// ---------------------------------------------------------------------------\n// The version-control surface's pure DOM half: folding a snapshot over the\n// shard key(s) an item stands for, the one badge that item flies, painting\n// those badges onto rows that already exist, and turning a document's editing\n// controls off when somebody else holds its shard.\n//\n// The family's, for the same reason the main half is: it follows from the app\n// SHAPE. Patterpad's #145 grammar, by way of Storyletter's port.\n//\n// Render-FREE by design, and that is load-bearing: a poll repaints badges\n// without rebuilding anything, so a mid-edit document is never disturbed.\n//\n// The one thing an app must supply is WHAT STAYS LIVE in a locked document\n// (`staysLive`): reading a shard somebody else holds must remain fully\n// possible, so navigation, disclosure and view switches keep working while the\n// writing stops - and only the app knows what those look like in its own\n// markup.\n// ---------------------------------------------------------------------------\n\nimport { icon, iconNode, type IconName } from \"./icons.js\";\nimport { el } from \"./dom.js\";\n\n/** One shard's state as the renderer sees it. The app's own DTO will be this\n * shape (it crosses its own bridge); this is what the view needs. */\nexport interface ShardVc {\n key: string;\n writable: boolean;\n lockedBy?: string[];\n outOfDate?: boolean;\n /** WE hold it: still ours to edit. */\n checkedOutByMe?: boolean;\n /** Tracked, with uncommitted local changes. */\n dirty?: boolean;\n /** Not in version control yet. */\n untracked?: boolean;\n}\n\n/** A snapshot, keyed by shard key. Absent = clean, writable, up to date. */\nexport type VcMap = ReadonlyMap<string, ShardVc>;\n\n/** Fold the state of the shard key(s) an item stands for (a box row spans its\n * box + tags + hands shards), most actionable winning: any holder locks it,\n * any stale shard makes it stale, any read-only shard makes it read-only. */\nexport function foldVc(shards: VcMap, keys: string | undefined): ShardVc | undefined {\n if (!keys) return undefined;\n let out: ShardVc | undefined;\n for (const key of keys.split(\" \")) {\n const s = shards.get(key);\n if (!s) continue;\n out ??= { key, writable: true };\n if (!s.writable) out.writable = false;\n if (s.lockedBy?.length) out.lockedBy = [...new Set([...(out.lockedBy ?? []), ...s.lockedBy])];\n if (s.outOfDate) out.outOfDate = true;\n if (s.checkedOutByMe) out.checkedOutByMe = true;\n if (s.dirty) out.dirty = true;\n if (s.untracked) out.untracked = true;\n }\n return out;\n}\n\n/** What a row flies: the word to draw (`name`), the class that colours it,\n * and the rollover. `glyph` is the deprecated typed character for a caller\n * still setting text; it goes when `icon` does. */\nexport interface VcBadge {\n name: IconName;\n /** @deprecated Draw `name` with `iconNode` instead. */\n glyph: string;\n cls: string;\n title: string;\n}\n\n/** The one badge an item flies, by priority, or null when there is nothing to\n * say. Drawn monochrome (never colour emoji), so each inherits its themed\n * colour and reads the same in Linen and Baize. */\nexport function vcBadgeFor(s: ShardVc | undefined): VcBadge | null {\n const badge = (name: IconName, cls: string, title: string): VcBadge => ({ name, glyph: icon[name as keyof typeof icon] ?? \"\", cls, title });\n if (s?.lockedBy?.length) return badge(\"locked\", \"vc-locked\", `Locked by ${s.lockedBy.join(\", \")}`);\n if (s?.outOfDate) return badge(\"down\", \"vc-stale\", \"Out of date. A newer version is on the server.\");\n // The three states of a file that is YOURS, most actionable first. Ordered so\n // the badge answers \"what would I do about this?\" rather than describing the\n // file: a checkout you are holding matters more than the edits inside it, and\n // both matter more than a file the VCS has never seen.\n if (s?.checkedOutByMe) return badge(\"checkedOut\", \"vc-mine\", \"Checked out by you\");\n if (s?.dirty) return badge(\"modified\", \"vc-dirty\", \"Modified. Local changes aren't committed yet.\");\n if (s?.untracked) return badge(\"untracked\", \"vc-new\", \"New. Not in version control yet.\");\n // Read-only on disk with NO other holder is still editable: the save checks\n // it out. Muted, and last, because under a lock-based VCS this is the\n // resting state of everything the author has not touched yet.\n if (s && !s.writable) return badge(\"readOnly\", \"vc-frozen\", \"Read-only on disk. Saving checks it out.\");\n return null;\n}\n\n/** Repaint the badge on every item under `root` that names a shard (its\n * `data-vc`). In place: no re-render, so nothing loses focus or scroll. */\nexport function paintVcBadges(root: ParentNode, shards: VcMap): void {\n root.querySelectorAll<HTMLElement>(\"[data-vc]\").forEach((host) => {\n host.querySelector(\":scope > .vc-badge\")?.remove();\n const badge = vcBadgeFor(foldVc(shards, host.dataset[\"vc\"]));\n if (!badge) return;\n // `tip` carries the accessible name too, so no separate aria-label. 12px:\n // the badge sits beside a row title at 0.78rem and must not outweigh it.\n const span = el(\"span\", { className: `vc-badge ${badge.cls}`, tip: badge.title }, iconNode(badge.name, 12));\n host.append(span);\n });\n}\n\ntype Disableable = HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\n\n/** The shell's own controls that only change what is shown, so a lock never\n * turns them off: an extension section's fold (value-fold.ts). */\nconst ALWAYS_LIVE = \".shell-fold-cap\";\n\n/** Turn a region's editing controls off - or back on, but only the ones WE\n * turned off, so a control disabled for its own reasons (the first card's\n * \"previous\", a redraw-turns box on \"always\") stays disabled. `staysLive` is\n * the app's own reading controls; the shell's (ALWAYS_LIVE) stay live too. */\nexport function lockControls(host: HTMLElement, off: boolean, staysLive: string): void {\n host.classList.toggle(\"vc-readonly\", off);\n if (off) {\n host.querySelectorAll<Disableable>(\"input, textarea, select, button\").forEach((c) => {\n if (c.disabled || c.matches(ALWAYS_LIVE) || (staysLive !== \"\" && c.matches(staysLive))) return;\n c.disabled = true;\n c.classList.add(\"vc-off\");\n });\n } else {\n host.querySelectorAll<Disableable>(\".vc-off\").forEach((c) => { c.disabled = false; c.classList.remove(\"vc-off\"); });\n }\n}\n\n/** The notice a locked document opens with: who holds it, and why nothing types. */\nexport function lockNotice(holders: string[]): HTMLElement {\n return el(\"div\", { className: \"vc-lock\" },\n el(\"span\", { className: \"vc-lock-glyph\" }, iconNode(\"locked\")),\n el(\"span\", { text: `Locked by ${holders.join(\", \")}. Read-only until they release it.` }));\n}\n","// ---------------------------------------------------------------------------\n// \"Who is working here?\" - Patterpad's first-run identity, as the family's.\n//\n// `{ name, email? }`, asked ONCE at first startup and skippable, editable later\n// from User Information… in the menu. It belongs to the person at the keyboard,\n// never to the project, which is why the app stores it in ITS OWN state and not\n// in a shard: a project carrying a name would hand it to whoever opened the file\n// next.\n//\n// The shell asks and returns; PERSISTING is the app's, because each app has its\n// own state file. `suggested` is there for the day simple-vc-lib can say who the\n// version control thinks you are (see patterkit from-storylets/vc-current-user),\n// so the box opens with a sensible answer rather than empty.\n//\n// Two occasions, one dialog (ui-review-2026-09, Patterpad adoption step 5): the\n// first run is a welcome, the menu item is an edit, and the title and the\n// primary button say which. Without a `mode` it is the plain ask it always was.\n// ---------------------------------------------------------------------------\n\nimport { el, labelled } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\nexport interface Identity { name: string; email?: string }\n\nexport interface IdentityOptions {\n /** What it already says, if anything. */\n current?: Identity;\n /** A name to offer when there is none: the VCS's, one day. */\n suggested?: Identity;\n /**\n * `\"welcome\"` is the first run (\"Welcome to <appName>\", Continue); `\"edit\"`\n * is the menu item (\"User information\", Save). Absent, the title is the\n * shell's \"Who is working here?\" and the button is Save.\n */\n mode?: \"welcome\" | \"edit\";\n /** Names the app in the welcome title. Without it the title is \"Welcome\". */\n appName?: string;\n /**\n * By default a skip resolves `undefined` and the app asks again next launch.\n * With this on, a skip resolves `{ name: \"\" }`: the app stores the blank and\n * never asks again (Patterpad's first run, where main fills in a default).\n * A Save with an empty name counts as a skip either way.\n */\n neverAskAgainOnSkip?: boolean;\n}\n\n/** Ask, and record the answer. Resolves when the dialog closes, either way. */\nexport function askIdentity(opts: IdentityOptions = {}): Promise<Identity | undefined> {\n return new Promise<Identity | undefined>((resolve) => {\n const seed = opts.current ?? opts.suggested;\n const name = el(\"input\", \"field shell-ident-input\") as HTMLInputElement;\n name.placeholder = \"Your name\";\n name.value = seed?.name ?? \"\";\n const email = el(\"input\", \"field shell-ident-input\") as HTMLInputElement;\n email.placeholder = \"Optional\";\n email.type = \"email\";\n email.value = seed?.email ?? \"\";\n\n const skip = el(\"button\", \"btn shell-ident-btn\", \"Skip\");\n const save = el(\"button\", \"btn primary shell-ident-btn\", opts.mode === \"welcome\" ? \"Continue\" : \"Save\");\n skip.type = \"button\"; save.type = \"button\";\n\n let done = false;\n const finish = (keep: boolean): void => {\n if (done) return;\n done = true;\n const typed = name.value.trim();\n const value = email.value.trim();\n const answer: Identity | undefined = keep && typed !== \"\"\n ? (value === \"\" ? { name: typed } : { name: typed, email: value })\n : (opts.neverAskAgainOnSkip === true ? { name: \"\" } : undefined);\n resolve(answer);\n frame.close();\n };\n\n // The frame owns the panel, the scrim, Escape and the exit motion; this\n // owns the two fields and the two answers.\n const frame = dialogFrame({\n title: titleFor(opts),\n sub: subFor(opts),\n className: \"shell-ident\",\n onClose: () => finish(false), // Escape, or closed by someone else: a skip\n });\n // Captions above the fields, not placeholders alone: a placeholder is gone\n // the moment there is a value in the box, and the VCS's suggestion is one.\n frame.body.append(el(\"div\", \"shell-ident-fields\", labelled(\"Name\", name), labelled(\"Email\", email)));\n frame.actions.append(skip, save);\n\n skip.addEventListener(\"click\", () => finish(false));\n save.addEventListener(\"click\", () => finish(true));\n // Enter saves, Escape skips: the two answers the dialog has.\n name.addEventListener(\"keydown\", (e) => { if (e.key === \"Enter\") finish(true); });\n email.addEventListener(\"keydown\", (e) => { if (e.key === \"Enter\") finish(true); });\n\n frame.open();\n name.focus();\n });\n}\n\nfunction titleFor(opts: IdentityOptions): string {\n if (opts.mode === \"welcome\") return opts.appName ? `Welcome to ${opts.appName}` : \"Welcome\";\n if (opts.mode === \"edit\") return \"User information\";\n return \"Who is working here?\";\n}\n\n/** The one sub line, minus its last sentence when this IS the later change. */\nfunction subFor(opts: IdentityOptions): string {\n const why = \"Your name goes on the comments you write. It's kept in this app, not in the project.\";\n return opts.mode === \"edit\" ? why : `${why} You can change it later.`;\n}\n","// ---------------------------------------------------------------------------\n// The documentation-notes editor: Patterpad's, as the family's (Patter spec 18).\n//\n// Documentation states the reason; comments are the conversation. A note says\n// why a card is here, who it is for, what a translator needs to know. It has no\n// author, no timestamp, and nobody replies to it.\n//\n// A MODAL, and that is a decision somebody already paid for: Patterpad tried an\n// anchored popover first and removed it as too cramped. Notes are prose, and\n// prose needs room.\n//\n// One text area PER CLASS, each non-blank line becoming a note of that class.\n// The alternative - a list of lines each with its own class dropdown - is more\n// granular and, as their proposal puts it, fiddlier: you would fill in a form to\n// write a sentence.\n//\n// THE SEAM: this knows nothing about what a note is attached to. The app hands\n// it the notes that apply, the ancestors they came from and the classes on\n// offer, and takes back a list. Whether the levels are box -> deck -> card or\n// scene -> block -> line never reaches here.\n//\n// Showing INHERITED notes read-only is Storyletter's addition, and it belongs in\n// the family: Patterpad deferred it because its stacked inspector already shows\n// each ancestor's Notes row, but an app whose editor has no such stack must show\n// them somewhere or an author will write the box's note again on the card.\n//\n// Patterpad moves back onto this one (the 2026-10 review, ruling I). Its own\n// copy had kept two things this had lost, the \"editor\" class (notes for an\n// outside editor, carried by the editable script) and the \"Needs re-record\"\n// prompt that opens straight on the Voice class, so both are here\n// (`focus`). It committed on every keystroke; this commits once, on close,\n// and the ruling takes that: opening a note to read it costs nothing.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\n/** One line of documentation. `type` names its class; absent = editor-only. */\nexport interface DocLine { type?: string; text: string }\n\n/** What applies to one thing: its own notes, and the ancestors' that reach it.\n * Inherited notes are READ-ONLY - they are edited where they live, which is\n * what stops a note being copied down and then disagreeing with itself. */\nexport interface Notes {\n own: DocLine[];\n /** Outermost first. Absent or empty in an app whose own UI already shows\n * each ancestor's notes (Patterpad's stacked inspector). */\n inherited?: { id: string; label: string; lines: DocLine[] }[];\n /** The classes this app offers, in the order they should appear. */\n classes: string[];\n}\n\nconst EDITOR_ONLY = \"\";\nconst UNTYPED_KEY = \"__untyped\";\n\n/** The classes the family already has words for; anything else is Title Cased,\n * so a studio can add one without touching this. Patterpad's labels. A class\n * is named for who READS the note, so \"editor\" is \"Editors\": the outside\n * editor the editable script goes to, not this app. */\nconst CLASS_LABEL: Record<string, string> = {\n everyone: \"Everyone\", writing: \"Writing\", vo: \"Voice (VO)\", loc: \"Localisers\", editor: \"Editors\",\n};\nconst label = (cls: string): string =>\n cls === EDITOR_ONLY ? \"Note (editor-only)\"\n : CLASS_LABEL[cls] ?? (cls[0]!.toUpperCase() + cls.slice(1));\n\nconst PLACEHOLDER: Record<string, string> = {\n vo: \"Direction for the voice actor or director\",\n loc: \"Context for translators\",\n editor: \"Context for an outside editor\",\n};\nconst placeholder = (cls: string): string =>\n cls === EDITOR_ONLY ? \"An internal note, never exported\"\n : PLACEHOLDER[cls] ?? \"Why this is here\";\n\nexport interface NotesEditorOptions {\n /** What this is about, for the dialog's title (\"Notes: Arrive at the gate\").\n * Absent, the title is \"Notes\". */\n subject?: string;\n /** One line under the title, for what the classes are FOR in this app\n * (Patterpad's \"A VO note travels with the voice script…\"): the dialog\n * frame's own sub line. */\n sub?: string;\n notes: Notes;\n /** Open on this class: its text area is shown even when empty, and the\n * caret is in it. Patterpad's \"Needs re-record\" asks why the line needs\n * another take, and the answer is a VO note, so it opens on \"vo\". Ignored\n * when the class is neither offered (`notes.classes`) nor already written\n * on, so a prompt cannot add a class the thing does not take. \"\" is the\n * editor-only bucket. */\n focus?: string;\n /** Commit. Called once, on close, and only when something changed. */\n save: (lines: DocLine[]) => void;\n}\n\n/** Open the notes modal. Returns at once; `save` runs when it closes. */\nexport function openNotesEditor(opts: NotesEditorOptions): void {\n const { notes } = opts;\n const inherited = notes.inherited ?? [];\n\n // Seed one text area per class that already has notes; a fresh thing gets the\n // first class in the vocabulary, so the commonest act (write a note) is typing\n // rather than choosing.\n const seed = new Map<string, string>();\n for (const line of notes.own) {\n const key = line.type ?? EDITOR_ONLY;\n seed.set(key, seed.has(key) ? `${seed.get(key)!}\\n${line.text}` : line.text);\n }\n const shown: string[] = [...seed.keys()];\n const focus = opts.focus !== undefined\n && (opts.focus === EDITOR_ONLY || notes.classes.includes(opts.focus) || seed.has(opts.focus))\n ? opts.focus : undefined;\n if (focus !== undefined && !shown.includes(focus)) shown.push(focus);\n if (shown.length === 0) shown.push(notes.classes[0] ?? EDITOR_ONLY);\n const areas = new Map<string, HTMLTextAreaElement>();\n\n const value = (): DocLine[] => {\n const out: DocLine[] = [];\n for (const cls of shown) {\n const area = areas.get(cls);\n if (!area) continue;\n for (const raw of area.value.split(\"\\n\")) {\n const text = raw.trim();\n if (text !== \"\") out.push(cls === EDITOR_ONLY ? { text } : { type: cls, text });\n }\n }\n return out;\n };\n\n const body = el(\"div\", \"shell-notes-body\");\n\n const render = (): void => {\n body.replaceChildren();\n\n // The inherited context first, because it is what an author needs BEFORE\n // deciding whether to write anything: outermost first, muted, and labelled\n // with where it lives so the answer to \"why can I not edit this\" is on\n // screen.\n if (inherited.length > 0) {\n const from = el(\"div\", { className: \"shell-notes-inherited\" },\n el(\"div\", \"shell-notes-caption\", \"Inherited\"));\n for (const level of inherited) {\n from.append(el(\"div\", { className: \"shell-notes-level\" },\n el(\"span\", \"shell-notes-level-label\", level.label),\n ...level.lines.map((line) => el(\"div\", { className: \"shell-notes-line\" },\n ...(line.type !== undefined && line.type !== \"everyone\"\n ? [el(\"span\", \"shell-notes-cls\", label(line.type))] : []),\n el(\"span\", \"\", line.text))),\n ));\n }\n body.append(from);\n }\n\n for (const cls of shown) {\n const block = el(\"div\", { className: \"shell-notes-class\" },\n el(\"label\", \"shell-notes-caption\", label(cls)));\n const area = el(\"textarea\", \"field shell-notes-text\");\n area.value = areas.get(cls)?.value ?? seed.get(cls) ?? \"\";\n area.rows = 4;\n area.placeholder = placeholder(cls);\n area.spellcheck = true;\n areas.set(cls, area);\n block.append(area);\n body.append(block);\n }\n\n // One line per note, said once where somebody is about to type.\n body.append(el(\"p\", \"shell-notes-hint\", \"One note per line.\"));\n\n const remaining = [...notes.classes, EDITOR_ONLY].filter((c) => !shown.includes(c));\n if (remaining.length > 0) {\n const add = el(\"select\", \"shell-notes-add\");\n add.append(new Option(\"Add a note for…\", \"\"));\n for (const cls of remaining) add.append(new Option(label(cls), cls === EDITOR_ONLY ? UNTYPED_KEY : cls));\n add.addEventListener(\"change\", () => {\n if (add.value === \"\") return;\n const cls = add.value === UNTYPED_KEY ? EDITOR_ONLY : add.value;\n shown.push(cls);\n render();\n areas.get(cls)?.focus();\n });\n body.append(add);\n }\n };\n render();\n\n const done = el(\"button\", \"btn primary shell-notes-btn\", \"Done\");\n done.type = \"button\";\n\n const before = JSON.stringify(notes.own);\n // Done and Escape both end up here (the frame's onClose runs for every way\n // out), so the guard is what stops one gesture saving twice.\n let closed = false;\n const commit = (): void => {\n if (closed) return;\n closed = true;\n const next = value();\n // Only when something changed: opening a note to read it must not cost an\n // undo step, and must not touch the file (or the version control status).\n if (JSON.stringify(next) !== before) opts.save(next);\n };\n // Escape closes the dialog by itself, and commits: this editor has no Cancel,\n // because a note is prose and losing a paragraph to a stray key is worse than\n // an unwanted note somebody can delete.\n const frame = dialogFrame({\n title: opts.subject !== undefined ? `Notes: ${opts.subject}` : \"Notes\",\n ...(opts.sub !== undefined ? { sub: opts.sub } : {}),\n className: \"shell-notes\", onClose: commit,\n });\n frame.body.append(body);\n frame.actions.append(done);\n done.addEventListener(\"click\", () => { commit(); frame.close(); });\n\n frame.open();\n areas.get(focus ?? shown[0] ?? EDITOR_ONLY)?.focus();\n}\n","// ---------------------------------------------------------------------------\n// The comment thread popover: Patterpad's (#148), as the family's.\n//\n// Comments are the conversation; documentation states the reason. Anchored\n// rather than modal, which is the deliberate contrast with the notes editor: a\n// comment is had BESIDE the thing, and a modal covering the card would hide\n// what is being discussed.\n//\n// Everything here that looks like a small decision is one somebody already\n// made: author and timestamp on every message (a comment without a \"who\" is an\n// anonymous complaint); Enter posts and closes while Shift+Enter is a newline;\n// the button reads Comment on a new thread and Reply on an old one; focus goes\n// to the composer ONLY for a new thread, because opening an existing one to\n// read must not yank the cursor out of the work; and a thread does not exist\n// until its first message is posted, so a composer somebody thought better of\n// leaves nothing behind.\n//\n// THE SEAM: an anchor is an opaque id. What it points at - a card, a beat, a\n// span of characters - is the app's, and so is where the threads are stored.\n// This takes a list and gives back \"post this\" and \"resolve that\".\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\nimport { openAnchoredPanel } from \"./anchored.js\";\n\n/** One message. The author is a NAME stamped at posting time, not a reference:\n * people leave projects and their words stay. */\nexport interface CommentMessage {\n author: string; ts: string; body: string;\n /** A TOMBSTONE: the words are gone, the turn in the conversation is not.\n * Deleting a reply out of a thread would renumber the argument around it, so\n * what is left says who spoke and when and that they withdrew it. The body is\n * emptied on the way out, so \"deleted\" means deleted from the file too. */\n deleted?: boolean;\n}\n\n/** A thread, anchored to the id of whatever it is about. */\nexport interface Comment {\n id: string;\n anchor: string;\n resolved?: boolean;\n messages: CommentMessage[];\n}\n\nconst stamp = (ts: string): string => {\n const when = new Date(ts);\n if (Number.isNaN(when.getTime())) return ts;\n return when.toLocaleString(undefined, { dateStyle: \"medium\", timeStyle: \"short\" });\n};\n\n/** A preview of a thread, for the list: its first message, shortened. The first\n * message that still SAYS something - a thread whose opener was withdrawn is\n * previewed by whatever carried on, not by its tombstone. */\nconst gist = (thread: Comment): string => {\n const first = thread.messages.find((m) => m.deleted !== true)?.body ?? \"\";\n return first.length > 60 ? `${first.slice(0, 57)}…` : first;\n};\n\nexport interface CommentsOptions {\n anchor: HTMLElement;\n /** What is being discussed, for the panel's title. */\n subject: string;\n /** Every thread on this thing, resolved ones included. */\n threads: Comment[];\n /** Review ▸ Show Resolved Comments. */\n showResolved: boolean;\n /** A fresh thread id, minted by the caller so main never has to. */\n newThreadId: () => string;\n post: (threadId: string, body: string) => void;\n setResolved: (threadId: string, resolved: boolean) => void;\n /**\n * Withdraw one message. Absent = the host does not offer deletion.\n *\n * By INDEX, because a message has no id of its own and giving every message\n * one would be a format change to every stored thread for the sake of one\n * verb. The index is read off the list the reader is looking at, and these\n * files are edited by one person at a time with a VCS underneath.\n *\n * The host decides what deleting MEANS: the agreed rule is that the message\n * becomes a tombstone, and the whole thread goes when nothing readable would\n * be left (which covers the common case of withdrawing a comment nobody had\n * replied to).\n */\n deleteMessage?: (threadId: string, index: number) => void;\n /** Where the panel sits: \"centre\" for a point on a canvas (see anchored.ts). */\n prefer?: \"left\" | \"below\" | \"centre\";\n /**\n * Chrome the panel must not cover. Defaults to `[\".problembar\"]`, which was\n * hardcoded here and should not have been: one app's class name in shared\n * code, and it misses any OTHER bar a host has. A second app arrived with two.\n */\n keepClear?: string[];\n /**\n * The text a thread is pinned to, shown above it as a quotation.\n *\n * A callback rather than a field on `Comment`, because resolving an anchor is\n * the APP's half of the seam and this is only that resolution rendered: an app\n * that pins threads to a span of prose can quote it, one that pins them to a\n * whole card has nothing to quote and passes nothing. Keeping it out of the\n * thread type also means no change to what is written to disk, which matters\n * for files living in version control.\n *\n * Return undefined (or \"\") for a thread with no quotable anchor.\n */\n quoteFor?: (thread: Comment) => string | undefined;\n}\n\nexport function openComments(opts: CommentsOptions): void {\n const visible = opts.threads.filter((t) => opts.showResolved || t.resolved !== true);\n const panel = openAnchoredPanel({\n anchor: opts.anchor, className: \"comments\", title: \"Comments\", width: 320,\n // Below its button (the topline), unlike Patterpad's inspector rows which\n // want it to the left; and never over the problems bar, which an author may\n // be stepping through while reading the thread.\n prefer: opts.prefer ?? \"below\",\n keepClear: opts.keepClear ?? [\".problembar\"],\n });\n if (!panel) return; // the same anchor again: that click closed it\n const { body, close } = panel;\n\n // Which thread is on screen. A brand-new one until it is posted: it exists\n // only here, which is why nothing is written when the panel closes unposted.\n let open: Comment | undefined = visible.length === 1 ? visible[0] : undefined;\n let composing = visible.length === 0;\n\n const render = (): void => {\n body.replaceChildren();\n\n if (!composing && open === undefined) {\n // More than one: pick.\n for (const thread of visible) {\n const row = el(\"button\", { className: `shell-cmt-row${thread.resolved === true ? \" resolved\" : \"\"}` },\n el(\"span\", { className: \"shell-cmt-row-author\", text: thread.messages[0]?.author ?? \"Someone\" }),\n el(\"span\", { className: \"shell-cmt-row-gist\", text: gist(thread) }),\n ...(thread.messages.length > 1\n ? [el(\"span\", { className: \"shell-cmt-row-count\", text: `${thread.messages.length}` })] : []),\n );\n row.addEventListener(\"click\", () => { open = thread; render(); });\n body.append(row);\n }\n const fresh = el(\"button\", { className: \"shell-cmt-new\", text: \"New comment\" });\n fresh.addEventListener(\"click\", () => { open = undefined; composing = true; render(); });\n body.append(fresh);\n return;\n }\n\n const thread = open;\n if (thread) {\n // The span this thread is pinned to, quoted above it. Without it a comment\n // on a phrase reads as a comment on the whole thing, and the reader has to\n // remember what was selected.\n const quote = opts.quoteFor?.(thread);\n if (quote !== undefined && quote !== \"\") {\n body.append(el(\"blockquote\", { className: \"shell-cmt-quote\", text: quote }));\n }\n const list = el(\"div\", { className: `shell-cmt-list${thread.resolved === true ? \" resolved\" : \"\"}` });\n thread.messages.forEach((message, index) => {\n const head = el(\"div\", { className: \"shell-cmt-msg-head\" },\n el(\"span\", { className: \"shell-cmt-author\", text: message.author === \"\" ? \"Someone\" : message.author }),\n el(\"span\", { className: \"shell-cmt-ts\", text: stamp(message.ts) }));\n // Rollover-revealed, at the end of the head: the family's grip grammar,\n // and a delete that is always visible invites the accident it is.\n // Nothing is offered on a tombstone: there is nothing left to withdraw.\n if (opts.deleteMessage && message.deleted !== true) {\n const bin = el(\"button\", { className: \"shell-cmt-del\" }, iconNode(\"close\", 12));\n bin.dataset.tip = \"Delete this comment\";\n bin.setAttribute(\"aria-label\", \"Delete this comment\");\n bin.addEventListener(\"click\", () => opts.deleteMessage?.(thread.id, index));\n head.append(bin);\n }\n list.append(el(\"div\", { className: `shell-cmt-msg${message.deleted === true ? \" gone\" : \"\"}` },\n head,\n el(\"div\", { className: \"shell-cmt-body\", text: message.deleted === true ? \"Comment deleted\" : message.body })));\n });\n body.append(list);\n }\n\n const input = el(\"textarea\", { className: \"shell-cmt-input\" });\n input.rows = 2;\n input.placeholder = thread ? \"Reply…\" : \"Comment…\";\n input.addEventListener(\"keydown\", (e) => {\n e.stopPropagation();\n if (e.key !== \"Enter\" || e.shiftKey) return;\n e.preventDefault();\n submit(true);\n });\n body.append(input);\n\n const submit = (andClose: boolean): void => {\n const text = input.value.trim();\n if (text === \"\") return;\n const id = thread?.id ?? opts.newThreadId();\n opts.post(id, text);\n if (andClose) close(); else { input.value = \"\"; }\n };\n\n const actions = el(\"div\", { className: \"shell-cmt-actions\" });\n const post = el(\"button\", { className: \"shell-cmt-btn primary\", text: thread ? \"Reply\" : \"Comment\" });\n post.addEventListener(\"click\", () => submit(false));\n actions.append(post);\n\n if (thread) {\n const resolved = thread.resolved === true;\n const toggle = el(\"button\", { className: \"shell-cmt-btn\", text: resolved ? \"Reopen\" : \"Mark complete\" });\n toggle.addEventListener(\"click\", () => {\n opts.setResolved(thread.id, !resolved);\n // A resolved thread hides, so there is nothing left to look at.\n if (!resolved) close(); else render();\n });\n actions.append(toggle);\n }\n body.append(actions);\n\n // Only for a new thread: reading an old one must not steal the cursor.\n if (!thread) input.focus();\n };\n render();\n}\n","// ---------------------------------------------------------------------------\n// The long-job kit (renderer): the progress strip the main-side JobHost feeds.\n// A determinate bar, a count, elapsed and estimated remaining, and Cancel.\n//\n// A STRIP, NOT A MODAL. The window that started the job is the one that shows\n// it, and the rest of the app stays usable: these are tool windows, and taking\n// the whole editor hostage while a sweep runs was the old system's mistake.\n// The strip is hidden until a job starts and hides itself when one ends.\n//\n// TWO MODES, ONE STRIP. A job that knows its size (a coverage sweep: N runs)\n// gets the determinate bar, the count and the time readout. A job that does\n// not (a publish, a pull, a pack: one write of unknown length) gets an\n// INDETERMINATE band that sweeps the bar, no count, and the elapsed time\n// alone. The host's progress message already says which: `total` 0 is \"no\n// idea yet\" (job.ts), so the strip follows the data and flips to the\n// determinate bar the moment a total arrives. A host that knows its jobs\n// never count pins the mode with `indeterminate: true`; a host whose jobs\n// cannot be stopped mid-write drops Cancel with `cancellable: false` (or by\n// passing no `onCancel` at all).\n//\n// The time readout only appears once there is enough evidence for it. An ETA\n// that swings wildly for the first second reads as a broken app, so the\n// estimate waits until a second of work and a tenth of the job are behind it.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\n\n/** Per-run overrides for a strip that serves several kinds of job (the main\n * window's: a countable export beside an uncountable pull). Each begin()\n * starts from the mount options and applies these on top. */\nexport interface JobBeginOptions {\n /** Pin the band regardless of what the job reports. */\n indeterminate?: boolean;\n /** Show Cancel for this run. */\n cancellable?: boolean;\n}\n\nexport interface JobProgressView {\n /** The strip; mount it where the job belongs. */\n readonly element: HTMLElement;\n /** Show the strip, reset to zero, and (optionally) name what is running. */\n begin(label?: string, run?: JobBeginOptions): void;\n /** Feed it a progress message from the main-side host. A `total` of 0\n * (the host's \"no idea yet\") draws the indeterminate band; the first\n * positive total flips the strip to the determinate bar. */\n update(done: number, total: number, elapsedMs: number): void;\n /** Hide the strip (the job finished, failed, or was cancelled). */\n end(): void;\n readonly visible: boolean;\n /** True while the strip is drawing the band rather than the bar. */\n readonly indeterminate: boolean;\n}\n\nexport interface JobProgressOptions {\n /** Called when Cancel is clicked. The button disables itself and says so;\n * the job stops at its next yield, and the host then calls end(). Omit it\n * for a strip whose jobs cannot be stopped: Cancel is then not drawn. */\n onCancel?: () => void;\n /** The word for the units, for the count readout (default \"runs\"). */\n units?: string;\n /** Always draw the band and never the count, whatever the job reports.\n * Default: follow the data (band until a total arrives). */\n indeterminate?: boolean;\n /** Draw Cancel. Default: only when `onCancel` was given. */\n cancellable?: boolean;\n}\n\nconst clock = (ms: number): string => {\n const total = Math.round(ms / 1000);\n const m = Math.floor(total / 60);\n const s = total % 60;\n return m > 0 ? `${m}m ${String(s).padStart(2, \"0\")}s` : `${s}s`;\n};\n\n/** Enough of the job done, and enough time passed, for a remaining-time\n * estimate to be worth showing rather than misleading. */\nconst estimable = (done: number, total: number, elapsedMs: number): boolean =>\n done > 0 && total > 0 && elapsedMs > 1000 && done / total > 0.1;\n\nexport function mountJobProgress(host: HTMLElement, opts: JobProgressOptions): JobProgressView {\n const units = opts.units ?? \"runs\";\n const pinned = opts.indeterminate === true;\n const cancellableByDefault = opts.cancellable ?? (opts.onCancel !== undefined);\n\n const fill = el(\"i\", { className: \"job-fill\" });\n const bar = el(\"div\", { className: \"job-bar\" }, fill);\n bar.setAttribute(\"role\", \"progressbar\");\n bar.setAttribute(\"aria-valuemin\", \"0\");\n bar.setAttribute(\"aria-valuemax\", \"100\");\n const what = el(\"span\", { className: \"job-label\" });\n const count = el(\"span\", { className: \"job-count\" });\n const time = el(\"span\", { className: \"job-time\" });\n const cancel = el(\"button\", { className: \"job-cancel\", text: \"Cancel\" });\n cancel.type = \"button\";\n\n const strip = el(\"div\", { className: \"job-strip\" }, what, bar, count, time, cancel);\n strip.hidden = true;\n host.append(strip);\n\n // The run's settings: reset by begin(), read by update().\n let runPinned = pinned;\n let indeterminate = false;\n\n /** Draw the band (no count, no value) or the bar (both). Every write is\n * idempotent, so update() may call it each time and only a flip shows. */\n const setMode = (band: boolean): void => {\n indeterminate = band;\n strip.classList.toggle(\"indeterminate\", band);\n count.hidden = band;\n if (band) {\n // The band's width and motion are the stylesheet's: clear the inline\n // width the bar set, or it wins over the class.\n fill.style.width = \"\";\n bar.removeAttribute(\"aria-valuenow\");\n }\n };\n\n const view: JobProgressView = {\n element: strip,\n get visible() { return !strip.hidden; },\n get indeterminate() { return indeterminate; },\n\n begin(label?: string, run?: JobBeginOptions) {\n runPinned = run?.indeterminate ?? pinned;\n const cancellable = run?.cancellable ?? cancellableByDefault;\n what.textContent = label ?? \"Running…\";\n count.textContent = \"\";\n time.textContent = \"\";\n cancel.hidden = !cancellable;\n cancel.disabled = false;\n cancel.textContent = \"Cancel\";\n // Every run opens on the band: nothing is known until the first report.\n setMode(true);\n strip.hidden = false;\n },\n\n update(done: number, total: number, elapsedMs: number) {\n if (strip.hidden) strip.hidden = false;\n const band = runPinned || !(total > 0);\n setMode(band);\n if (!band) {\n const pct = Math.min(100, (done / total) * 100);\n fill.style.width = `${pct}%`;\n bar.setAttribute(\"aria-valuenow\", String(Math.round(pct)));\n count.textContent = `${done} / ${total} ${units}`.trimEnd();\n }\n if (!band && estimable(done, total, elapsedMs)) {\n const remaining = (elapsedMs / done) * (total - done);\n time.textContent = `${clock(elapsedMs)} elapsed, about ${clock(remaining)} left`;\n } else {\n time.textContent = clock(elapsedMs);\n }\n },\n\n end() {\n strip.hidden = true;\n },\n };\n\n cancel.addEventListener(\"click\", () => {\n // Cancellation lands at the job's next yield, which may be a moment away:\n // say so, rather than leaving a dead-looking button.\n cancel.disabled = true;\n cancel.textContent = \"Stopping…\";\n opts.onCancel?.();\n });\n\n // Before the first begin(): the band, and Cancel per the mount options, so\n // a host that only ever calls update() (the strip appearing on the first\n // progress message) still gets a coherent strip.\n cancel.hidden = !cancellableByDefault;\n setMode(true);\n\n return view;\n}\n","// ---------------------------------------------------------------------------\n// The save/dirty controller: the little machine that turns \"the author typed\n// something\" into \"the bytes are on disk\", and says so on screen.\n//\n// Both apps wrote one. Neither is complete, and the two are incomplete in\n// OPPOSITE directions, which is the clearest argument for a shared one that\n// this review has produced:\n//\n// - Patterpad runs an INTERVAL (one timer for the app's life, saving any\n// dirty scene every N seconds). Continuous typing is saved throughout, but\n// a single edit can sit unwritten for the whole period.\n// - Storyletter DEBOUNCES (write 700ms after edits settle). A single edit is\n// written promptly, but an author typing steadily is never written at all\n// until they pause - which on a long paragraph is a long time.\n//\n// So this does both: debounce for promptness, with a MAXIMUM AGE so a run of\n// edits cannot outrun the writer. Neither app had the pair; each had half.\n//\n// The other thing neither had is re-entrancy. Storyletter's flush cleared its\n// pending queue and then awaited the write, so a flush arriving mid-write saw\n// nothing pending and reported \"saved\" while bytes were still in the air. Here\n// a write in flight is a thing you can await, and the status is only \"saved\"\n// when nothing arrived while it was running.\n//\n// WHAT THIS DOES NOT OWN is what a save IS. The controller never touches the\n// document: it calls the app's `write` and reads its boolean. What is pending,\n// how it coalesces, what to repaint afterwards - all of that stays in the app,\n// because all of it is about the app's model.\n//\n// DOM-free, so a test can drive it with fake timers and no window.\n// ---------------------------------------------------------------------------\n\n/**\n * Three states, not a boolean.\n *\n * \"saving\" earns its place: on a slow disk or a slow VC hook a save takes long\n * enough to see, and a status that flips straight from unsaved to saved leaves\n * the author wondering whether the click did anything.\n */\nexport type SaveStatus = \"saved\" | \"unsaved\" | \"saving\";\n\nexport interface SaveControllerOptions {\n /**\n * Write whatever is pending. Return false if it failed: the controller stays\n * \"unsaved\" and will try again on the next touch or flush, rather than\n * quietly reporting success over a write that did not happen.\n *\n * Called only when something is pending, and never twice at once.\n */\n write: () => Promise<boolean> | boolean;\n /** Told every time the status changes, and only when it changes. */\n onStatus?: (status: SaveStatus) => void;\n /** How long after the last edit to write. Storyletter's 700ms by default. */\n delayMs?: number;\n /**\n * The longest an edit may go unwritten while more keep arriving.\n *\n * This is the interval half of the two apps put back in: without it, steady\n * typing defers the write for as long as the typing lasts.\n */\n maxWaitMs?: number;\n}\n\nexport interface SaveController {\n /** What the indicator should say. */\n readonly status: SaveStatus;\n /** Is anything unwritten? For a close guard, or an updater asking whether it\n * may restart. True while a write is in flight, too: it is not on disk yet. */\n readonly pending: boolean;\n /** Something changed. Starts (or extends) the countdown. */\n touch(): void;\n /**\n * Write now, and wait for it.\n *\n * Call on every transition that must see current bytes: switching what is\n * being edited, opening a tool window that reads the disk, closing the\n * window, Cmd+S. Safe to call when nothing is pending (it does nothing) and\n * safe to call during a write (it waits for it, then writes again if more\n * arrived while it ran).\n */\n flush(): Promise<void>;\n /** Give up on what is pending without writing it. For a project close after\n * the author has said to discard, and for teardown. */\n cancel(): void;\n /**\n * Autosave on or off (Patterpad's per-project setting).\n *\n * Off does not mean \"never write\": touch() still marks the work unsaved, and\n * flush() still writes on transitions and on Cmd+S. It only stops the clock.\n */\n setAuto(on: boolean): void;\n}\n\nexport function createSaveController(opts: SaveControllerOptions): SaveController {\n const delayMs = opts.delayMs ?? 700;\n const maxWaitMs = opts.maxWaitMs ?? 5000;\n\n let status: SaveStatus = \"saved\";\n let auto = true;\n let dirty = false;\n /** When the oldest unwritten edit arrived, for the maximum age. */\n let dirtySince = 0;\n let timer: ReturnType<typeof setTimeout> | undefined;\n /** The write in flight, so a second flush awaits it instead of racing it. */\n let inFlight: Promise<void> | undefined;\n\n const setStatus = (next: SaveStatus): void => {\n if (next === status) return;\n status = next;\n opts.onStatus?.(status);\n };\n\n const clearTimer = (): void => {\n if (timer === undefined) return;\n clearTimeout(timer);\n timer = undefined;\n };\n\n const arm = (): void => {\n clearTimer();\n if (!auto) return;\n // Never later than maxWaitMs after the first unwritten edit, however much\n // longer the author keeps typing.\n const deadline = dirtySince + maxWaitMs;\n const wait = Math.max(0, Math.min(delayMs, deadline - Date.now()));\n timer = setTimeout(() => { timer = undefined; void run(); }, wait);\n };\n\n const run = async (): Promise<void> => {\n if (inFlight) return inFlight; // one writer at a time\n if (!dirty) { setStatus(\"saved\"); return; }\n clearTimer();\n dirty = false;\n setStatus(\"saving\");\n inFlight = (async () => {\n let ok = false;\n try {\n ok = await opts.write();\n } catch {\n // A thrown write is a failed write. The app has already said whatever\n // it wants to say about it; here it only means \"still unsaved\".\n ok = false;\n }\n if (!ok) {\n // Put the dirt back so the next touch or flush retries, and keep the\n // oldest timestamp: a failing save must not reset the maximum age.\n if (!dirty) { dirty = true; dirtySince = dirtySince || Date.now(); }\n }\n setStatus(dirty ? \"unsaved\" : \"saved\");\n if (dirty) arm(); // more arrived while we wrote, or it failed\n })();\n try {\n await inFlight;\n } finally {\n inFlight = undefined;\n }\n };\n\n return {\n get status() { return status; },\n get pending() { return dirty || inFlight !== undefined; },\n touch() {\n if (!dirty) dirtySince = Date.now();\n dirty = true;\n // An edit arriving mid-write leaves the status alone: a write really is\n // running, and it will settle to \"unsaved\" on its own when it lands.\n if (!inFlight) setStatus(\"unsaved\");\n arm();\n },\n async flush() {\n // Await a write already running, then write again if this call's work (or\n // anything that arrived mid-write) is still outstanding.\n if (inFlight) await inFlight;\n if (dirty) await run();\n else setStatus(\"saved\");\n },\n cancel() {\n clearTimer();\n dirty = false;\n setStatus(inFlight ? \"saving\" : \"saved\");\n },\n setAuto(on) {\n auto = on;\n if (on) { if (dirty) arm(); } else clearTimer();\n },\n };\n}\n","// ---------------------------------------------------------------------------\n// The save indicator: the six lines that draw what `createSaveController`\n// already computes.\n//\n// The controller has been shared since 0.14.0 and its three states with it, but\n// the DRAWING stayed in each app - so both apps hand-rendered the output of one\n// machine and promptly drifted. Patterpad's was `<span id=\"dirty\" hidden>●\n// unsaved</span>`: present only when there was something to worry about.\n// Storyletter's always paints one of Saved / Saving… / Unsaved. (Design review\n// 2026-08, A4.)\n//\n// STORYLETTER'S WON, and the reasoning is worth keeping because it is not\n// \"newer is better\". Three states are what the shared controller models, and an\n// app that autosaves without being asked owes the author the reassurance as\n// well as the warning: a dot that only ever appears to worry you leaves \"did\n// that get written?\" unanswered at exactly the moment it is asked. \"Saving…\"\n// earns its place separately - on a slow disk or a slow version-control hook a\n// write takes long enough to see.\n//\n// It cannot drift again, which is the actual point of putting it here.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport type { SaveStatus } from \"./save.js\";\n\nexport interface SaveIndicator {\n /** Put it in the topbar. */\n readonly el: HTMLElement;\n /** Wire straight to the controller's `onStatus`. */\n set(status: SaveStatus): void;\n}\n\n/** What each state says. Not an option: two apps saying it differently is the\n * drift this exists to end. */\nconst WORDS: Record<SaveStatus, string> = {\n saved: \"Saved\",\n saving: \"Saving…\",\n unsaved: \"Unsaved\",\n};\n\nexport function saveIndicator(initial: SaveStatus = \"saved\"): SaveIndicator {\n const node = el(\"span\");\n const set = (status: SaveStatus): void => {\n node.className = `savestat ${status}`;\n node.textContent = WORDS[status];\n };\n set(initial);\n return { el: node, set };\n}\n","// ---------------------------------------------------------------------------\n// The stale-session bar: \"what you are playing is older than the project\".\n//\n// Both apps grew one without copying the other, and landed within a word of the\n// same sentence - Patterpad's \"Scene changed in the editor: restart to play the\n// new version\", Storyletter's \"The project changed in the editor. Restart to\n// play the new version.\" That is the strongest evidence a thing is shape-level\n// rather than a taste: any app that compiles a document into something you then\n// PLAY has a running session that can fall behind its source, and the only two\n// things it can say are what went out of date and how to start again.\n//\n// So the app supplies the NOUN and the callback, and nothing else. Everything\n// left over - the warn-toned banner, the sentence, the restart glyph - is the\n// shape, and having it in one place is what stops the two apps drifting to two\n// wordings for one situation.\n//\n// WHOSE DRAWING WON, since the two differed here where the words did not.\n// Patterpad's was a line of centred grey text in the choice tray; Storyletter's\n// is a bordered warn-toned bar with the button in it. The bar, because this is\n// a condition rather than a remark: the session is frozen until you act, and a\n// note that reads like the end of a passage does not say so.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\n\nexport interface StaleBarOptions {\n /**\n * What went out of date, as a capitalised noun phrase: \"The project\", \"The\n * scene\". Written into \"<subject> changed in the editor.\"\n */\n subject: string;\n /**\n * The whole first sentence, for when \"changed in the editor\" is not what\n * happened: another project was opened, or the project was closed. Replaces\n * \"<subject> changed in the editor.\"; the advice still follows it.\n */\n message?: string;\n /** Start again from the current source. */\n onRestart: () => void;\n /** The button, when \"Restart\" is not the app's word for it. */\n restartLabel?: string;\n /**\n * The rest of the sentence, for an app whose session is not a PLAY: the\n * default is \"Restart to play the new version.\"\n */\n advice?: string;\n}\n\nexport function staleBar(opts: StaleBarOptions): HTMLElement {\n const label = opts.restartLabel ?? \"Restart\";\n return el(\"div\", { className: \"stale-bar\" },\n el(\"span\", { className: \"stale-bar-msg\",\n text: `${opts.message ?? `${opts.subject} changed in the editor.`} ${opts.advice ?? \"Restart to play the new version.\"}` }),\n // The icon is a node beside the word, not a character inside it; the\n // button's flex gap sets the space (stale.css).\n el(\"button\", { className: \"stale-bar-go\", onClick: () => opts.onRestart() },\n iconNode(\"restart\", 12), label),\n );\n}\n","// ---------------------------------------------------------------------------\n// The toast: a transient remark from the app - \"Bundle published\", \"Save\n// refused: ...\", \"Spell-check is off\" - that shows itself, waits, and leaves.\n//\n// Both apps grew one without copying the other, and drifted on everything a\n// toast can drift on. Patterpad's sat bottom-right, an outlined card on the\n// surface colour, entered and left on the shared panel motion, lingered longer\n// for an error, and announced itself to a screen reader. Storyletter's sat\n// bottom-centre, a solid --danger or --ok block with surface-coloured text, had\n// no motion, one duration, and no ARIA. Same three sentences either side.\n//\n// WHOSE DRAWING WON, and why, since the two differed here in every particular:\n//\n// PLACE: bottom-right. A toast is a REMARK, not a condition (that is the stale\n// bar's job), and a remark must not sit over what the author is reading or\n// typing. Bottom-centre lands on the centre column in the editor and on the\n// choice tray in Play; the corner is out of the reading measure, and it is\n// where the platforms put their own notifications, so the eye already knows\n// to glance there and back.\n//\n// COLOUR: an outlined card on --surface, with the KIND carried by the border\n// and the text colour. A solid block of --danger shouts, and the design\n// language asks for restraint at the seams; the card also keeps its text as\n// --ink, which reads on every palette without each one having to tune a\n// contrast pair for white-on-red.\n//\n// TIME: 4s, 7s for an error. Something went wrong is worth an extra glance.\n//\n// MOTION: the shared panel vocabulary in tokens.css, in and out, so a toast\n// moves the way every other transient surface in the family moves.\n//\n// One toast at a time: a new one replaces the old, never stacks. Rapid events\n// (a burst of saves) would otherwise pile up a column of remarks about one\n// moment. The dismiss checks it is still dismissing the CURRENT toast, so a\n// replacement is never hidden by its predecessor's timer.\n//\n// Mounted by the module, not by host markup: it appends its own node to\n// document.body on first use, so a second window gets the whole thing by\n// importing it (multi-window-rules.md section 1). Its CSS travels beside it.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { closeWithExit } from \"./exit.js\";\n\n/** What the remark is about. `info` is the plain case; `ok` says a thing you\n * asked for happened; `error` says it did not. */\nexport type ToastKind = \"info\" | \"ok\" | \"error\";\n\nexport interface ToastOptions {\n /** How long it stays, in ms, before the exit plays. Defaults to 4000, or\n * 7000 for an error. */\n duration?: number;\n}\n\nconst DURATION: Record<ToastKind, number> = { info: 4000, ok: 4000, error: 7000 };\n\nlet node: HTMLElement | undefined;\nlet timer = 0;\nlet seq = 0;\n\nfunction mount(): HTMLElement {\n if (node && node.isConnected) return node;\n // role=status + aria-live=polite: announced without interrupting, which is\n // exactly a toast's manners.\n node = el(\"div\", { className: \"shell-toast\" });\n node.setAttribute(\"role\", \"status\");\n node.setAttribute(\"aria-live\", \"polite\");\n node.hidden = true;\n document.body.append(node);\n return node;\n}\n\n/**\n * Show a toast. Replaces any toast already showing.\n *\n * `message` is shown as written; a newline breaks the line (\"Bundle\n * published\\n<path>\"), and a long unbroken token such as a path wraps inside\n * the card rather than spilling past it.\n */\nexport function toast(message: string, kind: ToastKind = \"info\", opts: ToastOptions = {}): void {\n const t = mount();\n const id = ++seq;\n t.textContent = message;\n t.className = `shell-toast ${kind}`; // also clears a `.closing` left by a prior dismiss\n t.hidden = false;\n window.clearTimeout(timer);\n timer = window.setTimeout(() => {\n // Only hide if this is still the toast that set the timer: a replacement\n // that arrived meanwhile has its own.\n closeWithExit(t, () => { if (seq === id) t.hidden = true; });\n }, opts.duration ?? DURATION[kind]);\n}\n\n/** Dismiss the current toast now, if any. Plays the exit. */\nexport function dismissToast(): void {\n if (!node || node.hidden) return;\n window.clearTimeout(timer);\n const id = seq;\n const t = node;\n closeWithExit(t, () => { if (seq === id) t.hidden = true; });\n}\n","// ---------------------------------------------------------------------------\n// The stepper bar: a bottom bar that walks you through a list of things to\n// attend to, one at a time.\n//\n// Count, previous, position, next, the current thing as a button, and a slot at\n// the right for whatever acts on it. That is the whole shape, and it is already\n// four bars across two apps: Patterpad's problems bar and its review walk,\n// Storyletter's problems bar and its Review Feedback walk. Patterpad drew its\n// two with parallel class sets (`.problembar-*` and `.reviewbar-*`) for one\n// idea; Storyletter drew its second as the first plus one modifier, which is\n// the better rule and the reason this ended up here (design review 2026-08,\n// section 5 items 2 and 3).\n//\n// NO DOMAIN WORD anywhere in it, which is the test that says it belongs in the\n// shell rather than in either app. It knows about a list, an index, and three\n// pieces of text per entry; it does not know what a problem or a comment is.\n// Severity, resolvedness and quick fixes all arrive as strings and elements the\n// app made.\n//\n// TWO THINGS PATTERPAD'S BARS LACK and gain by adopting this: the position\n// readout (`3/11`, so the walk has a length and an end rather than just a next),\n// and the `where` segment naming what the entry is about before you go there.\n//\n// STEPPING NAVIGATES, or does not: that is the CALLER's decision and this bar\n// takes no view. It reports `onStep` and `onGo` and nothing more. The rule that\n// settles it lives in design-language.md - an ambient surface moves the view and\n// never the focus; a mode you entered may take you somewhere - and the guard for\n// an uncommitted edit belongs to the app, because only the app knows what is\n// unsaved.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconNode } from \"./icons.js\";\n\nexport interface StepperItem {\n /**\n * One word for what kind of thing this is: \"error\", \"comment\", \"resolved\".\n * Absent when the bar carries one kind of thing only.\n */\n kind?: string;\n /** An extra class on the kind chip, so an app can colour its own kinds\n * (Storyletter's `sev-error`, and `done` for a resolved thread). */\n kindClass?: string;\n /** Where it is. A string is the app's own address, set in the mono face; a\n * Node is a made element, a `breadcrumb([\"Box\", \"Deck\", \"Card\"])` say,\n * drawn as it is. Give the bar plain crumbs: the whole entry is already\n * the button that goes there, so a crumb with its own click has nowhere\n * else to take you. */\n where?: string | Node;\n /** The entry itself, in one line. Overflow is clipped, not wrapped: the bar\n * is one row high and stays that way however long the sentence is. */\n text: string;\n}\n\n/** How loud the count is. The app picks; the palette supplies the value. */\nexport type StepperTone = \"danger\" | \"warn\" | \"accent\" | \"muted\";\n\nexport interface StepperBarOptions {\n items: StepperItem[];\n /** Which one is current. Clamped, so a shrinking list cannot point off the end. */\n at: number;\n tone?: StepperTone;\n /** Go to another index. The bar wraps, so this is always in range. */\n onStep: (next: number) => void;\n /** Take me to what the current entry is about. */\n onGo: (index: number) => void;\n /** What the three controls say they do. Keyboard shortcuts belong in here. */\n tips?: { prev?: string; next?: string; go?: string };\n /**\n * What to say when the list is empty.\n *\n * Absent hides the bar, which is right for an AMBIENT bar: no problems, no\n * problems bar. Present keeps it up, which is right for a MODE: entering the\n * walk and seeing nothing at all reads as a broken command rather than as\n * \"there is no feedback\".\n */\n empty?: string;\n /** Leave the mode. Drawn as the last control, hard right. */\n onClose?: () => void;\n closeTip?: string;\n /** The right-hand end before the close: a quick fix, say. Nulls are skipped,\n * so a caller can pass a conditional straight in. */\n actions?: (HTMLElement | null | undefined)[];\n}\n\nconst STEP_ICON = 12;\n\n/** Draw (or hide) the bar in `host`. Idempotent: call it again with new state. */\nexport function renderStepperBar(host: HTMLElement, opts: StepperBarOptions): void {\n const { items } = opts;\n // 12px inside the bar's 20px control: the bar is set at 0.8rem.\n const close = opts.onClose === undefined ? null : el(\"button\", {\n className: \"stepbar-nav stepbar-close\",\n tip: opts.closeTip ?? \"Close\", onClick: opts.onClose,\n }, iconNode(\"close\", STEP_ICON));\n const actions = (opts.actions ?? []).filter((a): a is HTMLElement => a !== null && a !== undefined);\n\n if (items.length === 0) {\n if (opts.empty === undefined) { host.hidden = true; host.replaceChildren(); return; }\n host.hidden = false;\n host.replaceChildren(\n el(\"span\", { className: \"stepbar-count muted\", text: \"0\" }),\n el(\"span\", { className: \"stepbar-empty\", text: opts.empty }),\n ...actions, ...(close ? [close] : []),\n );\n return;\n }\n\n host.hidden = false;\n const index = Math.min(Math.max(opts.at, 0), items.length - 1);\n const item = items[index]!;\n const step = (delta: number): void => opts.onStep((index + delta + items.length) % items.length);\n\n // The steppers are absent for a list of one: two arrows that both come back\n // to where you are is furniture pretending to be a control.\n const nav = items.length > 1\n ? [\n el(\"button\", { className: \"stepbar-nav\",\n tip: opts.tips?.prev ?? \"Previous\", onClick: () => step(-1) }, iconNode(\"back\", STEP_ICON)),\n el(\"span\", { className: \"stepbar-of\", text: `${index + 1}/${items.length}` }),\n el(\"button\", { className: \"stepbar-nav\",\n tip: opts.tips?.next ?? \"Next\", onClick: () => step(1) }, iconNode(\"forward\", STEP_ICON)),\n ]\n : [];\n\n host.replaceChildren(\n el(\"span\", { className: `stepbar-count ${opts.tone ?? \"muted\"}`, text: String(items.length) }),\n ...nav,\n el(\"button\", { className: \"stepbar-cur\", tip: opts.tips?.go ?? \"Go to this\", onClick: () => opts.onGo(index) },\n item.kind === undefined ? null\n : el(\"span\", { className: `stepbar-cat${item.kindClass ? ` ${item.kindClass}` : \"\"}`, text: item.kind }),\n item.where === undefined ? null\n : typeof item.where === \"string\" ? el(\"span\", { className: \"stepbar-where\", text: item.where })\n : el(\"span\", \"stepbar-where\", item.where),\n el(\"span\", { className: \"stepbar-msg\", text: item.text }),\n ),\n ...actions, ...(close ? [close] : []),\n );\n}\n","// ---------------------------------------------------------------------------\n// Navigation history - the VS Code back/forward pair, family-wide.\n//\n// The shell owns the STACK and the ARROWS; each app owns what a place is.\n// `Place` is opaque here for the same reason the app-store's `places` is:\n// \"where you are\" means a focus + document + tab in Storyletter and a scene +\n// position in Patterpad, and the shell has no business knowing either. The app\n// supplies two judgements - `same` (two places that are the same DOCUMENT, so\n// tab and selection twitches coalesce instead of filling the stack) and\n// `usable` (a place can still be restored: the card it names may have been\n// deleted since) - and the mechanics cannot drift between the apps.\n//\n// The model is the browser's: `visit` records the place being LEFT on an\n// ordinary navigation and clears the forward list (navigating anew after\n// going back branches history, and the abandoned branch is gone); `back` and\n// `forward` exchange the current place for the adjacent one. Entries that\n// fail `usable` are DISCARDED on the way past, never restored and never moved\n// to the other side.\n//\n// One duty stays with the app, documented rather than latched: restoring a\n// place re-dispatches the app's own navigation actions, and those call\n// `visit`. The app must not `visit` while it is travelling (Storyletter's\n// `arriving` flag is the local idiom), or every Back would push what it just\n// left and Forward would never reach anything.\n// ---------------------------------------------------------------------------\n\nimport { iconNode, type IconName } from \"./icons.js\";\n\nexport interface NavHistoryOptions<Place> {\n /** Same DOCUMENT: a visit whose place is `same` as the newest entry\n * REPLACES it (keeping the freshest tab) instead of stacking. */\n same(a: Place, b: Place): boolean;\n /** Still restorable? Checked as an entry is stepped onto; a stale one is\n * dropped and the step continues past it. Default: everything is. */\n usable?(p: Place): boolean;\n /** Entries beyond this age off the far end (default 100). */\n cap?: number;\n}\n\nexport interface NavHistory<Place> {\n /** Record the place being left, before an ordinary navigation. */\n visit(place: Place): void;\n /** Step back: hands over the current place (it becomes the forward entry)\n * and returns the place to restore, or undefined at the far end. */\n back(current: Place): Place | undefined;\n forward(current: Place): Place | undefined;\n /** Whether the arrows light. Optimistic: a side holding only stale entries\n * still reports true, and the press quietly discards them (the alternative\n * is validating the whole stack on every paint). */\n canBack(): boolean;\n canForward(): boolean;\n /** Forget both sides, for when the places stop meaning anything: a\n * different project opened, or the welcome screen with none. Without it\n * Back walks into the project just closed (Patterpad, 2026-10-07: on the\n * welcome screen the arrows looked live and did nothing). The caller\n * re-`set`s its arrows afterwards, as after any other change. */\n clear(): void;\n}\n\nexport function createNavHistory<Place>(opts: NavHistoryOptions<Place>): NavHistory<Place> {\n const cap = opts.cap ?? 100;\n const usable = opts.usable ?? ((): boolean => true);\n let past: Place[] = [];\n let ahead: Place[] = [];\n\n const step = (from: Place[], to: Place[], current: Place): Place | undefined => {\n for (;;) {\n const p = from.pop();\n if (p === undefined) return undefined;\n if (!usable(p)) continue; // its document is gone: drop, keep walking\n if (opts.same(p, current)) continue; // a step must MOVE: a self-visit (a\n // tab click routed through a navigation action) is not a journey, and\n // restoring it reads as a dead button\n to.push(current);\n return p;\n }\n };\n\n return {\n visit(place) {\n ahead = [];\n const top = past[past.length - 1];\n if (top !== undefined && opts.same(top, place)) past[past.length - 1] = place;\n else past.push(place);\n if (past.length > cap) past = past.slice(past.length - cap);\n },\n back(current) { return step(past, ahead, current); },\n forward(current) { return step(ahead, past, current); },\n canBack() { return past.length > 0; },\n canForward() { return ahead.length > 0; },\n clear() { past = []; ahead = []; },\n };\n}\n\n/**\n * The quiet arrow pair, VS Code's shape: greyed where there is nowhere to\n * go, one component so it looks and sits the same in every app of the\n * family; the caller re-`set`s it whenever a navigation may have changed\n * what the arrows can do.\n *\n * ARROWS, not chevrons, and the distinction is the family's glyph grammar\n * (the author's ruling, 2026-08-28): a chevron says STRUCTURE - the pane\n * toggles, a labelled up-crumb, a tree row's disclosure - and an arrow says\n * TIME. The first cut used chevrons, and sitting on the topbar two seats\n * from the pane toggle's chevron the pair read as more of the same thing;\n * it is how every browser, editor and file manager already split the two.\n *\n * The pair's HOME is the topbar's lead, immediately after the nav toggle\n * (Patterpad placed it there first, and the ruling ratified it).\n */\nexport function historyNav(onBack: () => void, onForward: () => void): {\n el: HTMLElement;\n set(canBack: boolean, canForward: boolean): void;\n} {\n const btn = (name: IconName, tip: string, onClick: () => void): HTMLButtonElement => {\n const b = document.createElement(\"button\");\n b.type = \"button\";\n b.className = \"shell-histnav-btn\";\n b.append(iconNode(name));\n b.dataset[\"tip\"] = tip;\n b.setAttribute(\"aria-label\", tip);\n b.addEventListener(\"click\", onClick);\n return b;\n };\n const back = btn(\"arrowLeft\", \"Back\", onBack);\n const forward = btn(\"arrowRight\", \"Forward\", onForward);\n const el = document.createElement(\"span\");\n el.className = \"shell-histnav\";\n el.append(back, forward);\n return {\n el,\n set(canBack, canForward) { back.disabled = !canBack; forward.disabled = !canForward; },\n };\n}\n"]}
|