@wildwinter/app-shell 0.39.0 → 0.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/README.md +7 -0
  2. package/dist/about.css +5 -5
  3. package/dist/about.css.map +1 -1
  4. package/dist/anchored.css +5 -5
  5. package/dist/anchored.css.map +1 -1
  6. package/dist/chunk-4OAORWEM.js +56 -0
  7. package/dist/chunk-4OAORWEM.js.map +1 -0
  8. package/dist/{chunk-K2AOLQW4.js → chunk-EHR2KBZC.js} +3 -3
  9. package/dist/{chunk-K2AOLQW4.js.map → chunk-EHR2KBZC.js.map} +1 -1
  10. package/dist/{chunk-KGAMP5LW.js → chunk-G2JT3HZH.js} +16 -3
  11. package/dist/chunk-G2JT3HZH.js.map +1 -0
  12. package/dist/{chunk-S4KWQLYN.js → chunk-OPZPFOPS.js} +4 -4
  13. package/dist/{chunk-S4KWQLYN.js.map → chunk-OPZPFOPS.js.map} +1 -1
  14. package/dist/{chunk-L5HOBEKL.js → chunk-R3NXPHRZ.js} +3 -3
  15. package/dist/{chunk-L5HOBEKL.js.map → chunk-R3NXPHRZ.js.map} +1 -1
  16. package/dist/{chunk-C6UVQYKI.js → chunk-SEWJLUOD.js} +3 -3
  17. package/dist/{chunk-C6UVQYKI.js.map → chunk-SEWJLUOD.js.map} +1 -1
  18. package/dist/{chunk-XZYZXY7I.js → chunk-UG2P777F.js} +36 -3
  19. package/dist/chunk-UG2P777F.js.map +1 -0
  20. package/dist/{chunk-2NMVIM54.js → chunk-YRFJLJUH.js} +3 -3
  21. package/dist/{chunk-2NMVIM54.js.map → chunk-YRFJLJUH.js.map} +1 -1
  22. package/dist/chunk-YWMSQFXX.js +146 -0
  23. package/dist/chunk-YWMSQFXX.js.map +1 -0
  24. package/dist/comments.css +11 -11
  25. package/dist/comments.css.map +1 -1
  26. package/dist/confirm.css +2 -2
  27. package/dist/confirm.css.map +1 -1
  28. package/dist/context-menu.cjs.map +1 -1
  29. package/dist/context-menu.css +1 -1
  30. package/dist/context-menu.css.map +1 -1
  31. package/dist/context-menu.js +2 -2
  32. package/dist/controls.css +70 -11
  33. package/dist/controls.css.map +1 -1
  34. package/dist/dialog.cjs.map +1 -1
  35. package/dist/dialog.css +3 -2
  36. package/dist/dialog.css.map +1 -1
  37. package/dist/dialog.js +2 -2
  38. package/dist/identity.css +2 -2
  39. package/dist/identity.css.map +1 -1
  40. package/dist/index.cjs +270 -13
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +39 -6
  43. package/dist/index.d.ts +39 -6
  44. package/dist/index.js +17 -13
  45. package/dist/index.js.map +1 -1
  46. package/dist/job.css +1 -1
  47. package/dist/job.css.map +1 -1
  48. package/dist/keys.cjs +155 -0
  49. package/dist/keys.cjs.map +1 -0
  50. package/dist/keys.css +41 -0
  51. package/dist/keys.css.map +1 -0
  52. package/dist/keys.d.cts +57 -0
  53. package/dist/keys.d.ts +57 -0
  54. package/dist/keys.js +3 -0
  55. package/dist/keys.js.map +1 -0
  56. package/dist/link-status.cjs +13 -0
  57. package/dist/link-status.cjs.map +1 -1
  58. package/dist/link-status.css +8 -8
  59. package/dist/link-status.css.map +1 -1
  60. package/dist/link-status.js +2 -2
  61. package/dist/notes-editor.css +7 -7
  62. package/dist/notes-editor.css.map +1 -1
  63. package/dist/pane-shell.css +7 -2
  64. package/dist/pane-shell.css.map +1 -1
  65. package/dist/problems.cjs +61 -0
  66. package/dist/problems.cjs.map +1 -0
  67. package/dist/problems.d.cts +57 -0
  68. package/dist/problems.d.ts +57 -0
  69. package/dist/problems.js +3 -0
  70. package/dist/problems.js.map +1 -0
  71. package/dist/property-list.cjs.map +1 -1
  72. package/dist/property-list.js +2 -2
  73. package/dist/save.css +1 -1
  74. package/dist/save.css.map +1 -1
  75. package/dist/settings.css +13 -13
  76. package/dist/settings.css.map +1 -1
  77. package/dist/stale.css +1 -1
  78. package/dist/stale.css.map +1 -1
  79. package/dist/stepper.css +14 -4
  80. package/dist/stepper.css.map +1 -1
  81. package/dist/toast.css +5 -5
  82. package/dist/toast.css.map +1 -1
  83. package/dist/tokens.css +2 -1
  84. package/dist/tokens.css.map +1 -1
  85. package/dist/tool-window.css +3 -2
  86. package/dist/tool-window.css.map +1 -1
  87. package/dist/tooltip.css +3 -3
  88. package/dist/tooltip.css.map +1 -1
  89. package/dist/updater-view.cjs.map +1 -1
  90. package/dist/updater-view.js +3 -3
  91. package/dist/updater.css +5 -5
  92. package/dist/updater.css.map +1 -1
  93. package/dist/vc.css +1 -1
  94. package/dist/vc.css.map +1 -1
  95. package/dist/welcome.cjs.map +1 -1
  96. package/dist/welcome.css +21 -21
  97. package/dist/welcome.css.map +1 -1
  98. package/dist/welcome.js +2 -2
  99. package/package.json +15 -1
  100. package/dist/chunk-KGAMP5LW.js.map +0 -1
  101. package/dist/chunk-XZYZXY7I.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/tooltip.ts","../src/icons.ts","../src/dom.ts"],"names":["el","tip"],"mappings":"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","file":"chunk-UG2P777F.js","sourcesContent":["// ---------------------------------------------------------------------------\n// A single themed tooltip the family controls, replacing native `title`\n// rollovers: those are unstyled OS chrome and appear on the platform's slow\n// (~1s) delay, which is exactly the seam design-language section 4 (\"coherent\n// to the edges\") says not to leave showing.\n//\n// ONE floating bubble, document-DELEGATED: any element carrying a `data-tip`\n// attribute gets it, so a caller just sets `data-tip=\"...\"` anywhere and wires\n// nothing. The bubble anchors above the element (flipping below near the top\n// edge), uses our own snappy delay and the shared panel-enter motion.\n// `initTooltips()` is idempotent, so it is safe to call from more than one\n// place in an app that has several entry points.\n//\n// Lifted from Patterpad's patterpad-surface/web/tooltip.ts, which proved it,\n// with the one app-specific rule (suppressed in Writing View) generalised into\n// the `suppressed` option so the shell knows nothing about either app's modes.\n// ---------------------------------------------------------------------------\n\n// Bold markers: a producer wraps a span in these PRIVATE control chars (U+0002 STX / U+0003 ETX,\n// never present in user text) and the renderer turns it into a <strong>. Lets a data-tip bold part\n// of itself (a name, a \"VO:\" prefix) without any HTML, and each segment is still set via\n// textContent, so there is no injection risk from user content.\nconst B_OPEN = \"\\u0002\";\nconst B_CLOSE = \"\\u0003\";\n\n/** Wrap `s` so it renders bold inside a tooltip (see the marker note above). */\nexport function tipBold(s: string): string { return `${B_OPEN}${s}${B_CLOSE}`; }\n\n/** Render `text` into `host`, turning `\\u0002…\\u0003`-marked spans into <strong>; all set as text (safe). */\nfunction renderTip(host: HTMLElement, text: string): void {\n host.textContent = \"\";\n const re = /\\u0002([^\\u0003]*)\\u0003/g;\n let last = 0;\n let m: RegExpExecArray | null;\n while ((m = re.exec(text)) !== null) {\n if (m.index > last) host.append(text.slice(last, m.index));\n const strong = document.createElement(\"strong\");\n strong.textContent = m[1] ?? \"\";\n host.append(strong);\n last = m.index + m[0].length;\n }\n if (last < text.length) host.append(text.slice(last));\n}\n\nexport interface TooltipOptions {\n /** Called before every show: return true to keep the bubble down. For a mode\n * that wants no chrome at all (Patterpad's Writing View). */\n suppressed?: () => boolean;\n}\n\nlet inited = false;\nlet checked = false; // the one-shot no-host warning below has been scheduled\nlet suppressed: (() => boolean) | undefined;\nconst SHOW_DELAY = 350; // ms - snappier than the OS title delay, slow enough not to flicker on pass-through\nconst EDGE = 6; // viewport inset so the bubble never touches the window edge\nconst GAP = 7; // px between the anchor and the bubble\n\nlet tip: HTMLDivElement | null = null;\nlet timer = 0;\nlet active: HTMLElement | null = null;\n/** The subject of a rect-anchored tip (see `tipAt`), when there is one. */\nlet activeKey: string | null = null;\n\nfunction ensureEl(): HTMLDivElement {\n if (tip) return tip;\n const el = document.createElement(\"div\");\n el.className = \"tooltip\";\n el.setAttribute(\"role\", \"tooltip\");\n el.hidden = true;\n document.body.appendChild(el);\n tip = el;\n return el;\n}\n\n/** A rectangle in VIEWPORT coordinates, for anchoring a tip to something that is\n * not a DOM element: a shape on a canvas, a run of text inside one. */\nexport interface TipRect { left: number; top: number; width: number; height: number }\n\nconst rectOf = (anchor: HTMLElement | TipRect): TipRect =>\n (anchor instanceof HTMLElement ? anchor.getBoundingClientRect() : anchor);\n\n/** Anchor the (already-visible, so measurable) bubble above the anchor, flipping below near the top. */\nfunction place(anchor: HTMLElement | TipRect): void {\n const t = ensureEl();\n const r = rectOf(anchor);\n const tr = t.getBoundingClientRect();\n let top = r.top - tr.height - GAP;\n if (top < EDGE) top = r.top + r.height + GAP; // not enough room above -> below\n let left = r.left + r.width / 2 - tr.width / 2;\n left = Math.max(EDGE, Math.min(left, window.innerWidth - tr.width - EDGE));\n t.style.left = `${Math.round(left)}px`;\n t.style.top = `${Math.round(top)}px`;\n}\n\nfunction show(anchor: HTMLElement): void {\n const text = anchor.dataset[\"tip\"];\n if (!text) return;\n paint(anchor, text);\n}\n\n/** Fill, place and reveal the bubble. The one path: both the delegated\n * `data-tip` route and `tipAt` end up here, so a canvas tip and a button tip\n * are the same bubble with the same look and the same motion. */\nfunction paint(anchor: HTMLElement | TipRect, text: string): void {\n if (suppressed?.()) return;\n const t = ensureEl();\n // A modal <dialog> (settings, a confirm) renders in the browser TOP LAYER, above every normal-flow\n // element whatever its z-index. If the anchor is inside one, move the bubble into that dialog so it\n // shares the top layer; otherwise keep it on <body>. Re-parenting per show also moves it back out\n // once the modal closes.\n const host: HTMLElement = (anchor instanceof HTMLElement ? anchor.closest<HTMLElement>(\"dialog[open]\") : null)\n ?? document.body;\n if (t.parentElement !== host) host.appendChild(t);\n active = anchor instanceof HTMLElement ? anchor : null;\n renderTip(t, text);\n t.hidden = false;\n t.classList.remove(\"show\"); void t.offsetWidth; // restart the enter animation\n place(anchor); // measure + position now it has content + is visible\n t.classList.add(\"show\");\n}\n\nfunction hide(): void {\n if (timer) { window.clearTimeout(timer); timer = 0; }\n active = null;\n activeKey = null;\n if (tip) { tip.hidden = true; tip.classList.remove(\"show\"); }\n}\n\nfunction schedule(anchor: HTMLElement): void {\n if (anchor === active) return;\n if (timer) window.clearTimeout(timer);\n timer = window.setTimeout(() => show(anchor), SHOW_DELAY);\n}\n\n/**\n * Show a tip for something that has no DOM node of its own: a card on a canvas,\n * a zone, a pin. The caller says WHERE (a viewport rectangle) and WHAT, and\n * identifies the subject with `key`.\n *\n * The key is what makes this usable from a canvas. Re-calling with the same key\n * re-places the bubble WITHOUT restarting the delay, so a tip can follow its\n * shape while the camera moves; calling with a different key starts the wait\n * again, so sweeping the pointer across a dense board does not strobe.\n *\n * The bubble is ordinary DOM, so it holds its size whatever the canvas is\n * zoomed to. That is the point of it: the label it stands in for is the one\n * thing on a canvas that must not shrink with everything else.\n */\nexport function tipAt(key: string, rect: TipRect, text: string): void {\n if (key === activeKey) {\n // Already up for this subject: move it, do not re-animate it.\n if (tip && !tip.hidden) { renderTip(tip, text); place(rect); }\n return;\n }\n // A different subject: down first, then wait again. Leaving the old bubble up\n // during the wait would put one card's name over another card, which is worse\n // than no name at all - and it matches the delegated route, where leaving an\n // element hides the tip before the next one is scheduled.\n hide();\n activeKey = key;\n timer = window.setTimeout(() => { timer = 0; paint(rect, text); }, SHOW_DELAY);\n}\n\n/** Take any tip down: the pointer has left, or a gesture has started. */\nexport function hideTip(): void { hide(); }\n\n/**\n * Mount the renderer because something is about to speak the grammar.\n *\n * The themed tooltip is not a feature a host turns on; it is the renderer for a grammar the shell's own\n * components already speak, and `data-tip` with nothing listening draws NOTHING - not the themed\n * bubble, not the platform one. That silence cost Patterpad two releases of a pin with no tooltip, and\n * cost it again when a `title` was converted to `data-tip` in a window that had never mounted this.\n *\n * So `el` calls this whenever it sets a tip. Mounting a delegated listener is the same class of act as\n * `confirmDialog` appending to `document.body`, which this package has always done.\n */\nexport function ensureTooltipHost(): void { initTooltips(); }\n\n/**\n * One deferred check that an app writing `data-tip` ITSELF has a host, warning if not.\n *\n * `ensureTooltipHost` covers anything built through `el`. It cannot cover a `data-tip` written straight\n * into markup or set by hand, which is the case that leaves a whole window of dead tips with no shell\n * component involved - so that one gets a warning rather than silence. Scheduled once, after the\n * current task, so it sees a rendered document rather than an empty one.\n */\nexport function checkTooltipHost(): void {\n if (checked) return;\n checked = true;\n setTimeout(() => {\n if (inited || typeof document === \"undefined\") return;\n if (document.querySelector(\"[data-tip]\")) {\n console.warn(\"app-shell: something set `data-tip` but no tooltip host is mounted, so those rollovers will not draw. Call initTooltips() once per window.\");\n }\n }, 0);\n}\n\nconst tipAncestor = (n: EventTarget | null): HTMLElement | null =>\n (n instanceof Element ? n.closest<HTMLElement>(\"[data-tip]\") : null);\n\n/**\n * Wire the one delegated controller. Mounting happens once; OPTIONS do not.\n *\n * An explicit call is always authoritative about its options, even after something else has already\n * mounted the listener. That ordering is not hypothetical, and Storyletter caught it before it shipped:\n * once components self-mount (see `ensureTooltipHost`), a host's own\n * `initTooltips({ suppressed })` could land second and be dropped by an early return, silently taking\n * a rule like \"no tooltips in Writing View\" with it. Nothing would fail and nothing would show.\n *\n * Components therefore call this with NO arguments: they mount the renderer without claiming the\n * options, and the host keeps the last word on behaviour.\n */\nexport function initTooltips(opts: TooltipOptions = {}): void {\n if (opts.suppressed !== undefined) suppressed = opts.suppressed; // an explicit call always wins\n if (inited) return;\n inited = true;\n\n document.addEventListener(\"pointerover\", (e) => {\n const el = tipAncestor(e.target);\n if (el) schedule(el);\n });\n document.addEventListener(\"pointerout\", (e) => {\n const el = tipAncestor(e.target);\n if (!el) return;\n const to = (e as PointerEvent).relatedTarget;\n if (to instanceof Node && el.contains(to)) return; // still inside the same anchor\n hide();\n });\n // Keyboard parity: reveal on focus, dismiss on blur.\n document.addEventListener(\"focusin\", (e) => { const el = tipAncestor(e.target); if (el) schedule(el); });\n document.addEventListener(\"focusout\", () => hide());\n // It's ANCHORED (not cursor-following), so anything that moves the layout or context dismisses it.\n document.addEventListener(\"pointerdown\", () => hide(), true);\n document.addEventListener(\"scroll\", () => { if (active) hide(); }, true);\n document.addEventListener(\"keydown\", (e) => { if (e.key === \"Escape\") hide(); });\n window.addEventListener(\"blur\", () => hide());\n}\n","// ---------------------------------------------------------------------------\n// 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// `iconNode(\"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 a cross twelve times, Storyletter used the same\n// cross once and a multiplication sign twice, in one window, and a comment\n// bubble had just been invented from scratch with nothing to check it against.\n//\n// DRAWN, not typed (design-language.md section 4, \"Icons are drawn, and so are\n// separators\", 2026-09-16). The first cut of this table was Unicode glyphs,\n// which render from whatever symbol font the OS has, so weight, baseline and\n// optical size drifted per character and per platform. The vocabulary is now\n// one drawn set: Lucide, vendored below as inline SVG strings on one 24-unit\n// grid at one stroke weight. The words did not change; what they draw did.\n//\n// Lucide 1.46.0 (lucide-static), ISC licence.\n// https://lucide.dev / https://cdn.jsdelivr.net/npm/lucide-static@1.46.0/icons/<name>.svg\n// Fetched 2026-09-16. The chosen decision record is\n// patterkit/design/ui-review-2026-09/07c-track-c-icon-sets-notes.md.\n//\n// STROKE. The family draws icons at 14px inside its small square buttons and\n// wants a 1.5px rendered stroke against Inter at that size (Inter Regular's\n// stems are about 1.3px there). Lucide's native stroke is 2 on a 24 grid,\n// which is 2 x 14/24 = 1.17px at 14px, a shade light. So the root of every\n// stroke icon carries stroke-width=\"2.571\": 2.571 x 14/24 = 1.5. A caller\n// drawing at another size gets the proportional weight (12px: 1.29px; 16px:\n// 1.71px), which is what one drawn set means. Filled shapes (the discs) carry\n// fill=\"currentColor\" and no stroke at all.\n//\n// Every icon is `currentColor`, so it takes the colour of the button it sits\n// in, and none of the strings carries width/height: the size is the caller's\n// (`iconNode(name, size)`), so an icon can never be the wrong size somewhere\n// because a CSS rule failed to reach it.\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// Lucide licence (the icons vendored in this file):\n//\n// ISC License\n// Copyright (c) 2026 Lucide Icons and Contributors\n//\n// Permission to use, copy, modify, and/or distribute this software for any\n// purpose with or without fee is hereby granted, provided that the above\n// copyright notice and this permission notice appear in all copies.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\" AND THE AUTHOR DISCLAIMS ALL WARRANTIES\n// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF\n// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR\n// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES\n// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN\n// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF\n// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.\n//\n// Portions of Lucide (among those used here: arrow-down, arrow-left,\n// arrow-right, arrow-up, check, chevron-down, chevron-left, chevron-right,\n// circle, lock, plus, search, x) derive from Feather, MIT,\n// Copyright (c) 2013-present Cole Bemis. See LICENSES.md.\n// ---------------------------------------------------------------------------\n\n/**\n * The typed glyphs the vocabulary used to draw.\n *\n * @deprecated Since the move to drawn icons (design-language.md, \"Icons are\n * drawn\"). Kept byte-for-byte so a consumer still doing\n * `textContent = icon.close` keeps working while each app moves; a new call\n * site uses `iconNode(name)` (a node to append) or `iconHtml(name)` (for an\n * innerHTML build). The names are the same in all three. Removed once both\n * apps have moved.\n */\nexport const icon = {\n // --- dismissing and revealing ---------------------------------------------\n /** Close a panel, a dialog, a popover. Patterpad's, twelve times over. */\n close: \"✕\",\n /** The overflow menu on a document or a row: \"more about this\". */\n more: \"⋯\",\n /** Collapsed disclosure, pointing at what would open. */\n collapsed: \"▸\",\n /** Expanded disclosure. */\n expanded: \"▾\",\n /** A dropdown's affordance, where it is not a real <select>. */\n dropdown: \"▾\",\n\n // --- moving through things ------------------------------------------------\n /** Back / previous, in a breadcrumb or a stepper. */\n back: \"‹\",\n /** Forward / next. */\n forward: \"›\",\n /** Up one, in a list. */\n up: \"↑\",\n /** Down one. */\n down: \"↓\",\n\n // --- state ----------------------------------------------------------------\n /** Done, applied, agreed. */\n tick: \"✓\",\n /** Somebody else holds this: the version-control lock. */\n locked: \"⊘\",\n /** Read-only on disk with no other holder: the resting state of everything\n * in a lock-based working copy that nobody has checked out yet. */\n readOnly: \"○\",\n /** YOU hold this one: checked out / opened by the current user, still yours to\n * edit. Shares its glyph with `note`, which the table permits: an entry is a\n * MEANING, and two meanings may draw the same shape. */\n checkedOut: \"✎\",\n /** Tracked, with local changes not yet committed. */\n modified: \"●\",\n /** Not in version control yet: new since the last commit. */\n untracked: \"+\",\n /** Something needs attention but is not fatal. */\n warning: \"‼\",\n /** A live thing: the running position, an active marker. */\n dot: \"●\",\n /** Play, in the sense of \"run this\". */\n play: \"▶\",\n /** Start the thing again from the top: a play session, a sweep. Named for the\n * act, not the arrow, which is the table's rule. */\n restart: \"⟲\",\n /** The three ways a collection can be shown. Named by what you get rather than\n * by the glyph, so a second app can pick the same three without copying\n * somebody's taste in symbols. */\n viewCards: \"▦\",\n viewTable: \"☰\",\n viewNode: \"◈\",\n\n // --- handling -------------------------------------------------------------\n /** The drag grip on a reorderable row. */\n grip: \"⠿\",\n /** Add one of whatever this list holds. */\n add: \"+\",\n\n // --- annotation -----------------------------------------------------------\n /** Documentation notes: the pencil. Both apps had chosen it independently,\n * which is the happiest way for a vocabulary entry to arrive. */\n note: \"✎\",\n /** A comment thread. Speech, not a pencil: documentation states the reason,\n * comments are the conversation, and the two must never look alike. */\n comment: \"❝\",\n} as const;\n\n/**\n * Every word in the vocabulary. The 26 the glyph table had, plus the seven the\n * 2026-09 review found the apps drawing by hand with nothing to check against.\n */\nexport type IconName =\n | keyof typeof icon\n /** History back, the topbar's quiet arrow pair (nav-history.ts). An ARROW,\n * not a chevron: a chevron says structure, an arrow says time (the author's\n * ruling, 2026-08-28). Added beside `arrowRight` so the pair share a set. */\n | \"arrowLeft\"\n /** History forward; also the one honest \"go there\" arrow where a link used\n * to carry a typed one. */\n | \"arrowRight\"\n /** Connect to a server, a live link, a running game. */\n | \"connect\"\n /** Search. */\n | \"search\"\n /** Keep this window on top (tool-window-web.ts). */\n | \"pin\"\n /** Settings. */\n | \"settings\"\n /** Record. The icon is the DISC; painting it in the danger colour while\n * recording is the caller's job, through the button's own colour. */\n | \"record\";\n\n// The two roots every drawing shares. Kept as constants so the table below is\n// the inner elements only, which is what a reader compares against lucide.dev.\nconst STROKE = '<svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2.571\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">';\nconst FILL = '<svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">';\nconst stroked = (inner: string): string => `${STROKE}${inner}</svg>`;\nconst filled = (inner: string): string => `${FILL}${inner}</svg>`;\n\n// The three discs are the one drawing in the table that is not Lucide's own\n// element as shipped: Lucide's `circle` is an r=10 outline that fills its box,\n// which at badge size beside a row title is a blot, so the disc is drawn at a\n// smaller radius with the fill forced (the same amount of hand-drawing as the\n// legacy noteFilled below, and the decision record's stated cost). `modified`\n// and `dot` share it, as their glyphs did; `record` is larger because a record\n// button is meant to fill more of its box.\nconst DISC = filled('<circle cx=\"12\" cy=\"12\" r=\"6\"/>');\nconst DISC_LARGE = filled('<circle cx=\"12\" cy=\"12\" r=\"9\"/>');\n\n/**\n * The drawings, by word. Each is a complete `<svg viewBox=\"0 0 24 24\" …>`\n * string with no width/height (see `iconNode` / `iconHtml`, which size it).\n * The Lucide name each derives from is on the entry, so a reviewer can put\n * the two side by side.\n */\nconst DRAWN: Record<IconName, string> = {\n // --- dismissing and revealing ---------------------------------------------\n /** Close a panel, a dialog, a popover. (x) */\n close: stroked('<path d=\"M18 6 6 18\"/><path d=\"m6 6 12 12\"/>'),\n /** The overflow menu on a document or a row: \"more about this\". (ellipsis) */\n more: stroked('<circle cx=\"12\" cy=\"12\" r=\"1\"/><circle cx=\"19\" cy=\"12\" r=\"1\"/><circle cx=\"5\" cy=\"12\" r=\"1\"/>'),\n /** Collapsed disclosure, pointing at what would open. (chevron-right) */\n collapsed: stroked('<path d=\"m9 18 6-6-6-6\"/>'),\n /** Expanded disclosure. (chevron-down) */\n expanded: stroked('<path d=\"m6 9 6 6 6-6\"/>'),\n /** A dropdown's affordance, where it is not a real <select>. (chevron-down) */\n dropdown: stroked('<path d=\"m6 9 6 6 6-6\"/>'),\n\n // --- moving through things ------------------------------------------------\n /** Back / previous, in a breadcrumb or a stepper. (chevron-left) */\n back: stroked('<path d=\"m15 18-6-6 6-6\"/>'),\n /** Forward / next. (chevron-right) */\n forward: stroked('<path d=\"m9 18 6-6-6-6\"/>'),\n /** Up one, in a list. (arrow-up) */\n up: stroked('<path d=\"m5 12 7-7 7 7\"/><path d=\"M12 19V5\"/>'),\n /** Down one. (arrow-down) */\n down: stroked('<path d=\"M12 5v14\"/><path d=\"m19 12-7 7-7-7\"/>'),\n /** History back. (arrow-left) */\n arrowLeft: stroked('<path d=\"m12 19-7-7 7-7\"/><path d=\"M19 12H5\"/>'),\n /** History forward; \"go there\". (arrow-right) */\n arrowRight: stroked('<path d=\"M5 12h14\"/><path d=\"m12 5 7 7-7 7\"/>'),\n\n // --- state ----------------------------------------------------------------\n /** Done, applied, agreed. (check) */\n tick: stroked('<path d=\"M20 6 9 17l-5-5\"/>'),\n /** Somebody else holds this: the version-control lock. (lock) */\n locked: stroked('<rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 10 0v4\"/>'),\n /** Read-only on disk with no other holder. (lock-open) An open lock rather\n * than a struck-out eye: this state is not a prohibition. Nobody holds the\n * file, saving checks it out, and it is the resting state of everything the\n * author has not touched yet under a lock-based VCS. eye-off would say \"you\n * cannot\", which is false here; the open lock says \"not held\", beside\n * `locked`, which is the shut one, so the two read as a pair. */\n readOnly: stroked('<rect width=\"18\" height=\"11\" x=\"3\" y=\"11\" rx=\"2\" ry=\"2\"/><path d=\"M7 11V7a5 5 0 0 1 9.9-1\"/>'),\n /** YOU hold this one: checked out, still yours to edit. (pencil) */\n checkedOut: stroked('<path d=\"M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z\"/><path d=\"m15 5 4 4\"/>'),\n /** Tracked, with local changes not yet committed. (a disc; see DISC) */\n modified: DISC,\n /** Not in version control yet: new since the last commit. (plus) */\n untracked: stroked('<path d=\"M5 12h14\"/><path d=\"M12 5v14\"/>'),\n /** Something needs attention but is not fatal. (triangle-alert) */\n warning: stroked('<path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/>'),\n /** A live thing: the running position, an active marker. (a disc) */\n dot: DISC,\n /** Play, in the sense of \"run this\". (play) */\n play: stroked('<path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/>'),\n /** Start the thing again from the top: a play session, a sweep. (rotate-ccw) */\n restart: stroked('<path d=\"M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8\"/><path d=\"M3 3v5h5\"/>'),\n /** The three ways a collection can be shown, named by what you get. */\n /** Cards. (layout-grid) */\n viewCards: stroked('<rect width=\"7\" height=\"7\" x=\"3\" y=\"3\" rx=\"1\"/><rect width=\"7\" height=\"7\" x=\"14\" y=\"3\" rx=\"1\"/><rect width=\"7\" height=\"7\" x=\"14\" y=\"14\" rx=\"1\"/><rect width=\"7\" height=\"7\" x=\"3\" y=\"14\" rx=\"1\"/>'),\n /** A table. (rows-3: a ruled box, which is a table. `list` is bullets, and\n * the view is not a list.) */\n viewTable: stroked('<rect width=\"18\" height=\"18\" x=\"3\" y=\"3\" rx=\"2\"/><path d=\"M21 9H3\"/><path d=\"M21 15H3\"/>'),\n /** A node graph. (waypoints: four nodes on edges reads as a graph.\n * `git-fork` reads as version control, which this family also draws, and\n * a picture that means two things is the fault this table exists to\n * prevent.) */\n viewNode: stroked('<path d=\"m10.586 5.414-5.172 5.172\"/><path d=\"m18.586 13.414-5.172 5.172\"/><path d=\"M6 12h12\"/><circle cx=\"12\" cy=\"20\" r=\"2\"/><circle cx=\"12\" cy=\"4\" r=\"2\"/><circle cx=\"20\" cy=\"12\" r=\"2\"/><circle cx=\"4\" cy=\"12\" r=\"2\"/>'),\n /** Record: the disc, in whatever colour the button is. (a disc; see DISC_LARGE) */\n record: DISC_LARGE,\n\n // --- handling -------------------------------------------------------------\n /** The drag grip on a reorderable row. (grip-vertical) */\n grip: stroked('<circle cx=\"9\" cy=\"12\" r=\"1\"/><circle cx=\"9\" cy=\"5\" r=\"1\"/><circle cx=\"9\" cy=\"19\" r=\"1\"/><circle cx=\"15\" cy=\"12\" r=\"1\"/><circle cx=\"15\" cy=\"5\" r=\"1\"/><circle cx=\"15\" cy=\"19\" r=\"1\"/>'),\n /** Add one of whatever this list holds. (plus) */\n add: stroked('<path d=\"M5 12h14\"/><path d=\"M12 5v14\"/>'),\n /** Connect: to a server, a live link, a running game. (plug) */\n connect: stroked('<path d=\"M12 22v-5\"/><path d=\"M15 8V2\"/><path d=\"M17 8a1 1 0 0 1 1 1v4a4 4 0 0 1-4 4h-4a4 4 0 0 1-4-4V9a1 1 0 0 1 1-1z\"/><path d=\"M9 8V2\"/>'),\n /** Search. (search) */\n search: stroked('<path d=\"m21 21-4.34-4.34\"/><circle cx=\"11\" cy=\"11\" r=\"8\"/>'),\n /** Keep this window on top. (pin) */\n pin: stroked('<path d=\"M12 17v5\"/><path d=\"M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24V16a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V7a1 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\"/>'),\n /** Settings. (settings) */\n settings: stroked('<path d=\"M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915\"/><circle cx=\"12\" cy=\"12\" r=\"3\"/>'),\n\n // --- annotation -----------------------------------------------------------\n /** Documentation notes: a document with lines on it. (file-text) The glyph\n * table drew a pencil and shared it with `checkedOut`; the drawn set gives\n * the two meanings two shapes, and notes get the document, which is what\n * the shell's own noteFilled/noteOutline pair had always drawn. */\n note: stroked('<path d=\"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z\"/><path d=\"M14 2v5a1 1 0 0 0 1 1h5\"/><path d=\"M10 9H8\"/><path d=\"M16 13H8\"/><path d=\"M16 17H8\"/>'),\n /** A comment thread. Speech, not a document: documentation states the\n * reason, comments are the conversation, and the two must never look\n * alike. (message-square) */\n comment: stroked('<path d=\"M22 17a2 2 0 0 1-2 2H6.828a2 2 0 0 0-1.414.586l-2.202 2.202A.71.71 0 0 1 2 21.286V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2z\"/>'),\n};\n\n/**\n * The drawings as strings, by word, for a caller that builds markup itself.\n * Prefer `iconHtml(name, size)`, which sizes it; these carry no width/height.\n *\n * Two legacy entries ride along unchanged: `noteFilled` and `noteOutline`, the\n * has-notes / no-notes pair Patterpad drew on a 16-unit grid before the set\n * existed. They are sized in the markup (13px) and on their own grid, so they\n * are NOT part of the vocabulary above and `iconNode` does not know them; no\n * app uses them today. They stay until a 24-grid pair replaces them, because\n * their two-state meaning (there are notes / there are none) has no Lucide\n * shape, and re-weighting the 16-grid drawing to the family stroke merges its\n * text lines.\n */\nexport const iconSvg = {\n ...DRAWN,\n /** A document with lines: notes exist on this thing. (legacy, 16 grid) */\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. (legacy, 16 grid) */\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/** Every word, in table order, for a test or a gallery to walk. */\nexport const ICON_NAMES = Object.keys(DRAWN) as IconName[];\n\n/** The default size: 14px, the family's icon inside its small square button. */\nexport const ICON_SIZE = 14;\n\n/**\n * The drawing as markup with its size set, for a caller building innerHTML.\n * `data-icon` carries the word, so a stylesheet or a test can find it.\n */\nexport function iconHtml(name: IconName, size = ICON_SIZE): string {\n return DRAWN[name].replace(\"<svg \", `<svg width=\"${size}\" height=\"${size}\" data-icon=\"${name}\" `);\n}\n\n// One parsed template per word, filled on first use; every call clones it, so\n// the string is parsed once per window and each caller gets its own node.\nconst templates = new Map<IconName, SVGSVGElement>();\n\n/**\n * The drawing as a fresh SVG node, sized. Append it where the glyph used to be\n * set as text; it is `aria-hidden`, so the button's accessible name stays the\n * button's (`aria-label` / `data-tip`), as it always had to.\n */\nexport function iconNode(name: IconName, size = ICON_SIZE): SVGSVGElement {\n let tpl = templates.get(name);\n if (!tpl) {\n const holder = document.createElement(\"template\");\n holder.innerHTML = DRAWN[name];\n tpl = holder.content.firstElementChild as SVGSVGElement;\n tpl.setAttribute(\"data-icon\", name);\n templates.set(name, tpl);\n }\n const node = tpl.cloneNode(true) as SVGSVGElement;\n node.setAttribute(\"width\", String(size));\n node.setAttribute(\"height\", String(size));\n return node;\n}\n\n// The glyph table, reversed, so a legacy call site that still hands the shell\n// a typed character (\"✕\", \"↑\") draws the word it stood for. Where two words\n// shared a glyph the first in table order wins, which is fine: the picture was\n// the same either way. Goes when `icon` does.\nconst BY_GLYPH = new Map<string, IconName>();\nfor (const [name, glyph] of Object.entries(icon) as [IconName, string][]) {\n if (!BY_GLYPH.has(glyph)) BY_GLYPH.set(glyph, name);\n}\n\n/** The word a deprecated glyph stood for, or undefined for any other string. */\nexport function iconNameOfGlyph(glyph: string): IconName | undefined {\n return BY_GLYPH.get(glyph);\n}\n\n/** Whether a string is a word in the vocabulary. */\nexport function isIconName(s: string): s is IconName {\n return Object.prototype.hasOwnProperty.call(DRAWN, s);\n}\n","// ---------------------------------------------------------------------------\n// `el`: the tiny tag-typed element factory both apps build their imperative,\n// idempotent views with.\n//\n// A SUPERSET of the two apps' prior signatures, so neither rewrites call sites:\n// - Patterpad: el(tag, cls?, text?) (positional)\n// - Storylet Studio: el(tag, { className, text, ... }, ...kids) (options bag)\n// A string second argument is the className; an object is the props bag; string\n// children become text nodes. So `el(\"div\", \"c\", \"t\")` and\n// `el(\"div\", { className: \"c\" }, \"t\")` produce identical DOM.\n// ---------------------------------------------------------------------------\n\nimport { ensureTooltipHost, checkTooltipHost } from \"./tooltip.js\";\nimport { iconNode, iconNameOfGlyph, isIconName, type IconName } from \"./icons.js\";\n\nexport type Child = Node | string | null | undefined;\n\nexport interface ElProps {\n className?: string;\n text?: string;\n /** A NATIVE rollover. Prefer `tip`: `title` is OS chrome on the platform's own\n * slow delay, which is the seam the themed tooltip exists to close. */\n title?: string;\n /** A themed rollover (`data-tip`, picked up by the delegated controller that\n * `initTooltips()` wires). Also the accessible name, since the elements that\n * want one are usually icon buttons with no text of their own. */\n tip?: string;\n onClick?: (event: MouseEvent) => void;\n}\n\nexport function el<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n classNameOrProps?: string | ElProps,\n ...children: Child[]\n): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag);\n const props: ElProps = typeof classNameOrProps === \"string\"\n ? { className: classNameOrProps }\n : classNameOrProps ?? {};\n if (props.className) node.className = props.className;\n if (props.text !== undefined && props.text !== null) node.textContent = props.text;\n if (props.title !== undefined) node.title = props.title;\n if (props.tip !== undefined) {\n node.dataset[\"tip\"] = props.tip;\n node.setAttribute(\"aria-label\", props.tip);\n // Setting a tip mounts the renderer. Without this, `data-tip` draws nothing at all in a window whose\n // host never called `initTooltips()`, which is a silent failure rather than a degraded one. No\n // options passed: the host stays authoritative about behaviour (see tooltip.ts).\n ensureTooltipHost();\n }\n if (props.onClick) node.addEventListener(\"click\", props.onClick as EventListener);\n for (const child of children) {\n if (child === null || child === undefined) continue;\n node.append(child);\n }\n checkTooltipHost(); // one-shot: warns if the APP wrote data-tip and nothing mounted a host\n return node;\n}\n\n/** A small square icon 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.\n *\n * `name` is a word from the vocabulary (\"up\", \"close\"), drawn at 14px. A\n * typed glyph from the deprecated `icon` table (\"↑\", \"✕\") is accepted for\n * now and drawn as the word it stood for, so an app that has not moved its\n * call sites yet still gets the drawn set; any other string is set as text,\n * which is the old behaviour and on its way out. */\nexport function iconBtn(name: IconName | (string & {}), title: string, onClick: () => void, disabled = false, danger = false): HTMLButtonElement {\n const b = el(\"button\", `shell-icon${danger ? \" danger\" : \"\"}`);\n b.type = \"button\"; b.dataset[\"tip\"] = title; b.setAttribute(\"aria-label\", title);\n const word = isIconName(name) ? name : iconNameOfGlyph(name);\n if (word) b.append(iconNode(word)); else b.textContent = name;\n b.disabled = disabled;\n b.addEventListener(\"click\", onClick);\n return b;\n}\n\n/**\n * A captioned field: `<label class=\"shell-labelled\"><span class=\"shell-fieldcap\">…</span>control</label>`.\n *\n * The label is pointed at the field it captions, EXPLICITLY, and never at a button. A `<label>` with no\n * `for` forwards a click to its first labelable descendant, and buttons are labelable - so a caption\n * wrapped around a control containing buttons (`tagChips`, whose every chip carries a remove button)\n * turns every click on the row's dead space into a press of the FIRST button in it. That shipped:\n * patterkit/patter#44, where clicking a Game Data list value anywhere but its ✕ deleted the first value\n * in the list. Reported as \"clicking a value removes the wrong one\", which is not a wrong-index bug at\n * all - the click never reached the chip.\n *\n * When the control holds no labelable field (a chips editor with the add input removed, say), this is a\n * plain `<div>`: no caption behaviour is better than a caption that presses something.\n */\nexport function labelled(label: string, control: HTMLElement): HTMLElement {\n const target = control.matches(\"input, select, textarea\")\n ? control\n : control.querySelector<HTMLElement>(\"input:not([type=button]):not([type=submit]), select, textarea\");\n const w = el(target ? \"label\" : \"div\", \"shell-labelled\");\n w.append(el(\"span\", \"shell-fieldcap\", label), control);\n if (target) {\n if (!target.id) target.id = `shell-f-${Math.random().toString(36).slice(2, 9)}`;\n (w as HTMLLabelElement).htmlFor = target.id;\n }\n return w;\n}\n\n/** Swap item `i` with its neighbour `i + delta` IN PLACE (the up/down reorder list editors share);\n * a no-op when the target is out of range. Returns whether anything moved. */\nexport function moveItem<T>(arr: T[], i: number, delta: number): boolean {\n const j = i + delta;\n if (j < 0 || j >= arr.length) return false;\n [arr[i], arr[j]] = [arr[j]!, arr[i]!];\n return true;\n}\n\n/** The icon size inside a chip's remove / move buttons: the chip is set at\n * 0.76rem, so its controls draw smaller than a toolbar button's 14px. */\nconst CHIP_ICON = 10;\n\n/** A tag-style editor for a string-list field (an enum's allowed values / flags): removable chips + an\n * add input (Enter or \",\" commits; blank / duplicate ignored). Mutates `holder.values` in place (read\n * back on save). `onChange` fires after any add / remove so callers can refresh a dependent control. */\nexport function tagChips(holder: { values?: string[] }, onChange?: () => void): HTMLElement {\n const wrap = el(\"div\", \"shell-tags\");\n const input = el(\"input\", \"shell-tag-input\");\n // Sentence-cased like every other hint (the microcopy ruling, 2026-08-28):\n // lowercase fragments read as adrift, and a placeholder is a hint speaking.\n input.type = \"text\"; input.placeholder = \"Add value\"; input.spellcheck = false;\n const makeChip = (v: string): HTMLElement => {\n const chip = el(\"span\", \"shell-tag\", v);\n const x = el(\"button\", \"shell-tag-x\", iconNode(\"close\", CHIP_ICON));\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/**\n * An ORDERED chips editor for a string-list field whose order means something\n * (a quality's stages: `advance()` walks them, so 2 comes after 1): numbered\n * chips, each with move-earlier / move-later / remove, plus an add input\n * (Enter or \",\" commits; blank / duplicate ignored). Mutates `holder.stages`\n * in place; `onChange` fires after any add / move / remove.\n *\n * Patterpad's, lifted (ui-review-2026-09, finding 3); Storyletter had been\n * faking it with `tagChips` over a `values` holder, which loses the order\n * controls. Same class family as `tagChips` (`.shell-tags`), so it is styled by\n * settings.css already.\n */\nexport function stageChips(holder: { stages?: string[] }, onChange?: () => void): HTMLElement {\n const wrap = el(\"div\", \"shell-tags shell-stages\");\n const input = el(\"input\", \"shell-tag-input\");\n input.type = \"text\"; input.placeholder = \"Add stage\"; input.spellcheck = false;\n const control = (name: IconName, tip: string, disabled: boolean, onClick: () => void): HTMLButtonElement => {\n const b = el(\"button\", \"shell-tag-x shell-tag-move\", iconNode(name, CHIP_ICON));\n b.type = \"button\"; b.dataset[\"tip\"] = tip; b.setAttribute(\"aria-label\", tip);\n b.disabled = disabled;\n b.addEventListener(\"click\", onClick);\n return b;\n };\n const rebuild = (): void => {\n for (const c of Array.from(wrap.children)) if (c !== input) c.remove();\n const stages = holder.stages ?? [];\n stages.forEach((v, i) => {\n const chip = el(\"span\", \"shell-tag\", `${i + 1}. ${v}`);\n chip.dataset[\"stage\"] = v;\n chip.append(\n control(\"back\", `Move ${v} earlier`, i === 0,\n () => { if (moveItem(stages, i, -1)) { rebuild(); onChange?.(); } }),\n control(\"forward\", `Move ${v} later`, i === stages.length - 1,\n () => { if (moveItem(stages, i, 1)) { rebuild(); onChange?.(); } }),\n );\n const x = el(\"button\", \"shell-tag-x\", iconNode(\"close\", CHIP_ICON));\n x.type = \"button\"; x.dataset[\"tip\"] = `Remove ${v}`; x.setAttribute(\"aria-label\", `Remove ${v}`);\n x.addEventListener(\"click\", () => { holder.stages = (holder.stages ?? []).filter((o) => o !== v); rebuild(); onChange?.(); });\n chip.append(x);\n wrap.insertBefore(chip, input);\n });\n };\n const commit = (): void => {\n const v = input.value.trim();\n if (v && !(holder.stages ?? []).includes(v)) { (holder.stages ??= []).push(v); rebuild(); 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 wrap.append(input);\n rebuild();\n return wrap;\n}\n\n// --- drag-to-reorder ---------------------------------------------------------\n// One drag at a time per window, shared across every wired list, so the\n// midpoint test knows what is being carried without a dataTransfer round trip.\nlet reorderDragId: string | null = null;\nconst clearDropMarks = (host: ParentNode): void =>\n host.querySelectorAll(\".drop-before, .drop-after\").forEach((e) => e.classList.remove(\"drop-before\", \"drop-after\"));\n\n/**\n * Make `el` a drag source and a drop target for HTML5 drag-to-reorder among\n * its siblings. While carried it wears `.dragging`; a hovered target wears\n * `.drop-before` or `.drop-after` by the midpoint test on `axis` (\"y\" for\n * rows, \"x\" for a wrapping grid); a drop calls `onMove(draggedId, before,\n * targetId)` and the caller reorders its model and re-renders.\n *\n * Storyletter's `wireCardDrag`, lifted (ui-review-2026-09, finding 30);\n * Patterpad's `wireSceneDrag` is the same idea fixed on \"y\". The marks are\n * classes only: the app draws them (Patterpad's `box-shadow: 0 -2px 0 0\n * var(--accent)` pair is the family's look).\n */\nexport function wireReorder(el: HTMLElement, id: string, axis: \"x\" | \"y\", onMove: (id: string, before: boolean, targetId: string) => void): void {\n el.draggable = true;\n el.dataset[\"reorderId\"] = id;\n const host = (): ParentNode => el.parentElement ?? el;\n el.addEventListener(\"dragstart\", (e) => {\n reorderDragId = id; el.classList.add(\"dragging\");\n if (e.dataTransfer) { e.dataTransfer.effectAllowed = \"move\"; e.dataTransfer.setData(\"text/plain\", id); }\n });\n el.addEventListener(\"dragend\", () => { reorderDragId = null; el.classList.remove(\"dragging\"); clearDropMarks(host()); });\n el.addEventListener(\"dragover\", (e) => {\n if (!reorderDragId || reorderDragId === id) return;\n e.preventDefault();\n if (e.dataTransfer) e.dataTransfer.dropEffect = \"move\";\n const r = el.getBoundingClientRect();\n const before = axis === \"y\" ? e.clientY < r.top + r.height / 2 : e.clientX < r.left + r.width / 2;\n clearDropMarks(host());\n el.classList.add(before ? \"drop-before\" : \"drop-after\");\n });\n el.addEventListener(\"dragleave\", () => el.classList.remove(\"drop-before\", \"drop-after\"));\n el.addEventListener(\"drop\", (e) => {\n e.preventDefault();\n const before = el.classList.contains(\"drop-before\");\n clearDropMarks(host());\n if (reorderDragId && reorderDragId !== id) onMove(reorderDragId, before, id);\n });\n}\n\n// --- metadata and breadcrumbs ---------------------------------------------------\n// \"Icons are drawn, and so are separators\" (design-language.md section 4,\n// 2026-09-16): metadata is never joined with \"·\" and a breadcrumb is never a\n// string with \"›\" in it. Both apps had been building exactly those strings\n// (ui-review-2026-09, marker A); these two helpers make the same lines as DOM,\n// with the separator drawn by CSS (controls.css) or by the icon vocabulary.\n\n/**\n * A metadata line: `parts` as spans in a flex row, separated by a drawn\n * 3px disc (a `::before` on every part after the first, in controls.css),\n * so `metaLine([\"6 runs\", \"200 max steps\", \"seed 4\"])` is what\n * \"6 runs · 200 max steps · seed 4\" used to be, without the typed dot.\n * Empty, null and undefined parts are skipped, so a caller can pass an\n * optional segment without branching.\n */\nexport function metaLine(parts: (string | Node | null | undefined)[]): HTMLElement {\n const row = el(\"span\", \"shell-meta\");\n for (const part of parts) {\n if (part === null || part === undefined || part === \"\") continue;\n const seg = el(\"span\", \"shell-meta-part\");\n seg.append(part);\n row.append(seg);\n }\n return row;\n}\n\nexport interface Crumb {\n label: string;\n /** Makes the crumb a button. Ignored on the last crumb, which is where the\n * reader already is. */\n onClick?: (event: MouseEvent) => void;\n}\n\n/**\n * A breadcrumb trail: each crumb a span, or a button when it carries\n * `onClick` and is not the last, with `iconNode(\"forward\", 12)` drawn between\n * them. `breadcrumb([\"Box\", \"Deck\", \"Card\"])` is what \"Box › Deck › Card\"\n * used to be; the chevrons are the family's own drawing, not a symbol-font\n * character.\n */\nexport function breadcrumb(parts: (string | Crumb)[]): HTMLElement {\n const trail = el(\"nav\", \"shell-crumbs\");\n trail.setAttribute(\"aria-label\", \"Where this is\");\n parts.forEach((part, i) => {\n const crumb: Crumb = typeof part === \"string\" ? { label: part } : part;\n const last = i === parts.length - 1;\n if (i > 0) {\n const sep = iconNode(\"forward\", 12);\n sep.classList.add(\"shell-crumb-sep\");\n trail.append(sep);\n }\n if (crumb.onClick && !last) {\n const b = el(\"button\", { className: \"shell-crumb shell-crumb-link\", text: crumb.label, onClick: crumb.onClick });\n b.type = \"button\";\n trail.append(b);\n } else {\n const s = el(\"span\", \"shell-crumb\", crumb.label);\n if (last) s.setAttribute(\"aria-current\", \"location\");\n trail.append(s);\n }\n });\n return trail;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { el } from './chunk-XZYZXY7I.js';
