textmode.export.js 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +166 -0
  2. package/dist/textmode.export.esm.js +2061 -0
  3. package/dist/textmode.export.umd.js +3 -0
  4. package/dist/types/exporters/base/DataExtractor.d.ts +23 -0
  5. package/dist/types/exporters/base/DataExtractor.d.ts.map +1 -0
  6. package/dist/types/exporters/base/FileHandler.d.ts +24 -0
  7. package/dist/types/exporters/base/FileHandler.d.ts.map +1 -0
  8. package/dist/types/exporters/base/index.d.ts +3 -0
  9. package/dist/types/exporters/base/index.d.ts.map +1 -0
  10. package/dist/types/exporters/gif/GIFExporter.d.ts +30 -0
  11. package/dist/types/exporters/gif/GIFExporter.d.ts.map +1 -0
  12. package/dist/types/exporters/gif/GIFRecorder.d.ts +24 -0
  13. package/dist/types/exporters/gif/GIFRecorder.d.ts.map +1 -0
  14. package/dist/types/exporters/gif/index.d.ts +4 -0
  15. package/dist/types/exporters/gif/index.d.ts.map +1 -0
  16. package/dist/types/exporters/gif/types.d.ts +69 -0
  17. package/dist/types/exporters/gif/types.d.ts.map +1 -0
  18. package/dist/types/exporters/image/ImageExporter.d.ts +40 -0
  19. package/dist/types/exporters/image/ImageExporter.d.ts.map +1 -0
  20. package/dist/types/exporters/image/index.d.ts +4 -0
  21. package/dist/types/exporters/image/index.d.ts.map +1 -0
  22. package/dist/types/exporters/image/types.d.ts +45 -0
  23. package/dist/types/exporters/image/types.d.ts.map +1 -0
  24. package/dist/types/exporters/svg/SVGContentGenerator.d.ts +70 -0
  25. package/dist/types/exporters/svg/SVGContentGenerator.d.ts.map +1 -0
  26. package/dist/types/exporters/svg/SVGDataExtractor.d.ts +34 -0
  27. package/dist/types/exporters/svg/SVGDataExtractor.d.ts.map +1 -0
  28. package/dist/types/exporters/svg/SVGExporter.d.ts +29 -0
  29. package/dist/types/exporters/svg/SVGExporter.d.ts.map +1 -0
  30. package/dist/types/exporters/svg/SVGPathGenerator.d.ts +50 -0
  31. package/dist/types/exporters/svg/SVGPathGenerator.d.ts.map +1 -0
  32. package/dist/types/exporters/svg/index.d.ts +6 -0
  33. package/dist/types/exporters/svg/index.d.ts.map +1 -0
  34. package/dist/types/exporters/svg/types.d.ts +111 -0
  35. package/dist/types/exporters/svg/types.d.ts.map +1 -0
  36. package/dist/types/exporters/txt/TXTExporter.d.ts +36 -0
  37. package/dist/types/exporters/txt/TXTExporter.d.ts.map +1 -0
  38. package/dist/types/exporters/txt/index.d.ts +3 -0
  39. package/dist/types/exporters/txt/index.d.ts.map +1 -0
  40. package/dist/types/exporters/txt/types.d.ts +37 -0
  41. package/dist/types/exporters/txt/types.d.ts.map +1 -0
  42. package/dist/types/exporters/video/VideoExporter.d.ts +30 -0
  43. package/dist/types/exporters/video/VideoExporter.d.ts.map +1 -0
  44. package/dist/types/exporters/video/VideoRecorder.d.ts +28 -0
  45. package/dist/types/exporters/video/VideoRecorder.d.ts.map +1 -0
  46. package/dist/types/exporters/video/index.d.ts +4 -0
  47. package/dist/types/exporters/video/index.d.ts.map +1 -0
  48. package/dist/types/exporters/video/types.d.ts +64 -0
  49. package/dist/types/exporters/video/types.d.ts.map +1 -0
  50. package/dist/types/index.d.ts +35 -0
  51. package/dist/types/index.d.ts.map +1 -0
  52. package/dist/types/overlay/blades/Blade.d.ts +20 -0
  53. package/dist/types/overlay/blades/Blade.d.ts.map +1 -0
  54. package/dist/types/overlay/blades/GIFBlade.d.ts +23 -0
  55. package/dist/types/overlay/blades/GIFBlade.d.ts.map +1 -0
  56. package/dist/types/overlay/blades/ImageBlade.d.ts +14 -0
  57. package/dist/types/overlay/blades/ImageBlade.d.ts.map +1 -0
  58. package/dist/types/overlay/blades/SVGBlade.d.ts +16 -0
  59. package/dist/types/overlay/blades/SVGBlade.d.ts.map +1 -0
  60. package/dist/types/overlay/blades/TextBlade.d.ts +14 -0
  61. package/dist/types/overlay/blades/TextBlade.d.ts.map +1 -0
  62. package/dist/types/overlay/blades/VideoBlade.d.ts +22 -0
  63. package/dist/types/overlay/blades/VideoBlade.d.ts.map +1 -0
  64. package/dist/types/overlay/blades/index.d.ts +7 -0
  65. package/dist/types/overlay/blades/index.d.ts.map +1 -0
  66. package/dist/types/overlay/components/actions/Button.d.ts +19 -0
  67. package/dist/types/overlay/components/actions/Button.d.ts.map +1 -0
  68. package/dist/types/overlay/components/base/Component.d.ts +18 -0
  69. package/dist/types/overlay/components/base/Component.d.ts.map +1 -0
  70. package/dist/types/overlay/components/base/Container.d.ts +9 -0
  71. package/dist/types/overlay/components/base/Container.d.ts.map +1 -0
  72. package/dist/types/overlay/components/base/Field.d.ts +17 -0
  73. package/dist/types/overlay/components/base/Field.d.ts.map +1 -0
  74. package/dist/types/overlay/components/display/Header.d.ts +8 -0
  75. package/dist/types/overlay/components/display/Header.d.ts.map +1 -0
  76. package/dist/types/overlay/components/display/StatusDisplay.d.ts +19 -0
  77. package/dist/types/overlay/components/display/StatusDisplay.d.ts.map +1 -0
  78. package/dist/types/overlay/components/inputs/CheckboxInput.d.ts +20 -0
  79. package/dist/types/overlay/components/inputs/CheckboxInput.d.ts.map +1 -0
  80. package/dist/types/overlay/components/inputs/NumberInput.d.ts +32 -0
  81. package/dist/types/overlay/components/inputs/NumberInput.d.ts.map +1 -0
  82. package/dist/types/overlay/components/inputs/RangeInput.d.ts +35 -0
  83. package/dist/types/overlay/components/inputs/RangeInput.d.ts.map +1 -0
  84. package/dist/types/overlay/components/inputs/SelectInput.d.ts +25 -0
  85. package/dist/types/overlay/components/inputs/SelectInput.d.ts.map +1 -0
  86. package/dist/types/overlay/components/inputs/TextInput.d.ts +19 -0
  87. package/dist/types/overlay/components/inputs/TextInput.d.ts.map +1 -0
  88. package/dist/types/overlay/core/EventBus.d.ts +10 -0
  89. package/dist/types/overlay/core/EventBus.d.ts.map +1 -0
  90. package/dist/types/overlay/core/OverlayController.d.ts +46 -0
  91. package/dist/types/overlay/core/OverlayController.d.ts.map +1 -0
  92. package/dist/types/overlay/core/StateManager.d.ts +11 -0
  93. package/dist/types/overlay/core/StateManager.d.ts.map +1 -0
  94. package/dist/types/overlay/formatRegistry.d.ts +4 -0
  95. package/dist/types/overlay/formatRegistry.d.ts.map +1 -0
  96. package/dist/types/overlay/index.d.ts +13 -0
  97. package/dist/types/overlay/index.d.ts.map +1 -0
  98. package/dist/types/overlay/models/FormatDefinition.d.ts +9 -0
  99. package/dist/types/overlay/models/FormatDefinition.d.ts.map +1 -0
  100. package/dist/types/overlay/models/OverlayEvents.d.ts +26 -0
  101. package/dist/types/overlay/models/OverlayEvents.d.ts.map +1 -0
  102. package/dist/types/overlay/models/OverlayState.d.ts +16 -0
  103. package/dist/types/overlay/models/OverlayState.d.ts.map +1 -0
  104. package/dist/types/overlay/services/ClipboardService.d.ts +8 -0
  105. package/dist/types/overlay/services/ClipboardService.d.ts.map +1 -0
  106. package/dist/types/overlay/services/ExportService.d.ts +18 -0
  107. package/dist/types/overlay/services/ExportService.d.ts.map +1 -0
  108. package/dist/types/overlay/services/PositionService.d.ts +14 -0
  109. package/dist/types/overlay/services/PositionService.d.ts.map +1 -0
  110. package/dist/types/overlay/types.d.ts +14 -0
  111. package/dist/types/overlay/types.d.ts.map +1 -0
  112. package/dist/types/overlay/utils/classes.d.ts +35 -0
  113. package/dist/types/overlay/utils/classes.d.ts.map +1 -0
  114. package/dist/types/types.d.ts +63 -0
  115. package/dist/types/types.d.ts.map +1 -0
  116. package/dist/types/utils/pixels.d.ts +13 -0
  117. package/dist/types/utils/pixels.d.ts.map +1 -0
  118. package/package.json +72 -0
