stickyflow 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +51 -5
  3. package/dist/binding/index.cjs +34 -0
  4. package/dist/binding/index.cjs.map +1 -0
  5. package/dist/binding/index.d.cts +11 -0
  6. package/dist/binding/index.d.ts +11 -0
  7. package/dist/binding/index.js +5 -0
  8. package/dist/binding/index.js.map +1 -0
  9. package/dist/{chunk-XEN5QIIJ.js → chunk-7VW2XKVB.js} +3 -3
  10. package/dist/{chunk-XEN5QIIJ.js.map → chunk-7VW2XKVB.js.map} +1 -1
  11. package/dist/chunk-BODZN3ZI.js +1182 -0
  12. package/dist/chunk-BODZN3ZI.js.map +1 -0
  13. package/dist/{chunk-CSXQD3ON.js → chunk-G7NRK5AG.js} +1128 -1081
  14. package/dist/chunk-G7NRK5AG.js.map +1 -0
  15. package/dist/{chunk-BYMIYXP5.js → chunk-IEHE6WVN.js} +197 -19
  16. package/dist/chunk-IEHE6WVN.js.map +1 -0
  17. package/dist/{chunk-HFOGKSNU.cjs → chunk-L3VJXZFD.cjs} +15 -15
  18. package/dist/{chunk-HFOGKSNU.cjs.map → chunk-L3VJXZFD.cjs.map} +1 -1
  19. package/dist/{chunk-65IVT4GK.js → chunk-LTGF5OHP.js} +1008 -631
  20. package/dist/chunk-LTGF5OHP.js.map +1 -0
  21. package/dist/chunk-TWDLOXRM.cjs +1189 -0
  22. package/dist/chunk-TWDLOXRM.cjs.map +1 -0
  23. package/dist/{chunk-UXDWH2JC.cjs → chunk-VK3T6O5Z.cjs} +1165 -1118
  24. package/dist/chunk-VK3T6O5Z.cjs.map +1 -0
  25. package/dist/{chunk-CXL254BT.cjs → chunk-XY2GZUHC.cjs} +204 -18
  26. package/dist/chunk-XY2GZUHC.cjs.map +1 -0
  27. package/dist/{chunk-DDCTNLLD.cjs → chunk-ZMLQIMJK.cjs} +1504 -1128
  28. package/dist/chunk-ZMLQIMJK.cjs.map +1 -0
  29. package/dist/index.cjs +53 -1013
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +22 -14
  32. package/dist/index.d.ts +22 -14
  33. package/dist/index.js +6 -993
  34. package/dist/index.js.map +1 -1
  35. package/dist/interaction/index.cjs +474 -305
  36. package/dist/interaction/index.cjs.map +1 -1
  37. package/dist/interaction/index.d.cts +11 -3
  38. package/dist/interaction/index.d.ts +11 -3
  39. package/dist/interaction/index.js +255 -87
  40. package/dist/interaction/index.js.map +1 -1
  41. package/dist/{render-C5X74hds.d.cts → render-CgX_y15a.d.cts} +193 -116
  42. package/dist/{render-C5X74hds.d.ts → render-CgX_y15a.d.ts} +193 -116
  43. package/dist/runtime-DnUxca3Y.d.ts +10 -0
  44. package/dist/runtime-reWgr7MP.d.cts +10 -0
  45. package/dist/{runtimeTypes-D8hPZ62T.d.ts → runtimeTypes-CEBGueYM.d.ts} +10 -5
  46. package/dist/{runtimeTypes-yTSAvtCn.d.cts → runtimeTypes-D60w6nyF.d.cts} +10 -5
  47. package/dist/stickyflow-diagram.global.js +35 -35
  48. package/dist/stickyflow-diagram.global.js.map +1 -1
  49. package/dist/{types-BDKDf4zr.d.cts → types-DYLC_lsk.d.ts} +3 -2
  50. package/dist/{types-CdgGdtoD.d.ts → types-DvhMpqrL.d.cts} +3 -2
  51. package/dist/viewer/index.cjs +9 -9
  52. package/dist/viewer/index.d.cts +4 -4
  53. package/dist/viewer/index.d.ts +4 -4
  54. package/dist/viewer/index.js +4 -4
  55. package/dist/web-component/index.cjs +6 -6
  56. package/dist/web-component/index.d.cts +2 -2
  57. package/dist/web-component/index.d.ts +2 -2
  58. package/dist/web-component/index.js +4 -4
  59. package/package.json +6 -1
  60. package/dist/chunk-65IVT4GK.js.map +0 -1
  61. package/dist/chunk-BYMIYXP5.js.map +0 -1
  62. package/dist/chunk-CSXQD3ON.js.map +0 -1
  63. package/dist/chunk-CXL254BT.cjs.map +0 -1
  64. package/dist/chunk-DDCTNLLD.cjs.map +0 -1
  65. package/dist/chunk-UXDWH2JC.cjs.map +0 -1
@@ -1,248 +1,618 @@
1
1
  'use strict';
2
2
 
3
- var chunkDDCTNLLD_cjs = require('./chunk-DDCTNLLD.cjs');
4
- var chunkHFOGKSNU_cjs = require('./chunk-HFOGKSNU.cjs');
3
+ var chunkZMLQIMJK_cjs = require('./chunk-ZMLQIMJK.cjs');
4
+ var chunkL3VJXZFD_cjs = require('./chunk-L3VJXZFD.cjs');
5
+ var chunkXY2GZUHC_cjs = require('./chunk-XY2GZUHC.cjs');
5
6
 
