@utsp/render 0.1.1
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/LICENSE +21 -0
- package/README.md +36 -0
- package/dist/index.cjs +105 -0
- package/dist/index.d.ts +1058 -0
- package/dist/index.mjs +105 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thomas Piquet
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# @utsp/render
|
|
2
|
+
|
|
3
|
+
> ⚠️ **PROTOTYPE - NOT READY FOR PRODUCTION**
|
|
4
|
+
>
|
|
5
|
+
> This package is currently in early development and should **NOT** be used in production.
|
|
6
|
+
> The API is unstable and subject to breaking changes without notice.
|
|
7
|
+
|
|
8
|
+
Rendering system for UTSP (Universal Text Stream Protocol).
|
|
9
|
+
|
|
10
|
+
[](https://opensource.org/licenses/MIT)
|
|
11
|
+
|
|
12
|
+
## ⚠️ Development Status
|
|
13
|
+
|
|
14
|
+
**This is a prototype package under active development.**
|
|
15
|
+
|
|
16
|
+
- ❌ No stable API
|
|
17
|
+
- ❌ No documentation available yet
|
|
18
|
+
- ❌ Breaking changes expected
|
|
19
|
+
- ❌ Not recommended for production use
|
|
20
|
+
|
|
21
|
+
**Please check back later for updates or watch the repository for release announcements.**
|
|
22
|
+
|
|
23
|
+
## Installation
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install @utsp/render
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Repository
|
|
30
|
+
|
|
31
|
+
- [GitHub](https://github.com/thp-software/utsp)
|
|
32
|
+
- [Issues](https://github.com/thp-software/utsp/issues)
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT © 2025 Thomas Piquet
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";var B=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var $=Object.getOwnPropertyNames;var X=Object.prototype.hasOwnProperty;var V=(v,e,t)=>e in v?B(v,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):v[e]=t;var A=(v,e)=>B(v,"name",{value:e,configurable:!0});var k=(v,e)=>{for(var t in e)B(v,t,{get:e[t],enumerable:!0})},q=(v,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of $(e))!X.call(v,i)&&i!==t&&B(v,i,{get:()=>e[i],enumerable:!(s=N(e,i))||s.enumerable});return v};var j=v=>q(B({},"__esModule",{value:!0}),v);var a=(v,e,t)=>(V(v,typeof e!="symbol"?e+"":e,t),t);var Z={};k(Z,{BitmapFontAtlas:()=>I,DEFAULT_PALETTE:()=>U,GridOverlay:()=>w,Terminal2D:()=>L,TerminalGL:()=>D,colorToPaletteIndex:()=>z,paletteIndexToColor:()=>Y,version:()=>Q});module.exports=j(Z);var U=["#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 Y(v,e=U){return v<0||v>=e.length?"#ff00ff":e[v]}A(Y,"paletteIndexToColor");function z(v,e=U){let t=e.indexOf(v.toLowerCase());return t>=0?t:0}A(z,"colorToPaletteIndex");var _=class _{constructor(e,t){a(this,"canvas");a(this,"ctx");a(this,"container");a(this,"cols",0);a(this,"rows",0);a(this,"cellWidth",0);a(this,"cellHeight",0);a(this,"offsetX",0);a(this,"offsetY",0);a(this,"strokeColor","rgba(80, 80, 80, 0.4)");a(this,"lineWidth",1);a(this,"innerPadding",1);this.container=e,this.canvas=document.createElement("canvas"),this.canvas.className="grid-overlay-canvas";let s=t?.zIndex??10;this.canvas.style.cssText=`
|
|
2
|
+
position: absolute !important;
|
|
3
|
+
top: 0 !important;
|
|
4
|
+
left: 0 !important;
|
|
5
|
+
width: 100% !important;
|
|
6
|
+
height: 100% !important;
|
|
7
|
+
object-fit: contain !important;
|
|
8
|
+
z-index: ${s} !important;
|
|
9
|
+
pointer-events: none !important;
|
|
10
|
+
image-rendering: pixelated !important;
|
|
11
|
+
image-rendering: crisp-edges !important;
|
|
12
|
+
`,this.container.appendChild(this.canvas);let i=this.canvas.getContext("2d");if(!i)throw new Error("[GridOverlay] Impossible de cr\xE9er le contexte 2D");this.ctx=i,t&&(t.strokeColor&&(this.strokeColor=t.strokeColor),t.lineWidth!==void 0&&(this.lineWidth=t.lineWidth),t.innerPadding!==void 0&&(this.innerPadding=t.innerPadding))}setDimensions(e,t,s,i,r=0,n=0){this.cols=e,this.rows=t,this.cellWidth=s,this.cellHeight=i,this.offsetX=r,this.offsetY=n}setCanvasSize(e,t){let s=window.devicePixelRatio||1;this.canvas.width=e*s,this.canvas.height=t*s,this.ctx.setTransform(1,0,0,1,0,0),this.ctx.scale(s,s)}setStyle(e){e.strokeColor&&(this.strokeColor=e.strokeColor),e.lineWidth!==void 0&&(this.lineWidth=e.lineWidth),e.innerPadding!==void 0&&(this.innerPadding=e.innerPadding)}render(){if(!this.ctx)return;this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height),this.ctx.strokeStyle=this.strokeColor,this.ctx.lineWidth=this.lineWidth;let e=this.cols*this.cellWidth,t=this.rows*this.cellHeight;this.ctx.beginPath();for(let s=0;s<=this.cols;s++){let i=this.offsetX+s*this.cellWidth;if(s===0){let r=i+this.innerPadding+.5;this.ctx.moveTo(r,this.offsetY+this.innerPadding),this.ctx.lineTo(r,this.offsetY+t-this.innerPadding)}else if(s===this.cols){let r=i-this.innerPadding+.5;this.ctx.moveTo(r,this.offsetY+this.innerPadding),this.ctx.lineTo(r,this.offsetY+t-this.innerPadding)}else{let r=i+.5;this.ctx.moveTo(r,this.offsetY+this.innerPadding),this.ctx.lineTo(r,this.offsetY+t-this.innerPadding)}}for(let s=0;s<=this.rows;s++){let i=this.offsetY+s*this.cellHeight;if(s===0){let r=i+this.innerPadding+.5;this.ctx.moveTo(this.offsetX+this.innerPadding,r),this.ctx.lineTo(this.offsetX+e-this.innerPadding,r)}else if(s===this.rows){let r=i-this.innerPadding+.5;this.ctx.moveTo(this.offsetX+this.innerPadding,r),this.ctx.lineTo(this.offsetX+e-this.innerPadding,r)}else{let r=i+.5;this.ctx.moveTo(this.offsetX+this.innerPadding,r),this.ctx.lineTo(this.offsetX+e-this.innerPadding,r)}}this.ctx.stroke()}update(e,t,s,i,r,n,l=0,o=0){this.setDimensions(e,t,s,i,l,o),this.setCanvasSize(r,n),this.render()}setVisible(e){this.canvas.style.display=e?"block":"none"}destroy(){this.canvas.parentElement&&this.canvas.parentElement.removeChild(this.canvas)}getCanvas(){return this.canvas}};A(_,"GridOverlay");var w=_;var F=class F{constructor(e,t){a(this,"canvas");a(this,"gl");a(this,"parentElement");a(this,"containerDiv");a(this,"cols");a(this,"rows");a(this,"charWidth");a(this,"charHeight");a(this,"cellWidth",8);a(this,"cellHeight",8);a(this,"glyphOffsetX",0);a(this,"glyphOffsetY",0);a(this,"canvasBgColor");a(this,"showGrid");a(this,"supportsUint32Indices",!1);a(this,"useUint16Indices",!1);a(this,"gridOverlay");a(this,"bitmapFont");a(this,"atlasTexture",null);a(this,"atlasCanvas");a(this,"atlasColumns",0);a(this,"paletteTexture",null);a(this,"program",null);a(this,"positionBuffer",null);a(this,"texCoordBuffer",null);a(this,"colorIndexBuffer",null);a(this,"indexBuffer",null);a(this,"aPosition");a(this,"aTexCoord");a(this,"aColorIndex");a(this,"uResolution",null);a(this,"uTexture",null);a(this,"uPalette",null);a(this,"resizeObserver");a(this,"charCodeToAtlasIndex",new Uint16Array(65536).fill(65535));a(this,"atlasUVs",new Float32Array(0));a(this,"cachedAtlasWidth",0);a(this,"cachedAtlasHeight",0);a(this,"paletteFloat",new Float32Array(256*4));a(this,"maxCells",0);a(this,"renderPositions",new Float32Array(0));a(this,"renderTexCoords",new Float32Array(0));a(this,"renderColorIndices",new Float32Array(0));a(this,"renderIndices",new Uint32Array(0));a(this,"cachedResolution",[0,0]);a(this,"cachedTextureUnit",-1);a(this,"cachedPaletteUnit",-1);a(this,"cachedTextureUniform",!1);a(this,"cachedPaletteUniform",!1);a(this,"paletteHash",0);a(this,"staticPositionsInitialized",!1);a(this,"vaoExtension",null);a(this,"vao",null);a(this,"instancedExtension",null);a(this,"useInstancing",!1);a(this,"instanceDataBuffer",null);a(this,"instanceData",new Float32Array(0));a(this,"templateQuadPositions",new Float32Array(0));a(this,"templateQuadIndices",new Uint16Array(0));if(!e)throw new Error("TerminalGL: Parent element is required");if(!Number.isInteger(t.cols)||t.cols<=0)throw new Error(`TerminalGL: Invalid cols: ${t.cols}. Must be a positive integer.`);if(!Number.isInteger(t.rows)||t.rows<=0)throw new Error(`TerminalGL: Invalid rows: ${t.rows}. Must be a positive integer.`);let s=t.cols*t.rows,i=F.checkCompatibility();if(i.errors.length>0)throw new Error(`TerminalGL: WebGL incompatible - ${i.errors.join(", ")}`);let r=t.forceUint16??!1;if(r&&s>i.maxCellsUint16)throw new Error(`TerminalGL: Terminal size ${t.cols}\xD7${t.rows} (${s} cells) exceeds Uint16 limit of ${i.maxCellsUint16} cells. Maximum size with Uint16: ${Math.floor(Math.sqrt(i.maxCellsUint16))}\xD7${Math.floor(Math.sqrt(i.maxCellsUint16))} (or smaller rectangles like 127\xD764)`);if(!i.uint32Indices&&s>i.maxCellsUint16)throw new Error(`TerminalGL: Terminal size ${t.cols}\xD7${t.rows} (${s} cells) exceeds device limit of ${i.maxCellsUint16} cells (OES_element_index_uint not supported). Maximum size: ${Math.floor(Math.sqrt(i.maxCellsUint16))}\xD7${Math.floor(Math.sqrt(i.maxCellsUint16))}`);s>i.recommendedMaxCells&&console.warn(`[TerminalGL] \u26A0\uFE0F Large terminal size ${t.cols}\xD7${t.rows} (${s} cells) exceeds recommended limit of ${i.recommendedMaxCells} cells. Performance may be impacted.`),i.warnings.length>0&&console.warn("[TerminalGL] WebGL Compatibility Warnings:",i.warnings);let n=t.charWidth??8,l=t.charHeight??8;if(!Number.isFinite(n)||n<=0)throw new Error(`TerminalGL: Invalid charWidth: ${n}. 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=e,this.cols=t.cols,this.rows=t.rows,this.charWidth=n,this.charHeight=l,this.canvasBgColor=t.canvasBgColor!==void 0?t.canvasBgColor:null,this.showGrid=t.showGrid??!1;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=`
|
|
13
|
+
position: absolute !important;
|
|
14
|
+
top: 0 !important;
|
|
15
|
+
left: 0 !important;
|
|
16
|
+
width: 100% !important;
|
|
17
|
+
height: 100% !important;
|
|
18
|
+
overflow: hidden !important;
|
|
19
|
+
`,this.canvas=document.createElement("canvas"),this.canvas.className="terminalgl-webgl-canvas",this.canvas.width=this.cols*this.cellWidth,this.canvas.height=this.rows*this.cellHeight,this.canvas.style.cssText=`
|
|
20
|
+
position: absolute !important;
|
|
21
|
+
top: 0 !important;
|
|
22
|
+
left: 0 !important;
|
|
23
|
+
image-rendering: pixelated !important;
|
|
24
|
+
image-rendering: crisp-edges !important;
|
|
25
|
+
object-fit: contain !important;
|
|
26
|
+
`;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 ${i.maxCellsUint16} cells)`):console.warn(`[TerminalGL] \u{1F4CA} Using Uint32 indices (max ${i.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.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 e={webgl1:!1,uint32Indices:!1,maxTextureSize:0,maxViewportDims:[0,0],maxCellsUint16:0,maxCellsUint32:0,recommendedMaxCells:0,warnings:[],errors:[]},t=document.createElement("canvas"),s=t.getContext("webgl")||t.getContext("experimental-webgl");if(!s||!(s instanceof WebGLRenderingContext))return e.errors.push("\u274C WebGL 1.0 not supported by this browser/device"),e;let i=s;e.webgl1=!0;try{e.maxTextureSize=i.getParameter(i.MAX_TEXTURE_SIZE);let o=i.getParameter(i.MAX_VIEWPORT_DIMS);e.maxViewportDims=[o[0],o[1]]}catch(o){return e.errors.push(`\u274C Failed to query WebGL parameters: ${o}`),e}let r=i.getExtension("OES_element_index_uint");e.uint32Indices=!!r;let n=8;if(e.maxCellsUint16=Math.floor(65535/n),e.uint32Indices){let o=Math.floor(e.maxTextureSize*e.maxTextureSize/64);e.maxCellsUint32=Math.min(o,262144)}else e.maxCellsUint32=e.maxCellsUint16,e.warnings.push(`\u26A0\uFE0F OES_element_index_uint not supported - limited to ${e.maxCellsUint16} cells (e.g., 90\xD790 or 127\xD764)`);return e.recommendedMaxCells=Math.floor((e.uint32Indices?e.maxCellsUint32:e.maxCellsUint16)*.8),e.maxTextureSize<256?e.errors.push(`\u274C MAX_TEXTURE_SIZE too small: ${e.maxTextureSize} (minimum 256 required for font atlas)`):e.maxTextureSize<2048&&e.warnings.push(`\u26A0\uFE0F Small MAX_TEXTURE_SIZE: ${e.maxTextureSize} (may limit font atlas)`),(e.maxViewportDims[0]<1024||e.maxViewportDims[1]<768)&&e.warnings.push(`\u26A0\uFE0F Small MAX_VIEWPORT_DIMS: ${e.maxViewportDims[0]}\xD7${e.maxViewportDims[1]}`),i.getExtension("WEBGL_lose_context")||e.warnings.push("\u26A0\uFE0F WEBGL_lose_context not supported - may cause memory leaks"),e}initInstancedBuffers(){let e=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]),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.bufferData(e.ARRAY_BUFFER,this.templateQuadPositions,e.STATIC_DRAW),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer),e.bufferData(e.ELEMENT_ARRAY_BUFFER,this.templateQuadIndices,e.STATIC_DRAW);let t=this.cols*this.rows*2;this.instanceData=new Float32Array(t*8),e.bindBuffer(e.ARRAY_BUFFER,this.instanceDataBuffer),e.bufferData(e.ARRAY_BUFFER,this.instanceData.byteLength,e.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 e=this.cellWidth,t=this.cellHeight,s=this.glyphOffsetX,i=this.glyphOffsetY,r=this.charWidth,n=this.charHeight,l=0,o=0,h=0;for(let d=0;d<this.rows;d++){let f=Math.round(d*t),g=Math.round(f+t);for(let u=0;u<this.cols;u++){let m=Math.round(u*e),p=Math.round(m+e);this.renderPositions[l++]=m,this.renderPositions[l++]=f,this.renderPositions[l++]=p,this.renderPositions[l++]=f,this.renderPositions[l++]=m,this.renderPositions[l++]=g,this.renderPositions[l++]=p,this.renderPositions[l++]=g,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=Math.round(m+s),T=Math.round(f+i),b=Math.round(C+r),E=Math.round(T+n);this.renderPositions[l++]=C,this.renderPositions[l++]=T,this.renderPositions[l++]=b,this.renderPositions[l++]=T,this.renderPositions[l++]=C,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 e=this.gl,t;this.useInstancing?t=`
|
|
27
|
+
// Per-vertex attributes (template quad)
|
|
28
|
+
attribute vec2 aPosition; // Local quad position (0,0 to cellW,cellH)
|
|
29
|
+
|
|
30
|
+
// Per-instance attributes (different for each cell)
|
|
31
|
+
attribute vec2 aInstanceOffset; // Cell grid position (col, row)
|
|
32
|
+
attribute vec4 aInstanceUVs; // Atlas UVs (u1, v1, u2, v2)
|
|
33
|
+
attribute vec2 aInstanceColors; // (bgColorIdx, fgColorIdx)
|
|
34
|
+
|
|
35
|
+
uniform vec2 uResolution;
|
|
36
|
+
uniform vec2 uCellSize; // (cellWidth, cellHeight)
|
|
37
|
+
uniform sampler2D uPalette;
|
|
38
|
+
|
|
39
|
+
varying vec2 vTexCoord;
|
|
40
|
+
varying vec4 vColor;
|
|
41
|
+
|
|
42
|
+
void main() {
|
|
43
|
+
// Compute world position: instance offset + local quad position
|
|
44
|
+
vec2 worldPos = aInstanceOffset * uCellSize + aPosition;
|
|
45
|
+
vec2 clipSpace = (worldPos / uResolution) * 2.0 - 1.0;
|
|
46
|
+
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
|
|
47
|
+
|
|
48
|
+
// Interpolate UVs based on local position
|
|
49
|
+
vec2 localUV = aPosition / uCellSize; // (0,0) to (1,1)
|
|
50
|
+
vTexCoord = mix(
|
|
51
|
+
aInstanceUVs.xy, // top-left (u1, v1)
|
|
52
|
+
aInstanceUVs.zw, // bottom-right (u2, v2)
|
|
53
|
+
localUV
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Choose background or foreground color based on instance UVs
|
|
57
|
+
// If instance UVs are all (0,0), it's a background quad
|
|
58
|
+
float isBg = step(length(aInstanceUVs), 0.001);
|
|
59
|
+
float colorIndex = mix(aInstanceColors.y, aInstanceColors.x, isBg);
|
|
60
|
+
|
|
61
|
+
// Lookup color in palette
|
|
62
|
+
float paletteU = (colorIndex + 0.5) / 256.0;
|
|
63
|
+
vColor = texture2D(uPalette, vec2(paletteU, 0.5));
|
|
64
|
+
}
|
|
65
|
+
`:t=`
|
|
66
|
+
attribute vec2 aPosition;
|
|
67
|
+
attribute vec2 aTexCoord;
|
|
68
|
+
attribute float aColorIndex; // \u{1F680} Index dans palette (0-255)
|
|
69
|
+
|
|
70
|
+
uniform vec2 uResolution;
|
|
71
|
+
uniform sampler2D uPalette; // \u{1F680} Texture palette 256\xD71 RGBA
|
|
72
|
+
|
|
73
|
+
varying vec2 vTexCoord;
|
|
74
|
+
varying vec4 vColor;
|
|
75
|
+
|
|
76
|
+
void main() {
|
|
77
|
+
// Convert from pixels to clip space (-1 to 1)
|
|
78
|
+
vec2 clipSpace = (aPosition / uResolution) * 2.0 - 1.0;
|
|
79
|
+
gl_Position = vec4(clipSpace * vec2(1, -1), 0, 1);
|
|
80
|
+
|
|
81
|
+
vTexCoord = aTexCoord;
|
|
82
|
+
|
|
83
|
+
// \u{1F680} Lookup couleur dans la palette texture (GPU-side)
|
|
84
|
+
float paletteU = (aColorIndex + 0.5) / 256.0;
|
|
85
|
+
vColor = texture2D(uPalette, vec2(paletteU, 0.5));
|
|
86
|
+
}
|
|
87
|
+
`;let s=`
|
|
88
|
+
precision mediump float;
|
|
89
|
+
|
|
90
|
+
uniform sampler2D uTexture;
|
|
91
|
+
|
|
92
|
+
varying vec2 vTexCoord;
|
|
93
|
+
varying vec4 vColor;
|
|
94
|
+
|
|
95
|
+
void main() {
|
|
96
|
+
// If texCoord is (0, 0), it's a background without texture
|
|
97
|
+
if (vTexCoord.x == 0.0 && vTexCoord.y == 0.0) {
|
|
98
|
+
gl_FragColor = vColor;
|
|
99
|
+
} else {
|
|
100
|
+
// Textured character: multiply color by texture alpha
|
|
101
|
+
vec4 texColor = texture2D(uTexture, vTexCoord);
|
|
102
|
+
gl_FragColor = vec4(vColor.rgb * texColor.a, texColor.a);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
`,i=this.compileShader(e.VERTEX_SHADER,t),r=this.compileShader(e.FRAGMENT_SHADER,s);if(!i||!r)throw new Error("Shader compilation error");let n=e.createProgram();if(!n)throw new Error("Unable to create WebGL program");if(e.attachShader(n,i),e.attachShader(n,r),e.linkProgram(n),!e.getProgramParameter(n,e.LINK_STATUS)){let l=e.getProgramInfoLog(n);throw new Error("Program linking error: "+l)}this.program=n,this.cachedTextureUniform=!1,this.cachedPaletteUniform=!1,this.aPosition=e.getAttribLocation(n,"aPosition"),this.useInstancing?(this.aTexCoord=e.getAttribLocation(n,"aInstanceOffset"),this.aColorIndex=e.getAttribLocation(n,"aInstanceUVs"),this.instanceDataBuffer=e.createBuffer()):(this.aTexCoord=e.getAttribLocation(n,"aTexCoord"),this.aColorIndex=e.getAttribLocation(n,"aColorIndex")),this.uResolution=e.getUniformLocation(n,"uResolution"),this.uTexture=e.getUniformLocation(n,"uTexture"),this.uPalette=e.getUniformLocation(n,"uPalette"),this.positionBuffer=e.createBuffer(),this.texCoordBuffer=e.createBuffer(),this.colorIndexBuffer=e.createBuffer(),this.indexBuffer=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,this.positionBuffer),e.bufferData(e.ARRAY_BUFFER,this.renderPositions.byteLength,e.DYNAMIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,this.texCoordBuffer),e.bufferData(e.ARRAY_BUFFER,this.renderTexCoords.byteLength,e.DYNAMIC_DRAW),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.bufferData(e.ARRAY_BUFFER,this.renderColorIndices.byteLength,e.DYNAMIC_DRAW),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer),e.bufferData(e.ELEMENT_ARRAY_BUFFER,this.renderIndices.byteLength,e.DYNAMIC_DRAW),this.vaoExtension&&(this.vao=this.vaoExtension.createVertexArrayOES(),this.vao&&(this.vaoExtension.bindVertexArrayOES(this.vao),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.enableVertexAttribArray(this.aTexCoord),e.vertexAttribPointer(this.aTexCoord,2,e.FLOAT,!1,0,0),e.bindBuffer(e.ARRAY_BUFFER,this.colorIndexBuffer),e.enableVertexAttribArray(this.aColorIndex),e.vertexAttribPointer(this.aColorIndex,1,e.FLOAT,!1,0,0),e.bindBuffer(e.ELEMENT_ARRAY_BUFFER,this.indexBuffer),this.vaoExtension.bindVertexArrayOES(null))),e.enable(e.BLEND),e.blendFunc(e.SRC_ALPHA,e.ONE_MINUS_SRC_ALPHA),e.viewport(0,0,this.canvas.width,this.canvas.height)}compileShader(e,t){let s=this.gl,i=s.createShader(e);return i?(s.shaderSource(i,t),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 w(this.containerDiv,{strokeColor:"rgba(144, 24, 24, 1)",lineWidth:1,innerPadding:1,zIndex:10}),this.updateGridOverlay()}updateGridOverlay(){if(!this.gridOverlay)return;let e=parseFloat(this.canvas.style.width)||this.cols*this.charWidth,t=parseFloat(this.canvas.style.height)||this.rows*this.charHeight,s=e/this.cols,i=t/this.rows;this.gridOverlay.update(this.cols,this.rows,s,i,e,t,0,0)}setBitmapFont(e,t,s,i,r){this.bitmapFont=e,this.charWidth=Math.round(t),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(n){throw console.error("[TerminalGL] \u274C Failed to generate atlas:",n),n}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 e=Array.from(this.bitmapFont.keys()).sort((o,h)=>o-h),t=e.length;this.atlasColumns=Math.ceil(Math.sqrt(t));let s=Math.ceil(t/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 n=this.atlasCanvas.getContext("2d");if(!n)throw new Error("Unable to create 2D context for atlas");n.clearRect(0,0,i,r),n.fillStyle="#ffffff";let l=0;for(let o of e){let h=this.bitmapFont.get(o);if(!h)continue;let c=l%this.atlasColumns,d=Math.floor(l/this.atlasColumns),f=c*this.charWidth,g=d*this.charHeight;for(let u=0;u<Math.min(h.length,this.charHeight);u++){let m=h[u];for(let p=0;p<this.charWidth;p++){let C=1<<7-p;m&C&&n.fillRect(f+p,g+u,1,1)}}l++}this.createAtlasTexture()}buildCharCodeMap(){if(!this.bitmapFont)return;this.charCodeToAtlasIndex.fill(65535);let e=Array.from(this.bitmapFont.keys()).sort((t,s)=>t-s);for(let t=0;t<e.length;t++)this.charCodeToAtlasIndex[e[t]]=t}precomputeAtlasUVs(){if(!this.bitmapFont)return;let e=this.bitmapFont.size;this.atlasUVs=new Float32Array(e*4),this.cachedAtlasWidth=this.atlasColumns*this.charWidth,this.cachedAtlasHeight=Math.ceil(e/this.atlasColumns)*this.charHeight;let t=this.cachedAtlasWidth,s=this.cachedAtlasHeight,i=.5/t,r=.5/s;for(let n=0;n<e;n++){let l=n%this.atlasColumns,o=Math.floor(n/this.atlasColumns),h=(l*this.charWidth+i)/t,c=(o*this.charHeight+r)/s,d=((l+1)*this.charWidth-i)/t,f=((o+1)*this.charHeight-r)/s,g=n*4;this.atlasUVs[g]=h,this.atlasUVs[g+1]=c,this.atlasUVs[g+2]=d,this.atlasUVs[g+3]=f}}createAtlasTexture(){if(this.atlasCanvas)try{let e=this.gl,t=e.createTexture();if(!t)throw new Error("Unable to create texture");e.bindTexture(e.TEXTURE_2D,t),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,this.atlasCanvas),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),this.atlasTexture=t}catch(e){throw console.error("[TerminalGL] \u274C Failed to create atlas texture:",e),e}}clear(){let e=this.gl;e&&e.clear(e.COLOR_BUFFER_BIT)}parseColor(e){if(e.startsWith("#")){let t=e.slice(1),s=0,i=0,r=0;return t.length===3?(s=parseInt(t[0]+t[0],16),i=parseInt(t[1]+t[1],16),r=parseInt(t[2]+t[2],16)):t.length===6&&(s=parseInt(t.slice(0,2),16),i=parseInt(t.slice(2,4),16),r=parseInt(t.slice(4,6),16)),[s/255,i/255,r/255,1]}if(e.startsWith("rgb")){let t=e.match(/rgba?\(([^)]+)\)/);if(t){let s=t[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(e){console.error("[TerminalGL] \u274C Error in resize callback:",e)}}),this.resizeObserver.observe(this.parentElement),this.updateCanvasSize()}catch(e){console.error("[TerminalGL] \u274C Failed to setup ResizeObserver:",e)}}updateCanvasSize(){let e=this.parentElement.clientWidth,t=this.parentElement.clientHeight;if(e===0||t===0)return;let s=Math.floor(e/this.cellWidth),i=Math.floor(t/this.cellHeight),r=s/this.cols,n=i/this.rows,l=Math.min(r,n),o=Math.max(1,Math.floor(l)),h=this.cols*this.cellWidth*o,c=this.rows*this.cellHeight*o;this.canvas.style.width=`${h}px`,this.canvas.style.height=`${c}px`,this.showGrid&&this.gridOverlay&&this.updateGridOverlay()}setPalette(e){let t=0;for(let s=0;s<Math.min(e.length,256);s++){let i=e[s];t=(t<<5)-t+i.r,t=(t<<5)-t+i.g,t=(t<<5)-t+i.b,t=(t<<5)-t+i.a,t=t|0}if(t!==this.paletteHash){this.paletteHash=t;for(let s=0;s<e.length&&s<256;s++){let i=e[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 e=this.gl;this.paletteTexture||(this.paletteTexture=e.createTexture()),e.bindTexture(e.TEXTURE_2D,this.paletteTexture);let t=new Uint8Array(256*4);for(let s=0;s<256;s++){let i=s*4;t[i]=this.paletteFloat[i]*255,t[i+1]=this.paletteFloat[i+1]*255,t[i+2]=this.paletteFloat[i+2]*255,t[i+3]=this.paletteFloat[i+3]*255}e.texImage2D(e.TEXTURE_2D,0,e.RGBA,256,1,0,e.RGBA,e.UNSIGNED_BYTE,t),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.bindTexture(e.TEXTURE_2D,null)}catch(e){throw console.error("[TerminalGL] \u274C Failed to update palette texture:",e),e}}renderDisplayData(e){try{if(!this.isReady()){console.warn("[TerminalGL] \u26A0\uFE0F Not ready: font not loaded yet");return}(e.width!==this.cols||e.height!==this.rows)&&this.resize(e.width,e.height),this.renderDirect(e)}catch(t){console.error("[TerminalGL] \u274C Error rendering display data:",t)}}renderDirect(e){let t=this.gl;if(!this.staticPositionsInitialized&&!this.useInstancing&&this.precomputeStaticPositions(),this.canvasBgColor!==null){let s=this.parseColor(this.canvasBgColor);t.clearColor(s[0],s[1],s[2],s[3])}else t.clearColor(0,0,0,0);t.clear(t.COLOR_BUFFER_BIT),this.useInstancing?this.renderInstanced(e):this.renderDirectBuffers(e)}renderInstanced(e){let t=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(e);return}let i=e.width*e.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(e);return}let r=0,n=this.instanceData.length/8;for(let f=0;f<e.height;f++)for(let g=0;g<e.width;g++){let u=e.cells[f*e.width+g],m=u.bgColorIndex,p=u.fgColorIndex,C=this.canvasBgColor!==null&&m===255,T=u.char===" "||p===255;if(!C){if(r>=n){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${n}`);break}let b=r*8;this.instanceData[b]=g,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]=m,this.instanceData[b+7]=0,r++}if(!T){let b=u.char.charCodeAt(0),E=this.charCodeToAtlasIndex[b];if(E!==65535){if(r>=n){console.error(`[TerminalGL] \u274C Instance buffer overflow! instanceIdx=${r}, max=${n}`);break}let R=E*4,x=r*8;this.instanceData[x]=g,this.instanceData[x+1]=f,this.instanceData[x+2]=this.atlasUVs[R],this.instanceData[x+3]=this.atlasUVs[R+1],this.instanceData[x+4]=this.atlasUVs[R+2],this.instanceData[x+5]=this.atlasUVs[R+3],this.instanceData[x+6]=0,this.instanceData[x+7]=p,r++}}}if(r===0){console.warn("[TerminalGL] \u26A0\uFE0F No instances to draw (all cells skipped)");return}t.bindBuffer(t.ARRAY_BUFFER,this.instanceDataBuffer),t.bufferSubData(t.ARRAY_BUFFER,0,this.instanceData.subarray(0,r*8)),t.useProgram(this.program),t.bindBuffer(t.ARRAY_BUFFER,this.positionBuffer),t.enableVertexAttribArray(this.aPosition),t.vertexAttribPointer(this.aPosition,2,t.FLOAT,!1,0,0),s.vertexAttribDivisorANGLE(this.aPosition,0),t.bindBuffer(t.ARRAY_BUFFER,this.instanceDataBuffer);let l=8*Float32Array.BYTES_PER_ELEMENT,o=t.getAttribLocation(this.program,"aInstanceOffset");t.enableVertexAttribArray(o),t.vertexAttribPointer(o,2,t.FLOAT,!1,l,0),s.vertexAttribDivisorANGLE(o,1);let h=t.getAttribLocation(this.program,"aInstanceUVs");t.enableVertexAttribArray(h),t.vertexAttribPointer(h,4,t.FLOAT,!1,l,2*Float32Array.BYTES_PER_ELEMENT),s.vertexAttribDivisorANGLE(h,1);let c=t.getAttribLocation(this.program,"aInstanceColors");t.enableVertexAttribArray(c),t.vertexAttribPointer(c,2,t.FLOAT,!1,l,6*Float32Array.BYTES_PER_ELEMENT),s.vertexAttribDivisorANGLE(c,1),t.uniform2f(this.uResolution,this.canvas.width,this.canvas.height);let d=t.getUniformLocation(this.program,"uCellSize");t.uniform2f(d,this.cellWidth,this.cellHeight),t.activeTexture(t.TEXTURE0),t.bindTexture(t.TEXTURE_2D,this.atlasTexture),t.uniform1i(this.uTexture,0),t.activeTexture(t.TEXTURE1),t.bindTexture(t.TEXTURE_2D,this.paletteTexture),t.uniform1i(this.uPalette,1),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer),s.drawElementsInstancedANGLE(t.TRIANGLES,6,t.UNSIGNED_SHORT,0,r),s.vertexAttribDivisorANGLE(this.aPosition,0),s.vertexAttribDivisorANGLE(o,0),s.vertexAttribDivisorANGLE(h,0),s.vertexAttribDivisorANGLE(c,0)}renderDirectBuffers(e){let t=this.gl,s=e.width*e.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,n=0,l=e.cells,o=s;for(let u=0;u<o;u++){let m=l[u],p=m.bgColorIndex,T=this.canvasBgColor!==null&&p===255?255:p,b=0;for(let x=0;x<4;x++)this.renderTexCoords[r++]=b,this.renderTexCoords[r++]=b,this.renderColorIndices[n++]=T;let E=m.fgColorIndex,R=m.char;if(R===" "||E===255)for(let x=0;x<4;x++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[n++]=255;else{let x=R.charCodeAt(0),P=this.charCodeToAtlasIndex[x];if(P!==65535){let y=P*4,W=this.atlasUVs[y],S=this.atlasUVs[y+1],H=this.atlasUVs[y+2],O=this.atlasUVs[y+3];this.renderTexCoords[r++]=W,this.renderTexCoords[r++]=S,this.renderColorIndices[n++]=E,this.renderTexCoords[r++]=H,this.renderTexCoords[r++]=S,this.renderColorIndices[n++]=E,this.renderTexCoords[r++]=W,this.renderTexCoords[r++]=O,this.renderColorIndices[n++]=E,this.renderTexCoords[r++]=H,this.renderTexCoords[r++]=O,this.renderColorIndices[n++]=E}else for(let y=0;y<4;y++)this.renderTexCoords[r++]=0,this.renderTexCoords[r++]=0,this.renderColorIndices[n++]=255}}(r!==o*2*4*2||n!==o*2*4)&&console.error("[TerminalGL] \u274C Buffer index mismatch!",{texIdx:r,colorIdx:n,expected:o*2*4}),t.useProgram(this.program),this.vao&&this.vaoExtension?(this.vaoExtension.bindVertexArrayOES(this.vao),t.bindBuffer(t.ARRAY_BUFFER,this.texCoordBuffer),t.bufferSubData(t.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.bufferSubData(t.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,n))):(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.bufferSubData(t.ARRAY_BUFFER,0,this.renderTexCoords.subarray(0,r)),t.enableVertexAttribArray(this.aTexCoord),t.vertexAttribPointer(this.aTexCoord,2,t.FLOAT,!1,0,0),t.bindBuffer(t.ARRAY_BUFFER,this.colorIndexBuffer),t.bufferSubData(t.ARRAY_BUFFER,0,this.renderColorIndices.subarray(0,n)),t.enableVertexAttribArray(this.aColorIndex),t.vertexAttribPointer(this.aColorIndex,1,t.FLOAT,!1,0,0));let h=this.canvas.width,c=this.canvas.height;(this.cachedResolution[0]!==h||this.cachedResolution[1]!==c)&&(t.uniform2f(this.uResolution,h,c),this.cachedResolution[0]=h,this.cachedResolution[1]=c),this.cachedTextureUnit!==0&&(t.activeTexture(t.TEXTURE0),this.cachedTextureUnit=0),t.bindTexture(t.TEXTURE_2D,this.atlasTexture),this.cachedTextureUniform||(t.uniform1i(this.uTexture,0),this.cachedTextureUniform=!0),this.cachedPaletteUnit!==1&&(t.activeTexture(t.TEXTURE1),this.cachedPaletteUnit=1),t.bindTexture(t.TEXTURE_2D,this.paletteTexture),this.cachedPaletteUniform||(t.uniform1i(this.uPalette,1),this.cachedPaletteUniform=!0),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this.indexBuffer);let f=e.width*e.height*2*6,g=this.useUint16Indices?t.UNSIGNED_SHORT:t.UNSIGNED_INT;t.drawElements(t.TRIANGLES,f,g,0)}resize(e,t){if(!Number.isInteger(e)||e<=0){console.error(`[TerminalGL] \u274C Invalid cols: ${e}. Must be positive integer.`);return}if(!Number.isInteger(t)||t<=0){console.error(`[TerminalGL] \u274C Invalid rows: ${t}. Must be positive integer.`);return}if(e===this.cols&&t===this.rows)return;let s=e*t,i=this.useUint16Indices?8191:262144;if(s>i){let c=Math.floor(Math.sqrt(i));console.error(`[TerminalGL] \u274C Cannot resize to ${e}\xD7${t} (${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 ${e}\xD7${t} (${s} cells) exceeds recommended limit (${r.recommendedMaxCells} cells). Performance may degrade.`),this.cols=e,this.rows=t,this.staticPositionsInitialized=!1;let n=this.cols*this.rows*2,l=Math.ceil(n*1.5),o=n>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",g=c*160/1048576,u=this.maxCells*160/1048576;console.warn(`[TerminalGL] ${f} buffers: ${g.toFixed(2)}MB \u2192 ${u.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.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}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 e=this.gl;this.program&&e.deleteProgram(this.program),this.positionBuffer&&e.deleteBuffer(this.positionBuffer),this.texCoordBuffer&&e.deleteBuffer(this.texCoordBuffer),this.colorIndexBuffer&&e.deleteBuffer(this.colorIndexBuffer),this.indexBuffer&&e.deleteBuffer(this.indexBuffer),this.instanceDataBuffer&&e.deleteBuffer(this.instanceDataBuffer),this.atlasTexture&&e.deleteTexture(this.atlasTexture),this.paletteTexture&&e.deleteTexture(this.paletteTexture),this.vao&&this.vaoExtension&&this.vaoExtension.deleteVertexArrayOES(this.vao);let t=e.getExtension("WEBGL_lose_context");t&&t.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.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)}};A(F,"TerminalGL");var D=F;var G=class G{constructor(e,t,s,i,r){a(this,"atlases");a(this,"charMap");a(this,"baseCharWidth");a(this,"baseCharHeight");a(this,"baseCellWidth");a(this,"baseCellHeight");a(this,"atlasColumns",16);a(this,"font");a(this,"SCALES",[1,2,4,8]);a(this,"colorCache",new Map);a(this,"MAX_CACHE_SIZE",512);this.font=e,this.baseCharWidth=t,this.baseCharHeight=s,this.baseCellWidth=i??t,this.baseCellHeight=r??s,this.charMap=new Map,this.atlases=new Map,this.generateAtlases()}generateAtlases(){this.atlasColumns=16;let e=0;for(let[t,s]of this.font){let i=e%this.atlasColumns,r=Math.floor(e/this.atlasColumns);this.charMap.set(t,{x:i*this.baseCharWidth,y:r*this.baseCharHeight});for(let n of this.SCALES){let l=this.getOrCreateAtlas(n),o=i*l.charWidth,h=r*l.charHeight;this.renderBitmapToAtlas(l,s,o,h)}e++}}getOrCreateAtlas(e){let t=this.atlases.get(e);if(!t){let s=this.baseCharWidth*e,i=this.baseCharHeight*e,n=Math.ceil(256/this.atlasColumns),l=document.createElement("canvas");l.width=this.atlasColumns*s,l.height=n*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 ${e}x`);o.imageSmoothingEnabled=!1,t={canvas:l,ctx:o,scale:e,charWidth:s,charHeight:i},this.atlases.set(e,t)}return t}renderBitmapToAtlas(e,t,s,i){let r=e.scale,n=e.ctx;n.fillStyle="#ffffff";for(let l=0;l<Math.min(t.length,this.baseCharHeight);l++){let o=t[l];for(let h=0;h<this.baseCharWidth;h++){let c=1<<7-h;o&c&&n.fillRect(s+h*r,i+l*r,r,r)}}}drawChar(e,t,s,i,r,n,l){let o=this.charMap.get(t);if(!o)return;let h=`${t}:${l}`,c=this.colorCache.get(h);if(c)c.lastUsed=performance.now();else{let C=this.createColoredGlyph(t,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=n/this.baseCellHeight,g=(this.baseCellWidth-this.baseCharWidth)/2*d,u=(this.baseCellHeight-this.baseCharHeight)/2*f,m=this.baseCharWidth*d,p=this.baseCharHeight*f;e.drawImage(c.canvas,s+g,i+u,m,p)}createColoredGlyph(e,t,s){let i=this.atlases.get(1);if(!i)return null;let r=i.ctx.getImageData(s.x,s.y,this.baseCharWidth,this.baseCharHeight),n=r.data,l=this.hexToRgb(t);for(let c=0;c<n.length;c+=4)n[c+3]>0&&(n[c]=l.r,n[c+1]=l.g,n[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(e){if(e.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;e=e.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let n=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);return n?{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[3],16)}:{r:255,g:255,b:255}}let t=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*[\d.]+)?\)/.exec(e);if(t)return{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[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]}[e.toLowerCase()];return i?{r:i[0],g:i[1],b:i[2]}:{r:255,g:255,b:255}}evictLRU(){let e=null,t=1/0;for(let[s,i]of this.colorCache)i.lastUsed<t&&(t=i.lastUsed,e=s);if(e){let s=this.colorCache.get(e);s&&(s.canvas.width=0,s.canvas.height=0),this.colorCache.delete(e)}}getAtlasCanvas(e=1){return this.atlases.get(e)?.canvas}getAllAtlases(){return this.atlases}getCharDimensions(){return{width:this.baseCharWidth,height:this.baseCharHeight}}getCharCount(){return this.charMap.size}hasChar(e){return this.charMap.has(e)}getAtlasDimensions(e=1){let t=this.atlases.get(e);if(t)return{width:t.canvas.width,height:t.canvas.height}}toDataURL(e=1,t="image/png"){return this.atlases.get(e)?.canvas.toDataURL(t)}getCacheStats(){return{size:this.colorCache.size,maxSize:this.MAX_CACHE_SIZE}}clearCache(){for(let e of this.colorCache.values())e.canvas.width=0,e.canvas.height=0;this.colorCache.clear()}destroy(){this.charMap.clear();for(let e of this.atlases.values())e.canvas.width=0,e.canvas.height=0;this.atlases.clear(),this.clearCache()}};A(G,"BitmapFontAtlas");var I=G;var M=class M{constructor(e,t={}){a(this,"canvas");a(this,"ctx");a(this,"parentElement");a(this,"cells");a(this,"cols",0);a(this,"rows",0);a(this,"fontSize");a(this,"fontFamily");a(this,"defaultFgColor");a(this,"defaultBgColor");a(this,"canvasBgColor");a(this,"cellWidth");a(this,"cellHeight");a(this,"offsetX",0);a(this,"offsetY",0);a(this,"fontType","web");a(this,"bitmapFont");a(this,"bitmapAtlas");a(this,"bitmapCharWidth",8);a(this,"bitmapCharHeight",8);a(this,"showDebugGrid");a(this,"debugGridColor");a(this,"gridOverlay");a(this,"fixedGridMode");a(this,"fixedCols");a(this,"fixedRows");a(this,"cellAspectRatio");a(this,"resizeObserver");a(this,"imageDataBuffer");a(this,"useImageDataRendering",!1);a(this,"paletteCache");if(!e)throw new Error("Render: L'\xE9l\xE9ment parent est requis");this.parentElement=e,this.fixedGridMode=!!(t.fixedCols&&t.fixedRows),this.fixedCols=t.fixedCols,this.fixedRows=t.fixedRows,this.cellAspectRatio=t.cellAspectRatio??10/14,this.cellWidth=t.cellWidth??10,this.cellHeight=t.cellHeight??14,this.fontSize=t.fontSize??12,this.fontFamily=t.fontFamily??"monospace",this.defaultFgColor=t.defaultFgColor??"#ffffff",this.defaultBgColor=t.defaultBgColor??"#000000",this.canvasBgColor=t.canvasBgColor!==void 0?t.canvasBgColor:null,this.showDebugGrid=t.showDebugGrid??!1,this.debugGridColor=t.debugGridColor??"rgba(144, 24, 24, 1)",this.canvas=document.createElement("canvas"),t.className&&(this.canvas.className=t.className),t.style&&Object.assign(this.canvas.style,t.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 w(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,innerPadding:1,zIndex:10})),this.enableAutoResize(),this.render()}calculateGridSize(){let e=this.parentElement.clientWidth||800,t=this.parentElement.clientHeight||600;if(this.fixedGridMode&&this.fixedCols&&this.fixedRows)if(this.cols=this.fixedCols,this.rows=this.fixedRows,this.fontType==="bitmap"){let n=e/this.cols,l=t/this.rows,o=this.bitmapCharWidth/this.bitmapCharHeight;n/l>o?(this.cellHeight=Math.floor(l),this.cellWidth=Math.floor(this.cellHeight*o)):(this.cellWidth=Math.floor(n),this.cellHeight=Math.floor(this.cellWidth/o))}else{let n=e/this.cols,l=t/this.rows;n/l>this.cellAspectRatio?(this.cellHeight=Math.floor(l*100)/100,this.cellWidth=Math.floor(this.cellHeight*this.cellAspectRatio*100)/100):(this.cellWidth=Math.floor(n*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(e/this.cellWidth),this.rows=Math.floor(t/this.cellHeight);let s=this.cols*this.cellWidth,i=this.rows*this.cellHeight;this.offsetX=Math.floor((e-s)/2),this.offsetY=Math.floor((t-i)/2);let r=window.devicePixelRatio||1;this.canvas.style.width=`${e}px`,this.canvas.style.height=`${t}px`,this.canvas.width=e*r,this.canvas.height=t*r,this.ctx.scale(r,r)}createEmptyGrid(){let e=[];for(let t=0;t<this.rows;t++){let s=[];for(let i=0;i<this.cols;i++)s.push({char:" ",fgColor:this.defaultFgColor,bgColor:this.defaultBgColor});e.push(s)}return e}enableAutoResize(){this.resizeObserver=new ResizeObserver(()=>{let e=this.cols,t=this.rows;if(this.calculateGridSize(),this.cols!==e||this.rows!==t){let s=this.cells;this.cells=this.createEmptyGrid();let i=Math.min(e,this.cols),r=Math.min(t,this.rows);for(let n=0;n<r;n++)for(let l=0;l<i;l++)this.cells[n][l]=s[n][l]}this.render()}),this.resizeObserver.observe(this.parentElement)}setCell(e,t,s,i,r){if(t<0||t>=this.rows||e<0||e>=this.cols)return;let n=s&&typeof s=="string"?s.charAt(0):" ";this.cells[t][e]={char:n,fgColor:i??this.defaultFgColor,bgColor:r??this.defaultBgColor}}getCell(e,t){return t<0||t>=this.rows||e<0||e>=this.cols?null:this.cells[t][e]}write(e,t,s,i,r){for(let n=0;n<s.length;n++)this.setCell(e+n,t,s[n],i,r)}fillRect(e,t,s,i,r=" ",n,l){for(let o=t;o<t+i;o++)for(let h=e;h<e+s;h++)this.setCell(h,o,r,n,l)}clear(){this.cells=this.createEmptyGrid()}setFromArray(e){let t=e.width*e.height;if(e.cells.length!==t)throw new Error(`Invalid array length: expected ${t} (${e.width}\xD7${e.height}), got ${e.cells.length}`);let s=0;for(let i=0;i<e.height;i++)for(let r=0;r<e.width;r++){let n=e.cells[s];i<this.rows&&r<this.cols&&this.setCell(r,i,n.char,n.fgColor,n.bgColor),s++}}render(){if(this.fontType==="bitmap"&&this.bitmapFont&&this.useImageDataRendering){this.renderWithImageData();return}this.renderClassic()}renderWithImageData(){if(!this.bitmapFont)return;let e=this.cols*this.bitmapCharWidth,t=this.rows*this.bitmapCharHeight;(!this.imageDataBuffer||this.imageDataBuffer.width!==e||this.imageDataBuffer.height!==t)&&(this.imageDataBuffer=this.ctx.createImageData(e,t));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),g=c.char.charCodeAt(0),u=this.bitmapFont.get(g);for(let m=0;m<this.bitmapCharHeight;m++)for(let p=0;p<this.bitmapCharWidth;p++){let C=h*this.bitmapCharWidth+p,b=((o*this.bitmapCharHeight+m)*e+C)*4,E=!1;if(u&&m<u.length){let R=u[m],x=1<<7-p;E=(R&x)!==0}E?(s[b]=f.r,s[b+1]=f.g,s[b+2]=f.b,s[b+3]=f.a):(s[b]=d.r,s[b+1]=d.g,s[b+2]=d.b,s[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 i=document.createElement("canvas");i.width=e,i.height=t,i.getContext("2d").putImageData(this.imageDataBuffer,0,0);let n=this.cols*this.cellWidth,l=this.rows*this.cellHeight;this.ctx.drawImage(i,0,0,e,t,this.offsetX,this.offsetY,n,l),this.showDebugGrid&&this.drawDebugGrid()}parseColorToRGB(e){let t=/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/.exec(e);if(t)return{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10),a:t[4]?Math.round(parseFloat(t[4])*255):255};if(e.startsWith("#")){let r=/^#?([a-f\d])([a-f\d])([a-f\d])$/i;e=e.replace(r,(l,o,h,c)=>o+o+h+h+c+c);let n=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(e);if(n)return{r:parseInt(n[1],16),g:parseInt(n[2],16),b:parseInt(n[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]}[e.toLowerCase()];return i?{r:i[0],g:i[1],b:i[2],a:e==="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 e=0;e<this.rows;e++)for(let t=0;t<this.cols;t++){let s=this.cells[e][t],i=Math.floor(this.offsetX+t*this.cellWidth),r=Math.floor(this.offsetY+e*this.cellHeight),n=Math.floor(this.offsetX+(t+1)*this.cellWidth),l=Math.floor(this.offsetY+(e+1)*this.cellHeight),o=n-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(e,t,s,i,r,n){let l=e.charCodeAt(0);if(this.bitmapAtlas){this.bitmapAtlas.drawChar(this.ctx,l,t,s,i,r,n);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=n;for(let d=0;d<Math.min(o.length,this.bitmapCharHeight);d++){let f=o[d],g=s+d*c,u=s+(d+1)*c;for(let m=0;m<this.bitmapCharWidth;m++){let p=1<<7-m;if(f&p){let C=t+m*h,T=t+(m+1)*h;this.ctx.fillRect(C,g,T-C,u-g)}}}}drawDebugGrid(){if(!this.gridOverlay)return;let e=this.parentElement.clientWidth||800,t=this.parentElement.clientHeight||600;this.gridOverlay.update(this.cols,this.rows,this.cellWidth,this.cellHeight,e,t,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(e){this.showDebugGrid=e,e&&!this.gridOverlay?(this.gridOverlay=new w(this.parentElement,{strokeColor:this.debugGridColor,lineWidth:1,innerPadding:1,zIndex:10}),this.drawDebugGrid()):!e&&this.gridOverlay?(this.gridOverlay.destroy(),this.gridOverlay=void 0):e&&this.gridOverlay&&this.gridOverlay.setVisible(!0)}setCanvasBackgroundColor(e){this.canvasBgColor=e}getCanvasBackgroundColor(){return this.canvasBgColor}setFixedGrid(e,t,s){this.fixedGridMode=!0,this.fixedCols=e,this.fixedRows=t,s!==void 0&&(this.cellAspectRatio=s);let i=this.cols,r=this.rows,n=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]=n[h][c]}this.render()}setAdaptiveGrid(e,t){this.fixedGridMode=!1,this.fixedCols=void 0,this.fixedRows=void 0,e!==void 0&&(this.cellWidth=e),t!==void 0&&(this.cellHeight=t);let s=this.cols,i=this.rows,r=this.cells;if(this.calculateGridSize(),this.cols!==s||this.rows!==i){this.cells=this.createEmptyGrid();let n=Math.min(s,this.cols),l=Math.min(i,this.rows);for(let o=0;o<l;o++)for(let h=0;h<n;h++)this.cells[o][h]=r[o][h]}this.render()}isFixedGridMode(){return this.fixedGridMode}setDebugGridColor(e){this.debugGridColor=e}isDebugGridEnabled(){return this.showDebugGrid}setImageDataRendering(e){this.useImageDataRendering=e,e&&this.fontType==="bitmap"?console.warn("[Render] ImageData rendering enabled (optimized for benchmarks)"):e&&(console.warn("[Render] ImageData rendering requires bitmap font, keeping classic mode"),this.useImageDataRendering=!1)}isImageDataRenderingEnabled(){return this.useImageDataRendering}setWebFont(e,t){this.fontType="web",this.fontFamily=e,t!==void 0&&(this.fontSize=t),this.bitmapFont=void 0,this.bitmapAtlas=void 0,this.useImageDataRendering=!1,this.calculateGridSize(),this.cells=this.createEmptyGrid()}setBitmapFont(e,t,s,i,r){this.fontType="bitmap",this.bitmapFont=e,this.bitmapCharWidth=t,this.bitmapCharHeight=s,this.bitmapAtlas=new I(e,t,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(e){this.paletteCache=e}renderDisplayData(e){if(!e||!e.cells||e.cells.length===0){console.warn("[Terminal2D] Empty display data");return}if(e.width===0||e.height===0){console.warn("[Terminal2D] Invalid display dimensions:",e.width,e.height);return}(e.width!==this.cols||e.height!==this.rows)&&this.setFixedGrid(e.width,e.height,this.cellAspectRatio);let t=this.paletteCache??e.palette;if(!t||t.length===0){console.error("[Terminal2D] No palette available (neither cached nor in display)");return}let s=A(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>=t.length)return console.warn(`[Terminal2D] Invalid palette index: ${i}`),this.defaultFgColor;let r=t[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<e.height&&i<this.rows;i++)for(let r=0;r<e.width&&r<this.cols;r++){let n=i*e.width+r;if(n>=e.cells.length)break;let l=e.cells[n];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(e,t){this.setFixedGrid(e,t)}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)}};A(M,"Terminal2D");var L=M;var Q="0.1.0";
|