visualfries 0.1.902 → 0.1.1096

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.
@@ -264,16 +264,11 @@ export class SceneBuilder {
264
264
  if (!layer) {
265
265
  return false;
266
266
  }
267
- // Create clone of component data with adjusted times
268
- const originalEndAt = component.props.timeline.endAt;
267
+ // Create clone of component data EXACTLY as it is
269
268
  const newData = changeIdDeep(component.props.getData());
270
269
  const cloneData = {
271
270
  ...newData,
272
271
  id: uuidv4(), // Generate new ID for clone
273
- timeline: {
274
- ...newData.timeline,
275
- endAt: originalEndAt
276
- },
277
272
  checksum: 'new-' + uuidv4()
278
273
  };
279
274
  // Update original component's end time
@@ -110,6 +110,11 @@ export class WordHighlighterAnimationBuilder {
110
110
  };
111
111
  }
112
112
  static createBackgroundElement(target) {
113
+ // Clean up any existing highlighter-bg element from previous builds
114
+ const existingBg = target.querySelector('#highlighter-bg');
115
+ if (existingBg) {
116
+ existingBg.remove();
117
+ }
113
118
  const bgHighlighter = document.createElement('div');
114
119
  bgHighlighter.id = 'highlighter-bg';
115
120
  bgHighlighter.style.position = 'absolute';
@@ -108,39 +108,42 @@ export class ComponentState {
108
108
  this.#emitChange();
109
109
  }
110
110
  #changeVideoStart(diff) {
111
- if (this.type === 'VIDEO') {
111
+ if (this.type === 'VIDEO' || this.type === 'AUDIO') {
112
112
  let source = this.#data.source;
113
113
  if (!source) {
114
- source = {
115
- startAt: 0
116
- };
114
+ source = { startAt: 0 };
117
115
  this.#data.source = source;
118
116
  }
119
117
  if (source.startAt !== undefined && source.startAt !== null) {
120
118
  source.startAt += diff;
121
119
  source.startAt = Math.max(0, source.startAt);
122
120
  }
121
+ }
122
+ }
123
+ #changeVideoEnd(diff) {
124
+ if (this.type === 'VIDEO' || this.type === 'AUDIO') {
125
+ let source = this.#data.source;
126
+ if (!source) {
127
+ source = { startAt: 0 };
128
+ this.#data.source = source;
129
+ }
130
+ const currentDuration = this.#data.timeline.endAt - this.#data.timeline.startAt;
123
131
  if (source.endAt !== undefined && source.endAt !== null) {
124
132
  source.endAt += diff;
125
133
  }
134
+ else {
135
+ // Initialize the implicit end bound via start bound prior to truncation
136
+ const startAt = source.startAt || 0;
137
+ source.endAt = startAt + currentDuration + diff;
138
+ }
126
139
  }
127
- // TODO verify starting time is not beyond video duration
128
- // const metadata = this.#data!.metadata
129
- // ? (this.#data!.metadata as Metadata)
130
- // : ({ starting_time: 0 } as Metadata);
131
- // let startingTime = metadata.starting_time ? (metadata.starting_time as number) : 0;
132
- // startingTime += diff;
133
- // startingTime = Math.max(0, startingTime);
134
- // this.updateMetadata({
135
- // starting_time: startingTime
136
- // });
137
140
  }
