visualfries 0.1.0

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.
Files changed (219) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +213 -0
  3. package/dist/DIContainer.d.ts +4 -0
  4. package/dist/DIContainer.js +145 -0
  5. package/dist/SceneBuilder.svelte.d.ts +8574 -0
  6. package/dist/SceneBuilder.svelte.js +409 -0
  7. package/dist/adapters/subtitleHelpers.d.ts +2 -0
  8. package/dist/adapters/subtitleHelpers.js +187 -0
  9. package/dist/animations/AnimationContext.d.ts +17 -0
  10. package/dist/animations/AnimationContext.js +72 -0
  11. package/dist/animations/AnimationPresetsRegister.d.ts +362 -0
  12. package/dist/animations/AnimationPresetsRegister.js +20 -0
  13. package/dist/animations/AnimationSetup.d.ts +8 -0
  14. package/dist/animations/AnimationSetup.js +30 -0
  15. package/dist/animations/SplitTextCache.d.ts +28 -0
  16. package/dist/animations/SplitTextCache.js +68 -0
  17. package/dist/animations/animationBuilder.d.ts +31 -0
  18. package/dist/animations/animationBuilder.js +255 -0
  19. package/dist/animations/animationPreset.d.ts +7 -0
  20. package/dist/animations/animationPreset.js +31 -0
  21. package/dist/animations/builders/AnimationPresetFactory.d.ts +43 -0
  22. package/dist/animations/builders/AnimationPresetFactory.js +139 -0
  23. package/dist/animations/builders/LineHighlighterAnimationBuilder.d.ts +16 -0
  24. package/dist/animations/builders/LineHighlighterAnimationBuilder.js +183 -0
  25. package/dist/animations/builders/WordHighlighterAnimationBuilder.d.ts +15 -0
  26. package/dist/animations/builders/WordHighlighterAnimationBuilder.js +180 -0
  27. package/dist/animations/engines/AnimationEngineAdaptor.d.ts +107 -0
  28. package/dist/animations/engines/AnimationEngineAdaptor.js +1 -0
  29. package/dist/animations/engines/GSAPEngineAdaptor.d.ts +21 -0
  30. package/dist/animations/engines/GSAPEngineAdaptor.js +145 -0
  31. package/dist/animations/presets/index.d.ts +2 -0
  32. package/dist/animations/presets/index.js +3 -0
  33. package/dist/animations/presets/lines.d.ts +52 -0
  34. package/dist/animations/presets/lines.js +547 -0
  35. package/dist/animations/presets/words.d.ts +31 -0
  36. package/dist/animations/presets/words.js +268 -0
  37. package/dist/animations/transformers/AnimationReferenceTransformer.d.ts +9 -0
  38. package/dist/animations/transformers/AnimationReferenceTransformer.js +114 -0
  39. package/dist/builders/PixiComponentBuilder.d.ts +63 -0
  40. package/dist/builders/PixiComponentBuilder.js +112 -0
  41. package/dist/builders/_ComponentState.svelte.d.ts +795 -0
  42. package/dist/builders/_ComponentState.svelte.js +203 -0
  43. package/dist/builders/html/HtmlBuilder.d.ts +66 -0
  44. package/dist/builders/html/HtmlBuilder.js +171 -0
  45. package/dist/builders/html/HtmlBuilderFactory.d.ts +27 -0
  46. package/dist/builders/html/HtmlBuilderFactory.js +30 -0
  47. package/dist/builders/html/StyleBuilder.d.ts +13 -0
  48. package/dist/builders/html/StyleBuilder.js +133 -0
  49. package/dist/builders/html/StyleProcessor.d.ts +9 -0
  50. package/dist/builders/html/StyleProcessor.js +1 -0
  51. package/dist/builders/html/TextComponentHtmlBuilder.d.ts +16 -0
  52. package/dist/builders/html/TextComponentHtmlBuilder.js +93 -0
  53. package/dist/builders/html/TextShadowBuilder.d.ts +60 -0
  54. package/dist/builders/html/TextShadowBuilder.js +227 -0
  55. package/dist/builders/html/processors/AppearanceStyleProcessor.d.ts +5 -0
  56. package/dist/builders/html/processors/AppearanceStyleProcessor.js +57 -0
  57. package/dist/builders/html/processors/TextAppearanceStyleProcessor.d.ts +5 -0
  58. package/dist/builders/html/processors/TextAppearanceStyleProcessor.js +37 -0
  59. package/dist/builders/html/processors/TextEffectsStyleProcessor.d.ts +6 -0
  60. package/dist/builders/html/processors/TextEffectsStyleProcessor.js +68 -0
  61. package/dist/commands/Command.d.ts +6 -0
  62. package/dist/commands/Command.js +1 -0
  63. package/dist/commands/CommandRunner.d.ts +28 -0
  64. package/dist/commands/CommandRunner.js +81 -0
  65. package/dist/commands/CommandTypes.d.ts +11 -0
  66. package/dist/commands/CommandTypes.js +13 -0
  67. package/dist/commands/PauseCommand.d.ts +4 -0
  68. package/dist/commands/PauseCommand.js +5 -0
  69. package/dist/commands/PlayCommand.d.ts +4 -0
  70. package/dist/commands/PlayCommand.js +6 -0
  71. package/dist/commands/RenderCommand.d.ts +15 -0
  72. package/dist/commands/RenderCommand.js +18 -0
  73. package/dist/commands/RenderFrameCommand.d.ts +17 -0
  74. package/dist/commands/RenderFrameCommand.js +93 -0
  75. package/dist/commands/ReplaceSourceOnTimeCommand.d.ts +4 -0
  76. package/dist/commands/ReplaceSourceOnTimeCommand.js +22 -0
  77. package/dist/commands/SeekCommand.d.ts +15 -0
  78. package/dist/commands/SeekCommand.js +39 -0
  79. package/dist/commands/UpdateComponentCommand.d.ts +4 -0
  80. package/dist/commands/UpdateComponentCommand.js +17 -0
  81. package/dist/components/AnimatedGIF.d.ts +201 -0
  82. package/dist/components/AnimatedGIF.js +391 -0
  83. package/dist/components/Component.svelte.d.ts +33 -0
  84. package/dist/components/Component.svelte.js +152 -0
  85. package/dist/components/ComponentContext.svelte.d.ts +33 -0
  86. package/dist/components/ComponentContext.svelte.js +105 -0
  87. package/dist/components/hooks/AnimationHook.d.ts +25 -0
  88. package/dist/components/hooks/AnimationHook.js +180 -0
  89. package/dist/components/hooks/CanvasShapeHook.d.ts +12 -0
  90. package/dist/components/hooks/CanvasShapeHook.js +229 -0
  91. package/dist/components/hooks/HtmlAnimationHook.d.ts +8 -0
  92. package/dist/components/hooks/HtmlAnimationHook.js +70 -0
  93. package/dist/components/hooks/HtmlTextHook.d.ts +16 -0
  94. package/dist/components/hooks/HtmlTextHook.js +102 -0
  95. package/dist/components/hooks/HtmlToCanvasHook.d.ts +16 -0
  96. package/dist/components/hooks/HtmlToCanvasHook.js +148 -0
  97. package/dist/components/hooks/ImageHook.d.ts +10 -0
  98. package/dist/components/hooks/ImageHook.js +45 -0
  99. package/dist/components/hooks/MediaHook.d.ts +15 -0
  100. package/dist/components/hooks/MediaHook.js +252 -0
  101. package/dist/components/hooks/MediaSeekingHook.d.ts +12 -0
  102. package/dist/components/hooks/MediaSeekingHook.js +204 -0
  103. package/dist/components/hooks/PixiDisplayObjectHook.d.ts +15 -0
  104. package/dist/components/hooks/PixiDisplayObjectHook.js +77 -0
  105. package/dist/components/hooks/PixiGifHook.d.ts +15 -0
  106. package/dist/components/hooks/PixiGifHook.js +97 -0
  107. package/dist/components/hooks/PixiProgressShapeHook.d.ts +12 -0
  108. package/dist/components/hooks/PixiProgressShapeHook.js +128 -0
  109. package/dist/components/hooks/PixiSplitScreenDisplayObjectHook.d.ts +21 -0
  110. package/dist/components/hooks/PixiSplitScreenDisplayObjectHook.js +210 -0
  111. package/dist/components/hooks/PixiTextureHook.d.ts +7 -0
  112. package/dist/components/hooks/PixiTextureHook.js +29 -0
  113. package/dist/components/hooks/PixiVideoTextureHook.d.ts +10 -0
  114. package/dist/components/hooks/PixiVideoTextureHook.js +35 -0
  115. package/dist/components/hooks/SubtitlesHook.d.ts +88 -0
  116. package/dist/components/hooks/SubtitlesHook.js +199 -0
  117. package/dist/components/hooks/VerifyGifHook.d.ts +7 -0
  118. package/dist/components/hooks/VerifyGifHook.js +27 -0
  119. package/dist/components/hooks/VerifyImageHook.d.ts +7 -0
  120. package/dist/components/hooks/VerifyImageHook.js +27 -0
  121. package/dist/components/hooks/VerifyMediaHook.d.ts +7 -0
  122. package/dist/components/hooks/VerifyMediaHook.js +21 -0
  123. package/dist/components/hooks/shapes/progress/CustomProgressRenderer.d.ts +8 -0
  124. package/dist/components/hooks/shapes/progress/CustomProgressRenderer.js +53 -0
  125. package/dist/components/hooks/shapes/progress/DoubleProgressRenderer.d.ts +8 -0
  126. package/dist/components/hooks/shapes/progress/DoubleProgressRenderer.js +69 -0
  127. package/dist/components/hooks/shapes/progress/LinearProgressRenderer.d.ts +8 -0
  128. package/dist/components/hooks/shapes/progress/LinearProgressRenderer.js +60 -0
  129. package/dist/components/hooks/shapes/progress/PerimeterProgressRenderer.d.ts +9 -0
  130. package/dist/components/hooks/shapes/progress/PerimeterProgressRenderer.js +213 -0
  131. package/dist/components/hooks/shapes/progress/ProgressRenderer.d.ts +17 -0
  132. package/dist/components/hooks/shapes/progress/ProgressRenderer.js +75 -0
  133. package/dist/components/hooks/shapes/progress/RadialProgressRenderer.d.ts +8 -0
  134. package/dist/components/hooks/shapes/progress/RadialProgressRenderer.js +50 -0
  135. package/dist/components/hooks/shapes/progress/index.d.ts +6 -0
  136. package/dist/components/hooks/shapes/progress/index.js +6 -0
  137. package/dist/composers/componentComposer.d.ts +55 -0
  138. package/dist/composers/componentComposer.js +118 -0
  139. package/dist/composers/layerComposer.d.ts +46 -0
  140. package/dist/composers/layerComposer.js +79 -0
  141. package/dist/composers/sceneComposer.d.ts +48 -0
  142. package/dist/composers/sceneComposer.js +92 -0
  143. package/dist/constants.d.ts +12 -0
  144. package/dist/constants.js +14 -0
  145. package/dist/directors/ComponentDirector.d.ts +20 -0
  146. package/dist/directors/ComponentDirector.js +86 -0
  147. package/dist/factories/SceneBuilderFactory.d.ts +15 -0
  148. package/dist/factories/SceneBuilderFactory.js +51 -0
  149. package/dist/fonts/GoogleFontsProvider.d.ts +12 -0
  150. package/dist/fonts/GoogleFontsProvider.js +125 -0
  151. package/dist/fonts/fontLoader.d.ts +15 -0
  152. package/dist/fonts/fontLoader.js +41 -0
  153. package/dist/fonts/types.d.ts +1 -0
  154. package/dist/fonts/types.js +1 -0
  155. package/dist/index.d.ts +11 -0
  156. package/dist/index.js +14 -0
  157. package/dist/layers/Layer.svelte.d.ts +8492 -0
  158. package/dist/layers/Layer.svelte.js +125 -0
  159. package/dist/managers/AppManager.svelte.d.ts +23 -0
  160. package/dist/managers/AppManager.svelte.js +89 -0
  161. package/dist/managers/ComponentsManager.svelte.d.ts +49 -0
  162. package/dist/managers/ComponentsManager.svelte.js +247 -0
  163. package/dist/managers/DomManager.d.ts +18 -0
  164. package/dist/managers/DomManager.js +73 -0
  165. package/dist/managers/EventManager.d.ts +7 -0
  166. package/dist/managers/EventManager.js +22 -0
  167. package/dist/managers/LayersManager.svelte.d.ts +8499 -0
  168. package/dist/managers/LayersManager.svelte.js +176 -0
  169. package/dist/managers/MediaManager.d.ts +32 -0
  170. package/dist/managers/MediaManager.js +243 -0
  171. package/dist/managers/RenderManager.d.ts +23 -0
  172. package/dist/managers/RenderManager.js +59 -0
  173. package/dist/managers/StateManager.svelte.d.ts +8746 -0
  174. package/dist/managers/StateManager.svelte.js +272 -0
  175. package/dist/managers/SubtitlesManager.svelte.d.ts +261 -0
  176. package/dist/managers/SubtitlesManager.svelte.js +1385 -0
  177. package/dist/managers/TimeManager.svelte.d.ts +6 -0
  178. package/dist/managers/TimeManager.svelte.js +18 -0
  179. package/dist/managers/TimelineManager.svelte.d.ts +25 -0
  180. package/dist/managers/TimelineManager.svelte.js +152 -0
  181. package/dist/registers.d.ts +12 -0
  182. package/dist/registers.js +29 -0
  183. package/dist/schemas/runtime/index.d.ts +3 -0
  184. package/dist/schemas/runtime/index.js +4 -0
  185. package/dist/schemas/runtime/types.d.ts +323 -0
  186. package/dist/schemas/runtime/types.js +12 -0
  187. package/dist/schemas/scene/animations.d.ts +89738 -0
  188. package/dist/schemas/scene/animations.js +211 -0
  189. package/dist/schemas/scene/components.js +515 -0
  190. package/dist/schemas/scene/core.js +160 -0
  191. package/dist/schemas/scene/index.d.ts +22 -0
  192. package/dist/schemas/scene/index.js +10 -0
  193. package/dist/schemas/scene/properties.d.ts +914 -0
  194. package/dist/schemas/scene/properties.js +398 -0
  195. package/dist/schemas/scene/subtitles.d.ts +1141 -0
  196. package/dist/schemas/scene/subtitles.js +111 -0
  197. package/dist/schemas/scene/utils.d.ts +1 -0
  198. package/dist/schemas/scene/utils.js +5 -0
  199. package/dist/seeds/SeedFactory.d.ts +59 -0
  200. package/dist/seeds/SeedFactory.js +99 -0
  201. package/dist/seeds/index.d.ts +8 -0
  202. package/dist/seeds/index.js +8 -0
  203. package/dist/transformers/ColorTransformer.d.ts +5 -0
  204. package/dist/transformers/ColorTransformer.js +67 -0
  205. package/dist/transformers/PixiColorTransformer.d.ts +22 -0
  206. package/dist/transformers/PixiColorTransformer.js +104 -0
  207. package/dist/utils/canvas.d.ts +6 -0
  208. package/dist/utils/canvas.js +18 -0
  209. package/dist/utils/document.d.ts +2 -0
  210. package/dist/utils/document.js +36 -0
  211. package/dist/utils/emoji.d.ts +10 -0
  212. package/dist/utils/emoji.js +51 -0
  213. package/dist/utils/html.d.ts +4 -0
  214. package/dist/utils/html.js +45 -0
  215. package/dist/utils/svgGenerator.d.ts +20 -0
  216. package/dist/utils/svgGenerator.js +103 -0
  217. package/dist/utils/utils.d.ts +5 -0
  218. package/dist/utils/utils.js +125 -0
  219. package/package.json +96 -0
