vireglass 2.1.0 → 2.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.
- package/README.md +2 -1
- package/dist/{chunk-BZC2WHCK.js → chunk-2XJJO7SC.js} +4 -4
- package/dist/{chunk-BZC2WHCK.js.map → chunk-2XJJO7SC.js.map} +1 -1
- package/dist/{chunk-3PGYF2NK.js → chunk-CTTZNZFV.js} +3 -3
- package/dist/{chunk-3PGYF2NK.js.map → chunk-CTTZNZFV.js.map} +1 -1
- package/dist/{chunk-FL5UZPY5.js → chunk-H5QTBF67.js} +4 -4
- package/dist/{chunk-FL5UZPY5.js.map → chunk-H5QTBF67.js.map} +1 -1
- package/dist/{chunk-TTOKBARR.js → chunk-MMPK6DCK.js} +10 -3
- package/dist/chunk-MMPK6DCK.js.map +1 -0
- package/dist/{chunk-P2ZOODXD.js → chunk-SLRPHDV7.js} +3 -3
- package/dist/{chunk-P2ZOODXD.js.map → chunk-SLRPHDV7.js.map} +1 -1
- package/dist/dom.cjs +48 -20
- package/dist/dom.cjs.map +1 -1
- package/dist/dom.d.cts +15 -3
- package/dist/dom.d.ts +15 -3
- package/dist/dom.js +44 -23
- package/dist/dom.js.map +1 -1
- package/dist/index.cjs +1 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -5
- package/dist/law.cjs +8 -1
- package/dist/law.cjs.map +1 -1
- package/dist/law.d.cts +7 -0
- package/dist/law.d.ts +7 -0
- package/dist/law.js +1 -1
- package/dist/reference.js +2 -2
- package/dist/web/index.cjs +1 -2
- package/dist/web/index.cjs.map +1 -1
- package/dist/web/index.js +4 -4
- package/docs/law.md +2 -2
- package/package.json +11 -9
- package/src/dom/body.ts +24 -9
- package/src/dom/index.ts +43 -10
- package/src/dom/probe.ts +27 -3
- package/src/law.ts +7 -0
- package/dist/chunk-TTOKBARR.js.map +0 -1
package/README.md
CHANGED
|
@@ -222,6 +222,7 @@ preference; `applyGlassScale` gives that preference its own axis, ultra clear to
|
|
|
222
222
|
npm run typecheck
|
|
223
223
|
npm test
|
|
224
224
|
npm run check:glsl # both shaders compile and link as GLSL ES 3.0
|
|
225
|
+
npm run check:agsl # ...and are valid SkSL, which is what Android runs
|
|
225
226
|
npm run check:optics
|
|
226
227
|
npm run check:law # every calibrated number is in src/law.ts, cited or named unmeasured
|
|
227
228
|
npm run check:dom # a real browser: the glass bends live DOM, and only where it should
|
|
@@ -281,7 +282,7 @@ timestamp so the claims can be checked. No Apple code, assets or footage is incl
|
|
|
281
282
|
distributed with this package.
|
|
282
283
|
|
|
283
284
|
Every calibrated number lives in `src/law.ts` with its provenance, and `check:law` names the ones
|
|
284
|
-
that have none: **
|
|
285
|
+
that have none: **43 of 121** today. See [`docs/law.md`](docs/law.md).
|
|
285
286
|
|
|
286
287
|
## License
|
|
287
288
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { VG_SDF, VG_FALLOFF } from './chunk-
|
|
2
|
-
import { MEDIUM, SHADOW_GAP, INK } from './chunk-
|
|
1
|
+
import { VG_SDF, VG_FALLOFF } from './chunk-H5QTBF67.js';
|
|
2
|
+
import { MEDIUM, SHADOW_GAP, INK } from './chunk-MMPK6DCK.js';
|
|
3
3
|
|
|
4
4
|
// src/surface-shader.ts
|
|
5
5
|
var SURFACE_SHADER = `
|
|
@@ -373,5 +373,5 @@ function toGLSL(shaderSource) {
|
|
|
373
373
|
}
|
|
374
374
|
|
|
375
375
|
export { SURFACE_SHADER, addPrologue, convertEntryPoint, convertEvalCalls, convertHalfTypes, convertReturns, convertShaderUniforms, convertVecTypes, toGLSL };
|
|
376
|
-
//# sourceMappingURL=chunk-
|
|
377
|
-
//# sourceMappingURL=chunk-
|
|
376
|
+
//# sourceMappingURL=chunk-2XJJO7SC.js.map
|
|
377
|
+
//# sourceMappingURL=chunk-2XJJO7SC.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/surface-shader.ts","../src/targets/glsl.ts"],"names":[],"mappings":"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","file":"chunk-BZC2WHCK.js","sourcesContent":["import { INK, MEDIUM, SHADOW_GAP } from './law';\nimport { VG_FALLOFF, VG_SDF } from './sdf';\n\n/** Order matches `DEBUG_MODES` in `material.ts`: the index goes straight into `u_debug`. */\nexport const SURFACE_SHADER = `\nuniform shader u_icon;\n/** App-colored content ON the glass — cover art, a thumbnail. A separate layer from the ink\n * mask: that one is single-channel and colored by polarity, this one carries its own color\n * as-is. */\nuniform shader u_overlay;\n\nuniform float2 u_center;\nuniform float2 u_halfSize;\nuniform float u_corner;\nuniform float u_bevel;\nuniform float u_thickness;\nuniform float2 u_morphOffset;\nuniform float2 u_morphHalf;\nuniform float u_morphCorner;\nuniform float u_morphK;\nuniform float2 u_morph2Offset;\nuniform float2 u_morph2Half;\nuniform float u_morph2Corner;\n\nuniform float u_press;\nuniform float u_active;\n/* 0 — the element sinks in under the finger, 1 — it rises into glass (reference §5). */\nuniform float u_lift;\n\nuniform float u_edgeDensity;\nuniform float u_dispersion;\nuniform float u_refraction;\nuniform float4 u_tint;\nuniform float u_shadow;\nuniform float u_shadowReach;\nuniform float3 u_ambient;\nuniform float u_presence;\nuniform float u_progress;\nuniform float u_appear;\nuniform float u_debug;\n\nuniform float u_iconOn;\nuniform float u_overlayOn;\nuniform float u_iconScale;\nuniform float4 u_inkIdle;\nuniform float4 u_inkActive;\nuniform float2 u_touch;\nuniform float2 u_pull;\nuniform float u_touchPress;\nuniform float u_touchRadius;\nuniform float2 u_wave;\n\n${VG_SDF}\n\nconst float VG_FALLOFF = ${VG_FALLOFF};\n\n// Tint density in the flat middle; at the bevel it's multiplied by u_edgeDensity.\nconst float VG_BODY_DENSITY = ${MEDIUM.bodyDensityFlat};\n/* Fraction of the ambient hue in the shadow. The shadow has to stay a shadow, not a colored\n * blob. */\nconst float VG_SHADOW_TINT = ${SHADOW_GAP.tint}.0;\n// How much weaker the shadow is right at the outline than under the middle of the gap, and what\n// fraction of the offset it takes to reach full depth. In the reference the shadow's minimum\n// sits below the rim, not right at it.\nconst float VG_GAP_LIGHT = ${SHADOW_GAP.light};\nconst float VG_GAP_REACH = ${SHADOW_GAP.reach};\n/* Depth of the ink beneath the surface, dp. The normal displaces it by exactly this much right\n * at the rim. */\nconst float VG_INK_DEPTH = ${INK.depth}.0;\n/* Ink defocus under the finger (reference §6): a glyph loses its edge and drowns in milk. Given\n * as a FRACTION of the touch blob, not a number: the blob already arrives in device units, so\n * the radius automatically follows both screen density and the element's size. At rest it's\n * zero, and the sample stays a single one. */\nconst float VG_INK_DEFOCUS = ${INK.defocus};\n\nhalf4 vgPack(half3 c, float a) { return half4(c * half(a), half(a)); }\n\n// What bleeds into the shadow is the COLOR of the surroundings, not its brightness: the hue's own\n// lightness is subtracted out of it, otherwise the shadow pales and loses depth instead of\n// warming up. Strength is set by how much light there is around: over a nearly black backdrop\n// there's nothing to bleed into the shadow.\n// JS twin in shadow-tint.ts, which the DOM renderer reads: same derivation, same reason.\nfloat3 vgShadowTint(float3 ambient) {\n float peak = max(max(ambient.r, ambient.g), max(ambient.b, 0.001));\n float3 hue = ambient / peak;\n float lumaWeights = dot(ambient, float3(0.2126, 0.7152, 0.0722));\n return (hue - dot(hue, float3(0.2126, 0.7152, 0.0722))) * lumaWeights;\n}\n\nhalf3 vgHeat(float v) {\n float x = clamp(v, 0.0, 1.0);\n return half3(half(clamp(x * 2.2 - 0.2, 0.0, 1.0)),\n half(clamp(1.0 - abs(x - 0.5) * 2.2, 0.0, 1.0)),\n half(clamp(1.2 - x * 2.4, 0.0, 1.0)));\n}\n\nhalf4 main(float2 xy) {\n float2 p = vgTouchWarp(xy - u_center, u_touch, u_pull, u_touchPress, u_touchRadius, u_wave.x, u_wave.y);\n\n // Inverse deformation: stretch A along the pull vector, compression 1/sqrt(A) across it. This\n // exact law mirrors the transform of the live backdrop under the canvas, otherwise the two\n // would drift apart. There's NO motion geometry here — no pull, no press-driven bulge. All of\n // that is done by a single wrapper transform, the same one that carries the native lens: the\n // shader doesn't touch it, and two pipelines driving one motion would drift apart by a frame,\n // making the layers visible as separate.\n\n float halfMin = max(min(u_halfSize.x, u_halfSize.y), 1.0);\n float bevel = max(u_bevel, 1.0);\n\n float sd = vgScene(p, u_halfSize, u_corner, u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float t = vgBevelT(sd, bevel);\n float2 n = vgSceneNormal(p, u_halfSize, u_corner, u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float3 N = normalize(float3(n * vgBevelSlope(t), 1.0));\n\n // Progress is an active state turned into a field: the played fraction shines and glows\n // exactly as much as an active element shines whole. This does NOT touch ink: recoloring text\n // as the track advances would mean changing it mid-word.\n float lit = max(u_active, vgProgress(p, u_halfSize, u_progress));\n\n // The highlight and the rim are computed by the lens: reflection is a function of the\n // surroundings, and only the lens can see them.\n\n if (u_debug > 0.5) {\n float inMask = 1.0 - smoothstep(-1.0, 1.0, sd);\n if (u_debug < 1.5) {\n float band = abs(fract(sd / (halfMin * 0.22)) - 0.5) * 2.0;\n half3 c = sd < 0.0 ? half3(0.20, 0.62, 1.0) : half3(1.0, 0.42, 0.22);\n return vgPack(c * half(0.25 + band * 0.75), 1.0);\n }\n if (u_debug < 2.5) { return vgPack(half3(1.0), inMask); }\n if (u_debug < 3.5) { return vgPack(vgHeat(t), inMask); }\n // Fresnel now lives in the lens; here we show its SHAPE — how grazing the view is.\n if (u_debug < 4.5) { return vgPack(vgHeat(1.0 - clamp(N.z, 0.0, 1.0)), inMask); }\n if (u_debug < 5.5) {\n float push = pow(t, VG_FALLOFF) * u_refraction;\n return vgPack(vgHeat(push), inMask);\n }\n if (u_debug < 6.5) { return half4(0.0); }\n if (u_debug < 7.5) { return half4(0.0); }\n // Dispersion is computed by the lens; here is the field it grows along.\n if (u_debug < 8.5) { return vgPack(vgHeat(u_dispersion * t * t), inMask); }\n if (u_debug < 9.5) { return vgPack(half3(N * 0.5 + 0.5), inMask); }\n // spectral and adapt are shown by the LENS — the surface has to get out of the way.\n return half4(0.0);\n }\n\n // The shadow and the halo live OUTSIDE the shape: inside, their place is taken by the glass\n // itself. Separation from the content rests entirely on the shadow — without it the surface\n // would sit ON the picture, not above it. The shadow only lives at the rim and outside it: at\n // sd < −1 the outside multiplier is already zero. Computing it deep inside the shape would be\n // a wasted FULL SDF evaluation for every such pixel, and the body covers almost the whole\n // area. The branch here is on the coordinate, but only the threads right at the rim actually\n // diverge.\n float shade = 0.0;\n float halo = 0.0;\n if (sd > -1.0) {\n float outside = smoothstep(-1.0, 1.0, sd);\n // Under the finger a button moves TOWARD the backdrop, and the shadow tightens: it's exactly\n // the gap between the two. A control rising into glass (a switch knob, a slider handle —\n // reference §5) moves AWAY from it, and then the shadow spreads out instead. Only the app\n // knows which one applies, so the direction arrives as a fraction: 0 is sinking in, the way\n // every element behaved before this rule existed.\n float reach = u_shadowReach * (1.0 + mix(-0.25, 0.55, u_lift) * u_press);\n // The shadow is soft and wide, shifted downward (M 11:58): it's what holds the element's\n // separation from the content.\n float sdDrop = vgScene(p - float2(0.0, reach * 0.35), u_halfSize, u_corner,\n u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float amb = 1.0 - smoothstep(-reach * 0.3, reach, sdDrop);\n // THE GAP UNDER A RAISED ELEMENT IS LIT: right at the outline the shadow is WEAKER than\n // further down. Otherwise the minimum sits right against the silhouette — both parts of the\n // shadow are monotonic in distance.\n float gap = smoothstep(0.0, max(reach * VG_GAP_REACH, 1.0), max(sd, 0.0));\n shade = amb * amb * 0.13 * mix(VG_GAP_LIGHT, 1.0, gap) * outside * u_shadow * u_appear;\n halo = 1.0 - smoothstep(0.0, reach * 0.30, max(sd, 0.0));\n halo = halo * halo * lit * 0.10 * outside * u_appear;\n }\n\n if (sd > 1.0) {\n // Ambient light bleeds into the shadow (219 @8:22): it isn't a black hole, it's tinted by\n // whatever lies nearby. Otherwise an element over colored content would hang over a gray\n // blob.\n half3 spill = max(half3(half(halo)) + half3(vgShadowTint(u_ambient)) * half(VG_SHADOW_TINT * shade * (1.0 - halo)), half3(0.0));\n return half4(spill, half(halo + shade * (1.0 - halo)));\n }\n\n float density = u_tint.w;\n float inner = clamp(-sd / halfMin, 0.0, 1.0);\n half3 col = half3(u_tint.rgb);\n\n // The tint is LIGHT and weak, not dark: dark glass over dark content disappears, and has to be\n // held together with a heavy rim — which makes the surface read as a chrome bead. Tint density\n // at the bevel is a separate value: physically the bevel bends light harder, but it doesn't\n // get hazier. Coupled together, the two produced a milky ring around the whole outline.\n float body = mix(VG_BODY_DENSITY, VG_BODY_DENSITY * u_edgeDensity, smoothstep(0.10, 0.62, t)) * density;\n float vignette = smoothstep(0.15, 1.0, inner) * 0.19 * density;\n // There's NO density boost on press here. It was meant as \"the element stands out more under\n // the finger,\" but it pulls the body toward the tint, and the tint depends on polarity: over a\n // light backdrop (dark polarity) press ended up DARKENING the element. Presence is shown\n // instead by the vgTouchWarp field's deformation and the highlight blooming below — neither\n // cares about the sign of polarity.\n float a = max(body, vignette);\n\n // Active state does NOT touch the body's color. Mixing in a fixed gray here flipped the\n // effect's sign depending on the backdrop: over dark the element lightened, over light it\n // darkened, for the exact same state. Active state is shown instead by the highlight and rim\n // lighting above: neither cares about the backdrop.\n\n // THE ICON SITS BENEATH THE SURFACE, it isn't stuck on top of it: it used to be mixed in last,\n // over the highlight, and read as a flat sticker on volumetric glass. At the rim the normal\n // displaces it, like everything seen through the glass, while the highlight sits ON TOP — it\n // lives on the surface itself.\n //\n // There is NO shadow under the icon here. It used to be made from the difference of two offset\n // mask samples and produced a second outline along the edge — the icon's borders looked\n // ragged.\n // THE INK'S SHIFT IS SET BY ITS OWN DEPTH, NOT BY BEVEL WIDTH. The ink sits right at the\n // surface, and it's displaced by exactly the thin layer of glass above it — not by how wide a\n // bevel this particular piece of glass happens to have. It used to be a fraction of the bevel:\n // on thin glass the shift came out to 4 dp and everything lined up, but on thick glass it was\n // 8, and cover art inset from the edge by 6 stretched toward the rim and spilled past its\n // bounds. The ceiling is absolute: ink has one depth regardless of the glass.\n float2 inkShift = n * (min(u_bevel * 0.5, VG_INK_DEPTH) * t);\n float2 inkUv = u_center + p - inkShift;\n\n // INK LOSES SHARPNESS UNDER THE FINGER (reference §6): in the reference, a glyph under\n // pressure drowns in milk rather than simply getting lighter. This branch is uniform across\n // the whole element: press arrives as a uniform, so defocus costs nothing while the element\n // isn't being touched.\n float defocus = VG_INK_DEFOCUS * u_touchRadius * u_touchPress;\n float2 inkDx = float2(defocus, 0.0);\n float2 inkDy = float2(0.0, defocus);\n\n half4 ink;\n half4 over;\n if (defocus > 0.01) {\n // A cross around the center with double weight in the middle: the ink mask is\n // high-contrast, and four samples are enough for a stroke to stop holding its edge.\n ink = (u_icon.eval((inkUv - inkDx) * u_iconScale)\n + u_icon.eval((inkUv + inkDx) * u_iconScale)\n + u_icon.eval((inkUv - inkDy) * u_iconScale)\n + u_icon.eval((inkUv + inkDy) * u_iconScale)\n + u_icon.eval(inkUv * u_iconScale) * 2.0) * half(u_iconOn / 6.0);\n // Colored content lives in the same plane as the ink and blurs together with it: different\n // sharpness for two layers in one plane would read as a defect, not a press.\n over = (u_overlay.eval((inkUv - inkDx) * u_iconScale)\n + u_overlay.eval((inkUv + inkDx) * u_iconScale)\n + u_overlay.eval((inkUv - inkDy) * u_iconScale)\n + u_overlay.eval((inkUv + inkDy) * u_iconScale)\n + u_overlay.eval(inkUv * u_iconScale) * 2.0) * half(u_overlayOn * u_appear / 6.0);\n } else {\n ink = u_icon.eval(inkUv * u_iconScale) * half(u_iconOn);\n over = u_overlay.eval(inkUv * u_iconScale) * half(u_overlayOn * u_appear);\n }\n\n // The backing under the ink is held by the LENS (body density from the probe), not the\n // surface: legibility is a property of the material, and computing it here a second time\n // would give two different answers to one question.\n\n // Layers are composited premultiplied-over: in straight alpha, a nearly transparent dark body\n // used to pull the color of semi-transparent ink and the finger's light toward itself — letter\n // edges grayed out, pressing darkened things.\n half3 pm = col * half(a);\n half inkA = ink.g * half(mix(0.82, 1.0, u_active) * u_appear);\n half3 inkCol = mix(half3(u_inkIdle.rgb), half3(u_inkActive.rgb), half(u_active));\n pm = pm * (1.0 - inkA) + inkCol * inkA;\n a = a + float(inkA) * (1.0 - a);\n\n // COLORED CONTENT LIVES IN THE SAME PLACE AS THE INK — inside the material, at the same\n // coordinate. Otherwise deformation would carry them apart: on press, the title and artist\n // name would shake together with the surface while the cover art stayed put, because it used\n // to be a separate layer on top of the glass. Polarity doesn't touch it — it has its own\n // color, and there's nothing to swap it for.\n pm = pm * (1.0 - over.a) + over.rgb * over.a;\n a = a + float(over.a) * (1.0 - a);\n\n // Light from the finger is gathered by the LENS: it's a concentration of the surroundings, not\n // the glass's own whiteness. The surface is left with only what the glass spills outward — the\n // halo above.\n\n float mask = 1.0 - smoothstep(-1.0, 1.0, sd);\n pm = clamp(pm, half3(0.0), half3(1.0)) * half(mask);\n a *= mask;\n pm = max(pm + half3(vgShadowTint(u_ambient)) * half(VG_SHADOW_TINT * shade * (1.0 - a)), half3(0.0));\n return half4(pm, half(a + shade * (1.0 - a)));\n}\n`;\n","// WebGL2 target. Translates the same AGSL text (SDF + body, see `targets/agsl.ts`) into GLSL\n// ES 3.0. Each conversion rule is its own function with its own test in\n// `__tests__/targets.test.ts`. Three spots are NOT mechanical: the coordinate origin, units of\n// measure (settled in the web renderer's own setup, not here), and sampler filtering (also the\n// renderer's concern). What's here is text conversion only.\n\n/**\n * The sampler-size uniform's name, for translating `NAME.eval(coord)` → `texture(NAME, coord / SIZE)`.\n * A new uniform — there's no equivalent in AGSL: `.eval()` has the size implicitly known to the\n * shader, while GLSL's `texture()` needs a normalized coordinate. One per `uniform shader`/\n * `uniform sampler2D`, its name derived from the sampler's name rather than hardcoded.\n */\nfunction sizeUniformName(sampler: string): string {\n return sampler.startsWith('u_') ? `${sampler}Size` : `u_${sampler}Size`;\n}\n\n/** float2/float3/float4 → vec2/vec3/vec4. */\nexport function convertVecTypes(src: string): string {\n return src.replace(/\\bfloat([234])\\b/g, 'vec$1');\n}\n\n/** half/half2/half3/half4 → float/vec2/vec3/vec4, including half(x) → float(x) casts. */\nexport function convertHalfTypes(src: string): string {\n return src.replace(/\\bhalf([234])\\b/g, 'vec$1').replace(/\\bhalf\\b/g, 'float');\n}\n\n/** `uniform shader NAME;` → `uniform sampler2D NAME;` + a new `uniform vec2 <sizeUniform>;`. */\nexport function convertShaderUniforms(src: string): string {\n return src.replace(/uniform\\s+shader\\s+(\\w+)\\s*;/g, (_match, name: string) => {\n return `uniform sampler2D ${name};\\nuniform vec2 ${sizeUniformName(name)};`;\n });\n}\n\n/**\n * `NAME.eval(coord)` → `texture(NAME, (coord) / <sizeUniform>)` for each declared\n * `uniform sampler2D`. The bounds of `coord` are found by scanning parens, not with a regex: the\n * arguments are nested calls (`content.eval(vgInContent(s + dR - e))`) with commas and\n * parentheses inside.\n */\nexport function convertEvalCalls(src: string): string {\n const samplers = new Set<string>();\n const declRe = /uniform\\s+sampler2D\\s+(\\w+)\\s*;/g;\n for (let m = declRe.exec(src); m; m = declRe.exec(src)) samplers.add(m[1]);\n if (samplers.size === 0) return src;\n\n const callRe = /(\\w+)\\.eval\\(/g;\n let out = '';\n let cursor = 0;\n for (let m = callRe.exec(src); m; m = callRe.exec(src)) {\n const name = m[1];\n if (!samplers.has(name)) continue;\n const argStart = m.index + m[0].length;\n let depth = 1;\n let i = argStart;\n while (i < src.length && depth > 0) {\n if (src[i] === '(') depth += 1;\n else if (src[i] === ')') depth -= 1;\n i += 1;\n }\n const args = src.slice(argStart, i - 1);\n out += src.slice(cursor, m.index);\n out += `texture(${name}, (${args}) / ${sizeUniformName(name)})`;\n cursor = i;\n callRe.lastIndex = i;\n }\n out += src.slice(cursor);\n return out;\n}\n\n/**\n * Early `return <expression>;` inside `main` → `fragColor = <expression>; return;`. Takes ONLY\n * the body of main (see `convertEntryPoint`) — ordinary `return`s in helper functions live\n * outside this text and are untouched. Expressions never contain a `;` of their own (shaders have\n * none — the only `;` lives inside `for(...)`, which isn't a return), so `[^;]+` is safe.\n */\nexport function convertReturns(mainBody: string): string {\n return mainBody.replace(/\\breturn\\s+([^;]+);/g, 'fragColor = $1; return;');\n}\n\nconst ENTRY_RE = /half4\\s+main\\s*\\(\\s*float2\\s+(\\w+)\\s*\\)\\s*\\{/;\n\n/**\n * `half4 main(float2 xy) { ... }` → `void main() { vec2 xy = <Y-flipped coordinate>; ...; }`.\n * AGSL gives `xy` in screen pixels, top-left origin, y down; `gl_FragCoord` is bottom-left\n * origin, y up. The flip via `u_resolution.y - gl_FragCoord.y` restores the original convention\n * (both coordinate systems center the pixel at `+0.5`, so no X shift or constant is needed).\n * `out vec4 fragColor` and `uniform vec2 u_resolution` are declared in the prologue (`addPrologue`).\n */\nexport function convertEntryPoint(src: string): string {\n const match = ENTRY_RE.exec(src);\n if (!match) {\n throw new Error('vireglass/targets/glsl: entry point \"half4 main(float2 xy)\" not found');\n }\n const bodyStart = match.index + match[0].length;\n let depth = 1;\n let i = bodyStart;\n while (i < src.length && depth > 0) {\n if (src[i] === '{') depth += 1;\n else if (src[i] === '}') depth -= 1;\n i += 1;\n }\n const bodyEnd = i - 1;\n const param = match[1];\n const body = convertReturns(src.slice(bodyStart, bodyEnd));\n const head = src.slice(0, match.index);\n const tail = src.slice(i);\n return (\n `${head}void main() {\\n` +\n ` vec2 ${param} = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y);\\n` +\n `${body}}${tail}`\n );\n}\n\n/** `#version 300 es` + `precision highp float;` — no equivalent in AGSL, WebGL2 requires them as\n * the file's first lines. `out vec4 fragColor` and `uniform vec2 u_resolution` ride along here\n * too: both new, with no counterpart in the source, needed by exactly one entry point\n * (`convertEntryPoint`). */\nexport function addPrologue(src: string): string {\n return (\n '#version 300 es\\n' +\n 'precision highp float;\\n\\n' +\n 'out vec4 fragColor;\\n' +\n 'uniform vec2 u_resolution;\\n\\n' +\n src\n );\n}\n\n/** Full translation AGSL/SkSL → GLSL ES 3.0. Order matters: the entry point is found by its\n * literal AGSL signature BEFORE type conversion, otherwise `half4 main(float2 xy)` can't be\n * found. */\nexport function toGLSL(shaderSource: string): string {\n let out = shaderSource;\n out = convertEntryPoint(out);\n out = convertShaderUniforms(out);\n out = convertEvalCalls(out);\n out = convertVecTypes(out);\n out = convertHalfTypes(out);\n out = addPrologue(out);\n return out;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/surface-shader.ts","../src/targets/glsl.ts"],"names":[],"mappings":"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","file":"chunk-2XJJO7SC.js","sourcesContent":["import { INK, MEDIUM, SHADOW_GAP } from './law';\nimport { VG_FALLOFF, VG_SDF } from './sdf';\n\n/** Order matches `DEBUG_MODES` in `material.ts`: the index goes straight into `u_debug`. */\nexport const SURFACE_SHADER = `\nuniform shader u_icon;\n/** App-colored content ON the glass — cover art, a thumbnail. A separate layer from the ink\n * mask: that one is single-channel and colored by polarity, this one carries its own color\n * as-is. */\nuniform shader u_overlay;\n\nuniform float2 u_center;\nuniform float2 u_halfSize;\nuniform float u_corner;\nuniform float u_bevel;\nuniform float u_thickness;\nuniform float2 u_morphOffset;\nuniform float2 u_morphHalf;\nuniform float u_morphCorner;\nuniform float u_morphK;\nuniform float2 u_morph2Offset;\nuniform float2 u_morph2Half;\nuniform float u_morph2Corner;\n\nuniform float u_press;\nuniform float u_active;\n/* 0 — the element sinks in under the finger, 1 — it rises into glass (reference §5). */\nuniform float u_lift;\n\nuniform float u_edgeDensity;\nuniform float u_dispersion;\nuniform float u_refraction;\nuniform float4 u_tint;\nuniform float u_shadow;\nuniform float u_shadowReach;\nuniform float3 u_ambient;\nuniform float u_presence;\nuniform float u_progress;\nuniform float u_appear;\nuniform float u_debug;\n\nuniform float u_iconOn;\nuniform float u_overlayOn;\nuniform float u_iconScale;\nuniform float4 u_inkIdle;\nuniform float4 u_inkActive;\nuniform float2 u_touch;\nuniform float2 u_pull;\nuniform float u_touchPress;\nuniform float u_touchRadius;\nuniform float2 u_wave;\n\n${VG_SDF}\n\nconst float VG_FALLOFF = ${VG_FALLOFF};\n\n// Tint density in the flat middle; at the bevel it's multiplied by u_edgeDensity.\nconst float VG_BODY_DENSITY = ${MEDIUM.bodyDensityFlat};\n/* Fraction of the ambient hue in the shadow. The shadow has to stay a shadow, not a colored\n * blob. */\nconst float VG_SHADOW_TINT = ${SHADOW_GAP.tint}.0;\n// How much weaker the shadow is right at the outline than under the middle of the gap, and what\n// fraction of the offset it takes to reach full depth. In the reference the shadow's minimum\n// sits below the rim, not right at it.\nconst float VG_GAP_LIGHT = ${SHADOW_GAP.light};\nconst float VG_GAP_REACH = ${SHADOW_GAP.reach};\n/* Depth of the ink beneath the surface, dp. The normal displaces it by exactly this much right\n * at the rim. */\nconst float VG_INK_DEPTH = ${INK.depth}.0;\n/* Ink defocus under the finger (reference §6): a glyph loses its edge and drowns in milk. Given\n * as a FRACTION of the touch blob, not a number: the blob already arrives in device units, so\n * the radius automatically follows both screen density and the element's size. At rest it's\n * zero, and the sample stays a single one. */\nconst float VG_INK_DEFOCUS = ${INK.defocus};\n\nhalf4 vgPack(half3 c, float a) { return half4(c * half(a), half(a)); }\n\n// What bleeds into the shadow is the COLOR of the surroundings, not its brightness: the hue's own\n// lightness is subtracted out of it, otherwise the shadow pales and loses depth instead of\n// warming up. Strength is set by how much light there is around: over a nearly black backdrop\n// there's nothing to bleed into the shadow.\n// JS twin in shadow-tint.ts, which the DOM renderer reads: same derivation, same reason.\nfloat3 vgShadowTint(float3 ambient) {\n float peak = max(max(ambient.r, ambient.g), max(ambient.b, 0.001));\n float3 hue = ambient / peak;\n float lumaWeights = dot(ambient, float3(0.2126, 0.7152, 0.0722));\n return (hue - dot(hue, float3(0.2126, 0.7152, 0.0722))) * lumaWeights;\n}\n\nhalf3 vgHeat(float v) {\n float x = clamp(v, 0.0, 1.0);\n return half3(half(clamp(x * 2.2 - 0.2, 0.0, 1.0)),\n half(clamp(1.0 - abs(x - 0.5) * 2.2, 0.0, 1.0)),\n half(clamp(1.2 - x * 2.4, 0.0, 1.0)));\n}\n\nhalf4 main(float2 xy) {\n float2 p = vgTouchWarp(xy - u_center, u_touch, u_pull, u_touchPress, u_touchRadius, u_wave.x, u_wave.y);\n\n // Inverse deformation: stretch A along the pull vector, compression 1/sqrt(A) across it. This\n // exact law mirrors the transform of the live backdrop under the canvas, otherwise the two\n // would drift apart. There's NO motion geometry here — no pull, no press-driven bulge. All of\n // that is done by a single wrapper transform, the same one that carries the native lens: the\n // shader doesn't touch it, and two pipelines driving one motion would drift apart by a frame,\n // making the layers visible as separate.\n\n float halfMin = max(min(u_halfSize.x, u_halfSize.y), 1.0);\n float bevel = max(u_bevel, 1.0);\n\n float sd = vgScene(p, u_halfSize, u_corner, u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float t = vgBevelT(sd, bevel);\n float2 n = vgSceneNormal(p, u_halfSize, u_corner, u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float3 N = normalize(float3(n * vgBevelSlope(t), 1.0));\n\n // Progress is an active state turned into a field: the played fraction shines and glows\n // exactly as much as an active element shines whole. This does NOT touch ink: recoloring text\n // as the track advances would mean changing it mid-word.\n float lit = max(u_active, vgProgress(p, u_halfSize, u_progress));\n\n // The highlight and the rim are computed by the lens: reflection is a function of the\n // surroundings, and only the lens can see them.\n\n if (u_debug > 0.5) {\n float inMask = 1.0 - smoothstep(-1.0, 1.0, sd);\n if (u_debug < 1.5) {\n float band = abs(fract(sd / (halfMin * 0.22)) - 0.5) * 2.0;\n half3 c = sd < 0.0 ? half3(0.20, 0.62, 1.0) : half3(1.0, 0.42, 0.22);\n return vgPack(c * half(0.25 + band * 0.75), 1.0);\n }\n if (u_debug < 2.5) { return vgPack(half3(1.0), inMask); }\n if (u_debug < 3.5) { return vgPack(vgHeat(t), inMask); }\n // Fresnel now lives in the lens; here we show its SHAPE — how grazing the view is.\n if (u_debug < 4.5) { return vgPack(vgHeat(1.0 - clamp(N.z, 0.0, 1.0)), inMask); }\n if (u_debug < 5.5) {\n float push = pow(t, VG_FALLOFF) * u_refraction;\n return vgPack(vgHeat(push), inMask);\n }\n if (u_debug < 6.5) { return half4(0.0); }\n if (u_debug < 7.5) { return half4(0.0); }\n // Dispersion is computed by the lens; here is the field it grows along.\n if (u_debug < 8.5) { return vgPack(vgHeat(u_dispersion * t * t), inMask); }\n if (u_debug < 9.5) { return vgPack(half3(N * 0.5 + 0.5), inMask); }\n // spectral and adapt are shown by the LENS — the surface has to get out of the way.\n return half4(0.0);\n }\n\n // The shadow and the halo live OUTSIDE the shape: inside, their place is taken by the glass\n // itself. Separation from the content rests entirely on the shadow — without it the surface\n // would sit ON the picture, not above it. The shadow only lives at the rim and outside it: at\n // sd < −1 the outside multiplier is already zero. Computing it deep inside the shape would be\n // a wasted FULL SDF evaluation for every such pixel, and the body covers almost the whole\n // area. The branch here is on the coordinate, but only the threads right at the rim actually\n // diverge.\n float shade = 0.0;\n float halo = 0.0;\n if (sd > -1.0) {\n float outside = smoothstep(-1.0, 1.0, sd);\n // Under the finger a button moves TOWARD the backdrop, and the shadow tightens: it's exactly\n // the gap between the two. A control rising into glass (a switch knob, a slider handle —\n // reference §5) moves AWAY from it, and then the shadow spreads out instead. Only the app\n // knows which one applies, so the direction arrives as a fraction: 0 is sinking in, the way\n // every element behaved before this rule existed.\n float reach = u_shadowReach * (1.0 + mix(-0.25, 0.55, u_lift) * u_press);\n // The shadow is soft and wide, shifted downward (M 11:58): it's what holds the element's\n // separation from the content.\n float sdDrop = vgScene(p - float2(0.0, reach * 0.35), u_halfSize, u_corner,\n u_morphOffset, u_morphHalf, u_morphCorner, u_morphK,\n u_morph2Offset, u_morph2Half, u_morph2Corner);\n float amb = 1.0 - smoothstep(-reach * 0.3, reach, sdDrop);\n // THE GAP UNDER A RAISED ELEMENT IS LIT: right at the outline the shadow is WEAKER than\n // further down. Otherwise the minimum sits right against the silhouette — both parts of the\n // shadow are monotonic in distance.\n float gap = smoothstep(0.0, max(reach * VG_GAP_REACH, 1.0), max(sd, 0.0));\n shade = amb * amb * 0.13 * mix(VG_GAP_LIGHT, 1.0, gap) * outside * u_shadow * u_appear;\n halo = 1.0 - smoothstep(0.0, reach * 0.30, max(sd, 0.0));\n halo = halo * halo * lit * 0.10 * outside * u_appear;\n }\n\n if (sd > 1.0) {\n // Ambient light bleeds into the shadow (219 @8:22): it isn't a black hole, it's tinted by\n // whatever lies nearby. Otherwise an element over colored content would hang over a gray\n // blob.\n half3 spill = max(half3(half(halo)) + half3(vgShadowTint(u_ambient)) * half(VG_SHADOW_TINT * shade * (1.0 - halo)), half3(0.0));\n return half4(spill, half(halo + shade * (1.0 - halo)));\n }\n\n float density = u_tint.w;\n float inner = clamp(-sd / halfMin, 0.0, 1.0);\n half3 col = half3(u_tint.rgb);\n\n // The tint is LIGHT and weak, not dark: dark glass over dark content disappears, and has to be\n // held together with a heavy rim — which makes the surface read as a chrome bead. Tint density\n // at the bevel is a separate value: physically the bevel bends light harder, but it doesn't\n // get hazier. Coupled together, the two produced a milky ring around the whole outline.\n float body = mix(VG_BODY_DENSITY, VG_BODY_DENSITY * u_edgeDensity, smoothstep(0.10, 0.62, t)) * density;\n float vignette = smoothstep(0.15, 1.0, inner) * 0.19 * density;\n // There's NO density boost on press here. It was meant as \"the element stands out more under\n // the finger,\" but it pulls the body toward the tint, and the tint depends on polarity: over a\n // light backdrop (dark polarity) press ended up DARKENING the element. Presence is shown\n // instead by the vgTouchWarp field's deformation and the highlight blooming below — neither\n // cares about the sign of polarity.\n float a = max(body, vignette);\n\n // Active state does NOT touch the body's color. Mixing in a fixed gray here flipped the\n // effect's sign depending on the backdrop: over dark the element lightened, over light it\n // darkened, for the exact same state. Active state is shown instead by the highlight and rim\n // lighting above: neither cares about the backdrop.\n\n // THE ICON SITS BENEATH THE SURFACE, it isn't stuck on top of it: it used to be mixed in last,\n // over the highlight, and read as a flat sticker on volumetric glass. At the rim the normal\n // displaces it, like everything seen through the glass, while the highlight sits ON TOP — it\n // lives on the surface itself.\n //\n // There is NO shadow under the icon here. It used to be made from the difference of two offset\n // mask samples and produced a second outline along the edge — the icon's borders looked\n // ragged.\n // THE INK'S SHIFT IS SET BY ITS OWN DEPTH, NOT BY BEVEL WIDTH. The ink sits right at the\n // surface, and it's displaced by exactly the thin layer of glass above it — not by how wide a\n // bevel this particular piece of glass happens to have. It used to be a fraction of the bevel:\n // on thin glass the shift came out to 4 dp and everything lined up, but on thick glass it was\n // 8, and cover art inset from the edge by 6 stretched toward the rim and spilled past its\n // bounds. The ceiling is absolute: ink has one depth regardless of the glass.\n float2 inkShift = n * (min(u_bevel * 0.5, VG_INK_DEPTH) * t);\n float2 inkUv = u_center + p - inkShift;\n\n // INK LOSES SHARPNESS UNDER THE FINGER (reference §6): in the reference, a glyph under\n // pressure drowns in milk rather than simply getting lighter. This branch is uniform across\n // the whole element: press arrives as a uniform, so defocus costs nothing while the element\n // isn't being touched.\n float defocus = VG_INK_DEFOCUS * u_touchRadius * u_touchPress;\n float2 inkDx = float2(defocus, 0.0);\n float2 inkDy = float2(0.0, defocus);\n\n half4 ink;\n half4 over;\n if (defocus > 0.01) {\n // A cross around the center with double weight in the middle: the ink mask is\n // high-contrast, and four samples are enough for a stroke to stop holding its edge.\n ink = (u_icon.eval((inkUv - inkDx) * u_iconScale)\n + u_icon.eval((inkUv + inkDx) * u_iconScale)\n + u_icon.eval((inkUv - inkDy) * u_iconScale)\n + u_icon.eval((inkUv + inkDy) * u_iconScale)\n + u_icon.eval(inkUv * u_iconScale) * 2.0) * half(u_iconOn / 6.0);\n // Colored content lives in the same plane as the ink and blurs together with it: different\n // sharpness for two layers in one plane would read as a defect, not a press.\n over = (u_overlay.eval((inkUv - inkDx) * u_iconScale)\n + u_overlay.eval((inkUv + inkDx) * u_iconScale)\n + u_overlay.eval((inkUv - inkDy) * u_iconScale)\n + u_overlay.eval((inkUv + inkDy) * u_iconScale)\n + u_overlay.eval(inkUv * u_iconScale) * 2.0) * half(u_overlayOn * u_appear / 6.0);\n } else {\n ink = u_icon.eval(inkUv * u_iconScale) * half(u_iconOn);\n over = u_overlay.eval(inkUv * u_iconScale) * half(u_overlayOn * u_appear);\n }\n\n // The backing under the ink is held by the LENS (body density from the probe), not the\n // surface: legibility is a property of the material, and computing it here a second time\n // would give two different answers to one question.\n\n // Layers are composited premultiplied-over: in straight alpha, a nearly transparent dark body\n // used to pull the color of semi-transparent ink and the finger's light toward itself — letter\n // edges grayed out, pressing darkened things.\n half3 pm = col * half(a);\n half inkA = ink.g * half(mix(0.82, 1.0, u_active) * u_appear);\n half3 inkCol = mix(half3(u_inkIdle.rgb), half3(u_inkActive.rgb), half(u_active));\n pm = pm * (1.0 - inkA) + inkCol * inkA;\n a = a + float(inkA) * (1.0 - a);\n\n // COLORED CONTENT LIVES IN THE SAME PLACE AS THE INK — inside the material, at the same\n // coordinate. Otherwise deformation would carry them apart: on press, the title and artist\n // name would shake together with the surface while the cover art stayed put, because it used\n // to be a separate layer on top of the glass. Polarity doesn't touch it — it has its own\n // color, and there's nothing to swap it for.\n pm = pm * (1.0 - over.a) + over.rgb * over.a;\n a = a + float(over.a) * (1.0 - a);\n\n // Light from the finger is gathered by the LENS: it's a concentration of the surroundings, not\n // the glass's own whiteness. The surface is left with only what the glass spills outward — the\n // halo above.\n\n float mask = 1.0 - smoothstep(-1.0, 1.0, sd);\n pm = clamp(pm, half3(0.0), half3(1.0)) * half(mask);\n a *= mask;\n pm = max(pm + half3(vgShadowTint(u_ambient)) * half(VG_SHADOW_TINT * shade * (1.0 - a)), half3(0.0));\n return half4(pm, half(a + shade * (1.0 - a)));\n}\n`;\n","// WebGL2 target. Translates the same AGSL text (SDF + body, see `targets/agsl.ts`) into GLSL\n// ES 3.0. Each conversion rule is its own function with its own test in\n// `__tests__/targets.test.ts`. Three spots are NOT mechanical: the coordinate origin, units of\n// measure (settled in the web renderer's own setup, not here), and sampler filtering (also the\n// renderer's concern). What's here is text conversion only.\n\n/**\n * The sampler-size uniform's name, for translating `NAME.eval(coord)` → `texture(NAME, coord / SIZE)`.\n * A new uniform — there's no equivalent in AGSL: `.eval()` has the size implicitly known to the\n * shader, while GLSL's `texture()` needs a normalized coordinate. One per `uniform shader`/\n * `uniform sampler2D`, its name derived from the sampler's name rather than hardcoded.\n */\nfunction sizeUniformName(sampler: string): string {\n return sampler.startsWith('u_') ? `${sampler}Size` : `u_${sampler}Size`;\n}\n\n/** float2/float3/float4 → vec2/vec3/vec4. */\nexport function convertVecTypes(src: string): string {\n return src.replace(/\\bfloat([234])\\b/g, 'vec$1');\n}\n\n/** half/half2/half3/half4 → float/vec2/vec3/vec4, including half(x) → float(x) casts. */\nexport function convertHalfTypes(src: string): string {\n return src.replace(/\\bhalf([234])\\b/g, 'vec$1').replace(/\\bhalf\\b/g, 'float');\n}\n\n/** `uniform shader NAME;` → `uniform sampler2D NAME;` + a new `uniform vec2 <sizeUniform>;`. */\nexport function convertShaderUniforms(src: string): string {\n return src.replace(/uniform\\s+shader\\s+(\\w+)\\s*;/g, (_match, name: string) => {\n return `uniform sampler2D ${name};\\nuniform vec2 ${sizeUniformName(name)};`;\n });\n}\n\n/**\n * `NAME.eval(coord)` → `texture(NAME, (coord) / <sizeUniform>)` for each declared\n * `uniform sampler2D`. The bounds of `coord` are found by scanning parens, not with a regex: the\n * arguments are nested calls (`content.eval(vgInContent(s + dR - e))`) with commas and\n * parentheses inside.\n */\nexport function convertEvalCalls(src: string): string {\n const samplers = new Set<string>();\n const declRe = /uniform\\s+sampler2D\\s+(\\w+)\\s*;/g;\n for (let m = declRe.exec(src); m; m = declRe.exec(src)) samplers.add(m[1]);\n if (samplers.size === 0) return src;\n\n const callRe = /(\\w+)\\.eval\\(/g;\n let out = '';\n let cursor = 0;\n for (let m = callRe.exec(src); m; m = callRe.exec(src)) {\n const name = m[1];\n if (!samplers.has(name)) continue;\n const argStart = m.index + m[0].length;\n let depth = 1;\n let i = argStart;\n while (i < src.length && depth > 0) {\n if (src[i] === '(') depth += 1;\n else if (src[i] === ')') depth -= 1;\n i += 1;\n }\n const args = src.slice(argStart, i - 1);\n out += src.slice(cursor, m.index);\n out += `texture(${name}, (${args}) / ${sizeUniformName(name)})`;\n cursor = i;\n callRe.lastIndex = i;\n }\n out += src.slice(cursor);\n return out;\n}\n\n/**\n * Early `return <expression>;` inside `main` → `fragColor = <expression>; return;`. Takes ONLY\n * the body of main (see `convertEntryPoint`) — ordinary `return`s in helper functions live\n * outside this text and are untouched. Expressions never contain a `;` of their own (shaders have\n * none — the only `;` lives inside `for(...)`, which isn't a return), so `[^;]+` is safe.\n */\nexport function convertReturns(mainBody: string): string {\n return mainBody.replace(/\\breturn\\s+([^;]+);/g, 'fragColor = $1; return;');\n}\n\nconst ENTRY_RE = /half4\\s+main\\s*\\(\\s*float2\\s+(\\w+)\\s*\\)\\s*\\{/;\n\n/**\n * `half4 main(float2 xy) { ... }` → `void main() { vec2 xy = <Y-flipped coordinate>; ...; }`.\n * AGSL gives `xy` in screen pixels, top-left origin, y down; `gl_FragCoord` is bottom-left\n * origin, y up. The flip via `u_resolution.y - gl_FragCoord.y` restores the original convention\n * (both coordinate systems center the pixel at `+0.5`, so no X shift or constant is needed).\n * `out vec4 fragColor` and `uniform vec2 u_resolution` are declared in the prologue (`addPrologue`).\n */\nexport function convertEntryPoint(src: string): string {\n const match = ENTRY_RE.exec(src);\n if (!match) {\n throw new Error('vireglass/targets/glsl: entry point \"half4 main(float2 xy)\" not found');\n }\n const bodyStart = match.index + match[0].length;\n let depth = 1;\n let i = bodyStart;\n while (i < src.length && depth > 0) {\n if (src[i] === '{') depth += 1;\n else if (src[i] === '}') depth -= 1;\n i += 1;\n }\n const bodyEnd = i - 1;\n const param = match[1];\n const body = convertReturns(src.slice(bodyStart, bodyEnd));\n const head = src.slice(0, match.index);\n const tail = src.slice(i);\n return (\n `${head}void main() {\\n` +\n ` vec2 ${param} = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y);\\n` +\n `${body}}${tail}`\n );\n}\n\n/** `#version 300 es` + `precision highp float;` — no equivalent in AGSL, WebGL2 requires them as\n * the file's first lines. `out vec4 fragColor` and `uniform vec2 u_resolution` ride along here\n * too: both new, with no counterpart in the source, needed by exactly one entry point\n * (`convertEntryPoint`). */\nexport function addPrologue(src: string): string {\n return (\n '#version 300 es\\n' +\n 'precision highp float;\\n\\n' +\n 'out vec4 fragColor;\\n' +\n 'uniform vec2 u_resolution;\\n\\n' +\n src\n );\n}\n\n/** Full translation AGSL/SkSL → GLSL ES 3.0. Order matters: the entry point is found by its\n * literal AGSL signature BEFORE type conversion, otherwise `half4 main(float2 xy)` can't be\n * found. */\nexport function toGLSL(shaderSource: string): string {\n let out = shaderSource;\n out = convertEntryPoint(out);\n out = convertShaderUniforms(out);\n out = convertEvalCalls(out);\n out = convertVecTypes(out);\n out = convertHalfTypes(out);\n out = addPrologue(out);\n return out;\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CONFIRMATIONS, FLIP_LUMA, RETURN_LUMA } from './chunk-JNRZNMJJ.js';
|
|
2
|
-
import { SCALE, SCROLL_EDGE, ACCENT, ACCESSIBILITY, BODY, SPRING } from './chunk-
|
|
2
|
+
import { SCALE, SCROLL_EDGE, ACCENT, ACCESSIBILITY, BODY, SPRING } from './chunk-MMPK6DCK.js';
|
|
3
3
|
|
|
4
4
|
// src/accent.ts
|
|
5
5
|
var clamp01 = (v) => v < 0 ? 0 : v > 1 ? 1 : v;
|
|
@@ -356,5 +356,5 @@ function createGroupState(sink) {
|
|
|
356
356
|
}
|
|
357
357
|
|
|
358
358
|
export { GLASS_SCALE_DEFAULT, GRADIENT, NO_ACCESSIBILITY, SCROLL_EDGE_ENGAGE_DP, SETTLE, SMOOTH, accentAmount, accentTone, aggregate, applyAccessibility, applyAppear, applyGlassScale, concentricFit, concentricInset, concentricRadius, contrastRimLuma, createDeform, createGroupState, elasticAllowed, probeValuesAt, raiseIntoGlass, resolveShape, scrollEdgeReachDp, scrollEdgeStrength, scrollEdgeStyle, shadowTint, smoothPlane };
|
|
359
|
-
//# sourceMappingURL=chunk-
|
|
360
|
-
//# sourceMappingURL=chunk-
|
|
359
|
+
//# sourceMappingURL=chunk-CTTZNZFV.js.map
|
|
360
|
+
//# sourceMappingURL=chunk-CTTZNZFV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/accent.ts","../src/appear.ts","../src/shadow-tint.ts","../src/touch-response.ts","../src/accessibility.ts","../src/glass-scale.ts","../src/scroll-edge.ts","../src/concentric.ts","../src/group-model.ts"],"names":["clamp01","k","luma"],"mappings":"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","file":"chunk-3PGYF2NK.js","sourcesContent":["// Tinting (docs/reference.md §7, 219 @16:31): \"Selecting a color generates a range of tones that\n// are mapped to content brightness underneath the tinted element. It draws inspiration from how\n// colored glass works in reality: changing its hue, brightness and saturation depending on what's\n// behind without deviating too much from the intended color.\"\n//\n// So a tint is not a fill. @17:03 is explicit about what a fill costs: \"it is completely opaque\n// and breaks the visual character of Liquid Glass.\" The colour has to be a property of the medium,\n// and the content has to keep coming through it.\n//\n// JS twin of the accent block in the lens shader.\nimport type { VireGlassAccent } from './adapters';\nimport { ACCENT } from './law';\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/**\n * The tone this accent takes over a backdrop of the given lightness — deeper over dark, lighter\n * over light, which is what \"a range of tones mapped to content brightness\" means.\n *\n * @17:21: tinting is for the primary action, not for everything. \"When every element is tinted,\n * nothing stands out.\" That is the host's call, not this function's.\n */\nexport function accentTone(\n color: readonly [number, number, number],\n backdropLuma: number,\n): [number, number, number] {\n const k = ACCENT.deep + (ACCENT.light - ACCENT.deep) * clamp01(backdropLuma);\n return [clamp01(color[0] * k), clamp01(color[1] * k), clamp01(color[2] * k)];\n}\n\n/** The fraction of the medium the colour occupies, which is what keeps the content showing\n * through it rather than being covered by it. */\nexport function accentAmount(accent: VireGlassAccent): number {\n return clamp01(accent.amount ?? ACCENT.amount);\n}\n","// Materialising in and out (docs/reference.md §1, 219 @2:55): \"Instead of fading, Liquid Glass\n// objects materialize in and out by gradually modulating the light bending and lensing, ensuring a\n// graceful transition that preserves the optical integrity of the material.\"\n//\n// So an element must not arrive by opacity. Fading it leaves a half-transparent picture OF glass;\n// modulating the lens leaves glass that is not yet bending much light. The difference is visible:\n// a faded element shows the backdrop through a veil, a materialising one shows it undistorted.\n//\n// JS twin of what `u_appear` multiplies in the lens: the lens strength itself, the frost, the\n// body, the dimming layer and the rim's light. Everything that makes the material present, and\n// nothing that makes it opaque.\nimport type { VireGlassOptics } from './material';\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/**\n * `t` of 0 is an element indistinguishable from the backdrop beneath it; 1 is the material at\n * full strength. Between them the glass bends progressively more light.\n */\nexport function applyAppear(optics: VireGlassOptics, t: number): VireGlassOptics {\n const a = clamp01(t);\n if (a === 1) return optics;\n return {\n ...optics,\n refraction: optics.refraction * a,\n blur: optics.blur * a,\n bodyDensity: optics.bodyDensity * a,\n dimming: optics.dimming * a,\n edgeLight: optics.edgeLight * a,\n // A floor on how far the element must stand out from its backdrop (§3). Left alone it holds\n // the element visible at t = 0, which is the opposite of what materialising means.\n presence: optics.presence * a,\n // The spectral trio is lensing too — §1's \"light bending and lensing\" is not only the\n // displacement. The fringe map and the per-channel split are drawn from these, and a filter\n // graph that still paints them is an element that is not there but is casting a rainbow.\n dispersion: optics.dispersion * a,\n iridescence: optics.iridescence * a,\n diffraction: optics.diffraction * a,\n tintStrength: optics.tintStrength * a,\n // Legibility is a REQUIREMENT, not an effect: an element still arriving has no ink on it yet,\n // and holding a demand it cannot meet would darken the body exactly while it should be\n // clearing. It fades with the rest.\n legibility: optics.legibility * a,\n };\n}\n","// The colour that bleeds into a shadow (docs/reference.md §4, 219 @8:22): \"the light reflects,\n// scatters, and bleeds into the shadow as well — much like it would in the physical world.\"\n//\n// JS twin of the lens surface's `vgShadowTint`. A shadow that is simply black leaves an element\n// over colourful content hanging above a grey blob, which reads as pasted on rather than resting\n// there.\n\nconst LUMA: readonly [number, number, number] = [0.2126, 0.7152, 0.0722];\n\nconst luma = (c: readonly [number, number, number]) => c[0] * LUMA[0] + c[1] * LUMA[1] + c[2] * LUMA[2];\n\n/**\n * Hue without lightness, scaled by how much light there is to bleed. Lightness is subtracted out\n * of the hue on purpose: left in, the shadow PALES and loses its depth instead of warming up. And\n * over a nearly black backdrop there is nothing to spill, so the result goes to zero on its own.\n */\nexport function shadowTint(ambient: readonly [number, number, number]): [number, number, number] {\n const peak = Math.max(ambient[0], ambient[1], ambient[2], 0.001);\n const hue: [number, number, number] = [ambient[0] / peak, ambient[1] / peak, ambient[2] / peak];\n const grey = luma(hue);\n const available = luma(ambient);\n return [(hue[0] - grey) * available, (hue[1] - grey) * available, (hue[2] - grey) * available];\n}\n","// The element's response to a finger — PART OF THE MATERIAL, not the app: the springs here\n// describe what the medium feels like, exactly the way ior describes what it looks like through.\n// Keeping them with the consumer would mean web and Android end up with two different glasses\n// under one name.\n//\n// It's the FIELD around the touch point that deforms (vgTouchWarp in sdf.ts), so four values are\n// computed here: where it was touched, how far it's being dragged, how deep the press is, and\n// what wave is currently running.\n//\n// The key point: drag is anchored to the touch BLOB. Pull the right edge and the right edge\n// moves, the left stays put; the finger has area, not a point — the plateau inside `vgTouchWarp`.\n// Scaling the shape's bounding box isn't an option: that would make a rightward pull also bulge\n// the left side, which liquid doesn't do.\n//\n// PRESS is viscous: it dents shallow and releases slowly.\n// DRAG is thick: the body noticeably lags the finger, travel is short, and on release it snaps\n// back quickly with almost no overshoot. A wide, springy overshoot would be liquid jelly, not\n// dense glass.\n// WAVE: touch and lift-off each throw a short ring that decays within a quarter second.\n\nimport { ACCESSIBILITY, SPRING } from './law';\n\nexport type DeformSample = {\n /** Touch point relative to the element's center, CSS pixels. */\n touchX: number;\n touchY: number;\n pullX: number;\n pullY: number;\n press: number;\n active: number;\n /** Wave amplitude in CSS pixels and its phase in turns. */\n waveAmp: number;\n wavePhase: number;\n};\n\n/**\n * `elastic: false` is reduced motion (§9): the spring and the ripple are the material's elastic\n * properties and stop, the press stays and is damped. An element that goes dead under the setting\n * would not be reduced motion — it would be no feedback at all, and the press is what lights the\n * element from within (§5), which is light rather than movement.\n */\nexport type DeformOptions = { elastic?: boolean };\n\nexport function createDeform(options: DeformOptions = {}) {\n let elastic = options.elastic !== false;\n let pressCeiling = elastic ? 1 : ACCESSIBILITY.stillPress;\n let touchX = 0;\n let touchY = 0;\n let targetTouchX = 0;\n let targetTouchY = 0;\n let pullX = 0;\n let pullY = 0;\n let vx = 0;\n let vy = 0;\n let targetX = 0;\n let targetY = 0;\n let held = false;\n let press = 0;\n let active = 0;\n let waveAmp = 0;\n let wavePhase = 0;\n\n /** `x`, `y` — touch point relative to the element's center, CSS pixels. */\n function grab(x: number, y: number, waveStart: number): void {\n // Rest is checked BEFORE the grab: idle() looks at !held, and after `held = true` it's\n // always false — the point would then jump even on a first touch of a resting element.\n const atRest = idle();\n held = true;\n targetTouchX = x;\n targetTouchY = y;\n // On a resting element the finger lands instantly; if it hasn't settled yet from the\n // previous touch, the point travels there over a couple of frames. An instant jump tears the\n // deformation, and on rapid taps that reads as jitter.\n if (atRest) {\n touchX = x;\n touchY = y;\n }\n targetX = 0;\n targetY = 0;\n // Impulses ADD UP rather than restart, and phase isn't reset: resetting it would cut off a\n // running wave mid-period, which is exactly what causes a jolt on rapid clicks.\n if (elastic) waveAmp = Math.min(waveAmp + waveStart, waveStart * SPRING.waveCapOnGrab);\n }\n\n function drag(dx: number, dy: number, limit: number): void {\n if (!held || !elastic) return;\n const len = Math.hypot(dx, dy);\n // Travel saturates rather than clips: near the limit the finger keeps moving while the field\n // barely does — that's how a thick liquid behaves once it hits its limit.\n const scale = len > 1e-3 ? (limit * Math.tanh(len / limit)) / len : 0;\n targetX = dx * scale;\n targetY = dy * scale;\n }\n\n /** Lifting the finger throws a second wave — weaker than the one from touching down. */\n function release(waveStart: number): void {\n held = false;\n targetX = 0;\n targetY = 0;\n if (elastic) waveAmp = Math.min(waveAmp + waveStart, waveStart * SPRING.waveCapOnRelease);\n }\n\n function integrate(dt: number): void {\n const k = held ? SPRING.holdStiffness : SPRING.releaseStiffness;\n const c = held ? SPRING.holdDamping : SPRING.releaseDamping;\n vx += (k * (targetX - pullX) - c * vx) * dt;\n vy += (k * (targetY - pullY) - c * vy) * dt;\n pullX += vx * dt;\n pullY += vy * dt;\n\n const pressTarget = held ? pressCeiling : 0;\n const tau = held ? SPRING.pressAttack : SPRING.pressRelease;\n press += (pressTarget - press) * (1 - Math.exp(-dt / tau));\n active += (pressTarget - active) * (1 - Math.exp(-dt / SPRING.activeFollow));\n\n // The touch point catches up to the finger fast, but not instantly — see grab().\n const follow = 1 - Math.exp(-dt / SPRING.pointFollow);\n touchX += (targetTouchX - touchX) * follow;\n touchY += (targetTouchY - touchY) * follow;\n\n wavePhase += dt * SPRING.waveTurnsPerSecond;\n waveAmp *= Math.exp(-dt / SPRING.waveDecay);\n if (waveAmp < 0.01) waveAmp = 0;\n }\n\n /**\n * The step is fixed, the frame isn't: on a software renderer there can be as few as ten frames\n * a second, and integrating \"however much time passed\" makes the response lag behind real time\n * (and at a large step the spring can even diverge). So time is caught up in sub-steps.\n */\n function step(dt: number): void {\n let rest = Math.min(Math.max(dt, 0), 0.25);\n const h = 1 / 120;\n while (rest > 1e-6) {\n const slice = Math.min(h, rest);\n integrate(slice);\n rest -= slice;\n }\n // Settled means exactly at rest. The `idle()` threshold stops rendering, but the values\n // themselves only approach zero asymptotically, and the element would stay forever very\n // slightly deformed: invisible to the eye, but a frame-vs-reference comparison would flag the\n // mismatch forever.\n if (idle()) {\n pullX = 0;\n pullY = 0;\n vx = 0;\n vy = 0;\n press = 0;\n active = 0;\n wavePhase = 0;\n }\n }\n\n /** Whether the element has settled — the bench uses this to decide whether to keep drawing frames. */\n function idle(): boolean {\n return (\n !held &&\n Math.abs(pullX) < 0.05 &&\n Math.abs(pullY) < 0.05 &&\n Math.hypot(vx, vy) < 0.5 &&\n press < 0.004 &&\n active < 0.004 &&\n waveAmp === 0\n );\n }\n\n /** The setting can be turned on while the page is open, and a deformation already in flight has\n * to stop rather than finish. */\n function setElastic(on: boolean): void {\n if (on === elastic) return;\n elastic = on;\n pressCeiling = on ? 1 : ACCESSIBILITY.stillPress;\n if (!on) {\n pullX = 0;\n pullY = 0;\n vx = 0;\n vy = 0;\n targetX = 0;\n targetY = 0;\n waveAmp = 0;\n wavePhase = 0;\n }\n }\n\n function sample(): DeformSample {\n return { touchX, touchY, pullX, pullY, press, active, waveAmp, wavePhase };\n }\n\n return { grab, drag, release, step, idle, sample, setElastic };\n}\n\n/**\n * RAISING INTO GLASS (reference §5, M 4:10–4:30). A control that isn't glass at rest — a switch\n * knob, a slider handle — stops being frosted under the finger and becomes a lens: whatever is\n * beneath it shows through.\n *\n * Glass comes in FASTER than frost leaves: the two fractions overlap, otherwise midway through\n * the transition the backdrop flashes through the knob for an instant — a hole where the control\n * should be.\n *\n * Lifting off the backdrop is NOT part of this. The shader's `u_lift` is a direction (\"this kind\n * of element sinks in or rises\"), not a magnitude — how far it has already risen is carried by\n * the press value that `u_lift` gets multiplied by. Returning a third fraction here would mean\n * multiplying press by itself — which is exactly what happened the first time the web bench wired\n * this up.\n */\nexport function raiseIntoGlass(press: number): { glass: number; solid: number } {\n const t = press < 0 ? 0 : press > 1 ? 1 : press;\n return { glass: Math.pow(t, 0.6), solid: 1 - Math.pow(t, 1.4) };\n}\n","// System accessibility settings change the material's LAYERS, they don't override it (reference\n// 219 @18:15). So what's adjusted here are EFFECTS, like with toggles: the shader knows nothing\n// about accessibility.\nimport { ACCESSIBILITY, BODY } from './law';\nimport type { VireGlassOptics } from './material';\n\nexport type VireGlassAccessibility = {\n /** The glass becomes more frosted and hides content beneath it more strongly (219 @18:22). */\n reduceTransparency: boolean;\n /** The element moves nearly to black or white and picks up a contrasting border (219 @18:29). */\n increaseContrast: boolean;\n /** The material's springiness is turned off, effects are quieter (219 @18:35). */\n reduceMotion: boolean;\n};\n\nexport const NO_ACCESSIBILITY: VireGlassAccessibility = {\n reduceTransparency: false,\n increaseContrast: false,\n reduceMotion: false,\n};\n\n/** A system setting acts on the whole glass and outranks the material preset (219 @18:45): even\n * a transparent preset moves to the edge of the scale under contrast — the user needs contrast\n * more than they need aesthetics. */\nexport function applyAccessibility(\n optics: VireGlassOptics,\n mods: VireGlassAccessibility = NO_ACCESSIBILITY,\n): VireGlassOptics {\n if (!mods.reduceTransparency && !mods.increaseContrast) return optics;\n const out = { ...optics };\n if (mods.reduceTransparency) {\n out.blur = Math.max(out.blur, ACCESSIBILITY.frostMinDp);\n out.bodyDensity = Math.max(out.bodyDensity, ACCESSIBILITY.obscureMin);\n }\n if (mods.increaseContrast) {\n out.bodyDensity = Math.max(out.bodyDensity, ACCESSIBILITY.contrastDensity);\n out.presence = Math.max(out.presence, ACCESSIBILITY.contrastPresence);\n out.legibility = 1;\n }\n return out;\n}\n\n/**\n * The lightness of the contrasting border §9 asks for, for a body sitting at `tintLuma`.\n *\n * It is the one place the rim stops being an environmental thing. Everywhere else the hairline\n * takes its colour from what is around the element — \"over a yellow flower the rim is yellow\"\n * (§2) — and a hairline the colour of its surroundings is not a border that contrasts with the\n * element it outlines. Under this setting it is measured from the body instead.\n */\nexport function contrastRimLuma(tintLuma: number): number {\n const light = tintLuma < 0.5;\n return light\n ? Math.min(tintLuma + ACCESSIBILITY.contrastRim, BODY.tintLight)\n : Math.max(tintLuma - ACCESSIBILITY.contrastRim, BODY.tintDark);\n}\n\n/**\n * Whether the material may deform. §9: reduced motion \"disables any elastic properties for the\n * material\" — the spring and the ripple. It does NOT disable the response: the press that lights\n * an element from within (§5) is light, not motion, and survives at reduced intensity.\n */\nexport function elasticAllowed(mods: VireGlassAccessibility = NO_ACCESSIBILITY): boolean {\n return !mods.reduceMotion;\n}\n","// User-facing glass transparency scale: iOS 26.1 gave a choice between two options (\"default clear\n// look\" or \"tinted look which increases opacity of the material\"), iOS 27 a continuous slider\n// \"ultra clear → fully tinted\", and apps get it without recompiling.\n//\n// It adjusts EFFECTS, not causes, and that's not a compromise. The cause \"medium density\" is\n// deliberately absent from the model: the glass has no color of its own, `bodyDensity` is derived\n// from absorption through the body and never exceeds 0.03, and the visible density is held by the\n// legibility requirement and scattering. A thickness scale would move a number that isn't visible\n// in the frame (measured by the `check:optics` gate).\nimport { SCALE } from './law';\nimport type { VireGlassOptics } from './material';\n\n/** The point on the scale where the material stays exactly as it is today. */\nexport const GLASS_SCALE_DEFAULT = SCALE.default;\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/**\n * `scale` 0 is ultra clear, 1 is fully tinted. Toward the transparent end, the legibility\n * requirement and the dimming layer are relaxed (the user chose clarity); toward the tinted end,\n * body density grows.\n *\n * Accessibility settings apply AFTER the scale: they set floors, and the user's chosen clarity\n * does not override them (219 @18:45).\n */\nexport function applyGlassScale(optics: VireGlassOptics, scale: number): VireGlassOptics {\n const s = clamp01(scale);\n if (s === GLASS_SCALE_DEFAULT) return optics;\n if (s < GLASS_SCALE_DEFAULT) {\n const k = s / GLASS_SCALE_DEFAULT;\n return { ...optics, legibility: optics.legibility * k, dimming: optics.dimming * k };\n }\n const k = (s - GLASS_SCALE_DEFAULT) / (1 - GLASS_SCALE_DEFAULT);\n return { ...optics, bodyDensity: Math.max(optics.bodyDensity, SCALE.tintedDensity * k) };\n}\n","// Scroll edge effect (reference 219 @8:52) — the SCREEN's job, not the material's: it sits over\n// the content beneath the panel, and the glass sees an already-dimmed backdrop. Only the general\n// rules live here.\nimport { SCROLL_EDGE } from './law';\n\n/**\n * 356 @11:48 names two styles across the system, \"soft and hard\", and says not to mix or stack\n * them. Soft is the default; hard is \"a stronger, more opaque boundary\", mostly macOS, for pinned\n * table headers and controls without backgrounds.\n *\n * Soft has two forms, and which one is in play is not a choice: 219 @9:16 dissolves the content\n * into the background, and @9:33, when darker content scrolls under and the glass itself turns to\n * its dark style, \"the effect intelligently switches to apply a subtle dimming instead\". Same\n * trigger as ink polarity, because it is the same problem — dark content under dark glass merges\n * with it unless the content is pushed down.\n */\nexport type ScrollEdgeStyle = 'dissolve' | 'dim' | 'hard';\n\nexport function scrollEdgeStyle(inkLight: boolean, pinned = false): ScrollEdgeStyle {\n if (pinned) return 'hard';\n return inkLight ? 'dim' : 'dissolve';\n}\n\n/** How far the content has to slide under the panel for the effect to fully engage, dp. */\nexport const SCROLL_EDGE_ENGAGE_DP = SCROLL_EDGE.engageDp;\n\n/**\n * How tall to make the edge, given the bar it sits under. The host positions the element; without\n * this it has to guess a height, and the reference has an answer — measured off the footage rather\n * than chosen (see `SCROLL_EDGE.reachOfBarHeight`).\n */\nexport function scrollEdgeReachDp(barHeightDp: number): number {\n return Math.max(barHeightDp, 0) * SCROLL_EDGE.reachOfBarHeight;\n}\n\n/**\n * Edge strength as a function of scroll. With no scroll, content near the top doesn't slide\n * under the panel yet — so there's no effect (a mail screen at the top: the header sits on a\n * clean background). At the bottom, content slides under it for as long as there's room left\n * to scroll.\n */\nexport function scrollEdgeStrength(side: 'top' | 'bottom', scroll: number, maxScroll: number): number {\n const room = side === 'top' ? scroll : maxScroll - scroll;\n return Math.min(Math.max(room / SCROLL_EDGE_ENGAGE_DP, 0), 1);\n}\n","// Concentricity (reference 219 @7:53): nested shapes share the CENTER of curvature, not the radius.\n// So the inner shape's radius is smaller by exactly the inset — otherwise the corners don't run\n// parallel and the gap between the shapes either gets eaten or spreads apart.\n\n/**\n * Radius of a nested shape: outer radius minus inset. Zero is a square corner; it never goes\n * negative — once the inset exceeds the outer radius, the corner is simply a right angle.\n *\n * `minimum` — a floor on the radius (analogous to `concentric(minimum:)` in SwiftUI): far from\n * the edge, honest concentricity yields zero and the nested element drops out of the family with\n * a sharp corner. The floor breaks concentricity on purpose — here kinship between shapes matters\n * more than a shared center of curvature.\n */\nexport function concentricRadius(outerRadius: number, inset: number, minimum = 0): number {\n return Math.max(outerRadius - inset, Math.max(minimum, 0));\n}\n\n/**\n * The inset at which an inner shape with a given radius stays concentric with the outer one.\n * The inverse problem: the element's radius is known (its size dictates it), and what's needed\n * is the margin around it.\n */\nexport function concentricInset(outerRadius: number, innerRadius: number): number {\n return Math.max(outerRadius - innerRadius, 0);\n}\n\n/**\n * The three shape types the design system builds concentric layouts from (356 @3:42): \"fixed\n * shapes have a constant corner radius. Capsules use a radius that's half the height of the\n * container. And concentric shapes calculate their radius by subtracting padding from the\n * parent's.\"\n *\n * Naming them is the point. 356 @5:09: \"using the three shape types we covered earlier helps keep\n * your components aligned with the system.\" A host that only has a number has to decide what that\n * number means every time the layout changes; a host that has the type gets the number recomputed\n * for it.\n */\nexport type ShapeSpec =\n | { type: 'fixed'; radius: number }\n | { type: 'capsule' }\n /**\n * `fallback` is 356 @6:00: \"a neat trick for managing components that need to work both inside a\n * container and on their own: use a concentric shape with a fallback radius. The concentric value\n * adapts when nested, and the fallback kicks in when the component stands alone.\"\n *\n * It is not the same as `minimum`. A minimum is a floor that applies while nested and breaks\n * concentricity on purpose; a fallback applies only when there is no parent to be concentric\n * with, and never overrides a parent that exists.\n */\n | { type: 'concentric'; inset: number; minimum?: number; fallback?: number };\n\n/** What is around a shape, when there is something around it. */\nexport type ShapeParent = { cornerRadius: number };\n\n/**\n * The corner radius a shape takes in the place it finds itself. `height` is the shape's own, which\n * is what a capsule's radius is measured from; `parent` is absent when the shape stands alone.\n */\nexport function resolveShape(spec: ShapeSpec, height: number, parent?: ShapeParent): number {\n switch (spec.type) {\n case 'fixed':\n return Math.max(spec.radius, 0);\n case 'capsule':\n return Math.max(height, 0) / 2;\n case 'concentric':\n return parent\n ? concentricRadius(parent.cornerRadius, spec.inset, spec.minimum)\n : Math.max(spec.fallback ?? 0, 0);\n }\n}\n\n/**\n * Whether two nested shapes are concentric, and which way they are wrong when they are not.\n * 356 @5:19: \"keep an eye out for corners that feel too pinched — or flared. They can create\n * tension and break the sense of balance.\"\n *\n * Pinched is an inner radius smaller than concentricity asks for: the gap between the corners\n * widens through the curve. Flared is larger: the gap narrows and the corners crowd each other.\n * The defect is visible to the eye and usually goes unnamed, which is why this names it.\n */\nexport function concentricFit(\n outerRadius: number,\n inset: number,\n innerRadius: number,\n tolerance = 0.5,\n): 'concentric' | 'pinched' | 'flared' {\n const want = concentricRadius(outerRadius, inset);\n if (innerRadius < want - tolerance) return 'pinched';\n if (innerRadius > want + tolerance) return 'flared';\n return 'concentric';\n}\n","import { CONFIRMATIONS, FLIP_LUMA, RETURN_LUMA, type BackdropSample } from './adaptation';\n\n/**\n * GLASS GROUP ESTIMATE — the pure part of the surface group (`glass-group.tsx`).\n *\n * Everything that decides lives here: the lightness plane from members' samples, the group's\n * aggregates, the polarity requirement and its hysteresis. None of these rules need React or the\n * native layer, so they live apart from the component and are checked by a unit test.\n */\n\nexport type GroupMember = { x: number; y: number; sample: BackdropSample; legibility: number };\n\n/** Group estimate: center and average lightness, slope along x, the lightest spot, and the\n * measurement values shared by everyone — [variegation, lo, hi, r, g, b]. */\nexport type GroupPlane = {\n mx: number;\n ml: number;\n slope: number;\n base: number;\n rest: number[];\n};\n\n/** Input to the polarity decision: the lightness the group judges by, and the strictest of the requirements. */\nexport type GroupDecision = { decisive: number; legibility: number };\n\n/** Fraction of a new sample in the smoothed estimate. Samples come in from every member, so the\n * group collects roughly twenty a second — a quarter is enough for a response within ~200 ms. */\nexport const SMOOTH = 0.25;\n\n/** The remainder below which smoothing snaps the value to the sample. An exponential never\n * actually reaches its target, and the estimate kept \"changing\" by denormals for two more\n * minutes after the backdrop had settled. */\nexport const SETTLE = 1e-6;\n\n/**\n * Fraction of the slope that reaches the members.\n *\n * A plane through four points nearly interpolates them: right at a black/white border, buttons\n * were getting 0.0, 0.3, 0.7 and 1.0 — that is, exactly their own individual values, and the\n * group fell apart into independent elements again. Density is set by the group's LIGHTEST spot;\n * the slope only lets it off slightly at the dark end.\n */\nexport const GRADIENT = 0.35;\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/**\n * Lightness plane from members' samples. The slope is a least-squares fit along x: the group's\n * members stand in a row, and one axis is enough — there's no vertical spread among them. The\n * coordinates are centered, so the normal equations decouple.\n */\nfunction fitLuma(members: readonly GroupMember[]): {\n mx: number;\n ml: number;\n slope: number;\n} {\n const n = members.length;\n let mx = 0;\n let ml = 0;\n for (const m of members) {\n mx += m.x;\n ml += m.sample.luma;\n }\n mx /= n;\n ml /= n;\n let sxx = 0;\n let sxl = 0;\n for (const m of members) {\n const dx = m.x - mx;\n sxx += dx * dx;\n sxl += dx * m.sample.luma;\n }\n return { mx, ml, slope: sxx > 1e-6 ? sxl / sxx : 0 };\n}\n\n/** Group estimate from its members. An empty group has nothing to estimate — that's `null`. */\nexport function aggregate(\n members: readonly GroupMember[],\n): { plane: GroupPlane; decision: GroupDecision } | null {\n if (members.length === 0) return null;\n\n const { mx, ml, slope } = fitLuma(members);\n let busy = 0;\n let lo = 1;\n let hi = 0;\n // The group's lightest SPOT: density for everyone is computed from it. Otherwise, right at a\n // black/white border the group would hold to the average, a light ink would never darken\n // against anything, and it would drown over the light half.\n let base = 0;\n let r = 0;\n let g = 0;\n let b = 0;\n // The legibility requirement is the group's strictest: polarity is one for all, and going by\n // the weakest requirement would leave a neighbor with pickier optics illegible.\n let legibility = 0;\n for (const m of members) {\n busy = Math.max(busy, m.sample.busy);\n lo = Math.min(lo, m.sample.lo);\n hi = Math.max(hi, m.sample.hi);\n base = Math.max(base, m.sample.luma);\n r += m.sample.r;\n g += m.sample.g;\n b += m.sample.b;\n legibility = Math.max(legibility, m.legibility);\n }\n const k = members.length;\n return {\n plane: { mx, ml, slope, base, rest: [busy, lo, hi, r / k, g / k, b / k] },\n // The polarity decision is one per group, by its AVERAGE lightness. Deciding on its own,\n // each button used to end up a different color over a busy backdrop.\n decision: { decisive: ml * 0.75 + hi * 0.25, legibility },\n };\n}\n\n/**\n * Smoothing runs over samples, not frames: the native lens smooths ITS OWN estimate, and the\n * group estimate overrides it with an already-finished value. Without this the group's tone\n * changed in steps — the probe's 180 ms cadence is too coarse for the adaptation to read as\n * unnoticeable.\n */\nexport function smoothPlane(prev: GroupPlane | null, next: GroupPlane): GroupPlane {\n if (!prev) return next;\n const e = (was: number, now: number) =>\n Math.abs(now - was) < SETTLE ? now : was + (now - was) * SMOOTH;\n const ml = e(prev.ml, next.ml);\n const slope = e(prev.slope, next.slope);\n const base = e(prev.base, next.base);\n const rest = next.rest.map((v, i) => e(prev.rest[i], v));\n // A converged estimate is handed back as the same object: otherwise `setPlane` would repaint\n // the group on every sample — twenty times a second, even on a still screen.\n const same =\n next.mx === prev.mx &&\n ml === prev.ml &&\n slope === prev.slope &&\n base === prev.base &&\n rest.every((v, i) => v === prev.rest[i]);\n return same ? prev : { mx: next.mx, ml, slope, base, rest };\n}\n\n/** The sample a member at position `x` hands its own lens — in the order of its prop. */\nexport function probeValuesAt(plane: GroupPlane, x: number): number[] {\n // Down from the group's lightest spot — by exactly as much as that spot is darker along the\n // plane, and even then only a GRADIENT fraction of it. This lets the dark end off on density,\n // without canceling it: the group stays a single material.\n const here = plane.ml + plane.slope * (x - plane.mx);\n const luma = clamp01(plane.base - (plane.base - here) * GRADIENT);\n // The slope doesn't carry over into the lens: the group's is measured in screen dp, while the\n // shader multiplies `u_probeSlope` by a normalized position within the element — different\n // units.\n return [luma, plane.rest[0], plane.rest[1], plane.rest[2], 0, 0, plane.rest[3], plane.rest[4], plane.rest[5]];\n}\n\n/** Whether the group needs to flip ink polarity given the current one (1 light, 0 dark). */\nfunction wantsPolarityChange(decision: GroupDecision, polarity: number): boolean {\n return polarity === 1 ? decision.decisive >= FLIP_LUMA : decision.decisive < RETURN_LUMA;\n}\n\nexport type GroupSink = {\n /** The group estimate has been recomputed: members take their values from it. */\n plane: (plane: GroupPlane | null) => void;\n /** The group flips ink polarity for everyone at once. */\n flip: (polarity: number) => void;\n};\n\nexport type GroupState = {\n report: (\n id: string,\n x: number,\n y: number,\n sample: BackdropSample,\n legibility: number,\n ) => void;\n release: (id: string) => void;\n plane: () => GroupPlane | null;\n polarity: () => number;\n confirmations: () => number;\n};\n\n/** Group state: members, the smoothed estimate, and polarity hysteresis. */\nexport function createGroupState(sink: GroupSink): GroupState {\n const members = new Map<string, GroupMember>();\n let plane: GroupPlane | null = null;\n let pending = 0;\n let polarity = 1;\n\n const measure = (): GroupDecision | null => {\n const next = aggregate([...members.values()]);\n if (!next) {\n pending = 0;\n plane = null;\n sink.plane(null);\n return null;\n }\n plane = smoothPlane(plane, next.plane);\n sink.plane(plane);\n return next.decision;\n };\n\n return {\n report(id, x, y, sample, legibility) {\n members.set(id, { x, y, sample, legibility });\n const decision = measure();\n if (!decision) return;\n if (!wantsPolarityChange(decision, polarity)) {\n pending = 0;\n return;\n }\n pending += 1;\n if (pending < CONFIRMATIONS) return;\n pending = 0;\n polarity = polarity === 1 ? 0 : 1;\n sink.flip(polarity);\n },\n // Only recomputes the estimate: losing a member isn't a backdrop sample and can't count as a\n // polarity confirmation — unmounting the group used to rack up CONFIRMATIONS instantly and\n // trigger a recolor nobody was left to cancel.\n release(id) {\n if (members.delete(id)) measure();\n },\n plane: () => plane,\n polarity: () => polarity,\n confirmations: () => pending,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/accent.ts","../src/appear.ts","../src/shadow-tint.ts","../src/touch-response.ts","../src/accessibility.ts","../src/glass-scale.ts","../src/scroll-edge.ts","../src/concentric.ts","../src/group-model.ts"],"names":["clamp01","k","luma"],"mappings":"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","file":"chunk-CTTZNZFV.js","sourcesContent":["// Tinting (docs/reference.md §7, 219 @16:31): \"Selecting a color generates a range of tones that\n// are mapped to content brightness underneath the tinted element. It draws inspiration from how\n// colored glass works in reality: changing its hue, brightness and saturation depending on what's\n// behind without deviating too much from the intended color.\"\n//\n// So a tint is not a fill. @17:03 is explicit about what a fill costs: \"it is completely opaque\n// and breaks the visual character of Liquid Glass.\" The colour has to be a property of the medium,\n// and the content has to keep coming through it.\n//\n// JS twin of the accent block in the lens shader.\nimport type { VireGlassAccent } from './adapters';\nimport { ACCENT } from './law';\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/**\n * The tone this accent takes over a backdrop of the given lightness — deeper over dark, lighter\n * over light, which is what \"a range of tones mapped to content brightness\" means.\n *\n * @17:21: tinting is for the primary action, not for everything. \"When every element is tinted,\n * nothing stands out.\" That is the host's call, not this function's.\n */\nexport function accentTone(\n color: readonly [number, number, number],\n backdropLuma: number,\n): [number, number, number] {\n const k = ACCENT.deep + (ACCENT.light - ACCENT.deep) * clamp01(backdropLuma);\n return [clamp01(color[0] * k), clamp01(color[1] * k), clamp01(color[2] * k)];\n}\n\n/** The fraction of the medium the colour occupies, which is what keeps the content showing\n * through it rather than being covered by it. */\nexport function accentAmount(accent: VireGlassAccent): number {\n return clamp01(accent.amount ?? ACCENT.amount);\n}\n","// Materialising in and out (docs/reference.md §1, 219 @2:55): \"Instead of fading, Liquid Glass\n// objects materialize in and out by gradually modulating the light bending and lensing, ensuring a\n// graceful transition that preserves the optical integrity of the material.\"\n//\n// So an element must not arrive by opacity. Fading it leaves a half-transparent picture OF glass;\n// modulating the lens leaves glass that is not yet bending much light. The difference is visible:\n// a faded element shows the backdrop through a veil, a materialising one shows it undistorted.\n//\n// JS twin of what `u_appear` multiplies in the lens: the lens strength itself, the frost, the\n// body, the dimming layer and the rim's light. Everything that makes the material present, and\n// nothing that makes it opaque.\nimport type { VireGlassOptics } from './material';\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/**\n * `t` of 0 is an element indistinguishable from the backdrop beneath it; 1 is the material at\n * full strength. Between them the glass bends progressively more light.\n */\nexport function applyAppear(optics: VireGlassOptics, t: number): VireGlassOptics {\n const a = clamp01(t);\n if (a === 1) return optics;\n return {\n ...optics,\n refraction: optics.refraction * a,\n blur: optics.blur * a,\n bodyDensity: optics.bodyDensity * a,\n dimming: optics.dimming * a,\n edgeLight: optics.edgeLight * a,\n // A floor on how far the element must stand out from its backdrop (§3). Left alone it holds\n // the element visible at t = 0, which is the opposite of what materialising means.\n presence: optics.presence * a,\n // The spectral trio is lensing too — §1's \"light bending and lensing\" is not only the\n // displacement. The fringe map and the per-channel split are drawn from these, and a filter\n // graph that still paints them is an element that is not there but is casting a rainbow.\n dispersion: optics.dispersion * a,\n iridescence: optics.iridescence * a,\n diffraction: optics.diffraction * a,\n tintStrength: optics.tintStrength * a,\n // Legibility is a REQUIREMENT, not an effect: an element still arriving has no ink on it yet,\n // and holding a demand it cannot meet would darken the body exactly while it should be\n // clearing. It fades with the rest.\n legibility: optics.legibility * a,\n };\n}\n","// The colour that bleeds into a shadow (docs/reference.md §4, 219 @8:22): \"the light reflects,\n// scatters, and bleeds into the shadow as well — much like it would in the physical world.\"\n//\n// JS twin of the lens surface's `vgShadowTint`. A shadow that is simply black leaves an element\n// over colourful content hanging above a grey blob, which reads as pasted on rather than resting\n// there.\n\nconst LUMA: readonly [number, number, number] = [0.2126, 0.7152, 0.0722];\n\nconst luma = (c: readonly [number, number, number]) => c[0] * LUMA[0] + c[1] * LUMA[1] + c[2] * LUMA[2];\n\n/**\n * Hue without lightness, scaled by how much light there is to bleed. Lightness is subtracted out\n * of the hue on purpose: left in, the shadow PALES and loses its depth instead of warming up. And\n * over a nearly black backdrop there is nothing to spill, so the result goes to zero on its own.\n */\nexport function shadowTint(ambient: readonly [number, number, number]): [number, number, number] {\n const peak = Math.max(ambient[0], ambient[1], ambient[2], 0.001);\n const hue: [number, number, number] = [ambient[0] / peak, ambient[1] / peak, ambient[2] / peak];\n const grey = luma(hue);\n const available = luma(ambient);\n return [(hue[0] - grey) * available, (hue[1] - grey) * available, (hue[2] - grey) * available];\n}\n","// The element's response to a finger — PART OF THE MATERIAL, not the app: the springs here\n// describe what the medium feels like, exactly the way ior describes what it looks like through.\n// Keeping them with the consumer would mean web and Android end up with two different glasses\n// under one name.\n//\n// It's the FIELD around the touch point that deforms (vgTouchWarp in sdf.ts), so four values are\n// computed here: where it was touched, how far it's being dragged, how deep the press is, and\n// what wave is currently running.\n//\n// The key point: drag is anchored to the touch BLOB. Pull the right edge and the right edge\n// moves, the left stays put; the finger has area, not a point — the plateau inside `vgTouchWarp`.\n// Scaling the shape's bounding box isn't an option: that would make a rightward pull also bulge\n// the left side, which liquid doesn't do.\n//\n// PRESS is viscous: it dents shallow and releases slowly.\n// DRAG is thick: the body noticeably lags the finger, travel is short, and on release it snaps\n// back quickly with almost no overshoot. A wide, springy overshoot would be liquid jelly, not\n// dense glass.\n// WAVE: touch and lift-off each throw a short ring that decays within a quarter second.\n\nimport { ACCESSIBILITY, SPRING } from './law';\n\nexport type DeformSample = {\n /** Touch point relative to the element's center, CSS pixels. */\n touchX: number;\n touchY: number;\n pullX: number;\n pullY: number;\n press: number;\n active: number;\n /** Wave amplitude in CSS pixels and its phase in turns. */\n waveAmp: number;\n wavePhase: number;\n};\n\n/**\n * `elastic: false` is reduced motion (§9): the spring and the ripple are the material's elastic\n * properties and stop, the press stays and is damped. An element that goes dead under the setting\n * would not be reduced motion — it would be no feedback at all, and the press is what lights the\n * element from within (§5), which is light rather than movement.\n */\nexport type DeformOptions = { elastic?: boolean };\n\nexport function createDeform(options: DeformOptions = {}) {\n let elastic = options.elastic !== false;\n let pressCeiling = elastic ? 1 : ACCESSIBILITY.stillPress;\n let touchX = 0;\n let touchY = 0;\n let targetTouchX = 0;\n let targetTouchY = 0;\n let pullX = 0;\n let pullY = 0;\n let vx = 0;\n let vy = 0;\n let targetX = 0;\n let targetY = 0;\n let held = false;\n let press = 0;\n let active = 0;\n let waveAmp = 0;\n let wavePhase = 0;\n\n /** `x`, `y` — touch point relative to the element's center, CSS pixels. */\n function grab(x: number, y: number, waveStart: number): void {\n // Rest is checked BEFORE the grab: idle() looks at !held, and after `held = true` it's\n // always false — the point would then jump even on a first touch of a resting element.\n const atRest = idle();\n held = true;\n targetTouchX = x;\n targetTouchY = y;\n // On a resting element the finger lands instantly; if it hasn't settled yet from the\n // previous touch, the point travels there over a couple of frames. An instant jump tears the\n // deformation, and on rapid taps that reads as jitter.\n if (atRest) {\n touchX = x;\n touchY = y;\n }\n targetX = 0;\n targetY = 0;\n // Impulses ADD UP rather than restart, and phase isn't reset: resetting it would cut off a\n // running wave mid-period, which is exactly what causes a jolt on rapid clicks.\n if (elastic) waveAmp = Math.min(waveAmp + waveStart, waveStart * SPRING.waveCapOnGrab);\n }\n\n function drag(dx: number, dy: number, limit: number): void {\n if (!held || !elastic) return;\n const len = Math.hypot(dx, dy);\n // Travel saturates rather than clips: near the limit the finger keeps moving while the field\n // barely does — that's how a thick liquid behaves once it hits its limit.\n const scale = len > 1e-3 ? (limit * Math.tanh(len / limit)) / len : 0;\n targetX = dx * scale;\n targetY = dy * scale;\n }\n\n /** Lifting the finger throws a second wave — weaker than the one from touching down. */\n function release(waveStart: number): void {\n held = false;\n targetX = 0;\n targetY = 0;\n if (elastic) waveAmp = Math.min(waveAmp + waveStart, waveStart * SPRING.waveCapOnRelease);\n }\n\n function integrate(dt: number): void {\n const k = held ? SPRING.holdStiffness : SPRING.releaseStiffness;\n const c = held ? SPRING.holdDamping : SPRING.releaseDamping;\n vx += (k * (targetX - pullX) - c * vx) * dt;\n vy += (k * (targetY - pullY) - c * vy) * dt;\n pullX += vx * dt;\n pullY += vy * dt;\n\n const pressTarget = held ? pressCeiling : 0;\n const tau = held ? SPRING.pressAttack : SPRING.pressRelease;\n press += (pressTarget - press) * (1 - Math.exp(-dt / tau));\n active += (pressTarget - active) * (1 - Math.exp(-dt / SPRING.activeFollow));\n\n // The touch point catches up to the finger fast, but not instantly — see grab().\n const follow = 1 - Math.exp(-dt / SPRING.pointFollow);\n touchX += (targetTouchX - touchX) * follow;\n touchY += (targetTouchY - touchY) * follow;\n\n wavePhase += dt * SPRING.waveTurnsPerSecond;\n waveAmp *= Math.exp(-dt / SPRING.waveDecay);\n if (waveAmp < 0.01) waveAmp = 0;\n }\n\n /**\n * The step is fixed, the frame isn't: on a software renderer there can be as few as ten frames\n * a second, and integrating \"however much time passed\" makes the response lag behind real time\n * (and at a large step the spring can even diverge). So time is caught up in sub-steps.\n */\n function step(dt: number): void {\n let rest = Math.min(Math.max(dt, 0), 0.25);\n const h = 1 / 120;\n while (rest > 1e-6) {\n const slice = Math.min(h, rest);\n integrate(slice);\n rest -= slice;\n }\n // Settled means exactly at rest. The `idle()` threshold stops rendering, but the values\n // themselves only approach zero asymptotically, and the element would stay forever very\n // slightly deformed: invisible to the eye, but a frame-vs-reference comparison would flag the\n // mismatch forever.\n if (idle()) {\n pullX = 0;\n pullY = 0;\n vx = 0;\n vy = 0;\n press = 0;\n active = 0;\n wavePhase = 0;\n }\n }\n\n /** Whether the element has settled — the bench uses this to decide whether to keep drawing frames. */\n function idle(): boolean {\n return (\n !held &&\n Math.abs(pullX) < 0.05 &&\n Math.abs(pullY) < 0.05 &&\n Math.hypot(vx, vy) < 0.5 &&\n press < 0.004 &&\n active < 0.004 &&\n waveAmp === 0\n );\n }\n\n /** The setting can be turned on while the page is open, and a deformation already in flight has\n * to stop rather than finish. */\n function setElastic(on: boolean): void {\n if (on === elastic) return;\n elastic = on;\n pressCeiling = on ? 1 : ACCESSIBILITY.stillPress;\n if (!on) {\n pullX = 0;\n pullY = 0;\n vx = 0;\n vy = 0;\n targetX = 0;\n targetY = 0;\n waveAmp = 0;\n wavePhase = 0;\n }\n }\n\n function sample(): DeformSample {\n return { touchX, touchY, pullX, pullY, press, active, waveAmp, wavePhase };\n }\n\n return { grab, drag, release, step, idle, sample, setElastic };\n}\n\n/**\n * RAISING INTO GLASS (reference §5, M 4:10–4:30). A control that isn't glass at rest — a switch\n * knob, a slider handle — stops being frosted under the finger and becomes a lens: whatever is\n * beneath it shows through.\n *\n * Glass comes in FASTER than frost leaves: the two fractions overlap, otherwise midway through\n * the transition the backdrop flashes through the knob for an instant — a hole where the control\n * should be.\n *\n * Lifting off the backdrop is NOT part of this. The shader's `u_lift` is a direction (\"this kind\n * of element sinks in or rises\"), not a magnitude — how far it has already risen is carried by\n * the press value that `u_lift` gets multiplied by. Returning a third fraction here would mean\n * multiplying press by itself — which is exactly what happened the first time the web bench wired\n * this up.\n */\nexport function raiseIntoGlass(press: number): { glass: number; solid: number } {\n const t = press < 0 ? 0 : press > 1 ? 1 : press;\n return { glass: Math.pow(t, 0.6), solid: 1 - Math.pow(t, 1.4) };\n}\n","// System accessibility settings change the material's LAYERS, they don't override it (reference\n// 219 @18:15). So what's adjusted here are EFFECTS, like with toggles: the shader knows nothing\n// about accessibility.\nimport { ACCESSIBILITY, BODY } from './law';\nimport type { VireGlassOptics } from './material';\n\nexport type VireGlassAccessibility = {\n /** The glass becomes more frosted and hides content beneath it more strongly (219 @18:22). */\n reduceTransparency: boolean;\n /** The element moves nearly to black or white and picks up a contrasting border (219 @18:29). */\n increaseContrast: boolean;\n /** The material's springiness is turned off, effects are quieter (219 @18:35). */\n reduceMotion: boolean;\n};\n\nexport const NO_ACCESSIBILITY: VireGlassAccessibility = {\n reduceTransparency: false,\n increaseContrast: false,\n reduceMotion: false,\n};\n\n/** A system setting acts on the whole glass and outranks the material preset (219 @18:45): even\n * a transparent preset moves to the edge of the scale under contrast — the user needs contrast\n * more than they need aesthetics. */\nexport function applyAccessibility(\n optics: VireGlassOptics,\n mods: VireGlassAccessibility = NO_ACCESSIBILITY,\n): VireGlassOptics {\n if (!mods.reduceTransparency && !mods.increaseContrast) return optics;\n const out = { ...optics };\n if (mods.reduceTransparency) {\n out.blur = Math.max(out.blur, ACCESSIBILITY.frostMinDp);\n out.bodyDensity = Math.max(out.bodyDensity, ACCESSIBILITY.obscureMin);\n }\n if (mods.increaseContrast) {\n out.bodyDensity = Math.max(out.bodyDensity, ACCESSIBILITY.contrastDensity);\n out.presence = Math.max(out.presence, ACCESSIBILITY.contrastPresence);\n out.legibility = 1;\n }\n return out;\n}\n\n/**\n * The lightness of the contrasting border §9 asks for, for a body sitting at `tintLuma`.\n *\n * It is the one place the rim stops being an environmental thing. Everywhere else the hairline\n * takes its colour from what is around the element — \"over a yellow flower the rim is yellow\"\n * (§2) — and a hairline the colour of its surroundings is not a border that contrasts with the\n * element it outlines. Under this setting it is measured from the body instead.\n */\nexport function contrastRimLuma(tintLuma: number): number {\n const light = tintLuma < 0.5;\n return light\n ? Math.min(tintLuma + ACCESSIBILITY.contrastRim, BODY.tintLight)\n : Math.max(tintLuma - ACCESSIBILITY.contrastRim, BODY.tintDark);\n}\n\n/**\n * Whether the material may deform. §9: reduced motion \"disables any elastic properties for the\n * material\" — the spring and the ripple. It does NOT disable the response: the press that lights\n * an element from within (§5) is light, not motion, and survives at reduced intensity.\n */\nexport function elasticAllowed(mods: VireGlassAccessibility = NO_ACCESSIBILITY): boolean {\n return !mods.reduceMotion;\n}\n","// User-facing glass transparency scale: iOS 26.1 gave a choice between two options (\"default clear\n// look\" or \"tinted look which increases opacity of the material\"), iOS 27 a continuous slider\n// \"ultra clear → fully tinted\", and apps get it without recompiling.\n//\n// It adjusts EFFECTS, not causes, and that's not a compromise. The cause \"medium density\" is\n// deliberately absent from the model: the glass has no color of its own, `bodyDensity` is derived\n// from absorption through the body and never exceeds 0.03, and the visible density is held by the\n// legibility requirement and scattering. A thickness scale would move a number that isn't visible\n// in the frame (measured by the `check:optics` gate).\nimport { SCALE } from './law';\nimport type { VireGlassOptics } from './material';\n\n/** The point on the scale where the material stays exactly as it is today. */\nexport const GLASS_SCALE_DEFAULT = SCALE.default;\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/**\n * `scale` 0 is ultra clear, 1 is fully tinted. Toward the transparent end, the legibility\n * requirement and the dimming layer are relaxed (the user chose clarity); toward the tinted end,\n * body density grows.\n *\n * Accessibility settings apply AFTER the scale: they set floors, and the user's chosen clarity\n * does not override them (219 @18:45).\n */\nexport function applyGlassScale(optics: VireGlassOptics, scale: number): VireGlassOptics {\n const s = clamp01(scale);\n if (s === GLASS_SCALE_DEFAULT) return optics;\n if (s < GLASS_SCALE_DEFAULT) {\n const k = s / GLASS_SCALE_DEFAULT;\n return { ...optics, legibility: optics.legibility * k, dimming: optics.dimming * k };\n }\n const k = (s - GLASS_SCALE_DEFAULT) / (1 - GLASS_SCALE_DEFAULT);\n return { ...optics, bodyDensity: Math.max(optics.bodyDensity, SCALE.tintedDensity * k) };\n}\n","// Scroll edge effect (reference 219 @8:52) — the SCREEN's job, not the material's: it sits over\n// the content beneath the panel, and the glass sees an already-dimmed backdrop. Only the general\n// rules live here.\nimport { SCROLL_EDGE } from './law';\n\n/**\n * 356 @11:48 names two styles across the system, \"soft and hard\", and says not to mix or stack\n * them. Soft is the default; hard is \"a stronger, more opaque boundary\", mostly macOS, for pinned\n * table headers and controls without backgrounds.\n *\n * Soft has two forms, and which one is in play is not a choice: 219 @9:16 dissolves the content\n * into the background, and @9:33, when darker content scrolls under and the glass itself turns to\n * its dark style, \"the effect intelligently switches to apply a subtle dimming instead\". Same\n * trigger as ink polarity, because it is the same problem — dark content under dark glass merges\n * with it unless the content is pushed down.\n */\nexport type ScrollEdgeStyle = 'dissolve' | 'dim' | 'hard';\n\nexport function scrollEdgeStyle(inkLight: boolean, pinned = false): ScrollEdgeStyle {\n if (pinned) return 'hard';\n return inkLight ? 'dim' : 'dissolve';\n}\n\n/** How far the content has to slide under the panel for the effect to fully engage, dp. */\nexport const SCROLL_EDGE_ENGAGE_DP = SCROLL_EDGE.engageDp;\n\n/**\n * How tall to make the edge, given the bar it sits under. The host positions the element; without\n * this it has to guess a height, and the reference has an answer — measured off the footage rather\n * than chosen (see `SCROLL_EDGE.reachOfBarHeight`).\n */\nexport function scrollEdgeReachDp(barHeightDp: number): number {\n return Math.max(barHeightDp, 0) * SCROLL_EDGE.reachOfBarHeight;\n}\n\n/**\n * Edge strength as a function of scroll. With no scroll, content near the top doesn't slide\n * under the panel yet — so there's no effect (a mail screen at the top: the header sits on a\n * clean background). At the bottom, content slides under it for as long as there's room left\n * to scroll.\n */\nexport function scrollEdgeStrength(side: 'top' | 'bottom', scroll: number, maxScroll: number): number {\n const room = side === 'top' ? scroll : maxScroll - scroll;\n return Math.min(Math.max(room / SCROLL_EDGE_ENGAGE_DP, 0), 1);\n}\n","// Concentricity (reference 219 @7:53): nested shapes share the CENTER of curvature, not the radius.\n// So the inner shape's radius is smaller by exactly the inset — otherwise the corners don't run\n// parallel and the gap between the shapes either gets eaten or spreads apart.\n\n/**\n * Radius of a nested shape: outer radius minus inset. Zero is a square corner; it never goes\n * negative — once the inset exceeds the outer radius, the corner is simply a right angle.\n *\n * `minimum` — a floor on the radius (analogous to `concentric(minimum:)` in SwiftUI): far from\n * the edge, honest concentricity yields zero and the nested element drops out of the family with\n * a sharp corner. The floor breaks concentricity on purpose — here kinship between shapes matters\n * more than a shared center of curvature.\n */\nexport function concentricRadius(outerRadius: number, inset: number, minimum = 0): number {\n return Math.max(outerRadius - inset, Math.max(minimum, 0));\n}\n\n/**\n * The inset at which an inner shape with a given radius stays concentric with the outer one.\n * The inverse problem: the element's radius is known (its size dictates it), and what's needed\n * is the margin around it.\n */\nexport function concentricInset(outerRadius: number, innerRadius: number): number {\n return Math.max(outerRadius - innerRadius, 0);\n}\n\n/**\n * The three shape types the design system builds concentric layouts from (356 @3:42): \"fixed\n * shapes have a constant corner radius. Capsules use a radius that's half the height of the\n * container. And concentric shapes calculate their radius by subtracting padding from the\n * parent's.\"\n *\n * Naming them is the point. 356 @5:09: \"using the three shape types we covered earlier helps keep\n * your components aligned with the system.\" A host that only has a number has to decide what that\n * number means every time the layout changes; a host that has the type gets the number recomputed\n * for it.\n */\nexport type ShapeSpec =\n | { type: 'fixed'; radius: number }\n | { type: 'capsule' }\n /**\n * `fallback` is 356 @6:00: \"a neat trick for managing components that need to work both inside a\n * container and on their own: use a concentric shape with a fallback radius. The concentric value\n * adapts when nested, and the fallback kicks in when the component stands alone.\"\n *\n * It is not the same as `minimum`. A minimum is a floor that applies while nested and breaks\n * concentricity on purpose; a fallback applies only when there is no parent to be concentric\n * with, and never overrides a parent that exists.\n */\n | { type: 'concentric'; inset: number; minimum?: number; fallback?: number };\n\n/** What is around a shape, when there is something around it. */\nexport type ShapeParent = { cornerRadius: number };\n\n/**\n * The corner radius a shape takes in the place it finds itself. `height` is the shape's own, which\n * is what a capsule's radius is measured from; `parent` is absent when the shape stands alone.\n */\nexport function resolveShape(spec: ShapeSpec, height: number, parent?: ShapeParent): number {\n switch (spec.type) {\n case 'fixed':\n return Math.max(spec.radius, 0);\n case 'capsule':\n return Math.max(height, 0) / 2;\n case 'concentric':\n return parent\n ? concentricRadius(parent.cornerRadius, spec.inset, spec.minimum)\n : Math.max(spec.fallback ?? 0, 0);\n }\n}\n\n/**\n * Whether two nested shapes are concentric, and which way they are wrong when they are not.\n * 356 @5:19: \"keep an eye out for corners that feel too pinched — or flared. They can create\n * tension and break the sense of balance.\"\n *\n * Pinched is an inner radius smaller than concentricity asks for: the gap between the corners\n * widens through the curve. Flared is larger: the gap narrows and the corners crowd each other.\n * The defect is visible to the eye and usually goes unnamed, which is why this names it.\n */\nexport function concentricFit(\n outerRadius: number,\n inset: number,\n innerRadius: number,\n tolerance = 0.5,\n): 'concentric' | 'pinched' | 'flared' {\n const want = concentricRadius(outerRadius, inset);\n if (innerRadius < want - tolerance) return 'pinched';\n if (innerRadius > want + tolerance) return 'flared';\n return 'concentric';\n}\n","import { CONFIRMATIONS, FLIP_LUMA, RETURN_LUMA, type BackdropSample } from './adaptation';\n\n/**\n * GLASS GROUP ESTIMATE — the pure part of the surface group (`glass-group.tsx`).\n *\n * Everything that decides lives here: the lightness plane from members' samples, the group's\n * aggregates, the polarity requirement and its hysteresis. None of these rules need React or the\n * native layer, so they live apart from the component and are checked by a unit test.\n */\n\nexport type GroupMember = { x: number; y: number; sample: BackdropSample; legibility: number };\n\n/** Group estimate: center and average lightness, slope along x, the lightest spot, and the\n * measurement values shared by everyone — [variegation, lo, hi, r, g, b]. */\nexport type GroupPlane = {\n mx: number;\n ml: number;\n slope: number;\n base: number;\n rest: number[];\n};\n\n/** Input to the polarity decision: the lightness the group judges by, and the strictest of the requirements. */\nexport type GroupDecision = { decisive: number; legibility: number };\n\n/** Fraction of a new sample in the smoothed estimate. Samples come in from every member, so the\n * group collects roughly twenty a second — a quarter is enough for a response within ~200 ms. */\nexport const SMOOTH = 0.25;\n\n/** The remainder below which smoothing snaps the value to the sample. An exponential never\n * actually reaches its target, and the estimate kept \"changing\" by denormals for two more\n * minutes after the backdrop had settled. */\nexport const SETTLE = 1e-6;\n\n/**\n * Fraction of the slope that reaches the members.\n *\n * A plane through four points nearly interpolates them: right at a black/white border, buttons\n * were getting 0.0, 0.3, 0.7 and 1.0 — that is, exactly their own individual values, and the\n * group fell apart into independent elements again. Density is set by the group's LIGHTEST spot;\n * the slope only lets it off slightly at the dark end.\n */\nexport const GRADIENT = 0.35;\n\nconst clamp01 = (v: number) => (v < 0 ? 0 : v > 1 ? 1 : v);\n\n/**\n * Lightness plane from members' samples. The slope is a least-squares fit along x: the group's\n * members stand in a row, and one axis is enough — there's no vertical spread among them. The\n * coordinates are centered, so the normal equations decouple.\n */\nfunction fitLuma(members: readonly GroupMember[]): {\n mx: number;\n ml: number;\n slope: number;\n} {\n const n = members.length;\n let mx = 0;\n let ml = 0;\n for (const m of members) {\n mx += m.x;\n ml += m.sample.luma;\n }\n mx /= n;\n ml /= n;\n let sxx = 0;\n let sxl = 0;\n for (const m of members) {\n const dx = m.x - mx;\n sxx += dx * dx;\n sxl += dx * m.sample.luma;\n }\n return { mx, ml, slope: sxx > 1e-6 ? sxl / sxx : 0 };\n}\n\n/** Group estimate from its members. An empty group has nothing to estimate — that's `null`. */\nexport function aggregate(\n members: readonly GroupMember[],\n): { plane: GroupPlane; decision: GroupDecision } | null {\n if (members.length === 0) return null;\n\n const { mx, ml, slope } = fitLuma(members);\n let busy = 0;\n let lo = 1;\n let hi = 0;\n // The group's lightest SPOT: density for everyone is computed from it. Otherwise, right at a\n // black/white border the group would hold to the average, a light ink would never darken\n // against anything, and it would drown over the light half.\n let base = 0;\n let r = 0;\n let g = 0;\n let b = 0;\n // The legibility requirement is the group's strictest: polarity is one for all, and going by\n // the weakest requirement would leave a neighbor with pickier optics illegible.\n let legibility = 0;\n for (const m of members) {\n busy = Math.max(busy, m.sample.busy);\n lo = Math.min(lo, m.sample.lo);\n hi = Math.max(hi, m.sample.hi);\n base = Math.max(base, m.sample.luma);\n r += m.sample.r;\n g += m.sample.g;\n b += m.sample.b;\n legibility = Math.max(legibility, m.legibility);\n }\n const k = members.length;\n return {\n plane: { mx, ml, slope, base, rest: [busy, lo, hi, r / k, g / k, b / k] },\n // The polarity decision is one per group, by its AVERAGE lightness. Deciding on its own,\n // each button used to end up a different color over a busy backdrop.\n decision: { decisive: ml * 0.75 + hi * 0.25, legibility },\n };\n}\n\n/**\n * Smoothing runs over samples, not frames: the native lens smooths ITS OWN estimate, and the\n * group estimate overrides it with an already-finished value. Without this the group's tone\n * changed in steps — the probe's 180 ms cadence is too coarse for the adaptation to read as\n * unnoticeable.\n */\nexport function smoothPlane(prev: GroupPlane | null, next: GroupPlane): GroupPlane {\n if (!prev) return next;\n const e = (was: number, now: number) =>\n Math.abs(now - was) < SETTLE ? now : was + (now - was) * SMOOTH;\n const ml = e(prev.ml, next.ml);\n const slope = e(prev.slope, next.slope);\n const base = e(prev.base, next.base);\n const rest = next.rest.map((v, i) => e(prev.rest[i], v));\n // A converged estimate is handed back as the same object: otherwise `setPlane` would repaint\n // the group on every sample — twenty times a second, even on a still screen.\n const same =\n next.mx === prev.mx &&\n ml === prev.ml &&\n slope === prev.slope &&\n base === prev.base &&\n rest.every((v, i) => v === prev.rest[i]);\n return same ? prev : { mx: next.mx, ml, slope, base, rest };\n}\n\n/** The sample a member at position `x` hands its own lens — in the order of its prop. */\nexport function probeValuesAt(plane: GroupPlane, x: number): number[] {\n // Down from the group's lightest spot — by exactly as much as that spot is darker along the\n // plane, and even then only a GRADIENT fraction of it. This lets the dark end off on density,\n // without canceling it: the group stays a single material.\n const here = plane.ml + plane.slope * (x - plane.mx);\n const luma = clamp01(plane.base - (plane.base - here) * GRADIENT);\n // The slope doesn't carry over into the lens: the group's is measured in screen dp, while the\n // shader multiplies `u_probeSlope` by a normalized position within the element — different\n // units.\n return [luma, plane.rest[0], plane.rest[1], plane.rest[2], 0, 0, plane.rest[3], plane.rest[4], plane.rest[5]];\n}\n\n/** Whether the group needs to flip ink polarity given the current one (1 light, 0 dark). */\nfunction wantsPolarityChange(decision: GroupDecision, polarity: number): boolean {\n return polarity === 1 ? decision.decisive >= FLIP_LUMA : decision.decisive < RETURN_LUMA;\n}\n\nexport type GroupSink = {\n /** The group estimate has been recomputed: members take their values from it. */\n plane: (plane: GroupPlane | null) => void;\n /** The group flips ink polarity for everyone at once. */\n flip: (polarity: number) => void;\n};\n\nexport type GroupState = {\n report: (\n id: string,\n x: number,\n y: number,\n sample: BackdropSample,\n legibility: number,\n ) => void;\n release: (id: string) => void;\n plane: () => GroupPlane | null;\n polarity: () => number;\n confirmations: () => number;\n};\n\n/** Group state: members, the smoothed estimate, and polarity hysteresis. */\nexport function createGroupState(sink: GroupSink): GroupState {\n const members = new Map<string, GroupMember>();\n let plane: GroupPlane | null = null;\n let pending = 0;\n let polarity = 1;\n\n const measure = (): GroupDecision | null => {\n const next = aggregate([...members.values()]);\n if (!next) {\n pending = 0;\n plane = null;\n sink.plane(null);\n return null;\n }\n plane = smoothPlane(plane, next.plane);\n sink.plane(plane);\n return next.decision;\n };\n\n return {\n report(id, x, y, sample, legibility) {\n members.set(id, { x, y, sample, legibility });\n const decision = measure();\n if (!decision) return;\n if (!wantsPolarityChange(decision, polarity)) {\n pending = 0;\n return;\n }\n pending += 1;\n if (pending < CONFIRMATIONS) return;\n pending = 0;\n polarity = polarity === 1 ? 0 : 1;\n sink.flip(polarity);\n },\n // Only recomputes the estimate: losing a member isn't a backdrop sample and can't count as a\n // polarity confirmation — unmounting the group used to rack up CONFIRMATIONS instantly and\n // trigger a recolor nobody was left to cancel.\n release(id) {\n if (members.delete(id)) measure();\n },\n plane: () => plane,\n polarity: () => polarity,\n confirmations: () => pending,\n };\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { bevelDp, thicknessDp, rimDp, surfacePadDp, shadowReachDp, bevelFraction, halfMinDp } from './chunk-
|
|
2
|
-
import { DERIVE, TOUCH, EPSILON, BEVEL, LENS, BODY, MEDIUM, SPECTRAL, DISPERSION, RIM, ACCENT, MORPH } from './chunk-
|
|
1
|
+
import { bevelDp, thicknessDp, rimDp, surfacePadDp, shadowReachDp, bevelFraction, halfMinDp } from './chunk-SLRPHDV7.js';
|
|
2
|
+
import { DERIVE, TOUCH, EPSILON, BEVEL, LENS, BODY, MEDIUM, SPECTRAL, DISPERSION, RIM, ACCENT, MORPH } from './chunk-MMPK6DCK.js';
|
|
3
3
|
|
|
4
4
|
// src/optics.ts
|
|
5
5
|
var clamp = (v, lo, hi) => v < lo ? lo : v > hi ? hi : v;
|
|
@@ -1339,5 +1339,5 @@ function morphOutOf(source, target, offsetX, offsetY, t) {
|
|
|
1339
1339
|
}
|
|
1340
1340
|
|
|
1341
1341
|
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 };
|
|
1342
|
-
//# sourceMappingURL=chunk-
|
|
1343
|
-
//# sourceMappingURL=chunk-
|
|
1342
|
+
//# sourceMappingURL=chunk-H5QTBF67.js.map
|
|
1343
|
+
//# sourceMappingURL=chunk-H5QTBF67.js.map
|