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.
Files changed (54) hide show
  1. package/README.md +11 -2
  2. package/dist/textmode.document.esm.js +1 -0
  3. package/dist/textmode.export.esm.js +1 -1
  4. package/dist/textmode.export.umd.js +1 -1
  5. package/dist/types/document/constants.d.ts +32 -0
  6. package/dist/types/document/decode-shapes.d.ts +4 -0
  7. package/dist/types/document/decode.d.ts +5 -0
  8. package/dist/types/document/index.d.ts +13 -0
  9. package/dist/types/document/parse.d.ts +3 -0
  10. package/dist/types/document/types.d.ts +209 -0
  11. package/dist/types/document/validation.d.ts +37 -0
  12. package/dist/types/exporters/base/FileHandler.d.ts +1 -14
  13. package/dist/types/exporters/base/FilenamePolicy.d.ts +6 -0
  14. package/dist/types/exporters/base/LayerTarget.d.ts +0 -2
  15. package/dist/types/exporters/base/frame-sequence/ExclusiveCaptureGate.d.ts +7 -0
  16. package/dist/types/exporters/{video/VideoFrameSchedule.d.ts → base/frame-sequence/FrameSchedule.d.ts} +2 -2
  17. package/dist/types/exporters/{video/VideoFrameDriver.d.ts → base/frame-sequence/FrameSequenceDriver.d.ts} +5 -11
  18. package/dist/types/exporters/base/frame-sequence/errors.d.ts +12 -0
  19. package/dist/types/exporters/base/frame-sequence/index.d.ts +5 -0
  20. package/dist/types/exporters/base/frame-sequence/types.d.ts +20 -0
  21. package/dist/types/exporters/base/frame-sequence/withAbortableTimeout.d.ts +1 -0
  22. package/dist/types/exporters/base/index.d.ts +3 -0
  23. package/dist/types/exporters/gif/GIFExporter.d.ts +1 -1
  24. package/dist/types/exporters/gif/index.d.ts +0 -1
  25. package/dist/types/exporters/gif/types.d.ts +0 -10
  26. package/dist/types/exporters/image/types.d.ts +0 -2
  27. package/dist/types/exporters/json/JSONExporter.d.ts +2 -2
  28. package/dist/types/exporters/json/types.d.ts +14 -204
  29. package/dist/types/exporters/svg/types.d.ts +1 -3
  30. package/dist/types/exporters/txt/types.d.ts +0 -2
  31. package/dist/types/exporters/video/VideoExporter.d.ts +1 -2
  32. package/dist/types/exporters/video/errors.d.ts +1 -0
  33. package/dist/types/exporters/video/types.d.ts +3 -35
  34. package/dist/types/exporters/zip/FrameSerializerRegistry.d.ts +4 -0
  35. package/dist/types/exporters/zip/ZIPArchiveWriter.d.ts +21 -0
  36. package/dist/types/exporters/zip/ZIPExporter.d.ts +32 -0
  37. package/dist/types/exporters/zip/ZIPManifest.d.ts +12 -0
  38. package/dist/types/exporters/zip/errors.d.ts +13 -0
  39. package/dist/types/exporters/zip/index.d.ts +3 -0
  40. package/dist/types/exporters/zip/types.d.ts +170 -0
  41. package/dist/types/index.d.ts +5 -24
  42. package/dist/types/runtime/TextmodeExportController.d.ts +3 -0
  43. package/dist/types/types.d.ts +43 -24
  44. package/package.json +50 -11
  45. package/protocol/textmode-document/2.0/SPECIFICATION.md +233 -0
  46. package/protocol/textmode-document/2.0/fixture.d.ts +3 -0
  47. package/protocol/textmode-document/2.0/fixtures/textmode-document-all-v2.json +94 -0
  48. package/protocol/textmode-document/2.0/fixtures/textmode-document-create-integration-v2.json +112 -0
  49. package/protocol/textmode-document/2.0/fixtures/textmode-document-selected-v2.json +86 -0
  50. package/protocol/textmode-document/2.0/fixtures/textmode-layer-all-v1.1.json +46 -0
  51. package/protocol/textmode-document/2.0/fixtures/textmode-layer-selected-v1.json +38 -0
  52. package/protocol/textmode-document/2.0/textmode-document-2.0.schema.d.ts +3 -0
  53. package/protocol/textmode-document/2.0/textmode-document-2.0.schema.json +171 -0
  54. 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
- | [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![Vite](https://img.shields.io/badge/Vite-646CFF?logo=vite&logoColor=white)](https://vitejs.dev/) | [![API](https://img.shields.io/badge/API-typedoc-3178c6?logo=typescript&logoColor=white)](https://code.textmode.art/api/textmode.export.js/) [![docs](https://img.shields.io/badge/docs-vitepress-646cff?logo=vitepress&logoColor=white)](https://code.textmode.art/docs/exporting.html) [![Discord](https://img.shields.io/discord/1357070706181017691?color=5865F2&label=Discord&logo=discord&logoColor=white)](https://discord.gg/sjrw8QXNks) | [![ko-fi](https://shields.io/badge/ko--fi-donate-ff5f5f?logo=ko-fi)](https://ko-fi.com/V7V8JG2FY) [![GitHub-sponsors](https://img.shields.io/badge/sponsor-30363D?logo=GitHub-Sponsors&logoColor=#EA4AAA)](https://github.com/sponsors/humanbydefinition) |
7
+ | [![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/) [![Vite](https://img.shields.io/badge/Vite-646CFF?logo=vite&logoColor=white)](https://vitejs.dev/) | [![API](https://img.shields.io/badge/API-typedoc-3178c6?logo=typescript&logoColor=white)](https://code.textmode.art/api/textmode.export.js/) [![docs](https://img.shields.io/badge/docs-vitepress-646cff?logo=vitepress&logoColor=white)](https://code.textmode.art/docs/exporting.html) | [![ko-fi](https://shields.io/badge/ko--fi-donate-ff5f5f?logo=ko-fi)](https://ko-fi.com/V7V8JG2FY) [![GitHub-sponsors](https://img.shields.io/badge/sponsor-30363D?logo=GitHub-Sponsors&logoColor=#EA4AAA)](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, and scalable vector graphics.
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};