1
+ import { el } from './chunk-UG2P777F.js';
2
2
  import { closeWithExit } from './chunk-IU7EY6BI.js';
3
3
 
4
4
  // src/dialog.ts
@@ -51,5 +51,5 @@ function dialogFrame(opts) {
51
51
  }
52
52
 
53
53
  export { dialogFrame };
54
- //# sourceMappingURL=chunk-2NMVIM54.js.map
55
- //# sourceMappingURL=chunk-2NMVIM54.js.map
54
+ //# sourceMappingURL=chunk-YRFJLJUH.js.map
55
+ //# sourceMappingURL=chunk-YRFJLJUH.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/dialog.ts"],"names":[],"mappings":";;;;AAiDO,SAAS,YAAY,IAAA,EAAuC;AACjE,EAAA,MAAM,MAAA,GAAS,GAAG,QAAA,EAAU,CAAA,aAAA,EAAgB,KAAK,SAAA,IAAa,EAAE,CAAA,CAAA,CAAG,IAAA,EAAM,CAAA;AACzE,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,KAAA,EAAO,mBAAmB,CAAA;AAC1C,EAAA,MAAM,OAAA,GAAU,EAAA,CAAG,KAAA,EAAO,sBAAsB,CAAA;AAChD,EAAA,MAAA,CAAO,OAAO,EAAA,CAAG,IAAA,EAAM,oBAAA,EAAsB,IAAA,CAAK,KAAK,CAAC,CAAA;AACxD,EAAA,IAAI,IAAA,CAAK,GAAA,KAAQ,MAAA,EAAW,MAAA,CAAO,MAAA,CAAO,GAAG,GAAA,EAAK,kBAAA,EAAoB,IAAA,CAAK,GAAG,CAAC,CAAA;AAC/E,EAAA,MAAA,CAAO,MAAA,CAAO,MAAM,OAAO,CAAA;AAC3B,EAAA,MAAA,CAAO,YAAA,CAAa,YAAA,EAAc,IAAA,CAAK,KAAK,CAAA;AAC5C,EAAA,QAAA,CAAS,IAAA,CAAK,OAAO,MAAM,CAAA;AAE3B,EAAA,IAAI,KAAA,GAAuC,QAAA;AAC3C,EAAA,IAAI,MAAA,GAAyB,IAAA;AAE7B,EAAA,MAAM,WAAW,MAAY;AAC3B,IAAA,MAAA,CAAO,MAAA,EAAO;AAId,IAAA,IAAI,MAAA,YAAkB,WAAA,IAAe,MAAA,CAAO,WAAA,SAAoB,KAAA,EAAM;AACtE,IAAA,MAAA,GAAS,IAAA;AACT,IAAA,KAAA,GAAQ,QAAA;AACR,IAAA,IAAA,CAAK,OAAA,IAAU;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,QAAQ,MAAY;AACxB,IAAA,IAAI,UAAU,MAAA,EAAQ;AACtB,IAAA,KAAA,GAAQ,SAAA;AACR,IAAA,aAAA,CAAc,QAAQ,MAAM;AAAE,MAAA,IAAI,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,KAAA,EAAM;AAAG,MAAA,QAAA,EAAS;AAAA,IAAG,CAAC,CAAA;AAAA,EAC9E,CAAA;AAGA,EAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,CAAC,CAAA,KAAM;AAAE,IAAA,CAAA,CAAE,cAAA,EAAe;AAAG,IAAA,KAAA,EAAM;AAAA,EAAG,CAAC,CAAA;AAGzE,EAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AAAE,IAAA,IAAI,KAAA,KAAU,QAAQ,QAAA,EAAS;AAAA,EAAG,CAAC,CAAA;AAE5E,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IAAQ,IAAA;AAAA,IAAM,OAAA;AAAA,IACd,IAAA,GAAO;AACL,MAAA,IAAI,UAAU,QAAA,EAAU;AACxB,MAAA,MAAA,GAAS,QAAA,CAAS,aAAA;AAClB,MAAA,KAAA,GAAQ,MAAA;AACR,MAAA,MAAA,CAAO,SAAA,EAAU;AAAA,IACnB,CAAA;AAAA,IACA,KAAA;AAAA,IACA,MAAA,EAAQ,MAAM,KAAA,KAAU;AAAA,GAC1B;AACF","file":"chunk-2NMVIM54.js","sourcesContent":["// ---------------------------------------------------------------------------\n// dialogFrame: the one modal skeleton every dialog in the family sits on.\n//\n// Title, an optional sub line, a body the caller fills, an actions row the\n// caller fills, on a native <dialog> so the focus trap and Escape come from the\n// platform. Four scrims and three panel drawings had grown across five shell\n// files and a dozen app dialogs (ui-review-2026-09, findings 9 and 30); this is\n// the frame they all sit on now, and the shell's own dialogs (confirm, identity,\n// about, notes) were moved onto it first, which is how it is proven.\n//\n// What the frame owns: the backdrop, the panel, the enter and exit motion\n// (closeWithExit), Escape, and giving focus back to whatever had it when the\n// dialog opened. What it does not own: the content classes, the buttons'\n// meaning, and when to close. The caller keeps those.\n//\n// Styles ship as dialog.css. The action buttons are expected to wear `.btn`\n// from controls.css; the frame does not draw buttons of its own.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface DialogFrameOptions {\n /** The heading. Also the dialog's accessible name. */\n title: string;\n /** One plain sentence under the heading, if the title needs one. */\n sub?: string;\n /** Extra class(es) on the <dialog>, for the caller's own content rules and\n * width (`--dialog-width` on the class; see dialog.css). */\n className?: string;\n /** Runs once, after the dialog has left the document, however it closed. */\n onClose?: () => void;\n}\n\nexport interface DialogFrame {\n readonly dialog: HTMLDialogElement;\n /** Fill this with the content. */\n readonly body: HTMLElement;\n /** Fill this with the buttons, right-aligned; empty, it draws nothing. */\n readonly actions: HTMLElement;\n /** Show modally. Records what had focus so `close()` can give it back. */\n open(): void;\n /** Play the exit, tear down, restore focus. Idempotent. */\n close(): void;\n isOpen(): boolean;\n}\n\n/** Build a dialog on the family's frame. Appended to <body> at once (so an\n * `open()` on the next line has nothing to wait for); shown only on `open()`. */\nexport function dialogFrame(opts: DialogFrameOptions): DialogFrame {\n const dialog = el(\"dialog\", `shell-dialog ${opts.className ?? \"\"}`.trim());\n const body = el(\"div\", \"shell-dialog-body\");\n const actions = el(\"div\", \"shell-dialog-actions\");\n dialog.append(el(\"h2\", \"shell-dialog-title\", opts.title));\n if (opts.sub !== undefined) dialog.append(el(\"p\", \"shell-dialog-sub\", opts.sub));\n dialog.append(body, actions);\n dialog.setAttribute(\"aria-label\", opts.title);\n document.body.append(dialog);\n\n let state: \"closed\" | \"open\" | \"closing\" = \"closed\";\n let opener: Element | null = null;\n\n const teardown = (): void => {\n dialog.remove();\n // Back to the control that opened this. The platform does it for a plain\n // dialog.close(), but the exit motion ends in a remove(), and a removed\n // dialog restores nothing.\n if (opener instanceof HTMLElement && opener.isConnected) opener.focus();\n opener = null;\n state = \"closed\";\n opts.onClose?.();\n };\n\n const close = (): void => {\n if (state !== \"open\") return;\n state = \"closing\";\n closeWithExit(dialog, () => { if (dialog.open) dialog.close(); teardown(); });\n };\n\n // Escape arrives as the cancel event; route it through the exit motion.\n dialog.addEventListener(\"cancel\", (e) => { e.preventDefault(); close(); });\n // Closed by someone else (a form with method=\"dialog\", or a direct\n // dialog.close()): no motion to play, tear down at once.\n dialog.addEventListener(\"close\", () => { if (state === \"open\") teardown(); });\n\n return {\n dialog, body, actions,\n open() {\n if (state !== \"closed\") return;\n opener = document.activeElement;\n state = \"open\";\n dialog.showModal();\n },\n close,\n isOpen: () => state === \"open\",\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/dialog.ts"],"names":[],"mappings":";;;;AAiDO,SAAS,YAAY,IAAA,EAAuC;AACjE,EAAA,MAAM,MAAA,GAAS,GAAG,QAAA,EAAU,CAAA,aAAA,EAAgB,KAAK,SAAA,IAAa,EAAE,CAAA,CAAA,CAAG,IAAA,EAAM,CAAA;AACzE,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,KAAA,EAAO,mBAAmB,CAAA;AAC1C,EAAA,MAAM,OAAA,GAAU,EAAA,CAAG,KAAA,EAAO,sBAAsB,CAAA;AAChD,EAAA,MAAA,CAAO,OAAO,EAAA,CAAG,IAAA,EAAM,oBAAA,EAAsB,IAAA,CAAK,KAAK,CAAC,CAAA;AACxD,EAAA,IAAI,IAAA,CAAK,GAAA,KAAQ,MAAA,EAAW,MAAA,CAAO,MAAA,CAAO,GAAG,GAAA,EAAK,kBAAA,EAAoB,IAAA,CAAK,GAAG,CAAC,CAAA;AAC/E,EAAA,MAAA,CAAO,MAAA,CAAO,MAAM,OAAO,CAAA;AAC3B,EAAA,MAAA,CAAO,YAAA,CAAa,YAAA,EAAc,IAAA,CAAK,KAAK,CAAA;AAC5C,EAAA,QAAA,CAAS,IAAA,CAAK,OAAO,MAAM,CAAA;AAE3B,EAAA,IAAI,KAAA,GAAuC,QAAA;AAC3C,EAAA,IAAI,MAAA,GAAyB,IAAA;AAE7B,EAAA,MAAM,WAAW,MAAY;AAC3B,IAAA,MAAA,CAAO,MAAA,EAAO;AAId,IAAA,IAAI,MAAA,YAAkB,WAAA,IAAe,MAAA,CAAO,WAAA,SAAoB,KAAA,EAAM;AACtE,IAAA,MAAA,GAAS,IAAA;AACT,IAAA,KAAA,GAAQ,QAAA;AACR,IAAA,IAAA,CAAK,OAAA,IAAU;AAAA,EACjB,CAAA;AAEA,EAAA,MAAM,QAAQ,MAAY;AACxB,IAAA,IAAI,UAAU,MAAA,EAAQ;AACtB,IAAA,KAAA,GAAQ,SAAA;AACR,IAAA,aAAA,CAAc,QAAQ,MAAM;AAAE,MAAA,IAAI,MAAA,CAAO,IAAA,EAAM,MAAA,CAAO,KAAA,EAAM;AAAG,MAAA,QAAA,EAAS;AAAA,IAAG,CAAC,CAAA;AAAA,EAC9E,CAAA;AAGA,EAAA,MAAA,CAAO,gBAAA,CAAiB,QAAA,EAAU,CAAC,CAAA,KAAM;AAAE,IAAA,CAAA,CAAE,cAAA,EAAe;AAAG,IAAA,KAAA,EAAM;AAAA,EAAG,CAAC,CAAA;AAGzE,EAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AAAE,IAAA,IAAI,KAAA,KAAU,QAAQ,QAAA,EAAS;AAAA,EAAG,CAAC,CAAA;AAE5E,EAAA,OAAO;AAAA,IACL,MAAA;AAAA,IAAQ,IAAA;AAAA,IAAM,OAAA;AAAA,IACd,IAAA,GAAO;AACL,MAAA,IAAI,UAAU,QAAA,EAAU;AACxB,MAAA,MAAA,GAAS,QAAA,CAAS,aAAA;AAClB,MAAA,KAAA,GAAQ,MAAA;AACR,MAAA,MAAA,CAAO,SAAA,EAAU;AAAA,IACnB,CAAA;AAAA,IACA,KAAA;AAAA,IACA,MAAA,EAAQ,MAAM,KAAA,KAAU;AAAA,GAC1B;AACF","file":"chunk-YRFJLJUH.js","sourcesContent":["// ---------------------------------------------------------------------------\n// dialogFrame: the one modal skeleton every dialog in the family sits on.\n//\n// Title, an optional sub line, a body the caller fills, an actions row the\n// caller fills, on a native <dialog> so the focus trap and Escape come from the\n// platform. Four scrims and three panel drawings had grown across five shell\n// files and a dozen app dialogs (ui-review-2026-09, findings 9 and 30); this is\n// the frame they all sit on now, and the shell's own dialogs (confirm, identity,\n// about, notes) were moved onto it first, which is how it is proven.\n//\n// What the frame owns: the backdrop, the panel, the enter and exit motion\n// (closeWithExit), Escape, and giving focus back to whatever had it when the\n// dialog opened. What it does not own: the content classes, the buttons'\n// meaning, and when to close. The caller keeps those.\n//\n// Styles ship as dialog.css. The action buttons are expected to wear `.btn`\n// from controls.css; the frame does not draw buttons of its own.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface DialogFrameOptions {\n /** The heading. Also the dialog's accessible name. */\n title: string;\n /** One plain sentence under the heading, if the title needs one. */\n sub?: string;\n /** Extra class(es) on the <dialog>, for the caller's own content rules and\n * width (`--dialog-width` on the class; see dialog.css). */\n className?: string;\n /** Runs once, after the dialog has left the document, however it closed. */\n onClose?: () => void;\n}\n\nexport interface DialogFrame {\n readonly dialog: HTMLDialogElement;\n /** Fill this with the content. */\n readonly body: HTMLElement;\n /** Fill this with the buttons, right-aligned; empty, it draws nothing. */\n readonly actions: HTMLElement;\n /** Show modally. Records what had focus so `close()` can give it back. */\n open(): void;\n /** Play the exit, tear down, restore focus. Idempotent. */\n close(): void;\n isOpen(): boolean;\n}\n\n/** Build a dialog on the family's frame. Appended to <body> at once (so an\n * `open()` on the next line has nothing to wait for); shown only on `open()`. */\nexport function dialogFrame(opts: DialogFrameOptions): DialogFrame {\n const dialog = el(\"dialog\", `shell-dialog ${opts.className ?? \"\"}`.trim());\n const body = el(\"div\", \"shell-dialog-body\");\n const actions = el(\"div\", \"shell-dialog-actions\");\n dialog.append(el(\"h2\", \"shell-dialog-title\", opts.title));\n if (opts.sub !== undefined) dialog.append(el(\"p\", \"shell-dialog-sub\", opts.sub));\n dialog.append(body, actions);\n dialog.setAttribute(\"aria-label\", opts.title);\n document.body.append(dialog);\n\n let state: \"closed\" | \"open\" | \"closing\" = \"closed\";\n let opener: Element | null = null;\n\n const teardown = (): void => {\n dialog.remove();\n // Back to the control that opened this. The platform does it for a plain\n // dialog.close(), but the exit motion ends in a remove(), and a removed\n // dialog restores nothing.\n if (opener instanceof HTMLElement && opener.isConnected) opener.focus();\n opener = null;\n state = \"closed\";\n opts.onClose?.();\n };\n\n const close = (): void => {\n if (state !== \"open\") return;\n state = \"closing\";\n closeWithExit(dialog, () => { if (dialog.open) dialog.close(); teardown(); });\n };\n\n // Escape arrives as the cancel event; route it through the exit motion.\n dialog.addEventListener(\"cancel\", (e) => { e.preventDefault(); close(); });\n // Closed by someone else (a form with method=\"dialog\", or a direct\n // dialog.close()): no motion to play, tear down at once.\n dialog.addEventListener(\"close\", () => { if (state === \"open\") teardown(); });\n\n return {\n dialog, body, actions,\n open() {\n if (state !== \"closed\") return;\n opener = document.activeElement;\n state = \"open\";\n dialog.showModal();\n },\n close,\n isOpen: () => state === \"open\",\n };\n}\n"]}
@@ -0,0 +1,146 @@
1
+ // src/keys.ts
2
+ var override;
3
+ function setKeyPlatform(platform) {
4
+ override = platform;
5
+ }
6
+ function keyPlatform() {
7
+ if (override) return override;
8
+ if (typeof navigator !== "undefined") {
9
+ const s = `${navigator.platform ?? ""} ${navigator.userAgent ?? ""}`;
10
+ if (/mac|iphone|ipad/i.test(s)) return "mac";
11
+ if (/win/i.test(s)) return "win";
12
+ if (s.trim()) return "linux";
13
+ }
14
+ const p = typeof process !== "undefined" ? process.platform : void 0;
15
+ return p === "darwin" ? "mac" : p === "win32" ? "win" : "linux";
16
+ }
17
+ var MAC_ORDER = ["ctrl", "alt", "shift", "mod"];
18
+ var OTHER_ORDER = ["mod", "ctrl", "alt", "shift"];
19
+ var MAC_MOD = { ctrl: "\u2303", alt: "\u2325", shift: "\u21E7", mod: "\u2318" };
20
+ var OTHER_MOD = { ctrl: "Ctrl", alt: "Alt", shift: "Shift", mod: "Ctrl" };
21
+ var MODIFIER_ALIASES = {
22
+ mod: "mod",
23
+ cmd: "mod",
24
+ command: "mod",
25
+ meta: "mod",
26
+ super: "mod",
27
+ ctrl: "ctrl",
28
+ control: "ctrl",
29
+ alt: "alt",
30
+ option: "alt",
31
+ opt: "alt",
32
+ shift: "shift"
33
+ };
34
+ var NAMED = {
35
+ enter: { mac: "\u21A9", other: "Enter" },
36
+ return: { mac: "\u21A9", other: "Enter" },
37
+ esc: { mac: "esc", other: "Esc" },
38
+ escape: { mac: "esc", other: "Esc" },
39
+ up: { mac: "\u2191", other: "Up" },
40
+ down: { mac: "\u2193", other: "Down" },
41
+ left: { mac: "\u2190", other: "Left" },
42
+ right: { mac: "\u2192", other: "Right" },
43
+ backspace: { mac: "\u232B", other: "Backspace" },
44
+ delete: { mac: "\u2326", other: "Del" },
45
+ del: { mac: "\u2326", other: "Del" },
46
+ tab: { mac: "\u21E5", other: "Tab" },
47
+ space: { mac: "Space", other: "Space" },
48
+ home: { mac: "\u2196", other: "Home" },
49
+ end: { mac: "\u2198", other: "End" },
50
+ pageup: { mac: "\u21DE", other: "PgUp" },
51
+ pagedown: { mac: "\u21DF", other: "PgDn" }
52
+ };
53
+ var PORTABLE_MODS = /* @__PURE__ */ new Set(["mod", "ctrl", "alt", "shift"]);
54
+ function parse(combo) {
55
+ const parts = combo.split("+");
56
+ const mods = [];
57
+ let key;
58
+ for (let i = 0; i < parts.length; i++) {
59
+ const raw = parts[i] ?? "";
60
+ if (raw === "" && i > 0) {
61
+ key = "+";
62
+ continue;
63
+ }
64
+ const mod = MODIFIER_ALIASES[raw.toLowerCase()];
65
+ if (mod) {
66
+ if (!mods.includes(mod)) mods.push(mod);
67
+ continue;
68
+ }
69
+ key = raw;
70
+ }
71
+ return { mods, key };
72
+ }
73
+ function isKeyCombo(value) {
74
+ const plus = value.length > 2 && value.endsWith("++");
75
+ const parts = (plus ? value.slice(0, -2) : value).split("+");
76
+ let keys = plus ? 1 : 0;
77
+ for (const raw of parts) {
78
+ if (raw === "") return false;
79
+ const word = raw.toLowerCase();
80
+ if (PORTABLE_MODS.has(word)) continue;
81
+ if (raw.length === 1 || word in NAMED) {
82
+ keys++;
83
+ continue;
84
+ }
85
+ return false;
86
+ }
87
+ return keys === 1;
88
+ }
89
+ function keyLegend(key, mac) {
90
+ const named = NAMED[key.toLowerCase()];
91
+ if (named) return mac ? named.mac : named.other;
92
+ return key.length === 1 ? key.toUpperCase() : key;
93
+ }
94
+ function keyLabel(combo, platform = keyPlatform()) {
95
+ const mac = platform === "mac";
96
+ const { mods, key } = parse(combo);
97
+ const order = mac ? MAC_ORDER : OTHER_ORDER;
98
+ const words = order.filter((m) => mods.includes(m)).map((m) => (mac ? MAC_MOD : OTHER_MOD)[m]);
99
+ const seen = /* @__PURE__ */ new Set();
100
+ const modWords = words.filter((w) => seen.has(w) ? false : (seen.add(w), true));
101
+ const legend = key === void 0 ? [] : [keyLegend(key, mac)];
102
+ return mac ? [...modWords, ...legend].join("") : [...modWords, ...legend].join("+");
103
+ }
104
+ function keyLegends(combo, platform = keyPlatform()) {
105
+ if (platform === "mac") return [keyLabel(combo, platform)];
106
+ const { mods, key } = parse(combo);
107
+ const seen = /* @__PURE__ */ new Set();
108
+ const words = OTHER_ORDER.filter((m) => mods.includes(m)).map((m) => OTHER_MOD[m]).filter((w) => seen.has(w) ? false : (seen.add(w), true));
109
+ return key === void 0 ? words : [...words, keyLegend(key, false)];
110
+ }
111
+ function chip(legend) {
112
+ const k = document.createElement("kbd");
113
+ k.className = "shell-kbd";
114
+ k.textContent = legend;
115
+ return k;
116
+ }
117
+ function keyHint(combo, opts = {}) {
118
+ const platform = opts.platform ?? keyPlatform();
119
+ const legends = combo.trim().split(/\s+/).flatMap((c) => keyLegends(c, platform));
120
+ if (legends.length === 1) return chip(legends[0]);
121
+ const group = document.createElement("span");
122
+ group.className = "shell-keys";
123
+ for (const legend of legends) group.append(chip(legend));
124
+ return group;
125
+ }
126
+ function hintBar(items, opts = {}) {
127
+ const bar = document.createElement("div");
128
+ bar.className = "shell-hintbar";
129
+ for (const item of items) {
130
+ const hint = document.createElement("span");
131
+ hint.className = "shell-hint";
132
+ const label = document.createElement("span");
133
+ label.className = "shell-hint-label";
134
+ label.textContent = item.label;
135
+ hint.append(keyHint(item.keys, opts), label);
136
+ bar.append(hint);
137
+ }
138
+ return bar;
139
+ }
140
+ function tipWithKey(text, combo, platform = keyPlatform()) {
141
+ return `${text} (${keyLabel(combo, platform)})`;
142
+ }
143
+
144
+ export { hintBar, isKeyCombo, keyHint, keyLabel, keyLegends, keyPlatform, setKeyPlatform, tipWithKey };
145
+ //# sourceMappingURL=chunk-YWMSQFXX.js.map
146
+ //# sourceMappingURL=chunk-YWMSQFXX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/keys.ts"],"names":[],"mappings":"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","file":"chunk-YWMSQFXX.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Keyboard hints, platform-true, from ONE helper.\n//\n// \"One casing rule, and platform-true key hints\" (design-language.md section\n// 4, 2026-09-16): a hint writes \"⌘\" on macOS and \"Ctrl\" elsewhere, and \"⌘\"\n// never appears in a hard-coded string. Before this module the family had four\n// notations for one idea (ui-review-2026-09, finding 26): a <kbd> here, \"(⌘1)\"\n// in a tooltip there, a platform fork in one label, and \"↑↓ move · ↵ jump ·\n// esc to close\" typed into a hint bar, which on Windows showed a Mac glyph.\n//\n// The input is the portable spelling every caller already knows from the menu\n// spine: \"Mod+1\", \"Mod+Shift+M\", \"Enter\", \"Esc\", \"Up\". The output is what the\n// platform writes: \"⌘1\" / \"⇧⌘M\" / \"↩\" / \"esc\" on a Mac, in Apple's modifier\n// order and with no joiner; \"Ctrl+1\" / \"Ctrl+Shift+M\" / \"Enter\" / \"Esc\"\n// everywhere else. A hint is DOM (`keyHint`, `hintBar`): a keycap element,\n// never a glyph string, and a tooltip gets its bracketed key from\n// `tipWithKey` so it is right on both platforms.\n// ---------------------------------------------------------------------------\n\nexport type KeyPlatform = \"mac\" | \"win\" | \"linux\";\n\nlet override: KeyPlatform | undefined;\n\n/** Pin the platform every helper here renders for (undefined = detect again).\n * A seam for the tests and for a preview harness; an app leaves it unset. */\nexport function setKeyPlatform(platform?: KeyPlatform): void { override = platform; }\n\n/** The platform the hints render for: the override, else the browser's, else\n * the process's, else \"linux\" (which draws the same as \"win\"). */\nexport function keyPlatform(): KeyPlatform {\n if (override) return override;\n if (typeof navigator !== \"undefined\") {\n const s = `${navigator.platform ?? \"\"} ${navigator.userAgent ?? \"\"}`;\n if (/mac|iphone|ipad/i.test(s)) return \"mac\";\n if (/win/i.test(s)) return \"win\";\n if (s.trim()) return \"linux\";\n }\n const p = typeof process !== \"undefined\" ? process.platform : undefined;\n return p === \"darwin\" ? \"mac\" : p === \"win32\" ? \"win\" : \"linux\";\n}\n\n/** Canonical modifier words, in the order each platform writes them. Apple\n * puts them ⌃ ⌥ ⇧ ⌘ before the key; Windows and Linux write Ctrl+Alt+Shift. */\ntype Modifier = \"ctrl\" | \"alt\" | \"shift\" | \"mod\";\nconst MAC_ORDER: Modifier[] = [\"ctrl\", \"alt\", \"shift\", \"mod\"];\nconst OTHER_ORDER: Modifier[] = [\"mod\", \"ctrl\", \"alt\", \"shift\"];\nconst MAC_MOD: Record<Modifier, string> = { ctrl: \"⌃\", alt: \"⌥\", shift: \"⇧\", mod: \"⌘\" };\nconst OTHER_MOD: Record<Modifier, string> = { ctrl: \"Ctrl\", alt: \"Alt\", shift: \"Shift\", mod: \"Ctrl\" };\n\nconst MODIFIER_ALIASES: Record<string, Modifier> = {\n mod: \"mod\", cmd: \"mod\", command: \"mod\", meta: \"mod\", super: \"mod\",\n ctrl: \"ctrl\", control: \"ctrl\",\n alt: \"alt\", option: \"alt\", opt: \"alt\",\n shift: \"shift\",\n};\n\n/** Named keys: the Mac legend and the spelled-out word the others use. */\nconst NAMED: Record<string, { mac: string; other: string }> = {\n enter: { mac: \"↩\", other: \"Enter\" }, return: { mac: \"↩\", other: \"Enter\" },\n esc: { mac: \"esc\", other: \"Esc\" }, escape: { mac: \"esc\", other: \"Esc\" },\n up: { mac: \"↑\", other: \"Up\" }, down: { mac: \"↓\", other: \"Down\" },\n left: { mac: \"←\", other: \"Left\" }, right: { mac: \"→\", other: \"Right\" },\n backspace: { mac: \"⌫\", other: \"Backspace\" }, delete: { mac: \"⌦\", other: \"Del\" }, del: { mac: \"⌦\", other: \"Del\" },\n tab: { mac: \"⇥\", other: \"Tab\" }, space: { mac: \"Space\", other: \"Space\" },\n home: { mac: \"↖\", other: \"Home\" }, end: { mac: \"↘\", other: \"End\" },\n pageup: { mac: \"⇞\", other: \"PgUp\" }, pagedown: { mac: \"⇟\", other: \"PgDn\" },\n};\n\ninterface Parsed { mods: Modifier[]; key: string | undefined }\n\n/** The portable modifier words, exactly: the spelling the menu spine uses. The\n * aliases above (\"Cmd\", \"Option\") are tolerated by `parse` but they are not\n * portable, which is what `isKeyCombo` keys on. */\nconst PORTABLE_MODS = new Set<string>([\"mod\", \"ctrl\", \"alt\", \"shift\"]);\n\n/** Split \"Mod+Shift+M\" into its modifiers and its key. A literal \"+\" key is\n * written \"Mod++\" (the last empty part). Case is ignored on the words. */\nfunction parse(combo: string): Parsed {\n const parts = combo.split(\"+\");\n const mods: Modifier[] = [];\n let key: string | undefined;\n for (let i = 0; i < parts.length; i++) {\n const raw = parts[i] ?? \"\";\n if (raw === \"\" && i > 0) { key = \"+\"; continue; }\n const mod = MODIFIER_ALIASES[raw.toLowerCase()];\n if (mod) { if (!mods.includes(mod)) mods.push(mod); continue; }\n key = raw;\n }\n return { mods, key };\n}\n\n/**\n * Whether `value` is a combo in the portable spelling (\"Mod+1\", \"Ctrl+Shift+M\",\n * \"Enter\", \"Mod++\") rather than a literal hint someone already wrote out\n * (\"Cmd+1\", \"⌘1\", \"F1 twice\"). The test is strict on purpose: only the words\n * `Mod`, `Ctrl`, `Alt` and `Shift` (any case) count as modifiers, and the one\n * key must be a single character or a named key. A place that took a literal\n * string before (the pane shell's `shortcutHint`) can then take a combo as\n * well without changing what any existing literal draws.\n */\nexport function isKeyCombo(value: string): boolean {\n // \"Mod++\" is the plus key, as `parse` reads it: count it, then read the rest.\n const plus = value.length > 2 && value.endsWith(\"++\");\n const parts = (plus ? value.slice(0, -2) : value).split(\"+\");\n let keys = plus ? 1 : 0;\n for (const raw of parts) {\n if (raw === \"\") return false;\n const word = raw.toLowerCase();\n if (PORTABLE_MODS.has(word)) continue;\n if (raw.length === 1 || word in NAMED) { keys++; continue; }\n return false;\n }\n return keys === 1;\n}\n\nfunction keyLegend(key: string, mac: boolean): string {\n const named = NAMED[key.toLowerCase()];\n if (named) return mac ? named.mac : named.other;\n return key.length === 1 ? key.toUpperCase() : key;\n}\n\n/** The platform's spelling of a combo as one string: \"⇧⌘M\" on a Mac,\n * \"Ctrl+Shift+M\" elsewhere. For a tooltip or a menu label; a visible hint in\n * a window is `keyHint` (a keycap), not this. */\nexport function keyLabel(combo: string, platform: KeyPlatform = keyPlatform()): string {\n const mac = platform === \"mac\";\n const { mods, key } = parse(combo);\n const order = mac ? MAC_ORDER : OTHER_ORDER;\n const words = order.filter((m) => mods.includes(m)).map((m) => (mac ? MAC_MOD : OTHER_MOD)[m]);\n // On Windows \"Mod\" and \"Ctrl\" are both Ctrl; say it once.\n const seen = new Set<string>();\n const modWords = words.filter((w) => (seen.has(w) ? false : (seen.add(w), true)));\n const legend = key === undefined ? [] : [keyLegend(key, mac)];\n return mac ? [...modWords, ...legend].join(\"\") : [...modWords, ...legend].join(\"+\");\n}\n\n/** The legends of a combo as the platform would draw them on keycaps: one\n * entry on a Mac (Apple's glyph string is one legend), one per key elsewhere. */\nexport function keyLegends(combo: string, platform: KeyPlatform = keyPlatform()): string[] {\n if (platform === \"mac\") return [keyLabel(combo, platform)];\n const { mods, key } = parse(combo);\n const seen = new Set<string>();\n const words = OTHER_ORDER.filter((m) => mods.includes(m)).map((m) => OTHER_MOD[m])\n .filter((w) => (seen.has(w) ? false : (seen.add(w), true)));\n return key === undefined ? words : [...words, keyLegend(key, false)];\n}\n\nexport interface KeyHintOptions {\n /** Render for this platform rather than the detected one. */\n platform?: KeyPlatform;\n}\n\nfunction chip(legend: string): HTMLElement {\n const k = document.createElement(\"kbd\");\n k.className = \"shell-kbd\";\n k.textContent = legend;\n return k;\n}\n\n/**\n * A keycap for `combo`: `<kbd class=\"shell-kbd\">` (keys.css). On a Mac one\n * chip carrying the glyph string (\"⇧⌘M\"); elsewhere one chip per key\n * (\"Ctrl\" \"Shift\" \"M\") side by side in a `<span class=\"shell-keys\">`, with no\n * typed joiner: keycaps beside each other read as \"together\". `combo` may\n * also list ALTERNATIVES separated by spaces (\"Up Down\" for a move hint),\n * each drawn as its own keycap.\n */\nexport function keyHint(combo: string, opts: KeyHintOptions = {}): HTMLElement {\n const platform = opts.platform ?? keyPlatform();\n const legends = combo.trim().split(/\\s+/).flatMap((c) => keyLegends(c, platform));\n if (legends.length === 1) return chip(legends[0]!);\n const group = document.createElement(\"span\");\n group.className = \"shell-keys\";\n for (const legend of legends) group.append(chip(legend));\n return group;\n}\n\nexport interface HintBarItem {\n /** A combo, or space-separated alternatives (\"Up Down\"). */\n keys: string;\n /** What the key does, as a sentence-case fragment (\"Move\", \"Jump\"). */\n label: string;\n}\n\n/**\n * The hint bar under a picker or a tool window (\"↑↓ move · ↵ jump · esc\n * close\" as it used to be typed): one `<div class=\"shell-hintbar\">` of\n * `<span class=\"shell-hint\">` items, each a keycap (`keyHint`) and a label,\n * spaced by CSS and separated by nothing typed at all.\n */\nexport function hintBar(items: HintBarItem[], opts: KeyHintOptions = {}): HTMLElement {\n const bar = document.createElement(\"div\");\n bar.className = \"shell-hintbar\";\n for (const item of items) {\n const hint = document.createElement(\"span\");\n hint.className = \"shell-hint\";\n const label = document.createElement(\"span\");\n label.className = \"shell-hint-label\";\n label.textContent = item.label;\n hint.append(keyHint(item.keys, opts), label);\n bar.append(hint);\n }\n return bar;\n}\n\n/** Tooltip text with its key in brackets, platform-true: `tipWithKey(\"Show\n * scenes\", \"Mod+1\")` is \"Show scenes (⌘1)\" on a Mac and \"Show scenes\n * (Ctrl+1)\" elsewhere. For `data-tip`; the pane shell calls it itself when\n * `PaneSideConfig.shortcutHint` is a combo. */\nexport function tipWithKey(text: string, combo: string, platform: KeyPlatform = keyPlatform()): string {\n return `${text} (${keyLabel(combo, platform)})`;\n}\n"]}
package/dist/comments.css CHANGED
@@ -1,10 +1,10 @@
1
1
  /* src/comments.css */
