@utsp/render 0.9.0-nightly.20251210150441.b1cbb1e → 0.9.0-nightly.20251211152508.63e8b0d
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/index.cjs +39 -11
- package/dist/index.d.ts +283 -4
- package/dist/index.mjs +39 -11
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var L=Object.defineProperty;var et=Object.getOwnPropertyDescriptor;var it=Object.getOwnPropertyNames;var st=Object.prototype.hasOwnProperty;var rt=(b,t,e)=>t in b?L(b,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):b[t]=e;var C=(b,t)=>L(b,"name",{value:t,configurable:!0});var at=(b,t)=>{for(var e in t)L(b,e,{get:t[e],enumerable:!0})},nt=(b,t,e,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of it(t))!st.call(b,s)&&s!==e&&L(b,s,{get:()=>t[s],enumerable:!(i=et(t,s))||i.enumerable});return b};var ot=b=>nt(L({},"__esModule",{value:!0}),b);var n=(b,t,e)=>(rt(b,typeof t!="symbol"?t+"":t,e),e);var ht={};at(ht,{AutoplayOverlay:()=>O,BitmapFontAtlas:()=>U,DEFAULT_PALETTE:()=>H,GridOverlay:()=>T,ImageFontAtlas:()=>S,PostProcessOverlay:()=>k,ScalingMode:()=>E.ScalingMode,Terminal2D:()=>P,TerminalGL:()=>_,colorToPaletteIndex:()=>tt,getAtlasColumns:()=>M,getCharGridPosition:()=>D,getMaxCharCode:()=>F,paletteIndexToColor:()=>J,version:()=>lt});module.exports=ot(ht);var H=["#000000","#800000","#008000","#808000","#000080","#800080","#008080","#c0c0c0","#808080","#ff0000","#00ff00","#ffff00","#0000ff","#ff00ff","#00ffff","#ffffff","#080808","#121212","#1c1c1c","#262626","#303030","#3a3a3a","#444444","#4e4e4e","#585858","#626262","#6c6c6c","#767676","#808080","#8a8a8a","#949494","#9e9e9e","#a80000","#00a800","#a8a800","#0000a8","#a800a8","#00a8a8","#a8a8a8","#545454","#fc5454","#54fc54","#fcfc54","#5454fc","#fc54fc","#54fcfc","#fcfcfc","#000000",...Array(192).fill("#808080")];function J(b,t=H){return b<0||b>=t.length?"#ff00ff":t[b]}C(J,"paletteIndexToColor");function tt(b,t=H){let e=t.indexOf(b.toLowerCase());return e>=0?e:0}C(tt,"colorToPaletteIndex");var z=class z{constructor(t,e){n(this,"canvas");n(this,"ctx");n(this,"container");n(this,"cols",0);n(this,"rows",0);n(this,"cellWidth",0);n(this,"cellHeight",0);n(this,"offsetX",0);n(this,"offsetY",0);n(this,"strokeColor","rgba(80, 80, 80, 0.4)");n(this,"lineWidth",1);this.container=t,this.canvas=document.createElement("canvas"),this.canvas.className="grid-overlay-canvas";let i=e?.zIndex??10;this.canvas.style.cssText=`
|
|
2
2
|
display: block !important;
|
|
3
3
|
position: absolute !important;
|
|
4
4
|
pointer-events: none !important;
|
|
5
5
|
image-rendering: pixelated !important;
|
|
6
6
|
image-rendering: crisp-edges !important;
|
|
7
|
-
z-index: ${
|
|
8
|
-
`,this.container.appendChild(this.canvas);let
|
|
7
|
+
z-index: ${i} !important;
|
|
8
|
+
`,this.container.appendChild(this.canvas);let s=this.canvas.getContext("2d");if(!s)throw new Error("[GridOverlay] Impossible de cr\xE9er le contexte 2D");this.ctx=s,e&&(e.strokeColor&&(this.strokeColor=e.strokeColor),e.lineWidth!==void 0&&(this.lineWidth=e.lineWidth))}setDimensions(t,e,i,s,r=0,a=0){this.cols=t,this.rows=e,this.cellWidth=i,this.cellHeight=s,this.offsetX=r,this.offsetY=a}setCanvasSize(t,e){this.canvas.width=t,this.canvas.height=e,this.ctx.setTransform(1,0,0,1,0,0)}setStyle(t){t.strokeColor&&(this.strokeColor=t.strokeColor),t.lineWidth!==void 0&&(this.lineWidth=t.lineWidth)}setTransform(t,e,i,s,r){let a=s.left-r.left,l=s.top-r.top,o=s.width,h=s.height;(this.canvas.width!==o||this.canvas.height!==h)&&(this.canvas.width=o,this.canvas.height=h);let c=this.canvas.style;c.width=`${o}px`,c.height=`${h}px`,c.left=`${a}px`,c.top=`${l}px`}render(){if(!this.ctx)return;let t=this.cols*this.cellWidth,e=this.rows*this.cellHeight,i=t>0?this.canvas.width/t:1,s=e>0?this.canvas.height/e:1,r=Math.min(i,s),a=this.cellWidth*r,l=this.cellHeight*r,o=this.cols*a,h=this.rows*l,c=this.offsetX*r,d=this.offsetY*r;if(!(o===0||h===0)){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.strokeStyle=this.strokeColor,this.ctx.lineWidth=Math.max(1,this.lineWidth*r),this.ctx.beginPath();for(let f=0;f<=this.cols;f++){let u=c+f*a+.5;this.ctx.moveTo(u,d),this.ctx.lineTo(u,d+h)}for(let f=0;f<=this.rows;f++){let u=d+f*l+.5;this.ctx.moveTo(c,u),this.ctx.lineTo(c+o,u)}this.ctx.stroke()}}update(t,e,i,s,r,a,l=0,o=0){this.setDimensions(t,e,i,s,l,o),this.setCanvasSize(r,a),this.render()}setVisible(t){this.canvas.style.display=t?"block":"none"}destroy(){this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}getCanvas(){return this.canvas}};C(z,"GridOverlay");var T=z;function M(b){return Math.sqrt(b)*16}C(M,"getAtlasColumns");function F(b){return b*256-1}C(F,"getMaxCharCode");function D(b,t){let e=Math.floor(b/256),i=b%256,s=Math.sqrt(t),r=e%s,a=Math.floor(e/s),l=i%16,o=Math.floor(i/16);return{col:r*16+l,row:a*16+o}}C(D,"getCharGridPosition");var E=require("@utsp/types");var W=class W{constructor(t,e){n(this,"canvas");n(this,"gl");n(this,"parentElement");n(this,"containerDiv");n(this,"cols");n(this,"rows");n(this,"charWidth");n(this,"charHeight");n(this,"cellWidth",8);n(this,"cellHeight",8);n(this,"glyphOffsetX",0);n(this,"glyphOffsetY",0);n(this,"canvasBgColor");n(this,"showGrid");n(this,"supportsUint32Indices",!1);n(this,"useUint16Indices",!1);n(this,"gridOverlay");n(this,"bitmapFont");n(this,"atlasTexture",null);n(this,"atlasCanvas");n(this,"atlasColumns",0);n(this,"fontLoaded",!1);n(this,"paletteTexture",null);n(this,"program",null);n(this,"positionBuffer",null);n(this,"texCoordBuffer",null);n(this,"colorIndexBuffer",null);n(this,"indexBuffer",null);n(this,"aPosition");n(this,"aTexCoord");n(this,"aColorIndex");n(this,"uResolution",null);n(this,"uTexture",null);n(this,"uPalette",null);n(this,"resizeObserver");n(this,"charCodeToAtlasIndex",new Uint16Array(65536).fill(65535));n(this,"atlasUVs",new Float32Array(0));n(this,"cachedAtlasWidth",0);n(this,"cachedAtlasHeight",0);n(this,"paletteFloat",new Float32Array(256*4));n(this,"maxCells",0);n(this,"renderPositions",new Float32Array(0));n(this,"renderTexCoords",new Float32Array(0));n(this,"renderColorIndices",new Float32Array(0));n(this,"renderIndices",new Uint32Array(0));n(this,"cachedResolution",[0,0]);n(this,"cachedTextureUnit",-1);n(this,"cachedPaletteUnit",-1);n(this,"cachedTextureUniform",!1);n(this,"cachedPaletteUniform",!1);n(this,"paletteHash",0);n(this,"currentScale",1);n(this,"scalingMode",E.ScalingMode.None);n(this,"ambientEffectEnabled",!1);n(this,"ambientEffectCanvas",null);n(this,"ambientEffectCtx",null);n(this,"ambientEffectBlur",E.POST_PROCESS_DEFAULTS.ambientEffect.blur);n(this,"ambientEffectScale",E.POST_PROCESS_DEFAULTS.ambientEffect.scale);n(this,"ambientEffectOpacity",.7);n(this,"onResizeCallback");n(this,"staticPositionsInitialized",!1);n(this,"vaoExtension",null);n(this,"vao",null);n(this,"instancedExtension",null);n(this,"useInstancing",!1);n(this,"instanceDataBuffer",null);n(this,"instanceData",new Float32Array(0));n(this,"templateQuadPositions",new Float32Array(0));n(this,"templateQuadIndices",new Uint16Array(0));if(!t)throw new Error("TerminalGL: Parent element is required");if(!Number.isInteger(e.cols)||e.cols<=0)throw new Error(`TerminalGL: Invalid cols: ${e.cols}. Must be a positive integer.`);if(!Number.isInteger(e.rows)||e.rows<=0)throw new Error(`TerminalGL: Invalid rows: ${e.rows}. Must be a positive integer.`);let i=e.cols*e.rows,s=W.checkCompatibility();if(s.errors.length>0)throw new Error(`TerminalGL: WebGL incompatible - ${s.errors.join(", ")}`);let r=e.forceUint16??!1;if(r&&i>s.maxCellsUint16)throw new Error(`TerminalGL: Terminal size ${e.cols}\xD7${e.rows} (${i} cells) exceeds Uint16 limit of ${s.maxCellsUint16} cells. Maximum size with Uint16: ${Math.floor(Math.sqrt(s.maxCellsUint16))}\xD7${Math.floor(Math.sqrt(s.maxCellsUint16))} (or smaller rectangles like 127\xD764)`);if(!s.uint32Indices&&i>s.maxCellsUint16)throw new Error(`TerminalGL: Terminal size ${e.cols}\xD7${e.rows} (${i} cells) exceeds device limit of ${s.maxCellsUint16} cells (OES_element_index_uint not supported). Maximum size: ${Math.floor(Math.sqrt(s.maxCellsUint16))}\xD7${Math.floor(Math.sqrt(s.maxCellsUint16))}`);i>s.recommendedMaxCells&&console.warn(`[TerminalGL] \u26A0\uFE0F Large terminal size ${e.cols}\xD7${e.rows} (${i} cells) exceeds recommended limit of ${s.recommendedMaxCells} cells. Performance may be impacted.`),s.warnings.length>0&&console.warn("[TerminalGL] WebGL Compatibility Warnings:",s.warnings);let a=e.charWidth??8,l=e.charHeight??8;if(!Number.isFinite(a)||a<=0)throw new Error(`TerminalGL: Invalid charWidth: ${a}. Must be a positive number.`);if(!Number.isFinite(l)||l<=0)throw new Error(`TerminalGL: Invalid charHeight: ${l}. Must be a positive number.`);for(this.parentElement=t,this.cols=e.cols,this.rows=e.rows,this.charWidth=a,this.charHeight=l,this.canvasBgColor=e.canvasBgColor!==void 0?e.canvasBgColor:null,this.showGrid=e.showGrid??!1,this.scalingMode=e.scalingMode??E.ScalingMode.None,e.ambientEffect&&(this.ambientEffectEnabled=!0,typeof e.ambientEffect=="object"&&(this.ambientEffectBlur=e.ambientEffect.blur??E.POST_PROCESS_DEFAULTS.ambientEffect.blur,this.ambientEffectScale=e.ambientEffect.scale??E.POST_PROCESS_DEFAULTS.ambientEffect.scale,this.ambientEffectOpacity=e.ambientEffect.opacity??.7));this.parentElement.firstChild;)this.parentElement.removeChild(this.parentElement.firstChild);let o=window.getComputedStyle(this.parentElement).position;o!=="relative"&&o!=="absolute"&&o!=="fixed"&&(this.parentElement.style.position="relative"),this.containerDiv=document.createElement("div"),this.containerDiv.className="terminalgl-container",this.containerDiv.style.cssText=`
|
|
9
9
|
position: absolute !important;
|
|
10
10
|
top: 0 !important;
|
|
11
11
|
left: 0 !important;
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
backface-visibility: hidden !important;
|
|
33
33
|
position: relative !important;
|
|
34
34
|
z-index: 1 !important;
|
|
35
|
-
`;let h=this.canvas.getContext("webgl",{alpha:this.canvasBgColor===null,premultipliedAlpha:!1});if(!h)throw new Error("TerminalGL: WebGL not supported");this.gl=h,this.supportsUint32Indices=!!h.getExtension("OES_element_index_uint"),this.useUint16Indices=r||!this.supportsUint32Indices,this.useUint16Indices?console.warn(`[TerminalGL] \u{1F4CA} Using Uint16 indices (max ${
|
|
35
|
+
`;let h=this.canvas.getContext("webgl",{alpha:this.canvasBgColor===null,premultipliedAlpha:!1});if(!h)throw new Error("TerminalGL: WebGL not supported");this.gl=h,this.supportsUint32Indices=!!h.getExtension("OES_element_index_uint"),this.useUint16Indices=r||!this.supportsUint32Indices,this.useUint16Indices?console.warn(`[TerminalGL] \u{1F4CA} Using Uint16 indices (max ${s.maxCellsUint16} cells)`):console.warn(`[TerminalGL] \u{1F4CA} Using Uint32 indices (max ${s.maxCellsUint32} cells)`),this.vaoExtension=h.getExtension("OES_vertex_array_object"),this.vaoExtension&&console.warn("[TerminalGL] \u2705 VAO extension enabled (faster attribute binding)"),this.instancedExtension=h.getExtension("ANGLE_instanced_arrays"),this.instancedExtension?(this.useInstancing=!0,console.warn("[TerminalGL] \u{1F680} Instanced rendering enabled (massive draw call reduction)")):console.warn("[TerminalGL] \u26A0\uFE0F Instanced rendering not available (fallback to standard rendering)"),this.containerDiv.appendChild(this.canvas),this.ambientEffectEnabled&&(this.createAmbientEffectCanvas(),console.warn("[TerminalGL] \u{1F308} Ambient effect enabled at startup")),this.parentElement.appendChild(this.containerDiv),this.initRenderBuffers();try{this.initWebGL()}catch(c){throw console.error("[TerminalGL] \u274C Failed to initialize WebGL:",c),c}this.showGrid&&this.initGridOverlay(),this.setupResizeObserver()}static checkCompatibility(){let t={webgl1:!1,uint32Indices:!1,maxTextureSize:0,maxViewportDims:[0,0],maxCellsUint16:0,maxCellsUint32:0,recommendedMaxCells:0,warnings:[],errors:[]},e=document.createElement("canvas"),i=e.getContext("webgl")||e.getContext("experimental-webgl");if(!i||!(i instanceof WebGLRenderingContext))return t.errors.push("\u274C WebGL 1.0 not supported by this browser/device"),t;let s=i;t.webgl1=!0;try{t.maxTextureSize=s.getParameter(s.MAX_TEXTURE_SIZE);let o=s.getParameter(s.MAX_VIEWPORT_DIMS);t.maxViewportDims=[o[0],o[1]]}catch(o){return t.errors.push(`\u274C Failed to query WebGL parameters: ${o}`),t}let r=s.getExtension("OES_element_index_uint");t.uint32Indices=!!r;let a=8;if(t.maxCellsUint16=Math.floor(65535/a),t.uint32Indices){let o=Math.floor(t.maxTextureSize*t.maxTextureSize/64);t.maxCellsUint32=Math.min(o,262144)}else t.maxCellsUint32=t.maxCellsUint16,t.warnings.push(`\u26A0\uFE0F OES_element_index_uint not supported - limited to ${t.maxCellsUint16} cells (e.g., 90\xD790 or 127\xD764)`);return t.recommendedMaxCells=Math.floor((t.uint32Indices?t.maxCellsUint32:t.maxCellsUint16)*.8),t.maxTextureSize<256?t.errors.push(`\u274C MAX_TEXTURE_SIZE too small: ${t.maxTextureSize} (minimum 256 required for font atlas)`):t.maxTextureSize<2048&&t.warnings.push(`\u26A0\uFE0F Small MAX_TEXTURE_SIZE: ${t.maxTextureSize} (may limit font atlas)`),(t.maxViewportDims[0]<1024||t.maxViewportDims[1]<768)&&t.warnings.push(`\u26A0\uFE0F Small MAX_VIEWPORT_DIMS: ${t.maxViewportDims[0]}\xD7${t.maxViewportDims[1]}`),s.getExtension("WEBGL_lose_context")||t.warnings.push("\u26A0\uFE0F WEBGL_lose_context not supported - may cause memory leaks"),t}initInstancedBuffers(){let t=this.gl;this.templateQuadPositions=new Float32Array([0,0,this.cellWidth,0,0,this.cellHeight,this.cellWidth,this.cellHeight]),this.templateQuadIndices=new Uint16Array([0,1,2,2,1,3]),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferData(t.ARRAY_BUFFER,this.templateQuadPositions,t.STATIC_DRAW),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,this.templateQuadIndices,t.STATIC_DRAW);let e=this.cols*this.rows*2;this.instanceData=new Float32Array(e*8),t.bindBuffer(t.ARRAY_BUFFER,this.instanceDataBuffer),t.bufferData(t.ARRAY_BUFFER,this.instanceData.byteLength,t.DYNAMIC_DRAW)}initRenderBuffers(){this.maxCells=this.cols*this.rows*2,this.renderPositions=new Float32Array(this.maxCells*4*2),this.renderTexCoords=new Float32Array(this.maxCells*4*2),this.renderColorIndices=new Float32Array(this.maxCells*4),this.useUint16Indices?this.renderIndices=new Uint16Array(this.maxCells*6):this.renderIndices=new Uint32Array(this.maxCells*6),this.staticPositionsInitialized=!1}precomputeStaticPositions(){let t=this.cellWidth,e=this.cellHeight,i=this.glyphOffsetX,s=this.glyphOffsetY,r=this.charWidth,a=this.charHeight,l=0,o=0,h=0;for(let d=0;d<this.rows;d++){let f=Math.round(d*e),u=Math.round(f+e);for(let m=0;m<this.cols;m++){let g=Math.round(m*t),v=Math.round(g+t);this.renderPositions[l++]=g,this.renderPositions[l++]=f,this.renderPositions[l++]=v,this.renderPositions[l++]=f,this.renderPositions[l++]=g,this.renderPositions[l++]=u,this.renderPositions[l++]=v,this.renderPositions[l++]=u,this.renderIndices[o++]=h,this.renderIndices[o++]=h+1,this.renderIndices[o++]=h+2,this.renderIndices[o++]=h+2,this.renderIndices[o++]=h+1,this.renderIndices[o++]=h+3,h+=4;let x=Math.round(g+i),A=Math.round(f+s),p=Math.round(x+r),w=Math.round(A+a);this.renderPositions[l++]=x,this.renderPositions[l++]=A,this.renderPositions[l++]=p,this.renderPositions[l++]=A,this.renderPositions[l++]=x,this.renderPositions[l++]=w,this.renderPositions[l++]=p,this.renderPositions[l++]=w,this.renderIndices[o++]=h,this.renderIndices[o++]=h+1,this.renderIndices[o++]=h+2,this.renderIndices[o++]=h+2,this.renderIndices[o++]=h+1,this.renderIndices[o++]=h+3,h+=4}}let c=this.gl;c.bindBuffer(c.ARRAY_BUFFER,this.positionBuffer),c.bufferSubData(c.ARRAY_BUFFER,0,this.renderPositions),c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,this.indexBuffer),c.bufferSubData(c.ELEMENT_ARRAY_BUFFER,0,this.renderIndices),this.staticPositionsInitialized=!0}initWebGL(){let t=this.gl,e;this.useInstancing?e=`
|
|
36
36
|
// Per-vertex attributes (template quad)
|
|
37
37
|
attribute vec2 aPosition; // Local quad position (0,0 to cellW,cellH)
|
|
38
38
|
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
float paletteU = (aColorIndex + 0.5) / 256.0;
|
|
94
94
|
vColor = texture2D(uPalette, vec2(paletteU, 0.5));
|
|
95
95
|
}
|
|
96
|
-
`;let
|
|
96
|
+
`;let i=`
|
|
97
97
|
precision mediump float;
|
|
98
98
|
|
|
99
99
|
uniform sampler2D uTexture;
|
|
@@ -106,12 +106,14 @@
|
|
|
106
106
|
if (vTexCoord.x == 0.0 && vTexCoord.y == 0.0) {
|
|
107
107
|
gl_FragColor = vColor;
|
|
108
108
|
} else {
|
|
109
|
-
// Textured character: multiply color by
|
|
109
|
+
// Textured character: multiply atlas color by uniform color
|
|
110
|
+
// - White glyph (1,1,1) \u2192 uniform color applied normally
|
|
111
|
+
// - Colored glyph \u2192 atlas color \xD7 uniform color (multiply blend)
|
|
110
112
|
vec4 texColor = texture2D(uTexture, vTexCoord);
|
|
111
|
-
gl_FragColor = vec4(
|
|
113
|
+
gl_FragColor = vec4(texColor.rgb * vColor.rgb, texColor.a);
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
|
-
`,i=this.compileShader(t.VERTEX_SHADER,e),r=this.compileShader(t.FRAGMENT_SHADER,s);if(!i||!r)throw new Error("Shader compilation error");let a=t.createProgram();if(!a)throw new Error("Unable to create WebGL program");if(t.attachShader(a,i),t.attachShader(a,r),t.linkProgram(a),!t.getProgramParameter(a,t.LINK_STATUS)){let l=t.getProgramInfoLog(a);throw new Error("Program linking error: "+l)}this.program=a,this.cachedTextureUniform=!1,this.cachedPaletteUniform=!1,this.aPosition=t.getAttribLocation(a,"aPosition"),this.useInstancing?(this.aTexCoord=t.getAttribLocation(a,"aInstanceOffset"),this.aColorIndex=t.getAttribLocation(a,"aInstanceUVs"),this.instanceDataBuffer=t.createBuffer()):(this.aTexCoord=t.getAttribLocation(a,"aTexCoord"),this.aColorIndex=t.getAttribLocation(a,"aColorIndex")),this.uResolution=t.getUniformLocation(a,"uResolution"),this.uTexture=t.getUniformLocation(a,"uTexture"),this.uPalette=t.getUniformLocation(a,"uPalette"),this.positionBuffer=t.createBuffer(),this.texCoordBuffer=t.createBuffer(),this.colorIndexBuffer=t.createBuffer(),this.indexBuffer=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderPositions.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderTexCoords.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderColorIndices.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,t.DYNAMIC_DRAW),this.vaoExtension&&(this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.enableVertexAttribArray(this.aPosition),t.vertexAttribPointer(this.aPosition,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.enableVertexAttribArray(this.aTexCoord),t.vertexAttribPointer(this.aTexCoord,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.enableVertexAttribArray(this.aColorIndex),t.vertexAttribPointer(this.aColorIndex,1,t.FLOAT,!1,0,0),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null))),t.enable(t.BLEND),t.blendFunc(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA),t.viewport(0,0,this.canvas.width,this.canvas.height)}compileShader(t,e){let s=this.gl,i=s.createShader(t);return i?(s.shaderSource(i,e),s.compileShader(i),s.getShaderParameter(i,s.COMPILE_STATUS)?i:(console.error("Shader compilation error:",s.getShaderInfoLog(i)),s.deleteShader(i),null)):null}initGridOverlay(){this.gridOverlay=new T(this.containerDiv,{strokeColor:"rgba(144, 24, 24, 1)",lineWidth:1,zIndex:10}),this.updateGridOverlay()}updateGridOverlay(){if(!this.gridOverlay)return;let t=this.cols*this.cellWidth,e=this.rows*this.cellHeight,s=this.canvas.getBoundingClientRect(),i=this.containerDiv.getBoundingClientRect();this.gridOverlay.setDimensions(this.cols,this.rows,this.cellWidth,this.cellHeight,0,0),this.gridOverlay.setTransform(this.currentScale,t,e,s,i),this.gridOverlay.render()}setBitmapFont(t,e,s,i,r){this.bitmapFont=t,this.charWidth=Math.round(e),this.charHeight=Math.round(s),this.cellWidth=Math.round(i),this.cellHeight=Math.round(r),this.glyphOffsetX=Math.round((this.cellWidth-this.charWidth)/2),this.glyphOffsetY=Math.round((this.cellHeight-this.charHeight)/2),this.useInstancing&&(console.warn("[TerminalGL] \u{1F680} Initializing instanced buffers..."),this.initInstancedBuffers());try{this.generateAtlas()}catch(a){throw console.error("[TerminalGL] \u274C Failed to generate atlas:",a),a}this.buildCharCodeMap(),this.precomputeAtlasUVs(),this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.useInstancing||this.precomputeStaticPositions(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}generateAtlas(){if(!this.bitmapFont)return;let t=Array.from(this.bitmapFont.keys()).sort((o,h)=>o-h),e=t.length;this.atlasColumns=Math.ceil(Math.sqrt(e));let s=Math.ceil(e/this.atlasColumns),i=this.atlasColumns*this.charWidth,r=s*this.charHeight;this.atlasCanvas=document.createElement("canvas"),this.atlasCanvas.width=i,this.atlasCanvas.height=r;let a=this.atlasCanvas.getContext("2d");if(!a)throw new Error("Unable to create 2D context for atlas");a.clearRect(0,0,i,r),a.fillStyle="#ffffff";let l=0;for(let o of t){let h=this.bitmapFont.get(o);if(!h)continue;let c=l%this.atlasColumns,d=Math.floor(l/this.atlasColumns),f=c*this.charWidth,u=d*this.charHeight;for(let m=0;m<Math.min(h.length,this.charHeight);m++){let b=h[m];for(let g=0;g<this.charWidth;g++){let x=1<<7-g;b&x&&a.fillRect(f+g,u+m,1,1)}}l++}this.createAtlasTexture()}buildCharCodeMap(){if(!this.bitmapFont)return;this.charCodeToAtlasIndex.fill(65535);let t=Array.from(this.bitmapFont.keys()).sort((e,s)=>e-s);for(let e=0;e<t.length;e++)this.charCodeToAtlasIndex[t[e]]=e}precomputeAtlasUVs(){if(!this.bitmapFont)return;let t=this.bitmapFont.size;this.atlasUVs=new Float32Array(t*4),this.cachedAtlasWidth=this.atlasColumns*this.charWidth,this.cachedAtlasHeight=Math.ceil(t/this.atlasColumns)*this.charHeight;let e=this.cachedAtlasWidth,s=this.cachedAtlasHeight,i=.5/e,r=.5/s;for(let a=0;a<t;a++){let l=a%this.atlasColumns,o=Math.floor(a/this.atlasColumns),h=(l*this.charWidth+i)/e,c=(o*this.charHeight+r)/s,d=((l+1)*this.charWidth-i)/e,f=((o+1)*this.charHeight-r)/s,u=a*4;this.atlasUVs[u]=h,this.atlasUVs[u+1]=c,this.atlasUVs[u+2]=d,this.atlasUVs[u+3]=f}}createAtlasTexture(){if(this.atlasCanvas)try{let t=this.gl,e=t.createTexture();if(!e)throw new Error("Unable to create texture");t.bindTexture(t.TEXTURE_2D,e),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,this.atlasCanvas),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),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),this.atlasTexture=e}catch(t){throw console.error("[TerminalGL] \u274C Failed to create atlas texture:",t),t}}clear(){let t=this.gl;t&&t.clear(t.COLOR_BUFFER_BIT)}parseColor(t){if(t.startsWith("#")){let e=t.slice(1),s=0,i=0,r=0;return e.length===3?(s=parseInt(e[0]+e[0],16),i=parseInt(e[1]+e[1],16),r=parseInt(e[2]+e[2],16)):e.length===6&&(s=parseInt(e.slice(0,2),16),i=parseInt(e.slice(2,4),16),r=parseInt(e.slice(4,6),16)),[s/255,i/255,r/255,1]}if(t.startsWith("rgb")){let e=t.match(/rgba?\(([^)]+)\)/);if(e){let s=e[1].split(",").map(i=>parseFloat(i.trim()));return[s[0]/255,s[1]/255,s[2]/255,s[3]!==void 0?s[3]:1]}}return[1,1,1,1]}setupResizeObserver(){if(!(typeof ResizeObserver>"u"))try{this.resizeObserver=new ResizeObserver(()=>{try{this.updateCanvasSize()}catch(t){console.error("[TerminalGL] \u274C Error in resize callback:",t)}}),this.resizeObserver.observe(this.parentElement),this.updateCanvasSize()}catch(t){console.error("[TerminalGL] \u274C Failed to setup ResizeObserver:",t)}}updateCanvasSize(){let t=this.parentElement.clientWidth,e=this.parentElement.clientHeight;if(t===0||e===0)return;let s=this.cols*this.cellWidth,i=this.rows*this.cellHeight;if(s===0||i===0)return;let r=t/s,a=e/i,l=Math.min(r,a),o;switch(this.scalingMode){case y.ScalingMode.Integer:o=Math.max(1,Math.floor(l));break;case y.ScalingMode.Half:o=Math.max(1,Math.floor(l*2)/2);break;case y.ScalingMode.Quarter:o=Math.max(1,Math.floor(l*4)/4);break;case y.ScalingMode.Eighth:o=Math.max(1,Math.floor(l*8)/8);break;case y.ScalingMode.None:default:o=Math.max(.1,l);break}if(this.canvas.style.transform=`scale(${o})`,this.ambientEffectEnabled&&this.ambientEffectCanvas){let h=o*this.ambientEffectScale;this.ambientEffectCanvas.style.transform=`scale(${h})`}this.currentScale=o,this.onResizeCallback&&this.onResizeCallback(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}updateAmbientEffect(){!this.ambientEffectCanvas||!this.ambientEffectCtx||this.ambientEffectCtx.drawImage(this.canvas,0,0)}setPalette(t){let e=0;for(let s=0;s<Math.min(t.length,256);s++){let i=t[s];e=(e<<5)-e+i.r,e=(e<<5)-e+i.g,e=(e<<5)-e+i.b,e=(e<<5)-e+i.a,e=e|0}if(e!==this.paletteHash){this.paletteHash=e;for(let s=0;s<t.length&&s<256;s++){let i=t[s],r=s*4;this.paletteFloat[r]=i.r/255,this.paletteFloat[r+1]=i.g/255,this.paletteFloat[r+2]=i.b/255,this.paletteFloat[r+3]=i.a/255}this.updatePaletteTexture()}}updatePaletteTexture(){try{let t=this.gl;this.paletteTexture||(this.paletteTexture=t.createTexture()),t.bindTexture(t.TEXTURE_2D,this.paletteTexture);let e=new Uint8Array(256*4);for(let s=0;s<256;s++){let i=s*4;e[i]=this.paletteFloat[i]*255,e[i+1]=this.paletteFloat[i+1]*255,e[i+2]=this.paletteFloat[i+2]*255,e[i+3]=this.paletteFloat[i+3]*255}t.texImage2D(t.TEXTURE_2D,0,t.RGBA,256,1,0,t.RGBA,t.UNSIGNED_BYTE,e),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),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),t.bindTexture(t.TEXTURE_2D,null)}catch(t){throw console.error("[TerminalGL] \u274C Failed to update palette texture:",t),t}}renderDisplayData(t){try{if(!this.isReady()){console.warn("[TerminalGL] \u26A0\uFE0F Not ready: font not loaded yet");return}(t.width!==this.cols||t.height!==this.rows)&&this.resize(t.width,t.height),this.renderDirect(t),this.ambientEffectEnabled&&this.ambientEffectCanvas&&this.ambientEffectCtx&&this.updateAmbientEffect()}catch(e){console.error("[TerminalGL] \u274C Error rendering display data:",e)}}renderDirect(t){let e=this.gl;if(!this.staticPositionsInitialized&&!this.useInstancing&&this.precomputeStaticPositions(),this.canvasBgColor!==null){let s=this.parseColor(this.canvasBgColor);e.clearColor(s[0],s[1],s[2],s[3])}else e.clearColor(0,0,0,0);e.clear(e.COLOR_BUFFER_BIT),this.useInstancing?this.renderInstanced(t):this.renderDirectBuffers(t)}renderInstanced(t){let e=this.gl,s=this.instancedExtension;if(!this.instanceData||this.instanceData.length===0){console.warn("[TerminalGL] \u26A0\uFE0F Instance data not initialized, falling back to standard rendering"),this.useInstancing=!1,this.renderDirectBuffers(t);return}let i=t.width*t.height;if(this.useUint16Indices&&i>8191){console.warn(`[TerminalGL] \u26A0\uFE0F Terminal too large for instanced rendering with Uint16 (${i} cells > 8191). Falling back to standard rendering.`),this.useInstancing=!1,this.renderDirectBuffers(t);return}let r=0,a=this.instanceData.length/8;for(let f=0;f<t.height;f++)for(let u=0;u<t.width;u++){let m=t.cells[f*t.width+u],b=m.bgColorIndex,g=m.fgColorIndex,x=this.canvasBgColor!==null&&b===255,R=m.char===" "||g===255;if(!x){if(r>=a){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${a}`);break}let p=r*8;this.instanceData[p]=u,this.instanceData[p+1]=f,this.instanceData[p+2]=0,this.instanceData[p+3]=0,this.instanceData[p+4]=0,this.instanceData[p+5]=0,this.instanceData[p+6]=b,this.instanceData[p+7]=0,r++}if(!R){let p=m.char.charCodeAt(0),w=this.charCodeToAtlasIndex[p];if(w!==65535){if(r>=a){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${a}`);break}let A=w*4,C=r*8;this.instanceData[C]=u,this.instanceData[C+1]=f,this.instanceData[C+2]=this.atlasUVs[A],this.instanceData[C+3]=this.atlasUVs[A+1],this.instanceData[C+4]=this.atlasUVs[A+2],this.instanceData[C+5]=this.atlasUVs[A+3],this.instanceData[C+6]=0,this.instanceData[C+7]=g,r++}}}if(r===0){console.warn("[TerminalGL] \u26A0\uFE0F No instances to draw (all cells skipped)");return}e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.instanceData.subarray(0,r*8)),e.useProgram(this.program),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(this.aPosition),e.vertexAttribPointer(this.aPosition,2,e.FLOAT,!1,0,0),s.vertexAttribDivisorANGLE(this.aPosition,0),e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer);let l=8*Float32Array.BYTES_PER_ELEMENT,o=e.getAttribLocation(this.program,"aInstanceOffset");e.enableVertexAttribArray(o),e.vertexAttribPointer(o,2,e.FLOAT,!1,l,0),s.vertexAttribDivisorANGLE(o,1);let h=e.getAttribLocation(this.program,"aInstanceUVs");e.enableVertexAttribArray(h),e.vertexAttribPointer(h,4,e.FLOAT,!1,l,2*Float32Array.BYTES_PER_ELEMENT),s.vertexAttribDivisorANGLE(h,1);let c=e.getAttribLocation(this.program,"aInstanceColors");e.enableVertexAttribArray(c),e.vertexAttribPointer(c,2,e.FLOAT,!1,l,6*Float32Array.BYTES_PER_ELEMENT),s.vertexAttribDivisorANGLE(c,1),e.uniform2f(this.uResolution,this.canvas.width,this.canvas.height);let d=e.getUniformLocation(this.program,"uCellSize");e.uniform2f(d,this.cellWidth,this.cellHeight),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.atlasTexture),e.uniform1i(this.uTexture,0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.paletteTexture),e.uniform1i(this.uPalette,1),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer),s.drawElementsInstancedANGLE(e.TRIANGLES,6,e.UNSIGNED_SHORT,0,r),s.vertexAttribDivisorANGLE(this.aPosition,0),s.vertexAttribDivisorANGLE(o,0),s.vertexAttribDivisorANGLE(h,0),s.vertexAttribDivisorANGLE(c,0)}renderDirectBuffers(t){let e=this.gl,s=t.width*t.height,i=s*2;if(i>this.maxCells){console.error(`[TerminalGL] \u274C Buffer overflow detected! Trying to render ${i} quads but buffer capacity is ${this.maxCells}. This should not happen - resize() should have reallocated.`);return}let r=0,a=0,l=t.cells,o=s;for(let m=0;m<o;m++){let b=l[m],g=b.bgColorIndex,R=this.canvasBgColor!==null&&g===255?255:g,p=0;for(let C=0;C<4;C++)this.renderTexCoords[r++]=p,this.renderTexCoords[r++]=p,this.renderColorIndices[a++]=R;let w=b.fgColorIndex,A=b.char;if(A===" "||w===255)for(let C=0;C<4;C++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[a++]=255;else{let C=A.charCodeAt(0),k=this.charCodeToAtlasIndex[C];if(k!==65535){let I=k*4,$=this.atlasUVs[I],Y=this.atlasUVs[I+1],N=this.atlasUVs[I+2],V=this.atlasUVs[I+3];this.renderTexCoords[r++]=$,this.renderTexCoords[r++]=Y,this.renderColorIndices[a++]=w,this.renderTexCoords[r++]=N,this.renderTexCoords[r++]=Y,this.renderColorIndices[a++]=w,this.renderTexCoords[r++]=$,this.renderTexCoords[r++]=V,this.renderColorIndices[a++]=w,this.renderTexCoords[r++]=N,this.renderTexCoords[r++]=V,this.renderColorIndices[a++]=w}else for(let I=0;I<4;I++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[a++]=255}}(r!==o*2*4*2||a!==o*2*4)&&console.error("[TerminalGL] \u274C Buffer index mismatch!",{texIdx:r,colorIdx:a,expected:o*2*4}),e.useProgram(this.program),this.vao&&this.vaoExtension?(this.vaoExtension.bindVertexArrayOES(this.vao),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,a))):(e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(this.aPosition),e.vertexAttribPointer(this.aPosition,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),e.enableVertexAttribArray(this.aTexCoord),e.vertexAttribPointer(this.aTexCoord,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,a)),e.enableVertexAttribArray(this.aColorIndex),e.vertexAttribPointer(this.aColorIndex,1,e.FLOAT,!1,0,0));let h=this.canvas.width,c=this.canvas.height;(this.cachedResolution[0]!==h||this.cachedResolution[1]!==c)&&(e.uniform2f(this.uResolution,h,c),this.cachedResolution[0]=h,this.cachedResolution[1]=c),this.cachedTextureUnit!==0&&(e.activeTexture(e.TEXTURE0),this.cachedTextureUnit=0),e.bindTexture(e.TEXTURE_2D,this.atlasTexture),this.cachedTextureUniform||(e.uniform1i(this.uTexture,0),this.cachedTextureUniform=!0),this.cachedPaletteUnit!==1&&(e.activeTexture(e.TEXTURE1),this.cachedPaletteUnit=1),e.bindTexture(e.TEXTURE_2D,this.paletteTexture),this.cachedPaletteUniform||(e.uniform1i(this.uPalette,1),this.cachedPaletteUniform=!0),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer);let f=t.width*t.height*2*6,u=this.useUint16Indices?e.UNSIGNED_SHORT:e.UNSIGNED_INT;e.drawElements(e.TRIANGLES,f,u,0)}resize(t,e){if(!Number.isInteger(t)||t<=0){console.error(`[TerminalGL] \u274C Invalid cols: ${t}. Must be positive integer.`);return}if(!Number.isInteger(e)||e<=0){console.error(`[TerminalGL] \u274C Invalid rows: ${e}. Must be positive integer.`);return}if(t===this.cols&&e===this.rows)return;let s=t*e,i=this.useUint16Indices?8191:262144;if(s>i){let c=Math.floor(Math.sqrt(i));console.error(`[TerminalGL] \u274C Cannot resize to ${t}\xD7${e} (${s} cells). Device limit: ${i} cells (max ~${c}\xD7${c}). ${this.useUint16Indices?"Device lacks OES_element_index_uint extension.":""}`);return}let r=F.checkCompatibility();s>r.recommendedMaxCells&&console.warn(`[TerminalGL] \u26A0\uFE0F Resizing to ${t}\xD7${e} (${s} cells) exceeds recommended limit (${r.recommendedMaxCells} cells). Performance may degrade.`),this.cols=t,this.rows=e,this.staticPositionsInitialized=!1;let a=this.cols*this.rows*2,l=Math.ceil(a*1.5),o=a>this.maxCells,h=this.maxCells>l*4;if(o||h){let c=this.maxCells;this.maxCells=l,this.renderPositions=new Float32Array(this.maxCells*4*2),this.renderTexCoords=new Float32Array(this.maxCells*4*2),this.renderColorIndices=new Float32Array(this.maxCells*4),this.useUint16Indices?this.renderIndices=new Uint16Array(this.maxCells*6):this.renderIndices=new Uint32Array(this.maxCells*6);let d=this.gl;this.useInstancing||(d.bindBuffer(d.ARRAY_BUFFER,this.positionBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderPositions.byteLength,d.DYNAMIC_DRAW)),d.bindBuffer(d.ARRAY_BUFFER,this.texCoordBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderTexCoords.byteLength,d.DYNAMIC_DRAW),d.bindBuffer(d.ARRAY_BUFFER,this.colorIndexBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderColorIndices.byteLength,d.DYNAMIC_DRAW),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,this.indexBuffer),d.bufferData(d.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,d.DYNAMIC_DRAW),this.vaoExtension&&this.vao&&(this.vaoExtension.deleteVertexArrayOES(this.vao),this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),d.bindBuffer(d.ARRAY_BUFFER,this.positionBuffer),d.enableVertexAttribArray(this.aPosition),d.vertexAttribPointer(this.aPosition,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,this.texCoordBuffer),d.enableVertexAttribArray(this.aTexCoord),d.vertexAttribPointer(this.aTexCoord,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,this.colorIndexBuffer),d.enableVertexAttribArray(this.aColorIndex),d.vertexAttribPointer(this.aColorIndex,1,d.FLOAT,!1,0,0),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null)));let f=o?"\u{1F4C8} Growing":"\u{1F4C9} Shrinking",u=c*160/1048576,m=this.maxCells*160/1048576;console.warn(`[TerminalGL] ${f} buffers: ${u.toFixed(2)}MB \u2192 ${m.toFixed(2)}MB (${c} \u2192 ${this.maxCells} quads)`)}this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.ambientEffectEnabled&&this.ambientEffectCanvas&&(this.ambientEffectCanvas.width=this.canvas.width,this.ambientEffectCanvas.height=this.canvas.height,this.ambientEffectCanvas.style.width=`${this.canvas.width}px`,this.ambientEffectCanvas.style.height=`${this.canvas.height}px`),this.showGrid&&this.gridOverlay&&this.updateGridOverlay(),this.updateCanvasSize()}getCanvas(){return this.canvas}getGridSize(){return{cols:this.cols,rows:this.rows}}getCellWidth(){return this.cellWidth}getCellHeight(){return this.cellHeight}getCurrentScale(){return this.currentScale}getBaseDimensions(){return{width:this.cols*this.cellWidth,height:this.rows*this.cellHeight}}setOnResizeCallback(t){this.onResizeCallback=t}clearOnResizeCallback(){this.onResizeCallback=void 0}setScalingMode(t){this.scalingMode!==t&&(this.scalingMode=t,this.updateCanvasSize())}getScalingMode(){return this.scalingMode}setGrid(t){t.enabled?(this.gridOverlay?this.gridOverlay.setStyle({strokeColor:t.color,lineWidth:t.lineWidth}):this.gridOverlay=new T(this.containerDiv,{strokeColor:t.color??"rgba(144, 24, 24, 1)",lineWidth:t.lineWidth??1,zIndex:10}),this.showGrid=!0,this.gridOverlay.setVisible(!0),this.updateGridOverlay()):(this.showGrid=!1,this.gridOverlay&&this.gridOverlay.setVisible(!1))}isGridEnabled(){return this.showGrid}setAmbientEffect(t){typeof t=="boolean"?this.ambientEffectEnabled=t:(this.ambientEffectEnabled=!0,t.blur!==void 0&&(this.ambientEffectBlur=t.blur),t.scale!==void 0&&(this.ambientEffectScale=t.scale)),this.ambientEffectEnabled&&!this.ambientEffectCanvas&&this.createAmbientEffectCanvas(),this.ambientEffectCanvas&&(this.ambientEffectEnabled?(this.ambientEffectCanvas.style.display="block",this.ambientEffectCanvas.style.filter=`blur(${this.ambientEffectBlur}px)`,this.ambientEffectCanvas.style.transform=`scale(${this.ambientEffectScale})`,this.updateAmbientEffect()):this.ambientEffectCanvas.style.display="none")}createAmbientEffectCanvas(){this.ambientEffectCanvas||(this.ambientEffectCanvas=document.createElement("canvas"),this.ambientEffectCanvas.className="terminalgl-ambient-effect-canvas",this.ambientEffectCanvas.width=this.canvas.width,this.ambientEffectCanvas.height=this.canvas.height,this.ambientEffectCanvas.style.cssText=`
|
|
116
|
+
`,s=this.compileShader(t.VERTEX_SHADER,e),r=this.compileShader(t.FRAGMENT_SHADER,i);if(!s||!r)throw new Error("Shader compilation error");let a=t.createProgram();if(!a)throw new Error("Unable to create WebGL program");if(t.attachShader(a,s),t.attachShader(a,r),t.linkProgram(a),!t.getProgramParameter(a,t.LINK_STATUS)){let l=t.getProgramInfoLog(a);throw new Error("Program linking error: "+l)}this.program=a,this.cachedTextureUniform=!1,this.cachedPaletteUniform=!1,this.aPosition=t.getAttribLocation(a,"aPosition"),this.useInstancing?(this.aTexCoord=t.getAttribLocation(a,"aInstanceOffset"),this.aColorIndex=t.getAttribLocation(a,"aInstanceUVs"),this.instanceDataBuffer=t.createBuffer()):(this.aTexCoord=t.getAttribLocation(a,"aTexCoord"),this.aColorIndex=t.getAttribLocation(a,"aColorIndex")),this.uResolution=t.getUniformLocation(a,"uResolution"),this.uTexture=t.getUniformLocation(a,"uTexture"),this.uPalette=t.getUniformLocation(a,"uPalette"),this.positionBuffer=t.createBuffer(),this.texCoordBuffer=t.createBuffer(),this.colorIndexBuffer=t.createBuffer(),this.indexBuffer=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderPositions.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderTexCoords.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderColorIndices.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,t.DYNAMIC_DRAW),this.vaoExtension&&(this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.enableVertexAttribArray(this.aPosition),t.vertexAttribPointer(this.aPosition,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.enableVertexAttribArray(this.aTexCoord),t.vertexAttribPointer(this.aTexCoord,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.enableVertexAttribArray(this.aColorIndex),t.vertexAttribPointer(this.aColorIndex,1,t.FLOAT,!1,0,0),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null))),t.enable(t.BLEND),t.blendFunc(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA),t.viewport(0,0,this.canvas.width,this.canvas.height)}compileShader(t,e){let i=this.gl,s=i.createShader(t);return s?(i.shaderSource(s,e),i.compileShader(s),i.getShaderParameter(s,i.COMPILE_STATUS)?s:(console.error("Shader compilation error:",i.getShaderInfoLog(s)),i.deleteShader(s),null)):null}initGridOverlay(){this.gridOverlay=new T(this.containerDiv,{strokeColor:"rgba(144, 24, 24, 1)",lineWidth:1,zIndex:10}),this.updateGridOverlay()}updateGridOverlay(){if(!this.gridOverlay)return;let t=this.cols*this.cellWidth,e=this.rows*this.cellHeight,i=this.canvas.getBoundingClientRect(),s=this.containerDiv.getBoundingClientRect();this.gridOverlay.setDimensions(this.cols,this.rows,this.cellWidth,this.cellHeight,0,0),this.gridOverlay.setTransform(this.currentScale,t,e,i,s),this.gridOverlay.render()}setBitmapFont(t,e,i,s,r){this.bitmapFont=t,this.fontLoaded=!0,this.charWidth=Math.round(e),this.charHeight=Math.round(i),this.cellWidth=Math.round(s),this.cellHeight=Math.round(r),this.glyphOffsetX=Math.round((this.cellWidth-this.charWidth)/2),this.glyphOffsetY=Math.round((this.cellHeight-this.charHeight)/2),this.useInstancing&&(console.warn("[TerminalGL] \u{1F680} Initializing instanced buffers..."),this.initInstancedBuffers());try{this.generateAtlas()}catch(a){throw console.error("[TerminalGL] \u274C Failed to generate atlas:",a),a}this.buildCharCodeMap(),this.precomputeAtlasUVs(),this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.useInstancing||this.precomputeStaticPositions(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}async setImageFont(t,e,i,s,r,a){this.fontLoaded=!0,this.charWidth=Math.round(e),this.charHeight=Math.round(i),this.cellWidth=Math.round(s),this.cellHeight=Math.round(r),this.atlasColumns=M(a);let l=F(a);this.glyphOffsetX=Math.round((this.cellWidth-this.charWidth)/2),this.glyphOffsetY=Math.round((this.cellHeight-this.charHeight)/2),this.useInstancing&&(console.warn("[TerminalGL] \u{1F680} Initializing instanced buffers for ImageFont..."),this.initInstancedBuffers()),await this.loadImageFontAtlas(t),this.charCodeToAtlasIndex.fill(65535);for(let o=0;o<=l;o++)this.charCodeToAtlasIndex[o]=o;this.precomputeImageFontUVs(a),this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.useInstancing||this.precomputeStaticPositions(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}async loadImageFontAtlas(t){return new Promise((e,i)=>{let s=new Uint8Array(t),r=new Blob([s],{type:"image/png"}),a=URL.createObjectURL(r),l=new Image;l.onload=()=>{URL.revokeObjectURL(a),this.atlasCanvas=document.createElement("canvas"),this.atlasCanvas.width=l.width,this.atlasCanvas.height=l.height;let o=this.atlasCanvas.getContext("2d");if(!o){i(new Error("Failed to create 2D context for atlas"));return}o.drawImage(l,0,0),this.cachedAtlasWidth=l.width,this.cachedAtlasHeight=l.height,this.createAtlasTexture(),e()},l.onerror=()=>{URL.revokeObjectURL(a),i(new Error("Failed to load PNG image for ImageFont atlas"))},l.src=a})}precomputeImageFontUVs(t){let e=t*256;this.atlasUVs=new Float32Array(e*4);let i=this.cachedAtlasWidth,s=this.cachedAtlasHeight,r=.5/i,a=.5/s;for(let l=0;l<e;l++){let{col:o,row:h}=D(l,t),c=(o*this.charWidth+r)/i,d=(h*this.charHeight+a)/s,f=((o+1)*this.charWidth-r)/i,u=((h+1)*this.charHeight-a)/s,m=l*4;this.atlasUVs[m]=c,this.atlasUVs[m+1]=d,this.atlasUVs[m+2]=f,this.atlasUVs[m+3]=u}}generateAtlas(){if(!this.bitmapFont)return;let t=Array.from(this.bitmapFont.keys()).sort((o,h)=>o-h),e=t.length;this.atlasColumns=Math.ceil(Math.sqrt(e));let i=Math.ceil(e/this.atlasColumns),s=this.atlasColumns*this.charWidth,r=i*this.charHeight;this.atlasCanvas=document.createElement("canvas"),this.atlasCanvas.width=s,this.atlasCanvas.height=r;let a=this.atlasCanvas.getContext("2d");if(!a)throw new Error("Unable to create 2D context for atlas");a.clearRect(0,0,s,r),a.fillStyle="#ffffff";let l=0;for(let o of t){let h=this.bitmapFont.get(o);if(!h)continue;let c=l%this.atlasColumns,d=Math.floor(l/this.atlasColumns),f=c*this.charWidth,u=d*this.charHeight;for(let m=0;m<Math.min(h.length,this.charHeight);m++){let g=h[m];for(let v=0;v<this.charWidth;v++){let x=1<<7-v;g&x&&a.fillRect(f+v,u+m,1,1)}}l++}this.createAtlasTexture()}buildCharCodeMap(){if(!this.bitmapFont)return;this.charCodeToAtlasIndex.fill(65535);let t=Array.from(this.bitmapFont.keys()).sort((e,i)=>e-i);for(let e=0;e<t.length;e++)this.charCodeToAtlasIndex[t[e]]=e}precomputeAtlasUVs(){if(!this.bitmapFont)return;let t=this.bitmapFont.size;this.atlasUVs=new Float32Array(t*4),this.cachedAtlasWidth=this.atlasColumns*this.charWidth,this.cachedAtlasHeight=Math.ceil(t/this.atlasColumns)*this.charHeight;let e=this.cachedAtlasWidth,i=this.cachedAtlasHeight,s=.5/e,r=.5/i;for(let a=0;a<t;a++){let l=a%this.atlasColumns,o=Math.floor(a/this.atlasColumns),h=(l*this.charWidth+s)/e,c=(o*this.charHeight+r)/i,d=((l+1)*this.charWidth-s)/e,f=((o+1)*this.charHeight-r)/i,u=a*4;this.atlasUVs[u]=h,this.atlasUVs[u+1]=c,this.atlasUVs[u+2]=d,this.atlasUVs[u+3]=f}}createAtlasTexture(){if(this.atlasCanvas)try{let t=this.gl,e=t.createTexture();if(!e)throw new Error("Unable to create texture");t.bindTexture(t.TEXTURE_2D,e),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,this.atlasCanvas),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),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),this.atlasTexture=e}catch(t){throw console.error("[TerminalGL] \u274C Failed to create atlas texture:",t),t}}clear(){let t=this.gl;t&&t.clear(t.COLOR_BUFFER_BIT)}parseColor(t){if(t.startsWith("#")){let e=t.slice(1),i=0,s=0,r=0;return e.length===3?(i=parseInt(e[0]+e[0],16),s=parseInt(e[1]+e[1],16),r=parseInt(e[2]+e[2],16)):e.length===6&&(i=parseInt(e.slice(0,2),16),s=parseInt(e.slice(2,4),16),r=parseInt(e.slice(4,6),16)),[i/255,s/255,r/255,1]}if(t.startsWith("rgb")){let e=t.match(/rgba?\(([^)]+)\)/);if(e){let i=e[1].split(",").map(s=>parseFloat(s.trim()));return[i[0]/255,i[1]/255,i[2]/255,i[3]!==void 0?i[3]:1]}}return[1,1,1,1]}setupResizeObserver(){if(!(typeof ResizeObserver>"u"))try{this.resizeObserver=new ResizeObserver(()=>{try{this.updateCanvasSize()}catch(t){console.error("[TerminalGL] \u274C Error in resize callback:",t)}}),this.resizeObserver.observe(this.parentElement),this.updateCanvasSize()}catch(t){console.error("[TerminalGL] \u274C Failed to setup ResizeObserver:",t)}}updateCanvasSize(){let t=this.parentElement.clientWidth,e=this.parentElement.clientHeight;if(t===0||e===0)return;let i=this.cols*this.cellWidth,s=this.rows*this.cellHeight;if(i===0||s===0)return;let r=t/i,a=e/s,l=Math.min(r,a),o;switch(this.scalingMode){case E.ScalingMode.Integer:o=Math.max(1,Math.floor(l));break;case E.ScalingMode.Half:o=Math.max(1,Math.floor(l*2)/2);break;case E.ScalingMode.Quarter:o=Math.max(1,Math.floor(l*4)/4);break;case E.ScalingMode.Eighth:o=Math.max(1,Math.floor(l*8)/8);break;case E.ScalingMode.None:default:o=Math.max(.1,l);break}if(this.canvas.style.transform=`scale(${o})`,this.ambientEffectEnabled&&this.ambientEffectCanvas){let h=o*this.ambientEffectScale;this.ambientEffectCanvas.style.transform=`scale(${h})`}this.currentScale=o,this.onResizeCallback&&this.onResizeCallback(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}updateAmbientEffect(){!this.ambientEffectCanvas||!this.ambientEffectCtx||this.ambientEffectCtx.drawImage(this.canvas,0,0)}setPalette(t){let e=0;for(let i=0;i<Math.min(t.length,256);i++){let s=t[i];e=(e<<5)-e+s.r,e=(e<<5)-e+s.g,e=(e<<5)-e+s.b,e=(e<<5)-e+s.a,e=e|0}if(e!==this.paletteHash){this.paletteHash=e;for(let i=0;i<t.length&&i<256;i++){let s=t[i],r=i*4;this.paletteFloat[r]=s.r/255,this.paletteFloat[r+1]=s.g/255,this.paletteFloat[r+2]=s.b/255,this.paletteFloat[r+3]=s.a/255}this.updatePaletteTexture()}}updatePaletteTexture(){try{let t=this.gl;this.paletteTexture||(this.paletteTexture=t.createTexture()),t.bindTexture(t.TEXTURE_2D,this.paletteTexture);let e=new Uint8Array(256*4);for(let i=0;i<256;i++){let s=i*4;e[s]=this.paletteFloat[s]*255,e[s+1]=this.paletteFloat[s+1]*255,e[s+2]=this.paletteFloat[s+2]*255,e[s+3]=this.paletteFloat[s+3]*255}t.texImage2D(t.TEXTURE_2D,0,t.RGBA,256,1,0,t.RGBA,t.UNSIGNED_BYTE,e),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),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),t.bindTexture(t.TEXTURE_2D,null)}catch(t){throw console.error("[TerminalGL] \u274C Failed to update palette texture:",t),t}}renderDisplayData(t){try{if(!this.isReady()){console.warn("[TerminalGL] \u26A0\uFE0F Not ready: font not loaded yet");return}(t.width!==this.cols||t.height!==this.rows)&&this.resize(t.width,t.height),this.renderDirect(t),this.ambientEffectEnabled&&this.ambientEffectCanvas&&this.ambientEffectCtx&&this.updateAmbientEffect()}catch(e){console.error("[TerminalGL] \u274C Error rendering display data:",e)}}renderDirect(t){let e=this.gl;if(!this.staticPositionsInitialized&&!this.useInstancing&&this.precomputeStaticPositions(),this.canvasBgColor!==null){let i=this.parseColor(this.canvasBgColor);e.clearColor(i[0],i[1],i[2],i[3])}else e.clearColor(0,0,0,0);e.clear(e.COLOR_BUFFER_BIT),this.useInstancing?this.renderInstanced(t):this.renderDirectBuffers(t)}renderInstanced(t){let e=this.gl,i=this.instancedExtension;if(!this.instanceData||this.instanceData.length===0){console.warn("[TerminalGL] \u26A0\uFE0F Instance data not initialized, falling back to standard rendering"),this.useInstancing=!1,this.renderDirectBuffers(t);return}let s=t.width*t.height;if(this.useUint16Indices&&s>8191){console.warn(`[TerminalGL] \u26A0\uFE0F Terminal too large for instanced rendering with Uint16 (${s} cells > 8191). Falling back to standard rendering.`),this.useInstancing=!1,this.renderDirectBuffers(t);return}let r=0,a=this.instanceData.length/8;for(let f=0;f<t.height;f++)for(let u=0;u<t.width;u++){let m=t.cells[f*t.width+u],g=m.bgColorIndex,v=m.fgColorIndex,x=this.canvasBgColor!==null&&g===255,A=m.char===" "||v===255;if(!x){if(r>=a){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${a}`);break}let p=r*8;this.instanceData[p]=u,this.instanceData[p+1]=f,this.instanceData[p+2]=0,this.instanceData[p+3]=0,this.instanceData[p+4]=0,this.instanceData[p+5]=0,this.instanceData[p+6]=g,this.instanceData[p+7]=0,r++}if(!A){let p=m.char.charCodeAt(0),w=this.charCodeToAtlasIndex[p];if(w!==65535){if(r>=a){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${a}`);break}let R=w*4,y=r*8;this.instanceData[y]=u,this.instanceData[y+1]=f,this.instanceData[y+2]=this.atlasUVs[R],this.instanceData[y+3]=this.atlasUVs[R+1],this.instanceData[y+4]=this.atlasUVs[R+2],this.instanceData[y+5]=this.atlasUVs[R+3],this.instanceData[y+6]=0,this.instanceData[y+7]=v,r++}}}if(r===0){console.warn("[TerminalGL] \u26A0\uFE0F No instances to draw (all cells skipped)");return}e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.instanceData.subarray(0,r*8)),e.useProgram(this.program),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(this.aPosition),e.vertexAttribPointer(this.aPosition,2,e.FLOAT,!1,0,0),i.vertexAttribDivisorANGLE(this.aPosition,0),e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer);let l=8*Float32Array.BYTES_PER_ELEMENT,o=e.getAttribLocation(this.program,"aInstanceOffset");e.enableVertexAttribArray(o),e.vertexAttribPointer(o,2,e.FLOAT,!1,l,0),i.vertexAttribDivisorANGLE(o,1);let h=e.getAttribLocation(this.program,"aInstanceUVs");e.enableVertexAttribArray(h),e.vertexAttribPointer(h,4,e.FLOAT,!1,l,2*Float32Array.BYTES_PER_ELEMENT),i.vertexAttribDivisorANGLE(h,1);let c=e.getAttribLocation(this.program,"aInstanceColors");e.enableVertexAttribArray(c),e.vertexAttribPointer(c,2,e.FLOAT,!1,l,6*Float32Array.BYTES_PER_ELEMENT),i.vertexAttribDivisorANGLE(c,1),e.uniform2f(this.uResolution,this.canvas.width,this.canvas.height);let d=e.getUniformLocation(this.program,"uCellSize");e.uniform2f(d,this.cellWidth,this.cellHeight),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.atlasTexture),e.uniform1i(this.uTexture,0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.paletteTexture),e.uniform1i(this.uPalette,1),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer),i.drawElementsInstancedANGLE(e.TRIANGLES,6,e.UNSIGNED_SHORT,0,r),i.vertexAttribDivisorANGLE(this.aPosition,0),i.vertexAttribDivisorANGLE(o,0),i.vertexAttribDivisorANGLE(h,0),i.vertexAttribDivisorANGLE(c,0)}renderDirectBuffers(t){let e=this.gl,i=t.width*t.height,s=i*2;if(s>this.maxCells){console.error(`[TerminalGL] \u274C Buffer overflow detected! Trying to render ${s} quads but buffer capacity is ${this.maxCells}. This should not happen - resize() should have reallocated.`);return}let r=0,a=0,l=t.cells,o=i;for(let m=0;m<o;m++){let g=l[m],v=g.bgColorIndex,A=this.canvasBgColor!==null&&v===255?255:v,p=0;for(let y=0;y<4;y++)this.renderTexCoords[r++]=p,this.renderTexCoords[r++]=p,this.renderColorIndices[a++]=A;let w=g.fgColorIndex,R=g.char;if(R===" "||w===255)for(let y=0;y<4;y++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[a++]=255;else{let y=R.charCodeAt(0),q=this.charCodeToAtlasIndex[y];if(q!==65535){let B=q*4,j=this.atlasUVs[B],Q=this.atlasUVs[B+1],Z=this.atlasUVs[B+2],K=this.atlasUVs[B+3];this.renderTexCoords[r++]=j,this.renderTexCoords[r++]=Q,this.renderColorIndices[a++]=w,this.renderTexCoords[r++]=Z,this.renderTexCoords[r++]=Q,this.renderColorIndices[a++]=w,this.renderTexCoords[r++]=j,this.renderTexCoords[r++]=K,this.renderColorIndices[a++]=w,this.renderTexCoords[r++]=Z,this.renderTexCoords[r++]=K,this.renderColorIndices[a++]=w}else for(let B=0;B<4;B++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[a++]=255}}(r!==o*2*4*2||a!==o*2*4)&&console.error("[TerminalGL] \u274C Buffer index mismatch!",{texIdx:r,colorIdx:a,expected:o*2*4}),e.useProgram(this.program),this.vao&&this.vaoExtension?(this.vaoExtension.bindVertexArrayOES(this.vao),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,a))):(e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(this.aPosition),e.vertexAttribPointer(this.aPosition,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),e.enableVertexAttribArray(this.aTexCoord),e.vertexAttribPointer(this.aTexCoord,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,a)),e.enableVertexAttribArray(this.aColorIndex),e.vertexAttribPointer(this.aColorIndex,1,e.FLOAT,!1,0,0));let h=this.canvas.width,c=this.canvas.height;(this.cachedResolution[0]!==h||this.cachedResolution[1]!==c)&&(e.uniform2f(this.uResolution,h,c),this.cachedResolution[0]=h,this.cachedResolution[1]=c),this.cachedTextureUnit!==0&&(e.activeTexture(e.TEXTURE0),this.cachedTextureUnit=0),e.bindTexture(e.TEXTURE_2D,this.atlasTexture),this.cachedTextureUniform||(e.uniform1i(this.uTexture,0),this.cachedTextureUniform=!0),this.cachedPaletteUnit!==1&&(e.activeTexture(e.TEXTURE1),this.cachedPaletteUnit=1),e.bindTexture(e.TEXTURE_2D,this.paletteTexture),this.cachedPaletteUniform||(e.uniform1i(this.uPalette,1),this.cachedPaletteUniform=!0),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer);let f=t.width*t.height*2*6,u=this.useUint16Indices?e.UNSIGNED_SHORT:e.UNSIGNED_INT;e.drawElements(e.TRIANGLES,f,u,0)}resize(t,e){if(!Number.isInteger(t)||t<=0){console.error(`[TerminalGL] \u274C Invalid cols: ${t}. Must be positive integer.`);return}if(!Number.isInteger(e)||e<=0){console.error(`[TerminalGL] \u274C Invalid rows: ${e}. Must be positive integer.`);return}if(t===this.cols&&e===this.rows)return;let i=t*e,s=this.useUint16Indices?8191:262144;if(i>s){let c=Math.floor(Math.sqrt(s));console.error(`[TerminalGL] \u274C Cannot resize to ${t}\xD7${e} (${i} cells). Device limit: ${s} cells (max ~${c}\xD7${c}). ${this.useUint16Indices?"Device lacks OES_element_index_uint extension.":""}`);return}let r=W.checkCompatibility();i>r.recommendedMaxCells&&console.warn(`[TerminalGL] \u26A0\uFE0F Resizing to ${t}\xD7${e} (${i} cells) exceeds recommended limit (${r.recommendedMaxCells} cells). Performance may degrade.`),this.cols=t,this.rows=e,this.staticPositionsInitialized=!1;let a=this.cols*this.rows*2,l=Math.ceil(a*1.5),o=a>this.maxCells,h=this.maxCells>l*4;if(o||h){let c=this.maxCells;this.maxCells=l,this.renderPositions=new Float32Array(this.maxCells*4*2),this.renderTexCoords=new Float32Array(this.maxCells*4*2),this.renderColorIndices=new Float32Array(this.maxCells*4),this.useUint16Indices?this.renderIndices=new Uint16Array(this.maxCells*6):this.renderIndices=new Uint32Array(this.maxCells*6);let d=this.gl;this.useInstancing||(d.bindBuffer(d.ARRAY_BUFFER,this.positionBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderPositions.byteLength,d.DYNAMIC_DRAW)),d.bindBuffer(d.ARRAY_BUFFER,this.texCoordBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderTexCoords.byteLength,d.DYNAMIC_DRAW),d.bindBuffer(d.ARRAY_BUFFER,this.colorIndexBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderColorIndices.byteLength,d.DYNAMIC_DRAW),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,this.indexBuffer),d.bufferData(d.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,d.DYNAMIC_DRAW),this.vaoExtension&&this.vao&&(this.vaoExtension.deleteVertexArrayOES(this.vao),this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),d.bindBuffer(d.ARRAY_BUFFER,this.positionBuffer),d.enableVertexAttribArray(this.aPosition),d.vertexAttribPointer(this.aPosition,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,this.texCoordBuffer),d.enableVertexAttribArray(this.aTexCoord),d.vertexAttribPointer(this.aTexCoord,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,this.colorIndexBuffer),d.enableVertexAttribArray(this.aColorIndex),d.vertexAttribPointer(this.aColorIndex,1,d.FLOAT,!1,0,0),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null)));let f=o?"\u{1F4C8} Growing":"\u{1F4C9} Shrinking",u=c*160/1048576,m=this.maxCells*160/1048576;console.warn(`[TerminalGL] ${f} buffers: ${u.toFixed(2)}MB \u2192 ${m.toFixed(2)}MB (${c} \u2192 ${this.maxCells} quads)`)}this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.ambientEffectEnabled&&this.ambientEffectCanvas&&(this.ambientEffectCanvas.width=this.canvas.width,this.ambientEffectCanvas.height=this.canvas.height,this.ambientEffectCanvas.style.width=`${this.canvas.width}px`,this.ambientEffectCanvas.style.height=`${this.canvas.height}px`),this.showGrid&&this.gridOverlay&&this.updateGridOverlay(),this.updateCanvasSize()}getCanvas(){return this.canvas}getGridSize(){return{cols:this.cols,rows:this.rows}}getCellWidth(){return this.cellWidth}getCellHeight(){return this.cellHeight}getCurrentScale(){return this.currentScale}getBaseDimensions(){return{width:this.cols*this.cellWidth,height:this.rows*this.cellHeight}}setOnResizeCallback(t){this.onResizeCallback=t}clearOnResizeCallback(){this.onResizeCallback=void 0}setScalingMode(t){this.scalingMode!==t&&(this.scalingMode=t,this.updateCanvasSize())}getScalingMode(){return this.scalingMode}setGrid(t){t.enabled?(this.gridOverlay?this.gridOverlay.setStyle({strokeColor:t.color,lineWidth:t.lineWidth}):this.gridOverlay=new T(this.containerDiv,{strokeColor:t.color??"rgba(144, 24, 24, 1)",lineWidth:t.lineWidth??1,zIndex:10}),this.showGrid=!0,this.gridOverlay.setVisible(!0),this.updateGridOverlay()):(this.showGrid=!1,this.gridOverlay&&this.gridOverlay.setVisible(!1))}isGridEnabled(){return this.showGrid}setAmbientEffect(t){typeof t=="boolean"?this.ambientEffectEnabled=t:(this.ambientEffectEnabled=!0,t.blur!==void 0&&(this.ambientEffectBlur=t.blur),t.scale!==void 0&&(this.ambientEffectScale=t.scale)),this.ambientEffectEnabled&&!this.ambientEffectCanvas&&this.createAmbientEffectCanvas(),this.ambientEffectCanvas&&(this.ambientEffectEnabled?(this.ambientEffectCanvas.style.display="block",this.ambientEffectCanvas.style.filter=`blur(${this.ambientEffectBlur}px)`,this.ambientEffectCanvas.style.transform=`scale(${this.ambientEffectScale})`,this.updateAmbientEffect()):this.ambientEffectCanvas.style.display="none")}createAmbientEffectCanvas(){this.ambientEffectCanvas||(this.ambientEffectCanvas=document.createElement("canvas"),this.ambientEffectCanvas.className="terminalgl-ambient-effect-canvas",this.ambientEffectCanvas.width=this.canvas.width,this.ambientEffectCanvas.height=this.canvas.height,this.ambientEffectCanvas.style.cssText=`
|
|
115
117
|
display: block !important;
|
|
116
118
|
position: absolute !important;
|
|
117
119
|
width: ${this.canvas.width}px !important;
|
|
@@ -122,7 +124,33 @@
|
|
|
122
124
|
transform: scale(${this.ambientEffectScale}) !important;
|
|
123
125
|
pointer-events: none !important;
|
|
124
126
|
z-index: 0 !important;
|
|
125
|
-
`,this.ambientEffectCtx=this.ambientEffectCanvas.getContext("2d",{alpha:!0}),this.containerDiv.insertBefore(this.ambientEffectCanvas,this.canvas),console.warn("[TerminalGL] \u{1F308} ambient effect canvas created dynamically"))}isAmbientEffectEnabled(){return this.ambientEffectEnabled}getAmbientEffectConfig(){return{enabled:this.ambientEffectEnabled,blur:this.ambientEffectBlur,scale:this.ambientEffectScale}}getCols(){return this.cols}getRows(){return this.rows}isReady(){return!!(this.bitmapFont&&this.atlasTexture&&this.program)}destroy(){this.dispose()}dispose(){this.resizeObserver&&this.resizeObserver.disconnect();let t=this.gl;this.program&&t.deleteProgram(this.program),this.positionBuffer&&t.deleteBuffer(this.positionBuffer),this.texCoordBuffer&&t.deleteBuffer(this.texCoordBuffer),this.colorIndexBuffer&&t.deleteBuffer(this.colorIndexBuffer),this.indexBuffer&&t.deleteBuffer(this.indexBuffer),this.instanceDataBuffer&&t.deleteBuffer(this.instanceDataBuffer),this.atlasTexture&&t.deleteTexture(this.atlasTexture),this.paletteTexture&&t.deleteTexture(this.paletteTexture),this.vao&&this.vaoExtension&&this.vaoExtension.deleteVertexArrayOES(this.vao);let e=t.getExtension("WEBGL_lose_context");e&&e.loseContext(),this.atlasCanvas&&(this.atlasCanvas.width=0,this.atlasCanvas.height=0,this.atlasCanvas=void 0),this.containerDiv.parentElement&&this.containerDiv.parentElement.removeChild(this.containerDiv),this.canvas.width=0,this.canvas.height=0,this.ambientEffectCanvas&&(this.ambientEffectCanvas.width=0,this.ambientEffectCanvas.height=0,this.ambientEffectCanvas=null),this.gridOverlay&&(this.gridOverlay.destroy(),this.gridOverlay=void 0),this.renderPositions=new Float32Array(0),this.renderTexCoords=new Float32Array(0),this.renderColorIndices=new Float32Array(0),this.renderIndices=new Uint32Array(0),this.paletteFloat=new Float32Array(0),this.atlasUVs=new Float32Array(0),this.instanceData=new Float32Array(0),this.templateQuadPositions=new Float32Array(0),this.templateQuadIndices=new Uint16Array(0)}};E(F,"TerminalGL");var M=F;var P=class P{constructor(t,e,s,i,r){n(this,"atlases");n(this,"charMap");n(this,"baseCharWidth");n(this,"baseCharHeight");n(this,"baseCellWidth");n(this,"baseCellHeight");n(this,"atlasColumns",16);n(this,"font");n(this,"SCALES",[1,2,4,8]);n(this,"colorCache",new Map);n(this,"MAX_CACHE_SIZE",512);this.font=t,this.baseCharWidth=e,this.baseCharHeight=s,this.baseCellWidth=i??e,this.baseCellHeight=r??s,this.charMap=new Map,this.atlases=new Map,this.generateAtlases()}generateAtlases(){this.atlasColumns=16;let t=0;for(let[e,s]of this.font){let i=t%this.atlasColumns,r=Math.floor(t/this.atlasColumns);this.charMap.set(e,{x:i*this.baseCharWidth,y:r*this.baseCharHeight});for(let a of this.SCALES){let l=this.getOrCreateAtlas(a),o=i*l.charWidth,h=r*l.charHeight;this.renderBitmapToAtlas(l,s,o,h)}t++}}getOrCreateAtlas(t){let e=this.atlases.get(t);if(!e){let s=this.baseCharWidth*t,i=this.baseCharHeight*t,a=Math.ceil(256/this.atlasColumns),l=document.createElement("canvas");l.width=this.atlasColumns*s,l.height=a*i;let o=l.getContext("2d",{alpha:!0,willReadFrequently:!1});if(!o)throw new Error(`Impossible de cr\xE9er le contexte 2D pour l'atlas ${t}x`);o.imageSmoothingEnabled=!1,e={canvas:l,ctx:o,scale:t,charWidth:s,charHeight:i},this.atlases.set(t,e)}return e}renderBitmapToAtlas(t,e,s,i){let r=t.scale,a=t.ctx;a.fillStyle="#ffffff";for(let l=0;l<Math.min(e.length,this.baseCharHeight);l++){let o=e[l];for(let h=0;h<this.baseCharWidth;h++){let c=1<<7-h;o&c&&a.fillRect(s+h*r,i+l*r,r,r)}}}drawChar(t,e,s,i,r,a,l){let o=this.charMap.get(e);if(!o)return;let h=`${e}:${l}`,c=this.colorCache.get(h);if(c)c.lastUsed=performance.now();else{let x=this.createColoredGlyph(e,l,o);if(!x)return;c={canvas:x,lastUsed:performance.now()},this.colorCache.set(h,c),this.colorCache.size>this.MAX_CACHE_SIZE&&this.evictLRU()}let d=r/this.baseCellWidth,f=a/this.baseCellHeight,u=(this.baseCellWidth-this.baseCharWidth)/2*d,m=(this.baseCellHeight-this.baseCharHeight)/2*f,b=this.baseCharWidth*d,g=this.baseCharHeight*f;t.drawImage(c.canvas,s+u,i+m,b,g)}createColoredGlyph(t,e,s){let i=this.atlases.get(1);if(!i)return null;let r=i.ctx.getImageData(s.x,s.y,this.baseCharWidth,this.baseCharHeight),a=r.data,l=this.hexToRgb(e);for(let c=0;c<a.length;c+=4)a[c+3]>0&&(a[c]=l.r,a[c+1]=l.g,a[c+2]=l.b);let o=document.createElement("canvas");o.width=this.baseCharWidth,o.height=this.baseCharHeight;let h=o.getContext("2d",{alpha:!0});return h?(h.imageSmoothingEnabled=!1,h.putImageData(r,0,0),o):null}hexToRgb(t){if(t.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return a?{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16)}:{r:255,g:255,b:255}}let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/.exec(t);if(e)return{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)};let i={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],yellow:[255,255,0],cyan:[0,255,255],magenta:[255,0,255]}[t.toLowerCase()];return i?{r:i[0],g:i[1],b:i[2]}:{r:255,g:255,b:255}}evictLRU(){let t=null,e=1/0;for(let[s,i]of this.colorCache)i.lastUsed<e&&(e=i.lastUsed,t=s);if(t){let s=this.colorCache.get(t);s&&(s.canvas.width=0,s.canvas.height=0),this.colorCache.delete(t)}}getAtlasCanvas(t=1){return this.atlases.get(t)?.canvas}getAllAtlases(){return this.atlases}getCharDimensions(){return{width:this.baseCharWidth,height:this.baseCharHeight}}getCharCount(){return this.charMap.size}hasChar(t){return this.charMap.has(t)}getAtlasDimensions(t=1){let e=this.atlases.get(t);if(e)return{width:e.canvas.width,height:e.canvas.height}}toDataURL(t=1,e="image/png"){return this.atlases.get(t)?.canvas.toDataURL(e)}getCacheStats(){return{size:this.colorCache.size,maxSize:this.MAX_CACHE_SIZE}}clearCache(){for(let t of this.colorCache.values())t.canvas.width=0,t.canvas.height=0;this.colorCache.clear()}destroy(){this.charMap.clear();for(let t of this.atlases.values())t.canvas.width=0,t.canvas.height=0;this.atlases.clear(),this.clearCache()}};E(P,"BitmapFontAtlas");var B=P;var O=class O{constructor(t,e={}){n(this,"canvas");n(this,"ctx");n(this,"parentElement");n(this,"cells");n(this,"cols",0);n(this,"rows",0);n(this,"fontSize");n(this,"fontFamily");n(this,"defaultFgColor");n(this,"defaultBgColor");n(this,"canvasBgColor");n(this,"cellWidth");n(this,"cellHeight");n(this,"offsetX",0);n(this,"offsetY",0);n(this,"fontType","web");n(this,"bitmapFont");n(this,"bitmapAtlas");n(this,"bitmapCharWidth",8);n(this,"bitmapCharHeight",8);n(this,"showDebugGrid");n(this,"debugGridColor");n(this,"gridOverlay");n(this,"fixedGridMode");n(this,"fixedCols");n(this,"fixedRows");n(this,"cellAspectRatio");n(this,"resizeObserver");n(this,"imageDataBuffer");n(this,"useImageDataRendering",!1);n(this,"paletteCache");if(!t)throw new Error("Render: L'\xE9l\xE9ment parent est requis");this.parentElement=t,this.fixedGridMode=!!(e.fixedCols&&e.fixedRows),this.fixedCols=e.fixedCols,this.fixedRows=e.fixedRows,this.cellAspectRatio=e.cellAspectRatio??10/14,this.cellWidth=e.cellWidth??10,this.cellHeight=e.cellHeight??14,this.fontSize=e.fontSize??12,this.fontFamily=e.fontFamily??"monospace",this.defaultFgColor=e.defaultFgColor??"#ffffff",this.defaultBgColor=e.defaultBgColor??"#000000",this.canvasBgColor=e.canvasBgColor!==void 0?e.canvasBgColor:null,this.showDebugGrid=e.showDebugGrid??!1,this.debugGridColor=e.debugGridColor??"rgba(144, 24, 24, 1)",this.canvas=document.createElement("canvas"),e.className&&(this.canvas.className=e.className),e.style&&Object.assign(this.canvas.style,e.style),this.canvas.style.imageRendering="auto",this.parentElement.appendChild(this.canvas);let s=this.canvas.getContext("2d",{alpha:!0,desynchronized:!1});if(!s)throw new Error("UTSPRender: Impossible d'obtenir le contexte 2D du canvas");this.ctx=s,this.calculateGridSize(),this.cells=this.createEmptyGrid(),this.showDebugGrid&&(this.gridOverlay=new T(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,zIndex:10})),this.enableAutoResize(),this.render()}calculateGridSize(){let t=this.parentElement.clientWidth||800,e=this.parentElement.clientHeight||600;if(this.fixedGridMode&&this.fixedCols&&this.fixedRows)if(this.cols=this.fixedCols,this.rows=this.fixedRows,this.fontType==="bitmap"){let a=t/this.cols,l=e/this.rows,o=this.bitmapCharWidth/this.bitmapCharHeight;a/l>o?(this.cellHeight=Math.floor(l),this.cellWidth=Math.floor(this.cellHeight*o)):(this.cellWidth=Math.floor(a),this.cellHeight=Math.floor(this.cellWidth/o))}else{let a=t/this.cols,l=e/this.rows;a/l>this.cellAspectRatio?(this.cellHeight=Math.floor(l*100)/100,this.cellWidth=Math.floor(this.cellHeight*this.cellAspectRatio*100)/100):(this.cellWidth=Math.floor(a*100)/100,this.cellHeight=Math.floor(this.cellWidth/this.cellAspectRatio*100)/100);let o=14,h=12;this.fontSize=Math.floor(this.cellHeight/o*h*100)/100}else this.cols=Math.floor(t/this.cellWidth),this.rows=Math.floor(e/this.cellHeight);let s=this.cols*this.cellWidth,i=this.rows*this.cellHeight;this.offsetX=Math.floor((t-s)/2),this.offsetY=Math.floor((e-i)/2);let r=window.devicePixelRatio||1;this.canvas.style.width=`${t}px`,this.canvas.style.height=`${e}px`,this.canvas.width=t*r,this.canvas.height=e*r,this.ctx.scale(r,r)}createEmptyGrid(){let t=[];for(let e=0;e<this.rows;e++){let s=[];for(let i=0;i<this.cols;i++)s.push({char:" ",fgColor:this.defaultFgColor,bgColor:this.defaultBgColor});t.push(s)}return t}enableAutoResize(){this.resizeObserver=new ResizeObserver(()=>{let t=this.cols,e=this.rows;if(this.calculateGridSize(),this.cols!==t||this.rows!==e){let s=this.cells;this.cells=this.createEmptyGrid();let i=Math.min(t,this.cols),r=Math.min(e,this.rows);for(let a=0;a<r;a++)for(let l=0;l<i;l++)this.cells[a][l]=s[a][l]}this.render()}),this.resizeObserver.observe(this.parentElement)}setCell(t,e,s,i,r){if(e<0||e>=this.rows||t<0||t>=this.cols)return;let a=s&&typeof s=="string"?s.charAt(0):" ";this.cells[e][t]={char:a,fgColor:i??this.defaultFgColor,bgColor:r??this.defaultBgColor}}getCell(t,e){return e<0||e>=this.rows||t<0||t>=this.cols?null:this.cells[e][t]}write(t,e,s,i,r){for(let a=0;a<s.length;a++)this.setCell(t+a,e,s[a],i,r)}fillRect(t,e,s,i,r=" ",a,l){for(let o=e;o<e+i;o++)for(let h=t;h<t+s;h++)this.setCell(h,o,r,a,l)}clear(){this.cells=this.createEmptyGrid()}setFromArray(t){let e=t.width*t.height;if(t.cells.length!==e)throw new Error(`Invalid array length: expected ${e} (${t.width}\xD7${t.height}), got ${t.cells.length}`);let s=0;for(let i=0;i<t.height;i++)for(let r=0;r<t.width;r++){let a=t.cells[s];i<this.rows&&r<this.cols&&this.setCell(r,i,a.char,a.fgColor,a.bgColor),s++}}render(){if(this.fontType==="bitmap"&&this.bitmapFont&&this.useImageDataRendering){this.renderWithImageData();return}this.renderClassic()}renderWithImageData(){if(!this.bitmapFont)return;let t=this.cols*this.bitmapCharWidth,e=this.rows*this.bitmapCharHeight;(!this.imageDataBuffer||this.imageDataBuffer.width!==t||this.imageDataBuffer.height!==e)&&(this.imageDataBuffer=this.ctx.createImageData(t,e));let s=this.imageDataBuffer.data;for(let o=0;o<this.rows;o++)for(let h=0;h<this.cols;h++){let c=this.cells[o][h],d=this.parseColorToRGB(c.bgColor),f=this.parseColorToRGB(c.fgColor),u=c.char.charCodeAt(0),m=this.bitmapFont.get(u);for(let b=0;b<this.bitmapCharHeight;b++)for(let g=0;g<this.bitmapCharWidth;g++){let x=h*this.bitmapCharWidth+g,p=((o*this.bitmapCharHeight+b)*t+x)*4,w=!1;if(m&&b<m.length){let A=m[b],C=1<<7-g;w=(A&C)!==0}w?(s[p]=f.r,s[p+1]=f.g,s[p+2]=f.b,s[p+3]=f.a):(s[p]=d.r,s[p+1]=d.g,s[p+2]=d.b,s[p+3]=d.a)}}this.canvasBgColor!==null?(this.ctx.fillStyle=this.canvasBgColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.imageSmoothingEnabled=!1,this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges";let i=document.createElement("canvas");i.width=t,i.height=e,i.getContext("2d").putImageData(this.imageDataBuffer,0,0);let a=this.cols*this.cellWidth,l=this.rows*this.cellHeight;this.ctx.drawImage(i,0,0,t,e,this.offsetX,this.offsetY,a,l),this.showDebugGrid&&this.drawDebugGrid()}parseColorToRGB(t){let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/.exec(t);if(e)return{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10),a:e[4]?Math.round(parseFloat(e[4])*255):255};if(t.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);if(a)return{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16),a:255}}let i={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],yellow:[255,255,0],cyan:[0,255,255],magenta:[255,0,255],transparent:[0,0,0]}[t.toLowerCase()];return i?{r:i[0],g:i[1],b:i[2],a:t==="transparent"?0:255}:{r:255,g:255,b:255,a:255}}renderClassic(){this.canvasBgColor!==null?(this.ctx.fillStyle=this.canvasBgColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.fontType==="bitmap"?(this.ctx.imageSmoothingEnabled=!1,this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges"):(this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.canvas.style.imageRendering="auto",this.ctx.font=`${this.fontSize}px ${this.fontFamily}`,this.ctx.textBaseline="top",this.ctx.textRendering="optimizeLegibility");for(let t=0;t<this.rows;t++)for(let e=0;e<this.cols;e++){let s=this.cells[t][e],i=Math.floor(this.offsetX+e*this.cellWidth),r=Math.floor(this.offsetY+t*this.cellHeight),a=Math.floor(this.offsetX+(e+1)*this.cellWidth),l=Math.floor(this.offsetY+(t+1)*this.cellHeight),o=a-i,h=l-r;if((this.canvasBgColor!==null||s.bgColor!==this.defaultBgColor)&&(this.ctx.fillStyle=s.bgColor,this.ctx.fillRect(i,r,o,h)),s.char!==" ")if(this.fontType==="bitmap"&&this.bitmapFont)this.drawBitmapChar(s.char,i,r,o,h,s.fgColor);else{this.ctx.fillStyle=s.fgColor;let d=i+(o-this.ctx.measureText(s.char).width)/2,f=r+(h-this.fontSize)/2;this.ctx.fillText(s.char,d,f)}}this.showDebugGrid&&this.drawDebugGrid()}drawBitmapChar(t,e,s,i,r,a){let l=t.charCodeAt(0);if(this.bitmapAtlas){this.bitmapAtlas.drawChar(this.ctx,l,e,s,i,r,a);return}if(!this.bitmapFont)return;let o=this.bitmapFont.get(l);if(!o)return;let h=i/this.bitmapCharWidth,c=r/this.bitmapCharHeight;this.ctx.fillStyle=a;for(let d=0;d<Math.min(o.length,this.bitmapCharHeight);d++){let f=o[d],u=s+d*c,m=s+(d+1)*c;for(let b=0;b<this.bitmapCharWidth;b++){let g=1<<7-b;if(f&g){let x=e+b*h,R=e+(b+1)*h;this.ctx.fillRect(x,u,R-x,m-u)}}}}drawDebugGrid(){if(!this.gridOverlay)return;let t=this.parentElement.clientWidth||800,e=this.parentElement.clientHeight||600;this.gridOverlay.update(this.cols,this.rows,this.cellWidth,this.cellHeight,t,e,this.offsetX,this.offsetY)}getCanvas(){return this.canvas}getContext(){return this.ctx}getDimensions(){return{cols:this.cols,rows:this.rows}}getCellDimensions(){return{cellWidth:this.cellWidth,cellHeight:this.cellHeight}}getCellWidth(){return this.cellWidth}getCellHeight(){return this.cellHeight}getOffsets(){return{offsetX:this.offsetX,offsetY:this.offsetY}}setDebugGrid(t){this.showDebugGrid=t,t&&!this.gridOverlay?(this.gridOverlay=new T(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,zIndex:10}),this.drawDebugGrid()):!t&&this.gridOverlay?(this.gridOverlay.destroy(),this.gridOverlay=void 0):t&&this.gridOverlay&&this.gridOverlay.setVisible(!0)}setCanvasBackgroundColor(t){this.canvasBgColor=t}getCanvasBackgroundColor(){return this.canvasBgColor}setFixedGrid(t,e,s){this.fixedGridMode=!0,this.fixedCols=t,this.fixedRows=e,s!==void 0&&(this.cellAspectRatio=s);let i=this.cols,r=this.rows,a=this.cells;if(this.calculateGridSize(),this.cols!==i||this.rows!==r){this.cells=this.createEmptyGrid();let l=Math.min(i,this.cols),o=Math.min(r,this.rows);for(let h=0;h<o;h++)for(let c=0;c<l;c++)this.cells[h][c]=a[h][c]}this.render()}setAdaptiveGrid(t,e){this.fixedGridMode=!1,this.fixedCols=void 0,this.fixedRows=void 0,t!==void 0&&(this.cellWidth=t),e!==void 0&&(this.cellHeight=e);let s=this.cols,i=this.rows,r=this.cells;if(this.calculateGridSize(),this.cols!==s||this.rows!==i){this.cells=this.createEmptyGrid();let a=Math.min(s,this.cols),l=Math.min(i,this.rows);for(let o=0;o<l;o++)for(let h=0;h<a;h++)this.cells[o][h]=r[o][h]}this.render()}isFixedGridMode(){return this.fixedGridMode}setDebugGridColor(t){this.debugGridColor=t}isDebugGridEnabled(){return this.showDebugGrid}setImageDataRendering(t){this.useImageDataRendering=t,t&&this.fontType==="bitmap"?console.warn("[Render] ImageData rendering enabled (optimized for benchmarks)"):t&&(console.warn("[Render] ImageData rendering requires bitmap font, keeping classic mode"),this.useImageDataRendering=!1)}isImageDataRenderingEnabled(){return this.useImageDataRendering}setWebFont(t,e){this.fontType="web",this.fontFamily=t,e!==void 0&&(this.fontSize=e),this.bitmapFont=void 0,this.bitmapAtlas=void 0,this.useImageDataRendering=!1,this.calculateGridSize(),this.cells=this.createEmptyGrid()}setBitmapFont(t,e,s,i,r){this.fontType="bitmap",this.bitmapFont=t,this.bitmapCharWidth=e,this.bitmapCharHeight=s,this.bitmapAtlas=new B(t,e,s,i,r),this.cellWidth=i,this.cellHeight=r,this.calculateGridSize(),this.cells=this.createEmptyGrid()}getFontType(){return this.fontType}getBitmapFont(){return this.bitmapFont}getBitmapCharDimensions(){return this.fontType!=="bitmap"?null:{width:this.bitmapCharWidth,height:this.bitmapCharHeight}}setPalette(t){this.paletteCache=t}renderDisplayData(t){if(!t||!t.cells||t.cells.length===0){console.warn("[Terminal2D] Empty display data");return}if(t.width===0||t.height===0){console.warn("[Terminal2D] Invalid display dimensions:",t.width,t.height);return}(t.width!==this.cols||t.height!==this.rows)&&this.setFixedGrid(t.width,t.height,this.cellAspectRatio);let e=this.paletteCache??t.palette;if(!e||e.length===0){console.error("[Terminal2D] No palette available (neither cached nor in display)");return}let s=E(i=>{if(i==null||isNaN(i))return console.warn("[Terminal2D] Invalid palette index (undefined/null/NaN):",i),this.defaultFgColor;if(i===255)return"rgba(0, 0, 0, 0)";if(i<0||i>=e.length)return console.warn(`[Terminal2D] Invalid palette index: ${i}`),this.defaultFgColor;let r=e[i];return!r||typeof r.r!="number"?(console.warn(`[Terminal2D] Corrupted palette entry at index ${i}:`,r),this.defaultFgColor):`rgba(${r.r}, ${r.g}, ${r.b}, ${r.a/255})`},"convertColor");for(let i=0;i<t.height&&i<this.rows;i++)for(let r=0;r<t.width&&r<this.cols;r++){let a=i*t.width+r;if(a>=t.cells.length)break;let l=t.cells[a];if(!l)continue;let o=s(l.fgColorIndex),h=s(l.bgColorIndex);this.setCell(r,i,l.char??" ",o,h)}this.render()}isReady(){return!0}getCols(){return this.cols}getRows(){return this.rows}resize(t,e){this.setFixedGrid(t,e)}destroy(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=void 0),this.gridOverlay&&(this.gridOverlay.destroy(),this.gridOverlay=void 0),this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}};E(O,"Terminal2D");var L=O;var H=class H{constructor(t,e={}){n(this,"container");n(this,"overlayDiv",null);n(this,"button",null);n(this,"options");n(this,"started",!1);n(this,"onStartCallback");this.container=t,this.options={buttonText:e.buttonText??"Click to Start",onStart:e.onStart??(()=>{}),backgroundColor:e.backgroundColor??"rgba(0, 0, 0, 0.8)",buttonColor:e.buttonColor??"#4a90d9",buttonHoverColor:e.buttonHoverColor??"#357abd",buttonTextColor:e.buttonTextColor??"#ffffff",zIndex:e.zIndex??1e3},this.onStartCallback=e.onStart,this.createOverlay()}createOverlay(){let t=window.getComputedStyle(this.container).position;t!=="relative"&&t!=="absolute"&&t!=="fixed"&&(this.container.style.position="relative"),this.overlayDiv=document.createElement("div"),this.overlayDiv.className="utsp-autoplay-overlay",this.overlayDiv.style.cssText=`
|
|
127
|
+
`,this.ambientEffectCtx=this.ambientEffectCanvas.getContext("2d",{alpha:!0}),this.containerDiv.insertBefore(this.ambientEffectCanvas,this.canvas),console.warn("[TerminalGL] \u{1F308} ambient effect canvas created dynamically"))}isAmbientEffectEnabled(){return this.ambientEffectEnabled}getAmbientEffectConfig(){return{enabled:this.ambientEffectEnabled,blur:this.ambientEffectBlur,scale:this.ambientEffectScale}}getCols(){return this.cols}getRows(){return this.rows}isReady(){return!!(this.fontLoaded&&this.atlasTexture&&this.program)}destroy(){this.dispose()}dispose(){this.resizeObserver&&this.resizeObserver.disconnect();let t=this.gl;this.program&&t.deleteProgram(this.program),this.positionBuffer&&t.deleteBuffer(this.positionBuffer),this.texCoordBuffer&&t.deleteBuffer(this.texCoordBuffer),this.colorIndexBuffer&&t.deleteBuffer(this.colorIndexBuffer),this.indexBuffer&&t.deleteBuffer(this.indexBuffer),this.instanceDataBuffer&&t.deleteBuffer(this.instanceDataBuffer),this.atlasTexture&&t.deleteTexture(this.atlasTexture),this.paletteTexture&&t.deleteTexture(this.paletteTexture),this.vao&&this.vaoExtension&&this.vaoExtension.deleteVertexArrayOES(this.vao);let e=t.getExtension("WEBGL_lose_context");e&&e.loseContext(),this.atlasCanvas&&(this.atlasCanvas.width=0,this.atlasCanvas.height=0,this.atlasCanvas=void 0),this.containerDiv.parentElement&&this.containerDiv.parentElement.removeChild(this.containerDiv),this.canvas.width=0,this.canvas.height=0,this.ambientEffectCanvas&&(this.ambientEffectCanvas.width=0,this.ambientEffectCanvas.height=0,this.ambientEffectCanvas=null),this.gridOverlay&&(this.gridOverlay.destroy(),this.gridOverlay=void 0),this.renderPositions=new Float32Array(0),this.renderTexCoords=new Float32Array(0),this.renderColorIndices=new Float32Array(0),this.renderIndices=new Uint32Array(0),this.paletteFloat=new Float32Array(0),this.atlasUVs=new Float32Array(0),this.instanceData=new Float32Array(0),this.templateQuadPositions=new Float32Array(0),this.templateQuadIndices=new Uint16Array(0)}};C(W,"TerminalGL");var _=W;var $=class ${constructor(t,e,i,s,r){n(this,"atlases");n(this,"charMap");n(this,"baseCharWidth");n(this,"baseCharHeight");n(this,"baseCellWidth");n(this,"baseCellHeight");n(this,"atlasColumns",16);n(this,"font");n(this,"SCALES",[1,2,4,8]);n(this,"colorCache",new Map);n(this,"MAX_CACHE_SIZE",512);this.font=t,this.baseCharWidth=e,this.baseCharHeight=i,this.baseCellWidth=s??e,this.baseCellHeight=r??i,this.charMap=new Map,this.atlases=new Map,this.generateAtlases()}generateAtlases(){this.atlasColumns=16;let t=0;for(let[e,i]of this.font){let s=t%this.atlasColumns,r=Math.floor(t/this.atlasColumns);this.charMap.set(e,{x:s*this.baseCharWidth,y:r*this.baseCharHeight});for(let a of this.SCALES){let l=this.getOrCreateAtlas(a),o=s*l.charWidth,h=r*l.charHeight;this.renderBitmapToAtlas(l,i,o,h)}t++}}getOrCreateAtlas(t){let e=this.atlases.get(t);if(!e){let i=this.baseCharWidth*t,s=this.baseCharHeight*t,a=Math.ceil(256/this.atlasColumns),l=document.createElement("canvas");l.width=this.atlasColumns*i,l.height=a*s;let o=l.getContext("2d",{alpha:!0,willReadFrequently:!1});if(!o)throw new Error(`Impossible de cr\xE9er le contexte 2D pour l'atlas ${t}x`);o.imageSmoothingEnabled=!1,e={canvas:l,ctx:o,scale:t,charWidth:i,charHeight:s},this.atlases.set(t,e)}return e}renderBitmapToAtlas(t,e,i,s){let r=t.scale,a=t.ctx;a.fillStyle="#ffffff";for(let l=0;l<Math.min(e.length,this.baseCharHeight);l++){let o=e[l];for(let h=0;h<this.baseCharWidth;h++){let c=1<<7-h;o&c&&a.fillRect(i+h*r,s+l*r,r,r)}}}drawChar(t,e,i,s,r,a,l){let o=this.charMap.get(e);if(!o)return;let h=`${e}:${l}`,c=this.colorCache.get(h);if(c)c.lastUsed=performance.now();else{let x=this.createColoredGlyph(e,l,o);if(!x)return;c={canvas:x,lastUsed:performance.now()},this.colorCache.set(h,c),this.colorCache.size>this.MAX_CACHE_SIZE&&this.evictLRU()}let d=r/this.baseCellWidth,f=a/this.baseCellHeight,u=(this.baseCellWidth-this.baseCharWidth)/2*d,m=(this.baseCellHeight-this.baseCharHeight)/2*f,g=this.baseCharWidth*d,v=this.baseCharHeight*f;t.drawImage(c.canvas,i+u,s+m,g,v)}createColoredGlyph(t,e,i){let s=this.atlases.get(1);if(!s)return null;let r=s.ctx.getImageData(i.x,i.y,this.baseCharWidth,this.baseCharHeight),a=r.data,l=this.hexToRgb(e);for(let c=0;c<a.length;c+=4)a[c+3]>0&&(a[c]=l.r,a[c+1]=l.g,a[c+2]=l.b);let o=document.createElement("canvas");o.width=this.baseCharWidth,o.height=this.baseCharHeight;let h=o.getContext("2d",{alpha:!0});return h?(h.imageSmoothingEnabled=!1,h.putImageData(r,0,0),o):null}hexToRgb(t){if(t.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return a?{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16)}:{r:255,g:255,b:255}}let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/.exec(t);if(e)return{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)};let s={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],yellow:[255,255,0],cyan:[0,255,255],magenta:[255,0,255]}[t.toLowerCase()];return s?{r:s[0],g:s[1],b:s[2]}:{r:255,g:255,b:255}}evictLRU(){let t=null,e=1/0;for(let[i,s]of this.colorCache)s.lastUsed<e&&(e=s.lastUsed,t=i);if(t){let i=this.colorCache.get(t);i&&(i.canvas.width=0,i.canvas.height=0),this.colorCache.delete(t)}}getAtlasCanvas(t=1){return this.atlases.get(t)?.canvas}getAllAtlases(){return this.atlases}getCharDimensions(){return{width:this.baseCharWidth,height:this.baseCharHeight}}getCharCount(){return this.charMap.size}hasChar(t){return this.charMap.has(t)}getAtlasDimensions(t=1){let e=this.atlases.get(t);if(e)return{width:e.canvas.width,height:e.canvas.height}}toDataURL(t=1,e="image/png"){return this.atlases.get(t)?.canvas.toDataURL(e)}getCacheStats(){return{size:this.colorCache.size,maxSize:this.MAX_CACHE_SIZE}}clearCache(){for(let t of this.colorCache.values())t.canvas.width=0,t.canvas.height=0;this.colorCache.clear()}destroy(){this.charMap.clear();for(let t of this.atlases.values())t.canvas.width=0,t.canvas.height=0;this.atlases.clear(),this.clearCache()}};C($,"BitmapFontAtlas");var U=$;var N=class N{constructor(t){n(this,"atlasCanvas",null);n(this,"atlasCtx",null);n(this,"atlasImage",null);n(this,"glyphWidth");n(this,"glyphHeight");n(this,"cellWidth");n(this,"cellHeight");n(this,"atlasBlocks");n(this,"atlasColumns");n(this,"maxCharCode");n(this,"isLoaded",!1);n(this,"colorCache",new Map);n(this,"MAX_CACHE_SIZE",1024);this.glyphWidth=t.glyphWidth,this.glyphHeight=t.glyphHeight,this.cellWidth=t.cellWidth??t.glyphWidth,this.cellHeight=t.cellHeight??t.glyphHeight,this.atlasBlocks=t.atlasBlocks,this.atlasColumns=M(t.atlasBlocks),this.maxCharCode=F(t.atlasBlocks)}async loadFromPNG(t){return new Promise((e,i)=>{let s=new Uint8Array(t),r=new Blob([s],{type:"image/png"}),a=URL.createObjectURL(r),l=new Image;l.onload=()=>{URL.revokeObjectURL(a);let o=this.atlasColumns*this.glyphWidth,h=this.atlasColumns*this.glyphHeight;if((l.width!==o||l.height!==h)&&console.warn(`ImageFontAtlas: Image size ${l.width}\xD7${l.height} doesn't match expected ${o}\xD7${h} for ${this.atlasBlocks} block(s) with ${this.glyphWidth}\xD7${this.glyphHeight} glyphs`),this.atlasCanvas=document.createElement("canvas"),this.atlasCanvas.width=l.width,this.atlasCanvas.height=l.height,this.atlasCtx=this.atlasCanvas.getContext("2d",{alpha:!0,willReadFrequently:!0}),!this.atlasCtx){i(new Error("Failed to create 2D context for atlas"));return}this.atlasCtx.imageSmoothingEnabled=!1,this.atlasCtx.drawImage(l,0,0),this.atlasImage=l,this.isLoaded=!0,e()},l.onerror=()=>{URL.revokeObjectURL(a),i(new Error("Failed to load PNG image for atlas"))},l.src=a})}isReady(){return this.isLoaded}getGlyphPosition(t){if(t<0||t>this.maxCharCode)return null;let{col:e,row:i}=D(t,this.atlasBlocks);return{x:e*this.glyphWidth,y:i*this.glyphHeight}}getCharUV(t){if(t<0||t>this.maxCharCode)return null;let{col:e,row:i}=D(t,this.atlasBlocks),s=e/this.atlasColumns,r=i/this.atlasColumns,a=(e+1)/this.atlasColumns,l=(i+1)/this.atlasColumns;return{u1:s,v1:r,u2:a,v2:l}}drawChar(t,e,i,s,r,a,l){if(!this.isLoaded||!this.atlasCanvas)return;let o=this.getGlyphPosition(e);if(!o)return;let h=`${e}:${l}`,c=this.colorCache.get(h);if(c)c.lastUsed=performance.now();else{let f=this.createColoredGlyph(e,l,o);if(!f)return;c={canvas:f,lastUsed:performance.now()},this.colorCache.set(h,c),this.colorCache.size>this.MAX_CACHE_SIZE&&this.evictLRU()}if(t.imageSmoothingEnabled=!1,r===this.cellWidth&&a===this.cellHeight){let f=Math.floor((this.cellWidth-this.glyphWidth)/2),u=Math.floor((this.cellHeight-this.glyphHeight)/2);t.drawImage(c.canvas,Math.floor(i)+f,Math.floor(s)+u)}else{let f=r/this.cellWidth,u=a/this.cellHeight,m=Math.floor((this.cellWidth-this.glyphWidth)/2*f),g=Math.floor((this.cellHeight-this.glyphHeight)/2*u),v=Math.ceil(this.glyphWidth*f),x=Math.ceil(this.glyphHeight*u);t.drawImage(c.canvas,Math.floor(i)+m,Math.floor(s)+g,v,x)}}createColoredGlyph(t,e,i){if(!this.atlasCtx)return null;let s=this.atlasCtx.getImageData(i.x,i.y,this.glyphWidth,this.glyphHeight),r=s.data,a=this.parseColor(e);for(let h=0;h<r.length;h+=4)r[h+3]>0&&(r[h]=a.r,r[h+1]=a.g,r[h+2]=a.b);let l=document.createElement("canvas");l.width=this.glyphWidth,l.height=this.glyphHeight;let o=l.getContext("2d",{alpha:!0});return o?(o.putImageData(s,0,0),l):null}parseColor(t){if(t.startsWith("#")){let i=t.slice(1);return i.length===3&&(i=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]),{r:parseInt(i.slice(0,2),16),g:parseInt(i.slice(2,4),16),b:parseInt(i.slice(4,6),16)}}let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(t);return e?{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)}:{r:255,g:255,b:255}}evictLRU(){let t=null,e=1/0;for(let[i,s]of this.colorCache)s.lastUsed<e&&(e=s.lastUsed,t=i);if(t){let i=this.colorCache.get(t);i&&(i.canvas.width=0,i.canvas.height=0),this.colorCache.delete(t)}}getAtlasCanvas(){return this.atlasCanvas}getAtlasImage(){return this.atlasImage}getConfig(){return{glyphWidth:this.glyphWidth,glyphHeight:this.glyphHeight,cellWidth:this.cellWidth,cellHeight:this.cellHeight,atlasBlocks:this.atlasBlocks}}getGlyphDimensions(){return{width:this.glyphWidth,height:this.glyphHeight}}getCellDimensions(){return{width:this.cellWidth,height:this.cellHeight}}getAtlasDimensions(){return{width:this.atlasColumns*this.glyphWidth,height:this.atlasColumns*this.glyphHeight}}getAtlasColumns(){return this.atlasColumns}getMaxCharCode(){return this.maxCharCode}isValidCharCode(t){return t>=0&&t<=this.maxCharCode}clearCache(){for(let t of this.colorCache.values())t.canvas.width=0,t.canvas.height=0;this.colorCache.clear()}getCacheStats(){return{size:this.colorCache.size,maxSize:this.MAX_CACHE_SIZE}}destroy(){this.clearCache(),this.atlasCanvas&&(this.atlasCanvas.width=0,this.atlasCanvas.height=0,this.atlasCanvas=null),this.atlasCtx=null,this.atlasImage=null,this.isLoaded=!1}};C(N,"ImageFontAtlas");var S=N;var I=require("@utsp/types");var Y=class Y{constructor(t,e={}){n(this,"containerDiv");n(this,"canvas");n(this,"ctx");n(this,"parentElement");n(this,"cells");n(this,"cols",0);n(this,"rows",0);n(this,"fontSize");n(this,"fontFamily");n(this,"defaultFgColor");n(this,"defaultBgColor");n(this,"canvasBgColor");n(this,"cellWidth");n(this,"cellHeight");n(this,"offsetX",0);n(this,"offsetY",0);n(this,"fontType","web");n(this,"bitmapFont");n(this,"bitmapAtlas");n(this,"imageAtlas");n(this,"bitmapCharWidth",8);n(this,"bitmapCharHeight",8);n(this,"showDebugGrid");n(this,"debugGridColor");n(this,"gridOverlay");n(this,"fixedGridMode");n(this,"fixedCols");n(this,"fixedRows");n(this,"cellAspectRatio");n(this,"resizeObserver");n(this,"imageDataBuffer");n(this,"useImageDataRendering",!1);n(this,"paletteCache");n(this,"scalingMode",I.ScalingMode.None);n(this,"currentScale",1);if(!t)throw new Error("Render: L'\xE9l\xE9ment parent est requis");this.parentElement=t,this.fixedGridMode=!!(e.fixedCols&&e.fixedRows),this.fixedCols=e.fixedCols,this.fixedRows=e.fixedRows,this.cellAspectRatio=e.cellAspectRatio??10/14,this.cellWidth=e.cellWidth??10,this.cellHeight=e.cellHeight??14,this.fontSize=e.fontSize??12,this.fontFamily=e.fontFamily??"monospace",this.defaultFgColor=e.defaultFgColor??"#ffffff",this.defaultBgColor=e.defaultBgColor??"#000000",this.canvasBgColor=e.canvasBgColor!==void 0?e.canvasBgColor:null,this.showDebugGrid=e.showDebugGrid??!1,this.debugGridColor=e.debugGridColor??"rgba(144, 24, 24, 1)",this.scalingMode=e.scalingMode??I.ScalingMode.None,window.getComputedStyle(this.parentElement).position==="static"&&(this.parentElement.style.position="relative"),this.containerDiv=document.createElement("div"),this.containerDiv.className="terminal2d-container",this.containerDiv.style.cssText=`
|
|
128
|
+
position: absolute !important;
|
|
129
|
+
top: 0 !important;
|
|
130
|
+
left: 0 !important;
|
|
131
|
+
right: 0 !important;
|
|
132
|
+
bottom: 0 !important;
|
|
133
|
+
width: 100% !important;
|
|
134
|
+
height: 100% !important;
|
|
135
|
+
overflow: visible !important;
|
|
136
|
+
contain: layout style !important;
|
|
137
|
+
display: flex !important;
|
|
138
|
+
justify-content: center !important;
|
|
139
|
+
align-items: center !important;
|
|
140
|
+
isolation: isolate !important;
|
|
141
|
+
`,this.canvas=document.createElement("canvas"),this.canvas.className="terminal2d-canvas",e.className&&(this.canvas.className+=" "+e.className),e.style&&Object.assign(this.canvas.style,e.style),this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges",this.containerDiv.appendChild(this.canvas),this.parentElement.appendChild(this.containerDiv);let s=this.canvas.getContext("2d",{alpha:!0,desynchronized:!1});if(!s)throw new Error("UTSPRender: Impossible d'obtenir le contexte 2D du canvas");this.ctx=s,this.ctx.imageSmoothingEnabled=!1,this.calculateGridSize(),this.cells=this.createEmptyGrid(),this.showDebugGrid&&(this.gridOverlay=new T(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,zIndex:10})),this.enableAutoResize(),this.render()}calculateGridSize(){let t=this.parentElement.clientWidth||800,e=this.parentElement.clientHeight||600,i,s;if(this.fontType==="image"&&this.imageAtlas){let d=this.imageAtlas.getCellDimensions();i=d.width,s=d.height}else this.fontType==="bitmap"?(i=this.bitmapCharWidth,s=this.bitmapCharHeight):(i=Math.round(this.cellWidth)||10,s=Math.round(this.cellHeight)||14);this.cellWidth=Math.round(i),this.cellHeight=Math.round(s),this.fixedGridMode&&this.fixedCols&&this.fixedRows?(this.cols=this.fixedCols,this.rows=this.fixedRows):(this.cols=Math.floor(t/this.cellWidth)||1,this.rows=Math.floor(e/this.cellHeight)||1);let r=this.cols*this.cellWidth,a=this.rows*this.cellHeight,l=t/r,o=e/a,h=Math.min(l,o),c;switch(this.scalingMode){case I.ScalingMode.Integer:c=Math.max(1,Math.floor(h));break;case I.ScalingMode.Half:c=Math.max(.5,Math.floor(h*2)/2);break;case I.ScalingMode.Quarter:c=Math.max(.25,Math.floor(h*4)/4);break;case I.ScalingMode.Eighth:c=Math.max(.125,Math.floor(h*8)/8);break;case I.ScalingMode.None:default:c=Math.max(.1,h);break}this.currentScale=c,this.canvas.width=r,this.canvas.height=a,this.ctx.imageSmoothingEnabled=!1,this.canvas.style.cssText=`
|
|
142
|
+
flex-shrink: 0 !important;
|
|
143
|
+
flex-grow: 0 !important;
|
|
144
|
+
width: ${r}px !important;
|
|
145
|
+
height: ${a}px !important;
|
|
146
|
+
image-rendering: pixelated !important;
|
|
147
|
+
image-rendering: crisp-edges !important;
|
|
148
|
+
-ms-interpolation-mode: nearest-neighbor !important;
|
|
149
|
+
transform-origin: center center !important;
|
|
150
|
+
transform: scale(${c}) !important;
|
|
151
|
+
will-change: transform !important;
|
|
152
|
+
backface-visibility: hidden !important;
|
|
153
|
+
`,this.fontType==="web"&&(this.fontSize=Math.round(this.cellHeight/14*12)),this.offsetX=0,this.offsetY=0}createEmptyGrid(){let t=[];for(let e=0;e<this.rows;e++){let i=[];for(let s=0;s<this.cols;s++)i.push({char:" ",fgColor:this.defaultFgColor,bgColor:this.defaultBgColor});t.push(i)}return t}enableAutoResize(){this.resizeObserver=new ResizeObserver(()=>{let t=this.cols,e=this.rows;if(this.calculateGridSize(),this.cols!==t||this.rows!==e){let i=this.cells;this.cells=this.createEmptyGrid();let s=Math.min(t,this.cols),r=Math.min(e,this.rows);for(let a=0;a<r;a++)for(let l=0;l<s;l++)this.cells[a][l]=i[a][l]}this.render()}),this.resizeObserver.observe(this.parentElement)}setCell(t,e,i,s,r){if(e<0||e>=this.rows||t<0||t>=this.cols)return;let a=i&&typeof i=="string"?i.charAt(0):" ";this.cells[e][t]={char:a,fgColor:s??this.defaultFgColor,bgColor:r??this.defaultBgColor}}getCell(t,e){return e<0||e>=this.rows||t<0||t>=this.cols?null:this.cells[e][t]}write(t,e,i,s,r){for(let a=0;a<i.length;a++)this.setCell(t+a,e,i[a],s,r)}fillRect(t,e,i,s,r=" ",a,l){for(let o=e;o<e+s;o++)for(let h=t;h<t+i;h++)this.setCell(h,o,r,a,l)}clear(){this.cells=this.createEmptyGrid()}setFromArray(t){let e=t.width*t.height;if(t.cells.length!==e)throw new Error(`Invalid array length: expected ${e} (${t.width}\xD7${t.height}), got ${t.cells.length}`);let i=0;for(let s=0;s<t.height;s++)for(let r=0;r<t.width;r++){let a=t.cells[i];s<this.rows&&r<this.cols&&this.setCell(r,s,a.char,a.fgColor,a.bgColor),i++}}render(){if(this.fontType==="bitmap"&&this.bitmapFont&&this.useImageDataRendering){this.renderWithImageData();return}this.renderClassic()}renderWithImageData(){if(!this.bitmapFont)return;let t=this.cols*this.bitmapCharWidth,e=this.rows*this.bitmapCharHeight;(!this.imageDataBuffer||this.imageDataBuffer.width!==t||this.imageDataBuffer.height!==e)&&(this.imageDataBuffer=this.ctx.createImageData(t,e));let i=this.imageDataBuffer.data;for(let o=0;o<this.rows;o++)for(let h=0;h<this.cols;h++){let c=this.cells[o][h],d=this.parseColorToRGB(c.bgColor),f=this.parseColorToRGB(c.fgColor),u=c.char.charCodeAt(0),m=this.bitmapFont.get(u);for(let g=0;g<this.bitmapCharHeight;g++)for(let v=0;v<this.bitmapCharWidth;v++){let x=h*this.bitmapCharWidth+v,p=((o*this.bitmapCharHeight+g)*t+x)*4,w=!1;if(m&&g<m.length){let R=m[g],y=1<<7-v;w=(R&y)!==0}w?(i[p]=f.r,i[p+1]=f.g,i[p+2]=f.b,i[p+3]=f.a):(i[p]=d.r,i[p+1]=d.g,i[p+2]=d.b,i[p+3]=d.a)}}this.canvasBgColor!==null?(this.ctx.fillStyle=this.canvasBgColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.imageSmoothingEnabled=!1,this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges";let s=document.createElement("canvas");s.width=t,s.height=e,s.getContext("2d").putImageData(this.imageDataBuffer,0,0);let a=this.cols*this.cellWidth,l=this.rows*this.cellHeight;this.ctx.drawImage(s,0,0,t,e,this.offsetX,this.offsetY,a,l),this.showDebugGrid&&this.drawDebugGrid()}parseColorToRGB(t){let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/.exec(t);if(e)return{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10),a:e[4]?Math.round(parseFloat(e[4])*255):255};if(t.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);if(a)return{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16),a:255}}let s={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],yellow:[255,255,0],cyan:[0,255,255],magenta:[255,0,255],transparent:[0,0,0]}[t.toLowerCase()];return s?{r:s[0],g:s[1],b:s[2],a:t==="transparent"?0:255}:{r:255,g:255,b:255,a:255}}renderClassic(){this.canvasBgColor!==null?(this.ctx.fillStyle=this.canvasBgColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.fontType==="bitmap"||this.fontType==="image"?(this.ctx.imageSmoothingEnabled=!1,this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges"):(this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.canvas.style.imageRendering="auto",this.ctx.font=`${this.fontSize}px ${this.fontFamily}`,this.ctx.textBaseline="top",this.ctx.textRendering="optimizeLegibility");for(let t=0;t<this.rows;t++)for(let e=0;e<this.cols;e++){let i=this.cells[t][e],s=Math.floor(this.offsetX+e*this.cellWidth),r=Math.floor(this.offsetY+t*this.cellHeight),a=Math.floor(this.offsetX+(e+1)*this.cellWidth),l=Math.floor(this.offsetY+(t+1)*this.cellHeight),o=a-s,h=l-r;if((this.canvasBgColor!==null||i.bgColor!==this.defaultBgColor)&&(this.ctx.fillStyle=i.bgColor,this.ctx.fillRect(s,r,o,h)),i.char!==" ")if(this.fontType==="image"&&this.imageAtlas){let d=i.char.charCodeAt(0);this.imageAtlas.drawChar(this.ctx,d,s,r,o,h,i.fgColor)}else if(this.fontType==="bitmap"&&this.bitmapFont)this.drawBitmapChar(i.char,s,r,o,h,i.fgColor);else{this.ctx.fillStyle=i.fgColor;let d=s+(o-this.ctx.measureText(i.char).width)/2,f=r+(h-this.fontSize)/2;this.ctx.fillText(i.char,d,f)}}this.showDebugGrid&&this.drawDebugGrid()}drawBitmapChar(t,e,i,s,r,a){let l=t.charCodeAt(0);if(this.bitmapAtlas){this.bitmapAtlas.drawChar(this.ctx,l,e,i,s,r,a);return}if(!this.bitmapFont)return;let o=this.bitmapFont.get(l);if(!o)return;let h=s/this.bitmapCharWidth,c=r/this.bitmapCharHeight;this.ctx.fillStyle=a;for(let d=0;d<Math.min(o.length,this.bitmapCharHeight);d++){let f=o[d],u=i+d*c,m=i+(d+1)*c;for(let g=0;g<this.bitmapCharWidth;g++){let v=1<<7-g;if(f&v){let x=e+g*h,A=e+(g+1)*h;this.ctx.fillRect(x,u,A-x,m-u)}}}}drawDebugGrid(){if(!this.gridOverlay)return;let t=this.parentElement.clientWidth||800,e=this.parentElement.clientHeight||600;this.gridOverlay.update(this.cols,this.rows,this.cellWidth,this.cellHeight,t,e,this.offsetX,this.offsetY)}getCanvas(){return this.canvas}getContext(){return this.ctx}getDimensions(){return{cols:this.cols,rows:this.rows}}getCellDimensions(){return{cellWidth:this.cellWidth,cellHeight:this.cellHeight}}getCellWidth(){return this.cellWidth}getCellHeight(){return this.cellHeight}getCurrentScale(){return this.currentScale}getScalingMode(){return this.scalingMode}getOffsets(){return{offsetX:this.offsetX,offsetY:this.offsetY}}setDebugGrid(t){this.showDebugGrid=t,t&&!this.gridOverlay?(this.gridOverlay=new T(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,zIndex:10}),this.drawDebugGrid()):!t&&this.gridOverlay?(this.gridOverlay.destroy(),this.gridOverlay=void 0):t&&this.gridOverlay&&this.gridOverlay.setVisible(!0)}setCanvasBackgroundColor(t){this.canvasBgColor=t}getCanvasBackgroundColor(){return this.canvasBgColor}setFixedGrid(t,e,i){this.fixedGridMode=!0,this.fixedCols=t,this.fixedRows=e,i!==void 0&&(this.cellAspectRatio=i);let s=this.cols,r=this.rows,a=this.cells;if(this.calculateGridSize(),this.cols!==s||this.rows!==r){this.cells=this.createEmptyGrid();let l=Math.min(s,this.cols),o=Math.min(r,this.rows);for(let h=0;h<o;h++)for(let c=0;c<l;c++)this.cells[h][c]=a[h][c]}this.render()}setAdaptiveGrid(t,e){this.fixedGridMode=!1,this.fixedCols=void 0,this.fixedRows=void 0,t!==void 0&&(this.cellWidth=t),e!==void 0&&(this.cellHeight=e);let i=this.cols,s=this.rows,r=this.cells;if(this.calculateGridSize(),this.cols!==i||this.rows!==s){this.cells=this.createEmptyGrid();let a=Math.min(i,this.cols),l=Math.min(s,this.rows);for(let o=0;o<l;o++)for(let h=0;h<a;h++)this.cells[o][h]=r[o][h]}this.render()}isFixedGridMode(){return this.fixedGridMode}setDebugGridColor(t){this.debugGridColor=t}isDebugGridEnabled(){return this.showDebugGrid}setImageDataRendering(t){this.useImageDataRendering=t,t&&this.fontType==="bitmap"?console.warn("[Render] ImageData rendering enabled (optimized for benchmarks)"):t&&(console.warn("[Render] ImageData rendering requires bitmap font, keeping classic mode"),this.useImageDataRendering=!1)}isImageDataRenderingEnabled(){return this.useImageDataRendering}setWebFont(t,e){this.fontType="web",this.fontFamily=t,e!==void 0&&(this.fontSize=e),this.bitmapFont=void 0,this.bitmapAtlas=void 0,this.useImageDataRendering=!1,this.calculateGridSize(),this.cells=this.createEmptyGrid()}setBitmapFont(t,e,i,s,r){this.fontType="bitmap",this.bitmapFont=t,this.bitmapCharWidth=e,this.bitmapCharHeight=i,this.bitmapAtlas=new U(t,e,i,s,r),this.cellWidth=s,this.cellHeight=r,this.calculateGridSize(),this.cells=this.createEmptyGrid()}async setImageFont(t,e,i,s,r,a){this.fontType="image",this.imageAtlas=new S({glyphWidth:e,glyphHeight:i,cellWidth:s,cellHeight:r,atlasBlocks:a}),await this.imageAtlas.loadFromPNG(t),this.cellWidth=s,this.cellHeight=r,this.bitmapCharWidth=e,this.bitmapCharHeight=i,this.calculateGridSize(),this.cells=this.createEmptyGrid()}getFontType(){return this.fontType}getBitmapFont(){return this.bitmapFont}getBitmapCharDimensions(){return this.fontType!=="bitmap"?null:{width:this.bitmapCharWidth,height:this.bitmapCharHeight}}setPalette(t){this.paletteCache=t}renderDisplayData(t){if(!t||!t.cells||t.cells.length===0){console.warn("[Terminal2D] Empty display data");return}if(t.width===0||t.height===0){console.warn("[Terminal2D] Invalid display dimensions:",t.width,t.height);return}(t.width!==this.cols||t.height!==this.rows)&&this.setFixedGrid(t.width,t.height,this.cellAspectRatio);let e=this.paletteCache??t.palette;if(!e||e.length===0){console.error("[Terminal2D] No palette available (neither cached nor in display)");return}let i=C(s=>{if(s==null||isNaN(s))return console.warn("[Terminal2D] Invalid palette index (undefined/null/NaN):",s),this.defaultFgColor;if(s===255)return"rgba(0, 0, 0, 0)";if(s<0||s>=e.length)return console.warn(`[Terminal2D] Invalid palette index: ${s}`),this.defaultFgColor;let r=e[s];return!r||typeof r.r!="number"?(console.warn(`[Terminal2D] Corrupted palette entry at index ${s}:`,r),this.defaultFgColor):`rgba(${r.r}, ${r.g}, ${r.b}, ${r.a/255})`},"convertColor");for(let s=0;s<t.height&&s<this.rows;s++)for(let r=0;r<t.width&&r<this.cols;r++){let a=s*t.width+r;if(a>=t.cells.length)break;let l=t.cells[a];if(!l)continue;let o=i(l.fgColorIndex),h=i(l.bgColorIndex);this.setCell(r,s,l.char??" ",o,h)}this.render()}isReady(){return!0}getCols(){return this.cols}getRows(){return this.rows}resize(t,e){this.setFixedGrid(t,e)}destroy(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=void 0),this.gridOverlay&&(this.gridOverlay.destroy(),this.gridOverlay=void 0),this.containerDiv.parentElement&&this.containerDiv.parentElement.removeChild(this.containerDiv)}};C(Y,"Terminal2D");var P=Y;var V=class V{constructor(t,e={}){n(this,"container");n(this,"overlayDiv",null);n(this,"button",null);n(this,"options");n(this,"started",!1);n(this,"onStartCallback");this.container=t,this.options={buttonText:e.buttonText??"Click to Start",onStart:e.onStart??(()=>{}),backgroundColor:e.backgroundColor??"rgba(0, 0, 0, 0.8)",buttonColor:e.buttonColor??"#4a90d9",buttonHoverColor:e.buttonHoverColor??"#357abd",buttonTextColor:e.buttonTextColor??"#ffffff",zIndex:e.zIndex??1e3},this.onStartCallback=e.onStart,this.createOverlay()}createOverlay(){let t=window.getComputedStyle(this.container).position;t!=="relative"&&t!=="absolute"&&t!=="fixed"&&(this.container.style.position="relative"),this.overlayDiv=document.createElement("div"),this.overlayDiv.className="utsp-autoplay-overlay",this.overlayDiv.style.cssText=`
|
|
126
154
|
position: absolute !important;
|
|
127
155
|
top: 0 !important;
|
|
128
156
|
left: 0 !important;
|
|
@@ -146,7 +174,7 @@
|
|
|
146
174
|
cursor: pointer !important;
|
|
147
175
|
transition: background-color 0.2s ease, transform 0.1s ease !important;
|
|
148
176
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
|
|
149
|
-
`,this.button.addEventListener("mouseenter",()=>{this.button&&(this.button.style.backgroundColor=`${this.options.buttonHoverColor} !important`,this.button.style.transform="scale(1.05)")}),this.button.addEventListener("mouseleave",()=>{this.button&&(this.button.style.backgroundColor=`${this.options.buttonColor} !important`,this.button.style.transform="scale(1)")}),this.button.addEventListener("click",e=>{e.stopPropagation(),this.start()}),this.overlayDiv.addEventListener("click",()=>{this.start()}),this.overlayDiv.appendChild(this.button),this.container.appendChild(this.overlayDiv)}start(){this.started||(this.started=!0,this.overlayDiv&&(this.overlayDiv.style.transition="opacity 0.3s ease",this.overlayDiv.style.opacity="0",setTimeout(()=>{this.removeOverlay()},300)),this.onStartCallback&&this.onStartCallback())}isStarted(){return this.started}isVisible(){return this.overlayDiv!==null&&!this.started}removeOverlay(){this.overlayDiv&&this.overlayDiv.parentElement&&this.overlayDiv.parentElement.removeChild(this.overlayDiv),this.overlayDiv=null,this.button=null}setButtonText(t){this.options.buttonText=t,this.button&&(this.button.textContent=t)}setOnStart(t){this.onStartCallback=t}destroy(){this.removeOverlay(),this.started=!1}};
|
|
177
|
+
`,this.button.addEventListener("mouseenter",()=>{this.button&&(this.button.style.backgroundColor=`${this.options.buttonHoverColor} !important`,this.button.style.transform="scale(1.05)")}),this.button.addEventListener("mouseleave",()=>{this.button&&(this.button.style.backgroundColor=`${this.options.buttonColor} !important`,this.button.style.transform="scale(1)")}),this.button.addEventListener("click",e=>{e.stopPropagation(),this.start()}),this.overlayDiv.addEventListener("click",()=>{this.start()}),this.overlayDiv.appendChild(this.button),this.container.appendChild(this.overlayDiv)}start(){this.started||(this.started=!0,this.overlayDiv&&(this.overlayDiv.style.transition="opacity 0.3s ease",this.overlayDiv.style.opacity="0",setTimeout(()=>{this.removeOverlay()},300)),this.onStartCallback&&this.onStartCallback())}isStarted(){return this.started}isVisible(){return this.overlayDiv!==null&&!this.started}removeOverlay(){this.overlayDiv&&this.overlayDiv.parentElement&&this.overlayDiv.parentElement.removeChild(this.overlayDiv),this.overlayDiv=null,this.button=null}setButtonText(t){this.options.buttonText=t,this.button&&(this.button.textContent=t)}setOnStart(t){this.onStartCallback=t}destroy(){this.removeOverlay(),this.started=!1}};C(V,"AutoplayOverlay");var O=V;var G=require("@utsp/types");var X=class X{constructor(t,e){n(this,"canvas");n(this,"ctx");n(this,"containerDiv");n(this,"parentElement");n(this,"resizeObserver");n(this,"width",0);n(this,"height",0);n(this,"currentConfig",null);n(this,"configHash","");this.parentElement=t,this.containerDiv=document.createElement("div"),this.containerDiv.className="postprocess-container",this.containerDiv.style.cssText=`
|
|
150
178
|
position: absolute !important;
|
|
151
179
|
top: 0 !important;
|
|
152
180
|
left: 0 !important;
|
|
@@ -164,4 +192,4 @@
|
|
|
164
192
|
width: 100% !important;
|
|
165
193
|
height: 100% !important;
|
|
166
194
|
pointer-events: none !important;
|
|
167
|
-
`;let
|
|
195
|
+
`;let i=this.canvas.getContext("2d",{alpha:!0});if(!i)throw new Error("PostProcessOverlay: Failed to get 2D context");this.ctx=i,this.containerDiv.appendChild(this.canvas),this.parentElement.appendChild(this.containerDiv),this.resizeObserver=new ResizeObserver(r=>{for(let a of r){let{width:l,height:o}=a.contentRect;l>0&&o>0&&this.handleResize(Math.floor(l),Math.floor(o))}}),this.resizeObserver.observe(this.parentElement);let s=this.parentElement.getBoundingClientRect();s.width>0&&s.height>0&&this.handleResize(Math.floor(s.width),Math.floor(s.height)),this.hide()}handleResize(t,e){this.width===t&&this.height===e||(this.width=t,this.height=e,this.canvas.width=t,this.canvas.height=e,this.currentConfig&&this.isActive()&&this.render())}setConfig(t){let e=this.hashConfig(t);if(e!==this.configHash){if(this.currentConfig=t,this.configHash=e,!t||!this.isActiveConfig(t)){this.clear(),this.hide();return}this.show(),this.render()}}setScanlines(t){t===null?this.setConfig({scanlines:{enabled:!1}}):this.setConfig({scanlines:t})}setScanlinesEnabled(t){if(t){let e=this.currentConfig?.scanlines;this.setScanlines({enabled:!0,opacity:e?.opacity??G.POST_PROCESS_DEFAULTS.scanlines.opacity,pattern:e?.pattern??"horizontal",spacing:e?.spacing??2,thickness:e?.thickness??1,color:e?.color??{r:0,g:0,b:0}})}else this.setScanlines({enabled:!1})}setScanlinesOpacity(t){let e=Math.max(0,Math.min(1,t)),i=this.currentConfig?.scanlines;this.setScanlines({enabled:i?.enabled??!0,opacity:e,pattern:i?.pattern??"horizontal",spacing:i?.spacing??2,thickness:i?.thickness??1,color:i?.color??{r:0,g:0,b:0}})}setScanlinesPattern(t){let e=this.currentConfig?.scanlines;this.setScanlines({enabled:e?.enabled??!0,opacity:e?.opacity??G.POST_PROCESS_DEFAULTS.scanlines.opacity,pattern:t,spacing:e?.spacing??2,thickness:e?.thickness??1,color:e?.color??{r:0,g:0,b:0}})}setScanlinesSpacing(t){let e=Math.max(2,Math.round(t)),i=this.currentConfig?.scanlines;this.setScanlines({enabled:i?.enabled??!0,opacity:i?.opacity??G.POST_PROCESS_DEFAULTS.scanlines.opacity,pattern:i?.pattern??"horizontal",spacing:e,thickness:i?.thickness??1,color:i?.color??{r:0,g:0,b:0}})}getConfig(){return this.currentConfig}isActive(){return this.isActiveConfig(this.currentConfig)}destroy(){this.resizeObserver.disconnect(),this.containerDiv.parentElement&&this.containerDiv.parentElement.removeChild(this.containerDiv)}syncWithRenderer(t){}resize(t,e){}setCellDimensions(t,e){}show(){this.containerDiv.style.display="block"}hide(){this.containerDiv.style.display="none"}clear(){this.ctx.clearRect(0,0,this.width,this.height)}isActiveConfig(t){return t?.scanlines?.enabled??!1}render(){this.clear(),this.currentConfig?.scanlines?.enabled&&this.renderScanlines(this.currentConfig.scanlines)}renderScanlines(t){let e=t.opacity??G.POST_PROCESS_DEFAULTS.scanlines.opacity,i=t.pattern??"horizontal",s=t.spacing??2,r=t.thickness??1,a=t.color??{r:0,g:0,b:0},l=this.ctx;switch(l.fillStyle=`rgba(${a.r}, ${a.g}, ${a.b}, ${e})`,i){case"horizontal":for(let o=s-1;o<this.height;o+=s)l.fillRect(0,o,this.width,r);break;case"vertical":for(let o=s-1;o<this.width;o+=s)l.fillRect(o,0,r,this.height);break;case"grid":for(let o=s-1;o<this.height;o+=s)l.fillRect(0,o,this.width,r);for(let o=s-1;o<this.width;o+=s)l.fillRect(o,0,r,this.height);break}}hashConfig(t){return t?JSON.stringify(t):"null"}};C(X,"PostProcessOverlay");var k=X;var lt="0.1.0";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IRenderer, RGBColor, RenderState,
|
|
1
|
+
import { IRenderer, ScalingMode, RGBColor, RenderState, PostProcessConfig, ScanlinesConfig } from '@utsp/types';
|
|
2
2
|
export { IColorPalette, IRenderer, PostProcessConfig, RenderState, ScalingMode, ScanlinesConfig } from '@utsp/types';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -15,6 +15,56 @@ declare function paletteIndexToColor(index: number, palette?: readonly string[])
|
|
|
15
15
|
*/
|
|
16
16
|
declare function colorToPaletteIndex(color: string, palette?: readonly string[]): number;
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Atlas Utilities
|
|
20
|
+
*
|
|
21
|
+
* Shared utilities for block-based font atlas calculations.
|
|
22
|
+
* Used by both TerminalGL (WebGL) and ImageFontAtlas (Canvas2D).
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Number of 256-char blocks in an atlas
|
|
26
|
+
* - 1 block = 16×16 grid = 256 chars (8-bit)
|
|
27
|
+
* - 4 blocks = 32×32 grid = 1024 chars (10-bit)
|
|
28
|
+
* - 16 blocks = 64×64 grid = 4096 chars (12-bit)
|
|
29
|
+
*/
|
|
30
|
+
type AtlasBlocks = 1 | 4 | 16;
|
|
31
|
+
/**
|
|
32
|
+
* Get the number of columns in the atlas grid
|
|
33
|
+
* @param blocks - Number of 256-char blocks (1, 4, or 16)
|
|
34
|
+
* @returns Number of character columns (16, 32, or 64)
|
|
35
|
+
*/
|
|
36
|
+
declare function getAtlasColumns(blocks: AtlasBlocks): number;
|
|
37
|
+
/**
|
|
38
|
+
* Get the maximum valid charCode for a given atlas size
|
|
39
|
+
* @param blocks - Number of 256-char blocks (1, 4, or 16)
|
|
40
|
+
* @returns Maximum charCode (255, 1023, or 4095)
|
|
41
|
+
*/
|
|
42
|
+
declare function getMaxCharCode(blocks: AtlasBlocks): number;
|
|
43
|
+
/**
|
|
44
|
+
* Get character grid position in a block-based atlas
|
|
45
|
+
*
|
|
46
|
+
* Atlas layout for multi-block fonts:
|
|
47
|
+
* - Each block contains 256 chars in a 16×16 grid
|
|
48
|
+
* - Blocks are arranged in a square:
|
|
49
|
+
* - 1 block: 1×1 (16×16 chars total)
|
|
50
|
+
* - 4 blocks: 2×2 (32×32 chars total)
|
|
51
|
+
* - 16 blocks: 4×4 (64×64 chars total)
|
|
52
|
+
*
|
|
53
|
+
* CharCode mapping:
|
|
54
|
+
* charCode 0-255 → Block 0 (top-left)
|
|
55
|
+
* charCode 256-511 → Block 1 (top-right for 4+ blocks)
|
|
56
|
+
* charCode 512-767 → Block 2 (bottom-left for 4 blocks)
|
|
57
|
+
* etc.
|
|
58
|
+
*
|
|
59
|
+
* @param charCode - Character code (0 to maxCharCode)
|
|
60
|
+
* @param blocks - Number of 256-char blocks (1, 4, or 16)
|
|
61
|
+
* @returns Grid position { col, row } in the atlas
|
|
62
|
+
*/
|
|
63
|
+
declare function getCharGridPosition(charCode: number, blocks: AtlasBlocks): {
|
|
64
|
+
col: number;
|
|
65
|
+
row: number;
|
|
66
|
+
};
|
|
67
|
+
|
|
18
68
|
/**
|
|
19
69
|
* Représente une cellule du terminal
|
|
20
70
|
*/
|
|
@@ -95,6 +145,19 @@ interface RenderOptions {
|
|
|
95
145
|
fixedRows?: number;
|
|
96
146
|
/** Ratio largeur/hauteur de cellule pour mode fixedGrid (défaut: 10/14 ≈ 0.714) */
|
|
97
147
|
cellAspectRatio?: number;
|
|
148
|
+
/**
|
|
149
|
+
* Scaling mode for pixel-perfect rendering (default: ScalingMode.None)
|
|
150
|
+
*
|
|
151
|
+
* Controls how the canvas is scaled to fit the container:
|
|
152
|
+
* - ScalingMode.None: Scales cells to fill space, may have sub-pixel artifacts
|
|
153
|
+
* - ScalingMode.Eighth: Snaps to 0.125 increments (1.0, 1.125, 1.25...)
|
|
154
|
+
* - ScalingMode.Quarter: Snaps to 0.25 increments (1.0, 1.25, 1.5...)
|
|
155
|
+
* - ScalingMode.Half: Snaps to 0.5 increments (1.0, 1.5, 2.0...)
|
|
156
|
+
* - ScalingMode.Integer: Integer scaling only (1x, 2x, 3x...), crispest pixels
|
|
157
|
+
*
|
|
158
|
+
* Note: For ImageFont mode, this uses native glyph sizes + CSS transform scaling.
|
|
159
|
+
*/
|
|
160
|
+
scalingMode?: ScalingMode;
|
|
98
161
|
}
|
|
99
162
|
/**
|
|
100
163
|
* UTSP Render - Gère une grille de caractères avec couleurs
|
|
@@ -103,6 +166,7 @@ interface RenderOptions {
|
|
|
103
166
|
* Implements IRenderer interface for compatibility with TerminalGL and dependency injection.
|
|
104
167
|
*/
|
|
105
168
|
declare class Terminal2D implements IRenderer {
|
|
169
|
+
private containerDiv;
|
|
106
170
|
private canvas;
|
|
107
171
|
private ctx;
|
|
108
172
|
private parentElement;
|
|
@@ -121,6 +185,7 @@ declare class Terminal2D implements IRenderer {
|
|
|
121
185
|
private fontType;
|
|
122
186
|
private bitmapFont?;
|
|
123
187
|
private bitmapAtlas?;
|
|
188
|
+
private imageAtlas?;
|
|
124
189
|
private bitmapCharWidth;
|
|
125
190
|
private bitmapCharHeight;
|
|
126
191
|
private showDebugGrid;
|
|
@@ -134,6 +199,8 @@ declare class Terminal2D implements IRenderer {
|
|
|
134
199
|
private imageDataBuffer?;
|
|
135
200
|
private useImageDataRendering;
|
|
136
201
|
private paletteCache?;
|
|
202
|
+
private scalingMode;
|
|
203
|
+
private currentScale;
|
|
137
204
|
/**
|
|
138
205
|
* Crée une instance de UTSPRender
|
|
139
206
|
* @param parentDiv - L'élément HTML parent dans lequel créer le canvas
|
|
@@ -143,6 +210,12 @@ declare class Terminal2D implements IRenderer {
|
|
|
143
210
|
/**
|
|
144
211
|
* Calcule le nombre de colonnes et de lignes en fonction de la taille du parent
|
|
145
212
|
* et centre le terminal dans le canvas
|
|
213
|
+
*
|
|
214
|
+
* 🎯 PIXEL-PERFECT APPROACH (like TerminalGL):
|
|
215
|
+
* 1. Cell dimensions are ALWAYS integers (native font size)
|
|
216
|
+
* 2. Canvas renders at native resolution (cols * cellWidth)
|
|
217
|
+
* 3. CSS transform: scale() handles visual upscaling
|
|
218
|
+
* 4. Flexbox handles centering
|
|
146
219
|
*/
|
|
147
220
|
private calculateGridSize;
|
|
148
221
|
/**
|
|
@@ -272,6 +345,15 @@ declare class Terminal2D implements IRenderer {
|
|
|
272
345
|
* Obtient la hauteur d'une cellule
|
|
273
346
|
*/
|
|
274
347
|
getCellHeight(): number;
|
|
348
|
+
/**
|
|
349
|
+
* Get current scaling factor (for pixel-perfect modes)
|
|
350
|
+
* Returns 1 if scalingMode is None or not using ImageFont
|
|
351
|
+
*/
|
|
352
|
+
getCurrentScale(): number;
|
|
353
|
+
/**
|
|
354
|
+
* Get scaling mode
|
|
355
|
+
*/
|
|
356
|
+
getScalingMode(): ScalingMode;
|
|
275
357
|
/**
|
|
276
358
|
* Obtient les décalages de centrage du terminal
|
|
277
359
|
*/
|
|
@@ -348,10 +430,20 @@ declare class Terminal2D implements IRenderer {
|
|
|
348
430
|
* @param cellHeight - Hauteur de la cellule de rendu en pixels
|
|
349
431
|
*/
|
|
350
432
|
setBitmapFont(font: BitmapFont$1, charWidth: number, charHeight: number, cellWidth: number, cellHeight: number): void;
|
|
433
|
+
/**
|
|
434
|
+
* Configure une police image (atlas PNG)
|
|
435
|
+
* @param imageData - Données PNG brutes
|
|
436
|
+
* @param glyphWidth - Largeur du glyphe en pixels
|
|
437
|
+
* @param glyphHeight - Hauteur du glyphe en pixels
|
|
438
|
+
* @param cellWidth - Largeur de la cellule en pixels
|
|
439
|
+
* @param cellHeight - Hauteur de la cellule en pixels
|
|
440
|
+
* @param atlasBlocks - Nombre de blocs de 256 caractères (1, 4, ou 16)
|
|
441
|
+
*/
|
|
442
|
+
setImageFont(imageData: Uint8Array, glyphWidth: number, glyphHeight: number, cellWidth: number, cellHeight: number, atlasBlocks: AtlasBlocks): Promise<void>;
|
|
351
443
|
/**
|
|
352
444
|
* Retourne le type de police actuellement utilisé
|
|
353
445
|
*/
|
|
354
|
-
getFontType(): 'web' | 'bitmap';
|
|
446
|
+
getFontType(): 'web' | 'bitmap' | 'image';
|
|
355
447
|
/**
|
|
356
448
|
* Retourne la police bitmap actuelle (si applicable)
|
|
357
449
|
*/
|
|
@@ -526,6 +618,7 @@ declare class TerminalGL implements IRenderer {
|
|
|
526
618
|
private atlasTexture;
|
|
527
619
|
private atlasCanvas?;
|
|
528
620
|
private atlasColumns;
|
|
621
|
+
private fontLoaded;
|
|
529
622
|
private paletteTexture;
|
|
530
623
|
private program;
|
|
531
624
|
private positionBuffer;
|
|
@@ -645,6 +738,39 @@ declare class TerminalGL implements IRenderer {
|
|
|
645
738
|
* ```
|
|
646
739
|
*/
|
|
647
740
|
setBitmapFont(font: BitmapFont$1, charWidth: number, charHeight: number, cellWidth: number, cellHeight: number): void;
|
|
741
|
+
/**
|
|
742
|
+
* Set image font (PNG atlas) for extended character rendering
|
|
743
|
+
* Supports 1, 4, or 16 blocks (256, 1024, or 4096 characters)
|
|
744
|
+
*
|
|
745
|
+
* @param imageData - PNG image data as Uint8Array
|
|
746
|
+
* @param glyphWidth - Glyph width in pixels
|
|
747
|
+
* @param glyphHeight - Glyph height in pixels
|
|
748
|
+
* @param cellWidth - Cell width in pixels
|
|
749
|
+
* @param cellHeight - Cell height in pixels
|
|
750
|
+
* @param atlasBlocks - Number of 256-char blocks (1, 4, or 16)
|
|
751
|
+
*/
|
|
752
|
+
setImageFont(imageData: Uint8Array, glyphWidth: number, glyphHeight: number, cellWidth: number, cellHeight: number, atlasBlocks: AtlasBlocks): Promise<void>;
|
|
753
|
+
/**
|
|
754
|
+
* Load PNG image and create WebGL texture
|
|
755
|
+
*/
|
|
756
|
+
private loadImageFontAtlas;
|
|
757
|
+
/**
|
|
758
|
+
* Pre-compute UVs for ImageFont (block-based grid layout)
|
|
759
|
+
*
|
|
760
|
+
* Atlas layout for multi-block fonts:
|
|
761
|
+
* - Each block contains 256 chars in a 16×16 grid
|
|
762
|
+
* - Blocks are arranged visually:
|
|
763
|
+
* - 1 block: 1×1 (16×16 chars)
|
|
764
|
+
* - 4 blocks: 2×2 (32×32 chars total)
|
|
765
|
+
* - 16 blocks: 4×4 (64×64 chars total)
|
|
766
|
+
*
|
|
767
|
+
* CharCode mapping:
|
|
768
|
+
* charCode 0-255 → Block 0 (top-left)
|
|
769
|
+
* charCode 256-511 → Block 1 (top-right for 4 blocks)
|
|
770
|
+
* charCode 512-767 → Block 2 (bottom-left for 4 blocks)
|
|
771
|
+
* etc.
|
|
772
|
+
*/
|
|
773
|
+
private precomputeImageFontUVs;
|
|
648
774
|
/**
|
|
649
775
|
* Generate texture atlas from bitmap font
|
|
650
776
|
*/
|
|
@@ -1118,6 +1244,159 @@ declare class BitmapFontAtlas {
|
|
|
1118
1244
|
destroy(): void;
|
|
1119
1245
|
}
|
|
1120
1246
|
|
|
1247
|
+
/**
|
|
1248
|
+
* ImageFontAtlas - Atlas for PNG-based fonts with multi-block support
|
|
1249
|
+
*
|
|
1250
|
+
* Supports 1, 4, or 16 blocks of 256 characters each:
|
|
1251
|
+
* - 1 block = 16×16 grid = 256 chars (8-bit)
|
|
1252
|
+
* - 4 blocks = 32×32 grid = 1024 chars (10-bit)
|
|
1253
|
+
* - 16 blocks = 64×64 grid = 4096 chars (12-bit)
|
|
1254
|
+
*/
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* Configuration for ImageFontAtlas
|
|
1258
|
+
*/
|
|
1259
|
+
interface ImageFontAtlasConfig {
|
|
1260
|
+
glyphWidth: number;
|
|
1261
|
+
glyphHeight: number;
|
|
1262
|
+
cellWidth?: number;
|
|
1263
|
+
cellHeight?: number;
|
|
1264
|
+
atlasBlocks: AtlasBlocks;
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* ImageFontAtlas class
|
|
1268
|
+
* Manages a PNG-based font atlas with support for extended character sets
|
|
1269
|
+
*/
|
|
1270
|
+
declare class ImageFontAtlas {
|
|
1271
|
+
private atlasCanvas;
|
|
1272
|
+
private atlasCtx;
|
|
1273
|
+
private atlasImage;
|
|
1274
|
+
private readonly glyphWidth;
|
|
1275
|
+
private readonly glyphHeight;
|
|
1276
|
+
private readonly cellWidth;
|
|
1277
|
+
private readonly cellHeight;
|
|
1278
|
+
private readonly atlasBlocks;
|
|
1279
|
+
private readonly atlasColumns;
|
|
1280
|
+
private readonly maxCharCode;
|
|
1281
|
+
private isLoaded;
|
|
1282
|
+
private colorCache;
|
|
1283
|
+
private readonly MAX_CACHE_SIZE;
|
|
1284
|
+
constructor(config: ImageFontAtlasConfig);
|
|
1285
|
+
/**
|
|
1286
|
+
* Load atlas from PNG binary data
|
|
1287
|
+
* @param pngData PNG image as Uint8Array
|
|
1288
|
+
* @returns Promise that resolves when atlas is loaded
|
|
1289
|
+
*/
|
|
1290
|
+
loadFromPNG(pngData: Uint8Array): Promise<void>;
|
|
1291
|
+
/**
|
|
1292
|
+
* Check if atlas is loaded and ready
|
|
1293
|
+
*/
|
|
1294
|
+
isReady(): boolean;
|
|
1295
|
+
/**
|
|
1296
|
+
* Get glyph position in atlas
|
|
1297
|
+
* @param charCode Character code (0 to maxCharCode)
|
|
1298
|
+
* @returns Position { x, y } or null if out of range
|
|
1299
|
+
*/
|
|
1300
|
+
getGlyphPosition(charCode: number): {
|
|
1301
|
+
x: number;
|
|
1302
|
+
y: number;
|
|
1303
|
+
} | null;
|
|
1304
|
+
/**
|
|
1305
|
+
* Get UV coordinates for a character (block-based layout)
|
|
1306
|
+
* @param charCode Character code
|
|
1307
|
+
* @returns UV coordinates { u1, v1, u2, v2 } or null if invalid
|
|
1308
|
+
*/
|
|
1309
|
+
getCharUV(charCode: number): {
|
|
1310
|
+
u1: number;
|
|
1311
|
+
v1: number;
|
|
1312
|
+
u2: number;
|
|
1313
|
+
v2: number;
|
|
1314
|
+
} | null;
|
|
1315
|
+
/**
|
|
1316
|
+
* Draw a character to a canvas context
|
|
1317
|
+
* @param ctx Destination context
|
|
1318
|
+
* @param charCode Character to draw
|
|
1319
|
+
* @param x Destination X
|
|
1320
|
+
* @param y Destination Y
|
|
1321
|
+
* @param width Destination width
|
|
1322
|
+
* @param height Destination height
|
|
1323
|
+
* @param color Color for the glyph (CSS color string)
|
|
1324
|
+
*/
|
|
1325
|
+
drawChar(ctx: CanvasRenderingContext2D, charCode: number, x: number, y: number, width: number, height: number, color: string): void;
|
|
1326
|
+
/**
|
|
1327
|
+
* Create a colored glyph from the atlas
|
|
1328
|
+
*/
|
|
1329
|
+
private createColoredGlyph;
|
|
1330
|
+
/**
|
|
1331
|
+
* Parse CSS color to RGB
|
|
1332
|
+
*/
|
|
1333
|
+
private parseColor;
|
|
1334
|
+
/**
|
|
1335
|
+
* Evict least recently used cache entry
|
|
1336
|
+
*/
|
|
1337
|
+
private evictLRU;
|
|
1338
|
+
/**
|
|
1339
|
+
* Get the atlas canvas (for WebGL texture upload)
|
|
1340
|
+
*/
|
|
1341
|
+
getAtlasCanvas(): HTMLCanvasElement | null;
|
|
1342
|
+
/**
|
|
1343
|
+
* Get the atlas image (for WebGL texture upload)
|
|
1344
|
+
*/
|
|
1345
|
+
getAtlasImage(): HTMLImageElement | null;
|
|
1346
|
+
/**
|
|
1347
|
+
* Get atlas configuration
|
|
1348
|
+
*/
|
|
1349
|
+
getConfig(): ImageFontAtlasConfig;
|
|
1350
|
+
/**
|
|
1351
|
+
* Get glyph dimensions
|
|
1352
|
+
*/
|
|
1353
|
+
getGlyphDimensions(): {
|
|
1354
|
+
width: number;
|
|
1355
|
+
height: number;
|
|
1356
|
+
};
|
|
1357
|
+
/**
|
|
1358
|
+
* Get cell dimensions
|
|
1359
|
+
*/
|
|
1360
|
+
getCellDimensions(): {
|
|
1361
|
+
width: number;
|
|
1362
|
+
height: number;
|
|
1363
|
+
};
|
|
1364
|
+
/**
|
|
1365
|
+
* Get atlas dimensions
|
|
1366
|
+
*/
|
|
1367
|
+
getAtlasDimensions(): {
|
|
1368
|
+
width: number;
|
|
1369
|
+
height: number;
|
|
1370
|
+
};
|
|
1371
|
+
/**
|
|
1372
|
+
* Get number of atlas columns
|
|
1373
|
+
*/
|
|
1374
|
+
getAtlasColumns(): number;
|
|
1375
|
+
/**
|
|
1376
|
+
* Get maximum supported character code
|
|
1377
|
+
*/
|
|
1378
|
+
getMaxCharCode(): number;
|
|
1379
|
+
/**
|
|
1380
|
+
* Check if a character code is valid
|
|
1381
|
+
*/
|
|
1382
|
+
isValidCharCode(charCode: number): boolean;
|
|
1383
|
+
/**
|
|
1384
|
+
* Clear the color cache
|
|
1385
|
+
*/
|
|
1386
|
+
clearCache(): void;
|
|
1387
|
+
/**
|
|
1388
|
+
* Get cache statistics
|
|
1389
|
+
*/
|
|
1390
|
+
getCacheStats(): {
|
|
1391
|
+
size: number;
|
|
1392
|
+
maxSize: number;
|
|
1393
|
+
};
|
|
1394
|
+
/**
|
|
1395
|
+
* Destroy and release resources
|
|
1396
|
+
*/
|
|
1397
|
+
destroy(): void;
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1121
1400
|
/**
|
|
1122
1401
|
* 🔲 GridOverlay - Classe réutilisable pour gérer le canvas de grille de débogage
|
|
1123
1402
|
*
|
|
@@ -1448,5 +1727,5 @@ declare class PostProcessOverlay {
|
|
|
1448
1727
|
|
|
1449
1728
|
declare const version = "0.1.0";
|
|
1450
1729
|
|
|
1451
|
-
export { AutoplayOverlay, BitmapFontAtlas, DEFAULT_PALETTE, GridOverlay, PostProcessOverlay, Terminal2D, TerminalGL, colorToPaletteIndex, paletteIndexToColor, version };
|
|
1452
|
-
export type { AutoplayOverlayOptions, BitmapFont$1 as BitmapFont, FontType, PostProcessOverlayOptions, RenderOptions, TerminalCell, TerminalData, TerminalGLOptions, WebGLCompatibilityReport };
|
|
1730
|
+
export { AutoplayOverlay, BitmapFontAtlas, DEFAULT_PALETTE, GridOverlay, ImageFontAtlas, PostProcessOverlay, Terminal2D, TerminalGL, colorToPaletteIndex, getAtlasColumns, getCharGridPosition, getMaxCharCode, paletteIndexToColor, version };
|
|
1731
|
+
export type { AtlasBlocks, AutoplayOverlayOptions, BitmapFont$1 as BitmapFont, FontType, ImageFontAtlasConfig, PostProcessOverlayOptions, RenderOptions, TerminalCell, TerminalData, TerminalGLOptions, WebGLCompatibilityReport };
|
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
var
|
|
1
|
+
var J=Object.defineProperty;var tt=(C,t,e)=>t in C?J(C,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):C[t]=e;var v=(C,t)=>J(C,"name",{value:t,configurable:!0});var n=(C,t,e)=>(tt(C,typeof t!="symbol"?t+"":t,e),e);var H=["#000000","#800000","#008000","#808000","#000080","#800080","#008080","#c0c0c0","#808080","#ff0000","#00ff00","#ffff00","#0000ff","#ff00ff","#00ffff","#ffffff","#080808","#121212","#1c1c1c","#262626","#303030","#3a3a3a","#444444","#4e4e4e","#585858","#626262","#6c6c6c","#767676","#808080","#8a8a8a","#949494","#9e9e9e","#a80000","#00a800","#a8a800","#0000a8","#a800a8","#00a8a8","#a8a8a8","#545454","#fc5454","#54fc54","#fcfc54","#5454fc","#fc54fc","#54fcfc","#fcfcfc","#000000",...Array(192).fill("#808080")];function et(C,t=H){return C<0||C>=t.length?"#ff00ff":t[C]}v(et,"paletteIndexToColor");function it(C,t=H){let e=t.indexOf(C.toLowerCase());return e>=0?e:0}v(it,"colorToPaletteIndex");var _=class _{constructor(t,e){n(this,"canvas");n(this,"ctx");n(this,"container");n(this,"cols",0);n(this,"rows",0);n(this,"cellWidth",0);n(this,"cellHeight",0);n(this,"offsetX",0);n(this,"offsetY",0);n(this,"strokeColor","rgba(80, 80, 80, 0.4)");n(this,"lineWidth",1);this.container=t,this.canvas=document.createElement("canvas"),this.canvas.className="grid-overlay-canvas";let i=e?.zIndex??10;this.canvas.style.cssText=`
|
|
2
2
|
display: block !important;
|
|
3
3
|
position: absolute !important;
|
|
4
4
|
pointer-events: none !important;
|
|
5
5
|
image-rendering: pixelated !important;
|
|
6
6
|
image-rendering: crisp-edges !important;
|
|
7
|
-
z-index: ${
|
|
8
|
-
`,this.container.appendChild(this.canvas);let
|
|
7
|
+
z-index: ${i} !important;
|
|
8
|
+
`,this.container.appendChild(this.canvas);let s=this.canvas.getContext("2d");if(!s)throw new Error("[GridOverlay] Impossible de cr\xE9er le contexte 2D");this.ctx=s,e&&(e.strokeColor&&(this.strokeColor=e.strokeColor),e.lineWidth!==void 0&&(this.lineWidth=e.lineWidth))}setDimensions(t,e,i,s,r=0,a=0){this.cols=t,this.rows=e,this.cellWidth=i,this.cellHeight=s,this.offsetX=r,this.offsetY=a}setCanvasSize(t,e){this.canvas.width=t,this.canvas.height=e,this.ctx.setTransform(1,0,0,1,0,0)}setStyle(t){t.strokeColor&&(this.strokeColor=t.strokeColor),t.lineWidth!==void 0&&(this.lineWidth=t.lineWidth)}setTransform(t,e,i,s,r){let a=s.left-r.left,l=s.top-r.top,o=s.width,h=s.height;(this.canvas.width!==o||this.canvas.height!==h)&&(this.canvas.width=o,this.canvas.height=h);let c=this.canvas.style;c.width=`${o}px`,c.height=`${h}px`,c.left=`${a}px`,c.top=`${l}px`}render(){if(!this.ctx)return;let t=this.cols*this.cellWidth,e=this.rows*this.cellHeight,i=t>0?this.canvas.width/t:1,s=e>0?this.canvas.height/e:1,r=Math.min(i,s),a=this.cellWidth*r,l=this.cellHeight*r,o=this.cols*a,h=this.rows*l,c=this.offsetX*r,d=this.offsetY*r;if(!(o===0||h===0)){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.strokeStyle=this.strokeColor,this.ctx.lineWidth=Math.max(1,this.lineWidth*r),this.ctx.beginPath();for(let f=0;f<=this.cols;f++){let u=c+f*a+.5;this.ctx.moveTo(u,d),this.ctx.lineTo(u,d+h)}for(let f=0;f<=this.rows;f++){let u=d+f*l+.5;this.ctx.moveTo(c,u),this.ctx.lineTo(c+o,u)}this.ctx.stroke()}}update(t,e,i,s,r,a,l=0,o=0){this.setDimensions(t,e,i,s,l,o),this.setCanvasSize(r,a),this.render()}setVisible(t){this.canvas.style.display=t?"block":"none"}destroy(){this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}getCanvas(){return this.canvas}};v(_,"GridOverlay");var T=_;function M(C){return Math.sqrt(C)*16}v(M,"getAtlasColumns");function F(C){return C*256-1}v(F,"getMaxCharCode");function D(C,t){let e=Math.floor(C/256),i=C%256,s=Math.sqrt(t),r=e%s,a=Math.floor(e/s),l=i%16,o=Math.floor(i/16);return{col:r*16+l,row:a*16+o}}v(D,"getCharGridPosition");import{POST_PROCESS_DEFAULTS as W,ScalingMode as R}from"@utsp/types";var U=class U{constructor(t,e){n(this,"canvas");n(this,"gl");n(this,"parentElement");n(this,"containerDiv");n(this,"cols");n(this,"rows");n(this,"charWidth");n(this,"charHeight");n(this,"cellWidth",8);n(this,"cellHeight",8);n(this,"glyphOffsetX",0);n(this,"glyphOffsetY",0);n(this,"canvasBgColor");n(this,"showGrid");n(this,"supportsUint32Indices",!1);n(this,"useUint16Indices",!1);n(this,"gridOverlay");n(this,"bitmapFont");n(this,"atlasTexture",null);n(this,"atlasCanvas");n(this,"atlasColumns",0);n(this,"fontLoaded",!1);n(this,"paletteTexture",null);n(this,"program",null);n(this,"positionBuffer",null);n(this,"texCoordBuffer",null);n(this,"colorIndexBuffer",null);n(this,"indexBuffer",null);n(this,"aPosition");n(this,"aTexCoord");n(this,"aColorIndex");n(this,"uResolution",null);n(this,"uTexture",null);n(this,"uPalette",null);n(this,"resizeObserver");n(this,"charCodeToAtlasIndex",new Uint16Array(65536).fill(65535));n(this,"atlasUVs",new Float32Array(0));n(this,"cachedAtlasWidth",0);n(this,"cachedAtlasHeight",0);n(this,"paletteFloat",new Float32Array(256*4));n(this,"maxCells",0);n(this,"renderPositions",new Float32Array(0));n(this,"renderTexCoords",new Float32Array(0));n(this,"renderColorIndices",new Float32Array(0));n(this,"renderIndices",new Uint32Array(0));n(this,"cachedResolution",[0,0]);n(this,"cachedTextureUnit",-1);n(this,"cachedPaletteUnit",-1);n(this,"cachedTextureUniform",!1);n(this,"cachedPaletteUniform",!1);n(this,"paletteHash",0);n(this,"currentScale",1);n(this,"scalingMode",R.None);n(this,"ambientEffectEnabled",!1);n(this,"ambientEffectCanvas",null);n(this,"ambientEffectCtx",null);n(this,"ambientEffectBlur",W.ambientEffect.blur);n(this,"ambientEffectScale",W.ambientEffect.scale);n(this,"ambientEffectOpacity",.7);n(this,"onResizeCallback");n(this,"staticPositionsInitialized",!1);n(this,"vaoExtension",null);n(this,"vao",null);n(this,"instancedExtension",null);n(this,"useInstancing",!1);n(this,"instanceDataBuffer",null);n(this,"instanceData",new Float32Array(0));n(this,"templateQuadPositions",new Float32Array(0));n(this,"templateQuadIndices",new Uint16Array(0));if(!t)throw new Error("TerminalGL: Parent element is required");if(!Number.isInteger(e.cols)||e.cols<=0)throw new Error(`TerminalGL: Invalid cols: ${e.cols}. Must be a positive integer.`);if(!Number.isInteger(e.rows)||e.rows<=0)throw new Error(`TerminalGL: Invalid rows: ${e.rows}. Must be a positive integer.`);let i=e.cols*e.rows,s=U.checkCompatibility();if(s.errors.length>0)throw new Error(`TerminalGL: WebGL incompatible - ${s.errors.join(", ")}`);let r=e.forceUint16??!1;if(r&&i>s.maxCellsUint16)throw new Error(`TerminalGL: Terminal size ${e.cols}\xD7${e.rows} (${i} cells) exceeds Uint16 limit of ${s.maxCellsUint16} cells. Maximum size with Uint16: ${Math.floor(Math.sqrt(s.maxCellsUint16))}\xD7${Math.floor(Math.sqrt(s.maxCellsUint16))} (or smaller rectangles like 127\xD764)`);if(!s.uint32Indices&&i>s.maxCellsUint16)throw new Error(`TerminalGL: Terminal size ${e.cols}\xD7${e.rows} (${i} cells) exceeds device limit of ${s.maxCellsUint16} cells (OES_element_index_uint not supported). Maximum size: ${Math.floor(Math.sqrt(s.maxCellsUint16))}\xD7${Math.floor(Math.sqrt(s.maxCellsUint16))}`);i>s.recommendedMaxCells&&console.warn(`[TerminalGL] \u26A0\uFE0F Large terminal size ${e.cols}\xD7${e.rows} (${i} cells) exceeds recommended limit of ${s.recommendedMaxCells} cells. Performance may be impacted.`),s.warnings.length>0&&console.warn("[TerminalGL] WebGL Compatibility Warnings:",s.warnings);let a=e.charWidth??8,l=e.charHeight??8;if(!Number.isFinite(a)||a<=0)throw new Error(`TerminalGL: Invalid charWidth: ${a}. Must be a positive number.`);if(!Number.isFinite(l)||l<=0)throw new Error(`TerminalGL: Invalid charHeight: ${l}. Must be a positive number.`);for(this.parentElement=t,this.cols=e.cols,this.rows=e.rows,this.charWidth=a,this.charHeight=l,this.canvasBgColor=e.canvasBgColor!==void 0?e.canvasBgColor:null,this.showGrid=e.showGrid??!1,this.scalingMode=e.scalingMode??R.None,e.ambientEffect&&(this.ambientEffectEnabled=!0,typeof e.ambientEffect=="object"&&(this.ambientEffectBlur=e.ambientEffect.blur??W.ambientEffect.blur,this.ambientEffectScale=e.ambientEffect.scale??W.ambientEffect.scale,this.ambientEffectOpacity=e.ambientEffect.opacity??.7));this.parentElement.firstChild;)this.parentElement.removeChild(this.parentElement.firstChild);let o=window.getComputedStyle(this.parentElement).position;o!=="relative"&&o!=="absolute"&&o!=="fixed"&&(this.parentElement.style.position="relative"),this.containerDiv=document.createElement("div"),this.containerDiv.className="terminalgl-container",this.containerDiv.style.cssText=`
|
|
9
9
|
position: absolute !important;
|
|
10
10
|
top: 0 !important;
|
|
11
11
|
left: 0 !important;
|
|
@@ -32,7 +32,7 @@ var X=Object.defineProperty;var q=(y,t,e)=>t in y?X(y,t,{enumerable:!0,configura
|
|
|
32
32
|
backface-visibility: hidden !important;
|
|
33
33
|
position: relative !important;
|
|
34
34
|
z-index: 1 !important;
|
|
35
|
-
`;let h=this.canvas.getContext("webgl",{alpha:this.canvasBgColor===null,premultipliedAlpha:!1});if(!h)throw new Error("TerminalGL: WebGL not supported");this.gl=h,this.supportsUint32Indices=!!h.getExtension("OES_element_index_uint"),this.useUint16Indices=r||!this.supportsUint32Indices,this.useUint16Indices?console.warn(`[TerminalGL] \u{1F4CA} Using Uint16 indices (max ${
|
|
35
|
+
`;let h=this.canvas.getContext("webgl",{alpha:this.canvasBgColor===null,premultipliedAlpha:!1});if(!h)throw new Error("TerminalGL: WebGL not supported");this.gl=h,this.supportsUint32Indices=!!h.getExtension("OES_element_index_uint"),this.useUint16Indices=r||!this.supportsUint32Indices,this.useUint16Indices?console.warn(`[TerminalGL] \u{1F4CA} Using Uint16 indices (max ${s.maxCellsUint16} cells)`):console.warn(`[TerminalGL] \u{1F4CA} Using Uint32 indices (max ${s.maxCellsUint32} cells)`),this.vaoExtension=h.getExtension("OES_vertex_array_object"),this.vaoExtension&&console.warn("[TerminalGL] \u2705 VAO extension enabled (faster attribute binding)"),this.instancedExtension=h.getExtension("ANGLE_instanced_arrays"),this.instancedExtension?(this.useInstancing=!0,console.warn("[TerminalGL] \u{1F680} Instanced rendering enabled (massive draw call reduction)")):console.warn("[TerminalGL] \u26A0\uFE0F Instanced rendering not available (fallback to standard rendering)"),this.containerDiv.appendChild(this.canvas),this.ambientEffectEnabled&&(this.createAmbientEffectCanvas(),console.warn("[TerminalGL] \u{1F308} Ambient effect enabled at startup")),this.parentElement.appendChild(this.containerDiv),this.initRenderBuffers();try{this.initWebGL()}catch(c){throw console.error("[TerminalGL] \u274C Failed to initialize WebGL:",c),c}this.showGrid&&this.initGridOverlay(),this.setupResizeObserver()}static checkCompatibility(){let t={webgl1:!1,uint32Indices:!1,maxTextureSize:0,maxViewportDims:[0,0],maxCellsUint16:0,maxCellsUint32:0,recommendedMaxCells:0,warnings:[],errors:[]},e=document.createElement("canvas"),i=e.getContext("webgl")||e.getContext("experimental-webgl");if(!i||!(i instanceof WebGLRenderingContext))return t.errors.push("\u274C WebGL 1.0 not supported by this browser/device"),t;let s=i;t.webgl1=!0;try{t.maxTextureSize=s.getParameter(s.MAX_TEXTURE_SIZE);let o=s.getParameter(s.MAX_VIEWPORT_DIMS);t.maxViewportDims=[o[0],o[1]]}catch(o){return t.errors.push(`\u274C Failed to query WebGL parameters: ${o}`),t}let r=s.getExtension("OES_element_index_uint");t.uint32Indices=!!r;let a=8;if(t.maxCellsUint16=Math.floor(65535/a),t.uint32Indices){let o=Math.floor(t.maxTextureSize*t.maxTextureSize/64);t.maxCellsUint32=Math.min(o,262144)}else t.maxCellsUint32=t.maxCellsUint16,t.warnings.push(`\u26A0\uFE0F OES_element_index_uint not supported - limited to ${t.maxCellsUint16} cells (e.g., 90\xD790 or 127\xD764)`);return t.recommendedMaxCells=Math.floor((t.uint32Indices?t.maxCellsUint32:t.maxCellsUint16)*.8),t.maxTextureSize<256?t.errors.push(`\u274C MAX_TEXTURE_SIZE too small: ${t.maxTextureSize} (minimum 256 required for font atlas)`):t.maxTextureSize<2048&&t.warnings.push(`\u26A0\uFE0F Small MAX_TEXTURE_SIZE: ${t.maxTextureSize} (may limit font atlas)`),(t.maxViewportDims[0]<1024||t.maxViewportDims[1]<768)&&t.warnings.push(`\u26A0\uFE0F Small MAX_VIEWPORT_DIMS: ${t.maxViewportDims[0]}\xD7${t.maxViewportDims[1]}`),s.getExtension("WEBGL_lose_context")||t.warnings.push("\u26A0\uFE0F WEBGL_lose_context not supported - may cause memory leaks"),t}initInstancedBuffers(){let t=this.gl;this.templateQuadPositions=new Float32Array([0,0,this.cellWidth,0,0,this.cellHeight,this.cellWidth,this.cellHeight]),this.templateQuadIndices=new Uint16Array([0,1,2,2,1,3]),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferData(t.ARRAY_BUFFER,this.templateQuadPositions,t.STATIC_DRAW),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,this.templateQuadIndices,t.STATIC_DRAW);let e=this.cols*this.rows*2;this.instanceData=new Float32Array(e*8),t.bindBuffer(t.ARRAY_BUFFER,this.instanceDataBuffer),t.bufferData(t.ARRAY_BUFFER,this.instanceData.byteLength,t.DYNAMIC_DRAW)}initRenderBuffers(){this.maxCells=this.cols*this.rows*2,this.renderPositions=new Float32Array(this.maxCells*4*2),this.renderTexCoords=new Float32Array(this.maxCells*4*2),this.renderColorIndices=new Float32Array(this.maxCells*4),this.useUint16Indices?this.renderIndices=new Uint16Array(this.maxCells*6):this.renderIndices=new Uint32Array(this.maxCells*6),this.staticPositionsInitialized=!1}precomputeStaticPositions(){let t=this.cellWidth,e=this.cellHeight,i=this.glyphOffsetX,s=this.glyphOffsetY,r=this.charWidth,a=this.charHeight,l=0,o=0,h=0;for(let d=0;d<this.rows;d++){let f=Math.round(d*e),u=Math.round(f+e);for(let m=0;m<this.cols;m++){let g=Math.round(m*t),p=Math.round(g+t);this.renderPositions[l++]=g,this.renderPositions[l++]=f,this.renderPositions[l++]=p,this.renderPositions[l++]=f,this.renderPositions[l++]=g,this.renderPositions[l++]=u,this.renderPositions[l++]=p,this.renderPositions[l++]=u,this.renderIndices[o++]=h,this.renderIndices[o++]=h+1,this.renderIndices[o++]=h+2,this.renderIndices[o++]=h+2,this.renderIndices[o++]=h+1,this.renderIndices[o++]=h+3,h+=4;let x=Math.round(g+i),w=Math.round(f+s),b=Math.round(x+r),E=Math.round(w+a);this.renderPositions[l++]=x,this.renderPositions[l++]=w,this.renderPositions[l++]=b,this.renderPositions[l++]=w,this.renderPositions[l++]=x,this.renderPositions[l++]=E,this.renderPositions[l++]=b,this.renderPositions[l++]=E,this.renderIndices[o++]=h,this.renderIndices[o++]=h+1,this.renderIndices[o++]=h+2,this.renderIndices[o++]=h+2,this.renderIndices[o++]=h+1,this.renderIndices[o++]=h+3,h+=4}}let c=this.gl;c.bindBuffer(c.ARRAY_BUFFER,this.positionBuffer),c.bufferSubData(c.ARRAY_BUFFER,0,this.renderPositions),c.bindBuffer(c.ELEMENT_ARRAY_BUFFER,this.indexBuffer),c.bufferSubData(c.ELEMENT_ARRAY_BUFFER,0,this.renderIndices),this.staticPositionsInitialized=!0}initWebGL(){let t=this.gl,e;this.useInstancing?e=`
|
|
36
36
|
// Per-vertex attributes (template quad)
|
|
37
37
|
attribute vec2 aPosition; // Local quad position (0,0 to cellW,cellH)
|
|
38
38
|
|
|
@@ -93,7 +93,7 @@ var X=Object.defineProperty;var q=(y,t,e)=>t in y?X(y,t,{enumerable:!0,configura
|
|
|
93
93
|
float paletteU = (aColorIndex + 0.5) / 256.0;
|
|
94
94
|
vColor = texture2D(uPalette, vec2(paletteU, 0.5));
|
|
95
95
|
}
|
|
96
|
-
`;let
|
|
96
|
+
`;let i=`
|
|
97
97
|
precision mediump float;
|
|
98
98
|
|
|
99
99
|
uniform sampler2D uTexture;
|
|
@@ -106,12 +106,14 @@ var X=Object.defineProperty;var q=(y,t,e)=>t in y?X(y,t,{enumerable:!0,configura
|
|
|
106
106
|
if (vTexCoord.x == 0.0 && vTexCoord.y == 0.0) {
|
|
107
107
|
gl_FragColor = vColor;
|
|
108
108
|
} else {
|
|
109
|
-
// Textured character: multiply color by
|
|
109
|
+
// Textured character: multiply atlas color by uniform color
|
|
110
|
+
// - White glyph (1,1,1) \u2192 uniform color applied normally
|
|
111
|
+
// - Colored glyph \u2192 atlas color \xD7 uniform color (multiply blend)
|
|
110
112
|
vec4 texColor = texture2D(uTexture, vTexCoord);
|
|
111
|
-
gl_FragColor = vec4(
|
|
113
|
+
gl_FragColor = vec4(texColor.rgb * vColor.rgb, texColor.a);
|
|
112
114
|
}
|
|
113
115
|
}
|
|
114
|
-
`,i=this.compileShader(t.VERTEX_SHADER,e),r=this.compileShader(t.FRAGMENT_SHADER,s);if(!i||!r)throw new Error("Shader compilation error");let a=t.createProgram();if(!a)throw new Error("Unable to create WebGL program");if(t.attachShader(a,i),t.attachShader(a,r),t.linkProgram(a),!t.getProgramParameter(a,t.LINK_STATUS)){let l=t.getProgramInfoLog(a);throw new Error("Program linking error: "+l)}this.program=a,this.cachedTextureUniform=!1,this.cachedPaletteUniform=!1,this.aPosition=t.getAttribLocation(a,"aPosition"),this.useInstancing?(this.aTexCoord=t.getAttribLocation(a,"aInstanceOffset"),this.aColorIndex=t.getAttribLocation(a,"aInstanceUVs"),this.instanceDataBuffer=t.createBuffer()):(this.aTexCoord=t.getAttribLocation(a,"aTexCoord"),this.aColorIndex=t.getAttribLocation(a,"aColorIndex")),this.uResolution=t.getUniformLocation(a,"uResolution"),this.uTexture=t.getUniformLocation(a,"uTexture"),this.uPalette=t.getUniformLocation(a,"uPalette"),this.positionBuffer=t.createBuffer(),this.texCoordBuffer=t.createBuffer(),this.colorIndexBuffer=t.createBuffer(),this.indexBuffer=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderPositions.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderTexCoords.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderColorIndices.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,t.DYNAMIC_DRAW),this.vaoExtension&&(this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.enableVertexAttribArray(this.aPosition),t.vertexAttribPointer(this.aPosition,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.enableVertexAttribArray(this.aTexCoord),t.vertexAttribPointer(this.aTexCoord,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.enableVertexAttribArray(this.aColorIndex),t.vertexAttribPointer(this.aColorIndex,1,t.FLOAT,!1,0,0),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null))),t.enable(t.BLEND),t.blendFunc(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA),t.viewport(0,0,this.canvas.width,this.canvas.height)}compileShader(t,e){let s=this.gl,i=s.createShader(t);return i?(s.shaderSource(i,e),s.compileShader(i),s.getShaderParameter(i,s.COMPILE_STATUS)?i:(console.error("Shader compilation error:",s.getShaderInfoLog(i)),s.deleteShader(i),null)):null}initGridOverlay(){this.gridOverlay=new T(this.containerDiv,{strokeColor:"rgba(144, 24, 24, 1)",lineWidth:1,zIndex:10}),this.updateGridOverlay()}updateGridOverlay(){if(!this.gridOverlay)return;let t=this.cols*this.cellWidth,e=this.rows*this.cellHeight,s=this.canvas.getBoundingClientRect(),i=this.containerDiv.getBoundingClientRect();this.gridOverlay.setDimensions(this.cols,this.rows,this.cellWidth,this.cellHeight,0,0),this.gridOverlay.setTransform(this.currentScale,t,e,s,i),this.gridOverlay.render()}setBitmapFont(t,e,s,i,r){this.bitmapFont=t,this.charWidth=Math.round(e),this.charHeight=Math.round(s),this.cellWidth=Math.round(i),this.cellHeight=Math.round(r),this.glyphOffsetX=Math.round((this.cellWidth-this.charWidth)/2),this.glyphOffsetY=Math.round((this.cellHeight-this.charHeight)/2),this.useInstancing&&(console.warn("[TerminalGL] \u{1F680} Initializing instanced buffers..."),this.initInstancedBuffers());try{this.generateAtlas()}catch(a){throw console.error("[TerminalGL] \u274C Failed to generate atlas:",a),a}this.buildCharCodeMap(),this.precomputeAtlasUVs(),this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.useInstancing||this.precomputeStaticPositions(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}generateAtlas(){if(!this.bitmapFont)return;let t=Array.from(this.bitmapFont.keys()).sort((o,h)=>o-h),e=t.length;this.atlasColumns=Math.ceil(Math.sqrt(e));let s=Math.ceil(e/this.atlasColumns),i=this.atlasColumns*this.charWidth,r=s*this.charHeight;this.atlasCanvas=document.createElement("canvas"),this.atlasCanvas.width=i,this.atlasCanvas.height=r;let a=this.atlasCanvas.getContext("2d");if(!a)throw new Error("Unable to create 2D context for atlas");a.clearRect(0,0,i,r),a.fillStyle="#ffffff";let l=0;for(let o of t){let h=this.bitmapFont.get(o);if(!h)continue;let c=l%this.atlasColumns,d=Math.floor(l/this.atlasColumns),f=c*this.charWidth,u=d*this.charHeight;for(let m=0;m<Math.min(h.length,this.charHeight);m++){let b=h[m];for(let g=0;g<this.charWidth;g++){let C=1<<7-g;b&C&&a.fillRect(f+g,u+m,1,1)}}l++}this.createAtlasTexture()}buildCharCodeMap(){if(!this.bitmapFont)return;this.charCodeToAtlasIndex.fill(65535);let t=Array.from(this.bitmapFont.keys()).sort((e,s)=>e-s);for(let e=0;e<t.length;e++)this.charCodeToAtlasIndex[t[e]]=e}precomputeAtlasUVs(){if(!this.bitmapFont)return;let t=this.bitmapFont.size;this.atlasUVs=new Float32Array(t*4),this.cachedAtlasWidth=this.atlasColumns*this.charWidth,this.cachedAtlasHeight=Math.ceil(t/this.atlasColumns)*this.charHeight;let e=this.cachedAtlasWidth,s=this.cachedAtlasHeight,i=.5/e,r=.5/s;for(let a=0;a<t;a++){let l=a%this.atlasColumns,o=Math.floor(a/this.atlasColumns),h=(l*this.charWidth+i)/e,c=(o*this.charHeight+r)/s,d=((l+1)*this.charWidth-i)/e,f=((o+1)*this.charHeight-r)/s,u=a*4;this.atlasUVs[u]=h,this.atlasUVs[u+1]=c,this.atlasUVs[u+2]=d,this.atlasUVs[u+3]=f}}createAtlasTexture(){if(this.atlasCanvas)try{let t=this.gl,e=t.createTexture();if(!e)throw new Error("Unable to create texture");t.bindTexture(t.TEXTURE_2D,e),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,this.atlasCanvas),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),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),this.atlasTexture=e}catch(t){throw console.error("[TerminalGL] \u274C Failed to create atlas texture:",t),t}}clear(){let t=this.gl;t&&t.clear(t.COLOR_BUFFER_BIT)}parseColor(t){if(t.startsWith("#")){let e=t.slice(1),s=0,i=0,r=0;return e.length===3?(s=parseInt(e[0]+e[0],16),i=parseInt(e[1]+e[1],16),r=parseInt(e[2]+e[2],16)):e.length===6&&(s=parseInt(e.slice(0,2),16),i=parseInt(e.slice(2,4),16),r=parseInt(e.slice(4,6),16)),[s/255,i/255,r/255,1]}if(t.startsWith("rgb")){let e=t.match(/rgba?\(([^)]+)\)/);if(e){let s=e[1].split(",").map(i=>parseFloat(i.trim()));return[s[0]/255,s[1]/255,s[2]/255,s[3]!==void 0?s[3]:1]}}return[1,1,1,1]}setupResizeObserver(){if(!(typeof ResizeObserver>"u"))try{this.resizeObserver=new ResizeObserver(()=>{try{this.updateCanvasSize()}catch(t){console.error("[TerminalGL] \u274C Error in resize callback:",t)}}),this.resizeObserver.observe(this.parentElement),this.updateCanvasSize()}catch(t){console.error("[TerminalGL] \u274C Failed to setup ResizeObserver:",t)}}updateCanvasSize(){let t=this.parentElement.clientWidth,e=this.parentElement.clientHeight;if(t===0||e===0)return;let s=this.cols*this.cellWidth,i=this.rows*this.cellHeight;if(s===0||i===0)return;let r=t/s,a=e/i,l=Math.min(r,a),o;switch(this.scalingMode){case A.Integer:o=Math.max(1,Math.floor(l));break;case A.Half:o=Math.max(1,Math.floor(l*2)/2);break;case A.Quarter:o=Math.max(1,Math.floor(l*4)/4);break;case A.Eighth:o=Math.max(1,Math.floor(l*8)/8);break;case A.None:default:o=Math.max(.1,l);break}if(this.canvas.style.transform=`scale(${o})`,this.ambientEffectEnabled&&this.ambientEffectCanvas){let h=o*this.ambientEffectScale;this.ambientEffectCanvas.style.transform=`scale(${h})`}this.currentScale=o,this.onResizeCallback&&this.onResizeCallback(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}updateAmbientEffect(){!this.ambientEffectCanvas||!this.ambientEffectCtx||this.ambientEffectCtx.drawImage(this.canvas,0,0)}setPalette(t){let e=0;for(let s=0;s<Math.min(t.length,256);s++){let i=t[s];e=(e<<5)-e+i.r,e=(e<<5)-e+i.g,e=(e<<5)-e+i.b,e=(e<<5)-e+i.a,e=e|0}if(e!==this.paletteHash){this.paletteHash=e;for(let s=0;s<t.length&&s<256;s++){let i=t[s],r=s*4;this.paletteFloat[r]=i.r/255,this.paletteFloat[r+1]=i.g/255,this.paletteFloat[r+2]=i.b/255,this.paletteFloat[r+3]=i.a/255}this.updatePaletteTexture()}}updatePaletteTexture(){try{let t=this.gl;this.paletteTexture||(this.paletteTexture=t.createTexture()),t.bindTexture(t.TEXTURE_2D,this.paletteTexture);let e=new Uint8Array(256*4);for(let s=0;s<256;s++){let i=s*4;e[i]=this.paletteFloat[i]*255,e[i+1]=this.paletteFloat[i+1]*255,e[i+2]=this.paletteFloat[i+2]*255,e[i+3]=this.paletteFloat[i+3]*255}t.texImage2D(t.TEXTURE_2D,0,t.RGBA,256,1,0,t.RGBA,t.UNSIGNED_BYTE,e),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),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),t.bindTexture(t.TEXTURE_2D,null)}catch(t){throw console.error("[TerminalGL] \u274C Failed to update palette texture:",t),t}}renderDisplayData(t){try{if(!this.isReady()){console.warn("[TerminalGL] \u26A0\uFE0F Not ready: font not loaded yet");return}(t.width!==this.cols||t.height!==this.rows)&&this.resize(t.width,t.height),this.renderDirect(t),this.ambientEffectEnabled&&this.ambientEffectCanvas&&this.ambientEffectCtx&&this.updateAmbientEffect()}catch(e){console.error("[TerminalGL] \u274C Error rendering display data:",e)}}renderDirect(t){let e=this.gl;if(!this.staticPositionsInitialized&&!this.useInstancing&&this.precomputeStaticPositions(),this.canvasBgColor!==null){let s=this.parseColor(this.canvasBgColor);e.clearColor(s[0],s[1],s[2],s[3])}else e.clearColor(0,0,0,0);e.clear(e.COLOR_BUFFER_BIT),this.useInstancing?this.renderInstanced(t):this.renderDirectBuffers(t)}renderInstanced(t){let e=this.gl,s=this.instancedExtension;if(!this.instanceData||this.instanceData.length===0){console.warn("[TerminalGL] \u26A0\uFE0F Instance data not initialized, falling back to standard rendering"),this.useInstancing=!1,this.renderDirectBuffers(t);return}let i=t.width*t.height;if(this.useUint16Indices&&i>8191){console.warn(`[TerminalGL] \u26A0\uFE0F Terminal too large for instanced rendering with Uint16 (${i} cells > 8191). Falling back to standard rendering.`),this.useInstancing=!1,this.renderDirectBuffers(t);return}let r=0,a=this.instanceData.length/8;for(let f=0;f<t.height;f++)for(let u=0;u<t.width;u++){let m=t.cells[f*t.width+u],b=m.bgColorIndex,g=m.fgColorIndex,C=this.canvasBgColor!==null&&b===255,w=m.char===" "||g===255;if(!C){if(r>=a){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${a}`);break}let p=r*8;this.instanceData[p]=u,this.instanceData[p+1]=f,this.instanceData[p+2]=0,this.instanceData[p+3]=0,this.instanceData[p+4]=0,this.instanceData[p+5]=0,this.instanceData[p+6]=b,this.instanceData[p+7]=0,r++}if(!w){let p=m.char.charCodeAt(0),E=this.charCodeToAtlasIndex[p];if(E!==65535){if(r>=a){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${a}`);break}let R=E*4,v=r*8;this.instanceData[v]=u,this.instanceData[v+1]=f,this.instanceData[v+2]=this.atlasUVs[R],this.instanceData[v+3]=this.atlasUVs[R+1],this.instanceData[v+4]=this.atlasUVs[R+2],this.instanceData[v+5]=this.atlasUVs[R+3],this.instanceData[v+6]=0,this.instanceData[v+7]=g,r++}}}if(r===0){console.warn("[TerminalGL] \u26A0\uFE0F No instances to draw (all cells skipped)");return}e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.instanceData.subarray(0,r*8)),e.useProgram(this.program),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(this.aPosition),e.vertexAttribPointer(this.aPosition,2,e.FLOAT,!1,0,0),s.vertexAttribDivisorANGLE(this.aPosition,0),e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer);let l=8*Float32Array.BYTES_PER_ELEMENT,o=e.getAttribLocation(this.program,"aInstanceOffset");e.enableVertexAttribArray(o),e.vertexAttribPointer(o,2,e.FLOAT,!1,l,0),s.vertexAttribDivisorANGLE(o,1);let h=e.getAttribLocation(this.program,"aInstanceUVs");e.enableVertexAttribArray(h),e.vertexAttribPointer(h,4,e.FLOAT,!1,l,2*Float32Array.BYTES_PER_ELEMENT),s.vertexAttribDivisorANGLE(h,1);let c=e.getAttribLocation(this.program,"aInstanceColors");e.enableVertexAttribArray(c),e.vertexAttribPointer(c,2,e.FLOAT,!1,l,6*Float32Array.BYTES_PER_ELEMENT),s.vertexAttribDivisorANGLE(c,1),e.uniform2f(this.uResolution,this.canvas.width,this.canvas.height);let d=e.getUniformLocation(this.program,"uCellSize");e.uniform2f(d,this.cellWidth,this.cellHeight),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.atlasTexture),e.uniform1i(this.uTexture,0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.paletteTexture),e.uniform1i(this.uPalette,1),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer),s.drawElementsInstancedANGLE(e.TRIANGLES,6,e.UNSIGNED_SHORT,0,r),s.vertexAttribDivisorANGLE(this.aPosition,0),s.vertexAttribDivisorANGLE(o,0),s.vertexAttribDivisorANGLE(h,0),s.vertexAttribDivisorANGLE(c,0)}renderDirectBuffers(t){let e=this.gl,s=t.width*t.height,i=s*2;if(i>this.maxCells){console.error(`[TerminalGL] \u274C Buffer overflow detected! Trying to render ${i} quads but buffer capacity is ${this.maxCells}. This should not happen - resize() should have reallocated.`);return}let r=0,a=0,l=t.cells,o=s;for(let m=0;m<o;m++){let b=l[m],g=b.bgColorIndex,w=this.canvasBgColor!==null&&g===255?255:g,p=0;for(let v=0;v<4;v++)this.renderTexCoords[r++]=p,this.renderTexCoords[r++]=p,this.renderColorIndices[a++]=w;let E=b.fgColorIndex,R=b.char;if(R===" "||E===255)for(let v=0;v<4;v++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[a++]=255;else{let v=R.charCodeAt(0),k=this.charCodeToAtlasIndex[v];if(k!==65535){let I=k*4,$=this.atlasUVs[I],Y=this.atlasUVs[I+1],N=this.atlasUVs[I+2],V=this.atlasUVs[I+3];this.renderTexCoords[r++]=$,this.renderTexCoords[r++]=Y,this.renderColorIndices[a++]=E,this.renderTexCoords[r++]=N,this.renderTexCoords[r++]=Y,this.renderColorIndices[a++]=E,this.renderTexCoords[r++]=$,this.renderTexCoords[r++]=V,this.renderColorIndices[a++]=E,this.renderTexCoords[r++]=N,this.renderTexCoords[r++]=V,this.renderColorIndices[a++]=E}else for(let I=0;I<4;I++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[a++]=255}}(r!==o*2*4*2||a!==o*2*4)&&console.error("[TerminalGL] \u274C Buffer index mismatch!",{texIdx:r,colorIdx:a,expected:o*2*4}),e.useProgram(this.program),this.vao&&this.vaoExtension?(this.vaoExtension.bindVertexArrayOES(this.vao),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,a))):(e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(this.aPosition),e.vertexAttribPointer(this.aPosition,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),e.enableVertexAttribArray(this.aTexCoord),e.vertexAttribPointer(this.aTexCoord,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,a)),e.enableVertexAttribArray(this.aColorIndex),e.vertexAttribPointer(this.aColorIndex,1,e.FLOAT,!1,0,0));let h=this.canvas.width,c=this.canvas.height;(this.cachedResolution[0]!==h||this.cachedResolution[1]!==c)&&(e.uniform2f(this.uResolution,h,c),this.cachedResolution[0]=h,this.cachedResolution[1]=c),this.cachedTextureUnit!==0&&(e.activeTexture(e.TEXTURE0),this.cachedTextureUnit=0),e.bindTexture(e.TEXTURE_2D,this.atlasTexture),this.cachedTextureUniform||(e.uniform1i(this.uTexture,0),this.cachedTextureUniform=!0),this.cachedPaletteUnit!==1&&(e.activeTexture(e.TEXTURE1),this.cachedPaletteUnit=1),e.bindTexture(e.TEXTURE_2D,this.paletteTexture),this.cachedPaletteUniform||(e.uniform1i(this.uPalette,1),this.cachedPaletteUniform=!0),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer);let f=t.width*t.height*2*6,u=this.useUint16Indices?e.UNSIGNED_SHORT:e.UNSIGNED_INT;e.drawElements(e.TRIANGLES,f,u,0)}resize(t,e){if(!Number.isInteger(t)||t<=0){console.error(`[TerminalGL] \u274C Invalid cols: ${t}. Must be positive integer.`);return}if(!Number.isInteger(e)||e<=0){console.error(`[TerminalGL] \u274C Invalid rows: ${e}. Must be positive integer.`);return}if(t===this.cols&&e===this.rows)return;let s=t*e,i=this.useUint16Indices?8191:262144;if(s>i){let c=Math.floor(Math.sqrt(i));console.error(`[TerminalGL] \u274C Cannot resize to ${t}\xD7${e} (${s} cells). Device limit: ${i} cells (max ~${c}\xD7${c}). ${this.useUint16Indices?"Device lacks OES_element_index_uint extension.":""}`);return}let r=B.checkCompatibility();s>r.recommendedMaxCells&&console.warn(`[TerminalGL] \u26A0\uFE0F Resizing to ${t}\xD7${e} (${s} cells) exceeds recommended limit (${r.recommendedMaxCells} cells). Performance may degrade.`),this.cols=t,this.rows=e,this.staticPositionsInitialized=!1;let a=this.cols*this.rows*2,l=Math.ceil(a*1.5),o=a>this.maxCells,h=this.maxCells>l*4;if(o||h){let c=this.maxCells;this.maxCells=l,this.renderPositions=new Float32Array(this.maxCells*4*2),this.renderTexCoords=new Float32Array(this.maxCells*4*2),this.renderColorIndices=new Float32Array(this.maxCells*4),this.useUint16Indices?this.renderIndices=new Uint16Array(this.maxCells*6):this.renderIndices=new Uint32Array(this.maxCells*6);let d=this.gl;this.useInstancing||(d.bindBuffer(d.ARRAY_BUFFER,this.positionBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderPositions.byteLength,d.DYNAMIC_DRAW)),d.bindBuffer(d.ARRAY_BUFFER,this.texCoordBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderTexCoords.byteLength,d.DYNAMIC_DRAW),d.bindBuffer(d.ARRAY_BUFFER,this.colorIndexBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderColorIndices.byteLength,d.DYNAMIC_DRAW),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,this.indexBuffer),d.bufferData(d.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,d.DYNAMIC_DRAW),this.vaoExtension&&this.vao&&(this.vaoExtension.deleteVertexArrayOES(this.vao),this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),d.bindBuffer(d.ARRAY_BUFFER,this.positionBuffer),d.enableVertexAttribArray(this.aPosition),d.vertexAttribPointer(this.aPosition,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,this.texCoordBuffer),d.enableVertexAttribArray(this.aTexCoord),d.vertexAttribPointer(this.aTexCoord,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,this.colorIndexBuffer),d.enableVertexAttribArray(this.aColorIndex),d.vertexAttribPointer(this.aColorIndex,1,d.FLOAT,!1,0,0),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null)));let f=o?"\u{1F4C8} Growing":"\u{1F4C9} Shrinking",u=c*160/1048576,m=this.maxCells*160/1048576;console.warn(`[TerminalGL] ${f} buffers: ${u.toFixed(2)}MB \u2192 ${m.toFixed(2)}MB (${c} \u2192 ${this.maxCells} quads)`)}this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.ambientEffectEnabled&&this.ambientEffectCanvas&&(this.ambientEffectCanvas.width=this.canvas.width,this.ambientEffectCanvas.height=this.canvas.height,this.ambientEffectCanvas.style.width=`${this.canvas.width}px`,this.ambientEffectCanvas.style.height=`${this.canvas.height}px`),this.showGrid&&this.gridOverlay&&this.updateGridOverlay(),this.updateCanvasSize()}getCanvas(){return this.canvas}getGridSize(){return{cols:this.cols,rows:this.rows}}getCellWidth(){return this.cellWidth}getCellHeight(){return this.cellHeight}getCurrentScale(){return this.currentScale}getBaseDimensions(){return{width:this.cols*this.cellWidth,height:this.rows*this.cellHeight}}setOnResizeCallback(t){this.onResizeCallback=t}clearOnResizeCallback(){this.onResizeCallback=void 0}setScalingMode(t){this.scalingMode!==t&&(this.scalingMode=t,this.updateCanvasSize())}getScalingMode(){return this.scalingMode}setGrid(t){t.enabled?(this.gridOverlay?this.gridOverlay.setStyle({strokeColor:t.color,lineWidth:t.lineWidth}):this.gridOverlay=new T(this.containerDiv,{strokeColor:t.color??"rgba(144, 24, 24, 1)",lineWidth:t.lineWidth??1,zIndex:10}),this.showGrid=!0,this.gridOverlay.setVisible(!0),this.updateGridOverlay()):(this.showGrid=!1,this.gridOverlay&&this.gridOverlay.setVisible(!1))}isGridEnabled(){return this.showGrid}setAmbientEffect(t){typeof t=="boolean"?this.ambientEffectEnabled=t:(this.ambientEffectEnabled=!0,t.blur!==void 0&&(this.ambientEffectBlur=t.blur),t.scale!==void 0&&(this.ambientEffectScale=t.scale)),this.ambientEffectEnabled&&!this.ambientEffectCanvas&&this.createAmbientEffectCanvas(),this.ambientEffectCanvas&&(this.ambientEffectEnabled?(this.ambientEffectCanvas.style.display="block",this.ambientEffectCanvas.style.filter=`blur(${this.ambientEffectBlur}px)`,this.ambientEffectCanvas.style.transform=`scale(${this.ambientEffectScale})`,this.updateAmbientEffect()):this.ambientEffectCanvas.style.display="none")}createAmbientEffectCanvas(){this.ambientEffectCanvas||(this.ambientEffectCanvas=document.createElement("canvas"),this.ambientEffectCanvas.className="terminalgl-ambient-effect-canvas",this.ambientEffectCanvas.width=this.canvas.width,this.ambientEffectCanvas.height=this.canvas.height,this.ambientEffectCanvas.style.cssText=`
|
|
116
|
+
`,s=this.compileShader(t.VERTEX_SHADER,e),r=this.compileShader(t.FRAGMENT_SHADER,i);if(!s||!r)throw new Error("Shader compilation error");let a=t.createProgram();if(!a)throw new Error("Unable to create WebGL program");if(t.attachShader(a,s),t.attachShader(a,r),t.linkProgram(a),!t.getProgramParameter(a,t.LINK_STATUS)){let l=t.getProgramInfoLog(a);throw new Error("Program linking error: "+l)}this.program=a,this.cachedTextureUniform=!1,this.cachedPaletteUniform=!1,this.aPosition=t.getAttribLocation(a,"aPosition"),this.useInstancing?(this.aTexCoord=t.getAttribLocation(a,"aInstanceOffset"),this.aColorIndex=t.getAttribLocation(a,"aInstanceUVs"),this.instanceDataBuffer=t.createBuffer()):(this.aTexCoord=t.getAttribLocation(a,"aTexCoord"),this.aColorIndex=t.getAttribLocation(a,"aColorIndex")),this.uResolution=t.getUniformLocation(a,"uResolution"),this.uTexture=t.getUniformLocation(a,"uTexture"),this.uPalette=t.getUniformLocation(a,"uPalette"),this.positionBuffer=t.createBuffer(),this.texCoordBuffer=t.createBuffer(),this.colorIndexBuffer=t.createBuffer(),this.indexBuffer=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderPositions.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderTexCoords.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.bufferData(t.ARRAY_BUFFER,this.renderColorIndices.byteLength,t.DYNAMIC_DRAW),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,t.DYNAMIC_DRAW),this.vaoExtension&&(this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.enableVertexAttribArray(this.aPosition),t.vertexAttribPointer(this.aPosition,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.enableVertexAttribArray(this.aTexCoord),t.vertexAttribPointer(this.aTexCoord,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.enableVertexAttribArray(this.aColorIndex),t.vertexAttribPointer(this.aColorIndex,1,t.FLOAT,!1,0,0),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null))),t.enable(t.BLEND),t.blendFunc(t.SRC_ALPHA,t.ONE_MINUS_SRC_ALPHA),t.viewport(0,0,this.canvas.width,this.canvas.height)}compileShader(t,e){let i=this.gl,s=i.createShader(t);return s?(i.shaderSource(s,e),i.compileShader(s),i.getShaderParameter(s,i.COMPILE_STATUS)?s:(console.error("Shader compilation error:",i.getShaderInfoLog(s)),i.deleteShader(s),null)):null}initGridOverlay(){this.gridOverlay=new T(this.containerDiv,{strokeColor:"rgba(144, 24, 24, 1)",lineWidth:1,zIndex:10}),this.updateGridOverlay()}updateGridOverlay(){if(!this.gridOverlay)return;let t=this.cols*this.cellWidth,e=this.rows*this.cellHeight,i=this.canvas.getBoundingClientRect(),s=this.containerDiv.getBoundingClientRect();this.gridOverlay.setDimensions(this.cols,this.rows,this.cellWidth,this.cellHeight,0,0),this.gridOverlay.setTransform(this.currentScale,t,e,i,s),this.gridOverlay.render()}setBitmapFont(t,e,i,s,r){this.bitmapFont=t,this.fontLoaded=!0,this.charWidth=Math.round(e),this.charHeight=Math.round(i),this.cellWidth=Math.round(s),this.cellHeight=Math.round(r),this.glyphOffsetX=Math.round((this.cellWidth-this.charWidth)/2),this.glyphOffsetY=Math.round((this.cellHeight-this.charHeight)/2),this.useInstancing&&(console.warn("[TerminalGL] \u{1F680} Initializing instanced buffers..."),this.initInstancedBuffers());try{this.generateAtlas()}catch(a){throw console.error("[TerminalGL] \u274C Failed to generate atlas:",a),a}this.buildCharCodeMap(),this.precomputeAtlasUVs(),this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.useInstancing||this.precomputeStaticPositions(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}async setImageFont(t,e,i,s,r,a){this.fontLoaded=!0,this.charWidth=Math.round(e),this.charHeight=Math.round(i),this.cellWidth=Math.round(s),this.cellHeight=Math.round(r),this.atlasColumns=M(a);let l=F(a);this.glyphOffsetX=Math.round((this.cellWidth-this.charWidth)/2),this.glyphOffsetY=Math.round((this.cellHeight-this.charHeight)/2),this.useInstancing&&(console.warn("[TerminalGL] \u{1F680} Initializing instanced buffers for ImageFont..."),this.initInstancedBuffers()),await this.loadImageFontAtlas(t),this.charCodeToAtlasIndex.fill(65535);for(let o=0;o<=l;o++)this.charCodeToAtlasIndex[o]=o;this.precomputeImageFontUVs(a),this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.useInstancing||this.precomputeStaticPositions(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}async loadImageFontAtlas(t){return new Promise((e,i)=>{let s=new Uint8Array(t),r=new Blob([s],{type:"image/png"}),a=URL.createObjectURL(r),l=new Image;l.onload=()=>{URL.revokeObjectURL(a),this.atlasCanvas=document.createElement("canvas"),this.atlasCanvas.width=l.width,this.atlasCanvas.height=l.height;let o=this.atlasCanvas.getContext("2d");if(!o){i(new Error("Failed to create 2D context for atlas"));return}o.drawImage(l,0,0),this.cachedAtlasWidth=l.width,this.cachedAtlasHeight=l.height,this.createAtlasTexture(),e()},l.onerror=()=>{URL.revokeObjectURL(a),i(new Error("Failed to load PNG image for ImageFont atlas"))},l.src=a})}precomputeImageFontUVs(t){let e=t*256;this.atlasUVs=new Float32Array(e*4);let i=this.cachedAtlasWidth,s=this.cachedAtlasHeight,r=.5/i,a=.5/s;for(let l=0;l<e;l++){let{col:o,row:h}=D(l,t),c=(o*this.charWidth+r)/i,d=(h*this.charHeight+a)/s,f=((o+1)*this.charWidth-r)/i,u=((h+1)*this.charHeight-a)/s,m=l*4;this.atlasUVs[m]=c,this.atlasUVs[m+1]=d,this.atlasUVs[m+2]=f,this.atlasUVs[m+3]=u}}generateAtlas(){if(!this.bitmapFont)return;let t=Array.from(this.bitmapFont.keys()).sort((o,h)=>o-h),e=t.length;this.atlasColumns=Math.ceil(Math.sqrt(e));let i=Math.ceil(e/this.atlasColumns),s=this.atlasColumns*this.charWidth,r=i*this.charHeight;this.atlasCanvas=document.createElement("canvas"),this.atlasCanvas.width=s,this.atlasCanvas.height=r;let a=this.atlasCanvas.getContext("2d");if(!a)throw new Error("Unable to create 2D context for atlas");a.clearRect(0,0,s,r),a.fillStyle="#ffffff";let l=0;for(let o of t){let h=this.bitmapFont.get(o);if(!h)continue;let c=l%this.atlasColumns,d=Math.floor(l/this.atlasColumns),f=c*this.charWidth,u=d*this.charHeight;for(let m=0;m<Math.min(h.length,this.charHeight);m++){let g=h[m];for(let p=0;p<this.charWidth;p++){let x=1<<7-p;g&x&&a.fillRect(f+p,u+m,1,1)}}l++}this.createAtlasTexture()}buildCharCodeMap(){if(!this.bitmapFont)return;this.charCodeToAtlasIndex.fill(65535);let t=Array.from(this.bitmapFont.keys()).sort((e,i)=>e-i);for(let e=0;e<t.length;e++)this.charCodeToAtlasIndex[t[e]]=e}precomputeAtlasUVs(){if(!this.bitmapFont)return;let t=this.bitmapFont.size;this.atlasUVs=new Float32Array(t*4),this.cachedAtlasWidth=this.atlasColumns*this.charWidth,this.cachedAtlasHeight=Math.ceil(t/this.atlasColumns)*this.charHeight;let e=this.cachedAtlasWidth,i=this.cachedAtlasHeight,s=.5/e,r=.5/i;for(let a=0;a<t;a++){let l=a%this.atlasColumns,o=Math.floor(a/this.atlasColumns),h=(l*this.charWidth+s)/e,c=(o*this.charHeight+r)/i,d=((l+1)*this.charWidth-s)/e,f=((o+1)*this.charHeight-r)/i,u=a*4;this.atlasUVs[u]=h,this.atlasUVs[u+1]=c,this.atlasUVs[u+2]=d,this.atlasUVs[u+3]=f}}createAtlasTexture(){if(this.atlasCanvas)try{let t=this.gl,e=t.createTexture();if(!e)throw new Error("Unable to create texture");t.bindTexture(t.TEXTURE_2D,e),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,this.atlasCanvas),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),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),this.atlasTexture=e}catch(t){throw console.error("[TerminalGL] \u274C Failed to create atlas texture:",t),t}}clear(){let t=this.gl;t&&t.clear(t.COLOR_BUFFER_BIT)}parseColor(t){if(t.startsWith("#")){let e=t.slice(1),i=0,s=0,r=0;return e.length===3?(i=parseInt(e[0]+e[0],16),s=parseInt(e[1]+e[1],16),r=parseInt(e[2]+e[2],16)):e.length===6&&(i=parseInt(e.slice(0,2),16),s=parseInt(e.slice(2,4),16),r=parseInt(e.slice(4,6),16)),[i/255,s/255,r/255,1]}if(t.startsWith("rgb")){let e=t.match(/rgba?\(([^)]+)\)/);if(e){let i=e[1].split(",").map(s=>parseFloat(s.trim()));return[i[0]/255,i[1]/255,i[2]/255,i[3]!==void 0?i[3]:1]}}return[1,1,1,1]}setupResizeObserver(){if(!(typeof ResizeObserver>"u"))try{this.resizeObserver=new ResizeObserver(()=>{try{this.updateCanvasSize()}catch(t){console.error("[TerminalGL] \u274C Error in resize callback:",t)}}),this.resizeObserver.observe(this.parentElement),this.updateCanvasSize()}catch(t){console.error("[TerminalGL] \u274C Failed to setup ResizeObserver:",t)}}updateCanvasSize(){let t=this.parentElement.clientWidth,e=this.parentElement.clientHeight;if(t===0||e===0)return;let i=this.cols*this.cellWidth,s=this.rows*this.cellHeight;if(i===0||s===0)return;let r=t/i,a=e/s,l=Math.min(r,a),o;switch(this.scalingMode){case R.Integer:o=Math.max(1,Math.floor(l));break;case R.Half:o=Math.max(1,Math.floor(l*2)/2);break;case R.Quarter:o=Math.max(1,Math.floor(l*4)/4);break;case R.Eighth:o=Math.max(1,Math.floor(l*8)/8);break;case R.None:default:o=Math.max(.1,l);break}if(this.canvas.style.transform=`scale(${o})`,this.ambientEffectEnabled&&this.ambientEffectCanvas){let h=o*this.ambientEffectScale;this.ambientEffectCanvas.style.transform=`scale(${h})`}this.currentScale=o,this.onResizeCallback&&this.onResizeCallback(),this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}updateAmbientEffect(){!this.ambientEffectCanvas||!this.ambientEffectCtx||this.ambientEffectCtx.drawImage(this.canvas,0,0)}setPalette(t){let e=0;for(let i=0;i<Math.min(t.length,256);i++){let s=t[i];e=(e<<5)-e+s.r,e=(e<<5)-e+s.g,e=(e<<5)-e+s.b,e=(e<<5)-e+s.a,e=e|0}if(e!==this.paletteHash){this.paletteHash=e;for(let i=0;i<t.length&&i<256;i++){let s=t[i],r=i*4;this.paletteFloat[r]=s.r/255,this.paletteFloat[r+1]=s.g/255,this.paletteFloat[r+2]=s.b/255,this.paletteFloat[r+3]=s.a/255}this.updatePaletteTexture()}}updatePaletteTexture(){try{let t=this.gl;this.paletteTexture||(this.paletteTexture=t.createTexture()),t.bindTexture(t.TEXTURE_2D,this.paletteTexture);let e=new Uint8Array(256*4);for(let i=0;i<256;i++){let s=i*4;e[s]=this.paletteFloat[s]*255,e[s+1]=this.paletteFloat[s+1]*255,e[s+2]=this.paletteFloat[s+2]*255,e[s+3]=this.paletteFloat[s+3]*255}t.texImage2D(t.TEXTURE_2D,0,t.RGBA,256,1,0,t.RGBA,t.UNSIGNED_BYTE,e),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),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),t.bindTexture(t.TEXTURE_2D,null)}catch(t){throw console.error("[TerminalGL] \u274C Failed to update palette texture:",t),t}}renderDisplayData(t){try{if(!this.isReady()){console.warn("[TerminalGL] \u26A0\uFE0F Not ready: font not loaded yet");return}(t.width!==this.cols||t.height!==this.rows)&&this.resize(t.width,t.height),this.renderDirect(t),this.ambientEffectEnabled&&this.ambientEffectCanvas&&this.ambientEffectCtx&&this.updateAmbientEffect()}catch(e){console.error("[TerminalGL] \u274C Error rendering display data:",e)}}renderDirect(t){let e=this.gl;if(!this.staticPositionsInitialized&&!this.useInstancing&&this.precomputeStaticPositions(),this.canvasBgColor!==null){let i=this.parseColor(this.canvasBgColor);e.clearColor(i[0],i[1],i[2],i[3])}else e.clearColor(0,0,0,0);e.clear(e.COLOR_BUFFER_BIT),this.useInstancing?this.renderInstanced(t):this.renderDirectBuffers(t)}renderInstanced(t){let e=this.gl,i=this.instancedExtension;if(!this.instanceData||this.instanceData.length===0){console.warn("[TerminalGL] \u26A0\uFE0F Instance data not initialized, falling back to standard rendering"),this.useInstancing=!1,this.renderDirectBuffers(t);return}let s=t.width*t.height;if(this.useUint16Indices&&s>8191){console.warn(`[TerminalGL] \u26A0\uFE0F Terminal too large for instanced rendering with Uint16 (${s} cells > 8191). Falling back to standard rendering.`),this.useInstancing=!1,this.renderDirectBuffers(t);return}let r=0,a=this.instanceData.length/8;for(let f=0;f<t.height;f++)for(let u=0;u<t.width;u++){let m=t.cells[f*t.width+u],g=m.bgColorIndex,p=m.fgColorIndex,x=this.canvasBgColor!==null&&g===255,w=m.char===" "||p===255;if(!x){if(r>=a){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${a}`);break}let b=r*8;this.instanceData[b]=u,this.instanceData[b+1]=f,this.instanceData[b+2]=0,this.instanceData[b+3]=0,this.instanceData[b+4]=0,this.instanceData[b+5]=0,this.instanceData[b+6]=g,this.instanceData[b+7]=0,r++}if(!w){let b=m.char.charCodeAt(0),E=this.charCodeToAtlasIndex[b];if(E!==65535){if(r>=a){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${a}`);break}let A=E*4,y=r*8;this.instanceData[y]=u,this.instanceData[y+1]=f,this.instanceData[y+2]=this.atlasUVs[A],this.instanceData[y+3]=this.atlasUVs[A+1],this.instanceData[y+4]=this.atlasUVs[A+2],this.instanceData[y+5]=this.atlasUVs[A+3],this.instanceData[y+6]=0,this.instanceData[y+7]=p,r++}}}if(r===0){console.warn("[TerminalGL] \u26A0\uFE0F No instances to draw (all cells skipped)");return}e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.instanceData.subarray(0,r*8)),e.useProgram(this.program),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(this.aPosition),e.vertexAttribPointer(this.aPosition,2,e.FLOAT,!1,0,0),i.vertexAttribDivisorANGLE(this.aPosition,0),e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer);let l=8*Float32Array.BYTES_PER_ELEMENT,o=e.getAttribLocation(this.program,"aInstanceOffset");e.enableVertexAttribArray(o),e.vertexAttribPointer(o,2,e.FLOAT,!1,l,0),i.vertexAttribDivisorANGLE(o,1);let h=e.getAttribLocation(this.program,"aInstanceUVs");e.enableVertexAttribArray(h),e.vertexAttribPointer(h,4,e.FLOAT,!1,l,2*Float32Array.BYTES_PER_ELEMENT),i.vertexAttribDivisorANGLE(h,1);let c=e.getAttribLocation(this.program,"aInstanceColors");e.enableVertexAttribArray(c),e.vertexAttribPointer(c,2,e.FLOAT,!1,l,6*Float32Array.BYTES_PER_ELEMENT),i.vertexAttribDivisorANGLE(c,1),e.uniform2f(this.uResolution,this.canvas.width,this.canvas.height);let d=e.getUniformLocation(this.program,"uCellSize");e.uniform2f(d,this.cellWidth,this.cellHeight),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,this.atlasTexture),e.uniform1i(this.uTexture,0),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,this.paletteTexture),e.uniform1i(this.uPalette,1),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer),i.drawElementsInstancedANGLE(e.TRIANGLES,6,e.UNSIGNED_SHORT,0,r),i.vertexAttribDivisorANGLE(this.aPosition,0),i.vertexAttribDivisorANGLE(o,0),i.vertexAttribDivisorANGLE(h,0),i.vertexAttribDivisorANGLE(c,0)}renderDirectBuffers(t){let e=this.gl,i=t.width*t.height,s=i*2;if(s>this.maxCells){console.error(`[TerminalGL] \u274C Buffer overflow detected! Trying to render ${s} quads but buffer capacity is ${this.maxCells}. This should not happen - resize() should have reallocated.`);return}let r=0,a=0,l=t.cells,o=i;for(let m=0;m<o;m++){let g=l[m],p=g.bgColorIndex,w=this.canvasBgColor!==null&&p===255?255:p,b=0;for(let y=0;y<4;y++)this.renderTexCoords[r++]=b,this.renderTexCoords[r++]=b,this.renderColorIndices[a++]=w;let E=g.fgColorIndex,A=g.char;if(A===" "||E===255)for(let y=0;y<4;y++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[a++]=255;else{let y=A.charCodeAt(0),q=this.charCodeToAtlasIndex[y];if(q!==65535){let I=q*4,j=this.atlasUVs[I],Q=this.atlasUVs[I+1],Z=this.atlasUVs[I+2],K=this.atlasUVs[I+3];this.renderTexCoords[r++]=j,this.renderTexCoords[r++]=Q,this.renderColorIndices[a++]=E,this.renderTexCoords[r++]=Z,this.renderTexCoords[r++]=Q,this.renderColorIndices[a++]=E,this.renderTexCoords[r++]=j,this.renderTexCoords[r++]=K,this.renderColorIndices[a++]=E,this.renderTexCoords[r++]=Z,this.renderTexCoords[r++]=K,this.renderColorIndices[a++]=E}else for(let I=0;I<4;I++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[a++]=255}}(r!==o*2*4*2||a!==o*2*4)&&console.error("[TerminalGL] \u274C Buffer index mismatch!",{texIdx:r,colorIdx:a,expected:o*2*4}),e.useProgram(this.program),this.vao&&this.vaoExtension?(this.vaoExtension.bindVertexArrayOES(this.vao),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,a))):(e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.enableVertexAttribArray(this.aPosition),e.vertexAttribPointer(this.aPosition,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),e.enableVertexAttribArray(this.aTexCoord),e.vertexAttribPointer(this.aTexCoord,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferSubData(e.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,a)),e.enableVertexAttribArray(this.aColorIndex),e.vertexAttribPointer(this.aColorIndex,1,e.FLOAT,!1,0,0));let h=this.canvas.width,c=this.canvas.height;(this.cachedResolution[0]!==h||this.cachedResolution[1]!==c)&&(e.uniform2f(this.uResolution,h,c),this.cachedResolution[0]=h,this.cachedResolution[1]=c),this.cachedTextureUnit!==0&&(e.activeTexture(e.TEXTURE0),this.cachedTextureUnit=0),e.bindTexture(e.TEXTURE_2D,this.atlasTexture),this.cachedTextureUniform||(e.uniform1i(this.uTexture,0),this.cachedTextureUniform=!0),this.cachedPaletteUnit!==1&&(e.activeTexture(e.TEXTURE1),this.cachedPaletteUnit=1),e.bindTexture(e.TEXTURE_2D,this.paletteTexture),this.cachedPaletteUniform||(e.uniform1i(this.uPalette,1),this.cachedPaletteUniform=!0),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer);let f=t.width*t.height*2*6,u=this.useUint16Indices?e.UNSIGNED_SHORT:e.UNSIGNED_INT;e.drawElements(e.TRIANGLES,f,u,0)}resize(t,e){if(!Number.isInteger(t)||t<=0){console.error(`[TerminalGL] \u274C Invalid cols: ${t}. Must be positive integer.`);return}if(!Number.isInteger(e)||e<=0){console.error(`[TerminalGL] \u274C Invalid rows: ${e}. Must be positive integer.`);return}if(t===this.cols&&e===this.rows)return;let i=t*e,s=this.useUint16Indices?8191:262144;if(i>s){let c=Math.floor(Math.sqrt(s));console.error(`[TerminalGL] \u274C Cannot resize to ${t}\xD7${e} (${i} cells). Device limit: ${s} cells (max ~${c}\xD7${c}). ${this.useUint16Indices?"Device lacks OES_element_index_uint extension.":""}`);return}let r=U.checkCompatibility();i>r.recommendedMaxCells&&console.warn(`[TerminalGL] \u26A0\uFE0F Resizing to ${t}\xD7${e} (${i} cells) exceeds recommended limit (${r.recommendedMaxCells} cells). Performance may degrade.`),this.cols=t,this.rows=e,this.staticPositionsInitialized=!1;let a=this.cols*this.rows*2,l=Math.ceil(a*1.5),o=a>this.maxCells,h=this.maxCells>l*4;if(o||h){let c=this.maxCells;this.maxCells=l,this.renderPositions=new Float32Array(this.maxCells*4*2),this.renderTexCoords=new Float32Array(this.maxCells*4*2),this.renderColorIndices=new Float32Array(this.maxCells*4),this.useUint16Indices?this.renderIndices=new Uint16Array(this.maxCells*6):this.renderIndices=new Uint32Array(this.maxCells*6);let d=this.gl;this.useInstancing||(d.bindBuffer(d.ARRAY_BUFFER,this.positionBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderPositions.byteLength,d.DYNAMIC_DRAW)),d.bindBuffer(d.ARRAY_BUFFER,this.texCoordBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderTexCoords.byteLength,d.DYNAMIC_DRAW),d.bindBuffer(d.ARRAY_BUFFER,this.colorIndexBuffer),d.bufferData(d.ARRAY_BUFFER,this.renderColorIndices.byteLength,d.DYNAMIC_DRAW),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,this.indexBuffer),d.bufferData(d.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,d.DYNAMIC_DRAW),this.vaoExtension&&this.vao&&(this.vaoExtension.deleteVertexArrayOES(this.vao),this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),d.bindBuffer(d.ARRAY_BUFFER,this.positionBuffer),d.enableVertexAttribArray(this.aPosition),d.vertexAttribPointer(this.aPosition,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,this.texCoordBuffer),d.enableVertexAttribArray(this.aTexCoord),d.vertexAttribPointer(this.aTexCoord,2,d.FLOAT,!1,0,0),d.bindBuffer(d.ARRAY_BUFFER,this.colorIndexBuffer),d.enableVertexAttribArray(this.aColorIndex),d.vertexAttribPointer(this.aColorIndex,1,d.FLOAT,!1,0,0),d.bindBuffer(d.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null)));let f=o?"\u{1F4C8} Growing":"\u{1F4C9} Shrinking",u=c*160/1048576,m=this.maxCells*160/1048576;console.warn(`[TerminalGL] ${f} buffers: ${u.toFixed(2)}MB \u2192 ${m.toFixed(2)}MB (${c} \u2192 ${this.maxCells} quads)`)}this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.gl.viewport(0,0,this.canvas.width,this.canvas.height),this.ambientEffectEnabled&&this.ambientEffectCanvas&&(this.ambientEffectCanvas.width=this.canvas.width,this.ambientEffectCanvas.height=this.canvas.height,this.ambientEffectCanvas.style.width=`${this.canvas.width}px`,this.ambientEffectCanvas.style.height=`${this.canvas.height}px`),this.showGrid&&this.gridOverlay&&this.updateGridOverlay(),this.updateCanvasSize()}getCanvas(){return this.canvas}getGridSize(){return{cols:this.cols,rows:this.rows}}getCellWidth(){return this.cellWidth}getCellHeight(){return this.cellHeight}getCurrentScale(){return this.currentScale}getBaseDimensions(){return{width:this.cols*this.cellWidth,height:this.rows*this.cellHeight}}setOnResizeCallback(t){this.onResizeCallback=t}clearOnResizeCallback(){this.onResizeCallback=void 0}setScalingMode(t){this.scalingMode!==t&&(this.scalingMode=t,this.updateCanvasSize())}getScalingMode(){return this.scalingMode}setGrid(t){t.enabled?(this.gridOverlay?this.gridOverlay.setStyle({strokeColor:t.color,lineWidth:t.lineWidth}):this.gridOverlay=new T(this.containerDiv,{strokeColor:t.color??"rgba(144, 24, 24, 1)",lineWidth:t.lineWidth??1,zIndex:10}),this.showGrid=!0,this.gridOverlay.setVisible(!0),this.updateGridOverlay()):(this.showGrid=!1,this.gridOverlay&&this.gridOverlay.setVisible(!1))}isGridEnabled(){return this.showGrid}setAmbientEffect(t){typeof t=="boolean"?this.ambientEffectEnabled=t:(this.ambientEffectEnabled=!0,t.blur!==void 0&&(this.ambientEffectBlur=t.blur),t.scale!==void 0&&(this.ambientEffectScale=t.scale)),this.ambientEffectEnabled&&!this.ambientEffectCanvas&&this.createAmbientEffectCanvas(),this.ambientEffectCanvas&&(this.ambientEffectEnabled?(this.ambientEffectCanvas.style.display="block",this.ambientEffectCanvas.style.filter=`blur(${this.ambientEffectBlur}px)`,this.ambientEffectCanvas.style.transform=`scale(${this.ambientEffectScale})`,this.updateAmbientEffect()):this.ambientEffectCanvas.style.display="none")}createAmbientEffectCanvas(){this.ambientEffectCanvas||(this.ambientEffectCanvas=document.createElement("canvas"),this.ambientEffectCanvas.className="terminalgl-ambient-effect-canvas",this.ambientEffectCanvas.width=this.canvas.width,this.ambientEffectCanvas.height=this.canvas.height,this.ambientEffectCanvas.style.cssText=`
|
|
115
117
|
display: block !important;
|
|
116
118
|
position: absolute !important;
|
|
117
119
|
width: ${this.canvas.width}px !important;
|
|
@@ -122,7 +124,33 @@ var X=Object.defineProperty;var q=(y,t,e)=>t in y?X(y,t,{enumerable:!0,configura
|
|
|
122
124
|
transform: scale(${this.ambientEffectScale}) !important;
|
|
123
125
|
pointer-events: none !important;
|
|
124
126
|
z-index: 0 !important;
|
|
125
|
-
`,this.ambientEffectCtx=this.ambientEffectCanvas.getContext("2d",{alpha:!0}),this.containerDiv.insertBefore(this.ambientEffectCanvas,this.canvas),console.warn("[TerminalGL] \u{1F308} ambient effect canvas created dynamically"))}isAmbientEffectEnabled(){return this.ambientEffectEnabled}getAmbientEffectConfig(){return{enabled:this.ambientEffectEnabled,blur:this.ambientEffectBlur,scale:this.ambientEffectScale}}getCols(){return this.cols}getRows(){return this.rows}isReady(){return!!(this.bitmapFont&&this.atlasTexture&&this.program)}destroy(){this.dispose()}dispose(){this.resizeObserver&&this.resizeObserver.disconnect();let t=this.gl;this.program&&t.deleteProgram(this.program),this.positionBuffer&&t.deleteBuffer(this.positionBuffer),this.texCoordBuffer&&t.deleteBuffer(this.texCoordBuffer),this.colorIndexBuffer&&t.deleteBuffer(this.colorIndexBuffer),this.indexBuffer&&t.deleteBuffer(this.indexBuffer),this.instanceDataBuffer&&t.deleteBuffer(this.instanceDataBuffer),this.atlasTexture&&t.deleteTexture(this.atlasTexture),this.paletteTexture&&t.deleteTexture(this.paletteTexture),this.vao&&this.vaoExtension&&this.vaoExtension.deleteVertexArrayOES(this.vao);let e=t.getExtension("WEBGL_lose_context");e&&e.loseContext(),this.atlasCanvas&&(this.atlasCanvas.width=0,this.atlasCanvas.height=0,this.atlasCanvas=void 0),this.containerDiv.parentElement&&this.containerDiv.parentElement.removeChild(this.containerDiv),this.canvas.width=0,this.canvas.height=0,this.ambientEffectCanvas&&(this.ambientEffectCanvas.width=0,this.ambientEffectCanvas.height=0,this.ambientEffectCanvas=null),this.gridOverlay&&(this.gridOverlay.destroy(),this.gridOverlay=void 0),this.renderPositions=new Float32Array(0),this.renderTexCoords=new Float32Array(0),this.renderColorIndices=new Float32Array(0),this.renderIndices=new Uint32Array(0),this.paletteFloat=new Float32Array(0),this.atlasUVs=new Float32Array(0),this.instanceData=new Float32Array(0),this.templateQuadPositions=new Float32Array(0),this.templateQuadIndices=new Uint16Array(0)}};x(B,"TerminalGL");var L=B;var _=class _{constructor(t,e,s,i,r){n(this,"atlases");n(this,"charMap");n(this,"baseCharWidth");n(this,"baseCharHeight");n(this,"baseCellWidth");n(this,"baseCellHeight");n(this,"atlasColumns",16);n(this,"font");n(this,"SCALES",[1,2,4,8]);n(this,"colorCache",new Map);n(this,"MAX_CACHE_SIZE",512);this.font=t,this.baseCharWidth=e,this.baseCharHeight=s,this.baseCellWidth=i??e,this.baseCellHeight=r??s,this.charMap=new Map,this.atlases=new Map,this.generateAtlases()}generateAtlases(){this.atlasColumns=16;let t=0;for(let[e,s]of this.font){let i=t%this.atlasColumns,r=Math.floor(t/this.atlasColumns);this.charMap.set(e,{x:i*this.baseCharWidth,y:r*this.baseCharHeight});for(let a of this.SCALES){let l=this.getOrCreateAtlas(a),o=i*l.charWidth,h=r*l.charHeight;this.renderBitmapToAtlas(l,s,o,h)}t++}}getOrCreateAtlas(t){let e=this.atlases.get(t);if(!e){let s=this.baseCharWidth*t,i=this.baseCharHeight*t,a=Math.ceil(256/this.atlasColumns),l=document.createElement("canvas");l.width=this.atlasColumns*s,l.height=a*i;let o=l.getContext("2d",{alpha:!0,willReadFrequently:!1});if(!o)throw new Error(`Impossible de cr\xE9er le contexte 2D pour l'atlas ${t}x`);o.imageSmoothingEnabled=!1,e={canvas:l,ctx:o,scale:t,charWidth:s,charHeight:i},this.atlases.set(t,e)}return e}renderBitmapToAtlas(t,e,s,i){let r=t.scale,a=t.ctx;a.fillStyle="#ffffff";for(let l=0;l<Math.min(e.length,this.baseCharHeight);l++){let o=e[l];for(let h=0;h<this.baseCharWidth;h++){let c=1<<7-h;o&c&&a.fillRect(s+h*r,i+l*r,r,r)}}}drawChar(t,e,s,i,r,a,l){let o=this.charMap.get(e);if(!o)return;let h=`${e}:${l}`,c=this.colorCache.get(h);if(c)c.lastUsed=performance.now();else{let C=this.createColoredGlyph(e,l,o);if(!C)return;c={canvas:C,lastUsed:performance.now()},this.colorCache.set(h,c),this.colorCache.size>this.MAX_CACHE_SIZE&&this.evictLRU()}let d=r/this.baseCellWidth,f=a/this.baseCellHeight,u=(this.baseCellWidth-this.baseCharWidth)/2*d,m=(this.baseCellHeight-this.baseCharHeight)/2*f,b=this.baseCharWidth*d,g=this.baseCharHeight*f;t.drawImage(c.canvas,s+u,i+m,b,g)}createColoredGlyph(t,e,s){let i=this.atlases.get(1);if(!i)return null;let r=i.ctx.getImageData(s.x,s.y,this.baseCharWidth,this.baseCharHeight),a=r.data,l=this.hexToRgb(e);for(let c=0;c<a.length;c+=4)a[c+3]>0&&(a[c]=l.r,a[c+1]=l.g,a[c+2]=l.b);let o=document.createElement("canvas");o.width=this.baseCharWidth,o.height=this.baseCharHeight;let h=o.getContext("2d",{alpha:!0});return h?(h.imageSmoothingEnabled=!1,h.putImageData(r,0,0),o):null}hexToRgb(t){if(t.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return a?{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16)}:{r:255,g:255,b:255}}let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/.exec(t);if(e)return{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)};let i={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],yellow:[255,255,0],cyan:[0,255,255],magenta:[255,0,255]}[t.toLowerCase()];return i?{r:i[0],g:i[1],b:i[2]}:{r:255,g:255,b:255}}evictLRU(){let t=null,e=1/0;for(let[s,i]of this.colorCache)i.lastUsed<e&&(e=i.lastUsed,t=s);if(t){let s=this.colorCache.get(t);s&&(s.canvas.width=0,s.canvas.height=0),this.colorCache.delete(t)}}getAtlasCanvas(t=1){return this.atlases.get(t)?.canvas}getAllAtlases(){return this.atlases}getCharDimensions(){return{width:this.baseCharWidth,height:this.baseCharHeight}}getCharCount(){return this.charMap.size}hasChar(t){return this.charMap.has(t)}getAtlasDimensions(t=1){let e=this.atlases.get(t);if(e)return{width:e.canvas.width,height:e.canvas.height}}toDataURL(t=1,e="image/png"){return this.atlases.get(t)?.canvas.toDataURL(e)}getCacheStats(){return{size:this.colorCache.size,maxSize:this.MAX_CACHE_SIZE}}clearCache(){for(let t of this.colorCache.values())t.canvas.width=0,t.canvas.height=0;this.colorCache.clear()}destroy(){this.charMap.clear();for(let t of this.atlases.values())t.canvas.width=0,t.canvas.height=0;this.atlases.clear(),this.clearCache()}};x(_,"BitmapFontAtlas");var D=_;var W=class W{constructor(t,e={}){n(this,"canvas");n(this,"ctx");n(this,"parentElement");n(this,"cells");n(this,"cols",0);n(this,"rows",0);n(this,"fontSize");n(this,"fontFamily");n(this,"defaultFgColor");n(this,"defaultBgColor");n(this,"canvasBgColor");n(this,"cellWidth");n(this,"cellHeight");n(this,"offsetX",0);n(this,"offsetY",0);n(this,"fontType","web");n(this,"bitmapFont");n(this,"bitmapAtlas");n(this,"bitmapCharWidth",8);n(this,"bitmapCharHeight",8);n(this,"showDebugGrid");n(this,"debugGridColor");n(this,"gridOverlay");n(this,"fixedGridMode");n(this,"fixedCols");n(this,"fixedRows");n(this,"cellAspectRatio");n(this,"resizeObserver");n(this,"imageDataBuffer");n(this,"useImageDataRendering",!1);n(this,"paletteCache");if(!t)throw new Error("Render: L'\xE9l\xE9ment parent est requis");this.parentElement=t,this.fixedGridMode=!!(e.fixedCols&&e.fixedRows),this.fixedCols=e.fixedCols,this.fixedRows=e.fixedRows,this.cellAspectRatio=e.cellAspectRatio??10/14,this.cellWidth=e.cellWidth??10,this.cellHeight=e.cellHeight??14,this.fontSize=e.fontSize??12,this.fontFamily=e.fontFamily??"monospace",this.defaultFgColor=e.defaultFgColor??"#ffffff",this.defaultBgColor=e.defaultBgColor??"#000000",this.canvasBgColor=e.canvasBgColor!==void 0?e.canvasBgColor:null,this.showDebugGrid=e.showDebugGrid??!1,this.debugGridColor=e.debugGridColor??"rgba(144, 24, 24, 1)",this.canvas=document.createElement("canvas"),e.className&&(this.canvas.className=e.className),e.style&&Object.assign(this.canvas.style,e.style),this.canvas.style.imageRendering="auto",this.parentElement.appendChild(this.canvas);let s=this.canvas.getContext("2d",{alpha:!0,desynchronized:!1});if(!s)throw new Error("UTSPRender: Impossible d'obtenir le contexte 2D du canvas");this.ctx=s,this.calculateGridSize(),this.cells=this.createEmptyGrid(),this.showDebugGrid&&(this.gridOverlay=new T(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,zIndex:10})),this.enableAutoResize(),this.render()}calculateGridSize(){let t=this.parentElement.clientWidth||800,e=this.parentElement.clientHeight||600;if(this.fixedGridMode&&this.fixedCols&&this.fixedRows)if(this.cols=this.fixedCols,this.rows=this.fixedRows,this.fontType==="bitmap"){let a=t/this.cols,l=e/this.rows,o=this.bitmapCharWidth/this.bitmapCharHeight;a/l>o?(this.cellHeight=Math.floor(l),this.cellWidth=Math.floor(this.cellHeight*o)):(this.cellWidth=Math.floor(a),this.cellHeight=Math.floor(this.cellWidth/o))}else{let a=t/this.cols,l=e/this.rows;a/l>this.cellAspectRatio?(this.cellHeight=Math.floor(l*100)/100,this.cellWidth=Math.floor(this.cellHeight*this.cellAspectRatio*100)/100):(this.cellWidth=Math.floor(a*100)/100,this.cellHeight=Math.floor(this.cellWidth/this.cellAspectRatio*100)/100);let o=14,h=12;this.fontSize=Math.floor(this.cellHeight/o*h*100)/100}else this.cols=Math.floor(t/this.cellWidth),this.rows=Math.floor(e/this.cellHeight);let s=this.cols*this.cellWidth,i=this.rows*this.cellHeight;this.offsetX=Math.floor((t-s)/2),this.offsetY=Math.floor((e-i)/2);let r=window.devicePixelRatio||1;this.canvas.style.width=`${t}px`,this.canvas.style.height=`${e}px`,this.canvas.width=t*r,this.canvas.height=e*r,this.ctx.scale(r,r)}createEmptyGrid(){let t=[];for(let e=0;e<this.rows;e++){let s=[];for(let i=0;i<this.cols;i++)s.push({char:" ",fgColor:this.defaultFgColor,bgColor:this.defaultBgColor});t.push(s)}return t}enableAutoResize(){this.resizeObserver=new ResizeObserver(()=>{let t=this.cols,e=this.rows;if(this.calculateGridSize(),this.cols!==t||this.rows!==e){let s=this.cells;this.cells=this.createEmptyGrid();let i=Math.min(t,this.cols),r=Math.min(e,this.rows);for(let a=0;a<r;a++)for(let l=0;l<i;l++)this.cells[a][l]=s[a][l]}this.render()}),this.resizeObserver.observe(this.parentElement)}setCell(t,e,s,i,r){if(e<0||e>=this.rows||t<0||t>=this.cols)return;let a=s&&typeof s=="string"?s.charAt(0):" ";this.cells[e][t]={char:a,fgColor:i??this.defaultFgColor,bgColor:r??this.defaultBgColor}}getCell(t,e){return e<0||e>=this.rows||t<0||t>=this.cols?null:this.cells[e][t]}write(t,e,s,i,r){for(let a=0;a<s.length;a++)this.setCell(t+a,e,s[a],i,r)}fillRect(t,e,s,i,r=" ",a,l){for(let o=e;o<e+i;o++)for(let h=t;h<t+s;h++)this.setCell(h,o,r,a,l)}clear(){this.cells=this.createEmptyGrid()}setFromArray(t){let e=t.width*t.height;if(t.cells.length!==e)throw new Error(`Invalid array length: expected ${e} (${t.width}\xD7${t.height}), got ${t.cells.length}`);let s=0;for(let i=0;i<t.height;i++)for(let r=0;r<t.width;r++){let a=t.cells[s];i<this.rows&&r<this.cols&&this.setCell(r,i,a.char,a.fgColor,a.bgColor),s++}}render(){if(this.fontType==="bitmap"&&this.bitmapFont&&this.useImageDataRendering){this.renderWithImageData();return}this.renderClassic()}renderWithImageData(){if(!this.bitmapFont)return;let t=this.cols*this.bitmapCharWidth,e=this.rows*this.bitmapCharHeight;(!this.imageDataBuffer||this.imageDataBuffer.width!==t||this.imageDataBuffer.height!==e)&&(this.imageDataBuffer=this.ctx.createImageData(t,e));let s=this.imageDataBuffer.data;for(let o=0;o<this.rows;o++)for(let h=0;h<this.cols;h++){let c=this.cells[o][h],d=this.parseColorToRGB(c.bgColor),f=this.parseColorToRGB(c.fgColor),u=c.char.charCodeAt(0),m=this.bitmapFont.get(u);for(let b=0;b<this.bitmapCharHeight;b++)for(let g=0;g<this.bitmapCharWidth;g++){let C=h*this.bitmapCharWidth+g,p=((o*this.bitmapCharHeight+b)*t+C)*4,E=!1;if(m&&b<m.length){let R=m[b],v=1<<7-g;E=(R&v)!==0}E?(s[p]=f.r,s[p+1]=f.g,s[p+2]=f.b,s[p+3]=f.a):(s[p]=d.r,s[p+1]=d.g,s[p+2]=d.b,s[p+3]=d.a)}}this.canvasBgColor!==null?(this.ctx.fillStyle=this.canvasBgColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.imageSmoothingEnabled=!1,this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges";let i=document.createElement("canvas");i.width=t,i.height=e,i.getContext("2d").putImageData(this.imageDataBuffer,0,0);let a=this.cols*this.cellWidth,l=this.rows*this.cellHeight;this.ctx.drawImage(i,0,0,t,e,this.offsetX,this.offsetY,a,l),this.showDebugGrid&&this.drawDebugGrid()}parseColorToRGB(t){let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/.exec(t);if(e)return{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10),a:e[4]?Math.round(parseFloat(e[4])*255):255};if(t.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);if(a)return{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16),a:255}}let i={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],yellow:[255,255,0],cyan:[0,255,255],magenta:[255,0,255],transparent:[0,0,0]}[t.toLowerCase()];return i?{r:i[0],g:i[1],b:i[2],a:t==="transparent"?0:255}:{r:255,g:255,b:255,a:255}}renderClassic(){this.canvasBgColor!==null?(this.ctx.fillStyle=this.canvasBgColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.fontType==="bitmap"?(this.ctx.imageSmoothingEnabled=!1,this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges"):(this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.canvas.style.imageRendering="auto",this.ctx.font=`${this.fontSize}px ${this.fontFamily}`,this.ctx.textBaseline="top",this.ctx.textRendering="optimizeLegibility");for(let t=0;t<this.rows;t++)for(let e=0;e<this.cols;e++){let s=this.cells[t][e],i=Math.floor(this.offsetX+e*this.cellWidth),r=Math.floor(this.offsetY+t*this.cellHeight),a=Math.floor(this.offsetX+(e+1)*this.cellWidth),l=Math.floor(this.offsetY+(t+1)*this.cellHeight),o=a-i,h=l-r;if((this.canvasBgColor!==null||s.bgColor!==this.defaultBgColor)&&(this.ctx.fillStyle=s.bgColor,this.ctx.fillRect(i,r,o,h)),s.char!==" ")if(this.fontType==="bitmap"&&this.bitmapFont)this.drawBitmapChar(s.char,i,r,o,h,s.fgColor);else{this.ctx.fillStyle=s.fgColor;let d=i+(o-this.ctx.measureText(s.char).width)/2,f=r+(h-this.fontSize)/2;this.ctx.fillText(s.char,d,f)}}this.showDebugGrid&&this.drawDebugGrid()}drawBitmapChar(t,e,s,i,r,a){let l=t.charCodeAt(0);if(this.bitmapAtlas){this.bitmapAtlas.drawChar(this.ctx,l,e,s,i,r,a);return}if(!this.bitmapFont)return;let o=this.bitmapFont.get(l);if(!o)return;let h=i/this.bitmapCharWidth,c=r/this.bitmapCharHeight;this.ctx.fillStyle=a;for(let d=0;d<Math.min(o.length,this.bitmapCharHeight);d++){let f=o[d],u=s+d*c,m=s+(d+1)*c;for(let b=0;b<this.bitmapCharWidth;b++){let g=1<<7-b;if(f&g){let C=e+b*h,w=e+(b+1)*h;this.ctx.fillRect(C,u,w-C,m-u)}}}}drawDebugGrid(){if(!this.gridOverlay)return;let t=this.parentElement.clientWidth||800,e=this.parentElement.clientHeight||600;this.gridOverlay.update(this.cols,this.rows,this.cellWidth,this.cellHeight,t,e,this.offsetX,this.offsetY)}getCanvas(){return this.canvas}getContext(){return this.ctx}getDimensions(){return{cols:this.cols,rows:this.rows}}getCellDimensions(){return{cellWidth:this.cellWidth,cellHeight:this.cellHeight}}getCellWidth(){return this.cellWidth}getCellHeight(){return this.cellHeight}getOffsets(){return{offsetX:this.offsetX,offsetY:this.offsetY}}setDebugGrid(t){this.showDebugGrid=t,t&&!this.gridOverlay?(this.gridOverlay=new T(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,zIndex:10}),this.drawDebugGrid()):!t&&this.gridOverlay?(this.gridOverlay.destroy(),this.gridOverlay=void 0):t&&this.gridOverlay&&this.gridOverlay.setVisible(!0)}setCanvasBackgroundColor(t){this.canvasBgColor=t}getCanvasBackgroundColor(){return this.canvasBgColor}setFixedGrid(t,e,s){this.fixedGridMode=!0,this.fixedCols=t,this.fixedRows=e,s!==void 0&&(this.cellAspectRatio=s);let i=this.cols,r=this.rows,a=this.cells;if(this.calculateGridSize(),this.cols!==i||this.rows!==r){this.cells=this.createEmptyGrid();let l=Math.min(i,this.cols),o=Math.min(r,this.rows);for(let h=0;h<o;h++)for(let c=0;c<l;c++)this.cells[h][c]=a[h][c]}this.render()}setAdaptiveGrid(t,e){this.fixedGridMode=!1,this.fixedCols=void 0,this.fixedRows=void 0,t!==void 0&&(this.cellWidth=t),e!==void 0&&(this.cellHeight=e);let s=this.cols,i=this.rows,r=this.cells;if(this.calculateGridSize(),this.cols!==s||this.rows!==i){this.cells=this.createEmptyGrid();let a=Math.min(s,this.cols),l=Math.min(i,this.rows);for(let o=0;o<l;o++)for(let h=0;h<a;h++)this.cells[o][h]=r[o][h]}this.render()}isFixedGridMode(){return this.fixedGridMode}setDebugGridColor(t){this.debugGridColor=t}isDebugGridEnabled(){return this.showDebugGrid}setImageDataRendering(t){this.useImageDataRendering=t,t&&this.fontType==="bitmap"?console.warn("[Render] ImageData rendering enabled (optimized for benchmarks)"):t&&(console.warn("[Render] ImageData rendering requires bitmap font, keeping classic mode"),this.useImageDataRendering=!1)}isImageDataRenderingEnabled(){return this.useImageDataRendering}setWebFont(t,e){this.fontType="web",this.fontFamily=t,e!==void 0&&(this.fontSize=e),this.bitmapFont=void 0,this.bitmapAtlas=void 0,this.useImageDataRendering=!1,this.calculateGridSize(),this.cells=this.createEmptyGrid()}setBitmapFont(t,e,s,i,r){this.fontType="bitmap",this.bitmapFont=t,this.bitmapCharWidth=e,this.bitmapCharHeight=s,this.bitmapAtlas=new D(t,e,s,i,r),this.cellWidth=i,this.cellHeight=r,this.calculateGridSize(),this.cells=this.createEmptyGrid()}getFontType(){return this.fontType}getBitmapFont(){return this.bitmapFont}getBitmapCharDimensions(){return this.fontType!=="bitmap"?null:{width:this.bitmapCharWidth,height:this.bitmapCharHeight}}setPalette(t){this.paletteCache=t}renderDisplayData(t){if(!t||!t.cells||t.cells.length===0){console.warn("[Terminal2D] Empty display data");return}if(t.width===0||t.height===0){console.warn("[Terminal2D] Invalid display dimensions:",t.width,t.height);return}(t.width!==this.cols||t.height!==this.rows)&&this.setFixedGrid(t.width,t.height,this.cellAspectRatio);let e=this.paletteCache??t.palette;if(!e||e.length===0){console.error("[Terminal2D] No palette available (neither cached nor in display)");return}let s=x(i=>{if(i==null||isNaN(i))return console.warn("[Terminal2D] Invalid palette index (undefined/null/NaN):",i),this.defaultFgColor;if(i===255)return"rgba(0, 0, 0, 0)";if(i<0||i>=e.length)return console.warn(`[Terminal2D] Invalid palette index: ${i}`),this.defaultFgColor;let r=e[i];return!r||typeof r.r!="number"?(console.warn(`[Terminal2D] Corrupted palette entry at index ${i}:`,r),this.defaultFgColor):`rgba(${r.r}, ${r.g}, ${r.b}, ${r.a/255})`},"convertColor");for(let i=0;i<t.height&&i<this.rows;i++)for(let r=0;r<t.width&&r<this.cols;r++){let a=i*t.width+r;if(a>=t.cells.length)break;let l=t.cells[a];if(!l)continue;let o=s(l.fgColorIndex),h=s(l.bgColorIndex);this.setCell(r,i,l.char??" ",o,h)}this.render()}isReady(){return!0}getCols(){return this.cols}getRows(){return this.rows}resize(t,e){this.setFixedGrid(t,e)}destroy(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=void 0),this.gridOverlay&&(this.gridOverlay.destroy(),this.gridOverlay=void 0),this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}};x(W,"Terminal2D");var G=W;var O=class O{constructor(t,e={}){n(this,"container");n(this,"overlayDiv",null);n(this,"button",null);n(this,"options");n(this,"started",!1);n(this,"onStartCallback");this.container=t,this.options={buttonText:e.buttonText??"Click to Start",onStart:e.onStart??(()=>{}),backgroundColor:e.backgroundColor??"rgba(0, 0, 0, 0.8)",buttonColor:e.buttonColor??"#4a90d9",buttonHoverColor:e.buttonHoverColor??"#357abd",buttonTextColor:e.buttonTextColor??"#ffffff",zIndex:e.zIndex??1e3},this.onStartCallback=e.onStart,this.createOverlay()}createOverlay(){let t=window.getComputedStyle(this.container).position;t!=="relative"&&t!=="absolute"&&t!=="fixed"&&(this.container.style.position="relative"),this.overlayDiv=document.createElement("div"),this.overlayDiv.className="utsp-autoplay-overlay",this.overlayDiv.style.cssText=`
|
|
127
|
+
`,this.ambientEffectCtx=this.ambientEffectCanvas.getContext("2d",{alpha:!0}),this.containerDiv.insertBefore(this.ambientEffectCanvas,this.canvas),console.warn("[TerminalGL] \u{1F308} ambient effect canvas created dynamically"))}isAmbientEffectEnabled(){return this.ambientEffectEnabled}getAmbientEffectConfig(){return{enabled:this.ambientEffectEnabled,blur:this.ambientEffectBlur,scale:this.ambientEffectScale}}getCols(){return this.cols}getRows(){return this.rows}isReady(){return!!(this.fontLoaded&&this.atlasTexture&&this.program)}destroy(){this.dispose()}dispose(){this.resizeObserver&&this.resizeObserver.disconnect();let t=this.gl;this.program&&t.deleteProgram(this.program),this.positionBuffer&&t.deleteBuffer(this.positionBuffer),this.texCoordBuffer&&t.deleteBuffer(this.texCoordBuffer),this.colorIndexBuffer&&t.deleteBuffer(this.colorIndexBuffer),this.indexBuffer&&t.deleteBuffer(this.indexBuffer),this.instanceDataBuffer&&t.deleteBuffer(this.instanceDataBuffer),this.atlasTexture&&t.deleteTexture(this.atlasTexture),this.paletteTexture&&t.deleteTexture(this.paletteTexture),this.vao&&this.vaoExtension&&this.vaoExtension.deleteVertexArrayOES(this.vao);let e=t.getExtension("WEBGL_lose_context");e&&e.loseContext(),this.atlasCanvas&&(this.atlasCanvas.width=0,this.atlasCanvas.height=0,this.atlasCanvas=void 0),this.containerDiv.parentElement&&this.containerDiv.parentElement.removeChild(this.containerDiv),this.canvas.width=0,this.canvas.height=0,this.ambientEffectCanvas&&(this.ambientEffectCanvas.width=0,this.ambientEffectCanvas.height=0,this.ambientEffectCanvas=null),this.gridOverlay&&(this.gridOverlay.destroy(),this.gridOverlay=void 0),this.renderPositions=new Float32Array(0),this.renderTexCoords=new Float32Array(0),this.renderColorIndices=new Float32Array(0),this.renderIndices=new Uint32Array(0),this.paletteFloat=new Float32Array(0),this.atlasUVs=new Float32Array(0),this.instanceData=new Float32Array(0),this.templateQuadPositions=new Float32Array(0),this.templateQuadIndices=new Uint16Array(0)}};v(U,"TerminalGL");var P=U;var O=class O{constructor(t,e,i,s,r){n(this,"atlases");n(this,"charMap");n(this,"baseCharWidth");n(this,"baseCharHeight");n(this,"baseCellWidth");n(this,"baseCellHeight");n(this,"atlasColumns",16);n(this,"font");n(this,"SCALES",[1,2,4,8]);n(this,"colorCache",new Map);n(this,"MAX_CACHE_SIZE",512);this.font=t,this.baseCharWidth=e,this.baseCharHeight=i,this.baseCellWidth=s??e,this.baseCellHeight=r??i,this.charMap=new Map,this.atlases=new Map,this.generateAtlases()}generateAtlases(){this.atlasColumns=16;let t=0;for(let[e,i]of this.font){let s=t%this.atlasColumns,r=Math.floor(t/this.atlasColumns);this.charMap.set(e,{x:s*this.baseCharWidth,y:r*this.baseCharHeight});for(let a of this.SCALES){let l=this.getOrCreateAtlas(a),o=s*l.charWidth,h=r*l.charHeight;this.renderBitmapToAtlas(l,i,o,h)}t++}}getOrCreateAtlas(t){let e=this.atlases.get(t);if(!e){let i=this.baseCharWidth*t,s=this.baseCharHeight*t,a=Math.ceil(256/this.atlasColumns),l=document.createElement("canvas");l.width=this.atlasColumns*i,l.height=a*s;let o=l.getContext("2d",{alpha:!0,willReadFrequently:!1});if(!o)throw new Error(`Impossible de cr\xE9er le contexte 2D pour l'atlas ${t}x`);o.imageSmoothingEnabled=!1,e={canvas:l,ctx:o,scale:t,charWidth:i,charHeight:s},this.atlases.set(t,e)}return e}renderBitmapToAtlas(t,e,i,s){let r=t.scale,a=t.ctx;a.fillStyle="#ffffff";for(let l=0;l<Math.min(e.length,this.baseCharHeight);l++){let o=e[l];for(let h=0;h<this.baseCharWidth;h++){let c=1<<7-h;o&c&&a.fillRect(i+h*r,s+l*r,r,r)}}}drawChar(t,e,i,s,r,a,l){let o=this.charMap.get(e);if(!o)return;let h=`${e}:${l}`,c=this.colorCache.get(h);if(c)c.lastUsed=performance.now();else{let x=this.createColoredGlyph(e,l,o);if(!x)return;c={canvas:x,lastUsed:performance.now()},this.colorCache.set(h,c),this.colorCache.size>this.MAX_CACHE_SIZE&&this.evictLRU()}let d=r/this.baseCellWidth,f=a/this.baseCellHeight,u=(this.baseCellWidth-this.baseCharWidth)/2*d,m=(this.baseCellHeight-this.baseCharHeight)/2*f,g=this.baseCharWidth*d,p=this.baseCharHeight*f;t.drawImage(c.canvas,i+u,s+m,g,p)}createColoredGlyph(t,e,i){let s=this.atlases.get(1);if(!s)return null;let r=s.ctx.getImageData(i.x,i.y,this.baseCharWidth,this.baseCharHeight),a=r.data,l=this.hexToRgb(e);for(let c=0;c<a.length;c+=4)a[c+3]>0&&(a[c]=l.r,a[c+1]=l.g,a[c+2]=l.b);let o=document.createElement("canvas");o.width=this.baseCharWidth,o.height=this.baseCharHeight;let h=o.getContext("2d",{alpha:!0});return h?(h.imageSmoothingEnabled=!1,h.putImageData(r,0,0),o):null}hexToRgb(t){if(t.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);return a?{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16)}:{r:255,g:255,b:255}}let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/.exec(t);if(e)return{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)};let s={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],yellow:[255,255,0],cyan:[0,255,255],magenta:[255,0,255]}[t.toLowerCase()];return s?{r:s[0],g:s[1],b:s[2]}:{r:255,g:255,b:255}}evictLRU(){let t=null,e=1/0;for(let[i,s]of this.colorCache)s.lastUsed<e&&(e=s.lastUsed,t=i);if(t){let i=this.colorCache.get(t);i&&(i.canvas.width=0,i.canvas.height=0),this.colorCache.delete(t)}}getAtlasCanvas(t=1){return this.atlases.get(t)?.canvas}getAllAtlases(){return this.atlases}getCharDimensions(){return{width:this.baseCharWidth,height:this.baseCharHeight}}getCharCount(){return this.charMap.size}hasChar(t){return this.charMap.has(t)}getAtlasDimensions(t=1){let e=this.atlases.get(t);if(e)return{width:e.canvas.width,height:e.canvas.height}}toDataURL(t=1,e="image/png"){return this.atlases.get(t)?.canvas.toDataURL(e)}getCacheStats(){return{size:this.colorCache.size,maxSize:this.MAX_CACHE_SIZE}}clearCache(){for(let t of this.colorCache.values())t.canvas.width=0,t.canvas.height=0;this.colorCache.clear()}destroy(){this.charMap.clear();for(let t of this.atlases.values())t.canvas.width=0,t.canvas.height=0;this.atlases.clear(),this.clearCache()}};v(O,"BitmapFontAtlas");var S=O;var k=class k{constructor(t){n(this,"atlasCanvas",null);n(this,"atlasCtx",null);n(this,"atlasImage",null);n(this,"glyphWidth");n(this,"glyphHeight");n(this,"cellWidth");n(this,"cellHeight");n(this,"atlasBlocks");n(this,"atlasColumns");n(this,"maxCharCode");n(this,"isLoaded",!1);n(this,"colorCache",new Map);n(this,"MAX_CACHE_SIZE",1024);this.glyphWidth=t.glyphWidth,this.glyphHeight=t.glyphHeight,this.cellWidth=t.cellWidth??t.glyphWidth,this.cellHeight=t.cellHeight??t.glyphHeight,this.atlasBlocks=t.atlasBlocks,this.atlasColumns=M(t.atlasBlocks),this.maxCharCode=F(t.atlasBlocks)}async loadFromPNG(t){return new Promise((e,i)=>{let s=new Uint8Array(t),r=new Blob([s],{type:"image/png"}),a=URL.createObjectURL(r),l=new Image;l.onload=()=>{URL.revokeObjectURL(a);let o=this.atlasColumns*this.glyphWidth,h=this.atlasColumns*this.glyphHeight;if((l.width!==o||l.height!==h)&&console.warn(`ImageFontAtlas: Image size ${l.width}\xD7${l.height} doesn't match expected ${o}\xD7${h} for ${this.atlasBlocks} block(s) with ${this.glyphWidth}\xD7${this.glyphHeight} glyphs`),this.atlasCanvas=document.createElement("canvas"),this.atlasCanvas.width=l.width,this.atlasCanvas.height=l.height,this.atlasCtx=this.atlasCanvas.getContext("2d",{alpha:!0,willReadFrequently:!0}),!this.atlasCtx){i(new Error("Failed to create 2D context for atlas"));return}this.atlasCtx.imageSmoothingEnabled=!1,this.atlasCtx.drawImage(l,0,0),this.atlasImage=l,this.isLoaded=!0,e()},l.onerror=()=>{URL.revokeObjectURL(a),i(new Error("Failed to load PNG image for atlas"))},l.src=a})}isReady(){return this.isLoaded}getGlyphPosition(t){if(t<0||t>this.maxCharCode)return null;let{col:e,row:i}=D(t,this.atlasBlocks);return{x:e*this.glyphWidth,y:i*this.glyphHeight}}getCharUV(t){if(t<0||t>this.maxCharCode)return null;let{col:e,row:i}=D(t,this.atlasBlocks),s=e/this.atlasColumns,r=i/this.atlasColumns,a=(e+1)/this.atlasColumns,l=(i+1)/this.atlasColumns;return{u1:s,v1:r,u2:a,v2:l}}drawChar(t,e,i,s,r,a,l){if(!this.isLoaded||!this.atlasCanvas)return;let o=this.getGlyphPosition(e);if(!o)return;let h=`${e}:${l}`,c=this.colorCache.get(h);if(c)c.lastUsed=performance.now();else{let f=this.createColoredGlyph(e,l,o);if(!f)return;c={canvas:f,lastUsed:performance.now()},this.colorCache.set(h,c),this.colorCache.size>this.MAX_CACHE_SIZE&&this.evictLRU()}if(t.imageSmoothingEnabled=!1,r===this.cellWidth&&a===this.cellHeight){let f=Math.floor((this.cellWidth-this.glyphWidth)/2),u=Math.floor((this.cellHeight-this.glyphHeight)/2);t.drawImage(c.canvas,Math.floor(i)+f,Math.floor(s)+u)}else{let f=r/this.cellWidth,u=a/this.cellHeight,m=Math.floor((this.cellWidth-this.glyphWidth)/2*f),g=Math.floor((this.cellHeight-this.glyphHeight)/2*u),p=Math.ceil(this.glyphWidth*f),x=Math.ceil(this.glyphHeight*u);t.drawImage(c.canvas,Math.floor(i)+m,Math.floor(s)+g,p,x)}}createColoredGlyph(t,e,i){if(!this.atlasCtx)return null;let s=this.atlasCtx.getImageData(i.x,i.y,this.glyphWidth,this.glyphHeight),r=s.data,a=this.parseColor(e);for(let h=0;h<r.length;h+=4)r[h+3]>0&&(r[h]=a.r,r[h+1]=a.g,r[h+2]=a.b);let l=document.createElement("canvas");l.width=this.glyphWidth,l.height=this.glyphHeight;let o=l.getContext("2d",{alpha:!0});return o?(o.putImageData(s,0,0),l):null}parseColor(t){if(t.startsWith("#")){let i=t.slice(1);return i.length===3&&(i=i[0]+i[0]+i[1]+i[1]+i[2]+i[2]),{r:parseInt(i.slice(0,2),16),g:parseInt(i.slice(2,4),16),b:parseInt(i.slice(4,6),16)}}let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)/.exec(t);return e?{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10)}:{r:255,g:255,b:255}}evictLRU(){let t=null,e=1/0;for(let[i,s]of this.colorCache)s.lastUsed<e&&(e=s.lastUsed,t=i);if(t){let i=this.colorCache.get(t);i&&(i.canvas.width=0,i.canvas.height=0),this.colorCache.delete(t)}}getAtlasCanvas(){return this.atlasCanvas}getAtlasImage(){return this.atlasImage}getConfig(){return{glyphWidth:this.glyphWidth,glyphHeight:this.glyphHeight,cellWidth:this.cellWidth,cellHeight:this.cellHeight,atlasBlocks:this.atlasBlocks}}getGlyphDimensions(){return{width:this.glyphWidth,height:this.glyphHeight}}getCellDimensions(){return{width:this.cellWidth,height:this.cellHeight}}getAtlasDimensions(){return{width:this.atlasColumns*this.glyphWidth,height:this.atlasColumns*this.glyphHeight}}getAtlasColumns(){return this.atlasColumns}getMaxCharCode(){return this.maxCharCode}isValidCharCode(t){return t>=0&&t<=this.maxCharCode}clearCache(){for(let t of this.colorCache.values())t.canvas.width=0,t.canvas.height=0;this.colorCache.clear()}getCacheStats(){return{size:this.colorCache.size,maxSize:this.MAX_CACHE_SIZE}}destroy(){this.clearCache(),this.atlasCanvas&&(this.atlasCanvas.width=0,this.atlasCanvas.height=0,this.atlasCanvas=null),this.atlasCtx=null,this.atlasImage=null,this.isLoaded=!1}};v(k,"ImageFontAtlas");var L=k;import{ScalingMode as B}from"@utsp/types";var $=class ${constructor(t,e={}){n(this,"containerDiv");n(this,"canvas");n(this,"ctx");n(this,"parentElement");n(this,"cells");n(this,"cols",0);n(this,"rows",0);n(this,"fontSize");n(this,"fontFamily");n(this,"defaultFgColor");n(this,"defaultBgColor");n(this,"canvasBgColor");n(this,"cellWidth");n(this,"cellHeight");n(this,"offsetX",0);n(this,"offsetY",0);n(this,"fontType","web");n(this,"bitmapFont");n(this,"bitmapAtlas");n(this,"imageAtlas");n(this,"bitmapCharWidth",8);n(this,"bitmapCharHeight",8);n(this,"showDebugGrid");n(this,"debugGridColor");n(this,"gridOverlay");n(this,"fixedGridMode");n(this,"fixedCols");n(this,"fixedRows");n(this,"cellAspectRatio");n(this,"resizeObserver");n(this,"imageDataBuffer");n(this,"useImageDataRendering",!1);n(this,"paletteCache");n(this,"scalingMode",B.None);n(this,"currentScale",1);if(!t)throw new Error("Render: L'\xE9l\xE9ment parent est requis");this.parentElement=t,this.fixedGridMode=!!(e.fixedCols&&e.fixedRows),this.fixedCols=e.fixedCols,this.fixedRows=e.fixedRows,this.cellAspectRatio=e.cellAspectRatio??10/14,this.cellWidth=e.cellWidth??10,this.cellHeight=e.cellHeight??14,this.fontSize=e.fontSize??12,this.fontFamily=e.fontFamily??"monospace",this.defaultFgColor=e.defaultFgColor??"#ffffff",this.defaultBgColor=e.defaultBgColor??"#000000",this.canvasBgColor=e.canvasBgColor!==void 0?e.canvasBgColor:null,this.showDebugGrid=e.showDebugGrid??!1,this.debugGridColor=e.debugGridColor??"rgba(144, 24, 24, 1)",this.scalingMode=e.scalingMode??B.None,window.getComputedStyle(this.parentElement).position==="static"&&(this.parentElement.style.position="relative"),this.containerDiv=document.createElement("div"),this.containerDiv.className="terminal2d-container",this.containerDiv.style.cssText=`
|
|
128
|
+
position: absolute !important;
|
|
129
|
+
top: 0 !important;
|
|
130
|
+
left: 0 !important;
|
|
131
|
+
right: 0 !important;
|
|
132
|
+
bottom: 0 !important;
|
|
133
|
+
width: 100% !important;
|
|
134
|
+
height: 100% !important;
|
|
135
|
+
overflow: visible !important;
|
|
136
|
+
contain: layout style !important;
|
|
137
|
+
display: flex !important;
|
|
138
|
+
justify-content: center !important;
|
|
139
|
+
align-items: center !important;
|
|
140
|
+
isolation: isolate !important;
|
|
141
|
+
`,this.canvas=document.createElement("canvas"),this.canvas.className="terminal2d-canvas",e.className&&(this.canvas.className+=" "+e.className),e.style&&Object.assign(this.canvas.style,e.style),this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges",this.containerDiv.appendChild(this.canvas),this.parentElement.appendChild(this.containerDiv);let s=this.canvas.getContext("2d",{alpha:!0,desynchronized:!1});if(!s)throw new Error("UTSPRender: Impossible d'obtenir le contexte 2D du canvas");this.ctx=s,this.ctx.imageSmoothingEnabled=!1,this.calculateGridSize(),this.cells=this.createEmptyGrid(),this.showDebugGrid&&(this.gridOverlay=new T(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,zIndex:10})),this.enableAutoResize(),this.render()}calculateGridSize(){let t=this.parentElement.clientWidth||800,e=this.parentElement.clientHeight||600,i,s;if(this.fontType==="image"&&this.imageAtlas){let d=this.imageAtlas.getCellDimensions();i=d.width,s=d.height}else this.fontType==="bitmap"?(i=this.bitmapCharWidth,s=this.bitmapCharHeight):(i=Math.round(this.cellWidth)||10,s=Math.round(this.cellHeight)||14);this.cellWidth=Math.round(i),this.cellHeight=Math.round(s),this.fixedGridMode&&this.fixedCols&&this.fixedRows?(this.cols=this.fixedCols,this.rows=this.fixedRows):(this.cols=Math.floor(t/this.cellWidth)||1,this.rows=Math.floor(e/this.cellHeight)||1);let r=this.cols*this.cellWidth,a=this.rows*this.cellHeight,l=t/r,o=e/a,h=Math.min(l,o),c;switch(this.scalingMode){case B.Integer:c=Math.max(1,Math.floor(h));break;case B.Half:c=Math.max(.5,Math.floor(h*2)/2);break;case B.Quarter:c=Math.max(.25,Math.floor(h*4)/4);break;case B.Eighth:c=Math.max(.125,Math.floor(h*8)/8);break;case B.None:default:c=Math.max(.1,h);break}this.currentScale=c,this.canvas.width=r,this.canvas.height=a,this.ctx.imageSmoothingEnabled=!1,this.canvas.style.cssText=`
|
|
142
|
+
flex-shrink: 0 !important;
|
|
143
|
+
flex-grow: 0 !important;
|
|
144
|
+
width: ${r}px !important;
|
|
145
|
+
height: ${a}px !important;
|
|
146
|
+
image-rendering: pixelated !important;
|
|
147
|
+
image-rendering: crisp-edges !important;
|
|
148
|
+
-ms-interpolation-mode: nearest-neighbor !important;
|
|
149
|
+
transform-origin: center center !important;
|
|
150
|
+
transform: scale(${c}) !important;
|
|
151
|
+
will-change: transform !important;
|
|
152
|
+
backface-visibility: hidden !important;
|
|
153
|
+
`,this.fontType==="web"&&(this.fontSize=Math.round(this.cellHeight/14*12)),this.offsetX=0,this.offsetY=0}createEmptyGrid(){let t=[];for(let e=0;e<this.rows;e++){let i=[];for(let s=0;s<this.cols;s++)i.push({char:" ",fgColor:this.defaultFgColor,bgColor:this.defaultBgColor});t.push(i)}return t}enableAutoResize(){this.resizeObserver=new ResizeObserver(()=>{let t=this.cols,e=this.rows;if(this.calculateGridSize(),this.cols!==t||this.rows!==e){let i=this.cells;this.cells=this.createEmptyGrid();let s=Math.min(t,this.cols),r=Math.min(e,this.rows);for(let a=0;a<r;a++)for(let l=0;l<s;l++)this.cells[a][l]=i[a][l]}this.render()}),this.resizeObserver.observe(this.parentElement)}setCell(t,e,i,s,r){if(e<0||e>=this.rows||t<0||t>=this.cols)return;let a=i&&typeof i=="string"?i.charAt(0):" ";this.cells[e][t]={char:a,fgColor:s??this.defaultFgColor,bgColor:r??this.defaultBgColor}}getCell(t,e){return e<0||e>=this.rows||t<0||t>=this.cols?null:this.cells[e][t]}write(t,e,i,s,r){for(let a=0;a<i.length;a++)this.setCell(t+a,e,i[a],s,r)}fillRect(t,e,i,s,r=" ",a,l){for(let o=e;o<e+s;o++)for(let h=t;h<t+i;h++)this.setCell(h,o,r,a,l)}clear(){this.cells=this.createEmptyGrid()}setFromArray(t){let e=t.width*t.height;if(t.cells.length!==e)throw new Error(`Invalid array length: expected ${e} (${t.width}\xD7${t.height}), got ${t.cells.length}`);let i=0;for(let s=0;s<t.height;s++)for(let r=0;r<t.width;r++){let a=t.cells[i];s<this.rows&&r<this.cols&&this.setCell(r,s,a.char,a.fgColor,a.bgColor),i++}}render(){if(this.fontType==="bitmap"&&this.bitmapFont&&this.useImageDataRendering){this.renderWithImageData();return}this.renderClassic()}renderWithImageData(){if(!this.bitmapFont)return;let t=this.cols*this.bitmapCharWidth,e=this.rows*this.bitmapCharHeight;(!this.imageDataBuffer||this.imageDataBuffer.width!==t||this.imageDataBuffer.height!==e)&&(this.imageDataBuffer=this.ctx.createImageData(t,e));let i=this.imageDataBuffer.data;for(let o=0;o<this.rows;o++)for(let h=0;h<this.cols;h++){let c=this.cells[o][h],d=this.parseColorToRGB(c.bgColor),f=this.parseColorToRGB(c.fgColor),u=c.char.charCodeAt(0),m=this.bitmapFont.get(u);for(let g=0;g<this.bitmapCharHeight;g++)for(let p=0;p<this.bitmapCharWidth;p++){let x=h*this.bitmapCharWidth+p,b=((o*this.bitmapCharHeight+g)*t+x)*4,E=!1;if(m&&g<m.length){let A=m[g],y=1<<7-p;E=(A&y)!==0}E?(i[b]=f.r,i[b+1]=f.g,i[b+2]=f.b,i[b+3]=f.a):(i[b]=d.r,i[b+1]=d.g,i[b+2]=d.b,i[b+3]=d.a)}}this.canvasBgColor!==null?(this.ctx.fillStyle=this.canvasBgColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.imageSmoothingEnabled=!1,this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges";let s=document.createElement("canvas");s.width=t,s.height=e,s.getContext("2d").putImageData(this.imageDataBuffer,0,0);let a=this.cols*this.cellWidth,l=this.rows*this.cellHeight;this.ctx.drawImage(s,0,0,t,e,this.offsetX,this.offsetY,a,l),this.showDebugGrid&&this.drawDebugGrid()}parseColorToRGB(t){let e=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/.exec(t);if(e)return{r:parseInt(e[1],10),g:parseInt(e[2],10),b:parseInt(e[3],10),a:e[4]?Math.round(parseFloat(e[4])*255):255};if(t.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;t=t.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let a=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(t);if(a)return{r:parseInt(a[1],16),g:parseInt(a[2],16),b:parseInt(a[3],16),a:255}}let s={white:[255,255,255],black:[0,0,0],red:[255,0,0],green:[0,255,0],blue:[0,0,255],yellow:[255,255,0],cyan:[0,255,255],magenta:[255,0,255],transparent:[0,0,0]}[t.toLowerCase()];return s?{r:s[0],g:s[1],b:s[2],a:t==="transparent"?0:255}:{r:255,g:255,b:255,a:255}}renderClassic(){this.canvasBgColor!==null?(this.ctx.fillStyle=this.canvasBgColor,this.ctx.fillRect(0,0,this.canvas.width,this.canvas.height)):this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.fontType==="bitmap"||this.fontType==="image"?(this.ctx.imageSmoothingEnabled=!1,this.canvas.style.imageRendering="pixelated",this.canvas.style.imageRendering="crisp-edges"):(this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.canvas.style.imageRendering="auto",this.ctx.font=`${this.fontSize}px ${this.fontFamily}`,this.ctx.textBaseline="top",this.ctx.textRendering="optimizeLegibility");for(let t=0;t<this.rows;t++)for(let e=0;e<this.cols;e++){let i=this.cells[t][e],s=Math.floor(this.offsetX+e*this.cellWidth),r=Math.floor(this.offsetY+t*this.cellHeight),a=Math.floor(this.offsetX+(e+1)*this.cellWidth),l=Math.floor(this.offsetY+(t+1)*this.cellHeight),o=a-s,h=l-r;if((this.canvasBgColor!==null||i.bgColor!==this.defaultBgColor)&&(this.ctx.fillStyle=i.bgColor,this.ctx.fillRect(s,r,o,h)),i.char!==" ")if(this.fontType==="image"&&this.imageAtlas){let d=i.char.charCodeAt(0);this.imageAtlas.drawChar(this.ctx,d,s,r,o,h,i.fgColor)}else if(this.fontType==="bitmap"&&this.bitmapFont)this.drawBitmapChar(i.char,s,r,o,h,i.fgColor);else{this.ctx.fillStyle=i.fgColor;let d=s+(o-this.ctx.measureText(i.char).width)/2,f=r+(h-this.fontSize)/2;this.ctx.fillText(i.char,d,f)}}this.showDebugGrid&&this.drawDebugGrid()}drawBitmapChar(t,e,i,s,r,a){let l=t.charCodeAt(0);if(this.bitmapAtlas){this.bitmapAtlas.drawChar(this.ctx,l,e,i,s,r,a);return}if(!this.bitmapFont)return;let o=this.bitmapFont.get(l);if(!o)return;let h=s/this.bitmapCharWidth,c=r/this.bitmapCharHeight;this.ctx.fillStyle=a;for(let d=0;d<Math.min(o.length,this.bitmapCharHeight);d++){let f=o[d],u=i+d*c,m=i+(d+1)*c;for(let g=0;g<this.bitmapCharWidth;g++){let p=1<<7-g;if(f&p){let x=e+g*h,w=e+(g+1)*h;this.ctx.fillRect(x,u,w-x,m-u)}}}}drawDebugGrid(){if(!this.gridOverlay)return;let t=this.parentElement.clientWidth||800,e=this.parentElement.clientHeight||600;this.gridOverlay.update(this.cols,this.rows,this.cellWidth,this.cellHeight,t,e,this.offsetX,this.offsetY)}getCanvas(){return this.canvas}getContext(){return this.ctx}getDimensions(){return{cols:this.cols,rows:this.rows}}getCellDimensions(){return{cellWidth:this.cellWidth,cellHeight:this.cellHeight}}getCellWidth(){return this.cellWidth}getCellHeight(){return this.cellHeight}getCurrentScale(){return this.currentScale}getScalingMode(){return this.scalingMode}getOffsets(){return{offsetX:this.offsetX,offsetY:this.offsetY}}setDebugGrid(t){this.showDebugGrid=t,t&&!this.gridOverlay?(this.gridOverlay=new T(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,zIndex:10}),this.drawDebugGrid()):!t&&this.gridOverlay?(this.gridOverlay.destroy(),this.gridOverlay=void 0):t&&this.gridOverlay&&this.gridOverlay.setVisible(!0)}setCanvasBackgroundColor(t){this.canvasBgColor=t}getCanvasBackgroundColor(){return this.canvasBgColor}setFixedGrid(t,e,i){this.fixedGridMode=!0,this.fixedCols=t,this.fixedRows=e,i!==void 0&&(this.cellAspectRatio=i);let s=this.cols,r=this.rows,a=this.cells;if(this.calculateGridSize(),this.cols!==s||this.rows!==r){this.cells=this.createEmptyGrid();let l=Math.min(s,this.cols),o=Math.min(r,this.rows);for(let h=0;h<o;h++)for(let c=0;c<l;c++)this.cells[h][c]=a[h][c]}this.render()}setAdaptiveGrid(t,e){this.fixedGridMode=!1,this.fixedCols=void 0,this.fixedRows=void 0,t!==void 0&&(this.cellWidth=t),e!==void 0&&(this.cellHeight=e);let i=this.cols,s=this.rows,r=this.cells;if(this.calculateGridSize(),this.cols!==i||this.rows!==s){this.cells=this.createEmptyGrid();let a=Math.min(i,this.cols),l=Math.min(s,this.rows);for(let o=0;o<l;o++)for(let h=0;h<a;h++)this.cells[o][h]=r[o][h]}this.render()}isFixedGridMode(){return this.fixedGridMode}setDebugGridColor(t){this.debugGridColor=t}isDebugGridEnabled(){return this.showDebugGrid}setImageDataRendering(t){this.useImageDataRendering=t,t&&this.fontType==="bitmap"?console.warn("[Render] ImageData rendering enabled (optimized for benchmarks)"):t&&(console.warn("[Render] ImageData rendering requires bitmap font, keeping classic mode"),this.useImageDataRendering=!1)}isImageDataRenderingEnabled(){return this.useImageDataRendering}setWebFont(t,e){this.fontType="web",this.fontFamily=t,e!==void 0&&(this.fontSize=e),this.bitmapFont=void 0,this.bitmapAtlas=void 0,this.useImageDataRendering=!1,this.calculateGridSize(),this.cells=this.createEmptyGrid()}setBitmapFont(t,e,i,s,r){this.fontType="bitmap",this.bitmapFont=t,this.bitmapCharWidth=e,this.bitmapCharHeight=i,this.bitmapAtlas=new S(t,e,i,s,r),this.cellWidth=s,this.cellHeight=r,this.calculateGridSize(),this.cells=this.createEmptyGrid()}async setImageFont(t,e,i,s,r,a){this.fontType="image",this.imageAtlas=new L({glyphWidth:e,glyphHeight:i,cellWidth:s,cellHeight:r,atlasBlocks:a}),await this.imageAtlas.loadFromPNG(t),this.cellWidth=s,this.cellHeight=r,this.bitmapCharWidth=e,this.bitmapCharHeight=i,this.calculateGridSize(),this.cells=this.createEmptyGrid()}getFontType(){return this.fontType}getBitmapFont(){return this.bitmapFont}getBitmapCharDimensions(){return this.fontType!=="bitmap"?null:{width:this.bitmapCharWidth,height:this.bitmapCharHeight}}setPalette(t){this.paletteCache=t}renderDisplayData(t){if(!t||!t.cells||t.cells.length===0){console.warn("[Terminal2D] Empty display data");return}if(t.width===0||t.height===0){console.warn("[Terminal2D] Invalid display dimensions:",t.width,t.height);return}(t.width!==this.cols||t.height!==this.rows)&&this.setFixedGrid(t.width,t.height,this.cellAspectRatio);let e=this.paletteCache??t.palette;if(!e||e.length===0){console.error("[Terminal2D] No palette available (neither cached nor in display)");return}let i=v(s=>{if(s==null||isNaN(s))return console.warn("[Terminal2D] Invalid palette index (undefined/null/NaN):",s),this.defaultFgColor;if(s===255)return"rgba(0, 0, 0, 0)";if(s<0||s>=e.length)return console.warn(`[Terminal2D] Invalid palette index: ${s}`),this.defaultFgColor;let r=e[s];return!r||typeof r.r!="number"?(console.warn(`[Terminal2D] Corrupted palette entry at index ${s}:`,r),this.defaultFgColor):`rgba(${r.r}, ${r.g}, ${r.b}, ${r.a/255})`},"convertColor");for(let s=0;s<t.height&&s<this.rows;s++)for(let r=0;r<t.width&&r<this.cols;r++){let a=s*t.width+r;if(a>=t.cells.length)break;let l=t.cells[a];if(!l)continue;let o=i(l.fgColorIndex),h=i(l.bgColorIndex);this.setCell(r,s,l.char??" ",o,h)}this.render()}isReady(){return!0}getCols(){return this.cols}getRows(){return this.rows}resize(t,e){this.setFixedGrid(t,e)}destroy(){this.resizeObserver&&(this.resizeObserver.disconnect(),this.resizeObserver=void 0),this.gridOverlay&&(this.gridOverlay.destroy(),this.gridOverlay=void 0),this.containerDiv.parentElement&&this.containerDiv.parentElement.removeChild(this.containerDiv)}};v($,"Terminal2D");var z=$;var Y=class Y{constructor(t,e={}){n(this,"container");n(this,"overlayDiv",null);n(this,"button",null);n(this,"options");n(this,"started",!1);n(this,"onStartCallback");this.container=t,this.options={buttonText:e.buttonText??"Click to Start",onStart:e.onStart??(()=>{}),backgroundColor:e.backgroundColor??"rgba(0, 0, 0, 0.8)",buttonColor:e.buttonColor??"#4a90d9",buttonHoverColor:e.buttonHoverColor??"#357abd",buttonTextColor:e.buttonTextColor??"#ffffff",zIndex:e.zIndex??1e3},this.onStartCallback=e.onStart,this.createOverlay()}createOverlay(){let t=window.getComputedStyle(this.container).position;t!=="relative"&&t!=="absolute"&&t!=="fixed"&&(this.container.style.position="relative"),this.overlayDiv=document.createElement("div"),this.overlayDiv.className="utsp-autoplay-overlay",this.overlayDiv.style.cssText=`
|
|
126
154
|
position: absolute !important;
|
|
127
155
|
top: 0 !important;
|
|
128
156
|
left: 0 !important;
|
|
@@ -146,7 +174,7 @@ var X=Object.defineProperty;var q=(y,t,e)=>t in y?X(y,t,{enumerable:!0,configura
|
|
|
146
174
|
cursor: pointer !important;
|
|
147
175
|
transition: background-color 0.2s ease, transform 0.1s ease !important;
|
|
148
176
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3) !important;
|
|
149
|
-
`,this.button.addEventListener("mouseenter",()=>{this.button&&(this.button.style.backgroundColor=`${this.options.buttonHoverColor} !important`,this.button.style.transform="scale(1.05)")}),this.button.addEventListener("mouseleave",()=>{this.button&&(this.button.style.backgroundColor=`${this.options.buttonColor} !important`,this.button.style.transform="scale(1)")}),this.button.addEventListener("click",e=>{e.stopPropagation(),this.start()}),this.overlayDiv.addEventListener("click",()=>{this.start()}),this.overlayDiv.appendChild(this.button),this.container.appendChild(this.overlayDiv)}start(){this.started||(this.started=!0,this.overlayDiv&&(this.overlayDiv.style.transition="opacity 0.3s ease",this.overlayDiv.style.opacity="0",setTimeout(()=>{this.removeOverlay()},300)),this.onStartCallback&&this.onStartCallback())}isStarted(){return this.started}isVisible(){return this.overlayDiv!==null&&!this.started}removeOverlay(){this.overlayDiv&&this.overlayDiv.parentElement&&this.overlayDiv.parentElement.removeChild(this.overlayDiv),this.overlayDiv=null,this.button=null}setButtonText(t){this.options.buttonText=t,this.button&&(this.button.textContent=t)}setOnStart(t){this.onStartCallback=t}destroy(){this.removeOverlay(),this.started=!1}};
|
|
177
|
+
`,this.button.addEventListener("mouseenter",()=>{this.button&&(this.button.style.backgroundColor=`${this.options.buttonHoverColor} !important`,this.button.style.transform="scale(1.05)")}),this.button.addEventListener("mouseleave",()=>{this.button&&(this.button.style.backgroundColor=`${this.options.buttonColor} !important`,this.button.style.transform="scale(1)")}),this.button.addEventListener("click",e=>{e.stopPropagation(),this.start()}),this.overlayDiv.addEventListener("click",()=>{this.start()}),this.overlayDiv.appendChild(this.button),this.container.appendChild(this.overlayDiv)}start(){this.started||(this.started=!0,this.overlayDiv&&(this.overlayDiv.style.transition="opacity 0.3s ease",this.overlayDiv.style.opacity="0",setTimeout(()=>{this.removeOverlay()},300)),this.onStartCallback&&this.onStartCallback())}isStarted(){return this.started}isVisible(){return this.overlayDiv!==null&&!this.started}removeOverlay(){this.overlayDiv&&this.overlayDiv.parentElement&&this.overlayDiv.parentElement.removeChild(this.overlayDiv),this.overlayDiv=null,this.button=null}setButtonText(t){this.options.buttonText=t,this.button&&(this.button.textContent=t)}setOnStart(t){this.onStartCallback=t}destroy(){this.removeOverlay(),this.started=!1}};v(Y,"AutoplayOverlay");var N=Y;import{POST_PROCESS_DEFAULTS as G}from"@utsp/types";var X=class X{constructor(t,e){n(this,"canvas");n(this,"ctx");n(this,"containerDiv");n(this,"parentElement");n(this,"resizeObserver");n(this,"width",0);n(this,"height",0);n(this,"currentConfig",null);n(this,"configHash","");this.parentElement=t,this.containerDiv=document.createElement("div"),this.containerDiv.className="postprocess-container",this.containerDiv.style.cssText=`
|
|
150
178
|
position: absolute !important;
|
|
151
179
|
top: 0 !important;
|
|
152
180
|
left: 0 !important;
|
|
@@ -164,4 +192,4 @@ var X=Object.defineProperty;var q=(y,t,e)=>t in y?X(y,t,{enumerable:!0,configura
|
|
|
164
192
|
width: 100% !important;
|
|
165
193
|
height: 100% !important;
|
|
166
194
|
pointer-events: none !important;
|
|
167
|
-
`;let
|
|
195
|
+
`;let i=this.canvas.getContext("2d",{alpha:!0});if(!i)throw new Error("PostProcessOverlay: Failed to get 2D context");this.ctx=i,this.containerDiv.appendChild(this.canvas),this.parentElement.appendChild(this.containerDiv),this.resizeObserver=new ResizeObserver(r=>{for(let a of r){let{width:l,height:o}=a.contentRect;l>0&&o>0&&this.handleResize(Math.floor(l),Math.floor(o))}}),this.resizeObserver.observe(this.parentElement);let s=this.parentElement.getBoundingClientRect();s.width>0&&s.height>0&&this.handleResize(Math.floor(s.width),Math.floor(s.height)),this.hide()}handleResize(t,e){this.width===t&&this.height===e||(this.width=t,this.height=e,this.canvas.width=t,this.canvas.height=e,this.currentConfig&&this.isActive()&&this.render())}setConfig(t){let e=this.hashConfig(t);if(e!==this.configHash){if(this.currentConfig=t,this.configHash=e,!t||!this.isActiveConfig(t)){this.clear(),this.hide();return}this.show(),this.render()}}setScanlines(t){t===null?this.setConfig({scanlines:{enabled:!1}}):this.setConfig({scanlines:t})}setScanlinesEnabled(t){if(t){let e=this.currentConfig?.scanlines;this.setScanlines({enabled:!0,opacity:e?.opacity??G.scanlines.opacity,pattern:e?.pattern??"horizontal",spacing:e?.spacing??2,thickness:e?.thickness??1,color:e?.color??{r:0,g:0,b:0}})}else this.setScanlines({enabled:!1})}setScanlinesOpacity(t){let e=Math.max(0,Math.min(1,t)),i=this.currentConfig?.scanlines;this.setScanlines({enabled:i?.enabled??!0,opacity:e,pattern:i?.pattern??"horizontal",spacing:i?.spacing??2,thickness:i?.thickness??1,color:i?.color??{r:0,g:0,b:0}})}setScanlinesPattern(t){let e=this.currentConfig?.scanlines;this.setScanlines({enabled:e?.enabled??!0,opacity:e?.opacity??G.scanlines.opacity,pattern:t,spacing:e?.spacing??2,thickness:e?.thickness??1,color:e?.color??{r:0,g:0,b:0}})}setScanlinesSpacing(t){let e=Math.max(2,Math.round(t)),i=this.currentConfig?.scanlines;this.setScanlines({enabled:i?.enabled??!0,opacity:i?.opacity??G.scanlines.opacity,pattern:i?.pattern??"horizontal",spacing:e,thickness:i?.thickness??1,color:i?.color??{r:0,g:0,b:0}})}getConfig(){return this.currentConfig}isActive(){return this.isActiveConfig(this.currentConfig)}destroy(){this.resizeObserver.disconnect(),this.containerDiv.parentElement&&this.containerDiv.parentElement.removeChild(this.containerDiv)}syncWithRenderer(t){}resize(t,e){}setCellDimensions(t,e){}show(){this.containerDiv.style.display="block"}hide(){this.containerDiv.style.display="none"}clear(){this.ctx.clearRect(0,0,this.width,this.height)}isActiveConfig(t){return t?.scanlines?.enabled??!1}render(){this.clear(),this.currentConfig?.scanlines?.enabled&&this.renderScanlines(this.currentConfig.scanlines)}renderScanlines(t){let e=t.opacity??G.scanlines.opacity,i=t.pattern??"horizontal",s=t.spacing??2,r=t.thickness??1,a=t.color??{r:0,g:0,b:0},l=this.ctx;switch(l.fillStyle=`rgba(${a.r}, ${a.g}, ${a.b}, ${e})`,i){case"horizontal":for(let o=s-1;o<this.height;o+=s)l.fillRect(0,o,this.width,r);break;case"vertical":for(let o=s-1;o<this.width;o+=s)l.fillRect(o,0,r,this.height);break;case"grid":for(let o=s-1;o<this.height;o+=s)l.fillRect(0,o,this.width,r);for(let o=s-1;o<this.width;o+=s)l.fillRect(o,0,r,this.height);break}}hashConfig(t){return t?JSON.stringify(t):"null"}};v(X,"PostProcessOverlay");var V=X;var Ut="0.1.0";export{N as AutoplayOverlay,S as BitmapFontAtlas,H as DEFAULT_PALETTE,T as GridOverlay,L as ImageFontAtlas,V as PostProcessOverlay,R as ScalingMode,z as Terminal2D,P as TerminalGL,it as colorToPaletteIndex,M as getAtlasColumns,D as getCharGridPosition,F as getMaxCharCode,et as paletteIndexToColor,Ut as version};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@utsp/render",
|
|
3
|
-
"version": "0.9.0-nightly.
|
|
3
|
+
"version": "0.9.0-nightly.20251211152508.63e8b0d",
|
|
4
4
|
"description": "UTSP Render - Browser-based rendering engine for terminal graphics",
|
|
5
5
|
"author": "THP Software",
|
|
6
6
|
"license": "MIT",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"access": "public"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@utsp/types": "0.9.0-nightly.
|
|
52
|
+
"@utsp/types": "0.9.0-nightly.20251211152508.63e8b0d"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"typescript": "^5.6.3"
|