saccade 0.1.0 → 0.2.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
@@ -1,5 +1,5 @@
1
1
  type ExportFilter = 'active' | 'all-animations' | 'all-elements';
2
- type OutputDetailLevel = 'compact' | 'standard' | 'detailed' | 'forensic';
2
+ type OutputDetailLevel = 'brief' | 'moderate' | 'detailed' | 'granular';
3
3
  type Rect = {
4
4
  x: number;
5
5
  y: number;
@@ -101,6 +101,18 @@ declare class SaccadeEngine {
101
101
  getCapture(): TimelineCapture | null;
102
102
  setSpeed(speed: number): void;
103
103
  getSpeed(): number;
104
+ /**
105
+ * Install the timing patches immediately, without changing speed.
106
+ *
107
+ * Call this as early as possible (before app code, GSAP, or Framer Motion
108
+ * run) so they capture the patched time functions rather than the originals.
109
+ * Idempotent and harmless to call more than once. `setSpeed` and
110
+ * `startRecording` also install on demand, so this is only needed to win the
111
+ * early-load race against libraries that cache `Date.now`/`performance.now`.
112
+ */
113
+ install(): void;
114
+ /** Register a module-imported GSAP instance so saccade can slow it. */
115
+ registerGSAP(gsap: any): void;
104
116
  startRecording(boundingBox?: Rect | null): void;
105
117
  stopRecording(): TimelineCapture;
106
118
  seekTo(timeMs: number): void;
@@ -112,6 +124,10 @@ declare class SaccadeEngine {
112
124
  destroy(): void;
113
125
  }
114
126
 
127
+ declare function getSharedEngine(): SaccadeEngine;
128
+ /** Test/teardown hook — drops the singleton so the next get() makes a fresh one. */
129
+ declare function resetSharedEngine(): void;
130
+
115
131
  /**
116
132
  * Timing controller — patches all JS timing APIs to respect a speed factor.
117
133
  *
@@ -129,6 +145,7 @@ declare class TimingController {
129
145
  private _origAnimate;
130
146
  private installed;
131
147
  private animPollId;
148
+ private gsapInstance;
132
149
  private trackedAnims;
133
150
  private trackedMedia;
134
151
  private _raf;
@@ -155,6 +172,11 @@ declare class TimingController {
155
172
  private patchAnimations;
156
173
  /** Patch playbackRate on all video/audio elements. */
157
174
  private patchMedia;
175
+ /**
176
+ * Register a GSAP instance (for ES-module imports where window.gsap is
177
+ * undefined). Applies the current timeScale immediately if speed !== 1.
178
+ */
179
+ registerGSAP(gsap: any): void;
158
180
  /** Sync GSAP's global timeline if present. */
159
181
  private patchGSAP;
160
182
  /** Restore all patched APIs to originals. */
@@ -303,4 +325,4 @@ declare function getFrameAtTime(frames: FrameSnapshot[], timeMs: number): FrameS
303
325
  declare function generateExport(animations: AnimationInfo[], frames: FrameSnapshot[], timeMs: number, filter?: ExportFilter): TimelineExport;
304
326
  declare function formatExportForLLM(exp: TimelineExport, detail?: OutputDetailLevel): string;
305
327
 
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 };
328
+ 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, getSharedEngine, resetSharedEngine };
package/dist/core.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  type ExportFilter = 'active' | 'all-animations' | 'all-elements';
2
- type OutputDetailLevel = 'compact' | 'standard' | 'detailed' | 'forensic';
2
+ type OutputDetailLevel = 'brief' | 'moderate' | 'detailed' | 'granular';
3
3
  type Rect = {
4
4
  x: number;
5
5
  y: number;
@@ -101,6 +101,18 @@ declare class SaccadeEngine {
101
101
  getCapture(): TimelineCapture | null;
102
102
  setSpeed(speed: number): void;
103
103
  getSpeed(): number;
104
+ /**
105
+ * Install the timing patches immediately, without changing speed.
106
+ *
107
+ * Call this as early as possible (before app code, GSAP, or Framer Motion
108
+ * run) so they capture the patched time functions rather than the originals.
109
+ * Idempotent and harmless to call more than once. `setSpeed` and
110
+ * `startRecording` also install on demand, so this is only needed to win the
111
+ * early-load race against libraries that cache `Date.now`/`performance.now`.
112
+ */
113
+ install(): void;
114
+ /** Register a module-imported GSAP instance so saccade can slow it. */
115
+ registerGSAP(gsap: any): void;
104
116
  startRecording(boundingBox?: Rect | null): void;
105
117
  stopRecording(): TimelineCapture;
106
118
  seekTo(timeMs: number): void;
@@ -112,6 +124,10 @@ declare class SaccadeEngine {
112
124
  destroy(): void;
113
125
  }
114
126
 
127
+ declare function getSharedEngine(): SaccadeEngine;
128
+ /** Test/teardown hook — drops the singleton so the next get() makes a fresh one. */
129
+ declare function resetSharedEngine(): void;
130
+
115
131
  /**
116
132
  * Timing controller — patches all JS timing APIs to respect a speed factor.
117
133
  *
@@ -129,6 +145,7 @@ declare class TimingController {
129
145
  private _origAnimate;
130
146
  private installed;
131
147
  private animPollId;
148
+ private gsapInstance;
132
149
  private trackedAnims;
133
150
  private trackedMedia;
134
151
  private _raf;
@@ -155,6 +172,11 @@ declare class TimingController {
155
172
  private patchAnimations;
156
173
  /** Patch playbackRate on all video/audio elements. */
157
174
  private patchMedia;
175
+ /**
176
+ * Register a GSAP instance (for ES-module imports where window.gsap is
177
+ * undefined). Applies the current timeScale immediately if speed !== 1.
178
+ */
179
+ registerGSAP(gsap: any): void;
158
180
  /** Sync GSAP's global timeline if present. */
159
181
  private patchGSAP;
160
182
  /** Restore all patched APIs to originals. */
@@ -303,4 +325,4 @@ declare function getFrameAtTime(frames: FrameSnapshot[], timeMs: number): FrameS
303
325
  declare function generateExport(animations: AnimationInfo[], frames: FrameSnapshot[], timeMs: number, filter?: ExportFilter): TimelineExport;
304
326
  declare function formatExportForLLM(exp: TimelineExport, detail?: OutputDetailLevel): string;
305
327
 
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 };
328
+ 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, getSharedEngine, resetSharedEngine };
package/dist/core.mjs CHANGED
@@ -11,6 +11,7 @@ var TimingController = class {
11
11
  this._origAnimate = null;
12
12
  this.installed = false;
13
13
  this.animPollId = 0;
14
+ this.gsapInstance = null;
14
15
  // WeakMap tracking for animations and media
15
16
  this.trackedAnims = /* @__PURE__ */ new WeakMap();
16
17
  this.trackedMedia = /* @__PURE__ */ new WeakMap();
@@ -186,13 +187,19 @@ var TimingController = class {
186
187
  } catch {
187
188
  }
188
189
  }
