@wildwinter/app-shell 0.38.0 → 0.39.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES.md +62 -0
- package/README.md +8 -0
- package/dist/about.css +13 -34
- package/dist/about.css.map +1 -1
- package/dist/anchored.css +5 -5
- package/dist/anchored.css.map +1 -1
- package/dist/app-store.cjs +15 -0
- package/dist/app-store.cjs.map +1 -1
- package/dist/app-store.d.cts +25 -1
- package/dist/app-store.d.ts +25 -1
- package/dist/app-store.js +14 -1
- package/dist/app-store.js.map +1 -1
- package/dist/chunk-2ECJUTSX.js +97 -0
- package/dist/chunk-2ECJUTSX.js.map +1 -0
- package/dist/chunk-2NMVIM54.js +55 -0
- package/dist/chunk-2NMVIM54.js.map +1 -0
- package/dist/chunk-C6UVQYKI.js +70 -0
- package/dist/chunk-C6UVQYKI.js.map +1 -0
- package/dist/chunk-IU7EY6BI.js +26 -0
- package/dist/chunk-IU7EY6BI.js.map +1 -0
- package/dist/chunk-K2AOLQW4.js +82 -0
- package/dist/chunk-K2AOLQW4.js.map +1 -0
- package/dist/chunk-KGAMP5LW.js +87 -0
- package/dist/chunk-KGAMP5LW.js.map +1 -0
- package/dist/chunk-L5HOBEKL.js +534 -0
- package/dist/chunk-L5HOBEKL.js.map +1 -0
- package/dist/chunk-P4G4ERG6.js +60 -0
- package/dist/chunk-P4G4ERG6.js.map +1 -0
- package/dist/chunk-P6WLAEKZ.js +71 -0
- package/dist/chunk-P6WLAEKZ.js.map +1 -0
- package/dist/chunk-S4KWQLYN.js +66 -0
- package/dist/chunk-S4KWQLYN.js.map +1 -0
- package/dist/chunk-XZYZXY7I.js +554 -0
- package/dist/chunk-XZYZXY7I.js.map +1 -0
- package/dist/comments.css +5 -3
- package/dist/comments.css.map +1 -1
- package/dist/confirm.css +1 -54
- package/dist/confirm.css.map +1 -1
- package/dist/context-menu.cjs +250 -0
- package/dist/context-menu.cjs.map +1 -0
- package/dist/context-menu.css +64 -0
- package/dist/context-menu.css.map +1 -0
- package/dist/context-menu.d.cts +25 -0
- package/dist/context-menu.d.ts +25 -0
- package/dist/context-menu.js +5 -0
- package/dist/context-menu.js.map +1 -0
- package/dist/controls.css +155 -0
- package/dist/controls.css.map +1 -0
- package/dist/dialog.cjs +222 -0
- package/dist/dialog.cjs.map +1 -0
- package/dist/dialog.css +55 -0
- package/dist/dialog.css.map +1 -0
- package/dist/dialog.d.cts +28 -0
- package/dist/dialog.d.ts +28 -0
- package/dist/dialog.js +5 -0
- package/dist/dialog.js.map +1 -0
- package/dist/floating.cjs +83 -0
- package/dist/floating.cjs.map +1 -0
- package/dist/floating.d.cts +25 -0
- package/dist/floating.d.ts +25 -0
- package/dist/floating.js +4 -0
- package/dist/floating.js.map +1 -0
- package/dist/identity.css +15 -50
- package/dist/identity.css.map +1 -1
- package/dist/index.cjs +1307 -258
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +349 -149
- package/dist/index.d.ts +349 -149
- package/dist/index.js +259 -822
- package/dist/index.js.map +1 -1
- package/dist/job.css +23 -0
- package/dist/job.css.map +1 -1
- package/dist/link-status.cjs +430 -0
- package/dist/link-status.cjs.map +1 -0
- package/dist/link-status.css +76 -0
- package/dist/link-status.css.map +1 -0
- package/dist/link-status.d.cts +53 -0
- package/dist/link-status.d.ts +53 -0
- package/dist/link-status.js +5 -0
- package/dist/link-status.js.map +1 -0
- package/dist/menu.cjs +62 -1
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.d.cts +88 -1
- package/dist/menu.d.ts +88 -1
- package/dist/menu.js +1 -1
- package/dist/notes-editor.css +3 -46
- package/dist/notes-editor.css.map +1 -1
- package/dist/pane-shell.css +16 -3
- package/dist/pane-shell.css.map +1 -1
- package/dist/property-list-D3VZCJzi.d.cts +162 -0
- package/dist/property-list-D3VZCJzi.d.ts +162 -0
- package/dist/property-list.cjs +784 -0
- package/dist/property-list.cjs.map +1 -0
- package/dist/property-list.d.cts +1 -0
- package/dist/property-list.d.ts +1 -0
- package/dist/property-list.js +4 -0
- package/dist/property-list.js.map +1 -0
- package/dist/settings.css +52 -8
- package/dist/settings.css.map +1 -1
- package/dist/stale.css +3 -0
- package/dist/stale.css.map +1 -1
- package/dist/stepper.css +35 -0
- package/dist/stepper.css.map +1 -1
- package/dist/tokens.css +19 -0
- package/dist/tokens.css.map +1 -1
- package/dist/tool-window.cjs +63 -0
- package/dist/tool-window.cjs.map +1 -1
- package/dist/tool-window.css +6 -3
- package/dist/tool-window.css.map +1 -1
- package/dist/tool-window.d.cts +67 -1
- package/dist/tool-window.d.ts +67 -1
- package/dist/tool-window.js +63 -1
- package/dist/tool-window.js.map +1 -1
- package/dist/updater-view.cjs +283 -0
- package/dist/updater-view.cjs.map +1 -0
- package/dist/updater-view.d.cts +23 -0
- package/dist/updater-view.d.ts +23 -0
- package/dist/updater-view.js +6 -0
- package/dist/updater-view.js.map +1 -0
- package/dist/updater.cjs +12 -16
- package/dist/updater.cjs.map +1 -1
- package/dist/updater.css +44 -0
- package/dist/updater.css.map +1 -0
- package/dist/updater.js +12 -16
- package/dist/updater.js.map +1 -1
- package/dist/util.cjs +78 -0
- package/dist/util.cjs.map +1 -0
- package/dist/util.d.cts +37 -0
- package/dist/util.d.ts +37 -0
- package/dist/util.js +3 -0
- package/dist/util.js.map +1 -0
- package/dist/vc.css +3 -2
- package/dist/vc.css.map +1 -1
- package/dist/welcome.cjs +215 -0
- package/dist/welcome.cjs.map +1 -0
- package/dist/welcome.css +112 -0
- package/dist/welcome.css.map +1 -0
- package/dist/welcome.d.cts +55 -0
- package/dist/welcome.d.ts +55 -0
- package/dist/welcome.js +4 -0
- package/dist/welcome.js.map +1 -0
- package/package.json +58 -2
- package/dist/chunk-QDPFU4WX.js +0 -43
- package/dist/chunk-QDPFU4WX.js.map +0 -1
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tooltip.ts","../src/dom.ts","../src/colour.ts","../src/icons.ts","../src/pane-shell.ts","../src/settings.ts","../src/tool-window-web.ts","../src/exit.ts","../src/confirm.ts","../src/about.ts","../src/anchored.ts","../src/ids.ts","../src/property-names.ts","../src/property-name-field.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","node","label","active","timer","GAP","thread","current","place","tip"],"mappings":"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","file":"index.js","sourcesContent":["// ---------------------------------------------------------------------------\n// A single themed tooltip the family controls, replacing native `title`\n// rollovers: those are unstyled OS chrome and appear on the platform's slow\n// (~1s) delay, which is exactly the seam design-language section 4 (\"coherent\n// to the edges\") says not to leave showing.\n//\n// ONE floating bubble, document-DELEGATED: any element carrying a `data-tip`\n// attribute gets it, so a caller just sets `data-tip=\"...\"` anywhere and wires\n// nothing. The bubble anchors above the element (flipping below near the top\n// edge), uses our own snappy delay and the shared panel-enter motion.\n// `initTooltips()` is idempotent, so it is safe to call from more than one\n// place in an app that has several entry points.\n//\n// Lifted from Patterpad's patterpad-surface/web/tooltip.ts, which proved it,\n// with the one app-specific rule (suppressed in Writing View) generalised into\n// the `suppressed` option so the shell knows nothing about either app's modes.\n// ---------------------------------------------------------------------------\n\n// Bold markers: a producer wraps a span in these PRIVATE control chars (U+0002 STX / U+0003 ETX,\n// never present in user text) and the renderer turns it into a <strong>. Lets a data-tip bold part\n// of itself (a name, a \"VO:\" prefix) without any HTML, and each segment is still set via\n// textContent, so there is no injection risk from user content.\nconst B_OPEN = \"\\u0002\";\nconst B_CLOSE = \"\\u0003\";\n\n/** Wrap `s` so it renders bold inside a tooltip (see the marker note above). */\nexport function tipBold(s: string): string { return `${B_OPEN}${s}${B_CLOSE}`; }\n\n/** Render `text` into `host`, turning `\\u0002…\\u0003`-marked spans into <strong>; all set as text (safe). */\nfunction renderTip(host: HTMLElement, text: string): void {\n host.textContent = \"\";\n const re = /\\u0002([^\\u0003]*)\\u0003/g;\n let last = 0;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n if (m.index > last) host.append(text.slice(last, m.index));\n const strong = document.createElement(\"strong\");\n strong.textContent = m[1] ?? \"\";\n host.append(strong);\n last = m.index + m[0].length;\n }\n if (last < text.length) host.append(text.slice(last));\n}\n\nexport interface TooltipOptions {\n /** Called before every show: return true to keep the bubble down. For a mode\n * that wants no chrome at all (Patterpad's Writing View). */\n suppressed?: () => boolean;\n}\n\nlet inited = false;\nlet checked = false; // the one-shot no-host warning below has been scheduled\nlet suppressed: (() => boolean) | undefined;\nconst SHOW_DELAY = 350; // ms - snappier than the OS title delay, slow enough not to flicker on pass-through\nconst EDGE = 6; // viewport inset so the bubble never touches the window edge\nconst GAP = 7; // px between the anchor and the bubble\n\nlet tip: HTMLDivElement | null = null;\nlet timer = 0;\nlet active: HTMLElement | null = null;\n/** The subject of a rect-anchored tip (see `tipAt`), when there is one. */\nlet activeKey: string | null = null;\n\nfunction ensureEl(): HTMLDivElement {\n if (tip) return tip;\n const el = document.createElement(\"div\");\n el.className = \"tooltip\";\n el.setAttribute(\"role\", \"tooltip\");\n el.hidden = true;\n document.body.appendChild(el);\n tip = el;\n return el;\n}\n\n/** A rectangle in VIEWPORT coordinates, for anchoring a tip to something that is\n * not a DOM element: a shape on a canvas, a run of text inside one. */\nexport interface TipRect { left: number; top: number; width: number; height: number }\n\nconst rectOf = (anchor: HTMLElement | TipRect): TipRect =>\n (anchor instanceof HTMLElement ? anchor.getBoundingClientRect() : anchor);\n\n/** Anchor the (already-visible, so measurable) bubble above the anchor, flipping below near the top. */\nfunction place(anchor: HTMLElement | TipRect): void {\n const t = ensureEl();\n const r = rectOf(anchor);\n const tr = t.getBoundingClientRect();\n let top = r.top - tr.height - GAP;\n if (top < EDGE) top = r.top + r.height + GAP; // not enough room above -> below\n let left = r.left + r.width / 2 - tr.width / 2;\n left = Math.max(EDGE, Math.min(left, window.innerWidth - tr.width - EDGE));\n t.style.left = `${Math.round(left)}px`;\n t.style.top = `${Math.round(top)}px`;\n}\n\nfunction show(anchor: HTMLElement): void {\n const text = anchor.dataset[\"tip\"];\n if (!text) return;\n paint(anchor, text);\n}\n\n/** Fill, place and reveal the bubble. The one path: both the delegated\n * `data-tip` route and `tipAt` end up here, so a canvas tip and a button tip\n * are the same bubble with the same look and the same motion. */\nfunction paint(anchor: HTMLElement | TipRect, text: string): void {\n if (suppressed?.()) return;\n const t = ensureEl();\n // A modal <dialog> (settings, a confirm) renders in the browser TOP LAYER, above every normal-flow\n // element whatever its z-index. If the anchor is inside one, move the bubble into that dialog so it\n // shares the top layer; otherwise keep it on <body>. Re-parenting per show also moves it back out\n // once the modal closes.\n const host: HTMLElement = (anchor instanceof HTMLElement ? anchor.closest<HTMLElement>(\"dialog[open]\") : null)\n ?? document.body;\n if (t.parentElement !== host) host.appendChild(t);\n active = anchor instanceof HTMLElement ? anchor : null;\n renderTip(t, text);\n t.hidden = false;\n t.classList.remove(\"show\"); void t.offsetWidth; // restart the enter animation\n place(anchor); // measure + position now it has content + is visible\n t.classList.add(\"show\");\n}\n\nfunction hide(): void {\n if (timer) { window.clearTimeout(timer); timer = 0; }\n active = null;\n activeKey = null;\n if (tip) { tip.hidden = true; tip.classList.remove(\"show\"); }\n}\n\nfunction schedule(anchor: HTMLElement): void {\n if (anchor === active) return;\n if (timer) window.clearTimeout(timer);\n timer = window.setTimeout(() => show(anchor), SHOW_DELAY);\n}\n\n/**\n * Show a tip for something that has no DOM node of its own: a card on a canvas,\n * a zone, a pin. The caller says WHERE (a viewport rectangle) and WHAT, and\n * identifies the subject with `key`.\n *\n * The key is what makes this usable from a canvas. Re-calling with the same key\n * re-places the bubble WITHOUT restarting the delay, so a tip can follow its\n * shape while the camera moves; calling with a different key starts the wait\n * again, so sweeping the pointer across a dense board does not strobe.\n *\n * The bubble is ordinary DOM, so it holds its size whatever the canvas is\n * zoomed to. That is the point of it: the label it stands in for is the one\n * thing on a canvas that must not shrink with everything else.\n */\nexport function tipAt(key: string, rect: TipRect, text: string): void {\n if (key === activeKey) {\n // Already up for this subject: move it, do not re-animate it.\n if (tip && !tip.hidden) { renderTip(tip, text); place(rect); }\n return;\n }\n // A different subject: down first, then wait again. Leaving the old bubble up\n // during the wait would put one card's name over another card, which is worse\n // than no name at all - and it matches the delegated route, where leaving an\n // element hides the tip before the next one is scheduled.\n hide();\n activeKey = key;\n timer = window.setTimeout(() => { timer = 0; paint(rect, text); }, SHOW_DELAY);\n}\n\n/** Take any tip down: the pointer has left, or a gesture has started. */\nexport function hideTip(): void { hide(); }\n\n/**\n * Mount the renderer because something is about to speak the grammar.\n *\n * The themed tooltip is not a feature a host turns on; it is the renderer for a grammar the shell's own\n * components already speak, and `data-tip` with nothing listening draws NOTHING - not the themed\n * bubble, not the platform one. That silence cost Patterpad two releases of a pin with no tooltip, and\n * cost it again when a `title` was converted to `data-tip` in a window that had never mounted this.\n *\n * So `el` calls this whenever it sets a tip. Mounting a delegated listener is the same class of act as\n * `confirmDialog` appending to `document.body`, which this package has always done.\n */\nexport function ensureTooltipHost(): void { initTooltips(); }\n\n/**\n * One deferred check that an app writing `data-tip` ITSELF has a host, warning if not.\n *\n * `ensureTooltipHost` covers anything built through `el`. It cannot cover a `data-tip` written straight\n * into markup or set by hand, which is the case that leaves a whole window of dead tips with no shell\n * component involved - so that one gets a warning rather than silence. Scheduled once, after the\n * current task, so it sees a rendered document rather than an empty one.\n */\nexport function checkTooltipHost(): void {\n if (checked) return;\n checked = true;\n setTimeout(() => {\n if (inited || typeof document === \"undefined\") return;\n if (document.querySelector(\"[data-tip]\")) {\n console.warn(\"app-shell: something set `data-tip` but no tooltip host is mounted, so those rollovers will not draw. Call initTooltips() once per window.\");\n }\n }, 0);\n}\n\nconst tipAncestor = (n: EventTarget | null): HTMLElement | null =>\n (n instanceof Element ? n.closest<HTMLElement>(\"[data-tip]\") : null);\n\n/**\n * Wire the one delegated controller. Mounting happens once; OPTIONS do not.\n *\n * An explicit call is always authoritative about its options, even after something else has already\n * mounted the listener. That ordering is not hypothetical, and Storyletter caught it before it shipped:\n * once components self-mount (see `ensureTooltipHost`), a host's own\n * `initTooltips({ suppressed })` could land second and be dropped by an early return, silently taking\n * a rule like \"no tooltips in Writing View\" with it. Nothing would fail and nothing would show.\n *\n * Components therefore call this with NO arguments: they mount the renderer without claiming the\n * options, and the host keeps the last word on behaviour.\n */\nexport function initTooltips(opts: TooltipOptions = {}): void {\n if (opts.suppressed !== undefined) suppressed = opts.suppressed; // an explicit call always wins\n if (inited) return;\n inited = true;\n\n document.addEventListener(\"pointerover\", (e) => {\n const el = tipAncestor(e.target);\n if (el) schedule(el);\n });\n document.addEventListener(\"pointerout\", (e) => {\n const el = tipAncestor(e.target);\n if (!el) return;\n const to = (e as PointerEvent).relatedTarget;\n if (to instanceof Node && el.contains(to)) return; // still inside the same anchor\n hide();\n });\n // Keyboard parity: reveal on focus, dismiss on blur.\n document.addEventListener(\"focusin\", (e) => { const el = tipAncestor(e.target); if (el) schedule(el); });\n document.addEventListener(\"focusout\", () => hide());\n // It's ANCHORED (not cursor-following), so anything that moves the layout or context dismisses it.\n document.addEventListener(\"pointerdown\", () => hide(), true);\n document.addEventListener(\"scroll\", () => { if (active) hide(); }, true);\n document.addEventListener(\"keydown\", (e) => { if (e.key === \"Escape\") hide(); });\n window.addEventListener(\"blur\", () => hide());\n}\n","// ---------------------------------------------------------------------------\n// `el`: the tiny tag-typed element factory both apps build their imperative,\n// idempotent views with.\n//\n// A SUPERSET of the two apps' prior signatures, so neither rewrites call sites:\n// - Patterpad: el(tag, cls?, text?) (positional)\n// - Storylet Studio: el(tag, { className, text, ... }, ...kids) (options bag)\n// A string second argument is the className; an object is the props bag; string\n// children become text nodes. So `el(\"div\", \"c\", \"t\")` and\n// `el(\"div\", { className: \"c\" }, \"t\")` produce identical DOM.\n// ---------------------------------------------------------------------------\n\nimport { ensureTooltipHost, checkTooltipHost } from \"./tooltip.js\";\n\nexport type Child = Node | string | null | undefined;\n\nexport interface ElProps {\n className?: string;\n text?: string;\n /** A NATIVE rollover. Prefer `tip`: `title` is OS chrome on the platform's own\n * slow delay, which is the seam the themed tooltip exists to close. */\n title?: string;\n /** A themed rollover (`data-tip`, picked up by the delegated controller that\n * `initTooltips()` wires). Also the accessible name, since the elements that\n * want one are usually icon buttons with no text of their own. */\n tip?: string;\n onClick?: (event: MouseEvent) => void;\n}\n\nexport function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n classNameOrProps?: string | ElProps,\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n const props: ElProps = typeof classNameOrProps === \"string\"\n ? { className: classNameOrProps }\n : classNameOrProps ?? {};\n if (props.className) node.className = props.className;\n if (props.text !== undefined && props.text !== null) node.textContent = props.text;\n if (props.title !== undefined) node.title = props.title;\n if (props.tip !== undefined) {\n node.dataset[\"tip\"] = props.tip;\n node.setAttribute(\"aria-label\", props.tip);\n // Setting a tip mounts the renderer. Without this, `data-tip` draws nothing at all in a window whose\n // host never called `initTooltips()`, which is a silent failure rather than a degraded one. No\n // options passed: the host stays authoritative about behaviour (see tooltip.ts).\n ensureTooltipHost();\n }\n if (props.onClick) node.addEventListener(\"click\", props.onClick as EventListener);\n for (const child of children) {\n if (child === null || child === undefined) continue;\n node.append(child);\n }\n checkTooltipHost(); // one-shot: warns if the APP wrote data-tip and nothing mounted a host\n return node;\n}\n\n/** A small square glyph button (the move/delete controls list editors share).\n * Its rollover is the THEMED one (`data-tip`), so an app that has called\n * `initTooltips()` gets our bubble rather than the platform's. */\nexport function iconBtn(glyph: string, title: string, onClick: () => void, disabled = false, danger = false): HTMLButtonElement {\n const b = el(\"button\", `shell-icon${danger ? \" danger\" : \"\"}`);\n b.type = \"button\"; b.textContent = glyph; b.dataset[\"tip\"] = title; b.setAttribute(\"aria-label\", title);\n b.disabled = disabled;\n b.addEventListener(\"click\", onClick);\n return b;\n}\n\n/**\n * A captioned field: `<label class=\"shell-labelled\"><span class=\"shell-fieldcap\">…</span>control</label>`.\n *\n * The label is pointed at the field it captions, EXPLICITLY, and never at a button. A `<label>` with no\n * `for` forwards a click to its first labelable descendant, and buttons are labelable - so a caption\n * wrapped around a control containing buttons (`tagChips`, whose every chip carries a remove button)\n * turns every click on the row's dead space into a press of the FIRST button in it. That shipped:\n * patterkit/patter#44, where clicking a Game Data list value anywhere but its ✕ deleted the first value\n * in the list. Reported as \"clicking a value removes the wrong one\", which is not a wrong-index bug at\n * all - the click never reached the chip.\n *\n * When the control holds no labelable field (a chips editor with the add input removed, say), this is a\n * plain `<div>`: no caption behaviour is better than a caption that presses something.\n */\nexport function labelled(label: string, control: HTMLElement): HTMLElement {\n const target = control.matches(\"input, select, textarea\")\n ? control\n : control.querySelector<HTMLElement>(\"input:not([type=button]):not([type=submit]), select, textarea\");\n const w = el(target ? \"label\" : \"div\", \"shell-labelled\");\n w.append(el(\"span\", \"shell-fieldcap\", label), control);\n if (target) {\n if (!target.id) target.id = `shell-f-${Math.random().toString(36).slice(2, 9)}`;\n (w as HTMLLabelElement).htmlFor = target.id;\n }\n return w;\n}\n\n/** Swap item `i` with its neighbour `i + delta` IN PLACE (the up/down reorder list editors share);\n * a no-op when the target is out of range. Returns whether anything moved. */\nexport function moveItem<T>(arr: T[], i: number, delta: number): boolean {\n const j = i + delta;\n if (j < 0 || j >= arr.length) return false;\n [arr[i], arr[j]] = [arr[j]!, arr[i]!];\n return true;\n}\n\n/** A tag-style editor for a string-list field (an enum's allowed values / flags): removable chips + an\n * add input (Enter or \",\" commits; blank / duplicate ignored). Mutates `holder.values` in place (read\n * back on save). `onChange` fires after any add / remove so callers can refresh a dependent control. */\nexport function tagChips(holder: { values?: string[] }, onChange?: () => void): HTMLElement {\n const wrap = el(\"div\", \"shell-tags\");\n const input = el(\"input\", \"shell-tag-input\");\n // Sentence-cased like every other hint (the microcopy ruling, 2026-08-28):\n // lowercase fragments read as adrift, and a placeholder is a hint speaking.\n input.type = \"text\"; input.placeholder = \"Add value\"; input.spellcheck = false;\n const makeChip = (v: string): HTMLElement => {\n const chip = el(\"span\", \"shell-tag\", v);\n const x = el(\"button\", \"shell-tag-x\", \"✕\");\n x.type = \"button\"; x.dataset[\"tip\"] = `remove ${v}`; x.setAttribute(\"aria-label\", `remove ${v}`);\n x.addEventListener(\"click\", () => { holder.values = (holder.values ?? []).filter((o) => o !== v); chip.remove(); onChange?.(); });\n chip.append(x);\n return chip;\n };\n const commit = (): void => {\n const v = input.value.trim();\n if (v && !(holder.values ?? []).includes(v)) { (holder.values ??= []).push(v); wrap.insertBefore(makeChip(v), input); onChange?.(); }\n input.value = \"\";\n };\n input.addEventListener(\"keydown\", (e) => { if (e.key === \"Enter\" || e.key === \",\") { e.preventDefault(); commit(); } });\n input.addEventListener(\"blur\", commit);\n for (const v of holder.values ?? []) wrap.append(makeChip(v));\n wrap.append(input);\n return wrap;\n}\n","// ---------------------------------------------------------------------------\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// The family's icon vocabulary.\n//\n// An icon is a WORD. Two apps in one suite spelling \"close\" differently is the\n// same fault as calling one thing a scene here and a chapter there, and it is\n// harder to notice because nobody reads glyphs, they just feel wrong. This\n// module exists so that no app ever types a codepoint again: it asks for\n// `icon.close` and gets whatever the family has agreed that means.\n//\n// The evidence that it was needed, gathered 2026-08-09 across both apps:\n// Patterpad spelled close as `✕` twelve times, Storyletter used `✕` once and\n// `×` twice - in one window - and a comment bubble had just been invented from\n// scratch with nothing to check it against.\n//\n// GLYPHS, mostly, rather than SVG. That is a deliberate limit: a text glyph\n// inherits colour, size and weight from its button for free, costs nothing to\n// ship, and is legible at the small sizes this chrome lives at. SVG is here for\n// the two or three shapes Unicode has no honest character for (a document with\n// lines on it), lifted from Patterpad where it already drew them.\n//\n// Adding one: it needs a MEANING, not a picture. \"A downward chevron\" is not an\n// entry; \"expand this\" is. If two callers want the same picture for two\n// meanings, that is two entries, and one of them will be wrong later.\n// ---------------------------------------------------------------------------\n\n/**\n * Named by what it MEANS. Grouped by the job, because that is how a caller\n * looks for one.\n */\nexport const icon = {\n // --- dismissing and revealing ---------------------------------------------\n /** Close a panel, a dialog, a popover. Patterpad's, twelve times over. */\n close: \"✕\",\n /** The overflow menu on a document or a row: \"more about this\". */\n more: \"⋯\",\n /** Collapsed disclosure, pointing at what would open. */\n collapsed: \"▸\",\n /** Expanded disclosure. */\n expanded: \"▾\",\n /** A dropdown's affordance, where it is not a real <select>. */\n dropdown: \"▾\",\n\n // --- moving through things ------------------------------------------------\n /** Back / previous, in a breadcrumb or a stepper. */\n back: \"‹\",\n /** Forward / next. */\n forward: \"›\",\n /** Up one, in a list. */\n up: \"↑\",\n /** Down one. */\n down: \"↓\",\n\n // --- state ----------------------------------------------------------------\n /** Done, applied, agreed. */\n tick: \"✓\",\n /** Somebody else holds this: the version-control lock. */\n locked: \"⊘\",\n /** Read-only on disk with no other holder: the resting state of everything\n * in a lock-based working copy that nobody has checked out yet. */\n readOnly: \"○\",\n /** YOU hold this one: checked out / opened by the current user, still yours to\n * edit. Shares its glyph with `note`, which the table permits: an entry is a\n * MEANING, and two meanings may draw the same shape. */\n checkedOut: \"✎\",\n /** Tracked, with local changes not yet committed. */\n modified: \"●\",\n /** Not in version control yet: new since the last commit. */\n untracked: \"+\",\n /** Something needs attention but is not fatal. */\n warning: \"‼\",\n /** A live thing: the running position, an active marker. */\n dot: \"●\",\n /** Play, in the sense of \"run this\". */\n play: \"▶\",\n /** Start the thing again from the top: a play session, a sweep. Named for the\n * act, not the arrow, which is the table's rule. */\n restart: \"⟲\",\n /** The three ways a collection can be shown. Named by what you get rather than\n * by the glyph, so a second app can pick the same three without copying\n * somebody's taste in symbols. */\n viewCards: \"▦\",\n viewTable: \"☰\",\n viewNode: \"◈\",\n\n // --- handling -------------------------------------------------------------\n /** The drag grip on a reorderable row. */\n grip: \"⠿\",\n /** Add one of whatever this list holds. */\n add: \"+\",\n\n // --- annotation -----------------------------------------------------------\n /** Documentation notes: the pencil. Both apps had chosen it independently,\n * which is the happiest way for a vocabulary entry to arrive. */\n note: \"✎\",\n /** A comment thread. Speech, not a pencil: documentation states the reason,\n * comments are the conversation, and the two must never look alike. */\n comment: \"❝\",\n} as const;\n\nexport type IconName = keyof typeof icon;\n\n/**\n * The few shapes no glyph says honestly, as inline SVG.\n *\n * `currentColor` throughout, so they take the colour of the button they sit in\n * exactly as the glyphs do, and sized in the markup for the same reason: an icon\n * that needs a CSS rule to be the right size is an icon that will be the wrong\n * size somewhere.\n *\n * Lifted from Patterpad, which drew them first.\n */\nexport const iconSvg = {\n /** A document with lines: notes exist on this thing. */\n noteFilled: '<svg viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" fill=\"currentColor\" fill-rule=\"evenodd\" aria-hidden=\"true\"><path d=\"M3.4 1.4h5.3L12.6 5.3v8.1a1.2 1.2 0 0 1-1.2 1.2H3.4a1.2 1.2 0 0 1-1.2-1.2V2.6A1.2 1.2 0 0 1 3.4 1.4ZM4.9 6.8h6.2v1.1H4.9Zm0 2.1h6.2v1.1H4.9Zm0 2.1h3.7v1.1H4.9Z\"/></svg>',\n /** The same document, empty: nothing written here yet. */\n noteOutline: '<svg viewBox=\"0 0 16 16\" width=\"13\" height=\"13\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.1\" stroke-linejoin=\"round\" stroke-linecap=\"round\" aria-hidden=\"true\"><path d=\"M3.4 2h4.9l3.9 3.9v7.5a1 1 0 0 1-1 1H3.4a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1Z\"/><path d=\"M8.3 2.2v3.8h3.8\"/><path d=\"M4.9 8h6.2M4.9 10h6.2M4.9 12h3.7\" stroke-width=\"0.95\"/></svg>',\n} as const;\n\nexport type IconSvgName = keyof typeof iconSvg;\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\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 /** Shortcut hint appended to the tooltip, e.g. \"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}\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 isOpen(side: PaneSide): boolean;\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\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 function applyToggleGlyph(side: PaneSide, button: HTMLButtonElement): void {\n const isOpen = open[side];\n // Chevron points toward where the pane collapses to.\n button.textContent = side === \"nav\" ? (isOpen ? \"‹\" : \"›\") : (isOpen ? \"›\" : \"‹\");\n const noun = cfg[side].label ?? side;\n const hint = cfg[side].shortcutHint ? ` (${cfg[side].shortcutHint})` : \"\";\n button.title = `${isOpen ? \"Hide\" : \"Show\"} ${noun}${hint}`;\n button.setAttribute(\"aria-label\", button.title);\n button.setAttribute(\"aria-pressed\", String(isOpen));\n }\n\n function apply(): void {\n panes.classList.toggle(collapseClass.nav, !open.nav);\n panes.classList.toggle(collapseClass.inspector, !open.inspector);\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 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 || !open[side]) return;\n startEvent.preventDefault();\n const pane = side === \"nav\" ? navPane : inspPane;\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 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: (s) => open[s], resetWidths, state: snapshot,\n destroy: () => { root.remove(); },\n };\n}\n","// ---------------------------------------------------------------------------\n// The shared settings surface: a native <dialog> with a left-rail of grouped\n// tabs and one panel per section (mountSettingsDialog), plus the list-editing\n// core those sections lean on (expandableRow, focusNewRow, dupGuard). The app\n// supplies the sections; each holds its own state and reports the first invalid\n// control so the Save gate can block + jump. Framework here, content in the app.\n//\n// Lifted from Patterpad's Project Settings (settings-list.ts + the inlined\n// dialog logic in its renderer), with neutral class names. Styling ships as\n// settings.css.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\n\n// --- list-editing core --------------------------------------------------------\n\n/** A compact list row: a single `.set-rowline` (inline controls) plus, when\n * `details` are given, a disclosure that reveals a `.set-details` panel below,\n * so a list scans like a table with secondary fields tucked away. `name` is\n * what the row declares (a property's name, a field's), stamped on it as\n * `data-name` so `revealRow` can land on it. */\nexport function expandableRow(opts: { line: HTMLElement[]; details?: HTMLElement[]; name?: string }): HTMLElement {\n const row = el(\"div\", \"set-row\");\n if (opts.name !== undefined) row.dataset.name = opts.name;\n const line = el(\"div\", \"set-rowline\");\n if (opts.details && opts.details.length) {\n const details = el(\"div\", \"set-details\"); details.hidden = true;\n for (const d of opts.details) details.append(d);\n // The triangle is drawn by CSS off `.open` (a text glyph stays hairline-thin).\n const toggle = el(\"button\", \"set-expand\"); toggle.type = \"button\";\n // `data-tip`, not `title`: this package ships a themed tooltip and nothing\n // else in it raises an OS rollover. A lone `title` here would be the one\n // place a host's chrome broke character, and on the platform's own delay.\n toggle.dataset.tip = \"More\"; toggle.setAttribute(\"aria-label\", \"More\");\n toggle.setAttribute(\"aria-expanded\", \"false\");\n toggle.addEventListener(\"click\", () => {\n const open = details.hidden;\n details.hidden = !open;\n toggle.setAttribute(\"aria-expanded\", String(open));\n toggle.classList.toggle(\"open\", open);\n });\n line.append(toggle);\n opts.line.forEach((c) => line.append(c));\n row.append(line, details);\n } else {\n opts.line.forEach((c) => line.append(c));\n row.append(line);\n }\n return row;\n}\n\n/**\n * Land on one row of a list by the `name` it was built with: open its details\n * (an enum's values sit behind the disclosure, and they are usually what the\n * jump was for), bring it to the middle of its scroller, and light it for a\n * moment (`.set-row.landed` in settings.css). \"Go to definition\" in both apps\n * used to open the page a property is declared on and stop there, with the\n * declaration below the fold and nothing marking it (Storyletter, 2026-09-14).\n *\n * Returns false when no row carries the name, so a host whose page fills in\n * asynchronously can ask again on a later frame; asking again is the host's,\n * since only the host knows what opens on its own time. The row is matched by\n * reading the stamp rather than by an attribute selector, so a name needs no\n * escaping.\n */\nexport function revealRow(within: ParentNode, name: string): boolean {\n const row = [...within.querySelectorAll<HTMLElement>(\".set-row\")].find((r) => r.dataset.name === name);\n if (!row) return false;\n const toggle = row.querySelector<HTMLButtonElement>(\".set-rowline > .set-expand\");\n if (toggle && toggle.getAttribute(\"aria-expanded\") !== \"true\") toggle.click();\n const still = typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n row.scrollIntoView({ block: \"center\", behavior: still ? \"auto\" : \"smooth\" });\n // Restarted, so landing on the same row twice lights it twice.\n row.classList.remove(\"landed\");\n void row.offsetWidth;\n row.classList.add(\"landed\");\n row.addEventListener(\"animationend\", () => row.classList.remove(\"landed\"), { once: true });\n return true;\n}\n\n/** After a \"+ Add\" re-renders a list, bring the new (last) row into view and\n * focus its name input (`.set-name`, else the first input). */\nexport function focusNewRow(listEl: HTMLElement | null | undefined): void {\n const last = listEl?.lastElementChild;\n if (!(last instanceof HTMLElement)) return;\n last.scrollIntoView({ block: \"nearest\" });\n const name = last.querySelector<HTMLInputElement>(\"input.set-name\") ?? last.querySelector<HTMLInputElement>(\"input\");\n name?.focus();\n}\n\nexport interface DupGuard {\n /** Forget the previous render's inputs (call at the top of render). */\n reset(): void;\n /** Register a name input; `key` returns the comparison string (default the\n * input's value). Re-checks live on input. `also` = extra inputs that change the key. */\n track(input: HTMLInputElement, key?: () => string, also?: HTMLInputElement[]): void;\n /** Re-paint duplicates; true if any two non-blank keys collide. */\n check(): boolean;\n /** The first input currently flagged (after a fresh check), or null. */\n firstDuplicate(): HTMLInputElement | null;\n}\n\n/** Case-insensitive duplicate-name detection: paints clashing inputs `.invalid`\n * and lets a Save gate block + jump via `firstDuplicate()`. */\nexport function dupGuard(): DupGuard {\n const items: Array<{ input: HTMLInputElement; key: () => string }> = [];\n const check = (): boolean => {\n const counts = new Map<string, number>();\n for (const it of items) { const k = it.key().trim().toLowerCase(); if (k) counts.set(k, (counts.get(k) ?? 0) + 1); }\n let any = false;\n for (const it of items) {\n const k = it.key().trim().toLowerCase();\n const dup = !!k && (counts.get(k) ?? 0) > 1;\n it.input.classList.toggle(\"invalid\", dup);\n if (dup) { it.input.title = \"Duplicate name. Names must be unique.\"; any = true; }\n else if (it.input.title === \"Duplicate name. Names must be unique.\") it.input.removeAttribute(\"title\");\n }\n return any;\n };\n return {\n reset() { items.length = 0; },\n track(input, key = () => input.value, also) {\n items.push({ input, key });\n input.addEventListener(\"input\", check);\n for (const a of also ?? []) a.addEventListener(\"input\", check);\n },\n check,\n firstDuplicate() { check(); return items.find((it) => it.input.classList.contains(\"invalid\"))?.input ?? null; },\n };\n}\n\n// --- the settings dialog ------------------------------------------------------\n\nexport interface SettingsSectionHandle {\n /** The first invalid control to jump to, blocking Save (e.g. a duplicate name). */\n firstInvalid?(): HTMLElement | null;\n}\n\nexport interface SettingsSection {\n id: string;\n label: string;\n /** Optional left-rail group heading above this tab. */\n group?: string;\n /** Build the section body into `host`; called fresh on each open so it reads\n * the current data. Return a handle (for the Save gate). */\n mount(host: HTMLElement): SettingsSectionHandle;\n}\n\nexport interface SettingsDialogOptions {\n title: string;\n sections: SettingsSection[];\n /** Fired when Save is clicked and every section validates. The app reads its\n * own section state and persists. May be async; the dialog stays open until\n * it resolves, then closes. */\n onSave: () => void | Promise<void>;\n}\n\nexport interface SettingsDialog {\n /** Open (optionally on a given tab id). Sections re-mount from current data. */\n open(tabId?: string): void;\n destroy(): void;\n}\n\n/** Build a settings dialog. The frame owns the modal, the tab rail, save-on-Save\n * (with a validation gate), and Cancel; the sections own their content + state. */\nexport function mountSettingsDialog(opts: SettingsDialogOptions): SettingsDialog {\n const dialog = el(\"dialog\", \"settings-dialog\");\n const form = el(\"form\", \"settings-form\"); form.method = \"dialog\";\n const tabs = el(\"nav\", \"settings-tabs\");\n const panels = el(\"div\", \"settings-panels\");\n const error = el(\"p\", \"settings-error\"); error.hidden = true;\n const cancel = el(\"button\", \"settings-cancel\", \"Cancel\"); cancel.type = \"button\"; cancel.value = \"cancel\";\n const save = el(\"button\", \"settings-save primary\", \"Save\"); save.value = \"save\";\n\n const panelFor = new Map<string, HTMLElement>();\n const tabFor = new Map<string, HTMLButtonElement>();\n const handles = new Map<string, SettingsSectionHandle>();\n let active = opts.sections[0]?.id ?? \"\";\n\n let lastGroup: string | undefined;\n for (const s of opts.sections) {\n if (s.group && s.group !== lastGroup) { tabs.append(el(\"div\", \"settings-group\", s.group)); lastGroup = s.group; }\n const tab = el(\"button\", \"settings-tab\", s.label); tab.type = \"button\"; tab.dataset[\"tab\"] = s.id;\n tab.addEventListener(\"click\", () => showTab(s.id));\n tabs.append(tab); tabFor.set(s.id, tab);\n const panel = el(\"section\", \"settings-panel\"); panel.dataset[\"panel\"] = s.id; panel.hidden = true;\n panels.append(panel); panelFor.set(s.id, panel);\n }\n\n function showTab(id: string): void {\n active = id;\n for (const [sid, t] of tabFor) t.classList.toggle(\"active\", sid === id);\n for (const [sid, p] of panelFor) p.hidden = sid !== id;\n }\n\n form.append(\n el(\"h2\", \"settings-title\", opts.title),\n el(\"div\", \"settings-body\", tabs, panels),\n el(\"div\", \"settings-actions\", error, cancel, save),\n );\n dialog.append(form);\n document.body.append(dialog);\n\n // Enter should not submit while editing a field; only the Save button does.\n form.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && (e.target as HTMLElement)?.tagName === \"INPUT\") e.preventDefault();\n });\n\n cancel.addEventListener(\"click\", () => dialog.close(\"cancel\"));\n save.addEventListener(\"click\", (e) => {\n e.preventDefault();\n error.hidden = true;\n for (const s of opts.sections) {\n const bad = handles.get(s.id)?.firstInvalid?.();\n if (bad) { showTab(s.id); bad.focus(); error.textContent = \"Fix the highlighted fields first.\"; error.hidden = false; return; }\n }\n void (async () => { await opts.onSave(); dialog.close(\"save\"); })();\n });\n\n return {\n open(tabId) {\n handles.clear();\n for (const s of opts.sections) {\n const panel = panelFor.get(s.id)!;\n panel.replaceChildren();\n handles.set(s.id, s.mount(panel));\n }\n error.hidden = true;\n showTab(tabId ?? active ?? opts.sections[0]?.id ?? \"\");\n dialog.showModal();\n },\n destroy() { dialog.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 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\";\n\n/** The pin glyph (shared across the family; byte-identical everywhere). */\nconst PIN_ICON = `<svg viewBox=\"0 0 24 24\" width=\"14\" height=\"14\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"><line x1=\"12\" y1=\"17\" x2=\"12\" y2=\"22\"/><path d=\"M9 10.8a2 2 0 0 1-1.1 1.8l-1.8.9A2 2 0 0 0 5 15.3V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.7a2 2 0 0 0-1.1-1.8l-1.8-.9A2 2 0 0 1 15 10.8V7a1 1 0 0 1 1-1 2 2 0 0 0 0-4H8a2 2 0 0 0 0 4 1 1 0 0 1 1 1z\"/></svg>`;\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 b.innerHTML = PIN_ICON;\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\" : \"Click to 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 exit half of the panel motion vocabulary.\n//\n// Enters are pure CSS: they run on insertion. Exits cannot be, because the node\n// has to OUTLIVE the close for the animation to play and then be torn down. So\n// every closing surface routes its teardown through here instead of removing\n// itself directly.\n//\n// Patterpad's `closeWithExit`, lifted. It was already app-agnostic, and the\n// shell had grown a second copy of the same twelve lines inside `confirm.ts`,\n// which is the drift this package exists to stop: one motion vocabulary, drawn\n// one way. `tokens.css` already carried the keyframes and the durations; this\n// is the missing half that plays them.\n// ---------------------------------------------------------------------------\n\n/**\n * Play the close animation on `element`, then run `done` exactly once.\n *\n * `done` does the real teardown: remove the node, drop listeners, unmount\n * whatever was inside it. Under `prefers-reduced-motion` the duration reads 0\n * and `done` runs synchronously, so reduced motion means instant rather than\n * an invisible wait.\n */\nexport function closeWithExit(element: HTMLElement, done: () => void): void {\n element.classList.add(\"closing\");\n // getComputedStyle flushes pending style, so this reflects the `.closing`\n // rule we just added rather than the state before it.\n const seconds = parseFloat(getComputedStyle(element).animationDuration) || 0;\n if (seconds === 0) { done(); return; }\n\n let fired = false;\n const finish = (): void => {\n if (fired) return;\n fired = true;\n element.removeEventListener(\"animationend\", onEnd);\n clearTimeout(timer);\n done();\n };\n // Only OUR animation ending counts. Content inside the panel (rows, pills,\n // spinners) raises its own animationend, and an inner one firing first would\n // tear the panel down mid-fade.\n const onEnd = (e: AnimationEvent): void => { if (e.target === element) finish(); };\n element.addEventListener(\"animationend\", onEnd);\n // Fallback: animationend is missed if the element is hidden or the animation\n // is interrupted, and a panel that never tears down is worse than an abrupt one.\n const timer = setTimeout(finish, seconds * 1000 + 120);\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 is native <dialog> + showModal() so the focus trap and\n// Esc handling come free. Two deliberate corrections over that original,\n// kept when Patterpad migrates here: the destructive button wears --danger\n// (a required host token - the surface one mistakenly used --accent), and\n// initial focus lands on Cancel so a stray Enter cannot destroy anything.\n//\n// Styles ship as confirm.css (import it beside tokens.css). Callers keep the\n// elide-the-dialog rule: when nothing is at stake, don't ask.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface ConfirmOptions {\n title: string;\n body: string;\n /** The destructive button's label (\"Delete box\", \"Restart\"). */\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 const cancel = el(\"button\", \"confirm-btn cancel\", \"Cancel\");\n const danger = el(\"button\", \"confirm-btn danger\", opts.confirmLabel);\n cancel.type = \"button\";\n danger.type = \"button\";\n const dlg = el(\n \"dialog\",\n \"confirm-dialog\",\n el(\"div\", \"confirm-title\", opts.title),\n el(\"div\", \"confirm-body\", opts.body),\n el(\"div\", \"confirm-actions\", cancel, danger),\n );\n dlg.setAttribute(\"aria-label\", opts.title);\n document.body.append(dlg);\n\n let done = false;\n const finish = (ok: boolean): void => {\n if (done) return;\n done = true;\n resolve(ok);\n // Exit motion, shared with every other closing surface (exit.ts): .closing\n // swaps the enter keyframes for their reverse, and teardown is instant\n // under prefers-reduced-motion.\n closeWithExit(dlg, () => { dlg.close(); dlg.remove(); });\n };\n\n cancel.addEventListener(\"click\", () => finish(false));\n danger.addEventListener(\"click\", () => finish(true));\n // Esc arrives as the dialog's cancel event; route it through the exit motion.\n dlg.addEventListener(\"cancel\", (e) => { e.preventDefault(); finish(false); });\n // A mousedown that lands on the dialog element itself is the backdrop.\n dlg.addEventListener(\"mousedown\", (e) => { if (e.target === dlg) finish(false); });\n\n dlg.showModal();\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\";\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 const dialog = el(\"dialog\", \"shell-about\");\n if (opts.wordmark !== undefined) {\n const mark = el(\"div\", \"shell-about-mark\");\n mark.innerHTML = opts.wordmark;\n dialog.append(mark);\n }\n dialog.append(\n el(\"div\", \"shell-about-name\", opts.appName),\n el(\"div\", \"shell-about-version\", `Version ${opts.version}`),\n );\n if (opts.blurb !== undefined) dialog.append(el(\"p\", \"shell-about-blurb\", opts.blurb));\n if (opts.credits !== undefined) dialog.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 dialog.append(row);\n }\n\n const close = el(\"button\", \"shell-about-close\", \"Close\");\n close.type = \"button\";\n dialog.append(el(\"div\", \"shell-about-actions\", close));\n dialog.setAttribute(\"aria-label\", `About ${opts.appName}`);\n document.body.append(dialog);\n\n let done = false;\n const finish = (): void => {\n if (done) return;\n done = true;\n dialog.close();\n dialog.remove();\n resolve();\n };\n close.addEventListener(\"click\", finish);\n dialog.addEventListener(\"close\", finish);\n dialog.showModal();\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 { 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 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\", \"✕\");\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 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// Property names: what an author may call a value the story reads and writes.\n//\n// This belongs to the shell for the same reason gameIds do (see ids.ts): it\n// follows from the SHAPE these apps share - a project of data files, authored in\n// an editor, compiled for something else to run - rather than from any one\n// subject. Nothing here needs the words card, beat, hand or scene.\n//\n// UNLIKE gameIds, the rule is not a house style. It is dictated by the\n// expression language both families embed (`@wildwinter/expr`), and every clause\n// below is a failure observed by compiling one:\n//\n// parser.ts tokenises an identifier as /[a-zA-Z_][a-zA-Z0-9_]*/ and then\n// FOLDS it: `name: propTok.value.toLowerCase()`.\n//\n// @patter.is_night -> [\"sv\",\"patter\",\"is_night\"] reachable\n// @patter.isNight -> [\"sv\",\"patter\",\"isnight\"] folded: a declaration\n// spelled `isNight` is\n// one nothing can reach\n// @patter.is-night -> [\"bin\",\"-\",[\"sv\",\"patter\",\"is\"], NOT an error: it is\n// [\"s\",\"night\"]] SUBTRACTION. A hyphen\n// silently compiles to\n// a different meaning\n// @patter.is night -> parse error\n// @patter.9lives -> parse error (a name cannot start with a digit)\n// @patter.not -> parse error (`true false and or not` are keywords)\n//\n// The hyphen line is why this is worth a shared rule rather than a per-app\n// check: every other violation is loud, and that one quietly means something\n// else. It is also why a property name is NOT a gameId with different spelling -\n// a gameId is built out of hyphens, and here a hyphen is an operator.\n//\n// DEPENDENCY-FREE on purpose, ids.ts's reasoning verbatim: the apps' compilers,\n// CLIs and embedded runtimes need these rules far from any UI, so they keep\n// their own copies and assert parity with these in a test.\n// ---------------------------------------------------------------------------\n\n/** The five words `@wildwinter/expr` lexes as keywords, so a property may not be\n * called any of them: the reference does not resolve, it fails to parse. Kept\n * here as a copy for the same reason the rules are (no dependency), and each\n * app probes the real parser to prove the list is still true. */\nexport const RESERVED_PROPERTY_NAMES: readonly string[] = [\"true\", \"false\", \"and\", \"or\", \"not\"];\n\n/** What an editor should tell an author, in one line. */\nexport const PROPERTY_NAME_HINT =\n \"Lower case letters, digits and underscores. Cannot start with a digit. No hyphens or spaces.\";\n\n/**\n * Coerce a label into a legal property name: lower case, apostrophes dropped,\n * runs of anything else to a single underscore, no trailing underscore, an\n * underscore in front if it would otherwise start with a digit, and one behind\n * if it would otherwise be a keyword.\n *\n * A LEADING underscore is kept where the author typed one, because `_private` is\n * a legal name and a meaningful choice; only the ends that would be illegal are\n * touched.\n *\n * May return \"\", when there was nothing usable in the input. Callers treat that\n * as \"no name yet\" rather than as a name.\n */\nexport function propertyNameify(text: string): string {\n const trimmed = text.trim();\n // An underscore the author actually typed is kept; one that is only the ghost of\n // leading punctuation is not. That is the difference between `_private` and `!gold`.\n const deliberateLeading = trimmed.startsWith(\"_\");\n let out = trimmed.toLowerCase().replace(/['’]/g, \"\")\n .replace(/[^a-z0-9_]+/g, \"_\").replace(/_+/g, \"_\").replace(/^_+|_+$/g, \"\");\n if (out === \"\") return \"\";\n if (deliberateLeading || /^[0-9]/.test(out)) out = `_${out}`;\n if (RESERVED_PROPERTY_NAMES.includes(out)) out = `${out}_`;\n return out;\n}\n\n/**\n * Is this a name the expression language can actually reach? Lower case letters,\n * digits and underscores, not starting with a digit, and not a keyword.\n *\n * \"\" is NOT legal: an unnamed declaration is an unfinished row, which is a\n * different thing from an illegal one, and callers distinguish them.\n */\nexport function isValidPropertyName(name: string): boolean {\n return /^[a-z_][a-z0-9_]*$/.test(name) && !RESERVED_PROPERTY_NAMES.includes(name);\n}\n\n/**\n * The one thing that is only a case difference, e.g. `isNight` -> `isnight`.\n *\n * Worth its own question because it is the only violation that can be repaired\n * without guessing at intent: the language already folds every REFERENCE, so\n * folding the declaration to match changes nothing observable, where inventing\n * an underscore for a space is a decision about what the author meant. Loaders\n * fold this class silently and report the rest.\n */\nexport function isCaseOnlyPropertyName(name: string): boolean {\n return !isValidPropertyName(name) && isValidPropertyName(name.toLowerCase());\n}\n","// ---------------------------------------------------------------------------\n// The manners for a field that names a declared property.\n//\n// The RULE is property-names.ts, which is dependency-free because each app's\n// domain layer keeps its own copy of it. This is the other half: how a field\n// BEHAVES while somebody types a name into it, which only UI touches, so the\n// shell owns it outright.\n//\n// The manners are the gameId editor's, adopted rather than re-argued (see\n// id-editor.ts): an illegal name is marked and blocks Save, and Tab coerces it,\n// in the field, where it can be seen and undone. Nothing is rewritten as it is\n// typed. Patterpad's first pass at this folded every keystroke to lower case;\n// that is the manner the id editor had already rejected, on the grounds that an\n// app which silently renames a thing has made a decision that was not its to\n// make, and a property name is exactly that kind of thing: the author is naming\n// something their own conditions will call.\n//\n// It arrived here as two copies, one per app, written in one session and already\n// carrying the same forty lines of fault wording. Lifting them before they could\n// drift is the whole reason this file exists.\n// ---------------------------------------------------------------------------\n\nimport { isValidPropertyName, propertyNameify, RESERVED_PROPERTY_NAMES } from \"./property-names.js\";\n\n/** Re-check callbacks for bound REFERENCE fields, so a host can say \"the declarations\n * changed\" without holding a handle per row. */\nconst refSyncs = new WeakMap<HTMLInputElement, () => void>();\n\n/**\n * One line saying what is wrong with this name, or undefined when nothing is.\n *\n * Each branch names what actually HAPPENS rather than reciting the rule, because\n * the person reading it is usually somebody wondering why a condition does\n * nothing. Kept to one line: it lands in a rollover on the field, and hosts\n * repeat it in the Save error rather than inventing a summary of their own.\n */\nexport function propertyNameProblem(name: string): string | undefined {\n if (isValidPropertyName(name)) return undefined;\n const suggestion = propertyNameify(name);\n const tail = suggestion ? ` Try \"${suggestion}\", or press Tab.` : \"\";\n if (RESERVED_PROPERTY_NAMES.includes(name.toLowerCase())) {\n return `Cannot be used: \"${name.toLowerCase()}\" is a keyword in expressions.${tail}`;\n }\n // The one that is not an error anywhere else: `@scope.a-b` compiles to a-minus-b.\n if (name.includes(\"-\")) return `Cannot be used: a hyphen reads as subtraction in an expression.${tail}`;\n if (/^[0-9]/.test(name)) return `Cannot be used: a name cannot start with a digit.${tail}`;\n if (name !== name.toLowerCase()) {\n return `Cannot be used: expressions fold names, so this would look for \"${name.toLowerCase()}\".${tail}`;\n }\n return `Cannot be used: only lower case letters, digits and underscores.${tail}`;\n}\n\n/**\n * Wire a name field: mark it live, explain the fault in its rollover, and coerce\n * on Tab. `onCommit` receives what the author typed, VERBATIM, on every input.\n *\n * `.illegal` rather than dupGuard's `.invalid`, because both can be true of one\n * field and `dupGuard.check()` owns that class: it would clear this marking every\n * time it ran, including at the moment of a blocked Save.\n *\n * An empty field is not marked. An unfinished row is a different thing from an\n * illegal one, and hosts prune blank names on save.\n */\nexport function bindPropertyName(\n input: HTMLInputElement,\n onCommit: (value: string) => void,\n opts: { hint?: string } = {},\n): void {\n const hint = opts.hint;\n const sync = (): void => {\n const v = input.value.trim();\n const problem = v === \"\" ? undefined : propertyNameProblem(v);\n input.classList.toggle(\"illegal\", !!problem);\n if (problem) input.title = problem;\n else if (input.title.startsWith(\"Cannot be used\")) {\n if (hint) input.title = hint;\n else input.removeAttribute(\"title\");\n }\n };\n input.addEventListener(\"input\", () => { onCommit(input.value); sync(); });\n input.addEventListener(\"keydown\", (e) => {\n // The opt-in coercion, the id editor's gesture. Shift+Tab is left alone: it is\n // how somebody leaves a field they have not finished with.\n if (e.key !== \"Tab\" || e.shiftKey) return;\n const typed = input.value.trim();\n if (typed === \"\" || isValidPropertyName(typed)) return;\n const coerced = propertyNameify(typed);\n if (!coerced || coerced === input.value) return;\n e.preventDefault();\n input.value = coerced;\n onCommit(coerced);\n sync();\n });\n sync();\n}\n\n/**\n * The first field in `host` holding a name no expression could reach, for a Save\n * gate. Hosts hand this back beside `dupGuard.firstDuplicate()`: two faults, two\n * sets of words, one gate.\n */\nexport function firstIllegalPropertyName(host: ParentNode): HTMLInputElement | null {\n return host.querySelector<HTMLInputElement>(\"input.illegal\");\n}\n\n// ---------------------------------------------------------------------------\n// Reference fields, which are a different job from declaration fields.\n//\n// A declaration NAMES a property; a reference POINTS at one. The expression\n// parser folds every reference, so `@world.isNight` reaches `isnight` perfectly\n// well and complaining about the capital would be inventing a rule the language\n// does not have. Everything else still bites, and harder: no declaration may\n// contain a hyphen, so `is-night` in a reference can never match anything that\n// exists.\n//\n// And a reference has a fault a declaration cannot have: naming something that\n// was never declared. That is not a grammar question, so the host supplies the\n// list. The cost of getting it wrong is quiet - a coverage driver pointed at a\n// property nobody declared feeds a value nobody reads, the branches it was meant\n// to exercise stay unexercised, and the report shows them as never hit, which\n// reads as \"this content is unreachable\" rather than \"that name is a typo\".\n// ---------------------------------------------------------------------------\n\nexport interface PropertyRefOptions {\n /** The names that exist, read fresh on every check so a declaration added in the\n * same dialog clears the complaint. Compared case-insensitively, since the\n * parser folds. */\n known: () => string[];\n /** The scope token this field addresses, for the wording (\"no @world property...\"). */\n scope?: string;\n /** Shown on the field when there is nothing to complain about. */\n hint?: string;\n}\n\n/** The closest known name, when one is close enough to be worth naming. Deliberately\n * narrow: a wrong suggestion is worse than none, so it wants a shared prefix or a\n * single-character slip, not the least-bad match in the list. */\nfunction nearest(name: string, known: string[]): string | undefined {\n const n = name.toLowerCase();\n const byPrefix = known.filter((k) => k.toLowerCase().startsWith(n) || n.startsWith(k.toLowerCase()));\n if (byPrefix.length === 1) return byPrefix[0];\n let best: string | undefined;\n let bestScore = 3; // strictly better than \"three edits away\"\n for (const k of known) {\n const a = k.toLowerCase();\n if (Math.abs(a.length - n.length) > 2) continue;\n // Levenshtein, small strings, no need for a matrix beyond two rows.\n let prev = Array.from({ length: n.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i++) {\n const row = [i];\n for (let j = 1; j <= n.length; j++) {\n row[j] = Math.min(prev[j]! + 1, row[j - 1]! + 1, prev[j - 1]! + (a[i - 1] === n[j - 1] ? 0 : 1));\n }\n prev = row;\n }\n const d = prev[n.length]!;\n if (d < bestScore) { bestScore = d; best = k; }\n }\n return best;\n}\n\n/** Why this reference cannot be used, or undefined when it can. */\nexport function propertyRefProblem(name: string, opts: PropertyRefOptions): string | undefined {\n const scope = opts.scope ? `@${opts.scope}` : \"that scope\";\n // Case is NOT a fault here: the parser folds references.\n const folded = name.toLowerCase();\n if (!isValidPropertyName(folded)) {\n const suggestion = propertyNameify(name);\n const tail = suggestion ? ` Try \"${suggestion}\", or press Tab.` : \"\";\n if (RESERVED_PROPERTY_NAMES.includes(folded)) return `Cannot be used: \"${folded}\" is a keyword in expressions.${tail}`;\n if (name.includes(\"-\")) return `Cannot be used: a hyphen reads as subtraction in an expression.${tail}`;\n if (/^[0-9]/.test(name)) return `Cannot be used: a name cannot start with a digit.${tail}`;\n return `Cannot be used: only lower case letters, digits and underscores.${tail}`;\n }\n const known = opts.known();\n if (known.some((k) => k.toLowerCase() === folded)) return undefined;\n const near = nearest(folded, known);\n if (near) return `No ${scope} property is called \"${name}\". Did you mean \"${near}\"?`;\n if (known.length === 0) return `No ${scope} properties are declared yet. Declare one above first.`;\n const shown = known.slice(0, 3).map((k) => `\"${k}\"`).join(\", \");\n const more = known.length > 3 ? `, and ${known.length - 3} more` : \"\";\n return `No ${scope} property is called \"${name}\". Declared: ${shown}${more}.`;\n}\n\n/**\n * Wire a field that POINTS at a declared property: same manners as a declaration\n * field (mark, do not rewrite, Tab coerces), plus the declared names offered as a\n * datalist, because the rule is declare-then-reference and a picker makes that\n * painless rather than pedantic.\n *\n * Marks `.illegal` for an undeclared name too, which gates Save wherever the host\n * already asks `firstIllegalPropertyName`.\n */\nexport function bindPropertyRef(\n input: HTMLInputElement,\n onCommit: (value: string) => void,\n opts: PropertyRefOptions,\n): void {\n const list = document.createElement(\"datalist\");\n list.id = `shell-propref-${Math.random().toString(36).slice(2, 9)}`;\n input.setAttribute(\"list\", list.id);\n\n // Attached lazily, not once at bind. A datalist has to be IN the document to do anything, and it is\n // attached as a sibling of the input - so binding a field that has not been appended yet (which is\n // the ordinary order for a builder that makes its nodes and then mounts them) silently produced no\n // autocomplete at all: no error, nothing to see. This re-tries on every sync, so it heals as soon as\n // the field reaches the tree.\n const attach = (): void => {\n if (!list.isConnected && input.parentNode) input.after(list);\n };\n\n const sync = (): void => {\n attach();\n const known = opts.known();\n list.replaceChildren(...known.map((k) => { const o = document.createElement(\"option\"); o.value = k; return o; }));\n const v = input.value.trim();\n const problem = v === \"\" ? undefined : propertyRefProblem(v, opts);\n input.classList.toggle(\"illegal\", !!problem);\n if (problem) input.title = problem;\n else if (input.title.startsWith(\"Cannot be used\") || input.title.startsWith(\"No \")) {\n if (opts.hint) input.title = opts.hint;\n else input.removeAttribute(\"title\");\n }\n };\n refSyncs.set(input, sync);\n\n input.addEventListener(\"input\", () => { onCommit(input.value); sync(); });\n input.addEventListener(\"keydown\", (e) => {\n if (e.key !== \"Tab\" || e.shiftKey) return;\n const typed = input.value.trim();\n // Only a GRAMMAR fault is coercible. An undeclared name is not something Tab can\n // fix, and a legal reference in the wrong case is not a fault at all.\n if (typed === \"\" || isValidPropertyName(typed.toLowerCase())) return;\n const coerced = propertyNameify(typed);\n if (!coerced || coerced === input.value) return;\n e.preventDefault();\n input.value = coerced;\n onCommit(coerced);\n sync();\n });\n sync();\n}\n\n/**\n * Re-check every bound reference field inside `host`. Hosts call this when the\n * DECLARATIONS change, since a reference's validity depends on a list it does not\n * own: rename a property and the driver pointing at it is wrong from that moment,\n * with nothing typed into it.\n */\nexport function revalidatePropertyRefs(host: ParentNode): void {\n for (const input of host.querySelectorAll(\"input\")) refSyncs.get(input)?.();\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, digits, hyphens. Empty = auto 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 } 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/** The one badge an item flies, by priority, or null when there is nothing to\n * say. Monochrome typographic glyphs (never colour emoji), so each inherits\n * its themed colour and reads the same in Linen and Baize. */\nexport function vcBadgeFor(s: ShardVc | undefined): { glyph: string; cls: string; title: string } | null {\n if (s?.lockedBy?.length) return { glyph: icon.locked, cls: \"vc-locked\", title: `Locked by ${s.lockedBy.join(\", \")}` };\n if (s?.outOfDate) return { glyph: icon.down, cls: \"vc-stale\", title: \"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 { glyph: icon.checkedOut, cls: \"vc-mine\", title: \"Checked out by you\" };\n if (s?.dirty) return { glyph: icon.modified, cls: \"vc-dirty\", title: \"Modified - uncommitted local changes\" };\n if (s?.untracked) return { glyph: icon.untracked, cls: \"vc-new\", title: \"New - not yet committed\" };\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 { glyph: icon.readOnly, cls: \"vc-frozen\", title: \"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.\n const span = el(\"span\", { className: `vc-badge ${badge.cls}`, text: badge.glyph, tip: badge.title });\n host.append(span);\n });\n}\n\ntype Disableable = HTMLButtonElement | HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;\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. */\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 || (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\", text: icon.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\nimport { el } from \"./dom.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\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\", \"shell-ident-input\") as HTMLInputElement;\n name.placeholder = \"Your name\";\n name.value = seed?.name ?? \"\";\n const email = el(\"input\", \"shell-ident-input\") as HTMLInputElement;\n email.placeholder = \"Email (optional)\";\n email.type = \"email\";\n email.value = seed?.email ?? \"\";\n\n const skip = el(\"button\", \"shell-ident-btn\", \"Skip\");\n const save = el(\"button\", \"shell-ident-btn primary\", \"Save\");\n skip.type = \"button\"; save.type = \"button\";\n\n const dialog = el(\"dialog\", \"shell-ident\",\n el(\"div\", \"shell-ident-title\", \"Who is working here?\"),\n el(\"p\", \"shell-ident-hint\",\n \"Stamped on the comments you write. Kept in this app, never in the project, and changeable later.\"),\n el(\"div\", \"shell-ident-fields\", name, email),\n el(\"div\", \"shell-ident-actions\", skip, save),\n );\n document.body.append(dialog);\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 : undefined;\n dialog.close();\n dialog.remove();\n resolve(answer);\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 dialog.addEventListener(\"close\", () => finish(false));\n\n dialog.showModal();\n name.focus();\n });\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\nimport { el } from \"./dom.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 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. */\nconst CLASS_LABEL: Record<string, string> = {\n everyone: \"Everyone\", writing: \"Writing\", vo: \"Voice (VO)\", loc: \"Localisers\",\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 / director\",\n loc: \"context for translators\",\n};\nconst placeholder = (cls: string): string =>\n cls === EDITOR_ONLY ? \"an internal note - never exported\"\n : PLACEHOLDER[cls] ?? \"intent / rationale - why this is here\";\n\nexport interface NotesEditorOptions {\n /** What this is about, for the dialog's title (\"Notes: Arrive at the gate\"). */\n subject: string;\n notes: Notes;\n /** Commit. Called once, on close, and only when something changed. */\n save: (lines: DocLine[]) => void;\n}\n\n/** Open the notes modal. Resolves when it closes. */\nexport function openNotesEditor(opts: NotesEditorOptions): void {\n const { notes } = opts;\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 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 (notes.inherited.length > 0) {\n const from = el(\"div\", { className: \"shell-notes-inherited\" },\n el(\"div\", \"shell-notes-caption\", \"Inherited\"));\n for (const level of notes.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\", \"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\", \"shell-notes-btn primary\", \"Done\");\n const dialog = el(\"dialog\", { className: \"shell-notes\" },\n el(\"div\", { className: \"shell-notes-title\", text: `Notes: ${opts.subject}` }),\n body,\n el(\"div\", { className: \"shell-notes-actions\" }, done),\n );\n dialog.setAttribute(\"aria-label\", `Notes: ${opts.subject}`);\n document.body.append(dialog);\n\n const before = JSON.stringify(notes.own);\n // Done and Escape both end up here (dialog.close() fires the close event), so\n // the guard is what stops one gesture saving twice.\n let closed = false;\n const close = (): 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 dialog.close();\n dialog.remove();\n };\n done.addEventListener(\"click\", close);\n // Escape closes a <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 dialog.addEventListener(\"close\", close);\n\n dialog.showModal();\n areas.get(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 { 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\", text: \"✕\" });\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// 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\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): void;\n /** Feed it a progress message from the main-side host. */\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}\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(). */\n onCancel: () => void;\n /** The word for the units, for the count readout (default \"runs\"). */\n units?: string;\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\n const fill = el(\"i\", { className: \"job-fill\" });\n const bar = el(\"div\", { className: \"job-bar\" }, fill);\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\n const strip = el(\"div\", { className: \"job-strip\" }, what, bar, count, time, cancel);\n strip.hidden = true;\n host.append(strip);\n\n const view: JobProgressView = {\n element: strip,\n get visible() { return !strip.hidden; },\n\n begin(label?: string) {\n what.textContent = label ?? \"Running…\";\n count.textContent = \"\";\n time.textContent = \"\";\n fill.style.width = \"0%\";\n cancel.disabled = false;\n cancel.textContent = \"Cancel\";\n strip.hidden = false;\n },\n\n update(done: number, total: number, elapsedMs: number) {\n if (strip.hidden) strip.hidden = false;\n const pct = total > 0 ? Math.min(100, (done / total) * 100) : 0;\n fill.style.width = `${pct}%`;\n count.textContent = total > 0 ? `${done} / ${total} ${units}` : `${done} ${units}`;\n if (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 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 { icon } 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 /** 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.subject} changed in the editor. ${opts.advice ?? \"Restart to play the new version.\"}` }),\n el(\"button\", { className: \"stale-bar-go\", text: `${icon.restart} ${label}`,\n onClick: () => opts.onRestart() }),\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 { icon } 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, in the app's own address. Set in the mono face. */\n where?: string;\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\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 const close = opts.onClose === undefined ? null : el(\"button\", {\n className: \"stepbar-nav stepbar-close\", text: icon.close,\n tip: opts.closeTip ?? \"Close\", onClick: opts.onClose,\n });\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\", text: icon.back,\n tip: opts.tips?.prev ?? \"Previous\", onClick: () => step(-1) }),\n el(\"span\", { className: \"stepbar-of\", text: `${index + 1}/${items.length}` }),\n el(\"button\", { className: \"stepbar-nav\", text: icon.forward,\n tip: opts.tips?.next ?? \"Next\", onClick: () => step(1) }),\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 : el(\"span\", { className: \"stepbar-where\", text: 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\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}\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 };\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 = (glyph: string, tip: string, onClick: () => void): HTMLButtonElement => {\n const b = document.createElement(\"button\");\n b.type = \"button\";\n b.className = \"shell-histnav-btn\";\n b.textContent = glyph;\n b.dataset[\"tip\"] = tip;\n b.setAttribute(\"aria-label\", tip);\n b.addEventListener(\"click\", onClick);\n return b;\n };\n const back = btn(\"\\u2190\", \"Back\", onBack);\n const forward = btn(\"\\u2192\", \"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 { 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 /** Shortcut hint appended to the tooltip, e.g. \"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. Written as `data-tip` (the themed tooltip)\n * and as the accessible name; never as `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 noun = cfg[side].label ?? side;\n const hint = cfg[side].shortcutHint ? ` (${cfg[side].shortcutHint})` : \"\";\n return `${isOpen ? \"Hide\" : \"Show\"} ${noun}${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","// ---------------------------------------------------------------------------\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/** 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. */\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 || (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\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 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. */\nconst CLASS_LABEL: Record<string, string> = {\n everyone: \"Everyone\", writing: \"Writing\", vo: \"Voice (VO)\", loc: \"Localisers\",\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};\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 subject: string;\n notes: Notes;\n /** Commit. Called once, on close, and only when something changed. */\n save: (lines: DocLine[]) => void;\n}\n\n/** Open the notes modal. Resolves when it closes. */\nexport function openNotesEditor(opts: NotesEditorOptions): void {\n const { notes } = opts;\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 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 (notes.inherited.length > 0) {\n const from = el(\"div\", { className: \"shell-notes-inherited\" },\n el(\"div\", \"shell-notes-caption\", \"Inherited\"));\n for (const level of notes.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({ title: `Notes: ${opts.subject}`, className: \"shell-notes\", onClose: commit });\n frame.body.append(body);\n frame.actions.append(done);\n done.addEventListener(\"click\", () => { commit(); frame.close(); });\n\n frame.open();\n areas.get(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 /** 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.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, in the app's own address. Set in the mono face. */\n where?: string;\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 : el(\"span\", { className: \"stepbar-where\", text: 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}\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 };\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"]}
|