screenci 0.0.76 → 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 +51 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +557 -200
- package/dist/cli.js.map +1 -1
- package/dist/docs/manifest.d.ts +253 -98
- package/dist/docs/manifest.d.ts.map +1 -1
- package/dist/docs/manifest.js +92 -32
- 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 +14 -3
- package/dist/docs/video-sources/installation.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 +1 -1
- package/dist/docs/video-sources/locator-highlight-animated.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/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 +8 -0
- 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 +10 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -1
- package/dist/index.js.map +1 -1
- package/dist/src/asset.d.ts +154 -13
- package/dist/src/asset.d.ts.map +1 -1
- package/dist/src/asset.js +262 -32
- 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 +8 -5
- package/dist/src/audio.d.ts.map +1 -1
- package/dist/src/audio.js +56 -31
- package/dist/src/audio.js.map +1 -1
- package/dist/src/builder.d.ts +132 -32
- package/dist/src/builder.d.ts.map +1 -1
- package/dist/src/builder.js +66 -20
- package/dist/src/builder.js.map +1 -1
- package/dist/src/changeFocus.d.ts.map +1 -1
- package/dist/src/changeFocus.js +103 -34
- package/dist/src/changeFocus.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 +8 -4
- package/dist/src/declare.d.ts.map +1 -1
- package/dist/src/declare.js +28 -10
- package/dist/src/declare.js.map +1 -1
- package/dist/src/defaults.d.ts +23 -0
- package/dist/src/defaults.d.ts.map +1 -1
- package/dist/src/defaults.js +23 -0
- package/dist/src/defaults.js.map +1 -1
- package/dist/src/events.d.ts +289 -25
- package/dist/src/events.d.ts.map +1 -1
- package/dist/src/events.js +145 -13
- 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 +64 -18
- package/dist/src/init.js.map +1 -1
- package/dist/src/instrument.d.ts.map +1 -1
- package/dist/src/instrument.js +25 -7
- 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 +25 -17
- package/dist/src/localize.d.ts.map +1 -1
- package/dist/src/localize.js +18 -10
- package/dist/src/localize.js.map +1 -1
- package/dist/src/localizeRuntime.d.ts +18 -13
- package/dist/src/localizeRuntime.d.ts.map +1 -1
- package/dist/src/localizeRuntime.js +28 -18
- 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/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/recordingData.d.ts +7 -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 +34 -4
- package/dist/src/screenAudio.d.ts.map +1 -1
- package/dist/src/screenAudio.js +0 -0
- package/dist/src/screenAudio.js.map +1 -1
- package/dist/src/screenAudioSink.d.ts +12 -0
- package/dist/src/screenAudioSink.d.ts.map +1 -1
- package/dist/src/screenAudioSink.js +30 -0
- package/dist/src/screenAudioSink.js.map +1 -1
- package/dist/src/screenshot.d.ts +16 -14
- package/dist/src/screenshot.d.ts.map +1 -1
- package/dist/src/screenshot.js +11 -11
- 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/studio.d.ts +71 -3
- package/dist/src/studio.d.ts.map +1 -1
- package/dist/src/studio.js +19 -1
- 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 +85 -0
- 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 +37 -34
- package/dist/src/video.d.ts.map +1 -1
- package/dist/src/video.js +99 -48
- 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/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/templates/recordings/assets/logo.png +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordingSize.d.ts","sourceRoot":"","sources":["../../src/recordingSize.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAgBxC,MAAM,MAAM,sBAAsB,GAAG;IACnC,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,IAAI,CAAC,CASf;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC,CAKxD"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ScreenciError } from './errors.js';
|
|
2
|
+
import { getActiveHideRecorder } from './timelineBlock.js';
|
|
3
|
+
import { getRuntimeRecordingSize, setRuntimeRecordingSize, } from './runtimeContext.js';
|
|
4
|
+
/**
|
|
5
|
+
* Full-screen size: the recording fills the output frame, with one side
|
|
6
|
+
* touching the background edge.
|
|
7
|
+
*/
|
|
8
|
+
const FULL_SCREEN_SIZE = 1;
|
|
9
|
+
function assertValidSize(size) {
|
|
10
|
+
if (!Number.isFinite(size) || size <= 0 || size > 1) {
|
|
11
|
+
throw new ScreenciError(`resizeRecording(size) requires size in (0, 1]; received ${size}`);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Shrinks the recording to a limited size, revealing the styled background
|
|
16
|
+
* around it from this point on.
|
|
17
|
+
*
|
|
18
|
+
* `size` is a 0-1 fraction of the full frame: `1` is full screen, `0.8` shows
|
|
19
|
+
* the recording at 80% of the frame, centered. Call {@link resetRecordingSize}
|
|
20
|
+
* to return to full screen.
|
|
21
|
+
*
|
|
22
|
+
* Pass `duration` to animate the transition over that many milliseconds.
|
|
23
|
+
* Use `easing` to control the animation curve (defaults to `'ease-out'`).
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* await resizeRecording(0.8, { duration: 400, easing: 'ease-in-out' })
|
|
28
|
+
* await page.getByRole('button', { name: 'Continue' }).click()
|
|
29
|
+
* await resetRecordingSize()
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export async function resizeRecording(size, options) {
|
|
33
|
+
assertValidSize(size);
|
|
34
|
+
const recorder = getActiveHideRecorder();
|
|
35
|
+
if (options !== undefined) {
|
|
36
|
+
recorder.addRecordingSizeStart(size, options);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
recorder.addRecordingSizeStart(size);
|
|
40
|
+
}
|
|
41
|
+
setRuntimeRecordingSize(size);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Returns the recording to full screen (size 1) from this point on. No-op when
|
|
45
|
+
* the recording is already full screen.
|
|
46
|
+
*/
|
|
47
|
+
export async function resetRecordingSize() {
|
|
48
|
+
if (getRuntimeRecordingSize() === FULL_SCREEN_SIZE)
|
|
49
|
+
return;
|
|
50
|
+
const recorder = getActiveHideRecorder();
|
|
51
|
+
recorder.addRecordingSizeEnd();
|
|
52
|
+
setRuntimeRecordingSize(FULL_SCREEN_SIZE);
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=recordingSize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordingSize.js","sourceRoot":"","sources":["../../src/recordingSize.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAA;AAC1D,OAAO,EACL,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,qBAAqB,CAAA;AAG5B;;;GAGG;AACH,MAAM,gBAAgB,GAAG,CAAC,CAAA;AAE1B,SAAS,eAAe,CAAC,IAAY;IACnC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,aAAa,CACrB,2DAA2D,IAAI,EAAE,CAClE,CAAA;IACH,CAAC;AACH,CAAC;AASD;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY,EACZ,OAAgC;IAEhC,eAAe,CAAC,IAAI,CAAC,CAAA;IACrB,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAA;IACxC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,QAAQ,CAAC,qBAAqB,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC/C,CAAC;SAAM,CAAC;QACN,QAAQ,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAA;IACtC,CAAC;IACD,uBAAuB,CAAC,IAAI,CAAC,CAAA;AAC/B,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,uBAAuB,EAAE,KAAK,gBAAgB;QAAE,OAAM;IAC1D,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAA;IACxC,QAAQ,CAAC,mBAAmB,EAAE,CAAA;IAC9B,uBAAuB,CAAC,gBAAgB,CAAC,CAAA;AAC3C,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Locator, Page } from '@playwright/test';
|
|
2
|
+
import type { RedactHandle, RedactOptions } from './types.js';
|
|
3
|
+
import { type ResolvedRedactStyle } from './redactController.js';
|
|
4
|
+
/** Default corner radius (px) for the mask. */
|
|
5
|
+
export declare const DEFAULT_REDACT_RADIUS = 12;
|
|
6
|
+
/** Default mask shadow. Reads as a raised chip so a redaction is obvious. */
|
|
7
|
+
export declare const DEFAULT_REDACT_SHADOW = "0 2px 8px rgba(0,0,0,0.25)";
|
|
8
|
+
/**
|
|
9
|
+
* How long `redact()` waits for at least one matching element before masking,
|
|
10
|
+
* so a redact issued just before the element renders does not silently no-op.
|
|
11
|
+
*/
|
|
12
|
+
export declare const REDACT_ATTACH_TIMEOUT_MS = 5000;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve user-facing {@link RedactOptions} to the concrete pixel values the
|
|
15
|
+
* in-page controller applies. Pure (no page access), so it is unit-testable on
|
|
16
|
+
* its own.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveRedactStyle(options?: RedactOptions): ResolvedRedactStyle;
|
|
19
|
+
/**
|
|
20
|
+
* Make sure the in-page controller is installed. `addInitScript` covers every
|
|
21
|
+
* future document (registered once per recording); the extra `evaluate` covers
|
|
22
|
+
* the document that is already open when `redact()` is first called. Both are
|
|
23
|
+
* idempotent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function ensureRedactControllerInstalled(page: Page): Promise<void>;
|
|
26
|
+
/**
|
|
27
|
+
* Install the in-page controller on a freshly created page, before its first
|
|
28
|
+
* navigation, so masks apply from the first painted frame. Also registers the
|
|
29
|
+
* `recordOptions.redact` first-paint selectors. Called once per page from the
|
|
30
|
+
* video fixture; the runtime flag is set on the passed context (which is not yet
|
|
31
|
+
* the active context at page-creation time).
|
|
32
|
+
*/
|
|
33
|
+
export declare function installRedactController(page: Page, redactState: {
|
|
34
|
+
controllerInstalled: boolean;
|
|
35
|
+
}, configSelectors?: string[]): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Mask the element(s) matched by `locator` so the content never enters the
|
|
38
|
+
* recording. The mask is applied client side in the live DOM, so the obscured
|
|
39
|
+
* pixels are never captured and never uploaded to screenci. This is true
|
|
40
|
+
* redaction, unlike {@link cover}, which only hides content in the published
|
|
41
|
+
* output.
|
|
42
|
+
*
|
|
43
|
+
* To hide a secret that is already on screen without a visible flash, register
|
|
44
|
+
* the mask before revealing it (inside `hide()` or via the `recordOptions.redact`
|
|
45
|
+
* config for always-secret elements).
|
|
46
|
+
*
|
|
47
|
+
* @example Persistent
|
|
48
|
+
* ```ts
|
|
49
|
+
* const handle = await redact(page.getByTestId('api-key'))
|
|
50
|
+
* // ... later
|
|
51
|
+
* await handle.unredact()
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example Scoped
|
|
55
|
+
* ```ts
|
|
56
|
+
* await redact(page.getByTestId('balance'), async () => {
|
|
57
|
+
* await page.getByRole('button', { name: 'Reveal' }).click()
|
|
58
|
+
* })
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
export declare function redact(locator: Locator, options?: RedactOptions): Promise<RedactHandle>;
|
|
62
|
+
export declare function redact(locator: Locator, fn: () => Promise<void> | void, options?: RedactOptions): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Remove every active mask. Called automatically at the end of each recording
|
|
65
|
+
* so masks never leak across recordings that share a browser worker.
|
|
66
|
+
*/
|
|
67
|
+
export declare function unredactAll(): Promise<void>;
|
|
68
|
+
//# sourceMappingURL=redact.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/redact.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACrD,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC7D,OAAO,EAIL,KAAK,mBAAmB,EACzB,MAAM,uBAAuB,CAAA;AAI9B,+CAA+C;AAC/C,eAAO,MAAM,qBAAqB,KAAK,CAAA;AACvC,6EAA6E;AAC7E,eAAO,MAAM,qBAAqB,+BAA+B,CAAA;AAEjE;;;GAGG;AACH,eAAO,MAAM,wBAAwB,OAAO,CAAA;AAE5C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,CAAC,EAAE,aAAa,GACtB,mBAAmB,CAUrB;AAWD;;;;;GAKG;AACH,wBAAsB,+BAA+B,CACnD,IAAI,EAAE,IAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,IAAI,EACV,WAAW,EAAE;IAAE,mBAAmB,EAAE,OAAO,CAAA;CAAE,EAC7C,eAAe,CAAC,EAAE,MAAM,EAAE,GACzB,OAAO,CAAC,IAAI,CAAC,CASf;AAqDD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,MAAM,CACpB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,YAAY,CAAC,CAAA;AACxB,wBAAgB,MAAM,CACpB,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,EAC9B,OAAO,CAAC,EAAE,aAAa,GACtB,OAAO,CAAC,IAAI,CAAC,CAAA;AAkBhB;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAYjD"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { redactApplyConfigSelectors, redactControllerBootstrap, } from './redactController.js';
|
|
2
|
+
import { getRuntimePage, getRuntimeRedactState } from './runtimeContext.js';
|
|
3
|
+
import { logger } from './logger.js';
|
|
4
|
+
/** Default corner radius (px) for the mask. */
|
|
5
|
+
export const DEFAULT_REDACT_RADIUS = 12;
|
|
6
|
+
/** Default mask shadow. Reads as a raised chip so a redaction is obvious. */
|
|
7
|
+
export const DEFAULT_REDACT_SHADOW = '0 2px 8px rgba(0,0,0,0.25)';
|
|
8
|
+
/**
|
|
9
|
+
* How long `redact()` waits for at least one matching element before masking,
|
|
10
|
+
* so a redact issued just before the element renders does not silently no-op.
|
|
11
|
+
*/
|
|
12
|
+
export const REDACT_ATTACH_TIMEOUT_MS = 5000;
|
|
13
|
+
/**
|
|
14
|
+
* Resolve user-facing {@link RedactOptions} to the concrete pixel values the
|
|
15
|
+
* in-page controller applies. Pure (no page access), so it is unit-testable on
|
|
16
|
+
* its own.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveRedactStyle(options) {
|
|
19
|
+
const style = options?.style;
|
|
20
|
+
return {
|
|
21
|
+
// null means "sample a color from underneath" in the page.
|
|
22
|
+
color: style?.color ?? null,
|
|
23
|
+
radiusPx: style?.radius ?? DEFAULT_REDACT_RADIUS,
|
|
24
|
+
shadow: style?.shadow === false ? null : (style?.shadow ?? DEFAULT_REDACT_SHADOW),
|
|
25
|
+
css: style?.css ?? null,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
let redactCounter = 0;
|
|
29
|
+
function nextRedactId() {
|
|
30
|
+
redactCounter += 1;
|
|
31
|
+
return `__screenci_redact_${redactCounter}`;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Make sure the in-page controller is installed. `addInitScript` covers every
|
|
35
|
+
* future document (registered once per recording); the extra `evaluate` covers
|
|
36
|
+
* the document that is already open when `redact()` is first called. Both are
|
|
37
|
+
* idempotent.
|
|
38
|
+
*/
|
|
39
|
+
export async function ensureRedactControllerInstalled(page) {
|
|
40
|
+
const state = getRuntimeRedactState();
|
|
41
|
+
if (!state.controllerInstalled) {
|
|
42
|
+
await page.addInitScript(redactControllerBootstrap);
|
|
43
|
+
state.controllerInstalled = true;
|
|
44
|
+
}
|
|
45
|
+
await page.evaluate(redactControllerBootstrap);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Install the in-page controller on a freshly created page, before its first
|
|
49
|
+
* navigation, so masks apply from the first painted frame. Also registers the
|
|
50
|
+
* `recordOptions.redact` first-paint selectors. Called once per page from the
|
|
51
|
+
* video fixture; the runtime flag is set on the passed context (which is not yet
|
|
52
|
+
* the active context at page-creation time).
|
|
53
|
+
*/
|
|
54
|
+
export async function installRedactController(page, redactState, configSelectors) {
|
|
55
|
+
await page.addInitScript(redactControllerBootstrap);
|
|
56
|
+
redactState.controllerInstalled = true;
|
|
57
|
+
if (configSelectors && configSelectors.length > 0) {
|
|
58
|
+
await page.addInitScript(redactApplyConfigSelectors, {
|
|
59
|
+
selectors: configSelectors,
|
|
60
|
+
style: resolveRedactStyle(),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async function applyRedact(locator, options) {
|
|
65
|
+
const page = locator.page();
|
|
66
|
+
const resolved = resolveRedactStyle(options);
|
|
67
|
+
const id = nextRedactId();
|
|
68
|
+
await ensureRedactControllerInstalled(page);
|
|
69
|
+
// Wait for at least one match so a redact issued a beat before the element
|
|
70
|
+
// renders still masks it instead of silently doing nothing.
|
|
71
|
+
try {
|
|
72
|
+
await locator
|
|
73
|
+
.first()
|
|
74
|
+
.waitFor({ state: 'attached', timeout: REDACT_ATTACH_TIMEOUT_MS });
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
logger.warn('[screenci] redact() found no matching element to mask. The selector may be wrong or the element may not have rendered yet.');
|
|
78
|
+
}
|
|
79
|
+
// Register inside the page so the controller receives the real elements
|
|
80
|
+
// (this pierces shadow DOM, which a selector re-query could not). The
|
|
81
|
+
// controller positions the mask synchronously here, so no unmasked frame
|
|
82
|
+
// escapes once this resolves.
|
|
83
|
+
await locator.evaluateAll((elements, arg) => {
|
|
84
|
+
const api = window.__screenci_redact;
|
|
85
|
+
if (api)
|
|
86
|
+
api.addElements(arg.id, elements, arg.style);
|
|
87
|
+
}, { id, style: resolved });
|
|
88
|
+
getRuntimeRedactState().activeMasks.set(id, resolved);
|
|
89
|
+
return {
|
|
90
|
+
async unredact() {
|
|
91
|
+
getRuntimeRedactState().activeMasks.delete(id);
|
|
92
|
+
try {
|
|
93
|
+
await page.evaluate((maskId) => {
|
|
94
|
+
const api = window.__screenci_redact;
|
|
95
|
+
if (api)
|
|
96
|
+
api.remove(maskId);
|
|
97
|
+
}, id);
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// The page may already be closing; the mask dies with it.
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export async function redact(locator, optionsOrFn, maybeOptions) {
|
|
106
|
+
if (typeof optionsOrFn === 'function') {
|
|
107
|
+
const handle = await applyRedact(locator, maybeOptions);
|
|
108
|
+
try {
|
|
109
|
+
await optionsOrFn();
|
|
110
|
+
}
|
|
111
|
+
finally {
|
|
112
|
+
await handle.unredact();
|
|
113
|
+
}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
return applyRedact(locator, optionsOrFn);
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Remove every active mask. Called automatically at the end of each recording
|
|
120
|
+
* so masks never leak across recordings that share a browser worker.
|
|
121
|
+
*/
|
|
122
|
+
export async function unredactAll() {
|
|
123
|
+
getRuntimeRedactState().activeMasks.clear();
|
|
124
|
+
const page = getRuntimePage();
|
|
125
|
+
if (!page)
|
|
126
|
+
return;
|
|
127
|
+
try {
|
|
128
|
+
await page.evaluate(() => {
|
|
129
|
+
const api = window.__screenci_redact;
|
|
130
|
+
if (api)
|
|
131
|
+
api.clear();
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Best effort; the page may already be closing.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=redact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../src/redact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-page redaction controller.
|
|
3
|
+
*
|
|
4
|
+
* screenci records the live browser page, so the only way to keep a secret out
|
|
5
|
+
* of `recording.mp4` (and therefore out of the upload) is to mask it in the DOM
|
|
6
|
+
* before Chrome captures the frame. This module is the browser-side half: a
|
|
7
|
+
* self-contained controller installed via `addInitScript` that covers target
|
|
8
|
+
* elements with an opaque panel (by default tinted to match the surface beneath
|
|
9
|
+
* them), tracking their position every animation frame so the mask follows
|
|
10
|
+
* scroll, layout, and animation.
|
|
11
|
+
*
|
|
12
|
+
* The functions here are serialized and shipped to the page by Playwright, so
|
|
13
|
+
* they must not reference anything outside their own bodies.
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* A redact style resolved to concrete pixel values, ready to apply in the page.
|
|
17
|
+
* `RedactStyle` (the user-facing option) is resolved to this by
|
|
18
|
+
* `resolveRedactStyle` in `redact.ts`.
|
|
19
|
+
*/
|
|
20
|
+
export type ResolvedRedactStyle = {
|
|
21
|
+
/** Fixed fill color, or null to sample one from underneath in the page. */
|
|
22
|
+
color: string | null;
|
|
23
|
+
radiusPx: number;
|
|
24
|
+
shadow: string | null;
|
|
25
|
+
/** Extra CSS for the mask panel, or null. */
|
|
26
|
+
css: string | null;
|
|
27
|
+
};
|
|
28
|
+
/** Window global exposed by the controller for the Node side to drive. */
|
|
29
|
+
export type RedactWindowApi = {
|
|
30
|
+
/** Mask specific element handles (used by runtime `redact`; pierces shadow DOM). */
|
|
31
|
+
addElements: (id: string, elements: Element[], style: ResolvedRedactStyle) => void;
|
|
32
|
+
/** Mask everything matching a CSS selector, re-queried each frame. */
|
|
33
|
+
addSelector: (id: string, selector: string, style: ResolvedRedactStyle) => void;
|
|
34
|
+
/** Remove a mask by id, restoring the element. */
|
|
35
|
+
remove: (id: string) => void;
|
|
36
|
+
/** Remove every mask. */
|
|
37
|
+
clear: () => void;
|
|
38
|
+
};
|
|
39
|
+
/** Attribute stamped on elements masked by a runtime `redact` call. */
|
|
40
|
+
export declare const REDACT_ID_ATTRIBUTE = "data-screenci-redact-id";
|
|
41
|
+
/** Id of the fixed-position container that holds every tint div. */
|
|
42
|
+
export declare const REDACT_ROOT_ID = "__screenci_redact_root";
|
|
43
|
+
/** Name of the window global the controller installs. */
|
|
44
|
+
export declare const REDACT_WINDOW_KEY = "__screenci_redact";
|
|
45
|
+
/**
|
|
46
|
+
* The controller bootstrap. Runs in the page (via `addInitScript`) on every
|
|
47
|
+
* document. Idempotent: a second run is a no-op, so it is safe to inject more
|
|
48
|
+
* than once.
|
|
49
|
+
*/
|
|
50
|
+
export declare function redactControllerBootstrap(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Init script that masks the configured first-paint selectors. Registered after
|
|
53
|
+
* the bootstrap so `window.__screenci_redact` already exists. A CSS rule hides
|
|
54
|
+
* matches the instant they paint (race free, no JS timing), and the controller
|
|
55
|
+
* lays the opaque panel over them a frame later.
|
|
56
|
+
*/
|
|
57
|
+
export declare function redactApplyConfigSelectors(config: {
|
|
58
|
+
selectors: string[];
|
|
59
|
+
style: ResolvedRedactStyle;
|
|
60
|
+
}): void;
|
|
61
|
+
//# sourceMappingURL=redactController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactController.d.ts","sourceRoot":"","sources":["../../src/redactController.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,2EAA2E;IAC3E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,6CAA6C;IAC7C,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACnB,CAAA;AAED,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG;IAC5B,oFAAoF;IACpF,WAAW,EAAE,CACX,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,OAAO,EAAE,EACnB,KAAK,EAAE,mBAAmB,KACvB,IAAI,CAAA;IACT,sEAAsE;IACtE,WAAW,EAAE,CACX,EAAE,EAAE,MAAM,EACV,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,mBAAmB,KACvB,IAAI,CAAA;IACT,kDAAkD;IAClD,MAAM,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IAC5B,yBAAyB;IACzB,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB,CAAA;AAED,uEAAuE;AACvE,eAAO,MAAM,mBAAmB,4BAA4B,CAAA;AAE5D,oEAAoE;AACpE,eAAO,MAAM,cAAc,2BAA2B,CAAA;AAEtD,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,sBAAsB,CAAA;AAEpD;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,IAAI,CAiMhD;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE;IACjD,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,KAAK,EAAE,mBAAmB,CAAA;CAC3B,GAAG,IAAI,CA0BP"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-page redaction controller.
|
|
3
|
+
*
|
|
4
|
+
* screenci records the live browser page, so the only way to keep a secret out
|
|
5
|
+
* of `recording.mp4` (and therefore out of the upload) is to mask it in the DOM
|
|
6
|
+
* before Chrome captures the frame. This module is the browser-side half: a
|
|
7
|
+
* self-contained controller installed via `addInitScript` that covers target
|
|
8
|
+
* elements with an opaque panel (by default tinted to match the surface beneath
|
|
9
|
+
* them), tracking their position every animation frame so the mask follows
|
|
10
|
+
* scroll, layout, and animation.
|
|
11
|
+
*
|
|
12
|
+
* The functions here are serialized and shipped to the page by Playwright, so
|
|
13
|
+
* they must not reference anything outside their own bodies.
|
|
14
|
+
*/
|
|
15
|
+
/** Attribute stamped on elements masked by a runtime `redact` call. */
|
|
16
|
+
export const REDACT_ID_ATTRIBUTE = 'data-screenci-redact-id';
|
|
17
|
+
/** Id of the fixed-position container that holds every tint div. */
|
|
18
|
+
export const REDACT_ROOT_ID = '__screenci_redact_root';
|
|
19
|
+
/** Name of the window global the controller installs. */
|
|
20
|
+
export const REDACT_WINDOW_KEY = '__screenci_redact';
|
|
21
|
+
/**
|
|
22
|
+
* The controller bootstrap. Runs in the page (via `addInitScript`) on every
|
|
23
|
+
* document. Idempotent: a second run is a no-op, so it is safe to inject more
|
|
24
|
+
* than once.
|
|
25
|
+
*/
|
|
26
|
+
export function redactControllerBootstrap() {
|
|
27
|
+
const KEY = '__screenci_redact';
|
|
28
|
+
const ROOT_ID = '__screenci_redact_root';
|
|
29
|
+
const win = window;
|
|
30
|
+
if (win[KEY])
|
|
31
|
+
return;
|
|
32
|
+
const entries = new Map();
|
|
33
|
+
let root = null;
|
|
34
|
+
let rafId = null;
|
|
35
|
+
function ensureRoot() {
|
|
36
|
+
if (root && root.isConnected)
|
|
37
|
+
return root;
|
|
38
|
+
// At document-start (init script) the parent may not exist yet; the rAF
|
|
39
|
+
// loop retries next frame rather than throwing and dying.
|
|
40
|
+
const parent = document.body ?? document.documentElement;
|
|
41
|
+
if (!parent)
|
|
42
|
+
return null;
|
|
43
|
+
const existing = document.getElementById(ROOT_ID);
|
|
44
|
+
if (existing) {
|
|
45
|
+
root = existing;
|
|
46
|
+
return root;
|
|
47
|
+
}
|
|
48
|
+
const el = document.createElement('div');
|
|
49
|
+
el.id = ROOT_ID;
|
|
50
|
+
el.setAttribute('aria-hidden', 'true');
|
|
51
|
+
el.style.cssText =
|
|
52
|
+
'position:fixed;top:0;left:0;width:0;height:0;margin:0;padding:0;' +
|
|
53
|
+
'border:0;pointer-events:none;z-index:2147483647;';
|
|
54
|
+
// Append to documentElement so it survives <body> swaps in SPAs.
|
|
55
|
+
document.documentElement.appendChild(el);
|
|
56
|
+
root = el;
|
|
57
|
+
return el;
|
|
58
|
+
}
|
|
59
|
+
function isTransparentColor(c) {
|
|
60
|
+
return (!c ||
|
|
61
|
+
c === 'transparent' ||
|
|
62
|
+
/rgba?\(\s*0\s*,\s*0\s*,\s*0\s*,\s*0\s*\)/.test(c));
|
|
63
|
+
}
|
|
64
|
+
// Pick an opaque fill color from the surface under the element: its own
|
|
65
|
+
// background, or the nearest ancestor (or the body) that paints one. This
|
|
66
|
+
// gives a clean, uniform panel that blends with the surrounding UI.
|
|
67
|
+
function sampleRegionColor(el) {
|
|
68
|
+
let node = el;
|
|
69
|
+
while (node) {
|
|
70
|
+
const bg = getComputedStyle(node).backgroundColor;
|
|
71
|
+
if (!isTransparentColor(bg))
|
|
72
|
+
return bg;
|
|
73
|
+
node = node.parentElement;
|
|
74
|
+
}
|
|
75
|
+
const bodyBg = document.body
|
|
76
|
+
? getComputedStyle(document.body).backgroundColor
|
|
77
|
+
: '';
|
|
78
|
+
return isTransparentColor(bodyBg) ? '#e5e5e5' : bodyBg;
|
|
79
|
+
}
|
|
80
|
+
function styleMask(div, style, autoColor) {
|
|
81
|
+
// Opaque panel: a fixed color, or one sampled to match what is underneath.
|
|
82
|
+
div.style.background = style.color ?? autoColor ?? '#e5e5e5';
|
|
83
|
+
div.style.borderRadius = `${style.radiusPx}px`;
|
|
84
|
+
div.style.boxShadow = style.shadow ?? 'none';
|
|
85
|
+
// Custom styling, applied last so it can override the defaults.
|
|
86
|
+
if (style.css)
|
|
87
|
+
div.style.cssText += ';' + style.css;
|
|
88
|
+
// Keep the panel non-interactive and absolutely positioned regardless of
|
|
89
|
+
// any custom CSS.
|
|
90
|
+
div.style.position = 'absolute';
|
|
91
|
+
div.style.pointerEvents = 'none';
|
|
92
|
+
}
|
|
93
|
+
function resolveElements(entry) {
|
|
94
|
+
if (entry.selector !== null) {
|
|
95
|
+
return Array.from(document.querySelectorAll(entry.selector));
|
|
96
|
+
}
|
|
97
|
+
return (entry.elements ?? []).filter((el) => el.isConnected);
|
|
98
|
+
}
|
|
99
|
+
function positionFrame() {
|
|
100
|
+
const container = ensureRoot();
|
|
101
|
+
if (!container) {
|
|
102
|
+
// DOM not ready yet; try again next frame while masks are pending.
|
|
103
|
+
rafId = entries.size > 0 ? requestAnimationFrame(positionFrame) : null;
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
for (const entry of entries.values()) {
|
|
107
|
+
const seen = new Set();
|
|
108
|
+
for (const el of resolveElements(entry)) {
|
|
109
|
+
const rect = el.getBoundingClientRect();
|
|
110
|
+
if (rect.width <= 0 || rect.height <= 0)
|
|
111
|
+
continue;
|
|
112
|
+
seen.add(el);
|
|
113
|
+
let div = entry.masks.get(el);
|
|
114
|
+
if (!div) {
|
|
115
|
+
div = document.createElement('div');
|
|
116
|
+
div.style.cssText =
|
|
117
|
+
'position:absolute;top:0;left:0;pointer-events:none;will-change:transform;';
|
|
118
|
+
const autoColor = entry.style.color === null ? sampleRegionColor(el) : null;
|
|
119
|
+
styleMask(div, entry.style, autoColor);
|
|
120
|
+
container.appendChild(div);
|
|
121
|
+
entry.masks.set(el, div);
|
|
122
|
+
}
|
|
123
|
+
// Inflate just enough that the rounded corners never expose the
|
|
124
|
+
// element's square corners underneath. A rounded rect of radius r covers
|
|
125
|
+
// an inner corner once it extends about 0.29*r beyond it, so this stays
|
|
126
|
+
// tight to the element rather than looking oversized.
|
|
127
|
+
const pad = Math.ceil(entry.style.radiusPx * 0.3);
|
|
128
|
+
div.style.transform = `translate(${rect.left - pad}px, ${rect.top - pad}px)`;
|
|
129
|
+
div.style.width = `${rect.width + pad * 2}px`;
|
|
130
|
+
div.style.height = `${rect.height + pad * 2}px`;
|
|
131
|
+
div.style.display = 'block';
|
|
132
|
+
}
|
|
133
|
+
// Tear down masks for elements that vanished this frame.
|
|
134
|
+
for (const [el, div] of entry.masks) {
|
|
135
|
+
if (seen.has(el))
|
|
136
|
+
continue;
|
|
137
|
+
div.remove();
|
|
138
|
+
entry.masks.delete(el);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
rafId = entries.size > 0 ? requestAnimationFrame(positionFrame) : null;
|
|
142
|
+
}
|
|
143
|
+
function startLoop() {
|
|
144
|
+
if (rafId !== null)
|
|
145
|
+
return;
|
|
146
|
+
// Run one synchronous frame so a freshly added mask covers its element
|
|
147
|
+
// before this call returns (no unmasked frame escapes to the recording),
|
|
148
|
+
// then keep tracking on rAF.
|
|
149
|
+
positionFrame();
|
|
150
|
+
}
|
|
151
|
+
function teardownEntry(entry) {
|
|
152
|
+
for (const [el, div] of entry.masks) {
|
|
153
|
+
div.remove();
|
|
154
|
+
entry.masks.delete(el);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
const api = {
|
|
158
|
+
addElements(id, elements, style) {
|
|
159
|
+
const entry = {
|
|
160
|
+
style: style,
|
|
161
|
+
selector: null,
|
|
162
|
+
elements,
|
|
163
|
+
masks: new Map(),
|
|
164
|
+
};
|
|
165
|
+
const prev = entries.get(id);
|
|
166
|
+
if (prev)
|
|
167
|
+
teardownEntry(prev);
|
|
168
|
+
entries.set(id, entry);
|
|
169
|
+
startLoop();
|
|
170
|
+
},
|
|
171
|
+
addSelector(id, selector, style) {
|
|
172
|
+
const entry = {
|
|
173
|
+
style: style,
|
|
174
|
+
selector,
|
|
175
|
+
elements: null,
|
|
176
|
+
masks: new Map(),
|
|
177
|
+
};
|
|
178
|
+
const prev = entries.get(id);
|
|
179
|
+
if (prev)
|
|
180
|
+
teardownEntry(prev);
|
|
181
|
+
entries.set(id, entry);
|
|
182
|
+
startLoop();
|
|
183
|
+
},
|
|
184
|
+
remove(id) {
|
|
185
|
+
const entry = entries.get(id);
|
|
186
|
+
if (!entry)
|
|
187
|
+
return;
|
|
188
|
+
teardownEntry(entry);
|
|
189
|
+
entries.delete(id);
|
|
190
|
+
},
|
|
191
|
+
clear() {
|
|
192
|
+
for (const entry of entries.values())
|
|
193
|
+
teardownEntry(entry);
|
|
194
|
+
entries.clear();
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
win[KEY] = api;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Init script that masks the configured first-paint selectors. Registered after
|
|
201
|
+
* the bootstrap so `window.__screenci_redact` already exists. A CSS rule hides
|
|
202
|
+
* matches the instant they paint (race free, no JS timing), and the controller
|
|
203
|
+
* lays the opaque panel over them a frame later.
|
|
204
|
+
*/
|
|
205
|
+
export function redactApplyConfigSelectors(config) {
|
|
206
|
+
const KEY = '__screenci_redact';
|
|
207
|
+
const api = window[KEY];
|
|
208
|
+
if (!api || config.selectors.length === 0)
|
|
209
|
+
return;
|
|
210
|
+
// Hide matches the instant they paint, before the JS panel can position
|
|
211
|
+
// itself, so the secret never flashes on the first frame. `visibility:hidden`
|
|
212
|
+
// keeps the layout box so the panel is sized correctly, and it fails closed if
|
|
213
|
+
// the controller never runs.
|
|
214
|
+
const css = config.selectors
|
|
215
|
+
.map((sel) => `${sel}{visibility:hidden !important;}`)
|
|
216
|
+
.join('');
|
|
217
|
+
const style = document.createElement('style');
|
|
218
|
+
style.setAttribute('data-screenci-redact', 'config');
|
|
219
|
+
style.textContent = css;
|
|
220
|
+
const attach = () => {
|
|
221
|
+
;
|
|
222
|
+
(document.head ?? document.documentElement).appendChild(style);
|
|
223
|
+
};
|
|
224
|
+
if (document.head)
|
|
225
|
+
attach();
|
|
226
|
+
else
|
|
227
|
+
document.addEventListener('DOMContentLoaded', attach, { once: true });
|
|
228
|
+
config.selectors.forEach((selector, index) => {
|
|
229
|
+
api.addSelector(`__screenci_config_${index}`, selector, config.style);
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=redactController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redactController.js","sourceRoot":"","sources":["../../src/redactController.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import type { Page } from '@playwright/test';
|
|
|
2
2
|
import { type IEventRecorder, type ElementRect } from './events.js';
|
|
3
3
|
import type { AutoZoomOptions, RecordOptions, RenderOptions } from './types.js';
|
|
4
4
|
import type { ScreenshotCropRecord } from './crop.js';
|
|
5
|
+
import type { ResolvedRedactStyle } from './redactController.js';
|
|
5
6
|
export type CurrentZoomViewport = {
|
|
6
7
|
focusPoint: {
|
|
7
8
|
x: number;
|
|
@@ -32,6 +33,13 @@ export type AutoZoomState = {
|
|
|
32
33
|
mode: 'idle' | 'auto' | 'manual';
|
|
33
34
|
options: AutoZoomOptions;
|
|
34
35
|
currentZoomViewport: CurrentZoomViewport | null;
|
|
36
|
+
/**
|
|
37
|
+
* Vertical framing bias (0–1) applied when a plain interaction scrolls its
|
|
38
|
+
* target into view without zooming. Populated once at record init from
|
|
39
|
+
* `recordOptions.scrollCentering` (default `DEFAULT_SCROLL_CENTERING`). An
|
|
40
|
+
* explicit per-call `centering` still overrides this.
|
|
41
|
+
*/
|
|
42
|
+
scrollCentering: number;
|
|
35
43
|
};
|
|
36
44
|
export type ActiveCueRun = {
|
|
37
45
|
finished: Promise<void>;
|
|
@@ -113,6 +121,26 @@ export type ScreenCIRuntimeContext = {
|
|
|
113
121
|
activeRuns: Map<string, ActiveAudioRun>;
|
|
114
122
|
};
|
|
115
123
|
autoZoom: AutoZoomState;
|
|
124
|
+
/**
|
|
125
|
+
* Tracks the recording's current display size (0-1 fraction of the frame; 1 =
|
|
126
|
+
* full screen) as toggled by `resizeRecording()`/`resetRecordingSize()`. Used
|
|
127
|
+
* to no-op a reset that is already at full screen and to detect redundant
|
|
128
|
+
* resizes. Resets to 1 with each fresh recording context.
|
|
129
|
+
*/
|
|
130
|
+
recordingSize: {
|
|
131
|
+
current: number;
|
|
132
|
+
};
|
|
133
|
+
/**
|
|
134
|
+
* Client-side redaction state. `controllerInstalled` guards the one-time
|
|
135
|
+
* `addInitScript` install of the in-page mask controller; `activeMasks` maps
|
|
136
|
+
* each live mask id to its resolved style so masks can be cleaned up at the
|
|
137
|
+
* end of a recording. Redaction is purely client side, so nothing here is
|
|
138
|
+
* serialized to `data.json`.
|
|
139
|
+
*/
|
|
140
|
+
redact: {
|
|
141
|
+
controllerInstalled: boolean;
|
|
142
|
+
activeMasks: Map<string, ResolvedRedactStyle>;
|
|
143
|
+
};
|
|
116
144
|
};
|
|
117
145
|
export declare function createScreenCIRuntimeContext(overrides?: {
|
|
118
146
|
recorder?: IEventRecorder | null;
|
|
@@ -154,6 +182,8 @@ export declare function getRuntimeCrop(): ScreenshotCropRecord | undefined;
|
|
|
154
182
|
export declare function resetCueRuntimeState(): void;
|
|
155
183
|
export declare function resetAssetRuntimeState(): void;
|
|
156
184
|
export declare function resetAudioRuntimeState(): void;
|
|
185
|
+
export declare function getRuntimeRedactState(): ScreenCIRuntimeContext['redact'];
|
|
186
|
+
export declare function resetRedactRuntimeState(): void;
|
|
157
187
|
export declare function pushRuntimeTimelineBlock(block: TimelineBlockState): void;
|
|
158
188
|
export declare function popRuntimeTimelineBlock(expectedType: TimelineBlockType): TimelineBlockState;
|
|
159
189
|
export declare function hasRuntimeTimelineBlock(type: TimelineBlockType): boolean;
|
|
@@ -161,4 +191,6 @@ export declare function getRuntimeTimelineBlocks(): TimelineBlockState[];
|
|
|
161
191
|
export declare function isRuntimeInsideHide(): boolean;
|
|
162
192
|
export declare function getRuntimeAutoZoomState(): AutoZoomState;
|
|
163
193
|
export declare function setRuntimeAutoZoomState(state: AutoZoomState): void;
|
|
194
|
+
export declare function getRuntimeRecordingSize(): number;
|
|
195
|
+
export declare function setRuntimeRecordingSize(size: number): void;
|
|
164
196
|
//# sourceMappingURL=runtimeContext.d.ts.map
|