190
+ /**
191
+ * Register a GSAP instance (for ES-module imports where window.gsap is
192
+ * undefined). Applies the current timeScale immediately if speed !== 1.
193
+ */
194
+ registerGSAP(gsap) {
195
+ this.gsapInstance = gsap;
196
+ if (this.speed !== 1) this.patchGSAP();
197
+ }
189
198
  /** Sync GSAP's global timeline if present. */
190
199
  patchGSAP() {
191
200
  try {
192
- const gsap = window.gsap;
193
- if (gsap?.globalTimeline) {
194
- gsap.globalTimeline.timeScale(this.speed || 1e-3);
195
- }
201
+ const gsap = this.gsapInstance ?? window.gsap;
202
+ gsap?.globalTimeline?.timeScale(this.speed || 1e-3);
196
203
  } catch {
197
204
  }
198
205
  }
@@ -238,10 +245,11 @@ var TimingController = class {
238
245
  } catch {
239
246
  }
240
247
  try {
241
- const gsap = window.gsap;
242
- if (gsap?.globalTimeline) gsap.globalTimeline.timeScale(1);
248
+ const gsap = this.gsapInstance ?? window.gsap;
249
+ gsap?.globalTimeline?.timeScale(1);
243
250
  } catch {
244
251
  }
252
+ this.gsapInstance = null;
245
253
  delete window.__LAPSE_ORIGINAL_RAF__;
246
254
  delete window.__saccadeInstalled;
247
255
  this.installed = false;
@@ -1516,7 +1524,7 @@ function generateExport(animations, frames, timeMs, filter = "active") {
1516
1524
  animations: animExports
1517
1525
  };
1518
1526
  }
