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