shaders 2.3.69 → 2.3.71

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 (80) hide show
  1. package/dist/svelte/index.js +76 -76
  2. package/dist/svelte/source/components/AngularBlur.svelte +287 -0
  3. package/dist/svelte/source/components/Ascii.svelte +288 -0
  4. package/dist/svelte/source/components/Aurora.svelte +292 -0
  5. package/dist/svelte/source/components/Beam.svelte +290 -0
  6. package/dist/svelte/source/components/Blob.svelte +289 -0
  7. package/dist/svelte/source/components/Blur.svelte +287 -0
  8. package/dist/svelte/source/components/BrightnessContrast.svelte +288 -0
  9. package/dist/svelte/source/components/Bulge.svelte +289 -0
  10. package/dist/svelte/source/components/CRTScreen.svelte +294 -0
  11. package/dist/svelte/source/components/ChannelBlur.svelte +289 -0
  12. package/dist/svelte/source/components/Checkerboard.svelte +288 -0
  13. package/dist/svelte/source/components/ChromaFlow.svelte +286 -0
  14. package/dist/svelte/source/components/ChromaticAberration.svelte +291 -0
  15. package/dist/svelte/source/components/Circle.svelte +289 -0
  16. package/dist/svelte/source/components/ConcentricSpin.svelte +290 -0
  17. package/dist/svelte/source/components/ContourLines.svelte +289 -0
  18. package/dist/svelte/source/components/CursorRipples.svelte +288 -0
  19. package/dist/svelte/source/components/CursorTrail.svelte +286 -0
  20. package/dist/svelte/source/components/DiffuseBlur.svelte +287 -0
  21. package/dist/svelte/source/components/Dither.svelte +288 -0
  22. package/dist/svelte/source/components/DotGrid.svelte +288 -0
  23. package/dist/svelte/source/components/Duotone.svelte +287 -0
  24. package/dist/svelte/source/components/FilmGrain.svelte +287 -0
  25. package/dist/svelte/source/components/FloatingParticles.svelte +290 -0
  26. package/dist/svelte/source/components/FlowField.svelte +288 -0
  27. package/dist/svelte/source/components/GlassTiles.svelte +290 -0
  28. package/dist/svelte/source/components/Glitch.svelte +292 -0
  29. package/dist/svelte/source/components/Glow.svelte +289 -0
  30. package/dist/svelte/source/components/Godrays.svelte +289 -0
  31. package/dist/svelte/source/components/Grayscale.svelte +286 -0
  32. package/dist/svelte/source/components/Grid.svelte +288 -0
  33. package/dist/svelte/source/components/GridDistortion.svelte +287 -0
  34. package/dist/svelte/source/components/Group.svelte +286 -0
  35. package/dist/svelte/source/components/Halftone.svelte +289 -0
  36. package/dist/svelte/source/components/HueShift.svelte +287 -0
  37. package/dist/svelte/source/components/ImageTexture.svelte +286 -0
  38. package/dist/svelte/source/components/Invert.svelte +286 -0
  39. package/dist/svelte/source/components/Kaleidoscope.svelte +288 -0
  40. package/dist/svelte/source/components/LensFlare.svelte +299 -0
  41. package/dist/svelte/source/components/LinearBlur.svelte +288 -0
  42. package/dist/svelte/source/components/LinearGradient.svelte +287 -0
  43. package/dist/svelte/source/components/Liquify.svelte +286 -0
  44. package/dist/svelte/source/components/Mirror.svelte +287 -0
  45. package/dist/svelte/source/components/Perspective.svelte +290 -0
  46. package/dist/svelte/source/components/Pixelate.svelte +287 -0
  47. package/dist/svelte/source/components/Plasma.svelte +290 -0
  48. package/dist/svelte/source/components/PolarCoordinates.svelte +289 -0
  49. package/dist/svelte/source/components/Posterize.svelte +287 -0
  50. package/dist/svelte/source/components/ProgressiveBlur.svelte +289 -0
  51. package/dist/svelte/source/components/RadialGradient.svelte +287 -0
  52. package/dist/svelte/source/components/RectangularCoordinates.svelte +288 -0
  53. package/dist/svelte/source/components/Ripples.svelte +290 -0
  54. package/dist/svelte/source/components/Saturation.svelte +287 -0
  55. package/dist/svelte/source/components/Sharpness.svelte +287 -0
  56. package/dist/svelte/source/components/Shatter.svelte +289 -0
  57. package/dist/svelte/source/components/SimplexNoise.svelte +288 -0
  58. package/dist/svelte/source/components/SineWave.svelte +291 -0
  59. package/dist/svelte/source/components/SolidColor.svelte +286 -0
  60. package/dist/svelte/source/components/Spherize.svelte +290 -0
  61. package/dist/svelte/source/components/Spiral.svelte +290 -0
  62. package/dist/svelte/source/components/Strands.svelte +289 -0
  63. package/dist/svelte/source/components/Stretch.svelte +289 -0
  64. package/dist/svelte/source/components/Stripes.svelte +291 -0
  65. package/dist/svelte/source/components/StudioBackground.svelte +298 -0
  66. package/dist/svelte/source/components/Swirl.svelte +288 -0
  67. package/dist/svelte/source/components/TiltShift.svelte +290 -0
  68. package/dist/svelte/source/components/Tint.svelte +287 -0
  69. package/dist/svelte/source/components/Tritone.svelte +287 -0
  70. package/dist/svelte/source/components/Twirl.svelte +287 -0
  71. package/dist/svelte/source/components/Vibrance.svelte +287 -0
  72. package/dist/svelte/source/components/VideoTexture.svelte +286 -0
  73. package/dist/svelte/source/components/WaveDistortion.svelte +289 -0
  74. package/dist/svelte/source/components/WebcamTexture.svelte +286 -0
  75. package/dist/svelte/source/components/ZoomBlur.svelte +287 -0
  76. package/dist/svelte/source/engine/Preview.svelte +320 -0
  77. package/dist/svelte/source/engine/PreviewRenderComponent.svelte +27 -0
  78. package/dist/svelte/source/engine/Shader.svelte +263 -0
  79. package/dist/svelte/source/index.js +77 -0
  80. package/package.json +2 -1
