@povio/ui 5.0.0-rc.12 → 5.0.0-rc.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +9 -1
  2. package/dist/{AppCanvasPage-DCjiFfQW.js → AppCanvasPage-vta1j5YQ.js} +583 -847
  3. package/dist/{CanvasComponentsFrame-CZXY0Dxi.js → CanvasComponentsFrame-D6Mh_G3q.js} +1 -1
  4. package/dist/{ComponentGallery-D4U5hXZL.js → ComponentGallery-CzKU899_.js} +2 -2
  5. package/dist/{Form-DmcByQts.d.ts → Form-DAtj2szV.d.ts} +1 -1
  6. package/dist/{Inputs-C0sUmuPv.d.ts → Inputs-C_6dVwC7.d.ts} +1 -1
  7. package/dist/{Table-BN2_J_Ne.js → Table-piX1SWvN.js} +54 -1
  8. package/dist/{UIConfigurator-CBU0G6rb.js → UIConfigurator-CjAW1SJY.js} +1521 -318
  9. package/dist/config/app-canvas-tanstack.js +1 -1
  10. package/dist/config/app-canvas.d.ts +1 -1
  11. package/dist/config/app-canvas.js +1 -1
  12. package/dist/config/ui-config.d.ts +1 -1
  13. package/dist/config/ui-configurator-vite.d.ts +9 -0
  14. package/dist/config/ui-configurator-vite.js +560 -184
  15. package/dist/config/ui-configurator.d.ts +14 -2
  16. package/dist/config/ui-configurator.js +2 -2
  17. package/dist/{configuration.context-iuZu-ie0.d.ts → configuration.context-3I7-wBII.d.ts} +3 -2
  18. package/dist/{dynamicInputs-DdcsSoYu.d.ts → dynamicInputs-R5i7AT_C.d.ts} +1 -1
  19. package/dist/index.d.ts +2 -2
  20. package/dist/index.js +3 -3
  21. package/dist/rhf/inputs.d.ts +1 -1
  22. package/dist/rhf.d.ts +2 -2
  23. package/dist/rhf.js +3 -3
  24. package/dist/styles/app-canvas.css +15 -42
  25. package/dist/styles/configurator.css +453 -92
  26. package/dist/{table.cva-D5oQ-QvA.js → table.cva-DlMT-zLD.js} +1 -54
  27. package/dist/tanstack/form.d.ts +1 -1
  28. package/dist/tanstack/inputs.d.ts +2 -2
  29. package/dist/tanstack/table.js +2 -2
  30. package/dist/tanstack.d.ts +4 -4
  31. package/dist/tanstack.js +3 -3
  32. package/dist/{uiConfig.context-BqKWd_MO.d.ts → uiConfig.context-BksnNQyw.d.ts} +1 -1
  33. package/dist/{useTableColumnConfig-6zn3zKl3.js → useTableColumnConfig-CNJ_ZvIN.js} +1 -1
  34. package/package.json +1 -1
  35. package/src/configurator/README.md +18 -1
@@ -3,7 +3,6 @@ import { t as UIConfig } from "./uiConfig.context-BS3r_kTS.js";
3
3
  import { t as Check } from "./check-DYMBkp_Z.js";
4
4
  import { t as ChevronDown } from "./chevron-down-B4Pr8bD3.js";
5
5
  import { t as ChevronRight } from "./chevron-right-3nKJR5Bb.js";
6
- import { a as tableHeaderTextDefinition, c as GripVertical, i as tableHeadRowDefinition, n as tableDataDefinition, o as tableRowDefinition, r as tableHeadDataDefinition, s as theadDefinition, t as tableCellTextDefinition } from "./table.cva-D5oQ-QvA.js";
7
6
  import { t as Info } from "./info-iUmX3uDk.js";
8
7
  import { a as checkboxDefinition, i as checkboxContentWrapperDefinition, o as checkboxGroupLabelDefinition, r as checkboxContentRowDefinition, s as checkboxIconDefinition, u as Minus } from "./CheckboxCheckmark-FwKjPVbV.js";
9
8
  import { i as Search, n as selectListBoxItemDefinition, r as selectInputTagsContentWrapperDefinition, t as selectPopoverDefinition } from "./selectDesktop.cva-CHAMxhfO.js";
@@ -19,6 +18,7 @@ import { t as $2ec61d1d4f780267$export$a8a3e93435678ff9 } from "./Heading-nI1_VO
19
18
  import { t as $efe09c6d1c304b50$export$9afb8bc826b033ea } from "./Text-D2MPZ3sY.js";
20
19
  import { n as $1f3c3b1a70cec653$export$ff05c3ac10437e03, t as $cdaed739b1139372$export$c24727297075ec6a } from "./Form-DlQcyN1f.js";
21
20
  import { t as Checkbox } from "./Checkbox-D3f2Plbc.js";
21
+ import { t as $aa5f63badbfee5df$exports } from "./intlStrings-chfsan9H.js";
22
22
  import { a as $928221da08ecbc62$export$dca12b0bb56e4fc, i as $928221da08ecbc62$export$a11e76429ed99b4, n as $928221da08ecbc62$export$7c5906fe4f1f2af2, r as $928221da08ecbc62$export$7ff8f37d2d81a48d, t as $928221da08ecbc62$export$41f133550aa26f48 } from "./ListBox-CL8Ar4Q_.js";
23
23
  import { n as $8b8d26808cb8cb53$export$8948f78d83984c69, t as $8b8d26808cb8cb53$export$2b77a92f1a5ad772 } from "./Modal-w60dVQcb.js";
24
24
  import { i as radioDefinition, n as radioContentRowDefinition, r as radioContentWrapperDefinition, t as RadioGroup } from "./RadioGroup-efBKgTYA.js";
