@wildwinter/app-shell 0.38.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/LICENSES.md +62 -0
  2. package/README.md +8 -0
  3. package/dist/about.css +13 -34
  4. package/dist/about.css.map +1 -1
  5. package/dist/anchored.css +5 -5
  6. package/dist/anchored.css.map +1 -1
  7. package/dist/app-store.cjs +15 -0
  8. package/dist/app-store.cjs.map +1 -1
  9. package/dist/app-store.d.cts +25 -1
  10. package/dist/app-store.d.ts +25 -1
  11. package/dist/app-store.js +14 -1
  12. package/dist/app-store.js.map +1 -1
  13. package/dist/chunk-2ECJUTSX.js +97 -0
  14. package/dist/chunk-2ECJUTSX.js.map +1 -0
  15. package/dist/chunk-2NMVIM54.js +55 -0
  16. package/dist/chunk-2NMVIM54.js.map +1 -0
  17. package/dist/chunk-C6UVQYKI.js +70 -0
  18. package/dist/chunk-C6UVQYKI.js.map +1 -0
  19. package/dist/chunk-IU7EY6BI.js +26 -0
  20. package/dist/chunk-IU7EY6BI.js.map +1 -0
  21. package/dist/chunk-K2AOLQW4.js +82 -0
  22. package/dist/chunk-K2AOLQW4.js.map +1 -0
  23. package/dist/chunk-KGAMP5LW.js +87 -0
  24. package/dist/chunk-KGAMP5LW.js.map +1 -0
  25. package/dist/chunk-L5HOBEKL.js +534 -0
  26. package/dist/chunk-L5HOBEKL.js.map +1 -0
  27. package/dist/chunk-P4G4ERG6.js +60 -0
  28. package/dist/chunk-P4G4ERG6.js.map +1 -0
  29. package/dist/chunk-P6WLAEKZ.js +71 -0
  30. package/dist/chunk-P6WLAEKZ.js.map +1 -0
  31. package/dist/chunk-S4KWQLYN.js +66 -0
  32. package/dist/chunk-S4KWQLYN.js.map +1 -0
  33. package/dist/chunk-XZYZXY7I.js +554 -0
  34. package/dist/chunk-XZYZXY7I.js.map +1 -0
  35. package/dist/comments.css +5 -3
  36. package/dist/comments.css.map +1 -1
  37. package/dist/confirm.css +1 -54
  38. package/dist/confirm.css.map +1 -1
  39. package/dist/context-menu.cjs +250 -0
  40. package/dist/context-menu.cjs.map +1 -0
  41. package/dist/context-menu.css +64 -0
  42. package/dist/context-menu.css.map +1 -0
  43. package/dist/context-menu.d.cts +25 -0
  44. package/dist/context-menu.d.ts +25 -0
  45. package/dist/context-menu.js +5 -0
  46. package/dist/context-menu.js.map +1 -0
  47. package/dist/controls.css +155 -0
  48. package/dist/controls.css.map +1 -0
  49. package/dist/dialog.cjs +222 -0
  50. package/dist/dialog.cjs.map +1 -0
  51. package/dist/dialog.css +55 -0
  52. package/dist/dialog.css.map +1 -0
  53. package/dist/dialog.d.cts +28 -0
  54. package/dist/dialog.d.ts +28 -0
  55. package/dist/dialog.js +5 -0
  56. package/dist/dialog.js.map +1 -0
  57. package/dist/floating.cjs +83 -0
  58. package/dist/floating.cjs.map +1 -0
  59. package/dist/floating.d.cts +25 -0
  60. package/dist/floating.d.ts +25 -0
  61. package/dist/floating.js +4 -0
  62. package/dist/floating.js.map +1 -0
  63. package/dist/identity.css +15 -50
  64. package/dist/identity.css.map +1 -1
  65. package/dist/index.cjs +1307 -258
  66. package/dist/index.cjs.map +1 -1
  67. package/dist/index.d.cts +349 -149
  68. package/dist/index.d.ts +349 -149
  69. package/dist/index.js +259 -822
  70. package/dist/index.js.map +1 -1
  71. package/dist/job.css +23 -0
  72. package/dist/job.css.map +1 -1
  73. package/dist/link-status.cjs +430 -0
  74. package/dist/link-status.cjs.map +1 -0
  75. package/dist/link-status.css +76 -0
  76. package/dist/link-status.css.map +1 -0
  77. package/dist/link-status.d.cts +53 -0
  78. package/dist/link-status.d.ts +53 -0
  79. package/dist/link-status.js +5 -0
  80. package/dist/link-status.js.map +1 -0
  81. package/dist/menu.cjs +62 -1
  82. package/dist/menu.cjs.map +1 -1
  83. package/dist/menu.d.cts +88 -1
  84. package/dist/menu.d.ts +88 -1
  85. package/dist/menu.js +1 -1
  86. package/dist/notes-editor.css +3 -46
  87. package/dist/notes-editor.css.map +1 -1
  88. package/dist/pane-shell.css +16 -3
  89. package/dist/pane-shell.css.map +1 -1
  90. package/dist/property-list-D3VZCJzi.d.cts +162 -0
  91. package/dist/property-list-D3VZCJzi.d.ts +162 -0
  92. package/dist/property-list.cjs +784 -0
  93. package/dist/property-list.cjs.map +1 -0
  94. package/dist/property-list.d.cts +1 -0
  95. package/dist/property-list.d.ts +1 -0
  96. package/dist/property-list.js +4 -0
  97. package/dist/property-list.js.map +1 -0
  98. package/dist/settings.css +52 -8
  99. package/dist/settings.css.map +1 -1
  100. package/dist/stale.css +3 -0
  101. package/dist/stale.css.map +1 -1
  102. package/dist/stepper.css +35 -0
  103. package/dist/stepper.css.map +1 -1
  104. package/dist/tokens.css +19 -0
  105. package/dist/tokens.css.map +1 -1
  106. package/dist/tool-window.cjs +63 -0
  107. package/dist/tool-window.cjs.map +1 -1
  108. package/dist/tool-window.css +6 -3
  109. package/dist/tool-window.css.map +1 -1
  110. package/dist/tool-window.d.cts +67 -1
  111. package/dist/tool-window.d.ts +67 -1
  112. package/dist/tool-window.js +63 -1
  113. package/dist/tool-window.js.map +1 -1
  114. package/dist/updater-view.cjs +283 -0
  115. package/dist/updater-view.cjs.map +1 -0
  116. package/dist/updater-view.d.cts +23 -0
  117. package/dist/updater-view.d.ts +23 -0
  118. package/dist/updater-view.js +6 -0
  119. package/dist/updater-view.js.map +1 -0
  120. package/dist/updater.cjs +12 -16
  121. package/dist/updater.cjs.map +1 -1
  122. package/dist/updater.css +44 -0
  123. package/dist/updater.css.map +1 -0
  124. package/dist/updater.js +12 -16
  125. package/dist/updater.js.map +1 -1
  126. package/dist/util.cjs +78 -0
  127. package/dist/util.cjs.map +1 -0
  128. package/dist/util.d.cts +37 -0
  129. package/dist/util.d.ts +37 -0
  130. package/dist/util.js +3 -0
  131. package/dist/util.js.map +1 -0
  132. package/dist/vc.css +3 -2
  133. package/dist/vc.css.map +1 -1
  134. package/dist/welcome.cjs +215 -0
  135. package/dist/welcome.cjs.map +1 -0
  136. package/dist/welcome.css +112 -0
  137. package/dist/welcome.css.map +1 -0
  138. package/dist/welcome.d.cts +55 -0
  139. package/dist/welcome.d.ts +55 -0
  140. package/dist/welcome.js +4 -0
  141. package/dist/welcome.js.map +1 -0
  142. package/package.json +58 -2
  143. package/dist/chunk-QDPFU4WX.js +0 -43
  144. package/dist/chunk-QDPFU4WX.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/settings.ts","../src/property-names.ts","../src/property-name-field.ts","../src/property-list.ts"],"names":[],"mappings":"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","file":"chunk-L5HOBEKL.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The shared settings surface: a native <dialog> with a left-rail of grouped\n// tabs and one panel per section (mountSettingsDialog), plus the list-editing\n// core those sections lean on (expandableRow, focusNewRow, dupGuard). The app\n// supplies the sections; each holds its own state and reports the first invalid\n// control so the Save gate can block + jump. Framework here, content in the app.\n//\n// Lifted from Patterpad's Project Settings (settings-list.ts + the inlined\n// dialog logic in its renderer), with neutral class names. Styling ships as\n// settings.css.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\n\n// --- list-editing core --------------------------------------------------------\n\n/** A compact list row: a single `.set-rowline` (inline controls) plus, when\n * `details` are given, a disclosure that reveals a `.set-details` panel below,\n * so a list scans like a table with secondary fields tucked away. `name` is\n * what the row declares (a property's name, a field's), stamped on it as\n * `data-name` so `revealRow` can land on it. */\nexport function expandableRow(opts: { line: HTMLElement[]; details?: HTMLElement[]; name?: string }): HTMLElement {\n const row = el(\"div\", \"set-row\");\n if (opts.name !== undefined) row.dataset.name = opts.name;\n const line = el(\"div\", \"set-rowline\");\n if (opts.details && opts.details.length) {\n const details = el(\"div\", \"set-details\"); details.hidden = true;\n for (const d of opts.details) details.append(d);\n // The triangle is drawn by CSS off `.open` (a text glyph stays hairline-thin).\n const toggle = el(\"button\", \"set-expand\"); toggle.type = \"button\";\n // `data-tip`, not `title`: this package ships a themed tooltip and nothing\n // else in it raises an OS rollover. A lone `title` here would be the one\n // place a host's chrome broke character, and on the platform's own delay.\n toggle.dataset.tip = \"More\"; toggle.setAttribute(\"aria-label\", \"More\");\n toggle.setAttribute(\"aria-expanded\", \"false\");\n toggle.addEventListener(\"click\", () => {\n const open = details.hidden;\n details.hidden = !open;\n toggle.setAttribute(\"aria-expanded\", String(open));\n toggle.classList.toggle(\"open\", open);\n });\n line.append(toggle);\n opts.line.forEach((c) => line.append(c));\n row.append(line, details);\n } else {\n opts.line.forEach((c) => line.append(c));\n row.append(line);\n }\n return row;\n}\n\n/**\n * Land on one row of a list by the `name` it was built with: open its details\n * (an enum's values sit behind the disclosure, and they are usually what the\n * jump was for), bring it to the middle of its scroller, and light it for a\n * moment (`.set-row.landed` in settings.css). \"Go to definition\" in both apps\n * used to open the page a property is declared on and stop there, with the\n * declaration below the fold and nothing marking it (Storyletter, 2026-09-14).\n *\n * Returns false when no row carries the name, so a host whose page fills in\n * asynchronously can ask again on a later frame; asking again is the host's,\n * since only the host knows what opens on its own time. The row is matched by\n * reading the stamp rather than by an attribute selector, so a name needs no\n * escaping.\n */\nexport function revealRow(within: ParentNode, name: string): boolean {\n const row = [...within.querySelectorAll<HTMLElement>(\".set-row\")].find((r) => r.dataset.name === name);\n if (!row) return false;\n const toggle = row.querySelector<HTMLButtonElement>(\".set-rowline > .set-expand\");\n if (toggle && toggle.getAttribute(\"aria-expanded\") !== \"true\") toggle.click();\n const still = typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n row.scrollIntoView({ block: \"center\", behavior: still ? \"auto\" : \"smooth\" });\n // Restarted, so landing on the same row twice lights it twice.\n row.classList.remove(\"landed\");\n void row.offsetWidth;\n row.classList.add(\"landed\");\n row.addEventListener(\"animationend\", () => row.classList.remove(\"landed\"), { once: true });\n return true;\n}\n\n/**\n * `revealRow`, retried over the next frames until the row exists or the tries\n * run out: for a page that fills in on its own time after a navigation. Both\n * apps hand-rolled this as `landOn` with the same twelve tries\n * (ui-review-2026-09, finding 29). Resolves true when it landed, false when it\n * gave up, and giving up is quiet: a name no page shows as a row is not a fault.\n */\nexport function revealRowWhenReady(within: ParentNode, name: string, tries = 12): Promise<boolean> {\n return new Promise((resolve) => {\n const attempt = (left: number): void => {\n if (revealRow(within, name)) { resolve(true); return; }\n if (left <= 0) { resolve(false); return; }\n requestAnimationFrame(() => attempt(left - 1));\n };\n attempt(tries);\n });\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\nconst DUP_MESSAGE = \"Another row already has this name.\";\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 // The reason rides `data-tip` (the themed rollover, and what the Save\n // gate's message channel reads), never `title`.\n if (dup) { it.input.dataset[\"tip\"] = DUP_MESSAGE; any = true; }\n else if (it.input.dataset[\"tip\"] === DUP_MESSAGE) delete it.input.dataset[\"tip\"];\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\n * name). Return `{ el, message }` to say WHICH fault in the error line;\n * a bare element says it through its own `data-tip` (the dup guard's, a\n * property-name field's), and the line falls back to a generic sentence\n * when it has neither. */\n firstInvalid?(): HTMLElement | { el: HTMLElement; message?: string } | 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 /** Why this tab cannot be used right now, or null when it can. A reason\n * renders the tab dimmed and inert with the reason as its rollover\n * (Patterpad's Audio tab while the project is not voiced). Read on open\n * and on `refreshTabs()`, so a control inside the dialog can flip it. */\n disabled?: () => string | null;\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 /** Re-read every section's `disabled()`; a tab that has just become\n * disabled while showing hands over to the first usable one. */\n refreshTabs(): 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 // A disabled tab is inert by class, not by the `disabled` attribute: a\n // disabled button swallows the pointer, and the reason has to be readable\n // on hover.\n tab.addEventListener(\"click\", () => { if (!tab.classList.contains(\"is-disabled\")) 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 const usable = (s: SettingsSection): boolean => !tabFor.get(s.id)?.classList.contains(\"is-disabled\");\n function refreshTabs(): void {\n for (const s of opts.sections) {\n const tab = tabFor.get(s.id)!;\n const reason = s.disabled?.() ?? null;\n tab.classList.toggle(\"is-disabled\", reason !== null);\n if (reason !== null) { tab.dataset[\"tip\"] = reason; tab.setAttribute(\"aria-disabled\", \"true\"); }\n else { delete tab.dataset[\"tip\"]; tab.removeAttribute(\"aria-disabled\"); }\n }\n if (!opts.sections.some((s) => s.id === active && usable(s))) {\n const first = opts.sections.find(usable);\n if (first) showTab(first.id);\n }\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 found = handles.get(s.id)?.firstInvalid?.();\n if (!found) continue;\n const bad = found instanceof HTMLElement ? found : found.el;\n const message = (found instanceof HTMLElement ? undefined : found.message) ?? bad.dataset[\"tip\"];\n showTab(s.id);\n bad.focus();\n // The field's own rollover says which fault it is, so the line repeats it\n // rather than inventing a summary; the generic sentence is the fallback.\n error.textContent = message || \"Fix the highlighted fields first.\";\n error.hidden = false;\n 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 refreshTabs(); // a disabled tab asked for by id hands over to the first usable one\n dialog.showModal();\n },\n refreshTabs,\n destroy() { dialog.remove(); },\n };\n}\n","// ---------------------------------------------------------------------------\n// Property names: what an author may call a value the story reads and writes.\n//\n// This belongs to the shell for the same reason gameIds do (see ids.ts): it\n// follows from the SHAPE these apps share - a project of data files, authored in\n// an editor, compiled for something else to run - rather than from any one\n// subject. Nothing here needs the words card, beat, hand or scene.\n//\n// UNLIKE gameIds, the rule is not a house style. It is dictated by the\n// expression language both families embed (`@wildwinter/expr`), and every clause\n// below is a failure observed by compiling one:\n//\n// parser.ts tokenises an identifier as /[a-zA-Z_][a-zA-Z0-9_]*/ and then\n// FOLDS it: `name: propTok.value.toLowerCase()`.\n//\n// @patter.is_night -> [\"sv\",\"patter\",\"is_night\"] reachable\n// @patter.isNight -> [\"sv\",\"patter\",\"isnight\"] folded: a declaration\n// spelled `isNight` is\n// one nothing can reach\n// @patter.is-night -> [\"bin\",\"-\",[\"sv\",\"patter\",\"is\"], NOT an error: it is\n// [\"s\",\"night\"]] SUBTRACTION. A hyphen\n// silently compiles to\n// a different meaning\n// @patter.is night -> parse error\n// @patter.9lives -> parse error (a name cannot start with a digit)\n// @patter.not -> parse error (`true false and or not` are keywords)\n//\n// The hyphen line is why this is worth a shared rule rather than a per-app\n// check: every other violation is loud, and that one quietly means something\n// else. It is also why a property name is NOT a gameId with different spelling -\n// a gameId is built out of hyphens, and here a hyphen is an operator.\n//\n// DEPENDENCY-FREE on purpose, ids.ts's reasoning verbatim: the apps' compilers,\n// CLIs and embedded runtimes need these rules far from any UI, so they keep\n// their own copies and assert parity with these in a test.\n// ---------------------------------------------------------------------------\n\n/** The five words `@wildwinter/expr` lexes as keywords, so a property may not be\n * called any of them: the reference does not resolve, it fails to parse. Kept\n * here as a copy for the same reason the rules are (no dependency), and each\n * app probes the real parser to prove the list is still true. */\nexport const RESERVED_PROPERTY_NAMES: readonly string[] = [\"true\", \"false\", \"and\", \"or\", \"not\"];\n\n/** What an editor should tell an author, in one line. */\nexport const PROPERTY_NAME_HINT =\n \"Lower case letters, digits, and underscores. It can't start with a digit. No hyphens or spaces.\";\n\n/**\n * Coerce a label into a legal property name: lower case, apostrophes dropped,\n * runs of anything else to a single underscore, no trailing underscore, an\n * underscore in front if it would otherwise start with a digit, and one behind\n * if it would otherwise be a keyword.\n *\n * A LEADING underscore is kept where the author typed one, because `_private` is\n * a legal name and a meaningful choice; only the ends that would be illegal are\n * touched.\n *\n * May return \"\", when there was nothing usable in the input. Callers treat that\n * as \"no name yet\" rather than as a name.\n */\nexport function propertyNameify(text: string): string {\n const trimmed = text.trim();\n // An underscore the author actually typed is kept; one that is only the ghost of\n // leading punctuation is not. That is the difference between `_private` and `!gold`.\n const deliberateLeading = trimmed.startsWith(\"_\");\n let out = trimmed.toLowerCase().replace(/['’]/g, \"\")\n .replace(/[^a-z0-9_]+/g, \"_\").replace(/_+/g, \"_\").replace(/^_+|_+$/g, \"\");\n if (out === \"\") return \"\";\n if (deliberateLeading || /^[0-9]/.test(out)) out = `_${out}`;\n if (RESERVED_PROPERTY_NAMES.includes(out)) out = `${out}_`;\n return out;\n}\n\n/**\n * Is this a name the expression language can actually reach? Lower case letters,\n * digits and underscores, not starting with a digit, and not a keyword.\n *\n * \"\" is NOT legal: an unnamed declaration is an unfinished row, which is a\n * different thing from an illegal one, and callers distinguish them.\n */\nexport function isValidPropertyName(name: string): boolean {\n return /^[a-z_][a-z0-9_]*$/.test(name) && !RESERVED_PROPERTY_NAMES.includes(name);\n}\n\n/**\n * The one thing that is only a case difference, e.g. `isNight` -> `isnight`.\n *\n * Worth its own question because it is the only violation that can be repaired\n * without guessing at intent: the language already folds every REFERENCE, so\n * folding the declaration to match changes nothing observable, where inventing\n * an underscore for a space is a decision about what the author meant. Loaders\n * fold this class silently and report the rest.\n */\nexport function isCaseOnlyPropertyName(name: string): boolean {\n return !isValidPropertyName(name) && isValidPropertyName(name.toLowerCase());\n}\n","// ---------------------------------------------------------------------------\n// The manners for a field that names a declared property.\n//\n// The RULE is property-names.ts, which is dependency-free because each app's\n// domain layer keeps its own copy of it. This is the other half: how a field\n// BEHAVES while somebody types a name into it, which only UI touches, so the\n// shell owns it outright.\n//\n// The manners are the gameId editor's, adopted rather than re-argued (see\n// id-editor.ts): an illegal name is marked and blocks Save, and Tab coerces it,\n// in the field, where it can be seen and undone. Nothing is rewritten as it is\n// typed. Patterpad's first pass at this folded every keystroke to lower case;\n// that is the manner the id editor had already rejected, on the grounds that an\n// app which silently renames a thing has made a decision that was not its to\n// make, and a property name is exactly that kind of thing: the author is naming\n// something their own conditions will call.\n//\n// It arrived here as two copies, one per app, written in one session and already\n// carrying the same forty lines of fault wording. Lifting them before they could\n// drift is the whole reason this file exists.\n// ---------------------------------------------------------------------------\n\nimport { isValidPropertyName, propertyNameify, RESERVED_PROPERTY_NAMES } from \"./property-names.js\";\n\n/** Re-check callbacks for bound REFERENCE fields, so a host can say \"the declarations\n * changed\" without holding a handle per row. */\nconst refSyncs = new WeakMap<HTMLInputElement, () => void>();\n\n/**\n * One line saying what is wrong with this name, or undefined when nothing is.\n *\n * Each branch names what actually HAPPENS rather than reciting the rule, because\n * the person reading it is usually somebody wondering why a condition does\n * nothing. Kept to one line: it lands in a rollover on the field, and hosts\n * repeat it in the Save error rather than inventing a summary of their own.\n */\nexport function propertyNameProblem(name: string): string | undefined {\n if (isValidPropertyName(name)) return undefined;\n const suggestion = propertyNameify(name);\n const tail = suggestion ? ` Try \"${suggestion}\", or press Tab.` : \"\";\n if (RESERVED_PROPERTY_NAMES.includes(name.toLowerCase())) {\n return `Can't be used. \"${name.toLowerCase()}\" is a keyword in expressions.${tail}`;\n }\n // The one that is not an error anywhere else: `@scope.a-b` compiles to a-minus-b.\n if (name.includes(\"-\")) return `Can't be used. A hyphen reads as subtraction in an expression.${tail}`;\n if (/^[0-9]/.test(name)) return `Can't be used. A name can't start with a digit.${tail}`;\n if (name !== name.toLowerCase()) {\n return `Can't be used. Expressions fold names, so this would look for \"${name.toLowerCase()}\".${tail}`;\n }\n return `Can't be used. Only lower case letters, digits, and underscores.${tail}`;\n}\n\n/**\n * Wire a name field: mark it live, explain the fault in its rollover, and coerce\n * on Tab. `onCommit` receives what the author typed, VERBATIM, on every input.\n *\n * `.illegal` rather than dupGuard's `.invalid`, because both can be true of one\n * field and `dupGuard.check()` owns that class: it would clear this marking every\n * time it ran, including at the moment of a blocked Save.\n *\n * An empty field is not marked. An unfinished row is a different thing from an\n * illegal one, and hosts prune blank names on save.\n */\nexport function bindPropertyName(\n input: HTMLInputElement,\n onCommit: (value: string) => void,\n opts: { hint?: string } = {},\n): void {\n const hint = opts.hint;\n const sync = (): void => {\n const v = input.value.trim();\n const problem = v === \"\" ? undefined : propertyNameProblem(v);\n input.classList.toggle(\"illegal\", !!problem);\n if (problem) input.title = problem;\n else if (input.title.startsWith(\"Can't be used\")) {\n if (hint) input.title = hint;\n else input.removeAttribute(\"title\");\n }\n };\n input.addEventListener(\"input\", () => { onCommit(input.value); sync(); });\n input.addEventListener(\"keydown\", (e) => {\n // The opt-in coercion, the id editor's gesture. Shift+Tab is left alone: it is\n // how somebody leaves a field they have not finished with.\n if (e.key !== \"Tab\" || e.shiftKey) return;\n const typed = input.value.trim();\n if (typed === \"\" || isValidPropertyName(typed)) return;\n const coerced = propertyNameify(typed);\n if (!coerced || coerced === input.value) return;\n e.preventDefault();\n input.value = coerced;\n onCommit(coerced);\n sync();\n });\n sync();\n}\n\n/**\n * The first field in `host` holding a name no expression could reach, for a Save\n * gate. Hosts hand this back beside `dupGuard.firstDuplicate()`: two faults, two\n * sets of words, one gate.\n */\nexport function firstIllegalPropertyName(host: ParentNode): HTMLInputElement | null {\n return host.querySelector<HTMLInputElement>(\"input.illegal\");\n}\n\n// ---------------------------------------------------------------------------\n// Reference fields, which are a different job from declaration fields.\n//\n// A declaration NAMES a property; a reference POINTS at one. The expression\n// parser folds every reference, so `@world.isNight` reaches `isnight` perfectly\n// well and complaining about the capital would be inventing a rule the language\n// does not have. Everything else still bites, and harder: no declaration may\n// contain a hyphen, so `is-night` in a reference can never match anything that\n// exists.\n//\n// And a reference has a fault a declaration cannot have: naming something that\n// was never declared. That is not a grammar question, so the host supplies the\n// list. The cost of getting it wrong is quiet - a coverage driver pointed at a\n// property nobody declared feeds a value nobody reads, the branches it was meant\n// to exercise stay unexercised, and the report shows them as never hit, which\n// reads as \"this content is unreachable\" rather than \"that name is a typo\".\n// ---------------------------------------------------------------------------\n\nexport interface PropertyRefOptions {\n /** The names that exist, read fresh on every check so a declaration added in the\n * same dialog clears the complaint. Compared case-insensitively, since the\n * parser folds. */\n known: () => string[];\n /** The scope token this field addresses, for the wording (\"no @world property...\"). */\n scope?: string;\n /** Shown on the field when there is nothing to complain about. */\n hint?: string;\n}\n\n/** The closest known name, when one is close enough to be worth naming. Deliberately\n * narrow: a wrong suggestion is worse than none, so it wants a shared prefix or a\n * single-character slip, not the least-bad match in the list. */\nfunction nearest(name: string, known: string[]): string | undefined {\n const n = name.toLowerCase();\n const byPrefix = known.filter((k) => k.toLowerCase().startsWith(n) || n.startsWith(k.toLowerCase()));\n if (byPrefix.length === 1) return byPrefix[0];\n let best: string | undefined;\n let bestScore = 3; // strictly better than \"three edits away\"\n for (const k of known) {\n const a = k.toLowerCase();\n if (Math.abs(a.length - n.length) > 2) continue;\n // Levenshtein, small strings, no need for a matrix beyond two rows.\n let prev = Array.from({ length: n.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i++) {\n const row = [i];\n for (let j = 1; j <= n.length; j++) {\n row[j] = Math.min(prev[j]! + 1, row[j - 1]! + 1, prev[j - 1]! + (a[i - 1] === n[j - 1] ? 0 : 1));\n }\n prev = row;\n }\n const d = prev[n.length]!;\n if (d < bestScore) { bestScore = d; best = k; }\n }\n return best;\n}\n\n/** Why this reference cannot be used, or undefined when it can. */\nexport function propertyRefProblem(name: string, opts: PropertyRefOptions): string | undefined {\n const scope = opts.scope ? `@${opts.scope}` : \"that scope\";\n // Case is NOT a fault here: the parser folds references.\n const folded = name.toLowerCase();\n if (!isValidPropertyName(folded)) {\n const suggestion = propertyNameify(name);\n const tail = suggestion ? ` Try \"${suggestion}\", or press Tab.` : \"\";\n if (RESERVED_PROPERTY_NAMES.includes(folded)) return `Can't be used. \"${folded}\" is a keyword in expressions.${tail}`;\n if (name.includes(\"-\")) return `Can't be used. A hyphen reads as subtraction in an expression.${tail}`;\n if (/^[0-9]/.test(name)) return `Can't be used. A name can't start with a digit.${tail}`;\n return `Can't be used. Only lower case letters, digits, and underscores.${tail}`;\n }\n const known = opts.known();\n if (known.some((k) => k.toLowerCase() === folded)) return undefined;\n const near = nearest(folded, known);\n if (near) return `No ${scope} property is called \"${name}\". Did you mean \"${near}\"?`;\n if (known.length === 0) return `No ${scope} properties are declared yet. Declare one above first.`;\n const shown = known.slice(0, 3).map((k) => `\"${k}\"`).join(\", \");\n const more = known.length > 3 ? `, and ${known.length - 3} more` : \"\";\n return `No ${scope} property is called \"${name}\". Pick one of ${shown}${more}.`;\n}\n\n/**\n * Wire a field that POINTS at a declared property: same manners as a declaration\n * field (mark, do not rewrite, Tab coerces), plus the declared names offered as a\n * datalist, because the rule is declare-then-reference and a picker makes that\n * painless rather than pedantic.\n *\n * Marks `.illegal` for an undeclared name too, which gates Save wherever the host\n * already asks `firstIllegalPropertyName`.\n */\nexport function bindPropertyRef(\n input: HTMLInputElement,\n onCommit: (value: string) => void,\n opts: PropertyRefOptions,\n): void {\n const list = document.createElement(\"datalist\");\n list.id = `shell-propref-${Math.random().toString(36).slice(2, 9)}`;\n input.setAttribute(\"list\", list.id);\n\n // Attached lazily, not once at bind. A datalist has to be IN the document to do anything, and it is\n // attached as a sibling of the input - so binding a field that has not been appended yet (which is\n // the ordinary order for a builder that makes its nodes and then mounts them) silently produced no\n // autocomplete at all: no error, nothing to see. This re-tries on every sync, so it heals as soon as\n // the field reaches the tree.\n const attach = (): void => {\n if (!list.isConnected && input.parentNode) input.after(list);\n };\n\n const sync = (): void => {\n attach();\n const known = opts.known();\n list.replaceChildren(...known.map((k) => { const o = document.createElement(\"option\"); o.value = k; return o; }));\n const v = input.value.trim();\n const problem = v === \"\" ? undefined : propertyRefProblem(v, opts);\n input.classList.toggle(\"illegal\", !!problem);\n if (problem) input.title = problem;\n else if (input.title.startsWith(\"Can't be used\") || input.title.startsWith(\"No \")) {\n if (opts.hint) input.title = opts.hint;\n else input.removeAttribute(\"title\");\n }\n };\n refSyncs.set(input, sync);\n\n input.addEventListener(\"input\", () => { onCommit(input.value); sync(); });\n input.addEventListener(\"keydown\", (e) => {\n if (e.key !== \"Tab\" || e.shiftKey) return;\n const typed = input.value.trim();\n // Only a GRAMMAR fault is coercible. An undeclared name is not something Tab can\n // fix, and a legal reference in the wrong case is not a fault at all.\n if (typed === \"\" || isValidPropertyName(typed.toLowerCase())) return;\n const coerced = propertyNameify(typed);\n if (!coerced || coerced === input.value) return;\n e.preventDefault();\n input.value = coerced;\n onCommit(coerced);\n sync();\n });\n sync();\n}\n\n/**\n * Re-check every bound reference field inside `host`. Hosts call this when the\n * DECLARATIONS change, since a reference's validity depends on a list it does not\n * own: rename a property and the driver pointing at it is wrong from that moment,\n * with nothing typed into it.\n */\nexport function revalidatePropertyRefs(host: ParentNode): void {\n for (const input of host.querySelectorAll(\"input\")) refSyncs.get(input)?.();\n}\n","// ---------------------------------------------------------------------------\n// THE property declaration list: name, type, default, values or stages,\n// purpose, in a row per declaration, with reorder, remove, add-then-focus and\n// the duplicate / illegal-name gates. Patterpad's settings-properties.ts and\n// Storyletter's prop-list.ts were built from the same shell primitives in the\n// same order (dupGuard, bindPropertyName, a type select over the same six\n// types, defaultControl, three iconBtns on moveItem, labelled tagChips and\n// stageChips, expandableRow, focusNewRow); this is the one list, and each\n// app's extras (Shared, Temporary, Read-only, Durable) arrive through\n// `extraLine` / `extraDetails`.\n//\n// `defaultControl` was four copies (three inside Patterpad), and the two apps'\n// option labels had already parted (\"(unset)\" vs \"(none)\"). One label set now.\n//\n// Storage of the default differs by app: Patterpad's model holds a typed\n// value (true, 3), Storyletter's DTO holds the string the field showed. The\n// list mutates `decls` in place either way; `typed` picks the encoding.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconBtn, labelled, moveItem, stageChips, tagChips } from \"./dom.js\";\nimport { dupGuard, expandableRow, focusNewRow } from \"./settings.js\";\nimport type { SettingsSectionHandle } from \"./settings.js\";\nimport { bindPropertyName, firstIllegalPropertyName } from \"./property-name-field.js\";\nimport { PROPERTY_NAME_HINT } from \"./property-names.js\";\n\n/** The six types both apps declare. */\nexport type PropertyListType = \"number\" | \"boolean\" | \"string\" | \"enum\" | \"flags\" | \"quality\";\n\n/** The shape a row edits. Apps' DTOs carry more (shared, writable, durable);\n * those ride through untouched. */\nexport interface PropertyDeclLike {\n name: string;\n type: string;\n default?: unknown;\n /** enum / flags */\n values?: string[];\n /** quality: the ladder, in order */\n stages?: string[];\n purpose?: string;\n}\n\n/** The type list, in the family's order, with the words the select shows. */\nexport const PROPERTY_TYPES: ReadonlyArray<readonly [PropertyListType, string]> = [\n [\"number\", \"Number\"], [\"boolean\", \"True / False\"], [\"string\", \"Text\"],\n [\"enum\", \"List\"], [\"flags\", \"Flags\"], [\"quality\", \"Quality\"],\n];\n\nexport interface DefaultControlOptions {\n /**\n * How the default is stored. `false` (default): the string the field shows,\n * \"\" for none (Storyletter's DTO). `true`: a typed value (boolean, number,\n * string), and none DELETES the key (Patterpad's model).\n */\n typed?: boolean;\n}\n\nconst option = (value: string, label: string, selected: boolean): HTMLOptionElement => {\n const o = el(\"option\", undefined, label);\n o.value = value; o.selected = selected;\n return o;\n};\n\nconst NONE = \"(none)\";\n\n/**\n * The type-appropriate control for a declaration's DEFAULT: a true / false\n * picker for a boolean, the value list for an enum, the stage list for a\n * quality (blank = the first rung), a note for flags (a set starts empty), a\n * number or text field otherwise. The one label for \"no default\" is \"(none)\".\n */\nexport function defaultControl(p: PropertyDeclLike, onChange?: () => void, opts: DefaultControlOptions = {}): HTMLElement {\n const typed = opts.typed === true;\n const write = (raw: string): void => {\n if (raw === \"\") { if (typed) delete p.default; else p.default = \"\"; }\n else if (typed && p.type === \"boolean\") p.default = raw === \"true\";\n else if (typed && p.type === \"number\") p.default = Number(raw);\n else p.default = raw;\n onChange?.();\n };\n const current = p.default === undefined || p.default === null ? \"\" : String(p.default);\n if (p.type === \"boolean\") {\n const sel = el(\"select\", \"field set-default\");\n sel.append(option(\"\", NONE, current === \"\"), option(\"true\", \"True\", current === \"true\"), option(\"false\", \"False\", current === \"false\"));\n sel.addEventListener(\"change\", () => write(sel.value));\n return sel;\n }\n if (p.type === \"enum\") {\n const sel = el(\"select\", \"field set-default\");\n sel.append(option(\"\", NONE, current === \"\"));\n for (const v of p.values ?? []) sel.append(option(v, v, current === v));\n sel.addEventListener(\"change\", () => write(sel.value));\n return sel;\n }\n if (p.type === \"quality\") {\n // The default is a STAGE; blank means the first rung, so say so rather\n // than \"(none)\".\n const sel = el(\"select\", \"field set-default\");\n sel.append(option(\"\", \"(first stage)\", current === \"\"));\n for (const v of p.stages ?? []) sel.append(option(v, v, current === v));\n sel.addEventListener(\"change\", () => write(sel.value));\n return sel;\n }\n if (p.type === \"flags\") {\n // Flags hold a SET of values, so there is no single default: it starts empty.\n const s = el(\"span\", \"set-flagnote\", \"Starts empty\");\n s.dataset[\"tip\"] = \"A flags property starts with none set.\";\n return s;\n }\n const input = el(\"input\", \"field set-default\");\n input.type = p.type === \"number\" ? \"number\" : \"text\";\n input.placeholder = \"Default\";\n input.value = current;\n input.addEventListener(\"input\", () => write(input.value));\n return input;\n}\n\nexport interface PropertyListOptions<T extends PropertyDeclLike> extends DefaultControlOptions {\n /** Called after every mutation; a centre editor feeds its autosave here. */\n onChange?: () => void;\n /** The add button's label. Default \"+ Add property\". */\n addLabel?: string;\n /** The empty sentence. Default \"No properties yet.\" */\n emptyText?: string;\n /** The type select's entries; default `PROPERTY_TYPES`. */\n types?: ReadonlyArray<readonly [string, string]>;\n /** A fresh declaration for the add button. Default: a blank of the first type. */\n newDecl?: () => T;\n /** Controls on the row's line, between the default and the movers\n * (Storyletter's \"uses\" affordance). */\n extraLine?: (decl: T, index: number) => (HTMLElement | null)[];\n /** Fields behind the disclosure, before Values / Stages / Purpose\n * (Shared, Temporary, Read-only, Durable). */\n extraDetails?: (decl: T, index: number) => (HTMLElement | null)[];\n /** Offer the Purpose field. Default true. */\n purpose?: boolean;\n}\n\nexport interface PropertyListHandle extends SettingsSectionHandle {\n /** Both faults, for the Save gate: a duplicate first, then an illegal name. */\n firstInvalid(): HTMLInputElement | null;\n /** The first name that clashes with another, or null. */\n firstDuplicate(): HTMLInputElement | null;\n /** The first name no expression could reach, or null. */\n firstIllegalName(): HTMLInputElement | null;\n /** Redraw from `decls` (after an outside change). */\n render(): void;\n}\n\n/** Mount the list into `host`. Mutates `decls` in place; the caller reads\n * them back on save (pruning blank names is the caller's, since only it\n * knows its DTO). */\nexport function mountPropertyList<T extends PropertyDeclLike>(host: HTMLElement, decls: T[], opts: PropertyListOptions<T> = {}): PropertyListHandle {\n const changed = (): void => opts.onChange?.();\n const guard = dupGuard();\n const types = opts.types ?? PROPERTY_TYPES;\n const list = el(\"div\", \"set-list\");\n const keep = (parts: (HTMLElement | null)[] | undefined): HTMLElement[] =>\n (parts ?? []).filter((p): p is HTMLElement => p !== null);\n\n const row = (p: T, i: number): HTMLElement => {\n const name = el(\"input\", \"field set-name\");\n name.type = \"text\"; name.placeholder = \"Property name\"; name.value = p.name; name.spellcheck = false;\n name.dataset[\"tip\"] = PROPERTY_NAME_HINT;\n bindPropertyName(name, (v) => { p.name = v; changed(); }, { hint: PROPERTY_NAME_HINT });\n guard.track(name);\n\n const type = el(\"select\", \"field set-type\");\n for (const [v, label] of types) type.append(option(v, label, v === p.type));\n type.addEventListener(\"change\", () => {\n p.type = type.value;\n if (opts.typed) delete p.default; else p.default = \"\";\n if (p.type === \"enum\" || p.type === \"flags\") p.values ??= []; else delete p.values;\n if (p.type === \"quality\") p.stages ??= []; else delete p.stages;\n render(); changed();\n });\n\n // The default control is rebuilt in place when the values change, so a\n // new value becomes selectable as the default.\n let def = defaultControl(p, changed, opts);\n const refreshDefault = (): void => { const fresh = defaultControl(p, changed, opts); def.replaceWith(fresh); def = fresh; changed(); };\n\n const up = iconBtn(\"up\", \"Move up\", () => { if (moveItem(decls, i, -1)) { render(); changed(); } }, i === 0);\n const down = iconBtn(\"down\", \"Move down\", () => { if (moveItem(decls, i, 1)) { render(); changed(); } }, i === decls.length - 1);\n const del = iconBtn(\"close\", \"Delete property\", () => { decls.splice(i, 1); render(); changed(); }, false, true);\n\n const details: HTMLElement[] = keep(opts.extraDetails?.(p, i));\n if (p.type === \"enum\" || p.type === \"flags\") details.push(labelled(\"Values\", tagChips(p, refreshDefault)));\n // A quality's ladder, IN ORDER: the chips carry movers because position is the meaning.\n if (p.type === \"quality\") details.push(labelled(\"Stages (in order)\", stageChips(p, refreshDefault)));\n if (opts.purpose !== false) {\n const purpose = el(\"input\", \"field set-purpose\");\n purpose.type = \"text\"; purpose.placeholder = \"What this property is for\"; purpose.value = p.purpose ?? \"\";\n purpose.addEventListener(\"input\", () => {\n if (purpose.value.trim()) p.purpose = purpose.value; else delete p.purpose;\n changed();\n });\n details.push(labelled(\"Purpose\", purpose));\n }\n // Stamped with the declared name so \"Go to definition\" lands on the row (revealRow).\n return expandableRow({ line: [name, type, def, ...keep(opts.extraLine?.(p, i)), up, down, del], details, name: p.name });\n };\n\n const render = (): void => {\n guard.reset();\n list.replaceChildren();\n if (decls.length === 0) list.append(el(\"p\", \"empty\", opts.emptyText ?? \"No properties yet.\"));\n else decls.forEach((p, i) => list.append(row(p, i)));\n guard.check();\n };\n render();\n\n const add = el(\"button\", \"btn set-add\", opts.addLabel ?? \"+ Add property\");\n add.type = \"button\";\n add.addEventListener(\"click\", () => {\n decls.push(opts.newDecl ? opts.newDecl() : ({ name: \"\", type: types[0]?.[0] ?? \"string\", ...(opts.typed ? {} : { default: \"\" }) } as unknown as T));\n render(); changed(); focusNewRow(list);\n });\n host.append(list, add);\n\n return {\n firstDuplicate: () => guard.firstDuplicate(),\n firstIllegalName: () => firstIllegalPropertyName(host),\n // The Save gate takes both faults; the field's own rollover says which.\n firstInvalid: () => guard.firstDuplicate() ?? firstIllegalPropertyName(host),\n render,\n };\n}\n"]}