2
2
  .shell-cmt-quote {
3
3
  margin: 0;
4
- padding: 0.2rem 0.5rem;
4
+ padding: 3px 8px;
5
5
  border-left: 2px solid var(--accent);
6
6
  color: var(--muted);
7
- font: italic 0.8rem var(--font-ui);
7
+ font: italic 13px var(--font-ui);
8
8
  }
9
9
  .shell-cmt-list {
10
10
  display: flex;
@@ -47,14 +47,14 @@
47
47
  color: var(--muted);
48
48
  }
49
49
  .shell-cmt-author {
50
- font: 600 0.76rem/1.2 var(--font-ui);
50
+ font: 600 12px/1.2 var(--font-ui);
51
51
  }
52
52
  .shell-cmt-ts {
53
- font: 0.68rem/1.2 var(--font-ui);
53
+ font: 11px/1.2 var(--font-ui);
54
54
  color: var(--muted);
55
55
  }
56
56
  .shell-cmt-body {
57
- font: 0.82rem/1.45 var(--font-read, var(--font-ui));
57
+ font: 13px/1.45 var(--font-read, var(--font-ui));
58
58
  white-space: pre-wrap;
59
59
  }
60
60
  .shell-cmt-input {
@@ -65,7 +65,7 @@
65
65
  border-radius: var(--radius-sm);
66
66
  background: var(--card);
67
67
  color: var(--ink);
68
- font: 0.82rem/1.4 var(--font-read, var(--font-ui));
68
+ font: 13px/1.4 var(--font-read, var(--font-ui));
69
69
  padding: 6px 8px;
70
70
  }
