screenci 0.0.75 → 0.0.78
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/cli.d.ts +62 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +699 -229
- package/dist/cli.js.map +1 -1
- package/dist/docs/manifest.d.ts +277 -91
- package/dist/docs/manifest.d.ts.map +1 -1
- package/dist/docs/manifest.js +101 -30
- package/dist/docs/manifest.js.map +1 -1
- package/dist/docs/video-sources/camera-and-zooming.screenci.d.ts +2 -0
- package/dist/docs/video-sources/camera-and-zooming.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/camera-and-zooming.screenci.js +57 -0
- package/dist/docs/video-sources/camera-and-zooming.screenci.js.map +1 -0
- package/dist/docs/video-sources/installation.screenci.js +19 -10
- package/dist/docs/video-sources/installation.screenci.js.map +1 -1
- package/dist/docs/video-sources/landing.screenci.js +6 -8
- package/dist/docs/video-sources/landing.screenci.js.map +1 -1
- package/dist/docs/video-sources/languages.screenci.d.ts +2 -0
- package/dist/docs/video-sources/languages.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/languages.screenci.js +68 -0
- package/dist/docs/video-sources/languages.screenci.js.map +1 -0
- package/dist/docs/video-sources/locator-highlight-animated.screenci.js +4 -5
- package/dist/docs/video-sources/locator-highlight-animated.screenci.js.map +1 -1
- package/dist/docs/video-sources/locator-highlight-still.screenci.js +3 -4
- package/dist/docs/video-sources/locator-highlight-still.screenci.js.map +1 -1
- package/dist/docs/video-sources/narration.screenci.d.ts +2 -0
- package/dist/docs/video-sources/narration.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/narration.screenci.js +71 -0
- package/dist/docs/video-sources/narration.screenci.js.map +1 -0
- package/dist/docs/video-sources/public-urls-and-embeds.screenci.d.ts +2 -0
- package/dist/docs/video-sources/public-urls-and-embeds.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/public-urls-and-embeds.screenci.js +60 -0
- package/dist/docs/video-sources/public-urls-and-embeds.screenci.js.map +1 -0
- package/dist/docs/video-sources/speaking-style.screenci.d.ts +2 -0
- package/dist/docs/video-sources/speaking-style.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/speaking-style.screenci.js +76 -0
- package/dist/docs/video-sources/speaking-style.screenci.js.map +1 -0
- package/dist/docs/video-sources/studio.screenci.js +8 -10
- package/dist/docs/video-sources/studio.screenci.js.map +1 -1
- package/dist/docs/video-sources/vertical-video.screenci.d.ts +2 -0
- package/dist/docs/video-sources/vertical-video.screenci.d.ts.map +1 -0
- package/dist/docs/video-sources/vertical-video.screenci.js +58 -0
- package/dist/docs/video-sources/vertical-video.screenci.js.map +1 -0
- package/dist/docs/videos.d.ts +20 -0
- package/dist/docs/videos.d.ts.map +1 -1
- package/dist/docs/videos.js +25 -0
- package/dist/docs/videos.js.map +1 -1
- package/dist/e2e/instrument.e2e.js +60 -1
- package/dist/e2e/instrument.e2e.js.map +1 -1
- package/dist/e2e/redact.e2e.d.ts +2 -0
- package/dist/e2e/redact.e2e.d.ts.map +1 -0
- package/dist/e2e/redact.e2e.js +161 -0
- package/dist/e2e/redact.e2e.js.map +1 -0
- package/dist/index.d.ts +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/src/asset.d.ts +174 -14
- package/dist/src/asset.d.ts.map +1 -1
- package/dist/src/asset.js +320 -39
- package/dist/src/asset.js.map +1 -1
- package/dist/src/assetHash.d.ts +27 -0
- package/dist/src/assetHash.d.ts.map +1 -0
- package/dist/src/assetHash.js +0 -0
- package/dist/src/assetHash.js.map +1 -0
- package/dist/src/audio.d.ts +20 -1
- package/dist/src/audio.d.ts.map +1 -1
- package/dist/src/audio.js +126 -24
- package/dist/src/audio.js.map +1 -1
- package/dist/src/autoZoom.d.ts.map +1 -1
- package/dist/src/autoZoom.js +3 -1
- package/dist/src/autoZoom.js.map +1 -1
- package/dist/src/browserLaunchOptions.d.ts +24 -2
- package/dist/src/browserLaunchOptions.d.ts.map +1 -1
- package/dist/src/browserLaunchOptions.js +57 -3
- package/dist/src/browserLaunchOptions.js.map +1 -1
- package/dist/src/builder.d.ts +229 -136
- package/dist/src/builder.d.ts.map +1 -1
- package/dist/src/builder.js +264 -77
- package/dist/src/builder.js.map +1 -1
- package/dist/src/callerFile.d.ts +12 -0
- package/dist/src/callerFile.d.ts.map +1 -0
- package/dist/src/callerFile.js +46 -0
- package/dist/src/callerFile.js.map +1 -0
- package/dist/src/changeFocus.d.ts.map +1 -1
- package/dist/src/changeFocus.js +109 -36
- package/dist/src/changeFocus.js.map +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +15 -0
- package/dist/src/config.js.map +1 -1
- package/dist/src/cue.d.ts +29 -17
- package/dist/src/cue.d.ts.map +1 -1
- package/dist/src/cue.js +138 -61
- package/dist/src/cue.js.map +1 -1
- package/dist/src/declare.d.ts +63 -0
- package/dist/src/declare.d.ts.map +1 -0
- package/dist/src/declare.js +111 -0
- package/dist/src/declare.js.map +1 -0
- package/dist/src/defaults.d.ts +23 -0
- package/dist/src/defaults.d.ts.map +1 -1
- package/dist/src/defaults.js +27 -3
- package/dist/src/defaults.js.map +1 -1
- package/dist/src/events.d.ts +339 -25
- package/dist/src/events.d.ts.map +1 -1
- package/dist/src/events.js +250 -26
- package/dist/src/events.js.map +1 -1
- package/dist/src/htmlRasterizer.d.ts +10 -0
- package/dist/src/htmlRasterizer.d.ts.map +1 -1
- package/dist/src/htmlRasterizer.js +12 -2
- package/dist/src/htmlRasterizer.js.map +1 -1
- package/dist/src/init.d.ts +2 -0
- package/dist/src/init.d.ts.map +1 -1
- package/dist/src/init.js +81 -33
- package/dist/src/init.js.map +1 -1
- package/dist/src/instrument.d.ts.map +1 -1
- package/dist/src/instrument.js +164 -8
- package/dist/src/instrument.js.map +1 -1
- package/dist/src/locales.d.ts.map +1 -1
- package/dist/src/locales.js +1 -0
- package/dist/src/locales.js.map +1 -1
- package/dist/src/localize.d.ts +26 -17
- package/dist/src/localize.d.ts.map +1 -1
- package/dist/src/localize.js +19 -11
- package/dist/src/localize.js.map +1 -1
- package/dist/src/localizeRuntime.d.ts +29 -33
- package/dist/src/localizeRuntime.d.ts.map +1 -1
- package/dist/src/localizeRuntime.js +74 -49
- package/dist/src/localizeRuntime.js.map +1 -1
- package/dist/src/logger.d.ts +5 -0
- package/dist/src/logger.d.ts.map +1 -1
- package/dist/src/logger.js +7 -0
- package/dist/src/logger.js.map +1 -1
- package/dist/src/manualZoom.d.ts.map +1 -1
- package/dist/src/manualZoom.js +8 -5
- package/dist/src/manualZoom.js.map +1 -1
- package/dist/src/narrationVisibility.d.ts +19 -0
- package/dist/src/narrationVisibility.d.ts.map +1 -0
- package/dist/src/narrationVisibility.js +26 -0
- package/dist/src/narrationVisibility.js.map +1 -0
- package/dist/src/openBrowser.d.ts +34 -0
- package/dist/src/openBrowser.d.ts.map +1 -0
- package/dist/src/openBrowser.js +62 -0
- package/dist/src/openBrowser.js.map +1 -0
- package/dist/src/recordingData.d.ts +27 -0
- package/dist/src/recordingData.d.ts.map +1 -1
- package/dist/src/recordingSize.d.ts +32 -0
- package/dist/src/recordingSize.d.ts.map +1 -0
- package/dist/src/recordingSize.js +54 -0
- package/dist/src/recordingSize.js.map +1 -0
- package/dist/src/redact.d.ts +68 -0
- package/dist/src/redact.d.ts.map +1 -0
- package/dist/src/redact.js +138 -0
- package/dist/src/redact.js.map +1 -0
- package/dist/src/redactController.d.ts +61 -0
- package/dist/src/redactController.d.ts.map +1 -0
- package/dist/src/redactController.js +232 -0
- package/dist/src/redactController.js.map +1 -0
- package/dist/src/runtimeContext.d.ts +32 -0
- package/dist/src/runtimeContext.d.ts.map +1 -1
- package/dist/src/runtimeContext.js +21 -0
- package/dist/src/runtimeContext.js.map +1 -1
- package/dist/src/runtimeMode.d.ts +15 -7
- package/dist/src/runtimeMode.d.ts.map +1 -1
- package/dist/src/runtimeMode.js +17 -6
- package/dist/src/runtimeMode.js.map +1 -1
- package/dist/src/screenAudio.d.ts +88 -0
- package/dist/src/screenAudio.d.ts.map +1 -0
- package/dist/src/screenAudio.js +0 -0
- package/dist/src/screenAudio.js.map +1 -0
- package/dist/src/screenAudioSink.d.ts +46 -0
- package/dist/src/screenAudioSink.d.ts.map +1 -0
- package/dist/src/screenAudioSink.js +77 -0
- package/dist/src/screenAudioSink.js.map +1 -0
- package/dist/src/screenshot.d.ts +27 -29
- package/dist/src/screenshot.d.ts.map +1 -1
- package/dist/src/screenshot.js +39 -24
- package/dist/src/screenshot.js.map +1 -1
- package/dist/src/sourceTrim.d.ts +29 -0
- package/dist/src/sourceTrim.d.ts.map +1 -0
- package/dist/src/sourceTrim.js +76 -0
- package/dist/src/sourceTrim.js.map +1 -0
- package/dist/src/stillCapture.d.ts +5 -0
- package/dist/src/stillCapture.d.ts.map +1 -1
- package/dist/src/stillCapture.js +11 -1
- package/dist/src/stillCapture.js.map +1 -1
- package/dist/src/studio.d.ts +69 -57
- package/dist/src/studio.d.ts.map +1 -1
- package/dist/src/studio.js +16 -38
- package/dist/src/studio.js.map +1 -1
- package/dist/src/timelineOffset.d.ts +46 -0
- package/dist/src/timelineOffset.d.ts.map +1 -0
- package/dist/src/timelineOffset.js +66 -0
- package/dist/src/timelineOffset.js.map +1 -0
- package/dist/src/titleValidation.d.ts +38 -0
- package/dist/src/titleValidation.d.ts.map +1 -1
- package/dist/src/titleValidation.js +42 -0
- package/dist/src/titleValidation.js.map +1 -1
- package/dist/src/types.d.ts +218 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/types.js +1 -1
- package/dist/src/types.js.map +1 -1
- package/dist/src/video.d.ts +74 -60
- package/dist/src/video.d.ts.map +1 -1
- package/dist/src/video.js +201 -39
- package/dist/src/video.js.map +1 -1
- package/dist/src/voiceSampleAudio.d.ts +34 -0
- package/dist/src/voiceSampleAudio.d.ts.map +1 -0
- package/dist/src/voiceSampleAudio.js +105 -0
- package/dist/src/voiceSampleAudio.js.map +1 -0
- package/dist/src/voices.d.ts +2 -2
- package/dist/src/voices.d.ts.map +1 -1
- package/dist/src/voices.js +8 -3
- package/dist/src/voices.js.map +1 -1
- package/dist/src/zoom.d.ts +1 -0
- package/dist/src/zoom.d.ts.map +1 -1
- package/dist/src/zoom.js +3 -0
- package/dist/src/zoom.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/skills/screenci/SKILL.md +2 -2
- package/skills/screenci/references/init.md +4 -3
- package/skills/screenci/references/record.md +1 -1
- package/templates/recordings/assets/logo.png +0 -0
- package/dist/docs/video-sources/public-urls.screenci.d.ts +0 -2
- package/dist/docs/video-sources/public-urls.screenci.d.ts.map +0 -1
- package/dist/docs/video-sources/public-urls.screenci.js +0 -41
- package/dist/docs/video-sources/public-urls.screenci.js.map +0 -1
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A dedicated PulseAudio/PipeWire null sink created for one recording worker.
|
|
3
|
+
* The worker's browser is routed into `sinkName` (so its audio is silent on the
|
|
4
|
+
* host and isolated from other apps), and the recorder captures `monitorSource`.
|
|
5
|
+
*/
|
|
6
|
+
export type NullSink = {
|
|
7
|
+
/** `pactl` module id, used to unload the sink on teardown. */
|
|
8
|
+
moduleId: string;
|
|
9
|
+
/** Sink name the browser plays into (via `PULSE_SINK`). */
|
|
10
|
+
sinkName: string;
|
|
11
|
+
/** Monitor source the recorder captures (`<sinkName>.monitor`). */
|
|
12
|
+
monitorSource: string;
|
|
13
|
+
};
|
|
14
|
+
/** @internal - injected in tests */
|
|
15
|
+
export type SinkDeps = {
|
|
16
|
+
run: (cmd: string, args: string[]) => Promise<{
|
|
17
|
+
stdout: string;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Deterministic, per-process sink name. Each Playwright worker is its own
|
|
22
|
+
* process with a unique pid, so this gives one sink per worker that is trivial
|
|
23
|
+
* to clean up and cannot collide with a concurrent worker.
|
|
24
|
+
*/
|
|
25
|
+
export declare function workerSinkName(pid?: number): string;
|
|
26
|
+
/**
|
|
27
|
+
* Creates a null sink via `pactl load-module module-null-sink`. Returns the
|
|
28
|
+
* parsed module id and sink/monitor names, or `null` when `pactl` is missing or
|
|
29
|
+
* the command fails (callers fall back to the default device and warn).
|
|
30
|
+
*/
|
|
31
|
+
export declare function createNullSink(name?: string, deps?: SinkDeps): Promise<NullSink | null>;
|
|
32
|
+
/**
|
|
33
|
+
* Verifies that screen-audio capture can actually run on this machine: the
|
|
34
|
+
* `pactl` control tool is in PATH and a PulseAudio-compatible server is
|
|
35
|
+
* reachable. Throws an actionable error otherwise, so captureAudio fails fast
|
|
36
|
+
* instead of producing a recording that silently lacks the isolated audio it
|
|
37
|
+
* promises.
|
|
38
|
+
*
|
|
39
|
+
* Only `pactl` plus a running pulse server are required. The `pulseaudio` daemon
|
|
40
|
+
* binary itself is NOT needed: PipeWire systems provide the pulse server and
|
|
41
|
+
* `pactl` (via pipewire-pulse) without it, and capture works there.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assertScreenAudioCaptureReady(deps?: SinkDeps): Promise<void>;
|
|
44
|
+
/** Unloads a previously created null sink. Best effort: never throws. */
|
|
45
|
+
export declare function unloadNullSink(sink: NullSink, deps?: SinkDeps): Promise<void>;
|
|
46
|
+
//# sourceMappingURL=screenAudioSink.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenAudioSink.d.ts","sourceRoot":"","sources":["../../src/screenAudioSink.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,8DAA8D;IAC9D,QAAQ,EAAE,MAAM,CAAA;IAChB,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAA;IAChB,mEAAmE;IACnE,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,oCAAoC;AACpC,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CAClE,CAAA;AAMD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,GAAE,MAAoB,GAAG,MAAM,CAEhE;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAClC,IAAI,GAAE,MAAyB,EAC/B,IAAI,GAAE,QAAsB,GAC3B,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAgB1B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,6BAA6B,CACjD,IAAI,GAAE,QAAsB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,yEAAyE;AACzE,wBAAsB,cAAc,CAClC,IAAI,EAAE,QAAQ,EACd,IAAI,GAAE,QAAsB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { execFile as nodeExecFile } from 'child_process';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
import { SCREEN_AUDIO_DOCS_URL } from './screenAudio.js';
|
|
4
|
+
const execFileAsync = promisify(nodeExecFile);
|
|
5
|
+
const defaultDeps = {
|
|
6
|
+
run: (cmd, args) => execFileAsync(cmd, args),
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Deterministic, per-process sink name. Each Playwright worker is its own
|
|
10
|
+
* process with a unique pid, so this gives one sink per worker that is trivial
|
|
11
|
+
* to clean up and cannot collide with a concurrent worker.
|
|
12
|
+
*/
|
|
13
|
+
export function workerSinkName(pid = process.pid) {
|
|
14
|
+
return `screenci_${pid}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates a null sink via `pactl load-module module-null-sink`. Returns the
|
|
18
|
+
* parsed module id and sink/monitor names, or `null` when `pactl` is missing or
|
|
19
|
+
* the command fails (callers fall back to the default device and warn).
|
|
20
|
+
*/
|
|
21
|
+
export async function createNullSink(name = workerSinkName(), deps = defaultDeps) {
|
|
22
|
+
try {
|
|
23
|
+
const { stdout } = await deps.run('pactl', [
|
|
24
|
+
'load-module',
|
|
25
|
+
'module-null-sink',
|
|
26
|
+
`sink_name=${name}`,
|
|
27
|
+
`sink_properties=device.description=${name}`,
|
|
28
|
+
]);
|
|
29
|
+
const moduleId = stdout.trim();
|
|
30
|
+
if (!/^\d+$/.test(moduleId)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
return { moduleId, sinkName: name, monitorSource: `${name}.monitor` };
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Verifies that screen-audio capture can actually run on this machine: the
|
|
41
|
+
* `pactl` control tool is in PATH and a PulseAudio-compatible server is
|
|
42
|
+
* reachable. Throws an actionable error otherwise, so captureAudio fails fast
|
|
43
|
+
* instead of producing a recording that silently lacks the isolated audio it
|
|
44
|
+
* promises.
|
|
45
|
+
*
|
|
46
|
+
* Only `pactl` plus a running pulse server are required. The `pulseaudio` daemon
|
|
47
|
+
* binary itself is NOT needed: PipeWire systems provide the pulse server and
|
|
48
|
+
* `pactl` (via pipewire-pulse) without it, and capture works there.
|
|
49
|
+
*/
|
|
50
|
+
export async function assertScreenAudioCaptureReady(deps = defaultDeps) {
|
|
51
|
+
try {
|
|
52
|
+
await deps.run('pactl', ['--version']);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
throw new Error(`[screenci] captureAudio: "pactl" is not installed or not in PATH. ` +
|
|
56
|
+
`Install "pulseaudio-utils" (or "pipewire-pulse") and try again. ` +
|
|
57
|
+
`See ${SCREEN_AUDIO_DOCS_URL} for setup instructions.`);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
await deps.run('pactl', ['info']);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new Error(`[screenci] captureAudio: no PulseAudio/PipeWire server is reachable ` +
|
|
64
|
+
`(\`pactl info\` failed). Start one (for example "pulseaudio --start") ` +
|
|
65
|
+
`and try again. See ${SCREEN_AUDIO_DOCS_URL} for setup instructions.`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/** Unloads a previously created null sink. Best effort: never throws. */
|
|
69
|
+
export async function unloadNullSink(sink, deps = defaultDeps) {
|
|
70
|
+
try {
|
|
71
|
+
await deps.run('pactl', ['unload-module', sink.moduleId]);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
// Best effort: the server may already be gone (e.g. on shutdown).
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=screenAudioSink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"screenAudioSink.js","sourceRoot":"","sources":["../../src/screenAudioSink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,MAAM,eAAe,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAA;AAExD,MAAM,aAAa,GAAG,SAAS,CAAC,YAAY,CAAC,CAAA;AAqB7C,MAAM,WAAW,GAAa;IAC5B,GAAG,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC;CAC7C,CAAA;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,OAAO,CAAC,GAAG;IACtD,OAAO,YAAY,GAAG,EAAE,CAAA;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,OAAe,cAAc,EAAE,EAC/B,OAAiB,WAAW;IAE5B,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE;YACzC,aAAa;YACb,kBAAkB;YAClB,aAAa,IAAI,EAAE;YACnB,sCAAsC,IAAI,EAAE;SAC7C,CAAC,CAAA;QACF,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAA;QAC9B,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC5B,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,aAAa,EAAE,GAAG,IAAI,UAAU,EAAE,CAAA;IACvE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,6BAA6B,CACjD,OAAiB,WAAW;IAE5B,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,WAAW,CAAC,CAAC,CAAA;IACxC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,oEAAoE;YAClE,kEAAkE;YAClE,OAAO,qBAAqB,0BAA0B,CACzD,CAAA;IACH,CAAC;IACD,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAA;IACnC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,sEAAsE;YACpE,wEAAwE;YACxE,sBAAsB,qBAAqB,0BAA0B,CACxE,CAAA;IACH,CAAC;AACH,CAAC;AAED,yEAAyE;AACzE,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,IAAc,EACd,OAAiB,WAAW;IAE5B,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,eAAe,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,kEAAkE;IACpE,CAAC;AACH,CAAC"}
|
package/dist/src/screenshot.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import type { TestType, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestDetails, TestInfo } from '@playwright/test';
|
|
2
2
|
import type { RecordOptions, RenderOptions, ScreenCIPage } from './types.js';
|
|
3
|
-
import type {
|
|
4
|
-
import { type OverlayController } from './asset.js';
|
|
3
|
+
import type { StudioMarker } from './studio.js';
|
|
4
|
+
import { type OverlayController, type OverlayInputOrFactory } from './asset.js';
|
|
5
5
|
import type { CropTarget, CropOptions } from './crop.js';
|
|
6
|
-
import type
|
|
7
|
-
import type {
|
|
8
|
-
import { type
|
|
6
|
+
import { type MediaBuilder } from './builder.js';
|
|
7
|
+
import type { NormalizedFeature } from './declare.js';
|
|
8
|
+
import { type Values } from './localizeRuntime.js';
|
|
9
9
|
/**
|
|
10
10
|
* The `crop` fixture argument. Call it inside a `screenshot()` body to crop the
|
|
11
11
|
* implicit end-of-body capture to a locator or pixel region. Replaces the
|
|
@@ -13,21 +13,16 @@ import { type TextValues } from './localizeRuntime.js';
|
|
|
13
13
|
*/
|
|
14
14
|
export type CropFixture = (target: CropTarget, options?: CropOptions) => Promise<void>;
|
|
15
15
|
type ScreenshotFixtureOptions = {
|
|
16
|
-
recordOptions: RecordOptions
|
|
17
|
-
renderOptions: RenderOptions | undefined;
|
|
16
|
+
recordOptions: RecordOptions | StudioMarker<Partial<RecordOptions>>;
|
|
17
|
+
renderOptions: RenderOptions | StudioMarker<Partial<RenderOptions>> | undefined;
|
|
18
18
|
/** Active language for this pass; see {@link video} for details. Internal. */
|
|
19
19
|
_screenciLanguage: string | undefined;
|
|
20
20
|
/** Grouping name written to `metadata.videoName`. Internal. */
|
|
21
21
|
_screenciVideoName: string | undefined;
|
|
22
|
-
/**
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
* render/record deferral flags and the Studio-managed `text` and `overlays`
|
|
27
|
-
* fixtures. A still is silent, so narration/audio declarations are ignored.
|
|
28
|
-
* Internal.
|
|
29
|
-
*/
|
|
30
|
-
_screenciStudio: StudioDeclaration | undefined;
|
|
22
|
+
/** Values-field declaration (`screenshot.values(...)`). Internal. */
|
|
23
|
+
_screenciValues: NormalizedFeature<string> | undefined;
|
|
24
|
+
/** Overlay declaration (`screenshot.overlays(...)`). Internal. */
|
|
25
|
+
_screenciOverlays: NormalizedFeature<OverlayInputOrFactory> | undefined;
|
|
31
26
|
/**
|
|
32
27
|
* Absolute path of the `.screenci` script that registered this test, captured
|
|
33
28
|
* by the fan-out builder. Asset paths are resolved relative to it, since
|
|
@@ -40,16 +35,16 @@ type ScreenshotFixtures = {
|
|
|
40
35
|
/** The language being captured in this pass; `undefined` outside per-language mode. */
|
|
41
36
|
language: string | undefined;
|
|
42
37
|
/**
|
|
43
|
-
* Injected
|
|
44
|
-
* declared in `screenshot.
|
|
38
|
+
* Injected values fields for the active language, keyed by the field names
|
|
39
|
+
* declared in `screenshot.values(...)`. A still is silent, so there is no
|
|
45
40
|
* `narration` fixture.
|
|
46
41
|
*/
|
|
47
|
-
|
|
42
|
+
values: Values;
|
|
48
43
|
/**
|
|
49
|
-
* Overlay controllers for the
|
|
50
|
-
*
|
|
44
|
+
* Overlay controllers for the names declared in `screenshot.overlays(...)`.
|
|
45
|
+
* Empty when none are declared.
|
|
51
46
|
*/
|
|
52
|
-
overlays: Record<string, OverlayController>;
|
|
47
|
+
overlays: Record<string, OverlayController | ((props: unknown) => OverlayController)>;
|
|
53
48
|
};
|
|
54
49
|
type ScreenshotType = TestType<PlaywrightTestArgs & PlaywrightTestOptions & ScreenshotFixtureOptions & ScreenshotFixtures & PlaywrightWorkerArgs & PlaywrightWorkerOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
|
|
55
50
|
type ScreenshotArgs = Omit<PlaywrightTestArgs, 'page'> & {
|
|
@@ -99,21 +94,24 @@ interface Screenshot extends ScreenshotCallSignatures {
|
|
|
99
94
|
/**
|
|
100
95
|
* Capture one localized screenshot per declared language. By default each
|
|
101
96
|
* language is captured in its own pass with the browser `locale` set from the
|
|
102
|
-
* language, and the body receives the active `language` and `
|
|
97
|
+
* language, and the body receives the active `language` and `values` fields.
|
|
103
98
|
* Chainable with `.each(...)`.
|
|
104
99
|
*/
|
|
105
|
-
|
|
100
|
+
/** Declare on-screen values fields (array = Studio-owned, object = code values). */
|
|
101
|
+
values: MediaBuilder<ScreenshotArgs>['values'];
|
|
102
|
+
/** Declare overlays (`studio([...])` = Studio-owned, object = code values/factories). */
|
|
103
|
+
overlays: MediaBuilder<ScreenshotArgs>['overlays'];
|
|
106
104
|
/**
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
105
|
+
* Declare the recorded language set / capture mode. Pass `studio()` to let the
|
|
106
|
+
* web app own the set (`studio(['en', 'fi'])` to seed it), an array
|
|
107
|
+
* `['en', 'fi']`, or an options object.
|
|
110
108
|
*/
|
|
111
|
-
|
|
109
|
+
languages: MediaBuilder<ScreenshotArgs>['languages'];
|
|
112
110
|
/**
|
|
113
111
|
* Produce a separate screenshot per variant (viewport, theme, ...). Each
|
|
114
112
|
* variant has its own identity and history. Chainable with `.languages(...)`.
|
|
115
113
|
*/
|
|
116
|
-
each:
|
|
114
|
+
each: MediaBuilder<ScreenshotArgs>['each'];
|
|
117
115
|
beforeEach(inner: (args: ScreenshotArgs, testInfo: TestInfo) => Promise<void> | void): void;
|
|
118
116
|
beforeEach(title: string, inner: (args: ScreenshotArgs, testInfo: TestInfo) => Promise<void> | void): void;
|
|
119
117
|
afterEach(inner: (args: ScreenshotArgs, testInfo: TestInfo) => Promise<void> | void): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/screenshot.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAClB,qBAAqB,EACrB,oBAAoB,EACpB,uBAAuB,EACvB,WAAW,EACX,QAAQ,EACT,MAAM,kBAAkB,CAAA;AAGzB,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"screenshot.d.ts","sourceRoot":"","sources":["../../src/screenshot.ts"],"names":[],"mappings":"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"}
|
package/dist/src/screenshot.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { test as base } from '@playwright/test';
|
|
2
2
|
import { mkdir, rm } from 'fs/promises';
|
|
3
3
|
import { join, relative } from 'path';
|
|
4
|
-
import {
|
|
5
|
-
import { buildStudioOverlays } from './asset.js';
|
|
4
|
+
import { buildOverlays, } from './asset.js';
|
|
6
5
|
import { getDimensions } from './dimensions.js';
|
|
7
6
|
import { DEFAULT_VIDEO_OPTIONS, DEFAULT_ASPECT_RATIO, DEFAULT_QUALITY, DEFAULT_SCREENSHOT_DEVICE_SCALE_FACTOR, } from './defaults.js';
|
|
8
7
|
import { EventRecorder } from './events.js';
|
|
@@ -12,12 +11,13 @@ import { createScreenCIRuntimeContext } from './runtimeContext.js';
|
|
|
12
11
|
import { escapeFileSystemPathSegment } from './fileSystemName.js';
|
|
13
12
|
import { buildScreenCIContextOptions, resolveDeviceScaleFactor, } from './contextOptions.js';
|
|
14
13
|
import { resolveCrop } from './crop.js';
|
|
14
|
+
import { getMousePosition } from './mouse.js';
|
|
15
15
|
import { getRuntimePage, setRuntimeCrop } from './runtimeContext.js';
|
|
16
16
|
import { ScreenciError } from './errors.js';
|
|
17
|
-
import { withActiveRecordingContext, resolveEffectiveRecordOptions, } from './video.js';
|
|
18
|
-
import { createVideoBuilder } from './builder.js';
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
17
|
+
import { withActiveRecordingContext, resolveEffectiveRecordOptions, resolveStudioRecordOptions, resolveStudioRenderOptions, } from './video.js';
|
|
18
|
+
import { createVideoBuilder, SCREENSHOT_FEATURES, } from './builder.js';
|
|
19
|
+
import { buildValuesDeclaration, buildValues, } from './localizeRuntime.js';
|
|
20
|
+
import { parseValuesOverrides } from './runtimeMode.js';
|
|
21
21
|
/** File name of the raw page capture written beside `data.json`. */
|
|
22
22
|
const SCREENSHOT_FILE_NAME = 'screenshot.png';
|
|
23
23
|
const _screenshotBase = base.extend({
|
|
@@ -25,17 +25,17 @@ const _screenshotBase = base.extend({
|
|
|
25
25
|
renderOptions: [undefined, { option: true }],
|
|
26
26
|
_screenciLanguage: [undefined, { option: true }],
|
|
27
27
|
_screenciVideoName: [undefined, { option: true }],
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
_screenciValues: [undefined, { option: true }],
|
|
29
|
+
_screenciOverlays: [undefined, { option: true }],
|
|
30
30
|
_screenciSourceFile: [undefined, { option: true }],
|
|
31
31
|
language: async ({ _screenciLanguage }, use) => {
|
|
32
32
|
await use(_screenciLanguage);
|
|
33
33
|
},
|
|
34
|
-
|
|
35
|
-
await use(
|
|
34
|
+
values: async ({ _screenciValues, _screenciLanguage }, use) => {
|
|
35
|
+
await use(buildValues(_screenciValues, _screenciLanguage, parseValuesOverrides()));
|
|
36
36
|
},
|
|
37
|
-
overlays: async ({
|
|
38
|
-
await use(
|
|
37
|
+
overlays: async ({ _screenciOverlays, _screenciLanguage }, use) => {
|
|
38
|
+
await use(buildOverlays(_screenciOverlays, _screenciLanguage));
|
|
39
39
|
},
|
|
40
40
|
crop: async ({}, use) => {
|
|
41
41
|
await use(async (target, options) => {
|
|
@@ -56,8 +56,9 @@ const _screenshotBase = base.extend({
|
|
|
56
56
|
await browser.close();
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
|
-
context: async ({ browser, recordOptions,
|
|
60
|
-
const
|
|
59
|
+
context: async ({ browser, recordOptions, _screenciVideoName, colorScheme, locale, timezoneId, userAgent, geolocation, permissions, extraHTTPHeaders, httpCredentials, ignoreHTTPSErrors, offline, storageState, baseURL, bypassCSP, acceptDownloads, javaScriptEnabled, hasTouch, isMobile, deviceScaleFactor, }, use, testInfo) => {
|
|
60
|
+
const { base: baseRecordOptions, studio: studioRecord } = resolveStudioRecordOptions(recordOptions);
|
|
61
|
+
const effectiveRecordOptions = resolveEffectiveRecordOptions(baseRecordOptions, studioRecord, _screenciVideoName ?? testInfo.title);
|
|
61
62
|
const aspectRatio = effectiveRecordOptions.aspectRatio ?? DEFAULT_ASPECT_RATIO;
|
|
62
63
|
const quality = effectiveRecordOptions.quality ?? DEFAULT_QUALITY;
|
|
63
64
|
const dimensions = getDimensions(aspectRatio, quality);
|
|
@@ -95,14 +96,19 @@ const _screenshotBase = base.extend({
|
|
|
95
96
|
await context.close();
|
|
96
97
|
}
|
|
97
98
|
},
|
|
98
|
-
page: async ({ context, recordOptions: codeRecordOptions, renderOptions, deviceScaleFactor, _screenciLanguage,
|
|
99
|
+
page: async ({ context, recordOptions: codeRecordOptions, renderOptions, deviceScaleFactor, _screenciLanguage, _screenciValues, _screenciVideoName, _screenciSourceFile, }, use, testInfo) => {
|
|
99
100
|
const shouldRecord = process.env.SCREENCI_RECORDING === 'true';
|
|
100
|
-
const
|
|
101
|
-
const
|
|
101
|
+
const { base: baseRecordOptions, studio: studioRecord } = resolveStudioRecordOptions(codeRecordOptions);
|
|
102
|
+
const { obj: renderOptionsObj, studio: studioRender } = resolveStudioRenderOptions(renderOptions);
|
|
103
|
+
const recordOptions = resolveEffectiveRecordOptions(baseRecordOptions, studioRecord, _screenciVideoName ?? testInfo.title);
|
|
104
|
+
const recorder = new EventRecorder(renderOptionsObj, recordOptions, {
|
|
105
|
+
renderOptions: studioRender,
|
|
106
|
+
recordOptions: studioRecord,
|
|
107
|
+
});
|
|
102
108
|
recorder.setActiveLanguage(_screenciLanguage ?? null);
|
|
103
|
-
// Declared `
|
|
109
|
+
// Declared `values` fields (and the active language's seeds) emitted once at
|
|
104
110
|
// recording start so the backend/Studio learn them.
|
|
105
|
-
const
|
|
111
|
+
const valuesDeclaration = buildValuesDeclaration(_screenciValues, _screenciLanguage);
|
|
106
112
|
const videoName = _screenciVideoName ?? testInfo.title;
|
|
107
113
|
// Asset paths are authored relative to the user's script. Playwright reports
|
|
108
114
|
// `testInfo.file` as the builder module that registered the test, so prefer
|
|
@@ -123,7 +129,7 @@ const _screenshotBase = base.extend({
|
|
|
123
129
|
page,
|
|
124
130
|
recorder,
|
|
125
131
|
unendedOverlays: 'autoEnd',
|
|
126
|
-
|
|
132
|
+
valuesDeclaration,
|
|
127
133
|
fn: async () => {
|
|
128
134
|
await use(page);
|
|
129
135
|
},
|
|
@@ -160,7 +166,7 @@ const _screenshotBase = base.extend({
|
|
|
160
166
|
page,
|
|
161
167
|
recorder,
|
|
162
168
|
unendedOverlays: 'autoEnd',
|
|
163
|
-
|
|
169
|
+
valuesDeclaration,
|
|
164
170
|
fn: async () => {
|
|
165
171
|
await use(page);
|
|
166
172
|
},
|
|
@@ -172,11 +178,19 @@ const _screenshotBase = base.extend({
|
|
|
172
178
|
path: join(screenshotDir, SCREENSHOT_FILE_NAME),
|
|
173
179
|
});
|
|
174
180
|
const crop = runtimeContext.crop ?? undefined;
|
|
181
|
+
// The cursor lands at its final position even for a still (the move is
|
|
182
|
+
// instant). Record it so the renderer can draw the cursor on the still
|
|
183
|
+
// when `renderOptions.screenshot.mouse.show` is set. Absent when the body
|
|
184
|
+
// never moved the cursor, so no cursor is ever shown for such a still.
|
|
185
|
+
const mousePosition = getMousePosition(page);
|
|
175
186
|
const screenshot = {
|
|
176
187
|
path: SCREENSHOT_FILE_NAME,
|
|
177
188
|
width: Math.round(dimensions.width * dsf),
|
|
178
189
|
height: Math.round(dimensions.height * dsf),
|
|
179
190
|
deviceScaleFactor: dsf,
|
|
191
|
+
...(mousePosition !== undefined && {
|
|
192
|
+
mousePosition: { x: mousePosition.x, y: mousePosition.y },
|
|
193
|
+
}),
|
|
180
194
|
};
|
|
181
195
|
const configDir = process.env.SCREENCI_CONFIG_DIR ?? process.cwd();
|
|
182
196
|
// The crop (from the `crop` fixture) is a render option, so it goes into
|
|
@@ -220,8 +234,9 @@ const _screenshotBase = base.extend({
|
|
|
220
234
|
*/
|
|
221
235
|
export const screenshot = _screenshotBase;
|
|
222
236
|
// Attach the chainable fan-out builders, mirroring `video`.
|
|
223
|
-
const _screenshotRootBuilder = createVideoBuilder(_screenshotBase);
|
|
224
|
-
screenshot.
|
|
225
|
-
screenshot.
|
|
237
|
+
const _screenshotRootBuilder = createVideoBuilder(_screenshotBase, SCREENSHOT_FEATURES);
|
|
238
|
+
screenshot.values = _screenshotRootBuilder.values;
|
|
239
|
+
screenshot.overlays = _screenshotRootBuilder.overlays;
|
|
240
|
+
screenshot.languages = _screenshotRootBuilder.languages;
|
|
226
241
|
screenshot.each = _screenshotRootBuilder.each;
|
|
227
242
|
//# sourceMappingURL=screenshot.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,IAAI,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAU/C,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,MAAM,CAAA;AAGrC,OAAO,
|
|
1
|
+
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/screenshot.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared validation and parsing for the source `crop` rectangle and the
|
|
3
|
+
* `start`/`end` source-trim positions accepted by file overlays, narration video
|
|
4
|
+
* cues, and `selected()` render dependencies.
|
|
5
|
+
*
|
|
6
|
+
* `crop` is a rectangle in the source file's own pixels. `start`/`end` are time
|
|
7
|
+
* strings (`'2s'`, `'0:02'`, `'50%'`): seconds/timecodes resolve to a concrete ms
|
|
8
|
+
* offset into the source, while a percentage stays symbolic (a fraction of the
|
|
9
|
+
* SOURCE duration) because the source length is not known until render time.
|
|
10
|
+
*/
|
|
11
|
+
import { type TimelineOffset } from './timelineOffset.js';
|
|
12
|
+
import type { OverlayCrop, SourceTrimPoint } from './events.js';
|
|
13
|
+
/**
|
|
14
|
+
* Validates a {@link OverlayCrop}: every field finite, `x`/`y` non-negative, and
|
|
15
|
+
* `width`/`height` strictly positive. Returns the crop unchanged so callers can
|
|
16
|
+
* thread it inline.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validateCrop(label: string, crop: OverlayCrop): OverlayCrop;
|
|
19
|
+
/**
|
|
20
|
+
* Parses and validates the optional `start`/`end` source-trim pair. When both are
|
|
21
|
+
* concrete (ms or percent), `start` must come before `end`; a mixed ms/percent
|
|
22
|
+
* pair cannot be compared here (the source length is unknown) and is deferred to
|
|
23
|
+
* the renderer. Returns the parsed points to thread onto the recorded event.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveSourceTrim(label: string, start: TimelineOffset | undefined, end: TimelineOffset | undefined): {
|
|
26
|
+
sourceStart?: SourceTrimPoint;
|
|
27
|
+
sourceEnd?: SourceTrimPoint;
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=sourceTrim.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sourceTrim.d.ts","sourceRoot":"","sources":["../../src/sourceTrim.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAuB,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAE/D;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,WAAW,CAgB1E;AA2BD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GAAG,SAAS,EACjC,GAAG,EAAE,cAAc,GAAG,SAAS,GAC9B;IAAE,WAAW,CAAC,EAAE,eAAe,CAAC;IAAC,SAAS,CAAC,EAAE,eAAe,CAAA;CAAE,CAiChE"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared validation and parsing for the source `crop` rectangle and the
|
|
3
|
+
* `start`/`end` source-trim positions accepted by file overlays, narration video
|
|
4
|
+
* cues, and `selected()` render dependencies.
|
|
5
|
+
*
|
|
6
|
+
* `crop` is a rectangle in the source file's own pixels. `start`/`end` are time
|
|
7
|
+
* strings (`'2s'`, `'0:02'`, `'50%'`): seconds/timecodes resolve to a concrete ms
|
|
8
|
+
* offset into the source, while a percentage stays symbolic (a fraction of the
|
|
9
|
+
* SOURCE duration) because the source length is not known until render time.
|
|
10
|
+
*/
|
|
11
|
+
import { parseTimelineOffset } from './timelineOffset.js';
|
|
12
|
+
/**
|
|
13
|
+
* Validates a {@link OverlayCrop}: every field finite, `x`/`y` non-negative, and
|
|
14
|
+
* `width`/`height` strictly positive. Returns the crop unchanged so callers can
|
|
15
|
+
* thread it inline.
|
|
16
|
+
*/
|
|
17
|
+
export function validateCrop(label, crop) {
|
|
18
|
+
const { x, y, width, height } = crop;
|
|
19
|
+
const finite = (v) => typeof v === 'number' && Number.isFinite(v);
|
|
20
|
+
if (!finite(x) || !finite(y) || !finite(width) || !finite(height)) {
|
|
21
|
+
throw new Error(`[screenci] ${label} crop must have finite numeric x, y, width, and height (source pixels). Received: ${JSON.stringify(crop)}`);
|
|
22
|
+
}
|
|
23
|
+
if (x < 0 || y < 0) {
|
|
24
|
+
throw new Error(`[screenci] ${label} crop x and y must be >= 0.`);
|
|
25
|
+
}
|
|
26
|
+
if (width <= 0 || height <= 0) {
|
|
27
|
+
throw new Error(`[screenci] ${label} crop width and height must be > 0.`);
|
|
28
|
+
}
|
|
29
|
+
return crop;
|
|
30
|
+
}
|
|
31
|
+
/** Parses a single `start`/`end` time string into a {@link SourceTrimPoint}. */
|
|
32
|
+
function parseSourceTrimPoint(value, label) {
|
|
33
|
+
if (typeof value !== 'string') {
|
|
34
|
+
throw new Error(`[screenci] ${label} must be a time string such as '2s', '0:02', or '50%', got ${typeof value}.`);
|
|
35
|
+
}
|
|
36
|
+
const parsed = parseTimelineOffset(value);
|
|
37
|
+
if (parsed.kind === 'percent') {
|
|
38
|
+
if (parsed.fraction <= 0 || parsed.fraction > 1) {
|
|
39
|
+
throw new Error(`[screenci] ${label} percentage must be greater than 0% and at most 100%. Received: '${value}'.`);
|
|
40
|
+
}
|
|
41
|
+
return { percent: parsed.fraction };
|
|
42
|
+
}
|
|
43
|
+
if (parsed.ms < 0) {
|
|
44
|
+
throw new Error(`[screenci] ${label} must be >= 0. Received: '${value}'.`);
|
|
45
|
+
}
|
|
46
|
+
return { ms: parsed.ms };
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Parses and validates the optional `start`/`end` source-trim pair. When both are
|
|
50
|
+
* concrete (ms or percent), `start` must come before `end`; a mixed ms/percent
|
|
51
|
+
* pair cannot be compared here (the source length is unknown) and is deferred to
|
|
52
|
+
* the renderer. Returns the parsed points to thread onto the recorded event.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveSourceTrim(label, start, end) {
|
|
55
|
+
const sourceStart = start !== undefined
|
|
56
|
+
? parseSourceTrimPoint(start, `${label} start`)
|
|
57
|
+
: undefined;
|
|
58
|
+
const sourceEnd = end !== undefined ? parseSourceTrimPoint(end, `${label} end`) : undefined;
|
|
59
|
+
if (sourceStart !== undefined && sourceEnd !== undefined) {
|
|
60
|
+
if ('ms' in sourceStart &&
|
|
61
|
+
'ms' in sourceEnd &&
|
|
62
|
+
sourceStart.ms >= sourceEnd.ms) {
|
|
63
|
+
throw new Error(`[screenci] ${label} start must be before end (got start ${start}, end ${end}).`);
|
|
64
|
+
}
|
|
65
|
+
if ('percent' in sourceStart &&
|
|
66
|
+
'percent' in sourceEnd &&
|
|
67
|
+
sourceStart.percent >= sourceEnd.percent) {
|
|
68
|
+
throw new Error(`[screenci] ${label} start must be before end (got start ${start}, end ${end}).`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
...(sourceStart !== undefined && { sourceStart }),
|
|
73
|
+
...(sourceEnd !== undefined && { sourceEnd }),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=sourceTrim.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sourceTrim.js","sourceRoot":"","sources":["../../src/sourceTrim.ts"],"names":[],"mappings":"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"}
|
|
@@ -47,6 +47,11 @@ export declare function writeStillRecording(params: {
|
|
|
47
47
|
};
|
|
48
48
|
deviceScaleFactor: number;
|
|
49
49
|
crop?: ScreenshotCropRecord;
|
|
50
|
+
/** Final cursor position in CSS px of the viewport, if the body moved it. */
|
|
51
|
+
mousePosition?: {
|
|
52
|
+
x: number;
|
|
53
|
+
y: number;
|
|
54
|
+
};
|
|
50
55
|
testFilePath: string | null;
|
|
51
56
|
configDir: string;
|
|
52
57
|
recordOptions: RecordOptions | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stillCapture.d.ts","sourceRoot":"","sources":["../../src/stillCapture.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAI5C,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,aAAa,CAAA;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"stillCapture.d.ts","sourceRoot":"","sources":["../../src/stillCapture.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AAI5C,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,aAAa,CAAA;AAEjE,OAAO,KAAK,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAQjE,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAK9D;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,+EAA+E;IAC/E,IAAI,CAAC,EAAE,UAAU,CAAA;CAClB,CAAA;AAQD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,MAAM,GAAG,SAAS,EACxB,IAAI,EAAE,GAAG,CAAC,MAAM,CAAC,GAChB,MAAM,CAqBR;AAED,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,gEAAgE;IAChE,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IAC7C,EAAE,CAAC,EAAE;QACH,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;QACnC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KACvC,CAAA;IACD,YAAY,CAAC,EAAE,CACb,aAAa,EAAE,aAAa,GAAG,SAAS,EACxC,aAAa,EAAE,aAAa,GAAG,SAAS,KACrC,cAAc,CAAA;CACpB,CAAA;AAED;;;;;GAKG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC7C,iBAAiB,EAAE,MAAM,CAAA;IACzB,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,6EAA6E;IAC7E,aAAa,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACxC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,aAAa,GAAG,SAAS,CAAA;IACxC,aAAa,EAAE,aAAa,GAAG,SAAS,CAAA;IACxC,IAAI,EAAE,gBAAgB,CAAA;CACvB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuDlB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,IAAI,CAgE7D"}
|
package/dist/src/stillCapture.js
CHANGED
|
@@ -4,6 +4,7 @@ import { getDimensions } from './dimensions.js';
|
|
|
4
4
|
import { escapeFileSystemPathSegment } from './fileSystemName.js';
|
|
5
5
|
import { EventRecorder } from './events.js';
|
|
6
6
|
import { resolveCrop } from './crop.js';
|
|
7
|
+
import { getMousePosition } from './mouse.js';
|
|
7
8
|
import { getScreenCIRuntimeContext } from './runtimeContext.js';
|
|
8
9
|
import { DEFAULT_VIDEO_OPTIONS, DEFAULT_ASPECT_RATIO, DEFAULT_QUALITY, } from './defaults.js';
|
|
9
10
|
/** File name of the raw page capture written beside a still's `data.json`. */
|
|
@@ -41,7 +42,7 @@ export function resolveStillName(name, path, used) {
|
|
|
41
42
|
* Returns the captured bytes so the wrapped `page.screenshot()` can return them.
|
|
42
43
|
*/
|
|
43
44
|
export async function writeStillRecording(params) {
|
|
44
|
-
const { name, screenciDir, dimensions, deviceScaleFactor, crop, testFilePath, configDir, recordOptions, renderOptions, deps, } = params;
|
|
45
|
+
const { name, screenciDir, dimensions, deviceScaleFactor, crop, mousePosition, testFilePath, configDir, recordOptions, renderOptions, deps, } = params;
|
|
45
46
|
const rm = deps.fs?.rm ??
|
|
46
47
|
((path) => fsRm(path, { recursive: true, force: true }));
|
|
47
48
|
const mkdir = deps.fs?.mkdir ??
|
|
@@ -58,6 +59,9 @@ export async function writeStillRecording(params) {
|
|
|
58
59
|
width: Math.round(dimensions.width * deviceScaleFactor),
|
|
59
60
|
height: Math.round(dimensions.height * deviceScaleFactor),
|
|
60
61
|
deviceScaleFactor,
|
|
62
|
+
...(mousePosition !== undefined && {
|
|
63
|
+
mousePosition: { x: mousePosition.x, y: mousePosition.y },
|
|
64
|
+
}),
|
|
61
65
|
};
|
|
62
66
|
const recorder = makeRecorder(renderOptions, recordOptions);
|
|
63
67
|
recorder.start();
|
|
@@ -103,6 +107,9 @@ export function bindStillCaptureToPage(page) {
|
|
|
103
107
|
if (cropTarget !== undefined && ctx.page !== null) {
|
|
104
108
|
crop = await resolveCrop(cropTarget, ctx.page);
|
|
105
109
|
}
|
|
110
|
+
// The cursor's last position, so the renderer can draw it on the still when
|
|
111
|
+
// the show option is set. Undefined when the body never moved the cursor.
|
|
112
|
+
const mousePosition = getMousePosition(page);
|
|
106
113
|
const configDir = process.env.SCREENCI_CONFIG_DIR ?? process.cwd();
|
|
107
114
|
return writeStillRecording({
|
|
108
115
|
name: resolvedName,
|
|
@@ -112,6 +119,9 @@ export function bindStillCaptureToPage(page) {
|
|
|
112
119
|
// captured mid-recording is at the viewport resolution (DSF 1).
|
|
113
120
|
deviceScaleFactor: 1,
|
|
114
121
|
...(crop !== undefined && { crop }),
|
|
122
|
+
...(mousePosition !== undefined && {
|
|
123
|
+
mousePosition: { x: mousePosition.x, y: mousePosition.y },
|
|
124
|
+
}),
|
|
115
125
|
testFilePath: ctx.testFilePath,
|
|
116
126
|
configDir,
|
|
117
127
|
recordOptions,
|