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.
- package/README.md +166 -0
- package/dist/textmode.export.esm.js +2061 -0
- package/dist/textmode.export.umd.js +3 -0
- package/dist/types/exporters/base/DataExtractor.d.ts +23 -0
- package/dist/types/exporters/base/DataExtractor.d.ts.map +1 -0
- package/dist/types/exporters/base/FileHandler.d.ts +24 -0
- package/dist/types/exporters/base/FileHandler.d.ts.map +1 -0
- package/dist/types/exporters/base/index.d.ts +3 -0
- package/dist/types/exporters/base/index.d.ts.map +1 -0
- package/dist/types/exporters/gif/GIFExporter.d.ts +30 -0
- package/dist/types/exporters/gif/GIFExporter.d.ts.map +1 -0
- package/dist/types/exporters/gif/GIFRecorder.d.ts +24 -0
- package/dist/types/exporters/gif/GIFRecorder.d.ts.map +1 -0
- package/dist/types/exporters/gif/index.d.ts +4 -0
- package/dist/types/exporters/gif/index.d.ts.map +1 -0
- package/dist/types/exporters/gif/types.d.ts +69 -0
- package/dist/types/exporters/gif/types.d.ts.map +1 -0
- package/dist/types/exporters/image/ImageExporter.d.ts +40 -0
- package/dist/types/exporters/image/ImageExporter.d.ts.map +1 -0
- package/dist/types/exporters/image/index.d.ts +4 -0
- package/dist/types/exporters/image/index.d.ts.map +1 -0
- package/dist/types/exporters/image/types.d.ts +45 -0
- package/dist/types/exporters/image/types.d.ts.map +1 -0
- package/dist/types/exporters/svg/SVGContentGenerator.d.ts +70 -0
- package/dist/types/exporters/svg/SVGContentGenerator.d.ts.map +1 -0
- package/dist/types/exporters/svg/SVGDataExtractor.d.ts +34 -0
- package/dist/types/exporters/svg/SVGDataExtractor.d.ts.map +1 -0
- package/dist/types/exporters/svg/SVGExporter.d.ts +29 -0
- package/dist/types/exporters/svg/SVGExporter.d.ts.map +1 -0
- package/dist/types/exporters/svg/SVGPathGenerator.d.ts +50 -0
- package/dist/types/exporters/svg/SVGPathGenerator.d.ts.map +1 -0
- package/dist/types/exporters/svg/index.d.ts +6 -0
- package/dist/types/exporters/svg/index.d.ts.map +1 -0
- package/dist/types/exporters/svg/types.d.ts +111 -0
- package/dist/types/exporters/svg/types.d.ts.map +1 -0
- package/dist/types/exporters/txt/TXTExporter.d.ts +36 -0
- package/dist/types/exporters/txt/TXTExporter.d.ts.map +1 -0
- package/dist/types/exporters/txt/index.d.ts +3 -0
- package/dist/types/exporters/txt/index.d.ts.map +1 -0
- package/dist/types/exporters/txt/types.d.ts +37 -0
- package/dist/types/exporters/txt/types.d.ts.map +1 -0
- package/dist/types/exporters/video/VideoExporter.d.ts +30 -0
- package/dist/types/exporters/video/VideoExporter.d.ts.map +1 -0
- package/dist/types/exporters/video/VideoRecorder.d.ts +28 -0
- package/dist/types/exporters/video/VideoRecorder.d.ts.map +1 -0
- package/dist/types/exporters/video/index.d.ts +4 -0
- package/dist/types/exporters/video/index.d.ts.map +1 -0
- package/dist/types/exporters/video/types.d.ts +64 -0
- package/dist/types/exporters/video/types.d.ts.map +1 -0
- package/dist/types/index.d.ts +35 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/overlay/blades/Blade.d.ts +20 -0
- package/dist/types/overlay/blades/Blade.d.ts.map +1 -0
- package/dist/types/overlay/blades/GIFBlade.d.ts +23 -0
- package/dist/types/overlay/blades/GIFBlade.d.ts.map +1 -0
- package/dist/types/overlay/blades/ImageBlade.d.ts +14 -0
- package/dist/types/overlay/blades/ImageBlade.d.ts.map +1 -0
- package/dist/types/overlay/blades/SVGBlade.d.ts +16 -0
- package/dist/types/overlay/blades/SVGBlade.d.ts.map +1 -0
- package/dist/types/overlay/blades/TextBlade.d.ts +14 -0
- package/dist/types/overlay/blades/TextBlade.d.ts.map +1 -0
- package/dist/types/overlay/blades/VideoBlade.d.ts +22 -0
- package/dist/types/overlay/blades/VideoBlade.d.ts.map +1 -0
- package/dist/types/overlay/blades/index.d.ts +7 -0
- package/dist/types/overlay/blades/index.d.ts.map +1 -0
- package/dist/types/overlay/components/actions/Button.d.ts +19 -0
- package/dist/types/overlay/components/actions/Button.d.ts.map +1 -0
- package/dist/types/overlay/components/base/Component.d.ts +18 -0
- package/dist/types/overlay/components/base/Component.d.ts.map +1 -0
- package/dist/types/overlay/components/base/Container.d.ts +9 -0
- package/dist/types/overlay/components/base/Container.d.ts.map +1 -0
- package/dist/types/overlay/components/base/Field.d.ts +17 -0
- package/dist/types/overlay/components/base/Field.d.ts.map +1 -0
- package/dist/types/overlay/components/display/Header.d.ts +8 -0
- package/dist/types/overlay/components/display/Header.d.ts.map +1 -0
- package/dist/types/overlay/components/display/StatusDisplay.d.ts +19 -0
- package/dist/types/overlay/components/display/StatusDisplay.d.ts.map +1 -0
- package/dist/types/overlay/components/inputs/CheckboxInput.d.ts +20 -0
- package/dist/types/overlay/components/inputs/CheckboxInput.d.ts.map +1 -0
- package/dist/types/overlay/components/inputs/NumberInput.d.ts +32 -0
- package/dist/types/overlay/components/inputs/NumberInput.d.ts.map +1 -0
- package/dist/types/overlay/components/inputs/RangeInput.d.ts +35 -0
- package/dist/types/overlay/components/inputs/RangeInput.d.ts.map +1 -0
- package/dist/types/overlay/components/inputs/SelectInput.d.ts +25 -0
- package/dist/types/overlay/components/inputs/SelectInput.d.ts.map +1 -0
- package/dist/types/overlay/components/inputs/TextInput.d.ts +19 -0
- package/dist/types/overlay/components/inputs/TextInput.d.ts.map +1 -0
- package/dist/types/overlay/core/EventBus.d.ts +10 -0
- package/dist/types/overlay/core/EventBus.d.ts.map +1 -0
- package/dist/types/overlay/core/OverlayController.d.ts +46 -0
- package/dist/types/overlay/core/OverlayController.d.ts.map +1 -0
- package/dist/types/overlay/core/StateManager.d.ts +11 -0
- package/dist/types/overlay/core/StateManager.d.ts.map +1 -0
- package/dist/types/overlay/formatRegistry.d.ts +4 -0
- package/dist/types/overlay/formatRegistry.d.ts.map +1 -0
- package/dist/types/overlay/index.d.ts +13 -0
- package/dist/types/overlay/index.d.ts.map +1 -0
- package/dist/types/overlay/models/FormatDefinition.d.ts +9 -0
- package/dist/types/overlay/models/FormatDefinition.d.ts.map +1 -0
- package/dist/types/overlay/models/OverlayEvents.d.ts +26 -0
- package/dist/types/overlay/models/OverlayEvents.d.ts.map +1 -0
- package/dist/types/overlay/models/OverlayState.d.ts +16 -0
- package/dist/types/overlay/models/OverlayState.d.ts.map +1 -0
- package/dist/types/overlay/services/ClipboardService.d.ts +8 -0
- package/dist/types/overlay/services/ClipboardService.d.ts.map +1 -0
- package/dist/types/overlay/services/ExportService.d.ts +18 -0
- package/dist/types/overlay/services/ExportService.d.ts.map +1 -0
- package/dist/types/overlay/services/PositionService.d.ts +14 -0
- package/dist/types/overlay/services/PositionService.d.ts.map +1 -0
- package/dist/types/overlay/types.d.ts +14 -0
- package/dist/types/overlay/types.d.ts.map +1 -0
- package/dist/types/overlay/utils/classes.d.ts +35 -0
- package/dist/types/overlay/utils/classes.d.ts.map +1 -0
- package/dist/types/types.d.ts +63 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/utils/pixels.d.ts +13 -0
- package/dist/types/utils/pixels.d.ts.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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"}
|