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
|
@@ -58,7 +58,7 @@ export class SubtitlesHook {
|
|
|
58
58
|
const assetSubtitles = this.subtitlesManager.getAssetSubtitles(assetId);
|
|
59
59
|
const languageCode = source?.languageCode || 'default';
|
|
60
60
|
const subtitleArray = assetSubtitles[languageCode] || Object.values(assetSubtitles)[0] || [];
|
|
61
|
-
this.#subtitles = subtitleArray;
|
|
61
|
+
this.#subtitles = [...subtitleArray].sort((a, b) => a.start_at - b.start_at);
|
|
62
62
|
// const el = this.#buildHtmlElement();
|
|
63
63
|
// this.#context.setResource('wrapperHtmlEl', el);
|
|
64
64
|
if (this.activeSubtitle === undefined) {
|
|
@@ -66,16 +66,38 @@ export class SubtitlesHook {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
get activeSubtitle() {
|
|
69
|
+
const currentTime = this.state.currentTime;
|
|
69
70
|
if (this.#currentSubtitle) {
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
return this.#currentSubtitle.visible ? this.#currentSubtitle : undefined;
|
|
71
|
+
if (currentTime >= this.#currentSubtitle.start_at &&
|
|
72
|
+
currentTime <= this.#currentSubtitle.end_at) {
|
|
73
|
+
return this.#currentSubtitle.visible !== false ? this.#currentSubtitle : undefined;
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
let low = 0;
|
|
77
|
+
let high = this.#subtitles.length - 1;
|
|
78
|
+
let candidateIndex = -1;
|
|
79
|
+
while (low <= high) {
|
|
80
|
+
const mid = Math.floor((low + high) / 2);
|
|
81
|
+
const subtitle = this.#subtitles[mid];
|
|
82
|
+
if (subtitle.start_at <= currentTime) {
|
|
83
|
+
candidateIndex = mid;
|
|
84
|
+
low = mid + 1;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
high = mid - 1;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (candidateIndex === -1) {
|
|
91
|
+
this.#currentSubtitle = undefined;
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const subtitle = this.#subtitles[candidateIndex];
|
|
95
|
+
if (currentTime <= subtitle.end_at && subtitle.visible !== false) {
|
|
96
|
+
this.#currentSubtitle = subtitle;
|
|
97
|
+
return subtitle;
|
|
98
|
+
}
|
|
99
|
+
this.#currentSubtitle = undefined;
|
|
100
|
+
return undefined;
|
|
79
101
|
}
|
|
80
102
|
#buildFakeContext() {
|
|
81
103
|
let background = this.#context.data.appearance.background;
|
|
@@ -108,19 +130,21 @@ export class SubtitlesHook {
|
|
|
108
130
|
this.#context.updateContextData(updateData);
|
|
109
131
|
}
|
|
110
132
|
async #handleUpdate() {
|
|
111
|
-
const
|
|
133
|
+
const activeSubtitle = this.activeSubtitle;
|
|
134
|
+
const contextId = activeSubtitle?.id;
|
|
112
135
|
if (contextId === this.#currentId && !this.#refreshed) {
|
|
113
136
|
return;
|
|
114
137
|
}
|
|
115
|
-
if ((
|
|
116
|
-
(
|
|
117
|
-
this.#
|
|
118
|
-
|
|
119
|
-
const
|
|
138
|
+
if ((activeSubtitle && activeSubtitle.id !== this.#context.contextData.id) ||
|
|
139
|
+
(activeSubtitle && this.#refreshed)) {
|
|
140
|
+
this.#currentSubtitle = activeSubtitle;
|
|
141
|
+
this.#currentId = activeSubtitle.id;
|
|
142
|
+
const startTime = activeSubtitle.start_at ?? 0;
|
|
143
|
+
const endTime = activeSubtitle.end_at ?? 0;
|
|
120
144
|
const duration = endTime - startTime;
|
|
121
145
|
const animationData = {};
|
|
122
146
|
const wordTimings = [];
|
|
123
|
-
|
|
147
|
+
activeSubtitle.words?.forEach((word) => {
|
|
124
148
|
wordTimings.push(gsap.utils.clamp(0, duration, word[1] - startTime)); // timings is relative to the start of the subtitle
|
|
125
149
|
});
|
|
126
150
|
// reorder wordTimings by start_at
|
|
@@ -135,24 +159,24 @@ export class SubtitlesHook {
|
|
|
135
159
|
this.#context.setResource('animationData', animationData);
|
|
136
160
|
// we need to rebuild subtitle component as text component
|
|
137
161
|
const currentSize = get(this.#context.data, 'appearance.text.fontSize.value', get(this.#context.data, 'appearance.text.fontSize', 50));
|
|
138
|
-
const colorOverride =
|
|
139
|
-
const visibleOverride = 'visible' in
|
|
140
|
-
const fontSizeOverride = 'enlarge' in
|
|
162
|
+
const colorOverride = activeSubtitle.color ? { color: activeSubtitle.color } : {};
|
|
163
|
+
const visibleOverride = 'visible' in activeSubtitle ? { visible: activeSubtitle.visible } : {};
|
|
164
|
+
const fontSizeOverride = 'enlarge' in activeSubtitle && currentSize
|
|
141
165
|
? {
|
|
142
166
|
fontSize: {
|
|
143
|
-
value: currentSize * (
|
|
167
|
+
value: currentSize * (activeSubtitle.enlarge / 100),
|
|
144
168
|
unit: this.#context.data.appearance.text?.fontSize?.unit
|
|
145
169
|
}
|
|
146
170
|
}
|
|
147
171
|
: {};
|
|
148
172
|
const subtitleContextData = {
|
|
149
173
|
...this.#context.data,
|
|
150
|
-
id:
|
|
174
|
+
id: activeSubtitle.id,
|
|
151
175
|
type: 'TEXT',
|
|
152
|
-
text: this.#removePunctuation(
|
|
176
|
+
text: this.#removePunctuation(activeSubtitle.text || ''),
|
|
153
177
|
timeline: {
|
|
154
|
-
startAt:
|
|
155
|
-
endAt:
|
|
178
|
+
startAt: activeSubtitle.start_at,
|
|
179
|
+
endAt: activeSubtitle.end_at
|
|
156
180
|
},
|
|
157
181
|
appearance: {
|
|
158
182
|
...this.#context.data.appearance,
|
|
@@ -169,7 +193,7 @@ export class SubtitlesHook {
|
|
|
169
193
|
this.#refreshed = false;
|
|
170
194
|
}
|
|
171
195
|
}
|
|
172
|
-
if (!
|
|
196
|
+
if (!activeSubtitle) {
|
|
173
197
|
this.#buildFakeContext();
|
|
174
198
|
}
|
|
175
199
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { VideoComponentShape } from '../..';
|
|
2
2
|
export class VerifyMediaHook {
|
|
3
|
-
types = ['setup', 'refresh'];
|
|
3
|
+
types = ['setup', 'refresh:content'];
|
|
4
4
|
priority = 1;
|
|
5
5
|
#context;
|
|
6
6
|
async #handleSetup() {
|
|
@@ -14,7 +14,7 @@ export class VerifyMediaHook {
|
|
|
14
14
|
}
|
|
15
15
|
async handle(type, context) {
|
|
16
16
|
this.#context = context;
|
|
17
|
-
if (type === 'setup' || type === 'refresh') {
|
|
17
|
+
if (type === 'setup' || type === 'refresh:content') {
|
|
18
18
|
return await this.#handleSetup();
|
|
19
19
|
}
|
|
20
20
|
}
|
|
@@ -19,6 +19,7 @@ export declare class ComponentDirector {
|
|
|
19
19
|
constructImage(): import("..").IComponent;
|
|
20
20
|
constructGif(): import("..").IComponent;
|
|
21
21
|
constructShape(): import("..").IComponent;
|
|
22
|
+
constructFill(): import("..").IComponent;
|
|
22
23
|
constructSubtitle(): import("..").IComponent;
|
|
23
24
|
constructText(): import("..").IComponent;
|
|
24
25
|
}
|
|
@@ -36,18 +36,23 @@ export class ComponentDirector {
|
|
|
36
36
|
return this.constructText();
|
|
37
37
|
case 'COLOR':
|
|
38
38
|
case 'GRADIENT':
|
|
39
|
-
return this.
|
|
39
|
+
return this.constructFill();
|
|
40
40
|
default:
|
|
41
41
|
throw new Error(`Unsupported component type`);
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
constructVideo() {
|
|
45
45
|
if (this.shouldUseDeterministicMedia) {
|
|
46
|
-
this.builder
|
|
46
|
+
this.builder
|
|
47
|
+
.withDeterministicMedia()
|
|
48
|
+
.withTexture()
|
|
49
|
+
.withSplitScreen()
|
|
50
|
+
.withPixiAnimationTarget()
|
|
51
|
+
.withAnimation();
|
|
47
52
|
return this.builder.getComponent();
|
|
48
53
|
}
|
|
49
54
|
this.builder.withMedia().withMediaSeeking();
|
|
50
|
-
this.builder.withVideoTexture().withSplitScreen();
|
|
55
|
+
this.builder.withVideoTexture().withSplitScreen().withPixiAnimationTarget().withAnimation();
|
|
51
56
|
return this.builder.getComponent();
|
|
52
57
|
}
|
|
53
58
|
constructAudio() {
|
|
@@ -55,15 +60,25 @@ export class ComponentDirector {
|
|
|
55
60
|
return this.builder.getComponent();
|
|
56
61
|
}
|
|
57
62
|
constructImage() {
|
|
58
|
-
this.builder
|
|
63
|
+
this.builder
|
|
64
|
+
.withImage()
|
|
65
|
+
.withTexture()
|
|
66
|
+
.withSplitScreen()
|
|
67
|
+
.withPixiAnimationTarget()
|
|
68
|
+
.withAnimation();
|
|
59
69
|
return this.builder.getComponent();
|
|
60
70
|
}
|
|
61
71
|
constructGif() {
|
|
62
72
|
if (this.shouldUseDeterministicMedia) {
|
|
63
|
-
this.builder
|
|
73
|
+
this.builder
|
|
74
|
+
.withDeterministicMedia()
|
|
75
|
+
.withTexture()
|
|
76
|
+
.withDisplayObject()
|
|
77
|
+
.withPixiAnimationTarget()
|
|
78
|
+
.withAnimation();
|
|
64
79
|
return this.builder.getComponent();
|
|
65
80
|
}
|
|
66
|
-
this.builder.withGif()
|
|
81
|
+
this.builder.withGif().withPixiAnimationTarget().withAnimation();
|
|
67
82
|
return this.builder.getComponent();
|
|
68
83
|
}
|
|
69
84
|
constructShape() {
|
|
@@ -72,14 +87,32 @@ export class ComponentDirector {
|
|
|
72
87
|
if (shapeData.shape?.type === 'progress') {
|
|
73
88
|
// Progress shapes use GPU-based PIXI Graphics rendering
|
|
74
89
|
// Still need DisplayObjectHook to add the progress display object to the stage
|
|
75
|
-
this.builder
|
|
90
|
+
this.builder
|
|
91
|
+
.withProgressShape()
|
|
92
|
+
.withDisplayObject()
|
|
93
|
+
.withPixiAnimationTarget()
|
|
94
|
+
.withAnimation();
|
|
76
95
|
}
|
|
77
96
|
else {
|
|
78
97
|
// Regular shapes use canvas-based rendering
|
|
79
|
-
this.builder
|
|
98
|
+
this.builder
|
|
99
|
+
.withCanvasShape()
|
|
100
|
+
.withTexture()
|
|
101
|
+
.withDisplayObject()
|
|
102
|
+
.withPixiAnimationTarget()
|
|
103
|
+
.withAnimation();
|
|
80
104
|
}
|
|
81
105
|
return this.builder.getComponent();
|
|
82
106
|
}
|
|
107
|
+
constructFill() {
|
|
108
|
+
this.builder
|
|
109
|
+
.withCanvasFill()
|
|
110
|
+
.withTexture()
|
|
111
|
+
.withDisplayObject()
|
|
112
|
+
.withPixiAnimationTarget()
|
|
113
|
+
.withAnimation();
|
|
114
|
+
return this.builder.getComponent();
|
|
115
|
+
}
|
|
83
116
|
constructSubtitle() {
|
|
84
117
|
this.builder.withSubtitles().withHtmlText().withAnimation();
|
|
85
118
|
if (this.sceneState.environment === 'server') {
|
|
@@ -128,11 +128,17 @@ const collectComponentTextVariants = (component, configuredLookup, output) => {
|
|
|
128
128
|
if (!textAppearance) {
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
|
-
const family = normalizeFamily(textAppearance.fontFamily) ??
|
|
131
|
+
const family = normalizeFamily(textAppearance.fontFamily) ??
|
|
132
|
+
normalizeFamily(textAppearance.fontSource?.family);
|
|
132
133
|
if (!family) {
|
|
133
134
|
return;
|
|
134
135
|
}
|
|
135
136
|
const configMatch = configuredLookup.get(family.toLowerCase());
|
|
137
|
+
// An unconfigured family is a browser/system-font request, not an implicit
|
|
138
|
+
// Google Fonts dependency. Remote loading must be explicit and reproducible.
|
|
139
|
+
if (!textAppearance.fontSource?.source && !configMatch) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
136
142
|
const source = (textAppearance.fontSource?.source ?? configMatch?.source ?? 'google');
|
|
137
143
|
const fileUrl = normalizeFamily(textAppearance.fontSource?.fileUrl) ?? configMatch?.fileUrl ?? undefined;
|
|
138
144
|
const weights = new Set();
|
|
@@ -191,6 +197,11 @@ export const discoverRequiredFontVariants = (sceneData, configuredFonts = []) =>
|
|
|
191
197
|
if (component.type !== 'TEXT' && component.type !== 'SUBTITLES') {
|
|
192
198
|
continue;
|
|
193
199
|
}
|
|
200
|
+
if (component.type === 'TEXT' &&
|
|
201
|
+
typeof component.text === 'string' &&
|
|
202
|
+
!component.text.trim()) {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
194
205
|
collectComponentTextVariants(component, configuredLookup, variants);
|
|
195
206
|
}
|
|
196
207
|
}
|
|
@@ -482,6 +482,12 @@ export declare class Layer implements ILayer {
|
|
|
482
482
|
unit: "px" | "em" | "rem" | "%";
|
|
483
483
|
} | null | undefined;
|
|
484
484
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
485
|
+
padding?: number | {
|
|
486
|
+
top: number;
|
|
487
|
+
right: number;
|
|
488
|
+
bottom: number;
|
|
489
|
+
left: number;
|
|
490
|
+
} | undefined;
|
|
485
491
|
shadow?: {
|
|
486
492
|
enabled?: boolean | undefined;
|
|
487
493
|
preset?: string | undefined;
|
|
@@ -1101,6 +1107,12 @@ export declare class Layer implements ILayer {
|
|
|
1101
1107
|
unit: "px" | "em" | "rem" | "%";
|
|
1102
1108
|
} | null | undefined;
|
|
1103
1109
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
1110
|
+
padding?: number | {
|
|
1111
|
+
top: number;
|
|
1112
|
+
right: number;
|
|
1113
|
+
bottom: number;
|
|
1114
|
+
left: number;
|
|
1115
|
+
} | undefined;
|
|
1104
1116
|
shadow?: {
|
|
1105
1117
|
enabled?: boolean | undefined;
|
|
1106
1118
|
preset?: string | undefined;
|
|
@@ -1694,6 +1706,12 @@ export declare class Layer implements ILayer {
|
|
|
1694
1706
|
unit: "px" | "em" | "rem" | "%";
|
|
1695
1707
|
} | null | undefined;
|
|
1696
1708
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
1709
|
+
padding?: number | {
|
|
1710
|
+
top: number;
|
|
1711
|
+
right: number;
|
|
1712
|
+
bottom: number;
|
|
1713
|
+
left: number;
|
|
1714
|
+
} | undefined;
|
|
1697
1715
|
shadow?: {
|
|
1698
1716
|
enabled?: boolean | undefined;
|
|
1699
1717
|
preset?: string | undefined;
|
|
@@ -2285,6 +2303,12 @@ export declare class Layer implements ILayer {
|
|
|
2285
2303
|
unit: "px" | "em" | "rem" | "%";
|
|
2286
2304
|
} | null | undefined;
|
|
2287
2305
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
2306
|
+
padding?: number | {
|
|
2307
|
+
top: number;
|
|
2308
|
+
right: number;
|
|
2309
|
+
bottom: number;
|
|
2310
|
+
left: number;
|
|
2311
|
+
} | undefined;
|
|
2288
2312
|
shadow?: {
|
|
2289
2313
|
enabled?: boolean | undefined;
|
|
2290
2314
|
preset?: string | undefined;
|
|
@@ -2910,6 +2934,12 @@ export declare class Layer implements ILayer {
|
|
|
2910
2934
|
unit: "px" | "em" | "rem" | "%";
|
|
2911
2935
|
} | null | undefined;
|
|
2912
2936
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
2937
|
+
padding?: number | {
|
|
2938
|
+
top: number;
|
|
2939
|
+
right: number;
|
|
2940
|
+
bottom: number;
|
|
2941
|
+
left: number;
|
|
2942
|
+
} | undefined;
|
|
2913
2943
|
shadow?: {
|
|
2914
2944
|
enabled?: boolean | undefined;
|
|
2915
2945
|
preset?: string | undefined;
|
|
@@ -3505,6 +3535,12 @@ export declare class Layer implements ILayer {
|
|
|
3505
3535
|
unit: "px" | "em" | "rem" | "%";
|
|
3506
3536
|
} | null | undefined;
|
|
3507
3537
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
3538
|
+
padding?: number | {
|
|
3539
|
+
top: number;
|
|
3540
|
+
right: number;
|
|
3541
|
+
bottom: number;
|
|
3542
|
+
left: number;
|
|
3543
|
+
} | undefined;
|
|
3508
3544
|
shadow?: {
|
|
3509
3545
|
enabled?: boolean | undefined;
|
|
3510
3546
|
preset?: string | undefined;
|
|
@@ -4051,6 +4087,12 @@ export declare class Layer implements ILayer {
|
|
|
4051
4087
|
unit: "px" | "em" | "rem" | "%";
|
|
4052
4088
|
} | null | undefined;
|
|
4053
4089
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
4090
|
+
padding?: number | {
|
|
4091
|
+
top: number;
|
|
4092
|
+
right: number;
|
|
4093
|
+
bottom: number;
|
|
4094
|
+
left: number;
|
|
4095
|
+
} | undefined;
|
|
4054
4096
|
shadow?: {
|
|
4055
4097
|
enabled?: boolean | undefined;
|
|
4056
4098
|
preset?: string | undefined;
|
|
@@ -4602,6 +4644,12 @@ export declare class Layer implements ILayer {
|
|
|
4602
4644
|
unit: "px" | "em" | "rem" | "%";
|
|
4603
4645
|
} | null | undefined;
|
|
4604
4646
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
4647
|
+
padding?: number | {
|
|
4648
|
+
top: number;
|
|
4649
|
+
right: number;
|
|
4650
|
+
bottom: number;
|
|
4651
|
+
left: number;
|
|
4652
|
+
} | undefined;
|
|
4605
4653
|
shadow?: {
|
|
4606
4654
|
enabled?: boolean | undefined;
|
|
4607
4655
|
preset?: string | undefined;
|
|
@@ -5146,6 +5194,12 @@ export declare class Layer implements ILayer {
|
|
|
5146
5194
|
unit: "px" | "em" | "rem" | "%";
|
|
5147
5195
|
} | null | undefined;
|
|
5148
5196
|
textTransform?: "none" | "uppercase" | "lowercase" | "capitalize" | undefined;
|
|
5197
|
+
padding?: number | {
|
|
5198
|
+
top: number;
|
|
5199
|
+
right: number;
|
|
5200
|
+
bottom: number;
|
|
5201
|
+
left: number;
|
|
5202
|
+
} | undefined;
|
|
5149
5203
|
shadow?: {
|
|
5150
5204
|
enabled?: boolean | undefined;
|
|
5151
5205
|
preset?: string | undefined;
|
|
@@ -49,7 +49,7 @@ export class Layer {
|
|
|
49
49
|
this.#displayObject.addChild(component.displayObject);
|
|
50
50
|
}
|
|
51
51
|
// resort components
|
|
52
|
-
this.components.sort((a, b) => a.props.timeline.startAt - b.props.timeline.startAt);
|
|
52
|
+
this.components.sort((a, b) => a.props.order - b.props.order || a.props.timeline.startAt - b.props.timeline.startAt);
|
|
53
53
|
this.#emit('layerschange');
|
|
54
54
|
}
|
|
55
55
|
syncDisplayObjects() {
|
|
@@ -92,29 +92,6 @@ export class Layer {
|
|
|
92
92
|
}
|
|
93
93
|
componentDisplayIndex += 1;
|
|
94
94
|
}
|
|
95
|
-
// Ensure deterministic ordering among only component display objects without
|
|
96
|
-
// assuming a 1:1 mapping with all container children.
|
|
97
|
-
if (typeof this.#displayObject.getChildIndex === 'function' &&
|
|
98
|
-
typeof this.#displayObject.setChildIndex === 'function') {
|
|
99
|
-
let nextIndex = 0;
|
|
100
|
-
for (const component of this.components) {
|
|
101
|
-
const displayObject = component.displayObject;
|
|
102
|
-
if (!displayObject || displayObject.parent !== this.#displayObject) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
const childCount = this.#displayObject.children?.length ?? 0;
|
|
106
|
-
if (childCount === 0) {
|
|
107
|
-
break;
|
|
108
|
-
}
|
|
109
|
-
const currentIndex = this.#displayObject.getChildIndex(displayObject);
|
|
110
|
-
const targetIndex = Math.max(0, Math.min(nextIndex, childCount - 1));
|
|
111
|
-
if (currentIndex >= 0 && currentIndex !== targetIndex) {
|
|
112
|
-
this.#displayObject.setChildIndex(displayObject, targetIndex);
|
|
113
|
-
changed = true;
|
|
114
|
-
}
|
|
115
|
-
nextIndex += 1;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
95
|
return changed;
|
|
119
96
|
}
|
|
120
97
|
removeComponent(component) {
|
|
@@ -124,7 +101,7 @@ export class Layer {
|
|
|
124
101
|
if (component.displayObject) {
|
|
125
102
|
this.#displayObject.removeChild(component.displayObject);
|
|
126
103
|
}
|
|
127
|
-
this.components.sort((a, b) => a.props.timeline.startAt - b.props.timeline.startAt);
|
|
104
|
+
this.components.sort((a, b) => a.props.order - b.props.order || a.props.timeline.startAt - b.props.timeline.startAt);
|
|
128
105
|
this.#emit('layerschange');
|
|
129
106
|
}
|
|
130
107
|
}
|
|
@@ -45,6 +45,8 @@ export class AppManager {
|
|
|
45
45
|
const { PixiPlugin } = await registerGsapPlugins();
|
|
46
46
|
// give the plugin a reference to the PIXI object
|
|
47
47
|
PixiPlugin.registerPIXI(PIXI);
|
|
48
|
+
const sceneBackground = this.state.data.settings.backgroundColor;
|
|
49
|
+
const solidBackground = typeof sceneBackground === 'string' ? sceneBackground : 'transparent';
|
|
48
50
|
const baseOptions = {
|
|
49
51
|
...PIXI_DEFAULTS,
|
|
50
52
|
preserveDrawingBuffer: environment === 'server',
|
|
@@ -52,8 +54,8 @@ export class AppManager {
|
|
|
52
54
|
width,
|
|
53
55
|
height,
|
|
54
56
|
view: canvas,
|
|
55
|
-
backgroundColor:
|
|
56
|
-
backgroundAlpha:
|
|
57
|
+
backgroundColor: solidBackground,
|
|
58
|
+
backgroundAlpha: solidBackground === 'transparent' ? 0 : 1
|
|
57
59
|
};
|
|
58
60
|
let options = this.#buildRendererOptions(baseOptions, environment, rendererSelection);
|
|
59
61
|
try {
|
|
@@ -175,10 +177,11 @@ export class AppManager {
|
|
|
175
177
|
}
|
|
176
178
|
#checkWebGLSupport() {
|
|
177
179
|
try {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
const probe = this.dom?.canvas ??
|
|
181
|
+
(typeof document !== 'undefined' ? document.createElement('canvas') : null);
|
|
182
|
+
if (!probe || typeof probe.getContext !== 'function') {
|
|
183
|
+
return { supported: false, reason: 'WebGL unavailable: no canvas available for probing' };
|
|
180
184
|
}
|
|
181
|
-
const probe = document.createElement('canvas');
|
|
182
185
|
if (this.preferWebGL2 && probe.getContext('webgl2')) {
|
|
183
186
|
return { supported: true, reason: '' };
|
|
184
187
|
}
|
|
@@ -24,9 +24,9 @@ export declare class ComponentsManager implements ResourceManager<IComponent, Co
|
|
|
24
24
|
private isVisible;
|
|
25
25
|
getAll(): IComponent[];
|
|
26
26
|
getMediaComponents(): (Component & MediaComponent)[];
|
|
27
|
-
update(componentId: string, data: Partial<AppearanceInput>, refreshType?: ComponentRefreshType):
|
|
27
|
+
update(componentId: string, data: Partial<AppearanceInput>, refreshType?: ComponentRefreshType): void;
|
|
28
28
|
get(componentId: string): IComponent | undefined;
|
|
29
|
-
delete(componentId: string): void
|
|
29
|
+
delete(componentId: string): Promise<void>;
|
|
30
30
|
create(componentData: ComponentData): Promise<IComponent | null>;
|
|
31
31
|
setOrder(id: string, order: number): void;
|
|
32
32
|
moveUp(id: string): void;
|
|
@@ -39,11 +39,11 @@ export declare class ComponentsManager implements ResourceManager<IComponent, Co
|
|
|
39
39
|
id: string;
|
|
40
40
|
data: Partial<AppearanceInput>;
|
|
41
41
|
}[]): void;
|
|
42
|
-
bulkDelete(ids: string[]): void
|
|
43
|
-
hide(id: string):
|
|
44
|
-
show(id: string):
|
|
45
|
-
toggle(id: string):
|
|
42
|
+
bulkDelete(ids: string[]): Promise<void>;
|
|
43
|
+
hide(id: string): void;
|
|
44
|
+
show(id: string): void;
|
|
45
|
+
toggle(id: string): void;
|
|
46
46
|
filter(predicate: (component: IComponent) => boolean): IComponent[];
|
|
47
47
|
isComponentVisible(componentId: string): boolean;
|
|
48
|
-
destroy(): void
|
|
48
|
+
destroy(): Promise<void>;
|
|
49
49
|
}
|
|
@@ -6,6 +6,7 @@ import { PixiComponentBuilder } from '../builders/PixiComponentBuilder.js';
|
|
|
6
6
|
import { ComponentState } from '../builders/_ComponentState.svelte.js';
|
|
7
7
|
import { asClass, asValue } from 'awilix/browser';
|
|
8
8
|
import { debounce } from 'lodash-es';
|
|
9
|
+
import { isTimeWithinTimeline } from '../utils/timelineWindow.js';
|
|
9
10
|
// import { builtTextComponentForSubtitle } from '../utils/subtitles.js';
|
|
10
11
|
export class ComponentsManager {
|
|
11
12
|
components = $state(new Map());
|
|
@@ -28,7 +29,7 @@ export class ComponentsManager {
|
|
|
28
29
|
this.initializeEventListeners();
|
|
29
30
|
}
|
|
30
31
|
initializeEventListeners() {
|
|
31
|
-
this.eventManager.on('subtitleschange', this.debouncedRefreshSubtitles
|
|
32
|
+
this.eventManager.on('subtitleschange', this.debouncedRefreshSubtitles);
|
|
32
33
|
}
|
|
33
34
|
#scheduleSubtitleRefresh() {
|
|
34
35
|
// Schedule the refresh on the next animation frame to avoid blocking
|
|
@@ -43,7 +44,7 @@ export class ComponentsManager {
|
|
|
43
44
|
const { currentTime, duration } = this.state;
|
|
44
45
|
const startAt = component.props.timeline.startAt || 0;
|
|
45
46
|
const endAt = component.props.timeline.endAt || duration;
|
|
46
|
-
return currentTime
|
|
47
|
+
return isTimeWithinTimeline(currentTime, startAt, endAt);
|
|
47
48
|
}
|
|
48
49
|
getAll() {
|
|
49
50
|
return Array.from(this.components.values());
|
|
@@ -57,10 +58,10 @@ export class ComponentsManager {
|
|
|
57
58
|
component.refresh(refreshType);
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
|
-
|
|
61
|
+
update(componentId, data, refreshType = 'refresh') {
|
|
61
62
|
const component = this.get(componentId);
|
|
62
63
|
if (component) {
|
|
63
|
-
|
|
64
|
+
component.props.updateAppearance(data);
|
|
64
65
|
// Only manually refresh if auto-refresh is not enabled
|
|
65
66
|
if (!component.autoRefresh) {
|
|
66
67
|
this.#refreshComponent(component, refreshType);
|
|
@@ -70,7 +71,7 @@ export class ComponentsManager {
|
|
|
70
71
|
get(componentId) {
|
|
71
72
|
return this.components.get(componentId);
|
|
72
73
|
}
|
|
73
|
-
delete(componentId) {
|
|
74
|
+
async delete(componentId) {
|
|
74
75
|
const component = this.components.get(componentId);
|
|
75
76
|
if (component) {
|
|
76
77
|
const layers = this.layersManager.getAll();
|
|
@@ -82,8 +83,8 @@ export class ComponentsManager {
|
|
|
82
83
|
this.layersManager.delete(componentLayer.id);
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
|
-
component.destroy();
|
|
86
86
|
this.components.delete(componentId);
|
|
87
|
+
await component.destroy();
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
async #buildComponent(componentData) {
|
|
@@ -236,25 +237,30 @@ export class ComponentsManager {
|
|
|
236
237
|
bulkUpdate(updates) {
|
|
237
238
|
updates.forEach(({ id, data }) => this.update(id, data));
|
|
238
239
|
}
|
|
239
|
-
bulkDelete(ids) {
|
|
240
|
-
ids.
|
|
240
|
+
async bulkDelete(ids) {
|
|
241
|
+
const results = await Promise.allSettled(ids.map((id) => this.delete(id)));
|
|
242
|
+
const errors = results
|
|
243
|
+
.filter((result) => result.status === 'rejected')
|
|
244
|
+
.map((result) => result.reason);
|
|
245
|
+
if (errors.length > 0)
|
|
246
|
+
throw new AggregateError(errors, 'Component deletion failed.');
|
|
241
247
|
}
|
|
242
|
-
|
|
248
|
+
hide(id) {
|
|
243
249
|
const component = this.get(id);
|
|
244
250
|
if (component) {
|
|
245
|
-
|
|
251
|
+
component.props.setVisible(false);
|
|
246
252
|
}
|
|
247
253
|
}
|
|
248
|
-
|
|
254
|
+
show(id) {
|
|
249
255
|
const component = this.get(id);
|
|
250
256
|
if (component) {
|
|
251
|
-
|
|
257
|
+
component.props.setVisible(true);
|
|
252
258
|
}
|
|
253
259
|
}
|
|
254
|
-
|
|
260
|
+
toggle(id) {
|
|
255
261
|
const component = this.get(id);
|
|
256
262
|
if (component) {
|
|
257
|
-
|
|
263
|
+
component.props.setVisible(!component.props.visible);
|
|
258
264
|
}
|
|
259
265
|
}
|
|
260
266
|
filter(predicate) {
|
|
@@ -267,8 +273,16 @@ export class ComponentsManager {
|
|
|
267
273
|
}
|
|
268
274
|
return this.isVisible(component);
|
|
269
275
|
}
|
|
270
|
-
destroy() {
|
|
276
|
+
async destroy() {
|
|
271
277
|
this.eventManager.removeEventListener('subtitleschange', this.debouncedRefreshSubtitles);
|
|
272
278
|
this.debouncedRefreshSubtitles.cancel(); // Cancel any pending debounced calls
|
|
279
|
+
const components = [...this.components.values()];
|
|
280
|
+
this.components.clear();
|
|
281
|
+
const results = await Promise.allSettled(components.map((component) => component.destroy()));
|
|
282
|
+
const errors = results
|
|
283
|
+
.filter((result) => result.status === 'rejected')
|
|
284
|
+
.map((result) => result.reason);
|
|
285
|
+
if (errors.length > 0)
|
|
286
|
+
throw new AggregateError(errors, 'Component teardown failed.');
|
|
273
287
|
}
|
|
274
288
|
}
|