@povio/ui 5.0.0-rc.8 → 5.0.0-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/dist/Alert-CnvEb1D_.js +244 -0
- package/dist/AppCanvasPage--RG31Yyv.js +2421 -0
- package/dist/AppCanvasPage-BIFYeEeS.d.ts +80 -0
- package/dist/{button.cva-rv5Uq4aW.js → Button-C5rv4Cr2.js} +84 -1
- package/dist/{Calendar-BxtOuqIM.js → Calendar-BTjajO6i.js} +3 -3
- package/dist/{Checkbox-DYxXYfKL.js → Checkbox-CV1ILXXO.js} +2 -3
- package/dist/{checkbox.cva-IfOByVLA.js → CheckboxCheckmark-CpC3wlMs.js} +42 -1
- package/dist/{ComponentGallery-Da70Y4_Q.js → ComponentGallery-C6exitZN.js} +17 -17
- package/dist/{DatePicker-wulRNGyL.js → DatePicker-DOLL7mkc.js} +6 -6
- package/dist/{DatePickerInput-BB_vPlKu.js → DatePickerInput-BLBcySA2.js} +3 -3
- package/dist/{DateRangePicker-B0tMVREG.js → DateRangePicker-BF-1CztH.js} +6 -6
- package/dist/{DateTimePicker-WGMr0bEH.js → DateTimePicker-Cj0qvJUz.js} +6 -6
- package/dist/{FileInput-DP105Iy9.js → FileInput-Br5DQaGQ.js} +2 -2
- package/dist/{FileUpload-CgNDIvzg.js → FileUpload--7ub3EZO.js} +3 -3
- package/dist/{FormField-AY25LzbW.js → FormField-B99dLt8o.js} +96 -3
- package/dist/{FormFieldError-BVAO5p7H.js → FormFieldError-DeOHHLo2.js} +13 -2
- package/dist/{InputItem-ZeN-aIz8.js → InputItem-CIxbTxZu.js} +11 -11
- package/dist/{Inputs-BC5q5Cvj.js → Inputs-DJSKiaWY.js} +1 -1
- package/dist/{NumberRangeInput-B_sMIpP1.js → NumberRangeInput-B8-LHfg3.js} +2 -2
- package/dist/{PaginationList-DptMNpNs.js → PaginationList-DtvuP63b.js} +1 -1
- package/dist/{Progress-Du30L4_s.js → Progress-B3mY9rIP.js} +27 -2
- package/dist/{RadioGroup-CUGNSJql.js → RadioGroup-DOw3PRoX.js} +82 -3
- package/dist/{RichTextEditor-VDOpj7sk.js → RichTextEditor-D4yN7Wj7.js} +3 -3
- package/dist/{Select-B2pAfwRk.js → Select-UR0ec2Ux.js} +2 -2
- package/dist/{Slider-Clc1szFY.js → Slider-CzEmKYBF.js} +1 -1
- package/dist/{Switch-CimmMqEk.js → Switch-CqnGEdeD.js} +27 -3
- package/dist/{Table-CqepqSfe.js → Table-BN2_J_Ne.js} +1 -2
- package/dist/{tag.cva-B3MudLeC.js → Tag-DPg5sL1Z.js} +92 -1
- package/dist/{TextArea-B3teIhsJ.js → TextArea-D8WkKsGn.js} +5 -3
- package/dist/{TimePicker-CU_yL2sY.js → TimePicker-C4taAOFQ.js} +4 -4
- package/dist/{Toast-DPbnLlBi.js → Toast-DtG3pA61.js} +1 -1
- package/dist/{ToggleGroup-BULVwErp.js → ToggleGroup-DOk27fgI.js} +1 -1
- package/dist/UIConfigurator-BwLRfe-a.js +6704 -0
- package/dist/components/alert-dialog.js +1 -1
- package/dist/components/alert.js +1 -116
- package/dist/components/autocomplete.js +2 -2
- package/dist/components/button.js +1 -1
- package/dist/components/checkbox-group.js +3 -3
- package/dist/components/checkbox.js +1 -1
- package/dist/components/date-picker.js +1 -1
- package/dist/components/date-range-picker.js +1 -1
- package/dist/components/date-time-picker.js +1 -1
- package/dist/components/file-input.js +1 -1
- package/dist/components/file-upload-container.js +1 -1
- package/dist/components/file-upload.js +1 -1
- package/dist/components/form-field.js +1 -1
- package/dist/components/icon-button.js +1 -1
- package/dist/components/inline-icon-button.js +1 -1
- package/dist/components/input.js +3 -3
- package/dist/components/number-field.js +3 -3
- package/dist/components/number-range-input.js +1 -1
- package/dist/components/pagination-list.js +1 -1
- package/dist/components/pagination.js +1 -1
- package/dist/components/progress.js +1 -1
- package/dist/components/query-autocomplete.js +1 -1
- package/dist/components/query-select.js +2 -2
- package/dist/components/radio-group.js +1 -1
- package/dist/components/select.js +1 -1
- package/dist/components/slider.js +1 -1
- package/dist/components/split-button.js +1 -1
- package/dist/components/switch.js +1 -1
- package/dist/components/tag.js +1 -93
- package/dist/components/text-area.js +1 -1
- package/dist/components/text-button.js +1 -1
- package/dist/components/time-picker.js +1 -1
- package/dist/components/toast.js +1 -1
- package/dist/components/toggle-button.js +1 -1
- package/dist/components/toggle-group.js +1 -1
- package/dist/components/use-toast.js +2 -2
- package/dist/config/app-canvas-tanstack.d.ts +23 -0
- package/dist/config/app-canvas-tanstack.js +62 -0
- package/dist/config/app-canvas.d.ts +2 -69
- package/dist/config/app-canvas.js +2 -2253
- package/dist/config/ui-configurator.js +2 -5383
- package/dist/{datePickerValue.utils-CIFHjKwv.js → datePickerValue.utils-DCgoSrP3.js} +1 -1
- package/dist/index.js +19 -24
- package/dist/rhf/checkbox.js +1 -1
- package/dist/rhf/date-picker.js +1 -1
- package/dist/rhf/date-range-picker.js +1 -1
- package/dist/rhf/date-time-picker.js +1 -1
- package/dist/rhf/file-input.js +1 -1
- package/dist/rhf/file-upload.js +1 -1
- package/dist/rhf/number-range-input.js +1 -1
- package/dist/rhf/radio-group.js +1 -1
- package/dist/rhf/rich-text-editor.js +1 -1
- package/dist/rhf/select.js +1 -1
- package/dist/rhf/slider.js +1 -1
- package/dist/rhf/switch.js +1 -1
- package/dist/rhf/text-area.js +1 -1
- package/dist/rhf/time-picker.js +1 -1
- package/dist/rhf/toggle-group.js +1 -1
- package/dist/rhf.js +19 -24
- package/dist/rich-text-editor.js +1 -1
- package/dist/{staticDateTimeSegments-sf_ND7zL.js → staticDateTimeSegments-DtU63_dw.js} +1 -1
- package/dist/{staticSelect.utils-CzlnLPPd.js → staticSelect.utils-uHa8f_c6.js} +3 -3
- package/dist/styles/app-canvas.css +7 -0
- package/dist/{table.cva-DlMT-zLD.js → table.cva-D5oQ-QvA.js} +54 -1
- package/dist/tanstack/checkbox.js +1 -1
- package/dist/tanstack/date-picker.js +1 -1
- package/dist/tanstack/date-range-picker.js +1 -1
- package/dist/tanstack/date-time-picker.js +1 -1
- package/dist/tanstack/file-input.js +1 -1
- package/dist/tanstack/file-upload.js +1 -1
- package/dist/tanstack/form.js +1 -1
- package/dist/tanstack/inputs.js +1 -1
- package/dist/tanstack/number-range-input.js +1 -1
- package/dist/tanstack/radio-group.js +1 -1
- package/dist/tanstack/rich-text-editor.js +1 -1
- package/dist/tanstack/select.js +1 -1
- package/dist/tanstack/slider.js +1 -1
- package/dist/tanstack/switch.js +1 -1
- package/dist/tanstack/table.js +2 -2
- package/dist/tanstack/text-area.js +1 -1
- package/dist/tanstack/time-picker.js +1 -1
- package/dist/tanstack/toggle-group.js +1 -1
- package/dist/tanstack.js +31 -39
- package/dist/{toast.runtime-Zf-XyAC3.js → toast.runtime-cxex9e4E.js} +1 -1
- package/dist/{useFieldAccessibility-CcBTgGfm.js → useFieldAccessibility-ScC6Mtd5.js} +1 -1
- package/dist/{useFormAutosave-BD_TFqDF.js → useFormAutosave-DfuP7drj.js} +1 -1
- package/dist/{useStaticInputHandoff-kkQrc6DA.js → useStaticInputHandoff-NeZVnci-.js} +1 -1
- package/dist/{useTableColumnConfig-aVUWC2Mp.js → useTableColumnConfig-Di0_w80v.js} +2 -2
- package/package.json +10 -1
- package/dist/Button-BPTvffuk.js +0 -86
- package/dist/CheckboxCheckmark-D_uc9pon.js +0 -44
- package/dist/TextArea.cva-Bb73hriz.js +0 -5
- package/dist/formFieldError.cva-CsXaQRHX.js +0 -14
- package/dist/formFieldHelper.cva-B8F9LmsE.js +0 -96
- package/dist/grip-vertical-BVxcjdfr.js +0 -54
- package/dist/inspection-model-LuI36xTg.js +0 -1339
- package/dist/progress.cva-WQB8BGbk.js +0 -28
- package/dist/radio.cva-DGbvTymp.js +0 -82
- package/dist/status.cva-6HxNR4EP.js +0 -130
- package/dist/switch.cva-BX3HOxbL.js +0 -27
|
@@ -0,0 +1,2421 @@
|
|
|
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-BwLRfe-a.js";
|
|
3
|
+
import { t as ChevronDown } from "./chevron-down-B4Pr8bD3.js";
|
|
4
|
+
import { t as ChevronRight } from "./chevron-right-3nKJR5Bb.js";
|
|
5
|
+
import { i as Search } from "./selectDesktop.cva-CHAMxhfO.js";
|
|
6
|
+
import { t as Upload } from "./upload-CXU83BJ4.js";
|
|
7
|
+
import { t as X } from "./x-B6vfz8Ps.js";
|
|
8
|
+
import { memo, useCallback, useEffect, useMemo, useReducer, useRef, useState } from "react";
|
|
9
|
+
import { c } from "react/compiler-runtime";
|
|
10
|
+
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
11
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/code-xml.mjs
|
|
12
|
+
/**
|
|
13
|
+
* @license lucide-react v1.43.0 - ISC
|
|
14
|
+
*
|
|
15
|
+
* This source code is licensed under the ISC license.
|
|
16
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
17
|
+
*/
|
|
18
|
+
const __iconData$9 = {
|
|
19
|
+
name: "code-xml",
|
|
20
|
+
size: 24,
|
|
21
|
+
node: [
|
|
22
|
+
["path", {
|
|
23
|
+
d: "m18 16 4-4-4-4",
|
|
24
|
+
key: "1inbqp"
|
|
25
|
+
}],
|
|
26
|
+
["path", {
|
|
27
|
+
d: "m6 8-4 4 4 4",
|
|
28
|
+
key: "15zrgr"
|
|
29
|
+
}],
|
|
30
|
+
["path", {
|
|
31
|
+
d: "m14.5 4-5 16",
|
|
32
|
+
key: "e7oirm"
|
|
33
|
+
}]
|
|
34
|
+
],
|
|
35
|
+
aliases: ["code-2"]
|
|
36
|
+
};
|
|
37
|
+
__iconData$9.node;
|
|
38
|
+
const CodeXml = createLucideIcon(__iconData$9);
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/download.mjs
|
|
41
|
+
/**
|
|
42
|
+
* @license lucide-react v1.43.0 - ISC
|
|
43
|
+
*
|
|
44
|
+
* This source code is licensed under the ISC license.
|
|
45
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
46
|
+
*/
|
|
47
|
+
const __iconData$8 = {
|
|
48
|
+
name: "download",
|
|
49
|
+
size: 24,
|
|
50
|
+
node: [
|
|
51
|
+
["path", {
|
|
52
|
+
d: "M12 15V3",
|
|
53
|
+
key: "m9g1x1"
|
|
54
|
+
}],
|
|
55
|
+
["path", {
|
|
56
|
+
d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",
|
|
57
|
+
key: "ih7n3h"
|
|
58
|
+
}],
|
|
59
|
+
["path", {
|
|
60
|
+
d: "m7 10 5 5 5-5",
|
|
61
|
+
key: "brsn70"
|
|
62
|
+
}]
|
|
63
|
+
]
|
|
64
|
+
};
|
|
65
|
+
__iconData$8.node;
|
|
66
|
+
const Download = createLucideIcon(__iconData$8);
|
|
67
|
+
//#endregion
|
|
68
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/frame.mjs
|
|
69
|
+
/**
|
|
70
|
+
* @license lucide-react v1.43.0 - ISC
|
|
71
|
+
*
|
|
72
|
+
* This source code is licensed under the ISC license.
|
|
73
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
74
|
+
*/
|
|
75
|
+
const __iconData$7 = {
|
|
76
|
+
name: "frame",
|
|
77
|
+
size: 24,
|
|
78
|
+
node: [
|
|
79
|
+
["line", {
|
|
80
|
+
x1: "22",
|
|
81
|
+
x2: "2",
|
|
82
|
+
y1: "6",
|
|
83
|
+
y2: "6",
|
|
84
|
+
key: "15w7dq"
|
|
85
|
+
}],
|
|
86
|
+
["line", {
|
|
87
|
+
x1: "22",
|
|
88
|
+
x2: "2",
|
|
89
|
+
y1: "18",
|
|
90
|
+
y2: "18",
|
|
91
|
+
key: "1ip48p"
|
|
92
|
+
}],
|
|
93
|
+
["line", {
|
|
94
|
+
x1: "6",
|
|
95
|
+
x2: "6",
|
|
96
|
+
y1: "2",
|
|
97
|
+
y2: "22",
|
|
98
|
+
key: "a2lnyx"
|
|
99
|
+
}],
|
|
100
|
+
["line", {
|
|
101
|
+
x1: "18",
|
|
102
|
+
x2: "18",
|
|
103
|
+
y1: "2",
|
|
104
|
+
y2: "22",
|
|
105
|
+
key: "8vb6jd"
|
|
106
|
+
}]
|
|
107
|
+
]
|
|
108
|
+
};
|
|
109
|
+
__iconData$7.node;
|
|
110
|
+
const Frame = createLucideIcon(__iconData$7);
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/hand.mjs
|
|
113
|
+
/**
|
|
114
|
+
* @license lucide-react v1.43.0 - ISC
|
|
115
|
+
*
|
|
116
|
+
* This source code is licensed under the ISC license.
|
|
117
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
118
|
+
*/
|
|
119
|
+
const __iconData$6 = {
|
|
120
|
+
name: "hand",
|
|
121
|
+
size: 24,
|
|
122
|
+
node: [
|
|
123
|
+
["path", {
|
|
124
|
+
d: "M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2",
|
|
125
|
+
key: "1fvzgz"
|
|
126
|
+
}],
|
|
127
|
+
["path", {
|
|
128
|
+
d: "M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2",
|
|
129
|
+
key: "1kc0my"
|
|
130
|
+
}],
|
|
131
|
+
["path", {
|
|
132
|
+
d: "M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8",
|
|
133
|
+
key: "10h0bg"
|
|
134
|
+
}],
|
|
135
|
+
["path", {
|
|
136
|
+
d: "M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15",
|
|
137
|
+
key: "1s1gnw"
|
|
138
|
+
}]
|
|
139
|
+
]
|
|
140
|
+
};
|
|
141
|
+
__iconData$6.node;
|
|
142
|
+
const Hand = createLucideIcon(__iconData$6);
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/monitor.mjs
|
|
145
|
+
/**
|
|
146
|
+
* @license lucide-react v1.43.0 - ISC
|
|
147
|
+
*
|
|
148
|
+
* This source code is licensed under the ISC license.
|
|
149
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
150
|
+
*/
|
|
151
|
+
const __iconData$5 = {
|
|
152
|
+
name: "monitor",
|
|
153
|
+
size: 24,
|
|
154
|
+
node: [
|
|
155
|
+
["rect", {
|
|
156
|
+
width: "20",
|
|
157
|
+
height: "14",
|
|
158
|
+
x: "2",
|
|
159
|
+
y: "3",
|
|
160
|
+
rx: "2",
|
|
161
|
+
key: "48i651"
|
|
162
|
+
}],
|
|
163
|
+
["line", {
|
|
164
|
+
x1: "8",
|
|
165
|
+
x2: "16",
|
|
166
|
+
y1: "21",
|
|
167
|
+
y2: "21",
|
|
168
|
+
key: "1svkeh"
|
|
169
|
+
}],
|
|
170
|
+
["line", {
|
|
171
|
+
x1: "12",
|
|
172
|
+
x2: "12",
|
|
173
|
+
y1: "17",
|
|
174
|
+
y2: "21",
|
|
175
|
+
key: "vw1qmm"
|
|
176
|
+
}]
|
|
177
|
+
]
|
|
178
|
+
};
|
|
179
|
+
__iconData$5.node;
|
|
180
|
+
const Monitor = createLucideIcon(__iconData$5);
|
|
181
|
+
//#endregion
|
|
182
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/mouse-pointer-2.mjs
|
|
183
|
+
/**
|
|
184
|
+
* @license lucide-react v1.43.0 - ISC
|
|
185
|
+
*
|
|
186
|
+
* This source code is licensed under the ISC license.
|
|
187
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
188
|
+
*/
|
|
189
|
+
const __iconData$4 = {
|
|
190
|
+
name: "mouse-pointer-2",
|
|
191
|
+
size: 24,
|
|
192
|
+
node: [["path", {
|
|
193
|
+
d: "M4.037 4.688a.495.495 0 0 1 .651-.651l16 6.5a.5.5 0 0 1-.063.947l-6.124 1.58a2 2 0 0 0-1.438 1.435l-1.579 6.126a.5.5 0 0 1-.947.063z",
|
|
194
|
+
key: "edeuup"
|
|
195
|
+
}]]
|
|
196
|
+
};
|
|
197
|
+
__iconData$4.node;
|
|
198
|
+
const MousePointer2 = createLucideIcon(__iconData$4);
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/panel-left.mjs
|
|
201
|
+
/**
|
|
202
|
+
* @license lucide-react v1.43.0 - ISC
|
|
203
|
+
*
|
|
204
|
+
* This source code is licensed under the ISC license.
|
|
205
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
206
|
+
*/
|
|
207
|
+
const __iconData$3 = {
|
|
208
|
+
name: "panel-left",
|
|
209
|
+
size: 24,
|
|
210
|
+
node: [["rect", {
|
|
211
|
+
width: "18",
|
|
212
|
+
height: "18",
|
|
213
|
+
x: "3",
|
|
214
|
+
y: "3",
|
|
215
|
+
rx: "2",
|
|
216
|
+
key: "afitv7"
|
|
217
|
+
}], ["path", {
|
|
218
|
+
d: "M9 3v18",
|
|
219
|
+
key: "fh3hqa"
|
|
220
|
+
}]],
|
|
221
|
+
aliases: ["sidebar"]
|
|
222
|
+
};
|
|
223
|
+
__iconData$3.node;
|
|
224
|
+
const PanelLeft = createLucideIcon(__iconData$3);
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/panels-top-left.mjs
|
|
227
|
+
/**
|
|
228
|
+
* @license lucide-react v1.43.0 - ISC
|
|
229
|
+
*
|
|
230
|
+
* This source code is licensed under the ISC license.
|
|
231
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
232
|
+
*/
|
|
233
|
+
const __iconData$2 = {
|
|
234
|
+
name: "panels-top-left",
|
|
235
|
+
size: 24,
|
|
236
|
+
node: [
|
|
237
|
+
["rect", {
|
|
238
|
+
width: "18",
|
|
239
|
+
height: "18",
|
|
240
|
+
x: "3",
|
|
241
|
+
y: "3",
|
|
242
|
+
rx: "2",
|
|
243
|
+
key: "afitv7"
|
|
244
|
+
}],
|
|
245
|
+
["path", {
|
|
246
|
+
d: "M3 9h18",
|
|
247
|
+
key: "1pudct"
|
|
248
|
+
}],
|
|
249
|
+
["path", {
|
|
250
|
+
d: "M9 21V9",
|
|
251
|
+
key: "1oto5p"
|
|
252
|
+
}]
|
|
253
|
+
],
|
|
254
|
+
aliases: ["layout"]
|
|
255
|
+
};
|
|
256
|
+
__iconData$2.node;
|
|
257
|
+
const PanelsTopLeft = createLucideIcon(__iconData$2);
|
|
258
|
+
//#endregion
|
|
259
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/scan-line.mjs
|
|
260
|
+
/**
|
|
261
|
+
* @license lucide-react v1.43.0 - ISC
|
|
262
|
+
*
|
|
263
|
+
* This source code is licensed under the ISC license.
|
|
264
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
265
|
+
*/
|
|
266
|
+
const __iconData$1 = {
|
|
267
|
+
name: "scan-line",
|
|
268
|
+
size: 24,
|
|
269
|
+
node: [
|
|
270
|
+
["path", {
|
|
271
|
+
d: "M3 7V5a2 2 0 0 1 2-2h2",
|
|
272
|
+
key: "aa7l1z"
|
|
273
|
+
}],
|
|
274
|
+
["path", {
|
|
275
|
+
d: "M17 3h2a2 2 0 0 1 2 2v2",
|
|
276
|
+
key: "4qcy5o"
|
|
277
|
+
}],
|
|
278
|
+
["path", {
|
|
279
|
+
d: "M21 17v2a2 2 0 0 1-2 2h-2",
|
|
280
|
+
key: "6vwrx8"
|
|
281
|
+
}],
|
|
282
|
+
["path", {
|
|
283
|
+
d: "M7 21H5a2 2 0 0 1-2-2v-2",
|
|
284
|
+
key: "ioqczr"
|
|
285
|
+
}],
|
|
286
|
+
["path", {
|
|
287
|
+
d: "M7 12h10",
|
|
288
|
+
key: "b7w52i"
|
|
289
|
+
}]
|
|
290
|
+
]
|
|
291
|
+
};
|
|
292
|
+
__iconData$1.node;
|
|
293
|
+
const ScanLine = createLucideIcon(__iconData$1);
|
|
294
|
+
//#endregion
|
|
295
|
+
//#region ../../node_modules/.bun/lucide-react@1.43.0+83d5fd7b249dbeef/node_modules/lucide-react/dist/esm/icons/settings-2.mjs
|
|
296
|
+
/**
|
|
297
|
+
* @license lucide-react v1.43.0 - ISC
|
|
298
|
+
*
|
|
299
|
+
* This source code is licensed under the ISC license.
|
|
300
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
301
|
+
*/
|
|
302
|
+
const __iconData = {
|
|
303
|
+
name: "settings-2",
|
|
304
|
+
size: 24,
|
|
305
|
+
node: [
|
|
306
|
+
["path", {
|
|
307
|
+
d: "M14 17H5",
|
|
308
|
+
key: "gfn3mx"
|
|
309
|
+
}],
|
|
310
|
+
["path", {
|
|
311
|
+
d: "M19 7h-9",
|
|
312
|
+
key: "6i9tg"
|
|
313
|
+
}],
|
|
314
|
+
["circle", {
|
|
315
|
+
cx: "17",
|
|
316
|
+
cy: "17",
|
|
317
|
+
r: "3",
|
|
318
|
+
key: "18b49y"
|
|
319
|
+
}],
|
|
320
|
+
["circle", {
|
|
321
|
+
cx: "7",
|
|
322
|
+
cy: "7",
|
|
323
|
+
r: "3",
|
|
324
|
+
key: "dfmy0x"
|
|
325
|
+
}]
|
|
326
|
+
]
|
|
327
|
+
};
|
|
328
|
+
__iconData.node;
|
|
329
|
+
const Settings2 = createLucideIcon(__iconData);
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/app-canvas/useFrameStartup.ts
|
|
332
|
+
/** Bound concurrent bootstraps, without unloading any admitted page. */
|
|
333
|
+
function useFrameStartup(keys, concurrency = 2) {
|
|
334
|
+
const [admitted, setAdmitted] = useState(() => /* @__PURE__ */ new Set());
|
|
335
|
+
const state = useRef({
|
|
336
|
+
keys: [],
|
|
337
|
+
admitted: /* @__PURE__ */ new Set(),
|
|
338
|
+
pending: /* @__PURE__ */ new Map()
|
|
339
|
+
});
|
|
340
|
+
const pumpRef = useRef(() => {});
|
|
341
|
+
const pump = useCallback(() => {
|
|
342
|
+
const current = state.current;
|
|
343
|
+
let changed = false;
|
|
344
|
+
for (const key of current.keys) {
|
|
345
|
+
if (current.pending.size >= concurrency) break;
|
|
346
|
+
if (current.admitted.has(key)) continue;
|
|
347
|
+
current.admitted.add(key);
|
|
348
|
+
changed = true;
|
|
349
|
+
current.pending.set(key, setTimeout(() => {
|
|
350
|
+
current.pending.delete(key);
|
|
351
|
+
pumpRef.current();
|
|
352
|
+
}, 3e4));
|
|
353
|
+
}
|
|
354
|
+
if (changed) setAdmitted(new Set(current.admitted));
|
|
355
|
+
}, [concurrency]);
|
|
356
|
+
pumpRef.current = pump;
|
|
357
|
+
const signature = JSON.stringify(keys);
|
|
358
|
+
useEffect(() => {
|
|
359
|
+
const next = JSON.parse(signature);
|
|
360
|
+
const wanted = new Set(next);
|
|
361
|
+
const current_0 = state.current;
|
|
362
|
+
current_0.keys = next;
|
|
363
|
+
let changed_0 = false;
|
|
364
|
+
for (const key_0 of current_0.admitted) {
|
|
365
|
+
if (wanted.has(key_0)) continue;
|
|
366
|
+
current_0.admitted.delete(key_0);
|
|
367
|
+
const timer = current_0.pending.get(key_0);
|
|
368
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
369
|
+
current_0.pending.delete(key_0);
|
|
370
|
+
changed_0 = true;
|
|
371
|
+
}
|
|
372
|
+
if (changed_0) setAdmitted(new Set(current_0.admitted));
|
|
373
|
+
pump();
|
|
374
|
+
}, [signature, pump]);
|
|
375
|
+
useEffect(() => () => {
|
|
376
|
+
for (const timer_0 of state.current.pending.values()) clearTimeout(timer_0);
|
|
377
|
+
state.current.pending.clear();
|
|
378
|
+
state.current.admitted.clear();
|
|
379
|
+
}, []);
|
|
380
|
+
return {
|
|
381
|
+
admitted,
|
|
382
|
+
complete: useCallback((key_1) => {
|
|
383
|
+
if (!key_1) return;
|
|
384
|
+
const timer_1 = state.current.pending.get(key_1);
|
|
385
|
+
if (timer_1 === void 0) return;
|
|
386
|
+
clearTimeout(timer_1);
|
|
387
|
+
state.current.pending.delete(key_1);
|
|
388
|
+
pumpRef.current();
|
|
389
|
+
}, [])
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
//#endregion
|
|
393
|
+
//#region src/app-canvas/PageLayers.tsx
|
|
394
|
+
const excluded = /* @__PURE__ */ new Set([
|
|
395
|
+
"SCRIPT",
|
|
396
|
+
"STYLE",
|
|
397
|
+
"LINK",
|
|
398
|
+
"META",
|
|
399
|
+
"NOSCRIPT",
|
|
400
|
+
"TEMPLATE"
|
|
401
|
+
]);
|
|
402
|
+
function childrenOf(element) {
|
|
403
|
+
return Array.from(element.children).filter((child) => child.namespaceURI === "http://www.w3.org/1999/xhtml" && !excluded.has(child.tagName) && !child.hidden && child.getClientRects().length > 0);
|
|
404
|
+
}
|
|
405
|
+
function labelOf(element) {
|
|
406
|
+
return (element.getAttribute("aria-label") || element.getAttribute("alt") || (!element.children.length ? element.textContent?.trim().replace(/\s+/g, " ") : ""))?.slice(0, 48) || (element.id ? `#${element.id}` : "");
|
|
407
|
+
}
|
|
408
|
+
/** Only expanded branches are read and rendered; large pages do not create a second full DOM tree. */
|
|
409
|
+
function PageLayers({ frame, title, selected, onSelect }) {
|
|
410
|
+
const [root, setRoot] = useState(null);
|
|
411
|
+
const [, refresh] = useReducer((value) => value + 1, 0);
|
|
412
|
+
const expanded = useRef(/* @__PURE__ */ new WeakSet());
|
|
413
|
+
const collapsed = useRef(/* @__PURE__ */ new WeakSet());
|
|
414
|
+
const ids = useRef(/* @__PURE__ */ new WeakMap());
|
|
415
|
+
const nextId = useRef(0);
|
|
416
|
+
const [limit, setLimit] = useState(300);
|
|
417
|
+
useEffect(() => {
|
|
418
|
+
if (!frame) return;
|
|
419
|
+
let observer;
|
|
420
|
+
let scheduled = null;
|
|
421
|
+
const update = () => {
|
|
422
|
+
scheduled = null;
|
|
423
|
+
refresh();
|
|
424
|
+
};
|
|
425
|
+
const attach = () => {
|
|
426
|
+
observer?.disconnect();
|
|
427
|
+
try {
|
|
428
|
+
const doc = frame.contentDocument;
|
|
429
|
+
const element = doc?.querySelector("#storybook-root, #app, #root") ?? doc?.body ?? null;
|
|
430
|
+
setRoot(element);
|
|
431
|
+
if (element) {
|
|
432
|
+
observer = new MutationObserver(() => {
|
|
433
|
+
if (scheduled === null) scheduled = requestAnimationFrame(update);
|
|
434
|
+
});
|
|
435
|
+
observer.observe(element, {
|
|
436
|
+
childList: true,
|
|
437
|
+
subtree: true,
|
|
438
|
+
attributes: true,
|
|
439
|
+
characterData: true
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
} catch {
|
|
443
|
+
setRoot(null);
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
attach();
|
|
447
|
+
frame.addEventListener("load", attach);
|
|
448
|
+
return () => {
|
|
449
|
+
observer?.disconnect();
|
|
450
|
+
frame.removeEventListener("load", attach);
|
|
451
|
+
if (scheduled !== null) cancelAnimationFrame(scheduled);
|
|
452
|
+
};
|
|
453
|
+
}, [frame]);
|
|
454
|
+
useEffect(() => {
|
|
455
|
+
if (!root || !selected || !root.contains(selected)) return;
|
|
456
|
+
for (let parent = selected.parentElement; parent && parent !== root; parent = parent.parentElement) {
|
|
457
|
+
expanded.current.add(parent);
|
|
458
|
+
collapsed.current.delete(parent);
|
|
459
|
+
}
|
|
460
|
+
refresh();
|
|
461
|
+
}, [root, selected]);
|
|
462
|
+
const toggle = (element_0, open) => {
|
|
463
|
+
if (open) {
|
|
464
|
+
expanded.current.delete(element_0);
|
|
465
|
+
collapsed.current.add(element_0);
|
|
466
|
+
} else {
|
|
467
|
+
expanded.current.add(element_0);
|
|
468
|
+
collapsed.current.delete(element_0);
|
|
469
|
+
}
|
|
470
|
+
refresh();
|
|
471
|
+
};
|
|
472
|
+
let count = 0;
|
|
473
|
+
let truncated = false;
|
|
474
|
+
const render = (element_1, depth) => {
|
|
475
|
+
if (++count > limit || depth > 60) {
|
|
476
|
+
truncated = true;
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
if (!ids.current.has(element_1)) ids.current.set(element_1, ++nextId.current);
|
|
480
|
+
const children = childrenOf(element_1);
|
|
481
|
+
const open_0 = !collapsed.current.has(element_1) && (depth < 2 || expanded.current.has(element_1));
|
|
482
|
+
const label = labelOf(element_1);
|
|
483
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
484
|
+
role: "treeitem",
|
|
485
|
+
"aria-label": `${element_1.localName}${label ? ` ${label}` : ""}`,
|
|
486
|
+
"aria-level": depth + 1,
|
|
487
|
+
"aria-selected": element_1 === selected,
|
|
488
|
+
"aria-expanded": children.length ? open_0 : void 0,
|
|
489
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
490
|
+
className: "povio-canvas-layer-row",
|
|
491
|
+
style: { paddingLeft: 8 + depth * 12 },
|
|
492
|
+
children: [children.length ? /* @__PURE__ */ jsx("button", {
|
|
493
|
+
className: "povio-canvas-layer-toggle",
|
|
494
|
+
"aria-label": `${open_0 ? "Collapse" : "Expand"} ${element_1.localName}${label ? ` ${label}` : ""}`,
|
|
495
|
+
onClick: () => toggle(element_1, open_0),
|
|
496
|
+
children: open_0 ? /* @__PURE__ */ jsx(ChevronDown, { size: 12 }) : /* @__PURE__ */ jsx(ChevronRight, { size: 12 })
|
|
497
|
+
}) : /* @__PURE__ */ jsx("span", { className: "povio-canvas-layer-spacer" }), /* @__PURE__ */ jsxs("button", {
|
|
498
|
+
className: "povio-canvas-layer-select",
|
|
499
|
+
title: `${element_1.localName}${label ? ` · ${label}` : ""}`,
|
|
500
|
+
onClick: () => onSelect(element_1),
|
|
501
|
+
onKeyDown: (event) => {
|
|
502
|
+
if (event.key === "ArrowRight" && children.length && !open_0) {
|
|
503
|
+
event.preventDefault();
|
|
504
|
+
toggle(element_1, false);
|
|
505
|
+
}
|
|
506
|
+
if (event.key === "ArrowLeft" && open_0) {
|
|
507
|
+
event.preventDefault();
|
|
508
|
+
toggle(element_1, true);
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
children: [/* @__PURE__ */ jsx("code", { children: element_1.localName }), label && /* @__PURE__ */ jsx("span", { children: label })]
|
|
512
|
+
})]
|
|
513
|
+
}), open_0 && !!children.length && /* @__PURE__ */ jsx("div", {
|
|
514
|
+
role: "group",
|
|
515
|
+
children: children.map((child) => render(child, depth + 1))
|
|
516
|
+
})]
|
|
517
|
+
}, ids.current.get(element_1));
|
|
518
|
+
};
|
|
519
|
+
const content = root ? childrenOf(root).map((element_2) => render(element_2, 0)) : null;
|
|
520
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
521
|
+
className: "povio-canvas-layers",
|
|
522
|
+
"aria-label": `Layers for ${title}`,
|
|
523
|
+
children: [
|
|
524
|
+
/* @__PURE__ */ jsxs("header", { children: [
|
|
525
|
+
/* @__PURE__ */ jsx(CodeXml, { size: 14 }),
|
|
526
|
+
/* @__PURE__ */ jsx("strong", { children: "Layers" }),
|
|
527
|
+
/* @__PURE__ */ jsx("span", { children: title })
|
|
528
|
+
] }),
|
|
529
|
+
/* @__PURE__ */ jsx("div", {
|
|
530
|
+
role: "tree",
|
|
531
|
+
"aria-label": "HTML layers",
|
|
532
|
+
children: content
|
|
533
|
+
}),
|
|
534
|
+
!root && /* @__PURE__ */ jsx("p", { children: "Waiting for the page…" }),
|
|
535
|
+
truncated && /* @__PURE__ */ jsx("button", {
|
|
536
|
+
className: "povio-canvas-more-layers",
|
|
537
|
+
onClick: () => setLimit((value_0) => value_0 + 300),
|
|
538
|
+
children: "Show more layers"
|
|
539
|
+
})
|
|
540
|
+
]
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
//#endregion
|
|
544
|
+
//#region src/app-canvas/CanvasInspector.tsx
|
|
545
|
+
const unknownToken = () => false;
|
|
546
|
+
const readOnly = () => {};
|
|
547
|
+
/** Inspect the live iframe DOM using its own, unscaled viewport coordinates. */
|
|
548
|
+
function useCanvasInspector(enabled, generation) {
|
|
549
|
+
const [selection, setSelection] = useState(null);
|
|
550
|
+
const [measurement, setMeasurement] = useState(null);
|
|
551
|
+
const [pinned, setPinned] = useState(false);
|
|
552
|
+
const [highlightKey, setHighlightKey] = useState(null);
|
|
553
|
+
const hoverFrame = useRef(null);
|
|
554
|
+
const pending = useRef(null);
|
|
555
|
+
const clear = useCallback(() => {
|
|
556
|
+
if (hoverFrame.current !== null) cancelAnimationFrame(hoverFrame.current);
|
|
557
|
+
hoverFrame.current = null;
|
|
558
|
+
pending.current = null;
|
|
559
|
+
setSelection(null);
|
|
560
|
+
setMeasurement(null);
|
|
561
|
+
setPinned(false);
|
|
562
|
+
setHighlightKey(null);
|
|
563
|
+
}, []);
|
|
564
|
+
useEffect(() => {
|
|
565
|
+
if (!enabled) clear();
|
|
566
|
+
}, [enabled, clear]);
|
|
567
|
+
useEffect(() => {
|
|
568
|
+
clear();
|
|
569
|
+
}, [generation, clear]);
|
|
570
|
+
useEffect(() => () => {
|
|
571
|
+
if (hoverFrame.current !== null) cancelAnimationFrame(hoverFrame.current);
|
|
572
|
+
}, []);
|
|
573
|
+
const pick = useCallback((pageId, frame, clientX, clientY, pin) => {
|
|
574
|
+
if (!enabled || pinned && !pin) return;
|
|
575
|
+
const bounds = frame.getBoundingClientRect();
|
|
576
|
+
if (!bounds.width || !bounds.height) return;
|
|
577
|
+
try {
|
|
578
|
+
const doc = frame.contentDocument;
|
|
579
|
+
if (!doc) return;
|
|
580
|
+
const x = (clientX - bounds.left) * frame.clientWidth / bounds.width;
|
|
581
|
+
const y = (clientY - bounds.top) * frame.clientHeight / bounds.height;
|
|
582
|
+
const hit = doc.elementFromPoint(x, y);
|
|
583
|
+
const element = hit?.closest("button,input,textarea,select,a,label,[role],h1,h2,h3,h4,h5,h6,p") ?? hit;
|
|
584
|
+
if (!element || element === doc.documentElement || element === doc.body || element.namespaceURI !== "http://www.w3.org/1999/xhtml") return;
|
|
585
|
+
const target = element;
|
|
586
|
+
setSelection((current) => current?.element === target ? current : {
|
|
587
|
+
pageId,
|
|
588
|
+
element: target
|
|
589
|
+
});
|
|
590
|
+
setHighlightKey(null);
|
|
591
|
+
if (pin) setPinned((current_0) => !(current_0 && selection?.element === target));
|
|
592
|
+
} catch {
|
|
593
|
+
clear();
|
|
594
|
+
}
|
|
595
|
+
}, [
|
|
596
|
+
enabled,
|
|
597
|
+
pinned,
|
|
598
|
+
selection,
|
|
599
|
+
clear
|
|
600
|
+
]);
|
|
601
|
+
const hover = useCallback((pageId_0, frame_0, x_0, y_0) => {
|
|
602
|
+
if (!enabled || pinned) return;
|
|
603
|
+
pending.current = () => pick(pageId_0, frame_0, x_0, y_0, false);
|
|
604
|
+
if (hoverFrame.current !== null) return;
|
|
605
|
+
hoverFrame.current = requestAnimationFrame(() => {
|
|
606
|
+
hoverFrame.current = null;
|
|
607
|
+
pending.current?.();
|
|
608
|
+
pending.current = null;
|
|
609
|
+
});
|
|
610
|
+
}, [
|
|
611
|
+
enabled,
|
|
612
|
+
pinned,
|
|
613
|
+
pick
|
|
614
|
+
]);
|
|
615
|
+
const select = useCallback((pageId_1, frame_1, x_1, y_1) => {
|
|
616
|
+
if (hoverFrame.current !== null) cancelAnimationFrame(hoverFrame.current);
|
|
617
|
+
hoverFrame.current = null;
|
|
618
|
+
pending.current = null;
|
|
619
|
+
pick(pageId_1, frame_1, x_1, y_1, true);
|
|
620
|
+
}, [pick]);
|
|
621
|
+
useEffect(() => {
|
|
622
|
+
if (!enabled || !selection) return;
|
|
623
|
+
const { element: element_0 } = selection;
|
|
624
|
+
const doc_0 = element_0.ownerDocument;
|
|
625
|
+
const owner = doc_0.defaultView;
|
|
626
|
+
if (!owner) return;
|
|
627
|
+
let scheduled = null;
|
|
628
|
+
const measure = () => {
|
|
629
|
+
scheduled = null;
|
|
630
|
+
if (!element_0.isConnected) {
|
|
631
|
+
clear();
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const rect = element_0.getBoundingClientRect();
|
|
635
|
+
const snapshot = inspectElement(element_0, unknownToken);
|
|
636
|
+
const details = highlightKey ? getBoxHighlights(element_0, highlightKey) : null;
|
|
637
|
+
const spacing = highlightKey ? [] : [
|
|
638
|
+
"padding.top",
|
|
639
|
+
"padding.right",
|
|
640
|
+
"padding.bottom",
|
|
641
|
+
"padding.left",
|
|
642
|
+
"margin.top",
|
|
643
|
+
"margin.right",
|
|
644
|
+
"margin.bottom",
|
|
645
|
+
"margin.left",
|
|
646
|
+
"gap.row",
|
|
647
|
+
"gap.column"
|
|
648
|
+
].flatMap((key) => {
|
|
649
|
+
const [kind, side] = key.split(".");
|
|
650
|
+
const raw = kind === "padding" ? snapshot.box.padding[side] : kind === "margin" ? snapshot.box.margin[side] : snapshot.box.gap[side];
|
|
651
|
+
if (!(Math.abs(Number.parseFloat(raw)) > 0)) return [];
|
|
652
|
+
const details_0 = getBoxHighlights(element_0, key);
|
|
653
|
+
const regions = details_0.regions.filter((region) => region.width > 0 && region.height > 0);
|
|
654
|
+
return regions.length ? [{
|
|
655
|
+
key,
|
|
656
|
+
label: details_0.label,
|
|
657
|
+
regions
|
|
658
|
+
}] : [];
|
|
659
|
+
});
|
|
660
|
+
if (!highlightKey) {
|
|
661
|
+
const seen = new Set(spacing.flatMap((item) => item.regions.map((region_0) => `${item.key}:${region_0.left}:${region_0.top}:${region_0.width}:${region_0.height}`)));
|
|
662
|
+
for (const child of Array.from(element_0.children).slice(0, 80)) {
|
|
663
|
+
if (child.namespaceURI !== "http://www.w3.org/1999/xhtml" || !child.getClientRects().length) continue;
|
|
664
|
+
const style = owner.getComputedStyle(child);
|
|
665
|
+
if (style.display === "none" || style.visibility === "hidden" || /^(absolute|fixed)$/.test(style.position)) continue;
|
|
666
|
+
for (const side_0 of [
|
|
667
|
+
"top",
|
|
668
|
+
"right",
|
|
669
|
+
"bottom",
|
|
670
|
+
"left"
|
|
671
|
+
]) {
|
|
672
|
+
if (!(Math.abs(Number.parseFloat(style.getPropertyValue(`margin-${side_0}`))) > 0)) continue;
|
|
673
|
+
const key_0 = `margin.${side_0}`;
|
|
674
|
+
const details_1 = getBoxHighlights(child, key_0);
|
|
675
|
+
const regions_0 = details_1.regions.filter((region_1) => {
|
|
676
|
+
const id = `${key_0}:${region_1.left}:${region_1.top}:${region_1.width}:${region_1.height}`;
|
|
677
|
+
if (seen.has(id)) return false;
|
|
678
|
+
seen.add(id);
|
|
679
|
+
return region_1.width > 0 && region_1.height > 0;
|
|
680
|
+
});
|
|
681
|
+
if (regions_0.length) spacing.push({
|
|
682
|
+
key: key_0,
|
|
683
|
+
label: details_1.label,
|
|
684
|
+
regions: regions_0
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const next = {
|
|
690
|
+
snapshot,
|
|
691
|
+
spacing,
|
|
692
|
+
outline: {
|
|
693
|
+
left: rect.left,
|
|
694
|
+
top: rect.top,
|
|
695
|
+
width: rect.width,
|
|
696
|
+
height: rect.height
|
|
697
|
+
},
|
|
698
|
+
highlight: details && highlightKey ? {
|
|
699
|
+
key: highlightKey,
|
|
700
|
+
...details
|
|
701
|
+
} : null
|
|
702
|
+
};
|
|
703
|
+
setMeasurement((current_1) => JSON.stringify(current_1) === JSON.stringify(next) ? current_1 : next);
|
|
704
|
+
};
|
|
705
|
+
const schedule = () => {
|
|
706
|
+
if (scheduled === null) scheduled = requestAnimationFrame(measure);
|
|
707
|
+
};
|
|
708
|
+
const mutations = new MutationObserver(schedule);
|
|
709
|
+
mutations.observe(doc_0.documentElement, {
|
|
710
|
+
subtree: true,
|
|
711
|
+
childList: true,
|
|
712
|
+
attributes: true,
|
|
713
|
+
characterData: true
|
|
714
|
+
});
|
|
715
|
+
const resize = new ResizeObserver(schedule);
|
|
716
|
+
resize.observe(element_0);
|
|
717
|
+
owner.addEventListener("scroll", schedule, true);
|
|
718
|
+
measure();
|
|
719
|
+
return () => {
|
|
720
|
+
mutations.disconnect();
|
|
721
|
+
resize.disconnect();
|
|
722
|
+
owner.removeEventListener("scroll", schedule, true);
|
|
723
|
+
if (scheduled !== null) cancelAnimationFrame(scheduled);
|
|
724
|
+
};
|
|
725
|
+
}, [
|
|
726
|
+
enabled,
|
|
727
|
+
selection,
|
|
728
|
+
highlightKey,
|
|
729
|
+
clear
|
|
730
|
+
]);
|
|
731
|
+
return {
|
|
732
|
+
selection,
|
|
733
|
+
selectElement: (pageId_2, element_1) => {
|
|
734
|
+
setSelection({
|
|
735
|
+
pageId: pageId_2,
|
|
736
|
+
element: element_1
|
|
737
|
+
});
|
|
738
|
+
setPinned(true);
|
|
739
|
+
setHighlightKey(null);
|
|
740
|
+
},
|
|
741
|
+
measurement,
|
|
742
|
+
pinned,
|
|
743
|
+
hover,
|
|
744
|
+
select,
|
|
745
|
+
clear,
|
|
746
|
+
highlight: setHighlightKey,
|
|
747
|
+
togglePin: () => setPinned((value) => !value)
|
|
748
|
+
};
|
|
749
|
+
}
|
|
750
|
+
function CanvasInspectorPanel(t0) {
|
|
751
|
+
const $ = c(21);
|
|
752
|
+
const { inspector, onClose } = t0;
|
|
753
|
+
let t1;
|
|
754
|
+
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
|
755
|
+
t1 = /* @__PURE__ */ jsx("strong", { children: "Inspector view" });
|
|
756
|
+
$[0] = t1;
|
|
757
|
+
} else t1 = $[0];
|
|
758
|
+
const t2 = !inspector.selection;
|
|
759
|
+
let t3;
|
|
760
|
+
if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
|
|
761
|
+
t3 = /* @__PURE__ */ jsx(Pin, { size: 14 });
|
|
762
|
+
$[1] = t3;
|
|
763
|
+
} else t3 = $[1];
|
|
764
|
+
let t4;
|
|
765
|
+
if ($[2] !== inspector.pinned || $[3] !== inspector.togglePin || $[4] !== t2) {
|
|
766
|
+
t4 = /* @__PURE__ */ jsx("button", {
|
|
767
|
+
"aria-label": "Pin inspected element",
|
|
768
|
+
"aria-pressed": inspector.pinned,
|
|
769
|
+
disabled: t2,
|
|
770
|
+
onClick: inspector.togglePin,
|
|
771
|
+
children: t3
|
|
772
|
+
});
|
|
773
|
+
$[2] = inspector.pinned;
|
|
774
|
+
$[3] = inspector.togglePin;
|
|
775
|
+
$[4] = t2;
|
|
776
|
+
$[5] = t4;
|
|
777
|
+
} else t4 = $[5];
|
|
778
|
+
let t5;
|
|
779
|
+
if ($[6] === Symbol.for("react.memo_cache_sentinel")) {
|
|
780
|
+
t5 = /* @__PURE__ */ jsx(X, { size: 15 });
|
|
781
|
+
$[6] = t5;
|
|
782
|
+
} else t5 = $[6];
|
|
783
|
+
let t6;
|
|
784
|
+
if ($[7] !== onClose) {
|
|
785
|
+
t6 = /* @__PURE__ */ jsx("button", {
|
|
786
|
+
"aria-label": "Close inspector view",
|
|
787
|
+
onClick: onClose,
|
|
788
|
+
children: t5
|
|
789
|
+
});
|
|
790
|
+
$[7] = onClose;
|
|
791
|
+
$[8] = t6;
|
|
792
|
+
} else t6 = $[8];
|
|
793
|
+
let t7;
|
|
794
|
+
if ($[9] !== t4 || $[10] !== t6) {
|
|
795
|
+
t7 = /* @__PURE__ */ jsxs("header", { children: [
|
|
796
|
+
t1,
|
|
797
|
+
t4,
|
|
798
|
+
t6
|
|
799
|
+
] });
|
|
800
|
+
$[9] = t4;
|
|
801
|
+
$[10] = t6;
|
|
802
|
+
$[11] = t7;
|
|
803
|
+
} else t7 = $[11];
|
|
804
|
+
const t8 = inspector.pinned ? "Selection pinned · click it again to unpin" : "Hover a component · click to pin";
|
|
805
|
+
let t9;
|
|
806
|
+
if ($[12] !== t8) {
|
|
807
|
+
t9 = /* @__PURE__ */ jsx("p", {
|
|
808
|
+
className: "povio-canvas-inspect-hint",
|
|
809
|
+
children: t8
|
|
810
|
+
});
|
|
811
|
+
$[12] = t8;
|
|
812
|
+
$[13] = t9;
|
|
813
|
+
} else t9 = $[13];
|
|
814
|
+
let t10;
|
|
815
|
+
if ($[14] !== inspector.highlight || $[15] !== inspector.measurement) {
|
|
816
|
+
t10 = inspector.measurement ? /* @__PURE__ */ jsx(InspectorDetails, {
|
|
817
|
+
snapshot: inspector.measurement.snapshot,
|
|
818
|
+
onSelect: readOnly,
|
|
819
|
+
onHighlight: inspector.highlight
|
|
820
|
+
}) : /* @__PURE__ */ jsx("p", {
|
|
821
|
+
className: "povio-canvas-inspect-hint",
|
|
822
|
+
children: "Inspect a live page to see its padding, margins, borders, radii and gaps."
|
|
823
|
+
});
|
|
824
|
+
$[14] = inspector.highlight;
|
|
825
|
+
$[15] = inspector.measurement;
|
|
826
|
+
$[16] = t10;
|
|
827
|
+
} else t10 = $[16];
|
|
828
|
+
let t11;
|
|
829
|
+
if ($[17] !== t10 || $[18] !== t7 || $[19] !== t9) {
|
|
830
|
+
t11 = /* @__PURE__ */ jsxs("aside", {
|
|
831
|
+
className: "studio-inspector-panel povio-canvas-inspector",
|
|
832
|
+
"aria-label": "Inspector view",
|
|
833
|
+
children: [
|
|
834
|
+
t7,
|
|
835
|
+
t9,
|
|
836
|
+
t10
|
|
837
|
+
]
|
|
838
|
+
});
|
|
839
|
+
$[17] = t10;
|
|
840
|
+
$[18] = t7;
|
|
841
|
+
$[19] = t9;
|
|
842
|
+
$[20] = t11;
|
|
843
|
+
} else t11 = $[20];
|
|
844
|
+
return t11;
|
|
845
|
+
}
|
|
846
|
+
function CanvasInspectionOverlay(t0) {
|
|
847
|
+
const $ = c(9);
|
|
848
|
+
const { measurement, width } = t0;
|
|
849
|
+
let t1;
|
|
850
|
+
if ($[0] !== measurement.outline) {
|
|
851
|
+
t1 = /* @__PURE__ */ jsx("div", {
|
|
852
|
+
className: "studio-hover-outline",
|
|
853
|
+
style: measurement.outline
|
|
854
|
+
});
|
|
855
|
+
$[0] = measurement.outline;
|
|
856
|
+
$[1] = t1;
|
|
857
|
+
} else t1 = $[1];
|
|
858
|
+
let t2;
|
|
859
|
+
if ($[2] !== measurement.highlight || $[3] !== measurement.spacing || $[4] !== width) {
|
|
860
|
+
t2 = measurement.highlight ? /* @__PURE__ */ jsx(HighlightOverlay, {
|
|
861
|
+
highlight: measurement.highlight,
|
|
862
|
+
width
|
|
863
|
+
}) : measurement.spacing.map((highlight) => /* @__PURE__ */ jsxs("div", {
|
|
864
|
+
className: "povio-canvas-auto-spacing",
|
|
865
|
+
children: [/* @__PURE__ */ jsx(HighlightOverlay, {
|
|
866
|
+
highlight,
|
|
867
|
+
width
|
|
868
|
+
}), highlight.regions.map((region) => /* @__PURE__ */ jsx("span", {
|
|
869
|
+
className: "povio-canvas-spacing-value",
|
|
870
|
+
style: {
|
|
871
|
+
left: region.left + region.width / 2,
|
|
872
|
+
top: region.top + region.height / 2
|
|
873
|
+
},
|
|
874
|
+
children: highlight.label.split(": ")[1]
|
|
875
|
+
}, `${region.left}:${region.top}:${region.width}:${region.height}`))]
|
|
876
|
+
}, `${highlight.key}:${highlight.label}:${highlight.regions[0]?.left}:${highlight.regions[0]?.top}`));
|
|
877
|
+
$[2] = measurement.highlight;
|
|
878
|
+
$[3] = measurement.spacing;
|
|
879
|
+
$[4] = width;
|
|
880
|
+
$[5] = t2;
|
|
881
|
+
} else t2 = $[5];
|
|
882
|
+
let t3;
|
|
883
|
+
if ($[6] !== t1 || $[7] !== t2) {
|
|
884
|
+
t3 = /* @__PURE__ */ jsxs("div", {
|
|
885
|
+
className: "povio-canvas-inspection-overlay",
|
|
886
|
+
"aria-hidden": "true",
|
|
887
|
+
children: [t1, t2]
|
|
888
|
+
});
|
|
889
|
+
$[6] = t1;
|
|
890
|
+
$[7] = t2;
|
|
891
|
+
$[8] = t3;
|
|
892
|
+
} else t3 = $[8];
|
|
893
|
+
return t3;
|
|
894
|
+
}
|
|
895
|
+
//#endregion
|
|
896
|
+
//#region src/app-canvas/route-model.ts
|
|
897
|
+
function joinPath(parent, path) {
|
|
898
|
+
return `${parent.replace(/\/$/, "")}/${path.replace(/^\//, "")}`.replace(/\/{2,}/g, "/") || "/";
|
|
899
|
+
}
|
|
900
|
+
function resolvePath(path, params) {
|
|
901
|
+
const missingParams = /* @__PURE__ */ new Set();
|
|
902
|
+
const value = (key, optional = false) => {
|
|
903
|
+
const actualKey = key || "_splat";
|
|
904
|
+
const raw = params[actualKey];
|
|
905
|
+
if (raw === void 0 || !optional && raw === "") {
|
|
906
|
+
if (!optional) missingParams.add(actualKey);
|
|
907
|
+
return "";
|
|
908
|
+
}
|
|
909
|
+
return actualKey === "_splat" ? raw.split("/").map(encodeURIComponent).join("/") : encodeURIComponent(raw);
|
|
910
|
+
};
|
|
911
|
+
return {
|
|
912
|
+
href: `/${path.split("/").map((segment) => {
|
|
913
|
+
if (segment.includes("{")) return segment.replace(/\{(-?)\$([^}]*)\}/g, (_, optional, key) => value(key, optional === "-"));
|
|
914
|
+
return segment.startsWith("$") ? value(segment.slice(1)) : segment;
|
|
915
|
+
}).filter(Boolean).join("/")}`,
|
|
916
|
+
missingParams: [...missingParams]
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
/** Catalog route destinations without executing loaders or needing every dynamic parameter. */
|
|
920
|
+
function discoverCanvasPages(router, options = {}) {
|
|
921
|
+
const records = [];
|
|
922
|
+
const visited = /* @__PURE__ */ new Set();
|
|
923
|
+
function visit(route, parentPath, ancestors, root = false) {
|
|
924
|
+
if (visited.has(route)) return;
|
|
925
|
+
visited.add(route);
|
|
926
|
+
const localPath = route.path ?? route.options?.path;
|
|
927
|
+
const path = route.fullPath ?? (localPath ? joinPath(parentPath, localPath) : parentPath);
|
|
928
|
+
const id = route.id ?? route.options?.id ?? path;
|
|
929
|
+
const children = Object.values(route.children ?? {});
|
|
930
|
+
if (!root && localPath !== void 0 && !options.exclude?.includes(id) && !options.excludePaths?.some((excluded) => (excluded.replace(/\/$/, "") || "/") === (path.replace(/\/$/, "") || "/"))) records.push({
|
|
931
|
+
route,
|
|
932
|
+
id,
|
|
933
|
+
path,
|
|
934
|
+
ancestors,
|
|
935
|
+
index: localPath === "/"
|
|
936
|
+
});
|
|
937
|
+
for (const child of children) visit(child, path, [...ancestors, id]);
|
|
938
|
+
}
|
|
939
|
+
visit(router.routeTree, "/", [], true);
|
|
940
|
+
for (const route of Object.values(router.routesById ?? {})) if (!visited.has(route)) visit(route, "/", []);
|
|
941
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
942
|
+
for (const record of records) {
|
|
943
|
+
const key = record.path.replace(/\/$/, "") || "/";
|
|
944
|
+
const existing = byPath.get(key);
|
|
945
|
+
if (!existing || record.index || record.ancestors.length > existing.ancestors.length) byPath.set(key, record);
|
|
946
|
+
}
|
|
947
|
+
const selectedIds = new Set([...byPath.values()].map(({ id }) => id));
|
|
948
|
+
const destinationIds = new Map(records.map((record) => [record.id, byPath.get(record.path.replace(/\/$/, "") || "/")?.id]));
|
|
949
|
+
return [...byPath.values()].map(({ id, path, ancestors }) => {
|
|
950
|
+
const example = options.examples?.[id];
|
|
951
|
+
const resolved = resolvePath(path, example?.params ?? {});
|
|
952
|
+
const query = new URLSearchParams();
|
|
953
|
+
for (const [key, value] of Object.entries(example?.search ?? {})) if (value !== void 0) query.set(key, typeof value === "string" ? value : JSON.stringify(value));
|
|
954
|
+
const queryString = query.toString();
|
|
955
|
+
return {
|
|
956
|
+
id,
|
|
957
|
+
path,
|
|
958
|
+
title: example?.title ?? (path === "/" ? "Home" : path),
|
|
959
|
+
href: resolved.missingParams.length ? void 0 : `${resolved.href}${queryString ? `?${queryString}` : ""}`,
|
|
960
|
+
parentId: [...ancestors].reverse().map((ancestor) => destinationIds.get(ancestor) ?? ancestor).find((ancestor) => ancestor !== id && selectedIds.has(ancestor)),
|
|
961
|
+
missingParams: resolved.missingParams,
|
|
962
|
+
kind: resolved.missingParams.length ? "unresolved" : "page"
|
|
963
|
+
};
|
|
964
|
+
});
|
|
965
|
+
}
|
|
966
|
+
/** Deterministic masonry placement. Full measured heights (including 20,000px pages) are never capped. */
|
|
967
|
+
function layoutCanvasPages(pages, width, heights, positions = {}) {
|
|
968
|
+
const pageWidth = Number.isFinite(width) && width > 0 ? width : 1440;
|
|
969
|
+
const columns = Math.min(3, Math.max(1, pages.length));
|
|
970
|
+
const bottoms = Array.from({ length: columns }, () => 0);
|
|
971
|
+
for (const page of pages) {
|
|
972
|
+
const saved = positions[page.id];
|
|
973
|
+
if (!saved || !Number.isFinite(saved.x) || !Number.isFinite(saved.y)) continue;
|
|
974
|
+
const measured = heights[page.id];
|
|
975
|
+
const height = Number.isFinite(measured) && measured > 0 ? measured : 900;
|
|
976
|
+
for (let i = 0; i < columns; i++) {
|
|
977
|
+
const x = i * (pageWidth + 80);
|
|
978
|
+
if (saved.x < x + pageWidth && saved.x + pageWidth > x) bottoms[i] = Math.max(bottoms[i], saved.y + height + 128);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
const result = [];
|
|
982
|
+
for (const page of pages) {
|
|
983
|
+
const measured = heights[page.id];
|
|
984
|
+
const height = Number.isFinite(measured) && measured > 0 ? measured : 900;
|
|
985
|
+
const column = bottoms.indexOf(Math.min(...bottoms));
|
|
986
|
+
const saved = positions[page.id];
|
|
987
|
+
const position = saved && Number.isFinite(saved.x) && Number.isFinite(saved.y) ? saved : {
|
|
988
|
+
x: column * (pageWidth + 80),
|
|
989
|
+
y: bottoms[column]
|
|
990
|
+
};
|
|
991
|
+
result.push({
|
|
992
|
+
...page,
|
|
993
|
+
...position,
|
|
994
|
+
width: pageWidth,
|
|
995
|
+
height
|
|
996
|
+
});
|
|
997
|
+
for (let i = 0; i < columns; i++) {
|
|
998
|
+
const x = i * (pageWidth + 80);
|
|
999
|
+
if (position.x < x + pageWidth && position.x + pageWidth > x) bottoms[i] = Math.max(bottoms[i], position.y + height + 128);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
return result;
|
|
1003
|
+
}
|
|
1004
|
+
/** Match observed navigation back to a catalog destination, including optional and splat routes. */
|
|
1005
|
+
function matchCanvasPageHref(pages, href, base = "http://canvas.local/") {
|
|
1006
|
+
let pathname;
|
|
1007
|
+
try {
|
|
1008
|
+
pathname = new URL(href, base).pathname.replace(/\/$/, "") || "/";
|
|
1009
|
+
} catch {
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const exact = pages.find((page) => {
|
|
1013
|
+
if (!page.href) return false;
|
|
1014
|
+
try {
|
|
1015
|
+
return (new URL(page.href, base).pathname.replace(/\/$/, "") || "/") === pathname;
|
|
1016
|
+
} catch {
|
|
1017
|
+
return false;
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
if (exact) return exact;
|
|
1021
|
+
const escape = (part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1022
|
+
for (const page of pages) {
|
|
1023
|
+
const pattern = page.path.split("/").filter(Boolean).map((part) => {
|
|
1024
|
+
if (part === "$") return "/.+";
|
|
1025
|
+
if (part.startsWith("$")) return "/[^/]+";
|
|
1026
|
+
let text = "", cursor = 0;
|
|
1027
|
+
let optional = false;
|
|
1028
|
+
for (const match of part.matchAll(/\{(-?)\$([^}]*)\}/g)) {
|
|
1029
|
+
text += escape(part.slice(cursor, match.index)) + (match[2] ? "[^/]+" : ".+");
|
|
1030
|
+
cursor = match.index + match[0].length;
|
|
1031
|
+
optional ||= match[1] === "-";
|
|
1032
|
+
}
|
|
1033
|
+
text += escape(part.slice(cursor));
|
|
1034
|
+
return optional ? `(?:/${text})?` : `/${text}`;
|
|
1035
|
+
}).join("");
|
|
1036
|
+
if (new RegExp(`^${pattern || "/"}$`).test(pathname)) return page;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
//#endregion
|
|
1040
|
+
//#region src/app-canvas/protocol.ts
|
|
1041
|
+
const canvasProtocol = "povio-app-canvas-v1";
|
|
1042
|
+
function canvasFrameIdentity() {
|
|
1043
|
+
if (typeof window === "undefined" || window.parent === window) return null;
|
|
1044
|
+
try {
|
|
1045
|
+
const query = new URLSearchParams(window.location.search);
|
|
1046
|
+
const name = window.name.startsWith(`povio-app-canvas-v1:`) ? JSON.parse(window.name.slice(20)) : null;
|
|
1047
|
+
const id = query.get("__povio_canvas_frame") ?? name?.id;
|
|
1048
|
+
const token = query.get("__povio_canvas_token") ?? name?.token;
|
|
1049
|
+
const height = Number(query.get("__povio_canvas_height") ?? name?.viewportHeight ?? 900);
|
|
1050
|
+
return typeof id === "string" && typeof token === "string" && token.length > 10 ? {
|
|
1051
|
+
id,
|
|
1052
|
+
token,
|
|
1053
|
+
viewportHeight: Math.max(240, Math.min(4e3, height || 900))
|
|
1054
|
+
} : null;
|
|
1055
|
+
} catch {
|
|
1056
|
+
return null;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
const isAppCanvasFrame = () => !!canvasFrameIdentity();
|
|
1060
|
+
function validConfiguration(value) {
|
|
1061
|
+
if (!value || typeof value !== "object") return false;
|
|
1062
|
+
const config = value;
|
|
1063
|
+
return !!config.theme && [
|
|
1064
|
+
"light",
|
|
1065
|
+
"dark",
|
|
1066
|
+
"theme"
|
|
1067
|
+
].every((key) => {
|
|
1068
|
+
const tokens = config.theme[key];
|
|
1069
|
+
return tokens && typeof tokens === "object" && Object.values(tokens).every((v) => typeof v === "string");
|
|
1070
|
+
}) && !!config.config && typeof config.config === "object";
|
|
1071
|
+
}
|
|
1072
|
+
//#endregion
|
|
1073
|
+
//#region src/app-canvas/manifest.ts
|
|
1074
|
+
function createCanvasManifest() {
|
|
1075
|
+
return {
|
|
1076
|
+
version: 1,
|
|
1077
|
+
positions: {},
|
|
1078
|
+
examples: {},
|
|
1079
|
+
manualEdges: [],
|
|
1080
|
+
hiddenEdges: [],
|
|
1081
|
+
breakpoint: "desktop"
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
function isRecord(value) {
|
|
1085
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1086
|
+
}
|
|
1087
|
+
function isJSON(value, ancestors = /* @__PURE__ */ new Set(), depth = 0) {
|
|
1088
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") return true;
|
|
1089
|
+
if (typeof value === "number") return Number.isFinite(value);
|
|
1090
|
+
if (typeof value !== "object" || depth > 30 || ancestors.has(value)) return false;
|
|
1091
|
+
ancestors.add(value);
|
|
1092
|
+
const valid = Object.values(value).every((child) => isJSON(child, ancestors, depth + 1));
|
|
1093
|
+
ancestors.delete(value);
|
|
1094
|
+
return valid;
|
|
1095
|
+
}
|
|
1096
|
+
/** Sanitize either stored JSON text or an imported value, retaining valid v1 entries. */
|
|
1097
|
+
function parseCanvasManifest(input) {
|
|
1098
|
+
const fallback = createCanvasManifest();
|
|
1099
|
+
let value = input;
|
|
1100
|
+
if (typeof value === "string") try {
|
|
1101
|
+
value = JSON.parse(value);
|
|
1102
|
+
} catch {
|
|
1103
|
+
return fallback;
|
|
1104
|
+
}
|
|
1105
|
+
if (!isRecord(value) || value.version !== 1) return fallback;
|
|
1106
|
+
const positions = Object.fromEntries(Object.entries(isRecord(value.positions) ? value.positions : {}).flatMap(([breakpoint, entries]) => {
|
|
1107
|
+
if (!isRecord(entries)) return [];
|
|
1108
|
+
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, {
|
|
1109
|
+
x: point.x,
|
|
1110
|
+
y: point.y
|
|
1111
|
+
}]] : []))]];
|
|
1112
|
+
}));
|
|
1113
|
+
const examples = Object.fromEntries(Object.entries(isRecord(value.examples) ? value.examples : {}).flatMap(([id, example]) => {
|
|
1114
|
+
if (!isRecord(example)) return [];
|
|
1115
|
+
const clean = {};
|
|
1116
|
+
if (typeof example.title === "string") clean.title = example.title;
|
|
1117
|
+
if (isRecord(example.params)) clean.params = Object.fromEntries(Object.entries(example.params).filter((entry) => typeof entry[1] === "string"));
|
|
1118
|
+
if (isRecord(example.search)) clean.search = Object.fromEntries(Object.entries(example.search).filter(([, item]) => isJSON(item)));
|
|
1119
|
+
return [[id, clean]];
|
|
1120
|
+
}));
|
|
1121
|
+
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 ? [{
|
|
1122
|
+
from: edge.from,
|
|
1123
|
+
to: edge.to
|
|
1124
|
+
}] : []);
|
|
1125
|
+
return {
|
|
1126
|
+
version: 1,
|
|
1127
|
+
positions,
|
|
1128
|
+
examples,
|
|
1129
|
+
manualEdges: [...new Map(manualEdges.map((edge) => [JSON.stringify(edge), edge])).values()],
|
|
1130
|
+
hiddenEdges: [...new Set((Array.isArray(value.hiddenEdges) ? value.hiddenEdges : []).filter((edge) => typeof edge === "string"))],
|
|
1131
|
+
breakpoint: typeof value.breakpoint === "string" && value.breakpoint.length > 0 ? value.breakpoint : fallback.breakpoint
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
//#endregion
|
|
1135
|
+
//#region src/app-canvas/AppCanvas.tsx
|
|
1136
|
+
const defaultCanvasBreakpoints = [
|
|
1137
|
+
{
|
|
1138
|
+
id: "mobile",
|
|
1139
|
+
label: "Mobile",
|
|
1140
|
+
width: 390,
|
|
1141
|
+
height: 844
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
id: "tablet",
|
|
1145
|
+
label: "Tablet",
|
|
1146
|
+
width: 768,
|
|
1147
|
+
height: 1024
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
id: "desktop",
|
|
1151
|
+
label: "Desktop",
|
|
1152
|
+
width: 1440,
|
|
1153
|
+
height: 900
|
|
1154
|
+
}
|
|
1155
|
+
];
|
|
1156
|
+
const emptyExamples = {};
|
|
1157
|
+
const emptyExclude = [];
|
|
1158
|
+
function readManifest(key) {
|
|
1159
|
+
if (typeof window === "undefined") return createCanvasManifest();
|
|
1160
|
+
try {
|
|
1161
|
+
return parseCanvasManifest(localStorage.getItem(key));
|
|
1162
|
+
} catch {
|
|
1163
|
+
return createCanvasManifest();
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const PageFrame = memo(function PageFrame(t0) {
|
|
1167
|
+
const $ = c(16);
|
|
1168
|
+
const { id, src, width, height, name, interact, inspect, onLoad, register } = t0;
|
|
1169
|
+
let t1;
|
|
1170
|
+
if ($[0] !== id || $[1] !== register) {
|
|
1171
|
+
t1 = (element) => register(id, element);
|
|
1172
|
+
$[0] = id;
|
|
1173
|
+
$[1] = register;
|
|
1174
|
+
$[2] = t1;
|
|
1175
|
+
} else t1 = $[2];
|
|
1176
|
+
const t2 = !interact && !inspect;
|
|
1177
|
+
const t3 = interact ? 0 : -1;
|
|
1178
|
+
const t4 = `Page ${id}`;
|
|
1179
|
+
let t5;
|
|
1180
|
+
if ($[3] !== id || $[4] !== onLoad) {
|
|
1181
|
+
t5 = (event) => onLoad(id, event.currentTarget);
|
|
1182
|
+
$[3] = id;
|
|
1183
|
+
$[4] = onLoad;
|
|
1184
|
+
$[5] = t5;
|
|
1185
|
+
} else t5 = $[5];
|
|
1186
|
+
let t6;
|
|
1187
|
+
if ($[6] !== height || $[7] !== name || $[8] !== src || $[9] !== t1 || $[10] !== t2 || $[11] !== t3 || $[12] !== t4 || $[13] !== t5 || $[14] !== width) {
|
|
1188
|
+
t6 = /* @__PURE__ */ jsx("iframe", {
|
|
1189
|
+
ref: t1,
|
|
1190
|
+
inert: t2,
|
|
1191
|
+
tabIndex: t3,
|
|
1192
|
+
title: t4,
|
|
1193
|
+
name,
|
|
1194
|
+
src,
|
|
1195
|
+
width,
|
|
1196
|
+
height,
|
|
1197
|
+
className: "povio-canvas-frame",
|
|
1198
|
+
onLoad: t5
|
|
1199
|
+
});
|
|
1200
|
+
$[6] = height;
|
|
1201
|
+
$[7] = name;
|
|
1202
|
+
$[8] = src;
|
|
1203
|
+
$[9] = t1;
|
|
1204
|
+
$[10] = t2;
|
|
1205
|
+
$[11] = t3;
|
|
1206
|
+
$[12] = t4;
|
|
1207
|
+
$[13] = t5;
|
|
1208
|
+
$[14] = width;
|
|
1209
|
+
$[15] = t6;
|
|
1210
|
+
} else t6 = $[15];
|
|
1211
|
+
return t6;
|
|
1212
|
+
});
|
|
1213
|
+
/** Live route artboards. The camera transforms only the canvas, never the iframe viewport width. */
|
|
1214
|
+
function AppCanvas({ router, examples = emptyExamples, exclude = emptyExclude, excludePaths = emptyExclude, breakpoints = defaultCanvasBreakpoints, storageKey = "povio-app-canvas", frameUrl }) {
|
|
1215
|
+
const configuration = useUIConfiguration();
|
|
1216
|
+
const [manifest, setManifest] = useState(() => readManifest(storageKey));
|
|
1217
|
+
const [runtime, setRuntime] = useState({});
|
|
1218
|
+
const [selected, setSelected] = useState(null);
|
|
1219
|
+
const [interact, setInteract] = useState(false);
|
|
1220
|
+
const [inspectMode, setInspectMode] = useState(false);
|
|
1221
|
+
const [dark, setDark] = useState(false);
|
|
1222
|
+
const [mobilePanel, setMobilePanel] = useState(null);
|
|
1223
|
+
const [query, setQuery] = useState("");
|
|
1224
|
+
const [notice, setNotice] = useState("");
|
|
1225
|
+
const [documentGeneration, setDocumentGeneration] = useState(0);
|
|
1226
|
+
const [reload, setReload] = useState({});
|
|
1227
|
+
const zoomOutput = useRef(null);
|
|
1228
|
+
const importInput = useRef(null);
|
|
1229
|
+
const viewport = useRef(null), world = useRef(null);
|
|
1230
|
+
const frames = useRef(/* @__PURE__ */ new Map());
|
|
1231
|
+
const [token] = useState(() => crypto.randomUUID());
|
|
1232
|
+
const revision = useRef(0), camera = useRef({
|
|
1233
|
+
x: 40,
|
|
1234
|
+
y: 40,
|
|
1235
|
+
zoom: .25
|
|
1236
|
+
}), pendingCamera = useRef(null);
|
|
1237
|
+
const pointer = useRef(null);
|
|
1238
|
+
const cameraRevision = useRef(0);
|
|
1239
|
+
const bp = breakpoints.find((item) => item.id === manifest.breakpoint) ?? breakpoints[breakpoints.length - 1] ?? defaultCanvasBreakpoints[2];
|
|
1240
|
+
const inspector = useCanvasInspector(inspectMode, `${bp.id}:${documentGeneration}:${JSON.stringify(reload)}`);
|
|
1241
|
+
const catalog = useMemo(() => discoverCanvasPages(router, {
|
|
1242
|
+
examples: {
|
|
1243
|
+
...examples,
|
|
1244
|
+
...manifest.examples
|
|
1245
|
+
},
|
|
1246
|
+
exclude,
|
|
1247
|
+
excludePaths
|
|
1248
|
+
}), [
|
|
1249
|
+
router,
|
|
1250
|
+
examples,
|
|
1251
|
+
manifest.examples,
|
|
1252
|
+
exclude,
|
|
1253
|
+
excludePaths
|
|
1254
|
+
]);
|
|
1255
|
+
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}`])), [
|
|
1256
|
+
catalog,
|
|
1257
|
+
documentGeneration,
|
|
1258
|
+
bp.id,
|
|
1259
|
+
reload
|
|
1260
|
+
]);
|
|
1261
|
+
const startup = useFrameStartup([...startupKeys.values()]);
|
|
1262
|
+
const startupRef = useRef({
|
|
1263
|
+
keys: startupKeys,
|
|
1264
|
+
complete: startup.complete
|
|
1265
|
+
});
|
|
1266
|
+
startupRef.current = {
|
|
1267
|
+
keys: startupKeys,
|
|
1268
|
+
complete: startup.complete
|
|
1269
|
+
};
|
|
1270
|
+
const heights = useMemo(() => Object.fromEntries(catalog.map((page_1) => [page_1.id, runtime[`${bp.id}:${page_1.id}:${page_1.href ?? ""}`]?.height ?? bp.height])), [
|
|
1271
|
+
catalog,
|
|
1272
|
+
runtime,
|
|
1273
|
+
bp
|
|
1274
|
+
]);
|
|
1275
|
+
const nodes = useMemo(() => layoutCanvasPages(catalog, bp.width, heights, manifest.positions[bp.id] ?? {}), [
|
|
1276
|
+
catalog,
|
|
1277
|
+
bp,
|
|
1278
|
+
heights,
|
|
1279
|
+
manifest.positions
|
|
1280
|
+
]);
|
|
1281
|
+
const nodeMap = useMemo(() => new Map(nodes.map((node) => [node.id, node])), [nodes]);
|
|
1282
|
+
const latest = useRef({
|
|
1283
|
+
configuration: configuration.active,
|
|
1284
|
+
dark,
|
|
1285
|
+
interact,
|
|
1286
|
+
bp,
|
|
1287
|
+
catalog,
|
|
1288
|
+
frameUrl
|
|
1289
|
+
});
|
|
1290
|
+
latest.current = {
|
|
1291
|
+
configuration: configuration.active,
|
|
1292
|
+
dark,
|
|
1293
|
+
interact,
|
|
1294
|
+
bp,
|
|
1295
|
+
catalog,
|
|
1296
|
+
frameUrl
|
|
1297
|
+
};
|
|
1298
|
+
const register = useCallback((id, frame) => {
|
|
1299
|
+
if (frame) frames.current.set(id, frame);
|
|
1300
|
+
else frames.current.delete(id);
|
|
1301
|
+
}, []);
|
|
1302
|
+
const paint = useCallback(() => {
|
|
1303
|
+
if (world.current) world.current.style.transform = `translate3d(${camera.current.x}px,${camera.current.y}px,0) scale(${camera.current.zoom})`;
|
|
1304
|
+
if (zoomOutput.current) zoomOutput.current.textContent = `${Math.round(camera.current.zoom * 1e3) / 10}%`;
|
|
1305
|
+
const canvas = viewport.current?.closest(".povio-app-canvas");
|
|
1306
|
+
if (canvas) canvas.setAttribute("data-camera-revision", String(++cameraRevision.current));
|
|
1307
|
+
}, []);
|
|
1308
|
+
const schedulePaint = useCallback(() => {
|
|
1309
|
+
if (pendingCamera.current !== null) return;
|
|
1310
|
+
pendingCamera.current = requestAnimationFrame(() => {
|
|
1311
|
+
pendingCamera.current = null;
|
|
1312
|
+
paint();
|
|
1313
|
+
});
|
|
1314
|
+
}, [paint]);
|
|
1315
|
+
const send = useCallback((id_0, type = "configuration") => {
|
|
1316
|
+
const frame_0 = frames.current.get(id_0);
|
|
1317
|
+
if (!frame_0?.contentWindow) return;
|
|
1318
|
+
const current = latest.current;
|
|
1319
|
+
try {
|
|
1320
|
+
frame_0.contentWindow.postMessage({
|
|
1321
|
+
protocol: canvasProtocol,
|
|
1322
|
+
id: id_0,
|
|
1323
|
+
token,
|
|
1324
|
+
viewportHeight: current.bp.height,
|
|
1325
|
+
type,
|
|
1326
|
+
configuration: current.configuration,
|
|
1327
|
+
revision: revision.current,
|
|
1328
|
+
dark: current.dark,
|
|
1329
|
+
interact: current.interact
|
|
1330
|
+
}, new URL(frame_0.src).origin);
|
|
1331
|
+
} catch {
|
|
1332
|
+
setNotice("This configuration could not be sent to the page. Check for non-serializable values.");
|
|
1333
|
+
}
|
|
1334
|
+
}, [token]);
|
|
1335
|
+
const focusPage = useCallback((id_1) => {
|
|
1336
|
+
const node_0 = nodeMap.get(id_1), view = viewport.current;
|
|
1337
|
+
if (!node_0 || !view) return;
|
|
1338
|
+
setSelected(id_1);
|
|
1339
|
+
camera.current.zoom = Math.min(1, Math.max(.08, (view.clientWidth - 100) / node_0.width));
|
|
1340
|
+
camera.current.x = 40 - node_0.x * camera.current.zoom;
|
|
1341
|
+
camera.current.y = 40 - node_0.y * camera.current.zoom;
|
|
1342
|
+
paint();
|
|
1343
|
+
}, [nodeMap, paint]);
|
|
1344
|
+
const focusRef = useRef(focusPage);
|
|
1345
|
+
focusRef.current = focusPage;
|
|
1346
|
+
const matchHref = useCallback((href) => matchCanvasPageHref(latest.current.catalog, href, window.location.href), []);
|
|
1347
|
+
useEffect(() => {
|
|
1348
|
+
const queue = /* @__PURE__ */ new Map();
|
|
1349
|
+
let batch = 0;
|
|
1350
|
+
const flush = () => {
|
|
1351
|
+
batch = 0;
|
|
1352
|
+
const changes = Array.from(queue);
|
|
1353
|
+
queue.clear();
|
|
1354
|
+
setRuntime((current_0) => {
|
|
1355
|
+
const next = { ...current_0 };
|
|
1356
|
+
let changed = false;
|
|
1357
|
+
for (const [key, item_0] of changes) {
|
|
1358
|
+
if (frames.current.get(item_0.id) !== item_0.frame || item_0.frame.contentWindow !== item_0.source) continue;
|
|
1359
|
+
next[key] = {
|
|
1360
|
+
...next[key],
|
|
1361
|
+
...item_0.change
|
|
1362
|
+
};
|
|
1363
|
+
changed = true;
|
|
1364
|
+
}
|
|
1365
|
+
return changed ? next : current_0;
|
|
1366
|
+
});
|
|
1367
|
+
};
|
|
1368
|
+
const receive = (event) => {
|
|
1369
|
+
const packet = event.data;
|
|
1370
|
+
if (packet?.protocol !== "povio-app-canvas-v1" || packet.token !== token || typeof packet.id !== "string") return;
|
|
1371
|
+
const frame_1 = frames.current.get(packet.id);
|
|
1372
|
+
if (!frame_1 || event.source !== frame_1.contentWindow || event.origin !== new URL(frame_1.src).origin) return;
|
|
1373
|
+
const key_0 = `${latest.current.bp.id}:${packet.id}:${latest.current.catalog.find((page_2) => page_2.id === packet.id)?.href ?? ""}`;
|
|
1374
|
+
let change = {};
|
|
1375
|
+
if (packet.type === "ready") {
|
|
1376
|
+
startupRef.current.complete(startupRef.current.keys.get(packet.id));
|
|
1377
|
+
change = {
|
|
1378
|
+
ready: true,
|
|
1379
|
+
href: packet.href,
|
|
1380
|
+
timedOut: false
|
|
1381
|
+
};
|
|
1382
|
+
send(packet.id);
|
|
1383
|
+
send(packet.id, "mode");
|
|
1384
|
+
}
|
|
1385
|
+
if (packet.type === "height" && typeof packet.height === "number" && Number.isFinite(packet.height) && packet.height > 0) change.height = packet.height;
|
|
1386
|
+
if (packet.type === "warning" && typeof packet.warning === "string") change.warning = packet.warning;
|
|
1387
|
+
if (packet.type === "ack" && typeof packet.revision === "number") change.revision = packet.revision;
|
|
1388
|
+
if (packet.type === "navigate" && typeof packet.href === "string") {
|
|
1389
|
+
const target = matchHref(packet.href);
|
|
1390
|
+
if (target) focusRef.current(target.id);
|
|
1391
|
+
else setNotice(`Navigation target is not in this canvas: ${packet.href}`);
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
if (!Object.keys(change).length) return;
|
|
1395
|
+
const pending = queue.get(key_0);
|
|
1396
|
+
const previous = pending?.frame === frame_1 && pending.source === event.source ? pending.change : {};
|
|
1397
|
+
queue.set(key_0, {
|
|
1398
|
+
id: packet.id,
|
|
1399
|
+
frame: frame_1,
|
|
1400
|
+
source: event.source,
|
|
1401
|
+
change: {
|
|
1402
|
+
...previous,
|
|
1403
|
+
...change
|
|
1404
|
+
}
|
|
1405
|
+
});
|
|
1406
|
+
if (!batch) batch = requestAnimationFrame(flush);
|
|
1407
|
+
};
|
|
1408
|
+
window.addEventListener("message", receive);
|
|
1409
|
+
return () => {
|
|
1410
|
+
window.removeEventListener("message", receive);
|
|
1411
|
+
cancelAnimationFrame(batch);
|
|
1412
|
+
};
|
|
1413
|
+
}, [
|
|
1414
|
+
token,
|
|
1415
|
+
send,
|
|
1416
|
+
matchHref
|
|
1417
|
+
]);
|
|
1418
|
+
useEffect(() => {
|
|
1419
|
+
const frame_2 = requestAnimationFrame(() => {
|
|
1420
|
+
revision.current++;
|
|
1421
|
+
for (const id_2 of frames.current.keys()) send(id_2);
|
|
1422
|
+
});
|
|
1423
|
+
return () => cancelAnimationFrame(frame_2);
|
|
1424
|
+
}, [
|
|
1425
|
+
configuration.active,
|
|
1426
|
+
dark,
|
|
1427
|
+
send
|
|
1428
|
+
]);
|
|
1429
|
+
useEffect(() => {
|
|
1430
|
+
for (const id_3 of frames.current.keys()) send(id_3, "mode");
|
|
1431
|
+
}, [interact, send]);
|
|
1432
|
+
useEffect(() => {
|
|
1433
|
+
try {
|
|
1434
|
+
localStorage.setItem(storageKey, JSON.stringify(manifest));
|
|
1435
|
+
} catch {
|
|
1436
|
+
setNotice("Browser storage is unavailable; use Export layout to save this canvas.");
|
|
1437
|
+
}
|
|
1438
|
+
}, [manifest, storageKey]);
|
|
1439
|
+
useEffect(() => {
|
|
1440
|
+
paint();
|
|
1441
|
+
const element = viewport.current;
|
|
1442
|
+
if (!element) return;
|
|
1443
|
+
const wheel = (event_0) => {
|
|
1444
|
+
event_0.preventDefault();
|
|
1445
|
+
if (event_0.ctrlKey || event_0.metaKey) {
|
|
1446
|
+
const box = element.getBoundingClientRect();
|
|
1447
|
+
const x = event_0.clientX - box.left, y = event_0.clientY - box.top;
|
|
1448
|
+
const previous_0 = camera.current.zoom;
|
|
1449
|
+
const unit = event_0.deltaMode === 1 ? 16 : event_0.deltaMode === 2 ? element.clientHeight : 1;
|
|
1450
|
+
const next_0 = Math.max(.005, Math.min(2, previous_0 * Math.exp(-event_0.deltaY * unit * .01)));
|
|
1451
|
+
camera.current.x = x - (x - camera.current.x) * next_0 / previous_0;
|
|
1452
|
+
camera.current.y = y - (y - camera.current.y) * next_0 / previous_0;
|
|
1453
|
+
camera.current.zoom = next_0;
|
|
1454
|
+
} else {
|
|
1455
|
+
camera.current.x -= event_0.deltaX;
|
|
1456
|
+
camera.current.y -= event_0.deltaY;
|
|
1457
|
+
}
|
|
1458
|
+
schedulePaint();
|
|
1459
|
+
};
|
|
1460
|
+
element.addEventListener("wheel", wheel, { passive: false });
|
|
1461
|
+
return () => {
|
|
1462
|
+
element.removeEventListener("wheel", wheel);
|
|
1463
|
+
if (pendingCamera.current !== null) cancelAnimationFrame(pendingCamera.current);
|
|
1464
|
+
pendingCamera.current = null;
|
|
1465
|
+
};
|
|
1466
|
+
}, [paint, schedulePaint]);
|
|
1467
|
+
const fit = () => {
|
|
1468
|
+
if (!nodes.length || !viewport.current) return;
|
|
1469
|
+
const left = Math.min(...nodes.map((n) => n.x)), top = Math.min(...nodes.map((n_0) => n_0.y));
|
|
1470
|
+
const width = Math.max(...nodes.map((n_1) => n_1.x + n_1.width)) - left, height = Math.max(...nodes.map((n_2) => n_2.y + n_2.height + 48)) - top;
|
|
1471
|
+
camera.current.zoom = Math.max(.005, Math.min(1, (viewport.current.clientWidth - 80) / width, (viewport.current.clientHeight - 80) / height));
|
|
1472
|
+
camera.current.x = 40 - left * camera.current.zoom;
|
|
1473
|
+
camera.current.y = 40 - top * camera.current.zoom;
|
|
1474
|
+
paint();
|
|
1475
|
+
};
|
|
1476
|
+
const zoomTo = (value) => {
|
|
1477
|
+
const view_0 = viewport.current;
|
|
1478
|
+
if (!view_0) return;
|
|
1479
|
+
const previous_1 = camera.current.zoom, next_1 = Math.max(.005, Math.min(2, value));
|
|
1480
|
+
camera.current.x = view_0.clientWidth / 2 - (view_0.clientWidth / 2 - camera.current.x) * next_1 / previous_1;
|
|
1481
|
+
camera.current.y = view_0.clientHeight / 2 - (view_0.clientHeight / 2 - camera.current.y) * next_1 / previous_1;
|
|
1482
|
+
camera.current.zoom = next_1;
|
|
1483
|
+
paint();
|
|
1484
|
+
};
|
|
1485
|
+
const stepZoom = (direction) => {
|
|
1486
|
+
const stops = [
|
|
1487
|
+
.005,
|
|
1488
|
+
.01,
|
|
1489
|
+
.02,
|
|
1490
|
+
.05,
|
|
1491
|
+
.1,
|
|
1492
|
+
.25,
|
|
1493
|
+
.5,
|
|
1494
|
+
.75,
|
|
1495
|
+
1,
|
|
1496
|
+
1.5,
|
|
1497
|
+
2
|
|
1498
|
+
];
|
|
1499
|
+
const current_1 = camera.current.zoom;
|
|
1500
|
+
const next_2 = direction === 1 ? stops.find((value_0) => value_0 > current_1 + 1e-4) : [...stops].reverse().find((value_1) => value_1 < current_1 - 1e-4);
|
|
1501
|
+
if (next_2 !== void 0) zoomTo(next_2);
|
|
1502
|
+
};
|
|
1503
|
+
const startPan = (event_1) => {
|
|
1504
|
+
if (inspectMode && event_1.target.closest(".povio-canvas-frame-cover")) return;
|
|
1505
|
+
if (event_1.button !== 0 || event_1.target.closest("button:not(.povio-canvas-frame-cover),input,select,.povio-canvas-node-header")) return;
|
|
1506
|
+
event_1.currentTarget.setPointerCapture(event_1.pointerId);
|
|
1507
|
+
pointer.current = {
|
|
1508
|
+
id: event_1.pointerId,
|
|
1509
|
+
x: event_1.clientX,
|
|
1510
|
+
y: event_1.clientY,
|
|
1511
|
+
startX: camera.current.x,
|
|
1512
|
+
startY: camera.current.y
|
|
1513
|
+
};
|
|
1514
|
+
};
|
|
1515
|
+
const move = (event_2) => {
|
|
1516
|
+
const current_2 = pointer.current;
|
|
1517
|
+
if (!current_2 || current_2.id !== event_2.pointerId) return;
|
|
1518
|
+
const dx = event_2.clientX - current_2.x, dy = event_2.clientY - current_2.y;
|
|
1519
|
+
if (current_2.node) {
|
|
1520
|
+
const element_0 = world.current?.querySelector(`[data-node-index="${nodes.findIndex((n_3) => n_3.id === current_2.node)}"]`);
|
|
1521
|
+
if (element_0) {
|
|
1522
|
+
element_0.style.left = `${current_2.nodeX + dx / camera.current.zoom}px`;
|
|
1523
|
+
element_0.style.top = `${current_2.nodeY + dy / camera.current.zoom}px`;
|
|
1524
|
+
}
|
|
1525
|
+
} else {
|
|
1526
|
+
camera.current.x = current_2.startX + dx;
|
|
1527
|
+
camera.current.y = current_2.startY + dy;
|
|
1528
|
+
schedulePaint();
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1531
|
+
const end = (event_3) => {
|
|
1532
|
+
const current_3 = pointer.current;
|
|
1533
|
+
if (!current_3 || current_3.id !== event_3.pointerId) return;
|
|
1534
|
+
if (current_3.node) {
|
|
1535
|
+
const position = {
|
|
1536
|
+
x: current_3.nodeX + (event_3.clientX - current_3.x) / camera.current.zoom,
|
|
1537
|
+
y: current_3.nodeY + (event_3.clientY - current_3.y) / camera.current.zoom
|
|
1538
|
+
};
|
|
1539
|
+
setManifest((m) => ({
|
|
1540
|
+
...m,
|
|
1541
|
+
positions: {
|
|
1542
|
+
...m.positions,
|
|
1543
|
+
[bp.id]: {
|
|
1544
|
+
...m.positions[bp.id],
|
|
1545
|
+
[current_3.node]: position
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
}));
|
|
1549
|
+
}
|
|
1550
|
+
pointer.current = null;
|
|
1551
|
+
if (event_3.currentTarget.hasPointerCapture(event_3.pointerId)) event_3.currentTarget.releasePointerCapture(event_3.pointerId);
|
|
1552
|
+
};
|
|
1553
|
+
const onLoad = useCallback((id_4, frame_3) => {
|
|
1554
|
+
if (frames.current.get(id_4) !== frame_3) return;
|
|
1555
|
+
startupRef.current.complete(startupRef.current.keys.get(id_4));
|
|
1556
|
+
send(id_4);
|
|
1557
|
+
send(id_4, "mode");
|
|
1558
|
+
}, [send]);
|
|
1559
|
+
useEffect(() => {
|
|
1560
|
+
const timer = setTimeout(() => setRuntime((current_4) => {
|
|
1561
|
+
const next_3 = { ...current_4 };
|
|
1562
|
+
for (const page_3 of latest.current.catalog) {
|
|
1563
|
+
const key_1 = `${latest.current.bp.id}:${page_3.id}:${page_3.href ?? ""}`;
|
|
1564
|
+
if (page_3.href && frames.current.has(page_3.id) && !next_3[key_1]?.ready) next_3[key_1] = {
|
|
1565
|
+
...next_3[key_1],
|
|
1566
|
+
timedOut: true
|
|
1567
|
+
};
|
|
1568
|
+
}
|
|
1569
|
+
return next_3;
|
|
1570
|
+
}), 15e3);
|
|
1571
|
+
return () => clearTimeout(timer);
|
|
1572
|
+
}, [
|
|
1573
|
+
bp.id,
|
|
1574
|
+
catalog,
|
|
1575
|
+
reload,
|
|
1576
|
+
startup.admitted
|
|
1577
|
+
]);
|
|
1578
|
+
const exportLayout = () => {
|
|
1579
|
+
const url = URL.createObjectURL(new Blob([JSON.stringify(manifest, null, 2)], { type: "application/json" }));
|
|
1580
|
+
const a = document.createElement("a");
|
|
1581
|
+
a.href = url;
|
|
1582
|
+
a.download = "povio-app-canvas.json";
|
|
1583
|
+
a.click();
|
|
1584
|
+
setTimeout(() => URL.revokeObjectURL(url), 1e3);
|
|
1585
|
+
};
|
|
1586
|
+
const width_0 = Math.max(1, ...nodes.map((n_4) => n_4.x + n_4.width + 80)), height_0 = Math.max(1, ...nodes.map((n_5) => n_5.y + n_5.height + 128));
|
|
1587
|
+
const ready = nodes.filter((n_6) => runtime[`${bp.id}:${n_6.id}:${n_6.href ?? ""}`]?.ready).length;
|
|
1588
|
+
return /* @__PURE__ */ jsxs("section", {
|
|
1589
|
+
className: "povio-app-canvas",
|
|
1590
|
+
"data-mobile-panel": mobilePanel ?? "none",
|
|
1591
|
+
"data-inspecting": inspectMode,
|
|
1592
|
+
"aria-label": "App canvas",
|
|
1593
|
+
"data-ready-count": ready,
|
|
1594
|
+
"data-page-count": nodes.length,
|
|
1595
|
+
"data-camera-revision": "0",
|
|
1596
|
+
children: [
|
|
1597
|
+
/* @__PURE__ */ jsx("input", {
|
|
1598
|
+
ref: importInput,
|
|
1599
|
+
type: "file",
|
|
1600
|
+
accept: "application/json,.json",
|
|
1601
|
+
hidden: true,
|
|
1602
|
+
"aria-label": "Import canvas layout",
|
|
1603
|
+
onChange: async (event_4) => {
|
|
1604
|
+
const file = event_4.currentTarget.files?.[0];
|
|
1605
|
+
event_4.currentTarget.value = "";
|
|
1606
|
+
if (!file) return;
|
|
1607
|
+
try {
|
|
1608
|
+
const raw = JSON.parse(await file.text());
|
|
1609
|
+
if (raw?.version !== 1) throw new Error("version");
|
|
1610
|
+
setManifest(parseCanvasManifest(raw));
|
|
1611
|
+
setRuntime({});
|
|
1612
|
+
setDocumentGeneration((value_2) => value_2 + 1);
|
|
1613
|
+
setNotice("Layout imported.");
|
|
1614
|
+
} catch {
|
|
1615
|
+
setNotice("Choose a valid version 1 canvas layout JSON file.");
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
}),
|
|
1619
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1620
|
+
className: "povio-canvas-body",
|
|
1621
|
+
children: [
|
|
1622
|
+
/* @__PURE__ */ jsxs("aside", {
|
|
1623
|
+
className: "povio-canvas-catalog",
|
|
1624
|
+
"aria-label": "Page catalog",
|
|
1625
|
+
children: [
|
|
1626
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1627
|
+
className: "povio-canvas-brand",
|
|
1628
|
+
children: [/* @__PURE__ */ jsx("strong", { children: "Pages" }), /* @__PURE__ */ jsx("span", {
|
|
1629
|
+
className: "povio-canvas-page-count",
|
|
1630
|
+
children: nodes.length
|
|
1631
|
+
})]
|
|
1632
|
+
}),
|
|
1633
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1634
|
+
className: "povio-canvas-search",
|
|
1635
|
+
children: [
|
|
1636
|
+
/* @__PURE__ */ jsx(Search, { size: 14 }),
|
|
1637
|
+
/* @__PURE__ */ jsx("input", {
|
|
1638
|
+
"aria-label": "Find page",
|
|
1639
|
+
placeholder: "Find page…",
|
|
1640
|
+
value: query,
|
|
1641
|
+
onChange: (event_5) => setQuery(event_5.target.value)
|
|
1642
|
+
}),
|
|
1643
|
+
/* @__PURE__ */ jsx("button", {
|
|
1644
|
+
className: "povio-canvas-panel-close",
|
|
1645
|
+
"aria-label": "Close pages panel",
|
|
1646
|
+
onClick: () => setMobilePanel(null),
|
|
1647
|
+
children: /* @__PURE__ */ jsx(X, { size: 16 })
|
|
1648
|
+
})
|
|
1649
|
+
]
|
|
1650
|
+
}),
|
|
1651
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1652
|
+
className: "povio-canvas-page-list",
|
|
1653
|
+
children: [nodes.filter((node_1) => `${node_1.title} ${node_1.path}`.toLowerCase().includes(query.toLowerCase())).map((node_2) => /* @__PURE__ */ jsxs("button", {
|
|
1654
|
+
"aria-pressed": selected === node_2.id,
|
|
1655
|
+
onClick: () => {
|
|
1656
|
+
focusPage(node_2.id);
|
|
1657
|
+
setMobilePanel(null);
|
|
1658
|
+
},
|
|
1659
|
+
children: [/* @__PURE__ */ jsx(Frame, { size: 15 }), /* @__PURE__ */ jsxs("span", {
|
|
1660
|
+
className: "povio-canvas-page-label",
|
|
1661
|
+
children: [/* @__PURE__ */ jsx("strong", { children: node_2.title }), node_2.kind === "unresolved" && /* @__PURE__ */ jsx("small", { children: "Needs parameters" })]
|
|
1662
|
+
})]
|
|
1663
|
+
}, node_2.id)), selected && nodeMap.get(selected)?.href && /* @__PURE__ */ jsx(PageLayers, {
|
|
1664
|
+
frame: frames.current.get(selected) ?? null,
|
|
1665
|
+
title: nodeMap.get(selected).title,
|
|
1666
|
+
selected: inspector.selection?.pageId === selected ? inspector.selection.element : void 0,
|
|
1667
|
+
onSelect: (element_1) => {
|
|
1668
|
+
setInteract(false);
|
|
1669
|
+
setInspectMode(true);
|
|
1670
|
+
inspector.selectElement(selected, element_1);
|
|
1671
|
+
setMobilePanel(null);
|
|
1672
|
+
requestAnimationFrame(() => {
|
|
1673
|
+
const node_3 = nodeMap.get(selected), view_1 = viewport.current;
|
|
1674
|
+
if (!node_3 || !view_1 || !element_1.isConnected) return;
|
|
1675
|
+
const rect = element_1.getBoundingClientRect();
|
|
1676
|
+
camera.current.x = view_1.clientWidth / 2 - (node_3.x + rect.left + rect.width / 2) * camera.current.zoom;
|
|
1677
|
+
camera.current.y = view_1.clientHeight / 2 - (node_3.y + 48 + rect.top + rect.height / 2) * camera.current.zoom;
|
|
1678
|
+
paint();
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
}, `${bp.id}:${documentGeneration}:${reload[selected] ?? 0}:${selected}:${nodeMap.get(selected)?.href}`)]
|
|
1682
|
+
}),
|
|
1683
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1684
|
+
className: "povio-canvas-sidebar-actions",
|
|
1685
|
+
children: [
|
|
1686
|
+
/* @__PURE__ */ jsx("button", {
|
|
1687
|
+
className: "povio-canvas-file-action",
|
|
1688
|
+
onClick: exportLayout,
|
|
1689
|
+
"aria-label": "Export layout",
|
|
1690
|
+
title: "Export layout",
|
|
1691
|
+
children: /* @__PURE__ */ jsx(Download, { size: 16 })
|
|
1692
|
+
}),
|
|
1693
|
+
/* @__PURE__ */ jsx("button", {
|
|
1694
|
+
className: "povio-canvas-file-action",
|
|
1695
|
+
onClick: () => importInput.current?.click(),
|
|
1696
|
+
"aria-label": "Import layout",
|
|
1697
|
+
title: "Import layout",
|
|
1698
|
+
children: /* @__PURE__ */ jsx(Upload, { size: 16 })
|
|
1699
|
+
}),
|
|
1700
|
+
/* @__PURE__ */ jsxs("span", {
|
|
1701
|
+
className: "povio-canvas-ready",
|
|
1702
|
+
role: "status",
|
|
1703
|
+
children: [
|
|
1704
|
+
/* @__PURE__ */ jsx("i", {}),
|
|
1705
|
+
ready,
|
|
1706
|
+
"/",
|
|
1707
|
+
nodes.length,
|
|
1708
|
+
" pages ready"
|
|
1709
|
+
]
|
|
1710
|
+
})
|
|
1711
|
+
]
|
|
1712
|
+
})
|
|
1713
|
+
]
|
|
1714
|
+
}),
|
|
1715
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1716
|
+
ref: viewport,
|
|
1717
|
+
className: "povio-canvas-viewport",
|
|
1718
|
+
onKeyDownCapture: (event_6) => {
|
|
1719
|
+
if (inspectMode && event_6.key === "Escape") {
|
|
1720
|
+
event_6.preventDefault();
|
|
1721
|
+
inspector.clear();
|
|
1722
|
+
}
|
|
1723
|
+
},
|
|
1724
|
+
onPointerDown: startPan,
|
|
1725
|
+
onPointerMove: move,
|
|
1726
|
+
onPointerUp: end,
|
|
1727
|
+
onPointerCancel: (event_7) => {
|
|
1728
|
+
const current_5 = pointer.current;
|
|
1729
|
+
if (current_5?.node) {
|
|
1730
|
+
const element_2 = world.current?.querySelector(`[data-node-index="${nodes.findIndex((node_4) => node_4.id === current_5.node)}"]`);
|
|
1731
|
+
if (element_2) {
|
|
1732
|
+
element_2.style.left = `${current_5.nodeX}px`;
|
|
1733
|
+
element_2.style.top = `${current_5.nodeY}px`;
|
|
1734
|
+
}
|
|
1735
|
+
}
|
|
1736
|
+
pointer.current = null;
|
|
1737
|
+
if (event_7.currentTarget.hasPointerCapture(event_7.pointerId)) event_7.currentTarget.releasePointerCapture(event_7.pointerId);
|
|
1738
|
+
},
|
|
1739
|
+
children: [
|
|
1740
|
+
/* @__PURE__ */ jsx("div", {
|
|
1741
|
+
className: "povio-canvas-mobile-tools",
|
|
1742
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
1743
|
+
"aria-label": "Toggle pages panel",
|
|
1744
|
+
"aria-pressed": mobilePanel === "pages",
|
|
1745
|
+
onClick: () => setMobilePanel(mobilePanel === "pages" ? null : "pages"),
|
|
1746
|
+
children: /* @__PURE__ */ jsx(PanelLeft, { size: 18 })
|
|
1747
|
+
})
|
|
1748
|
+
}),
|
|
1749
|
+
/* @__PURE__ */ jsx("div", {
|
|
1750
|
+
ref: world,
|
|
1751
|
+
className: "povio-canvas-world",
|
|
1752
|
+
style: {
|
|
1753
|
+
width: width_0,
|
|
1754
|
+
height: height_0
|
|
1755
|
+
},
|
|
1756
|
+
children: nodes.map((node_5, index) => {
|
|
1757
|
+
const state = runtime[`${bp.id}:${node_5.id}:${node_5.href ?? ""}`] ?? {};
|
|
1758
|
+
let source = "";
|
|
1759
|
+
let originError = false;
|
|
1760
|
+
if (node_5.href) try {
|
|
1761
|
+
const url_0 = new URL(frameUrl?.(node_5) ?? node_5.href, window.location.href);
|
|
1762
|
+
if (url_0.origin !== window.location.origin) throw new Error("same origin required");
|
|
1763
|
+
url_0.searchParams.delete("povioCanvas");
|
|
1764
|
+
url_0.searchParams.delete("__povio_canvas_frame");
|
|
1765
|
+
url_0.searchParams.delete("__povio_canvas_token");
|
|
1766
|
+
url_0.searchParams.delete("__povio_canvas_height");
|
|
1767
|
+
source = url_0.href;
|
|
1768
|
+
} catch {
|
|
1769
|
+
originError = true;
|
|
1770
|
+
}
|
|
1771
|
+
const redirected = state.href && node_5.href && new URL(state.href, window.location.href).pathname !== new URL(node_5.href, window.location.href).pathname;
|
|
1772
|
+
return /* @__PURE__ */ jsxs("article", {
|
|
1773
|
+
"data-node-index": index,
|
|
1774
|
+
"data-page-id": node_5.id,
|
|
1775
|
+
className: `povio-canvas-node${selected === node_5.id ? " is-selected" : ""}`,
|
|
1776
|
+
style: {
|
|
1777
|
+
left: node_5.x,
|
|
1778
|
+
top: node_5.y,
|
|
1779
|
+
width: node_5.width
|
|
1780
|
+
},
|
|
1781
|
+
children: [
|
|
1782
|
+
/* @__PURE__ */ jsxs("header", {
|
|
1783
|
+
className: "povio-canvas-node-header",
|
|
1784
|
+
onPointerDown: (event_8) => {
|
|
1785
|
+
if (event_8.button !== 0 || event_8.target.closest("button")) return;
|
|
1786
|
+
event_8.stopPropagation();
|
|
1787
|
+
viewport.current?.setPointerCapture(event_8.pointerId);
|
|
1788
|
+
pointer.current = {
|
|
1789
|
+
id: event_8.pointerId,
|
|
1790
|
+
x: event_8.clientX,
|
|
1791
|
+
y: event_8.clientY,
|
|
1792
|
+
startX: 0,
|
|
1793
|
+
startY: 0,
|
|
1794
|
+
node: node_5.id,
|
|
1795
|
+
nodeX: node_5.x,
|
|
1796
|
+
nodeY: node_5.y
|
|
1797
|
+
};
|
|
1798
|
+
setSelected(node_5.id);
|
|
1799
|
+
},
|
|
1800
|
+
children: [
|
|
1801
|
+
/* @__PURE__ */ jsx("strong", { children: node_5.title }),
|
|
1802
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1803
|
+
bp.width,
|
|
1804
|
+
" × ",
|
|
1805
|
+
Math.round(node_5.height).toLocaleString()
|
|
1806
|
+
] }),
|
|
1807
|
+
/* @__PURE__ */ jsx("button", {
|
|
1808
|
+
"aria-label": `Focus ${node_5.title}`,
|
|
1809
|
+
onClick: () => focusPage(node_5.id),
|
|
1810
|
+
children: "↗"
|
|
1811
|
+
}),
|
|
1812
|
+
/* @__PURE__ */ jsx("button", {
|
|
1813
|
+
"aria-label": `Reload ${node_5.title}`,
|
|
1814
|
+
onClick: () => {
|
|
1815
|
+
setRuntime((r) => ({
|
|
1816
|
+
...r,
|
|
1817
|
+
[`${bp.id}:${node_5.id}:${node_5.href ?? ""}`]: {}
|
|
1818
|
+
}));
|
|
1819
|
+
setReload((r_0) => ({
|
|
1820
|
+
...r_0,
|
|
1821
|
+
[node_5.id]: (r_0[node_5.id] ?? 0) + 1
|
|
1822
|
+
}));
|
|
1823
|
+
},
|
|
1824
|
+
children: "↻"
|
|
1825
|
+
})
|
|
1826
|
+
]
|
|
1827
|
+
}),
|
|
1828
|
+
!node_5.href ? /* @__PURE__ */ jsxs("form", {
|
|
1829
|
+
className: "povio-canvas-unresolved",
|
|
1830
|
+
onSubmit: (event_9) => {
|
|
1831
|
+
event_9.preventDefault();
|
|
1832
|
+
const form = new FormData(event_9.currentTarget);
|
|
1833
|
+
const enteredParams = Object.fromEntries(node_5.missingParams.map((key_2) => {
|
|
1834
|
+
const value_3 = form.get(key_2);
|
|
1835
|
+
return [key_2, typeof value_3 === "string" ? value_3 : ""];
|
|
1836
|
+
}));
|
|
1837
|
+
setManifest((m_0) => ({
|
|
1838
|
+
...m_0,
|
|
1839
|
+
examples: {
|
|
1840
|
+
...m_0.examples,
|
|
1841
|
+
[node_5.id]: {
|
|
1842
|
+
...examples[node_5.id],
|
|
1843
|
+
...m_0.examples[node_5.id],
|
|
1844
|
+
params: {
|
|
1845
|
+
...examples[node_5.id]?.params,
|
|
1846
|
+
...m_0.examples[node_5.id]?.params,
|
|
1847
|
+
...enteredParams
|
|
1848
|
+
}
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
}));
|
|
1852
|
+
},
|
|
1853
|
+
children: [
|
|
1854
|
+
/* @__PURE__ */ jsx("strong", { children: "Route needs parameters" }),
|
|
1855
|
+
/* @__PURE__ */ jsx("code", { children: node_5.path }),
|
|
1856
|
+
node_5.missingParams.map((key_3) => /* @__PURE__ */ jsxs("label", { children: [key_3, /* @__PURE__ */ jsx("input", {
|
|
1857
|
+
name: key_3,
|
|
1858
|
+
required: true
|
|
1859
|
+
})] }, key_3)),
|
|
1860
|
+
/* @__PURE__ */ jsx("button", { children: "Render page" })
|
|
1861
|
+
]
|
|
1862
|
+
}) : originError ? /* @__PURE__ */ jsx("p", {
|
|
1863
|
+
className: "povio-canvas-unresolved",
|
|
1864
|
+
children: "Canvas pages must use the same origin."
|
|
1865
|
+
}) : !startup.admitted.has(startupKeys.get(node_5.id) ?? "") ? /* @__PURE__ */ jsx("div", {
|
|
1866
|
+
className: "povio-canvas-unresolved",
|
|
1867
|
+
role: "status",
|
|
1868
|
+
children: "Waiting to load page…"
|
|
1869
|
+
}) : /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
1870
|
+
/* @__PURE__ */ jsx(PageFrame, {
|
|
1871
|
+
id: node_5.id,
|
|
1872
|
+
interact,
|
|
1873
|
+
inspect: inspectMode,
|
|
1874
|
+
src: source,
|
|
1875
|
+
width: bp.width,
|
|
1876
|
+
height: node_5.height,
|
|
1877
|
+
name: `${canvasProtocol}:${JSON.stringify({
|
|
1878
|
+
id: node_5.id,
|
|
1879
|
+
token,
|
|
1880
|
+
viewportHeight: bp.height
|
|
1881
|
+
})}`,
|
|
1882
|
+
onLoad,
|
|
1883
|
+
register
|
|
1884
|
+
}, `${documentGeneration}:${bp.id}:${source}:${reload[node_5.id] ?? 0}`),
|
|
1885
|
+
!interact && /* @__PURE__ */ jsx("button", {
|
|
1886
|
+
type: "button",
|
|
1887
|
+
className: "povio-canvas-frame-cover",
|
|
1888
|
+
tabIndex: 0,
|
|
1889
|
+
"aria-label": `Select ${node_5.title}`,
|
|
1890
|
+
onKeyDown: (event_10) => {
|
|
1891
|
+
if (event_10.key === "Enter" || event_10.key === " ") {
|
|
1892
|
+
event_10.preventDefault();
|
|
1893
|
+
setSelected(node_5.id);
|
|
1894
|
+
}
|
|
1895
|
+
},
|
|
1896
|
+
onPointerMove: (event_11) => {
|
|
1897
|
+
const frame_4 = frames.current.get(node_5.id);
|
|
1898
|
+
if (frame_4) inspector.hover(node_5.id, frame_4, event_11.clientX, event_11.clientY);
|
|
1899
|
+
},
|
|
1900
|
+
onPointerDown: (event_12) => {
|
|
1901
|
+
setSelected(node_5.id);
|
|
1902
|
+
if (inspectMode) {
|
|
1903
|
+
const frame_5 = frames.current.get(node_5.id);
|
|
1904
|
+
if (frame_5) inspector.select(node_5.id, frame_5, event_12.clientX, event_12.clientY);
|
|
1905
|
+
}
|
|
1906
|
+
},
|
|
1907
|
+
onClick: () => setSelected(node_5.id)
|
|
1908
|
+
}),
|
|
1909
|
+
inspectMode && inspector.selection?.pageId === node_5.id && inspector.measurement && /* @__PURE__ */ jsx(CanvasInspectionOverlay, {
|
|
1910
|
+
measurement: inspector.measurement,
|
|
1911
|
+
width: node_5.width
|
|
1912
|
+
})
|
|
1913
|
+
] }),
|
|
1914
|
+
(redirected || state.warning || state.timedOut) && /* @__PURE__ */ jsxs("div", {
|
|
1915
|
+
className: "povio-canvas-page-note",
|
|
1916
|
+
children: [
|
|
1917
|
+
redirected ? `Rendered ${state.href} (route redirected). ` : "",
|
|
1918
|
+
state.warning,
|
|
1919
|
+
state.timedOut && !state.ready && startup.admitted.has(startupKeys.get(node_5.id) ?? "") ? " Frame bridge has not connected. Check access or page errors." : ""
|
|
1920
|
+
]
|
|
1921
|
+
})
|
|
1922
|
+
]
|
|
1923
|
+
}, node_5.id);
|
|
1924
|
+
})
|
|
1925
|
+
}),
|
|
1926
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1927
|
+
className: "povio-canvas-tool-dock",
|
|
1928
|
+
children: [
|
|
1929
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1930
|
+
className: "povio-canvas-mode-switch",
|
|
1931
|
+
role: "group",
|
|
1932
|
+
"aria-label": "Canvas mode",
|
|
1933
|
+
children: [
|
|
1934
|
+
/* @__PURE__ */ jsxs("button", {
|
|
1935
|
+
"aria-label": "Inspect layouts",
|
|
1936
|
+
title: "Inspect layouts",
|
|
1937
|
+
"aria-pressed": inspectMode,
|
|
1938
|
+
onClick: () => {
|
|
1939
|
+
setInteract(false);
|
|
1940
|
+
setInspectMode(true);
|
|
1941
|
+
},
|
|
1942
|
+
children: [/* @__PURE__ */ jsx(ScanLine, { size: 18 }), /* @__PURE__ */ jsx("span", { children: "Inspect" })]
|
|
1943
|
+
}),
|
|
1944
|
+
/* @__PURE__ */ jsxs("button", {
|
|
1945
|
+
"aria-label": "Pan",
|
|
1946
|
+
title: "Pan",
|
|
1947
|
+
"aria-pressed": !inspectMode && !interact,
|
|
1948
|
+
onClick: () => {
|
|
1949
|
+
setInspectMode(false);
|
|
1950
|
+
setInteract(false);
|
|
1951
|
+
},
|
|
1952
|
+
children: [/* @__PURE__ */ jsx(Hand, { size: 18 }), /* @__PURE__ */ jsx("span", { children: "Pan" })]
|
|
1953
|
+
}),
|
|
1954
|
+
/* @__PURE__ */ jsxs("button", {
|
|
1955
|
+
"aria-label": "Interact",
|
|
1956
|
+
title: "Interact with pages",
|
|
1957
|
+
"aria-pressed": interact,
|
|
1958
|
+
onClick: () => {
|
|
1959
|
+
setInspectMode(false);
|
|
1960
|
+
setInteract(true);
|
|
1961
|
+
},
|
|
1962
|
+
children: [/* @__PURE__ */ jsx(MousePointer2, { size: 18 }), /* @__PURE__ */ jsx("span", { children: "Interact" })]
|
|
1963
|
+
})
|
|
1964
|
+
]
|
|
1965
|
+
}),
|
|
1966
|
+
/* @__PURE__ */ jsx("span", { className: "povio-canvas-dock-divider" }),
|
|
1967
|
+
/* @__PURE__ */ jsxs("button", {
|
|
1968
|
+
"aria-label": "Auto layout",
|
|
1969
|
+
onClick: () => setManifest((m_1) => ({
|
|
1970
|
+
...m_1,
|
|
1971
|
+
positions: {
|
|
1972
|
+
...m_1.positions,
|
|
1973
|
+
[bp.id]: {}
|
|
1974
|
+
}
|
|
1975
|
+
})),
|
|
1976
|
+
children: [/* @__PURE__ */ jsx(PanelsTopLeft, { size: 17 }), /* @__PURE__ */ jsx("span", { children: "Auto layout" })]
|
|
1977
|
+
})
|
|
1978
|
+
]
|
|
1979
|
+
}),
|
|
1980
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1981
|
+
className: "povio-canvas-camera",
|
|
1982
|
+
children: [
|
|
1983
|
+
/* @__PURE__ */ jsx("button", {
|
|
1984
|
+
className: "povio-canvas-configure",
|
|
1985
|
+
"aria-label": "Configure UI",
|
|
1986
|
+
title: "Configure UI",
|
|
1987
|
+
onClick: configuration.openFloating,
|
|
1988
|
+
children: /* @__PURE__ */ jsx(Settings2, { size: 16 })
|
|
1989
|
+
}),
|
|
1990
|
+
/* @__PURE__ */ jsx("button", {
|
|
1991
|
+
className: "povio-canvas-theme-toggle",
|
|
1992
|
+
onClick: () => setDark(!dark),
|
|
1993
|
+
"aria-pressed": dark,
|
|
1994
|
+
"aria-label": dark ? "Dark" : "Light",
|
|
1995
|
+
title: "Toggle page theme",
|
|
1996
|
+
children: dark ? /* @__PURE__ */ jsx(Moon, { size: 16 }) : /* @__PURE__ */ jsx(Sun, { size: 16 })
|
|
1997
|
+
}),
|
|
1998
|
+
/* @__PURE__ */ jsxs("div", {
|
|
1999
|
+
className: "povio-canvas-breakpoint",
|
|
2000
|
+
children: [/* @__PURE__ */ jsx(Monitor, { size: 15 }), /* @__PURE__ */ jsx("select", {
|
|
2001
|
+
"aria-label": "Canvas breakpoint",
|
|
2002
|
+
value: bp.id,
|
|
2003
|
+
onChange: (e) => {
|
|
2004
|
+
if (e.target.value === bp.id) return;
|
|
2005
|
+
setRuntime({});
|
|
2006
|
+
setManifest((m_2) => ({
|
|
2007
|
+
...m_2,
|
|
2008
|
+
breakpoint: e.target.value
|
|
2009
|
+
}));
|
|
2010
|
+
},
|
|
2011
|
+
children: breakpoints.map((item_1) => /* @__PURE__ */ jsxs("option", {
|
|
2012
|
+
value: item_1.id,
|
|
2013
|
+
children: [
|
|
2014
|
+
item_1.label,
|
|
2015
|
+
" · ",
|
|
2016
|
+
item_1.width,
|
|
2017
|
+
"px"
|
|
2018
|
+
]
|
|
2019
|
+
}, item_1.id))
|
|
2020
|
+
})]
|
|
2021
|
+
}),
|
|
2022
|
+
/* @__PURE__ */ jsx("span", { className: "povio-canvas-dock-divider" }),
|
|
2023
|
+
/* @__PURE__ */ jsx("button", {
|
|
2024
|
+
onClick: () => stepZoom(-1),
|
|
2025
|
+
"aria-label": "Zoom out",
|
|
2026
|
+
children: "−"
|
|
2027
|
+
}),
|
|
2028
|
+
/* @__PURE__ */ jsx("button", {
|
|
2029
|
+
className: "povio-canvas-zoom-reset",
|
|
2030
|
+
"aria-label": "Zoom to 100%",
|
|
2031
|
+
title: "Zoom to 100%",
|
|
2032
|
+
onClick: () => zoomTo(1),
|
|
2033
|
+
children: /* @__PURE__ */ jsx("output", {
|
|
2034
|
+
ref: zoomOutput,
|
|
2035
|
+
children: "25%"
|
|
2036
|
+
})
|
|
2037
|
+
}),
|
|
2038
|
+
/* @__PURE__ */ jsx("button", {
|
|
2039
|
+
onClick: () => stepZoom(1),
|
|
2040
|
+
"aria-label": "Zoom in",
|
|
2041
|
+
children: "+"
|
|
2042
|
+
}),
|
|
2043
|
+
/* @__PURE__ */ jsx("button", {
|
|
2044
|
+
onClick: fit,
|
|
2045
|
+
children: "Fit all"
|
|
2046
|
+
})
|
|
2047
|
+
]
|
|
2048
|
+
})
|
|
2049
|
+
]
|
|
2050
|
+
}),
|
|
2051
|
+
inspectMode && /* @__PURE__ */ jsx(CanvasInspectorPanel, {
|
|
2052
|
+
inspector,
|
|
2053
|
+
onClose: () => setInspectMode(false)
|
|
2054
|
+
})
|
|
2055
|
+
]
|
|
2056
|
+
}),
|
|
2057
|
+
notice && /* @__PURE__ */ jsxs("div", {
|
|
2058
|
+
className: "povio-canvas-notice",
|
|
2059
|
+
role: "status",
|
|
2060
|
+
children: [notice, /* @__PURE__ */ jsx("button", {
|
|
2061
|
+
onClick: () => setNotice(""),
|
|
2062
|
+
children: "Dismiss"
|
|
2063
|
+
})]
|
|
2064
|
+
})
|
|
2065
|
+
]
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
//#endregion
|
|
2069
|
+
//#region src/app-canvas/document-frame.ts
|
|
2070
|
+
/** Adapt a same-origin document to an uncropped, full-page canvas artboard. */
|
|
2071
|
+
function startCanvasDocument(doc, options) {
|
|
2072
|
+
const win = doc.defaultView;
|
|
2073
|
+
if (!win) throw new Error("Canvas document must belong to a window.");
|
|
2074
|
+
const viewportHeight = Math.max(1, options.viewportHeight);
|
|
2075
|
+
let interact = options.interact ?? false;
|
|
2076
|
+
let destroyed = false;
|
|
2077
|
+
let frame = 0;
|
|
2078
|
+
let lastHeight = 0;
|
|
2079
|
+
let lastLinks = "";
|
|
2080
|
+
const originals = /* @__PURE__ */ new Map();
|
|
2081
|
+
const mediaOriginals = /* @__PURE__ */ new Map();
|
|
2082
|
+
const liveStyles = /* @__PURE__ */ new Set();
|
|
2083
|
+
const liveMedia = /* @__PURE__ */ new Set();
|
|
2084
|
+
const warnedSheets = /* @__PURE__ */ new WeakSet();
|
|
2085
|
+
const positioned = /* @__PURE__ */ new WeakSet();
|
|
2086
|
+
const set = (style, property, value, priority = "important") => {
|
|
2087
|
+
liveStyles.add(style);
|
|
2088
|
+
if (style.getPropertyValue(property) === value && style.getPropertyPriority(property) === priority) return;
|
|
2089
|
+
let saved = originals.get(style);
|
|
2090
|
+
if (!saved) {
|
|
2091
|
+
saved = /* @__PURE__ */ new Map();
|
|
2092
|
+
originals.set(style, saved);
|
|
2093
|
+
}
|
|
2094
|
+
if (!saved.has(property)) saved.set(property, [style.getPropertyValue(property), style.getPropertyPriority(property)]);
|
|
2095
|
+
style.setProperty(property, value, priority);
|
|
2096
|
+
};
|
|
2097
|
+
const freezeUnits = (style) => {
|
|
2098
|
+
liveStyles.add(style);
|
|
2099
|
+
for (const property of Array.from(style)) {
|
|
2100
|
+
const value = style.getPropertyValue(property);
|
|
2101
|
+
const frozen = value.replace(/(-?(?:\d*\.)?\d+)(?:dvh|svh|lvh|vh)\b/gi, (_, amount) => `${Number(amount) * viewportHeight / 100}px`);
|
|
2102
|
+
if (value !== frozen) set(style, property, frozen, style.getPropertyPriority(property));
|
|
2103
|
+
}
|
|
2104
|
+
};
|
|
2105
|
+
const freezeMedia = (media) => {
|
|
2106
|
+
liveMedia.add(media);
|
|
2107
|
+
if (mediaOriginals.has(media)) return;
|
|
2108
|
+
const original = media.mediaText;
|
|
2109
|
+
const frozen = original.replace(/\([^()]*\b(?:height|orientation|aspect-ratio)\b[^()]*\)/gi, (predicate) => win.matchMedia(predicate).matches ? "(min-width: 0px)" : "(max-width: -1px)");
|
|
2110
|
+
if (frozen !== original) {
|
|
2111
|
+
mediaOriginals.set(media, original);
|
|
2112
|
+
media.mediaText = frozen;
|
|
2113
|
+
}
|
|
2114
|
+
};
|
|
2115
|
+
const visitRules = (rules) => {
|
|
2116
|
+
for (const rule of Array.from(rules)) {
|
|
2117
|
+
if ("style" in rule) freezeUnits(rule.style);
|
|
2118
|
+
if ("media" in rule) freezeMedia(rule.media);
|
|
2119
|
+
if ("cssRules" in rule) visitRules(rule.cssRules);
|
|
2120
|
+
if ("styleSheet" in rule && rule.styleSheet) visitSheet(rule.styleSheet);
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2123
|
+
const visitSheet = (sheet) => {
|
|
2124
|
+
try {
|
|
2125
|
+
freezeMedia(sheet.media);
|
|
2126
|
+
visitRules(sheet.cssRules);
|
|
2127
|
+
} catch {
|
|
2128
|
+
if (!warnedSheets.has(sheet)) {
|
|
2129
|
+
warnedSheets.add(sheet);
|
|
2130
|
+
const message = `App canvas cannot normalize inaccessible stylesheet ${sheet.href ?? "(inline)"}. Serve its CSS from the app origin for accurate full-page dimensions.`;
|
|
2131
|
+
if (options.onWarning) options.onWarning(message);
|
|
2132
|
+
else win.console.warn(message);
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
const anchorURL = (anchor) => {
|
|
2137
|
+
try {
|
|
2138
|
+
const url = new URL(anchor.getAttribute("href") ?? "", doc.baseURI);
|
|
2139
|
+
return /^https?:$/.test(url.protocol) && url.origin === new URL(doc.baseURI).origin ? url.href : null;
|
|
2140
|
+
} catch {
|
|
2141
|
+
return null;
|
|
2142
|
+
}
|
|
2143
|
+
};
|
|
2144
|
+
const refresh = () => {
|
|
2145
|
+
if (destroyed) return;
|
|
2146
|
+
if (frame) {
|
|
2147
|
+
win.cancelAnimationFrame(frame);
|
|
2148
|
+
frame = 0;
|
|
2149
|
+
}
|
|
2150
|
+
observer.disconnect();
|
|
2151
|
+
liveStyles.clear();
|
|
2152
|
+
liveMedia.clear();
|
|
2153
|
+
for (const sheet of [...Array.from(doc.styleSheets), ...Array.from(doc.adoptedStyleSheets ?? [])]) visitSheet(sheet);
|
|
2154
|
+
const elements = Array.from(doc.querySelectorAll("*")).filter((el) => "style" in el);
|
|
2155
|
+
for (const el of elements) freezeUnits(el.style);
|
|
2156
|
+
const snapshots = elements.map((el) => ({
|
|
2157
|
+
el,
|
|
2158
|
+
css: win.getComputedStyle(el),
|
|
2159
|
+
rect: el.getBoundingClientRect()
|
|
2160
|
+
}));
|
|
2161
|
+
for (const { el, css, rect } of snapshots) {
|
|
2162
|
+
if (!positioned.has(el) && (css.position === "fixed" || css.position === "sticky")) {
|
|
2163
|
+
positioned.add(el);
|
|
2164
|
+
if (css.position === "sticky") set(el.style, "position", "static");
|
|
2165
|
+
else {
|
|
2166
|
+
set(el.style, "position", "absolute");
|
|
2167
|
+
const containingBlock = el.offsetParent;
|
|
2168
|
+
const parentRect = containingBlock?.getBoundingClientRect();
|
|
2169
|
+
const parentTop = containingBlock && containingBlock !== doc.body && containingBlock !== doc.documentElement ? (parentRect?.top ?? 0) + containingBlock.clientTop - containingBlock.scrollTop : -win.scrollY;
|
|
2170
|
+
const parentLeft = containingBlock && containingBlock !== doc.body && containingBlock !== doc.documentElement ? (parentRect?.left ?? 0) + containingBlock.clientLeft - containingBlock.scrollLeft : -win.scrollX;
|
|
2171
|
+
set(el.style, "top", `${rect.top - parentTop}px`);
|
|
2172
|
+
set(el.style, "left", `${rect.left - parentLeft}px`);
|
|
2173
|
+
set(el.style, "right", "auto");
|
|
2174
|
+
set(el.style, "bottom", "auto");
|
|
2175
|
+
set(el.style, "width", `${rect.width}px`);
|
|
2176
|
+
set(el.style, "height", `${rect.height}px`);
|
|
2177
|
+
}
|
|
2178
|
+
}
|
|
2179
|
+
if (/^(auto|scroll)$/.test(css.overflowY) && el.scrollHeight > el.clientHeight) {
|
|
2180
|
+
let ancestor = el;
|
|
2181
|
+
while (ancestor && ancestor !== doc.documentElement) {
|
|
2182
|
+
set(ancestor.style, "height", "auto");
|
|
2183
|
+
set(ancestor.style, "max-height", "none");
|
|
2184
|
+
set(ancestor.style, "overflow-y", "visible");
|
|
2185
|
+
set(ancestor.style, "flex-shrink", "0");
|
|
2186
|
+
ancestor = ancestor.parentElement;
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
}
|
|
2190
|
+
set(doc.documentElement.style, "height", "auto");
|
|
2191
|
+
set(doc.documentElement.style, "min-height", "0px");
|
|
2192
|
+
set(doc.documentElement.style, "overflow-y", "hidden");
|
|
2193
|
+
if (doc.body) {
|
|
2194
|
+
set(doc.body.style, "height", "auto");
|
|
2195
|
+
set(doc.body.style, "min-height", "0px");
|
|
2196
|
+
}
|
|
2197
|
+
let bottom = 0;
|
|
2198
|
+
for (const el of elements) {
|
|
2199
|
+
if (el === doc.documentElement || el === doc.body || !el.getClientRects().length) continue;
|
|
2200
|
+
bottom = Math.max(bottom, el.getBoundingClientRect().bottom + win.scrollY);
|
|
2201
|
+
}
|
|
2202
|
+
if (doc.body) bottom = Math.max(bottom, doc.body.getBoundingClientRect().bottom + win.scrollY);
|
|
2203
|
+
const height = Math.max(viewportHeight, Math.ceil(bottom));
|
|
2204
|
+
if (height !== lastHeight) {
|
|
2205
|
+
lastHeight = height;
|
|
2206
|
+
options.onHeight(height);
|
|
2207
|
+
}
|
|
2208
|
+
if (options.onLinks) {
|
|
2209
|
+
const links = Array.from(doc.querySelectorAll("a[href]")).flatMap((anchor) => {
|
|
2210
|
+
const href = anchorURL(anchor);
|
|
2211
|
+
return href ? [{
|
|
2212
|
+
href,
|
|
2213
|
+
label: anchor.textContent?.trim() || anchor.getAttribute("aria-label") || href
|
|
2214
|
+
}] : [];
|
|
2215
|
+
});
|
|
2216
|
+
const signature = JSON.stringify(links);
|
|
2217
|
+
if (signature !== lastLinks) {
|
|
2218
|
+
lastLinks = signature;
|
|
2219
|
+
options.onLinks(links);
|
|
2220
|
+
}
|
|
2221
|
+
}
|
|
2222
|
+
for (const style of originals.keys()) if (!liveStyles.has(style)) originals.delete(style);
|
|
2223
|
+
for (const media of mediaOriginals.keys()) if (!liveMedia.has(media)) mediaOriginals.delete(media);
|
|
2224
|
+
liveStyles.clear();
|
|
2225
|
+
liveMedia.clear();
|
|
2226
|
+
observer.observe(doc.documentElement, {
|
|
2227
|
+
subtree: true,
|
|
2228
|
+
childList: true,
|
|
2229
|
+
attributes: true,
|
|
2230
|
+
characterData: true
|
|
2231
|
+
});
|
|
2232
|
+
};
|
|
2233
|
+
const schedule = () => {
|
|
2234
|
+
if (!destroyed && !frame) frame = win.requestAnimationFrame(refresh);
|
|
2235
|
+
};
|
|
2236
|
+
const observer = new win.MutationObserver(schedule);
|
|
2237
|
+
const resize = new win.ResizeObserver(schedule);
|
|
2238
|
+
resize.observe(doc.documentElement);
|
|
2239
|
+
if (doc.body) resize.observe(doc.body);
|
|
2240
|
+
const click = (event) => {
|
|
2241
|
+
const anchor = event.target?.closest?.("a[href]");
|
|
2242
|
+
if (interact && !anchor) return;
|
|
2243
|
+
event.preventDefault();
|
|
2244
|
+
event.stopImmediatePropagation();
|
|
2245
|
+
if (anchor) {
|
|
2246
|
+
const href = anchorURL(anchor);
|
|
2247
|
+
if (href) options.onNavigate(href);
|
|
2248
|
+
}
|
|
2249
|
+
};
|
|
2250
|
+
const submit = (event) => {
|
|
2251
|
+
if (!interact) {
|
|
2252
|
+
event.preventDefault();
|
|
2253
|
+
event.stopImmediatePropagation();
|
|
2254
|
+
}
|
|
2255
|
+
};
|
|
2256
|
+
win.addEventListener("click", click, true);
|
|
2257
|
+
win.addEventListener("submit", submit, true);
|
|
2258
|
+
win.addEventListener("resize", schedule);
|
|
2259
|
+
doc.addEventListener("load", schedule, true);
|
|
2260
|
+
doc.fonts?.ready.then(schedule);
|
|
2261
|
+
refresh();
|
|
2262
|
+
return {
|
|
2263
|
+
refresh,
|
|
2264
|
+
setInteract(value) {
|
|
2265
|
+
interact = value;
|
|
2266
|
+
},
|
|
2267
|
+
destroy() {
|
|
2268
|
+
destroyed = true;
|
|
2269
|
+
if (frame) win.cancelAnimationFrame(frame);
|
|
2270
|
+
observer.disconnect();
|
|
2271
|
+
resize.disconnect();
|
|
2272
|
+
win.removeEventListener("click", click, true);
|
|
2273
|
+
win.removeEventListener("submit", submit, true);
|
|
2274
|
+
win.removeEventListener("resize", schedule);
|
|
2275
|
+
doc.removeEventListener("load", schedule, true);
|
|
2276
|
+
for (const [media, value] of mediaOriginals) media.mediaText = value;
|
|
2277
|
+
for (const [style, values] of originals) for (const [property, [value, priority]] of values) if (value) style.setProperty(property, value, priority);
|
|
2278
|
+
else style.removeProperty(property);
|
|
2279
|
+
}
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
//#endregion
|
|
2283
|
+
//#region src/app-canvas/AppCanvasFrameBridge.tsx
|
|
2284
|
+
/** Mount once inside the application's existing UIConfigurationProvider. No separate app copy or theme fork. */
|
|
2285
|
+
function AppCanvasFrameBridge({ href }) {
|
|
2286
|
+
const { setPreviewConfiguration } = useUIConfiguration();
|
|
2287
|
+
const [identity] = useState(canvasFrameIdentity);
|
|
2288
|
+
const currentHref = useRef(href);
|
|
2289
|
+
currentHref.current = href;
|
|
2290
|
+
useEffect(() => {
|
|
2291
|
+
if (!identity) return;
|
|
2292
|
+
const send = (data) => window.parent.postMessage({
|
|
2293
|
+
protocol: canvasProtocol,
|
|
2294
|
+
...identity,
|
|
2295
|
+
...data
|
|
2296
|
+
}, window.location.origin);
|
|
2297
|
+
const renderer = startCanvasDocument(document, {
|
|
2298
|
+
viewportHeight: identity.viewportHeight,
|
|
2299
|
+
onHeight: (height) => send({
|
|
2300
|
+
type: "height",
|
|
2301
|
+
height
|
|
2302
|
+
}),
|
|
2303
|
+
onNavigate: (url) => send({
|
|
2304
|
+
type: "navigate",
|
|
2305
|
+
href: url
|
|
2306
|
+
}),
|
|
2307
|
+
onWarning: (warning) => send({
|
|
2308
|
+
type: "warning",
|
|
2309
|
+
warning
|
|
2310
|
+
})
|
|
2311
|
+
});
|
|
2312
|
+
let ackFrame = 0;
|
|
2313
|
+
const receive = (event) => {
|
|
2314
|
+
const packet = event.data;
|
|
2315
|
+
if (event.source !== window.parent || event.origin !== window.location.origin || packet?.protocol !== "povio-app-canvas-v1" || packet.token !== identity.token || packet.id !== identity.id) return;
|
|
2316
|
+
if (packet.type === "mode") renderer.setInteract(!!packet.interact);
|
|
2317
|
+
if (packet.type === "configuration" && validConfiguration(packet.configuration)) {
|
|
2318
|
+
setPreviewConfiguration(packet.configuration);
|
|
2319
|
+
document.documentElement.classList.toggle("dark", !!packet.dark);
|
|
2320
|
+
cancelAnimationFrame(ackFrame);
|
|
2321
|
+
ackFrame = requestAnimationFrame(() => {
|
|
2322
|
+
send({
|
|
2323
|
+
type: "ack",
|
|
2324
|
+
revision: packet.revision
|
|
2325
|
+
});
|
|
2326
|
+
});
|
|
2327
|
+
}
|
|
2328
|
+
};
|
|
2329
|
+
window.addEventListener("message", receive);
|
|
2330
|
+
send({
|
|
2331
|
+
type: "ready",
|
|
2332
|
+
href: currentHref.current ?? window.location.href
|
|
2333
|
+
});
|
|
2334
|
+
return () => {
|
|
2335
|
+
cancelAnimationFrame(ackFrame);
|
|
2336
|
+
window.removeEventListener("message", receive);
|
|
2337
|
+
renderer.destroy();
|
|
2338
|
+
setPreviewConfiguration(null);
|
|
2339
|
+
};
|
|
2340
|
+
}, [identity, setPreviewConfiguration]);
|
|
2341
|
+
useEffect(() => {
|
|
2342
|
+
if (identity && href) window.parent.postMessage({
|
|
2343
|
+
protocol: canvasProtocol,
|
|
2344
|
+
...identity,
|
|
2345
|
+
type: "ready",
|
|
2346
|
+
href
|
|
2347
|
+
}, window.location.origin);
|
|
2348
|
+
}, [href, identity]);
|
|
2349
|
+
return null;
|
|
2350
|
+
}
|
|
2351
|
+
//#endregion
|
|
2352
|
+
//#region src/app-canvas/AppCanvasPage.tsx
|
|
2353
|
+
const APP_CANVAS_PATH = "/_canvas";
|
|
2354
|
+
/** Ready-made route page. Mount beneath the app's UIConfigurationProvider. */
|
|
2355
|
+
function AppCanvasPage(t0) {
|
|
2356
|
+
const $ = c(17);
|
|
2357
|
+
let excludePaths;
|
|
2358
|
+
let props;
|
|
2359
|
+
let t1;
|
|
2360
|
+
if ($[0] !== t0) {
|
|
2361
|
+
({canvasPath: t1, excludePaths, ...props} = t0);
|
|
2362
|
+
$[0] = t0;
|
|
2363
|
+
$[1] = excludePaths;
|
|
2364
|
+
$[2] = props;
|
|
2365
|
+
$[3] = t1;
|
|
2366
|
+
} else {
|
|
2367
|
+
excludePaths = $[1];
|
|
2368
|
+
props = $[2];
|
|
2369
|
+
t1 = $[3];
|
|
2370
|
+
}
|
|
2371
|
+
const canvasPath = t1 === void 0 ? APP_CANVAS_PATH : t1;
|
|
2372
|
+
let t2;
|
|
2373
|
+
if ($[4] !== excludePaths) {
|
|
2374
|
+
t2 = excludePaths ?? [];
|
|
2375
|
+
$[4] = excludePaths;
|
|
2376
|
+
$[5] = t2;
|
|
2377
|
+
} else t2 = $[5];
|
|
2378
|
+
let t3;
|
|
2379
|
+
if ($[6] !== canvasPath || $[7] !== t2) {
|
|
2380
|
+
t3 = /* @__PURE__ */ new Set([...t2, canvasPath]);
|
|
2381
|
+
$[6] = canvasPath;
|
|
2382
|
+
$[7] = t2;
|
|
2383
|
+
$[8] = t3;
|
|
2384
|
+
} else t3 = $[8];
|
|
2385
|
+
let t4;
|
|
2386
|
+
if ($[9] !== t3) {
|
|
2387
|
+
t4 = [...t3];
|
|
2388
|
+
$[9] = t3;
|
|
2389
|
+
$[10] = t4;
|
|
2390
|
+
} else t4 = $[10];
|
|
2391
|
+
const exclusions = t4;
|
|
2392
|
+
if (isAppCanvasFrame()) return null;
|
|
2393
|
+
let t5;
|
|
2394
|
+
if ($[11] !== exclusions || $[12] !== props) {
|
|
2395
|
+
t5 = /* @__PURE__ */ jsx(AppCanvas, {
|
|
2396
|
+
...props,
|
|
2397
|
+
excludePaths: exclusions
|
|
2398
|
+
});
|
|
2399
|
+
$[11] = exclusions;
|
|
2400
|
+
$[12] = props;
|
|
2401
|
+
$[13] = t5;
|
|
2402
|
+
} else t5 = $[13];
|
|
2403
|
+
let t6;
|
|
2404
|
+
if ($[14] === Symbol.for("react.memo_cache_sentinel")) {
|
|
2405
|
+
t6 = /* @__PURE__ */ jsx(UIConfigurationModal, {});
|
|
2406
|
+
$[14] = t6;
|
|
2407
|
+
} else t6 = $[14];
|
|
2408
|
+
let t7;
|
|
2409
|
+
if ($[15] !== t5) {
|
|
2410
|
+
t7 = /* @__PURE__ */ jsxs("div", {
|
|
2411
|
+
className: "povio-app-canvas-page",
|
|
2412
|
+
"aria-label": "App canvas page",
|
|
2413
|
+
children: [t5, t6]
|
|
2414
|
+
});
|
|
2415
|
+
$[15] = t5;
|
|
2416
|
+
$[16] = t7;
|
|
2417
|
+
} else t7 = $[16];
|
|
2418
|
+
return t7;
|
|
2419
|
+
}
|
|
2420
|
+
//#endregion
|
|
2421
|
+
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 };
|