vireglass 1.0.0 → 2.0.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 (97) hide show
  1. package/README.md +83 -9
  2. package/dist/adapters-BfeAopGO.d.ts +154 -0
  3. package/dist/adapters-DugC6Qri.d.cts +154 -0
  4. package/dist/chunk-3PGYF2NK.js +360 -0
  5. package/dist/chunk-3PGYF2NK.js.map +1 -0
  6. package/dist/{chunk-HF2AWZEZ.js → chunk-IQOBZGCF.js} +210 -606
  7. package/dist/chunk-IQOBZGCF.js.map +1 -0
  8. package/dist/chunk-JF3FK254.js +377 -0
  9. package/dist/chunk-JF3FK254.js.map +1 -0
  10. package/dist/chunk-P2ZOODXD.js +58 -0
  11. package/dist/chunk-P2ZOODXD.js.map +1 -0
  12. package/dist/chunk-TTOKBARR.js +456 -0
  13. package/dist/chunk-TTOKBARR.js.map +1 -0
  14. package/dist/dom.cjs +2055 -0
  15. package/dist/dom.cjs.map +1 -0
  16. package/dist/dom.d.cts +347 -0
  17. package/dist/dom.d.ts +347 -0
  18. package/dist/dom.js +1082 -0
  19. package/dist/dom.js.map +1 -0
  20. package/dist/geometry-GvXtt-_S.d.ts +105 -0
  21. package/dist/geometry-RhKd7CM5.d.cts +105 -0
  22. package/dist/group-model-8u4GKrUu.d.cts +194 -0
  23. package/dist/group-model-vYSmEVia.d.ts +194 -0
  24. package/dist/index.cjs +737 -244
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +122 -113
  27. package/dist/index.d.ts +122 -113
  28. package/dist/index.js +6 -277
  29. package/dist/index.js.map +1 -1
  30. package/dist/law.cjs +478 -0
  31. package/dist/law.cjs.map +1 -0
  32. package/dist/law.d.cts +543 -0
  33. package/dist/law.d.ts +543 -0
  34. package/dist/law.js +3 -0
  35. package/dist/law.js.map +1 -0
  36. package/dist/{material-CkUHUBoy.d.cts → material-CnUmdKg1.d.cts} +1 -1
  37. package/dist/{material-CkUHUBoy.d.ts → material-CnUmdKg1.d.ts} +1 -1
  38. package/dist/react.d.cts +1 -1
  39. package/dist/react.d.ts +1 -1
  40. package/dist/reference.cjs +221 -0
  41. package/dist/reference.cjs.map +1 -0
  42. package/dist/reference.d.cts +183 -0
  43. package/dist/reference.d.ts +183 -0
  44. package/dist/reference.js +197 -0
  45. package/dist/reference.js.map +1 -0
  46. package/dist/web/index.cjs +247 -165
  47. package/dist/web/index.cjs.map +1 -1
  48. package/dist/web/index.d.cts +4 -29
  49. package/dist/web/index.d.ts +4 -29
  50. package/dist/web/index.js +5 -90
  51. package/dist/web/index.js.map +1 -1
  52. package/docs/adr-001-rendering.md +288 -0
  53. package/docs/architecture.md +161 -0
  54. package/docs/benchmarks.md +191 -0
  55. package/docs/dom.md +160 -0
  56. package/docs/law.md +65 -0
  57. package/docs/material-lab.md +799 -0
  58. package/docs/platform-parity.md +266 -0
  59. package/docs/reference.md +397 -0
  60. package/package.json +24 -3
  61. package/src/accent.ts +35 -0
  62. package/src/accessibility.ts +30 -16
  63. package/src/adapters.ts +71 -3
  64. package/src/appear.ts +45 -0
  65. package/src/concentric.ts +66 -0
  66. package/src/dom/body.ts +98 -0
  67. package/src/dom/displacement.ts +163 -0
  68. package/src/dom/group.ts +124 -0
  69. package/src/dom/index.ts +739 -0
  70. package/src/dom/map-cache.ts +37 -0
  71. package/src/dom/nesting.ts +32 -0
  72. package/src/dom/preferences.ts +53 -0
  73. package/src/dom/probe.ts +163 -0
  74. package/src/dom/rim.ts +103 -0
  75. package/src/dom/scroll-edge.ts +106 -0
  76. package/src/dom/shadow.ts +56 -0
  77. package/src/dom/spectral-map.ts +102 -0
  78. package/src/dom/support.ts +47 -0
  79. package/src/geometry.ts +41 -14
  80. package/src/glass-scale.ts +3 -5
  81. package/src/index.ts +3 -1
  82. package/src/law.ts +562 -0
  83. package/src/lens-shader.ts +53 -30
  84. package/src/native/glass-surface.tsx +8 -14
  85. package/src/native/provider.tsx +22 -2
  86. package/src/optics.ts +43 -62
  87. package/src/reference.ts +9 -0
  88. package/src/scroll-edge.ts +22 -7
  89. package/src/sdf.ts +195 -8
  90. package/src/shadow-tint.ts +23 -0
  91. package/src/spectral.ts +110 -0
  92. package/src/surface-shader.ts +8 -6
  93. package/src/touch-response.ts +42 -27
  94. package/src/web/index.ts +0 -1
  95. package/dist/adapters-BPB0D5nr.d.cts +0 -357
  96. package/dist/adapters-BgSc-smX.d.ts +0 -357
  97. package/dist/chunk-HF2AWZEZ.js.map +0 -1
@@ -1,51 +1,42 @@
1
+ import { bevelDp, thicknessDp, rimDp, surfacePadDp, shadowReachDp, bevelFraction, halfMinDp } from './chunk-P2ZOODXD.js';
2
+ import { DERIVE, TOUCH, EPSILON, BEVEL, LENS, BODY, MEDIUM, SPECTRAL, DISPERSION, RIM, ACCENT, MORPH } from './chunk-TTOKBARR.js';
3
+
1
4
  // src/optics.ts
2
5
  var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
3
6
  var clamp01 = (v) => clamp(v, 0, 1);
4
7
  var fresnelF0 = (ior) => ((ior - 1) / (ior + 1)) ** 2;
5
- var FRESNEL_GAIN = 17.5;
6
- var fresnelStrength = (ior) => clamp01(fresnelF0(ior) * FRESNEL_GAIN);
7
- var FRESNEL_EXPONENT = 5;
8
- var refractionStrength = (ior) => clamp01((ior - 1) / 0.6);
9
- var MAGNIFY_PER_DP = 6e-3;
10
- var refractionScale = (ior, thicknessDp2) => 1 + MAGNIFY_PER_DP * thicknessDp2 * (1 - 1 / Math.max(ior, 1));
11
- var DISPERSION_PER_IOR = 1.1;
12
- var dispersion = (ior) => clamp01((ior - 1) * DISPERSION_PER_IOR);
13
- var IOR_SPREAD_PER_DISPERSION = 0.045;
14
- var iorSpread = (ior) => dispersion(ior) * IOR_SPREAD_PER_DISPERSION;
15
- var absorption = (pathDp) => 1 - Math.exp(-0.017 * Math.max(pathDp, 0));
8
+ var fresnelStrength = (ior) => clamp01(fresnelF0(ior) * DERIVE.fresnelGain);
9
+ var FRESNEL_EXPONENT = DERIVE.fresnelExponent;
10
+ var refractionStrength = (ior) => clamp01((ior - 1) / DERIVE.iorFullBend);
11
+ var refractionScale = (ior, thicknessDp2) => 1 + DERIVE.magnifyPerDp * thicknessDp2 * (1 - 1 / Math.max(ior, 1));
12
+ var dispersion = (ior) => clamp01((ior - 1) * DERIVE.dispersionPerIor);
13
+ var iorSpread = (ior) => dispersion(ior) * DERIVE.iorSpreadPerDispersion;
14
+ var absorption = (pathDp) => 1 - Math.exp(-DERIVE.absorbPerDp * Math.max(pathDp, 0));
16
15
  var edgeDensity = (thicknessDp2, bevelDp2) => {
17
16
  const body = absorption(thicknessDp2);
18
17
  if (body <= 0) return 1;
19
- return clamp(absorption(thicknessDp2 + bevelDp2) / body, 1, 4);
18
+ return clamp(absorption(thicknessDp2 + bevelDp2) / body, 1, DERIVE.edgeDensityMax);
20
19
  };
