stickyflow 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. package/CHANGELOG.md +157 -0
  2. package/LICENSE +202 -0
  3. package/README.md +167 -2
  4. package/dist/chunk-34PK7HEU.cjs +842 -0
  5. package/dist/chunk-34PK7HEU.cjs.map +1 -0
  6. package/dist/chunk-6JKNT2YC.js +4685 -0
  7. package/dist/chunk-6JKNT2YC.js.map +1 -0
  8. package/dist/chunk-6LQJUAD4.js +801 -0
  9. package/dist/chunk-6LQJUAD4.js.map +1 -0
  10. package/dist/chunk-IIOJFR73.cjs +3671 -0
  11. package/dist/chunk-IIOJFR73.cjs.map +1 -0
  12. package/dist/chunk-KFJIFHXG.js +8559 -0
  13. package/dist/chunk-KFJIFHXG.js.map +1 -0
  14. package/dist/chunk-N6WVHOUQ.js +3665 -0
  15. package/dist/chunk-N6WVHOUQ.js.map +1 -0
  16. package/dist/chunk-S5BVAAEF.cjs +8568 -0
  17. package/dist/chunk-S5BVAAEF.cjs.map +1 -0
  18. package/dist/chunk-YM3XFBI4.cjs +4814 -0
  19. package/dist/chunk-YM3XFBI4.cjs.map +1 -0
  20. package/dist/index.cjs +1167 -0
  21. package/dist/index.cjs.map +1 -0
  22. package/dist/index.d.cts +335 -0
  23. package/dist/index.d.ts +335 -0
  24. package/dist/index.js +1097 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/interaction/index.cjs +7358 -0
  27. package/dist/interaction/index.cjs.map +1 -0
  28. package/dist/interaction/index.d.cts +273 -0
  29. package/dist/interaction/index.d.ts +273 -0
  30. package/dist/interaction/index.js +7333 -0
  31. package/dist/interaction/index.js.map +1 -0
  32. package/dist/render-r1GYvjNm.d.cts +970 -0
  33. package/dist/render-r1GYvjNm.d.ts +970 -0
  34. package/dist/runtimeTypes-ByiafBwg.d.ts +239 -0
  35. package/dist/runtimeTypes-DwLeD-em.d.cts +239 -0
  36. package/dist/stickyflow-diagram.global.js +1442 -0
  37. package/dist/stickyflow-diagram.global.js.map +1 -0
  38. package/dist/types-T7W2yTQF.d.cts +116 -0
  39. package/dist/types-ndGA1kbl.d.ts +116 -0
  40. package/dist/viewer/index.cjs +31 -0
  41. package/dist/viewer/index.cjs.map +1 -0
  42. package/dist/viewer/index.d.cts +44 -0
  43. package/dist/viewer/index.d.ts +44 -0
  44. package/dist/viewer/index.js +6 -0
  45. package/dist/viewer/index.js.map +1 -0
  46. package/dist/web-component/index.cjs +144 -0
  47. package/dist/web-component/index.cjs.map +1 -0
  48. package/dist/web-component/index.d.cts +31 -0
  49. package/dist/web-component/index.d.ts +31 -0
  50. package/dist/web-component/index.js +141 -0
  51. package/dist/web-component/index.js.map +1 -0
  52. package/package.json +81 -4
