@wildwinter/app-shell 0.46.1 → 0.46.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/anchored.css +4 -0
- package/dist/anchored.css.map +1 -1
- package/dist/chunk-CPNTROWD.js +213 -0
- package/dist/chunk-CPNTROWD.js.map +1 -0
- package/dist/{chunk-P4G4ERG6.js → chunk-PFERSFRB.js} +3 -2
- package/dist/chunk-PFERSFRB.js.map +1 -0
- package/dist/{chunk-OPZPFOPS.js → chunk-WKPKBAXS.js} +3 -3
- package/dist/chunk-WKPKBAXS.js.map +1 -0
- package/dist/context-menu.css +6 -0
- package/dist/context-menu.css.map +1 -1
- package/dist/dialog.css +4 -0
- package/dist/dialog.css.map +1 -1
- package/dist/floating.cjs +1 -0
- package/dist/floating.cjs.map +1 -1
- package/dist/floating.js +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/settings.css +4 -0
- package/dist/settings.css.map +1 -1
- package/dist/toast.css +4 -0
- package/dist/toast.css.map +1 -1
- package/dist/tokens.css +4 -0
- package/dist/tokens.css.map +1 -1
- package/dist/tool-window.cjs +49 -2
- package/dist/tool-window.cjs.map +1 -1
- package/dist/tool-window.d.cts +10 -1
- package/dist/tool-window.d.ts +10 -1
- package/dist/tool-window.js +1 -165
- package/dist/tool-window.js.map +1 -1
- package/dist/updater-view.cjs +1 -1
- package/dist/updater-view.cjs.map +1 -1
- package/dist/updater-view.js +1 -1
- package/dist/updater.cjs +64 -3
- package/dist/updater.cjs.map +1 -1
- package/dist/updater.d.cts +4 -0
- package/dist/updater.d.ts +4 -0
- package/dist/updater.js +24 -4
- package/dist/updater.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-OPZPFOPS.js.map +0 -1
- package/dist/chunk-P4G4ERG6.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { mountWelcome } from './chunk-CUFREPJ5.js';
|
|
2
|
-
export { feedUpdaterDownloadProgress, showUpdaterDialog } from './chunk-
|
|
2
|
+
export { feedUpdaterDownloadProgress, showUpdaterDialog } from './chunk-WKPKBAXS.js';
|
|
3
3
|
export { defaultProblemCopy, describeProblem, problemLine, problemName } from './chunk-4OAORWEM.js';
|
|
4
4
|
export { PROPERTY_NAME_HINT, PROPERTY_TYPES, RESERVED_PROPERTY_NAMES, bindPropertyName, bindPropertyRef, defaultControl, dupGuard, expandableRow, firstIllegalPropertyName, focusNewRow, isCaseOnlyPropertyName, isValidPropertyName, mountPropertyList, mountSettingsDialog, propertyNameProblem, propertyNameify, propertyRefProblem, revalidatePropertyRefs, revealRow, revealRowWhenReady } from './chunk-R3NXPHRZ.js';
|
|
5
5
|
import { isKeyCombo, tipWithKey } from './chunk-YWMSQFXX.js';
|
|
@@ -11,7 +11,7 @@ export { copyWithFeedback, debounce, formatCount, isEditableTarget, plural, rela
|
|
|
11
11
|
export { EDIT_MENU, FILE_MENU, GO_MENU, PANE_MENU, PLAY_MENU, PUBLISH_MENU, REVIEW_MENU, VIEW_MENU } from './chunk-2ECJUTSX.js';
|
|
12
12
|
import { dialogFrame } from './chunk-YRFJLJUH.js';
|
|
13
13
|
export { dialogFrame } from './chunk-YRFJLJUH.js';
|
|
14
|
-
export { createFloating } from './chunk-
|
|
14
|
+
export { createFloating } from './chunk-PFERSFRB.js';
|
|
15
15
|
export { JOB_PROGRESS } from './chunk-MDGOIT3O.js';
|
|
16
16
|
export { openContextMenu, openPopover } from './chunk-EHR2KBZC.js';
|
|
17
17
|
import { ensureTooltipHost, el, iconNode, labelled, icon } from './chunk-UG2P777F.js';
|
package/dist/settings.css
CHANGED
package/dist/settings.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/settings.css"],"sourcesContent":["/* ============================================================================\n The settings dialog + list-editor styling. Reads the shared token grammar\n (tokens.css) and the app's palette vars (--surface / --ink / --muted / --line\n / --accent / --danger / --bg, optional --line-soft / --accent-soft). Neutral\n class names; the app supplies section content.\n ============================================================================ */\n\n.settings-dialog {\n padding: 0; border: 1px solid var(--line); border-radius: var(--radius-lg);\n background: var(--surface); color: var(--ink);\n width: min(760px, 92vw); max-height: 82vh;\n box-shadow: var(--shadow-3);\n font: 14px var(--font-ui, system-ui, sans-serif);\n}\n/* Its own frame (the tab rail makes the generic dialog frame a poor fit), on\n the same tokens as dialog.css: the one scrim, the one radius, the one panel\n motion. */\n.settings-dialog { animation: var(--anim-panel-in); }\n.settings-dialog::backdrop { background: var(--scrim); backdrop-filter: blur(3px); animation: var(--anim-backdrop-in); }\n.settings-form { display: flex; flex-direction: column; max-height: 82vh; }\n.settings-title { margin: 0; padding: 16px 20px; font-family: var(--font-read, inherit); font-size: 18.5px; font-weight: 600; border-bottom: 1px solid var(--line-soft, var(--line)); }\n\n.settings-body { display: flex; min-height: 0; flex: 1; }\n.settings-tabs { flex: none; width: 192px; overflow-y: auto; padding: 10px 8px; border-right: 1px solid var(--line-soft, var(--line)); display: flex; flex-direction: column; gap: 1px; }\n.settings-group { padding: 12px 8px 4px; font: 600 12.5px/1.2 var(--font-ui); color: var(--muted); }\n.settings-tab { text-align: left; width: 100%; padding: 6px 10px; border: 0; border-radius: var(--radius-sm); background: none; color: var(--ink); font: inherit; font-size: 14px; cursor: pointer; }\n.settings-tab:hover { background: var(--accent-soft, color-mix(in oklab, var(--accent) 12%, transparent)); }\n.settings-tab.active { background: var(--accent-soft, color-mix(in oklab, var(--accent) 16%, transparent)); color: var(--accent); font-weight: 500; }\n/* Dimmed and inert, with the reason on its rollover; it must not light up on\n hover like a live tab. */\n.settings-tab.is-disabled { color: var(--muted); opacity: 0.5; cursor: default; }\n.settings-tab.is-disabled:hover { background: none; }\n\n.settings-panels { flex: 1; min-width: 0; overflow-y: auto; padding: 18px 22px; }\n.settings-panel { display: flex; flex-direction: column; gap: 14px; }\n/* A `display` on the class beats the UA's `[hidden] { display: none }`, so the\n panels the tab switcher hides would all render at once. Opt back out. */\n.settings-panel[hidden] { display: none; }\n\n.settings-actions { display: flex; align-items: center; gap: 10px; padding: 12px 20px; border-top: 1px solid var(--line-soft, var(--line)); }\n.settings-error { flex: 1; margin: 0; color: var(--danger); font-size: 13px; }\n.settings-actions button { font: inherit; padding: 6px 16px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: none; color: var(--muted); cursor: pointer; }\n.settings-actions button:hover { color: var(--accent); border-color: var(--accent); }\n.settings-actions .primary { background: var(--accent); color: var(--surface); border-color: transparent; font-weight: 500; }\n.settings-actions .primary:hover { color: var(--surface); filter: brightness(1.06); }\n\n/* --- list editor (rows + disclosure + inline field kit) --------------------- */\n/* A row is a WELL, not a ruled line: bordered, rounded, and sitting on `--card`\n rather than on the panel behind it. Patterpad's design, adopted as the family's\n (author's ruling, 2026-08-16), reversing the usual direction the way the bars\n did. A flat rule reads as a table of text; a well reads as a thing you can\n edit, which is what every one of these rows is. Rows are separated by the\n list's `gap`, so nothing here draws a divider. */\n.set-row { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--card); }\n/* Where a jump landed (revealRow): lit for a moment, then left alone. A colour\n fade and not a movement, so it stays under reduced motion; the scroll before\n it is what goes instant there. */\n.set-row.landed { animation: set-landed 1.8s var(--ease-standard) both; }\n@keyframes set-landed {\n 0%, 35% { box-shadow: 0 0 0 2px var(--accent); }\n 100% { box-shadow: 0 0 0 2px transparent; }\n}\n.set-rowline { display: flex; align-items: center; gap: 8px; padding: 5px 7px; }\n/* Indented past the disclosure so the secondary fields hang under the row's\n content rather than under its triangle, and ruled off with a lighter line than\n the well's own border. */\n.set-details {\n display: flex; flex-direction: column; gap: 7px; align-items: stretch;\n padding: 6px 8px 8px 21px;\n border-top: 1px solid color-mix(in oklab, var(--line) 60%, transparent);\n}\n.set-details[hidden] { display: none; } /* see .settings-panel[hidden] */\n/* The disclosure triangle is drawn, not typed: U+25B8/25BE are \"small\"\n triangles that stay hairline-thin at any font size. Rotates when open. */\n.set-expand { flex: none; width: 18px; height: 18px; border: 0; background: none; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }\n.set-expand::before {\n content: \"\"; width: 0; height: 0;\n border-left: 6px solid currentColor; border-top: 4.5px solid transparent; border-bottom: 4.5px solid transparent;\n transform-origin: 35% 50%; transition: transform var(--dur-quick) var(--ease-standard);\n}\n.set-expand { color: var(--muted); }\n.set-expand:hover, .set-expand.open { color: var(--accent); }\n.set-expand.open::before { transform: rotate(90deg); }\n\n.set-rowline input, .set-rowline select, .set-details input, .set-details select {\n font: inherit; font-size: 13.5px; background: var(--bg); color: var(--ink);\n border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 4px 8px;\n}\n.set-rowline input.set-name { flex: 1; min-width: 96px; }\n.set-rowline input.invalid, .set-details input.invalid { border-color: var(--danger); }\n/* `.illegal`: a value that breaks a RULE, as opposed to `.invalid`, which the dup guard\n owns for a value that clashes with another. Both can be true of one field, so they are\n separate classes and draw the same. */\n.set-rowline input.illegal, .set-details input.illegal { border-color: var(--danger); }\n.set-rowline input:focus-visible, .set-rowline select:focus-visible,\n.set-details input:focus-visible, .set-details select:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; border-color: var(--accent); }\n\n/* The square icon button (`iconBtn`): a flex box centring a 14px drawing.\n (Until 0.39 it centred a typed glyph, which needed a workaround for the\n font's metrics; a drawn icon has none.) */\n.shell-icon { flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 0; line-height: 1; width: 24px; height: 24px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: none; color: var(--muted); cursor: pointer; }\n.shell-icon:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }\n.shell-icon:disabled { opacity: 0.4; cursor: default; }\n.shell-icon.danger:hover:not(:disabled) { color: var(--surface); background: var(--danger); border-color: var(--danger); }\n\n.shell-labelled { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }\n.shell-fieldcap { color: var(--muted); min-width: 80px; }\n\n.shell-tags { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }\n.shell-tag { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; padding: 2px 4px 2px 9px; border: 1px solid var(--line); border-radius: var(--radius-pill); color: var(--ink); }\n.shell-tag-x { display: inline-flex; align-items: center; justify-content: center; border: 0; background: none; color: var(--muted); cursor: pointer; padding: 0 2px; }\n.shell-tag-x:hover:not(:disabled) { color: var(--danger); }\n/* stageChips' move buttons at either end of the order. */\n.shell-tag-x:disabled { opacity: 0.35; cursor: default; }\n.shell-tag-move:hover:not(:disabled) { color: var(--accent); }\n.shell-tag-input { min-width: 128px; }\n\n/* The property list (property-list.ts) on top of the row kit above. */\n.set-list { display: flex; flex-direction: column; gap: 6px; }\n.set-rowline select.set-type { flex: none; }\n.set-rowline .set-default { flex: 1; min-width: 80px; }\n.set-flagnote { flex: 1; color: var(--muted); font-size: 13px; font-style: italic; }\n.set-details input.set-purpose { flex: 1; }\n.set-add { align-self: flex-start; margin-top: 6px; }\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/settings.css"],"sourcesContent":["/* ============================================================================\n The settings dialog + list-editor styling. Reads the shared token grammar\n (tokens.css) and the app's palette vars (--surface / --ink / --muted / --line\n / --accent / --danger / --bg, optional --line-soft / --accent-soft). Neutral\n class names; the app supplies section content.\n ============================================================================ */\n\n.settings-dialog {\n padding: 0; border: 1px solid var(--line); border-radius: var(--radius-lg);\n background: var(--surface); color: var(--ink);\n width: min(760px, 92vw); max-height: 82vh;\n box-shadow: var(--shadow-3);\n font: 14px var(--font-ui, system-ui, sans-serif);\n}\n/* Its own frame (the tab rail makes the generic dialog frame a poor fit), on\n the same tokens as dialog.css: the one scrim, the one radius, the one panel\n motion. */\n.settings-dialog { animation: var(--anim-panel-in); }\n.settings-dialog::backdrop { background: var(--scrim); backdrop-filter: blur(3px); animation: var(--anim-backdrop-in); }\n.settings-form { display: flex; flex-direction: column; max-height: 82vh; }\n.settings-title { margin: 0; padding: 16px 20px; font-family: var(--font-read, inherit); font-size: 18.5px; font-weight: 600; border-bottom: 1px solid var(--line-soft, var(--line)); }\n\n.settings-body { display: flex; min-height: 0; flex: 1; }\n.settings-tabs { flex: none; width: 192px; overflow-y: auto; padding: 10px 8px; border-right: 1px solid var(--line-soft, var(--line)); display: flex; flex-direction: column; gap: 1px; }\n.settings-group { padding: 12px 8px 4px; font: 600 12.5px/1.2 var(--font-ui); color: var(--muted); }\n.settings-tab { text-align: left; width: 100%; padding: 6px 10px; border: 0; border-radius: var(--radius-sm); background: none; color: var(--ink); font: inherit; font-size: 14px; cursor: pointer; }\n.settings-tab:hover { background: var(--accent-soft, color-mix(in oklab, var(--accent) 12%, transparent)); }\n.settings-tab.active { background: var(--accent-soft, color-mix(in oklab, var(--accent) 16%, transparent)); color: var(--accent); font-weight: 500; }\n/* Dimmed and inert, with the reason on its rollover; it must not light up on\n hover like a live tab. */\n.settings-tab.is-disabled { color: var(--muted); opacity: 0.5; cursor: default; }\n.settings-tab.is-disabled:hover { background: none; }\n\n.settings-panels { flex: 1; min-width: 0; overflow-y: auto; padding: 18px 22px; }\n.settings-panel { display: flex; flex-direction: column; gap: 14px; }\n/* A `display` on the class beats the UA's `[hidden] { display: none }`, so the\n panels the tab switcher hides would all render at once. Opt back out. */\n.settings-panel[hidden] { display: none; }\n\n.settings-actions { display: flex; align-items: center; gap: 10px; padding: 12px 20px; border-top: 1px solid var(--line-soft, var(--line)); }\n.settings-error { flex: 1; margin: 0; color: var(--danger); font-size: 13px; }\n.settings-actions button { font: inherit; padding: 6px 16px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: none; color: var(--muted); cursor: pointer; }\n.settings-actions button:hover { color: var(--accent); border-color: var(--accent); }\n.settings-actions .primary { background: var(--accent); color: var(--surface); border-color: transparent; font-weight: 500; }\n.settings-actions .primary:hover { color: var(--surface); filter: brightness(1.06); }\n\n/* --- list editor (rows + disclosure + inline field kit) --------------------- */\n/* A row is a WELL, not a ruled line: bordered, rounded, and sitting on `--card`\n rather than on the panel behind it. Patterpad's design, adopted as the family's\n (author's ruling, 2026-08-16), reversing the usual direction the way the bars\n did. A flat rule reads as a table of text; a well reads as a thing you can\n edit, which is what every one of these rows is. Rows are separated by the\n list's `gap`, so nothing here draws a divider. */\n.set-row { display: flex; flex-direction: column; border: 1px solid var(--line); border-radius: var(--radius-md); background: var(--card); }\n/* Where a jump landed (revealRow): lit for a moment, then left alone. A colour\n fade and not a movement, so it stays under reduced motion; the scroll before\n it is what goes instant there. */\n.set-row.landed { animation: set-landed 1.8s var(--ease-standard) both; }\n@keyframes set-landed {\n 0%, 35% { box-shadow: 0 0 0 2px var(--accent); }\n 100% { box-shadow: 0 0 0 2px transparent; }\n}\n.set-rowline { display: flex; align-items: center; gap: 8px; padding: 5px 7px; }\n/* Indented past the disclosure so the secondary fields hang under the row's\n content rather than under its triangle, and ruled off with a lighter line than\n the well's own border. */\n.set-details {\n display: flex; flex-direction: column; gap: 7px; align-items: stretch;\n padding: 6px 8px 8px 21px;\n border-top: 1px solid color-mix(in oklab, var(--line) 60%, transparent);\n}\n.set-details[hidden] { display: none; } /* see .settings-panel[hidden] */\n/* The disclosure triangle is drawn, not typed: U+25B8/25BE are \"small\"\n triangles that stay hairline-thin at any font size. Rotates when open. */\n.set-expand { flex: none; width: 18px; height: 18px; border: 0; background: none; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }\n.set-expand::before {\n content: \"\"; width: 0; height: 0;\n border-left: 6px solid currentColor; border-top: 4.5px solid transparent; border-bottom: 4.5px solid transparent;\n transform-origin: 35% 50%; transition: transform var(--dur-quick) var(--ease-standard);\n}\n.set-expand { color: var(--muted); }\n.set-expand:hover, .set-expand.open { color: var(--accent); }\n.set-expand.open::before { transform: rotate(90deg); }\n\n.set-rowline input, .set-rowline select, .set-details input, .set-details select {\n font: inherit; font-size: 13.5px; background: var(--bg); color: var(--ink);\n border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 4px 8px;\n}\n.set-rowline input.set-name { flex: 1; min-width: 96px; }\n.set-rowline input.invalid, .set-details input.invalid { border-color: var(--danger); }\n/* `.illegal`: a value that breaks a RULE, as opposed to `.invalid`, which the dup guard\n owns for a value that clashes with another. Both can be true of one field, so they are\n separate classes and draw the same. */\n.set-rowline input.illegal, .set-details input.illegal { border-color: var(--danger); }\n.set-rowline input:focus-visible, .set-rowline select:focus-visible,\n.set-details input:focus-visible, .set-details select:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; border-color: var(--accent); }\n\n/* The square icon button (`iconBtn`): a flex box centring a 14px drawing.\n (Until 0.39 it centred a typed glyph, which needed a workaround for the\n font's metrics; a drawn icon has none.) */\n.shell-icon { flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 0; line-height: 1; width: 24px; height: 24px; border: 1px solid var(--line); border-radius: var(--radius-sm); background: none; color: var(--muted); cursor: pointer; }\n.shell-icon:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }\n.shell-icon:disabled { opacity: 0.4; cursor: default; }\n.shell-icon.danger:hover:not(:disabled) { color: var(--surface); background: var(--danger); border-color: var(--danger); }\n\n.shell-labelled { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }\n.shell-fieldcap { color: var(--muted); min-width: 80px; }\n\n.shell-tags { display: flex; flex-wrap: wrap; gap: 5px; align-items: center; }\n.shell-tag { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; padding: 2px 4px 2px 9px; border: 1px solid var(--line); border-radius: var(--radius-pill); color: var(--ink); }\n.shell-tag-x { display: inline-flex; align-items: center; justify-content: center; border: 0; background: none; color: var(--muted); cursor: pointer; padding: 0 2px; }\n.shell-tag-x:hover:not(:disabled) { color: var(--danger); }\n/* stageChips' move buttons at either end of the order. */\n.shell-tag-x:disabled { opacity: 0.35; cursor: default; }\n.shell-tag-move:hover:not(:disabled) { color: var(--accent); }\n.shell-tag-input { min-width: 128px; }\n\n/* The property list (property-list.ts) on top of the row kit above. */\n.set-list { display: flex; flex-direction: column; gap: 6px; }\n.set-rowline select.set-type { flex: none; }\n.set-rowline .set-default { flex: 1; min-width: 80px; }\n.set-flagnote { flex: 1; color: var(--muted); font-size: 13px; font-style: italic; }\n.set-details input.set-purpose { flex: 1; }\n.set-add { align-self: flex-start; margin-top: 6px; }\n\n/* Never a window-drag region, even where it lies over one. Electron hands a click inside a\n `-webkit-app-region: drag` area to the OS as a window drag before the page sees it, and a modal\n making the page behind it inert does not change that, so a layer drawn over the welcome\n screen's whole-window drag region lost every click while the keyboard still worked\n (2026-09-28: first seen on the identity dialog and About). Reproduced in a real\n Electron window; `no-drag` on the layer itself is the fix. */\n.settings-dialog, .settings-dialog * { -webkit-app-region: no-drag; }\n"],"mappings":";AAOA,CAAC;AACC,WAAS;AAAG,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAC9D,cAAY,IAAI;AAAY,SAAO,IAAI;AACvC,SAAO,IAAI,KAAK,EAAE;AAAO,cAAY;AACrC,cAAY,IAAI;AAChB,QAAM,KAAK,IAAI,SAAS,EAAE,SAAS,EAAE;AACvC;AAIA,CAVC;AAUkB,aAAW,IAAI;AAAkB;AACpD,CAXC,eAWe;AAAa,cAAY,IAAI;AAAU,mBAAiB,KAAK;AAAM,aAAW,IAAI;AAAqB;AACvH,CAAC;AAAgB,WAAS;AAAM,kBAAgB;AAAQ,cAAY;AAAM;AAC1E,CAAC;AAAiB,UAAQ;AAAG,WAAS,KAAK;AAAM,eAAa,IAAI,WAAW,EAAE;AAAU,aAAW;AAAQ,eAAa;AAAK,iBAAe,IAAI,MAAM,IAAI,WAAW,EAAE,IAAI;AAAU;AAEtL,CAAC;AAAgB,WAAS;AAAM,cAAY;AAAG,QAAM;AAAG;AACxD,CAAC;AAAgB,QAAM;AAAM,SAAO;AAAO,cAAY;AAAM,WAAS,KAAK;AAAK,gBAAc,IAAI,MAAM,IAAI,WAAW,EAAE,IAAI;AAAU,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK;AACxL,CAAC;AAAiB,WAAS,KAAK,IAAI;AAAK,QAAM,IAAI,MAAM,CAAC,IAAI,IAAI;AAAY,SAAO,IAAI;AAAU;AACnG,CAAC;AAAe,cAAY;AAAM,SAAO;AAAM,WAAS,IAAI;AAAM,UAAQ;AAAG,iBAAe,IAAI;AAAc,cAAY;AAAM,SAAO,IAAI;AAAQ,QAAM;AAAS,aAAW;AAAM,UAAQ;AAAS;AACpM,CADC,YACY;AAAS,cAAY,IAAI,aAAa,EAAE,UAAU,GAAG,KAAK,EAAE,IAAI,UAAU,GAAG,EAAE;AAAe;AAC3G,CAFC,YAEY,CAAC;AAAS,cAAY,IAAI,aAAa,EAAE,UAAU,GAAG,KAAK,EAAE,IAAI,UAAU,GAAG,EAAE;AAAe,SAAO,IAAI;AAAW,eAAa;AAAK;AAGpJ,CALC,YAKY,CAAC;AAAc,SAAO,IAAI;AAAU,WAAS;AAAK,UAAQ;AAAS;AAChF,CANC,YAMY,CADC,WACW;AAAS,cAAY;AAAM;AAEpD,CAAC;AAAkB,QAAM;AAAG,aAAW;AAAG,cAAY;AAAM,WAAS,KAAK;AAAM;AAChF,CAAC;AAAiB,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAM;AAGpE,CAHC,cAGc,CAAC;AAAU,WAAS;AAAM;AAEzC,CAAC;AAAmB,WAAS;AAAM,eAAa;AAAQ,OAAK;AAAM,WAAS,KAAK;AAAM,cAAY,IAAI,MAAM,IAAI,WAAW,EAAE,IAAI;AAAU;AAC5I,CAAC;AAAiB,QAAM;AAAG,UAAQ;AAAG,SAAO,IAAI;AAAW,aAAW;AAAM;AAC7E,CAFC,iBAEiB;AAAS,QAAM;AAAS,WAAS,IAAI;AAAM,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAAc,cAAY;AAAM,SAAO,IAAI;AAAU,UAAQ;AAAS;AACrL,CAHC,iBAGiB,MAAM;AAAS,SAAO,IAAI;AAAW,gBAAc,IAAI;AAAW;AACpF,CAJC,iBAIiB,CAAC;AAAU,cAAY,IAAI;AAAW,SAAO,IAAI;AAAY,gBAAc;AAAa,eAAa;AAAK;AAC5H,CALC,iBAKiB,CADC,OACO;AAAS,SAAO,IAAI;AAAY,UAAQ,WAAW;AAAO;AASpF,CAAC;AAAU,WAAS;AAAM,kBAAgB;AAAQ,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAAc,cAAY,IAAI;AAAS;AAI3I,CAJC,OAIO,CAAC;AAAS,aAAW,WAAW,KAAK,IAAI,iBAAiB;AAAM;AACxE,WAD6B;AAE3B;AAAU,gBAAY,EAAE,EAAE,EAAE,IAAI,IAAI;AAAW;AAC/C;AAAO,gBAAY,EAAE,EAAE,EAAE,IAAI;AAAa;AAC5C;AACA,CAAC;AAAc,WAAS;AAAM,eAAa;AAAQ,OAAK;AAAK,WAAS,IAAI;AAAK;AAI/E,CAAC;AACC,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK,eAAa;AAC9D,WAAS,IAAI,IAAI,IAAI;AACrB,cAAY,IAAI,MAAM,UAAU,GAAG,KAAK,EAAE,IAAI,QAAQ,GAAG,EAAE;AAC7D;AACA,CALC,WAKW,CAAC;AAAU,WAAS;AAAM;AAGtC,CAAC;AAAa,QAAM;AAAM,SAAO;AAAM,UAAQ;AAAM,UAAQ;AAAG,cAAY;AAAM,UAAQ;AAAS,WAAS;AAAM,eAAa;AAAQ,mBAAiB;AAAQ,WAAS;AAAG;AAC5K,CADC,UACU;AACT,WAAS;AAAI,SAAO;AAAG,UAAQ;AAC/B,eAAa,IAAI,MAAM;AAAc,cAAY,MAAM,MAAM;AAAa,iBAAe,MAAM,MAAM;AACrG,oBAAkB,IAAI;AAAK,cAAY,UAAU,IAAI,aAAa,IAAI;AACxE;AACA,CANC;AAMa,SAAO,IAAI;AAAU;AACnC,CAPC,UAOU;AAAQ,CAPlB,UAO6B,CAAC;AAAO,SAAO,IAAI;AAAW;AAC5D,CARC,UAQU,CADoB,IACf;AAAW,aAAW,OAAO;AAAQ;AAErD,CAtBC,YAsBY;AAAO,CAtBnB,YAsBgC;AAAQ,CAlBxC,YAkBqD;AAAO,CAlB5D,YAkByE;AACxE,QAAM;AAAS,aAAW;AAAQ,cAAY,IAAI;AAAO,SAAO,IAAI;AACpE,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAAc,WAAS,IAAI;AAC/E;AACA,CA1BC,YA0BY,KAAK,CAAC;AAAW,QAAM;AAAG,aAAW;AAAM;AACxD,CA3BC,YA2BY,KAAK,CAAC;AAAS,CAvB3B,YAuBwC,KAAK,CAA3B;AAAsC,gBAAc,IAAI;AAAW;AAItF,CA/BC,YA+BY,KAAK,CAAC;AAAS,CA3B3B,YA2BwC,KAAK,CAA3B;AAAsC,gBAAc,IAAI;AAAW;AACtF,CAhCC,YAgCY,KAAK;AAAgB,CAhCjC,YAgC8C,MAAM;AACrD,CA7BC,YA6BY,KAAK;AAAgB,CA7BjC,YA6B8C,MAAM;AAAiB,WAAS,IAAI,MAAM,IAAI;AAAW,kBAAgB;AAAG,gBAAc,IAAI;AAAW;AAKxJ,CAAC;AAAa,QAAM;AAAM,WAAS;AAAa,eAAa;AAAQ,mBAAiB;AAAQ,WAAS;AAAG,eAAa;AAAG,SAAO;AAAM,UAAQ;AAAM,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAAc,cAAY;AAAM,SAAO,IAAI;AAAU,UAAQ;AAAS;AAC7Q,CADC,UACU,MAAM,KAAK;AAAa,SAAO,IAAI;AAAW,gBAAc,IAAI;AAAW;AACtF,CAFC,UAEU;AAAY,WAAS;AAAK,UAAQ;AAAS;AACtD,CAHC,UAGU,CAAC,MAAM,MAAM,KAAK;AAAa,SAAO,IAAI;AAAY,cAAY,IAAI;AAAW,gBAAc,IAAI;AAAW;AAEzH,CAAC;AAAiB,WAAS;AAAM,eAAa;AAAQ,OAAK;AAAK,aAAW;AAAQ;AACnF,CAAC;AAAiB,SAAO,IAAI;AAAU,aAAW;AAAM;AAExD,CAAC;AAAa,WAAS;AAAM,aAAW;AAAM,OAAK;AAAK,eAAa;AAAQ;AAC7E,CAAC;AAAY,WAAS;AAAa,eAAa;AAAQ,OAAK;AAAK,aAAW;AAAM,WAAS,IAAI,IAAI,IAAI;AAAK,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAAgB,SAAO,IAAI;AAAQ;AAClM,CAAC;AAAc,WAAS;AAAa,eAAa;AAAQ,mBAAiB;AAAQ,UAAQ;AAAG,cAAY;AAAM,SAAO,IAAI;AAAU,UAAQ;AAAS,WAAS,EAAE;AAAK;AACtK,CADC,WACW,MAAM,KAAK;AAAa,SAAO,IAAI;AAAW;AAE1D,CAHC,WAGW;AAAY,WAAS;AAAM,UAAQ;AAAS;AACxD,CAAC,cAAc,MAAM,KAAK;AAAa,SAAO,IAAI;AAAW;AAC7D,CAAC;AAAkB,aAAW;AAAO;AAGrC,CAAC;AAAW,WAAS;AAAM,kBAAgB;AAAQ,OAAK;AAAK;AAC7D,CAzDC,YAyDY,MAAM,CAAC;AAAW,QAAM;AAAM;AAC3C,CA1DC,YA0DY,CAAC;AAAc,QAAM;AAAG,aAAW;AAAM;AACtD,CAAC;AAAe,QAAM;AAAG,SAAO,IAAI;AAAU,aAAW;AAAM,cAAY;AAAQ;AACnF,CAxDC,YAwDY,KAAK,CAAC;AAAc,QAAM;AAAG;AAC1C,CAAC;AAAU,cAAY;AAAY,cAAY;AAAK;AAQpD,CA5HC;AA4HiB,CA5HjB,gBA4HkC;AAAI,sBAAoB;AAAS;","names":[]}
|
package/dist/toast.css
CHANGED
package/dist/toast.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/toast.css"],"sourcesContent":["/* The toast (toast.ts): a transient remark, bottom-right, an outlined card on the\n surface colour. The KIND is carried by the border and the text, never by a\n solid fill: a remark should not shout, and --ink on --surface reads on every\n palette without a tuned contrast pair. Tokens only, so it lands in any palette\n and any window (multi-window-rules.md). */\n.shell-toast {\n position: fixed; right: 16px; bottom: 35px; z-index: 40; max-width: 416px;\n background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);\n padding: 9px 13px; color: var(--ink); font: 13px/1.4 var(--font-ui);\n /* A long unbroken token (a file path) wraps INSIDE the card; a newline in the\n message is a line break. */\n overflow-wrap: anywhere; white-space: pre-line;\n box-shadow: var(--shadow-2);\n animation: var(--anim-panel-in, none);\n}\n.shell-toast.ok { border-color: var(--ok); color: var(--ok); }\n.shell-toast.error { border-color: var(--danger); color: var(--danger); }\n/* The eased dismiss (closeWithExit), mirroring the enter. */\n.shell-toast.closing { animation: var(--anim-panel-out); }\n.shell-toast[hidden] { display: none; }\n"],"mappings":";AAKA,CAAC;AACC,YAAU;AAAO,SAAO;AAAM,UAAQ;AAAM,WAAS;AAAI,aAAW;AACpE,cAAY,IAAI;AAAY,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAC9E,WAAS,IAAI;AAAM,SAAO,IAAI;AAAQ,QAAM,IAAI,CAAC,IAAI,IAAI;AAGzD,iBAAe;AAAU,eAAa;AACtC,cAAY,IAAI;AAChB,aAAW,IAAI,eAAe,EAAE;AAClC;AACA,CAVC,WAUW,CAAC;AAAQ,gBAAc,IAAI;AAAW,SAAO,IAAI;AAAO;AACpE,CAXC,WAWW,CAAC;AAAQ,gBAAc,IAAI;AAAW,SAAO,IAAI;AAAW;AAExE,CAbC,WAaW,CAAC;AAAU,aAAW,IAAI;AAAmB;AACzD,CAdC,WAcW,CAAC;AAAU,WAAS;AAAM;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/toast.css"],"sourcesContent":["/* The toast (toast.ts): a transient remark, bottom-right, an outlined card on the\n surface colour. The KIND is carried by the border and the text, never by a\n solid fill: a remark should not shout, and --ink on --surface reads on every\n palette without a tuned contrast pair. Tokens only, so it lands in any palette\n and any window (multi-window-rules.md). */\n.shell-toast {\n position: fixed; right: 16px; bottom: 35px; z-index: 40; max-width: 416px;\n background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius-md);\n padding: 9px 13px; color: var(--ink); font: 13px/1.4 var(--font-ui);\n /* A long unbroken token (a file path) wraps INSIDE the card; a newline in the\n message is a line break. */\n overflow-wrap: anywhere; white-space: pre-line;\n box-shadow: var(--shadow-2);\n animation: var(--anim-panel-in, none);\n}\n.shell-toast.ok { border-color: var(--ok); color: var(--ok); }\n.shell-toast.error { border-color: var(--danger); color: var(--danger); }\n/* The eased dismiss (closeWithExit), mirroring the enter. */\n.shell-toast.closing { animation: var(--anim-panel-out); }\n.shell-toast[hidden] { display: none; }\n\n/* Never a window-drag region, even over one (dialog.css says why). */\n.shell-toast, .shell-toast * { -webkit-app-region: no-drag; }\n"],"mappings":";AAKA,CAAC;AACC,YAAU;AAAO,SAAO;AAAM,UAAQ;AAAM,WAAS;AAAI,aAAW;AACpE,cAAY,IAAI;AAAY,UAAQ,IAAI,MAAM,IAAI;AAAS,iBAAe,IAAI;AAC9E,WAAS,IAAI;AAAM,SAAO,IAAI;AAAQ,QAAM,IAAI,CAAC,IAAI,IAAI;AAGzD,iBAAe;AAAU,eAAa;AACtC,cAAY,IAAI;AAChB,aAAW,IAAI,eAAe,EAAE;AAClC;AACA,CAVC,WAUW,CAAC;AAAQ,gBAAc,IAAI;AAAW,SAAO,IAAI;AAAO;AACpE,CAXC,WAWW,CAAC;AAAQ,gBAAc,IAAI;AAAW,SAAO,IAAI;AAAW;AAExE,CAbC,WAaW,CAAC;AAAU,aAAW,IAAI;AAAmB;AACzD,CAdC,WAcW,CAAC;AAAU,WAAS;AAAM;AAGtC,CAjBC;AAiBa,CAjBb,YAiB0B;AAAI,sBAAoB;AAAS;","names":[]}
|
package/dist/tokens.css
CHANGED
package/dist/tokens.css.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tokens.css"],"sourcesContent":["/* ============================================================================\n Design-token GRAMMAR - the app-agnostic layer (design-language.md section 4).\n\n The shared half of \"two palettes, one grammar\": the scales that are IDENTICAL\n across the editor family - elevation, motion, radius, scrollbar chrome. Each\n app supplies its own colour + font VALUES (its palette) over this grammar in\n its own theme.css. Do not add app-specific colours here.\n\n SCALE. Chrome is sized in px off a 14px base; the reading surface alone\n scales with the root (\"Chrome has its own scale\", design-language.md\n section 4, 2026-09-16). Every size in the shell's stylesheets (font-size,\n padding, gap, margin, width, height) is a pixel value: the navigator, the\n inspector, the bars, the dialogs and the controls in them read the same in\n every app of the family whatever the reading size is. `em` survives only\n where a rule deliberately scales with its own font; line-heights stay\n unitless. The values are what Storyletter rendered at 0.39.0 (rem x 16, to\n the nearest half pixel), so the conversion moved nothing there and brought\n Patterpad, whose chrome had grown 7 to 10 percent with its reading root,\n back to the same size. No `rem` in a chrome rule; the test suite greps.\n ============================================================================ */\n\n:root {\n color-scheme: light dark;\n\n /* The semantic set's third member, and the only COLOUR in this file.\n\n It is here because it kept not being anywhere. An app's palette owns\n --danger and --warn; neither owned \"all is well\", so a real need - the live\n debug link, an in-sync shard, a saved document - was met with a raw green\n literal that could not theme-shift (surface-audit-shell.md F2). A default\n in the grammar layer means the shell's own widgets may say it, and an app\n that has not thought about green yet still gets a green that works on both\n grounds. Every palette should override it in its theme.css, as it does the\n other two: this is a floor, not a decision. */\n --ok: light-dark(#3f7d49, #79bd80);\n\n /* The ink that sits ON the accent: a primary button's label, a selected\n row's text. Every palette overrides it (a light tan accent on a dark\n palette wants dark ink, not white); this floor only stops the shell's own\n primary buttons from falling back to a raw #fff literal, which is what\n they did until 0.39.0 and which read white-on-tan on Night and Slate. */\n --on-accent: light-dark(#ffffff, #1b1916);\n\n /* The chrome base. `.shell-dialog`, `.pane-shell` and `.swin-head` set it as\n their font-size, so unstyled text inside them lands at 14px regardless of\n the app's root; a rule that wants the base by name reads it here. Not a\n scale factor: the sizes below it are literal px, on purpose. */\n --chrome-size: 14px;\n\n /* The one scrim behind every modal in the family (confirm, settings,\n identity, about, notes). Four recipes had grown across five files. */\n --scrim: color-mix(in oklab, var(--ink) 28%, transparent);\n\n /* Elevation: three tiers by how far a surface floats (resting cards,\n floating menus/popovers, full overlays). Theme-agnostic black so it\n deepens naturally on the dark palettes. */\n --shadow-1: 0 1px 3px rgba(0, 0, 0, 0.05);\n --shadow-2: 0 6px 20px rgba(0, 0, 0, 0.14);\n --shadow-3: 0 16px 48px rgba(0, 0, 0, 0.28);\n\n /* Motion: one easing curve + a small duration scale, plus the shared\n panel/backdrop/screen enter-exit vocabulary. Nothing pops. */\n --ease-standard: cubic-bezier(0.2, 0, 0, 1);\n --dur-quick: 120ms;\n --dur-base: 150ms;\n --dur-settle: 200ms;\n --dur-slow: 1s;\n --dur-panel: 260ms;\n --dur-exit: 170ms;\n --anim-panel-in: panel-in var(--dur-panel) var(--ease-standard);\n --anim-backdrop-in: backdrop-in var(--dur-settle) var(--ease-standard);\n --anim-screen-in: screen-in var(--dur-panel) var(--ease-standard);\n --anim-panel-out: panel-out var(--dur-exit) var(--ease-standard) forwards;\n --anim-backdrop-out: backdrop-out var(--dur-exit) var(--ease-standard) forwards;\n /* The long-job strip's indeterminate band (job.css draws the keyframes). */\n --anim-job-band: job-band 1.4s var(--ease-standard) infinite;\n\n /* Corner radius: four steps cover every surface. */\n --radius-sm: 6px;\n --radius-md: 10px;\n --radius-lg: 14px;\n --radius-pill: 999px;\n\n /* Scrollbar thumb: a translucent ink, so it adapts per palette. */\n --scroll-thumb: color-mix(in oklab, var(--ink) 20%, transparent);\n --scroll-thumb-hover: color-mix(in oklab, var(--ink) 40%, transparent);\n}\n\n/* A drawn icon (`iconNode` / `iconHtml` in icons.ts stamp `data-icon`). A\n block, so it never sits on a text baseline with descender space under it;\n no flex shrink, so a crowded row squeezes the text and not the picture; and\n no pointer events, so a click lands on the button and never on the drawing\n (a handler comparing `event.target` to its button would otherwise miss).\n Colour is inherited: every drawing is currentColor. */\n[data-icon] { display: block; flex: none; pointer-events: none; }\n\n/* The family's keyboard focus ring: one rule, zero specificity (:where), so a\n component may draw its own and win without !important. Until 0.39.0\n Patterpad's main window had no ring at all and Storyletter carried five\n copies of this line. Mouse focus stays quiet; only :focus-visible draws. */\n:where(button, [role=\"button\"], input, select, textarea, summary, a[href], [tabindex]):focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n\n/* The tracked uppercase caption, kept for exactly two jobs: table column heads\n and screenplay character cues, where uppercase is the domain's own\n convention (design-language.md, \"Captions are words, not overlines\"). A\n section caption anywhere else is sentence-case text at label size, weight\n 600, in the UI face, which is what the shell's own captions now are. */\n.overline { font: 600 10.5px/1 var(--font-ui); text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }\n\n* { scrollbar-width: thin; scrollbar-color: var(--scroll-thumb) transparent; }\n::-webkit-scrollbar { width: 11px; height: 11px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-corner { background: transparent; }\n::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: var(--radius-sm); border: 3px solid transparent; background-clip: padding-box; }\n::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); background-clip: padding-box; }\n\n@keyframes panel-in { from { opacity: 0; transform: translateY(-9px) scale(0.965); } to { opacity: 1; transform: none; } }\n@keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }\n@keyframes screen-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }\n@keyframes panel-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(-6px) scale(0.97); } }\n@keyframes backdrop-out { from { opacity: 1; } to { opacity: 0; } }\n\n@media (prefers-reduced-motion: reduce) {\n :root {\n --anim-panel-in: none; --anim-backdrop-in: none; --anim-screen-in: none;\n --anim-panel-out: none; --anim-backdrop-out: none; --anim-job-band: none;\n }\n}\n"],"mappings":";AAqBA;AACE,gBAAc,MAAM;AAYpB,QAAM,WAAW,OAAO,EAAE;AAO1B,eAAa,WAAW,OAAO,EAAE;AAMjC,iBAAe;AAIf,WAAS,UAAU,GAAG,KAAK,EAAE,IAAI,OAAO,GAAG,EAAE;AAK7C,cAAY,EAAE,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACpC,cAAY,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACrC,cAAY,EAAE,KAAK,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAItC,mBAAiB,aAAa,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE;AACzC,eAAa;AACb,cAAY;AACZ,gBAAc;AACd,cAAY;AACZ,eAAa;AACb,cAAY;AACZ,mBAAiB,SAAS,IAAI,aAAa,IAAI;AAC/C,sBAAoB,YAAY,IAAI,cAAc,IAAI;AACtD,oBAAkB,UAAU,IAAI,aAAa,IAAI;AACjD,oBAAkB,UAAU,IAAI,YAAY,IAAI,iBAAiB;AACjE,uBAAqB,aAAa,IAAI,YAAY,IAAI,iBAAiB;AAEvE,mBAAiB,SAAS,KAAK,IAAI,iBAAiB;AAGpD,eAAa;AACb,eAAa;AACb,eAAa;AACb,iBAAe;AAGf,kBAAgB,UAAU,GAAG,KAAK,EAAE,IAAI,OAAO,GAAG,EAAE;AACpD,wBAAsB,UAAU,GAAG,KAAK,EAAE,IAAI,OAAO,GAAG,EAAE;AAC5D;AAQA,CAAC;AAAa,WAAS;AAAO,QAAM;AAAM,kBAAgB;AAAM;AAMhE,OAAO,QAAQ,CAAC,cAAgB,OAAO,QAAQ,UAAU,SAAS,CAAC,CAAC,OAAO,CAAC,UAAU;AACpF,WAAS,IAAI,MAAM,IAAI;AACvB,kBAAgB;AAClB;AAOA,CAAC;AAAW,QAAM,IAAI,MAAM,CAAC,EAAE,IAAI;AAAY,kBAAgB;AAAW,kBAAgB;AAAQ,SAAO,IAAI;AAAU;AAEvH;AAAI,mBAAiB;AAAM,mBAAiB,IAAI,gBAAgB;AAAa;AAC7E;AAAsB,SAAO;AAAM,UAAQ;AAAM;AACjD;AAA4B,cAAY;AAAa;AACrD;AAA6B,cAAY;AAAa;AACtD;AAA4B,cAAY,IAAI;AAAiB,iBAAe,IAAI;AAAc,UAAQ,IAAI,MAAM;AAAa,mBAAiB;AAAa;AAC3J,yBAAyB;AAAS,cAAY,IAAI;AAAuB,mBAAiB;AAAa;AAEvG,WAAW;AAAW;AAAO,aAAS;AAAG,eAAW,WAAW,MAAM,MAAM;AAAQ;AAAE;AAAK,aAAS;AAAG,eAAW;AAAM;AAAE;AACzH,WAAW;AAAc;AAAO,aAAS;AAAG;AAAE;AAAK,aAAS;AAAG;AAAE;AACjE,WAAW;AAAY;AAAO,aAAS;AAAG,eAAW,WAAW;AAAM;AAAE;AAAK,aAAS;AAAG,eAAW;AAAM;AAAE;AAC5G,WAAW;AAAY;AAAO,aAAS;AAAG,eAAW;AAAM;AAAE;AAAK,aAAS;AAAG,eAAW,WAAW,MAAM,MAAM;AAAO;AAAE;AACzH,WAAW;AAAe;AAAO,aAAS;AAAG;AAAE;AAAK,aAAS;AAAG;AAAE;AAElE,QAAO,wBAAyB;AAC9B;AACE,qBAAiB;AAAM,wBAAoB;AAAM,sBAAkB;AACnE,sBAAkB;AAAM,yBAAqB;AAAM,qBAAiB;AACtE;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/tokens.css"],"sourcesContent":["/* ============================================================================\n Design-token GRAMMAR - the app-agnostic layer (design-language.md section 4).\n\n The shared half of \"two palettes, one grammar\": the scales that are IDENTICAL\n across the editor family - elevation, motion, radius, scrollbar chrome. Each\n app supplies its own colour + font VALUES (its palette) over this grammar in\n its own theme.css. Do not add app-specific colours here.\n\n SCALE. Chrome is sized in px off a 14px base; the reading surface alone\n scales with the root (\"Chrome has its own scale\", design-language.md\n section 4, 2026-09-16). Every size in the shell's stylesheets (font-size,\n padding, gap, margin, width, height) is a pixel value: the navigator, the\n inspector, the bars, the dialogs and the controls in them read the same in\n every app of the family whatever the reading size is. `em` survives only\n where a rule deliberately scales with its own font; line-heights stay\n unitless. The values are what Storyletter rendered at 0.39.0 (rem x 16, to\n the nearest half pixel), so the conversion moved nothing there and brought\n Patterpad, whose chrome had grown 7 to 10 percent with its reading root,\n back to the same size. No `rem` in a chrome rule; the test suite greps.\n ============================================================================ */\n\n:root {\n color-scheme: light dark;\n\n /* The semantic set's third member, and the only COLOUR in this file.\n\n It is here because it kept not being anywhere. An app's palette owns\n --danger and --warn; neither owned \"all is well\", so a real need - the live\n debug link, an in-sync shard, a saved document - was met with a raw green\n literal that could not theme-shift (surface-audit-shell.md F2). A default\n in the grammar layer means the shell's own widgets may say it, and an app\n that has not thought about green yet still gets a green that works on both\n grounds. Every palette should override it in its theme.css, as it does the\n other two: this is a floor, not a decision. */\n --ok: light-dark(#3f7d49, #79bd80);\n\n /* The ink that sits ON the accent: a primary button's label, a selected\n row's text. Every palette overrides it (a light tan accent on a dark\n palette wants dark ink, not white); this floor only stops the shell's own\n primary buttons from falling back to a raw #fff literal, which is what\n they did until 0.39.0 and which read white-on-tan on Night and Slate. */\n --on-accent: light-dark(#ffffff, #1b1916);\n\n /* The chrome base. `.shell-dialog`, `.pane-shell` and `.swin-head` set it as\n their font-size, so unstyled text inside them lands at 14px regardless of\n the app's root; a rule that wants the base by name reads it here. Not a\n scale factor: the sizes below it are literal px, on purpose. */\n --chrome-size: 14px;\n\n /* The one scrim behind every modal in the family (confirm, settings,\n identity, about, notes). Four recipes had grown across five files. */\n --scrim: color-mix(in oklab, var(--ink) 28%, transparent);\n\n /* Elevation: three tiers by how far a surface floats (resting cards,\n floating menus/popovers, full overlays). Theme-agnostic black so it\n deepens naturally on the dark palettes. */\n --shadow-1: 0 1px 3px rgba(0, 0, 0, 0.05);\n --shadow-2: 0 6px 20px rgba(0, 0, 0, 0.14);\n --shadow-3: 0 16px 48px rgba(0, 0, 0, 0.28);\n\n /* Motion: one easing curve + a small duration scale, plus the shared\n panel/backdrop/screen enter-exit vocabulary. Nothing pops. */\n --ease-standard: cubic-bezier(0.2, 0, 0, 1);\n --dur-quick: 120ms;\n --dur-base: 150ms;\n --dur-settle: 200ms;\n --dur-slow: 1s;\n --dur-panel: 260ms;\n --dur-exit: 170ms;\n --anim-panel-in: panel-in var(--dur-panel) var(--ease-standard);\n --anim-backdrop-in: backdrop-in var(--dur-settle) var(--ease-standard);\n --anim-screen-in: screen-in var(--dur-panel) var(--ease-standard);\n --anim-panel-out: panel-out var(--dur-exit) var(--ease-standard) forwards;\n --anim-backdrop-out: backdrop-out var(--dur-exit) var(--ease-standard) forwards;\n /* The long-job strip's indeterminate band (job.css draws the keyframes). */\n --anim-job-band: job-band 1.4s var(--ease-standard) infinite;\n\n /* Corner radius: four steps cover every surface. */\n --radius-sm: 6px;\n --radius-md: 10px;\n --radius-lg: 14px;\n --radius-pill: 999px;\n\n /* Scrollbar thumb: a translucent ink, so it adapts per palette. */\n --scroll-thumb: color-mix(in oklab, var(--ink) 20%, transparent);\n --scroll-thumb-hover: color-mix(in oklab, var(--ink) 40%, transparent);\n}\n\n/* A drawn icon (`iconNode` / `iconHtml` in icons.ts stamp `data-icon`). A\n block, so it never sits on a text baseline with descender space under it;\n no flex shrink, so a crowded row squeezes the text and not the picture; and\n no pointer events, so a click lands on the button and never on the drawing\n (a handler comparing `event.target` to its button would otherwise miss).\n Colour is inherited: every drawing is currentColor. */\n[data-icon] { display: block; flex: none; pointer-events: none; }\n\n/* The family's keyboard focus ring: one rule, zero specificity (:where), so a\n component may draw its own and win without !important. Until 0.39.0\n Patterpad's main window had no ring at all and Storyletter carried five\n copies of this line. Mouse focus stays quiet; only :focus-visible draws. */\n:where(button, [role=\"button\"], input, select, textarea, summary, a[href], [tabindex]):focus-visible {\n outline: 2px solid var(--accent);\n outline-offset: 1px;\n}\n\n/* The tracked uppercase caption, kept for exactly two jobs: table column heads\n and screenplay character cues, where uppercase is the domain's own\n convention (design-language.md, \"Captions are words, not overlines\"). A\n section caption anywhere else is sentence-case text at label size, weight\n 600, in the UI face, which is what the shell's own captions now are. */\n.overline { font: 600 10.5px/1 var(--font-ui); text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }\n\n* { scrollbar-width: thin; scrollbar-color: var(--scroll-thumb) transparent; }\n::-webkit-scrollbar { width: 11px; height: 11px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-corner { background: transparent; }\n::-webkit-scrollbar-thumb { background: var(--scroll-thumb); border-radius: var(--radius-sm); border: 3px solid transparent; background-clip: padding-box; }\n::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); background-clip: padding-box; }\n\n@keyframes panel-in { from { opacity: 0; transform: translateY(-9px) scale(0.965); } to { opacity: 1; transform: none; } }\n@keyframes backdrop-in { from { opacity: 0; } to { opacity: 1; } }\n@keyframes screen-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }\n@keyframes panel-out { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(-6px) scale(0.97); } }\n@keyframes backdrop-out { from { opacity: 1; } to { opacity: 0; } }\n\n@media (prefers-reduced-motion: reduce) {\n :root {\n --anim-panel-in: none; --anim-backdrop-in: none; --anim-screen-in: none;\n --anim-panel-out: none; --anim-backdrop-out: none; --anim-job-band: none;\n }\n}\n\n/* A floating layer from createFloating (floating.ts) carries this attribute, because its class is\n the caller's. Never a window-drag region, even over one: Electron gives a click in a drag area to\n the OS before the page sees it (dialog.css has the whole story, 2026-09-28). */\n[data-shell-layer], [data-shell-layer] * { -webkit-app-region: no-drag; }\n"],"mappings":";AAqBA;AACE,gBAAc,MAAM;AAYpB,QAAM,WAAW,OAAO,EAAE;AAO1B,eAAa,WAAW,OAAO,EAAE;AAMjC,iBAAe;AAIf,WAAS,UAAU,GAAG,KAAK,EAAE,IAAI,OAAO,GAAG,EAAE;AAK7C,cAAY,EAAE,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACpC,cAAY,EAAE,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AACrC,cAAY,EAAE,KAAK,KAAK,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE;AAItC,mBAAiB,aAAa,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE;AACzC,eAAa;AACb,cAAY;AACZ,gBAAc;AACd,cAAY;AACZ,eAAa;AACb,cAAY;AACZ,mBAAiB,SAAS,IAAI,aAAa,IAAI;AAC/C,sBAAoB,YAAY,IAAI,cAAc,IAAI;AACtD,oBAAkB,UAAU,IAAI,aAAa,IAAI;AACjD,oBAAkB,UAAU,IAAI,YAAY,IAAI,iBAAiB;AACjE,uBAAqB,aAAa,IAAI,YAAY,IAAI,iBAAiB;AAEvE,mBAAiB,SAAS,KAAK,IAAI,iBAAiB;AAGpD,eAAa;AACb,eAAa;AACb,eAAa;AACb,iBAAe;AAGf,kBAAgB,UAAU,GAAG,KAAK,EAAE,IAAI,OAAO,GAAG,EAAE;AACpD,wBAAsB,UAAU,GAAG,KAAK,EAAE,IAAI,OAAO,GAAG,EAAE;AAC5D;AAQA,CAAC;AAAa,WAAS;AAAO,QAAM;AAAM,kBAAgB;AAAM;AAMhE,OAAO,QAAQ,CAAC,cAAgB,OAAO,QAAQ,UAAU,SAAS,CAAC,CAAC,OAAO,CAAC,UAAU;AACpF,WAAS,IAAI,MAAM,IAAI;AACvB,kBAAgB;AAClB;AAOA,CAAC;AAAW,QAAM,IAAI,MAAM,CAAC,EAAE,IAAI;AAAY,kBAAgB;AAAW,kBAAgB;AAAQ,SAAO,IAAI;AAAU;AAEvH;AAAI,mBAAiB;AAAM,mBAAiB,IAAI,gBAAgB;AAAa;AAC7E;AAAsB,SAAO;AAAM,UAAQ;AAAM;AACjD;AAA4B,cAAY;AAAa;AACrD;AAA6B,cAAY;AAAa;AACtD;AAA4B,cAAY,IAAI;AAAiB,iBAAe,IAAI;AAAc,UAAQ,IAAI,MAAM;AAAa,mBAAiB;AAAa;AAC3J,yBAAyB;AAAS,cAAY,IAAI;AAAuB,mBAAiB;AAAa;AAEvG,WAAW;AAAW;AAAO,aAAS;AAAG,eAAW,WAAW,MAAM,MAAM;AAAQ;AAAE;AAAK,aAAS;AAAG,eAAW;AAAM;AAAE;AACzH,WAAW;AAAc;AAAO,aAAS;AAAG;AAAE;AAAK,aAAS;AAAG;AAAE;AACjE,WAAW;AAAY;AAAO,aAAS;AAAG,eAAW,WAAW;AAAM;AAAE;AAAK,aAAS;AAAG,eAAW;AAAM;AAAE;AAC5G,WAAW;AAAY;AAAO,aAAS;AAAG,eAAW;AAAM;AAAE;AAAK,aAAS;AAAG,eAAW,WAAW,MAAM,MAAM;AAAO;AAAE;AACzH,WAAW;AAAe;AAAO,aAAS;AAAG;AAAE;AAAK,aAAS;AAAG;AAAE;AAElE,QAAO,wBAAyB;AAC9B;AACE,qBAAiB;AAAM,wBAAoB;AAAM,sBAAkB;AACnE,sBAAkB;AAAM,yBAAqB;AAAM,qBAAiB;AACtE;AACF;AAKA,CAAC;AAAmB,CAAC,kBAAkB;AAAI,sBAAoB;AAAS;","names":[]}
|
package/dist/tool-window.cjs
CHANGED
|
@@ -49,6 +49,7 @@ function wireMacActivation() {
|
|
|
49
49
|
if (macActivationWired) return;
|
|
50
50
|
macActivationWired = true;
|
|
51
51
|
electron.app.on("did-become-active", () => {
|
|
52
|
+
if (pinsHeld > 0) return;
|
|
52
53
|
for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(true, "floating");
|
|
53
54
|
});
|
|
54
55
|
electron.app.on("did-resign-active", () => {
|
|
@@ -65,7 +66,7 @@ function pinToolWindow(w, parent, on, platform = process.platform) {
|
|
|
65
66
|
w.once("closed", () => macPinned.delete(w));
|
|
66
67
|
}
|
|
67
68
|
wireMacActivation();
|
|
68
|
-
w.setAlwaysOnTop(electron.BrowserWindow.getFocusedWindow() !== null, "floating");
|
|
69
|
+
w.setAlwaysOnTop(pinsHeld === 0 && electron.BrowserWindow.getFocusedWindow() !== null, "floating");
|
|
69
70
|
} else {
|
|
70
71
|
macPinned.delete(w);
|
|
71
72
|
w.setAlwaysOnTop(false);
|
|
@@ -73,7 +74,52 @@ function pinToolWindow(w, parent, on, platform = process.platform) {
|
|
|
73
74
|
return;
|
|
74
75
|
}
|
|
75
76
|
w.setAlwaysOnTop(false);
|
|
76
|
-
|
|
77
|
+
const owner = on && parent && !parent.isDestroyed() ? parent : null;
|
|
78
|
+
if (pinsHeld > 0) {
|
|
79
|
+
if (owner) heldChildren.set(w, owner);
|
|
80
|
+
else heldChildren.delete(w);
|
|
81
|
+
w.setParentWindow(null);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
w.setParentWindow(owner);
|
|
85
|
+
}
|
|
86
|
+
var pinsHeld = 0;
|
|
87
|
+
var heldChildren = /* @__PURE__ */ new Map();
|
|
88
|
+
function frontForModal(main, platform = process.platform) {
|
|
89
|
+
if (main.isDestroyed()) return () => {
|
|
90
|
+
};
|
|
91
|
+
pinsHeld++;
|
|
92
|
+
if (platform === "darwin") {
|
|
93
|
+
for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(false);
|
|
94
|
+
} else {
|
|
95
|
+
for (const w of electron.BrowserWindow.getAllWindows()) {
|
|
96
|
+
if (w === main || w.isDestroyed()) continue;
|
|
97
|
+
const owner = w.getParentWindow();
|
|
98
|
+
if (owner !== main) continue;
|
|
99
|
+
heldChildren.set(w, main);
|
|
100
|
+
w.setParentWindow(null);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (main.isMinimized()) main.restore();
|
|
104
|
+
main.show();
|
|
105
|
+
main.focus();
|
|
106
|
+
let released = false;
|
|
107
|
+
return () => {
|
|
108
|
+
if (released) return;
|
|
109
|
+
released = true;
|
|
110
|
+
if (--pinsHeld > 0) return;
|
|
111
|
+
if (platform === "darwin") {
|
|
112
|
+
const active = electron.BrowserWindow.getFocusedWindow() !== null;
|
|
113
|
+
if (active) {
|
|
114
|
+
for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(true, "floating");
|
|
115
|
+
}
|
|
116
|
+
} else {
|
|
117
|
+
for (const [w, owner] of heldChildren) {
|
|
118
|
+
if (!w.isDestroyed() && !owner.isDestroyed()) w.setParentWindow(owner);
|
|
119
|
+
}
|
|
120
|
+
heldChildren.clear();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
77
123
|
}
|
|
78
124
|
function openToolWindow(existing, opts) {
|
|
79
125
|
if (existing && !existing.isDestroyed()) {
|
|
@@ -166,6 +212,7 @@ function defineToolWindows(specs, env) {
|
|
|
166
212
|
|
|
167
213
|
exports.centeredOnPrimary = centeredOnPrimary;
|
|
168
214
|
exports.defineToolWindows = defineToolWindows;
|
|
215
|
+
exports.frontForModal = frontForModal;
|
|
169
216
|
exports.openToolWindow = openToolWindow;
|
|
170
217
|
exports.pinToolWindow = pinToolWindow;
|
|
171
218
|
exports.rememberBounds = rememberBounds;
|
package/dist/tool-window.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tool-window.ts"],"names":["screen","app","BrowserWindow","join"],"mappings":"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","file":"tool-window.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// The tool-window kit, MAIN-PROCESS half: the detached helper-window\n// machinery both apps had grown four copies of (Patterpad's Play + Search +\n// Coverage, Storyletter's Board + Find). Remembered bounds with a\n// disconnected-monitor guard, debounced persistence, the Reset View rescue,\n// and the pinnable window factory they compose into.\n//\n// This module imports electron, so it ships as its own subpath\n// (@wildwinter/app-shell/tool-window) - never import it from renderer code.\n// The renderer half (pin button, .swin drag-bar grammar) lives in the root\n// export + tool-window.css.\n//\n// Two deliberate corrections over the Patterpad originals, for when it\n// migrates here: rescueToolWindow re-pins EVERY window it rescues (Patterpad\n// re-pinned only Play, so its store lied about Search + Coverage), and the\n// factory always wires closed-cleanup through an identity guard.\n// ---------------------------------------------------------------------------\n\nimport { BrowserWindow, app, screen } from \"electron\";\nimport { join } from \"node:path\";\nimport type { Satellite } from \"./session.js\";\n\nexport interface ToolWindowSize {\n width: number;\n height: number;\n}\n\nexport interface ToolWindowBounds {\n x?: number;\n y?: number;\n width: number;\n height: number;\n}\n\n/** The per-window store slice every app remembers: where it was, whether it\n * floats. (Patterpad's PlayWindowState, generalised.) */\nexport interface ToolWindowState {\n bounds?: ToolWindowBounds;\n pinned: boolean;\n}\n\n/** Use a remembered rect only if it still intersects a live display (a window\n * saved on a now-disconnected monitor must not open offscreen). Falls back\n * to the default size; a remembered size is kept even when the position is\n * dropped (Electron then centres it). */\nexport function savedWindowRect(\n saved: ToolWindowBounds | undefined,\n def: ToolWindowSize,\n min: ToolWindowSize,\n): ToolWindowBounds {\n if (!saved) return { ...def };\n const w = Math.max(min.width, saved.width), h = Math.max(min.height, saved.height);\n if (saved.x != null && saved.y != null) {\n const onScreen = screen.getAllDisplays().some((d) => {\n const a = d.workArea;\n return saved.x! + w > a.x + 40 && saved.x! < a.x + a.width - 40 && saved.y! + h > a.y + 20 && saved.y! < a.y + a.height - 20;\n });\n if (onScreen) return { x: saved.x, y: saved.y, width: w, height: h };\n }\n return { width: w, height: h };\n}\n\n/** Persist a helper window's bounds as the user moves / resizes / closes it\n * (debounced 400ms; close flushes synchronously). */\nexport function rememberBounds(w: BrowserWindow, write: (bounds: ToolWindowBounds) => void): void {\n const saveBounds = (): void => { if (!w.isDestroyed()) write(w.getBounds()); };\n let boundsTimer: ReturnType<typeof setTimeout> | undefined;\n const queueSave = (): void => { clearTimeout(boundsTimer); boundsTimer = setTimeout(saveBounds, 400); };\n w.on(\"resize\", queueSave);\n w.on(\"move\", queueSave);\n w.on(\"close\", () => { clearTimeout(boundsTimer); saveBounds(); });\n}\n\n/** Centre a rect on the primary display's work area. */\nexport function centeredOnPrimary(size: ToolWindowSize): { x: number; y: number } {\n const a = screen.getPrimaryDisplay().workArea;\n return { x: Math.round(a.x + (a.width - size.width) / 2), y: Math.round(a.y + (a.height - size.height) / 2) };\n}\n\n/** Reset View's per-window half: un-minimise, re-pin (always - the store\n * says pinned:true after a rescue, so the window must agree), default size,\n * centred on the primary display, shown. The host clears its remembered\n * bounds itself (that's a store concern). No focus steal: helpers show\n * behind the editor. */\nexport function rescueToolWindow(w: BrowserWindow | undefined | null, def: ToolWindowSize): void {\n if (!w || w.isDestroyed()) return;\n if (w.isMinimized()) w.restore();\n w.setBounds({ ...def, ...centeredOnPrimary(def) });\n w.show();\n // Raise within OUR stack. This used to setAlwaysOnTop(true), which on macOS\n // and Windows floats the window above every other APPLICATION - and stuck\n // that way, since nothing ever unset it.\n w.moveTop();\n}\n\n/** macOS pinned windows, for the activation toggle below. */\nconst macPinned = new Set<BrowserWindow>();\nlet macActivationWired = false;\nfunction wireMacActivation(): void {\n if (macActivationWired) return;\n macActivationWired = true;\n // Floating is a GLOBAL window level: left on while another app is front,\n // the pinned window would sit over that app too - the exact 2026-08-25\n // complaint. Scoping it to our app's active state keeps the pin meaning\n // \"above my editor\" and nothing more.\n app.on(\"did-become-active\", () => { for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(true, \"floating\"); });\n app.on(\"did-resign-active\", () => { for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(false); });\n}\n\n/**\n * The pin: keep a tool window above THIS APP'S MAIN WINDOW, not above the\n * world. Two findings shaped the mechanism, one per platform family:\n *\n * - Bare `alwaysOnTop` (the first implementation) floats the window over\n * every other application on macOS and Windows, so a pinned Board meant\n * nothing else on the machine could come to the front (a Storyletter user\n * report, 2026-08-25).\n * - A CHILD window (the second implementation) is position-coupled to its\n * parent on macOS: dragging the editor dragged the Board with it (a\n * Storyletter user report, 2026-08-28). Windows and Linux owned windows do\n * not move with their owner, so the child mechanism stays right for them.\n *\n * So: on macOS the pin is ACTIVATION-SCOPED alwaysOnTop - floating while\n * this app is active, dropped the moment it resigns - which is above the\n * editor without following its drags and without covering anyone else. On\n * Windows and Linux the pin is a child window, which stays over its parent\n * and stacks normally against everything else. Each branch clears the other\n * mechanism first, which also heals a window an older build pinned the old\n * way. `platform` is a seam for the tests; leave it defaulted.\n */\nexport function pinToolWindow(\n w: BrowserWindow | undefined | null, parent: BrowserWindow | undefined | null, on: boolean,\n platform: NodeJS.Platform = process.platform,\n): void {\n if (!w || w.isDestroyed()) return;\n if (platform === \"darwin\") {\n w.setParentWindow(null);\n if (on) {\n if (!macPinned.has(w)) {\n macPinned.add(w);\n w.once(\"closed\", () => macPinned.delete(w));\n }\n wireMacActivation();\n // Raise only when the app is active now; otherwise the next\n // did-become-active raises it.\n w.setAlwaysOnTop(BrowserWindow.getFocusedWindow() !== null, \"floating\");\n } else {\n macPinned.delete(w);\n w.setAlwaysOnTop(false);\n }\n return;\n }\n w.setAlwaysOnTop(false);\n w.setParentWindow(on && parent && !parent.isDestroyed() ? parent : null);\n}\n\nexport interface ToolWindowOptions {\n title: string;\n /** Renderer entry, relative to rendererDir (e.g. \"table.html\", \"search/index.html\"). */\n page: string;\n /** Absolute path of the built renderer dir (join(import.meta.dirname, \"../renderer\")). */\n rendererDir: string;\n /** Absolute path of the preload bridge. */\n preload: string;\n rect: ToolWindowBounds;\n min?: ToolWindowSize;\n /** false = frameless: the renderer draws its own slim drag bar (.swin-head). */\n frame?: boolean;\n /** The window to PIN ABOVE: the app's main window, resolved at open time (a\n * getter, because the main window can be recreated). With `pinned` true\n * the new tool window rides above it - on macOS via activation-scoped\n * alwaysOnTop (a child window there follows the editor's drags), on\n * Windows and Linux as a child window (bare alwaysOnTop there floats over\n * the whole machine). See pinToolWindow for both findings. */\n pinTo?: () => BrowserWindow | undefined;\n /** Start pinned (the host remembers); flip later with `pinToolWindow`. */\n pinned?: boolean;\n /** Wire debounced bounds persistence into the host's store slice. */\n remember?: (bounds: ToolWindowBounds) => void;\n /** Fired when THIS window closes (identity-guarded; a stale close never\n * clears a newer window). Null the host's handle here. */\n onClosed?: () => void;\n}\n\n/** The pinnable detached-window factory (hardened webPreferences, one\n * preload, electron-vite's dev-URL/file split). Focuses and returns the\n * existing window when there is one; otherwise creates, wires, and loads. */\nexport function openToolWindow(existing: BrowserWindow | undefined | null, opts: ToolWindowOptions): BrowserWindow {\n if (existing && !existing.isDestroyed()) { existing.focus(); return existing; }\n const w = new BrowserWindow({\n ...opts.rect,\n ...(opts.min !== undefined ? { minWidth: opts.min.width, minHeight: opts.min.height } : {}),\n show: false,\n title: opts.title,\n ...(opts.frame !== undefined ? { frame: opts.frame } : {}),\n webPreferences: {\n contextIsolation: true, nodeIntegration: false, sandbox: true,\n preload: opts.preload,\n },\n });\n if (opts.pinTo !== undefined) pinToolWindow(w, opts.pinTo(), opts.pinned ?? false);\n if (opts.remember) rememberBounds(w, opts.remember);\n w.once(\"ready-to-show\", () => w.show());\n if (opts.onClosed) w.on(\"closed\", opts.onClosed);\n if (process.env[\"ELECTRON_RENDERER_URL\"]) void w.loadURL(`${process.env[\"ELECTRON_RENDERER_URL\"]}/${opts.page}`);\n else void w.loadFile(join(opts.rendererDir, opts.page));\n return w;\n}\n\n// ---------------------------------------------------------------------------\n// The tool windows AS DATA: one row per window, and everything that walks the\n// windows walks the rows. Lifted from Storyletter's TOOL_WINDOWS table +\n// openToolWindowFor + rescueWindows (studio/src/main/index.ts), where it had\n// replaced four near-identical openX() functions whose differences were all\n// mistakes (only one forwarded its console; Reset View rescued two of four).\n// Patterpad's three hand copies (createPlayWindow / createSearchWindow /\n// createCoverageWindow and a four-branch rescueWindows) become three rows.\n//\n// Two things the table makes structurally impossible: forgetting to register\n// a window as a satellite for the project-changed nudge (every row is\n// registered when a session is given), and Reset View rescuing a subset (it\n// walks the rows).\n// ---------------------------------------------------------------------------\n\n\nexport interface ToolWindowSpec<N extends string = string> {\n /** The window's key: the store slice it remembers under, the dev-console\n * prefix, and what `open(name)` takes. */\n name: N;\n title: string;\n /** Renderer entry, relative to the env's rendererDir. */\n page: string;\n def: ToolWindowSize;\n min: ToolWindowSize;\n /** The remembered rect, read at open time (a store read-through). */\n bounds: () => ToolWindowBounds | undefined;\n /** Persist a moved / resized rect (debounced by the factory). */\n remember: (bounds: ToolWindowBounds) => void;\n /** Whether it starts pinned above the editor (both apps default true). */\n pinned: () => boolean;\n /** Frameless (the family's shape: the renderer draws `toolWindowHead`).\n * Default false = frameless; pass `frame: true` for an OS title bar. */\n frame?: boolean;\n /** The project-changed channel for THIS window when it differs from the\n * env's (Storyletter's Links window listens on \"links:focus\"). */\n channel?: string;\n /** Drop what main caches for the outgoing project on this window's behalf\n * (a coverage report, a search anchor). Runs whether the window is open\n * or not: see session.ts. */\n clear?: () => void;\n}\n\nexport interface ToolWindowEnv {\n /** Absolute path of the built renderer dir. */\n rendererDir: string;\n /** Absolute path of the preload bridge. */\n preload: string;\n /** The window to pin above: the app's main window, resolved at open time. */\n pinTo: () => BrowserWindow | undefined;\n /** Register every row as a satellite of the project session, so a new\n * project cannot open underneath a tool window that still shows the old\n * one. `channel` is the nudge each window listens on (a row may override). */\n session?: { addSatellite: (satellite: Satellite) => () => void; channel: string };\n /** Reset View's store half, run first: forget every remembered rect and\n * set pinned true (`resetWindows(store)` from app-store, or the app's own). */\n resetStore?: () => void;\n /** The channel a rescued window's renderer hears its re-pin on, so its pin\n * button agrees with the store (default \"state:pinned\"). */\n pinChannel?: string;\n /** Runs once per window created: forward its console in dev, say. */\n onOpened?: (w: BrowserWindow, name: string) => void;\n}\n\nexport interface ToolWindows<N extends string> {\n /** Open (or focus) one window by name. */\n open(name: N): BrowserWindow;\n /** The live window, if open. */\n get(name: N): BrowserWindow | undefined;\n /** Reset View's window half, over the table: store reset, then every open\n * window restored, default size, centred, re-pinned, and told so. */\n rescue(): void;\n /** Every window that is open right now. */\n all(): BrowserWindow[];\n readonly names: readonly N[];\n}\n\n/** Build the table. The windows are owned here: a host reads them through\n * `get(name)` rather than holding module variables of its own. */\nexport function defineToolWindows<N extends string>(specs: ToolWindowSpec<N>[], env: ToolWindowEnv): ToolWindows<N> {\n const live = new Map<N, BrowserWindow>();\n const byName = (name: N): ToolWindowSpec<N> => {\n const found = specs.find((s) => s.name === name);\n if (!found) throw new Error(`no tool window \"${name}\"`);\n return found;\n };\n const get = (name: N): BrowserWindow | undefined => {\n const w = live.get(name);\n return w && !w.isDestroyed() ? w : undefined;\n };\n if (env.session) {\n for (const spec of specs) {\n env.session.addSatellite({\n window: () => get(spec.name),\n channel: spec.channel ?? env.session.channel,\n ...(spec.clear ? { clear: spec.clear } : {}),\n });\n }\n }\n const open = (name: N): BrowserWindow => {\n const spec = byName(name);\n const existing = get(name);\n const opened = openToolWindow(existing, {\n title: spec.title, page: spec.page, frame: spec.frame ?? false,\n rendererDir: env.rendererDir, preload: env.preload,\n rect: savedWindowRect(spec.bounds(), spec.def, spec.min),\n min: spec.min,\n pinTo: env.pinTo,\n pinned: spec.pinned(),\n remember: spec.remember,\n });\n if (opened !== existing) {\n live.set(name, opened);\n // Identity-guarded: a stale close never clears a newer window.\n opened.on(\"closed\", () => { if (live.get(name) === opened) live.delete(name); });\n env.onOpened?.(opened, name);\n }\n return opened;\n };\n const rescue = (): void => {\n env.resetStore?.();\n for (const spec of specs) rescueToolWindow(get(spec.name), spec.def);\n // ACTUALLY pin them, then tell them. `rescueToolWindow` restores, resizes,\n // centres and raises but never pins; the store now says pinned, so the\n // window and its button have to agree before the claim is made.\n for (const spec of specs) {\n const w = get(spec.name);\n if (!w) continue;\n pinToolWindow(w, env.pinTo(), true);\n w.webContents.send(env.pinChannel ?? \"state:pinned\", true);\n }\n };\n return {\n open, get, rescue,\n all: () => specs.map((s) => get(s.name)).filter((w): w is BrowserWindow => w !== undefined),\n names: specs.map((s) => s.name),\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/tool-window.ts"],"names":["screen","app","BrowserWindow","join"],"mappings":"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","file":"tool-window.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// The tool-window kit, MAIN-PROCESS half: the detached helper-window\n// machinery both apps had grown four copies of (Patterpad's Play + Search +\n// Coverage, Storyletter's Board + Find). Remembered bounds with a\n// disconnected-monitor guard, debounced persistence, the Reset View rescue,\n// and the pinnable window factory they compose into.\n//\n// This module imports electron, so it ships as its own subpath\n// (@wildwinter/app-shell/tool-window) - never import it from renderer code.\n// The renderer half (pin button, .swin drag-bar grammar) lives in the root\n// export + tool-window.css.\n//\n// Two deliberate corrections over the Patterpad originals, for when it\n// migrates here: rescueToolWindow re-pins EVERY window it rescues (Patterpad\n// re-pinned only Play, so its store lied about Search + Coverage), and the\n// factory always wires closed-cleanup through an identity guard.\n// ---------------------------------------------------------------------------\n\nimport { BrowserWindow, app, screen } from \"electron\";\nimport { join } from \"node:path\";\nimport type { Satellite } from \"./session.js\";\n\nexport interface ToolWindowSize {\n width: number;\n height: number;\n}\n\nexport interface ToolWindowBounds {\n x?: number;\n y?: number;\n width: number;\n height: number;\n}\n\n/** The per-window store slice every app remembers: where it was, whether it\n * floats. (Patterpad's PlayWindowState, generalised.) */\nexport interface ToolWindowState {\n bounds?: ToolWindowBounds;\n pinned: boolean;\n}\n\n/** Use a remembered rect only if it still intersects a live display (a window\n * saved on a now-disconnected monitor must not open offscreen). Falls back\n * to the default size; a remembered size is kept even when the position is\n * dropped (Electron then centres it). */\nexport function savedWindowRect(\n saved: ToolWindowBounds | undefined,\n def: ToolWindowSize,\n min: ToolWindowSize,\n): ToolWindowBounds {\n if (!saved) return { ...def };\n const w = Math.max(min.width, saved.width), h = Math.max(min.height, saved.height);\n if (saved.x != null && saved.y != null) {\n const onScreen = screen.getAllDisplays().some((d) => {\n const a = d.workArea;\n return saved.x! + w > a.x + 40 && saved.x! < a.x + a.width - 40 && saved.y! + h > a.y + 20 && saved.y! < a.y + a.height - 20;\n });\n if (onScreen) return { x: saved.x, y: saved.y, width: w, height: h };\n }\n return { width: w, height: h };\n}\n\n/** Persist a helper window's bounds as the user moves / resizes / closes it\n * (debounced 400ms; close flushes synchronously). */\nexport function rememberBounds(w: BrowserWindow, write: (bounds: ToolWindowBounds) => void): void {\n const saveBounds = (): void => { if (!w.isDestroyed()) write(w.getBounds()); };\n let boundsTimer: ReturnType<typeof setTimeout> | undefined;\n const queueSave = (): void => { clearTimeout(boundsTimer); boundsTimer = setTimeout(saveBounds, 400); };\n w.on(\"resize\", queueSave);\n w.on(\"move\", queueSave);\n w.on(\"close\", () => { clearTimeout(boundsTimer); saveBounds(); });\n}\n\n/** Centre a rect on the primary display's work area. */\nexport function centeredOnPrimary(size: ToolWindowSize): { x: number; y: number } {\n const a = screen.getPrimaryDisplay().workArea;\n return { x: Math.round(a.x + (a.width - size.width) / 2), y: Math.round(a.y + (a.height - size.height) / 2) };\n}\n\n/** Reset View's per-window half: un-minimise, re-pin (always - the store\n * says pinned:true after a rescue, so the window must agree), default size,\n * centred on the primary display, shown. The host clears its remembered\n * bounds itself (that's a store concern). No focus steal: helpers show\n * behind the editor. */\nexport function rescueToolWindow(w: BrowserWindow | undefined | null, def: ToolWindowSize): void {\n if (!w || w.isDestroyed()) return;\n if (w.isMinimized()) w.restore();\n w.setBounds({ ...def, ...centeredOnPrimary(def) });\n w.show();\n // Raise within OUR stack. This used to setAlwaysOnTop(true), which on macOS\n // and Windows floats the window above every other APPLICATION - and stuck\n // that way, since nothing ever unset it.\n w.moveTop();\n}\n\n/** macOS pinned windows, for the activation toggle below. */\nconst macPinned = new Set<BrowserWindow>();\nlet macActivationWired = false;\nfunction wireMacActivation(): void {\n if (macActivationWired) return;\n macActivationWired = true;\n // Floating is a GLOBAL window level: left on while another app is front,\n // the pinned window would sit over that app too - the exact 2026-08-25\n // complaint. Scoping it to our app's active state keeps the pin meaning\n // \"above my editor\" and nothing more.\n // Not while a modal has the pins held (frontForModal): the editor is in\n // front for it, and coming back to the app must not cover it again.\n app.on(\"did-become-active\", () => { if (pinsHeld > 0) return; for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(true, \"floating\"); });\n app.on(\"did-resign-active\", () => { for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(false); });\n}\n\n/**\n * The pin: keep a tool window above THIS APP'S MAIN WINDOW, not above the\n * world. Two findings shaped the mechanism, one per platform family:\n *\n * - Bare `alwaysOnTop` (the first implementation) floats the window over\n * every other application on macOS and Windows, so a pinned Board meant\n * nothing else on the machine could come to the front (a Storyletter user\n * report, 2026-08-25).\n * - A CHILD window (the second implementation) is position-coupled to its\n * parent on macOS: dragging the editor dragged the Board with it (a\n * Storyletter user report, 2026-08-28). Windows and Linux owned windows do\n * not move with their owner, so the child mechanism stays right for them.\n *\n * So: on macOS the pin is ACTIVATION-SCOPED alwaysOnTop - floating while\n * this app is active, dropped the moment it resigns - which is above the\n * editor without following its drags and without covering anyone else. On\n * Windows and Linux the pin is a child window, which stays over its parent\n * and stacks normally against everything else. Each branch clears the other\n * mechanism first, which also heals a window an older build pinned the old\n * way. `platform` is a seam for the tests; leave it defaulted.\n */\nexport function pinToolWindow(\n w: BrowserWindow | undefined | null, parent: BrowserWindow | undefined | null, on: boolean,\n platform: NodeJS.Platform = process.platform,\n): void {\n if (!w || w.isDestroyed()) return;\n if (platform === \"darwin\") {\n w.setParentWindow(null);\n if (on) {\n if (!macPinned.has(w)) {\n macPinned.add(w);\n w.once(\"closed\", () => macPinned.delete(w));\n }\n wireMacActivation();\n // Raise only when the app is active now and no modal has the pins\n // held; otherwise the next did-become-active, or the release, raises it.\n w.setAlwaysOnTop(pinsHeld === 0 && BrowserWindow.getFocusedWindow() !== null, \"floating\");\n } else {\n macPinned.delete(w);\n w.setAlwaysOnTop(false);\n }\n return;\n }\n w.setAlwaysOnTop(false);\n const owner = on && parent && !parent.isDestroyed() ? parent : null;\n // While a modal has the pins held, remember the choice and apply it at the\n // release, so the editor stays in front until the modal closes.\n if (pinsHeld > 0) {\n if (owner) heldChildren.set(w, owner); else heldChildren.delete(w);\n w.setParentWindow(null);\n return;\n }\n w.setParentWindow(owner);\n}\n\n// ---------------------------------------------------------------------------\n// Fronting the main window for a modal. A pinned tool window rides above the\n// editor by design, so a modal the EDITOR shows (the updater's prompts) opened\n// underneath one: the editor blurred behind its scrim and the prompt was\n// hidden, with nothing on screen to say why (both apps, 2026-10-02, the Play\n// window over the editor). While such a modal is up the pins step aside and\n// the editor comes forward; when it closes, every pin goes back as it was.\n// ---------------------------------------------------------------------------\n\n/** How many modals are holding the pins down. The pins return at zero. */\nlet pinsHeld = 0;\n/** Windows and Linux: the tool windows unparented by the hold, and the window\n * each goes back to. */\nconst heldChildren = new Map<BrowserWindow, BrowserWindow>();\n\n/**\n * Bring `main` in front of every tool window, pinned ones included, for as\n * long as a modal it shows is open: un-minimise, show, focus, with the pins\n * set aside. Returns the release, which puts the pins back (idempotent; holds\n * nest, and the pins return when the last is released). Call it only while\n * the app is active: focusing a window of an inactive app is the focus steal\n * the platforms forbid. `platform` is a seam for the tests.\n */\nexport function frontForModal(main: BrowserWindow, platform: NodeJS.Platform = process.platform): () => void {\n if (main.isDestroyed()) return () => {};\n pinsHeld++;\n if (platform === \"darwin\") {\n for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(false);\n } else {\n for (const w of BrowserWindow.getAllWindows()) {\n if (w === main || w.isDestroyed()) continue;\n const owner = w.getParentWindow();\n if (owner !== main) continue;\n heldChildren.set(w, main);\n w.setParentWindow(null);\n }\n }\n if (main.isMinimized()) main.restore();\n main.show();\n main.focus();\n\n let released = false;\n return () => {\n if (released) return;\n released = true;\n if (--pinsHeld > 0) return;\n if (platform === \"darwin\") {\n // Only float again if the app is active; otherwise did-become-active does.\n const active = BrowserWindow.getFocusedWindow() !== null;\n if (active) for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(true, \"floating\");\n } else {\n for (const [w, owner] of heldChildren) {\n if (!w.isDestroyed() && !owner.isDestroyed()) w.setParentWindow(owner);\n }\n heldChildren.clear();\n }\n };\n}\n\nexport interface ToolWindowOptions {\n title: string;\n /** Renderer entry, relative to rendererDir (e.g. \"table.html\", \"search/index.html\"). */\n page: string;\n /** Absolute path of the built renderer dir (join(import.meta.dirname, \"../renderer\")). */\n rendererDir: string;\n /** Absolute path of the preload bridge. */\n preload: string;\n rect: ToolWindowBounds;\n min?: ToolWindowSize;\n /** false = frameless: the renderer draws its own slim drag bar (.swin-head). */\n frame?: boolean;\n /** The window to PIN ABOVE: the app's main window, resolved at open time (a\n * getter, because the main window can be recreated). With `pinned` true\n * the new tool window rides above it - on macOS via activation-scoped\n * alwaysOnTop (a child window there follows the editor's drags), on\n * Windows and Linux as a child window (bare alwaysOnTop there floats over\n * the whole machine). See pinToolWindow for both findings. */\n pinTo?: () => BrowserWindow | undefined;\n /** Start pinned (the host remembers); flip later with `pinToolWindow`. */\n pinned?: boolean;\n /** Wire debounced bounds persistence into the host's store slice. */\n remember?: (bounds: ToolWindowBounds) => void;\n /** Fired when THIS window closes (identity-guarded; a stale close never\n * clears a newer window). Null the host's handle here. */\n onClosed?: () => void;\n}\n\n/** The pinnable detached-window factory (hardened webPreferences, one\n * preload, electron-vite's dev-URL/file split). Focuses and returns the\n * existing window when there is one; otherwise creates, wires, and loads. */\nexport function openToolWindow(existing: BrowserWindow | undefined | null, opts: ToolWindowOptions): BrowserWindow {\n if (existing && !existing.isDestroyed()) { existing.focus(); return existing; }\n const w = new BrowserWindow({\n ...opts.rect,\n ...(opts.min !== undefined ? { minWidth: opts.min.width, minHeight: opts.min.height } : {}),\n show: false,\n title: opts.title,\n ...(opts.frame !== undefined ? { frame: opts.frame } : {}),\n webPreferences: {\n contextIsolation: true, nodeIntegration: false, sandbox: true,\n preload: opts.preload,\n },\n });\n if (opts.pinTo !== undefined) pinToolWindow(w, opts.pinTo(), opts.pinned ?? false);\n if (opts.remember) rememberBounds(w, opts.remember);\n w.once(\"ready-to-show\", () => w.show());\n if (opts.onClosed) w.on(\"closed\", opts.onClosed);\n if (process.env[\"ELECTRON_RENDERER_URL\"]) void w.loadURL(`${process.env[\"ELECTRON_RENDERER_URL\"]}/${opts.page}`);\n else void w.loadFile(join(opts.rendererDir, opts.page));\n return w;\n}\n\n// ---------------------------------------------------------------------------\n// The tool windows AS DATA: one row per window, and everything that walks the\n// windows walks the rows. Lifted from Storyletter's TOOL_WINDOWS table +\n// openToolWindowFor + rescueWindows (studio/src/main/index.ts), where it had\n// replaced four near-identical openX() functions whose differences were all\n// mistakes (only one forwarded its console; Reset View rescued two of four).\n// Patterpad's three hand copies (createPlayWindow / createSearchWindow /\n// createCoverageWindow and a four-branch rescueWindows) become three rows.\n//\n// Two things the table makes structurally impossible: forgetting to register\n// a window as a satellite for the project-changed nudge (every row is\n// registered when a session is given), and Reset View rescuing a subset (it\n// walks the rows).\n// ---------------------------------------------------------------------------\n\n\nexport interface ToolWindowSpec<N extends string = string> {\n /** The window's key: the store slice it remembers under, the dev-console\n * prefix, and what `open(name)` takes. */\n name: N;\n title: string;\n /** Renderer entry, relative to the env's rendererDir. */\n page: string;\n def: ToolWindowSize;\n min: ToolWindowSize;\n /** The remembered rect, read at open time (a store read-through). */\n bounds: () => ToolWindowBounds | undefined;\n /** Persist a moved / resized rect (debounced by the factory). */\n remember: (bounds: ToolWindowBounds) => void;\n /** Whether it starts pinned above the editor (both apps default true). */\n pinned: () => boolean;\n /** Frameless (the family's shape: the renderer draws `toolWindowHead`).\n * Default false = frameless; pass `frame: true` for an OS title bar. */\n frame?: boolean;\n /** The project-changed channel for THIS window when it differs from the\n * env's (Storyletter's Links window listens on \"links:focus\"). */\n channel?: string;\n /** Drop what main caches for the outgoing project on this window's behalf\n * (a coverage report, a search anchor). Runs whether the window is open\n * or not: see session.ts. */\n clear?: () => void;\n}\n\nexport interface ToolWindowEnv {\n /** Absolute path of the built renderer dir. */\n rendererDir: string;\n /** Absolute path of the preload bridge. */\n preload: string;\n /** The window to pin above: the app's main window, resolved at open time. */\n pinTo: () => BrowserWindow | undefined;\n /** Register every row as a satellite of the project session, so a new\n * project cannot open underneath a tool window that still shows the old\n * one. `channel` is the nudge each window listens on (a row may override). */\n session?: { addSatellite: (satellite: Satellite) => () => void; channel: string };\n /** Reset View's store half, run first: forget every remembered rect and\n * set pinned true (`resetWindows(store)` from app-store, or the app's own). */\n resetStore?: () => void;\n /** The channel a rescued window's renderer hears its re-pin on, so its pin\n * button agrees with the store (default \"state:pinned\"). */\n pinChannel?: string;\n /** Runs once per window created: forward its console in dev, say. */\n onOpened?: (w: BrowserWindow, name: string) => void;\n}\n\nexport interface ToolWindows<N extends string> {\n /** Open (or focus) one window by name. */\n open(name: N): BrowserWindow;\n /** The live window, if open. */\n get(name: N): BrowserWindow | undefined;\n /** Reset View's window half, over the table: store reset, then every open\n * window restored, default size, centred, re-pinned, and told so. */\n rescue(): void;\n /** Every window that is open right now. */\n all(): BrowserWindow[];\n readonly names: readonly N[];\n}\n\n/** Build the table. The windows are owned here: a host reads them through\n * `get(name)` rather than holding module variables of its own. */\nexport function defineToolWindows<N extends string>(specs: ToolWindowSpec<N>[], env: ToolWindowEnv): ToolWindows<N> {\n const live = new Map<N, BrowserWindow>();\n const byName = (name: N): ToolWindowSpec<N> => {\n const found = specs.find((s) => s.name === name);\n if (!found) throw new Error(`no tool window \"${name}\"`);\n return found;\n };\n const get = (name: N): BrowserWindow | undefined => {\n const w = live.get(name);\n return w && !w.isDestroyed() ? w : undefined;\n };\n if (env.session) {\n for (const spec of specs) {\n env.session.addSatellite({\n window: () => get(spec.name),\n channel: spec.channel ?? env.session.channel,\n ...(spec.clear ? { clear: spec.clear } : {}),\n });\n }\n }\n const open = (name: N): BrowserWindow => {\n const spec = byName(name);\n const existing = get(name);\n const opened = openToolWindow(existing, {\n title: spec.title, page: spec.page, frame: spec.frame ?? false,\n rendererDir: env.rendererDir, preload: env.preload,\n rect: savedWindowRect(spec.bounds(), spec.def, spec.min),\n min: spec.min,\n pinTo: env.pinTo,\n pinned: spec.pinned(),\n remember: spec.remember,\n });\n if (opened !== existing) {\n live.set(name, opened);\n // Identity-guarded: a stale close never clears a newer window.\n opened.on(\"closed\", () => { if (live.get(name) === opened) live.delete(name); });\n env.onOpened?.(opened, name);\n }\n return opened;\n };\n const rescue = (): void => {\n env.resetStore?.();\n for (const spec of specs) rescueToolWindow(get(spec.name), spec.def);\n // ACTUALLY pin them, then tell them. `rescueToolWindow` restores, resizes,\n // centres and raises but never pins; the store now says pinned, so the\n // window and its button have to agree before the claim is made.\n for (const spec of specs) {\n const w = get(spec.name);\n if (!w) continue;\n pinToolWindow(w, env.pinTo(), true);\n w.webContents.send(env.pinChannel ?? \"state:pinned\", true);\n }\n };\n return {\n open, get, rescue,\n all: () => specs.map((s) => get(s.name)).filter((w): w is BrowserWindow => w !== undefined),\n names: specs.map((s) => s.name),\n };\n}\n"]}
|
package/dist/tool-window.d.cts
CHANGED
|
@@ -59,6 +59,15 @@ declare function rescueToolWindow(w: BrowserWindow | undefined | null, def: Tool
|
|
|
59
59
|
* way. `platform` is a seam for the tests; leave it defaulted.
|
|
60
60
|
*/
|
|
61
61
|
declare function pinToolWindow(w: BrowserWindow | undefined | null, parent: BrowserWindow | undefined | null, on: boolean, platform?: NodeJS.Platform): void;
|
|
62
|
+
/**
|
|
63
|
+
* Bring `main` in front of every tool window, pinned ones included, for as
|
|
64
|
+
* long as a modal it shows is open: un-minimise, show, focus, with the pins
|
|
65
|
+
* set aside. Returns the release, which puts the pins back (idempotent; holds
|
|
66
|
+
* nest, and the pins return when the last is released). Call it only while
|
|
67
|
+
* the app is active: focusing a window of an inactive app is the focus steal
|
|
68
|
+
* the platforms forbid. `platform` is a seam for the tests.
|
|
69
|
+
*/
|
|
70
|
+
declare function frontForModal(main: BrowserWindow, platform?: NodeJS.Platform): () => void;
|
|
62
71
|
interface ToolWindowOptions {
|
|
63
72
|
title: string;
|
|
64
73
|
/** Renderer entry, relative to rendererDir (e.g. "table.html", "search/index.html"). */
|
|
@@ -155,4 +164,4 @@ interface ToolWindows<N extends string> {
|
|
|
155
164
|
* `get(name)` rather than holding module variables of its own. */
|
|
156
165
|
declare function defineToolWindows<N extends string>(specs: ToolWindowSpec<N>[], env: ToolWindowEnv): ToolWindows<N>;
|
|
157
166
|
|
|
158
|
-
export { type ToolWindowBounds, type ToolWindowEnv, type ToolWindowOptions, type ToolWindowSize, type ToolWindowSpec, type ToolWindowState, type ToolWindows, centeredOnPrimary, defineToolWindows, openToolWindow, pinToolWindow, rememberBounds, rescueToolWindow, savedWindowRect };
|
|
167
|
+
export { type ToolWindowBounds, type ToolWindowEnv, type ToolWindowOptions, type ToolWindowSize, type ToolWindowSpec, type ToolWindowState, type ToolWindows, centeredOnPrimary, defineToolWindows, frontForModal, openToolWindow, pinToolWindow, rememberBounds, rescueToolWindow, savedWindowRect };
|
package/dist/tool-window.d.ts
CHANGED
|
@@ -59,6 +59,15 @@ declare function rescueToolWindow(w: BrowserWindow | undefined | null, def: Tool
|
|
|
59
59
|
* way. `platform` is a seam for the tests; leave it defaulted.
|
|
60
60
|
*/
|
|
61
61
|
declare function pinToolWindow(w: BrowserWindow | undefined | null, parent: BrowserWindow | undefined | null, on: boolean, platform?: NodeJS.Platform): void;
|
|
62
|
+
/**
|
|
63
|
+
* Bring `main` in front of every tool window, pinned ones included, for as
|
|
64
|
+
* long as a modal it shows is open: un-minimise, show, focus, with the pins
|
|
65
|
+
* set aside. Returns the release, which puts the pins back (idempotent; holds
|
|
66
|
+
* nest, and the pins return when the last is released). Call it only while
|
|
67
|
+
* the app is active: focusing a window of an inactive app is the focus steal
|
|
68
|
+
* the platforms forbid. `platform` is a seam for the tests.
|
|
69
|
+
*/
|
|
70
|
+
declare function frontForModal(main: BrowserWindow, platform?: NodeJS.Platform): () => void;
|
|
62
71
|
interface ToolWindowOptions {
|
|
63
72
|
title: string;
|
|
64
73
|
/** Renderer entry, relative to rendererDir (e.g. "table.html", "search/index.html"). */
|
|
@@ -155,4 +164,4 @@ interface ToolWindows<N extends string> {
|
|
|
155
164
|
* `get(name)` rather than holding module variables of its own. */
|
|
156
165
|
declare function defineToolWindows<N extends string>(specs: ToolWindowSpec<N>[], env: ToolWindowEnv): ToolWindows<N>;
|
|
157
166
|
|
|
158
|
-
export { type ToolWindowBounds, type ToolWindowEnv, type ToolWindowOptions, type ToolWindowSize, type ToolWindowSpec, type ToolWindowState, type ToolWindows, centeredOnPrimary, defineToolWindows, openToolWindow, pinToolWindow, rememberBounds, rescueToolWindow, savedWindowRect };
|
|
167
|
+
export { type ToolWindowBounds, type ToolWindowEnv, type ToolWindowOptions, type ToolWindowSize, type ToolWindowSpec, type ToolWindowState, type ToolWindows, centeredOnPrimary, defineToolWindows, frontForModal, openToolWindow, pinToolWindow, rememberBounds, rescueToolWindow, savedWindowRect };
|
package/dist/tool-window.js
CHANGED
|
@@ -1,167 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
|
|
4
|
-
// src/tool-window.ts
|
|
5
|
-
function savedWindowRect(saved, def, min) {
|
|
6
|
-
if (!saved) return { ...def };
|
|
7
|
-
const w = Math.max(min.width, saved.width), h = Math.max(min.height, saved.height);
|
|
8
|
-
if (saved.x != null && saved.y != null) {
|
|
9
|
-
const onScreen = screen.getAllDisplays().some((d) => {
|
|
10
|
-
const a = d.workArea;
|
|
11
|
-
return saved.x + w > a.x + 40 && saved.x < a.x + a.width - 40 && saved.y + h > a.y + 20 && saved.y < a.y + a.height - 20;
|
|
12
|
-
});
|
|
13
|
-
if (onScreen) return { x: saved.x, y: saved.y, width: w, height: h };
|
|
14
|
-
}
|
|
15
|
-
return { width: w, height: h };
|
|
16
|
-
}
|
|
17
|
-
function rememberBounds(w, write) {
|
|
18
|
-
const saveBounds = () => {
|
|
19
|
-
if (!w.isDestroyed()) write(w.getBounds());
|
|
20
|
-
};
|
|
21
|
-
let boundsTimer;
|
|
22
|
-
const queueSave = () => {
|
|
23
|
-
clearTimeout(boundsTimer);
|
|
24
|
-
boundsTimer = setTimeout(saveBounds, 400);
|
|
25
|
-
};
|
|
26
|
-
w.on("resize", queueSave);
|
|
27
|
-
w.on("move", queueSave);
|
|
28
|
-
w.on("close", () => {
|
|
29
|
-
clearTimeout(boundsTimer);
|
|
30
|
-
saveBounds();
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
function centeredOnPrimary(size) {
|
|
34
|
-
const a = screen.getPrimaryDisplay().workArea;
|
|
35
|
-
return { x: Math.round(a.x + (a.width - size.width) / 2), y: Math.round(a.y + (a.height - size.height) / 2) };
|
|
36
|
-
}
|
|
37
|
-
function rescueToolWindow(w, def) {
|
|
38
|
-
if (!w || w.isDestroyed()) return;
|
|
39
|
-
if (w.isMinimized()) w.restore();
|
|
40
|
-
w.setBounds({ ...def, ...centeredOnPrimary(def) });
|
|
41
|
-
w.show();
|
|
42
|
-
w.moveTop();
|
|
43
|
-
}
|
|
44
|
-
var macPinned = /* @__PURE__ */ new Set();
|
|
45
|
-
var macActivationWired = false;
|
|
46
|
-
function wireMacActivation() {
|
|
47
|
-
if (macActivationWired) return;
|
|
48
|
-
macActivationWired = true;
|
|
49
|
-
app.on("did-become-active", () => {
|
|
50
|
-
for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(true, "floating");
|
|
51
|
-
});
|
|
52
|
-
app.on("did-resign-active", () => {
|
|
53
|
-
for (const w of macPinned) if (!w.isDestroyed()) w.setAlwaysOnTop(false);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
function pinToolWindow(w, parent, on, platform = process.platform) {
|
|
57
|
-
if (!w || w.isDestroyed()) return;
|
|
58
|
-
if (platform === "darwin") {
|
|
59
|
-
w.setParentWindow(null);
|
|
60
|
-
if (on) {
|
|
61
|
-
if (!macPinned.has(w)) {
|
|
62
|
-
macPinned.add(w);
|
|
63
|
-
w.once("closed", () => macPinned.delete(w));
|
|
64
|
-
}
|
|
65
|
-
wireMacActivation();
|
|
66
|
-
w.setAlwaysOnTop(BrowserWindow.getFocusedWindow() !== null, "floating");
|
|
67
|
-
} else {
|
|
68
|
-
macPinned.delete(w);
|
|
69
|
-
w.setAlwaysOnTop(false);
|
|
70
|
-
}
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
w.setAlwaysOnTop(false);
|
|
74
|
-
w.setParentWindow(on && parent && !parent.isDestroyed() ? parent : null);
|
|
75
|
-
}
|
|
76
|
-
function openToolWindow(existing, opts) {
|
|
77
|
-
if (existing && !existing.isDestroyed()) {
|
|
78
|
-
existing.focus();
|
|
79
|
-
return existing;
|
|
80
|
-
}
|
|
81
|
-
const w = new BrowserWindow({
|
|
82
|
-
...opts.rect,
|
|
83
|
-
...opts.min !== void 0 ? { minWidth: opts.min.width, minHeight: opts.min.height } : {},
|
|
84
|
-
show: false,
|
|
85
|
-
title: opts.title,
|
|
86
|
-
...opts.frame !== void 0 ? { frame: opts.frame } : {},
|
|
87
|
-
webPreferences: {
|
|
88
|
-
contextIsolation: true,
|
|
89
|
-
nodeIntegration: false,
|
|
90
|
-
sandbox: true,
|
|
91
|
-
preload: opts.preload
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
if (opts.pinTo !== void 0) pinToolWindow(w, opts.pinTo(), opts.pinned ?? false);
|
|
95
|
-
if (opts.remember) rememberBounds(w, opts.remember);
|
|
96
|
-
w.once("ready-to-show", () => w.show());
|
|
97
|
-
if (opts.onClosed) w.on("closed", opts.onClosed);
|
|
98
|
-
if (process.env["ELECTRON_RENDERER_URL"]) void w.loadURL(`${process.env["ELECTRON_RENDERER_URL"]}/${opts.page}`);
|
|
99
|
-
else void w.loadFile(join(opts.rendererDir, opts.page));
|
|
100
|
-
return w;
|
|
101
|
-
}
|
|
102
|
-
function defineToolWindows(specs, env) {
|
|
103
|
-
const live = /* @__PURE__ */ new Map();
|
|
104
|
-
const byName = (name) => {
|
|
105
|
-
const found = specs.find((s) => s.name === name);
|
|
106
|
-
if (!found) throw new Error(`no tool window "${name}"`);
|
|
107
|
-
return found;
|
|
108
|
-
};
|
|
109
|
-
const get = (name) => {
|
|
110
|
-
const w = live.get(name);
|
|
111
|
-
return w && !w.isDestroyed() ? w : void 0;
|
|
112
|
-
};
|
|
113
|
-
if (env.session) {
|
|
114
|
-
for (const spec of specs) {
|
|
115
|
-
env.session.addSatellite({
|
|
116
|
-
window: () => get(spec.name),
|
|
117
|
-
channel: spec.channel ?? env.session.channel,
|
|
118
|
-
...spec.clear ? { clear: spec.clear } : {}
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
const open = (name) => {
|
|
123
|
-
const spec = byName(name);
|
|
124
|
-
const existing = get(name);
|
|
125
|
-
const opened = openToolWindow(existing, {
|
|
126
|
-
title: spec.title,
|
|
127
|
-
page: spec.page,
|
|
128
|
-
frame: spec.frame ?? false,
|
|
129
|
-
rendererDir: env.rendererDir,
|
|
130
|
-
preload: env.preload,
|
|
131
|
-
rect: savedWindowRect(spec.bounds(), spec.def, spec.min),
|
|
132
|
-
min: spec.min,
|
|
133
|
-
pinTo: env.pinTo,
|
|
134
|
-
pinned: spec.pinned(),
|
|
135
|
-
remember: spec.remember
|
|
136
|
-
});
|
|
137
|
-
if (opened !== existing) {
|
|
138
|
-
live.set(name, opened);
|
|
139
|
-
opened.on("closed", () => {
|
|
140
|
-
if (live.get(name) === opened) live.delete(name);
|
|
141
|
-
});
|
|
142
|
-
env.onOpened?.(opened, name);
|
|
143
|
-
}
|
|
144
|
-
return opened;
|
|
145
|
-
};
|
|
146
|
-
const rescue = () => {
|
|
147
|
-
env.resetStore?.();
|
|
148
|
-
for (const spec of specs) rescueToolWindow(get(spec.name), spec.def);
|
|
149
|
-
for (const spec of specs) {
|
|
150
|
-
const w = get(spec.name);
|
|
151
|
-
if (!w) continue;
|
|
152
|
-
pinToolWindow(w, env.pinTo(), true);
|
|
153
|
-
w.webContents.send(env.pinChannel ?? "state:pinned", true);
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
return {
|
|
157
|
-
open,
|
|
158
|
-
get,
|
|
159
|
-
rescue,
|
|
160
|
-
all: () => specs.map((s) => get(s.name)).filter((w) => w !== void 0),
|
|
161
|
-
names: specs.map((s) => s.name)
|
|
162
|
-
};
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
export { centeredOnPrimary, defineToolWindows, openToolWindow, pinToolWindow, rememberBounds, rescueToolWindow, savedWindowRect };
|
|
1
|
+
export { centeredOnPrimary, defineToolWindows, frontForModal, openToolWindow, pinToolWindow, rememberBounds, rescueToolWindow, savedWindowRect } from './chunk-CPNTROWD.js';
|
|
166
2
|
//# sourceMappingURL=tool-window.js.map
|
|
167
3
|
//# sourceMappingURL=tool-window.js.map
|