@wildwinter/app-shell 0.21.0 → 0.23.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/dist/index.cjs +4 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -3
- package/dist/index.d.ts +16 -3
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/settings.css +8 -5
- package/dist/settings.css.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/dom.ts","../src/colour.ts","../src/icons.ts","../src/pane-shell.ts","../src/menu.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/id-editor.ts","../src/vc-view.ts","../src/identity.ts","../src/notes-editor.ts","../src/comments.ts","../src/job-view.ts","../src/job.ts","../src/tooltip.ts","../src/save.ts","../src/save-view.ts","../src/stale.ts","../src/stepper.ts"],"names":["label","el","active","timer","GAP","thread"],"mappings":"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","file":"index.cjs","sourcesContent":["// ---------------------------------------------------------------------------\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\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) { node.dataset[\"tip\"] = props.tip; node.setAttribute(\"aria-label\", props.tip); }\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 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/** A captioned field: `<label class=\"shell-labelled\"><span class=\"shell-fieldcap\">…</span>control</label>`. */\nexport function labelled(label: string, control: HTMLElement): HTMLElement {\n const w = el(\"label\", \"shell-labelled\");\n w.append(el(\"span\", \"shell-fieldcap\", label), control);\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 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 /** 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}\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 open: Record<PaneSide, boolean> = { nav: opts.initial?.open?.nav ?? true, inspector: opts.initial?.open?.inspector ?? true };\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\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 ? \"none\" : \"\";\n inspResizer.style.display = cfg.inspector.resizable === false ? \"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 { 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// Shell-owned menu naming: the labels + accelerators for the pane-frame menu\n// items, single-sourced so every app in the family names them identically\n// (and a rename here flows to all of them). Pure data - no electron, no DOM -\n// so a main-process menu builder can import it. Each app builds its own\n// MenuItem from these and wires the click to its shell controller.\n// ---------------------------------------------------------------------------\n\nexport interface MenuLabel {\n label: string;\n /** Electron accelerator string, when the item has one. */\n accelerator?: string;\n}\n\n/**\n * The Edit menu items the family shares.\n *\n * Undo and Redo are here and the MECHANISM is not, which is the whole finding\n * about undo: Patterpad's is ProseMirror's document history, Storyletter's is a\n * replay of file bytes, and those have nothing in common but this. See\n * `design/shared-shell.md`.\n *\n * The rule both apps discovered separately, and the reason these are spelled\n * out rather than left as Electron's `role: \"undo\"`: the native role runs a DOM\n * text-field undo, which knows nothing about either app's model and will\n * silently undo the wrong thing.\n */\nexport const EDIT_MENU: {\n undo: MenuLabel;\n redo: MenuLabel;\n duplicate: MenuLabel;\n find: MenuLabel;\n} = {\n undo: { label: \"Undo\", accelerator: \"CmdOrCtrl+Z\" },\n redo: { label: \"Redo\", accelerator: \"Shift+CmdOrCtrl+Z\" },\n duplicate: { label: \"Duplicate\", accelerator: \"CmdOrCtrl+D\" },\n find: { label: \"Find…\", accelerator: \"CmdOrCtrl+F\" },\n};\n\n/** The pane-frame menu items (a View menu, conventionally). */\nexport const PANE_MENU: {\n showNav: MenuLabel;\n showInspector: MenuLabel;\n resetView: MenuLabel;\n} = {\n showNav: { label: \"Show Navigator\", accelerator: \"CmdOrCtrl+1\" },\n showInspector: { label: \"Show Inspector\", accelerator: \"CmdOrCtrl+2\" },\n resetView: { label: \"Reset View\" },\n};\n\n/**\n * The Help menu items the family shares, and the one platform rule that goes\n * with them.\n *\n * Lifted from Patterpad, which is the only app that has ever had these: a\n * second app writing its own updater or About is pure waste (`design/\n * shared-shell.md`), and a suite is recognised by finding Check for Updates\n * where you left it. Storyletter will grow both; until it does, these labels\n * are the agreement rather than a description of two implementations.\n *\n * PLACEMENT, which differs by platform and is easy to get wrong:\n * - macOS keeps About in the APP menu (the one named after the product), and\n * Help carries documentation + Check for Updates only.\n * - Windows and Linux have no app menu, so About goes at the foot of Help,\n * after a separator. That is the conventional home there.\n */\nexport const HELP_MENU: {\n checkForUpdates: MenuLabel;\n} = {\n checkForUpdates: { label: \"Check for Updates\\u2026\" },\n};\n\n/** The app-menu items the family shares (macOS; the identity item also belongs in File elsewhere). */\nexport const APP_MENU: {\n userInfo: MenuLabel;\n} = {\n /** Name + optional email, used to sign edits and comments. One label across the suite. */\n userInfo: { label: \"User Information\\u2026\" },\n};\n\n/** What an app must tell the shell to build its named menu items. */\nexport interface MenuNaming {\n /** The product, as it appears in menus: \"Patterpad\", \"Storyletter\". */\n appName: string;\n /** The product's own documentation URL. Omit while an app has no docs yet and\n * the item is rendered disabled rather than opening a broken link. */\n docsUrl?: string;\n /** The suite's documentation home, shared by every app in the family. */\n suiteDocsUrl?: string;\n /** The suite, as it appears in \"<suite> Documentation Home\". Defaults to \"Patter\". */\n suiteName?: string;\n}\n\n/** A named menu item: the label, plus the URL it opens (absent = nothing wired yet). */\nexport interface NamedMenuItem extends MenuLabel {\n url?: string;\n /** False when the app has not supplied a URL, so the builder can render it\n * disabled instead of opening nothing. A placeholder, honestly labelled. */\n ready: boolean;\n}\n\n/**\n * The menu labels that carry a product or suite name, so no app spells its own\n * About or Documentation item differently from the rest of the family.\n *\n * An app with no documentation site yet still gets the ITEM, marked\n * `ready: false`, so the menu has its family shape from day one and wiring the\n * URL later is a one-line change rather than a menu redesign.\n */\nexport function namedMenuItems(naming: MenuNaming): {\n about: NamedMenuItem;\n docs: NamedMenuItem;\n suiteDocs: NamedMenuItem;\n} {\n const suite = naming.suiteName ?? \"Patter\";\n return {\n about: { label: `About ${naming.appName}`, ready: true },\n docs: {\n label: `${naming.appName} Documentation`,\n ...(naming.docsUrl ? { url: naming.docsUrl } : {}),\n ready: naming.docsUrl !== undefined,\n },\n suiteDocs: {\n label: `${suite} Documentation Home`,\n ...(naming.suiteDocsUrl ? { url: naming.suiteDocsUrl } : {}),\n ready: naming.suiteDocsUrl !== undefined,\n },\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. */\nexport function expandableRow(opts: { line: HTMLElement[]; details?: HTMLElement[] }): HTMLElement {\n const row = el(\"div\", \"set-row\");\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/** 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 keeps its own visual state). */\n onToggle: (on: 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): HTMLButtonElement {\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 b.title = pinned ? \"Pinned on top: click to unpin\" : \"Click to keep on top\";\n };\n reflect();\n b.addEventListener(\"click\", () => { pinned = !pinned; reflect(); opts.onToggle(pinned); });\n return b;\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// 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}\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 }\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 // 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 long-job kit (main process): run a piece of work that takes long enough\n// to need a progress bar and a Cancel button, without freezing the app.\n//\n// The shape every editor in the family needs: coverage sweeps, big exports,\n// corpus runs. One kind of job at a time, progress reported to the windows\n// that asked for it, cancellation honoured at the next yield point, and a\n// partial result when the work can offer one.\n//\n// COOPERATIVE, NOT PARALLEL. The work still runs in the main process; it just\n// stops hogging it. `ctx.step()` is both the progress report and the yield:\n// the work hands the event loop back every few milliseconds, so IPC keeps\n// flowing, the renderer keeps painting, and Cancel is heard. The alternative\n// (a utilityProcess or a worker) buys true parallelism at the cost of\n// serialising the job's inputs and outputs across a process boundary, and a\n// second entry point per app. Reach for that only when the work is heavy\n// enough that a responsive main process is not enough.\n//\n// Electron is an OPTIONAL peer here: nothing in this file imports it. The host\n// supplies `send`, so the kit is testable headlessly and usable from any\n// message transport.\n// ---------------------------------------------------------------------------\n\n/** The channel a host should send progress on, and the shape it carries. */\nexport const JOB_PROGRESS = \"job:progress\";\n\nexport interface JobProgress {\n /** Which job this is about (\"coverage\"), so one listener can serve many. */\n kind: string;\n /** Units finished and expected. `total` 0 means \"no idea yet\". */\n done: number;\n total: number;\n /** Milliseconds since the job started, for elapsed + estimated remaining. */\n elapsedMs: number;\n}\n\n/** What the work is given: a cancellation flag and the one call that both\n * reports progress and yields. */\nexport interface JobContext {\n /** True once someone has cancelled. Check it to bail out early, or to\n * return a partial result rather than nothing. */\n readonly cancelled: boolean;\n /** Report progress and, when enough time has passed, hand the event loop\n * back. Await it: that await IS the yield. */\n step(done: number, total: number): Promise<void>;\n}\n\nexport type JobResult<T> =\n | { ok: true; value: T }\n | { cancelled: true; value?: T }\n | { error: string };\n\nexport interface JobHost {\n /** Run `work` as the job of this kind. Rejects a second start of the same\n * kind while one is in flight (the caller should have disabled the button;\n * this is the backstop). */\n start<T>(kind: string, work: (ctx: JobContext) => Promise<T>): Promise<JobResult<T>>;\n /** Ask the running job of this kind to stop. It stops at its next step(). */\n cancel(kind: string): void;\n running(kind: string): boolean;\n}\n\nexport interface JobHostOptions {\n /** Deliver a progress message to whoever is watching (typically\n * `window.webContents.send(JOB_PROGRESS, payload)` for each live window). */\n send: (channel: string, payload: JobProgress) => void;\n /** How long the work may hold the event loop between yields (default 16ms,\n * about one frame). Smaller is smoother and slower. */\n sliceMs?: number;\n /** How often a progress message may be sent (default 100ms). Progress is\n * for a human to read; flooding the channel just costs IPC. */\n reportMs?: number;\n /** Injectable clock + yield, for tests. */\n now?: () => number;\n yieldToLoop?: () => Promise<void>;\n}\n\nconst defaultYield = (): Promise<void> =>\n new Promise((resolve) => { setTimeout(resolve, 0); });\n\nexport function createJobHost(opts: JobHostOptions): JobHost {\n const now = opts.now ?? (() => Date.now());\n const handOver = opts.yieldToLoop ?? defaultYield;\n const sliceMs = opts.sliceMs ?? 16;\n const reportMs = opts.reportMs ?? 100;\n const live = new Map<string, { cancelled: boolean }>();\n\n return {\n running: (kind) => live.has(kind),\n\n cancel(kind) {\n const job = live.get(kind);\n if (job) job.cancelled = true;\n },\n\n async start<T>(kind: string, work: (ctx: JobContext) => Promise<T>): Promise<JobResult<T>> {\n if (live.has(kind)) return { error: `a ${kind} job is already running` };\n const job = { cancelled: false };\n live.set(kind, job);\n\n const started = now();\n let lastYield = started;\n // A flag, not a timestamp sentinel: on a clock that can legitimately\n // read 0, \"never reported yet\" and \"reported at 0\" must not be the\n // same state. The first step always reports, so the bar moves at once.\n let reported = false;\n let lastReport = 0;\n\n const ctx: JobContext = {\n get cancelled() { return job.cancelled; },\n async step(done: number, total: number): Promise<void> {\n const at = now();\n if (!reported || at - lastReport >= reportMs || done >= total) {\n reported = true;\n lastReport = at;\n opts.send(JOB_PROGRESS, { kind, done, total, elapsedMs: at - started });\n }\n if (at - lastYield >= sliceMs) {\n lastYield = at;\n await handOver();\n }\n },\n };\n\n try {\n const value = await work(ctx);\n return job.cancelled ? { cancelled: true, value } : { ok: true, value };\n } catch (e) {\n // A cancelled job that threw on the way out is still a cancellation:\n // the author asked it to stop, and an error report would be noise.\n if (job.cancelled) return { cancelled: true };\n return { error: e instanceof Error ? e.message : String(e) };\n } finally {\n live.delete(kind);\n }\n },\n };\n}\n","// ---------------------------------------------------------------------------\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 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\nconst tipAncestor = (n: EventTarget | null): HTMLElement | null =>\n (n instanceof Element ? n.closest<HTMLElement>(\"[data-tip]\") : null);\n\n/** Wire the one delegated controller. Idempotent; later options are ignored. */\nexport function initTooltips(opts: TooltipOptions = {}): void {\n if (inited) return;\n inited = true;\n suppressed = opts.suppressed;\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// 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 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"]}
|
|
1
|
+
{"version":3,"sources":["../src/dom.ts","../src/colour.ts","../src/icons.ts","../src/pane-shell.ts","../src/menu.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/id-editor.ts","../src/vc-view.ts","../src/identity.ts","../src/notes-editor.ts","../src/comments.ts","../src/job-view.ts","../src/job.ts","../src/tooltip.ts","../src/save.ts","../src/save-view.ts","../src/stale.ts","../src/stepper.ts"],"names":["label","el","active","timer","GAP","thread"],"mappings":"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","file":"index.cjs","sourcesContent":["// ---------------------------------------------------------------------------\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\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) { node.dataset[\"tip\"] = props.tip; node.setAttribute(\"aria-label\", props.tip); }\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 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/** A captioned field: `<label class=\"shell-labelled\"><span class=\"shell-fieldcap\">…</span>control</label>`. */\nexport function labelled(label: string, control: HTMLElement): HTMLElement {\n const w = el(\"label\", \"shell-labelled\");\n w.append(el(\"span\", \"shell-fieldcap\", label), control);\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 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 /** 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}\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 open: Record<PaneSide, boolean> = { nav: opts.initial?.open?.nav ?? true, inspector: opts.initial?.open?.inspector ?? true };\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\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 ? \"none\" : \"\";\n inspResizer.style.display = cfg.inspector.resizable === false ? \"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 { 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// Shell-owned menu naming: the labels + accelerators for the pane-frame menu\n// items, single-sourced so every app in the family names them identically\n// (and a rename here flows to all of them). Pure data - no electron, no DOM -\n// so a main-process menu builder can import it. Each app builds its own\n// MenuItem from these and wires the click to its shell controller.\n// ---------------------------------------------------------------------------\n\nexport interface MenuLabel {\n label: string;\n /** Electron accelerator string, when the item has one. */\n accelerator?: string;\n}\n\n/**\n * The Edit menu items the family shares.\n *\n * Undo and Redo are here and the MECHANISM is not, which is the whole finding\n * about undo: Patterpad's is ProseMirror's document history, Storyletter's is a\n * replay of file bytes, and those have nothing in common but this. See\n * `design/shared-shell.md`.\n *\n * The rule both apps discovered separately, and the reason these are spelled\n * out rather than left as Electron's `role: \"undo\"`: the native role runs a DOM\n * text-field undo, which knows nothing about either app's model and will\n * silently undo the wrong thing.\n */\nexport const EDIT_MENU: {\n undo: MenuLabel;\n redo: MenuLabel;\n duplicate: MenuLabel;\n find: MenuLabel;\n} = {\n undo: { label: \"Undo\", accelerator: \"CmdOrCtrl+Z\" },\n redo: { label: \"Redo\", accelerator: \"Shift+CmdOrCtrl+Z\" },\n duplicate: { label: \"Duplicate\", accelerator: \"CmdOrCtrl+D\" },\n find: { label: \"Find…\", accelerator: \"CmdOrCtrl+F\" },\n};\n\n/** The pane-frame menu items (a View menu, conventionally). */\nexport const PANE_MENU: {\n showNav: MenuLabel;\n showInspector: MenuLabel;\n resetView: MenuLabel;\n} = {\n showNav: { label: \"Show Navigator\", accelerator: \"CmdOrCtrl+1\" },\n showInspector: { label: \"Show Inspector\", accelerator: \"CmdOrCtrl+2\" },\n resetView: { label: \"Reset View\" },\n};\n\n/**\n * The Help menu items the family shares, and the one platform rule that goes\n * with them.\n *\n * Lifted from Patterpad, which is the only app that has ever had these: a\n * second app writing its own updater or About is pure waste (`design/\n * shared-shell.md`), and a suite is recognised by finding Check for Updates\n * where you left it. Storyletter will grow both; until it does, these labels\n * are the agreement rather than a description of two implementations.\n *\n * PLACEMENT, which differs by platform and is easy to get wrong:\n * - macOS keeps About in the APP menu (the one named after the product), and\n * Help carries documentation + Check for Updates only.\n * - Windows and Linux have no app menu, so About goes at the foot of Help,\n * after a separator. That is the conventional home there.\n */\nexport const HELP_MENU: {\n checkForUpdates: MenuLabel;\n} = {\n checkForUpdates: { label: \"Check for Updates\\u2026\" },\n};\n\n/** The app-menu items the family shares (macOS; the identity item also belongs in File elsewhere). */\nexport const APP_MENU: {\n userInfo: MenuLabel;\n} = {\n /** Name + optional email, used to sign edits and comments. One label across the suite. */\n userInfo: { label: \"User Information\\u2026\" },\n};\n\n/** What an app must tell the shell to build its named menu items. */\nexport interface MenuNaming {\n /** The product, as it appears in menus: \"Patterpad\", \"Storyletter\". */\n appName: string;\n /** The product's own documentation URL. Omit while an app has no docs yet and\n * the item is rendered disabled rather than opening a broken link. */\n docsUrl?: string;\n /** The suite's documentation home, shared by every app in the family. */\n suiteDocsUrl?: string;\n /** The suite, as it appears in \"<suite> Documentation Home\". Defaults to \"Patter\". */\n suiteName?: string;\n}\n\n/** A named menu item: the label, plus the URL it opens (absent = nothing wired yet). */\nexport interface NamedMenuItem extends MenuLabel {\n url?: string;\n /** False when the app has not supplied a URL, so the builder can render it\n * disabled instead of opening nothing. A placeholder, honestly labelled. */\n ready: boolean;\n}\n\n/**\n * The menu labels that carry a product or suite name, so no app spells its own\n * About or Documentation item differently from the rest of the family.\n *\n * An app with no documentation site yet still gets the ITEM, marked\n * `ready: false`, so the menu has its family shape from day one and wiring the\n * URL later is a one-line change rather than a menu redesign.\n */\nexport function namedMenuItems(naming: MenuNaming): {\n about: NamedMenuItem;\n docs: NamedMenuItem;\n suiteDocs: NamedMenuItem;\n} {\n const suite = naming.suiteName ?? \"Patter\";\n return {\n about: { label: `About ${naming.appName}`, ready: true },\n docs: {\n label: `${naming.appName} Documentation`,\n ...(naming.docsUrl ? { url: naming.docsUrl } : {}),\n ready: naming.docsUrl !== undefined,\n },\n suiteDocs: {\n label: `${suite} Documentation Home`,\n ...(naming.suiteDocsUrl ? { url: naming.suiteDocsUrl } : {}),\n ready: naming.suiteDocsUrl !== undefined,\n },\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. */\nexport function expandableRow(opts: { line: HTMLElement[]; details?: HTMLElement[] }): HTMLElement {\n const row = el(\"div\", \"set-row\");\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/** 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 b.title = pinned ? \"Pinned on top: click to unpin\" : \"Click to keep on top\";\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// 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}\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 }\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 // 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 long-job kit (main process): run a piece of work that takes long enough\n// to need a progress bar and a Cancel button, without freezing the app.\n//\n// The shape every editor in the family needs: coverage sweeps, big exports,\n// corpus runs. One kind of job at a time, progress reported to the windows\n// that asked for it, cancellation honoured at the next yield point, and a\n// partial result when the work can offer one.\n//\n// COOPERATIVE, NOT PARALLEL. The work still runs in the main process; it just\n// stops hogging it. `ctx.step()` is both the progress report and the yield:\n// the work hands the event loop back every few milliseconds, so IPC keeps\n// flowing, the renderer keeps painting, and Cancel is heard. The alternative\n// (a utilityProcess or a worker) buys true parallelism at the cost of\n// serialising the job's inputs and outputs across a process boundary, and a\n// second entry point per app. Reach for that only when the work is heavy\n// enough that a responsive main process is not enough.\n//\n// Electron is an OPTIONAL peer here: nothing in this file imports it. The host\n// supplies `send`, so the kit is testable headlessly and usable from any\n// message transport.\n// ---------------------------------------------------------------------------\n\n/** The channel a host should send progress on, and the shape it carries. */\nexport const JOB_PROGRESS = \"job:progress\";\n\nexport interface JobProgress {\n /** Which job this is about (\"coverage\"), so one listener can serve many. */\n kind: string;\n /** Units finished and expected. `total` 0 means \"no idea yet\". */\n done: number;\n total: number;\n /** Milliseconds since the job started, for elapsed + estimated remaining. */\n elapsedMs: number;\n}\n\n/** What the work is given: a cancellation flag and the one call that both\n * reports progress and yields. */\nexport interface JobContext {\n /** True once someone has cancelled. Check it to bail out early, or to\n * return a partial result rather than nothing. */\n readonly cancelled: boolean;\n /** Report progress and, when enough time has passed, hand the event loop\n * back. Await it: that await IS the yield. */\n step(done: number, total: number): Promise<void>;\n}\n\nexport type JobResult<T> =\n | { ok: true; value: T }\n | { cancelled: true; value?: T }\n | { error: string };\n\nexport interface JobHost {\n /** Run `work` as the job of this kind. Rejects a second start of the same\n * kind while one is in flight (the caller should have disabled the button;\n * this is the backstop). */\n start<T>(kind: string, work: (ctx: JobContext) => Promise<T>): Promise<JobResult<T>>;\n /** Ask the running job of this kind to stop. It stops at its next step(). */\n cancel(kind: string): void;\n running(kind: string): boolean;\n}\n\nexport interface JobHostOptions {\n /** Deliver a progress message to whoever is watching (typically\n * `window.webContents.send(JOB_PROGRESS, payload)` for each live window). */\n send: (channel: string, payload: JobProgress) => void;\n /** How long the work may hold the event loop between yields (default 16ms,\n * about one frame). Smaller is smoother and slower. */\n sliceMs?: number;\n /** How often a progress message may be sent (default 100ms). Progress is\n * for a human to read; flooding the channel just costs IPC. */\n reportMs?: number;\n /** Injectable clock + yield, for tests. */\n now?: () => number;\n yieldToLoop?: () => Promise<void>;\n}\n\nconst defaultYield = (): Promise<void> =>\n new Promise((resolve) => { setTimeout(resolve, 0); });\n\nexport function createJobHost(opts: JobHostOptions): JobHost {\n const now = opts.now ?? (() => Date.now());\n const handOver = opts.yieldToLoop ?? defaultYield;\n const sliceMs = opts.sliceMs ?? 16;\n const reportMs = opts.reportMs ?? 100;\n const live = new Map<string, { cancelled: boolean }>();\n\n return {\n running: (kind) => live.has(kind),\n\n cancel(kind) {\n const job = live.get(kind);\n if (job) job.cancelled = true;\n },\n\n async start<T>(kind: string, work: (ctx: JobContext) => Promise<T>): Promise<JobResult<T>> {\n if (live.has(kind)) return { error: `a ${kind} job is already running` };\n const job = { cancelled: false };\n live.set(kind, job);\n\n const started = now();\n let lastYield = started;\n // A flag, not a timestamp sentinel: on a clock that can legitimately\n // read 0, \"never reported yet\" and \"reported at 0\" must not be the\n // same state. The first step always reports, so the bar moves at once.\n let reported = false;\n let lastReport = 0;\n\n const ctx: JobContext = {\n get cancelled() { return job.cancelled; },\n async step(done: number, total: number): Promise<void> {\n const at = now();\n if (!reported || at - lastReport >= reportMs || done >= total) {\n reported = true;\n lastReport = at;\n opts.send(JOB_PROGRESS, { kind, done, total, elapsedMs: at - started });\n }\n if (at - lastYield >= sliceMs) {\n lastYield = at;\n await handOver();\n }\n },\n };\n\n try {\n const value = await work(ctx);\n return job.cancelled ? { cancelled: true, value } : { ok: true, value };\n } catch (e) {\n // A cancelled job that threw on the way out is still a cancellation:\n // the author asked it to stop, and an error report would be noise.\n if (job.cancelled) return { cancelled: true };\n return { error: e instanceof Error ? e.message : String(e) };\n } finally {\n live.delete(kind);\n }\n },\n };\n}\n","// ---------------------------------------------------------------------------\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 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\nconst tipAncestor = (n: EventTarget | null): HTMLElement | null =>\n (n instanceof Element ? n.closest<HTMLElement>(\"[data-tip]\") : null);\n\n/** Wire the one delegated controller. Idempotent; later options are ignored. */\nexport function initTooltips(opts: TooltipOptions = {}): void {\n if (inited) return;\n inited = true;\n suppressed = opts.suppressed;\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// 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 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"]}
|