vireglass 2.3.0 → 3.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 (61) hide show
  1. package/README.md +37 -6
  2. package/dist/{adapters-DugC6Qri.d.cts → adapters--bXcGvhW.d.cts} +5 -3
  3. package/dist/{adapters-BfeAopGO.d.ts → adapters-BlMhc6pI.d.ts} +5 -3
  4. package/dist/{chunk-MMPK6DCK.js → chunk-CPSIY2DZ.js} +58 -8
  5. package/dist/chunk-CPSIY2DZ.js.map +1 -0
  6. package/dist/{chunk-PM2SWCSF.js → chunk-RWUR3OV6.js} +26 -6
  7. package/dist/chunk-RWUR3OV6.js.map +1 -0
  8. package/dist/{chunk-CTTZNZFV.js → chunk-VLDD655U.js} +3 -3
  9. package/dist/{chunk-CTTZNZFV.js.map → chunk-VLDD655U.js.map} +1 -1
  10. package/dist/{chunk-H5QTBF67.js → chunk-YAN2OK4H.js} +31 -38
  11. package/dist/chunk-YAN2OK4H.js.map +1 -0
  12. package/dist/{chunk-SLRPHDV7.js → chunk-Z4FKYY47.js} +3 -3
  13. package/dist/{chunk-SLRPHDV7.js.map → chunk-Z4FKYY47.js.map} +1 -1
  14. package/dist/dom.cjs +180 -55
  15. package/dist/dom.cjs.map +1 -1
  16. package/dist/dom.d.cts +72 -25
  17. package/dist/dom.d.ts +72 -25
  18. package/dist/dom.js +116 -54
  19. package/dist/dom.js.map +1 -1
  20. package/dist/{geometry-RhKd7CM5.d.cts → geometry-ip9WSlZv.d.cts} +1 -1
  21. package/dist/{geometry-GvXtt-_S.d.ts → geometry-pabkYV7w.d.ts} +1 -1
  22. package/dist/{group-model-z4N27qsC.d.ts → group-model-CQiI5cCe.d.ts} +1 -1
  23. package/dist/{group-model-DwtixGhG.d.cts → group-model-DX2_W9J_.d.cts} +1 -1
  24. package/dist/index.cjs +114 -41
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +13 -8
  27. package/dist/index.d.ts +13 -8
  28. package/dist/index.js +5 -5
  29. package/dist/law.cjs +56 -6
  30. package/dist/law.cjs.map +1 -1
  31. package/dist/law.d.cts +54 -4
  32. package/dist/law.d.ts +54 -4
  33. package/dist/law.js +1 -1
  34. package/dist/{material-CnUmdKg1.d.ts → material-D9m1EN47.d.cts} +9 -6
  35. package/dist/{material-CnUmdKg1.d.cts → material-D9m1EN47.d.ts} +9 -6
  36. package/dist/react.d.cts +1 -1
  37. package/dist/react.d.ts +1 -1
  38. package/dist/reference.d.cts +2 -2
  39. package/dist/reference.d.ts +2 -2
  40. package/dist/reference.js +2 -2
  41. package/dist/web/index.cjs +112 -40
  42. package/dist/web/index.cjs.map +1 -1
  43. package/dist/web/index.d.cts +3 -3
  44. package/dist/web/index.d.ts +3 -3
  45. package/dist/web/index.js +4 -4
  46. package/docs/dom.md +49 -3
  47. package/docs/law.md +1 -1
  48. package/package.json +1 -1
  49. package/src/adapters.ts +3 -1
  50. package/src/dom/body.ts +93 -39
  51. package/src/dom/index.ts +49 -20
  52. package/src/dom/probe.ts +1 -1
  53. package/src/dom/rim.ts +63 -28
  54. package/src/dom/style-hold.ts +8 -0
  55. package/src/law.ts +54 -4
  56. package/src/lens-shader.ts +26 -33
  57. package/src/material.ts +10 -7
  58. package/src/targets/glsl.ts +27 -2
  59. package/dist/chunk-H5QTBF67.js.map +0 -1
  60. package/dist/chunk-MMPK6DCK.js.map +0 -1
  61. package/dist/chunk-PM2SWCSF.js.map +0 -1