1519
- function formatExportForLLM(exp, detail = "standard") {
1527
+ function formatExportForLLM(exp, detail = "moderate") {
1520
1528
  const lines = [];
1521
1529
  const grouped = /* @__PURE__ */ new Map();
1522
1530
  for (const anim of exp.animations) {
@@ -1531,7 +1539,7 @@ function formatExportForLLM(exp, detail = "standard") {
1531
1539
  const [from, to] = prop.range.split(" \u2192 ");
1532
1540
  return !(from && to && from.trim() === to.trim());
1533
1541
  }
1534
- if (detail === "compact") {
1542
+ if (detail === "brief") {
1535
1543
  lines.push(`# Animation State at ${exp.timestamp}`);
1536
1544
  lines.push("");
1537
1545
  for (const [, group] of grouped) {
@@ -1556,7 +1564,7 @@ function formatExportForLLM(exp, detail = "standard") {
1556
1564
  }
1557
1565
  lines.push(`# Animation State at ${exp.timestamp}`);
1558
1566
  lines.push("");
1559
- if (detail === "forensic") {
1567
+ if (detail === "granular") {
1560
1568
  lines.push("**Environment:**");
1561
1569
  lines.push(`- Viewport: ${window.innerWidth}\xD7${window.innerHeight}`);
1562
1570
  lines.push(`- URL: ${window.location.href}`);
@@ -1623,7 +1631,7 @@ function formatExportForLLM(exp, detail = "standard") {
1623
1631
  lines.push(`Transitions: ${[...transitionSet].join(", ")}`);
1624
1632
  lines.push("");
1625
1633
  for (const line of cssPropLines) lines.push(line);
1626
- if (detail === "detailed" || detail === "forensic") {
1634
+ if (detail === "detailed" || detail === "granular") {
1627
1635
  const allVars = {};
1628
1636
  for (const anim of cssAnims) {
1629
1637
  if (anim.resolvedVars) Object.assign(allVars, anim.resolvedVars);
@@ -1678,6 +1686,22 @@ var SaccadeEngine = class {
1678
1686
  getSpeed() {
1679
1687
  return this.timing.getSpeed();
1680
1688
  }
1689
+ /**
1690
+ * Install the timing patches immediately, without changing speed.
1691
+ *
1692
+ * Call this as early as possible (before app code, GSAP, or Framer Motion
1693
+ * run) so they capture the patched time functions rather than the originals.
1694
+ * Idempotent and harmless to call more than once. `setSpeed` and
1695
+ * `startRecording` also install on demand, so this is only needed to win the
1696
+ * early-load race against libraries that cache `Date.now`/`performance.now`.
1697
+ */
1698
+ install() {
1699
+ this.timing.install();
1700
+ }
1701
+ /** Register a module-imported GSAP instance so saccade can slow it. */
1702
+ registerGSAP(gsap) {
1703
+ this.timing.registerGSAP(gsap);
1704
+ }
1681
1705
  // -- Timeline recording ---------------------------------------------------
1682
1706
  startRecording(boundingBox) {
1683
1707
  if (this._state !== "idle") return;
@@ -1752,7 +1776,7 @@ var SaccadeEngine = class {
1752
1776
  filter
1753
1777
  );
1754
1778
  }
1755
- exportForLLM(timeMs, filter = "active", detail = "standard") {
1779
+ exportForLLM(timeMs, filter = "active", detail = "moderate") {
1756
1780
  const exp = this.generateExport(timeMs, filter);
1757
1781
  if (!exp) return "";
1758
1782
  return formatExportForLLM(exp, detail);
@@ -1776,6 +1800,19 @@ var SaccadeEngine = class {
1776
1800
  this._state = "idle";
1777
1801
  }
1778
1802
  };
1803
+
1804
+ // src/core/shared.ts
1805
+ var KEY = "__saccadeSharedEngine__";
1806
+ function getSharedEngine() {
1807
+ const g = globalThis;
1808
+ if (!g[KEY]) g[KEY] = new SaccadeEngine();
1809
+ return g[KEY];
1810
+ }
1811
+ function resetSharedEngine() {
1812
+ const g = globalThis;
1813
+ g[KEY]?.destroy();
1814
+ g[KEY] = null;
1815
+ }
1779
1816
  export {
1780
1817
  SaccadeEngine,
1781
1818
  TimelineRecorder,
@@ -1783,6 +1820,8 @@ export {
1783
1820
  TimingController,
1784
1821
  formatExportForLLM,
1785
1822
  generateExport,
1786
- getFrameAtTime
1823
+ getFrameAtTime,
1824
+ getSharedEngine,
1825
+ resetSharedEngine
1787
1826
  };
1788
1827
  //# sourceMappingURL=core.mjs.map