textmode.export.js 1.8.1 → 1.9.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/README.md +7 -2
  2. package/dist/textmode.export.esm.js +1 -1
  3. package/dist/textmode.export.umd.js +1 -1
  4. package/dist/types/exporters/base/FileHandler.d.ts +1 -14
  5. package/dist/types/exporters/base/FilenamePolicy.d.ts +6 -0
  6. package/dist/types/exporters/base/frame-sequence/ExclusiveCaptureGate.d.ts +7 -0
  7. package/dist/types/exporters/{video/VideoFrameSchedule.d.ts → base/frame-sequence/FrameSchedule.d.ts} +2 -2
  8. package/dist/types/exporters/{video/VideoFrameDriver.d.ts → base/frame-sequence/FrameSequenceDriver.d.ts} +5 -11
  9. package/dist/types/exporters/base/frame-sequence/errors.d.ts +12 -0
  10. package/dist/types/exporters/base/frame-sequence/index.d.ts +5 -0
  11. package/dist/types/exporters/base/frame-sequence/types.d.ts +20 -0
  12. package/dist/types/exporters/base/frame-sequence/withAbortableTimeout.d.ts +1 -0
  13. package/dist/types/exporters/base/index.d.ts +3 -0
  14. package/dist/types/exporters/gif/GIFExporter.d.ts +1 -1
  15. package/dist/types/exporters/gif/index.d.ts +0 -1
  16. package/dist/types/exporters/gif/types.d.ts +0 -6
  17. package/dist/types/exporters/json/JSONExporter.d.ts +2 -2
  18. package/dist/types/exporters/video/VideoExporter.d.ts +1 -2
  19. package/dist/types/exporters/video/errors.d.ts +1 -0
  20. package/dist/types/exporters/video/types.d.ts +3 -15
  21. package/dist/types/exporters/zip/FrameSerializerRegistry.d.ts +4 -0
  22. package/dist/types/exporters/zip/ZIPArchiveWriter.d.ts +21 -0
  23. package/dist/types/exporters/zip/ZIPExporter.d.ts +32 -0
  24. package/dist/types/exporters/zip/ZIPManifest.d.ts +12 -0
  25. package/dist/types/exporters/zip/errors.d.ts +14 -0
  26. package/dist/types/exporters/zip/index.d.ts +3 -0
  27. package/dist/types/exporters/zip/types.d.ts +177 -0
  28. package/dist/types/index.d.ts +5 -0
  29. package/dist/types/runtime/TextmodeExportController.d.ts +3 -0
  30. package/dist/types/types.d.ts +47 -0
  31. package/package.json +5 -2
  32. package/dist/types/exporters/gif/GIFRecorder.d.ts +0 -23
@@ -9,18 +9,5 @@ export declare class FileHandler {
9
9
  * @param blob The content to download
10
10
  * @param filename The filename to use for the downloaded file
11
11
  */
12
- $downloadFile(blob: Blob, filename: string | undefined): void;
13
- /**
14
- * Validates and sanitizes filename for safety and compatibility
15
- *
16
- * @param filename The filename to validate
17
- * @returns Sanitized filename
18
- */
19
- private _sanitizeFilename;
20
- /**
21
- * Generates a default filename with timestamp
22
- *
23
- * @returns Generated filename
24
- */
25
- private _generateDefaultFilename;
12
+ $downloadFile(blob: Blob, filename: string | undefined, extension?: `.${string}`): void;
26
13
  }
