screenwright 0.1.36 → 0.1.38
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/composition/DemoVideo.d.ts.map +1 -1
- package/dist/src/composition/DemoVideo.js +45 -74
- package/dist/src/composition/DemoVideo.js.map +1 -1
- package/dist/src/composition/remotion-root.d.ts.map +1 -1
- package/dist/src/composition/remotion-root.js +5 -2
- package/dist/src/composition/remotion-root.js.map +1 -1
- package/dist/src/composition/time-remap.d.ts +43 -16
- package/dist/src/composition/time-remap.d.ts.map +1 -1
- package/dist/src/composition/time-remap.js +129 -78
- package/dist/src/composition/time-remap.js.map +1 -1
- package/dist/src/composition/transition-styles.js +2 -2
- package/dist/src/composition/transition-styles.js.map +1 -1
- package/dist/src/runtime/action-helpers.d.ts.map +1 -1
- package/dist/src/runtime/action-helpers.js +38 -20
- package/dist/src/runtime/action-helpers.js.map +1 -1
- package/dist/src/runtime/instrumented-page.d.ts.map +1 -1
- package/dist/src/runtime/instrumented-page.js +3 -1
- package/dist/src/runtime/instrumented-page.js.map +1 -1
- package/dist/src/timeline/schema.d.ts +156 -1
- package/dist/src/timeline/schema.d.ts.map +1 -1
- package/dist/src/timeline/schema.js +6 -1
- package/dist/src/timeline/schema.js.map +1 -1
- package/dist/src/timeline/types.d.ts +1 -0
- package/dist/src/timeline/types.d.ts.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DemoVideo.d.ts","sourceRoot":"","sources":["../../../src/composition/DemoVideo.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"DemoVideo.d.ts","sourceRoot":"","sources":["../../../src/composition/DemoVideo.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAkBjE,UAAU,KAAK;IACb,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC3B;AAED,eAAO,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,KAAK,CA2KrC,CAAC"}
|
|
@@ -6,25 +6,28 @@ import { SceneSlide } from './SceneSlide.js';
|
|
|
6
6
|
import { precomputeCursorPaths } from './cursor-path.js';
|
|
7
7
|
import { findClosestFrame } from './frame-lookup.js';
|
|
8
8
|
import { getTransitionStyles } from './transition-styles.js';
|
|
9
|
-
import { resolveSlideScenes, sourceTimeMs,
|
|
9
|
+
import { resolveSlideScenes, resolveTransitions, sourceTimeMs, computeOutputSegments, remapEvents, } from './time-remap.js';
|
|
10
|
+
const IMG_STYLE = { width: '100%', height: '100%', display: 'block' };
|
|
11
|
+
const JITTER_MS = 50;
|
|
10
12
|
export const DemoVideo = ({ timeline, branding }) => {
|
|
11
13
|
const fps = 30;
|
|
12
14
|
const frame = useCurrentFrame();
|
|
13
15
|
const outputTimeMs = (frame / fps) * 1000;
|
|
14
16
|
const scenes = timeline.events.filter((e) => e.type === 'scene');
|
|
15
|
-
const slideScenes = resolveSlideScenes(scenes
|
|
16
|
-
const
|
|
17
|
-
|
|
17
|
+
const slideScenes = resolveSlideScenes(scenes);
|
|
18
|
+
const resolvedTransitions = resolveTransitions(timeline.events);
|
|
19
|
+
const hasInsertions = slideScenes.length > 0 || resolvedTransitions.length > 0;
|
|
20
|
+
const timeMs = hasInsertions
|
|
21
|
+
? sourceTimeMs(outputTimeMs, slideScenes, resolvedTransitions)
|
|
18
22
|
: outputTimeMs;
|
|
19
|
-
const eventsToUse =
|
|
20
|
-
? remapEvents(timeline.events, slideScenes)
|
|
23
|
+
const eventsToUse = hasInsertions
|
|
24
|
+
? remapEvents(timeline.events, slideScenes, resolvedTransitions)
|
|
21
25
|
: timeline.events;
|
|
22
26
|
const cursorEvents = precomputeCursorPaths(eventsToUse.filter((e) => e.type === 'cursor_target'));
|
|
23
|
-
const transitionEvents = eventsToUse.filter((e) => e.type === 'transition');
|
|
24
27
|
const clickEvents = eventsToUse.filter((e) => e.type === 'action' && e.action === 'click');
|
|
25
28
|
const narrations = eventsToUse.filter((e) => e.type === 'narration');
|
|
26
29
|
const { frameManifest, videoFile } = timeline.metadata;
|
|
27
|
-
const slideSegments =
|
|
30
|
+
const { slides: slideSegments, transitions: transitionSegments } = computeOutputSegments(scenes, resolvedTransitions);
|
|
28
31
|
function resolveSlideProps(seg) {
|
|
29
32
|
return {
|
|
30
33
|
title: seg.sceneTitle,
|
|
@@ -37,24 +40,8 @@ export const DemoVideo = ({ timeline, branding }) => {
|
|
|
37
40
|
}
|
|
38
41
|
// Check if current time is inside a slide
|
|
39
42
|
let activeSlide = slideSegments.find(s => outputTimeMs >= s.slideStartMs && outputTimeMs < s.slideEndMs);
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
if (!activeSlide) {
|
|
43
|
-
for (const t of transitionEvents) {
|
|
44
|
-
if (outputTimeMs < t.timestampMs || outputTimeMs >= t.timestampMs + t.durationMs)
|
|
45
|
-
continue;
|
|
46
|
-
const tEnd = t.timestampMs + t.durationMs;
|
|
47
|
-
const before = slideSegments.find(s => Math.abs(s.slideEndMs - t.timestampMs) < 50) ?? null;
|
|
48
|
-
const after = slideSegments.find(s => Math.abs(s.slideStartMs - tEnd) < 50) ?? null;
|
|
49
|
-
if (before || after) {
|
|
50
|
-
slideTransition = { transition: t, before, after };
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// Fallback: if no exact match but slides exist, snap to the nearest slide.
|
|
56
|
-
// Handles timing jitter gaps (e.g. first frame at t=0 when first slide starts at t=1).
|
|
57
|
-
if (!activeSlide && !slideTransition && slideSegments.length > 0) {
|
|
43
|
+
// Fallback: snap to nearest slide within 100ms jitter
|
|
44
|
+
if (!activeSlide && slideSegments.length > 0) {
|
|
58
45
|
let best = slideSegments[0];
|
|
59
46
|
let bestDist = Infinity;
|
|
60
47
|
for (const s of slideSegments) {
|
|
@@ -71,49 +58,44 @@ export const DemoVideo = ({ timeline, branding }) => {
|
|
|
71
58
|
if (bestDist < 100)
|
|
72
59
|
activeSlide = best;
|
|
73
60
|
}
|
|
74
|
-
//
|
|
75
|
-
const activeTransition = !activeSlide
|
|
76
|
-
?
|
|
61
|
+
// Check if current time is inside a transition segment
|
|
62
|
+
const activeTransition = !activeSlide
|
|
63
|
+
? transitionSegments.find(t => outputTimeMs >= t.outputStartMs && outputTimeMs < t.outputEndMs)
|
|
77
64
|
: null;
|
|
78
65
|
let baseLayer;
|
|
79
66
|
if (activeSlide) {
|
|
80
|
-
// Slide IS the base layer — no frame images
|
|
81
67
|
baseLayer = _jsx(SceneSlide, { ...resolveSlideProps(activeSlide) });
|
|
82
68
|
}
|
|
83
|
-
else if (
|
|
84
|
-
|
|
85
|
-
const { transition: t, before, after } = slideTransition;
|
|
86
|
-
const progress = (outputTimeMs - t.timestampMs) / t.durationMs;
|
|
69
|
+
else if (activeTransition && frameManifest && frameManifest.length > 0) {
|
|
70
|
+
const progress = (outputTimeMs - activeTransition.outputStartMs) / activeTransition.durationMs;
|
|
87
71
|
const { width: vw } = timeline.metadata.viewport;
|
|
88
|
-
const styles = getTransitionStyles(
|
|
72
|
+
const styles = getTransitionStyles(activeTransition.transition, progress, vw);
|
|
89
73
|
const faceClip = styles.container ? {} : { overflow: 'hidden' };
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
// When no slide follows and no scene/action follows, leave empty so backdrop shows.
|
|
96
|
-
let entranceContent;
|
|
97
|
-
if (after) {
|
|
98
|
-
entranceContent = _jsx(SceneSlide, { ...resolveSlideProps(after) });
|
|
74
|
+
// Resolve exit content (before the transition)
|
|
75
|
+
const beforeSlide = slideSegments.find(s => Math.abs(s.slideEndMs - activeTransition.outputStartMs) < JITTER_MS);
|
|
76
|
+
let exitContent;
|
|
77
|
+
if (!activeTransition.hasContentBefore && !beforeSlide) {
|
|
78
|
+
exitContent = undefined; // scenario start edge — backdrop
|
|
99
79
|
}
|
|
100
|
-
else if (
|
|
101
|
-
|
|
102
|
-
? sourceTimeMs(tEnd, slideScenes) : tEnd;
|
|
103
|
-
const afterEntry = findClosestFrame(frameManifest, afterSourceTime);
|
|
104
|
-
entranceContent = _jsx(Img, { src: staticFile(afterEntry.file), style: imgStyle });
|
|
80
|
+
else if (beforeSlide) {
|
|
81
|
+
exitContent = _jsx(SceneSlide, { ...resolveSlideProps(beforeSlide) });
|
|
105
82
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
83
|
+
else {
|
|
84
|
+
const beforeEntry = findClosestFrame(frameManifest, activeTransition.beforeSourceMs);
|
|
85
|
+
exitContent = _jsx(Img, { src: staticFile(beforeEntry.file), style: IMG_STYLE });
|
|
86
|
+
}
|
|
87
|
+
// Resolve entrance content (after the transition)
|
|
88
|
+
const afterSlide = slideSegments.find(s => Math.abs(s.slideStartMs - activeTransition.outputEndMs) < JITTER_MS);
|
|
89
|
+
let entranceContent;
|
|
90
|
+
if (!activeTransition.hasContentAfter && !afterSlide) {
|
|
91
|
+
entranceContent = undefined; // scenario end edge — backdrop
|
|
111
92
|
}
|
|
112
|
-
else if (
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
93
|
+
else if (afterSlide) {
|
|
94
|
+
entranceContent = _jsx(SceneSlide, { ...resolveSlideProps(afterSlide) });
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const afterEntry = findClosestFrame(frameManifest, activeTransition.afterSourceMs);
|
|
98
|
+
entranceContent = _jsx(Img, { src: staticFile(afterEntry.file), style: IMG_STYLE });
|
|
117
99
|
}
|
|
118
100
|
const faces = (_jsxs(_Fragment, { children: [_jsx("div", { style: { position: 'absolute', inset: 0, ...faceClip, ...styles.entrance }, children: entranceContent }), _jsx("div", { style: { position: 'absolute', inset: 0, ...faceClip, ...styles.exit }, children: exitContent }), styles.exit2 && (_jsx("div", { style: { position: 'absolute', inset: 0, ...faceClip, ...styles.exit2 }, children: exitContent }))] }));
|
|
119
101
|
let wrappedFaces = faces;
|
|
@@ -123,26 +105,15 @@ export const DemoVideo = ({ timeline, branding }) => {
|
|
|
123
105
|
if (styles.perspective) {
|
|
124
106
|
wrappedFaces = _jsx("div", { style: { position: 'absolute', inset: 0, perspective: styles.perspective }, children: wrappedFaces });
|
|
125
107
|
}
|
|
126
|
-
const
|
|
127
|
-
const backdropColor = styles.backdrop
|
|
128
|
-
?? (slideSide ? resolveSlideProps(slideSide).brandColor : '#000000');
|
|
108
|
+
const backdropColor = styles.backdrop ?? branding?.brandColor ?? '#000000';
|
|
129
109
|
baseLayer = (_jsxs(_Fragment, { children: [_jsx("div", { style: { position: 'absolute', inset: 0, backgroundColor: backdropColor } }), wrappedFaces] }));
|
|
130
110
|
}
|
|
131
|
-
else if (activeTransition && frameManifest && frameManifest.length > 0) {
|
|
132
|
-
const progress = (outputTimeMs - activeTransition.timestampMs) / activeTransition.durationMs;
|
|
133
|
-
const styles = getTransitionStyles(activeTransition.transition, progress, timeline.metadata.viewport.width);
|
|
134
|
-
const beforeSourceTime = slideScenes.length > 0
|
|
135
|
-
? sourceTimeMs(activeTransition.timestampMs, slideScenes)
|
|
136
|
-
: activeTransition.timestampMs;
|
|
137
|
-
const beforeEntry = findClosestFrame(frameManifest, beforeSourceTime);
|
|
138
|
-
const afterEntry = findClosestFrame(frameManifest, timeMs);
|
|
139
|
-
baseLayer = (_jsxs(_Fragment, { children: [_jsx(Img, { src: staticFile(afterEntry.file), style: { position: 'absolute', width: '100%', height: '100%', display: 'block', ...styles.entrance } }), _jsx(Img, { src: staticFile(beforeEntry.file), style: { position: 'absolute', width: '100%', height: '100%', display: 'block', ...styles.exit } }), styles.exit2 && (_jsx(Img, { src: staticFile(beforeEntry.file), style: { position: 'absolute', width: '100%', height: '100%', display: 'block', ...styles.exit2 } }))] }));
|
|
140
|
-
}
|
|
141
111
|
else if (frameManifest && frameManifest.length > 0) {
|
|
142
112
|
const entry = findClosestFrame(frameManifest, timeMs);
|
|
143
|
-
baseLayer = (_jsx(Img, { src: staticFile(entry.file), style:
|
|
113
|
+
baseLayer = (_jsx(Img, { src: staticFile(entry.file), style: IMG_STYLE }));
|
|
144
114
|
}
|
|
145
115
|
else if (videoFile) {
|
|
116
|
+
const transitionEvents = timeline.events.filter((e) => e.type === 'transition');
|
|
146
117
|
if (transitionEvents.length > 0 && frame === 0) {
|
|
147
118
|
console.warn('sw.transition() effects require frame-based capture (captureMode: "frame"). Transitions will be ignored with video-based capture.');
|
|
148
119
|
}
|
|
@@ -156,6 +127,6 @@ export const DemoVideo = ({ timeline, branding }) => {
|
|
|
156
127
|
width: timeline.metadata.viewport.width,
|
|
157
128
|
height: timeline.metadata.viewport.height,
|
|
158
129
|
overflow: 'hidden',
|
|
159
|
-
}, children: [baseLayer, !activeSlide && !
|
|
130
|
+
}, children: [baseLayer, !activeSlide && !activeTransition && (_jsx(CursorOverlay, { cursorEvents: cursorEvents, clickEvents: clickEvents, fps: fps })), _jsx(NarrationTrack, { narrations: narrations, fps: fps })] }));
|
|
160
131
|
};
|
|
161
132
|
//# sourceMappingURL=DemoVideo.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DemoVideo.js","sourceRoot":"","sources":["../../../src/composition/DemoVideo.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,cAAc,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAI5E,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,
|
|
1
|
+
{"version":3,"file":"DemoVideo.js","sourceRoot":"","sources":["../../../src/composition/DemoVideo.tsx"],"names":[],"mappings":"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"}
|
|
@@ -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;AAc1B,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"remotion-root.d.ts","sourceRoot":"","sources":["../../../src/composition/remotion-root.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAc1B,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EA4ChC,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 { resolveSlideScenes, totalSlideDurationMs, msToFrames } from './time-remap.js';
|
|
7
|
+
import { resolveSlideScenes, resolveTransitions, totalSlideDurationMs, totalTransitionDurationMs, msToFrames } from './time-remap.js';
|
|
8
8
|
const propsSchema = z.object({
|
|
9
9
|
timeline: timelineSchema,
|
|
10
10
|
branding: brandingSchema.optional(),
|
|
@@ -27,7 +27,10 @@ export const RemotionRoot = () => {
|
|
|
27
27
|
const fps = 30;
|
|
28
28
|
const scenes = props.timeline.events.filter((e) => e.type === 'scene');
|
|
29
29
|
const slideScenes = resolveSlideScenes(scenes);
|
|
30
|
-
const
|
|
30
|
+
const resolvedTransitions = resolveTransitions(props.timeline.events);
|
|
31
|
+
const totalMs = props.timeline.metadata.videoDurationMs
|
|
32
|
+
+ totalSlideDurationMs(slideScenes)
|
|
33
|
+
+ totalTransitionDurationMs(resolvedTransitions);
|
|
31
34
|
const durationInFrames = Math.max(30, msToFrames(totalMs, fps));
|
|
32
35
|
return {
|
|
33
36
|
durationInFrames,
|
|
@@ -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;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,iBAAiB,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;AAE3C,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAEtI,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,eAAe,EAAE,CAAC;wBAClB,SAAS,EAAE,kBAAkB;qBAC9B;oBACD,MAAM,EAAE,EAAE;iBACX;aACF,EACD,iBAAiB,EAAE,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE;gBAC/B,MAAM,GAAG,GAAG,EAAE,CAAC;gBACf,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAmB,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;gBACxF,MAAM,WAAW,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;gBAC/C,MAAM,mBAAmB,GAAG,kBAAkB,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACtE,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,eAAe;sBACnD,oBAAoB,CAAC,WAAW,CAAC;sBACjC,yBAAyB,CAAC,mBAAmB,CAAC,CAAC;gBACnD,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;gBAChE,OAAO;oBACL,gBAAgB;oBAChB,GAAG;oBACH,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,11 +1,20 @@
|
|
|
1
|
-
import type { SceneEvent, SceneSlideConfig, TimelineEvent } from '../timeline/types.js';
|
|
1
|
+
import type { SceneEvent, SceneSlideConfig, TimelineEvent, TransitionType } from '../timeline/types.js';
|
|
2
2
|
export declare const DEFAULT_SLIDE_DURATION_MS = 2000;
|
|
3
3
|
export declare function msToFrames(ms: number, fps: number): number;
|
|
4
4
|
export interface ResolvedSlideScene {
|
|
5
5
|
timestampMs: number;
|
|
6
6
|
slideDurationMs: number;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
}
|
|
8
|
+
export interface ResolvedTransition {
|
|
9
|
+
timestampMs: number;
|
|
10
|
+
transitionDurationMs: number;
|
|
11
|
+
transition: TransitionType;
|
|
12
|
+
beforeSourceMs: number;
|
|
13
|
+
afterSourceMs: number;
|
|
14
|
+
/** True when a visual action exists before this transition. */
|
|
15
|
+
hasContentBefore: boolean;
|
|
16
|
+
/** True when a visual action exists after this transition. */
|
|
17
|
+
hasContentAfter: boolean;
|
|
9
18
|
}
|
|
10
19
|
export interface SlideSegment {
|
|
11
20
|
slideStartMs: number;
|
|
@@ -15,28 +24,46 @@ export interface SlideSegment {
|
|
|
15
24
|
sceneDescription?: string;
|
|
16
25
|
slideConfig: SceneSlideConfig;
|
|
17
26
|
}
|
|
27
|
+
export interface TransitionSegment {
|
|
28
|
+
outputStartMs: number;
|
|
29
|
+
outputEndMs: number;
|
|
30
|
+
durationMs: number;
|
|
31
|
+
transition: TransitionType;
|
|
32
|
+
beforeSourceMs: number;
|
|
33
|
+
afterSourceMs: number;
|
|
34
|
+
hasContentBefore: boolean;
|
|
35
|
+
hasContentAfter: boolean;
|
|
36
|
+
}
|
|
18
37
|
/**
|
|
19
38
|
* Filter scenes that have a `slide` field and resolve their duration.
|
|
20
|
-
* When allEvents is provided, also computes the dead zone after each slide
|
|
21
|
-
* (stale source time from transition waits + navigate page loads).
|
|
22
39
|
*/
|
|
23
|
-
export declare function resolveSlideScenes(scenes: SceneEvent[]
|
|
40
|
+
export declare function resolveSlideScenes(scenes: SceneEvent[]): ResolvedSlideScene[];
|
|
24
41
|
/**
|
|
25
|
-
*
|
|
26
|
-
*
|
|
42
|
+
* Walk timeline events and resolve each transition's frame references.
|
|
43
|
+
* beforeSourceMs = settledAtMs (or timestampMs) of the last visual action before it.
|
|
44
|
+
* afterSourceMs = settledAtMs (or timestampMs) of the first visual action after it.
|
|
27
45
|
*/
|
|
28
|
-
export declare function
|
|
46
|
+
export declare function resolveTransitions(events: TimelineEvent[]): ResolvedTransition[];
|
|
29
47
|
/**
|
|
30
48
|
* Map an output-time position back to its source-time position.
|
|
31
|
-
* During
|
|
32
|
-
*
|
|
33
|
-
* Source times that fall in a dead zone are clamped to its end.
|
|
49
|
+
* During an insertion (slide or transition), returns the insertion's
|
|
50
|
+
* source timestamp (freeze-frame). Otherwise subtracts accumulated offsets.
|
|
34
51
|
*/
|
|
35
|
-
export declare function sourceTimeMs(outputTimeMs: number, slideScenes: ResolvedSlideScene[]): number;
|
|
52
|
+
export declare function sourceTimeMs(outputTimeMs: number, slideScenes: ResolvedSlideScene[], transitions?: ResolvedTransition[]): number;
|
|
36
53
|
export declare function totalSlideDurationMs(slideScenes: ResolvedSlideScene[]): number;
|
|
54
|
+
export declare function totalTransitionDurationMs(transitions: ResolvedTransition[]): number;
|
|
55
|
+
/**
|
|
56
|
+
* Compute output-time intervals for both slides and transitions,
|
|
57
|
+
* accounting for all preceding insertions.
|
|
58
|
+
*/
|
|
59
|
+
export declare function computeOutputSegments(scenes: SceneEvent[], transitions: ResolvedTransition[]): {
|
|
60
|
+
slides: SlideSegment[];
|
|
61
|
+
transitions: TransitionSegment[];
|
|
62
|
+
};
|
|
37
63
|
/**
|
|
38
|
-
* Shift every event's timestampMs forward by
|
|
39
|
-
* durations that precede it.
|
|
64
|
+
* Shift every event's timestampMs forward by accumulated insertion
|
|
65
|
+
* durations (slides + transitions) that precede it.
|
|
66
|
+
* Returns a new array (no mutation).
|
|
40
67
|
*/
|
|
41
|
-
export declare function remapEvents<T extends TimelineEvent>(events: T[], slideScenes: ResolvedSlideScene[]): T[];
|
|
68
|
+
export declare function remapEvents<T extends TimelineEvent>(events: T[], slideScenes: ResolvedSlideScene[], transitions?: ResolvedTransition[]): T[];
|
|
42
69
|
//# sourceMappingURL=time-remap.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"time-remap.d.ts","sourceRoot":"","sources":["../../../src/composition/time-remap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAE,gBAAgB,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"time-remap.d.ts","sourceRoot":"","sources":["../../../src/composition/time-remap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,UAAU,EAAE,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAErH,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,cAAc,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,gBAAgB,EAAE,OAAO,CAAC;IAC1B,8DAA8D;IAC9D,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,gBAAgB,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,kBAAkB,EAAE,CAO7E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,kBAAkB,EAAE,CA0BhF;AAqCD;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,kBAAkB,EAAE,EACjC,WAAW,GAAE,kBAAkB,EAAO,GACrC,MAAM,CAWR;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAI9E;AAED,wBAAgB,yBAAyB,CAAC,WAAW,EAAE,kBAAkB,EAAE,GAAG,MAAM,CAInF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,UAAU,EAAE,EACpB,WAAW,EAAE,kBAAkB,EAAE,GAChC;IAAE,MAAM,EAAE,YAAY,EAAE,CAAC;IAAC,WAAW,EAAE,iBAAiB,EAAE,CAAA;CAAE,CAmD9D;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,aAAa,EACjD,MAAM,EAAE,CAAC,EAAE,EACX,WAAW,EAAE,kBAAkB,EAAE,EACjC,WAAW,GAAE,kBAAkB,EAAO,GACrC,CAAC,EAAE,CAaL"}
|
|
@@ -4,109 +4,160 @@ export function msToFrames(ms, fps) {
|
|
|
4
4
|
}
|
|
5
5
|
/**
|
|
6
6
|
* Filter scenes that have a `slide` field and resolve their duration.
|
|
7
|
-
* When allEvents is provided, also computes the dead zone after each slide
|
|
8
|
-
* (stale source time from transition waits + navigate page loads).
|
|
9
7
|
*/
|
|
10
|
-
export function resolveSlideScenes(scenes
|
|
8
|
+
export function resolveSlideScenes(scenes) {
|
|
11
9
|
return scenes
|
|
12
10
|
.filter(s => s.slide !== undefined)
|
|
13
|
-
.map(s => {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if (afterTrans) {
|
|
18
|
-
const transEnd = s.timestampMs + afterTrans.durationMs;
|
|
19
|
-
const firstAction = allEvents.find((e) => e.type === 'action' && e.timestampMs >= transEnd - 50);
|
|
20
|
-
if (firstAction?.action === 'navigate') {
|
|
21
|
-
const settled = allEvents.find(e => e.timestampMs > firstAction.timestampMs + 50);
|
|
22
|
-
deadAfterMs = settled
|
|
23
|
-
? settled.timestampMs - s.timestampMs
|
|
24
|
-
: afterTrans.durationMs;
|
|
25
|
-
}
|
|
26
|
-
else {
|
|
27
|
-
deadAfterMs = afterTrans.durationMs;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
timestampMs: s.timestampMs,
|
|
33
|
-
slideDurationMs: s.slide.duration ?? DEFAULT_SLIDE_DURATION_MS,
|
|
34
|
-
deadAfterMs,
|
|
35
|
-
};
|
|
36
|
-
});
|
|
11
|
+
.map(s => ({
|
|
12
|
+
timestampMs: s.timestampMs,
|
|
13
|
+
slideDurationMs: s.slide.duration ?? DEFAULT_SLIDE_DURATION_MS,
|
|
14
|
+
}));
|
|
37
15
|
}
|
|
38
16
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
17
|
+
* Walk timeline events and resolve each transition's frame references.
|
|
18
|
+
* beforeSourceMs = settledAtMs (or timestampMs) of the last visual action before it.
|
|
19
|
+
* afterSourceMs = settledAtMs (or timestampMs) of the first visual action after it.
|
|
41
20
|
*/
|
|
42
|
-
export function
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
for (const
|
|
46
|
-
if (
|
|
21
|
+
export function resolveTransitions(events) {
|
|
22
|
+
const result = [];
|
|
23
|
+
const actions = events.filter((e) => e.type === 'action');
|
|
24
|
+
for (const event of events) {
|
|
25
|
+
if (event.type !== 'transition')
|
|
47
26
|
continue;
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
27
|
+
const lastBefore = findLastAction(actions, event.timestampMs);
|
|
28
|
+
const firstAfter = actions.find(a => a.timestampMs > event.timestampMs) ?? null;
|
|
29
|
+
result.push({
|
|
30
|
+
timestampMs: event.timestampMs,
|
|
31
|
+
transitionDurationMs: event.durationMs,
|
|
32
|
+
transition: event.transition,
|
|
33
|
+
beforeSourceMs: lastBefore
|
|
34
|
+
? (lastBefore.settledAtMs ?? lastBefore.timestampMs)
|
|
35
|
+
: event.timestampMs,
|
|
36
|
+
afterSourceMs: firstAfter
|
|
37
|
+
? (firstAfter.settledAtMs ?? firstAfter.timestampMs)
|
|
38
|
+
: event.timestampMs,
|
|
39
|
+
hasContentBefore: lastBefore !== null,
|
|
40
|
+
hasContentAfter: firstAfter !== null,
|
|
57
41
|
});
|
|
58
|
-
accumulated += slideDurationMs;
|
|
59
42
|
}
|
|
60
|
-
return
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function findLastAction(actions, beforeOrAt) {
|
|
46
|
+
let last = null;
|
|
47
|
+
for (const a of actions) {
|
|
48
|
+
if (a.timestampMs <= beforeOrAt)
|
|
49
|
+
last = a;
|
|
50
|
+
else
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
return last;
|
|
54
|
+
}
|
|
55
|
+
function buildSortedInsertions(slideScenes, transitions) {
|
|
56
|
+
const ins = [];
|
|
57
|
+
for (const ss of slideScenes) {
|
|
58
|
+
ins.push({ sourceTimeMs: ss.timestampMs, durationMs: ss.slideDurationMs, kind: 'slide' });
|
|
59
|
+
}
|
|
60
|
+
for (const t of transitions) {
|
|
61
|
+
ins.push({ sourceTimeMs: t.timestampMs, durationMs: t.transitionDurationMs, kind: 'transition' });
|
|
62
|
+
}
|
|
63
|
+
// Slides sort before transitions at the same source time; stable for same-kind ties.
|
|
64
|
+
ins.sort((a, b) => a.sourceTimeMs - b.sourceTimeMs || (a.kind === b.kind ? 0 : a.kind === 'slide' ? -1 : 1));
|
|
65
|
+
return ins;
|
|
61
66
|
}
|
|
62
67
|
/**
|
|
63
68
|
* Map an output-time position back to its source-time position.
|
|
64
|
-
* During
|
|
65
|
-
*
|
|
66
|
-
* Source times that fall in a dead zone are clamped to its end.
|
|
69
|
+
* During an insertion (slide or transition), returns the insertion's
|
|
70
|
+
* source timestamp (freeze-frame). Otherwise subtracts accumulated offsets.
|
|
67
71
|
*/
|
|
68
|
-
export function sourceTimeMs(outputTimeMs, slideScenes) {
|
|
69
|
-
const
|
|
72
|
+
export function sourceTimeMs(outputTimeMs, slideScenes, transitions = []) {
|
|
73
|
+
const insertions = buildSortedInsertions(slideScenes, transitions);
|
|
70
74
|
let accumulated = 0;
|
|
71
|
-
for (const
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
if (outputTimeMs <
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
accumulated += ss.slideDurationMs;
|
|
81
|
-
}
|
|
82
|
-
return clampDeadZones(outputTimeMs - accumulated, sorted);
|
|
83
|
-
}
|
|
84
|
-
function clampDeadZones(sourceTime, slides) {
|
|
85
|
-
for (const ss of slides) {
|
|
86
|
-
if (ss.deadAfterMs > 0 && sourceTime >= ss.timestampMs && sourceTime < ss.timestampMs + ss.deadAfterMs) {
|
|
87
|
-
return ss.timestampMs + ss.deadAfterMs;
|
|
88
|
-
}
|
|
75
|
+
for (const ins of insertions) {
|
|
76
|
+
const start = ins.sourceTimeMs + accumulated;
|
|
77
|
+
const end = start + ins.durationMs;
|
|
78
|
+
if (outputTimeMs < start)
|
|
79
|
+
return outputTimeMs - accumulated;
|
|
80
|
+
if (outputTimeMs < end)
|
|
81
|
+
return ins.sourceTimeMs;
|
|
82
|
+
accumulated += ins.durationMs;
|
|
89
83
|
}
|
|
90
|
-
return
|
|
84
|
+
return outputTimeMs - accumulated;
|
|
91
85
|
}
|
|
92
86
|
export function totalSlideDurationMs(slideScenes) {
|
|
93
87
|
let total = 0;
|
|
94
|
-
for (const ss of slideScenes)
|
|
88
|
+
for (const ss of slideScenes)
|
|
95
89
|
total += ss.slideDurationMs;
|
|
96
|
-
}
|
|
97
90
|
return total;
|
|
98
91
|
}
|
|
92
|
+
export function totalTransitionDurationMs(transitions) {
|
|
93
|
+
let total = 0;
|
|
94
|
+
for (const t of transitions)
|
|
95
|
+
total += t.transitionDurationMs;
|
|
96
|
+
return total;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Compute output-time intervals for both slides and transitions,
|
|
100
|
+
* accounting for all preceding insertions.
|
|
101
|
+
*/
|
|
102
|
+
export function computeOutputSegments(scenes, transitions) {
|
|
103
|
+
const slideScenes = resolveSlideScenes(scenes);
|
|
104
|
+
const insertions = buildSortedInsertions(slideScenes, transitions);
|
|
105
|
+
// Build queues (same order as sorted insertions) so we pop metadata
|
|
106
|
+
// in order even when multiple insertions share the same timestamp.
|
|
107
|
+
const slideQueue = scenes.filter(s => s.slide).sort((a, b) => a.timestampMs - b.timestampMs);
|
|
108
|
+
const transQueue = [...transitions].sort((a, b) => a.timestampMs - b.timestampMs);
|
|
109
|
+
let si = 0;
|
|
110
|
+
let ti = 0;
|
|
111
|
+
const slides = [];
|
|
112
|
+
const transSegs = [];
|
|
113
|
+
let accumulated = 0;
|
|
114
|
+
for (const ins of insertions) {
|
|
115
|
+
const outputStart = ins.sourceTimeMs + accumulated;
|
|
116
|
+
const outputEnd = outputStart + ins.durationMs;
|
|
117
|
+
if (ins.kind === 'slide') {
|
|
118
|
+
const sc = slideQueue[si++];
|
|
119
|
+
if (sc) {
|
|
120
|
+
slides.push({
|
|
121
|
+
slideStartMs: outputStart,
|
|
122
|
+
slideEndMs: outputEnd,
|
|
123
|
+
slideDurationMs: ins.durationMs,
|
|
124
|
+
sceneTitle: sc.title,
|
|
125
|
+
sceneDescription: sc.description,
|
|
126
|
+
slideConfig: sc.slide,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
const rt = transQueue[ti++];
|
|
132
|
+
if (rt) {
|
|
133
|
+
transSegs.push({
|
|
134
|
+
outputStartMs: outputStart,
|
|
135
|
+
outputEndMs: outputEnd,
|
|
136
|
+
durationMs: ins.durationMs,
|
|
137
|
+
transition: rt.transition,
|
|
138
|
+
beforeSourceMs: rt.beforeSourceMs,
|
|
139
|
+
afterSourceMs: rt.afterSourceMs,
|
|
140
|
+
hasContentBefore: rt.hasContentBefore,
|
|
141
|
+
hasContentAfter: rt.hasContentAfter,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
accumulated += ins.durationMs;
|
|
146
|
+
}
|
|
147
|
+
return { slides, transitions: transSegs };
|
|
148
|
+
}
|
|
99
149
|
/**
|
|
100
|
-
* Shift every event's timestampMs forward by
|
|
101
|
-
* durations that precede it.
|
|
150
|
+
* Shift every event's timestampMs forward by accumulated insertion
|
|
151
|
+
* durations (slides + transitions) that precede it.
|
|
152
|
+
* Returns a new array (no mutation).
|
|
102
153
|
*/
|
|
103
|
-
export function remapEvents(events, slideScenes) {
|
|
104
|
-
const
|
|
154
|
+
export function remapEvents(events, slideScenes, transitions = []) {
|
|
155
|
+
const insertions = buildSortedInsertions(slideScenes, transitions);
|
|
105
156
|
return events.map(event => {
|
|
106
157
|
let offset = 0;
|
|
107
|
-
for (const
|
|
108
|
-
if (event.timestampMs >=
|
|
109
|
-
offset +=
|
|
158
|
+
for (const ins of insertions) {
|
|
159
|
+
if (event.timestampMs >= ins.sourceTimeMs) {
|
|
160
|
+
offset += ins.durationMs;
|
|
110
161
|
}
|
|
111
162
|
else {
|
|
112
163
|
break;
|