138
141
  setStart(start) {
139
142
  const beforeStart = this.sceneState.transformTime(this.#data.timeline.startAt);
140
143
  const newStart = this.sceneState.transformTime(start);
141
144
  const diff = newStart - beforeStart;
142
145
  if (diff !== 0) {
143
- if (this.type === 'VIDEO') {
146
+ if (this.type === 'VIDEO' || this.type === 'AUDIO') {
144
147
  this.#changeVideoStart(diff);
145
148
  }
146
149
  this.#data.timeline.startAt = this.sceneState.transformTime(start);
@@ -148,8 +151,16 @@ export class ComponentState {
148
151
  }
149
152
  }
150
153
  setEnd(end) {
151
- this.#data.timeline.endAt = this.sceneState.transformTime(end);
152
- this.#emitChange();
154
+ const beforeEnd = this.sceneState.transformTime(this.#data.timeline.endAt);
155
+ const newEnd = this.sceneState.transformTime(end);
156
+ const diff = newEnd - beforeEnd;
157
+ if (diff !== 0) {
158
+ if (this.type === 'VIDEO' || this.type === 'AUDIO') {
159
+ this.#changeVideoEnd(diff);
160
+ }
161
+ this.#data.timeline.endAt = this.sceneState.transformTime(end);
162
+ this.#emitChange();
163
+ }
153
164
  }
154
165
  setStreamPath(path) {
155
166
  // if (this.type === 'VIDEO' || this.type === 'AUDIO') {
@@ -179,8 +190,10 @@ export class ComponentState {
179
190
  // }
180
191
  }
181
192
  async updateAppearance(appearance) {
182
- const mergedAppearance = merge({}, this.#data.appearance, appearance);
183
- this.#data = { ...this.#data, appearance: mergedAppearance };
193
+ // Use $state.snapshot() to properly extract all properties from the reactive proxy
194
+ const currentData = $state.snapshot(this.#data);
195
+ const mergedAppearance = merge({}, currentData.appearance, appearance);
196
+ this.#data = { ...currentData, appearance: mergedAppearance };
184
197
  this.#emitChange();
185
198
  await this.maybeAutoRefresh();
186
199
  }
@@ -23,6 +23,21 @@ export class SeekCommand {
23
23
  this.timeline.seek(time);
24
24
  // Ensure a deterministic render on server after seek to advance media frames
25
25
  if (this.state.environment === 'server') {
26
+ // Wait for fonts to be ready before rendering
27
+ // This is critical for subtitle animations that use SplitText -
28
+ // if fonts aren't loaded, text measurements will be wrong and
29
+ // animations may fail silently or not appear on first subtitles
30
+ if (typeof document !== 'undefined' && document.fonts?.ready) {
31
+ try {
32
+ await Promise.race([
33
+ document.fonts.ready,
34
+ new Promise((resolve) => setTimeout(resolve, 2000)) // 2s timeout
35
+ ]);
36
+ }
37
+ catch {
38
+ // Ignore font loading errors, continue with rendering
39
+ }
40
+ }
26
41
  // Try multiple render passes until loading state clears or attempts exhausted
27
42
  const maxAttempts = 10;
28
43
  for (let i = 0; i < maxAttempts; i += 1) {
@@ -34,6 +49,11 @@ export class SeekCommand {
34
49
  if (this.state.state === 'loading') {
35
50
  console.warn('SeekCommand: Max render attempts exhausted while still loading');
36
51
  }
52
+ // Re-seek to apply correct animation state to any animations
53
+ // that were added during the render passes above.
54
+ // This fixes the race condition where subtitle animations are added
55
+ // AFTER the initial seek, causing them to miss their initial state.
56
+ this.timeline.seek(time);
37
57
  }
38
58
  }
39
59
  }
@@ -98,7 +98,22 @@ export class ComponentContext {
98
98
  const sortedHooks = [...hooks].sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0));
99
99
  for (let i = 0; i < sortedHooks.length; i += 1) {
100
100
  const handler = sortedHooks[i];
101
- await handler.handle(type, this);
101
+ try {
102
+ await handler.handle(type, this);
103
+ }
104
+ catch (error) {
105
+ // Log the error but continue to next hook
106
+ const hookName = handler.constructor?.name ?? `Hook[${i}]`;
107
+ console.warn(`[ComponentContext] Hook "${hookName}" failed during "${type}" for component "${this.id}":`, error instanceof Error ? error.message : String(error));
108
+ // Emit error event for debugging/monitoring
109
+ this.eventManager.emit('hookerror', {
110
+ hookName,
111
+ hookType: type,
112
+ error: error instanceof Error ? error : new Error(String(error)),
113
+ componentId: this.id,
114
+ timestamp: Date.now()
115
+ });
116
+ }
102
117
  }
103
118
  }
104
119
  destroy() { }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Component Context Helpers
3
+ *
4
+ * Type guards and helper functions for type-safe access to component-specific data
5
+ * in hook contexts. Eliminates the need for `as any` casts throughout the codebase.
6
+ */
7
+ import type { Component, VideoComponent, AudioComponent, ImageComponent, GifComponent, TextComponent, SubtitleComponent, ShapeComponent, ColorComponent, GradientComponent, ComponentSource, IComponentContext } from '..';
8
+ export declare const MEDIA_COMPONENT_TYPES: readonly ["VIDEO", "AUDIO"];
9
+ export declare const SOURCE_COMPONENT_TYPES: readonly ["VIDEO", "AUDIO", "IMAGE", "GIF"];
10
+ export declare const VISUAL_COMPONENT_TYPES: readonly ["VIDEO", "IMAGE", "GIF", "TEXT", "SHAPE", "SUBTITLES", "COLOR", "GRADIENT"];
11
+ export type MediaComponentType = (typeof MEDIA_COMPONENT_TYPES)[number];
12
+ export type SourceComponentType = (typeof SOURCE_COMPONENT_TYPES)[number];
13
+ /**
14
+ * Type guard for VIDEO component
15
+ */
16
+ export declare function isVideoComponent(data: Component | undefined): data is VideoComponent;
17
+ /**
18
+ * Type guard for AUDIO component
19
+ */
20
+ export declare function isAudioComponent(data: Component | undefined): data is AudioComponent;
21
+ /**
22
+ * Type guard for IMAGE component
23
+ */
24
+ export declare function isImageComponent(data: Component | undefined): data is ImageComponent;
25
+ /**
26
+ * Type guard for GIF component
27
+ */
28
+ export declare function isGifComponent(data: Component | undefined): data is GifComponent;
29
+ /**
30
+ * Type guard for TEXT component
31
+ */
32
+ export declare function isTextComponent(data: Component | undefined): data is TextComponent;
33
+ /**
34
+ * Type guard for SUBTITLES component
35
+ */
36
+ export declare function isSubtitleComponent(data: Component | undefined): data is SubtitleComponent;
37
+ /**
38
+ * Type guard for SHAPE component
39
+ */
40
+ export declare function isShapeComponent(data: Component | undefined): data is ShapeComponent;
41
+ /**
42
+ * Type guard for COLOR component
43
+ */
44
+ export declare function isColorComponent(data: Component | undefined): data is ColorComponent;
45
+ /**
46
+ * Type guard for GRADIENT component
47
+ */
48
+ export declare function isGradientComponent(data: Component | undefined): data is GradientComponent;
49
+ /**
50
+ * Type guard for components with a media source (VIDEO, AUDIO, IMAGE, GIF)
51
+ */
52
+ export declare function hasSource(data: Component | undefined): data is VideoComponent | AudioComponent | ImageComponent | GifComponent;
53
+ /**
54
+ * Type guard for media components (VIDEO, AUDIO)
55
+ */
56
+ export declare function isMediaComponent(data: Component | undefined): data is VideoComponent | AudioComponent;
57
+ /**
58
+ * Safely get source from component data
59
+ */
60
+ export declare function getSource(data: Component | undefined): ComponentSource | undefined;
61
+ /**
62
+ * Safely get source.url from component data
63
+ */
64
+ export declare function getSourceUrl(data: Component | undefined): string | undefined;
65
+ /**
66
+ * Safely get source.startAt from component data (for VIDEO/AUDIO)
67
+ */
68
+ export declare function getSourceStartAt(data: Component | undefined): number | undefined;
69
+ /**
70
+ * Safely get muted state from media component
71
+ */
72
+ export declare function getMuted(data: Component | undefined): boolean;
73
+ /**
74
+ * Safely get volume from media component
75
+ */
76
+ export declare function getVolume(data: Component | undefined): number;
77
+ /**
78
+ * Safely get text content from TEXT component
79
+ */
80
+ export declare function getTextContent(data: Component | undefined): string | undefined;
81
+ /**
82
+ * Get typed component data from context
83
+ */
84
+ export declare function getContextData<T extends Component>(context: IComponentContext, typeGuard: (data: Component | undefined) => data is T): T | undefined;
85
+ /**
86
+ * Get video component data from context
87
+ */
88
+ export declare function getVideoData(context: IComponentContext): VideoComponent | undefined;
89
+ /**
90
+ * Get audio component data from context
91
+ */
92
+ export declare function getAudioData(context: IComponentContext): AudioComponent | undefined;
93
+ /**
94
+ * Get image component data from context
95
+ */
96
+ export declare function getImageData(context: IComponentContext): ImageComponent | undefined;
97
+ /**
98
+ * Get gif component data from context
99
+ */
100
+ export declare function getGifData(context: IComponentContext): GifComponent | undefined;
101
+ /**
102
+ * Get text component data from context
103
+ */
104
+ export declare function getTextData(context: IComponentContext): TextComponent | undefined;
105
+ /**
106
+ * Get source from context's component data
107
+ */
108
+ export declare function getContextSource(context: IComponentContext): ComponentSource | undefined;
109
+ /**
110
+ * Get media properties (muted, volume) from context
111
+ */
112
+ export declare function getMediaProps(context: IComponentContext): {
113
+ muted: boolean;
114
+ volume: number;
115
+ };
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Component Context Helpers
3
+ *
4
+ * Type guards and helper functions for type-safe access to component-specific data
5
+ * in hook contexts. Eliminates the need for `as any` casts throughout the codebase.
6
+ */
7
+ // ============================================================================
8
+ // Component Type Constants
9
+ // ============================================================================
10
+ export const MEDIA_COMPONENT_TYPES = ['VIDEO', 'AUDIO'];
11
+ export const SOURCE_COMPONENT_TYPES = ['VIDEO', 'AUDIO', 'IMAGE', 'GIF'];
12
+ export const VISUAL_COMPONENT_TYPES = ['VIDEO', 'IMAGE', 'GIF', 'TEXT', 'SHAPE', 'SUBTITLES', 'COLOR', 'GRADIENT'];
13
+ // ============================================================================
14
+ // Type Guards for Component Data
15
+ // ============================================================================
16
+ /**
17
+ * Type guard for VIDEO component
18
+ */
19
+ export function isVideoComponent(data) {
20
+ return data?.type === 'VIDEO';
21
+ }
22
+ /**
23
+ * Type guard for AUDIO component
24
+ */
25
+ export function isAudioComponent(data) {
26
+ return data?.type === 'AUDIO';
27
+ }
28
+ /**
29
+ * Type guard for IMAGE component
30
+ */
31
+ export function isImageComponent(data) {
32
+ return data?.type === 'IMAGE';
33
+ }
34
+ /**
35
+ * Type guard for GIF component
36
+ */
37
+ export function isGifComponent(data) {
38
+ return data?.type === 'GIF';
39
+ }
40
+ /**
41
+ * Type guard for TEXT component
42
+ */
43
+ export function isTextComponent(data) {
44
+ return data?.type === 'TEXT';
45
+ }
46
+ /**
47
+ * Type guard for SUBTITLES component
48
+ */
49
+ export function isSubtitleComponent(data) {
50
+ return data?.type === 'SUBTITLES';
51
+ }
52
+ /**
53
+ * Type guard for SHAPE component
54
+ */
55
+ export function isShapeComponent(data) {
56
+ return data?.type === 'SHAPE';
57
+ }
58
+ /**
59
+ * Type guard for COLOR component
60
+ */
61
+ export function isColorComponent(data) {
62
+ return data?.type === 'COLOR';
63
+ }
64
+ /**
65
+ * Type guard for GRADIENT component
66
+ */
67
+ export function isGradientComponent(data) {
68
+ return data?.type === 'GRADIENT';
69
+ }
70
+ /**
71
+ * Type guard for components with a media source (VIDEO, AUDIO, IMAGE, GIF)
72
+ */
73
+ export function hasSource(data) {
74
+ return (data !== undefined &&
75
+ SOURCE_COMPONENT_TYPES.includes(data.type));
76
+ }
77
+ /**
78
+ * Type guard for media components (VIDEO, AUDIO)
79
+ */
80
+ export function isMediaComponent(data) {
81
+ return (data !== undefined &&
82
+ MEDIA_COMPONENT_TYPES.includes(data.type));
83
+ }
84
+ // ============================================================================
85
+ // Safe Accessor Helpers
86
+ // ============================================================================
87
+ /**
88
+ * Safely get source from component data
89
+ */
90
+ export function getSource(data) {
91
+ if (hasSource(data)) {
92
+ return data.source;
93
+ }
94
+ return undefined;
95
+ }
96
+ /**
97
+ * Safely get source.url from component data
98
+ */
99
+ export function getSourceUrl(data) {
100
+ return getSource(data)?.url;
101
+ }
102
+ /**
103
+ * Safely get source.startAt from component data (for VIDEO/AUDIO)
104
+ */
105
+ export function getSourceStartAt(data) {
106
+ const startAt = getSource(data)?.startAt;
107
+ // Convert null to undefined for consistency
108
+ return startAt ?? undefined;
109
+ }
110
+ /**
111
+ * Safely get muted state from media component
112
+ */
113
+ export function getMuted(data) {
114
+ if (isVideoComponent(data) || isAudioComponent(data)) {
115
+ return data.muted ?? false;
116
+ }
117
+ return false;
118
+ }
119
+ /**
120
+ * Safely get volume from media component
121
+ */
122
+ export function getVolume(data) {
123
+ if (isVideoComponent(data) || isAudioComponent(data)) {
124
+ return data.volume ?? 1;
125
+ }
126
+ return 1;
127
+ }
128
+ /**
129
+ * Safely get text content from TEXT component
130
+ */
131
+ export function getTextContent(data) {
132
+ if (isTextComponent(data)) {
133
+ return data.text;
134
+ }
135
+ return undefined;
136
+ }
137
+ // ============================================================================
138
+ // Context Helper Functions
139
+ // ============================================================================
140
+ /**
141
+ * Get typed component data from context
142
+ */
143
+ export function getContextData(context, typeGuard) {
144
+ const data = context.contextData;
145
+ if (typeGuard(data)) {
146
+ return data;
147
+ }
148
+ return undefined;
149
+ }
150
+ /**
151
+ * Get video component data from context
152
+ */
153
+ export function getVideoData(context) {
154
+ return getContextData(context, isVideoComponent);
155
+ }
156
+ /**
157
+ * Get audio component data from context
158
+ */
159
+ export function getAudioData(context) {
160
+ return getContextData(context, isAudioComponent);
161
+ }
162
+ /**
163
+ * Get image component data from context
164
+ */
165
+ export function getImageData(context) {
166
+ return getContextData(context, isImageComponent);
167
+ }
168
+ /**
169
+ * Get gif component data from context
170
+ */
171
+ export function getGifData(context) {
172
+ return getContextData(context, isGifComponent);
173
+ }
174
+ /**
175
+ * Get text component data from context
176
+ */
177
+ export function getTextData(context) {
178
+ return getContextData(context, isTextComponent);
179
+ }
180
+ /**
181
+ * Get source from context's component data
182
+ */
183
+ export function getContextSource(context) {
184
+ const data = context.contextData;
185
+ return getSource(data);
186
+ }
187
+ /**
188
+ * Get media properties (muted, volume) from context
189
+ */
190
+ export function getMediaProps(context) {
191
+ const data = context.contextData;
192
+ return {
193
+ muted: getMuted(data),
194
+ volume: getVolume(data)
195
+ };
196
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Safe Hook Runner
3
+ *
4
+ * Utility for safely executing hooks with error boundaries.
5
+ * Prevents one failing hook from crashing the entire component lifecycle.
6
+ */
7
+ import type { EventManager } from '../managers/EventManager.js';
8
+ export interface HookError {
9
+ hookName: string;
10
+ hookType: string;
11
+ error: Error;
12
+ componentId: string;
13
+ timestamp: number;
14
+ }
15
+ export interface SafeHookRunnerOptions {
16
+ /**
17
+ * Whether to continue executing remaining hooks after one fails
18
+ * @default true
19
+ */
20
+ continueOnError?: boolean;
21
+ /**
22
+ * Whether to log errors to console
23
+ * @default true
24
+ */
25
+ logErrors?: boolean;
26
+ /**
27
+ * Event manager to emit error events
28
+ */
29
+ eventManager?: EventManager;
30
+ }
31
+ /**
32
+ * Safely execute a single hook handler with error boundary
33
+ */
34
+ export declare function safeExecuteHook<T>(hookName: string, hookType: string, componentId: string, handler: () => Promise<T> | T, options?: SafeHookRunnerOptions): Promise<{
35
+ success: boolean;
36
+ result?: T;
37
+ error?: HookError;
38
+ }>;
39
+ /**
40
+ * Execute multiple hooks in sequence with error boundaries
41
+ */
42
+ export declare function safeExecuteHooks(hooks: Array<{
43
+ name: string;
44
+ handler: () => Promise<void> | void;
45
+ }>, hookType: string, componentId: string, options?: SafeHookRunnerOptions): Promise<{
46
+ allSucceeded: boolean;
47
+ errors: HookError[];
48
+ }>;
49
+ /**
50
+ * Create a wrapped version of a hook handler that catches errors
51
+ */
52
+ export declare function createSafeHandler<T extends (...args: any[]) => Promise<any> | any>(hookName: string, hookType: string, componentId: string, handler: T, options?: SafeHookRunnerOptions): T;
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Safe Hook Runner
3
+ *
4
+ * Utility for safely executing hooks with error boundaries.
5
+ * Prevents one failing hook from crashing the entire component lifecycle.
6
+ */
7
+ const defaultOptions = {
8
+ continueOnError: true,
9
+ logErrors: true
10
+ };
11
+ /**
12
+ * Safely execute a single hook handler with error boundary
13
+ */
14
+ export async function safeExecuteHook(hookName, hookType, componentId, handler, options = {}) {
15
+ const opts = { ...defaultOptions, ...options };
16
+ try {
17
+ const result = await handler();
18
+ return { success: true, result };
19
+ }
20
+ catch (err) {
21
+ const error = err instanceof Error ? err : new Error(String(err));
22
+ const hookError = {
23
+ hookName,
24
+ hookType,
25
+ error,
26
+ componentId,
27
+ timestamp: Date.now()
28
+ };
29
+ if (opts.logErrors) {
30
+ console.warn(`[SafeHookRunner] Hook "${hookName}" failed during "${hookType}" for component "${componentId}":`, error.message);
31
+ }
32
+ if (opts.eventManager) {
33
+ opts.eventManager.emit('hookerror', hookError);
34
+ }
35
+ return { success: false, error: hookError };
36
+ }
37
+ }
38
+ /**
39
+ * Execute multiple hooks in sequence with error boundaries
40
+ */
41
+ export async function safeExecuteHooks(hooks, hookType, componentId, options = {}) {
42
+ const opts = { ...defaultOptions, ...options };
43
+ const errors = [];
44
+ for (const hook of hooks) {
45
+ const result = await safeExecuteHook(hook.name, hookType, componentId, hook.handler, options);
46
+ if (!result.success && result.error) {
47
+ errors.push(result.error);
48
+ if (!opts.continueOnError) {
49
+ break;
50
+ }
51
+ }
52
+ }
53
+ return {
54
+ allSucceeded: errors.length === 0,
55
+ errors
56
+ };
57
+ }
58
+ /**
59
+ * Create a wrapped version of a hook handler that catches errors
60
+ */
61
+ export function createSafeHandler(hookName, hookType, componentId, handler, options = {}) {
62
+ const wrapped = async (...args) => {
63
+ const result = await safeExecuteHook(hookName, hookType, componentId, () => handler(...args), options);
64
+ return result.result;
65
+ };
66
+ return wrapped;
67
+ }
@@ -67,7 +67,30 @@ export class HtmlToCanvasHook {
67
67
  }
68
68
  const { width, height } = this.state;
69
69
  if (!this.svgBase) {
70
- const { base, content, end } = await svgGenerator.generateSVG(this.#htmlEl, this.#context.data.appearance.text, width, height, 'svg-' + this.#context.contextData.id, encodeURIComponent(this.state.getCharactersList().join('')));
70
+ // Safely encode characters list, filtering out any problematic characters
71
+ let encodedChars = '';
72
+ try {
73
+ const charsList = this.state.getCharactersList().join('');
74
+ encodedChars = encodeURIComponent(charsList);
75
+ }
76
+ catch (error) {
77
+ // If encoding fails (e.g., unpaired surrogates), filter to only safe characters
78
+ console.warn('Failed to encode characters list, filtering to safe characters:', error);
79
+ const safeChars = this.state
80
+ .getCharactersList()
81
+ .filter((char) => {
82
+ try {
83
+ encodeURIComponent(char);
84
+ return true;
85
+ }
86
+ catch {
87
+ return false;
88
+ }
89
+ })
90
+ .join('');
91
+ encodedChars = encodeURIComponent(safeChars);
92
+ }
93
+ const { base, content, end } = await svgGenerator.generateSVG(this.#htmlEl, this.#context.data.appearance.text, width, height, 'svg-' + this.#context.contextData.id, encodedChars);
71
94
  this.svgBase = base;
72
95
  this.svgEnd = end;
73
96
  this.svg = base + content + end;
@@ -2,7 +2,7 @@ import { MediaManager } from '../../managers/MediaManager.js';
2
2
  import { z } from 'zod';
3
3
  import { StateManager } from '../../managers/StateManager.svelte.js';
4
4
  export class MediaHook {
5
- types = ['setup', 'update', 'destroy', 'refresh'];
5
+ types = ['setup', 'update', 'destroy'];
6
6
  priority = 1;
7
7
  #context;
8
8
  #mediaElement;