textmode.export.js 1.8.1 → 1.9.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/dist/textmode.document.esm.js +1 -0
- package/dist/textmode.export.esm.js +1 -1
- package/dist/textmode.export.umd.js +1 -1
- package/dist/types/document/constants.d.ts +32 -0
- package/dist/types/document/decode-shapes.d.ts +4 -0
- package/dist/types/document/decode.d.ts +5 -0
- package/dist/types/document/index.d.ts +13 -0
- package/dist/types/document/parse.d.ts +3 -0
- package/dist/types/document/types.d.ts +209 -0
- package/dist/types/document/validation.d.ts +37 -0
- package/dist/types/exporters/base/FileHandler.d.ts +1 -14
- package/dist/types/exporters/base/FilenamePolicy.d.ts +6 -0
- package/dist/types/exporters/base/LayerTarget.d.ts +0 -2
- package/dist/types/exporters/base/frame-sequence/ExclusiveCaptureGate.d.ts +7 -0
- package/dist/types/exporters/{video/VideoFrameSchedule.d.ts → base/frame-sequence/FrameSchedule.d.ts} +2 -2
- package/dist/types/exporters/{video/VideoFrameDriver.d.ts → base/frame-sequence/FrameSequenceDriver.d.ts} +5 -11
- package/dist/types/exporters/base/frame-sequence/errors.d.ts +12 -0
- package/dist/types/exporters/base/frame-sequence/index.d.ts +5 -0
- package/dist/types/exporters/base/frame-sequence/types.d.ts +20 -0
- package/dist/types/exporters/base/frame-sequence/withAbortableTimeout.d.ts +1 -0
- package/dist/types/exporters/base/index.d.ts +3 -0
- package/dist/types/exporters/gif/GIFExporter.d.ts +1 -1
- package/dist/types/exporters/gif/index.d.ts +0 -1
- package/dist/types/exporters/gif/types.d.ts +0 -10
- package/dist/types/exporters/image/types.d.ts +0 -2
- package/dist/types/exporters/json/JSONExporter.d.ts +2 -2
- package/dist/types/exporters/json/types.d.ts +14 -204
- package/dist/types/exporters/svg/types.d.ts +1 -3
- package/dist/types/exporters/txt/types.d.ts +0 -2
- package/dist/types/exporters/video/VideoExporter.d.ts +1 -2
- package/dist/types/exporters/video/errors.d.ts +1 -0
- package/dist/types/exporters/video/types.d.ts +3 -35
- package/dist/types/exporters/zip/FrameSerializerRegistry.d.ts +4 -0
- package/dist/types/exporters/zip/ZIPArchiveWriter.d.ts +21 -0
- package/dist/types/exporters/zip/ZIPExporter.d.ts +32 -0
- package/dist/types/exporters/zip/ZIPManifest.d.ts +12 -0
- package/dist/types/exporters/zip/errors.d.ts +13 -0
- package/dist/types/exporters/zip/index.d.ts +3 -0
- package/dist/types/exporters/zip/types.d.ts +170 -0
- package/dist/types/index.d.ts +5 -24
- package/dist/types/runtime/TextmodeExportController.d.ts +3 -0
- package/dist/types/types.d.ts +43 -24
- package/package.json +50 -11
- package/protocol/textmode-document/2.0/SPECIFICATION.md +233 -0
- package/protocol/textmode-document/2.0/fixture.d.ts +3 -0
- package/protocol/textmode-document/2.0/fixtures/textmode-document-all-v2.json +94 -0
- package/protocol/textmode-document/2.0/fixtures/textmode-document-create-integration-v2.json +112 -0
- package/protocol/textmode-document/2.0/fixtures/textmode-document-selected-v2.json +86 -0
- package/protocol/textmode-document/2.0/fixtures/textmode-layer-all-v1.1.json +46 -0
- package/protocol/textmode-document/2.0/fixtures/textmode-layer-selected-v1.json +38 -0
- package/protocol/textmode-document/2.0/textmode-document-2.0.schema.d.ts +3 -0
- package/protocol/textmode-document/2.0/textmode-document-2.0.schema.json +171 -0
- package/dist/types/exporters/gif/GIFRecorder.d.ts +0 -23
package/README.md
CHANGED
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
<img alt="textmode.export.js — save textmode in every format" src=".github/assets/readme-og.png" />
|
|
6
6
|
|
|
7
|
-
| [](https://www.typescriptlang.org/) [](https://vitejs.dev/) | [](https://code.textmode.art/api/textmode.export.js/) [](https://code.textmode.art/docs/exporting.html)
|
|
7
|
+
| [](https://www.typescriptlang.org/) [](https://vitejs.dev/) | [](https://code.textmode.art/api/textmode.export.js/) [](https://code.textmode.art/docs/exporting.html) | [](https://ko-fi.com/V7V8JG2FY) [](https://github.com/sponsors/humanbydefinition) |
|
|
8
8
|
|:---|:---|:---|
|
|
9
9
|
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
|
-
`textmode.export.js` is an export add-on for [`textmode.js`](https://github.com/humanbydefinition/textmode.js) that turns a sketch into shareable files and data. Through one consistent browser-based API, it produces plain text and JSON, raster images, animated GIFs, WebM and MP4 video,
|
|
12
|
+
`textmode.export.js` is an export add-on for [`textmode.js`](https://github.com/humanbydefinition/textmode.js) that turns a sketch into shareable files and data. Through one consistent browser-based API, it produces plain text and JSON, raster images, animated GIFs, WebM and MP4 video, scalable vector graphics, and ZIP frame sequences.
|
|
13
13
|
|
|
14
14
|
Choose a compact still, structured document data, or a complete animation to suit the way your work will be used. `textmode.export.js` offers programmatic controls for automated workflows and an optional overlay UI for people saving and sharing directly from a sketch.
|
|
15
15
|
|
|
@@ -22,6 +22,11 @@ Choose a compact still, structured document data, or a complete animation to sui
|
|
|
22
22
|
- **Interactive overlay** - Built-in format controls, live layer selectors, runtime defaults, visibility controls, and remembered placement
|
|
23
23
|
- **Clipboard workflows** - Copy text, JSON, SVG, and raster output without downloading files
|
|
24
24
|
- **Controlled animation capture** - Configure frames, frame rate, scale, GIF repetition, video encoding, transparency, progress, and more
|
|
25
|
+
- **ZIP frame sequences** - Archive deterministic PNG, JPEG, WebP, SVG, JSON, or TXT frames with a versioned manifest
|
|
26
|
+
|
|
27
|
+
The [textmode document 2.0 specification](./protocol/textmode-document/2.0/SPECIFICATION.md) defines the editable
|
|
28
|
+
JSON format, its schema, and the published compatibility fixtures. The dependency-free decoder is available from the
|
|
29
|
+
ESM-only `textmode.export.js/document` entry point.
|
|
25
30
|
|
|
26
31
|
## Try it online first
|
|
27
32
|
|
|
@@ -104,3 +109,7 @@ Contribution details and profile links are maintained on the [textmode.js contri
|
|
|
104
109
|
- **[gifenc](https://github.com/mattdesl/gifenc)**
|
|
105
110
|
- Animated GIF encoding by [Matt DesLauriers](https://github.com/mattdesl).
|
|
106
111
|
- License: [MIT License](https://github.com/mattdesl/gifenc/blob/main/LICENSE.md).
|
|
112
|
+
|
|
113
|
+
- **[fflate](https://github.com/101arrowz/fflate)**
|
|
114
|
+
- Streaming ZIP archive creation by [101arrowz](https://github.com/101arrowz).
|
|
115
|
+
- License: [MIT License](https://github.com/101arrowz/fflate/blob/master/LICENSE).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e="textmode.document",t="2.0.0",r="textmode.layer",n="1.0.0",a="1.1.0",o="object-rows-v1",i={maxCells:16777216,maxLayers:4096,maxLayerIdLength:1024,maxCharacterLength:64,maxMetadataStringLength:4096},s=class extends Error{detail;constructor(e){super(e.message),this.detail=e}};function c(e,t){if("string"==typeof e){const r=/^#([0-9a-f]{6}|[0-9a-f]{8})$/i.exec(e);r||b("INVALID_DOCUMENT","Expected #RRGGBB or #RRGGBBAA color notation.",t);const n=r[1];return{r:Number.parseInt(n.slice(0,2),16),g:Number.parseInt(n.slice(2,4),16),b:Number.parseInt(n.slice(4,6),16),a:8===n.length?Number.parseInt(n.slice(6,8),16):255}}const r=I(e,t);return{r:T(r.r,`${t}.r`),g:T(r.g,`${t}.g`),b:T(r.b,`${t}.b`),a:T(r.a,`${t}.a`)}}function l(e,t){const r=I(e,t);return{width:A(r.width,`${t}.width`),height:A(r.height,`${t}.height`)}}function m(e,t){const r=I(e,t);return{cols:A(r.cols,`${t}.cols`),rows:A(r.rows,`${t}.rows`),cellWidth:A(r.cellWidth,`${t}.cellWidth`),cellHeight:A(r.cellHeight,`${t}.cellHeight`)}}function d(e,t,r){const n=I(e,t),a=I(n.generator,`${t}.generator`),o=N(a.name,`${t}.generator.name`,r.maxMetadataStringLength),i=N(n.createdAt,`${t}.createdAt`,r.maxMetadataStringLength);return function(e,t){const r=u.exec(e);r||b("INVALID_DOCUMENT","Expected an uppercase RFC 3339 timestamp with seconds and a timezone.",t);const n=Number(r[1]),a=Number(r[2]);Number(r[3])>[31,n%4!=0||n%100==0&&n%400!=0?28:29,31,30,31,30,31,31,30,31,30,31][a-1]&&b("INVALID_DOCUMENT","Timestamp contains an invalid calendar date.",t)}(i,`${t}.createdAt`),{createdAt:i,generator:{name:o,version:N(a.version,`${t}.generator.version`,r.maxMetadataStringLength)}}}var u=/^([0-9]{4})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](?:\.[0-9]+)?(?:Z|[+-](?:[01][0-9]|2[0-3]):[0-5][0-9])$/;function g(e,t,r){return N(e,t,r.maxLayerIdLength)}function f(e,t,r){const n=$(t.cols,t.cellWidth,`${r}.width`),a=$(t.rows,t.cellHeight,`${r}.height`);e.width===n&&e.height===a||b("INVALID_DOCUMENT",`Canvas must equal the grid pixel extent ${n}x${a}.`,r)}function h(e,t,r){if(void 0===e)return t;if(!Number.isSafeInteger(e)||e<=0)throw new TypeError(`${r} must be a positive safe integer.`);return e}function $(e,t,r){const n=e*t;return Number.isSafeInteger(n)||b("LIMIT_EXCEEDED","Numeric dimensions exceed the safe integer range.",r),n}function x(e){const t=(e%360+360)%360;return Object.is(t,-0)?0:t}function I(e,t){return("object"!=typeof e||null===e||Array.isArray(e))&&b("INVALID_DOCUMENT","Expected an object.",t),e}function E(e,t){return Array.isArray(e)||b("INVALID_DOCUMENT","Expected an array.",t),e}function p(e,t){return"string"!=typeof e&&b("INVALID_DOCUMENT","Expected a string.",t),e}function N(e,t,r){const n=p(e,t);return 0===n.length&&b("INVALID_DOCUMENT","Expected a non-empty string.",t),n.length>r&&b("LIMIT_EXCEEDED",`String length exceeds the configured limit ${r}.`,t),n}function L(e,t,r){const n=p(e,t);return 0===n.length&&b("INVALID_DOCUMENT","Expected a non-empty glyph string.",t),function(e,t){for(let r=0;r<e.length;r++){const n=e.charCodeAt(r);if(n>=55296&&n<=56319){const n=e.charCodeAt(r+1);(!Number.isInteger(n)||n<56320||n>57343)&&b("INVALID_DOCUMENT","Glyph strings must not contain unpaired surrogates.",t),r++;continue}n>=56320&&n<=57343&&b("INVALID_DOCUMENT","Glyph strings must not contain unpaired surrogates.",t)}}(n,t),Array.from(n).length>r.maxCharacterLength&&b("LIMIT_EXCEEDED",`Glyph string scalar count exceeds the configured limit ${r.maxCharacterLength}.`,t),n}function D(e,t){return"boolean"!=typeof e&&b("INVALID_DOCUMENT","Expected a boolean.",t),e}function y(e,t){return"number"==typeof e&&Number.isFinite(e)||b("INVALID_DOCUMENT","Expected a finite number.",t),e}function C(e,t,r,n){const a=y(e,t);return(a<r||a>n)&&b("INVALID_DOCUMENT",`Expected a number from ${r} through ${n}.`,t),a}function M(e,t){return"number"==typeof e&&Number.isSafeInteger(e)||b("INVALID_DOCUMENT","Expected a safe integer.",t),e}function A(e,t){const r=M(e,t);return r<=0&&b("INVALID_DOCUMENT","Expected a positive safe integer.",t),r}function T(e,t){const r=M(e,t);return(r<0||r>255)&&b("INVALID_DOCUMENT","Expected an integer from 0 through 255.",t),r}function b(e,t,r){throw new s({code:e,message:t,...r?{path:r}:{}})}function v(r,n){const a=l(r.canvas,"$.canvas"),o=m(r.grid,"$.grid");f(a,o,"$.canvas");const i=I(r.layer,"$.layer");return{format:e,formatVersion:t,target:"selected",...void 0===r.metadata?{}:{metadata:d(r.metadata,"$.metadata",n.limits)},canvas:a,grid:o,layer:{id:g(i.id,"$.layer.id",n.limits),cells:O(i.cells,o,"$.layer.cells",n)}}}function U(r,n){const a=l(r.canvas,"$.canvas"),o=E(r.layers,"$.layers");0===o.length&&b("INVALID_DOCUMENT","A layer-stack document must contain at least one layer.","$.layers"),o.length>n.limits.maxLayers&&b("LIMIT_EXCEEDED",`Layer count ${o.length} exceeds the configured limit ${n.limits.maxLayers}.`,"$.layers");const i=o.map((e,t)=>function(e,t,r){const n=I(e,t),a=m(n.grid,`${t}.grid`);return{id:g(n.id,`${t}.id`,r.limits),visible:D(n.visible,`${t}.visible`),opacity:C(n.opacity,`${t}.opacity`,0,1),blendMode:N(n.blendMode,`${t}.blendMode`,r.limits.maxMetadataStringLength),offsetX:y(n.offsetX,`${t}.offsetX`),offsetY:y(n.offsetY,`${t}.offsetY`),rotationZ:y(n.rotationZ,`${t}.rotationZ`),grid:a,cells:O(n.cells,a,`${t}.cells`,r)}}(e,`$.layers[${t}]`,n));return f(a,i[0].grid,"$.canvas"),{format:e,formatVersion:t,target:"all",...void 0===r.metadata?{}:{metadata:d(r.metadata,"$.metadata",n.limits)},canvas:a,layers:i}}function O(e,t,r,n){const a=I(e,r),i=p(a.encoding,`${r}.encoding`);"object-rows-v1"!==i&&b("UNSUPPORTED_ENCODING",`Unsupported cell encoding: ${i}.`,`${r}.encoding`);const s=$(t.cols,t.rows,`${r}.rows`);s>n.remainingCells&&b("LIMIT_EXCEEDED",`Document cell count exceeds the configured limit ${n.limits.maxCells}.`,`${r}.rows`),n.remainingCells-=s;const l=E(a.rows,`${r}.rows`);return l.length!==t.rows&&b("INVALID_DOCUMENT",`Expected ${t.rows} cell rows, received ${l.length}.`,`${r}.rows`),{encoding:o,rows:l.map((e,a)=>{const o=`${r}.rows[${a}]`,i=E(e,o);return i.length!==t.cols&&b("INVALID_DOCUMENT",`Expected ${t.cols} cells, received ${i.length}.`,o),i.map((e,t)=>function(e,t,r,n,a){const o=I(e,n),i=M(o.x,`${n}.x`),s=M(o.y,`${n}.y`);i===t&&s===r||b("INVALID_DOCUMENT",`Cell coordinates must match their row position (${t}, ${r}).`,n);const l=I(o.transform,`${n}.transform`);return{x:i,y:s,character:L(o.character,`${n}.character`,a.limits),foreground:c(o.foreground,`${n}.foreground`),background:c(o.background,`${n}.background`),transform:{invert:D(l.invert,`${n}.transform.invert`),flipX:D(l.flipX,`${n}.transform.flipX`),flipY:D(l.flipY,`${n}.transform.flipY`),rotation:x(y(l.rotation,`${n}.transform.rotation`))}}}(e,t,a,`${o}[${t}]`,n))})}}function _(e){try{return{ok:!0,value:JSON.parse(e)}}catch(t){return{ok:!1,error:{code:"INVALID_JSON",message:t instanceof Error?t.message:"The file is not valid JSON."}}}}function V(e,t={}){try{const r=function(e){return{maxCells:h(e?.maxCells,i.maxCells,"maxCells"),maxLayers:h(e?.maxLayers,i.maxLayers,"maxLayers"),maxLayerIdLength:h(e?.maxLayerIdLength,i.maxLayerIdLength,"maxLayerIdLength"),maxCharacterLength:h(e?.maxCharacterLength,i.maxCharacterLength,"maxCharacterLength"),maxMetadataStringLength:h(e?.maxMetadataStringLength,i.maxMetadataStringLength,"maxMetadataStringLength")}}(t.limits),n={limits:r,remainingCells:r.maxCells},a=I(e,"$"),o=p(a.format,"$.format");return"textmode.document"===o?{ok:!0,document:S(a,n),warnings:[]}:"textmode.layer"===o&&(t.acceptLegacy??!0)?{ok:!0,document:R(a,n),warnings:[{code:"LEGACY_DOCUMENT_MIGRATED",message:"Migrated a legacy textmode.layer document to textmode.document 2.0.0."}]}:{ok:!1,error:{code:"UNSUPPORTED_FORMAT",message:`Unsupported textmode document format: ${o}.`,..."$.format"?{path:"$.format"}:{}}}}catch(r){if(r instanceof s)return{ok:!1,error:r.detail};throw r}}function w(e,t={}){const r=_(e);return r.ok?V(r.value,t):r}function S(e,t){const r=p(e.formatVersion,"$.formatVersion");"2.0.0"!==r&&b("UNSUPPORTED_VERSION",`Unsupported textmode.document version: ${r}.`,"$.formatVersion");const n=p(e.target,"$.target");return"selected"===n?v(e,t):"all"===n?U(e,t):void b("UNSUPPORTED_TARGET",`Unsupported textmode document target: ${n}.`,"$.target")}function R(e,t){const r=p(e.formatVersion,"$.formatVersion");return"1.0.0"===r?v(e,t):"1.1.0"===r?U(e,t):void b("UNSUPPORTED_VERSION",`Unsupported legacy textmode.layer version: ${r}.`,"$.formatVersion")}export{i as DEFAULT_TEXTMODE_DOCUMENT_LIMITS,a as LEGACY_TEXTMODE_ALL_VERSION,r as LEGACY_TEXTMODE_LAYER_FORMAT,n as LEGACY_TEXTMODE_SELECTED_VERSION,e as TEXTMODE_DOCUMENT_FORMAT,t as TEXTMODE_DOCUMENT_FORMAT_VERSION,o as TEXTMODE_OBJECT_ROWS_ENCODING,V as decodeTextmodeDocument,_ as parseJSON,w as parseTextmodeDocumentJSON};
|