saccade 0.0.2 → 0.0.3

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/index.d.cts CHANGED
@@ -2,12 +2,12 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
 
5
- type LapsePosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
6
- type LapseProps = {
5
+ type SaccadePosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
6
+ type SaccadeProps = {
7
7
  /** Panel position. Default: 'bottom-left' */
8
- position?: LapsePosition;
8
+ position?: SaccadePosition;
9
9
  };
10
- declare function Lapse({ position }: LapseProps): react_jsx_runtime.JSX.Element;
10
+ declare function Saccade({ position }: SaccadeProps): react_jsx_runtime.JSX.Element;
11
11
 
12
12
  type ExportFilter = 'active' | 'all-animations' | 'all-elements';
13
13
  type OutputDetailLevel = 'compact' | 'standard' | 'detailed' | 'forensic';
@@ -98,15 +98,15 @@ type TimelineExport = {
98
98
  }[];
99
99
  };
100
100
 
101
- type LapseState = 'idle' | 'recording' | 'scrubbing';
102
- declare class LapseEngine {
101
+ type SaccadeState = 'idle' | 'recording' | 'scrubbing';
102
+ declare class SaccadeEngine {
103
103
  private timing;
104
104
  private recorder;
105
105
  private scrubber;
106
106
  private capture;
107
107
  private _state;
108
108
  private listeners;
109
- get state(): LapseState;
109
+ get state(): SaccadeState;
110
110
  getCapture(): TimelineCapture | null;
111
111
  setSpeed(speed: number): void;
112
112
  getSpeed(): number;
@@ -121,13 +121,13 @@ declare class LapseEngine {
121
121
  destroy(): void;
122
122
  }
123
123
 
124
- declare function LapseProvider({ children }: {
124
+ declare function SaccadeProvider({ children }: {
125
125
  children: ReactNode;
126
126
  }): react_jsx_runtime.JSX.Element;
127
- declare function useLapseEngine(): LapseEngine;
127
+ declare function useSaccadeEngine(): SaccadeEngine;
128
128
 
129
129
  declare function useTimeline(): {
130
- state: LapseState;
130
+ state: SaccadeState;
131
131
  capture: TimelineCapture | null;
132
132
  scrubTime: number;
133
133
  copied: boolean;
@@ -149,4 +149,4 @@ declare function useSpeed(): {
149
149
  togglePause: () => void;
150
150
  };
151
151
 
152
- export { type AnimationInfo, type ExportFilter, type FrameSnapshot, Lapse, LapseEngine, type LapsePosition, type LapseProps, LapseProvider, type LapseState, type OutputDetailLevel, type Rect, type TimelineCapture, type TimelineExport, useLapseEngine, useSpeed, useTimeline };
152
+ export { type AnimationInfo, type ExportFilter, type FrameSnapshot, type OutputDetailLevel, type Rect, Saccade, SaccadeEngine, type SaccadePosition, type SaccadeProps, SaccadeProvider, type SaccadeState, type TimelineCapture, type TimelineExport, useSaccadeEngine, useSpeed, useTimeline };
package/dist/index.d.ts CHANGED
@@ -2,12 +2,12 @@ import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import * as react from 'react';
3
3
  import { ReactNode } from 'react';
4
4
 
5
- type LapsePosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
6
- type LapseProps = {
5
+ type SaccadePosition = 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
6
+ type SaccadeProps = {
7
7
  /** Panel position. Default: 'bottom-left' */
8
- position?: LapsePosition;
8
+ position?: SaccadePosition;
9
9
  };
10
- declare function Lapse({ position }: LapseProps): react_jsx_runtime.JSX.Element;
10
+ declare function Saccade({ position }: SaccadeProps): react_jsx_runtime.JSX.Element;
11
11
 
12
12
  type ExportFilter = 'active' | 'all-animations' | 'all-elements';
13
13
  type OutputDetailLevel = 'compact' | 'standard' | 'detailed' | 'forensic';
@@ -98,15 +98,15 @@ type TimelineExport = {
98
98
  }[];
99
99
  };
100
100
 
101
- type LapseState = 'idle' | 'recording' | 'scrubbing';
102
- declare class LapseEngine {
101
+ type SaccadeState = 'idle' | 'recording' | 'scrubbing';
102
+ declare class SaccadeEngine {
103
103
  private timing;
104
104
  private recorder;
105
105
  private scrubber;
106
106
  private capture;
107
107
  private _state;
108
108
  private listeners;
109
- get state(): LapseState;
109
+ get state(): SaccadeState;
110
110
  getCapture(): TimelineCapture | null;
111
111
  setSpeed(speed: number): void;
112
112
  getSpeed(): number;
@@ -121,13 +121,13 @@ declare class LapseEngine {
121
121
  destroy(): void;
122
122
  }
123
123
 
124
- declare function LapseProvider({ children }: {
124
+ declare function SaccadeProvider({ children }: {
125
125
  children: ReactNode;
126
126
  }): react_jsx_runtime.JSX.Element;
127
- declare function useLapseEngine(): LapseEngine;
127
+ declare function useSaccadeEngine(): SaccadeEngine;
128
128
 
129
129
  declare function useTimeline(): {
130
- state: LapseState;
130
+ state: SaccadeState;
131
131
  capture: TimelineCapture | null;
132
132
  scrubTime: number;
133
133
  copied: boolean;
@@ -149,4 +149,4 @@ declare function useSpeed(): {
149
149
  togglePause: () => void;
150
150
  };
151
151
 
152
- export { type AnimationInfo, type ExportFilter, type FrameSnapshot, Lapse, LapseEngine, type LapsePosition, type LapseProps, LapseProvider, type LapseState, type OutputDetailLevel, type Rect, type TimelineCapture, type TimelineExport, useLapseEngine, useSpeed, useTimeline };
152
+ export { type AnimationInfo, type ExportFilter, type FrameSnapshot, type OutputDetailLevel, type Rect, Saccade, SaccadeEngine, type SaccadePosition, type SaccadeProps, SaccadeProvider, type SaccadeState, type TimelineCapture, type TimelineExport, useSaccadeEngine, useSpeed, useTimeline };
package/dist/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  "use client";
2
2
 
3
- // src/react/Lapse.tsx
3
+ // src/react/Saccade.tsx
4
4
  import { useRef as useRef6, useState as useState4, useEffect as useEffect4 } from "react";
5
5
  import { createPortal } from "react-dom";
6
6
 
7
- // src/react/LapseContext.tsx
7
+ // src/react/SaccadeContext.tsx
8
8
  import { createContext, useContext, useRef } from "react";
9
9
 
10
10
  // src/core/timing.ts
@@ -1030,64 +1030,61 @@ var _TimelineRecorder = class _TimelineRecorder {
1030
1030
  blocker.title = "Clear the timeline to interact with the page";
1031
1031
  document.body.appendChild(blocker);
1032
1032
  this.blockerEl = blocker;
1033
- try {
1034
- const lapseStyle = document.createElement("style");
1035
- lapseStyle.id = "__lapse-state-rules";
1036
- let allCss = "";
1037
- for (const style of document.querySelectorAll("style")) {
1038
- if (style.id === "__lapse-state-rules") continue;
1039
- allCss += style.textContent + "\n";
1040
- }
1041
- for (const sheet of document.styleSheets) {
1042
- try {
1043
- let walk2 = function(rules) {
1044
- for (const rule of rules) {
1045
- if (rule.cssRules) {
1046
- walk2(rule.cssRules);
1047
- continue;
1048
- }
1049
- const t = rule.cssText;
1050
- if (t && (t.includes(":hover") || t.includes(":focus"))) {
1051
- allCss += t + "\n";
1033
+ setTimeout(() => {
1034
+ try {
1035
+ const lapseStyle = document.createElement("style");
1036
+ lapseStyle.id = "__lapse-state-rules";
1037
+ const hoverFocusRules = [];
1038
+ for (const sheet of document.styleSheets) {
1039
+ try {
1040
+ const walk = (rules) => {
1041
+ for (const rule of rules) {
1042
+ if (rule.cssRules) {
1043
+ walk(rule.cssRules);
1044
+ continue;
1045
+ }
1046
+ const t = rule.cssText;
1047
+ if (t && (t.includes(":hover") || t.includes(":focus"))) {
1048
+ hoverFocusRules.push(t);
1049
+ }
1052
1050
  }
1053
- }
1054
- };
1055
- var walk = walk2;
1056
- walk2(sheet.cssRules);
1057
- } catch (_) {
1058
- }
1059
- }
1060
- const stateRegex = /([^{}]*(?::hover|:focus-visible|:focus-within|:focus(?!-))[^{}]*)\{([^{}]*)\}/g;
1061
- let match;
1062
- while ((match = stateRegex.exec(allCss)) !== null) {
1063
- const selector = match[1].trim();
1064
- const body = match[2].trim();
1065
- if (!body) continue;
1066
- const newBody = body.replace(
1067
- /([^;:]+):\s*([^;]+)(;|$)/g,
1068
- (m, prop, val, end) => {
1069
- if (val.includes("!important")) return m;
1070
- return prop + ": " + val.trim() + " !important" + end;
1051
+ };
1052
+ walk(sheet.cssRules);
1053
+ } catch (_) {
1071
1054
  }
1072
- );
1073
- if (selector.includes(":hover")) {
1074
- lapseStyle.textContent += selector.replace(/:hover/g, "[data-lapse-hover]") + " { " + newBody + " }\n";
1075
1055
  }
1076
- if (selector.includes(":focus-visible")) {
1077
- lapseStyle.textContent += selector.replace(/:focus-visible/g, "[data-lapse-focus]") + " { " + newBody + " }\n";
1078
- } else if (selector.includes(":focus-within")) {
1079
- lapseStyle.textContent += selector.replace(
1080
- /:focus-within/g,
1081
- ":has([data-lapse-focus])"
1082
- ) + " { " + newBody + " }\n";
1083
- } else if (selector.includes(":focus")) {
1084
- lapseStyle.textContent += selector.replace(/:focus(?!-)/g, "[data-lapse-focus]") + " { " + newBody + " }\n";
1056
+ const parts = [];
1057
+ for (const ruleText of hoverFocusRules) {
1058
+ const braceIdx = ruleText.indexOf("{");
1059
+ if (braceIdx === -1) continue;
1060
+ const selector = ruleText.slice(0, braceIdx).trim();
1061
+ const bodyEnd = ruleText.lastIndexOf("}");
1062
+ const body = ruleText.slice(braceIdx + 1, bodyEnd).trim();
1063
+ if (!body) continue;
1064
+ const newBody = body.replace(
1065
+ /([^;:]+):\s*([^;]+)(;|$)/g,
1066
+ (m, prop, val, end) => {
1067
+ if (val.includes("!important")) return m;
1068
+ return prop + ": " + val.trim() + " !important" + end;
1069
+ }
1070
+ );
1071
+ if (selector.includes(":hover")) {
1072
+ parts.push(selector.replace(/:hover/g, "[data-lapse-hover]") + " { " + newBody + " }");
1073
+ }
1074
+ if (selector.includes(":focus-visible")) {
1075
+ parts.push(selector.replace(/:focus-visible/g, "[data-lapse-focus]") + " { " + newBody + " }");
1076
+ } else if (selector.includes(":focus-within")) {
1077
+ parts.push(selector.replace(/:focus-within/g, ":has([data-lapse-focus])") + " { " + newBody + " }");
1078
+ } else if (selector.includes(":focus")) {
1079
+ parts.push(selector.replace(/:focus(?!-)/g, "[data-lapse-focus]") + " { " + newBody + " }");
1080
+ }
1085
1081
  }
1082
+ lapseStyle.textContent = parts.join("\n");
1083
+ document.head.appendChild(lapseStyle);
1084
+ this.lapseStyleEl = lapseStyle;
1085
+ } catch (_) {
1086
1086
  }
1087
- document.head.appendChild(lapseStyle);
1088
- this.lapseStyleEl = lapseStyle;
1089
- } catch (_) {
1090
- }
1087
+ }, 0);
1091
1088
  if (this.frames.length > 0) {
1092
1089
  const frame0 = this.frames[0];
1093
1090
  if (frame0.elementSnapshots) {
@@ -1600,7 +1597,7 @@ function formatExportForLLM(exp, detail = "standard") {
1600
1597
  }
1601
1598
 
1602
1599
  // src/core/engine.ts
1603
- var LapseEngine = class {
1600
+ var SaccadeEngine = class {
1604
1601
  constructor() {
1605
1602
  this.timing = new TimingController();
1606
1603
  this.recorder = new TimelineRecorder();
@@ -1642,7 +1639,25 @@ var LapseEngine = class {
1642
1639
  boundingBox: null
1643
1640
  };
1644
1641
  }
1645
- const capture = this.recorder.stopRecording();
1642
+ let capture;
1643
+ try {
1644
+ capture = this.recorder.stopRecording();
1645
+ } catch (e) {
1646
+ console.error("[Saccade] stopRecording failed:", e);
1647
+ document.getElementById("__lapse-scrub-blocker")?.remove();
1648
+ document.getElementById("__lapse-no-transitions")?.remove();
1649
+ document.getElementById("__lapse-state-rules")?.remove();
1650
+ this._state = "idle";
1651
+ this.notify();
1652
+ return {
1653
+ startTime: 0,
1654
+ endTime: 0,
1655
+ duration: 0,
1656
+ animations: [],
1657
+ frames: [],
1658
+ boundingBox: null
1659
+ };
1660
+ }
1646
1661
  this.capture = capture;
1647
1662
  const scrubberState = {
1648
1663
  elements: this.recorder.elements,
@@ -1702,23 +1717,23 @@ var LapseEngine = class {
1702
1717
  }
1703
1718
  };
1704
1719
 
1705
- // src/react/LapseContext.tsx
1720
+ // src/react/SaccadeContext.tsx
1706
1721
  import { jsx } from "react/jsx-runtime";
1707
- var LapseContext = createContext(null);
1708
- function LapseProvider({ children }) {
1722
+ var SaccadeContext = createContext(null);
1723
+ function SaccadeProvider({ children }) {
1709
1724
  const engineRef = useRef(null);
1710
1725
  if (!engineRef.current) {
1711
- engineRef.current = new LapseEngine();
1726
+ engineRef.current = new SaccadeEngine();
1712
1727
  }
1713
- return /* @__PURE__ */ jsx(LapseContext.Provider, { value: engineRef.current, children });
1728
+ return /* @__PURE__ */ jsx(SaccadeContext.Provider, { value: engineRef.current, children });
1714
1729
  }
1715
- function useLapseEngine() {
1716
- const engine = useContext(LapseContext);
1717
- if (!engine) throw new Error("useLapseEngine must be used within <LapseProvider>");
1730
+ function useSaccadeEngine() {
1731
+ const engine = useContext(SaccadeContext);
1732
+ if (!engine) throw new Error("useSaccadeEngine must be used within <SaccadeProvider>");
1718
1733
  return engine;
1719
1734
  }
1720
1735
 
1721
- // src/react/LapsePanel.tsx
1736
+ // src/react/SaccadePanel.tsx
1722
1737
  import { useRef as useRef5, useCallback as useCallback5 } from "react";
1723
1738
 
1724
1739
  // src/react/Timeline.tsx
@@ -2112,7 +2127,7 @@ function SpeedControl({ speed, isPaused, onSetSpeed, onTogglePause }) {
2112
2127
  import { useState as useState2, useCallback as useCallback3, useEffect as useEffect2, useRef as useRef4, useSyncExternalStore } from "react";
2113
2128
  var DETAIL_LEVELS = ["compact", "standard", "detailed", "forensic"];
2114
2129
  function useTimeline() {
2115
- const engine = useLapseEngine();
2130
+ const engine = useSaccadeEngine();
2116
2131
  const state = useSyncExternalStore(
2117
2132
  (cb) => engine.subscribe(cb),
2118
2133
  () => engine.state
@@ -2146,9 +2161,13 @@ function useTimeline() {
2146
2161
  [engine]
2147
2162
  );
2148
2163
  const stopRecording = useCallback3(() => {
2149
- const result = engine.stopRecording();
2150
- setCapture(result);
2151
- setScrubTime(0);
2164
+ try {
2165
+ const result = engine.stopRecording();
2166
+ setCapture(result);
2167
+ setScrubTime(0);
2168
+ } catch (e) {
2169
+ console.error("[Saccade] stopRecording failed:", e);
2170
+ }
2152
2171
  }, [engine]);
2153
2172
  const seek = useCallback3(
2154
2173
  (timeMs) => {
@@ -2217,7 +2236,7 @@ function useTimeline() {
2217
2236
  // src/react/useSpeed.ts
2218
2237
  import { useState as useState3, useCallback as useCallback4, useEffect as useEffect3 } from "react";
2219
2238
  function useSpeed() {
2220
- const engine = useLapseEngine();
2239
+ const engine = useSaccadeEngine();
2221
2240
  const [speed, setSpeedState] = useState3(1);
2222
2241
  const [previousSpeed, setPreviousSpeed] = useState3(1);
2223
2242
  const [isPaused, setIsPaused] = useState3(false);
@@ -2280,9 +2299,9 @@ function useSpeed() {
2280
2299
  return { speed, isPaused, setSpeed, togglePause };
2281
2300
  }
2282
2301
 
2283
- // src/react/LapsePanel.tsx
2302
+ // src/react/SaccadePanel.tsx
2284
2303
  import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
2285
- function LapsePanel() {
2304
+ function SaccadePanel() {
2286
2305
  const timeline = useTimeline();
2287
2306
  const { speed, isPaused, setSpeed, togglePause } = useSpeed();
2288
2307
  const panelRef = useRef5(null);
@@ -2742,9 +2761,9 @@ var PANEL_STYLES = (
2742
2761
  `
2743
2762
  );
2744
2763
 
2745
- // src/react/Lapse.tsx
2764
+ // src/react/Saccade.tsx
2746
2765
  import { jsx as jsx5 } from "react/jsx-runtime";
2747
- function Lapse({ position = "bottom-left" }) {
2766
+ function Saccade({ position = "bottom-left" }) {
2748
2767
  const hostRef = useRef6(null);
2749
2768
  const [shadowRoot, setShadowRoot] = useState4(null);
2750
2769
  useEffect4(() => {
@@ -2775,17 +2794,17 @@ function Lapse({ position = "bottom-left" }) {
2775
2794
  ...positionOffset
2776
2795
  },
2777
2796
  children: shadowRoot && createPortal(
2778
- /* @__PURE__ */ jsx5(LapseProvider, { children: /* @__PURE__ */ jsx5(LapsePanel, {}) }),
2797
+ /* @__PURE__ */ jsx5(SaccadeProvider, { children: /* @__PURE__ */ jsx5(SaccadePanel, {}) }),
2779
2798
  shadowRoot.lastElementChild || shadowRoot
2780
2799
  )
2781
2800
  }
2782
2801
  );
2783
2802
  }
2784
2803
  export {
2785
- Lapse,
2786
- LapseEngine,
2787
- LapseProvider,
2788
- useLapseEngine,
2804
+ Saccade,
2805
+ SaccadeEngine,
2806
+ SaccadeProvider,
2807
+ useSaccadeEngine,
2789
2808
  useSpeed,
2790
2809
  useTimeline
2791
2810
  };