@leeguoo/pwtk-network-debugger 1.3.7 → 1.3.8
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/{button-B5OqHDUm.mjs → button-CboZyj7j.mjs} +46 -31
- package/dist/button-DXA15AWP.js +1 -0
- package/dist/container-CAnQoMGr.js +1 -0
- package/dist/{container-fXvGYIU-.mjs → container-D0255Dzc.mjs} +146 -15
- package/dist/index.cjs.js +4 -4
- package/dist/index.esm.js +442 -453
- package/dist/index.js +4 -4
- package/package.json +1 -1
- package/dist/button-Bcbh--qO.js +0 -1
- package/dist/container-CwCxCCvi.js +0 -1
|
@@ -5,11 +5,13 @@ class Button extends Container {
|
|
|
5
5
|
const onClick = options.onClick || null;
|
|
6
6
|
const type = options.type || "rounded";
|
|
7
7
|
const warp = options.warp !== void 0 ? options.warp : false;
|
|
8
|
-
const tintOpacity = options.tintOpacity !== void 0 ? options.tintOpacity : 0.
|
|
8
|
+
const tintOpacity = options.tintOpacity !== void 0 ? options.tintOpacity : 0.06;
|
|
9
|
+
const glassOpacity = options.glassOpacity !== void 0 ? options.glassOpacity : 0.08;
|
|
9
10
|
super({
|
|
10
11
|
borderRadius: fontSize,
|
|
11
12
|
type,
|
|
12
|
-
tintOpacity
|
|
13
|
+
tintOpacity,
|
|
14
|
+
glassOpacity
|
|
13
15
|
});
|
|
14
16
|
this.text = text;
|
|
15
17
|
this.fontSize = fontSize;
|
|
@@ -27,6 +29,21 @@ class Button extends Container {
|
|
|
27
29
|
this.setupClickHandler();
|
|
28
30
|
this.setSizeFromText();
|
|
29
31
|
}
|
|
32
|
+
updateCanvasSize(width, height) {
|
|
33
|
+
if (!this.canvas) return;
|
|
34
|
+
this.dpr = Container.getDevicePixelRatio();
|
|
35
|
+
const pixelWidth = Math.max(1, Math.round(width * this.dpr));
|
|
36
|
+
const pixelHeight = Math.max(1, Math.round(height * this.dpr));
|
|
37
|
+
this.canvas.width = pixelWidth;
|
|
38
|
+
this.canvas.height = pixelHeight;
|
|
39
|
+
this.canvas.style.width = width + "px";
|
|
40
|
+
this.canvas.style.height = height + "px";
|
|
41
|
+
if (this.gl_refs.gl) {
|
|
42
|
+
this.gl_refs.gl.viewport(0, 0, pixelWidth, pixelHeight);
|
|
43
|
+
this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc, width, height);
|
|
44
|
+
this.gl_refs.gl.uniform1f(this.gl_refs.borderRadiusLoc, this.borderRadius);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
30
47
|
setSizeFromText() {
|
|
31
48
|
let width, height;
|
|
32
49
|
if (this.type === "circle") {
|
|
@@ -62,17 +79,7 @@ class Button extends Container {
|
|
|
62
79
|
if (this.type === "circle") {
|
|
63
80
|
this.width = width;
|
|
64
81
|
this.height = height;
|
|
65
|
-
|
|
66
|
-
this.canvas.width = width;
|
|
67
|
-
this.canvas.height = height;
|
|
68
|
-
this.canvas.style.width = width + "px";
|
|
69
|
-
this.canvas.style.height = height + "px";
|
|
70
|
-
if (this.gl_refs.gl) {
|
|
71
|
-
this.gl_refs.gl.viewport(0, 0, width, height);
|
|
72
|
-
this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc, width, height);
|
|
73
|
-
this.gl_refs.gl.uniform1f(this.gl_refs.borderRadiusLoc, this.borderRadius);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
82
|
+
this.updateCanvasSize(width, height);
|
|
76
83
|
} else if (this.type === "pill") {
|
|
77
84
|
this.width = width;
|
|
78
85
|
this.height = height;
|
|
@@ -80,17 +87,7 @@ class Button extends Container {
|
|
|
80
87
|
this.element.style.height = height + "px";
|
|
81
88
|
this.element.style.maxWidth = width + "px";
|
|
82
89
|
this.element.style.maxHeight = height + "px";
|
|
83
|
-
|
|
84
|
-
this.canvas.width = width;
|
|
85
|
-
this.canvas.height = height;
|
|
86
|
-
this.canvas.style.width = width + "px";
|
|
87
|
-
this.canvas.style.height = height + "px";
|
|
88
|
-
if (this.gl_refs.gl) {
|
|
89
|
-
this.gl_refs.gl.viewport(0, 0, width, height);
|
|
90
|
-
this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc, width, height);
|
|
91
|
-
this.gl_refs.gl.uniform1f(this.gl_refs.borderRadiusLoc, this.borderRadius);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
90
|
+
this.updateCanvasSize(width, height);
|
|
94
91
|
} else {
|
|
95
92
|
this.updateSizeFromDOM();
|
|
96
93
|
}
|
|
@@ -140,8 +137,7 @@ class Button extends Container {
|
|
|
140
137
|
super.applySnapshot(snapshotCanvas);
|
|
141
138
|
}
|
|
142
139
|
initNestedGlass() {
|
|
143
|
-
if (!this.parent.webglInitialized) {
|
|
144
|
-
setTimeout(() => this.initNestedGlass(), 100);
|
|
140
|
+
if (!this.parent || !this.parent.webglInitialized) {
|
|
145
141
|
return;
|
|
146
142
|
}
|
|
147
143
|
this.setupDynamicNestedShader();
|
|
@@ -179,7 +175,8 @@ class Button extends Container {
|
|
|
179
175
|
uniform float u_cornerBoost;
|
|
180
176
|
uniform float u_rippleEffect;
|
|
181
177
|
uniform float u_tintOpacity;
|
|
182
|
-
|
|
178
|
+
uniform float u_glassOpacity;
|
|
179
|
+
varying vec2 v_texcoord;
|
|
183
180
|
|
|
184
181
|
// Function to calculate distance from rounded rectangle edge
|
|
185
182
|
float roundedRectDistance(vec2 coord, vec2 size, float radius) {
|
|
@@ -337,7 +334,7 @@ class Button extends Container {
|
|
|
337
334
|
|
|
338
335
|
// HIGH-QUALITY BUTTON BLUR on container texture
|
|
339
336
|
vec4 color = vec4(0.0);
|
|
340
|
-
vec2 texelSize = 1.0 /
|
|
337
|
+
vec2 texelSize = 1.0 / u_textureSize;
|
|
341
338
|
float sigma = u_blurRadius / 3.0; // More substantial blur
|
|
342
339
|
vec2 blurStep = texelSize * sigma;
|
|
343
340
|
|
|
@@ -413,8 +410,9 @@ class Button extends Container {
|
|
|
413
410
|
maskDistance = roundedRectDistance(coord, u_resolution, u_borderRadius);
|
|
414
411
|
}
|
|
415
412
|
float mask = 1.0 - smoothstep(-1.0, 1.0, maskDistance);
|
|
413
|
+
float opacity = clamp(u_glassOpacity, 0.02, 0.95);
|
|
416
414
|
|
|
417
|
-
gl_FragColor = vec4(finalTinted, mask);
|
|
415
|
+
gl_FragColor = vec4(finalTinted, mask * opacity);
|
|
418
416
|
}
|
|
419
417
|
`;
|
|
420
418
|
const program = this.createProgram(gl, vsSource, fsSource);
|
|
@@ -445,6 +443,7 @@ class Button extends Container {
|
|
|
445
443
|
const cornerBoostLoc = gl.getUniformLocation(program, "u_cornerBoost");
|
|
446
444
|
const rippleEffectLoc = gl.getUniformLocation(program, "u_rippleEffect");
|
|
447
445
|
const tintOpacityLoc = gl.getUniformLocation(program, "u_tintOpacity");
|
|
446
|
+
const glassOpacityLoc = gl.getUniformLocation(program, "u_glassOpacity");
|
|
448
447
|
const imageLoc = gl.getUniformLocation(program, "u_image");
|
|
449
448
|
const texture = gl.createTexture();
|
|
450
449
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
@@ -486,6 +485,7 @@ class Button extends Container {
|
|
|
486
485
|
cornerBoostLoc,
|
|
487
486
|
rippleEffectLoc,
|
|
488
487
|
tintOpacityLoc,
|
|
488
|
+
glassOpacityLoc,
|
|
489
489
|
imageLoc,
|
|
490
490
|
positionBuffer,
|
|
491
491
|
texcoordBuffer
|
|
@@ -498,7 +498,9 @@ class Button extends Container {
|
|
|
498
498
|
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
|
|
499
499
|
gl.enableVertexAttribArray(texcoordLoc);
|
|
500
500
|
gl.vertexAttribPointer(texcoordLoc, 2, gl.FLOAT, false, 0, 0);
|
|
501
|
-
|
|
501
|
+
const cssWidth = this.width || Math.max(1, Math.round(this.canvas.width / this.dpr));
|
|
502
|
+
const cssHeight = this.height || Math.max(1, Math.round(this.canvas.height / this.dpr));
|
|
503
|
+
gl.uniform2f(resolutionLoc, cssWidth, cssHeight);
|
|
502
504
|
gl.uniform2f(textureSizeLoc, containerCanvas.width, containerCanvas.height);
|
|
503
505
|
gl.uniform1f(blurRadiusLoc, window.glassControls?.blurRadius || 2);
|
|
504
506
|
gl.uniform1f(borderRadiusLoc, this.borderRadius);
|
|
@@ -512,6 +514,7 @@ class Button extends Container {
|
|
|
512
514
|
gl.uniform1f(cornerBoostLoc, window.glassControls?.cornerBoost || 0.02);
|
|
513
515
|
gl.uniform1f(rippleEffectLoc, window.glassControls?.rippleEffect || 0.1);
|
|
514
516
|
gl.uniform1f(tintOpacityLoc, this.tintOpacity);
|
|
517
|
+
gl.uniform1f(glassOpacityLoc, window.glassControls?.glassOpacity ?? this.glassOpacity);
|
|
515
518
|
const buttonPosition = this.getPosition();
|
|
516
519
|
const containerPosition = this.parent.getPosition();
|
|
517
520
|
gl.uniform2f(buttonPositionLoc, buttonPosition.x, buttonPosition.y);
|
|
@@ -528,7 +531,15 @@ class Button extends Container {
|
|
|
528
531
|
this.nestedAnimationFrameId = null;
|
|
529
532
|
}
|
|
530
533
|
const render = () => {
|
|
531
|
-
if (!this.gl_refs.gl || !this.parent) return;
|
|
534
|
+
if (this.destroyed || !this.gl_refs.gl || !this.parent) return;
|
|
535
|
+
if (!this.element || !this.element.isConnected) {
|
|
536
|
+
this.stopNestedAnimation();
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
if (!this.parent.element || !this.parent.element.isConnected) {
|
|
540
|
+
this.stopNestedAnimation();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
532
543
|
const gl = this.gl_refs.gl;
|
|
533
544
|
const containerCanvas = this.parent.canvas;
|
|
534
545
|
gl.bindTexture(gl.TEXTURE_2D, this.gl_refs.texture);
|
|
@@ -561,6 +572,10 @@ class Button extends Container {
|
|
|
561
572
|
this.nestedAnimationFrameId = null;
|
|
562
573
|
}
|
|
563
574
|
}
|
|
575
|
+
destroy() {
|
|
576
|
+
this.stopNestedAnimation();
|
|
577
|
+
super.destroy();
|
|
578
|
+
}
|
|
564
579
|
}
|
|
565
580
|
if (typeof window !== "undefined") {
|
|
566
581
|
window.Button = Button;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class t extends Container{constructor(t={}){const e=t.text||"Button",i=parseInt(t.size)||48,n=t.onClick||null,o=t.type||"rounded",s=void 0!==t.warp?t.warp:!1;super({borderRadius:i,type:o,tintOpacity:void 0!==t.tintOpacity?t.tintOpacity:.06,glassOpacity:void 0!==t.glassOpacity?t.glassOpacity:.08});this.text=e;this.fontSize=i;this.onClick=n;this.type=o;this.warp=s;this.parent=null;this.isNestedGlass=!1;this.nestedAnimationFrameId=null;this.element.classList.add("glass-button");if("circle"===this.type)this.element.classList.add("glass-button-circle");this.createTextElement();this.setupClickHandler();this.setSizeFromText()}updateCanvasSize(t,e){if(!this.canvas)return;this.dpr=Container.getDevicePixelRatio();const i=Math.max(1,Math.round(t*this.dpr)),n=Math.max(1,Math.round(e*this.dpr));this.canvas.width=i;this.canvas.height=n;this.canvas.style.width=t+"px";this.canvas.style.height=e+"px";if(this.gl_refs.gl){this.gl_refs.gl.viewport(0,0,i,n);this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc,t,e);this.gl_refs.gl.uniform1f(this.gl_refs.borderRadiusLoc,this.borderRadius)}}setSizeFromText(){let e,i;if("circle"===this.type){const t=2.5*this.fontSize;e=t;i=t;this.borderRadius=t/2;this.element.style.width=e+"px";this.element.style.height=i+"px";this.element.style.minWidth=e+"px";this.element.style.minHeight=i+"px";this.element.style.maxWidth=e+"px";this.element.style.maxHeight=i+"px"}else if("pill"===this.type){const n=t.measureText(this.text,this.fontSize);e=Math.ceil(n.width+2*this.fontSize);i=Math.ceil(this.fontSize+1.2*this.fontSize);this.borderRadius=i/2;this.element.style.minWidth=e+"px";this.element.style.minHeight=i+"px"}else{const n=t.measureText(this.text,this.fontSize);e=Math.ceil(n.width+2*this.fontSize);i=Math.ceil(this.fontSize+1.5*this.fontSize);this.borderRadius=this.fontSize;this.element.style.minWidth=e+"px";this.element.style.minHeight=i+"px"}this.element.style.borderRadius=this.borderRadius+"px";if(this.canvas)this.canvas.style.borderRadius=this.borderRadius+"px";if("circle"===this.type){this.width=e;this.height=i;this.updateCanvasSize(e,i)}else if("pill"===this.type){this.width=e;this.height=i;this.element.style.width=e+"px";this.element.style.height=i+"px";this.element.style.maxWidth=e+"px";this.element.style.maxHeight=i+"px";this.updateCanvasSize(e,i)}else this.updateSizeFromDOM()}setupAsNestedGlass(){if(this.parent&&!this.isNestedGlass){this.isNestedGlass=!0;if(this.webglInitialized)this.initWebGL()}}static measureText(t,e){const i=document.createElement("canvas").getContext("2d");i.font=`${e}px system-ui, -apple-system, sans-serif`;return i.measureText(t)}createTextElement(){this.textElement=document.createElement("div");this.textElement.className="glass-button-text";this.textElement.textContent=this.text;this.textElement.style.fontSize=this.fontSize+"px";this.element.appendChild(this.textElement)}setupClickHandler(){if(this.onClick&&this.element)this.element.addEventListener("click",t=>{t.preventDefault();this.onClick(this.text)})}initWebGL(){if(Container.pageSnapshot&&this.gl)if(this.parent&&this.isNestedGlass)this.initNestedGlass();else super.initWebGL()}applySnapshot(t){if(!this.parent||!this.isNestedGlass)super.applySnapshot(t)}initNestedGlass(){if(this.parent&&this.parent.webglInitialized){this.setupDynamicNestedShader();this.webglInitialized=!0}}setupDynamicNestedShader(){const t=this.gl,e=this.createProgram(t,"\n attribute vec2 a_position;\n attribute vec2 a_texcoord;\n varying vec2 v_texcoord;\n\n void main() {\n gl_Position = vec4(a_position, 0, 1);\n v_texcoord = a_texcoord;\n }\n ","\n precision mediump float;\n uniform sampler2D u_image;\n uniform vec2 u_resolution;\n uniform vec2 u_textureSize;\n uniform float u_blurRadius;\n uniform float u_borderRadius;\n uniform vec2 u_buttonPosition;\n uniform vec2 u_containerPosition;\n uniform vec2 u_containerSize;\n uniform float u_warp;\n uniform float u_edgeIntensity;\n uniform float u_rimIntensity;\n uniform float u_baseIntensity;\n uniform float u_edgeDistance;\n uniform float u_rimDistance;\n uniform float u_baseDistance;\n uniform float u_cornerBoost;\n uniform float u_rippleEffect;\n uniform float u_tintOpacity;\n uniform float u_glassOpacity;\n varying vec2 v_texcoord;\n\n // Function to calculate distance from rounded rectangle edge\n float roundedRectDistance(vec2 coord, vec2 size, float radius) {\n vec2 center = size * 0.5;\n vec2 pixelCoord = coord * size;\n vec2 toCorner = abs(pixelCoord - center) - (center - radius);\n float outsideCorner = length(max(toCorner, 0.0));\n float insideCorner = min(max(toCorner.x, toCorner.y), 0.0);\n return (outsideCorner + insideCorner - radius);\n }\n \n // Function to calculate distance from circle edge (negative inside, positive outside)\n float circleDistance(vec2 coord, vec2 size, float radius) {\n vec2 center = vec2(0.5, 0.5);\n vec2 pixelCoord = coord * size;\n vec2 centerPixel = center * size;\n float distFromCenter = length(pixelCoord - centerPixel);\n return distFromCenter - radius;\n }\n \n // Check if this is a pill (border radius is approximately 50% of height AND width > height)\n bool isPill(vec2 size, float radius) {\n float heightRatioDiff = abs(radius - size.y * 0.5);\n bool radiusMatchesHeight = heightRatioDiff < 2.0;\n bool isWiderThanTall = size.x > size.y + 4.0; // Must be significantly wider\n return radiusMatchesHeight && isWiderThanTall;\n }\n \n // Check if this is a circle (border radius is approximately 50% of smaller dimension AND roughly square)\n bool isCircle(vec2 size, float radius) {\n float minDim = min(size.x, size.y);\n bool radiusMatchesMinDim = abs(radius - minDim * 0.5) < 1.0;\n bool isRoughlySquare = abs(size.x - size.y) < 4.0; // Width and height are similar\n return radiusMatchesMinDim && isRoughlySquare;\n }\n \n // Function to calculate distance from pill edge (capsule shape)\n float pillDistance(vec2 coord, vec2 size, float radius) {\n vec2 center = size * 0.5;\n vec2 pixelCoord = coord * size;\n \n // Proper capsule: line segment with radius\n // The capsule axis runs horizontally from (radius, center.y) to (size.x - radius, center.y)\n vec2 capsuleStart = vec2(radius, center.y);\n vec2 capsuleEnd = vec2(size.x - radius, center.y);\n \n // Project point onto the capsule axis (line segment)\n vec2 capsuleAxis = capsuleEnd - capsuleStart;\n float capsuleLength = length(capsuleAxis);\n \n if (capsuleLength > 0.0) {\n vec2 toPoint = pixelCoord - capsuleStart;\n float t = clamp(dot(toPoint, capsuleAxis) / dot(capsuleAxis, capsuleAxis), 0.0, 1.0);\n vec2 closestPointOnAxis = capsuleStart + t * capsuleAxis;\n return length(pixelCoord - closestPointOnAxis) - radius;\n } else {\n // Degenerate case: just a circle\n return length(pixelCoord - center) - radius;\n }\n }\n\n void main() {\n vec2 coord = v_texcoord;\n \n // Calculate button position within container space\n vec2 buttonSize = u_resolution;\n vec2 containerSize = u_containerSize;\n \n // Convert screen positions to container-relative coordinates\n // Container position is center, convert to top-left\n vec2 containerTopLeft = u_containerPosition - containerSize * 0.5;\n vec2 buttonTopLeft = u_buttonPosition - buttonSize * 0.5;\n \n // Get button's position relative to container's top-left\n vec2 buttonRelativePos = buttonTopLeft - containerTopLeft;\n \n // Current pixel position within the button (0 to buttonSize)\n vec2 buttonPixel = coord * buttonSize;\n \n // Absolute pixel position in container space\n vec2 containerPixel = buttonRelativePos + buttonPixel;\n \n // Convert to texture coordinates (0 to 1)\n vec2 baseTextureCoord = containerPixel / containerSize;\n \n // BUTTON'S SOPHISTICATED GLASS EFFECTS on top of container's glass\n float distFromEdgeShape;\n vec2 shapeNormal; // Normal vector pointing away from shape surface\n \n if (isPill(u_resolution, u_borderRadius)) {\n distFromEdgeShape = -pillDistance(coord, u_resolution, u_borderRadius);\n \n // Calculate normal for pill shape\n vec2 center = vec2(0.5, 0.5);\n vec2 pixelCoord = coord * u_resolution;\n vec2 capsuleStart = vec2(u_borderRadius, center.y * u_resolution.y);\n vec2 capsuleEnd = vec2(u_resolution.x - u_borderRadius, center.y * u_resolution.y);\n vec2 capsuleAxis = capsuleEnd - capsuleStart;\n float capsuleLength = length(capsuleAxis);\n \n if (capsuleLength > 0.0) {\n vec2 toPoint = pixelCoord - capsuleStart;\n float t = clamp(dot(toPoint, capsuleAxis) / dot(capsuleAxis, capsuleAxis), 0.0, 1.0);\n vec2 closestPointOnAxis = capsuleStart + t * capsuleAxis;\n vec2 normalDir = pixelCoord - closestPointOnAxis;\n shapeNormal = length(normalDir) > 0.0 ? normalize(normalDir) : vec2(0.0, 1.0);\n } else {\n shapeNormal = normalize(coord - center);\n }\n } else if (isCircle(u_resolution, u_borderRadius)) {\n distFromEdgeShape = -circleDistance(coord, u_resolution, u_borderRadius);\n vec2 center = vec2(0.5, 0.5);\n shapeNormal = normalize(coord - center);\n } else {\n distFromEdgeShape = -roundedRectDistance(coord, u_resolution, u_borderRadius);\n vec2 center = vec2(0.5, 0.5);\n shapeNormal = normalize(coord - center);\n }\n distFromEdgeShape = max(distFromEdgeShape, 0.0);\n \n float distFromLeft = coord.x;\n float distFromRight = 1.0 - coord.x;\n float distFromTop = coord.y;\n float distFromBottom = 1.0 - coord.y;\n float distFromEdge = distFromEdgeShape / min(u_resolution.x, u_resolution.y);\n \n // MULTI-LAYER BUTTON GLASS REFRACTION using shape-aware normal\n float normalizedDistance = distFromEdge * min(u_resolution.x, u_resolution.y);\n float baseIntensity = 1.0 - exp(-normalizedDistance * u_baseDistance);\n float edgeIntensity = exp(-normalizedDistance * u_edgeDistance);\n float rimIntensity = exp(-normalizedDistance * u_rimDistance);\n \n // Apply center warping only if warp is enabled, keep edge and rim effects always\n float baseComponent = u_warp > 0.5 ? baseIntensity * u_baseIntensity : 0.0;\n float totalIntensity = baseComponent + edgeIntensity * u_edgeIntensity + rimIntensity * u_rimIntensity;\n \n vec2 baseRefraction = shapeNormal * totalIntensity;\n \n // Corner enhancement for buttons\n float cornerProximityX = min(distFromLeft, distFromRight);\n float cornerProximityY = min(distFromTop, distFromBottom);\n float cornerDistance = max(cornerProximityX, cornerProximityY);\n float cornerNormalized = cornerDistance * min(u_resolution.x, u_resolution.y);\n \n float cornerBoost = exp(-cornerNormalized * 0.3) * u_cornerBoost;\n vec2 cornerRefraction = shapeNormal * cornerBoost;\n \n // Button ripple texture\n vec2 perpendicular = vec2(-shapeNormal.y, shapeNormal.x);\n float rippleEffect = sin(distFromEdge * 30.0) * u_rippleEffect * rimIntensity;\n vec2 textureRefraction = perpendicular * rippleEffect;\n \n vec2 totalRefraction = baseRefraction + cornerRefraction + textureRefraction;\n vec2 textureCoord = baseTextureCoord + totalRefraction;\n \n // HIGH-QUALITY BUTTON BLUR on container texture\n vec4 color = vec4(0.0);\n vec2 texelSize = 1.0 / u_textureSize;\n float sigma = u_blurRadius / 3.0; // More substantial blur\n vec2 blurStep = texelSize * sigma;\n \n float totalWeight = 0.0;\n \n // 9x9 blur for buttons (more samples for quality)\n for(float i = -4.0; i <= 4.0; i += 1.0) {\n for(float j = -4.0; j <= 4.0; j += 1.0) {\n float distance = length(vec2(i, j));\n if(distance > 4.0) continue;\n \n float weight = exp(-(distance * distance) / (2.0 * sigma * sigma));\n \n vec2 offset = vec2(i, j) * blurStep;\n color += texture2D(u_image, textureCoord + offset) * weight;\n totalWeight += weight;\n }\n }\n \n color /= totalWeight;\n \n // BUTTON'S OWN GRADIENT LAYERS (same sophistication as container)\n float gradientPosition = coord.y;\n \n // Primary button gradient\n vec3 topTint = vec3(1.0, 1.0, 1.0);\n vec3 bottomTint = vec3(0.7, 0.7, 0.7);\n vec3 gradientTint = mix(topTint, bottomTint, gradientPosition);\n vec3 tintedColor = mix(color.rgb, gradientTint, u_tintOpacity * 0.7);\n color = vec4(tintedColor, color.a);\n \n // SECOND BUTTON GRADIENT - sampling from container's texture for variation\n vec2 viewportCenter = u_buttonPosition;\n float topY = max(0.0, (viewportCenter.y - buttonSize.y * 0.4) / containerSize.y);\n float midY = viewportCenter.y / containerSize.y;\n float bottomY = min(1.0, (viewportCenter.y + buttonSize.y * 0.4) / containerSize.y);\n \n vec3 topColor = texture2D(u_image, vec2(0.5, topY)).rgb;\n vec3 midColor = texture2D(u_image, vec2(0.5, midY)).rgb;\n vec3 bottomColor = texture2D(u_image, vec2(0.5, bottomY)).rgb;\n \n vec3 sampledGradient;\n if (gradientPosition < 0.1) {\n sampledGradient = topColor;\n } else if (gradientPosition > 0.9) {\n sampledGradient = bottomColor;\n } else {\n float transitionPos = (gradientPosition - 0.1) / 0.8;\n if (transitionPos < 0.5) {\n float t = transitionPos * 2.0;\n sampledGradient = mix(topColor, midColor, t);\n } else {\n float t = (transitionPos - 0.5) * 2.0;\n sampledGradient = mix(midColor, bottomColor, t);\n }\n }\n \n vec3 secondTinted = mix(color.rgb, sampledGradient, u_tintOpacity * 0.4);\n \n // Button highlighting/shadow system\n vec3 buttonTopTint = vec3(1.08, 1.08, 1.08); \n vec3 buttonBottomTint = vec3(0.92, 0.92, 0.92); \n vec3 buttonGradient = mix(buttonTopTint, buttonBottomTint, gradientPosition);\n vec3 finalTinted = secondTinted * buttonGradient;\n \n // Shape mask (rounded rectangle, circle, or pill)\n float maskDistance;\n if (isPill(u_resolution, u_borderRadius)) {\n maskDistance = pillDistance(coord, u_resolution, u_borderRadius);\n } else if (isCircle(u_resolution, u_borderRadius)) {\n maskDistance = circleDistance(coord, u_resolution, u_borderRadius);\n } else {\n maskDistance = roundedRectDistance(coord, u_resolution, u_borderRadius);\n }\n float mask = 1.0 - smoothstep(-1.0, 1.0, maskDistance);\n float opacity = clamp(u_glassOpacity, 0.02, 0.95);\n \n gl_FragColor = vec4(finalTinted, mask * opacity);\n }\n ");if(!e)return;t.useProgram(e);const i=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,i);t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),t.STATIC_DRAW);const n=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,n);t.bufferData(t.ARRAY_BUFFER,new Float32Array([0,1,1,1,0,0,0,0,1,1,1,0]),t.STATIC_DRAW);const o=t.getAttribLocation(e,"a_position"),s=t.getAttribLocation(e,"a_texcoord"),r=t.getUniformLocation(e,"u_resolution"),a=t.getUniformLocation(e,"u_textureSize"),l=t.getUniformLocation(e,"u_blurRadius"),c=t.getUniformLocation(e,"u_borderRadius"),u=t.getUniformLocation(e,"u_buttonPosition"),d=t.getUniformLocation(e,"u_containerPosition"),m=t.getUniformLocation(e,"u_containerSize"),h=t.getUniformLocation(e,"u_warp"),f=t.getUniformLocation(e,"u_edgeIntensity"),p=t.getUniformLocation(e,"u_rimIntensity"),g=t.getUniformLocation(e,"u_baseIntensity"),x=t.getUniformLocation(e,"u_edgeDistance"),_=t.getUniformLocation(e,"u_rimDistance"),v=t.getUniformLocation(e,"u_baseDistance"),b=t.getUniformLocation(e,"u_cornerBoost"),y=t.getUniformLocation(e,"u_rippleEffect"),R=t.getUniformLocation(e,"u_tintOpacity"),T=t.getUniformLocation(e,"u_glassOpacity"),C=t.getUniformLocation(e,"u_image"),E=t.createTexture();t.bindTexture(t.TEXTURE_2D,E);const A=this.parent.canvas;t.texImage2D(t.TEXTURE_2D,0,t.RGBA,A.width,A.height,0,t.RGBA,t.UNSIGNED_BYTE,null);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.LINEAR);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE);this.gl_refs={gl:t,texture:E,textureSizeLoc:a,positionLoc:o,texcoordLoc:s,resolutionLoc:r,blurRadiusLoc:l,borderRadiusLoc:c,buttonPositionLoc:u,containerPositionLoc:d,containerSizeLoc:m,warpLoc:h,edgeIntensityLoc:f,rimIntensityLoc:p,baseIntensityLoc:g,edgeDistanceLoc:x,rimDistanceLoc:_,baseDistanceLoc:v,cornerBoostLoc:b,rippleEffectLoc:y,tintOpacityLoc:R,glassOpacityLoc:T,imageLoc:C,positionBuffer:i,texcoordBuffer:n};t.viewport(0,0,this.canvas.width,this.canvas.height);t.clearColor(0,0,0,0);t.bindBuffer(t.ARRAY_BUFFER,i);t.enableVertexAttribArray(o);t.vertexAttribPointer(o,2,t.FLOAT,!1,0,0);t.bindBuffer(t.ARRAY_BUFFER,n);t.enableVertexAttribArray(s);t.vertexAttribPointer(s,2,t.FLOAT,!1,0,0);const S=this.width||Math.max(1,Math.round(this.canvas.width/this.dpr)),L=this.height||Math.max(1,Math.round(this.canvas.height/this.dpr));t.uniform2f(r,S,L);t.uniform2f(a,A.width,A.height);t.uniform1f(l,window.glassControls?.blurRadius||2);t.uniform1f(c,this.borderRadius);t.uniform1f(h,this.warp?1:0);t.uniform1f(f,window.glassControls?.edgeIntensity||.01);t.uniform1f(p,window.glassControls?.rimIntensity||.05);t.uniform1f(g,window.glassControls?.baseIntensity||.01);t.uniform1f(x,window.glassControls?.edgeDistance||.15);t.uniform1f(_,window.glassControls?.rimDistance||.8);t.uniform1f(v,window.glassControls?.baseDistance||.1);t.uniform1f(b,window.glassControls?.cornerBoost||.02);t.uniform1f(y,window.glassControls?.rippleEffect||.1);t.uniform1f(R,this.tintOpacity);t.uniform1f(T,window.glassControls?.glassOpacity??this.glassOpacity);const D=this.getPosition(),z=this.parent.getPosition();t.uniform2f(u,D.x,D.y);t.uniform2f(d,z.x,z.y);t.uniform2f(m,this.parent.width,this.parent.height);t.activeTexture(t.TEXTURE0);t.bindTexture(t.TEXTURE_2D,E);t.uniform1i(C,0);this.startNestedRenderLoop()}startNestedRenderLoop(){if(null!==this.nestedAnimationFrameId){cancelAnimationFrame(this.nestedAnimationFrameId);this.nestedAnimationFrameId=null}const t=()=>{if(this.destroyed||!this.gl_refs.gl||!this.parent)return;if(!this.element||!this.element.isConnected){this.stopNestedAnimation();return}if(!this.parent.element||!this.parent.element.isConnected){this.stopNestedAnimation();return}const t=this.gl_refs.gl,e=this.parent.canvas;t.bindTexture(t.TEXTURE_2D,this.gl_refs.texture);t.texSubImage2D(t.TEXTURE_2D,0,0,0,t.RGBA,t.UNSIGNED_BYTE,e);t.clear(t.COLOR_BUFFER_BIT);const i=this.getPosition(),n=this.parent.getPosition();t.uniform2f(this.gl_refs.buttonPositionLoc,i.x,i.y);t.uniform2f(this.gl_refs.containerPositionLoc,n.x,n.y);t.drawArrays(t.TRIANGLES,0,6)},e=()=>{if("undefined"!=typeof Container){const t=!1!==Container.settings?.enableButtonAnimation,e=Container.shouldUseWebGL?Container.shouldUseWebGL():!0;if(!t||!e){this.nestedAnimationFrameId=null;return}}t();this.nestedAnimationFrameId=requestAnimationFrame(e)};e();this.render=t}stopNestedAnimation(){if(null!==this.nestedAnimationFrameId){cancelAnimationFrame(this.nestedAnimationFrameId);this.nestedAnimationFrameId=null}}destroy(){this.stopNestedAnimation();super.destroy()}}if("undefined"!=typeof window)window.Button=t;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";class e{static instances=[];static pageSnapshot=null;static isCapturing=!1;static waitingForSnapshot=[];static mutationObserver=null;static globalListenersSetup=!1;static domReadyListenerAttached=!1;static refreshTimer=null;static pendingForceRefresh=!1;static settingsKey="pwtk_glass_settings_v1";static defaultSettings={enableWebGL:!1,autoRefresh:!0,enableButtonAnimation:!0};static settings={...e.defaultSettings};static maxSnapshotPixels=3e6;static minSnapshotScale=.5;static pageSnapshotScale=1;static documentWriteWarnDepth=0;static originalConsoleWarn=null;static handleWindowRefresh=()=>e.scheduleSnapshotRefresh();static handleVisibilityRefresh=()=>{if("undefined"!=typeof document&&!document.hidden)e.scheduleSnapshotRefresh()};static getDevicePixelRatio(){if("undefined"==typeof window)return 1;const e=window.devicePixelRatio||1;return Math.max(1,Math.min(e,2))}static computeSnapshotScale(t,i){const n=e.getDevicePixelRatio(),o=t*i;if(!Number.isFinite(o)||o<=0)return n;const s=Math.sqrt(e.maxSnapshotPixels/o),r=Math.min(n,s);return Math.max(e.minSnapshotScale,Math.min(r,n))}static loadSettings(){if("undefined"==typeof window||void 0===window.localStorage)return{...e.defaultSettings};try{const t=window.localStorage.getItem(e.settingsKey);if(!t)return{...e.defaultSettings};const i=JSON.parse(t);return{...e.defaultSettings,...i}}catch{return{...e.defaultSettings}}}static saveSettings(){if("undefined"!=typeof window&&void 0!==window.localStorage)try{window.localStorage.setItem(e.settingsKey,JSON.stringify(e.settings))}catch{}}static updateSetting(t,i){e.settings[t]=i;e.saveSettings();e.applySettings(t)}static applySettings(t){const i=e.shouldUseWebGL();e.instances.forEach(t=>{if(i){t.restoreFromFallback();if(t.webglInitialized)t.startRenderLoop();if(!1!==e.settings.enableButtonAnimation)t.children.forEach(e=>{if("function"==typeof e.startNestedRenderLoop)e.startNestedRenderLoop()})}else t.applyFallbackStyles()});if("undefined"!=typeof window)window.glassControls={...window.glassControls||{},nestedAnimationEnabled:!1!==e.settings.enableButtonAnimation};if("enableWebGL"===t)if(i)if(!e.pageSnapshot&&e.instances.length){e.waitingForSnapshot=e.instances.slice();if(!e.isCapturing){e.isCapturing=!0;e.instances[0].capturePageSnapshot(!0)}}else e.instances.forEach(t=>{if(!t.webglInitialized&&e.pageSnapshot)t.initWebGL()});else{e.isCapturing=!1;e.waitingForSnapshot=[]}if("autoRefresh"===t&&!1===e.settings.autoRefresh)if(e.refreshTimer){window.clearTimeout(e.refreshTimer);e.refreshTimer=null}if("enableButtonAnimation"===t){const t=!1!==e.settings.enableButtonAnimation;e.instances.forEach(e=>{e.children.forEach(e=>{if("function"==typeof e.startNestedRenderLoop)if(t)e.startNestedRenderLoop();else if("function"==typeof e.stopNestedAnimation)e.stopNestedAnimation()})})}}static suppressDocumentWriteWarnings(){if("undefined"!=typeof console&&"function"==typeof console.warn){if(0===e.documentWriteWarnDepth){e.originalConsoleWarn=console.warn;console.warn=(...t)=>{if(!(t.length>0&&"string"==typeof t[0]&&t[0].includes("Avoid using document.write")))if(e.originalConsoleWarn)e.originalConsoleWarn.apply(console,t)}}e.documentWriteWarnDepth++}}static restoreDocumentWriteWarnings(){if(e.documentWriteWarnDepth>0){e.documentWriteWarnDepth--;if(0===e.documentWriteWarnDepth&&e.originalConsoleWarn){console.warn=e.originalConsoleWarn;e.originalConsoleWarn=null}}}static shouldUseWebGL(){return!1!==e.settings.enableWebGL}static isAutoRefreshEnabled(){return!1!==e.settings.autoRefresh&&e.shouldUseWebGL()}constructor(t={}){this.width=0;this.height=0;this.dpr=e.getDevicePixelRatio();this.borderRadius=t.borderRadius||48;this.type=t.type||"rounded";this.tintOpacity=void 0!==t.tintOpacity?t.tintOpacity:.06;this.glassOpacity=void 0!==t.glassOpacity?t.glassOpacity:.08;this.canvas=null;this.element=null;this.gl=null;this.gl_refs={};this.webglInitialized=!1;this.children=[];this.animationFrameId=null;this.destroyed=!1;e.instances.push(this);e.ensureGlobalObservers();this.init()}addChild(e){this.children.push(e);e.parent=this;if(e.element&&this.element)this.element.appendChild(e.element);if(e instanceof Button)e.setupAsNestedGlass();this.updateSizeFromDOM();return e}removeChild(e){const t=this.children.indexOf(e);if(t>-1){this.children.splice(t,1);e.parent=null;if(e.element&&this.element.contains(e.element))this.element.removeChild(e.element);this.updateSizeFromDOM()}}updateSizeFromDOM(){requestAnimationFrame(()=>{if(!this.element||!this.canvas)return;const t=this.element.getBoundingClientRect();let i=Math.ceil(t.width),n=Math.ceil(t.height);if("circle"===this.type){const e=Math.max(i,n);i=e;n=e;this.borderRadius=e/2;this.element.style.width=e+"px";this.element.style.height=e+"px";this.element.style.borderRadius=this.borderRadius+"px"}else if("pill"===this.type){this.borderRadius=n/2;this.element.style.borderRadius=this.borderRadius+"px"}const o=e.getDevicePixelRatio(),s=Math.max(1,Math.round(i*o)),r=Math.max(1,Math.round(n*o));if(i!==this.width||n!==this.height||o!==this.dpr){this.width=i;this.height=n;this.dpr=o;this.canvas.width=s;this.canvas.height=r;this.canvas.style.width=i+"px";this.canvas.style.height=n+"px";this.canvas.style.borderRadius=this.borderRadius+"px";if(this.gl_refs.gl){this.gl_refs.gl.viewport(0,0,s,r);this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc,i,n);this.gl_refs.gl.uniform1f(this.gl_refs.borderRadiusLoc,this.borderRadius)}this.children.forEach(e=>{if(e instanceof Button&&e.isNestedGlass&&e.gl_refs.gl){const t=e.gl_refs.gl;t.bindTexture(t.TEXTURE_2D,e.gl_refs.texture);t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.canvas.width,this.canvas.height,0,t.RGBA,t.UNSIGNED_BYTE,null);t.uniform2f(e.gl_refs.textureSizeLoc,this.canvas.width,this.canvas.height);if(e.gl_refs.containerSizeLoc)t.uniform2f(e.gl_refs.containerSizeLoc,i,n)}})}})}init(){this.createElement();this.setupCanvas();this.updateSizeFromDOM();if(e.shouldUseWebGL())if(e.pageSnapshot)this.initWebGL();else if(e.isCapturing)e.waitingForSnapshot.push(this);else{e.isCapturing=!0;e.waitingForSnapshot.push(this);this.capturePageSnapshot()}else this.applyFallbackStyles()}createElement(){this.element=document.createElement("div");this.element.className="glass-container";if("circle"===this.type)this.element.classList.add("glass-container-circle");else if("pill"===this.type)this.element.classList.add("glass-container-pill");this.element.style.borderRadius=this.borderRadius+"px";this.canvas=document.createElement("canvas");this.canvas.style.borderRadius=this.borderRadius+"px";this.canvas.style.position="absolute";this.canvas.style.top="0";this.canvas.style.left="0";this.canvas.style.width="100%";this.canvas.style.height="100%";this.canvas.style.boxShadow="0 25px 50px rgba(0, 0, 0, 0.25)";this.canvas.style.zIndex="-1";this.element.appendChild(this.canvas)}setupCanvas(){this.gl=this.canvas.getContext("webgl",{preserveDrawingBuffer:!0});if(this.gl);else{console.error("WebGL not supported");this.applyFallbackStyles()}}applyFallbackStyles(){if(this.element){if(this.canvas)this.canvas.style.display="none";if(null!==this.animationFrameId){window.cancelAnimationFrame(this.animationFrameId);this.animationFrameId=null}this.element.dataset.pwtkFallback="true";this.element.style.background="linear-gradient(135deg, rgba(255,255,255,0.35), rgba(200,210,230,0.18))";this.element.style.backdropFilter="blur(14px) saturate(140%)";this.element.style.webkitBackdropFilter="blur(14px) saturate(140%)";this.element.style.border="1px solid rgba(255,255,255,0.28)";this.element.style.boxShadow="0 15px 35px rgba(15, 23, 42, 0.33)";this.children.forEach(e=>{if("function"==typeof e.stopNestedAnimation)e.stopNestedAnimation()})}}restoreFromFallback(){if(this.element)if("true"===this.element.dataset.pwtkFallback){delete this.element.dataset.pwtkFallback;if(this.canvas)this.canvas.style.display="";this.element.style.removeProperty("background");this.element.style.removeProperty("backdrop-filter");this.element.style.removeProperty("-webkit-backdrop-filter");this.element.style.removeProperty("border");this.element.style.removeProperty("box-shadow")}}getPosition(){const e=this.canvas.getBoundingClientRect();return{x:e.left+e.width/2,y:e.top+e.height/2}}capturePageSnapshot(){if(!e.shouldUseWebGL()){e.isCapturing=!1;return}const t=window.html2canvas;if(!t){console.error("[PWTK] html2canvas not available, containers will not initialize");e.isCapturing=!1;const t=e.waitingForSnapshot.slice();e.waitingForSnapshot=[];t.forEach(e=>{if(e&&"function"==typeof e.applyFallbackStyles)e.applyFallbackStyles()});return}const i=e.drainWaitingQueue(),n=document.documentElement,o=document.body,s=Math.max(n?.scrollWidth||0,o?.scrollWidth||0,window.innerWidth||0),r=Math.max(n?.scrollHeight||0,o?.scrollHeight||0,window.innerHeight||0),a=e.computeSnapshotScale(s,r);e.pageSnapshotScale=a;e.suppressDocumentWriteWarnings();t(document.body,{scale:a,useCORS:!0,allowTaint:!1,backgroundColor:null,windowWidth:s,windowHeight:r,logging:!1,ignoreElements:function(e){return e.classList.contains("glass-container")||e.classList.contains("glass-button")||e.classList.contains("glass-button-text")}}).then(t=>{e.pageSnapshot=t;e.isCapturing=!1;e.toUniqueList(i).forEach(e=>{e.initWebGL()})}).catch(t=>{console.error("[PWTK] html2canvas error:",t);e.isCapturing=!1;const n=e.toUniqueList([...i,...e.waitingForSnapshot]);e.waitingForSnapshot=[];n.forEach(e=>{if(e&&"function"==typeof e.applyFallbackStyles)e.applyFallbackStyles()})}).finally(()=>{e.isCapturing=!1;if(e.pendingForceRefresh||e.waitingForSnapshot.length>0){const t=e.pendingForceRefresh;e.pendingForceRefresh=!1;e.refreshAll(t)}e.restoreDocumentWriteWarnings()})}initWebGL(){if(e.pageSnapshot&&this.gl)this.applySnapshot(e.pageSnapshot)}applySnapshot(e){if(!e||!this.gl)return;if(!this.webglInitialized){this.setupShader(e);this.webglInitialized=!0;this.children.forEach(e=>{if(e instanceof Button&&e.isNestedGlass&&!e.webglInitialized)e.initNestedGlass()});return}const t=this.gl_refs.gl;if(t&&this.gl_refs.texture){t.bindTexture(t.TEXTURE_2D,this.gl_refs.texture);t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,e);if(this.gl_refs.textureSizeLoc)t.uniform2f(this.gl_refs.textureSizeLoc,e.width,e.height)}}static drainWaitingQueue(){const t=e.waitingForSnapshot.slice();e.waitingForSnapshot=[];return t}static toUniqueList(e){return Array.from(new Set(e.filter(Boolean)))}static invalidateSnapshot(t={}){if(e.isCapturing)e.isCapturing=!1;e.pageSnapshot=null;if(!1!==t.clearQueue)e.waitingForSnapshot=[]}static refreshAll(t=!1){if("undefined"==typeof window)return;if(!e.instances.length)return;if(!e.shouldUseWebGL())return;e.instances.forEach(t=>{if(!("undefined"!=typeof Button&&t instanceof Button&&t.isNestedGlass))if(!e.waitingForSnapshot.includes(t))e.waitingForSnapshot.push(t)});if(e.isCapturing){e.pendingForceRefresh=e.pendingForceRefresh||t||!1;return}const i=e.instances[0];if(i){e.isCapturing=!0;i.capturePageSnapshot(t)}}static scheduleSnapshotRefresh(){if("undefined"!=typeof window)if(e.instances.length)if(e.isAutoRefreshEnabled())if(!e.isCapturing){if(e.refreshTimer)window.clearTimeout(e.refreshTimer);e.refreshTimer=window.setTimeout(()=>{e.refreshTimer=null;e.refreshAll(!0)},200)}else e.pendingForceRefresh=!0}static ensureGlobalObservers(){if("undefined"!=typeof window&&"undefined"!=typeof document&&!e.globalListenersSetup)if(document.body){e.globalListenersSetup=!0;e.settings=e.loadSettings();e.setupMutationObserver();window.addEventListener("resize",e.handleWindowRefresh);window.addEventListener("orientationchange",e.handleWindowRefresh);window.addEventListener("focus",e.handleWindowRefresh);window.addEventListener("pageshow",e.handleWindowRefresh);document.addEventListener("visibilitychange",e.handleVisibilityRefresh);e.applySettings()}else if(!e.domReadyListenerAttached){e.domReadyListenerAttached=!0;document.addEventListener("DOMContentLoaded",()=>{e.domReadyListenerAttached=!1;e.ensureGlobalObservers()},{once:!0})}}static setupMutationObserver(){if(e.mutationObserver||"undefined"==typeof MutationObserver||"undefined"==typeof document)return;const t=new MutationObserver(t=>{if(t.some(t=>{if(e.isHtml2CanvasMutation(t))return!1;const i=t.target;return!e.isMutationInsideGlass(i)}))e.scheduleSnapshotRefresh()});t.observe(document.body,{childList:!0,attributes:!0,characterData:!0,subtree:!0});e.mutationObserver=t}static isMutationInsideGlass(t){if(!t||"object"!=typeof t||"number"!=typeof t.nodeType)return!1;else return e.instances.some(e=>e.element&&e.element.contains(t))}static isHtml2CanvasMutation(e){const t=e=>{if(!e||"object"!=typeof e)return!1;if("number"!=typeof e.nodeType||1!==e.nodeType)return!1;const t=e,i=t.classList;if(i&&(i.contains("html2canvas-container")||i.contains("html2canvas-clone")))return!0;if("string"==typeof t.id&&t.id.startsWith("html2canvas"))return!0;if(("string"==typeof t.className?t.className:"").includes("html2canvas"))return!0;if("function"==typeof t.getAttribute&&"true"===t.getAttribute("data-html2canvas-ignore"))return!0;else return!1};if(t(e.target))return!0;if(e.addedNodes)for(const i of e.addedNodes)if(t(i))return!0;if(e.removedNodes)for(const i of e.removedNodes)if(t(i))return!0;return!1}setupShader(e){const t=this.gl,i=this.createProgram(t,"\n attribute vec2 a_position;\n attribute vec2 a_texcoord;\n varying vec2 v_texcoord;\n\n void main() {\n gl_Position = vec4(a_position, 0, 1);\n v_texcoord = a_texcoord;\n }\n ","\n precision mediump float;\n uniform sampler2D u_image;\n uniform vec2 u_resolution;\n uniform vec2 u_textureSize;\n uniform float u_scrollY;\n uniform float u_pageWidth;\n uniform float u_pageHeight;\n uniform float u_viewportHeight;\n uniform float u_blurRadius;\n uniform float u_borderRadius;\n uniform vec2 u_containerPosition;\n uniform float u_warp;\n uniform float u_edgeIntensity;\n uniform float u_rimIntensity;\n uniform float u_baseIntensity;\n uniform float u_edgeDistance;\n uniform float u_rimDistance;\n uniform float u_baseDistance;\n uniform float u_cornerBoost;\n uniform float u_rippleEffect;\n uniform float u_tintOpacity;\n uniform float u_glassOpacity;\n varying vec2 v_texcoord;\n\n // Function to calculate distance from rounded rectangle edge\n float roundedRectDistance(vec2 coord, vec2 size, float radius) {\n vec2 center = size * 0.5;\n vec2 pixelCoord = coord * size;\n vec2 toCorner = abs(pixelCoord - center) - (center - radius);\n float outsideCorner = length(max(toCorner, 0.0));\n float insideCorner = min(max(toCorner.x, toCorner.y), 0.0);\n return (outsideCorner + insideCorner - radius);\n }\n \n // Function to calculate distance from circle edge (negative inside, positive outside)\n float circleDistance(vec2 coord, vec2 size, float radius) {\n vec2 center = vec2(0.5, 0.5);\n vec2 pixelCoord = coord * size;\n vec2 centerPixel = center * size;\n float distFromCenter = length(pixelCoord - centerPixel);\n return distFromCenter - radius;\n }\n \n // Check if this is a pill (border radius is approximately 50% of height AND width > height)\n bool isPill(vec2 size, float radius) {\n float heightRatioDiff = abs(radius - size.y * 0.5);\n bool radiusMatchesHeight = heightRatioDiff < 2.0;\n bool isWiderThanTall = size.x > size.y + 4.0; // Must be significantly wider\n return radiusMatchesHeight && isWiderThanTall;\n }\n \n // Check if this is a circle (border radius is approximately 50% of smaller dimension AND roughly square)\n bool isCircle(vec2 size, float radius) {\n float minDim = min(size.x, size.y);\n bool radiusMatchesMinDim = abs(radius - minDim * 0.5) < 1.0;\n bool isRoughlySquare = abs(size.x - size.y) < 4.0; // Width and height are similar\n return radiusMatchesMinDim && isRoughlySquare;\n }\n \n // Function to calculate distance from pill edge (capsule shape)\n float pillDistance(vec2 coord, vec2 size, float radius) {\n vec2 center = size * 0.5;\n vec2 pixelCoord = coord * size;\n \n // Proper capsule: line segment with radius\n // The capsule axis runs horizontally from (radius, center.y) to (size.x - radius, center.y)\n vec2 capsuleStart = vec2(radius, center.y);\n vec2 capsuleEnd = vec2(size.x - radius, center.y);\n \n // Project point onto the capsule axis (line segment)\n vec2 capsuleAxis = capsuleEnd - capsuleStart;\n float capsuleLength = length(capsuleAxis);\n \n if (capsuleLength > 0.0) {\n vec2 toPoint = pixelCoord - capsuleStart;\n float t = clamp(dot(toPoint, capsuleAxis) / dot(capsuleAxis, capsuleAxis), 0.0, 1.0);\n vec2 closestPointOnAxis = capsuleStart + t * capsuleAxis;\n return length(pixelCoord - closestPointOnAxis) - radius;\n } else {\n // Degenerate case: just a circle\n return length(pixelCoord - center) - radius;\n }\n }\n\n void main() {\n vec2 coord = v_texcoord;\n \n // Calculate which area of the page should be visible through the container\n float scrollY = u_scrollY;\n vec2 containerSize = u_resolution;\n vec2 textureSize = u_textureSize;\n \n // Container position in viewport coordinates\n vec2 containerCenter = u_containerPosition + vec2(0.0, scrollY);\n \n // Convert container coordinates to page coordinates\n vec2 containerOffset = (coord - 0.5) * containerSize;\n vec2 pagePixel = containerCenter + containerOffset;\n \n float scaleX = u_textureSize.x / max(u_pageWidth, 1.0);\n float scaleY = u_textureSize.y / max(u_pageHeight, 1.0);\n vec2 scaledPagePixel = vec2(pagePixel.x * scaleX, pagePixel.y * scaleY);\n \n // Convert to texture coordinate (0 to 1)\n vec2 textureCoord = scaledPagePixel / textureSize;\n textureCoord = clamp(textureCoord, vec2(0.0), vec2(1.0));\n \n // Glass refraction effects\n float distFromEdgeShape;\n vec2 shapeNormal; // Normal vector pointing away from shape surface\n \n if (isPill(u_resolution, u_borderRadius)) {\n distFromEdgeShape = -pillDistance(coord, u_resolution, u_borderRadius);\n \n // Calculate normal for pill shape\n vec2 center = vec2(0.5, 0.5);\n vec2 pixelCoord = coord * u_resolution;\n vec2 capsuleStart = vec2(u_borderRadius, center.y * u_resolution.y);\n vec2 capsuleEnd = vec2(u_resolution.x - u_borderRadius, center.y * u_resolution.y);\n vec2 capsuleAxis = capsuleEnd - capsuleStart;\n float capsuleLength = length(capsuleAxis);\n \n if (capsuleLength > 0.0) {\n vec2 toPoint = pixelCoord - capsuleStart;\n float t = clamp(dot(toPoint, capsuleAxis) / dot(capsuleAxis, capsuleAxis), 0.0, 1.0);\n vec2 closestPointOnAxis = capsuleStart + t * capsuleAxis;\n vec2 normalDir = pixelCoord - closestPointOnAxis;\n shapeNormal = length(normalDir) > 0.0 ? normalize(normalDir) : vec2(0.0, 1.0);\n } else {\n shapeNormal = normalize(coord - center);\n }\n } else if (isCircle(u_resolution, u_borderRadius)) {\n distFromEdgeShape = -circleDistance(coord, u_resolution, u_borderRadius);\n vec2 center = vec2(0.5, 0.5);\n shapeNormal = normalize(coord - center);\n } else {\n distFromEdgeShape = -roundedRectDistance(coord, u_resolution, u_borderRadius);\n vec2 center = vec2(0.5, 0.5);\n shapeNormal = normalize(coord - center);\n }\n distFromEdgeShape = max(distFromEdgeShape, 0.0);\n \n float distFromLeft = coord.x;\n float distFromRight = 1.0 - coord.x;\n float distFromTop = coord.y;\n float distFromBottom = 1.0 - coord.y;\n float distFromEdge = distFromEdgeShape / min(u_resolution.x, u_resolution.y);\n \n // Smooth glass refraction using shape-aware normal\n float normalizedDistance = distFromEdge * min(u_resolution.x, u_resolution.y);\n float baseIntensity = 1.0 - exp(-normalizedDistance * u_baseDistance);\n float edgeIntensity = exp(-normalizedDistance * u_edgeDistance);\n float rimIntensity = exp(-normalizedDistance * u_rimDistance);\n \n // Apply center warping only if warp is enabled, keep edge and rim effects always\n float baseComponent = u_warp > 0.5 ? baseIntensity * u_baseIntensity : 0.0;\n float totalIntensity = baseComponent + edgeIntensity * u_edgeIntensity + rimIntensity * u_rimIntensity;\n \n vec2 baseRefraction = shapeNormal * totalIntensity;\n \n float cornerProximityX = min(distFromLeft, distFromRight);\n float cornerProximityY = min(distFromTop, distFromBottom);\n float cornerDistance = max(cornerProximityX, cornerProximityY);\n float cornerNormalized = cornerDistance * min(u_resolution.x, u_resolution.y);\n \n float cornerBoost = exp(-cornerNormalized * 0.3) * u_cornerBoost;\n vec2 cornerRefraction = shapeNormal * cornerBoost;\n \n vec2 perpendicular = vec2(-shapeNormal.y, shapeNormal.x);\n float rippleEffect = sin(distFromEdge * 25.0) * u_rippleEffect * rimIntensity;\n vec2 textureRefraction = perpendicular * rippleEffect;\n \n vec2 totalRefraction = baseRefraction + cornerRefraction + textureRefraction;\n textureCoord += totalRefraction;\n \n // Gaussian blur\n vec4 color = vec4(0.0);\n vec2 texelSize = 1.0 / u_textureSize;\n float sigma = u_blurRadius / 2.0;\n vec2 blurStep = texelSize * sigma;\n \n float totalWeight = 0.0;\n \n for(float i = -6.0; i <= 6.0; i += 1.0) {\n for(float j = -6.0; j <= 6.0; j += 1.0) {\n float distance = length(vec2(i, j));\n if(distance > 6.0) continue;\n \n float weight = exp(-(distance * distance) / (2.0 * sigma * sigma));\n \n vec2 offset = vec2(i, j) * blurStep;\n color += texture2D(u_image, textureCoord + offset) * weight;\n totalWeight += weight;\n }\n }\n \n color /= totalWeight;\n \n // Simple vertical gradient\n float gradientPosition = coord.y;\n vec3 topTint = vec3(1.0, 1.0, 1.0);\n vec3 bottomTint = vec3(0.7, 0.7, 0.7);\n vec3 gradientTint = mix(topTint, bottomTint, gradientPosition);\n vec3 tintedColor = mix(color.rgb, gradientTint, u_tintOpacity);\n color = vec4(tintedColor, color.a);\n \n // Sampled gradient\n vec2 viewportCenter = containerCenter;\n float topY = (viewportCenter.y - containerSize.y * 0.4) / textureSize.y;\n float midY = viewportCenter.y / textureSize.y;\n float bottomY = (viewportCenter.y + containerSize.y * 0.4) / textureSize.y;\n \n vec3 topColor = vec3(0.0);\n vec3 midColor = vec3(0.0);\n vec3 bottomColor = vec3(0.0);\n \n float sampleCount = 0.0;\n for(float x = 0.0; x < 1.0; x += 0.05) {\n for(float yOffset = -5.0; yOffset <= 5.0; yOffset += 1.0) {\n vec2 topSample = vec2(x, topY + yOffset * texelSize.y);\n vec2 midSample = vec2(x, midY + yOffset * texelSize.y);\n vec2 bottomSample = vec2(x, bottomY + yOffset * texelSize.y);\n \n topColor += texture2D(u_image, topSample).rgb;\n midColor += texture2D(u_image, midSample).rgb;\n bottomColor += texture2D(u_image, bottomSample).rgb;\n sampleCount += 1.0;\n }\n }\n \n topColor /= sampleCount;\n midColor /= sampleCount;\n bottomColor /= sampleCount;\n \n vec3 sampledGradient;\n if (gradientPosition < 0.1) {\n sampledGradient = topColor;\n } else if (gradientPosition > 0.9) {\n sampledGradient = bottomColor;\n } else {\n float transitionPos = (gradientPosition - 0.1) / 0.8;\n if (transitionPos < 0.5) {\n float t = transitionPos * 2.0;\n sampledGradient = mix(topColor, midColor, t);\n } else {\n float t = (transitionPos - 0.5) * 2.0;\n sampledGradient = mix(midColor, bottomColor, t);\n }\n }\n \n vec3 finalTinted = mix(color.rgb, sampledGradient, u_tintOpacity * 0.3);\n color = vec4(finalTinted, color.a);\n \n // Shape mask (rounded rectangle, circle, or pill)\n float maskDistance;\n if (isPill(u_resolution, u_borderRadius)) {\n maskDistance = pillDistance(coord, u_resolution, u_borderRadius);\n } else if (isCircle(u_resolution, u_borderRadius)) {\n maskDistance = circleDistance(coord, u_resolution, u_borderRadius);\n } else {\n maskDistance = roundedRectDistance(coord, u_resolution, u_borderRadius);\n }\n float mask = 1.0 - smoothstep(-1.0, 1.0, maskDistance);\n \n float opacity = clamp(u_glassOpacity, 0.02, 0.95);\n gl_FragColor = vec4(color.rgb, mask * opacity);\n }\n ");if(!i)return;t.useProgram(i);const n=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,n);t.bufferData(t.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),t.STATIC_DRAW);const o=t.createBuffer();t.bindBuffer(t.ARRAY_BUFFER,o);t.bufferData(t.ARRAY_BUFFER,new Float32Array([0,1,1,1,0,0,0,0,1,1,1,0]),t.STATIC_DRAW);const s=t.getAttribLocation(i,"a_position"),r=t.getAttribLocation(i,"a_texcoord"),a=t.getUniformLocation(i,"u_resolution"),l=t.getUniformLocation(i,"u_textureSize"),c=t.getUniformLocation(i,"u_scrollY"),d=t.getUniformLocation(i,"u_pageWidth"),u=t.getUniformLocation(i,"u_pageHeight"),f=t.getUniformLocation(i,"u_viewportHeight"),h=t.getUniformLocation(i,"u_blurRadius"),m=t.getUniformLocation(i,"u_borderRadius"),p=t.getUniformLocation(i,"u_containerPosition"),g=t.getUniformLocation(i,"u_warp"),y=t.getUniformLocation(i,"u_edgeIntensity"),v=t.getUniformLocation(i,"u_rimIntensity"),b=t.getUniformLocation(i,"u_baseIntensity"),w=t.getUniformLocation(i,"u_edgeDistance"),x=t.getUniformLocation(i,"u_rimDistance"),_=t.getUniformLocation(i,"u_baseDistance"),S=t.getUniformLocation(i,"u_cornerBoost"),R=t.getUniformLocation(i,"u_rippleEffect"),L=t.getUniformLocation(i,"u_tintOpacity"),C=t.getUniformLocation(i,"u_glassOpacity"),E=t.getUniformLocation(i,"u_image"),F=t.createTexture();t.bindTexture(t.TEXTURE_2D,F);t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,e);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.LINEAR);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.LINEAR);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE);t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE);this.gl_refs={gl:t,program:i,texture:F,textureSizeLoc:l,scrollYLoc:c,pageWidthLoc:d,positionLoc:s,texcoordLoc:r,resolutionLoc:a,pageHeightLoc:u,viewportHeightLoc:f,blurRadiusLoc:h,borderRadiusLoc:m,containerPositionLoc:p,warpLoc:g,edgeIntensityLoc:y,rimIntensityLoc:v,baseIntensityLoc:b,edgeDistanceLoc:w,rimDistanceLoc:x,baseDistanceLoc:_,cornerBoostLoc:S,rippleEffectLoc:R,tintOpacityLoc:L,glassOpacityLoc:C,imageLoc:E,positionBuffer:n,texcoordBuffer:o};t.viewport(0,0,this.canvas.width,this.canvas.height);t.clearColor(0,0,0,0);t.bindBuffer(t.ARRAY_BUFFER,n);t.enableVertexAttribArray(s);t.vertexAttribPointer(s,2,t.FLOAT,!1,0,0);t.bindBuffer(t.ARRAY_BUFFER,o);t.enableVertexAttribArray(r);t.vertexAttribPointer(r,2,t.FLOAT,!1,0,0);const A=this.width||Math.max(1,Math.round(this.canvas.width/this.dpr)),D=this.height||Math.max(1,Math.round(this.canvas.height/this.dpr));t.uniform2f(a,A,D);t.uniform2f(l,e.width,e.height);t.uniform1f(h,window.glassControls?.blurRadius||5);t.uniform1f(m,this.borderRadius);t.uniform1f(g,this.warp?1:0);t.uniform1f(y,window.glassControls?.edgeIntensity||.01);t.uniform1f(v,window.glassControls?.rimIntensity||.05);t.uniform1f(b,window.glassControls?.baseIntensity||.01);t.uniform1f(w,window.glassControls?.edgeDistance||.15);t.uniform1f(x,window.glassControls?.rimDistance||.8);t.uniform1f(_,window.glassControls?.baseDistance||.1);t.uniform1f(S,window.glassControls?.cornerBoost||.02);t.uniform1f(R,window.glassControls?.rippleEffect||.1);t.uniform1f(L,this.tintOpacity);t.uniform1f(C,window.glassControls?.glassOpacity??this.glassOpacity);const T=this.getPosition();t.uniform2f(p,T.x,T.y);const P=Math.max(document.body.scrollWidth,document.documentElement.scrollWidth),W=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight),z=window.innerHeight;t.uniform1f(d,P);t.uniform1f(u,W);t.uniform1f(f,z);t.activeTexture(t.TEXTURE0);t.bindTexture(t.TEXTURE_2D,F);t.uniform1i(E,0);this.startRenderLoop()}startRenderLoop(){if(null!==this.animationFrameId){window.cancelAnimationFrame(this.animationFrameId);this.animationFrameId=null}const e=()=>{if(this.destroyed||!this.gl_refs.gl){this.animationFrameId=null;return}if(!this.element||!this.element.isConnected){this.destroy();return}const t=this.gl_refs.gl;t.clear(t.COLOR_BUFFER_BIT);const i=window.pageYOffset||document.documentElement.scrollTop;t.uniform1f(this.gl_refs.scrollYLoc,i);const n=this.getPosition();t.uniform2f(this.gl_refs.containerPositionLoc,n.x,n.y);const o=Math.max(document.body.scrollWidth,document.documentElement.scrollWidth),s=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight);t.uniform1f(this.gl_refs.pageWidthLoc,o);t.uniform1f(this.gl_refs.pageHeightLoc,s);t.uniform1f(this.gl_refs.viewportHeightLoc,window.innerHeight);t.drawArrays(t.TRIANGLES,0,6);this.animationFrameId=window.requestAnimationFrame(e)};e();this.render=e}createProgram(e,t,i){const n=this.compileShader(e,e.VERTEX_SHADER,t),o=this.compileShader(e,e.FRAGMENT_SHADER,i);if(!n||!o)return null;const s=e.createProgram();e.attachShader(s,n);e.attachShader(s,o);e.linkProgram(s);if(!e.getProgramParameter(s,e.LINK_STATUS)){console.error("Program link error:",e.getProgramInfoLog(s));return null}return s}compileShader(e,t,i){const n=e.createShader(t);e.shaderSource(n,i);e.compileShader(n);if(!e.getShaderParameter(n,e.COMPILE_STATUS)){console.error("Shader compile error:",e.getShaderInfoLog(n));return null}return n}destroy(){if(!this.destroyed){this.destroyed=!0;if(null!==this.animationFrameId){window.cancelAnimationFrame(this.animationFrameId);this.animationFrameId=null}this.children.forEach(e=>{if("function"==typeof e.stopNestedAnimation)e.stopNestedAnimation()});if(this.gl_refs.gl){const e=this.gl_refs.gl;if(this.gl_refs.texture)e.deleteTexture(this.gl_refs.texture);if(this.gl_refs.positionBuffer)e.deleteBuffer(this.gl_refs.positionBuffer);if(this.gl_refs.texcoordBuffer)e.deleteBuffer(this.gl_refs.texcoordBuffer);if(this.gl_refs.program)e.deleteProgram(this.gl_refs.program)}this.gl_refs={};this.webglInitialized=!1;e.waitingForSnapshot=e.waitingForSnapshot.filter(e=>e!==this);e.instances=e.instances.filter(e=>e!==this);if(!e.instances.length){e.teardownGlobalObservers();e.pageSnapshot=null;e.pageSnapshotScale=1;e.isCapturing=!1;e.pendingForceRefresh=!1}}}static teardownGlobalObservers(){if(e.globalListenersSetup)if("undefined"!=typeof window&&"undefined"!=typeof document){if(e.refreshTimer){window.clearTimeout(e.refreshTimer);e.refreshTimer=null}if(e.mutationObserver){e.mutationObserver.disconnect();e.mutationObserver=null}window.removeEventListener("resize",e.handleWindowRefresh);window.removeEventListener("orientationchange",e.handleWindowRefresh);window.removeEventListener("focus",e.handleWindowRefresh);window.removeEventListener("pageshow",e.handleWindowRefresh);document.removeEventListener("visibilitychange",e.handleVisibilityRefresh);e.globalListenersSetup=!1}else e.globalListenersSetup=!1}}if("undefined"!=typeof window){e.settings=e.loadSettings();window.Container=e}
|
|
@@ -10,11 +10,14 @@ class Container {
|
|
|
10
10
|
static pendingForceRefresh = false;
|
|
11
11
|
static settingsKey = "pwtk_glass_settings_v1";
|
|
12
12
|
static defaultSettings = {
|
|
13
|
-
enableWebGL:
|
|
14
|
-
autoRefresh:
|
|
13
|
+
enableWebGL: false,
|
|
14
|
+
autoRefresh: true,
|
|
15
15
|
enableButtonAnimation: true
|
|
16
16
|
};
|
|
17
17
|
static settings = { ...Container.defaultSettings };
|
|
18
|
+
static maxSnapshotPixels = 3e6;
|
|
19
|
+
static minSnapshotScale = 0.5;
|
|
20
|
+
static pageSnapshotScale = 1;
|
|
18
21
|
static documentWriteWarnDepth = 0;
|
|
19
22
|
static originalConsoleWarn = null;
|
|
20
23
|
static handleWindowRefresh = () => Container.scheduleSnapshotRefresh();
|
|
@@ -23,6 +26,21 @@ class Container {
|
|
|
23
26
|
Container.scheduleSnapshotRefresh();
|
|
24
27
|
}
|
|
25
28
|
};
|
|
29
|
+
static getDevicePixelRatio() {
|
|
30
|
+
if (typeof window === "undefined") return 1;
|
|
31
|
+
const dpr = window.devicePixelRatio || 1;
|
|
32
|
+
return Math.max(1, Math.min(dpr, 2));
|
|
33
|
+
}
|
|
34
|
+
static computeSnapshotScale(pageWidth, pageHeight) {
|
|
35
|
+
const baseScale = Container.getDevicePixelRatio();
|
|
36
|
+
const area = pageWidth * pageHeight;
|
|
37
|
+
if (!Number.isFinite(area) || area <= 0) {
|
|
38
|
+
return baseScale;
|
|
39
|
+
}
|
|
40
|
+
const maxScale = Math.sqrt(Container.maxSnapshotPixels / area);
|
|
41
|
+
const scale = Math.min(baseScale, maxScale);
|
|
42
|
+
return Math.max(Container.minSnapshotScale, Math.min(scale, baseScale));
|
|
43
|
+
}
|
|
26
44
|
static loadSettings() {
|
|
27
45
|
if (typeof window === "undefined" || typeof window.localStorage === "undefined") {
|
|
28
46
|
return { ...Container.defaultSettings };
|
|
@@ -153,9 +171,11 @@ class Container {
|
|
|
153
171
|
constructor(options = {}) {
|
|
154
172
|
this.width = 0;
|
|
155
173
|
this.height = 0;
|
|
174
|
+
this.dpr = Container.getDevicePixelRatio();
|
|
156
175
|
this.borderRadius = options.borderRadius || 48;
|
|
157
176
|
this.type = options.type || "rounded";
|
|
158
|
-
this.tintOpacity = options.tintOpacity !== void 0 ? options.tintOpacity : 0.
|
|
177
|
+
this.tintOpacity = options.tintOpacity !== void 0 ? options.tintOpacity : 0.06;
|
|
178
|
+
this.glassOpacity = options.glassOpacity !== void 0 ? options.glassOpacity : 0.08;
|
|
159
179
|
this.canvas = null;
|
|
160
180
|
this.element = null;
|
|
161
181
|
this.gl = null;
|
|
@@ -163,6 +183,7 @@ class Container {
|
|
|
163
183
|
this.webglInitialized = false;
|
|
164
184
|
this.children = [];
|
|
165
185
|
this.animationFrameId = null;
|
|
186
|
+
this.destroyed = false;
|
|
166
187
|
Container.instances.push(this);
|
|
167
188
|
Container.ensureGlobalObservers();
|
|
168
189
|
this.init();
|
|
@@ -192,6 +213,7 @@ class Container {
|
|
|
192
213
|
}
|
|
193
214
|
updateSizeFromDOM() {
|
|
194
215
|
requestAnimationFrame(() => {
|
|
216
|
+
if (!this.element || !this.canvas) return;
|
|
195
217
|
const rect = this.element.getBoundingClientRect();
|
|
196
218
|
let newWidth = Math.ceil(rect.width);
|
|
197
219
|
let newHeight = Math.ceil(rect.height);
|
|
@@ -207,16 +229,20 @@ class Container {
|
|
|
207
229
|
this.borderRadius = newHeight / 2;
|
|
208
230
|
this.element.style.borderRadius = this.borderRadius + "px";
|
|
209
231
|
}
|
|
210
|
-
|
|
232
|
+
const nextDpr = Container.getDevicePixelRatio();
|
|
233
|
+
const pixelWidth = Math.max(1, Math.round(newWidth * nextDpr));
|
|
234
|
+
const pixelHeight = Math.max(1, Math.round(newHeight * nextDpr));
|
|
235
|
+
if (newWidth !== this.width || newHeight !== this.height || nextDpr !== this.dpr) {
|
|
211
236
|
this.width = newWidth;
|
|
212
237
|
this.height = newHeight;
|
|
213
|
-
this.
|
|
214
|
-
this.canvas.
|
|
238
|
+
this.dpr = nextDpr;
|
|
239
|
+
this.canvas.width = pixelWidth;
|
|
240
|
+
this.canvas.height = pixelHeight;
|
|
215
241
|
this.canvas.style.width = newWidth + "px";
|
|
216
242
|
this.canvas.style.height = newHeight + "px";
|
|
217
243
|
this.canvas.style.borderRadius = this.borderRadius + "px";
|
|
218
244
|
if (this.gl_refs.gl) {
|
|
219
|
-
this.gl_refs.gl.viewport(0, 0,
|
|
245
|
+
this.gl_refs.gl.viewport(0, 0, pixelWidth, pixelHeight);
|
|
220
246
|
this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc, newWidth, newHeight);
|
|
221
247
|
this.gl_refs.gl.uniform1f(this.gl_refs.borderRadiusLoc, this.borderRadius);
|
|
222
248
|
}
|
|
@@ -224,8 +250,8 @@ class Container {
|
|
|
224
250
|
if (child instanceof Button && child.isNestedGlass && child.gl_refs.gl) {
|
|
225
251
|
const gl = child.gl_refs.gl;
|
|
226
252
|
gl.bindTexture(gl.TEXTURE_2D, child.gl_refs.texture);
|
|
227
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA,
|
|
228
|
-
gl.uniform2f(child.gl_refs.textureSizeLoc,
|
|
253
|
+
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.canvas.width, this.canvas.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
|
|
254
|
+
gl.uniform2f(child.gl_refs.textureSizeLoc, this.canvas.width, this.canvas.height);
|
|
229
255
|
if (child.gl_refs.containerSizeLoc) {
|
|
230
256
|
gl.uniform2f(child.gl_refs.containerSizeLoc, newWidth, newHeight);
|
|
231
257
|
}
|
|
@@ -276,6 +302,7 @@ class Container {
|
|
|
276
302
|
this.gl = this.canvas.getContext("webgl", { preserveDrawingBuffer: true });
|
|
277
303
|
if (!this.gl) {
|
|
278
304
|
console.error("WebGL not supported");
|
|
305
|
+
this.applyFallbackStyles();
|
|
279
306
|
return;
|
|
280
307
|
}
|
|
281
308
|
}
|
|
@@ -331,16 +358,30 @@ class Container {
|
|
|
331
358
|
if (!html2canvas) {
|
|
332
359
|
console.error("[PWTK] html2canvas not available, containers will not initialize");
|
|
333
360
|
Container.isCapturing = false;
|
|
361
|
+
const pending = Container.waitingForSnapshot.slice();
|
|
334
362
|
Container.waitingForSnapshot = [];
|
|
363
|
+
pending.forEach((container) => {
|
|
364
|
+
if (container && typeof container.applyFallbackStyles === "function") {
|
|
365
|
+
container.applyFallbackStyles();
|
|
366
|
+
}
|
|
367
|
+
});
|
|
335
368
|
return;
|
|
336
369
|
}
|
|
337
370
|
const targets = Container.drainWaitingQueue();
|
|
371
|
+
const doc = document.documentElement;
|
|
372
|
+
const body = document.body;
|
|
373
|
+
const pageWidth = Math.max(doc?.scrollWidth || 0, body?.scrollWidth || 0, window.innerWidth || 0);
|
|
374
|
+
const pageHeight = Math.max(doc?.scrollHeight || 0, body?.scrollHeight || 0, window.innerHeight || 0);
|
|
375
|
+
const scale = Container.computeSnapshotScale(pageWidth, pageHeight);
|
|
376
|
+
Container.pageSnapshotScale = scale;
|
|
338
377
|
Container.suppressDocumentWriteWarnings();
|
|
339
378
|
html2canvas(document.body, {
|
|
340
|
-
scale
|
|
379
|
+
scale,
|
|
341
380
|
useCORS: true,
|
|
342
|
-
allowTaint:
|
|
381
|
+
allowTaint: false,
|
|
343
382
|
backgroundColor: null,
|
|
383
|
+
windowWidth: pageWidth,
|
|
384
|
+
windowHeight: pageHeight,
|
|
344
385
|
logging: false,
|
|
345
386
|
ignoreElements: function(element) {
|
|
346
387
|
return element.classList.contains("glass-container") || element.classList.contains("glass-button") || element.classList.contains("glass-button-text");
|
|
@@ -355,7 +396,13 @@ class Container {
|
|
|
355
396
|
}).catch((error) => {
|
|
356
397
|
console.error("[PWTK] html2canvas error:", error);
|
|
357
398
|
Container.isCapturing = false;
|
|
399
|
+
const pending = Container.toUniqueList([...targets, ...Container.waitingForSnapshot]);
|
|
358
400
|
Container.waitingForSnapshot = [];
|
|
401
|
+
pending.forEach((container) => {
|
|
402
|
+
if (container && typeof container.applyFallbackStyles === "function") {
|
|
403
|
+
container.applyFallbackStyles();
|
|
404
|
+
}
|
|
405
|
+
});
|
|
359
406
|
}).finally(() => {
|
|
360
407
|
Container.isCapturing = false;
|
|
361
408
|
if (Container.pendingForceRefresh || Container.waitingForSnapshot.length > 0) {
|
|
@@ -375,6 +422,11 @@ class Container {
|
|
|
375
422
|
if (!this.webglInitialized) {
|
|
376
423
|
this.setupShader(snapshotCanvas);
|
|
377
424
|
this.webglInitialized = true;
|
|
425
|
+
this.children.forEach((child) => {
|
|
426
|
+
if (child instanceof Button && child.isNestedGlass && !child.webglInitialized) {
|
|
427
|
+
child.initNestedGlass();
|
|
428
|
+
}
|
|
429
|
+
});
|
|
378
430
|
return;
|
|
379
431
|
}
|
|
380
432
|
const gl = this.gl_refs.gl;
|
|
@@ -564,6 +616,7 @@ class Container {
|
|
|
564
616
|
uniform vec2 u_resolution;
|
|
565
617
|
uniform vec2 u_textureSize;
|
|
566
618
|
uniform float u_scrollY;
|
|
619
|
+
uniform float u_pageWidth;
|
|
567
620
|
uniform float u_pageHeight;
|
|
568
621
|
uniform float u_viewportHeight;
|
|
569
622
|
uniform float u_blurRadius;
|
|
@@ -579,6 +632,7 @@ class Container {
|
|
|
579
632
|
uniform float u_cornerBoost;
|
|
580
633
|
uniform float u_rippleEffect;
|
|
581
634
|
uniform float u_tintOpacity;
|
|
635
|
+
uniform float u_glassOpacity;
|
|
582
636
|
varying vec2 v_texcoord;
|
|
583
637
|
|
|
584
638
|
// Function to calculate distance from rounded rectangle edge
|
|
@@ -656,8 +710,13 @@ class Container {
|
|
|
656
710
|
vec2 containerOffset = (coord - 0.5) * containerSize;
|
|
657
711
|
vec2 pagePixel = containerCenter + containerOffset;
|
|
658
712
|
|
|
713
|
+
float scaleX = u_textureSize.x / max(u_pageWidth, 1.0);
|
|
714
|
+
float scaleY = u_textureSize.y / max(u_pageHeight, 1.0);
|
|
715
|
+
vec2 scaledPagePixel = vec2(pagePixel.x * scaleX, pagePixel.y * scaleY);
|
|
716
|
+
|
|
659
717
|
// Convert to texture coordinate (0 to 1)
|
|
660
|
-
vec2 textureCoord =
|
|
718
|
+
vec2 textureCoord = scaledPagePixel / textureSize;
|
|
719
|
+
textureCoord = clamp(textureCoord, vec2(0.0), vec2(1.0));
|
|
661
720
|
|
|
662
721
|
// Glass refraction effects
|
|
663
722
|
float distFromEdgeShape;
|
|
@@ -816,7 +875,8 @@ class Container {
|
|
|
816
875
|
}
|
|
817
876
|
float mask = 1.0 - smoothstep(-1.0, 1.0, maskDistance);
|
|
818
877
|
|
|
819
|
-
|
|
878
|
+
float opacity = clamp(u_glassOpacity, 0.02, 0.95);
|
|
879
|
+
gl_FragColor = vec4(color.rgb, mask * opacity);
|
|
820
880
|
}
|
|
821
881
|
`;
|
|
822
882
|
const program = this.createProgram(gl, vsSource, fsSource);
|
|
@@ -833,6 +893,7 @@ class Container {
|
|
|
833
893
|
const resolutionLoc = gl.getUniformLocation(program, "u_resolution");
|
|
834
894
|
const textureSizeLoc = gl.getUniformLocation(program, "u_textureSize");
|
|
835
895
|
const scrollYLoc = gl.getUniformLocation(program, "u_scrollY");
|
|
896
|
+
const pageWidthLoc = gl.getUniformLocation(program, "u_pageWidth");
|
|
836
897
|
const pageHeightLoc = gl.getUniformLocation(program, "u_pageHeight");
|
|
837
898
|
const viewportHeightLoc = gl.getUniformLocation(program, "u_viewportHeight");
|
|
838
899
|
const blurRadiusLoc = gl.getUniformLocation(program, "u_blurRadius");
|
|
@@ -848,6 +909,7 @@ class Container {
|
|
|
848
909
|
const cornerBoostLoc = gl.getUniformLocation(program, "u_cornerBoost");
|
|
849
910
|
const rippleEffectLoc = gl.getUniformLocation(program, "u_rippleEffect");
|
|
850
911
|
const tintOpacityLoc = gl.getUniformLocation(program, "u_tintOpacity");
|
|
912
|
+
const glassOpacityLoc = gl.getUniformLocation(program, "u_glassOpacity");
|
|
851
913
|
const imageLoc = gl.getUniformLocation(program, "u_image");
|
|
852
914
|
const texture = gl.createTexture();
|
|
853
915
|
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
@@ -858,9 +920,11 @@ class Container {
|
|
|
858
920
|
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
859
921
|
this.gl_refs = {
|
|
860
922
|
gl,
|
|
923
|
+
program,
|
|
861
924
|
texture,
|
|
862
925
|
textureSizeLoc,
|
|
863
926
|
scrollYLoc,
|
|
927
|
+
pageWidthLoc,
|
|
864
928
|
positionLoc,
|
|
865
929
|
texcoordLoc,
|
|
866
930
|
resolutionLoc,
|
|
@@ -879,6 +943,7 @@ class Container {
|
|
|
879
943
|
cornerBoostLoc,
|
|
880
944
|
rippleEffectLoc,
|
|
881
945
|
tintOpacityLoc,
|
|
946
|
+
glassOpacityLoc,
|
|
882
947
|
imageLoc,
|
|
883
948
|
positionBuffer,
|
|
884
949
|
texcoordBuffer
|
|
@@ -891,7 +956,9 @@ class Container {
|
|
|
891
956
|
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
|
|
892
957
|
gl.enableVertexAttribArray(texcoordLoc);
|
|
893
958
|
gl.vertexAttribPointer(texcoordLoc, 2, gl.FLOAT, false, 0, 0);
|
|
894
|
-
|
|
959
|
+
const cssWidth = this.width || Math.max(1, Math.round(this.canvas.width / this.dpr));
|
|
960
|
+
const cssHeight = this.height || Math.max(1, Math.round(this.canvas.height / this.dpr));
|
|
961
|
+
gl.uniform2f(resolutionLoc, cssWidth, cssHeight);
|
|
895
962
|
gl.uniform2f(textureSizeLoc, image.width, image.height);
|
|
896
963
|
gl.uniform1f(blurRadiusLoc, window.glassControls?.blurRadius || 5);
|
|
897
964
|
gl.uniform1f(borderRadiusLoc, this.borderRadius);
|
|
@@ -905,10 +972,13 @@ class Container {
|
|
|
905
972
|
gl.uniform1f(cornerBoostLoc, window.glassControls?.cornerBoost || 0.02);
|
|
906
973
|
gl.uniform1f(rippleEffectLoc, window.glassControls?.rippleEffect || 0.1);
|
|
907
974
|
gl.uniform1f(tintOpacityLoc, this.tintOpacity);
|
|
975
|
+
gl.uniform1f(glassOpacityLoc, window.glassControls?.glassOpacity ?? this.glassOpacity);
|
|
908
976
|
const position = this.getPosition();
|
|
909
977
|
gl.uniform2f(containerPositionLoc, position.x, position.y);
|
|
978
|
+
const pageWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
|
|
910
979
|
const pageHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
|
|
911
980
|
const viewportHeight = window.innerHeight;
|
|
981
|
+
gl.uniform1f(pageWidthLoc, pageWidth);
|
|
912
982
|
gl.uniform1f(pageHeightLoc, pageHeight);
|
|
913
983
|
gl.uniform1f(viewportHeightLoc, viewportHeight);
|
|
914
984
|
gl.activeTexture(gl.TEXTURE0);
|
|
@@ -922,16 +992,25 @@ class Container {
|
|
|
922
992
|
this.animationFrameId = null;
|
|
923
993
|
}
|
|
924
994
|
const render = () => {
|
|
925
|
-
if (!this.gl_refs.gl) {
|
|
995
|
+
if (this.destroyed || !this.gl_refs.gl) {
|
|
926
996
|
this.animationFrameId = null;
|
|
927
997
|
return;
|
|
928
998
|
}
|
|
999
|
+
if (!this.element || !this.element.isConnected) {
|
|
1000
|
+
this.destroy();
|
|
1001
|
+
return;
|
|
1002
|
+
}
|
|
929
1003
|
const gl = this.gl_refs.gl;
|
|
930
1004
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
|
931
1005
|
const scrollY = window.pageYOffset || document.documentElement.scrollTop;
|
|
932
1006
|
gl.uniform1f(this.gl_refs.scrollYLoc, scrollY);
|
|
933
1007
|
const position = this.getPosition();
|
|
934
1008
|
gl.uniform2f(this.gl_refs.containerPositionLoc, position.x, position.y);
|
|
1009
|
+
const pageWidth = Math.max(document.body.scrollWidth, document.documentElement.scrollWidth);
|
|
1010
|
+
const pageHeight = Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
|
|
1011
|
+
gl.uniform1f(this.gl_refs.pageWidthLoc, pageWidth);
|
|
1012
|
+
gl.uniform1f(this.gl_refs.pageHeightLoc, pageHeight);
|
|
1013
|
+
gl.uniform1f(this.gl_refs.viewportHeightLoc, window.innerHeight);
|
|
935
1014
|
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
|
936
1015
|
this.animationFrameId = window.requestAnimationFrame(render);
|
|
937
1016
|
};
|
|
@@ -962,6 +1041,58 @@ class Container {
|
|
|
962
1041
|
}
|
|
963
1042
|
return shader;
|
|
964
1043
|
}
|
|
1044
|
+
destroy() {
|
|
1045
|
+
if (this.destroyed) return;
|
|
1046
|
+
this.destroyed = true;
|
|
1047
|
+
if (this.animationFrameId !== null) {
|
|
1048
|
+
window.cancelAnimationFrame(this.animationFrameId);
|
|
1049
|
+
this.animationFrameId = null;
|
|
1050
|
+
}
|
|
1051
|
+
this.children.forEach((child) => {
|
|
1052
|
+
if (typeof child.stopNestedAnimation === "function") {
|
|
1053
|
+
child.stopNestedAnimation();
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
if (this.gl_refs.gl) {
|
|
1057
|
+
const gl = this.gl_refs.gl;
|
|
1058
|
+
if (this.gl_refs.texture) gl.deleteTexture(this.gl_refs.texture);
|
|
1059
|
+
if (this.gl_refs.positionBuffer) gl.deleteBuffer(this.gl_refs.positionBuffer);
|
|
1060
|
+
if (this.gl_refs.texcoordBuffer) gl.deleteBuffer(this.gl_refs.texcoordBuffer);
|
|
1061
|
+
if (this.gl_refs.program) gl.deleteProgram(this.gl_refs.program);
|
|
1062
|
+
}
|
|
1063
|
+
this.gl_refs = {};
|
|
1064
|
+
this.webglInitialized = false;
|
|
1065
|
+
Container.waitingForSnapshot = Container.waitingForSnapshot.filter((item) => item !== this);
|
|
1066
|
+
Container.instances = Container.instances.filter((item) => item !== this);
|
|
1067
|
+
if (!Container.instances.length) {
|
|
1068
|
+
Container.teardownGlobalObservers();
|
|
1069
|
+
Container.pageSnapshot = null;
|
|
1070
|
+
Container.pageSnapshotScale = 1;
|
|
1071
|
+
Container.isCapturing = false;
|
|
1072
|
+
Container.pendingForceRefresh = false;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
static teardownGlobalObservers() {
|
|
1076
|
+
if (!Container.globalListenersSetup) return;
|
|
1077
|
+
if (typeof window === "undefined" || typeof document === "undefined") {
|
|
1078
|
+
Container.globalListenersSetup = false;
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
if (Container.refreshTimer) {
|
|
1082
|
+
window.clearTimeout(Container.refreshTimer);
|
|
1083
|
+
Container.refreshTimer = null;
|
|
1084
|
+
}
|
|
1085
|
+
if (Container.mutationObserver) {
|
|
1086
|
+
Container.mutationObserver.disconnect();
|
|
1087
|
+
Container.mutationObserver = null;
|
|
1088
|
+
}
|
|
1089
|
+
window.removeEventListener("resize", Container.handleWindowRefresh);
|
|
1090
|
+
window.removeEventListener("orientationchange", Container.handleWindowRefresh);
|
|
1091
|
+
window.removeEventListener("focus", Container.handleWindowRefresh);
|
|
1092
|
+
window.removeEventListener("pageshow", Container.handleWindowRefresh);
|
|
1093
|
+
document.removeEventListener("visibilitychange", Container.handleVisibilityRefresh);
|
|
1094
|
+
Container.globalListenersSetup = false;
|
|
1095
|
+
}
|
|
965
1096
|
}
|
|
966
1097
|
if (typeof window !== "undefined") {
|
|
967
1098
|
Container.settings = Container.loadSettings();
|