@effing/annie-player 0.39.0 → 0.40.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.
@@ -1,5 +1,5 @@
1
1
  // src/core.ts
2
- import { untar } from "@andrewbranch/untar.js";
2
+ import { annieFrames } from "@effing/annie";
3
3
  var AnniePlayerCore = class {
4
4
  src;
5
5
  fps;
@@ -101,6 +101,7 @@ var AnniePlayerCore = class {
101
101
  this.cleanup();
102
102
  const imageFiles = [];
103
103
  const imageLoadPromises = [];
104
+ const objectUrls = [];
104
105
  try {
105
106
  const response = await fetch(this.src);
106
107
  if (!response.ok) {
@@ -110,35 +111,32 @@ var AnniePlayerCore = class {
110
111
  throw new Error("Response body is null");
111
112
  }
112
113
  this.setStatus("Extracting frames...");
113
- untar(await response.arrayBuffer()).forEach((file) => {
114
- if (!file.name || file.typeflag !== "0") return;
115
- if (!file.fileData) return;
116
- const blob = new Blob([new Uint8Array(file.fileData)]);
114
+ for await (const frame of annieFrames(response.body)) {
115
+ const blob = new Blob([frame.data], {
116
+ type: frame.contentType
117
+ });
117
118
  const promise = new Promise((resolve, reject) => {
118
119
  const img = new Image();
119
120
  img.onload = () => {
120
- imageFiles.push({ name: file.name, image: img });
121
- if (imageFiles.length === 1) {
122
- this._dimensions = {
123
- width: img.naturalWidth,
124
- height: img.naturalHeight
125
- };
126
- }
121
+ imageFiles.push({ index: frame.index, image: img });
127
122
  resolve();
128
123
  };
129
124
  img.onerror = (err) => {
130
- reject(new Error(`Could not load image: ${file.name} (${err})`));
125
+ reject(new Error(`Could not load image: ${frame.name} (${err})`));
131
126
  };
132
- img.src = URL.createObjectURL(blob);
127
+ const objectUrl = URL.createObjectURL(blob);
128
+ objectUrls.push(objectUrl);
129
+ img.src = objectUrl;
133
130
  });
134
131
  imageLoadPromises.push(promise);
135
- });
136
- await Promise.all(imageLoadPromises);
137
- if (imageFiles.length === 0) {
138
- throw new Error("No frames found in the TAR file.");
139
132
  }
140
- imageFiles.sort((a, b) => a.name.localeCompare(b.name));
133
+ await Promise.all(imageLoadPromises);
134
+ imageFiles.sort((a, b) => a.index - b.index);
141
135
  this.frames = imageFiles.map((f) => f.image);
136
+ this._dimensions = {
137
+ width: this.frames[0].naturalWidth,
138
+ height: this.frames[0].naturalHeight
139
+ };
142
140
  this._isLoading = false;
143
141
  this.setStatus(`Loaded ${this.frames.length} frames. Ready to play.`);
144
142
  this.emit("load", {
@@ -149,6 +147,7 @@ var AnniePlayerCore = class {
149
147
  this.play();
150
148
  }
151
149
  } catch (err) {
150
+ objectUrls.forEach((url) => URL.revokeObjectURL(url));
152
151
  const errorMessage = err instanceof Error ? err.message : "Unknown error";
153
152
  this._isLoading = false;
154
153
  this.setError(errorMessage);
@@ -266,4 +265,4 @@ var AnniePlayerCore = class {
266
265
  export {
267
266
  AnniePlayerCore
268
267
  };
269
- //# sourceMappingURL=chunk-XXS5ARJX.js.map
268
+ //# sourceMappingURL=chunk-KBVFZAAO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core.ts"],"sourcesContent":["import { annieFrames } from \"@effing/annie\";\n\nexport type AnniePlayerState = {\n status: string;\n error: string | null;\n isLoading: boolean;\n isPlaying: boolean;\n frameCount: number;\n currentFrame: number;\n dimensions: { width: number; height: number } | null;\n};\n\nexport type AnniePlayerEvents = {\n statechange: AnniePlayerState;\n load: { frameCount: number; dimensions: { width: number; height: number } };\n error: Error;\n};\n\nexport type AnniePlayerOptions = {\n src: string;\n fps?: number;\n autoPlay?: boolean;\n};\n\ntype EventCallback<T> = (data: T) => void;\n\n/**\n * Framework-agnostic Annie animation player.\n * Handles loading TAR archives of image frames and playing them on a canvas.\n */\nexport class AnniePlayerCore {\n private src: string;\n private fps: number;\n private autoPlay: boolean;\n private frameInterval: number;\n\n private frames: HTMLImageElement[] = [];\n private canvas: HTMLCanvasElement | null = null;\n private context: CanvasRenderingContext2D | null = null;\n\n private animationFrameId: number | null = null;\n private currentFrameIndex = 0;\n private lastFrameTime = 0;\n\n private _isLoading = false;\n private _isPlaying = false;\n private _error: string | null = null;\n private _status = \"Ready to load animation.\";\n private _dimensions: { width: number; height: number } | null = null;\n\n private listeners: Map<\n keyof AnniePlayerEvents,\n Set<EventCallback<AnniePlayerEvents[keyof AnniePlayerEvents]>>\n > = new Map();\n\n constructor(options: AnniePlayerOptions) {\n this.src = options.src;\n this.fps = options.fps ?? 30;\n this.autoPlay = options.autoPlay ?? false;\n this.frameInterval = 1000 / this.fps;\n }\n\n /**\n * Attach a canvas element to render the animation on.\n */\n attachCanvas(canvas: HTMLCanvasElement): void {\n this.canvas = canvas;\n this.context = canvas.getContext(\"2d\");\n }\n\n /**\n * Detach the canvas and stop playback.\n */\n detachCanvas(): void {\n this.stop();\n this.canvas = null;\n this.context = null;\n }\n\n /**\n * Get the current state of the player.\n */\n getState(): AnniePlayerState {\n return {\n status: this._status,\n error: this._error,\n isLoading: this._isLoading,\n isPlaying: this._isPlaying,\n frameCount: this.frames.length,\n currentFrame: this.currentFrameIndex,\n dimensions: this._dimensions,\n };\n }\n\n /**\n * Subscribe to player events.\n */\n on<K extends keyof AnniePlayerEvents>(\n event: K,\n callback: EventCallback<AnniePlayerEvents[K]>,\n ): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const callbacks = this.listeners.get(event)!;\n callbacks.add(\n callback as EventCallback<AnniePlayerEvents[keyof AnniePlayerEvents]>,\n );\n\n return () => {\n callbacks.delete(\n callback as EventCallback<AnniePlayerEvents[keyof AnniePlayerEvents]>,\n );\n };\n }\n\n private emit<K extends keyof AnniePlayerEvents>(\n event: K,\n data: AnniePlayerEvents[K],\n ): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n callbacks.forEach((cb) => cb(data));\n }\n }\n\n private emitStateChange(): void {\n this.emit(\"statechange\", this.getState());\n }\n\n private setStatus(status: string): void {\n this._status = status;\n this.emitStateChange();\n }\n\n private setError(error: string | null): void {\n this._error = error;\n if (error) {\n this.emit(\"error\", new Error(error));\n }\n this.emitStateChange();\n }\n\n /**\n * Load the animation from the source URL.\n */\n async load(): Promise<void> {\n if (this._isLoading) return;\n\n this.stop();\n this._isLoading = true;\n this._error = null;\n this.setStatus(\"Loading Annie file...\");\n this.cleanup();\n\n const imageFiles: { index: number; image: HTMLImageElement }[] = [];\n const imageLoadPromises: Promise<void>[] = [];\n const objectUrls: string[] = [];\n\n try {\n const response = await fetch(this.src);\n if (!response.ok) {\n throw new Error(`Failed to fetch animation: ${response.statusText}`);\n }\n if (!response.body) {\n throw new Error(\"Response body is null\");\n }\n\n this.setStatus(\"Extracting frames...\");\n\n for await (const frame of annieFrames(response.body)) {\n const blob = new Blob([frame.data as BlobPart], {\n type: frame.contentType,\n });\n\n const promise = new Promise<void>((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n imageFiles.push({ index: frame.index, image: img });\n resolve();\n };\n img.onerror = (err) => {\n reject(new Error(`Could not load image: ${frame.name} (${err})`));\n };\n const objectUrl = URL.createObjectURL(blob);\n objectUrls.push(objectUrl);\n img.src = objectUrl;\n });\n imageLoadPromises.push(promise);\n }\n\n await Promise.all(imageLoadPromises);\n\n imageFiles.sort((a, b) => a.index - b.index);\n this.frames = imageFiles.map((f) => f.image);\n this._dimensions = {\n width: this.frames[0].naturalWidth,\n height: this.frames[0].naturalHeight,\n };\n this._isLoading = false;\n this.setStatus(`Loaded ${this.frames.length} frames. Ready to play.`);\n\n this.emit(\"load\", {\n frameCount: this.frames.length,\n dimensions: this._dimensions!,\n });\n\n if (this.autoPlay && this.frames.length > 0) {\n this.play();\n }\n } catch (err) {\n // this.frames is still empty, so cleanup() can't reach these URLs\n objectUrls.forEach((url) => URL.revokeObjectURL(url));\n const errorMessage = err instanceof Error ? err.message : \"Unknown error\";\n this._isLoading = false;\n this.setError(errorMessage);\n this.setStatus(`Error: ${errorMessage}`);\n }\n }\n\n /**\n * Start playing the animation from the current frame.\n */\n play(): void {\n if (this._isPlaying || this.frames.length === 0) return;\n\n this._isPlaying = true;\n this.lastFrameTime = 0;\n this.drawFrame(this.currentFrameIndex);\n this.setStatus(\"Playing...\");\n this.animationFrameId = requestAnimationFrame(this.animate);\n }\n\n /**\n * Pause the animation at the current frame.\n */\n pause(): void {\n if (!this._isPlaying) return;\n\n this._isPlaying = false;\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n this.setStatus(\"Paused.\");\n }\n\n /**\n * Stop the animation and reset to the first frame.\n */\n stop(): void {\n this._isPlaying = false;\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n this.currentFrameIndex = 0;\n this.lastFrameTime = 0;\n }\n\n /**\n * Seek to a specific frame. The frame is drawn immediately. Playback state\n * is preserved — if playing, playback continues forward from the new frame.\n */\n seek(frameIndex: number): void {\n if (this.frames.length === 0) return;\n const clamped = Math.max(\n 0,\n Math.min(Math.floor(frameIndex), this.frames.length - 1),\n );\n this.lastFrameTime = 0;\n this.drawFrame(clamped);\n }\n\n private drawFrame(index: number): void {\n const indexChanged = index !== this.currentFrameIndex;\n if (!this.canvas || !this.context) {\n if (indexChanged) {\n this.currentFrameIndex = index;\n this.emitStateChange();\n }\n return;\n }\n const frame = this.frames[index];\n if (!frame) return;\n this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.context.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);\n if (indexChanged) {\n this.currentFrameIndex = index;\n this.emitStateChange();\n }\n }\n\n private animate = (timestamp: number): void => {\n if (!this._isPlaying || this.frames.length === 0) {\n this.animationFrameId = null;\n return;\n }\n\n if (!this.canvas || !this.context) {\n this.animationFrameId = null;\n return;\n }\n\n if (this.lastFrameTime === 0) {\n this.lastFrameTime = timestamp;\n }\n\n const elapsed = timestamp - this.lastFrameTime;\n\n if (elapsed >= this.frameInterval) {\n this.lastFrameTime = timestamp - (elapsed % this.frameInterval);\n const nextIndex = (this.currentFrameIndex + 1) % this.frames.length;\n this.drawFrame(nextIndex);\n }\n\n this.animationFrameId = requestAnimationFrame(this.animate);\n };\n\n /**\n * Clean up resources (revoke blob URLs).\n */\n cleanup(): void {\n this.frames.forEach((img) => {\n if (img.src.startsWith(\"blob:\")) {\n URL.revokeObjectURL(img.src);\n }\n });\n this.frames = [];\n }\n\n /**\n * Destroy the player and clean up all resources.\n */\n destroy(): void {\n this.stop();\n this.cleanup();\n this.detachCanvas();\n this.listeners.clear();\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AnniePlayerCore
3
- } from "./chunk-XXS5ARJX.js";
3
+ } from "./chunk-KBVFZAAO.js";
4
4
  export {
5
5
  AnniePlayerCore
6
6
  };
@@ -1,4 +1,4 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
1
+ import * as react from 'react';
2
2
  import { AnniePlayerState } from '../index.js';
3
3
 
4
4
  type AnniePlayerContextValue = {
@@ -31,49 +31,49 @@ type AnniePlayerRootProps = {
31
31
  /** Children can be React nodes or a render function for headless usage */
32
32
  children?: React.ReactNode | ((value: AnniePlayerContextValue) => React.ReactNode);
33
33
  };
34
- declare function AnniePlayerRoot({ src, height, defaultWidth, autoLoad, autoPlay, fps, style, className, children, }: AnniePlayerRootProps): react_jsx_runtime.JSX.Element;
34
+ declare function AnniePlayerRoot({ src, height, defaultWidth, autoLoad, autoPlay, fps, style, className, children, }: AnniePlayerRootProps): react.JSX.Element;
35
35
  type AnniePlayerWrapperProps = {
36
36
  children: React.ReactNode;
37
37
  className?: string;
38
38
  style?: React.CSSProperties;
39
39
  };
40
- declare function AnniePlayerWrapper({ children, className, style, }: AnniePlayerWrapperProps): react_jsx_runtime.JSX.Element;
40
+ declare function AnniePlayerWrapper({ children, className, style, }: AnniePlayerWrapperProps): react.JSX.Element;
41
41
  type AnniePlayerCanvasProps = {
42
42
  className?: string;
43
43
  style?: React.CSSProperties;
44
44
  };
45
- declare function AnniePlayerCanvas({ className, style }: AnniePlayerCanvasProps): react_jsx_runtime.JSX.Element;
45
+ declare function AnniePlayerCanvas({ className, style }: AnniePlayerCanvasProps): react.JSX.Element;
46
46
  type AnniePlayerControlsProps = {
47
47
  children: React.ReactNode;
48
48
  className?: string;
49
49
  style?: React.CSSProperties;
50
50
  };
51
- declare function AnniePlayerControls({ children, className, style, }: AnniePlayerControlsProps): react_jsx_runtime.JSX.Element;
51
+ declare function AnniePlayerControls({ children, className, style, }: AnniePlayerControlsProps): react.JSX.Element;
52
52
  type AnniePlayerLoadButtonProps = {
53
53
  children?: React.ReactNode;
54
54
  className?: string;
55
55
  style?: React.CSSProperties;
56
56
  };
57
- declare function AnniePlayerLoadButton({ children, className, style, }: AnniePlayerLoadButtonProps): react_jsx_runtime.JSX.Element;
57
+ declare function AnniePlayerLoadButton({ children, className, style, }: AnniePlayerLoadButtonProps): react.JSX.Element;
58
58
  type AnniePlayerPlayButtonProps = {
59
59
  children?: React.ReactNode;
60
60
  className?: string;
61
61
  style?: React.CSSProperties;
62
62
  };
63
- declare function AnniePlayerPlayButton({ children, className, style, }: AnniePlayerPlayButtonProps): react_jsx_runtime.JSX.Element;
63
+ declare function AnniePlayerPlayButton({ children, className, style, }: AnniePlayerPlayButtonProps): react.JSX.Element;
64
64
  type AnniePlayerPauseButtonProps = {
65
65
  children?: React.ReactNode;
66
66
  className?: string;
67
67
  style?: React.CSSProperties;
68
68
  };
69
- declare function AnniePlayerPauseButton({ children, className, style, }: AnniePlayerPauseButtonProps): react_jsx_runtime.JSX.Element;
69
+ declare function AnniePlayerPauseButton({ children, className, style, }: AnniePlayerPauseButtonProps): react.JSX.Element;
70
70
  type AnniePlayerScrubberProps = {
71
71
  className?: string;
72
72
  style?: React.CSSProperties;
73
73
  /** If true, pause playback while the user is interacting with the scrubber. */
74
74
  pauseWhileScrubbing?: boolean;
75
75
  };
76
- declare function AnniePlayerScrubber({ className, style, pauseWhileScrubbing, }: AnniePlayerScrubberProps): react_jsx_runtime.JSX.Element;
76
+ declare function AnniePlayerScrubber({ className, style, pauseWhileScrubbing, }: AnniePlayerScrubberProps): react.JSX.Element;
77
77
  type AnniePlayerFrameCounterProps = {
78
78
  className?: string;
79
79
  style?: React.CSSProperties;
@@ -83,13 +83,13 @@ type AnniePlayerFrameCounterProps = {
83
83
  total: number;
84
84
  }) => React.ReactNode;
85
85
  };
86
- declare function AnniePlayerFrameCounter({ className, style, children, }: AnniePlayerFrameCounterProps): react_jsx_runtime.JSX.Element;
86
+ declare function AnniePlayerFrameCounter({ className, style, children, }: AnniePlayerFrameCounterProps): react.JSX.Element;
87
87
  type AnniePlayerStatusProps = {
88
88
  children?: React.ReactNode;
89
89
  className?: string;
90
90
  style?: React.CSSProperties;
91
91
  };
92
- declare function AnniePlayerStatus({ children, className, style, }: AnniePlayerStatusProps): react_jsx_runtime.JSX.Element;
92
+ declare function AnniePlayerStatus({ children, className, style, }: AnniePlayerStatusProps): react.JSX.Element;
93
93
  type AnniePlayerProps = {
94
94
  src: string;
95
95
  height: number;
@@ -102,7 +102,7 @@ type AnniePlayerProps = {
102
102
  /** Class name for the root container */
103
103
  className?: string;
104
104
  };
105
- declare function AnniePlayerSimple({ src, height, defaultWidth, autoLoad, autoPlay, fps, style, className, }: AnniePlayerProps): react_jsx_runtime.JSX.Element;
105
+ declare function AnniePlayerSimple({ src, height, defaultWidth, autoLoad, autoPlay, fps, style, className, }: AnniePlayerProps): react.JSX.Element;
106
106
  /**
107
107
  * AnniePlayer - Play TAR archives of PNG/JPEG frames in the browser.
108
108
  *
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AnniePlayerCore
3
- } from "../chunk-XXS5ARJX.js";
3
+ } from "../chunk-KBVFZAAO.js";
4
4
 
5
5
  // src/react/index.tsx
6
6
  import { createContext, useContext, useEffect, useRef, useState } from "react";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effing/annie-player",
3
- "version": "0.39.0",
3
+ "version": "0.40.1",
4
4
  "description": "Annie animation player that runs in the browser",
5
5
  "repository": {
6
6
  "type": "git",
@@ -22,14 +22,14 @@
22
22
  "dist"
23
23
  ],
24
24
  "dependencies": {
25
- "@andrewbranch/untar.js": "^1.0.3"
25
+ "@effing/annie": "0.40.1"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/react": "^19.0.0",
29
29
  "jsdom": "^29.1.1",
30
30
  "tsup": "^8.0.0",
31
31
  "typescript": "^5.9.3",
32
- "vitest": "^3.2.4"
32
+ "vitest": "^3.2.7"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "react": "^18.0.0 || ^19.0.0"
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core.ts"],"sourcesContent":["import { untar } from \"@andrewbranch/untar.js\";\n\nexport type AnniePlayerState = {\n status: string;\n error: string | null;\n isLoading: boolean;\n isPlaying: boolean;\n frameCount: number;\n currentFrame: number;\n dimensions: { width: number; height: number } | null;\n};\n\nexport type AnniePlayerEvents = {\n statechange: AnniePlayerState;\n load: { frameCount: number; dimensions: { width: number; height: number } };\n error: Error;\n};\n\nexport type AnniePlayerOptions = {\n src: string;\n fps?: number;\n autoPlay?: boolean;\n};\n\ntype EventCallback<T> = (data: T) => void;\n\n/**\n * Framework-agnostic Annie animation player.\n * Handles loading TAR archives of image frames and playing them on a canvas.\n */\nexport class AnniePlayerCore {\n private src: string;\n private fps: number;\n private autoPlay: boolean;\n private frameInterval: number;\n\n private frames: HTMLImageElement[] = [];\n private canvas: HTMLCanvasElement | null = null;\n private context: CanvasRenderingContext2D | null = null;\n\n private animationFrameId: number | null = null;\n private currentFrameIndex = 0;\n private lastFrameTime = 0;\n\n private _isLoading = false;\n private _isPlaying = false;\n private _error: string | null = null;\n private _status = \"Ready to load animation.\";\n private _dimensions: { width: number; height: number } | null = null;\n\n private listeners: Map<\n keyof AnniePlayerEvents,\n Set<EventCallback<AnniePlayerEvents[keyof AnniePlayerEvents]>>\n > = new Map();\n\n constructor(options: AnniePlayerOptions) {\n this.src = options.src;\n this.fps = options.fps ?? 30;\n this.autoPlay = options.autoPlay ?? false;\n this.frameInterval = 1000 / this.fps;\n }\n\n /**\n * Attach a canvas element to render the animation on.\n */\n attachCanvas(canvas: HTMLCanvasElement): void {\n this.canvas = canvas;\n this.context = canvas.getContext(\"2d\");\n }\n\n /**\n * Detach the canvas and stop playback.\n */\n detachCanvas(): void {\n this.stop();\n this.canvas = null;\n this.context = null;\n }\n\n /**\n * Get the current state of the player.\n */\n getState(): AnniePlayerState {\n return {\n status: this._status,\n error: this._error,\n isLoading: this._isLoading,\n isPlaying: this._isPlaying,\n frameCount: this.frames.length,\n currentFrame: this.currentFrameIndex,\n dimensions: this._dimensions,\n };\n }\n\n /**\n * Subscribe to player events.\n */\n on<K extends keyof AnniePlayerEvents>(\n event: K,\n callback: EventCallback<AnniePlayerEvents[K]>,\n ): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const callbacks = this.listeners.get(event)!;\n callbacks.add(\n callback as EventCallback<AnniePlayerEvents[keyof AnniePlayerEvents]>,\n );\n\n return () => {\n callbacks.delete(\n callback as EventCallback<AnniePlayerEvents[keyof AnniePlayerEvents]>,\n );\n };\n }\n\n private emit<K extends keyof AnniePlayerEvents>(\n event: K,\n data: AnniePlayerEvents[K],\n ): void {\n const callbacks = this.listeners.get(event);\n if (callbacks) {\n callbacks.forEach((cb) => cb(data));\n }\n }\n\n private emitStateChange(): void {\n this.emit(\"statechange\", this.getState());\n }\n\n private setStatus(status: string): void {\n this._status = status;\n this.emitStateChange();\n }\n\n private setError(error: string | null): void {\n this._error = error;\n if (error) {\n this.emit(\"error\", new Error(error));\n }\n this.emitStateChange();\n }\n\n /**\n * Load the animation from the source URL.\n */\n async load(): Promise<void> {\n if (this._isLoading) return;\n\n this.stop();\n this._isLoading = true;\n this._error = null;\n this.setStatus(\"Loading Annie file...\");\n this.cleanup();\n\n const imageFiles: { name: string; image: HTMLImageElement }[] = [];\n const imageLoadPromises: Promise<void>[] = [];\n\n try {\n const response = await fetch(this.src);\n if (!response.ok) {\n throw new Error(`Failed to fetch animation: ${response.statusText}`);\n }\n if (!response.body) {\n throw new Error(\"Response body is null\");\n }\n\n this.setStatus(\"Extracting frames...\");\n\n untar(await response.arrayBuffer()).forEach((file) => {\n if (!file.name || file.typeflag !== \"0\") return;\n if (!file.fileData) return;\n\n const blob = new Blob([new Uint8Array(file.fileData)]);\n\n const promise = new Promise<void>((resolve, reject) => {\n const img = new Image();\n img.onload = () => {\n imageFiles.push({ name: file.name!, image: img });\n if (imageFiles.length === 1) {\n this._dimensions = {\n width: img.naturalWidth,\n height: img.naturalHeight,\n };\n }\n resolve();\n };\n img.onerror = (err) => {\n reject(new Error(`Could not load image: ${file.name} (${err})`));\n };\n img.src = URL.createObjectURL(blob);\n });\n imageLoadPromises.push(promise);\n });\n\n await Promise.all(imageLoadPromises);\n\n if (imageFiles.length === 0) {\n throw new Error(\"No frames found in the TAR file.\");\n }\n\n imageFiles.sort((a, b) => a.name.localeCompare(b.name));\n this.frames = imageFiles.map((f) => f.image);\n this._isLoading = false;\n this.setStatus(`Loaded ${this.frames.length} frames. Ready to play.`);\n\n this.emit(\"load\", {\n frameCount: this.frames.length,\n dimensions: this._dimensions!,\n });\n\n if (this.autoPlay && this.frames.length > 0) {\n this.play();\n }\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : \"Unknown error\";\n this._isLoading = false;\n this.setError(errorMessage);\n this.setStatus(`Error: ${errorMessage}`);\n }\n }\n\n /**\n * Start playing the animation from the current frame.\n */\n play(): void {\n if (this._isPlaying || this.frames.length === 0) return;\n\n this._isPlaying = true;\n this.lastFrameTime = 0;\n this.drawFrame(this.currentFrameIndex);\n this.setStatus(\"Playing...\");\n this.animationFrameId = requestAnimationFrame(this.animate);\n }\n\n /**\n * Pause the animation at the current frame.\n */\n pause(): void {\n if (!this._isPlaying) return;\n\n this._isPlaying = false;\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n this.setStatus(\"Paused.\");\n }\n\n /**\n * Stop the animation and reset to the first frame.\n */\n stop(): void {\n this._isPlaying = false;\n if (this.animationFrameId !== null) {\n cancelAnimationFrame(this.animationFrameId);\n this.animationFrameId = null;\n }\n this.currentFrameIndex = 0;\n this.lastFrameTime = 0;\n }\n\n /**\n * Seek to a specific frame. The frame is drawn immediately. Playback state\n * is preserved — if playing, playback continues forward from the new frame.\n */\n seek(frameIndex: number): void {\n if (this.frames.length === 0) return;\n const clamped = Math.max(\n 0,\n Math.min(Math.floor(frameIndex), this.frames.length - 1),\n );\n this.lastFrameTime = 0;\n this.drawFrame(clamped);\n }\n\n private drawFrame(index: number): void {\n const indexChanged = index !== this.currentFrameIndex;\n if (!this.canvas || !this.context) {\n if (indexChanged) {\n this.currentFrameIndex = index;\n this.emitStateChange();\n }\n return;\n }\n const frame = this.frames[index];\n if (!frame) return;\n this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);\n this.context.drawImage(frame, 0, 0, this.canvas.width, this.canvas.height);\n if (indexChanged) {\n this.currentFrameIndex = index;\n this.emitStateChange();\n }\n }\n\n private animate = (timestamp: number): void => {\n if (!this._isPlaying || this.frames.length === 0) {\n this.animationFrameId = null;\n return;\n }\n\n if (!this.canvas || !this.context) {\n this.animationFrameId = null;\n return;\n }\n\n if (this.lastFrameTime === 0) {\n this.lastFrameTime = timestamp;\n }\n\n const elapsed = timestamp - this.lastFrameTime;\n\n if (elapsed >= this.frameInterval) {\n this.lastFrameTime = timestamp - (elapsed % this.frameInterval);\n const nextIndex = (this.currentFrameIndex + 1) % this.frames.length;\n this.drawFrame(nextIndex);\n }\n\n this.animationFrameId = requestAnimationFrame(this.animate);\n };\n\n /**\n * Clean up resources (revoke blob URLs).\n */\n cleanup(): void {\n this.frames.forEach((img) => {\n if (img.src.startsWith(\"blob:\")) {\n URL.revokeObjectURL(img.src);\n }\n });\n this.frames = [];\n }\n\n /**\n * Destroy the player and clean up all resources.\n */\n destroy(): void {\n this.stop();\n this.cleanup();\n this.detachCanvas();\n this.listeners.clear();\n }\n}\n"],"mappings":"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","names":[]}