saccade 0.0.1 → 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
@@ -1043,11 +1043,12 @@ var _TimelineRecorder = class _TimelineRecorder {
1043
1043
  document.removeEventListener("pointerup", this.onPointerUp, true);
1044
1044
  try {
1045
1045
  for (const anim of document.getAnimations()) {
1046
+ const target = anim.effect?.target;
1047
+ if (target?.closest?.("[data-lapse-panel]")) continue;
1046
1048
  anim.cancel();
1047
1049
  }
1048
1050
  } catch (_) {
1049
1051
  }
1050
- window.requestAnimationFrame = () => 0;
1051
1052
  const noTransitions = document.createElement("style");
1052
1053
  noTransitions.id = "__lapse-no-transitions";
1053
1054
  noTransitions.textContent = "*, *::before, *::after { transition: none !important; animation: none !important; }";
@@ -1059,64 +1060,61 @@ var _TimelineRecorder = class _TimelineRecorder {
1059
1060
  blocker.title = "Clear the timeline to interact with the page";
1060
1061
  document.body.appendChild(blocker);
1061
1062
  this.blockerEl = blocker;
1062
- try {
1063
- const lapseStyle = document.createElement("style");
1064
- lapseStyle.id = "__lapse-state-rules";
1065
- let allCss = "";
1066
- for (const style of document.querySelectorAll("style")) {
1067
- if (style.id === "__lapse-state-rules") continue;
1068
- allCss += style.textContent + "\n";
1069
- }
1070
- for (const sheet of document.styleSheets) {
1071
- try {
1072
- let walk2 = function(rules) {
1073
- for (const rule of rules) {
1074
- if (rule.cssRules) {
1075
- walk2(rule.cssRules);
1076
- continue;
1077
- }
1078
- const t = rule.cssText;
1079
- if (t && (t.includes(":hover") || t.includes(":focus"))) {
1080
- 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
+ }
1081
1080
  }
1082
- }
1083
- };
1084
- var walk = walk2;
1085
- walk2(sheet.cssRules);
1086
- } catch (_) {
1087
- }
1088
- }
1089
- const stateRegex = /([^{}]*(?::hover|:focus-visible|:focus-within|:focus(?!-))[^{}]*)\{([^{}]*)\}/g;
1090
- let match;
1091
- while ((match = stateRegex.exec(allCss)) !== null) {
1092
- const selector = match[1].trim();
1093
- const body = match[2].trim();
1094
- if (!body) continue;
1095
- const newBody = body.replace(
1096
- /([^;:]+):\s*([^;]+)(;|$)/g,
1097
- (m, prop, val, end) => {
1098
- if (val.includes("!important")) return m;
1099
- return prop + ": " + val.trim() + " !important" + end;
1081
+ };
1082
+ walk(sheet.cssRules);
1083
+ } catch (_) {
1100
1084
  }
1101
- );
1102
- if (selector.includes(":hover")) {
1103
- lapseStyle.textContent += selector.replace(/:hover/g, "[data-lapse-hover]") + " { " + newBody + " }\n";
1104
1085
  }
1105
- if (selector.includes(":focus-visible")) {
1106
- lapseStyle.textContent += selector.replace(/:focus-visible/g, "[data-lapse-focus]") + " { " + newBody + " }\n";
1107
- } else if (selector.includes(":focus-within")) {
1108
- lapseStyle.textContent += selector.replace(
1109
- /:focus-within/g,
1110
- ":has([data-lapse-focus])"
1111
- ) + " { " + newBody + " }\n";
1112
- } else if (selector.includes(":focus")) {
1113
- 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
+ }
1114
1111
  }
1112
+ lapseStyle.textContent = parts.join("\n");
1113
+ document.head.appendChild(lapseStyle);
1114
+ this.lapseStyleEl = lapseStyle;
1115
+ } catch (_) {
1115
1116
  }