@@ -28,14 +28,17 @@ type VireGlassMaterial = {
28
28
  /** Lightness of what the app draws OVER the glass: 1 is light icons and text, 0 is dark. A
29
29
  * cause, not a knob: the calling screen knows it. */
30
30
  ink: number;
31
- /** Minimum PRESENCE of the element itself: how far its body must stand off in lightness from
32
- * the backdrop behind it. Not the same as `legibility` — that's about the ink OVER the glass,
31
+ /** Minimum PRESENCE of the element itself: a floor on its body density, tinted toward light and
32
+ * fading as the backdrop nears the light pole. Not the same as `legibility` — that's about the ink OVER the glass,
33
33
  * this is about the element itself. Over a uniform backdrop the glass has nothing to refract,
34
34
  * and the element disappears; for a control that's unacceptable. 0 means no presence is
35
35
  * required.
36
36
  *
37
- * The sign is taken FROM THE BACKDROP, not from ink polarity: over dark the body lightens,
38
- * over light it darkens. So the requirement works the same way on any backdrop. */
37
+ * The direction is always toward light, whatever the ink: the reference body is lighter than
38
+ * its surroundings from dark video to sky, under a white glyph as much as a dark one, and only
39
+ * as far as the range allows. It never darkens the element on its own account. What can send
40
+ * the body to the ink's pole is the ink's legibility over texture, a system setting (reduced
41
+ * transparency, increased contrast) or a density past `BODY.inkPoleTo`. */
39
42
  presence: number;
40
43
  /** Surface film thickness, nm. This is where interference comes from: the optical path
41
44
  * difference in the film becomes comparable to the wavelength, and the reflection picks up
@@ -152,8 +155,8 @@ declare const VIREGLASS_LYRICS_MATERIAL: VireGlassMaterial;
152
155
  * a native lens that sets body density: there the element is denser by 2.4 percentage points. On
153
156
  * the web the surface has no body at all; the effect only shows on Android below 13.
154
157
  *
155
- * `presence` is raised for the same reason: a piece of background is allowed to disappear over a
156
- * uniform canvas, a control isn't — it has to be visible before it's been tapped.
158
+ * `presence` is left at none: the medium's own lift already separates the body from a uniform
159
+ * backdrop as far as the reference does (docs/dom.md), and the rim carries the silhouette.
157
160
  *
158
161
  * IMPORTANT about size. The bevel is specified by the medium in dp and doesn't depend on the
159
162
  * element's size, but the geometry clamps it to half the smaller half-size (`MAX_BEVEL_FRACTION`).
package/dist/react.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { V as VireGlassOptics } from './material-CnUmdKg1.cjs';
1
+ import { V as VireGlassOptics } from './material-D9m1EN47.cjs';
2
2
  import { B as BackdropSample } from './adaptation-BSbbdExK.cjs';
3
3
 
4
4
  type GlassAdaptation = {
package/dist/react.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { V as VireGlassOptics } from './material-CnUmdKg1.js';
1
+ import { V as VireGlassOptics } from './material-D9m1EN47.js';
2
2
  import { B as BackdropSample } from './adaptation-BSbbdExK.js';
3
3
 
4
4
  type GlassAdaptation = {
@@ -1,5 +1,5 @@
1
- import { V as VireGlassGeometry } from './geometry-RhKd7CM5.cjs';
2
- import './material-CnUmdKg1.cjs';
1
+ import { V as VireGlassGeometry } from './geometry-ip9WSlZv.cjs';
2
+ import './material-D9m1EN47.cjs';
3
3
 
4
4
  /**
5
5
  * REFERENCE CANVASES — the set that both benches draw and that the gates run against.
@@ -1,5 +1,5 @@
1
- import { V as VireGlassGeometry } from './geometry-GvXtt-_S.js';
2
- import './material-CnUmdKg1.js';
1
+ import { V as VireGlassGeometry } from './geometry-pabkYV7w.js';
2
+ import './material-D9m1EN47.js';
3
3
 
4
4
  /**
5
5
  * REFERENCE CANVASES — the set that both benches draw and that the gates run against.
package/dist/reference.js CHANGED
@@ -1,5 +1,5 @@
1
- import { roundedRectGeometry, capsuleGeometry, circleGeometry } from './chunk-SLRPHDV7.js';
2
- import './chunk-MMPK6DCK.js';
1
+ import { roundedRectGeometry, capsuleGeometry, circleGeometry } from './chunk-Z4FKYY47.js';
2
+ import './chunk-CPSIY2DZ.js';
3
3
 
4
4
  // src/reference-scene.ts
5
5
  var REFERENCE_SHAPES = {
@@ -23,7 +23,48 @@ var RIM = {
23
23
  * The dark edge along the whole silhouette, as a multiplicative darkening. iOS 27 made it a
24
24
  * layer of its own, and it does not rule out the highlight sitting on top of it (§2).
25
25
  */
26
- darkEdge: 0.22};
26
+ darkEdge: 0.22,
27
+ /**
28
+ * How strongly the line is drawn, by the lightness around the element. The rim is a reflection
29
+ * of the surroundings, so it is faint over a dark one and plain over a bright one, not white at
30
+ * full strength everywhere (§2, §6).
31
+ *
32
+ * MEASURED off the controls of A 2:17 (over dark video), M 2:52 (over sky) and A 2:18 (over
33
+ * grass), as the brightest rim bin's OKLab lightness over the surround's: 0.24 to 0.27 over dark,
34
+ * 0.25 over sky, 0.37 over grass. Through the DOM path onto the same backdrops these land within
35
+ * 0.08 of the smooth rows (docs/dom.md); a line white at full strength reads 0.70 to 0.80 over the
36
+ * dark frames.
37
+ */
38
+ strengthDark: 0.37,
39
+ strengthLight: 0.95,
40
+ /**
41
+ * The surround lightness between which the strength moves from one to the other. UNMEASURED: the
42
+ * two ends are, and the controls in between were not looked for.
43
+ */
44
+ ambientFrom: 0.15,
45
+ ambientTo: 0.45,
46
+ /**
47
+ * The share of the line's width, from the silhouette inward, that the light line leaves to the
48
+ * dark edge. UNMEASURED: without it the line covers the dark edge and an element over a bright
49
+ * flat backdrop loses the one mark that holds it there.
50
+ */
51
+ lineInset: 0.3,
52
+ /**
53
+ * The share of the peak that the line keeps along the whole contour, away from the key light.
54
+ * MEASURED: the reference line goes all the way round. Its dimmest bin sits 0.06 to 0.12 of
55
+ * lightness over the surround on the dark frames, against a peak of 0.24 to 0.27 (A 2:17).
56
+ */
57
+ contour: 0.3,
58
+ /** How far the line's colour is lifted from the surroundings' toward white. UNMEASURED: the
59
+ * lens always had 0.75 here, and this is that figure with a name. */
60
+ whiteShare: 0.75,
61
+ /**
62
+ * The reflectance (`fresnelStrength`) at which the line is drawn at the strengths above: that
63
+ * of ordinary glass, index 1.5. A denser medium does not draw it brighter than they say; a
64
+ * thinner one draws it fainter. UNMEASURED.
65
+ */
66
+ reflectanceRef: 0.7
67
+ };
27
68
  var BODY = {
28
69
  /** Lightness ceiling for the body when legibility is loose. */
29
70
  capLoose: 0.62,
@@ -36,12 +77,30 @@ var BODY = {
36
77
  busyEdge: 0.75,
37
78
  /** Fine texture the body has to stop being a window for. */
38
79
  groundSpan: 0.06,
39
- /** Where the tint flips direction, by the element's AVERAGE lightness — a local threshold cuts
40
- * a diagonal step across the body on a gradient. */
41
- darkSideFrom: 0.42,
42
- darkSideTo: 0.58,
80
+ /**
81
+ * The spread under the element at which the ink's legibility requirement starts to bind, and the
82
+ * spread where it binds fully. Over a flat backdrop polarity flips before the ink drowns, so
83
+ * the body does not darken under a light glyph there; over texture it does. UNMEASURED: the
84
+ * reference shows a lighter body under a white glyph over sky, nothing about texture.
85
+ */
86
+ inkBusyFrom: 0.02,
87
+ inkBusyTo: 0.12,
43
88
  /** Ceiling on any single demand, so one requirement cannot make the glass opaque on its own. */
44
- maxDemand: 0.92};
89
+ maxDemand: 0.92,
90
+ /**
91
+ * And a tighter one for presence specifically. It is a FLOOR on visibility, not a licence to go
92
+ * opaque: wherever the body's tint sits close to the backdrop, the density that would deliver the
93
+ * separation is the ceiling, and glass at `maxDemand` has stopped being a window. Better to
94
+ * separate as far as transparency allows and no further. UNMEASURED.
95
+ */
96
+ presenceDemand: 0.5,
97
+ /**
98
+ * The density at which the body is wholly the ink's own pole. Between `presenceDemand` and this
99
+ * the body moves from the light a thin body carries to the pole: a density no presence floor
100
+ * reaches is one the ink's legibility, or a system setting, has asked for. UNMEASURED.
101
+ */
102
+ inkPoleTo: 0.8
103
+ };
45
104
  var DISPERSION = {
46
105
  /** Red bends less than the base index, blue more — and not symmetrically. */
47
106
  redShift: -0.4,
@@ -718,7 +777,11 @@ half4 main(float2 xy) {
718
777
  float rimKey = u_specular * (pow(max(rimFacing, 0.0), ${RIM.lobeExponent}.0) + ${RIM.opposingArc} * pow(max(-rimFacing, 0.0), ${RIM.lobeExponent}.0));
719
778
  // Wherever the key light doesn't fall, the face reflects its shaded surroundings \u2014 hence the
720
779
  // dark outline.
721
- float3 env = refl * (0.55 + 0.45 * min(key, 1.0)) + float3(1.2 * key);
780
+ // The key light's glint is a reflection of the surroundings too: it is scaled by the same
781
+ // ambient strength as the rim line, so it is not white at full strength over a dark backdrop.
782
+ float rimStrength = mix(${RIM.strengthDark}, ${RIM.strengthLight},
783
+ smoothstep(${RIM.ambientFrom}, ${RIM.ambientTo}, vgLuma(ambient)));
784
+ float3 env = refl * (0.55 + 0.45 * min(key, 1.0)) + float3(1.2 * key * rimStrength / ${RIM.strengthLight});
722
785
 
723
786
  // Interference and diffraction live in the reflected ray at a grazing angle \u2014 a single hue
724
787
  // multiplier applied to the reflection, no extra samples.
@@ -794,12 +857,10 @@ half4 main(float2 xy) {
794
857
  // transparent glass by the model.
795
858
  float demand = clamp(u_legibility * 4.0, 0.0, 1.0);
796
859
 
797
- // Where there's no ink, the glass tints away from the backdrop. The direction is chosen by the
798
- // average lightness under the element, not the local spot's lightness: otherwise on a gradient
799
- // the threshold cuts across the body as a diagonal step.
800
- float darkSide = smoothstep(${BODY.darkSideFrom}, ${BODY.darkSideTo}, mean);
801
- float away = mix(VG_TINT_LIGHT, VG_TINT_DARK, darkSide);
802
- float tintLuma = mix(away, mix(VG_TINT_LIGHT, VG_TINT_DARK, pol), demand);
860
+ // The body is lighter than its surroundings whatever the glyph on it is: glass concentrates
861
+ // light (M 2:29), and the reference keeps a lighter body under a white glyph over sky as much as
862
+ // over dark. Only a requirement of the ink pulls it to the ink's own pole, below.
863
+ float inkPole = mix(VG_TINT_LIGHT, VG_TINT_DARK, pol);
803
864
 
804
865
  // How much medium it takes to push lightness under the ink past the threshold \u2014 locally.
805
866
  // Computed from whichever edge of the backdrop's spread is closer to the ink: a light ink
@@ -814,32 +875,22 @@ half4 main(float2 xy) {
814
875
  float needForDark = inkLo < floorDark
815
876
  ? clamp((floorDark - inkLo) / max(VG_TINT_LIGHT - inkLo, 1e-4), 0.0, ${BODY.maxDemand})
816
877
  : 0.0;
817
- float needForInk = mix(needForDark, needForLight, pol) * demand;
878
+ // Over a flat backdrop the ink asks for nothing: polarity flips before it drowns. It is spread
879
+ // under the ink that the body has to answer.
880
+ float structured = smoothstep(${BODY.inkBusyFrom}, ${BODY.inkBusyTo}, busy);
881
+ float needForInk = mix(needForDark, needForLight, pol) * demand * structured;
818
882
 
819
883
  float ground = structure * u_legibility * VG_GROUND_SPAN;
820
884
  float density = max(max(u_bodyDensity, ground), needForInk);
821
885
 
822
886
  // PRESENCE (\xA73). Over a uniform backdrop there is nothing to refract, and an element that is
823
- // only a rim honestly disappears \u2014 right for a piece of background, wrong for a control.
824
- //
825
- // This used to live on the rim line alone, as lit = max(rimKey, u_presence x 2), while the
826
- // DOM path has always implemented it as a floor on the BODY. Two renderers, one field, two
827
- // meanings. Measured on Apple's own control over a flat page (frames/verify/m-707), the body
828
- // carries 16 levels of separation there and the rim 11.6 \u2014 so the body is where it belongs,
829
- // and the rim's reading was the wrong one.
830
- // The separation asked for is capped by the range that is left: over a backdrop at 0.96 there
831
- // is no room to go 0.05 lighter. And what it is DIVIDED by is the distance to the tint's own
832
- // end, not to the tint the body currently sits at \u2014 dividing by the latter explodes wherever
833
- // the two are close, which is how the first version of this drove density to its ceiling at
834
- // both extremes and took the window down to 7%.
835
- bool lighter = local < 0.5;
836
- float target = lighter ? min(local + u_presence, 1.0) : max(local - u_presence, 0.0);
837
- float separation = abs(target - local);
838
- float reached = abs(tintLuma - local) * density;
839
- if (u_presence > 0.0 && reached < separation) {
840
- float toEnd = abs((lighter ? VG_TINT_LIGHT : VG_TINT_DARK) - local);
841
- density = max(density, min(separation / max(toEnd, 1e-4), ${BODY.maxDemand}));
887
+ // only a rim honestly disappears \u2014 right for a piece of background, wrong for a control. A floor
888
+ // on the density, toward light: the lift it gives is presence times the room left under the
889
+ // light pole, so it fades out as the room does and has no step anywhere on the scale.
890
+ if (u_presence > 0.0) {
891
+ density = max(density, min(u_presence / VG_TINT_LIGHT, ${BODY.presenceDemand}));
842
892
  }
893
+ float tintLuma = mix(VG_TINT_LIGHT, inkPole, max(clamp(needForInk / max(density, 1e-4), 0.0, 1.0), smoothstep(${BODY.presenceDemand}, ${BODY.inkPoleTo}, density)));
843
894
 
844
895
  // Glass has no color of its own \u2014 only the color of what's beneath it (HIG "Color").
845
896
  float3 tintHue = mix(float3(1.0), vgHue(wide * 0.5 + ambient * 0.5), u_colorPickup);
@@ -879,17 +930,18 @@ half4 main(float2 xy) {
879
930
  }
880
931
 
881
932
  // RIM LIGHT \u2014 a separate layer, a line about 1 pt wide along the silhouette (M 2:36, 11:04):
882
- // brighter where the face faces the key light, weaker opposite it; where no light falls, a
883
- // dark outline. This line is what holds the element's presence, not a fill on the body: over a
884
- // flat backdrop a control is visible along its whole silhouette because of it.
885
- float line = (1.0 - smoothstep(0.0, lineW, e)) * lens;
933
+ // brighter where the face faces the key light, weaker opposite it, and never absent: the line
934
+ // runs the whole contour. It is a reflection of the surroundings, so it is faint over a dark
935
+ // one and plain over a bright one (\xA72, \xA76).
936
+ float line = smoothstep(0.0, lineW * ${RIM.lineInset}, e) * (1.0 - smoothstep(lineW * ${RIM.lineInset}, lineW, e)) * lens;
886
937
  float outline = (1.0 - smoothstep(0.0, lineW * 0.6, e)) * lens;
887
- float lit = clamp(max(rimKey * 1.4, u_presence * 2.0), 0.0, 1.0);
938
+ float reflectN = min(u_fresnel / ${RIM.reflectanceRef}, 1.0);
939
+ float lit = clamp(rimStrength * reflectN * (${RIM.contour} + (1.0 - ${RIM.contour}) * rimKey / max(u_fresnel, 0.001)), 0.0, 1.0);
888
940
  // The dark rim is its own layer around the whole silhouette, and the highlight sits ON TOP of
889
941
  // it (iOS 27). While the outline used to fade via a (1 \u2212 lit) multiplier, the silhouette read
890
942
  // as a single arc: highlight OR shadow.
891
943
  rgb *= 1.0 - ${RIM.darkEdge} * outline;
892
- rgb = mix(rgb, mix(refl, float3(1.0), 0.75), line * lit);
944
+ rgb = mix(rgb, mix(refl, float3(1.0), ${RIM.whiteShare}), line * lit);
893
945
 
894
946
  if (u_debug > 9.5 && u_debug < 10.5) { rgb = spectral * 0.5; }
895
947
  if (u_debug > 10.5 && u_debug < 11.5) { rgb = float3(density, needForInk, structure); }
@@ -1459,6 +1511,26 @@ function convertEvalCalls(src) {
1459
1511
  out += src.slice(cursor);
1460
1512
  return out;
1461
1513
  }
1514
+ function stripComments(src) {
1515
+ let out = "";
1516
+ let i = 0;
1517
+ while (i < src.length) {
1518
+ if (src.startsWith("//", i)) {
1519
+ const end = src.indexOf("\n", i);
1520
+ i = end < 0 ? src.length : end;
1521
+ } else if (src.startsWith("/*", i)) {
1522
+ const end = src.indexOf("*/", i + 2);
1523
+ if (end < 0) throw new Error("vireglass/targets/glsl: unterminated block comment");
1524
+ const stop = end + 2;
1525
+ out += src.slice(i, stop).replace(/[^\n]/g, "");
1526
+ i = stop;
1527
+ } else {
1528
+ out += src[i];
1529
+ i += 1;
1530
+ }
1531
+ }
1532
+ return out;
1533
+ }
1462
1534
  function convertReturns(mainBody) {
1463
1535
  return mainBody.replace(/\breturn\s+([^;]+);/g, "fragColor = $1; return;");
1464
1536
  }
@@ -1489,7 +1561,7 @@ function addPrologue(src) {
1489
1561
  return "#version 300 es\nprecision highp float;\n\nout vec4 fragColor;\nuniform vec2 u_resolution;\n\n" + src;
1490
1562
  }
1491
1563
  function toGLSL(shaderSource) {
1492
- let out = shaderSource;
1564
+ let out = stripComments(shaderSource);
1493
1565
  out = convertEntryPoint(out);
1494
1566
  out = convertShaderUniforms(out);
1495
1567
  out = convertEvalCalls(out);