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/dist/dom.d.cts CHANGED
@@ -160,9 +160,21 @@ type GlassBody = {
160
160
  declare function resolveBody(optics: VireGlassOptics, sample: BodyBackdrop, ink: number): GlassBody;
161
161
  /**
162
162
  * PRESENCE (§3): over a uniform backdrop there is nothing to refract and the glass honestly
163
- * disappears — right for a piece of background, wrong for a control. The sign comes from the
164
- * BACKDROP, not from ink polarity: lighter over dark, darker over light. It is a minimum, not an
165
- * addition, so a body that is already dense enough is left alone.
163
+ * disappears — right for a piece of background, wrong for a control. It is a MINIMUM, not an
164
+ * addition, so a body that already stands apart is left alone.
165
+ *
166
+ * The direction is the body's own. The previous version decided "lighter over dark, darker over
167
+ * light" from the backdrop alone, and then, when the body disagreed, clamped `tintLuma` to the
168
+ * backdrop's mean — the one value that separates from nothing. Light ink over a dark backdrop puts
169
+ * the body at 0.07 against a backdrop of 0.12: already separating, downward, and the clamp pulled
170
+ * it back onto the backdrop. Measured on the default material at presence 0.08: exactly 0.0000 at
171
+ * backdrops of 0.12, 0.20 and 0.35.
172
+ *
173
+ * What the demand is DIVIDED by is deliberately the distance to the tint's pole rather than to the
174
+ * body's own tint. Dividing by the latter is arithmetically tighter and asks for far more density
175
+ * wherever the two are close — enough to drive it to the ceiling over a dark backdrop and stop the
176
+ * glass being a window at all, which check:optics says plainly. Presence is a floor on visibility,
177
+ * not a licence to go opaque.
166
178
  */
167
179
  declare function withPresence(body: GlassBody, optics: VireGlassOptics, sample: BodyBackdrop): GlassBody;
168
180
 
package/dist/dom.d.ts CHANGED
@@ -160,9 +160,21 @@ type GlassBody = {
160
160
  declare function resolveBody(optics: VireGlassOptics, sample: BodyBackdrop, ink: number): GlassBody;
161
161
  /**
162
162
  * PRESENCE (§3): over a uniform backdrop there is nothing to refract and the glass honestly
163
- * disappears — right for a piece of background, wrong for a control. The sign comes from the
164
- * BACKDROP, not from ink polarity: lighter over dark, darker over light. It is a minimum, not an
165
- * addition, so a body that is already dense enough is left alone.
163
+ * disappears — right for a piece of background, wrong for a control. It is a MINIMUM, not an
164
+ * addition, so a body that already stands apart is left alone.
165
+ *
166
+ * The direction is the body's own. The previous version decided "lighter over dark, darker over
167
+ * light" from the backdrop alone, and then, when the body disagreed, clamped `tintLuma` to the
168
+ * backdrop's mean — the one value that separates from nothing. Light ink over a dark backdrop puts
169
+ * the body at 0.07 against a backdrop of 0.12: already separating, downward, and the clamp pulled
170
+ * it back onto the backdrop. Measured on the default material at presence 0.08: exactly 0.0000 at
171
+ * backdrops of 0.12, 0.20 and 0.35.
172
+ *
173
+ * What the demand is DIVIDED by is deliberately the distance to the tint's pole rather than to the
174
+ * body's own tint. Dividing by the latter is arithmetically tighter and asks for far more density
175
+ * wherever the two are close — enough to drive it to the ceiling over a dark backdrop and stop the
176
+ * glass being a window at all, which check:optics says plainly. Presence is a floor on visibility,
177
+ * not a licence to go opaque.
166
178
  */
167
179
  declare function withPresence(body: GlassBody, optics: VireGlassOptics, sample: BodyBackdrop): GlassBody;
168
180
 
package/dist/dom.js CHANGED
@@ -1,8 +1,8 @@
1
- import { shadowTint, NO_ACCESSIBILITY, createGroupState, createDeform, elasticAllowed, contrastRimLuma, concentricRadius, scrollEdgeStrength, scrollEdgeStyle, applyGlassScale, GLASS_SCALE_DEFAULT, applyAccessibility, applyAppear, accentTone, accentAmount, raiseIntoGlass } from './chunk-3PGYF2NK.js';
1
+ import { shadowTint, NO_ACCESSIBILITY, createGroupState, createDeform, elasticAllowed, contrastRimLuma, concentricRadius, scrollEdgeStrength, scrollEdgeStyle, applyGlassScale, GLASS_SCALE_DEFAULT, applyAccessibility, applyAppear, accentTone, accentAmount, raiseIntoGlass } from './chunk-CTTZNZFV.js';
2
2
  import { ambientFrom, shouldInkBeLight, INK_LIGHT, INK_DARK } from './chunk-JNRZNMJJ.js';
3
- import { touchWarp, sceneDistance, sceneGradient, sdfRoundedRect, VIREGLASS_MATERIAL, REST_LIGHT, resolveOptics, refractionStrength } from './chunk-FL5UZPY5.js';
4
- import { shadowOpacity, bevelDp, thicknessDp, rimDp, shadowOpacityFrom, shadowReachDp, halfMinDp, MAX_STRETCH, roundedRectGeometry } from './chunk-P2ZOODXD.js';
5
- import { LENS, BODY, RIM, TOUCH, SHADOW, SHADOW_GAP, SCROLL_EDGE, BEVEL, SPECTRAL, DISPERSION } from './chunk-TTOKBARR.js';
3
+ import { touchWarp, sceneDistance, sceneGradient, sdfRoundedRect, VIREGLASS_MATERIAL, REST_LIGHT, resolveOptics, refractionStrength } from './chunk-H5QTBF67.js';
4
+ import { shadowOpacity, bevelDp, thicknessDp, rimDp, shadowOpacityFrom, shadowReachDp, halfMinDp, MAX_STRETCH, roundedRectGeometry } from './chunk-SLRPHDV7.js';
5
+ import { LENS, BODY, RIM, TOUCH, SHADOW, SHADOW_GAP, SCROLL_EDGE, BEVEL, SPECTRAL, DISPERSION } from './chunk-MMPK6DCK.js';
6
6
 
7
7
  // src/dom/displacement.ts
8
8
  var SCALE_GAIN = 0.75;
@@ -311,10 +311,18 @@ function sampleColorAt(el, x, y) {
311
311
  }
312
312
  const composited = compositeLayers(layers);
313
313
  if (composited) return composited;
314
- const topmost = behind[0];
315
- if (!topmost) return null;
316
- return parseColor(getComputedStyle(topmost).color);
314
+ return canvasColor(el);
317
315
  }
316
+ function canvasColor(el) {
317
+ const doc = el.ownerDocument;
318
+ for (const node of [doc.documentElement, doc.body]) {
319
+ if (!node) continue;
320
+ const bg = parseColor(getComputedStyle(node).backgroundColor);
321
+ if (bg && bg.a > 1e-3) return bg.a >= 0.999 ? bg : compositeLayers([bg, WHITE]);
322
+ }
323
+ return WHITE;
324
+ }
325
+ var WHITE = { r: 255, g: 255, b: 255, a: 1 };
318
326
  function probeBackdrop(el, opts = {}) {
319
327
  const cols = opts.cols ?? GRID_COLS;
320
328
  const rows = opts.rows ?? GRID_ROWS;
@@ -417,15 +425,17 @@ function resolveBody(optics, sample, ink) {
417
425
  function withPresence(body, optics, sample) {
418
426
  if (optics.presence <= 0) return body;
419
427
  const mean = clamp(sample.luma, 0, 1);
420
- const lighter = mean < 0.5;
421
- const target = lighter ? Math.min(mean + optics.presence, 1) : Math.max(mean - optics.presence, 0);
422
- const separation = Math.abs(target - mean);
428
+ const above = Math.abs(body.tintLuma - mean) > 1e-4 ? body.tintLuma > mean : mean < 0.5;
429
+ const pole = above ? TINT_LIGHT : TINT_DARK;
430
+ const separation = Math.min(optics.presence, Math.abs(pole - mean));
423
431
  const reached = Math.abs(body.tintLuma - mean) * body.density;
424
432
  if (reached >= separation) return body;
425
- const needed = separation / Math.max(Math.abs((lighter ? TINT_LIGHT : TINT_DARK) - mean), 1e-4);
433
+ const needed = separation / Math.max(Math.abs(pole - mean), 1e-4);
426
434
  return {
427
- density: Math.max(body.density, Math.min(needed, MAX_DEMAND)),
428
- tintLuma: lighter ? Math.max(body.tintLuma, mean) : Math.min(body.tintLuma, mean)
435
+ density: Math.max(body.density, Math.min(needed, BODY.presenceDemand)),
436
+ // Left where the tint law put it, unless it landed exactly on the backdrop — which is the
437
+ // case that used to be created here rather than avoided.
438
+ tintLuma: Math.abs(body.tintLuma - mean) > 1e-4 ? body.tintLuma : pole
429
439
  };
430
440
  }
431
441
 
@@ -660,15 +670,22 @@ function waveImpulse(g) {
660
670
  return halfMinDp(g) * MAX_STRETCH * WAVE_OF_TRAVEL;
661
671
  }
662
672
  var STYLE_ATTR = "data-vireglass-styles";
663
- function ensureRimStyles() {
664
- if (document.querySelector(`style[${STYLE_ATTR}]`)) return;
673
+ function hostFor(el) {
674
+ const root = el.getRootNode();
675
+ return root instanceof ShadowRoot ? root : el.ownerDocument ?? document;
676
+ }
677
+ function styleParent(host) {
678
+ return host instanceof ShadowRoot ? host : host.head;
679
+ }
680
+ function ensureRimStyles(host) {
681
+ if (host.querySelector(`style[${STYLE_ATTR}]`)) return;
665
682
  const style = document.createElement("style");
666
683
  style.setAttribute(STYLE_ATTR, "");
667
684
  style.textContent = `[${GLASS_ATTR}]::after{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;padding:var(--vireglass-rim-width,1px);background:var(--vireglass-rim,none);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude}[${GLASS_ATTR}]::before{content:'';position:absolute;inset:0;border-radius:inherit;pointer-events:none;background:var(--vireglass-glow,none);opacity:var(--vireglass-glow-opacity,0)}`;
668
- document.head.appendChild(style);
685
+ styleParent(host).appendChild(style);
669
686
  }
670
- function getFilterHost() {
671
- const existing = document.querySelector(`svg[${FILTER_HOST_ATTR}]`);
687
+ function getFilterHost(host) {
688
+ const existing = host.querySelector(`svg[${FILTER_HOST_ATTR}]`);
672
689
  if (existing) return existing;
673
690
  const svg = document.createElementNS(SVG_NS, "svg");
674
691
  svg.setAttribute(FILTER_HOST_ATTR, "");
@@ -678,7 +695,7 @@ function getFilterHost() {
678
695
  svg.style.position = "absolute";
679
696
  svg.style.pointerEvents = "none";
680
697
  svg.appendChild(document.createElementNS(SVG_NS, "defs"));
681
- document.body.appendChild(svg);
698
+ (host instanceof ShadowRoot ? host : host.body).appendChild(svg);
682
699
  return svg;
683
700
  }
684
701
  function buildFilterElement(id, mapUrl, optics, scale, spectral) {
@@ -716,6 +733,7 @@ function buildFilterElement(id, mapUrl, optics, scale, spectral) {
716
733
  pass.setAttribute("xChannelSelector", "R");
717
734
  pass.setAttribute("yChannelSelector", "G");
718
735
  pass.setAttribute("result", `${name}raw`);
736
+ pass.setAttribute("data-vireglass-ratio", String(scale > 0 ? chScale / scale : 1));
719
737
  const only = document.createElementNS(SVG_NS, "feColorMatrix");
720
738
  only.setAttribute("in", `${name}raw`);
721
739
  only.setAttribute("type", "matrix");
@@ -822,7 +840,8 @@ function attachGlass(el, opts = {}) {
822
840
  apply();
823
841
  });
824
842
  warnIfNested(el);
825
- ensureRimStyles();
843
+ const host = hostFor(el);
844
+ ensureRimStyles(host);
826
845
  el.setAttribute(GLASS_ATTR, "");
827
846
  const positionWasStatic = getComputedStyle(el).position === "static";
828
847
  if (positionWasStatic) el.style.setProperty("position", "relative");
@@ -850,7 +869,7 @@ function attachGlass(el, opts = {}) {
850
869
  const key = `a${appear.toFixed(3)}|m${morphKey()}|${geometry.width}x${geometry.height}r${geometry.cornerRadius}@${devicePixelRatio()}:${opticsNow.refraction}:${opticsNow.refractionScale}:${opticsNow.bevelDp}:${opticsNow.blur}`;
851
870
  if (key !== mapKey || !filterEl) {
852
871
  const map = cached(`d|${key}`, () => buildDisplacementMap(opticsNow, geometry, devicePixelRatio(), morphState()));
853
- const defs = getFilterHost().querySelector("defs");
872
+ const defs = getFilterHost(host).querySelector("defs");
854
873
  const hue = hasSpectralEdge(opticsNow) ? cached(`h|${key}`, () => buildSpectralMap(opticsNow, geometry, devicePixelRatio())) : null;
855
874
  const next = buildFilterElement(id, map.url, opticsNow, map.scale, hue);
856
875
  if (filterEl?.parentNode) defs.replaceChild(next, filterEl);
@@ -916,9 +935,11 @@ function attachGlass(el, opts = {}) {
916
935
  });
917
936
  if (filterEl) {
918
937
  const feImage = filterEl.querySelector("feImage");
919
- const feDisplace = filterEl.querySelector("feDisplacementMap");
920
938
  feImage?.setAttribute("href", map.url);
921
- feDisplace?.setAttribute("scale", String(map.scale));
939
+ for (const pass of filterEl.querySelectorAll("feDisplacementMap")) {
940
+ const ratio = Number(pass.getAttribute("data-vireglass-ratio") ?? 1);
941
+ pass.setAttribute("scale", String(map.scale * (Number.isFinite(ratio) ? ratio : 1)));
942
+ }
922
943
  }
923
944
  mapKey = "";
924
945
  paintGlow(sampleOut.touchX, sampleOut.touchY, sampleOut.active * base.edgeLight, geometry);