@@ -0,0 +1,291 @@
1
+ <script lang="ts">
2
+ import { getContext, setContext, onMount, onDestroy } from 'svelte';
3
+ import {
4
+ createUniformsMap,
5
+ type UniformsMap,
6
+ type BlendMode,
7
+ type NodeMetadata,
8
+ type PropConfig,
9
+ type MaskConfig,
10
+ type MapConfig,
11
+ type PropDriver,
12
+ type TransformConfig
13
+ } from '../../../core/index.js';
14
+ import {setColorSpaceMode} from '../../../core/utilities/transformations/index.js';
15
+
16
+ // @ts-ignore - this import is replaced at build time
17
+ import { componentDefinition, type ComponentProps } from '../../../core/shaders/Stripes/index.js';
18
+
19
+ /**
20
+ * Define component props including blend mode, opacity, visibility, masking, and transformation
21
+ */
22
+ interface ExtendedComponentProps extends Partial<ComponentProps> {
23
+ angle?: ComponentProps['angle'] | PropDriver;
24
+ density?: ComponentProps['density'] | PropDriver;
25
+ balance?: ComponentProps['balance'] | PropDriver;
26
+ softness?: ComponentProps['softness'] | PropDriver;
27
+ offset?: ComponentProps['offset'] | PropDriver;
28
+ blendMode?: BlendMode;
29
+ opacity?: number;
30
+ visible?: boolean;
31
+ id?: string;
32
+ maskSource?: string;
33
+ maskType?: string;
34
+ renderOrder?: number;
35
+ transform?: Partial<TransformConfig>;
36
+ children?: import('svelte').Snippet;
37
+ }
38
+
39
+ function isPropDriver(value: unknown): value is PropDriver {
40
+ return typeof value === 'object' && value !== null && 'type' in value && (value as any).type === 'map';
41
+ }
42
+
43
+ /**
44
+ * Default transform configuration (optimized for zero overhead)
45
+ */
46
+ const DEFAULT_TRANSFORM: TransformConfig = {
47
+ offsetX: 0,
48
+ offsetY: 0,
49
+ rotation: 0,
50
+ scale: 1,
51
+ anchorX: 0.5,
52
+ anchorY: 0.5,
53
+ edges: 'transparent'
54
+ };
55
+
56
+ // Define the component props and their default values from the shader definition
57
+ const componentDefaults = {
58
+ blendMode: 'normal' as BlendMode,
59
+ visible: true,
60
+ // opacity intentionally has no default - handled by renderer
61
+ // transform intentionally has no default - handled by effectiveTransform
62
+ ...Object.entries(componentDefinition.props).reduce(
63
+ (acc, [key, config]) => {
64
+ acc[key] = (config as unknown as PropConfig<typeof config>).default;
65
+ return acc;
66
+ },
67
+ {} as Record<string, any>
68
+ )
69
+ };
70
+
71
+ // Declare props using Svelte 5's syntax
72
+ const props: ExtendedComponentProps = $props();
73
+
74
+ // Apply defaults manually since Svelte 5 doesn't have withDefaults equivalent
75
+ // Use $derived so the metadata $effect re-runs when any of these change at runtime
76
+ const blendMode = $derived(props.blendMode ?? componentDefaults.blendMode);
77
+ const opacity = $derived(props.opacity); // No default - handled by renderer
78
+ const visible = $derived(props.visible ?? componentDefaults.visible); // Default to true
79
+ const id = $derived(props.id);
80
+ const maskSource = $derived(props.maskSource);
81
+ const maskType = $derived(props.maskType);
82
+ const renderOrder = $derived(props.renderOrder);
83
+ const { children } = props;
84
+
85
+ // Collect PropDriver values from shader props into the maps metadata structure
86
+ const mapsFromProps = $derived.by(() => {
87
+ const maps: Record<string, MapConfig> = {};
88
+ for (const key of Object.keys(componentDefinition.props)) {
89
+ const val = (props as any)[key];
90
+ if (isPropDriver(val)) maps[key] = val as MapConfig;
91
+ }
92
+ return Object.keys(maps).length > 0 ? maps : undefined;
93
+ });
94
+
95
+ /**
96
+ * Computed transform that merges user-provided values with defaults
97
+ */
98
+ const effectiveTransform = $derived({
99
+ ...DEFAULT_TRANSFORM,
100
+ ...props.transform
101
+ });
102
+
103
+ /**
104
+ * FIRST: Get the parent ID from context BEFORE setting our own context
105
+ */
106
+ const parentId = getContext<string>('shaderParentId');
107
+ if (parentId === undefined) {
108
+ throw new Error('Shader components must be used inside an <Shader> component or another shader component');
109
+ }
110
+
111
+ /**
112
+ * Use the provided ID or generate a unique identifier for this component instance
113
+ */
114
+ const instanceId = (id ? id.replace(/[^a-zA-Z0-9_]/g, '_') : null) || Math.random().toString(36).substring(2, 10);
115
+
116
+ /**
117
+ * THEN: Provide our unique identifier to child components
118
+ */
119
+ setContext('shaderParentId', instanceId);
120
+
121
+ /**
122
+ * Creates a non-reactive object containing only props that differ from defaults
123
+ * This optimization prevents unnecessary GPU uniform updates for unchanged values
124
+ * Special props like blendMode and opacity are handled separately
125
+ */
126
+ const shaderReadyProps = $derived.by(() => {
127
+ let baseProps = { ...componentDefaults };
128
+
129
+ // Only include props that differ from defaults (excluding special props and PropDrivers)
130
+ for (const key in props) {
131
+ if (key !== 'blendMode' && key !== 'opacity' && key !== 'visible' &&
132
+ key !== 'id' && key !== 'maskSource' && key !== 'maskType' && key !== 'renderOrder' &&
133
+ key !== 'transform' && key !== 'children') {
134
+ const propValue = (props as any)[key];
135
+ if (isPropDriver(propValue)) continue; // PropDrivers go to metadata.maps, not uniforms
136
+ const defaultValue = (componentDefaults as any)[key];
137
+ if (propValue !== undefined && propValue !== defaultValue) {
138
+ (baseProps as any)[key] = propValue;
139
+ }
140
+ }
141
+ }
142
+ return baseProps;
143
+ });
144
+
145
+ /**
146
+ * Get the color space from the root Shader component.
147
+ * Used to set the global color space mode before creating uniforms.
148
+ */
149
+ const shaderColorSpace = getContext<() => 'p3-linear' | 'srgb'>('shaderColorSpace');
150
+
151
+ /**
152
+ * Creates the GPU uniform values map using only the changed props
153
+ * Set the global color space mode before creating uniforms so colors are transformed correctly
154
+ * Note: Intentionally captures initial value - props are immutable after initialization
155
+ */
156
+ if (shaderColorSpace) {
157
+ setColorSpaceMode(shaderColorSpace());
158
+ }
159
+ // svelte-ignore state_referenced_locally
160
+ const uniforms: UniformsMap = createUniformsMap(componentDefinition, shaderReadyProps, instanceId);
161
+
162
+ /**
163
+ * Get the node registration function from parent context
164
+ */
165
+ const parentRegister = getContext<(id: string, fragmentNodeFunc: any, parentId: string | null, metadata: NodeMetadata | null, uniforms: UniformsMap | null, componentDefinition: any) => void>('shaderNodeRegister');
166
+ if (parentRegister === undefined) {
167
+ throw new Error('Shader components must be used inside an <Shader> component or another shader component');
168
+ }
169
+
170
+ /**
171
+ * Get the uniform update function from parent context
172
+ */
173
+ const parentUniformUpdate = getContext<(nodeId: string, uniformName: string, value: any) => void>('shaderUniformUpdate');
174
+ if (parentUniformUpdate === undefined) {
175
+ throw new Error('Shader components require shaderUniformUpdate from parent');
176
+ }
177
+
178
+ /**
179
+ * Get the metadata update function from parent context
180
+ */
181
+ const parentMetadataUpdate = getContext<(nodeId: string, metadata: NodeMetadata) => void>('shaderMetadataUpdate');
182
+ if (parentMetadataUpdate === undefined) {
183
+ throw new Error('Shader components require shaderMetadataUpdate from parent');
184
+ }
185
+
186
+ // DOM marker ref for determining render order from template position
187
+ let orderMarker: HTMLSpanElement;
188
+
189
+ // Stores the DOM-detected render order
190
+ let detectedRenderOrder: number | undefined = undefined;
191
+
192
+ // Flag to track when component is registered
193
+ let isRegistered = $state(false);
194
+
195
+ // Setup uniform watchers with registration guard
196
+ Object.entries(uniforms).forEach(([propName, { uniform, transform }]) => {
197
+ $effect(() => {
198
+ // Only run after component is registered
199
+ if (!isRegistered) return;
200
+
201
+ if (uniform && uniform.value !== undefined) {
202
+ const newValue = (props as any)[propName];
203
+ if (newValue !== undefined && !isPropDriver(newValue)) {
204
+ // Send raw value - renderer will handle transformation
205
+ // PropDriver values go to metadata.maps, not uniforms
206
+ parentUniformUpdate(instanceId, propName, newValue);
207
+ }
208
+ }
209
+ });
210
+ });
211
+
212
+ // Watch blend mode, opacity, visibility, masking, transformations, and prop maps changes
213
+ $effect(() => {
214
+ // Only run after component is registered
215
+ if (!isRegistered) return;
216
+
217
+ const metadata: NodeMetadata = {
218
+ blendMode,
219
+ opacity,
220
+ visible: visible === false ? false : true,
221
+ id,
222
+ mask: maskSource ? {
223
+ source: maskSource,
224
+ type: (maskType || 'alpha') as MaskConfig['type']
225
+ } : undefined,
226
+ maps: mapsFromProps,
227
+ renderOrder: renderOrder ?? detectedRenderOrder,
228
+ transform: effectiveTransform
229
+ };
230
+ parentMetadataUpdate(instanceId, metadata);
231
+ });
232
+
233
+ // Register this component after mount to ensure parent is ready
234
+ onMount(() => {
235
+ // Register this component with safety check
236
+ if (componentDefinition && typeof componentDefinition.fragmentNode === 'function') {
237
+ parentRegister(
238
+ instanceId,
239
+ componentDefinition.fragmentNode,
240
+ parentId,
241
+ {
242
+ blendMode,
243
+ opacity,
244
+ visible: visible !== false ? true : false,
245
+ id,
246
+ mask: maskSource ? {
247
+ source: maskSource,
248
+ type: (maskType || 'alpha') as MaskConfig['type']
249
+ } as MaskConfig : undefined,
250
+ maps: mapsFromProps,
251
+ renderOrder: renderOrder ?? detectedRenderOrder,
252
+ transform: effectiveTransform
253
+ },
254
+ uniforms,
255
+ componentDefinition
256
+ );
257
+
258
+ // Set flag to enable effects after successful registration
259
+ isRegistered = true;
260
+
261
+ // Detect DOM position for correct render ordering
262
+ if (renderOrder === undefined && orderMarker) {
263
+ const parent = orderMarker.parentElement;
264
+ if (parent) {
265
+ const siblings = parent.querySelectorAll(':scope > [data-shader-id]');
266
+ const position = Array.from(siblings).indexOf(orderMarker);
267
+ if (position >= 0) {
268
+ detectedRenderOrder = position;
269
+ parentMetadataUpdate(instanceId, { renderOrder: position } as NodeMetadata);
270
+ }
271
+ }
272
+ }
273
+ } else {
274
+ console.error('componentDefinition.fragmentNode is not a function:', {
275
+ componentDefinition,
276
+ fragmentNode: componentDefinition?.fragmentNode,
277
+ type: typeof componentDefinition?.fragmentNode
278
+ });
279
+ }
280
+ });
281
+
282
+ // Clean up node from registry when component is unmounted
283
+ onDestroy(() => {
284
+ isRegistered = false;
285
+ parentRegister(instanceId, null, null, null, null);
286
+ });
287
+ </script>
288
+
289
+ <span bind:this={orderMarker} style="display:contents" data-shader-id={instanceId}>
290
+ {@render children?.()}
291
+ </span>
@@ -0,0 +1,298 @@
1
+ <script lang="ts">
2
+ import { getContext, setContext, onMount, onDestroy } from 'svelte';
3
+ import {
4
+ createUniformsMap,
5
+ type UniformsMap,
6
+ type BlendMode,
7
+ type NodeMetadata,
8
+ type PropConfig,
9
+ type MaskConfig,
10
+ type MapConfig,
11
+ type PropDriver,
12
+ type TransformConfig
13
+ } from '../../../core/index.js';
14
+ import {setColorSpaceMode} from '../../../core/utilities/transformations/index.js';
15
+
16
+ // @ts-ignore - this import is replaced at build time
17
+ import { componentDefinition, type ComponentProps } from '../../../core/shaders/StudioBackground/index.js';
18
+
19
+ /**
20
+ * Define component props including blend mode, opacity, visibility, masking, and transformation
21
+ */
22
+ interface ExtendedComponentProps extends Partial<ComponentProps> {
23
+ keyIntensity?: ComponentProps['keyIntensity'] | PropDriver;
24
+ keySoftness?: ComponentProps['keySoftness'] | PropDriver;
25
+ fillIntensity?: ComponentProps['fillIntensity'] | PropDriver;
26
+ fillSoftness?: ComponentProps['fillSoftness'] | PropDriver;
27
+ fillAngle?: ComponentProps['fillAngle'] | PropDriver;
28
+ backIntensity?: ComponentProps['backIntensity'] | PropDriver;
29
+ backSoftness?: ComponentProps['backSoftness'] | PropDriver;
30
+ brightness?: ComponentProps['brightness'] | PropDriver;
31
+ vignette?: ComponentProps['vignette'] | PropDriver;
32
+ lightTarget?: ComponentProps['lightTarget'] | PropDriver;
33
+ wallCurvature?: ComponentProps['wallCurvature'] | PropDriver;
34
+ ambientIntensity?: ComponentProps['ambientIntensity'] | PropDriver;
35
+ blendMode?: BlendMode;
36
+ opacity?: number;
37
+ visible?: boolean;
38
+ id?: string;
39
+ maskSource?: string;
40
+ maskType?: string;
41
+ renderOrder?: number;
42
+ transform?: Partial<TransformConfig>;
43
+ children?: import('svelte').Snippet;
44
+ }
45
+
46
+ function isPropDriver(value: unknown): value is PropDriver {
47
+ return typeof value === 'object' && value !== null && 'type' in value && (value as any).type === 'map';
48
+ }
49
+
50
+ /**
51
+ * Default transform configuration (optimized for zero overhead)
52
+ */
53
+ const DEFAULT_TRANSFORM: TransformConfig = {
54
+ offsetX: 0,
55
+ offsetY: 0,
56
+ rotation: 0,
57
+ scale: 1,
58
+ anchorX: 0.5,
59
+ anchorY: 0.5,
60
+ edges: 'transparent'
61
+ };
62
+
63
+ // Define the component props and their default values from the shader definition
64
+ const componentDefaults = {
65
+ blendMode: 'normal' as BlendMode,
66
+ visible: true,
67
+ // opacity intentionally has no default - handled by renderer
68
+ // transform intentionally has no default - handled by effectiveTransform
69
+ ...Object.entries(componentDefinition.props).reduce(
70
+ (acc, [key, config]) => {
71
+ acc[key] = (config as unknown as PropConfig<typeof config>).default;
72
+ return acc;
73
+ },
74
+ {} as Record<string, any>
75
+ )
76
+ };
77
+
78
+ // Declare props using Svelte 5's syntax
79
+ const props: ExtendedComponentProps = $props();
80
+
81
+ // Apply defaults manually since Svelte 5 doesn't have withDefaults equivalent
82
+ // Use $derived so the metadata $effect re-runs when any of these change at runtime
83
+ const blendMode = $derived(props.blendMode ?? componentDefaults.blendMode);
84
+ const opacity = $derived(props.opacity); // No default - handled by renderer
85
+ const visible = $derived(props.visible ?? componentDefaults.visible); // Default to true
86
+ const id = $derived(props.id);
87
+ const maskSource = $derived(props.maskSource);
88
+ const maskType = $derived(props.maskType);
89
+ const renderOrder = $derived(props.renderOrder);
90
+ const { children } = props;
91
+
92
+ // Collect PropDriver values from shader props into the maps metadata structure
93
+ const mapsFromProps = $derived.by(() => {
94
+ const maps: Record<string, MapConfig> = {};
95
+ for (const key of Object.keys(componentDefinition.props)) {
96
+ const val = (props as any)[key];
97
+ if (isPropDriver(val)) maps[key] = val as MapConfig;
98
+ }
99
+ return Object.keys(maps).length > 0 ? maps : undefined;
100
+ });
101
+
102
+ /**
103
+ * Computed transform that merges user-provided values with defaults
104
+ */
105
+ const effectiveTransform = $derived({
106
+ ...DEFAULT_TRANSFORM,
107
+ ...props.transform
108
+ });
109
+
110
+ /**
111
+ * FIRST: Get the parent ID from context BEFORE setting our own context
112
+ */
113
+ const parentId = getContext<string>('shaderParentId');
114
+ if (parentId === undefined) {
115
+ throw new Error('Shader components must be used inside an <Shader> component or another shader component');
116
+ }
117
+
118
+ /**
119
+ * Use the provided ID or generate a unique identifier for this component instance
120
+ */
121
+ const instanceId = (id ? id.replace(/[^a-zA-Z0-9_]/g, '_') : null) || Math.random().toString(36).substring(2, 10);
122
+
123
+ /**
124
+ * THEN: Provide our unique identifier to child components
125
+ */
126
+ setContext('shaderParentId', instanceId);
127
+
128
+ /**
129
+ * Creates a non-reactive object containing only props that differ from defaults
130
+ * This optimization prevents unnecessary GPU uniform updates for unchanged values
131
+ * Special props like blendMode and opacity are handled separately
132
+ */
133
+ const shaderReadyProps = $derived.by(() => {
134
+ let baseProps = { ...componentDefaults };
135
+
136
+ // Only include props that differ from defaults (excluding special props and PropDrivers)
137
+ for (const key in props) {
138
+ if (key !== 'blendMode' && key !== 'opacity' && key !== 'visible' &&
139
+ key !== 'id' && key !== 'maskSource' && key !== 'maskType' && key !== 'renderOrder' &&
140
+ key !== 'transform' && key !== 'children') {
141
+ const propValue = (props as any)[key];
142
+ if (isPropDriver(propValue)) continue; // PropDrivers go to metadata.maps, not uniforms
143
+ const defaultValue = (componentDefaults as any)[key];
144
+ if (propValue !== undefined && propValue !== defaultValue) {
145
+ (baseProps as any)[key] = propValue;
146
+ }
147
+ }
148
+ }
149
+ return baseProps;
150
+ });
151
+
152
+ /**
153
+ * Get the color space from the root Shader component.
154
+ * Used to set the global color space mode before creating uniforms.
155
+ */
156
+ const shaderColorSpace = getContext<() => 'p3-linear' | 'srgb'>('shaderColorSpace');
157
+
158
+ /**
159
+ * Creates the GPU uniform values map using only the changed props
160
+ * Set the global color space mode before creating uniforms so colors are transformed correctly
161
+ * Note: Intentionally captures initial value - props are immutable after initialization
162
+ */
163
+ if (shaderColorSpace) {
164
+ setColorSpaceMode(shaderColorSpace());
165
+ }
166
+ // svelte-ignore state_referenced_locally
167
+ const uniforms: UniformsMap = createUniformsMap(componentDefinition, shaderReadyProps, instanceId);
168
+
169
+ /**
170
+ * Get the node registration function from parent context
171
+ */
172
+ const parentRegister = getContext<(id: string, fragmentNodeFunc: any, parentId: string | null, metadata: NodeMetadata | null, uniforms: UniformsMap | null, componentDefinition: any) => void>('shaderNodeRegister');
173
+ if (parentRegister === undefined) {
174
+ throw new Error('Shader components must be used inside an <Shader> component or another shader component');
175
+ }
176
+
177
+ /**
178
+ * Get the uniform update function from parent context
179
+ */
180
+ const parentUniformUpdate = getContext<(nodeId: string, uniformName: string, value: any) => void>('shaderUniformUpdate');
181
+ if (parentUniformUpdate === undefined) {
182
+ throw new Error('Shader components require shaderUniformUpdate from parent');
183
+ }
184
+
185
+ /**
186
+ * Get the metadata update function from parent context
187
+ */
188
+ const parentMetadataUpdate = getContext<(nodeId: string, metadata: NodeMetadata) => void>('shaderMetadataUpdate');
189
+ if (parentMetadataUpdate === undefined) {
190
+ throw new Error('Shader components require shaderMetadataUpdate from parent');
191
+ }
192
+
193
+ // DOM marker ref for determining render order from template position
194
+ let orderMarker: HTMLSpanElement;
195
+
196
+ // Stores the DOM-detected render order
197
+ let detectedRenderOrder: number | undefined = undefined;
198
+
199
+ // Flag to track when component is registered
200
+ let isRegistered = $state(false);
201
+
202
+ // Setup uniform watchers with registration guard
203
+ Object.entries(uniforms).forEach(([propName, { uniform, transform }]) => {
204
+ $effect(() => {
205
+ // Only run after component is registered
206
+ if (!isRegistered) return;
207
+
208
+ if (uniform && uniform.value !== undefined) {
209
+ const newValue = (props as any)[propName];
210
+ if (newValue !== undefined && !isPropDriver(newValue)) {
211
+ // Send raw value - renderer will handle transformation
212
+ // PropDriver values go to metadata.maps, not uniforms
213
+ parentUniformUpdate(instanceId, propName, newValue);
214
+ }
215
+ }
216
+ });
217
+ });
218
+
219
+ // Watch blend mode, opacity, visibility, masking, transformations, and prop maps changes
220
+ $effect(() => {
221
+ // Only run after component is registered
222
+ if (!isRegistered) return;
223
+
224
+ const metadata: NodeMetadata = {
225
+ blendMode,
226
+ opacity,
227
+ visible: visible === false ? false : true,
228
+ id,
229
+ mask: maskSource ? {
230
+ source: maskSource,
231
+ type: (maskType || 'alpha') as MaskConfig['type']
232
+ } : undefined,
233
+ maps: mapsFromProps,
234
+ renderOrder: renderOrder ?? detectedRenderOrder,
235
+ transform: effectiveTransform
236
+ };
237
+ parentMetadataUpdate(instanceId, metadata);
238
+ });
239
+
240
+ // Register this component after mount to ensure parent is ready
241
+ onMount(() => {
242
+ // Register this component with safety check
243
+ if (componentDefinition && typeof componentDefinition.fragmentNode === 'function') {
244
+ parentRegister(
245
+ instanceId,
246
+ componentDefinition.fragmentNode,
247
+ parentId,
248
+ {
249
+ blendMode,
250
+ opacity,
251
+ visible: visible !== false ? true : false,
252
+ id,
253
+ mask: maskSource ? {
254
+ source: maskSource,
255
+ type: (maskType || 'alpha') as MaskConfig['type']
256
+ } as MaskConfig : undefined,
257
+ maps: mapsFromProps,
258
+ renderOrder: renderOrder ?? detectedRenderOrder,
259
+ transform: effectiveTransform
260
+ },
261
+ uniforms,
262
+ componentDefinition
263
+ );
264
+
265
+ // Set flag to enable effects after successful registration
266
+ isRegistered = true;
267
+
268
+ // Detect DOM position for correct render ordering
269
+ if (renderOrder === undefined && orderMarker) {
270
+ const parent = orderMarker.parentElement;
271
+ if (parent) {
272
+ const siblings = parent.querySelectorAll(':scope > [data-shader-id]');
273
+ const position = Array.from(siblings).indexOf(orderMarker);
274
+ if (position >= 0) {
275
+ detectedRenderOrder = position;
276
+ parentMetadataUpdate(instanceId, { renderOrder: position } as NodeMetadata);
277
+ }
278
+ }
279
+ }
280
+ } else {
281
+ console.error('componentDefinition.fragmentNode is not a function:', {
282
+ componentDefinition,
283
+ fragmentNode: componentDefinition?.fragmentNode,
284
+ type: typeof componentDefinition?.fragmentNode
285
+ });
286
+ }
287
+ });
288
+
289
+ // Clean up node from registry when component is unmounted
290
+ onDestroy(() => {
291
+ isRegistered = false;
292
+ parentRegister(instanceId, null, null, null, null);
293
+ });
294
+ </script>
295
+
296
+ <span bind:this={orderMarker} style="display:contents" data-shader-id={instanceId}>
297
+ {@render children?.()}
298
+ </span>