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,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":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,mBAAmB,EAAuB,MAAM,qBAAqB,CAAA;AAG9E;;;;GAIG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,IAAiB;IAC3D,MAAM,EAAE,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;IACpC,MAAM,MAAM,GAAG,CAAC,CAAU,EAAe,EAAE,CACzC,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;IAC7C,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;QAClE,MAAM,IAAI,KAAK,CACb,cAAc,KAAK,qFAAqF,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAC/H,CAAA;IACH,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;QACnB,MAAM,IAAI,KAAK,CAAC,cAAc,KAAK,6BAA6B,CAAC,CAAA;IACnE,CAAC;IACD,IAAI,KAAK,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,cAAc,KAAK,qCAAqC,CAAC,CAAA;IAC3E,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED,gFAAgF;AAChF,SAAS,oBAAoB,CAC3B,KAAqB,EACrB,KAAa;IAEb,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CACb,cAAc,KAAK,8DAA8D,OAAO,KAAK,GAAG,CACjG,CAAA;IACH,CAAC;IACD,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAA;IACzC,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,QAAQ,IAAI,CAAC,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CACb,cAAc,KAAK,oEAAoE,KAAK,IAAI,CACjG,CAAA;QACH,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAA;IACrC,CAAC;IACD,IAAI,MAAM,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,cAAc,KAAK,6BAA6B,KAAK,IAAI,CAAC,CAAA;IAC5E,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,CAAA;AAC1B,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,KAAiC,EACjC,GAA+B;IAE/B,MAAM,WAAW,GACf,KAAK,KAAK,SAAS;QACjB,CAAC,CAAC,oBAAoB,CAAC,KAAK,EAAE,GAAG,KAAK,QAAQ,CAAC;QAC/C,CAAC,CAAC,SAAS,CAAA;IACf,MAAM,SAAS,GACb,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,oBAAoB,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAE3E,IAAI,WAAW,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QACzD,IACE,IAAI,IAAI,WAAW;YACnB,IAAI,IAAI,SAAS;YACjB,WAAW,CAAC,EAAE,IAAI,SAAS,CAAC,EAAE,EAC9B,CAAC;YACD,MAAM,IAAI,KAAK,CACb,cAAc,KAAK,wCAAwC,KAAK,SAAS,GAAG,IAAI,CACjF,CAAA;QACH,CAAC;QACD,IACE,SAAS,IAAI,WAAW;YACxB,SAAS,IAAI,SAAS;YACtB,WAAW,CAAC,OAAO,IAAI,SAAS,CAAC,OAAO,EACxC,CAAC;YACD,MAAM,IAAI,KAAK,CACb,cAAc,KAAK,wCAAwC,KAAK,SAAS,GAAG,IAAI,CACjF,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,CAAC;QACjD,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,EAAE,SAAS,EAAE,CAAC;KAC9C,CAAA;AACH,CAAC"}
|
package/dist/src/studio.d.ts
CHANGED
|
@@ -1,10 +1,78 @@
|
|
|
1
|
+
import type { ContentMajor, LanguageMajor } from './declare.js';
|
|
1
2
|
/**
|
|
2
|
-
* The studio
|
|
3
|
-
*
|
|
4
|
-
* sentinel;
|
|
3
|
+
* The studio axes deferred to the ScreenCI web app, read by the recorder to
|
|
4
|
+
* stamp `metadata.studio`. `renderOptions`/`recordOptions` come from the
|
|
5
|
+
* matching `'studio'` sentinel; `languages` is set when the recording's
|
|
6
|
+
* language set is web-owned (`video.languages(studio())`).
|
|
5
7
|
*/
|
|
6
8
|
export type StudioOptionFlags = {
|
|
7
9
|
renderOptions: boolean;
|
|
8
10
|
recordOptions: boolean;
|
|
11
|
+
/** Whether the language set is owned by the web app (`video.languages(studio())`). */
|
|
12
|
+
languages?: boolean;
|
|
13
|
+
};
|
|
14
|
+
/** Brand marking a value produced by {@link studio}. */
|
|
15
|
+
export declare const STUDIO: unique symbol;
|
|
16
|
+
/**
|
|
17
|
+
* A Studio-owned declaration produced by {@link studio}: the content (or, for
|
|
18
|
+
* `languages`, the set) is owned by the ScreenCI web app. `names` lists the
|
|
19
|
+
* declared keys (cue / overlay / audio names, or language codes); `seed`, when
|
|
20
|
+
* present, supplies initial values the web app starts from but may override.
|
|
21
|
+
*
|
|
22
|
+
* `S` carries the seed shape so the consuming feature method validates seed
|
|
23
|
+
* values against its own value type (e.g. narration seeds are checked as
|
|
24
|
+
* narration cues). A blank declaration (`studio([...])`) has `seed` undefined.
|
|
25
|
+
*/
|
|
26
|
+
export type StudioMarker<S = unknown> = {
|
|
27
|
+
readonly [STUDIO]: true;
|
|
28
|
+
readonly names: readonly string[];
|
|
29
|
+
readonly seed?: S;
|
|
30
|
+
};
|
|
31
|
+
/** A blank Studio declaration: names only, no seed. */
|
|
32
|
+
export type StudioNames = StudioMarker<undefined> & {
|
|
33
|
+
readonly seed?: undefined;
|
|
34
|
+
/** Phantom guard: keeps the keyless form ({@link StudioPending}) out of feature args. */
|
|
35
|
+
readonly languagesOnly?: never;
|
|
36
|
+
};
|
|
37
|
+
/** A seeded Studio declaration: the web app starts from `seed` but may override it. */
|
|
38
|
+
export type StudioSeeded<S> = StudioMarker<S> & {
|
|
39
|
+
readonly seed: S;
|
|
40
|
+
readonly languagesOnly?: never;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* The keyless form, `studio()`: the web app owns the entire set. Only
|
|
44
|
+
* `video.languages(studio())` accepts it (a content feature has no keys to
|
|
45
|
+
* defer, so its keyless form is rejected by {@link FeatureArg}). The
|
|
46
|
+
* `languagesOnly` brand is what makes it unassignable to {@link StudioNames}.
|
|
47
|
+
*/
|
|
48
|
+
export type StudioPending = StudioMarker<undefined> & {
|
|
49
|
+
readonly names: readonly [];
|
|
50
|
+
readonly seed?: undefined;
|
|
51
|
+
readonly languagesOnly: true;
|
|
52
|
+
};
|
|
53
|
+
/** Whether a value was produced by {@link studio}. */
|
|
54
|
+
export declare function isStudioMarker(value: unknown): value is StudioMarker;
|
|
55
|
+
/**
|
|
56
|
+
* Declare something Studio-owned: its content is configured in the ScreenCI web
|
|
57
|
+
* app, and code only declares that it exists. Three forms:
|
|
58
|
+
*
|
|
59
|
+
* - `studio(['intro', 'cta'])`: blank names. The keys exist in code (so the test
|
|
60
|
+
* body can reference `narration.intro`), but the web app owns their content.
|
|
61
|
+
* - `studio({ intro: 'Welcome' })`: seeded. The web app starts from these values
|
|
62
|
+
* but owns them: once edited in Studio, the Studio value wins (a seed never
|
|
63
|
+
* clobbers an existing Studio edit).
|
|
64
|
+
* - `studio()`: keyless. Only `video.languages(studio())` accepts it: the web app
|
|
65
|
+
* owns the entire language set. Content features have keys to declare, so their
|
|
66
|
+
* keyless form does not type-check.
|
|
67
|
+
*
|
|
68
|
+
* For `video.languages`, the names are language codes: `studio(['en', 'fi'])`
|
|
69
|
+
* seeds the initial set (web-owned, but starts with those languages).
|
|
70
|
+
*/
|
|
71
|
+
export declare function studio(): StudioPending;
|
|
72
|
+
export declare function studio<const N extends readonly string[]>(names: N): StudioNames & {
|
|
73
|
+
readonly names: N;
|
|
74
|
+
};
|
|
75
|
+
export declare function studio<const S extends ContentMajor<unknown> | LanguageMajor<unknown>>(seed: S): StudioSeeded<S> & {
|
|
76
|
+
readonly names: readonly (keyof S & string)[];
|
|
9
77
|
};
|
|
10
78
|
//# sourceMappingURL=studio.d.ts.map
|
package/dist/src/studio.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../../src/studio.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"studio.d.ts","sourceRoot":"","sources":["../../src/studio.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE/D;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,aAAa,EAAE,OAAO,CAAA;IACtB,aAAa,EAAE,OAAO,CAAA;IACtB,sFAAsF;IACtF,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED,wDAAwD;AACxD,eAAO,MAAM,MAAM,EAAE,OAAO,MAAsC,CAAA;AAElE;;;;;;;;;GASG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,OAAO,IAAI;IACtC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,CAAA;IACvB,QAAQ,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,CAAA;IACjC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;CAClB,CAAA;AAED,uDAAuD;AACvD,MAAM,MAAM,WAAW,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG;IAClD,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,yFAAyF;IACzF,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAA;CAC/B,CAAA;AAED,uFAAuF;AACvF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,YAAY,CAAC,CAAC,CAAC,GAAG;IAC9C,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAA;IAChB,QAAQ,CAAC,aAAa,CAAC,EAAE,KAAK,CAAA;CAC/B,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,YAAY,CAAC,SAAS,CAAC,GAAG;IACpD,QAAQ,CAAC,KAAK,EAAE,SAAS,EAAE,CAAA;IAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;IACzB,QAAQ,CAAC,aAAa,EAAE,IAAI,CAAA;CAC7B,CAAA;AAED,sDAAsD;AACtD,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,YAAY,CAMpE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,IAAI,aAAa,CAAA;AACvC,wBAAgB,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,SAAS,MAAM,EAAE,EACtD,KAAK,EAAE,CAAC,GACP,WAAW,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAA;CAAE,CAAA;AACtC,wBAAgB,MAAM,CACpB,KAAK,CAAC,CAAC,SAAS,YAAY,CAAC,OAAO,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,EAC9D,IAAI,EAAE,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,GAAG;IAAE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,EAAE,CAAA;CAAE,CAAA"}
|
package/dist/src/studio.js
CHANGED
|
@@ -1,2 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
/** Brand marking a value produced by {@link studio}. */
|
|
2
|
+
export const STUDIO = Symbol.for('screenci.studio');
|
|
3
|
+
/** Whether a value was produced by {@link studio}. */
|
|
4
|
+
export function isStudioMarker(value) {
|
|
5
|
+
return (typeof value === 'object' &&
|
|
6
|
+
value !== null &&
|
|
7
|
+
value[STUDIO] === true);
|
|
8
|
+
}
|
|
9
|
+
export function studio(arg) {
|
|
10
|
+
if (arg === undefined)
|
|
11
|
+
return { [STUDIO]: true, names: [] };
|
|
12
|
+
if (Array.isArray(arg))
|
|
13
|
+
return { [STUDIO]: true, names: [...arg] };
|
|
14
|
+
return {
|
|
15
|
+
[STUDIO]: true,
|
|
16
|
+
names: Object.keys(arg),
|
|
17
|
+
seed: arg,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
2
20
|
//# sourceMappingURL=studio.js.map
|
package/dist/src/studio.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"studio.js","sourceRoot":"","sources":["../../src/studio.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"studio.js","sourceRoot":"","sources":["../../src/studio.ts"],"names":[],"mappings":"AAeA,wDAAwD;AACxD,MAAM,CAAC,MAAM,MAAM,GAAkB,MAAM,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;AA2ClE,sDAAsD;AACtD,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,OAAO,CACL,OAAO,KAAK,KAAK,QAAQ;QACzB,KAAK,KAAK,IAAI;QACb,KAAsC,CAAC,MAAM,CAAC,KAAK,IAAI,CACzD,CAAA;AACH,CAAC;AAyBD,MAAM,UAAU,MAAM,CACpB,GAAiD;IAEjD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IAC3D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,GAAG,GAAG,CAAC,EAAE,CAAA;IAClE,OAAO;QACL,CAAC,MAAM,CAAC,EAAE,IAAI;QACd,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;QACvB,IAAI,EAAE,GAA4B;KACnC,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing for the string position arguments accepted by the narration and
|
|
3
|
+
* overlay controllers, e.g. `narration.intro('0:02')` or `overlays.tip('56%')`.
|
|
4
|
+
*
|
|
5
|
+
* A controller called with a NUMBER keeps its existing meaning (a relative
|
|
6
|
+
* duration in milliseconds). A controller called with a STRING means an absolute
|
|
7
|
+
* position in the final video: "this cue/overlay window should reach this point."
|
|
8
|
+
* Only strings flow through this module; numbers never do.
|
|
9
|
+
*
|
|
10
|
+
* Absolute positions are resolved against the finished render, so a percentage
|
|
11
|
+
* cannot be turned into milliseconds here (the total length is not known until
|
|
12
|
+
* render time). The parser therefore returns a discriminated union: concrete
|
|
13
|
+
* `'<n>s'`/timecode forms resolve to milliseconds, while `'<n>%'` surfaces a
|
|
14
|
+
* fraction the renderer multiplies by the final total.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* A position within the final video. Strings only (numbers are the existing
|
|
18
|
+
* duration path on each controller). Accepts:
|
|
19
|
+
* - `'<n>s'` seconds, e.g. `'2s'` or `'5.51s'`
|
|
20
|
+
* - `'m:ss(.f)'` timecode minutes:seconds, e.g. `'0:05.51'` (= 5.51s)
|
|
21
|
+
* - `'h:mm:ss(.f)'` timecode hours:minutes:seconds, e.g. `'1:02:03.5'`
|
|
22
|
+
* - `'<n>%'` percentage of the total video, e.g. `'56.1%'`
|
|
23
|
+
*
|
|
24
|
+
* The template-literal members give editor hints for the common forms; the
|
|
25
|
+
* trailing `string` keeps timecodes (which the type system cannot express)
|
|
26
|
+
* assignable. Validation happens at runtime in {@link parseTimelineOffset}.
|
|
27
|
+
*/
|
|
28
|
+
export type TimelineOffset = `${number}s` | `${number}%` | string;
|
|
29
|
+
/** Result of parsing a {@link TimelineOffset} string. */
|
|
30
|
+
export type ParsedTimelineOffset =
|
|
31
|
+
/** From `'<n>s'` or a timecode: a concrete absolute position in milliseconds. */
|
|
32
|
+
{
|
|
33
|
+
kind: 'absolute';
|
|
34
|
+
ms: number;
|
|
35
|
+
}
|
|
36
|
+
/** From `'<n>%'`: a fraction (0.561 for `'56.1%'`), resolved against the total at render. */
|
|
37
|
+
| {
|
|
38
|
+
kind: 'percent';
|
|
39
|
+
fraction: number;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Parses a string position into a {@link ParsedTimelineOffset}. Throws on any
|
|
43
|
+
* unrecognized or malformed input. Pure: no I/O, no globals.
|
|
44
|
+
*/
|
|
45
|
+
export declare function parseTimelineOffset(value: string): ParsedTimelineOffset;
|
|
46
|
+
//# sourceMappingURL=timelineOffset.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timelineOffset.d.ts","sourceRoot":"","sources":["../../src/timelineOffset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,MAAM,GAAG,GAAG,MAAM,CAAA;AAEjE,yDAAyD;AACzD,MAAM,MAAM,oBAAoB;AAC9B,iFAAiF;AAC/E;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AAClC,6FAA6F;GAC3F;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAA;AAmBzC;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,CAqCvE"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parsing for the string position arguments accepted by the narration and
|
|
3
|
+
* overlay controllers, e.g. `narration.intro('0:02')` or `overlays.tip('56%')`.
|
|
4
|
+
*
|
|
5
|
+
* A controller called with a NUMBER keeps its existing meaning (a relative
|
|
6
|
+
* duration in milliseconds). A controller called with a STRING means an absolute
|
|
7
|
+
* position in the final video: "this cue/overlay window should reach this point."
|
|
8
|
+
* Only strings flow through this module; numbers never do.
|
|
9
|
+
*
|
|
10
|
+
* Absolute positions are resolved against the finished render, so a percentage
|
|
11
|
+
* cannot be turned into milliseconds here (the total length is not known until
|
|
12
|
+
* render time). The parser therefore returns a discriminated union: concrete
|
|
13
|
+
* `'<n>s'`/timecode forms resolve to milliseconds, while `'<n>%'` surfaces a
|
|
14
|
+
* fraction the renderer multiplies by the final total.
|
|
15
|
+
*/
|
|
16
|
+
const SECONDS_RE = /^(\d+(?:\.\d+)?)s$/;
|
|
17
|
+
const PERCENT_RE = /^(\d+(?:\.\d+)?)%$/;
|
|
18
|
+
const TIMECODE_MIN_RE = /^(\d+):(\d{2}(?:\.\d+)?)$/;
|
|
19
|
+
const TIMECODE_HOUR_RE = /^(\d+):(\d{2}):(\d{2}(?:\.\d+)?)$/;
|
|
20
|
+
function invalidTimecode(value) {
|
|
21
|
+
throw new Error(`invalid timecode '${value}'; expected m:ss(.f) or h:mm:ss(.f) with mm/ss < 60`);
|
|
22
|
+
}
|
|
23
|
+
function invalidPosition(value) {
|
|
24
|
+
throw new Error(`invalid position '${value}'; expected '<n>s', a timecode 'm:ss(.f)'/'h:mm:ss(.f)', or '<n>%'`);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Parses a string position into a {@link ParsedTimelineOffset}. Throws on any
|
|
28
|
+
* unrecognized or malformed input. Pure: no I/O, no globals.
|
|
29
|
+
*/
|
|
30
|
+
export function parseTimelineOffset(value) {
|
|
31
|
+
const trimmed = value.trim();
|
|
32
|
+
if (trimmed.length === 0)
|
|
33
|
+
invalidPosition(value);
|
|
34
|
+
const percent = PERCENT_RE.exec(trimmed);
|
|
35
|
+
if (percent !== null) {
|
|
36
|
+
return { kind: 'percent', fraction: Number(percent[1]) / 100 };
|
|
37
|
+
}
|
|
38
|
+
const seconds = SECONDS_RE.exec(trimmed);
|
|
39
|
+
if (seconds !== null) {
|
|
40
|
+
return { kind: 'absolute', ms: Number(seconds[1]) * 1000 };
|
|
41
|
+
}
|
|
42
|
+
const hour = TIMECODE_HOUR_RE.exec(trimmed);
|
|
43
|
+
if (hour !== null) {
|
|
44
|
+
const hours = Number(hour[1]);
|
|
45
|
+
const minutes = Number(hour[2]);
|
|
46
|
+
const secs = Number(hour[3]);
|
|
47
|
+
if (minutes >= 60 || secs >= 60)
|
|
48
|
+
invalidTimecode(value);
|
|
49
|
+
return { kind: 'absolute', ms: (hours * 3600 + minutes * 60 + secs) * 1000 };
|
|
50
|
+
}
|
|
51
|
+
const min = TIMECODE_MIN_RE.exec(trimmed);
|
|
52
|
+
if (min !== null) {
|
|
53
|
+
const minutes = Number(min[1]);
|
|
54
|
+
const secs = Number(min[2]);
|
|
55
|
+
if (secs >= 60)
|
|
56
|
+
invalidTimecode(value);
|
|
57
|
+
return { kind: 'absolute', ms: (minutes * 60 + secs) * 1000 };
|
|
58
|
+
}
|
|
59
|
+
// A bare `h:mm:ss`-shaped string that failed the strict patterns above (for
|
|
60
|
+
// example two-digit seconds were not supplied) is reported as a timecode error
|
|
61
|
+
// so the message points at the right fix.
|
|
62
|
+
if (/^\d+:\d/.test(trimmed))
|
|
63
|
+
invalidTimecode(value);
|
|
64
|
+
invalidPosition(value);
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=timelineOffset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timelineOffset.js","sourceRoot":"","sources":["../../src/timelineOffset.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAuBH,MAAM,UAAU,GAAG,oBAAoB,CAAA;AACvC,MAAM,UAAU,GAAG,oBAAoB,CAAA;AACvC,MAAM,eAAe,GAAG,2BAA2B,CAAA;AACnD,MAAM,gBAAgB,GAAG,mCAAmC,CAAA;AAE5D,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,IAAI,KAAK,CACb,qBAAqB,KAAK,qDAAqD,CAChF,CAAA;AACH,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,MAAM,IAAI,KAAK,CACb,qBAAqB,KAAK,oEAAoE,CAC/F,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,eAAe,CAAC,KAAK,CAAC,CAAA;IAEhD,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACxC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,EAAE,CAAA;IAChE,CAAC;IAED,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACxC,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IAC5D,CAAC;IAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IAC3C,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAC7B,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAC/B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAA;QAC5B,IAAI,OAAO,IAAI,EAAE,IAAI,IAAI,IAAI,EAAE;YAAE,eAAe,CAAC,KAAK,CAAC,CAAA;QACvD,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,KAAK,GAAG,IAAI,GAAG,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,CAAA;IAC9E,CAAC;IAED,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACzC,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;QAC3B,IAAI,IAAI,IAAI,EAAE;YAAE,eAAe,CAAC,KAAK,CAAC,CAAA;QACtC,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,CAAA;IAC/D,CAAC;IAED,4EAA4E;IAC5E,+EAA+E;IAC/E,0CAA0C;IAC1C,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,eAAe,CAAC,KAAK,CAAC,CAAA;IAEnD,eAAe,CAAC,KAAK,CAAC,CAAA;AACxB,CAAC"}
|
|
@@ -1,3 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A render dependency declared via `selected(name)`: the `from` render embeds the
|
|
3
|
+
* `to` render's output as an overlay. Used for best-effort registration-time
|
|
4
|
+
* warnings; the authoritative existence/one-level/medium checks run on the
|
|
5
|
+
* backend at upload (it sees every file and language).
|
|
6
|
+
*/
|
|
7
|
+
export type DependencyReference = {
|
|
8
|
+
/** Name of the dependent video/screenshot (the one declaring `selected`). */
|
|
9
|
+
from: string;
|
|
10
|
+
/** Name referenced via `selected(name)` (the embedded target). */
|
|
11
|
+
to: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Videos and screenshots share a single name namespace within a run (both are
|
|
15
|
+
* rows in the same table, addressed by `selected(name)`). Titles must therefore
|
|
16
|
+
* be unique across BOTH mediums, not just within one. {@link findDuplicateTitles}
|
|
17
|
+
* already enforces this because every video and screenshot title is collected
|
|
18
|
+
* into the same list before checking, so a name reused across mediums surfaces as
|
|
19
|
+
* a duplicate.
|
|
20
|
+
*/
|
|
1
21
|
export declare function findDuplicateTitles(titles: readonly string[]): string[];
|
|
2
22
|
export declare function formatDuplicateTitlesMessage(duplicates: readonly string[]): string;
|
|
23
|
+
/**
|
|
24
|
+
* Returns the dependency references that point at themselves (`from === to`). A
|
|
25
|
+
* render cannot embed its own output, so these are always a mistake.
|
|
26
|
+
*/
|
|
27
|
+
export declare function findSelfReferences(references: readonly DependencyReference[]): DependencyReference[];
|
|
28
|
+
/**
|
|
29
|
+
* Returns the dependency references whose target (`to`) is not among the
|
|
30
|
+
* `knownTitles` discovered in the run. This is a best-effort warning: a target
|
|
31
|
+
* may legitimately live in another file not part of this discovery, so the
|
|
32
|
+
* authoritative existence check runs on the backend at upload.
|
|
33
|
+
*/
|
|
34
|
+
export declare function findMissingReferences(references: readonly DependencyReference[], knownTitles: readonly string[]): DependencyReference[];
|
|
35
|
+
/**
|
|
36
|
+
* Formats best-effort registration-time warnings for self-references and
|
|
37
|
+
* obviously missing dependency targets. Returns an empty string when there is
|
|
38
|
+
* nothing to warn about, so callers can skip emitting anything.
|
|
39
|
+
*/
|
|
40
|
+
export declare function formatDependencyWarnings(selfReferences: readonly DependencyReference[], missingReferences: readonly DependencyReference[]): string;
|
|
3
41
|
//# sourceMappingURL=titleValidation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"titleValidation.d.ts","sourceRoot":"","sources":["../../src/titleValidation.ts"],"names":[],"mappings":"AAAA,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAUvE;AAED,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,MAAM,CAUR"}
|
|
1
|
+
{"version":3,"file":"titleValidation.d.ts","sourceRoot":"","sources":["../../src/titleValidation.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,6EAA6E;IAC7E,IAAI,EAAE,MAAM,CAAA;IACZ,kEAAkE;IAClE,EAAE,EAAE,MAAM,CAAA;CACX,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAUvE;AAED,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,SAAS,MAAM,EAAE,GAC5B,MAAM,CAUR;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,UAAU,EAAE,SAAS,mBAAmB,EAAE,GACzC,mBAAmB,EAAE,CAEvB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,SAAS,mBAAmB,EAAE,EAC1C,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,mBAAmB,EAAE,CAGvB;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,cAAc,EAAE,SAAS,mBAAmB,EAAE,EAC9C,iBAAiB,EAAE,SAAS,mBAAmB,EAAE,GAChD,MAAM,CAcR"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Videos and screenshots share a single name namespace within a run (both are
|
|
3
|
+
* rows in the same table, addressed by `selected(name)`). Titles must therefore
|
|
4
|
+
* be unique across BOTH mediums, not just within one. {@link findDuplicateTitles}
|
|
5
|
+
* already enforces this because every video and screenshot title is collected
|
|
6
|
+
* into the same list before checking, so a name reused across mediums surfaces as
|
|
7
|
+
* a duplicate.
|
|
8
|
+
*/
|
|
1
9
|
export function findDuplicateTitles(titles) {
|
|
2
10
|
const counts = new Map();
|
|
3
11
|
for (const title of titles) {
|
|
@@ -16,4 +24,38 @@ export function formatDuplicateTitlesMessage(duplicates) {
|
|
|
16
24
|
}
|
|
17
25
|
return lines.join('\n');
|
|
18
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Returns the dependency references that point at themselves (`from === to`). A
|
|
29
|
+
* render cannot embed its own output, so these are always a mistake.
|
|
30
|
+
*/
|
|
31
|
+
export function findSelfReferences(references) {
|
|
32
|
+
return references.filter((ref) => ref.from === ref.to);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Returns the dependency references whose target (`to`) is not among the
|
|
36
|
+
* `knownTitles` discovered in the run. This is a best-effort warning: a target
|
|
37
|
+
* may legitimately live in another file not part of this discovery, so the
|
|
38
|
+
* authoritative existence check runs on the backend at upload.
|
|
39
|
+
*/
|
|
40
|
+
export function findMissingReferences(references, knownTitles) {
|
|
41
|
+
const known = new Set(knownTitles);
|
|
42
|
+
return references.filter((ref) => !known.has(ref.to));
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Formats best-effort registration-time warnings for self-references and
|
|
46
|
+
* obviously missing dependency targets. Returns an empty string when there is
|
|
47
|
+
* nothing to warn about, so callers can skip emitting anything.
|
|
48
|
+
*/
|
|
49
|
+
export function formatDependencyWarnings(selfReferences, missingReferences) {
|
|
50
|
+
const lines = [];
|
|
51
|
+
for (const ref of selfReferences) {
|
|
52
|
+
lines.push(` - "${ref.from}" uses selected("${ref.to}"), which references itself. A render cannot embed its own output.`);
|
|
53
|
+
}
|
|
54
|
+
for (const ref of missingReferences) {
|
|
55
|
+
lines.push(` - "${ref.from}" uses selected("${ref.to}"), but no video or screenshot named "${ref.to}" was found in this run.`);
|
|
56
|
+
}
|
|
57
|
+
if (lines.length === 0)
|
|
58
|
+
return '';
|
|
59
|
+
return ['Dependency warnings:', ...lines].join('\n');
|
|
60
|
+
}
|
|
19
61
|
//# sourceMappingURL=titleValidation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"titleValidation.js","sourceRoot":"","sources":["../../src/titleValidation.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"titleValidation.js","sourceRoot":"","sources":["../../src/titleValidation.ts"],"names":[],"mappings":"AAaA;;;;;;;GAOG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAyB;IAC3D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAA;IAExC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;IACjD,CAAC;IAED,OAAO,CAAC,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;SACzB,MAAM,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,CAAC;SAChC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAA;AAC5B,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC1C,UAA6B;IAE7B,MAAM,KAAK,GAAG;QACZ,gEAAgE;KACjE,CAAA;IAED,KAAK,MAAM,KAAK,IAAI,UAAU,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAA;IAC9B,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,UAA0C;IAE1C,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC,CAAA;AACxD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,qBAAqB,CACnC,UAA0C,EAC1C,WAA8B;IAE9B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,CAAA;IAClC,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CACtC,cAA8C,EAC9C,iBAAiD;IAEjD,MAAM,KAAK,GAAa,EAAE,CAAA;IAC1B,KAAK,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;QACjC,KAAK,CAAC,IAAI,CACR,QAAQ,GAAG,CAAC,IAAI,oBAAoB,GAAG,CAAC,EAAE,oEAAoE,CAC/G,CAAA;IACH,CAAC;IACD,KAAK,MAAM,GAAG,IAAI,iBAAiB,EAAE,CAAC;QACpC,KAAK,CAAC,IAAI,CACR,QAAQ,GAAG,CAAC,IAAI,oBAAoB,GAAG,CAAC,EAAE,yCAAyC,GAAG,CAAC,EAAE,0BAA0B,CACpH,CAAA;IACH,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IACjC,OAAO,CAAC,sBAAsB,EAAE,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;AACtD,CAAC"}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -139,6 +139,13 @@ export type RenderOptions = {
|
|
|
139
139
|
narration?: {
|
|
140
140
|
/** 0-1: 1=mask size equals shorter side of output */
|
|
141
141
|
size?: number;
|
|
142
|
+
/**
|
|
143
|
+
* Narration size when the recording is shrunk via `resizeRecording()`.
|
|
144
|
+
* When set, the narration bubble scales down and tightens into its corner
|
|
145
|
+
* in sync with the recording size transition. Omit to keep a fixed size.
|
|
146
|
+
* 0-1: same units as `size`.
|
|
147
|
+
*/
|
|
148
|
+
sizeZoomed?: number;
|
|
142
149
|
/** 0-1: 0=square, 1=circle */
|
|
143
150
|
roundness?: number;
|
|
144
151
|
shape?: 'rounded';
|
|
@@ -403,6 +410,35 @@ export type RecordOptions = {
|
|
|
403
410
|
* @default 0
|
|
404
411
|
*/
|
|
405
412
|
captureAudio?: number;
|
|
413
|
+
/**
|
|
414
|
+
* CSS selectors whose matching elements are masked from the very first frame,
|
|
415
|
+
* before any page script runs. Use this for elements that are always secret
|
|
416
|
+
* (API keys, account numbers) so there is no window where they could be
|
|
417
|
+
* captured in the clear.
|
|
418
|
+
*
|
|
419
|
+
* The mask is applied client side in the live DOM, so the obscured pixels
|
|
420
|
+
* never enter `recording.mp4` and are never uploaded. For masking that starts
|
|
421
|
+
* at a specific moment, use the `redact()` helper instead.
|
|
422
|
+
*
|
|
423
|
+
* @example ['.api-key', '[data-sensitive]']
|
|
424
|
+
*/
|
|
425
|
+
redact?: string[];
|
|
426
|
+
/**
|
|
427
|
+
* Vertical framing bias (0–1) used when a plain interaction (a `click()`,
|
|
428
|
+
* `fill()`, `scrollIntoViewIfNeeded()`, etc. that is not zooming) scrolls its
|
|
429
|
+
* target into view.
|
|
430
|
+
*
|
|
431
|
+
* `0` reveals the target just inside the top edge, `1` places it dead center,
|
|
432
|
+
* and the default `0.2` frames it gently toward the upper third so
|
|
433
|
+
* already-visible elements are not yanked to the center on every click.
|
|
434
|
+
*
|
|
435
|
+
* This only affects plain scroll reveals. Zooming (`zoomTo`/`autoZoom`) keeps
|
|
436
|
+
* its tight centering, and an explicit per-call `centering` (via
|
|
437
|
+
* `autoZoomOptions` or `scrollIntoViewIfNeeded({ centering })`) always wins.
|
|
438
|
+
*
|
|
439
|
+
* @default 0.2
|
|
440
|
+
*/
|
|
441
|
+
scrollCentering?: number;
|
|
406
442
|
};
|
|
407
443
|
/**
|
|
408
444
|
* Encoder preset for the realtime screen capture. See {@link RecordOptions.encoder}.
|
|
@@ -490,6 +526,42 @@ export type ScreenCILocatorPostClickMoveOptions = CursorMoveTimingOption & {
|
|
|
490
526
|
easing?: Easing;
|
|
491
527
|
padding?: number;
|
|
492
528
|
};
|
|
529
|
+
/**
|
|
530
|
+
* How a redacted region is drawn over the page. The mask is an opaque panel
|
|
531
|
+
* applied client side in the live DOM, so the obscured pixels never enter the
|
|
532
|
+
* recording and are never uploaded, and it cannot leak under any renderer.
|
|
533
|
+
*
|
|
534
|
+
* By default the panel is filled with a color sampled from the surface beneath
|
|
535
|
+
* it, so it blends in as a clean block. Set `color` for a fixed fill, or `css`
|
|
536
|
+
* to fully style the panel.
|
|
537
|
+
*/
|
|
538
|
+
export type RedactStyle = {
|
|
539
|
+
/**
|
|
540
|
+
* Opaque fill color, e.g. '#111' or 'black'. Omit to sample a color from the
|
|
541
|
+
* surface underneath the element.
|
|
542
|
+
*/
|
|
543
|
+
color?: string;
|
|
544
|
+
/** Corner radius of the mask in px (default 12). */
|
|
545
|
+
radius?: number;
|
|
546
|
+
/** CSS box-shadow for the mask, or `false` to disable it. */
|
|
547
|
+
shadow?: string | false;
|
|
548
|
+
/**
|
|
549
|
+
* Extra CSS applied to the mask panel for full custom styling, e.g.
|
|
550
|
+
* `'background: repeating-linear-gradient(45deg,#222 0 6px,#333 6px 12px)'`.
|
|
551
|
+
* Applied last, so it overrides the defaults. Keep the panel opaque, or the
|
|
552
|
+
* content underneath may show through.
|
|
553
|
+
*/
|
|
554
|
+
css?: string;
|
|
555
|
+
};
|
|
556
|
+
/** Options for {@link redact} and the per-action `redact` switch. */
|
|
557
|
+
export type RedactOptions = {
|
|
558
|
+
style?: RedactStyle;
|
|
559
|
+
};
|
|
560
|
+
/** Handle returned by a persistent {@link redact} call. */
|
|
561
|
+
export type RedactHandle = {
|
|
562
|
+
/** Remove this mask, revealing the element again. */
|
|
563
|
+
unredact(): Promise<void>;
|
|
564
|
+
};
|
|
493
565
|
export type ScreenCILocatorFillOptions = ScreenCILocatorClickOptions & {
|
|
494
566
|
/**
|
|
495
567
|
* When `true`, forces the pre-type click animation even if the target input
|
|
@@ -506,6 +578,13 @@ export type ScreenCILocatorFillOptions = ScreenCILocatorClickOptions & {
|
|
|
506
578
|
};
|
|
507
579
|
/** Hide the cursor while typing; shown again on the next mouse move. */
|
|
508
580
|
hideMouse?: boolean;
|
|
581
|
+
/**
|
|
582
|
+
* Mask the typed value in the recording so the secret never enters
|
|
583
|
+
* `recording.mp4`. The mask is applied before the first character is typed,
|
|
584
|
+
* so the value is never captured in the clear. Pass `true` for the default
|
|
585
|
+
* mask, or `RedactOptions` to customize it.
|
|
586
|
+
*/
|
|
587
|
+
redact?: boolean | RedactOptions;
|
|
509
588
|
click?: false | ScreenCILocatorClickOptions;
|
|
510
589
|
postClickMove?: ScreenCILocatorPostClickMoveOptions;
|
|
511
590
|
autoZoomOptions?: AutoZoomOptions;
|
|
@@ -524,6 +603,12 @@ export type ScreenCILocatorPressSequentiallyOptions = Omit<NonNullable<Parameter
|
|
|
524
603
|
};
|
|
525
604
|
/** Hide the cursor while typing; shown again on the next mouse move. */
|
|
526
605
|
hideMouse?: boolean;
|
|
606
|
+
/**
|
|
607
|
+
* Mask the typed value in the recording so the secret never enters
|
|
608
|
+
* `recording.mp4`. Applied before the first character is typed. Pass `true`
|
|
609
|
+
* for the default mask, or `RedactOptions` to customize it.
|
|
610
|
+
*/
|
|
611
|
+
redact?: boolean | RedactOptions;
|
|
527
612
|
autoZoomOptions?: AutoZoomOptions;
|
|
528
613
|
};
|
|
529
614
|
export type ScreenCILocatorCheckOptions = NonNullable<Parameters<Locator['check']>[0]> & CursorMoveOptions & {
|