screenci 0.0.71 → 0.0.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -13
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +257 -81
- package/dist/cli.js.map +1 -1
- package/dist/docs/manifest.d.ts +3 -3
- package/dist/docs/manifest.js +1 -1
- package/dist/docs/manifest.js.map +1 -1
- package/dist/docs/video-sources/installation.screenci.js +13 -12
- package/dist/docs/video-sources/installation.screenci.js.map +1 -1
- package/dist/docs/video-sources/landing.screenci.js +16 -9
- package/dist/docs/video-sources/landing.screenci.js.map +1 -1
- package/dist/docs/video-sources/public-urls.screenci.js +16 -9
- package/dist/docs/video-sources/public-urls.screenci.js.map +1 -1
- package/dist/docs/video-sources/studio.screenci.js +42 -27
- package/dist/docs/video-sources/studio.screenci.js.map +1 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -4
- package/dist/index.js.map +1 -1
- package/dist/src/asset.d.ts +82 -51
- package/dist/src/asset.d.ts.map +1 -1
- package/dist/src/asset.js +93 -46
- package/dist/src/asset.js.map +1 -1
- package/dist/src/audio.d.ts +6 -24
- package/dist/src/audio.d.ts.map +1 -1
- package/dist/src/audio.js +10 -35
- package/dist/src/audio.js.map +1 -1
- package/dist/src/builder.d.ts +214 -0
- package/dist/src/builder.d.ts.map +1 -0
- package/dist/src/builder.js +212 -0
- package/dist/src/builder.js.map +1 -0
- package/dist/src/config.d.ts +1 -1
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +13 -7
- package/dist/src/config.js.map +1 -1
- package/dist/src/cue.d.ts +40 -123
- package/dist/src/cue.d.ts.map +1 -1
- package/dist/src/cue.js +190 -30
- package/dist/src/cue.js.map +1 -1
- package/dist/src/defaults.d.ts +2 -2
- package/dist/src/defaults.d.ts.map +1 -1
- package/dist/src/defaults.js +2 -2
- package/dist/src/defaults.js.map +1 -1
- package/dist/src/events.d.ts +80 -15
- package/dist/src/events.d.ts.map +1 -1
- package/dist/src/events.js +72 -6
- package/dist/src/events.js.map +1 -1
- package/dist/src/init.d.ts.map +1 -1
- package/dist/src/init.js +80 -39
- package/dist/src/init.js.map +1 -1
- package/dist/src/locales.d.ts +19 -0
- package/dist/src/locales.d.ts.map +1 -0
- package/dist/src/locales.js +60 -0
- package/dist/src/locales.js.map +1 -0
- package/dist/src/localize.d.ts +135 -0
- package/dist/src/localize.d.ts.map +1 -0
- package/dist/src/localize.js +195 -0
- package/dist/src/localize.js.map +1 -0
- package/dist/src/localizeRuntime.d.ts +53 -0
- package/dist/src/localizeRuntime.d.ts.map +1 -0
- package/dist/src/localizeRuntime.js +79 -0
- package/dist/src/localizeRuntime.js.map +1 -0
- package/dist/src/mouse.d.ts.map +1 -1
- package/dist/src/mouse.js +6 -0
- package/dist/src/mouse.js.map +1 -1
- package/dist/src/recordingData.d.ts +5 -4
- package/dist/src/recordingData.d.ts.map +1 -1
- package/dist/src/runtimeContext.d.ts +22 -4
- package/dist/src/runtimeContext.d.ts.map +1 -1
- package/dist/src/runtimeContext.js +11 -0
- package/dist/src/runtimeContext.js.map +1 -1
- package/dist/src/runtimeMode.d.ts +47 -0
- package/dist/src/runtimeMode.d.ts.map +1 -1
- package/dist/src/runtimeMode.js +124 -0
- package/dist/src/runtimeMode.js.map +1 -1
- package/dist/src/screenshot.d.ts +56 -2
- package/dist/src/screenshot.d.ts.map +1 -1
- package/dist/src/screenshot.js +49 -10
- package/dist/src/screenshot.js.map +1 -1
- package/dist/src/stillCapture.d.ts +10 -4
- package/dist/src/stillCapture.d.ts.map +1 -1
- package/dist/src/stillCapture.js +11 -0
- package/dist/src/stillCapture.js.map +1 -1
- package/dist/src/studio.d.ts +55 -22
- package/dist/src/studio.d.ts.map +1 -1
- package/dist/src/studio.js +38 -12
- package/dist/src/studio.js.map +1 -1
- package/dist/src/types.d.ts +20 -7
- 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 +149 -13
- package/dist/src/video.d.ts.map +1 -1
- package/dist/src/video.js +98 -25
- package/dist/src/video.js.map +1 -1
- package/dist/src/voiceConfig.d.ts +110 -0
- package/dist/src/voiceConfig.d.ts.map +1 -0
- package/dist/src/voiceConfig.js +2 -0
- package/dist/src/voiceConfig.js.map +1 -0
- package/dist/src/voices.d.ts +27 -23
- package/dist/src/voices.d.ts.map +1 -1
- package/dist/src/voices.js +7 -4
- package/dist/src/voices.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/skills/screenci/SKILL.md +51 -47
- package/skills/screenci/references/init.md +1 -1
- package/skills/screenci/references/record.md +6 -6
- package/dist/docs/video-sources/product-pitch.screenci.d.ts +0 -2
- package/dist/docs/video-sources/product-pitch.screenci.d.ts.map +0 -1
- package/dist/docs/video-sources/product-pitch.screenci.js +0 -136
- package/dist/docs/video-sources/product-pitch.screenci.js.map +0 -1
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { hide, resetZoom, video, voices, zoomTo } from 'screenci';
|
|
2
|
+
video.use({
|
|
3
|
+
renderOptions: {
|
|
4
|
+
narration: {
|
|
5
|
+
voice: { name: voices.Sophie, style: 'Friendly product guide' },
|
|
6
|
+
},
|
|
7
|
+
},
|
|
8
|
+
});
|
|
9
|
+
const appUrl = process.env.SCREENCI_APP_URL ?? 'https://app.screenci.com/';
|
|
2
10
|
// Studio walkthrough recorded against the ScreenCI app (app.screenci.com), for
|
|
3
11
|
// the "Remix in Studio on the web" tile on the landing page. Studio lives behind
|
|
4
12
|
// auth, so recording this needs a logged-in session: set SCREENCI_APP_STORAGE_STATE
|
|
5
13
|
// to a Playwright storageState JSON (see screenci.config.ts). The walkthrough is
|
|
6
14
|
// self-contained: it opens the first project and video, then enters Studio.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
video.localize({
|
|
16
|
+
narration: {
|
|
17
|
+
en: {
|
|
18
|
+
intro: 'Open any finished video in Studio to edit it right in the browser.',
|
|
19
|
+
edit: 'Review render options in the Studio panel, then render a new version. No code, no re-recording.',
|
|
20
|
+
},
|
|
21
|
+
es: {
|
|
22
|
+
intro: 'Abre cualquier video terminado en Studio para editarlo directamente en el navegador.',
|
|
23
|
+
edit: 'Revisa las opciones de render en el panel de Studio y genera una nueva version. Sin codigo y sin volver a grabar.',
|
|
24
|
+
},
|
|
16
25
|
},
|
|
17
|
-
})
|
|
18
|
-
video('Studio web editing', async ({ page }) => {
|
|
26
|
+
})('Studio web editing', async ({ page, narration }) => {
|
|
19
27
|
// Studio lives behind auth, so this recording only works with a logged-in
|
|
20
28
|
// session (SCREENCI_APP_STORAGE_STATE, see screenci.config.ts). When it is not
|
|
21
29
|
// configured (e.g. CI without the session secret), skip instead of timing out
|
|
@@ -23,35 +31,42 @@ video('Studio web editing', async ({ page }) => {
|
|
|
23
31
|
video.skip(!process.env.SCREENCI_APP_STORAGE_STATE, 'Requires SCREENCI_APP_STORAGE_STATE (a logged-in app session).');
|
|
24
32
|
// Navigate from the dashboard into Studio without showing it in the recording.
|
|
25
33
|
await hide(async () => {
|
|
26
|
-
await page.goto(
|
|
34
|
+
await page.goto(appUrl);
|
|
27
35
|
await page.waitForLoadState('networkidle');
|
|
28
36
|
await page
|
|
29
37
|
.getByRole('heading', { name: 'Projects' })
|
|
30
38
|
.waitFor({ timeout: 30000 });
|
|
31
39
|
await page.getByTestId('projects-list').getByRole('link').first().click();
|
|
32
|
-
// Open the first video
|
|
33
|
-
|
|
40
|
+
// Open the first video, then its first language page where Studio is shown
|
|
41
|
+
// inline below the preview.
|
|
42
|
+
await page.locator('a[href*="/video/"]').first().click();
|
|
34
43
|
await page
|
|
35
|
-
.getByRole('heading', { name: /
|
|
44
|
+
.getByRole('heading', { name: /language versions/i })
|
|
36
45
|
.waitFor({ timeout: 15000 });
|
|
37
|
-
await page
|
|
46
|
+
await page
|
|
47
|
+
.getByRole('link', { name: /^open /i })
|
|
48
|
+
.first()
|
|
49
|
+
.click();
|
|
38
50
|
await page
|
|
39
51
|
.getByRole('heading', { name: /^studio$/i })
|
|
40
52
|
.waitFor({ timeout: 30000 });
|
|
41
53
|
});
|
|
42
54
|
await narration.intro();
|
|
43
|
-
// Show
|
|
55
|
+
// Show the Studio panel without depending on a specific video having editable
|
|
56
|
+
// narration cues in the seeded dev data.
|
|
44
57
|
await narration.edit.start();
|
|
45
|
-
await
|
|
46
|
-
const addLanguage = page.getByPlaceholder(/add language/i);
|
|
47
|
-
await addLanguage.scrollIntoViewIfNeeded();
|
|
48
|
-
await addLanguage.click();
|
|
49
|
-
await addLanguage.fill('French');
|
|
50
|
-
});
|
|
51
|
-
await narration.edit.end();
|
|
52
|
-
// Highlight the Render action without clicking it (avoid kicking off a render).
|
|
53
|
-
await zoomTo(page.getByRole('button', { name: /^render( [a-z-]+)?$/i }));
|
|
58
|
+
await zoomTo(page.getByRole('heading', { name: /^studio$/i }));
|
|
54
59
|
await page.waitForTimeout(900);
|
|
55
60
|
await resetZoom();
|
|
61
|
+
await narration.edit.end();
|
|
62
|
+
const createOneOffButton = page.getByRole('button', {
|
|
63
|
+
name: 'Create one-off version',
|
|
64
|
+
});
|
|
65
|
+
if (await createOneOffButton.isVisible().catch(() => false)) {
|
|
66
|
+
// Highlight the render entry point without clicking it.
|
|
67
|
+
await zoomTo(createOneOffButton);
|
|
68
|
+
await page.waitForTimeout(900);
|
|
69
|
+
await resetZoom();
|
|
70
|
+
}
|
|
56
71
|
});
|
|
57
72
|
//# sourceMappingURL=studio.screenci.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio.screenci.js","sourceRoot":"","sources":["../../../docs/video-sources/studio.screenci.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"studio.screenci.js","sourceRoot":"","sources":["../../../docs/video-sources/studio.screenci.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -6,20 +6,22 @@ export { video } from './src/video.js';
|
|
|
6
6
|
export { screenshot } from './src/screenshot.js';
|
|
7
7
|
export type { CropTarget, CropRegion, CropOptions, ScreenshotCrop, ScreenshotCropRecord, ResolvedCropPadding, } from './src/crop.js';
|
|
8
8
|
export type { ScreenCIPage, ScreenCILocator } from './src/types.js';
|
|
9
|
-
export { createNarration, createStudioNarration } from './src/cue.js';
|
|
10
9
|
export type { NarrationCue, Cues, TopLevelVoiceConfig } from './src/cue.js';
|
|
11
|
-
export {
|
|
12
|
-
export type {
|
|
10
|
+
export type { LangNarrationOverride } from './src/voiceConfig.js';
|
|
11
|
+
export type { EachVariant, VideoBuilder } from './src/builder.js';
|
|
12
|
+
export type { LocalizeSpec, LocalizeMode, LocalizeVoiceSpec, NarrationByLang, TextByLang, VoiceConfig, LocalizeNarrationValue, } from './src/localize.js';
|
|
13
|
+
export { resolveLocaleForLanguage, DEFAULT_LANGUAGE_LOCALES, } from './src/locales.js';
|
|
14
|
+
export type { StudioDeclaration } from './src/studio.js';
|
|
13
15
|
export { hide } from './src/hide.js';
|
|
14
16
|
export { speed } from './src/speed.js';
|
|
15
17
|
export { time } from './src/time.js';
|
|
16
18
|
export { autoZoom } from './src/autoZoom.js';
|
|
17
19
|
export { zoomTo, resetZoom } from './src/manualZoom.js';
|
|
18
|
-
export { createOverlays,
|
|
20
|
+
export { createOverlays, setOverlayCss, MAX_AUDIO_LEVEL } from './src/asset.js';
|
|
19
21
|
export type { OverlayController, OverlayConfig, OverlayConfigFactory, OverlayInput, OverlayInputOrFactory, Overlays, OverlayControllerFor, ReactElementLike, OverlayPlacement, } from './src/asset.js';
|
|
20
22
|
export { overlayRect } from './src/overlayRect.js';
|
|
21
23
|
export type { OverlayRect, OverlayRectOptions } from './src/overlayRect.js';
|
|
22
|
-
export { createAudio
|
|
24
|
+
export { createAudio } from './src/audio.js';
|
|
23
25
|
export type { AudioController, AudioConfig, AudioInput, AudioTracks, } from './src/audio.js';
|
|
24
26
|
export type { ZoomTarget, ZoomTargetPoint } from './src/manualZoom.js';
|
|
25
27
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,WAAW,EACX,GAAG,EACH,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,MAAM,EACN,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACpD,YAAY,EACV,QAAQ,EACR,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,SAAS,GACV,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9C,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,YAAY,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,WAAW,EACX,GAAG,EACH,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACd,sBAAsB,EACtB,eAAe,EACf,MAAM,EACN,kBAAkB,GACnB,MAAM,gBAAgB,CAAA;AAGvB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACpD,YAAY,EACV,QAAQ,EACR,aAAa,EACb,kBAAkB,EAClB,cAAc,EACd,SAAS,GACV,MAAM,iBAAiB,CAAA;AAGxB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAG9C,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AAChD,YAAY,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,eAAe,CAAA;AACtB,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAKnE,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAA;AAC3E,YAAY,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AACjE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACjE,YAAY,EACV,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,WAAW,EACX,sBAAsB,GACvB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,kBAAkB,CAAA;AACzB,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAC/E,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,oBAAoB,EACpB,YAAY,EACZ,qBAAqB,EACrB,QAAQ,EACR,oBAAoB,EACpB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AACvB,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAClD,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,YAAY,EACV,eAAe,EACf,WAAW,EACX,UAAU,EACV,WAAW,GACZ,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -5,14 +5,13 @@ export { defineConfig } from './src/config.js';
|
|
|
5
5
|
// Re-export video fixture and narration cue
|
|
6
6
|
export { video } from './src/video.js';
|
|
7
7
|
export { screenshot } from './src/screenshot.js';
|
|
8
|
-
export {
|
|
9
|
-
export { isStudioRenderOptions } from './src/studio.js';
|
|
8
|
+
export { resolveLocaleForLanguage, DEFAULT_LANGUAGE_LOCALES, } from './src/locales.js';
|
|
10
9
|
export { hide } from './src/hide.js';
|
|
11
10
|
export { speed } from './src/speed.js';
|
|
12
11
|
export { time } from './src/time.js';
|
|
13
12
|
export { autoZoom } from './src/autoZoom.js';
|
|
14
13
|
export { zoomTo, resetZoom } from './src/manualZoom.js';
|
|
15
|
-
export { createOverlays,
|
|
14
|
+
export { createOverlays, setOverlayCss, MAX_AUDIO_LEVEL } from './src/asset.js';
|
|
16
15
|
export { overlayRect } from './src/overlayRect.js';
|
|
17
|
-
export { createAudio
|
|
16
|
+
export { createAudio } from './src/audio.js';
|
|
18
17
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAiBA,mBAAmB;AACnB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AASpD,4BAA4B;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,4CAA4C;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAiBA,mBAAmB;AACnB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AASpD,4BAA4B;AAC5B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,4CAA4C;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAA;AA0BhD,OAAO,EACL,wBAAwB,EACxB,wBAAwB,GACzB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AACtC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAC5C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAY/E,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAElD,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA"}
|
package/dist/src/asset.d.ts
CHANGED
|
@@ -15,30 +15,15 @@ export type ReactElementLike = {
|
|
|
15
15
|
key?: string | null;
|
|
16
16
|
};
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
19
|
-
* CSS pixels in the recording viewport (the same
|
|
20
|
-
* `boundingBox()`, `page.mouse`, and `viewportSize()`),
|
|
21
|
-
* independently: `relativeTo: 'recording'`, `x: 0`, `y: 0`.
|
|
22
|
-
* of `width`/`height` (the other follows the source aspect,
|
|
23
|
-
* When no placement field is set, the overlay fills the
|
|
18
|
+
* Placement and capture fields shared by every overlay variant. Placement is
|
|
19
|
+
* flat (not nested) and uses CSS pixels in the recording viewport (the same
|
|
20
|
+
* space as Playwright's `boundingBox()`, `page.mouse`, and `viewportSize()`),
|
|
21
|
+
* each defaulting independently: `relativeTo: 'recording'`, `x: 0`, `y: 0`.
|
|
22
|
+
* Provide exactly one of `width`/`height` (the other follows the source aspect,
|
|
23
|
+
* or `aspectRatio`). When no placement field is set, the overlay fills the
|
|
24
|
+
* recording area.
|
|
24
25
|
*/
|
|
25
|
-
|
|
26
|
-
/** File path: `.html` (rendered), `.svg`/`.png` (image), or `.mp4` (video). */
|
|
27
|
-
path?: string;
|
|
28
|
-
/**
|
|
29
|
-
* A React element, rendered to a transparent PNG. Use this for overlays built
|
|
30
|
-
* in JSX. Provide exactly one source: `path`, `element`, or `html`.
|
|
31
|
-
*/
|
|
32
|
-
element?: ReactElementLike;
|
|
33
|
-
/**
|
|
34
|
-
* An inline HTML fragment, rendered to a transparent PNG. Use this when you
|
|
35
|
-
* want plain HTML without a React dependency or a separate `.html` file. It
|
|
36
|
-
* must be a fragment (for example `'<div class="badge">New</div>'`), never a
|
|
37
|
-
* full document: `<!doctype>`, `<html>`, `<head>`, and `<body>` tags are
|
|
38
|
-
* rejected because screenci wraps the markup in its own document. Provide
|
|
39
|
-
* exactly one source: `path`, `element`, or `html`.
|
|
40
|
-
*/
|
|
41
|
-
html?: string;
|
|
26
|
+
type OverlayCommon = {
|
|
42
27
|
/** Reference box for placement coordinates. Defaults to `'recording'`. */
|
|
43
28
|
relativeTo?: 'screen' | 'recording';
|
|
44
29
|
/** Left edge in CSS px of the recording viewport. Defaults to `0`. */
|
|
@@ -54,13 +39,18 @@ export type OverlayConfig = {
|
|
|
54
39
|
* you provide, instead of the source's intrinsic aspect. Optional.
|
|
55
40
|
*/
|
|
56
41
|
aspectRatio?: number;
|
|
57
|
-
/**
|
|
58
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Fill the whole frame instead of positioning the overlay. `'recording'`
|
|
44
|
+
* fills the recording area (the same as omitting every placement field);
|
|
45
|
+
* `'screen'` fills the entire output frame, including any padding around the
|
|
46
|
+
* recording. Overrides `x`/`y`/`width`/`height`.
|
|
47
|
+
*/
|
|
48
|
+
fill?: 'recording' | 'screen';
|
|
59
49
|
/**
|
|
60
50
|
* Position the overlay over a live element, captured at recording time from
|
|
61
51
|
* the locator's bounding box. The overlay is sized to that box (plus
|
|
62
52
|
* {@link margin}) and fills it, so it frames the element exactly. Overrides
|
|
63
|
-
* `x`/`y`/`width`/`height`/`relativeTo`/`
|
|
53
|
+
* `x`/`y`/`width`/`height`/`relativeTo`/`fill` (placement is always
|
|
64
54
|
* recording-relative). HTML files, inline `html`, and React elements only;
|
|
65
55
|
* your content should fill its box (for example `width:100%;height:100%`).
|
|
66
56
|
*/
|
|
@@ -101,13 +91,16 @@ export type OverlayConfig = {
|
|
|
101
91
|
* HTML files and React elements only.
|
|
102
92
|
*/
|
|
103
93
|
capturePadding?: number;
|
|
94
|
+
};
|
|
95
|
+
/** Fields that only apply to a `.mp4` video overlay (a file `path`). */
|
|
96
|
+
type OverlayVideoFields = {
|
|
104
97
|
/**
|
|
105
98
|
* Soundtrack level for `.mp4` overlays as a linear gain. `1` (the default)
|
|
106
99
|
* plays the source at its natural level, `0` mutes it, and values above `1`
|
|
107
100
|
* boost it (e.g. `2` is twice the natural level). Capped at
|
|
108
101
|
* {@link MAX_AUDIO_LEVEL}.
|
|
109
102
|
*/
|
|
110
|
-
|
|
103
|
+
volume?: number;
|
|
111
104
|
/**
|
|
112
105
|
* Playback-rate multiplier for `.mp4` overlays. `2` plays the clip (and its
|
|
113
106
|
* audio) twice as fast, `0.5` at half speed; `1` (the default) is the natural
|
|
@@ -126,6 +119,59 @@ export type OverlayConfig = {
|
|
|
126
119
|
*/
|
|
127
120
|
time?: number;
|
|
128
121
|
};
|
|
122
|
+
/**
|
|
123
|
+
* An overlay drawn from a file `path`: `.svg`/`.png` (image), `.mp4` (video),
|
|
124
|
+
* or `.html` (rendered). Only this variant accepts the {@link OverlayVideoFields}
|
|
125
|
+
* (`volume`/`speed`/`time`), which apply to `.mp4` files; they are rejected at
|
|
126
|
+
* recording time for image and HTML files.
|
|
127
|
+
*/
|
|
128
|
+
export type FileOverlayConfig = OverlayCommon & OverlayVideoFields & {
|
|
129
|
+
/** File path: `.html` (rendered), `.svg`/`.png` (image), or `.mp4` (video). */
|
|
130
|
+
path: string;
|
|
131
|
+
element?: never;
|
|
132
|
+
html?: never;
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* An overlay rendered from a React `element` to a transparent PNG (or animated
|
|
136
|
+
* clip). Use this for overlays built in JSX. Video-only fields
|
|
137
|
+
* (`volume`/`speed`/`time`) do not apply.
|
|
138
|
+
*/
|
|
139
|
+
export type ElementOverlayConfig = OverlayCommon & {
|
|
140
|
+
/** A React element, rendered to a transparent PNG. */
|
|
141
|
+
element: ReactElementLike;
|
|
142
|
+
path?: never;
|
|
143
|
+
html?: never;
|
|
144
|
+
volume?: never;
|
|
145
|
+
speed?: never;
|
|
146
|
+
time?: never;
|
|
147
|
+
};
|
|
148
|
+
/**
|
|
149
|
+
* An overlay rendered from an inline `html` fragment to a transparent PNG (or
|
|
150
|
+
* animated clip). Use this when you want plain HTML without a React dependency
|
|
151
|
+
* or a separate `.html` file. The markup must be a single-rooted fragment (for
|
|
152
|
+
* example `'<div class="badge">New</div>'`), never a full document: it must
|
|
153
|
+
* contain exactly one top-level element, and `<!doctype>`, `<html>`, `<head>`,
|
|
154
|
+
* and `<body>` tags are rejected because screenci wraps the markup in its own
|
|
155
|
+
* document. Video-only fields (`volume`/`speed`/`time`) do not apply.
|
|
156
|
+
*/
|
|
157
|
+
export type HtmlOverlayConfig = OverlayCommon & {
|
|
158
|
+
/** An inline HTML fragment (single root element), rendered to a PNG. */
|
|
159
|
+
html: string;
|
|
160
|
+
path?: never;
|
|
161
|
+
element?: never;
|
|
162
|
+
volume?: never;
|
|
163
|
+
speed?: never;
|
|
164
|
+
time?: never;
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Display options for an overlay. An overlay draws its content from exactly one
|
|
168
|
+
* source, which selects the variant: a file {@link FileOverlayConfig.path}, a
|
|
169
|
+
* React {@link ElementOverlayConfig.element}, or an inline
|
|
170
|
+
* {@link HtmlOverlayConfig.html} fragment. The `path` variant additionally
|
|
171
|
+
* accepts the video-only `volume`/`speed`/`time` fields (for `.mp4` files); the
|
|
172
|
+
* others reject them at compile time.
|
|
173
|
+
*/
|
|
174
|
+
export type OverlayConfig = FileOverlayConfig | ElementOverlayConfig | HtmlOverlayConfig;
|
|
129
175
|
/**
|
|
130
176
|
* Upper bound for an audio level (linear gain). `4` is +12 dB, plenty of
|
|
131
177
|
* headroom for a boost while guarding against accidental extreme distortion.
|
|
@@ -236,7 +282,7 @@ export type Overlays<T extends Record<string, OverlayInputOrFactory>> = {
|
|
|
236
282
|
* badge: <Badge label="New" />, // React element
|
|
237
283
|
* note: { html: '<div class="note">Tip</div>', x: 1340, y: 110, width: 380 },
|
|
238
284
|
* logo: { path: 'logo.png', x: 96, y: 96, width: 240 },
|
|
239
|
-
* intro: { path: 'intro.mp4',
|
|
285
|
+
* intro: { path: 'intro.mp4', fill: 'screen' },
|
|
240
286
|
* })
|
|
241
287
|
*
|
|
242
288
|
* video('Product demo', async ({ page }) => {
|
|
@@ -254,31 +300,16 @@ export type Overlays<T extends Record<string, OverlayInputOrFactory>> = {
|
|
|
254
300
|
*/
|
|
255
301
|
export declare function createOverlays<const T extends Record<string, OverlayInputOrFactory>>(overlays: T): Overlays<T>;
|
|
256
302
|
/**
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
* The file (`.svg`, `.png`, or `.mp4`), placement, image duration, and video
|
|
264
|
-
* audio level all come from Studio.
|
|
303
|
+
* Builds overlay controllers for Studio-managed overlays declared via
|
|
304
|
+
* `video.studio({ overlays: [...] })`. Their file (`.svg`, `.png`, or `.mp4`),
|
|
305
|
+
* placement, image duration, and video audio level are configured on the
|
|
306
|
+
* ScreenCI Studio page instead of in code. Each name becomes a callable overlay
|
|
307
|
+
* controller with the same timeline behavior as a {@link createOverlays}
|
|
308
|
+
* controller, including `start()`/`end()`.
|
|
265
309
|
*
|
|
266
|
-
*
|
|
267
|
-
* video is configured in Studio (the CLI prints a direct link). Later uploads
|
|
268
|
-
* reuse the saved Studio configuration automatically.
|
|
269
|
-
*
|
|
270
|
-
* @example
|
|
271
|
-
* ```ts
|
|
272
|
-
* const overlays = createStudioOverlays('intro', 'logo')
|
|
273
|
-
*
|
|
274
|
-
* video('Product demo', async ({ page }) => {
|
|
275
|
-
* await overlays.intro()
|
|
276
|
-
* await page.goto('/dashboard')
|
|
277
|
-
* await overlays.logo()
|
|
278
|
-
* })
|
|
279
|
-
* ```
|
|
310
|
+
* Internal: the `overlays` fixture exposes these to the test body.
|
|
280
311
|
*/
|
|
281
|
-
export declare function
|
|
312
|
+
export declare function buildStudioOverlays(names: readonly string[]): Record<string, OverlayController>;
|
|
282
313
|
/**
|
|
283
314
|
* Upper bound for a playback-speed multiplier. `16` is already an extreme rate;
|
|
284
315
|
* the cap guards against accidental runaway values producing degenerate output.
|
package/dist/src/asset.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"asset.d.ts","sourceRoot":"","sources":["../../src/asset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAA;AAC/C,OAAO,KAAK,EAAE,cAAc,EAAoB,MAAM,aAAa,CAAA;AAWnE,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAWnD,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAEnD;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB,CAAA;AAED
|
|
1
|
+
{"version":3,"file":"asset.d.ts","sourceRoot":"","sources":["../../src/asset.ts"],"names":[],"mappings":"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"}
|
package/dist/src/asset.js
CHANGED
|
@@ -90,7 +90,7 @@ async function validateAssetPath(assetPath, testFilePath) {
|
|
|
90
90
|
* badge: <Badge label="New" />, // React element
|
|
91
91
|
* note: { html: '<div class="note">Tip</div>', x: 1340, y: 110, width: 380 },
|
|
92
92
|
* logo: { path: 'logo.png', x: 96, y: 96, width: 240 },
|
|
93
|
-
* intro: { path: 'intro.mp4',
|
|
93
|
+
* intro: { path: 'intro.mp4', fill: 'screen' },
|
|
94
94
|
* })
|
|
95
95
|
*
|
|
96
96
|
* video('Product demo', async ({ page }) => {
|
|
@@ -157,7 +157,7 @@ function buildOverlayFromConfig(name, config) {
|
|
|
157
157
|
hasHtml,
|
|
158
158
|
hasPath,
|
|
159
159
|
});
|
|
160
|
-
const fullScreen = config.
|
|
160
|
+
const fullScreen = config.fill === 'screen';
|
|
161
161
|
const animate = config.animate === true;
|
|
162
162
|
if (config.fps !== undefined && !animate) {
|
|
163
163
|
throw new Error(`[screenci] Overlay "${name}" sets "fps" without "animate: true". "fps" only applies to animated overlays.`);
|
|
@@ -221,8 +221,8 @@ function buildOverlayFromConfig(name, config) {
|
|
|
221
221
|
}
|
|
222
222
|
// File-backed image / video overlays.
|
|
223
223
|
if (extension === '.svg' || extension === '.png') {
|
|
224
|
-
if (config.
|
|
225
|
-
throw new Error(`[screenci] Overlay "${name}" (${path}) is an image and must not provide
|
|
224
|
+
if (config.volume !== undefined) {
|
|
225
|
+
throw new Error(`[screenci] Overlay "${name}" (${path}) is an image and must not provide volume. Use durationMs instead.`);
|
|
226
226
|
}
|
|
227
227
|
if (config.durationMs !== undefined) {
|
|
228
228
|
validateDurationMs(name, path, config.durationMs);
|
|
@@ -240,11 +240,11 @@ function buildOverlayFromConfig(name, config) {
|
|
|
240
240
|
if (config.durationMs !== undefined) {
|
|
241
241
|
throw new Error(`[screenci] Overlay "${name}" (${path}) is a video and must not provide durationMs. Its natural media duration is used instead.`);
|
|
242
242
|
}
|
|
243
|
-
if (config.
|
|
244
|
-
(!Number.isFinite(config.
|
|
245
|
-
config.
|
|
246
|
-
config.
|
|
247
|
-
throw new Error(`[screenci] Overlay "${name}" (${path}) must provide a finite
|
|
243
|
+
if (config.volume !== undefined &&
|
|
244
|
+
(!Number.isFinite(config.volume) ||
|
|
245
|
+
config.volume < 0 ||
|
|
246
|
+
config.volume > MAX_AUDIO_LEVEL)) {
|
|
247
|
+
throw new Error(`[screenci] Overlay "${name}" (${path}) must provide a finite volume between 0 and ${MAX_AUDIO_LEVEL} for .mp4 overlays. 1 is the natural level, 0 is silent, and values above 1 boost it.`);
|
|
248
248
|
}
|
|
249
249
|
validateSpeedTime(`Overlay "${name}" (${path})`, config.speed, config.time);
|
|
250
250
|
registeredAssetPaths.add(path);
|
|
@@ -253,7 +253,7 @@ function buildOverlayFromConfig(name, config) {
|
|
|
253
253
|
path,
|
|
254
254
|
...(placement !== undefined && { placement }),
|
|
255
255
|
fullScreen,
|
|
256
|
-
...(config.
|
|
256
|
+
...(config.volume !== undefined && { audio: config.volume }),
|
|
257
257
|
...(config.speed !== undefined && { speed: config.speed }),
|
|
258
258
|
...(config.time !== undefined && { time: config.time }),
|
|
259
259
|
});
|
|
@@ -283,6 +283,71 @@ function validateInlineHtmlFragment(name, html) {
|
|
|
283
283
|
throw new Error(`[screenci] Overlay "${name}" inline "html" must be a fragment, not a full HTML document. Remove the ${label} tag; screenci wraps the markup in a document for you.`);
|
|
284
284
|
}
|
|
285
285
|
}
|
|
286
|
+
validateSingleRootElement(name, html);
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* HTML void elements: they never have a closing tag, so they do not open a
|
|
290
|
+
* nesting level when counting top-level nodes.
|
|
291
|
+
*/
|
|
292
|
+
const VOID_ELEMENTS = new Set([
|
|
293
|
+
'area',
|
|
294
|
+
'base',
|
|
295
|
+
'br',
|
|
296
|
+
'col',
|
|
297
|
+
'embed',
|
|
298
|
+
'hr',
|
|
299
|
+
'img',
|
|
300
|
+
'input',
|
|
301
|
+
'link',
|
|
302
|
+
'meta',
|
|
303
|
+
'param',
|
|
304
|
+
'source',
|
|
305
|
+
'track',
|
|
306
|
+
'wbr',
|
|
307
|
+
]);
|
|
308
|
+
/**
|
|
309
|
+
* Ensures an inline `html` fragment has exactly one top-level (root) element and
|
|
310
|
+
* no loose top-level text, so it wraps cleanly into screenci's overlay document
|
|
311
|
+
* and sizes predictably. Multiple siblings (for example `<div/><div/>`) or stray
|
|
312
|
+
* text outside the root are rejected. The markup inside the root may be anything,
|
|
313
|
+
* including `<script>`/`<style>`, which are left to the overlay renderer.
|
|
314
|
+
*
|
|
315
|
+
* This is a lightweight tag scanner, not a full HTML parser: it tracks nesting
|
|
316
|
+
* depth across opening, closing, void, and self-closing tags (skipping comments
|
|
317
|
+
* and quoted attribute values) which covers ordinary fragment markup.
|
|
318
|
+
*/
|
|
319
|
+
function validateSingleRootElement(name, html) {
|
|
320
|
+
const fail = () => {
|
|
321
|
+
throw new Error(`[screenci] Overlay "${name}" inline "html" must contain a single root element (for example '<div class="badge">New</div>'). Wrap multiple top-level nodes in one container.`);
|
|
322
|
+
};
|
|
323
|
+
// Drop comments so they never count as top-level content.
|
|
324
|
+
const stripped = html.replace(/<!--[\s\S]*?-->/g, '');
|
|
325
|
+
const tagRe = /<(\/?)([a-zA-Z][a-zA-Z0-9-]*)((?:[^>"']|"[^"]*"|'[^']*')*?)(\/?)>/g;
|
|
326
|
+
let depth = 0;
|
|
327
|
+
let rootElements = 0;
|
|
328
|
+
let lastIndex = 0;
|
|
329
|
+
let match;
|
|
330
|
+
while ((match = tagRe.exec(stripped)) !== null) {
|
|
331
|
+
const before = stripped.slice(lastIndex, match.index);
|
|
332
|
+
if (depth === 0 && before.trim().length > 0)
|
|
333
|
+
fail();
|
|
334
|
+
lastIndex = match.index + match[0].length;
|
|
335
|
+
const isClosing = match[1] === '/';
|
|
336
|
+
const tagName = match[2].toLowerCase();
|
|
337
|
+
const selfClosing = match[4] === '/';
|
|
338
|
+
if (isClosing) {
|
|
339
|
+
depth = Math.max(0, depth - 1);
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (depth === 0)
|
|
343
|
+
rootElements += 1;
|
|
344
|
+
if (!selfClosing && !VOID_ELEMENTS.has(tagName))
|
|
345
|
+
depth += 1;
|
|
346
|
+
}
|
|
347
|
+
if (stripped.slice(lastIndex).trim().length > 0 && depth === 0)
|
|
348
|
+
fail();
|
|
349
|
+
if (rootElements !== 1)
|
|
350
|
+
fail();
|
|
286
351
|
}
|
|
287
352
|
async function renderElementToMarkup(name, element) {
|
|
288
353
|
let reactDomServer;
|
|
@@ -347,41 +412,19 @@ async function readHtmlOverlayFile(path) {
|
|
|
347
412
|
return readFile(resolved, 'utf-8');
|
|
348
413
|
}
|
|
349
414
|
/**
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
*
|
|
415
|
+
* Builds overlay controllers for Studio-managed overlays declared via
|
|
416
|
+
* `video.studio({ overlays: [...] })`. Their file (`.svg`, `.png`, or `.mp4`),
|
|
417
|
+
* placement, image duration, and video audio level are configured on the
|
|
418
|
+
* ScreenCI Studio page instead of in code. Each name becomes a callable overlay
|
|
419
|
+
* controller with the same timeline behavior as a {@link createOverlays}
|
|
420
|
+
* controller, including `start()`/`end()`.
|
|
353
421
|
*
|
|
354
|
-
*
|
|
355
|
-
* behavior as {@link createOverlays} controllers, including `start()`/`end()`.
|
|
356
|
-
* The file (`.svg`, `.png`, or `.mp4`), placement, image duration, and video
|
|
357
|
-
* audio level all come from Studio.
|
|
358
|
-
*
|
|
359
|
-
* On the first upload of a studio-mode video, rendering is held until the
|
|
360
|
-
* video is configured in Studio (the CLI prints a direct link). Later uploads
|
|
361
|
-
* reuse the saved Studio configuration automatically.
|
|
362
|
-
*
|
|
363
|
-
* @example
|
|
364
|
-
* ```ts
|
|
365
|
-
* const overlays = createStudioOverlays('intro', 'logo')
|
|
366
|
-
*
|
|
367
|
-
* video('Product demo', async ({ page }) => {
|
|
368
|
-
* await overlays.intro()
|
|
369
|
-
* await page.goto('/dashboard')
|
|
370
|
-
* await overlays.logo()
|
|
371
|
-
* })
|
|
372
|
-
* ```
|
|
422
|
+
* Internal: the `overlays` fixture exposes these to the test body.
|
|
373
423
|
*/
|
|
374
|
-
export function
|
|
375
|
-
const seen = new Set();
|
|
376
|
-
for (const key of keys) {
|
|
377
|
-
if (seen.has(key)) {
|
|
378
|
-
throw new Error(`Duplicate overlay key "${key}" passed to createStudioOverlays. Overlay keys must be unique.`);
|
|
379
|
-
}
|
|
380
|
-
seen.add(key);
|
|
381
|
-
}
|
|
424
|
+
export function buildStudioOverlays(names) {
|
|
382
425
|
const result = {};
|
|
383
|
-
for (const
|
|
384
|
-
result[
|
|
426
|
+
for (const name of names) {
|
|
427
|
+
result[name] = createStudioAssetController(name);
|
|
385
428
|
}
|
|
386
429
|
return result;
|
|
387
430
|
}
|
|
@@ -682,9 +725,13 @@ function validatePlacement(name, placement) {
|
|
|
682
725
|
* since the recording size is known there.
|
|
683
726
|
*/
|
|
684
727
|
function resolveOverlayPlacement(name, config) {
|
|
685
|
-
if (config.
|
|
728
|
+
if (config.fill === 'screen') {
|
|
686
729
|
return { fullScreen: true };
|
|
687
730
|
}
|
|
731
|
+
if (config.fill === 'recording') {
|
|
732
|
+
// Fill the recording area (resolved by the renderer, which knows its size).
|
|
733
|
+
return undefined;
|
|
734
|
+
}
|
|
688
735
|
if (config.width !== undefined && config.height !== undefined) {
|
|
689
736
|
throw new Error(`[screenci] Overlay "${name}" must set only one of width or height (the other is derived from the aspect ratio).`);
|
|
690
737
|
}
|
|
@@ -695,7 +742,7 @@ function resolveOverlayPlacement(name, config) {
|
|
|
695
742
|
config.relativeTo !== undefined ||
|
|
696
743
|
config.aspectRatio !== undefined;
|
|
697
744
|
if (positioned) {
|
|
698
|
-
throw new Error(`[screenci] Overlay "${name}" must set "width" or "height" (in CSS px) when positioning it. Omit all placement fields to fill the recording area, or set "
|
|
745
|
+
throw new Error(`[screenci] Overlay "${name}" must set "width" or "height" (in CSS px) when positioning it. Omit all placement fields to fill the recording area, or set "fill".`);
|
|
699
746
|
}
|
|
700
747
|
// Fill the recording area (resolved by the renderer, which knows its size).
|
|
701
748
|
return undefined;
|
|
@@ -740,8 +787,8 @@ function resolvePlacementSource(name, config, flags) {
|
|
|
740
787
|
if (!isRendered) {
|
|
741
788
|
throw new Error(`[screenci] Overlay "${name}" can only use "over" with a React element, inline "html", or an .html file (the overlay is sized to the element's box).`);
|
|
742
789
|
}
|
|
743
|
-
if (config.
|
|
744
|
-
throw new Error(`[screenci] Overlay "${name}" cannot set both "over" and "
|
|
790
|
+
if (config.fill !== undefined) {
|
|
791
|
+
throw new Error(`[screenci] Overlay "${name}" cannot set both "over" and "fill".`);
|
|
745
792
|
}
|
|
746
793
|
if (config.x !== undefined ||
|
|
747
794
|
config.y !== undefined ||
|