visualfries 0.1.10120 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -0
- package/bin/visualfries.js +1570 -0
- package/dist/DIContainer.d.ts +2 -1
- package/dist/DIContainer.js +16 -5
- package/dist/SceneBuilder.svelte.d.ts +70 -1
- package/dist/SceneBuilder.svelte.js +96 -29
- package/dist/agent/audioMixer.d.ts +34 -0
- package/dist/agent/audioMixer.js +308 -0
- package/dist/agent/broll.d.ts +35 -0
- package/dist/agent/broll.js +226 -0
- package/dist/agent/capabilities.d.ts +34 -0
- package/dist/agent/capabilities.js +246 -0
- package/dist/agent/captionScene.d.ts +23 -0
- package/dist/agent/captionScene.js +204 -0
- package/dist/agent/catalog.d.ts +24 -0
- package/dist/agent/catalog.js +67 -0
- package/dist/agent/cueFiles.d.ts +54 -0
- package/dist/agent/cueFiles.js +219 -0
- package/dist/agent/cuePresets.d.ts +7 -0
- package/dist/agent/cuePresets.js +50 -0
- package/dist/agent/deterministicMedia.d.ts +61 -0
- package/dist/agent/deterministicMedia.js +303 -0
- package/dist/agent/index.d.ts +18 -0
- package/dist/agent/index.js +18 -0
- package/dist/agent/inspectScene.d.ts +36 -0
- package/dist/agent/inspectScene.js +139 -0
- package/dist/agent/localRender.d.ts +82 -0
- package/dist/agent/localRender.js +702 -0
- package/dist/agent/overlays.d.ts +40 -0
- package/dist/agent/overlays.js +325 -0
- package/dist/agent/productionPlan.d.ts +131 -0
- package/dist/agent/productionPlan.js +322 -0
- package/dist/agent/renderPlan.d.ts +27 -0
- package/dist/agent/renderPlan.js +68 -0
- package/dist/agent/renderRanges.d.ts +24 -0
- package/dist/agent/renderRanges.js +73 -0
- package/dist/agent/runtimeSupport.d.ts +19 -0
- package/dist/agent/runtimeSupport.js +192 -0
- package/dist/agent/streamingEncoder.d.ts +31 -0
- package/dist/agent/streamingEncoder.js +208 -0
- package/dist/agent/transcripts.d.ts +29 -0
- package/dist/agent/transcripts.js +146 -0
- package/dist/agent/transitions.d.ts +20 -0
- package/dist/agent/transitions.js +169 -0
- package/dist/animations/PixiAnimationTarget.d.ts +29 -0
- package/dist/animations/PixiAnimationTarget.js +63 -0
- package/dist/animations/builders/LineHighlighterAnimationBuilder.js +20 -3
- package/dist/animations/builders/WordHighlighterAnimationBuilder.js +15 -2
- package/dist/browser/browserExport.d.ts +73 -0
- package/dist/browser/browserExport.js +180 -0
- package/dist/browser/glTransitions.d.ts +37 -0
- package/dist/browser/glTransitions.js +261 -0
- package/dist/browser/index.d.ts +3 -0
- package/dist/browser/index.js +3 -0
- package/dist/browser/mediaDecode.d.ts +31 -0
- package/dist/browser/mediaDecode.js +152 -0
- package/dist/builders/PixiComponentBuilder.d.ts +6 -0
- package/dist/builders/PixiComponentBuilder.js +12 -0
- package/dist/builders/_ComponentState.svelte.d.ts +7 -5
- package/dist/builders/_ComponentState.svelte.js +29 -15
- package/dist/builders/html/StyleBuilder.js +9 -8
- package/dist/builders/html/TextComponentHtmlBuilder.js +4 -2
- package/dist/builders/html/processors/AppearanceStyleProcessor.js +1 -1
- package/dist/builders/html/processors/TextAppearanceStyleProcessor.js +7 -0
- package/dist/builders/html/processors/TextEffectsStyleProcessor.js +32 -13
- package/dist/commands/ReplaceSourceOnTimeCommand.js +9 -1
- package/dist/commands/SeekCommand.js +19 -6
- package/dist/components/AnimatedGIF.d.ts +3 -0
- package/dist/components/AnimatedGIF.js +28 -1
- package/dist/components/Component.svelte.d.ts +5 -5
- package/dist/components/Component.svelte.js +29 -13
- package/dist/components/ComponentContext.svelte.js +68 -23
- package/dist/components/hooks/AnimationHook.js +1 -1
- package/dist/components/hooks/CanvasFillHook.d.ts +12 -0
- package/dist/components/hooks/CanvasFillHook.js +75 -0
- package/dist/components/hooks/DeterministicMediaFrameHook.js +3 -0
- package/dist/components/hooks/HtmlTextHook.js +11 -3
- package/dist/components/hooks/HtmlToCanvasHook.js +8 -25
- package/dist/components/hooks/MediaHook.js +172 -47
- package/dist/components/hooks/MediaSeekingHook.js +138 -88
- package/dist/components/hooks/PixiGifHook.js +4 -4
- package/dist/components/hooks/PixiSplitScreenDisplayObjectHook.js +73 -6
- package/dist/components/hooks/PixiVideoTextureHook.js +10 -4
- package/dist/components/hooks/PixiVisualTransformHook.d.ts +16 -0
- package/dist/components/hooks/PixiVisualTransformHook.js +102 -0
- package/dist/components/hooks/SubtitlesHook.js +48 -24
- package/dist/components/hooks/VerifyMediaHook.js +2 -2
- package/dist/directors/ComponentDirector.d.ts +1 -0
- package/dist/directors/ComponentDirector.js +41 -8
- package/dist/fonts/fontDiscovery.js +12 -1
- package/dist/layers/Layer.svelte.d.ts +54 -0
- package/dist/layers/Layer.svelte.js +2 -25
- package/dist/managers/AppManager.svelte.js +8 -5
- package/dist/managers/ComponentsManager.svelte.d.ts +7 -7
- package/dist/managers/ComponentsManager.svelte.js +29 -15
- package/dist/managers/DeterministicMediaManager.js +38 -3
- package/dist/managers/EventManager.js +3 -1
- package/dist/managers/LayersManager.svelte.d.ts +54 -0
- package/dist/managers/RenderManager.d.ts +5 -0
- package/dist/managers/RenderManager.js +67 -21
- package/dist/managers/StateManager.svelte.d.ts +56 -0
- package/dist/managers/StateManager.svelte.js +60 -58
- package/dist/managers/SubtitlesManager.svelte.js +1 -3
- package/dist/managers/TimeManager.svelte.d.ts +4 -0
- package/dist/managers/TimeManager.svelte.js +23 -7
- package/dist/schemas/runtime/deterministic.js +5 -1
- package/dist/schemas/runtime/types.d.ts +36 -15
- package/dist/schemas/scene/components.d.ts +126 -0
- package/dist/schemas/scene/components.js +11 -0
- package/dist/schemas/scene/core.d.ts +109 -10
- package/dist/schemas/scene/core.js +1 -1
- package/dist/utils/mediaWindow.d.ts +13 -0
- package/dist/utils/mediaWindow.js +25 -0
- package/dist/utils/svgGenerator.js +1 -1
- package/dist/utils/timelineWindow.d.ts +2 -0
- package/dist/utils/timelineWindow.js +7 -0
- package/dist/utils/utils.js +2 -4
- package/docs/AGENT_PATTERNS.md +243 -0
- package/docs/AGENT_WORKFLOW.md +364 -0
- package/docs/AUTHORING_BEST_PRACTICES.md +188 -0
- package/docs/AUTHORING_SYSTEM_AUDIT.md +98 -0
- package/docs/BROWSER_EXPORT_SPIKE.md +55 -0
- package/docs/DETERMINISTIC_RENDERER.md +83 -0
- package/docs/NEXT_RELEASE.md +28 -0
- package/docs/PROPOSED_VISUALFRIES_SKILL.md +68 -0
- package/docs/examples/agent-cues.json +49 -0
- package/docs/examples/agent-transcript.json +23 -0
- package/docs/examples/agent-transcript.srt +7 -0
- package/docs/examples/agent-transcript.vtt +7 -0
- package/docs/examples/image-animation.json +87 -0
- package/docs/examples/native-text-metric-badge.json +109 -0
- package/package.json +133 -100
- package/skills/visualfries/SKILL.md +328 -0
|
@@ -16,6 +16,7 @@ const createDefaultDiagnosticsState = () => ({
|
|
|
16
16
|
latencyMinMs: Number.POSITIVE_INFINITY,
|
|
17
17
|
latencyMaxMs: 0
|
|
18
18
|
});
|
|
19
|
+
const IMAGE_LOAD_TIMEOUT_MS = 5000;
|
|
19
20
|
export class DeterministicMediaManager {
|
|
20
21
|
config;
|
|
21
22
|
#provider;
|
|
@@ -319,10 +320,44 @@ export class DeterministicMediaManager {
|
|
|
319
320
|
async #loadImage(url) {
|
|
320
321
|
const img = new Image();
|
|
321
322
|
img.crossOrigin = 'anonymous';
|
|
322
|
-
img.src = url;
|
|
323
323
|
await new Promise((resolve, reject) => {
|
|
324
|
-
|
|
325
|
-
|
|
324
|
+
let settled = false;
|
|
325
|
+
const timeoutId = setTimeout(() => {
|
|
326
|
+
if (settled) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
settled = true;
|
|
330
|
+
cleanup();
|
|
331
|
+
try {
|
|
332
|
+
img.src = '';
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
// Ignore cancellation failures.
|
|
336
|
+
}
|
|
337
|
+
reject(new Error(`Timed out loading deterministic frame after ${IMAGE_LOAD_TIMEOUT_MS}ms: ${url}`));
|
|
338
|
+
}, IMAGE_LOAD_TIMEOUT_MS);
|
|
339
|
+
const cleanup = () => {
|
|
340
|
+
clearTimeout(timeoutId);
|
|
341
|
+
img.onload = null;
|
|
342
|
+
img.onerror = null;
|
|
343
|
+
};
|
|
344
|
+
img.onload = () => {
|
|
345
|
+
if (settled) {
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
settled = true;
|
|
349
|
+
cleanup();
|
|
350
|
+
resolve();
|
|
351
|
+
};
|
|
352
|
+
img.onerror = () => {
|
|
353
|
+
if (settled) {
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
settled = true;
|
|
357
|
+
cleanup();
|
|
358
|
+
reject(new Error(`Failed to load deterministic frame: ${url}`));
|
|
359
|
+
};
|
|
360
|
+
img.src = url;
|
|
326
361
|
});
|
|
327
362
|
return img;
|
|
328
363
|
}
|
|
@@ -17,6 +17,8 @@ export class EventManager extends EventTarget {
|
|
|
17
17
|
}
|
|
18
18
|
// "on" automatically returns the right type under event.details object
|
|
19
19
|
on(event, callback, options) {
|
|
20
|
-
|
|
20
|
+
// Register the original function identity so removeEventListener can reliably
|
|
21
|
+
// detach manager callbacks during scene teardown.
|
|
22
|
+
this.addEventListener(event, callback, options);
|
|
21
23
|
}
|
|
22
24
|
}
|
|
@@ -476,6 +476,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
476
476
|
unit: "px" | "em" | "rem" | "%";
|
|
477
477
|
} | null | undefined;
|
|
478
478
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
479
|
+
padding?: number | {
|
|
480
|
+
top: number;
|
|
481
|
+
right: number;
|
|
482
|
+
bottom: number;
|
|
483
|
+
left: number;
|
|
484
|
+
} | undefined;
|
|
479
485
|
shadow?: {
|
|
480
486
|
enabled?: boolean | undefined;
|
|
481
487
|
preset?: string | undefined;
|
|
@@ -1095,6 +1101,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
1095
1101
|
unit: "px" | "em" | "rem" | "%";
|
|
1096
1102
|
} | null | undefined;
|
|
1097
1103
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
1104
|
+
padding?: number | {
|
|
1105
|
+
top: number;
|
|
1106
|
+
right: number;
|
|
1107
|
+
bottom: number;
|
|
1108
|
+
left: number;
|
|
1109
|
+
} | undefined;
|
|
1098
1110
|
shadow?: {
|
|
1099
1111
|
enabled?: boolean | undefined;
|
|
1100
1112
|
preset?: string | undefined;
|
|
@@ -1688,6 +1700,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
1688
1700
|
unit: "px" | "em" | "rem" | "%";
|
|
1689
1701
|
} | null | undefined;
|
|
1690
1702
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
1703
|
+
padding?: number | {
|
|
1704
|
+
top: number;
|
|
1705
|
+
right: number;
|
|
1706
|
+
bottom: number;
|
|
1707
|
+
left: number;
|
|
1708
|
+
} | undefined;
|
|
1691
1709
|
shadow?: {
|
|
1692
1710
|
enabled?: boolean | undefined;
|
|
1693
1711
|
preset?: string | undefined;
|
|
@@ -2279,6 +2297,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
2279
2297
|
unit: "px" | "em" | "rem" | "%";
|
|
2280
2298
|
} | null | undefined;
|
|
2281
2299
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
2300
|
+
padding?: number | {
|
|
2301
|
+
top: number;
|
|
2302
|
+
right: number;
|
|
2303
|
+
bottom: number;
|
|
2304
|
+
left: number;
|
|
2305
|
+
} | undefined;
|
|
2282
2306
|
shadow?: {
|
|
2283
2307
|
enabled?: boolean | undefined;
|
|
2284
2308
|
preset?: string | undefined;
|
|
@@ -2904,6 +2928,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
2904
2928
|
unit: "px" | "em" | "rem" | "%";
|
|
2905
2929
|
} | null | undefined;
|
|
2906
2930
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
2931
|
+
padding?: number | {
|
|
2932
|
+
top: number;
|
|
2933
|
+
right: number;
|
|
2934
|
+
bottom: number;
|
|
2935
|
+
left: number;
|
|
2936
|
+
} | undefined;
|
|
2907
2937
|
shadow?: {
|
|
2908
2938
|
enabled?: boolean | undefined;
|
|
2909
2939
|
preset?: string | undefined;
|
|
@@ -3499,6 +3529,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
3499
3529
|
unit: "px" | "em" | "rem" | "%";
|
|
3500
3530
|
} | null | undefined;
|
|
3501
3531
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
3532
|
+
padding?: number | {
|
|
3533
|
+
top: number;
|
|
3534
|
+
right: number;
|
|
3535
|
+
bottom: number;
|
|
3536
|
+
left: number;
|
|
3537
|
+
} | undefined;
|
|
3502
3538
|
shadow?: {
|
|
3503
3539
|
enabled?: boolean | undefined;
|
|
3504
3540
|
preset?: string | undefined;
|
|
@@ -4045,6 +4081,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
4045
4081
|
unit: "px" | "em" | "rem" | "%";
|
|
4046
4082
|
} | null | undefined;
|
|
4047
4083
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
4084
|
+
padding?: number | {
|
|
4085
|
+
top: number;
|
|
4086
|
+
right: number;
|
|
4087
|
+
bottom: number;
|
|
4088
|
+
left: number;
|
|
4089
|
+
} | undefined;
|
|
4048
4090
|
shadow?: {
|
|
4049
4091
|
enabled?: boolean | undefined;
|
|
4050
4092
|
preset?: string | undefined;
|
|
@@ -4596,6 +4638,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
4596
4638
|
unit: "px" | "em" | "rem" | "%";
|
|
4597
4639
|
} | null | undefined;
|
|
4598
4640
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
4641
|
+
padding?: number | {
|
|
4642
|
+
top: number;
|
|
4643
|
+
right: number;
|
|
4644
|
+
bottom: number;
|
|
4645
|
+
left: number;
|
|
4646
|
+
} | undefined;
|
|
4599
4647
|
shadow?: {
|
|
4600
4648
|
enabled?: boolean | undefined;
|
|
4601
4649
|
preset?: string | undefined;
|
|
@@ -5140,6 +5188,12 @@ export declare class LayersManager implements ResourceManager<ILayer, SceneLayer
|
|
|
5140
5188
|
unit: "px" | "em" | "rem" | "%";
|
|
5141
5189
|
} | null | undefined;
|
|
5142
5190
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
5191
|
+
padding?: number | {
|
|
5192
|
+
top: number;
|
|
5193
|
+
right: number;
|
|
5194
|
+
bottom: number;
|
|
5195
|
+
left: number;
|
|
5196
|
+
} | undefined;
|
|
5143
5197
|
shadow?: {
|
|
5144
5198
|
enabled?: boolean | undefined;
|
|
5145
5199
|
preset?: string | undefined;
|
|
@@ -13,6 +13,11 @@ export declare class RenderManager {
|
|
|
13
13
|
private layersManager;
|
|
14
14
|
private lastActiveById;
|
|
15
15
|
private lastRenderTime;
|
|
16
|
+
private renderInFlight;
|
|
17
|
+
private rerenderRequested;
|
|
18
|
+
private readonly boundHandleBeforeRender;
|
|
19
|
+
private readonly boundRender;
|
|
20
|
+
private readonly boundHandleChangeState;
|
|
16
21
|
constructor(cradle: {
|
|
17
22
|
stateManager: StateManager;
|
|
18
23
|
componentsManager: ResourceManager<IComponent, ComponentData, AppearanceInput>;
|
|
@@ -2,32 +2,41 @@ import { StateManager } from './StateManager.svelte.js';
|
|
|
2
2
|
import { EventManager } from './EventManager.js';
|
|
3
3
|
import { AppManager } from './AppManager.svelte.js';
|
|
4
4
|
import { LayersManager } from './LayersManager.svelte.js';
|
|
5
|
+
import { shouldPrepareMediaAtTime } from '../utils/mediaWindow.js';
|
|
6
|
+
import { isTimeWithinTimeline } from '../utils/timelineWindow.js';
|
|
5
7
|
export class RenderManager {
|
|
6
8
|
state;
|
|
7
9
|
componentsManager;
|
|
8
10
|
eventManager;
|
|
9
11
|
appManager;
|
|
10
12
|
layersManager;
|
|
11
|
-
// Track last
|
|
13
|
+
// Track last prepared/visible state so media hooks get one final update to release resources.
|
|
12
14
|
lastActiveById = new Map();
|
|
13
15
|
lastRenderTime = -1;
|
|
16
|
+
renderInFlight = null;
|
|
17
|
+
rerenderRequested = false;
|
|
18
|
+
boundHandleBeforeRender;
|
|
19
|
+
boundRender;
|
|
20
|
+
boundHandleChangeState;
|
|
14
21
|
constructor(cradle) {
|
|
15
22
|
this.state = cradle.stateManager;
|
|
16
23
|
this.componentsManager = cradle.componentsManager;
|
|
17
24
|
this.eventManager = cradle.eventManager;
|
|
18
25
|
this.appManager = cradle.appManager;
|
|
19
26
|
this.layersManager = cradle.layersManager;
|
|
27
|
+
this.boundHandleBeforeRender = this.handleBeforeRender.bind(this);
|
|
28
|
+
this.boundRender = this.render.bind(this);
|
|
29
|
+
this.boundHandleChangeState = (event) => {
|
|
30
|
+
if (event.detail.state !== 'playing')
|
|
31
|
+
void this.render();
|
|
32
|
+
};
|
|
20
33
|
this.initializeEventListeners();
|
|
21
34
|
}
|
|
22
35
|
initializeEventListeners() {
|
|
23
|
-
this.eventManager.on('beforerender', this.
|
|
24
|
-
this.eventManager.on('timeupdate', this.
|
|
25
|
-
this.eventManager.on('rerender', this.
|
|
26
|
-
this.eventManager.on('changestate',
|
|
27
|
-
if (event.detail.state !== 'playing') {
|
|
28
|
-
this.render();
|
|
29
|
-
}
|
|
30
|
-
});
|
|
36
|
+
this.eventManager.on('beforerender', this.boundHandleBeforeRender);
|
|
37
|
+
this.eventManager.on('timeupdate', this.boundRender);
|
|
38
|
+
this.eventManager.on('rerender', this.boundRender);
|
|
39
|
+
this.eventManager.on('changestate', this.boundHandleChangeState);
|
|
31
40
|
}
|
|
32
41
|
async handleBeforeRender() { }
|
|
33
42
|
#syncLayerDisplayObjects() {
|
|
@@ -43,33 +52,70 @@ export class RenderManager {
|
|
|
43
52
|
}
|
|
44
53
|
}
|
|
45
54
|
async render() {
|
|
55
|
+
if (this.renderInFlight) {
|
|
56
|
+
this.rerenderRequested = true;
|
|
57
|
+
return this.renderInFlight;
|
|
58
|
+
}
|
|
59
|
+
this.renderInFlight = this.#drainRenderQueue();
|
|
60
|
+
return this.renderInFlight;
|
|
61
|
+
}
|
|
62
|
+
async #drainRenderQueue() {
|
|
63
|
+
try {
|
|
64
|
+
do {
|
|
65
|
+
this.rerenderRequested = false;
|
|
66
|
+
await this.#renderOnce();
|
|
67
|
+
} while (this.rerenderRequested);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
this.renderInFlight = null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async #renderOnce() {
|
|
46
74
|
const components = this.componentsManager.getAll();
|
|
47
75
|
const currentTime = this.state.currentTime;
|
|
48
76
|
const entries = components.map((component) => {
|
|
49
77
|
const startAt = component.props.timeline.startAt ?? 0;
|
|
50
78
|
const endAt = component.props.timeline.endAt ?? this.state.duration;
|
|
51
|
-
const isVisibleByTime = currentTime
|
|
79
|
+
const isVisibleByTime = isTimeWithinTimeline(currentTime, startAt, endAt);
|
|
52
80
|
const isExplicitlyVisible = component.props.visible !== false;
|
|
53
|
-
const shouldBeVisible =
|
|
81
|
+
const shouldBeVisible = isVisibleByTime && isExplicitlyVisible;
|
|
82
|
+
const shouldPrepareMedia = component.type === 'VIDEO' || component.type === 'AUDIO'
|
|
83
|
+
? shouldPrepareMediaAtTime({
|
|
84
|
+
type: component.type,
|
|
85
|
+
visible: component.props.visible,
|
|
86
|
+
timeline: component.props.timeline,
|
|
87
|
+
source: { url: component.props.sourceUrl }
|
|
88
|
+
}, currentTime)
|
|
89
|
+
: false;
|
|
54
90
|
const wasVisible = this.lastActiveById.get(component.id) === true;
|
|
55
|
-
return { component, shouldBeVisible, wasVisible };
|
|
91
|
+
return { component, shouldBeVisible, shouldPrepareMedia, wasVisible };
|
|
56
92
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
93
|
+
// Server output is correctness-first: every display hook reasserts timeline
|
|
94
|
+
// visibility on every frame. The optimized preview path can otherwise leave
|
|
95
|
+
// a stale outgoing image visible across a cut in a long frame-range render.
|
|
96
|
+
const toUpdate = this.state.environment === 'server'
|
|
97
|
+
? components
|
|
98
|
+
: entries
|
|
99
|
+
// Every component needs one initial update so its display hook can hide it
|
|
100
|
+
// when the first rendered frame is outside its timeline.
|
|
101
|
+
.filter((e) => !this.lastActiveById.has(e.component.id) ||
|
|
102
|
+
e.shouldBeVisible ||
|
|
103
|
+
e.wasVisible ||
|
|
104
|
+
e.shouldPrepareMedia)
|
|
105
|
+
.map((e) => e.component);
|
|
60
106
|
await Promise.all(toUpdate.map((component) => component.update()));
|
|
61
|
-
//
|
|
107
|
+
// Keep one extra tick after the warm window ends so media hooks can release pooled resources.
|
|
62
108
|
for (const e of entries) {
|
|
63
|
-
this.lastActiveById.set(e.component.id, e.shouldBeVisible);
|
|
109
|
+
this.lastActiveById.set(e.component.id, e.shouldBeVisible || e.shouldPrepareMedia);
|
|
64
110
|
}
|
|
65
111
|
this.#syncLayerDisplayObjects();
|
|
66
112
|
this.appManager.render();
|
|
67
113
|
this.lastRenderTime = currentTime;
|
|
68
114
|
}
|
|
69
115
|
destroy() {
|
|
70
|
-
this.eventManager.removeEventListener('beforerender', this.
|
|
71
|
-
this.eventManager.removeEventListener('timeupdate', this.
|
|
72
|
-
this.eventManager.removeEventListener('rerender', this.
|
|
73
|
-
this.eventManager.removeEventListener('changestate', this.
|
|
116
|
+
this.eventManager.removeEventListener('beforerender', this.boundHandleBeforeRender);
|
|
117
|
+
this.eventManager.removeEventListener('timeupdate', this.boundRender);
|
|
118
|
+
this.eventManager.removeEventListener('rerender', this.boundRender);
|
|
119
|
+
this.eventManager.removeEventListener('changestate', this.boundHandleChangeState);
|
|
74
120
|
}
|
|
75
121
|
}
|
|
@@ -22,6 +22,8 @@ export declare class StateManager implements IStateManager {
|
|
|
22
22
|
private loop;
|
|
23
23
|
private timeManager;
|
|
24
24
|
private layersManager;
|
|
25
|
+
private disabledTimeZonesCache;
|
|
26
|
+
private disabledTimeZonesCacheKey;
|
|
25
27
|
constructor(cradle: {
|
|
26
28
|
sceneData: SceneData;
|
|
27
29
|
environment: RenderEnvironment;
|
|
@@ -647,6 +649,12 @@ export declare class StateManager implements IStateManager {
|
|
|
647
649
|
unit: "px" | "em" | "rem" | "%";
|
|
648
650
|
} | null | undefined;
|
|
649
651
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
652
|
+
padding?: number | {
|
|
653
|
+
top: number;
|
|
654
|
+
right: number;
|
|
655
|
+
bottom: number;
|
|
656
|
+
left: number;
|
|
657
|
+
} | undefined;
|
|
650
658
|
shadow?: {
|
|
651
659
|
enabled?: boolean | undefined;
|
|
652
660
|
preset?: string | undefined;
|
|
@@ -1266,6 +1274,12 @@ export declare class StateManager implements IStateManager {
|
|
|
1266
1274
|
unit: "px" | "em" | "rem" | "%";
|
|
1267
1275
|
} | null | undefined;
|
|
1268
1276
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
1277
|
+
padding?: number | {
|
|
1278
|
+
top: number;
|
|
1279
|
+
right: number;
|
|
1280
|
+
bottom: number;
|
|
1281
|
+
left: number;
|
|
1282
|
+
} | undefined;
|
|
1269
1283
|
shadow?: {
|
|
1270
1284
|
enabled?: boolean | undefined;
|
|
1271
1285
|
preset?: string | undefined;
|
|
@@ -1859,6 +1873,12 @@ export declare class StateManager implements IStateManager {
|
|
|
1859
1873
|
unit: "px" | "em" | "rem" | "%";
|
|
1860
1874
|
} | null | undefined;
|
|
1861
1875
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
1876
|
+
padding?: number | {
|
|
1877
|
+
top: number;
|
|
1878
|
+
right: number;
|
|
1879
|
+
bottom: number;
|
|
1880
|
+
left: number;
|
|
1881
|
+
} | undefined;
|
|
1862
1882
|
shadow?: {
|
|
1863
1883
|
enabled?: boolean | undefined;
|
|
1864
1884
|
preset?: string | undefined;
|
|
@@ -2450,6 +2470,12 @@ export declare class StateManager implements IStateManager {
|
|
|
2450
2470
|
unit: "px" | "em" | "rem" | "%";
|
|
2451
2471
|
} | null | undefined;
|
|
2452
2472
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
2473
|
+
padding?: number | {
|
|
2474
|
+
top: number;
|
|
2475
|
+
right: number;
|
|
2476
|
+
bottom: number;
|
|
2477
|
+
left: number;
|
|
2478
|
+
} | undefined;
|
|
2453
2479
|
shadow?: {
|
|
2454
2480
|
enabled?: boolean | undefined;
|
|
2455
2481
|
preset?: string | undefined;
|
|
@@ -3075,6 +3101,12 @@ export declare class StateManager implements IStateManager {
|
|
|
3075
3101
|
unit: "px" | "em" | "rem" | "%";
|
|
3076
3102
|
} | null | undefined;
|
|
3077
3103
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
3104
|
+
padding?: number | {
|
|
3105
|
+
top: number;
|
|
3106
|
+
right: number;
|
|
3107
|
+
bottom: number;
|
|
3108
|
+
left: number;
|
|
3109
|
+
} | undefined;
|
|
3078
3110
|
shadow?: {
|
|
3079
3111
|
enabled?: boolean | undefined;
|
|
3080
3112
|
preset?: string | undefined;
|
|
@@ -3670,6 +3702,12 @@ export declare class StateManager implements IStateManager {
|
|
|
3670
3702
|
unit: "px" | "em" | "rem" | "%";
|
|
3671
3703
|
} | null | undefined;
|
|
3672
3704
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
3705
|
+
padding?: number | {
|
|
3706
|
+
top: number;
|
|
3707
|
+
right: number;
|
|
3708
|
+
bottom: number;
|
|
3709
|
+
left: number;
|
|
3710
|
+
} | undefined;
|
|
3673
3711
|
shadow?: {
|
|
3674
3712
|
enabled?: boolean | undefined;
|
|
3675
3713
|
preset?: string | undefined;
|
|
@@ -4216,6 +4254,12 @@ export declare class StateManager implements IStateManager {
|
|
|
4216
4254
|
unit: "px" | "em" | "rem" | "%";
|
|
4217
4255
|
} | null | undefined;
|
|
4218
4256
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
4257
|
+
padding?: number | {
|
|
4258
|
+
top: number;
|
|
4259
|
+
right: number;
|
|
4260
|
+
bottom: number;
|
|
4261
|
+
left: number;
|
|
4262
|
+
} | undefined;
|
|
4219
4263
|
shadow?: {
|
|
4220
4264
|
enabled?: boolean | undefined;
|
|
4221
4265
|
preset?: string | undefined;
|
|
@@ -4767,6 +4811,12 @@ export declare class StateManager implements IStateManager {
|
|
|
4767
4811
|
unit: "px" | "em" | "rem" | "%";
|
|
4768
4812
|
} | null | undefined;
|
|
4769
4813
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
4814
|
+
padding?: number | {
|
|
4815
|
+
top: number;
|
|
4816
|
+
right: number;
|
|
4817
|
+
bottom: number;
|
|
4818
|
+
left: number;
|
|
4819
|
+
} | undefined;
|
|
4770
4820
|
shadow?: {
|
|
4771
4821
|
enabled?: boolean | undefined;
|
|
4772
4822
|
preset?: string | undefined;
|
|
@@ -5311,6 +5361,12 @@ export declare class StateManager implements IStateManager {
|
|
|
5311
5361
|
unit: "px" | "em" | "rem" | "%";
|
|
5312
5362
|
} | null | undefined;
|
|
5313
5363
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
5364
|
+
padding?: number | {
|
|
5365
|
+
top: number;
|
|
5366
|
+
right: number;
|
|
5367
|
+
bottom: number;
|
|
5368
|
+
left: number;
|
|
5369
|
+
} | undefined;
|
|
5314
5370
|
shadow?: {
|
|
5315
5371
|
enabled?: boolean | undefined;
|
|
5316
5372
|
preset?: string | undefined;
|
|
@@ -15,6 +15,8 @@ export class StateManager {
|
|
|
15
15
|
loop = false;
|
|
16
16
|
timeManager;
|
|
17
17
|
layersManager;
|
|
18
|
+
disabledTimeZonesCache = [];
|
|
19
|
+
disabledTimeZonesCacheKey = '';
|
|
18
20
|
constructor(cradle) {
|
|
19
21
|
const sceneDataRune = $state(cradle.sceneData);
|
|
20
22
|
this.currentSceneData = sceneDataRune;
|
|
@@ -35,14 +37,9 @@ export class StateManager {
|
|
|
35
37
|
this.currentSceneData.settings.subtitles = subtitlesSettings;
|
|
36
38
|
}
|
|
37
39
|
get currentTime() {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
else if (this.currentTimeRune <= this.startTime) {
|
|
42
|
-
return this.startTime;
|
|
43
|
-
}
|
|
44
|
-
// Could cache fps if it doesn't change frequently
|
|
45
|
-
return this.timeManager.transformTime(this.currentTimeRune);
|
|
40
|
+
const clampedTime = Math.max(this.startTime, Math.min(this.currentTimeRune, this.endTime));
|
|
41
|
+
const frameTime = this.timeManager.getCurrentFrameTime(clampedTime, true);
|
|
42
|
+
return Math.min(frameTime, this.endTime);
|
|
46
43
|
}
|
|
47
44
|
get data() {
|
|
48
45
|
return this.currentSceneData;
|
|
@@ -71,7 +68,7 @@ export class StateManager {
|
|
|
71
68
|
return Math.min(this.data.settings.endAt || this.duration, this.duration);
|
|
72
69
|
}
|
|
73
70
|
setCurrentTime(time) {
|
|
74
|
-
const currentTime =
|
|
71
|
+
const currentTime = Math.max(this.startTime, Math.min(time, this.endTime));
|
|
75
72
|
this.currentTimeRune = currentTime;
|
|
76
73
|
if (time >= this.endTime && this.isPlaying) {
|
|
77
74
|
if (this.loop) {
|
|
@@ -101,63 +98,68 @@ export class StateManager {
|
|
|
101
98
|
}
|
|
102
99
|
}
|
|
103
100
|
get currentFrame() {
|
|
104
|
-
|
|
101
|
+
const clampedTime = Math.max(this.startTime, Math.min(this.currentTimeRune, this.endTime));
|
|
102
|
+
return this.timeManager.getFrameIndex(clampedTime, 'current', true);
|
|
105
103
|
}
|
|
106
104
|
get disabledTimeZones() {
|
|
107
|
-
const start =
|
|
108
|
-
const end =
|
|
109
|
-
const
|
|
110
|
-
const duration =
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
105
|
+
const start = this.data.settings.startAt;
|
|
106
|
+
const end = this.data.settings.endAt;
|
|
107
|
+
const excludedTimestamps = this.data.settings.trimZones ?? [];
|
|
108
|
+
const duration = this.data.settings.duration;
|
|
109
|
+
const cacheKey = [
|
|
110
|
+
start ?? '',
|
|
111
|
+
end ?? '',
|
|
112
|
+
duration,
|
|
113
|
+
excludedTimestamps.map((zone) => `${zone.start}:${zone.end}`).join('|')
|
|
114
|
+
].join('::');
|
|
115
|
+
if (cacheKey === this.disabledTimeZonesCacheKey) {
|
|
116
|
+
return this.disabledTimeZonesCache;
|
|
117
|
+
}
|
|
118
|
+
const output = [];
|
|
119
|
+
if (start && start > 0) {
|
|
120
|
+
output.push({
|
|
121
|
+
start: 0,
|
|
122
|
+
end: start
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (excludedTimestamps.length) {
|
|
126
|
+
for (const disabledZone of excludedTimestamps) {
|
|
127
|
+
const { start, end } = disabledZone;
|
|
128
|
+
if (end < duration) {
|
|
129
|
+
output.push({
|
|
130
|
+
start,
|
|
131
|
+
end
|
|
132
|
+
});
|
|
128
133
|
}
|
|
129
134
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
+
}
|
|
136
|
+
if (end && end < duration) {
|
|
137
|
+
output.push({
|
|
138
|
+
start: end,
|
|
139
|
+
end: duration
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
output.sort((a, b) => a.start - b.start);
|
|
143
|
+
const mergedZones = [];
|
|
144
|
+
let currentZone = null;
|
|
145
|
+
for (const zone of output) {
|
|
146
|
+
if (!currentZone) {
|
|
147
|
+
currentZone = { ...zone };
|
|
135
148
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
// Merge overlapping zones
|
|
139
|
-
const mergedZones = [];
|
|
140
|
-
let currentZone = null;
|
|
141
|
-
for (const zone of output) {
|
|
142
|
-
if (!currentZone) {
|
|
143
|
-
currentZone = { ...zone };
|
|
144
|
-
}
|
|
145
|
-
else if (zone.start <= currentZone.end) {
|
|
146
|
-
// Zones overlap, extend the current zone
|
|
147
|
-
currentZone.end = Math.max(currentZone.end, zone.end);
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
// No overlap, push current zone and start a new one
|
|
151
|
-
mergedZones.push(currentZone);
|
|
152
|
-
currentZone = { ...zone };
|
|
153
|
-
}
|
|
149
|
+
else if (zone.start <= currentZone.end) {
|
|
150
|
+
currentZone.end = Math.max(currentZone.end, zone.end);
|
|
154
151
|
}
|
|
155
|
-
|
|
152
|
+
else {
|
|
156
153
|
mergedZones.push(currentZone);
|
|
154
|
+
currentZone = { ...zone };
|
|
157
155
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
156
|
+
}
|
|
157
|
+
if (currentZone) {
|
|
158
|
+
mergedZones.push(currentZone);
|
|
159
|
+
}
|
|
160
|
+
this.disabledTimeZonesCacheKey = cacheKey;
|
|
161
|
+
this.disabledTimeZonesCache = mergedZones;
|
|
162
|
+
return this.disabledTimeZonesCache;
|
|
161
163
|
}
|
|
162
164
|
changeState(updateState) {
|
|
163
165
|
let newState = this.determineNewState(updateState);
|
|
@@ -208,9 +208,7 @@ function buildSubtitlesManager(timeManager, eventManager, sceneData, subtitles)
|
|
|
208
208
|
for (const [lang, langIndex] of Object.entries(assetIndex)) {
|
|
209
209
|
for (const [subtitleId, subtitle] of Object.entries(langIndex)) {
|
|
210
210
|
const text = subtitle.text;
|
|
211
|
-
|
|
212
|
-
// This prevents splitting emojis into unpaired surrogates that cause encodeURIComponent to fail
|
|
213
|
-
const characters = Array.from(text);
|
|
211
|
+
const characters = text.split('');
|
|
214
212
|
if (characters && characters.length > 0) {
|
|
215
213
|
for (const char of characters) {
|
|
216
214
|
if (!charactersList.includes(char)) {
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export declare class TimeManager {
|
|
2
2
|
fps: number;
|
|
3
3
|
duration: number;
|
|
4
|
+
private static readonly FRAME_EPSILON;
|
|
5
|
+
getFrameIndex(time: number, mode?: 'nearest' | 'current', skipDurationCheck?: boolean): number;
|
|
6
|
+
getTimeForFrame(frame: number, skipDurationCheck?: boolean): number;
|
|
4
7
|
transformTime(time: number, skipDurationCheck?: boolean): number;
|
|
8
|
+
getCurrentFrameTime(time: number, skipDurationCheck?: boolean): number;
|
|
5
9
|
updateTimeConfig(newFps: number, newDuration: number): void;
|
|
6
10
|
}
|