@wildwinter/app-shell 0.46.5 → 0.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSES.md +5 -4
- package/dist/{chunk-J6RSONXM.js → chunk-62KWJPRP.js} +4 -4
- package/dist/{chunk-J6RSONXM.js.map → chunk-62KWJPRP.js.map} +1 -1
- package/dist/{chunk-WKPKBAXS.js → chunk-C245WRPF.js} +4 -4
- package/dist/{chunk-WKPKBAXS.js.map → chunk-C245WRPF.js.map} +1 -1
- package/dist/{chunk-R3NXPHRZ.js → chunk-CRUILFAT.js} +28 -6
- package/dist/chunk-CRUILFAT.js.map +1 -0
- package/dist/{chunk-EHR2KBZC.js → chunk-OMG6A5YB.js} +3 -3
- package/dist/{chunk-EHR2KBZC.js.map → chunk-OMG6A5YB.js.map} +1 -1
- package/dist/{chunk-2ECJUTSX.js → chunk-T25WXWPB.js} +3 -2
- package/dist/chunk-T25WXWPB.js.map +1 -0
- package/dist/{chunk-B2TC6Q5J.js → chunk-UQTUCOGV.js} +3 -3
- package/dist/{chunk-B2TC6Q5J.js.map → chunk-UQTUCOGV.js.map} +1 -1
- package/dist/{chunk-YRFJLJUH.js → chunk-VXEFIL43.js} +3 -3
- package/dist/{chunk-YRFJLJUH.js.map → chunk-VXEFIL43.js.map} +1 -1
- package/dist/{chunk-UG2P777F.js → chunk-W4WC4FO5.js} +102 -30
- package/dist/chunk-W4WC4FO5.js.map +1 -0
- package/dist/{chunk-CUFREPJ5.js → chunk-XSS7KM6F.js} +4 -4
- package/dist/{chunk-CUFREPJ5.js.map → chunk-XSS7KM6F.js.map} +1 -1
- package/dist/context-menu.cjs.map +1 -1
- package/dist/context-menu.js +2 -2
- package/dist/dialog.cjs.map +1 -1
- package/dist/dialog.js +2 -2
- package/dist/index.cjs +146 -36
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +97 -14
- package/dist/index.d.ts +97 -14
- package/dist/index.js +31 -17
- package/dist/index.js.map +1 -1
- package/dist/kit-gallery.cjs.map +1 -1
- package/dist/kit-gallery.js +3 -3
- package/dist/link-status.cjs +7 -0
- package/dist/link-status.cjs.map +1 -1
- package/dist/link-status.js +2 -2
- package/dist/menu.cjs +1 -0
- package/dist/menu.cjs.map +1 -1
- package/dist/menu.d.cts +8 -4
- package/dist/menu.d.ts +8 -4
- package/dist/menu.js +1 -1
- package/dist/{property-list-D3VZCJzi.d.cts → property-list-BgZlkcbz.d.cts} +19 -2
- package/dist/{property-list-D3VZCJzi.d.ts → property-list-BgZlkcbz.d.ts} +19 -2
- package/dist/property-list.cjs +93 -27
- package/dist/property-list.cjs.map +1 -1
- package/dist/property-list.d.cts +1 -1
- package/dist/property-list.d.ts +1 -1
- package/dist/property-list.js +2 -2
- package/dist/settings.css +78 -0
- package/dist/settings.css.map +1 -1
- package/dist/updater-view.cjs.map +1 -1
- package/dist/updater-view.js +3 -3
- package/dist/welcome.cjs.map +1 -1
- package/dist/welcome.js +4 -4
- package/package.json +1 -1
- package/dist/chunk-2ECJUTSX.js.map +0 -1
- package/dist/chunk-R3NXPHRZ.js.map +0 -1
- package/dist/chunk-UG2P777F.js.map +0 -1
package/LICENSES.md
CHANGED
|
@@ -4,15 +4,16 @@ This package is MIT (see `LICENSE`). The following is vendored inside it.
|
|
|
4
4
|
|
|
5
5
|
## Lucide
|
|
6
6
|
|
|
7
|
-
The icon vocabulary in `src/icons.ts` vendors
|
|
8
|
-
[Lucide](https://lucide.dev) 1.46.0 (`lucide-static`), fetched 2026-09-16
|
|
7
|
+
The icon vocabulary in `src/icons.ts` vendors 32 drawings from
|
|
8
|
+
[Lucide](https://lucide.dev) 1.46.0 (`lucide-static`), fetched 2026-09-16 (copy
|
|
9
|
+
and volume-2 on 2026-10-07) from
|
|
9
10
|
`https://cdn.jsdelivr.net/npm/lucide-static@1.46.0/icons/<name>.svg`, with the
|
|
10
11
|
root attributes normalised (no `xmlns`, `class`, `width` or `height`; the stroke
|
|
11
12
|
width set to 2.571 so a 14px rendering strokes at 1.5px). The drawings used:
|
|
12
13
|
arrow-down, arrow-left, arrow-right, arrow-up, check, chevron-down, chevron-left,
|
|
13
|
-
chevron-right, ellipsis, file-text, grip-vertical, layout-grid, lock, lock-open,
|
|
14
|
+
chevron-right, copy, ellipsis, file-text, grip-vertical, layout-grid, lock, lock-open,
|
|
14
15
|
message-square, pencil, pin, play, plug, plus, rotate-ccw, rows-3, search,
|
|
15
|
-
settings, triangle-alert, waypoints, x. The three filled discs (`modified`,
|
|
16
|
+
settings, triangle-alert, volume-2, waypoints, x. The three filled discs (`modified`,
|
|
16
17
|
`dot`, `record`) are this package's own circle elements.
|
|
17
18
|
|
|
18
19
|
```
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { dialogFrame } from './chunk-
|
|
2
|
-
import { el } from './chunk-
|
|
1
|
+
import { dialogFrame } from './chunk-VXEFIL43.js';
|
|
2
|
+
import { el } from './chunk-W4WC4FO5.js';
|
|
3
3
|
|
|
4
4
|
// src/kit-gallery.ts
|
|
5
5
|
function featurePills(features) {
|
|
@@ -158,5 +158,5 @@ function openKitGallery(opts) {
|
|
|
158
158
|
}
|
|
159
159
|
|
|
160
160
|
export { kitTile, openKitGallery };
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
162
|
-
//# sourceMappingURL=chunk-
|
|
161
|
+
//# sourceMappingURL=chunk-62KWJPRP.js.map
|
|
162
|
+
//# sourceMappingURL=chunk-62KWJPRP.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/kit-gallery.ts"],"names":[],"mappings":";;;;AAoGA,SAAS,aAAa,QAAA,EAAoD;AACxE,EAAA,IAAI,CAAC,QAAA,IAAY,QAAA,CAAS,MAAA,KAAW,GAAG,OAAO,IAAA;AAC/C,EAAA,OAAO,EAAA,CAAG,MAAA,EAAQ,sBAAA,EAAwB,GAAG,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,KAAM,EAAA,CAAG,MAAA,EAAQ,qBAAA,EAAuB,CAAC,CAAC,CAAC,CAAA;AACxG;AAKO,SAAS,OAAA,CAAQ,MAA4E,OAAA,EAAwC;AAC1I,EAAA,MAAM,IAAA,GAAO,EAAA;AAAA,IAAG,QAAA;AAAA,IAAU,EAAE,SAAA,EAAW,kBAAA,EAAoB,OAAA,EAAQ;AAAA,IACjE,EAAA;AAAA,MAAG,MAAA;AAAA,MAAQ,uBAAA;AAAA,MACT,EAAA,CAAG,MAAA,EAAQ,uBAAA,EAAyB,IAAA,CAAK,IAAI,CAAA;AAAA,MAC7C,IAAA,CAAK,UAAU,MAAA,GAAY,EAAA,CAAG,QAAQ,mBAAA,EAAqB,IAAA,CAAK,KAAK,CAAA,GAAI;AAAA,KAAI;AAAA,IAC/E,EAAA,CAAG,MAAA,EAAQ,wBAAA,EAA0B,IAAA,CAAK,KAAK,CAAA;AAAA,IAC/C,YAAA,CAAa,KAAK,QAAQ;AAAA,GAAC;AAC7B,EAAA,IAAA,CAAK,IAAA,GAAO,QAAA;AACZ,EAAA,OAAO,IAAA;AACT;AAGO,SAAS,eAAiC,IAAA,EAAwC;AACvF,EAAA,MAAM,KAAA,GAAQ,WAAA,CAAY,EAAE,KAAA,EAAO,IAAA,CAAK,KAAA,EAAO,GAAI,IAAA,CAAK,IAAA,KAAS,MAAA,GAAY,EAAE,GAAA,EAAK,IAAA,CAAK,IAAA,EAAK,GAAI,EAAC,EAAI,SAAA,EAAW,aAAA,EAAe,GAAI,IAAA,CAAK,OAAA,GAAU,EAAE,OAAA,EAAS,IAAA,CAAK,OAAA,EAAQ,GAAI,IAAK,CAAA;AACrL,EAAA,MAAM,EAAE,QAAO,GAAI,KAAA;AAEnB,EAAA,MAAM,UAAU,IAAA,CAAK,QAAA,CAAS,OAAA,CAAQ,CAAC,SAAS,CAAA,KAAM,OAAA,CAAQ,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,MAAU,EAAE,MAAM,OAAA,EAAS,CAAA,GAAI,CAAC,CAAA;AACzG,EAAA,IAAI,QAAQ,MAAA,KAAW,CAAA,EAAG,MAAM,IAAI,MAAM,yCAAyC,CAAA;AACnF,EAAA,IAAI,MAAA,GAAS,OAAA,CAAQ,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,CAAK,EAAA,KAAO,IAAA,CAAK,OAAO,CAAA,IAAK,OAAA,CAAQ,CAAC,CAAA;AAGzE,EAAA,MAAM,SAAA,GAAY,IAAA,CAAK,eAAA,KAAoB,MAAA,GAAY,MAAA,GAAY,GAAG,OAAA,EAAS,EAAE,SAAA,EAAW,wBAAA,EAA0B,CAAA;AACtH,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,SAAA,CAAU,cAAc,IAAA,CAAK,eAAA;AAC7B,IAAA,SAAA,CAAU,YAAA,CAAa,YAAA,EAAc,IAAA,CAAK,SAAA,IAAa,MAAM,CAAA;AAAA,EAC/D;AACA,EAAA,MAAM,MAAA,GAAS,EAAA;AAAA,IAAG,KAAA;AAAA,IAAO,oBAAA;AAAA,IACvB,GAAI,SAAA,GAAY,CAAC,EAAA,CAAG,OAAA,EAAS,mBAAA,EAAqB,IAAA,CAAK,SAAA,IAAa,MAAM,CAAA,EAAG,SAAS,CAAA,GAAI,EAAC;AAAA,IAC3F,GAAI,IAAA,CAAK,OAAA,GAAU,CAAC,IAAA,CAAK,OAAO,IAAI;AAAC,GAAE;AAEzC,EAAA,MAAM,SAAA,GAAY,EAAA,CAAG,IAAA,EAAM,wBAAwB,CAAA;AACnD,EAAA,MAAM,UAAA,GAAa,EAAA,CAAG,GAAA,EAAK,yBAAyB,CAAA;AACpD,EAAA,MAAM,WAAA,GAAc,EAAA,CAAG,GAAA,EAAK,0BAA0B,CAAA;AACtD,EAAA,MAAM,SAAA,GAAY,EAAA,CAAG,GAAA,EAAK,kBAAkB,CAAA;AAC5C,EAAA,MAAM,aAAA,GAAgB,EAAA,CAAG,KAAA,EAAO,4BAA4B,CAAA;AAC5D,EAAA,MAAM,UAAA,GAAa,EAAA,CAAG,IAAA,EAAM,mBAAmB,CAAA;AAC/C,EAAA,MAAM,iBAAA,GAAoB,EAAA,CAAG,GAAA,EAAK,2BAAA,EAA6B,cAAc,CAAA;AAC7E,EAAA,MAAM,SAAA,GAAY,EAAA,CAAG,GAAA,EAAK,kBAAkB,CAAA;AAC5C,EAAA,MAAM,KAAA,GAAQ,EAAA;AAAA,IAAG,KAAA;AAAA,IAAO,mBAAA;AAAA,IACtB,SAAA;AAAA,IAAW,aAAA;AAAA,IAAe,UAAA;AAAA,IAAY,WAAA;AAAA,IAAa,SAAA;AAAA,IAAW,iBAAA;AAAA,IAAmB,UAAA;AAAA,IAAY,SAAA;AAAA,IAAW;AAAA,GAAM;AAEhH,EAAA,MAAM,OAAA,GAAU,EAAA,CAAG,QAAA,EAAU,EAAE,SAAA,EAAW,eAAe,OAAA,EAAS,MAAM,MAAA,EAAO,EAAG,CAAA;AAClF,EAAA,OAAA,CAAQ,IAAA,GAAO,QAAA;AACf,EAAA,MAAM,MAAA,GAAS,EAAA,CAAG,QAAA,EAAU,EAAE,SAAA,EAAW,KAAA,EAAO,IAAA,EAAM,QAAA,EAAU,OAAA,EAAS,MAAM,KAAA,CAAM,KAAA,IAAS,CAAA;AAC9F,EAAA,MAAA,CAAO,IAAA,GAAO,QAAA;AAEd,EAAA,MAAM,OAAA,GAAU,CAAC,IAAA,EAAmB,IAAA,KAAmC;AACrE,IAAA,IAAA,CAAK,cAAc,IAAA,IAAQ,EAAA;AAC3B,IAAA,IAAA,CAAK,MAAA,GAAS,IAAA,KAAS,MAAA,IAAa,IAAA,KAAS,EAAA;AAAA,EAC/C,CAAA;AAGA,EAAA,MAAM,KAAA,uBAAY,GAAA,EAA0B;AAC5C,EAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,KAAY;AAC7C,IAAA,MAAM,IAAA,GAAO,GAAG,KAAA,EAAO,kBAAA,EAAoB,GAAG,OAAA,CAAQ,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS;AACxE,MAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,EAAE,GAAG,MAAM,KAAA,EAAO,IAAA,CAAK,IAAA,IAAQ,IAAA,CAAK,OAAM,EAAG,MAAM,MAAA,CAAO,IAAA,CAAK,EAAE,CAAC,CAAA;AACvF,MAAA,IAAA,CAAK,OAAA,CAAQ,MAAM,IAAA,CAAK,EAAA;AAGxB,MAAA,IAAA,CAAK,gBAAA,CAAiB,YAAY,MAAM;AAAE,QAAA,MAAA,CAAO,KAAK,EAAE,CAAA;AAAG,QAAA,MAAA,EAAO;AAAA,MAAG,CAAC,CAAA;AACtE,MAAA,KAAA,CAAM,GAAA,CAAI,IAAA,CAAK,EAAA,EAAI,IAAI,CAAA;AACvB,MAAA,OAAO,IAAA;AAAA,IACT,CAAC,CAAC,CAAA;AACF,IAAA,OAAO,EAAA;AAAA,MAAG,SAAA;AAAA,MAAW,mBAAA;AAAA,MACnB,OAAA,CAAQ,YAAY,MAAA,GAAY,EAAA,CAAG,MAAM,qBAAA,EAAuB,OAAA,CAAQ,OAAO,CAAA,GAAI,IAAA;AAAA,MACnF,OAAA,CAAQ,SAAS,MAAA,GAAY,EAAA,CAAG,KAAK,wBAAA,EAA0B,OAAA,CAAQ,IAAI,CAAA,GAAI,IAAA;AAAA,MAC/E;AAAA,KAAI;AAAA,EACR,CAAC,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,CAAC,EAAA,KAAgB;AAC9B,IAAA,MAAM,IAAA,GAAO,QAAQ,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,IAAA,CAAK,OAAO,EAAE,CAAA;AACjD,IAAA,IAAI,CAAC,IAAA,EAAM;AACX,IAAA,MAAA,GAAS,IAAA;AACT,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,IAAI,CAAA,IAAK,KAAA,EAAO,IAAA,CAAK,YAAA,CAAa,cAAA,EAAgB,MAAA,CAAO,GAAA,KAAQ,EAAE,CAAC,CAAA;AACrF,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAI,MAAA;AAC1B,IAAA,SAAA,CAAU,cAAc,IAAA,CAAK,IAAA;AAC7B,IAAA,OAAA,CAAQ,UAAA,EAAY,KAAK,KAAK,CAAA;AAC9B,IAAA,OAAA,CAAQ,WAAA,EAAa,KAAK,MAAM,CAAA;AAChC,IAAA,OAAA,CAAQ,SAAA,EAAW,KAAK,IAAI,CAAA;AAC5B,IAAA,MAAM,KAAA,GAAQ,YAAA,CAAa,IAAA,CAAK,QAAQ,CAAA;AACxC,IAAA,aAAA,CAAc,gBAAgB,GAAI,KAAA,GAAQ,CAAC,KAAK,CAAA,GAAI,EAAG,CAAA;AACvD,IAAA,aAAA,CAAc,SAAS,KAAA,KAAU,IAAA;AACjC,IAAA,UAAA,CAAW,eAAA,CAAgB,GAAA,CAAI,IAAA,CAAK,KAAA,IAAS,EAAC,EAAG,GAAA,CAAI,CAAC,CAAA,KAAM,EAAA,CAAG,IAAA,EAAM,MAAA,EAAW,CAAC,CAAC,CAAC,CAAA;AACnF,IAAA,UAAA,CAAW,MAAA,GAAA,CAAU,IAAA,CAAK,KAAA,IAAS,IAAI,MAAA,KAAW,CAAA;AAClD,IAAA,iBAAA,CAAkB,SAAS,UAAA,CAAW,MAAA;AACtC,IAAA,MAAM,OAAA,GAAU,QAAQ,WAAA,KAAgB,KAAA;AACxC,IAAA,MAAA,CAAO,SAAS,CAAC,OAAA,IAAY,SAAA,KAAc,MAAA,IAAa,KAAK,OAAA,KAAY,MAAA;AAGzE,IAAA,OAAA,CAAQ,SAAA,EAAW,OAAA,GAAU,MAAA,GAAY,OAAA,CAAQ,IAAI,CAAA;AACrD,IAAA,OAAA,CAAQ,WAAA,GAAc,QAAQ,MAAA,IAAU,QAAA;AACxC,IAAA,IAAA,CAAK,QAAA,GAAW,KAAK,EAAE,CAAA;AAAA,EACzB,CAAA;AAEA,EAAA,MAAM,SAAS,MAAY;AACzB,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAQ,GAAI,MAAA;AAC1B,IAAA,MAAM,OAAA,GAAU,QAAQ,WAAA,KAAgB,KAAA;AACxC,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,WAAW,SAAA,EAAW;AACxB,MAAA,IAAA,GAAO,SAAA,CAAU,MAAM,IAAA,EAAK;AAC5B,MAAA,IAAI,SAAS,EAAA,EAAI;AAAE,QAAA,SAAA,CAAU,KAAA,EAAM;AAAG,QAAA,SAAA,CAAU,SAAA,CAAU,IAAI,qBAAqB,CAAA;AAAG,QAAA;AAAA,MAAQ;AAAA,IAChG;AACA,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,QAAA,GAAW,IAAA,CAAK,EAAE,CAAA,IAAK,IAAA;AAC5C,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,OAAA,CAAQ,SAAA,CAAU,IAAI,qBAAqB,CAAA;AAC3C,MAAA,OAAA,CAAQ,gBAAA,CAAiB,OAAA,EAAS,MAAM,OAAA,CAAQ,SAAA,CAAU,MAAA,CAAO,qBAAqB,CAAA,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACvG,MAAA,OAAA,CAAQ,KAAA,EAAM;AACd,MAAA;AAAA,IACF;AACA,IAAA,KAAA,CAAM,KAAA,EAAM;AACZ,IAAA,IAAA,CAAK,MAAA,CAAO,KAAK,EAAA,EAAI,IAAA,KAAS,SAAY,EAAE,IAAA,EAAK,GAAI,EAAE,CAAA;AAAA,EACzD,CAAA;AAEA,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,SAAA,CAAU,gBAAA,CAAiB,SAAS,MAAM;AACxC,MAAA,SAAA,CAAU,SAAA,CAAU,OAAO,qBAAqB,CAAA;AAChD,MAAA,IAAA,CAAK,WAAA,GAAc,UAAU,KAAK,CAAA;AAAA,IACpC,CAAC,CAAA;AACD,IAAA,IAAA,CAAK,cAAc,EAAE,CAAA;AAGrB,IAAA,SAAA,CAAU,gBAAA,CAAiB,SAAA,EAAW,CAAC,CAAA,KAAM;AAC3C,MAAA,CAAA,CAAE,eAAA,EAAgB;AAClB,MAAA,IAAI,CAAA,CAAE,QAAQ,OAAA,EAAS;AACvB,MAAA,CAAA,CAAE,cAAA,EAAe;AACjB,MAAA,MAAA,EAAO;AAAA,IACT,CAAC,CAAA;AAAA,EACH;AAEA,EAAA,KAAA,CAAM,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,KAAA,EAAO,oBAAA,EAAsB,EAAA,CAAG,KAAA,EAAO,qBAAA,EAAuB,GAAG,OAAO,CAAA,EAAG,KAAK,CAAC,CAAA;AACtG,EAAA,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,MAAA,EAAQ,OAAO,CAAA;AACpC,EAAA,MAAA,CAAO,gBAAA,CAAiB,OAAA,EAAS,CAAC,CAAA,KAAM;AAAE,IAAA,IAAI,CAAA,CAAE,MAAA,KAAW,MAAA,EAAQ,KAAA,CAAM,KAAA,EAAM;AAAA,EAAG,CAAC,CAAA;AAEnF,EAAA,MAAA,CAAO,MAAA,CAAO,KAAK,EAAE,CAAA;AACrB,EAAA,KAAA,CAAM,IAAA,EAAK;AAGX,EAAA,IAAI,aAAa,MAAA,CAAO,OAAA,CAAQ,WAAA,KAAgB,KAAA,YAAiB,KAAA,EAAM;AAAA,aAC5D,GAAA,CAAI,MAAA,CAAO,IAAA,CAAK,EAAE,GAAG,KAAA,EAAM;AAEtC,EAAA,OAAO,EAAE,MAAA,EAAQ,KAAA,EAAO,MAAM,KAAA,CAAM,OAAM,EAAE;AAC9C","file":"chunk-J6RSONXM.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The kit gallery: pick a starting point by what you want to make.\n//\n// The new-thing moment for the family (New Project, New Box, New Scene), drawn\n// once. Adobe's New Document is the shape: tiles on the left, a details panel\n// on the right, one screen. Not a wizard: a wizard asks for settings before\n// anything has been chosen, and the choice is the part that teaches.\n//\n// Storyletter grew this as `kit-picker.ts` (one component at two scales on\n// <dialog>, \"a shell candidate, held back until it had been used in earnest\")\n// and the kit gallery brief moved it here, 2026-09-27\n// (storylet-studio/design/kit-gallery.md). What a kit IS made of stays in\n// each app: this file knows names, words and pictures, never boxes or scenes.\n//\n// Sections are shelves (\"Start from a kit\", \"Learn from a finished project\"),\n// and each says what its button does, because opening a copy of an example\n// and creating from a kit are different acts behind the same kind of tile.\n// The app's own fields (a project name, version control, a build path) sit in\n// the details panel, shown only for the sections that use them.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\nexport interface KitGalleryItem<T extends string> {\n id: T;\n /** What you are making, in the author's terms, not the model's. */\n name: string;\n /** What it is FOR: the panel's first line, and the tile's unless `tile` says otherwise. */\n blurb: string;\n /** The tile's line, when the blurb is longer than a tile holds (three\n * lines). A whole short sentence reads better than a long one cut off. */\n tile?: string;\n /** More, for the panel only. */\n detail?: string;\n /** What pressing Play (or Run) shows, said as a promise. */\n play?: string;\n /** What it has, in a word or two each (\"Drawn map\", \"Copies\"): pills on the tile and in the\n * panel, so kits compare at a glance before any is chosen. The author's ask, 2026-09-27, in\n * place of pictures, which were ruled out the same day. */\n features?: string[];\n /** What lands, in the concrete, as a short list in the panel. */\n lands?: string[];\n /** A word or two on the tile (\"Start here\"). */\n badge?: string;\n}\n\nexport interface KitGallerySection<T extends string> {\n /** Sentence-case label text (\"Start from a kit\"). Absent: no caption. */\n caption?: string;\n /** The consequence of a click, said before the click. */\n note?: string;\n items: KitGalleryItem<T>[];\n /** The primary button's label while an item here is chosen. Default \"Create\". */\n action?: string;\n /** Show the app's detail fields (and ask for the name) for this section.\n * Default true. An example is opened, not named, so its shelf says false. */\n usesDetails?: boolean;\n}\n\nexport interface KitGalleryChoice {\n /** Present when a name was asked for and the chosen section uses details. */\n name?: string;\n}\n\nexport interface KitGalleryOptions<T extends string> {\n /** The dialog's heading: \"New project\", \"New box\", \"New scene\". */\n title: string;\n /** What the thing being made IS, in concrete terms. */\n what?: string;\n sections: KitGallerySection<T>[];\n /** Ask for a name in the details panel, with this placeholder. Required\n * when asked: making a thing without one would have to invent a folder. */\n namePlaceholder?: string;\n /** The name field's label. Default \"Name\". */\n nameLabel?: string;\n /** The app's own fields, below the name (version control, a build path). */\n details?: HTMLElement;\n /** Runs whenever an item is chosen, the opening one included, so the app can show or hide its\n * own fields for it (New Scene's Speaker, for a kit with lines). */\n onChoose?: (id: T) => void;\n /** Checked before `onPick`, after the name: return the app's field that still needs filling (it\n * is focused and marked, and nothing is made), or null when all is well. */\n validate?: (id: T) => HTMLElement | null;\n /** Runs as the name is typed (and once on open, with \"\"), so the app's own\n * fields can follow it: Patterpad's folder preview and default publish path. */\n onNameInput?: (name: string) => void;\n /** The item chosen when the gallery opens. Default: the first. */\n initial?: T;\n onPick: (id: T, choice: KitGalleryChoice) => void;\n /** Runs once the gallery has closed, whichever way. */\n onClose?: () => void;\n}\n\nexport interface KitGallery {\n readonly dialog: HTMLDialogElement;\n close(): void;\n}\n\n/** A kit's features as pills, or null when it lists none. */\nfunction featurePills(features: string[] | undefined): HTMLElement | null {\n if (!features || features.length === 0) return null;\n return el(\"span\", \"kit-gallery-features\", ...features.map((f) => el(\"span\", \"kit-gallery-feature\", f)));\n}\n\n/** One tile: the name with its badge, the blurb, the feature pills. The gallery's\n * own, and the welcome screen's, so a kit looks the same wherever it is met\n * (welcome.ts draws a tiled group with it; import kit-gallery.css there too). */\nexport function kitTile(item: { name: string; blurb: string; badge?: string; features?: string[] }, onClick: () => void): HTMLButtonElement {\n const tile = el(\"button\", { className: \"kit-gallery-tile\", onClick },\n el(\"span\", \"kit-gallery-tile-head\",\n el(\"span\", \"kit-gallery-tile-name\", item.name),\n item.badge !== undefined ? el(\"span\", \"kit-gallery-badge\", item.badge) : null),\n el(\"span\", \"kit-gallery-tile-blurb\", item.blurb),\n featurePills(item.features)) as HTMLButtonElement;\n tile.type = \"button\";\n return tile;\n}\n\n/** Open the gallery as a modal dialog on the family's frame. */\nexport function openKitGallery<T extends string>(opts: KitGalleryOptions<T>): KitGallery {\n const frame = dialogFrame({ title: opts.title, ...(opts.what !== undefined ? { sub: opts.what } : {}), className: \"kit-gallery\", ...(opts.onClose ? { onClose: opts.onClose } : {}) });\n const { dialog } = frame;\n\n const entries = opts.sections.flatMap((section, s) => section.items.map((item) => ({ item, section, s })));\n if (entries.length === 0) throw new Error(\"openKitGallery: no items to choose from\");\n let chosen = entries.find((e) => e.item.id === opts.initial) ?? entries[0]!;\n\n // --- the details panel ----------------------------------------------------\n const nameInput = opts.namePlaceholder === undefined ? undefined : el(\"input\", { className: \"field kit-gallery-name\" }) as HTMLInputElement;\n if (nameInput) {\n nameInput.placeholder = opts.namePlaceholder!;\n nameInput.setAttribute(\"aria-label\", opts.nameLabel ?? \"Name\");\n }\n const fields = el(\"div\", \"kit-gallery-fields\",\n ...(nameInput ? [el(\"label\", \"kit-gallery-label\", opts.nameLabel ?? \"Name\"), nameInput] : []),\n ...(opts.details ? [opts.details] : []));\n\n const panelName = el(\"h3\", \"kit-gallery-panel-name\");\n const panelBlurb = el(\"p\", \"kit-gallery-panel-blurb\");\n const panelDetail = el(\"p\", \"kit-gallery-panel-detail\");\n const panelPlay = el(\"p\", \"kit-gallery-play\");\n const panelFeatures = el(\"div\", \"kit-gallery-panel-features\");\n const panelLands = el(\"ul\", \"kit-gallery-lands\");\n const panelLandsCaption = el(\"p\", \"kit-gallery-lands-caption\", \"What you get\");\n const panelNote = el(\"p\", \"kit-gallery-note\");\n const panel = el(\"div\", \"kit-gallery-panel\",\n panelName, panelFeatures, panelBlurb, panelDetail, panelPlay, panelLandsCaption, panelLands, panelNote, fields);\n\n const primary = el(\"button\", { className: \"btn primary\", onClick: () => commit() }) as HTMLButtonElement;\n primary.type = \"button\";\n const cancel = el(\"button\", { className: \"btn\", text: \"Cancel\", onClick: () => frame.close() }) as HTMLButtonElement;\n cancel.type = \"button\";\n\n const setText = (node: HTMLElement, text: string | undefined): void => {\n node.textContent = text ?? \"\";\n node.hidden = text === undefined || text === \"\";\n };\n\n // --- the tiles --------------------------------------------------------------\n const tiles = new Map<T, HTMLButtonElement>();\n const shelves = opts.sections.map((section) => {\n const grid = el(\"div\", \"kit-gallery-grid\", ...section.items.map((item) => {\n const tile = kitTile({ ...item, blurb: item.tile ?? item.blurb }, () => choose(item.id));\n tile.dataset.kit = item.id;\n // Double-click is the desktop's \"choose and go\"; a single click only chooses,\n // so a tile can be read in the panel before anything is made.\n tile.addEventListener(\"dblclick\", () => { choose(item.id); commit(); });\n tiles.set(item.id, tile);\n return tile;\n }));\n return el(\"section\", \"kit-gallery-shelf\",\n section.caption !== undefined ? el(\"h3\", \"kit-gallery-caption\", section.caption) : null,\n section.note !== undefined ? el(\"p\", \"kit-gallery-shelf-note\", section.note) : null,\n grid);\n });\n\n const choose = (id: T): void => {\n const next = entries.find((e) => e.item.id === id);\n if (!next) return;\n chosen = next;\n for (const [tid, tile] of tiles) tile.setAttribute(\"aria-pressed\", String(tid === id));\n const { item, section } = chosen;\n panelName.textContent = item.name;\n setText(panelBlurb, item.blurb);\n setText(panelDetail, item.detail);\n setText(panelPlay, item.play);\n const pills = featurePills(item.features);\n panelFeatures.replaceChildren(...(pills ? [pills] : []));\n panelFeatures.hidden = pills === null;\n panelLands.replaceChildren(...(item.lands ?? []).map((l) => el(\"li\", undefined, l)));\n panelLands.hidden = (item.lands ?? []).length === 0;\n panelLandsCaption.hidden = panelLands.hidden;\n const details = section.usesDetails !== false;\n fields.hidden = !details || (nameInput === undefined && opts.details === undefined);\n // Without the fields, the shelf's note is what the click will do next\n // (\"a folder you choose\"), so the panel repeats it beside the button.\n setText(panelNote, details ? undefined : section.note);\n primary.textContent = section.action ?? \"Create\";\n opts.onChoose?.(item.id);\n };\n\n const commit = (): void => {\n const { item, section } = chosen;\n const details = section.usesDetails !== false;\n let name: string | undefined;\n if (details && nameInput) {\n name = nameInput.value.trim();\n if (name === \"\") { nameInput.focus(); nameInput.classList.add(\"kit-gallery-missing\"); return; }\n }\n const missing = opts.validate?.(item.id) ?? null;\n if (missing) {\n missing.classList.add(\"kit-gallery-missing\");\n missing.addEventListener(\"input\", () => missing.classList.remove(\"kit-gallery-missing\"), { once: true });\n missing.focus();\n return;\n }\n frame.close();\n opts.onPick(item.id, name !== undefined ? { name } : {});\n };\n\n if (nameInput) {\n nameInput.addEventListener(\"input\", () => {\n nameInput.classList.remove(\"kit-gallery-missing\");\n opts.onNameInput?.(nameInput.value);\n });\n opts.onNameInput?.(\"\");\n // Enter creates with what is chosen: the author has said what it should be\n // called, and the tile they are looking at is the one they meant.\n nameInput.addEventListener(\"keydown\", (e) => {\n e.stopPropagation();\n if (e.key !== \"Enter\") return;\n e.preventDefault();\n commit();\n });\n }\n\n frame.body.append(el(\"div\", \"kit-gallery-layout\", el(\"div\", \"kit-gallery-shelves\", ...shelves), panel));\n frame.actions.append(cancel, primary);\n dialog.addEventListener(\"click\", (e) => { if (e.target === dialog) frame.close(); });\n\n choose(chosen.item.id);\n frame.open();\n // Focus where the next keystroke is wanted: the name when one is asked for\n // and the opening shelf uses it, otherwise the chosen tile.\n if (nameInput && chosen.section.usesDetails !== false) nameInput.focus();\n else tiles.get(chosen.item.id)?.focus();\n\n return { dialog, close: () => frame.close() };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/kit-gallery.ts"],"names":[],"mappings":"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","file":"chunk-62KWJPRP.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The kit gallery: pick a starting point by what you want to make.\n//\n// The new-thing moment for the family (New Project, New Box, New Scene), drawn\n// once. Adobe's New Document is the shape: tiles on the left, a details panel\n// on the right, one screen. Not a wizard: a wizard asks for settings before\n// anything has been chosen, and the choice is the part that teaches.\n//\n// Storyletter grew this as `kit-picker.ts` (one component at two scales on\n// <dialog>, \"a shell candidate, held back until it had been used in earnest\")\n// and the kit gallery brief moved it here, 2026-09-27\n// (storylet-studio/design/kit-gallery.md). What a kit IS made of stays in\n// each app: this file knows names, words and pictures, never boxes or scenes.\n//\n// Sections are shelves (\"Start from a kit\", \"Learn from a finished project\"),\n// and each says what its button does, because opening a copy of an example\n// and creating from a kit are different acts behind the same kind of tile.\n// The app's own fields (a project name, version control, a build path) sit in\n// the details panel, shown only for the sections that use them.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\n\nexport interface KitGalleryItem<T extends string> {\n id: T;\n /** What you are making, in the author's terms, not the model's. */\n name: string;\n /** What it is FOR: the panel's first line, and the tile's unless `tile` says otherwise. */\n blurb: string;\n /** The tile's line, when the blurb is longer than a tile holds (three\n * lines). A whole short sentence reads better than a long one cut off. */\n tile?: string;\n /** More, for the panel only. */\n detail?: string;\n /** What pressing Play (or Run) shows, said as a promise. */\n play?: string;\n /** What it has, in a word or two each (\"Drawn map\", \"Copies\"): pills on the tile and in the\n * panel, so kits compare at a glance before any is chosen. The author's ask, 2026-09-27, in\n * place of pictures, which were ruled out the same day. */\n features?: string[];\n /** What lands, in the concrete, as a short list in the panel. */\n lands?: string[];\n /** A word or two on the tile (\"Start here\"). */\n badge?: string;\n}\n\nexport interface KitGallerySection<T extends string> {\n /** Sentence-case label text (\"Start from a kit\"). Absent: no caption. */\n caption?: string;\n /** The consequence of a click, said before the click. */\n note?: string;\n items: KitGalleryItem<T>[];\n /** The primary button's label while an item here is chosen. Default \"Create\". */\n action?: string;\n /** Show the app's detail fields (and ask for the name) for this section.\n * Default true. An example is opened, not named, so its shelf says false. */\n usesDetails?: boolean;\n}\n\nexport interface KitGalleryChoice {\n /** Present when a name was asked for and the chosen section uses details. */\n name?: string;\n}\n\nexport interface KitGalleryOptions<T extends string> {\n /** The dialog's heading: \"New project\", \"New box\", \"New scene\". */\n title: string;\n /** What the thing being made IS, in concrete terms. */\n what?: string;\n sections: KitGallerySection<T>[];\n /** Ask for a name in the details panel, with this placeholder. Required\n * when asked: making a thing without one would have to invent a folder. */\n namePlaceholder?: string;\n /** The name field's label. Default \"Name\". */\n nameLabel?: string;\n /** The app's own fields, below the name (version control, a build path). */\n details?: HTMLElement;\n /** Runs whenever an item is chosen, the opening one included, so the app can show or hide its\n * own fields for it (New Scene's Speaker, for a kit with lines). */\n onChoose?: (id: T) => void;\n /** Checked before `onPick`, after the name: return the app's field that still needs filling (it\n * is focused and marked, and nothing is made), or null when all is well. */\n validate?: (id: T) => HTMLElement | null;\n /** Runs as the name is typed (and once on open, with \"\"), so the app's own\n * fields can follow it: Patterpad's folder preview and default publish path. */\n onNameInput?: (name: string) => void;\n /** The item chosen when the gallery opens. Default: the first. */\n initial?: T;\n onPick: (id: T, choice: KitGalleryChoice) => void;\n /** Runs once the gallery has closed, whichever way. */\n onClose?: () => void;\n}\n\nexport interface KitGallery {\n readonly dialog: HTMLDialogElement;\n close(): void;\n}\n\n/** A kit's features as pills, or null when it lists none. */\nfunction featurePills(features: string[] | undefined): HTMLElement | null {\n if (!features || features.length === 0) return null;\n return el(\"span\", \"kit-gallery-features\", ...features.map((f) => el(\"span\", \"kit-gallery-feature\", f)));\n}\n\n/** One tile: the name with its badge, the blurb, the feature pills. The gallery's\n * own, and the welcome screen's, so a kit looks the same wherever it is met\n * (welcome.ts draws a tiled group with it; import kit-gallery.css there too). */\nexport function kitTile(item: { name: string; blurb: string; badge?: string; features?: string[] }, onClick: () => void): HTMLButtonElement {\n const tile = el(\"button\", { className: \"kit-gallery-tile\", onClick },\n el(\"span\", \"kit-gallery-tile-head\",\n el(\"span\", \"kit-gallery-tile-name\", item.name),\n item.badge !== undefined ? el(\"span\", \"kit-gallery-badge\", item.badge) : null),\n el(\"span\", \"kit-gallery-tile-blurb\", item.blurb),\n featurePills(item.features)) as HTMLButtonElement;\n tile.type = \"button\";\n return tile;\n}\n\n/** Open the gallery as a modal dialog on the family's frame. */\nexport function openKitGallery<T extends string>(opts: KitGalleryOptions<T>): KitGallery {\n const frame = dialogFrame({ title: opts.title, ...(opts.what !== undefined ? { sub: opts.what } : {}), className: \"kit-gallery\", ...(opts.onClose ? { onClose: opts.onClose } : {}) });\n const { dialog } = frame;\n\n const entries = opts.sections.flatMap((section, s) => section.items.map((item) => ({ item, section, s })));\n if (entries.length === 0) throw new Error(\"openKitGallery: no items to choose from\");\n let chosen = entries.find((e) => e.item.id === opts.initial) ?? entries[0]!;\n\n // --- the details panel ----------------------------------------------------\n const nameInput = opts.namePlaceholder === undefined ? undefined : el(\"input\", { className: \"field kit-gallery-name\" }) as HTMLInputElement;\n if (nameInput) {\n nameInput.placeholder = opts.namePlaceholder!;\n nameInput.setAttribute(\"aria-label\", opts.nameLabel ?? \"Name\");\n }\n const fields = el(\"div\", \"kit-gallery-fields\",\n ...(nameInput ? [el(\"label\", \"kit-gallery-label\", opts.nameLabel ?? \"Name\"), nameInput] : []),\n ...(opts.details ? [opts.details] : []));\n\n const panelName = el(\"h3\", \"kit-gallery-panel-name\");\n const panelBlurb = el(\"p\", \"kit-gallery-panel-blurb\");\n const panelDetail = el(\"p\", \"kit-gallery-panel-detail\");\n const panelPlay = el(\"p\", \"kit-gallery-play\");\n const panelFeatures = el(\"div\", \"kit-gallery-panel-features\");\n const panelLands = el(\"ul\", \"kit-gallery-lands\");\n const panelLandsCaption = el(\"p\", \"kit-gallery-lands-caption\", \"What you get\");\n const panelNote = el(\"p\", \"kit-gallery-note\");\n const panel = el(\"div\", \"kit-gallery-panel\",\n panelName, panelFeatures, panelBlurb, panelDetail, panelPlay, panelLandsCaption, panelLands, panelNote, fields);\n\n const primary = el(\"button\", { className: \"btn primary\", onClick: () => commit() }) as HTMLButtonElement;\n primary.type = \"button\";\n const cancel = el(\"button\", { className: \"btn\", text: \"Cancel\", onClick: () => frame.close() }) as HTMLButtonElement;\n cancel.type = \"button\";\n\n const setText = (node: HTMLElement, text: string | undefined): void => {\n node.textContent = text ?? \"\";\n node.hidden = text === undefined || text === \"\";\n };\n\n // --- the tiles --------------------------------------------------------------\n const tiles = new Map<T, HTMLButtonElement>();\n const shelves = opts.sections.map((section) => {\n const grid = el(\"div\", \"kit-gallery-grid\", ...section.items.map((item) => {\n const tile = kitTile({ ...item, blurb: item.tile ?? item.blurb }, () => choose(item.id));\n tile.dataset.kit = item.id;\n // Double-click is the desktop's \"choose and go\"; a single click only chooses,\n // so a tile can be read in the panel before anything is made.\n tile.addEventListener(\"dblclick\", () => { choose(item.id); commit(); });\n tiles.set(item.id, tile);\n return tile;\n }));\n return el(\"section\", \"kit-gallery-shelf\",\n section.caption !== undefined ? el(\"h3\", \"kit-gallery-caption\", section.caption) : null,\n section.note !== undefined ? el(\"p\", \"kit-gallery-shelf-note\", section.note) : null,\n grid);\n });\n\n const choose = (id: T): void => {\n const next = entries.find((e) => e.item.id === id);\n if (!next) return;\n chosen = next;\n for (const [tid, tile] of tiles) tile.setAttribute(\"aria-pressed\", String(tid === id));\n const { item, section } = chosen;\n panelName.textContent = item.name;\n setText(panelBlurb, item.blurb);\n setText(panelDetail, item.detail);\n setText(panelPlay, item.play);\n const pills = featurePills(item.features);\n panelFeatures.replaceChildren(...(pills ? [pills] : []));\n panelFeatures.hidden = pills === null;\n panelLands.replaceChildren(...(item.lands ?? []).map((l) => el(\"li\", undefined, l)));\n panelLands.hidden = (item.lands ?? []).length === 0;\n panelLandsCaption.hidden = panelLands.hidden;\n const details = section.usesDetails !== false;\n fields.hidden = !details || (nameInput === undefined && opts.details === undefined);\n // Without the fields, the shelf's note is what the click will do next\n // (\"a folder you choose\"), so the panel repeats it beside the button.\n setText(panelNote, details ? undefined : section.note);\n primary.textContent = section.action ?? \"Create\";\n opts.onChoose?.(item.id);\n };\n\n const commit = (): void => {\n const { item, section } = chosen;\n const details = section.usesDetails !== false;\n let name: string | undefined;\n if (details && nameInput) {\n name = nameInput.value.trim();\n if (name === \"\") { nameInput.focus(); nameInput.classList.add(\"kit-gallery-missing\"); return; }\n }\n const missing = opts.validate?.(item.id) ?? null;\n if (missing) {\n missing.classList.add(\"kit-gallery-missing\");\n missing.addEventListener(\"input\", () => missing.classList.remove(\"kit-gallery-missing\"), { once: true });\n missing.focus();\n return;\n }\n frame.close();\n opts.onPick(item.id, name !== undefined ? { name } : {});\n };\n\n if (nameInput) {\n nameInput.addEventListener(\"input\", () => {\n nameInput.classList.remove(\"kit-gallery-missing\");\n opts.onNameInput?.(nameInput.value);\n });\n opts.onNameInput?.(\"\");\n // Enter creates with what is chosen: the author has said what it should be\n // called, and the tile they are looking at is the one they meant.\n nameInput.addEventListener(\"keydown\", (e) => {\n e.stopPropagation();\n if (e.key !== \"Enter\") return;\n e.preventDefault();\n commit();\n });\n }\n\n frame.body.append(el(\"div\", \"kit-gallery-layout\", el(\"div\", \"kit-gallery-shelves\", ...shelves), panel));\n frame.actions.append(cancel, primary);\n dialog.addEventListener(\"click\", (e) => { if (e.target === dialog) frame.close(); });\n\n choose(chosen.item.id);\n frame.open();\n // Focus where the next keystroke is wanted: the name when one is asked for\n // and the opening shelf uses it, otherwise the chosen tile.\n if (nameInput && chosen.section.usesDetails !== false) nameInput.focus();\n else tiles.get(chosen.item.id)?.focus();\n\n return { dialog, close: () => frame.close() };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { dialogFrame } from './chunk-
|
|
2
|
-
import { el } from './chunk-
|
|
1
|
+
import { dialogFrame } from './chunk-VXEFIL43.js';
|
|
2
|
+
import { el } from './chunk-W4WC4FO5.js';
|
|
3
3
|
|
|
4
4
|
// src/updater-view.ts
|
|
5
5
|
var liveProgress = null;
|
|
@@ -62,5 +62,5 @@ function showUpdaterDialog(opts) {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export { feedUpdaterDownloadProgress, showUpdaterDialog };
|
|
65
|
-
//# sourceMappingURL=chunk-
|
|
66
|
-
//# sourceMappingURL=chunk-
|
|
65
|
+
//# sourceMappingURL=chunk-C245WRPF.js.map
|
|
66
|
+
//# sourceMappingURL=chunk-C245WRPF.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/updater-view.ts"],"names":[],"mappings":"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","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/updater-view.ts"],"names":[],"mappings":"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","file":"chunk-C245WRPF.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The updater's VIEW: the themed prompt main summons over `updater:prompt`\n// (updater.ts) and reads the chosen button index back from, the same contract\n// as dialog.showMessageBox's `response`. The renderer half of the updater is\n// not optional: an unanswered prompt does not fall back to a native dialog,\n// the shell waits 300 seconds and resolves its own fallback index. Both apps\n// had written this view by hand (55 of 60 lines identical); this is the one\n// they share, on the family's dialog frame, the way save.ts has save-view.ts.\n//\n// Storyletter's copy, plus Patterpad's `links` row as an option (an About box\n// used to share this dialog there). Styles ship as updater.css.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { dialogFrame } from \"./dialog.js\";\nimport type { UpdaterDownloadProgress, UpdaterPromptOptions } from \"./updater.js\";\n\nexport interface UpdaterLink { label: string; url: string }\n\nexport interface UpdaterViewOptions extends UpdaterPromptOptions {\n /** Web links under the detail. Opened through `openExternal`, never by\n * navigating the window. */\n links?: UpdaterLink[];\n /** How a link opens (the app's allow-listed shell.openExternal bridge).\n * Required for `links` to do anything. */\n openExternal?: (url: string) => void;\n}\n\n// The progress row of the open `progress: true` dialog, if any. These are\n// modal, so one at a time, so one slot is enough. Closing clears it.\nlet liveProgress: { bar: HTMLElement; label: HTMLElement } | null = null;\n\nconst mb = (n: number): string => (n / (1024 * 1024)).toFixed(1);\n\n/** Route a download-progress tick into the open dialog's bar. No-op when none\n * is showing. Registered once at boot on the app's progress channel. */\nexport function feedUpdaterDownloadProgress(p: UpdaterDownloadProgress): void {\n if (!liveProgress) return;\n const pct = Math.max(0, Math.min(100, p.percent));\n liveProgress.bar.style.width = `${pct}%`;\n liveProgress.label.textContent =\n `${pct.toFixed(0)}%, ${mb(p.transferred)} of ${mb(p.total)} MB (${mb(p.bytesPerSecond)} MB/s)`;\n}\n\n/** Show the prompt as a modal themed dialog; resolve the index of the clicked\n * button (Esc resolves `cancelId`, which defaults to the last button). */\nexport function showUpdaterDialog(opts: UpdaterViewOptions): Promise<number> {\n return new Promise((resolve) => {\n const defaultId = opts.defaultId ?? 0;\n const cancelId = opts.cancelId ?? opts.buttons.length - 1;\n\n let done = false;\n const finish = (idx: number): void => {\n if (done) return;\n done = true;\n liveProgress = null; // stop feeding a dialog that is gone\n resolve(idx);\n frame.close();\n };\n const frame = dialogFrame({\n title: opts.message, className: \"updater-dialog\",\n // Esc is the cancel button, not a further answer: main is waiting on an index.\n onClose: () => finish(cancelId),\n });\n if (opts.detail) frame.body.append(el(\"p\", \"updater-detail\", opts.detail));\n\n if (opts.progress) {\n const track = el(\"div\", \"updater-progress-track\");\n const bar = el(\"div\", \"updater-progress-bar\");\n track.append(bar);\n const label = el(\"p\", \"updater-progress-label\", \"Starting download…\");\n frame.body.append(track, label);\n liveProgress = { bar, label };\n }\n\n if (opts.links?.length) {\n const row = el(\"p\", \"updater-links\");\n for (const link of opts.links) {\n const a = el(\"a\", undefined, link.label);\n a.href = link.url;\n a.addEventListener(\"click\", (e) => { e.preventDefault(); opts.openExternal?.(link.url); });\n row.append(a);\n }\n frame.body.append(row);\n }\n\n opts.buttons.forEach((label, i) => {\n // The default is the affirmative and wears the accent; every other\n // button, the way out included, is the plain one.\n const b = el(\"button\", i === defaultId ? \"btn primary\" : \"btn\", label);\n b.type = \"button\";\n b.addEventListener(\"click\", () => finish(i));\n frame.actions.append(b);\n // Focus the default, unless nobody asked for this prompt: then the\n // cancel button, so a stray Enter or Space dismisses it, never says yes.\n if (i === (opts.unasked ? cancelId : defaultId)) queueMicrotask(() => b.focus());\n });\n\n frame.open();\n });\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { el, iconBtn, moveItem, labelled, tagChips, stageChips } from './chunk-
|
|
1
|
+
import { el, iconBtn, moveItem, labelled, tagChips, stageChips } from './chunk-W4WC4FO5.js';
|
|
2
2
|
|
|
3
3
|
// src/settings.ts
|
|
4
4
|
function expandableRow(opts) {
|
|
@@ -188,9 +188,17 @@ function mountSettingsDialog(opts) {
|
|
|
188
188
|
error.hidden = false;
|
|
189
189
|
return;
|
|
190
190
|
}
|
|
191
|
+
save.disabled = true;
|
|
191
192
|
void (async () => {
|
|
192
|
-
|
|
193
|
-
|
|
193
|
+
let saved;
|
|
194
|
+
try {
|
|
195
|
+
saved = await opts.onSave();
|
|
196
|
+
} catch (err) {
|
|
197
|
+
saved = false;
|
|
198
|
+
console.error("Settings save failed:", err);
|
|
199
|
+
}
|
|
200
|
+
save.disabled = false;
|
|
201
|
+
if (saved !== false) dialog.close("save");
|
|
194
202
|
})();
|
|
195
203
|
});
|
|
196
204
|
return {
|
|
@@ -488,7 +496,21 @@ function mountPropertyList(host, decls, opts = {}) {
|
|
|
488
496
|
}, false, true);
|
|
489
497
|
const details = keep(opts.extraDetails?.(p, i));
|
|
490
498
|
if (p.type === "enum" || p.type === "flags") details.push(labelled("Values", tagChips(p, refreshDefault)));
|
|
491
|
-
if (p.type === "quality")
|
|
499
|
+
if (p.type === "quality") {
|
|
500
|
+
const note = el("p", "shell-stage-note");
|
|
501
|
+
const stages = stageChips(p, refreshDefault, {
|
|
502
|
+
onRename: (from, to) => {
|
|
503
|
+
if (p.default === from) p.default = to;
|
|
504
|
+
const fresh = defaultControl(p, changed, opts);
|
|
505
|
+
def.replaceWith(fresh);
|
|
506
|
+
def = fresh;
|
|
507
|
+
if (opts.onStageRename) opts.onStageRename(p, from, to);
|
|
508
|
+
else changed();
|
|
509
|
+
},
|
|
510
|
+
onReorder: () => opts.onStageReorder?.(p, note)
|
|
511
|
+
});
|
|
512
|
+
details.push(labelled("Stages (in order)", stages), note);
|
|
513
|
+
}
|
|
492
514
|
if (opts.purpose !== false) {
|
|
493
515
|
const purpose = el("input", "field set-purpose");
|
|
494
516
|
purpose.type = "text";
|
|
@@ -530,5 +552,5 @@ function mountPropertyList(host, decls, opts = {}) {
|
|
|
530
552
|
}
|
|
531
553
|
|
|
532
554
|
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 };
|
|
533
|
-
//# sourceMappingURL=chunk-
|
|
534
|
-
//# sourceMappingURL=chunk-
|
|
555
|
+
//# sourceMappingURL=chunk-CRUILFAT.js.map
|
|
556
|
+
//# sourceMappingURL=chunk-CRUILFAT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/settings.ts","../src/property-names.ts","../src/property-name-field.ts","../src/property-list.ts"],"names":[],"mappings":";;;AAqBO,SAAS,cAAc,IAAA,EAAoF;AAChH,EAAA,MAAM,GAAA,GAAM,EAAA,CAAG,KAAA,EAAO,SAAS,CAAA;AAC/B,EAAA,IAAI,KAAK,IAAA,KAAS,MAAA,EAAW,GAAA,CAAI,OAAA,CAAQ,OAAO,IAAA,CAAK,IAAA;AACrD,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,KAAA,EAAO,aAAa,CAAA;AACpC,EAAA,IAAI,IAAA,CAAK,OAAA,IAAW,IAAA,CAAK,OAAA,CAAQ,MAAA,EAAQ;AACvC,IAAA,MAAM,OAAA,GAAU,EAAA,CAAG,KAAA,EAAO,aAAa,CAAA;AAAG,IAAA,OAAA,CAAQ,MAAA,GAAS,IAAA;AAC3D,IAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,OAAA,EAAS,OAAA,CAAQ,OAAO,CAAC,CAAA;AAE9C,IAAA,MAAM,MAAA,GAAS,EAAA,CAAG,QAAA,EAAU,YAAY,CAAA;AAAG,IAAA,MAAA,CAAO,IAAA,GAAO,QAAA;AAIzD,IAAA,MAAA,CAAO,QAAQ,GAAA,GAAM,MAAA;AAAQ,IAAA,MAAA,CAAO,YAAA,CAAa,cAAc,MAAM,CAAA;AACrE,IAAA,MAAA,CAAO,YAAA,CAAa,iBAAiB,OAAO,CAAA;AAC5C,IAAA,MAAA,CAAO,gBAAA,CAAiB,SAAS,MAAM;AACrC,MAAA,MAAM,OAAO,OAAA,CAAQ,MAAA;AACrB,MAAA,OAAA,CAAQ,SAAS,CAAC,IAAA;AAClB,MAAA,MAAA,CAAO,YAAA,CAAa,eAAA,EAAiB,MAAA,CAAO,IAAI,CAAC,CAAA;AACjD,MAAA,MAAA,CAAO,SAAA,CAAU,MAAA,CAAO,MAAA,EAAQ,IAAI,CAAA;AAAA,IACtC,CAAC,CAAA;AACD,IAAA,IAAA,CAAK,OAAO,MAAM,CAAA;AAClB,IAAA,IAAA,CAAK,KAAK,OAAA,CAAQ,CAAC,MAAM,IAAA,CAAK,MAAA,CAAO,CAAC,CAAC,CAAA;AACvC,IAAA,GAAA,CAAI,MAAA,CAAO,MAAM,OAAO,CAAA;AAAA,EAC1B,CAAA,MAAO;AACL,IAAA,IAAA,CAAK,KAAK,OAAA,CAAQ,CAAC,MAAM,IAAA,CAAK,MAAA,CAAO,CAAC,CAAC,CAAA;AACvC,IAAA,GAAA,CAAI,OAAO,IAAI,CAAA;AAAA,EACjB;AACA,EAAA,OAAO,GAAA;AACT;AAgBO,SAAS,SAAA,CAAU,QAAoB,IAAA,EAAuB;AACnE,EAAA,MAAM,GAAA,GAAM,CAAC,GAAG,MAAA,CAAO,iBAA8B,UAAU,CAAC,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAA,CAAQ,SAAS,IAAI,CAAA;AACrG,EAAA,IAAI,CAAC,KAAK,OAAO,KAAA;AACjB,EAAA,MAAM,MAAA,GAAS,GAAA,CAAI,aAAA,CAAiC,4BAA4B,CAAA;AAChF,EAAA,IAAI,UAAU,MAAA,CAAO,YAAA,CAAa,eAAe,CAAA,KAAM,MAAA,SAAe,KAAA,EAAM;AAC5E,EAAA,MAAM,QAAQ,OAAO,UAAA,KAAe,UAAA,IAAc,UAAA,CAAW,kCAAkC,CAAA,CAAE,OAAA;AACjG,EAAA,GAAA,CAAI,cAAA,CAAe,EAAE,KAAA,EAAO,QAAA,EAAU,UAAU,KAAA,GAAQ,MAAA,GAAS,UAAU,CAAA;AAE3E,EAAA,GAAA,CAAI,SAAA,CAAU,OAAO,QAAQ,CAAA;AAC7B,EAAA,KAAK,GAAA,CAAI,WAAA;AACT,EAAA,GAAA,CAAI,SAAA,CAAU,IAAI,QAAQ,CAAA;AAC1B,EAAA,GAAA,CAAI,gBAAA,CAAiB,cAAA,EAAgB,MAAM,GAAA,CAAI,SAAA,CAAU,MAAA,CAAO,QAAQ,CAAA,EAAG,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AACzF,EAAA,OAAO,IAAA;AACT;AASO,SAAS,kBAAA,CAAmB,MAAA,EAAoB,IAAA,EAAc,KAAA,GAAQ,EAAA,EAAsB;AACjG,EAAA,OAAO,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY;AAC9B,IAAA,MAAM,OAAA,GAAU,CAAC,IAAA,KAAuB;AACtC,MAAA,IAAI,SAAA,CAAU,MAAA,EAAQ,IAAI,CAAA,EAAG;AAAE,QAAA,OAAA,CAAQ,IAAI,CAAA;AAAG,QAAA;AAAA,MAAQ;AACtD,MAAA,IAAI,QAAQ,CAAA,EAAG;AAAE,QAAA,OAAA,CAAQ,KAAK,CAAA;AAAG,QAAA;AAAA,MAAQ;AACzC,MAAA,qBAAA,CAAsB,MAAM,OAAA,CAAQ,IAAA,GAAO,CAAC,CAAC,CAAA;AAAA,IAC/C,CAAA;AACA,IAAA,OAAA,CAAQ,KAAK,CAAA;AAAA,EACf,CAAC,CAAA;AACH;AAIO,SAAS,YAAY,MAAA,EAA8C;AACxE,EAAA,MAAM,OAAO,MAAA,EAAQ,gBAAA;AACrB,EAAA,IAAI,EAAE,gBAAgB,WAAA,CAAA,EAAc;AACpC,EAAA,IAAA,CAAK,cAAA,CAAe,EAAE,KAAA,EAAO,SAAA,EAAW,CAAA;AACxC,EAAA,MAAM,OAAO,IAAA,CAAK,aAAA,CAAgC,gBAAgB,CAAA,IAAK,IAAA,CAAK,cAAgC,OAAO,CAAA;AACnH,EAAA,IAAA,EAAM,KAAA,EAAM;AACd;AAcA,IAAM,WAAA,GAAc,oCAAA;AAIb,SAAS,QAAA,GAAqB;AACnC,EAAA,MAAM,QAA+D,EAAC;AACtE,EAAA,MAAM,QAAQ,MAAe;AAC3B,IAAA,MAAM,MAAA,uBAAa,GAAA,EAAoB;AACvC,IAAA,KAAA,MAAW,MAAM,KAAA,EAAO;AAAE,MAAA,MAAM,IAAI,EAAA,CAAG,GAAA,EAAI,CAAE,IAAA,GAAO,WAAA,EAAY;AAAG,MAAA,IAAI,CAAA,SAAU,GAAA,CAAI,CAAA,EAAA,CAAI,OAAO,GAAA,CAAI,CAAC,CAAA,IAAK,CAAA,IAAK,CAAC,CAAA;AAAA,IAAG;AACnH,IAAA,IAAI,GAAA,GAAM,KAAA;AACV,IAAA,KAAA,MAAW,MAAM,KAAA,EAAO;AACtB,MAAA,MAAM,IAAI,EAAA,CAAG,GAAA,EAAI,CAAE,IAAA,GAAO,WAAA,EAAY;AACtC,MAAA,MAAM,GAAA,GAAM,CAAC,CAAC,CAAA,IAAA,CAAM,OAAO,GAAA,CAAI,CAAC,KAAK,CAAA,IAAK,CAAA;AAC1C,MAAA,EAAA,CAAG,KAAA,CAAM,SAAA,CAAU,MAAA,CAAO,SAAA,EAAW,GAAG,CAAA;AAGxC,MAAA,IAAI,GAAA,EAAK;AAAE,QAAA,EAAA,CAAG,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,GAAI,WAAA;AAAa,QAAA,GAAA,GAAM,IAAA;AAAA,MAAM,CAAA,MAAA,IACrD,EAAA,CAAG,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA,KAAM,WAAA,EAAa,OAAO,EAAA,CAAG,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA;AAAA,IACjF;AACA,IAAA,OAAO,GAAA;AAAA,EACT,CAAA;AACA,EAAA,OAAO;AAAA,IACL,KAAA,GAAQ;AAAE,MAAA,KAAA,CAAM,MAAA,GAAS,CAAA;AAAA,IAAG,CAAA;AAAA,IAC5B,MAAM,KAAA,EAAO,GAAA,GAAM,MAAM,KAAA,CAAM,OAAO,IAAA,EAAM;AAC1C,MAAA,KAAA,CAAM,IAAA,CAAK,EAAE,KAAA,EAAO,GAAA,EAAK,CAAA;AACzB,MAAA,KAAA,CAAM,gBAAA,CAAiB,SAAS,KAAK,CAAA;AACrC,MAAA,KAAA,MAAW,KAAK,IAAA,IAAQ,IAAI,CAAA,CAAE,gBAAA,CAAiB,SAAS,KAAK,CAAA;AAAA,IAC/D,CAAA;AAAA,IACA,KAAA;AAAA,IACA,cAAA,GAAiB;AAAE,MAAA,KAAA,EAAM;AAAG,MAAA,OAAO,KAAA,CAAM,IAAA,CAAK,CAAC,EAAA,KAAO,EAAA,CAAG,KAAA,CAAM,SAAA,CAAU,QAAA,CAAS,SAAS,CAAC,CAAA,EAAG,KAAA,IAAS,IAAA;AAAA,IAAM;AAAA,GAChH;AACF;AAsDO,SAAS,oBAAoB,IAAA,EAA6C;AAC/E,EAAA,MAAM,MAAA,GAAS,EAAA,CAAG,QAAA,EAAU,iBAAiB,CAAA;AAC7C,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,MAAA,EAAQ,eAAe,CAAA;AAAG,EAAA,IAAA,CAAK,MAAA,GAAS,QAAA;AACxD,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,KAAA,EAAO,eAAe,CAAA;AACtC,EAAA,MAAM,MAAA,GAAS,EAAA,CAAG,KAAA,EAAO,iBAAiB,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,GAAA,EAAK,gBAAgB,CAAA;AAAG,EAAA,KAAA,CAAM,MAAA,GAAS,IAAA;AACxD,EAAA,MAAM,MAAA,GAAS,EAAA,CAAG,QAAA,EAAU,iBAAA,EAAmB,QAAQ,CAAA;AAAG,EAAA,MAAA,CAAO,IAAA,GAAO,QAAA;AAAU,EAAA,MAAA,CAAO,KAAA,GAAQ,QAAA;AACjG,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,QAAA,EAAU,uBAAA,EAAyB,MAAM,CAAA;AAAG,EAAA,IAAA,CAAK,KAAA,GAAQ,MAAA;AAEzE,EAAA,MAAM,QAAA,uBAAe,GAAA,EAAyB;AAC9C,EAAA,MAAM,MAAA,uBAAa,GAAA,EAA+B;AAClD,EAAA,MAAM,OAAA,uBAAc,GAAA,EAAmC;AACvD,EAAA,IAAI,MAAA,GAAS,IAAA,CAAK,QAAA,CAAS,CAAC,GAAG,EAAA,IAAM,EAAA;AAErC,EAAA,IAAI,SAAA;AACJ,EAAA,KAAA,MAAW,CAAA,IAAK,KAAK,QAAA,EAAU;AAC7B,IAAA,IAAI,CAAA,CAAE,KAAA,IAAS,CAAA,CAAE,KAAA,KAAU,SAAA,EAAW;AAAE,MAAA,IAAA,CAAK,OAAO,EAAA,CAAG,KAAA,EAAO,gBAAA,EAAkB,CAAA,CAAE,KAAK,CAAC,CAAA;AAAG,MAAA,SAAA,GAAY,CAAA,CAAE,KAAA;AAAA,IAAO;AAChH,IAAA,MAAM,GAAA,GAAM,EAAA,CAAG,QAAA,EAAU,cAAA,EAAgB,EAAE,KAAK,CAAA;AAAG,IAAA,GAAA,CAAI,IAAA,GAAO,QAAA;AAAU,IAAA,GAAA,CAAI,OAAA,CAAQ,KAAK,CAAA,GAAI,CAAA,CAAE,EAAA;AAI/F,IAAA,GAAA,CAAI,gBAAA,CAAiB,SAAS,MAAM;AAAE,MAAA,IAAI,CAAC,IAAI,SAAA,CAAU,QAAA,CAAS,aAAa,CAAA,EAAG,OAAA,CAAQ,EAAE,EAAE,CAAA;AAAA,IAAG,CAAC,CAAA;AAClG,IAAA,IAAA,CAAK,OAAO,GAAG,CAAA;AAAG,IAAA,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,EAAA,EAAI,GAAG,CAAA;AACtC,IAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,SAAA,EAAW,gBAAgB,CAAA;AAAG,IAAA,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAA,GAAI,CAAA,CAAE,EAAA;AAAI,IAAA,KAAA,CAAM,MAAA,GAAS,IAAA;AAC7F,IAAA,MAAA,CAAO,OAAO,KAAK,CAAA;AAAG,IAAA,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,EAAA,EAAI,KAAK,CAAA;AAAA,EAChD;AAEA,EAAA,SAAS,QAAQ,EAAA,EAAkB;AACjC,IAAA,MAAA,GAAS,EAAA;AACT,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,CAAC,CAAA,IAAK,MAAA,IAAU,SAAA,CAAU,MAAA,CAAO,QAAA,EAAU,GAAA,KAAQ,EAAE,CAAA;AACtE,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,CAAC,KAAK,QAAA,EAAU,CAAA,CAAE,SAAS,GAAA,KAAQ,EAAA;AAAA,EACtD;AAEA,EAAA,MAAM,MAAA,GAAS,CAAC,CAAA,KAAgC,CAAC,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA,EAAG,SAAA,CAAU,QAAA,CAAS,aAAa,CAAA;AACnG,EAAA,SAAS,WAAA,GAAoB;AAC3B,IAAA,KAAA,MAAW,CAAA,IAAK,KAAK,QAAA,EAAU;AAC7B,MAAA,MAAM,GAAA,GAAM,MAAA,CAAO,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA;AAC3B,MAAA,MAAM,MAAA,GAAS,CAAA,CAAE,QAAA,IAAW,IAAK,IAAA;AACjC,MAAA,GAAA,CAAI,SAAA,CAAU,MAAA,CAAO,aAAA,EAAe,MAAA,KAAW,IAAI,CAAA;AACnD,MAAA,IAAI,WAAW,IAAA,EAAM;AAAE,QAAA,GAAA,CAAI,OAAA,CAAQ,KAAK,CAAA,GAAI,MAAA;AAAQ,QAAA,GAAA,CAAI,YAAA,CAAa,iBAAiB,MAAM,CAAA;AAAA,MAAG,CAAA,MAC1F;AAAE,QAAA,OAAO,GAAA,CAAI,QAAQ,KAAK,CAAA;AAAG,QAAA,GAAA,CAAI,gBAAgB,eAAe,CAAA;AAAA,MAAG;AAAA,IAC1E;AACA,IAAA,IAAI,CAAC,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,MAAA,IAAU,MAAA,CAAO,CAAC,CAAC,CAAA,EAAG;AAC5D,MAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,QAAA,CAAS,IAAA,CAAK,MAAM,CAAA;AACvC,MAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,KAAA,CAAM,EAAE,CAAA;AAAA,IAC7B;AAAA,EACF;AAEA,EAAA,IAAA,CAAK,MAAA;AAAA,IACH,EAAA,CAAG,IAAA,EAAM,gBAAA,EAAkB,IAAA,CAAK,KAAK,CAAA;AAAA,IACrC,EAAA,CAAG,KAAA,EAAO,eAAA,EAAiB,IAAA,EAAM,MAAM,CAAA;AAAA,IACvC,EAAA,CAAG,KAAA,EAAO,kBAAA,EAAoB,KAAA,EAAO,QAAQ,IAAI;AAAA,GACnD;AACA,EAAA,MAAA,CAAO,OAAO,IAAI,CAAA;AAClB,EAAA,QAAA,CAAS,IAAA,CAAK,OAAO,MAAM,CAAA;AAG3B,EAAA,IAAA,CAAK,gBAAA,CAAiB,SAAA,EAAW,CAAC,CAAA,KAAM;AACtC,IAAA,IAAI,CAAA,CAAE,QAAQ,OAAA,IAAY,CAAA,CAAE,QAAwB,OAAA,KAAY,OAAA,IAAW,cAAA,EAAe;AAAA,EAC5F,CAAC,CAAA;AAED,EAAA,MAAA,CAAO,iBAAiB,OAAA,EAAS,MAAM,MAAA,CAAO,KAAA,CAAM,QAAQ,CAAC,CAAA;AAC7D,EAAA,IAAA,CAAK,gBAAA,CAAiB,OAAA,EAAS,CAAC,CAAA,KAAM;AACpC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,KAAA,CAAM,MAAA,GAAS,IAAA;AACf,IAAA,KAAA,MAAW,CAAA,IAAK,KAAK,QAAA,EAAU;AAC7B,MAAA,MAAM,QAAQ,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAE,EAAE,GAAG,YAAA,IAAe;AAChD,MAAA,IAAI,CAAC,KAAA,EAAO;AACZ,MAAA,MAAM,GAAA,GAAM,KAAA,YAAiB,WAAA,GAAc,KAAA,GAAQ,KAAA,CAAM,EAAA;AACzD,MAAA,MAAM,OAAA,GAAA,CAAW,iBAAiB,WAAA,GAAc,MAAA,GAAY,MAAM,OAAA,KAAY,GAAA,CAAI,QAAQ,KAAK,CAAA;AAC/F,MAAA,OAAA,CAAQ,EAAE,EAAE,CAAA;AACZ,MAAA,GAAA,CAAI,KAAA,EAAM;AAGV,MAAA,KAAA,CAAM,cAAc,OAAA,IAAW,mCAAA;AAC/B,MAAA,KAAA,CAAM,MAAA,GAAS,KAAA;AACf,MAAA;AAAA,IACF;AAKA,IAAA,IAAA,CAAK,QAAA,GAAW,IAAA;AAChB,IAAA,KAAA,CAAM,YAAY;AAChB,MAAA,IAAI,KAAA;AACJ,MAAA,IAAI;AAAE,QAAA,KAAA,GAAQ,MAAM,KAAK,MAAA,EAAO;AAAA,MAAG,SAAS,GAAA,EAAK;AAAE,QAAA,KAAA,GAAQ,KAAA;AAAO,QAAA,OAAA,CAAQ,KAAA,CAAM,yBAAyB,GAAG,CAAA;AAAA,MAAG;AAC/G,MAAA,IAAA,CAAK,QAAA,GAAW,KAAA;AAChB,MAAA,IAAI,KAAA,KAAU,KAAA,EAAO,MAAA,CAAO,KAAA,CAAM,MAAM,CAAA;AAAA,IAC1C,CAAA,GAAG;AAAA,EACL,CAAC,CAAA;AAED,EAAA,OAAO;AAAA,IACL,KAAK,KAAA,EAAO;AACV,MAAA,OAAA,CAAQ,KAAA,EAAM;AACd,MAAA,KAAA,MAAW,CAAA,IAAK,KAAK,QAAA,EAAU;AAC7B,QAAA,MAAM,KAAA,GAAQ,QAAA,CAAS,GAAA,CAAI,CAAA,CAAE,EAAE,CAAA;AAC/B,QAAA,KAAA,CAAM,eAAA,EAAgB;AACtB,QAAA,OAAA,CAAQ,IAAI,CAAA,CAAE,EAAA,EAAI,CAAA,CAAE,KAAA,CAAM,KAAK,CAAC,CAAA;AAAA,MAClC;AACA,MAAA,KAAA,CAAM,MAAA,GAAS,IAAA;AACf,MAAA,OAAA,CAAQ,SAAS,MAAA,IAAU,IAAA,CAAK,SAAS,CAAC,CAAA,EAAG,MAAM,EAAE,CAAA;AACrD,MAAA,WAAA,EAAY;AACZ,MAAA,MAAA,CAAO,SAAA,EAAU;AAAA,IACnB,CAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAA,GAAU;AAAE,MAAA,MAAA,CAAO,MAAA,EAAO;AAAA,IAAG;AAAA,GAC/B;AACF;;;AC/QO,IAAM,0BAA6C,CAAC,MAAA,EAAQ,OAAA,EAAS,KAAA,EAAO,MAAM,KAAK;AAGvF,IAAM,kBAAA,GACX;AAeK,SAAS,gBAAgB,IAAA,EAAsB;AACpD,EAAA,MAAM,OAAA,GAAU,KAAK,IAAA,EAAK;AAG1B,EAAA,MAAM,iBAAA,GAAoB,OAAA,CAAQ,UAAA,CAAW,GAAG,CAAA;AAChD,EAAA,IAAI,MAAM,OAAA,CAAQ,WAAA,GAAc,OAAA,CAAQ,OAAA,EAAS,EAAE,CAAA,CAChD,OAAA,CAAQ,cAAA,EAAgB,GAAG,EAAE,OAAA,CAAQ,KAAA,EAAO,GAAG,CAAA,CAAE,OAAA,CAAQ,YAAY,EAAE,CAAA;AAC1E,EAAA,IAAI,GAAA,KAAQ,IAAI,OAAO,EAAA;AACvB,EAAA,IAAI,qBAAqB,QAAA,CAAS,IAAA,CAAK,GAAG,CAAA,EAAG,GAAA,GAAM,IAAI,GAAG,CAAA,CAAA;AAC1D,EAAA,IAAI,wBAAwB,QAAA,CAAS,GAAG,CAAA,EAAG,GAAA,GAAM,GAAG,GAAG,CAAA,CAAA,CAAA;AACvD,EAAA,OAAO,GAAA;AACT;AASO,SAAS,oBAAoB,IAAA,EAAuB;AACzD,EAAA,OAAO,qBAAqB,IAAA,CAAK,IAAI,KAAK,CAAC,uBAAA,CAAwB,SAAS,IAAI,CAAA;AAClF;AAWO,SAAS,uBAAuB,IAAA,EAAuB;AAC5D,EAAA,OAAO,CAAC,mBAAA,CAAoB,IAAI,KAAK,mBAAA,CAAoB,IAAA,CAAK,aAAa,CAAA;AAC7E;;;ACrEA,IAAM,QAAA,uBAAe,OAAA,EAAsC;AAUpD,SAAS,oBAAoB,IAAA,EAAkC;AACpE,EAAA,IAAI,mBAAA,CAAoB,IAAI,CAAA,EAAG,OAAO,MAAA;AACtC,EAAA,MAAM,UAAA,GAAa,gBAAgB,IAAI,CAAA;AACvC,EAAA,MAAM,IAAA,GAAO,UAAA,GAAa,CAAA,MAAA,EAAS,UAAU,CAAA,gBAAA,CAAA,GAAqB,EAAA;AAClE,EAAA,IAAI,uBAAA,CAAwB,QAAA,CAAS,IAAA,CAAK,WAAA,EAAa,CAAA,EAAG;AACxD,IAAA,OAAO,CAAA,gBAAA,EAAmB,IAAA,CAAK,WAAA,EAAa,iCAAiC,IAAI,CAAA,CAAA;AAAA,EACnF;AAEA,EAAA,IAAI,KAAK,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,iEAAiE,IAAI,CAAA,CAAA;AACpG,EAAA,IAAI,SAAS,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,kDAAkD,IAAI,CAAA,CAAA;AACtF,EAAA,IAAI,IAAA,KAAS,IAAA,CAAK,WAAA,EAAY,EAAG;AAC/B,IAAA,OAAO,CAAA,+DAAA,EAAkE,IAAA,CAAK,WAAA,EAAa,KAAK,IAAI,CAAA,CAAA;AAAA,EACtG;AACA,EAAA,OAAO,mEAAmE,IAAI,CAAA,CAAA;AAChF;AAaO,SAAS,gBAAA,CACd,KAAA,EACA,QAAA,EACA,IAAA,GAA0B,EAAC,EACrB;AACN,EAAA,MAAM,OAAO,IAAA,CAAK,IAAA;AAClB,EAAA,MAAM,OAAO,MAAY;AACvB,IAAA,MAAM,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,IAAA,EAAK;AAC3B,IAAA,MAAM,OAAA,GAAU,CAAA,KAAM,EAAA,GAAK,MAAA,GAAY,oBAAoB,CAAC,CAAA;AAC5D,IAAA,KAAA,CAAM,SAAA,CAAU,MAAA,CAAO,SAAA,EAAW,CAAC,CAAC,OAAO,CAAA;AAC3C,IAAA,IAAI,OAAA,QAAe,KAAA,GAAQ,OAAA;AAAA,SAAA,IAClB,KAAA,CAAM,KAAA,CAAM,UAAA,CAAW,eAAe,CAAA,EAAG;AAChD,MAAA,IAAI,IAAA,QAAY,KAAA,GAAQ,IAAA;AAAA,WACnB,KAAA,CAAM,gBAAgB,OAAO,CAAA;AAAA,IACpC;AAAA,EACF,CAAA;AACA,EAAA,KAAA,CAAM,gBAAA,CAAiB,SAAS,MAAM;AAAE,IAAA,QAAA,CAAS,MAAM,KAAK,CAAA;AAAG,IAAA,IAAA,EAAK;AAAA,EAAG,CAAC,CAAA;AACxE,EAAA,KAAA,CAAM,gBAAA,CAAiB,SAAA,EAAW,CAAC,CAAA,KAAM;AAGvC,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,KAAA,IAAS,CAAA,CAAE,QAAA,EAAU;AACnC,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,IAAA,EAAK;AAC/B,IAAA,IAAI,KAAA,KAAU,EAAA,IAAM,mBAAA,CAAoB,KAAK,CAAA,EAAG;AAChD,IAAA,MAAM,OAAA,GAAU,gBAAgB,KAAK,CAAA;AACrC,IAAA,IAAI,CAAC,OAAA,IAAW,OAAA,KAAY,KAAA,CAAM,KAAA,EAAO;AACzC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,KAAA,CAAM,KAAA,GAAQ,OAAA;AACd,IAAA,QAAA,CAAS,OAAO,CAAA;AAChB,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,IAAA,EAAK;AACP;AAOO,SAAS,yBAAyB,IAAA,EAA2C;AAClF,EAAA,OAAO,IAAA,CAAK,cAAgC,eAAe,CAAA;AAC7D;AAkCA,SAAS,OAAA,CAAQ,MAAc,KAAA,EAAqC;AAClE,EAAA,MAAM,CAAA,GAAI,KAAK,WAAA,EAAY;AAC3B,EAAA,MAAM,WAAW,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,EAAE,WAAA,EAAY,CAAE,UAAA,CAAW,CAAC,KAAK,CAAA,CAAE,UAAA,CAAW,CAAA,CAAE,WAAA,EAAa,CAAC,CAAA;AACnG,EAAA,IAAI,QAAA,CAAS,MAAA,KAAW,CAAA,EAAG,OAAO,SAAS,CAAC,CAAA;AAC5C,EAAA,IAAI,IAAA;AACJ,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,MAAM,CAAA,GAAI,EAAE,WAAA,EAAY;AACxB,IAAA,IAAI,KAAK,GAAA,CAAI,CAAA,CAAE,SAAS,CAAA,CAAE,MAAM,IAAI,CAAA,EAAG;AAEvC,IAAA,IAAI,IAAA,GAAO,KAAA,CAAM,IAAA,CAAK,EAAE,MAAA,EAAQ,CAAA,CAAE,MAAA,GAAS,CAAA,EAAE,EAAG,CAAC,CAAA,EAAG,CAAA,KAAM,CAAC,CAAA;AAC3D,IAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AAClC,MAAA,MAAM,GAAA,GAAM,CAAC,CAAC,CAAA;AACd,MAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,IAAK,CAAA,CAAE,QAAQ,CAAA,EAAA,EAAK;AAClC,QAAA,GAAA,CAAI,CAAC,CAAA,GAAI,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,CAAC,CAAA,GAAK,CAAA,EAAG,GAAA,CAAI,CAAA,GAAI,CAAC,CAAA,GAAK,CAAA,EAAG,KAAK,CAAA,GAAI,CAAC,CAAA,IAAM,CAAA,CAAE,CAAA,GAAI,CAAC,CAAA,KAAM,CAAA,CAAE,CAAA,GAAI,CAAC,CAAA,GAAI,CAAA,GAAI,CAAA,CAAE,CAAA;AAAA,MACjG;AACA,MAAA,IAAA,GAAO,GAAA;AAAA,IACT;AACA,IAAA,MAAM,CAAA,GAAI,IAAA,CAAK,CAAA,CAAE,MAAM,CAAA;AACvB,IAAA,IAAI,IAAI,SAAA,EAAW;AAAE,MAAA,SAAA,GAAY,CAAA;AAAG,MAAA,IAAA,GAAO,CAAA;AAAA,IAAG;AAAA,EAChD;AACA,EAAA,OAAO,IAAA;AACT;AAGO,SAAS,kBAAA,CAAmB,MAAc,IAAA,EAA8C;AAC7F,EAAA,MAAM,QAAQ,IAAA,CAAK,KAAA,GAAQ,CAAA,CAAA,EAAI,IAAA,CAAK,KAAK,CAAA,CAAA,GAAK,YAAA;AAE9C,EAAA,MAAM,MAAA,GAAS,KAAK,WAAA,EAAY;AAChC,EAAA,IAAI,CAAC,mBAAA,CAAoB,MAAM,CAAA,EAAG;AAChC,IAAA,MAAM,UAAA,GAAa,gBAAgB,IAAI,CAAA;AACvC,IAAA,MAAM,IAAA,GAAO,UAAA,GAAa,CAAA,MAAA,EAAS,UAAU,CAAA,gBAAA,CAAA,GAAqB,EAAA;AAClE,IAAA,IAAI,uBAAA,CAAwB,SAAS,MAAM,CAAA,SAAU,CAAA,gBAAA,EAAmB,MAAM,iCAAiC,IAAI,CAAA,CAAA;AACnH,IAAA,IAAI,KAAK,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,iEAAiE,IAAI,CAAA,CAAA;AACpG,IAAA,IAAI,SAAS,IAAA,CAAK,IAAI,CAAA,EAAG,OAAO,kDAAkD,IAAI,CAAA,CAAA;AACtF,IAAA,OAAO,mEAAmE,IAAI,CAAA,CAAA;AAAA,EAChF;AACA,EAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,EAAM;AACzB,EAAA,IAAI,KAAA,CAAM,KAAK,CAAC,CAAA,KAAM,EAAE,WAAA,EAAY,KAAM,MAAM,CAAA,EAAG,OAAO,MAAA;AAC1D,EAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,MAAA,EAAQ,KAAK,CAAA;AAClC,EAAA,IAAI,MAAM,OAAO,CAAA,GAAA,EAAM,KAAK,CAAA,qBAAA,EAAwB,IAAI,oBAAoB,IAAI,CAAA,EAAA,CAAA;AAChF,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,MAAM,KAAK,CAAA,sDAAA,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,CAAC,CAAA,CAAE,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAA,EAAI,CAAC,CAAA,CAAA,CAAG,CAAA,CAAE,KAAK,IAAI,CAAA;AAC9D,EAAA,MAAM,IAAA,GAAO,MAAM,MAAA,GAAS,CAAA,GAAI,SAAS,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA,KAAA,CAAA,GAAU,EAAA;AACnE,EAAA,OAAO,MAAM,KAAK,CAAA,qBAAA,EAAwB,IAAI,CAAA,eAAA,EAAkB,KAAK,GAAG,IAAI,CAAA,CAAA,CAAA;AAC9E;AAWO,SAAS,eAAA,CACd,KAAA,EACA,QAAA,EACA,IAAA,EACM;AACN,EAAA,MAAM,IAAA,GAAO,QAAA,CAAS,aAAA,CAAc,UAAU,CAAA;AAC9C,EAAA,IAAA,CAAK,EAAA,GAAK,CAAA,cAAA,EAAiB,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AACjE,EAAA,KAAA,CAAM,YAAA,CAAa,MAAA,EAAQ,IAAA,CAAK,EAAE,CAAA;AAOlC,EAAA,MAAM,SAAS,MAAY;AACzB,IAAA,IAAI,CAAC,IAAA,CAAK,WAAA,IAAe,MAAM,UAAA,EAAY,KAAA,CAAM,MAAM,IAAI,CAAA;AAAA,EAC7D,CAAA;AAEA,EAAA,MAAM,OAAO,MAAY;AACvB,IAAA,MAAA,EAAO;AACP,IAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,EAAM;AACzB,IAAA,IAAA,CAAK,eAAA,CAAgB,GAAG,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM;AAAE,MAAA,MAAM,CAAA,GAAI,QAAA,CAAS,aAAA,CAAc,QAAQ,CAAA;AAAG,MAAA,CAAA,CAAE,KAAA,GAAQ,CAAA;AAAG,MAAA,OAAO,CAAA;AAAA,IAAG,CAAC,CAAC,CAAA;AAChH,IAAA,MAAM,CAAA,GAAI,KAAA,CAAM,KAAA,CAAM,IAAA,EAAK;AAC3B,IAAA,MAAM,UAAU,CAAA,KAAM,EAAA,GAAK,MAAA,GAAY,kBAAA,CAAmB,GAAG,IAAI,CAAA;AACjE,IAAA,KAAA,CAAM,SAAA,CAAU,MAAA,CAAO,SAAA,EAAW,CAAC,CAAC,OAAO,CAAA;AAC3C,IAAA,IAAI,OAAA,QAAe,KAAA,GAAQ,OAAA;AAAA,SAAA,IAClB,KAAA,CAAM,MAAM,UAAA,CAAW,eAAe,KAAK,KAAA,CAAM,KAAA,CAAM,UAAA,CAAW,KAAK,CAAA,EAAG;AACjF,MAAA,IAAI,IAAA,CAAK,IAAA,EAAM,KAAA,CAAM,KAAA,GAAQ,IAAA,CAAK,IAAA;AAAA,WAC7B,KAAA,CAAM,gBAAgB,OAAO,CAAA;AAAA,IACpC;AAAA,EACF,CAAA;AACA,EAAA,QAAA,CAAS,GAAA,CAAI,OAAO,IAAI,CAAA;AAExB,EAAA,KAAA,CAAM,gBAAA,CAAiB,SAAS,MAAM;AAAE,IAAA,QAAA,CAAS,MAAM,KAAK,CAAA;AAAG,IAAA,IAAA,EAAK;AAAA,EAAG,CAAC,CAAA;AACxE,EAAA,KAAA,CAAM,gBAAA,CAAiB,SAAA,EAAW,CAAC,CAAA,KAAM;AACvC,IAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,KAAA,IAAS,CAAA,CAAE,QAAA,EAAU;AACnC,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,IAAA,EAAK;AAG/B,IAAA,IAAI,UAAU,EAAA,IAAM,mBAAA,CAAoB,KAAA,CAAM,WAAA,EAAa,CAAA,EAAG;AAC9D,IAAA,MAAM,OAAA,GAAU,gBAAgB,KAAK,CAAA;AACrC,IAAA,IAAI,CAAC,OAAA,IAAW,OAAA,KAAY,KAAA,CAAM,KAAA,EAAO;AACzC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,KAAA,CAAM,KAAA,GAAQ,OAAA;AACd,IAAA,QAAA,CAAS,OAAO,CAAA;AAChB,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,IAAA,EAAK;AACP;AAQO,SAAS,uBAAuB,IAAA,EAAwB;AAC7D,EAAA,KAAA,MAAW,KAAA,IAAS,KAAK,gBAAA,CAAiB,OAAO,GAAG,QAAA,CAAS,GAAA,CAAI,KAAK,CAAA,IAAI;AAC5E;;;AChNO,IAAM,cAAA,GAAqE;AAAA,EAChF,CAAC,UAAU,QAAQ,CAAA;AAAA,EAAG,CAAC,WAAW,cAAc,CAAA;AAAA,EAAG,CAAC,UAAU,MAAM,CAAA;AAAA,EACpE,CAAC,QAAQ,MAAM,CAAA;AAAA,EAAG,CAAC,SAAS,OAAO,CAAA;AAAA,EAAG,CAAC,WAAW,SAAS;AAC7D;AAWA,IAAM,MAAA,GAAS,CAAC,KAAA,EAAe,KAAA,EAAe,QAAA,KAAyC;AACrF,EAAA,MAAM,CAAA,GAAI,EAAA,CAAG,QAAA,EAAU,MAAA,EAAW,KAAK,CAAA;AACvC,EAAA,CAAA,CAAE,KAAA,GAAQ,KAAA;AAAO,EAAA,CAAA,CAAE,QAAA,GAAW,QAAA;AAC9B,EAAA,OAAO,CAAA;AACT,CAAA;AAEA,IAAM,IAAA,GAAO,QAAA;AAQN,SAAS,cAAA,CAAe,CAAA,EAAqB,QAAA,EAAuB,IAAA,GAA8B,EAAC,EAAgB;AACxH,EAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,KAAU,IAAA;AAC7B,EAAA,MAAM,KAAA,GAAQ,CAAC,GAAA,KAAsB;AACnC,IAAA,IAAI,QAAQ,EAAA,EAAI;AAAE,MAAA,IAAI,KAAA,SAAc,CAAA,CAAE,OAAA;AAAA,aAAgB,OAAA,GAAU,EAAA;AAAA,IAAI,WAC3D,KAAA,IAAS,CAAA,CAAE,SAAS,SAAA,EAAW,CAAA,CAAE,UAAU,GAAA,KAAQ,MAAA;AAAA,SAAA,IACnD,SAAS,CAAA,CAAE,IAAA,KAAS,UAAU,CAAA,CAAE,OAAA,GAAU,OAAO,GAAG,CAAA;AAAA,WACtD,OAAA,GAAU,GAAA;AACjB,IAAA,QAAA,IAAW;AAAA,EACb,CAAA;AACA,EAAA,MAAM,OAAA,GAAU,CAAA,CAAE,OAAA,KAAY,MAAA,IAAa,CAAA,CAAE,YAAY,IAAA,GAAO,EAAA,GAAK,MAAA,CAAO,CAAA,CAAE,OAAO,CAAA;AACrF,EAAA,IAAI,CAAA,CAAE,SAAS,SAAA,EAAW;AACxB,IAAA,MAAM,GAAA,GAAM,EAAA,CAAG,QAAA,EAAU,mBAAmB,CAAA;AAC5C,IAAA,GAAA,CAAI,OAAO,MAAA,CAAO,EAAA,EAAI,MAAM,OAAA,KAAY,EAAE,GAAG,MAAA,CAAO,MAAA,EAAQ,MAAA,EAAQ,OAAA,KAAY,MAAM,CAAA,EAAG,MAAA,CAAO,SAAS,OAAA,EAAS,OAAA,KAAY,OAAO,CAAC,CAAA;AACtI,IAAA,GAAA,CAAI,iBAAiB,QAAA,EAAU,MAAM,KAAA,CAAM,GAAA,CAAI,KAAK,CAAC,CAAA;AACrD,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,IAAI,CAAA,CAAE,SAAS,MAAA,EAAQ;AACrB,IAAA,MAAM,GAAA,GAAM,EAAA,CAAG,QAAA,EAAU,mBAAmB,CAAA;AAC5C,IAAA,GAAA,CAAI,OAAO,MAAA,CAAO,EAAA,EAAI,IAAA,EAAM,OAAA,KAAY,EAAE,CAAC,CAAA;AAC3C,IAAA,KAAA,MAAW,CAAA,IAAK,CAAA,CAAE,MAAA,IAAU,EAAC,EAAG,GAAA,CAAI,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,EAAG,OAAA,KAAY,CAAC,CAAC,CAAA;AACtE,IAAA,GAAA,CAAI,iBAAiB,QAAA,EAAU,MAAM,KAAA,CAAM,GAAA,CAAI,KAAK,CAAC,CAAA;AACrD,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,IAAI,CAAA,CAAE,SAAS,SAAA,EAAW;AAGxB,IAAA,MAAM,GAAA,GAAM,EAAA,CAAG,QAAA,EAAU,mBAAmB,CAAA;AAC5C,IAAA,GAAA,CAAI,OAAO,MAAA,CAAO,EAAA,EAAI,eAAA,EAAiB,OAAA,KAAY,EAAE,CAAC,CAAA;AACtD,IAAA,KAAA,MAAW,CAAA,IAAK,CAAA,CAAE,MAAA,IAAU,EAAC,EAAG,GAAA,CAAI,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,CAAA,EAAG,OAAA,KAAY,CAAC,CAAC,CAAA;AACtE,IAAA,GAAA,CAAI,iBAAiB,QAAA,EAAU,MAAM,KAAA,CAAM,GAAA,CAAI,KAAK,CAAC,CAAA;AACrD,IAAA,OAAO,GAAA;AAAA,EACT;AACA,EAAA,IAAI,CAAA,CAAE,SAAS,OAAA,EAAS;AAEtB,IAAA,MAAM,CAAA,GAAI,EAAA,CAAG,MAAA,EAAQ,cAAA,EAAgB,cAAc,CAAA;AACnD,IAAA,CAAA,CAAE,OAAA,CAAQ,KAAK,CAAA,GAAI,wCAAA;AACnB,IAAA,OAAO,CAAA;AAAA,EACT;AACA,EAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,OAAA,EAAS,mBAAmB,CAAA;AAC7C,EAAA,KAAA,CAAM,IAAA,GAAO,CAAA,CAAE,IAAA,KAAS,QAAA,GAAW,QAAA,GAAW,MAAA;AAC9C,EAAA,KAAA,CAAM,WAAA,GAAc,SAAA;AACpB,EAAA,KAAA,CAAM,KAAA,GAAQ,OAAA;AACd,EAAA,KAAA,CAAM,iBAAiB,OAAA,EAAS,MAAM,KAAA,CAAM,KAAA,CAAM,KAAK,CAAC,CAAA;AACxD,EAAA,OAAO,KAAA;AACT;AAgDO,SAAS,iBAAA,CAA8C,IAAA,EAAmB,KAAA,EAAY,IAAA,GAA+B,EAAC,EAAuB;AAClJ,EAAA,MAAM,OAAA,GAAU,MAAY,IAAA,CAAK,QAAA,IAAW;AAC5C,EAAA,MAAM,QAAQ,QAAA,EAAS;AACvB,EAAA,MAAM,KAAA,GAAQ,KAAK,KAAA,IAAS,cAAA;AAC5B,EAAA,MAAM,IAAA,GAAO,EAAA,CAAG,KAAA,EAAO,UAAU,CAAA;AACjC,EAAA,MAAM,IAAA,GAAO,CAAC,KAAA,KAAA,CACX,KAAA,IAAS,IAAI,MAAA,CAAO,CAAC,CAAA,KAAwB,CAAA,KAAM,IAAI,CAAA;AAE1D,EAAA,MAAM,GAAA,GAAM,CAAC,CAAA,EAAM,CAAA,KAA2B;AAC5C,IAAA,MAAM,IAAA,GAAO,EAAA,CAAG,OAAA,EAAS,gBAAgB,CAAA;AACzC,IAAA,IAAA,CAAK,IAAA,GAAO,MAAA;AAAQ,IAAA,IAAA,CAAK,WAAA,GAAc,eAAA;AAAiB,IAAA,IAAA,CAAK,QAAQ,CAAA,CAAE,IAAA;AAAM,IAAA,IAAA,CAAK,UAAA,GAAa,KAAA;AAC/F,IAAA,IAAA,CAAK,OAAA,CAAQ,KAAK,CAAA,GAAI,kBAAA;AACtB,IAAA,gBAAA,CAAiB,IAAA,EAAM,CAAC,CAAA,KAAM;AAAE,MAAA,CAAA,CAAE,IAAA,GAAO,CAAA;AAAG,MAAA,OAAA,EAAQ;AAAA,IAAG,CAAA,EAAG,EAAE,IAAA,EAAM,kBAAA,EAAoB,CAAA;AACtF,IAAA,KAAA,CAAM,MAAM,IAAI,CAAA;AAEhB,IAAA,MAAM,IAAA,GAAO,EAAA,CAAG,QAAA,EAAU,gBAAgB,CAAA;AAC1C,IAAA,KAAA,MAAW,CAAC,CAAA,EAAG,KAAK,CAAA,IAAK,KAAA,EAAO,IAAA,CAAK,MAAA,CAAO,MAAA,CAAO,CAAA,EAAG,KAAA,EAAO,CAAA,KAAM,CAAA,CAAE,IAAI,CAAC,CAAA;AAC1E,IAAA,IAAA,CAAK,gBAAA,CAAiB,UAAU,MAAM;AACpC,MAAA,CAAA,CAAE,OAAO,IAAA,CAAK,KAAA;AACd,MAAA,IAAI,IAAA,CAAK,KAAA,EAAO,OAAO,CAAA,CAAE,OAAA;AAAA,aAAgB,OAAA,GAAU,EAAA;AACnD,MAAA,IAAI,CAAA,CAAE,SAAS,MAAA,IAAU,CAAA,CAAE,SAAS,OAAA,EAAS,CAAA,CAAE,WAAW,EAAC;AAAA,kBAAe,CAAA,CAAE,MAAA;AAC5E,MAAA,IAAI,CAAA,CAAE,IAAA,KAAS,SAAA,EAAW,CAAA,CAAE,WAAW,EAAC;AAAA,kBAAe,CAAA,CAAE,MAAA;AACzD,MAAA,MAAA,EAAO;AAAG,MAAA,OAAA,EAAQ;AAAA,IACpB,CAAC,CAAA;AAID,IAAA,IAAI,GAAA,GAAM,cAAA,CAAe,CAAA,EAAG,OAAA,EAAS,IAAI,CAAA;AACzC,IAAA,MAAM,iBAAiB,MAAY;AAAE,MAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,CAAA,EAAG,OAAA,EAAS,IAAI,CAAA;AAAG,MAAA,GAAA,CAAI,YAAY,KAAK,CAAA;AAAG,MAAA,GAAA,GAAM,KAAA;AAAO,MAAA,OAAA,EAAQ;AAAA,IAAG,CAAA;AAErI,IAAA,MAAM,EAAA,GAAK,OAAA,CAAQ,IAAA,EAAM,SAAA,EAAW,MAAM;AAAE,MAAA,IAAI,QAAA,CAAS,KAAA,EAAO,CAAA,EAAG,EAAE,CAAA,EAAG;AAAE,QAAA,MAAA,EAAO;AAAG,QAAA,OAAA,EAAQ;AAAA,MAAG;AAAA,IAAE,CAAA,EAAG,MAAM,CAAC,CAAA;AAC3G,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,MAAA,EAAQ,WAAA,EAAa,MAAM;AAAE,MAAA,IAAI,QAAA,CAAS,KAAA,EAAO,CAAA,EAAG,CAAC,CAAA,EAAG;AAAE,QAAA,MAAA,EAAO;AAAG,QAAA,OAAA,EAAQ;AAAA,MAAG;AAAA,IAAE,CAAA,EAAG,CAAA,KAAM,KAAA,CAAM,MAAA,GAAS,CAAC,CAAA;AAC/H,IAAA,MAAM,GAAA,GAAM,OAAA,CAAQ,OAAA,EAAS,iBAAA,EAAmB,MAAM;AAAE,MAAA,KAAA,CAAM,MAAA,CAAO,GAAG,CAAC,CAAA;AAAG,MAAA,MAAA,EAAO;AAAG,MAAA,OAAA,EAAQ;AAAA,IAAG,CAAA,EAAG,OAAO,IAAI,CAAA;AAE/G,IAAA,MAAM,UAAyB,IAAA,CAAK,IAAA,CAAK,YAAA,GAAe,CAAA,EAAG,CAAC,CAAC,CAAA;AAC7D,IAAA,IAAI,CAAA,CAAE,IAAA,KAAS,MAAA,IAAU,CAAA,CAAE,SAAS,OAAA,EAAS,OAAA,CAAQ,IAAA,CAAK,QAAA,CAAS,QAAA,EAAU,QAAA,CAAS,CAAA,EAAG,cAAc,CAAC,CAAC,CAAA;AAEzG,IAAA,IAAI,CAAA,CAAE,SAAS,SAAA,EAAW;AACxB,MAAA,MAAM,IAAA,GAAO,EAAA,CAAG,GAAA,EAAK,kBAAkB,CAAA;AACvC,MAAA,MAAM,MAAA,GAAS,UAAA,CAAW,CAAA,EAAG,cAAA,EAAgB;AAAA,QAC3C,QAAA,EAAU,CAAC,IAAA,EAAM,EAAA,KAAO;AACtB,UAAA,IAAI,CAAA,CAAE,OAAA,KAAY,IAAA,EAAM,CAAA,CAAE,OAAA,GAAU,EAAA;AACpC,UAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,CAAA,EAAG,OAAA,EAAS,IAAI,CAAA;AAAG,UAAA,GAAA,CAAI,YAAY,KAAK,CAAA;AAAG,UAAA,GAAA,GAAM,KAAA;AAC9E,UAAA,IAAI,KAAK,aAAA,EAAe,IAAA,CAAK,aAAA,CAAc,CAAA,EAAG,MAAM,EAAE,CAAA;AAAA,eAAQ,OAAA,EAAQ;AAAA,QACxE,CAAA;AAAA,QACA,SAAA,EAAW,MAAM,IAAA,CAAK,cAAA,GAAiB,GAAG,IAAI;AAAA,OAC/C,CAAA;AACD,MAAA,OAAA,CAAQ,IAAA,CAAK,QAAA,CAAS,mBAAA,EAAqB,MAAM,GAAG,IAAI,CAAA;AAAA,IAC1D;AACA,IAAA,IAAI,IAAA,CAAK,YAAY,KAAA,EAAO;AAC1B,MAAA,MAAM,OAAA,GAAU,EAAA,CAAG,OAAA,EAAS,mBAAmB,CAAA;AAC/C,MAAA,OAAA,CAAQ,IAAA,GAAO,MAAA;AAAQ,MAAA,OAAA,CAAQ,WAAA,GAAc,2BAAA;AAA6B,MAAA,OAAA,CAAQ,KAAA,GAAQ,EAAE,OAAA,IAAW,EAAA;AACvG,MAAA,OAAA,CAAQ,gBAAA,CAAiB,SAAS,MAAM;AACtC,QAAA,IAAI,QAAQ,KAAA,CAAM,IAAA,EAAK,EAAG,CAAA,CAAE,UAAU,OAAA,CAAQ,KAAA;AAAA,oBAAmB,CAAA,CAAE,OAAA;AACnE,QAAA,OAAA,EAAQ;AAAA,MACV,CAAC,CAAA;AACD,MAAA,OAAA,CAAQ,IAAA,CAAK,QAAA,CAAS,SAAA,EAAW,OAAO,CAAC,CAAA;AAAA,IAC3C;AAEA,IAAA,OAAO,aAAA,CAAc,EAAE,IAAA,EAAM,CAAC,MAAM,IAAA,EAAM,GAAA,EAAK,GAAG,IAAA,CAAK,IAAA,CAAK,SAAA,GAAY,GAAG,CAAC,CAAC,CAAA,EAAG,EAAA,EAAI,IAAA,EAAM,GAAG,GAAG,OAAA,EAAS,IAAA,EAAM,CAAA,CAAE,IAAA,EAAM,CAAA;AAAA,EACzH,CAAA;AAEA,EAAA,MAAM,SAAS,MAAY;AACzB,IAAA,KAAA,CAAM,KAAA,EAAM;AACZ,IAAA,IAAA,CAAK,eAAA,EAAgB;AACrB,IAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,EAAA,CAAG,GAAA,EAAK,OAAA,EAAS,IAAA,CAAK,SAAA,IAAa,oBAAoB,CAAC,CAAA;AAAA,SACvF,KAAA,CAAM,OAAA,CAAQ,CAAC,CAAA,EAAG,CAAA,KAAM,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,CAAA,EAAG,CAAC,CAAC,CAAC,CAAA;AACnD,IAAA,KAAA,CAAM,KAAA,EAAM;AAAA,EACd,CAAA;AACA,EAAA,MAAA,EAAO;AAEP,EAAA,MAAM,MAAM,EAAA,CAAG,QAAA,EAAU,aAAA,EAAe,IAAA,CAAK,YAAY,gBAAgB,CAAA;AACzE,EAAA,GAAA,CAAI,IAAA,GAAO,QAAA;AACX,EAAA,GAAA,CAAI,gBAAA,CAAiB,SAAS,MAAM;AAClC,IAAA,KAAA,CAAM,IAAA,CAAK,IAAA,CAAK,OAAA,GAAU,IAAA,CAAK,OAAA,KAAa,EAAE,IAAA,EAAM,EAAA,EAAI,IAAA,EAAM,KAAA,CAAM,CAAC,IAAI,CAAC,CAAA,IAAK,QAAA,EAAU,GAAI,IAAA,CAAK,KAAA,GAAQ,EAAC,GAAI,EAAE,OAAA,EAAS,EAAA,EAAG,EAAqB,CAAA;AAClJ,IAAA,MAAA,EAAO;AAAG,IAAA,OAAA,EAAQ;AAAG,IAAA,WAAA,CAAY,IAAI,CAAA;AAAA,EACvC,CAAC,CAAA;AACD,EAAA,IAAA,CAAK,MAAA,CAAO,MAAM,GAAG,CAAA;AAErB,EAAA,OAAO;AAAA,IACL,cAAA,EAAgB,MAAM,KAAA,CAAM,cAAA,EAAe;AAAA,IAC3C,gBAAA,EAAkB,MAAM,wBAAA,CAAyB,IAAI,CAAA;AAAA;AAAA,IAErD,cAAc,MAAM,KAAA,CAAM,cAAA,EAAe,IAAK,yBAAyB,IAAI,CAAA;AAAA,IAC3E;AAAA,GACF;AACF","file":"chunk-CRUILFAT.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The shared settings surface: a native <dialog> with a left-rail of grouped\n// tabs and one panel per section (mountSettingsDialog), plus the list-editing\n// core those sections lean on (expandableRow, focusNewRow, dupGuard). The app\n// supplies the sections; each holds its own state and reports the first invalid\n// control so the Save gate can block + jump. Framework here, content in the app.\n//\n// Lifted from Patterpad's Project Settings (settings-list.ts + the inlined\n// dialog logic in its renderer), with neutral class names. Styling ships as\n// settings.css.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\n\n// --- list-editing core --------------------------------------------------------\n\n/** A compact list row: a single `.set-rowline` (inline controls) plus, when\n * `details` are given, a disclosure that reveals a `.set-details` panel below,\n * so a list scans like a table with secondary fields tucked away. `name` is\n * what the row declares (a property's name, a field's), stamped on it as\n * `data-name` so `revealRow` can land on it. */\nexport function expandableRow(opts: { line: HTMLElement[]; details?: HTMLElement[]; name?: string }): HTMLElement {\n const row = el(\"div\", \"set-row\");\n if (opts.name !== undefined) row.dataset.name = opts.name;\n const line = el(\"div\", \"set-rowline\");\n if (opts.details && opts.details.length) {\n const details = el(\"div\", \"set-details\"); details.hidden = true;\n for (const d of opts.details) details.append(d);\n // The triangle is drawn by CSS off `.open` (a text glyph stays hairline-thin).\n const toggle = el(\"button\", \"set-expand\"); toggle.type = \"button\";\n // `data-tip`, not `title`: this package ships a themed tooltip and nothing\n // else in it raises an OS rollover. A lone `title` here would be the one\n // place a host's chrome broke character, and on the platform's own delay.\n toggle.dataset.tip = \"More\"; toggle.setAttribute(\"aria-label\", \"More\");\n toggle.setAttribute(\"aria-expanded\", \"false\");\n toggle.addEventListener(\"click\", () => {\n const open = details.hidden;\n details.hidden = !open;\n toggle.setAttribute(\"aria-expanded\", String(open));\n toggle.classList.toggle(\"open\", open);\n });\n line.append(toggle);\n opts.line.forEach((c) => line.append(c));\n row.append(line, details);\n } else {\n opts.line.forEach((c) => line.append(c));\n row.append(line);\n }\n return row;\n}\n\n/**\n * Land on one row of a list by the `name` it was built with: open its details\n * (an enum's values sit behind the disclosure, and they are usually what the\n * jump was for), bring it to the middle of its scroller, and light it for a\n * moment (`.set-row.landed` in settings.css). \"Go to definition\" in both apps\n * used to open the page a property is declared on and stop there, with the\n * declaration below the fold and nothing marking it (Storyletter, 2026-09-14).\n *\n * Returns false when no row carries the name, so a host whose page fills in\n * asynchronously can ask again on a later frame; asking again is the host's,\n * since only the host knows what opens on its own time. The row is matched by\n * reading the stamp rather than by an attribute selector, so a name needs no\n * escaping.\n */\nexport function revealRow(within: ParentNode, name: string): boolean {\n const row = [...within.querySelectorAll<HTMLElement>(\".set-row\")].find((r) => r.dataset.name === name);\n if (!row) return false;\n const toggle = row.querySelector<HTMLButtonElement>(\".set-rowline > .set-expand\");\n if (toggle && toggle.getAttribute(\"aria-expanded\") !== \"true\") toggle.click();\n const still = typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n row.scrollIntoView({ block: \"center\", behavior: still ? \"auto\" : \"smooth\" });\n // Restarted, so landing on the same row twice lights it twice.\n row.classList.remove(\"landed\");\n void row.offsetWidth;\n row.classList.add(\"landed\");\n row.addEventListener(\"animationend\", () => row.classList.remove(\"landed\"), { once: true });\n return true;\n}\n\n/**\n * `revealRow`, retried over the next frames until the row exists or the tries\n * run out: for a page that fills in on its own time after a navigation. Both\n * apps hand-rolled this as `landOn` with the same twelve tries\n * (ui-review-2026-09, finding 29). Resolves true when it landed, false when it\n * gave up, and giving up is quiet: a name no page shows as a row is not a fault.\n */\nexport function revealRowWhenReady(within: ParentNode, name: string, tries = 12): Promise<boolean> {\n return new Promise((resolve) => {\n const attempt = (left: number): void => {\n if (revealRow(within, name)) { resolve(true); return; }\n if (left <= 0) { resolve(false); return; }\n requestAnimationFrame(() => attempt(left - 1));\n };\n attempt(tries);\n });\n}\n\n/** After a \"+ Add\" re-renders a list, bring the new (last) row into view and\n * focus its name input (`.set-name`, else the first input). */\nexport function focusNewRow(listEl: HTMLElement | null | undefined): void {\n const last = listEl?.lastElementChild;\n if (!(last instanceof HTMLElement)) return;\n last.scrollIntoView({ block: \"nearest\" });\n const name = last.querySelector<HTMLInputElement>(\"input.set-name\") ?? last.querySelector<HTMLInputElement>(\"input\");\n name?.focus();\n}\n\nexport interface DupGuard {\n /** Forget the previous render's inputs (call at the top of render). */\n reset(): void;\n /** Register a name input; `key` returns the comparison string (default the\n * input's value). Re-checks live on input. `also` = extra inputs that change the key. */\n track(input: HTMLInputElement, key?: () => string, also?: HTMLInputElement[]): void;\n /** Re-paint duplicates; true if any two non-blank keys collide. */\n check(): boolean;\n /** The first input currently flagged (after a fresh check), or null. */\n firstDuplicate(): HTMLInputElement | null;\n}\n\nconst DUP_MESSAGE = \"Another row already has this name.\";\n\n/** Case-insensitive duplicate-name detection: paints clashing inputs `.invalid`\n * and lets a Save gate block + jump via `firstDuplicate()`. */\nexport function dupGuard(): DupGuard {\n const items: Array<{ input: HTMLInputElement; key: () => string }> = [];\n const check = (): boolean => {\n const counts = new Map<string, number>();\n for (const it of items) { const k = it.key().trim().toLowerCase(); if (k) counts.set(k, (counts.get(k) ?? 0) + 1); }\n let any = false;\n for (const it of items) {\n const k = it.key().trim().toLowerCase();\n const dup = !!k && (counts.get(k) ?? 0) > 1;\n it.input.classList.toggle(\"invalid\", dup);\n // The reason rides `data-tip` (the themed rollover, and what the Save\n // gate's message channel reads), never `title`.\n if (dup) { it.input.dataset[\"tip\"] = DUP_MESSAGE; any = true; }\n else if (it.input.dataset[\"tip\"] === DUP_MESSAGE) delete it.input.dataset[\"tip\"];\n }\n return any;\n };\n return {\n reset() { items.length = 0; },\n track(input, key = () => input.value, also) {\n items.push({ input, key });\n input.addEventListener(\"input\", check);\n for (const a of also ?? []) a.addEventListener(\"input\", check);\n },\n check,\n firstDuplicate() { check(); return items.find((it) => it.input.classList.contains(\"invalid\"))?.input ?? null; },\n };\n}\n\n// --- the settings dialog ------------------------------------------------------\n\nexport interface SettingsSectionHandle {\n /** The first invalid control to jump to, blocking Save (e.g. a duplicate\n * name). Return `{ el, message }` to say WHICH fault in the error line;\n * a bare element says it through its own `data-tip` (the dup guard's, a\n * property-name field's), and the line falls back to a generic sentence\n * when it has neither. */\n firstInvalid?(): HTMLElement | { el: HTMLElement; message?: string } | null;\n}\n\nexport interface SettingsSection {\n id: string;\n label: string;\n /** Optional left-rail group heading above this tab. */\n group?: string;\n /** Build the section body into `host`; called fresh on each open so it reads\n * the current data. Return a handle (for the Save gate). */\n mount(host: HTMLElement): SettingsSectionHandle;\n /** Why this tab cannot be used right now, or null when it can. A reason\n * renders the tab dimmed and inert with the reason as its rollover\n * (Patterpad's Audio tab while the project is not voiced). Read on open\n * and on `refreshTabs()`, so a control inside the dialog can flip it. */\n disabled?: () => string | null;\n}\n\nexport interface SettingsDialogOptions {\n title: string;\n sections: SettingsSection[];\n /** Fired when Save is clicked and every section validates. The app reads its\n * own section state and persists. May be async; the dialog stays open until\n * it resolves, then closes.\n *\n * Return `false` (or a Promise of it) when the save was REFUSED, a version\n * control lock say, and the dialog stays open with the author's edits in it\n * for another try; saying why is the app's (a toast, usually). A throw, or a\n * rejected Promise, is treated the same way rather than surfacing as an\n * unhandled rejection. Anything else closes the dialog, as it always has. */\n onSave: () => void | boolean | Promise<void | boolean>;\n}\n\nexport interface SettingsDialog {\n /** Open (optionally on a given tab id). Sections re-mount from current data. */\n open(tabId?: string): void;\n /** Re-read every section's `disabled()`; a tab that has just become\n * disabled while showing hands over to the first usable one. */\n refreshTabs(): void;\n destroy(): void;\n}\n\n/** Build a settings dialog. The frame owns the modal, the tab rail, save-on-Save\n * (with a validation gate), and Cancel; the sections own their content + state. */\nexport function mountSettingsDialog(opts: SettingsDialogOptions): SettingsDialog {\n const dialog = el(\"dialog\", \"settings-dialog\");\n const form = el(\"form\", \"settings-form\"); form.method = \"dialog\";\n const tabs = el(\"nav\", \"settings-tabs\");\n const panels = el(\"div\", \"settings-panels\");\n const error = el(\"p\", \"settings-error\"); error.hidden = true;\n const cancel = el(\"button\", \"settings-cancel\", \"Cancel\"); cancel.type = \"button\"; cancel.value = \"cancel\";\n const save = el(\"button\", \"settings-save primary\", \"Save\"); save.value = \"save\";\n\n const panelFor = new Map<string, HTMLElement>();\n const tabFor = new Map<string, HTMLButtonElement>();\n const handles = new Map<string, SettingsSectionHandle>();\n let active = opts.sections[0]?.id ?? \"\";\n\n let lastGroup: string | undefined;\n for (const s of opts.sections) {\n if (s.group && s.group !== lastGroup) { tabs.append(el(\"div\", \"settings-group\", s.group)); lastGroup = s.group; }\n const tab = el(\"button\", \"settings-tab\", s.label); tab.type = \"button\"; tab.dataset[\"tab\"] = s.id;\n // A disabled tab is inert by class, not by the `disabled` attribute: a\n // disabled button swallows the pointer, and the reason has to be readable\n // on hover.\n tab.addEventListener(\"click\", () => { if (!tab.classList.contains(\"is-disabled\")) showTab(s.id); });\n tabs.append(tab); tabFor.set(s.id, tab);\n const panel = el(\"section\", \"settings-panel\"); panel.dataset[\"panel\"] = s.id; panel.hidden = true;\n panels.append(panel); panelFor.set(s.id, panel);\n }\n\n function showTab(id: string): void {\n active = id;\n for (const [sid, t] of tabFor) t.classList.toggle(\"active\", sid === id);\n for (const [sid, p] of panelFor) p.hidden = sid !== id;\n }\n\n const usable = (s: SettingsSection): boolean => !tabFor.get(s.id)?.classList.contains(\"is-disabled\");\n function refreshTabs(): void {\n for (const s of opts.sections) {\n const tab = tabFor.get(s.id)!;\n const reason = s.disabled?.() ?? null;\n tab.classList.toggle(\"is-disabled\", reason !== null);\n if (reason !== null) { tab.dataset[\"tip\"] = reason; tab.setAttribute(\"aria-disabled\", \"true\"); }\n else { delete tab.dataset[\"tip\"]; tab.removeAttribute(\"aria-disabled\"); }\n }\n if (!opts.sections.some((s) => s.id === active && usable(s))) {\n const first = opts.sections.find(usable);\n if (first) showTab(first.id);\n }\n }\n\n form.append(\n el(\"h2\", \"settings-title\", opts.title),\n el(\"div\", \"settings-body\", tabs, panels),\n el(\"div\", \"settings-actions\", error, cancel, save),\n );\n dialog.append(form);\n document.body.append(dialog);\n\n // Enter should not submit while editing a field; only the Save button does.\n form.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\" && (e.target as HTMLElement)?.tagName === \"INPUT\") e.preventDefault();\n });\n\n cancel.addEventListener(\"click\", () => dialog.close(\"cancel\"));\n save.addEventListener(\"click\", (e) => {\n e.preventDefault();\n error.hidden = true;\n for (const s of opts.sections) {\n const found = handles.get(s.id)?.firstInvalid?.();\n if (!found) continue;\n const bad = found instanceof HTMLElement ? found : found.el;\n const message = (found instanceof HTMLElement ? undefined : found.message) ?? bad.dataset[\"tip\"];\n showTab(s.id);\n bad.focus();\n // The field's own rollover says which fault it is, so the line repeats it\n // rather than inventing a summary; the generic sentence is the fallback.\n error.textContent = message || \"Fix the highlighted fields first.\";\n error.hidden = false;\n return;\n }\n // The dialog closes only on a save that went through. It used to close\n // whatever happened, so a save refused under a lock took the author's edits\n // away with it. Save is off while one is in flight, so a second click\n // cannot start a second save.\n save.disabled = true;\n void (async () => {\n let saved: unknown;\n try { saved = await opts.onSave(); } catch (err) { saved = false; console.error(\"Settings save failed:\", err); }\n save.disabled = false;\n if (saved !== false) dialog.close(\"save\");\n })();\n });\n\n return {\n open(tabId) {\n handles.clear();\n for (const s of opts.sections) {\n const panel = panelFor.get(s.id)!;\n panel.replaceChildren();\n handles.set(s.id, s.mount(panel));\n }\n error.hidden = true;\n showTab(tabId ?? active ?? opts.sections[0]?.id ?? \"\");\n refreshTabs(); // a disabled tab asked for by id hands over to the first usable one\n dialog.showModal();\n },\n refreshTabs,\n destroy() { dialog.remove(); },\n };\n}\n","// ---------------------------------------------------------------------------\n// Property names: what an author may call a value the story reads and writes.\n//\n// This belongs to the shell for the same reason gameIds do (see ids.ts): it\n// follows from the SHAPE these apps share - a project of data files, authored in\n// an editor, compiled for something else to run - rather than from any one\n// subject. Nothing here needs the words card, beat, hand or scene.\n//\n// UNLIKE gameIds, the rule is not a house style. It is dictated by the\n// expression language both families embed (`@wildwinter/expr`), and every clause\n// below is a failure observed by compiling one:\n//\n// parser.ts tokenises an identifier as /[a-zA-Z_][a-zA-Z0-9_]*/ and then\n// FOLDS it: `name: propTok.value.toLowerCase()`.\n//\n// @patter.is_night -> [\"sv\",\"patter\",\"is_night\"] reachable\n// @patter.isNight -> [\"sv\",\"patter\",\"isnight\"] folded: a declaration\n// spelled `isNight` is\n// one nothing can reach\n// @patter.is-night -> [\"bin\",\"-\",[\"sv\",\"patter\",\"is\"], NOT an error: it is\n// [\"s\",\"night\"]] SUBTRACTION. A hyphen\n// silently compiles to\n// a different meaning\n// @patter.is night -> parse error\n// @patter.9lives -> parse error (a name cannot start with a digit)\n// @patter.not -> parse error (`true false and or not` are keywords)\n//\n// The hyphen line is why this is worth a shared rule rather than a per-app\n// check: every other violation is loud, and that one quietly means something\n// else. It is also why a property name is NOT a gameId with different spelling -\n// a gameId is built out of hyphens, and here a hyphen is an operator.\n//\n// DEPENDENCY-FREE on purpose, ids.ts's reasoning verbatim: the apps' compilers,\n// CLIs and embedded runtimes need these rules far from any UI, so they keep\n// their own copies and assert parity with these in a test.\n// ---------------------------------------------------------------------------\n\n/** The five words `@wildwinter/expr` lexes as keywords, so a property may not be\n * called any of them: the reference does not resolve, it fails to parse. Kept\n * here as a copy for the same reason the rules are (no dependency), and each\n * app probes the real parser to prove the list is still true. */\nexport const RESERVED_PROPERTY_NAMES: readonly string[] = [\"true\", \"false\", \"and\", \"or\", \"not\"];\n\n/** What an editor should tell an author, in one line. */\nexport const PROPERTY_NAME_HINT =\n \"Lower case letters, digits, and underscores. It can't start with a digit. No hyphens or spaces.\";\n\n/**\n * Coerce a label into a legal property name: lower case, apostrophes dropped,\n * runs of anything else to a single underscore, no trailing underscore, an\n * underscore in front if it would otherwise start with a digit, and one behind\n * if it would otherwise be a keyword.\n *\n * A LEADING underscore is kept where the author typed one, because `_private` is\n * a legal name and a meaningful choice; only the ends that would be illegal are\n * touched.\n *\n * May return \"\", when there was nothing usable in the input. Callers treat that\n * as \"no name yet\" rather than as a name.\n */\nexport function propertyNameify(text: string): string {\n const trimmed = text.trim();\n // An underscore the author actually typed is kept; one that is only the ghost of\n // leading punctuation is not. That is the difference between `_private` and `!gold`.\n const deliberateLeading = trimmed.startsWith(\"_\");\n let out = trimmed.toLowerCase().replace(/['’]/g, \"\")\n .replace(/[^a-z0-9_]+/g, \"_\").replace(/_+/g, \"_\").replace(/^_+|_+$/g, \"\");\n if (out === \"\") return \"\";\n if (deliberateLeading || /^[0-9]/.test(out)) out = `_${out}`;\n if (RESERVED_PROPERTY_NAMES.includes(out)) out = `${out}_`;\n return out;\n}\n\n/**\n * Is this a name the expression language can actually reach? Lower case letters,\n * digits and underscores, not starting with a digit, and not a keyword.\n *\n * \"\" is NOT legal: an unnamed declaration is an unfinished row, which is a\n * different thing from an illegal one, and callers distinguish them.\n */\nexport function isValidPropertyName(name: string): boolean {\n return /^[a-z_][a-z0-9_]*$/.test(name) && !RESERVED_PROPERTY_NAMES.includes(name);\n}\n\n/**\n * The one thing that is only a case difference, e.g. `isNight` -> `isnight`.\n *\n * Worth its own question because it is the only violation that can be repaired\n * without guessing at intent: the language already folds every REFERENCE, so\n * folding the declaration to match changes nothing observable, where inventing\n * an underscore for a space is a decision about what the author meant. Loaders\n * fold this class silently and report the rest.\n */\nexport function isCaseOnlyPropertyName(name: string): boolean {\n return !isValidPropertyName(name) && isValidPropertyName(name.toLowerCase());\n}\n","// ---------------------------------------------------------------------------\n// The manners for a field that names a declared property.\n//\n// The RULE is property-names.ts, which is dependency-free because each app's\n// domain layer keeps its own copy of it. This is the other half: how a field\n// BEHAVES while somebody types a name into it, which only UI touches, so the\n// shell owns it outright.\n//\n// The manners are the gameId editor's, adopted rather than re-argued (see\n// id-editor.ts): an illegal name is marked and blocks Save, and Tab coerces it,\n// in the field, where it can be seen and undone. Nothing is rewritten as it is\n// typed. Patterpad's first pass at this folded every keystroke to lower case;\n// that is the manner the id editor had already rejected, on the grounds that an\n// app which silently renames a thing has made a decision that was not its to\n// make, and a property name is exactly that kind of thing: the author is naming\n// something their own conditions will call.\n//\n// It arrived here as two copies, one per app, written in one session and already\n// carrying the same forty lines of fault wording. Lifting them before they could\n// drift is the whole reason this file exists.\n// ---------------------------------------------------------------------------\n\nimport { isValidPropertyName, propertyNameify, RESERVED_PROPERTY_NAMES } from \"./property-names.js\";\n\n/** Re-check callbacks for bound REFERENCE fields, so a host can say \"the declarations\n * changed\" without holding a handle per row. */\nconst refSyncs = new WeakMap<HTMLInputElement, () => void>();\n\n/**\n * One line saying what is wrong with this name, or undefined when nothing is.\n *\n * Each branch names what actually HAPPENS rather than reciting the rule, because\n * the person reading it is usually somebody wondering why a condition does\n * nothing. Kept to one line: it lands in a rollover on the field, and hosts\n * repeat it in the Save error rather than inventing a summary of their own.\n */\nexport function propertyNameProblem(name: string): string | undefined {\n if (isValidPropertyName(name)) return undefined;\n const suggestion = propertyNameify(name);\n const tail = suggestion ? ` Try \"${suggestion}\", or press Tab.` : \"\";\n if (RESERVED_PROPERTY_NAMES.includes(name.toLowerCase())) {\n return `Can't be used. \"${name.toLowerCase()}\" is a keyword in expressions.${tail}`;\n }\n // The one that is not an error anywhere else: `@scope.a-b` compiles to a-minus-b.\n if (name.includes(\"-\")) return `Can't be used. A hyphen reads as subtraction in an expression.${tail}`;\n if (/^[0-9]/.test(name)) return `Can't be used. A name can't start with a digit.${tail}`;\n if (name !== name.toLowerCase()) {\n return `Can't be used. Expressions fold names, so this would look for \"${name.toLowerCase()}\".${tail}`;\n }\n return `Can't be used. Only lower case letters, digits, and underscores.${tail}`;\n}\n\n/**\n * Wire a name field: mark it live, explain the fault in its rollover, and coerce\n * on Tab. `onCommit` receives what the author typed, VERBATIM, on every input.\n *\n * `.illegal` rather than dupGuard's `.invalid`, because both can be true of one\n * field and `dupGuard.check()` owns that class: it would clear this marking every\n * time it ran, including at the moment of a blocked Save.\n *\n * An empty field is not marked. An unfinished row is a different thing from an\n * illegal one, and hosts prune blank names on save.\n */\nexport function bindPropertyName(\n input: HTMLInputElement,\n onCommit: (value: string) => void,\n opts: { hint?: string } = {},\n): void {\n const hint = opts.hint;\n const sync = (): void => {\n const v = input.value.trim();\n const problem = v === \"\" ? undefined : propertyNameProblem(v);\n input.classList.toggle(\"illegal\", !!problem);\n if (problem) input.title = problem;\n else if (input.title.startsWith(\"Can't be used\")) {\n if (hint) input.title = hint;\n else input.removeAttribute(\"title\");\n }\n };\n input.addEventListener(\"input\", () => { onCommit(input.value); sync(); });\n input.addEventListener(\"keydown\", (e) => {\n // The opt-in coercion, the id editor's gesture. Shift+Tab is left alone: it is\n // how somebody leaves a field they have not finished with.\n if (e.key !== \"Tab\" || e.shiftKey) return;\n const typed = input.value.trim();\n if (typed === \"\" || isValidPropertyName(typed)) return;\n const coerced = propertyNameify(typed);\n if (!coerced || coerced === input.value) return;\n e.preventDefault();\n input.value = coerced;\n onCommit(coerced);\n sync();\n });\n sync();\n}\n\n/**\n * The first field in `host` holding a name no expression could reach, for a Save\n * gate. Hosts hand this back beside `dupGuard.firstDuplicate()`: two faults, two\n * sets of words, one gate.\n */\nexport function firstIllegalPropertyName(host: ParentNode): HTMLInputElement | null {\n return host.querySelector<HTMLInputElement>(\"input.illegal\");\n}\n\n// ---------------------------------------------------------------------------\n// Reference fields, which are a different job from declaration fields.\n//\n// A declaration NAMES a property; a reference POINTS at one. The expression\n// parser folds every reference, so `@world.isNight` reaches `isnight` perfectly\n// well and complaining about the capital would be inventing a rule the language\n// does not have. Everything else still bites, and harder: no declaration may\n// contain a hyphen, so `is-night` in a reference can never match anything that\n// exists.\n//\n// And a reference has a fault a declaration cannot have: naming something that\n// was never declared. That is not a grammar question, so the host supplies the\n// list. The cost of getting it wrong is quiet - a coverage driver pointed at a\n// property nobody declared feeds a value nobody reads, the branches it was meant\n// to exercise stay unexercised, and the report shows them as never hit, which\n// reads as \"this content is unreachable\" rather than \"that name is a typo\".\n// ---------------------------------------------------------------------------\n\nexport interface PropertyRefOptions {\n /** The names that exist, read fresh on every check so a declaration added in the\n * same dialog clears the complaint. Compared case-insensitively, since the\n * parser folds. */\n known: () => string[];\n /** The scope token this field addresses, for the wording (\"no @world property...\"). */\n scope?: string;\n /** Shown on the field when there is nothing to complain about. */\n hint?: string;\n}\n\n/** The closest known name, when one is close enough to be worth naming. Deliberately\n * narrow: a wrong suggestion is worse than none, so it wants a shared prefix or a\n * single-character slip, not the least-bad match in the list. */\nfunction nearest(name: string, known: string[]): string | undefined {\n const n = name.toLowerCase();\n const byPrefix = known.filter((k) => k.toLowerCase().startsWith(n) || n.startsWith(k.toLowerCase()));\n if (byPrefix.length === 1) return byPrefix[0];\n let best: string | undefined;\n let bestScore = 3; // strictly better than \"three edits away\"\n for (const k of known) {\n const a = k.toLowerCase();\n if (Math.abs(a.length - n.length) > 2) continue;\n // Levenshtein, small strings, no need for a matrix beyond two rows.\n let prev = Array.from({ length: n.length + 1 }, (_, i) => i);\n for (let i = 1; i <= a.length; i++) {\n const row = [i];\n for (let j = 1; j <= n.length; j++) {\n row[j] = Math.min(prev[j]! + 1, row[j - 1]! + 1, prev[j - 1]! + (a[i - 1] === n[j - 1] ? 0 : 1));\n }\n prev = row;\n }\n const d = prev[n.length]!;\n if (d < bestScore) { bestScore = d; best = k; }\n }\n return best;\n}\n\n/** Why this reference cannot be used, or undefined when it can. */\nexport function propertyRefProblem(name: string, opts: PropertyRefOptions): string | undefined {\n const scope = opts.scope ? `@${opts.scope}` : \"that scope\";\n // Case is NOT a fault here: the parser folds references.\n const folded = name.toLowerCase();\n if (!isValidPropertyName(folded)) {\n const suggestion = propertyNameify(name);\n const tail = suggestion ? ` Try \"${suggestion}\", or press Tab.` : \"\";\n if (RESERVED_PROPERTY_NAMES.includes(folded)) return `Can't be used. \"${folded}\" is a keyword in expressions.${tail}`;\n if (name.includes(\"-\")) return `Can't be used. A hyphen reads as subtraction in an expression.${tail}`;\n if (/^[0-9]/.test(name)) return `Can't be used. A name can't start with a digit.${tail}`;\n return `Can't be used. Only lower case letters, digits, and underscores.${tail}`;\n }\n const known = opts.known();\n if (known.some((k) => k.toLowerCase() === folded)) return undefined;\n const near = nearest(folded, known);\n if (near) return `No ${scope} property is called \"${name}\". Did you mean \"${near}\"?`;\n if (known.length === 0) return `No ${scope} properties are declared yet. Declare one above first.`;\n const shown = known.slice(0, 3).map((k) => `\"${k}\"`).join(\", \");\n const more = known.length > 3 ? `, and ${known.length - 3} more` : \"\";\n return `No ${scope} property is called \"${name}\". Pick one of ${shown}${more}.`;\n}\n\n/**\n * Wire a field that POINTS at a declared property: same manners as a declaration\n * field (mark, do not rewrite, Tab coerces), plus the declared names offered as a\n * datalist, because the rule is declare-then-reference and a picker makes that\n * painless rather than pedantic.\n *\n * Marks `.illegal` for an undeclared name too, which gates Save wherever the host\n * already asks `firstIllegalPropertyName`.\n */\nexport function bindPropertyRef(\n input: HTMLInputElement,\n onCommit: (value: string) => void,\n opts: PropertyRefOptions,\n): void {\n const list = document.createElement(\"datalist\");\n list.id = `shell-propref-${Math.random().toString(36).slice(2, 9)}`;\n input.setAttribute(\"list\", list.id);\n\n // Attached lazily, not once at bind. A datalist has to be IN the document to do anything, and it is\n // attached as a sibling of the input - so binding a field that has not been appended yet (which is\n // the ordinary order for a builder that makes its nodes and then mounts them) silently produced no\n // autocomplete at all: no error, nothing to see. This re-tries on every sync, so it heals as soon as\n // the field reaches the tree.\n const attach = (): void => {\n if (!list.isConnected && input.parentNode) input.after(list);\n };\n\n const sync = (): void => {\n attach();\n const known = opts.known();\n list.replaceChildren(...known.map((k) => { const o = document.createElement(\"option\"); o.value = k; return o; }));\n const v = input.value.trim();\n const problem = v === \"\" ? undefined : propertyRefProblem(v, opts);\n input.classList.toggle(\"illegal\", !!problem);\n if (problem) input.title = problem;\n else if (input.title.startsWith(\"Can't be used\") || input.title.startsWith(\"No \")) {\n if (opts.hint) input.title = opts.hint;\n else input.removeAttribute(\"title\");\n }\n };\n refSyncs.set(input, sync);\n\n input.addEventListener(\"input\", () => { onCommit(input.value); sync(); });\n input.addEventListener(\"keydown\", (e) => {\n if (e.key !== \"Tab\" || e.shiftKey) return;\n const typed = input.value.trim();\n // Only a GRAMMAR fault is coercible. An undeclared name is not something Tab can\n // fix, and a legal reference in the wrong case is not a fault at all.\n if (typed === \"\" || isValidPropertyName(typed.toLowerCase())) return;\n const coerced = propertyNameify(typed);\n if (!coerced || coerced === input.value) return;\n e.preventDefault();\n input.value = coerced;\n onCommit(coerced);\n sync();\n });\n sync();\n}\n\n/**\n * Re-check every bound reference field inside `host`. Hosts call this when the\n * DECLARATIONS change, since a reference's validity depends on a list it does not\n * own: rename a property and the driver pointing at it is wrong from that moment,\n * with nothing typed into it.\n */\nexport function revalidatePropertyRefs(host: ParentNode): void {\n for (const input of host.querySelectorAll(\"input\")) refSyncs.get(input)?.();\n}\n","// ---------------------------------------------------------------------------\n// THE property declaration list: name, type, default, values or stages,\n// purpose, in a row per declaration, with reorder, remove, add-then-focus and\n// the duplicate / illegal-name gates. Patterpad's settings-properties.ts and\n// Storyletter's prop-list.ts were built from the same shell primitives in the\n// same order (dupGuard, bindPropertyName, a type select over the same six\n// types, defaultControl, three iconBtns on moveItem, labelled tagChips and\n// stageChips, expandableRow, focusNewRow); this is the one list, and each\n// app's extras (Shared, Temporary, Read-only, Durable) arrive through\n// `extraLine` / `extraDetails`.\n//\n// `defaultControl` was four copies (three inside Patterpad), and the two apps'\n// option labels had already parted (\"(unset)\" vs \"(none)\"). One label set now.\n//\n// Storage of the default differs by app: Patterpad's model holds a typed\n// value (true, 3), Storyletter's DTO holds the string the field showed. The\n// list mutates `decls` in place either way; `typed` picks the encoding.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { iconBtn, labelled, moveItem, stageChips, tagChips } from \"./dom.js\";\nimport { dupGuard, expandableRow, focusNewRow } from \"./settings.js\";\nimport type { SettingsSectionHandle } from \"./settings.js\";\nimport { bindPropertyName, firstIllegalPropertyName } from \"./property-name-field.js\";\nimport { PROPERTY_NAME_HINT } from \"./property-names.js\";\n\n/** The six types both apps declare. */\nexport type PropertyListType = \"number\" | \"boolean\" | \"string\" | \"enum\" | \"flags\" | \"quality\";\n\n/** The shape a row edits. Apps' DTOs carry more (shared, writable, durable);\n * those ride through untouched. */\nexport interface PropertyDeclLike {\n name: string;\n type: string;\n default?: unknown;\n /** enum / flags */\n values?: string[];\n /** quality: the ladder, in order */\n stages?: string[];\n purpose?: string;\n}\n\n/** The type list, in the family's order, with the words the select shows. */\nexport const PROPERTY_TYPES: ReadonlyArray<readonly [PropertyListType, string]> = [\n [\"number\", \"Number\"], [\"boolean\", \"True / False\"], [\"string\", \"Text\"],\n [\"enum\", \"List\"], [\"flags\", \"Flags\"], [\"quality\", \"Quality\"],\n];\n\nexport interface DefaultControlOptions {\n /**\n * How the default is stored. `false` (default): the string the field shows,\n * \"\" for none (Storyletter's DTO). `true`: a typed value (boolean, number,\n * string), and none DELETES the key (Patterpad's model).\n */\n typed?: boolean;\n}\n\nconst option = (value: string, label: string, selected: boolean): HTMLOptionElement => {\n const o = el(\"option\", undefined, label);\n o.value = value; o.selected = selected;\n return o;\n};\n\nconst NONE = \"(none)\";\n\n/**\n * The type-appropriate control for a declaration's DEFAULT: a true / false\n * picker for a boolean, the value list for an enum, the stage list for a\n * quality (blank = the first rung), a note for flags (a set starts empty), a\n * number or text field otherwise. The one label for \"no default\" is \"(none)\".\n */\nexport function defaultControl(p: PropertyDeclLike, onChange?: () => void, opts: DefaultControlOptions = {}): HTMLElement {\n const typed = opts.typed === true;\n const write = (raw: string): void => {\n if (raw === \"\") { if (typed) delete p.default; else p.default = \"\"; }\n else if (typed && p.type === \"boolean\") p.default = raw === \"true\";\n else if (typed && p.type === \"number\") p.default = Number(raw);\n else p.default = raw;\n onChange?.();\n };\n const current = p.default === undefined || p.default === null ? \"\" : String(p.default);\n if (p.type === \"boolean\") {\n const sel = el(\"select\", \"field set-default\");\n sel.append(option(\"\", NONE, current === \"\"), option(\"true\", \"True\", current === \"true\"), option(\"false\", \"False\", current === \"false\"));\n sel.addEventListener(\"change\", () => write(sel.value));\n return sel;\n }\n if (p.type === \"enum\") {\n const sel = el(\"select\", \"field set-default\");\n sel.append(option(\"\", NONE, current === \"\"));\n for (const v of p.values ?? []) sel.append(option(v, v, current === v));\n sel.addEventListener(\"change\", () => write(sel.value));\n return sel;\n }\n if (p.type === \"quality\") {\n // The default is a STAGE; blank means the first rung, so say so rather\n // than \"(none)\".\n const sel = el(\"select\", \"field set-default\");\n sel.append(option(\"\", \"(first stage)\", current === \"\"));\n for (const v of p.stages ?? []) sel.append(option(v, v, current === v));\n sel.addEventListener(\"change\", () => write(sel.value));\n return sel;\n }\n if (p.type === \"flags\") {\n // Flags hold a SET of values, so there is no single default: it starts empty.\n const s = el(\"span\", \"set-flagnote\", \"Starts empty\");\n s.dataset[\"tip\"] = \"A flags property starts with none set.\";\n return s;\n }\n const input = el(\"input\", \"field set-default\");\n input.type = p.type === \"number\" ? \"number\" : \"text\";\n input.placeholder = \"Default\";\n input.value = current;\n input.addEventListener(\"input\", () => write(input.value));\n return input;\n}\n\nexport interface PropertyListOptions<T extends PropertyDeclLike> extends DefaultControlOptions {\n /** Called after every mutation; a centre editor feeds its autosave here. */\n onChange?: () => void;\n /** The add button's label. Default \"+ Add property\". */\n addLabel?: string;\n /** The empty sentence. Default \"No properties yet.\" */\n emptyText?: string;\n /** The type select's entries; default `PROPERTY_TYPES`. */\n types?: ReadonlyArray<readonly [string, string]>;\n /** A fresh declaration for the add button. Default: a blank of the first type. */\n newDecl?: () => T;\n /** Controls on the row's line, between the default and the movers\n * (Storyletter's \"uses\" affordance). */\n extraLine?: (decl: T, index: number) => (HTMLElement | null)[];\n /** Fields behind the disclosure, before Values / Stages / Purpose\n * (Shared, Temporary, Read-only, Durable). */\n extraDetails?: (decl: T, index: number) => (HTMLElement | null)[];\n /** Offer the Purpose field. Default true. */\n purpose?: boolean;\n /**\n * A quality's stage was renamed in place (the declaration's own default\n * already follows it). Given, the rename is handed here INSTEAD of\n * `onChange`, so the caller can carry every value that names the stage in\n * the same edit as the declaration; absent, it is an ordinary change.\n */\n onStageRename?: (decl: T, from: string, to: string) => void;\n /** A quality's stages were reordered (after `onChange`). `note` is a quiet\n * line under the stages for the caller to fill, such as how many gates\n * compare against the ladder. */\n onStageReorder?: (decl: T, note: HTMLElement) => void;\n}\n\nexport interface PropertyListHandle extends SettingsSectionHandle {\n /** Both faults, for the Save gate: a duplicate first, then an illegal name. */\n firstInvalid(): HTMLInputElement | null;\n /** The first name that clashes with another, or null. */\n firstDuplicate(): HTMLInputElement | null;\n /** The first name no expression could reach, or null. */\n firstIllegalName(): HTMLInputElement | null;\n /** Redraw from `decls` (after an outside change). */\n render(): void;\n}\n\n/** Mount the list into `host`. Mutates `decls` in place; the caller reads\n * them back on save (pruning blank names is the caller's, since only it\n * knows its DTO). */\nexport function mountPropertyList<T extends PropertyDeclLike>(host: HTMLElement, decls: T[], opts: PropertyListOptions<T> = {}): PropertyListHandle {\n const changed = (): void => opts.onChange?.();\n const guard = dupGuard();\n const types = opts.types ?? PROPERTY_TYPES;\n const list = el(\"div\", \"set-list\");\n const keep = (parts: (HTMLElement | null)[] | undefined): HTMLElement[] =>\n (parts ?? []).filter((p): p is HTMLElement => p !== null);\n\n const row = (p: T, i: number): HTMLElement => {\n const name = el(\"input\", \"field set-name\");\n name.type = \"text\"; name.placeholder = \"Property name\"; name.value = p.name; name.spellcheck = false;\n name.dataset[\"tip\"] = PROPERTY_NAME_HINT;\n bindPropertyName(name, (v) => { p.name = v; changed(); }, { hint: PROPERTY_NAME_HINT });\n guard.track(name);\n\n const type = el(\"select\", \"field set-type\");\n for (const [v, label] of types) type.append(option(v, label, v === p.type));\n type.addEventListener(\"change\", () => {\n p.type = type.value;\n if (opts.typed) delete p.default; else p.default = \"\";\n if (p.type === \"enum\" || p.type === \"flags\") p.values ??= []; else delete p.values;\n if (p.type === \"quality\") p.stages ??= []; else delete p.stages;\n render(); changed();\n });\n\n // The default control is rebuilt in place when the values change, so a\n // new value becomes selectable as the default.\n let def = defaultControl(p, changed, opts);\n const refreshDefault = (): void => { const fresh = defaultControl(p, changed, opts); def.replaceWith(fresh); def = fresh; changed(); };\n\n const up = iconBtn(\"up\", \"Move up\", () => { if (moveItem(decls, i, -1)) { render(); changed(); } }, i === 0);\n const down = iconBtn(\"down\", \"Move down\", () => { if (moveItem(decls, i, 1)) { render(); changed(); } }, i === decls.length - 1);\n const del = iconBtn(\"close\", \"Delete property\", () => { decls.splice(i, 1); render(); changed(); }, false, true);\n\n const details: HTMLElement[] = keep(opts.extraDetails?.(p, i));\n if (p.type === \"enum\" || p.type === \"flags\") details.push(labelled(\"Values\", tagChips(p, refreshDefault)));\n // A quality's ladder, IN ORDER: the chips carry movers because position is the meaning.\n if (p.type === \"quality\") {\n const note = el(\"p\", \"shell-stage-note\");\n const stages = stageChips(p, refreshDefault, {\n onRename: (from, to) => {\n if (p.default === from) p.default = to;\n const fresh = defaultControl(p, changed, opts); def.replaceWith(fresh); def = fresh;\n if (opts.onStageRename) opts.onStageRename(p, from, to); else changed();\n },\n onReorder: () => opts.onStageReorder?.(p, note),\n });\n details.push(labelled(\"Stages (in order)\", stages), note);\n }\n if (opts.purpose !== false) {\n const purpose = el(\"input\", \"field set-purpose\");\n purpose.type = \"text\"; purpose.placeholder = \"What this property is for\"; purpose.value = p.purpose ?? \"\";\n purpose.addEventListener(\"input\", () => {\n if (purpose.value.trim()) p.purpose = purpose.value; else delete p.purpose;\n changed();\n });\n details.push(labelled(\"Purpose\", purpose));\n }\n // Stamped with the declared name so \"Go to definition\" lands on the row (revealRow).\n return expandableRow({ line: [name, type, def, ...keep(opts.extraLine?.(p, i)), up, down, del], details, name: p.name });\n };\n\n const render = (): void => {\n guard.reset();\n list.replaceChildren();\n if (decls.length === 0) list.append(el(\"p\", \"empty\", opts.emptyText ?? \"No properties yet.\"));\n else decls.forEach((p, i) => list.append(row(p, i)));\n guard.check();\n };\n render();\n\n const add = el(\"button\", \"btn set-add\", opts.addLabel ?? \"+ Add property\");\n add.type = \"button\";\n add.addEventListener(\"click\", () => {\n decls.push(opts.newDecl ? opts.newDecl() : ({ name: \"\", type: types[0]?.[0] ?? \"string\", ...(opts.typed ? {} : { default: \"\" }) } as unknown as T));\n render(); changed(); focusNewRow(list);\n });\n host.append(list, add);\n\n return {\n firstDuplicate: () => guard.firstDuplicate(),\n firstIllegalName: () => firstIllegalPropertyName(host),\n // The Save gate takes both faults; the field's own rollover says which.\n firstInvalid: () => guard.firstDuplicate() ?? firstIllegalPropertyName(host),\n render,\n };\n}\n"]}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { el } from './chunk-
|
|
1
|
+
import { el } from './chunk-W4WC4FO5.js';
|
|
2
2
|
import { closeWithExit } from './chunk-IU7EY6BI.js';
|
|
3
3
|
|
|
4
4
|
// src/context-menu.ts
|
|
@@ -78,5 +78,5 @@ function openContextMenu(x, y, items) {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
export { openContextMenu, openPopover };
|
|
81
|
-
//# sourceMappingURL=chunk-
|
|
82
|
-
//# sourceMappingURL=chunk-
|
|
81
|
+
//# sourceMappingURL=chunk-OMG6A5YB.js.map
|
|
82
|
+
//# sourceMappingURL=chunk-OMG6A5YB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/context-menu.ts"],"names":[],"mappings":"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","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/context-menu.ts"],"names":[],"mappings":"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","file":"chunk-OMG6A5YB.js","sourcesContent":["// ---------------------------------------------------------------------------\n// A right-click context menu and a small anchored popover: a floating list of\n// actions at the cursor (or a panel under a control), dismissed on outside\n// click or Escape, clamped to the viewport, closed with the family's exit\n// motion.\n//\n// Storyletter's, lifted (ui-review-2026-09, finding 11): its own header had\n// named itself an extraction candidate, and it had the two things Patterpad's\n// inline `sceneContextMenu` lacked, a `disabled` item and the clamp. A DOM\n// popover, not a native menu, so it is themed like everything else.\n//\n// Styles ship as context-menu.css; import it wherever the module is used,\n// including tool windows, which is the bug that made Storyletter give the\n// menu its own stylesheet in the first place.\n// ---------------------------------------------------------------------------\n\nimport { el } from \"./dom.js\";\nimport { closeWithExit } from \"./exit.js\";\n\nexport interface ContextItem {\n label: string;\n /** Drawn in --danger: a destructive action, which names its object. */\n danger?: boolean;\n /** Shown greyed and unclickable. For an action that belongs on this menu\n * but cannot apply right now (Move up on the first row), so the menu keeps\n * the same shape and the same item stays in the same place every time. */\n disabled?: boolean;\n onClick: () => void;\n}\n\nconst EDGE = 6;\n\n/** The one open menu or popover. Only ever one, so a fresh open replaces it\n * and a re-render that removed the anchor cannot leave one hanging in the air. */\nlet current: (() => void) | null = null;\n\n/** Outside-click / Escape wiring shared by the menu and the popover. The\n * returned `dismiss` plays the exit and removes the node; `onDismiss` fires\n * for every way out so a caller can flush on all of them. */\nfunction floating(node: HTMLElement, onDismiss?: () => void): { dismiss: () => void } {\n let gone = false;\n const dismiss = (): void => {\n if (gone) return;\n gone = true;\n if (current === dismiss) current = null;\n window.removeEventListener(\"pointerdown\", onAway, true);\n window.removeEventListener(\"keydown\", onKey, true);\n // A node mid-fade must not take a second click.\n closeWithExit(node, () => node.remove());\n onDismiss?.();\n };\n const onAway = (e: PointerEvent): void => { if (!node.contains(e.target as Node)) dismiss(); };\n const onKey = (e: KeyboardEvent): void => { if (e.key === \"Escape\") { e.preventDefault(); dismiss(); } };\n window.addEventListener(\"pointerdown\", onAway, true);\n window.addEventListener(\"keydown\", onKey, true);\n current?.();\n current = dismiss;\n return { dismiss };\n}\n\n/** Clamp a floating node into the viewport, preferring below `anchor` and\n * flipping above it when there is no room. */\nfunction placeBelow(node: HTMLElement, anchor: DOMRect): void {\n node.style.left = `${anchor.left}px`;\n node.style.top = `${anchor.bottom + 4}px`;\n const r = node.getBoundingClientRect();\n if (r.right > window.innerWidth) node.style.left = `${Math.max(EDGE, window.innerWidth - r.width - EDGE)}px`;\n if (r.bottom > window.innerHeight) node.style.top = `${Math.max(EDGE, anchor.top - r.height - 4)}px`;\n}\n\nexport interface PopoverOptions {\n /** Where the popover is appended. Default <body>. A popover opened from\n * inside a modal <dialog> passes the dialog: a body-level node sits behind\n * the scrim, inert, while one inside the dialog paints in its top layer\n * (Patterpad's colour swatch popover in Project settings). */\n host?: HTMLElement;\n}\n\n/** Open a small panel anchored to an element; `build` gets a close callback.\n * `onClose` runs however it closes (Escape, outside click, or `close`), the\n * place to flush an edit the panel was collecting. Only one is ever open. */\nexport function openPopover(\n anchor: HTMLElement, build: (close: () => void) => HTMLElement, onClose?: () => void, opts: PopoverOptions = {},\n): void {\n const pop = el(\"div\", \"popover\");\n let closed = false;\n const finish = (): void => { if (closed) return; closed = true; onClose?.(); };\n const { dismiss } = floating(pop, finish);\n pop.append(build(dismiss));\n (opts.host ?? document.body).append(pop);\n placeBelow(pop, anchor.getBoundingClientRect());\n}\n\n/** Open a context menu at (x, y). Only one is ever open. */\nexport function openContextMenu(x: number, y: number, items: ContextItem[]): void {\n const menu = el(\"div\", \"ctxmenu\");\n menu.setAttribute(\"role\", \"menu\");\n const { dismiss } = floating(menu);\n\n for (const item of items) {\n const button = el(\"button\", {\n className: `ctxmenu-item${item.danger ? \" danger\" : \"\"}`, text: item.label,\n onClick: () => { if (item.disabled === true) return; dismiss(); item.onClick(); },\n });\n button.type = \"button\";\n button.setAttribute(\"role\", \"menuitem\");\n if (item.disabled === true) button.disabled = true;\n menu.append(button);\n }\n\n menu.style.left = `${x}px`;\n menu.style.top = `${y}px`;\n document.body.append(menu);\n // Keep it on-screen.\n const r = menu.getBoundingClientRect();\n if (r.right > window.innerWidth) menu.style.left = `${Math.max(EDGE, window.innerWidth - r.width - EDGE)}px`;\n if (r.bottom > window.innerHeight) menu.style.top = `${Math.max(EDGE, window.innerHeight - r.height - EDGE)}px`;\n}\n"]}
|
|
@@ -50,6 +50,7 @@ var FILE_MENU = {
|
|
|
50
50
|
projectSettings: { label: "Project Settings\u2026", accelerator: "CmdOrCtrl+," }
|
|
51
51
|
};
|
|
52
52
|
var PLAY_MENU = {
|
|
53
|
+
play: { accelerator: "CmdOrCtrl+P" },
|
|
53
54
|
liveLink: { label: "Live Link" }
|
|
54
55
|
};
|
|
55
56
|
var REVIEW_MENU = {
|
|
@@ -93,5 +94,5 @@ function recentsSubmenu(recents, opts) {
|
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
export { APP_MENU, EDIT_MENU, FILE_MENU, GO_MENU, HELP_MENU, PANE_MENU, PLAY_MENU, PUBLISH_MENU, REVIEW_MENU, VIEW_MENU, namedMenuItems, recentsSubmenu, tildePath };
|
|
96
|
-
//# sourceMappingURL=chunk-
|
|
97
|
-
//# sourceMappingURL=chunk-
|
|
97
|
+
//# sourceMappingURL=chunk-T25WXWPB.js.map
|
|
98
|
+
//# sourceMappingURL=chunk-T25WXWPB.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/menu.ts"],"names":[],"mappings":"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","file":"chunk-T25WXWPB.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Shell-owned menu naming: the labels + accelerators for the pane-frame menu\n// items, single-sourced so every app in the family names them identically\n// (and a rename here flows to all of them). Pure data - no electron, no DOM -\n// so a main-process menu builder can import it. Each app builds its own\n// MenuItem from these and wires the click to its shell controller.\n// ---------------------------------------------------------------------------\n\nimport type { RecentProject } from \"./app-store.js\";\n\nexport interface MenuLabel {\n label: string;\n /** Electron accelerator string, when the item has one. */\n accelerator?: string;\n}\n\n/**\n * The Edit menu items the family shares.\n *\n * Undo and Redo are here and the MECHANISM is not, which is the whole finding\n * about undo: Patterpad's is ProseMirror's document history, Storyletter's is a\n * replay of file bytes, and those have nothing in common but this. See\n * `design/shared-shell.md`.\n *\n * The rule both apps discovered separately, and the reason these are spelled\n * out rather than left as Electron's `role: \"undo\"`: the native role runs a DOM\n * text-field undo, which knows nothing about either app's model and will\n * silently undo the wrong thing.\n */\nexport const EDIT_MENU: {\n undo: MenuLabel;\n redo: MenuLabel;\n duplicate: MenuLabel;\n find: MenuLabel;\n /** Replace is Find's second tab, on the platform's key for it. */\n replace: { label: string; acceleratorMac: string; acceleratorOther: string };\n} = {\n undo: { label: \"Undo\", accelerator: \"CmdOrCtrl+Z\" },\n redo: { label: \"Redo\", accelerator: \"Shift+CmdOrCtrl+Z\" },\n duplicate: { label: \"Duplicate\", accelerator: \"CmdOrCtrl+D\" },\n find: { label: \"Find…\", accelerator: \"CmdOrCtrl+F\" },\n replace: { label: \"Replace…\", acceleratorMac: \"Cmd+Alt+F\", acceleratorOther: \"Ctrl+H\" },\n};\n\n/**\n * Back / Forward: navigation HISTORY, the other axis from Up a Level's\n * hierarchy. One string cannot serve both platforms - the mac pair is Xcode's\n * (Cmd+[ is already Up a Level in this family, so the browser pair is taken),\n * and the Alt pair is what every Windows browser and Explorer trained - so the\n * menu builder picks by platform.\n */\nexport const GO_MENU: {\n back: { label: string; acceleratorMac: string; acceleratorOther: string };\n forward: { label: string; acceleratorMac: string; acceleratorOther: string };\n} = {\n back: { label: \"Back\", acceleratorMac: \"Ctrl+Cmd+Left\", acceleratorOther: \"Alt+Left\" },\n forward: { label: \"Forward\", acceleratorMac: \"Ctrl+Cmd+Right\", acceleratorOther: \"Alt+Right\" },\n};\n\n/** The pane-frame menu items (a View menu, conventionally). */\nexport const PANE_MENU: {\n showNav: MenuLabel;\n showInspector: MenuLabel;\n resetView: MenuLabel;\n} = {\n showNav: { label: \"Show Navigator\", accelerator: \"CmdOrCtrl+1\" },\n showInspector: { label: \"Show Inspector\", accelerator: \"CmdOrCtrl+2\" },\n resetView: { label: \"Reset View\" },\n};\n\n/**\n * The Help menu items the family shares, and the one platform rule that goes\n * with them.\n *\n * Lifted from Patterpad, which is the only app that has ever had these: a\n * second app writing its own updater or About is pure waste (`design/\n * shared-shell.md`), and a suite is recognised by finding Check for Updates\n * where you left it. Storyletter will grow both; until it does, these labels\n * are the agreement rather than a description of two implementations.\n *\n * PLACEMENT, which differs by platform and is easy to get wrong:\n * - macOS keeps About in the APP menu (the one named after the product), and\n * Help carries documentation + Check for Updates only.\n * - Windows and Linux have no app menu, so About goes at the foot of Help,\n * after a separator. That is the conventional home there.\n */\nexport const HELP_MENU: {\n checkForUpdates: MenuLabel;\n} = {\n checkForUpdates: { label: \"Check for Updates\\u2026\" },\n};\n\n/** The app-menu items the family shares (macOS; the identity item also belongs in File elsewhere). */\nexport const APP_MENU: {\n userInfo: MenuLabel;\n} = {\n /** Name + optional email, used to sign edits and comments. One label across the suite. */\n userInfo: { label: \"User Information\\u2026\" },\n};\n\n/** What an app must tell the shell to build its named menu items. */\nexport interface MenuNaming {\n /** The product, as it appears in menus: \"Patterpad\", \"Storyletter\". */\n appName: string;\n /** The product's own documentation URL. Omit while an app has no docs yet and\n * the item is rendered disabled rather than opening a broken link. */\n docsUrl?: string;\n /** The suite's documentation home, shared by every app in the family. */\n suiteDocsUrl?: string;\n /** The suite, as it appears in \"<suite> Documentation Home\". Defaults to \"Patter\". */\n suiteName?: string;\n}\n\n/** A named menu item: the label, plus the URL it opens (absent = nothing wired yet). */\nexport interface NamedMenuItem extends MenuLabel {\n url?: string;\n /** False when the app has not supplied a URL, so the builder can render it\n * disabled instead of opening nothing. A placeholder, honestly labelled. */\n ready: boolean;\n}\n\n/**\n * The menu labels that carry a product or suite name, so no app spells its own\n * About or Documentation item differently from the rest of the family.\n *\n * An app with no documentation site yet still gets the ITEM, marked\n * `ready: false`, so the menu has its family shape from day one and wiring the\n * URL later is a one-line change rather than a menu redesign.\n */\nexport function namedMenuItems(naming: MenuNaming): {\n about: NamedMenuItem;\n docs: NamedMenuItem;\n suiteDocs: NamedMenuItem;\n} {\n const suite = naming.suiteName ?? \"Patter\";\n return {\n about: { label: `About ${naming.appName}`, ready: true },\n docs: {\n label: `${naming.appName} Documentation`,\n ...(naming.docsUrl ? { url: naming.docsUrl } : {}),\n ready: naming.docsUrl !== undefined,\n },\n suiteDocs: {\n label: `${suite} Documentation Home`,\n ...(naming.suiteDocsUrl ? { url: naming.suiteDocsUrl } : {}),\n ready: naming.suiteDocsUrl !== undefined,\n },\n };\n}\n\n// --- the rest of the spine (ui-review-2026-09, finding 12) -----------------------\n// Each app used roughly half of what is above and hand-typed the other half,\n// and the copies had already drifted (Review Feedback's accelerator spelled\n// two ways; \"No Recent Projects\" vs \"No recent projects\"). Labels are Title\n// Case, as the platform's menus are; an item that opens something ends in the\n// command ellipsis.\n\n/** The File menu items the family shares. */\nexport const FILE_MENU: {\n newProject: MenuLabel;\n openProject: MenuLabel;\n openRecent: MenuLabel;\n /** The empty Open Recent submenu's one disabled item. */\n noRecents: MenuLabel;\n clearRecents: MenuLabel;\n closeProject: MenuLabel;\n save: MenuLabel;\n saveAs: MenuLabel;\n projectSettings: MenuLabel;\n} = {\n newProject: { label: \"New Project…\", accelerator: \"CmdOrCtrl+N\" },\n openProject: { label: \"Open Project…\", accelerator: \"CmdOrCtrl+O\" },\n openRecent: { label: \"Open Recent\" },\n noRecents: { label: \"No Recent Projects\" },\n clearRecents: { label: \"Clear Recents\" },\n closeProject: { label: \"Close Project\" },\n save: { label: \"Save\", accelerator: \"CmdOrCtrl+S\" },\n saveAs: { label: \"Save As…\", accelerator: \"Shift+CmdOrCtrl+S\" },\n projectSettings: { label: \"Project Settings…\", accelerator: \"CmdOrCtrl+,\" },\n};\n\n/**\n * The Play menu's shared items. The play SURFACE is app-NAMED (Patterpad's\n * \"Play Scene\", Storyletter's \"The Board\": the two are not the same act), but\n * the family KEYS it the same in both, Cmd+P (ruling O, 2026-10), so `play`\n * carries the accelerator and no label: spread it beside the app's own,\n * `{ label: \"Play Scene\", ...PLAY_MENU.play }`.\n */\nexport const PLAY_MENU: {\n play: { accelerator: string };\n liveLink: MenuLabel;\n} = {\n play: { accelerator: \"CmdOrCtrl+P\" },\n liveLink: { label: \"Live Link\" },\n};\n\n/** The Review menu: examining what you have built rather than building or\n * playing it. Review Feedback's accelerator is spelled ONCE, here. */\nexport const REVIEW_MENU: {\n reviewFeedback: MenuLabel;\n nextFeedback: MenuLabel;\n previousFeedback: MenuLabel;\n coverageTest: MenuLabel;\n findPropertyUsage: MenuLabel;\n showResolvedComments: MenuLabel;\n} = {\n reviewFeedback: { label: \"Review Feedback\", accelerator: \"CmdOrCtrl+Shift+R\" },\n nextFeedback: { label: \"Next Feedback\", accelerator: \"F8\" },\n previousFeedback: { label: \"Previous Feedback\", accelerator: \"Shift+F8\" },\n coverageTest: { label: \"Coverage Test…\", accelerator: \"Shift+CmdOrCtrl+C\" },\n findPropertyUsage: { label: \"Find Property Usage…\" },\n showResolvedComments: { label: \"Show Resolved Comments\" },\n};\n\n/** The Publish menu: everything that turns the project into something you\n * hand to others (\"Publish\", never \"Export\"). */\nexport const PUBLISH_MENU: {\n playableHtml: MenuLabel;\n bundle: MenuLabel;\n autoRebuild: MenuLabel;\n} = {\n playableHtml: { label: \"Publish Playable HTML…\" },\n bundle: { label: \"Publish Bundle\", accelerator: \"Shift+CmdOrCtrl+B\" },\n autoRebuild: { label: \"Auto Rebuild\" },\n};\n\n/** The View menu items beyond the pane toggles (PANE_MENU) and history\n * (GO_MENU). Cmd+[ is family-reserved for Up a Level, which is why Back\n * takes Xcode's pair on the mac. */\nexport const VIEW_MENU: {\n projectOverview: MenuLabel;\n upALevel: MenuLabel;\n colourTheme: MenuLabel;\n} = {\n projectOverview: { label: \"Project Overview\" },\n upALevel: { label: \"Up a Level\", accelerator: \"CmdOrCtrl+[\" },\n colourTheme: { label: \"Colour Theme\" },\n};\n\n/** A path with the home directory as `~`, so a recents entry stays readable\n * at menu width. `home` is `app.getPath(\"home\")`; this file stays\n * electron-free. */\nexport function tildePath(path: string, home: string): string {\n if (path === home) return \"~\";\n const sep = home.includes(\"\\\\\") ? \"\\\\\" : \"/\";\n return path.startsWith(home + sep) ? `~${path.slice(home.length)}` : path;\n}\n\n/** As much of Electron's MenuItemConstructorOptions as a recents entry needs;\n * structurally assignable to it. */\nexport interface RecentsMenuItem {\n label?: string;\n sublabel?: string;\n toolTip?: string;\n enabled?: boolean;\n type?: \"separator\";\n click?: () => void;\n}\n\nexport interface RecentsSubmenuOptions {\n onOpen: (path: string) => void;\n /** Offered after a separator when there is anything to clear. */\n onClear?: () => void;\n /** `app.getPath(\"home\")`, for the `~` abbreviation. */\n home: string;\n /** Defaults to process.platform; a seam for the tests. */\n platform?: string;\n}\n\n/**\n * The Open Recent submenu: the fifteen lines both apps had.\n *\n * Shows WHERE each recent lives. macOS renders `sublabel` as a dimmed second\n * line and `toolTip` on hover; native menus elsewhere render neither, so\n * there the path is folded into the label itself. Empty, it is the one\n * disabled \"No Recent Projects\" item.\n */\nexport function recentsSubmenu(recents: RecentProject[], opts: RecentsSubmenuOptions): RecentsMenuItem[] {\n const isMac = (opts.platform ?? process.platform) === \"darwin\";\n if (recents.length === 0) return [{ label: FILE_MENU.noRecents.label, enabled: false }];\n const items: RecentsMenuItem[] = recents.map((r) => {\n const shown = tildePath(r.path, opts.home);\n const name = r.name ?? r.path.split(/[\\\\/]/).filter(Boolean).pop() ?? r.path;\n return {\n label: isMac ? name : `${name} (${shown})`,\n sublabel: shown,\n toolTip: r.path,\n click: () => opts.onOpen(r.path),\n };\n });\n if (opts.onClear) items.push({ type: \"separator\" }, { label: FILE_MENU.clearRecents.label, click: opts.onClear });\n return items;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { copyWithFeedback } from './chunk-QVACRWG7.js';
|
|
2
|
-
import { el, iconNode } from './chunk-
|
|
2
|
+
import { el, iconNode } from './chunk-W4WC4FO5.js';
|
|
3
3
|
|
|
4
4
|
// src/link-status.ts
|
|
5
5
|
function linkStatusClass(s) {
|
|
@@ -96,5 +96,5 @@ function mountLinkStatus(host, opts) {
|
|
|
96
96
|
}
|
|
97
97
|
|
|
98
98
|
export { linkAddress, linkStatusClass, linkStatusTip, mountLinkStatus };
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
100
|
-
//# sourceMappingURL=chunk-
|
|
99
|
+
//# sourceMappingURL=chunk-UQTUCOGV.js.map
|
|
100
|
+
//# sourceMappingURL=chunk-UQTUCOGV.js.map
|