textmode.js 0.1.3 → 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.
@@ -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");
@@ -239,9 +253,9 @@ class M {
239
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
255
  c ? (f = r / l * 2 - 1, g = (r + s) / l * 2 - 1) : (f = 1 - r / l * 2, g = 1 - (r + s) / l * 2);
242
- let _, p, C, b;
243
- _ = u, C = d, p = f, b = g;
244
- const v = this.generateVertices(_, p, C, b);
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
  /**
@@ -303,7 +317,7 @@ class he 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,7 +326,7 @@ class he 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
  }
@@ -333,9 +347,9 @@ class le {
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 / l * 2 - 1, K = R / l * 2 - 1, ee = P / l * 2 - 1, te = Z / l * 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,
@@ -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
  */
@@ -732,8 +759,8 @@ class fe {
732
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;
@@ -755,15 +782,15 @@ 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, l = (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, l - 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)
@@ -772,8 +799,8 @@ class fe {
772
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: l };
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,6 +832,19 @@ 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
  */
@@ -834,8 +874,8 @@ m.parse = function(h) {
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 = l[_];
838
- p == null && (p = u[f].parseTab(s, _, g[1], d)), d[f] = l[_] = 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;
@@ -900,15 +940,15 @@ m.T.cmap = {
900
940
  e += 2;
901
941
  var f = i.readUint(h, e);
902
942
  e += 4;
903
- var g = "p" + u + "e" + d, _ = l.indexOf(f);
904
- if (_ == -1) {
905
- _ = r.tables.length;
906
- var p = {};
943
+ var g = "p" + u + "e" + d, p = l.indexOf(f);
944
+ if (p == -1) {
945
+ p = r.tables.length;
946
+ var _ = {};
907
947
  l.push(f);
908
- var C = p.format = s(h, f);
909
- C == 4 ? p = a.parse4(h, f, p) : C == 12 && (p = a.parse12(h, f, p)), r.tables.push(p);
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
  },
@@ -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;
@@ -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
  */
@@ -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
@@ -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,8 +1699,8 @@ 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)
1637
- r.width = e || 800, r.height = t || 600, r.style.width = e + "px", r.style.height = t + "px", document.body.appendChild(r);
1702
+ if (r.className = "textmodeCanvas", r.style.imageRendering = "pixelated", this._isStandalone)
1703
+ r.width = e || 800, r.height = t || 600, document.body.appendChild(r);
1638
1704
  else {
1639
1705
  const s = this.captureSource.getBoundingClientRect();
1640
1706
  let a = Math.round(s.width), n = Math.round(s.height);
@@ -1642,7 +1708,7 @@ class Ce {
1642
1708
  const u = this.captureSource;
1643
1709
  (a === 0 || n === 0) && u.videoWidth > 0 && u.videoHeight > 0 && (a = u.videoWidth, n = u.videoHeight);
1644
1710
  }
1645
- r.width = a, r.height = n, r.style.width = a + "px", r.style.height = n + "px", r.style.position = "absolute", r.style.pointerEvents = "none";
1711
+ r.width = a, r.height = n, r.style.position = "absolute", r.style.pointerEvents = "none";
1646
1712
  const l = window.getComputedStyle(this.captureSource);
1647
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);
@@ -1660,7 +1726,7 @@ class Ce {
1660
1726
  }
1661
1727
  resize(e, t) {
1662
1728
  if (this._isStandalone)
1663
- this._canvas.width = e ?? this._canvas.width, this._canvas.height = t ?? this._canvas.height, this._canvas.style.width = (e ?? this._canvas.width) + "px", this._canvas.style.height = (t ?? this._canvas.height) + "px";
1729
+ this._canvas.width = e ?? this._canvas.width, this._canvas.height = t ?? this._canvas.height;
1664
1730
  else {
1665
1731
  const r = this.captureSource.getBoundingClientRect();
1666
1732
  let i = Math.round(r.width), s = Math.round(r.height);
@@ -1668,7 +1734,7 @@ class Ce {
1668
1734
  const a = this.captureSource;
1669
1735
  (i === 0 || s === 0) && a.videoWidth > 0 && a.videoHeight > 0 && (i = a.videoWidth, s = a.videoHeight);
1670
1736
  }
1671
- this._canvas.width = i, this._canvas.height = s, this._canvas.style.width = i + "px", this._canvas.style.height = s + "px", this.positionOverlayCanvas(this._canvas);
1737
+ this._canvas.width = i, this._canvas.height = s, this.positionOverlayCanvas(this._canvas);
1672
1738
  }
1673
1739
  }
1674
1740
  /**
@@ -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
@@ -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
@@ -2435,22 +2550,22 @@ class Me {
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 ((l[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;
2561
+ const C = t + s[_] * i, v = r - a[_] * i;
2562
+ let w = _ + 1 > f ? u : _ + 1;
2448
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";
@@ -2506,10 +2621,10 @@ class Me {
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
@@ -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
@@ -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 = {}) {
@@ -4078,7 +4273,7 @@ class O {
4078
4273
  * ```
4079
4274
  */
4080
4275
  static get version() {
4081
- return "0.1.2";
4276
+ return "0.1.4-beta.1";
4082
4277
  }
4083
4278
  constructor() {
4084
4279
  throw new Error("Textmode is a static class and cannot be instantiated.");
@@ -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,