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,3 @@
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
+ (function(Q,j){typeof exports=="object"&&typeof module<"u"?j(exports):typeof define=="function"&&define.amd?define(["exports"],j):j((Q=typeof globalThis<"u"?globalThis:Q||self).TextmodeExport={})})(this,function(Q){"use strict";class j{$extractFramebufferData(t){const e=t.readPixels(0),i=t.readPixels(1),r=t.readPixels(2),s=t.readPixels(3);return{characterPixels:e,primaryColorPixels:i,secondaryColorPixels:r,transformPixels:t.readPixels(4),rotationPixels:s}}$getCharacterIndex(t,e){return t[e]+(t[e+1]<<8)}}class X{$downloadFile(t,e){try{const i=this._sanitizeFilename(e),r=URL.createObjectURL(t),s=document.createElement("a");s.href=r,s.download=i,s.style.display="none",s.rel="noopener",document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(r)}catch(i){console.error("[textmode-export] Failed to download file:",i)}}_sanitizeFilename(t){if(!t)return this._generateDefaultFilename();const e=t.trim();return e?e.replace(/[<>:"/\\|?*]/g,"_").replace(/\s+/g,"_").replace(/_{2,}/g,"_").replace(/^_+|_+$/g,"").substring(0,255)||this._generateDefaultFilename():this._generateDefaultFilename()}_generateDefaultFilename(){return`textmode-export-${new Date().toISOString().slice(0,19).replace(/:/g,"-")}`}}function bt(c,t){return{r:c[t],g:c[t+1],b:c[t+2],a:c[t+3]}}class ne extends j{_extractTransformData(t,e,i){const r=t[i]===255,s=t[i+1]===255,n=t[i+2]===255,u=e[i]+e[i+1]/255;return{isInverted:r,flipHorizontal:s,flipVertical:n,rotation:Math.round(360*u/255*100)/100}}_calculateCellPosition(t,e,i){return{x:t,y:e,cellX:t*i.cellWidth,cellY:e*i.cellHeight}}$extractSVGCellData(t,e){const i=[];let r=0;for(let s=0;s<e.rows;s++)for(let n=0;n<e.cols;n++){const u=4*r,l=this.$getCharacterIndex(t.characterPixels,u);let p=bt(t.primaryColorPixels,u),a=bt(t.secondaryColorPixels,u);const o=this._extractTransformData(t.transformPixels,t.rotationPixels,u);if(o.isInverted){const m=p;p=a,a=m}const d=this._calculateCellPosition(n,s,e);i.push({charIndex:l,primaryColor:p,secondaryColor:a,transform:o,position:d}),r++}return i}}class oe{_createGlyphPath(t,e,i,r,s){const n=s/t.head.unitsPerEm;return{getBoundingBox:()=>({x1:i+e.xMin*n,y1:r+-e.yMax*n,x2:i+e.xMax*n,y2:r+-e.yMin*n}),toSVG:()=>this._glyphToSVGPath(e,i,r,n)}}_glyphToSVGPath(t,e,i,r){if(!t||!t.xs)return"";const{xs:s,ys:n,endPts:u,flags:l}=t;if(!(s&&n&&u&&l))return"";let p="",a=0;for(let o=0;o<u.length;o++){const d=u[o];if(!(d<a)){if(d>=a){const m=e+s[a]*r,y=i-n[a]*r;p+=`M${m.toFixed(2)},${y.toFixed(2)}`;let h=a+1;for(;h<=d;)if(1&l[h]){const v=e+s[h]*r,E=i-n[h]*r;p+=`L${v.toFixed(2)},${E.toFixed(2)}`,h++}else{const v=e+s[h]*r,E=i-n[h]*r;let w=h+1>d?a:h+1;if(1&l[w]){const g=e+s[w]*r,B=i-n[w]*r;p+=`Q${v.toFixed(2)},${E.toFixed(2)} ${g.toFixed(2)},${B.toFixed(2)}`,h=w+1}else{const g=(v+(e+s[w]*r))/2,B=(E+(i-n[w]*r))/2;p+=`Q${v.toFixed(2)},${E.toFixed(2)} ${g.toFixed(2)},${B.toFixed(2)}`,h=w}}p+="Z"}a=d+1}}return p}_generateCharacterPath(t,e,i,r,s){const n=t.codePointAt(0)??0,u=e.getGlyphData(n);return u?this._createGlyphPath(e.font,u,i,r,s):null}$generatePositionedCharacterPath(t,e,i,r,s,n,u,l){const p=i+(s-l*(u/e.font.head.unitsPerEm))/2,a=r+(n+.7*u)/2,o=this._generateCharacterPath(t,e,p,a,u);return o&&o.toSVG()||null}}class le{_pathGenerator;constructor(){this._pathGenerator=new oe}$generateSVGHeader(t){const{width:e,height:i}=t;return`<?xml version="1.0" encoding="UTF-8"?><svg width="${e}" height="${i}" viewBox="0 0 ${e} ${i}" xmlns="http://www.w3.org/2000/svg"><title>textmode.js sketch</title>`}$generateSVGFooter(){return"</g></svg>"}_generateTransformAttribute(t,e){const{transform:i,position:r}=t;if(!i.flipHorizontal&&!i.flipVertical&&!i.rotation)return"";const s=r.cellX+e.cellWidth/2,n=r.cellY+e.cellHeight/2,u=[];if(i.flipHorizontal||i.flipVertical){const l=i.flipHorizontal?-1:1,p=i.flipVertical?-1:1;u.push(`translate(${s} ${n})scale(${l} ${p})translate(${-s} ${-n})`)}return i.rotation&&u.push(`rotate(${i.rotation} ${s} ${n})`),` transform="${u.join(" ")}"`}_generateCellBackground(t,e,i){if(!i.includeBackgroundRectangles||t.secondaryColor.a===0)return"";const{position:r}=t,{r:s,g:n,b:u,a:l}=t.secondaryColor,p=`rgba(${s},${n},${u},${l/255})`;return i.drawMode==="stroke"?`<rect x="${r.cellX}" y="${r.cellY}" width="${e.cellWidth}" height="${e.cellHeight}" stroke="${p}" fill="none" stroke-width="${i.strokeWidth}"/>`:`<rect x="${r.cellX}" y="${r.cellY}" width="${e.cellWidth}" height="${e.cellHeight}" fill="${p}"/>`}_generateCharacterPath(t,e,i,r){const s=i.characters[t.charIndex];if(!s)return"";const n=this._pathGenerator.$generatePositionedCharacterPath(s.character,i,t.position.cellX,t.position.cellY,e.cellWidth,e.cellHeight,i.fontSize,s.advanceWidth);if(!n)return"";const{r:u,g:l,b:p,a}=t.primaryColor,o=`rgba(${u},${l},${p},${a/255})`;return r.drawMode==="stroke"?`<path d="${n}" stroke="${o}" stroke-width="${r.strokeWidth}" fill="none"/>`:`<path d="${n}" fill="${o}"/>`}$generateCellContent(t,e,i,r){const s=[],n=this._generateCellBackground(t,e,r);n&&s.push(n);const u=this._generateCharacterPath(t,e,i,r);if(u){const l=this._generateTransformAttribute(t,e);s.push(l?`<g${l}>${u}</g>`:u)}return s.join("")}$generateSVGContent(t,e,i,r){const s=[this.$generateSVGHeader(e),'<g id="ascii-cells">'];for(const n of t)s.push(this.$generateCellContent(n,e,i,r));return s.push(this.$generateSVGFooter()),s.join("")}$optimizeSVGContent(t){return t.replace(/<path[^>]*d=""[^>]*\/>/g,"").replace(/\s+/g," ").replace(/> </g,"><")}}class wt{_applyDefaultOptions(t){return{includeBackgroundRectangles:t.includeBackgroundRectangles??!0,drawMode:t.drawMode??"fill",strokeWidth:t.strokeWidth??1,filename:t.filename}}$generateSVG(t,e={}){const i=new ne,r=new le,s=i.$extractSVGCellData(i.$extractFramebufferData(t.drawFramebuffer),t.grid),n=r.$generateSVGContent(s,t.grid,t.font,this._applyDefaultOptions(e));return r.$optimizeSVGContent(n)}$saveSVG(t,e={}){new X().$downloadFile(new Blob([this.$generateSVG(t,e)],{type:"image/svg+xml;charset=utf-8"}),e.filename)}}const nt={png:"image/png",jpg:"image/jpeg",webp:"image/webp"},de=".png",ce=".jpg",ue=".webp";class yt{_applyDefaultOptions(t){return{format:t.format??"png",scale:Math.abs(t.scale??1),filename:t.filename}}_validateOptions(t){if(!(t.format in nt))throw new Error(`Saving '${t.format}' files is not supported`)}async $generateImageBlob(t,e){const i=t,r=document.createElement("canvas"),s=r.getContext("2d"),n=Math.round(i.width*e.scale),u=Math.round(i.height*e.scale);return r.width=n,r.height=u,s.imageSmoothingEnabled=!1,s.clearRect(0,0,n,u),s.drawImage(i,0,0,i.width,i.height,0,0,n,u),await new Promise((l,p)=>{r.toBlob(a=>{a?l(a):p(new Error(`Failed to generate ${e.format.toUpperCase()} blob`))},nt[e.format])})}async $saveImage(t,e={}){const i=this._applyDefaultOptions(e);this._validateOptions(i);const r=await this.$generateImageBlob(t,i);new X().$downloadFile(r,i.filename)}async $copyImageToClipboard(t,e={}){if(typeof navigator>"u"||!navigator.clipboard||typeof navigator.clipboard.write!="function")throw new Error("Clipboard API is not available in this environment");const i=this._applyDefaultOptions(e);this._validateOptions(i);const r=await this.$generateImageBlob(t,i),s=new ClipboardItem({[nt[i.format]]:r});await navigator.clipboard.write([s])}}class xt{_applyDefaultOptions(t){return{preserveTrailingSpaces:t.preserveTrailingSpaces??!1,emptyCharacter:t.emptyCharacter??" ",filename:t.filename}}_createTXTContent(t,e){const i=new j;let r=i.$extractFramebufferData(t.drawFramebuffer);const s=[];let n=0;for(let l=0;l<t.grid.rows;l++){const p=[];for(let a=0;a<t.grid.cols;a++){const o=4*n,d=i.$getCharacterIndex(r.characterPixels,o),m=t.font.characters[d]?.character||e.emptyCharacter;p.push(m),n++}s.push(p)}const u=[];for(const l of s){let p=l.join("");e.preserveTrailingSpaces||(p=p.replace(/\s+$/,"")),u.push(p)}return u.join(`
3
+ `)}$generateTXT(t,e={}){return this._createTXTContent(t,this._applyDefaultOptions(e))}$saveTXT(t,e={}){const i=this._applyDefaultOptions(e),r=this._createTXTContent(t,i),s=new Blob([r],{type:"text/plain;charset=utf-8"});new X().$downloadFile(s,i.filename)}}class he{_canvas;_ctx;async $record(t,e,i,r){const s=Math.max(1,Math.round(e.frameRate)),n=Math.round(1e3/s),u=Math.max(1,Math.round(e.frameCount));return await new Promise((l,p)=>{const a=[];let o,d=0,m=!1;const y=()=>{o&&(o(),o=void 0)},h=E=>{m||(m=!0,y(),l(E))},v=()=>{if(d>=u)h(a);else try{const E=e.scale,w=Math.max(1,Math.round(t.width*E)),g=Math.max(1,Math.round(t.height*E));this._canvas||(this._canvas=document.createElement("canvas")),this._canvas.width===w&&this._canvas.height===g||(this._canvas.width=w,this._canvas.height=g),this._ctx||(this._ctx=this._canvas.getContext("2d"));const B=this._ctx;B.imageSmoothingEnabled=!1,B.clearRect(0,0,w,g),B.drawImage(t,0,0,t.width,t.height,0,0,w,g);const $={imageData:B.getImageData(0,0,w,g),width:w,height:g,delayMs:n};a.push($),d+=1,r?.({state:"recording",frameIndex:d,totalFrames:u}),d>=u&&h(a)}catch(E){(w=>{if(m)return;m=!0,y();const g=w instanceof Error?w.message:"GIF recording failed";r?.({state:"error",message:g}),p(w instanceof Error?w:new Error(String(w)))})(E)}};o=i(()=>{v()}),r?.({state:"recording",frameIndex:0,totalFrames:u})})}}var _t,tt,Ct=Object.defineProperty,pe=(_t=c=>{function t(o,d,m){return .29889531*o+.58662247*d+.11448223*m}function e(o,d,m){return .59597799*o-.2741761*d-.32180189*m}function i(o,d,m){return .21147017*o-.52261711*d+.31114694*m}function r(o,d){let m=o[0]-d[0],y=o[1]-d[1],h=o[2]-d[2],v=s(o)-s(d);return m*m*.5053+y*y*.299+h*h*.1957+v*v}function s(o){return o[3]!=null?o[3]:255}function n(o,d){return Math.sqrt(r(o,d))}function u(o,d){let[m,y,h]=o,[v,E,w]=d,g=t(m,y,h)-t(v,E,w),B=e(m,y,h)-e(v,E,w),$=i(m,y,h)-i(v,E,w),I=s(o)-s(d);return g*g*.5053+B*B*.299+$*$*.1957+I*I}function l(o,d){return Math.sqrt(u(o,d))}function p(o,d){var m,y=0;for(m=0;m<o.length;m++){let h=o[m]-d[m];y+=h*h}return y}function a(o,d){return Math.sqrt(p(o,d))}Ct(c,"__esModule",{value:!0}),((o,d)=>{for(var m in d)Ct(o,m,{get:d[m],enumerable:!0})})(c,{colorDifferenceRGBToYIQ:()=>l,colorDifferenceRGBToYIQSquared:()=>u,colorDifferenceYIQ:()=>n,colorDifferenceYIQSquared:()=>r,euclideanDistance:()=>a,euclideanDistanceSquared:()=>p})},()=>(tt||_t((tt={exports:{}}).exports,tt),tt.exports)),me=59;function Et(c=256){let t=0,e=new Uint8Array(c);return{get buffer(){return e.buffer},reset(){t=0},bytesView:()=>e.subarray(0,t),bytes:()=>e.slice(0,t),writeByte(r){i(t+1),e[t]=r,t++},writeBytes(r,s=0,n=r.length){i(t+n);for(let u=0;u<n;u++)e[t++]=r[u+s]},writeBytesView(r,s=0,n=r.byteLength){i(t+n),e.set(r.subarray(s,s+n),t),t+=n}};function i(r){var s=e.length;if(s>=r)return;r=Math.max(r,s*(s<1048576?2:1.125)>>>0),s!=0&&(r=Math.max(r,256));let n=e;e=new Uint8Array(r),t>0&&e.set(n.subarray(0,t),0)}}var fe=[0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535],ge=function(c,t,e,i,r,s,n,u){r=r||Et(512),s=s||new Uint8Array(256),n=n||new Int32Array(5003),u=u||new Int32Array(5003);let l=n.length,p=Math.max(2,i);s.fill(0),u.fill(0),k();let a=0,o=0,d=p+1,m=d,y=!1,h=m,v=S(h),E=1<<d-1,w=E+1,g=E+2,B=0,$=e[0],I=0;for(let b=l;b<65536;b*=2)++I;I=8-I,r.writeByte(p),V(E);for(let b=1;b<e.length;b++)L(e[b]);return V($),V(w),r.writeByte(0),r.bytesView();function C(b){s[B++]=b,B>=254&&_()}function k(){n.fill(-1)}function L(b){let D=(b<<12)+$,f=b<<I^$;if(n[f]===D)$=u[f];else{if(n[f]>=0){let x=f===0?1:l-f;do if(f-=x,f<0&&(f+=l),n[f]===D)return void($=u[f]);while(n[f]>=0)}V($),$=b,g<4096?(u[f]=g++,n[f]=D):(k(),g=E+2,y=!0,V(E))}}function _(){B>0&&(r.writeByte(B),r.writeBytesView(s,0,B),B=0)}function S(b){return(1<<b)-1}function V(b){for(a&=fe[o],o>0?a|=b<<o:a=b,o+=h;o>=8;)C(255&a),a>>=8,o-=8;if((g>v||y)&&(y?(h=m,v=S(h),y=!1):(++h,v=h==12?4096:S(h))),b==w){for(;o>0;)C(255&a),a>>=8,o-=8;_()}}};function Bt(c,t,e){return c<<8&63488|t<<2&992|e>>3&31}function It(c,t,e,i){return c>>4|240&t|(240&e)<<4|(240&i)<<8}function Ft(c,t,e){return c>>4<<8|t>>4<<4|e>>4}function et(c,t,e){return c<t?t:c>e?e:c}function it(c){return c*c}function kt(c,t,e){for(var i=0,r=1e100,s=c[t],n=s.cnt,u=(s.ac,s.rc),l=s.gc,p=s.bc,a=s.fw;a!=0;a=c[a].fw){var o=c[a].cnt,d=n*o/(n+o);if(!(d>=r)){var m=0;!((m+=d*it(c[a].rc-u))>=r)&&!((m+=d*it(c[a].gc-l))>=r)&&!((m+=d*it(c[a].bc-p))>=r)&&(r=m,i=a)}}s.err=r,s.nn=i}function ot(){return{ac:0,rc:0,gc:0,bc:0,cnt:0,nn:0,fw:0,bk:0,tm:0,mtm:0,err:0}}function ve(c,t,e){let{format:i="rgb565",clearAlpha:r=!0,clearAlphaColor:s=0,clearAlphaThreshold:n=0,oneBitAlpha:u=!1}=e||{},l=e.useSqrt!==!1,p=i==="rgba4444",a=(function(b,D){let f=new Array(D==="rgb444"?4096:65536),x=b.length;if(D==="rgba4444")for(let M=0;M<x;++M){let P=b[M],A=P>>24&255,O=P>>16&255,F=P>>8&255,T=255&P,U=It(T,F,O,A),W=U in f?f[U]:f[U]=ot();W.rc+=T,W.gc+=F,W.bc+=O,W.ac+=A,W.cnt++}else if(D==="rgb444")for(let M=0;M<x;++M){let P=b[M],A=P>>16&255,O=P>>8&255,F=255&P,T=Ft(F,O,A),U=T in f?f[T]:f[T]=ot();U.rc+=F,U.gc+=O,U.bc+=A,U.cnt++}else for(let M=0;M<x;++M){let P=b[M],A=P>>16&255,O=P>>8&255,F=255&P,T=Bt(F,O,A),U=T in f?f[T]:f[T]=ot();U.rc+=F,U.gc+=O,U.bc+=A,U.cnt++}return f})(c,i),o=a.length,d=o-1,m=new Uint32Array(o+1);for(var y=0,h=0;h<a.length;++h){let b=a[h];if(b!=null){var v=1/b.cnt;p&&(b.ac*=v),b.rc*=v,b.gc*=v,b.bc*=v,a[y++]=b}}for(it(t)/y<.022&&(l=!1),h=0;h<y-1;++h)a[h].fw=h+1,a[h+1].bk=h,l&&(a[h].cnt=Math.sqrt(a[h].cnt));var E,w,g;for(l&&(a[h].cnt=Math.sqrt(a[h].cnt)),h=0;h<y;++h){kt(a,h);var B=a[h].err;for(w=++m[0];w>1&&!(a[E=m[g=w>>1]].err<=B);w=g)m[w]=E;m[w]=h}var $=y-t;for(h=0;h<$;){for(var I;;){var C=m[1];if((I=a[C]).tm>=I.mtm&&a[I.nn].mtm<=I.tm)break;for(I.mtm==d?C=m[1]=m[m[0]--]:(kt(a,C),I.tm=h),B=a[C].err,w=1;(g=w+w)<=m[0]&&(g<m[0]&&a[m[g]].err>a[m[g+1]].err&&g++,!(B<=a[E=m[g]].err));w=g)m[w]=E;m[w]=C}var k=a[I.nn],L=I.cnt,_=k.cnt;v=1/(L+_),p&&(I.ac=v*(L*I.ac+_*k.ac)),I.rc=v*(L*I.rc+_*k.rc),I.gc=v*(L*I.gc+_*k.gc),I.bc=v*(L*I.bc+_*k.bc),I.cnt+=k.cnt,I.mtm=++h,a[k.bk].fw=k.fw,a[k.fw].bk=k.bk,k.mtm=d}let S=[];var V=0;for(h=0;;++V){let b=et(Math.round(a[h].rc),0,255),D=et(Math.round(a[h].gc),0,255),f=et(Math.round(a[h].bc),0,255),x=255;p&&(x=et(Math.round(a[h].ac),0,255),u&&(x=x<=(typeof u=="number"?u:127)?0:255),r&&x<=n&&(b=D=f=s,x=0));let M=p?[b,D,f,x]:[b,D,f];if(be(S,M)||S.push(M),(h=a[h].fw)==0)break}return S}function be(c,t){for(let e=0;e<c.length;e++){let i=c[e],r=i[0]===t[0]&&i[1]===t[1]&&i[2]===t[2],s=!(i.length>=4&&t.length>=4)||i[3]===t[3];if(r&&s)return!0}return!1}function we(c,t,e,i,r){let s=0,n=1e100;for(let u=0;u<r.length;u++){let l=r[u],p=l[0],a=l[1],o=l[2],d=H(l[3]-i);d>n||(d+=H(p-c),!(d>n)&&(d+=H(a-t),!(d>n)&&(d+=H(o-e),!(d>n)&&(n=d,s=u))))}return s}function ye(c,t,e,i){let r=0,s=1e100;for(let n=0;n<i.length;n++){let u=i[n],l=u[0],p=u[1],a=u[2],o=H(l-c);o>s||(o+=H(p-t),!(o>s)&&(o+=H(a-e),!(o>s)&&(s=o,r=n)))}return r}function H(c){return c*c}function xe(c={}){let{initialCapacity:t=4096,auto:e=!0}=c,i=Et(t),r=new Uint8Array(256),s=new Int32Array(5003),n=new Int32Array(5003),u=!1;return{reset(){i.reset(),u=!1},finish(){i.writeByte(me)},bytes:()=>i.bytes(),bytesView:()=>i.bytesView(),get buffer(){return i.buffer},get stream(){return i},writeHeader:l,writeFrame(p,a,o,d={}){let{transparent:m=!1,transparentIndex:y=0,delay:h=0,palette:v=null,repeat:E=0,colorDepth:w=8,dispose:g=-1}=d,B=!1;if(e?u||(B=!0,l(),u=!0):B=!!d.first,a=Math.max(0,Math.floor(a)),o=Math.max(0,Math.floor(o)),B){if(!v)throw new Error("First frame must include a { palette } option");(function(C,k,L,_,S=8){let V=1,b=0,D=lt(_.length)-1,f=V<<7|S-1<<4|b<<3|D;z(C,k),z(C,L),C.writeBytes([f,0,0])})(i,a,o,v,w),Mt(i,v),E>=0&&(function(C,k){C.writeByte(33),C.writeByte(255),C.writeByte(11),St(C,"NETSCAPE2.0"),C.writeByte(3),C.writeByte(1),z(C,k),C.writeByte(0)})(i,E)}let $=Math.round(h/10);(function(C,k,L,_,S){var V,b;C.writeByte(33),C.writeByte(249),C.writeByte(4),S<0&&(S=0,_=!1),_?(V=1,b=2):(V=0,b=0),k>=0&&(b=7&k),b<<=2,C.writeByte(0|b|0|V),z(C,L),C.writeByte(S||0),C.writeByte(0)})(i,g,$,m,y);let I=!!v&&!B;(function(C,k,L,_){if(C.writeByte(44),z(C,0),z(C,0),z(C,k),z(C,L),_){let S=0,V=0,b=lt(_.length)-1;C.writeByte(128|S|V|b)}else C.writeByte(0)})(i,a,o,I?v:null),I&&Mt(i,v),(function(C,k,L,_,S=8,V,b,D){ge(L,_,k,S,C,V,b,D)})(i,p,a,o,w,r,s,n)}};function l(){St(i,"GIF89a")}}function Mt(c,t){let e=1<<lt(t.length);for(let i=0;i<e;i++){let r=[0,0,0];i<t.length&&(r=t[i]),c.writeByte(r[0]),c.writeByte(r[1]),c.writeByte(r[2])}}function z(c,t){c.writeByte(255&t),c.writeByte(t>>8&255)}function St(c,t){for(var e=0;e<t.length;e++)c.writeByte(t.charCodeAt(e))}function lt(c){return Math.max(Math.ceil(Math.log2(c)),1)}pe();class _e{_recorder;_textmodifier;_registerPostDrawHook;constructor(t,e){this._recorder=new he,this._textmodifier=t,this._registerPostDrawHook=e}async $saveGIF(t={}){const e=this._textmodifier.canvas,i=this._applyDefaultOptions(t),r=t.onProgress;try{const s=await this._recorder.$record(e,i,this._registerPostDrawHook,r),n=xe(),{repeat:u}=t;s.forEach((a,o)=>{const{width:d,height:m,imageData:y,delayMs:h}=a,v=new Uint32Array(y.data.buffer.slice(0)),E=ve(v,256,{}),w=(function(g,B,$){let I=($=$||"rgb565")==="rgb444"?4096:65536,C=new Uint8Array(g.length),k=new Array(I);if($==="rgba4444")for(let L=0;L<g.length;L++){let _,S=g[L],V=S>>24&255,b=S>>16&255,D=S>>8&255,f=255&S,x=It(f,D,b,V);k[x]!=null?_=k[x]:(_=we(f,D,b,V,B),k[x]=_),C[L]=_}else for(let L=0;L<g.length;L++){let _,S=g[L],V=S>>16&255,b=S>>8&255,D=255&S,f=$==="rgb444"?Ft(D,b,V):Bt(D,b,V);k[f]!=null?_=k[f]:(_=ye(D,b,V,B),k[f]=_),C[L]=_}return C})(v,E);n.writeFrame(w,d,m,{palette:E,delay:h,repeat:o===0?u:-1})}),n.finish();const l=n.bytes(),p=l.buffer.slice(l.byteOffset,l.byteOffset+l.byteLength);new X().$downloadFile(new Blob([p],{type:"image/gif"}),i.filename),r?.({state:"completed",totalFrames:i.frameCount})}catch(s){throw r?.({state:"error",message:s instanceof Error?s.message:"GIF export failed"}),s}}_applyDefaultOptions(t){const e=Math.abs(Math.round(t.frameCount??300)),i=Math.abs(t.frameRate??60),r=Math.abs(t.scale??1),s=Math.max(-1,Math.round(t.repeat??0));return{filename:t.filename,frameCount:e,frameRate:i,scale:r,repeat:s}}}function Ce(c){return c&&c.__esModule&&Object.prototype.hasOwnProperty.call(c,"default")?c.default:c}var $t,Lt={exports:{}};function Ee(){return $t||($t=1,(function(c){(function(){function t(l,p){return(function(a){if(typeof a!="string"||!a.match(/^data:image\/webp;base64,/i))throw new Error("Failed to decode WebP Base64 URL");return window.atob(a.substring(23))})(typeof l=="string"&&/^data:image\/webp/.test(l)?l:l.toDataURL("image/webp",p))}function e(l){return(l.charCodeAt(0)|l.charCodeAt(1)<<8|l.charCodeAt(2)<<16|l.charCodeAt(3)<<24)>>>0}function i(l){let p=l.indexOf("VP8",12);if(p===-1)throw new Error("Bad image format, does this browser support WebP?");let a=!1;for(;p<l.length-8;){let o,d;switch(d=l.substring(p,p+4),p+=4,o=e(l.substring(p,p+4)),p+=4,d){case"VP8 ":return{frame:l.substring(p,p+o),hasAlpha:a};case"ALPH":a=!0}p+=o,1&o&&p++}throw new Error("Failed to find VP8 keyframe in WebP image, is this image mistakenly encoded in the Lossless WebP format?")}function r(l){this.value=l}function s(l){this.value=l}function n(l,p,a){if(Array.isArray(a))for(let o=0;o<a.length;o++)n(l,p,a[o]);else if(typeof a=="string")l.writeString(a);else if(a instanceof Uint8Array)l.writeBytes(a);else{if(!a.id)throw new Error("Bad EBML datatype "+typeof a.data);if(a.offset=l.pos+p,l.writeUnsignedIntBE(a.id),Array.isArray(a.data)){let o,d,m;a.size===-1?l.writeByte(255):a.size===-2?(o=l.pos,l.writeBytes([15,255,255,255,255])):(o=l.pos,l.writeBytes([0,0,0,0])),d=l.pos,a.dataOffset=d+p,n(l,p,a.data),a.size!==-1&&a.size!==-2&&(m=l.pos,a.size=m-d,l.seek(o),l.writeEBMLVarIntWidth(a.size,4),l.seek(m))}else if(typeof a.data=="string")l.writeEBMLVarInt(a.data.length),a.dataOffset=l.pos+p,l.writeString(a.data);else if(typeof a.data=="number")a.size||(a.size=l.measureUnsignedInt(a.data)),l.writeEBMLVarInt(a.size),a.dataOffset=l.pos+p,l.writeUnsignedIntBE(a.data,a.size);else if(a.data instanceof s)l.writeEBMLVarInt(8),a.dataOffset=l.pos+p,l.writeDoubleBE(a.data.value);else if(a.data instanceof r)l.writeEBMLVarInt(4),a.dataOffset=l.pos+p,l.writeFloatBE(a.data.value);else{if(!(a.data instanceof Uint8Array))throw new Error("Bad EBML datatype "+typeof a.data);l.writeEBMLVarInt(a.data.byteLength),a.dataOffset=l.pos+p,l.writeBytes(a.data)}}}let u=function(l,p){return function(a){let o,d,m=!1,y=0,h=0,v=null,E=null,w=null,g=[],B=0,$=0,I={quality:.95,transparent:!1,alphaQuality:void 0,fileWriter:null,fd:null,frameDuration:null,frameRate:null},C={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}},k={id:17545,data:new s(0)},L=[],_=new p(a.fileWriter||a.fd);function S(f){return f-o.dataOffset}function V(){d=(function(){let A={id:21420,size:5,data:0},O={id:290298740,data:[]};for(let F in C){let T=C[F];T.positionEBML=Object.create(A),O.data.push({id:19899,data:[{id:21419,data:T.id},T.positionEBML]})}return O})();let f={id:357149030,data:[{id:2807729,data:1e6},{id:19840,data:"webm-writer-js"},{id:22337,data:"webm-writer-js"},k]},x=[{id:176,data:y},{id:186,data:h}];a.transparent&&x.push({id:21440,data:1});let M={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}]}]};o={id:408125543,size:-2,data:[d,f,M]};let P=new l(256);n(P,_.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}]},o]),_.write(P.getAsDataArray()),C.SegmentInfo.positionEBML.data=S(f.offset),C.Tracks.positionEBML.data=S(M.offset),m=!0}function b(f){return f.alpha?(function(x){let M,P,A=new l(4);if(!(x.trackNumber>0&&x.trackNumber<127))throw new Error("TrackNumber must be > 0 and < 127");return A.writeEBMLVarInt(x.trackNumber),A.writeU16BE(x.timecode),A.writeByte(0),M={id:161,data:[A.getAsDataArray(),x.frame]},P={id:30113,data:[{id:166,data:[{id:238,data:1},{id:165,data:x.alpha}]}]},{id:160,data:[M,P]}})(f):(function(x){let M=new l(4);if(!(x.trackNumber>0&&x.trackNumber<127))throw new Error("TrackNumber must be > 0 and < 127");return M.writeEBMLVarInt(x.trackNumber),M.writeU16BE(x.timecode),M.writeByte(128),{id:163,data:[M.getAsDataArray(),x.frame]}})(f)}function D(){if(g.length===0)return;let f=0;for(let F=0;F<g.length;F++)f+=g[F].frame.length+(g[F].alpha?g[F].alpha.length:0);let x=new l(f+64*g.length),M=(function(F){return{id:524531317,data:[{id:231,data:Math.round(F.timecode)}]}})({timecode:Math.round(B)});for(let F=0;F<g.length;F++)M.data.push(b(g[F]));var P,A,O;n(x,_.pos,M),_.write(x.getAsDataArray()),P=1,A=Math.round(B),O=M.offset,L.push({id:187,data:[{id:179,data:A},{id:183,data:[{id:247,data:P},{id:241,data:S(O)}]}]}),g=[],B+=$,$=0}this.addFrame=function(f,x,M){m||(y=f.width||0,h=f.height||0,V());let P,A=i(t(f,a.quality)),O=null;P=M||(typeof x=="number"?x:a.frameDuration),a.transparent&&(x instanceof HTMLCanvasElement||typeof x=="string"?O=x:A.hasAlpha&&(O=(function(F){v!==null&&v.width===F.width&&v.height===F.height||(v=document.createElement("canvas"),v.width=F.width,v.height=F.height,E=v.getContext("2d"),w=E.createImageData(v.width,v.height));let T=F.getContext("2d").getImageData(0,0,F.width,F.height).data,U=w.data,W=0,mi=F.width*F.height*4;for(let gt=3;gt<mi;gt+=4){let vt=T[gt];U[W++]=vt,U[W++]=vt,U[W++]=vt,U[W++]=255}return E.putImageData(w,0,0),v})(f))),(function(F){F.trackNumber=1,F.timecode=Math.round($),g.push(F),$+=F.duration,$>=5e3&&D()})({frame:A.frame,duration:P,alpha:O?i(t(O,a.alphaQuality)).frame:null})},this.complete=function(){return m||V(),D(),(function(){let f={id:475249515,data:L},x=new l(16+32*L.length);n(x,_.pos,f),_.write(x.getAsDataArray()),C.Cues.positionEBML.data=S(f.offset)})(),(function(){let f=new l(d.size),x=_.pos;n(f,d.dataOffset,d.data),_.seek(d.dataOffset),_.write(f.getAsDataArray()),_.seek(x)})(),(function(){let f=new l(8),x=_.pos;f.writeDoubleBE(B),_.seek(k.dataOffset),_.write(f.getAsDataArray()),_.seek(x)})(),(function(){let f=new l(10),x=_.pos;f.writeUnsignedIntBE(o.id),f.writeEBMLVarIntWidth(_.pos-o.dataOffset,5),_.seek(o.offset),_.write(f.getAsDataArray()),_.seek(x)})(),_.complete("video/webm")},this.getWrittenSize=function(){return _.length},a=(function(f,x){let M={};return[f,x].forEach(function(P){for(let A in P)Object.prototype.hasOwnProperty.call(P,A)&&(M[A]=P[A])}),M})(I,a||{}),(function(){if(!a.frameDuration){if(!a.frameRate)throw new Error("Missing required frameDuration or frameRate setting");a.frameDuration=1e3/a.frameRate}a.quality=Math.max(Math.min(a.quality,.99999),0),a.alphaQuality===void 0?a.alphaQuality=a.quality:a.alphaQuality=Math.max(Math.min(a.alphaQuality,.99999),0)})()}};c.exports=u})()})(Lt)),Lt.exports}var Pt,Vt={exports:{}};function Be(){return Pt||(Pt=1,(function(c){(function(){let t=function(e){this.data=new Uint8Array(e),this.pos=0};t.prototype.seek=function(e){this.pos=e},t.prototype.writeBytes=function(e){for(let i=0;i<e.length;i++)this.data[this.pos++]=e[i]},t.prototype.writeByte=function(e){this.data[this.pos++]=e},t.prototype.writeU8=t.prototype.writeByte,t.prototype.writeU16BE=function(e){this.data[this.pos++]=e>>8,this.data[this.pos++]=e},t.prototype.writeDoubleBE=function(e){let i=new Uint8Array(new Float64Array([e]).buffer);for(let r=i.length-1;r>=0;r--)this.writeByte(i[r])},t.prototype.writeFloatBE=function(e){let i=new Uint8Array(new Float32Array([e]).buffer);for(let r=i.length-1;r>=0;r--)this.writeByte(i[r])},t.prototype.writeString=function(e){for(let i=0;i<e.length;i++)this.data[this.pos++]=e.charCodeAt(i)},t.prototype.writeEBMLVarIntWidth=function(e,i){switch(i){case 1:this.writeU8(128|e);break;case 2:this.writeU8(64|e>>8),this.writeU8(e);break;case 3:this.writeU8(32|e>>16),this.writeU8(e>>8),this.writeU8(e);break;case 4:this.writeU8(16|e>>24),this.writeU8(e>>16),this.writeU8(e>>8),this.writeU8(e);break;case 5:this.writeU8(8|e/4294967296&7),this.writeU8(e>>24),this.writeU8(e>>16),this.writeU8(e>>8),this.writeU8(e);break;default:throw new Error("Bad EBML VINT size "+i)}},t.prototype.measureEBMLVarInt=function(e){if(e<127)return 1;if(e<16383)return 2;if(e<2097151)return 3;if(e<268435455)return 4;if(e<34359738367)return 5;throw new Error("EBML VINT size not supported "+e)},t.prototype.writeEBMLVarInt=function(e){this.writeEBMLVarIntWidth(e,this.measureEBMLVarInt(e))},t.prototype.writeUnsignedIntBE=function(e,i){switch(i===void 0&&(i=this.measureUnsignedInt(e)),i){case 5:this.writeU8(Math.floor(e/4294967296));case 4:this.writeU8(e>>24);case 3:this.writeU8(e>>16);case 2:this.writeU8(e>>8);case 1:this.writeU8(e);break;default:throw new Error("Bad UINT size "+i)}},t.prototype.measureUnsignedInt=function(e){return e<256?1:e<65536?2:e<1<<24?3:e<4294967296?4:5},t.prototype.getAsDataArray=function(){if(this.pos<this.data.byteLength)return this.data.subarray(0,this.pos);if(this.pos==this.data.byteLength)return this.data;throw new Error("ArrayBufferDataStream's pos lies beyond end of buffer")},c.exports=t})()})(Vt)),Vt.exports}var Dt,At,Ot,Ut={exports:{}};function Ie(){return Dt||(Dt=1,(function(c){(function(){let t=function(e){return function(i){let r=[],s=Promise.resolve(),n=null,u=null;function l(o){return new Promise(function(d,m){let y=new FileReader;y.addEventListener("loadend",function(){d(y.result)}),y.readAsArrayBuffer(o)})}function p(o){return new Promise(function(d,m){o instanceof Uint8Array?d(o):o instanceof ArrayBuffer||ArrayBuffer.isView(o)?d(new Uint8Array(o)):o instanceof Blob?d(l(o).then(function(y){return new Uint8Array(y)})):d(l(new Blob([o])).then(function(y){return new Uint8Array(y)}))})}function a(o){let d=o.byteLength||o.length||o.size;if(!Number.isInteger(d))throw new Error("Failed to determine size of element");return d}i&&i.constructor.name==="FileWriter"?n=i:e&&i&&(u=i),this.pos=0,this.length=0,this.seek=function(o){if(o<0)throw new Error("Offset may not be negative");if(isNaN(o))throw new Error("Offset may not be NaN");if(o>this.length)throw new Error("Seeking beyond the end of file is not allowed");this.pos=o},this.write=function(o){let d={offset:this.pos,data:o,length:a(o)},m=d.offset>=this.length;this.pos+=d.length,this.length=Math.max(this.length,this.pos),s=s.then(function(){if(u)return new Promise(function(y,h){p(d.data).then(function(v){let E=0,w=Buffer.from(v.buffer),g=function(B,$,I){E+=$,E>=I.length?y():e.write(u,I,E,I.length-E,d.offset+E,g)};e.write(u,w,0,w.length,d.offset,g)})});if(n)return new Promise(function(y,h){n.onwriteend=y,n.seek(d.offset),n.write(new Blob([d.data]))});if(!m)for(let y=0;y<r.length;y++){let h=r[y];if(!(d.offset+d.length<=h.offset||d.offset>=h.offset+h.length)){if(d.offset<h.offset||d.offset+d.length>h.offset+h.length)throw new Error("Overwrite crosses blob boundaries");return d.offset==h.offset&&d.length==h.length?void(h.data=d.data):p(h.data).then(function(v){return h.data=v,p(d.data)}).then(function(v){d.data=v,h.data.set(d.data,d.offset-h.offset)})}}r.push(d)})},this.complete=function(o){return s=u||n?s.then(function(){return null}):s.then(function(){let d=[];for(let m=0;m<r.length;m++)d.push(r[m].data);return new Blob(d,{type:o})}),s}}};c.exports=t})()})(Ut)),Ut.exports}function Fe(){return Ot?At:(Ot=1,At=Ee()(Be(),Ie()(null)))}const ke=Ce(Fe());class Me{async $record(t,e,i,r){const s=Math.max(1,Math.round(e.frameRate)),n=Math.max(1,Math.round(e.frameCount)),u=new ke({quality:e.quality,alphaQuality:e.transparent?e.quality:void 0,transparent:e.transparent,frameRate:s}),l=this._createSnapshotSurface();return await new Promise((p,a)=>{let o,d="capturing",m=!1,y=0;const h=()=>{o&&(o(),o=void 0)},v=()=>{r?.({state:"recording",frameIndex:y,totalFrames:n})},E=g=>{if(m)return;m=!0,h();const B=g instanceof Error?g.message:"WEBM export failed";r?.({state:"error",message:B}),a(g instanceof Error?g:new Error(String(g)))},w=()=>{d!=="capturing"||m||(d="encoding",h(),u.complete().then(g=>{(B=>{m||(m=!0,r?.({state:"completed",frameIndex:Math.min(n,y),totalFrames:n}),p(B))})(g)}).catch(E))};o=i(()=>{(()=>{if(d==="capturing")if(y>=n)w();else try{const g=l(t);u.addFrame(g),y+=1,v(),y>=n&&w()}catch(g){E(g)}})()}),v()})}_createSnapshotSurface(){let t=null,e=null;return i=>{const r=Math.max(1,i.width),s=Math.max(1,i.height),n=((u,l)=>(t||(t=document.createElement("canvas")),t.width!==u||t.height!==l?(t.width=u,t.height=l,e=t.getContext("2d"),e&&(e.imageSmoothingEnabled=!1)):e||(e=t.getContext("2d"),e&&(e.imageSmoothingEnabled=!1)),e?t:null))(r,s);return n&&e?(e.clearRect(0,0,r,s),e.drawImage(i,0,0,r,s),n):i}}}class Se{_recorder;_textmodifier;_registerPostDrawHook;constructor(t,e){this._recorder=new Me,this._textmodifier=t,this._registerPostDrawHook=e}async $saveWEBM(t={}){const e=this._textmodifier.canvas,i=this._applyDefaultOptions(t);try{const r=await this._recorder.$record(e,i,this._registerPostDrawHook,t.onProgress);new X().$downloadFile(r,i.filename)}catch(r){throw t.onProgress?.({state:"error",message:r instanceof Error?r.message:"WEBM export failed"}),r}}_applyDefaultOptions(t){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}}}const $e="textmode-export-overlay",G="textmode-export-overlay__stack",dt="textmode-export-overlay__stack--dense",Tt="textmode-export-overlay__stack--compact",Nt="textmode-export-overlay__section",Le="textmode-export-overlay__header",Pe="textmode-export-overlay__header-title-row",Ve="textmode-export-overlay__header-links",De="textmode-export-overlay__row",Rt="textmode-export-overlay__label",Gt="textmode-export-overlay__field",Wt="textmode-export-overlay__field--compact",zt="textmode-export-overlay__field--dense",qt="textmode-export-overlay__field--full",Ht="textmode-export-overlay__field--channel",jt="textmode-export-overlay__input",Ae="textmode-export-overlay__checkbox",Qt="textmode-export-overlay__muted",Oe="textmode-export-overlay__status",Ue="textmode-export-overlay__status--gif",Te="textmode-export-overlay__status--video",Ne="textmode-export-overlay__status-title",Re="textmode-export-overlay__status-value",Ge="textmode-export-overlay__divider",We="textmode-export-overlay__title",ze="textmode-export-overlay__button",ct="textmode-export-overlay__button--primary",ut="textmode-export-overlay__button--secondary",Xt="textmode-export-overlay__button--full",qe="textmode-export-overlay__support-link",He="textmode-export-overlay__support-icon",je="textmode-export-overlay__link-icon";class R{element;mounted=!1;destroyed=!1;mount(t){if(this.destroyed)throw new Error("Cannot mount a destroyed component");if(this.mounted)return;const e=this.render();t.appendChild(e),this.element=e,this.onMount(),this.mounted=!0}unmount(){this.mounted&&this.element&&(this._onUnmount(),this.element.remove(),this.element=void 0,this.mounted=!1)}destroy(){this.destroyed||(this.unmount(),this._onDestroy(),this.destroyed=!0)}update(t){this.mounted&&this.onUpdate(t)}onMount(){}_onUnmount(){}_onDestroy(){}onUpdate(t){}get root(){if(!this.element)throw new Error("Component is not mounted yet");return this.element}isMounted(){return this.mounted}}class Y extends R{static _iconNamespace="http://www.w3.org/2000/svg";render(){const t=document.createElement("div");t.classList.add(G,dt,Le);const e=document.createElement("div");e.classList.add(Pe);const i=document.createElement("strong");i.textContent="textmode.export.js",i.classList.add(We);const r=document.createElement("div");r.classList.add(Ve);const s=this._createLink("https://github.com/humanbydefinition/textmode.export.js","View repository on GitHub",je,[{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",He,[{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"}]);r.appendChild(s),r.appendChild(n),e.appendChild(i),e.appendChild(r);const u=document.createElement("div");return u.classList.add(Ge),t.appendChild(e),t.appendChild(u),t}_createLink(t,e,i,r){const s=document.createElement("a");return s.href=t,s.target="_blank",s.rel="noopener noreferrer",s.classList.add(qe),s.setAttribute("aria-label",e),s.appendChild(this._createIcon(i,r)),s}_createIcon(t,e){const i=document.createElementNS(Y._iconNamespace,"svg");i.setAttribute("xmlns",Y._iconNamespace),i.setAttribute("width","18"),i.setAttribute("height","18"),i.setAttribute("viewBox","0 0 24 24"),i.setAttribute("fill","none"),i.setAttribute("stroke","currentColor"),i.setAttribute("stroke-width","2"),i.setAttribute("stroke-linecap","round"),i.setAttribute("stroke-linejoin","round"),i.classList.add(t);for(const r of e){const s=document.createElementNS(Y._iconNamespace,"path");for(const[n,u]of Object.entries(r))s.setAttribute(n,u);i.appendChild(s)}return i}}class N extends R{props;constructor(t){super(),this.props=t}render(){const t=document.createElement("div");t.classList.add(Gt),this.applyVariant(t,this.props.variant);const e=document.createElement("label");if(e.classList.add(Rt),e.textContent=this.props.label,this.props.labelFor&&(e.htmlFor=this.props.labelFor),t.appendChild(e),this.props.description){const i=document.createElement("span");i.classList.add(Qt),i.textContent=this.props.description,t.appendChild(i)}return t}attachControl(t){this.root.appendChild(t)}update(t){this.props=t,super.update(t)}onUpdate(t){const e=this.root;for(;e.firstChild;)e.removeChild(e.firstChild);this.props=t,e.classList.add(Gt),this.applyVariant(e,t.variant);const i=document.createElement("label");if(i.classList.add(Rt),i.textContent=t.label,t.labelFor&&(i.htmlFor=t.labelFor),e.appendChild(i),t.description){const r=document.createElement("span");r.classList.add(Qt),r.textContent=t.description,e.appendChild(r)}}applyVariant(t,e){switch(t.classList.remove(Wt,zt,qt,Ht),e){case"compact":t.classList.add(Wt);break;case"dense":t.classList.add(zt);break;case"full":t.classList.add(qt);break;case"channel":t.classList.add(Ht)}}}class ht extends R{props;select;handleChange=()=>{this.props.defaultValue=this.select.value};constructor(t){super(),this.props=t}render(){return this.select=document.createElement("select"),this.select.classList.add(jt),this.props.id&&(this.select.id=this.props.id),this.populateOptions(),this.select.addEventListener("change",this.handleChange),this.select}get selectElement(){return this.select}get value(){return this.select.value}set value(t){this.select&&(this.select.value=t),this.props.defaultValue=t}_onUnmount(){this.select.removeEventListener("change",this.handleChange)}onUpdate(t){this.props=t,this.populateOptions(),t.defaultValue&&(this.select.value=t.defaultValue)}update(t){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))}populateOptions(){if(this.select){this.select.innerHTML="";for(const t of this.props.options){const e=document.createElement("option");e.value=t.value,e.textContent=t.label,this.select.appendChild(e)}this.props.defaultValue&&(this.select.value=this.props.defaultValue)}}}class Yt extends R{props;button;constructor(t){super(),this.props=t}render(){return this.button=document.createElement("button"),this.button.type="button",this.button.textContent=this.props.label,this.button.classList.add(ze),this.props.variant==="secondary"?this.button.classList.add(ut):this.button.classList.add(ct),this.props.fullWidth&&this.button.classList.add(Xt),this.button.disabled=!!this.props.disabled,this.button}get buttonElement(){return this.button}setLabel(t){this.props.label=t,this.button.textContent=t}setDisabled(t){this.props.disabled=t,this.button.disabled=t}onUpdate(t){this.props=t,this.button.textContent=t.label,this.button.disabled=!!t.disabled,t.variant==="secondary"?(this.button.classList.add(ut),this.button.classList.remove(ct)):(this.button.classList.add(ct),this.button.classList.remove(ut)),this.button.classList.toggle(Xt,!!t.fullWidth)}}class Qe{api;constructor(t){this.api=t}async $copy(t,e){switch(t){case"txt":{const i=this.api.toString(e);await navigator.clipboard.writeText(i);break}case"svg":{const i=this.api.toSVG(e);await navigator.clipboard.writeText(i);break}case"image":await this.api.copyCanvas(e);break;default:throw new Error(`Clipboard not supported for ${t}`)}}}class Xe{api;events;constructor(t,e){this.api=t,this.events=e}async $requestExport(t,e,i={}){this.events.$emit("export:request",{format:t});try{const r={onGIFProgress:i.onGIFProgress?s=>{i.onGIFProgress?.(s),this.events.$emit("export:progress",{format:t,progress:s})}:t==="gif"?s=>this.events.$emit("export:progress",{format:t,progress:s}):void 0,onVideoProgress:i.onVideoProgress?s=>{i.onVideoProgress?.(s),this.events.$emit("export:progress",{format:t,progress:s})}:t==="video"?s=>this.events.$emit("export:progress",{format:t,progress:s}):void 0};await this._execute(t,e,r),this.events.$emit("export:success",{format:t})}catch(r){throw this.events.$emit("export:error",{format:t,error:r}),r}}_execute(t,e,i){switch(t){case"txt":return Promise.resolve(this.api.saveStrings(e));case"image":return this.api.saveCanvas(e);case"svg":return Promise.resolve(this.api.saveSVG(e));case"gif":{const r={...e,onProgress:i.onGIFProgress};return this.api.saveGIF(r)}case"video":{const r={...e,onProgress:i.onVideoProgress};return this.api.saveWEBM(r)}}}}class Ye{modifier;overlay;animationFrameId=null;handleUpdate;bound=!1;constructor(t,e){this.modifier=t,this.overlay=e,this.handleUpdate=()=>this.scheduleUpdate()}scheduleUpdate(){this.animationFrameId!==null&&cancelAnimationFrame(this.animationFrameId),this.animationFrameId=requestAnimationFrame(()=>this.update())}bind(){this.bound||(window.addEventListener("resize",this.handleUpdate),window.addEventListener("scroll",this.handleUpdate,!0),this.bound=!0,this.handleUpdate())}update(){this.animationFrameId=null;const t=this.modifier.canvas.getBoundingClientRect();this.overlay.style.top=`${t.top+window.scrollY+8}px`,this.overlay.style.left=`${t.left+window.scrollX+8}px`}dispose(){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)}}const J="textmode-export-number-input",Je=`${J}__field`,Ze=`${J}__controls`,Ke=`${J}__control`,Jt=`${J}__display`,rt=`${Jt}--visible`,Zt=c=>{const t=document.createElement("button");return t.type="button",t.className=Ke,t.textContent=c>0?"▲":"▼",t};class q extends R{props;input;display;incrementButton;decrementButton;suppressClickAfterPointer=new WeakMap;holdTimeoutId;holdIntervalId;activePointerId;disabledObserver;constructor(t){super(),this.props=t}render(){const t=document.createElement("div");t.className=J,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=Je,this.display=document.createElement("div"),this.display.className=Jt;const e=document.createElement("div");return e.className=Ze,this.incrementButton=Zt(1),this.decrementButton=Zt(-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}get inputElement(){return this.input}get value(){return this.input.value}set value(t){this.input.value=t,this.updateDisplay()}refresh(){this.updateDisplay()}_onDestroy(){this.clearHoldTimers(),this.disabledObserver?.disconnect()}bindStepControls(){const t=s=>()=>{if(this.input.disabled)return;const n=this.input.value,u=this.input.getAttribute("step");if(u&&u!=="any")s>0?this.input.stepUp():this.input.stepDown();else{const p=s>0?1:-1,a=Number.parseFloat(this.input.value||"0"),o=Number.isFinite(a)?a+p:p;this.input.value=String(o)}var l;this.input.value!==n&&((l=this.input).dispatchEvent(new Event("input",{bubbles:!0})),l.dispatchEvent(new Event("change",{bubbles:!0}))),this.updateDisplay(),this.input.focus({preventScroll:!0})},e=t(1),i=t(-1),r=(s,n)=>u=>{if(this.suppressClickAfterPointer.get(s))return this.suppressClickAfterPointer.set(s,!1),void u.preventDefault();n()};this.incrementButton.addEventListener("click",r(this.incrementButton,e)),this.decrementButton.addEventListener("click",r(this.decrementButton,i))}bindHoldBehavior(){const t=(e,i)=>{const r=i>0?()=>this.incrementButton.click():()=>this.decrementButton.click();e.addEventListener("pointerdown",u=>{if(u.button===0){u.preventDefault(),this.suppressClickAfterPointer.set(e,!0),this.activePointerId=u.pointerId;try{e.setPointerCapture(u.pointerId)}catch{}r(),this.holdTimeoutId=window.setTimeout(()=>{this.holdIntervalId=window.setInterval(r,80)},380)}});const s=()=>{if(this.activePointerId!==void 0){try{e.releasePointerCapture(this.activePointerId)}catch{}this.activePointerId=void 0}},n=()=>{this.clearHoldTimers(),s()};e.addEventListener("pointerup",n),e.addEventListener("pointerleave",n),e.addEventListener("pointercancel",n)};t(this.incrementButton,1),t(this.decrementButton,-1)}bindInputListeners(){const t=()=>{this.props.defaultValue=this.input.value,this.updateDisplay()};this.input.addEventListener("input",t),this.input.addEventListener("change",t)}observeDisabledState(t){const e=()=>{const i=this.input.disabled;this.incrementButton.disabled=i,this.decrementButton.disabled=i,t.classList.toggle("is-disabled",i),i?(this.display.classList.remove(rt),this.input.style.removeProperty("color"),this.input.style.removeProperty("caretColor")):this.updateDisplay()};typeof MutationObserver<"u"&&(this.disabledObserver=new MutationObserver(e),this.disabledObserver.observe(this.input,{attributes:!0,attributeFilter:["disabled"]})),e()}updateDisplay(){const t=this.props.formatDisplay;if(!t)return this.display.textContent="",this.display.classList.remove(rt),this.input.style.removeProperty("color"),void this.input.style.removeProperty("caretColor");const e=this.input.value,i=Number.parseFloat(e),r=t(Number.isFinite(i)?i:Number.NaN,e,this.input);r?(this.display.textContent=r,this.display.classList.add(rt),this.input.style.color="transparent",this.input.style.caretColor="#f8fafc"):(this.display.textContent="",this.display.classList.remove(rt),this.input.style.removeProperty("color"),this.input.style.removeProperty("caretColor"))}clearHoldTimers(){this.holdTimeoutId!==void 0&&(window.clearTimeout(this.holdTimeoutId),this.holdTimeoutId=void 0),this.holdIntervalId!==void 0&&(window.clearInterval(this.holdIntervalId),this.holdIntervalId=void 0)}}class Z extends R{variant;additionalClasses;constructor(t="stack",e=[]){super(),this.variant=t,this.additionalClasses=e}render(){const t=document.createElement("div"),e=[this.variant==="stack"?G:void 0,this.variant==="stackDense"?dt:void 0,this.variant==="stackCompact"?Tt:void 0,this.variant==="row"?De:void 0,this.variant==="section"?Nt:void 0,...this.additionalClasses].filter(Boolean);return t.classList.add(...e),t}}const Kt={neutral:"textmode-export-overlay__status-value--neutral",active:"textmode-export-overlay__status-value--active",alert:"textmode-export-overlay__status-value--alert"};class te extends R{props;container;messageElement;constructor(t){super(),this.props=t}render(){this.container=document.createElement("div"),this.container.classList.add(Oe),this.props.context==="gif"?this.container.classList.add(Ue):this.props.context==="video"&&this.container.classList.add(Te);const t=document.createElement("span");return t.classList.add(Ne),t.textContent=this.props.title,this.messageElement=document.createElement("span"),this.messageElement.classList.add(Re),this.messageElement.textContent=this.props.message,this.applyVariant(this.props.variant??"neutral"),this.container.appendChild(t),this.container.appendChild(this.messageElement),this.container}setMessage(t,e="neutral"){this.messageElement.textContent=t,this.applyVariant(e)}onUpdate(t){this.props=t,this.setMessage(t.message,t.variant??"neutral")}applyVariant(t){this.messageElement.classList.remove(...Object.values(Kt)),this.messageElement.classList.add(Kt[t])}}class K extends R{_config;_managedComponents=new Set;constructor(t){super(),this._config=t}_manageComponent(t){return this._managedComponents.add(t),t}_onUnmount(){for(const t of this._managedComponents)t.unmount()}_onDestroy(){for(const t of this._managedComponents)t.destroy();this._managedComponents.clear()}}const ee=300,ie=60,re=1,ti=0;class pt extends K{frameCountInput=this._manageComponent(new q({defaultValue:String(ee),attributes:{min:"1",max:"600",step:"1"}}));frameRateInput=this._manageComponent(new q({defaultValue:String(ie),attributes:{min:"1",max:"60",step:"1"}}));scaleInput=this._manageComponent(new q({defaultValue:String(re),attributes:{min:"0.1",max:"8",step:"0.1"}}));repeatInput=this._manageComponent(new q({defaultValue:"0",attributes:{min:"0",max:"50",step:"1"},formatDisplay:(t,e)=>Number.isFinite(t)?t===0?"∞":null:e.trim()===""?null:e}));status=this._manageComponent(new te({title:"status",message:"ready to record",variant:"neutral",context:"gif"}));recordingState="idle";constructor(t){super(t)}render(){const t=document.createElement("div");t.classList.add(G);const e=new Z("row");e.mount(t);const i=new N({label:"number of frames",labelFor:"textmode-export-gif-frame-count",variant:"dense"});i.mount(e.root),this.frameCountInput.mount(i.root),this.frameCountInput.inputElement.id="textmode-export-gif-frame-count";const r=new N({label:"frame rate (fps)",labelFor:"textmode-export-gif-frame-rate",variant:"dense"});r.mount(e.root),this.frameRateInput.mount(r.root),this.frameRateInput.inputElement.id="textmode-export-gif-frame-rate";const s=new Z("row");s.mount(t);const n=new N({label:"scale",labelFor:"textmode-export-gif-scale",variant:"dense"});n.mount(s.root),this.scaleInput.mount(n.root),this.scaleInput.inputElement.id="textmode-export-gif-scale";const u=new N({label:"loop count",labelFor:"textmode-export-gif-repeat",variant:"dense"});return u.mount(s.root),this.repeatInput.mount(u.root),this.repeatInput.inputElement.id="textmode-export-gif-repeat",this.status.mount(t),t}getOptions(){const t=this._config.defaultOptions??{};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)}}reset(){this.recordingState="idle",this.applyDefaults(),this.setRecordingState("idle")}validate(){const t=Number.parseInt(this.frameCountInput.value,10),e=Number.parseInt(this.frameRateInput.value,10),i=Number.parseFloat(this.scaleInput.value);return Number.isFinite(t)&&t>0&&Number.isFinite(e)&&e>0&&Number.isFinite(i)&&i>0}isRecording(){return this.recordingState==="recording"}setRecordingState(t,e){this.recordingState=t;const i=t==="recording";switch(this.frameCountInput.inputElement.disabled=i,this.frameRateInput.inputElement.disabled=i,this.scaleInput.inputElement.disabled=i,this.repeatInput.inputElement.disabled=i,t){case"recording":if(e?.totalFrames){const r=e.frameIndex??0;this.status.setMessage(`recording ${r}/${e.totalFrames}`,"active")}else this.status.setMessage("recording…","active");break;case"completed":this.status.setMessage("GIF saved successfully","active");break;case"error":this.status.setMessage(e?.message??"failed to export GIF","alert");break;default:this.status.setMessage("ready to record","neutral")}}handleProgress(t){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")}applyDefaults(){const t=this._config.defaultOptions??{},e=t.frameCount??ee,i=t.frameRate??ie,r=t.scale??re,s=t.repeat??ti;this.frameCountInput.value=String(e),this.frameRateInput.value=String(i),this.scaleInput.value=String(r),this.repeatInput.value=String(s),this.frameCountInput.refresh(),this.frameRateInput.refresh(),this.scaleInput.refresh(),this.repeatInput.refresh()}safeParseInt(t,e){const i=Number.parseInt(t,10);return Number.isFinite(i)?i:e}safeParseFloat(t,e){const i=Number.parseFloat(t);return Number.isFinite(i)?i:e}}const st="textmode-export-range-input",ei=`${st}__field`,ii=`${st}__tooltip`,mt=`${st}__tooltip--visible`,at=(c,t)=>{if(typeof c!="string"||c.length===0)return t;const e=Number.parseFloat(c);return Number.isFinite(e)?e:t};class ri extends R{props;input;tooltip;hoverActive=!1;focusActive=!1;pointerActive=!1;suppressFocusFromPointer=!1;activePointerId;disabledObserver;resizeObserver;constructor(t){super(),this.props=t}render(){const t=document.createElement("div");return t.className=st,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=ei,this.tooltip=document.createElement("div"),this.tooltip.className=ii,t.appendChild(this.input),t.appendChild(this.tooltip),this.bindEvents(t),this.updateTooltip(),this.syncDisabledState(t),t}get inputElement(){return this.input}get value(){return this.input.value}set value(t){this.input.value=t,this.updateTooltip()}_onDestroy(){this.disabledObserver?.disconnect(),this.resizeObserver?.disconnect()}bindEvents(t){this.input.addEventListener("pointerenter",()=>{this.hoverActive=!0,this.refreshTooltipVisibility()}),this.input.addEventListener("pointerleave",()=>{this.hoverActive=!1,this.refreshTooltipVisibility()}),this.input.addEventListener("focus",()=>{let i=!1;try{i=this.input.matches(":focus-visible")}catch{i=!this.suppressFocusFromPointer}this.suppressFocusFromPointer||!i||this.input.disabled?this.focusActive=!1:this.focusActive=!0,this.suppressFocusFromPointer=!1,this.refreshTooltipVisibility()}),this.input.addEventListener("blur",()=>{this.focusActive=!1,this.refreshTooltipVisibility()}),this.input.addEventListener("pointerdown",i=>{this.pointerActive=!0,this.suppressFocusFromPointer=!0,this.hoverActive=!0,this.refreshTooltipVisibility();try{this.input.setPointerCapture(i.pointerId),this.activePointerId=i.pointerId}catch{this.activePointerId=void 0}});const e=()=>{this.pointerActive=!1,this.suppressFocusFromPointer=!1,this.hoverActive=this.input.matches(":hover"),this.releasePointerCapture(),this.refreshTooltipVisibility()};this.input.addEventListener("pointerup",e),this.input.addEventListener("pointercancel",e),this.input.addEventListener("lostpointercapture",e),this.input.addEventListener("input",()=>{(this.hoverActive||this.focusActive||this.pointerActive)&&this.updateTooltip(),this.props.defaultValue=this.input.value}),this.input.addEventListener("change",()=>{this.pointerActive||this.updateTooltip(),this.props.defaultValue=this.input.value}),typeof ResizeObserver<"u"&&(this.resizeObserver=new ResizeObserver(()=>{this.tooltip.classList.contains(mt)&&this.updateTooltipPosition()}),this.resizeObserver.observe(t))}refreshTooltipVisibility(){const t=(this.hoverActive||this.focusActive||this.pointerActive)&&!this.input.disabled;t&&this.updateTooltip(),this.tooltip.classList.toggle(mt,t)}syncDisabledState(t){const e=()=>{const i=this.input.disabled;t.classList.toggle("is-disabled",i),i&&(this.hoverActive=!1,this.focusActive=!1,this.pointerActive=!1,this.suppressFocusFromPointer=!1,this.releasePointerCapture(),this.tooltip.classList.remove(mt))};typeof MutationObserver<"u"&&(this.disabledObserver=new MutationObserver(e),this.disabledObserver.observe(this.input,{attributes:!0,attributeFilter:["disabled"]})),e()}releasePointerCapture(){if(this.activePointerId!==void 0){try{this.input.releasePointerCapture(this.activePointerId)}catch{}this.activePointerId=void 0}}updateTooltip(){this.updateTooltipContent(),this.updateTooltipPosition()}updateTooltipContent(){const t=this.props.formatValue??(i=>`${i}`),e=this.getCurrentValue();this.tooltip.textContent=t(e,this.input)}updateTooltipPosition(){const t=this.getMin(),e=this.getMax(),i=((n,u,l)=>Math.min(l,Math.max(u,n)))(this.getCurrentValue(),t,e),r=e-t,s=r===0?0:(i-t)/r;this.tooltip.style.left=100*s+"%"}getMin(){return at(this.input.min,0)}getMax(){return at(this.input.max,100)}getCurrentValue(){return at(this.input.value,at(this.props.defaultValue,0))}}function si(c){return`${(function(e){if(!Number.isFinite(e))return"default quality";const i=Math.max(0,Math.min(1,e));return i>=.995?"near-lossless":i>=.98?"maximum detail":i>=.88?"ultra quality":i>=.7?"high quality":i>=.5?"balanced":i>=.25?"compact":i>=.1?"lightweight":"draft"})(c)} (quality ≈ ${c.toFixed(3)})`}class ft extends K{frameRateInput=this._manageComponent(new q({defaultValue:String(60),attributes:{min:String(1),max:String(60),step:"1"},formatDisplay:t=>Number.isFinite(t)?`${t} fps`:null}));frameCountInput=this._manageComponent(new q({defaultValue:String(480),attributes:{min:String(1),max:String(3600),step:"1"}}));qualityInput=this._manageComponent(new ri({defaultValue:String(.7),attributes:{min:String(0),max:String(1),step:"0.001"},formatValue:t=>si(t)}));status=this._manageComponent(new te({title:"status",message:"ready to record",variant:"neutral",context:"video"}));recordingState="idle";constructor(t){super(t)}render(){const t=document.createElement("div");t.classList.add(G);const e=new Z("row");e.mount(t);const i=new N({label:"number of frames",labelFor:"textmode-export-video-frame-count",variant:"compact"});i.mount(e.root),this.frameCountInput.mount(i.root),this.frameCountInput.inputElement.id="textmode-export-video-frame-count";const r=new N({label:"frame rate (fps)",labelFor:"textmode-export-video-frame-rate",variant:"compact"});r.mount(e.root),this.frameRateInput.mount(r.root),this.frameRateInput.inputElement.id="textmode-export-video-frame-rate";const s=new N({label:"quality",labelFor:"textmode-export-video-quality",variant:"full"});return s.mount(t),this.qualityInput.mount(s.root),this.qualityInput.inputElement.id="textmode-export-video-quality",this.status.mount(t),t}getOptions(){const t=this._config.defaultOptions??{},e=Number.parseInt(this.frameCountInput.value,10),i=Number.parseFloat(this.frameRateInput.value),r=Number.parseFloat(this.qualityInput.value);return{frameCount:Number.isFinite(e)?e:t.frameCount??480,frameRate:Number.isFinite(i)?i:t.frameRate??60,quality:Number.isFinite(r)?r:t.quality??.7}}reset(){this.recordingState="idle",this.applyDefaults(),this.status.setMessage("ready to record","neutral")}validate(){const t=Number.parseInt(this.frameCountInput.value,10),e=Number.parseFloat(this.frameRateInput.value);return Number.isFinite(t)&&t>=1&&Number.isFinite(e)&&e>=1}isRecording(){return this.recordingState==="recording"}setRecordingState(t,e){this.recordingState=t;const i=t==="recording";this.frameCountInput.inputElement.disabled=i,this.frameRateInput.inputElement.disabled=i,this.qualityInput.inputElement.disabled=i,this.syncStatus(t,e)}handleProgress(t){this.syncStatus(t.state,t)}syncStatus(t,e){switch(t){case"recording":{const i=e?.frameIndex??0,r=e?.totalFrames??this.resolvePlannedFrameCount();if(r){const s=Math.min(Math.max(0,Math.round(i)),r);this.status.setMessage(`recording ${s}/${r} frames`,"active")}else this.status.setMessage(`recording ${Math.max(0,Math.round(i))} frames`,"active");break}case"completed":this.status.setMessage("saved to disk","active");break;case"error":this.status.setMessage(e?.message?`error: ${e.message}`:"recording failed","alert");break;default:this.status.setMessage("ready to record","neutral")}}resolvePlannedFrameCount(){const t=Number.parseInt(this.frameCountInput.value,10);return Number.isFinite(t)&&t>0?Math.round(t):void 0}applyDefaults(){const t=this._config.defaultOptions??{},e=t.frameCount??480,i=t.frameRate??60,r=t.quality??.7;this.frameCountInput.value=String(e),this.frameRateInput.value=String(i),this.qualityInput.value=String(r),this.frameCountInput.refresh(),this.frameRateInput.refresh()}}class ai{_textmodifier;_state;_events;_exportService;_clipboardService;_definitions;_formats=new Map;_eventUnsubscribers=[];_overlayElement;_optionsContainer;_copyButtonContainer;_positionService;_header=new Y;_formatField=new N({label:"export format",labelFor:"textmode-export-format",variant:"full"});_formatSelect=new ht({id:"textmode-export-format",options:[]});_exportButton=new Yt({label:"download file",fullWidth:!0,variant:"primary"});_copyButton=new Yt({label:"copy to clipboard",fullWidth:!0,variant:"primary"});_currentFormat;_currentBlade;_handleFormatSelectChange=()=>{this._handleFormatChange(this._formatSelect.value)};_handleExportClickSafe=()=>{this._handleExportClick().catch(t=>{console.error("[textmode-export] Export failed",t)})};_handleCopyClickSafe=()=>{this._handleCopyClick().catch(t=>{console.error("[textmode-export] Copy failed",t)})};constructor(t,e,i,r,s){this._textmodifier=t,this._state=i,this._events=r,this._exportService=new Xe(e,r),this._clipboardService=new Qe(e),this._definitions=s,this._currentFormat=i.snapshot.format,this._initializeFormatMap(),this._registerEventHandlers()}$mount(){this._createOverlay(),this._renderStaticContent(),this._positionService=new Ye(this._textmodifier,this._overlayElement),this._positionService.bind(),this._switchFormat(this._currentFormat)}$dispose(){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);for(const t of this._eventUnsubscribers)t();this._eventUnsubscribers.length=0,this._events.$clear();for(const t of this._formats.values())t.blade.destroy();this._formats.clear(),this._currentBlade=void 0,this._overlayElement?.isConnected&&this._overlayElement.remove(),this._positionService?.dispose()}_initializeFormatMap(){for(const t of this._definitions){const e=t.createBlade();this._formats.set(t.format,{definition:t,blade:e,initialized:!1})}}_createOverlay(){this._overlayElement=document.createElement("div"),this._overlayElement.dataset.plugin="textmode-export-overlay",this._overlayElement.classList.add($e,G),document.body.appendChild(this._overlayElement)}_renderStaticContent(){this._header.mount(this._overlayElement);const t=document.createElement("div");t.classList.add(Nt),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(G,Tt),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(G,dt),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)}_prepareFormatOptions(){const t=this._definitions.map(e=>({value:e.format,label:e.label}));this._formatSelect.update({options:t,defaultValue:this._currentFormat})}_registerEventHandlers(){this._eventUnsubscribers.push(this._events.$on("export:request",({format:t})=>{t===this._currentFormat&&(this._state.$set({isBusy:!0,error:void 0}),this._updateExportButton())}),this._events.$on("export:success",({format:t})=>{if(t===this._currentFormat){const e={isBusy:!1};t==="gif"&&(e.gifProgress=void 0),t==="video"&&(e.videoProgress=void 0),this._state.$set(e),this._updateExportButton()}}),this._events.$on("export:error",({format:t,error:e})=>{t===this._currentFormat&&(this._state.$set({isBusy:!1,error:e}),this._updateExportButton())}),this._events.$on("export:progress",({format:t,progress:e})=>{if(e){if(t==="gif"&&this._currentBlade?.blade instanceof pt){const i=e;this._state.$set({gifProgress:i}),this._currentBlade.blade.handleProgress(i)}else if(t==="video"&&this._currentBlade?.blade instanceof ft){const i=e;this._state.$set({videoProgress:i}),this._currentBlade.blade.handleProgress(i)}this._updateExportButton()}}))}_handleFormatChange(t){this._currentFormat=t,this._state.$set({format:t}),this._switchFormat(t),this._events.$emit("format:change",{format:t})}_switchFormat(t){const e=this._formats.get(t);if(!e)throw new Error(`Unknown export format: ${t}`);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()}_updateCopyButtonState(){const t=this._currentBlade?.definition.supportsClipboard??!1;this._copyButtonContainer.style.display=t?"flex":"none",this._copyButton.setDisabled(!t)}async _handleExportClick(){if(!this._currentBlade)return;const t=this._currentBlade.definition.format,e=this._currentBlade.blade.getOptions();if(this._currentBlade.blade.validate()){if(t==="gif"){const i=this._currentBlade.blade;if(i.isRecording())return;i.setRecordingState("recording");try{await this._exportService.$requestExport("gif",e,{onGIFProgress:r=>{i.setRecordingState(r.state,r)}})}catch(r){throw i.setRecordingState("error"),r}return void window.setTimeout(()=>{i.setRecordingState("idle"),this._updateExportButton()},1600)}if(t==="video"){const i=this._currentBlade.blade;if(i.isRecording())return;i.setRecordingState("recording");try{await this._exportService.$requestExport("video",e,{onVideoProgress:r=>{i.setRecordingState(r.state,r)}})}catch(r){throw i.setRecordingState("error"),r}return void window.setTimeout(()=>{i.setRecordingState("idle"),this._updateExportButton()},1600)}this._exportButton.setDisabled(!0),this._exportButton.setLabel("exporting…");try{await this._exportService.$requestExport(t,e)}finally{this._exportButton.setDisabled(!1),this._exportButton.setLabel("download file")}}else console.warn("[textmode-export] Export options failed validation")}async _handleCopyClick(){if(!this._currentBlade||!this._currentBlade.definition.supportsClipboard)return;const t=this._currentBlade.definition.format,e=this._currentBlade.blade.getOptions(),i=this._copyButton.buttonElement.dataset.defaultLabel??"copy to clipboard";this._copyButton.setDisabled(!0),this._copyButton.setLabel("copying…");try{await this._clipboardService.$copy(t,e),this._copyButton.setLabel("copied!")}catch(r){console.error("[textmode-export] Failed to copy to clipboard",r),this._copyButton.setLabel("copy failed!")}finally{window.setTimeout(()=>{this._copyButton.setLabel(i),this._copyButton.setDisabled(!1)},1200)}}_updateExportButton(){if(!this._currentBlade)return;const t=this._currentBlade.definition.format;if(t==="gif"&&this._currentBlade.blade instanceof pt){const i=this._currentBlade.blade,r=this._state.snapshot.gifProgress;if(i.isRecording())if(this._exportButton.setDisabled(!0),r?.totalFrames){const s=r.frameIndex??0;this._exportButton.setLabel(`recording ${s}/${r.totalFrames}`)}else this._exportButton.setLabel("recording…");else this._exportButton.setDisabled(!1),this._exportButton.setLabel("start recording");return}if(t==="video"&&this._currentBlade.blade instanceof ft){const i=this._currentBlade.blade,r=this._state.snapshot.videoProgress;if(i.isRecording())if(this._exportButton.setDisabled(!0),r?.totalFrames){const s=r.frameIndex??0;this._exportButton.setLabel(`recording ${s}/${r.totalFrames} frames`)}else this._exportButton.setLabel("recording…");else this._exportButton.setDisabled(!1),this._exportButton.setLabel("start recording");return}const e=this._state.snapshot.isBusy;this._exportButton.setDisabled(e),this._exportButton.setLabel(e?"exporting…":"download file")}}class ni{_listeners=new Map;$on(t,e){this._listeners.has(t)||this._listeners.set(t,new Set);const i=this._listeners.get(t);return i.add(e),()=>{i.delete(e),i.size===0&&this._listeners.delete(t)}}$emit(t,e){const i=this._listeners.get(t);if(i)for(const r of[...i])try{r(e)}catch(s){console.error("[textmode-export] Event handler failed",s)}}$clear(){this._listeners.clear()}}class oi{_state;_listeners=new Set;constructor(t){this._state=t}get snapshot(){return Object.freeze({...this._state})}$set(t){this._state=Object.assign({},this._state,t);const e=this.snapshot;for(const i of[...this._listeners])try{i(e)}catch(r){console.error("[textmode-export] State listener failed",r)}}$subscribe(t){return this._listeners.add(t),t(this.snapshot),()=>{this._listeners.delete(t)}}$reset(t){this._state=t;const e=this.snapshot;for(const i of[...this._listeners])i(e)}}class se extends R{props;checkbox;labelElement;handleChange=()=>{this.props.defaultChecked=this.checkbox.checked};constructor(t){super(),this.props=t}render(){this.labelElement=document.createElement("label"),this.labelElement.classList.add(Ae),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);const t=document.createElement("span");return t.textContent=this.props.label,this.labelElement.htmlFor=this.props.id??"",this.labelElement.appendChild(this.checkbox),this.labelElement.appendChild(t),this.labelElement}get inputElement(){return this.checkbox}get checked(){return this.checkbox.checked}set checked(t){this.checkbox.checked=t}_onUnmount(){this.checkbox.removeEventListener("change",this.handleChange)}onUpdate(t){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)}}class li extends R{props;input;handleInput=()=>{this.props.defaultValue=this.input.value};constructor(t){super(),this.props=t}render(){return this.input=document.createElement("input"),this.input.type="text",this.input.classList.add(jt),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}get inputElement(){return this.input}get value(){return this.input.value}set value(t){this.input.value=t}_onUnmount(){this.input.removeEventListener("input",this.handleInput)}onUpdate(t){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)}}class di extends K{trailingSpaces=this._manageComponent(new se({label:"preserve trailing spaces",defaultChecked:!1}));emptyCharacter=this._manageComponent(new li({id:"textmode-export-empty-character",defaultValue:" ",maxLength:1}));constructor(t){super(t)}render(){const t=document.createElement("div");t.classList.add(G),this.trailingSpaces.mount(t);const e=new N({label:"empty character",labelFor:"textmode-export-empty-character",variant:"full"});return e.mount(t),this.emptyCharacter.mount(e.root),t}getOptions(){const t=this._config.defaultOptions??{},e=this.emptyCharacter.value||t.emptyCharacter||" ";return{preserveTrailingSpaces:this.trailingSpaces.checked,emptyCharacter:e}}reset(){this.applyDefaults()}validate(){return this.emptyCharacter.value.length<=1}applyDefaults(){const t=this._config.defaultOptions??{};this.trailingSpaces.checked=t.preserveTrailingSpaces??!1,this.emptyCharacter.value=t.emptyCharacter??" "}}class ci extends K{formatSelect=this._manageComponent(new ht({id:"textmode-export-image-format",options:[{value:"png",label:`PNG (${de})`},{value:"jpg",label:`JPG (${ce})`},{value:"webp",label:`WEBP (${ue})`}],defaultValue:"png"}));scaleInput=this._manageComponent(new q({defaultValue:"1",attributes:{min:"0.1",step:"0.1"}}));constructor(t){super(t)}render(){const t=document.createElement("div");t.classList.add(G);const e=new Z("row");e.mount(t);const i=new N({label:"image format",labelFor:"textmode-export-image-format",variant:"compact"});i.mount(e.root),this.formatSelect.mount(i.root);const r=new N({label:"scale",labelFor:"textmode-export-image-scale",variant:"dense"});return r.mount(e.root),this.scaleInput.mount(r.root),this.scaleInput.inputElement.id="textmode-export-image-scale",t}getOptions(){const t=Number.parseFloat(this.scaleInput.value);return{format:this.formatSelect.value,scale:Number.isFinite(t)?t:this._config.defaultOptions?.scale??1}}reset(){this.applyDefaults(),this.scaleInput.refresh()}validate(){const t=Number.parseFloat(this.scaleInput.value);return Number.isFinite(t)&&t>0}applyDefaults(){const t=this._config.defaultOptions??{};this.formatSelect.value=t.format??"png";const e=t.scale??1;this.scaleInput.value=String(e)}}class ui extends K{includeBackground=this._manageComponent(new se({id:"textmode-export-svg-include-backgrounds",label:"include cell backgrounds",defaultChecked:!0}));drawMode=this._manageComponent(new ht({id:"textmode-export-svg-draw-mode",options:[{value:"fill",label:"fill"},{value:"stroke",label:"stroke"}],defaultValue:"fill"}));strokeWidth=this._manageComponent(new q({defaultValue:"1",attributes:{min:"0",step:"0.1"}}));constructor(t){super(t)}render(){const t=document.createElement("div");t.classList.add(G),this.includeBackground.mount(t);const e=new Z("row");e.mount(t);const i=new N({label:"draw mode",labelFor:"textmode-export-svg-draw-mode",variant:"compact"});i.mount(e.root),this.drawMode.mount(i.root);const r=new N({label:"stroke width",labelFor:"textmode-export-svg-stroke-width",variant:"compact"});return r.mount(e.root),this.strokeWidth.mount(r.root),this.strokeWidth.inputElement.id="textmode-export-svg-stroke-width",this.drawMode.selectElement.addEventListener("change",()=>this.updateStrokeControls()),this.updateStrokeControls(),t}getOptions(){return{includeBackgroundRectangles:this.includeBackground.checked,drawMode:this.drawMode.value,strokeWidth:Number.parseFloat(this.strokeWidth.value)}}reset(){this.applyDefaults(),this.updateStrokeControls()}validate(){const t=Number.parseFloat(this.strokeWidth.value);return this.drawMode.value!=="stroke"||Number.isFinite(t)&&t>=0}updateStrokeControls(){const t=this.drawMode.value==="stroke";this.strokeWidth.inputElement.disabled=!t,this.strokeWidth.refresh()}applyDefaults(){const t=this._config.defaultOptions??{};this.includeBackground.checked=t.includeBackgroundRectangles??!0,this.drawMode.value=t.drawMode??"fill";const e=t.strokeWidth??1;this.strokeWidth.value=String(e),this.strokeWidth.refresh()}}const hi=[{format:"txt",label:"plain text (.txt)",supportsClipboard:!0,createBlade:()=>new di({format:"txt",label:"plain text (.txt)",supportsClipboard:!0,defaultOptions:{preserveTrailingSpaces:!1,emptyCharacter:" "}})},{format:"image",label:"image (.png / .jpg / .webp)",supportsClipboard:!0,createBlade:()=>new ci({format:"image",label:"image (.png / .jpg / .webp)",supportsClipboard:!0,defaultOptions:{format:"png",scale:1}})},{format:"svg",label:"vector (.svg)",supportsClipboard:!0,createBlade:()=>new ui({format:"svg",label:"vector (.svg)",supportsClipboard:!0,defaultOptions:{includeBackgroundRectangles:!0,drawMode:"fill",strokeWidth:1}})},{format:"gif",label:"animated GIF (.gif)",supportsClipboard:!1,createBlade:()=>new pt({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 ft({format:"video",label:"video (.webm)",supportsClipboard:!1,defaultOptions:{frameCount:480,frameRate:60,quality:.7}})}];function pi(c,t){const e=hi,i=e[0]?.format,r=new oi((u=>({format:u,isBusy:!1}))(i)),s=new ni,n=new ai(c,t,r,s,e);return n.$mount(),n}const ae=(c={})=>{const t=c.overlay??!0;let e;return{name:"textmode-export",version:"1.0.0",async install(i,r){const s={saveCanvas:async(n={})=>(r.flushDrawCommands(),new yt().$saveImage(i.canvas,n)),copyCanvas:async(n={})=>(r.flushDrawCommands(),new yt().$copyImageToClipboard(i.canvas,n)),saveSVG:(n={})=>{r.flushDrawCommands(),new wt().$saveSVG(i,n)},saveStrings:(n={})=>{r.flushDrawCommands(),new xt().$saveTXT(i,n)},toSVG:(n={})=>(r.flushDrawCommands(),new wt().$generateSVG(i,n)),toString:(n={})=>(r.flushDrawCommands(),new xt().$generateTXT(i,n)),saveGIF:async(n={})=>(r.flushDrawCommands(),new _e(i,r.registerPostDrawHook).$saveGIF(n)),saveWEBM:async(n={})=>(r.flushDrawCommands(),new Se(i,r.registerPostDrawHook).$saveWEBM(n))};Object.assign(i,s),t&&(e=pi(i,s))},async uninstall(i){e?.$dispose(),e=void 0;const r=["saveCanvas","copyCanvas","saveSVG","saveStrings","toSVG","toString","saveGIF","saveWEBM"];for(const s of r)delete i[s]}}};typeof window<"u"&&(window.createTextmodeExportPlugin=ae),Q.createTextmodeExportPlugin=ae,Object.defineProperty(Q,Symbol.toStringTag,{value:"Module"})});
@@ -0,0 +1,23 @@
1
+ import type { TextmodeFramebuffer } from 'textmode.js';
2
+ import type { FramebufferData } from '../svg/types';
3
+ /**
4
+ * Base class for data extraction from textmode framebuffers.
5
+ * Provides common functionality shared between different export formats.
6
+ */
7
+ export declare class DataExtractor {
8
+ /**
9
+ * Extracts pixel data from all framebuffers needed for export
10
+ * @param framebuffer The conversion pipeline containing framebuffers
11
+ * @returns Object containing all pixel data arrays
12
+ */
13
+ $extractFramebufferData(framebuffer: TextmodeFramebuffer): FramebufferData;
14
+ /**
15
+ * Gets character index from character framebuffer pixels
16
+ * @param characterPixels Character framebuffer pixel data
17
+ * @param pixelIndex Index in the pixel array (already multiplied by 4 for RGBA)
18
+ * @param charactersLength Total number of available characters
19
+ * @returns Character index
20
+ */
21
+ $getCharacterIndex(characterPixels: Uint8Array, pixelIndex: number): number;
22
+ }
23
+ //# sourceMappingURL=DataExtractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DataExtractor.d.ts","sourceRoot":"","sources":["../../../../src/exporters/base/DataExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAEpD;;;GAGG;AACH,qBAAa,aAAa;IACtB;;;;OAIG;IACI,uBAAuB,CAAC,WAAW,EAAE,mBAAmB,GAAG,eAAe;IAUjF;;;;;;OAMG;IACI,kBAAkB,CAAC,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM;CAMrF"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Base class for file handling operations.
3
+ * Provides common functionality for downloading files in the browser.
4
+ */
5
+ export declare class FileHandler {
6
+ /**
7
+ * Downloads content as a file
8
+ * @param blob The content to download
9
+ * @param filename The filename to use for the downloaded file
10
+ */
11
+ $downloadFile(blob: Blob, filename: string | undefined): void;
12
+ /**
13
+ * Validates and sanitizes filename for safety and compatibility
14
+ * @param filename The filename to validate
15
+ * @returns Sanitized filename
16
+ */
17
+ private _sanitizeFilename;
18
+ /**
19
+ * Generates a default filename with timestamp
20
+ * @returns Generated filename
21
+ */
22
+ private _generateDefaultFilename;
23
+ }
24
+ //# sourceMappingURL=FileHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileHandler.d.ts","sourceRoot":"","sources":["../../../../src/exporters/base/FileHandler.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,qBAAa,WAAW;IAEpB;;;;OAIG;IACI,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAyBpE;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAuBzB;;;OAGG;IACH,OAAO,CAAC,wBAAwB;CAGnC"}
@@ -0,0 +1,3 @@
1
+ export { DataExtractor } from './DataExtractor';
2
+ export { FileHandler } from './FileHandler';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/exporters/base/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,30 @@
1
+ import type { TextmodePluginAPI, Textmodifier } from 'textmode.js';
2
+ import type { GIFExportOptions } from './types';
3
+ /**
4
+ * Main GIF exporter for the textmode.js library.
5
+ * Orchestrates the GIF export process by coordinating canvas capture,
6
+ * frame processing, and file handling.
7
+ */
8
+ export declare class GIFExporter {
9
+ private readonly _recorder;
10
+ private readonly _textmodifier;
11
+ private readonly _registerPostDrawHook;
12
+ /**
13
+ * Creates an instance of GIFExporter.
14
+ * @param textmodifier The Textmodifier instance to capture frames from
15
+ * @param registerPostDrawHook Function to register post-draw hooks
16
+ */
17
+ constructor(textmodifier: Textmodifier, registerPostDrawHook: TextmodePluginAPI['registerPostDrawHook']);
18
+ /**
19
+ * Captures frames and saves them as a GIF file
20
+ * @param options Export options
21
+ */
22
+ $saveGIF(options?: GIFExportOptions): Promise<void>;
23
+ /**
24
+ * Applies default values to the provided export options
25
+ * @param options User-provided options
26
+ * @returns Complete options with defaults applied
27
+ */
28
+ private _applyDefaultOptions;
29
+ }
30
+ //# sourceMappingURL=GIFExporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GIFExporter.d.ts","sourceRoot":"","sources":["../../../../src/exporters/gif/GIFExporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,KAAK,EAAE,gBAAgB,EAAwB,MAAM,SAAS,CAAC;AAItE;;;;GAIG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAe;IAC7C,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAA4C;IAElF;;;;OAIG;gBACS,YAAY,EAAE,YAAY,EAAE,oBAAoB,EAAE,iBAAiB,CAAC,sBAAsB,CAAC;IAMvG;;;OAGG;IACU,QAAQ,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAwDpE;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;CAc7B"}
@@ -0,0 +1,24 @@
1
+ import type { TextmodePluginAPI } from 'textmode.js';
2
+ import type { GIFExportProgress, GIFFrame, GIFGenerationOptions } from './types';
3
+ /**
4
+ * Records frames from a canvas for GIF generation.
5
+ * Captures frames at specified intervals using textmode.js post-draw hooks.
6
+ */
7
+ export declare class GIFRecorder {
8
+ /** Reusable canvas for frame scaling */
9
+ private _canvas?;
10
+ /** Reusable 2D context for frame scaling */
11
+ private _ctx?;
12
+ /**
13
+ * Records a sequence of frames from the provided canvas.
14
+ *
15
+ * @param canvas - Source canvas to capture frames from
16
+ * @param options - Frame generation options (rate, count, scale)
17
+ * @param registerPostDrawHook - Hook registration function from textmode.js API
18
+ * @param onProgress - Optional callback for recording progress updates
19
+ * @returns Promise resolving to array of captured frames
20
+ * @throws Error if frame capture fails
21
+ */
22
+ $record(canvas: HTMLCanvasElement, options: GIFGenerationOptions, registerPostDrawHook: TextmodePluginAPI['registerPostDrawHook'], onProgress?: (progress: GIFExportProgress) => void): Promise<GIFFrame[]>;
23
+ }
24
+ //# sourceMappingURL=GIFRecorder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GIFRecorder.d.ts","sourceRoot":"","sources":["../../../../src/exporters/gif/GIFRecorder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEjF;;;GAGG;AACH,qBAAa,WAAW;IACtB,wCAAwC;IACxC,OAAO,CAAC,OAAO,CAAC,CAAoB;IAEpC,4CAA4C;IAC5C,OAAO,CAAC,IAAI,CAAC,CAA2B;IAExC;;;;;;;;;OASG;IACU,OAAO,CAClB,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,oBAAoB,EAC7B,oBAAoB,EAAE,iBAAiB,CAAC,sBAAsB,CAAC,EAC/D,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,GACjD,OAAO,CAAC,QAAQ,EAAE,CAAC;CAoIvB"}
@@ -0,0 +1,4 @@
1
+ export { GIFExporter } from './GIFExporter';
2
+ export { GIFRecorder } from './GIFRecorder';
3
+ export type { GIFExportOptions, GIFExportProgress, GIFGenerationOptions, GIFRecordingState } from './types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/exporters/gif/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC"}
@@ -0,0 +1,69 @@
1
+ export type GIFRecordingState = 'idle' | 'recording' | 'completed' | 'error';
2
+ /**
3
+ * Progress information emitted during the GIF export process.
4
+ */
5
+ export type GIFExportProgress = {
6
+ /**
7
+ * Current state of the recording process.
8
+ */
9
+ state: 'idle' | 'recording' | 'completed' | 'error';
10
+ /**
11
+ * Number of frames that have been recorded so far.
12
+ */
13
+ frameIndex?: number;
14
+ /**
15
+ * Total number of frames planned for the recording.
16
+ */
17
+ totalFrames?: number;
18
+ /**
19
+ * Optional status message for UI consumption.
20
+ */
21
+ message?: string;
22
+ };
23
+ /**
24
+ * Options for exporting the textmode content to GIF format.
25
+ */
26
+ export type GIFExportOptions = {
27
+ /**
28
+ * Target filename without extension. Defaults to an auto-generated value.
29
+ */
30
+ filename?: string;
31
+ /**
32
+ * Desired total number of frames to capture. Defaults to `300`.
33
+ */
34
+ frameCount?: number;
35
+ /**
36
+ * Target frame rate for the export, in frames per second. Defaults to `60`.
37
+ */
38
+ frameRate?: number;
39
+ /**
40
+ * Scale factor for the output image.
41
+ *
42
+ * `1.0` = original size, `2.0` = double size, `0.5` = half size.
43
+ *
44
+ * Defaults to `1.0`.
45
+ */
46
+ scale?: number;
47
+ /**
48
+ * GIF loop count. 0 = loop forever. Defaults to `0`.
49
+ */
50
+ repeat?: number;
51
+ /**
52
+ * Progress callback invoked throughout the recording lifecycle.
53
+ */
54
+ onProgress?: (progress: GIFExportProgress) => void;
55
+ };
56
+ export interface GIFGenerationOptions {
57
+ filename?: string;
58
+ frameCount: number;
59
+ frameRate: number;
60
+ scale: number;
61
+ repeat: number;
62
+ }
63
+ export interface GIFFrame {
64
+ imageData: ImageData;
65
+ width: number;
66
+ height: number;
67
+ delayMs?: number;
68
+ }
69
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/exporters/gif/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GACzB,MAAM,GACN,WAAW,GACX,WAAW,GACX,OAAO,CAAC;AAEZ;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;OAEG;IACH,KAAK,EAAE,MAAM,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAC;IACpD;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC;CACpD,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,SAAS,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB"}
@@ -0,0 +1,40 @@
1
+ import { type ImageExportOptions, type ImageGenerationOptions } from './types';
2
+ /**
3
+ * Main image exporter for the textmode.js library.
4
+ * Orchestrates the image export process by coordinating canvas capture,
5
+ * format conversion, and file handling.
6
+ */
7
+ export declare class ImageExporter {
8
+ /**
9
+ * Applies default values to image export options
10
+ * @param options User-provided options
11
+ * @returns Complete options with defaults applied
12
+ */
13
+ private _applyDefaultOptions;
14
+ /**
15
+ * Validates export options and browser support
16
+ * @param options The options to validate
17
+ * @throws Error if options are invalid or format is not supported
18
+ */
19
+ private _validateOptions;
20
+ /**
21
+ * Generates image blob from textmode rendering without saving to file
22
+ * @param canvasData The canvas data containing the rendered textmode graphics
23
+ * @param options Export options
24
+ * @returns Promise that resolves to a Blob containing the image data
25
+ */
26
+ $generateImageBlob(canvas: HTMLCanvasElement, options: ImageGenerationOptions): Promise<Blob>;
27
+ /**
28
+ * Exports image to a downloadable file
29
+ * @param canvas The canvas data containing the rendered textmode graphics
30
+ * @param options Export options
31
+ */
32
+ $saveImage(canvas: HTMLCanvasElement, options?: ImageExportOptions): Promise<void>;
33
+ /**
34
+ * Copies the generated image to the clipboard using the modern Clipboard API
35
+ * @param canvas The source canvas element
36
+ * @param options Export options
37
+ */
38
+ $copyImageToClipboard(canvas: HTMLCanvasElement, options?: ImageExportOptions): Promise<void>;
39
+ }
40
+ //# sourceMappingURL=ImageExporter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImageExporter.d.ts","sourceRoot":"","sources":["../../../../src/exporters/image/ImageExporter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAGjG;;;;GAIG;AACH,qBAAa,aAAa;IAEtB;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;;;OAIG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;;;;OAKG;IACU,kBAAkB,CAC3B,MAAM,EAAE,iBAAiB,EACzB,OAAO,EAAE,sBAAsB,GAChC,OAAO,CAAC,IAAI,CAAC;IAmChB;;;;OAIG;IACU,UAAU,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IASnG;;;;OAIG;IACU,qBAAqB,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;CAajH"}
@@ -0,0 +1,4 @@
1
+ export { ImageExporter } from './ImageExporter';
2
+ export type { ImageExportOptions, ImageGenerationOptions, ImageFormat, } from './types';
3
+ export { IMAGE_MIME_TYPES, IMAGE_EXTENSIONS } from './types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/exporters/image/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,YAAY,EACR,kBAAkB,EAClB,sBAAsB,EACtB,WAAW,GACd,MAAM,SAAS,CAAC;AAEjB,OAAO,EACH,gBAAgB,EAChB,gBAAgB,EACnB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Image-specific type definitions for the textmode.js library.
3
+ */
4
+ /**
5
+ * Supported image formats for export.
6
+ */
7
+ export type ImageFormat = 'png' | 'jpg' | 'webp';
8
+ /**
9
+ * Options for exporting the textmode content to image format.
10
+ */
11
+ export type ImageExportOptions = {
12
+ /**
13
+ * Target filename without extension. Defaults to an auto-generated value.
14
+ */
15
+ filename?: string;
16
+ /**
17
+ * The image format to export *(`'png'`, `'jpg'`, or `'webp'`)*. Defaults to `'png'`.
18
+ */
19
+ format?: 'png' | 'jpg' | 'webp';
20
+ /**
21
+ * Scale factor for the output image.
22
+ *
23
+ * `1.0` = original size, `2.0` = double size, `0.5` = half size.
24
+ *
25
+ * Defaults to `1.0`.
26
+ */
27
+ scale?: number;
28
+ };
29
+ /**
30
+ * Internal options used by image generation (with all defaults applied).
31
+ */
32
+ export interface ImageGenerationOptions {
33
+ filename?: string;
34
+ format: ImageFormat;
35
+ scale: number;
36
+ }
37
+ /**
38
+ * MIME type mapping for image formats.
39
+ */
40
+ export declare const IMAGE_MIME_TYPES: Record<ImageFormat, string>;
41
+ /**
42
+ * File extension mapping for image formats.
43
+ */
44
+ export declare const IMAGE_EXTENSIONS: Record<ImageFormat, string>;
45
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/exporters/image/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAC;IAEhC;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB,CAAA;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAI/C,CAAC;AAEX;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAI/C,CAAC"}
@@ -0,0 +1,70 @@
1
+ import type { SVGCellData, SVGGenerationOptions } from './types';
2
+ import type { TextmodeFont, TextmodeGrid } from 'textmode.js';
3
+ /**
4
+ * Generates SVG content and markup from processed cell data.
5
+ * This class handles the creation of SVG elements, groups, and styling.
6
+ */
7
+ export declare class SVGContentGenerator {
8
+ private _pathGenerator;
9
+ constructor();
10
+ /**
11
+ * Generates the SVG header with metadata
12
+ * @param gridInfo Grid dimensions
13
+ * @returns SVG header string
14
+ */
15
+ $generateSVGHeader(gridInfo: TextmodeGrid): string;
16
+ /**
17
+ * Generates the SVG footer
18
+ * @returns SVG footer string
19
+ */
20
+ $generateSVGFooter(): string;
21
+ /**
22
+ * Generates SVG transform attribute string
23
+ * @param cellData Cell data with transform information
24
+ * @param gridInfo Grid information for center calculations
25
+ * @returns Transform attribute string or empty string
26
+ */
27
+ private _generateTransformAttribute;
28
+ /**
29
+ * Generates background rectangle for a cell
30
+ * @param cellData Cell data
31
+ * @param gridInfo Grid information
32
+ * @param options SVG generation options
33
+ * @returns Background rectangle SVG string or empty string
34
+ */
35
+ private _generateCellBackground;
36
+ /**
37
+ * Generates character path element for a cell
38
+ * @param cellData Cell data
39
+ * @param gridInfo Grid information
40
+ * @param fontInfo Font information
41
+ * @param options SVG generation options
42
+ * @returns Character path SVG string
43
+ */
44
+ private _generateCharacterPath;
45
+ /**
46
+ * Generates complete SVG content for a single cell
47
+ * @param cellData Cell data
48
+ * @param gridInfo Grid information
49
+ * @param fontInfo Font information
50
+ * @param options SVG generation options
51
+ * @returns Complete cell SVG content
52
+ */
53
+ $generateCellContent(cellData: SVGCellData, gridInfo: TextmodeGrid, fontInfo: TextmodeFont, options: SVGGenerationOptions): string;
54
+ /**
55
+ * Generates the complete SVG content from cell data
56
+ * @param cellDataArray Array of cell data
57
+ * @param grid Grid information
58
+ * @param fontInfo Font information
59
+ * @param options SVG generation options
60
+ * @returns Complete SVG string
61
+ */
62
+ $generateSVGContent(cellDataArray: SVGCellData[], grid: TextmodeGrid, fontInfo: TextmodeFont, options: SVGGenerationOptions): string;
63
+ /**
64
+ * Optimizes SVG content by removing empty elements and unnecessary whitespace
65
+ * @param svgContent Raw SVG content
66
+ * @returns Optimized SVG content
67
+ */
68
+ $optimizeSVGContent(svgContent: string): string;
69
+ }
70
+ //# sourceMappingURL=SVGContentGenerator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SVGContentGenerator.d.ts","sourceRoot":"","sources":["../../../../src/exporters/svg/SVGContentGenerator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACR,WAAW,EACX,oBAAoB,EACvB,MAAM,SAAS,CAAC;AAEjB,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE9D;;;GAGG;AACH,qBAAa,mBAAmB;IAC5B,OAAO,CAAC,cAAc,CAAmB;;IAMzC;;;;OAIG;IACI,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM;IAKzD;;;OAGG;IACI,kBAAkB,IAAI,MAAM;IAInC;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B;IAwBnC;;;;;;OAMG;IACH,OAAO,CAAC,uBAAuB;IAgB/B;;;;;;;OAOG;IACH,OAAO,CAAC,sBAAsB;IA8B9B;;;;;;;OAOG;IACI,oBAAoB,CACvB,QAAQ,EAAE,WAAW,EACrB,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,oBAAoB,GAC9B,MAAM;IAiBT;;;;;;;OAOG;IACI,mBAAmB,CACtB,aAAa,EAAE,WAAW,EAAE,EAC5B,IAAI,EAAE,YAAY,EAClB,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,oBAAoB,GAC9B,MAAM;IAeT;;;;OAIG;IACI,mBAAmB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;CAMzD"}