saccade 0.0.3 → 0.1.0

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/dist/core.d.cts CHANGED
@@ -19,6 +19,8 @@ type AnimationInfo = {
19
19
  resolvedVars: Record<string, string> | null;
20
20
  conflicts: string[] | null;
21
21
  layoutAnimation?: boolean;
22
+ rawKeyframes?: Keyframe[];
23
+ rawTiming?: EffectTiming;
22
24
  };
23
25
  type PropertySnapshot = {
24
26
  property: string;
@@ -120,12 +122,15 @@ declare class TimingController {
120
122
  private speed;
121
123
  private realBaseline;
122
124
  private virtualBaseline;
125
+ private dateRealBaseline;
126
+ private dateVirtualBaseline;
123
127
  private intervalMap;
124
128
  private nextIntervalId;
125
- private mediaObserver;
126
- private animObserver;
127
129
  private _origAnimate;
128
130
  private installed;
131
+ private animPollId;
132
+ private trackedAnims;
133
+ private trackedMedia;
129
134
  private _raf;
130
135
  private _caf;
131
136
  private _setTimeout;
@@ -136,14 +141,22 @@ declare class TimingController {
136
141
  private _dateNow;
137
142
  constructor();
138
143
  private getVirtualTime;
144
+ private getVirtualDateNow;
139
145
  private reanchor;
146
+ /** Effective speed divisor — avoids division by zero at speed=0. */
147
+ private get speedDivisor();
140
148
  /** Install timing patches. Safe to call multiple times. */
141
149
  install(): void;
142
150
  /** Set playback speed. Requires install() first. */
143
151
  setSpeed(newSpeed: number): void;
144
- /** Patch playbackRate on all active CSS transitions/animations via WAAPI. */
145
- private patchAnimations;
146
152
  getSpeed(): number;
153
+ private startAnimationPoll;
154
+ /** Patch playbackRate on all active animations via WAAPI. */
155
+ private patchAnimations;
156
+ /** Patch playbackRate on all video/audio elements. */
157
+ private patchMedia;
158
+ /** Sync GSAP's global timeline if present. */
159
+ private patchGSAP;
147
160
  /** Restore all patched APIs to originals. */
148
161
  destroy(): void;
149
162
  }
@@ -197,6 +210,11 @@ declare class TimelineRecorder {
197
210
  animation: Animation;
198
211
  target: Element;
199
212
  }[];
213
+ readonly seekableClones: Map<string, {
214
+ animation: Animation;
215
+ element: HTMLElement;
216
+ effect: KeyframeEffect;
217
+ }>;
200
218
  private static readonly MAX_DURATION_MS;
201
219
  private static readonly MAX_FRAMES;
202
220
  private hiddenSince;
@@ -230,6 +248,12 @@ interface InterceptedAnimation {
230
248
  animation: Animation;
231
249
  target: Element;
232
250
  }
251
+ /** A seekable WAAPI clone created from recorded keyframes. */
252
+ interface SeekableClone {
253
+ animation: Animation;
254
+ element: HTMLElement;
255
+ effect: KeyframeEffect;
256
+ }
233
257
  /**
234
258
  * State handed from the recorder after stopRecording().
235
259
  * This is everything the scrubber needs to replay frames.
@@ -245,26 +269,32 @@ interface ScrubberState {
245
269
  interceptedAnimations: InterceptedAnimation[];
246
270
  /** Set of CSS properties that are safe (visual-only, no layout side-effects) */
247
271
  SAFE_PROPS_SET: Set<string>;
272
+ /** Seekable WAAPI clones created from recorded animation keyframes */
273
+ seekableClones: Map<string, SeekableClone>;
248
274
  }
249
275
  /**
250
- * Timeline scrubber — applies captured frame snapshots to the live DOM.
276
+ * Timeline scrubber — seeks through recorded animation state using WAAPI.
251
277
  *
252
- * Ported from the inline JS that the Electron version injected via
253
- * executeJavaScript() in timeline-controller.ts (seekTo / releaseAnimations).
278
+ * Instead of applying computed styles as inline overrides (which breaks
279
+ * Tailwind/class-driven layouts), this scrubber creates paused WAAPI
280
+ * animation clones and seeks them via animation.currentTime. The browser
281
+ * computes exact intermediate values natively — the same mechanism Chrome
282
+ * DevTools uses for its Animation panel.
254
283
  */
255
284
  declare class TimelineScrubber {
256
285
  private elements;
257
286
  private frames;
258
287
  private capturedPortals;
259
288
  private interceptedAnimations;
260
- private SAFE_PROPS_SET;
289
+ private seekableClones;
290
+ /** Precomputed frame range per animation for O(1) before/after lookup. */
291
+ private animFrameRanges;
261
292
  /** Saved originals for restore on release */
262
293
  private _originalAnimate;
263
294
  private _originalRaf;
264
295
  private _originalRemoveChild;
265
296
  private _originalRemove;
266
297
  constructor(state: ScrubberState);
267
- private getSelector;
268
298
  seekTo(timeMs: number): void;
269
299
  release(): void;
270
300
  }
@@ -273,4 +303,4 @@ declare function getFrameAtTime(frames: FrameSnapshot[], timeMs: number): FrameS
273
303
  declare function generateExport(animations: AnimationInfo[], frames: FrameSnapshot[], timeMs: number, filter?: ExportFilter): TimelineExport;
274
304
  declare function formatExportForLLM(exp: TimelineExport, detail?: OutputDetailLevel): string;
275
305
 
276
- export { type AnimationInfo, type ElementSnapshot, type ExportFilter, type FrameAnimation, type FrameSnapshot, type InterceptedAnimation, type OutputDetailLevel, type PropertySnapshot, type Rect, SaccadeEngine, type SaccadeState, type ScrubberState, type TimelineCapture, type TimelineExport, TimelineRecorder, TimelineScrubber, TimingController, formatExportForLLM, generateExport, getFrameAtTime };
306
+ export { type AnimationInfo, type ElementSnapshot, type ExportFilter, type FrameAnimation, type FrameSnapshot, type InterceptedAnimation, type OutputDetailLevel, type PropertySnapshot, type Rect, SaccadeEngine, type SaccadeState, type ScrubberState, type SeekableClone, type TimelineCapture, type TimelineExport, TimelineRecorder, TimelineScrubber, TimingController, formatExportForLLM, generateExport, getFrameAtTime };
package/dist/core.d.ts CHANGED
@@ -19,6 +19,8 @@ type AnimationInfo = {
19
19
  resolvedVars: Record<string, string> | null;
20
20
  conflicts: string[] | null;
21
21
  layoutAnimation?: boolean;
22
+ rawKeyframes?: Keyframe[];
23
+ rawTiming?: EffectTiming;
22
24
  };
23
25
  type PropertySnapshot = {
24
26
  property: string;
@@ -120,12 +122,15 @@ declare class TimingController {
120
122
  private speed;
121
123
  private realBaseline;
122
124
  private virtualBaseline;
125
+ private dateRealBaseline;
126
+ private dateVirtualBaseline;
123
127
  private intervalMap;
124
128
  private nextIntervalId;
125
- private mediaObserver;
126
- private animObserver;
127
129
  private _origAnimate;
128
130
  private installed;
131
+ private animPollId;
132
+ private trackedAnims;
133
+ private trackedMedia;
129
134
  private _raf;
130
135
  private _caf;
131
136
  private _setTimeout;
@@ -136,14 +141,22 @@ declare class TimingController {
136
141
  private _dateNow;
137
142
  constructor();
138
143
  private getVirtualTime;
144
+ private getVirtualDateNow;
139
145
  private reanchor;
146
+ /** Effective speed divisor — avoids division by zero at speed=0. */
147
+ private get speedDivisor();
140
148
  /** Install timing patches. Safe to call multiple times. */
141
149
  install(): void;
142
150
  /** Set playback speed. Requires install() first. */
143
151
  setSpeed(newSpeed: number): void;
144
- /** Patch playbackRate on all active CSS transitions/animations via WAAPI. */
145
- private patchAnimations;
146
152
  getSpeed(): number;
153
+ private startAnimationPoll;
154
+ /** Patch playbackRate on all active animations via WAAPI. */
155
+ private patchAnimations;
156
+ /** Patch playbackRate on all video/audio elements. */
157
+ private patchMedia;
158
+ /** Sync GSAP's global timeline if present. */
159
+ private patchGSAP;
147
160
  /** Restore all patched APIs to originals. */
148
161
  destroy(): void;
149
162
  }
@@ -197,6 +210,11 @@ declare class TimelineRecorder {
197
210
  animation: Animation;
198
211
  target: Element;
199
212
  }[];
213
+ readonly seekableClones: Map<string, {
214
+ animation: Animation;
215
+ element: HTMLElement;
216
+ effect: KeyframeEffect;
217
+ }>;
200
218
  private static readonly MAX_DURATION_MS;
201
219
  private static readonly MAX_FRAMES;
202
220
  private hiddenSince;
@@ -230,6 +248,12 @@ interface InterceptedAnimation {
230
248
  animation: Animation;
231
249
  target: Element;
232
250
  }
251
+ /** A seekable WAAPI clone created from recorded keyframes. */
252
+ interface SeekableClone {
253
+ animation: Animation;
254
+ element: HTMLElement;
255
+ effect: KeyframeEffect;
256
+ }
233
257
  /**
234
258
  * State handed from the recorder after stopRecording().
235
259
  * This is everything the scrubber needs to replay frames.
@@ -245,26 +269,32 @@ interface ScrubberState {
245
269
  interceptedAnimations: InterceptedAnimation[];
246
270
  /** Set of CSS properties that are safe (visual-only, no layout side-effects) */
247
271
  SAFE_PROPS_SET: Set<string>;
272
+ /** Seekable WAAPI clones created from recorded animation keyframes */
273
+ seekableClones: Map<string, SeekableClone>;
248
274
  }
249
275
  /**
250
- * Timeline scrubber — applies captured frame snapshots to the live DOM.
276
+ * Timeline scrubber — seeks through recorded animation state using WAAPI.
251
277
  *
252
- * Ported from the inline JS that the Electron version injected via
253
- * executeJavaScript() in timeline-controller.ts (seekTo / releaseAnimations).
278
+ * Instead of applying computed styles as inline overrides (which breaks
279
+ * Tailwind/class-driven layouts), this scrubber creates paused WAAPI
280
+ * animation clones and seeks them via animation.currentTime. The browser
281
+ * computes exact intermediate values natively — the same mechanism Chrome
282
+ * DevTools uses for its Animation panel.
254
283
  */
255
284
  declare class TimelineScrubber {
256
285
  private elements;
257
286
  private frames;
258
287
  private capturedPortals;
259
288
  private interceptedAnimations;
260
- private SAFE_PROPS_SET;
289
+ private seekableClones;
290
+ /** Precomputed frame range per animation for O(1) before/after lookup. */
291
+ private animFrameRanges;
261
292
  /** Saved originals for restore on release */
262
293
  private _originalAnimate;
263
294
  private _originalRaf;
264
295
  private _originalRemoveChild;
265
296
  private _originalRemove;
266
297
  constructor(state: ScrubberState);
267
- private getSelector;
268
298
  seekTo(timeMs: number): void;
269
299
  release(): void;
270
300
  }
@@ -273,4 +303,4 @@ declare function getFrameAtTime(frames: FrameSnapshot[], timeMs: number): FrameS
273
303
  declare function generateExport(animations: AnimationInfo[], frames: FrameSnapshot[], timeMs: number, filter?: ExportFilter): TimelineExport;
274
304
  declare function formatExportForLLM(exp: TimelineExport, detail?: OutputDetailLevel): string;
275
305
 
276
- export { type AnimationInfo, type ElementSnapshot, type ExportFilter, type FrameAnimation, type FrameSnapshot, type InterceptedAnimation, type OutputDetailLevel, type PropertySnapshot, type Rect, SaccadeEngine, type SaccadeState, type ScrubberState, type TimelineCapture, type TimelineExport, TimelineRecorder, TimelineScrubber, TimingController, formatExportForLLM, generateExport, getFrameAtTime };
306
+ export { type AnimationInfo, type ElementSnapshot, type ExportFilter, type FrameAnimation, type FrameSnapshot, type InterceptedAnimation, type OutputDetailLevel, type PropertySnapshot, type Rect, SaccadeEngine, type SaccadeState, type ScrubberState, type SeekableClone, type TimelineCapture, type TimelineExport, TimelineRecorder, TimelineScrubber, TimingController, formatExportForLLM, generateExport, getFrameAtTime };