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/README.md CHANGED
@@ -13,13 +13,13 @@ npm install saccade -D
13
13
  ## Usage
14
14
 
15
15
  ```tsx
16
- import { Lapse } from 'saccade'
16
+ import { Saccade } from 'saccade'
17
17
 
18
18
  function App() {
19
19
  return (
20
20
  <>
21
21
  <YourApp />
22
- <Lapse />
22
+ <Saccade />
23
23
  </>
24
24
  )
25
25
  }
@@ -51,9 +51,9 @@ Saccade patches timing APIs (`setTimeout`, `setInterval`, `requestAnimationFrame
51
51
  For non-React usage or programmatic control:
52
52
 
53
53
  ```ts
54
- import { LapseEngine } from 'saccade/core'
54
+ import { SaccadeEngine } from 'saccade/core'
55
55
 
56
- const engine = new LapseEngine()
56
+ const engine = new SaccadeEngine()
57
57
 
58
58
  // Speed control
59
59
  engine.setSpeed(0.25) // quarter speed
@@ -77,7 +77,7 @@ engine.destroy()
77
77
  ## React Hooks
78
78
 
79
79
  ```tsx
80
- import { LapseProvider, useLapseEngine, useTimeline, useSpeed } from 'saccade'
80
+ import { SaccadeProvider, useSaccadeEngine, useTimeline, useSpeed } from 'saccade'
81
81
  ```
82
82
 
83
83
  ### `useSpeed`
@@ -127,7 +127,7 @@ import type {
127
127
 
128
128
  ## Requirements
129
129
 
130
- - React 18+ (for the `<Lapse>` component)
130
+ - React 18+ (for the `<Saccade>` component)
131
131
  - No React dependency needed for `saccade/core`
132
132
 
133
133
  ## License
package/dist/core.cjs CHANGED
@@ -20,7 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/core/index.ts
21
21
  var core_exports = {};
22
22
  __export(core_exports, {
23
- LapseEngine: () => LapseEngine,
23
+ SaccadeEngine: () => SaccadeEngine,
24
24
  TimelineRecorder: () => TimelineRecorder,
25
25
  TimelineScrubber: () => TimelineScrubber,
26
26
  TimingController: () => TimingController,
@@ -1053,64 +1053,61 @@ var _TimelineRecorder = class _TimelineRecorder {
1053
1053
  blocker.title = "Clear the timeline to interact with the page";
1054
1054
  document.body.appendChild(blocker);
1055
1055
  this.blockerEl = blocker;
1056
- try {
1057
- const lapseStyle = document.createElement("style");
1058
- lapseStyle.id = "__lapse-state-rules";
1059
- let allCss = "";
1060
- for (const style of document.querySelectorAll("style")) {
1061
- if (style.id === "__lapse-state-rules") continue;
1062
- allCss += style.textContent + "\n";
1063
- }
1064
- for (const sheet of document.styleSheets) {
1065
- try {
1066
- let walk2 = function(rules) {
1067
- for (const rule of rules) {
1068
- if (rule.cssRules) {
1069
- walk2(rule.cssRules);
1070
- continue;
1071
- }
1072
- const t = rule.cssText;
1073
- if (t && (t.includes(":hover") || t.includes(":focus"))) {
1074
- allCss += t + "\n";
1056
+ setTimeout(() => {
1057
+ try {
1058
+ const lapseStyle = document.createElement("style");
1059
+ lapseStyle.id = "__lapse-state-rules";
1060
+ const hoverFocusRules = [];
1061
+ for (const sheet of document.styleSheets) {
1062
+ try {
1063
+ const walk = (rules) => {
1064
+ for (const rule of rules) {
1065
+ if (rule.cssRules) {
1066
+ walk(rule.cssRules);
1067
+ continue;
1068
+ }
1069
+ const t = rule.cssText;
1070
+ if (t && (t.includes(":hover") || t.includes(":focus"))) {
1071
+ hoverFocusRules.push(t);
1072
+ }
1075
1073
  }
1076
- }
1077
- };
1078
- var walk = walk2;
1079
- walk2(sheet.cssRules);
1080
- } catch (_) {
1081
- }
1082
- }
1083
- const stateRegex = /([^{}]*(?::hover|:focus-visible|:focus-within|:focus(?!-))[^{}]*)\{([^{}]*)\}/g;
1084
- let match;
1085
- while ((match = stateRegex.exec(allCss)) !== null) {
1086
- const selector = match[1].trim();
1087
- const body = match[2].trim();
1088
- if (!body) continue;
1089
- const newBody = body.replace(
1090
- /([^;:]+):\s*([^;]+)(;|$)/g,
1091
- (m, prop, val, end) => {
1092
- if (val.includes("!important")) return m;
1093
- return prop + ": " + val.trim() + " !important" + end;
1074
+ };
1075
+ walk(sheet.cssRules);
1076
+ } catch (_) {
1094
1077
  }
1095
- );
1096
- if (selector.includes(":hover")) {
1097
- lapseStyle.textContent += selector.replace(/:hover/g, "[data-lapse-hover]") + " { " + newBody + " }\n";
1098
1078
  }
1099
- if (selector.includes(":focus-visible")) {
1100
- lapseStyle.textContent += selector.replace(/:focus-visible/g, "[data-lapse-focus]") + " { " + newBody + " }\n";
1101
- } else if (selector.includes(":focus-within")) {
1102
- lapseStyle.textContent += selector.replace(
1103
- /:focus-within/g,
1104
- ":has([data-lapse-focus])"
1105
- ) + " { " + newBody + " }\n";
1106
- } else if (selector.includes(":focus")) {
1107
- lapseStyle.textContent += selector.replace(/:focus(?!-)/g, "[data-lapse-focus]") + " { " + newBody + " }\n";
1079
+ const parts = [];
1080
+ for (const ruleText of hoverFocusRules) {
1081
+ const braceIdx = ruleText.indexOf("{");
1082
+ if (braceIdx === -1) continue;
1083
+ const selector = ruleText.slice(0, braceIdx).trim();
1084
+ const bodyEnd = ruleText.lastIndexOf("}");
1085
+ const body = ruleText.slice(braceIdx + 1, bodyEnd).trim();
1086
+ if (!body) continue;
1087
+ const newBody = body.replace(
1088
+ /([^;:]+):\s*([^;]+)(;|$)/g,
1089
+ (m, prop, val, end) => {
1090
+ if (val.includes("!important")) return m;
1091
+ return prop + ": " + val.trim() + " !important" + end;
1092
+ }
1093
+ );
1094
+ if (selector.includes(":hover")) {
1095
+ parts.push(selector.replace(/:hover/g, "[data-lapse-hover]") + " { " + newBody + " }");
1096
+ }
1097
+ if (selector.includes(":focus-visible")) {
1098
+ parts.push(selector.replace(/:focus-visible/g, "[data-lapse-focus]") + " { " + newBody + " }");
1099
+ } else if (selector.includes(":focus-within")) {
1100
+ parts.push(selector.replace(/:focus-within/g, ":has([data-lapse-focus])") + " { " + newBody + " }");
1101
+ } else if (selector.includes(":focus")) {
1102
+ parts.push(selector.replace(/:focus(?!-)/g, "[data-lapse-focus]") + " { " + newBody + " }");
1103
+ }
1108
1104
  }
1105
+ lapseStyle.textContent = parts.join("\n");
1106
+ document.head.appendChild(lapseStyle);
1107
+ this.lapseStyleEl = lapseStyle;
1108
+ } catch (_) {
1109
1109
  }
1110
- document.head.appendChild(lapseStyle);
1111
- this.lapseStyleEl = lapseStyle;
1112
- } catch (_) {
1113
- }
1110
+ }, 0);
1114
1111
  if (this.frames.length > 0) {
1115
1112
  const frame0 = this.frames[0];
1116
1113
  if (frame0.elementSnapshots) {
@@ -1623,7 +1620,7 @@ function formatExportForLLM(exp, detail = "standard") {
1623
1620
  }
1624
1621
 
1625
1622
  // src/core/engine.ts
1626
- var LapseEngine = class {
1623
+ var SaccadeEngine = class {
1627
1624
  constructor() {
1628
1625
  this.timing = new TimingController();
1629
1626
  this.recorder = new TimelineRecorder();
@@ -1665,7 +1662,25 @@ var LapseEngine = class {
1665
1662
  boundingBox: null
1666
1663
  };
1667
1664
  }
1668
- const capture = this.recorder.stopRecording();
1665
+ let capture;
1666
+ try {
1667
+ capture = this.recorder.stopRecording();
1668
+ } catch (e) {
1669
+ console.error("[Saccade] stopRecording failed:", e);
1670
+ document.getElementById("__lapse-scrub-blocker")?.remove();
1671
+ document.getElementById("__lapse-no-transitions")?.remove();
1672
+ document.getElementById("__lapse-state-rules")?.remove();
1673
+ this._state = "idle";
1674
+ this.notify();
1675
+ return {
1676
+ startTime: 0,
1677
+ endTime: 0,
1678
+ duration: 0,
1679
+ animations: [],
1680
+ frames: [],
1681
+ boundingBox: null
1682
+ };
1683
+ }
1669
1684
  this.capture = capture;
1670
1685
  const scrubberState = {
1671
1686
  elements: this.recorder.elements,
@@ -1726,7 +1741,7 @@ var LapseEngine = class {
1726
1741
  };
1727
1742
  // Annotate the CommonJS export names for ESM import in node:
1728
1743
  0 && (module.exports = {
1729
- LapseEngine,
1744
+ SaccadeEngine,
1730
1745
  TimelineRecorder,
1731
1746
  TimelineScrubber,
1732
1747
  TimingController,