@blotch/angular-lynx 22.2.2 → 22.2.3

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.
@@ -0,0 +1,951 @@
1
+ const BLUR_VIEW_LAYOUT_CSS = ":host{--lynx-display-toggle:var(--lynx-display-linear);--lynx-display-linear:var(--lynx-display-toggle,);--lynx-display-flex:var(--lynx-display-toggle,);--lynx-linear-orientation-toggle:var(--lynx-linear-orientation-vertical);--lynx-linear-orientation-horizontal:var(--lynx-linear-orientation-toggle,);--lynx-linear-orientation-vertical:var(--lynx-linear-orientation-toggle,);--lynx-linear-orientation-horizontal-reverse:var(--lynx-linear-orientation-toggle,);--lynx-linear-orientation-vertical-reverse:var(--lynx-linear-orientation-toggle,);--linear-flex-direction:var(--lynx-linear-orientation-horizontal,row) var(--lynx-linear-orientation-vertical,column) var(--lynx-linear-orientation-horizontal-reverse,row-reverse) var(--lynx-linear-orientation-vertical-reverse,column-reverse);--linear-justify-content:var(--lynx-linear-orientation-horizontal,var(--justify-content-row)) var(--lynx-linear-orientation-vertical,var(--justify-content-column)) var(--lynx-linear-orientation-horizontal-reverse,var(--justify-content-row-reverse)) var(--lynx-linear-orientation-vertical-reverse,var(--justify-content-column-reverse));flex-wrap:var(--lynx-display-linear,nowrap) var(--lynx-display-flex,var(--flex-wrap));flex-direction:var(--lynx-display-linear,var(--linear-flex-direction)) var(--lynx-display-flex,var(--flex-direction));justify-content:var(--lynx-display-linear,var(--linear-justify-content))}";
2
+ const applyBlurViewLayout = (element) => {
3
+ const root = element.shadowRoot;
4
+ if (!root) return;
5
+ const style = element.ownerDocument.createElement("style");
6
+ style.textContent = BLUR_VIEW_LAYOUT_CSS;
7
+ root.appendChild(style);
8
+ };
9
+ const observeBackdrop = (backdrop, glass, onChange) => {
10
+ const win = backdrop.ownerDocument.defaultView;
11
+ const mutations = new win.MutationObserver((records) => {
12
+ if (records.some((record) => !glass.contains(record.target))) onChange();
13
+ });
14
+ mutations.observe(backdrop, {
15
+ subtree: true,
16
+ childList: true,
17
+ attributes: true,
18
+ characterData: true
19
+ });
20
+ const resizes = new win.ResizeObserver(onChange);
21
+ resizes.observe(backdrop);
22
+ backdrop.addEventListener("scroll", onChange, true);
23
+ backdrop.addEventListener("load", onChange, true);
24
+ const scheme = win.matchMedia("(prefers-color-scheme: dark)");
25
+ scheme.addEventListener("change", onChange);
26
+ backdrop.ownerDocument.fonts.ready.then(onChange);
27
+ return () => {
28
+ mutations.disconnect();
29
+ resizes.disconnect();
30
+ backdrop.removeEventListener("scroll", onChange, true);
31
+ backdrop.removeEventListener("load", onChange, true);
32
+ scheme.removeEventListener("change", onChange);
33
+ };
34
+ };
35
+ const TRANSPARENT = [
36
+ 0,
37
+ 0,
38
+ 0,
39
+ 0
40
+ ];
41
+ const RGB_FUNCTION = /^rgba?\(([^)]*)\)$/;
42
+ const HEX = /^#([0-9a-f]{6})$/i;
43
+ const parseNormalizedColor = (value) => {
44
+ const hex = HEX.exec(value);
45
+ if (hex) {
46
+ const n = parseInt(hex[1], 16);
47
+ return [
48
+ (n >> 16) / 255,
49
+ (n >> 8 & 255) / 255,
50
+ (n & 255) / 255,
51
+ 1
52
+ ];
53
+ }
54
+ const fn = RGB_FUNCTION.exec(value.trim());
55
+ if (!fn) return TRANSPARENT;
56
+ const parts = fn[1].split(/[\s,/]+/).filter(Boolean).map(Number);
57
+ if (parts.length < 3 || parts.some(Number.isNaN)) return TRANSPARENT;
58
+ const [r, g, b, a = 1] = parts;
59
+ return [
60
+ r / 255,
61
+ g / 255,
62
+ b / 255,
63
+ a
64
+ ];
65
+ };
66
+ const cache = /* @__PURE__ */ new Map();
67
+ let context;
68
+ const resolveCssColor = (doc, value) => {
69
+ if (!value) return TRANSPARENT;
70
+ const cached = cache.get(value);
71
+ if (cached) return cached;
72
+ context ??= doc.createElement("canvas").getContext("2d");
73
+ if (!context) return TRANSPARENT;
74
+ const sentinel = "rgba(1, 2, 3, 0.5)";
75
+ context.fillStyle = sentinel;
76
+ context.fillStyle = value;
77
+ const normalized = String(context.fillStyle);
78
+ const color = normalized === sentinel ? TRANSPARENT : parseNormalizedColor(normalized);
79
+ cache.set(value, color);
80
+ return color;
81
+ };
82
+ const luminance = ([r, g, b]) => .2126 * r + .7152 * g + .0722 * b;
83
+ const drawnScale = (element) => {
84
+ const drawn = element.getBoundingClientRect().width;
85
+ const laidOut = element.offsetWidth;
86
+ if (!laidOut || Math.abs(drawn - laidOut) < 1) return 1;
87
+ return drawn / laidOut;
88
+ };
89
+ const SKIPPED_PROPERTY = /^(--|transition|animation|will-change|cursor|pointer-events|user-select|-webkit-user-)/;
90
+ const ALWAYS_INLINED = /^(border-(top|right|bottom|left)-width|outline-width|column-rule-width)$/;
91
+ const SKIPPED_TAGS = /* @__PURE__ */ new Set([
92
+ "style",
93
+ "script",
94
+ "template",
95
+ "link",
96
+ "meta"
97
+ ]);
98
+ const ELEMENT_NODE = 1;
99
+ const TEXT_NODE = 3;
100
+ const defaultsByDocument = /* @__PURE__ */ new WeakMap();
101
+ const defaultStyle = (doc, win) => {
102
+ let defaults = defaultsByDocument.get(doc);
103
+ if (!defaults) {
104
+ const probe = doc.createElement("div");
105
+ doc.documentElement.appendChild(probe);
106
+ const style = win.getComputedStyle(probe);
107
+ defaults = new Map(Array.from(style, (name) => [name, style.getPropertyValue(name)]));
108
+ probe.remove();
109
+ defaultsByDocument.set(doc, defaults);
110
+ }
111
+ return defaults;
112
+ };
113
+ const inlineStyle = (style, parent, defaults) => {
114
+ let css = "";
115
+ for (const name of Array.from(style)) {
116
+ if (SKIPPED_PROPERTY.test(name)) continue;
117
+ const value = style.getPropertyValue(name);
118
+ if (ALWAYS_INLINED.test(name) || value !== defaults.get(name) || parent && value !== parent.getPropertyValue(name)) css += `${name}:${value};`;
119
+ }
120
+ return css;
121
+ };
122
+ const renderedChildren = (element) => Array.from(element.shadowRoot?.childNodes ?? element.childNodes).flatMap((child) => child.localName === "slot" ? child.assignedNodes({ flatten: true }) : [child]);
123
+ const scrollShift = (style, [x, y]) => {
124
+ if (!x && !y) return "";
125
+ if (style.position === "fixed" || style.position === "sticky") return "";
126
+ if (style.position === "static") return `position:relative;left:${-x}px;top:${-y}px;`;
127
+ return `left:${(parseFloat(style.left) || 0) - x}px;top:${(parseFloat(style.top) || 0) - y}px;`;
128
+ };
129
+ const mediaSource = (element, doc) => {
130
+ try {
131
+ if (element.localName === "canvas") return element.toDataURL();
132
+ const image = element;
133
+ if (!image.complete || !image.naturalWidth) return "";
134
+ const canvas = doc.createElement("canvas");
135
+ canvas.width = image.naturalWidth;
136
+ canvas.height = image.naturalHeight;
137
+ canvas.getContext("2d").drawImage(image, 0, 0);
138
+ return canvas.toDataURL();
139
+ } catch {
140
+ return "";
141
+ }
142
+ };
143
+ const outside = (rect, region) => rect.right <= region.left || rect.left >= region.right || rect.bottom <= region.top || rect.top >= region.bottom;
144
+ const cloneNode = (node, context, parent, scroll) => {
145
+ if (node.nodeType === TEXT_NODE) return context.doc.createTextNode(node.data);
146
+ if (node.nodeType !== ELEMENT_NODE) return null;
147
+ const element = node;
148
+ if (SKIPPED_TAGS.has(element.localName) || context.exclude(element)) return null;
149
+ const style = context.win.getComputedStyle(element);
150
+ if (style.display === "none") return null;
151
+ const media = element.localName === "img" || element.localName === "canvas";
152
+ const clone = context.doc.createElement(media ? "img" : "div");
153
+ clone.setAttribute("style", inlineStyle(style, parent, context.defaults) + scrollShift(style, scroll));
154
+ if (media) {
155
+ const source = mediaSource(element, context.doc);
156
+ if (source) clone.setAttribute("src", source);
157
+ return clone;
158
+ }
159
+ if ((style.overflowX !== "visible" || style.overflowY !== "visible") && outside(element.getBoundingClientRect(), context.region)) return clone;
160
+ const childScroll = [element.scrollLeft, element.scrollTop];
161
+ for (const child of renderedChildren(element)) {
162
+ const copy = cloneNode(child, context, style, childScroll);
163
+ if (copy) clone.appendChild(copy);
164
+ }
165
+ return clone;
166
+ };
167
+ const effectiveBackground = (element) => {
168
+ const win = element.ownerDocument.defaultView;
169
+ let node = element;
170
+ while (node) {
171
+ const color = parseNormalizedColor(win.getComputedStyle(node).backgroundColor);
172
+ if (color[3] > 0) return color;
173
+ node = node.parentElement ?? node.getRootNode().host ?? null;
174
+ }
175
+ return [
176
+ 1,
177
+ 1,
178
+ 1,
179
+ 1
180
+ ];
181
+ };
182
+ const cloneBackdrop = (root, options) => {
183
+ const doc = root.ownerDocument;
184
+ const win = doc.defaultView;
185
+ const bounds = root.getBoundingClientRect();
186
+ const zoom = drawnScale(root);
187
+ const width = bounds.width / zoom;
188
+ const height = bounds.height / zoom;
189
+ const [r, g, b, a] = effectiveBackground(root);
190
+ const frame = doc.createElement("div");
191
+ frame.setAttribute("style", `width:${Math.ceil(width)}px;height:${Math.ceil(height)}px;overflow:hidden;background:rgba(${r * 255},${g * 255},${b * 255},${a});`);
192
+ const context = {
193
+ ...options,
194
+ doc,
195
+ win,
196
+ defaults: defaultStyle(doc, win)
197
+ };
198
+ const clone = cloneNode(root, context, null, [0, 0]);
199
+ if (clone) {
200
+ clone.style.cssText += `margin:0;position:relative;left:0;top:0;right:auto;bottom:auto;transform:none;width:${width}px;height:${height}px;`;
201
+ frame.appendChild(clone);
202
+ }
203
+ return frame;
204
+ };
205
+ const MAX_IMAGE_SIZE = 4096;
206
+ const snapshotBackdrop = async (root, options) => {
207
+ const doc = root.ownerDocument;
208
+ const win = doc.defaultView;
209
+ const bounds = root.getBoundingClientRect();
210
+ const left = Math.max(options.region.left, bounds.left);
211
+ const top = Math.max(options.region.top, bounds.top);
212
+ const right = Math.min(options.region.right, bounds.right);
213
+ const bottom = Math.min(options.region.bottom, bounds.bottom);
214
+ if (right <= left || bottom <= top) return null;
215
+ const zoom = drawnScale(root);
216
+ const x = (left - bounds.left) / zoom;
217
+ const y = (top - bounds.top) / zoom;
218
+ const width = (right - left) / zoom;
219
+ const height = (bottom - top) / zoom;
220
+ const scale = Math.min(options.scale, MAX_IMAGE_SIZE / Math.max(width, height));
221
+ const markup = new win.XMLSerializer().serializeToString(cloneBackdrop(root, options));
222
+ const pixelWidth = Math.max(1, Math.round(width * scale));
223
+ const pixelHeight = Math.max(1, Math.round(height * scale));
224
+ const svg = `<svg xmlns="http://www.w3.org/2000/svg" width="${pixelWidth}" height="${pixelHeight}" viewBox="${x} ${y} ${width} ${height}" preserveAspectRatio="none"><foreignObject width="${Math.ceil(bounds.width / zoom)}" height="${Math.ceil(bounds.height / zoom)}">${markup}</foreignObject></svg>`;
225
+ const picture = new win.Image();
226
+ picture.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
227
+ await picture.decode();
228
+ const image = doc.createElement("canvas");
229
+ image.width = pixelWidth;
230
+ image.height = pixelHeight;
231
+ image.getContext("2d").drawImage(picture, 0, 0, pixelWidth, pixelHeight);
232
+ return {
233
+ image,
234
+ x,
235
+ y,
236
+ width,
237
+ height,
238
+ luminance: luminance(effectiveBackground(root))
239
+ };
240
+ };
241
+ const QUAD_VERTEX_SHADER = `
242
+ attribute vec2 a_position;
243
+ varying vec2 v_uv;
244
+ void main() {
245
+ v_uv = a_position * 0.5 + 0.5;
246
+ gl_Position = vec4(a_position, 0.0, 1.0);
247
+ }`;
248
+ const COPY_FRAGMENT_SHADER = `
249
+ precision mediump float;
250
+ varying vec2 v_uv;
251
+ uniform sampler2D u_source;
252
+ void main() {
253
+ gl_FragColor = texture2D(u_source, v_uv);
254
+ }`;
255
+ const BLUR_FRAGMENT_SHADER = `
256
+ precision mediump float;
257
+ varying vec2 v_uv;
258
+ uniform sampler2D u_source;
259
+ uniform vec2 u_direction;
260
+ void main() {
261
+ vec2 near = u_direction * 1.3846153846;
262
+ vec2 far = u_direction * 3.2307692308;
263
+ gl_FragColor = texture2D(u_source, v_uv) * 0.2270270270
264
+ + (texture2D(u_source, v_uv + near) + texture2D(u_source, v_uv - near)) * 0.3162162162
265
+ + (texture2D(u_source, v_uv + far) + texture2D(u_source, v_uv - far)) * 0.0702702703;
266
+ }`;
267
+ const GLASS_FRAGMENT_SHADER = `
268
+ precision highp float;
269
+ #define MAX_SHAPES 16
270
+ varying vec2 v_uv;
271
+ uniform sampler2D u_backdrop;
272
+ uniform sampler2D u_frosted;
273
+ uniform vec2 u_size;
274
+ uniform vec2 u_offset;
275
+ uniform vec2 u_backdropSize;
276
+ uniform float u_scale;
277
+ uniform float u_smoothing;
278
+ uniform float u_dark;
279
+ uniform float u_shadow;
280
+ uniform int u_count;
281
+ uniform vec4 u_bounds[MAX_SHAPES];
282
+ uniform vec4 u_material[MAX_SHAPES];
283
+ uniform vec4 u_tint[MAX_SHAPES];
284
+ uniform vec3 u_pointer;
285
+
286
+ float roundedBox(vec2 p, vec2 halfSize, float radius) {
287
+ vec2 q = abs(p) - halfSize + radius;
288
+ return length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
289
+ }
290
+
291
+ float smoothUnion(float a, float b, float k) {
292
+ if (k <= 0.0) return min(a, b);
293
+ float h = max(k - abs(a - b), 0.0) / k;
294
+ return min(a, b) - h * h * k * 0.25;
295
+ }
296
+
297
+ float scene(vec2 p) {
298
+ float d = 1e5;
299
+ for (int i = 0; i < MAX_SHAPES; i++) {
300
+ if (i >= u_count) break;
301
+ d = smoothUnion(d, roundedBox(p - u_bounds[i].xy, u_bounds[i].zw, u_material[i].x), u_smoothing);
302
+ }
303
+ return d;
304
+ }
305
+
306
+ void main() {
307
+ vec2 p = vec2(v_uv.x, 1.0 - v_uv.y) * u_size;
308
+
309
+ // Union the shapes, and blend each shape's material by how close it is, so
310
+ // a merged bridge between two tints shades from one to the other.
311
+ float d = 1e5;
312
+ float weights = 0.0;
313
+ float frost = 0.0;
314
+ float lens = 0.0;
315
+ vec4 tint = vec4(0.0);
316
+ for (int i = 0; i < MAX_SHAPES; i++) {
317
+ if (i >= u_count) break;
318
+ float di = roundedBox(p - u_bounds[i].xy, u_bounds[i].zw, u_material[i].x);
319
+ d = smoothUnion(d, di, u_smoothing);
320
+ float w = exp(-max(di, 0.0) / max(u_smoothing * 0.3, 1.0)) + 1e-6;
321
+ weights += w;
322
+ frost += u_material[i].y * w;
323
+ lens += min(u_bounds[i].z, u_bounds[i].w) * w;
324
+ tint += u_tint[i] * w;
325
+ }
326
+ frost /= weights;
327
+ lens = max(lens / weights, 1.0);
328
+ tint /= weights;
329
+
330
+ // One device pixel of anti-aliasing at the edge.
331
+ float coverage = clamp(0.5 - d * u_scale, 0.0, 1.0);
332
+ // A container can hug its shapes, so the shadow fades out before the canvas
333
+ // edge rather than stopping at it in a hard line.
334
+ vec2 inside = min(p, u_size - p);
335
+ float shadow = u_shadow * 0.1 * exp(-max(d, 0.0) / 6.0)
336
+ * smoothstep(0.0, 12.0, min(inside.x, inside.y));
337
+ if (coverage <= 0.0) {
338
+ gl_FragColor = vec4(0.0, 0.0, 0.0, d < 24.0 ? shadow : 0.0);
339
+ return;
340
+ }
341
+
342
+ vec2 e = vec2(0.5, 0.0);
343
+ vec2 gradient = vec2(
344
+ scene(p + e.xy) - scene(p - e.xy),
345
+ scene(p + e.yx) - scene(p - e.yx));
346
+ float slope = length(gradient);
347
+ vec2 normal = slope > 0.0 ? gradient / slope : vec2(0.0);
348
+ gradient = normal * min(slope, 1.0);
349
+
350
+ float depth = max(-d, 0.0);
351
+ float rim = pow(1.0 - clamp(depth / lens, 0.0, 1.0), 3.0);
352
+ vec2 offset = gradient * (lens * 0.45 * rim - max(lens - depth, 0.0) * 0.05);
353
+ vec2 base = p + u_offset;
354
+ vec2 uv = (base + offset) / u_backdropSize;
355
+
356
+ // A touch of dispersion: red bends a little more than blue.
357
+ vec3 sharp = vec3(
358
+ texture2D(u_backdrop, (base + offset * 1.035) / u_backdropSize).r,
359
+ texture2D(u_backdrop, uv).g,
360
+ texture2D(u_backdrop, (base + offset * 0.965) / u_backdropSize).b);
361
+ vec3 color = mix(sharp, texture2D(u_frosted, uv).rgb, frost);
362
+
363
+ float brightness = dot(color, vec3(0.2126, 0.7152, 0.0722));
364
+ color = mix(vec3(brightness), color, 1.0 + 0.3 * frost);
365
+ color = mix(color, mix(vec3(1.0), vec3(0.12), u_dark), frost * mix(0.22, 0.34, u_dark));
366
+ color *= 1.0 - 0.08 * u_dark * (1.0 - frost);
367
+ color = mix(color, tint.rgb, tint.a);
368
+
369
+ vec2 light = normalize(vec2(-0.6, -0.8));
370
+ float facing = dot(normal, light);
371
+ color += (1.0 - smoothstep(0.0, 1.5, depth)) * (0.22 + 0.58 * pow(abs(facing), 1.5)) * mix(0.75, 0.5, u_dark);
372
+ color += (1.0 - smoothstep(0.0, lens * 0.5, depth)) * max(facing, 0.0) * 0.16;
373
+ color += (1.0 - smoothstep(0.0, lens * 0.35, depth)) * max(-facing, 0.0) * 0.07;
374
+
375
+ float reach = length(p - u_pointer.xy);
376
+ color += u_pointer.z * 0.2 * exp(-reach * reach / (1.2 * lens * lens));
377
+
378
+ gl_FragColor = vec4(clamp(color, 0.0, 1.0) * coverage, coverage + shadow * (1.0 - coverage));
379
+ }`;
380
+ const compile = (gl, fragmentSource) => {
381
+ const program = gl.createProgram();
382
+ for (const [type, source] of [[gl.VERTEX_SHADER, QUAD_VERTEX_SHADER], [gl.FRAGMENT_SHADER, fragmentSource]]) {
383
+ const shader = gl.createShader(type);
384
+ gl.shaderSource(shader, source);
385
+ gl.compileShader(shader);
386
+ gl.attachShader(program, shader);
387
+ }
388
+ gl.bindAttribLocation(program, 0, "a_position");
389
+ gl.linkProgram(program);
390
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) return null;
391
+ const locations = /* @__PURE__ */ new Map();
392
+ const uniform = (name) => {
393
+ if (!locations.has(name)) locations.set(name, gl.getUniformLocation(program, name));
394
+ return locations.get(name);
395
+ };
396
+ return {
397
+ program,
398
+ uniform
399
+ };
400
+ };
401
+ const createTexture = (gl) => {
402
+ const texture = gl.createTexture();
403
+ gl.bindTexture(gl.TEXTURE_2D, texture);
404
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
405
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
406
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
407
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
408
+ return texture;
409
+ };
410
+ const createTarget = (gl, width, height) => {
411
+ const texture = createTexture(gl);
412
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
413
+ const framebuffer = gl.createFramebuffer();
414
+ gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
415
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
416
+ return {
417
+ texture,
418
+ framebuffer
419
+ };
420
+ };
421
+ const FROST_DOWNSAMPLE = 4;
422
+ const FROST_PASSES = 3;
423
+ var GlassRenderer = class GlassRenderer {
424
+ #gl;
425
+ #copy;
426
+ #blur;
427
+ #glass;
428
+ #quad;
429
+ #backdrop = null;
430
+ #frosted = null;
431
+ #frostedSize = "";
432
+ #backdropWidth = 1;
433
+ #backdropHeight = 1;
434
+ constructor(gl, copy, blur, glass) {
435
+ this.#gl = gl;
436
+ this.#copy = copy;
437
+ this.#blur = blur;
438
+ this.#glass = glass;
439
+ this.#quad = gl.createBuffer();
440
+ gl.bindBuffer(gl.ARRAY_BUFFER, this.#quad);
441
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
442
+ -1,
443
+ -1,
444
+ 1,
445
+ -1,
446
+ -1,
447
+ 1,
448
+ 1,
449
+ 1
450
+ ]), gl.STATIC_DRAW);
451
+ gl.enableVertexAttribArray(0);
452
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
453
+ }
454
+ static create(canvas) {
455
+ const gl = canvas.getContext("webgl", {
456
+ alpha: true,
457
+ premultipliedAlpha: true,
458
+ antialias: false,
459
+ depth: false,
460
+ stencil: false
461
+ });
462
+ if (!gl) return null;
463
+ const copy = compile(gl, COPY_FRAGMENT_SHADER);
464
+ const blur = compile(gl, BLUR_FRAGMENT_SHADER);
465
+ const glass = compile(gl, GLASS_FRAGMENT_SHADER);
466
+ if (!copy || !blur || !glass) return null;
467
+ return new GlassRenderer(gl, copy, blur, glass);
468
+ }
469
+ get lost() {
470
+ return this.#gl.isContextLost();
471
+ }
472
+ setBackdrop(image, width, height) {
473
+ const gl = this.#gl;
474
+ this.#backdropWidth = width;
475
+ this.#backdropHeight = height;
476
+ this.#backdrop ??= createTexture(gl);
477
+ gl.bindTexture(gl.TEXTURE_2D, this.#backdrop);
478
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image);
479
+ const w = Math.max(1, Math.round(image.width / FROST_DOWNSAMPLE));
480
+ const h = Math.max(1, Math.round(image.height / FROST_DOWNSAMPLE));
481
+ if (this.#frostedSize !== `${w}x${h}`) {
482
+ this.#deleteFrosted();
483
+ this.#frosted = [createTarget(gl, w, h), createTarget(gl, w, h)];
484
+ this.#frostedSize = `${w}x${h}`;
485
+ }
486
+ const [a, b] = this.#frosted;
487
+ gl.viewport(0, 0, w, h);
488
+ this.#pass(this.#copy, this.#backdrop, a);
489
+ for (let i = 0; i < FROST_PASSES; i++) {
490
+ this.#pass(this.#blur, a.texture, b, [1 / w, 0]);
491
+ this.#pass(this.#blur, b.texture, a, [0, 1 / h]);
492
+ }
493
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
494
+ }
495
+ draw(frame) {
496
+ const gl = this.#gl;
497
+ const { uniform } = this.#glass;
498
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
499
+ gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
500
+ gl.clearColor(0, 0, 0, 0);
501
+ gl.clear(gl.COLOR_BUFFER_BIT);
502
+ if (!this.#backdrop) return;
503
+ gl.useProgram(this.#glass.program);
504
+ gl.activeTexture(gl.TEXTURE1);
505
+ gl.bindTexture(gl.TEXTURE_2D, this.#frosted[0].texture);
506
+ gl.uniform1i(uniform("u_frosted"), 1);
507
+ gl.activeTexture(gl.TEXTURE0);
508
+ gl.bindTexture(gl.TEXTURE_2D, this.#backdrop);
509
+ gl.uniform1i(uniform("u_backdrop"), 0);
510
+ gl.uniform2f(uniform("u_size"), frame.width, frame.height);
511
+ gl.uniform2f(uniform("u_offset"), frame.offsetX, frame.offsetY);
512
+ gl.uniform2f(uniform("u_backdropSize"), this.#backdropWidth, this.#backdropHeight);
513
+ gl.uniform1f(uniform("u_scale"), frame.scale);
514
+ gl.uniform1f(uniform("u_smoothing"), frame.smoothing);
515
+ gl.uniform1f(uniform("u_dark"), frame.dark);
516
+ gl.uniform1f(uniform("u_shadow"), frame.shadow);
517
+ gl.uniform3f(uniform("u_pointer"), ...frame.pointer);
518
+ const shapes = frame.shapes.slice(0, 16);
519
+ const bounds = /* @__PURE__ */ new Float32Array(64);
520
+ const material = /* @__PURE__ */ new Float32Array(64);
521
+ const tint = /* @__PURE__ */ new Float32Array(64);
522
+ shapes.forEach((shape, i) => {
523
+ bounds.set([
524
+ shape.centerX,
525
+ shape.centerY,
526
+ shape.halfWidth,
527
+ shape.halfHeight
528
+ ], i * 4);
529
+ material.set([
530
+ shape.radius,
531
+ shape.frost,
532
+ 0,
533
+ 0
534
+ ], i * 4);
535
+ tint.set(shape.tint, i * 4);
536
+ });
537
+ gl.uniform1i(uniform("u_count"), shapes.length);
538
+ gl.uniform4fv(uniform("u_bounds"), bounds);
539
+ gl.uniform4fv(uniform("u_material"), material);
540
+ gl.uniform4fv(uniform("u_tint"), tint);
541
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
542
+ }
543
+ dispose() {
544
+ const gl = this.#gl;
545
+ this.#deleteFrosted();
546
+ gl.deleteTexture(this.#backdrop);
547
+ gl.deleteBuffer(this.#quad);
548
+ for (const { program } of [
549
+ this.#copy,
550
+ this.#blur,
551
+ this.#glass
552
+ ]) gl.deleteProgram(program);
553
+ gl.getExtension("WEBGL_lose_context")?.loseContext();
554
+ }
555
+ #pass(program, source, target, direction) {
556
+ const gl = this.#gl;
557
+ gl.useProgram(program.program);
558
+ gl.bindFramebuffer(gl.FRAMEBUFFER, target.framebuffer);
559
+ gl.activeTexture(gl.TEXTURE0);
560
+ gl.bindTexture(gl.TEXTURE_2D, source);
561
+ gl.uniform1i(program.uniform("u_source"), 0);
562
+ if (direction) gl.uniform2f(program.uniform("u_direction"), ...direction);
563
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
564
+ }
565
+ #deleteFrosted() {
566
+ for (const target of this.#frosted ?? []) {
567
+ this.#gl.deleteTexture(target.texture);
568
+ this.#gl.deleteFramebuffer(target.framebuffer);
569
+ }
570
+ this.#frosted = null;
571
+ }
572
+ };
573
+ const presses = /* @__PURE__ */ new WeakMap();
574
+ const PRESS_IN_MS = 70;
575
+ const PRESS_OUT_MS = 180;
576
+ const trackPress = (element) => {
577
+ const state = {
578
+ target: 0,
579
+ value: 0,
580
+ x: 0,
581
+ y: 0
582
+ };
583
+ presses.set(element, state);
584
+ const follow = (event) => {
585
+ state.x = event.clientX;
586
+ state.y = event.clientY;
587
+ };
588
+ const release = () => {
589
+ state.target = 0;
590
+ };
591
+ const options = { passive: true };
592
+ element.addEventListener("pointerdown", (event) => {
593
+ if (!element.hasAttribute("glass-interactive")) return;
594
+ state.target = 1;
595
+ follow(event);
596
+ }, options);
597
+ element.addEventListener("pointermove", (event) => {
598
+ if (state.target) follow(event);
599
+ }, options);
600
+ for (const type of [
601
+ "pointerup",
602
+ "pointercancel",
603
+ "pointerleave"
604
+ ]) element.addEventListener(type, release, options);
605
+ };
606
+ const pressOf = (element) => presses.get(element);
607
+ const stepPress = (state, elapsed) => {
608
+ const time = state.target ? PRESS_IN_MS : PRESS_OUT_MS;
609
+ const next = state.target + (state.value - state.target) * Math.exp(-elapsed / time);
610
+ state.value = Math.abs(next - state.target) < .001 ? state.target : next;
611
+ return state.value;
612
+ };
613
+ const glassRole = (element) => {
614
+ const effect = element.getAttribute("blur-effect");
615
+ if (effect === "glass-container") return "container";
616
+ if (effect !== "glass") return null;
617
+ return element.parentElement?.getAttribute("blur-effect") === "glass-container" ? "member" : "standalone";
618
+ };
619
+ const containerShapes = (container) => Array.from(container.children).filter((child) => child.getAttribute("blur-effect") === "glass");
620
+ const cornerRadius = (value, width, height) => {
621
+ const shorter = Math.min(width, height);
622
+ const radius = value.endsWith("%") ? parseFloat(value) / 100 * shorter : parseFloat(value) || 0;
623
+ return Math.min(radius, shorter / 2);
624
+ };
625
+ const PRESS_GROWTH = .06;
626
+ const readShape = (element, origin, zoom, elapsed, grow) => {
627
+ const box = element.getBoundingClientRect();
628
+ const left = (box.left - origin.left) / zoom;
629
+ const top = (box.top - origin.top) / zoom;
630
+ const width = box.width / zoom;
631
+ const height = box.height / zoom;
632
+ const style = element.ownerDocument.defaultView.getComputedStyle(element);
633
+ const press = pressOf(element);
634
+ const pressed = press ? stepPress(press, elapsed) : 0;
635
+ const swell = grow ? 1 + PRESS_GROWTH * pressed : 1;
636
+ return {
637
+ centerX: left + width / 2,
638
+ centerY: top + height / 2,
639
+ halfWidth: width / 2 * swell,
640
+ halfHeight: height / 2 * swell,
641
+ radius: cornerRadius(style.borderTopLeftRadius, width, height) * swell,
642
+ frost: element.getAttribute("glass-style") === "clear" ? 0 : 1,
643
+ tint: resolveCssColor(element.ownerDocument, element.getAttribute("glass-tint-color")),
644
+ press: pressed
645
+ };
646
+ };
647
+ const CAPTURE_MARGIN = 48;
648
+ const MIN_CAPTURE_INTERVAL = 100;
649
+ const MAX_SCALE = 2;
650
+ const DARK_LUMINANCE = .4;
651
+ var GlassGroup = class {
652
+ root;
653
+ container;
654
+ #win;
655
+ #canvas;
656
+ #renderer;
657
+ #backdrop;
658
+ #stopObserving = null;
659
+ #snapshot = null;
660
+ #stale = true;
661
+ #capturing = false;
662
+ #failed = false;
663
+ #disposed = false;
664
+ #nextCapture = 0;
665
+ #drawn = "";
666
+ constructor(root, container) {
667
+ this.root = root;
668
+ this.container = container;
669
+ const doc = root.ownerDocument;
670
+ this.#win = doc.defaultView;
671
+ this.#canvas = doc.createElement("canvas");
672
+ this.#canvas.setAttribute("aria-hidden", "true");
673
+ this.#canvas.style.cssText = "position:absolute;left:0;top:0;width:100%;height:100%;pointer-events:none;z-index:-1;opacity:0;transition:opacity 0.2s";
674
+ const shadow = root.shadowRoot;
675
+ shadow?.insertBefore(this.#canvas, shadow.querySelector("slot"));
676
+ this.#renderer = shadow ? GlassRenderer.create(this.#canvas) : null;
677
+ this.#backdrop = root.parentElement;
678
+ if (this.#renderer && this.#backdrop) this.#stopObserving = observeBackdrop(this.#backdrop, root, () => {
679
+ this.#stale = true;
680
+ });
681
+ }
682
+ get shapes() {
683
+ return this.container ? containerShapes(this.root) : [this.root];
684
+ }
685
+ get active() {
686
+ return this.#drawn !== "" && !this.#failed;
687
+ }
688
+ get lost() {
689
+ return this.#renderer?.lost ?? false;
690
+ }
691
+ frame(elapsed) {
692
+ const renderer = this.#renderer;
693
+ const backdrop = this.#backdrop;
694
+ if (!renderer || !backdrop || this.#failed) return;
695
+ const origin = this.#canvas.getBoundingClientRect();
696
+ const area = backdrop.getBoundingClientRect();
697
+ const zoom = drawnScale(backdrop);
698
+ if (this.#needsCapture(origin, area, zoom)) this.#capture(backdrop, origin, zoom);
699
+ const snapshot = this.#snapshot;
700
+ if (!snapshot) return;
701
+ const scale = Math.min(this.#win.devicePixelRatio || 1, MAX_SCALE) * zoom;
702
+ const elements = this.shapes;
703
+ const shapes = elements.map((element) => readShape(element, origin, zoom, elapsed, this.container));
704
+ let pointer = [
705
+ 0,
706
+ 0,
707
+ 0
708
+ ];
709
+ elements.forEach((element, i) => {
710
+ const value = shapes[i].press;
711
+ if (value > pointer[2]) {
712
+ const { x, y } = pressOf(element);
713
+ pointer = [
714
+ (x - origin.left) / zoom,
715
+ (y - origin.top) / zoom,
716
+ value
717
+ ];
718
+ }
719
+ });
720
+ const spacing = parseFloat(this.root.getAttribute("spacing") ?? "") || 0;
721
+ const frame = {
722
+ scale,
723
+ width: origin.width / zoom,
724
+ height: origin.height / zoom,
725
+ offsetX: (origin.left - area.left) / zoom - snapshot.x,
726
+ offsetY: (origin.top - area.top) / zoom - snapshot.y,
727
+ smoothing: this.container ? spacing * 2 : 0,
728
+ dark: this.#dark(snapshot),
729
+ shadow: this.container ? 1 : 0,
730
+ shapes,
731
+ pointer
732
+ };
733
+ const key = JSON.stringify(frame);
734
+ if (key === this.#drawn) return;
735
+ const width = Math.max(1, Math.round(frame.width * scale));
736
+ const height = Math.max(1, Math.round(frame.height * scale));
737
+ if (this.#canvas.width !== width) this.#canvas.width = width;
738
+ if (this.#canvas.height !== height) this.#canvas.height = height;
739
+ renderer.draw(frame);
740
+ this.#drawn = key;
741
+ this.#canvas.style.opacity = "1";
742
+ }
743
+ dispose() {
744
+ this.#disposed = true;
745
+ this.#stopObserving?.();
746
+ this.#renderer?.dispose();
747
+ this.#canvas.remove();
748
+ }
749
+ #dark(snapshot) {
750
+ const forced = this.root.getAttribute("ios-user-interface-style");
751
+ if (forced) return forced === "dark" ? 1 : 0;
752
+ return snapshot.luminance < DARK_LUMINANCE ? 1 : 0;
753
+ }
754
+ #needsCapture(origin, area, zoom) {
755
+ if (this.#capturing) return false;
756
+ const snapshot = this.#snapshot;
757
+ if (snapshot) {
758
+ const left = (Math.max(origin.left, area.left) - area.left) / zoom;
759
+ const top = (Math.max(origin.top, area.top) - area.top) / zoom;
760
+ const right = (Math.min(origin.right, area.right) - area.left) / zoom;
761
+ const bottom = (Math.min(origin.bottom, area.bottom) - area.top) / zoom;
762
+ if (left < snapshot.x - 1 || top < snapshot.y - 1 || right > snapshot.x + snapshot.width + 1 || bottom > snapshot.y + snapshot.height + 1) this.#stale = true;
763
+ }
764
+ return this.#stale && this.#win.performance.now() >= this.#nextCapture;
765
+ }
766
+ #capture(backdrop, origin, zoom) {
767
+ const win = this.#win;
768
+ this.#capturing = true;
769
+ this.#stale = false;
770
+ const started = win.performance.now();
771
+ const above = /* @__PURE__ */ new Set();
772
+ for (let node = this.root; node; node = node.nextElementSibling) above.add(node);
773
+ const margin = CAPTURE_MARGIN * zoom;
774
+ snapshotBackdrop(backdrop, {
775
+ exclude: (element) => above.has(element),
776
+ region: {
777
+ left: origin.left - margin,
778
+ top: origin.top - margin,
779
+ right: origin.right + margin,
780
+ bottom: origin.bottom + margin
781
+ },
782
+ scale: Math.min(win.devicePixelRatio || 1, MAX_SCALE) * zoom
783
+ }).then((snapshot) => {
784
+ if (this.#disposed || !snapshot) return;
785
+ this.#renderer.setBackdrop(snapshot.image, snapshot.width, snapshot.height);
786
+ this.#snapshot = snapshot;
787
+ this.#drawn = "";
788
+ }).catch(() => {
789
+ this.#failed = true;
790
+ }).finally(() => {
791
+ this.#capturing = false;
792
+ const now = win.performance.now();
793
+ this.#nextCapture = now + Math.max(MIN_CAPTURE_INTERVAL, (now - started) * 4);
794
+ });
795
+ }
796
+ };
797
+ const styles = /* @__PURE__ */ new WeakMap();
798
+ const DEFAULT_FALLBACK_BLUR = 12;
799
+ const fallbackGlassCss = (element) => {
800
+ const clear = element.getAttribute("glass-style") === "clear";
801
+ const filter = `blur(${parseFloat(element.getAttribute("blur-radius") ?? "") || (clear ? DEFAULT_FALLBACK_BLUR / 3 : DEFAULT_FALLBACK_BLUR)}px) saturate(${clear ? 140 : 180}%)`;
802
+ return `:host{backdrop-filter:${filter};-webkit-backdrop-filter:${filter};background-color:${element.getAttribute("glass-tint-color") || `rgba(255, 255, 255, ${clear ? .04 : .18})`};box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.5), inset 0 0 0 1px rgba(255, 255, 255, 0.25)}`;
803
+ };
804
+ const materialCss = (element) => {
805
+ const effect = element.getAttribute("blur-effect") ?? "light";
806
+ const radius = parseFloat(element.getAttribute("blur-radius") ?? "");
807
+ if (!radius || effect === "glass" || effect === "glass-container") return "";
808
+ if (effect === "extra-light") return ":host{background-color:rgba(255, 255, 255, 0.75)}";
809
+ if (effect === "dark") return ":host{background-color:rgba(0, 0, 0, 0.6)}";
810
+ const filter = `blur(${radius}px) brightness(1.35)`;
811
+ return `:host{backdrop-filter:${filter};-webkit-backdrop-filter:${filter}}`;
812
+ };
813
+ const hostStyleCss = (element, state) => [
814
+ materialCss(element),
815
+ state.hostsCanvas ? ":host{isolation:isolate}" : "",
816
+ state.rendered ? ":host{backdrop-filter:none!important;-webkit-backdrop-filter:none!important}" : "",
817
+ state.fallback ? fallbackGlassCss(element) : ""
818
+ ].join("");
819
+ const applyHostStyle = (element, state) => {
820
+ const root = element.shadowRoot;
821
+ if (!root) return;
822
+ const css = hostStyleCss(element, state);
823
+ let style = styles.get(element);
824
+ if (!style) {
825
+ if (!css) return;
826
+ style = element.ownerDocument.createElement("style");
827
+ styles.set(element, style);
828
+ }
829
+ if (style.parentNode !== root) root.appendChild(style);
830
+ if (style.textContent !== css) style.textContent = css;
831
+ };
832
+ const GLASS_ATTRIBUTES = [
833
+ "blur-effect",
834
+ "blur-radius",
835
+ "glass-style",
836
+ "glass-tint-color",
837
+ "glass-interactive",
838
+ "spacing",
839
+ "ios-user-interface-style"
840
+ ];
841
+ const DETACHED_POLL_MS = 1e3;
842
+ const MAX_ELAPSED_MS = 100;
843
+ const managers = /* @__PURE__ */ new WeakMap();
844
+ var GlassManager = class GlassManager {
845
+ #win;
846
+ #visible = /* @__PURE__ */ new Set();
847
+ #groups = /* @__PURE__ */ new Map();
848
+ #detached = /* @__PURE__ */ new Set();
849
+ #intersection;
850
+ #attributes;
851
+ #frame = 0;
852
+ #poll = 0;
853
+ #last = 0;
854
+ constructor(win) {
855
+ this.#win = win;
856
+ this.#intersection = new win.IntersectionObserver((entries) => this.#onIntersection(entries));
857
+ this.#attributes = new win.MutationObserver(() => this.#wake());
858
+ }
859
+ static for(win) {
860
+ let manager = managers.get(win);
861
+ if (!manager) {
862
+ manager = new GlassManager(win);
863
+ managers.set(win, manager);
864
+ }
865
+ return manager;
866
+ }
867
+ register(element) {
868
+ this.#intersection.observe(element);
869
+ this.#attributes.observe(element, {
870
+ attributes: true,
871
+ attributeFilter: GLASS_ATTRIBUTES
872
+ });
873
+ trackPress(element);
874
+ }
875
+ #onIntersection(entries) {
876
+ for (const { target, isIntersecting } of entries) {
877
+ const element = target;
878
+ if (isIntersecting) {
879
+ this.#visible.add(element);
880
+ continue;
881
+ }
882
+ this.#visible.delete(element);
883
+ if (!element.isConnected) {
884
+ this.#intersection.unobserve(element);
885
+ this.#detached.add(new WeakRef(element));
886
+ if (!this.#poll) this.#poll = this.#win.setInterval(() => this.#checkDetached(), DETACHED_POLL_MS);
887
+ }
888
+ }
889
+ this.#wake();
890
+ }
891
+ #checkDetached() {
892
+ for (const ref of this.#detached) {
893
+ const element = ref.deref();
894
+ if (element && !element.isConnected) continue;
895
+ if (element) this.#intersection.observe(element);
896
+ this.#detached.delete(ref);
897
+ }
898
+ if (!this.#detached.size) {
899
+ this.#win.clearInterval(this.#poll);
900
+ this.#poll = 0;
901
+ }
902
+ }
903
+ #wake() {
904
+ if (this.#frame) return;
905
+ this.#frame = this.#win.requestAnimationFrame((now) => this.#tick(now));
906
+ }
907
+ #tick(now) {
908
+ this.#frame = 0;
909
+ const elapsed = this.#last ? Math.min(now - this.#last, MAX_ELAPSED_MS) : 0;
910
+ this.#last = now;
911
+ const roots = /* @__PURE__ */ new Map();
912
+ for (const element of this.#visible) {
913
+ const role = glassRole(element);
914
+ if (role === "container" || role === "standalone") roots.set(element, role === "container");
915
+ }
916
+ for (const [root, group] of this.#groups) if (roots.get(root) !== group.container || group.lost) {
917
+ group.dispose();
918
+ this.#groups.delete(root);
919
+ }
920
+ for (const [root, container] of roots) if (!this.#groups.has(root)) this.#groups.set(root, new GlassGroup(root, container));
921
+ const styled = new Set(this.#visible);
922
+ const rendered = /* @__PURE__ */ new Set();
923
+ for (const group of this.#groups.values()) {
924
+ group.frame(elapsed);
925
+ for (const shape of group.shapes) {
926
+ styled.add(shape);
927
+ if (group.active) rendered.add(shape);
928
+ }
929
+ }
930
+ for (const element of styled) applyHostStyle(element, {
931
+ hostsCanvas: this.#groups.get(element)?.active ?? false,
932
+ rendered: rendered.has(element),
933
+ fallback: element.getAttribute("blur-effect") === "glass" && !rendered.has(element)
934
+ });
935
+ if (this.#groups.size) this.#wake();
936
+ else this.#last = 0;
937
+ }
938
+ };
939
+ const attachWebGlass = (element) => {
940
+ const node = element;
941
+ const win = node.ownerDocument?.defaultView;
942
+ if (!win) return;
943
+ applyBlurViewLayout(node);
944
+ if (!win.IntersectionObserver) return;
945
+ try {
946
+ GlassManager.for(win).register(node);
947
+ } catch {}
948
+ };
949
+ export { attachWebGlass };
950
+
951
+ //# sourceMappingURL=blotch-angular-lynx-web-glass-8MFrhPz0.mjs.map