screenwright 0.1.45 → 0.2.1
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/src/commands/compose.d.ts.map +1 -1
- package/dist/src/commands/compose.js +66 -41
- package/dist/src/commands/compose.js.map +1 -1
- package/dist/src/commands/preview.d.ts.map +1 -1
- package/dist/src/commands/preview.js +9 -12
- package/dist/src/commands/preview.js.map +1 -1
- package/dist/src/composition/DemoVideo.d.ts.map +1 -1
- package/dist/src/composition/DemoVideo.js +24 -78
- package/dist/src/composition/DemoVideo.js.map +1 -1
- package/dist/src/composition/frame-resolve.d.ts +37 -0
- package/dist/src/composition/frame-resolve.d.ts.map +1 -0
- package/dist/src/composition/frame-resolve.js +114 -0
- package/dist/src/composition/frame-resolve.js.map +1 -0
- package/dist/src/composition/remotion-root.d.ts.map +1 -1
- package/dist/src/composition/remotion-root.js +7 -14
- package/dist/src/composition/remotion-root.js.map +1 -1
- package/dist/src/composition/render.d.ts.map +1 -1
- package/dist/src/composition/render.js +2 -6
- package/dist/src/composition/render.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/runtime/action-helpers.d.ts +15 -5
- package/dist/src/runtime/action-helpers.d.ts.map +1 -1
- package/dist/src/runtime/action-helpers.js +218 -60
- package/dist/src/runtime/action-helpers.js.map +1 -1
- package/dist/src/runtime/instrumented-page.d.ts +3 -2
- package/dist/src/runtime/instrumented-page.d.ts.map +1 -1
- package/dist/src/runtime/instrumented-page.js +105 -101
- package/dist/src/runtime/instrumented-page.js.map +1 -1
- package/dist/src/runtime/narration-preprocess.d.ts +30 -0
- package/dist/src/runtime/narration-preprocess.d.ts.map +1 -0
- package/dist/src/runtime/narration-preprocess.js +79 -0
- package/dist/src/runtime/narration-preprocess.js.map +1 -0
- package/dist/src/runtime/timeline-collector.d.ts +1 -7
- package/dist/src/runtime/timeline-collector.d.ts.map +1 -1
- package/dist/src/runtime/timeline-collector.js +2 -17
- package/dist/src/runtime/timeline-collector.js.map +1 -1
- package/dist/src/timeline/schema.d.ts +143 -162
- package/dist/src/timeline/schema.d.ts.map +1 -1
- package/dist/src/timeline/schema.js +12 -18
- package/dist/src/timeline/schema.js.map +1 -1
- package/dist/src/timeline/types.d.ts +15 -18
- package/dist/src/timeline/types.d.ts.map +1 -1
- package/dist/src/timeline/types.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +3 -3
- package/dist/src/composition/SceneSlide.d.ts +0 -12
- package/dist/src/composition/SceneSlide.d.ts.map +0 -1
- package/dist/src/composition/SceneSlide.js +0 -71
- package/dist/src/composition/SceneSlide.js.map +0 -1
- package/dist/src/composition/frame-lookup.d.ts +0 -8
- package/dist/src/composition/frame-lookup.d.ts.map +0 -1
- package/dist/src/composition/frame-lookup.js +0 -26
- package/dist/src/composition/frame-lookup.js.map +0 -1
- package/dist/src/composition/time-remap.d.ts +0 -87
- package/dist/src/composition/time-remap.d.ts.map +0 -1
- package/dist/src/composition/time-remap.js +0 -218
- package/dist/src/composition/time-remap.js.map +0 -1
- package/dist/src/voiceover/narration-timing.d.ts +0 -18
- package/dist/src/voiceover/narration-timing.d.ts.map +0 -1
- package/dist/src/voiceover/narration-timing.js +0 -40
- package/dist/src/voiceover/narration-timing.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frame-resolve.js","sourceRoot":"","sources":["../../../src/composition/frame-resolve.ts"],"names":[],"mappings":"AAEA,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;AAiB3B,6CAA6C;AAC7C,MAAM,UAAU,kBAAkB,CAAC,QAAyB;IAC1D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,gBAAgB,CAAC,QAAyB,EAAE,WAAmB;IAC7E,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,KAAK,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACtD,IAAI,WAAW,GAAG,WAAW,GAAG,KAAK;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC;QACzD,WAAW,IAAI,KAAK,CAAC;IACvB,CAAC;IACD,OAAO,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,SAAS,eAAe,CAAC,KAAoB;IAC3C,OAAO,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAyB,EAAE,UAAkB;IACvE,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,UAAU,IAAI,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3D,KAAK,IAAI,eAAe,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IACxC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,sBAAsB,CAAC,QAAyB,EAAE,UAAkB;IAC3E,OAAO,kBAAkB,CAAC,QAAQ,EAAE,UAAU,CAAC,GAAG,eAAe,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC;AAC9F,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,iBAAiB,CAAC,QAAyB,EAAE,WAA+B;IAC1F,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC5C,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;QAC5B,QAAQ,IAAI,CAAC,CAAC,cAAc,CAAC;QAC7B,QAAQ,IAAI,CAAC,CAAC,CAAC,sEAAsE;IACvF,CAAC;IACD,OAAO,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AACtC,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAChC,WAAmB,EACnB,QAAyB,EACzB,WAA+B;IAE/B,MAAM,MAAM,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC;IAEtF,IAAI,MAAM,GAAG,CAAC,CAAC,CAAC,2DAA2D;IAE3E,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,sBAAsB,CAAC,QAAQ,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC;QAEpE,kDAAkD;QAClD,MAAM,OAAO,GAAG,OAAO,GAAG,MAAM,CAAC;QAEjC,qFAAqF;QACrF,MAAM,UAAU,GAAG,OAAO,GAAG,CAAC,CAAC;QAC/B,MAAM,QAAQ,GAAG,OAAO,GAAG,CAAC,CAAC,cAAc,CAAC;QAE5C,IAAI,WAAW,IAAI,OAAO,EAAE,CAAC;YAC3B,4CAA4C;YAC5C,MAAM;QACR,CAAC;QAED,IAAI,WAAW,IAAI,UAAU,IAAI,WAAW,IAAI,QAAQ,EAAE,CAAC;YACzD,MAAM,QAAQ,GAAG,CAAC,WAAW,GAAG,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,cAAc,CAAC;YACnE,MAAM,UAAU,GAAG,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACvD,wDAAwD;YACxD,MAAM,aAAa,GAAG,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC;YAC5C,MAAM,SAAS,GAAG,aAAa,GAAG,QAAQ,CAAC,MAAM;gBAC/C,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,CAAC,IAAI;gBAC9B,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;YACvC,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU,EAAE,CAAC;QAC3F,CAAC;QAED,sCAAsC;QACtC,yDAAyD;QACzD,MAAM,IAAI,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC;IACjC,CAAC;IAED,wCAAwC;IACxC,MAAM,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;IACzC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,kBAAkB,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACrF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;AACvE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,oBAAoB,CAClC,MAAW,EACX,QAAyB,EACzB,WAA+B;IAE/B,MAAM,MAAM,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,eAAe,GAAG,CAAC,CAAC,eAAe,CAAC,CAAC;IAEtF,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QACxB,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;YACvB,6DAA6D;YAC7D,MAAM,OAAO,GAAG,sBAAsB,CAAC,QAAQ,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC;YACpE,MAAM,kBAAkB,GAAG,OAAO,GAAG,QAAQ,CAAC;YAE9C,IAAI,KAAK,CAAC,WAAW,GAAG,kBAAkB,EAAE,CAAC;gBAC3C,QAAQ,IAAI,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC;YAChD,CAAC;QACH,CAAC;QACD,IAAI,QAAQ,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC;QACjC,OAAO,EAAE,GAAG,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,WAAW,GAAG,QAAQ,EAAE,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remotion-root.d.ts","sourceRoot":"","sources":["../../../src/composition/remotion-root.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"remotion-root.d.ts","sourceRoot":"","sources":["../../../src/composition/remotion-root.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAa1B,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAwChC,CAAC"}
|
|
@@ -4,7 +4,7 @@ import { z } from 'zod';
|
|
|
4
4
|
import { DemoVideo } from './DemoVideo.js';
|
|
5
5
|
import { timelineSchema } from '../timeline/schema.js';
|
|
6
6
|
import { brandingSchema } from '../config/config-schema.js';
|
|
7
|
-
import {
|
|
7
|
+
import { totalOutputFrames } from './frame-resolve.js';
|
|
8
8
|
const propsSchema = z.object({
|
|
9
9
|
timeline: timelineSchema,
|
|
10
10
|
branding: brandingSchema.optional(),
|
|
@@ -12,29 +12,22 @@ const propsSchema = z.object({
|
|
|
12
12
|
export const RemotionRoot = () => {
|
|
13
13
|
return (_jsx(_Fragment, { children: _jsx(Composition, { id: "DemoVideo", lazyComponent: () => Promise.resolve({ default: DemoVideo }), schema: propsSchema, durationInFrames: 300, fps: 30, width: 1280, height: 720, defaultProps: {
|
|
14
14
|
timeline: {
|
|
15
|
-
version:
|
|
15
|
+
version: 2,
|
|
16
16
|
metadata: {
|
|
17
17
|
testFile: '',
|
|
18
18
|
scenarioFile: '',
|
|
19
19
|
recordedAt: new Date().toISOString(),
|
|
20
20
|
viewport: { width: 1280, height: 720 },
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
frameManifest: [{ type: 'frame', file: 'placeholder.jpg' }],
|
|
22
|
+
transitionMarkers: [],
|
|
23
23
|
},
|
|
24
24
|
events: [],
|
|
25
25
|
},
|
|
26
26
|
}, calculateMetadata: ({ props }) => {
|
|
27
|
-
const
|
|
28
|
-
const scenes = props.timeline.events.filter((e) => e.type === 'scene');
|
|
29
|
-
const slideScenes = resolveSlideScenes(scenes, props.timeline.events);
|
|
30
|
-
const resolvedTransitions = resolveTransitions(props.timeline.events);
|
|
31
|
-
const totalMs = props.timeline.metadata.videoDurationMs
|
|
32
|
-
+ totalSlideDurationMs(slideScenes)
|
|
33
|
-
+ totalTransitionDurationMs(resolvedTransitions);
|
|
34
|
-
const durationInFrames = Math.max(30, msToFrames(totalMs, fps));
|
|
27
|
+
const total = totalOutputFrames(props.timeline.metadata.frameManifest, props.timeline.metadata.transitionMarkers);
|
|
35
28
|
return {
|
|
36
|
-
durationInFrames,
|
|
37
|
-
fps,
|
|
29
|
+
durationInFrames: Math.max(30, total),
|
|
30
|
+
fps: 30,
|
|
38
31
|
width: props.timeline.metadata.viewport.width,
|
|
39
32
|
height: props.timeline.metadata.viewport.height,
|
|
40
33
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remotion-root.js","sourceRoot":"","sources":["../../../src/composition/remotion-root.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"remotion-root.js","sourceRoot":"","sources":["../../../src/composition/remotion-root.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC;IAC3B,QAAQ,EAAE,cAAc;IACxB,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;CACpC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,YAAY,GAAa,GAAG,EAAE;IACzC,OAAO,CACL,4BACE,KAAC,WAAW,IACV,EAAE,EAAC,WAAW,EACd,aAAa,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,SAAgB,EAAE,CAAC,EACnE,MAAM,EAAE,WAAW,EACnB,gBAAgB,EAAE,GAAG,EACrB,GAAG,EAAE,EAAE,EACP,KAAK,EAAE,IAAI,EACX,MAAM,EAAE,GAAG,EACX,YAAY,EAAE;gBACZ,QAAQ,EAAE;oBACR,OAAO,EAAE,CAAU;oBACnB,QAAQ,EAAE;wBACR,QAAQ,EAAE,EAAE;wBACZ,YAAY,EAAE,EAAE;wBAChB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;wBACpC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE;wBACtC,aAAa,EAAE,CAAC,EAAE,IAAI,EAAE,OAAgB,EAAE,IAAI,EAAE,iBAAiB,EAAE,CAAC;wBACpE,iBAAiB,EAAE,EAAE;qBACtB;oBACD,MAAM,EAAE,EAAE;iBACX;aACF,EACD,iBAAiB,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC/B,MAAM,KAAK,GAAG,iBAAiB,CAC7B,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,EACrC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,iBAAiB,CAC1C,CAAC;gBACF,OAAO;oBACL,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,KAAK,CAAC;oBACrC,GAAG,EAAE,EAAE;oBACP,KAAK,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK;oBAC7C,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM;iBAChD,CAAC;YACJ,CAAC,GACD,GACD,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,YAAY,CAAC,YAAY,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/composition/render.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/composition/render.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAqBD,wBAAsB,eAAe,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAiC1E"}
|
|
@@ -5,16 +5,12 @@ import { resolve, basename } from 'node:path';
|
|
|
5
5
|
* Rewrite absolute file paths in the timeline to basenames.
|
|
6
6
|
* Remotion components run in a browser (webpack) and resolve assets
|
|
7
7
|
* via staticFile() against the publicDir — they only need filenames.
|
|
8
|
+
*
|
|
9
|
+
* frameManifest paths are already relative to publicDir — no rewrite needed.
|
|
8
10
|
*/
|
|
9
11
|
function toStaticPaths(timeline) {
|
|
10
|
-
const meta = { ...timeline.metadata };
|
|
11
|
-
if (meta.videoFile) {
|
|
12
|
-
meta.videoFile = basename(meta.videoFile);
|
|
13
|
-
}
|
|
14
|
-
// frameManifest paths are already relative to publicDir — no rewrite needed
|
|
15
12
|
return {
|
|
16
13
|
...timeline,
|
|
17
|
-
metadata: meta,
|
|
18
14
|
events: timeline.events.map(e => {
|
|
19
15
|
if (e.type === 'narration' && e.audioFile) {
|
|
20
16
|
return { ...e, audioFile: basename(e.audioFile) };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/composition/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAY9C
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/composition/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAY9C;;;;;;GAMG;AACH,SAAS,aAAa,CAAC,QAAkB;IACvC,OAAO;QACL,GAAG,QAAQ;QACX,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;YAC9B,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC;gBAC1C,OAAO,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;YACpD,CAAC;YACD,OAAO,CAAC,CAAC;QACX,CAAC,CAAC;KACH,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAmB;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;IAEvF,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC;QAC9B,UAAU;QACV,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAC;IAEH,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEpD,MAAM,UAAU,GAA4B,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC;IACzE,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC;IACtC,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC;QAC1C,QAAQ,EAAE,UAAU;QACpB,EAAE,EAAE,WAAW;QACf,UAAU;KACX,CAAC,CAAC;IAEH,MAAM,WAAW,CAAC;QAChB,WAAW;QACX,QAAQ,EAAE,UAAU;QACpB,KAAK,EAAE,MAAM;QACb,GAAG,EAAE,EAAE;QACP,WAAW,EAAE,SAAS;QACtB,KAAK,EAAE,CAAC;QACR,cAAc,EAAE,IAAI,CAAC,UAAU;QAC/B,UAAU;KACX,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,UAAU,CAAC;AACzB,CAAC"}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type { ScreenwrightHelpers, ActionOptions, SceneOptions, TransitionOptions } from './runtime/action-helpers.js';
|
|
2
2
|
export type { ScenarioFn } from './runtime/instrumented-page.js';
|
|
3
|
-
export type { Timeline, TimelineEvent, SceneEvent, ActionEvent, CursorTargetEvent, NarrationEvent, WaitEvent,
|
|
3
|
+
export type { Timeline, TimelineEvent, SceneEvent, ActionEvent, CursorTargetEvent, NarrationEvent, WaitEvent, ManifestEntry, TransitionMarker, SceneSlideConfig, TransitionType } from './timeline/types.js';
|
|
4
4
|
export { transitionTypes } from './timeline/types.js';
|
|
5
5
|
export type { ScreenwrightConfig, OpenaiVoice } from './config/config-schema.js';
|
|
6
6
|
export { openaiVoices } from './config/config-schema.js';
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACvH,YAAY,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACvH,YAAY,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,UAAU,EAAE,WAAW,EAAE,iBAAiB,EAAE,cAAc,EAAE,SAAS,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC7M,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,mCAAmC,CAAC;AAC9F,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Page } from 'playwright';
|
|
2
2
|
import type { TimelineCollector } from './timeline-collector.js';
|
|
3
|
-
import type { SceneSlideConfig, TransitionType } from '../timeline/types.js';
|
|
3
|
+
import type { ManifestEntry, TransitionMarker, SceneSlideConfig, TransitionType } from '../timeline/types.js';
|
|
4
|
+
import type { PregeneratedNarration } from './narration-preprocess.js';
|
|
4
5
|
export interface ActionOptions {
|
|
5
6
|
narration?: string;
|
|
6
7
|
}
|
|
@@ -24,9 +25,18 @@ export interface ScreenwrightHelpers {
|
|
|
24
25
|
narrate(text: string): Promise<void>;
|
|
25
26
|
transition(opts?: TransitionOptions): Promise<void>;
|
|
26
27
|
}
|
|
27
|
-
export
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
export interface RecordingContext {
|
|
29
|
+
pauseCapture(): Promise<void>;
|
|
30
|
+
resumeCapture(): void;
|
|
31
|
+
captureOneFrame(): Promise<string>;
|
|
32
|
+
addHold(file: string, count: number): void;
|
|
33
|
+
addTransitionMarker(marker: TransitionMarker): void;
|
|
34
|
+
popNarration(): PregeneratedNarration;
|
|
35
|
+
currentTimeMs(): number;
|
|
36
|
+
readonly manifest: ManifestEntry[];
|
|
37
|
+
transitionPending: boolean;
|
|
38
|
+
readonly narrationCount: number;
|
|
30
39
|
}
|
|
31
|
-
export declare function
|
|
40
|
+
export declare function calculateMoveDuration(fromX: number, fromY: number, toX: number, toY: number): number;
|
|
41
|
+
export declare function createHelpers(page: Page, collector: TimelineCollector, ctx: RecordingContext): ScreenwrightHelpers;
|
|
32
42
|
//# sourceMappingURL=action-helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-helpers.d.ts","sourceRoot":"","sources":["../../../src/runtime/action-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"action-helpers.d.ts","sourceRoot":"","sources":["../../../src/runtime/action-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AACjE,OAAO,KAAK,EAAE,aAAa,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC9G,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAEvE,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,oBAAoB,CAAC,EAAE,MAAM,GAAG,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3D,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,UAAU,CAAC,IAAI,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,aAAa,IAAI,IAAI,CAAC;IACtB,eAAe,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IACnC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,mBAAmB,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACpD,YAAY,IAAI,qBAAqB,CAAC;IACtC,aAAa,IAAI,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,aAAa,EAAE,CAAC;IACnC,iBAAiB,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAQD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAGpG;AA6FD,wBAAgB,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,EAAE,gBAAgB,GAAG,mBAAmB,CAsTlH"}
|
|
@@ -1,42 +1,115 @@
|
|
|
1
|
-
const
|
|
1
|
+
const DEFAULT_SLIDE_DURATION_MS = 2000;
|
|
2
2
|
const CHAR_TYPE_DELAY_MS = 30;
|
|
3
3
|
const CURSOR_MOVE_MIN_MS = 200;
|
|
4
4
|
const CURSOR_MOVE_MAX_MS = 800;
|
|
5
|
-
|
|
6
|
-
const words = text.split(/\s+/).length;
|
|
7
|
-
return Math.round((words / NARRATION_WPM) * 60 * 1000);
|
|
8
|
-
}
|
|
5
|
+
const FPS = 30;
|
|
9
6
|
export function calculateMoveDuration(fromX, fromY, toX, toY) {
|
|
10
7
|
const distance = Math.sqrt((toX - fromX) ** 2 + (toY - fromY) ** 2);
|
|
11
8
|
return Math.min(CURSOR_MOVE_MAX_MS, Math.max(CURSOR_MOVE_MIN_MS, Math.round(200 * Math.log2(distance / 10 + 1))));
|
|
12
9
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
function msToFrames(ms) {
|
|
11
|
+
return Math.ceil(ms / 1000 * FPS);
|
|
12
|
+
}
|
|
13
|
+
const SLIDE_OVERLAY_ID = '__screenwright_slide_overlay__';
|
|
14
|
+
function escapeJs(s) {
|
|
15
|
+
return s.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n');
|
|
16
|
+
}
|
|
17
|
+
async function injectSlideOverlay(page, title, description, config) {
|
|
18
|
+
const brandColor = config.brandColor ?? '#000000';
|
|
19
|
+
const textColor = config.textColor ?? '#FFFFFF';
|
|
20
|
+
const fontFamily = config.fontFamily;
|
|
21
|
+
const titleFontSize = config.titleFontSize ?? 64;
|
|
22
|
+
const descFontSize = Math.round(titleFontSize * 0.44);
|
|
23
|
+
const resolvedFont = fontFamily
|
|
24
|
+
? `"${fontFamily}", system-ui, -apple-system, sans-serif`
|
|
25
|
+
: 'system-ui, -apple-system, sans-serif';
|
|
26
|
+
// Use string-based evaluate to avoid TypeScript checking browser globals
|
|
27
|
+
let script = '';
|
|
28
|
+
if (fontFamily) {
|
|
29
|
+
const encoded = encodeURIComponent(fontFamily);
|
|
30
|
+
script += `
|
|
31
|
+
if (!document.getElementById('${SLIDE_OVERLAY_ID}_font')) {
|
|
32
|
+
var link = document.createElement('link');
|
|
33
|
+
link.id = '${SLIDE_OVERLAY_ID}_font';
|
|
34
|
+
link.rel = 'stylesheet';
|
|
35
|
+
link.href = 'https://fonts.googleapis.com/css2?family=${encoded}&display=swap';
|
|
36
|
+
document.head.appendChild(link);
|
|
37
|
+
}
|
|
38
|
+
`;
|
|
39
|
+
}
|
|
40
|
+
script += `
|
|
41
|
+
var overlay = document.createElement('div');
|
|
42
|
+
overlay.id = '${SLIDE_OVERLAY_ID}';
|
|
43
|
+
overlay.style.cssText = 'position:fixed;inset:0;z-index:999999;display:flex;flex-direction:column;align-items:center;justify-content:center;background-color:${escapeJs(brandColor)};font-family:${escapeJs(resolvedFont)};';
|
|
44
|
+
var inner = document.createElement('div');
|
|
45
|
+
inner.style.cssText = 'text-align:center;padding:0 10%;';
|
|
46
|
+
var h1 = document.createElement('h1');
|
|
47
|
+
h1.textContent = '${escapeJs(title)}';
|
|
48
|
+
h1.style.cssText = 'color:${escapeJs(textColor)};font-size:${titleFontSize}px;font-weight:700;margin:0;line-height:1.2;';
|
|
49
|
+
inner.appendChild(h1);
|
|
50
|
+
`;
|
|
51
|
+
if (description) {
|
|
52
|
+
script += `
|
|
53
|
+
var divider = document.createElement('div');
|
|
54
|
+
divider.style.cssText = 'width:80px;height:4px;background-color:${escapeJs(textColor)};opacity:0.4;margin:24px auto;border-radius:2px;';
|
|
55
|
+
inner.appendChild(divider);
|
|
56
|
+
var p = document.createElement('p');
|
|
57
|
+
p.textContent = '${escapeJs(description)}';
|
|
58
|
+
p.style.cssText = 'color:${escapeJs(textColor)};font-size:${descFontSize}px;font-weight:400;margin:0;opacity:0.85;line-height:1.5;';
|
|
59
|
+
inner.appendChild(p);
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
script += `
|
|
63
|
+
overlay.appendChild(inner);
|
|
64
|
+
document.body.appendChild(overlay);
|
|
65
|
+
`;
|
|
66
|
+
await page.evaluate(script);
|
|
67
|
+
// Wait for font to load if specified, with 3s timeout
|
|
68
|
+
if (fontFamily) {
|
|
69
|
+
await page.evaluate(`
|
|
70
|
+
(async () => {
|
|
20
71
|
try {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
}
|
|
72
|
+
await Promise.race([
|
|
73
|
+
document.fonts.load('700 64px "${escapeJs(fontFamily)}"'),
|
|
74
|
+
new Promise(r => setTimeout(r, 3000)),
|
|
75
|
+
]);
|
|
76
|
+
} catch {}
|
|
77
|
+
})()
|
|
78
|
+
`).catch(() => { });
|
|
29
79
|
}
|
|
80
|
+
}
|
|
81
|
+
async function removeSlideOverlay(page) {
|
|
82
|
+
await page.evaluate(`
|
|
83
|
+
var el = document.getElementById('${SLIDE_OVERLAY_ID}');
|
|
84
|
+
if (el) el.remove();
|
|
85
|
+
var fontLink = document.getElementById('${SLIDE_OVERLAY_ID}_font');
|
|
86
|
+
if (fontLink) fontLink.remove();
|
|
87
|
+
`).catch(() => { });
|
|
88
|
+
}
|
|
89
|
+
export function createHelpers(page, collector, ctx) {
|
|
90
|
+
let lastX = 640;
|
|
91
|
+
let lastY = 360;
|
|
30
92
|
async function emitNarration(text) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
93
|
+
await ctx.pauseCapture();
|
|
94
|
+
const narration = ctx.popNarration();
|
|
95
|
+
const file = await ctx.captureOneFrame();
|
|
96
|
+
const holdFrames = msToFrames(narration.durationMs);
|
|
97
|
+
if (holdFrames > 1)
|
|
98
|
+
ctx.addHold(file, holdFrames - 1); // captureOneFrame already added 1
|
|
99
|
+
collector.emit({
|
|
100
|
+
type: 'narration',
|
|
101
|
+
timestampMs: ctx.currentTimeMs(),
|
|
102
|
+
text: narration.text,
|
|
103
|
+
audioDurationMs: narration.durationMs,
|
|
104
|
+
audioFile: narration.audioFile,
|
|
105
|
+
});
|
|
106
|
+
ctx.resumeCapture();
|
|
35
107
|
}
|
|
36
108
|
async function moveCursorTo(toX, toY) {
|
|
37
109
|
const moveDurationMs = calculateMoveDuration(lastX, lastY, toX, toY);
|
|
38
110
|
collector.emit({
|
|
39
111
|
type: 'cursor_target',
|
|
112
|
+
timestampMs: ctx.currentTimeMs(),
|
|
40
113
|
fromX: lastX, fromY: lastY,
|
|
41
114
|
toX, toY,
|
|
42
115
|
moveDurationMs,
|
|
@@ -61,22 +134,67 @@ export function createHelpers(page, collector, opts) {
|
|
|
61
134
|
err.cause = cause;
|
|
62
135
|
return err;
|
|
63
136
|
}
|
|
137
|
+
/** Resolve a pending transition after an action settles. */
|
|
138
|
+
async function resolveTransition() {
|
|
139
|
+
if (!ctx.transitionPending)
|
|
140
|
+
return;
|
|
141
|
+
const file = await ctx.captureOneFrame();
|
|
142
|
+
// The "after" frame is now in the manifest — resume capture
|
|
143
|
+
ctx.transitionPending = false;
|
|
144
|
+
ctx.resumeCapture();
|
|
145
|
+
// We don't need the file reference; it's in the manifest at the correct position
|
|
146
|
+
void file;
|
|
147
|
+
}
|
|
64
148
|
return {
|
|
65
149
|
page,
|
|
66
150
|
async scene(title, descriptionOrOptions) {
|
|
67
|
-
|
|
68
|
-
|
|
151
|
+
let description;
|
|
152
|
+
let slide;
|
|
153
|
+
if (typeof descriptionOrOptions === 'string') {
|
|
154
|
+
description = descriptionOrOptions;
|
|
155
|
+
}
|
|
156
|
+
else if (descriptionOrOptions !== undefined) {
|
|
157
|
+
description = descriptionOrOptions.description;
|
|
158
|
+
slide = descriptionOrOptions.slide;
|
|
159
|
+
}
|
|
160
|
+
if (slide) {
|
|
161
|
+
// DOM injection slide
|
|
162
|
+
await ctx.pauseCapture();
|
|
163
|
+
const slideDurationMs = slide.duration ?? DEFAULT_SLIDE_DURATION_MS;
|
|
164
|
+
await injectSlideOverlay(page, title, description, slide);
|
|
165
|
+
try {
|
|
166
|
+
const file = await ctx.captureOneFrame();
|
|
167
|
+
const holdFrames = msToFrames(slideDurationMs);
|
|
168
|
+
if (holdFrames > 1)
|
|
169
|
+
ctx.addHold(file, holdFrames - 1);
|
|
170
|
+
}
|
|
171
|
+
finally {
|
|
172
|
+
await removeSlideOverlay(page);
|
|
173
|
+
}
|
|
174
|
+
collector.emit({ type: 'scene', timestampMs: ctx.currentTimeMs(), title, description, slide });
|
|
175
|
+
// If transition was pending, the slide resolves it
|
|
176
|
+
if (ctx.transitionPending) {
|
|
177
|
+
ctx.transitionPending = false;
|
|
178
|
+
}
|
|
179
|
+
// Do NOT resume capture here — the slide is self-contained.
|
|
180
|
+
// Resuming would capture blank-page frames that corrupt subsequent
|
|
181
|
+
// transition markers. The next real action resumes capture.
|
|
69
182
|
}
|
|
70
183
|
else {
|
|
71
|
-
|
|
72
|
-
collector.emit({ type: 'scene', title, description
|
|
184
|
+
// No slide — just emit scene marker
|
|
185
|
+
collector.emit({ type: 'scene', timestampMs: ctx.currentTimeMs(), title, description });
|
|
73
186
|
}
|
|
74
187
|
},
|
|
75
188
|
async navigate(url, actionOpts) {
|
|
76
|
-
|
|
189
|
+
if (actionOpts?.narration)
|
|
190
|
+
await emitNarration(actionOpts.narration);
|
|
191
|
+
const wasPending = ctx.transitionPending;
|
|
192
|
+
if (wasPending) {
|
|
193
|
+
// Capture loop is already paused from transition()
|
|
194
|
+
}
|
|
195
|
+
const startMs = ctx.currentTimeMs();
|
|
77
196
|
try {
|
|
78
197
|
await page.goto(url, { waitUntil: 'domcontentloaded' });
|
|
79
|
-
const settledSnapshot = await takeSnapshot();
|
|
80
198
|
collector.emit({
|
|
81
199
|
type: 'action',
|
|
82
200
|
action: 'navigate',
|
|
@@ -84,27 +202,30 @@ export function createHelpers(page, collector, opts) {
|
|
|
84
202
|
durationMs: 0,
|
|
85
203
|
boundingBox: null,
|
|
86
204
|
timestampMs: startMs,
|
|
87
|
-
settledAtMs:
|
|
88
|
-
...(settledSnapshot ? { settledSnapshot } : {}),
|
|
205
|
+
settledAtMs: ctx.currentTimeMs(),
|
|
89
206
|
});
|
|
90
207
|
}
|
|
91
208
|
catch (err) {
|
|
92
209
|
throw actionError('navigate', url, err);
|
|
93
210
|
}
|
|
94
|
-
if (
|
|
95
|
-
await
|
|
211
|
+
if (wasPending) {
|
|
212
|
+
await resolveTransition();
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
ctx.resumeCapture();
|
|
216
|
+
}
|
|
96
217
|
},
|
|
97
218
|
async click(selector, actionOpts) {
|
|
98
219
|
if (actionOpts?.narration)
|
|
99
220
|
await emitNarration(actionOpts.narration);
|
|
221
|
+
const wasPending = ctx.transitionPending;
|
|
100
222
|
try {
|
|
101
223
|
const center = await resolveCenter(selector);
|
|
102
224
|
await moveCursorTo(center.x, center.y);
|
|
103
225
|
const locator = page.locator(selector).first();
|
|
104
226
|
const box = await locator.boundingBox();
|
|
105
|
-
const startMs =
|
|
227
|
+
const startMs = ctx.currentTimeMs();
|
|
106
228
|
await locator.click();
|
|
107
|
-
const settledSnapshot = await takeSnapshot();
|
|
108
229
|
collector.emit({
|
|
109
230
|
type: 'action',
|
|
110
231
|
action: 'click',
|
|
@@ -112,28 +233,33 @@ export function createHelpers(page, collector, opts) {
|
|
|
112
233
|
durationMs: 200,
|
|
113
234
|
boundingBox: box ? { x: Math.round(box.x), y: Math.round(box.y), width: Math.round(box.width), height: Math.round(box.height) } : null,
|
|
114
235
|
timestampMs: startMs,
|
|
115
|
-
settledAtMs:
|
|
116
|
-
...(settledSnapshot ? { settledSnapshot } : {}),
|
|
236
|
+
settledAtMs: ctx.currentTimeMs(),
|
|
117
237
|
});
|
|
118
238
|
}
|
|
119
239
|
catch (err) {
|
|
120
240
|
throw actionError('click', selector, err);
|
|
121
241
|
}
|
|
242
|
+
if (wasPending) {
|
|
243
|
+
await resolveTransition();
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
ctx.resumeCapture();
|
|
247
|
+
}
|
|
122
248
|
},
|
|
123
249
|
async fill(selector, value, actionOpts) {
|
|
124
250
|
if (actionOpts?.narration)
|
|
125
251
|
await emitNarration(actionOpts.narration);
|
|
252
|
+
const wasPending = ctx.transitionPending;
|
|
126
253
|
try {
|
|
127
254
|
const center = await resolveCenter(selector);
|
|
128
255
|
await moveCursorTo(center.x, center.y);
|
|
129
256
|
const locator = page.locator(selector).first();
|
|
130
257
|
const box = await locator.boundingBox();
|
|
131
258
|
await locator.click();
|
|
132
|
-
const startMs =
|
|
259
|
+
const startMs = ctx.currentTimeMs();
|
|
133
260
|
for (const char of value) {
|
|
134
261
|
await page.keyboard.type(char, { delay: CHAR_TYPE_DELAY_MS });
|
|
135
262
|
}
|
|
136
|
-
const settledSnapshot = await takeSnapshot();
|
|
137
263
|
collector.emit({
|
|
138
264
|
type: 'action',
|
|
139
265
|
action: 'fill',
|
|
@@ -142,25 +268,30 @@ export function createHelpers(page, collector, opts) {
|
|
|
142
268
|
durationMs: value.length * CHAR_TYPE_DELAY_MS,
|
|
143
269
|
boundingBox: box ? { x: Math.round(box.x), y: Math.round(box.y), width: Math.round(box.width), height: Math.round(box.height) } : null,
|
|
144
270
|
timestampMs: startMs,
|
|
145
|
-
settledAtMs:
|
|
146
|
-
...(settledSnapshot ? { settledSnapshot } : {}),
|
|
271
|
+
settledAtMs: ctx.currentTimeMs(),
|
|
147
272
|
});
|
|
148
273
|
}
|
|
149
274
|
catch (err) {
|
|
150
275
|
throw actionError('fill', selector, err);
|
|
151
276
|
}
|
|
277
|
+
if (wasPending) {
|
|
278
|
+
await resolveTransition();
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
ctx.resumeCapture();
|
|
282
|
+
}
|
|
152
283
|
},
|
|
153
284
|
async hover(selector, actionOpts) {
|
|
154
285
|
if (actionOpts?.narration)
|
|
155
286
|
await emitNarration(actionOpts.narration);
|
|
287
|
+
const wasPending = ctx.transitionPending;
|
|
156
288
|
try {
|
|
157
289
|
const center = await resolveCenter(selector);
|
|
158
290
|
await moveCursorTo(center.x, center.y);
|
|
159
291
|
const locator = page.locator(selector).first();
|
|
160
292
|
const box = await locator.boundingBox();
|
|
161
|
-
const startMs =
|
|
293
|
+
const startMs = ctx.currentTimeMs();
|
|
162
294
|
await locator.hover();
|
|
163
|
-
const settledSnapshot = await takeSnapshot();
|
|
164
295
|
collector.emit({
|
|
165
296
|
type: 'action',
|
|
166
297
|
action: 'hover',
|
|
@@ -168,21 +299,26 @@ export function createHelpers(page, collector, opts) {
|
|
|
168
299
|
durationMs: 200,
|
|
169
300
|
boundingBox: box ? { x: Math.round(box.x), y: Math.round(box.y), width: Math.round(box.width), height: Math.round(box.height) } : null,
|
|
170
301
|
timestampMs: startMs,
|
|
171
|
-
settledAtMs:
|
|
172
|
-
...(settledSnapshot ? { settledSnapshot } : {}),
|
|
302
|
+
settledAtMs: ctx.currentTimeMs(),
|
|
173
303
|
});
|
|
174
304
|
}
|
|
175
305
|
catch (err) {
|
|
176
306
|
throw actionError('hover', selector, err);
|
|
177
307
|
}
|
|
308
|
+
if (wasPending) {
|
|
309
|
+
await resolveTransition();
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
ctx.resumeCapture();
|
|
313
|
+
}
|
|
178
314
|
},
|
|
179
315
|
async press(key, actionOpts) {
|
|
180
316
|
if (actionOpts?.narration)
|
|
181
317
|
await emitNarration(actionOpts.narration);
|
|
182
|
-
const
|
|
318
|
+
const wasPending = ctx.transitionPending;
|
|
319
|
+
const startMs = ctx.currentTimeMs();
|
|
183
320
|
try {
|
|
184
321
|
await page.keyboard.press(key);
|
|
185
|
-
const settledSnapshot = await takeSnapshot();
|
|
186
322
|
collector.emit({
|
|
187
323
|
type: 'action',
|
|
188
324
|
action: 'press',
|
|
@@ -190,17 +326,35 @@ export function createHelpers(page, collector, opts) {
|
|
|
190
326
|
durationMs: 100,
|
|
191
327
|
boundingBox: null,
|
|
192
328
|
timestampMs: startMs,
|
|
193
|
-
settledAtMs:
|
|
194
|
-
...(settledSnapshot ? { settledSnapshot } : {}),
|
|
329
|
+
settledAtMs: ctx.currentTimeMs(),
|
|
195
330
|
});
|
|
196
331
|
}
|
|
197
332
|
catch (err) {
|
|
198
333
|
throw actionError('press', key, err);
|
|
199
334
|
}
|
|
335
|
+
if (wasPending) {
|
|
336
|
+
await resolveTransition();
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
ctx.resumeCapture();
|
|
340
|
+
}
|
|
200
341
|
},
|
|
201
342
|
async wait(ms) {
|
|
202
|
-
|
|
203
|
-
|
|
343
|
+
if (ctx.transitionPending) {
|
|
344
|
+
// Capture loop is already paused. Wait for real settling, then hold.
|
|
345
|
+
await page.waitForTimeout(ms);
|
|
346
|
+
const file = await ctx.captureOneFrame();
|
|
347
|
+
const holdFrames = msToFrames(ms);
|
|
348
|
+
if (holdFrames > 1)
|
|
349
|
+
ctx.addHold(file, holdFrames - 1);
|
|
350
|
+
collector.emit({ type: 'wait', timestampMs: ctx.currentTimeMs(), durationMs: ms, reason: 'pacing' });
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
// Ensure capture is running — captures real frames during the wait
|
|
354
|
+
ctx.resumeCapture();
|
|
355
|
+
collector.emit({ type: 'wait', timestampMs: ctx.currentTimeMs(), durationMs: ms, reason: 'pacing' });
|
|
356
|
+
await page.waitForTimeout(ms);
|
|
357
|
+
}
|
|
204
358
|
},
|
|
205
359
|
async narrate(text) {
|
|
206
360
|
await emitNarration(text);
|
|
@@ -210,17 +364,21 @@ export function createHelpers(page, collector, opts) {
|
|
|
210
364
|
if (durationMs <= 0 || !Number.isFinite(durationMs)) {
|
|
211
365
|
throw new Error(`sw.transition() duration must be a positive number, got ${durationMs}`);
|
|
212
366
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
367
|
+
if (ctx.transitionPending) {
|
|
368
|
+
console.warn('sw.transition() called twice with no action between them — replacing previous transition marker.');
|
|
369
|
+
// Remove the last marker
|
|
370
|
+
const markers = ctx._transitionMarkers;
|
|
371
|
+
if (markers && markers.length > 0)
|
|
372
|
+
markers.pop();
|
|
216
373
|
}
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
374
|
+
await ctx.pauseCapture();
|
|
375
|
+
ctx.addTransitionMarker({
|
|
376
|
+
afterEntryIndex: ctx.manifest.length - 1,
|
|
220
377
|
transition: transitionOpts?.type ?? 'fade',
|
|
221
|
-
durationMs,
|
|
222
|
-
...(pageSnapshot ? { pageSnapshot } : {}),
|
|
378
|
+
durationFrames: msToFrames(durationMs),
|
|
223
379
|
});
|
|
380
|
+
ctx.transitionPending = true;
|
|
381
|
+
// Capture loop stays paused — next resolving action will resume it
|
|
224
382
|
},
|
|
225
383
|
};
|
|
226
384
|
}
|