21
- var BLUR_MAX = 12;
22
- var blur = (roughness) => clamp01(roughness) * BLUR_MAX;
23
- var specularPower = (roughness) => 160 - clamp01(roughness) * 152;
24
- var specularStrength = (ior, roughness) => clamp01(fresnelStrength(ior) * (1 - clamp01(roughness) * 0.6));
25
- var COOL = { r: 0.86, g: 1, b: 1.08 };
26
- var NEUTRAL = { r: 1, g: 1.02, b: 0.99 };
27
- var WARM = { r: 1.08, g: 1, b: 0.88 };
28
- var mixHue = (a, b, t) => ({
29
- r: a.r + (b.r - a.r) * t,
30
- g: a.g + (b.g - a.g) * t,
31
- b: a.b + (b.b - a.b) * t
32
- });
20
+ var blur = (roughness) => clamp01(roughness) * DERIVE.blurMaxDp;
21
+ var specularPower = (roughness) => DERIVE.specularPowerSmooth - clamp01(roughness) * (DERIVE.specularPowerSmooth - DERIVE.specularPowerRough);
22
+ var specularStrength = (ior, roughness) => clamp01(fresnelStrength(ior) * (1 - clamp01(roughness) * DERIVE.specularRoughDamping));
23
+ var mixHue = (a, b, t) => [
24
+ a[0] + (b[0] - a[0]) * t,
25
+ a[1] + (b[1] - a[1]) * t,
26
+ a[2] + (b[2] - a[2]) * t
27
+ ];
33
28
  function mediumTint(ior) {
34
- const t = clamp01((ior - 1.2) / 0.55);
35
- const hue = t < 0.5 ? mixHue(COOL, NEUTRAL, t * 2) : mixHue(NEUTRAL, WARM, (t - 0.5) * 2);
36
- const lift = 0.34 + fresnelF0(ior) * 2.4;
37
- return { r: clamp01(hue.r * lift), g: clamp01(hue.g * lift), b: clamp01(hue.b * lift) };
29
+ const t = clamp01((ior - DERIVE.hueFromIor) / DERIVE.hueSpan);
30
+ const hue = t < 0.5 ? mixHue(DERIVE.hueCool, DERIVE.hueNeutral, t * 2) : mixHue(DERIVE.hueNeutral, DERIVE.hueWarm, (t - 0.5) * 2);
31
+ const lift = DERIVE.bodyLiftBase + fresnelF0(ior) * DERIVE.bodyLiftPerF0;
32
+ return { r: clamp01(hue[0] * lift), g: clamp01(hue[1] * lift), b: clamp01(hue[2] * lift) };
38
33
  }
39
- var GATHER_PER_BEVEL = 4;
40
- var GATHER_MAX = 28;
41
- var gatherRadius = (bevelDp2) => clamp(Math.max(bevelDp2, 1) * GATHER_PER_BEVEL, 4, GATHER_MAX);
42
- var BODY_DENSITY = 0.05;
43
- var bodyDensity = (thicknessDp2) => BODY_DENSITY * absorption(thicknessDp2);
44
- var EDGE_LIGHT_GAIN = 6;
45
- var edgeLight = (ior) => clamp01(fresnelF0(ior) * EDGE_LIGHT_GAIN);
46
- var iridescence = (ior, filmNm) => filmNm <= 0 ? 0 : clamp01(fresnelStrength(ior) * 1.6);
47
- var diffraction = (ior) => clamp01(dispersion(ior) * 0.5);
48
- var colorPickup = (ior) => clamp(fresnelStrength(ior) * 0.9, 0, 0.42);
34
+ var gatherRadius = (bevelDp2) => clamp(Math.max(bevelDp2, 1) * DERIVE.gatherPerBevel, DERIVE.gatherMinDp, DERIVE.gatherMaxDp);
35
+ var bodyDensity = (thicknessDp2) => DERIVE.bodyDensity * absorption(thicknessDp2);
36
+ var edgeLight = (ior) => clamp01(fresnelF0(ior) * DERIVE.edgeLightGain);
37
+ var iridescence = (ior, filmNm) => filmNm <= 0 ? 0 : clamp01(fresnelStrength(ior) * DERIVE.iridescencePerFresnel);
38
+ var diffraction = (ior) => clamp01(dispersion(ior) * DERIVE.diffractionPerDispersion);
39
+ var colorPickup = (ior) => clamp(fresnelStrength(ior) * DERIVE.colorPickupPerFresnel, 0, DERIVE.colorPickupMax);
49
40
 
50
41
  // src/material.ts