@@ -0,0 +1,3671 @@
1
+ 'use strict';
2
+
3
+ var chunkS5BVAAEF_cjs = require('./chunk-S5BVAAEF.cjs');
4
+ var chunk34PK7HEU_cjs = require('./chunk-34PK7HEU.cjs');
5
+
6
+ // src/runtime/viewer/display/rootAttributesPatch.ts
7
+ var CAMERA_OWNED_ATTRIBUTES = /* @__PURE__ */ new Set(["viewbox", "width", "height"]);
8
+ function patchRootAttributes(svg, next) {
9
+ for (const attribute of Array.from(svg.attributes)) {
10
+ if (!CAMERA_OWNED_ATTRIBUTES.has(attribute.name.toLowerCase())) {
11
+ svg.removeAttribute(attribute.name);
12
+ }
13
+ }
14
+ for (const attribute of Array.from(next.attributes)) {
15
+ if (!CAMERA_OWNED_ATTRIBUTES.has(attribute.name.toLowerCase())) {
16
+ svg.setAttribute(attribute.name, attribute.value);
17
+ }
18
+ }
19
+ }
20
+
21
+ // src/runtime/viewer/display/classifySceneDiff.ts
22
+ function classifySceneDiff(oldOwnerIds, newOwnerIds, touchedOwnerIds) {
23
+ const added = [];
24
+ const removed = [];
25
+ const changed = [];
26
+ for (const id of touchedOwnerIds) {
27
+ const wasPresent = oldOwnerIds.has(id);
28
+ const isPresent = newOwnerIds.has(id);
29
+ if (isPresent && !wasPresent) {
30
+ added.push(id);
31
+ } else if (wasPresent && !isPresent) {
32
+ removed.push(id);
33
+ } else if (wasPresent && isPresent) {
34
+ changed.push(id);
35
+ }
36
+ }
37
+ return { added, removed, changed };
38
+ }
39
+
40
+ // src/runtime/viewer/display/definitionsPatch.ts
41
+ function patchDefinitions(svg, next) {
42
+ const currentDefinitions = svg.querySelector(":scope > defs");
43
+ const nextDefinitions = next.querySelector(":scope > defs");
44
+ if (nextDefinitions) {
45
+ if (currentDefinitions) {
46
+ currentDefinitions.replaceWith(nextDefinitions);
47
+ } else {
48
+ svg.insertBefore(nextDefinitions, svg.firstChild);
49
+ }
50
+ return nextDefinitions;
51
+ }
52
+ currentDefinitions?.remove();
53
+ return currentDefinitions;
54
+ }
55
+
56
+ // src/runtime/viewer/display/elementPatch.ts
57
+ function patchAttributes(current, next) {
58
+ let changed = false;
59
+ for (const attribute of Array.from(current.attributes)) {
60
+ if (!next.hasAttribute(attribute.name)) {
61
+ current.removeAttribute(attribute.name);
62
+ changed = true;
63
+ }
64
+ }
65
+ for (const attribute of Array.from(next.attributes)) {
66
+ if (current.getAttribute(attribute.name) !== attribute.value) {
67
+ current.setAttribute(attribute.name, attribute.value);
68
+ changed = true;
69
+ }
70
+ }
71
+ return changed;
72
+ }
73
+ function childrenEqual(a, b) {
74
+ const aChildren = Array.from(a.childNodes);
75
+ const bChildren = Array.from(b.childNodes);
76
+ if (aChildren.length !== bChildren.length) {
77
+ return false;
78
+ }
79
+ for (let i = 0; i < aChildren.length; i++) {
80
+ const x = aChildren[i];
81
+ const y = bChildren[i];
82
+ if (x.nodeType !== y.nodeType) {
83
+ return false;
84
+ }
85
+ if (x.nodeType === Node.TEXT_NODE) {
86
+ if (x.textContent !== y.textContent) {
87
+ return false;
88
+ }
89
+ continue;
90
+ }
91
+ if (x.nodeType !== Node.ELEMENT_NODE) {
92
+ continue;
93
+ }
94
+ const xElement = x;
95
+ const yElement = y;
96
+ if (xElement.tagName !== yElement.tagName) {
97
+ return false;
98
+ }
99
+ if (xElement.attributes.length !== yElement.attributes.length) {
100
+ return false;
101
+ }
102
+ for (const attribute of Array.from(xElement.attributes)) {
103
+ if (yElement.getAttribute(attribute.name) !== attribute.value) {
104
+ return false;
105
+ }
106
+ }
107
+ if (!childrenEqual(xElement, yElement)) {
108
+ return false;
109
+ }
110
+ }
111
+ return true;
112
+ }
113
+ function replaceTextContent(current, next) {
114
+ while (current.firstChild) {
115
+ current.removeChild(current.firstChild);
116
+ }
117
+ while (next.firstChild) {
118
+ current.appendChild(next.firstChild);
119
+ }
120
+ }
121
+ function patchSceneElement(current, next) {
122
+ const widgetRestyled = next.hasAttribute("data-sf-widget") && (current.getAttribute("data-sf-widget-style") !== next.getAttribute("data-sf-widget-style") || current.getAttribute("data-sf-widget-value") !== next.getAttribute("data-sf-widget-value"));
123
+ const attributesChanged = patchAttributes(current, next);
124
+ let contentChanged = false;
125
+ if (widgetRestyled) {
126
+ contentChanged = true;
127
+ replaceTextContent(current, next);
128
+ }
129
+ if (next.tagName.toLowerCase() === "text" && !childrenEqual(current, next)) {
130
+ contentChanged = true;
131
+ replaceTextContent(current, next);
132
+ }
133
+ return attributesChanged || contentChanged;
134
+ }
135
+
136
+ // src/runtime/viewer/display/sceneNodesPatch.ts
137
+ function ownerIdOf(element) {
138
+ return element.getAttribute("data-sf-id") ?? element.id;
139
+ }
140
+ function currentElementsById(svg) {
141
+ const elements = /* @__PURE__ */ new Map();
142
+ for (const element of svg.querySelectorAll(":scope > [id]")) {
143
+ elements.set(element.id, element);
144
+ }
145
+ return elements;
146
+ }
147
+ function reusableElement(current, next, markTouched) {
148
+ if (current && current.tagName === next.tagName) {
149
+ if (patchSceneElement(current, next)) {
150
+ markTouched(ownerIdOf(next));
151
+ }
152
+ return current;
153
+ }
154
+ current?.remove();
155
+ markTouched(ownerIdOf(next));
156
+ return next;
157
+ }
158
+ function placeAfter(svg, element, previousSibling) {
159
+ const wantedNext = previousSibling ? previousSibling.nextSibling : svg.firstChild;
160
+ if (wantedNext !== element) {
161
+ svg.insertBefore(element, wantedNext);
162
+ }
163
+ }
164
+ function patchSceneNodes(svg, next) {
165
+ const definitions = patchDefinitions(svg, next);
166
+ const currentById = currentElementsById(svg);
167
+ const retainedIds = /* @__PURE__ */ new Set();
168
+ const oldOwnerIds = /* @__PURE__ */ new Set();
169
+ for (const element of currentById.values()) {
170
+ oldOwnerIds.add(ownerIdOf(element));
171
+ }
172
+ const newOwnerIds = /* @__PURE__ */ new Set();
173
+ const touchedOwnerIds = /* @__PURE__ */ new Set();
174
+ const markTouched = (ownerId) => {
175
+ touchedOwnerIds.add(ownerId);
176
+ };
177
+ let previousSibling = definitions;
178
+ for (const nextElement of next.querySelectorAll(":scope > [id]")) {
179
+ retainedIds.add(nextElement.id);
180
+ newOwnerIds.add(ownerIdOf(nextElement));
181
+ const liveElement = reusableElement(currentById.get(nextElement.id), nextElement, markTouched);
182
+ placeAfter(svg, liveElement, previousSibling);
183
+ previousSibling = liveElement;
184
+ }
185
+ for (const [id, element] of currentById) {
186
+ if (!retainedIds.has(id)) {
187
+ touchedOwnerIds.add(ownerIdOf(element));
188
+ element.remove();
189
+ }
190
+ }
191
+ return classifySceneDiff(oldOwnerIds, newOwnerIds, touchedOwnerIds);
192
+ }
193
+
194
+ // src/runtime/viewer/display/scenePatch.ts
195
+ function patchDisplayContent(svg, next) {
196
+ patchRootAttributes(svg, next);
197
+ return patchSceneNodes(svg, next);
198
+ }
199
+
200
+ // src/runtime/viewer/display/svgDocument.ts
201
+ function parseSvg(svgString) {
202
+ const document = new DOMParser().parseFromString(svgString, "image/svg+xml");
203
+ const root = document.documentElement;
204
+ if (root.nodeName.toLowerCase() !== "svg") {
205
+ throw new Error("viewer: source did not parse to an <svg> root");
206
+ }
207
+ return root;
208
+ }
209
+ function readViewBox(svg) {
210
+ const values = (svg.getAttribute("viewBox") ?? "0 0 100 100").split(/\s+/).map(Number);
211
+ return { x: values[0], y: values[1], w: values[2], h: values[3] };
212
+ }
213
+
214
+ // src/runtime/viewer/display/viewport.ts
215
+ var ViewportController = class {
216
+ #svg;
217
+ #natural;
218
+ #view;
219
+ constructor(svg, natural) {
220
+ this.#svg = svg;
221
+ this.#natural = natural;
222
+ this.#view = { ...natural };
223
+ }
224
+ apply() {
225
+ this.#svg.setAttribute("viewBox", `${this.#view.x} ${this.#view.y} ${this.#view.w} ${this.#view.h}`);
226
+ }
227
+ fit() {
228
+ this.#view = { ...this.#natural };
229
+ this.apply();
230
+ }
231
+ get() {
232
+ return { ...this.#view };
233
+ }
234
+ setNatural(natural) {
235
+ this.#natural = natural;
236
+ }
237
+ zoomBy(factor, clientX, clientY) {
238
+ const rect = this.#svg.getBoundingClientRect();
239
+ if (rect.width <= 0 || rect.height <= 0) {
240
+ return;
241
+ }
242
+ const centerX = clientX ?? rect.left + rect.width / 2;
243
+ const centerY = clientY ?? rect.top + rect.height / 2;
244
+ const worldX = this.#view.x + (centerX - rect.left) / rect.width * this.#view.w;
245
+ const worldY = this.#view.y + (centerY - rect.top) / rect.height * this.#view.h;
246
+ this.#view = { w: this.#view.w / factor, h: this.#view.h / factor, x: 0, y: 0 };
247
+ this.#view.x = worldX - (centerX - rect.left) / rect.width * this.#view.w;
248
+ this.#view.y = worldY - (centerY - rect.top) / rect.height * this.#view.h;
249
+ this.apply();
250
+ }
251
+ panBy(dxPixels, dyPixels) {
252
+ const rect = this.#svg.getBoundingClientRect();
253
+ const pixelsPerUnit = rect.width > 0 ? rect.width / this.#view.w : 1;
254
+ this.#view.x -= dxPixels / pixelsPerUnit;
255
+ this.#view.y -= dyPixels / pixelsPerUnit;
256
+ this.apply();
257
+ }
258
+ };
259
+
260
+ // src/runtime/viewer/display/index.ts
261
+ function mountDisplay(container, svgString) {
262
+ const svg = parseSvg(svgString);
263
+ svg.setAttribute("width", "100%");
264
+ svg.setAttribute("height", "100%");
265
+ svg.style.display = "block";
266
+ svg.style.touchAction = "none";
267
+ const viewport = new ViewportController(svg, readViewBox(svg));
268
+ container.appendChild(svg);
269
+ viewport.apply();
270
+ return {
271
+ svg,
272
+ fit: () => viewport.fit(),
273
+ getViewport: () => viewport.get(),
274
+ zoomBy: (factor, clientX, clientY) => viewport.zoomBy(factor, clientX, clientY),
275
+ panBy: (dxPixels, dyPixels) => viewport.panBy(dxPixels, dyPixels),
276
+ updateContent(nextSvgString) {
277
+ const next = parseSvg(nextSvgString);
278
+ const diff = patchDisplayContent(svg, next);
279
+ viewport.setNatural(readViewBox(next));
280
+ return diff;
281
+ },
282
+ destroy() {
283
+ svg.remove();
284
+ }
285
+ };
286
+ }
287
+
288
+ // src/runtime/viewer/events.ts
289
+ var Emitter2 = class extends chunk34PK7HEU_cjs.Emitter {
290
+ };
291
+
292
+ // src/runtime/viewer/export/raster.ts
293
+ function positive(value, fallback) {
294
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
295
+ }
296
+ function loadImage(document, source) {
297
+ return new Promise((resolve, reject) => {
298
+ const ImageConstructor = document.defaultView?.Image;
299
+ if (!ImageConstructor) {
300
+ reject(new Error("viewer export: Image is unavailable in this environment."));
301
+ return;
302
+ }
303
+ const image = new ImageConstructor();
304
+ image.onload = () => resolve(image);
305
+ image.onerror = () => reject(new Error("viewer export: the SVG snapshot could not be decoded."));
306
+ image.src = source;
307
+ });
308
+ }
309
+ function canvasBlob(canvas, mimeType, quality) {
310
+ return new Promise((resolve, reject) => {
311
+ canvas.toBlob(
312
+ (blob) => {
313
+ if (blob) {
314
+ resolve(blob);
315
+ } else {
316
+ reject(new Error(`viewer export: ${mimeType} encoding failed.`));
317
+ }
318
+ },
319
+ mimeType,
320
+ quality
321
+ );
322
+ });
323
+ }
324
+ async function rasterizeSvgSnapshot(svg, snapshot, options = {}) {
325
+ const viewBox = readViewBox(svg);
326
+ const scale = positive(options.scale, 1);
327
+ const width = Math.max(1, Math.round(viewBox.w * scale));
328
+ const height = Math.max(1, Math.round(viewBox.h * scale));
329
+ const canvas = svg.ownerDocument.createElement("canvas");
330
+ canvas.width = width;
331
+ canvas.height = height;
332
+ const context = canvas.getContext("2d");
333
+ if (!context) {
334
+ throw new Error("viewer export: a 2D canvas context is unavailable.");
335
+ }
336
+ const blob = new Blob([snapshot], { type: "image/svg+xml;charset=utf-8" });
337
+ const url = URL.createObjectURL(blob);
338
+ try {
339
+ const image = await loadImage(svg.ownerDocument, url);
340
+ const background = options.backgroundColor ?? (options.format === "jpeg" ? "#ffffff" : void 0);
341
+ if (background) {
342
+ context.fillStyle = background;
343
+ context.fillRect(0, 0, width, height);
344
+ }
345
+ context.drawImage(image, 0, 0, width, height);
346
+ const mimeType = options.format === "jpeg" ? "image/jpeg" : "image/png";
347
+ return await canvasBlob(canvas, mimeType, options.quality);
348
+ } finally {
349
+ URL.revokeObjectURL(url);
350
+ }
351
+ }
352
+
353
+ // src/runtime/viewer/export/snapshot.ts
354
+ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
355
+ function copyAttributes(source, target) {
356
+ for (const attribute of source.attributes) {
357
+ if (!attribute.name.startsWith("data-sf-shader")) {
358
+ target.setAttribute(attribute.name, attribute.value);
359
+ }
360
+ }
361
+ }
362
+ function removeRuntimeOverlays(svg) {
363
+ for (const overlay of svg.querySelectorAll(
364
+ '[data-sf-selection-overlay], [data-sf-change-overlay], [id*="link-hit-"]'
365
+ )) {
366
+ overlay.remove();
367
+ }
368
+ }
369
+ function svgElement(document, name, attributes) {
370
+ const element = document.createElementNS(SVG_NAMESPACE, name);
371
+ for (const [key, value] of Object.entries(attributes)) {
372
+ element.setAttribute(key, String(value));
373
+ }
374
+ return element;
375
+ }
376
+ function readWidgetStyle(surface) {
377
+ try {
378
+ return {
379
+ ...chunkS5BVAAEF_cjs.DEFAULT_ROLLING_NUMBER_STYLE,
380
+ ...JSON.parse(surface.getAttribute("data-sf-widget-style") ?? "{}")
381
+ };
382
+ } catch {
383
+ return chunkS5BVAAEF_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
384
+ }
385
+ }
386
+ function cellsOf(box, plain) {
387
+ const size = Math.max(1, Math.min(box.width, box.height) * 0.28);
388
+ return {
389
+ size,
390
+ plain,
391
+ cellWidth: plain ? size * 0.62 : size * 0.72 + 2,
392
+ cellHeight: plain ? size * 1.1 : size * 1.2 + 2,
393
+ gap: size * (plain ? 0.02 : 0.1)
394
+ };
395
+ }
396
+ function groupOf(document, surface, style) {
397
+ const group = svgElement(document, "g", { "font-family": style.fontFamily });
398
+ for (const attribute of ["id", "transform", "data-sf-id", "data-sf-role", "data-sf-kind"]) {
399
+ const attributeValue = surface.getAttribute(attribute);
400
+ if (attributeValue !== null) {
401
+ group.setAttribute(attribute, attributeValue);
402
+ }
403
+ }
404
+ return group;
405
+ }
406
+ function labelText(document, label, box, top, cells, style) {
407
+ const text = svgElement(document, "text", {
408
+ x: box.x + box.width / 2,
409
+ y: top - cells.size * 0.14,
410
+ "text-anchor": "middle",
411
+ "font-size": cells.size * 0.16,
412
+ "font-weight": 600,
413
+ "letter-spacing": cells.size * 0.04,
414
+ fill: style.labelColor
415
+ });
416
+ text.textContent = label;
417
+ return text;
418
+ }
419
+ function digitCell(document, character, cellX, top, cells, style) {
420
+ const { size, plain, cellWidth, cellHeight } = cells;
421
+ const nodes = [];
422
+ if (!plain) {
423
+ nodes.push(
424
+ svgElement(document, "rect", {
425
+ x: cellX + 0.5,
426
+ y: top + 0.5,
427
+ width: cellWidth - 1,
428
+ height: cellHeight - 1,
429
+ rx: size * 0.09,
430
+ fill: style.surface,
431
+ stroke: style.divider,
432
+ "stroke-width": 1
433
+ })
434
+ );
435
+ }
436
+ const digit = svgElement(document, "text", {
437
+ x: cellX + cellWidth / 2,
438
+ y: top + cellHeight / 2,
439
+ "text-anchor": "middle",
440
+ "dominant-baseline": "central",
441
+ "font-size": size,
442
+ "font-weight": style.fontWeight,
443
+ fill: style.color
444
+ });
445
+ digit.textContent = character;
446
+ nodes.push(digit);
447
+ if (!plain) {
448
+ nodes.push(
449
+ svgElement(document, "line", {
450
+ x1: cellX + 0.5,
451
+ x2: cellX + cellWidth - 0.5,
452
+ y1: top + cellHeight / 2,
453
+ y2: top + cellHeight / 2,
454
+ stroke: style.divider,
455
+ "stroke-width": 1
456
+ })
457
+ );
458
+ }
459
+ return nodes;
460
+ }
461
+ function flattenRollingNumbers(svg) {
462
+ const document = svg.ownerDocument;
463
+ for (const surface of svg.querySelectorAll('foreignObject[data-sf-widget="rollingNumber"]')) {
464
+ const box = {
465
+ x: Number(surface.getAttribute("x")),
466
+ y: Number(surface.getAttribute("y")),
467
+ width: Number(surface.getAttribute("width")),
468
+ height: Number(surface.getAttribute("height"))
469
+ };
470
+ const value = surface.getAttribute("data-sf-widget-value") ?? "";
471
+ const style = readWidgetStyle(surface);
472
+ const label = surface.querySelector("div > span:not([data-sf-rolling-digit])")?.textContent ?? "";
473
+ const cells = cellsOf(box, style.variant === "plain");
474
+ const labelBlock = label ? cells.size * 0.3 : 0;
475
+ const rowWidth = value.length * cells.cellWidth + Math.max(0, value.length - 1) * cells.gap;
476
+ const left = box.x + (box.width - rowWidth) / 2;
477
+ const top = box.y + (box.height - (labelBlock + cells.cellHeight)) / 2 + labelBlock;
478
+ const group = groupOf(document, surface, style);
479
+ if (label) {
480
+ group.appendChild(labelText(document, label, box, top, cells, style));
481
+ }
482
+ [...value].forEach((character, index) => {
483
+ const cellX = left + index * (cells.cellWidth + cells.gap);
484
+ for (const node of digitCell(document, character, cellX, top, cells, style)) {
485
+ group.appendChild(node);
486
+ }
487
+ });
488
+ surface.replaceWith(group);
489
+ }
490
+ }
491
+ function freezeShaderSurfaces(svg, captures) {
492
+ const clone = svg.cloneNode(true);
493
+ removeRuntimeOverlays(clone);
494
+ const surfaces = [...clone.querySelectorAll("foreignObject[data-sf-shader]")];
495
+ surfaces.forEach((surface, index) => {
496
+ const capture = captures[index];
497
+ if (!capture) {
498
+ surface.remove();
499
+ return;
500
+ }
501
+ const image = clone.ownerDocument.createElementNS(SVG_NAMESPACE, "image");
502
+ copyAttributes(surface, image);
503
+ image.setAttribute("href", capture);
504
+ image.setAttribute("preserveAspectRatio", "none");
505
+ surface.replaceWith(image);
506
+ });
507
+ flattenRollingNumbers(clone);
508
+ const viewBox = (clone.getAttribute("viewBox") ?? "").split(/\s+/).map(Number);
509
+ if (viewBox.length === 4 && viewBox.every(Number.isFinite)) {
510
+ clone.setAttribute("width", String(viewBox[2]));
511
+ clone.setAttribute("height", String(viewBox[3]));
512
+ }
513
+ clone.setAttribute("xmlns", SVG_NAMESPACE);
514
+ return clone;
515
+ }
516
+ function serializeSvgSnapshot(svg, captures) {
517
+ return new XMLSerializer().serializeToString(freezeShaderSurfaces(svg, captures));
518
+ }
519
+
520
+ // src/runtime/viewer/interaction/changeFlash.ts
521
+ function createChangeFlash(svg) {
522
+ const timers = /* @__PURE__ */ new Map();
523
+ const flash = (ownerIds, color2 = chunk34PK7HEU_cjs.CHANGE_COLOR) => {
524
+ const ids = [...new Set(ownerIds)];
525
+ if (ids.length === 0) {
526
+ return;
527
+ }
528
+ const doc = svg.ownerDocument;
529
+ const reducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
530
+ for (const id of ids) {
531
+ const primary = chunk34PK7HEU_cjs.findOwnerNode(svg, id, chunk34PK7HEU_cjs.isPrimary);
532
+ if (!primary) {
533
+ continue;
534
+ }
535
+ const pending = timers.get(id);
536
+ if (pending) {
537
+ clearTimeout(pending);
538
+ }
539
+ for (const stale of svg.querySelectorAll(`[data-sf-change-overlay="${CSS.escape(id)}"]`)) {
540
+ stale.remove();
541
+ }
542
+ const overlay = primary.getAttribute("data-sf-role") === "link" ? chunk34PK7HEU_cjs.linkChangeOverlay(doc, primary, id, color2) : chunk34PK7HEU_cjs.shapeChangeOverlay(doc, primary, id, color2);
543
+ svg.appendChild(overlay);
544
+ chunk34PK7HEU_cjs.startChangeFade(overlay, reducedMotion);
545
+ const duration = reducedMotion ? chunk34PK7HEU_cjs.REDUCED_MOTION_DURATION_MS : chunk34PK7HEU_cjs.FLASH_DURATION_MS;
546
+ timers.set(
547
+ id,
548
+ setTimeout(() => {
549
+ overlay.remove();
550
+ timers.delete(id);
551
+ }, duration)
552
+ );
553
+ }
554
+ };
555
+ return {
556
+ flash,
557
+ destroy() {
558
+ for (const timer of timers.values()) {
559
+ clearTimeout(timer);
560
+ }
561
+ timers.clear();
562
+ }
563
+ };
564
+ }
565
+
566
+ // src/runtime/viewer/interaction/keyboard.ts
567
+ function bindKeyboardSelection(svg, selection, selectable) {
568
+ const onKeyDown = (event) => {
569
+ if (selectable && event.key === "Escape") {
570
+ selection.dispatch({ type: "clear" });
571
+ }
572
+ };
573
+ svg.addEventListener("keydown", onKeyDown);
574
+ return () => {
575
+ svg.removeEventListener("keydown", onKeyDown);
576
+ };
577
+ }
578
+
579
+ // src/runtime/viewer/interaction/modifiers.ts
580
+ function modifiersOf(event) {
581
+ return {
582
+ shift: event.shiftKey,
583
+ ctrl: event.ctrlKey,
584
+ meta: event.metaKey,
585
+ alt: event.altKey
586
+ };
587
+ }
588
+
589
+ // src/runtime/viewer/interaction/selection.ts
590
+ function selectionReducer(current, action) {
591
+ switch (action.type) {
592
+ case "clear":
593
+ return /* @__PURE__ */ new Set();
594
+ case "set":
595
+ return new Set(action.ids);
596
+ case "select":
597
+ return /* @__PURE__ */ new Set([action.id]);
598
+ case "add":
599
+ return /* @__PURE__ */ new Set([...current, action.id]);
600
+ case "toggle": {
601
+ const next = new Set(current);
602
+ if (next.has(action.id)) {
603
+ next.delete(action.id);
604
+ } else {
605
+ next.add(action.id);
606
+ }
607
+ return next;
608
+ }
609
+ }
610
+ }
611
+ function clickAction(id, modifiers) {
612
+ return modifiers.ctrl || modifiers.meta || modifiers.shift ? { type: "toggle", id } : { type: "select", id };
613
+ }
614
+
615
+ // src/runtime/viewer/interaction/pointerSelection.ts
616
+ var SELECTION_OVERLAY_CONTROL_SELECTOR = "[data-sf-resize-handle], [data-sf-link-endpoint], [data-sf-link-segment], [data-sf-quick-duplicate]";
617
+ function isSelectionOverlayControl(target) {
618
+ return Boolean(target?.closest(SELECTION_OVERLAY_CONTROL_SELECTOR));
619
+ }
620
+ var OwnerPointerController = class {
621
+ #svg;
622
+ #emitter;
623
+ #diagramId;
624
+ #selection;
625
+ constructor(svg, emitter, diagramId, selection) {
626
+ this.#svg = svg;
627
+ this.#emitter = emitter;
628
+ this.#diagramId = diagramId;
629
+ this.#selection = selection;
630
+ }
631
+ hover(event) {
632
+ const owner = chunk34PK7HEU_cjs.resolveOwner(event.target);
633
+ this.#emitter.emit(
634
+ "ownerHover",
635
+ owner ? { diagramId: this.#diagramId, owner, modifiers: modifiersOf(event) } : { diagramId: this.#diagramId, owner: null }
636
+ );
637
+ }
638
+ select(event) {
639
+ const target = event.target;
640
+ const owner = chunk34PK7HEU_cjs.resolveOwner(target);
641
+ if (!owner) {
642
+ this.#clearWhenOutsideOverlay(target);
643
+ return;
644
+ }
645
+ this.#selectOwner(owner, event);
646
+ }
647
+ #clearWhenOutsideOverlay(target) {
648
+ if (!isSelectionOverlayControl(target)) {
649
+ this.#selection.dispatch({ type: "clear" });
650
+ }
651
+ }
652
+ #selectOwner(owner, event) {
653
+ this.#selection.remember(owner);
654
+ const modifiers = modifiersOf(event);
655
+ this.#selection.dispatch(clickAction(owner.id, modifiers));
656
+ this.#emitter.emit(owner.role === "link" ? "linkClick" : "canvasObjectClick", {
657
+ diagramId: this.#diagramId,
658
+ owner,
659
+ modifiers
660
+ });
661
+ const node = chunk34PK7HEU_cjs.findOwnerNode(this.#svg, owner.id, chunk34PK7HEU_cjs.isPrimary);
662
+ if (node && !node.hasAttribute("tabindex")) {
663
+ node.setAttribute("tabindex", "-1");
664
+ }
665
+ node?.focus({ preventScroll: true });
666
+ }
667
+ };
668
+
669
+ // src/runtime/viewer/interaction/pointer.ts
670
+ var DRAG_THRESHOLD = 4;
671
+ var PointerInteractionSession = class {
672
+ #display;
673
+ #ownerPointer;
674
+ #selectable;
675
+ #down = null;
676
+ constructor(display, ownerPointer, selectable) {
677
+ this.#display = display;
678
+ this.#ownerPointer = ownerPointer;
679
+ this.#selectable = selectable;
680
+ }
681
+ bind() {
682
+ this.#display.svg.addEventListener("pointerdown", this.#onPointerDown);
683
+ this.#display.svg.addEventListener("pointermove", this.#onPointerMove);
684
+ this.#display.svg.addEventListener("pointerup", this.#onPointerUp);
685
+ return () => this.#destroy();
686
+ }
687
+ #onPointerDown = (event) => {
688
+ const target = event.target;
689
+ const overContent = Boolean(chunk34PK7HEU_cjs.resolveOwner(target)) || isSelectionOverlayControl(target);
690
+ this.#down = {
691
+ x: event.clientX,
692
+ y: event.clientY,
693
+ moved: false,
694
+ panable: chunk34PK7HEU_cjs.isCameraGesture(event) || !overContent
695
+ };
696
+ };
697
+ #onPointerMove = (event) => {
698
+ if (this.#continueDrag(event)) {
699
+ return;
700
+ }
701
+ if (this.#selectable) {
702
+ this.#ownerPointer.hover(event);
703
+ }
704
+ };
705
+ #onPointerUp = (event) => {
706
+ const wasDrag = this.#down?.moved ?? false;
707
+ this.#down = null;
708
+ if (!wasDrag && this.#selectable) {
709
+ this.#ownerPointer.select(event);
710
+ }
711
+ };
712
+ #continueDrag(event) {
713
+ if (!this.#down) {
714
+ return false;
715
+ }
716
+ if (!this.#down.moved && Math.hypot(event.clientX - this.#down.x, event.clientY - this.#down.y) > DRAG_THRESHOLD) {
717
+ this.#down.moved = true;
718
+ }
719
+ if (!this.#down.moved) {
720
+ return false;
721
+ }
722
+ if (this.#down.panable && chunk34PK7HEU_cjs.isCameraGesture(event)) {
723
+ this.#display.panBy(event.movementX, event.movementY);
724
+ }
725
+ return true;
726
+ }
727
+ #destroy() {
728
+ this.#display.svg.removeEventListener("pointerdown", this.#onPointerDown);
729
+ this.#display.svg.removeEventListener("pointermove", this.#onPointerMove);
730
+ this.#display.svg.removeEventListener("pointerup", this.#onPointerUp);
731
+ }
732
+ };
733
+ function bindPointerInteraction(binding) {
734
+ const { display, emitter, diagramId, selection, selectable } = binding;
735
+ const ownerPointer = new OwnerPointerController(display.svg, emitter, diagramId, selection);
736
+ return new PointerInteractionSession(display, ownerPointer, selectable).bind();
737
+ }
738
+
739
+ // src/runtime/viewer/interaction/overlayPainter.ts
740
+ function paintSelectionOverlays(svg, selectedIds, owners) {
741
+ for (const overlay of svg.querySelectorAll("[data-sf-selection-overlay]")) {
742
+ overlay.remove();
743
+ }
744
+ const document = svg.ownerDocument;
745
+ for (const id of selectedIds) {
746
+ const primary = chunk34PK7HEU_cjs.findOwnerNode(svg, id, chunk34PK7HEU_cjs.isPrimary);
747
+ if (!primary) {
748
+ continue;
749
+ }
750
+ const owner = owners.get(id);
751
+ const overlay = owner?.role === "link" ? chunk34PK7HEU_cjs.linkSelectionOverlay(document, primary, id, svg) : owner?.role === "path" ? chunk34PK7HEU_cjs.pathSelectionOverlay(document, primary, id, svg) : owner?.role === "isometric" ? chunk34PK7HEU_cjs.isometricSelectionOverlay(document, primary, id, svg) : chunk34PK7HEU_cjs.shapeSelectionOverlay(document, primary, id, svg, owner ? chunk34PK7HEU_cjs.DERIVED_HEIGHT_ROLES.has(owner.role) : false);
752
+ svg.appendChild(overlay);
753
+ if (owner?.role === "shape" && selectedIds.size === 1) {
754
+ svg.appendChild(chunk34PK7HEU_cjs.quickDuplicateBubbles(document, primary, id, svg));
755
+ }
756
+ }
757
+ }
758
+
759
+ // src/runtime/viewer/interaction/selectionController.ts
760
+ var SelectionController = class {
761
+ #svg;
762
+ #emitter;
763
+ #diagramId;
764
+ #owners = /* @__PURE__ */ new Map();
765
+ #ids = /* @__PURE__ */ new Set();
766
+ constructor(svg, emitter, diagramId) {
767
+ this.#svg = svg;
768
+ this.#emitter = emitter;
769
+ this.#diagramId = diagramId;
770
+ }
771
+ getOwners() {
772
+ return [...this.#ids].map((id) => this.#owners.get(id)).filter((owner) => Boolean(owner));
773
+ }
774
+ remember(owner) {
775
+ this.#owners.set(owner.id, owner);
776
+ }
777
+ dispatch(action) {
778
+ this.#ids = selectionReducer(this.#ids, action);
779
+ paintSelectionOverlays(this.#svg, this.#ids, this.#owners);
780
+ this.#emitter.emit("selectionChange", {
781
+ diagramId: this.#diagramId,
782
+ selection: this.getOwners()
783
+ });
784
+ }
785
+ repaint() {
786
+ if (this.#ids.size > 0) {
787
+ paintSelectionOverlays(this.#svg, this.#ids, this.#owners);
788
+ }
789
+ }
790
+ selectIds(ids) {
791
+ for (const id of ids) {
792
+ const element = this.#svg.querySelector(`[data-sf-id="${CSS.escape(id)}"]`);
793
+ const owner = chunk34PK7HEU_cjs.resolveOwner(element);
794
+ if (owner) {
795
+ this.remember(owner);
796
+ }
797
+ }
798
+ this.dispatch({ type: "set", ids });
799
+ }
800
+ };
801
+
802
+ // src/runtime/viewer/interaction/wheel.ts
803
+ function bindWheelZoom(display, selection) {
804
+ const onWheel = (event) => {
805
+ if (!chunk34PK7HEU_cjs.isCameraGesture(event)) {
806
+ return;
807
+ }
808
+ event.preventDefault();
809
+ display.zoomBy(event.deltaY < 0 ? 1.1 : 1 / 1.1, event.clientX, event.clientY);
810
+ selection.repaint();
811
+ };
812
+ display.svg.addEventListener("wheel", onWheel, { passive: false });
813
+ return () => {
814
+ display.svg.removeEventListener("wheel", onWheel);
815
+ };
816
+ }
817
+
818
+ // src/runtime/viewer/interaction/index.ts
819
+ function bindInteraction(display, emitter, diagramId, options = {}) {
820
+ const selectable = options.selectable ?? true;
821
+ const selection = new SelectionController(display.svg, emitter, diagramId);
822
+ const unbindPointer = bindPointerInteraction({ display, emitter, diagramId, selection, selectable });
823
+ const unbindWheel = bindWheelZoom(display, selection);
824
+ const unbindKeyboard = bindKeyboardSelection(display.svg, selection, selectable);
825
+ const changeFlash = createChangeFlash(display.svg);
826
+ return {
827
+ getSelection: () => selection.getOwners(),
828
+ select: (ids) => selection.selectIds(ids),
829
+ repaint: () => selection.repaint(),
830
+ flashChanged: (ownerIds, color2) => changeFlash.flash(ownerIds, color2),
831
+ destroy() {
832
+ unbindPointer();
833
+ unbindWheel();
834
+ unbindKeyboard();
835
+ changeFlash.destroy();
836
+ }
837
+ };
838
+ }
839
+
840
+ // src/runtime/viewer/shaders/defaults.ts
841
+ var SHADER_DEFAULTS = {
842
+ numbers: {
843
+ intensity: 0.85,
844
+ speed: 0.18,
845
+ progress: -1,
846
+ boost: 0,
847
+ dent: -1,
848
+ day: -1,
849
+ light: 135,
850
+ gloss: 0.15,
851
+ depth: 0.4,
852
+ rim: 0.2
853
+ },
854
+ colors: {
855
+ color: [0.08, 0.02, 0.22],
856
+ accent: [0.58, 0.31, 1],
857
+ hot: [1, 0.45, 0.82]
858
+ }
859
+ };
860
+
861
+ // src/runtime/viewer/shaders/builtins/button.ts
862
+ var fragmentSource = `
863
+ precision highp float;
864
+
865
+ uniform vec2 uResolution;
866
+ uniform float uLight;
867
+ uniform float uGloss;
868
+ uniform float uDepth;
869
+ uniform float uRim;
870
+ uniform float uPressed;
871
+ uniform vec3 uColor;
872
+ varying vec2 vUv;
873
+
874
+ void main() {
875
+ vec2 p = vUv * 2.0 - 1.0;
876
+ p.x *= uResolution.x / max(uResolution.y, 1.0);
877
+ float pixel = 2.0 / max(uResolution.y, 1.0);
878
+ float pressed = clamp(uPressed, 0.0, 1.0);
879
+
880
+ float radius = 0.9 * (1.0 - 0.025 * pressed);
881
+ float r = length(p);
882
+ vec2 direction = r > 0.0001 ? p / r : vec2(0.0, 1.0);
883
+ float angle = radians(uLight);
884
+ vec2 toLight = vec2(cos(angle), sin(angle));
885
+ vec3 light = normalize(vec3(toLight, 0.85));
886
+
887
+ vec3 base = dot(uColor, uColor) > 0.0 ? uColor : vec3(0.15, 0.15, 0.17);
888
+ float depth = max(uDepth, 0.0) * (1.0 - 0.65 * pressed);
889
+ float inside = clamp(r / radius, 0.0, 1.0);
890
+
891
+ vec3 normal = normalize(vec3(direction * pow(inside, 1.6) * depth * 1.4, 1.0));
892
+ float diffuse = clamp(dot(normal, light), 0.0, 1.0);
893
+ vec3 color = base * (0.5 + 0.7 * diffuse) * (1.0 - 0.18 * pressed);
894
+
895
+ vec3 reflected = reflect(-light, normal);
896
+ float specular = pow(max(reflected.z, 0.0), 26.0) * max(uGloss, 0.0) * (1.0 - 0.8 * pressed);
897
+ color += vec3(specular) * 0.9;
898
+
899
+ float lit = max(dot(direction, toLight), 0.0);
900
+ float away = max(dot(direction, -toLight), 0.0);
901
+ float edge = smoothstep(0.55, 1.0, inside);
902
+ color *= 1.0 - 0.45 * edge * away * min(depth, 1.5) - 0.12 * edge;
903
+ color += mix(base, vec3(1.0), 0.6) * max(uRim, 0.0) * smoothstep(0.9, 1.0, inside) * lit * (1.0 - 0.7 * pressed);
904
+
905
+ float disc = 1.0 - smoothstep(radius - pixel, radius + pixel, r);
906
+
907
+ vec2 shadowAt = -toLight * 0.06 * (1.0 - 0.7 * pressed);
908
+ float shadowDistance = length(p - shadowAt);
909
+ float shadow = (1.0 - smoothstep(radius * 0.55, 0.99, shadowDistance)) * 0.55 * (1.0 - 0.4 * pressed);
910
+
911
+ float alpha = clamp(disc + shadow * (1.0 - disc), 0.0, 1.0);
912
+ gl_FragColor = vec4(clamp(color * disc, 0.0, 1.0), alpha);
913
+ }
914
+ `;
915
+ var buttonProgram = {
916
+ name: "button",
917
+ fragmentSource,
918
+ replacesBaseFill: true
919
+ };
920
+
921
+ // src/runtime/viewer/shaders/builtins/chromaticGlassPlate.ts
922
+ var fragmentSource2 = `
923
+ precision highp float;
924
+
925
+ uniform sampler2D uBackdrop;
926
+ uniform vec2 uBackdropOrigin;
927
+ uniform vec2 uBackdropScale;
928
+ uniform vec2 uResolution;
929
+ uniform float uTime;
930
+ uniform float uSpeed;
931
+ uniform float uIntensity;
932
+ uniform float uThickness;
933
+ uniform float uRefraction;
934
+ uniform vec3 uPinkColor;
935
+ uniform vec3 uVioletColor;
936
+ uniform vec3 uBlueColor;
937
+ uniform vec3 uEdgeColor;
938
+ varying vec2 vUv;
939
+
940
+ float bell(float value, float width) {
941
+ return exp(-pow(value / max(width, 0.001), 2.0));
942
+ }
943
+
944
+ void main() {
945
+ vec2 p = vUv * 2.0 - 1.0;
946
+ p.x *= uResolution.x / max(uResolution.y, 1.0);
947
+ float time = uTime * max(uSpeed, 0.0);
948
+ float depth = mix(0.025, 0.105, clamp(uThickness, 0.0, 1.0));
949
+ float radius = 0.79;
950
+ float distance = length(p) - radius;
951
+ float pixel = 2.0 / max(min(uResolution.x, uResolution.y), 1.0);
952
+ float alpha = 1.0 - smoothstep(-pixel, pixel, distance);
953
+ if (alpha < 0.001) discard;
954
+
955
+ float edge = smoothstep(0.66, 0.995, length(p) / radius);
956
+ vec2 normal = p / max(length(p), 0.001);
957
+ vec2 opticalWarp = normal * edge * max(uRefraction, 0.0) * 0.12;
958
+ opticalWarp += vec2(sin(p.y * 8.0 + time * 0.35), cos(p.x * 7.0 - time * 0.27)) * 0.002;
959
+ vec2 backdropUv = clamp(uBackdropOrigin + (vUv + opticalWarp) * uBackdropScale, 0.001, 0.999);
960
+ vec3 backdrop = texture2D(uBackdrop, backdropUv).rgb;
961
+
962
+ float wave = sin(p.x * 3.1 - time * 0.38) * 0.025;
963
+ float pinkBand = bell(p.y - 0.42 + wave, 0.20);
964
+ float pinkShelf = smoothstep(0.16, 0.30, p.y + wave);
965
+ float whiteCap = smoothstep(0.59, 0.75, p.y + wave) * 0.95;
966
+ vec3 face = backdrop * 0.19 + vec3(0.004, 0.002, 0.011);
967
+ face += uVioletColor * bell(p.y + 0.56, 0.18) * 0.10;
968
+ face += uBlueColor * bell(p.x - 0.49, 0.18) * bell(p.y + 0.22, 0.39) * 0.09;
969
+ face += uPinkColor * pinkBand * pinkShelf * 1.05;
970
+ face += uEdgeColor * whiteCap * 0.88;
971
+
972
+ float left = 1.0 - smoothstep(-0.68, -0.34, p.x);
973
+ float right = smoothstep(0.36, 0.68, p.x);
974
+ float upper = smoothstep(0.0, 0.58, p.y);
975
+ float lower = 1.0 - smoothstep(-0.52, -0.04, p.y);
976
+ float rim = bell(distance + 0.004, 0.007);
977
+ float bevel = bell(distance + depth * 0.55, depth * 0.48);
978
+ float innerArc = bell(distance + depth * 1.13, depth * 0.23) * right * lower;
979
+ face += uEdgeColor * rim * (0.61 + 0.35 * upper);
980
+ face += uPinkColor * bevel * (0.18 + 0.58 * left * upper);
981
+ face += uBlueColor * bevel * right * lower * 0.49;
982
+ face += uVioletColor * innerArc * 0.27;
983
+ face += uEdgeColor * bell(distance + depth * 1.3, depth * 0.31) * left * upper * 0.22;
984
+ float movingGlint = bell(p.x + 0.42 - sin(time * 0.47) * 0.075, 0.055);
985
+ face += uEdgeColor * movingGlint * bell(p.y - 0.26, 0.48) * left * 0.15;
986
+ gl_FragColor = vec4(clamp(face * max(uIntensity, 0.0), 0.0, 1.0) * alpha, alpha);
987
+ }
988
+ `;
989
+ var chromaticGlassPlateProgram = {
990
+ name: "chromaticGlassPlate",
991
+ fragmentSource: fragmentSource2,
992
+ usesBackdrop: true,
993
+ replacesBaseFill: true
994
+ };
995
+
996
+ // src/runtime/viewer/shaders/builtins/chromaticHalo.ts
997
+ var fragmentSource3 = `
998
+ precision highp float;
999
+
1000
+ uniform vec2 uResolution;
1001
+ uniform float uTime;
1002
+ uniform float uSpeed;
1003
+ uniform float uIntensity;
1004
+ uniform float uRingRadius;
1005
+ uniform float uRingWidth;
1006
+ uniform float uBloom;
1007
+ uniform float uMotion;
1008
+ uniform bool uLightMode;
1009
+ uniform vec3 uWarmColor;
1010
+ uniform vec3 uCoolColor;
1011
+ uniform vec3 uAccentColor;
1012
+ uniform vec3 uHighlightColor;
1013
+ varying vec2 vUv;
1014
+
1015
+ float bell(float value, float width) {
1016
+ return exp(-pow(value / max(width, 0.001), 2.0));
1017
+ }
1018
+
1019
+ float displacedRing(vec2 point, float radius, float width, float phase, float time, float motion) {
1020
+ float angle = atan(point.y, point.x);
1021
+ float wave = sin(angle * 2.0 + phase + time * 0.4) * 0.052;
1022
+ wave += sin(angle * 3.0 - phase * 0.45 + time * 0.27) * 0.025;
1023
+ wave += sin(angle * 6.0 - phase * 0.7 - time * 0.23) * 0.011;
1024
+ return bell(length(point) - radius - wave * motion, width);
1025
+ }
1026
+
1027
+ void main() {
1028
+ vec2 p = vUv * 2.0 - 1.0;
1029
+ p.x *= uResolution.x / max(uResolution.y, 1.0);
1030
+ float time = uTime * max(uSpeed, 0.0);
1031
+ float radius = clamp(uRingRadius, 0.25, 0.88);
1032
+ float width = max(uRingWidth, 0.025);
1033
+ float motion = max(uMotion, 0.0);
1034
+
1035
+ vec2 coolOffset = vec2(-0.052, -0.012) * (0.7 + motion * 0.3);
1036
+ vec2 warmOffset = vec2(0.042, 0.024) * (0.7 + motion * 0.3);
1037
+ vec2 accentOffset = vec2(0.016, -0.044) * (0.7 + motion * 0.3);
1038
+ float coolRing = displacedRing(p + coolOffset, radius, width * 1.18, 0.4, time, motion);
1039
+ float warmRing = displacedRing(p + warmOffset, radius, width * 1.05, 2.6, time, motion);
1040
+ float accentRing = displacedRing(p + accentOffset, radius, width, 4.8, time, motion);
1041
+ float coreRing = displacedRing(p, radius, width * 0.48, 1.7, time, motion);
1042
+
1043
+ vec3 color = uCoolColor * coolRing * 0.72;
1044
+ color += uWarmColor * warmRing * 0.68;
1045
+ color += uAccentColor * accentRing * 0.64;
1046
+ if (!uLightMode) {
1047
+ color += uHighlightColor * coreRing * 1.35;
1048
+ }
1049
+ float broadBloom = displacedRing(p, radius, width * 2.35, 0.0, time, motion);
1050
+ color += mix(uCoolColor, uWarmColor, smoothstep(-0.8, 0.8, p.y)) * broadBloom * max(uBloom, 0.0) * 0.2;
1051
+
1052
+ float ringAlpha = max(max(coolRing, warmRing), max(accentRing, coreRing));
1053
+ float alpha = clamp(ringAlpha * 0.9 + broadBloom * max(uBloom, 0.0) * 0.18, 0.0, 1.0);
1054
+ if (uLightMode) {
1055
+ color = mix(color, uHighlightColor, smoothstep(0.16, 0.82, coreRing));
1056
+ }
1057
+ float circleMask = 1.0 - smoothstep(0.965, 1.0, length(p));
1058
+ alpha *= circleMask * max(uIntensity, 0.0);
1059
+ color *= circleMask * max(uIntensity, 0.0);
1060
+
1061
+ gl_FragColor = vec4(clamp(color, 0.0, 1.0), clamp(alpha, 0.0, 1.0));
1062
+ }
1063
+ `;
1064
+ var chromaticHaloProgram = {
1065
+ name: "chromaticHalo",
1066
+ fragmentSource: fragmentSource3
1067
+ };
1068
+
1069
+ // src/runtime/viewer/shaders/builtins/eclipseOrb.ts
1070
+ var fragmentSource4 = `
1071
+ precision highp float;
1072
+
1073
+ uniform float uTime;
1074
+ uniform float uSpeed;
1075
+ uniform float uIntensity;
1076
+ uniform float uGlow;
1077
+ uniform float uDay;
1078
+ uniform vec3 uTopColor;
1079
+ uniform vec3 uMiddleColor;
1080
+ uniform vec3 uBottomColor;
1081
+ uniform vec3 uRimColor;
1082
+ varying vec2 vUv;
1083
+
1084
+ void main() {
1085
+ vec2 p = vUv * 2.0 - 1.0;
1086
+ float radius = length(p);
1087
+ float time = uTime * max(uSpeed, 0.0);
1088
+ float glow = max(uGlow, 0.0);
1089
+ float intensity = max(uIntensity, 0.0);
1090
+
1091
+ float current = sin(p.x * 4.2 - time * 0.75) * 0.025;
1092
+ current += sin(p.y * 5.0 + p.x * 2.3 + time * 0.55) * 0.018;
1093
+ float vertical = p.y + current;
1094
+ float useDay = step(0.0, uDay);
1095
+ float day = clamp(uDay, 0.0, 1.0);
1096
+ float capStart = mix(-0.04, -0.62 + day * 0.98, useDay);
1097
+ float tone = mix(1.0, mix(0.2, 1.2, day), useDay);
1098
+ vec3 bodyColor = mix(uBottomColor, uMiddleColor, smoothstep(-0.69, -0.06, vertical)) * tone;
1099
+ bodyColor = mix(bodyColor, uTopColor, smoothstep(capStart, capStart + 0.42, vertical));
1100
+
1101
+ float side = smoothstep(0.35, 0.9, abs(p.x) / max(radius, 0.001));
1102
+ float lower = 1.0 - smoothstep(-0.35, 0.38, p.y);
1103
+ float ring = exp(-pow((radius - 0.765) / 0.03, 2.0));
1104
+ float rimLight = ring * (0.28 + side * 0.55 + lower * 0.52) * mix(1.0, 0.35 + 0.65 * day, useDay);
1105
+ float crest = ring * smoothstep(0.3, 0.68, p.y) * mix(1.0, 0.15 + 0.85 * day, useDay);
1106
+ bodyColor = mix(bodyColor, uRimColor, clamp(rimLight * 0.78 * intensity, 0.0, 1.0));
1107
+ bodyColor = mix(bodyColor, vec3(1.0, 0.94, 0.86), clamp(crest * 0.84 * intensity, 0.0, 1.0));
1108
+
1109
+ float bodyAlpha = 1.0 - smoothstep(0.758, 0.778, radius);
1110
+ float halo = exp(-pow(max(radius - 0.765, 0.0) / 0.12, 2.0));
1111
+ halo *= (0.14 + rimLight * 0.28 + crest * 0.2) * glow * intensity;
1112
+ float crownGlow = exp(-pow(p.x / 0.48, 2.0) - pow((p.y - 0.83) / 0.16, 2.0));
1113
+ halo += crownGlow * 0.16 * glow * intensity;
1114
+ float outsideAlpha = clamp(halo, 0.0, 0.85) * (1.0 - smoothstep(0.84, 1.0, radius));
1115
+ vec3 haloColor = mix(uRimColor, vec3(1.0, 0.91, 0.77), crest * 0.55 + crownGlow * 0.45);
1116
+ float alpha = bodyAlpha + (1.0 - bodyAlpha) * outsideAlpha;
1117
+ vec3 premultipliedColor = bodyColor * bodyAlpha;
1118
+ premultipliedColor += haloColor * (1.0 - bodyAlpha) * outsideAlpha;
1119
+
1120
+ gl_FragColor = vec4(premultipliedColor, alpha);
1121
+ }
1122
+ `;
1123
+ var eclipseOrbProgram = {
1124
+ name: "eclipseOrb",
1125
+ fragmentSource: fragmentSource4
1126
+ };
1127
+
1128
+ // src/runtime/viewer/shaders/builtins/emberMatrix.ts
1129
+ var fragmentSource5 = `
1130
+ precision highp float;
1131
+
1132
+ uniform vec2 uResolution;
1133
+ uniform float uTime;
1134
+ uniform float uSpeed;
1135
+ uniform float uIntensity;
1136
+ uniform float uCellCount;
1137
+ uniform float uGrain;
1138
+ uniform vec3 uBaseColor;
1139
+ uniform vec3 uTopColor;
1140
+ uniform vec3 uCoreColor;
1141
+ uniform vec3 uBottomColor;
1142
+ uniform vec3 uCoolColor;
1143
+ varying vec2 vUv;
1144
+
1145
+ float band(float distance, float width) {
1146
+ return exp(-pow(distance / width, 2.0));
1147
+ }
1148
+
1149
+ float pixelNoise(vec2 uv) {
1150
+ vec2 pixel = floor(uv * uResolution);
1151
+ return fract(sin(dot(pixel, vec2(127.1, 311.7))) * 43758.5453);
1152
+ }
1153
+
1154
+ void main() {
1155
+ vec2 p = vUv * 2.0 - 1.0;
1156
+ float radius = length(p);
1157
+ if (radius >= 1.0) discard;
1158
+
1159
+ float time = uTime * max(uSpeed, 0.0);
1160
+ float cells = max(uCellCount, 1.0);
1161
+ vec2 grid = (p + 1.0) * cells * 0.5;
1162
+ vec2 cell = fract(grid) - 0.5;
1163
+ vec2 samplePoint = (floor(grid) + 0.5) * 2.0 / cells - 1.0;
1164
+ vec2 q = samplePoint;
1165
+ q.x += sin(samplePoint.y * 3.4 + time * 0.34) * 0.035;
1166
+ q.y += sin(samplePoint.x * 4.0 - time * 0.29) * 0.045;
1167
+
1168
+ float upperCurve = 0.56 + 0.11 * sin(q.x * 3.1 + time * 0.41);
1169
+ float topLight = band(q.y - upperCurve, 0.25) * (0.48 + 0.52 * abs(q.x));
1170
+ topLight += band(q.x + 0.77, 0.28) * band(q.y - 0.46, 0.35) * 0.58;
1171
+ topLight += band(q.x - 0.76, 0.28) * band(q.y - 0.43, 0.36) * 0.68;
1172
+ topLight *= 1.0 - band(q.x + 0.02, 0.45) * band(q.y - 0.78, 0.28) * 0.88;
1173
+
1174
+ float coreLight = band(q.x + 0.02 + 0.08 * sin(time * 0.28), 0.58);
1175
+ coreLight *= band(q.y + 0.19 + 0.07 * sin(q.x * 3.0 - time * 0.31), 0.44);
1176
+ float bottomLight = band(q.y + 0.82, 0.38) * band(q.x - 0.02, 0.85);
1177
+ float coolLight = band(q.x + 0.79, 0.31) * band(q.y + 0.03, 0.29);
1178
+ coolLight += band(q.x - 0.89, 0.23) * band(q.y + 0.02, 0.25) * 0.48;
1179
+
1180
+ float centerCut = band(q.y - 0.12 + 0.08 * sin(q.x * 3.2 + time * 0.33), 0.16);
1181
+ centerCut *= band(q.x + 0.03, 0.99);
1182
+ float rightCut = band(q.x - 0.68, 0.35) * band(q.y + 0.37, 0.26);
1183
+ float occlusion = clamp(centerCut * 0.78 + rightCut * 0.79, 0.0, 0.9);
1184
+
1185
+ vec3 color = uBaseColor;
1186
+ color += uTopColor * topLight * 0.91;
1187
+ color += uCoreColor * coreLight * 1.05;
1188
+ color += uBottomColor * bottomLight * 0.86;
1189
+ color += uCoolColor * coolLight * 0.68;
1190
+ color *= 1.0 - occlusion;
1191
+
1192
+ float squareRadius = max(abs(cell.x), abs(cell.y));
1193
+ float dot = 1.0 - smoothstep(0.23, 0.43, squareRadius);
1194
+ color *= 0.21 + dot * 0.79;
1195
+ float noise = (pixelNoise(vUv) - 0.5) * max(uGrain, 0.0);
1196
+ color += vec3(noise) * (0.25 + dot * 0.75);
1197
+
1198
+ float edge = 1.0 - smoothstep(0.955, 0.995, radius);
1199
+ gl_FragColor = vec4(clamp(color * max(uIntensity, 0.0) * edge, 0.0, 1.0), edge);
1200
+ }
1201
+ `;
1202
+ var emberMatrixProgram = {
1203
+ name: "emberMatrix",
1204
+ fragmentSource: fragmentSource5
1205
+ };
1206
+
1207
+ // src/runtime/viewer/shaders/builtins/emberSlits.ts
1208
+ var fragmentSource6 = `
1209
+ precision highp float;
1210
+
1211
+ uniform vec2 uResolution;
1212
+ uniform float uTime;
1213
+ uniform float uSpeed;
1214
+ uniform float uIntensity;
1215
+ uniform float uSlitCount;
1216
+ uniform float uOrientation;
1217
+ uniform bool uHole;
1218
+ uniform float uHoleSize;
1219
+ uniform float uGlow;
1220
+ uniform float uMotion;
1221
+ uniform vec3 uHotColor;
1222
+ uniform vec3 uCoreColor;
1223
+ uniform vec3 uHighlightColor;
1224
+ varying vec2 vUv;
1225
+
1226
+ float bell(float value, float width) {
1227
+ return exp(-pow(value / max(width, 0.001), 2.0));
1228
+ }
1229
+
1230
+ void main() {
1231
+ vec2 p = vUv * 2.0 - 1.0;
1232
+ p.x *= uResolution.x / max(uResolution.y, 1.0);
1233
+ float time = uTime * max(uSpeed, 0.0);
1234
+ float motion = max(uMotion, 0.0);
1235
+ float radius = length(p);
1236
+
1237
+ float wave = sin(p.y * 6.4 - time * 0.9) * 0.04 * motion;
1238
+ wave += sin(p.x * 5.1 + time * 0.6) * 0.026 * motion;
1239
+ float ringRadius = length(p + vec2(wave, 0.0));
1240
+ float holeSize = clamp(uHoleSize, 0.0, 0.72);
1241
+ float ring = uHole ? smoothstep(holeSize - 0.08, holeSize + 0.08, ringRadius) : 1.0;
1242
+ ring *= 1.0 - smoothstep(0.65, 0.83, ringRadius);
1243
+ float ringCore = bell(ringRadius - 0.56, 0.19);
1244
+
1245
+ float count = clamp(uSlitCount, 12.0, 90.0);
1246
+ vec2 slitPoint = uOrientation < 0.5 ? p : vec2(p.y, p.x);
1247
+ float phase = slitPoint.x * count * 3.14159265;
1248
+ phase += sin(slitPoint.y * 3.6 - time * 0.95) * 0.75 * motion;
1249
+ phase += time * 2.2 * motion;
1250
+ float bars = pow(0.5 + 0.5 * cos(phase), 1.9);
1251
+ float slit = 0.12 + 0.88 * bars;
1252
+ float sideFade = 1.0 - smoothstep(0.67, 0.91, abs(p.x));
1253
+
1254
+ float upperLight = bell(p.y - 0.45 - sin(time * 0.6) * 0.1 * motion, 0.21);
1255
+ float lowerLight = bell(p.y + 0.48 + sin(time * 0.5 + 1.7) * 0.1 * motion, 0.24);
1256
+ vec3 heat = mix(uHotColor, uCoreColor, smoothstep(-0.57, 0.55, p.y));
1257
+ heat = mix(heat, uHighlightColor, upperLight * 0.45 + lowerLight * 0.28);
1258
+ float molten = ring * slit * sideFade;
1259
+ vec3 color = heat * molten * (0.64 + ringCore * 0.68);
1260
+
1261
+ float verticalTrail = bell(max(ringRadius - 0.69, 0.0), 0.24);
1262
+ verticalTrail *= smoothstep(0.25, 0.75, abs(p.y));
1263
+ verticalTrail *= slit * sideFade;
1264
+ float bloom = bell(ringRadius - 0.57, 0.29) * ring;
1265
+ float glow = max(uGlow, 0.0);
1266
+ float glowMask = (verticalTrail * 0.25 + bloom * 0.17) * glow;
1267
+ color += heat * glowMask;
1268
+
1269
+ float frameFade = 1.0 - smoothstep(0.89, 1.0, max(abs(p.x), abs(p.y)));
1270
+ float alpha = clamp((molten * 0.92 + glowMask) * frameFade * max(uIntensity, 0.0), 0.0, 1.0);
1271
+ gl_FragColor = vec4(clamp(color * max(uIntensity, 0.0), 0.0, 1.0) * alpha, alpha);
1272
+ }
1273
+ `;
1274
+ var emberSlitsProgram = {
1275
+ name: "emberSlits",
1276
+ fragmentSource: fragmentSource6
1277
+ };
1278
+
1279
+ // src/runtime/viewer/shaders/builtins/glassRefraction.ts
1280
+ var fragmentSource7 = `
1281
+ precision highp float;
1282
+
1283
+ uniform sampler2D uBackdrop;
1284
+ uniform vec2 uBackdropOrigin;
1285
+ uniform vec2 uBackdropScale;
1286
+ uniform vec2 uResolution;
1287
+ uniform float uTime;
1288
+ uniform float uSpeed;
1289
+ uniform float uIntensity;
1290
+ uniform float uIor;
1291
+ uniform float uRefraction;
1292
+ uniform float uRoughness;
1293
+ uniform float uChromaticAberration;
1294
+ uniform float uEdgeLight;
1295
+ uniform vec3 uTint;
1296
+ uniform vec3 uEdgeColor;
1297
+ varying vec2 vUv;
1298
+
1299
+ float hash(vec2 point) {
1300
+ return fract(sin(dot(point, vec2(127.1, 311.7))) * 43758.5453123);
1301
+ }
1302
+
1303
+ float smoothNoise(vec2 point) {
1304
+ vec2 cell = floor(point);
1305
+ vec2 local = fract(point);
1306
+ local = local * local * (3.0 - 2.0 * local);
1307
+ float a = hash(cell);
1308
+ float b = hash(cell + vec2(1.0, 0.0));
1309
+ float c = hash(cell + vec2(0.0, 1.0));
1310
+ float d = hash(cell + vec2(1.0, 1.0));
1311
+ return mix(mix(a, b, local.x), mix(c, d, local.x), local.y);
1312
+ }
1313
+
1314
+ void main() {
1315
+ float time = uTime * max(uSpeed, 0.0);
1316
+ vec2 sphereOffset = vec2(
1317
+ sin(time * 0.72) * 0.04,
1318
+ cos(time * 0.56) * 0.026
1319
+ );
1320
+ float sphereScale = 0.95;
1321
+ vec2 p = (vUv * 2.0 - 1.0 - sphereOffset) / sphereScale;
1322
+ float radius = length(p);
1323
+ if (radius >= 1.0) discard;
1324
+
1325
+ float roughness = max(uRoughness, 0.0);
1326
+ vec2 flow = vec2(
1327
+ smoothNoise(p * 2.4 + vec2(0.7, -0.3)),
1328
+ smoothNoise(p * 2.8 + vec2(-0.4, 0.8))
1329
+ ) - 0.5;
1330
+ float sphereDepth = sqrt(max(1.0 - radius * radius, 0.0));
1331
+ vec2 glassMotion = vec2(
1332
+ sin(p.y * 3.2 + 0.6),
1333
+ cos(p.x * 2.8 - 0.35)
1334
+ ) * 0.014;
1335
+ vec3 surfaceNormal = normalize(vec3(p + glassMotion + flow * roughness * 0.22, sphereDepth));
1336
+ float eta = 1.0 / max(uIor, 1.001);
1337
+ vec3 refracted = refract(vec3(0.0, 0.0, -1.0), surfaceNormal, eta);
1338
+ vec2 distortion = refracted.xy * max(uRefraction, 0.0);
1339
+ distortion += flow * (0.008 + roughness * 0.025);
1340
+
1341
+ vec2 backdropUv = uBackdropOrigin + vUv * uBackdropScale;
1342
+ vec2 refractedUv = clamp(backdropUv + distortion * uBackdropScale, 0.001, 0.999);
1343
+ float chroma = max(uChromaticAberration, 0.0) * 0.012;
1344
+ vec2 chromaOffset = normalize(distortion + vec2(0.0001)) * chroma * uBackdropScale;
1345
+ float red = texture2D(uBackdrop, clamp(refractedUv + chromaOffset, 0.001, 0.999)).r;
1346
+ float green = texture2D(uBackdrop, refractedUv).g;
1347
+ float blue = texture2D(uBackdrop, clamp(refractedUv - chromaOffset, 0.001, 0.999)).b;
1348
+ vec3 sharpBackdrop = vec3(red, green, blue);
1349
+ vec2 texel = uBackdropScale / max(uResolution, vec2(1.0));
1350
+ vec2 blurOffset = texel * (0.65 + roughness * 5.0);
1351
+ vec3 blurredBackdrop = texture2D(uBackdrop, refractedUv + vec2(blurOffset.x, 0.0)).rgb;
1352
+ blurredBackdrop += texture2D(uBackdrop, refractedUv - vec2(blurOffset.x, 0.0)).rgb;
1353
+ blurredBackdrop += texture2D(uBackdrop, refractedUv + vec2(0.0, blurOffset.y)).rgb;
1354
+ blurredBackdrop += texture2D(uBackdrop, refractedUv - vec2(0.0, blurOffset.y)).rgb;
1355
+ blurredBackdrop *= 0.25;
1356
+ vec3 backdrop = mix(sharpBackdrop, blurredBackdrop, clamp(roughness * 1.15, 0.0, 1.0));
1357
+
1358
+ float fresnel = pow(1.0 - sphereDepth, 2.35);
1359
+ float innerRim = smoothstep(0.72, 0.995, radius);
1360
+ vec2 fixedLightPosition = vec2(-0.46, 0.7);
1361
+ vec2 surfacePosition = sphereOffset + p * sphereScale * 0.22;
1362
+ vec3 lightDirection = normalize(vec3(fixedLightPosition - surfacePosition, 0.62));
1363
+ float highlight = pow(max(dot(lightDirection, surfaceNormal), 0.0), 28.0);
1364
+ float hotSpot = pow(max(dot(lightDirection, surfaceNormal), 0.0), 92.0);
1365
+ float angle = atan(p.y, p.x);
1366
+ float lightAngle = atan(lightDirection.y, lightDirection.x);
1367
+ float movingRim = 0.68 + 0.32 * sin(angle * 2.0 - lightAngle);
1368
+ float rimGlint = pow(max(cos(angle - lightAngle), 0.0), 20.0) * innerRim;
1369
+ vec3 color = mix(backdrop, backdrop * uTint, 0.025 + fresnel * 0.09);
1370
+ color += uEdgeColor * innerRim * max(uEdgeLight, 0.0) * (0.1 + fresnel * 0.5) * movingRim;
1371
+ color += uEdgeColor * rimGlint * max(uEdgeLight, 0.0) * 0.72;
1372
+ color += uEdgeColor * highlight * 0.5;
1373
+ color += vec3(1.0) * hotSpot * 0.58;
1374
+ color += vec3(1.0) * fresnel * 0.05;
1375
+ color *= max(uIntensity, 0.0);
1376
+
1377
+ float edgeWidth = 2.2 / max(min(uResolution.x, uResolution.y), 1.0);
1378
+ float edgeAlpha = 1.0 - smoothstep(1.0 - edgeWidth, 1.0, radius);
1379
+ vec3 finalColor = clamp(color, 0.0, 1.0) * edgeAlpha;
1380
+ gl_FragColor = vec4(finalColor, edgeAlpha);
1381
+ }
1382
+ `;
1383
+ var glassRefractionProgram = {
1384
+ name: "glassRefraction",
1385
+ fragmentSource: fragmentSource7,
1386
+ usesBackdrop: true
1387
+ };
1388
+
1389
+ // src/runtime/viewer/shaders/builtins/halftoneHalo.ts
1390
+ var fragmentSource8 = `
1391
+ precision highp float;
1392
+
1393
+ uniform vec2 uResolution;
1394
+ uniform float uTime;
1395
+ uniform float uSpeed;
1396
+ uniform float uIntensity;
1397
+ uniform float uDotCount;
1398
+ uniform float uRingRadius;
1399
+ uniform float uRingWidth;
1400
+ uniform float uGlow;
1401
+ uniform float uMotion;
1402
+ uniform vec3 uWarmColor;
1403
+ uniform vec3 uLightColor;
1404
+ uniform vec3 uCoolColor;
1405
+ varying vec2 vUv;
1406
+
1407
+ mat2 rotate(float angle) {
1408
+ float sine = sin(angle);
1409
+ float cosine = cos(angle);
1410
+ return mat2(cosine, -sine, sine, cosine);
1411
+ }
1412
+
1413
+ float bell(float value, float width) {
1414
+ return exp(-pow(value / max(width, 0.001), 2.0));
1415
+ }
1416
+
1417
+ void main() {
1418
+ vec2 p = vUv * 2.0 - 1.0;
1419
+ p.x *= uResolution.x / max(uResolution.y, 1.0);
1420
+
1421
+ float speed = max(uSpeed, 0.0);
1422
+ float motion = max(uMotion, 0.0);
1423
+ float time = uTime * speed;
1424
+ float rotation = time * 0.08 * motion;
1425
+ vec2 gridPosition = rotate(rotation) * p;
1426
+ float cells = max(uDotCount, 8.0);
1427
+ vec2 grid = (gridPosition + 1.0) * cells * 0.5;
1428
+ vec2 cell = fract(grid) - 0.5;
1429
+ vec2 samplePoint = (floor(grid) + 0.5) * 2.0 / cells - 1.0;
1430
+ samplePoint = rotate(-rotation) * samplePoint;
1431
+
1432
+ float angle = atan(samplePoint.y, samplePoint.x);
1433
+ float radius = length(samplePoint);
1434
+ float travelingWave = sin(angle * 3.0 - time * 0.72) * 0.024 * motion;
1435
+ travelingWave += sin(angle * 7.0 + time * 0.31) * 0.009 * motion;
1436
+ float ringRadius = clamp(uRingRadius, 0.2, 0.92) + travelingWave;
1437
+ float ringWidth = max(uRingWidth, 0.025);
1438
+ float ring = bell(radius - ringRadius, ringWidth);
1439
+
1440
+ float lowerBloom = bell(samplePoint.x + 0.08, 0.72) * bell(samplePoint.y + 0.56, 0.42);
1441
+ float upperArc = bell(radius - ringRadius + 0.025, ringWidth * 0.7) * smoothstep(-0.18, 0.72, samplePoint.y);
1442
+ float light = clamp(ring * (0.58 + lowerBloom * 0.64 + upperArc * 0.25), 0.0, 1.0);
1443
+ float shimmer = 0.9 + 0.1 * sin(angle * 9.0 + time * 0.48);
1444
+ float dotRadius = mix(0.055, 0.43, pow(light, 0.72) * shimmer);
1445
+ float dotDistance = length(cell);
1446
+ float dot = 1.0 - smoothstep(dotRadius - 0.045, dotRadius + 0.045, dotDistance);
1447
+
1448
+ float warmWeight = smoothstep(-0.22, 0.72, samplePoint.y);
1449
+ warmWeight *= 0.76 + 0.24 * (1.0 - smoothstep(-0.45, 0.72, samplePoint.x));
1450
+ float coolWeight = smoothstep(-0.85, 0.42, samplePoint.x);
1451
+ coolWeight *= 1.0 - smoothstep(-0.78, 0.3, samplePoint.y);
1452
+ vec3 color = mix(uLightColor, uWarmColor, clamp(warmWeight, 0.0, 1.0));
1453
+ color = mix(color, uCoolColor, clamp(coolWeight * 0.72, 0.0, 0.82));
1454
+ color = mix(color, uLightColor, lowerBloom * ring * 0.58);
1455
+
1456
+ float circleMask = 1.0 - smoothstep(0.965, 1.0, length(p));
1457
+ float dotAlpha = dot * pow(light, 0.82) * smoothstep(0.06, 0.24, light) * circleMask;
1458
+ float glow = bell(length(p) - ringRadius, ringWidth * 1.75) * max(uGlow, 0.0) * 0.17;
1459
+ glow *= circleMask;
1460
+ float alpha = clamp((dotAlpha + glow) * max(uIntensity, 0.0), 0.0, 1.0);
1461
+ vec3 glowColor = mix(uWarmColor, uCoolColor, 1.0 - smoothstep(-0.75, 0.55, samplePoint.y));
1462
+ vec3 finalColor = color * dotAlpha + glowColor * glow;
1463
+
1464
+ gl_FragColor = vec4(clamp(finalColor * max(uIntensity, 0.0), 0.0, 1.0), alpha);
1465
+ }
1466
+ `;
1467
+ var halftoneHaloProgram = {
1468
+ name: "halftoneHalo",
1469
+ fragmentSource: fragmentSource8
1470
+ };
1471
+
1472
+ // src/runtime/viewer/shaders/builtins/halo.ts
1473
+ var fragmentSource9 = `
1474
+ precision highp float;
1475
+
1476
+ uniform vec2 uResolution;
1477
+ uniform float uTime;
1478
+ uniform vec3 uColor;
1479
+ uniform vec3 uAccent;
1480
+ uniform float uIntensity;
1481
+ uniform float uSpeed;
1482
+ uniform float uThickness;
1483
+ uniform float uProgress;
1484
+ uniform float uBoost;
1485
+ varying vec2 vUv;
1486
+
1487
+ const float TWO_PI = 6.28318530718;
1488
+
1489
+ void main() {
1490
+ vec2 p = vUv * 2.0 - 1.0;
1491
+ float radius = length(p);
1492
+ if (radius > 1.0) discard;
1493
+
1494
+ float angle = atan(p.y, p.x);
1495
+ float time = uTime * max(uSpeed, 0.0);
1496
+ float controlled = step(0.0, uProgress);
1497
+ float progress = clamp(uProgress, 0.0, 1.0);
1498
+ float boost = clamp(uBoost, 0.0, 1.0);
1499
+ float width = max(uThickness, 0.015);
1500
+ float wave = sin(angle * 2.0 - time * 2.4) * 0.5 + 0.5;
1501
+ float fineWave = sin(angle * 7.0 + time * 1.15) * 0.5 + 0.5;
1502
+ float ringRadius = 0.72 + sin(angle * 3.0 - time * 1.5) * (0.025 + boost * 0.012);
1503
+ float ring = exp(-pow((radius - ringRadius) / width, 2.0));
1504
+ float outerRim = exp(-pow((radius - 0.94) / 0.025, 2.0));
1505
+
1506
+ float startAngle = -2.35;
1507
+ float sweep = 4.70;
1508
+ float arcPosition = mod(startAngle - angle + TWO_PI, TWO_PI);
1509
+ float arcHead = progress * sweep;
1510
+ float idleTravel = sin(time * 0.82) * 0.025 * (1.0 - boost);
1511
+ float displayedHead = clamp(arcHead + idleTravel, 0.0, sweep);
1512
+ float arcEdge = 0.025;
1513
+ float arcMask = (1.0 - smoothstep(displayedHead - arcEdge, displayedHead + arcEdge, arcPosition));
1514
+ arcMask *= 1.0 - step(sweep, arcPosition);
1515
+ float arcActive = smoothstep(0.0, 0.012, progress);
1516
+ arcMask *= arcActive;
1517
+
1518
+ float ambientHead = -2.25 + sin(time * 0.45) * 0.35;
1519
+ float progressHead = startAngle - displayedHead;
1520
+ float headAngle = mix(ambientHead, progressHead, controlled);
1521
+ float angleFromHead = atan(sin(angle - headAngle), cos(angle - headAngle));
1522
+ float flare = exp(-pow(angleFromHead / (0.18 + boost * 0.035), 2.0));
1523
+ flare *= exp(-pow((radius - 0.73) / 0.12, 2.0));
1524
+ float ambientBand = ring * (0.32 + wave * 0.38);
1525
+ float startDelta = atan(sin(angle - startAngle), cos(angle - startAngle));
1526
+ float radialDelta = radius - ringRadius;
1527
+ float startCapDistance = length(vec2(radialDelta, startDelta * ringRadius));
1528
+ float endCapDistance = length(vec2(radialDelta, angleFromHead * ringRadius));
1529
+ float roundedCaps = max(
1530
+ exp(-pow(startCapDistance / width, 2.0)),
1531
+ exp(-pow(endCapDistance / width, 2.0))
1532
+ ) * arcActive;
1533
+ float progressShape = max(ring * arcMask, roundedCaps);
1534
+ float progressBand = progressShape * (0.72 + boost * 0.55);
1535
+ float idleBand = ring * (0.16 + wave * 0.16 + fineWave * 0.06);
1536
+ float band = mix(ambientBand, idleBand + progressBand, controlled);
1537
+ float pulse = 1.0 + boost * (0.18 + sin(time * 8.0) * 0.08);
1538
+ float energy = clamp((band + outerRim * (0.24 + boost * 0.12) + flare * (1.35 + boost * 0.55) + fineWave * ring * 0.12) * uIntensity * pulse, 0.0, 1.0);
1539
+ vec3 base = uColor * (0.1 + 0.28 * (1.0 - radius));
1540
+ vec3 color = mix(base, uAccent, energy);
1541
+ color += uAccent * flare * (0.3 + boost * 0.35);
1542
+ float alpha = 1.0 - smoothstep(0.96, 1.0, radius);
1543
+ gl_FragColor = vec4(color, alpha);
1544
+ }
1545
+ `;
1546
+ var haloProgram = {
1547
+ name: "halo",
1548
+ fragmentSource: fragmentSource9
1549
+ };
1550
+
1551
+ // src/runtime/viewer/shaders/builtins/lumenPortal.ts
1552
+ var fragmentSource10 = `
1553
+ precision highp float;
1554
+
1555
+ uniform float uTime;
1556
+ uniform float uSpeed;
1557
+ uniform float uIntensity;
1558
+ uniform vec3 uBaseColor;
1559
+ uniform vec3 uCoolColor;
1560
+ uniform vec3 uWarmColor;
1561
+ varying vec2 vUv;
1562
+
1563
+ float band(float distance, float width) {
1564
+ return exp(-pow(distance / width, 2.0));
1565
+ }
1566
+
1567
+ void main() {
1568
+ vec2 p = vUv * 2.0 - 1.0;
1569
+ float time = uTime * max(uSpeed, 0.0);
1570
+ vec2 q = p;
1571
+ q.x += sin(p.y * 4.0 + time * 0.58) * 0.02;
1572
+ q.y += sin(p.x * 3.3 - time * 0.43) * 0.013;
1573
+ float distance = length(q) - 0.64;
1574
+ float body = 1.0 - smoothstep(-0.012, 0.01, distance);
1575
+ float nearGlow = band(max(distance, 0.0), 0.19) * 0.62;
1576
+ float wideGlow = band(max(distance, 0.0), 0.48) * 0.3;
1577
+ float frameFade = 1.0 - smoothstep(0.88, 1.0, length(p));
1578
+ float halo = max(nearGlow, wideGlow) * frameFade;
1579
+ float alpha = body + (1.0 - body) * halo;
1580
+ if (alpha < 0.002) discard;
1581
+
1582
+ float vertical = 0.5 + 0.5 * q.y;
1583
+ float warmWeight = smoothstep(-0.44, 0.42, q.x + q.y * 0.14 + sin(time * 0.62 + q.y * 3.0) * 0.065);
1584
+ vec3 glowColor = mix(uCoolColor, uWarmColor, warmWeight);
1585
+ float breath = 0.78 + 0.22 * sin(time * 1.12 + q.y * 3.0);
1586
+ float rim = band(distance + 0.014, 0.042);
1587
+ float edge = band(distance + 0.006, 0.014);
1588
+ float bevel = band(distance + 0.092, 0.07);
1589
+
1590
+ vec3 color = uBaseColor * (0.62 + 0.23 * vertical);
1591
+ color += uWarmColor * smoothstep(-0.68, 0.6, q.x) * 0.13;
1592
+ color += uWarmColor * band(q.x - 0.38, 0.32) * band(q.y + 0.14, 0.74) * 0.11;
1593
+ color += uCoolColor * band(q.x + 0.28, 0.42) * band(q.y - 0.47, 0.35) * 0.11;
1594
+ color *= 1.0 - band(q.x - 0.06, 0.3) * band(q.y + 0.2, 0.5) * 0.46;
1595
+ color += glowColor * bevel * 0.14;
1596
+ color += glowColor * rim * 0.41 * breath;
1597
+ color += mix(uCoolColor, vec3(1.0), 0.45) * edge * (1.0 - warmWeight) * 0.43;
1598
+ color += mix(uWarmColor, vec3(1.0), 0.38) * edge * warmWeight * 0.45;
1599
+
1600
+ float topFlare = band(q.y - 0.6, 0.07) * band(q.x + 0.2, 0.28);
1601
+ float bottomFlare = band(q.y + 0.61, 0.07) * band(q.x - 0.15, 0.35);
1602
+ color += uCoolColor * topFlare * 0.39;
1603
+ color += uWarmColor * bottomFlare * 0.31;
1604
+ vec3 premultiplied = color * body + glowColor * (1.0 - body) * halo * breath;
1605
+ gl_FragColor = vec4(clamp(premultiplied * max(uIntensity, 0.0), 0.0, 1.0), alpha);
1606
+ }
1607
+ `;
1608
+ var lumenPortalProgram = {
1609
+ name: "lumenPortal",
1610
+ fragmentSource: fragmentSource10
1611
+ };
1612
+
1613
+ // src/runtime/viewer/shaders/builtins/lumenPortalLight.ts
1614
+ var fragmentSource11 = `
1615
+ precision highp float;
1616
+
1617
+ uniform float uTime;
1618
+ uniform float uSpeed;
1619
+ uniform float uIntensity;
1620
+ uniform vec3 uBaseColor;
1621
+ uniform vec3 uCoolColor;
1622
+ uniform vec3 uWarmColor;
1623
+ uniform vec3 uShadowColor;
1624
+ varying vec2 vUv;
1625
+
1626
+ float band(float distance, float width) {
1627
+ return exp(-pow(distance / width, 2.0));
1628
+ }
1629
+
1630
+ void main() {
1631
+ vec2 p = vUv * 2.0 - 1.0;
1632
+ float time = uTime * max(uSpeed, 0.0);
1633
+ vec2 q = p;
1634
+ q.x += sin(p.y * 4.1 + time * 0.55) * 0.013;
1635
+ q.y += sin(p.x * 3.4 - time * 0.41) * 0.01;
1636
+ float distance = length(q) - 0.65;
1637
+ float body = 1.0 - smoothstep(-0.012, 0.012, distance);
1638
+ float nearHalo = band(max(distance, 0.0), 0.12) * 0.19;
1639
+ float wideHalo = band(max(distance, 0.0), 0.28) * 0.055;
1640
+ float frameFade = 1.0 - smoothstep(0.89, 1.0, length(p));
1641
+ float outside = max(nearHalo, wideHalo) * frameFade;
1642
+ float alpha = body * 0.96 + (1.0 - body) * outside;
1643
+ if (alpha < 0.002) discard;
1644
+
1645
+ float warmWeight = smoothstep(-0.46, 0.47, q.x + q.y * 0.17 + sin(time * 0.58 + q.y * 3.0) * 0.052);
1646
+ vec3 rimColor = mix(uCoolColor, uWarmColor, warmWeight);
1647
+ float breath = 0.84 + 0.16 * sin(time * 0.91 + q.y * 2.5);
1648
+ float innerRim = band(distance + 0.038, 0.052);
1649
+ float outerRim = band(distance + 0.002, 0.025);
1650
+ float fineEdge = band(distance + 0.008, 0.01);
1651
+ float bevelShade = band(distance + 0.11, 0.055);
1652
+
1653
+ vec3 color = uBaseColor;
1654
+ color = mix(color, uCoolColor, band(q.x + 0.27, 0.39) * band(q.y - 0.35, 0.43) * 0.09);
1655
+ color = mix(color, uWarmColor, band(q.x - 0.31, 0.43) * band(q.y + 0.31, 0.48) * 0.085);
1656
+ color = mix(color, uShadowColor, bevelShade * 0.11);
1657
+ color = mix(color, rimColor, innerRim * 0.46 * breath);
1658
+ color = mix(color, rimColor, outerRim * 0.62 * breath);
1659
+ color = mix(color, uShadowColor, fineEdge * 0.12);
1660
+
1661
+ float topGlint = band(q.y - 0.6, 0.06) * band(q.x + 0.2, 0.22);
1662
+ float bottomGlint = band(q.y + 0.61, 0.06) * band(q.x - 0.16, 0.29);
1663
+ color += uCoolColor * topGlint * 0.12;
1664
+ color += uWarmColor * bottomGlint * 0.1;
1665
+
1666
+ vec3 premultiplied = color * body * 0.96;
1667
+ premultiplied += mix(uShadowColor, rimColor, 0.58) * (1.0 - body) * outside * breath;
1668
+ gl_FragColor = vec4(clamp(premultiplied * max(uIntensity, 0.0), 0.0, 1.0), alpha);
1669
+ }
1670
+ `;
1671
+ var lumenPortalLightProgram = {
1672
+ name: "lumenPortalLight",
1673
+ fragmentSource: fragmentSource11
1674
+ };
1675
+
1676
+ // src/runtime/viewer/shaders/builtins/orb.ts
1677
+ var fragmentSource12 = `
1678
+ precision highp float;
1679
+
1680
+ uniform vec2 uResolution;
1681
+ uniform float uTime;
1682
+ uniform vec3 uColor;
1683
+ uniform vec3 uAccent;
1684
+ uniform float uIntensity;
1685
+ uniform float uSpeed;
1686
+ varying vec2 vUv;
1687
+
1688
+ float hash(vec2 p) {
1689
+ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
1690
+ }
1691
+
1692
+ void main() {
1693
+ vec2 p = vUv * 2.0 - 1.0;
1694
+ float radius = length(p);
1695
+ if (radius > 1.0) discard;
1696
+
1697
+ float angle = atan(p.y, p.x);
1698
+ float time = uTime * max(uSpeed, 0.0);
1699
+ float fold = sin(angle * 3.0 - time * 1.7 + radius * 7.0);
1700
+ float ribbon = exp(-pow(radius - (0.69 + fold * 0.07), 2.0) * 95.0);
1701
+ float rim = smoothstep(0.58, 0.98, radius) * (1.0 - smoothstep(0.94, 1.0, radius));
1702
+ float light = exp(-length(p - vec2(-0.42, -0.34)) * 4.2);
1703
+ float grain = (hash(gl_FragCoord.xy + time) - 0.5) * 0.035;
1704
+ float energy = clamp((ribbon * 0.8 + rim * 0.55 + light * 0.7) * uIntensity, 0.0, 1.0);
1705
+ vec3 color = mix(uColor * (0.12 + 0.32 * (1.0 - radius)), uAccent, energy);
1706
+ color += grain;
1707
+ float alpha = 1.0 - smoothstep(0.94, 1.0, radius);
1708
+ gl_FragColor = vec4(color, alpha);
1709
+ }
1710
+ `;
1711
+ var orbProgram = {
1712
+ name: "orb",
1713
+ fragmentSource: fragmentSource12
1714
+ };
1715
+
1716
+ // src/runtime/viewer/shaders/builtins/pixelConstellation.ts
1717
+ var fragmentSource13 = `
1718
+ precision highp float;
1719
+
1720
+ uniform float uTime;
1721
+ uniform float uSpeed;
1722
+ uniform float uIntensity;
1723
+ uniform float uCellCount;
1724
+ uniform float uDensity;
1725
+ uniform vec3 uBaseColor;
1726
+ uniform vec3 uTopColor;
1727
+ uniform vec3 uWarmColor;
1728
+ uniform vec3 uCenterColor;
1729
+ uniform vec3 uVioletColor;
1730
+ uniform vec3 uBottomColor;
1731
+ varying vec2 vUv;
1732
+
1733
+ float band(float distance, float width) {
1734
+ return exp(-pow(distance / width, 2.0));
1735
+ }
1736
+
1737
+ float hash(vec2 p) {
1738
+ return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453);
1739
+ }
1740
+
1741
+ float noise(vec2 p) {
1742
+ vec2 cell = floor(p);
1743
+ vec2 blend = fract(p);
1744
+ blend = blend * blend * (3.0 - 2.0 * blend);
1745
+ float lower = mix(hash(cell), hash(cell + vec2(1.0, 0.0)), blend.x);
1746
+ float upper = mix(hash(cell + vec2(0.0, 1.0)), hash(cell + vec2(1.0, 1.0)), blend.x);
1747
+ return mix(lower, upper, blend.y);
1748
+ }
1749
+
1750
+ vec3 strandColor(float y) {
1751
+ vec3 color = mix(uBottomColor, uVioletColor, smoothstep(-0.9, -0.43, y));
1752
+ color = mix(color, uCenterColor, smoothstep(-0.38, 0.05, y));
1753
+ color = mix(color, uWarmColor, smoothstep(0.14, 0.48, y));
1754
+ return mix(color, uTopColor, smoothstep(0.64, 0.9, y));
1755
+ }
1756
+
1757
+ void main() {
1758
+ vec2 p = vUv * 2.0 - 1.0;
1759
+ float radius = length(p);
1760
+ if (radius >= 1.0) discard;
1761
+
1762
+ float time = uTime * max(uSpeed, 0.0);
1763
+ float cells = max(uCellCount, 1.0);
1764
+ vec2 grid = (p + 1.0) * cells * 0.5;
1765
+ vec2 cellId = floor(grid);
1766
+ vec2 q = (cellId + 0.5) * 2.0 / cells - 1.0;
1767
+ vec2 flow = vec2(
1768
+ 0.1 * sin(time * 0.48 + q.y * 2.6),
1769
+ 0.08 * cos(time * 0.4 + q.x * 2.1)
1770
+ );
1771
+ vec2 movingQ = q + flow;
1772
+
1773
+ float path = -0.29 + 0.19 * sin(movingQ.y * 4.0 + time * 0.27);
1774
+ path += 0.09 * sin(movingQ.y * 7.7 - time * 0.19);
1775
+ float strand = band(movingQ.x - path, 0.26) * 0.11;
1776
+ float top = band(movingQ.x + 0.52, 0.28) * band(movingQ.y - 0.78, 0.19) * 0.75;
1777
+ float warm = band(movingQ.x + 0.46, 0.2) * band(movingQ.y - 0.47, 0.23) * 0.76;
1778
+ float center = band(movingQ.x + 0.06, 0.25) * band(movingQ.y - 0.02, 0.21) * 1.14;
1779
+ float lower = band(movingQ.x + 0.38, 0.32) * band(movingQ.y + 0.48, 0.27) * 0.86;
1780
+ float bottom = band(movingQ.x + 0.56, 0.36) * band(movingQ.y + 0.85, 0.19) * 0.88;
1781
+ float blue = band(movingQ.x - 0.28, 0.19) * band(movingQ.y + 0.64, 0.18) * 0.94;
1782
+ float field = strand + top + warm + center + lower + bottom + blue;
1783
+ field *= 0.35 + 1.25 * noise(movingQ * 5.1 + vec2(time * 0.075, -time * 0.04));
1784
+
1785
+ float chance = clamp(field * max(uDensity, 0.0), 0.0, 0.96);
1786
+ float seed = hash(cellId);
1787
+ float active = smoothstep(seed - 0.055, seed + 0.055, chance);
1788
+ vec2 local = fract(grid) - 0.5;
1789
+ local -= vec2(
1790
+ 0.11 * sin(time * 0.72 + seed * 6.28318),
1791
+ 0.11 * cos(time * 0.61 + seed * 4.71239)
1792
+ );
1793
+ float growth = 0.78 + 0.34 * (0.5 + 0.5 * sin(time * 0.83 + seed * 5.3));
1794
+ float size = mix(0.27, 0.34, hash(cellId + vec2(19.3, 7.1))) * growth;
1795
+ float squareRadius = max(abs(local.x), abs(local.y));
1796
+ float outer = 1.0 - smoothstep(size - 0.045, size + 0.015, squareRadius);
1797
+ float inner = 1.0 - smoothstep(size - 0.17, size - 0.105, squareRadius);
1798
+ float outline = max(outer - inner, 0.0);
1799
+ float glow = band(squareRadius - size + 0.075, 0.115) * 0.24;
1800
+
1801
+ float pulse = 0.76 + 0.24 * sin(time * 1.1 - q.y * 5.1 + seed * 6.28318);
1802
+ float variation = 0.43 + 0.57 * hash(cellId + vec2(3.7, 27.4));
1803
+ float highlight = band(movingQ.x + 0.07, 0.25) * band(movingQ.y - 0.02, 0.24);
1804
+ vec3 colorSum = strandColor(movingQ.y) * strand;
1805
+ colorSum += uTopColor * top + uWarmColor * warm + uCenterColor * center;
1806
+ colorSum += uVioletColor * lower + uBottomColor * (bottom + blue);
1807
+ vec3 cellColor = colorSum / max(strand + top + warm + center + lower + bottom + blue, 0.001);
1808
+ cellColor = mix(cellColor, vec3(1.0, 0.96, 0.76), highlight * 0.54);
1809
+ vec3 color = uBaseColor + cellColor * active * variation * pulse * (outline + glow);
1810
+
1811
+ float edge = 1.0 - smoothstep(0.965, 0.997, radius);
1812
+ gl_FragColor = vec4(clamp(color * max(uIntensity, 0.0) * edge, 0.0, 1.0), edge);
1813
+ }
1814
+ `;
1815
+ var pixelConstellationProgram = {
1816
+ name: "pixelConstellation",
1817
+ fragmentSource: fragmentSource13
1818
+ };
1819
+
1820
+ // src/runtime/viewer/shaders/builtins/plasmaGauge.ts
1821
+ var fragmentSource14 = `
1822
+ precision highp float;
1823
+
1824
+ uniform vec2 uResolution;
1825
+ uniform float uTime;
1826
+ uniform float uSpeed;
1827
+ uniform float uIntensity;
1828
+ uniform float uProgress;
1829
+ uniform float uDent;
1830
+ uniform float uBoost;
1831
+ uniform float uFlare;
1832
+ uniform float uStart;
1833
+ uniform float uSweep;
1834
+ uniform vec3 uColor;
1835
+ uniform vec3 uAccent;
1836
+ uniform vec3 uHot;
1837
+ varying vec2 vUv;
1838
+
1839
+ float bell(float value, float width) {
1840
+ return exp(-pow(value / max(width, 0.001), 2.0));
1841
+ }
1842
+
1843
+ float wrap(float angle) {
1844
+ return atan(sin(angle), cos(angle));
1845
+ }
1846
+
1847
+ void main() {
1848
+ vec2 p = vUv * 2.0 - 1.0;
1849
+ p.x *= uResolution.x / max(uResolution.y, 1.0);
1850
+ float time = uTime * max(uSpeed, 0.0);
1851
+ float radius = length(p);
1852
+ float angle = atan(p.y, p.x);
1853
+
1854
+ float sweep = radians(uSweep > 0.0 ? uSweep : 270.0);
1855
+ float start = radians(uStart != 0.0 ? uStart : 225.0);
1856
+ float progress = clamp(uProgress < 0.0 ? 0.35 : uProgress, 0.0, 1.0);
1857
+ float needleAngle = start - progress * sweep;
1858
+ float delta = wrap(angle - needleAngle);
1859
+
1860
+ float dentValue = clamp(uDent < 0.0 ? progress : uDent, 0.0, 1.0);
1861
+ float engage = smoothstep(0.0, 0.28, dentValue);
1862
+ float boost = smoothstep(0.0, 1.0, clamp(uBoost, 0.0, 1.0));
1863
+ float energy = smoothstep(0.0, 1.0, dentValue);
1864
+ float level = (uFlare > 0.0 ? uFlare : 1.0) * engage * (0.45 + 0.5 * energy + 0.3 * boost);
1865
+
1866
+ float heat = smoothstep(0.15, 1.0, clamp(0.75 * energy + 0.5 * boost, 0.0, 1.0));
1867
+ vec3 cool = mix(uAccent, vec3(0.18, 0.28, 0.95), 0.55);
1868
+ vec3 hot = uHot;
1869
+ vec3 tint = mix(cool, hot, heat);
1870
+
1871
+ float ripple = 0.5 + 0.5 * sin(delta * 9.0 - time * 1.4);
1872
+ float wake = (progress - dentValue) * sweep;
1873
+ float lo = min(0.0, wake);
1874
+ float hi = max(0.0, wake);
1875
+ float segment = delta < lo ? lo - delta : (delta > hi ? delta - hi : 0.0);
1876
+ float wakePos = hi > lo ? clamp((delta - lo) / (hi - lo), 0.0, 1.0) : 0.0;
1877
+ float fromNeedle = wake >= 0.0 ? wakePos : 1.0 - wakePos;
1878
+ float taper = mix(1.0, 0.45, fromNeedle * smoothstep(0.02, 0.2, abs(wake)));
1879
+ float dent = bell(segment, 0.13 + 0.02 * ripple) * (0.82 + 0.18 * ripple) * taper;
1880
+ float shoulder = bell(delta, 0.34);
1881
+ float ahead = wake >= 0.0 ? -1.0 : 1.0;
1882
+ float pile = bell(delta - ahead * 0.17, 0.15) * clamp(abs(wake) * 2.2, 0.0, 1.0) * engage;
1883
+ float baseRadius = 0.72;
1884
+ float orbRadius = baseRadius * (1.0 - 0.32 * level * dent - 0.07 * level * shoulder + 0.13 * pile);
1885
+ float body = 1.0 - smoothstep(orbRadius - 0.012, orbRadius + 0.012, radius);
1886
+ float depth = clamp(radius / orbRadius, 0.0, 1.25);
1887
+ float roundDepth = clamp(radius / baseRadius, 0.0, 1.2);
1888
+
1889
+ float local = clamp(heat * (0.7 + 0.3 * bell(delta, 0.9)) + 0.25 * heat * bell(delta, 0.3), 0.0, 1.0);
1890
+ vec3 rim = mix(cool, hot, local);
1891
+ vec3 bodyColor = mix(uColor, rim * (0.5 + 0.25 * energy), pow(roundDepth, 2.6));
1892
+ bodyColor += mix(uAccent, hot, local) * (0.26 + 0.3 * energy) * bell(roundDepth - 0.93, 0.13);
1893
+ bodyColor *= (0.82 + 0.18 * smoothstep(-0.5, 0.5, -p.x)) * (0.9 + 0.6 * energy * energy);
1894
+
1895
+ float ribbons = 0.5 + 0.5 * sin(depth * 24.0 - delta * 8.0 + time * 1.1);
1896
+ float zone = max(bell(delta, 0.3), dent * 0.8) * bell(depth - 0.84, 0.32) * level * (1.0 + 0.9 * energy);
1897
+ vec3 ribbonColor = mix(tint, hot, smoothstep(0.2, 0.9, ribbons) * (0.35 + 0.65 * heat));
1898
+ bodyColor = mix(bodyColor, ribbonColor, clamp(zone * (0.4 + 0.45 * ribbons), 0.0, 1.0));
1899
+ float edge = max(max(bell(delta, 0.2), dent * 0.7), pile * 0.8) * bell(depth - 0.99, 0.09) * level;
1900
+ bodyColor += mix(hot, vec3(1.0, 0.92, 1.0), 0.45) * edge * (0.45 + 1.3 * energy);
1901
+
1902
+ float outside = max(radius - orbRadius, 0.0);
1903
+ float notchLight = max(max(bell(delta, 0.2), dent * 0.8), pile * 0.6) * level;
1904
+ float halo = exp(-outside * (12.0 - 6.0 * notchLight * (0.4 + energy))) * (1.0 - body) * (0.2 + 0.5 * energy * energy + 0.9 * notchLight * (0.5 + energy));
1905
+ vec3 haloColor = mix(tint, hot, clamp(notchLight * 1.2, 0.0, 1.0));
1906
+
1907
+ float cross = abs(p.x * sin(needleAngle) - p.y * cos(needleAngle));
1908
+ float along = p.x * cos(needleAngle) + p.y * sin(needleAngle);
1909
+ float needleSpan = smoothstep(0.1, 0.2, along) * (1.0 - smoothstep(0.9, 0.96, along));
1910
+ float needle = smoothstep(0.0055, 0.0008, cross) * needleSpan;
1911
+ float needleGlow = bell(cross, 0.02) * needleSpan * (0.25 + 0.35 * energy);
1912
+
1913
+ vec3 color = bodyColor * body + haloColor * halo + vec3(1.0, 0.94, 1.0) * needle + hot * needleGlow;
1914
+ float alpha = max(max(body, clamp(halo, 0.0, 1.0)), max(needle, needleGlow * 0.8));
1915
+
1916
+ float fade = 1.0 - smoothstep(0.88, 0.99, radius);
1917
+ float gain = max(uIntensity, 0.0) * fade;
1918
+ gl_FragColor = vec4(clamp(color * gain, 0.0, 1.0), clamp(alpha * gain, 0.0, 1.0));
1919
+ }
1920
+ `;
1921
+ var plasmaGaugeProgram = {
1922
+ name: "plasmaGauge",
1923
+ fragmentSource: fragmentSource14,
1924
+ replacesBaseFill: true
1925
+ };
1926
+
1927
+ // src/runtime/viewer/shaders/builtins/prismSlats.ts
1928
+ var fragmentSource15 = `
1929
+ precision highp float;
1930
+
1931
+ uniform vec2 uResolution;
1932
+ uniform float uTime;
1933
+ uniform float uSpeed;
1934
+ uniform float uIntensity;
1935
+ uniform float uRibCount;
1936
+ uniform float uGrain;
1937
+ uniform vec3 uBaseColor;
1938
+ uniform vec3 uCoolColor;
1939
+ uniform vec3 uWarmColor;
1940
+ uniform vec3 uHighlightColor;
1941
+ varying vec2 vUv;
1942
+
1943
+ float band(float distance, float width) {
1944
+ return exp(-pow(distance / width, 2.0));
1945
+ }
1946
+
1947
+ float lightField(vec2 p, float time) {
1948
+ float upperCurve = 0.43 + 0.16 * sin(p.x * 2.8 + time * 0.58) - p.x * 0.08;
1949
+ float lowerCurve = -0.46 + 0.13 * sin(p.x * 2.9 - time * 0.43 + 1.4);
1950
+ float upper = band(p.y - upperCurve, 0.2) * band(p.x + 0.03, 0.88);
1951
+ float lower = band(p.y - lowerCurve, 0.19) * band(p.x - 0.05, 0.85);
1952
+ float leftColumn = band(p.x + 0.39 + 0.1 * sin(p.y * 2.9 + time * 0.5), 0.23);
1953
+ leftColumn *= band(p.y + 0.01, 0.59);
1954
+ float centerShadow = 1.0 - band(p.x - 0.35, 0.46) * band(p.y + 0.01, 0.28) * 0.9;
1955
+ float drift = 0.84 + 0.16 * sin(p.x * 3.5 - p.y * 2.2 + time * 0.72);
1956
+ return clamp((upper + lower + leftColumn * 0.74) * centerShadow * drift, 0.0, 1.5);
1957
+ }
1958
+
1959
+ float grain(vec2 uv) {
1960
+ vec2 pixel = floor(uv * uResolution);
1961
+ return fract(sin(dot(pixel, vec2(127.1, 311.7))) * 43758.5453);
1962
+ }
1963
+
1964
+ void main() {
1965
+ vec2 p = vUv * 2.0 - 1.0;
1966
+ float radius = length(p);
1967
+ if (radius >= 1.0) discard;
1968
+ float time = uTime * max(uSpeed, 0.0);
1969
+
1970
+ float field = lightField(p, time);
1971
+ float redField = lightField(p + vec2(0.027, 0.0), time);
1972
+ float blueField = lightField(p - vec2(0.027, 0.0), time);
1973
+ float coolRegion = band(p.x + 0.58, 0.38) * 0.74;
1974
+ float warmRegion = band(p.x + 0.02, 0.34) * band(p.y + 0.08, 0.75) * 0.88;
1975
+ vec3 spectralTint = mix(uHighlightColor, uCoolColor, coolRegion);
1976
+ spectralTint = mix(spectralTint, uWarmColor, warmRegion);
1977
+ vec3 light = spectralTint * field * 1.25;
1978
+ light += uWarmColor * max(redField - blueField, 0.0) * 2.6;
1979
+ light += uCoolColor * max(blueField - redField, 0.0) * 2.6;
1980
+
1981
+ float ribCoordinate = (p.x + 1.0) * max(uRibCount, 1.0) * 0.5;
1982
+ float rib = fract(ribCoordinate);
1983
+ float glassFace = smoothstep(0.08, 0.18, rib) * (1.0 - smoothstep(0.77, 0.94, rib));
1984
+ float groove = 1.0 - glassFace;
1985
+ float refraction = 0.8 + 0.2 * sin(rib * 3.14159);
1986
+ vec3 color = uBaseColor + light * (0.12 + glassFace * 0.95) * refraction;
1987
+ color *= 1.0 - groove * 0.83;
1988
+
1989
+ float leftEdge = band(rib - 0.18, 0.028);
1990
+ float rightEdge = band(rib - 0.79, 0.04);
1991
+ color += mix(uCoolColor, uHighlightColor, 0.48) * leftEdge * (0.07 + field * 0.19);
1992
+ color += uHighlightColor * rightEdge * (0.025 + field * 0.075);
1993
+ color += uCoolColor * band(p.x + 0.78, 0.28) * band(p.y, 0.7) * 0.09;
1994
+
1995
+ float noise = (grain(vUv) - 0.5) * max(uGrain, 0.0);
1996
+ color += vec3(noise) * (0.18 + field * 0.82);
1997
+ float fade = 1.0 - smoothstep(0.88, 1.0, radius);
1998
+ gl_FragColor = vec4(clamp(color * max(uIntensity, 0.0) * fade, 0.0, 1.0), fade);
1999
+ }
2000
+ `;
2001
+ var prismSlatsProgram = {
2002
+ name: "prismSlats",
2003
+ fragmentSource: fragmentSource15
2004
+ };
2005
+
2006
+ // src/runtime/viewer/shaders/builtins/ruler.ts
2007
+ var fragmentSource16 = `
2008
+ precision highp float;
2009
+
2010
+ uniform vec2 uResolution;
2011
+ uniform float uIntensity;
2012
+ uniform float uProgress;
2013
+ uniform float uTicks;
2014
+ uniform float uMajor;
2015
+ uniform float uLength;
2016
+ uniform float uThickness;
2017
+ uniform float uRound;
2018
+ uniform float uDent;
2019
+ uniform float uSpread;
2020
+ uniform float uInset;
2021
+ uniform float uLine;
2022
+ uniform float uGap;
2023
+ uniform float uKnobRadius;
2024
+ uniform float uAxis;
2025
+ uniform vec3 uColor;
2026
+ uniform vec3 uAccent;
2027
+ uniform vec3 uGlow;
2028
+ uniform vec3 uCore;
2029
+ varying vec2 vUv;
2030
+
2031
+ float wireAt(float at, float lineAt, float depth, float radius, float knob, float spread) {
2032
+ float reach = abs(at - knob);
2033
+ if (uKnobRadius > 0.0) {
2034
+ float outline = max(sqrt(max(radius * radius - reach * reach, 0.0)) - (radius - depth), 0.0);
2035
+ float shoulder = max(sqrt(2.0 * depth * radius) * 0.9, 1.0);
2036
+ float soft = depth * exp(-(reach * reach) / (shoulder * shoulder));
2037
+ float k = max(depth * 0.15, 1.0);
2038
+ float h = max(k - abs(outline - soft), 0.0) / k;
2039
+ return lineAt - (max(outline, soft) + h * h * k * 0.25);
2040
+ }
2041
+ float away = reach / spread;
2042
+ return lineAt - depth * exp(-away * away);
2043
+ }
2044
+
2045
+ void main() {
2046
+ vec2 px = vUv * uResolution;
2047
+ float horizontal = step(0.5, uAxis);
2048
+ float along = mix(px.y, px.x, horizontal);
2049
+ float across = mix(px.x, uResolution.y - px.y, horizontal);
2050
+ float extent = mix(uResolution.y, uResolution.x, horizontal);
2051
+ float width = mix(uResolution.x, uResolution.y, horizontal);
2052
+
2053
+ float inset = uInset > 0.0 ? uInset : 0.05;
2054
+ float lineAt = (uLine > 0.0 ? uLine : 0.7) * width;
2055
+ float count = max(uTicks > 0.0 ? uTicks : 50.0, 2.0);
2056
+ float every = max(uMajor > 0.0 ? uMajor : 5.0, 1.0);
2057
+ float unit = (uThickness > 0.0 ? uThickness : 0.012) * width;
2058
+ float halfLine = max(unit * 0.5, 0.75);
2059
+ float spread = (uSpread > 0.0 ? uSpread : 0.07) * extent;
2060
+ float depth = (uDent < 0.0 ? 1.0 : uDent) * 0.16 * width;
2061
+
2062
+ float start = inset * extent;
2063
+ float span = (1.0 - 2.0 * inset) * extent;
2064
+ float knob = start + span * clamp(uProgress, 0.0, 1.0);
2065
+
2066
+ float away = (along - knob) / spread;
2067
+ float bump = exp(-away * away);
2068
+ float radius = (uKnobRadius + (uGap > 0.0 ? uGap : 0.04)) * width;
2069
+ float wire = wireAt(along, lineAt, depth, radius, knob, spread);
2070
+ float slope = (wireAt(along + 1.0, lineAt, depth, radius, knob, spread) - wireAt(along - 1.0, lineAt, depth, radius, knob, spread)) * 0.5;
2071
+ float lineDistance = abs(across - wire) / sqrt(1.0 + slope * slope);
2072
+ float line = 1.0 - smoothstep(halfLine - 0.75, halfLine + 0.75, lineDistance);
2073
+ float wireDistance = abs(across - wire);
2074
+ vec3 lineColor = mix(uColor, uAccent, bump);
2075
+
2076
+ vec3 glowColor = dot(uGlow, uGlow) > 0.0 ? uGlow : uAccent;
2077
+ float glowSide = across < wire ? 1.0 : 0.35;
2078
+ float glow = bump * exp(-wireDistance / (0.1 * width)) * glowSide * 1.3 * max(uIntensity, 0.0);
2079
+ vec3 coreColor = dot(uCore, uCore) > 0.0 ? uCore : mix(glowColor, vec3(1.0), 0.55);
2080
+ float core = bump * bump * exp(-wireDistance / (0.035 * width)) * glowSide * 1.6 * max(uIntensity, 0.0);
2081
+ float reachEdge = min(min(along, extent - along), min(across, width - across));
2082
+ float fade = smoothstep(0.0, 0.14 * width, reachEdge);
2083
+ glow *= fade;
2084
+ core *= fade;
2085
+
2086
+ float spacing = span / count;
2087
+ float position = (along - start) / spacing;
2088
+ float index = floor(position + 0.5);
2089
+ float inRange = step(0.0, index) * step(index, count);
2090
+ float tickAlong = abs(position - index) * spacing;
2091
+ float major = 1.0 - step(0.5, mod(index, every));
2092
+ float tickLength = (uLength > 0.0 ? uLength : 0.1) * width * mix(1.0, 1.9, major);
2093
+ float tickDistance = (start + index * spacing - knob) / spread;
2094
+ float tickBump = exp(-tickDistance * tickDistance);
2095
+ float tickEnd = lineAt - 0.12 * width - depth * 0.4 * tickBump;
2096
+ float halfTick = halfLine * mix(0.75, 1.2, major);
2097
+ float corner = halfTick * clamp(uRound, 0.0, 1.0);
2098
+ vec2 q = abs(vec2(across - (tickEnd - tickLength * 0.5), tickAlong)) - vec2(tickLength * 0.5, halfTick) + corner;
2099
+ float edge = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - corner;
2100
+ float tick = (1.0 - smoothstep(-0.75, 0.75, edge)) * inRange * mix(0.4, 1.0, tickBump);
2101
+ vec3 tickColor = mix(uColor, vec3(1.0), tickBump);
2102
+
2103
+ float alpha = clamp(tick + line * 0.95 + glow + core, 0.0, 1.0);
2104
+ vec3 rgb = tickColor * tick + lineColor * line * 0.95 + glowColor * glow + coreColor * core;
2105
+ gl_FragColor = vec4(clamp(rgb, 0.0, 1.0), alpha);
2106
+ }
2107
+ `;
2108
+ var rulerProgram = {
2109
+ name: "ruler",
2110
+ fragmentSource: fragmentSource16,
2111
+ replacesBaseFill: true
2112
+ };
2113
+
2114
+ // src/runtime/viewer/shaders/builtins/smearOrb.ts
2115
+ var fragmentSource17 = `
2116
+ precision highp float;
2117
+
2118
+ uniform vec2 uResolution;
2119
+ uniform float uTime;
2120
+ uniform float uSpeed;
2121
+ uniform float uIntensity;
2122
+ uniform float uBlur;
2123
+ uniform float uGlow;
2124
+ uniform float uMotion;
2125
+ uniform float uDirection;
2126
+ uniform float uReach;
2127
+ uniform vec3 uShapeColor;
2128
+ varying vec2 vUv;
2129
+
2130
+ float bell(float value, float width) {
2131
+ return exp(-pow(value / max(width, 0.001), 2.0));
2132
+ }
2133
+
2134
+ mat2 rotate(float angle) {
2135
+ float sine = sin(angle);
2136
+ float cosine = cos(angle);
2137
+ return mat2(cosine, -sine, sine, cosine);
2138
+ }
2139
+
2140
+ vec2 orient(vec2 point, float direction) {
2141
+ if (direction < 0.5) return point;
2142
+ if (direction < 1.5) return vec2(-point.x, point.y);
2143
+ if (direction < 2.5) return vec2(-point.y, point.x);
2144
+ return vec2(point.y, -point.x);
2145
+ }
2146
+
2147
+ void main() {
2148
+ vec2 p = vUv * 2.0 - 1.0;
2149
+ p.x *= uResolution.x / max(uResolution.y, 1.0);
2150
+ float time = uTime * max(uSpeed, 0.0);
2151
+ float motion = max(uMotion, 0.0);
2152
+ float blur = clamp(uBlur, 0.02, 0.55);
2153
+
2154
+ vec2 q = orient(p, uDirection) - vec2(0.12, 0.0);
2155
+ float sweep = sin(time * 0.55) * 0.42 * motion;
2156
+ vec2 qb = rotate(sweep) * q;
2157
+ float radius = length(q);
2158
+ float reach = clamp(uReach + sin(time * 0.41 + 1.3) * 0.16 * motion, 0.0, 1.0);
2159
+ float left = 1.0 - smoothstep(-0.42, mix(-0.24, 0.46, reach), qb.x);
2160
+ float upper = smoothstep(-0.05, 0.46, qb.y);
2161
+ float lower = 1.0 - smoothstep(-0.46, 0.05, qb.y);
2162
+ float blurSide = left * (0.50 + 0.50 * max(upper, lower));
2163
+ float breathing = 1.0 + sin(time * 0.72) * 0.28 * motion;
2164
+ float edgeWidth = mix(0.015, blur * breathing, blurSide);
2165
+ float body = 1.0 - smoothstep(0.49 - edgeWidth, 0.49 + edgeWidth, radius);
2166
+
2167
+ float upperPlume = bell(qb.x + 0.33 + sin(time * 0.62) * 0.07 * motion, 0.37 + blur * 0.42);
2168
+ upperPlume *= bell(qb.y - 0.28 + sin(time * 0.47) * 0.05 * motion, 0.24 + blur * 0.5);
2169
+ float lowerPlume = bell(qb.x + 0.32 - sin(time * 0.58) * 0.07 * motion, 0.34 + blur * 0.42);
2170
+ lowerPlume *= bell(qb.y + 0.34 - sin(time * 0.44) * 0.05 * motion, 0.26 + blur * 0.5);
2171
+ float plume = (upperPlume * 0.44 + lowerPlume * 0.40) * left * max(uGlow, 0.0);
2172
+
2173
+ vec3 lightColor = mix(uShapeColor, vec3(1.0), 0.24);
2174
+ vec3 darkColor = mix(uShapeColor, vec3(0.0), 0.7);
2175
+ float vertical = smoothstep(-0.45, 0.44, q.y);
2176
+ vec3 orbColor = mix(uShapeColor * 0.82, lightColor, vertical * 0.48);
2177
+ float shade = bell(q.x + 0.22, 0.38) * bell(q.y, 0.28);
2178
+ orbColor = mix(orbColor, darkColor, shade * 0.43);
2179
+ orbColor *= 0.76 + bell(q.x - 0.27, 0.36) * 0.32;
2180
+
2181
+ vec3 plumeColor = mix(uShapeColor, lightColor, smoothstep(-0.26, 0.30, q.y) * 0.35);
2182
+ float alpha = body + (1.0 - body) * plume;
2183
+ float frameFade = 1.0 - smoothstep(0.85, 1.0, max(abs(p.x), abs(p.y)));
2184
+ alpha = clamp(alpha * frameFade * max(uIntensity, 0.0), 0.0, 1.0);
2185
+ vec3 premultiplied = orbColor * body + plumeColor * (1.0 - body) * plume;
2186
+ gl_FragColor = vec4(clamp(premultiplied * frameFade * max(uIntensity, 0.0), 0.0, 1.0), alpha);
2187
+ }
2188
+ `;
2189
+ var smearOrbProgram = {
2190
+ name: "smearOrb",
2191
+ fragmentSource: fragmentSource17,
2192
+ replacesBaseFill: true
2193
+ };
2194
+
2195
+ // src/runtime/viewer/shaders/builtins/tickScale.ts
2196
+ var fragmentSource18 = `
2197
+ precision highp float;
2198
+
2199
+ uniform vec2 uResolution;
2200
+ uniform float uIntensity;
2201
+ uniform float uProgress;
2202
+ uniform float uTicks;
2203
+ uniform float uMajor;
2204
+ uniform float uStart;
2205
+ uniform float uSweep;
2206
+ uniform float uRadius;
2207
+ uniform float uLength;
2208
+ uniform float uThickness;
2209
+ uniform float uRound;
2210
+ uniform vec3 uColor;
2211
+ uniform vec3 uAccent;
2212
+ varying vec2 vUv;
2213
+
2214
+ const float TAU = 6.28318530718;
2215
+
2216
+ void main() {
2217
+ vec2 p = vUv * 2.0 - 1.0;
2218
+ p.x *= uResolution.x / max(uResolution.y, 1.0);
2219
+ float radius = length(p);
2220
+ float angle = atan(p.y, p.x);
2221
+ float pixel = 2.0 / max(uResolution.y, 1.0);
2222
+
2223
+ float sweep = radians(uSweep > 0.0 ? uSweep : 270.0);
2224
+ float start = radians(uStart != 0.0 ? uStart : 225.0);
2225
+ float gap = TAU - sweep;
2226
+ float fraction = (mod(start - angle + gap * 0.5, TAU) - gap * 0.5) / sweep;
2227
+
2228
+ float count = max(uTicks, 2.0);
2229
+ float inArc = step(-0.25 / count, fraction) * step(fraction, 1.0 + 0.25 / count);
2230
+ float phase = fraction * count;
2231
+ float index = floor(phase + 0.5);
2232
+ float arcDistance = abs(fract(phase + 0.5) - 0.5) / count * sweep * radius;
2233
+ float every = max(uMajor, 1.0);
2234
+ float major = 1.0 - step(0.5, mod(index, every));
2235
+
2236
+ float outer = uRadius > 0.0 ? uRadius : 0.9;
2237
+ float tickLength = (uLength > 0.0 ? uLength : 0.05) * mix(1.0, 1.8, major);
2238
+ float halfWidth = max((uThickness > 0.0 ? uThickness : 0.003) * mix(1.0, 1.4, major), pixel * 0.9);
2239
+ float halfLength = tickLength * 0.5;
2240
+ float corner = min(halfWidth, halfLength) * clamp(uRound, 0.0, 1.0);
2241
+ vec2 q = vec2(arcDistance, abs(radius - (outer - halfLength))) - vec2(halfWidth, halfLength) + corner;
2242
+ float edge = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - corner;
2243
+ float tick = (1.0 - smoothstep(-pixel, pixel, edge)) * inArc;
2244
+
2245
+ float progress = uProgress < 0.0 ? 1.0 : clamp(uProgress, 0.0, 1.0);
2246
+ float lit = clamp(progress * count - index + 1.0, 0.0, 1.0);
2247
+ vec3 base = uColor;
2248
+ vec3 color = mix(base, uAccent, lit);
2249
+ float alpha = tick * mix(0.32, 1.0, lit) * max(uIntensity, 0.0);
2250
+ gl_FragColor = vec4(color * alpha, clamp(alpha, 0.0, 1.0));
2251
+ }
2252
+ `;
2253
+ var tickScaleProgram = {
2254
+ name: "tickScale",
2255
+ fragmentSource: fragmentSource18,
2256
+ replacesBaseFill: true
2257
+ };
2258
+
2259
+ // src/runtime/viewer/shaders/builtins/violetGlass.ts
2260
+ var fragmentSource19 = `
2261
+ precision highp float;
2262
+
2263
+ uniform float uTime;
2264
+ uniform float uSpeed;
2265
+ uniform float uIntensity;
2266
+ uniform vec3 uBaseColor;
2267
+ uniform vec3 uColor;
2268
+ uniform vec3 uHighlightColor;
2269
+ varying vec2 vUv;
2270
+
2271
+ float band(float distance, float width) {
2272
+ return exp(-pow(distance / width, 2.0));
2273
+ }
2274
+
2275
+ float glassDistance(vec2 p, float time) {
2276
+ float angle = atan(p.y, p.x);
2277
+ float edge = 0.72;
2278
+ edge += sin(angle * 3.0 + time * 0.52) * 0.038;
2279
+ edge += sin(angle * 5.0 - time * 0.37 + 1.6) * 0.025;
2280
+ edge += sin(angle * 9.0 + time * 0.29) * 0.008;
2281
+ return length(p) - edge;
2282
+ }
2283
+
2284
+ void main() {
2285
+ vec2 p = vUv * 2.0 - 1.0;
2286
+ p.y += 0.025;
2287
+ float time = uTime * max(uSpeed, 0.0);
2288
+ float distance = glassDistance(p, time);
2289
+ float body = 1.0 - smoothstep(-0.012, 0.01, distance);
2290
+ float halo = band(max(distance, 0.0), 0.12) * 0.24;
2291
+ float alpha = body + (1.0 - body) * halo;
2292
+ if (alpha < 0.002) discard;
2293
+
2294
+ float lowerLight = 1.0 - smoothstep(-0.6, 0.45, p.y);
2295
+ float liquid = 0.5 + 0.5 * sin(p.x * 3.5 - p.y * 2.2 + time * 0.61);
2296
+ vec3 color = mix(uBaseColor, uColor, 0.17 + lowerLight * 0.3 + liquid * 0.09);
2297
+ color += uColor * band(p.x + 0.36, 0.42) * band(p.y + 0.12, 0.5) * 0.16;
2298
+ color *= 0.55 + 0.45 * smoothstep(-0.52, 0.5, p.y + 0.2);
2299
+
2300
+ float innerFold = p.y + 0.22 + 0.19 * sin(p.x * 4.0 + time * 0.53);
2301
+ float secondFold = p.y - 0.42 + 0.11 * sin(p.x * 5.5 - time * 0.42);
2302
+ float foldLight = band(innerFold, 0.041) * smoothstep(-0.65, -0.1, p.x);
2303
+ foldLight += band(secondFold, 0.027) * smoothstep(0.05, 0.6, p.x);
2304
+ color += uHighlightColor * foldLight * 0.64;
2305
+ color += uColor * band(innerFold + 0.07, 0.065) * 0.31;
2306
+ float softReflection = band(p.x + 0.3 + 0.1 * sin(p.y * 3.5 + time * 0.35), 0.2);
2307
+ softReflection *= band(p.y - 0.38, 0.35);
2308
+ color += uHighlightColor * softReflection * 0.1;
2309
+
2310
+ float dome = sqrt(max(0.04, 1.0 - dot(p, p) / 0.62));
2311
+ vec3 surfaceNormal = normalize(vec3(p.x * 1.18, p.y * 1.18, dome));
2312
+ surfaceNormal.xy += vec2(sin(p.y * 8.0 + time * 0.31), cos(p.x * 7.0 - time * 0.27)) * 0.065;
2313
+ surfaceNormal = normalize(surfaceNormal);
2314
+ vec3 reflection = reflect(vec3(0.0, 0.0, -1.0), surfaceNormal);
2315
+ float softbox = band(reflection.x + 0.42, 0.34) * band(reflection.y - 0.43, 0.24);
2316
+ float sidebox = band(reflection.x - 0.57, 0.19) * band(reflection.y + 0.11, 0.5);
2317
+ color += uHighlightColor * softbox * 0.21;
2318
+ color += uColor * sidebox * 0.22;
2319
+
2320
+ float e = 0.008;
2321
+ vec2 normal = normalize(vec2(
2322
+ glassDistance(p + vec2(e, 0.0), time) - glassDistance(p - vec2(e, 0.0), time),
2323
+ glassDistance(p + vec2(0.0, e), time) - glassDistance(p - vec2(0.0, e), time)
2324
+ ));
2325
+ float light = pow(max(0.0, dot(normal, normalize(vec2(-0.62, 0.78)))), 2.0);
2326
+ float sideLight = pow(max(0.0, dot(normal, normalize(vec2(0.8, -0.58)))), 4.0);
2327
+ float rim = band(distance + 0.012, 0.031);
2328
+ float sharpRim = band(distance + 0.004, 0.009);
2329
+ color += uColor * rim * (0.25 + light * 0.53);
2330
+ color += uHighlightColor * sharpRim * (light * 0.78 + sideLight * 0.38);
2331
+ color += uHighlightColor * band(distance + 0.09, 0.025) * light * 0.23;
2332
+ color += uColor * band(length(p - vec2(0.27, -0.31)) - 0.26, 0.09) * 0.1;
2333
+
2334
+ vec3 premultiplied = color * body + uColor * (1.0 - body) * halo * 0.48;
2335
+ gl_FragColor = vec4(clamp(premultiplied * max(uIntensity, 0.0), 0.0, 1.0), alpha);
2336
+ }
2337
+ `;
2338
+ var violetGlassProgram = {
2339
+ name: "violetGlass",
2340
+ fragmentSource: fragmentSource19
2341
+ };
2342
+
2343
+ // src/runtime/viewer/shaders/builtins/violetGlassLight.ts
2344
+ var fragmentSource20 = `
2345
+ precision highp float;
2346
+
2347
+ uniform float uTime;
2348
+ uniform float uSpeed;
2349
+ uniform float uIntensity;
2350
+ uniform vec3 uBaseColor;
2351
+ uniform vec3 uColor;
2352
+ uniform vec3 uHighlightColor;
2353
+ uniform vec3 uShadowColor;
2354
+ varying vec2 vUv;
2355
+
2356
+ float band(float distance, float width) {
2357
+ return exp(-pow(distance / width, 2.0));
2358
+ }
2359
+
2360
+ float glassDistance(vec2 p, float time) {
2361
+ float angle = atan(p.y, p.x);
2362
+ float edge = 0.69;
2363
+ edge += sin(angle * 3.0 + time * 0.48) * 0.025;
2364
+ edge += sin(angle * 5.0 - time * 0.36 + 1.7) * 0.016;
2365
+ return length(p) - edge;
2366
+ }
2367
+
2368
+ void main() {
2369
+ vec2 p = vUv * 2.0 - 1.0;
2370
+ float time = uTime * max(uSpeed, 0.0);
2371
+ float distance = glassDistance(p, time);
2372
+ float body = 1.0 - smoothstep(-0.012, 0.012, distance);
2373
+ float outside = band(max(distance, 0.0), 0.13) * 0.16;
2374
+ outside *= 1.0 - smoothstep(0.86, 1.0, length(p));
2375
+ float alpha = body * 0.94 + (1.0 - body) * outside;
2376
+ if (alpha < 0.002) discard;
2377
+
2378
+ float lowerTint = 1.0 - smoothstep(-0.54, 0.46, p.y);
2379
+ float flow = 0.5 + 0.5 * sin(p.x * 3.8 - p.y * 2.4 + time * 0.57);
2380
+ vec3 color = mix(uBaseColor, uColor, 0.08 + lowerTint * 0.19 + flow * 0.045);
2381
+ color *= 1.0 - band(p.x - 0.27, 0.31) * band(p.y + 0.25, 0.48) * 0.11;
2382
+
2383
+ float fold = p.y + 0.17 + 0.15 * sin(p.x * 4.1 + time * 0.49);
2384
+ float secondFold = p.y - 0.43 + 0.08 * sin(p.x * 5.2 - time * 0.32);
2385
+ float foldShade = band(fold + 0.045, 0.055) * smoothstep(-0.55, 0.18, p.x);
2386
+ foldShade += band(secondFold + 0.03, 0.038) * smoothstep(-0.1, 0.58, p.x);
2387
+ color = mix(color, uShadowColor, foldShade * 0.13);
2388
+ color += uHighlightColor * band(fold, 0.028) * 0.11;
2389
+ color += uHighlightColor * band(secondFold, 0.019) * 0.09;
2390
+
2391
+ float dome = sqrt(max(0.04, 1.0 - dot(p, p) / 0.57));
2392
+ vec3 normal = normalize(vec3(p.x * 1.15, p.y * 1.15, dome));
2393
+ normal.xy += vec2(sin(p.y * 7.0 + time * 0.29), cos(p.x * 6.0 - time * 0.24)) * 0.04;
2394
+ vec3 reflection = reflect(vec3(0.0, 0.0, -1.0), normalize(normal));
2395
+ float softbox = band(reflection.x + 0.45, 0.28) * band(reflection.y - 0.39, 0.22);
2396
+ color += uHighlightColor * softbox * 0.12;
2397
+
2398
+ float rim = band(distance + 0.018, 0.041);
2399
+ float thinRim = band(distance + 0.004, 0.012);
2400
+ float innerShade = band(distance + 0.094, 0.055);
2401
+ float upperLight = smoothstep(-0.34, 0.78, p.y - p.x * 0.24);
2402
+ color = mix(color, uShadowColor, innerShade * (0.08 + (1.0 - upperLight) * 0.1));
2403
+ color = mix(color, uColor, rim * (0.36 + upperLight * 0.15));
2404
+ color = mix(color, uShadowColor, thinRim * (1.0 - upperLight) * 0.29);
2405
+ color += uHighlightColor * thinRim * upperLight * 0.17;
2406
+
2407
+ vec3 premultiplied = color * body * 0.94;
2408
+ premultiplied += mix(uShadowColor, uColor, 0.36) * (1.0 - body) * outside;
2409
+ gl_FragColor = vec4(clamp(premultiplied * max(uIntensity, 0.0), 0.0, 1.0), alpha);
2410
+ }
2411
+ `;
2412
+ var violetGlassLightProgram = {
2413
+ name: "violetGlassLight",
2414
+ fragmentSource: fragmentSource20
2415
+ };
2416
+
2417
+ // src/runtime/viewer/shaders/programs.ts
2418
+ var builtins = [
2419
+ orbProgram,
2420
+ haloProgram,
2421
+ halftoneHaloProgram,
2422
+ chromaticHaloProgram,
2423
+ chromaticGlassPlateProgram,
2424
+ glassRefractionProgram,
2425
+ emberSlitsProgram,
2426
+ smearOrbProgram,
2427
+ pixelConstellationProgram,
2428
+ plasmaGaugeProgram,
2429
+ tickScaleProgram,
2430
+ rulerProgram,
2431
+ buttonProgram,
2432
+ prismSlatsProgram,
2433
+ eclipseOrbProgram,
2434
+ emberMatrixProgram,
2435
+ violetGlassProgram,
2436
+ violetGlassLightProgram,
2437
+ lumenPortalProgram,
2438
+ lumenPortalLightProgram
2439
+ ];
2440
+ var programs = new Map(builtins.map((program) => [program.name, program]));
2441
+ function registerShaderProgram(program) {
2442
+ if (!program.name.trim()) {
2443
+ throw new Error("registerShaderProgram: name must not be empty.");
2444
+ }
2445
+ if (!program.fragmentSource.includes("void main")) {
2446
+ throw new Error(`registerShaderProgram: "${program.name}" has no main function.`);
2447
+ }
2448
+ programs.set(program.name, program);
2449
+ return () => {
2450
+ if (programs.get(program.name) === program) {
2451
+ programs.delete(program.name);
2452
+ }
2453
+ };
2454
+ }
2455
+ function shaderProgram(name) {
2456
+ return programs.get(name);
2457
+ }
2458
+
2459
+ // src/runtime/viewer/shaders/backdrop.ts
2460
+ var MIN_BACKDROP_DENSITY = 2.5;
2461
+ var MAX_BACKDROP_PIXELS = 4096;
2462
+ var TRANSPARENT = /^rgba?(s*d+,s*d+,s*d+,s*0s*)$|^transparent$/;
2463
+ function hostBackground(svg) {
2464
+ const view = svg.ownerDocument.defaultView;
2465
+ for (let element = svg.parentElement; element && view; element = element.parentElement) {
2466
+ const color2 = view.getComputedStyle(element).backgroundColor;
2467
+ if (color2 && !TRANSPARENT.test(color2.trim())) {
2468
+ return color2;
2469
+ }
2470
+ }
2471
+ return void 0;
2472
+ }
2473
+ function backdropClone(svg, owner, width, height) {
2474
+ const clone = svg.cloneNode(true);
2475
+ const ownerIndex = [...svg.children].indexOf(owner);
2476
+ if (ownerIndex >= 0) {
2477
+ while (clone.children.length > ownerIndex) {
2478
+ clone.lastElementChild?.remove();
2479
+ }
2480
+ }
2481
+ for (const surface of clone.querySelectorAll("foreignObject[data-sf-shader]")) {
2482
+ surface.remove();
2483
+ }
2484
+ const background = hostBackground(svg);
2485
+ if (background) {
2486
+ const { x, y, width: viewWidth, height: viewHeight } = svg.viewBox.baseVal;
2487
+ const fill = svg.ownerDocument.createElementNS("http://www.w3.org/2000/svg", "rect");
2488
+ fill.setAttribute("x", String(x));
2489
+ fill.setAttribute("y", String(y));
2490
+ fill.setAttribute("width", String(viewWidth));
2491
+ fill.setAttribute("height", String(viewHeight));
2492
+ fill.setAttribute("fill", background);
2493
+ clone.insertBefore(fill, clone.firstChild);
2494
+ }
2495
+ clone.setAttribute("width", String(width));
2496
+ clone.setAttribute("height", String(height));
2497
+ clone.setAttribute("xmlns", "http://www.w3.org/2000/svg");
2498
+ return clone;
2499
+ }
2500
+ function captureShaderBackdrop(svg, owner) {
2501
+ const density = Math.max(globalThis.devicePixelRatio || 1, MIN_BACKDROP_DENSITY);
2502
+ const scale = Math.min(density, MAX_BACKDROP_PIXELS / Math.max(1, Math.max(svg.clientWidth, svg.clientHeight)));
2503
+ const width = Math.max(1, Math.round(svg.clientWidth * scale));
2504
+ const height = Math.max(1, Math.round(svg.clientHeight * scale));
2505
+ const view = svg.ownerDocument.defaultView;
2506
+ if (!view) {
2507
+ return Promise.resolve(void 0);
2508
+ }
2509
+ const markup = new XMLSerializer().serializeToString(backdropClone(svg, owner, width, height));
2510
+ const image = svg.ownerDocument.createElement("img");
2511
+ const source = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;
2512
+ return new Promise((resolve) => {
2513
+ image.onload = () => resolve({ image, width, height });
2514
+ image.onerror = () => resolve(void 0);
2515
+ image.src = source;
2516
+ });
2517
+ }
2518
+
2519
+ // src/runtime/viewer/shaders/uniforms.ts
2520
+ function color(value, fallback) {
2521
+ if (typeof value !== "string") {
2522
+ return fallback;
2523
+ }
2524
+ const match = /^#([0-9a-f]{6})$/i.exec(value);
2525
+ if (!match) {
2526
+ return fallback;
2527
+ }
2528
+ const integer = Number.parseInt(match[1], 16);
2529
+ return [(integer >> 16 & 255) / 255, (integer >> 8 & 255) / 255, (integer & 255) / 255];
2530
+ }
2531
+ function number(value, fallback) {
2532
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
2533
+ }
2534
+ function uniform1f(surface, name, value) {
2535
+ const location = surface.gl.getUniformLocation(surface.program, name);
2536
+ if (location) {
2537
+ surface.gl.uniform1f(location, value);
2538
+ }
2539
+ }
2540
+ function uniform3f(surface, name, value) {
2541
+ const location = surface.gl.getUniformLocation(surface.program, name);
2542
+ if (location) {
2543
+ surface.gl.uniform3f(location, ...value);
2544
+ }
2545
+ }
2546
+ function uniform2f(surface, name, value) {
2547
+ const location = surface.gl.getUniformLocation(surface.program, name);
2548
+ if (location) {
2549
+ surface.gl.uniform2f(location, ...value);
2550
+ }
2551
+ }
2552
+ function uniformName(parameter) {
2553
+ return `u${parameter.charAt(0).toUpperCase()}${parameter.slice(1)}`;
2554
+ }
2555
+ function enumParameter(name, value) {
2556
+ const values = {
2557
+ direction: { left: 0, right: 1, top: 2, bottom: 3 },
2558
+ orientation: { vertical: 0, horizontal: 1 },
2559
+ axis: { vertical: 0, horizontal: 1 }
2560
+ };
2561
+ return values[name]?.[value];
2562
+ }
2563
+ function applyParameters(surface, elapsed) {
2564
+ for (const [key, value] of Object.entries(surface.parameters)) {
2565
+ const name = uniformName(key);
2566
+ if (typeof value === "number") {
2567
+ const lag = surface.parameters[`${key}Lag`];
2568
+ if (typeof lag === "number" && lag > 0) {
2569
+ const current = surface.smoothed.get(key) ?? value;
2570
+ const next = current + (value - current) * (1 - Math.exp(-elapsed / lag));
2571
+ surface.smoothed.set(key, next);
2572
+ uniform1f(surface, name, next);
2573
+ continue;
2574
+ }
2575
+ uniform1f(surface, name, value);
2576
+ continue;
2577
+ }
2578
+ if (typeof value === "boolean") {
2579
+ const location = surface.gl.getUniformLocation(surface.program, name);
2580
+ if (location) {
2581
+ surface.gl.uniform1i(location, value ? 1 : 0);
2582
+ }
2583
+ continue;
2584
+ }
2585
+ const enumValue = enumParameter(key, value);
2586
+ if (enumValue !== void 0) {
2587
+ uniform1f(surface, name, enumValue);
2588
+ continue;
2589
+ }
2590
+ if (/^#[0-9a-f]{6}$/i.test(value)) {
2591
+ uniform3f(surface, name, color(value, [0, 0, 0]));
2592
+ }
2593
+ }
2594
+ }
2595
+
2596
+ // src/runtime/viewer/shaders/webgl.ts
2597
+ var VERTEX_SOURCE = `
2598
+ attribute vec2 aPosition;
2599
+ varying vec2 vUv;
2600
+
2601
+ void main() {
2602
+ vUv = aPosition * 0.5 + 0.5;
2603
+ gl_Position = vec4(aPosition, 0.0, 1.0);
2604
+ }
2605
+ `;
2606
+ function compile(gl, type, source) {
2607
+ const shader = gl.createShader(type);
2608
+ if (!shader) {
2609
+ return void 0;
2610
+ }
2611
+ gl.shaderSource(shader, source);
2612
+ gl.compileShader(shader);
2613
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
2614
+ gl.deleteShader(shader);
2615
+ return void 0;
2616
+ }
2617
+ return shader;
2618
+ }
2619
+ function linkProgram(gl, fragmentSource21) {
2620
+ const vertex = compile(gl, gl.VERTEX_SHADER, VERTEX_SOURCE);
2621
+ const fragment = compile(gl, gl.FRAGMENT_SHADER, fragmentSource21);
2622
+ if (!vertex || !fragment) {
2623
+ if (vertex) {
2624
+ gl.deleteShader(vertex);
2625
+ }
2626
+ if (fragment) {
2627
+ gl.deleteShader(fragment);
2628
+ }
2629
+ return void 0;
2630
+ }
2631
+ const program = gl.createProgram();
2632
+ if (!program) {
2633
+ gl.deleteShader(vertex);
2634
+ gl.deleteShader(fragment);
2635
+ return void 0;
2636
+ }
2637
+ gl.attachShader(program, vertex);
2638
+ gl.attachShader(program, fragment);
2639
+ gl.linkProgram(program);
2640
+ gl.deleteShader(vertex);
2641
+ gl.deleteShader(fragment);
2642
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
2643
+ gl.deleteProgram(program);
2644
+ return void 0;
2645
+ }
2646
+ return program;
2647
+ }
2648
+ function transparentTexture(gl) {
2649
+ const texture = gl.createTexture();
2650
+ if (!texture) {
2651
+ return void 0;
2652
+ }
2653
+ gl.bindTexture(gl.TEXTURE_2D, texture);
2654
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
2655
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
2656
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
2657
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
2658
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array([0, 0, 0, 0]));
2659
+ return texture;
2660
+ }
2661
+
2662
+ // src/runtime/viewer/shaders/createSurface.ts
2663
+ var FULL_SCREEN_QUAD = new Float32Array([-1, -1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1]);
2664
+ function parametersOf(owner) {
2665
+ try {
2666
+ return JSON.parse(owner.getAttribute("data-sf-shader-params") ?? "{}");
2667
+ } catch {
2668
+ return {};
2669
+ }
2670
+ }
2671
+ function bindFullScreenQuad(gl, program) {
2672
+ const buffer = gl.createBuffer();
2673
+ if (!buffer) {
2674
+ return void 0;
2675
+ }
2676
+ gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
2677
+ gl.bufferData(gl.ARRAY_BUFFER, FULL_SCREEN_QUAD, gl.STATIC_DRAW);
2678
+ const position = gl.getAttribLocation(program, "aPosition");
2679
+ if (position < 0) {
2680
+ gl.deleteBuffer(buffer);
2681
+ return void 0;
2682
+ }
2683
+ gl.enableVertexAttribArray(position);
2684
+ gl.vertexAttribPointer(position, 2, gl.FLOAT, false, 0, 0);
2685
+ return buffer;
2686
+ }
2687
+ function findBaseGeometry(owner, definition) {
2688
+ const ownerId = owner.getAttribute("data-sf-id");
2689
+ if (!definition.replacesBaseFill || !ownerId) {
2690
+ return void 0;
2691
+ }
2692
+ const candidates = [...owner.closest("svg")?.querySelectorAll('[data-sf-primary="true"]') ?? []];
2693
+ return candidates.find((element) => element !== owner && element.getAttribute("data-sf-id") === ownerId);
2694
+ }
2695
+ function createSurface(canvas) {
2696
+ const owner = canvas.closest("[data-sf-shader]");
2697
+ const name = owner?.getAttribute("data-sf-shader");
2698
+ const definition = name ? shaderProgram(name) : void 0;
2699
+ const gl = canvas.getContext("webgl", { alpha: true, antialias: true, premultipliedAlpha: true });
2700
+ if (!owner || !definition || !gl) {
2701
+ return void 0;
2702
+ }
2703
+ const program = linkProgram(gl, definition.fragmentSource);
2704
+ if (!program) {
2705
+ return void 0;
2706
+ }
2707
+ gl.useProgram(program);
2708
+ const buffer = bindFullScreenQuad(gl, program);
2709
+ if (!buffer) {
2710
+ gl.deleteProgram(program);
2711
+ return void 0;
2712
+ }
2713
+ const usesBackdrop = definition.usesBackdrop === true;
2714
+ const baseGeometry = findBaseGeometry(owner, definition);
2715
+ const baseFill = baseGeometry?.getAttribute("fill");
2716
+ baseGeometry?.setAttribute("fill", "none");
2717
+ return {
2718
+ owner,
2719
+ canvas,
2720
+ gl,
2721
+ program,
2722
+ buffer,
2723
+ parameters: parametersOf(owner),
2724
+ smoothed: /* @__PURE__ */ new Map(),
2725
+ backdropTexture: usesBackdrop ? transparentTexture(gl) : void 0,
2726
+ backdropOrigin: [0, 0],
2727
+ backdropScale: [1, 1],
2728
+ usesBackdrop,
2729
+ shapeColor: color(owner.getAttribute("data-sf-shader-fill") ?? void 0, [1, 1, 1]),
2730
+ baseGeometry,
2731
+ baseFill
2732
+ };
2733
+ }
2734
+ function releaseSurface(surface) {
2735
+ if (surface.backdropTexture) {
2736
+ surface.gl.deleteTexture(surface.backdropTexture);
2737
+ }
2738
+ surface.gl.deleteBuffer(surface.buffer);
2739
+ surface.gl.deleteProgram(surface.program);
2740
+ if (!surface.baseGeometry) {
2741
+ return;
2742
+ }
2743
+ if (surface.baseFill === null || surface.baseFill === void 0) {
2744
+ surface.baseGeometry.removeAttribute("fill");
2745
+ } else {
2746
+ surface.baseGeometry.setAttribute("fill", surface.baseFill);
2747
+ }
2748
+ }
2749
+
2750
+ // src/runtime/viewer/shaders/renderSurface.ts
2751
+ function syncBackdropBounds(surface) {
2752
+ const svg = surface.canvas.closest("svg");
2753
+ if (!svg) {
2754
+ return;
2755
+ }
2756
+ const svgBounds = svg.getBoundingClientRect();
2757
+ const surfaceBounds = surface.canvas.getBoundingClientRect();
2758
+ if (svgBounds.width <= 0 || svgBounds.height <= 0) {
2759
+ return;
2760
+ }
2761
+ surface.backdropOrigin = [
2762
+ (surfaceBounds.left - svgBounds.left) / svgBounds.width,
2763
+ (svgBounds.bottom - surfaceBounds.bottom) / svgBounds.height
2764
+ ];
2765
+ surface.backdropScale = [surfaceBounds.width / svgBounds.width, surfaceBounds.height / svgBounds.height];
2766
+ }
2767
+ function applyBackdrop(surface, backdrop) {
2768
+ if (!surface.backdropTexture) {
2769
+ return;
2770
+ }
2771
+ syncBackdropBounds(surface);
2772
+ const { gl } = surface;
2773
+ gl.bindTexture(gl.TEXTURE_2D, surface.backdropTexture);
2774
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
2775
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, backdrop.image);
2776
+ }
2777
+ var MAX_SURFACE_PIXELS = 2560;
2778
+ function renderSurface(surface, seconds) {
2779
+ const { canvas, gl, parameters: parameters3 } = surface;
2780
+ const shown = canvas.getBoundingClientRect();
2781
+ const density = Math.min(globalThis.devicePixelRatio || 1, 3);
2782
+ const fit = Math.min(1, MAX_SURFACE_PIXELS / Math.max(1, shown.width * density, shown.height * density));
2783
+ const width = Math.max(1, Math.round((shown.width || canvas.clientWidth) * density * fit));
2784
+ const height = Math.max(1, Math.round((shown.height || canvas.clientHeight) * density * fit));
2785
+ if (canvas.width !== width || canvas.height !== height) {
2786
+ canvas.width = width;
2787
+ canvas.height = height;
2788
+ }
2789
+ gl.viewport(0, 0, width, height);
2790
+ gl.clearColor(0, 0, 0, 0);
2791
+ gl.clear(gl.COLOR_BUFFER_BIT);
2792
+ gl.useProgram(surface.program);
2793
+ const resolution = gl.getUniformLocation(surface.program, "uResolution");
2794
+ if (resolution) {
2795
+ gl.uniform2f(resolution, width, height);
2796
+ }
2797
+ uniform1f(surface, "uTime", seconds);
2798
+ if (surface.backdropTexture) {
2799
+ syncBackdropBounds(surface);
2800
+ gl.activeTexture(gl.TEXTURE0);
2801
+ gl.bindTexture(gl.TEXTURE_2D, surface.backdropTexture);
2802
+ const sampler = gl.getUniformLocation(surface.program, "uBackdrop");
2803
+ if (sampler) {
2804
+ gl.uniform1i(sampler, 0);
2805
+ }
2806
+ uniform2f(surface, "uBackdropOrigin", surface.backdropOrigin);
2807
+ uniform2f(surface, "uBackdropScale", surface.backdropScale);
2808
+ }
2809
+ for (const [key, fallback] of Object.entries(SHADER_DEFAULTS.numbers)) {
2810
+ uniform1f(surface, uniformName(key), number(parameters3[key], fallback));
2811
+ }
2812
+ for (const [key, fallback] of Object.entries(SHADER_DEFAULTS.colors)) {
2813
+ uniform3f(surface, uniformName(key), color(parameters3[key], [...fallback]));
2814
+ }
2815
+ uniform3f(surface, "uShapeColor", surface.shapeColor);
2816
+ const elapsed = surface.lastSeconds === void 0 ? 0 : Math.max(0, Math.min(seconds - surface.lastSeconds, 0.1));
2817
+ surface.lastSeconds = seconds;
2818
+ applyParameters(surface, elapsed);
2819
+ gl.drawArrays(gl.TRIANGLES, 0, 6);
2820
+ }
2821
+
2822
+ // src/runtime/viewer/shaders/surfaceClock.ts
2823
+ function createSurfaceClock(view) {
2824
+ let pausedSince = null;
2825
+ let spentPaused = 0;
2826
+ const real = () => (view?.performance.now() ?? 0) / 1e3;
2827
+ return {
2828
+ now: () => (pausedSince ?? real()) - spentPaused,
2829
+ paused: () => pausedSince !== null,
2830
+ spent: () => spentPaused,
2831
+ setPaused(paused) {
2832
+ if (paused && pausedSince === null) {
2833
+ pausedSince = real();
2834
+ } else if (!paused && pausedSince !== null) {
2835
+ spentPaused += real() - pausedSince;
2836
+ pausedSince = null;
2837
+ }
2838
+ }
2839
+ };
2840
+ }
2841
+
2842
+ // src/runtime/viewer/shaders/surface.ts
2843
+ var RECAPTURE_DELAY_MS = 120;
2844
+ function capturePngs(svg, surfaces, seconds) {
2845
+ const surfaceByCanvas = new Map(surfaces.map((surface) => [surface.canvas, surface]));
2846
+ return [...svg.querySelectorAll("canvas[data-sf-shader-canvas]")].map((canvas) => {
2847
+ const surface = surfaceByCanvas.get(canvas);
2848
+ if (!surface) {
2849
+ return void 0;
2850
+ }
2851
+ try {
2852
+ renderSurface(surface, seconds);
2853
+ return surface.canvas.toDataURL("image/png");
2854
+ } catch {
2855
+ return void 0;
2856
+ }
2857
+ });
2858
+ }
2859
+ function updateParameters(surfaces, ownerId, parameters3, seconds) {
2860
+ let changed = false;
2861
+ for (const surface of surfaces) {
2862
+ if (surface.owner.getAttribute("data-sf-id") !== ownerId) {
2863
+ continue;
2864
+ }
2865
+ Object.assign(surface.parameters, parameters3);
2866
+ surface.owner.setAttribute("data-sf-shader-params", JSON.stringify(surface.parameters));
2867
+ renderSurface(surface, seconds);
2868
+ changed = true;
2869
+ }
2870
+ return changed;
2871
+ }
2872
+ function watchSvgChanges(svg, view, onChange) {
2873
+ if (!view) {
2874
+ return () => void 0;
2875
+ }
2876
+ const mutations = new view.MutationObserver(onChange);
2877
+ mutations.observe(svg, { attributes: true, attributeFilter: ["viewBox"] });
2878
+ const sizes = view.ResizeObserver ? new view.ResizeObserver(onChange) : void 0;
2879
+ sizes?.observe(svg);
2880
+ return () => {
2881
+ mutations.disconnect();
2882
+ sizes?.disconnect();
2883
+ };
2884
+ }
2885
+ function mountShaderSurfaces(svg) {
2886
+ let surfaces = [];
2887
+ let frame;
2888
+ let backdropGeneration = 0;
2889
+ let recapture;
2890
+ const view = svg.ownerDocument.defaultView;
2891
+ const clock = createSurfaceClock(view);
2892
+ const draw = (milliseconds) => {
2893
+ if (!clock.paused()) {
2894
+ for (const surface of surfaces) {
2895
+ renderSurface(surface, milliseconds / 1e3 - clock.spent());
2896
+ }
2897
+ }
2898
+ frame = view?.requestAnimationFrame(draw);
2899
+ };
2900
+ const stop = () => {
2901
+ if (frame !== void 0) {
2902
+ view?.cancelAnimationFrame(frame);
2903
+ frame = void 0;
2904
+ }
2905
+ for (const surface of surfaces) {
2906
+ releaseSurface(surface);
2907
+ }
2908
+ surfaces = [];
2909
+ };
2910
+ const captureBackdrops = (generation) => {
2911
+ for (const surface of surfaces) {
2912
+ if (!surface.usesBackdrop) {
2913
+ continue;
2914
+ }
2915
+ void captureShaderBackdrop(svg, surface.owner).then((backdrop) => {
2916
+ if (backdrop && generation === backdropGeneration) {
2917
+ applyBackdrop(surface, backdrop);
2918
+ }
2919
+ });
2920
+ }
2921
+ };
2922
+ const startFrames = () => {
2923
+ if (!view?.requestAnimationFrame) {
2924
+ for (const surface of surfaces) {
2925
+ renderSurface(surface, 0);
2926
+ }
2927
+ return;
2928
+ }
2929
+ if (clock.paused()) {
2930
+ for (const surface of surfaces) {
2931
+ renderSurface(surface, clock.now());
2932
+ }
2933
+ }
2934
+ frame = view.requestAnimationFrame(draw);
2935
+ };
2936
+ const refresh = () => {
2937
+ stop();
2938
+ const generation = ++backdropGeneration;
2939
+ surfaces = [...svg.querySelectorAll("canvas[data-sf-shader-canvas]")].flatMap((canvas) => {
2940
+ const surface = createSurface(canvas);
2941
+ return surface ? [surface] : [];
2942
+ });
2943
+ captureBackdrops(generation);
2944
+ if (surfaces.length > 0) {
2945
+ startFrames();
2946
+ }
2947
+ };
2948
+ const scheduleRecapture = () => {
2949
+ if (recapture !== void 0) {
2950
+ view?.clearTimeout(recapture);
2951
+ }
2952
+ recapture = view?.setTimeout(() => {
2953
+ recapture = void 0;
2954
+ captureBackdrops(backdropGeneration);
2955
+ }, RECAPTURE_DELAY_MS);
2956
+ };
2957
+ const unwatch = watchSvgChanges(svg, view, scheduleRecapture);
2958
+ refresh();
2959
+ const destroy = () => {
2960
+ backdropGeneration += 1;
2961
+ unwatch();
2962
+ if (recapture !== void 0) {
2963
+ view?.clearTimeout(recapture);
2964
+ }
2965
+ stop();
2966
+ };
2967
+ return {
2968
+ capture: () => capturePngs(svg, surfaces, clock.now()),
2969
+ setParameters: (ownerId, parameters3) => updateParameters(surfaces, ownerId, parameters3, clock.now()),
2970
+ setPaused: clock.setPaused,
2971
+ refresh,
2972
+ destroy
2973
+ };
2974
+ }
2975
+
2976
+ // src/runtime/viewer/source.ts
2977
+ function renderViewerSource(next, defaultRenderOptions) {
2978
+ const svg = next.svg ?? (next.source != null ? chunkS5BVAAEF_cjs.render(next.source, { ...next.renderOptions ?? defaultRenderOptions, linkHitAreas: true }).svg : null);
2979
+ if (svg == null) {
2980
+ throw new Error("createViewer: provide either `source` (SFL) or `svg`.");
2981
+ }
2982
+ return svg;
2983
+ }
2984
+ function assertCreateViewerContract(svg) {
2985
+ const version = chunk34PK7HEU_cjs.parseContractVersion(svg);
2986
+ if (!chunk34PK7HEU_cjs.isContractCompatible(version)) {
2987
+ throw new Error(`createViewer: incompatible DOM contract (svg=${version}). Renderer and viewer versions differ.`);
2988
+ }
2989
+ }
2990
+ function assertViewerUpdateContract(svg) {
2991
+ const version = chunk34PK7HEU_cjs.parseContractVersion(svg);
2992
+ if (!chunk34PK7HEU_cjs.isContractCompatible(version)) {
2993
+ throw new Error(`viewer.update: incompatible DOM contract (svg=${version}). Renderer and viewer versions differ.`);
2994
+ }
2995
+ }
2996
+
2997
+ // src/runtime/viewer/widgets/rollingNumberDom.ts
2998
+ var HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
2999
+ var setStyles = (element, styles) => {
3000
+ Object.assign(element.style, styles);
3001
+ };
3002
+ function createDigit(document, character, theme) {
3003
+ const digit = document.createElementNS(HTML_NAMESPACE, "span");
3004
+ digit.setAttribute("data-sf-rolling-current", "true");
3005
+ digit.textContent = character;
3006
+ setStyles(digit, {
3007
+ position: "absolute",
3008
+ inset: "0",
3009
+ display: "grid",
3010
+ placeItems: "center",
3011
+ color: theme.color,
3012
+ fontWeight: theme.fontWeight,
3013
+ lineHeight: "1"
3014
+ });
3015
+ return digit;
3016
+ }
3017
+ function createDigitCell(document, character, theme) {
3018
+ const cell = document.createElementNS(HTML_NAMESPACE, "span");
3019
+ cell.setAttribute("data-sf-rolling-digit", "true");
3020
+ if (theme.variant === "plain") {
3021
+ setStyles(cell, {
3022
+ position: "relative",
3023
+ display: "inline-block",
3024
+ width: "0.62em",
3025
+ height: "1.1em",
3026
+ overflow: "hidden"
3027
+ });
3028
+ cell.appendChild(createDigit(document, character, theme));
3029
+ return cell;
3030
+ }
3031
+ setStyles(cell, {
3032
+ position: "relative",
3033
+ display: "inline-block",
3034
+ width: "0.72em",
3035
+ height: "1.2em",
3036
+ overflow: "hidden",
3037
+ border: `1px solid ${theme.divider}`,
3038
+ borderRadius: "0.09em",
3039
+ background: theme.surface,
3040
+ boxShadow: "inset 0 1px 0 rgba(255,255,255,0.7), inset 0 -0.16em 0.38em rgba(0,0,0,0.12)"
3041
+ });
3042
+ cell.appendChild(createDigit(document, character, theme));
3043
+ const divider = document.createElementNS(HTML_NAMESPACE, "span");
3044
+ setStyles(divider, {
3045
+ position: "absolute",
3046
+ zIndex: "2",
3047
+ left: "0",
3048
+ right: "0",
3049
+ top: "50%",
3050
+ height: "1px",
3051
+ background: theme.divider,
3052
+ pointerEvents: "none"
3053
+ });
3054
+ cell.appendChild(divider);
3055
+ return cell;
3056
+ }
3057
+ function animateDigit(element, keyframes, duration) {
3058
+ if (duration === 0 || typeof element.animate !== "function") {
3059
+ return null;
3060
+ }
3061
+ return element.animate(keyframes, {
3062
+ duration,
3063
+ easing: "cubic-bezier(0.22, 1, 0.36, 1)",
3064
+ fill: "forwards"
3065
+ });
3066
+ }
3067
+ function fadeDigit(cell, character, theme, duration) {
3068
+ const previous = cell.querySelector("[data-sf-rolling-current]");
3069
+ previous?.removeAttribute("data-sf-rolling-current");
3070
+ const next = createDigit(cell.ownerDocument, character, theme);
3071
+ next.style.opacity = "0";
3072
+ cell.insertBefore(next, cell.lastElementChild ?? null);
3073
+ const hidden = { opacity: 0, filter: "blur(0.025em)", transform: "scale(0.985)" };
3074
+ const shown = { opacity: 1, filter: "blur(0)", transform: "scale(1)" };
3075
+ const incoming = animateDigit(next, [hidden, shown], duration);
3076
+ const outgoing = previous ? animateDigit(previous, [shown, hidden], duration) : null;
3077
+ const finish = () => {
3078
+ previous?.remove();
3079
+ next.style.opacity = "1";
3080
+ };
3081
+ if (incoming) {
3082
+ incoming.finished.then(finish, finish);
3083
+ } else {
3084
+ outgoing?.cancel();
3085
+ finish();
3086
+ }
3087
+ }
3088
+ function replaceDigit(cell, character, theme, duration) {
3089
+ if (theme.motion === "fade") {
3090
+ fadeDigit(cell, character, theme, duration);
3091
+ return;
3092
+ }
3093
+ const previous = cell.querySelector("[data-sf-rolling-current]");
3094
+ previous?.removeAttribute("data-sf-rolling-current");
3095
+ const next = createDigit(cell.ownerDocument, character, theme);
3096
+ next.style.transform = "translateY(100%)";
3097
+ next.style.opacity = "0.25";
3098
+ cell.insertBefore(next, cell.lastElementChild);
3099
+ const incoming = animateDigit(
3100
+ next,
3101
+ [
3102
+ { transform: "translateY(100%)", opacity: 0.25 },
3103
+ { transform: "translateY(0)", opacity: 1 }
3104
+ ],
3105
+ duration
3106
+ );
3107
+ const outgoing = previous ? animateDigit(
3108
+ previous,
3109
+ [
3110
+ { transform: "translateY(0)", opacity: 1 },
3111
+ { transform: "translateY(-100%)", opacity: 0.25 }
3112
+ ],
3113
+ duration
3114
+ ) : null;
3115
+ const finish = () => {
3116
+ previous?.remove();
3117
+ next.style.transform = "translateY(0)";
3118
+ next.style.opacity = "1";
3119
+ };
3120
+ if (incoming) {
3121
+ incoming.finished.then(finish, finish);
3122
+ } else {
3123
+ outgoing?.cancel();
3124
+ finish();
3125
+ }
3126
+ }
3127
+ function updateRollingDigits(digits, previousValue, nextValue, theme, duration) {
3128
+ if (previousValue.length !== nextValue.length) {
3129
+ digits.replaceChildren(...[...nextValue].map((character) => createDigitCell(digits.ownerDocument, character, theme)));
3130
+ digits.setAttribute("aria-label", nextValue);
3131
+ return;
3132
+ }
3133
+ const cells = [...digits.querySelectorAll("[data-sf-rolling-digit]")];
3134
+ [...nextValue].forEach((character, index) => {
3135
+ if (previousValue[index] !== character) {
3136
+ replaceDigit(cells[index], character, theme, duration);
3137
+ }
3138
+ });
3139
+ digits.setAttribute("aria-label", nextValue);
3140
+ }
3141
+
3142
+ // src/runtime/viewer/widgets/documentWidgets.ts
3143
+ function findRollingNumber(svg, widgetId) {
3144
+ return [...svg.querySelectorAll('foreignObject[data-sf-widget="rollingNumber"]')].find(
3145
+ (surface) => surface.getAttribute("data-sf-id") === widgetId
3146
+ );
3147
+ }
3148
+ var FADE_INTERVAL = 140;
3149
+ function widgetDigits(surface) {
3150
+ return surface.querySelector("[data-sf-rolling-digits]") ?? void 0;
3151
+ }
3152
+ function widgetStyle(surface) {
3153
+ const encoded = surface.getAttribute("data-sf-widget-style");
3154
+ if (!encoded) {
3155
+ return chunkS5BVAAEF_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
3156
+ }
3157
+ try {
3158
+ return { ...chunkS5BVAAEF_cjs.DEFAULT_ROLLING_NUMBER_STYLE, ...JSON.parse(encoded) };
3159
+ } catch {
3160
+ return chunkS5BVAAEF_cjs.DEFAULT_ROLLING_NUMBER_STYLE;
3161
+ }
3162
+ }
3163
+ function positiveInteger(value) {
3164
+ if (!value) {
3165
+ return void 0;
3166
+ }
3167
+ const number2 = Number(value);
3168
+ return Number.isInteger(number2) && number2 > 0 ? number2 : void 0;
3169
+ }
3170
+ function durationFor(surface) {
3171
+ const duration = Number(surface.getAttribute("data-sf-widget-duration"));
3172
+ if (!Number.isFinite(duration) || duration < 0) {
3173
+ return 420;
3174
+ }
3175
+ const reduceMotion = surface.ownerDocument.defaultView?.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
3176
+ return reduceMotion ? 0 : duration;
3177
+ }
3178
+ function setRollingNumber(surface, value, animate) {
3179
+ const digits = widgetDigits(surface);
3180
+ if (!digits) {
3181
+ return false;
3182
+ }
3183
+ const previousValue = surface.getAttribute("data-sf-widget-value") ?? "";
3184
+ const nextValue = chunkS5BVAAEF_cjs.formatRollingNumber(value, positiveInteger(surface.getAttribute("data-sf-widget-digits")));
3185
+ if (previousValue === nextValue) {
3186
+ return true;
3187
+ }
3188
+ updateRollingDigits(digits, previousValue, nextValue, widgetStyle(surface), animate ? durationFor(surface) : 0);
3189
+ surface.setAttribute("data-sf-widget-value", nextValue);
3190
+ return true;
3191
+ }
3192
+ function mountDocumentWidgets(svg) {
3193
+ const valueOverrides = /* @__PURE__ */ new Map();
3194
+ const lastApplied = /* @__PURE__ */ new Map();
3195
+ const pending = /* @__PURE__ */ new Map();
3196
+ return {
3197
+ setValue(widgetId, value) {
3198
+ const surface = findRollingNumber(svg, widgetId);
3199
+ if (!surface) {
3200
+ return false;
3201
+ }
3202
+ valueOverrides.set(widgetId, value);
3203
+ if (widgetStyle(surface).motion !== "fade") {
3204
+ return setRollingNumber(surface, value, true);
3205
+ }
3206
+ const now = Date.now();
3207
+ const wait = (lastApplied.get(widgetId) ?? 0) + FADE_INTERVAL - now;
3208
+ if (wait <= 0) {
3209
+ lastApplied.set(widgetId, now);
3210
+ return setRollingNumber(surface, value, true);
3211
+ }
3212
+ if (!pending.has(widgetId)) {
3213
+ pending.set(
3214
+ widgetId,
3215
+ setTimeout(() => {
3216
+ pending.delete(widgetId);
3217
+ lastApplied.set(widgetId, Date.now());
3218
+ const current = findRollingNumber(svg, widgetId);
3219
+ const latest = valueOverrides.get(widgetId);
3220
+ if (current && latest !== void 0) {
3221
+ setRollingNumber(current, latest, true);
3222
+ }
3223
+ }, wait)
3224
+ );
3225
+ }
3226
+ return true;
3227
+ },
3228
+ refresh() {
3229
+ for (const [widgetId, value] of valueOverrides) {
3230
+ const surface = findRollingNumber(svg, widgetId);
3231
+ if (surface) {
3232
+ setRollingNumber(surface, value, false);
3233
+ }
3234
+ }
3235
+ },
3236
+ clearValues() {
3237
+ for (const timer of pending.values()) {
3238
+ clearTimeout(timer);
3239
+ }
3240
+ pending.clear();
3241
+ valueOverrides.clear();
3242
+ },
3243
+ destroy() {
3244
+ for (const timer of pending.values()) {
3245
+ clearTimeout(timer);
3246
+ }
3247
+ pending.clear();
3248
+ valueOverrides.clear();
3249
+ }
3250
+ };
3251
+ }
3252
+
3253
+ // src/runtime/viewer/widgets/rulerKnob.ts
3254
+ var bases = /* @__PURE__ */ new WeakMap();
3255
+ var written = /* @__PURE__ */ new WeakMap();
3256
+ var fills = /* @__PURE__ */ new WeakMap();
3257
+ function clamp01(value) {
3258
+ return Math.min(1, Math.max(0, Number.isFinite(value) ? value : 0));
3259
+ }
3260
+ function parameters(surface) {
3261
+ try {
3262
+ return JSON.parse(surface.getAttribute("data-sf-shader-params") ?? "{}");
3263
+ } catch {
3264
+ return {};
3265
+ }
3266
+ }
3267
+ function positive2(value, fallback) {
3268
+ return Number(value) > 0 ? Number(value) : fallback;
3269
+ }
3270
+ function rulerProgress(surface, value) {
3271
+ const params = parameters(surface);
3272
+ const min = Number(params.min) || 0;
3273
+ const max = Number.isFinite(Number(params.max)) && params.max !== void 0 ? Number(params.max) : 100;
3274
+ return clamp01(max === min ? 0 : (value - min) / (max - min));
3275
+ }
3276
+ function move(node, dx, dy) {
3277
+ const current = node.getAttribute("transform") ?? "";
3278
+ const base = current === written.get(node) ? bases.get(node) ?? "" : current;
3279
+ bases.set(node, base);
3280
+ const next = `translate(${dx} ${dy}) ${base}`.trim();
3281
+ written.set(node, next);
3282
+ node.setAttribute("transform", next);
3283
+ }
3284
+ function hexToRgb(value) {
3285
+ const match = /^#([0-9a-f]{6})$/i.exec(value.trim());
3286
+ if (!match) {
3287
+ return null;
3288
+ }
3289
+ const number2 = parseInt(match[1], 16);
3290
+ return [number2 >> 16 & 255, number2 >> 8 & 255, number2 & 255];
3291
+ }
3292
+ function shade(node, bump) {
3293
+ const base = fills.get(node) ?? node.getAttribute("fill") ?? "";
3294
+ fills.set(node, base);
3295
+ const rgb = hexToRgb(base);
3296
+ if (!rgb) {
3297
+ return;
3298
+ }
3299
+ const mixed = rgb.map((channel) => Math.round(channel + (255 - channel) * bump));
3300
+ const fill = `rgb(${mixed[0]}, ${mixed[1]}, ${mixed[2]})`;
3301
+ node.style.fill = fill;
3302
+ for (const child of node.querySelectorAll("tspan")) {
3303
+ child.style.fill = fill;
3304
+ }
3305
+ }
3306
+ function placeRulerKnobs(svg, progressOf) {
3307
+ const radii = /* @__PURE__ */ new Map();
3308
+ for (const surface of svg.querySelectorAll('foreignObject[data-sf-shader="ruler"]')) {
3309
+ const params = parameters(surface);
3310
+ const id = surface.getAttribute("data-sf-id") ?? "";
3311
+ const x = Number(surface.getAttribute("x"));
3312
+ const y = Number(surface.getAttribute("y"));
3313
+ const width = Number(surface.getAttribute("width"));
3314
+ const height = Number(surface.getAttribute("height"));
3315
+ const horizontal = params.axis === "horizontal";
3316
+ const progress = clamp01(progressOf(id) ?? Number(params.progress));
3317
+ const inset = positive2(params.inset, 0.05);
3318
+ const line = positive2(params.line, 0.7);
3319
+ const extent = horizontal ? width : height;
3320
+ const across = horizontal ? height : width;
3321
+ const along = inset + (1 - 2 * inset) * progress;
3322
+ const dent = params.dent === void 0 ? 1 : Number(params.dent);
3323
+ const depth = (dent < 0 ? 1 : dent) * 0.16 * across;
3324
+ const knobId = typeof params.knob === "string" ? params.knob : "";
3325
+ if (knobId) {
3326
+ const nodes = [...svg.querySelectorAll(`[data-sf-id="${CSS.escape(knobId)}"]`)];
3327
+ const primary = nodes.find((node) => node.hasAttribute("data-sf-primary"));
3328
+ if (primary?.getBBox) {
3329
+ move(primary, 0, 0);
3330
+ const box = primary.getBBox();
3331
+ const half = (horizontal ? box.height : box.width) / 2;
3332
+ radii.set(id, half / across);
3333
+ const gap = (params.gap === void 0 ? 0.04 : Number(params.gap)) * across;
3334
+ const side = line * across - depth + half + gap;
3335
+ const target = horizontal ? { x: x + width * along, y: y + side } : { x: x + side, y: y + height * (1 - along) };
3336
+ const dx = target.x - (box.x + box.width / 2);
3337
+ const dy = target.y - (box.y + box.height / 2);
3338
+ for (const node of nodes) {
3339
+ move(node, dx, dy);
3340
+ }
3341
+ }
3342
+ }
3343
+ const min = Number(params.min) || 0;
3344
+ const max = Number.isFinite(Number(params.max)) && params.max !== void 0 ? Number(params.max) : 100;
3345
+ const spread = positive2(params.spread, 0.07) * extent;
3346
+ const start = inset * extent;
3347
+ const span = (1 - 2 * inset) * extent;
3348
+ const knobAt = start + span * progress;
3349
+ for (const label of svg.querySelectorAll(`text[data-sf-id="${CSS.escape(id)}"]`)) {
3350
+ const value = Number(label.textContent);
3351
+ if (!Number.isFinite(value) || max === min) {
3352
+ continue;
3353
+ }
3354
+ const labelAt = start + span * clamp01((value - min) / (max - min));
3355
+ const bump = Math.exp(-(((labelAt - knobAt) / spread) ** 2));
3356
+ const shift = depth * 0.4 * bump;
3357
+ move(label, horizontal ? 0 : -shift, horizontal ? -shift : 0);
3358
+ shade(label, bump);
3359
+ }
3360
+ }
3361
+ return radii;
3362
+ }
3363
+
3364
+ // src/runtime/viewer/session.ts
3365
+ var ViewerSession = class {
3366
+ constructor(options, initialSvg, diagramId) {
3367
+ this.options = options;
3368
+ this.diagramId = diagramId;
3369
+ this.display = mountDisplay(options.container, initialSvg);
3370
+ this.shaderSurfaces = mountShaderSurfaces(this.display.svg);
3371
+ this.documentWidgets = mountDocumentWidgets(this.display.svg);
3372
+ this.interaction = options.interactive ? bindInteraction(this.display, this.emitter, diagramId, { selectable: options.selectable }) : null;
3373
+ this.placeKnobs();
3374
+ }
3375
+ options;
3376
+ diagramId;
3377
+ emitter = new Emitter2();
3378
+ display;
3379
+ interaction;
3380
+ shaderSurfaces;
3381
+ documentWidgets;
3382
+ shaderParameterOverrides = /* @__PURE__ */ new Map();
3383
+ rulerProgressOverrides = /* @__PURE__ */ new Map();
3384
+ placeKnobs() {
3385
+ const radii = placeRulerKnobs(this.display.svg, (id) => this.rulerProgressOverrides.get(id));
3386
+ for (const [id, knobRadius] of radii) {
3387
+ this.setShaderParameters(id, { knobRadius });
3388
+ }
3389
+ }
3390
+ get svg() {
3391
+ return this.display.svg;
3392
+ }
3393
+ on(name, handler) {
3394
+ return this.emitter.on(name, handler);
3395
+ }
3396
+ getSelection() {
3397
+ return this.interaction?.getSelection() ?? [];
3398
+ }
3399
+ select(ids) {
3400
+ this.interaction?.select(ids);
3401
+ }
3402
+ fit() {
3403
+ this.display.fit();
3404
+ this.interaction?.repaint();
3405
+ }
3406
+ getViewport() {
3407
+ return this.display.getViewport();
3408
+ }
3409
+ exportSvg() {
3410
+ return serializeSvgSnapshot(this.svg, this.shaderSurfaces.capture());
3411
+ }
3412
+ exportImage(options) {
3413
+ return rasterizeSvgSnapshot(this.svg, this.exportSvg(), options);
3414
+ }
3415
+ setShaderParameters(ownerId, parameters3) {
3416
+ const merged = { ...this.shaderParameterOverrides.get(ownerId), ...parameters3 };
3417
+ this.shaderParameterOverrides.set(ownerId, merged);
3418
+ return this.shaderSurfaces.setParameters(ownerId, parameters3);
3419
+ }
3420
+ setWidgetValue(widgetId, value) {
3421
+ if (this.documentWidgets.setValue(widgetId, value)) {
3422
+ return true;
3423
+ }
3424
+ const ruler = [...this.svg.querySelectorAll('foreignObject[data-sf-shader="ruler"]')].find(
3425
+ (element) => element.getAttribute("data-sf-id") === widgetId
3426
+ );
3427
+ if (ruler) {
3428
+ const progress = rulerProgress(ruler, Number(value));
3429
+ this.rulerProgressOverrides.set(widgetId, progress);
3430
+ const applied = this.setShaderParameters(widgetId, { progress });
3431
+ this.placeKnobs();
3432
+ return applied;
3433
+ }
3434
+ const scale = [...this.svg.querySelectorAll('foreignObject[data-sf-shader="tickScale"]')].find(
3435
+ (element) => element.getAttribute("data-sf-id") === widgetId
3436
+ );
3437
+ if (!scale) {
3438
+ return false;
3439
+ }
3440
+ let max = 100;
3441
+ try {
3442
+ max = Number(JSON.parse(scale.getAttribute("data-sf-shader-params") ?? "{}").max) || 100;
3443
+ } catch {
3444
+ max = 100;
3445
+ }
3446
+ return this.setShaderParameters(widgetId, { progress: Math.min(1, Math.max(0, Number(value) / max)) });
3447
+ }
3448
+ setPaused(paused) {
3449
+ this.shaderSurfaces.setPaused(paused);
3450
+ }
3451
+ clearOverrides() {
3452
+ this.shaderParameterOverrides.clear();
3453
+ this.rulerProgressOverrides.clear();
3454
+ this.documentWidgets.clearValues();
3455
+ }
3456
+ update(next) {
3457
+ const svg = renderViewerSource(next, this.options.renderOptions);
3458
+ assertViewerUpdateContract(svg);
3459
+ const diff = this.display.updateContent(svg);
3460
+ this.shaderSurfaces.refresh();
3461
+ this.documentWidgets.refresh();
3462
+ this.placeKnobs();
3463
+ for (const [ownerId, parameters3] of this.shaderParameterOverrides) {
3464
+ this.shaderSurfaces.setParameters(ownerId, parameters3);
3465
+ }
3466
+ if (next.resetView) {
3467
+ this.display.fit();
3468
+ }
3469
+ const selectedIds = this.interaction?.getSelection().map((owner) => owner.id) ?? [];
3470
+ if (selectedIds.length > 0) {
3471
+ this.interaction?.select(selectedIds);
3472
+ }
3473
+ this.emitter.emit("sceneDiff", { diagramId: this.diagramId, ...diff });
3474
+ if (next.highlightChanges) {
3475
+ const color2 = typeof next.highlightChanges === "object" ? next.highlightChanges.color : void 0;
3476
+ this.interaction?.flashChanged([...diff.added, ...diff.changed], color2);
3477
+ }
3478
+ }
3479
+ announceReady() {
3480
+ queueMicrotask(() => this.emitter.emit("ready", { diagramId: this.diagramId }));
3481
+ }
3482
+ destroy() {
3483
+ this.interaction?.destroy();
3484
+ this.shaderSurfaces.destroy();
3485
+ this.documentWidgets.destroy();
3486
+ this.shaderParameterOverrides.clear();
3487
+ this.display.destroy();
3488
+ this.emitter.clear();
3489
+ }
3490
+ };
3491
+
3492
+ // src/runtime/viewer/connectInteraction.ts
3493
+ var TAU = Math.PI * 2;
3494
+ var LONGEST_TICK = 0.05;
3495
+ var finite = (value, fallback) => Number.isFinite(Number(value)) && value !== "" ? Number(value) : fallback;
3496
+ function parameters2(surface) {
3497
+ try {
3498
+ return JSON.parse(surface.getAttribute("data-sf-shader-params") ?? "{}");
3499
+ } catch {
3500
+ return {};
3501
+ }
3502
+ }
3503
+ function pointerValue(svg, id, x, y) {
3504
+ const surface = svg.querySelector(`foreignObject[data-sf-shader][data-sf-id="${CSS.escape(id)}"]`);
3505
+ if (!surface) {
3506
+ return void 0;
3507
+ }
3508
+ const params = parameters2(surface);
3509
+ const box = surface.getBoundingClientRect();
3510
+ const clamp = (value) => Math.min(1, Math.max(0, value));
3511
+ if (surface.getAttribute("data-sf-kind") === "ruler") {
3512
+ const inset = finite(params.inset, 0) > 0 ? finite(params.inset, 0.05) : 0.05;
3513
+ const min = finite(params.min, 0);
3514
+ const max = finite(params.max, 100);
3515
+ const along = params.axis === "horizontal" ? (x - box.left) / box.width : 1 - (y - box.top) / box.height;
3516
+ return min + clamp((along - inset) / (1 - 2 * inset)) * (max - min);
3517
+ }
3518
+ const start = finite(params.start, 225) * Math.PI / 180;
3519
+ const sweep = finite(params.sweep, 270) * Math.PI / 180;
3520
+ const gap = TAU - sweep;
3521
+ const angle = Math.atan2(-(y - (box.top + box.height / 2)), x - (box.left + box.width / 2));
3522
+ const shifted = start - angle + gap / 2;
3523
+ const fraction = clamp(((shifted % TAU + TAU) % TAU - gap / 2) / sweep);
3524
+ return fraction * finite(params.max, 100);
3525
+ }
3526
+ function hit(svg, ids, x, y) {
3527
+ let found = null;
3528
+ for (const node of svg.querySelectorAll("[data-sf-primary][data-sf-id]")) {
3529
+ const id = node.getAttribute("data-sf-id") ?? "";
3530
+ if (!ids.has(id)) {
3531
+ continue;
3532
+ }
3533
+ const box = node.getBoundingClientRect();
3534
+ if (x >= box.left && x <= box.right && y >= box.top && y <= box.bottom) {
3535
+ found = id;
3536
+ }
3537
+ }
3538
+ return found;
3539
+ }
3540
+ function trackScales(wiring, event) {
3541
+ const { runtime, svg } = wiring;
3542
+ for (const id of runtime.interactive.scales) {
3543
+ const value = pointerValue(svg, id, event.clientX, event.clientY);
3544
+ if (value !== void 0) {
3545
+ runtime.setElementState(id, { pointer: value });
3546
+ }
3547
+ }
3548
+ }
3549
+ function updateHover(wiring, event) {
3550
+ const { runtime, svg, options, session } = wiring;
3551
+ const over = options.enabled?.() === false ? null : hit(svg, runtime.interactive.pressable, event.clientX, event.clientY);
3552
+ if (over === session.hovered) {
3553
+ return;
3554
+ }
3555
+ if (session.hovered) {
3556
+ runtime.setElementState(session.hovered, { hovered: false });
3557
+ }
3558
+ session.hovered = over;
3559
+ if (over) {
3560
+ runtime.setElementState(over, { hovered: true });
3561
+ }
3562
+ }
3563
+ function pressStart(wiring, event) {
3564
+ const { runtime, svg, options, session } = wiring;
3565
+ session.swallowClick = false;
3566
+ const inside = event.target instanceof Node && svg.contains(event.target);
3567
+ if (event.button !== 0 || options.enabled?.() === false || !inside) {
3568
+ return;
3569
+ }
3570
+ const id = hit(svg, runtime.interactive.pressable, event.clientX, event.clientY);
3571
+ if (!id) {
3572
+ return;
3573
+ }
3574
+ event.stopPropagation();
3575
+ event.preventDefault();
3576
+ session.swallowClick = true;
3577
+ session.active = id;
3578
+ trackScales(wiring, event);
3579
+ runtime.press(id);
3580
+ runtime.drag(id);
3581
+ }
3582
+ function pressMove(wiring, event) {
3583
+ const { runtime, session } = wiring;
3584
+ if (!session.active) {
3585
+ updateHover(wiring, event);
3586
+ return;
3587
+ }
3588
+ trackScales(wiring, event);
3589
+ runtime.drag(session.active);
3590
+ }
3591
+ function pressEnd(wiring, event, tapOnElement) {
3592
+ const { runtime, svg, session } = wiring;
3593
+ if (!session.active) {
3594
+ return;
3595
+ }
3596
+ event.stopPropagation();
3597
+ const id = session.active;
3598
+ session.active = null;
3599
+ const tapped = tapOnElement && hit(svg, runtime.interactive.pressable, event.clientX, event.clientY) === id;
3600
+ runtime.release(id, tapped);
3601
+ updateHover(wiring, event);
3602
+ }
3603
+ function swallowClick(session, event) {
3604
+ if (!session.swallowClick) {
3605
+ return;
3606
+ }
3607
+ session.swallowClick = false;
3608
+ event.stopPropagation();
3609
+ event.preventDefault();
3610
+ }
3611
+ function startTicks(runtime, options) {
3612
+ if (!runtime.graph.handlers.some((handler) => handler.event === "onTick")) {
3613
+ return () => void 0;
3614
+ }
3615
+ let previous = performance.now();
3616
+ let frame = 0;
3617
+ const loop = (now) => {
3618
+ if (options.paused?.() !== true) {
3619
+ runtime.tick(Math.min((now - previous) / 1e3, LONGEST_TICK));
3620
+ }
3621
+ previous = now;
3622
+ frame = window.requestAnimationFrame(loop);
3623
+ };
3624
+ frame = window.requestAnimationFrame(loop);
3625
+ return () => window.cancelAnimationFrame(frame);
3626
+ }
3627
+ function connectInteraction(viewer, runtime, options = {}) {
3628
+ const session = { active: null, hovered: null, swallowClick: false };
3629
+ const wiring = { runtime, svg: viewer.svg, options, session };
3630
+ const listeners = [
3631
+ ["click", (event) => swallowClick(session, event)],
3632
+ ["pointerdown", (event) => pressStart(wiring, event)],
3633
+ ["pointermove", (event) => pressMove(wiring, event)],
3634
+ ["pointerup", (event) => pressEnd(wiring, event, true)],
3635
+ ["pointercancel", (event) => pressEnd(wiring, event, false)]
3636
+ ];
3637
+ for (const [type, listener] of listeners) {
3638
+ window.addEventListener(type, listener, true);
3639
+ }
3640
+ const stopTicks = startTicks(runtime, options);
3641
+ return () => {
3642
+ for (const [type, listener] of listeners) {
3643
+ window.removeEventListener(type, listener, true);
3644
+ }
3645
+ stopTicks();
3646
+ if (session.active) {
3647
+ runtime.release(session.active, false);
3648
+ }
3649
+ if (session.hovered) {
3650
+ runtime.setElementState(session.hovered, { hovered: false });
3651
+ }
3652
+ };
3653
+ }
3654
+
3655
+ // src/runtime/viewer/index.ts
3656
+ function createViewer(options) {
3657
+ const diagramId = options.diagramId ?? "diagram";
3658
+ const svg = renderViewerSource(options, options.renderOptions);
3659
+ assertCreateViewerContract(svg);
3660
+ const viewer = new ViewerSession(options, svg, diagramId);
3661
+ viewer.announceReady();
3662
+ return viewer;
3663
+ }
3664
+
3665
+ exports.SHADER_DEFAULTS = SHADER_DEFAULTS;
3666
+ exports.connectInteraction = connectInteraction;
3667
+ exports.createViewer = createViewer;
3668
+ exports.registerShaderProgram = registerShaderProgram;
3669
+ exports.shaderProgram = shaderProgram;
3670
+ //# sourceMappingURL=chunk-IIOJFR73.cjs.map
3671
+ //# sourceMappingURL=chunk-IIOJFR73.cjs.map