saccade 0.0.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.
package/dist/core.d.ts ADDED
@@ -0,0 +1,276 @@
1
+ type ExportFilter = 'active' | 'all-animations' | 'all-elements';
2
+ type OutputDetailLevel = 'compact' | 'standard' | 'detailed' | 'forensic';
3
+ type Rect = {
4
+ x: number;
5
+ y: number;
6
+ width: number;
7
+ height: number;
8
+ };
9
+ type AnimationInfo = {
10
+ id: string;
11
+ name: string;
12
+ selector: string;
13
+ elementLabel: string;
14
+ duration: number;
15
+ delay: number;
16
+ easing: string;
17
+ type: string;
18
+ source: string | null;
19
+ resolvedVars: Record<string, string> | null;
20
+ conflicts: string[] | null;
21
+ layoutAnimation?: boolean;
22
+ };
23
+ type PropertySnapshot = {
24
+ property: string;
25
+ value: string;
26
+ from: string | null;
27
+ to: string | null;
28
+ };
29
+ type FrameAnimation = {
30
+ animationId: string;
31
+ currentTime: number;
32
+ progress: number;
33
+ properties: PropertySnapshot[];
34
+ };
35
+ type ElementSnapshot = {
36
+ __styles: Record<string, string>;
37
+ __attrs: Record<string, string | null>;
38
+ __afterOpacity?: string;
39
+ __beforeOpacity?: string;
40
+ };
41
+ type FrameSnapshot = {
42
+ time: number;
43
+ animations: FrameAnimation[];
44
+ elementSnapshots: Record<string, ElementSnapshot>;
45
+ activePortalIds: string[];
46
+ hoveredSels: string[];
47
+ focusSel: string | null;
48
+ pointer: {
49
+ x: number;
50
+ y: number;
51
+ buttons: number;
52
+ };
53
+ scrollPositions: Record<string, {
54
+ x: number;
55
+ y: number;
56
+ }>;
57
+ };
58
+ type TimelineCapture = {
59
+ startTime: number;
60
+ endTime: number;
61
+ duration: number;
62
+ animations: AnimationInfo[];
63
+ frames: FrameSnapshot[];
64
+ boundingBox: Rect | null;
65
+ };
66
+ type TimelineExport = {
67
+ timestamp: string;
68
+ duration: string;
69
+ scrubPosition: number;
70
+ hoveredElements: string[];
71
+ focusedElement: string | null;
72
+ animations: {
73
+ element: string;
74
+ elementLabel: string;
75
+ name: string;
76
+ type: string;
77
+ timing: string;
78
+ progress: string;
79
+ properties: {
80
+ property: string;
81
+ value: string;
82
+ range: string;
83
+ }[];
84
+ source: string | null;
85
+ resolvedVars: Record<string, string> | null;
86
+ conflicts: string[] | null;
87
+ }[];
88
+ };
89
+
90
+ type LapseState = 'idle' | 'recording' | 'scrubbing';
91
+ declare class LapseEngine {
92
+ private timing;
93
+ private recorder;
94
+ private scrubber;
95
+ private capture;
96
+ private _state;
97
+ private listeners;
98
+ get state(): LapseState;
99
+ getCapture(): TimelineCapture | null;
100
+ setSpeed(speed: number): void;
101
+ getSpeed(): number;
102
+ startRecording(boundingBox?: Rect | null): void;
103
+ stopRecording(): TimelineCapture;
104
+ seekTo(timeMs: number): void;
105
+ release(): void;
106
+ generateExport(timeMs: number, filter?: ExportFilter): TimelineExport | null;
107
+ exportForLLM(timeMs: number, filter?: ExportFilter, detail?: OutputDetailLevel): string;
108
+ subscribe(listener: () => void): () => void;
109
+ private notify;
110
+ destroy(): void;
111
+ }
112
+
113
+ /**
114
+ * Timing controller — patches all JS timing APIs to respect a speed factor.
115
+ *
116
+ * At 0.5x speed, a 100ms setTimeout becomes 200ms real time, and
117
+ * performance.now() / rAF timestamps advance at half rate.
118
+ */
119
+ declare class TimingController {
120
+ private speed;
121
+ private realBaseline;
122
+ private virtualBaseline;
123
+ private intervalMap;
124
+ private nextIntervalId;
125
+ private mediaObserver;
126
+ private animObserver;
127
+ private _origAnimate;
128
+ private installed;
129
+ private _raf;
130
+ private _caf;
131
+ private _setTimeout;
132
+ private _clearTimeout;
133
+ private _setInterval;
134
+ private _clearInterval;
135
+ private _perfNow;
136
+ private _dateNow;
137
+ constructor();
138
+ private getVirtualTime;
139
+ private reanchor;
140
+ /** Install timing patches. Safe to call multiple times. */
141
+ install(): void;
142
+ /** Set playback speed. Requires install() first. */
143
+ setSpeed(newSpeed: number): void;
144
+ /** Patch playbackRate on all active CSS transitions/animations via WAAPI. */
145
+ private patchAnimations;
146
+ getSpeed(): number;
147
+ /** Restore all patched APIs to originals. */
148
+ destroy(): void;
149
+ }
150
+
151
+ declare global {
152
+ interface Window {
153
+ __LAPSE_ORIGINAL_RAF__?: typeof requestAnimationFrame;
154
+ }
155
+ }
156
+ /**
157
+ * Build a stable structural selector for `el` (up to 5 ancestors).
158
+ * Used as the element identity key throughout recording & scrubbing.
159
+ */
160
+ declare function getSelector(el: Element): string | null;
161
+ declare class TimelineRecorder {
162
+ private recording;
163
+ private startTime;
164
+ private boundingBox;
165
+ /** Structural-selector -> live DOM element. */
166
+ readonly elements: Map<string, HTMLElement>;
167
+ /** Animation id -> info. */
168
+ readonly animations: Map<string, AnimationInfo>;
169
+ /** Original inline style per element (captured on first encounter). */
170
+ private originalStyles;
171
+ /** Captured frames. */
172
+ private frames;
173
+ private activePortals;
174
+ private portalIdCounter;
175
+ private currentPortalIds;
176
+ private capturedPortals;
177
+ private prevInlineStyles;
178
+ private jsAnimStartTimes;
179
+ private jsAnimLastSeen;
180
+ private jsAnimFromValues;
181
+ private jsAnimChangeCount;
182
+ private currentHoverEls;
183
+ private currentFocusSel;
184
+ private currentPointer;
185
+ private attrObserver;
186
+ private portalObserver;
187
+ private exitObserver;
188
+ private onMouseOver;
189
+ private onMouseOut;
190
+ private onFocusIn;
191
+ private onFocusOut;
192
+ private onPointerMove;
193
+ private onPointerDown;
194
+ private onPointerUp;
195
+ /** Animations captured via Element.prototype.animate monkey-patch. */
196
+ readonly interceptedAnimations: {
197
+ animation: Animation;
198
+ target: Element;
199
+ }[];
200
+ private static readonly MAX_DURATION_MS;
201
+ private static readonly MAX_FRAMES;
202
+ private hiddenSince;
203
+ private onVisibilityChange;
204
+ /** Set to true when the capture loop self-terminates due to limits. */
205
+ autoStopped: boolean;
206
+ /** Called when recording auto-stops. Set by the engine. */
207
+ onAutoStop: (() => void) | null;
208
+ private _removeChild;
209
+ private _remove;
210
+ private _elementAnimate;
211
+ /** `<style>` that disables all transitions/animations after recording. */
212
+ noTransitionsEl: HTMLStyleElement | null;
213
+ /** Full-screen overlay blocking interaction during scrub. */
214
+ blockerEl: HTMLDivElement | null;
215
+ /** `<style>` with cloned :hover / :focus rules rewritten as `[data-lapse-*]`. */
216
+ lapseStyleEl: HTMLStyleElement | null;
217
+ private get _raf();
218
+ getSelector: typeof getSelector;
219
+ get SAFE_PROPS_SET(): Set<string>;
220
+ get capturedPortalIds(): Set<string>;
221
+ startRecording(boundingBox?: Rect | null): void;
222
+ stopRecording(): TimelineCapture;
223
+ }
224
+
225
+ /**
226
+ * Represents a WAAPI Animation intercepted during recording.
227
+ * The recorder monkey-patches Element.prototype.animate to capture these.
228
+ */
229
+ interface InterceptedAnimation {
230
+ animation: Animation;
231
+ target: Element;
232
+ }
233
+ /**
234
+ * State handed from the recorder after stopRecording().
235
+ * This is everything the scrubber needs to replay frames.
236
+ */
237
+ interface ScrubberState {
238
+ /** Selector string -> live DOM element */
239
+ elements: Map<string, HTMLElement>;
240
+ /** Captured frame snapshots from the recording */
241
+ frames: FrameSnapshot[];
242
+ /** All portal IDs ever captured (for show/hide management) */
243
+ capturedPortals: Set<string>;
244
+ /** WAAPI animations intercepted via Element.prototype.animate patch */
245
+ interceptedAnimations: InterceptedAnimation[];
246
+ /** Set of CSS properties that are safe (visual-only, no layout side-effects) */
247
+ SAFE_PROPS_SET: Set<string>;
248
+ }
249
+ /**
250
+ * Timeline scrubber — applies captured frame snapshots to the live DOM.
251
+ *
252
+ * Ported from the inline JS that the Electron version injected via
253
+ * executeJavaScript() in timeline-controller.ts (seekTo / releaseAnimations).
254
+ */
255
+ declare class TimelineScrubber {
256
+ private elements;
257
+ private frames;
258
+ private capturedPortals;
259
+ private interceptedAnimations;
260
+ private SAFE_PROPS_SET;
261
+ /** Saved originals for restore on release */
262
+ private _originalAnimate;
263
+ private _originalRaf;
264
+ private _originalRemoveChild;
265
+ private _originalRemove;
266
+ constructor(state: ScrubberState);
267
+ private getSelector;
268
+ seekTo(timeMs: number): void;
269
+ release(): void;
270
+ }
271
+
272
+ declare function getFrameAtTime(frames: FrameSnapshot[], timeMs: number): FrameSnapshot | null;
273
+ declare function generateExport(animations: AnimationInfo[], frames: FrameSnapshot[], timeMs: number, filter?: ExportFilter): TimelineExport;
274
+ declare function formatExportForLLM(exp: TimelineExport, detail?: OutputDetailLevel): string;
275
+
276
+ export { type AnimationInfo, type ElementSnapshot, type ExportFilter, type FrameAnimation, type FrameSnapshot, type InterceptedAnimation, LapseEngine, type LapseState, type OutputDetailLevel, type PropertySnapshot, type Rect, type ScrubberState, type TimelineCapture, type TimelineExport, TimelineRecorder, TimelineScrubber, TimingController, formatExportForLLM, generateExport, getFrameAtTime };