51
42
  var MATERIAL_RANGES = {
@@ -335,59 +326,8 @@ function debugIndex(mode) {
335
326
  return DEBUG_MODES.indexOf(mode);
336
327
  }
337
328
 
338
- // src/geometry.ts
339
- var circleGeometry = (size) => ({
340
- width: size,
341
- height: size,
342
- cornerRadius: size / 2
343
- });
344
- var capsuleGeometry = (width, height) => ({
345
- width,
346
- height,
347
- cornerRadius: Math.min(width, height) / 2
348
- });
349
- var roundedRectGeometry = (width, height, cornerRadius) => ({ width, height, cornerRadius });
350
- var halfMinDp = (g) => Math.max(Math.min(g.width, g.height) / 2, 1);
351
- var MAX_STRETCH = 0.34;
352
- var SIZE_REF_DP = 24;
353
- var sizeGain = (g) => Math.min(Math.max(Math.sqrt(halfMinDp(g) / SIZE_REF_DP), 0.8), 2.4);
354
- var MAX_BEVEL_FRACTION = 0.65;
355
- var bevelFraction = (g, o) => Math.min(MAX_BEVEL_FRACTION, o.bevelDp * sizeGain(g) / halfMinDp(g));
356
- var bevelDp = (g, o) => Math.max(bevelFraction(g, o) * halfMinDp(g), 1);
357
- var thicknessDp = (g, o) => o.thicknessDp * sizeGain(g);
358
- var RIM_FRACTION = 0.85;
359
- var rimDp = (g, o) => thicknessDp(g, o) * RIM_FRACTION;
360
- var shadowReachDp = (g) => Math.min(Math.max(halfMinDp(g) * 0.65, 6), 36);
361
- function morphReachDp(g, morph) {
362
- if (!morph || morph.smoothing <= 0) return 0;
363
- return Math.max(
364
- 0,
365
- Math.abs(morph.offsetX) + morph.width / 2 - g.width / 2,
366
- Math.abs(morph.offsetY) + morph.height / 2 - g.height / 2
367
- );
368
- }
369
- var PAD_STEP = 8;
370
- var quantise = (v) => Math.ceil(v / PAD_STEP) * PAD_STEP;
371
- function lensPadDp(g, o, morph, dragLimit = 0) {
372
- const sampling = Math.max(o.blur, o.gatherRadiusDp);
373
- const stretch = halfMinDp(g) * MAX_STRETCH;
374
- return quantise(sampling + dragLimit + stretch + morphReachDp(g, morph) + 2);
375
- }
376
- function surfacePadDp(g, dragLimit = 0, morph) {
377
- return quantise(shadowReachDp(g) * 1.2 + dragLimit + morphReachDp(g, morph) + 2);
378
- }
379
- var SHADOW_FLAT = 0.8;
380
- var SHADOW_BUSY_GAIN = 6;
381
- var SHADOW_BUSY_RISE = 1.2;
382
- var SHADOW_STEP = 0.05;
383
- function shadowOpacity(busy) {
384
- return SHADOW_FLAT + Math.min(Math.max(busy, 0) * SHADOW_BUSY_GAIN, SHADOW_BUSY_RISE);
385
- }
386
- function shadowOpacityFrom(sample) {
387
- return Math.round(shadowOpacity(sample.busy) / SHADOW_STEP) * SHADOW_STEP;
388
- }
389
-
390
329
  // src/sdf.ts
330
+ var VG_TAU = 6.28318530718;
391
331
  var VG_SDF = `
392
332
  float vgRoundRect(float2 p, float2 halfSize, float corner) {
393
333
  float2 q = abs(p) - halfSize + corner;
@@ -424,7 +364,7 @@ float2 vgTouchWarp(float2 p, float2 touch, float2 pull, float press, float radiu
424
364
  float waveAmp, float wavePhase) {
425
365
  if (radius <= 0.0) { return p; }
426
366
  // Under the finger the whole element grows (M 3:51; HIG: interactive "expands").
427
- p /= 1.0 + 0.06 * press;
367
+ p /= 1.0 + ${TOUCH.pressGrow} * press;
428
368
  float2 d = p - touch;
429
369
  float r = length(d);
430
370
 
@@ -442,16 +382,16 @@ float2 vgTouchWarp(float2 p, float2 touch, float2 pull, float press, float radiu
442
382
  // Without this ridge the shape just balloons, and a dense medium doesn't behave that way.
443
383
  float rim = k * k * (1.0 - k) * 4.0;
444
384
 
445
- float2 q = p - pull * (core - 0.42 * rim);
446
- q -= d * (press * 0.16 * core);
385
+ float2 q = p - pull * (core - ${TOUCH.ridge} * rim);
386
+ q -= d * (press * ${TOUCH.pressPull} * core);
447
387
 
448
388
  // The wave has its own scale, twice the drag radius: the ripple has to reach the far edge,
449
389
  // otherwise it reads as jitter under the finger rather than a wave across the surface. The
450
390
  // wavelength is short: in a dense medium ripples are frequent and small, long shallow swells
451
391
  // are water.
452
392
  if (waveAmp > 0.0 && r > 0.0001) {
453
- float span = radius * 2.0;
454
- float ring = sin(r / (span * 0.17) - wavePhase * 6.2831853) * exp(-r / (span * 0.7));
393
+ float span = radius * ${TOUCH.waveSpan}.0;
394
+ float ring = sin(r / (span * ${TOUCH.waveLength}) - wavePhase * ${VG_TAU}) * exp(-r / (span * ${TOUCH.waveDecay}));
455
395
  q -= (d / r) * ring * waveAmp;
456
396
  }
457
397
  return q;
@@ -485,14 +425,14 @@ float2 vgSceneNormal(float2 p, float2 halfSize, float corner,
485
425
  float a = vgRoundRect(p, halfSize, corner);
486
426
  float b = vgRoundRect(q, halfB, cornerB);
487
427
  float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0);
488
- float2 nab = normalize(mix(vgRoundRectNormal(q, halfB, cornerB), na, h) + float2(1e-5, 1e-5));
428
+ float2 nab = normalize(mix(vgRoundRectNormal(q, halfB, cornerB), na, h) + float2(${EPSILON}, ${EPSILON}));
489
429
  if (halfC.x <= 0.0) { return nab; }
490
430
  // Same derivation as for the pair: normals blend with the same weight as the distances.
491
431
  float2 qc = p - offsetC;
492
432
  float ab = vgSmin(a, b, k);
493
433
  float c = vgRoundRect(qc, halfC, cornerC);
494
434
  float hc = clamp(0.5 + 0.5 * (c - ab) / k, 0.0, 1.0);
495
- return normalize(mix(vgRoundRectNormal(qc, halfC, cornerC), nab, hc) + float2(1e-5, 1e-5));
435
+ return normalize(mix(vgRoundRectNormal(qc, halfC, cornerC), nab, hc) + float2(${EPSILON}, ${EPSILON}));
496
436
  }
497
437
 
498
438
  // Position within the bevel: 0 is the flat middle, 1 is the very edge. This one value feeds the
@@ -509,7 +449,7 @@ float vgBevelT(float sd, float bevel) {
509
449
  // This still doesn't control the field of view: only the bevel bends, the middle stays flat,
510
450
  // otherwise the surface reads as a soap bubble.
511
451
  float vgBevelSlope(float t) {
512
- return min(t * inversesqrt(max(1.0 - t * t * 0.94, 0.02)), 3.2);
452
+ return min(t * inversesqrt(max(1.0 - t * t * ${BEVEL.sphere}, ${BEVEL.floor})), ${BEVEL.slopeMax});
513
453
  }
514
454
 
515
455
  // PROGRESS FRACTION. Progress is an active state turned into a FIELD: to the left of the boundary
@@ -528,6 +468,97 @@ float vgProgress(float2 p, float2 halfSize, float progress) {
528
468
  }
529
469
  `;
530
470
  var VG_FALLOFF = 2.6;
471
+ function sdfRoundedRect(x, y, w, h, r) {
472
+ const qx = Math.abs(x) - w / 2 + r;
473
+ const qy = Math.abs(y) - h / 2 + r;
474
+ return Math.min(Math.max(qx, qy), 0) + Math.hypot(Math.max(qx, 0), Math.max(qy, 0)) - r;
475
+ }
476
+ function sdfRoundedRectGradient(x, y, w, h, r) {
477
+ const qx = Math.abs(x) - w / 2 + r;
478
+ const qy = Math.abs(y) - h / 2 + r;
479
+ let gx;
480
+ let gy;
481
+ if (qx > 0 && qy > 0) {
482
+ const cx = Math.max(qx, 1e-4);
483
+ const cy = Math.max(qy, 1e-4);
484
+ const len = Math.hypot(cx, cy);
485
+ gx = cx / len;
486
+ gy = cy / len;
487
+ } else if (qx > qy) {
488
+ gx = 1;
489
+ gy = 0;
490
+ } else {
491
+ gx = 0;
492
+ gy = 1;
493
+ }
494
+ return [gx * Math.sign(x), gy * Math.sign(y)];
495
+ }
496
+ function smin(a, b, k) {
497
+ const h = Math.min(Math.max(0.5 + 0.5 * (b - a) / k, 0), 1);
498
+ return b + (a - b) * h - k * h * (1 - h);
499
+ }
500
+ function neckToBridge(gap) {
501
+ return gap > 0 ? gap * 2 : 0;
502
+ }
503
+ function sceneDistance(x, y, w, h, r, smoothing = 0, b, c) {
504
+ const a = sdfRoundedRect(x, y, w, h, r);
505
+ if (smoothing <= 0 || !b || b.width <= 0) return a;
506
+ const db = sdfRoundedRect(x - b.offsetX, y - b.offsetY, b.width, b.height, b.cornerRadius);
507
+ const ab = smin(a, db, smoothing);
508
+ if (!c || c.width <= 0) return ab;
509
+ const dc = sdfRoundedRect(x - c.offsetX, y - c.offsetY, c.width, c.height, c.cornerRadius);
510
+ return smin(ab, dc, smoothing);
511
+ }
512
+ function sceneGradient(x, y, w, h, r, smoothing = 0, b, c) {
513
+ const na = sdfRoundedRectGradient(x, y, w, h, r);
514
+ if (smoothing <= 0 || !b || b.width <= 0) return na;
515
+ const bx = x - b.offsetX;
516
+ const by = y - b.offsetY;
517
+ const da = sdfRoundedRect(x, y, w, h, r);
518
+ const db = sdfRoundedRect(bx, by, b.width, b.height, b.cornerRadius);
519
+ const hb = Math.min(Math.max(0.5 + 0.5 * (db - da) / smoothing, 0), 1);
520
+ const nb = sdfRoundedRectGradient(bx, by, b.width, b.height, b.cornerRadius);
521
+ let gx = nb[0] + (na[0] - nb[0]) * hb;
522
+ let gy = nb[1] + (na[1] - nb[1]) * hb;
523
+ if (c && c.width > 0) {
524
+ const cx = x - c.offsetX;
525
+ const cy = y - c.offsetY;
526
+ const dab = smin(da, db, smoothing);
527
+ const dc = sdfRoundedRect(cx, cy, c.width, c.height, c.cornerRadius);
528
+ const hc = Math.min(Math.max(0.5 + 0.5 * (dc - dab) / smoothing, 0), 1);
529
+ const nc = sdfRoundedRectGradient(cx, cy, c.width, c.height, c.cornerRadius);
530
+ gx = nc[0] + (gx - nc[0]) * hc;
531
+ gy = nc[1] + (gy - nc[1]) * hc;
532
+ }
533
+ gx += EPSILON;
534
+ gy += EPSILON;
535
+ const len = Math.hypot(gx, gy) || 1;
536
+ return [gx / len, gy / len];
537
+ }
538
+ function touchWarp(x, y, t) {
539
+ if (t.radius <= 0) return [x, y];
540
+ const grow = 1 + TOUCH.pressGrow * t.press;
541
+ let px = x / grow;
542
+ let py = y / grow;
543
+ const dx = px - t.x;
544
+ const dy = py - t.y;
545
+ const r = Math.hypot(dx, dy);
546
+ const k = Math.min(Math.max((t.radius - r) / t.radius, 0), 1);
547
+ const s = k * k * (3 - 2 * k);
548
+ const core = s * s * (3 - 2 * s);
549
+ const rim = k * k * (1 - k) * 4;
550
+ px -= t.pullX * (core - TOUCH.ridge * rim);
551
+ py -= t.pullY * (core - TOUCH.ridge * rim);
552
+ px -= dx * (t.press * TOUCH.pressPull * core);
553
+ py -= dy * (t.press * TOUCH.pressPull * core);
554
+ if (t.waveAmp > 0 && r > 1e-4) {
555
+ const span = t.radius * TOUCH.waveSpan;
556
+ const ring = Math.sin(r / (span * TOUCH.waveLength) - t.wavePhase * VG_TAU) * Math.exp(-r / (span * TOUCH.waveDecay));
557
+ px -= dx / r * ring * t.waveAmp;
558
+ py -= dy / r * ring * t.waveAmp;
559
+ }
560
+ return [px, py];
561
+ }
531
562
 
532
563
  // src/lens-shader.ts
533
564
  var LENS_SHADER = `
@@ -603,51 +634,51 @@ const int VG_FROST_TAPS = 20;
603
634
  const int VG_FROST_TAPS_MAX = 64;
604
635
  // The radius correction for backdrop compression at the rim is capped: at the silhouette it goes
605
636
  // to infinity.
606
- const float VG_FOOTPRINT_MAX = 1.8;
637
+ const float VG_FOOTPRINT_MAX = ${LENS.footprintMax};
607
638
  const float VG_TAU = 6.28318530718;
608
639
  // Ceiling on body lightness under light ink (and the mirrored floor under dark ink). This is a
609
640
  // ceiling, not a difference: "0.14 darker than white" is a lightness of 0.86, at which white text
610
641
  // is invisible.
611
- const float VG_BODY_CAP_LOOSE = 0.62;
612
- const float VG_BODY_CAP_TIGHT = 0.38;
642
+ const float VG_BODY_CAP_LOOSE = ${BODY.capLoose};
643
+ const float VG_BODY_CAP_TIGHT = ${BODY.capTight};
613
644
  // Tint lightness in both directions: real glass is never coal-black or paper-white.
614
- const float VG_TINT_DARK = 0.07;
615
- const float VG_TINT_LIGHT = 0.94;
645
+ const float VG_TINT_DARK = ${BODY.tintDark};
646
+ const float VG_TINT_LIGHT = ${BODY.tintLight};
616
647
  // Reference channel wavelengths, nm \u2014 for diffraction and interference.
617
- const float3 VG_LAMBDA = float3(610.0, 550.0, 460.0);
648
+ const float3 VG_LAMBDA = float3(${LENS.lambdaR}.0, ${LENS.lambdaG}.0, ${LENS.lambdaB}.0);
618
649
  // Film index of refraction: every thin film on glass sits around this value.
619
- const float VG_FILM_IOR = 1.35;
650
+ const float VG_FILM_IOR = ${LENS.filmIor};
620
651
  // Ceiling on the profile's slope right at the silhouette: it goes to infinity there.
621
- const float VG_SLOPE_MAX = 40.0;
652
+ const float VG_SLOPE_MAX = ${LENS.slopeMax}.0;
622
653
  // Fraction of the gather radius that scattering reaches over a busy backdrop under ink (M 11:47).
623
654
  // Set against the reference: there, structure under the capsule fades by a factor of 9-10, not
624
655
  // thirty.
625
- const float VG_SCATTER_MAX = 0.20;
626
- const float VG_SCATTER_BASE = 0.1;
656
+ const float VG_SCATTER_MAX = ${MEDIUM.scatterMax};
657
+ const float VG_SCATTER_BASE = ${MEDIUM.scatterBase};
627
658
  // The response to structure under the glass saturates early: what competes with the ink isn't
628
659
  // the area of foreign text but the mere fact that it's there (a line under a tile gives a busy
629
660
  // of about 0.12).
630
- const float VG_STRUCTURE_GAIN = 20.0;
661
+ const float VG_STRUCTURE_GAIN = ${MEDIUM.structureGain}.0;
631
662
  // How much denser the body with ink gets over a BUSY backdrop than over a calm one. There's no
632
663
  // flat floor here: legibility picks up its own targeted requirement further down, and base
633
664
  // frosting comes from VG_MATTE_LIFT.
634
- const float VG_GROUND_SPAN = 0.06;
665
+ const float VG_GROUND_SPAN = ${BODY.groundSpan};
635
666
  // Scattered light on a frosted element is an ADDITION on top of the backdrop, not a fraction of
636
667
  // the way to the tint: the fraction goes to zero once the canvas reaches the tint's lightness,
637
668
  // and darkens past it (docs/benchmarks.md).
638
- const float VG_MATTE_LIFT = 0.10;
669
+ const float VG_MATTE_LIFT = ${MEDIUM.matteLift};
639
670
  // The fraction of backdrop spread that survives through to the body past scattering: the
640
671
  // legibility requirement is computed from this edge, not from the spot's average lightness.
641
- const float VG_BUSY_EDGE = 0.75;
672
+ const float VG_BUSY_EDGE = ${BODY.busyEdge};
642
673
  // Light concentration: the body is a touch lighter than what's beneath it (M 2:29).
643
- const float VG_CONCENTRATE = 0.01;
674
+ const float VG_CONCENTRATE = ${MEDIUM.concentrate};
644
675
  // The lightness the medium pulls content under the glass toward, and the strength of that pull.
645
676
  // The medium both removes light and mixes in scattered light: over a light backdrop the body
646
677
  // darkens, over a dark one it lightens.
647
- const float VG_MEDIUM_LUMA = 0.40;
648
- const float VG_MEDIUM_PULL = 0.07;
678
+ const float VG_MEDIUM_LUMA = ${MEDIUM.luma};
679
+ const float VG_MEDIUM_PULL = ${MEDIUM.pull};
649
680
  // How much ambient light reaches the body on top of the pull.
650
- const float VG_AMBIENT_SPILL = 0.01;
681
+ const float VG_AMBIENT_SPILL = ${MEDIUM.ambientSpill};
651
682
 
652
683
  float vgLuma(float3 c) { return dot(c, float3(0.2126, 0.7152, 0.0722)); }
653
684
 
@@ -712,8 +743,8 @@ float3 vgInterference(float cosI) {
712
743
 
713
744
  // Edge diffraction: fringes get denser the sharper the bevel. Also a hue, not a brightness.
714
745
  float3 vgDiffraction(float distFromEdge, float bevel) {
715
- float phase = VG_TAU * 4.0 * distFromEdge / max(bevel, 1.0);
716
- float3 d = 0.5 + 0.5 * cos(phase * (550.0 / VG_LAMBDA));
746
+ float phase = VG_TAU * ${SPECTRAL.diffractionFringes}.0 * distFromEdge / max(bevel, 1.0);
747
+ float3 d = 0.5 + 0.5 * cos(phase * (${SPECTRAL.referenceLambda}.0 / VG_LAMBDA));
717
748
  return d / max((d.r + d.g + d.b) / 3.0, 0.001);
718
749
  }
719
750
 
@@ -800,8 +831,8 @@ half4 main(float2 xy) {
800
831
  float spread = max(footprint - 1.0, 0.0) * 0.5 * on;
801
832
 
802
833
  // Dispersion: the same ray with each channel's own index. Blue bends more than red.
803
- float2 dR = vgShift(N, z, u_ior - 0.4 * u_iorSpread) * lens - shift;
804
- float2 dB = vgShift(N, z, u_ior + 0.6 * u_iorSpread) * lens - shift;
834
+ float2 dR = vgShift(N, z, u_ior + ${DISPERSION.redShift} * u_iorSpread) * lens - shift;
835
+ float2 dB = vgShift(N, z, u_ior + ${DISPERSION.blueShift} * u_iorSpread) * lens - shift;
805
836
  float chroma = length(dB - dR);
806
837
 
807
838
  float3 rgb;
@@ -892,9 +923,9 @@ half4 main(float2 xy) {
892
923
  // up, and there's nothing there for direction to depend on. Using the SDF normal instead
893
924
  // painted the body as a cone.
894
925
  float facing = dot(N.xy, L);
895
- float key = u_specular * (pow(max(facing, 0.0), 3.0) + 0.45 * pow(max(-facing, 0.0), 3.0));
926
+ float key = u_specular * (pow(max(facing, 0.0), ${RIM.lobeExponent}.0) + ${RIM.opposingArc} * pow(max(-facing, 0.0), ${RIM.lobeExponent}.0));
896
927
  float rimFacing = dot(n, L);
897
- float rimKey = u_specular * (pow(max(rimFacing, 0.0), 3.0) + 0.45 * pow(max(-rimFacing, 0.0), 3.0));
928
+ float rimKey = u_specular * (pow(max(rimFacing, 0.0), ${RIM.lobeExponent}.0) + ${RIM.opposingArc} * pow(max(-rimFacing, 0.0), ${RIM.lobeExponent}.0));
898
929
  // Wherever the key light doesn't fall, the face reflects its shaded surroundings \u2014 hence the
899
930
  // dark outline.
900
931
  float3 env = refl * (0.55 + 0.45 * min(key, 1.0)) + float3(1.2 * key);
@@ -906,7 +937,7 @@ half4 main(float2 xy) {
906
937
  spectral *= mix(float3(1.0), vgInterference(cosT), u_iridescence);
907
938
  }
908
939
  if (u_diffraction > 0.001) {
909
- float w = u_diffraction * smoothstep(0.45, 1.0, t);
940
+ float w = u_diffraction * smoothstep(${SPECTRAL.diffractionOnset}, 1.0, t);
910
941
  spectral *= mix(float3(1.0), vgDiffraction(e, bevel), w);
911
942
  }
912
943
 
@@ -976,7 +1007,7 @@ half4 main(float2 xy) {
976
1007
  // Where there's no ink, the glass tints away from the backdrop. The direction is chosen by the
977
1008
  // average lightness under the element, not the local spot's lightness: otherwise on a gradient
978
1009
  // the threshold cuts across the body as a diagonal step.
979
- float darkSide = smoothstep(0.42, 0.58, mean);
1010
+ float darkSide = smoothstep(${BODY.darkSideFrom}, ${BODY.darkSideTo}, mean);
980
1011
  float away = mix(VG_TINT_LIGHT, VG_TINT_DARK, darkSide);
981
1012
  float tintLuma = mix(away, mix(VG_TINT_LIGHT, VG_TINT_DARK, pol), demand);
982
1013
 
@@ -988,16 +1019,38 @@ half4 main(float2 xy) {
988
1019
  float inkHi = min(local + edge, 1.0);
989
1020
  float inkLo = max(local - edge, 0.0);
990
1021
  float needForLight = inkHi > capLight
991
- ? clamp((inkHi - capLight) / max(inkHi - VG_TINT_DARK, 1e-4), 0.0, 0.92)
1022
+ ? clamp((inkHi - capLight) / max(inkHi - VG_TINT_DARK, 1e-4), 0.0, ${BODY.maxDemand})
992
1023
  : 0.0;
993
1024
  float needForDark = inkLo < floorDark
994
- ? clamp((floorDark - inkLo) / max(VG_TINT_LIGHT - inkLo, 1e-4), 0.0, 0.92)
1025
+ ? clamp((floorDark - inkLo) / max(VG_TINT_LIGHT - inkLo, 1e-4), 0.0, ${BODY.maxDemand})
995
1026
  : 0.0;
996
1027
  float needForInk = mix(needForDark, needForLight, pol) * demand;
997
1028
 
998
1029
  float ground = structure * u_legibility * VG_GROUND_SPAN;
999
1030
  float density = max(max(u_bodyDensity, ground), needForInk);
1000
1031
 
1032
+ // PRESENCE (\xA73). Over a uniform backdrop there is nothing to refract, and an element that is
1033
+ // only a rim honestly disappears \u2014 right for a piece of background, wrong for a control.
1034
+ //
1035
+ // This used to live on the rim line alone, as lit = max(rimKey, u_presence x 2), while the
1036
+ // DOM path has always implemented it as a floor on the BODY. Two renderers, one field, two
1037
+ // meanings. Measured on Apple's own control over a flat page (frames/verify/m-707), the body
1038
+ // carries 16 levels of separation there and the rim 11.6 \u2014 so the body is where it belongs,
1039
+ // and the rim's reading was the wrong one.
1040
+ // The separation asked for is capped by the range that is left: over a backdrop at 0.96 there
1041
+ // is no room to go 0.05 lighter. And what it is DIVIDED by is the distance to the tint's own
1042
+ // end, not to the tint the body currently sits at \u2014 dividing by the latter explodes wherever
1043
+ // the two are close, which is how the first version of this drove density to its ceiling at
1044
+ // both extremes and took the window down to 7%.
1045
+ bool lighter = local < 0.5;
1046
+ float target = lighter ? min(local + u_presence, 1.0) : max(local - u_presence, 0.0);
1047
+ float separation = abs(target - local);
1048
+ float reached = abs(tintLuma - local) * density;
1049
+ if (u_presence > 0.0 && reached < separation) {
1050
+ float toEnd = abs((lighter ? VG_TINT_LIGHT : VG_TINT_DARK) - local);
1051
+ density = max(density, min(separation / max(toEnd, 1e-4), ${BODY.maxDemand}));
1052
+ }
1053
+
1001
1054
  // Glass has no color of its own \u2014 only the color of what's beneath it (HIG "Color").
1002
1055
  float3 tintHue = mix(float3(1.0), vgHue(wide * 0.5 + ambient * 0.5), u_colorPickup);
1003
1056
  // The element appears by the lens and body building up, not by fading in (M 2:55): at
@@ -1009,7 +1062,7 @@ half4 main(float2 xy) {
1009
1062
  // tone \u2014 deeper over dark, lighter over light \u2014 and the content's texture still shows through
1010
1063
  // the color.
1011
1064
  if (u_accent.a > 0.0) {
1012
- float3 tone = u_accent.rgb * mix(0.88, 1.08, local);
1065
+ float3 tone = u_accent.rgb * mix(${ACCENT.deep}, ${ACCENT.light}, local);
1013
1066
  float3 through = tone * (0.85 + 0.3 * vgLuma(rgb));
1014
1067
  rgb = mix(rgb, clamp(through, float3(0.0), float3(1.0)), u_accent.a * u_appear);
1015
1068
  }
@@ -1045,7 +1098,7 @@ half4 main(float2 xy) {
1045
1098
  // The dark rim is its own layer around the whole silhouette, and the highlight sits ON TOP of
1046
1099
  // it (iOS 27). While the outline used to fade via a (1 \u2212 lit) multiplier, the silhouette read
1047
1100
  // as a single arc: highlight OR shadow.
1048
- rgb *= 1.0 - 0.22 * outline;
1101
+ rgb *= 1.0 - ${RIM.darkEdge} * outline;
1049
1102
  rgb = mix(rgb, mix(refl, float3(1.0), 0.75), line * lit);
1050
1103
 
1051
1104
  if (u_debug > 9.5 && u_debug < 10.5) { rgb = spectral * 0.5; }
@@ -1066,395 +1119,6 @@ half4 main(float2 xy) {
1066
1119
  }
1067
1120
  `;
1068
1121
 
1069
- // src/surface-shader.ts
1070
- var SURFACE_SHADER = `
1071
- uniform shader u_icon;
1072
- /** App-colored content ON the glass \u2014 cover art, a thumbnail. A separate layer from the ink
1073
- * mask: that one is single-channel and colored by polarity, this one carries its own color
1074
- * as-is. */
1075
- uniform shader u_overlay;
1076
-
1077
- uniform float2 u_center;
1078
- uniform float2 u_halfSize;
1079
- uniform float u_corner;
1080
- uniform float u_bevel;
1081
- uniform float u_thickness;
1082
- uniform float2 u_morphOffset;
1083
- uniform float2 u_morphHalf;
1084
- uniform float u_morphCorner;
1085
- uniform float u_morphK;
1086
- uniform float2 u_morph2Offset;
1087
- uniform float2 u_morph2Half;
1088
- uniform float u_morph2Corner;
1089
-
1090
- uniform float u_press;
1091
- uniform float u_active;
1092
- /* 0 \u2014 the element sinks in under the finger, 1 \u2014 it rises into glass (reference \xA75). */
1093
- uniform float u_lift;
1094
-
1095
- uniform float u_edgeDensity;
1096
- uniform float u_dispersion;
1097
- uniform float u_refraction;
1098
- uniform float4 u_tint;
1099
- uniform float u_shadow;
1100
- uniform float u_shadowReach;
1101
- uniform float3 u_ambient;
1102
- uniform float u_presence;
1103
- uniform float u_progress;
1104
- uniform float u_appear;
1105
- uniform float u_debug;
1106
-
1107
- uniform float u_iconOn;
1108
- uniform float u_overlayOn;
1109
- uniform float u_iconScale;
1110
- uniform float4 u_inkIdle;
1111
- uniform float4 u_inkActive;
1112
- uniform float2 u_touch;
1113
- uniform float2 u_pull;
1114
- uniform float u_touchPress;
1115
- uniform float u_touchRadius;
1116
- uniform float2 u_wave;
1117
-
1118
- ${VG_SDF}
1119
-
1120
- const float VG_FALLOFF = ${VG_FALLOFF};
1121
-
1122
- // Tint density in the flat middle; at the bevel it's multiplied by u_edgeDensity.
1123
- const float VG_BODY_DENSITY = 0.19;
1124
- /* Fraction of the ambient hue in the shadow. The shadow has to stay a shadow, not a colored
1125
- * blob. */
1126
- const float VG_SHADOW_TINT = 1.0;
1127
- // How much weaker the shadow is right at the outline than under the middle of the gap, and what
1128
- // fraction of the offset it takes to reach full depth. In the reference the shadow's minimum
1129
- // sits below the rim, not right at it.
1130
- const float VG_GAP_LIGHT = 0.76;
1131
- const float VG_GAP_REACH = 0.20;
1132
- /* Depth of the ink beneath the surface, dp. The normal displaces it by exactly this much right
1133
- * at the rim. */
1134
- const float VG_INK_DEPTH = 4.0;
1135
- /* Ink defocus under the finger (reference \xA76): a glyph loses its edge and drowns in milk. Given
1136
- * as a FRACTION of the touch blob, not a number: the blob already arrives in device units, so
1137
- * the radius automatically follows both screen density and the element's size. At rest it's
1138
- * zero, and the sample stays a single one. */
1139
- const float VG_INK_DEFOCUS = 0.08;
1140
-
1141
- half4 vgPack(half3 c, float a) { return half4(c * half(a), half(a)); }
1142
-
1143
- // What bleeds into the shadow is the COLOR of the surroundings, not its brightness: the hue's own
1144
- // lightness is subtracted out of it, otherwise the shadow pales and loses depth instead of
1145
- // warming up. Strength is set by how much light there is around: over a nearly black backdrop
1146
- // there's nothing to bleed into the shadow.
1147
- float3 vgShadowTint(float3 ambient) {
1148
- float peak = max(max(ambient.r, ambient.g), max(ambient.b, 0.001));
1149
- float3 hue = ambient / peak;
1150
- float lumaWeights = dot(ambient, float3(0.2126, 0.7152, 0.0722));
1151
- return (hue - dot(hue, float3(0.2126, 0.7152, 0.0722))) * lumaWeights;
1152
- }
1153
-
1154
- half3 vgHeat(float v) {
1155
- float x = clamp(v, 0.0, 1.0);
1156
- return half3(half(clamp(x * 2.2 - 0.2, 0.0, 1.0)),
1157
- half(clamp(1.0 - abs(x - 0.5) * 2.2, 0.0, 1.0)),
1158
- half(clamp(1.2 - x * 2.4, 0.0, 1.0)));
1159
- }
1160
-
1161
- half4 main(float2 xy) {
1162
- float2 p = vgTouchWarp(xy - u_center, u_touch, u_pull, u_touchPress, u_touchRadius, u_wave.x, u_wave.y);
1163
-
1164
- // Inverse deformation: stretch A along the pull vector, compression 1/sqrt(A) across it. This
1165
- // exact law mirrors the transform of the live backdrop under the canvas, otherwise the two
1166
- // would drift apart. There's NO motion geometry here \u2014 no pull, no press-driven bulge. All of
1167
- // that is done by a single wrapper transform, the same one that carries the native lens: the
1168
- // shader doesn't touch it, and two pipelines driving one motion would drift apart by a frame,
1169
- // making the layers visible as separate.
1170
-
1171
- float halfMin = max(min(u_halfSize.x, u_halfSize.y), 1.0);
1172
- float bevel = max(u_bevel, 1.0);
1173
-
1174
- float sd = vgScene(p, u_halfSize, u_corner, u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,
1175
- u_morph2Offset, u_morph2Half, u_morph2Corner);
1176
- float t = vgBevelT(sd, bevel);
1177
- float2 n = vgSceneNormal(p, u_halfSize, u_corner, u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,
1178
- u_morph2Offset, u_morph2Half, u_morph2Corner);
1179
- float3 N = normalize(float3(n * vgBevelSlope(t), 1.0));
1180
-
1181
- // Progress is an active state turned into a field: the played fraction shines and glows
1182
- // exactly as much as an active element shines whole. This does NOT touch ink: recoloring text
1183
- // as the track advances would mean changing it mid-word.
1184
- float lit = max(u_active, vgProgress(p, u_halfSize, u_progress));
1185
-
1186
- // The highlight and the rim are computed by the lens: reflection is a function of the
1187
- // surroundings, and only the lens can see them.
1188
-
1189
- if (u_debug > 0.5) {
1190
- float inMask = 1.0 - smoothstep(-1.0, 1.0, sd);
1191
- if (u_debug < 1.5) {
1192
- float band = abs(fract(sd / (halfMin * 0.22)) - 0.5) * 2.0;
1193
- half3 c = sd < 0.0 ? half3(0.20, 0.62, 1.0) : half3(1.0, 0.42, 0.22);
1194
- return vgPack(c * half(0.25 + band * 0.75), 1.0);
1195
- }
1196
- if (u_debug < 2.5) { return vgPack(half3(1.0), inMask); }
1197
- if (u_debug < 3.5) { return vgPack(vgHeat(t), inMask); }
1198
- // Fresnel now lives in the lens; here we show its SHAPE \u2014 how grazing the view is.
1199
- if (u_debug < 4.5) { return vgPack(vgHeat(1.0 - clamp(N.z, 0.0, 1.0)), inMask); }
1200
- if (u_debug < 5.5) {
1201
- float push = pow(t, VG_FALLOFF) * u_refraction;
1202
- return vgPack(vgHeat(push), inMask);
1203
- }
1204
- if (u_debug < 6.5) { return half4(0.0); }
1205
- if (u_debug < 7.5) { return half4(0.0); }
1206
- // Dispersion is computed by the lens; here is the field it grows along.
1207
- if (u_debug < 8.5) { return vgPack(vgHeat(u_dispersion * t * t), inMask); }
1208
- if (u_debug < 9.5) { return vgPack(half3(N * 0.5 + 0.5), inMask); }
1209
- // spectral and adapt are shown by the LENS \u2014 the surface has to get out of the way.
1210
- return half4(0.0);
1211
- }
1212
-
1213
- // The shadow and the halo live OUTSIDE the shape: inside, their place is taken by the glass
1214
- // itself. Separation from the content rests entirely on the shadow \u2014 without it the surface
1215
- // would sit ON the picture, not above it. The shadow only lives at the rim and outside it: at
1216
- // sd < \u22121 the outside multiplier is already zero. Computing it deep inside the shape would be
1217
- // a wasted FULL SDF evaluation for every such pixel, and the body covers almost the whole
1218
- // area. The branch here is on the coordinate, but only the threads right at the rim actually
1219
- // diverge.
1220
- float shade = 0.0;
1221
- float halo = 0.0;
1222
- if (sd > -1.0) {
1223
- float outside = smoothstep(-1.0, 1.0, sd);
1224
- // Under the finger a button moves TOWARD the backdrop, and the shadow tightens: it's exactly
1225
- // the gap between the two. A control rising into glass (a switch knob, a slider handle \u2014
1226
- // reference \xA75) moves AWAY from it, and then the shadow spreads out instead. Only the app
1227
- // knows which one applies, so the direction arrives as a fraction: 0 is sinking in, the way
1228
- // every element behaved before this rule existed.
1229
- float reach = u_shadowReach * (1.0 + mix(-0.25, 0.55, u_lift) * u_press);
1230
- // The shadow is soft and wide, shifted downward (M 11:58): it's what holds the element's
1231
- // separation from the content.
1232
- float sdDrop = vgScene(p - float2(0.0, reach * 0.35), u_halfSize, u_corner,
1233
- u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,
1234
- u_morph2Offset, u_morph2Half, u_morph2Corner);
1235
- float amb = 1.0 - smoothstep(-reach * 0.3, reach, sdDrop);
1236
- // THE GAP UNDER A RAISED ELEMENT IS LIT: right at the outline the shadow is WEAKER than
1237
- // further down. Otherwise the minimum sits right against the silhouette \u2014 both parts of the
1238
- // shadow are monotonic in distance.
1239
- float gap = smoothstep(0.0, max(reach * VG_GAP_REACH, 1.0), max(sd, 0.0));
1240
- shade = amb * amb * 0.13 * mix(VG_GAP_LIGHT, 1.0, gap) * outside * u_shadow * u_appear;
1241
- halo = 1.0 - smoothstep(0.0, reach * 0.30, max(sd, 0.0));
1242
- halo = halo * halo * lit * 0.10 * outside * u_appear;
1243
- }
1244
-
1245
- if (sd > 1.0) {
1246
- // Ambient light bleeds into the shadow (219 @8:22): it isn't a black hole, it's tinted by
1247
- // whatever lies nearby. Otherwise an element over colored content would hang over a gray
1248
- // blob.
1249
- half3 spill = max(half3(half(halo)) + half3(vgShadowTint(u_ambient)) * half(VG_SHADOW_TINT * shade * (1.0 - halo)), half3(0.0));
1250
- return half4(spill, half(halo + shade * (1.0 - halo)));
1251
- }
1252
-
1253
- float density = u_tint.w;
1254
- float inner = clamp(-sd / halfMin, 0.0, 1.0);
1255
- half3 col = half3(u_tint.rgb);
1256
-
1257
- // The tint is LIGHT and weak, not dark: dark glass over dark content disappears, and has to be
1258
- // held together with a heavy rim \u2014 which makes the surface read as a chrome bead. Tint density
1259
- // at the bevel is a separate value: physically the bevel bends light harder, but it doesn't
1260
- // get hazier. Coupled together, the two produced a milky ring around the whole outline.
1261
- float body = mix(VG_BODY_DENSITY, VG_BODY_DENSITY * u_edgeDensity, smoothstep(0.10, 0.62, t)) * density;
1262
- float vignette = smoothstep(0.15, 1.0, inner) * 0.19 * density;
1263
- // There's NO density boost on press here. It was meant as "the element stands out more under
1264
- // the finger," but it pulls the body toward the tint, and the tint depends on polarity: over a
1265
- // light backdrop (dark polarity) press ended up DARKENING the element. Presence is shown
1266
- // instead by the vgTouchWarp field's deformation and the highlight blooming below \u2014 neither
1267
- // cares about the sign of polarity.
1268
- float a = max(body, vignette);
1269
-
1270
- // Active state does NOT touch the body's color. Mixing in a fixed gray here flipped the
1271
- // effect's sign depending on the backdrop: over dark the element lightened, over light it
1272
- // darkened, for the exact same state. Active state is shown instead by the highlight and rim
1273
- // lighting above: neither cares about the backdrop.
1274
-
1275
- // THE ICON SITS BENEATH THE SURFACE, it isn't stuck on top of it: it used to be mixed in last,
1276
- // over the highlight, and read as a flat sticker on volumetric glass. At the rim the normal
1277
- // displaces it, like everything seen through the glass, while the highlight sits ON TOP \u2014 it
1278
- // lives on the surface itself.
1279
- //
1280
- // There is NO shadow under the icon here. It used to be made from the difference of two offset
1281
- // mask samples and produced a second outline along the edge \u2014 the icon's borders looked
1282
- // ragged.
1283
- // THE INK'S SHIFT IS SET BY ITS OWN DEPTH, NOT BY BEVEL WIDTH. The ink sits right at the
1284
- // surface, and it's displaced by exactly the thin layer of glass above it \u2014 not by how wide a
1285
- // bevel this particular piece of glass happens to have. It used to be a fraction of the bevel:
1286
- // on thin glass the shift came out to 4 dp and everything lined up, but on thick glass it was
1287
- // 8, and cover art inset from the edge by 6 stretched toward the rim and spilled past its
1288
- // bounds. The ceiling is absolute: ink has one depth regardless of the glass.
1289
- float2 inkShift = n * (min(u_bevel * 0.5, VG_INK_DEPTH) * t);
1290
- float2 inkUv = u_center + p - inkShift;
1291
-
1292
- // INK LOSES SHARPNESS UNDER THE FINGER (reference \xA76): in the reference, a glyph under
1293
- // pressure drowns in milk rather than simply getting lighter. This branch is uniform across
1294
- // the whole element: press arrives as a uniform, so defocus costs nothing while the element
1295
- // isn't being touched.
1296
- float defocus = VG_INK_DEFOCUS * u_touchRadius * u_touchPress;
1297
- float2 inkDx = float2(defocus, 0.0);
1298
- float2 inkDy = float2(0.0, defocus);
1299
-
1300
- half4 ink;
1301
- half4 over;
1302
- if (defocus > 0.01) {
1303
- // A cross around the center with double weight in the middle: the ink mask is
1304
- // high-contrast, and four samples are enough for a stroke to stop holding its edge.
1305
- ink = (u_icon.eval((inkUv - inkDx) * u_iconScale)
1306
- + u_icon.eval((inkUv + inkDx) * u_iconScale)
1307
- + u_icon.eval((inkUv - inkDy) * u_iconScale)
1308
- + u_icon.eval((inkUv + inkDy) * u_iconScale)
1309
- + u_icon.eval(inkUv * u_iconScale) * 2.0) * half(u_iconOn / 6.0);
1310
- // Colored content lives in the same plane as the ink and blurs together with it: different
1311
- // sharpness for two layers in one plane would read as a defect, not a press.
1312
- over = (u_overlay.eval((inkUv - inkDx) * u_iconScale)
1313
- + u_overlay.eval((inkUv + inkDx) * u_iconScale)
1314
- + u_overlay.eval((inkUv - inkDy) * u_iconScale)
1315
- + u_overlay.eval((inkUv + inkDy) * u_iconScale)
1316
- + u_overlay.eval(inkUv * u_iconScale) * 2.0) * half(u_overlayOn * u_appear / 6.0);
1317
- } else {
1318
- ink = u_icon.eval(inkUv * u_iconScale) * half(u_iconOn);
1319
- over = u_overlay.eval(inkUv * u_iconScale) * half(u_overlayOn * u_appear);
1320
- }
1321
-
1322
- // The backing under the ink is held by the LENS (body density from the probe), not the
1323
- // surface: legibility is a property of the material, and computing it here a second time
1324
- // would give two different answers to one question.
1325
-
1326
- // Layers are composited premultiplied-over: in straight alpha, a nearly transparent dark body
1327
- // used to pull the color of semi-transparent ink and the finger's light toward itself \u2014 letter
1328
- // edges grayed out, pressing darkened things.
1329
- half3 pm = col * half(a);
1330
- half inkA = ink.g * half(mix(0.82, 1.0, u_active) * u_appear);
1331
- half3 inkCol = mix(half3(u_inkIdle.rgb), half3(u_inkActive.rgb), half(u_active));
1332
- pm = pm * (1.0 - inkA) + inkCol * inkA;
1333
- a = a + float(inkA) * (1.0 - a);
1334
-
1335
- // COLORED CONTENT LIVES IN THE SAME PLACE AS THE INK \u2014 inside the material, at the same
1336
- // coordinate. Otherwise deformation would carry them apart: on press, the title and artist
1337
- // name would shake together with the surface while the cover art stayed put, because it used
1338
- // to be a separate layer on top of the glass. Polarity doesn't touch it \u2014 it has its own
1339
- // color, and there's nothing to swap it for.
1340
- pm = pm * (1.0 - over.a) + over.rgb * over.a;
1341
- a = a + float(over.a) * (1.0 - a);
1342
-
1343
- // Light from the finger is gathered by the LENS: it's a concentration of the surroundings, not
1344
- // the glass's own whiteness. The surface is left with only what the glass spills outward \u2014 the
1345
- // halo above.
1346
-
1347
- float mask = 1.0 - smoothstep(-1.0, 1.0, sd);
1348
- pm = clamp(pm, half3(0.0), half3(1.0)) * half(mask);
1349
- a *= mask;
1350
- pm = max(pm + half3(vgShadowTint(u_ambient)) * half(VG_SHADOW_TINT * shade * (1.0 - a)), half3(0.0));
1351
- return half4(pm, half(a + shade * (1.0 - a)));
1352
- }
1353
- `;
1354
-
1355
- // src/reference-scene.ts
1356
- var REFERENCE_SHAPES = {
1357
- circle: circleGeometry(120),
1358
- capsule: capsuleGeometry(300, 110),
1359
- tile: roundedRectGeometry(220, 120, 32)
1360
- };
1361
- var REFERENCE_SCENE_WIDTH = 336;
1362
- var REFERENCE_SCENE_HEIGHT = 252;
1363
- var REFERENCE_SURROUND = "#3c1f4a";
1364
- var REFERENCE_PIECE_AT = {
1365
- xDp: REFERENCE_SCENE_WIDTH / 2,
1366
- yDp: REFERENCE_SCENE_HEIGHT / 2
1367
- };
1368
- var whole = (layer) => [
1369
- { heightDp: REFERENCE_SCENE_HEIGHT, layer }
1370
- ];
1371
- var stripeLevel = (level) => level < 0.5 ? level + 0.22 : level - 0.22;
1372
- var REFERENCE_SCENES = [
1373
- {
1374
- name: "flat",
1375
- what: "the body standing off the backdrop: the element must be visible over a flat canvas",
1376
- level: 0.5,
1377
- bands: (level) => whole({ kind: "flat", level })
1378
- },
1379
- {
1380
- name: "stripes",
1381
- what: "the window: the brightness range inside the element is a noticeable fraction of the range outside it",
1382
- level: 0.5,
1383
- bands: (level) => whole({ kind: "stripes", level, other: stripeLevel(level), periodDp: 24, widthDp: 10 })
1384
- },
1385
- {
1386
- name: "busy",
1387
- what: "ink against content: both the ink and whatever is under the glass have to live over a busy cover",
1388
- level: 0.5,
1389
- bands: (level) => whole({ kind: "checker", level, amp: 0.28, cellDp: 16 })
1390
- },
1391
- {
1392
- name: "bar",
1393
- what: "the rim gathers content: at the silhouette the lens bulges out whatever sits behind it",
1394
- level: 0.93,
1395
- bands: (level) => whole({ kind: "bar", level, barLevel: 0.12, thicknessDp: 20 })
1396
- },
1397
- {
1398
- name: "steps",
1399
- what: "the body profile at five levels at once \u2014 platforms are compared against it",
1400
- level: 0.5,
1401
- bands: () => [
1402
- { heightDp: 36, layer: { kind: "flat", level: 0.1 } },
1403
- { heightDp: 36, layer: { kind: "flat", level: 0.3 } },
1404
- { heightDp: 36, layer: { kind: "flat", level: 0.5 } },
1405
- { heightDp: 36, layer: { kind: "flat", level: 0.69 } },
1406
- { heightDp: 36, layer: { kind: "flat", level: 0.9 } },
1407
- { heightDp: 36, layer: { kind: "step", left: 0.05, right: 0.28 } },
1408
- { heightDp: 36, layer: { kind: "bar", level: 0.92, barLevel: 0.12, thicknessDp: 6 } }
1409
- ]
1410
- },
1411
- {
1412
- name: "edge",
1413
- what: "tint at a hard break: one half under the glass is black, the other white",
1414
- level: 0.5,
1415
- bands: () => whole({ kind: "step", left: 0.03, right: 0.95 })
1416
- },
1417
- {
1418
- name: "gradient",
1419
- what: "ink polarity: where the automatic switch flips the ink from light to dark",
1420
- level: 0.5,
1421
- bands: () => whole({ kind: "gradient", from: 0.04, to: 0.96, steps: 28 })
1422
- },
1423
- {
1424
- name: "grid",
1425
- what: "lens distortion: drift, tearing and jitter of a straight line show up immediately (by eye, not by number)",
1426
- level: 0.91,
1427
- measurable: false,
1428
- bands: (level) => whole({ kind: "grid", level, lineLevel: level > 0.5 ? level - 0.14 : level + 0.14, stepDp: 12 })
1429
- }
1430
- ];
1431
- var referenceScene = (name) => {
1432
- const scene = REFERENCE_SCENES.find((s) => s.name === name);
1433
- if (!scene) throw new Error(`vireglass: no reference canvas "${name}"`);
1434
- return scene;
1435
- };
1436
- var refGray = (level) => {
1437
- const v = Math.round(Math.min(Math.max(level, 0), 1) * 255).toString(16).padStart(2, "0");
1438
- return `#${v}${v}${v}`;
1439
- };
1440
- var refCheckerCells = (layer, widthDp, heightDp) => {
1441
- const cells = [];
1442
- const lo = layer.level - layer.amp;
1443
- const hi = layer.level + layer.amp;
1444
- let seed = 1;
1445
- for (let y = 0; y < heightDp; y += layer.cellDp) {
1446
- for (let x = 0; x < widthDp; x += layer.cellDp) {
1447
- seed = seed * 1103515245 + 12345 & 2147483647;
1448
- cells.push({ xDp: x, yDp: y, level: (seed >> 16) % 2 ? hi : lo });
1449
- }
1450
- }
1451
- return cells;
1452
- };
1453
- var refGradientSteps = (layer) => Array.from(
1454
- { length: layer.steps },
1455
- (_, i) => layer.from + (layer.to - layer.from) * i / Math.max(layer.steps - 1, 1)
1456
- );
1457
-
1458
1122
  // src/adapters.ts
1459
1123
  var NO_TOUCH = {
1460
1124
  x: 0,
@@ -1468,7 +1132,7 @@ var NO_TOUCH = {
1468
1132
  };
1469
1133
  var NO_MORPH = { offsetX: 0, offsetY: 0, width: 0, height: 0, cornerRadius: 0, smoothing: 0 };
1470
1134
  var NO_PROGRESS = -1;
1471
- var ACCENT_AMOUNT = 0.8;
1135
+ var ACCENT_AMOUNT = ACCENT.amount;
1472
1136
  var NO_ACCENT = [0, 0, 0, 0];
1473
1137
  var lensMagnify = (o) => 1 + (o.refractionScale - 1) * o.refraction;
1474
1138
  var shapes = /* @__PURE__ */ new Map();
@@ -1638,7 +1302,13 @@ var REST_LIGHT = [-0.577, -0.817];
1638
1302
  var DYNAMIC_UNIFORMS = ["u_press", "u_active"];
1639
1303
  var ICON_UNIFORMS = ["u_iconOn", "u_iconScale", "u_inkIdle", "u_inkActive"];
1640
1304
  var OVERLAY_UNIFORMS = ["u_overlayOn"];
1641
- var MORPH_NECK = 0.35;
1305
+ function neckFor(half, aw, ah, bw, bh, offsetX, offsetY) {
1306
+ const gx = Math.abs(offsetX) - (aw + bw) / 2;
1307
+ const gy = Math.abs(offsetY) - (ah + bh) / 2;
1308
+ const gap = Math.hypot(Math.max(gx, 0), Math.max(gy, 0));
1309
+ const widest = Math.min(aw, ah, bw, bh);
1310
+ return Math.min(Math.max(half * MORPH.neck, neckToBridge(gap) * MORPH.fuse), widest);
1311
+ }
1642
1312
  function morphBetween(a, b, offsetX, offsetY, t) {
1643
1313
  if (t <= 0) return void 0;
1644
1314
  const half = Math.min(halfMinDp(a), halfMinDp(b));
@@ -1648,94 +1318,28 @@ function morphBetween(a, b, offsetX, offsetY, t) {
1648
1318
  width: b.width,
1649
1319
  height: b.height,
1650
1320
  cornerRadius: b.cornerRadius,
1651
- smoothing: half * MORPH_NECK * Math.min(t, 1)
1321
+ smoothing: neckFor(half, a.width, a.height, b.width, b.height, offsetX, offsetY) * Math.min(t, 1)
1652
1322
  };
1653
1323
  }
1654
-
1655
- // src/targets/glsl.ts
1656
- function sizeUniformName(sampler) {
1657
- return sampler.startsWith("u_") ? `${sampler}Size` : `u_${sampler}Size`;
1658
- }
1659
- function convertVecTypes(src) {
1660
- return src.replace(/\bfloat([234])\b/g, "vec$1");
1661
- }
1662
- function convertHalfTypes(src) {
1663
- return src.replace(/\bhalf([234])\b/g, "vec$1").replace(/\bhalf\b/g, "float");
1664
- }
1665
- function convertShaderUniforms(src) {
1666
- return src.replace(/uniform\s+shader\s+(\w+)\s*;/g, (_match, name) => {
1667
- return `uniform sampler2D ${name};
1668
- uniform vec2 ${sizeUniformName(name)};`;
1669
- });
1670
- }
1671
- function convertEvalCalls(src) {
1672
- const samplers = /* @__PURE__ */ new Set();
1673
- const declRe = /uniform\s+sampler2D\s+(\w+)\s*;/g;
1674
- for (let m = declRe.exec(src); m; m = declRe.exec(src)) samplers.add(m[1]);
1675
- if (samplers.size === 0) return src;
1676
- const callRe = /(\w+)\.eval\(/g;
1677
- let out = "";
1678
- let cursor = 0;
1679
- for (let m = callRe.exec(src); m; m = callRe.exec(src)) {
1680
- const name = m[1];
1681
- if (!samplers.has(name)) continue;
1682
- const argStart = m.index + m[0].length;
1683
- let depth = 1;
1684
- let i = argStart;
1685
- while (i < src.length && depth > 0) {
1686
- if (src[i] === "(") depth += 1;
1687
- else if (src[i] === ")") depth -= 1;
1688
- i += 1;
1689
- }
1690
- const args = src.slice(argStart, i - 1);
1691
- out += src.slice(cursor, m.index);
1692
- out += `texture(${name}, (${args}) / ${sizeUniformName(name)})`;
1693
- cursor = i;
1694
- callRe.lastIndex = i;
1695
- }
1696
- out += src.slice(cursor);
1697
- return out;
1698
- }
1699
- function convertReturns(mainBody) {
1700
- return mainBody.replace(/\breturn\s+([^;]+);/g, "fragColor = $1; return;");
1701
- }
1702
- var ENTRY_RE = /half4\s+main\s*\(\s*float2\s+(\w+)\s*\)\s*\{/;
1703
- function convertEntryPoint(src) {
1704
- const match = ENTRY_RE.exec(src);
1705
- if (!match) {
1706
- throw new Error('vireglass/targets/glsl: entry point "half4 main(float2 xy)" not found');
1707
- }
1708
- const bodyStart = match.index + match[0].length;
1709
- let depth = 1;
1710
- let i = bodyStart;
1711
- while (i < src.length && depth > 0) {
1712
- if (src[i] === "{") depth += 1;
1713
- else if (src[i] === "}") depth -= 1;
1714
- i += 1;
1715
- }
1716
- const bodyEnd = i - 1;
1717
- const param = match[1];
1718
- const body = convertReturns(src.slice(bodyStart, bodyEnd));
1719
- const head = src.slice(0, match.index);
1720
- const tail = src.slice(i);
1721
- return `${head}void main() {
1722
- vec2 ${param} = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y);
1723
- ${body}}${tail}`;
1724
- }
1725
- function addPrologue(src) {
1726
- return "#version 300 es\nprecision highp float;\n\nout vec4 fragColor;\nuniform vec2 u_resolution;\n\n" + src;
1727
- }
1728
- function toGLSL(shaderSource) {
1729
- let out = shaderSource;
1730
- out = convertEntryPoint(out);
1731
- out = convertShaderUniforms(out);
1732
- out = convertEvalCalls(out);
1733
- out = convertVecTypes(out);
1734
- out = convertHalfTypes(out);
1735
- out = addPrologue(out);
1736
- return out;
1324
+ function morphOutOf(source, target, offsetX, offsetY, t) {
1325
+ if (t <= 0) return void 0;
1326
+ const k = Math.min(t, 1);
1327
+ const width = source.width + (target.width - source.width) * k;
1328
+ const height = source.height + (target.height - source.height) * k;
1329
+ const cornerRadius = source.cornerRadius + (target.cornerRadius - source.cornerRadius) * k;
1330
+ const half = Math.min(halfMinDp(source), Math.min(width, height) / 2);
1331
+ return {
1332
+ offsetX: offsetX * k,
1333
+ offsetY: offsetY * k,
1334
+ width,
1335
+ height,
1336
+ cornerRadius,
1337
+ // The lobe travels as it grows, so the span changes every frame and the neck is measured
1338
+ // against where the lobe actually is, not against where it will end up.
1339
+ smoothing: neckFor(half, source.width, source.height, width, height, offsetX * k, offsetY * k)
1340
+ };
1737
1341
  }
1738
1342
 
1739
- export { ACCENT_AMOUNT, ADAPT_RADIUS, ALL_EFFECTS_ON, DEBUG_MODES, DYNAMIC_UNIFORMS, EFFECTS, FRESNEL_EXPONENT, ICON_UNIFORMS, LEGACY_NAMES, LEGACY_OPTICS, LENS_SHADER, MATERIAL_PRESETS, MATERIAL_RANGES, MAX_BEVEL_FRACTION, MAX_STRETCH, OVERLAY_UNIFORMS, PRESET_NAMES, PRODUCT_DIM, REFERENCE_PIECE_AT, REFERENCE_SCENES, REFERENCE_SCENE_HEIGHT, REFERENCE_SCENE_WIDTH, REFERENCE_SHAPES, REFERENCE_SURROUND, REST_LIGHT, RIM_FRACTION, SURFACE_LENGTH_UNIFORMS, SURFACE_SHADER, VG_FALLOFF, VG_SDF, VIREGLASS_CLEAR_MATERIAL, VIREGLASS_CONTROL_MATERIAL, VIREGLASS_LYRICS_MATERIAL, VIREGLASS_MATERIAL, VIREGLASS_MATERIAL_V4, VIREGLASS_MATERIAL_V5, VIREGLASS_SHEET_MATERIAL, absorption, activeMaterial, addPrologue, applyToggles, bevelDp, bevelFraction, blur, bodyDensity, capsuleGeometry, circleGeometry, colorPickup, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, debugIndex, diffraction, dispersion, edgeDensity, edgeLight, fresnelF0, fresnelStrength, gatherRadius, halfMinDp, iorSpread, iridescence, lensMagnify, lensPadDp, materialForInk, mediumTint, morphBetween, morphReachDp, refCheckerCells, refGradientSteps, refGray, referenceScene, refractionScale, refractionStrength, resolveMaterial, resolveOptics, rimDp, roundedRectGeometry, shadowOpacity, shadowOpacityFrom, shadowReachDp, sizeGain, specularPower, specularStrength, surfacePadDp, thicknessDp, toDeviceSurfaceUniforms, toGLSL, toLensProps, toSurfaceUniforms };
1740
- //# sourceMappingURL=chunk-HF2AWZEZ.js.map
1741
- //# sourceMappingURL=chunk-HF2AWZEZ.js.map
1343
+ export { ACCENT_AMOUNT, ADAPT_RADIUS, ALL_EFFECTS_ON, DEBUG_MODES, DYNAMIC_UNIFORMS, EFFECTS, FRESNEL_EXPONENT, ICON_UNIFORMS, LEGACY_NAMES, LEGACY_OPTICS, LENS_SHADER, MATERIAL_PRESETS, MATERIAL_RANGES, OVERLAY_UNIFORMS, PRESET_NAMES, PRODUCT_DIM, REST_LIGHT, SURFACE_LENGTH_UNIFORMS, VG_FALLOFF, VG_SDF, VIREGLASS_CLEAR_MATERIAL, VIREGLASS_CONTROL_MATERIAL, VIREGLASS_LYRICS_MATERIAL, VIREGLASS_MATERIAL, VIREGLASS_MATERIAL_V4, VIREGLASS_MATERIAL_V5, VIREGLASS_SHEET_MATERIAL, absorption, activeMaterial, applyToggles, blur, bodyDensity, colorPickup, debugIndex, diffraction, dispersion, edgeDensity, edgeLight, fresnelF0, fresnelStrength, gatherRadius, iorSpread, iridescence, lensMagnify, materialForInk, mediumTint, morphBetween, morphOutOf, neckToBridge, refractionScale, refractionStrength, resolveMaterial, resolveOptics, sceneDistance, sceneGradient, sdfRoundedRect, sdfRoundedRectGradient, smin, specularPower, specularStrength, toDeviceSurfaceUniforms, toLensProps, toSurfaceUniforms, touchWarp };
1344
+ //# sourceMappingURL=chunk-IQOBZGCF.js.map
1345
+ //# sourceMappingURL=chunk-IQOBZGCF.js.map