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 +6 -6
- package/dist/core.cjs +72 -57
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +4 -4
- package/dist/core.d.ts +4 -4
- package/dist/core.mjs +71 -56
- package/dist/core.mjs.map +1 -1
- package/dist/index.cjs +103 -84
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -11
- package/dist/index.d.ts +11 -11
- package/dist/index.mjs +99 -80
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,13 +13,13 @@ npm install saccade -D
|
|
|
13
13
|
## Usage
|
|
14
14
|
|
|
15
15
|
```tsx
|
|
16
|
-
import {
|
|
16
|
+
import { Saccade } from 'saccade'
|
|
17
17
|
|
|
18
18
|
function App() {
|
|
19
19
|
return (
|
|
20
20
|
<>
|
|
21
21
|
<YourApp />
|
|
22
|
-
<
|
|
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 {
|
|
54
|
+
import { SaccadeEngine } from 'saccade/core'
|
|
55
55
|
|
|
56
|
-
const engine = new
|
|
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 {
|
|
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 `<
|
|
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
|
-
|
|
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
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1744
|
+
SaccadeEngine,
|
|
1730
1745
|
TimelineRecorder,
|
|
1731
1746
|
TimelineScrubber,
|
|
1732
1747
|
TimingController,
|