@yassine-bouassida/scenecap 1.0.0
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/LICENSE +7 -0
- package/README.md +183 -0
- package/dist/index.d.mts +376 -0
- package/dist/index.d.ts +376 -0
- package/dist/index.js +1459 -0
- package/dist/index.mjs +1420 -0
- package/package.json +52 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2026 Yassine Bouassida
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Scenecap
|
|
2
|
+
|
|
3
|
+
Record scripted screen scenarios as MP4/WebM videos directly from your React app — entirely in the browser, zero backend.
|
|
4
|
+
|
|
5
|
+
Describe what should happen in **plain English**, and Scenecap parses it, executes the actions on-screen (virtual cursor, animations, zoom, annotations), and captures everything into a downloadable video file.
|
|
6
|
+
|
|
7
|
+
> Think of it as "Playwright meets screen recorder meets annotation tool" — but running entirely in the browser.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install scenecap
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### React Hook (recommended for Next.js)
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
'use client';
|
|
25
|
+
import { useScenecap } from 'scenecap';
|
|
26
|
+
|
|
27
|
+
export default function MyPage() {
|
|
28
|
+
const { containerRef, record, download, isRecording, progress } = useScenecap();
|
|
29
|
+
|
|
30
|
+
const handleRecord = async () => {
|
|
31
|
+
await record(`
|
|
32
|
+
Click the "Login" button
|
|
33
|
+
Type "user@mail.com" in the email input
|
|
34
|
+
Zoom in on the password field
|
|
35
|
+
Circle the submit button in red
|
|
36
|
+
`);
|
|
37
|
+
download('my-demo.webm');
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<>
|
|
42
|
+
<button onClick={handleRecord} disabled={isRecording}>
|
|
43
|
+
{isRecording ? `${Math.round(progress)}%` : 'Record'}
|
|
44
|
+
</button>
|
|
45
|
+
<div ref={containerRef}>
|
|
46
|
+
{/* your UI */}
|
|
47
|
+
</div>
|
|
48
|
+
</>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Vanilla JS / Imperative
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { createScenecap } from 'scenecap';
|
|
57
|
+
|
|
58
|
+
const sc = createScenecap({ recording: { fps: 30 } });
|
|
59
|
+
const blob = await sc.record(`
|
|
60
|
+
Click the signup button
|
|
61
|
+
Type "hello" in the name field
|
|
62
|
+
`, document.getElementById('app')!);
|
|
63
|
+
|
|
64
|
+
const a = document.createElement('a');
|
|
65
|
+
a.href = URL.createObjectURL(blob);
|
|
66
|
+
a.download = 'recording.webm';
|
|
67
|
+
a.click();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Structured Scenario (skip NLP)
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { ScenarioRunner } from 'scenecap';
|
|
74
|
+
|
|
75
|
+
const runner = new ScenarioRunner();
|
|
76
|
+
const blob = await runner.recordScenario({
|
|
77
|
+
name: 'Login Flow',
|
|
78
|
+
actions: [
|
|
79
|
+
{ type: 'click', target: '#login-btn' },
|
|
80
|
+
{ type: 'type', target: 'input[name="email"]', value: 'test@test.com' },
|
|
81
|
+
{ type: 'zoom', target: '#submit', zoomLevel: 2.0 },
|
|
82
|
+
{ type: 'circle', target: '#submit', color: '#ff0000' },
|
|
83
|
+
],
|
|
84
|
+
}, containerEl);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Supported Actions
|
|
90
|
+
|
|
91
|
+
| What you write | What it does |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `Click the "Submit" button` | Clicks element by text content |
|
|
94
|
+
| `Type "hello" in the email field` | Keystroke-by-keystroke typing |
|
|
95
|
+
| `Zoom in on the navbar to 200%` | Smooth CSS transform zoom |
|
|
96
|
+
| `Zoom out` | Resets zoom to 100% |
|
|
97
|
+
| `Circle the logo in red` | Animated ellipse with glow effect |
|
|
98
|
+
| `Highlight the error message in yellow` | Semi-transparent overlay |
|
|
99
|
+
| `Draw an arrow to the submit button` | Animated arrow pointing to element |
|
|
100
|
+
| `Add text "Click here!" near the button` | Pill-shaped label |
|
|
101
|
+
| `Scroll down 500px` | Smooth scroll by pixel amount |
|
|
102
|
+
| `Scroll to the footer` | Scrolls element into view |
|
|
103
|
+
| `Hover over the menu` | Dispatches mouseenter/mouseover |
|
|
104
|
+
| `Wait 2 seconds` | Pauses execution |
|
|
105
|
+
| `Navigate to /dashboard` | Changes window location |
|
|
106
|
+
| `Take a screenshot` | White flash effect |
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## Configuration
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
createScenecap({
|
|
114
|
+
recording: {
|
|
115
|
+
format: 'webm', // 'webm' | 'mp4' (mp4 depends on browser support)
|
|
116
|
+
fps: 30,
|
|
117
|
+
videoBitrate: 5_000_000,
|
|
118
|
+
width: 1280,
|
|
119
|
+
height: 720,
|
|
120
|
+
audio: false,
|
|
121
|
+
backgroundColor: '#ffffff',
|
|
122
|
+
devicePixelRatio: 1,
|
|
123
|
+
},
|
|
124
|
+
annotations: {
|
|
125
|
+
circleColor: '#ff3b30',
|
|
126
|
+
highlightColor: '#ffcc02',
|
|
127
|
+
arrowColor: '#ff3b30',
|
|
128
|
+
textColor: '#1a1a1a',
|
|
129
|
+
thickness: 3,
|
|
130
|
+
},
|
|
131
|
+
animation: {
|
|
132
|
+
defaultDuration: 800, // ms per action
|
|
133
|
+
defaultEasing: 'ease-in-out',
|
|
134
|
+
zoomDuration: 600,
|
|
135
|
+
},
|
|
136
|
+
callbacks: {
|
|
137
|
+
onActionStart: (action, index) => {},
|
|
138
|
+
onActionComplete: (action, index) => {},
|
|
139
|
+
onProgress: (percent) => {},
|
|
140
|
+
onError: (error) => {},
|
|
141
|
+
onComplete: (blob) => {},
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Browser Requirements
|
|
149
|
+
|
|
150
|
+
| API | Minimum versions |
|
|
151
|
+
|---|---|
|
|
152
|
+
| MediaRecorder | Chrome 49+, Firefox 25+, Edge 79+, Safari 14.1+ |
|
|
153
|
+
| Canvas captureStream | Chrome 51+, Firefox 43+, Edge 79+, Safari 14.1+ |
|
|
154
|
+
|
|
155
|
+
Does **not** work in Node.js / SSR — all recording logic is browser-only.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Known Limitations
|
|
160
|
+
|
|
161
|
+
- **DOM fidelity**: The SVG foreignObject approach doesn't capture cross-origin images, iframes, or some CSS effects (backdrop-filter, complex shadows).
|
|
162
|
+
- **MP4 output**: MediaRecorder natively outputs WebM. True MP4 requires ffmpeg.wasm (not yet integrated).
|
|
163
|
+
- **NLP parser**: Regex-based — handles common phrasings well but won't understand arbitrary sentences.
|
|
164
|
+
- **No iframe support**: Can only record elements within the same document.
|
|
165
|
+
- **Single-tab only**: Cross-page navigations will break the recording session.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Development
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
npm install
|
|
173
|
+
npm run build # compile to dist/ (CJS + ESM + types)
|
|
174
|
+
npm run dev # watch mode
|
|
175
|
+
npm run lint
|
|
176
|
+
npm run test
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT — free for personal and commercial use.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
type ActionType = 'click' | 'type' | 'scroll' | 'hover' | 'wait' | 'zoom' | 'circle' | 'highlight' | 'arrow' | 'annotate' | 'navigate' | 'screenshot';
|
|
2
|
+
interface ScenarioAction {
|
|
3
|
+
/** The type of action to perform */
|
|
4
|
+
type: ActionType;
|
|
5
|
+
/** CSS selector or natural language description of the target element */
|
|
6
|
+
target?: string;
|
|
7
|
+
/** Value for type actions, text for annotations, URL for navigate */
|
|
8
|
+
value?: string;
|
|
9
|
+
/** Duration in milliseconds */
|
|
10
|
+
duration?: number;
|
|
11
|
+
/** Delay before executing this action (ms) */
|
|
12
|
+
delay?: number;
|
|
13
|
+
/** Zoom level for zoom actions (1.0 = 100%) */
|
|
14
|
+
zoomLevel?: number;
|
|
15
|
+
/** Color for annotations/highlights (CSS color string) */
|
|
16
|
+
color?: string;
|
|
17
|
+
/** Thickness for circles/arrows (px) */
|
|
18
|
+
thickness?: number;
|
|
19
|
+
/** Position offset for annotations */
|
|
20
|
+
offset?: {
|
|
21
|
+
x: number;
|
|
22
|
+
y: number;
|
|
23
|
+
};
|
|
24
|
+
/** Easing function for animations */
|
|
25
|
+
easing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
|
|
26
|
+
}
|
|
27
|
+
interface Scenario {
|
|
28
|
+
/** Unique name for this scenario */
|
|
29
|
+
name: string;
|
|
30
|
+
/** Description of what this scenario demonstrates */
|
|
31
|
+
description?: string;
|
|
32
|
+
/** The URL to start recording from */
|
|
33
|
+
startUrl?: string;
|
|
34
|
+
/** Viewport width */
|
|
35
|
+
viewportWidth?: number;
|
|
36
|
+
/** Viewport height */
|
|
37
|
+
viewportHeight?: number;
|
|
38
|
+
/** Ordered list of actions to perform */
|
|
39
|
+
actions: ScenarioAction[];
|
|
40
|
+
}
|
|
41
|
+
interface RecordingOptions {
|
|
42
|
+
/** Output format */
|
|
43
|
+
format: 'webm' | 'mp4';
|
|
44
|
+
/** Frames per second */
|
|
45
|
+
fps: number;
|
|
46
|
+
/** Video bitrate in bits/s */
|
|
47
|
+
videoBitrate: number;
|
|
48
|
+
/** Viewport dimensions */
|
|
49
|
+
width: number;
|
|
50
|
+
height: number;
|
|
51
|
+
/** Whether to record audio */
|
|
52
|
+
audio: boolean;
|
|
53
|
+
/** Background color */
|
|
54
|
+
backgroundColor: string;
|
|
55
|
+
/** Device pixel ratio (for retina) */
|
|
56
|
+
devicePixelRatio: number;
|
|
57
|
+
}
|
|
58
|
+
interface RecordingSession {
|
|
59
|
+
/** Unique session ID */
|
|
60
|
+
id: string;
|
|
61
|
+
/** The scenario being recorded */
|
|
62
|
+
scenario: Scenario;
|
|
63
|
+
/** Recording options */
|
|
64
|
+
options: RecordingOptions;
|
|
65
|
+
/** Current status */
|
|
66
|
+
status: 'idle' | 'preparing' | 'recording' | 'processing' | 'complete' | 'error';
|
|
67
|
+
/** Progress 0-100 */
|
|
68
|
+
progress: number;
|
|
69
|
+
/** Error message if failed */
|
|
70
|
+
error?: string;
|
|
71
|
+
/** The final video blob */
|
|
72
|
+
result?: Blob;
|
|
73
|
+
}
|
|
74
|
+
interface OverlayAnnotation {
|
|
75
|
+
id: string;
|
|
76
|
+
type: 'circle' | 'highlight' | 'arrow' | 'text' | 'zoom-indicator';
|
|
77
|
+
target: DOMRect;
|
|
78
|
+
color: string;
|
|
79
|
+
thickness: number;
|
|
80
|
+
text?: string;
|
|
81
|
+
opacity: number;
|
|
82
|
+
/** Animation progress 0-1 */
|
|
83
|
+
animProgress: number;
|
|
84
|
+
}
|
|
85
|
+
interface ParsedInstruction {
|
|
86
|
+
raw: string;
|
|
87
|
+
action: ScenarioAction;
|
|
88
|
+
confidence: number;
|
|
89
|
+
}
|
|
90
|
+
interface ScenecapCallbacks {
|
|
91
|
+
onActionStart?: (action: ScenarioAction, index: number) => void;
|
|
92
|
+
onActionComplete?: (action: ScenarioAction, index: number) => void;
|
|
93
|
+
onProgress?: (progress: number) => void;
|
|
94
|
+
onError?: (error: Error) => void;
|
|
95
|
+
onComplete?: (blob: Blob) => void;
|
|
96
|
+
}
|
|
97
|
+
interface ScenecapConfig {
|
|
98
|
+
/** Default recording options */
|
|
99
|
+
recording?: Partial<RecordingOptions>;
|
|
100
|
+
/** Default annotation styles */
|
|
101
|
+
annotations?: {
|
|
102
|
+
circleColor?: string;
|
|
103
|
+
highlightColor?: string;
|
|
104
|
+
arrowColor?: string;
|
|
105
|
+
textColor?: string;
|
|
106
|
+
thickness?: number;
|
|
107
|
+
fontFamily?: string;
|
|
108
|
+
fontSize?: number;
|
|
109
|
+
};
|
|
110
|
+
/** Animation defaults */
|
|
111
|
+
animation?: {
|
|
112
|
+
defaultDuration?: number;
|
|
113
|
+
defaultEasing?: 'linear' | 'ease-in' | 'ease-out' | 'ease-in-out';
|
|
114
|
+
zoomDuration?: number;
|
|
115
|
+
};
|
|
116
|
+
/** Callbacks */
|
|
117
|
+
callbacks?: ScenecapCallbacks;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
declare class ScenarioRunner {
|
|
121
|
+
private config;
|
|
122
|
+
private callbacks;
|
|
123
|
+
private session;
|
|
124
|
+
private overlay;
|
|
125
|
+
private recorder;
|
|
126
|
+
private zoom;
|
|
127
|
+
private cursor;
|
|
128
|
+
constructor(config?: Partial<ScenecapConfig>);
|
|
129
|
+
/**
|
|
130
|
+
* Record a scenario described in natural language.
|
|
131
|
+
*
|
|
132
|
+
* @param script - Natural language description of the scenario
|
|
133
|
+
* @param container - The DOM element to record
|
|
134
|
+
* @param options - Additional scenario options
|
|
135
|
+
* @returns The recorded video as a Blob
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* ```ts
|
|
139
|
+
* const blob = await runner.record(`
|
|
140
|
+
* Click on the "Sign Up" button
|
|
141
|
+
* Type "john@email.com" in the email field
|
|
142
|
+
* Zoom in on the submit button
|
|
143
|
+
* Circle the success message in green
|
|
144
|
+
* `, document.getElementById('app')!);
|
|
145
|
+
* ```
|
|
146
|
+
*/
|
|
147
|
+
record(script: string, container: HTMLElement, options?: {
|
|
148
|
+
name?: string;
|
|
149
|
+
description?: string;
|
|
150
|
+
startUrl?: string;
|
|
151
|
+
}): Promise<Blob>;
|
|
152
|
+
/**
|
|
153
|
+
* Record a structured scenario object.
|
|
154
|
+
*/
|
|
155
|
+
recordScenario(scenario: Scenario, container: HTMLElement): Promise<Blob>;
|
|
156
|
+
/**
|
|
157
|
+
* Parse a natural language script without recording (for previewing).
|
|
158
|
+
*/
|
|
159
|
+
parse(script: string): Scenario;
|
|
160
|
+
/**
|
|
161
|
+
* Get current session info.
|
|
162
|
+
*/
|
|
163
|
+
getSession(): RecordingSession | null;
|
|
164
|
+
private executeAction;
|
|
165
|
+
private cleanup;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Records a DOM element to a video file using the Canvas capture API.
|
|
170
|
+
* This captures the visual output of a container element frame-by-frame.
|
|
171
|
+
*/
|
|
172
|
+
declare class VideoRecorder {
|
|
173
|
+
private options;
|
|
174
|
+
private mediaRecorder;
|
|
175
|
+
private chunks;
|
|
176
|
+
private canvas;
|
|
177
|
+
private stream;
|
|
178
|
+
private captureInterval;
|
|
179
|
+
constructor(options?: Partial<RecordingOptions>);
|
|
180
|
+
/**
|
|
181
|
+
* Start recording from a canvas element.
|
|
182
|
+
*/
|
|
183
|
+
startFromCanvas(canvas: HTMLCanvasElement): Promise<void>;
|
|
184
|
+
/**
|
|
185
|
+
* Start recording a DOM element by continuously painting it to a canvas.
|
|
186
|
+
*/
|
|
187
|
+
startFromElement(element: HTMLElement): Promise<void>;
|
|
188
|
+
/**
|
|
189
|
+
* Stop recording and return the video blob.
|
|
190
|
+
*/
|
|
191
|
+
stop(): Promise<Blob>;
|
|
192
|
+
private cleanup;
|
|
193
|
+
get isRecording(): boolean;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Manages viewport zoom transformations during recording.
|
|
198
|
+
* Uses CSS transforms on a container for smooth, GPU-accelerated zooming.
|
|
199
|
+
*/
|
|
200
|
+
declare class ZoomController {
|
|
201
|
+
private container;
|
|
202
|
+
private currentZoom;
|
|
203
|
+
private currentTranslateX;
|
|
204
|
+
private currentTranslateY;
|
|
205
|
+
constructor(container: HTMLElement);
|
|
206
|
+
/**
|
|
207
|
+
* Zoom into a specific element.
|
|
208
|
+
*/
|
|
209
|
+
zoomTo(target: DOMRect, level?: number, duration?: number): Promise<void>;
|
|
210
|
+
/**
|
|
211
|
+
* Reset zoom to default.
|
|
212
|
+
*/
|
|
213
|
+
resetZoom(duration?: number): Promise<void>;
|
|
214
|
+
get zoom(): number;
|
|
215
|
+
destroy(): void;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
declare class AnnotationOverlay {
|
|
219
|
+
private canvas;
|
|
220
|
+
private ctx;
|
|
221
|
+
private annotations;
|
|
222
|
+
private animationFrame;
|
|
223
|
+
constructor(container: HTMLElement);
|
|
224
|
+
private resize;
|
|
225
|
+
addCircle(id: string, target: DOMRect, color?: string, thickness?: number): void;
|
|
226
|
+
addHighlight(id: string, target: DOMRect, color?: string, thickness?: number): void;
|
|
227
|
+
addArrow(id: string, target: DOMRect, color?: string, thickness?: number): void;
|
|
228
|
+
addText(id: string, target: DOMRect, text: string, color?: string): void;
|
|
229
|
+
addZoomIndicator(id: string, target: DOMRect): void;
|
|
230
|
+
remove(id: string): void;
|
|
231
|
+
clear(): void;
|
|
232
|
+
private startAnimation;
|
|
233
|
+
private stopAnimation;
|
|
234
|
+
private update;
|
|
235
|
+
private render;
|
|
236
|
+
private drawCircle;
|
|
237
|
+
private drawHighlight;
|
|
238
|
+
private drawArrow;
|
|
239
|
+
private drawText;
|
|
240
|
+
private drawZoomIndicator;
|
|
241
|
+
destroy(): void;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
declare function parseInstruction(text: string): ParsedInstruction;
|
|
245
|
+
declare function parseNaturalLanguageScenario(script: string, options?: {
|
|
246
|
+
name?: string;
|
|
247
|
+
description?: string;
|
|
248
|
+
startUrl?: string;
|
|
249
|
+
}): Scenario;
|
|
250
|
+
|
|
251
|
+
interface UseScenecapReturn {
|
|
252
|
+
/** Ref to attach to the container element you want to record */
|
|
253
|
+
containerRef: React.RefObject<HTMLElement>;
|
|
254
|
+
/** Record a natural language script */
|
|
255
|
+
record: (script: string, options?: {
|
|
256
|
+
name?: string;
|
|
257
|
+
description?: string;
|
|
258
|
+
}) => Promise<Blob | null>;
|
|
259
|
+
/** Record a structured scenario */
|
|
260
|
+
recordScenario: (scenario: Scenario) => Promise<Blob | null>;
|
|
261
|
+
/** Parse a script into a scenario (for previewing actions) */
|
|
262
|
+
parse: (script: string) => Scenario;
|
|
263
|
+
/** Download the last recording */
|
|
264
|
+
download: (filename?: string) => void;
|
|
265
|
+
/** Whether a recording is in progress */
|
|
266
|
+
isRecording: boolean;
|
|
267
|
+
/** Current progress 0-100 */
|
|
268
|
+
progress: number;
|
|
269
|
+
/** Current session info */
|
|
270
|
+
session: RecordingSession | null;
|
|
271
|
+
/** Last error, if any */
|
|
272
|
+
error: string | null;
|
|
273
|
+
/** The last recorded blob */
|
|
274
|
+
blob: Blob | null;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* React hook for using Scenecap in Next.js / React projects.
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```tsx
|
|
281
|
+
* function DemoRecorder() {
|
|
282
|
+
* const { containerRef, record, isRecording, progress, download } = useScenecap();
|
|
283
|
+
*
|
|
284
|
+
* const handleRecord = async () => {
|
|
285
|
+
* await record(`
|
|
286
|
+
* Click the "Get Started" button
|
|
287
|
+
* Type "hello@example.com" in the email input
|
|
288
|
+
* Zoom in on the submit button
|
|
289
|
+
* Circle the success message in green
|
|
290
|
+
* `);
|
|
291
|
+
* download('demo.webm');
|
|
292
|
+
* };
|
|
293
|
+
*
|
|
294
|
+
* return (
|
|
295
|
+
* <div>
|
|
296
|
+
* <button onClick={handleRecord} disabled={isRecording}>
|
|
297
|
+
* {isRecording ? `Recording... ${progress}%` : 'Record Demo'}
|
|
298
|
+
* </button>
|
|
299
|
+
* <div ref={containerRef}>
|
|
300
|
+
* {/* Your app UI here *\/}
|
|
301
|
+
* </div>
|
|
302
|
+
* </div>
|
|
303
|
+
* );
|
|
304
|
+
* }
|
|
305
|
+
* ```
|
|
306
|
+
*/
|
|
307
|
+
declare function useScenecap(config?: Partial<ScenecapConfig>): UseScenecapReturn;
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Resolves a CSS selector (including custom :has-text()) to a DOM element.
|
|
311
|
+
* Falls back through multiple strategies to find the best match.
|
|
312
|
+
*/
|
|
313
|
+
declare function resolveElement(selector: string, root?: Element): Element | null;
|
|
314
|
+
/**
|
|
315
|
+
* Smoothly scrolls an element into view.
|
|
316
|
+
*/
|
|
317
|
+
declare function scrollIntoView(el: Element, smooth?: boolean): Promise<void>;
|
|
318
|
+
/**
|
|
319
|
+
* Simulates a mouse click with visual feedback.
|
|
320
|
+
*/
|
|
321
|
+
declare function simulateClick(el: Element): void;
|
|
322
|
+
/**
|
|
323
|
+
* Simulates typing text into an input field.
|
|
324
|
+
*/
|
|
325
|
+
declare function simulateType(el: Element, text: string, speed?: number): Promise<void>;
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Virtual cursor that appears in recordings.
|
|
329
|
+
* Renders a smooth macOS-style pointer that animates between targets.
|
|
330
|
+
*/
|
|
331
|
+
declare class VirtualCursor {
|
|
332
|
+
private el;
|
|
333
|
+
private x;
|
|
334
|
+
private y;
|
|
335
|
+
private visible;
|
|
336
|
+
constructor(container: HTMLElement);
|
|
337
|
+
show(): void;
|
|
338
|
+
hide(): void;
|
|
339
|
+
/**
|
|
340
|
+
* Smoothly move cursor to a position.
|
|
341
|
+
*/
|
|
342
|
+
moveTo(targetX: number, targetY: number, duration?: number): Promise<void>;
|
|
343
|
+
/**
|
|
344
|
+
* Move to center of a DOM element.
|
|
345
|
+
*/
|
|
346
|
+
moveToElement(el: Element, duration?: number): Promise<void>;
|
|
347
|
+
/**
|
|
348
|
+
* Click animation at current position.
|
|
349
|
+
*/
|
|
350
|
+
clickAnimation(): Promise<void>;
|
|
351
|
+
destroy(): void;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/**
|
|
355
|
+
* Create a Scenecap instance with the given config.
|
|
356
|
+
*
|
|
357
|
+
* @example
|
|
358
|
+
* ```ts
|
|
359
|
+
* import { createScenecap } from 'scenecap';
|
|
360
|
+
*
|
|
361
|
+
* const sc = createScenecap({
|
|
362
|
+
* recording: { format: 'webm', fps: 30 },
|
|
363
|
+
* annotations: { circleColor: '#ff0000' },
|
|
364
|
+
* });
|
|
365
|
+
*
|
|
366
|
+
* const blob = await sc.record(`
|
|
367
|
+
* Click on the login button
|
|
368
|
+
* Type "admin" in the username field
|
|
369
|
+
* Zoom in on the password field
|
|
370
|
+
* Circle the submit button
|
|
371
|
+
* `, document.getElementById('app')!);
|
|
372
|
+
* ```
|
|
373
|
+
*/
|
|
374
|
+
declare function createScenecap(config?: Partial<ScenecapConfig>): ScenarioRunner;
|
|
375
|
+
|
|
376
|
+
export { type ActionType, AnnotationOverlay, type OverlayAnnotation, type ParsedInstruction, type RecordingOptions, type RecordingSession, type Scenario, type ScenarioAction, ScenarioRunner, type ScenecapCallbacks, type ScenecapConfig, type UseScenecapReturn, VideoRecorder, VirtualCursor, ZoomController, createScenecap, parseInstruction, parseNaturalLanguageScenario, resolveElement, scrollIntoView, simulateClick, simulateType, useScenecap };
|