@@ -0,0 +1,6 @@
1
+ /** Central filename normalization and canonical-extension policy. */
2
+ export declare class FilenamePolicy {
3
+ $resolve(filename: string | undefined, extension: `.${string}`, createdAt?: Date): string;
4
+ $sanitizeStem(filename: string | undefined, createdAt?: Date, maximumBytes?: number): string;
5
+ $defaultStem(createdAt?: Date): string;
6
+ }
@@ -0,0 +1,7 @@
1
+ /** Rejects overlapping deterministic captures for one installed plugin instance. */
2
+ export declare class ExclusiveCaptureGate {
3
+ private _active;
4
+ private _disposed;
5
+ acquire(): () => void;
6
+ dispose(): void;
7
+ }
@@ -1,8 +1,8 @@
1
- export interface VideoFrameTiming {
1
+ export interface FrameTiming {
2
2
  startSeconds: number;
3
3
  centerSeconds: number;
4
4
  endSeconds: number;
5
5
  durationSeconds: number;
6
6
  }
7
7
  /** Produces one stable microsecond-rounded interval for an output frame. */
8
- export declare function videoFrameTiming(frameIndex: number, frameRate: number): VideoFrameTiming;
8
+ export declare function frameTiming(frameIndex: number, frameRate: number): FrameTiming;
@@ -1,13 +1,7 @@
1
1
  import type { Textmodifier } from 'textmode.js';
2
- import type { VideoRenderFrameOptions } from './types';
3
- export type PostDrawSubscription = (callback: () => void) => () => void;
4
- /**
5
- * Drives deterministic video export frames through public textmode APIs.
6
- *
7
- * The visible textmode canvas remains untouched. Each presented frame is copied
8
- * into a staging canvas sized for the requested video output.
9
- */
10
- export declare class VideoFrameDriver {
2
+ import type { FrameSequenceRenderOptions, FrameSequenceStagingSurface, PostDrawSubscription } from './types';
3
+ /** Drives deterministic export frames through public textmode.js lifecycle APIs. */
4
+ export declare class FrameSequenceDriver {
11
5
  readonly canvas: HTMLCanvasElement;
12
6
  private readonly _textmodifier;
13
7
  private readonly _registerPostDrawHook;
@@ -16,8 +10,8 @@ export declare class VideoFrameDriver {
16
10
  private _pendingFrame;
17
11
  private _syntheticFrameCount;
18
12
  private _syntheticMillis;
19
- constructor(textmodifier: Textmodifier, registerPostDrawHook: PostDrawSubscription, width: number, height: number);
20
- $render(options: VideoRenderFrameOptions): Promise<void>;
13
+ constructor(textmodifier: Textmodifier, registerPostDrawHook: PostDrawSubscription, stagingSurface?: FrameSequenceStagingSurface);
14
+ $render(options: FrameSequenceRenderOptions): Promise<void>;
21
15
  private _renderOneFrame;
22
16
  private _capturePendingFrame;
23
17
  private _createStagingCanvas;
@@ -0,0 +1,12 @@
1
+ export type FrameSequenceErrorCode = 'FRAME_SEQUENCE_ABORTED' | 'FRAME_SEQUENCE_TIMEOUT' | 'FRAME_SEQUENCE_BUSY' | 'FRAME_SEQUENCE_DISPOSED';
2
+ /** Internal error used by deterministic capture infrastructure. */
3
+ export declare class FrameSequenceError extends Error {
4
+ readonly code: FrameSequenceErrorCode;
5
+ readonly cause?: unknown;
6
+ constructor(code: FrameSequenceErrorCode, message: string, cause?: unknown);
7
+ }
8
+ export declare function createFrameSequenceAbortError(): FrameSequenceError;
9
+ export declare function createFrameSequenceTimeoutError(message: string, cause?: unknown): FrameSequenceError;
10
+ export declare function createFrameSequenceBusyError(): FrameSequenceError;
11
+ export declare function createFrameSequenceDisposedError(): FrameSequenceError;
12
+ export declare function isFrameSequenceAbortError(error: unknown): boolean;
@@ -0,0 +1,5 @@
1
+ export { ExclusiveCaptureGate } from './ExclusiveCaptureGate';
2
+ export { FrameSequenceDriver } from './FrameSequenceDriver';
3
+ export { frameTiming } from './FrameSchedule';
4
+ export { FrameSequenceError, createFrameSequenceAbortError, isFrameSequenceAbortError } from './errors';
5
+ export type { FrameSequenceDriverLike, FrameSequenceRenderOptions, FrameSequenceStagingSurface, PostDrawSubscription, } from './types';
@@ -0,0 +1,20 @@
1
+ import type { PrepareExportFrame } from '../FramePreparation';
2
+ export type PostDrawSubscription = (callback: () => void) => () => void;
3
+ export interface FrameSequenceStagingSurface {
4
+ width: number;
5
+ height: number;
6
+ }
7
+ export interface FrameSequenceRenderOptions {
8
+ frameCount: number;
9
+ frameRate: number;
10
+ signal?: AbortSignal;
11
+ prepareFrame?: PrepareExportFrame;
12
+ onFrame(frame: {
13
+ frameIndex: number;
14
+ canvas: HTMLCanvasElement;
15
+ }): Promise<void> | void;
16
+ }
17
+ export interface FrameSequenceDriverLike {
18
+ readonly canvas: HTMLCanvasElement;
19
+ $render(options: FrameSequenceRenderOptions): Promise<void>;
20
+ }
@@ -0,0 +1 @@
1
+ export declare function withAbortableFrameTimeout<T>(promise: Promise<T>, message: string, signal: AbortSignal | undefined, timeoutMs: number): Promise<T>;
@@ -1,6 +1,9 @@
1
1
  export { DataExtractor } from './DataExtractor';
2
2
  export { FileHandler } from './FileHandler';
3
+ export { FilenamePolicy } from './FilenamePolicy';
3
4
  export { getEncodedGlyphValueFromColor, resolveGlyphByEncodedValue } from './CharacterResolver';
4
5
  export { createLayerTargetProvider, getLayerTargetId, getLayerTargetOptions, resolveLayerExportTarget, resolveLayerStackExportTargets, } from './LayerTarget';
5
6
  export type { LayerExportOptions, LayerTargetOption, LayerTargetProvider, ResolvedLayerExportTarget, ResolvedLayerStackExportTarget, } from './LayerTarget';
6
7
  export type { ExportFrameContext, PrepareExportFrame } from './FramePreparation';
8
+ export { ExclusiveCaptureGate, FrameSequenceDriver, FrameSequenceError, createFrameSequenceAbortError, frameTiming, isFrameSequenceAbortError, } from './frame-sequence';
9
+ export type { FrameSequenceDriverLike, FrameSequenceRenderOptions, FrameSequenceStagingSurface, PostDrawSubscription, } from './frame-sequence';
@@ -1,5 +1,5 @@
1
1
  import type { Textmodifier } from 'textmode.js';
2
- import { type PostDrawSubscription } from '../video/VideoFrameDriver';
2
+ import { type PostDrawSubscription } from '../base';
3
3
  import type { GIFExportOptions } from './types';
4
4
  /** Deterministic, bounded-memory GIF exporter. */
5
5
  export declare class GIFExporter {
@@ -1,3 +1,2 @@
1
1
  export { GIFExporter } from './GIFExporter';
2
- export { GIFRecorder } from './GIFRecorder';
3
2
  export type { GIFExportOptions, GIFExportProgress, GIFGenerationOptions, GIFRecordingState } from './types';
@@ -103,9 +103,3 @@ export interface GIFGenerationOptions {
103
103
  signal?: AbortSignal;
104
104
  prepareFrame?: PrepareExportFrame;
105
105
  }
106
- export interface GIFFrame {
107
- imageData: ImageData;
108
- width: number;
109
- height: number;
110
- delayMs?: number;
111
- }
@@ -41,7 +41,7 @@ export declare class JSONExporter {
41
41
  * @param options Export options
42
42
  * @returns Structured JSON document for the selected layer or layer stack
43
43
  */
44
- $generateJSONData(textmodifier: Textmodifier, options?: JSONExportOptions): TextmodeDocumentJSON;
44
+ $generateJSONData(textmodifier: Textmodifier, options?: JSONExportOptions, createdAt?: Date): TextmodeDocumentJSON;
45
45
  /**
46
46
  * Generates the serialized JSON string for the selected layer or layer stack.
47
47
  *
@@ -49,7 +49,7 @@ export declare class JSONExporter {
49
49
  * @param options Export options
50
50
  * @returns Serialized JSON string
51
51
  */
52
- $generateJSONString(textmodifier: Textmodifier, options?: JSONExportOptions): string;
52
+ $generateJSONString(textmodifier: Textmodifier, options?: JSONExportOptions, createdAt?: Date): string;
53
53
  /**
54
54
  * Exports the generated JSON to a downloadable file.
55
55
  *
@@ -1,5 +1,5 @@
1
1
  import type { Textmodifier } from 'textmode.js';
2
- import { type PostDrawSubscription } from './VideoFrameDriver';
2
+ import { type PostDrawSubscription } from '../base';
3
3
  import type { VideoExportOptions } from './types';
4
4
  /** Main video exporter for the textmode.js library. */
5
5
  export declare class VideoExporter {
@@ -20,6 +20,5 @@ export declare class VideoExporter {
20
20
  private _hardwareAcceleration;
21
21
  private _keyFrameInterval;
22
22
  private _currentPixelDensity;
23
- private _withExtension;
24
23
  private _createFileSystemDestination;
25
24
  }
@@ -7,3 +7,4 @@ export declare class VideoExportError extends Error {
7
7
  export declare function createAbortError(): VideoExportError;
8
8
  export declare function createTimeoutError(message: string, cause?: unknown): VideoExportError;
9
9
  export declare function isAbortError(error: unknown): boolean;
10
+ export declare function normalizeVideoExportError(error: unknown): VideoExportError;
@@ -1,4 +1,4 @@
1
- import type { PrepareExportFrame } from '../base';
1
+ import type { FrameSequenceDriverLike, FrameSequenceRenderOptions, PrepareExportFrame } from '../base';
2
2
  /**
3
3
  * High-level lifecycle state reported while a video export is captured, encoded, or completed.
4
4
  *
@@ -325,17 +325,5 @@ export interface VideoEncodingPlan {
325
325
  height: number;
326
326
  transparent: boolean;
327
327
  }
328
- export interface VideoRenderFrameOptions {
329
- frameCount: number;
330
- frameRate: number;
331
- signal?: AbortSignal;
332
- prepareFrame?: PrepareExportFrame;
333
- onFrame(frame: {
334
- frameIndex: number;
335
- canvas: HTMLCanvasElement;
336
- }): Promise<void> | void;
337
- }
338
- export interface VideoFrameDriverLike {
339
- readonly canvas: HTMLCanvasElement;
340
- $render(options: VideoRenderFrameOptions): Promise<void>;
341
- }
328
+ export type VideoRenderFrameOptions = FrameSequenceRenderOptions;
329
+ export type VideoFrameDriverLike = FrameSequenceDriverLike;
@@ -0,0 +1,4 @@
1
+ import type { Textmodifier } from 'textmode.js';
2
+ import type { ZIPFrameFormat, ZIPFrameSerializer } from './types';
3
+ /** Resolves the thin adapter for one supported ZIP frame format. */
4
+ export declare function createZIPFrameSerializerRegistry(textmodifier: Textmodifier): Record<ZIPFrameFormat, ZIPFrameSerializer>;
@@ -0,0 +1,21 @@
1
+ import type { ZIPEntryCompression } from './types';
2
+ /** Streaming ZIP wrapper that retains only compressed archive output chunks. */
3
+ export declare class ZIPArchiveWriter {
4
+ private readonly _archive;
5
+ private readonly _chunks;
6
+ private readonly _entryNames;
7
+ private readonly _modifiedAt;
8
+ private readonly _maxArchiveBytes;
9
+ private _state;
10
+ private _archiveBytes;
11
+ private _failure;
12
+ private _resolveFinal?;
13
+ private _rejectFinal?;
14
+ constructor(modifiedAt: Date, maxArchiveBytes?: number);
15
+ $addEntry(path: string, data: Uint8Array, compression: ZIPEntryCompression): Promise<void>;
16
+ $finalize(): Promise<Blob>;
17
+ $abort(reason?: unknown): void;
18
+ private _assertOpen;
19
+ private _validateEntryPath;
20
+ private _fail;
21
+ }
@@ -0,0 +1,32 @@
1
+ import type { Textmodifier } from 'textmode.js';
2
+ import { type FrameSequenceDriverLike, type PostDrawSubscription } from '../base';
3
+ import type { ZIPExportOptions, ZIPFrameFormat, ZIPFrameSerializer } from './types';
4
+ interface ZIPExporterDependencies {
5
+ createDriver?: () => FrameSequenceDriverLike;
6
+ createWriter?: (createdAt: Date) => ZIPArchiveWriterLike;
7
+ resolveSerializer?: (format: ZIPFrameFormat) => ZIPFrameSerializer;
8
+ now?: () => Date;
9
+ }
10
+ interface ZIPArchiveWriterLike {
11
+ $addEntry(path: string, data: Uint8Array, compression: 'store' | 'deflate'): Promise<void>;
12
+ $finalize(): Promise<Blob>;
13
+ $abort(reason?: unknown): void;
14
+ }
15
+ /** Captures deterministic frames and archives them in capture order. */
16
+ export declare class ZIPExporter {
17
+ private readonly _textmodifier;
18
+ private readonly _registerPostDrawHook;
19
+ private readonly _dependencies;
20
+ constructor(_textmodifier: Textmodifier, _registerPostDrawHook: PostDrawSubscription, dependencies?: ZIPExporterDependencies);
21
+ $saveZIP(options: ZIPExportOptions): Promise<void>;
22
+ /** Generates a deterministic ZIP archive without initiating a download. */
23
+ $generateZIPBlob(options: ZIPExportOptions): Promise<Blob>;
24
+ private _export;
25
+ private _executeExport;
26
+ private _validateAndNormalize;
27
+ private _emitProgress;
28
+ private _progressFrameCount;
29
+ private _throwIfAborted;
30
+ private _yieldToBrowser;
31
+ }
32
+ export {};
@@ -0,0 +1,12 @@
1
+ import type { ZIPFrameFormat, ZIPFrameSequenceManifest } from './types';
2
+ export declare function createZIPFramePath(rootStem: string, frameIndex: number, extension: string): string;
3
+ export declare function createZIPManifest(options: {
4
+ createdAt: Date;
5
+ format: ZIPFrameFormat;
6
+ frameCount: number;
7
+ frameRate: number;
8
+ width: number;
9
+ height: number;
10
+ extension: string;
11
+ }): ZIPFrameSequenceManifest;
12
+ export declare function encodeZIPManifest(manifest: ZIPFrameSequenceManifest): Uint8Array;
@@ -0,0 +1,14 @@
1
+ import type { ZIPExportErrorCode } from './types';
2
+ /**
3
+ * Error raised by ZIP frame-sequence validation, capture, or archive creation.
4
+ *
5
+ * @category ZIP frame-sequence export
6
+ * @see {@link https://code.textmode.art/api/textmode.export.js/classes/ZIPExportError | ZIPExportError API reference}
7
+ */
8
+ export declare class ZIPExportError extends Error {
9
+ readonly code: ZIPExportErrorCode;
10
+ readonly cause?: unknown;
11
+ constructor(code: ZIPExportErrorCode, message: string, cause?: unknown);
12
+ }
13
+ export declare function createZIPAbortError(cause?: unknown): ZIPExportError;
14
+ export declare function normalizeZIPExportError(error: unknown): ZIPExportError;
@@ -0,0 +1,3 @@
1
+ export { ZIPExporter } from './ZIPExporter';
2
+ export { ZIPExportError } from './errors';
3
+ export type { ZIPCaptureOptions, ZIPExportErrorCode, ZIPExportOptions, ZIPExportProgress, ZIPExportState, ZIPFrameFormat, ZIPFrameOptionsMap, } from './types';
@@ -0,0 +1,177 @@
1
+ import type { PrepareExportFrame } from '../base';
2
+ import type { ImageExportOptions } from '../image';
3
+ import type { JSONExportOptions } from '../json';
4
+ import type { SVGExportOptions } from '../svg';
5
+ import type { TXTExportOptions } from '../txt';
6
+ /**
7
+ * File format used for every frame in a ZIP frame-sequence export.
8
+ *
9
+ * @category ZIP frame-sequence export
10
+ * @see {@link https://code.textmode.art/api/textmode.export.js/type-aliases/ZIPFrameFormat | ZIPFrameFormat API reference}
11
+ */
12
+ export type ZIPFrameFormat = 'png' | 'jpg' | 'webp' | 'svg' | 'json' | 'txt';
13
+ /**
14
+ * Lifecycle state reported by a ZIP frame-sequence export.
15
+ *
16
+ * @category ZIP frame-sequence export
17
+ * @see {@link https://code.textmode.art/api/textmode.export.js/type-aliases/ZIPExportState | ZIPExportState API reference}
18
+ */
19
+ export type ZIPExportState = 'capturing' | 'finalizing' | 'completed' | 'error';
20
+ /**
21
+ * Machine-readable ZIP export failure category.
22
+ *
23
+ * @category ZIP frame-sequence export
24
+ * @see {@link https://code.textmode.art/api/textmode.export.js/type-aliases/ZIPExportErrorCode | ZIPExportErrorCode API reference}
25
+ */
26
+ export type ZIPExportErrorCode = 'ZIP_EXPORT_INVALID_OPTIONS' | 'ZIP_EXPORT_ENCODING_UNSUPPORTED' | 'ZIP_EXPORT_TOO_LARGE' | 'ZIP_EXPORT_ABORTED' | 'ZIP_EXPORT_TIMEOUT' | 'ZIP_EXPORT_BUSY' | 'ZIP_EXPORT_DISPOSED' | 'ZIP_EXPORT_FAILED';
27
+ /**
28
+ * Progress information emitted while ZIP frames are captured and archived.
29
+ *
30
+ * @category ZIP frame-sequence export
31
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPExportProgress | ZIPExportProgress API reference}
32
+ */
33
+ export interface ZIPExportProgress {
34
+ /**
35
+ * Current ZIP export state.
36
+ *
37
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPExportProgress#state | ZIPExportProgress.state API reference}
38
+ */
39
+ state: ZIPExportState;
40
+ /**
41
+ * Number of completed frame entries.
42
+ *
43
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPExportProgress#frameindex | ZIPExportProgress.frameIndex API reference}
44
+ */
45
+ frameIndex: number;
46
+ /**
47
+ * Total number of frame entries planned for the archive.
48
+ *
49
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPExportProgress#totalframes | ZIPExportProgress.totalFrames API reference}
50
+ */
51
+ totalFrames: number;
52
+ /**
53
+ * Normalized completed-frame ratio from `0` through `1`.
54
+ *
55
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPExportProgress#progress | ZIPExportProgress.progress API reference}
56
+ */
57
+ progress: number;
58
+ /**
59
+ * Optional failure message for an `error` event.
60
+ *
61
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPExportProgress#message | ZIPExportProgress.message API reference}
62
+ */
63
+ message?: string;
64
+ }
65
+ /**
66
+ * Options shared by all ZIP frame formats.
67
+ *
68
+ * @category ZIP frame-sequence export
69
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPCaptureOptions | ZIPCaptureOptions API reference}
70
+ */
71
+ export interface ZIPCaptureOptions {
72
+ /**
73
+ * Target archive filename, with or without `.zip`.
74
+ *
75
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPCaptureOptions#filename | ZIPCaptureOptions.filename API reference}
76
+ */
77
+ filename?: string;
78
+ /**
79
+ * Number of frames to capture. Defaults to `300`.
80
+ *
81
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPCaptureOptions#framecount | ZIPCaptureOptions.frameCount API reference}
82
+ */
83
+ frameCount?: number;
84
+ /**
85
+ * Deterministic sampling rate. Defaults to `60`.
86
+ *
87
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPCaptureOptions#framerate | ZIPCaptureOptions.frameRate API reference}
88
+ */
89
+ frameRate?: number;
90
+ /**
91
+ * Prepares external state before each deterministic redraw.
92
+ *
93
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPCaptureOptions#prepareframe | ZIPCaptureOptions.prepareFrame API reference}
94
+ */
95
+ prepareFrame?: PrepareExportFrame;
96
+ /**
97
+ * Cancels capture, serialization, or archive finalization.
98
+ *
99
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPCaptureOptions#signal | ZIPCaptureOptions.signal API reference}
100
+ */
101
+ signal?: AbortSignal;
102
+ /**
103
+ * Receives capture and archive lifecycle updates.
104
+ *
105
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPCaptureOptions#onprogress | ZIPCaptureOptions.onProgress API reference}
106
+ */
107
+ onProgress?: (progress: ZIPExportProgress) => void;
108
+ }
109
+ /**
110
+ * Format-specific options forwarded to each existing still exporter.
111
+ *
112
+ * @category ZIP frame-sequence export
113
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/ZIPFrameOptionsMap | ZIPFrameOptionsMap API reference}
114
+ */
115
+ export interface ZIPFrameOptionsMap {
116
+ png: Omit<ImageExportOptions, 'filename' | 'format'>;
117
+ jpg: Omit<ImageExportOptions, 'filename' | 'format'>;
118
+ webp: Omit<ImageExportOptions, 'filename' | 'format'>;
119
+ svg: Omit<SVGExportOptions, 'filename'>;
120
+ json: Omit<JSONExportOptions, 'filename'>;
121
+ txt: Omit<TXTExportOptions, 'filename'>;
122
+ }
123
+ /**
124
+ * Discriminated options for a deterministic ZIP frame-sequence export.
125
+ *
126
+ * @category ZIP frame-sequence export
127
+ * @see {@link https://code.textmode.art/api/textmode.export.js/type-aliases/ZIPExportOptions | ZIPExportOptions API reference}
128
+ */
129
+ export type ZIPExportOptions = {
130
+ [F in ZIPFrameFormat]: ZIPCaptureOptions & {
131
+ format: F;
132
+ frameOptions?: ZIPFrameOptionsMap[F];
133
+ };
134
+ }[ZIPFrameFormat];
135
+ export interface ZIPGenerationOptions {
136
+ format: ZIPFrameFormat;
137
+ filename?: string;
138
+ frameCount: number;
139
+ frameRate: number;
140
+ frameOptions: unknown;
141
+ prepareFrame?: PrepareExportFrame;
142
+ signal?: AbortSignal;
143
+ onProgress?: (progress: ZIPExportProgress) => void;
144
+ }
145
+ export type ZIPEntryCompression = 'store' | 'deflate';
146
+ export interface ZIPFrameSerializer {
147
+ readonly extension: `.${ZIPFrameFormat}`;
148
+ readonly compression: ZIPEntryCompression;
149
+ getDimensions(context: {
150
+ canvas: HTMLCanvasElement;
151
+ frameOptions: unknown;
152
+ }): {
153
+ width: number;
154
+ height: number;
155
+ };
156
+ serialize(context: {
157
+ canvas: HTMLCanvasElement;
158
+ frameOptions: unknown;
159
+ createdAt: Date;
160
+ }): Promise<Uint8Array>;
161
+ }
162
+ export interface ZIPFrameSequenceManifest {
163
+ schema: 'textmode.frame-sequence';
164
+ schemaVersion: '1.0.0';
165
+ generator: {
166
+ name: 'textmode.export.js';
167
+ version: string;
168
+ };
169
+ createdAt: string;
170
+ format: ZIPFrameFormat;
171
+ frameCount: number;
172
+ frameRate: number;
173
+ width: number;
174
+ height: number;
175
+ indexBase: 1;
176
+ filePattern: string;
177
+ }
@@ -27,6 +27,9 @@
27
27
  * @categoryDescription Video export
28
28
  * Capture deterministic frames and encode MP4 or WebM video with Mediabunny.
29
29
  *
30
+ * @categoryDescription ZIP frame-sequence export
31
+ * Capture deterministic PNG, JPEG, WebP, SVG, JSON, or TXT frames in a code-only ZIP archive.
32
+ *
30
33
  * @categoryDescription Layer data export
31
34
  * Export selected-layer text or vector data as TXT or SVG.
32
35
  *
@@ -47,6 +50,8 @@ export type { SVGExportOptions } from './exporters/svg';
47
50
  export type { TXTExportOptions } from './exporters/txt';
48
51
  export type { JSONExportColorMode, JSONExportOptions, JSONExportTarget, TextmodeDocumentJSON } from './exporters/json';
49
52
  export type { GIFExportOptions, GIFExportProgress } from './exporters/gif';
53
+ export { ZIPExportError } from './exporters/zip';
54
+ export type { ZIPCaptureOptions, ZIPExportErrorCode, ZIPExportOptions, ZIPExportProgress, ZIPExportState, ZIPFrameFormat, ZIPFrameOptionsMap, } from './exporters/zip';
50
55
  export type { VideoBitrateMode, VideoExportFormat, VideoExportOptions, VideoExportPhase, VideoExportProgress, VideoHardwareAcceleration, VideoQuality, VideoQualityLevel, VideoSaveDestination, VideoRecordingState, } from './exporters/video';
51
56
  export type { LayerExportOptions } from './exporters/base';
52
57
  /**
@@ -12,6 +12,7 @@ export declare class TextmodeExportController {
12
12
  readonly api: TextmodeExportAPI;
13
13
  private readonly _registerPostDrawHook;
14
14
  private readonly _activeOperations;
15
+ private readonly _captureGate;
15
16
  private readonly _overlayController;
16
17
  private readonly _stopOverlayRefresh;
17
18
  private _disposed;
@@ -19,6 +20,8 @@ export declare class TextmodeExportController {
19
20
  dispose(): void;
20
21
  private _createOverlayAPI;
21
22
  private _runOperation;
23
+ private _runCaptureOperation;
24
+ private _emitZIPGateError;
22
25
  private _assertLive;
23
26
  }
24
27
  export {};
@@ -4,6 +4,7 @@ import type { SVGExportOptions } from './exporters/svg';
4
4
  import type { GIFExportOptions } from './exporters/gif';
5
5
  import type { VideoExportOptions } from './exporters/video';
6
6
  import type { JSONExportOptions, TextmodeDocumentJSON } from './exporters/json';
7
+ import type { ZIPExportOptions } from './exporters/zip';
7
8
  /**
8
9
  * Default TXT export fields controlled by the export overlay.
9
10
  *
@@ -503,4 +504,50 @@ export interface TextmodeExportAPI {
503
504
  * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/TextmodeExportAPI#tovideoblob | TextmodeExportAPI.toVideoBlob API reference}
504
505
  */
505
506
  toVideoBlob(options?: VideoExportOptions): Promise<Blob>;
507
+ /**
508
+ * Captures and downloads a deterministic frame sequence as a ZIP archive.
509
+ *
510
+ * The archive contains a versioned manifest followed by one-based, zero-padded frame files. ZIP export is available
511
+ * through code only and does not appear in the export overlay.
512
+ *
513
+ * @param options Required archive format, deterministic capture controls, and per-frame exporter options.
514
+ * @returns A promise that resolves after the archive download is initiated.
515
+ * @throws {Error} When validation, capture, serialization, archive writing, or download fails.
516
+ *
517
+ * @category ZIP frame-sequence export
518
+ *
519
+ * @example
520
+ * ```ts
521
+ * await t.saveZip({
522
+ * format: 'png',
523
+ * filename: 'orbit-frames',
524
+ * frameCount: 120,
525
+ * frameRate: 30,
526
+ * frameOptions: { scale: 2 },
527
+ * });
528
+ * ```
529
+ *
530
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/TextmodeExportAPI#savezip | TextmodeExportAPI.saveZip API reference}
531
+ */
532
+ saveZip(options: ZIPExportOptions): Promise<void>;
533
+ /**
534
+ * Captures a deterministic frame sequence as an in-memory ZIP archive without downloading it.
535
+ *
536
+ * Peak memory includes the completed ZIP because this method returns a `Blob`; individual uncompressed frame
537
+ * payloads are serialized and released sequentially.
538
+ *
539
+ * @param options Required archive format, deterministic capture controls, and per-frame exporter options.
540
+ * @returns The completed `application/zip` blob.
541
+ * @throws {Error} When validation, capture, serialization, archive writing, or cancellation fails.
542
+ *
543
+ * @category ZIP frame-sequence export
544
+ *
545
+ * @example
546
+ * ```ts
547
+ * const archive = await t.toZipBlob({ format: 'svg', frameCount: 60, frameRate: 24 });
548
+ * ```
549
+ *
550
+ * @see {@link https://code.textmode.art/api/textmode.export.js/interfaces/TextmodeExportAPI#tozipblob | TextmodeExportAPI.toZipBlob API reference}
551
+ */
552
+ toZipBlob(options: ZIPExportOptions): Promise<Blob>;
506
553
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "textmode.export.js",
3
- "version": "1.8.1",
4
- "description": "Export plugin for textmode.js - Save your creations as images, GIFs, videos, SVG, JSON, and plain text.",
3
+ "version": "1.9.0-beta.1",
4
+ "description": "Export plugin for textmode.js - Save images, GIFs, videos, SVG, JSON, text, and ZIP frame sequences.",
5
5
  "type": "module",
6
6
  "devEngines": {
7
7
  "runtime": {
@@ -63,6 +63,7 @@
63
63
  "textmode.js": "^0.18.0"
64
64
  },
65
65
  "dependencies": {
66
+ "fflate": "0.8.3",
66
67
  "gifenc": "^1.0.1",
67
68
  "mediabunny": "1.55.2"
68
69
  },
@@ -94,6 +95,8 @@
94
95
  "png",
95
96
  "jpg",
96
97
  "webp",
98
+ "zip",
99
+ "frame-sequence",
97
100
  "creative-coding",
98
101
  "canvas"
99
102
  ],
@@ -1,23 +0,0 @@
1
- import type { PostDrawSubscription } from '../video/VideoFrameDriver';
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: PostDrawSubscription, onProgress?: (progress: GIFExportProgress) => void): Promise<GIFFrame[]>;
23
- }