textmode.js 0.1.4-beta.1 → 0.1.4-beta.2

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.
@@ -1,6 +1,6 @@
1
1
  var se = Object.defineProperty;
2
- var ae = (l, e, t) => e in l ? se(l, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[e] = t;
3
- var o = (l, e, t) => ae(l, typeof e != "symbol" ? e + "" : e, t);
2
+ var ae = (h, e, t) => e in h ? se(h, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : h[e] = t;
3
+ var o = (h, e, t) => ae(h, typeof e != "symbol" ? e + "" : e, t);
4
4
  class F extends Error {
5
5
  constructor(t, r, i = {}) {
6
6
  const s = F.createFormattedMessage(t, i);
@@ -46,7 +46,7 @@ class F extends Error {
46
46
  return String(t);
47
47
  }
48
48
  }
49
- var ne = /* @__PURE__ */ ((l) => (l[l.SILENT = 0] = "SILENT", l[l.WARNING = 1] = "WARNING", l[l.ERROR = 2] = "ERROR", l[l.THROW = 3] = "THROW", l))(ne || {});
49
+ var ne = /* @__PURE__ */ ((h) => (h[h.SILENT = 0] = "SILENT", h[h.WARNING = 1] = "WARNING", h[h.ERROR = 2] = "ERROR", h[h.THROW = 3] = "THROW", h))(ne || {});
50
50
  const E = class E {
51
51
  constructor() {
52
52
  o(this, "_options", {
@@ -104,8 +104,8 @@ const E = class E {
104
104
  }
105
105
  };
106
106
  o(E, "_instance", null);
107
- let z = E;
108
- const x = z.getInstance();
107
+ let $ = E;
108
+ const x = $.getInstance();
109
109
  class oe {
110
110
  constructor(e, t, r = t, i = {}) {
111
111
  o(this, "gl");
@@ -201,6 +201,20 @@ class oe {
201
201
  return s.bindFramebuffer(s.FRAMEBUFFER, this._framebuffer), s.readPixels(e, t, r, i, s.RGBA, s.UNSIGNED_BYTE, a), s.bindFramebuffer(s.FRAMEBUFFER, n), a;
202
202
  }
203
203
  }
204
+ /**
205
+ * Dispose of WebGL resources used by this framebuffer.
206
+ * This method is idempotent and safe to call multiple times.
207
+ */
208
+ dispose() {
209
+ const { gl: e } = this;
210
+ this._framebuffer && (e.deleteFramebuffer(this._framebuffer), this._framebuffer = null), this._texture && (e.deleteTexture(this._texture), this._texture = null), this._pixels = null;
211
+ }
212
+ /**
213
+ * Check if this framebuffer has been disposed
214
+ */
215
+ get isDisposed() {
216
+ return this._framebuffer === null || this._texture === null;
217
+ }
204
218
  get framebuffer() {
205
219
  return this._framebuffer;
206
220
  }
@@ -225,7 +239,7 @@ class Q {
225
239
  this.gl = e, this.x = t, this.y = r;
226
240
  }
227
241
  }
228
- class M {
242
+ class A {
229
243
  constructor(e, t, r, i, s) {
230
244
  /** The WebGL rendering context */
231
245
  o(this, "gl");
@@ -236,12 +250,12 @@ class M {
236
250
  /** Bytes per vertex: depends on position format (vec2 vs vec3) */
237
251
  o(this, "bytesPerVertex");
238
252
  this.gl = e, this.bytesPerVertex = 16;
239
- const a = e.getParameter(e.VIEWPORT), n = a[2], h = a[3], c = e.getParameter(e.FRAMEBUFFER_BINDING) !== null, u = t / n * 2 - 1, d = (t + i) / n * 2 - 1;
253
+ const a = e.getParameter(e.VIEWPORT), n = a[2], l = a[3], c = e.getParameter(e.FRAMEBUFFER_BINDING) !== null, u = t / n * 2 - 1, d = (t + i) / n * 2 - 1;
240
254
  let f, g;
241
- c ? (f = r / h * 2 - 1, g = (r + s) / h * 2 - 1) : (f = 1 - r / h * 2, g = 1 - (r + s) / h * 2);
242
- let _, p, C, b;
243
- _ = u, C = d, p = f, b = g;
244
- const v = this.generateVertices(_, p, C, b);
255
+ c ? (f = r / l * 2 - 1, g = (r + s) / l * 2 - 1) : (f = 1 - r / l * 2, g = 1 - (r + s) / l * 2);
256
+ let p, _, b, C;
257
+ p = u, b = d, _ = f, C = g;
258
+ const v = this.generateVertices(p, _, b, C);
245
259
  this.vertexBuffer = e.createBuffer(), e.bindBuffer(e.ARRAY_BUFFER, this.vertexBuffer), e.bufferData(e.ARRAY_BUFFER, v, e.STATIC_DRAW);
246
260
  }
247
261
  /**
@@ -292,7 +306,7 @@ class M {
292
306
  this.gl.enableVertexAttribArray(t), this.gl.vertexAttribPointer(t, 2, this.gl.FLOAT, !1, this.bytesPerVertex, 0), this.gl.enableVertexAttribArray(r), this.gl.vertexAttribPointer(r, 2, this.gl.FLOAT, !1, this.bytesPerVertex, 8), this.gl.drawArrays(this.gl.TRIANGLES, 0, this.vertexCount), this.gl.disableVertexAttribArray(t), this.gl.disableVertexAttribArray(r);
293
307
  }
294
308
  }
295
- class le extends Q {
309
+ class he extends Q {
296
310
  constructor(t, r, i, s, a) {
297
311
  super(t, r, i);
298
312
  o(this, "width");
@@ -303,7 +317,7 @@ class le extends Q {
303
317
  * Render the filled rectangle using the existing Rectangle geometry.
304
318
  */
305
319
  renderFill() {
306
- new M(this.gl, this.x, this.y, this.width, this.height).render();
320
+ new A(this.gl, this.x, this.y, this.width, this.height).render();
307
321
  }
308
322
  /**
309
323
  * Render the stroke rectangle as four separate Rectangle instances for each edge.
@@ -312,11 +326,11 @@ class le extends Q {
312
326
  */
313
327
  renderStroke(t) {
314
328
  if (t <= 0) return;
315
- const r = new M(this.gl, this.x, this.y, this.width, t), i = new M(this.gl, this.x + this.width - t, this.y, t, this.height), s = new M(this.gl, this.x, this.y + this.height - t, this.width, t), a = new M(this.gl, this.x, this.y, t, this.height);
329
+ const r = new A(this.gl, this.x, this.y, this.width, t), i = new A(this.gl, this.x + this.width - t, this.y, t, this.height), s = new A(this.gl, this.x, this.y + this.height - t, this.width, t), a = new A(this.gl, this.x, this.y, t, this.height);
316
330
  r.render(), i.render(), s.render(), a.render();
317
331
  }
318
332
  }
319
- class he {
333
+ class le {
320
334
  constructor(e, t, r, i, s, a) {
321
335
  /** The WebGL rendering context */
322
336
  o(this, "gl");
@@ -327,15 +341,15 @@ class he {
327
341
  /** Bytes per vertex: vec2+vec2 = 16 bytes */
328
342
  o(this, "bytesPerVertex");
329
343
  this.gl = e, this.bytesPerVertex = 16;
330
- const n = e.getParameter(e.VIEWPORT), h = n[2], c = n[3], u = e.getParameter(e.FRAMEBUFFER_BINDING) !== null, d = i - t, f = s - r, g = Math.sqrt(d * d + f * f);
344
+ const n = e.getParameter(e.VIEWPORT), l = n[2], c = n[3], u = e.getParameter(e.FRAMEBUFFER_BINDING) !== null, d = i - t, f = s - r, g = Math.sqrt(d * d + f * f);
331
345
  if (g === 0) {
332
346
  const ie = this.generateVertices(0, 0, 0, 0);
333
347
  this.vertexBuffer = e.createBuffer(), e.bindBuffer(e.ARRAY_BUFFER, this.vertexBuffer), e.bufferData(e.ARRAY_BUFFER, ie, e.STATIC_DRAW);
334
348
  return;
335
349
  }
336
- const _ = d / g, C = -(f / g), b = _, v = a / 2, w = t + C * v, A = r + b * v, R = t - C * v, y = r - b * v, P = i + C * v, D = s + b * v, Z = i - C * v, W = s - b * v, J = w / h * 2 - 1, K = R / h * 2 - 1, ee = P / h * 2 - 1, te = Z / h * 2 - 1;
350
+ const p = d / g, b = -(f / g), C = p, v = a / 2, w = t + b * v, D = r + C * v, R = t - b * v, y = r - C * v, P = i + b * v, M = s + C * v, Z = i - b * v, W = s - C * v, J = w / l * 2 - 1, K = R / l * 2 - 1, ee = P / l * 2 - 1, te = Z / l * 2 - 1;
337
351
  let B, G, V, k;
338
- u ? (B = A / c * 2 - 1, G = y / c * 2 - 1, V = D / c * 2 - 1, k = W / c * 2 - 1) : (B = 1 - A / c * 2, G = 1 - y / c * 2, V = 1 - D / c * 2, k = 1 - W / c * 2);
352
+ u ? (B = D / c * 2 - 1, G = y / c * 2 - 1, V = M / c * 2 - 1, k = W / c * 2 - 1) : (B = 1 - D / c * 2, G = 1 - y / c * 2, V = 1 - M / c * 2, k = 1 - W / c * 2);
339
353
  const re = this.generateLineVertices(
340
354
  J,
341
355
  B,
@@ -391,7 +405,7 @@ class he {
391
405
  * Uses the four corners calculated based on line direction and thickness
392
406
  * @private
393
407
  */
394
- generateLineVertices(e, t, r, i, s, a, n, h) {
408
+ generateLineVertices(e, t, r, i, s, a, n, l) {
395
409
  return new Float32Array([
396
410
  e,
397
411
  t,
@@ -414,7 +428,7 @@ class he {
414
428
  1,
415
429
  // corner2 (start - perpendicular)
416
430
  n,
417
- h,
431
+ l,
418
432
  1,
419
433
  1,
420
434
  // corner4 (end - perpendicular)
@@ -454,7 +468,7 @@ class ce extends Q {
454
468
  */
455
469
  renderStroke(t) {
456
470
  if (t <= 0) return;
457
- new he(this.gl, this.x, this.y, this.x2, this.y2, t).render();
471
+ new le(this.gl, this.x, this.y, this.x2, this.y2, t).render();
458
472
  }
459
473
  }
460
474
  class T {
@@ -590,6 +604,19 @@ class T {
590
604
  get glProgram() {
591
605
  return this.program;
592
606
  }
607
+ /**
608
+ * Dispose of WebGL resources used by this shader.
609
+ * This method is idempotent and safe to call multiple times.
610
+ */
611
+ dispose() {
612
+ this.program && (this.gl.deleteProgram(this.program), this.program = null), this.uniformLocations.clear(), this.attributeLocations.clear(), this.textureUnitCounter = 0;
613
+ }
614
+ /**
615
+ * Check if this shader has been disposed
616
+ */
617
+ get isDisposed() {
618
+ return this.program === null;
619
+ }
593
620
  /**
594
621
  * Reset texture unit counter (useful when starting a new frame)
595
622
  */
@@ -729,18 +756,18 @@ class fe {
729
756
  * Draw a rectangle with the current fill and/or stroke settings
730
757
  */
731
758
  rect(e, t, r, i) {
732
- const s = new le(this.gl, e, t, r, i);
759
+ const s = new he(this.gl, e, t, r, i);
733
760
  if (this.currentShader !== null) {
734
761
  if (this.currentRotation !== 0) {
735
- const { centerX: d, centerY: f, radians: g, aspectRatio: _ } = this.calculateRotationParams(e, t, r, i);
736
- this.setUniform("u_rotation", g), this.setUniform("u_center", [d, f]), this.setUniform("u_aspectRatio", _);
762
+ const { centerX: d, centerY: f, radians: g, aspectRatio: p } = this.calculateRotationParams(e, t, r, i);
763
+ this.setUniform("u_rotation", g), this.setUniform("u_center", [d, f]), this.setUniform("u_aspectRatio", p);
737
764
  } else
738
765
  this.setUniform("u_rotation", 0), this.setUniform("u_center", [0, 0]), this.setUniform("u_aspectRatio", 1);
739
766
  s.renderFill(), this.currentShader = null;
740
767
  return;
741
768
  }
742
- const a = this.solidColorShader, { centerX: n, centerY: h, radians: c, aspectRatio: u } = this.calculateRotationParams(e, t, r, i);
743
- this.fillMode && (this.shader(a), this.setUniform("u_color", this.currentFillColor), this.setUniform("u_rotation", c), this.setUniform("u_center", [n, h]), this.setUniform("u_aspectRatio", u), s.renderFill()), this.strokeMode && (this.shader(a), this.setUniform("u_color", this.currentStrokeColor), this.setUniform("u_rotation", c), this.setUniform("u_center", [n, h]), this.setUniform("u_aspectRatio", u), s.renderStroke(this.currentStrokeWeight)), this.currentShader = null;
769
+ const a = this.solidColorShader, { centerX: n, centerY: l, radians: c, aspectRatio: u } = this.calculateRotationParams(e, t, r, i);
770
+ this.fillMode && (this.shader(a), this.setUniform("u_color", this.currentFillColor), this.setUniform("u_rotation", c), this.setUniform("u_center", [n, l]), this.setUniform("u_aspectRatio", u), s.renderFill()), this.strokeMode && (this.shader(a), this.setUniform("u_color", this.currentStrokeColor), this.setUniform("u_rotation", c), this.setUniform("u_center", [n, l]), this.setUniform("u_aspectRatio", u), s.renderStroke(this.currentStrokeWeight)), this.currentShader = null;
744
771
  }
745
772
  /**
746
773
  * Draw a line from (x1, y1) to (x2, y2) with the current stroke settings.
@@ -755,25 +782,25 @@ class fe {
755
782
  const s = new ce(this.gl, e, t, r, i);
756
783
  if (this.currentShader !== null) {
757
784
  if (this.currentRotation !== 0) {
758
- const p = (e + r) / 2, C = (t + i) / 2, b = Math.abs(r - e), v = Math.abs(i - t), { centerX: w, centerY: A, radians: R, aspectRatio: y } = this.calculateRotationParams(p - b / 2, C - v / 2, b, v);
759
- this.setUniform("u_rotation", R), this.setUniform("u_center", [w, A]), this.setUniform("u_aspectRatio", y);
785
+ const _ = (e + r) / 2, b = (t + i) / 2, C = Math.abs(r - e), v = Math.abs(i - t), { centerX: w, centerY: D, radians: R, aspectRatio: y } = this.calculateRotationParams(_ - C / 2, b - v / 2, C, v);
786
+ this.setUniform("u_rotation", R), this.setUniform("u_center", [w, D]), this.setUniform("u_aspectRatio", y);
760
787
  } else
761
788
  this.setUniform("u_rotation", 0), this.setUniform("u_center", [0, 0]), this.setUniform("u_aspectRatio", 1);
762
789
  s.renderStroke(this.currentStrokeWeight), this.currentShader = null;
763
790
  return;
764
791
  }
765
- const a = this.solidColorShader, n = (e + r) / 2, h = (t + i) / 2, c = Math.abs(r - e), u = Math.abs(i - t), { centerX: d, centerY: f, radians: g, aspectRatio: _ } = this.calculateRotationParams(n - c / 2, h - u / 2, c, u);
766
- this.shader(a), this.setUniform("u_color", this.currentStrokeColor), this.setUniform("u_rotation", g), this.setUniform("u_center", [d, f]), this.setUniform("u_aspectRatio", _), s.renderStroke(this.currentStrokeWeight), this.currentShader = null;
792
+ const a = this.solidColorShader, n = (e + r) / 2, l = (t + i) / 2, c = Math.abs(r - e), u = Math.abs(i - t), { centerX: d, centerY: f, radians: g, aspectRatio: p } = this.calculateRotationParams(n - c / 2, l - u / 2, c, u);
793
+ this.shader(a), this.setUniform("u_color", this.currentStrokeColor), this.setUniform("u_rotation", g), this.setUniform("u_center", [d, f]), this.setUniform("u_aspectRatio", p), s.renderStroke(this.currentStrokeWeight), this.currentShader = null;
767
794
  }
768
795
  /**
769
796
  * Calculate rotation parameters for built-in shaders (NDC coordinates)
770
797
  */
771
798
  calculateRotationParams(e, t, r, i) {
772
- const s = this.gl.getParameter(this.gl.VIEWPORT), a = s[2], n = s[3], h = a / n, c = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING) !== null, u = e + r / 2, d = t + i / 2, f = u / a * 2 - 1;
799
+ const s = this.gl.getParameter(this.gl.VIEWPORT), a = s[2], n = s[3], l = a / n, c = this.gl.getParameter(this.gl.FRAMEBUFFER_BINDING) !== null, u = e + r / 2, d = t + i / 2, f = u / a * 2 - 1;
773
800
  let g;
774
801
  c ? g = d / n * 2 - 1 : g = 1 - d / n * 2;
775
- const _ = this.currentRotation * Math.PI / 180;
776
- return { centerX: f, centerY: g, radians: _, aspectRatio: h };
802
+ const p = this.currentRotation * Math.PI / 180;
803
+ return { centerX: f, centerY: g, radians: p, aspectRatio: l };
777
804
  }
778
805
  /**
779
806
  * Create a new framebuffer
@@ -805,23 +832,36 @@ class fe {
805
832
  get context() {
806
833
  return this.gl;
807
834
  }
835
+ /**
836
+ * Dispose of all WebGL resources managed by this renderer.
837
+ * This method is idempotent and safe to call multiple times.
838
+ */
839
+ dispose() {
840
+ this.imageShader && !this.imageShader.isDisposed && this.imageShader.dispose(), this.solidColorShader && !this.solidColorShader.isDisposed && this.solidColorShader.dispose(), this.imageShader = null, this.solidColorShader = null, this.currentShader = null, this.stateStack = [];
841
+ }
842
+ /**
843
+ * Check if this renderer has been disposed
844
+ */
845
+ get isDisposed() {
846
+ return this.imageShader === null || this.solidColorShader === null;
847
+ }
808
848
  /**
809
849
  * Render a framebuffer at a specific position with optional scaling
810
850
  */
811
851
  image(e, t, r, i, s) {
812
852
  this.shader(this.imageShader), this.setUniform("u_texture", e.texture);
813
- const { centerX: a, centerY: n, radians: h, aspectRatio: c } = this.calculateRotationParams(
853
+ const { centerX: a, centerY: n, radians: l, aspectRatio: c } = this.calculateRotationParams(
814
854
  t,
815
855
  r,
816
856
  i ?? e.width,
817
857
  s ?? e.height
818
858
  );
819
- this.setUniform("u_rotation", h), this.setUniform("u_center", [a, n]), this.setUniform("u_aspectRatio", c), this.rect(t, r, i ?? e.width, s ?? e.height);
859
+ this.setUniform("u_rotation", l), this.setUniform("u_center", [a, n]), this.setUniform("u_aspectRatio", c), this.rect(t, r, i ?? e.width, s ?? e.height);
820
860
  }
821
861
  }
822
862
  var m = {};
823
- m.parse = function(l) {
824
- var e = function(s, a, n, h) {
863
+ m.parse = function(h) {
864
+ var e = function(s, a, n, l) {
825
865
  var c = m.T, u = {
826
866
  cmap: c.cmap,
827
867
  head: c.head,
@@ -834,115 +874,115 @@ m.parse = function(l) {
834
874
  for (var f in u) {
835
875
  var g = m.findTable(s, f, n);
836
876
  if (g) {
837
- var _ = g[0], p = h[_];
838
- p == null && (p = u[f].parseTab(s, _, g[1], d)), d[f] = h[_] = p;
877
+ var p = g[0], _ = l[p];
878
+ _ == null && (_ = u[f].parseTab(s, p, g[1], d)), d[f] = l[p] = _;
839
879
  }
840
880
  }
841
881
  return d;
842
- }, t = new Uint8Array(l), r = {}, i = e(t, 0, 0, r);
882
+ }, t = new Uint8Array(h), r = {}, i = e(t, 0, 0, r);
843
883
  return [i];
844
884
  };
845
- m.findTable = function(l, e, t) {
846
- for (var r = m.B, i = r.readUshort(l, t + 4), s = t + 12, a = 0; a < i; a++) {
847
- var n = r.readASCII(l, s, 4);
848
- r.readUint(l, s + 4);
849
- var h = r.readUint(l, s + 8), c = r.readUint(l, s + 12);
850
- if (n == e) return [h, c];
885
+ m.findTable = function(h, e, t) {
886
+ for (var r = m.B, i = r.readUshort(h, t + 4), s = t + 12, a = 0; a < i; a++) {
887
+ var n = r.readASCII(h, s, 4);
888
+ r.readUint(h, s + 4);
889
+ var l = r.readUint(h, s + 8), c = r.readUint(h, s + 12);
890
+ if (n == e) return [l, c];
851
891
  s += 16;
852
892
  }
853
893
  return null;
854
894
  };
855
895
  m.T = {};
856
896
  m.B = {
857
- readShort: function(l, e) {
897
+ readShort: function(h, e) {
858
898
  var t = m.B.t.uint16;
859
- return t[0] = l[e] << 8 | l[e + 1], m.B.t.int16[0];
899
+ return t[0] = h[e] << 8 | h[e + 1], m.B.t.int16[0];
860
900
  },
861
- readUshort: function(l, e) {
862
- return l[e] << 8 | l[e + 1];
901
+ readUshort: function(h, e) {
902
+ return h[e] << 8 | h[e + 1];
863
903
  },
864
- readUshorts: function(l, e, t) {
904
+ readUshorts: function(h, e, t) {
865
905
  for (var r = [], i = 0; i < t; i++)
866
- r.push(m.B.readUshort(l, e + i * 2));
906
+ r.push(m.B.readUshort(h, e + i * 2));
867
907
  return r;
868
908
  },
869
- readUint: function(l, e) {
909
+ readUint: function(h, e) {
870
910
  var t = m.B.t.uint8;
871
- return t[3] = l[e], t[2] = l[e + 1], t[1] = l[e + 2], t[0] = l[e + 3], m.B.t.uint32[0];
911
+ return t[3] = h[e], t[2] = h[e + 1], t[1] = h[e + 2], t[0] = h[e + 3], m.B.t.uint32[0];
872
912
  },
873
- readASCII: function(l, e, t) {
874
- for (var r = "", i = 0; i < t; i++) r += String.fromCharCode(l[e + i]);
913
+ readASCII: function(h, e, t) {
914
+ for (var r = "", i = 0; i < t; i++) r += String.fromCharCode(h[e + i]);
875
915
  return r;
876
916
  },
877
917
  // Simplified typed array buffer - only what's needed
878
918
  t: function() {
879
- var l = new ArrayBuffer(8);
919
+ var h = new ArrayBuffer(8);
880
920
  return {
881
- uint8: new Uint8Array(l),
882
- int16: new Int16Array(l),
883
- uint16: new Uint16Array(l),
884
- uint32: new Uint32Array(l)
921
+ uint8: new Uint8Array(h),
922
+ int16: new Int16Array(h),
923
+ uint16: new Uint16Array(h),
924
+ uint32: new Uint32Array(h)
885
925
  };
886
926
  }()
887
927
  };
888
928
  m.T.cmap = {
889
- parseTab: function(l, e, t) {
929
+ parseTab: function(h, e, t) {
890
930
  var r = { tables: [], ids: {}, off: e };
891
- l = new Uint8Array(l.buffer, e, t), e = 0;
931
+ h = new Uint8Array(h.buffer, e, t), e = 0;
892
932
  var i = m.B, s = i.readUshort, a = m.T.cmap;
893
- s(l, e), e += 2;
894
- var n = s(l, e);
933
+ s(h, e), e += 2;
934
+ var n = s(h, e);
895
935
  e += 2;
896
- for (var h = [], c = 0; c < n; c++) {
897
- var u = s(l, e);
936
+ for (var l = [], c = 0; c < n; c++) {
937
+ var u = s(h, e);
898
938
  e += 2;
899
- var d = s(l, e);
939
+ var d = s(h, e);
900
940
  e += 2;
901
- var f = i.readUint(l, e);
941
+ var f = i.readUint(h, e);
902
942
  e += 4;
903
- var g = "p" + u + "e" + d, _ = h.indexOf(f);
904
- if (_ == -1) {
905
- _ = r.tables.length;
906
- var p = {};
907
- h.push(f);
908
- var C = p.format = s(l, f);
909
- C == 4 ? p = a.parse4(l, f, p) : C == 12 && (p = a.parse12(l, f, p)), r.tables.push(p);
943
+ var g = "p" + u + "e" + d, p = l.indexOf(f);
944
+ if (p == -1) {
945
+ p = r.tables.length;
946
+ var _ = {};
947
+ l.push(f);
948
+ var b = _.format = s(h, f);
949
+ b == 4 ? _ = a.parse4(h, f, _) : b == 12 && (_ = a.parse12(h, f, _)), r.tables.push(_);
910
950
  }
911
- r.ids[g] != null && console.log("multiple tables for one platform+encoding: " + g), r.ids[g] = _;
951
+ r.ids[g] != null && console.log("multiple tables for one platform+encoding: " + g), r.ids[g] = p;
912
952
  }
913
953
  return r;
914
954
  },
915
- parse4: function(l, e, t) {
955
+ parse4: function(h, e, t) {
916
956
  var r = m.B, i = r.readUshort, s = r.readUshorts, a = e;
917
957
  e += 2;
918
- var n = i(l, e);
919
- e += 2, i(l, e), e += 2;
920
- var h = i(l, e);
958
+ var n = i(h, e);
959
+ e += 2, i(h, e), e += 2;
960
+ var l = i(h, e);
921
961
  e += 2;
922
- var c = h >>> 1;
923
- t.searchRange = i(l, e), e += 2, t.entrySelector = i(l, e), e += 2, t.rangeShift = i(l, e), e += 2, t.endCount = s(l, e, c), e += c * 2, e += 2, t.startCount = s(l, e, c), e += c * 2, t.idDelta = [];
962
+ var c = l >>> 1;
963
+ t.searchRange = i(h, e), e += 2, t.entrySelector = i(h, e), e += 2, t.rangeShift = i(h, e), e += 2, t.endCount = s(h, e, c), e += c * 2, e += 2, t.startCount = s(h, e, c), e += c * 2, t.idDelta = [];
924
964
  for (var u = 0; u < c; u++)
925
- t.idDelta.push(r.readShort(l, e)), e += 2;
926
- return t.idRangeOffset = s(l, e, c), e += c * 2, t.glyphIdArray = s(l, e, a + n - e >> 1), t;
965
+ t.idDelta.push(r.readShort(h, e)), e += 2;
966
+ return t.idRangeOffset = s(h, e, c), e += c * 2, t.glyphIdArray = s(h, e, a + n - e >> 1), t;
927
967
  },
928
- parse12: function(l, e, t) {
968
+ parse12: function(h, e, t) {
929
969
  var r = m.B, i = r.readUint;
930
- e += 4, i(l, e), e += 4, i(l, e), e += 4;
931
- var s = i(l, e) * 3;
970
+ e += 4, i(h, e), e += 4, i(h, e), e += 4;
971
+ var s = i(h, e) * 3;
932
972
  e += 4;
933
973
  for (var a = t.groups = new Uint32Array(s), n = 0; n < s; n += 3)
934
- a[n] = i(l, e + (n << 2)), a[n + 1] = i(l, e + (n << 2) + 4), a[n + 2] = i(l, e + (n << 2) + 8);
974
+ a[n] = i(h, e + (n << 2)), a[n + 1] = i(h, e + (n << 2) + 4), a[n + 2] = i(h, e + (n << 2) + 8);
935
975
  return t;
936
976
  }
937
977
  };
938
978
  m.T.head = {
939
- parseTab: function(l, e, t) {
979
+ parseTab: function(h, e, t) {
940
980
  var r = m.B, i = {};
941
- return e += 18, i.unitsPerEm = r.readUshort(l, e), e += 2, e += 16, i.xMin = r.readShort(l, e), e += 2, i.yMin = r.readShort(l, e), e += 2, i.xMax = r.readShort(l, e), e += 2, i.yMax = r.readShort(l, e), e += 2, e += 6, i.indexToLocFormat = r.readShort(l, e), i;
981
+ return e += 18, i.unitsPerEm = r.readUshort(h, e), e += 2, e += 16, i.xMin = r.readShort(h, e), e += 2, i.yMin = r.readShort(h, e), e += 2, i.xMax = r.readShort(h, e), e += 2, i.yMax = r.readShort(h, e), e += 2, e += 6, i.indexToLocFormat = r.readShort(h, e), i;
942
982
  }
943
983
  };
944
984
  m.T.hhea = {
945
- parseTab: function(l, e, t) {
985
+ parseTab: function(h, e, t) {
946
986
  var r = m.B, i = {};
947
987
  e += 4;
948
988
  for (var s = [
@@ -963,52 +1003,52 @@ m.T.hhea = {
963
1003
  "metricDataFormat",
964
1004
  "numberOfHMetrics"
965
1005
  ], a = 0; a < s.length; a++) {
966
- var n = s[a], h = n == "advanceWidthMax" || n == "numberOfHMetrics" ? r.readUshort : r.readShort;
967
- i[n] = h(l, e + a * 2);
1006
+ var n = s[a], l = n == "advanceWidthMax" || n == "numberOfHMetrics" ? r.readUshort : r.readShort;
1007
+ i[n] = l(h, e + a * 2);
968
1008
  }
969
1009
  return i;
970
1010
  }
971
1011
  };
972
1012
  m.T.hmtx = {
973
- parseTab: function(l, e, t, r) {
974
- for (var i = m.B, s = [], a = [], n = r.maxp.numGlyphs, h = r.hhea.numberOfHMetrics, c = 0, u = 0, d = 0; d < h; )
975
- c = i.readUshort(l, e + (d << 2)), u = i.readShort(l, e + (d << 2) + 2), s.push(c), a.push(u), d++;
1013
+ parseTab: function(h, e, t, r) {
1014
+ for (var i = m.B, s = [], a = [], n = r.maxp.numGlyphs, l = r.hhea.numberOfHMetrics, c = 0, u = 0, d = 0; d < l; )
1015
+ c = i.readUshort(h, e + (d << 2)), u = i.readShort(h, e + (d << 2) + 2), s.push(c), a.push(u), d++;
976
1016
  for (; d < n; )
977
1017
  s.push(c), a.push(u), d++;
978
1018
  return { aWidth: s, lsBearing: a };
979
1019
  }
980
1020
  };
981
1021
  m.T.maxp = {
982
- parseTab: function(l, e, t) {
1022
+ parseTab: function(h, e, t) {
983
1023
  var r = m.B, i = r.readUshort, s = {};
984
- return r.readUint(l, e), e += 4, s.numGlyphs = i(l, e), e += 2, s;
1024
+ return r.readUint(h, e), e += 4, s.numGlyphs = i(h, e), e += 2, s;
985
1025
  }
986
1026
  };
987
1027
  m.T.loca = {
988
- parseTab: function(l, e, t, r) {
1028
+ parseTab: function(h, e, t, r) {
989
1029
  var i = m.B, s = [], a = r.head.indexToLocFormat, n = r.maxp.numGlyphs + 1;
990
- if (a == 0) for (var h = 0; h < n; h++) s.push(i.readUshort(l, e + (h << 1)) << 1);
991
- if (a == 1) for (var h = 0; h < n; h++) s.push(i.readUint(l, e + (h << 2)));
1030
+ if (a == 0) for (var l = 0; l < n; l++) s.push(i.readUshort(h, e + (l << 1)) << 1);
1031
+ if (a == 1) for (var l = 0; l < n; l++) s.push(i.readUint(h, e + (l << 2)));
992
1032
  return s;
993
1033
  }
994
1034
  };
995
1035
  m.T.glyf = {
996
- parseTab: function(l, e, t, r) {
1036
+ parseTab: function(h, e, t, r) {
997
1037
  for (var i = [], s = r.maxp.numGlyphs, a = 0; a < s; a++) i.push(null);
998
1038
  return i;
999
1039
  },
1000
- _parseGlyf: function(l, e) {
1001
- var t = m.B, r = l._data, i = l.loca;
1040
+ _parseGlyf: function(h, e) {
1041
+ var t = m.B, r = h._data, i = h.loca;
1002
1042
  if (i[e] == i[e + 1]) return null;
1003
- var s = m.findTable(r, "glyf", l._offset)[0] + i[e], a = {};
1043
+ var s = m.findTable(r, "glyf", h._offset)[0] + i[e], a = {};
1004
1044
  if (a.noc = t.readShort(r, s), s += 2, a.xMin = t.readShort(r, s), s += 2, a.yMin = t.readShort(r, s), s += 2, a.xMax = t.readShort(r, s), s += 2, a.yMax = t.readShort(r, s), s += 2, a.xMin >= a.xMax || a.yMin >= a.yMax) return null;
1005
1045
  if (a.noc > 0) {
1006
1046
  a.endPts = [];
1007
1047
  for (var n = 0; n < a.noc; n++)
1008
1048
  a.endPts.push(t.readUshort(r, s)), s += 2;
1009
- var h = t.readUshort(r, s);
1010
- if (s += 2, r.length - s < h) return null;
1011
- s += h;
1049
+ var l = t.readUshort(r, s);
1050
+ if (s += 2, r.length - s < l) return null;
1051
+ s += l;
1012
1052
  var c = a.endPts[a.noc - 1] + 1;
1013
1053
  a.flags = [];
1014
1054
  for (var n = 0; n < c; n++) {
@@ -1022,16 +1062,16 @@ m.T.glyf = {
1022
1062
  }
1023
1063
  a.xs = [];
1024
1064
  for (var n = 0; n < c; n++) {
1025
- var g = (a.flags[n] & 2) != 0, _ = (a.flags[n] & 16) != 0;
1026
- g ? (a.xs.push(_ ? r[s] : -r[s]), s++) : _ ? a.xs.push(0) : (a.xs.push(t.readShort(r, s)), s += 2);
1065
+ var g = (a.flags[n] & 2) != 0, p = (a.flags[n] & 16) != 0;
1066
+ g ? (a.xs.push(p ? r[s] : -r[s]), s++) : p ? a.xs.push(0) : (a.xs.push(t.readShort(r, s)), s += 2);
1027
1067
  }
1028
1068
  a.ys = [];
1029
1069
  for (var n = 0; n < c; n++) {
1030
- var g = (a.flags[n] & 4) != 0, _ = (a.flags[n] & 32) != 0;
1031
- g ? (a.ys.push(_ ? r[s] : -r[s]), s++) : _ ? a.ys.push(0) : (a.ys.push(t.readShort(r, s)), s += 2);
1070
+ var g = (a.flags[n] & 4) != 0, p = (a.flags[n] & 32) != 0;
1071
+ g ? (a.ys.push(p ? r[s] : -r[s]), s++) : p ? a.ys.push(0) : (a.ys.push(t.readShort(r, s)), s += 2);
1032
1072
  }
1033
- for (var p = 0, C = 0, n = 0; n < c; n++)
1034
- p += a.xs[n], C += a.ys[n], a.xs[n] = p, a.ys[n] = C;
1073
+ for (var _ = 0, b = 0, n = 0; n < c; n++)
1074
+ _ += a.xs[n], b += a.ys[n], a.xs[n] = _, a.ys[n] = b;
1035
1075
  } else
1036
1076
  a.parts = [];
1037
1077
  return a;
@@ -1071,8 +1111,8 @@ class me {
1071
1111
  if (!(i === 65535 && s === 65535)) {
1072
1112
  for (let a = i; a <= s; a++)
1073
1113
  if (this._calculateGlyphIndexFormat4(e, a, r) > 0) {
1074
- const h = String.fromCodePoint(a);
1075
- t.push(h);
1114
+ const l = String.fromCodePoint(a);
1115
+ t.push(l);
1076
1116
  }
1077
1117
  }
1078
1118
  }
@@ -1155,9 +1195,9 @@ class ge {
1155
1195
  * @returns Object containing framebuffer, columns, and rows
1156
1196
  */
1157
1197
  createTextureAtlas(e, t, r, i) {
1158
- const s = e.length, a = Math.ceil(Math.sqrt(s)), n = Math.ceil(s / a), h = t.width * a, c = t.height * n;
1159
- this._setupCanvas(h, c, r, i), this._renderCharactersToCanvas(e, t, a, r), this._applyBlackWhiteThreshold();
1160
- const u = this._renderer.createFramebuffer(h, c, { filter: "nearest" });
1198
+ const s = e.length, a = Math.ceil(Math.sqrt(s)), n = Math.ceil(s / a), l = t.width * a, c = t.height * n;
1199
+ this._setupCanvas(l, c, r, i), this._renderCharactersToCanvas(e, t, a, r), this._applyBlackWhiteThreshold();
1200
+ const u = this._renderer.createFramebuffer(l, c, { filter: "nearest" });
1161
1201
  return u.update(this._textureCanvas), {
1162
1202
  framebuffer: u,
1163
1203
  columns: a,
@@ -1185,7 +1225,7 @@ class ge {
1185
1225
  */
1186
1226
  _renderCharactersToCanvas(e, t, r, i) {
1187
1227
  for (let s = 0; s < e.length; s++) {
1188
- const a = s % r, n = Math.floor(s / r), h = a * t.width + t.width * 0.5, c = n * t.height + t.height * 0.5, u = Math.round(h - t.width * 0.5), d = Math.round(c - i * 0.5);
1228
+ const a = s % r, n = Math.floor(s / r), l = a * t.width + t.width * 0.5, c = n * t.height + t.height * 0.5, u = Math.round(l - t.width * 0.5), d = Math.round(c - i * 0.5);
1189
1229
  this._textureContext.fillText(e[s].character, u, d);
1190
1230
  }
1191
1231
  }
@@ -1204,7 +1244,7 @@ class ge {
1204
1244
  this._textureContext.putImageData(t, 0, 0);
1205
1245
  }
1206
1246
  }
1207
- class pe {
1247
+ class _e {
1208
1248
  /**
1209
1249
  * Creates a new MetricsCalculation instance.
1210
1250
  */
@@ -1225,8 +1265,8 @@ class pe {
1225
1265
  this._tempContext.font = `${t}px ${r}`;
1226
1266
  let i = 0, s = 0;
1227
1267
  for (const a of e) {
1228
- const n = this._tempContext.measureText(a), h = n.width, c = n.actualBoundingBoxAscent + n.actualBoundingBoxDescent;
1229
- h > 0 && (i = Math.max(i, h), s = Math.max(s, c));
1268
+ const n = this._tempContext.measureText(a), l = n.width, c = n.actualBoundingBoxAscent + n.actualBoundingBoxDescent;
1269
+ l > 0 && (i = Math.max(i, l), s = Math.max(s, c));
1230
1270
  }
1231
1271
  return {
1232
1272
  width: Math.ceil(i),
@@ -1234,7 +1274,7 @@ class pe {
1234
1274
  };
1235
1275
  }
1236
1276
  }
1237
- class _e {
1277
+ class pe {
1238
1278
  /**
1239
1279
  * Creates TextmodeCharacter objects with unique color assignments.
1240
1280
  * @param characters Array of character strings
@@ -1246,8 +1286,8 @@ class _e {
1246
1286
  const s = r.codePointAt(0) || 0, a = this._generateCharacterColor(i);
1247
1287
  let n = 0;
1248
1288
  if (t.hmtx && t.hmtx.aWidth) {
1249
- const h = this._getGlyphIndex(t, s);
1250
- h > 0 && t.hmtx.aWidth[h] !== void 0 && (n = t.hmtx.aWidth[h]);
1289
+ const l = this._getGlyphIndex(t, s);
1290
+ l > 0 && t.hmtx.aWidth[l] !== void 0 && (n = t.hmtx.aWidth[l]);
1251
1291
  }
1252
1292
  return {
1253
1293
  character: r,
@@ -1346,7 +1386,7 @@ class xe {
1346
1386
  o(this, "_textureAtlas");
1347
1387
  o(this, "_metricsCalculator");
1348
1388
  o(this, "_characterColorMapper");
1349
- this._fontSize = t, this._characterExtractor = new me(), this._textureAtlas = new ge(e), this._metricsCalculator = new pe(), this._characterColorMapper = new _e();
1389
+ this._fontSize = t, this._characterExtractor = new me(), this._textureAtlas = new ge(e), this._metricsCalculator = new _e(), this._characterColorMapper = new pe();
1350
1390
  }
1351
1391
  /**
1352
1392
  * Initializes the font manager by loading the font and creating the texture atlas.
@@ -1484,6 +1524,19 @@ class xe {
1484
1524
  get maxGlyphDimensions() {
1485
1525
  return this._maxGlyphDimensions;
1486
1526
  }
1527
+ /**
1528
+ * Dispose of all resources used by this font manager.
1529
+ * This method is idempotent and safe to call multiple times.
1530
+ */
1531
+ dispose() {
1532
+ this._fontFramebuffer && !this._fontFramebuffer.isDisposed && this._fontFramebuffer.dispose(), this._fontFace && document.fonts.has(this._fontFace) && document.fonts.delete(this._fontFace), this._fontFramebuffer = null, this._fontFace = null, this._font = null, this._characters = [], this._maxGlyphDimensions = { width: 0, height: 0 }, this._textureColumns = 0, this._textureRows = 0;
1533
+ }
1534
+ /**
1535
+ * Check if this font manager has been disposed
1536
+ */
1537
+ get isDisposed() {
1538
+ return this._fontFramebuffer === null || this._font === null;
1539
+ }
1487
1540
  /** Returns the font size used for rendering. */
1488
1541
  get fontSize() {
1489
1542
  return this._fontSize;
@@ -1598,6 +1651,19 @@ class ve {
1598
1651
  get cellHeight() {
1599
1652
  return this._cellHeight;
1600
1653
  }
1654
+ /**
1655
+ * Dispose of this TextmodeGrid and clean up references.
1656
+ * This method is idempotent and safe to call multiple times.
1657
+ */
1658
+ dispose() {
1659
+ this._canvas = null, this._cols = 0, this._rows = 0, this._width = 0, this._height = 0, this._offsetX = 0, this._offsetY = 0, this._cellWidth = 0, this._cellHeight = 0;
1660
+ }
1661
+ /**
1662
+ * Check if this TextmodeGrid has been disposed
1663
+ */
1664
+ get isDisposed() {
1665
+ return this._canvas === null;
1666
+ }
1601
1667
  /** Returns the number of columns in the grid. */
1602
1668
  get cols() {
1603
1669
  return this._cols;
@@ -1623,7 +1689,7 @@ class ve {
1623
1689
  return this._offsetY;
1624
1690
  }
1625
1691
  }
1626
- class Ce {
1692
+ class be {
1627
1693
  constructor(e, t = !1, r = {}) {
1628
1694
  o(this, "_canvas");
1629
1695
  o(this, "captureSource");
@@ -1633,7 +1699,7 @@ class Ce {
1633
1699
  createCanvas(e, t) {
1634
1700
  var i;
1635
1701
  const r = document.createElement("canvas");
1636
- if (r.className = "textmodeCanvas", this._isStandalone)
1702
+ if (r.className = "textmodeCanvas", r.style.imageRendering = "pixelated", this._isStandalone)
1637
1703
  r.width = e || 800, r.height = t || 600, document.body.appendChild(r);
1638
1704
  else {
1639
1705
  const s = this.captureSource.getBoundingClientRect();
@@ -1643,8 +1709,8 @@ class Ce {
1643
1709
  (a === 0 || n === 0) && u.videoWidth > 0 && u.videoHeight > 0 && (a = u.videoWidth, n = u.videoHeight);
1644
1710
  }
1645
1711
  r.width = a, r.height = n, r.style.position = "absolute", r.style.pointerEvents = "none";
1646
- const h = window.getComputedStyle(this.captureSource);
1647
- let c = parseInt(h.zIndex || "0", 10);
1712
+ const l = window.getComputedStyle(this.captureSource);
1713
+ let c = parseInt(l.zIndex || "0", 10);
1648
1714
  isNaN(c) && (c = 0), r.style.zIndex = (c + 1).toString(), this.positionOverlayCanvas(r), (i = this.captureSource.parentNode) == null || i.insertBefore(r, this.captureSource.nextSibling);
1649
1715
  }
1650
1716
  return r;
@@ -1688,6 +1754,27 @@ class Ce {
1688
1754
  throw new F("WebGL context could not be created. Ensure your browser supports WebGL.");
1689
1755
  return t;
1690
1756
  }
1757
+ /**
1758
+ * Dispose of this TextmodeCanvas and clean up all resources.
1759
+ * This method is idempotent and safe to call multiple times.
1760
+ */
1761
+ dispose() {
1762
+ if (this._canvas) {
1763
+ const e = this._canvas.getContext("webgl") || this._canvas.getContext("webgl2");
1764
+ if (e) {
1765
+ const t = e.getExtension("WEBGL_lose_context");
1766
+ t && t.loseContext();
1767
+ }
1768
+ this._canvas.parentNode && this._canvas.parentNode.removeChild(this._canvas), this._canvas = null;
1769
+ }
1770
+ this.captureSource = null;
1771
+ }
1772
+ /**
1773
+ * Check if this TextmodeCanvas has been disposed
1774
+ */
1775
+ get isDisposed() {
1776
+ return this._canvas === null;
1777
+ }
1691
1778
  get canvas() {
1692
1779
  return this._canvas;
1693
1780
  }
@@ -1745,6 +1832,19 @@ class U {
1745
1832
  disable() {
1746
1833
  this.enabled(!1);
1747
1834
  }
1835
+ /**
1836
+ * Dispose of all framebuffers used by this converter.
1837
+ * This method is idempotent and safe to call multiple times.
1838
+ */
1839
+ dispose() {
1840
+ this._characterFramebuffer && !this._characterFramebuffer.isDisposed && this._characterFramebuffer.dispose(), this._primaryColorFramebuffer && !this._primaryColorFramebuffer.isDisposed && this._primaryColorFramebuffer.dispose(), this._secondaryColorFramebuffer && !this._secondaryColorFramebuffer.isDisposed && this._secondaryColorFramebuffer.dispose(), this._rotationFramebuffer && !this._rotationFramebuffer.isDisposed && this._rotationFramebuffer.dispose(), this._transformFramebuffer && !this._transformFramebuffer.isDisposed && this._transformFramebuffer.dispose(), this._characterFramebuffer = null, this._primaryColorFramebuffer = null, this._secondaryColorFramebuffer = null, this._rotationFramebuffer = null, this._transformFramebuffer = null;
1841
+ }
1842
+ /**
1843
+ * Check if this converter has been disposed
1844
+ */
1845
+ get isDisposed() {
1846
+ return this._characterFramebuffer === null;
1847
+ }
1748
1848
  /** Returns the framebuffer containing character data. */
1749
1849
  get characterFramebuffer() {
1750
1850
  return this._characterFramebuffer;
@@ -1770,7 +1870,7 @@ class U {
1770
1870
  return this._options;
1771
1871
  }
1772
1872
  }
1773
- class be {
1873
+ class Ce {
1774
1874
  /**
1775
1875
  * Create a new color palette instance.
1776
1876
  * @param renderer The renderer instance.
@@ -1829,7 +1929,7 @@ class X extends U {
1829
1929
  constructor(t, r, i, s = {}) {
1830
1930
  super(t, r, i, s);
1831
1931
  o(this, "palette");
1832
- this.palette = new be(this.renderer, this.fontManager.getCharacterColors(" .:-=+*%@#"));
1932
+ this.palette = new Ce(this.renderer, this.fontManager.getCharacterColors(" .:-=+*%@#"));
1833
1933
  }
1834
1934
  /**
1835
1935
  * Sets the characters used for mapping.
@@ -2167,6 +2267,21 @@ class Ue {
2167
2267
  for (const e of this.converters)
2168
2268
  e.converter.enable();
2169
2269
  }
2270
+ /**
2271
+ * Dispose of all resources used by this conversion pipeline.
2272
+ * This method is idempotent and safe to call multiple times.
2273
+ */
2274
+ dispose() {
2275
+ for (const e of this.converters)
2276
+ e.converter && !e.converter.isDisposed && e.converter.dispose();
2277
+ this._characterFramebuffer && !this._characterFramebuffer.isDisposed && this._characterFramebuffer.dispose(), this._primaryColorFramebuffer && !this._primaryColorFramebuffer.isDisposed && this._primaryColorFramebuffer.dispose(), this._secondaryColorFramebuffer && !this._secondaryColorFramebuffer.isDisposed && this._secondaryColorFramebuffer.dispose(), this._rotationFramebuffer && !this._rotationFramebuffer.isDisposed && this._rotationFramebuffer.dispose(), this._transformFramebuffer && !this._transformFramebuffer.isDisposed && this._transformFramebuffer.dispose(), this._resultFramebuffer && !this._resultFramebuffer.isDisposed && this._resultFramebuffer.dispose(), this._asciiShader && !this._asciiShader.isDisposed && this._asciiShader.dispose(), this.converters = [], this._characterFramebuffer = null, this._primaryColorFramebuffer = null, this._secondaryColorFramebuffer = null, this._rotationFramebuffer = null, this._transformFramebuffer = null, this._resultFramebuffer = null, this._asciiShader = null;
2278
+ }
2279
+ /**
2280
+ * Check if this conversion pipeline has been disposed
2281
+ */
2282
+ get isDisposed() {
2283
+ return this._resultFramebuffer === null || this._asciiShader === null;
2284
+ }
2170
2285
  /** Returns the character framebuffer containing the combined result of all converters. */
2171
2286
  get characterFramebuffer() {
2172
2287
  return this._characterFramebuffer;
@@ -2287,7 +2402,7 @@ class N {
2287
2402
  return `'textmode-export'-${this.generateTimestamp()}`;
2288
2403
  }
2289
2404
  }
2290
- class Ae extends H {
2405
+ class De extends H {
2291
2406
  /**
2292
2407
  * Extracts transform data from transform pixels
2293
2408
  * @param transformPixels Transform framebuffer pixels
@@ -2296,10 +2411,10 @@ class Ae extends H {
2296
2411
  * @returns Transform data object
2297
2412
  */
2298
2413
  extractTransformData(e, t, r) {
2299
- const i = e[r], s = e[r + 1], a = e[r + 2], n = i === 255, h = s === 255, c = a === 255, u = t[r], d = t[r + 1], f = u + d / 255, g = Math.round(f * 360 / 255 * 100) / 100;
2414
+ const i = e[r], s = e[r + 1], a = e[r + 2], n = i === 255, l = s === 255, c = a === 255, u = t[r], d = t[r + 1], f = u + d / 255, g = Math.round(f * 360 / 255 * 100) / 100;
2300
2415
  return {
2301
2416
  isInverted: n,
2302
- flipHorizontal: h,
2417
+ flipHorizontal: l,
2303
2418
  flipVertical: c,
2304
2419
  rotation: g
2305
2420
  };
@@ -2331,7 +2446,7 @@ class Ae extends H {
2331
2446
  let i = 0;
2332
2447
  for (let s = 0; s < t.rows; s++)
2333
2448
  for (let a = 0; a < t.cols; a++) {
2334
- const n = i * 4, h = this.getCharacterIndex(
2449
+ const n = i * 4, l = this.getCharacterIndex(
2335
2450
  e.characterPixels,
2336
2451
  n
2337
2452
  );
@@ -2347,7 +2462,7 @@ class Ae extends H {
2347
2462
  }
2348
2463
  const f = this.calculateCellPosition(a, s, t);
2349
2464
  r.push({
2350
- charIndex: h,
2465
+ charIndex: l,
2351
2466
  primaryColor: c,
2352
2467
  secondaryColor: u,
2353
2468
  transform: d,
@@ -2357,7 +2472,7 @@ class Ae extends H {
2357
2472
  return r;
2358
2473
  }
2359
2474
  }
2360
- class Me {
2475
+ class Ae {
2361
2476
  /**
2362
2477
  * Gets the glyph index for a given Unicode code point in a Typr.js font
2363
2478
  * @param fontData The Typr.js font data
@@ -2428,29 +2543,29 @@ class Me {
2428
2543
  */
2429
2544
  glyphToSVGPath(e, t, r, i) {
2430
2545
  if (!e || !e.xs) return "";
2431
- const { xs: s, ys: a, endPts: n, flags: h } = e;
2432
- if (!s || !a || !n || !h) return "";
2546
+ const { xs: s, ys: a, endPts: n, flags: l } = e;
2547
+ if (!s || !a || !n || !l) return "";
2433
2548
  let c = "", u = 0;
2434
2549
  for (let d = 0; d < n.length; d++) {
2435
2550
  const f = n[d];
2436
2551
  if (!(f < u)) {
2437
2552
  if (f >= u) {
2438
- const g = t + s[u] * i, _ = r - a[u] * i;
2439
- c += `M${g.toFixed(2)},${_.toFixed(2)}`;
2440
- let p = u + 1;
2441
- for (; p <= f; )
2442
- if ((h[p] & 1) !== 0) {
2443
- const b = t + s[p] * i, v = r - a[p] * i;
2444
- c += `L${b.toFixed(2)},${v.toFixed(2)}`, p++;
2553
+ const g = t + s[u] * i, p = r - a[u] * i;
2554
+ c += `M${g.toFixed(2)},${p.toFixed(2)}`;
2555
+ let _ = u + 1;
2556
+ for (; _ <= f; )
2557
+ if ((l[_] & 1) !== 0) {
2558
+ const C = t + s[_] * i, v = r - a[_] * i;
2559
+ c += `L${C.toFixed(2)},${v.toFixed(2)}`, _++;
2445
2560
  } else {
2446
- const b = t + s[p] * i, v = r - a[p] * i;
2447
- let w = p + 1 > f ? u : p + 1;
2448
- if ((h[w] & 1) !== 0) {
2561
+ const C = t + s[_] * i, v = r - a[_] * i;
2562
+ let w = _ + 1 > f ? u : _ + 1;
2563
+ if ((l[w] & 1) !== 0) {
2449
2564
  const R = t + s[w] * i, y = r - a[w] * i;
2450
- c += `Q${b.toFixed(2)},${v.toFixed(2)} ${R.toFixed(2)},${y.toFixed(2)}`, p = w + 1;
2565
+ c += `Q${C.toFixed(2)},${v.toFixed(2)} ${R.toFixed(2)},${y.toFixed(2)}`, _ = w + 1;
2451
2566
  } else {
2452
- const R = t + s[w] * i, y = r - a[w] * i, P = (b + R) / 2, D = (v + y) / 2;
2453
- c += `Q${b.toFixed(2)},${v.toFixed(2)} ${P.toFixed(2)},${D.toFixed(2)}`, p = w;
2567
+ const R = t + s[w] * i, y = r - a[w] * i, P = (C + R) / 2, M = (v + y) / 2;
2568
+ c += `Q${C.toFixed(2)},${v.toFixed(2)} ${P.toFixed(2)},${M.toFixed(2)}`, _ = w;
2454
2569
  }
2455
2570
  }
2456
2571
  c += "Z";
@@ -2474,13 +2589,13 @@ class Me {
2474
2589
  const a = e.codePointAt(0) || 0, n = this.getGlyphIndex(t, a);
2475
2590
  if (n === 0)
2476
2591
  return this.createEmptyPath();
2477
- let h = null;
2592
+ let l = null;
2478
2593
  try {
2479
- t.glyf && t.glyf[n] !== null ? h = t.glyf[n] : m && m.T && m.T.glyf && m.T.glyf._parseGlyf && (h = m.T.glyf._parseGlyf(t, n), t.glyf && h && (t.glyf[n] = h));
2594
+ t.glyf && t.glyf[n] !== null ? l = t.glyf[n] : m && m.T && m.T.glyf && m.T.glyf._parseGlyf && (l = m.T.glyf._parseGlyf(t, n), t.glyf && l && (t.glyf[n] = l));
2480
2595
  } catch (c) {
2481
2596
  console.warn(`Failed to parse glyph ${n}:`, c);
2482
2597
  }
2483
- return h ? this.createGlyphPath(t, h, r, i, s) : this.createEmptyPath();
2598
+ return l ? this.createGlyphPath(t, l, r, i, s) : this.createEmptyPath();
2484
2599
  } catch (a) {
2485
2600
  return console.warn(`Failed to generate path for character "${e}":`, a), this.createEmptyPath();
2486
2601
  }
@@ -2497,19 +2612,19 @@ class Me {
2497
2612
  * @param advanceWidth Character advance width
2498
2613
  * @returns SVG path data string or null if generation fails
2499
2614
  */
2500
- generatePositionedCharacterPath(e, t, r, i, s, a, n, h) {
2615
+ generatePositionedCharacterPath(e, t, r, i, s, a, n, l) {
2501
2616
  try {
2502
- const c = n / t.head.unitsPerEm, u = h * c, d = r + (s - u) / 2, f = i + (a + n * 0.7) / 2;
2617
+ const c = n / t.head.unitsPerEm, u = l * c, d = r + (s - u) / 2, f = i + (a + n * 0.7) / 2;
2503
2618
  return this.generateCharacterPath(e, t, d, f, n).toSVG() || null;
2504
2619
  } catch (c) {
2505
2620
  return console.warn(`Failed to generate positioned character path for "${e}":`, c), null;
2506
2621
  }
2507
2622
  }
2508
2623
  }
2509
- class De {
2624
+ class Me {
2510
2625
  constructor() {
2511
2626
  o(this, "pathGenerator");
2512
- this.pathGenerator = new Me();
2627
+ this.pathGenerator = new Ae();
2513
2628
  }
2514
2629
  /**
2515
2630
  * Generates the SVG header with metadata
@@ -2563,8 +2678,8 @@ class De {
2563
2678
  generateTransformAttribute(e, t) {
2564
2679
  const { transform: r, position: i } = e, s = i.cellX + t.cellWidth / 2, a = i.cellY + t.cellHeight / 2, n = [];
2565
2680
  if (r.flipHorizontal || r.flipVertical) {
2566
- const h = r.flipHorizontal ? -1 : 1, c = r.flipVertical ? -1 : 1;
2567
- n.push(`translate(${s} ${a})`), n.push(`scale(${h} ${c})`), n.push(`translate(${-s} ${-a})`);
2681
+ const l = r.flipHorizontal ? -1 : 1, c = r.flipVertical ? -1 : 1;
2682
+ n.push(`translate(${s} ${a})`), n.push(`scale(${l} ${c})`), n.push(`translate(${-s} ${-a})`);
2568
2683
  }
2569
2684
  return r.rotation && n.push(`rotate(${r.rotation} ${s} ${a})`), n.length ? ` transform="${n.join(" ")}"` : "";
2570
2685
  }
@@ -2689,7 +2804,7 @@ class Y {
2689
2804
  o(this, "dataExtractor");
2690
2805
  o(this, "contentGenerator");
2691
2806
  o(this, "fileHandler");
2692
- this.dataExtractor = new Ae(), this.contentGenerator = new De(), this.fileHandler = new Ie();
2807
+ this.dataExtractor = new De(), this.contentGenerator = new Me(), this.fileHandler = new Ie();
2693
2808
  }
2694
2809
  /**
2695
2810
  * Applies default values to SVG export options
@@ -2749,7 +2864,7 @@ class Pe extends H {
2749
2864
  var n;
2750
2865
  const s = [];
2751
2866
  let a = 0;
2752
- for (let h = 0; h < t.rows; h++) {
2867
+ for (let l = 0; l < t.rows; l++) {
2753
2868
  const c = [];
2754
2869
  for (let u = 0; u < t.cols; u++) {
2755
2870
  const d = a * 4, f = this.getCharacterIndex(
@@ -2869,8 +2984,8 @@ class Ve extends H {
2869
2984
  const i = e.canvas;
2870
2985
  if (t === 1 && r === "transparent")
2871
2986
  return i;
2872
- const s = document.createElement("canvas"), a = s.getContext("2d"), n = Math.round(i.width * t), h = Math.round(i.height * t);
2873
- return s.width = n, s.height = h, r !== "transparent" && (a.fillStyle = r, a.fillRect(0, 0, n, h)), a.imageSmoothingEnabled = !1, a.drawImage(
2987
+ const s = document.createElement("canvas"), a = s.getContext("2d"), n = Math.round(i.width * t), l = Math.round(i.height * t);
2988
+ return s.width = n, s.height = l, r !== "transparent" && (a.fillStyle = r, a.fillRect(0, 0, n, l)), a.imageSmoothingEnabled = !1, a.drawImage(
2874
2989
  i,
2875
2990
  0,
2876
2991
  0,
@@ -2879,7 +2994,7 @@ class Ve extends H {
2879
2994
  0,
2880
2995
  0,
2881
2996
  n,
2882
- h
2997
+ l
2883
2998
  ), s;
2884
2999
  }
2885
3000
  }
@@ -2930,12 +3045,12 @@ const q = {
2930
3045
  png: "image/png",
2931
3046
  jpg: "image/jpeg",
2932
3047
  webp: "image/webp"
2933
- }, $ = {
3048
+ }, z = {
2934
3049
  png: ".png",
2935
3050
  jpg: ".jpg",
2936
3051
  webp: ".webp"
2937
3052
  };
2938
- class $e extends N {
3053
+ class ze extends N {
2939
3054
  /**
2940
3055
  * Saves image content as a downloadable file
2941
3056
  * @param content The image content (data URL or blob)
@@ -2944,7 +3059,7 @@ class $e extends N {
2944
3059
  */
2945
3060
  saveImage(e, t, r) {
2946
3061
  try {
2947
- const i = $[r];
3062
+ const i = z[r];
2948
3063
  typeof e == "string" ? this.saveImageFromDataURL(e, this.sanitizeFilename(t) + i) : this.saveImageFromBlob(e, this.sanitizeFilename(t) + i);
2949
3064
  } catch (i) {
2950
3065
  throw console.error(`Failed to save ${r.toUpperCase()} image:`, i), new Error(`Image save failed: ${i instanceof Error ? i.message : "Unknown error"}`);
@@ -2979,7 +3094,7 @@ class $e extends N {
2979
3094
  * @returns True if the format is supported for saving
2980
3095
  */
2981
3096
  validateSaveSupport(e) {
2982
- return e in q && e in $;
3097
+ return e in q && e in z;
2983
3098
  }
2984
3099
  /**
2985
3100
  * Gets the MIME type for the specified image format
@@ -2995,15 +3110,15 @@ class $e extends N {
2995
3110
  * @returns The file extension (including the dot)
2996
3111
  */
2997
3112
  getFileExtension(e) {
2998
- return $[e];
3113
+ return z[e];
2999
3114
  }
3000
3115
  }
3001
- class ze {
3116
+ class $e {
3002
3117
  constructor() {
3003
3118
  o(this, "dataExtractor");
3004
3119
  o(this, "contentGenerator");
3005
3120
  o(this, "fileHandler");
3006
- this.dataExtractor = new Ve(), this.contentGenerator = new ke(), this.fileHandler = new $e();
3121
+ this.dataExtractor = new Ve(), this.contentGenerator = new ke(), this.fileHandler = new ze();
3007
3122
  }
3008
3123
  /**
3009
3124
  * Applies default values to image export options
@@ -3117,6 +3232,9 @@ class I {
3117
3232
  });
3118
3233
  o(this, "_resizedCallback", () => {
3119
3234
  });
3235
+ // Destroy state
3236
+ o(this, "_isDestroyed", !1);
3237
+ o(this, "_windowResizeListener", null);
3120
3238
  this.captureSource = e, this._standalone = e === null, this._mode = t.renderMode ?? "auto", this._frameRateLimit = t.frameRate ?? 60, this.frameInterval = 1e3 / this._frameRateLimit;
3121
3239
  }
3122
3240
  /**
@@ -3127,16 +3245,16 @@ class I {
3127
3245
  */
3128
3246
  static async create(e = null, t = {}) {
3129
3247
  const r = new I(e, t), i = r._standalone ? t : void 0;
3130
- r.textmodeCanvas = new Ce(r.captureSource, r._standalone, i), r._renderer = new fe(r.textmodeCanvas.getWebGLContext());
3248
+ r.textmodeCanvas = new be(r.captureSource, r._standalone, i), r._renderer = new fe(r.textmodeCanvas.getWebGLContext());
3131
3249
  let s, a;
3132
3250
  r._standalone ? (s = t.width || 800, a = t.height || 600) : (s = r.textmodeCanvas.width || 800, a = r.textmodeCanvas.height || 600), r._canvasFramebuffer = r._renderer.createFramebuffer(s, a), r._font = new xe(r._renderer, t.fontSize ?? 16), await r._font.initialize(t.fontSource);
3133
3251
  const n = r._font.maxGlyphDimensions;
3134
3252
  return r._grid = new ve(r.textmodeCanvas.canvas, n.width, n.height), r._pipeline = new Ue(r._renderer, r._font, r._grid), r.setupEventListeners(), r.startAutoRendering(), r;
3135
3253
  }
3136
3254
  setupEventListeners() {
3137
- window.addEventListener("resize", () => {
3255
+ this._windowResizeListener = () => {
3138
3256
  this._standalone ? this._resizedCallback() : this.resize();
3139
- }), window.ResizeObserver && this.captureSource && !this._standalone && (this.resizeObserver = new ResizeObserver(() => {
3257
+ }, window.addEventListener("resize", this._windowResizeListener), window.ResizeObserver && this.captureSource && !this._standalone && (this.resizeObserver = new ResizeObserver(() => {
3140
3258
  this.resize();
3141
3259
  }), this.resizeObserver.observe(this.captureSource));
3142
3260
  }
@@ -3288,7 +3406,7 @@ class I {
3288
3406
  * ```
3289
3407
  */
3290
3408
  async saveCanvas(e, t = "png", r = {}) {
3291
- await new ze().saveImage(this.textmodeCanvas, {
3409
+ await new $e().saveImage(this.textmodeCanvas, {
3292
3410
  ...r,
3293
3411
  filename: e,
3294
3412
  format: t
@@ -3358,7 +3476,19 @@ class I {
3358
3476
  * ```
3359
3477
  */
3360
3478
  render() {
3361
- this.measureFrameRate(), this._frameCount++, this._standalone || this._canvasFramebuffer.update(this.captureSource), this._canvasFramebuffer.begin(), this._drawCallback(), this._renderer.reset(), this._canvasFramebuffer.end(), this._pipeline.render(this._canvasFramebuffer), this._pipeline.hasEnabledConverters() ? (this._renderer.background(0), this._renderer.image(this._pipeline.texture, this._grid.offsetX, this._grid.offsetY, this._pipeline.texture.width, this._pipeline.texture.height)) : (this._renderer.clear(), this._renderer.image(this._canvasFramebuffer, this._grid.offsetX, this._grid.offsetY, this._canvasFramebuffer.width, this._canvasFramebuffer.height));
3479
+ if (this._isDestroyed) {
3480
+ console.warn("Cannot render: Textmodifier instance has been destroyed");
3481
+ return;
3482
+ }
3483
+ if (this.measureFrameRate(), this._frameCount++, !this._canvasFramebuffer || !this._renderer || !this._pipeline) {
3484
+ console.warn("Cannot render: Required resources have been disposed");
3485
+ return;
3486
+ }
3487
+ if (this._standalone ? this._canvasFramebuffer && (this._canvasFramebuffer.begin(), this._drawCallback(), this._renderer && this._renderer.reset(), this._canvasFramebuffer && this._canvasFramebuffer.end()) : this.captureSource && this._canvasFramebuffer && this._canvasFramebuffer.update(this.captureSource), !this._pipeline || !this._renderer) {
3488
+ console.warn("Cannot complete render: Pipeline or renderer has been disposed");
3489
+ return;
3490
+ }
3491
+ this._pipeline.render(this._canvasFramebuffer), this._pipeline.hasEnabledConverters() ? this._renderer && this._pipeline.texture && this._grid && (this._renderer.background(0), this._renderer.image(this._pipeline.texture, this._grid.offsetX, this._grid.offsetY, this._pipeline.texture.width, this._pipeline.texture.height)) : this._renderer && this._canvasFramebuffer && this._grid && (this._renderer.clear(), this._renderer.image(this._canvasFramebuffer, this._grid.offsetX, this._grid.offsetY, this._canvasFramebuffer.width, this._canvasFramebuffer.height));
3362
3492
  }
3363
3493
  resize() {
3364
3494
  this.textmodeCanvas.resize(), this._canvasFramebuffer.resize(this.textmodeCanvas.width, this.textmodeCanvas.height), this._grid.resize(), this._pipeline.resize(), this._renderer.resetViewport(), this._mode !== "manual" && this.render();
@@ -3367,11 +3497,13 @@ class I {
3367
3497
  * Start automatic rendering
3368
3498
  */
3369
3499
  startAutoRendering() {
3370
- if (this._mode !== "auto") return;
3500
+ if (this._mode !== "auto" || this._isDestroyed) return;
3371
3501
  this.lastFrameTime = performance.now();
3372
3502
  const e = (t) => {
3503
+ if (this._isDestroyed)
3504
+ return;
3373
3505
  const r = t - this.lastFrameTime;
3374
- r >= this.frameInterval && (this.render(), this.lastFrameTime = t - r % this.frameInterval), this.animationFrameId = requestAnimationFrame(e);
3506
+ r >= this.frameInterval && (this.render(), this.lastFrameTime = t - r % this.frameInterval), this._isDestroyed || (this.animationFrameId = requestAnimationFrame(e));
3375
3507
  };
3376
3508
  this.animationFrameId = requestAnimationFrame(e);
3377
3509
  }
@@ -3380,6 +3512,7 @@ class I {
3380
3512
  * Uses a rolling average for smoother frame rate reporting
3381
3513
  */
3382
3514
  measureFrameRate() {
3515
+ if (this._isDestroyed) return;
3383
3516
  const e = performance.now();
3384
3517
  if (this.lastRenderTime > 0) {
3385
3518
  const t = e - this.lastRenderTime;
@@ -3420,6 +3553,10 @@ class I {
3420
3553
  * ```
3421
3554
  */
3422
3555
  renderMode(e) {
3556
+ if (this._isDestroyed) {
3557
+ console.warn("Cannot change render mode: Textmodifier instance has been destroyed");
3558
+ return;
3559
+ }
3423
3560
  this._mode !== e && (this.stopAutoRendering(), this._mode = e, e === "auto" && this.startAutoRendering());
3424
3561
  }
3425
3562
  /**
@@ -3973,33 +4110,77 @@ class I {
3973
4110
  setUniform(e, t) {
3974
4111
  this._renderer.setUniform(e, t);
3975
4112
  }
4113
+ /**
4114
+ * Completely destroy this Textmodifier instance and free all associated resources.
4115
+ *
4116
+ * This method performs comprehensive cleanup of:
4117
+ * - WebGL resources (framebuffers, textures, shaders)
4118
+ * - Animation frames and timers
4119
+ * - Event listeners
4120
+ * - DOM elements
4121
+ * - Font resources
4122
+ *
4123
+ * After calling this method, the instance should not be used and will be eligible for garbage collection.
4124
+ * This method is idempotent and safe to call multiple times.
4125
+ *
4126
+ * @example
4127
+ * ```javascript
4128
+ * // Create a textmodifier instance
4129
+ * const textmodifier = await textmode.create(canvas);
4130
+ *
4131
+ * // Use it for rendering
4132
+ * textmodifier.render();
4133
+ *
4134
+ * // When done, completely clean up
4135
+ * textmodifier.destroy();
4136
+ *
4137
+ * // Instance is now safely disposed and ready for garbage collection
4138
+ * ```
4139
+ */
4140
+ destroy() {
4141
+ this._isDestroyed || (this._isDestroyed = !0, this.stopAutoRendering(), this._windowResizeListener && (window.removeEventListener("resize", this._windowResizeListener), this._windowResizeListener = null), this.resizeObserver && (this.resizeObserver.disconnect(), this.resizeObserver = null), this._pipeline && !this._pipeline.isDisposed && this._pipeline.dispose(), this._font && !this._font.isDisposed && this._font.dispose(), this._grid && !this._grid.isDisposed && this._grid.dispose(), this._canvasFramebuffer && !this._canvasFramebuffer.isDisposed && this._canvasFramebuffer.dispose(), this._renderer && !this._renderer.isDisposed && this._renderer.dispose(), this.textmodeCanvas && !this.textmodeCanvas.isDisposed && this.textmodeCanvas.dispose(), this.captureSource = null, this.textmodeCanvas = null, this._renderer = null, this._canvasFramebuffer = null, this._font = null, this._grid = null, this._pipeline = null, this.resizeObserver = null, this._drawCallback = () => {
4142
+ }, this._resizedCallback = () => {
4143
+ }, this.animationFrameId = null, this.lastFrameTime = 0, this.lastRenderTime = 0, this._frameCount = 0, this._frameRate = 0, this.frameTimeHistory = []);
4144
+ }
3976
4145
  /** Get the current grid object used for rendering. */
3977
4146
  get grid() {
4147
+ if (this._isDestroyed)
4148
+ throw new Error("Cannot access grid: Textmodifier instance has been destroyed");
3978
4149
  return this._grid;
3979
4150
  }
3980
4151
  /** Get the current font object used for rendering. */
3981
4152
  get font() {
4153
+ if (this._isDestroyed)
4154
+ throw new Error("Cannot access font: Textmodifier instance has been destroyed");
3982
4155
  return this._font;
3983
4156
  }
3984
4157
  /** Get the current rendering mode.*/
3985
4158
  get mode() {
4159
+ if (this._isDestroyed)
4160
+ throw new Error("Cannot access mode: Textmodifier instance has been destroyed");
3986
4161
  return this._mode;
3987
4162
  }
3988
4163
  /** Get the current textmode conversion pipeline. */
3989
4164
  get pipeline() {
4165
+ if (this._isDestroyed)
4166
+ throw new Error("Cannot access pipeline: Textmodifier instance has been destroyed");
3990
4167
  return this._pipeline;
3991
4168
  }
3992
4169
  /** Get the current frame count. */
3993
4170
  get frameCount() {
3994
- return this._frameCount;
4171
+ return this._isDestroyed ? 0 : this._frameCount;
3995
4172
  }
3996
4173
  /** Get the width of the canvas. */
3997
4174
  get width() {
3998
- return this.textmodeCanvas.width;
4175
+ return this._isDestroyed ? 0 : this.textmodeCanvas.width;
3999
4176
  }
4000
4177
  /** Get the height of the canvas. */
4001
4178
  get height() {
4002
- return this.textmodeCanvas.height;
4179
+ return this._isDestroyed ? 0 : this.textmodeCanvas.height;
4180
+ }
4181
+ /** Check if this Textmodifier instance has been destroyed. */
4182
+ get isDestroyed() {
4183
+ return this._isDestroyed;
4003
4184
  }
4004
4185
  }
4005
4186
  class O {
@@ -4046,6 +4227,20 @@ class O {
4046
4227
  * // Draw a rectangle with the fill color
4047
4228
  * t.rect(x, y, 200, 150);
4048
4229
  * });
4230
+ *
4231
+ * ////////
4232
+ *
4233
+ * // Resource management example
4234
+ * const textmodifier = await textmode.create(canvas);
4235
+ *
4236
+ * // Use the textmodifier...
4237
+ * textmodifier.render();
4238
+ *
4239
+ * // When done, completely clean up all resources
4240
+ * textmodifier.destroy();
4241
+ *
4242
+ * // The instance is now safely disposed and ready for garbage collection
4243
+ * console.log(textmodifier.isDestroyed); // true
4049
4244
  * ```
4050
4245
  */
4051
4246
  static async create(e, t = {}) {
@@ -4088,7 +4283,7 @@ const He = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
4088
4283
  __proto__: null
4089
4284
  }, Symbol.toStringTag, { value: "Module" })), Ne = O.create, Oe = O.setErrorLevel, We = O.version;
4090
4285
  export {
4091
- Ce as TextmodeCanvas,
4286
+ be as TextmodeCanvas,
4092
4287
  Ue as TextmodeConversionPipeline,
4093
4288
  ne as TextmodeErrorLevel,
4094
4289
  xe as TextmodeFont,