@leeguoo/pwtk-network-debugger 1.3.4 → 1.3.5

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.
@@ -18,6 +18,7 @@ class Button extends Container {
18
18
  this.warp = warp;
19
19
  this.parent = null;
20
20
  this.isNestedGlass = false;
21
+ this.nestedAnimationFrameId = null;
21
22
  this.element.classList.add("glass-button");
22
23
  if (this.type === "circle") {
23
24
  this.element.classList.add("glass-button-circle");
@@ -522,6 +523,10 @@ class Button extends Container {
522
523
  this.startNestedRenderLoop();
523
524
  }
524
525
  startNestedRenderLoop() {
526
+ if (this.nestedAnimationFrameId !== null) {
527
+ cancelAnimationFrame(this.nestedAnimationFrameId);
528
+ this.nestedAnimationFrameId = null;
529
+ }
525
530
  const render = () => {
526
531
  if (!this.gl_refs.gl || !this.parent) return;
527
532
  const gl = this.gl_refs.gl;
@@ -536,12 +541,26 @@ class Button extends Container {
536
541
  gl.drawArrays(gl.TRIANGLES, 0, 6);
537
542
  };
538
543
  const animationLoop = () => {
544
+ if (typeof Container !== "undefined") {
545
+ const enableAnimations = Container.settings?.enableButtonAnimation !== false;
546
+ const enableWebGL = Container.shouldUseWebGL ? Container.shouldUseWebGL() : true;
547
+ if (!enableAnimations || !enableWebGL) {
548
+ this.nestedAnimationFrameId = null;
549
+ return;
550
+ }
551
+ }
539
552
  render();
540
- requestAnimationFrame(animationLoop);
553
+ this.nestedAnimationFrameId = requestAnimationFrame(animationLoop);
541
554
  };
542
555
  animationLoop();
543
556
  this.render = render;
544
557
  }
558
+ stopNestedAnimation() {
559
+ if (this.nestedAnimationFrameId !== null) {
560
+ cancelAnimationFrame(this.nestedAnimationFrameId);
561
+ this.nestedAnimationFrameId = null;
562
+ }
563
+ }
545
564
  }
546
565
  if (typeof window !== "undefined") {
547
566
  window.Button = Button;
@@ -0,0 +1 @@
1
+ "use strict";class e extends Container{constructor(e={}){const t=e.text||"Button",i=parseInt(e.size)||48,n=e.onClick||null,o=e.type||"rounded",s=void 0!==e.warp?e.warp:!1;super({borderRadius:i,type:o,tintOpacity:void 0!==e.tintOpacity?e.tintOpacity:.2});this.text=t;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()}setSizeFromText(){let t,i;if("circle"===this.type){const e=2.5*this.fontSize;t=e;i=e;this.borderRadius=e/2;this.element.style.width=t+"px";this.element.style.height=i+"px";this.element.style.minWidth=t+"px";this.element.style.minHeight=i+"px";this.element.style.maxWidth=t+"px";this.element.style.maxHeight=i+"px"}else if("pill"===this.type){const n=e.measureText(this.text,this.fontSize);t=Math.ceil(n.width+2*this.fontSize);i=Math.ceil(this.fontSize+1.2*this.fontSize);this.borderRadius=i/2;this.element.style.minWidth=t+"px";this.element.style.minHeight=i+"px"}else{const n=e.measureText(this.text,this.fontSize);t=Math.ceil(n.width+2*this.fontSize);i=Math.ceil(this.fontSize+1.5*this.fontSize);this.borderRadius=this.fontSize;this.element.style.minWidth=t+"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=t;this.height=i;if(this.canvas){this.canvas.width=t;this.canvas.height=i;this.canvas.style.width=t+"px";this.canvas.style.height=i+"px";if(this.gl_refs.gl){this.gl_refs.gl.viewport(0,0,t,i);this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc,t,i);this.gl_refs.gl.uniform1f(this.gl_refs.borderRadiusLoc,this.borderRadius)}}}else if("pill"===this.type){this.width=t;this.height=i;this.element.style.width=t+"px";this.element.style.height=i+"px";this.element.style.maxWidth=t+"px";this.element.style.maxHeight=i+"px";if(this.canvas){this.canvas.width=t;this.canvas.height=i;this.canvas.style.width=t+"px";this.canvas.style.height=i+"px";if(this.gl_refs.gl){this.gl_refs.gl.viewport(0,0,t,i);this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc,t,i);this.gl_refs.gl.uniform1f(this.gl_refs.borderRadiusLoc,this.borderRadius)}}}else this.updateSizeFromDOM()}setupAsNestedGlass(){if(this.parent&&!this.isNestedGlass){this.isNestedGlass=!0;if(this.webglInitialized)this.initWebGL()}}static measureText(e,t){const i=document.createElement("canvas").getContext("2d");i.font=`${t}px system-ui, -apple-system, sans-serif`;return i.measureText(e)}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",e=>{e.preventDefault();this.onClick(this.text)})}initWebGL(){if(Container.pageSnapshot&&this.gl)if(this.parent&&this.isNestedGlass)this.initNestedGlass();else super.initWebGL()}applySnapshot(e){if(!this.parent||!this.isNestedGlass)super.applySnapshot(e)}initNestedGlass(){if(this.parent.webglInitialized){this.setupDynamicNestedShader();this.webglInitialized=!0}else setTimeout(()=>this.initNestedGlass(),100)}setupDynamicNestedShader(){const e=this.gl,t=this.createProgram(e,"\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 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 / containerSize;\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 \n gl_FragColor = vec4(finalTinted, mask);\n }\n ");if(!t)return;e.useProgram(t);const i=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,i);e.bufferData(e.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),e.STATIC_DRAW);const n=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,n);e.bufferData(e.ARRAY_BUFFER,new Float32Array([0,1,1,1,0,0,0,0,1,1,1,0]),e.STATIC_DRAW);const o=e.getAttribLocation(t,"a_position"),s=e.getAttribLocation(t,"a_texcoord"),r=e.getUniformLocation(t,"u_resolution"),a=e.getUniformLocation(t,"u_textureSize"),l=e.getUniformLocation(t,"u_blurRadius"),c=e.getUniformLocation(t,"u_borderRadius"),u=e.getUniformLocation(t,"u_buttonPosition"),d=e.getUniformLocation(t,"u_containerPosition"),m=e.getUniformLocation(t,"u_containerSize"),f=e.getUniformLocation(t,"u_warp"),h=e.getUniformLocation(t,"u_edgeIntensity"),p=e.getUniformLocation(t,"u_rimIntensity"),g=e.getUniformLocation(t,"u_baseIntensity"),x=e.getUniformLocation(t,"u_edgeDistance"),_=e.getUniformLocation(t,"u_rimDistance"),v=e.getUniformLocation(t,"u_baseDistance"),b=e.getUniformLocation(t,"u_cornerBoost"),y=e.getUniformLocation(t,"u_rippleEffect"),R=e.getUniformLocation(t,"u_tintOpacity"),T=e.getUniformLocation(t,"u_image"),C=e.createTexture();e.bindTexture(e.TEXTURE_2D,C);const E=this.parent.canvas;e.texImage2D(e.TEXTURE_2D,0,e.RGBA,E.width,E.height,0,e.RGBA,e.UNSIGNED_BYTE,null);e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR);e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR);e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE);e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE);this.gl_refs={gl:e,texture:C,textureSizeLoc:a,positionLoc:o,texcoordLoc:s,resolutionLoc:r,blurRadiusLoc:l,borderRadiusLoc:c,buttonPositionLoc:u,containerPositionLoc:d,containerSizeLoc:m,warpLoc:f,edgeIntensityLoc:h,rimIntensityLoc:p,baseIntensityLoc:g,edgeDistanceLoc:x,rimDistanceLoc:_,baseDistanceLoc:v,cornerBoostLoc:b,rippleEffectLoc:y,tintOpacityLoc:R,imageLoc:T,positionBuffer:i,texcoordBuffer:n};e.viewport(0,0,this.canvas.width,this.canvas.height);e.clearColor(0,0,0,0);e.bindBuffer(e.ARRAY_BUFFER,i);e.enableVertexAttribArray(o);e.vertexAttribPointer(o,2,e.FLOAT,!1,0,0);e.bindBuffer(e.ARRAY_BUFFER,n);e.enableVertexAttribArray(s);e.vertexAttribPointer(s,2,e.FLOAT,!1,0,0);e.uniform2f(r,this.canvas.width,this.canvas.height);e.uniform2f(a,E.width,E.height);e.uniform1f(l,window.glassControls?.blurRadius||2);e.uniform1f(c,this.borderRadius);e.uniform1f(f,this.warp?1:0);e.uniform1f(h,window.glassControls?.edgeIntensity||.01);e.uniform1f(p,window.glassControls?.rimIntensity||.05);e.uniform1f(g,window.glassControls?.baseIntensity||.01);e.uniform1f(x,window.glassControls?.edgeDistance||.15);e.uniform1f(_,window.glassControls?.rimDistance||.8);e.uniform1f(v,window.glassControls?.baseDistance||.1);e.uniform1f(b,window.glassControls?.cornerBoost||.02);e.uniform1f(y,window.glassControls?.rippleEffect||.1);e.uniform1f(R,this.tintOpacity);const A=this.getPosition(),L=this.parent.getPosition();e.uniform2f(u,A.x,A.y);e.uniform2f(d,L.x,L.y);e.uniform2f(m,this.parent.width,this.parent.height);e.activeTexture(e.TEXTURE0);e.bindTexture(e.TEXTURE_2D,C);e.uniform1i(T,0);this.startNestedRenderLoop()}startNestedRenderLoop(){if(null!==this.nestedAnimationFrameId){cancelAnimationFrame(this.nestedAnimationFrameId);this.nestedAnimationFrameId=null}const e=()=>{if(!this.gl_refs.gl||!this.parent)return;const e=this.gl_refs.gl,t=this.parent.canvas;e.bindTexture(e.TEXTURE_2D,this.gl_refs.texture);e.texSubImage2D(e.TEXTURE_2D,0,0,0,e.RGBA,e.UNSIGNED_BYTE,t);e.clear(e.COLOR_BUFFER_BIT);const i=this.getPosition(),n=this.parent.getPosition();e.uniform2f(this.gl_refs.buttonPositionLoc,i.x,i.y);e.uniform2f(this.gl_refs.containerPositionLoc,n.x,n.y);e.drawArrays(e.TRIANGLES,0,6)},t=()=>{if("undefined"!=typeof Container){const e=!1!==Container.settings?.enableButtonAnimation,t=Container.shouldUseWebGL?Container.shouldUseWebGL():!0;if(!e||!t){this.nestedAnimationFrameId=null;return}}e();this.nestedAnimationFrameId=requestAnimationFrame(t)};t();this.render=e}stopNestedAnimation(){if(null!==this.nestedAnimationFrameId){cancelAnimationFrame(this.nestedAnimationFrameId);this.nestedAnimationFrameId=null}}}if("undefined"!=typeof window)window.Button=e;
@@ -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:!0,autoRefresh:!0,enableButtonAnimation:!0};static settings={...e.defaultSettings};static controlPanelInitialized=!1;static handleWindowRefresh=()=>e.scheduleSnapshotRefresh();static handleVisibilityRefresh=()=>{if("undefined"!=typeof document&&!document.hidden)e.scheduleSnapshotRefresh()};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 n=JSON.parse(t);return{...e.defaultSettings,...n}}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,n){e.settings[t]=n;e.saveSettings();e.applySettings(t)}static applySettings(t){const n=e.shouldUseWebGL();e.instances.forEach(t=>{if(n){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(n)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 shouldUseWebGL(){return!1!==e.settings.enableWebGL}static isAutoRefreshEnabled(){return!1!==e.settings.autoRefresh&&e.shouldUseWebGL()}static initControlPanel(){if(e.controlPanelInitialized||"undefined"==typeof document||!document.body)return;e.controlPanelInitialized=!0;const t=document.createElement("div");t.id="pwtk-glass-control-panel";t.dataset.pwtkControlPanel="true";t.setAttribute("data-html2canvas-ignore","true");t.style.cssText="\n position: fixed;\n right: 20px;\n bottom: 90px;\n z-index: 1000000;\n font-family: system-ui, -apple-system, sans-serif;\n color: #fff;\n text-align: left;\n pointer-events: auto;\n ";const n=document.createElement("button");n.type="button";n.dataset.pwtkControlPanel="true";n.textContent="⚙️ 液态玻璃控制";n.style.cssText="\n background: rgba(33, 33, 33, 0.7);\n border: 1px solid rgba(255, 255, 255, 0.25);\n border-radius: 10px;\n padding: 8px 14px;\n color: #fff;\n cursor: pointer;\n font-size: 13px;\n backdrop-filter: blur(12px);\n ";const i=document.createElement("div");i.dataset.pwtkControlPanel="true";i.style.cssText="\n margin-top: 8px;\n padding: 12px;\n width: 220px;\n border-radius: 12px;\n background: rgba(16, 18, 27, 0.78);\n border: 1px solid rgba(255, 255, 255, 0.18);\n box-shadow: 0 8px 30px rgba(0, 0, 0, 0.28);\n display: none;\n ";const o=(t,n,i)=>{const o=document.createElement("label");o.dataset.pwtkControlPanel="true";o.style.cssText="\n display: flex;\n align-items: flex-start;\n gap: 8px;\n margin-bottom: 10px;\n cursor: pointer;\n font-size: 12px;\n line-height: 1.4;\n ";const r=document.createElement("input");r.type="checkbox";r.checked=!1!==e.settings[t];r.dataset.pwtkControlPanel="true";r.style.cssText="\n margin-top: 3px;\n ";r.addEventListener("change",()=>{e.updateSetting(t,r.checked)});const s=document.createElement("div");s.dataset.pwtkControlPanel="true";const a=document.createElement("div");a.dataset.pwtkControlPanel="true";a.textContent=n;a.style.fontWeight="600";const l=document.createElement("div");l.dataset.pwtkControlPanel="true";l.textContent=i;l.style.opacity="0.7";l.style.fontSize="11px";s.appendChild(a);s.appendChild(l);o.appendChild(r);o.appendChild(s);return o};i.appendChild(o("enableWebGL","启用液态玻璃效果","关闭后使用轻量级 CSS 背景,适合低性能设备"));i.appendChild(o("autoRefresh","自动刷新背景快照","关闭后仅在手动刷新时更新快照"));i.appendChild(o("enableButtonAnimation","按钮动效","关闭后浮动按钮停止 WebGL 动画"));const r=document.createElement("button");r.type="button";r.dataset.pwtkControlPanel="true";r.textContent="手动刷新背景";r.style.cssText="\n width: 100%;\n margin-top: 6px;\n padding: 8px 0;\n border-radius: 8px;\n border: 1px solid rgba(255,255,255,0.24);\n background: rgba(44, 110, 203, 0.75);\n color: #fff;\n cursor: pointer;\n font-weight: 600;\n ";r.addEventListener("click",()=>{e.refreshAll(!0)});i.appendChild(r);n.addEventListener("click",()=>{i.style.display="none"===i.style.display?"block":"none"});t.appendChild(n);t.appendChild(i);document.body.appendChild(t)}constructor(t={}){this.width=0;this.height=0;this.borderRadius=t.borderRadius||48;this.type=t.type||"rounded";this.tintOpacity=void 0!==t.tintOpacity?t.tintOpacity:.2;this.canvas=null;this.element=null;this.gl=null;this.gl_refs={};this.webglInitialized=!1;this.children=[];this.animationFrameId=null;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(()=>{const e=this.element.getBoundingClientRect();let t=Math.ceil(e.width),n=Math.ceil(e.height);if("circle"===this.type){const e=Math.max(t,n);t=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"}if(t!==this.width||n!==this.height){this.width=t;this.height=n;this.canvas.width=t;this.canvas.height=n;this.canvas.style.width=t+"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,t,n);this.gl_refs.gl.uniform2f(this.gl_refs.resolutionLoc,t,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 i=e.gl_refs.gl;i.bindTexture(i.TEXTURE_2D,e.gl_refs.texture);i.texImage2D(i.TEXTURE_2D,0,i.RGBA,t,n,0,i.RGBA,i.UNSIGNED_BYTE,null);i.uniform2f(e.gl_refs.textureSizeLoc,t,n);if(e.gl_refs.containerSizeLoc)i.uniform2f(e.gl_refs.containerSizeLoc,t,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")}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;e.waitingForSnapshot=[];return}const n=e.drainWaitingQueue();t(document.body,{scale:1,useCORS:!0,allowTaint:!0,backgroundColor:null,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(n).forEach(e=>{e.initWebGL()})}).catch(t=>{console.error("[PWTK] html2canvas error:",t);e.isCapturing=!1;e.waitingForSnapshot=[]}).finally(()=>{e.isCapturing=!1;if(e.pendingForceRefresh||e.waitingForSnapshot.length>0){const t=e.pendingForceRefresh;e.pendingForceRefresh=!1;e.refreshAll(t)}})}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;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 n=e.instances[0];if(n){e.isCapturing=!0;n.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();e.initControlPanel()}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 n=t.target;return!e.isMutationInsideGlass(n)}))e.scheduleSnapshotRefresh()});t.observe(document.body,{childList:!0,attributes:!0,characterData:!0,subtree:!0});e.mutationObserver=t}static isControlPanelNode(e){if(!e||"object"!=typeof e)return!1;if("number"!=typeof e.nodeType||1!==e.nodeType)return!1;const t=e;if(t.dataset&&"true"===t.dataset.pwtkControlPanel)return!0;if("function"==typeof t.closest){if(t.closest('[data-pwtk-control-panel="true"]'))return!0}return!1}static isMutationInsideGlass(t){if(!t||"object"!=typeof t||"number"!=typeof t.nodeType)return!1;if(e.isControlPanelNode(t))return!0;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,n=t.classList;if(n&&(n.contains("html2canvas-container")||n.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 n of e.addedNodes)if(t(n))return!0;if(e.removedNodes)for(const n of e.removedNodes)if(t(n))return!0;return!1}setupShader(e){const t=this.gl,n=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_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 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 // Convert to texture coordinate (0 to 1)\n vec2 textureCoord = pagePixel / textureSize;\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 gl_FragColor = vec4(color.rgb, mask);\n }\n ");if(!n)return;t.useProgram(n);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 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 r=t.getAttribLocation(n,"a_position"),s=t.getAttribLocation(n,"a_texcoord"),a=t.getUniformLocation(n,"u_resolution"),l=t.getUniformLocation(n,"u_textureSize"),c=t.getUniformLocation(n,"u_scrollY"),d=t.getUniformLocation(n,"u_pageHeight"),u=t.getUniformLocation(n,"u_viewportHeight"),f=t.getUniformLocation(n,"u_blurRadius"),p=t.getUniformLocation(n,"u_borderRadius"),h=t.getUniformLocation(n,"u_containerPosition"),m=t.getUniformLocation(n,"u_warp"),g=t.getUniformLocation(n,"u_edgeIntensity"),b=t.getUniformLocation(n,"u_rimIntensity"),y=t.getUniformLocation(n,"u_baseIntensity"),v=t.getUniformLocation(n,"u_edgeDistance"),x=t.getUniformLocation(n,"u_rimDistance"),w=t.getUniformLocation(n,"u_baseDistance"),_=t.getUniformLocation(n,"u_cornerBoost"),S=t.getUniformLocation(n,"u_rippleEffect"),R=t.getUniformLocation(n,"u_tintOpacity"),C=t.getUniformLocation(n,"u_image"),L=t.createTexture();t.bindTexture(t.TEXTURE_2D,L);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,texture:L,textureSizeLoc:l,scrollYLoc:c,positionLoc:r,texcoordLoc:s,resolutionLoc:a,pageHeightLoc:d,viewportHeightLoc:u,blurRadiusLoc:f,borderRadiusLoc:p,containerPositionLoc:h,warpLoc:m,edgeIntensityLoc:g,rimIntensityLoc:b,baseIntensityLoc:y,edgeDistanceLoc:v,rimDistanceLoc:x,baseDistanceLoc:w,cornerBoostLoc:_,rippleEffectLoc:S,tintOpacityLoc:R,imageLoc:C,positionBuffer:i,texcoordBuffer:o};t.viewport(0,0,this.canvas.width,this.canvas.height);t.clearColor(0,0,0,0);t.bindBuffer(t.ARRAY_BUFFER,i);t.enableVertexAttribArray(r);t.vertexAttribPointer(r,2,t.FLOAT,!1,0,0);t.bindBuffer(t.ARRAY_BUFFER,o);t.enableVertexAttribArray(s);t.vertexAttribPointer(s,2,t.FLOAT,!1,0,0);t.uniform2f(a,this.canvas.width,this.canvas.height);t.uniform2f(l,e.width,e.height);t.uniform1f(f,window.glassControls?.blurRadius||5);t.uniform1f(p,this.borderRadius);t.uniform1f(m,this.warp?1:0);t.uniform1f(g,window.glassControls?.edgeIntensity||.01);t.uniform1f(b,window.glassControls?.rimIntensity||.05);t.uniform1f(y,window.glassControls?.baseIntensity||.01);t.uniform1f(v,window.glassControls?.edgeDistance||.15);t.uniform1f(x,window.glassControls?.rimDistance||.8);t.uniform1f(w,window.glassControls?.baseDistance||.1);t.uniform1f(_,window.glassControls?.cornerBoost||.02);t.uniform1f(S,window.glassControls?.rippleEffect||.1);t.uniform1f(R,this.tintOpacity);const E=this.getPosition();t.uniform2f(h,E.x,E.y);const T=Math.max(document.body.scrollHeight,document.documentElement.scrollHeight),A=window.innerHeight;t.uniform1f(d,T);t.uniform1f(u,A);t.activeTexture(t.TEXTURE0);t.bindTexture(t.TEXTURE_2D,L);t.uniform1i(C,0);this.startRenderLoop()}startRenderLoop(){if(null!==this.animationFrameId){window.cancelAnimationFrame(this.animationFrameId);this.animationFrameId=null}const e=()=>{if(!this.gl_refs.gl){this.animationFrameId=null;return}const t=this.gl_refs.gl;t.clear(t.COLOR_BUFFER_BIT);const n=window.pageYOffset||document.documentElement.scrollTop;t.uniform1f(this.gl_refs.scrollYLoc,n);const i=this.getPosition();t.uniform2f(this.gl_refs.containerPositionLoc,i.x,i.y);t.drawArrays(t.TRIANGLES,0,6);this.animationFrameId=window.requestAnimationFrame(e)};e();this.render=e}createProgram(e,t,n){const i=this.compileShader(e,e.VERTEX_SHADER,t),o=this.compileShader(e,e.FRAGMENT_SHADER,n);if(!i||!o)return null;const r=e.createProgram();e.attachShader(r,i);e.attachShader(r,o);e.linkProgram(r);if(!e.getProgramParameter(r,e.LINK_STATUS)){console.error("Program link error:",e.getProgramInfoLog(r));return null}return r}compileShader(e,t,n){const i=e.createShader(t);e.shaderSource(i,n);e.compileShader(i);if(!e.getShaderParameter(i,e.COMPILE_STATUS)){console.error("Shader compile error:",e.getShaderInfoLog(i));return null}return i}}if("undefined"!=typeof window){e.settings=e.loadSettings();window.Container=e}
@@ -8,12 +8,240 @@ class Container {
8
8
  static domReadyListenerAttached = false;
9
9
  static refreshTimer = null;
10
10
  static pendingForceRefresh = false;
11
+ static settingsKey = "pwtk_glass_settings_v1";
12
+ static defaultSettings = {
13
+ enableWebGL: true,
14
+ autoRefresh: true,
15
+ enableButtonAnimation: true
16
+ };
17
+ static settings = { ...Container.defaultSettings };
18
+ static controlPanelInitialized = false;
11
19
  static handleWindowRefresh = () => Container.scheduleSnapshotRefresh();
12
20
  static handleVisibilityRefresh = () => {
13
21
  if (typeof document !== "undefined" && !document.hidden) {
14
22
  Container.scheduleSnapshotRefresh();
15
23
  }
16
24
  };
25
+ static loadSettings() {
26
+ if (typeof window === "undefined" || typeof window.localStorage === "undefined") {
27
+ return { ...Container.defaultSettings };
28
+ }
29
+ try {
30
+ const raw = window.localStorage.getItem(Container.settingsKey);
31
+ if (!raw) {
32
+ return { ...Container.defaultSettings };
33
+ }
34
+ const parsed = JSON.parse(raw);
35
+ return { ...Container.defaultSettings, ...parsed };
36
+ } catch {
37
+ return { ...Container.defaultSettings };
38
+ }
39
+ }
40
+ static saveSettings() {
41
+ if (typeof window === "undefined" || typeof window.localStorage === "undefined") {
42
+ return;
43
+ }
44
+ try {
45
+ window.localStorage.setItem(Container.settingsKey, JSON.stringify(Container.settings));
46
+ } catch {
47
+ }
48
+ }
49
+ static updateSetting(key, value) {
50
+ Container.settings[key] = value;
51
+ Container.saveSettings();
52
+ Container.applySettings(key);
53
+ }
54
+ static applySettings(changedKey) {
55
+ const useWebGL = Container.shouldUseWebGL();
56
+ Container.instances.forEach((instance) => {
57
+ if (useWebGL) {
58
+ instance.restoreFromFallback();
59
+ if (instance.webglInitialized) {
60
+ instance.startRenderLoop();
61
+ }
62
+ if (Container.settings.enableButtonAnimation !== false) {
63
+ instance.children.forEach((child) => {
64
+ if (typeof child.startNestedRenderLoop === "function") {
65
+ child.startNestedRenderLoop();
66
+ }
67
+ });
68
+ }
69
+ } else {
70
+ instance.applyFallbackStyles();
71
+ }
72
+ });
73
+ if (typeof window !== "undefined") {
74
+ window.glassControls = {
75
+ ...window.glassControls || {},
76
+ nestedAnimationEnabled: Container.settings.enableButtonAnimation !== false
77
+ };
78
+ }
79
+ if (changedKey === "enableWebGL") {
80
+ if (useWebGL) {
81
+ if (!Container.pageSnapshot && Container.instances.length) {
82
+ Container.waitingForSnapshot = Container.instances.slice();
83
+ if (!Container.isCapturing) {
84
+ Container.isCapturing = true;
85
+ Container.instances[0].capturePageSnapshot(true);
86
+ }
87
+ } else {
88
+ Container.instances.forEach((instance) => {
89
+ if (!instance.webglInitialized && Container.pageSnapshot) {
90
+ instance.initWebGL();
91
+ }
92
+ });
93
+ }
94
+ } else {
95
+ Container.isCapturing = false;
96
+ Container.waitingForSnapshot = [];
97
+ }
98
+ }
99
+ if (changedKey === "autoRefresh" && Container.settings.autoRefresh === false) {
100
+ if (Container.refreshTimer) {
101
+ window.clearTimeout(Container.refreshTimer);
102
+ Container.refreshTimer = null;
103
+ }
104
+ }
105
+ if (changedKey === "enableButtonAnimation") {
106
+ const enabled = Container.settings.enableButtonAnimation !== false;
107
+ Container.instances.forEach((instance) => {
108
+ instance.children.forEach((child) => {
109
+ if (typeof child.startNestedRenderLoop === "function") {
110
+ if (enabled) {
111
+ child.startNestedRenderLoop();
112
+ } else if (typeof child.stopNestedAnimation === "function") {
113
+ child.stopNestedAnimation();
114
+ }
115
+ }
116
+ });
117
+ });
118
+ }
119
+ }
120
+ static shouldUseWebGL() {
121
+ return Container.settings.enableWebGL !== false;
122
+ }
123
+ static isAutoRefreshEnabled() {
124
+ return Container.settings.autoRefresh !== false && Container.shouldUseWebGL();
125
+ }
126
+ static initControlPanel() {
127
+ if (Container.controlPanelInitialized || typeof document === "undefined" || !document.body) {
128
+ return;
129
+ }
130
+ Container.controlPanelInitialized = true;
131
+ const panel = document.createElement("div");
132
+ panel.id = "pwtk-glass-control-panel";
133
+ panel.dataset.pwtkControlPanel = "true";
134
+ panel.setAttribute("data-html2canvas-ignore", "true");
135
+ panel.style.cssText = `
136
+ position: fixed;
137
+ right: 20px;
138
+ bottom: 90px;
139
+ z-index: 1000000;
140
+ font-family: system-ui, -apple-system, sans-serif;
141
+ color: #fff;
142
+ text-align: left;
143
+ pointer-events: auto;
144
+ `;
145
+ const toggleBtn = document.createElement("button");
146
+ toggleBtn.type = "button";
147
+ toggleBtn.dataset.pwtkControlPanel = "true";
148
+ toggleBtn.textContent = "⚙️ 液态玻璃控制";
149
+ toggleBtn.style.cssText = `
150
+ background: rgba(33, 33, 33, 0.7);
151
+ border: 1px solid rgba(255, 255, 255, 0.25);
152
+ border-radius: 10px;
153
+ padding: 8px 14px;
154
+ color: #fff;
155
+ cursor: pointer;
156
+ font-size: 13px;
157
+ backdrop-filter: blur(12px);
158
+ `;
159
+ const body = document.createElement("div");
160
+ body.dataset.pwtkControlPanel = "true";
161
+ body.style.cssText = `
162
+ margin-top: 8px;
163
+ padding: 12px;
164
+ width: 220px;
165
+ border-radius: 12px;
166
+ background: rgba(16, 18, 27, 0.78);
167
+ border: 1px solid rgba(255, 255, 255, 0.18);
168
+ box-shadow: 0 8px 30px rgba(0, 0, 0, 0.28);
169
+ display: none;
170
+ `;
171
+ const createToggle = (key, label, description) => {
172
+ const wrapper = document.createElement("label");
173
+ wrapper.dataset.pwtkControlPanel = "true";
174
+ wrapper.style.cssText = `
175
+ display: flex;
176
+ align-items: flex-start;
177
+ gap: 8px;
178
+ margin-bottom: 10px;
179
+ cursor: pointer;
180
+ font-size: 12px;
181
+ line-height: 1.4;
182
+ `;
183
+ const input = document.createElement("input");
184
+ input.type = "checkbox";
185
+ input.checked = Container.settings[key] !== false;
186
+ input.dataset.pwtkControlPanel = "true";
187
+ input.style.cssText = `
188
+ margin-top: 3px;
189
+ `;
190
+ input.addEventListener("change", () => {
191
+ Container.updateSetting(key, input.checked);
192
+ });
193
+ const textWrap = document.createElement("div");
194
+ textWrap.dataset.pwtkControlPanel = "true";
195
+ const title = document.createElement("div");
196
+ title.dataset.pwtkControlPanel = "true";
197
+ title.textContent = label;
198
+ title.style.fontWeight = "600";
199
+ const desc = document.createElement("div");
200
+ desc.dataset.pwtkControlPanel = "true";
201
+ desc.textContent = description;
202
+ desc.style.opacity = "0.7";
203
+ desc.style.fontSize = "11px";
204
+ textWrap.appendChild(title);
205
+ textWrap.appendChild(desc);
206
+ wrapper.appendChild(input);
207
+ wrapper.appendChild(textWrap);
208
+ return wrapper;
209
+ };
210
+ body.appendChild(
211
+ createToggle("enableWebGL", "启用液态玻璃效果", "关闭后使用轻量级 CSS 背景,适合低性能设备")
212
+ );
213
+ body.appendChild(
214
+ createToggle("autoRefresh", "自动刷新背景快照", "关闭后仅在手动刷新时更新快照")
215
+ );
216
+ body.appendChild(
217
+ createToggle("enableButtonAnimation", "按钮动效", "关闭后浮动按钮停止 WebGL 动画")
218
+ );
219
+ const manualRefresh = document.createElement("button");
220
+ manualRefresh.type = "button";
221
+ manualRefresh.dataset.pwtkControlPanel = "true";
222
+ manualRefresh.textContent = "手动刷新背景";
223
+ manualRefresh.style.cssText = `
224
+ width: 100%;
225
+ margin-top: 6px;
226
+ padding: 8px 0;
227
+ border-radius: 8px;
228
+ border: 1px solid rgba(255,255,255,0.24);
229
+ background: rgba(44, 110, 203, 0.75);
230
+ color: #fff;
231
+ cursor: pointer;
232
+ font-weight: 600;
233
+ `;
234
+ manualRefresh.addEventListener("click", () => {
235
+ Container.refreshAll(true);
236
+ });
237
+ body.appendChild(manualRefresh);
238
+ toggleBtn.addEventListener("click", () => {
239
+ body.style.display = body.style.display === "none" ? "block" : "none";
240
+ });
241
+ panel.appendChild(toggleBtn);
242
+ panel.appendChild(body);
243
+ document.body.appendChild(panel);
244
+ }
17
245
  constructor(options = {}) {
18
246
  this.width = 0;
19
247
  this.height = 0;
@@ -102,6 +330,10 @@ class Container {
102
330
  this.createElement();
103
331
  this.setupCanvas();
104
332
  this.updateSizeFromDOM();
333
+ if (!Container.shouldUseWebGL()) {
334
+ this.applyFallbackStyles();
335
+ return;
336
+ }
105
337
  if (Container.pageSnapshot) {
106
338
  this.initWebGL();
107
339
  } else if (Container.isCapturing) {
@@ -139,6 +371,42 @@ class Container {
139
371
  return;
140
372
  }
141
373
  }
374
+ applyFallbackStyles() {
375
+ if (!this.element) return;
376
+ if (this.canvas) {
377
+ this.canvas.style.display = "none";
378
+ }
379
+ if (this.animationFrameId !== null) {
380
+ window.cancelAnimationFrame(this.animationFrameId);
381
+ this.animationFrameId = null;
382
+ }
383
+ this.element.dataset.pwtkFallback = "true";
384
+ this.element.style.background = "linear-gradient(135deg, rgba(255,255,255,0.35), rgba(200,210,230,0.18))";
385
+ this.element.style.backdropFilter = "blur(14px) saturate(140%)";
386
+ this.element.style.webkitBackdropFilter = "blur(14px) saturate(140%)";
387
+ this.element.style.border = "1px solid rgba(255,255,255,0.28)";
388
+ this.element.style.boxShadow = "0 15px 35px rgba(15, 23, 42, 0.33)";
389
+ this.children.forEach((child) => {
390
+ if (typeof child.stopNestedAnimation === "function") {
391
+ child.stopNestedAnimation();
392
+ }
393
+ });
394
+ }
395
+ restoreFromFallback() {
396
+ if (!this.element) return;
397
+ if (this.element.dataset.pwtkFallback !== "true") {
398
+ return;
399
+ }
400
+ delete this.element.dataset.pwtkFallback;
401
+ if (this.canvas) {
402
+ this.canvas.style.display = "";
403
+ }
404
+ this.element.style.removeProperty("background");
405
+ this.element.style.removeProperty("backdrop-filter");
406
+ this.element.style.removeProperty("-webkit-backdrop-filter");
407
+ this.element.style.removeProperty("border");
408
+ this.element.style.removeProperty("box-shadow");
409
+ }
142
410
  getPosition() {
143
411
  const rect = this.canvas.getBoundingClientRect();
144
412
  return {
@@ -147,7 +415,10 @@ class Container {
147
415
  };
148
416
  }
149
417
  capturePageSnapshot() {
150
- console.log("Capturing page snapshot...");
418
+ if (!Container.shouldUseWebGL()) {
419
+ Container.isCapturing = false;
420
+ return;
421
+ }
151
422
  const html2canvas = window.html2canvas;
152
423
  if (!html2canvas) {
153
424
  console.error("[PWTK] html2canvas not available, containers will not initialize");
@@ -161,11 +432,11 @@ class Container {
161
432
  useCORS: true,
162
433
  allowTaint: true,
163
434
  backgroundColor: null,
435
+ logging: false,
164
436
  ignoreElements: function(element) {
165
437
  return element.classList.contains("glass-container") || element.classList.contains("glass-button") || element.classList.contains("glass-button-text");
166
438
  }
167
439
  }).then((snapshot) => {
168
- console.log("Page snapshot captured");
169
440
  Container.pageSnapshot = snapshot;
170
441
  Container.isCapturing = false;
171
442
  const uniqueTargets = Container.toUniqueList(targets);
@@ -227,6 +498,7 @@ class Container {
227
498
  static refreshAll(force = false) {
228
499
  if (typeof window === "undefined") return;
229
500
  if (!Container.instances.length) return;
501
+ if (!Container.shouldUseWebGL()) return;
230
502
  Container.instances.forEach((instance) => {
231
503
  if (typeof Button !== "undefined" && instance instanceof Button && instance.isNestedGlass) {
232
504
  return;
@@ -247,6 +519,7 @@ class Container {
247
519
  static scheduleSnapshotRefresh() {
248
520
  if (typeof window === "undefined") return;
249
521
  if (!Container.instances.length) return;
522
+ if (!Container.isAutoRefreshEnabled()) return;
250
523
  if (Container.isCapturing) {
251
524
  Container.pendingForceRefresh = true;
252
525
  return;
@@ -278,12 +551,15 @@ class Container {
278
551
  return;
279
552
  }
280
553
  Container.globalListenersSetup = true;
554
+ Container.settings = Container.loadSettings();
281
555
  Container.setupMutationObserver();
282
556
  window.addEventListener("resize", Container.handleWindowRefresh);
283
557
  window.addEventListener("orientationchange", Container.handleWindowRefresh);
284
558
  window.addEventListener("focus", Container.handleWindowRefresh);
285
559
  window.addEventListener("pageshow", Container.handleWindowRefresh);
286
560
  document.addEventListener("visibilitychange", Container.handleVisibilityRefresh);
561
+ Container.applySettings();
562
+ Container.initControlPanel();
287
563
  }
288
564
  static setupMutationObserver() {
289
565
  if (Container.mutationObserver || typeof MutationObserver === "undefined" || typeof document === "undefined") {
@@ -291,6 +567,9 @@ class Container {
291
567
  }
292
568
  const observer = new MutationObserver((mutations) => {
293
569
  const shouldRefresh = mutations.some((mutation) => {
570
+ if (Container.isHtml2CanvasMutation(mutation)) {
571
+ return false;
572
+ }
294
573
  const target = mutation.target;
295
574
  return !Container.isMutationInsideGlass(target);
296
575
  });
@@ -306,14 +585,76 @@ class Container {
306
585
  });
307
586
  Container.mutationObserver = observer;
308
587
  }
588
+ static isControlPanelNode(node) {
589
+ if (!node || typeof node !== "object") return false;
590
+ if (typeof node.nodeType !== "number" || node.nodeType !== 1) return false;
591
+ const el = node;
592
+ if (el.dataset && el.dataset.pwtkControlPanel === "true") {
593
+ return true;
594
+ }
595
+ if (typeof el.closest === "function") {
596
+ const parent = el.closest('[data-pwtk-control-panel="true"]');
597
+ if (parent) {
598
+ return true;
599
+ }
600
+ }
601
+ return false;
602
+ }
309
603
  static isMutationInsideGlass(node) {
310
604
  if (!node || typeof node !== "object" || typeof node.nodeType !== "number") {
311
605
  return false;
312
606
  }
607
+ if (Container.isControlPanelNode(node)) {
608
+ return true;
609
+ }
313
610
  return Container.instances.some((instance) => {
314
611
  return instance.element && instance.element.contains(node);
315
612
  });
316
613
  }
614
+ static isHtml2CanvasMutation(mutation) {
615
+ const checkNode = (node) => {
616
+ if (!node || typeof node !== "object") {
617
+ return false;
618
+ }
619
+ if (typeof node.nodeType !== "number" || node.nodeType !== 1) {
620
+ return false;
621
+ }
622
+ const el = node;
623
+ const classList = el.classList;
624
+ if (classList && (classList.contains("html2canvas-container") || classList.contains("html2canvas-clone"))) {
625
+ return true;
626
+ }
627
+ if (typeof el.id === "string" && el.id.startsWith("html2canvas")) {
628
+ return true;
629
+ }
630
+ const className = typeof el.className === "string" ? el.className : "";
631
+ if (className.includes("html2canvas")) {
632
+ return true;
633
+ }
634
+ if (typeof el.getAttribute === "function" && el.getAttribute("data-html2canvas-ignore") === "true") {
635
+ return true;
636
+ }
637
+ return false;
638
+ };
639
+ if (checkNode(mutation.target)) {
640
+ return true;
641
+ }
642
+ if (mutation.addedNodes) {
643
+ for (const node of mutation.addedNodes) {
644
+ if (checkNode(node)) {
645
+ return true;
646
+ }
647
+ }
648
+ }
649
+ if (mutation.removedNodes) {
650
+ for (const node of mutation.removedNodes) {
651
+ if (checkNode(node)) {
652
+ return true;
653
+ }
654
+ }
655
+ }
656
+ return false;
657
+ }
317
658
  setupShader(image) {
318
659
  const gl = this.gl;
319
660
  const vsSource = `
@@ -732,5 +1073,6 @@ class Container {
732
1073
  }
733
1074
  }
734
1075
  if (typeof window !== "undefined") {
1076
+ Container.settings = Container.loadSettings();
735
1077
  window.Container = Container;
736
1078
  }