6
- // src/runtime/viewer/display/rootAttributesPatch.ts
7
- var CAMERA_OWNED_ATTRIBUTES = /* @__PURE__ */ new Set(["viewbox", "width", "height"]);
8
- function patchRootAttributes(svg, next) {
9
- for (const attribute of Array.from(svg.attributes)) {
10
- if (!CAMERA_OWNED_ATTRIBUTES.has(attribute.name.toLowerCase())) {
11
- svg.removeAttribute(attribute.name);
12
- }
13
- }
14
- for (const attribute of Array.from(next.attributes)) {
15
- if (!CAMERA_OWNED_ATTRIBUTES.has(attribute.name.toLowerCase())) {
16
- svg.setAttribute(attribute.name, attribute.value);
17
- }
7
+ // src/runtime/viewer/components/rollingNumberDom.ts
8
+ var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
9
+ var setStyles = (element, styles) => {
10
+ Object.assign(element.style, styles);
11
+ };
12
+ function createDigit(document, character, theme) {
13
+ const digit = document.createElementNS(HTML_NAMESPACE, "span");
14
+ digit.setAttribute("data-sf-rolling-current", "true");
15
+ digit.textContent = character;
16
+ setStyles(digit, {
17
+ position: "absolute",
18
+ inset: "0",
19
+ display: "grid",
20
+ placeItems: "center",
21
+ color: theme.color,
22
+ fontWeight: theme.fontWeight,
23
+ lineHeight: "1"
24
+ });
25
+ return digit;
26
+ }
27
+ function createDigitCell(document, character, theme) {
28
+ const cell = document.createElementNS(HTML_NAMESPACE, "span");
29
+ cell.setAttribute("data-sf-rolling-digit", "true");
30
+ if (theme.variant === "plain") {
31
+ setStyles(cell, {
32
+ position: "relative",
33
+ display: "inline-block",
34
+ width: "0.62em",
35
+ height: "1.1em",
36
+ overflow: "hidden"
37
+ });
38
+ cell.appendChild(createDigit(document, character, theme));
39
+ return cell;
18
40
  }
41
+ setStyles(cell, {
42
+ position: "relative",
43
+ display: "inline-block",
44
+ width: "0.72em",
45
+ height: "1.2em",
46
+ overflow: "hidden",
47
+ border: `1px solid ${theme.divider}`,
48
+ borderRadius: "0.09em",
49
+ background: theme.surface,
50
+ boxShadow: "inset 0 1px 0 rgba(255,255,255,0.7), inset 0 -0.16em 0.38em rgba(0,0,0,0.12)"
51
+ });
52
+ cell.appendChild(createDigit(document, character, theme));
53
+ const divider = document.createElementNS(HTML_NAMESPACE, "span");
54
+ setStyles(divider, {
55
+ position: "absolute",
56
+ zIndex: "2",
57
+ left: "0",
58
+ right: "0",
59
+ top: "50%",
60
+ height: "1px",
61
+ background: theme.divider,
62
+ pointerEvents: "none"
63
+ });
64
+ cell.appendChild(divider);
65
+ return cell;
19
66
  }
20
-
21
- // src/runtime/viewer/display/classifySceneDiff.ts
22
- function classifySceneDiff(oldOwnerIds, newOwnerIds, touchedOwnerIds) {
23
- const added = [];
24
- const removed = [];
25
- const changed = [];
26
- for (const id of touchedOwnerIds) {
27
- const wasPresent = oldOwnerIds.has(id);
28
- const isPresent = newOwnerIds.has(id);
29
- if (isPresent && !wasPresent) {
30
- added.push(id);
31
- } else if (wasPresent && !isPresent) {
32
- removed.push(id);
33
- } else if (wasPresent && isPresent) {
34
- changed.push(id);
35
- }
67
+ function animateDigit(element, keyframes, duration) {
68
+ if (duration === 0 || typeof element.animate !== "function") {
69
+ return null;
36
70
  }
37
- return { added, removed, changed };
71
+ return element.animate(keyframes, {
72
+ duration,
73
+ easing: "cubic-bezier(0.22, 1, 0.36, 1)",
74
+ fill: "forwards"
75
+ });
38
76
  }
39
-
40
- // src/runtime/viewer/display/definitionsPatch.ts
41
- function patchDefinitions(svg, next) {
42
- const currentDefinitions = svg.querySelector(":scope > defs");
43
- const nextDefinitions = next.querySelector(":scope > defs");
44
- if (nextDefinitions) {
45
- if (currentDefinitions) {
46
- currentDefinitions.replaceWith(nextDefinitions);
47
- } else {
48
- svg.insertBefore(nextDefinitions, svg.firstChild);
49
- }
50
- return nextDefinitions;
77
+ function fadeDigit(cell, character, theme, duration) {
78
+ const previous = cell.querySelector("[data-sf-rolling-current]");
79
+ previous?.removeAttribute("data-sf-rolling-current");
80
+ const next = createDigit(cell.ownerDocument, character, theme);
81
+ next.style.opacity = "0";
82
+ cell.insertBefore(next, cell.lastElementChild ?? null);
83
+ const hidden = { opacity: 0, filter: "blur(0.025em)", transform: "scale(0.985)" };
84
+ const shown = { opacity: 1, filter: "blur(0)", transform: "scale(1)" };
85
+ const incoming = animateDigit(next, [hidden, shown], duration);
86
+ const outgoing = previous ? animateDigit(previous, [shown, hidden], duration) : null;
87
+ const finish = () => {
88
+ previous?.remove();
89
+ next.style.opacity = "1";
90
+ };
91
+ if (incoming) {
92
+ incoming.finished.then(finish, finish);
93
+ } else {
94
+ outgoing?.cancel();
95
+ finish();
51
96
  }
52
- currentDefinitions?.remove();
53
- return currentDefinitions;
54
97
  }
55
-
56
- // src/runtime/viewer/display/elementPatch.ts
57
- function patchAttributes(current, next) {
58
- let changed = false;
59
- for (const attribute of Array.from(current.attributes)) {
60
- if (!next.hasAttribute(attribute.name)) {
61
- current.removeAttribute(attribute.name);
62
- changed = true;
63
- }
98
+ function replaceDigit(cell, character, theme, duration) {
99
+ if (theme.motion === "fade") {
100
+ fadeDigit(cell, character, theme, duration);
101
+ return;
64
102
  }
65
- for (const attribute of Array.from(next.attributes)) {
66
- if (current.getAttribute(attribute.name) !== attribute.value) {
67
- current.setAttribute(attribute.name, attribute.value);
68
- changed = true;
69
- }
103
+ const previous = cell.querySelector("[data-sf-rolling-current]");
104
+ previous?.removeAttribute("data-sf-rolling-current");
105
+ const next = createDigit(cell.ownerDocument, character, theme);
106
+ next.style.transform = "translateY(100%)";
107
+ next.style.opacity = "0.25";
108
+ cell.insertBefore(next, cell.lastElementChild);
109
+ const incoming = animateDigit(
110
+ next,
111
+ [
112
+ { transform: "translateY(100%)", opacity: 0.25 },
113
+ { transform: "translateY(0)", opacity: 1 }
114
+ ],
115
+ duration
116
+ );
117
+ const outgoing = previous ? animateDigit(
118
+ previous,
119
+ [
120
+ { transform: "translateY(0)", opacity: 1 },
121
+ { transform: "translateY(-100%)", opacity: 0.25 }
122
+ ],
123
+ duration
124
+ ) : null;
125
+ const finish = () => {
126
+ previous?.remove();
127
+ next.style.transform = "translateY(0)";
128
+ next.style.opacity = "1";
129
+ };
130
+ if (incoming) {
131
+ incoming.finished.then(finish, finish);
132
+ } else {
133
+ outgoing?.cancel();
134
+ finish();
70
135
  }
71
- return changed;
72
136
  }
73
- function childrenEqual(a, b) {
74
- const aChildren = Array.from(a.childNodes);
75
- const bChildren = Array.from(b.childNodes);
76
- if (aChildren.length !== bChildren.length) {
77
- return false;
137
+ function updateRollingDigits(digits, previousValue, nextValue, theme, duration) {
138
+ if (previousValue.length !== nextValue.length) {
139
+ digits.replaceChildren(...[...nextValue].map((character) => createDigitCell(digits.ownerDocument, character, theme)));
140
+ digits.setAttribute("aria-label", nextValue);
141
+ return;
78
142
  }
79
- for (let i = 0; i < aChildren.length; i++) {
80
- const x = aChildren[i];
81
- const y = bChildren[i];
82
- if (x.nodeType !== y.nodeType) {
83
- return false;
84
- }
85
- if (x.nodeType === Node.TEXT_NODE) {
86
- if (x.textContent !== y.textContent) {
87
- return false;
88
- }
89
- continue;
90
- }
91
- if (x.nodeType !== Node.ELEMENT_NODE) {
92
- continue;
93
- }
94
- const xElement = x;
95
- const yElement = y;
96
- if (xElement.tagName !== yElement.tagName) {
97
- return false;
98
- }
99
- if (xElement.attributes.length !== yElement.attributes.length) {
100
- return false;
101
- }
102
- for (const attribute of Array.from(xElement.attributes)) {
103
- if (yElement.getAttribute(attribute.name) !== attribute.value) {
104
- return false;
105
- }
106
- }
107
- if (!childrenEqual(xElement, yElement)) {
108
- return false;
143
+ const cells = [...digits.querySelectorAll("[data-sf-rolling-digit]")];
144
+ [...nextValue].forEach((character, index) => {
145
+ if (previousValue[index] !== character) {
146
+ replaceDigit(cells[index], character, theme, duration);
109
147
  }
110
- }
111
- return true;
148
+ });
149
+ digits.setAttribute("aria-label", nextValue);
112
150
  }
113
- function replaceTextContent(current, next) {
114
- while (current.firstChild) {
115
- current.removeChild(current.firstChild);
116
- }
117
- while (next.firstChild) {
118
- current.appendChild(next.firstChild);
119
- }
151
+
152
+ // src/runtime/viewer/components/documentComponents.ts
153
+ function findRollingNumber(svg, componentId) {
154
+ return [...svg.querySelectorAll('foreignObject[data-sf-widget="rollingNumber"]')].find(
155
+ (surface) => surface.getAttribute("data-sf-id") === componentId
156
+ );
120
157
  }
121
- function patchSceneElement(current, next) {
122
- const widgetRestyled = next.hasAttribute("data-sf-widget") && (current.getAttribute("data-sf-widget-style") !== next.getAttribute("data-sf-widget-style") || current.getAttribute("data-sf-widget-value") !== next.getAttribute("data-sf-widget-value"));
123
- const attributesChanged = patchAttributes(current, next);
124
- let contentChanged = false;
125
- if (widgetRestyled) {
126
- contentChanged = true;
127
- replaceTextContent(current, next);
158
+ var FADE_INTERVAL = 140;
159
+ function componentDigits(surface) {
160
+ return surface.querySelector("[data-sf-rolling-digits]") ?? void 0;
161
+ }
162
+ function componentStyle(surface) {
163
+ const encoded = surface.getAttribute("data-sf-widget-style");
164
+ if (!encoded) {
165
+ return chunkXY2GZUHC_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
128
166
  }
129
- if (next.tagName.toLowerCase() === "text" && !childrenEqual(current, next)) {
130
- contentChanged = true;
131
- replaceTextContent(current, next);
167
+ try {
168
+ return { ...chunkXY2GZUHC_cjs.DEFAULT_ROLLING_NUMBER_STYLE, ...JSON.parse(encoded) };
169
+ } catch {
170
+ return chunkXY2GZUHC_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
132
171
  }
133
- return attributesChanged || contentChanged;
134
172
  }
135
-
136
- // src/runtime/viewer/display/sceneNodesPatch.ts
137
- function ownerIdOf(element) {
138
- return element.getAttribute("data-sf-id") ?? element.id;
139
- }
140
- function currentElementsById(svg) {
141
- const elements = /* @__PURE__ */ new Map();
142
- for (const element of svg.querySelectorAll(":scope > [id]")) {
143
- elements.set(element.id, element);
144
- }
145
- return elements;
146
- }
147
- function reusableElement(current, next, markTouched) {
148
- if (current && current.tagName === next.tagName) {
149
- if (patchSceneElement(current, next)) {
150
- markTouched(ownerIdOf(next));
151
- }
152
- return current;
173
+ function positiveInteger(value) {
174
+ if (!value) {
175
+ return void 0;
153
176
  }
154
- current?.remove();
155
- markTouched(ownerIdOf(next));
156
- return next;
177
+ const number2 = Number(value);
178
+ return Number.isInteger(number2) && number2 > 0 ? number2 : void 0;
157
179
  }
158
- function placeAfter(svg, element, previousSibling) {
159
- const wantedNext = previousSibling ? previousSibling.nextSibling : svg.firstChild;
160
- if (wantedNext !== element) {
161
- svg.insertBefore(element, wantedNext);
180
+ function durationFor(surface) {
181
+ const duration = Number(surface.getAttribute("data-sf-widget-duration"));
182
+ if (!Number.isFinite(duration) || duration < 0) {
183
+ return 420;
162
184
  }
185
+ const reduceMotion = surface.ownerDocument.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
186
+ return reduceMotion ? 0 : duration;
163
187
  }
164
- function patchSceneNodes(svg, next) {
165
- const definitions = patchDefinitions(svg, next);
166
- const currentById = currentElementsById(svg);
167
- const retainedIds = /* @__PURE__ */ new Set();
168
- const oldOwnerIds = /* @__PURE__ */ new Set();
169
- for (const element of currentById.values()) {
170
- oldOwnerIds.add(ownerIdOf(element));
188
+ function setRollingNumber(surface, value, animate) {
189
+ const digits = componentDigits(surface);
190
+ if (!digits) {
191
+ return false;
171
192
  }
172
- const newOwnerIds = /* @__PURE__ */ new Set();
173
- const touchedOwnerIds = /* @__PURE__ */ new Set();
174
- const markTouched = (ownerId) => {
175
- touchedOwnerIds.add(ownerId);
176
- };
177
- let previousSibling = definitions;
178
- for (const nextElement of next.querySelectorAll(":scope > [id]")) {
179
- retainedIds.add(nextElement.id);
180
- newOwnerIds.add(ownerIdOf(nextElement));
181
- const liveElement = reusableElement(currentById.get(nextElement.id), nextElement, markTouched);
182
- placeAfter(svg, liveElement, previousSibling);
183
- previousSibling = liveElement;
193
+ const previousValue = surface.getAttribute("data-sf-widget-value") ?? "";
194
+ const nextValue = chunkXY2GZUHC_cjs.formatRollingNumber(value, positiveInteger(surface.getAttribute("data-sf-widget-digits")));
195
+ if (previousValue === nextValue) {
196
+ return true;
184
197
  }
185
- for (const [id, element] of currentById) {
186
- if (!retainedIds.has(id)) {
187
- touchedOwnerIds.add(ownerIdOf(element));
188
- element.remove();
198
+ updateRollingDigits(digits, previousValue, nextValue, componentStyle(surface), animate ? durationFor(surface) : 0);
199
+ surface.setAttribute("data-sf-widget-value", nextValue);
200
+ return true;
201
+ }
202
+ function mountDocumentComponents(svg) {
203
+ const valueOverrides = /* @__PURE__ */ new Map();
204
+ const lastApplied = /* @__PURE__ */ new Map();
205
+ const pending = /* @__PURE__ */ new Map();
206
+ return {
207
+ setValue(componentId, value) {
208
+ const surface = findRollingNumber(svg, componentId);
209
+ if (!surface) {
210
+ return false;
211
+ }
212
+ valueOverrides.set(componentId, value);
213
+ if (componentStyle(surface).motion !== "fade") {
214
+ return setRollingNumber(surface, value, true);
215
+ }
216
+ const now = Date.now();
217
+ const wait = (lastApplied.get(componentId) ?? 0) + FADE_INTERVAL - now;
218
+ if (wait <= 0) {
219
+ lastApplied.set(componentId, now);
220
+ return setRollingNumber(surface, value, true);
221
+ }
222
+ if (!pending.has(componentId)) {
223
+ pending.set(
224
+ componentId,
225
+ setTimeout(() => {
226
+ pending.delete(componentId);
227
+ lastApplied.set(componentId, Date.now());
228
+ const current = findRollingNumber(svg, componentId);
229
+ const latest = valueOverrides.get(componentId);
230
+ if (current && latest !== void 0) {
231
+ setRollingNumber(current, latest, true);
232
+ }
233
+ }, wait)
234
+ );
235
+ }
236
+ return true;
237
+ },
238
+ refresh() {
239
+ for (const [componentId, value] of valueOverrides) {
240
+ const surface = findRollingNumber(svg, componentId);
241
+ if (surface) {
242
+ setRollingNumber(surface, value, false);
243
+ }
244
+ }
245
+ },
246
+ clearValues() {
247
+ for (const timer of pending.values()) {
248
+ clearTimeout(timer);
249
+ }
250
+ pending.clear();
251
+ valueOverrides.clear();
252
+ },
253
+ destroy() {
254
+ for (const timer of pending.values()) {
255
+ clearTimeout(timer);
256
+ }
257
+ pending.clear();
258
+ valueOverrides.clear();
189
259
  }
190
- }
191
- return classifySceneDiff(oldOwnerIds, newOwnerIds, touchedOwnerIds);
260
+ };
192
261
  }
193
262
 
194
- // src/runtime/viewer/display/scenePatch.ts
195
- function patchDisplayContent(svg, next) {
196
- patchRootAttributes(svg, next);
197
- return patchSceneNodes(svg, next);
263
+ // src/runtime/viewer/components/rulerKnob.ts
264
+ var bases = /* @__PURE__ */ new WeakMap();
265
+ var written = /* @__PURE__ */ new WeakMap();
266
+ var fills = /* @__PURE__ */ new WeakMap();
267
+ function clamp01(value) {
268
+ return Math.min(1, Math.max(0, Number.isFinite(value) ? value : 0));
198
269
  }
199
-
200
- // src/runtime/viewer/display/svgDocument.ts
201
- function parseSvg(svgString) {
202
- const document = new DOMParser().parseFromString(svgString, "image/svg+xml");
203
- const root = document.documentElement;
204
- if (root.nodeName.toLowerCase() !== "svg") {
205
- throw new Error("viewer: source did not parse to an <svg> root");
270
+ function parameters(surface) {
271
+ try {
272
+ return JSON.parse(surface.getAttribute("data-sf-shader-params") ?? "{}");
273
+ } catch {
274
+ return {};
206
275
  }
207
- return root;
208
276
  }
209
- function readViewBox(svg) {
210
- const values = (svg.getAttribute("viewBox") ?? "0 0 100 100").split(/\s+/).map(Number);
211
- return { x: values[0], y: values[1], w: values[2], h: values[3] };
277
+ function positive(value, fallback) {
278
+ return Number(value) > 0 ? Number(value) : fallback;
212
279
  }
213
-
214
- // src/runtime/viewer/display/viewport.ts
215
- var ViewportController = class {
216
- #svg;
217
- #natural;
218
- #view;
219
- constructor(svg, natural) {
220
- this.#svg = svg;
221
- this.#natural = natural;
222
- this.#view = { ...natural };
280
+ function rulerProgress(surface, value) {
281
+ const params = parameters(surface);
282
+ const min = Number(params.min) || 0;
283
+ const max = Number.isFinite(Number(params.max)) && params.max !== void 0 ? Number(params.max) : 100;
284
+ return clamp01(max === min ? 0 : (value - min) / (max - min));
285
+ }
286
+ function move(node, dx, dy) {
287
+ const current = node.getAttribute("transform") ?? "";
288
+ const base = current === written.get(node) ? bases.get(node) ?? "" : current;
289
+ bases.set(node, base);
290
+ const next = `translate(${dx} ${dy}) ${base}`.trim();
291
+ written.set(node, next);
292
+ node.setAttribute("transform", next);
293
+ }
294
+ function hexToRgb(value) {
295
+ const match = /^#([0-9a-f]{6})$/i.exec(value.trim());
296
+ if (!match) {
297
+ return null;
223
298
  }
224
- apply() {
225
- this.#svg.setAttribute("viewBox", `${this.#view.x} ${this.#view.y} ${this.#view.w} ${this.#view.h}`);
299
+ const number2 = parseInt(match[1], 16);
300
+ return [number2 >> 16 & 255, number2 >> 8 & 255, number2 & 255];
301
+ }
302
+ var WHITE = [255, 255, 255];
303
+ function shade(node, bump, target) {
304
+ const base = fills.get(node) ?? node.getAttribute("fill") ?? "";
305
+ fills.set(node, base);
306
+ const rgb = hexToRgb(base);
307
+ if (!rgb) {
308
+ return;
226
309
  }
227
- fit() {
228
- this.#view = { ...this.#natural };
229
- this.apply();
310
+ const mixed = rgb.map((channel, index) => Math.round(channel + (target[index] - channel) * bump));
311
+ const fill = `rgb(${mixed[0]}, ${mixed[1]}, ${mixed[2]})`;
312
+ node.style.fill = fill;
313
+ for (const child of node.querySelectorAll("tspan")) {
314
+ child.style.fill = fill;
230
315
  }
231
- get() {
232
- return { ...this.#view };
316
+ }
317
+ function placeRulerKnobs(svg, progressOf) {
318
+ const radii = /* @__PURE__ */ new Map();
319
+ for (const surface of svg.querySelectorAll('foreignObject[data-sf-shader="ruler"]')) {
320
+ const params = parameters(surface);
321
+ const peak = (typeof params.peak === "string" ? hexToRgb(params.peak) : null) ?? WHITE;
322
+ const id = surface.getAttribute("data-sf-id") ?? "";
323
+ const x = Number(surface.getAttribute("x"));
324
+ const y = Number(surface.getAttribute("y"));
325
+ const width = Number(surface.getAttribute("width"));
326
+ const height = Number(surface.getAttribute("height"));
327
+ const horizontal = params.axis === "horizontal";
328
+ const progress = clamp01(progressOf(id) ?? Number(params.progress));
329
+ const inset = positive(params.inset, 0.05);
330
+ const line = positive(params.line, 0.7);
331
+ const extent = horizontal ? width : height;
332
+ const across = horizontal ? height : width;
333
+ const along = inset + (1 - 2 * inset) * progress;
334
+ const dent = params.dent === void 0 ? 1 : Number(params.dent);
335
+ const depth = (dent < 0 ? 1 : dent) * 0.16 * across;
336
+ const knobId = typeof params.knob === "string" ? params.knob : "";
337
+ if (knobId) {
338
+ const nodes = [...svg.querySelectorAll(`[data-sf-id="${CSS.escape(knobId)}"]`)];
339
+ const primary = nodes.find((node) => node.hasAttribute("data-sf-primary"));
340
+ if (primary?.getBBox) {
341
+ move(primary, 0, 0);
342
+ const box = primary.getBBox();
343
+ const half = (horizontal ? box.height : box.width) / 2;
344
+ radii.set(id, half / across);
345
+ const gap = (params.gap === void 0 ? 0.04 : Number(params.gap)) * across;
346
+ const side = line * across - depth + half + gap;
347
+ const target = horizontal ? { x: x + width * along, y: y + side } : { x: x + side, y: y + height * (1 - along) };
348
+ const dx = target.x - (box.x + box.width / 2);
349
+ const dy = target.y - (box.y + box.height / 2);
350
+ for (const node of nodes) {
351
+ move(node, dx, dy);
352
+ }
353
+ }
354
+ }
355
+ const min = Number(params.min) || 0;
356
+ const max = Number.isFinite(Number(params.max)) && params.max !== void 0 ? Number(params.max) : 100;
357
+ const spread = positive(params.spread, 0.07) * extent;
358
+ const start = inset * extent;
359
+ const span = (1 - 2 * inset) * extent;
360
+ const knobAt = start + span * progress;
361
+ for (const label of svg.querySelectorAll(`text[data-sf-id="${CSS.escape(id)}"]`)) {
362
+ const value = Number(label.textContent);
363
+ if (!Number.isFinite(value) || max === min) {
364
+ continue;
365
+ }
366
+ const labelAt = start + span * clamp01((value - min) / (max - min));
367
+ const bump = Math.exp(-(((labelAt - knobAt) / spread) ** 2));
368
+ const shift = depth * 0.4 * bump;
369
+ move(label, horizontal ? 0 : -shift, horizontal ? -shift : 0);
370
+ shade(label, bump, peak);
371
+ }
233
372
  }
234
- setNatural(natural) {
235
- this.#natural = natural;
373
+ return radii;
374
+ }
375
+
376
+ // src/runtime/viewer/display/rootAttributesPatch.ts
377
+ var CAMERA_OWNED_ATTRIBUTES = /* @__PURE__ */ new Set(["viewbox", "width", "height"]);
378
+ function patchRootAttributes(svg, next) {
379
+ for (const attribute of Array.from(svg.attributes)) {
380
+ if (!CAMERA_OWNED_ATTRIBUTES.has(attribute.name.toLowerCase())) {
381
+ svg.removeAttribute(attribute.name);
382
+ }
236
383
  }
237
- #userPoint(clientX, clientY) {
238
- const matrix = this.#svg.getScreenCTM();
239
- if (!matrix) {
240
- return null;
384
+ for (const attribute of Array.from(next.attributes)) {
385
+ if (!CAMERA_OWNED_ATTRIBUTES.has(attribute.name.toLowerCase())) {
386
+ svg.setAttribute(attribute.name, attribute.value);
241
387
  }
242
- const point = new DOMPoint(clientX, clientY).matrixTransform(matrix.inverse());
243
- return { x: point.x, y: point.y };
244
388
  }
245
- zoomBy(factor, clientX, clientY) {
389
+ }
390
+
391
+ // src/runtime/viewer/display/classifySceneDiff.ts
392
+ function classifySceneDiff(oldOwnerIds, newOwnerIds, touchedOwnerIds) {
393
+ const added = [];
394
+ const removed = [];
395
+ const changed = [];
396
+ for (const id of touchedOwnerIds) {
397
+ const wasPresent = oldOwnerIds.has(id);
398
+ const isPresent = newOwnerIds.has(id);
399
+ if (isPresent && !wasPresent) {
400
+ added.push(id);
401
+ } else if (wasPresent && !isPresent) {
402
+ removed.push(id);
403
+ } else if (wasPresent && isPresent) {
404
+ changed.push(id);
405
+ }
406
+ }
407
+ return { added, removed, changed };
408
+ }
409
+
410
+ // src/runtime/viewer/display/definitionsPatch.ts
411
+ function patchDefinitions(svg, next) {
412
+ const currentDefinitions = svg.querySelector(":scope > defs");
413
+ const nextDefinitions = next.querySelector(":scope > defs");
414
+ if (nextDefinitions) {
415
+ if (currentDefinitions) {
416
+ currentDefinitions.replaceWith(nextDefinitions);
417
+ } else {
418
+ svg.insertBefore(nextDefinitions, svg.firstChild);
419
+ }
420
+ return nextDefinitions;
421
+ }
422
+ currentDefinitions?.remove();
423
+ return currentDefinitions;
424
+ }
425
+
426
+ // src/runtime/viewer/display/elementPatch.ts
427
+ function patchAttributes(current, next) {
428
+ let changed = false;
429
+ for (const attribute of Array.from(current.attributes)) {
430
+ if (!next.hasAttribute(attribute.name)) {
431
+ current.removeAttribute(attribute.name);
432
+ changed = true;
433
+ }
434
+ }
435
+ for (const attribute of Array.from(next.attributes)) {
436
+ if (current.getAttribute(attribute.name) !== attribute.value) {
437
+ current.setAttribute(attribute.name, attribute.value);
438
+ changed = true;
439
+ }
440
+ }
441
+ return changed;
442
+ }
443
+ function childrenEqual(a, b) {
444
+ const aChildren = Array.from(a.childNodes);
445
+ const bChildren = Array.from(b.childNodes);
446
+ if (aChildren.length !== bChildren.length) {
447
+ return false;
448
+ }
449
+ for (let i = 0; i < aChildren.length; i++) {
450
+ const x = aChildren[i];
451
+ const y = bChildren[i];
452
+ if (x.nodeType !== y.nodeType) {
453
+ return false;
454
+ }
455
+ if (x.nodeType === Node.TEXT_NODE) {
456
+ if (x.textContent !== y.textContent) {
457
+ return false;
458
+ }
459
+ continue;
460
+ }
461
+ if (x.nodeType !== Node.ELEMENT_NODE) {
462
+ continue;
463
+ }
464
+ const xElement = x;
465
+ const yElement = y;
466
+ if (xElement.tagName !== yElement.tagName) {
467
+ return false;
468
+ }
469
+ if (xElement.attributes.length !== yElement.attributes.length) {
470
+ return false;
471
+ }
472
+ for (const attribute of Array.from(xElement.attributes)) {
473
+ if (yElement.getAttribute(attribute.name) !== attribute.value) {
474
+ return false;
475
+ }
476
+ }
477
+ if (!childrenEqual(xElement, yElement)) {
478
+ return false;
479
+ }
480
+ }
481
+ return true;
482
+ }
483
+ function replaceTextContent(current, next) {
484
+ while (current.firstChild) {
485
+ current.removeChild(current.firstChild);
486
+ }
487
+ while (next.firstChild) {
488
+ current.appendChild(next.firstChild);
489
+ }
490
+ }
491
+ function patchSceneElement(current, next) {
492
+ const componentRestyled = next.hasAttribute("data-sf-widget") && (current.getAttribute("data-sf-widget-style") !== next.getAttribute("data-sf-widget-style") || current.getAttribute("data-sf-widget-value") !== next.getAttribute("data-sf-widget-value"));
493
+ const attributesChanged = patchAttributes(current, next);
494
+ let contentChanged = false;
495
+ if (componentRestyled) {
496
+ contentChanged = true;
497
+ replaceTextContent(current, next);
498
+ }
499
+ if (next.tagName.toLowerCase() === "text" && !childrenEqual(current, next)) {
500
+ contentChanged = true;
501
+ replaceTextContent(current, next);
502
+ }
503
+ return attributesChanged || contentChanged;
504
+ }
505
+
506
+ // src/runtime/viewer/display/sceneNodesPatch.ts
507
+ function ownerIdOf(element) {
508
+ return element.getAttribute("data-sf-id") ?? element.id;
509
+ }
510
+ function currentElementsById(svg) {
511
+ const elements = /* @__PURE__ */ new Map();
512
+ for (const element of svg.querySelectorAll(":scope > [id]")) {
513
+ elements.set(element.id, element);
514
+ }
515
+ return elements;
516
+ }
517
+ function reusableElement(current, next, markTouched) {
518
+ if (current && current.tagName === next.tagName) {
519
+ if (patchSceneElement(current, next)) {
520
+ markTouched(ownerIdOf(next));
521
+ }
522
+ return current;
523
+ }
524
+ current?.remove();
525
+ markTouched(ownerIdOf(next));
526
+ return next;
527
+ }
528
+ function placeAfter(svg, element, previousSibling) {
529
+ const wantedNext = previousSibling ? previousSibling.nextSibling : svg.firstChild;
530
+ if (wantedNext !== element) {
531
+ svg.insertBefore(element, wantedNext);
532
+ }
533
+ }
534
+ function patchSceneNodes(svg, next) {
535
+ const definitions = patchDefinitions(svg, next);
536
+ const currentById = currentElementsById(svg);
537
+ const retainedIds = /* @__PURE__ */ new Set();
538
+ const oldOwnerIds = /* @__PURE__ */ new Set();
539
+ for (const element of currentById.values()) {
540
+ oldOwnerIds.add(ownerIdOf(element));
541
+ }
542
+ const newOwnerIds = /* @__PURE__ */ new Set();
543
+ const touchedOwnerIds = /* @__PURE__ */ new Set();
544
+ const markTouched = (ownerId) => {
545
+ touchedOwnerIds.add(ownerId);
546
+ };
547
+ let previousSibling = definitions;
548
+ for (const nextElement of next.querySelectorAll(":scope > [id]")) {
549
+ retainedIds.add(nextElement.id);
550
+ newOwnerIds.add(ownerIdOf(nextElement));
551
+ const liveElement = reusableElement(currentById.get(nextElement.id), nextElement, markTouched);
552
+ placeAfter(svg, liveElement, previousSibling);
553
+ previousSibling = liveElement;
554
+ }
555
+ for (const [id, element] of currentById) {
556
+ if (!retainedIds.has(id)) {
557
+ touchedOwnerIds.add(ownerIdOf(element));
558
+ element.remove();
559
+ }
560
+ }
561
+ return classifySceneDiff(oldOwnerIds, newOwnerIds, touchedOwnerIds);
562
+ }
563
+
564
+ // src/runtime/viewer/display/scenePatch.ts
565
+ function patchDisplayContent(svg, next) {
566
+ patchRootAttributes(svg, next);
567
+ return patchSceneNodes(svg, next);
568
+ }
569
+
570
+ // src/runtime/viewer/display/svgDocument.ts
571
+ function parseSvg(svgString) {
572
+ const document = new DOMParser().parseFromString(svgString, "image/svg+xml");
573
+ const root = document.documentElement;
574
+ if (root.nodeName.toLowerCase() !== "svg") {
575
+ throw new Error("viewer: source did not parse to an <svg> root");
576
+ }
577
+ return root;
578
+ }
579
+ function readViewBox(svg) {
580
+ const values = (svg.getAttribute("viewBox") ?? "0 0 100 100").split(/\s+/).map(Number);
581
+ return { x: values[0], y: values[1], w: values[2], h: values[3] };
582
+ }
583
+
584
+ // src/runtime/viewer/display/viewport.ts
585
+ var ViewportController = class {
586
+ #svg;
587
+ #natural;
588
+ #view;
589
+ constructor(svg, natural) {
590
+ this.#svg = svg;
591
+ this.#natural = natural;
592
+ this.#view = { ...natural };
593
+ }
594
+ apply() {
595
+ this.#svg.setAttribute("viewBox", `${this.#view.x} ${this.#view.y} ${this.#view.w} ${this.#view.h}`);
596
+ }
597
+ fit() {
598
+ this.#view = { ...this.#natural };
599
+ this.apply();
600
+ }
601
+ get() {
602
+ return { ...this.#view };
603
+ }
604
+ setNatural(natural) {
605
+ this.#natural = natural;
606
+ }
607
+ #userPoint(clientX, clientY) {
608
+ const matrix = this.#svg.getScreenCTM();
609
+ if (!matrix) {
610
+ return null;
611
+ }
612
+ const point = new DOMPoint(clientX, clientY).matrixTransform(matrix.inverse());
613
+ return { x: point.x, y: point.y };
614
+ }
615
+ zoomBy(factor, clientX, clientY) {
246
616
  const rect = this.#svg.getBoundingClientRect();
247
617
  if (rect.width <= 0 || rect.height <= 0) {
248
618
  return;
@@ -297,11 +667,11 @@ function mountDisplay(container, svgString) {
297
667
  }
298
668
 
299
669
  // src/runtime/viewer/events.ts
300
- var Emitter2 = class extends chunkHFOGKSNU_cjs.Emitter {
670
+ var Emitter2 = class extends chunkL3VJXZFD_cjs.Emitter {
301
671
  };
302
672
 
303
673
  // src/runtime/viewer/export/raster.ts
304
- function positive(value, fallback) {
674
+ function positive2(value, fallback) {
305
675
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
306
676
  }
307
677
  function loadImage(document, source) {
@@ -334,7 +704,7 @@ function canvasBlob(canvas, mimeType, quality) {
334
704
  }
335
705
  async function rasterizeSvgSnapshot(svg, snapshot, options = {}) {
336
706
  const viewBox = readViewBox(svg);
337
- const scale = positive(options.scale, 1);
707
+ const scale = positive2(options.scale, 1);
338
708
  const width = Math.max(1, Math.round(viewBox.w * scale));
339
709
  const height = Math.max(1, Math.round(viewBox.h * scale));
340
710
  const canvas = svg.ownerDocument.createElement("canvas");
@@ -384,14 +754,14 @@ function svgElement(document, name, attributes) {
384
754
  }
385
755
  return element;
386
756
  }
387
- function readWidgetStyle(surface) {
757
+ function readComponentStyle(surface) {
388
758
  try {
389
759
  return {
390
- ...chunkDDCTNLLD_cjs.DEFAULT_ROLLING_NUMBER_STYLE,
760
+ ...chunkXY2GZUHC_cjs.DEFAULT_ROLLING_NUMBER_STYLE,
391
761
  ...JSON.parse(surface.getAttribute("data-sf-widget-style") ?? "{}")
392
762
  };
393
763
  } catch {
394
- return chunkDDCTNLLD_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
764
+ return chunkXY2GZUHC_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
395
765
  }
396
766
  }
397
767
  function cellsOf(box, plain, fontSize = 0) {
@@ -479,7 +849,7 @@ function flattenRollingNumbers(svg) {
479
849
  height: Number(surface.getAttribute("height"))
480
850
  };
481
851
  const value = surface.getAttribute("data-sf-widget-value") ?? "";
482
- const style = readWidgetStyle(surface);
852
+ const style = readComponentStyle(surface);
483
853
  const label = surface.querySelector("div > span:not([data-sf-rolling-digit])")?.textContent ?? "";
484
854
  const cells = cellsOf(box, style.variant === "plain", style.fontSize);
485
855
  const labelBlock = label ? cells.size * 0.3 : 0;
@@ -531,7 +901,7 @@ function serializeSvgSnapshot(svg, captures) {
531
901
  // src/runtime/viewer/interaction/changeFlash.ts
532
902
  function createChangeFlash(svg) {
533
903
  const timers = /* @__PURE__ */ new Map();
534
- const flash = (ownerIds, color2 = chunkHFOGKSNU_cjs.CHANGE_COLOR) => {
904
+ const flash = (ownerIds, color2 = chunkL3VJXZFD_cjs.CHANGE_COLOR) => {
535
905
  const ids = [...new Set(ownerIds)];
536
906
  if (ids.length === 0) {
537
907
  return;
@@ -539,7 +909,7 @@ function createChangeFlash(svg) {
539
909
  const doc = svg.ownerDocument;
540
910
  const reducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
541
911
  for (const id of ids) {
542
- const primary = chunkHFOGKSNU_cjs.findOwnerNode(svg, id, chunkHFOGKSNU_cjs.isPrimary);
912
+ const primary = chunkL3VJXZFD_cjs.findOwnerNode(svg, id, chunkL3VJXZFD_cjs.isPrimary);
543
913
  if (!primary) {
544
914
  continue;
545
915
  }
@@ -550,10 +920,10 @@ function createChangeFlash(svg) {
550
920
  for (const stale of svg.querySelectorAll(`[data-sf-change-overlay="${CSS.escape(id)}"]`)) {
551
921
  stale.remove();
552
922
  }
553
- const overlay = primary.getAttribute("data-sf-role") === "link" ? chunkHFOGKSNU_cjs.linkChangeOverlay(doc, primary, id, color2) : chunkHFOGKSNU_cjs.shapeChangeOverlay(doc, primary, id, color2);
923
+ const overlay = primary.getAttribute("data-sf-role") === "link" ? chunkL3VJXZFD_cjs.linkChangeOverlay(doc, primary, id, color2) : chunkL3VJXZFD_cjs.shapeChangeOverlay(doc, primary, id, color2);
554
924
  svg.appendChild(overlay);
555
- chunkHFOGKSNU_cjs.startChangeFade(overlay, reducedMotion);
556
- const duration = reducedMotion ? chunkHFOGKSNU_cjs.REDUCED_MOTION_DURATION_MS : chunkHFOGKSNU_cjs.FLASH_DURATION_MS;
925
+ chunkL3VJXZFD_cjs.startChangeFade(overlay, reducedMotion);
926
+ const duration = reducedMotion ? chunkL3VJXZFD_cjs.REDUCED_MOTION_DURATION_MS : chunkL3VJXZFD_cjs.FLASH_DURATION_MS;
557
927
  timers.set(
558
928
  id,
559
929
  setTimeout(() => {
@@ -640,7 +1010,7 @@ var OwnerPointerController = class {
640
1010
  this.#selection = selection;
641
1011
  }
642
1012
  hover(event) {
643
- const owner = chunkHFOGKSNU_cjs.resolveOwner(event.target);
1013
+ const owner = chunkL3VJXZFD_cjs.resolveOwner(event.target);
644
1014
  this.#emitter.emit(
645
1015
  "ownerHover",
646
1016
  owner ? { diagramId: this.#diagramId, owner, modifiers: modifiersOf(event) } : { diagramId: this.#diagramId, owner: null }
@@ -648,7 +1018,7 @@ var OwnerPointerController = class {
648
1018
  }
649
1019
  select(event) {
650
1020
  const target = event.target;
651
- const owner = chunkHFOGKSNU_cjs.resolveOwner(target);
1021
+ const owner = chunkL3VJXZFD_cjs.resolveOwner(target);
652
1022
  if (!owner) {
653
1023
  this.#clearWhenOutsideOverlay(target);
654
1024
  return;
@@ -669,7 +1039,7 @@ var OwnerPointerController = class {
669
1039
  owner,
670
1040
  modifiers
671
1041
  });
672
- const node = chunkHFOGKSNU_cjs.findOwnerNode(this.#svg, owner.id, chunkHFOGKSNU_cjs.isPrimary);
1042
+ const node = chunkL3VJXZFD_cjs.findOwnerNode(this.#svg, owner.id, chunkL3VJXZFD_cjs.isPrimary);
673
1043
  if (node && !node.hasAttribute("tabindex")) {
674
1044
  node.setAttribute("tabindex", "-1");
675
1045
  }
@@ -697,12 +1067,12 @@ var PointerInteractionSession = class {
697
1067
  }
698
1068
  #onPointerDown = (event) => {
699
1069
  const target = event.target;
700
- const overContent = Boolean(chunkHFOGKSNU_cjs.resolveOwner(target)) || isSelectionOverlayControl(target);
1070
+ const overContent = Boolean(chunkL3VJXZFD_cjs.resolveOwner(target)) || isSelectionOverlayControl(target);
701
1071
  this.#down = {
702
1072
  x: event.clientX,
703
1073
  y: event.clientY,
704
1074
  moved: false,
705
- panable: chunkHFOGKSNU_cjs.isCameraGesture(event) || !overContent
1075
+ panable: chunkL3VJXZFD_cjs.isCameraGesture(event) || !overContent
706
1076
  };
707
1077
  };
708
1078
  #onPointerMove = (event) => {
@@ -730,7 +1100,7 @@ var PointerInteractionSession = class {
730
1100
  if (!this.#down.moved) {
731
1101
  return false;
732
1102
  }
733
- if (this.#down.panable && chunkHFOGKSNU_cjs.isCameraGesture(event)) {
1103
+ if (this.#down.panable && chunkL3VJXZFD_cjs.isCameraGesture(event)) {
734
1104
  this.#display.panBy(event.movementX, event.movementY);
735
1105
  }
736
1106
  return true;
@@ -754,15 +1124,15 @@ function paintSelectionOverlays(svg, selectedIds, owners) {
754
1124
  }
755
1125
  const document = svg.ownerDocument;
756
1126
  for (const id of selectedIds) {
757
- const primary = chunkHFOGKSNU_cjs.findOwnerNode(svg, id, chunkHFOGKSNU_cjs.isPrimary);
1127
+ const primary = chunkL3VJXZFD_cjs.findOwnerNode(svg, id, chunkL3VJXZFD_cjs.isPrimary);
758
1128
  if (!primary) {
759
1129
  continue;
760
1130
  }
761
1131
  const owner = owners.get(id);
762
- const overlay = owner?.role === "link" ? chunkHFOGKSNU_cjs.linkSelectionOverlay(document, primary, id, svg) : owner?.role === "path" ? chunkHFOGKSNU_cjs.pathSelectionOverlay(document, primary, id, svg) : owner?.role === "isometric" ? chunkHFOGKSNU_cjs.isometricSelectionOverlay(document, primary, id, svg) : chunkHFOGKSNU_cjs.shapeSelectionOverlay(document, primary, id, svg, owner ? chunkHFOGKSNU_cjs.DERIVED_HEIGHT_ROLES.has(owner.role) : false);
1132
+ const overlay = owner?.role === "link" ? chunkL3VJXZFD_cjs.linkSelectionOverlay(document, primary, id, svg) : owner?.role === "path" ? chunkL3VJXZFD_cjs.pathSelectionOverlay(document, primary, id, svg) : owner?.role === "isometric" ? chunkL3VJXZFD_cjs.isometricSelectionOverlay(document, primary, id, svg) : chunkL3VJXZFD_cjs.shapeSelectionOverlay(document, primary, id, svg, owner ? chunkL3VJXZFD_cjs.DERIVED_HEIGHT_ROLES.has(owner.role) : false);
763
1133
  svg.appendChild(overlay);
764
1134
  if (owner?.role === "shape" && selectedIds.size === 1) {
765
- svg.appendChild(chunkHFOGKSNU_cjs.quickDuplicateBubbles(document, primary, id, svg));
1135
+ svg.appendChild(chunkL3VJXZFD_cjs.quickDuplicateBubbles(document, primary, id, svg));
766
1136
  }
767
1137
  }
768
1138
  }
@@ -801,7 +1171,7 @@ var SelectionController = class {
801
1171
  selectIds(ids) {
802
1172
  for (const id of ids) {
803
1173
  const element = this.#svg.querySelector(`[data-sf-id="${CSS.escape(id)}"]`);
804
- const owner = chunkHFOGKSNU_cjs.resolveOwner(element);
1174
+ const owner = chunkL3VJXZFD_cjs.resolveOwner(element);
805
1175
  if (owner) {
806
1176
  this.remember(owner);
807
1177
  }
@@ -813,7 +1183,7 @@ var SelectionController = class {
813
1183
  // src/runtime/viewer/interaction/wheel.ts
814
1184
  function bindWheelZoom(display, selection) {
815
1185
  const onWheel = (event) => {
816
- if (!chunkHFOGKSNU_cjs.isCameraGesture(event)) {
1186
+ if (!chunkL3VJXZFD_cjs.isCameraGesture(event)) {
817
1187
  return;
818
1188
  }
819
1189
  event.preventDefault();
@@ -2427,951 +2797,584 @@ var violetGlassLightProgram = {
2427
2797
  fragmentSource: fragmentSource20
2428
2798
  };
2429
2799
 
2430
- // src/runtime/viewer/shaders/programs.ts
2431
- var builtins = [
2432
- orbProgram,
2433
- haloProgram,
2434
- halftoneHaloProgram,
2435
- chromaticHaloProgram,
2436
- chromaticGlassPlateProgram,
2437
- glassRefractionProgram,
2438
- emberSlitsProgram,
2439
- smearOrbProgram,
2440
- pixelConstellationProgram,
2441
- plasmaGaugeProgram,
2442
- tickScaleProgram,
2443
- rulerProgram,
2444
- buttonProgram,
2445
- prismSlatsProgram,
2446
- eclipseOrbProgram,
2447
- emberMatrixProgram,
2448
- violetGlassProgram,
2449
- violetGlassLightProgram,
2450
- lumenPortalProgram,
2451
- lumenPortalLightProgram
2452
- ];
2453
- var programs = new Map(builtins.map((program) => [program.name, program]));
2454
- function registerShaderProgram(program) {
2455
- if (!program.name.trim()) {
2456
- throw new Error("registerShaderProgram: name must not be empty.");
2457
- }
2458
- if (!program.fragmentSource.includes("void main")) {
2459
- throw new Error(`registerShaderProgram: "${program.name}" has no main function.`);
2460
- }
2461
- programs.set(program.name, program);
2462
- return () => {
2463
- if (programs.get(program.name) === program) {
2464
- programs.delete(program.name);
2465
- }
2466
- };
2467
- }
2468
- function shaderProgram(name) {
2469
- return programs.get(name);
2470
- }
2471
-
2472
- // src/runtime/viewer/shaders/backdrop.ts
2473
- var MIN_BACKDROP_DENSITY = 2.5;
2474
- var MAX_BACKDROP_PIXELS = 4096;
2475
- var TRANSPARENT = /^rgba?(s*d+,s*d+,s*d+,s*0s*)$|^transparent$/;
2476
- function hostBackground(svg) {
2477
- const view = svg.ownerDocument.defaultView;
2478
- for (let element = svg.parentElement; element && view; element = element.parentElement) {
2479
- const color2 = view.getComputedStyle(element).backgroundColor;
2480
- if (color2 && !TRANSPARENT.test(color2.trim())) {
2481
- return color2;
2482
- }
2483
- }
2484
- return void 0;
2485
- }
2486
- function backdropClone(svg, owner, width, height) {
2487
- const clone = svg.cloneNode(true);
2488
- const ownerIndex = [...svg.children].indexOf(owner);
2489
- if (ownerIndex >= 0) {
2490
- while (clone.children.length > ownerIndex) {
2491
- clone.lastElementChild?.remove();
2492
- }
2493
- }
2494
- for (const surface of clone.querySelectorAll("foreignObject[data-sf-shader]")) {
2495
- surface.remove();
2496
- }
2497
- const background = hostBackground(svg);
2498
- if (background) {
2499
- const { x, y, width: viewWidth, height: viewHeight } = svg.viewBox.baseVal;
2500
- const fill = svg.ownerDocument.createElementNS("http://www.w3.org/2000/svg", "rect");
2501
- fill.setAttribute("x", String(x));
2502
- fill.setAttribute("y", String(y));
2503
- fill.setAttribute("width", String(viewWidth));
2504
- fill.setAttribute("height", String(viewHeight));
2505
- fill.setAttribute("fill", background);
2506
- clone.insertBefore(fill, clone.firstChild);
2507
- }
2508
- clone.setAttribute("width", String(width));
2509
- clone.setAttribute("height", String(height));
2510
- clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2511
- return clone;
2512
- }
2513
- function captureShaderBackdrop(svg, owner) {
2514
- const density = Math.max(globalThis.devicePixelRatio || 1, MIN_BACKDROP_DENSITY);
2515
- const scale = Math.min(density, MAX_BACKDROP_PIXELS / Math.max(1, Math.max(svg.clientWidth, svg.clientHeight)));
2516
- const width = Math.max(1, Math.round(svg.clientWidth * scale));
2517
- const height = Math.max(1, Math.round(svg.clientHeight * scale));
2518
- const view = svg.ownerDocument.defaultView;
2519
- if (!view) {
2520
- return Promise.resolve(void 0);
2521
- }
2522
- const markup = new XMLSerializer().serializeToString(backdropClone(svg, owner, width, height));
2523
- const image = svg.ownerDocument.createElement("img");
2524
- const source = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
2525
- return new Promise((resolve) => {
2526
- image.onload = () => resolve({ image, width, height });
2527
- image.onerror = () => resolve(void 0);
2528
- image.src = source;
2529
- });
2530
- }
2531
-
2532
- // src/runtime/viewer/shaders/uniforms.ts
2533
- function color(value, fallback) {
2534
- if (typeof value !== "string") {
2535
- return fallback;
2536
- }
2537
- const match = /^#([0-9a-f]{6})$/i.exec(value);
2538
- if (!match) {
2539
- return fallback;
2540
- }
2541
- const integer = Number.parseInt(match[1], 16);
2542
- return [(integer >> 16 & 255) / 255, (integer >> 8 & 255) / 255, (integer & 255) / 255];
2543
- }
2544
- function number(value, fallback) {
2545
- return typeof value === "number" && Number.isFinite(value) ? value : fallback;
2546
- }
2547
- function uniform1f(surface, name, value) {
2548
- const location = surface.gl.getUniformLocation(surface.program, name);
2549
- if (location) {
2550
- surface.gl.uniform1f(location, value);
2551
- }
2552
- }
2553
- function uniform3f(surface, name, value) {
2554
- const location = surface.gl.getUniformLocation(surface.program, name);
2555
- if (location) {
2556
- surface.gl.uniform3f(location, ...value);
2557
- }
2558
- }
2559
- function uniform2f(surface, name, value) {
2560
- const location = surface.gl.getUniformLocation(surface.program, name);
2561
- if (location) {
2562
- surface.gl.uniform2f(location, ...value);
2563
- }
2564
- }
2565
- function uniformName(parameter) {
2566
- return `u${parameter.charAt(0).toUpperCase()}${parameter.slice(1)}`;
2567
- }
2568
- function enumParameter(name, value) {
2569
- const values = {
2570
- direction: { left: 0, right: 1, top: 2, bottom: 3 },
2571
- orientation: { vertical: 0, horizontal: 1 },
2572
- axis: { vertical: 0, horizontal: 1 }
2573
- };
2574
- return values[name]?.[value];
2575
- }
2576
- function applyParameters(surface, elapsed) {
2577
- for (const [key, value] of Object.entries(surface.parameters)) {
2578
- const name = uniformName(key);
2579
- if (typeof value === "number") {
2580
- const lag = surface.parameters[`${key}Lag`];
2581
- if (typeof lag === "number" && lag > 0) {
2582
- const current = surface.smoothed.get(key) ?? value;
2583
- const next = current + (value - current) * (1 - Math.exp(-elapsed / lag));
2584
- surface.smoothed.set(key, next);
2585
- uniform1f(surface, name, next);
2586
- continue;
2587
- }
2588
- uniform1f(surface, name, value);
2589
- continue;
2590
- }
2591
- if (typeof value === "boolean") {
2592
- const location = surface.gl.getUniformLocation(surface.program, name);
2593
- if (location) {
2594
- surface.gl.uniform1i(location, value ? 1 : 0);
2595
- }
2596
- continue;
2597
- }
2598
- const enumValue = enumParameter(key, value);
2599
- if (enumValue !== void 0) {
2600
- uniform1f(surface, name, enumValue);
2601
- continue;
2602
- }
2603
- if (/^#[0-9a-f]{6}$/i.test(value)) {
2604
- uniform3f(surface, name, color(value, [0, 0, 0]));
2605
- }
2606
- }
2607
- }
2608
-
2609
- // src/runtime/viewer/shaders/webgl.ts
2610
- var VERTEX_SOURCE = `
2611
- attribute vec2 aPosition;
2612
- varying vec2 vUv;
2613
-
2614
- void main() {
2615
- vUv = aPosition * 0.5 + 0.5;
2616
- gl_Position = vec4(aPosition, 0.0, 1.0);
2617
- }
2618
- `;
2619
- function compile(gl, type, source) {
2620
- const shader = gl.createShader(type);
2621
- if (!shader) {
2622
- return void 0;
2623
- }
2624
- gl.shaderSource(shader, source);
2625
- gl.compileShader(shader);
2626
- if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
2627
- gl.deleteShader(shader);
2628
- return void 0;
2629
- }
2630
- return shader;
2631
- }
2632
- function linkProgram(gl, fragmentSource21) {
2633
- const vertex = compile(gl, gl.VERTEX_SHADER, VERTEX_SOURCE);
2634
- const fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource21);
2635
- if (!vertex || !fragment) {
2636
- if (vertex) {
2637
- gl.deleteShader(vertex);
2638
- }
2639
- if (fragment) {
2640
- gl.deleteShader(fragment);
2641
- }
2642
- return void 0;
2643
- }
2644
- const program = gl.createProgram();
2645
- if (!program) {
2646
- gl.deleteShader(vertex);
2647
- gl.deleteShader(fragment);
2648
- return void 0;
2649
- }
2650
- gl.attachShader(program, vertex);
2651
- gl.attachShader(program, fragment);
2652
- gl.linkProgram(program);
2653
- gl.deleteShader(vertex);
2654
- gl.deleteShader(fragment);
2655
- if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
2656
- gl.deleteProgram(program);
2657
- return void 0;
2658
- }
2659
- return program;
2660
- }
2661
- function transparentTexture(gl) {
2662
- const texture = gl.createTexture();
2663
- if (!texture) {
2664
- return void 0;
2665
- }
2666
- gl.bindTexture(gl.TEXTURE_2D, texture);
2667
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
2668
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
2669
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
2670
- gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
2671
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
2672
- return texture;
2673
- }
2674
-
2675
- // src/runtime/viewer/shaders/createSurface.ts
2676
- var FULL_SCREEN_QUAD = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
2677
- function parametersOf(owner) {
2678
- try {
2679
- return JSON.parse(owner.getAttribute("data-sf-shader-params") ?? "{}");
2680
- } catch {
2681
- return {};
2682
- }
2683
- }
2684
- function bindFullScreenQuad(gl, program) {
2685
- const buffer = gl.createBuffer();
2686
- if (!buffer) {
2687
- return void 0;
2688
- }
2689
- gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
2690
- gl.bufferData(gl.ARRAY_BUFFER, FULL_SCREEN_QUAD, gl.STATIC_DRAW);
2691
- const position = gl.getAttribLocation(program, "aPosition");
2692
- if (position < 0) {
2693
- gl.deleteBuffer(buffer);
2694
- return void 0;
2695
- }
2696
- gl.enableVertexAttribArray(position);
2697
- gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
2698
- return buffer;
2699
- }
2700
- function findBaseGeometry(owner, definition) {
2701
- const ownerId = owner.getAttribute("data-sf-id");
2702
- if (!definition.replacesBaseFill || !ownerId) {
2703
- return void 0;
2704
- }
2705
- const candidates = [...owner.closest("svg")?.querySelectorAll('[data-sf-primary="true"]') ?? []];
2706
- return candidates.find((element) => element !== owner && element.getAttribute("data-sf-id") === ownerId);
2707
- }
2708
- function createSurface(canvas) {
2709
- const owner = canvas.closest("[data-sf-shader]");
2710
- const name = owner?.getAttribute("data-sf-shader");
2711
- const definition = name ? shaderProgram(name) : void 0;
2712
- const gl = canvas.getContext("webgl", { alpha: true, antialias: true, premultipliedAlpha: true });
2713
- if (!owner || !definition || !gl) {
2714
- return void 0;
2715
- }
2716
- const program = linkProgram(gl, definition.fragmentSource);
2717
- if (!program) {
2718
- return void 0;
2719
- }
2720
- gl.useProgram(program);
2721
- const buffer = bindFullScreenQuad(gl, program);
2722
- if (!buffer) {
2723
- gl.deleteProgram(program);
2724
- return void 0;
2725
- }
2726
- const usesBackdrop = definition.usesBackdrop === true;
2727
- const baseGeometry = findBaseGeometry(owner, definition);
2728
- const baseFill = baseGeometry?.getAttribute("fill");
2729
- baseGeometry?.setAttribute("fill", "none");
2730
- return {
2731
- owner,
2732
- canvas,
2733
- gl,
2734
- program,
2735
- buffer,
2736
- parameters: parametersOf(owner),
2737
- smoothed: /* @__PURE__ */ new Map(),
2738
- backdropTexture: usesBackdrop ? transparentTexture(gl) : void 0,
2739
- backdropOrigin: [0, 0],
2740
- backdropScale: [1, 1],
2741
- usesBackdrop,
2742
- shapeColor: color(owner.getAttribute("data-sf-shader-fill") ?? void 0, [1, 1, 1]),
2743
- baseGeometry,
2744
- baseFill
2745
- };
2746
- }
2747
- function releaseSurface(surface) {
2748
- if (surface.backdropTexture) {
2749
- surface.gl.deleteTexture(surface.backdropTexture);
2750
- }
2751
- surface.gl.deleteBuffer(surface.buffer);
2752
- surface.gl.deleteProgram(surface.program);
2753
- if (!surface.baseGeometry) {
2754
- return;
2755
- }
2756
- if (surface.baseFill === null || surface.baseFill === void 0) {
2757
- surface.baseGeometry.removeAttribute("fill");
2758
- } else {
2759
- surface.baseGeometry.setAttribute("fill", surface.baseFill);
2760
- }
2761
- }
2762
-
2763
- // src/runtime/viewer/shaders/renderSurface.ts
2764
- function syncBackdropBounds(surface) {
2765
- const svg = surface.canvas.closest("svg");
2766
- if (!svg) {
2767
- return;
2800
+ // src/runtime/viewer/shaders/programs.ts
2801
+ var builtins = [
2802
+ orbProgram,
2803
+ haloProgram,
2804
+ halftoneHaloProgram,
2805
+ chromaticHaloProgram,
2806
+ chromaticGlassPlateProgram,
2807
+ glassRefractionProgram,
2808
+ emberSlitsProgram,
2809
+ smearOrbProgram,
2810
+ pixelConstellationProgram,
2811
+ plasmaGaugeProgram,
2812
+ tickScaleProgram,
2813
+ rulerProgram,
2814
+ buttonProgram,
2815
+ prismSlatsProgram,
2816
+ eclipseOrbProgram,
2817
+ emberMatrixProgram,
2818
+ violetGlassProgram,
2819
+ violetGlassLightProgram,
2820
+ lumenPortalProgram,
2821
+ lumenPortalLightProgram
2822
+ ];
2823
+ var programs = new Map(builtins.map((program) => [program.name, program]));
2824
+ function registerShaderProgram(program) {
2825
+ if (!program.name.trim()) {
2826
+ throw new Error("registerShaderProgram: name must not be empty.");
2768
2827
  }
2769
- const svgBounds = svg.getBoundingClientRect();
2770
- const surfaceBounds = surface.canvas.getBoundingClientRect();
2771
- if (svgBounds.width <= 0 || svgBounds.height <= 0) {
2772
- return;
2828
+ if (!program.fragmentSource.includes("void main")) {
2829
+ throw new Error(`registerShaderProgram: "${program.name}" has no main function.`);
2773
2830
  }
2774
- surface.backdropOrigin = [
2775
- (surfaceBounds.left - svgBounds.left) / svgBounds.width,
2776
- (svgBounds.bottom - surfaceBounds.bottom) / svgBounds.height
2777
- ];
2778
- surface.backdropScale = [surfaceBounds.width / svgBounds.width, surfaceBounds.height / svgBounds.height];
2831
+ programs.set(program.name, program);
2832
+ return () => {
2833
+ if (programs.get(program.name) === program) {
2834
+ programs.delete(program.name);
2835
+ }
2836
+ };
2779
2837
  }
2780
- function applyBackdrop(surface, backdrop) {
2781
- if (!surface.backdropTexture) {
2782
- return;
2783
- }
2784
- syncBackdropBounds(surface);
2785
- const { gl } = surface;
2786
- gl.bindTexture(gl.TEXTURE_2D, surface.backdropTexture);
2787
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
2788
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, backdrop.image);
2838
+ function shaderProgram(name) {
2839
+ return programs.get(name);
2789
2840
  }
2790
- var MAX_SURFACE_PIXELS = 2560;
2791
- function renderSurface(surface, seconds) {
2792
- const { canvas, gl, parameters: parameters3 } = surface;
2793
- const shown = canvas.getBoundingClientRect();
2794
- const density = Math.min(globalThis.devicePixelRatio || 1, 3);
2795
- const fit = Math.min(1, MAX_SURFACE_PIXELS / Math.max(1, shown.width * density, shown.height * density));
2796
- const width = Math.max(1, Math.round((shown.width || canvas.clientWidth) * density * fit));
2797
- const height = Math.max(1, Math.round((shown.height || canvas.clientHeight) * density * fit));
2798
- if (canvas.width !== width || canvas.height !== height) {
2799
- canvas.width = width;
2800
- canvas.height = height;
2801
- }
2802
- gl.viewport(0, 0, width, height);
2803
- gl.clearColor(0, 0, 0, 0);
2804
- gl.clear(gl.COLOR_BUFFER_BIT);
2805
- gl.useProgram(surface.program);
2806
- const resolution = gl.getUniformLocation(surface.program, "uResolution");
2807
- if (resolution) {
2808
- gl.uniform2f(resolution, width, height);
2841
+
2842
+ // src/runtime/viewer/shaders/backdrop.ts
2843
+ var MIN_BACKDROP_DENSITY = 2.5;
2844
+ var MAX_BACKDROP_PIXELS = 4096;
2845
+ var TRANSPARENT = /^rgba?(s*d+,s*d+,s*d+,s*0s*)$|^transparent$/;
2846
+ function hostBackground(svg) {
2847
+ const view = svg.ownerDocument.defaultView;
2848
+ for (let element = svg.parentElement; element && view; element = element.parentElement) {
2849
+ const color2 = view.getComputedStyle(element).backgroundColor;
2850
+ if (color2 && !TRANSPARENT.test(color2.trim())) {
2851
+ return color2;
2852
+ }
2809
2853
  }
2810
- uniform1f(surface, "uTime", seconds);
2811
- if (surface.backdropTexture) {
2812
- syncBackdropBounds(surface);
2813
- gl.activeTexture(gl.TEXTURE0);
2814
- gl.bindTexture(gl.TEXTURE_2D, surface.backdropTexture);
2815
- const sampler = gl.getUniformLocation(surface.program, "uBackdrop");
2816
- if (sampler) {
2817
- gl.uniform1i(sampler, 0);
2854
+ return void 0;
2855
+ }
2856
+ function backdropClone(svg, owner, width, height) {
2857
+ const clone = svg.cloneNode(true);
2858
+ const ownerIndex = [...svg.children].indexOf(owner);
2859
+ if (ownerIndex >= 0) {
2860
+ while (clone.children.length > ownerIndex) {
2861
+ clone.lastElementChild?.remove();
2818
2862
  }
2819
- uniform2f(surface, "uBackdropOrigin", surface.backdropOrigin);
2820
- uniform2f(surface, "uBackdropScale", surface.backdropScale);
2821
2863
  }
2822
- for (const [key, fallback] of Object.entries(SHADER_DEFAULTS.numbers)) {
2823
- uniform1f(surface, uniformName(key), number(parameters3[key], fallback));
2864
+ for (const surface of clone.querySelectorAll("foreignObject[data-sf-shader]")) {
2865
+ surface.remove();
2824
2866
  }
2825
- for (const [key, fallback] of Object.entries(SHADER_DEFAULTS.colors)) {
2826
- uniform3f(surface, uniformName(key), color(parameters3[key], [...fallback]));
2867
+ const background = hostBackground(svg);
2868
+ if (background) {
2869
+ const { x, y, width: viewWidth, height: viewHeight } = svg.viewBox.baseVal;
2870
+ const fill = svg.ownerDocument.createElementNS("http://www.w3.org/2000/svg", "rect");
2871
+ fill.setAttribute("x", String(x));
2872
+ fill.setAttribute("y", String(y));
2873
+ fill.setAttribute("width", String(viewWidth));
2874
+ fill.setAttribute("height", String(viewHeight));
2875
+ fill.setAttribute("fill", background);
2876
+ clone.insertBefore(fill, clone.firstChild);
2827
2877
  }
2828
- uniform3f(surface, "uShapeColor", surface.shapeColor);
2829
- const elapsed = surface.lastSeconds === void 0 ? 0 : Math.max(0, Math.min(seconds - surface.lastSeconds, 0.1));
2830
- surface.lastSeconds = seconds;
2831
- applyParameters(surface, elapsed);
2832
- gl.drawArrays(gl.TRIANGLES, 0, 6);
2878
+ clone.setAttribute("width", String(width));
2879
+ clone.setAttribute("height", String(height));
2880
+ clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2881
+ return clone;
2833
2882
  }
2834
-
2835
- // src/runtime/viewer/shaders/surfaceClock.ts
2836
- function createSurfaceClock(view) {
2837
- let pausedSince = null;
2838
- let spentPaused = 0;
2839
- const real = () => (view?.performance.now() ?? 0) / 1e3;
2840
- return {
2841
- now: () => (pausedSince ?? real()) - spentPaused,
2842
- paused: () => pausedSince !== null,
2843
- spent: () => spentPaused,
2844
- setPaused(paused) {
2845
- if (paused && pausedSince === null) {
2846
- pausedSince = real();
2847
- } else if (!paused && pausedSince !== null) {
2848
- spentPaused += real() - pausedSince;
2849
- pausedSince = null;
2850
- }
2851
- }
2852
- };
2883
+ function captureShaderBackdrop(svg, owner) {
2884
+ const density = Math.max(globalThis.devicePixelRatio || 1, MIN_BACKDROP_DENSITY);
2885
+ const scale = Math.min(density, MAX_BACKDROP_PIXELS / Math.max(1, Math.max(svg.clientWidth, svg.clientHeight)));
2886
+ const width = Math.max(1, Math.round(svg.clientWidth * scale));
2887
+ const height = Math.max(1, Math.round(svg.clientHeight * scale));
2888
+ const view = svg.ownerDocument.defaultView;
2889
+ if (!view) {
2890
+ return Promise.resolve(void 0);
2891
+ }
2892
+ const markup = new XMLSerializer().serializeToString(backdropClone(svg, owner, width, height));
2893
+ const image = svg.ownerDocument.createElement("img");
2894
+ const source = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
2895
+ return new Promise((resolve) => {
2896
+ image.onload = () => resolve({ image, width, height });
2897
+ image.onerror = () => resolve(void 0);
2898
+ image.src = source;
2899
+ });
2853
2900
  }
2854
2901
 
2855
- // src/runtime/viewer/shaders/surface.ts
2856
- var RECAPTURE_DELAY_MS = 120;
2857
- function capturePngs(svg, surfaces, seconds) {
2858
- const surfaceByCanvas = new Map(surfaces.map((surface) => [surface.canvas, surface]));
2859
- return [...svg.querySelectorAll("canvas[data-sf-shader-canvas]")].map((canvas) => {
2860
- const surface = surfaceByCanvas.get(canvas);
2861
- if (!surface) {
2862
- return void 0;
2863
- }
2864
- try {
2865
- renderSurface(surface, seconds);
2866
- return surface.canvas.toDataURL("image/png");
2867
- } catch {
2868
- return void 0;
2869
- }
2870
- });
2902
+ // src/runtime/viewer/shaders/uniforms.ts
2903
+ function color(value, fallback) {
2904
+ if (typeof value !== "string") {
2905
+ return fallback;
2906
+ }
2907
+ const match = /^#([0-9a-f]{6})$/i.exec(value);
2908
+ if (!match) {
2909
+ return fallback;
2910
+ }
2911
+ const integer = Number.parseInt(match[1], 16);
2912
+ return [(integer >> 16 & 255) / 255, (integer >> 8 & 255) / 255, (integer & 255) / 255];
2871
2913
  }
2872
- function updateParameters(surfaces, ownerId, parameters3, seconds) {
2873
- let changed = false;
2874
- for (const surface of surfaces) {
2875
- if (surface.owner.getAttribute("data-sf-id") !== ownerId) {
2876
- continue;
2877
- }
2878
- Object.assign(surface.parameters, parameters3);
2879
- surface.owner.setAttribute("data-sf-shader-params", JSON.stringify(surface.parameters));
2880
- renderSurface(surface, seconds);
2881
- changed = true;
2914
+ function number(value, fallback) {
2915
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
2916
+ }
2917
+ function uniform1f(surface, name, value) {
2918
+ const location = surface.gl.getUniformLocation(surface.program, name);
2919
+ if (location) {
2920
+ surface.gl.uniform1f(location, value);
2882
2921
  }
2883
- return changed;
2884
2922
  }
2885
- function watchSvgChanges(svg, view, onChange) {
2886
- if (!view) {
2887
- return () => void 0;
2923
+ function uniform3f(surface, name, value) {
2924
+ const location = surface.gl.getUniformLocation(surface.program, name);
2925
+ if (location) {
2926
+ surface.gl.uniform3f(location, ...value);
2888
2927
  }
2889
- const mutations = new view.MutationObserver(onChange);
2890
- mutations.observe(svg, { attributes: true, attributeFilter: ["viewBox"] });
2891
- const sizes = view.ResizeObserver ? new view.ResizeObserver(onChange) : void 0;
2892
- sizes?.observe(svg);
2893
- return () => {
2894
- mutations.disconnect();
2895
- sizes?.disconnect();
2896
- };
2897
2928
  }
2898
- function mountShaderSurfaces(svg) {
2899
- let surfaces = [];
2900
- let frame;
2901
- let backdropGeneration = 0;
2902
- let recapture;
2903
- const view = svg.ownerDocument.defaultView;
2904
- const clock = createSurfaceClock(view);
2905
- const draw = (milliseconds) => {
2906
- if (!clock.paused()) {
2907
- for (const surface of surfaces) {
2908
- renderSurface(surface, milliseconds / 1e3 - clock.spent());
2909
- }
2910
- }
2911
- frame = view?.requestAnimationFrame(draw);
2912
- };
2913
- const stop = () => {
2914
- if (frame !== void 0) {
2915
- view?.cancelAnimationFrame(frame);
2916
- frame = void 0;
2917
- }
2918
- for (const surface of surfaces) {
2919
- releaseSurface(surface);
2920
- }
2921
- surfaces = [];
2929
+ function uniform2f(surface, name, value) {
2930
+ const location = surface.gl.getUniformLocation(surface.program, name);
2931
+ if (location) {
2932
+ surface.gl.uniform2f(location, ...value);
2933
+ }
2934
+ }
2935
+ function uniformName(parameter) {
2936
+ return `u${parameter.charAt(0).toUpperCase()}${parameter.slice(1)}`;
2937
+ }
2938
+ function enumParameter(name, value) {
2939
+ const values = {
2940
+ direction: { left: 0, right: 1, top: 2, bottom: 3 },
2941
+ orientation: { vertical: 0, horizontal: 1 },
2942
+ axis: { vertical: 0, horizontal: 1 }
2922
2943
  };
2923
- const captureBackdrops = (generation) => {
2924
- for (const surface of surfaces) {
2925
- if (!surface.usesBackdrop) {
2944
+ return values[name]?.[value];
2945
+ }
2946
+ function applyParameters(surface, elapsed) {
2947
+ for (const [key, value] of Object.entries(surface.parameters)) {
2948
+ const name = uniformName(key);
2949
+ if (typeof value === "number") {
2950
+ const lag = surface.parameters[`${key}Lag`];
2951
+ if (typeof lag === "number" && lag > 0) {
2952
+ const current = surface.smoothed.get(key) ?? value;
2953
+ const next = current + (value - current) * (1 - Math.exp(-elapsed / lag));
2954
+ surface.smoothed.set(key, next);
2955
+ uniform1f(surface, name, next);
2926
2956
  continue;
2927
2957
  }
2928
- void captureShaderBackdrop(svg, surface.owner).then((backdrop) => {
2929
- if (backdrop && generation === backdropGeneration) {
2930
- applyBackdrop(surface, backdrop);
2931
- }
2932
- });
2933
- }
2934
- };
2935
- const startFrames = () => {
2936
- if (!view?.requestAnimationFrame) {
2937
- for (const surface of surfaces) {
2938
- renderSurface(surface, 0);
2939
- }
2940
- return;
2958
+ uniform1f(surface, name, value);
2959
+ continue;
2941
2960
  }
2942
- if (clock.paused()) {
2943
- for (const surface of surfaces) {
2944
- renderSurface(surface, clock.now());
2961
+ if (typeof value === "boolean") {
2962
+ const location = surface.gl.getUniformLocation(surface.program, name);
2963
+ if (location) {
2964
+ surface.gl.uniform1i(location, value ? 1 : 0);
2945
2965
  }
2966
+ continue;
2946
2967
  }
2947
- frame = view.requestAnimationFrame(draw);
2948
- };
2949
- const refresh = () => {
2950
- stop();
2951
- const generation = ++backdropGeneration;
2952
- surfaces = [...svg.querySelectorAll("canvas[data-sf-shader-canvas]")].flatMap((canvas) => {
2953
- const surface = createSurface(canvas);
2954
- return surface ? [surface] : [];
2955
- });
2956
- captureBackdrops(generation);
2957
- if (surfaces.length > 0) {
2958
- startFrames();
2959
- }
2960
- };
2961
- const scheduleRecapture = () => {
2962
- if (recapture !== void 0) {
2963
- view?.clearTimeout(recapture);
2968
+ const enumValue = enumParameter(key, value);
2969
+ if (enumValue !== void 0) {
2970
+ uniform1f(surface, name, enumValue);
2971
+ continue;
2964
2972
  }
2965
- recapture = view?.setTimeout(() => {
2966
- recapture = void 0;
2967
- captureBackdrops(backdropGeneration);
2968
- }, RECAPTURE_DELAY_MS);
2969
- };
2970
- const unwatch = watchSvgChanges(svg, view, scheduleRecapture);
2971
- refresh();
2972
- const destroy = () => {
2973
- backdropGeneration += 1;
2974
- unwatch();
2975
- if (recapture !== void 0) {
2976
- view?.clearTimeout(recapture);
2973
+ if (/^#[0-9a-f]{6}$/i.test(value)) {
2974
+ uniform3f(surface, name, color(value, [0, 0, 0]));
2977
2975
  }
2978
- stop();
2979
- };
2980
- return {
2981
- capture: () => capturePngs(svg, surfaces, clock.now()),
2982
- setParameters: (ownerId, parameters3) => updateParameters(surfaces, ownerId, parameters3, clock.now()),
2983
- setPaused: clock.setPaused,
2984
- refresh,
2985
- destroy
2986
- };
2976
+ }
2987
2977
  }
2988
2978
 
2989
- // src/runtime/viewer/source.ts
2990
- function renderViewerSource(next, defaultRenderOptions) {
2991
- const svg = next.svg ?? (next.source != null ? chunkDDCTNLLD_cjs.render(next.source, { ...next.renderOptions ?? defaultRenderOptions, linkHitAreas: true }).svg : null);
2992
- if (svg == null) {
2993
- throw new Error("createViewer: provide either `source` (SFL) or `svg`.");
2994
- }
2995
- return svg;
2979
+ // src/runtime/viewer/shaders/webgl.ts
2980
+ var VERTEX_SOURCE = `
2981
+ attribute vec2 aPosition;
2982
+ varying vec2 vUv;
2983
+
2984
+ void main() {
2985
+ vUv = aPosition * 0.5 + 0.5;
2986
+ gl_Position = vec4(aPosition, 0.0, 1.0);
2996
2987
  }
2997
- function assertCreateViewerContract(svg) {
2998
- const version = chunkHFOGKSNU_cjs.parseContractVersion(svg);
2999
- if (!chunkHFOGKSNU_cjs.isContractCompatible(version)) {
3000
- throw new Error(`createViewer: incompatible DOM contract (svg=${version}). Renderer and viewer versions differ.`);
2988
+ `;
2989
+ function compile(gl, type, source) {
2990
+ const shader = gl.createShader(type);
2991
+ if (!shader) {
2992
+ return void 0;
3001
2993
  }
3002
- }
3003
- function assertViewerUpdateContract(svg) {
3004
- const version = chunkHFOGKSNU_cjs.parseContractVersion(svg);
3005
- if (!chunkHFOGKSNU_cjs.isContractCompatible(version)) {
3006
- throw new Error(`viewer.update: incompatible DOM contract (svg=${version}). Renderer and viewer versions differ.`);
2994
+ gl.shaderSource(shader, source);
2995
+ gl.compileShader(shader);
2996
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
2997
+ gl.deleteShader(shader);
2998
+ return void 0;
3007
2999
  }
3000
+ return shader;
3008
3001
  }
3009
-
3010
- // src/runtime/viewer/widgets/rollingNumberDom.ts
3011
- var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
3012
- var setStyles = (element, styles) => {
3013
- Object.assign(element.style, styles);
3014
- };
3015
- function createDigit(document, character, theme) {
3016
- const digit = document.createElementNS(HTML_NAMESPACE, "span");
3017
- digit.setAttribute("data-sf-rolling-current", "true");
3018
- digit.textContent = character;
3019
- setStyles(digit, {
3020
- position: "absolute",
3021
- inset: "0",
3022
- display: "grid",
3023
- placeItems: "center",
3024
- color: theme.color,
3025
- fontWeight: theme.fontWeight,
3026
- lineHeight: "1"
3027
- });
3028
- return digit;
3029
- }
3030
- function createDigitCell(document, character, theme) {
3031
- const cell = document.createElementNS(HTML_NAMESPACE, "span");
3032
- cell.setAttribute("data-sf-rolling-digit", "true");
3033
- if (theme.variant === "plain") {
3034
- setStyles(cell, {
3035
- position: "relative",
3036
- display: "inline-block",
3037
- width: "0.62em",
3038
- height: "1.1em",
3039
- overflow: "hidden"
3040
- });
3041
- cell.appendChild(createDigit(document, character, theme));
3042
- return cell;
3002
+ function linkProgram(gl, fragmentSource21) {
3003
+ const vertex = compile(gl, gl.VERTEX_SHADER, VERTEX_SOURCE);
3004
+ const fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource21);
3005
+ if (!vertex || !fragment) {
3006
+ if (vertex) {
3007
+ gl.deleteShader(vertex);
3008
+ }
3009
+ if (fragment) {
3010
+ gl.deleteShader(fragment);
3011
+ }
3012
+ return void 0;
3043
3013
  }
3044
- setStyles(cell, {
3045
- position: "relative",
3046
- display: "inline-block",
3047
- width: "0.72em",
3048
- height: "1.2em",
3049
- overflow: "hidden",
3050
- border: `1px solid ${theme.divider}`,
3051
- borderRadius: "0.09em",
3052
- background: theme.surface,
3053
- boxShadow: "inset 0 1px 0 rgba(255,255,255,0.7), inset 0 -0.16em 0.38em rgba(0,0,0,0.12)"
3054
- });
3055
- cell.appendChild(createDigit(document, character, theme));
3056
- const divider = document.createElementNS(HTML_NAMESPACE, "span");
3057
- setStyles(divider, {
3058
- position: "absolute",
3059
- zIndex: "2",
3060
- left: "0",
3061
- right: "0",
3062
- top: "50%",
3063
- height: "1px",
3064
- background: theme.divider,
3065
- pointerEvents: "none"
3066
- });
3067
- cell.appendChild(divider);
3068
- return cell;
3014
+ const program = gl.createProgram();
3015
+ if (!program) {
3016
+ gl.deleteShader(vertex);
3017
+ gl.deleteShader(fragment);
3018
+ return void 0;
3019
+ }
3020
+ gl.attachShader(program, vertex);
3021
+ gl.attachShader(program, fragment);
3022
+ gl.linkProgram(program);
3023
+ gl.deleteShader(vertex);
3024
+ gl.deleteShader(fragment);
3025
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
3026
+ gl.deleteProgram(program);
3027
+ return void 0;
3028
+ }
3029
+ return program;
3069
3030
  }
3070
- function animateDigit(element, keyframes, duration) {
3071
- if (duration === 0 || typeof element.animate !== "function") {
3072
- return null;
3031
+ function transparentTexture(gl) {
3032
+ const texture = gl.createTexture();
3033
+ if (!texture) {
3034
+ return void 0;
3073
3035
  }
3074
- return element.animate(keyframes, {
3075
- duration,
3076
- easing: "cubic-bezier(0.22, 1, 0.36, 1)",
3077
- fill: "forwards"
3078
- });
3036
+ gl.bindTexture(gl.TEXTURE_2D, texture);
3037
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
3038
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
3039
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
3040
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
3041
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
3042
+ return texture;
3079
3043
  }
3080
- function fadeDigit(cell, character, theme, duration) {
3081
- const previous = cell.querySelector("[data-sf-rolling-current]");
3082
- previous?.removeAttribute("data-sf-rolling-current");
3083
- const next = createDigit(cell.ownerDocument, character, theme);
3084
- next.style.opacity = "0";
3085
- cell.insertBefore(next, cell.lastElementChild ?? null);
3086
- const hidden = { opacity: 0, filter: "blur(0.025em)", transform: "scale(0.985)" };
3087
- const shown = { opacity: 1, filter: "blur(0)", transform: "scale(1)" };
3088
- const incoming = animateDigit(next, [hidden, shown], duration);
3089
- const outgoing = previous ? animateDigit(previous, [shown, hidden], duration) : null;
3090
- const finish = () => {
3091
- previous?.remove();
3092
- next.style.opacity = "1";
3093
- };
3094
- if (incoming) {
3095
- incoming.finished.then(finish, finish);
3096
- } else {
3097
- outgoing?.cancel();
3098
- finish();
3044
+
3045
+ // src/runtime/viewer/shaders/createSurface.ts
3046
+ var FULL_SCREEN_QUAD = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
3047
+ function parametersOf(owner) {
3048
+ try {
3049
+ return JSON.parse(owner.getAttribute("data-sf-shader-params") ?? "{}");
3050
+ } catch {
3051
+ return {};
3099
3052
  }
3100
3053
  }
3101
- function replaceDigit(cell, character, theme, duration) {
3102
- if (theme.motion === "fade") {
3103
- fadeDigit(cell, character, theme, duration);
3104
- return;
3054
+ function bindFullScreenQuad(gl, program) {
3055
+ const buffer = gl.createBuffer();
3056
+ if (!buffer) {
3057
+ return void 0;
3105
3058
  }
3106
- const previous = cell.querySelector("[data-sf-rolling-current]");
3107
- previous?.removeAttribute("data-sf-rolling-current");
3108
- const next = createDigit(cell.ownerDocument, character, theme);
3109
- next.style.transform = "translateY(100%)";
3110
- next.style.opacity = "0.25";
3111
- cell.insertBefore(next, cell.lastElementChild);
3112
- const incoming = animateDigit(
3113
- next,
3114
- [
3115
- { transform: "translateY(100%)", opacity: 0.25 },
3116
- { transform: "translateY(0)", opacity: 1 }
3117
- ],
3118
- duration
3119
- );
3120
- const outgoing = previous ? animateDigit(
3121
- previous,
3122
- [
3123
- { transform: "translateY(0)", opacity: 1 },
3124
- { transform: "translateY(-100%)", opacity: 0.25 }
3125
- ],
3126
- duration
3127
- ) : null;
3128
- const finish = () => {
3129
- previous?.remove();
3130
- next.style.transform = "translateY(0)";
3131
- next.style.opacity = "1";
3132
- };
3133
- if (incoming) {
3134
- incoming.finished.then(finish, finish);
3135
- } else {
3136
- outgoing?.cancel();
3137
- finish();
3059
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
3060
+ gl.bufferData(gl.ARRAY_BUFFER, FULL_SCREEN_QUAD, gl.STATIC_DRAW);
3061
+ const position = gl.getAttribLocation(program, "aPosition");
3062
+ if (position < 0) {
3063
+ gl.deleteBuffer(buffer);
3064
+ return void 0;
3138
3065
  }
3066
+ gl.enableVertexAttribArray(position);
3067
+ gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
3068
+ return buffer;
3139
3069
  }
3140
- function updateRollingDigits(digits, previousValue, nextValue, theme, duration) {
3141
- if (previousValue.length !== nextValue.length) {
3142
- digits.replaceChildren(...[...nextValue].map((character) => createDigitCell(digits.ownerDocument, character, theme)));
3143
- digits.setAttribute("aria-label", nextValue);
3144
- return;
3070
+ function findBaseGeometry(owner, definition) {
3071
+ const ownerId = owner.getAttribute("data-sf-id");
3072
+ if (!definition.replacesBaseFill || !ownerId) {
3073
+ return void 0;
3145
3074
  }
3146
- const cells = [...digits.querySelectorAll("[data-sf-rolling-digit]")];
3147
- [...nextValue].forEach((character, index) => {
3148
- if (previousValue[index] !== character) {
3149
- replaceDigit(cells[index], character, theme, duration);
3150
- }
3151
- });
3152
- digits.setAttribute("aria-label", nextValue);
3153
- }
3154
-
3155
- // src/runtime/viewer/widgets/documentWidgets.ts
3156
- function findRollingNumber(svg, widgetId) {
3157
- return [...svg.querySelectorAll('foreignObject[data-sf-widget="rollingNumber"]')].find(
3158
- (surface) => surface.getAttribute("data-sf-id") === widgetId
3159
- );
3075
+ const candidates = [...owner.closest("svg")?.querySelectorAll('[data-sf-primary="true"]') ?? []];
3076
+ return candidates.find((element) => element !== owner && element.getAttribute("data-sf-id") === ownerId);
3160
3077
  }
3161
- var FADE_INTERVAL = 140;
3162
- function widgetDigits(surface) {
3163
- return surface.querySelector("[data-sf-rolling-digits]") ?? void 0;
3078
+ function createSurface(canvas) {
3079
+ const owner = canvas.closest("[data-sf-shader]");
3080
+ const name = owner?.getAttribute("data-sf-shader");
3081
+ const definition = name ? shaderProgram(name) : void 0;
3082
+ const gl = canvas.getContext("webgl", { alpha: true, antialias: true, premultipliedAlpha: true });
3083
+ if (!owner || !definition || !gl) {
3084
+ return void 0;
3085
+ }
3086
+ const program = linkProgram(gl, definition.fragmentSource);
3087
+ if (!program) {
3088
+ return void 0;
3089
+ }
3090
+ gl.useProgram(program);
3091
+ const buffer = bindFullScreenQuad(gl, program);
3092
+ if (!buffer) {
3093
+ gl.deleteProgram(program);
3094
+ return void 0;
3095
+ }
3096
+ const usesBackdrop = definition.usesBackdrop === true;
3097
+ const baseGeometry = findBaseGeometry(owner, definition);
3098
+ const baseFill = baseGeometry?.getAttribute("fill");
3099
+ baseGeometry?.setAttribute("fill", "none");
3100
+ return {
3101
+ owner,
3102
+ canvas,
3103
+ gl,
3104
+ program,
3105
+ buffer,
3106
+ parameters: parametersOf(owner),
3107
+ smoothed: /* @__PURE__ */ new Map(),
3108
+ backdropTexture: usesBackdrop ? transparentTexture(gl) : void 0,
3109
+ backdropOrigin: [0, 0],
3110
+ backdropScale: [1, 1],
3111
+ usesBackdrop,
3112
+ shapeColor: color(owner.getAttribute("data-sf-shader-fill") ?? void 0, [1, 1, 1]),
3113
+ baseGeometry,
3114
+ baseFill
3115
+ };
3164
3116
  }
3165
- function widgetStyle(surface) {
3166
- const encoded = surface.getAttribute("data-sf-widget-style");
3167
- if (!encoded) {
3168
- return chunkDDCTNLLD_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
3117
+ function releaseSurface(surface) {
3118
+ if (surface.backdropTexture) {
3119
+ surface.gl.deleteTexture(surface.backdropTexture);
3169
3120
  }
3170
- try {
3171
- return { ...chunkDDCTNLLD_cjs.DEFAULT_ROLLING_NUMBER_STYLE, ...JSON.parse(encoded) };
3172
- } catch {
3173
- return chunkDDCTNLLD_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
3121
+ surface.gl.deleteBuffer(surface.buffer);
3122
+ surface.gl.deleteProgram(surface.program);
3123
+ if (!surface.baseGeometry) {
3124
+ return;
3125
+ }
3126
+ if (surface.baseFill === null || surface.baseFill === void 0) {
3127
+ surface.baseGeometry.removeAttribute("fill");
3128
+ } else {
3129
+ surface.baseGeometry.setAttribute("fill", surface.baseFill);
3174
3130
  }
3175
3131
  }
3176
- function positiveInteger(value) {
3177
- if (!value) {
3178
- return void 0;
3132
+
3133
+ // src/runtime/viewer/shaders/renderSurface.ts
3134
+ function syncBackdropBounds(surface) {
3135
+ const svg = surface.canvas.closest("svg");
3136
+ if (!svg) {
3137
+ return;
3179
3138
  }
3180
- const number2 = Number(value);
3181
- return Number.isInteger(number2) && number2 > 0 ? number2 : void 0;
3139
+ const svgBounds = svg.getBoundingClientRect();
3140
+ const surfaceBounds = surface.canvas.getBoundingClientRect();
3141
+ if (svgBounds.width <= 0 || svgBounds.height <= 0) {
3142
+ return;
3143
+ }
3144
+ surface.backdropOrigin = [
3145
+ (surfaceBounds.left - svgBounds.left) / svgBounds.width,
3146
+ (svgBounds.bottom - surfaceBounds.bottom) / svgBounds.height
3147
+ ];
3148
+ surface.backdropScale = [surfaceBounds.width / svgBounds.width, surfaceBounds.height / svgBounds.height];
3182
3149
  }
3183
- function durationFor(surface) {
3184
- const duration = Number(surface.getAttribute("data-sf-widget-duration"));
3185
- if (!Number.isFinite(duration) || duration < 0) {
3186
- return 420;
3150
+ function applyBackdrop(surface, backdrop) {
3151
+ if (!surface.backdropTexture) {
3152
+ return;
3187
3153
  }
3188
- const reduceMotion = surface.ownerDocument.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
3189
- return reduceMotion ? 0 : duration;
3154
+ syncBackdropBounds(surface);
3155
+ const { gl } = surface;
3156
+ gl.bindTexture(gl.TEXTURE_2D, surface.backdropTexture);
3157
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
3158
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, backdrop.image);
3190
3159
  }
3191
- function setRollingNumber(surface, value, animate) {
3192
- const digits = widgetDigits(surface);
3193
- if (!digits) {
3194
- return false;
3160
+ var MAX_SURFACE_PIXELS = 2560;
3161
+ function renderSurface(surface, seconds) {
3162
+ const { canvas, gl, parameters: parameters3 } = surface;
3163
+ const shown = canvas.getBoundingClientRect();
3164
+ const density = Math.min(globalThis.devicePixelRatio || 1, 3);
3165
+ const fit = Math.min(1, MAX_SURFACE_PIXELS / Math.max(1, shown.width * density, shown.height * density));
3166
+ const width = Math.max(1, Math.round((shown.width || canvas.clientWidth) * density * fit));
3167
+ const height = Math.max(1, Math.round((shown.height || canvas.clientHeight) * density * fit));
3168
+ if (canvas.width !== width || canvas.height !== height) {
3169
+ canvas.width = width;
3170
+ canvas.height = height;
3195
3171
  }
3196
- const previousValue = surface.getAttribute("data-sf-widget-value") ?? "";
3197
- const nextValue = chunkDDCTNLLD_cjs.formatRollingNumber(value, positiveInteger(surface.getAttribute("data-sf-widget-digits")));
3198
- if (previousValue === nextValue) {
3199
- return true;
3172
+ gl.viewport(0, 0, width, height);
3173
+ gl.clearColor(0, 0, 0, 0);
3174
+ gl.clear(gl.COLOR_BUFFER_BIT);
3175
+ gl.useProgram(surface.program);
3176
+ const resolution = gl.getUniformLocation(surface.program, "uResolution");
3177
+ if (resolution) {
3178
+ gl.uniform2f(resolution, width, height);
3200
3179
  }
3201
- updateRollingDigits(digits, previousValue, nextValue, widgetStyle(surface), animate ? durationFor(surface) : 0);
3202
- surface.setAttribute("data-sf-widget-value", nextValue);
3203
- return true;
3180
+ uniform1f(surface, "uTime", seconds);
3181
+ if (surface.backdropTexture) {
3182
+ syncBackdropBounds(surface);
3183
+ gl.activeTexture(gl.TEXTURE0);
3184
+ gl.bindTexture(gl.TEXTURE_2D, surface.backdropTexture);
3185
+ const sampler = gl.getUniformLocation(surface.program, "uBackdrop");
3186
+ if (sampler) {
3187
+ gl.uniform1i(sampler, 0);
3188
+ }
3189
+ uniform2f(surface, "uBackdropOrigin", surface.backdropOrigin);
3190
+ uniform2f(surface, "uBackdropScale", surface.backdropScale);
3191
+ }
3192
+ for (const [key, fallback] of Object.entries(SHADER_DEFAULTS.numbers)) {
3193
+ uniform1f(surface, uniformName(key), number(parameters3[key], fallback));
3194
+ }
3195
+ for (const [key, fallback] of Object.entries(SHADER_DEFAULTS.colors)) {
3196
+ uniform3f(surface, uniformName(key), color(parameters3[key], [...fallback]));
3197
+ }
3198
+ uniform3f(surface, "uShapeColor", surface.shapeColor);
3199
+ const elapsed = surface.lastSeconds === void 0 ? 0 : Math.max(0, Math.min(seconds - surface.lastSeconds, 0.1));
3200
+ surface.lastSeconds = seconds;
3201
+ applyParameters(surface, elapsed);
3202
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
3204
3203
  }
3205
- function mountDocumentWidgets(svg) {
3206
- const valueOverrides = /* @__PURE__ */ new Map();
3207
- const lastApplied = /* @__PURE__ */ new Map();
3208
- const pending = /* @__PURE__ */ new Map();
3204
+
3205
+ // src/runtime/viewer/shaders/surfaceClock.ts
3206
+ function createSurfaceClock(view) {
3207
+ let pausedSince = null;
3208
+ let spentPaused = 0;
3209
+ const real = () => (view?.performance.now() ?? 0) / 1e3;
3209
3210
  return {
3210
- setValue(widgetId, value) {
3211
- const surface = findRollingNumber(svg, widgetId);
3212
- if (!surface) {
3213
- return false;
3214
- }
3215
- valueOverrides.set(widgetId, value);
3216
- if (widgetStyle(surface).motion !== "fade") {
3217
- return setRollingNumber(surface, value, true);
3218
- }
3219
- const now = Date.now();
3220
- const wait = (lastApplied.get(widgetId) ?? 0) + FADE_INTERVAL - now;
3221
- if (wait <= 0) {
3222
- lastApplied.set(widgetId, now);
3223
- return setRollingNumber(surface, value, true);
3224
- }
3225
- if (!pending.has(widgetId)) {
3226
- pending.set(
3227
- widgetId,
3228
- setTimeout(() => {
3229
- pending.delete(widgetId);
3230
- lastApplied.set(widgetId, Date.now());
3231
- const current = findRollingNumber(svg, widgetId);
3232
- const latest = valueOverrides.get(widgetId);
3233
- if (current && latest !== void 0) {
3234
- setRollingNumber(current, latest, true);
3235
- }
3236
- }, wait)
3237
- );
3238
- }
3239
- return true;
3240
- },
3241
- refresh() {
3242
- for (const [widgetId, value] of valueOverrides) {
3243
- const surface = findRollingNumber(svg, widgetId);
3244
- if (surface) {
3245
- setRollingNumber(surface, value, false);
3246
- }
3247
- }
3248
- },
3249
- clearValues() {
3250
- for (const timer of pending.values()) {
3251
- clearTimeout(timer);
3252
- }
3253
- pending.clear();
3254
- valueOverrides.clear();
3255
- },
3256
- destroy() {
3257
- for (const timer of pending.values()) {
3258
- clearTimeout(timer);
3211
+ now: () => (pausedSince ?? real()) - spentPaused,
3212
+ paused: () => pausedSince !== null,
3213
+ spent: () => spentPaused,
3214
+ setPaused(paused) {
3215
+ if (paused && pausedSince === null) {
3216
+ pausedSince = real();
3217
+ } else if (!paused && pausedSince !== null) {
3218
+ spentPaused += real() - pausedSince;
3219
+ pausedSince = null;
3259
3220
  }
3260
- pending.clear();
3261
- valueOverrides.clear();
3262
3221
  }
3263
3222
  };
3264
3223
  }
3265
3224
 
3266
- // src/runtime/viewer/widgets/rulerKnob.ts
3267
- var bases = /* @__PURE__ */ new WeakMap();
3268
- var written = /* @__PURE__ */ new WeakMap();
3269
- var fills = /* @__PURE__ */ new WeakMap();
3270
- function clamp01(value) {
3271
- return Math.min(1, Math.max(0, Number.isFinite(value) ? value : 0));
3272
- }
3273
- function parameters(surface) {
3274
- try {
3275
- return JSON.parse(surface.getAttribute("data-sf-shader-params") ?? "{}");
3276
- } catch {
3277
- return {};
3278
- }
3279
- }
3280
- function positive2(value, fallback) {
3281
- return Number(value) > 0 ? Number(value) : fallback;
3282
- }
3283
- function rulerProgress(surface, value) {
3284
- const params = parameters(surface);
3285
- const min = Number(params.min) || 0;
3286
- const max = Number.isFinite(Number(params.max)) && params.max !== void 0 ? Number(params.max) : 100;
3287
- return clamp01(max === min ? 0 : (value - min) / (max - min));
3288
- }
3289
- function move(node, dx, dy) {
3290
- const current = node.getAttribute("transform") ?? "";
3291
- const base = current === written.get(node) ? bases.get(node) ?? "" : current;
3292
- bases.set(node, base);
3293
- const next = `translate(${dx} ${dy}) ${base}`.trim();
3294
- written.set(node, next);
3295
- node.setAttribute("transform", next);
3225
+ // src/runtime/viewer/shaders/surface.ts
3226
+ var RECAPTURE_DELAY_MS = 120;
3227
+ function capturePngs(svg, surfaces, seconds) {
3228
+ const surfaceByCanvas = new Map(surfaces.map((surface) => [surface.canvas, surface]));
3229
+ return [...svg.querySelectorAll("canvas[data-sf-shader-canvas]")].map((canvas) => {
3230
+ const surface = surfaceByCanvas.get(canvas);
3231
+ if (!surface) {
3232
+ return void 0;
3233
+ }
3234
+ try {
3235
+ renderSurface(surface, seconds);
3236
+ return surface.canvas.toDataURL("image/png");
3237
+ } catch {
3238
+ return void 0;
3239
+ }
3240
+ });
3296
3241
  }
3297
- function hexToRgb(value) {
3298
- const match = /^#([0-9a-f]{6})$/i.exec(value.trim());
3299
- if (!match) {
3300
- return null;
3242
+ function updateParameters(surfaces, ownerId, parameters3, seconds) {
3243
+ let changed = false;
3244
+ for (const surface of surfaces) {
3245
+ if (surface.owner.getAttribute("data-sf-id") !== ownerId) {
3246
+ continue;
3247
+ }
3248
+ Object.assign(surface.parameters, parameters3);
3249
+ surface.owner.setAttribute("data-sf-shader-params", JSON.stringify(surface.parameters));
3250
+ renderSurface(surface, seconds);
3251
+ changed = true;
3301
3252
  }
3302
- const number2 = parseInt(match[1], 16);
3303
- return [number2 >> 16 & 255, number2 >> 8 & 255, number2 & 255];
3253
+ return changed;
3304
3254
  }
3305
- function shade(node, bump) {
3306
- const base = fills.get(node) ?? node.getAttribute("fill") ?? "";
3307
- fills.set(node, base);
3308
- const rgb = hexToRgb(base);
3309
- if (!rgb) {
3310
- return;
3311
- }
3312
- const mixed = rgb.map((channel) => Math.round(channel + (255 - channel) * bump));
3313
- const fill = `rgb(${mixed[0]}, ${mixed[1]}, ${mixed[2]})`;
3314
- node.style.fill = fill;
3315
- for (const child of node.querySelectorAll("tspan")) {
3316
- child.style.fill = fill;
3255
+ function watchSvgChanges(svg, view, onChange) {
3256
+ if (!view) {
3257
+ return () => void 0;
3317
3258
  }
3259
+ const mutations = new view.MutationObserver(onChange);
3260
+ mutations.observe(svg, { attributes: true, attributeFilter: ["viewBox"] });
3261
+ const sizes = view.ResizeObserver ? new view.ResizeObserver(onChange) : void 0;
3262
+ sizes?.observe(svg);
3263
+ return () => {
3264
+ mutations.disconnect();
3265
+ sizes?.disconnect();
3266
+ };
3318
3267
  }
3319
- function placeRulerKnobs(svg, progressOf) {
3320
- const radii = /* @__PURE__ */ new Map();
3321
- for (const surface of svg.querySelectorAll('foreignObject[data-sf-shader="ruler"]')) {
3322
- const params = parameters(surface);
3323
- const id = surface.getAttribute("data-sf-id") ?? "";
3324
- const x = Number(surface.getAttribute("x"));
3325
- const y = Number(surface.getAttribute("y"));
3326
- const width = Number(surface.getAttribute("width"));
3327
- const height = Number(surface.getAttribute("height"));
3328
- const horizontal = params.axis === "horizontal";
3329
- const progress = clamp01(progressOf(id) ?? Number(params.progress));
3330
- const inset = positive2(params.inset, 0.05);
3331
- const line = positive2(params.line, 0.7);
3332
- const extent = horizontal ? width : height;
3333
- const across = horizontal ? height : width;
3334
- const along = inset + (1 - 2 * inset) * progress;
3335
- const dent = params.dent === void 0 ? 1 : Number(params.dent);
3336
- const depth = (dent < 0 ? 1 : dent) * 0.16 * across;
3337
- const knobId = typeof params.knob === "string" ? params.knob : "";
3338
- if (knobId) {
3339
- const nodes = [...svg.querySelectorAll(`[data-sf-id="${CSS.escape(knobId)}"]`)];
3340
- const primary = nodes.find((node) => node.hasAttribute("data-sf-primary"));
3341
- if (primary?.getBBox) {
3342
- move(primary, 0, 0);
3343
- const box = primary.getBBox();
3344
- const half = (horizontal ? box.height : box.width) / 2;
3345
- radii.set(id, half / across);
3346
- const gap = (params.gap === void 0 ? 0.04 : Number(params.gap)) * across;
3347
- const side = line * across - depth + half + gap;
3348
- const target = horizontal ? { x: x + width * along, y: y + side } : { x: x + side, y: y + height * (1 - along) };
3349
- const dx = target.x - (box.x + box.width / 2);
3350
- const dy = target.y - (box.y + box.height / 2);
3351
- for (const node of nodes) {
3352
- move(node, dx, dy);
3353
- }
3268
+ function mountShaderSurfaces(svg) {
3269
+ let surfaces = [];
3270
+ let frame;
3271
+ let backdropGeneration = 0;
3272
+ let recapture;
3273
+ const view = svg.ownerDocument.defaultView;
3274
+ const clock = createSurfaceClock(view);
3275
+ const draw = (milliseconds) => {
3276
+ if (!clock.paused()) {
3277
+ for (const surface of surfaces) {
3278
+ renderSurface(surface, milliseconds / 1e3 - clock.spent());
3354
3279
  }
3355
3280
  }
3356
- const min = Number(params.min) || 0;
3357
- const max = Number.isFinite(Number(params.max)) && params.max !== void 0 ? Number(params.max) : 100;
3358
- const spread = positive2(params.spread, 0.07) * extent;
3359
- const start = inset * extent;
3360
- const span = (1 - 2 * inset) * extent;
3361
- const knobAt = start + span * progress;
3362
- for (const label of svg.querySelectorAll(`text[data-sf-id="${CSS.escape(id)}"]`)) {
3363
- const value = Number(label.textContent);
3364
- if (!Number.isFinite(value) || max === min) {
3281
+ frame = view?.requestAnimationFrame(draw);
3282
+ };
3283
+ const stop = () => {
3284
+ if (frame !== void 0) {
3285
+ view?.cancelAnimationFrame(frame);
3286
+ frame = void 0;
3287
+ }
3288
+ for (const surface of surfaces) {
3289
+ releaseSurface(surface);
3290
+ }
3291
+ surfaces = [];
3292
+ };
3293
+ const captureBackdrops = (generation) => {
3294
+ for (const surface of surfaces) {
3295
+ if (!surface.usesBackdrop) {
3365
3296
  continue;
3366
3297
  }
3367
- const labelAt = start + span * clamp01((value - min) / (max - min));
3368
- const bump = Math.exp(-(((labelAt - knobAt) / spread) ** 2));
3369
- const shift = depth * 0.4 * bump;
3370
- move(label, horizontal ? 0 : -shift, horizontal ? -shift : 0);
3371
- shade(label, bump);
3298
+ void captureShaderBackdrop(svg, surface.owner).then((backdrop) => {
3299
+ if (backdrop && generation === backdropGeneration) {
3300
+ applyBackdrop(surface, backdrop);
3301
+ }
3302
+ });
3303
+ }
3304
+ };
3305
+ const startFrames = () => {
3306
+ if (!view?.requestAnimationFrame) {
3307
+ for (const surface of surfaces) {
3308
+ renderSurface(surface, 0);
3309
+ }
3310
+ return;
3311
+ }
3312
+ if (clock.paused()) {
3313
+ for (const surface of surfaces) {
3314
+ renderSurface(surface, clock.now());
3315
+ }
3316
+ }
3317
+ frame = view.requestAnimationFrame(draw);
3318
+ };
3319
+ const refresh = () => {
3320
+ stop();
3321
+ const generation = ++backdropGeneration;
3322
+ surfaces = [...svg.querySelectorAll("canvas[data-sf-shader-canvas]")].flatMap((canvas) => {
3323
+ const surface = createSurface(canvas);
3324
+ return surface ? [surface] : [];
3325
+ });
3326
+ captureBackdrops(generation);
3327
+ if (surfaces.length > 0) {
3328
+ startFrames();
3329
+ }
3330
+ };
3331
+ const scheduleRecapture = () => {
3332
+ if (recapture !== void 0) {
3333
+ view?.clearTimeout(recapture);
3334
+ }
3335
+ recapture = view?.setTimeout(() => {
3336
+ recapture = void 0;
3337
+ captureBackdrops(backdropGeneration);
3338
+ }, RECAPTURE_DELAY_MS);
3339
+ };
3340
+ const unwatch = watchSvgChanges(svg, view, scheduleRecapture);
3341
+ refresh();
3342
+ const destroy = () => {
3343
+ backdropGeneration += 1;
3344
+ unwatch();
3345
+ if (recapture !== void 0) {
3346
+ view?.clearTimeout(recapture);
3372
3347
  }
3348
+ stop();
3349
+ };
3350
+ return {
3351
+ capture: () => capturePngs(svg, surfaces, clock.now()),
3352
+ setParameters: (ownerId, parameters3) => updateParameters(surfaces, ownerId, parameters3, clock.now()),
3353
+ setPaused: clock.setPaused,
3354
+ refresh,
3355
+ destroy
3356
+ };
3357
+ }
3358
+
3359
+ // src/runtime/viewer/source.ts
3360
+ function renderViewerSource(next, defaultRenderOptions) {
3361
+ const svg = next.svg ?? (next.source != null ? chunkZMLQIMJK_cjs.render(next.source, { ...next.renderOptions ?? defaultRenderOptions, linkHitAreas: true }).svg : null);
3362
+ if (svg == null) {
3363
+ throw new Error("createViewer: provide either `source` (SFL) or `svg`.");
3364
+ }
3365
+ return svg;
3366
+ }
3367
+ function assertCreateViewerContract(svg) {
3368
+ const version = chunkL3VJXZFD_cjs.parseContractVersion(svg);
3369
+ if (!chunkL3VJXZFD_cjs.isContractCompatible(version)) {
3370
+ throw new Error(`createViewer: incompatible DOM contract (svg=${version}). Renderer and viewer versions differ.`);
3371
+ }
3372
+ }
3373
+ function assertViewerUpdateContract(svg) {
3374
+ const version = chunkL3VJXZFD_cjs.parseContractVersion(svg);
3375
+ if (!chunkL3VJXZFD_cjs.isContractCompatible(version)) {
3376
+ throw new Error(`viewer.update: incompatible DOM contract (svg=${version}). Renderer and viewer versions differ.`);
3373
3377
  }
3374
- return radii;
3375
3378
  }
3376
3379
 
3377
3380
  // src/runtime/viewer/session.ts
@@ -3381,7 +3384,7 @@ var ViewerSession = class {
3381
3384
  this.diagramId = diagramId;
3382
3385
  this.display = mountDisplay(options.container, initialSvg);
3383
3386
  this.shaderSurfaces = mountShaderSurfaces(this.display.svg);
3384
- this.documentWidgets = mountDocumentWidgets(this.display.svg);
3387
+ this.documentComponents = mountDocumentComponents(this.display.svg);
3385
3388
  this.interaction = options.interactive ? bindInteraction(this.display, this.emitter, diagramId, { selectable: options.selectable }) : null;
3386
3389
  this.placeKnobs();
3387
3390
  }
@@ -3391,7 +3394,7 @@ var ViewerSession = class {
3391
3394
  display;
3392
3395
  interaction;
3393
3396
  shaderSurfaces;
3394
- documentWidgets;
3397
+ documentComponents;
3395
3398
  shaderParameterOverrides = /* @__PURE__ */ new Map();
3396
3399
  rulerProgressOverrides = /* @__PURE__ */ new Map();
3397
3400
  placeKnobs() {
@@ -3430,22 +3433,25 @@ var ViewerSession = class {
3430
3433
  this.shaderParameterOverrides.set(ownerId, merged);
3431
3434
  return this.shaderSurfaces.setParameters(ownerId, parameters3);
3432
3435
  }
3433
- setWidgetValue(widgetId, value) {
3434
- if (this.documentWidgets.setValue(widgetId, value)) {
3436
+ setWidgetValue(componentId, value) {
3437
+ return this.setComponentValue(componentId, value);
3438
+ }
3439
+ setComponentValue(componentId, value) {
3440
+ if (this.documentComponents.setValue(componentId, value)) {
3435
3441
  return true;
3436
3442
  }
3437
3443
  const ruler = [...this.svg.querySelectorAll('foreignObject[data-sf-shader="ruler"]')].find(
3438
- (element) => element.getAttribute("data-sf-id") === widgetId
3444
+ (element) => element.getAttribute("data-sf-id") === componentId
3439
3445
  );
3440
3446
  if (ruler) {
3441
3447
  const progress = rulerProgress(ruler, Number(value));
3442
- this.rulerProgressOverrides.set(widgetId, progress);
3443
- const applied = this.setShaderParameters(widgetId, { progress });
3448
+ this.rulerProgressOverrides.set(componentId, progress);
3449
+ const applied = this.setShaderParameters(componentId, { progress });
3444
3450
  this.placeKnobs();
3445
3451
  return applied;
3446
3452
  }
3447
3453
  const scale = [...this.svg.querySelectorAll('foreignObject[data-sf-shader="tickScale"]')].find(
3448
- (element) => element.getAttribute("data-sf-id") === widgetId
3454
+ (element) => element.getAttribute("data-sf-id") === componentId
3449
3455
  );
3450
3456
  if (!scale) {
3451
3457
  return false;
@@ -3456,7 +3462,7 @@ var ViewerSession = class {
3456
3462
  } catch {
3457
3463
  max = 100;
3458
3464
  }
3459
- return this.setShaderParameters(widgetId, { progress: Math.min(1, Math.max(0, Number(value) / max)) });
3465
+ return this.setShaderParameters(componentId, { progress: Math.min(1, Math.max(0, Number(value) / max)) });
3460
3466
  }
3461
3467
  setPaused(paused) {
3462
3468
  this.shaderSurfaces.setPaused(paused);
@@ -3464,14 +3470,14 @@ var ViewerSession = class {
3464
3470
  clearOverrides() {
3465
3471
  this.shaderParameterOverrides.clear();
3466
3472
  this.rulerProgressOverrides.clear();
3467
- this.documentWidgets.clearValues();
3473
+ this.documentComponents.clearValues();
3468
3474
  }
3469
3475
  update(next) {
3470
3476
  const svg = renderViewerSource(next, this.options.renderOptions);
3471
3477
  assertViewerUpdateContract(svg);
3472
3478
  const diff = this.display.updateContent(svg);
3473
3479
  this.shaderSurfaces.refresh();
3474
- this.documentWidgets.refresh();
3480
+ this.documentComponents.refresh();
3475
3481
  this.placeKnobs();
3476
3482
  for (const [ownerId, parameters3] of this.shaderParameterOverrides) {
3477
3483
  this.shaderSurfaces.setParameters(ownerId, parameters3);
@@ -3495,13 +3501,52 @@ var ViewerSession = class {
3495
3501
  destroy() {
3496
3502
  this.interaction?.destroy();
3497
3503
  this.shaderSurfaces.destroy();
3498
- this.documentWidgets.destroy();
3504
+ this.documentComponents.destroy();
3499
3505
  this.shaderParameterOverrides.clear();
3500
3506
  this.display.destroy();
3501
3507
  this.emitter.clear();
3502
3508
  }
3503
3509
  };
3504
3510
 
3511
+ // src/runtime/viewer/connectKeyboard.ts
3512
+ var ACTIVATION_KEYS = /* @__PURE__ */ new Set([" ", "Enter"]);
3513
+ function operatedId(runtime, target) {
3514
+ const node = target instanceof Element ? target.closest("[data-sf-primary][data-sf-id]") : null;
3515
+ const id = node?.getAttribute("data-sf-id") ?? null;
3516
+ return id && runtime.interactive.pressable.has(id) ? id : null;
3517
+ }
3518
+ function connectKeyboard(viewer, runtime, options = {}) {
3519
+ const focusChanged = (event, focused) => {
3520
+ const id = operatedId(runtime, event.target);
3521
+ if (id) {
3522
+ runtime.setElementState(id, { focused });
3523
+ }
3524
+ };
3525
+ const activate = (event) => {
3526
+ if (!ACTIVATION_KEYS.has(event.key) || event.repeat || options.enabled?.() === false) {
3527
+ return;
3528
+ }
3529
+ const id = operatedId(runtime, event.target);
3530
+ if (id) {
3531
+ event.preventDefault();
3532
+ runtime.activate(id);
3533
+ }
3534
+ };
3535
+ const listeners = [
3536
+ ["focusin", (event) => focusChanged(event, true)],
3537
+ ["focusout", (event) => focusChanged(event, false)],
3538
+ ["keydown", activate]
3539
+ ];
3540
+ for (const [type, listener] of listeners) {
3541
+ viewer.svg.addEventListener(type, listener);
3542
+ }
3543
+ return () => {
3544
+ for (const [type, listener] of listeners) {
3545
+ viewer.svg.removeEventListener(type, listener);
3546
+ }
3547
+ };
3548
+ }
3549
+
3505
3550
  // src/runtime/viewer/connectInteraction.ts
3506
3551
  var TAU = Math.PI * 2;
3507
3552
  var LONGEST_TICK = 0.05;
@@ -3650,11 +3695,13 @@ function connectInteraction(viewer, runtime, options = {}) {
3650
3695
  for (const [type, listener] of listeners) {
3651
3696
  window.addEventListener(type, listener, true);
3652
3697
  }
3698
+ const disconnectKeyboard = connectKeyboard(viewer, runtime, options);
3653
3699
  const stopTicks = startTicks(runtime, options);
3654
3700
  return () => {
3655
3701
  for (const [type, listener] of listeners) {
3656
3702
  window.removeEventListener(type, listener, true);
3657
3703
  }
3704
+ disconnectKeyboard();
3658
3705
  stopTicks();
3659
3706
  if (session.active) {
3660
3707
  runtime.release(session.active, false);
@@ -3680,5 +3727,5 @@ exports.connectInteraction = connectInteraction;
3680
3727
  exports.createViewer = createViewer;
3681
3728
  exports.registerShaderProgram = registerShaderProgram;
3682
3729
  exports.shaderProgram = shaderProgram;
3683
- //# sourceMappingURL=chunk-UXDWH2JC.cjs.map
3684
- //# sourceMappingURL=chunk-UXDWH2JC.cjs.map
3730
+ //# sourceMappingURL=chunk-VK3T6O5Z.cjs.map
3731
+ //# sourceMappingURL=chunk-VK3T6O5Z.cjs.map