@@ -0,0 +1,2061 @@
1
+ (function(){"use strict";try{if(typeof document<"u"){var e=document.createElement("style");e.appendChild(document.createTextNode("@layer textmode-export-overlay{.textmode-export-overlay{--overlay-bg: rgba(20, 20, 20, .8);--overlay-surface: rgba(40, 40, 40, .9);--overlay-border: rgba(255, 255, 255, .14);--overlay-border-strong: rgba(255, 255, 255, .2);--overlay-border-focus: rgba(160, 160, 160, .6);--overlay-focus-shadow: 0 0 0 1px rgba(140, 140, 140, .28);--overlay-text: #ffffff;--overlay-muted: rgba(200, 200, 200, .74);--overlay-radius: .75rem;--overlay-stack-gap: .55rem;position:absolute;top:0;left:0;display:flex;flex-direction:column;gap:var(--overlay-stack-gap);min-width:236px;max-width:236px;padding:.65rem .8rem;background:var(--overlay-bg);color:var(--overlay-text);border-radius:var(--overlay-radius);border:1px solid var(--overlay-border);box-shadow:0 12px 28px #0000004d;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;font-size:.82rem;line-height:1.35;pointer-events:auto;z-index:2147483647}.textmode-export-overlay *{box-sizing:border-box}.textmode-export-overlay__header{display:flex;flex-direction:column;gap:.4rem}.textmode-export-overlay__header-title-row{display:flex;align-items:center;justify-content:space-between;gap:.4rem}.textmode-export-overlay__header-links{display:inline-flex;align-items:center;gap:.3rem}.textmode-export-overlay__support-link{display:inline-flex;align-items:center;justify-content:center;padding:.2rem;border-radius:.45rem;border:1px solid transparent;color:#d7d7d7d1;background:transparent;text-decoration:none;line-height:0;transition:background .18s ease,color .18s ease,transform .18s ease,border-color .18s ease}.textmode-export-overlay__support-link:hover{color:#fff;background:#5a5a5a4d;border-color:#c8c8c824}.textmode-export-overlay__support-link:focus-visible{outline:none;border-color:var(--overlay-border-focus);box-shadow:var(--overlay-focus-shadow)}.textmode-export-overlay__support-icon,.textmode-export-overlay__link-icon{display:block;width:1rem;height:1rem}.textmode-export-overlay__stack{display:flex;flex-direction:column;gap:var(--overlay-stack-gap)}.textmode-export-overlay__stack--dense{gap:.3rem}.textmode-export-overlay__stack--compact{gap:.45rem}.textmode-export-overlay__section{display:flex;flex-direction:column;gap:.4rem}.textmode-export-overlay__title{font-size:.92rem;font-weight:600}.textmode-export-overlay__divider{width:100%;height:1px;background:#ffffff29;margin-top:.25rem}.textmode-export-overlay__label{font-weight:500;opacity:.9}.textmode-export-overlay__checkbox{display:flex;align-items:center;gap:.5rem}.textmode-export-overlay__checkbox input[type=checkbox]{width:1rem;height:1rem;accent-color:rgba(100,100,100,.9)}.textmode-export-overlay__row{display:flex;gap:.6rem;flex-wrap:wrap;align-items:flex-start;width:100%}.textmode-export-overlay__field{display:flex;flex-direction:column;gap:.3rem;flex:1 1 120px;min-width:120px}.textmode-export-overlay__field--compact{flex:1 1 100px;min-width:100px}.textmode-export-overlay__field--dense{flex:1 1 90px;min-width:90px}.textmode-export-overlay__field--channel{flex:1 1 0;min-width:0}.textmode-export-overlay__field--full{width:100%;flex:1 1 100%}.textmode-export-overlay__input,.textmode-export-overlay select.textmode-export-overlay__input,.textmode-export-overlay input.textmode-export-overlay__input{width:100%;padding:.35rem .5rem;border-radius:.4rem;border:1px solid var(--overlay-border-strong);background:var(--overlay-surface);color:var(--overlay-text);transition:border-color .18s ease,box-shadow .18s ease,background .18s ease}.textmode-export-overlay__input:focus{outline:none;border-color:var(--overlay-border-focus);box-shadow:var(--overlay-focus-shadow);background:#2d2f42eb}.textmode-export-overlay__input::placeholder{color:var(--overlay-muted);opacity:.7}.textmode-export-overlay__muted{font-size:.75rem;opacity:.7;display:block}.textmode-export-overlay__button{display:inline-flex;align-items:center;justify-content:center;gap:.35rem;border-radius:.45rem;border:none;font-weight:600;padding:.45rem .7rem;cursor:pointer;transition:background .2s ease,transform .2s ease,box-shadow .2s ease}.textmode-export-overlay__button--primary{background:#505050;color:#fff}.textmode-export-overlay__button--secondary{background:#606060;color:#fff}.textmode-export-overlay__button--full{width:100%}.textmode-export-overlay__button:hover:not(:disabled){transform:translateY(-1px);box-shadow:0 7px 18px #00000073}.textmode-export-overlay__button--primary:hover:not(:disabled){background:#404040}.textmode-export-overlay__button--secondary:hover:not(:disabled){background:#4a4a4a}.textmode-export-overlay__button:disabled{cursor:default;opacity:.55;transform:none;box-shadow:none}.textmode-export-overlay__status{display:flex;flex-direction:column;gap:.25rem;padding:.45rem .55rem;border-radius:.45rem;border:1px solid rgba(110,110,110,.22);background:#4646462e}.textmode-export-overlay__status--gif{background:#5050501f;border-color:#78787840}.textmode-export-overlay__status--video{background:#4646461f;border-color:#6e6e6e40}.textmode-export-overlay__status-title{font-weight:600;font-size:.75rem;opacity:.85;text-transform:uppercase;letter-spacing:.04em}.textmode-export-overlay__status-value{font-size:.82rem;color:#dedede;transition:color .18s ease}.textmode-export-overlay__status-value--neutral{color:#cfcfcf}.textmode-export-overlay__status-value--active{color:#f5f5f5}.textmode-export-overlay__status-value--alert{color:#b8b8b8}.textmode-export-number-input,.textmode-export-overlay .textmode-export-number-input{display:flex;align-items:stretch;width:100%;background:var(--overlay-surface);border:1px solid var(--overlay-border-strong);border-radius:.4rem;overflow:hidden;transition:border-color .18s ease,box-shadow .18s ease,background .18s ease;position:relative}.textmode-export-number-input:focus-within{border-color:var(--overlay-border-focus);box-shadow:var(--overlay-focus-shadow)}.textmode-export-number-input.is-disabled{opacity:.55}.textmode-export-number-input__field{flex:1 1 auto;min-width:0;border:none;background:transparent;color:var(--overlay-text);font:inherit;padding:.35rem .5rem;appearance:textfield}.textmode-export-number-input__field::-webkit-outer-spin-button,.textmode-export-number-input__field::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}.textmode-export-number-input__field:focus{outline:none}.textmode-export-number-input__controls{display:flex;flex-direction:column;border-left:1px solid rgba(255,255,255,.12);min-width:1.6rem}.textmode-export-number-input__control{display:flex;align-items:center;justify-content:center;width:100%;flex:1;border:none;background:#32323299;color:#d0d0d0;font-size:.68rem;line-height:1;cursor:pointer;transition:background .15s ease,color .15s ease;padding:0}.textmode-export-number-input__control:hover:not(:disabled){background:#5a5a5ad9;color:#fff}.textmode-export-number-input__control:disabled{cursor:default;opacity:.4;background:#28282880;color:#c8c8c866}.textmode-export-number-input:focus-within .textmode-export-number-input__control{background:#3c3c3cbf;color:#e8e8e8}.textmode-export-number-input__display{position:absolute;inset:0;display:flex;align-items:center;padding:.4rem .55rem;pointer-events:none;color:#f8fafc;opacity:0;transition:opacity .14s ease}.textmode-export-number-input__display--visible{opacity:1}.textmode-export-range-input{position:relative;width:100%}.textmode-export-range-input.is-disabled{opacity:.55}.textmode-export-range-input__field{width:100%;height:1.4rem;padding:0;margin:0;background:transparent;cursor:pointer;appearance:none;-webkit-appearance:none}.textmode-export-range-input__field::-webkit-slider-thumb{width:12px;height:12px;margin-top:-3.5px;-webkit-appearance:none;border-radius:50%;background:#a0a0a0;border:2px solid #303030;box-shadow:0 0 0 2px #00000059}.textmode-export-range-input__field::-webkit-slider-runnable-track{height:5px;border-radius:999px;background:linear-gradient(90deg,#8c8c8cd9,#646464a6)}.textmode-export-range-input__field::-moz-range-thumb{width:12px;height:12px;border-radius:50%;background:#a0a0a0;border:2px solid #303030;margin-top:-3.5px}.textmode-export-range-input__field::-moz-range-track{height:5px;border-radius:999px;background:linear-gradient(90deg,#8c8c8cd9,#646464a6)}.textmode-export-range-input__tooltip{position:absolute;top:-1.5rem;left:0;transform:translate(-50%);padding:.2rem .35rem;border-radius:.35rem;background:#1e1e1ee0;color:#f8fafc;font-size:.68rem;line-height:1;white-space:nowrap;pointer-events:none;opacity:0;transition:opacity .15s ease}.textmode-export-range-input__tooltip--visible{opacity:1}}")),document.head.appendChild(e)}}catch(o){console.error("vite-plugin-css-injected-by-js",o)}})();
2
+ class At {
3
+ $extractFramebufferData(t) {
4
+ const e = t.readPixels(0), r = t.readPixels(1), a = t.readPixels(2), i = t.readPixels(3);
5
+ return { characterPixels: e, primaryColorPixels: r, secondaryColorPixels: a, transformPixels: t.readPixels(4), rotationPixels: i };
6
+ }
7
+ $getCharacterIndex(t, e) {
8
+ return t[e] + (t[e + 1] << 8);
9
+ }
10
+ }
11
+ class Q {
12
+ $downloadFile(t, e) {
13
+ try {
14
+ const r = this._sanitizeFilename(e), a = URL.createObjectURL(t), i = document.createElement("a");
15
+ i.href = a, i.download = r, i.style.display = "none", i.rel = "noopener", document.body.appendChild(i), i.click(), document.body.removeChild(i), URL.revokeObjectURL(a);
16
+ } catch (r) {
17
+ console.error("[textmode-export] Failed to download file:", r);
18
+ }
19
+ }
20
+ _sanitizeFilename(t) {
21
+ if (!t) return this._generateDefaultFilename();
22
+ const e = t.trim();
23
+ return e ? e.replace(/[<>:"/\\|?*]/g, "_").replace(/\s+/g, "_").replace(/_{2,}/g, "_").replace(/^_+|_+$/g, "").substring(0, 255) || this._generateDefaultFilename() : this._generateDefaultFilename();
24
+ }
25
+ _generateDefaultFilename() {
26
+ return `textmode-export-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace(/:/g, "-")}`;
27
+ }
28
+ }
29
+ function ft(o, t) {
30
+ return { r: o[t], g: o[t + 1], b: o[t + 2], a: o[t + 3] };
31
+ }
32
+ class qt extends At {
33
+ _extractTransformData(t, e, r) {
34
+ const a = t[r] === 255, i = t[r + 1] === 255, n = t[r + 2] === 255, c = e[r] + e[r + 1] / 255;
35
+ return { isInverted: a, flipHorizontal: i, flipVertical: n, rotation: Math.round(360 * c / 255 * 100) / 100 };
36
+ }
37
+ _calculateCellPosition(t, e, r) {
38
+ return { x: t, y: e, cellX: t * r.cellWidth, cellY: e * r.cellHeight };
39
+ }
40
+ $extractSVGCellData(t, e) {
41
+ const r = [];
42
+ let a = 0;
43
+ for (let i = 0; i < e.rows; i++) for (let n = 0; n < e.cols; n++) {
44
+ const c = 4 * a, l = this.$getCharacterIndex(t.characterPixels, c);
45
+ let h = ft(t.primaryColorPixels, c), s = ft(t.secondaryColorPixels, c);
46
+ const d = this._extractTransformData(t.transformPixels, t.rotationPixels, c);
47
+ if (d.isInverted) {
48
+ const m = h;
49
+ h = s, s = m;
50
+ }
51
+ const u = this._calculateCellPosition(n, i, e);
52
+ r.push({ charIndex: l, primaryColor: h, secondaryColor: s, transform: d, position: u }), a++;
53
+ }
54
+ return r;
55
+ }
56
+ }
57
+ class zt {
58
+ _createGlyphPath(t, e, r, a, i) {
59
+ const n = i / t.head.unitsPerEm;
60
+ return { getBoundingBox: () => ({ x1: r + e.xMin * n, y1: a + -e.yMax * n, x2: r + e.xMax * n, y2: a + -e.yMin * n }), toSVG: () => this._glyphToSVGPath(e, r, a, n) };
61
+ }
62
+ _glyphToSVGPath(t, e, r, a) {
63
+ if (!t || !t.xs) return "";
64
+ const { xs: i, ys: n, endPts: c, flags: l } = t;
65
+ if (!(i && n && c && l)) return "";
66
+ let h = "", s = 0;
67
+ for (let d = 0; d < c.length; d++) {
68
+ const u = c[d];
69
+ if (!(u < s)) {
70
+ if (u >= s) {
71
+ const m = e + i[s] * a, y = r - n[s] * a;
72
+ h += `M${m.toFixed(2)},${y.toFixed(2)}`;
73
+ let p = s + 1;
74
+ for (; p <= u; )
75
+ if (1 & l[p]) {
76
+ const f = e + i[p] * a, _ = r - n[p] * a;
77
+ h += `L${f.toFixed(2)},${_.toFixed(2)}`, p++;
78
+ } else {
79
+ const f = e + i[p] * a, _ = r - n[p] * a;
80
+ let b = p + 1 > u ? s : p + 1;
81
+ if (1 & l[b]) {
82
+ const g = e + i[b] * a, C = r - n[b] * a;
83
+ h += `Q${f.toFixed(2)},${_.toFixed(2)} ${g.toFixed(2)},${C.toFixed(2)}`, p = b + 1;
84
+ } else {
85
+ const g = (f + (e + i[b] * a)) / 2, C = (_ + (r - n[b] * a)) / 2;
86
+ h += `Q${f.toFixed(2)},${_.toFixed(2)} ${g.toFixed(2)},${C.toFixed(2)}`, p = b;
87
+ }
88
+ }
89
+ h += "Z";
90
+ }
91
+ s = u + 1;
92
+ }
93
+ }
94
+ return h;
95
+ }
96
+ _generateCharacterPath(t, e, r, a, i) {
97
+ const n = t.codePointAt(0) ?? 0, c = e.getGlyphData(n);
98
+ return c ? this._createGlyphPath(e.font, c, r, a, i) : null;
99
+ }
100
+ $generatePositionedCharacterPath(t, e, r, a, i, n, c, l) {
101
+ const h = r + (i - l * (c / e.font.head.unitsPerEm)) / 2, s = a + (n + 0.7 * c) / 2, d = this._generateCharacterPath(t, e, h, s, c);
102
+ return d && d.toSVG() || null;
103
+ }
104
+ }
105
+ class jt {
106
+ _pathGenerator;
107
+ constructor() {
108
+ this._pathGenerator = new zt();
109
+ }
110
+ $generateSVGHeader(t) {
111
+ const { width: e, height: r } = t;
112
+ return `<?xml version="1.0" encoding="UTF-8"?><svg width="${e}" height="${r}" viewBox="0 0 ${e} ${r}" xmlns="http://www.w3.org/2000/svg"><title>textmode.js sketch</title>`;
113
+ }
114
+ $generateSVGFooter() {
115
+ return "</g></svg>";
116
+ }
117
+ _generateTransformAttribute(t, e) {
118
+ const { transform: r, position: a } = t;
119
+ if (!r.flipHorizontal && !r.flipVertical && !r.rotation) return "";
120
+ const i = a.cellX + e.cellWidth / 2, n = a.cellY + e.cellHeight / 2, c = [];
121
+ if (r.flipHorizontal || r.flipVertical) {
122
+ const l = r.flipHorizontal ? -1 : 1, h = r.flipVertical ? -1 : 1;
123
+ c.push(`translate(${i} ${n})scale(${l} ${h})translate(${-i} ${-n})`);
124
+ }
125
+ return r.rotation && c.push(`rotate(${r.rotation} ${i} ${n})`), ` transform="${c.join(" ")}"`;
126
+ }
127
+ _generateCellBackground(t, e, r) {
128
+ if (!r.includeBackgroundRectangles || t.secondaryColor.a === 0) return "";
129
+ const { position: a } = t, { r: i, g: n, b: c, a: l } = t.secondaryColor, h = `rgba(${i},${n},${c},${l / 255})`;
130
+ return r.drawMode === "stroke" ? `<rect x="${a.cellX}" y="${a.cellY}" width="${e.cellWidth}" height="${e.cellHeight}" stroke="${h}" fill="none" stroke-width="${r.strokeWidth}"/>` : `<rect x="${a.cellX}" y="${a.cellY}" width="${e.cellWidth}" height="${e.cellHeight}" fill="${h}"/>`;
131
+ }
132
+ _generateCharacterPath(t, e, r, a) {
133
+ const i = r.characters[t.charIndex];
134
+ if (!i) return "";
135
+ const n = this._pathGenerator.$generatePositionedCharacterPath(i.character, r, t.position.cellX, t.position.cellY, e.cellWidth, e.cellHeight, r.fontSize, i.advanceWidth);
136
+ if (!n) return "";
137
+ const { r: c, g: l, b: h, a: s } = t.primaryColor, d = `rgba(${c},${l},${h},${s / 255})`;
138
+ return a.drawMode === "stroke" ? `<path d="${n}" stroke="${d}" stroke-width="${a.strokeWidth}" fill="none"/>` : `<path d="${n}" fill="${d}"/>`;
139
+ }
140
+ $generateCellContent(t, e, r, a) {
141
+ const i = [], n = this._generateCellBackground(t, e, a);
142
+ n && i.push(n);
143
+ const c = this._generateCharacterPath(t, e, r, a);
144
+ if (c) {
145
+ const l = this._generateTransformAttribute(t, e);
146
+ i.push(l ? `<g${l}>${c}</g>` : c);
147
+ }
148
+ return i.join("");
149
+ }
150
+ $generateSVGContent(t, e, r, a) {
151
+ const i = [this.$generateSVGHeader(e), '<g id="ascii-cells">'];
152
+ for (const n of t) i.push(this.$generateCellContent(n, e, r, a));
153
+ return i.push(this.$generateSVGFooter()), i.join("");
154
+ }
155
+ $optimizeSVGContent(t) {
156
+ return t.replace(/<path[^>]*d=""[^>]*\/>/g, "").replace(/\s+/g, " ").replace(/> </g, "><");
157
+ }
158
+ }
159
+ class gt {
160
+ _applyDefaultOptions(t) {
161
+ return { includeBackgroundRectangles: t.includeBackgroundRectangles ?? !0, drawMode: t.drawMode ?? "fill", strokeWidth: t.strokeWidth ?? 1, filename: t.filename };
162
+ }
163
+ $generateSVG(t, e = {}) {
164
+ const r = new qt(), a = new jt(), i = r.$extractSVGCellData(r.$extractFramebufferData(t.drawFramebuffer), t.grid), n = a.$generateSVGContent(i, t.grid, t.font, this._applyDefaultOptions(e));
165
+ return a.$optimizeSVGContent(n);
166
+ }
167
+ $saveSVG(t, e = {}) {
168
+ new Q().$downloadFile(new Blob([this.$generateSVG(t, e)], { type: "image/svg+xml;charset=utf-8" }), e.filename);
169
+ }
170
+ }
171
+ const ot = { png: "image/png", jpg: "image/jpeg", webp: "image/webp" }, lt = { png: ".png", jpg: ".jpg", webp: ".webp" };
172
+ class vt {
173
+ _applyDefaultOptions(t) {
174
+ return { format: t.format ?? "png", scale: Math.abs(t.scale ?? 1), filename: t.filename };
175
+ }
176
+ _validateOptions(t) {
177
+ if (!(t.format in ot)) throw new Error(`Saving '${t.format}' files is not supported`);
178
+ }
179
+ async $generateImageBlob(t, e) {
180
+ const r = t, a = document.createElement("canvas"), i = a.getContext("2d"), n = Math.round(r.width * e.scale), c = Math.round(r.height * e.scale);
181
+ return a.width = n, a.height = c, i.imageSmoothingEnabled = !1, i.clearRect(0, 0, n, c), i.drawImage(r, 0, 0, r.width, r.height, 0, 0, n, c), await new Promise((l, h) => {
182
+ a.toBlob((s) => {
183
+ s ? l(s) : h(new Error(`Failed to generate ${e.format.toUpperCase()} blob`));
184
+ }, ot[e.format]);
185
+ });
186
+ }
187
+ async $saveImage(t, e = {}) {
188
+ const r = this._applyDefaultOptions(e);
189
+ this._validateOptions(r);
190
+ const a = await this.$generateImageBlob(t, r);
191
+ new Q().$downloadFile(a, r.filename);
192
+ }
193
+ async $copyImageToClipboard(t, e = {}) {
194
+ if (typeof navigator > "u" || !navigator.clipboard || typeof navigator.clipboard.write != "function") throw new Error("Clipboard API is not available in this environment");
195
+ const r = this._applyDefaultOptions(e);
196
+ this._validateOptions(r);
197
+ const a = await this.$generateImageBlob(t, r), i = new ClipboardItem({ [ot[r.format]]: a });
198
+ await navigator.clipboard.write([i]);
199
+ }
200
+ }
201
+ class bt {
202
+ _applyDefaultOptions(t) {
203
+ return { preserveTrailingSpaces: t.preserveTrailingSpaces ?? !1, emptyCharacter: t.emptyCharacter ?? " ", filename: t.filename };
204
+ }
205
+ _createTXTContent(t, e) {
206
+ const r = new At();
207
+ let a = r.$extractFramebufferData(t.drawFramebuffer);
208
+ const i = [];
209
+ let n = 0;
210
+ for (let l = 0; l < t.grid.rows; l++) {
211
+ const h = [];
212
+ for (let s = 0; s < t.grid.cols; s++) {
213
+ const d = 4 * n, u = r.$getCharacterIndex(a.characterPixels, d), m = t.font.characters[u]?.character || e.emptyCharacter;
214
+ h.push(m), n++;
215
+ }
216
+ i.push(h);
217
+ }
218
+ const c = [];
219
+ for (const l of i) {
220
+ let h = l.join("");
221
+ e.preserveTrailingSpaces || (h = h.replace(/\s+$/, "")), c.push(h);
222
+ }
223
+ return c.join(`
224
+ `);
225
+ }
226
+ $generateTXT(t, e = {}) {
227
+ return this._createTXTContent(t, this._applyDefaultOptions(e));
228
+ }
229
+ $saveTXT(t, e = {}) {
230
+ const r = this._applyDefaultOptions(e), a = this._createTXTContent(t, r), i = new Blob([a], { type: "text/plain;charset=utf-8" });
231
+ new Q().$downloadFile(i, r.filename);
232
+ }
233
+ }
234
+ class Ht {
235
+ _canvas;
236
+ _ctx;
237
+ async $record(t, e, r, a) {
238
+ const i = Math.max(1, Math.round(e.frameRate)), n = Math.round(1e3 / i), c = Math.max(1, Math.round(e.frameCount));
239
+ return await new Promise((l, h) => {
240
+ const s = [];
241
+ let d, u = 0, m = !1;
242
+ const y = () => {
243
+ d && (d(), d = void 0);
244
+ }, p = (_) => {
245
+ m || (m = !0, y(), l(_));
246
+ }, f = () => {
247
+ if (u >= c) p(s);
248
+ else try {
249
+ const _ = e.scale, b = Math.max(1, Math.round(t.width * _)), g = Math.max(1, Math.round(t.height * _));
250
+ this._canvas || (this._canvas = document.createElement("canvas")), this._canvas.width === b && this._canvas.height === g || (this._canvas.width = b, this._canvas.height = g), this._ctx || (this._ctx = this._canvas.getContext("2d"));
251
+ const C = this._ctx;
252
+ C.imageSmoothingEnabled = !1, C.clearRect(0, 0, b, g), C.drawImage(t, 0, 0, t.width, t.height, 0, 0, b, g);
253
+ const S = { imageData: C.getImageData(0, 0, b, g), width: b, height: g, delayMs: n };
254
+ s.push(S), u += 1, a?.({ state: "recording", frameIndex: u, totalFrames: c }), u >= c && p(s);
255
+ } catch (_) {
256
+ ((b) => {
257
+ if (m) return;
258
+ m = !0, y();
259
+ const g = b instanceof Error ? b.message : "GIF recording failed";
260
+ a?.({ state: "error", message: g }), h(b instanceof Error ? b : new Error(String(b)));
261
+ })(_);
262
+ }
263
+ };
264
+ d = r(() => {
265
+ f();
266
+ }), a?.({ state: "recording", frameIndex: 0, totalFrames: c });
267
+ });
268
+ }
269
+ }
270
+ var Pt = Object.defineProperty, Qt = (o) => Pt(o, "__esModule", { value: !0 }), Xt = (o, t) => () => (t || o((t = { exports: {} }).exports, t), t.exports), Yt = (o, t) => {
271
+ for (var e in t) Pt(o, e, { get: t[e], enumerable: !0 });
272
+ }, Jt = Xt((o) => {
273
+ function t(d, u, m) {
274
+ return 0.29889531 * d + 0.58662247 * u + 0.11448223 * m;
275
+ }
276
+ function e(d, u, m) {
277
+ return 0.59597799 * d - 0.2741761 * u - 0.32180189 * m;
278
+ }
279
+ function r(d, u, m) {
280
+ return 0.21147017 * d - 0.52261711 * u + 0.31114694 * m;
281
+ }
282
+ function a(d, u) {
283
+ let m = d[0] - u[0], y = d[1] - u[1], p = d[2] - u[2], f = i(d) - i(u);
284
+ return m * m * 0.5053 + y * y * 0.299 + p * p * 0.1957 + f * f;
285
+ }
286
+ function i(d) {
287
+ return d[3] != null ? d[3] : 255;
288
+ }
289
+ function n(d, u) {
290
+ return Math.sqrt(a(d, u));
291
+ }
292
+ function c(d, u) {
293
+ let [m, y, p] = d, [f, _, b] = u, g = t(m, y, p) - t(f, _, b), C = e(m, y, p) - e(f, _, b), S = r(m, y, p) - r(f, _, b), E = i(d) - i(u);
294
+ return g * g * 0.5053 + C * C * 0.299 + S * S * 0.1957 + E * E;
295
+ }
296
+ function l(d, u) {
297
+ return Math.sqrt(c(d, u));
298
+ }
299
+ function h(d, u) {
300
+ var m, y = 0;
301
+ for (m = 0; m < d.length; m++) {
302
+ let p = d[m] - u[m];
303
+ y += p * p;
304
+ }
305
+ return y;
306
+ }
307
+ function s(d, u) {
308
+ return Math.sqrt(h(d, u));
309
+ }
310
+ Qt(o), Yt(o, { colorDifferenceRGBToYIQ: () => l, colorDifferenceRGBToYIQSquared: () => c, colorDifferenceYIQ: () => n, colorDifferenceYIQSquared: () => a, euclideanDistance: () => s, euclideanDistanceSquared: () => h });
311
+ }), Kt = { trailer: 59 };
312
+ function Dt(o = 256) {
313
+ let t = 0, e = new Uint8Array(o);
314
+ return { get buffer() {
315
+ return e.buffer;
316
+ }, reset() {
317
+ t = 0;
318
+ }, bytesView: () => e.subarray(0, t), bytes: () => e.slice(0, t), writeByte(a) {
319
+ r(t + 1), e[t] = a, t++;
320
+ }, writeBytes(a, i = 0, n = a.length) {
321
+ r(t + n);
322
+ for (let c = 0; c < n; c++) e[t++] = a[c + i];
323
+ }, writeBytesView(a, i = 0, n = a.byteLength) {
324
+ r(t + n), e.set(a.subarray(i, i + n), t), t += n;
325
+ } };
326
+ function r(a) {
327
+ var i = e.length;
328
+ if (i >= a) return;
329
+ a = Math.max(a, i * (i < 1048576 ? 2 : 1.125) >>> 0), i != 0 && (a = Math.max(a, 256));
330
+ let n = e;
331
+ e = new Uint8Array(a), t > 0 && e.set(n.subarray(0, t), 0);
332
+ }
333
+ }
334
+ var Z = 12, yt = 5003, Zt = [0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767, 65535];
335
+ function te(o, t, e, r, a, i, n, c) {
336
+ a = a || Dt(512), i = i || new Uint8Array(256), n = n || new Int32Array(yt), c = c || new Int32Array(yt);
337
+ let l = n.length, h = Math.max(2, r);
338
+ i.fill(0), c.fill(0), L();
339
+ let s = 0, d = 0, u = h + 1, m = u, y = !1, p = m, f = D(p), _ = 1 << u - 1, b = _ + 1, g = _ + 2, C = 0, S = e[0], E = 0;
340
+ for (let B = l; B < 65536; B *= 2) ++E;
341
+ E = 8 - E, a.writeByte(h), T(_);
342
+ for (let B = 1; B < e.length; B++) V(e[B]);
343
+ return T(S), T(b), a.writeByte(0), a.bytesView();
344
+ function A(B) {
345
+ i[C++] = B, C >= 254 && I();
346
+ }
347
+ function L() {
348
+ n.fill(-1);
349
+ }
350
+ function V(B) {
351
+ let R = (B << Z) + S, w = B << E ^ S;
352
+ if (n[w] === R) S = c[w];
353
+ else {
354
+ if (n[w] >= 0) {
355
+ let x = w === 0 ? 1 : l - w;
356
+ do
357
+ if (w -= x, w < 0 && (w += l), n[w] === R) return void (S = c[w]);
358
+ while (n[w] >= 0);
359
+ }
360
+ T(S), S = B, g < 1 << Z ? (c[w] = g++, n[w] = R) : (L(), g = _ + 2, y = !0, T(_));
361
+ }
362
+ }
363
+ function I() {
364
+ C > 0 && (a.writeByte(C), a.writeBytesView(i, 0, C), C = 0);
365
+ }
366
+ function D(B) {
367
+ return (1 << B) - 1;
368
+ }
369
+ function T(B) {
370
+ for (s &= Zt[d], d > 0 ? s |= B << d : s = B, d += p; d >= 8; ) A(255 & s), s >>= 8, d -= 8;
371
+ if ((g > f || y) && (y ? (p = m, f = D(p), y = !1) : (++p, f = p == Z ? 1 << Z : D(p))), B == b) {
372
+ for (; d > 0; ) A(255 & s), s >>= 8, d -= 8;
373
+ I();
374
+ }
375
+ }
376
+ }
377
+ var ee = te;
378
+ function Vt(o, t, e) {
379
+ return o << 8 & 63488 | t << 2 & 992 | e >> 3 & 31;
380
+ }
381
+ function Ot(o, t, e, r) {
382
+ return o >> 4 | 240 & t | (240 & e) << 4 | (240 & r) << 8;
383
+ }
384
+ function Tt(o, t, e) {
385
+ return o >> 4 << 8 | t >> 4 << 4 | e >> 4;
386
+ }
387
+ function tt(o, t, e) {
388
+ return o < t ? t : o > e ? e : o;
389
+ }
390
+ function at(o) {
391
+ return o * o;
392
+ }
393
+ function wt(o, t, e) {
394
+ for (var r = 0, a = 1e100, i = o[t], n = i.cnt, c = (i.ac, i.rc), l = i.gc, h = i.bc, s = i.fw; s != 0; s = o[s].fw) {
395
+ var d = o[s].cnt, u = n * d / (n + d);
396
+ if (!(u >= a)) {
397
+ var m = 0;
398
+ !((m += u * at(o[s].rc - c)) >= a) && !((m += u * at(o[s].gc - l)) >= a) && !((m += u * at(o[s].bc - h)) >= a) && (a = m, r = s);
399
+ }
400
+ }
401
+ i.err = a, i.nn = r;
402
+ }
403
+ function dt() {
404
+ return { ac: 0, rc: 0, gc: 0, bc: 0, cnt: 0, nn: 0, fw: 0, bk: 0, tm: 0, mtm: 0, err: 0 };
405
+ }
406
+ function re(o, t) {
407
+ let e = new Array(t === "rgb444" ? 4096 : 65536), r = o.length;
408
+ if (t === "rgba4444") for (let a = 0; a < r; ++a) {
409
+ let i = o[a], n = i >> 24 & 255, c = i >> 16 & 255, l = i >> 8 & 255, h = 255 & i, s = Ot(h, l, c, n), d = s in e ? e[s] : e[s] = dt();
410
+ d.rc += h, d.gc += l, d.bc += c, d.ac += n, d.cnt++;
411
+ }
412
+ else if (t === "rgb444") for (let a = 0; a < r; ++a) {
413
+ let i = o[a], n = i >> 16 & 255, c = i >> 8 & 255, l = 255 & i, h = Tt(l, c, n), s = h in e ? e[h] : e[h] = dt();
414
+ s.rc += l, s.gc += c, s.bc += n, s.cnt++;
415
+ }
416
+ else for (let a = 0; a < r; ++a) {
417
+ let i = o[a], n = i >> 16 & 255, c = i >> 8 & 255, l = 255 & i, h = Vt(l, c, n), s = h in e ? e[h] : e[h] = dt();
418
+ s.rc += l, s.gc += c, s.bc += n, s.cnt++;
419
+ }
420
+ return e;
421
+ }
422
+ function ae(o, t, e) {
423
+ let { format: r = "rgb565", clearAlpha: a = !0, clearAlphaColor: i = 0, clearAlphaThreshold: n = 0, oneBitAlpha: c = !1 } = e || {}, l = e.useSqrt !== !1, h = r === "rgba4444", s = re(o, r), d = s.length, u = d - 1, m = new Uint32Array(d + 1);
424
+ for (var y = 0, p = 0; p < s.length; ++p) {
425
+ let B = s[p];
426
+ if (B != null) {
427
+ var f = 1 / B.cnt;
428
+ h && (B.ac *= f), B.rc *= f, B.gc *= f, B.bc *= f, s[y++] = B;
429
+ }
430
+ }
431
+ for (at(t) / y < 0.022 && (l = !1), p = 0; p < y - 1; ++p) s[p].fw = p + 1, s[p + 1].bk = p, l && (s[p].cnt = Math.sqrt(s[p].cnt));
432
+ var _, b, g;
433
+ for (l && (s[p].cnt = Math.sqrt(s[p].cnt)), p = 0; p < y; ++p) {
434
+ wt(s, p);
435
+ var C = s[p].err;
436
+ for (b = ++m[0]; b > 1 && !(s[_ = m[g = b >> 1]].err <= C); b = g) m[b] = _;
437
+ m[b] = p;
438
+ }
439
+ var S = y - t;
440
+ for (p = 0; p < S; ) {
441
+ for (var E; ; ) {
442
+ var A = m[1];
443
+ if ((E = s[A]).tm >= E.mtm && s[E.nn].mtm <= E.tm) break;
444
+ for (E.mtm == u ? A = m[1] = m[m[0]--] : (wt(s, A), E.tm = p), C = s[A].err, b = 1; (g = b + b) <= m[0] && (g < m[0] && s[m[g]].err > s[m[g + 1]].err && g++, !(C <= s[_ = m[g]].err)); b = g) m[b] = _;
445
+ m[b] = A;
446
+ }
447
+ var L = s[E.nn], V = E.cnt, I = L.cnt;
448
+ f = 1 / (V + I), h && (E.ac = f * (V * E.ac + I * L.ac)), E.rc = f * (V * E.rc + I * L.rc), E.gc = f * (V * E.gc + I * L.gc), E.bc = f * (V * E.bc + I * L.bc), E.cnt += L.cnt, E.mtm = ++p, s[L.bk].fw = L.fw, s[L.fw].bk = L.bk, L.mtm = u;
449
+ }
450
+ let D = [];
451
+ var T = 0;
452
+ for (p = 0; ; ++T) {
453
+ let B = tt(Math.round(s[p].rc), 0, 255), R = tt(Math.round(s[p].gc), 0, 255), w = tt(Math.round(s[p].bc), 0, 255), x = 255;
454
+ h && (x = tt(Math.round(s[p].ac), 0, 255), c && (x = x <= (typeof c == "number" ? c : 127) ? 0 : 255), a && x <= n && (B = R = w = i, x = 0));
455
+ let k = h ? [B, R, w, x] : [B, R, w];
456
+ if (ie(D, k) || D.push(k), (p = s[p].fw) == 0) break;
457
+ }
458
+ return D;
459
+ }
460
+ function ie(o, t) {
461
+ for (let e = 0; e < o.length; e++) {
462
+ let r = o[e], a = r[0] === t[0] && r[1] === t[1] && r[2] === t[2], i = !(r.length >= 4 && t.length >= 4) || r[3] === t[3];
463
+ if (a && i) return !0;
464
+ }
465
+ return !1;
466
+ }
467
+ function se(o, t, e) {
468
+ let r = (e = e || "rgb565") === "rgb444" ? 4096 : 65536, a = new Uint8Array(o.length), i = new Array(r);
469
+ if (e === "rgba4444") for (let n = 0; n < o.length; n++) {
470
+ let c, l = o[n], h = l >> 24 & 255, s = l >> 16 & 255, d = l >> 8 & 255, u = 255 & l, m = Ot(u, d, s, h);
471
+ i[m] != null ? c = i[m] : (c = ne(u, d, s, h, t), i[m] = c), a[n] = c;
472
+ }
473
+ else for (let n = 0; n < o.length; n++) {
474
+ let c, l = o[n], h = l >> 16 & 255, s = l >> 8 & 255, d = 255 & l, u = e === "rgb444" ? Tt(d, s, h) : Vt(d, s, h);
475
+ i[u] != null ? c = i[u] : (c = oe(d, s, h, t), i[u] = c), a[n] = c;
476
+ }
477
+ return a;
478
+ }
479
+ function ne(o, t, e, r, a) {
480
+ let i = 0, n = 1e100;
481
+ for (let c = 0; c < a.length; c++) {
482
+ let l = a[c], h = l[0], s = l[1], d = l[2], u = q(l[3] - r);
483
+ u > n || (u += q(h - o), !(u > n) && (u += q(s - t), !(u > n) && (u += q(d - e), !(u > n) && (n = u, i = c))));
484
+ }
485
+ return i;
486
+ }
487
+ function oe(o, t, e, r) {
488
+ let a = 0, i = 1e100;
489
+ for (let n = 0; n < r.length; n++) {
490
+ let c = r[n], l = c[0], h = c[1], s = c[2], d = q(l - o);
491
+ d > i || (d += q(h - t), !(d > i) && (d += q(s - e), !(d > i) && (i = d, a = n)));
492
+ }
493
+ return a;
494
+ }
495
+ function q(o) {
496
+ return o * o;
497
+ }
498
+ function le(o = {}) {
499
+ let { initialCapacity: t = 4096, auto: e = !0 } = o, r = Dt(t), a = new Uint8Array(256), i = new Int32Array(5003), n = new Int32Array(5003), c = !1;
500
+ return { reset() {
501
+ r.reset(), c = !1;
502
+ }, finish() {
503
+ r.writeByte(Kt.trailer);
504
+ }, bytes: () => r.bytes(), bytesView: () => r.bytesView(), get buffer() {
505
+ return r.buffer;
506
+ }, get stream() {
507
+ return r;
508
+ }, writeHeader: l, writeFrame(h, s, d, u = {}) {
509
+ let { transparent: m = !1, transparentIndex: y = 0, delay: p = 0, palette: f = null, repeat: _ = 0, colorDepth: b = 8, dispose: g = -1 } = u, C = !1;
510
+ if (e ? c || (C = !0, l(), c = !0) : C = !!u.first, s = Math.max(0, Math.floor(s)), d = Math.max(0, Math.floor(d)), C) {
511
+ if (!f) throw new Error("First frame must include a { palette } option");
512
+ ce(r, s, d, f, b), _t(r, f), _ >= 0 && ue(r, _);
513
+ }
514
+ let S = Math.round(p / 10);
515
+ de(r, g, S, m, y);
516
+ let E = !!f && !C;
517
+ he(r, s, d, E ? f : null), E && _t(r, f), pe(r, h, s, d, b, a, i, n);
518
+ } };
519
+ function l() {
520
+ Rt(r, "GIF89a");
521
+ }
522
+ }
523
+ function de(o, t, e, r, a) {
524
+ var i, n;
525
+ o.writeByte(33), o.writeByte(249), o.writeByte(4), a < 0 && (a = 0, r = !1), r ? (i = 1, n = 2) : (i = 0, n = 0), t >= 0 && (n = 7 & t), n <<= 2, o.writeByte(0 | n | i), N(o, e), o.writeByte(a || 0), o.writeByte(0);
526
+ }
527
+ function ce(o, t, e, r, a = 8) {
528
+ let i = 128 | a - 1 << 4 | pt(r.length) - 1;
529
+ N(o, t), N(o, e), o.writeBytes([i, 0, 0]);
530
+ }
531
+ function ue(o, t) {
532
+ o.writeByte(33), o.writeByte(255), o.writeByte(11), Rt(o, "NETSCAPE2.0"), o.writeByte(3), o.writeByte(1), N(o, t), o.writeByte(0);
533
+ }
534
+ function _t(o, t) {
535
+ let e = 1 << pt(t.length);
536
+ for (let r = 0; r < e; r++) {
537
+ let a = [0, 0, 0];
538
+ r < t.length && (a = t[r]), o.writeByte(a[0]), o.writeByte(a[1]), o.writeByte(a[2]);
539
+ }
540
+ }
541
+ function he(o, t, e, r) {
542
+ if (o.writeByte(44), N(o, 0), N(o, 0), N(o, t), N(o, e), r) {
543
+ let a = 0, i = 0, n = pt(r.length) - 1;
544
+ o.writeByte(128 | a | i | n);
545
+ } else o.writeByte(0);
546
+ }
547
+ function pe(o, t, e, r, a = 8, i, n, c) {
548
+ ee(e, r, t, a, o, i, n, c);
549
+ }
550
+ function N(o, t) {
551
+ o.writeByte(255 & t), o.writeByte(t >> 8 & 255);
552
+ }
553
+ function Rt(o, t) {
554
+ for (var e = 0; e < t.length; e++) o.writeByte(t.charCodeAt(e));
555
+ }
556
+ function pt(o) {
557
+ return Math.max(Math.ceil(Math.log2(o)), 1);
558
+ }
559
+ Jt();
560
+ class me {
561
+ _recorder;
562
+ _textmodifier;
563
+ _registerPostDrawHook;
564
+ constructor(t, e) {
565
+ this._recorder = new Ht(), this._textmodifier = t, this._registerPostDrawHook = e;
566
+ }
567
+ async $saveGIF(t = {}) {
568
+ const e = this._textmodifier.canvas, r = this._applyDefaultOptions(t), a = t.onProgress;
569
+ try {
570
+ const i = await this._recorder.$record(e, r, this._registerPostDrawHook, a), n = le(), { repeat: c } = t;
571
+ i.forEach((s, d) => {
572
+ const { width: u, height: m, imageData: y, delayMs: p } = s, f = new Uint32Array(y.data.buffer.slice(0)), _ = ae(f, 256, {}), b = se(f, _);
573
+ n.writeFrame(b, u, m, { palette: _, delay: p, repeat: d === 0 ? c : -1 });
574
+ }), n.finish();
575
+ const l = n.bytes(), h = l.buffer.slice(l.byteOffset, l.byteOffset + l.byteLength);
576
+ new Q().$downloadFile(new Blob([h], { type: "image/gif" }), r.filename), a?.({ state: "completed", totalFrames: r.frameCount });
577
+ } catch (i) {
578
+ throw a?.({ state: "error", message: i instanceof Error ? i.message : "GIF export failed" }), i;
579
+ }
580
+ }
581
+ _applyDefaultOptions(t) {
582
+ const e = Math.abs(Math.round(t.frameCount ?? 300)), r = Math.abs(t.frameRate ?? 60), a = Math.abs(t.scale ?? 1), i = Math.max(-1, Math.round(t.repeat ?? 0));
583
+ return { filename: t.filename, frameCount: e, frameRate: r, scale: a, repeat: i };
584
+ }
585
+ }
586
+ function fe(o) {
587
+ return o && o.__esModule && Object.prototype.hasOwnProperty.call(o, "default") ? o.default : o;
588
+ }
589
+ var xt, Ct = { exports: {} };
590
+ function ge() {
591
+ return xt || (xt = 1, o = Ct, (function() {
592
+ function t(l, h) {
593
+ return (function(s) {
594
+ if (typeof s != "string" || !s.match(/^data:image\/webp;base64,/i)) throw new Error("Failed to decode WebP Base64 URL");
595
+ return window.atob(s.substring(23));
596
+ })(typeof l == "string" && /^data:image\/webp/.test(l) ? l : l.toDataURL("image/webp", h));
597
+ }
598
+ function e(l) {
599
+ return (l.charCodeAt(0) | l.charCodeAt(1) << 8 | l.charCodeAt(2) << 16 | l.charCodeAt(3) << 24) >>> 0;
600
+ }
601
+ function r(l) {
602
+ let h = l.indexOf("VP8", 12);
603
+ if (h === -1) throw new Error("Bad image format, does this browser support WebP?");
604
+ let s = !1;
605
+ for (; h < l.length - 8; ) {
606
+ let d, u;
607
+ switch (u = l.substring(h, h + 4), h += 4, d = e(l.substring(h, h + 4)), h += 4, u) {
608
+ case "VP8 ":
609
+ return { frame: l.substring(h, h + d), hasAlpha: s };
610
+ case "ALPH":
611
+ s = !0;
612
+ }
613
+ h += d, 1 & d && h++;
614
+ }
615
+ throw new Error("Failed to find VP8 keyframe in WebP image, is this image mistakenly encoded in the Lossless WebP format?");
616
+ }
617
+ function a(l) {
618
+ this.value = l;
619
+ }
620
+ function i(l) {
621
+ this.value = l;
622
+ }
623
+ function n(l, h, s) {
624
+ if (Array.isArray(s)) for (let d = 0; d < s.length; d++) n(l, h, s[d]);
625
+ else if (typeof s == "string") l.writeString(s);
626
+ else if (s instanceof Uint8Array) l.writeBytes(s);
627
+ else {
628
+ if (!s.id) throw new Error("Bad EBML datatype " + typeof s.data);
629
+ if (s.offset = l.pos + h, l.writeUnsignedIntBE(s.id), Array.isArray(s.data)) {
630
+ let d, u, m;
631
+ s.size === -1 ? l.writeByte(255) : s.size === -2 ? (d = l.pos, l.writeBytes([15, 255, 255, 255, 255])) : (d = l.pos, l.writeBytes([0, 0, 0, 0])), u = l.pos, s.dataOffset = u + h, n(l, h, s.data), s.size !== -1 && s.size !== -2 && (m = l.pos, s.size = m - u, l.seek(d), l.writeEBMLVarIntWidth(s.size, 4), l.seek(m));
632
+ } else if (typeof s.data == "string") l.writeEBMLVarInt(s.data.length), s.dataOffset = l.pos + h, l.writeString(s.data);
633
+ else if (typeof s.data == "number") s.size || (s.size = l.measureUnsignedInt(s.data)), l.writeEBMLVarInt(s.size), s.dataOffset = l.pos + h, l.writeUnsignedIntBE(s.data, s.size);
634
+ else if (s.data instanceof i) l.writeEBMLVarInt(8), s.dataOffset = l.pos + h, l.writeDoubleBE(s.data.value);
635
+ else if (s.data instanceof a) l.writeEBMLVarInt(4), s.dataOffset = l.pos + h, l.writeFloatBE(s.data.value);
636
+ else {
637
+ if (!(s.data instanceof Uint8Array)) throw new Error("Bad EBML datatype " + typeof s.data);
638
+ l.writeEBMLVarInt(s.data.byteLength), s.dataOffset = l.pos + h, l.writeBytes(s.data);
639
+ }
640
+ }
641
+ }
642
+ let c = function(l, h) {
643
+ return function(s) {
644
+ let d, u, m = !1, y = 0, p = 0, f = null, _ = null, b = null, g = [], C = 0, S = 0, E = { quality: 0.95, transparent: !1, alphaQuality: void 0, fileWriter: null, fd: null, frameDuration: null, frameRate: null }, A = { Cues: { id: new Uint8Array([28, 83, 187, 107]), positionEBML: null }, SegmentInfo: { id: new Uint8Array([21, 73, 169, 102]), positionEBML: null }, Tracks: { id: new Uint8Array([22, 84, 174, 107]), positionEBML: null } }, L = { id: 17545, data: new i(0) }, V = [], I = new h(s.fileWriter || s.fd);
645
+ function D(w) {
646
+ return w - d.dataOffset;
647
+ }
648
+ function T() {
649
+ u = (function() {
650
+ let M = { id: 21420, size: 5, data: 0 }, U = { id: 290298740, data: [] };
651
+ for (let F in A) {
652
+ let z = A[F];
653
+ z.positionEBML = Object.create(M), U.data.push({ id: 19899, data: [{ id: 21419, data: z.id }, z.positionEBML] });
654
+ }
655
+ return U;
656
+ })();
657
+ let w = { id: 357149030, data: [{ id: 2807729, data: 1e6 }, { id: 19840, data: "webm-writer-js" }, { id: 22337, data: "webm-writer-js" }, L] }, x = [{ id: 176, data: y }, { id: 186, data: p }];
658
+ s.transparent && x.push({ id: 21440, data: 1 });
659
+ let k = { id: 374648427, data: [{ id: 174, data: [{ id: 215, data: 1 }, { id: 29637, data: 1 }, { id: 156, data: 0 }, { id: 2274716, data: "und" }, { id: 134, data: "V_VP8" }, { id: 2459272, data: "VP8" }, { id: 131, data: 1 }, { id: 224, data: x }] }] };
660
+ d = { id: 408125543, size: -2, data: [u, w, k] };
661
+ let $ = new l(256);
662
+ n($, I.pos, [{ id: 440786851, data: [{ id: 17030, data: 1 }, { id: 17143, data: 1 }, { id: 17138, data: 4 }, { id: 17139, data: 8 }, { id: 17026, data: "webm" }, { id: 17031, data: 2 }, { id: 17029, data: 2 }] }, d]), I.write($.getAsDataArray()), A.SegmentInfo.positionEBML.data = D(w.offset), A.Tracks.positionEBML.data = D(k.offset), m = !0;
663
+ }
664
+ function B(w) {
665
+ return w.alpha ? (function(x) {
666
+ let k, $, M = new l(4);
667
+ if (!(x.trackNumber > 0 && x.trackNumber < 127)) throw new Error("TrackNumber must be > 0 and < 127");
668
+ return M.writeEBMLVarInt(x.trackNumber), M.writeU16BE(x.timecode), M.writeByte(0), k = { id: 161, data: [M.getAsDataArray(), x.frame] }, $ = { id: 30113, data: [{ id: 166, data: [{ id: 238, data: 1 }, { id: 165, data: x.alpha }] }] }, { id: 160, data: [k, $] };
669
+ })(w) : (function(x) {
670
+ let k = new l(4);
671
+ if (!(x.trackNumber > 0 && x.trackNumber < 127)) throw new Error("TrackNumber must be > 0 and < 127");
672
+ return k.writeEBMLVarInt(x.trackNumber), k.writeU16BE(x.timecode), k.writeByte(128), { id: 163, data: [k.getAsDataArray(), x.frame] };
673
+ })(w);
674
+ }
675
+ function R() {
676
+ if (g.length === 0) return;
677
+ let w = 0;
678
+ for (let F = 0; F < g.length; F++) w += g[F].frame.length + (g[F].alpha ? g[F].alpha.length : 0);
679
+ let x = new l(w + 64 * g.length), k = (function(F) {
680
+ return { id: 524531317, data: [{ id: 231, data: Math.round(F.timecode) }] };
681
+ })({ timecode: Math.round(C) });
682
+ for (let F = 0; F < g.length; F++) k.data.push(B(g[F]));
683
+ var $, M, U;
684
+ n(x, I.pos, k), I.write(x.getAsDataArray()), $ = 1, M = Math.round(C), U = k.offset, V.push({ id: 187, data: [{ id: 179, data: M }, { id: 183, data: [{ id: 247, data: $ }, { id: 241, data: D(U) }] }] }), g = [], C += S, S = 0;
685
+ }
686
+ this.addFrame = function(w, x, k) {
687
+ m || (y = w.width || 0, p = w.height || 0, T());
688
+ let $, M = r(t(w, s.quality)), U = null;
689
+ $ = k || (typeof x == "number" ? x : s.frameDuration), s.transparent && (x instanceof HTMLCanvasElement || typeof x == "string" ? U = x : M.hasAlpha && (U = (function(F) {
690
+ f !== null && f.width === F.width && f.height === F.height || (f = document.createElement("canvas"), f.width = F.width, f.height = F.height, _ = f.getContext("2d"), b = _.createImageData(f.width, f.height));
691
+ let z = F.getContext("2d").getImageData(0, 0, F.width, F.height).data, J = b.data, K = 0, Wt = F.width * F.height * 4;
692
+ for (let st = 3; st < Wt; st += 4) {
693
+ let nt = z[st];
694
+ J[K++] = nt, J[K++] = nt, J[K++] = nt, J[K++] = 255;
695
+ }
696
+ return _.putImageData(b, 0, 0), f;
697
+ })(w))), (function(F) {
698
+ F.trackNumber = 1, F.timecode = Math.round(S), g.push(F), S += F.duration, S >= 5e3 && R();
699
+ })({ frame: M.frame, duration: $, alpha: U ? r(t(U, s.alphaQuality)).frame : null });
700
+ }, this.complete = function() {
701
+ return m || T(), R(), (function() {
702
+ let w = { id: 475249515, data: V }, x = new l(16 + 32 * V.length);
703
+ n(x, I.pos, w), I.write(x.getAsDataArray()), A.Cues.positionEBML.data = D(w.offset);
704
+ })(), (function() {
705
+ let w = new l(u.size), x = I.pos;
706
+ n(w, u.dataOffset, u.data), I.seek(u.dataOffset), I.write(w.getAsDataArray()), I.seek(x);
707
+ })(), (function() {
708
+ let w = new l(8), x = I.pos;
709
+ w.writeDoubleBE(C), I.seek(L.dataOffset), I.write(w.getAsDataArray()), I.seek(x);
710
+ })(), (function() {
711
+ let w = new l(10), x = I.pos;
712
+ w.writeUnsignedIntBE(d.id), w.writeEBMLVarIntWidth(I.pos - d.dataOffset, 5), I.seek(d.offset), I.write(w.getAsDataArray()), I.seek(x);
713
+ })(), I.complete("video/webm");
714
+ }, this.getWrittenSize = function() {
715
+ return I.length;
716
+ }, s = (function(w, x) {
717
+ let k = {};
718
+ return [w, x].forEach(function($) {
719
+ for (let M in $) Object.prototype.hasOwnProperty.call($, M) && (k[M] = $[M]);
720
+ }), k;
721
+ })(E, s || {}), (function() {
722
+ if (!s.frameDuration) {
723
+ if (!s.frameRate) throw new Error("Missing required frameDuration or frameRate setting");
724
+ s.frameDuration = 1e3 / s.frameRate;
725
+ }
726
+ s.quality = Math.max(Math.min(s.quality, 0.99999), 0), s.alphaQuality === void 0 ? s.alphaQuality = s.quality : s.alphaQuality = Math.max(Math.min(s.alphaQuality, 0.99999), 0);
727
+ })();
728
+ };
729
+ };
730
+ o.exports = c;
731
+ })()), Ct.exports;
732
+ var o;
733
+ }
734
+ var Et, Bt = { exports: {} };
735
+ function ve() {
736
+ return Et || (Et = 1, o = Bt, (function() {
737
+ let t = function(e) {
738
+ this.data = new Uint8Array(e), this.pos = 0;
739
+ };
740
+ t.prototype.seek = function(e) {
741
+ this.pos = e;
742
+ }, t.prototype.writeBytes = function(e) {
743
+ for (let r = 0; r < e.length; r++) this.data[this.pos++] = e[r];
744
+ }, t.prototype.writeByte = function(e) {
745
+ this.data[this.pos++] = e;
746
+ }, t.prototype.writeU8 = t.prototype.writeByte, t.prototype.writeU16BE = function(e) {
747
+ this.data[this.pos++] = e >> 8, this.data[this.pos++] = e;
748
+ }, t.prototype.writeDoubleBE = function(e) {
749
+ let r = new Uint8Array(new Float64Array([e]).buffer);
750
+ for (let a = r.length - 1; a >= 0; a--) this.writeByte(r[a]);
751
+ }, t.prototype.writeFloatBE = function(e) {
752
+ let r = new Uint8Array(new Float32Array([e]).buffer);
753
+ for (let a = r.length - 1; a >= 0; a--) this.writeByte(r[a]);
754
+ }, t.prototype.writeString = function(e) {
755
+ for (let r = 0; r < e.length; r++) this.data[this.pos++] = e.charCodeAt(r);
756
+ }, t.prototype.writeEBMLVarIntWidth = function(e, r) {
757
+ switch (r) {
758
+ case 1:
759
+ this.writeU8(128 | e);
760
+ break;
761
+ case 2:
762
+ this.writeU8(64 | e >> 8), this.writeU8(e);
763
+ break;
764
+ case 3:
765
+ this.writeU8(32 | e >> 16), this.writeU8(e >> 8), this.writeU8(e);
766
+ break;
767
+ case 4:
768
+ this.writeU8(16 | e >> 24), this.writeU8(e >> 16), this.writeU8(e >> 8), this.writeU8(e);
769
+ break;
770
+ case 5:
771
+ this.writeU8(8 | e / 4294967296 & 7), this.writeU8(e >> 24), this.writeU8(e >> 16), this.writeU8(e >> 8), this.writeU8(e);
772
+ break;
773
+ default:
774
+ throw new Error("Bad EBML VINT size " + r);
775
+ }
776
+ }, t.prototype.measureEBMLVarInt = function(e) {
777
+ if (e < 127) return 1;
778
+ if (e < 16383) return 2;
779
+ if (e < 2097151) return 3;
780
+ if (e < 268435455) return 4;
781
+ if (e < 34359738367) return 5;
782
+ throw new Error("EBML VINT size not supported " + e);
783
+ }, t.prototype.writeEBMLVarInt = function(e) {
784
+ this.writeEBMLVarIntWidth(e, this.measureEBMLVarInt(e));
785
+ }, t.prototype.writeUnsignedIntBE = function(e, r) {
786
+ switch (r === void 0 && (r = this.measureUnsignedInt(e)), r) {
787
+ case 5:
788
+ this.writeU8(Math.floor(e / 4294967296));
789
+ case 4:
790
+ this.writeU8(e >> 24);
791
+ case 3:
792
+ this.writeU8(e >> 16);
793
+ case 2:
794
+ this.writeU8(e >> 8);
795
+ case 1:
796
+ this.writeU8(e);
797
+ break;
798
+ default:
799
+ throw new Error("Bad UINT size " + r);
800
+ }
801
+ }, t.prototype.measureUnsignedInt = function(e) {
802
+ return e < 256 ? 1 : e < 65536 ? 2 : e < 1 << 24 ? 3 : e < 4294967296 ? 4 : 5;
803
+ }, t.prototype.getAsDataArray = function() {
804
+ if (this.pos < this.data.byteLength) return this.data.subarray(0, this.pos);
805
+ if (this.pos == this.data.byteLength) return this.data;
806
+ throw new Error("ArrayBufferDataStream's pos lies beyond end of buffer");
807
+ }, o.exports = t;
808
+ })()), Bt.exports;
809
+ var o;
810
+ }
811
+ var It, Ft, St, kt = { exports: {} };
812
+ function be() {
813
+ return It || (It = 1, o = kt, (function() {
814
+ let t = function(e) {
815
+ return function(r) {
816
+ let a = [], i = Promise.resolve(), n = null, c = null;
817
+ function l(d) {
818
+ return new Promise(function(u, m) {
819
+ let y = new FileReader();
820
+ y.addEventListener("loadend", function() {
821
+ u(y.result);
822
+ }), y.readAsArrayBuffer(d);
823
+ });
824
+ }
825
+ function h(d) {
826
+ return new Promise(function(u, m) {
827
+ d instanceof Uint8Array ? u(d) : d instanceof ArrayBuffer || ArrayBuffer.isView(d) ? u(new Uint8Array(d)) : d instanceof Blob ? u(l(d).then(function(y) {
828
+ return new Uint8Array(y);
829
+ })) : u(l(new Blob([d])).then(function(y) {
830
+ return new Uint8Array(y);
831
+ }));
832
+ });
833
+ }
834
+ function s(d) {
835
+ let u = d.byteLength || d.length || d.size;
836
+ if (!Number.isInteger(u)) throw new Error("Failed to determine size of element");
837
+ return u;
838
+ }
839
+ r && r.constructor.name === "FileWriter" ? n = r : e && r && (c = r), this.pos = 0, this.length = 0, this.seek = function(d) {
840
+ if (d < 0) throw new Error("Offset may not be negative");
841
+ if (isNaN(d)) throw new Error("Offset may not be NaN");
842
+ if (d > this.length) throw new Error("Seeking beyond the end of file is not allowed");
843
+ this.pos = d;
844
+ }, this.write = function(d) {
845
+ let u = { offset: this.pos, data: d, length: s(d) }, m = u.offset >= this.length;
846
+ this.pos += u.length, this.length = Math.max(this.length, this.pos), i = i.then(function() {
847
+ if (c) return new Promise(function(y, p) {
848
+ h(u.data).then(function(f) {
849
+ let _ = 0, b = Buffer.from(f.buffer), g = function(C, S, E) {
850
+ _ += S, _ >= E.length ? y() : e.write(c, E, _, E.length - _, u.offset + _, g);
851
+ };
852
+ e.write(c, b, 0, b.length, u.offset, g);
853
+ });
854
+ });
855
+ if (n) return new Promise(function(y, p) {
856
+ n.onwriteend = y, n.seek(u.offset), n.write(new Blob([u.data]));
857
+ });
858
+ if (!m) for (let y = 0; y < a.length; y++) {
859
+ let p = a[y];
860
+ if (!(u.offset + u.length <= p.offset || u.offset >= p.offset + p.length)) {
861
+ if (u.offset < p.offset || u.offset + u.length > p.offset + p.length) throw new Error("Overwrite crosses blob boundaries");
862
+ return u.offset == p.offset && u.length == p.length ? void (p.data = u.data) : h(p.data).then(function(f) {
863
+ return p.data = f, h(u.data);
864
+ }).then(function(f) {
865
+ u.data = f, p.data.set(u.data, u.offset - p.offset);
866
+ });
867
+ }
868
+ }
869
+ a.push(u);
870
+ });
871
+ }, this.complete = function(d) {
872
+ return i = c || n ? i.then(function() {
873
+ return null;
874
+ }) : i.then(function() {
875
+ let u = [];
876
+ for (let m = 0; m < a.length; m++) u.push(a[m].data);
877
+ return new Blob(u, { type: d });
878
+ }), i;
879
+ };
880
+ };
881
+ };
882
+ o.exports = t;
883
+ })()), kt.exports;
884
+ var o;
885
+ }
886
+ function ye() {
887
+ return St ? Ft : (St = 1, Ft = ge()(ve(), be()(null)));
888
+ }
889
+ var we = ye();
890
+ const _e = fe(we);
891
+ class xe {
892
+ async $record(t, e, r, a) {
893
+ const i = Math.max(1, Math.round(e.frameRate)), n = Math.max(1, Math.round(e.frameCount)), c = new _e({ quality: e.quality, alphaQuality: e.transparent ? e.quality : void 0, transparent: e.transparent, frameRate: i }), l = this._createSnapshotSurface();
894
+ return await new Promise((h, s) => {
895
+ let d, u = "capturing", m = !1, y = 0;
896
+ const p = () => {
897
+ d && (d(), d = void 0);
898
+ }, f = () => {
899
+ a?.({ state: "recording", frameIndex: y, totalFrames: n });
900
+ }, _ = (g) => {
901
+ if (m) return;
902
+ m = !0, p();
903
+ const C = g instanceof Error ? g.message : "WEBM export failed";
904
+ a?.({ state: "error", message: C }), s(g instanceof Error ? g : new Error(String(g)));
905
+ }, b = () => {
906
+ u !== "capturing" || m || (u = "encoding", p(), c.complete().then((g) => {
907
+ ((C) => {
908
+ m || (m = !0, a?.({ state: "completed", frameIndex: Math.min(n, y), totalFrames: n }), h(C));
909
+ })(g);
910
+ }).catch(_));
911
+ };
912
+ d = r(() => {
913
+ (() => {
914
+ if (u === "capturing") if (y >= n) b();
915
+ else try {
916
+ const g = l(t);
917
+ c.addFrame(g), y += 1, f(), y >= n && b();
918
+ } catch (g) {
919
+ _(g);
920
+ }
921
+ })();
922
+ }), f();
923
+ });
924
+ }
925
+ _createSnapshotSurface() {
926
+ let t = null, e = null;
927
+ return (r) => {
928
+ const a = Math.max(1, r.width), i = Math.max(1, r.height), n = ((c, l) => (t || (t = document.createElement("canvas")), t.width !== c || t.height !== l ? (t.width = c, t.height = l, e = t.getContext("2d"), e && (e.imageSmoothingEnabled = !1)) : e || (e = t.getContext("2d"), e && (e.imageSmoothingEnabled = !1)), e ? t : null))(a, i);
929
+ return n && e ? (e.clearRect(0, 0, a, i), e.drawImage(r, 0, 0, a, i), n) : r;
930
+ };
931
+ }
932
+ }
933
+ class Ce {
934
+ _recorder;
935
+ _textmodifier;
936
+ _registerPostDrawHook;
937
+ constructor(t, e) {
938
+ this._recorder = new xe(), this._textmodifier = t, this._registerPostDrawHook = e;
939
+ }
940
+ async $saveWEBM(t = {}) {
941
+ const e = this._textmodifier.canvas, r = this._applyDefaultOptions(t);
942
+ try {
943
+ const a = await this._recorder.$record(e, r, this._registerPostDrawHook, t.onProgress);
944
+ new Q().$downloadFile(a, r.filename);
945
+ } catch (a) {
946
+ throw t.onProgress?.({ state: "error", message: a instanceof Error ? a.message : "WEBM export failed" }), a;
947
+ }
948
+ }
949
+ _applyDefaultOptions(t) {
950
+ return { filename: t.filename, frameRate: Math.abs(Math.round(t.frameRate ?? 60)), frameCount: Math.abs(Math.round(t.frameCount ?? 300)), quality: Math.abs(Math.min(Math.max(t.quality ?? 1, 0), 1)), transparent: !!t.transparent, debugLogging: !!t.debugLogging };
951
+ }
952
+ }
953
+ const v = { root: "textmode-export-overlay", stack: "textmode-export-overlay__stack", stackDense: "textmode-export-overlay__stack--dense", stackCompact: "textmode-export-overlay__stack--compact", section: "textmode-export-overlay__section", header: "textmode-export-overlay__header", headerTitleRow: "textmode-export-overlay__header-title-row", headerLinks: "textmode-export-overlay__header-links", row: "textmode-export-overlay__row", label: "textmode-export-overlay__label", field: "textmode-export-overlay__field", fieldCompact: "textmode-export-overlay__field--compact", fieldDense: "textmode-export-overlay__field--dense", fieldFull: "textmode-export-overlay__field--full", fieldChannel: "textmode-export-overlay__field--channel", input: "textmode-export-overlay__input", checkbox: "textmode-export-overlay__checkbox", muted: "textmode-export-overlay__muted", status: "textmode-export-overlay__status", statusGif: "textmode-export-overlay__status--gif", statusVideo: "textmode-export-overlay__status--video", statusTitle: "textmode-export-overlay__status-title", statusValue: "textmode-export-overlay__status-value", divider: "textmode-export-overlay__divider", title: "textmode-export-overlay__title", button: "textmode-export-overlay__button", buttonPrimary: "textmode-export-overlay__button--primary", buttonSecondary: "textmode-export-overlay__button--secondary", buttonFull: "textmode-export-overlay__button--full", supportLink: "textmode-export-overlay__support-link", supportIcon: "textmode-export-overlay__support-icon", linkIcon: "textmode-export-overlay__link-icon" };
954
+ class O {
955
+ element;
956
+ mounted = !1;
957
+ destroyed = !1;
958
+ mount(t) {
959
+ if (this.destroyed) throw new Error("Cannot mount a destroyed component");
960
+ if (this.mounted) return;
961
+ const e = this.render();
962
+ t.appendChild(e), this.element = e, this.onMount(), this.mounted = !0;
963
+ }
964
+ unmount() {
965
+ this.mounted && this.element && (this._onUnmount(), this.element.remove(), this.element = void 0, this.mounted = !1);
966
+ }
967
+ destroy() {
968
+ this.destroyed || (this.unmount(), this._onDestroy(), this.destroyed = !0);
969
+ }
970
+ update(t) {
971
+ this.mounted && this.onUpdate(t);
972
+ }
973
+ onMount() {
974
+ }
975
+ _onUnmount() {
976
+ }
977
+ _onDestroy() {
978
+ }
979
+ onUpdate(t) {
980
+ }
981
+ get root() {
982
+ if (!this.element) throw new Error("Component is not mounted yet");
983
+ return this.element;
984
+ }
985
+ isMounted() {
986
+ return this.mounted;
987
+ }
988
+ }
989
+ class j extends O {
990
+ static _iconNamespace = "http://www.w3.org/2000/svg";
991
+ render() {
992
+ const t = document.createElement("div");
993
+ t.classList.add(v.stack, v.stackDense, v.header);
994
+ const e = document.createElement("div");
995
+ e.classList.add(v.headerTitleRow);
996
+ const r = document.createElement("strong");
997
+ r.textContent = "textmode.export.js", r.classList.add(v.title);
998
+ const a = document.createElement("div");
999
+ a.classList.add(v.headerLinks);
1000
+ const i = this._createLink("https://github.com/humanbydefinition/textmode.export.js", "View repository on GitHub", v.linkIcon, [{ d: "M0 0h24v24H0z", fill: "none", stroke: "none" }, { d: "M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" }]), n = this._createLink("https://code.textmode.art/support.html", "Support textmode.export.js", v.supportIcon, [{ d: "M0 0h24v24H0z", fill: "none", stroke: "none" }, { d: "M3 14c.83 .642 2.077 1.017 3.5 1c1.423 .017 2.67 -.358 3.5 -1c.83 -.642 2.077 -1.017 3.5 -1c1.423 -.017 2.67 .358 3.5 1" }, { d: "M8 3a2.4 2.4 0 0 0 -1 2a2.4 2.4 0 0 0 1 2" }, { d: "M12 3a2.4 2.4 0 0 0 -1 2a2.4 2.4 0 0 0 1 2" }, { d: "M3 10h14v5a6 6 0 0 1 -6 6h-2a6 6 0 0 1 -6 -6v-5z" }, { d: "M16.746 16.726a3 3 0 1 0 .252 -5.555" }]);
1001
+ a.appendChild(i), a.appendChild(n), e.appendChild(r), e.appendChild(a);
1002
+ const c = document.createElement("div");
1003
+ return c.classList.add(v.divider), t.appendChild(e), t.appendChild(c), t;
1004
+ }
1005
+ _createLink(t, e, r, a) {
1006
+ const i = document.createElement("a");
1007
+ return i.href = t, i.target = "_blank", i.rel = "noopener noreferrer", i.classList.add(v.supportLink), i.setAttribute("aria-label", e), i.appendChild(this._createIcon(r, a)), i;
1008
+ }
1009
+ _createIcon(t, e) {
1010
+ const r = document.createElementNS(j._iconNamespace, "svg");
1011
+ r.setAttribute("xmlns", j._iconNamespace), r.setAttribute("width", "18"), r.setAttribute("height", "18"), r.setAttribute("viewBox", "0 0 24 24"), r.setAttribute("fill", "none"), r.setAttribute("stroke", "currentColor"), r.setAttribute("stroke-width", "2"), r.setAttribute("stroke-linecap", "round"), r.setAttribute("stroke-linejoin", "round"), r.classList.add(t);
1012
+ for (const a of e) {
1013
+ const i = document.createElementNS(j._iconNamespace, "path");
1014
+ for (const [n, c] of Object.entries(a)) i.setAttribute(n, c);
1015
+ r.appendChild(i);
1016
+ }
1017
+ return r;
1018
+ }
1019
+ }
1020
+ class P extends O {
1021
+ props;
1022
+ constructor(t) {
1023
+ super(), this.props = t;
1024
+ }
1025
+ render() {
1026
+ const t = document.createElement("div");
1027
+ t.classList.add(v.field), this.applyVariant(t, this.props.variant);
1028
+ const e = document.createElement("label");
1029
+ if (e.classList.add(v.label), e.textContent = this.props.label, this.props.labelFor && (e.htmlFor = this.props.labelFor), t.appendChild(e), this.props.description) {
1030
+ const r = document.createElement("span");
1031
+ r.classList.add(v.muted), r.textContent = this.props.description, t.appendChild(r);
1032
+ }
1033
+ return t;
1034
+ }
1035
+ attachControl(t) {
1036
+ this.root.appendChild(t);
1037
+ }
1038
+ update(t) {
1039
+ this.props = t, super.update(t);
1040
+ }
1041
+ onUpdate(t) {
1042
+ const e = this.root;
1043
+ for (; e.firstChild; ) e.removeChild(e.firstChild);
1044
+ this.props = t, e.classList.add(v.field), this.applyVariant(e, t.variant);
1045
+ const r = document.createElement("label");
1046
+ if (r.classList.add(v.label), r.textContent = t.label, t.labelFor && (r.htmlFor = t.labelFor), e.appendChild(r), t.description) {
1047
+ const a = document.createElement("span");
1048
+ a.classList.add(v.muted), a.textContent = t.description, e.appendChild(a);
1049
+ }
1050
+ }
1051
+ applyVariant(t, e) {
1052
+ switch (t.classList.remove(v.fieldCompact, v.fieldDense, v.fieldFull, v.fieldChannel), e) {
1053
+ case "compact":
1054
+ t.classList.add(v.fieldCompact);
1055
+ break;
1056
+ case "dense":
1057
+ t.classList.add(v.fieldDense);
1058
+ break;
1059
+ case "full":
1060
+ t.classList.add(v.fieldFull);
1061
+ break;
1062
+ case "channel":
1063
+ t.classList.add(v.fieldChannel);
1064
+ }
1065
+ }
1066
+ }
1067
+ class mt extends O {
1068
+ props;
1069
+ select;
1070
+ handleChange = () => {
1071
+ this.props.defaultValue = this.select.value;
1072
+ };
1073
+ constructor(t) {
1074
+ super(), this.props = t;
1075
+ }
1076
+ render() {
1077
+ return this.select = document.createElement("select"), this.select.classList.add(v.input), this.props.id && (this.select.id = this.props.id), this.populateOptions(), this.select.addEventListener("change", this.handleChange), this.select;
1078
+ }
1079
+ get selectElement() {
1080
+ return this.select;
1081
+ }
1082
+ get value() {
1083
+ return this.select.value;
1084
+ }
1085
+ set value(t) {
1086
+ this.select && (this.select.value = t), this.props.defaultValue = t;
1087
+ }
1088
+ _onUnmount() {
1089
+ this.select.removeEventListener("change", this.handleChange);
1090
+ }
1091
+ onUpdate(t) {
1092
+ this.props = t, this.populateOptions(), t.defaultValue && (this.select.value = t.defaultValue);
1093
+ }
1094
+ update(t) {
1095
+ this.props = { ...this.props, ...t }, this.select && (t.id && (this.select.id = t.id), t.options && this.populateOptions(), t.defaultValue !== void 0 && (this.select.value = t.defaultValue));
1096
+ }
1097
+ populateOptions() {
1098
+ if (this.select) {
1099
+ this.select.innerHTML = "";
1100
+ for (const t of this.props.options) {
1101
+ const e = document.createElement("option");
1102
+ e.value = t.value, e.textContent = t.label, this.select.appendChild(e);
1103
+ }
1104
+ this.props.defaultValue && (this.select.value = this.props.defaultValue);
1105
+ }
1106
+ }
1107
+ }
1108
+ class Lt extends O {
1109
+ props;
1110
+ button;
1111
+ constructor(t) {
1112
+ super(), this.props = t;
1113
+ }
1114
+ render() {
1115
+ return this.button = document.createElement("button"), this.button.type = "button", this.button.textContent = this.props.label, this.button.classList.add(v.button), this.props.variant === "secondary" ? this.button.classList.add(v.buttonSecondary) : this.button.classList.add(v.buttonPrimary), this.props.fullWidth && this.button.classList.add(v.buttonFull), this.button.disabled = !!this.props.disabled, this.button;
1116
+ }
1117
+ get buttonElement() {
1118
+ return this.button;
1119
+ }
1120
+ setLabel(t) {
1121
+ this.props.label = t, this.button.textContent = t;
1122
+ }
1123
+ setDisabled(t) {
1124
+ this.props.disabled = t, this.button.disabled = t;
1125
+ }
1126
+ onUpdate(t) {
1127
+ this.props = t, this.button.textContent = t.label, this.button.disabled = !!t.disabled, t.variant === "secondary" ? (this.button.classList.add(v.buttonSecondary), this.button.classList.remove(v.buttonPrimary)) : (this.button.classList.add(v.buttonPrimary), this.button.classList.remove(v.buttonSecondary)), this.button.classList.toggle(v.buttonFull, !!t.fullWidth);
1128
+ }
1129
+ }
1130
+ class Ee {
1131
+ api;
1132
+ constructor(t) {
1133
+ this.api = t;
1134
+ }
1135
+ async $copy(t, e) {
1136
+ switch (t) {
1137
+ case "txt": {
1138
+ const r = this.api.toString(e);
1139
+ await navigator.clipboard.writeText(r);
1140
+ break;
1141
+ }
1142
+ case "svg": {
1143
+ const r = this.api.toSVG(e);
1144
+ await navigator.clipboard.writeText(r);
1145
+ break;
1146
+ }
1147
+ case "image":
1148
+ await this.api.copyCanvas(e);
1149
+ break;
1150
+ default:
1151
+ throw new Error(`Clipboard not supported for ${t}`);
1152
+ }
1153
+ }
1154
+ }
1155
+ class Be {
1156
+ api;
1157
+ events;
1158
+ constructor(t, e) {
1159
+ this.api = t, this.events = e;
1160
+ }
1161
+ async $requestExport(t, e, r = {}) {
1162
+ this.events.$emit("export:request", { format: t });
1163
+ try {
1164
+ const a = { onGIFProgress: r.onGIFProgress ? (i) => {
1165
+ r.onGIFProgress?.(i), this.events.$emit("export:progress", { format: t, progress: i });
1166
+ } : t === "gif" ? (i) => this.events.$emit("export:progress", { format: t, progress: i }) : void 0, onVideoProgress: r.onVideoProgress ? (i) => {
1167
+ r.onVideoProgress?.(i), this.events.$emit("export:progress", { format: t, progress: i });
1168
+ } : t === "video" ? (i) => this.events.$emit("export:progress", { format: t, progress: i }) : void 0 };
1169
+ await this._execute(t, e, a), this.events.$emit("export:success", { format: t });
1170
+ } catch (a) {
1171
+ throw this.events.$emit("export:error", { format: t, error: a }), a;
1172
+ }
1173
+ }
1174
+ _execute(t, e, r) {
1175
+ switch (t) {
1176
+ case "txt":
1177
+ return Promise.resolve(this.api.saveStrings(e));
1178
+ case "image":
1179
+ return this.api.saveCanvas(e);
1180
+ case "svg":
1181
+ return Promise.resolve(this.api.saveSVG(e));
1182
+ case "gif": {
1183
+ const a = { ...e, onProgress: r.onGIFProgress };
1184
+ return this.api.saveGIF(a);
1185
+ }
1186
+ case "video": {
1187
+ const a = { ...e, onProgress: r.onVideoProgress };
1188
+ return this.api.saveWEBM(a);
1189
+ }
1190
+ }
1191
+ }
1192
+ }
1193
+ class Ie {
1194
+ modifier;
1195
+ overlay;
1196
+ animationFrameId = null;
1197
+ handleUpdate;
1198
+ bound = !1;
1199
+ constructor(t, e) {
1200
+ this.modifier = t, this.overlay = e, this.handleUpdate = () => this.scheduleUpdate();
1201
+ }
1202
+ scheduleUpdate() {
1203
+ this.animationFrameId !== null && cancelAnimationFrame(this.animationFrameId), this.animationFrameId = requestAnimationFrame(() => this.update());
1204
+ }
1205
+ bind() {
1206
+ this.bound || (window.addEventListener("resize", this.handleUpdate), window.addEventListener("scroll", this.handleUpdate, !0), this.bound = !0, this.handleUpdate());
1207
+ }
1208
+ update() {
1209
+ this.animationFrameId = null;
1210
+ const t = this.modifier.canvas.getBoundingClientRect();
1211
+ this.overlay.style.top = `${t.top + window.scrollY + 8}px`, this.overlay.style.left = `${t.left + window.scrollX + 8}px`;
1212
+ }
1213
+ dispose() {
1214
+ this.animationFrameId !== null && (cancelAnimationFrame(this.animationFrameId), this.animationFrameId = null), this.bound && (window.removeEventListener("resize", this.handleUpdate), window.removeEventListener("scroll", this.handleUpdate, !0), this.bound = !1);
1215
+ }
1216
+ }
1217
+ const X = "textmode-export-number-input", Fe = `${X}__field`, Se = `${X}__controls`, ke = `${X}__control`, Ut = `${X}__display`, et = `${Ut}--visible`, Le = (o) => {
1218
+ o.dispatchEvent(new Event("input", { bubbles: !0 })), o.dispatchEvent(new Event("change", { bubbles: !0 }));
1219
+ }, Mt = (o) => {
1220
+ const t = document.createElement("button");
1221
+ return t.type = "button", t.className = ke, t.textContent = o > 0 ? "▲" : "▼", t;
1222
+ };
1223
+ class G extends O {
1224
+ props;
1225
+ input;
1226
+ display;
1227
+ incrementButton;
1228
+ decrementButton;
1229
+ suppressClickAfterPointer = /* @__PURE__ */ new WeakMap();
1230
+ holdTimeoutId;
1231
+ holdIntervalId;
1232
+ activePointerId;
1233
+ disabledObserver;
1234
+ constructor(t) {
1235
+ super(), this.props = t;
1236
+ }
1237
+ render() {
1238
+ const t = document.createElement("div");
1239
+ t.className = X, this.input = document.createElement("input"), this.input.type = "number", this.input.value = this.props.defaultValue, Object.assign(this.input, this.props.attributes), this.input.className = Fe, this.display = document.createElement("div"), this.display.className = Ut;
1240
+ const e = document.createElement("div");
1241
+ return e.className = Se, this.incrementButton = Mt(1), this.decrementButton = Mt(-1), e.appendChild(this.incrementButton), e.appendChild(this.decrementButton), t.appendChild(this.input), t.appendChild(this.display), t.appendChild(e), this.bindStepControls(), this.bindHoldBehavior(), this.bindInputListeners(), this.observeDisabledState(t), this.updateDisplay(), t;
1242
+ }
1243
+ get inputElement() {
1244
+ return this.input;
1245
+ }
1246
+ get value() {
1247
+ return this.input.value;
1248
+ }
1249
+ set value(t) {
1250
+ this.input.value = t, this.updateDisplay();
1251
+ }
1252
+ refresh() {
1253
+ this.updateDisplay();
1254
+ }
1255
+ _onDestroy() {
1256
+ this.clearHoldTimers(), this.disabledObserver?.disconnect();
1257
+ }
1258
+ bindStepControls() {
1259
+ const t = (i) => () => {
1260
+ if (this.input.disabled) return;
1261
+ const n = this.input.value, c = this.input.getAttribute("step");
1262
+ if (c && c !== "any") i > 0 ? this.input.stepUp() : this.input.stepDown();
1263
+ else {
1264
+ const l = i > 0 ? 1 : -1, h = Number.parseFloat(this.input.value || "0"), s = Number.isFinite(h) ? h + l : l;
1265
+ this.input.value = String(s);
1266
+ }
1267
+ this.input.value !== n && Le(this.input), this.updateDisplay(), this.input.focus({ preventScroll: !0 });
1268
+ }, e = t(1), r = t(-1), a = (i, n) => (c) => {
1269
+ if (this.suppressClickAfterPointer.get(i)) return this.suppressClickAfterPointer.set(i, !1), void c.preventDefault();
1270
+ n();
1271
+ };
1272
+ this.incrementButton.addEventListener("click", a(this.incrementButton, e)), this.decrementButton.addEventListener("click", a(this.decrementButton, r));
1273
+ }
1274
+ bindHoldBehavior() {
1275
+ const t = (e, r) => {
1276
+ const a = r > 0 ? () => this.incrementButton.click() : () => this.decrementButton.click();
1277
+ e.addEventListener("pointerdown", (c) => {
1278
+ if (c.button === 0) {
1279
+ c.preventDefault(), this.suppressClickAfterPointer.set(e, !0), this.activePointerId = c.pointerId;
1280
+ try {
1281
+ e.setPointerCapture(c.pointerId);
1282
+ } catch {
1283
+ }
1284
+ a(), this.holdTimeoutId = window.setTimeout(() => {
1285
+ this.holdIntervalId = window.setInterval(a, 80);
1286
+ }, 380);
1287
+ }
1288
+ });
1289
+ const i = () => {
1290
+ if (this.activePointerId !== void 0) {
1291
+ try {
1292
+ e.releasePointerCapture(this.activePointerId);
1293
+ } catch {
1294
+ }
1295
+ this.activePointerId = void 0;
1296
+ }
1297
+ }, n = () => {
1298
+ this.clearHoldTimers(), i();
1299
+ };
1300
+ e.addEventListener("pointerup", n), e.addEventListener("pointerleave", n), e.addEventListener("pointercancel", n);
1301
+ };
1302
+ t(this.incrementButton, 1), t(this.decrementButton, -1);
1303
+ }
1304
+ bindInputListeners() {
1305
+ const t = () => {
1306
+ this.props.defaultValue = this.input.value, this.updateDisplay();
1307
+ };
1308
+ this.input.addEventListener("input", t), this.input.addEventListener("change", t);
1309
+ }
1310
+ observeDisabledState(t) {
1311
+ const e = () => {
1312
+ const r = this.input.disabled;
1313
+ this.incrementButton.disabled = r, this.decrementButton.disabled = r, t.classList.toggle("is-disabled", r), r ? (this.display.classList.remove(et), this.input.style.removeProperty("color"), this.input.style.removeProperty("caretColor")) : this.updateDisplay();
1314
+ };
1315
+ typeof MutationObserver < "u" && (this.disabledObserver = new MutationObserver(e), this.disabledObserver.observe(this.input, { attributes: !0, attributeFilter: ["disabled"] })), e();
1316
+ }
1317
+ updateDisplay() {
1318
+ const t = this.props.formatDisplay;
1319
+ if (!t) return this.display.textContent = "", this.display.classList.remove(et), this.input.style.removeProperty("color"), void this.input.style.removeProperty("caretColor");
1320
+ const e = this.input.value, r = Number.parseFloat(e), a = t(Number.isFinite(r) ? r : Number.NaN, e, this.input);
1321
+ a ? (this.display.textContent = a, this.display.classList.add(et), this.input.style.color = "transparent", this.input.style.caretColor = "#f8fafc") : (this.display.textContent = "", this.display.classList.remove(et), this.input.style.removeProperty("color"), this.input.style.removeProperty("caretColor"));
1322
+ }
1323
+ clearHoldTimers() {
1324
+ this.holdTimeoutId !== void 0 && (window.clearTimeout(this.holdTimeoutId), this.holdTimeoutId = void 0), this.holdIntervalId !== void 0 && (window.clearInterval(this.holdIntervalId), this.holdIntervalId = void 0);
1325
+ }
1326
+ }
1327
+ class H extends O {
1328
+ variant;
1329
+ additionalClasses;
1330
+ constructor(t = "stack", e = []) {
1331
+ super(), this.variant = t, this.additionalClasses = e;
1332
+ }
1333
+ render() {
1334
+ const t = document.createElement("div"), e = [this.variant === "stack" ? v.stack : void 0, this.variant === "stackDense" ? v.stackDense : void 0, this.variant === "stackCompact" ? v.stackCompact : void 0, this.variant === "row" ? v.row : void 0, this.variant === "section" ? v.section : void 0, ...this.additionalClasses].filter(Boolean);
1335
+ return t.classList.add(...e), t;
1336
+ }
1337
+ }
1338
+ const $t = { neutral: "textmode-export-overlay__status-value--neutral", active: "textmode-export-overlay__status-value--active", alert: "textmode-export-overlay__status-value--alert" };
1339
+ class Nt extends O {
1340
+ props;
1341
+ container;
1342
+ messageElement;
1343
+ constructor(t) {
1344
+ super(), this.props = t;
1345
+ }
1346
+ render() {
1347
+ this.container = document.createElement("div"), this.container.classList.add(v.status), this.props.context === "gif" ? this.container.classList.add(v.statusGif) : this.props.context === "video" && this.container.classList.add(v.statusVideo);
1348
+ const t = document.createElement("span");
1349
+ return t.classList.add(v.statusTitle), t.textContent = this.props.title, this.messageElement = document.createElement("span"), this.messageElement.classList.add(v.statusValue), this.messageElement.textContent = this.props.message, this.applyVariant(this.props.variant ?? "neutral"), this.container.appendChild(t), this.container.appendChild(this.messageElement), this.container;
1350
+ }
1351
+ setMessage(t, e = "neutral") {
1352
+ this.messageElement.textContent = t, this.applyVariant(e);
1353
+ }
1354
+ onUpdate(t) {
1355
+ this.props = t, this.setMessage(t.message, t.variant ?? "neutral");
1356
+ }
1357
+ applyVariant(t) {
1358
+ this.messageElement.classList.remove(...Object.values($t)), this.messageElement.classList.add($t[t]);
1359
+ }
1360
+ }
1361
+ class Y extends O {
1362
+ _config;
1363
+ _managedComponents = /* @__PURE__ */ new Set();
1364
+ constructor(t) {
1365
+ super(), this._config = t;
1366
+ }
1367
+ _manageComponent(t) {
1368
+ return this._managedComponents.add(t), t;
1369
+ }
1370
+ _onUnmount() {
1371
+ for (const t of this._managedComponents) t.unmount();
1372
+ }
1373
+ _onDestroy() {
1374
+ for (const t of this._managedComponents) t.destroy();
1375
+ this._managedComponents.clear();
1376
+ }
1377
+ }
1378
+ const W = { frameCount: 300, frameRate: 60, scale: 1, repeat: 0 };
1379
+ class ut extends Y {
1380
+ frameCountInput = this._manageComponent(new G({ defaultValue: String(W.frameCount), attributes: { min: "1", max: "600", step: "1" } }));
1381
+ frameRateInput = this._manageComponent(new G({ defaultValue: String(W.frameRate), attributes: { min: "1", max: "60", step: "1" } }));
1382
+ scaleInput = this._manageComponent(new G({ defaultValue: String(W.scale), attributes: { min: "0.1", max: "8", step: "0.1" } }));
1383
+ repeatInput = this._manageComponent(new G({ defaultValue: "0", attributes: { min: "0", max: "50", step: "1" }, formatDisplay: (t, e) => Number.isFinite(t) ? t === 0 ? "∞" : null : e.trim() === "" ? null : e }));
1384
+ status = this._manageComponent(new Nt({ title: "status", message: "ready to record", variant: "neutral", context: "gif" }));
1385
+ recordingState = "idle";
1386
+ constructor(t) {
1387
+ super(t);
1388
+ }
1389
+ render() {
1390
+ const t = document.createElement("div");
1391
+ t.classList.add(v.stack);
1392
+ const e = new H("row");
1393
+ e.mount(t);
1394
+ const r = new P({ label: "number of frames", labelFor: "textmode-export-gif-frame-count", variant: "dense" });
1395
+ r.mount(e.root), this.frameCountInput.mount(r.root), this.frameCountInput.inputElement.id = "textmode-export-gif-frame-count";
1396
+ const a = new P({ label: "frame rate (fps)", labelFor: "textmode-export-gif-frame-rate", variant: "dense" });
1397
+ a.mount(e.root), this.frameRateInput.mount(a.root), this.frameRateInput.inputElement.id = "textmode-export-gif-frame-rate";
1398
+ const i = new H("row");
1399
+ i.mount(t);
1400
+ const n = new P({ label: "scale", labelFor: "textmode-export-gif-scale", variant: "dense" });
1401
+ n.mount(i.root), this.scaleInput.mount(n.root), this.scaleInput.inputElement.id = "textmode-export-gif-scale";
1402
+ const c = new P({ label: "loop count", labelFor: "textmode-export-gif-repeat", variant: "dense" });
1403
+ return c.mount(i.root), this.repeatInput.mount(c.root), this.repeatInput.inputElement.id = "textmode-export-gif-repeat", this.status.mount(t), t;
1404
+ }
1405
+ getOptions() {
1406
+ const t = this._config.defaultOptions ?? {};
1407
+ return { frameCount: this.safeParseInt(this.frameCountInput.value, t.frameCount ?? 300), frameRate: this.safeParseInt(this.frameRateInput.value, t.frameRate ?? 60), scale: this.safeParseFloat(this.scaleInput.value, t.scale ?? 1), repeat: this.safeParseInt(this.repeatInput.value, t.repeat ?? 0) };
1408
+ }
1409
+ reset() {
1410
+ this.recordingState = "idle", this.applyDefaults(), this.setRecordingState("idle");
1411
+ }
1412
+ validate() {
1413
+ const t = Number.parseInt(this.frameCountInput.value, 10), e = Number.parseInt(this.frameRateInput.value, 10), r = Number.parseFloat(this.scaleInput.value);
1414
+ return Number.isFinite(t) && t > 0 && Number.isFinite(e) && e > 0 && Number.isFinite(r) && r > 0;
1415
+ }
1416
+ isRecording() {
1417
+ return this.recordingState === "recording";
1418
+ }
1419
+ setRecordingState(t, e) {
1420
+ this.recordingState = t;
1421
+ const r = t === "recording";
1422
+ switch (this.frameCountInput.inputElement.disabled = r, this.frameRateInput.inputElement.disabled = r, this.scaleInput.inputElement.disabled = r, this.repeatInput.inputElement.disabled = r, t) {
1423
+ case "recording":
1424
+ if (e?.totalFrames) {
1425
+ const a = e.frameIndex ?? 0;
1426
+ this.status.setMessage(`recording ${a}/${e.totalFrames}`, "active");
1427
+ } else this.status.setMessage("recording…", "active");
1428
+ break;
1429
+ case "completed":
1430
+ this.status.setMessage("GIF saved successfully", "active");
1431
+ break;
1432
+ case "error":
1433
+ this.status.setMessage(e?.message ?? "failed to export GIF", "alert");
1434
+ break;
1435
+ default:
1436
+ this.status.setMessage("ready to record", "neutral");
1437
+ }
1438
+ }
1439
+ handleProgress(t) {
1440
+ t.state === "recording" && t.totalFrames ? this.status.setMessage(`recording ${t.frameIndex ?? 0}/${t.totalFrames}`, "active") : t.state === "completed" ? this.status.setMessage("GIF saved successfully", "active") : t.state === "error" && this.status.setMessage(t.message ?? "failed to export GIF", "alert");
1441
+ }
1442
+ applyDefaults() {
1443
+ const t = this._config.defaultOptions ?? {}, e = t.frameCount ?? W.frameCount, r = t.frameRate ?? W.frameRate, a = t.scale ?? W.scale, i = t.repeat ?? W.repeat;
1444
+ this.frameCountInput.value = String(e), this.frameRateInput.value = String(r), this.scaleInput.value = String(a), this.repeatInput.value = String(i), this.frameCountInput.refresh(), this.frameRateInput.refresh(), this.scaleInput.refresh(), this.repeatInput.refresh();
1445
+ }
1446
+ safeParseInt(t, e) {
1447
+ const r = Number.parseInt(t, 10);
1448
+ return Number.isFinite(r) ? r : e;
1449
+ }
1450
+ safeParseFloat(t, e) {
1451
+ const r = Number.parseFloat(t);
1452
+ return Number.isFinite(r) ? r : e;
1453
+ }
1454
+ }
1455
+ const it = "textmode-export-range-input", Me = `${it}__field`, $e = `${it}__tooltip`, ct = `${it}__tooltip--visible`, rt = (o, t) => {
1456
+ if (typeof o != "string" || o.length === 0) return t;
1457
+ const e = Number.parseFloat(o);
1458
+ return Number.isFinite(e) ? e : t;
1459
+ }, Ae = (o, t, e) => Math.min(e, Math.max(t, o));
1460
+ class Pe extends O {
1461
+ props;
1462
+ input;
1463
+ tooltip;
1464
+ hoverActive = !1;
1465
+ focusActive = !1;
1466
+ pointerActive = !1;
1467
+ suppressFocusFromPointer = !1;
1468
+ activePointerId;
1469
+ disabledObserver;
1470
+ resizeObserver;
1471
+ constructor(t) {
1472
+ super(), this.props = t;
1473
+ }
1474
+ render() {
1475
+ const t = document.createElement("div");
1476
+ return t.className = it, this.input = document.createElement("input"), this.input.type = "range", this.input.value = this.props.defaultValue, Object.assign(this.input, this.props.attributes), this.input.className = Me, this.tooltip = document.createElement("div"), this.tooltip.className = $e, t.appendChild(this.input), t.appendChild(this.tooltip), this.bindEvents(t), this.updateTooltip(), this.syncDisabledState(t), t;
1477
+ }
1478
+ get inputElement() {
1479
+ return this.input;
1480
+ }
1481
+ get value() {
1482
+ return this.input.value;
1483
+ }
1484
+ set value(t) {
1485
+ this.input.value = t, this.updateTooltip();
1486
+ }
1487
+ _onDestroy() {
1488
+ this.disabledObserver?.disconnect(), this.resizeObserver?.disconnect();
1489
+ }
1490
+ bindEvents(t) {
1491
+ this.input.addEventListener("pointerenter", () => {
1492
+ this.hoverActive = !0, this.refreshTooltipVisibility();
1493
+ }), this.input.addEventListener("pointerleave", () => {
1494
+ this.hoverActive = !1, this.refreshTooltipVisibility();
1495
+ }), this.input.addEventListener("focus", () => {
1496
+ let r = !1;
1497
+ try {
1498
+ r = this.input.matches(":focus-visible");
1499
+ } catch {
1500
+ r = !this.suppressFocusFromPointer;
1501
+ }
1502
+ this.suppressFocusFromPointer || !r || this.input.disabled ? this.focusActive = !1 : this.focusActive = !0, this.suppressFocusFromPointer = !1, this.refreshTooltipVisibility();
1503
+ }), this.input.addEventListener("blur", () => {
1504
+ this.focusActive = !1, this.refreshTooltipVisibility();
1505
+ }), this.input.addEventListener("pointerdown", (r) => {
1506
+ this.pointerActive = !0, this.suppressFocusFromPointer = !0, this.hoverActive = !0, this.refreshTooltipVisibility();
1507
+ try {
1508
+ this.input.setPointerCapture(r.pointerId), this.activePointerId = r.pointerId;
1509
+ } catch {
1510
+ this.activePointerId = void 0;
1511
+ }
1512
+ });
1513
+ const e = () => {
1514
+ this.pointerActive = !1, this.suppressFocusFromPointer = !1, this.hoverActive = this.input.matches(":hover"), this.releasePointerCapture(), this.refreshTooltipVisibility();
1515
+ };
1516
+ this.input.addEventListener("pointerup", e), this.input.addEventListener("pointercancel", e), this.input.addEventListener("lostpointercapture", e), this.input.addEventListener("input", () => {
1517
+ (this.hoverActive || this.focusActive || this.pointerActive) && this.updateTooltip(), this.props.defaultValue = this.input.value;
1518
+ }), this.input.addEventListener("change", () => {
1519
+ this.pointerActive || this.updateTooltip(), this.props.defaultValue = this.input.value;
1520
+ }), typeof ResizeObserver < "u" && (this.resizeObserver = new ResizeObserver(() => {
1521
+ this.tooltip.classList.contains(ct) && this.updateTooltipPosition();
1522
+ }), this.resizeObserver.observe(t));
1523
+ }
1524
+ refreshTooltipVisibility() {
1525
+ const t = (this.hoverActive || this.focusActive || this.pointerActive) && !this.input.disabled;
1526
+ t && this.updateTooltip(), this.tooltip.classList.toggle(ct, t);
1527
+ }
1528
+ syncDisabledState(t) {
1529
+ const e = () => {
1530
+ const r = this.input.disabled;
1531
+ t.classList.toggle("is-disabled", r), r && (this.hoverActive = !1, this.focusActive = !1, this.pointerActive = !1, this.suppressFocusFromPointer = !1, this.releasePointerCapture(), this.tooltip.classList.remove(ct));
1532
+ };
1533
+ typeof MutationObserver < "u" && (this.disabledObserver = new MutationObserver(e), this.disabledObserver.observe(this.input, { attributes: !0, attributeFilter: ["disabled"] })), e();
1534
+ }
1535
+ releasePointerCapture() {
1536
+ if (this.activePointerId !== void 0) {
1537
+ try {
1538
+ this.input.releasePointerCapture(this.activePointerId);
1539
+ } catch {
1540
+ }
1541
+ this.activePointerId = void 0;
1542
+ }
1543
+ }
1544
+ updateTooltip() {
1545
+ this.updateTooltipContent(), this.updateTooltipPosition();
1546
+ }
1547
+ updateTooltipContent() {
1548
+ const t = this.props.formatValue ?? ((r) => `${r}`), e = this.getCurrentValue();
1549
+ this.tooltip.textContent = t(e, this.input);
1550
+ }
1551
+ updateTooltipPosition() {
1552
+ const t = this.getMin(), e = this.getMax(), r = Ae(this.getCurrentValue(), t, e), a = e - t, i = a === 0 ? 0 : (r - t) / a;
1553
+ this.tooltip.style.left = 100 * i + "%";
1554
+ }
1555
+ getMin() {
1556
+ return rt(this.input.min, 0);
1557
+ }
1558
+ getMax() {
1559
+ return rt(this.input.max, 100);
1560
+ }
1561
+ getCurrentValue() {
1562
+ return rt(this.input.value, rt(this.props.defaultValue, 0));
1563
+ }
1564
+ }
1565
+ function De(o) {
1566
+ if (!Number.isFinite(o)) return "default quality";
1567
+ const t = Math.max(0, Math.min(1, o));
1568
+ return t >= 0.995 ? "near-lossless" : t >= 0.98 ? "maximum detail" : t >= 0.88 ? "ultra quality" : t >= 0.7 ? "high quality" : t >= 0.5 ? "balanced" : t >= 0.25 ? "compact" : t >= 0.1 ? "lightweight" : "draft";
1569
+ }
1570
+ function Ve(o) {
1571
+ return `${De(o)} (quality ≈ ${o.toFixed(3)})`;
1572
+ }
1573
+ class ht extends Y {
1574
+ frameRateInput = this._manageComponent(new G({ defaultValue: String(60), attributes: { min: String(1), max: String(60), step: "1" }, formatDisplay: (t) => Number.isFinite(t) ? `${t} fps` : null }));
1575
+ frameCountInput = this._manageComponent(new G({ defaultValue: String(480), attributes: { min: String(1), max: String(3600), step: "1" } }));
1576
+ qualityInput = this._manageComponent(new Pe({ defaultValue: String(0.7), attributes: { min: String(0), max: String(1), step: "0.001" }, formatValue: (t) => Ve(t) }));
1577
+ status = this._manageComponent(new Nt({ title: "status", message: "ready to record", variant: "neutral", context: "video" }));
1578
+ recordingState = "idle";
1579
+ constructor(t) {
1580
+ super(t);
1581
+ }
1582
+ render() {
1583
+ const t = document.createElement("div");
1584
+ t.classList.add(v.stack);
1585
+ const e = new H("row");
1586
+ e.mount(t);
1587
+ const r = new P({ label: "number of frames", labelFor: "textmode-export-video-frame-count", variant: "compact" });
1588
+ r.mount(e.root), this.frameCountInput.mount(r.root), this.frameCountInput.inputElement.id = "textmode-export-video-frame-count";
1589
+ const a = new P({ label: "frame rate (fps)", labelFor: "textmode-export-video-frame-rate", variant: "compact" });
1590
+ a.mount(e.root), this.frameRateInput.mount(a.root), this.frameRateInput.inputElement.id = "textmode-export-video-frame-rate";
1591
+ const i = new P({ label: "quality", labelFor: "textmode-export-video-quality", variant: "full" });
1592
+ return i.mount(t), this.qualityInput.mount(i.root), this.qualityInput.inputElement.id = "textmode-export-video-quality", this.status.mount(t), t;
1593
+ }
1594
+ getOptions() {
1595
+ const t = this._config.defaultOptions ?? {}, e = Number.parseInt(this.frameCountInput.value, 10), r = Number.parseFloat(this.frameRateInput.value), a = Number.parseFloat(this.qualityInput.value);
1596
+ return { frameCount: Number.isFinite(e) ? e : t.frameCount ?? 480, frameRate: Number.isFinite(r) ? r : t.frameRate ?? 60, quality: Number.isFinite(a) ? a : t.quality ?? 0.7 };
1597
+ }
1598
+ reset() {
1599
+ this.recordingState = "idle", this.applyDefaults(), this.status.setMessage("ready to record", "neutral");
1600
+ }
1601
+ validate() {
1602
+ const t = Number.parseInt(this.frameCountInput.value, 10), e = Number.parseFloat(this.frameRateInput.value);
1603
+ return Number.isFinite(t) && t >= 1 && Number.isFinite(e) && e >= 1;
1604
+ }
1605
+ isRecording() {
1606
+ return this.recordingState === "recording";
1607
+ }
1608
+ setRecordingState(t, e) {
1609
+ this.recordingState = t;
1610
+ const r = t === "recording";
1611
+ this.frameCountInput.inputElement.disabled = r, this.frameRateInput.inputElement.disabled = r, this.qualityInput.inputElement.disabled = r, this.syncStatus(t, e);
1612
+ }
1613
+ handleProgress(t) {
1614
+ this.syncStatus(t.state, t);
1615
+ }
1616
+ syncStatus(t, e) {
1617
+ switch (t) {
1618
+ case "recording": {
1619
+ const r = e?.frameIndex ?? 0, a = e?.totalFrames ?? this.resolvePlannedFrameCount();
1620
+ if (a) {
1621
+ const i = Math.min(Math.max(0, Math.round(r)), a);
1622
+ this.status.setMessage(`recording ${i}/${a} frames`, "active");
1623
+ } else this.status.setMessage(`recording ${Math.max(0, Math.round(r))} frames`, "active");
1624
+ break;
1625
+ }
1626
+ case "completed":
1627
+ this.status.setMessage("saved to disk", "active");
1628
+ break;
1629
+ case "error":
1630
+ this.status.setMessage(e?.message ? `error: ${e.message}` : "recording failed", "alert");
1631
+ break;
1632
+ default:
1633
+ this.status.setMessage("ready to record", "neutral");
1634
+ }
1635
+ }
1636
+ resolvePlannedFrameCount() {
1637
+ const t = Number.parseInt(this.frameCountInput.value, 10);
1638
+ return Number.isFinite(t) && t > 0 ? Math.round(t) : void 0;
1639
+ }
1640
+ applyDefaults() {
1641
+ const t = this._config.defaultOptions ?? {}, e = t.frameCount ?? 480, r = t.frameRate ?? 60, a = t.quality ?? 0.7;
1642
+ this.frameCountInput.value = String(e), this.frameRateInput.value = String(r), this.qualityInput.value = String(a), this.frameCountInput.refresh(), this.frameRateInput.refresh();
1643
+ }
1644
+ }
1645
+ class Oe {
1646
+ _textmodifier;
1647
+ _state;
1648
+ _events;
1649
+ _exportService;
1650
+ _clipboardService;
1651
+ _definitions;
1652
+ _formats = /* @__PURE__ */ new Map();
1653
+ _eventUnsubscribers = [];
1654
+ _overlayElement;
1655
+ _optionsContainer;
1656
+ _copyButtonContainer;
1657
+ _positionService;
1658
+ _header = new j();
1659
+ _formatField = new P({ label: "export format", labelFor: "textmode-export-format", variant: "full" });
1660
+ _formatSelect = new mt({ id: "textmode-export-format", options: [] });
1661
+ _exportButton = new Lt({ label: "download file", fullWidth: !0, variant: "primary" });
1662
+ _copyButton = new Lt({ label: "copy to clipboard", fullWidth: !0, variant: "primary" });
1663
+ _currentFormat;
1664
+ _currentBlade;
1665
+ _handleFormatSelectChange = () => {
1666
+ this._handleFormatChange(this._formatSelect.value);
1667
+ };
1668
+ _handleExportClickSafe = () => {
1669
+ this._handleExportClick().catch((t) => {
1670
+ console.error("[textmode-export] Export failed", t);
1671
+ });
1672
+ };
1673
+ _handleCopyClickSafe = () => {
1674
+ this._handleCopyClick().catch((t) => {
1675
+ console.error("[textmode-export] Copy failed", t);
1676
+ });
1677
+ };
1678
+ constructor(t, e, r, a, i) {
1679
+ this._textmodifier = t, this._state = r, this._events = a, this._exportService = new Be(e, a), this._clipboardService = new Ee(e), this._definitions = i, this._currentFormat = r.snapshot.format, this._initializeFormatMap(), this._registerEventHandlers();
1680
+ }
1681
+ $mount() {
1682
+ this._createOverlay(), this._renderStaticContent(), this._positionService = new Ie(this._textmodifier, this._overlayElement), this._positionService.bind(), this._switchFormat(this._currentFormat);
1683
+ }
1684
+ $dispose() {
1685
+ this._formatSelect.isMounted() && this._formatSelect.selectElement.removeEventListener("change", this._handleFormatSelectChange), this._exportButton.isMounted() && this._exportButton.buttonElement.removeEventListener("click", this._handleExportClickSafe), this._copyButton.isMounted() && this._copyButton.buttonElement.removeEventListener("click", this._handleCopyClickSafe);
1686
+ for (const t of this._eventUnsubscribers) t();
1687
+ this._eventUnsubscribers.length = 0, this._events.$clear();
1688
+ for (const t of this._formats.values()) t.blade.destroy();
1689
+ this._formats.clear(), this._currentBlade = void 0, this._overlayElement?.isConnected && this._overlayElement.remove(), this._positionService?.dispose();
1690
+ }
1691
+ _initializeFormatMap() {
1692
+ for (const t of this._definitions) {
1693
+ const e = t.createBlade();
1694
+ this._formats.set(t.format, { definition: t, blade: e, initialized: !1 });
1695
+ }
1696
+ }
1697
+ _createOverlay() {
1698
+ this._overlayElement = document.createElement("div"), this._overlayElement.dataset.plugin = "textmode-export-overlay", this._overlayElement.classList.add(v.root, v.stack), document.body.appendChild(this._overlayElement);
1699
+ }
1700
+ _renderStaticContent() {
1701
+ this._header.mount(this._overlayElement);
1702
+ const t = document.createElement("div");
1703
+ t.classList.add(v.section), this._overlayElement.appendChild(t), this._formatField.mount(t), this._prepareFormatOptions(), this._formatSelect.mount(this._formatField.root), this._formatSelect.selectElement.addEventListener("change", this._handleFormatSelectChange), this._optionsContainer = document.createElement("div"), this._optionsContainer.classList.add(v.stack, v.stackCompact), this._overlayElement.appendChild(this._optionsContainer), this._exportButton.mount(this._overlayElement), this._exportButton.buttonElement.addEventListener("click", this._handleExportClickSafe), this._copyButtonContainer = document.createElement("div"), this._copyButtonContainer.classList.add(v.stack, v.stackDense), this._overlayElement.appendChild(this._copyButtonContainer), this._copyButton.mount(this._copyButtonContainer), this._copyButton.buttonElement.dataset.defaultLabel = "copy to clipboard", this._copyButton.buttonElement.addEventListener("click", this._handleCopyClickSafe);
1704
+ }
1705
+ _prepareFormatOptions() {
1706
+ const t = this._definitions.map((e) => ({ value: e.format, label: e.label }));
1707
+ this._formatSelect.update({ options: t, defaultValue: this._currentFormat });
1708
+ }
1709
+ _registerEventHandlers() {
1710
+ this._eventUnsubscribers.push(this._events.$on("export:request", ({ format: t }) => {
1711
+ t === this._currentFormat && (this._state.$set({ isBusy: !0, error: void 0 }), this._updateExportButton());
1712
+ }), this._events.$on("export:success", ({ format: t }) => {
1713
+ if (t === this._currentFormat) {
1714
+ const e = { isBusy: !1 };
1715
+ t === "gif" && (e.gifProgress = void 0), t === "video" && (e.videoProgress = void 0), this._state.$set(e), this._updateExportButton();
1716
+ }
1717
+ }), this._events.$on("export:error", ({ format: t, error: e }) => {
1718
+ t === this._currentFormat && (this._state.$set({ isBusy: !1, error: e }), this._updateExportButton());
1719
+ }), this._events.$on("export:progress", ({ format: t, progress: e }) => {
1720
+ if (e) {
1721
+ if (t === "gif" && this._currentBlade?.blade instanceof ut) {
1722
+ const r = e;
1723
+ this._state.$set({ gifProgress: r }), this._currentBlade.blade.handleProgress(r);
1724
+ } else if (t === "video" && this._currentBlade?.blade instanceof ht) {
1725
+ const r = e;
1726
+ this._state.$set({ videoProgress: r }), this._currentBlade.blade.handleProgress(r);
1727
+ }
1728
+ this._updateExportButton();
1729
+ }
1730
+ }));
1731
+ }
1732
+ _handleFormatChange(t) {
1733
+ this._currentFormat = t, this._state.$set({ format: t }), this._switchFormat(t), this._events.$emit("format:change", { format: t });
1734
+ }
1735
+ _switchFormat(t) {
1736
+ const e = this._formats.get(t);
1737
+ if (!e) throw new Error(`Unknown export format: ${t}`);
1738
+ this._currentBlade && this._currentBlade.blade.unmount(), this._optionsContainer.innerHTML = "", e.blade.mount(this._optionsContainer), e.initialized || (e.blade.reset(), e.initialized = !0), this._currentBlade = e, this._formatSelect.value = t, this._updateCopyButtonState(), this._updateExportButton(), this._positionService?.scheduleUpdate();
1739
+ }
1740
+ _updateCopyButtonState() {
1741
+ const t = this._currentBlade?.definition.supportsClipboard ?? !1;
1742
+ this._copyButtonContainer.style.display = t ? "flex" : "none", this._copyButton.setDisabled(!t);
1743
+ }
1744
+ async _handleExportClick() {
1745
+ if (!this._currentBlade) return;
1746
+ const t = this._currentBlade.definition.format, e = this._currentBlade.blade.getOptions();
1747
+ if (this._currentBlade.blade.validate()) {
1748
+ if (t === "gif") {
1749
+ const r = this._currentBlade.blade;
1750
+ if (r.isRecording()) return;
1751
+ r.setRecordingState("recording");
1752
+ try {
1753
+ await this._exportService.$requestExport("gif", e, { onGIFProgress: (a) => {
1754
+ r.setRecordingState(a.state, a);
1755
+ } });
1756
+ } catch (a) {
1757
+ throw r.setRecordingState("error"), a;
1758
+ }
1759
+ return void window.setTimeout(() => {
1760
+ r.setRecordingState("idle"), this._updateExportButton();
1761
+ }, 1600);
1762
+ }
1763
+ if (t === "video") {
1764
+ const r = this._currentBlade.blade;
1765
+ if (r.isRecording()) return;
1766
+ r.setRecordingState("recording");
1767
+ try {
1768
+ await this._exportService.$requestExport("video", e, { onVideoProgress: (a) => {
1769
+ r.setRecordingState(a.state, a);
1770
+ } });
1771
+ } catch (a) {
1772
+ throw r.setRecordingState("error"), a;
1773
+ }
1774
+ return void window.setTimeout(() => {
1775
+ r.setRecordingState("idle"), this._updateExportButton();
1776
+ }, 1600);
1777
+ }
1778
+ this._exportButton.setDisabled(!0), this._exportButton.setLabel("exporting…");
1779
+ try {
1780
+ await this._exportService.$requestExport(t, e);
1781
+ } finally {
1782
+ this._exportButton.setDisabled(!1), this._exportButton.setLabel("download file");
1783
+ }
1784
+ } else console.warn("[textmode-export] Export options failed validation");
1785
+ }
1786
+ async _handleCopyClick() {
1787
+ if (!this._currentBlade || !this._currentBlade.definition.supportsClipboard) return;
1788
+ const t = this._currentBlade.definition.format, e = this._currentBlade.blade.getOptions(), r = this._copyButton.buttonElement.dataset.defaultLabel ?? "copy to clipboard";
1789
+ this._copyButton.setDisabled(!0), this._copyButton.setLabel("copying…");
1790
+ try {
1791
+ await this._clipboardService.$copy(t, e), this._copyButton.setLabel("copied!");
1792
+ } catch (a) {
1793
+ console.error("[textmode-export] Failed to copy to clipboard", a), this._copyButton.setLabel("copy failed!");
1794
+ } finally {
1795
+ window.setTimeout(() => {
1796
+ this._copyButton.setLabel(r), this._copyButton.setDisabled(!1);
1797
+ }, 1200);
1798
+ }
1799
+ }
1800
+ _updateExportButton() {
1801
+ if (!this._currentBlade) return;
1802
+ const t = this._currentBlade.definition.format;
1803
+ if (t === "gif" && this._currentBlade.blade instanceof ut) {
1804
+ const r = this._currentBlade.blade, a = this._state.snapshot.gifProgress;
1805
+ if (r.isRecording()) if (this._exportButton.setDisabled(!0), a?.totalFrames) {
1806
+ const i = a.frameIndex ?? 0;
1807
+ this._exportButton.setLabel(`recording ${i}/${a.totalFrames}`);
1808
+ } else this._exportButton.setLabel("recording…");
1809
+ else this._exportButton.setDisabled(!1), this._exportButton.setLabel("start recording");
1810
+ return;
1811
+ }
1812
+ if (t === "video" && this._currentBlade.blade instanceof ht) {
1813
+ const r = this._currentBlade.blade, a = this._state.snapshot.videoProgress;
1814
+ if (r.isRecording()) if (this._exportButton.setDisabled(!0), a?.totalFrames) {
1815
+ const i = a.frameIndex ?? 0;
1816
+ this._exportButton.setLabel(`recording ${i}/${a.totalFrames} frames`);
1817
+ } else this._exportButton.setLabel("recording…");
1818
+ else this._exportButton.setDisabled(!1), this._exportButton.setLabel("start recording");
1819
+ return;
1820
+ }
1821
+ const e = this._state.snapshot.isBusy;
1822
+ this._exportButton.setDisabled(e), this._exportButton.setLabel(e ? "exporting…" : "download file");
1823
+ }
1824
+ }
1825
+ class Te {
1826
+ _listeners = /* @__PURE__ */ new Map();
1827
+ $on(t, e) {
1828
+ this._listeners.has(t) || this._listeners.set(t, /* @__PURE__ */ new Set());
1829
+ const r = this._listeners.get(t);
1830
+ return r.add(e), () => {
1831
+ r.delete(e), r.size === 0 && this._listeners.delete(t);
1832
+ };
1833
+ }
1834
+ $emit(t, e) {
1835
+ const r = this._listeners.get(t);
1836
+ if (r) for (const a of [...r]) try {
1837
+ a(e);
1838
+ } catch (i) {
1839
+ console.error("[textmode-export] Event handler failed", i);
1840
+ }
1841
+ }
1842
+ $clear() {
1843
+ this._listeners.clear();
1844
+ }
1845
+ }
1846
+ class Re {
1847
+ _state;
1848
+ _listeners = /* @__PURE__ */ new Set();
1849
+ constructor(t) {
1850
+ this._state = t;
1851
+ }
1852
+ get snapshot() {
1853
+ return Object.freeze({ ...this._state });
1854
+ }
1855
+ $set(t) {
1856
+ this._state = Object.assign({}, this._state, t);
1857
+ const e = this.snapshot;
1858
+ for (const r of [...this._listeners]) try {
1859
+ r(e);
1860
+ } catch (a) {
1861
+ console.error("[textmode-export] State listener failed", a);
1862
+ }
1863
+ }
1864
+ $subscribe(t) {
1865
+ return this._listeners.add(t), t(this.snapshot), () => {
1866
+ this._listeners.delete(t);
1867
+ };
1868
+ }
1869
+ $reset(t) {
1870
+ this._state = t;
1871
+ const e = this.snapshot;
1872
+ for (const r of [...this._listeners]) r(e);
1873
+ }
1874
+ }
1875
+ const Ue = (o) => ({ format: o, isBusy: !1 });
1876
+ class Gt extends O {
1877
+ props;
1878
+ checkbox;
1879
+ labelElement;
1880
+ handleChange = () => {
1881
+ this.props.defaultChecked = this.checkbox.checked;
1882
+ };
1883
+ constructor(t) {
1884
+ super(), this.props = t;
1885
+ }
1886
+ render() {
1887
+ this.labelElement = document.createElement("label"), this.labelElement.classList.add(v.checkbox), this.checkbox = document.createElement("input"), this.checkbox.type = "checkbox", this.props.id && (this.checkbox.id = this.props.id), this.checkbox.checked = !!this.props.defaultChecked, this.checkbox.addEventListener("change", this.handleChange);
1888
+ const t = document.createElement("span");
1889
+ return t.textContent = this.props.label, this.labelElement.htmlFor = this.props.id ?? "", this.labelElement.appendChild(this.checkbox), this.labelElement.appendChild(t), this.labelElement;
1890
+ }
1891
+ get inputElement() {
1892
+ return this.checkbox;
1893
+ }
1894
+ get checked() {
1895
+ return this.checkbox.checked;
1896
+ }
1897
+ set checked(t) {
1898
+ this.checkbox.checked = t;
1899
+ }
1900
+ _onUnmount() {
1901
+ this.checkbox.removeEventListener("change", this.handleChange);
1902
+ }
1903
+ onUpdate(t) {
1904
+ this.props = t, t.id && (this.checkbox.id = t.id, this.labelElement.htmlFor = t.id), this.checkbox.checked = !!t.defaultChecked, this.labelElement.lastElementChild && (this.labelElement.lastElementChild.textContent = t.label);
1905
+ }
1906
+ }
1907
+ class Ne extends O {
1908
+ props;
1909
+ input;
1910
+ handleInput = () => {
1911
+ this.props.defaultValue = this.input.value;
1912
+ };
1913
+ constructor(t) {
1914
+ super(), this.props = t;
1915
+ }
1916
+ render() {
1917
+ return this.input = document.createElement("input"), this.input.type = "text", this.input.classList.add(v.input), this.props.id && (this.input.id = this.props.id), this.props.maxLength !== void 0 && (this.input.maxLength = this.props.maxLength), this.props.defaultValue !== void 0 && (this.input.value = this.props.defaultValue), this.input.addEventListener("input", this.handleInput), this.input;
1918
+ }
1919
+ get inputElement() {
1920
+ return this.input;
1921
+ }
1922
+ get value() {
1923
+ return this.input.value;
1924
+ }
1925
+ set value(t) {
1926
+ this.input.value = t;
1927
+ }
1928
+ _onUnmount() {
1929
+ this.input.removeEventListener("input", this.handleInput);
1930
+ }
1931
+ onUpdate(t) {
1932
+ this.props = t, t.id && (this.input.id = t.id), t.maxLength !== void 0 && (this.input.maxLength = t.maxLength), t.defaultValue !== void 0 && (this.input.value = t.defaultValue);
1933
+ }
1934
+ }
1935
+ class Ge extends Y {
1936
+ trailingSpaces = this._manageComponent(new Gt({ label: "preserve trailing spaces", defaultChecked: !1 }));
1937
+ emptyCharacter = this._manageComponent(new Ne({ id: "textmode-export-empty-character", defaultValue: " ", maxLength: 1 }));
1938
+ constructor(t) {
1939
+ super(t);
1940
+ }
1941
+ render() {
1942
+ const t = document.createElement("div");
1943
+ t.classList.add(v.stack), this.trailingSpaces.mount(t);
1944
+ const e = new P({ label: "empty character", labelFor: "textmode-export-empty-character", variant: "full" });
1945
+ return e.mount(t), this.emptyCharacter.mount(e.root), t;
1946
+ }
1947
+ getOptions() {
1948
+ const t = this._config.defaultOptions ?? {}, e = this.emptyCharacter.value || t.emptyCharacter || " ";
1949
+ return { preserveTrailingSpaces: this.trailingSpaces.checked, emptyCharacter: e };
1950
+ }
1951
+ reset() {
1952
+ this.applyDefaults();
1953
+ }
1954
+ validate() {
1955
+ return this.emptyCharacter.value.length <= 1;
1956
+ }
1957
+ applyDefaults() {
1958
+ const t = this._config.defaultOptions ?? {};
1959
+ this.trailingSpaces.checked = t.preserveTrailingSpaces ?? !1, this.emptyCharacter.value = t.emptyCharacter ?? " ";
1960
+ }
1961
+ }
1962
+ class We extends Y {
1963
+ formatSelect = this._manageComponent(new mt({ id: "textmode-export-image-format", options: [{ value: "png", label: `PNG (${lt.png})` }, { value: "jpg", label: `JPG (${lt.jpg})` }, { value: "webp", label: `WEBP (${lt.webp})` }], defaultValue: "png" }));
1964
+ scaleInput = this._manageComponent(new G({ defaultValue: "1", attributes: { min: "0.1", step: "0.1" } }));
1965
+ constructor(t) {
1966
+ super(t);
1967
+ }
1968
+ render() {
1969
+ const t = document.createElement("div");
1970
+ t.classList.add(v.stack);
1971
+ const e = new H("row");
1972
+ e.mount(t);
1973
+ const r = new P({ label: "image format", labelFor: "textmode-export-image-format", variant: "compact" });
1974
+ r.mount(e.root), this.formatSelect.mount(r.root);
1975
+ const a = new P({ label: "scale", labelFor: "textmode-export-image-scale", variant: "dense" });
1976
+ return a.mount(e.root), this.scaleInput.mount(a.root), this.scaleInput.inputElement.id = "textmode-export-image-scale", t;
1977
+ }
1978
+ getOptions() {
1979
+ const t = Number.parseFloat(this.scaleInput.value);
1980
+ return { format: this.formatSelect.value, scale: Number.isFinite(t) ? t : this._config.defaultOptions?.scale ?? 1 };
1981
+ }
1982
+ reset() {
1983
+ this.applyDefaults(), this.scaleInput.refresh();
1984
+ }
1985
+ validate() {
1986
+ const t = Number.parseFloat(this.scaleInput.value);
1987
+ return Number.isFinite(t) && t > 0;
1988
+ }
1989
+ applyDefaults() {
1990
+ const t = this._config.defaultOptions ?? {};
1991
+ this.formatSelect.value = t.format ?? "png";
1992
+ const e = t.scale ?? 1;
1993
+ this.scaleInput.value = String(e);
1994
+ }
1995
+ }
1996
+ class qe extends Y {
1997
+ includeBackground = this._manageComponent(new Gt({ id: "textmode-export-svg-include-backgrounds", label: "include cell backgrounds", defaultChecked: !0 }));
1998
+ drawMode = this._manageComponent(new mt({ id: "textmode-export-svg-draw-mode", options: [{ value: "fill", label: "fill" }, { value: "stroke", label: "stroke" }], defaultValue: "fill" }));
1999
+ strokeWidth = this._manageComponent(new G({ defaultValue: "1", attributes: { min: "0", step: "0.1" } }));
2000
+ constructor(t) {
2001
+ super(t);
2002
+ }
2003
+ render() {
2004
+ const t = document.createElement("div");
2005
+ t.classList.add(v.stack), this.includeBackground.mount(t);
2006
+ const e = new H("row");
2007
+ e.mount(t);
2008
+ const r = new P({ label: "draw mode", labelFor: "textmode-export-svg-draw-mode", variant: "compact" });
2009
+ r.mount(e.root), this.drawMode.mount(r.root);
2010
+ const a = new P({ label: "stroke width", labelFor: "textmode-export-svg-stroke-width", variant: "compact" });
2011
+ return a.mount(e.root), this.strokeWidth.mount(a.root), this.strokeWidth.inputElement.id = "textmode-export-svg-stroke-width", this.drawMode.selectElement.addEventListener("change", () => this.updateStrokeControls()), this.updateStrokeControls(), t;
2012
+ }
2013
+ getOptions() {
2014
+ return { includeBackgroundRectangles: this.includeBackground.checked, drawMode: this.drawMode.value, strokeWidth: Number.parseFloat(this.strokeWidth.value) };
2015
+ }
2016
+ reset() {
2017
+ this.applyDefaults(), this.updateStrokeControls();
2018
+ }
2019
+ validate() {
2020
+ const t = Number.parseFloat(this.strokeWidth.value);
2021
+ return this.drawMode.value !== "stroke" || Number.isFinite(t) && t >= 0;
2022
+ }
2023
+ updateStrokeControls() {
2024
+ const t = this.drawMode.value === "stroke";
2025
+ this.strokeWidth.inputElement.disabled = !t, this.strokeWidth.refresh();
2026
+ }
2027
+ applyDefaults() {
2028
+ const t = this._config.defaultOptions ?? {};
2029
+ this.includeBackground.checked = t.includeBackgroundRectangles ?? !0, this.drawMode.value = t.drawMode ?? "fill";
2030
+ const e = t.strokeWidth ?? 1;
2031
+ this.strokeWidth.value = String(e), this.strokeWidth.refresh();
2032
+ }
2033
+ }
2034
+ const ze = [{ format: "txt", label: "plain text (.txt)", supportsClipboard: !0, createBlade: () => new Ge({ format: "txt", label: "plain text (.txt)", supportsClipboard: !0, defaultOptions: { preserveTrailingSpaces: !1, emptyCharacter: " " } }) }, { format: "image", label: "image (.png / .jpg / .webp)", supportsClipboard: !0, createBlade: () => new We({ format: "image", label: "image (.png / .jpg / .webp)", supportsClipboard: !0, defaultOptions: { format: "png", scale: 1 } }) }, { format: "svg", label: "vector (.svg)", supportsClipboard: !0, createBlade: () => new qe({ format: "svg", label: "vector (.svg)", supportsClipboard: !0, defaultOptions: { includeBackgroundRectangles: !0, drawMode: "fill", strokeWidth: 1 } }) }, { format: "gif", label: "animated GIF (.gif)", supportsClipboard: !1, createBlade: () => new ut({ format: "gif", label: "animated GIF (.gif)", supportsClipboard: !1, defaultOptions: { frameCount: 300, frameRate: 60, scale: 1, repeat: 0 } }) }, { format: "video", label: "video (.webm)", supportsClipboard: !1, createBlade: () => new ht({ format: "video", label: "video (.webm)", supportsClipboard: !1, defaultOptions: { frameCount: 480, frameRate: 60, quality: 0.7 } }) }];
2035
+ function je() {
2036
+ return ze;
2037
+ }
2038
+ function He(o, t) {
2039
+ const e = je(), r = e[0]?.format, a = new Re(Ue(r)), i = new Te(), n = new Oe(o, t, a, i, e);
2040
+ return n.$mount(), n;
2041
+ }
2042
+ const Qe = (o = {}) => {
2043
+ const t = o.overlay ?? !0;
2044
+ let e;
2045
+ return { name: "textmode-export", version: "1.0.0", async install(r, a) {
2046
+ const i = { saveCanvas: async (n = {}) => (a.flushDrawCommands(), new vt().$saveImage(r.canvas, n)), copyCanvas: async (n = {}) => (a.flushDrawCommands(), new vt().$copyImageToClipboard(r.canvas, n)), saveSVG: (n = {}) => {
2047
+ a.flushDrawCommands(), new gt().$saveSVG(r, n);
2048
+ }, saveStrings: (n = {}) => {
2049
+ a.flushDrawCommands(), new bt().$saveTXT(r, n);
2050
+ }, toSVG: (n = {}) => (a.flushDrawCommands(), new gt().$generateSVG(r, n)), toString: (n = {}) => (a.flushDrawCommands(), new bt().$generateTXT(r, n)), saveGIF: async (n = {}) => (a.flushDrawCommands(), new me(r, a.registerPostDrawHook).$saveGIF(n)), saveWEBM: async (n = {}) => (a.flushDrawCommands(), new Ce(r, a.registerPostDrawHook).$saveWEBM(n)) };
2051
+ Object.assign(r, i), t && (e = He(r, i));
2052
+ }, async uninstall(r) {
2053
+ e?.$dispose(), e = void 0;
2054
+ const a = ["saveCanvas", "copyCanvas", "saveSVG", "saveStrings", "toSVG", "toString", "saveGIF", "saveWEBM"];
2055
+ for (const i of a) delete r[i];
2056
+ } };
2057
+ };
2058
+ typeof window < "u" && (window.createTextmodeExportPlugin = Qe);
2059
+ export {
2060
+ Qe as createTextmodeExportPlugin
2061
+ };