1116
- document.head.appendChild(lapseStyle);
1117
- this.lapseStyleEl = lapseStyle;
1118
- } catch (_) {
1119
- }
1117
+ }, 0);
1120
1118
  if (this.frames.length > 0) {
1121
1119
  const frame0 = this.frames[0];
1122
1120
  if (frame0.elementSnapshots) {
@@ -1629,7 +1627,7 @@ function formatExportForLLM(exp, detail = "standard") {
1629
1627
  }
1630
1628
 
1631
1629
  // src/core/engine.ts
1632
- var LapseEngine = class {
1630
+ var SaccadeEngine = class {
1633
1631
  constructor() {
1634
1632
  this.timing = new TimingController();
1635
1633
  this.recorder = new TimelineRecorder();
@@ -1671,7 +1669,25 @@ var LapseEngine = class {
1671
1669
  boundingBox: null
1672
1670
  };
1673
1671
  }
1674
- 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
+ }
1675
1691
  this.capture = capture;
1676
1692
  const scrubberState = {
1677
1693
  elements: this.recorder.elements,
@@ -1731,23 +1747,23 @@ var LapseEngine = class {
1731
1747
  }
1732
1748
  };
1733
1749
 
1734
- // src/react/LapseContext.tsx
1750
+ // src/react/SaccadeContext.tsx
1735
1751
  var import_jsx_runtime = require("react/jsx-runtime");
1736
- var LapseContext = (0, import_react.createContext)(null);
1737
- function LapseProvider({ children }) {
1752
+ var SaccadeContext = (0, import_react.createContext)(null);
1753
+ function SaccadeProvider({ children }) {
1738
1754
  const engineRef = (0, import_react.useRef)(null);
1739
1755
  if (!engineRef.current) {
1740
- engineRef.current = new LapseEngine();
1756
+ engineRef.current = new SaccadeEngine();
1741
1757
  }
1742
- 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 });
1743
1759
  }
1744
- function useLapseEngine() {
1745
- const engine = (0, import_react.useContext)(LapseContext);
1746
- 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>");
1747
1763
  return engine;
1748
1764
  }
1749
1765
 
1750
- // src/react/LapsePanel.tsx
1766
+ // src/react/SaccadePanel.tsx
1751
1767
  var import_react6 = require("react");
1752
1768
 
1753
1769
  // src/react/Timeline.tsx
@@ -2141,7 +2157,7 @@ function SpeedControl({ speed, isPaused, onSetSpeed, onTogglePause }) {
2141
2157
  var import_react4 = require("react");
2142
2158
  var DETAIL_LEVELS = ["compact", "standard", "detailed", "forensic"];
2143
2159
  function useTimeline() {
2144
- const engine = useLapseEngine();
2160
+ const engine = useSaccadeEngine();
2145
2161
  const state = (0, import_react4.useSyncExternalStore)(
2146
2162
  (cb) => engine.subscribe(cb),
2147
2163
  () => engine.state
@@ -2175,9 +2191,13 @@ function useTimeline() {
2175
2191
  [engine]
2176
2192
  );
2177
2193
  const stopRecording = (0, import_react4.useCallback)(() => {
2178
- const result = engine.stopRecording();
2179
- setCapture(result);
2180
- 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
+ }
2181
2201
  }, [engine]);
2182
2202
  const seek = (0, import_react4.useCallback)(
2183
2203
  (timeMs) => {
@@ -2246,7 +2266,7 @@ function useTimeline() {
2246
2266
  // src/react/useSpeed.ts
2247
2267
  var import_react5 = require("react");
2248
2268
  function useSpeed() {
2249
- const engine = useLapseEngine();
2269
+ const engine = useSaccadeEngine();
2250
2270
  const [speed, setSpeedState] = (0, import_react5.useState)(1);
2251
2271
  const [previousSpeed, setPreviousSpeed] = (0, import_react5.useState)(1);
2252
2272
  const [isPaused, setIsPaused] = (0, import_react5.useState)(false);
@@ -2309,9 +2329,9 @@ function useSpeed() {
2309
2329
  return { speed, isPaused, setSpeed, togglePause };
2310
2330
  }
2311
2331
 
2312
- // src/react/LapsePanel.tsx
2332
+ // src/react/SaccadePanel.tsx
2313
2333
  var import_jsx_runtime4 = require("react/jsx-runtime");
2314
- function LapsePanel() {
2334
+ function SaccadePanel() {
2315
2335
  const timeline = useTimeline();
2316
2336
  const { speed, isPaused, setSpeed, togglePause } = useSpeed();
2317
2337
  const panelRef = (0, import_react6.useRef)(null);
@@ -2771,9 +2791,9 @@ var PANEL_STYLES = (
2771
2791
  `
2772
2792
  );
2773
2793
 
2774
- // src/react/Lapse.tsx
2794
+ // src/react/Saccade.tsx
2775
2795
  var import_jsx_runtime5 = require("react/jsx-runtime");
2776
- function Lapse({ position = "bottom-left" }) {
2796
+ function Saccade({ position = "bottom-left" }) {
2777
2797
  const hostRef = (0, import_react7.useRef)(null);
2778
2798
  const [shadowRoot, setShadowRoot] = (0, import_react7.useState)(null);
2779
2799
  (0, import_react7.useEffect)(() => {
@@ -2804,7 +2824,7 @@ function Lapse({ position = "bottom-left" }) {
2804
2824
  ...positionOffset
2805
2825
  },
2806
2826
  children: shadowRoot && (0, import_react_dom.createPortal)(
2807
- /* @__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, {}) }),
2808
2828
  shadowRoot.lastElementChild || shadowRoot
2809
2829
  )
2810
2830
  }
@@ -2812,10 +2832,10 @@ function Lapse({ position = "bottom-left" }) {
2812
2832
  }
2813
2833
  // Annotate the CommonJS export names for ESM import in node:
2814
2834
  0 && (module.exports = {
2815
- Lapse,
2816
- LapseEngine,
2817
- LapseProvider,
2818
- useLapseEngine,
2835
+ Saccade,
2836
+ SaccadeEngine,
2837
+ SaccadeProvider,
2838
+ useSaccadeEngine,
2819
2839
  useSpeed,
2820
2840
  useTimeline
2821
2841
  });