@ytspar/sweetlink 1.20.0 → 1.22.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/dist/browser/consoleCapture.js +1 -1
- package/dist/browser/consoleCapture.js.map +1 -1
- package/dist/browser/earlyConsoleCapture.d.ts +1 -1
- package/dist/browser/earlyConsoleCapture.d.ts.map +1 -1
- package/dist/cli/sweetlink.js +154 -68
- package/dist/cli/sweetlink.js.map +1 -1
- package/dist/daemon/demo.d.ts.map +1 -1
- package/dist/daemon/demo.js +3 -19
- package/dist/daemon/demo.js.map +1 -1
- package/dist/daemon/evidence.js +1 -1
- package/dist/daemon/evidence.js.map +1 -1
- package/dist/daemon/listeners.js +1 -1
- package/dist/daemon/listeners.js.map +1 -1
- package/dist/daemon/recording.d.ts +1 -1
- package/dist/daemon/recording.d.ts.map +1 -1
- package/dist/daemon/recording.js +5 -23
- package/dist/daemon/recording.js.map +1 -1
- package/dist/daemon/refs.js +1 -1
- package/dist/daemon/refs.js.map +1 -1
- package/dist/daemon/ringBuffer.d.ts +8 -0
- package/dist/daemon/ringBuffer.d.ts.map +1 -1
- package/dist/daemon/ringBuffer.js +17 -0
- package/dist/daemon/ringBuffer.js.map +1 -1
- package/dist/daemon/server.d.ts.map +1 -1
- package/dist/daemon/server.js +84 -23
- package/dist/daemon/server.js.map +1 -1
- package/dist/daemon/session.d.ts +8 -1
- package/dist/daemon/session.d.ts.map +1 -1
- package/dist/daemon/stateFile.js +2 -2
- package/dist/daemon/stateFile.js.map +1 -1
- package/dist/daemon/summary.js +2 -2
- package/dist/daemon/summary.js.map +1 -1
- package/dist/daemon/types.d.ts +7 -2
- package/dist/daemon/types.d.ts.map +1 -1
- package/dist/daemon/types.js.map +1 -1
- package/dist/daemon/utils.d.ts +22 -0
- package/dist/daemon/utils.d.ts.map +1 -0
- package/dist/daemon/utils.js +44 -0
- package/dist/daemon/utils.js.map +1 -0
- package/dist/daemon/viewer.d.ts.map +1 -1
- package/dist/daemon/viewer.js +5 -11
- package/dist/daemon/viewer.js.map +1 -1
- package/dist/daemon/visualDiff.d.ts +9 -0
- package/dist/daemon/visualDiff.d.ts.map +1 -1
- package/dist/daemon/visualDiff.js +13 -2
- package/dist/daemon/visualDiff.js.map +1 -1
- package/dist/next.js +3 -3
- package/dist/next.js.map +1 -1
- package/dist/runs.d.ts +15 -3
- package/dist/runs.d.ts.map +1 -1
- package/dist/runs.js +29 -13
- package/dist/runs.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/simulator/android.d.ts +19 -0
- package/dist/simulator/android.d.ts.map +1 -1
- package/dist/simulator/android.js +141 -2
- package/dist/simulator/android.js.map +1 -1
- package/dist/simulator/androidTaps.d.ts +109 -0
- package/dist/simulator/androidTaps.d.ts.map +1 -0
- package/dist/simulator/androidTaps.js +208 -0
- package/dist/simulator/androidTaps.js.map +1 -0
- package/dist/simulator/ios.d.ts +7 -0
- package/dist/simulator/ios.d.ts.map +1 -1
- package/dist/simulator/ios.js +70 -25
- package/dist/simulator/ios.js.map +1 -1
- package/dist/simulator/overlay.d.ts +41 -0
- package/dist/simulator/overlay.d.ts.map +1 -0
- package/dist/simulator/overlay.js +78 -0
- package/dist/simulator/overlay.js.map +1 -0
- package/dist/term/cast.d.ts +24 -0
- package/dist/term/cast.d.ts.map +1 -0
- package/dist/term/cast.js +24 -0
- package/dist/term/cast.js.map +1 -0
- package/dist/term/player.d.ts.map +1 -1
- package/dist/term/player.js +37 -15
- package/dist/term/player.js.map +1 -1
- package/dist/term/recorder.d.ts +7 -0
- package/dist/term/recorder.d.ts.map +1 -1
- package/dist/term/recorder.js +36 -2
- package/dist/term/recorder.js.map +1 -1
- package/dist/types.d.ts +2 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/term/ansi.d.ts +0 -37
- package/dist/term/ansi.d.ts.map +0 -1
- package/dist/term/ansi.js +0 -205
- package/dist/term/ansi.js.map +0 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Android tap-event capture via `adb shell getevent -l`.
|
|
3
|
+
*
|
|
4
|
+
* We stream input events from the connected emulator/device while a screen
|
|
5
|
+
* recording is in progress, parse touch events into (x, y, t) tuples, and
|
|
6
|
+
* write them as a sidecar JSON. A separate post-process step (overlay.ts)
|
|
7
|
+
* renders rings on the recording at each tap timestamp.
|
|
8
|
+
*
|
|
9
|
+
* `getevent -p` is used once at startup to discover the touchscreen input
|
|
10
|
+
* device and its ABS coordinate range — coordinates from `getevent` are in
|
|
11
|
+
* raw input-device units, which we scale to screen pixels using the ratio
|
|
12
|
+
* of `wm size` vs. the input device's max X/Y.
|
|
13
|
+
*/
|
|
14
|
+
import { execFile, spawn } from 'child_process';
|
|
15
|
+
function adbExec(args) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
execFile('adb', args, { encoding: 'utf-8', maxBuffer: 4 * 1024 * 1024 }, (err, stdout) => {
|
|
18
|
+
if (err)
|
|
19
|
+
reject(err);
|
|
20
|
+
else
|
|
21
|
+
resolve(stdout);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Parse `adb shell wm size` output:
|
|
27
|
+
* Physical size: 1080x2400
|
|
28
|
+
* Override size: 720x1600 (optional)
|
|
29
|
+
*
|
|
30
|
+
* Override takes precedence when present (it's what apps actually render at).
|
|
31
|
+
*/
|
|
32
|
+
export function parseWmSize(out) {
|
|
33
|
+
const override = out.match(/Override size:\s*(\d+)x(\d+)/);
|
|
34
|
+
const physical = out.match(/Physical size:\s*(\d+)x(\d+)/);
|
|
35
|
+
const m = override ?? physical;
|
|
36
|
+
if (!m)
|
|
37
|
+
return null;
|
|
38
|
+
return { width: parseInt(m[1], 10), height: parseInt(m[2], 10) };
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parse `adb shell getevent -p` output and find the first device that
|
|
42
|
+
* declares BTN_TOUCH (014a) and ABS_MT_POSITION_X/Y. Returns the device
|
|
43
|
+
* path plus the raw coordinate ranges.
|
|
44
|
+
*
|
|
45
|
+
* Layout (one block per device):
|
|
46
|
+
*
|
|
47
|
+
* add device 3: /dev/input/event3
|
|
48
|
+
* name: "Touchscreen"
|
|
49
|
+
* events:
|
|
50
|
+
* KEY (0001): 014a
|
|
51
|
+
* ABS (0003): 0030 0031 0035 0036 ...
|
|
52
|
+
* input props:
|
|
53
|
+
* ...
|
|
54
|
+
* ABS_MT_POSITION_X : value 0, min 0, max 1080, fuzz 0, flat 0, resolution 0
|
|
55
|
+
* ABS_MT_POSITION_Y : value 0, min 0, max 2400, fuzz 0, flat 0, resolution 0
|
|
56
|
+
*/
|
|
57
|
+
export function parseGetEventProbe(out) {
|
|
58
|
+
const blocks = out.split(/^add device /m).slice(1);
|
|
59
|
+
const devices = [];
|
|
60
|
+
for (const block of blocks) {
|
|
61
|
+
const pathMatch = block.match(/^\d+:\s+(\/dev\/input\/event\d+)/);
|
|
62
|
+
if (!pathMatch)
|
|
63
|
+
continue;
|
|
64
|
+
const path = pathMatch[1];
|
|
65
|
+
// BTN_TOUCH = 014a appears in the KEY (0001) line OR is mentioned by name.
|
|
66
|
+
const hasBtnTouch = /KEY\b[^\n]*\b014a\b/i.test(block) || /BTN_TOUCH/.test(block);
|
|
67
|
+
const xMatch = block.match(/ABS_MT_POSITION_X[^\n]*max\s+(\d+)/);
|
|
68
|
+
const yMatch = block.match(/ABS_MT_POSITION_Y[^\n]*max\s+(\d+)/);
|
|
69
|
+
const maxX = xMatch ? parseInt(xMatch[1], 10) : 0;
|
|
70
|
+
const maxY = yMatch ? parseInt(yMatch[1], 10) : 0;
|
|
71
|
+
devices.push({ path, maxX, maxY, hasBtnTouch });
|
|
72
|
+
}
|
|
73
|
+
return devices;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Run the necessary adb queries to discover the touchscreen device + scale.
|
|
77
|
+
* Throws when no touchscreen device is found.
|
|
78
|
+
*/
|
|
79
|
+
export async function findTouchDevice(deviceSerial) {
|
|
80
|
+
const probe = await adbExec(['-s', deviceSerial, 'shell', 'getevent', '-p']);
|
|
81
|
+
const wm = await adbExec(['-s', deviceSerial, 'shell', 'wm', 'size']);
|
|
82
|
+
const screen = parseWmSize(wm);
|
|
83
|
+
if (!screen) {
|
|
84
|
+
throw new Error(`Could not parse screen size from \`wm size\`: ${wm.slice(0, 100)}`);
|
|
85
|
+
}
|
|
86
|
+
const candidates = parseGetEventProbe(probe).filter((d) => d.hasBtnTouch && d.maxX > 0 && d.maxY > 0);
|
|
87
|
+
if (candidates.length === 0) {
|
|
88
|
+
throw new Error('No touchscreen input device found on the emulator.');
|
|
89
|
+
}
|
|
90
|
+
const dev = candidates[0];
|
|
91
|
+
return {
|
|
92
|
+
path: dev.path,
|
|
93
|
+
maxX: dev.maxX,
|
|
94
|
+
maxY: dev.maxY,
|
|
95
|
+
screenWidth: screen.width,
|
|
96
|
+
screenHeight: screen.height,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
export function freshParseState() {
|
|
100
|
+
return { curX: null, curY: null, downAtMs: null };
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Parse a hex value from `getevent` output. A hostile / unusual adb daemon
|
|
104
|
+
* (manufacturer firmware, network adb at 5555) can emit non-hex tokens or
|
|
105
|
+
* extra-large values; reject those rather than letting NaN/huge numbers
|
|
106
|
+
* propagate into the overlay filter and silently break ffmpeg.
|
|
107
|
+
*/
|
|
108
|
+
function safeHex(value, max) {
|
|
109
|
+
if (!value)
|
|
110
|
+
return null;
|
|
111
|
+
if (!/^[0-9a-fA-F]+$/.test(value))
|
|
112
|
+
return null;
|
|
113
|
+
const n = parseInt(value, 16);
|
|
114
|
+
if (!Number.isFinite(n) || n < 0 || n > max)
|
|
115
|
+
return null;
|
|
116
|
+
return n;
|
|
117
|
+
}
|
|
118
|
+
export function processGetEventChunk(text, state, scaleX, scaleY, startMs, nowMs, bounds) {
|
|
119
|
+
const out = [];
|
|
120
|
+
for (const line of text.split('\n')) {
|
|
121
|
+
const m = line.match(/^\/dev\/input\/\S+:\s+(EV_\w+)\s+(\w+)\s+(\w+)/);
|
|
122
|
+
if (!m)
|
|
123
|
+
continue;
|
|
124
|
+
const [, evType, evCode, valueStr] = m;
|
|
125
|
+
if (evType === 'EV_ABS' && evCode === 'ABS_MT_POSITION_X') {
|
|
126
|
+
const v = safeHex(valueStr, bounds?.maxX ?? Number.MAX_SAFE_INTEGER);
|
|
127
|
+
if (v !== null)
|
|
128
|
+
state.curX = v;
|
|
129
|
+
}
|
|
130
|
+
else if (evType === 'EV_ABS' && evCode === 'ABS_MT_POSITION_Y') {
|
|
131
|
+
const v = safeHex(valueStr, bounds?.maxY ?? Number.MAX_SAFE_INTEGER);
|
|
132
|
+
if (v !== null)
|
|
133
|
+
state.curY = v;
|
|
134
|
+
}
|
|
135
|
+
else if (evType === 'EV_KEY' && evCode === 'BTN_TOUCH') {
|
|
136
|
+
if (valueStr === 'DOWN') {
|
|
137
|
+
state.downAtMs = nowMs;
|
|
138
|
+
}
|
|
139
|
+
else if (valueStr === 'UP') {
|
|
140
|
+
if (state.curX !== null && state.curY !== null && state.downAtMs !== null) {
|
|
141
|
+
const px = Math.round(state.curX * scaleX);
|
|
142
|
+
const py = Math.round(state.curY * scaleY);
|
|
143
|
+
const sw = bounds?.screenWidth;
|
|
144
|
+
const sh = bounds?.screenHeight;
|
|
145
|
+
const x = sw ? Math.max(0, Math.min(sw, px)) : px;
|
|
146
|
+
const y = sh ? Math.max(0, Math.min(sh, py)) : py;
|
|
147
|
+
if (Number.isFinite(x) && Number.isFinite(y)) {
|
|
148
|
+
out.push({ x, y, t: (state.downAtMs - startMs) / 1000 });
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
state.curX = null;
|
|
152
|
+
state.curY = null;
|
|
153
|
+
state.downAtMs = null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Spawn a `getevent -l` listener for the given input path. Returns the
|
|
161
|
+
* child process plus a live array of tap events that the caller can read
|
|
162
|
+
* after they await `done` (or kill the process and await close).
|
|
163
|
+
*
|
|
164
|
+
* `done` resolves once the proc 'close' event fires AND the trailing
|
|
165
|
+
* buffered partial line has been processed — without it, taps in the final
|
|
166
|
+
* milliseconds before SIGTERM are silently dropped.
|
|
167
|
+
*/
|
|
168
|
+
export function captureTapsLive(deviceSerial, info, startMs) {
|
|
169
|
+
const taps = [];
|
|
170
|
+
const state = freshParseState();
|
|
171
|
+
const scaleX = info.screenWidth / info.maxX;
|
|
172
|
+
const scaleY = info.screenHeight / info.maxY;
|
|
173
|
+
const bounds = {
|
|
174
|
+
maxX: info.maxX,
|
|
175
|
+
maxY: info.maxY,
|
|
176
|
+
screenWidth: info.screenWidth,
|
|
177
|
+
screenHeight: info.screenHeight,
|
|
178
|
+
};
|
|
179
|
+
const proc = spawn('adb', ['-s', deviceSerial, 'shell', 'getevent', '-l', info.path], {
|
|
180
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
181
|
+
});
|
|
182
|
+
proc.unref(); // don't keep the parent process alive past its main work
|
|
183
|
+
let buffer = '';
|
|
184
|
+
proc.stdout?.on('data', (chunk) => {
|
|
185
|
+
buffer += chunk.toString();
|
|
186
|
+
// Process complete lines; keep the trailing partial line for next chunk.
|
|
187
|
+
const lastNl = buffer.lastIndexOf('\n');
|
|
188
|
+
if (lastNl === -1)
|
|
189
|
+
return;
|
|
190
|
+
const ready = buffer.slice(0, lastNl);
|
|
191
|
+
buffer = buffer.slice(lastNl + 1);
|
|
192
|
+
const newTaps = processGetEventChunk(ready, state, scaleX, scaleY, startMs, Date.now(), bounds);
|
|
193
|
+
taps.push(...newTaps);
|
|
194
|
+
});
|
|
195
|
+
const done = new Promise((resolve) => {
|
|
196
|
+
proc.on('close', () => {
|
|
197
|
+
// Flush trailing buffer — anything that didn't end with \n.
|
|
198
|
+
if (buffer.length > 0) {
|
|
199
|
+
const newTaps = processGetEventChunk(buffer, state, scaleX, scaleY, startMs, Date.now(), bounds);
|
|
200
|
+
taps.push(...newTaps);
|
|
201
|
+
buffer = '';
|
|
202
|
+
}
|
|
203
|
+
resolve();
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
return { proc, taps, done };
|
|
207
|
+
}
|
|
208
|
+
//# sourceMappingURL=androidTaps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"androidTaps.js","sourceRoot":"","sources":["../../src/simulator/androidTaps.ts"],"names":[],"mappings":"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"}
|
package/dist/simulator/ios.d.ts
CHANGED
|
@@ -18,6 +18,13 @@ export interface IosRecordOptions {
|
|
|
18
18
|
cwd?: string;
|
|
19
19
|
/** Override shell. */
|
|
20
20
|
shell?: string;
|
|
21
|
+
/**
|
|
22
|
+
* When true, the spawned command sees the full process.env. Default false:
|
|
23
|
+
* only a small allowlist (PATH/HOME/USER/LANG/SHELL/etc.) is forwarded so
|
|
24
|
+
* secrets in env (NPM_TOKEN, GH_TOKEN, ANTHROPIC_API_KEY, ...) cannot leak
|
|
25
|
+
* into the recorded .mp4 via stack traces or test logs.
|
|
26
|
+
*/
|
|
27
|
+
inheritEnv?: boolean;
|
|
21
28
|
}
|
|
22
29
|
export interface IosRecordResult {
|
|
23
30
|
output: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ios.d.ts","sourceRoot":"","sources":["../../src/simulator/ios.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ios.d.ts","sourceRoot":"","sources":["../../src/simulator/ios.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,MAAM,WAAW,gBAAgB;IAC/B,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sBAAsB;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAyBD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAoBD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAqBhD;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,CAAC,CAuG5F"}
|
package/dist/simulator/ios.js
CHANGED
|
@@ -10,6 +10,28 @@
|
|
|
10
10
|
import { execFile, spawn } from 'child_process';
|
|
11
11
|
import { promises as fs } from 'fs';
|
|
12
12
|
import * as path from 'path';
|
|
13
|
+
const MINIMAL_ENV_KEYS = [
|
|
14
|
+
'PATH',
|
|
15
|
+
'HOME',
|
|
16
|
+
'USER',
|
|
17
|
+
'LANG',
|
|
18
|
+
'LC_ALL',
|
|
19
|
+
'LC_CTYPE',
|
|
20
|
+
'TERM',
|
|
21
|
+
'SHELL',
|
|
22
|
+
'TZ',
|
|
23
|
+
'TMPDIR',
|
|
24
|
+
'PWD',
|
|
25
|
+
];
|
|
26
|
+
function pickMinimalEnv() {
|
|
27
|
+
const out = {};
|
|
28
|
+
for (const key of MINIMAL_ENV_KEYS) {
|
|
29
|
+
const v = process.env[key];
|
|
30
|
+
if (typeof v === 'string')
|
|
31
|
+
out[key] = v;
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
13
35
|
/** Run `xcrun simctl <args>` and return parsed JSON or text output. */
|
|
14
36
|
function simctl(args, json = false) {
|
|
15
37
|
return new Promise((resolve, reject) => {
|
|
@@ -71,47 +93,70 @@ export async function recordIosSimulator(options) {
|
|
|
71
93
|
if (recProc.exitCode !== null) {
|
|
72
94
|
throw new Error(`recordVideo failed to start on ${device.name}: ${recStderr.trim() || 'unknown error'}`);
|
|
73
95
|
}
|
|
74
|
-
// Run the user's command and capture its exit code.
|
|
96
|
+
// Run the user's command and capture its exit code. Add an `error`
|
|
97
|
+
// handler so a missing shell or bad cwd doesn't crash the parent process
|
|
98
|
+
// before we get a chance to clean up the orphaned recordVideo child.
|
|
75
99
|
const startedAt = Date.now();
|
|
76
100
|
const cmdResult = await new Promise((resolve) => {
|
|
77
101
|
const child = spawn(options.shell ?? '/bin/sh', ['-c', options.command], {
|
|
78
102
|
cwd: options.cwd,
|
|
79
103
|
stdio: 'inherit',
|
|
104
|
+
env: options.inheritEnv
|
|
105
|
+
? process.env
|
|
106
|
+
: pickMinimalEnv(),
|
|
107
|
+
});
|
|
108
|
+
child.on('error', (err) => {
|
|
109
|
+
console.error(`[Sweetlink] Failed to spawn user command: ${err.message}`);
|
|
110
|
+
resolve({ exitCode: 127 });
|
|
80
111
|
});
|
|
81
112
|
child.on('close', (code) => resolve({ exitCode: code ?? 0 }));
|
|
82
113
|
});
|
|
83
114
|
const durationSec = (Date.now() - startedAt) / 1000;
|
|
84
115
|
// Stop recording: SIGINT triggers simctl's flush-and-exit path. SIGTERM
|
|
85
|
-
// would also work but truncates the trailing buffer.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
116
|
+
// would also work but truncates the trailing buffer. Wrapped in
|
|
117
|
+
// try/finally so orphaned recProc gets killed on any later failure.
|
|
118
|
+
let closed = false;
|
|
119
|
+
try {
|
|
120
|
+
closed = await new Promise((resolve) => {
|
|
121
|
+
let resolved = false;
|
|
122
|
+
const timer = setTimeout(() => {
|
|
123
|
+
if (!resolved) {
|
|
124
|
+
resolved = true;
|
|
125
|
+
// Recording didn't shut down within 5s — kill it forcefully.
|
|
126
|
+
try {
|
|
127
|
+
recProc.kill('SIGKILL');
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
/* ignore */
|
|
131
|
+
}
|
|
132
|
+
resolve(false);
|
|
94
133
|
}
|
|
95
|
-
|
|
96
|
-
|
|
134
|
+
}, 5_000);
|
|
135
|
+
recProc.on('close', () => {
|
|
136
|
+
if (!resolved) {
|
|
137
|
+
resolved = true;
|
|
138
|
+
clearTimeout(timer);
|
|
139
|
+
resolve(true);
|
|
97
140
|
}
|
|
98
|
-
|
|
141
|
+
});
|
|
142
|
+
try {
|
|
143
|
+
recProc.kill('SIGINT');
|
|
99
144
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (!resolved) {
|
|
103
|
-
resolved = true;
|
|
104
|
-
clearTimeout(timer);
|
|
105
|
-
resolve(true);
|
|
145
|
+
catch {
|
|
146
|
+
/* ignore */
|
|
106
147
|
}
|
|
107
148
|
});
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
149
|
+
}
|
|
150
|
+
finally {
|
|
151
|
+
if (recProc.exitCode === null) {
|
|
152
|
+
try {
|
|
153
|
+
recProc.kill('SIGKILL');
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
/* ignore */
|
|
157
|
+
}
|
|
113
158
|
}
|
|
114
|
-
}
|
|
159
|
+
}
|
|
115
160
|
return {
|
|
116
161
|
output: options.output,
|
|
117
162
|
device: device.name,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ios.js","sourceRoot":"","sources":["../../src/simulator/ios.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAqB,QAAQ,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ios.js","sourceRoot":"","sources":["../../src/simulator/ios.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tap overlay renderer.
|
|
3
|
+
*
|
|
4
|
+
* Takes an mp4 + a list of tap events and renders red rings at each tap
|
|
5
|
+
* position via ffmpeg's drawbox filter. drawbox doesn't draw circles, so
|
|
6
|
+
* we use a hollow square (the standard "tap target" idiom in iOS' built-
|
|
7
|
+
* in "show touches" mode is a circle, but a 4-px-thick red square reads
|
|
8
|
+
* just as well at video scale and avoids needing a generated PNG asset).
|
|
9
|
+
*/
|
|
10
|
+
export interface OverlayOptions {
|
|
11
|
+
inputPath: string;
|
|
12
|
+
outputPath: string;
|
|
13
|
+
taps: Array<{
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
t: number;
|
|
17
|
+
}>;
|
|
18
|
+
/** How long each tap indicator stays on screen in seconds. Default 0.6. */
|
|
19
|
+
durationSec?: number;
|
|
20
|
+
/** Half-side of the indicator square in px. Default 40. */
|
|
21
|
+
radius?: number;
|
|
22
|
+
/** Optional override for the ffmpeg binary; defaults to "ffmpeg" on PATH. */
|
|
23
|
+
ffmpeg?: string;
|
|
24
|
+
}
|
|
25
|
+
/** Returns true if `ffmpeg` is on PATH. */
|
|
26
|
+
export declare function hasFfmpeg(binary?: string): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Build the ffmpeg `-vf` filter chain for the given tap events.
|
|
29
|
+
* Pure helper exposed for unit testing.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildOverlayFilter(taps: Array<{
|
|
32
|
+
x: number;
|
|
33
|
+
y: number;
|
|
34
|
+
t: number;
|
|
35
|
+
}>, durationSec?: number, radius?: number): string;
|
|
36
|
+
/**
|
|
37
|
+
* Render `inputPath` → `outputPath` with tap overlays composited at each
|
|
38
|
+
* (x, y, t) coordinate. Throws if ffmpeg fails.
|
|
39
|
+
*/
|
|
40
|
+
export declare function applyTapOverlays(options: OverlayOptions): Promise<void>;
|
|
41
|
+
//# sourceMappingURL=overlay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../src/simulator/overlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,2CAA2C;AAC3C,wBAAgB,SAAS,CAAC,MAAM,SAAW,GAAG,OAAO,CAAC,OAAO,CAAC,CAI7D;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,KAAK,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,EAChD,WAAW,SAAM,EACjB,MAAM,SAAK,GACV,MAAM,CAoBR;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC7E"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tap overlay renderer.
|
|
3
|
+
*
|
|
4
|
+
* Takes an mp4 + a list of tap events and renders red rings at each tap
|
|
5
|
+
* position via ffmpeg's drawbox filter. drawbox doesn't draw circles, so
|
|
6
|
+
* we use a hollow square (the standard "tap target" idiom in iOS' built-
|
|
7
|
+
* in "show touches" mode is a circle, but a 4-px-thick red square reads
|
|
8
|
+
* just as well at video scale and avoids needing a generated PNG asset).
|
|
9
|
+
*/
|
|
10
|
+
import { execFile, spawn } from 'child_process';
|
|
11
|
+
import { promises as fs } from 'fs';
|
|
12
|
+
/** Returns true if `ffmpeg` is on PATH. */
|
|
13
|
+
export function hasFfmpeg(binary = 'ffmpeg') {
|
|
14
|
+
return new Promise((resolve) => {
|
|
15
|
+
execFile(binary, ['-version'], (err) => resolve(!err));
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Build the ffmpeg `-vf` filter chain for the given tap events.
|
|
20
|
+
* Pure helper exposed for unit testing.
|
|
21
|
+
*/
|
|
22
|
+
export function buildOverlayFilter(taps, durationSec = 0.6, radius = 40) {
|
|
23
|
+
if (taps.length === 0)
|
|
24
|
+
return '';
|
|
25
|
+
// Two boxes per tap: an outer ring (high-vis) and a smaller solid pulse.
|
|
26
|
+
const segments = [];
|
|
27
|
+
for (const tap of taps) {
|
|
28
|
+
const x = Math.round(tap.x - radius);
|
|
29
|
+
const y = Math.round(tap.y - radius);
|
|
30
|
+
const t0 = tap.t.toFixed(3);
|
|
31
|
+
const t1 = (tap.t + durationSec).toFixed(3);
|
|
32
|
+
// Hollow ring (4-px stroke), bright red, fades by enable window.
|
|
33
|
+
segments.push(`drawbox=x=${x}:y=${y}:w=${radius * 2}:h=${radius * 2}:color=red@0.85:t=4:enable='between(t,${t0},${t1})'`);
|
|
34
|
+
// Inner solid dot (small) so the centre of the tap is unmistakable.
|
|
35
|
+
const innerR = Math.max(4, Math.round(radius / 4));
|
|
36
|
+
segments.push(`drawbox=x=${x + radius - innerR}:y=${y + radius - innerR}:w=${innerR * 2}:h=${innerR * 2}:color=red@0.95:t=fill:enable='between(t,${t0},${t1})'`);
|
|
37
|
+
}
|
|
38
|
+
return segments.join(',');
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Render `inputPath` → `outputPath` with tap overlays composited at each
|
|
42
|
+
* (x, y, t) coordinate. Throws if ffmpeg fails.
|
|
43
|
+
*/
|
|
44
|
+
export async function applyTapOverlays(options) {
|
|
45
|
+
const ffmpeg = options.ffmpeg ?? 'ffmpeg';
|
|
46
|
+
if (options.taps.length === 0) {
|
|
47
|
+
// No taps to draw — just copy the file unchanged.
|
|
48
|
+
await fs.copyFile(options.inputPath, options.outputPath);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const filter = buildOverlayFilter(options.taps, options.durationSec, options.radius);
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
const proc = spawn(ffmpeg, [
|
|
54
|
+
'-i',
|
|
55
|
+
options.inputPath,
|
|
56
|
+
'-vf',
|
|
57
|
+
filter,
|
|
58
|
+
'-codec:v',
|
|
59
|
+
'libx264',
|
|
60
|
+
'-preset',
|
|
61
|
+
'fast',
|
|
62
|
+
'-y',
|
|
63
|
+
options.outputPath,
|
|
64
|
+
], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
65
|
+
let stderr = '';
|
|
66
|
+
proc.stderr?.on('data', (d) => {
|
|
67
|
+
stderr += d.toString();
|
|
68
|
+
});
|
|
69
|
+
proc.on('error', reject);
|
|
70
|
+
proc.on('close', (code) => {
|
|
71
|
+
if (code === 0)
|
|
72
|
+
resolve();
|
|
73
|
+
else
|
|
74
|
+
reject(new Error(`ffmpeg exited ${code}. stderr tail:\n${stderr.slice(-800)}`));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=overlay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../src/simulator/overlay.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the asciicast v2 format.
|
|
3
|
+
* Recorder and player both import these so the on-disk schema stays in sync.
|
|
4
|
+
*/
|
|
5
|
+
export type CastEventKind = 'o' | 'i';
|
|
6
|
+
export type CastEvent = [time: number, kind: CastEventKind, data: string];
|
|
7
|
+
export interface CastHeader {
|
|
8
|
+
version: 2;
|
|
9
|
+
width: number;
|
|
10
|
+
height: number;
|
|
11
|
+
timestamp?: number;
|
|
12
|
+
duration?: number;
|
|
13
|
+
title?: string;
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Escape a JSON string for safe interpolation inside an HTML <script> block.
|
|
18
|
+
* JSON.stringify alone does NOT escape `</script>`, `<!--`, or U+2028/U+2029,
|
|
19
|
+
* so attacker-controlled bytes inside event data could break out of the
|
|
20
|
+
* script element and execute. The .html player is designed for sharing in
|
|
21
|
+
* PR comments, so this matters.
|
|
22
|
+
*/
|
|
23
|
+
export declare function escapeJsonForScript(json: string): string;
|
|
24
|
+
//# sourceMappingURL=cast.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cast.d.ts","sourceRoot":"","sources":["../../src/term/cast.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,MAAM,aAAa,GAAG,GAAG,GAAG,GAAG,CAAC;AAEtC,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAE1E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,CAAC,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAKD;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASxD"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for the asciicast v2 format.
|
|
3
|
+
* Recorder and player both import these so the on-disk schema stays in sync.
|
|
4
|
+
*/
|
|
5
|
+
const LINE_SEP = String.fromCharCode(0x2028);
|
|
6
|
+
const PARA_SEP = String.fromCharCode(0x2029);
|
|
7
|
+
/**
|
|
8
|
+
* Escape a JSON string for safe interpolation inside an HTML <script> block.
|
|
9
|
+
* JSON.stringify alone does NOT escape `</script>`, `<!--`, or U+2028/U+2029,
|
|
10
|
+
* so attacker-controlled bytes inside event data could break out of the
|
|
11
|
+
* script element and execute. The .html player is designed for sharing in
|
|
12
|
+
* PR comments, so this matters.
|
|
13
|
+
*/
|
|
14
|
+
export function escapeJsonForScript(json) {
|
|
15
|
+
return json
|
|
16
|
+
.replace(/</g, '\\u003c')
|
|
17
|
+
.replace(/>/g, '\\u003e')
|
|
18
|
+
.replace(/&/g, '\\u0026')
|
|
19
|
+
.split(LINE_SEP)
|
|
20
|
+
.join('\\u2028')
|
|
21
|
+
.split(PARA_SEP)
|
|
22
|
+
.join('\\u2029');
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=cast.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cast.js","sourceRoot":"","sources":["../../src/term/cast.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgBH,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;AAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,IAAI;SACR,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;SACxB,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;SACxB,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC;SACxB,KAAK,CAAC,QAAQ,CAAC;SACf,IAAI,CAAC,SAAS,CAAC;SACf,KAAK,CAAC,QAAQ,CAAC;SACf,IAAI,CAAC,SAAS,CAAC,CAAC;AACrB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../src/term/player.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;
|
|
1
|
+
{"version":3,"file":"player.d.ts","sourceRoot":"","sources":["../../src/term/player.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAOH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,wBAAsB,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAiQ5E"}
|