71
71
  .shell-cmt-input:focus {
@@ -78,7 +78,7 @@
78
78
  justify-content: flex-end;
79
79
  }
80
80
  .shell-cmt-btn {
81
- font: 0.76rem/1 var(--font-ui);
81
+ font: 12px/1 var(--font-ui);
82
82
  padding: 5px 10px;
83
83
  border-radius: var(--radius-sm);
84
84
  border: 1px solid var(--line);
@@ -111,11 +111,11 @@
111
111
  opacity: 0.55;
112
112
  }
113
113
  .shell-cmt-row-author {
114
- font: 600 0.74rem/1.2 var(--font-ui);
114
+ font: 600 12px/1.2 var(--font-ui);
115
115
  flex: none;
116
116
  }
117
117
  .shell-cmt-row-gist {
118
- font: 0.78rem/1.3 var(--font-read, var(--font-ui));
118
+ font: 12.5px/1.3 var(--font-read, var(--font-ui));
119
119
  color: var(--muted);
120
120
  overflow: hidden;
121
121
  text-overflow: ellipsis;
@@ -123,12 +123,12 @@
123
123
  }
124
124
  .shell-cmt-row-count {
125
125
  margin-left: auto;
126
- font: 0.7rem/1 var(--font-ui);
126
+ font: 11px/1 var(--font-ui);
127
127
  color: var(--muted);
128
128
  }