@@ -0,0 +1,60 @@
1
+ import { closeWithExit } from './chunk-IU7EY6BI.js';
2
+
3
+ // src/floating.ts
4
+ function followOnScroll(reposition) {
5
+ const onMove = () => reposition();
6
+ window.addEventListener("scroll", onMove, true);
7
+ window.addEventListener("resize", onMove);
8
+ return () => {
9
+ window.removeEventListener("scroll", onMove, true);
10
+ window.removeEventListener("resize", onMove);
11
+ };
12
+ }
13
+ function createFloating(className, opts = {}) {
14
+ const el = document.createElement("div");
15
+ el.className = className;
16
+ el.style.display = "none";
17
+ (opts.host ?? document.body).appendChild(el);
18
+ let open = false;
19
+ let detach = null;
20
+ let outside = null;
21
+ let closeToken = 0;
22
+ const close = () => {
23
+ if (!open) return;
24
+ open = false;
25
+ detach?.();
26
+ detach = null;
27
+ if (outside) {
28
+ document.removeEventListener("mousedown", outside, true);
29
+ outside = null;
30
+ }
31
+ const myToken = ++closeToken;
32
+ closeWithExit(el, () => {
33
+ if (myToken !== closeToken) return;
34
+ el.style.display = "none";
35
+ el.replaceChildren();
36
+ el.classList.remove("closing");
37
+ });
38
+ };
39
+ const show = (reposition) => {
40
+ closeToken++;
41
+ el.classList.remove("closing");
42
+ open = true;
43
+ el.style.display = "block";
44
+ reposition();
45
+ detach ??= followOnScroll(reposition);
46
+ };
47
+ const dismissOnOutside = (onOutside, isInside) => {
48
+ if (outside) return;
49
+ outside = (e) => {
50
+ const t = e.target;
51
+ if (!el.contains(t) && !(isInside?.(t) ?? false)) onOutside();
52
+ };
53
+ document.addEventListener("mousedown", outside, true);
54
+ };
55
+ return { el, show, dismissOnOutside, close, isOpen: () => open };
56
+ }
57
+
58
+ export { createFloating };
59
+ //# sourceMappingURL=chunk-P4G4ERG6.js.map
60
+ //# sourceMappingURL=chunk-P4G4ERG6.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/floating.ts"],"names":[],"mappings":";;;AAqCA,SAAS,eAAe,UAAA,EAAoC;AAC1D,EAAA,MAAM,MAAA,GAAS,MAAY,UAAA,EAAW;AACtC,EAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,MAAA,EAAQ,IAAI,CAAA;AAC9C,EAAA,MAAA,CAAO,gBAAA,CAAiB,UAAU,MAAM,CAAA;AACxC,EAAA,OAAO,MAAM;AAAE,IAAA,MAAA,CAAO,mBAAA,CAAoB,QAAA,EAAU,MAAA,EAAQ,IAAI,CAAA;AAAG,IAAA,MAAA,CAAO,mBAAA,CAAoB,UAAU,MAAM,CAAA;AAAA,EAAG,CAAA;AACnH;AASO,SAAS,cAAA,CAAe,SAAA,EAAmB,IAAA,GAAwB,EAAC,EAAa;AACtF,EAAA,MAAM,EAAA,GAAK,QAAA,CAAS,aAAA,CAAc,KAAK,CAAA;AAAG,EAAA,EAAA,CAAG,SAAA,GAAY,SAAA;AAAW,EAAA,EAAA,CAAG,MAAM,OAAA,GAAU,MAAA;AACvF,EAAA,CAAC,IAAA,CAAK,IAAA,IAAQ,QAAA,CAAS,IAAA,EAAM,YAAY,EAAE,CAAA;AAC3C,EAAA,IAAI,IAAA,GAAO,KAAA;AACX,EAAA,IAAI,MAAA,GAA8B,IAAA;AAClC,EAAA,IAAI,OAAA,GAA4C,IAAA;AAChD,EAAA,IAAI,UAAA,GAAa,CAAA;AAEjB,EAAA,MAAM,QAAQ,MAAY;AACxB,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,IAAA,GAAO,KAAA;AACP,IAAA,MAAA,IAAS;AAAG,IAAA,MAAA,GAAS,IAAA;AACrB,IAAA,IAAI,OAAA,EAAS;AAAE,MAAA,QAAA,CAAS,mBAAA,CAAoB,WAAA,EAAa,OAAA,EAAS,IAAI,CAAA;AAAG,MAAA,OAAA,GAAU,IAAA;AAAA,IAAM;AACzF,IAAA,MAAM,UAAU,EAAE,UAAA;AAGlB,IAAA,aAAA,CAAc,IAAI,MAAM;AACtB,MAAA,IAAI,YAAY,UAAA,EAAY;AAC5B,MAAA,EAAA,CAAG,MAAM,OAAA,GAAU,MAAA;AAAQ,MAAA,EAAA,CAAG,eAAA,EAAgB;AAAG,MAAA,EAAA,CAAG,SAAA,CAAU,OAAO,SAAS,CAAA;AAAA,IAChF,CAAC,CAAA;AAAA,EACH,CAAA;AAEA,EAAA,MAAM,IAAA,GAAO,CAAC,UAAA,KAAiC;AAC7C,IAAA,UAAA,EAAA;AAAc,IAAA,EAAA,CAAG,SAAA,CAAU,OAAO,SAAS,CAAA;AAC3C,IAAA,IAAA,GAAO,IAAA;AAAM,IAAA,EAAA,CAAG,MAAM,OAAA,GAAU,OAAA;AAAS,IAAA,UAAA,EAAW;AACpD,IAAA,MAAA,KAAW,eAAe,UAAU,CAAA;AAAA,EACtC,CAAA;AAEA,EAAA,MAAM,gBAAA,GAAmB,CAAC,SAAA,EAAuB,QAAA,KAA+C;AAC9F,IAAA,IAAI,OAAA,EAAS;AACb,IAAA,OAAA,GAAU,CAAC,CAAA,KAAwB;AACjC,MAAA,MAAM,IAAI,CAAA,CAAE,MAAA;AACZ,MAAA,IAAI,CAAC,EAAA,CAAG,QAAA,CAAS,CAAC,CAAA,IAAK,EAAE,QAAA,GAAW,CAAC,CAAA,IAAK,KAAA,CAAA,EAAQ,SAAA,EAAU;AAAA,IAC9D,CAAA;AACA,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAA,EAAa,OAAA,EAAS,IAAI,CAAA;AAAA,EACtD,CAAA;AAEA,EAAA,OAAO,EAAE,EAAA,EAAI,IAAA,EAAM,kBAAkB,KAAA,EAAO,MAAA,EAAQ,MAAM,IAAA,EAAK;AACjE","file":"chunk-P4G4ERG6.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The floating-layer lifecycle every popup and menu shares: a body-appended\n// element, display toggling, a scroll / resize follower that keeps it glued to\n// its anchor, a click-away dismiss, and the family's exit motion on close.\n//\n// Patterpad's surface had it (four popups: the cue picker, the slash menu, the\n// action menu, the jump picker) and Storyletter's context menu had the same\n// idea in eleven lines without the follower (ui-review-2026-09, finding 5).\n// This is the surface's, lifted whole. Positioning stays the caller's, because\n// it differs per popup: pass a `reposition` closure to `show()` and the\n// follower re-runs it on scroll and resize until `close()`.\n//\n// The element is REUSED across opens (a popup that re-renders on every\n// keystroke must not tear itself down each time), which is why a stale exit\n// teardown has a token to lose against a reopen.\n// ---------------------------------------------------------------------------\n\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface Floating {\n /** The floating element. Build content into it; it is appended to <body>\n * (or the `host` given), hidden until `show()`. */\n readonly el: HTMLElement;\n /** Show `el` and keep `reposition` glued to the anchor on scroll / resize.\n * Safe to call repeatedly (on every re-render): the follower attaches once. */\n show(reposition: () => void): void;\n /** While open, call `onOutside` on a pointer-down outside `el`, and outside\n * anything `isInside` accepts (a second flyout, the anchor button).\n * Idempotent: arms once. */\n dismissOnOutside(onOutside: () => void, isInside?: (target: Node) => boolean): void;\n /** Play the exit, then hide `el`, clear its children, and drop the follower\n * and any outside listener. */\n close(): void;\n isOpen(): boolean;\n}\n\n/** Re-run `reposition` on any scroll or resize until the returned disposer runs. */\nfunction followOnScroll(reposition: () => void): () => void {\n const onMove = (): void => reposition();\n window.addEventListener(\"scroll\", onMove, true);\n window.addEventListener(\"resize\", onMove);\n return () => { window.removeEventListener(\"scroll\", onMove, true); window.removeEventListener(\"resize\", onMove); };\n}\n\nexport interface FloatingOptions {\n /** Where the element is appended. Default <body>. Under a modal <dialog> a\n * body-level node sits behind the scrim, inert, so a popup opened from\n * inside one passes the dialog here and paints in its top layer. */\n host?: HTMLElement;\n}\n\nexport function createFloating(className: string, opts: FloatingOptions = {}): Floating {\n const el = document.createElement(\"div\"); el.className = className; el.style.display = \"none\";\n (opts.host ?? document.body).appendChild(el);\n let open = false;\n let detach: (() => void) | null = null;\n let outside: ((e: MouseEvent) => void) | null = null;\n let closeToken = 0; // bumped on every show / close so a stale exit teardown cannot clobber a reopen\n\n const close = (): void => {\n if (!open) return;\n open = false;\n detach?.(); detach = null; // stop following + dismissing immediately; it is on its way out\n if (outside) { document.removeEventListener(\"mousedown\", outside, true); outside = null; }\n const myToken = ++closeToken;\n // Play the exit, THEN hide + clear. A reopen during the fade must win:\n // show() bumps closeToken, and this guard abandons the stale teardown.\n closeWithExit(el, () => {\n if (myToken !== closeToken) return;\n el.style.display = \"none\"; el.replaceChildren(); el.classList.remove(\"closing\");\n });\n };\n\n const show = (reposition: () => void): void => {\n closeToken++; el.classList.remove(\"closing\"); // cancel any in-flight exit, undo its class\n open = true; el.style.display = \"block\"; reposition();\n detach ??= followOnScroll(reposition);\n };\n\n const dismissOnOutside = (onOutside: () => void, isInside?: (target: Node) => boolean): void => {\n if (outside) return; // already armed\n outside = (e: MouseEvent): void => {\n const t = e.target as Node;\n if (!el.contains(t) && !(isInside?.(t) ?? false)) onOutside();\n };\n document.addEventListener(\"mousedown\", outside, true);\n };\n\n return { el, show, dismissOnOutside, close, isOpen: () => open };\n}\n"]}
@@ -0,0 +1,71 @@
1
+ // src/util.ts
2
+ function plural(n, noun, pluralForm = `${noun}s`) {
3
+ return `${n} ${n === 1 ? noun : pluralForm}`;
4
+ }
5
+ function debounce(fn, ms) {
6
+ let timer;
7
+ let pending;
8
+ const run = () => {
9
+ timer = void 0;
10
+ if (pending === void 0) return;
11
+ const args = pending;
12
+ pending = void 0;
13
+ fn(...args);
14
+ };
15
+ const debounced = ((...args) => {
16
+ pending = args;
17
+ if (timer !== void 0) clearTimeout(timer);
18
+ timer = setTimeout(run, ms);
19
+ });
20
+ debounced.cancel = () => {
21
+ if (timer !== void 0) clearTimeout(timer);
22
+ timer = void 0;
23
+ pending = void 0;
24
+ };
25
+ debounced.flush = () => {
26
+ if (timer !== void 0) clearTimeout(timer);
27
+ run();
28
+ };
29
+ return debounced;
30
+ }
31
+ function isEditableTarget(t) {
32
+ if (!(t instanceof Element)) return false;
33
+ if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement) return true;
34
+ if (t instanceof HTMLElement && t.isContentEditable === true) return true;
35
+ const attr = t.getAttribute("contenteditable");
36
+ return attr !== null && attr !== "false";
37
+ }
38
+ function formatCount(n) {
39
+ return n.toLocaleString();
40
+ }
41
+ var copyTimers = /* @__PURE__ */ new WeakMap();
42
+ function copyWithFeedback(el, text, ms = 1e3) {
43
+ const write = typeof navigator !== "undefined" && navigator.clipboard ? navigator.clipboard.writeText(text).catch(() => {
44
+ }) : Promise.resolve();
45
+ el.classList.add("copied");
46
+ const prior = copyTimers.get(el);
47
+ if (prior !== void 0) clearTimeout(prior);
48
+ copyTimers.set(el, setTimeout(() => {
49
+ el.classList.remove("copied");
50
+ copyTimers.delete(el);
51
+ }, ms));
52
+ return write;
53
+ }
54
+ function relativeTime(date, now = Date.now()) {
55
+ const then = date instanceof Date ? date.getTime() : new Date(date).getTime();
56
+ if (!Number.isFinite(then)) return "";
57
+ const mins = Math.floor((now - then) / 6e4);
58
+ if (mins < 1) return "just now";
59
+ if (mins < 60) return `${plural(mins, "minute")} ago`;
60
+ const hours = Math.floor(mins / 60);
61
+ if (hours === 1) return "an hour ago";
62
+ if (hours < 24) return `${hours} hours ago`;
63
+ const days = Math.floor(hours / 24);
64
+ if (days === 1) return "yesterday";
65
+ if (days < 7) return `${days} days ago`;
66
+ return new Date(then).toLocaleDateString(void 0, { day: "numeric", month: "short", year: "numeric" });
67
+ }
68
+
69
+ export { copyWithFeedback, debounce, formatCount, isEditableTarget, plural, relativeTime };
70
+ //# sourceMappingURL=chunk-P6WLAEKZ.js.map
71
+ //# sourceMappingURL=chunk-P6WLAEKZ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/util.ts"],"names":[],"mappings":"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","file":"chunk-P6WLAEKZ.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The small idioms both apps wrote by hand and never gave a name: the plural\n// suffix (about forty sites), the debounce (eight copies), \"is the focus in a\n// field\" (four copies that disagreed about <select>), the count with grouping\n// separators, clipboard copy with the `.copied` revert (three timings), and\n// relative time (two wordings). One answer each, here.\n//\n// DOM-free except where the idiom is about the DOM (isEditableTarget,\n// copyWithFeedback), so main-process code can import the rest.\n// ---------------------------------------------------------------------------\n\n/** \"1 link\" / \"3 links\". The plural form defaults to `noun + \"s\"`; pass the\n * irregular one (\"entry\", \"entries\") when that is wrong. */\nexport function plural(n: number, noun: string, pluralForm = `${noun}s`): string {\n return `${n} ${n === 1 ? noun : pluralForm}`;\n}\n\nexport interface Debounced<A extends unknown[]> {\n (...args: A): void;\n /** Drop the pending call. */\n cancel(): void;\n /** Run the pending call now, if there is one. */\n flush(): void;\n}\n\n/** Trailing-edge debounce: the call runs `ms` after the LAST call. */\nexport function debounce<A extends unknown[]>(fn: (...args: A) => void, ms: number): Debounced<A> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n let pending: A | undefined;\n const run = (): void => {\n timer = undefined;\n if (pending === undefined) return;\n const args = pending;\n pending = undefined;\n fn(...args);\n };\n const debounced = ((...args: A): void => {\n pending = args;\n if (timer !== undefined) clearTimeout(timer);\n timer = setTimeout(run, ms);\n }) as Debounced<A>;\n debounced.cancel = () => { if (timer !== undefined) clearTimeout(timer); timer = undefined; pending = undefined; };\n debounced.flush = () => { if (timer !== undefined) clearTimeout(timer); run(); };\n return debounced;\n}\n\n/**\n * Is the keyboard's focus in a field? The one agreed answer: an input, a\n * textarea, a select, or anything contenteditable (the attribute is read as\n * well as `isContentEditable`, which jsdom does not implement). A window-level\n * shortcut checks this before acting, so a bare letter typed into a field\n * stays a letter.\n */\nexport function isEditableTarget(t: EventTarget | null | undefined): boolean {\n if (!(t instanceof Element)) return false;\n if (t instanceof HTMLInputElement || t instanceof HTMLTextAreaElement || t instanceof HTMLSelectElement) return true;\n if (t instanceof HTMLElement && t.isContentEditable === true) return true;\n const attr = t.getAttribute(\"contenteditable\");\n return attr !== null && attr !== \"false\";\n}\n\n/** A count for reading: grouping separators in the user's locale. */\nexport function formatCount(n: number): string {\n return n.toLocaleString();\n}\n\nconst copyTimers = new WeakMap<Element, ReturnType<typeof setTimeout>>();\n\n/**\n * Copy `text` to the clipboard and light `el` with `.copied` for `ms` (the\n * family's 1000). A second click restarts the timer rather than stacking a\n * second revert under the first. Resolves when the clipboard write has.\n */\nexport function copyWithFeedback(el: Element, text: string, ms = 1000): Promise<void> {\n const write = typeof navigator !== \"undefined\" && navigator.clipboard\n ? navigator.clipboard.writeText(text).catch(() => {})\n : Promise.resolve();\n el.classList.add(\"copied\");\n const prior = copyTimers.get(el);\n if (prior !== undefined) clearTimeout(prior);\n copyTimers.set(el, setTimeout(() => { el.classList.remove(\"copied\"); copyTimers.delete(el); }, ms));\n return write;\n}\n\n/**\n * How long ago, in one wording: \"just now\", \"4 minutes ago\", \"an hour ago\",\n * \"3 hours ago\", \"yesterday\", \"5 days ago\", and past a week the date itself.\n * Coarse on purpose: the question a reader has is whether the thing is from\n * this sitting, not how many seconds ago it was. A future date is \"just now\".\n */\nexport function relativeTime(date: Date | number | string, now: number = Date.now()): string {\n const then = date instanceof Date ? date.getTime() : new Date(date).getTime();\n if (!Number.isFinite(then)) return \"\";\n const mins = Math.floor((now - then) / 60000);\n if (mins < 1) return \"just now\";\n if (mins < 60) return `${plural(mins, \"minute\")} ago`;\n const hours = Math.floor(mins / 60);\n if (hours === 1) return \"an hour ago\";\n if (hours < 24) return `${hours} hours ago`;\n const days = Math.floor(hours / 24);\n if (days === 1) return \"yesterday\";\n if (days < 7) return `${days} days ago`;\n return new Date(then).toLocaleDateString(undefined, { day: \"numeric\", month: \"short\", year: \"numeric\" });\n}\n"]}
@@ -0,0 +1,66 @@
1
+ import { dialogFrame } from './chunk-2NMVIM54.js';
2
+ import { el } from './chunk-XZYZXY7I.js';
3
+
4
+ // src/updater-view.ts
5
+ var liveProgress = null;
6
+ var mb = (n) => (n / (1024 * 1024)).toFixed(1);
7
+ function feedUpdaterDownloadProgress(p) {
8
+ if (!liveProgress) return;
9
+ const pct = Math.max(0, Math.min(100, p.percent));
10
+ liveProgress.bar.style.width = `${pct}%`;
11
+ liveProgress.label.textContent = `${pct.toFixed(0)}%, ${mb(p.transferred)} of ${mb(p.total)} MB (${mb(p.bytesPerSecond)} MB/s)`;
12
+ }
13
+ function showUpdaterDialog(opts) {
14
+ return new Promise((resolve) => {
15
+ const defaultId = opts.defaultId ?? 0;
16
+ const cancelId = opts.cancelId ?? opts.buttons.length - 1;
17
+ let done = false;
18
+ const finish = (idx) => {
19
+ if (done) return;
20
+ done = true;
21
+ liveProgress = null;
22
+ resolve(idx);
23
+ frame.close();
24
+ };
25
+ const frame = dialogFrame({
26
+ title: opts.message,
27
+ className: "updater-dialog",
28
+ // Esc is the cancel button, not a further answer: main is waiting on an index.
29
+ onClose: () => finish(cancelId)
30
+ });
31
+ if (opts.detail) frame.body.append(el("p", "updater-detail", opts.detail));
32
+ if (opts.progress) {
33
+ const track = el("div", "updater-progress-track");
34
+ const bar = el("div", "updater-progress-bar");
35
+ track.append(bar);
36
+ const label = el("p", "updater-progress-label", "Starting download\u2026");
37
+ frame.body.append(track, label);
38
+ liveProgress = { bar, label };
39
+ }
40
+ if (opts.links?.length) {
41
+ const row = el("p", "updater-links");
42
+ for (const link of opts.links) {
43
+ const a = el("a", void 0, link.label);
44
+ a.href = link.url;
45
+ a.addEventListener("click", (e) => {
46
+ e.preventDefault();
47
+ opts.openExternal?.(link.url);
48
+ });
49
+ row.append(a);
50
+ }
51
+ frame.body.append(row);
52
+ }
53
+ opts.buttons.forEach((label, i) => {
54
+ const b = el("button", i === defaultId ? "btn primary" : "btn", label);
55
+ b.type = "button";
56
+ b.addEventListener("click", () => finish(i));
57
+ frame.actions.append(b);
58
+ if (i === defaultId) queueMicrotask(() => b.focus());
59
+ });
60
+ frame.open();
61
+ });
62
+ }
63
+
64
+ export { feedUpdaterDownloadProgress, showUpdaterDialog };
65
+ //# sourceMappingURL=chunk-S4KWQLYN.js.map
66
+ //# sourceMappingURL=chunk-S4KWQLYN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/updater-view.ts"],"names":[],"mappings":"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","file":"chunk-S4KWQLYN.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The updater's VIEW: the themed prompt main summons over `updater:prompt`\n// (updater.ts) and reads the chosen button index back from, the same contract\n// as dialog.showMessageBox's `response`. The renderer half of the updater is\n// not optional: an unanswered prompt does not fall back to a native dialog,\n// the shell waits 300 seconds and resolves its own fallback index. Both apps\n// had written this view by hand (55 of 60 lines identical); this is the one\n// they share, on the family's dialog frame, the way save.ts has save-view.ts.\n//\n// Storyletter's copy, plus Patterpad's `links` row as an option (an About box\n// used to share this dialog there). Styles ship as updater.css.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\nimport type { UpdaterDownloadProgress, UpdaterPromptOptions } from \"./updater.js\";\n\nexport interface UpdaterLink { label: string; url: string }\n\nexport interface UpdaterViewOptions extends UpdaterPromptOptions {\n /** Web links under the detail. Opened through `openExternal`, never by\n * navigating the window. */\n links?: UpdaterLink[];\n /** How a link opens (the app's allow-listed shell.openExternal bridge).\n * Required for `links` to do anything. */\n openExternal?: (url: string) => void;\n}\n\n// The progress row of the open `progress: true` dialog, if any. These are\n// modal, so one at a time, so one slot is enough. Closing clears it.\nlet liveProgress: { bar: HTMLElement; label: HTMLElement } | null = null;\n\nconst mb = (n: number): string => (n / (1024 * 1024)).toFixed(1);\n\n/** Route a download-progress tick into the open dialog's bar. No-op when none\n * is showing. Registered once at boot on the app's progress channel. */\nexport function feedUpdaterDownloadProgress(p: UpdaterDownloadProgress): void {\n if (!liveProgress) return;\n const pct = Math.max(0, Math.min(100, p.percent));\n liveProgress.bar.style.width = `${pct}%`;\n liveProgress.label.textContent =\n `${pct.toFixed(0)}%, ${mb(p.transferred)} of ${mb(p.total)} MB (${mb(p.bytesPerSecond)} MB/s)`;\n}\n\n/** Show the prompt as a modal themed dialog; resolve the index of the clicked\n * button (Esc resolves `cancelId`, which defaults to the last button). */\nexport function showUpdaterDialog(opts: UpdaterViewOptions): Promise<number> {\n return new Promise((resolve) => {\n const defaultId = opts.defaultId ?? 0;\n const cancelId = opts.cancelId ?? opts.buttons.length - 1;\n\n let done = false;\n const finish = (idx: number): void => {\n if (done) return;\n done = true;\n liveProgress = null; // stop feeding a dialog that is gone\n resolve(idx);\n frame.close();\n };\n const frame = dialogFrame({\n title: opts.message, className: \"updater-dialog\",\n // Esc is the cancel button, not a further answer: main is waiting on an index.\n onClose: () => finish(cancelId),\n });\n if (opts.detail) frame.body.append(el(\"p\", \"updater-detail\", opts.detail));\n\n if (opts.progress) {\n const track = el(\"div\", \"updater-progress-track\");\n const bar = el(\"div\", \"updater-progress-bar\");\n track.append(bar);\n const label = el(\"p\", \"updater-progress-label\", \"Starting download…\");\n frame.body.append(track, label);\n liveProgress = { bar, label };\n }\n\n if (opts.links?.length) {\n const row = el(\"p\", \"updater-links\");\n for (const link of opts.links) {\n const a = el(\"a\", undefined, link.label);\n a.href = link.url;\n a.addEventListener(\"click\", (e) => { e.preventDefault(); opts.openExternal?.(link.url); });\n row.append(a);\n }\n frame.body.append(row);\n }\n\n opts.buttons.forEach((label, i) => {\n // The default is the affirmative and wears the accent; every other\n // button, the way out included, is the plain one.\n const b = el(\"button\", i === defaultId ? \"btn primary\" : \"btn\", label);\n b.type = \"button\";\n b.addEventListener(\"click\", () => finish(i));\n frame.actions.append(b);\n if (i === defaultId) queueMicrotask(() => b.focus());\n });\n\n frame.open();\n });\n}\n"]}