@@ -46,10 +46,11 @@ import { a as stepperSeparatorDefinition, i as stepperNumberDefinition, n as ste
46
46
  import { i as dialogOverlayDefinition, n as dialogMainDefinition, t as dialogContentDefinition } from "./dialog.cva-COhDZaVl.js";
47
47
  import { i as alertDefinition, n as statusIconDefinition, r as statusSeparatorDefinition, t as Alert } from "./Alert-CnvEb1D_.js";
48
48
  import { a as toastDefinition, i as toastContainerDefinition } from "./toast.cva-C_L_RffH.js";
49
+ import { a as tableHeaderTextDefinition, i as tableHeadRowDefinition, n as tableDataDefinition, o as tableRowDefinition, r as tableHeadDataDefinition, s as theadDefinition, t as tableCellTextDefinition } from "./table.cva-DlMT-zLD.js";
49
50
  import { t as linkDefinition } from "./link.cva-DMTHZiJG.js";
50
51
  import { t as tooltipWrapperTriggerDefinition } from "./tooltipWrapper.cva-BBQ4ic3D.js";
51
- import { C as toRgb, S as toHex, _ as listedLayoutToken, a as generateShades, b as runtimeCss, c as isEditableToken, d as labelFor, f as layoutKindFor, g as linkedLayoutTokens, h as layoutScales, i as exportCss, l as isFontSizeToken, m as layoutLevelFor, n as controlHeightCounterpart, o as groupFor, p as layoutKinds, r as emptyTheme, s as groups, t as baseColorNames, u as isRgb, w as validToken, x as spacingLevels, y as previewTokens } from "./theme-model-DtTsIxou.js";
52
- import $ahdqC$react, { Fragment, Suspense, createContext, lazy, use, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
52
+ import { C as toRgb, S as toHex, _ as listedLayoutToken, a as generateShades, b as runtimeCss, c as isEditableToken, d as labelFor, f as layoutKindFor, g as linkedLayoutTokens, h as layoutScales, i as exportCss, l as isFontSizeToken, m as layoutLevelFor, n as controlHeightCounterpart, o as groupFor, p as layoutKinds, r as emptyTheme, s as groups, t as baseColorNames, u as isRgb, v as parseThemeCss, w as validToken, x as spacingLevels, y as previewTokens } from "./theme-model-DtTsIxou.js";
53
+ import $ahdqC$react, { Fragment, Suspense, createContext, lazy, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
53
54
  import { c } from "react/compiler-runtime";
54
55
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
55
56
  import { mergeProps } from "react-aria/mergeProps";
@@ -58,8 +59,8 @@ import { filterDOMProps } from "react-aria/filterDOMProps";
58
59
  import { useFocusRing } from "react-aria/useFocusRing";
59
60
  import { createHideableComponent } from "react-aria/private/collections/Hidden";
60
61
  import { createPortal } from "react-dom";
61
- import "react-aria/useLocalizedStringFormatter";
62
- import "react-aria/useListFormatter";
62
+ import { useLocalizedStringFormatter } from "react-aria/useLocalizedStringFormatter";
63
+ import { useListFormatter } from "react-aria/useListFormatter";
63
64
  import { HiddenSelect, useSelect } from "react-aria/useSelect";
64
65
  import { useSelectState } from "react-stately/useSelectState";
65
66
  import { UNSAFE_PortalProvider } from "react-aria";
@@ -70,7 +71,7 @@ import { UNSAFE_PortalProvider } from "react-aria";
70
71
  * This source code is licensed under the ISC license.
71
72
  * See the LICENSE file in the root directory of this source tree.
72
73
  */
73
- const __iconData$9 = {
74
+ const __iconData$10 = {
74
75
  name: "arrow-down-to-line",
75
76
  size: 24,
76
77
  node: [
@@ -88,8 +89,8 @@ const __iconData$9 = {
88
89
  }]
89
90
  ]
90
91
  };
91
- __iconData$9.node;
92
- const ArrowDownToLine = createLucideIcon(__iconData$9);
92
+ __iconData$10.node;
93
+ const ArrowDownToLine = createLucideIcon(__iconData$10);
93
94
  //#endregion
94
95
  //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/chevrons-up-down.mjs
95
96
  /**
@@ -98,7 +99,7 @@ const ArrowDownToLine = createLucideIcon(__iconData$9);
98
99
  * This source code is licensed under the ISC license.
99
100
  * See the LICENSE file in the root directory of this source tree.
100
101
  */
101
- const __iconData$8 = {
102
+ const __iconData$9 = {
102
103
  name: "chevrons-up-down",
103
104
  size: 24,
104
105
  node: [["path", {
@@ -109,8 +110,8 @@ const __iconData$8 = {
109
110
  key: "sgt6xg"
110
111
  }]]
111
112
  };
112
- __iconData$8.node;
113
- const ChevronsUpDown = createLucideIcon(__iconData$8);
113
+ __iconData$9.node;
114
+ const ChevronsUpDown = createLucideIcon(__iconData$9);
114
115
  //#endregion
115
116
  //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/external-link.mjs
116
117
  /**
@@ -119,7 +120,7 @@ const ChevronsUpDown = createLucideIcon(__iconData$8);
119
120
  * This source code is licensed under the ISC license.
120
121
  * See the LICENSE file in the root directory of this source tree.
121
122
  */
122
- const __iconData$7 = {
123
+ const __iconData$8 = {
123
124
  name: "external-link",
124
125
  size: 24,
125
126
  node: [
@@ -137,8 +138,8 @@ const __iconData$7 = {
137
138
  }]
138
139
  ]
139
140
  };
140
- __iconData$7.node;
141
- const ExternalLink = createLucideIcon(__iconData$7);
141
+ __iconData$8.node;
142
+ const ExternalLink = createLucideIcon(__iconData$8);
142
143
  //#endregion
143
144
  //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/moon.mjs
144
145
  /**
@@ -147,7 +148,7 @@ const ExternalLink = createLucideIcon(__iconData$7);
147
148
  * This source code is licensed under the ISC license.
148
149
  * See the LICENSE file in the root directory of this source tree.
149
150
  */
150
- const __iconData$6 = {
151
+ const __iconData$7 = {
151
152
  name: "moon",
152
153
  size: 24,
153
154
  node: [["path", {
@@ -155,8 +156,8 @@ const __iconData$6 = {
155
156
  key: "kfwtm"
156
157
  }]]
157
158
  };
158
- __iconData$6.node;
159
- const Moon = createLucideIcon(__iconData$6);
159
+ __iconData$7.node;
160
+ const Moon = createLucideIcon(__iconData$7);
160
161
  //#endregion
161
162
  //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/panel-top.mjs
162
163
  /**
@@ -165,7 +166,7 @@ const Moon = createLucideIcon(__iconData$6);
165
166
  * This source code is licensed under the ISC license.
166
167
  * See the LICENSE file in the root directory of this source tree.
167
168
  */
168
- const __iconData$5 = {
169
+ const __iconData$6 = {
169
170
  name: "panel-top",
170
171
  size: 24,
171
172
  node: [["rect", {
@@ -180,8 +181,29 @@ const __iconData$5 = {
180
181
  key: "1pudct"
181
182
  }]]
182
183
  };
184
+ __iconData$6.node;
185
+ const PanelTop = createLucideIcon(__iconData$6);
186
+ //#endregion
187
+ //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/pencil.mjs
188
+ /**
189
+ * @license lucide-react v1.43.0 - ISC
190
+ *
191
+ * This source code is licensed under the ISC license.
192
+ * See the LICENSE file in the root directory of this source tree.
193
+ */
194
+ const __iconData$5 = {
195
+ name: "pencil",
196
+ size: 24,
197
+ node: [["path", {
198
+ d: "M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z",
199
+ key: "1a8usu"
200
+ }], ["path", {
201
+ d: "m15 5 4 4",
202
+ key: "1mk7zo"
203
+ }]]
204
+ };
183
205
  __iconData$5.node;
184
- const PanelTop = createLucideIcon(__iconData$5);
206
+ const Pencil = createLucideIcon(__iconData$5);
185
207
  //#endregion
186
208
  //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/pin.mjs
187
209
  /**
@@ -246,58 +268,6 @@ const __iconData$2 = {
246
268
  __iconData$2.node;
247
269
  const RotateCcw = createLucideIcon(__iconData$2);
248
270
  //#endregion
249
- //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.mjs
250
- /**
251
- * @license lucide-react v1.43.0 - ISC
252
- *
253
- * This source code is licensed under the ISC license.
254
- * See the LICENSE file in the root directory of this source tree.
255
- */
256
- const __iconData$1 = {
257
- name: "sliders-horizontal",
258
- size: 24,
259
- node: [
260
- ["path", {
261
- d: "M10 5H3",
262
- key: "1qgfaw"
263
- }],
264
- ["path", {
265
- d: "M12 19H3",
266
- key: "yhmn1j"
267
- }],
268
- ["path", {
269
- d: "M14 3v4",
270
- key: "1sua03"
271
- }],
272
- ["path", {
273
- d: "M16 17v4",
274
- key: "1q0r14"
275
- }],
276
- ["path", {
277
- d: "M21 12h-9",
278
- key: "1o4lsq"
279
- }],
280
- ["path", {
281
- d: "M21 19h-5",
282
- key: "1rlt1p"
283
- }],
284
- ["path", {
285
- d: "M21 5h-7",
286
- key: "1oszz2"
287
- }],
288
- ["path", {
289
- d: "M8 10v4",
290
- key: "tgpxqk"
291
- }],
292
- ["path", {
293
- d: "M8 12H3",
294
- key: "a7s4jb"
295
- }]
296
- ]
297
- };
298
- __iconData$1.node;
299
- const SlidersHorizontal = createLucideIcon(__iconData$1);
300
- //#endregion
301
271
  //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/sun.mjs
302
272
  /**
303
273
  * @license lucide-react v1.43.0 - ISC
@@ -305,7 +275,7 @@ const SlidersHorizontal = createLucideIcon(__iconData$1);
305
275
  * This source code is licensed under the ISC license.
306
276
  * See the LICENSE file in the root directory of this source tree.
307
277
  */
308
- const __iconData = {
278
+ const __iconData$1 = {
309
279
  name: "sun",
310
280
  size: 24,
311
281
  node: [
@@ -349,10 +319,50 @@ const __iconData = {
349
319
  }]
350
320
  ]
351
321
  };
322
+ __iconData$1.node;
323
+ const Sun = createLucideIcon(__iconData$1);
324
+ //#endregion
325
+ //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/trash.mjs
326
+ /**
327
+ * @license lucide-react v1.43.0 - ISC
328
+ *
329
+ * This source code is licensed under the ISC license.
330
+ * See the LICENSE file in the root directory of this source tree.
331
+ */
332
+ const __iconData = {
333
+ name: "trash",
334
+ size: 24,
335
+ node: [
336
+ ["path", {
337
+ d: "M10 11v6",
338
+ key: "nco0om"
339
+ }],
340
+ ["path", {
341
+ d: "M14 11v6",
342
+ key: "outv1u"
343
+ }],
344
+ ["path", {
345
+ d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6",
346
+ key: "miytrc"
347
+ }],
348
+ ["path", {
349
+ d: "M3 6h18",
350
+ key: "d0wm0j"
351
+ }],
352
+ ["path", {
353
+ d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2",
354
+ key: "e791ji"
355
+ }]
356
+ ],
357
+ aliases: ["trash-2"]
358
+ };
352
359
  __iconData.node;
353
- const Sun = createLucideIcon(__iconData);
360
+ const Trash = createLucideIcon(__iconData);
354
361
  //#endregion
355
362
  //#region ../../node_modules/.bun/react-aria-components@1.21.1+67f6792bdf102c28/node_modules/react-aria-components/dist/private/Select.mjs
363
+ function $parcel$interopDefault(a) {
364
+ return a && a.__esModule ? a.default : a;
365
+ }
356
366
  const $c8bb816105474884$export$7540cee5be7dc19b = /*#__PURE__*/ createContext(null);
357
367
  const $c8bb816105474884$export$ef445b55be0601bd = /*#__PURE__*/ createContext(null);
358
368
  const $c8bb816105474884$export$ef9b1a59e592288f = /*#__PURE__*/ createHideableComponent(function Select(props, ref) {
@@ -473,6 +483,68 @@ function $c8bb816105474884$var$SelectInner({ props, selectRef: ref, collection }
473
483
  })));
474
484
  }
475
485
  const $c8bb816105474884$export$f8f745c04421623f = /*#__PURE__*/ createContext(null);
486
+ const $c8bb816105474884$export$e288731fd71264f0 = /*#__PURE__*/ createHideableComponent(function SelectValue(props, ref) {
487
+ [props, ref] = $7230ffa83bc0c2cf$export$29f1550f4b0d4415(props, ref, $c8bb816105474884$export$f8f745c04421623f);
488
+ let state = useContext($c8bb816105474884$export$ef445b55be0601bd);
489
+ let { placeholder } = $7230ffa83bc0c2cf$export$fabf2dc03a41866e($c8bb816105474884$export$7540cee5be7dc19b);
490
+ let rendered = state.selectedItems.map((item) => {
491
+ let rendered = item.props?.children;
492
+ if (typeof rendered === "function") rendered = rendered({
493
+ isHovered: false,
494
+ isPressed: false,
495
+ isSelected: false,
496
+ isFocused: false,
497
+ isFocusVisible: false,
498
+ isDisabled: false,
499
+ selectionMode: "single",
500
+ selectionBehavior: "toggle"
501
+ });
502
+ return rendered;
503
+ });
504
+ let formatter = useListFormatter();
505
+ let textValue = useMemo(() => state.selectedItems.map((item) => item?.textValue), [state.selectedItems]);
506
+ let selectionMode = state.selectionManager.selectionMode;
507
+ let selectedText = useMemo(() => selectionMode === "single" ? textValue[0] ?? "" : formatter.format(textValue), [
508
+ selectionMode,
509
+ formatter,
510
+ textValue
511
+ ]);
512
+ let defaultChildren = useMemo(() => {
513
+ if (selectionMode === "single") return rendered[0];
514
+ let parts = formatter.formatToParts(textValue);
515
+ if (parts.length === 0) return null;
516
+ let index = 0;
517
+ return parts.map((part) => {
518
+ if (part.type === "element") return /*#__PURE__*/ $ahdqC$react.createElement(Fragment, { key: index }, rendered[index++]);
519
+ else return part.value;
520
+ });
521
+ }, [
522
+ selectionMode,
523
+ formatter,
524
+ textValue,
525
+ rendered
526
+ ]);
527
+ let stringFormatter = useLocalizedStringFormatter($parcel$interopDefault($aa5f63badbfee5df$exports), "react-aria-components");
528
+ let renderProps = $7230ffa83bc0c2cf$export$4d86445c2cf5e3({
529
+ ...props,
530
+ defaultChildren: defaultChildren ?? placeholder ?? stringFormatter.format("selectPlaceholder"),
531
+ defaultClassName: "react-aria-SelectValue",
532
+ values: {
533
+ selectedItem: state.selectedItems[0]?.value ?? null,
534
+ selectedItems: useMemo(() => state.selectedItems.map((item) => item.value ?? null), [state.selectedItems]),
535
+ selectedText,
536
+ isPlaceholder: state.selectedItems.length === 0,
537
+ state
538
+ }
539
+ });
540
+ let DOMProps = filterDOMProps(props, { global: true });
541
+ return /*#__PURE__*/ $ahdqC$react.createElement($7230ffa83bc0c2cf$export$df3a06d6289f983e.span, {
542
+ ref,
543
+ ...DOMProps,
544
+ ...renderProps,
545
+ "data-placeholder": state.selectedItems.length === 0 || void 0
546
+ }, /*#__PURE__*/ $ahdqC$react.createElement($efe09c6d1c304b50$export$9afb8bc826b033ea.Provider, { value: void 0 }, renderProps.children));
547
+ });
476
548
  //#endregion
477
549
  //#region src/configurator/cva-definitions.ts
478
550
  const cvaDefinitions = {
@@ -623,7 +695,7 @@ function UIConfigurationProvider(t0) {
623
695
  let t1;
624
696
  if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
625
697
  t1 = () => {
626
- setComponentHosts(_temp$7);
698
+ setComponentHosts(_temp$8);
627
699
  return () => setComponentHosts(_temp2$5);
628
700
  };
629
701
  $[0] = t1;
@@ -631,12 +703,14 @@ function UIConfigurationProvider(t0) {
631
703
  const hostComponents = t1;
632
704
  let t2;
633
705
  if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
634
- t2 = (value, t3) => {
635
- if (t3 === void 0 ? false : t3) setOpen(false);
706
+ t2 = (value, t3, t4) => {
707
+ const closeEditor = t3 === void 0 ? false : t3;
708
+ const remount = t4 === void 0 ? true : t4;
709
+ if (closeEditor) setOpen(false);
636
710
  setApplied(structuredClone(value));
637
711
  setDraft(structuredClone(value));
638
712
  setPreviewConfiguration(null);
639
- setRevision(_temp3$4);
713
+ if (remount) setRevision(_temp3$4);
640
714
  };
641
715
  $[1] = t2;
642
716
  } else t2 = $[1];
@@ -657,7 +731,7 @@ function UIConfigurationProvider(t0) {
657
731
  let t4;
658
732
  if ($[4] !== isOpen || $[5] !== open) {
659
733
  t4 = () => {
660
- if (isOpen) setRevision(_temp5$2);
734
+ if (isOpen) setRevision(_temp5$3);
661
735
  else open();
662
736
  setFloating(true);
663
737
  };
@@ -809,7 +883,7 @@ function UIConfigurationProvider(t0) {
809
883
  } else t17 = $[49];
810
884
  return t17;
811
885
  }
812
- function _temp5$2(value_1) {
886
+ function _temp5$3(value_1) {
813
887
  return value_1 + 1;
814
888
  }
815
889
  function _temp4$4(value_0) {
@@ -821,7 +895,7 @@ function _temp3$4(current) {
821
895
  function _temp2$5(count_0) {
822
896
  return count_0 - 1;
823
897
  }
824
- function _temp$7(count) {
898
+ function _temp$8(count) {
825
899
  return count + 1;
826
900
  }
827
901
  function useUIConfiguration() {
@@ -830,6 +904,360 @@ function useUIConfiguration() {
830
904
  return context;
831
905
  }
832
906
  //#endregion
907
+ //#region src/configurator/theme-writer-protocol.ts
908
+ /** Shared by the browser configurator and the isolated Node Vite entry. */
909
+ const themeWriterMetaName = "povio-ui-theme-writer";
910
+ const themeWriterTokenHeader = "x-povio-ui-theme-token";
911
+ //#endregion
912
+ //#region src/configurator/theme-writer-client.ts
913
+ function getThemeWriter() {
914
+ if (typeof document === "undefined") return null;
915
+ const content = document.querySelector(`meta[name="${themeWriterMetaName}"]`)?.content;
916
+ if (!content) return null;
917
+ try {
918
+ const connection = JSON.parse(content);
919
+ if (typeof connection.endpoint === "string" && connection.endpoint.startsWith("/") && !connection.endpoint.startsWith("//") && typeof connection.token === "string" && typeof connection.file === "string") return connection;
920
+ } catch {}
921
+ return null;
922
+ }
923
+ async function saveThemeCss(connection, css) {
924
+ const response = await fetch(connection.endpoint, {
925
+ method: "POST",
926
+ headers: {
927
+ "Content-Type": "application/json",
928
+ [themeWriterTokenHeader]: connection.token
929
+ },
930
+ body: JSON.stringify({ css })
931
+ });
932
+ if (!response.ok) {
933
+ const result = await response.json().catch(() => null);
934
+ throw new Error(result?.error ?? `Theme save failed (${response.status}).`);
935
+ }
936
+ }
937
+ //#endregion
938
+ //#region src/configurator/fontsource-client.ts
939
+ const validId = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
940
+ let catalog;
941
+ /** One metadata request per page, not one font download per search result. */
942
+ function loadFontsourceCatalog() {
943
+ catalog ??= fetch("https://api.fontsource.org/v1/fonts", { signal: AbortSignal.timeout(15e3) }).then(async (response) => {
944
+ if (!response.ok) throw new Error("Fontsource is unavailable. Try again.");
945
+ const data = await response.json();
946
+ if (!Array.isArray(data)) throw new Error("Fontsource returned an invalid catalog.");
947
+ return data.filter((font) => font && typeof font.id === "string" && font.id.length <= 100 && validId.test(font.id) && typeof font.family === "string" && typeof font.category === "string" && typeof font.variable === "boolean" && Array.isArray(font.subsets) && font.subsets.every((s) => typeof s === "string" && validId.test(s)) && typeof font.defSubset === "string" && font.subsets.includes(font.defSubset) && Array.isArray(font.weights) && font.weights.length > 0 && font.weights.every((w) => typeof w === "number" && w > 0 && w <= 1e3) && Array.isArray(font.styles) && (font.styles.includes("normal") || font.styles.includes("italic")) && font.type !== "icons").sort((a, b) => a.family.localeCompare(b.family));
948
+ }).catch((error) => {
949
+ catalog = void 0;
950
+ throw error;
951
+ });
952
+ return catalog;
953
+ }
954
+ function fontsourceWriter() {
955
+ const writer = getThemeWriter();
956
+ const fonts = writer?.fontsource;
957
+ return fonts && typeof fonts.endpoint === "string" && fonts.endpoint.startsWith("/") && !fonts.endpoint.startsWith("//") && typeof fonts.cssFile === "string" ? {
958
+ ...fonts,
959
+ token: writer.token
960
+ } : null;
961
+ }
962
+ async function addFontsourceFamily(id) {
963
+ const writer = fontsourceWriter();
964
+ if (!writer) throw new Error("Adding fonts requires the Povio UI Vite plugin in a development server.");
965
+ const response = await fetch(writer.endpoint, {
966
+ method: "POST",
967
+ headers: {
968
+ "Content-Type": "application/json",
969
+ [themeWriterTokenHeader]: writer.token
970
+ },
971
+ body: JSON.stringify({ id })
972
+ });
973
+ const result = await response.json();
974
+ if (!response.ok) throw new Error(result.error ?? "Could not add this font to the app.");
975
+ if (typeof result.family !== "string" || typeof result.packageName !== "string" || !Array.isArray(result.imports)) throw new Error("The font installer returned an invalid response.");
976
+ return result;
977
+ }
978
+ /** The static preview is temporary; installed variable families use their full range. */
979
+ function fontsourcePreview(font) {
980
+ const subset = font.subsets.includes("latin") ? "latin" : font.defSubset;
981
+ const weight = font.weights.includes(400) ? 400 : font.weights[0];
982
+ const style = font.styles.includes("normal") ? "normal" : "italic";
983
+ return {
984
+ alias: `Povio Fontsource preview ${font.id}`,
985
+ weight,
986
+ style,
987
+ url: `https://cdn.jsdelivr.net/fontsource/fonts/${font.id}@latest/${subset}-${weight}-${style}.woff2`
988
+ };
989
+ }
990
+ //#endregion
991
+ //#region src/configurator/FontsourcePicker.tsx
992
+ function FontsourcePicker({ roles, onAdded }) {
993
+ const trigger = useRef(null);
994
+ const mounted = useRef(true);
995
+ useEffect(() => {
996
+ mounted.current = true;
997
+ return () => {
998
+ mounted.current = false;
999
+ };
1000
+ }, []);
1001
+ const [open, setOpen] = useState(false);
1002
+ const [fonts, setFonts] = useState([]);
1003
+ const [loading, setLoading] = useState(false);
1004
+ const [query, setQuery] = useState("");
1005
+ const [limit, setLimit] = useState(30);
1006
+ const [category, setCategory] = useState("");
1007
+ const [variableOnly, setVariableOnly] = useState(false);
1008
+ useEffect(() => setLimit(30), [
1009
+ query,
1010
+ category,
1011
+ variableOnly
1012
+ ]);
1013
+ const [selected, setSelected] = useState(null);
1014
+ const [sample, setSample] = useState("The quick brown fox jumps over the lazy dog.");
1015
+ const [previewState, setPreviewState] = useState("loading");
1016
+ const [role, setRole] = useState(roles[0]?.token ?? "");
1017
+ const [adding, setAdding] = useState(false);
1018
+ const [error, setError] = useState("");
1019
+ const [notice, setNotice] = useState("");
1020
+ const [retry, setRetry] = useState(0);
1021
+ const writer = fontsourceWriter();
1022
+ useEffect(() => {
1023
+ if (!open) return;
1024
+ let active = true;
1025
+ setLoading(true);
1026
+ setError("");
1027
+ loadFontsourceCatalog().then((value) => {
1028
+ if (active) setFonts(value);
1029
+ }).catch((cause) => {
1030
+ if (active) setError(cause instanceof Error ? cause.message : "Could not load Fontsource.");
1031
+ }).finally(() => {
1032
+ if (active) setLoading(false);
1033
+ });
1034
+ return () => {
1035
+ active = false;
1036
+ };
1037
+ }, [open, retry]);
1038
+ useEffect(() => {
1039
+ if (!open || !selected) return;
1040
+ let active_0 = true;
1041
+ const doc = trigger.current?.ownerDocument ?? document;
1042
+ const preview = fontsourcePreview(selected);
1043
+ const face = new FontFace(preview.alias, `url("${preview.url}")`, {
1044
+ weight: String(preview.weight),
1045
+ style: preview.style
1046
+ });
1047
+ setPreviewState("loading");
1048
+ face.load().then((loaded) => {
1049
+ if (active_0) {
1050
+ doc.fonts.add(loaded);
1051
+ setPreviewState("ready");
1052
+ }
1053
+ }).catch(() => {
1054
+ if (active_0) setPreviewState("error");
1055
+ });
1056
+ return () => {
1057
+ active_0 = false;
1058
+ doc.fonts.delete(face);
1059
+ };
1060
+ }, [open, selected]);
1061
+ const filtered = useMemo(() => fonts.filter((font) => font.family.toLowerCase().includes(query.trim().toLowerCase()) && (!category || category === font.category) && (!variableOnly || font.variable)), [
1062
+ fonts,
1063
+ query,
1064
+ category,
1065
+ variableOnly
1066
+ ]);
1067
+ const preview_0 = selected ? fontsourcePreview(selected) : null;
1068
+ return /* @__PURE__ */ jsxs("div", {
1069
+ className: "studio-fontsource",
1070
+ children: [
1071
+ /* @__PURE__ */ jsx("button", {
1072
+ ref: trigger,
1073
+ type: "button",
1074
+ className: "studio-gallery-back",
1075
+ onClick: () => {
1076
+ setOpen(true);
1077
+ setError("");
1078
+ },
1079
+ children: "Browse Fontsource"
1080
+ }),
1081
+ notice && /* @__PURE__ */ jsx("span", {
1082
+ className: "studio-fontsource-notice",
1083
+ role: "status",
1084
+ children: notice
1085
+ }),
1086
+ /* @__PURE__ */ jsx($542a13ca2fa5b484$export$5b6b19405a83ff9d, {
1087
+ triggerRef: trigger,
1088
+ placement: "bottom start",
1089
+ offset: 6,
1090
+ isOpen: open,
1091
+ onOpenChange: (value_0) => {
1092
+ if (!adding) setOpen(value_0);
1093
+ },
1094
+ isKeyboardDismissDisabled: adding,
1095
+ className: "studio-fontsource-popover",
1096
+ children: /* @__PURE__ */ jsxs($f2ff30fde7b014be$export$3ddf2d174ce01153, {
1097
+ "aria-label": "Fontsource font picker",
1098
+ children: [
1099
+ /* @__PURE__ */ jsxs("header", { children: [/* @__PURE__ */ jsx($2ec61d1d4f780267$export$a8a3e93435678ff9, {
1100
+ slot: "title",
1101
+ children: "Fontsource fonts"
1102
+ }), /* @__PURE__ */ jsx("button", {
1103
+ type: "button",
1104
+ "aria-label": "Close font picker",
1105
+ disabled: adding,
1106
+ onClick: () => setOpen(false),
1107
+ children: /* @__PURE__ */ jsx(X, { size: 13 })
1108
+ })] }),
1109
+ /* @__PURE__ */ jsxs("label", {
1110
+ className: "studio-fontsource-search",
1111
+ children: [/* @__PURE__ */ jsx(Search, { size: 13 }), /* @__PURE__ */ jsx("input", {
1112
+ autoFocus: true,
1113
+ "aria-label": "Search Fontsource fonts",
1114
+ placeholder: "Find a font family…",
1115
+ value: query,
1116
+ onChange: (event) => setQuery(event.target.value)
1117
+ })]
1118
+ }),
1119
+ /* @__PURE__ */ jsxs("div", {
1120
+ className: "studio-fontsource-filters",
1121
+ children: [/* @__PURE__ */ jsxs("select", {
1122
+ "aria-label": "Font category",
1123
+ value: category,
1124
+ onChange: (event_0) => setCategory(event_0.target.value),
1125
+ children: [/* @__PURE__ */ jsx("option", {
1126
+ value: "",
1127
+ children: "All categories"
1128
+ }), [...new Set(fonts.map((font_0) => font_0.category))].sort().map((name) => /* @__PURE__ */ jsx("option", {
1129
+ value: name,
1130
+ children: name
1131
+ }, name))]
1132
+ }), /* @__PURE__ */ jsxs("label", { children: [
1133
+ /* @__PURE__ */ jsx("input", {
1134
+ type: "checkbox",
1135
+ checked: variableOnly,
1136
+ onChange: (event_1) => setVariableOnly(event_1.target.checked)
1137
+ }),
1138
+ " ",
1139
+ "Variable only"
1140
+ ] })]
1141
+ }),
1142
+ loading ? /* @__PURE__ */ jsx("p", {
1143
+ role: "status",
1144
+ children: "Loading font families…"
1145
+ }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
1146
+ /* @__PURE__ */ jsxs("span", {
1147
+ className: "studio-fontsource-count",
1148
+ children: [
1149
+ filtered.length,
1150
+ " families",
1151
+ filtered.length > limit ? ` · showing ${limit}` : ""
1152
+ ]
1153
+ }),
1154
+ /* @__PURE__ */ jsxs("div", {
1155
+ className: "studio-fontsource-list",
1156
+ role: "group",
1157
+ "aria-label": "Fontsource families",
1158
+ children: [filtered.slice(0, limit).map((font_1) => /* @__PURE__ */ jsxs("button", {
1159
+ type: "button",
1160
+ disabled: adding,
1161
+ "aria-pressed": selected?.id === font_1.id,
1162
+ onClick: () => {
1163
+ setSelected(font_1);
1164
+ setError("");
1165
+ },
1166
+ children: [
1167
+ /* @__PURE__ */ jsx("span", { children: font_1.family }),
1168
+ /* @__PURE__ */ jsxs("small", { children: [font_1.category, font_1.variable ? " · variable" : ""] }),
1169
+ selected?.id === font_1.id && /* @__PURE__ */ jsx(Check, { size: 12 })
1170
+ ]
1171
+ }, font_1.id)), !filtered.length && !error && /* @__PURE__ */ jsx("p", { children: "No matching font families." })]
1172
+ }),
1173
+ filtered.length > limit && /* @__PURE__ */ jsx("button", {
1174
+ type: "button",
1175
+ className: "studio-fontsource-more",
1176
+ onClick: () => setLimit((value_1) => value_1 + 30),
1177
+ children: "Show more fonts"
1178
+ })
1179
+ ] }),
1180
+ selected && /* @__PURE__ */ jsxs("section", {
1181
+ className: "studio-fontsource-preview",
1182
+ "aria-label": `Preview ${selected.family}`,
1183
+ children: [
1184
+ /* @__PURE__ */ jsx("strong", { children: selected.family }),
1185
+ /* @__PURE__ */ jsx("input", {
1186
+ "aria-label": "Font preview text",
1187
+ value: sample,
1188
+ onChange: (event_2) => setSample(event_2.target.value)
1189
+ }),
1190
+ /* @__PURE__ */ jsx("p", {
1191
+ className: "studio-fontsource-sample",
1192
+ style: {
1193
+ fontFamily: previewState === "ready" ? `"${preview_0.alias}", sans-serif` : "inherit",
1194
+ fontWeight: preview_0?.weight,
1195
+ fontStyle: preview_0?.style
1196
+ },
1197
+ children: sample || "Aa Bb Cc 0123456789"
1198
+ }),
1199
+ /* @__PURE__ */ jsx("span", {
1200
+ role: "status",
1201
+ children: previewState === "loading" ? "Loading preview…" : previewState === "error" ? "Preview could not load. You can still add this family." : ""
1202
+ }),
1203
+ /* @__PURE__ */ jsxs("label", {
1204
+ className: "studio-fontsource-role",
1205
+ children: ["Use for", /* @__PURE__ */ jsxs("select", {
1206
+ "aria-label": "Use font for",
1207
+ value: role,
1208
+ disabled: adding,
1209
+ onChange: (event_3) => setRole(event_3.target.value),
1210
+ children: [/* @__PURE__ */ jsx("option", {
1211
+ value: "",
1212
+ children: "Add to available fonts"
1213
+ }), roles.map((item) => /* @__PURE__ */ jsx("option", {
1214
+ value: item.token,
1215
+ children: item.label
1216
+ }, item.token))]
1217
+ })]
1218
+ }),
1219
+ /* @__PURE__ */ jsx("button", {
1220
+ type: "button",
1221
+ className: "studio-primary",
1222
+ disabled: adding || !writer,
1223
+ onClick: async () => {
1224
+ setAdding(true);
1225
+ setError("");
1226
+ try {
1227
+ const font_2 = await addFontsourceFamily(selected.id);
1228
+ if (!mounted.current) return;
1229
+ onAdded(font_2, role || void 0);
1230
+ setNotice(`${font_2.family} added to the app.`);
1231
+ setOpen(false);
1232
+ } catch (cause_0) {
1233
+ setError(cause_0 instanceof Error ? cause_0.message : "Could not add font.");
1234
+ } finally {
1235
+ setAdding(false);
1236
+ }
1237
+ },
1238
+ children: adding ? "Adding font…" : "Add font to app"
1239
+ })
1240
+ ]
1241
+ }),
1242
+ error && /* @__PURE__ */ jsxs("p", {
1243
+ role: "alert",
1244
+ children: [error, !fonts.length && /* @__PURE__ */ jsx("button", {
1245
+ type: "button",
1246
+ onClick: () => setRetry((value_2) => value_2 + 1),
1247
+ children: "Retry"
1248
+ })]
1249
+ }),
1250
+ /* @__PURE__ */ jsx("p", {
1251
+ className: "studio-fontsource-help",
1252
+ children: writer ? `Adding installs the font package and updates ${writer.cssFile}. Your theme version keeps the Heading/Body choice; Apply theme writes it to the repo.` : "Preview fonts here. Adding them to your app requires the Povio UI Vite plugin in development."
1253
+ })
1254
+ ]
1255
+ })
1256
+ })
1257
+ ]
1258
+ });
1259
+ }
1260
+ //#endregion
833
1261
  //#region src/configurator/inspector/InspectorDetails.tsx
834
1262
  /** A token as a design tool names a variable: `--text-large--line-height` → `text-large/line-height`. */
835
1263
  const variableName = (token) => token.replace(/^--/, "").replace(/-base$/, "").replaceAll("--", "/");
@@ -909,7 +1337,7 @@ function InspectorDetails(t0) {
909
1337
  $[13] = onSelect;
910
1338
  $[14] = t6;
911
1339
  } else t6 = $[14];
912
- t5 = snapshot.groups.map(_temp$6).filter(_temp2$4).map(t6);
1340
+ t5 = snapshot.groups.map(_temp$7).filter(_temp2$4).map(t6);
913
1341
  $[10] = onSelect;
914
1342
  $[11] = snapshot.groups;
915
1343
  $[12] = t5;
@@ -926,7 +1354,7 @@ function InspectorDetails(t0) {
926
1354
  function _temp2$4(group_0) {
927
1355
  return group_0.properties.length;
928
1356
  }
929
- function _temp$6(group) {
1357
+ function _temp$7(group) {
930
1358
  return {
931
1359
  name: group.name,
932
1360
  properties: group.properties.filter((property) => group.name !== "Layout" || ![
@@ -951,7 +1379,7 @@ function BoxModel(t0) {
951
1379
  let t3;
952
1380
  let t4;
953
1381
  if ($[0] !== box.border || $[1] !== box.content.height || $[2] !== box.content.width || $[3] !== box.gap.column || $[4] !== box.gap.row || $[5] !== box.margin || $[6] !== box.padding || $[7] !== box.position || $[8] !== box.radius || $[9] !== onHighlight || $[10] !== onSelect || $[11] !== snapshot.groups) {
954
- const properties = snapshot.groups.flatMap(_temp5$1);
1382
+ const properties = snapshot.groups.flatMap(_temp5$2);
955
1383
  const tokenFor = (label) => properties.find((property) => property.label === label)?.token;
956
1384
  const nonzero = _temp7$1;
957
1385
  let t5;
@@ -1130,7 +1558,7 @@ function _temp7$1(value_0) {
1130
1558
  function _temp6$1(part) {
1131
1559
  return parseFloat(part) !== 0 && Number.isFinite(parseFloat(part));
1132
1560
  }
1133
- function _temp5$1(group) {
1561
+ function _temp5$2(group) {
1134
1562
  return group.properties;
1135
1563
  }
1136
1564
  function _temp4$3(value) {
@@ -2177,120 +2605,6 @@ function PreviewInspector({ children, revision, isKnown, onSelect, onScroll }) {
2177
2605
  });
2178
2606
  }
2179
2607
  //#endregion
2180
- //#region src/configurator/ConfigurationLauncher.tsx
2181
- function ConfigurationLauncher({ position, onOpen }) {
2182
- const ref = useRef(null);
2183
- const gesture = useRef(null);
2184
- const dragged = useRef(false);
2185
- const pending = useRef(null);
2186
- const frame = useRef(null);
2187
- const place = useCallback((next) => {
2188
- const element = ref.current;
2189
- if (!element) return;
2190
- const left = Math.max(8, Math.min(next.left, window.innerWidth - element.offsetWidth - 8));
2191
- const top = Math.max(8, Math.min(next.top, window.innerHeight - element.offsetHeight - 8));
2192
- position.current = {
2193
- left,
2194
- top
2195
- };
2196
- element.style.left = `${left}px`;
2197
- element.style.top = `${top}px`;
2198
- }, [position]);
2199
- function finish() {
2200
- if (frame.current !== null) cancelAnimationFrame(frame.current);
2201
- frame.current = null;
2202
- if (pending.current) place(pending.current);
2203
- pending.current = null;
2204
- gesture.current = null;
2205
- }
2206
- useLayoutEffect(() => {
2207
- const element_0 = ref.current;
2208
- if (!element_0) return;
2209
- const resize = () => place(position.current ?? {
2210
- left: window.innerWidth - element_0.offsetWidth - 24,
2211
- top: window.innerHeight - element_0.offsetHeight - 24
2212
- });
2213
- resize();
2214
- window.addEventListener("resize", resize);
2215
- return () => {
2216
- window.removeEventListener("resize", resize);
2217
- if (frame.current !== null) cancelAnimationFrame(frame.current);
2218
- if (pending.current) position.current = pending.current;
2219
- };
2220
- }, [place, position]);
2221
- return /* @__PURE__ */ jsxs("button", {
2222
- ref,
2223
- type: "button",
2224
- className: "studio-floating-launcher",
2225
- "aria-label": "Open Povio UI configuration",
2226
- title: "Open Povio UI · Drag to move",
2227
- onClick: (event) => {
2228
- if (dragged.current && event.detail !== 0) {
2229
- dragged.current = false;
2230
- return;
2231
- }
2232
- onOpen();
2233
- },
2234
- onPointerDown: (event_0) => {
2235
- if (event_0.button !== 0) return;
2236
- const rect = event_0.currentTarget.getBoundingClientRect();
2237
- dragged.current = false;
2238
- gesture.current = {
2239
- x: event_0.clientX,
2240
- y: event_0.clientY,
2241
- left: rect.left,
2242
- top: rect.top,
2243
- pointerId: event_0.pointerId
2244
- };
2245
- event_0.currentTarget.setPointerCapture(event_0.pointerId);
2246
- },
2247
- onPointerMove: (event_1) => {
2248
- const start = gesture.current;
2249
- if (!start || start.pointerId !== event_1.pointerId) return;
2250
- const x = event_1.clientX - start.x;
2251
- const y = event_1.clientY - start.y;
2252
- if (!dragged.current && Math.hypot(x, y) < 5) return;
2253
- dragged.current = true;
2254
- pending.current = {
2255
- left: start.left + x,
2256
- top: start.top + y
2257
- };
2258
- if (frame.current !== null) return;
2259
- frame.current = requestAnimationFrame(() => {
2260
- frame.current = null;
2261
- if (pending.current) place(pending.current);
2262
- });
2263
- },
2264
- onPointerUp: (event_2) => {
2265
- if (gesture.current?.pointerId !== event_2.pointerId) return;
2266
- finish();
2267
- if (event_2.currentTarget.hasPointerCapture(event_2.pointerId)) event_2.currentTarget.releasePointerCapture(event_2.pointerId);
2268
- },
2269
- onPointerCancel: finish,
2270
- onLostPointerCapture: finish,
2271
- onKeyDown: (event_3) => {
2272
- const move = {
2273
- ArrowLeft: [-1, 0],
2274
- ArrowRight: [1, 0],
2275
- ArrowUp: [0, -1],
2276
- ArrowDown: [0, 1]
2277
- }[event_3.key];
2278
- if (!move || !position.current) return;
2279
- event_3.preventDefault();
2280
- const step = event_3.shiftKey ? 40 : 10;
2281
- place({
2282
- left: position.current.left + move[0] * step,
2283
- top: position.current.top + move[1] * step
2284
- });
2285
- },
2286
- children: [
2287
- /* @__PURE__ */ jsx(SlidersHorizontal, { size: 18 }),
2288
- /* @__PURE__ */ jsx("span", { children: "Povio UI" }),
2289
- /* @__PURE__ */ jsx(GripVertical, { size: 16 })
2290
- ]
2291
- });
2292
- }
2293
- //#endregion
2294
2608
  //#region src/configurator/useStudioPopout.ts
2295
2609
  const StudioPopoutContext = createContext(false);
2296
2610
  /** Keep the popup's styles current without copying scripts or starting a second app. */
@@ -2635,6 +2949,7 @@ function useStudioWindow(bounds, enabled = true) {
2635
2949
  onLostPointerCapture: move.onLostPointerCapture,
2636
2950
  onPointerCancel: move.onPointerCancel,
2637
2951
  onPointerDown(event_5) {
2952
+ if (!event_5.currentTarget.contains(event_5.target)) return;
2638
2953
  if (event_5.target.closest("button, input, select, textarea, a, label, [role='tab']")) return;
2639
2954
  move.onPointerDown(event_5);
2640
2955
  }
@@ -2662,7 +2977,7 @@ function CvaPreview(t0) {
2662
2977
  component = slot.split(".")[0];
2663
2978
  const props = {
2664
2979
  ...definition?.config.defaultVariants,
2665
- ...Object.fromEntries(Object.entries(variants).filter(_temp$5).map(_temp2$3))
2980
+ ...Object.fromEntries(Object.entries(variants).filter(_temp$6).map(_temp2$3))
2666
2981
  };
2667
2982
  const classes = (key, t4) => {
2668
2983
  const extra = t4 === void 0 ? {} : t4;
@@ -3431,7 +3746,7 @@ function _temp2$3(t0) {
3431
3746
  const [name, value_0] = t0;
3432
3747
  return [name, value_0 === "true" ? true : value_0 === "false" ? false : value_0];
3433
3748
  }
3434
- function _temp$5(t0) {
3749
+ function _temp$6(t0) {
3435
3750
  const [, value] = t0;
3436
3751
  return value !== "";
3437
3752
  }
@@ -3587,7 +3902,7 @@ function ClassField(t0) {
3587
3902
  onFocus,
3588
3903
  onChange: t4,
3589
3904
  onBlur: t5,
3590
- onKeyDown: _temp$4
3905
+ onKeyDown: _temp$5
3591
3906
  });
3592
3907
  $[10] = label;
3593
3908
  $[11] = onFocus;
@@ -3608,7 +3923,7 @@ function ClassField(t0) {
3608
3923
  } else t7 = $[17];
3609
3924
  return t7;
3610
3925
  }
3611
- function _temp$4(event_0) {
3926
+ function _temp$5(event_0) {
3612
3927
  if (event_0.key === "Enter" && !event_0.nativeEvent.isComposing) {
3613
3928
  event_0.preventDefault();
3614
3929
  event_0.currentTarget.parentElement?.focus();
@@ -3823,7 +4138,7 @@ function CvaEditor(t0) {
3823
4138
  children: [/* @__PURE__ */ jsx("option", {
3824
4139
  value: "",
3825
4140
  children: "None"
3826
- }), Object.keys(options).map(_temp5)]
4141
+ }), Object.keys(options).map(_temp5$1)]
3827
4142
  })]
3828
4143
  }),
3829
4144
  Object.entries(options).map((t20) => {
@@ -3925,7 +4240,7 @@ function _temp6(t0) {
3925
4240
  const [key_3] = t0;
3926
4241
  return key_3 !== "className";
3927
4242
  }
3928
- function _temp5(value_1) {
4243
+ function _temp5$1(value_1) {
3929
4244
  return /* @__PURE__ */ jsx("option", {
3930
4245
  value: value_1,
3931
4246
  children: value_1
@@ -3947,35 +4262,340 @@ function _temp2$2(key) {
3947
4262
  return key.split(".")[0];
3948
4263
  }
3949
4264
  //#endregion
3950
- //#region src/configurator/theme-writer-protocol.ts
3951
- /** Shared by the browser configurator and the isolated Node Vite entry. */
3952
- const themeWriterMetaName = "povio-ui-theme-writer";
3953
- const themeWriterTokenHeader = "x-povio-ui-theme-token";
3954
- //#endregion
3955
- //#region src/configurator/theme-writer-client.ts
3956
- function getThemeWriter() {
4265
+ //#region src/configurator/theme-versions.ts
4266
+ const themeVersionsMetaName = "povio-ui-theme-versions";
4267
+ function createThemeVersion(name, id, configuration) {
4268
+ return {
4269
+ id,
4270
+ name: name.trim() || "Theme",
4271
+ ...configuration ? { configuration: structuredClone(configuration) } : {}
4272
+ };
4273
+ }
4274
+ function createThemeVersions() {
4275
+ return {
4276
+ version: 1,
4277
+ versions: [createThemeVersion("Default", "default")],
4278
+ activeVersionId: "default"
4279
+ };
4280
+ }
4281
+ function isRecord(value) {
4282
+ return value !== null && typeof value === "object" && !Array.isArray(value);
4283
+ }
4284
+ function safeJSON(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
4285
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
4286
+ if (typeof value === "number") return Number.isFinite(value);
4287
+ if (typeof value !== "object" || depth > 30 || ancestors.has(value)) return false;
4288
+ if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) return false;
4289
+ ancestors.add(value);
4290
+ const valid = Object.entries(value).every(([key, child]) => ![
4291
+ "__proto__",
4292
+ "prototype",
4293
+ "constructor"
4294
+ ].includes(key) && safeJSON(child, ancestors, depth + 1));
4295
+ ancestors.delete(value);
4296
+ return valid;
4297
+ }
4298
+ const stringMap = (value) => isRecord(value) && Object.values(value).every((item) => typeof item === "string");
4299
+ const variantValue = (value) => value === null || typeof value === "string" || typeof value === "boolean";
4300
+ function validCvaEdits(value) {
4301
+ return isRecord(value) && Object.entries(value).every(([slot, definition]) => {
4302
+ if (!/^[a-zA-Z][a-zA-Z0-9]*\.[a-zA-Z][a-zA-Z0-9]*$/.test(slot) || !isRecord(definition) || !Array.isArray(definition.base) || !definition.base.every((item) => typeof item === "string") || !isRecord(definition.config)) return false;
4303
+ const config = definition.config;
4304
+ return isRecord(config.variants) && Object.values(config.variants).every(stringMap) && isRecord(config.defaultVariants) && Object.values(config.defaultVariants).every(variantValue) && Array.isArray(config.compoundVariants) && config.compoundVariants.every((compound) => isRecord(compound) && Object.values(compound).every((item) => variantValue(item) || Array.isArray(item) && (item.every((part) => typeof part === "string") || item.every((part) => typeof part === "boolean"))));
4305
+ });
4306
+ }
4307
+ /** Returns an independent JSON configuration, or undefined for invalid/unset overrides. */
4308
+ function parseUIConfiguration(value) {
4309
+ if (!isRecord(value) || !safeJSON(value) || !isRecord(value.theme) || ![
4310
+ "light",
4311
+ "dark",
4312
+ "theme"
4313
+ ].every((key) => stringMap(value.theme && value.theme[key])) || !isRecord(value.config) || value.overrides !== void 0 && !validCvaEdits(value.overrides)) return void 0;
4314
+ return JSON.parse(JSON.stringify(value));
4315
+ }
4316
+ /** Returns an independent component configuration, or undefined when invalid. */
4317
+ function parseComponentConfiguration(value) {
4318
+ if (!isRecord(value) || !safeJSON(value) || !isRecord(value.config) || value.overrides !== void 0 && !validCvaEdits(value.overrides)) return void 0;
4319
+ const copy = JSON.parse(JSON.stringify(value));
4320
+ return {
4321
+ config: copy.config ?? {},
4322
+ overrides: copy.overrides ?? {}
4323
+ };
4324
+ }
4325
+ /** Sanitize stored JSON text or a repository value. */
4326
+ function parseThemeVersions(input) {
4327
+ const fallback = createThemeVersions();
4328
+ let value = input;
4329
+ if (typeof value === "string") try {
4330
+ value = JSON.parse(value);
4331
+ } catch {
4332
+ return fallback;
4333
+ }
4334
+ if (!isRecord(value) || value.version !== 1 || !Array.isArray(value.versions)) return fallback;
4335
+ const versions = [];
4336
+ const ids = /* @__PURE__ */ new Set();
4337
+ for (const entry of value.versions) {
4338
+ if (!isRecord(entry) || typeof entry.id !== "string" || !entry.id.trim() || ids.has(entry.id)) continue;
4339
+ ids.add(entry.id);
4340
+ const configuration = parseUIConfiguration(entry.configuration);
4341
+ versions.push({
4342
+ id: entry.id,
4343
+ name: typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : "Theme",
4344
+ ...configuration ? { configuration } : {}
4345
+ });
4346
+ }
4347
+ if (!versions.length) return fallback;
4348
+ return {
4349
+ version: 1,
4350
+ versions,
4351
+ activeVersionId: typeof value.activeVersionId === "string" && versions.some((version) => version.id === value.activeVersionId) ? value.activeVersionId : versions[0].id
4352
+ };
4353
+ }
4354
+ //#endregion
4355
+ //#region src/configurator/component-configuration.ts
4356
+ const emptyComponentConfiguration = () => ({
4357
+ config: {},
4358
+ overrides: {}
4359
+ });
4360
+ /**
4361
+ * The app's applied component settings: pass the JSON written by the configurator's Apply theme
4362
+ * and the app's code-level overrides, which stay underneath the applied variant overrides.
4363
+ */
4364
+ function resolveUIConfiguration(file, baseOverrides) {
4365
+ const { config, overrides } = parseComponentConfiguration(file) ?? emptyComponentConfiguration();
4366
+ return {
4367
+ config,
4368
+ overrides: applyCvaEdits(baseOverrides, overrides)
4369
+ };
4370
+ }
4371
+ /** Key-order independent JSON without empty objects, for comparing component configurations. */
4372
+ function canonicalJSON(value) {
4373
+ const normalize = (item) => {
4374
+ if (Array.isArray(item)) return item.map(normalize);
4375
+ if (!item || typeof item !== "object") return item;
4376
+ const entries = Object.keys(item).sort().flatMap((key) => {
4377
+ const child = normalize(item[key]);
4378
+ return child === void 0 || child && typeof child === "object" && !Array.isArray(child) && !Object.keys(child).length ? [] : [[key, child]];
4379
+ });
4380
+ return Object.fromEntries(entries);
4381
+ };
4382
+ return JSON.stringify(normalize(value));
4383
+ }
4384
+ //#endregion
4385
+ //#region src/configurator/repository-storage-protocol.ts
4386
+ const componentConfigurationMetaName = "povio-ui-component-configuration";
4387
+ const repositoryStorageTokenHeader = "x-povio-ui-storage-token";
4388
+ /** Dev-server event sent when a repository storage file changes, so other tabs reload it. */
4389
+ const repositoryStorageChangedEvent = "povio-ui:repository-storage-changed";
4390
+ //#endregion
4391
+ //#region src/configurator/useRepositoryStorage.ts
4392
+ const hot = import.meta.hot;
4393
+ function connectionFromDocument(metaName) {
3957
4394
  if (typeof document === "undefined") return null;
3958
- const content = document.querySelector(`meta[name="${themeWriterMetaName}"]`)?.content;
3959
- if (!content) return null;
3960
4395
  try {
3961
- const connection = JSON.parse(content);
3962
- if (typeof connection.endpoint === "string" && connection.endpoint.startsWith("/") && !connection.endpoint.startsWith("//") && typeof connection.token === "string" && typeof connection.file === "string") return connection;
3963
- } catch {}
3964
- return null;
4396
+ const value = JSON.parse(document.querySelector(`meta[name="${metaName}"]`)?.content ?? "null");
4397
+ return value && typeof value.endpoint === "string" && value.endpoint.startsWith("/") && !value.endpoint.startsWith("//") && typeof value.token === "string" && typeof value.file === "string" ? value : null;
4398
+ } catch {
4399
+ return null;
4400
+ }
3965
4401
  }
3966
- async function saveThemeCss(connection, css) {
3967
- const response = await fetch(connection.endpoint, {
3968
- method: "POST",
4402
+ /** Repo is authoritative when connected; local storage remains an offline draft cache. */
4403
+ function useRepositoryStorage({ key, metaName, field, parse, noun, enabled = true }) {
4404
+ const Noun = `${noun[0].toUpperCase()}${noun.slice(1)}`;
4405
+ const [connection] = useState(() => enabled ? connectionFromDocument(metaName) : null);
4406
+ const [value, setValue] = useState(() => {
4407
+ try {
4408
+ return parse(localStorage.getItem(key));
4409
+ } catch {
4410
+ return parse(null);
4411
+ }
4412
+ });
4413
+ const [ready, setReady] = useState(!connection);
4414
+ const [status, setStatus] = useState(connection ? `Loading ${noun}…` : "Saved in this browser");
4415
+ const [error, setError] = useState("");
4416
+ const latest = useRef(value);
4417
+ latest.current = value;
4418
+ const revision = useRef(null);
4419
+ const saved = useRef("");
4420
+ const busy = useRef(false);
4421
+ const paused = useRef(false);
4422
+ const alive = useRef(true);
4423
+ const loadGeneration = useRef(0);
4424
+ useEffect(() => {
4425
+ alive.current = true;
4426
+ return () => {
4427
+ alive.current = false;
4428
+ };
4429
+ }, []);
4430
+ const read = useCallback(async (quiet) => {
4431
+ if (!connection) return;
4432
+ const generation = ++loadGeneration.current;
4433
+ if (!quiet) {
4434
+ setReady(false);
4435
+ setError("");
4436
+ setStatus(`Loading ${noun}…`);
4437
+ }
4438
+ paused.current = false;
4439
+ try {
4440
+ const response = await fetch(connection.endpoint, {
4441
+ headers: { [repositoryStorageTokenHeader]: connection.token },
4442
+ cache: "no-store"
4443
+ });
4444
+ const result = await response.json();
4445
+ if (!response.ok) throw new Error(result.error ?? `Could not read ${noun} settings.`);
4446
+ if (!alive.current || generation !== loadGeneration.current) return;
4447
+ revision.current = result.revision;
4448
+ if (result[field]) {
4449
+ const next = parse(result[field]);
4450
+ saved.current = JSON.stringify(next);
4451
+ latest.current = next;
4452
+ setValue(next);
4453
+ } else saved.current = "";
4454
+ setStatus(`Saved to ${connection.file}`);
4455
+ setReady(true);
4456
+ } catch (cause) {
4457
+ if (!alive.current || generation !== loadGeneration.current) return;
4458
+ paused.current = true;
4459
+ setError(cause instanceof Error ? cause.message : "Could not load repository settings.");
4460
+ setStatus("Repository unavailable");
4461
+ setReady(true);
4462
+ }
4463
+ }, [
4464
+ connection,
4465
+ field,
4466
+ noun,
4467
+ parse
4468
+ ]);
4469
+ const load = useCallback(() => read(false), [read]);
4470
+ useEffect(() => {
4471
+ load();
4472
+ }, [load]);
4473
+ useEffect(() => {
4474
+ if (!connection || !hot) return;
4475
+ const changed = (change) => {
4476
+ if (change.endpoint !== connection.endpoint || change.revision === revision.current) return;
4477
+ if (busy.current || JSON.stringify(latest.current) !== saved.current) return;
4478
+ read(true);
4479
+ };
4480
+ hot.on(repositoryStorageChangedEvent, changed);
4481
+ return () => hot.off?.(repositoryStorageChangedEvent, changed);
4482
+ }, [connection, read]);
4483
+ const save = useCallback(async () => {
4484
+ if (!connection || busy.current || paused.current || !ready) return;
4485
+ busy.current = true;
4486
+ try {
4487
+ while (alive.current && !paused.current) {
4488
+ const current = latest.current;
4489
+ const serialized = JSON.stringify(current);
4490
+ if (serialized === saved.current) break;
4491
+ setStatus("Saving to repository…");
4492
+ const response_0 = await fetch(connection.endpoint, {
4493
+ method: "POST",
4494
+ headers: {
4495
+ "Content-Type": "application/json",
4496
+ [repositoryStorageTokenHeader]: connection.token
4497
+ },
4498
+ body: JSON.stringify({
4499
+ [field]: current,
4500
+ revision: revision.current
4501
+ })
4502
+ });
4503
+ const result_0 = await response_0.json();
4504
+ if (!response_0.ok) throw new Error(response_0.status === 409 ? `${Noun} file changed elsewhere. Export your draft, then reload repository settings.` : result_0.error ?? `Could not save ${noun} settings.`);
4505
+ revision.current = result_0.revision;
4506
+ saved.current = serialized;
4507
+ }
4508
+ if (alive.current) {
4509
+ setError("");
4510
+ setStatus(`Saved to ${connection.file}`);
4511
+ }
4512
+ } catch (cause_0) {
4513
+ paused.current = true;
4514
+ if (alive.current) {
4515
+ setError(cause_0 instanceof Error ? cause_0.message : `Could not save ${noun} settings.`);
4516
+ setStatus(`${Noun} not saved to repository`);
4517
+ }
4518
+ } finally {
4519
+ busy.current = false;
4520
+ }
4521
+ }, [
4522
+ connection,
4523
+ field,
4524
+ noun,
4525
+ Noun,
4526
+ ready
4527
+ ]);
4528
+ useEffect(() => {
4529
+ if (!ready) return;
4530
+ try {
4531
+ localStorage.setItem(key, JSON.stringify(value));
4532
+ } catch {
4533
+ if (!connection) setStatus(`Browser storage unavailable — export your ${noun}`);
4534
+ }
4535
+ if (!connection || paused.current || JSON.stringify(value) === saved.current) return;
4536
+ setStatus("Saving to repository…");
4537
+ const timer = setTimeout(() => {
4538
+ save();
4539
+ }, 350);
4540
+ return () => clearTimeout(timer);
4541
+ }, [
4542
+ value,
4543
+ key,
4544
+ connection,
4545
+ noun,
4546
+ ready,
4547
+ save
4548
+ ]);
4549
+ return {
4550
+ value,
4551
+ setValue,
4552
+ ready,
4553
+ status,
4554
+ error,
4555
+ repositoryFile: connection?.file,
4556
+ reload: load
4557
+ };
4558
+ }
4559
+ //#endregion
4560
+ //#region src/configurator/component-configuration-client.ts
4561
+ const getComponentConfigurationWriter = () => connectionFromDocument(componentConfigurationMetaName);
4562
+ async function request(writer, init) {
4563
+ const response = await fetch(writer.endpoint, {
4564
+ ...init,
4565
+ cache: "no-store",
3969
4566
  headers: {
3970
- "Content-Type": "application/json",
3971
- [themeWriterTokenHeader]: connection.token
3972
- },
3973
- body: JSON.stringify({ css })
4567
+ ...init?.headers,
4568
+ [repositoryStorageTokenHeader]: writer.token
4569
+ }
3974
4570
  });
3975
- if (!response.ok) {
3976
- const result = await response.json().catch(() => null);
3977
- throw new Error(result?.error ?? `Theme save failed (${response.status}).`);
3978
- }
4571
+ return {
4572
+ response,
4573
+ result: await response.json()
4574
+ };
4575
+ }
4576
+ /** The component configuration in the repository, or null when the file does not exist yet. */
4577
+ async function readComponentConfiguration(writer) {
4578
+ const { response, result } = await request(writer);
4579
+ if (!response.ok) throw new Error(result.error ?? "Could not read UI configuration.");
4580
+ return {
4581
+ configuration: result.configuration ? parseComponentConfiguration(result.configuration) ?? null : null,
4582
+ revision: result.revision
4583
+ };
4584
+ }
4585
+ /** Applying is explicit, so a file changed elsewhere is re-read and overwritten once. */
4586
+ async function saveComponentConfiguration(writer, configuration, revision) {
4587
+ const post = (current) => request(writer, {
4588
+ method: "POST",
4589
+ headers: { "Content-Type": "application/json" },
4590
+ body: JSON.stringify({
4591
+ configuration,
4592
+ revision: current
4593
+ })
4594
+ });
4595
+ let { response, result } = await post(revision);
4596
+ if (response.status === 409) ({response, result} = await post((await readComponentConfiguration(writer)).revision));
4597
+ if (!response.ok) throw new Error(result.error ?? "Could not save UI configuration.");
4598
+ return result.revision;
3979
4599
  }
3980
4600
  //#endregion
3981
4601
  //#region src/configurator/ColorModeSwitch.tsx
@@ -4051,7 +4671,7 @@ function AlphaColorField(t0) {
4051
4671
  setPercentText(null);
4052
4672
  if (Number.isFinite(entered_0)) onChange(`#${color}${toAlpha(entered_0)}`);
4053
4673
  };
4054
- const blurOnEnter = _temp$3;
4674
+ const blurOnEnter = _temp$4;
4055
4675
  const t1 = `#${color}`;
4056
4676
  let t2;
4057
4677
  if ($[0] !== t1 || $[1] !== value) {
@@ -4153,7 +4773,7 @@ function AlphaColorField(t0) {
4153
4773
  ]
4154
4774
  });
4155
4775
  }
4156
- function _temp$3(event) {
4776
+ function _temp$4(event) {
4157
4777
  if (event.key === "Enter" && !event.nativeEvent.isComposing) event.currentTarget.blur();
4158
4778
  }
4159
4779
  //#endregion
@@ -4251,7 +4871,7 @@ function PaletteSelect(t0) {
4251
4871
  } else t6 = $[14];
4252
4872
  let t7;
4253
4873
  if ($[15] !== options) {
4254
- t7 = paletteFamilies(options).map(_temp$2);
4874
+ t7 = paletteFamilies(options).map(_temp$3);
4255
4875
  $[15] = options;
4256
4876
  $[16] = t7;
4257
4877
  } else t7 = $[16];
@@ -4412,7 +5032,7 @@ function _temp2$1(t0) {
4412
5032
  })]
4413
5033
  }, name_0);
4414
5034
  }
4415
- function _temp$2(t0) {
5035
+ function _temp$3(t0) {
4416
5036
  const { family: name, steps } = t0;
4417
5037
  return /* @__PURE__ */ jsx("optgroup", {
4418
5038
  label: `Base palette / ${name}`,
@@ -4585,7 +5205,7 @@ function useLoadedFonts() {
4585
5205
  const [revision, setRevision] = useState(0);
4586
5206
  let t1;
4587
5207
  if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
4588
- t1 = () => setRevision(_temp$1);
5208
+ t1 = () => setRevision(_temp$2);
4589
5209
  $[1] = t1;
4590
5210
  } else t1 = $[1];
4591
5211
  const refresh = t1;
@@ -4596,13 +5216,13 @@ function useLoadedFonts() {
4596
5216
  document.fonts.ready.then(() => {
4597
5217
  if (!active) return;
4598
5218
  const root = getComputedStyle(document.documentElement);
4599
- const families = [...new Set([...document.fonts].map(_temp2))];
4600
- setFonts(families.map((family) => {
4601
- const base = `--font-${family.split(/\s+/)[0].toLowerCase()}-base`;
4602
- const value_0 = unquote(root.getPropertyValue(base)) === family ? `var(${base}), sans-serif` : `"${family}", sans-serif`;
4603
- const group = uploadedFamilies.has(family) ? "Uploaded fonts" : "Loaded fonts";
5219
+ const families = [...new Set([...document.fonts].map(_temp2))].filter(_temp3);
5220
+ setFonts(families.map((family_0) => {
5221
+ const base = `--font-${family_0.split(/\s+/)[0].toLowerCase()}-base`;
5222
+ const value_0 = unquote(root.getPropertyValue(base)) === family_0 ? `var(${base}), sans-serif` : `"${family_0}", sans-serif`;
5223
+ const group = uploadedFamilies.has(family_0) ? "Uploaded fonts" : "Loaded fonts";
4604
5224
  return {
4605
- name: `loaded:${family}`,
5225
+ name: `loaded:${family_0}`,
4606
5226
  label: familyName(value_0),
4607
5227
  value: value_0,
4608
5228
  group
@@ -4631,10 +5251,13 @@ function useLoadedFonts() {
4631
5251
  return t4;
4632
5252
  }
4633
5253
  /** Families added through the upload button in this page session. */
5254
+ function _temp3(family) {
5255
+ return !family.startsWith("Povio Fontsource preview ");
5256
+ }
4634
5257
  function _temp2(face) {
4635
5258
  return unquote(face.family);
4636
5259
  }
4637
- function _temp$1(value) {
5260
+ function _temp$2(value) {
4638
5261
  return value + 1;
4639
5262
  }
4640
5263
  const uploadedFamilies = /* @__PURE__ */ new Set();
@@ -4809,7 +5432,7 @@ function FontRoleCard(t0) {
4809
5432
  } else t11 = $[21];
4810
5433
  let t12;
4811
5434
  if ($[22] !== options) {
4812
- t12 = new Set(options.map(_temp3));
5435
+ t12 = new Set(options.map(_temp4));
4813
5436
  $[22] = options;
4814
5437
  $[23] = t12;
4815
5438
  } else t12 = $[23];
@@ -4823,7 +5446,7 @@ function FontRoleCard(t0) {
4823
5446
  if ($[26] !== options || $[27] !== t13) {
4824
5447
  t14 = t13.map((group_0) => /* @__PURE__ */ jsx("optgroup", {
4825
5448
  label: group_0,
4826
- children: options.filter((option_1) => option_1.group === group_0).map(_temp4)
5449
+ children: options.filter((option_1) => option_1.group === group_0).map(_temp5)
4827
5450
  }, group_0));
4828
5451
  $[26] = options;
4829
5452
  $[27] = t13;
@@ -4885,13 +5508,13 @@ function FontRoleCard(t0) {
4885
5508
  } else t17 = $[45];
4886
5509
  return t17;
4887
5510
  }
4888
- function _temp4(option_2) {
5511
+ function _temp5(option_2) {
4889
5512
  return /* @__PURE__ */ jsx("option", {
4890
5513
  value: option_2.name,
4891
5514
  children: option_2.label
4892
5515
  }, option_2.name);
4893
5516
  }
4894
- function _temp3(t0) {
5517
+ function _temp4(t0) {
4895
5518
  const { group } = t0;
4896
5519
  return group;
4897
5520
  }
@@ -5266,7 +5889,7 @@ function ShadesGenerator(t0) {
5266
5889
  if ($[2] !== initial || $[3] !== steps || $[4] !== text || $[5] !== title) {
5267
5890
  const hex = text.trim().replace(/^#/, "");
5268
5891
  base = /^[0-9a-f]{6}$/i.test(hex) ? toRgb(`#${hex}`) : /^[0-9a-f]{3}$/i.test(hex) ? toRgb(`#${hex.replace(/./g, "$&$&")}`) : null;
5269
- shades = base ? generateShades(base, steps.map(_temp)) : null;
5892
+ shades = base ? generateShades(base, steps.map(_temp$1)) : null;
5270
5893
  pending = !!shades && base !== initial;
5271
5894
  t8 = "studio-shades";
5272
5895
  t9 = "group";
@@ -5437,13 +6060,13 @@ function ShadesGenerator(t0) {
5437
6060
  } else t18 = $[51];
5438
6061
  return t18;
5439
6062
  }
5440
- function _temp(t0) {
6063
+ function _temp$1(t0) {
5441
6064
  const { step } = t0;
5442
6065
  return step;
5443
6066
  }
5444
6067
  //#endregion
5445
6068
  //#region src/configurator/ComponentPreview.tsx
5446
- const ComponentGallery = lazy(() => import("./ComponentGallery-D4U5hXZL.js"));
6069
+ const ComponentGallery = lazy(() => import("./ComponentGallery-CzKU899_.js"));
5447
6070
  /** Every component, styled by a configuration; the configurator's preview and the App Canvas Components view. */
5448
6071
  function ComponentPreview(t0) {
5449
6072
  const $ = c(14);
@@ -5503,6 +6126,543 @@ function ComponentPreview(t0) {
5503
6126
  return t8;
5504
6127
  }
5505
6128
  //#endregion
6129
+ //#region src/configurator/ThemeVersionControls.tsx
6130
+ /** Switches, creates, renames and deletes theme versions from the configurator's title row. */
6131
+ function ThemeVersionControls(t0) {
6132
+ const $ = c(84);
6133
+ const { state } = t0;
6134
+ const { themes, setThemes, active, ready, error, status } = state;
6135
+ const createTrigger = useRef(null);
6136
+ const editTrigger = useRef(null);
6137
+ const [action, setAction] = useState(null);
6138
+ const [name, setName] = useState("");
6139
+ const disabled = !ready;
6140
+ let t1;
6141
+ if ($[0] !== active.name) {
6142
+ t1 = (next) => {
6143
+ setName(next === "create" ? `${active.name} copy` : active.name);
6144
+ setAction(next);
6145
+ };
6146
+ $[0] = active.name;
6147
+ $[1] = t1;
6148
+ } else t1 = $[1];
6149
+ const open = t1;
6150
+ const title = action === "create" ? "New theme version" : action === "delete" ? "Delete theme version?" : "Edit theme version";
6151
+ let t2;
6152
+ if ($[2] !== action || $[3] !== active.configuration || $[4] !== active.id || $[5] !== disabled || $[6] !== name || $[7] !== setThemes) {
6153
+ t2 = () => {
6154
+ if (disabled || !action) return;
6155
+ if (action !== "delete" && !name.trim()) return;
6156
+ if (action === "create") {
6157
+ const created = createThemeVersion(name, crypto.randomUUID(), active.configuration);
6158
+ setThemes((current) => ({
6159
+ ...current,
6160
+ versions: [...current.versions, created],
6161
+ activeVersionId: created.id
6162
+ }));
6163
+ } else if (action === "delete") setThemes((current_0) => {
6164
+ if (current_0.versions.length < 2) return current_0;
6165
+ const remaining = current_0.versions.filter((version) => version.id !== active.id);
6166
+ return {
6167
+ ...current_0,
6168
+ versions: remaining,
6169
+ activeVersionId: remaining[0].id
6170
+ };
6171
+ });
6172
+ else setThemes((current_1) => ({
6173
+ ...current_1,
6174
+ versions: current_1.versions.map((version_0) => version_0.id === active.id ? {
6175
+ ...version_0,
6176
+ name: name.trim()
6177
+ } : version_0)
6178
+ }));
6179
+ setAction(null);
6180
+ };
6181
+ $[2] = action;
6182
+ $[3] = active.configuration;
6183
+ $[4] = active.id;
6184
+ $[5] = disabled;
6185
+ $[6] = name;
6186
+ $[7] = setThemes;
6187
+ $[8] = t2;
6188
+ } else t2 = $[8];
6189
+ const save = t2;
6190
+ let t3;
6191
+ if ($[9] !== setThemes) {
6192
+ t3 = (id) => {
6193
+ if (id !== null) setThemes((current_2) => ({
6194
+ ...current_2,
6195
+ activeVersionId: String(id)
6196
+ }));
6197
+ };
6198
+ $[9] = setThemes;
6199
+ $[10] = t3;
6200
+ } else t3 = $[10];
6201
+ let t4;
6202
+ if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
6203
+ t4 = /* @__PURE__ */ jsxs($7705c033048f6da7$export$353f5b6fc5456de1, {
6204
+ className: "studio-version-trigger",
6205
+ children: [/* @__PURE__ */ jsx($c8bb816105474884$export$e288731fd71264f0, {}), /* @__PURE__ */ jsx(ChevronDown, {
6206
+ size: 12,
6207
+ "aria-hidden": "true"
6208
+ })]
6209
+ });
6210
+ $[11] = t4;
6211
+ } else t4 = $[11];
6212
+ let t5;
6213
+ if ($[12] !== themes.versions) {
6214
+ t5 = /* @__PURE__ */ jsx($542a13ca2fa5b484$export$5b6b19405a83ff9d, {
6215
+ className: "studio-version-popover",
6216
+ placement: "bottom start",
6217
+ children: /* @__PURE__ */ jsx($928221da08ecbc62$export$41f133550aa26f48, {
6218
+ className: "studio-version-list",
6219
+ items: themes.versions,
6220
+ children: _temp
6221
+ })
6222
+ });
6223
+ $[12] = themes.versions;
6224
+ $[13] = t5;
6225
+ } else t5 = $[13];
6226
+ let t6;
6227
+ if ($[14] !== active.id || $[15] !== disabled || $[16] !== t3 || $[17] !== t5) {
6228
+ t6 = /* @__PURE__ */ jsxs($c8bb816105474884$export$ef9b1a59e592288f, {
6229
+ "aria-label": "Theme version",
6230
+ className: "studio-version-select",
6231
+ selectedKey: active.id,
6232
+ isDisabled: disabled,
6233
+ onSelectionChange: t3,
6234
+ children: [t4, t5]
6235
+ });
6236
+ $[14] = active.id;
6237
+ $[15] = disabled;
6238
+ $[16] = t3;
6239
+ $[17] = t5;
6240
+ $[18] = t6;
6241
+ } else t6 = $[18];
6242
+ let t7;
6243
+ if ($[19] !== open) {
6244
+ t7 = () => open("create");
6245
+ $[19] = open;
6246
+ $[20] = t7;
6247
+ } else t7 = $[20];
6248
+ let t8;
6249
+ if ($[21] === Symbol.for("react.memo_cache_sentinel")) {
6250
+ t8 = /* @__PURE__ */ jsx(Plus, { size: 14 });
6251
+ $[21] = t8;
6252
+ } else t8 = $[21];
6253
+ let t9;
6254
+ if ($[22] !== disabled || $[23] !== t7) {
6255
+ t9 = /* @__PURE__ */ jsx("button", {
6256
+ ref: createTrigger,
6257
+ type: "button",
6258
+ className: "studio-version-action",
6259
+ "aria-label": "Add theme version",
6260
+ title: "Add theme version",
6261
+ disabled,
6262
+ onClick: t7,
6263
+ children: t8
6264
+ });
6265
+ $[22] = disabled;
6266
+ $[23] = t7;
6267
+ $[24] = t9;
6268
+ } else t9 = $[24];
6269
+ let t10;
6270
+ if ($[25] !== open) {
6271
+ t10 = () => open("rename");
6272
+ $[25] = open;
6273
+ $[26] = t10;
6274
+ } else t10 = $[26];
6275
+ let t11;
6276
+ if ($[27] === Symbol.for("react.memo_cache_sentinel")) {
6277
+ t11 = /* @__PURE__ */ jsx(Pencil, { size: 14 });
6278
+ $[27] = t11;
6279
+ } else t11 = $[27];
6280
+ let t12;
6281
+ if ($[28] !== disabled || $[29] !== t10) {
6282
+ t12 = /* @__PURE__ */ jsx("button", {
6283
+ ref: editTrigger,
6284
+ type: "button",
6285
+ className: "studio-version-action",
6286
+ "aria-label": "Edit theme version",
6287
+ title: "Edit theme version",
6288
+ disabled,
6289
+ onClick: t10,
6290
+ children: t11
6291
+ });
6292
+ $[28] = disabled;
6293
+ $[29] = t10;
6294
+ $[30] = t12;
6295
+ } else t12 = $[30];
6296
+ let t13;
6297
+ if ($[31] !== error) {
6298
+ t13 = error && /* @__PURE__ */ jsx("p", {
6299
+ className: "studio-version-error",
6300
+ role: "alert",
6301
+ children: error
6302
+ });
6303
+ $[31] = error;
6304
+ $[32] = t13;
6305
+ } else t13 = $[32];
6306
+ const t14 = action === "create" ? createTrigger : editTrigger;
6307
+ const t15 = action !== null;
6308
+ let t16;
6309
+ if ($[33] === Symbol.for("react.memo_cache_sentinel")) {
6310
+ t16 = (isOpen) => {
6311
+ if (!isOpen) setAction(null);
6312
+ };
6313
+ $[33] = t16;
6314
+ } else t16 = $[33];
6315
+ const t17 = action === "delete" ? "alertdialog" : "dialog";
6316
+ let t18;
6317
+ if ($[34] !== save) {
6318
+ t18 = (event) => {
6319
+ event.preventDefault();
6320
+ save();
6321
+ };
6322
+ $[34] = save;
6323
+ $[35] = t18;
6324
+ } else t18 = $[35];
6325
+ let t19;
6326
+ if ($[36] !== title) {
6327
+ t19 = /* @__PURE__ */ jsx($2ec61d1d4f780267$export$a8a3e93435678ff9, {
6328
+ slot: "title",
6329
+ children: title
6330
+ });
6331
+ $[36] = title;
6332
+ $[37] = t19;
6333
+ } else t19 = $[37];
6334
+ let t20;
6335
+ if ($[38] === Symbol.for("react.memo_cache_sentinel")) {
6336
+ t20 = /* @__PURE__ */ jsx("button", {
6337
+ type: "button",
6338
+ "aria-label": "Close theme version dialog",
6339
+ onClick: () => setAction(null),
6340
+ children: /* @__PURE__ */ jsx(X, { size: 12 })
6341
+ });
6342
+ $[38] = t20;
6343
+ } else t20 = $[38];
6344
+ let t21;
6345
+ if ($[39] !== t19) {
6346
+ t21 = /* @__PURE__ */ jsxs("header", {
6347
+ className: "studio-version-dialog-heading",
6348
+ children: [t19, t20]
6349
+ });
6350
+ $[39] = t19;
6351
+ $[40] = t21;
6352
+ } else t21 = $[40];
6353
+ let t22;
6354
+ if ($[41] !== action || $[42] !== active.name || $[43] !== name) {
6355
+ t22 = action === "delete" ? /* @__PURE__ */ jsxs("p", { children: [
6356
+ "Delete “",
6357
+ active.name,
6358
+ "” and its saved theme?"
6359
+ ] }) : /* @__PURE__ */ jsxs("label", { children: ["Version name", /* @__PURE__ */ jsx("input", {
6360
+ autoFocus: true,
6361
+ value: name,
6362
+ maxLength: 80,
6363
+ placeholder: "Version name",
6364
+ onChange: (event_0) => setName(event_0.target.value),
6365
+ required: true
6366
+ })] });
6367
+ $[41] = action;
6368
+ $[42] = active.name;
6369
+ $[43] = name;
6370
+ $[44] = t22;
6371
+ } else t22 = $[44];
6372
+ let t23;
6373
+ if ($[45] !== action || $[46] !== active.name) {
6374
+ t23 = action === "create" && /* @__PURE__ */ jsxs("p", {
6375
+ className: "studio-version-hint",
6376
+ children: [
6377
+ "Starts as a copy of “",
6378
+ active.name,
6379
+ "”."
6380
+ ]
6381
+ });
6382
+ $[45] = action;
6383
+ $[46] = active.name;
6384
+ $[47] = t23;
6385
+ } else t23 = $[47];
6386
+ let t24;
6387
+ if ($[48] !== action || $[49] !== themes.versions) {
6388
+ t24 = action === "rename" ? /* @__PURE__ */ jsxs("button", {
6389
+ type: "button",
6390
+ className: "studio-version-delete",
6391
+ disabled: themes.versions.length < 2,
6392
+ onClick: () => setAction("delete"),
6393
+ children: [/* @__PURE__ */ jsx(Trash, { size: 12 }), " Delete version"]
6394
+ }) : /* @__PURE__ */ jsx("button", {
6395
+ type: "button",
6396
+ autoFocus: action === "delete",
6397
+ onClick: () => action === "delete" ? setAction("rename") : setAction(null),
6398
+ children: action === "delete" ? "Back" : "Cancel"
6399
+ });
6400
+ $[48] = action;
6401
+ $[49] = themes.versions;
6402
+ $[50] = t24;
6403
+ } else t24 = $[50];
6404
+ const t25 = action === "delete" ? "is-danger" : "is-primary";
6405
+ let t26;
6406
+ if ($[51] !== action || $[52] !== disabled || $[53] !== name) {
6407
+ t26 = disabled || action !== "delete" && !name.trim();
6408
+ $[51] = action;
6409
+ $[52] = disabled;
6410
+ $[53] = name;
6411
+ $[54] = t26;
6412
+ } else t26 = $[54];
6413
+ const t27 = action === "create" ? "Create version" : action === "delete" ? "Delete" : "Save";
6414
+ let t28;
6415
+ if ($[55] !== t25 || $[56] !== t26 || $[57] !== t27) {
6416
+ t28 = /* @__PURE__ */ jsx("button", {
6417
+ type: "submit",
6418
+ className: t25,
6419
+ disabled: t26,
6420
+ children: t27
6421
+ });
6422
+ $[55] = t25;
6423
+ $[56] = t26;
6424
+ $[57] = t27;
6425
+ $[58] = t28;
6426
+ } else t28 = $[58];
6427
+ let t29;
6428
+ if ($[59] !== t24 || $[60] !== t28) {
6429
+ t29 = /* @__PURE__ */ jsxs("div", {
6430
+ className: "studio-version-dialog-actions",
6431
+ children: [t24, t28]
6432
+ });
6433
+ $[59] = t24;
6434
+ $[60] = t28;
6435
+ $[61] = t29;
6436
+ } else t29 = $[61];
6437
+ let t30;
6438
+ if ($[62] !== t18 || $[63] !== t21 || $[64] !== t22 || $[65] !== t23 || $[66] !== t29) {
6439
+ t30 = /* @__PURE__ */ jsxs("form", {
6440
+ onSubmit: t18,
6441
+ children: [
6442
+ t21,
6443
+ t22,
6444
+ t23,
6445
+ t29
6446
+ ]
6447
+ });
6448
+ $[62] = t18;
6449
+ $[63] = t21;
6450
+ $[64] = t22;
6451
+ $[65] = t23;
6452
+ $[66] = t29;
6453
+ $[67] = t30;
6454
+ } else t30 = $[67];
6455
+ let t31;
6456
+ if ($[68] !== action || $[69] !== t17 || $[70] !== t30 || $[71] !== title) {
6457
+ t31 = /* @__PURE__ */ jsx($f2ff30fde7b014be$export$3ddf2d174ce01153, {
6458
+ "aria-label": title,
6459
+ role: t17,
6460
+ children: t30
6461
+ }, action);
6462
+ $[68] = action;
6463
+ $[69] = t17;
6464
+ $[70] = t30;
6465
+ $[71] = title;
6466
+ $[72] = t31;
6467
+ } else t31 = $[72];
6468
+ let t32;
6469
+ if ($[73] !== t14 || $[74] !== t15 || $[75] !== t31) {
6470
+ t32 = /* @__PURE__ */ jsx($542a13ca2fa5b484$export$5b6b19405a83ff9d, {
6471
+ triggerRef: t14,
6472
+ placement: "bottom start",
6473
+ offset: 6,
6474
+ className: "studio-version-popover studio-version-dialog",
6475
+ isOpen: t15,
6476
+ onOpenChange: t16,
6477
+ children: t31
6478
+ });
6479
+ $[73] = t14;
6480
+ $[74] = t15;
6481
+ $[75] = t31;
6482
+ $[76] = t32;
6483
+ } else t32 = $[76];
6484
+ let t33;
6485
+ if ($[77] !== status || $[78] !== t12 || $[79] !== t13 || $[80] !== t32 || $[81] !== t6 || $[82] !== t9) {
6486
+ t33 = /* @__PURE__ */ jsxs("div", {
6487
+ className: "studio-version-picker",
6488
+ title: status,
6489
+ children: [
6490
+ t6,
6491
+ t9,
6492
+ t12,
6493
+ t13,
6494
+ t32
6495
+ ]
6496
+ });
6497
+ $[77] = status;
6498
+ $[78] = t12;
6499
+ $[79] = t13;
6500
+ $[80] = t32;
6501
+ $[81] = t6;
6502
+ $[82] = t9;
6503
+ $[83] = t33;
6504
+ } else t33 = $[83];
6505
+ return t33;
6506
+ }
6507
+ function _temp(version_1) {
6508
+ return /* @__PURE__ */ jsx($928221da08ecbc62$export$a11e76429ed99b4, {
6509
+ id: version_1.id,
6510
+ textValue: version_1.name,
6511
+ className: "studio-version-item",
6512
+ children: version_1.name
6513
+ });
6514
+ }
6515
+ //#endregion
6516
+ //#region src/configurator/useThemeVersions.ts
6517
+ /** The editor only needs differences from the repository base theme. */
6518
+ function withoutDefaults(value, defaults) {
6519
+ const differing = (mode) => Object.fromEntries(Object.entries(value.theme[mode]).filter(([name, item]) => item !== defaults[mode][name]));
6520
+ return {
6521
+ ...value,
6522
+ theme: {
6523
+ light: differing("light"),
6524
+ dark: differing("dark"),
6525
+ theme: differing("theme")
6526
+ }
6527
+ };
6528
+ }
6529
+ /**
6530
+ * Named theme versions kept in `povio.themes.json` (or this browser when no writer is connected).
6531
+ * Loads the active version into the configuration and records every edit back into it.
6532
+ */
6533
+ function useThemeVersions(t0) {
6534
+ const $ = c(26);
6535
+ const enabled = t0 === void 0 ? true : t0;
6536
+ let t1;
6537
+ if ($[0] !== enabled) {
6538
+ t1 = {
6539
+ key: "povio-ui-theme-versions",
6540
+ metaName: themeVersionsMetaName,
6541
+ field: "themes",
6542
+ parse: parseThemeVersions,
6543
+ noun: "theme versions",
6544
+ enabled
6545
+ };
6546
+ $[0] = enabled;
6547
+ $[1] = t1;
6548
+ } else t1 = $[1];
6549
+ const storage = useRepositoryStorage(t1);
6550
+ const { value: themes, setValue: setThemes, ready } = storage;
6551
+ let t2;
6552
+ if ($[2] !== themes.activeVersionId || $[3] !== themes.versions) {
6553
+ t2 = themes.versions.find((version) => version.id === themes.activeVersionId) ?? themes.versions[0];
6554
+ $[2] = themes.activeVersionId;
6555
+ $[3] = themes.versions;
6556
+ $[4] = t2;
6557
+ } else t2 = $[4];
6558
+ const active = t2;
6559
+ const { currentConfiguration, replaceConfiguration, defaults } = useUIConfiguration();
6560
+ const loaded = useRef(null);
6561
+ const t3 = active.id;
6562
+ let t4;
6563
+ if ($[5] !== defaults) {
6564
+ t4 = JSON.stringify(defaults);
6565
+ $[5] = defaults;
6566
+ $[6] = t4;
6567
+ } else t4 = $[6];
6568
+ const key = `${t3}:${t4}`;
6569
+ let t5;
6570
+ if ($[7] !== active.configuration) {
6571
+ t5 = JSON.stringify(active.configuration);
6572
+ $[7] = active.configuration;
6573
+ $[8] = t5;
6574
+ } else t5 = $[8];
6575
+ const saved = t5;
6576
+ let t6;
6577
+ let t7;
6578
+ if ($[9] !== active.configuration || $[10] !== active.id || $[11] !== currentConfiguration || $[12] !== defaults || $[13] !== enabled || $[14] !== key || $[15] !== ready || $[16] !== replaceConfiguration || $[17] !== saved || $[18] !== setThemes) {
6579
+ t6 = () => {
6580
+ if (!enabled || !ready) return;
6581
+ if (!loaded.current || loaded.current.key !== key || loaded.current.saved !== saved) {
6582
+ const value = active.configuration ?? (loaded.current ? emptyConfiguration() : currentConfiguration);
6583
+ const sameValues = !!loaded.current && loaded.current.versionId === active.id && loaded.current.saved === saved;
6584
+ loaded.current = {
6585
+ key,
6586
+ versionId: active.id,
6587
+ saved
6588
+ };
6589
+ replaceConfiguration(withoutDefaults(value, defaults), false, !sameValues);
6590
+ return;
6591
+ }
6592
+ const snapshot = {
6593
+ ...currentConfiguration,
6594
+ theme: {
6595
+ light: {
6596
+ ...defaults.light,
6597
+ ...currentConfiguration.theme.light
6598
+ },
6599
+ dark: {
6600
+ ...defaults.dark,
6601
+ ...currentConfiguration.theme.dark
6602
+ },
6603
+ theme: {
6604
+ ...defaults.theme,
6605
+ ...currentConfiguration.theme.theme
6606
+ }
6607
+ }
6608
+ };
6609
+ const serialized = JSON.stringify(snapshot);
6610
+ if (serialized === saved) return;
6611
+ loaded.current.saved = serialized;
6612
+ setThemes((current) => ({
6613
+ ...current,
6614
+ versions: current.versions.map((version_0) => version_0.id === active.id ? {
6615
+ ...version_0,
6616
+ configuration: structuredClone(snapshot)
6617
+ } : version_0)
6618
+ }));
6619
+ };
6620
+ t7 = [
6621
+ enabled,
6622
+ ready,
6623
+ key,
6624
+ saved,
6625
+ active.id,
6626
+ active.configuration,
6627
+ defaults,
6628
+ currentConfiguration,
6629
+ replaceConfiguration,
6630
+ setThemes
6631
+ ];
6632
+ $[9] = active.configuration;
6633
+ $[10] = active.id;
6634
+ $[11] = currentConfiguration;
6635
+ $[12] = defaults;
6636
+ $[13] = enabled;
6637
+ $[14] = key;
6638
+ $[15] = ready;
6639
+ $[16] = replaceConfiguration;
6640
+ $[17] = saved;
6641
+ $[18] = setThemes;
6642
+ $[19] = t6;
6643
+ $[20] = t7;
6644
+ } else {
6645
+ t6 = $[19];
6646
+ t7 = $[20];
6647
+ }
6648
+ useLayoutEffect(t6, t7);
6649
+ let t8;
6650
+ if ($[21] !== active || $[22] !== setThemes || $[23] !== storage || $[24] !== themes) {
6651
+ t8 = {
6652
+ ...storage,
6653
+ themes,
6654
+ setThemes,
6655
+ active
6656
+ };
6657
+ $[21] = active;
6658
+ $[22] = setThemes;
6659
+ $[23] = storage;
6660
+ $[24] = themes;
6661
+ $[25] = t8;
6662
+ } else t8 = $[25];
6663
+ return t8;
6664
+ }
6665
+ //#endregion
5506
6666
  //#region src/configurator/UIConfigurator.tsx
5507
6667
  const colorNamespaces = {
5508
6668
  surface: "Surfaces",
@@ -5817,7 +6977,7 @@ function PopoutOverlay(t0) {
5817
6977
  } else t3 = $[5];
5818
6978
  return t3;
5819
6979
  }
5820
- function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating, onPopout, onDock, onMinimize, popupBlocked }) {
6980
+ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating, onPopout, onDock, popupBlocked, themeVersions }) {
5821
6981
  const Overlay = poppedOut || floating ? PopoutOverlay : $8b8d26808cb8cb53$export$8948f78d83984c69;
5822
6982
  const Surface = poppedOut || floating ? "div" : $8b8d26808cb8cb53$export$2b77a92f1a5ad772;
5823
6983
  const studioWindow = useStudioWindow(windowBounds, !poppedOut);
@@ -5837,6 +6997,7 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
5837
6997
  const [mode, setMode] = useState(remembered.mode ?? "light");
5838
6998
  const [sizeUnit, setSizeUnit] = useState(remembered.sizeUnit ?? "px");
5839
6999
  const [loadedFonts, refreshLoadedFonts] = useLoadedFonts();
7000
+ const [fontsourceFamilies, setFontsourceFamilies] = useState([]);
5840
7001
  const [editorWidth, setEditorWidth] = useState(remembered.editorWidth);
5841
7002
  const [confirmingReset, setConfirmingReset] = useState(false);
5842
7003
  const [nothingToReset, setNothingToReset] = useState(false);
@@ -5864,6 +7025,23 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
5864
7025
  useEffect(() => {
5865
7026
  setThemeWriter(getThemeWriter());
5866
7027
  }, []);
7028
+ const [componentWriter, setComponentWriter] = useState(null);
7029
+ const [repositoryComponents, setRepositoryComponents] = useState(null);
7030
+ useEffect(() => {
7031
+ const writer = getComponentConfigurationWriter();
7032
+ setComponentWriter(writer);
7033
+ if (!writer) return;
7034
+ let alive = true;
7035
+ readComponentConfiguration(writer).then(({ configuration, revision: revision_0 }) => {
7036
+ if (alive) setRepositoryComponents({
7037
+ json: canonicalJSON(configuration ?? emptyComponentConfiguration()),
7038
+ revision: revision_0
7039
+ });
7040
+ }).catch(() => {});
7041
+ return () => {
7042
+ alive = false;
7043
+ };
7044
+ }, []);
5867
7045
  const [exportTab, setExportTab] = useState(remembered.exportTab ?? "css");
5868
7046
  const editorRef = useRef(null);
5869
7047
  const previewRef = useRef(null);
@@ -5901,6 +7079,18 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
5901
7079
  const exportRef = useRef(null);
5902
7080
  const changedCount = Object.keys(draft.overrides ?? {}).length + Object.values(draft.theme).reduce((count, tokens) => count + Object.keys(tokens).length, 0) + Object.values(draft.config).reduce((count_0, value) => count_0 + (typeof value === "object" && value !== null ? Object.keys(value).length : 1), 0);
5903
7081
  const config = inherited;
7082
+ const components = {
7083
+ config: draft.config,
7084
+ overrides: draft.overrides ?? {}
7085
+ };
7086
+ const componentsJson = canonicalJSON(components);
7087
+ const componentsDiffer = !!componentWriter && !!repositoryComponents && componentsJson !== repositoryComponents.json;
7088
+ const themeCss = exportCss(draft.theme, defaults);
7089
+ const themeDiffers = [
7090
+ "light",
7091
+ "dark",
7092
+ "theme"
7093
+ ].some((mode_0) => Object.keys(parseThemeCss(themeCss)[mode_0]).length > 0);
5904
7094
  const exportText = exportTab === "css" ? exportCss(draft.theme, defaults) : exportTab === "overrides" ? exportCvaEdits(draft.overrides) : `import { UIConfig } from "@povio/ui/ui-config";\n\nconst config = ${JSON.stringify(draft.config, null, 2)} satisfies Partial<UIConfig.Options>;\n\n// Wrap your application:\n// <UIConfig.Provider config={config}>{children}</UIConfig.Provider>\n`;
5905
7095
  const changeSection = (next) => {
5906
7096
  if (compactPreview && bodyRef.current?.parentElement) bodyRef.current.parentElement.scrollTop = 0;
@@ -6174,6 +7364,7 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6174
7364
  const options = [
6175
7365
  ...themeFonts,
6176
7366
  ...loadedFonts.filter((font) => !themeFonts.some(({ value: value_11 }) => value_11 === font.value)),
7367
+ ...fontsourceFamilies.filter((font_0) => !loadedFonts.some((loaded) => loaded.label === font_0.label)),
6177
7368
  ...systemFonts
6178
7369
  ];
6179
7370
  const roleLabels = {
@@ -6196,6 +7387,23 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6196
7387
  highlighted: highlightedToken === name_24
6197
7388
  }, `${revision}-${resetRevision}-${name_24}`))
6198
7389
  }),
7390
+ /* @__PURE__ */ jsx(FontsourcePicker, {
7391
+ roles: roles.map(([token_0]) => ({
7392
+ token: token_0,
7393
+ label: roleLabels[token_0] ?? labelFor(token_0).replace(/^font /, "")
7394
+ })),
7395
+ onAdded: (font_1, role) => {
7396
+ const value_13 = `${JSON.stringify(font_1.family)}, sans-serif`;
7397
+ setFontsourceFamilies((current_12) => [...current_12.filter((item) => item.name !== `fontsource:${font_1.packageName}`), {
7398
+ name: `fontsource:${font_1.packageName}`,
7399
+ label: font_1.family,
7400
+ value: value_13,
7401
+ group: "Fontsource fonts"
7402
+ }]);
7403
+ refreshLoadedFonts();
7404
+ if (role) setToken(role, value_13);
7405
+ }
7406
+ }),
6199
7407
  /* @__PURE__ */ jsx(FontUpload, { onUpload: refreshLoadedFonts }),
6200
7408
  !!families.length && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("hr", { className: "studio-font-divider" }), families.map((entry_2) => renderToken(entry_2))] })
6201
7409
  ]
@@ -6272,18 +7480,18 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6272
7480
  /* @__PURE__ */ jsxs("header", {
6273
7481
  className: "studio-topbar",
6274
7482
  ...poppedOut ? {} : studioWindow.dragArea,
6275
- children: [
6276
- /* @__PURE__ */ jsx("div", {
6277
- className: "studio-title",
6278
- children: /* @__PURE__ */ jsx($2ec61d1d4f780267$export$a8a3e93435678ff9, {
6279
- slot: "title",
6280
- children: "Povio UI Configuration"
6281
- })
6282
- }),
6283
- /* @__PURE__ */ jsxs("nav", {
6284
- className: "studio-nav",
6285
- "aria-label": "Configuration sections",
6286
- children: [/* @__PURE__ */ jsxs("div", {
7483
+ children: [/* @__PURE__ */ jsxs("div", {
7484
+ className: "studio-title",
7485
+ children: [/* @__PURE__ */ jsx($2ec61d1d4f780267$export$a8a3e93435678ff9, {
7486
+ slot: "title",
7487
+ className: "studio-visually-hidden",
7488
+ children: "Povio UI Configuration"
7489
+ }), /* @__PURE__ */ jsx(ThemeVersionControls, { state: themeVersions })]
7490
+ }), /* @__PURE__ */ jsxs("nav", {
7491
+ className: "studio-nav",
7492
+ "aria-label": "Configuration sections",
7493
+ children: [
7494
+ /* @__PURE__ */ jsxs("div", {
6287
7495
  className: "studio-section-tabs",
6288
7496
  children: [
6289
7497
  navSections.map((group_5) => /* @__PURE__ */ jsx("button", {
@@ -6301,7 +7509,8 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6301
7509
  children: "Advanced"
6302
7510
  })
6303
7511
  ]
6304
- }), !componentsHosted && /* @__PURE__ */ jsx("button", {
7512
+ }),
7513
+ !componentsHosted && /* @__PURE__ */ jsx("button", {
6305
7514
  type: "button",
6306
7515
  className: "studio-preview-tab",
6307
7516
  "aria-current": compactPreview ? "page" : void 0,
@@ -6311,11 +7520,8 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6311
7520
  if (dialog) dialog.scrollTop = 0;
6312
7521
  },
6313
7522
  children: "Components"
6314
- })]
6315
- }),
6316
- /* @__PURE__ */ jsx("div", {
6317
- className: "studio-topbar-controls",
6318
- children: /* @__PURE__ */ jsxs("label", {
7523
+ }),
7524
+ /* @__PURE__ */ jsxs("label", {
6319
7525
  className: "studio-search",
6320
7526
  title: "Search tokens",
6321
7527
  children: [
@@ -6342,8 +7548,8 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6342
7548
  })
6343
7549
  ]
6344
7550
  })
6345
- })
6346
- ]
7551
+ ]
7552
+ })]
6347
7553
  }),
6348
7554
  /* @__PURE__ */ jsxs("div", {
6349
7555
  ref: bodyRef,
@@ -6381,8 +7587,8 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6381
7587
  role: "tab",
6382
7588
  "aria-selected": !searching && tab_0 === activeSectionTab,
6383
7589
  disabled: searching,
6384
- onClick: () => setSectionTabs((current_12) => ({
6385
- ...current_12,
7590
+ onClick: () => setSectionTabs((current_13) => ({
7591
+ ...current_13,
6386
7592
  [section]: tab_0
6387
7593
  })),
6388
7594
  children: [tab_0, groupChanged([...groupedEntries].filter(([key_14]) => tabFor(key_14) === tab_0).flatMap(([, group_6]) => group_6.entries)) && /* @__PURE__ */ jsx("span", {
@@ -6570,10 +7776,10 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6570
7776
  children: [/* @__PURE__ */ jsxs("span", { children: ["Use static inputs until interaction", /* @__PURE__ */ jsx("small", { children: "Reduce work when rendering large forms." })] }), /* @__PURE__ */ jsx("input", {
6571
7777
  type: "checkbox",
6572
7778
  checked: config.renderStaticInput,
6573
- onChange: (event_5) => setDraft((current_13) => ({
6574
- ...current_13,
7779
+ onChange: (event_5) => setDraft((current_14) => ({
7780
+ ...current_14,
6575
7781
  config: {
6576
- ...current_13.config,
7782
+ ...current_14.config,
6577
7783
  renderStaticInput: event_5.target.checked
6578
7784
  }
6579
7785
  }))
@@ -6758,19 +7964,43 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6758
7964
  }),
6759
7965
  /* @__PURE__ */ jsx("button", {
6760
7966
  className: "studio-primary",
6761
- "aria-label": "Close UI configuration",
6762
- onClick: apply,
6763
- children: "Close"
7967
+ disabled: !(themeWriter && themeDiffers || componentsDiffer) || savingTheme || !!Object.keys(invalidFields).length,
7968
+ title: !themeWriter && !componentWriter ? "Applying needs the uiConfigurator Vite plugin" : themeWriter && themeDiffers || componentsDiffer ? `Write this version to ${[themeWriter && themeDiffers && themeWriter.file, componentsDiffer && componentWriter?.file].filter(Boolean).join(" and ")}` : "The repository already matches this version",
7969
+ onClick: async () => {
7970
+ setSavingTheme(true);
7971
+ setMessage("");
7972
+ const written = [];
7973
+ try {
7974
+ if (themeWriter && themeDiffers) {
7975
+ await saveThemeCss(themeWriter, themeCss);
7976
+ written.push(themeWriter.file);
7977
+ }
7978
+ if (componentsDiffer && componentWriter && repositoryComponents) {
7979
+ const revision_1 = await saveComponentConfiguration(componentWriter, components, repositoryComponents.revision);
7980
+ setRepositoryComponents({
7981
+ json: componentsJson,
7982
+ revision: revision_1
7983
+ });
7984
+ written.push(componentWriter.file);
7985
+ }
7986
+ setMessage(`Applied to ${written.join(" and ")}.`);
7987
+ } catch (error_0) {
7988
+ setMessage(error_0 instanceof Error ? error_0.message : "Could not apply theme.");
7989
+ } finally {
7990
+ setSavingTheme(false);
7991
+ }
7992
+ },
7993
+ children: savingTheme ? "Applying…" : "Apply theme"
6764
7994
  })
6765
7995
  ]
6766
7996
  }),
6767
7997
  /* @__PURE__ */ jsx("button", {
6768
7998
  type: "button",
6769
- className: "studio-window-minimize",
6770
- "aria-label": "Minimize UI configuration",
6771
- title: "Minimize to floating button",
6772
- onClick: onMinimize,
6773
- children: /* @__PURE__ */ jsx(Minus, { size: 17 })
7999
+ className: "studio-window-close",
8000
+ "aria-label": "Close UI configuration",
8001
+ title: "Close",
8002
+ onClick: apply,
8003
+ children: /* @__PURE__ */ jsx(X, { size: 17 })
6774
8004
  }),
6775
8005
  /* @__PURE__ */ jsx("button", {
6776
8006
  type: "button",
@@ -6785,8 +8015,7 @@ function ConfigurationModalContent({ memory, windowBounds, poppedOut, floating,
6785
8015
  className: "studio-window-grip",
6786
8016
  "aria-label": "Move UI configuration",
6787
8017
  title: "Drag to move, or use arrow keys",
6788
- ...studioWindow.move,
6789
- children: /* @__PURE__ */ jsx(GripVertical, { size: 20 })
8018
+ ...studioWindow.move
6790
8019
  }), windowEdges.map((edge) => /* @__PURE__ */ jsx("button", {
6791
8020
  type: "button",
6792
8021
  className: `studio-window-resize studio-window-resize-${edge}`,
@@ -6847,12 +8076,8 @@ const floatingDefault = {
6847
8076
  };
6848
8077
  const hostedFloatingWidth = 360;
6849
8078
  function UIConfigurationModal() {
6850
- const { isOpen, revision, open, openFloating, floating, componentsHosted } = useUIConfiguration();
6851
- const [minimized, setMinimized] = useState(false);
6852
- const [hasLauncher, setHasLauncher] = useState(false);
6853
- const restorePopout = useRef(false);
6854
- const launcherPosition = useRef(null);
6855
- useEffect(() => setMinimized(false), [revision]);
8079
+ const { isOpen, revision, floating, componentsHosted } = useUIConfiguration();
8080
+ const themeVersions = useThemeVersions();
6856
8081
  const memory = useRef({});
6857
8082
  const windowBounds = useRef(null);
6858
8083
  const floatingBounds = useRef(floatingDefault);
@@ -6860,39 +8085,17 @@ function UIConfigurationModal() {
6860
8085
  ...floatingDefault,
6861
8086
  width: hostedFloatingWidth
6862
8087
  };
6863
- const popout = useStudioPopout(isOpen && !minimized);
6864
- if ((!isOpen || minimized) && hasLauncher && !componentsHosted) return createPortal(/* @__PURE__ */ jsx(ConfigurationLauncher, {
6865
- position: launcherPosition,
6866
- onOpen: () => {
6867
- setMinimized(false);
6868
- if (!isOpen) {
6869
- if (floating) openFloating();
6870
- else open();
6871
- }
6872
- if (restorePopout.current) popout.open();
6873
- }
6874
- }), document.body);
6875
- if (!isOpen || minimized) return null;
8088
+ const popout = useStudioPopout(isOpen);
8089
+ if (!isOpen) return null;
6876
8090
  const content = /* @__PURE__ */ jsx(ConfigurationModalContent, {
6877
8091
  memory,
6878
8092
  windowBounds: floating ? floatingBounds : windowBounds,
6879
8093
  floating,
6880
8094
  poppedOut: !!popout.popup,
6881
- onPopout: () => {
6882
- restorePopout.current = true;
6883
- popout.open();
6884
- },
6885
- onDock: () => {
6886
- restorePopout.current = false;
6887
- popout.dock();
6888
- },
6889
- onMinimize: () => {
6890
- restorePopout.current = !!popout.popup;
6891
- setHasLauncher(true);
6892
- setMinimized(true);
6893
- popout.dock();
6894
- },
6895
- popupBlocked: popout.blocked
8095
+ onPopout: popout.open,
8096
+ onDock: popout.dock,
8097
+ popupBlocked: popout.blocked,
8098
+ themeVersions
6896
8099
  }, revision);
6897
8100
  return popout.popup ? createPortal(/* @__PURE__ */ jsx(StudioPopoutContext, {
6898
8101
  value: true,
@@ -6903,4 +8106,4 @@ function UIConfigurationModal() {
6903
8106
  }), popout.popup.document.body) : floating ? createPortal(content, document.body) : content;
6904
8107
  }
6905
8108
  //#endregion
6906
- export { inspectElement as a, HighlightOverlay as c, emptyConfiguration as d, useUIConfiguration as f, Moon as g, Pin as h, saveThemeCss as i, InspectorDetails as l, Plus as m, ColorModeSwitch as n, inspectTokens as o, Sun as p, getThemeWriter as r, getBoxHighlights as s, UIConfigurationModal as t, UIConfigurationProvider as u };
8109
+ export { Moon as _, inspectElement as a, HighlightOverlay as c, useUIConfiguration as d, Trash as f, Pencil as g, Pin as h, resolveUIConfiguration as i, InspectorDetails as l, Plus as m, ColorModeSwitch as n, inspectTokens as o, Sun as p, useRepositoryStorage as r, getBoxHighlights as s, UIConfigurationModal as t, UIConfigurationProvider as u };