@@ -0,0 +1,409 @@
1
+ import * as PIXI from 'pixi.js-legacy';
2
+ import { gsap } from 'gsap';
3
+ import { ComponentShape } from './';
4
+ import { buildCharactersListFromComponentsAndSubtitles, changeIdDeep } from './utils/utils.js';
5
+ import { loadFonts } from './utils/document.js';
6
+ import { CommandType } from './commands/CommandTypes.js';
7
+ import { CommandRunner } from './commands/CommandRunner.js';
8
+ import { StateManager } from './managers/StateManager.svelte.js';
9
+ import { TimelineManager } from './managers/TimelineManager.svelte.js';
10
+ import { EventManager } from './managers/EventManager.js';
11
+ import { DomManager } from './managers/DomManager.js';
12
+ import { AppManager } from './managers/AppManager.svelte.js';
13
+ import { ComponentsManager } from './managers/ComponentsManager.svelte.js';
14
+ import { v4 as uuidv4 } from 'uuid';
15
+ import { MediaManager } from './managers/MediaManager.js';
16
+ import { LayersManager } from './managers/LayersManager.svelte.js';
17
+ import { SubtitlesManager } from './managers/SubtitlesManager.svelte.js';
18
+ import { removeContainer } from './DIContainer.js';
19
+ export class SceneBuilder {
20
+ initialized = false;
21
+ renderTicker;
22
+ timelineManager;
23
+ eventManager;
24
+ domManager;
25
+ appManager;
26
+ layersManager;
27
+ componentsManager;
28
+ stateManager;
29
+ commandRunner;
30
+ mediaManager;
31
+ subtitlesManager;
32
+ fonts;
33
+ // Replace constructor with cradle pattern
34
+ constructor(cradle) {
35
+ this.timelineManager = cradle.timelineManager;
36
+ this.eventManager = cradle.eventManager;
37
+ this.domManager = cradle.domManager;
38
+ this.appManager = cradle.appManager;
39
+ this.layersManager = cradle.layersManager;
40
+ this.componentsManager = cradle.componentsManager;
41
+ this.stateManager = cradle.stateManager;
42
+ this.commandRunner = cradle.commandRunner;
43
+ this.mediaManager = cradle.mediaManager;
44
+ this.subtitlesManager = cradle.subtitlesManager;
45
+ this.fonts = cradle.fonts;
46
+ // TODO - check scene is v2
47
+ // SceneShape.parse(this.sceneData);
48
+ }
49
+ get sceneData() {
50
+ return this.stateManager.data;
51
+ }
52
+ get environment() {
53
+ return this.stateManager.environment;
54
+ }
55
+ get state() {
56
+ return this.stateManager.state;
57
+ }
58
+ get isPlaying() {
59
+ return this.stateManager.isPlaying;
60
+ }
61
+ get isLoading() {
62
+ return this.stateManager.state === 'loading';
63
+ }
64
+ get currentTime() {
65
+ return this.stateManager.currentTime;
66
+ }
67
+ get currentFrame() {
68
+ return Math.round(this.stateManager.currentTime * this.fps);
69
+ }
70
+ get duration() {
71
+ return this.stateManager.duration;
72
+ }
73
+ get progress() {
74
+ // value between 0 - 1, 1 is compo
75
+ return this.duration ? this.currentTime / this.duration : 0;
76
+ }
77
+ get app() {
78
+ return this.appManager.app;
79
+ }
80
+ get timeline() {
81
+ return this.timelineManager.timeline;
82
+ }
83
+ get fps() {
84
+ return this.sceneData.settings.fps ?? 30;
85
+ }
86
+ // we should remove this later and inject domManager where needed instead
87
+ get htmlContainer() {
88
+ return this.domManager.htmlContainer;
89
+ }
90
+ get canvasContainer() {
91
+ return this.domManager.canvas;
92
+ }
93
+ get components() {
94
+ return this.componentsManager;
95
+ }
96
+ get layers() {
97
+ return this.layersManager;
98
+ }
99
+ get subtitles() {
100
+ return this.subtitlesManager;
101
+ }
102
+ get disabledTimeZones() {
103
+ return this.stateManager.disabledTimeZones;
104
+ }
105
+ addExcludedTimestamp(start, end) {
106
+ this.stateManager.data.settings.trimZones = this.stateManager.data.settings?.trimZones || [];
107
+ this.stateManager.data.settings.trimZones.push({
108
+ start: this.stateManager.transformTime(start),
109
+ end: this.stateManager.transformTime(end)
110
+ });
111
+ }
112
+ removeExcludedTimestampsBetween(start, end) {
113
+ const transformedStart = this.stateManager.transformTime(start);
114
+ const transformedEnd = this.stateManager.transformTime(end);
115
+ if (transformedEnd === this.stateManager.startTime) {
116
+ this.setStartAt(undefined);
117
+ }
118
+ if (transformedStart === this.stateManager.endTime) {
119
+ this.setEndAt(undefined);
120
+ }
121
+ if (!this.stateManager.data.settings.trimZones) {
122
+ return;
123
+ }
124
+ this.stateManager.data.settings.trimZones = this.stateManager.data.settings.trimZones.filter(({ start, end }) => {
125
+ // Keep zones that are completely outside the removal range
126
+ return end < transformedStart || start > transformedEnd;
127
+ });
128
+ }
129
+ syncChanges() {
130
+ this.stateManager.updateLayers();
131
+ }
132
+ setStartAt(start) {
133
+ this.stateManager.setStartAt(start ? this.stateManager.transformTime(start) : undefined);
134
+ }
135
+ setEndAt(end) {
136
+ this.stateManager.setEndAt(end ? this.stateManager.transformTime(end) : undefined);
137
+ }
138
+ updateSubtitlesSettings(settings) {
139
+ const currentSubtitlesSettincs = this.stateManager.data.settings.subtitles
140
+ ? this.stateManager.data.settings.subtitles
141
+ : {
142
+ punctuation: true
143
+ };
144
+ const newSettings = { ...currentSubtitlesSettincs, ...settings };
145
+ this.stateManager.updateSceneSubtitlesSettings(newSettings);
146
+ this.subtitlesManager.updateSettings(newSettings);
147
+ }
148
+ dispatchEvent(event, props) {
149
+ this.eventManager.emit(event, props);
150
+ }
151
+ addEventListener(event, callback, options) {
152
+ this.eventManager.addEventListener(event, ((e) => callback(e)), options);
153
+ }
154
+ removeEventListener(event, callback, options) {
155
+ this.eventManager.removeEventListener(event, ((e) => callback(e)), options);
156
+ }
157
+ async run(commandType, props) {
158
+ return await this.commandRunner.run(commandType, props);
159
+ }
160
+ runSync(commandType, props) {
161
+ return this.commandRunner.runSync(commandType, props);
162
+ }
163
+ scale(scale) {
164
+ const clampedScale = Math.max(0.01, Math.min(scale, 2));
165
+ this.appManager.scale(clampedScale);
166
+ this.domManager.scale(clampedScale);
167
+ }
168
+ async loadFonts(fonts) {
169
+ return await loadFonts(fonts);
170
+ }
171
+ async initialize() {
172
+ if (this.initialized) {
173
+ return;
174
+ }
175
+ this.initialized = true;
176
+ gsap.ticker.fps(this.fps);
177
+ this.renderTicker = () => {
178
+ this.render();
179
+ };
180
+ if (this.fonts.length > 0) {
181
+ await this.loadFonts(this.fonts);
182
+ }
183
+ this.layersManager.setAppManager(this.appManager);
184
+ await this.appManager.initialize();
185
+ if (this.stateManager.scale !== 1) {
186
+ this.scale(this.stateManager.scale);
187
+ }
188
+ await this.buildSceneTree();
189
+ this.seek(0);
190
+ this.render();
191
+ this.eventManager.isReady = true;
192
+ this.domManager.removeLoader();
193
+ }
194
+ async buildSceneTree() {
195
+ // Sort layers by order
196
+ const sortedLayers = [...this.sceneData.layers].sort((a, b) => a.order - b.order);
197
+ for (const layerData of sortedLayers) {
198
+ await this.layersManager.create(layerData);
199
+ }
200
+ }
201
+ async addLayer(layerInput) {
202
+ const layers = this.layersManager.getAll().sort((a, b) => b.order - a.order);
203
+ const lastLayerOrder = layers.length ? layers[0].order : 0;
204
+ const sceneLayer = {
205
+ id: layerInput.id ?? uuidv4(),
206
+ name: layerInput.name ?? 'Layer ' + (layers.length + 1),
207
+ order: layerInput.order ?? lastLayerOrder + 1, // TODO - should probably go into the first layer
208
+ components: layerInput.components ?? [],
209
+ visible: layerInput.visible ?? true,
210
+ muted: layerInput.muted ?? false
211
+ };
212
+ const layer = await this.layersManager.create(sceneLayer);
213
+ this.eventManager.emit('layerschange');
214
+ this.eventManager.emit('componentschange');
215
+ return layer ? layer : undefined;
216
+ }
217
+ async addNewLayerWithComponents(components) {
218
+ const layers = this.layersManager.getAll().sort((a, b) => b.order - a.order);
219
+ const lastLayerOrder = layers.length ? layers[0].order : 0;
220
+ const layer = await this.layersManager.create({
221
+ id: uuidv4(),
222
+ name: 'Layer ' + (this.layersManager.getAll().length + 1),
223
+ order: lastLayerOrder + 1,
224
+ components: components,
225
+ visible: true,
226
+ muted: false
227
+ });
228
+ this.eventManager.emit('layerschange');
229
+ this.eventManager.emit('componentschange');
230
+ return layer ? layer : undefined;
231
+ }
232
+ async addComponent(componentInput) {
233
+ // 1. Parse the input using the Zod schema
234
+ const result = ComponentShape.safeParse(componentInput);
235
+ // 2. Handle validation failure
236
+ if (!result.success) {
237
+ console.error('Invalid component input:', result.error.format());
238
+ // Potentially emit an error event or throw
239
+ return undefined;
240
+ }
241
+ // 3. Work with the validated and normalized data (result.data is type ComponentData)
242
+ const validatedComponentData = result.data;
243
+ // Pass the validated data
244
+ const layer = await this.addNewLayerWithComponents([componentInput]);
245
+ if (!layer) {
246
+ // Handle layer creation failure if necessary
247
+ return undefined;
248
+ }
249
+ // Return the validated data object
250
+ return validatedComponentData;
251
+ }
252
+ async splitComponent(component) {
253
+ // Find the layer containing this component
254
+ const compStart = component.props.timeline.startAt;
255
+ const compEnd = component.props.timeline.endAt;
256
+ const currentTime = this.currentTime;
257
+ const isInTime = currentTime > compStart && currentTime < compEnd;
258
+ if (!isInTime) {
259
+ return false;
260
+ }
261
+ const layer = this.layers
262
+ .getAll()
263
+ .find((layer) => layer.components.some((comp) => comp.id === component.id));
264
+ if (!layer) {
265
+ return false;
266
+ }
267
+ // Create clone of component data with adjusted times
268
+ const originalEndAt = component.props.timeline.endAt;
269
+ const newData = changeIdDeep(component.props.getData());
270
+ const cloneData = {
271
+ ...newData,
272
+ id: uuidv4(), // Generate new ID for clone
273
+ timeline: {
274
+ ...newData.timeline,
275
+ endAt: originalEndAt
276
+ },
277
+ checksum: 'new-' + uuidv4()
278
+ };
279
+ // Update original component's end time
280
+ component.props.setEnd(this.currentTime);
281
+ // Add the cloned component to the same layer
282
+ const newComponent = await this.componentsManager.create(cloneData);
283
+ newComponent?.props.setStart(this.currentTime);
284
+ if (newComponent) {
285
+ layer.addComponent(newComponent);
286
+ }
287
+ this.dispatchEvent('componentschange');
288
+ return true;
289
+ }
290
+ async seek(time) {
291
+ await this.run(CommandType.SEEK, { time });
292
+ }
293
+ async replaceSourceOnTime(time, componentId, base64data) {
294
+ await this.run(CommandType.REPLACE_SOURCE_ON_TIME, {
295
+ time,
296
+ componentId,
297
+ base64data
298
+ });
299
+ }
300
+ async seekAndRenderFrame(time, target, format = 'png', quality = 1) {
301
+ await this.seek(time);
302
+ // Ensure scene is rendered after seek so media hooks apply their updates
303
+ this.render();
304
+ const frame = await this.renderFrame(target, format, quality);
305
+ return frame;
306
+ }
307
+ /**
308
+ * Check if seeking to a specific time would result in visual changes
309
+ * without actually extracting frame data.
310
+ *
311
+ * This is useful for determining if consecutive frames are identical,
312
+ * allowing you to skip rendering and return cached frame data.
313
+ *
314
+ * @param time Time in seconds to check
315
+ * @returns Promise<boolean> True if scene has visual changes at this time
316
+ *
317
+ * @example
318
+ * // Check if frame needs re-rendering
319
+ * const time = frame / sceneBuilder.fps;
320
+ * const isDirty = await sceneBuilder.isSceneDirty(time);
321
+ *
322
+ * if (!isDirty && isConsecutiveFrame) {
323
+ * // Return cached frame - scene hasn't changed
324
+ * return cachedFrameData;
325
+ * } else {
326
+ * // Render new frame - scene has changes
327
+ * return await sceneBuilder.seekAndRenderFrame(time);
328
+ * }
329
+ */
330
+ async isSceneDirty(time) {
331
+ // Seek to the specified time to trigger component/hook updates
332
+ await this.seek(time);
333
+ // Get current dirty state BEFORE calling render()
334
+ // This prevents render() from clearing the dirty flag
335
+ const wasDirty = this.stateManager.isDirty;
336
+ // Render to update all display objects and let hooks detect changes
337
+ // This triggers all hook updates but doesn't extract frame data
338
+ this.render();
339
+ // Return the dirty state we captured before render()
340
+ // Don't clear the dirty flag - let actual frame extraction handle that
341
+ return wasDirty;
342
+ }
343
+ async renderFrame(target, format = 'png', quality = 1) {
344
+ const frame = (await this.run(CommandType.RENDER_FRAME, { target, format, quality }));
345
+ if (!frame) {
346
+ throw new Error('Rendering frame failed');
347
+ }
348
+ return frame;
349
+ }
350
+ log(message) {
351
+ $effect.root(function () {
352
+ $inspect(message);
353
+ });
354
+ }
355
+ play(changeState = true) {
356
+ if (this.state === 'loading' || this.state === 'playing') {
357
+ return;
358
+ }
359
+ if (changeState) {
360
+ this.stateManager.changeState('playing');
361
+ }
362
+ this.timelineManager.play();
363
+ gsap.ticker.add(this.renderTicker);
364
+ }
365
+ pause(changeState = true) {
366
+ if (changeState) {
367
+ this.stateManager.changeState('paused');
368
+ }
369
+ this.timelineManager.pause();
370
+ this.render();
371
+ gsap.ticker.remove(this.renderTicker);
372
+ }
373
+ setPlaybackRate(rate) {
374
+ this.timeline.timeScale(rate);
375
+ }
376
+ addLoadingComponent(componentId) {
377
+ this.stateManager.addLoadingComponent(componentId);
378
+ }
379
+ removeLoadingComponent(componentId) {
380
+ this.stateManager.removeLoadingComponent(componentId);
381
+ }
382
+ buildCharactersList() {
383
+ // TODO - refactor
384
+ this.stateManager.setCharactersList(buildCharactersListFromComponentsAndSubtitles(this.sceneData.layers, this.subtitles.data));
385
+ }
386
+ render() {
387
+ const rendered = this.runSync(CommandType.RENDER);
388
+ if (rendered === false) {
389
+ this.stateManager.setRenderAfterLoadingFinished(true);
390
+ }
391
+ if (this.stateManager.state === 'ended') {
392
+ gsap.ticker.remove(this.renderTicker);
393
+ }
394
+ }
395
+ destroy() {
396
+ // Stop the timeline and remove the render ticker
397
+ gsap.ticker.remove(this.renderTicker);
398
+ // Clear the components map
399
+ this.initialized = false;
400
+ this.appManager.destroy();
401
+ this.domManager.destroy();
402
+ this.stateManager.destroy();
403
+ this.mediaManager.destroy();
404
+ this.timelineManager.destroy();
405
+ this.componentsManager.destroy();
406
+ // Remove the container from the DI container cache
407
+ removeContainer(this.sceneData.id);
408
+ }
409
+ }
@@ -0,0 +1,2 @@
1
+ import type { SubtitleWithCompactWords, Subtitle } from '..';
2
+ export declare function normalizeSubtitle(subtitle: Subtitle): SubtitleWithCompactWords | null;
@@ -0,0 +1,187 @@
1
+ import { SubtitleWithCompactWordsShape, ColorTypeShape } from '..';
2
+ // ============================================================================
3
+ // TYPE GUARDS
4
+ // ============================================================================
5
+ /**
6
+ * Type guard to check if a word is a CompactWordTuple
7
+ */
8
+ function isCompactWordTuple(word) {
9
+ return Array.isArray(word);
10
+ }
11
+ /**
12
+ * Type guard to check if a word is a SubtitleWord
13
+ */
14
+ function isSubtitleWord(word) {
15
+ return !Array.isArray(word);
16
+ }
17
+ /**
18
+ * Safely access CompactWordTuple elements with bounds checking
19
+ */
20
+ function safeGetTupleElement(tuple, index) {
21
+ return index < tuple.length ? tuple[index] : undefined;
22
+ }
23
+ /**
24
+ * Safely get text from CompactWordTuple with type checking
25
+ */
26
+ function safeGetWordText(word) {
27
+ const text = safeGetTupleElement(word, 0);
28
+ return text || '';
29
+ }
30
+ /**
31
+ * Safely get start time from CompactWordTuple with type checking
32
+ */
33
+ function safeGetWordStart(word) {
34
+ const start = safeGetTupleElement(word, 1);
35
+ return start || 0;
36
+ }
37
+ /**
38
+ * Safely get end time from CompactWordTuple with type checking
39
+ */
40
+ function safeGetWordEnd(word) {
41
+ const end = safeGetTupleElement(word, 2);
42
+ return end || 0;
43
+ }
44
+ /**
45
+ * Safely get metadata from CompactWordTuple with type checking
46
+ */
47
+ function safeGetWordMeta(word) {
48
+ return safeGetTupleElement(word, 3);
49
+ }
50
+ // ============================================================================
51
+ // WORD PROPERTY ACCESS
52
+ // ============================================================================
53
+ function wordText(word) {
54
+ return isCompactWordTuple(word) ? safeGetWordText(word) : word.text;
55
+ }
56
+ function wordStart(word) {
57
+ return isCompactWordTuple(word) ? safeGetWordStart(word) : word.start_at;
58
+ }
59
+ function wordEnd(word) {
60
+ return isCompactWordTuple(word) ? safeGetWordEnd(word) : word.end_at;
61
+ }
62
+ function wordMeta(word) {
63
+ return safeGetWordMeta(word);
64
+ }
65
+ // ============================================================================
66
+ // METADATA HELPERS
67
+ // ============================================================================
68
+ function hasEmoji(word) {
69
+ return wordMeta(word)?.e ? true : false;
70
+ }
71
+ function getSpeakerIndex(word) {
72
+ return wordMeta(word)?.s;
73
+ }
74
+ function getColor(word) {
75
+ const color = wordMeta(word)?.c;
76
+ if (color) {
77
+ const c = ColorTypeShape.safeParse(color);
78
+ if (c.success) {
79
+ return c.data;
80
+ }
81
+ }
82
+ return undefined;
83
+ }
84
+ // ============================================================================
85
+ // FORMAT CONVERSION - WORD LEVEL
86
+ // ============================================================================
87
+ function compactToLegacy(wordTuple, parentSubtitleId, position) {
88
+ const [text, start_at, end_at] = wordTuple;
89
+ return {
90
+ id: `${parentSubtitleId}-word-${position}`,
91
+ start_at,
92
+ end_at,
93
+ text,
94
+ position
95
+ };
96
+ }
97
+ function legacyToCompact(word, includeMetadata = false) {
98
+ const tuple = [word.text, word.start_at, word.end_at];
99
+ if (includeMetadata) {
100
+ const metadata = {};
101
+ tuple.push(metadata);
102
+ }
103
+ return tuple;
104
+ }
105
+ // ============================================================================
106
+ // FORMAT CONVERSION - SUBTITLE LEVEL
107
+ // ============================================================================
108
+ /**
109
+ * Convert subtitle with legacy words to compact format
110
+ */
111
+ function subtitleToCompact(subtitle) {
112
+ const compactWords = subtitle.words?.map((word) => legacyToCompact(word));
113
+ return {
114
+ ...subtitle,
115
+ words: compactWords
116
+ };
117
+ }
118
+ /**
119
+ * Convert subtitle with compact words to legacy format
120
+ */
121
+ function subtitleToLegacy(subtitle) {
122
+ const legacyWords = subtitle.words?.map((wordTuple, index) => compactToLegacy(wordTuple, subtitle.id, index));
123
+ return {
124
+ ...subtitle,
125
+ words: legacyWords,
126
+ // Convert ColorType properties to string if needed
127
+ color: typeof subtitle.color === 'string' ? subtitle.color : undefined,
128
+ background: typeof subtitle.background === 'string' ? subtitle.background : undefined
129
+ };
130
+ }
131
+ // ============================================================================
132
+ // COLLECTION UTILITIES
133
+ // ============================================================================
134
+ export function normalizeSubtitle(subtitle) {
135
+ // Start with the original subtitle as the candidate we'll validate
136
+ let candidate = subtitle;
137
+ // If words exist and are in legacy format, convert to compact
138
+ if (subtitle.words && subtitle.words.length > 0) {
139
+ const firstWord = subtitle.words[0];
140
+ if (Array.isArray(firstWord)) {
141
+ candidate = subtitle;
142
+ }
143
+ else {
144
+ candidate = subtitleToCompact(subtitle);
145
+ }
146
+ }
147
+ const resp = SubtitleWithCompactWordsShape.safeParse(candidate);
148
+ if (resp.success) {
149
+ return resp.data;
150
+ }
151
+ else {
152
+ console.error('Failed to normalize subtitle', candidate, resp.error);
153
+ }
154
+ return null;
155
+ }
156
+ /**
157
+ * Normalize subtitle collection to use compact format internally
158
+ */
159
+ function normalizeCollection(collection) {
160
+ const normalized = {};
161
+ for (const [key, subtitles] of Object.entries(collection)) {
162
+ normalized[key] = subtitles
163
+ .map((subtitle) => {
164
+ return normalizeSubtitle(subtitle); //subtitle as SubtitleWithCompactWords;
165
+ })
166
+ .filter(Boolean);
167
+ }
168
+ return normalized;
169
+ }
170
+ /**
171
+ * Calculate file size savings from using compact format
172
+ */
173
+ function calculateSavings(collection) {
174
+ const legacyJson = JSON.stringify(collection);
175
+ const compactCollection = normalizeCollection(collection);
176
+ const compactJson = JSON.stringify(compactCollection);
177
+ const legacySize = legacyJson.length;
178
+ const compactSize = compactJson.length;
179
+ const savingsBytes = legacySize - compactSize;
180
+ const savingsPercent = (savingsBytes / legacySize) * 100;
181
+ return {
182
+ legacySize,
183
+ compactSize,
184
+ savingsBytes,
185
+ savingsPercent: Math.round(savingsPercent * 100) / 100
186
+ };
187
+ }
@@ -0,0 +1,17 @@
1
+ import type { AnimationPreset } from './animationPreset.js';
2
+ import { SplitTextCache } from './SplitTextCache.js';
3
+ export declare class AnimationContext {
4
+ rootElement: any;
5
+ preset: AnimationPreset;
6
+ splitTextCache: SplitTextCache;
7
+ data: Record<string, any>;
8
+ private animationTargetDuration;
9
+ constructor(rootElement: any, preset: AnimationPreset, splitTextCache: SplitTextCache, data?: Record<string, any>);
10
+ get endAnchor(): number;
11
+ setAnimationTargetDuration(duration: number): void;
12
+ getData(key: string): any;
13
+ getElement(targetQuery: string): any;
14
+ getDuration(): number;
15
+ getAnimationTargetDuration(): number;
16
+ getAllData(): Record<string, any>;
17
+ }
@@ -0,0 +1,72 @@
1
+ import { SplitTextCache } from './SplitTextCache.js';
2
+ export class AnimationContext {
3
+ rootElement;
4
+ preset;
5
+ splitTextCache;
6
+ data;
7
+ animationTargetDuration;
8
+ constructor(rootElement, preset, splitTextCache, data = {}) {
9
+ this.rootElement = rootElement;
10
+ this.preset = preset;
11
+ this.splitTextCache = splitTextCache;
12
+ this.data = data;
13
+ let textShadow = '';
14
+ if (this.rootElement instanceof HTMLElement) {
15
+ textShadow = this.rootElement.style.textShadow ?? '';
16
+ }
17
+ this.data.textShadow = textShadow;
18
+ this.animationTargetDuration = 0;
19
+ this.preset.preset.data = {
20
+ ...this.preset.preset.data,
21
+ ...this.data
22
+ };
23
+ }
24
+ get endAnchor() {
25
+ return this.animationTargetDuration - this.getDuration();
26
+ }
27
+ setAnimationTargetDuration(duration) {
28
+ this.animationTargetDuration = duration;
29
+ }
30
+ getData(key) {
31
+ return this.data[key];
32
+ }
33
+ getElement(targetQuery) {
34
+ if (!(this.rootElement instanceof HTMLElement)) {
35
+ console.warn(`Root element is not an HTMLElement. Cannot target ${targetQuery}`);
36
+ return this.rootElement;
37
+ }
38
+ const splitTargets = ['words', 'lines', 'chars'];
39
+ if (splitTargets.includes(targetQuery)) {
40
+ // Get the splitText instance from the global singleton cache
41
+ return this.splitTextCache.getSplitText(this.rootElement, targetQuery);
42
+ }
43
+ if (targetQuery.startsWith('#') || targetQuery.startsWith('.')) {
44
+ const element = this.rootElement.querySelector(targetQuery);
45
+ if (!element) {
46
+ console.warn(`Element not found for query: ${targetQuery}`);
47
+ return null;
48
+ }
49
+ return element;
50
+ }
51
+ if (targetQuery === 'child') {
52
+ if (this.rootElement.children.length === 0) {
53
+ console.warn('No children found for "child" query');
54
+ return null;
55
+ }
56
+ return this.rootElement.children[0];
57
+ }
58
+ if (targetQuery === 'children') {
59
+ return this.rootElement.children;
60
+ }
61
+ return this.rootElement;
62
+ }
63
+ getDuration() {
64
+ return this.preset.duration;
65
+ }
66
+ getAnimationTargetDuration() {
67
+ return this.animationTargetDuration;
68
+ }
69
+ getAllData() {
70
+ return this.data;
71
+ }
72
+ }