129
129
  .shell-cmt-new {
130
130
  align-self: flex-start;
131
- font: 0.76rem/1 var(--font-ui);
131
+ font: 12px/1 var(--font-ui);
132
132
  border: 1px dashed var(--line);
133
133
  background: none;
134
134
  color: var(--muted);
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/comments.css"],"sourcesContent":["/* A comment thread inside the anchored panel (comments.ts). */\n\n/* The span the thread is pinned to (quoteFor). A quotation rather than a heading:\n these are somebody else's words, not a title the panel gave itself. */\n.shell-cmt-quote {\n margin: 0; padding: 0.2rem 0.5rem;\n border-left: 2px solid var(--accent);\n color: var(--muted); font: italic 0.8rem var(--font-ui);\n}\n\n.shell-cmt-list { display: flex; flex-direction: column; gap: 8px; }\n.shell-cmt-list.resolved { opacity: 0.55; }\n.shell-cmt-msg-head { display: flex; gap: 8px; align-items: baseline; }\n/* The delete: QUIET but always there.\n It was rollover-revealed at first, following the grip grammar the lists use,\n and that was the wrong borrowing: a list row hides its grip because a dense\n column of them would be clutter, whereas this popover holds two or three\n messages and is already a surface you opened on purpose. Hidden, the only\n way to delete a comment was to discover that hovering one exact row revealed\n something - which is to say there was no way to delete a comment. Reported as\n \"there is apparently no delete option\", which is exactly right.\n Low contrast carries \"secondary\"; invisibility carries \"absent\". */\n.shell-cmt-del {\n margin-left: auto; flex: none; display: inline-flex; align-items: center; justify-content: center;\n padding: 2px; line-height: 1;\n background: none; border: none; cursor: pointer;\n color: var(--muted); opacity: 0.55;\n transition: opacity var(--dur-quick, 120ms) var(--ease-standard, ease),\n color var(--dur-quick, 120ms) var(--ease-standard, ease);\n}\n.shell-cmt-msg:hover .shell-cmt-del, .shell-cmt-del:focus-visible { opacity: 1; }\n.shell-cmt-del:hover { opacity: 1; color: var(--danger, #b00); }\n/* A withdrawn message keeps its place in the thread and stops asking to be read. */\n.shell-cmt-msg.gone .shell-cmt-body { font-style: italic; color: var(--muted); }\n.shell-cmt-author { font: 600 0.76rem/1.2 var(--font-ui); }\n.shell-cmt-ts { font: 0.68rem/1.2 var(--font-ui); color: var(--muted); }\n.shell-cmt-body { font: 0.82rem/1.45 var(--font-read, var(--font-ui)); white-space: pre-wrap; }\n.shell-cmt-input {\n width: 100%; box-sizing: border-box; resize: vertical;\n border: 1px solid var(--line-soft); border-radius: var(--radius-sm);\n background: var(--card); color: var(--ink);\n font: 0.82rem/1.4 var(--font-read, var(--font-ui)); padding: 6px 8px;\n}\n.shell-cmt-input:focus { outline: none; border-color: var(--accent); }\n.shell-cmt-actions { display: flex; gap: 6px; justify-content: flex-end; }\n.shell-cmt-btn {\n font: 0.76rem/1 var(--font-ui); padding: 5px 10px;\n border-radius: var(--radius-sm); border: 1px solid var(--line);\n background: var(--card); color: var(--ink); cursor: pointer;\n}\n.shell-cmt-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }\n/* The picker, when one thing has more than one thread. */\n.shell-cmt-row {\n display: flex; gap: 6px; align-items: baseline; width: 100%; text-align: left;\n border: 0; background: none; color: var(--ink); cursor: pointer;\n padding: 6px 4px; border-radius: var(--radius-sm);\n}\n.shell-cmt-row:hover { background: var(--accent-soft); }\n.shell-cmt-row.resolved { opacity: 0.55; }\n.shell-cmt-row-author { font: 600 0.74rem/1.2 var(--font-ui); flex: none; }\n.shell-cmt-row-gist {\n font: 0.78rem/1.3 var(--font-read, var(--font-ui)); color: var(--muted);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.shell-cmt-row-count { margin-left: auto; font: 0.7rem/1 var(--font-ui); color: var(--muted); }\n.shell-cmt-new {\n align-self: flex-start; font: 0.76rem/1 var(--font-ui);\n border: 1px dashed var(--line); background: none; color: var(--muted);\n border-radius: var(--radius-sm); padding: 5px 10px; cursor: pointer;\n}\n.shell-cmt-new:hover { color: var(--accent); border-color: var(--accent); }\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/comments.css"],"sourcesContent":["/* A comment thread inside the anchored panel (comments.ts). */\n\n/* The span the thread is pinned to (quoteFor). A quotation rather than a heading:\n these are somebody else's words, not a title the panel gave itself. */\n.shell-cmt-quote {\n margin: 0; padding: 3px 8px;\n border-left: 2px solid var(--accent);\n color: var(--muted); font: italic 13px var(--font-ui);\n}\n\n.shell-cmt-list { display: flex; flex-direction: column; gap: 8px; }\n.shell-cmt-list.resolved { opacity: 0.55; }\n.shell-cmt-msg-head { display: flex; gap: 8px; align-items: baseline; }\n/* The delete: QUIET but always there.\n It was rollover-revealed at first, following the grip grammar the lists use,\n and that was the wrong borrowing: a list row hides its grip because a dense\n column of them would be clutter, whereas this popover holds two or three\n messages and is already a surface you opened on purpose. Hidden, the only\n way to delete a comment was to discover that hovering one exact row revealed\n something - which is to say there was no way to delete a comment. Reported as\n \"there is apparently no delete option\", which is exactly right.\n Low contrast carries \"secondary\"; invisibility carries \"absent\". */\n.shell-cmt-del {\n margin-left: auto; flex: none; display: inline-flex; align-items: center; justify-content: center;\n padding: 2px; line-height: 1;\n background: none; border: none; cursor: pointer;\n color: var(--muted); opacity: 0.55;\n transition: opacity var(--dur-quick, 120ms) var(--ease-standard, ease),\n color var(--dur-quick, 120ms) var(--ease-standard, ease);\n}\n.shell-cmt-msg:hover .shell-cmt-del, .shell-cmt-del:focus-visible { opacity: 1; }\n.shell-cmt-del:hover { opacity: 1; color: var(--danger, #b00); }\n/* A withdrawn message keeps its place in the thread and stops asking to be read. */\n.shell-cmt-msg.gone .shell-cmt-body { font-style: italic; color: var(--muted); }\n.shell-cmt-author { font: 600 12px/1.2 var(--font-ui); }\n.shell-cmt-ts { font: 11px/1.2 var(--font-ui); color: var(--muted); }\n.shell-cmt-body { font: 13px/1.45 var(--font-read, var(--font-ui)); white-space: pre-wrap; }\n.shell-cmt-input {\n width: 100%; box-sizing: border-box; resize: vertical;\n border: 1px solid var(--line-soft); border-radius: var(--radius-sm);\n background: var(--card); color: var(--ink);\n font: 13px/1.4 var(--font-read, var(--font-ui)); padding: 6px 8px;\n}\n.shell-cmt-input:focus { outline: none; border-color: var(--accent); }\n.shell-cmt-actions { display: flex; gap: 6px; justify-content: flex-end; }\n.shell-cmt-btn {\n font: 12px/1 var(--font-ui); padding: 5px 10px;\n border-radius: var(--radius-sm); border: 1px solid var(--line);\n background: var(--card); color: var(--ink); cursor: pointer;\n}\n.shell-cmt-btn.primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }\n/* The picker, when one thing has more than one thread. */\n.shell-cmt-row {\n display: flex; gap: 6px; align-items: baseline; width: 100%; text-align: left;\n border: 0; background: none; color: var(--ink); cursor: pointer;\n padding: 6px 4px; border-radius: var(--radius-sm);\n}\n.shell-cmt-row:hover { background: var(--accent-soft); }\n.shell-cmt-row.resolved { opacity: 0.55; }\n.shell-cmt-row-author { font: 600 12px/1.2 var(--font-ui); flex: none; }\n.shell-cmt-row-gist {\n font: 12.5px/1.3 var(--font-read, var(--font-ui)); color: var(--muted);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n}\n.shell-cmt-row-count { margin-left: auto; font: 11px/1 var(--font-ui); color: var(--muted); }\n.shell-cmt-new {\n align-self: flex-start; font: 12px/1 var(--font-ui);\n border: 1px dashed var(--line); background: none; color: var(--muted);\n border-radius: var(--radius-sm); padding: 5px 10px; cursor: pointer;\n}\n.shell-cmt-new:hover { color: var(--accent); border-color: var(--accent); }\n"],"mappings":"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","names":[]}
package/dist/confirm.css CHANGED
@@ -1,9 +1,9 @@
1
1
  /* src/confirm.css */
2
2
  .confirm-dialog {
3
- --dialog-width: 24rem;
3
+ --dialog-width: 384px;
4
4
  }
5
5
  .confirm-body {
6
- font: 0.82rem/1.45 var(--font-ui);
6
+ font: 13px/1.45 var(--font-ui);
7
7
  color: var(--muted);
8
8
  }
9
9
  /*# sourceMappingURL=confirm.css.map */
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/confirm.css"],"sourcesContent":["/* ---------------------------------------------------------------------------\n The themed confirm (confirm.ts): what is confirm's own. The panel, the\n scrim, the motion and the actions row are the dialog frame's (dialog.css);\n the buttons are controls.css's `.btn` and `.btn.danger`. Import those two\n beside this file. Requires the host to supply --danger alongside the\n standard palette tokens.\n --------------------------------------------------------------------------- */\n\n.confirm-dialog { --dialog-width: 24rem; }\n.confirm-body { font: 0.82rem/1.45 var(--font-ui); color: var(--muted); }\n"],"mappings":";AAQA,CAAC;AAAiB,kBAAgB;AAAO;AACzC,CAAC;AAAe,QAAM,OAAO,CAAC,KAAK,IAAI;AAAY,SAAO,IAAI;AAAU;","names":[]}
1
+ {"version":3,"sources":["../src/confirm.css"],"sourcesContent":["/* ---------------------------------------------------------------------------\n The themed confirm (confirm.ts): what is confirm's own. The panel, the\n scrim, the motion and the actions row are the dialog frame's (dialog.css);\n the buttons are controls.css's `.btn` and `.btn.danger`. Import those two\n beside this file. Requires the host to supply --danger alongside the\n standard palette tokens.\n --------------------------------------------------------------------------- */\n\n.confirm-dialog { --dialog-width: 384px; }\n.confirm-body { font: 13px/1.45 var(--font-ui); color: var(--muted); }\n"],"mappings":";AAQA,CAAC;AAAiB,kBAAgB;AAAO;AACzC,CAAC;AAAe,QAAM,IAAI,CAAC,KAAK,IAAI;AAAY,SAAO,IAAI;AAAU;","names":[]}