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.cjs CHANGED
@@ -21,20 +21,20 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  // src/index.ts
22
22
  var src_exports = {};
23
23
  __export(src_exports, {
24
- Lapse: () => Lapse,
25
- LapseEngine: () => LapseEngine,
26
- LapseProvider: () => LapseProvider,
27
- useLapseEngine: () => useLapseEngine,
24
+ Saccade: () => Saccade,
25
+ SaccadeEngine: () => SaccadeEngine,
26
+ SaccadeProvider: () => SaccadeProvider,
27
+ useSaccadeEngine: () => useSaccadeEngine,
28
28
  useSpeed: () => useSpeed,
29
29
  useTimeline: () => useTimeline
30
30
  });
31
31
  module.exports = __toCommonJS(src_exports);
32
32
 
33
- // src/react/Lapse.tsx
33
+ // src/react/Saccade.tsx
34
34
  var import_react7 = require("react");
35
35
  var import_react_dom = require("react-dom");
36
36
 
37
- // src/react/LapseContext.tsx
37
+ // src/react/SaccadeContext.tsx
38
38
  var import_react = require("react");
39
39
 
40
40
  // src/core/timing.ts
@@ -1060,64 +1060,61 @@ var _TimelineRecorder = class _TimelineRecorder {
1060
1060
  blocker.title = "Clear the timeline to interact with the page";
1061
1061
  document.body.appendChild(blocker);
1062
1062
  this.blockerEl = blocker;
1063
- try {
1064
- const lapseStyle = document.createElement("style");
1065
- lapseStyle.id = "__lapse-state-rules";
1066
- let allCss = "";
1067
- for (const style of document.querySelectorAll("style")) {
1068
- if (style.id === "__lapse-state-rules") continue;
1069
- allCss += style.textContent + "\n";
1070
- }
1071
- for (const sheet of document.styleSheets) {
1072
- try {
1073
- let walk2 = function(rules) {
1074
- for (const rule of rules) {
1075
- if (rule.cssRules) {
1076
- walk2(rule.cssRules);
1077
- continue;
1078
- }
1079
- const t = rule.cssText;
1080
- if (t && (t.includes(":hover") || t.includes(":focus"))) {
1081
- allCss += t + "\n";
1063
+ setTimeout(() => {
1064
+ try {
1065
+ const lapseStyle = document.createElement("style");
1066
+ lapseStyle.id = "__lapse-state-rules";
1067
+ const hoverFocusRules = [];
1068
+ for (const sheet of document.styleSheets) {
1069
+ try {
1070
+ const walk = (rules) => {
1071
+ for (const rule of rules) {
1072
+ if (rule.cssRules) {
1073
+ walk(rule.cssRules);
1074
+ continue;
1075
+ }
1076
+ const t = rule.cssText;
1077
+ if (t && (t.includes(":hover") || t.includes(":focus"))) {
1078
+ hoverFocusRules.push(t);
1079
+ }
1082
1080
  }
1083
- }
1084
- };
1085
- var walk = walk2;
1086
- walk2(sheet.cssRules);
1087
- } catch (_) {
1088
- }
1089
- }
1090
- const stateRegex = /([^{}]*(?::hover|:focus-visible|:focus-within|:focus(?!-))[^{}]*)\{([^{}]*)\}/g;
1091
- let match;
1092
- while ((match = stateRegex.exec(allCss)) !== null) {
1093
- const selector = match[1].trim();
1094
- const body = match[2].trim();
1095
- if (!body) continue;
1096
- const newBody = body.replace(
1097
- /([^;:]+):\s*([^;]+)(;|$)/g,
1098
- (m, prop, val, end) => {
1099
- if (val.includes("!important")) return m;
1100
- return prop + ": " + val.trim() + " !important" + end;
1081
+ };
1082
+ walk(sheet.cssRules);
1083
+ } catch (_) {
1101
1084
  }
1102
- );
1103
- if (selector.includes(":hover")) {
1104
- lapseStyle.textContent += selector.replace(/:hover/g, "[data-lapse-hover]") + " { " + newBody + " }\n";
1105
1085
  }
1106
- if (selector.includes(":focus-visible")) {
1107
- lapseStyle.textContent += selector.replace(/:focus-visible/g, "[data-lapse-focus]") + " { " + newBody + " }\n";
1108
- } else if (selector.includes(":focus-within")) {
1109
- lapseStyle.textContent += selector.replace(
1110
- /:focus-within/g,
1111
- ":has([data-lapse-focus])"
1112
- ) + " { " + newBody + " }\n";
1113
- } else if (selector.includes(":focus")) {
1114
- lapseStyle.textContent += selector.replace(/:focus(?!-)/g, "[data-lapse-focus]") + " { " + newBody + " }\n";
1086
+ const parts = [];
1087
+ for (const ruleText of hoverFocusRules) {
1088
+ const braceIdx = ruleText.indexOf("{");
1089
+ if (braceIdx === -1) continue;
1090
+ const selector = ruleText.slice(0, braceIdx).trim();
1091
+ const bodyEnd = ruleText.lastIndexOf("}");
1092
+ const body = ruleText.slice(braceIdx + 1, bodyEnd).trim();
1093
+ if (!body) continue;
1094
+ const newBody = body.replace(
1095
+ /([^;:]+):\s*([^;]+)(;|$)/g,
1096
+ (m, prop, val, end) => {
1097
+ if (val.includes("!important")) return m;
1098
+ return prop + ": " + val.trim() + " !important" + end;
1099
+ }
1100
+ );
1101
+ if (selector.includes(":hover")) {
1102
+ parts.push(selector.replace(/:hover/g, "[data-lapse-hover]") + " { " + newBody + " }");
1103
+ }
1104
+ if (selector.includes(":focus-visible")) {
1105
+ parts.push(selector.replace(/:focus-visible/g, "[data-lapse-focus]") + " { " + newBody + " }");
1106
+ } else if (selector.includes(":focus-within")) {
1107
+ parts.push(selector.replace(/:focus-within/g, ":has([data-lapse-focus])") + " { " + newBody + " }");
1108
+ } else if (selector.includes(":focus")) {
1109
+ parts.push(selector.replace(/:focus(?!-)/g, "[data-lapse-focus]") + " { " + newBody + " }");
1110
+ }
1115
1111
  }
1112
+ lapseStyle.textContent = parts.join("\n");
1113
+ document.head.appendChild(lapseStyle);
1114
+ this.lapseStyleEl = lapseStyle;
1115
+ } catch (_) {
1116
1116
  }
1117
- document.head.appendChild(lapseStyle);
1118
- this.lapseStyleEl = lapseStyle;
1119
- } catch (_) {
1120
- }
1117
+ }, 0);
1121
1118
  if (this.frames.length > 0) {
1122
1119
  const frame0 = this.frames[0];
1123
1120
  if (frame0.elementSnapshots) {
@@ -1630,7 +1627,7 @@ function formatExportForLLM(exp, detail = "standard") {
1630
1627
  }
1631
1628
 
1632
1629
  // src/core/engine.ts
1633
- var LapseEngine = class {
1630
+ var SaccadeEngine = class {
1634
1631
  constructor() {
1635
1632
  this.timing = new TimingController();
1636
1633
  this.recorder = new TimelineRecorder();
@@ -1672,7 +1669,25 @@ var LapseEngine = class {
1672
1669
  boundingBox: null
1673
1670
  };
1674
1671
  }
1675
- const capture = this.recorder.stopRecording();
1672
+ let capture;
1673
+ try {
1674
+ capture = this.recorder.stopRecording();
1675
+ } catch (e) {
1676
+ console.error("[Saccade] stopRecording failed:", e);
1677
+ document.getElementById("__lapse-scrub-blocker")?.remove();
1678
+ document.getElementById("__lapse-no-transitions")?.remove();
1679
+ document.getElementById("__lapse-state-rules")?.remove();
1680
+ this._state = "idle";
1681
+ this.notify();
1682
+ return {
1683
+ startTime: 0,
1684
+ endTime: 0,
1685
+ duration: 0,
1686
+ animations: [],
1687
+ frames: [],
1688
+ boundingBox: null
1689
+ };
1690
+ }
1676
1691
  this.capture = capture;
1677
1692
  const scrubberState = {
1678
1693
  elements: this.recorder.elements,
@@ -1732,23 +1747,23 @@ var LapseEngine = class {
1732
1747
  }
1733
1748
  };
1734
1749
 
1735
- // src/react/LapseContext.tsx
1750
+ // src/react/SaccadeContext.tsx
1736
1751
  var import_jsx_runtime = require("react/jsx-runtime");
1737
- var LapseContext = (0, import_react.createContext)(null);
1738
- function LapseProvider({ children }) {
1752
+ var SaccadeContext = (0, import_react.createContext)(null);
1753
+ function SaccadeProvider({ children }) {
1739
1754
  const engineRef = (0, import_react.useRef)(null);
1740
1755
  if (!engineRef.current) {
1741
- engineRef.current = new LapseEngine();
1756
+ engineRef.current = new SaccadeEngine();
1742
1757
  }
1743
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LapseContext.Provider, { value: engineRef.current, children });
1758
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SaccadeContext.Provider, { value: engineRef.current, children });
1744
1759
  }
1745
- function useLapseEngine() {
1746
- const engine = (0, import_react.useContext)(LapseContext);
1747
- if (!engine) throw new Error("useLapseEngine must be used within <LapseProvider>");
1760
+ function useSaccadeEngine() {
1761
+ const engine = (0, import_react.useContext)(SaccadeContext);
1762
+ if (!engine) throw new Error("useSaccadeEngine must be used within <SaccadeProvider>");
1748
1763
  return engine;
1749
1764
  }
1750
1765
 
1751
- // src/react/LapsePanel.tsx
1766
+ // src/react/SaccadePanel.tsx
1752
1767
  var import_react6 = require("react");
1753
1768
 
1754
1769
  // src/react/Timeline.tsx
@@ -2142,7 +2157,7 @@ function SpeedControl({ speed, isPaused, onSetSpeed, onTogglePause }) {
2142
2157
  var import_react4 = require("react");
2143
2158
  var DETAIL_LEVELS = ["compact", "standard", "detailed", "forensic"];
2144
2159
  function useTimeline() {
2145
- const engine = useLapseEngine();
2160
+ const engine = useSaccadeEngine();
2146
2161
  const state = (0, import_react4.useSyncExternalStore)(
2147
2162
  (cb) => engine.subscribe(cb),
2148
2163
  () => engine.state
@@ -2176,9 +2191,13 @@ function useTimeline() {
2176
2191
  [engine]
2177
2192
  );
2178
2193
  const stopRecording = (0, import_react4.useCallback)(() => {
2179
- const result = engine.stopRecording();
2180
- setCapture(result);
2181
- setScrubTime(0);
2194
+ try {
2195
+ const result = engine.stopRecording();
2196
+ setCapture(result);
2197
+ setScrubTime(0);
2198
+ } catch (e) {
2199
+ console.error("[Saccade] stopRecording failed:", e);
2200
+ }
2182
2201
  }, [engine]);
2183
2202
  const seek = (0, import_react4.useCallback)(
2184
2203
  (timeMs) => {
@@ -2247,7 +2266,7 @@ function useTimeline() {
2247
2266
  // src/react/useSpeed.ts
2248
2267
  var import_react5 = require("react");
2249
2268
  function useSpeed() {
2250
- const engine = useLapseEngine();
2269
+ const engine = useSaccadeEngine();
2251
2270
  const [speed, setSpeedState] = (0, import_react5.useState)(1);
2252
2271
  const [previousSpeed, setPreviousSpeed] = (0, import_react5.useState)(1);
2253
2272
  const [isPaused, setIsPaused] = (0, import_react5.useState)(false);
@@ -2310,9 +2329,9 @@ function useSpeed() {
2310
2329
  return { speed, isPaused, setSpeed, togglePause };
2311
2330
  }
2312
2331
 
2313
- // src/react/LapsePanel.tsx
2332
+ // src/react/SaccadePanel.tsx
2314
2333
  var import_jsx_runtime4 = require("react/jsx-runtime");
2315
- function LapsePanel() {
2334
+ function SaccadePanel() {
2316
2335
  const timeline = useTimeline();
2317
2336
  const { speed, isPaused, setSpeed, togglePause } = useSpeed();
2318
2337
  const panelRef = (0, import_react6.useRef)(null);
@@ -2772,9 +2791,9 @@ var PANEL_STYLES = (
2772
2791
  `
2773
2792
  );
2774
2793
 
2775
- // src/react/Lapse.tsx
2794
+ // src/react/Saccade.tsx
2776
2795
  var import_jsx_runtime5 = require("react/jsx-runtime");
2777
- function Lapse({ position = "bottom-left" }) {
2796
+ function Saccade({ position = "bottom-left" }) {
2778
2797
  const hostRef = (0, import_react7.useRef)(null);
2779
2798
  const [shadowRoot, setShadowRoot] = (0, import_react7.useState)(null);
2780
2799
  (0, import_react7.useEffect)(() => {
@@ -2805,7 +2824,7 @@ function Lapse({ position = "bottom-left" }) {
2805
2824
  ...positionOffset
2806
2825
  },
2807
2826
  children: shadowRoot && (0, import_react_dom.createPortal)(
2808
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LapseProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(LapsePanel, {}) }),
2827
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SaccadeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(SaccadePanel, {}) }),
2809
2828
  shadowRoot.lastElementChild || shadowRoot
2810
2829
  )
2811
2830
  }
@@ -2813,10 +2832,10 @@ function Lapse({ position = "bottom-left" }) {
2813
2832
  }
2814
2833
  // Annotate the CommonJS export names for ESM import in node:
2815
2834
  0 && (module.exports = {
2816
- Lapse,
2817
- LapseEngine,
2818
- LapseProvider,
2819
- useLapseEngine,
2835
+ Saccade,
2836
+ SaccadeEngine,
2837
+ SaccadeProvider,
2838
+ useSaccadeEngine,
2820
2839
  useSpeed,
2821
2840
  useTimeline
2822
2841
  });