@povio/ui 5.0.0-rc.11 → 5.0.0-rc.13

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 (37) hide show
  1. package/README.md +6 -0
  2. package/dist/{AppCanvasPage-BTmt8poT.d.ts → AppCanvasPage-BZ2mrKY4.d.ts} +3 -1
  3. package/dist/{AppCanvasPage-BsQrZ8q7.js → AppCanvasPage-DtWEvs_Y.js} +1065 -316
  4. package/dist/{CanvasComponentsFrame-C0Sf9RcC.js → CanvasComponentsFrame-CbgJJv-3.js} +1 -1
  5. package/dist/{ComponentGallery-DDSqnm9f.js → ComponentGallery-COqEqqN6.js} +2 -2
  6. package/dist/{Form-DmcByQts.d.ts → Form-DAtj2szV.d.ts} +1 -1
  7. package/dist/{Inputs-C0sUmuPv.d.ts → Inputs-C_6dVwC7.d.ts} +1 -1
  8. package/dist/{Table-BN2_J_Ne.js → Table-piX1SWvN.js} +54 -1
  9. package/dist/{UIConfigurator-C_2kgJtm.js → UIConfigurator-DekPXjv9.js} +1290 -406
  10. package/dist/config/app-canvas-tanstack.d.ts +1 -1
  11. package/dist/config/app-canvas-tanstack.js +1 -1
  12. package/dist/config/app-canvas.d.ts +2 -2
  13. package/dist/config/app-canvas.js +1 -1
  14. package/dist/config/ui-config.d.ts +1 -1
  15. package/dist/config/ui-configurator-vite.d.ts +9 -0
  16. package/dist/config/ui-configurator-vite.js +449 -10
  17. package/dist/config/ui-configurator.d.ts +14 -2
  18. package/dist/config/ui-configurator.js +2 -2
  19. package/dist/{configuration.context-qdjDwQJj.d.ts → configuration.context-3I7-wBII.d.ts} +4 -1
  20. package/dist/{dynamicInputs-DdcsSoYu.d.ts → dynamicInputs-R5i7AT_C.d.ts} +1 -1
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +3 -3
  23. package/dist/rhf/inputs.d.ts +1 -1
  24. package/dist/rhf.d.ts +2 -2
  25. package/dist/rhf.js +3 -3
  26. package/dist/styles/app-canvas.css +402 -18
  27. package/dist/styles/configurator.css +251 -92
  28. package/dist/{table.cva-D5oQ-QvA.js → table.cva-DlMT-zLD.js} +1 -54
  29. package/dist/tanstack/form.d.ts +1 -1
  30. package/dist/tanstack/inputs.d.ts +2 -2
  31. package/dist/tanstack/table.js +2 -2
  32. package/dist/tanstack.d.ts +4 -4
  33. package/dist/tanstack.js +3 -3
  34. package/dist/{uiConfig.context-BqKWd_MO.d.ts → uiConfig.context-BksnNQyw.d.ts} +1 -1
  35. package/dist/{useTableColumnConfig-6zn3zKl3.js → useTableColumnConfig-CNJ_ZvIN.js} +1 -1
  36. package/package.json +1 -1
  37. package/src/configurator/README.md +18 -1
@@ -1,44 +1,19 @@
1
1
  import { t as createLucideIcon } from "./createLucideIcon-LcezOE4z.js";
2
- import { a as getBoxHighlights, d as Pin, f as Moon, l as useUIConfiguration, o as HighlightOverlay, r as inspectElement, s as InspectorDetails, t as UIConfigurationModal, u as Sun } from "./UIConfigurator-C_2kgJtm.js";
2
+ import { _ as Moon, a as inspectElement, c as HighlightOverlay, d as useUIConfiguration, f as Trash, g as Pencil, h as Pin, l as InspectorDetails, m as Plus, p as Sun, r as useRepositoryStorage, s as getBoxHighlights, t as UIConfigurationModal } from "./UIConfigurator-DekPXjv9.js";
3
3
  import { t as ChevronDown } from "./chevron-down-B4Pr8bD3.js";
4
4
  import { t as ChevronRight } from "./chevron-right-3nKJR5Bb.js";
5
5
  import { i as Search } from "./selectDesktop.cva-CHAMxhfO.js";
6
6
  import { t as Upload } from "./upload-CXU83BJ4.js";
7
7
  import { t as X } from "./x-B6vfz8Ps.js";
8
+ import { c as $542a13ca2fa5b484$export$5b6b19405a83ff9d, n as $f2ff30fde7b014be$export$3ddf2d174ce01153 } from "./Dialog-CKLkS00H.js";
9
+ import { t as $2ec61d1d4f780267$export$a8a3e93435678ff9 } from "./Heading-nI1_VOaE.js";
10
+ import { t as Select } from "./Select-CgIhoWgk.js";
11
+ import { t as ThemeContext } from "./theme.context-B3wQDJ-i.js";
8
12
  import { c as isEditableToken } from "./theme-model-DtTsIxou.js";
9
13
  import { t as componentArtboards } from "./groups-oQmSeqXn.js";
10
- import { Suspense, lazy, memo, useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
14
+ import { Suspense, lazy, memo, useCallback, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState } from "react";
11
15
  import { c } from "react/compiler-runtime";
12
16
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
- //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/code-xml.mjs
14
- /**
15
- * @license lucide-react v1.43.0 - ISC
16
- *
17
- * This source code is licensed under the ISC license.
18
- * See the LICENSE file in the root directory of this source tree.
19
- */
20
- const __iconData$9 = {
21
- name: "code-xml",
22
- size: 24,
23
- node: [
24
- ["path", {
25
- d: "m18 16 4-4-4-4",
26
- key: "1inbqp"
27
- }],
28
- ["path", {
29
- d: "m6 8-4 4 4 4",
30
- key: "15zrgr"
31
- }],
32
- ["path", {
33
- d: "m14.5 4-5 16",
34
- key: "e7oirm"
35
- }]
36
- ],
37
- aliases: ["code-2"]
38
- };
39
- __iconData$9.node;
40
- const CodeXml = createLucideIcon(__iconData$9);
41
- //#endregion
42
17
  //#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/download.mjs
43
18
  /**
44
19
  * @license lucide-react v1.43.0 - ISC
@@ -330,6 +305,674 @@ const __iconData = {
330
305
  __iconData.node;
331
306
  const Settings2 = createLucideIcon(__iconData);
332
307
  //#endregion
308
+ //#region src/app-canvas/manifest.ts
309
+ function createCanvasWorkspace(name, id) {
310
+ return {
311
+ id,
312
+ name: name.trim() || "Workspace",
313
+ enabledPages: null,
314
+ positions: {}
315
+ };
316
+ }
317
+ function createCanvasManifest() {
318
+ return {
319
+ version: 2,
320
+ workspaces: [createCanvasWorkspace("All pages", "default")],
321
+ activeWorkspaceId: "default",
322
+ examples: {},
323
+ breakpoint: "desktop"
324
+ };
325
+ }
326
+ function isRecord(value) {
327
+ return value !== null && typeof value === "object" && !Array.isArray(value);
328
+ }
329
+ function isJSON(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
330
+ if (value === null || typeof value === "string" || typeof value === "boolean") return true;
331
+ if (typeof value === "number") return Number.isFinite(value);
332
+ if (typeof value !== "object" || depth > 30 || ancestors.has(value)) return false;
333
+ ancestors.add(value);
334
+ const valid = Object.values(value).every((child) => isJSON(child, ancestors, depth + 1));
335
+ ancestors.delete(value);
336
+ return valid;
337
+ }
338
+ function parsePositions(input) {
339
+ return Object.fromEntries(Object.entries(isRecord(input) ? input : {}).flatMap(([breakpoint, entries]) => {
340
+ if (!isRecord(entries)) return [];
341
+ return [[breakpoint, Object.fromEntries(Object.entries(entries).flatMap(([id, point]) => isRecord(point) && typeof point.x === "number" && Number.isFinite(point.x) && typeof point.y === "number" && Number.isFinite(point.y) ? [[id, {
342
+ x: point.x,
343
+ y: point.y
344
+ }]] : []))]];
345
+ }));
346
+ }
347
+ /** Sanitize either stored JSON text or an imported value, migrating v1 positions into the default workspace. */
348
+ function parseCanvasManifest(input) {
349
+ const fallback = createCanvasManifest();
350
+ let value = input;
351
+ if (typeof value === "string") try {
352
+ value = JSON.parse(value);
353
+ } catch {
354
+ return fallback;
355
+ }
356
+ if (!isRecord(value) || value.version !== 1 && value.version !== 2) return fallback;
357
+ const examples = Object.fromEntries(Object.entries(isRecord(value.examples) ? value.examples : {}).flatMap(([id, example]) => {
358
+ if (!isRecord(example)) return [];
359
+ const clean = {};
360
+ if (typeof example.title === "string") clean.title = example.title;
361
+ if (isRecord(example.params)) clean.params = Object.fromEntries(Object.entries(example.params).filter((entry) => typeof entry[1] === "string"));
362
+ if (isRecord(example.search)) clean.search = Object.fromEntries(Object.entries(example.search).filter(([, item]) => isJSON(item)));
363
+ return [[id, clean]];
364
+ }));
365
+ const workspaces = [];
366
+ const ids = /* @__PURE__ */ new Set();
367
+ if (value.version === 1) workspaces.push({
368
+ ...createCanvasWorkspace("All pages", "default"),
369
+ positions: parsePositions(value.positions)
370
+ });
371
+ else if (Array.isArray(value.workspaces)) for (const entry of value.workspaces) {
372
+ if (!isRecord(entry) || typeof entry.id !== "string" || !entry.id.trim() || ids.has(entry.id)) continue;
373
+ ids.add(entry.id);
374
+ workspaces.push({
375
+ id: entry.id,
376
+ name: typeof entry.name === "string" && entry.name.trim() ? entry.name.trim() : "Workspace",
377
+ enabledPages: Array.isArray(entry.enabledPages) ? [...new Set(entry.enabledPages.filter((id) => typeof id === "string" && id.length > 0))] : null,
378
+ positions: parsePositions(entry.positions)
379
+ });
380
+ }
381
+ if (!workspaces.length) workspaces.push(createCanvasWorkspace("All pages", "default"));
382
+ return {
383
+ version: 2,
384
+ workspaces,
385
+ activeWorkspaceId: typeof value.activeWorkspaceId === "string" && workspaces.some((workspace) => workspace.id === value.activeWorkspaceId) ? value.activeWorkspaceId : workspaces[0].id,
386
+ examples,
387
+ breakpoint: typeof value.breakpoint === "string" && value.breakpoint.length > 0 ? value.breakpoint : fallback.breakpoint
388
+ };
389
+ }
390
+ //#endregion
391
+ //#region src/app-canvas/WorkspaceControls.tsx
392
+ function WorkspaceControls(t0) {
393
+ const $ = c(125);
394
+ const { manifest, setManifest, pages, disabled } = t0;
395
+ const createTrigger = useRef(null);
396
+ const editTrigger = useRef(null);
397
+ let t1;
398
+ if ($[0] !== manifest.activeWorkspaceId || $[1] !== manifest.workspaces) {
399
+ let t2;
400
+ if ($[3] !== manifest.activeWorkspaceId) {
401
+ t2 = (item) => item.id === manifest.activeWorkspaceId;
402
+ $[3] = manifest.activeWorkspaceId;
403
+ $[4] = t2;
404
+ } else t2 = $[4];
405
+ t1 = manifest.workspaces.find(t2);
406
+ $[0] = manifest.activeWorkspaceId;
407
+ $[1] = manifest.workspaces;
408
+ $[2] = t1;
409
+ } else t1 = $[2];
410
+ const workspace = t1;
411
+ const [action, setAction] = useState(null);
412
+ const [name, setName] = useState("");
413
+ const [draftPages, setDraftPages] = useState(null);
414
+ const [query, setQuery] = useState("");
415
+ let t2;
416
+ if ($[5] === Symbol.for("react.memo_cache_sentinel")) {
417
+ t2 = () => {
418
+ setAction(null);
419
+ };
420
+ $[5] = t2;
421
+ } else t2 = $[5];
422
+ let t3;
423
+ if ($[6] !== workspace.id) {
424
+ t3 = [workspace.id];
425
+ $[6] = workspace.id;
426
+ $[7] = t3;
427
+ } else t3 = $[7];
428
+ useEffect(t2, t3);
429
+ let t4;
430
+ let t5;
431
+ if ($[8] !== disabled) {
432
+ t4 = () => {
433
+ if (disabled) setAction(null);
434
+ };
435
+ t5 = [disabled];
436
+ $[8] = disabled;
437
+ $[9] = t4;
438
+ $[10] = t5;
439
+ } else {
440
+ t4 = $[9];
441
+ t5 = $[10];
442
+ }
443
+ useEffect(t4, t5);
444
+ let t6;
445
+ if ($[11] !== workspace.enabledPages || $[12] !== workspace.name) {
446
+ t6 = (next) => {
447
+ setName(next === "create" ? "" : workspace.name);
448
+ setDraftPages(next === "create" ? [] : workspace.enabledPages);
449
+ setQuery("");
450
+ setAction(next);
451
+ };
452
+ $[11] = workspace.enabledPages;
453
+ $[12] = workspace.name;
454
+ $[13] = t6;
455
+ } else t6 = $[13];
456
+ const open = t6;
457
+ let t7;
458
+ if ($[14] !== draftPages) {
459
+ t7 = (id) => draftPages === null || draftPages.includes(id);
460
+ $[14] = draftPages;
461
+ $[15] = t7;
462
+ } else t7 = $[15];
463
+ const enabled = t7;
464
+ const title = action === "create" ? "Create workspace" : action === "delete" ? "Delete workspace?" : "Edit workspace";
465
+ let t8;
466
+ if ($[16] !== action || $[17] !== disabled || $[18] !== draftPages || $[19] !== name || $[20] !== setManifest || $[21] !== workspace.id) {
467
+ t8 = () => {
468
+ if (disabled || !action) return;
469
+ if ((action === "create" || action === "rename") && !name.trim()) return;
470
+ const created = action === "create" ? {
471
+ ...createCanvasWorkspace(name.trim(), crypto.randomUUID()),
472
+ enabledPages: draftPages
473
+ } : null;
474
+ setManifest((current) => {
475
+ if (created) return {
476
+ ...current,
477
+ workspaces: [...current.workspaces, created],
478
+ activeWorkspaceId: created.id
479
+ };
480
+ if (action === "delete") {
481
+ if (current.workspaces.length < 2) return current;
482
+ const remaining = current.workspaces.filter((item_0) => item_0.id !== workspace.id);
483
+ return {
484
+ ...current,
485
+ workspaces: remaining,
486
+ activeWorkspaceId: remaining[0].id
487
+ };
488
+ }
489
+ return {
490
+ ...current,
491
+ workspaces: current.workspaces.map((item_1) => item_1.id === workspace.id ? {
492
+ ...item_1,
493
+ ...action === "rename" && { name: name.trim() },
494
+ enabledPages: draftPages
495
+ } : item_1)
496
+ };
497
+ });
498
+ setAction(null);
499
+ };
500
+ $[16] = action;
501
+ $[17] = disabled;
502
+ $[18] = draftPages;
503
+ $[19] = name;
504
+ $[20] = setManifest;
505
+ $[21] = workspace.id;
506
+ $[22] = t8;
507
+ } else t8 = $[22];
508
+ const save = t8;
509
+ let T0;
510
+ let T1;
511
+ let t10;
512
+ let t11;
513
+ let t12;
514
+ let t13;
515
+ let t14;
516
+ let t15;
517
+ let t16;
518
+ let t17;
519
+ let t18;
520
+ let t19;
521
+ let t20;
522
+ let t21;
523
+ let t9;
524
+ if ($[23] !== action || $[24] !== disabled || $[25] !== draftPages || $[26] !== enabled || $[27] !== manifest.workspaces || $[28] !== name || $[29] !== open || $[30] !== pages || $[31] !== query || $[32] !== save || $[33] !== setManifest || $[34] !== title || $[35] !== workspace.id || $[36] !== workspace.name) {
525
+ let t22;
526
+ if ($[52] !== query) {
527
+ t22 = (page) => `${page.title} ${page.path}`.toLowerCase().includes(query.toLowerCase());
528
+ $[52] = query;
529
+ $[53] = t22;
530
+ } else t22 = $[53];
531
+ const visiblePages = pages.filter(t22);
532
+ const t23 = workspace.id;
533
+ let t24;
534
+ if ($[54] !== manifest.workspaces) {
535
+ t24 = manifest.workspaces.map(_temp);
536
+ $[54] = manifest.workspaces;
537
+ $[55] = t24;
538
+ } else t24 = $[55];
539
+ let t25;
540
+ if ($[56] !== setManifest) {
541
+ t25 = (id_0) => {
542
+ if (id_0 !== null) setManifest((current_0) => ({
543
+ ...current_0,
544
+ activeWorkspaceId: String(id_0)
545
+ }));
546
+ };
547
+ $[56] = setManifest;
548
+ $[57] = t25;
549
+ } else t25 = $[57];
550
+ let t26;
551
+ if ($[58] !== disabled || $[59] !== t24 || $[60] !== t25 || $[61] !== workspace.id) {
552
+ t26 = /* @__PURE__ */ jsx("div", {
553
+ className: "povio-canvas-workspace-select",
554
+ children: /* @__PURE__ */ jsx(Select, {
555
+ label: "Workspace",
556
+ hideLabel: true,
557
+ size: "small",
558
+ variant: "outlined",
559
+ as: "default",
560
+ className: "povio-canvas-workspace-input",
561
+ selectionMode: "single",
562
+ isClearable: false,
563
+ isDisabled: disabled,
564
+ renderStaticInput: false,
565
+ value: t23,
566
+ items: t24,
567
+ onChange: t25
568
+ })
569
+ });
570
+ $[58] = disabled;
571
+ $[59] = t24;
572
+ $[60] = t25;
573
+ $[61] = workspace.id;
574
+ $[62] = t26;
575
+ } else t26 = $[62];
576
+ let t27;
577
+ if ($[63] !== open) {
578
+ t27 = () => open("create");
579
+ $[63] = open;
580
+ $[64] = t27;
581
+ } else t27 = $[64];
582
+ let t28;
583
+ if ($[65] === Symbol.for("react.memo_cache_sentinel")) {
584
+ t28 = /* @__PURE__ */ jsx(Plus, { size: 15 });
585
+ $[65] = t28;
586
+ } else t28 = $[65];
587
+ let t29;
588
+ if ($[66] !== t27) {
589
+ t29 = /* @__PURE__ */ jsx("button", {
590
+ ref: createTrigger,
591
+ "aria-label": "Add workspace",
592
+ title: "Add workspace",
593
+ onClick: t27,
594
+ children: t28
595
+ });
596
+ $[66] = t27;
597
+ $[67] = t29;
598
+ } else t29 = $[67];
599
+ let t30;
600
+ if ($[68] !== open) {
601
+ t30 = () => open("rename");
602
+ $[68] = open;
603
+ $[69] = t30;
604
+ } else t30 = $[69];
605
+ let t31;
606
+ if ($[70] === Symbol.for("react.memo_cache_sentinel")) {
607
+ t31 = /* @__PURE__ */ jsx(Pencil, { size: 14 });
608
+ $[70] = t31;
609
+ } else t31 = $[70];
610
+ let t32;
611
+ if ($[71] !== t30) {
612
+ t32 = /* @__PURE__ */ jsx("button", {
613
+ ref: editTrigger,
614
+ "aria-label": "Rename workspace",
615
+ title: "Edit workspace",
616
+ onClick: t30,
617
+ children: t31
618
+ });
619
+ $[71] = t30;
620
+ $[72] = t32;
621
+ } else t32 = $[72];
622
+ let t33;
623
+ if ($[73] !== t26 || $[74] !== t29 || $[75] !== t32) {
624
+ t33 = /* @__PURE__ */ jsxs("div", {
625
+ className: "povio-canvas-workspace-picker",
626
+ children: [
627
+ t26,
628
+ t29,
629
+ t32
630
+ ]
631
+ });
632
+ $[73] = t26;
633
+ $[74] = t29;
634
+ $[75] = t32;
635
+ $[76] = t33;
636
+ } else t33 = $[76];
637
+ if ($[77] !== disabled || $[78] !== t33) {
638
+ t21 = /* @__PURE__ */ jsx("fieldset", {
639
+ className: "povio-canvas-workspaces",
640
+ disabled,
641
+ children: t33
642
+ });
643
+ $[77] = disabled;
644
+ $[78] = t33;
645
+ $[79] = t21;
646
+ } else t21 = $[79];
647
+ T1 = $542a13ca2fa5b484$export$5b6b19405a83ff9d;
648
+ t15 = action === "create" ? createTrigger : editTrigger;
649
+ t16 = "bottom start";
650
+ t17 = 6;
651
+ t18 = "povio-canvas-workspace-popover";
652
+ t19 = action !== null;
653
+ if ($[80] === Symbol.for("react.memo_cache_sentinel")) {
654
+ t20 = (isOpen) => {
655
+ if (!isOpen) setAction(null);
656
+ };
657
+ $[80] = t20;
658
+ } else t20 = $[80];
659
+ T0 = $f2ff30fde7b014be$export$3ddf2d174ce01153;
660
+ t12 = action;
661
+ t13 = title;
662
+ t14 = action === "delete" ? "alertdialog" : "dialog";
663
+ if ($[81] !== save) {
664
+ t9 = (event) => {
665
+ event.preventDefault();
666
+ save();
667
+ };
668
+ $[81] = save;
669
+ $[82] = t9;
670
+ } else t9 = $[82];
671
+ let t34;
672
+ if ($[83] !== title) {
673
+ t34 = /* @__PURE__ */ jsx($2ec61d1d4f780267$export$a8a3e93435678ff9, {
674
+ slot: "title",
675
+ children: title
676
+ });
677
+ $[83] = title;
678
+ $[84] = t34;
679
+ } else t34 = $[84];
680
+ let t35;
681
+ if ($[85] === Symbol.for("react.memo_cache_sentinel")) {
682
+ t35 = /* @__PURE__ */ jsx("button", {
683
+ type: "button",
684
+ "aria-label": "Close workspace dialog",
685
+ onClick: () => setAction(null),
686
+ children: /* @__PURE__ */ jsx(X, { size: 12 })
687
+ });
688
+ $[85] = t35;
689
+ } else t35 = $[85];
690
+ if ($[86] !== t34) {
691
+ t10 = /* @__PURE__ */ jsxs("header", {
692
+ className: "povio-canvas-workspace-popover-heading",
693
+ children: [t34, t35]
694
+ });
695
+ $[86] = t34;
696
+ $[87] = t10;
697
+ } else t10 = $[87];
698
+ t11 = action === "delete" ? /* @__PURE__ */ jsxs("p", { children: [
699
+ "Delete “",
700
+ workspace.name,
701
+ "” and its saved layout and theme?"
702
+ ] }) : /* @__PURE__ */ jsxs(Fragment$1, { children: [(action === "create" || action === "rename") && /* @__PURE__ */ jsxs("label", { children: ["Workspace name", /* @__PURE__ */ jsx("input", {
703
+ autoFocus: true,
704
+ value: name,
705
+ maxLength: 80,
706
+ placeholder: "Workspace name",
707
+ onChange: (event_0) => setName(event_0.target.value),
708
+ required: true
709
+ })] }), (action === "create" || action === "rename") && /* @__PURE__ */ jsxs("div", {
710
+ role: "group",
711
+ "aria-label": "Enabled pages",
712
+ className: "povio-canvas-popover-pages",
713
+ children: [
714
+ /* @__PURE__ */ jsxs("div", {
715
+ className: "povio-canvas-popover-page-tools",
716
+ children: [
717
+ /* @__PURE__ */ jsxs("span", { children: [
718
+ pages.filter((page_0) => enabled(page_0.id)).length,
719
+ "/",
720
+ pages.length,
721
+ " pages"
722
+ ] }),
723
+ /* @__PURE__ */ jsx("button", {
724
+ type: "button",
725
+ onClick: () => setDraftPages(null),
726
+ children: "Enable all"
727
+ }),
728
+ /* @__PURE__ */ jsx("button", {
729
+ type: "button",
730
+ onClick: () => setDraftPages([]),
731
+ children: "Disable all"
732
+ })
733
+ ]
734
+ }),
735
+ /* @__PURE__ */ jsxs("label", {
736
+ className: "povio-canvas-popover-search",
737
+ children: [/* @__PURE__ */ jsx(Search, { size: 12 }), /* @__PURE__ */ jsx("input", {
738
+ "aria-label": "Find workspace pages",
739
+ placeholder: "Find pages…",
740
+ value: query,
741
+ onChange: (event_1) => setQuery(event_1.target.value)
742
+ })]
743
+ }),
744
+ /* @__PURE__ */ jsxs("div", {
745
+ className: "povio-canvas-popover-page-list",
746
+ children: [visiblePages.map((page_1) => /* @__PURE__ */ jsxs("label", { children: [/* @__PURE__ */ jsx("input", {
747
+ type: "checkbox",
748
+ checked: enabled(page_1.id),
749
+ onChange: (event_2) => {
750
+ const ids = draftPages ?? pages.map(_temp2);
751
+ setDraftPages(event_2.target.checked ? [.../* @__PURE__ */ new Set([...ids, page_1.id])] : ids.filter((id_1) => id_1 !== page_1.id));
752
+ }
753
+ }), /* @__PURE__ */ jsx("span", { children: page_1.title })] }, page_1.id)), !visiblePages.length && /* @__PURE__ */ jsx("p", { children: "No matching pages." })]
754
+ })
755
+ ]
756
+ })] });
757
+ $[23] = action;
758
+ $[24] = disabled;
759
+ $[25] = draftPages;
760
+ $[26] = enabled;
761
+ $[27] = manifest.workspaces;
762
+ $[28] = name;
763
+ $[29] = open;
764
+ $[30] = pages;
765
+ $[31] = query;
766
+ $[32] = save;
767
+ $[33] = setManifest;
768
+ $[34] = title;
769
+ $[35] = workspace.id;
770
+ $[36] = workspace.name;
771
+ $[37] = T0;
772
+ $[38] = T1;
773
+ $[39] = t10;
774
+ $[40] = t11;
775
+ $[41] = t12;
776
+ $[42] = t13;
777
+ $[43] = t14;
778
+ $[44] = t15;
779
+ $[45] = t16;
780
+ $[46] = t17;
781
+ $[47] = t18;
782
+ $[48] = t19;
783
+ $[49] = t20;
784
+ $[50] = t21;
785
+ $[51] = t9;
786
+ } else {
787
+ T0 = $[37];
788
+ T1 = $[38];
789
+ t10 = $[39];
790
+ t11 = $[40];
791
+ t12 = $[41];
792
+ t13 = $[42];
793
+ t14 = $[43];
794
+ t15 = $[44];
795
+ t16 = $[45];
796
+ t17 = $[46];
797
+ t18 = $[47];
798
+ t19 = $[48];
799
+ t20 = $[49];
800
+ t21 = $[50];
801
+ t9 = $[51];
802
+ }
803
+ let t22;
804
+ if ($[88] !== action || $[89] !== manifest.workspaces.length) {
805
+ t22 = action === "rename" ? /* @__PURE__ */ jsxs("button", {
806
+ type: "button",
807
+ className: "povio-canvas-workspace-delete",
808
+ disabled: manifest.workspaces.length < 2,
809
+ onClick: () => setAction("delete"),
810
+ children: [/* @__PURE__ */ jsx(Trash, { size: 12 }), " Delete workspace"]
811
+ }) : /* @__PURE__ */ jsx("button", {
812
+ type: "button",
813
+ autoFocus: action === "delete",
814
+ onClick: () => action === "delete" ? setAction("rename") : setAction(null),
815
+ children: action === "delete" ? "Back" : "Cancel"
816
+ });
817
+ $[88] = action;
818
+ $[89] = manifest.workspaces.length;
819
+ $[90] = t22;
820
+ } else t22 = $[90];
821
+ const t23 = action === "delete" ? "is-danger" : "is-primary";
822
+ let t24;
823
+ if ($[91] !== action || $[92] !== disabled || $[93] !== name) {
824
+ t24 = disabled || (action === "create" || action === "rename") && !name.trim();
825
+ $[91] = action;
826
+ $[92] = disabled;
827
+ $[93] = name;
828
+ $[94] = t24;
829
+ } else t24 = $[94];
830
+ const t25 = action === "create" ? "Create workspace" : action === "delete" ? "Delete" : "Save";
831
+ let t26;
832
+ if ($[95] !== t23 || $[96] !== t24 || $[97] !== t25) {
833
+ t26 = /* @__PURE__ */ jsx("button", {
834
+ type: "submit",
835
+ className: t23,
836
+ disabled: t24,
837
+ children: t25
838
+ });
839
+ $[95] = t23;
840
+ $[96] = t24;
841
+ $[97] = t25;
842
+ $[98] = t26;
843
+ } else t26 = $[98];
844
+ let t27;
845
+ if ($[99] !== t22 || $[100] !== t26) {
846
+ t27 = /* @__PURE__ */ jsxs("div", {
847
+ className: "povio-canvas-workspace-popover-actions",
848
+ children: [t22, t26]
849
+ });
850
+ $[99] = t22;
851
+ $[100] = t26;
852
+ $[101] = t27;
853
+ } else t27 = $[101];
854
+ let t28;
855
+ if ($[102] !== t10 || $[103] !== t11 || $[104] !== t27 || $[105] !== t9) {
856
+ t28 = /* @__PURE__ */ jsxs("form", {
857
+ onSubmit: t9,
858
+ children: [
859
+ t10,
860
+ t11,
861
+ t27
862
+ ]
863
+ });
864
+ $[102] = t10;
865
+ $[103] = t11;
866
+ $[104] = t27;
867
+ $[105] = t9;
868
+ $[106] = t28;
869
+ } else t28 = $[106];
870
+ let t29;
871
+ if ($[107] !== T0 || $[108] !== t12 || $[109] !== t13 || $[110] !== t14 || $[111] !== t28) {
872
+ t29 = /* @__PURE__ */ jsx(T0, {
873
+ "aria-label": t13,
874
+ role: t14,
875
+ children: t28
876
+ }, t12);
877
+ $[107] = T0;
878
+ $[108] = t12;
879
+ $[109] = t13;
880
+ $[110] = t14;
881
+ $[111] = t28;
882
+ $[112] = t29;
883
+ } else t29 = $[112];
884
+ let t30;
885
+ if ($[113] !== T1 || $[114] !== t15 || $[115] !== t16 || $[116] !== t17 || $[117] !== t18 || $[118] !== t19 || $[119] !== t20 || $[120] !== t29) {
886
+ t30 = /* @__PURE__ */ jsx(T1, {
887
+ triggerRef: t15,
888
+ placement: t16,
889
+ offset: t17,
890
+ className: t18,
891
+ isOpen: t19,
892
+ onOpenChange: t20,
893
+ children: t29
894
+ });
895
+ $[113] = T1;
896
+ $[114] = t15;
897
+ $[115] = t16;
898
+ $[116] = t17;
899
+ $[117] = t18;
900
+ $[118] = t19;
901
+ $[119] = t20;
902
+ $[120] = t29;
903
+ $[121] = t30;
904
+ } else t30 = $[121];
905
+ let t31;
906
+ if ($[122] !== t21 || $[123] !== t30) {
907
+ t31 = /* @__PURE__ */ jsxs(Fragment$1, { children: [t21, t30] });
908
+ $[122] = t21;
909
+ $[123] = t30;
910
+ $[124] = t31;
911
+ } else t31 = $[124];
912
+ return t31;
913
+ }
914
+ function _temp2(item_3) {
915
+ return item_3.id;
916
+ }
917
+ function _temp(item_2) {
918
+ return {
919
+ id: item_2.id,
920
+ label: item_2.name
921
+ };
922
+ }
923
+ //#endregion
924
+ //#region src/app-canvas/storage-protocol.ts
925
+ const canvasStorageMetaName = "povio-ui-canvas-storage";
926
+ //#endregion
927
+ //#region src/app-canvas/useCanvasStorage.ts
928
+ /** Canvas workspaces in `povio.canvas.json` when connected, otherwise in this browser. */
929
+ function useCanvasStorage(key, t0) {
930
+ const $ = c(11);
931
+ const enabled = t0 === void 0 ? true : t0;
932
+ let t1;
933
+ if ($[0] !== enabled || $[1] !== key) {
934
+ t1 = {
935
+ key,
936
+ metaName: canvasStorageMetaName,
937
+ field: "manifest",
938
+ parse: parseCanvasManifest,
939
+ noun: "workspace",
940
+ enabled
941
+ };
942
+ $[0] = enabled;
943
+ $[1] = key;
944
+ $[2] = t1;
945
+ } else t1 = $[2];
946
+ const t2 = useRepositoryStorage(t1);
947
+ let setValue;
948
+ let storage;
949
+ let value;
950
+ if ($[3] !== t2) {
951
+ ({value, setValue, ...storage} = t2);
952
+ $[3] = t2;
953
+ $[4] = setValue;
954
+ $[5] = storage;
955
+ $[6] = value;
956
+ } else {
957
+ setValue = $[4];
958
+ storage = $[5];
959
+ value = $[6];
960
+ }
961
+ let t3;
962
+ if ($[7] !== setValue || $[8] !== storage || $[9] !== value) {
963
+ t3 = {
964
+ manifest: value,
965
+ setManifest: setValue,
966
+ ...storage
967
+ };
968
+ $[7] = setValue;
969
+ $[8] = storage;
970
+ $[9] = value;
971
+ $[10] = t3;
972
+ } else t3 = $[10];
973
+ return t3;
974
+ }
975
+ //#endregion
333
976
  //#region src/app-canvas/useFrameStartup.ts
334
977
  /** Bound concurrent bootstraps, without unloading any admitted page. */
335
978
  function useFrameStartup(keys, concurrency = 2) {
@@ -408,7 +1051,7 @@ function labelOf(element) {
408
1051
  return (element.getAttribute("aria-label") || element.getAttribute("alt") || (!element.children.length ? element.textContent?.trim().replace(/\s+/g, " ") : ""))?.slice(0, 48) || (element.id ? `#${element.id}` : "");
409
1052
  }
410
1053
  /** Only expanded branches are read and rendered; large pages do not create a second full DOM tree. */
411
- function PageLayers({ frame, title, selected, onSelect }) {
1054
+ function PageLayers({ frame, title, selected, onSelect, onHover }) {
412
1055
  const [root, setRoot] = useState(null);
413
1056
  const [, refresh] = useReducer((value) => value + 1, 0);
414
1057
  const expanded = useRef(/* @__PURE__ */ new WeakSet());
@@ -490,7 +1133,13 @@ function PageLayers({ frame, title, selected, onSelect }) {
490
1133
  "aria-expanded": children.length ? open_0 : void 0,
491
1134
  children: [/* @__PURE__ */ jsxs("div", {
492
1135
  className: "povio-canvas-layer-row",
493
- style: { paddingLeft: 8 + depth * 12 },
1136
+ onMouseEnter: () => onHover(element_1),
1137
+ onMouseLeave: () => onHover(null),
1138
+ onFocus: () => onHover(element_1),
1139
+ onBlur: (event) => {
1140
+ if (!event.currentTarget.contains(event.relatedTarget)) onHover(null);
1141
+ },
1142
+ style: { paddingLeft: 4 + depth * 10 },
494
1143
  children: [children.length ? /* @__PURE__ */ jsx("button", {
495
1144
  className: "povio-canvas-layer-toggle",
496
1145
  "aria-label": `${open_0 ? "Collapse" : "Expand"} ${element_1.localName}${label ? ` ${label}` : ""}`,
@@ -500,13 +1149,13 @@ function PageLayers({ frame, title, selected, onSelect }) {
500
1149
  className: "povio-canvas-layer-select",
501
1150
  title: `${element_1.localName}${label ? ` · ${label}` : ""}`,
502
1151
  onClick: () => onSelect(element_1),
503
- onKeyDown: (event) => {
504
- if (event.key === "ArrowRight" && children.length && !open_0) {
505
- event.preventDefault();
1152
+ onKeyDown: (event_0) => {
1153
+ if (event_0.key === "ArrowRight" && children.length && !open_0) {
1154
+ event_0.preventDefault();
506
1155
  toggle(element_1, false);
507
1156
  }
508
- if (event.key === "ArrowLeft" && open_0) {
509
- event.preventDefault();
1157
+ if (event_0.key === "ArrowLeft" && open_0) {
1158
+ event_0.preventDefault();
510
1159
  toggle(element_1, true);
511
1160
  }
512
1161
  },
@@ -523,11 +1172,6 @@ function PageLayers({ frame, title, selected, onSelect }) {
523
1172
  className: "povio-canvas-layers",
524
1173
  "aria-label": `Layers for ${title}`,
525
1174
  children: [
526
- /* @__PURE__ */ jsxs("header", { children: [
527
- /* @__PURE__ */ jsx(CodeXml, { size: 14 }),
528
- /* @__PURE__ */ jsx("strong", { children: "Layers" }),
529
- /* @__PURE__ */ jsx("span", { children: title })
530
- ] }),
531
1175
  /* @__PURE__ */ jsx("div", {
532
1176
  role: "tree",
533
1177
  "aria-label": "HTML layers",
@@ -894,6 +1538,64 @@ function CanvasInspectionOverlay(t0) {
894
1538
  } else t3 = $[8];
895
1539
  return t3;
896
1540
  }
1541
+ /** Temporary layer hover never replaces the pinned inspector selection. */
1542
+ function CanvasLayerHighlight(t0) {
1543
+ const $ = c(5);
1544
+ const { element } = t0;
1545
+ const [bounds, setBounds] = useState(null);
1546
+ let t1;
1547
+ let t2;
1548
+ if ($[0] !== element) {
1549
+ t1 = () => {
1550
+ const owner = element.ownerDocument.defaultView;
1551
+ const measure = () => {
1552
+ if (!element.isConnected) {
1553
+ setBounds(null);
1554
+ return;
1555
+ }
1556
+ const rect = element.getBoundingClientRect();
1557
+ setBounds({
1558
+ left: rect.left,
1559
+ top: rect.top,
1560
+ width: rect.width,
1561
+ height: rect.height
1562
+ });
1563
+ };
1564
+ measure();
1565
+ const observer = new ResizeObserver(measure);
1566
+ observer.observe(element);
1567
+ owner?.addEventListener("scroll", measure, true);
1568
+ owner?.addEventListener("resize", measure);
1569
+ return () => {
1570
+ observer.disconnect();
1571
+ owner?.removeEventListener("scroll", measure, true);
1572
+ owner?.removeEventListener("resize", measure);
1573
+ };
1574
+ };
1575
+ t2 = [element];
1576
+ $[0] = element;
1577
+ $[1] = t1;
1578
+ $[2] = t2;
1579
+ } else {
1580
+ t1 = $[1];
1581
+ t2 = $[2];
1582
+ }
1583
+ useLayoutEffect(t1, t2);
1584
+ let t3;
1585
+ if ($[3] !== bounds) {
1586
+ t3 = bounds ? /* @__PURE__ */ jsx("div", {
1587
+ className: "povio-canvas-inspection-overlay povio-canvas-layer-highlight",
1588
+ "aria-hidden": "true",
1589
+ children: /* @__PURE__ */ jsx("div", {
1590
+ className: "studio-hover-outline",
1591
+ style: bounds
1592
+ })
1593
+ }) : null;
1594
+ $[3] = bounds;
1595
+ $[4] = t3;
1596
+ } else t3 = $[4];
1597
+ return t3;
1598
+ }
897
1599
  //#endregion
898
1600
  //#region src/app-canvas/route-model.ts
899
1601
  function joinPath(parent, path) {
@@ -1072,68 +1774,6 @@ function validConfiguration(value) {
1072
1774
  }) && !!config.config && typeof config.config === "object";
1073
1775
  }
1074
1776
  //#endregion
1075
- //#region src/app-canvas/manifest.ts
1076
- function createCanvasManifest() {
1077
- return {
1078
- version: 1,
1079
- positions: {},
1080
- examples: {},
1081
- manualEdges: [],
1082
- hiddenEdges: [],
1083
- breakpoint: "desktop"
1084
- };
1085
- }
1086
- function isRecord(value) {
1087
- return value !== null && typeof value === "object" && !Array.isArray(value);
1088
- }
1089
- function isJSON(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
1090
- if (value === null || typeof value === "string" || typeof value === "boolean") return true;
1091
- if (typeof value === "number") return Number.isFinite(value);
1092
- if (typeof value !== "object" || depth > 30 || ancestors.has(value)) return false;
1093
- ancestors.add(value);
1094
- const valid = Object.values(value).every((child) => isJSON(child, ancestors, depth + 1));
1095
- ancestors.delete(value);
1096
- return valid;
1097
- }
1098
- /** Sanitize either stored JSON text or an imported value, retaining valid v1 entries. */
1099
- function parseCanvasManifest(input) {
1100
- const fallback = createCanvasManifest();
1101
- let value = input;
1102
- if (typeof value === "string") try {
1103
- value = JSON.parse(value);
1104
- } catch {
1105
- return fallback;
1106
- }
1107
- if (!isRecord(value) || value.version !== 1) return fallback;
1108
- const positions = Object.fromEntries(Object.entries(isRecord(value.positions) ? value.positions : {}).flatMap(([breakpoint, entries]) => {
1109
- if (!isRecord(entries)) return [];
1110
- return [[breakpoint, Object.fromEntries(Object.entries(entries).flatMap(([id, point]) => isRecord(point) && typeof point.x === "number" && Number.isFinite(point.x) && typeof point.y === "number" && Number.isFinite(point.y) ? [[id, {
1111
- x: point.x,
1112
- y: point.y
1113
- }]] : []))]];
1114
- }));
1115
- const examples = Object.fromEntries(Object.entries(isRecord(value.examples) ? value.examples : {}).flatMap(([id, example]) => {
1116
- if (!isRecord(example)) return [];
1117
- const clean = {};
1118
- if (typeof example.title === "string") clean.title = example.title;
1119
- if (isRecord(example.params)) clean.params = Object.fromEntries(Object.entries(example.params).filter((entry) => typeof entry[1] === "string"));
1120
- if (isRecord(example.search)) clean.search = Object.fromEntries(Object.entries(example.search).filter(([, item]) => isJSON(item)));
1121
- return [[id, clean]];
1122
- }));
1123
- const manualEdges = (Array.isArray(value.manualEdges) ? value.manualEdges : []).flatMap((edge) => isRecord(edge) && typeof edge.from === "string" && edge.from.length > 0 && typeof edge.to === "string" && edge.to.length > 0 ? [{
1124
- from: edge.from,
1125
- to: edge.to
1126
- }] : []);
1127
- return {
1128
- version: 1,
1129
- positions,
1130
- examples,
1131
- manualEdges: [...new Map(manualEdges.map((edge) => [JSON.stringify(edge), edge])).values()],
1132
- hiddenEdges: [...new Set((Array.isArray(value.hiddenEdges) ? value.hiddenEdges : []).filter((edge) => typeof edge === "string"))],
1133
- breakpoint: typeof value.breakpoint === "string" && value.breakpoint.length > 0 ? value.breakpoint : fallback.breakpoint
1134
- };
1135
- }
1136
- //#endregion
1137
1777
  //#region src/app-canvas/AppCanvas.tsx
1138
1778
  const defaultCanvasBreakpoints = [
1139
1779
  {
@@ -1160,14 +1800,6 @@ const emptyExclude = [];
1160
1800
  /** Zoom per wheel pixel, and the largest delta one wheel event may contribute. */
1161
1801
  const ZOOM_SENSITIVITY = .006;
1162
1802
  const ZOOM_DELTA_LIMIT = 40;
1163
- function readManifest(key) {
1164
- if (typeof window === "undefined") return createCanvasManifest();
1165
- try {
1166
- return parseCanvasManifest(localStorage.getItem(key));
1167
- } catch {
1168
- return createCanvasManifest();
1169
- }
1170
- }
1171
1803
  const PageFrame = memo(function PageFrame(t0) {
1172
1804
  const $ = c(16);
1173
1805
  const { id, src, width, height, name, interact, inspect, onLoad, register } = t0;
@@ -1216,10 +1848,13 @@ const PageFrame = memo(function PageFrame(t0) {
1216
1848
  return t6;
1217
1849
  });
1218
1850
  /** Live route artboards. The camera transforms only the canvas, never the iframe viewport width. */
1219
- function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, excludePaths = emptyExclude, breakpoints = defaultCanvasBreakpoints, storageKey = "povio-app-canvas", frameUrl, componentsHref }) {
1851
+ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, excludePaths = emptyExclude, breakpoints = defaultCanvasBreakpoints, storageKey = "povio-app-canvas", frameUrl, componentsHref, repositoryStorage = true }) {
1220
1852
  const configuration = useUIConfiguration();
1221
- const [manifest, setManifest] = useState(() => readManifest(storageKey));
1853
+ const storage = useCanvasStorage(storageKey, repositoryStorage);
1854
+ const { manifest, setManifest } = storage;
1855
+ const workspace = manifest.workspaces.find((item) => item.id === manifest.activeWorkspaceId);
1222
1856
  const [runtime, setRuntime] = useState({});
1857
+ const [hoveredLayer, setHoveredLayer] = useState(null);
1223
1858
  const [selected, setSelected] = useState(null);
1224
1859
  const [interact, setInteract] = useState(false);
1225
1860
  const [inspectMode, setInspectMode] = useState(false);
@@ -1231,6 +1866,14 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1231
1866
  const [notice, setNotice] = useState("");
1232
1867
  const [documentGeneration, setDocumentGeneration] = useState(0);
1233
1868
  const [reload, setReload] = useState({});
1869
+ const [showLayers, setShowLayers] = useState(true);
1870
+ useEffect(() => setHoveredLayer(null), [
1871
+ selected,
1872
+ showLayers,
1873
+ manifest.activeWorkspaceId,
1874
+ manifest.breakpoint,
1875
+ documentGeneration
1876
+ ]);
1234
1877
  const zoomOutput = useRef(null);
1235
1878
  const importInput = useRef(null);
1236
1879
  const viewport = useRef(null), world = useRef(null);
@@ -1243,12 +1886,12 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1243
1886
  }), pendingCamera = useRef(null);
1244
1887
  const pointer = useRef(null);
1245
1888
  const cameraRevision = useRef(0);
1246
- const bp = breakpoints.find((item) => item.id === manifest.breakpoint) ?? breakpoints[breakpoints.length - 1] ?? defaultCanvasBreakpoints[2];
1889
+ const bp = breakpoints.find((item_0) => item_0.id === manifest.breakpoint) ?? breakpoints[breakpoints.length - 1] ?? defaultCanvasBreakpoints[2];
1247
1890
  const isKnownToken = useCallback((name) => isEditableToken(name, configuration.defaults), [configuration.defaults]);
1248
1891
  const { hostComponents } = configuration;
1249
1892
  useEffect(() => hostComponents(), [hostComponents]);
1250
- const inspector = useCanvasInspector(inspectMode, `${content}:${bp.id}:${documentGeneration}:${JSON.stringify(reload)}`, isKnownToken);
1251
- const pageCatalog = useMemo(() => discoverCanvasPages(router, {
1893
+ const inspector = useCanvasInspector(inspectMode, `${manifest.activeWorkspaceId}:${content}:${bp.id}:${documentGeneration}:${JSON.stringify(reload)}`, isKnownToken);
1894
+ const allPages = useMemo(() => discoverCanvasPages(router, {
1252
1895
  examples: {
1253
1896
  ...examples,
1254
1897
  ...manifest.examples
@@ -1262,6 +1905,11 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1262
1905
  exclude,
1263
1906
  excludePaths
1264
1907
  ]);
1908
+ const pageCatalog = useMemo(() => storage.ready ? allPages.filter((page) => workspace.enabledPages === null || workspace.enabledPages.includes(page.id)) : [], [
1909
+ allPages,
1910
+ workspace.enabledPages,
1911
+ storage.ready
1912
+ ]);
1265
1913
  const componentCatalog = useMemo(() => componentsHref ? componentArtboards.map((group) => ({
1266
1914
  id: `components:${group.toLowerCase()}`,
1267
1915
  title: group,
@@ -1271,7 +1919,7 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1271
1919
  kind: "page"
1272
1920
  })) : [], [componentsHref]);
1273
1921
  const catalog = content === "components" && componentCatalog.length ? componentCatalog : pageCatalog;
1274
- const startupKeys = useMemo(() => new Map(catalog.filter((page) => page.href).map((page_0) => [page_0.id, `${documentGeneration}:${bp.id}:${page_0.id}:${page_0.href}:${reload[page_0.id] ?? 0}`])), [
1922
+ const startupKeys = useMemo(() => new Map(catalog.filter((page_0) => page_0.href).map((page_1) => [page_1.id, `${documentGeneration}:${bp.id}:${page_1.id}:${page_1.href}:${reload[page_1.id] ?? 0}`])), [
1275
1923
  catalog,
1276
1924
  documentGeneration,
1277
1925
  bp.id,
@@ -1286,18 +1934,37 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1286
1934
  keys: startupKeys,
1287
1935
  complete: startup.complete
1288
1936
  };
1289
- const heights = useMemo(() => Object.fromEntries(catalog.map((page_1) => [page_1.id, runtime[`${bp.id}:${page_1.id}:${page_1.href ?? ""}`]?.height ?? bp.height])), [
1937
+ const heights = useMemo(() => Object.fromEntries(catalog.map((page_2) => [page_2.id, runtime[`${bp.id}:${page_2.id}:${page_2.href ?? ""}`]?.height ?? bp.height])), [
1290
1938
  catalog,
1291
1939
  runtime,
1292
1940
  bp
1293
1941
  ]);
1294
- const nodes = useMemo(() => layoutCanvasPages(catalog, bp.width, heights, manifest.positions[bp.id] ?? {}), [
1942
+ const nodes = useMemo(() => layoutCanvasPages(catalog, bp.width, heights, workspace.positions[bp.id] ?? {}), [
1295
1943
  catalog,
1296
1944
  bp,
1297
1945
  heights,
1298
- manifest.positions
1946
+ workspace.positions
1299
1947
  ]);
1300
1948
  const nodeMap = useMemo(() => new Map(nodes.map((node) => [node.id, node])), [nodes]);
1949
+ useLayoutEffect(() => {
1950
+ const gesture = pointer.current;
1951
+ if (gesture && viewport.current?.hasPointerCapture(gesture.id)) viewport.current.releasePointerCapture(gesture.id);
1952
+ pointer.current = null;
1953
+ setSelected(null);
1954
+ setQuery("");
1955
+ camera.current = {
1956
+ x: 40,
1957
+ y: 40,
1958
+ zoom: .25
1959
+ };
1960
+ }, [manifest.activeWorkspaceId]);
1961
+ useEffect(() => {
1962
+ if (selected && !nodeMap.has(selected)) setSelected(null);
1963
+ }, [selected, nodeMap]);
1964
+ useEffect(() => {
1965
+ const valid = new Set(catalog.map((page_3) => `${bp.id}:${page_3.id}:${page_3.href ?? ""}`));
1966
+ setRuntime((current) => Object.keys(current).some((key_0) => !valid.has(key_0)) ? Object.fromEntries(Object.entries(current).filter(([key]) => valid.has(key))) : current);
1967
+ }, [catalog, bp.id]);
1301
1968
  const latest = useRef({
1302
1969
  configuration: configuration.active,
1303
1970
  dark,
@@ -1328,6 +1995,9 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1328
1995
  const canvas = viewport.current?.closest(".povio-app-canvas");
1329
1996
  if (canvas) canvas.setAttribute("data-camera-revision", String(++cameraRevision.current));
1330
1997
  }, []);
1998
+ useLayoutEffect(() => {
1999
+ paint();
2000
+ }, [manifest.activeWorkspaceId, paint]);
1331
2001
  const schedulePaint = useCallback(() => {
1332
2002
  if (pendingCamera.current !== null) return;
1333
2003
  pendingCamera.current = requestAnimationFrame(() => {
@@ -1338,18 +2008,18 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1338
2008
  const send = useCallback((id_0, type = "configuration") => {
1339
2009
  const frame_0 = frames.current.get(id_0);
1340
2010
  if (!frame_0?.contentWindow) return;
1341
- const current = latest.current;
2011
+ const current_0 = latest.current;
1342
2012
  try {
1343
2013
  frame_0.contentWindow.postMessage({
1344
2014
  protocol: canvasProtocol,
1345
2015
  id: id_0,
1346
2016
  token,
1347
- viewportHeight: current.bp.height,
2017
+ viewportHeight: current_0.bp.height,
1348
2018
  type,
1349
- configuration: current.configuration,
2019
+ configuration: current_0.configuration,
1350
2020
  revision: revision.current,
1351
- dark: current.dark,
1352
- interact: current.interact
2021
+ dark: current_0.dark,
2022
+ interact: current_0.interact
1353
2023
  }, new URL(frame_0.src).origin);
1354
2024
  } catch {
1355
2025
  setNotice("This configuration could not be sent to the page. Check for non-serializable values.");
@@ -1359,7 +2029,8 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1359
2029
  const node_0 = nodeMap.get(id_1), view = viewport.current;
1360
2030
  if (!node_0 || !view) return;
1361
2031
  setSelected(id_1);
1362
- camera.current.zoom = Math.min(1, Math.max(.08, (view.clientWidth - 100) / node_0.width));
2032
+ setShowLayers(true);
2033
+ camera.current.zoom = Math.min(1, Math.max(.005, (view.clientWidth - 80) / node_0.width));
1363
2034
  camera.current.x = 40 - node_0.x * camera.current.zoom;
1364
2035
  camera.current.y = 40 - node_0.y * camera.current.zoom;
1365
2036
  paint();
@@ -1374,18 +2045,18 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1374
2045
  batch = 0;
1375
2046
  const changes = Array.from(queue);
1376
2047
  queue.clear();
1377
- setRuntime((current_0) => {
1378
- const next = { ...current_0 };
2048
+ setRuntime((current_1) => {
2049
+ const next = { ...current_1 };
1379
2050
  let changed = false;
1380
- for (const [key, item_0] of changes) {
1381
- if (frames.current.get(item_0.id) !== item_0.frame || item_0.frame.contentWindow !== item_0.source) continue;
1382
- next[key] = {
1383
- ...next[key],
1384
- ...item_0.change
2051
+ for (const [key_1, item_1] of changes) {
2052
+ if (frames.current.get(item_1.id) !== item_1.frame || item_1.frame.contentWindow !== item_1.source) continue;
2053
+ next[key_1] = {
2054
+ ...next[key_1],
2055
+ ...item_1.change
1385
2056
  };
1386
2057
  changed = true;
1387
2058
  }
1388
- return changed ? next : current_0;
2059
+ return changed ? next : current_1;
1389
2060
  });
1390
2061
  };
1391
2062
  const receive = (event) => {
@@ -1393,7 +2064,7 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1393
2064
  if (packet?.protocol !== "povio-app-canvas-v1" || packet.token !== token || typeof packet.id !== "string") return;
1394
2065
  const frame_1 = frames.current.get(packet.id);
1395
2066
  if (!frame_1 || event.source !== frame_1.contentWindow || event.origin !== new URL(frame_1.src).origin) return;
1396
- const key_0 = `${latest.current.bp.id}:${packet.id}:${latest.current.catalog.find((page_2) => page_2.id === packet.id)?.href ?? ""}`;
2067
+ const key_2 = `${latest.current.bp.id}:${packet.id}:${latest.current.catalog.find((page_4) => page_4.id === packet.id)?.href ?? ""}`;
1397
2068
  let change = {};
1398
2069
  if (packet.type === "ready") {
1399
2070
  startupRef.current.complete(startupRef.current.keys.get(packet.id));
@@ -1415,9 +2086,9 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1415
2086
  return;
1416
2087
  }
1417
2088
  if (!Object.keys(change).length) return;
1418
- const pending = queue.get(key_0);
2089
+ const pending = queue.get(key_2);
1419
2090
  const previous = pending?.frame === frame_1 && pending.source === event.source ? pending.change : {};
1420
- queue.set(key_0, {
2091
+ queue.set(key_2, {
1421
2092
  id: packet.id,
1422
2093
  frame: frame_1,
1423
2094
  source: event.source,
@@ -1452,19 +2123,13 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1452
2123
  useEffect(() => {
1453
2124
  for (const id_3 of frames.current.keys()) send(id_3, "mode");
1454
2125
  }, [interact, send]);
1455
- useEffect(() => {
1456
- try {
1457
- localStorage.setItem(storageKey, JSON.stringify(manifest));
1458
- } catch {
1459
- setNotice("Browser storage is unavailable; use Export layout to save this canvas.");
1460
- }
1461
- }, [manifest, storageKey]);
1462
2126
  useEffect(() => {
1463
2127
  paint();
1464
2128
  const element = viewport.current;
1465
2129
  if (!element) return;
1466
2130
  const wheel = (event_0) => {
1467
2131
  event_0.preventDefault();
2132
+ if (pointer.current) return;
1468
2133
  if (event_0.ctrlKey || event_0.metaKey) {
1469
2134
  const box = element.getBoundingClientRect();
1470
2135
  const x = event_0.clientX - box.left, y = event_0.clientY - box.top;
@@ -1534,58 +2199,74 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1534
2199
  1.5,
1535
2200
  2
1536
2201
  ];
1537
- const current_1 = camera.current.zoom;
1538
- const next_3 = direction === 1 ? stops.find((value_0) => value_0 > current_1 + 1e-4) : [...stops].reverse().find((value_1) => value_1 < current_1 - 1e-4);
2202
+ const current_2 = camera.current.zoom;
2203
+ const next_3 = direction === 1 ? stops.find((value_0) => value_0 > current_2 + 1e-4) : [...stops].reverse().find((value_1) => value_1 < current_2 - 1e-4);
1539
2204
  if (next_3 !== void 0) zoomTo(next_3);
1540
2205
  };
1541
2206
  const startPan = (event_1) => {
1542
2207
  if (inspectMode && event_1.target.closest(".povio-canvas-frame-cover")) return;
1543
2208
  if (event_1.button !== 0 || event_1.target.closest("button:not(.povio-canvas-frame-cover),input,select,.povio-canvas-node-header")) return;
2209
+ event_1.preventDefault();
1544
2210
  event_1.currentTarget.setPointerCapture(event_1.pointerId);
1545
2211
  pointer.current = {
1546
2212
  id: event_1.pointerId,
1547
2213
  x: event_1.clientX,
1548
2214
  y: event_1.clientY,
2215
+ lastX: event_1.clientX,
2216
+ lastY: event_1.clientY,
1549
2217
  startX: camera.current.x,
1550
2218
  startY: camera.current.y
1551
2219
  };
1552
2220
  };
1553
2221
  const move = (event_2) => {
1554
- const current_2 = pointer.current;
1555
- if (!current_2 || current_2.id !== event_2.pointerId) return;
1556
- const dx = event_2.clientX - current_2.x, dy = event_2.clientY - current_2.y;
1557
- if (current_2.node) {
1558
- const element_0 = world.current?.querySelector(`[data-node-index="${nodes.findIndex((n_3) => n_3.id === current_2.node)}"]`);
2222
+ const current_3 = pointer.current;
2223
+ if (!current_3 || current_3.id !== event_2.pointerId) return;
2224
+ if (event_2.buttons === 0) {
2225
+ end(event_2, true);
2226
+ return;
2227
+ }
2228
+ current_3.lastX = event_2.clientX;
2229
+ current_3.lastY = event_2.clientY;
2230
+ const dx = event_2.clientX - current_3.x, dy = event_2.clientY - current_3.y;
2231
+ if (current_3.node) {
2232
+ const element_0 = world.current?.querySelector(`[data-node-index="${nodes.findIndex((n_3) => n_3.id === current_3.node)}"]`);
1559
2233
  if (element_0) {
1560
- element_0.style.left = `${current_2.nodeX + dx / camera.current.zoom}px`;
1561
- element_0.style.top = `${current_2.nodeY + dy / camera.current.zoom}px`;
2234
+ element_0.style.left = `${current_3.nodeX + dx / camera.current.zoom}px`;
2235
+ element_0.style.top = `${current_3.nodeY + dy / camera.current.zoom}px`;
1562
2236
  }
1563
2237
  } else {
1564
- camera.current.x = current_2.startX + dx;
1565
- camera.current.y = current_2.startY + dy;
2238
+ camera.current.x = current_3.startX + dx;
2239
+ camera.current.y = current_3.startY + dy;
1566
2240
  schedulePaint();
1567
2241
  }
1568
2242
  };
1569
- const end = (event_3) => {
1570
- const current_3 = pointer.current;
1571
- if (!current_3 || current_3.id !== event_3.pointerId) return;
1572
- if (current_3.node) {
2243
+ const end = (event_3, useLastPosition = false) => {
2244
+ const current_4 = pointer.current;
2245
+ if (!current_4 || current_4.id !== event_3.pointerId) return;
2246
+ pointer.current = null;
2247
+ if (current_4.node) {
1573
2248
  const position = {
1574
- x: current_3.nodeX + (event_3.clientX - current_3.x) / camera.current.zoom,
1575
- y: current_3.nodeY + (event_3.clientY - current_3.y) / camera.current.zoom
2249
+ x: current_4.nodeX + ((useLastPosition ? current_4.lastX : event_3.clientX) - current_4.x) / camera.current.zoom,
2250
+ y: current_4.nodeY + ((useLastPosition ? current_4.lastY : event_3.clientY) - current_4.y) / camera.current.zoom
1576
2251
  };
1577
2252
  setManifest((m) => ({
1578
2253
  ...m,
1579
- positions: {
1580
- ...m.positions,
1581
- [bp.id]: {
1582
- ...m.positions[bp.id],
1583
- [current_3.node]: position
2254
+ workspaces: m.workspaces.map((item_2) => item_2.id === m.activeWorkspaceId ? {
2255
+ ...item_2,
2256
+ positions: {
2257
+ ...item_2.positions,
2258
+ [bp.id]: {
2259
+ ...item_2.positions[bp.id],
2260
+ ...Object.fromEntries(nodes.map((node_1) => [node_1.id, {
2261
+ x: node_1.x,
2262
+ y: node_1.y
2263
+ }])),
2264
+ [current_4.node]: position
2265
+ }
1584
2266
  }
1585
- }
2267
+ } : item_2)
1586
2268
  }));
1587
2269
  }
1588
- pointer.current = null;
1589
2270
  if (event_3.currentTarget.hasPointerCapture(event_3.pointerId)) event_3.currentTarget.releasePointerCapture(event_3.pointerId);
1590
2271
  };
1591
2272
  const onLoad = useCallback((id_4, frame_3) => {
@@ -1595,12 +2276,12 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1595
2276
  send(id_4, "mode");
1596
2277
  }, [send]);
1597
2278
  useEffect(() => {
1598
- const timer = setTimeout(() => setRuntime((current_4) => {
1599
- const next_4 = { ...current_4 };
1600
- for (const page_3 of latest.current.catalog) {
1601
- const key_1 = `${latest.current.bp.id}:${page_3.id}:${page_3.href ?? ""}`;
1602
- if (page_3.href && frames.current.has(page_3.id) && !next_4[key_1]?.ready) next_4[key_1] = {
1603
- ...next_4[key_1],
2279
+ const timer = setTimeout(() => setRuntime((current_5) => {
2280
+ const next_4 = { ...current_5 };
2281
+ for (const page_5 of latest.current.catalog) {
2282
+ const key_3 = `${latest.current.bp.id}:${page_5.id}:${page_5.href ?? ""}`;
2283
+ if (page_5.href && frames.current.has(page_5.id) && !next_4[key_3]?.ready) next_4[key_3] = {
2284
+ ...next_4[key_3],
1604
2285
  timedOut: true
1605
2286
  };
1606
2287
  }
@@ -1644,13 +2325,13 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1644
2325
  if (!file) return;
1645
2326
  try {
1646
2327
  const raw = JSON.parse(await file.text());
1647
- if (raw?.version !== 1) throw new Error("version");
2328
+ if (raw?.version !== 1 && raw?.version !== 2) throw new Error("version");
1648
2329
  setManifest(parseCanvasManifest(raw));
1649
2330
  setRuntime({});
1650
2331
  setDocumentGeneration((value_2) => value_2 + 1);
1651
2332
  setNotice("Layout imported.");
1652
2333
  } catch {
1653
- setNotice("Choose a valid version 1 canvas layout JSON file.");
2334
+ setNotice("Choose a valid canvas workspace JSON file.");
1654
2335
  }
1655
2336
  }
1656
2337
  }),
@@ -1681,6 +2362,12 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1681
2362
  })]
1682
2363
  })]
1683
2364
  }),
2365
+ /* @__PURE__ */ jsx(WorkspaceControls, {
2366
+ manifest,
2367
+ setManifest,
2368
+ pages: allPages,
2369
+ disabled: !storage.ready
2370
+ }),
1684
2371
  /* @__PURE__ */ jsxs("div", {
1685
2372
  className: "povio-canvas-search",
1686
2373
  children: [
@@ -1699,37 +2386,49 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1699
2386
  })
1700
2387
  ]
1701
2388
  }),
1702
- /* @__PURE__ */ jsxs("div", {
2389
+ /* @__PURE__ */ jsx("div", {
1703
2390
  className: "povio-canvas-page-list",
1704
- children: [nodes.filter((node_1) => `${node_1.title} ${node_1.path}`.toLowerCase().includes(query.toLowerCase())).map((node_2) => /* @__PURE__ */ jsxs("button", {
1705
- "aria-pressed": selected === node_2.id,
1706
- onClick: () => {
1707
- focusPage(node_2.id);
1708
- setMobilePanel(null);
1709
- },
1710
- children: [/* @__PURE__ */ jsx(Frame, { size: 15 }), /* @__PURE__ */ jsxs("span", {
1711
- className: "povio-canvas-page-label",
1712
- children: [/* @__PURE__ */ jsx("strong", { children: node_2.title }), node_2.kind === "unresolved" && /* @__PURE__ */ jsx("small", { children: "Needs parameters" })]
2391
+ children: nodes.filter((node_2) => `${node_2.title} ${node_2.path}`.toLowerCase().includes(query.toLowerCase())).map((node_3) => /* @__PURE__ */ jsxs("div", {
2392
+ className: "povio-canvas-page-entry",
2393
+ children: [/* @__PURE__ */ jsxs("button", {
2394
+ "aria-expanded": node_3.href ? selected === node_3.id && showLayers : void 0,
2395
+ "aria-pressed": selected === node_3.id,
2396
+ onClick: () => {
2397
+ if (selected === node_3.id && showLayers) setShowLayers(false);
2398
+ else focusPage(node_3.id);
2399
+ },
2400
+ children: [node_3.href ? selected === node_3.id && showLayers ? /* @__PURE__ */ jsx(ChevronDown, { size: 12 }) : /* @__PURE__ */ jsx(ChevronRight, { size: 12 }) : /* @__PURE__ */ jsx(Frame, { size: 15 }), /* @__PURE__ */ jsxs("span", {
2401
+ className: "povio-canvas-page-label",
2402
+ children: [/* @__PURE__ */ jsx("strong", { children: node_3.title }), node_3.kind === "unresolved" && /* @__PURE__ */ jsx("small", { children: "Needs parameters" })]
2403
+ })]
2404
+ }), showLayers && selected === node_3.id && node_3.href && /* @__PURE__ */ jsx("div", {
2405
+ className: "povio-canvas-inline-layers",
2406
+ children: /* @__PURE__ */ jsx(PageLayers, {
2407
+ frame: frames.current.get(selected) ?? null,
2408
+ title: nodeMap.get(selected).title,
2409
+ selected: inspector.selection?.pageId === selected ? inspector.selection.element : void 0,
2410
+ onHover: (element_1) => setHoveredLayer(element_1 ? {
2411
+ pageId: node_3.id,
2412
+ element: element_1
2413
+ } : null),
2414
+ onSelect: (element_2) => {
2415
+ setInteract(false);
2416
+ setInspectMode(true);
2417
+ inspector.selectElement(selected, element_2);
2418
+ setMobilePanel(null);
2419
+ requestAnimationFrame(() => {
2420
+ const node_4 = nodeMap.get(selected), view_1 = viewport.current;
2421
+ if (!node_4 || !view_1 || !element_2.isConnected) return;
2422
+ const rect = element_2.getBoundingClientRect();
2423
+ camera.current.zoom = Math.min(camera.current.zoom, Math.max(.005, (view_1.clientWidth - 80) / node_4.width));
2424
+ camera.current.x = 40 - node_4.x * camera.current.zoom;
2425
+ camera.current.y = view_1.clientHeight / 2 - (node_4.y + 48 + rect.top + rect.height / 2) * camera.current.zoom;
2426
+ paint();
2427
+ });
2428
+ }
2429
+ }, `${bp.id}:${documentGeneration}:${reload[selected] ?? 0}:${selected}:${nodeMap.get(selected)?.href}`)
1713
2430
  })]
1714
- }, node_2.id)), selected && nodeMap.get(selected)?.href && /* @__PURE__ */ jsx(PageLayers, {
1715
- frame: frames.current.get(selected) ?? null,
1716
- title: nodeMap.get(selected).title,
1717
- selected: inspector.selection?.pageId === selected ? inspector.selection.element : void 0,
1718
- onSelect: (element_1) => {
1719
- setInteract(false);
1720
- setInspectMode(true);
1721
- inspector.selectElement(selected, element_1);
1722
- setMobilePanel(null);
1723
- requestAnimationFrame(() => {
1724
- const node_3 = nodeMap.get(selected), view_1 = viewport.current;
1725
- if (!node_3 || !view_1 || !element_1.isConnected) return;
1726
- const rect = element_1.getBoundingClientRect();
1727
- camera.current.x = view_1.clientWidth / 2 - (node_3.x + rect.left + rect.width / 2) * camera.current.zoom;
1728
- camera.current.y = view_1.clientHeight / 2 - (node_3.y + 48 + rect.top + rect.height / 2) * camera.current.zoom;
1729
- paint();
1730
- });
1731
- }
1732
- }, `${bp.id}:${documentGeneration}:${reload[selected] ?? 0}:${selected}:${nodeMap.get(selected)?.href}`)]
2431
+ }, node_3.id))
1733
2432
  }),
1734
2433
  /* @__PURE__ */ jsxs("div", {
1735
2434
  className: "povio-canvas-sidebar-actions",
@@ -1762,6 +2461,18 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1762
2461
  ]
1763
2462
  })
1764
2463
  ]
2464
+ }),
2465
+ /* @__PURE__ */ jsxs("div", {
2466
+ className: "povio-canvas-storage-status",
2467
+ role: "status",
2468
+ children: [storage.status, storage.error && /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("p", { children: storage.error }), /* @__PURE__ */ jsx("button", {
2469
+ onClick: () => {
2470
+ setRuntime({});
2471
+ setDocumentGeneration((value_3) => value_3 + 1);
2472
+ storage.reload();
2473
+ },
2474
+ children: "Reload repository settings"
2475
+ })] })]
1765
2476
  })
1766
2477
  ]
1767
2478
  }),
@@ -1777,18 +2488,8 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1777
2488
  onPointerDown: startPan,
1778
2489
  onPointerMove: move,
1779
2490
  onPointerUp: end,
1780
- onPointerCancel: (event_7) => {
1781
- const current_5 = pointer.current;
1782
- if (current_5?.node) {
1783
- const element_2 = world.current?.querySelector(`[data-node-index="${nodes.findIndex((node_4) => node_4.id === current_5.node)}"]`);
1784
- if (element_2) {
1785
- element_2.style.left = `${current_5.nodeX}px`;
1786
- element_2.style.top = `${current_5.nodeY}px`;
1787
- }
1788
- }
1789
- pointer.current = null;
1790
- if (event_7.currentTarget.hasPointerCapture(event_7.pointerId)) event_7.currentTarget.releasePointerCapture(event_7.pointerId);
1791
- },
2491
+ onPointerCancel: (event_7) => end(event_7, true),
2492
+ onLostPointerCapture: (event_8) => end(event_8, true),
1792
2493
  children: [
1793
2494
  /* @__PURE__ */ jsx("div", {
1794
2495
  className: "povio-canvas-mobile-tools",
@@ -1834,14 +2535,18 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1834
2535
  children: [
1835
2536
  /* @__PURE__ */ jsxs("header", {
1836
2537
  className: "povio-canvas-node-header",
1837
- onPointerDown: (event_8) => {
1838
- if (event_8.button !== 0 || event_8.target.closest("button")) return;
1839
- event_8.stopPropagation();
1840
- viewport.current?.setPointerCapture(event_8.pointerId);
2538
+ onDragStart: (event_9) => event_9.preventDefault(),
2539
+ onPointerDown: (event_10) => {
2540
+ if (event_10.button !== 0 || event_10.target.closest("button")) return;
2541
+ event_10.preventDefault();
2542
+ event_10.stopPropagation();
2543
+ viewport.current?.setPointerCapture(event_10.pointerId);
1841
2544
  pointer.current = {
1842
- id: event_8.pointerId,
1843
- x: event_8.clientX,
1844
- y: event_8.clientY,
2545
+ id: event_10.pointerId,
2546
+ x: event_10.clientX,
2547
+ y: event_10.clientY,
2548
+ lastX: event_10.clientX,
2549
+ lastY: event_10.clientY,
1845
2550
  startX: 0,
1846
2551
  startY: 0,
1847
2552
  node: node_5.id,
@@ -1880,12 +2585,12 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1880
2585
  }),
1881
2586
  !node_5.href ? /* @__PURE__ */ jsxs("form", {
1882
2587
  className: "povio-canvas-unresolved",
1883
- onSubmit: (event_9) => {
1884
- event_9.preventDefault();
1885
- const form = new FormData(event_9.currentTarget);
1886
- const enteredParams = Object.fromEntries(node_5.missingParams.map((key_2) => {
1887
- const value_3 = form.get(key_2);
1888
- return [key_2, typeof value_3 === "string" ? value_3 : ""];
2588
+ onSubmit: (event_11) => {
2589
+ event_11.preventDefault();
2590
+ const form = new FormData(event_11.currentTarget);
2591
+ const enteredParams = Object.fromEntries(node_5.missingParams.map((key_4) => {
2592
+ const value_4 = form.get(key_4);
2593
+ return [key_4, typeof value_4 === "string" ? value_4 : ""];
1889
2594
  }));
1890
2595
  setManifest((m_0) => ({
1891
2596
  ...m_0,
@@ -1906,10 +2611,10 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1906
2611
  children: [
1907
2612
  /* @__PURE__ */ jsx("strong", { children: "Route needs parameters" }),
1908
2613
  /* @__PURE__ */ jsx("code", { children: node_5.path }),
1909
- node_5.missingParams.map((key_3) => /* @__PURE__ */ jsxs("label", { children: [key_3, /* @__PURE__ */ jsx("input", {
1910
- name: key_3,
2614
+ node_5.missingParams.map((key_5) => /* @__PURE__ */ jsxs("label", { children: [key_5, /* @__PURE__ */ jsx("input", {
2615
+ name: key_5,
1911
2616
  required: true
1912
- })] }, key_3)),
2617
+ })] }, key_5)),
1913
2618
  /* @__PURE__ */ jsx("button", { children: "Render page" })
1914
2619
  ]
1915
2620
  }) : originError ? /* @__PURE__ */ jsx("p", {
@@ -1940,25 +2645,26 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1940
2645
  className: "povio-canvas-frame-cover",
1941
2646
  tabIndex: 0,
1942
2647
  "aria-label": `Select ${node_5.title}`,
1943
- onKeyDown: (event_10) => {
1944
- if (event_10.key === "Enter" || event_10.key === " ") {
1945
- event_10.preventDefault();
2648
+ onKeyDown: (event_12) => {
2649
+ if (event_12.key === "Enter" || event_12.key === " ") {
2650
+ event_12.preventDefault();
1946
2651
  setSelected(node_5.id);
1947
2652
  }
1948
2653
  },
1949
- onPointerMove: (event_11) => {
2654
+ onPointerMove: (event_13) => {
1950
2655
  const frame_4 = frames.current.get(node_5.id);
1951
- if (frame_4) inspector.hover(node_5.id, frame_4, event_11.clientX, event_11.clientY);
2656
+ if (frame_4) inspector.hover(node_5.id, frame_4, event_13.clientX, event_13.clientY);
1952
2657
  },
1953
- onPointerDown: (event_12) => {
2658
+ onPointerDown: (event_14) => {
1954
2659
  setSelected(node_5.id);
1955
2660
  if (inspectMode) {
1956
2661
  const frame_5 = frames.current.get(node_5.id);
1957
- if (frame_5) inspector.select(node_5.id, frame_5, event_12.clientX, event_12.clientY);
2662
+ if (frame_5) inspector.select(node_5.id, frame_5, event_14.clientX, event_14.clientY);
1958
2663
  }
1959
2664
  },
1960
2665
  onClick: () => setSelected(node_5.id)
1961
2666
  }),
2667
+ hoveredLayer?.pageId === node_5.id && /* @__PURE__ */ jsx(CanvasLayerHighlight, { element: hoveredLayer.element }),
1962
2668
  inspectMode && inspector.selection?.pageId === node_5.id && inspector.measurement && /* @__PURE__ */ jsx(CanvasInspectionOverlay, {
1963
2669
  measurement: inspector.measurement,
1964
2670
  width: node_5.width
@@ -1976,6 +2682,10 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
1976
2682
  }, node_5.id);
1977
2683
  })
1978
2684
  }),
2685
+ !nodes.length && /* @__PURE__ */ jsx("div", {
2686
+ className: "povio-canvas-empty",
2687
+ children: storage.ready ? "Enable pages in this workspace to start arranging your canvas." : "Loading workspace…"
2688
+ }),
1979
2689
  /* @__PURE__ */ jsxs("div", {
1980
2690
  className: "povio-canvas-tool-dock",
1981
2691
  children: [
@@ -2021,10 +2731,13 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
2021
2731
  "aria-label": "Auto layout",
2022
2732
  onClick: () => setManifest((m_1) => ({
2023
2733
  ...m_1,
2024
- positions: {
2025
- ...m_1.positions,
2026
- [bp.id]: {}
2027
- }
2734
+ workspaces: m_1.workspaces.map((item_3) => item_3.id === m_1.activeWorkspaceId ? {
2735
+ ...item_3,
2736
+ positions: {
2737
+ ...item_3.positions,
2738
+ [bp.id]: {}
2739
+ }
2740
+ } : item_3)
2028
2741
  })),
2029
2742
  children: [/* @__PURE__ */ jsx(PanelsTopLeft, { size: 17 }), /* @__PURE__ */ jsx("span", { children: "Auto layout" })]
2030
2743
  })
@@ -2048,29 +2761,33 @@ function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, e
2048
2761
  title: "Toggle page theme",
2049
2762
  children: dark ? /* @__PURE__ */ jsx(Moon, { size: 16 }) : /* @__PURE__ */ jsx(Sun, { size: 16 })
2050
2763
  }),
2051
- /* @__PURE__ */ jsxs("div", {
2052
- className: "povio-canvas-breakpoint",
2053
- children: [/* @__PURE__ */ jsx(Monitor, { size: 15 }), /* @__PURE__ */ jsx("select", {
2054
- "aria-label": "Canvas breakpoint",
2764
+ /* @__PURE__ */ jsx("div", {
2765
+ className: "povio-canvas-breakpoint povio-canvas-workspace-select",
2766
+ children: /* @__PURE__ */ jsx(Select, {
2767
+ label: "Canvas breakpoint",
2768
+ hideLabel: true,
2769
+ size: "small",
2770
+ variant: "outlined",
2771
+ as: "default",
2772
+ className: "povio-canvas-workspace-input",
2773
+ selectionMode: "single",
2774
+ isClearable: false,
2775
+ renderStaticInput: false,
2776
+ leadingContent: /* @__PURE__ */ jsx(Monitor, { size: 15 }),
2055
2777
  value: bp.id,
2056
- onChange: (e) => {
2057
- if (e.target.value === bp.id) return;
2778
+ items: breakpoints.map((item_4) => ({
2779
+ id: item_4.id,
2780
+ label: `${item_4.label} · ${item_4.width}px`
2781
+ })),
2782
+ onChange: (id_5) => {
2783
+ if (id_5 === null || id_5 === bp.id) return;
2058
2784
  setRuntime({});
2059
2785
  setManifest((m_2) => ({
2060
2786
  ...m_2,
2061
- breakpoint: e.target.value
2787
+ breakpoint: String(id_5)
2062
2788
  }));
2063
- },
2064
- children: breakpoints.map((item_1) => /* @__PURE__ */ jsxs("option", {
2065
- value: item_1.id,
2066
- children: [
2067
- item_1.label,
2068
- " · ",
2069
- item_1.width,
2070
- "px"
2071
- ]
2072
- }, item_1.id))
2073
- })]
2789
+ }
2790
+ })
2074
2791
  }),
2075
2792
  /* @__PURE__ */ jsx("span", { className: "povio-canvas-dock-divider" }),
2076
2793
  /* @__PURE__ */ jsx("button", {
@@ -2312,6 +3029,19 @@ function startCanvasDocument(doc, options) {
2312
3029
  win.addEventListener("resize", schedule);
2313
3030
  doc.addEventListener("load", schedule, true);
2314
3031
  doc.fonts?.ready.then(schedule);
3032
+ const focusDescriptor = Object.getOwnPropertyDescriptor(win.HTMLElement.prototype, "focus");
3033
+ const elementFocus = focusDescriptor.value;
3034
+ const windowFocusDescriptor = Object.getOwnPropertyDescriptor(win, "focus");
3035
+ const windowFocus = windowFocusDescriptor?.value;
3036
+ Object.defineProperty(win.HTMLElement.prototype, "focus", {
3037
+ ...focusDescriptor,
3038
+ value(focusOptions) {
3039
+ if (doc.hasFocus()) elementFocus.call(this, focusOptions);
3040
+ }
3041
+ });
3042
+ win.focus = () => {
3043
+ if (doc.hasFocus()) windowFocus?.call(win);
3044
+ };
2315
3045
  refresh();
2316
3046
  return {
2317
3047
  refresh,
@@ -2327,6 +3057,9 @@ function startCanvasDocument(doc, options) {
2327
3057
  win.removeEventListener("submit", submit, true);
2328
3058
  win.removeEventListener("resize", schedule);
2329
3059
  doc.removeEventListener("load", schedule, true);
3060
+ Object.defineProperty(win.HTMLElement.prototype, "focus", focusDescriptor);
3061
+ if (windowFocusDescriptor) Object.defineProperty(win, "focus", windowFocusDescriptor);
3062
+ else Reflect.deleteProperty(win, "focus");
2330
3063
  for (const [media, value] of mediaOriginals) media.mediaText = value;
2331
3064
  for (const [style, values] of originals) for (const [property, [value, priority]] of values) if (value) style.setProperty(property, value, priority);
2332
3065
  else style.removeProperty(property);
@@ -2407,10 +3140,10 @@ function AppCanvasFrameBridge({ href }) {
2407
3140
  const APP_CANVAS_PATH = "/_canvas";
2408
3141
  /** Must match `componentsFrameParam`; kept inline so the canvas page does not load the gallery eagerly. */
2409
3142
  const componentsParam = "povio-components";
2410
- const CanvasComponentsFrame = lazy(() => import("./CanvasComponentsFrame-C0Sf9RcC.js"));
3143
+ const CanvasComponentsFrame = lazy(() => import("./CanvasComponentsFrame-CbgJJv-3.js"));
2411
3144
  /** Ready-made route page. Mount beneath the app's UIConfigurationProvider. */
2412
3145
  function AppCanvasPage(t0) {
2413
- const $ = c(21);
3146
+ const $ = c(24);
2414
3147
  let excludePaths;
2415
3148
  let props;
2416
3149
  let t1;
@@ -2426,72 +3159,88 @@ function AppCanvasPage(t0) {
2426
3159
  t1 = $[3];
2427
3160
  }
2428
3161
  const canvasPath = t1 === void 0 ? APP_CANVAS_PATH : t1;
3162
+ const updateTheme = ThemeContext.useTheme()?.updateTheme;
2429
3163
  let t2;
2430
- if ($[4] !== excludePaths) {
2431
- t2 = excludePaths ?? [];
2432
- $[4] = excludePaths;
2433
- $[5] = t2;
2434
- } else t2 = $[5];
2435
3164
  let t3;
2436
- if ($[6] !== canvasPath || $[7] !== t2) {
2437
- t3 = /* @__PURE__ */ new Set([...t2, canvasPath]);
2438
- $[6] = canvasPath;
2439
- $[7] = t2;
2440
- $[8] = t3;
2441
- } else t3 = $[8];
3165
+ if ($[4] !== updateTheme) {
3166
+ t2 = () => {
3167
+ if (!isAppCanvasFrame()) updateTheme?.("light");
3168
+ };
3169
+ t3 = [updateTheme];
3170
+ $[4] = updateTheme;
3171
+ $[5] = t2;
3172
+ $[6] = t3;
3173
+ } else {
3174
+ t2 = $[5];
3175
+ t3 = $[6];
3176
+ }
3177
+ useEffect(t2, t3);
2442
3178
  let t4;
2443
- if ($[9] !== t3) {
2444
- t4 = [...t3];
2445
- $[9] = t3;
3179
+ if ($[7] !== excludePaths) {
3180
+ t4 = excludePaths ?? [];
3181
+ $[7] = excludePaths;
3182
+ $[8] = t4;
3183
+ } else t4 = $[8];
3184
+ let t5;
3185
+ if ($[9] !== canvasPath || $[10] !== t4) {
3186
+ t5 = /* @__PURE__ */ new Set([...t4, canvasPath]);
3187
+ $[9] = canvasPath;
2446
3188
  $[10] = t4;
2447
- } else t4 = $[10];
2448
- const exclusions = t4;
3189
+ $[11] = t5;
3190
+ } else t5 = $[11];
3191
+ let t6;
3192
+ if ($[12] !== t5) {
3193
+ t6 = [...t5];
3194
+ $[12] = t5;
3195
+ $[13] = t6;
3196
+ } else t6 = $[13];
3197
+ const exclusions = t6;
2449
3198
  if (isAppCanvasFrame()) {
2450
- let t5;
2451
- if ($[11] === Symbol.for("react.memo_cache_sentinel")) {
3199
+ let t7;
3200
+ if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
2452
3201
  const group = new URLSearchParams(window.location.search).get(componentsParam);
2453
- t5 = group ? /* @__PURE__ */ jsx(Suspense, {
3202
+ t7 = group ? /* @__PURE__ */ jsx(Suspense, {
2454
3203
  fallback: null,
2455
3204
  children: /* @__PURE__ */ jsx(CanvasComponentsFrame, { group })
2456
3205
  }) : null;
2457
- $[11] = t5;
2458
- } else t5 = $[11];
2459
- return t5;
3206
+ $[14] = t7;
3207
+ } else t7 = $[14];
3208
+ return t7;
2460
3209
  }
2461
- let t5;
2462
- if ($[12] !== canvasPath) {
2463
- t5 = (group_0) => `${canvasPath}?${componentsParam}=${encodeURIComponent(group_0)}`;
2464
- $[12] = canvasPath;
2465
- $[13] = t5;
2466
- } else t5 = $[13];
2467
- let t6;
2468
- if ($[14] !== exclusions || $[15] !== props || $[16] !== t5) {
2469
- t6 = /* @__PURE__ */ jsx(AppCanvas, {
3210
+ let t7;
3211
+ if ($[15] !== canvasPath) {
3212
+ t7 = (group_0) => `${canvasPath}?${componentsParam}=${encodeURIComponent(group_0)}`;
3213
+ $[15] = canvasPath;
3214
+ $[16] = t7;
3215
+ } else t7 = $[16];
3216
+ let t8;
3217
+ if ($[17] !== exclusions || $[18] !== props || $[19] !== t7) {
3218
+ t8 = /* @__PURE__ */ jsx(AppCanvas, {
2470
3219
  ...props,
2471
3220
  excludePaths: exclusions,
2472
- componentsHref: t5
3221
+ componentsHref: t7
2473
3222
  });
2474
- $[14] = exclusions;
2475
- $[15] = props;
2476
- $[16] = t5;
2477
- $[17] = t6;
2478
- } else t6 = $[17];
2479
- let t7;
2480
- if ($[18] === Symbol.for("react.memo_cache_sentinel")) {
2481
- t7 = /* @__PURE__ */ jsx(UIConfigurationModal, {});
2482
- $[18] = t7;
2483
- } else t7 = $[18];
2484
- let t8;
2485
- if ($[19] !== t6) {
2486
- t8 = /* @__PURE__ */ jsxs("div", {
3223
+ $[17] = exclusions;
3224
+ $[18] = props;
3225
+ $[19] = t7;
3226
+ $[20] = t8;
3227
+ } else t8 = $[20];
3228
+ let t9;
3229
+ if ($[21] === Symbol.for("react.memo_cache_sentinel")) {
3230
+ t9 = /* @__PURE__ */ jsx(UIConfigurationModal, {});
3231
+ $[21] = t9;
3232
+ } else t9 = $[21];
3233
+ let t10;
3234
+ if ($[22] !== t8) {
3235
+ t10 = /* @__PURE__ */ jsxs("div", {
2487
3236
  className: "povio-app-canvas-page",
2488
3237
  "aria-label": "App canvas page",
2489
- children: [t6, t7]
3238
+ children: [t8, t9]
2490
3239
  });
2491
- $[19] = t6;
2492
- $[20] = t8;
2493
- } else t8 = $[20];
2494
- return t8;
3240
+ $[22] = t8;
3241
+ $[23] = t10;
3242
+ } else t10 = $[23];
3243
+ return t10;
2495
3244
  }
2496
3245
  //#endregion
2497
3246
  export { defaultCanvasBreakpoints as a, layoutCanvasPages as c, AppCanvas as i, AppCanvasPage as n, isAppCanvasFrame as o, AppCanvasFrameBridge as r, discoverCanvasPages as s, APP_CANVAS_PATH as t };