@shohojdhara/atomix 0.2.4 → 0.2.6

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 (214) hide show
  1. package/README.md +19 -0
  2. package/dist/atomix.css +1300 -1418
  3. package/dist/atomix.min.css +3 -3
  4. package/dist/index.d.ts +1259 -874
  5. package/dist/index.esm.js +16256 -26366
  6. package/dist/index.esm.js.map +1 -1
  7. package/dist/index.js +15691 -22295
  8. package/dist/index.js.map +1 -1
  9. package/dist/index.min.js +1 -1
  10. package/dist/index.min.js.map +1 -1
  11. package/dist/themes/applemix.css +15036 -0
  12. package/dist/themes/applemix.min.css +72 -0
  13. package/dist/themes/boomdevs.css +1300 -1419
  14. package/dist/themes/boomdevs.min.css +3 -3
  15. package/dist/themes/esrar.css +1301 -1419
  16. package/dist/themes/esrar.min.css +3 -3
  17. package/dist/themes/flashtrade.css +15187 -0
  18. package/dist/themes/flashtrade.min.css +86 -0
  19. package/dist/themes/mashroom.css +1299 -1417
  20. package/dist/themes/mashroom.min.css +5 -5
  21. package/dist/themes/shaj-default.css +1300 -1418
  22. package/dist/themes/shaj-default.min.css +3 -3
  23. package/package.json +6 -17
  24. package/src/components/Accordion/Accordion.stories.tsx +4 -26
  25. package/src/components/Accordion/Accordion.tsx +21 -12
  26. package/src/components/AtomixGlass/AtomixGlass.test.tsx +106 -72
  27. package/src/components/AtomixGlass/AtomixGlass.tsx +485 -1215
  28. package/src/components/AtomixGlass/AtomixGlassContainer.tsx +399 -0
  29. package/src/components/AtomixGlass/GlassFilter.tsx +156 -0
  30. package/src/components/AtomixGlass/README.md +124 -2
  31. package/src/components/AtomixGlass/atomixGLass.old.tsx +1266 -0
  32. package/src/components/AtomixGlass/glass-utils.ts +263 -0
  33. package/src/components/AtomixGlass/shader-utils.ts +404 -236
  34. package/src/components/AtomixGlass/{AtomixGlass.stories.tsx → stories/AtomixGlass.stories.tsx} +55 -35
  35. package/src/components/AtomixGlass/stories/Examples.stories.tsx +57 -89
  36. package/src/components/AtomixGlass/stories/Modes.stories.tsx +149 -149
  37. package/src/components/AtomixGlass/stories/Playground.stories.tsx +95 -32
  38. package/src/components/AtomixGlass/stories/ShaderVariants.stories.tsx +0 -2
  39. package/src/components/AtomixGlass/stories/shared-components.tsx +9 -18
  40. package/src/components/AtomixGlass/utils.ts +3 -3
  41. package/src/components/Avatar/Avatar.tsx +3 -0
  42. package/src/components/Avatar/AvatarGroup.tsx +2 -1
  43. package/src/components/Badge/Badge.stories.tsx +74 -54
  44. package/src/components/Badge/Badge.tsx +8 -12
  45. package/src/components/Breadcrumb/Breadcrumb.tsx +23 -4
  46. package/src/components/Button/Button.tsx +3 -5
  47. package/src/components/Callout/Callout.stories.tsx +86 -35
  48. package/src/components/Callout/Callout.tsx +4 -0
  49. package/src/components/Card/Card.stories.tsx +89 -85
  50. package/src/components/Card/Card.tsx +15 -4
  51. package/src/components/Card/ElevationCard.tsx +2 -0
  52. package/src/components/Chart/AnimatedChart.tsx +179 -156
  53. package/src/components/Chart/AreaChart.tsx +123 -12
  54. package/src/components/Chart/BarChart.tsx +91 -100
  55. package/src/components/Chart/BaseChart.tsx +80 -0
  56. package/src/components/Chart/BubbleChart.tsx +114 -290
  57. package/src/components/Chart/CandlestickChart.tsx +282 -622
  58. package/src/components/Chart/Chart.stories.tsx +576 -179
  59. package/src/components/Chart/Chart.tsx +374 -75
  60. package/src/components/Chart/ChartRenderer.tsx +371 -220
  61. package/src/components/Chart/ChartToolbar.tsx +372 -61
  62. package/src/components/Chart/ChartTooltip.tsx +33 -18
  63. package/src/components/Chart/DonutChart.tsx +172 -254
  64. package/src/components/Chart/FunnelChart.tsx +169 -240
  65. package/src/components/Chart/GaugeChart.tsx +224 -392
  66. package/src/components/Chart/HeatmapChart.tsx +302 -440
  67. package/src/components/Chart/LineChart.tsx +148 -103
  68. package/src/components/Chart/MultiAxisChart.tsx +267 -395
  69. package/src/components/Chart/PieChart.tsx +114 -64
  70. package/src/components/Chart/RadarChart.tsx +202 -218
  71. package/src/components/Chart/ScatterChart.tsx +111 -97
  72. package/src/components/Chart/TreemapChart.tsx +147 -222
  73. package/src/components/Chart/WaterfallChart.tsx +253 -291
  74. package/src/components/Chart/index.ts +11 -9
  75. package/src/components/Chart/types.ts +85 -9
  76. package/src/components/Chart/utils.ts +66 -0
  77. package/src/components/ColorModeToggle/ColorModeToggle.stories.tsx +121 -11
  78. package/src/components/ColorModeToggle/ColorModeToggle.tsx +149 -45
  79. package/src/components/ColorModeToggle/index.ts +1 -1
  80. package/src/components/Countdown/Countdown.tsx +4 -0
  81. package/src/components/DataTable/DataTable.tsx +2 -1
  82. package/src/components/DatePicker/DatePicker.stories.tsx +0 -11
  83. package/src/components/DatePicker/DatePicker.tsx +3 -9
  84. package/src/components/DatePicker/types.ts +5 -0
  85. package/src/components/Dropdown/Dropdown.stories.tsx +32 -25
  86. package/src/components/Dropdown/Dropdown.tsx +26 -28
  87. package/src/components/EdgePanel/EdgePanel.stories.tsx +13 -15
  88. package/src/components/EdgePanel/EdgePanel.tsx +20 -5
  89. package/src/components/Footer/Footer.stories.tsx +187 -60
  90. package/src/components/Footer/Footer.test.tsx +134 -0
  91. package/src/components/Footer/Footer.tsx +133 -34
  92. package/src/components/Footer/FooterLink.tsx +1 -1
  93. package/src/components/Footer/FooterSection.tsx +53 -36
  94. package/src/components/Footer/FooterSocialLink.tsx +32 -29
  95. package/src/components/Footer/README.md +82 -3
  96. package/src/components/Footer/index.ts +1 -1
  97. package/src/components/Form/Checkbox.stories.tsx +13 -5
  98. package/src/components/Form/Checkbox.tsx +3 -6
  99. package/src/components/Form/Form.stories.tsx +10 -3
  100. package/src/components/Form/Form.tsx +2 -0
  101. package/src/components/Form/FormGroup.tsx +2 -1
  102. package/src/components/Form/Input.stories.tsx +12 -11
  103. package/src/components/Form/Input.tsx +97 -95
  104. package/src/components/Form/Radio.stories.tsx +22 -7
  105. package/src/components/Form/Radio.tsx +3 -6
  106. package/src/components/Form/Select.stories.tsx +21 -6
  107. package/src/components/Form/Select.tsx +3 -5
  108. package/src/components/Form/Textarea.stories.tsx +13 -11
  109. package/src/components/Form/Textarea.tsx +88 -86
  110. package/src/components/Hero/Hero.stories.tsx +2 -3
  111. package/src/components/Hero/Hero.tsx +5 -6
  112. package/src/components/Icon/Icon.tsx +12 -1
  113. package/src/components/List/List.tsx +2 -1
  114. package/src/components/List/ListGroup.tsx +2 -1
  115. package/src/components/Messages/Messages.tsx +3 -2
  116. package/src/components/Modal/Modal.stories.tsx +48 -34
  117. package/src/components/Modal/Modal.tsx +19 -23
  118. package/src/components/Navigation/Menu/MegaMenu.tsx +2 -2
  119. package/src/components/Navigation/Menu/Menu.tsx +2 -2
  120. package/src/components/Navigation/Nav/Nav.tsx +6 -1
  121. package/src/components/Navigation/Nav/NavDropdown.tsx +10 -1
  122. package/src/components/Navigation/Navbar/Navbar.tsx +4 -1
  123. package/src/components/Navigation/SideMenu/SideMenu.tsx +3 -2
  124. package/src/components/Pagination/Pagination.stories.tsx +13 -6
  125. package/src/components/Pagination/Pagination.tsx +7 -6
  126. package/src/components/PhotoViewer/PhotoViewer.tsx +2 -1
  127. package/src/components/Popover/Popover.stories.tsx +32 -24
  128. package/src/components/Popover/Popover.tsx +4 -1
  129. package/src/components/ProductReview/ProductReview.tsx +8 -2
  130. package/src/components/Progress/Progress.tsx +2 -1
  131. package/src/components/Rating/Rating.stories.tsx +11 -6
  132. package/src/components/Rating/Rating.tsx +3 -5
  133. package/src/components/River/River.tsx +5 -5
  134. package/src/components/SectionIntro/SectionIntro.tsx +8 -2
  135. package/src/components/Slider/Slider.stories.tsx +4 -4
  136. package/src/components/Slider/Slider.tsx +4 -3
  137. package/src/components/Spinner/Spinner.tsx +2 -1
  138. package/src/components/Steps/Steps.stories.tsx +5 -4
  139. package/src/components/Steps/Steps.tsx +8 -5
  140. package/src/components/Tab/Tab.stories.tsx +4 -3
  141. package/src/components/Tab/Tab.tsx +8 -6
  142. package/src/components/Testimonial/Testimonial.tsx +8 -2
  143. package/src/components/Todo/Todo.tsx +2 -1
  144. package/src/components/Toggle/Toggle.stories.tsx +5 -4
  145. package/src/components/Toggle/Toggle.tsx +8 -5
  146. package/src/components/Tooltip/Tooltip.stories.tsx +40 -30
  147. package/src/components/Tooltip/Tooltip.tsx +9 -2
  148. package/src/components/Upload/Upload.stories.tsx +252 -0
  149. package/src/components/Upload/Upload.tsx +92 -53
  150. package/src/components/VideoPlayer/VideoPlayer.tsx +3 -1
  151. package/src/components/index.ts +0 -4
  152. package/src/layouts/Grid/Grid.stories.tsx +10 -23
  153. package/src/layouts/Grid/Grid.tsx +20 -1
  154. package/src/layouts/Grid/GridCol.tsx +76 -48
  155. package/src/lib/composables/useAtomixGlass.ts +861 -44
  156. package/src/lib/composables/useBarChart.ts +13 -6
  157. package/src/lib/composables/useChart.ts +17 -13
  158. package/src/lib/composables/useChartExport.ts +19 -78
  159. package/src/lib/composables/useChartToolbar.ts +0 -1
  160. package/src/lib/composables/useEdgePanel.ts +111 -103
  161. package/src/lib/composables/useFooter.ts +3 -3
  162. package/src/lib/composables/useGlassContainer.ts +16 -7
  163. package/src/lib/composables/useLineChart.ts +8 -1
  164. package/src/lib/composables/useRiver.ts +5 -0
  165. package/src/lib/composables/useSlider.ts +62 -24
  166. package/src/lib/composables/useVideoPlayer.ts +60 -63
  167. package/src/lib/constants/components.ts +146 -32
  168. package/src/lib/types/components.ts +258 -10
  169. package/src/lib/utils/displacement-generator.ts +55 -49
  170. package/src/lib/utils/icons.ts +1 -1
  171. package/src/lib/utils/index.ts +16 -10
  172. package/src/styles/01-settings/_settings.accordion.scss +19 -19
  173. package/src/styles/01-settings/_settings.animations.scss +5 -5
  174. package/src/styles/01-settings/_settings.avatar-group.scss +1 -1
  175. package/src/styles/01-settings/_settings.avatar.scss +17 -17
  176. package/src/styles/01-settings/_settings.background.scss +1 -4
  177. package/src/styles/01-settings/_settings.badge.scss +1 -1
  178. package/src/styles/01-settings/_settings.breadcrumb.scss +1 -1
  179. package/src/styles/01-settings/_settings.card.scss +1 -1
  180. package/src/styles/01-settings/_settings.chart.scss +65 -2
  181. package/src/styles/01-settings/_settings.dropdown.scss +1 -1
  182. package/src/styles/01-settings/_settings.footer.scss +35 -42
  183. package/src/styles/01-settings/_settings.input.scss +1 -1
  184. package/src/styles/01-settings/_settings.list.scss +1 -1
  185. package/src/styles/01-settings/_settings.rating.scss +1 -1
  186. package/src/styles/01-settings/_settings.tabs.scss +1 -1
  187. package/src/styles/01-settings/_settings.upload.scss +6 -5
  188. package/src/styles/02-tools/_tools.animations.scss +4 -5
  189. package/src/styles/02-tools/_tools.background.scss +1 -13
  190. package/src/styles/02-tools/_tools.glass.scss +0 -1
  191. package/src/styles/02-tools/_tools.utility-api.scss +42 -34
  192. package/src/styles/03-generic/_generic.root.scss +1 -4
  193. package/src/styles/04-elements/_elements.body.scss +0 -1
  194. package/src/styles/06-components/_components.atomix-glass.scss +217 -39
  195. package/src/styles/06-components/_components.badge.scss +6 -8
  196. package/src/styles/06-components/_components.button.scss +8 -3
  197. package/src/styles/06-components/_components.card.scss +2 -14
  198. package/src/styles/06-components/_components.chart.scss +969 -1449
  199. package/src/styles/06-components/_components.color-mode-toggle.scss +43 -6
  200. package/src/styles/06-components/_components.dropdown.scss +19 -7
  201. package/src/styles/06-components/_components.edge-panel.scss +4 -2
  202. package/src/styles/06-components/_components.footer.scss +166 -85
  203. package/src/styles/06-components/_components.input.scss +8 -9
  204. package/src/styles/06-components/_components.list.scss +1 -0
  205. package/src/styles/06-components/_components.modal.scss +5 -3
  206. package/src/styles/06-components/_components.skeleton.scss +8 -6
  207. package/src/styles/06-components/_components.upload.scss +219 -4
  208. package/src/styles/06-components/old.chart.styles.scss +1 -30
  209. package/src/styles/99-utilities/_utilities.opacity.scss +1 -1
  210. package/src/styles/99-utilities/_utilities.scss +1 -1
  211. package/src/components/Chart/AdvancedChart.tsx +0 -624
  212. package/src/components/Chart/LineChartNew.tsx +0 -167
  213. package/src/components/Chart/RealTimeChart.tsx +0 -436
  214. package/src/components/DatePicker/DatePicker copy.tsx +0 -551
@@ -27,14 +27,36 @@ const DEPTH_LAYERS = 3;
27
27
  const ORGANIC_FLOW_SCALE = 12;
28
28
  const RADIAL_DISTORTION_STRENGTH = 0.4;
29
29
 
30
+ // Enhanced error handling constants
31
+ const MAX_CANVAS_DIMENSION = 4096;
32
+ const MIN_CANVAS_DIMENSION = 1;
33
+ const DEFAULT_CANVAS_WIDTH = 256;
34
+ const DEFAULT_CANVAS_HEIGHT = 256;
35
+
30
36
  // Utility functions
31
37
  const smoothStep = (a: number, b: number, t: number): number => {
38
+ // Add input validation
39
+ if (typeof a !== 'number' || typeof b !== 'number' || typeof t !== 'number') {
40
+ return 0;
41
+ }
42
+
32
43
  const clamped = Math.max(0, Math.min(1, (t - a) / (b - a)));
33
44
  return clamped * clamped * (3 - 2 * clamped);
34
45
  };
35
46
 
36
47
  const calculateLength = (x: number, y: number): number => {
37
- return Math.sqrt(x * x + y * y);
48
+ // Add input validation and error handling
49
+ if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) {
50
+ return 0;
51
+ }
52
+
53
+ // Prevent potential overflow
54
+ const maxX = Math.max(Math.abs(x), Math.abs(y));
55
+ if (maxX === 0) return 0;
56
+
57
+ const scaledX = x / maxX;
58
+ const scaledY = y / maxX;
59
+ return maxX * Math.sqrt(scaledX * scaledX + scaledY * scaledY);
38
60
  };
39
61
 
40
62
  const roundedRectSDF = (
@@ -44,80 +66,140 @@ const roundedRectSDF = (
44
66
  height: number,
45
67
  radius: number
46
68
  ): number => {
69
+ // Add input validation
70
+ if (typeof x !== 'number' || typeof y !== 'number' ||
71
+ typeof width !== 'number' || typeof height !== 'number' ||
72
+ typeof radius !== 'number') {
73
+ return 0;
74
+ }
75
+
47
76
  const qx = Math.abs(x) - width + radius;
48
77
  const qy = Math.abs(y) - height + radius;
49
78
  return Math.min(Math.max(qx, qy), 0) + calculateLength(Math.max(qx, 0), Math.max(qy, 0)) - radius;
50
79
  };
51
80
 
52
- const createTexture = (x: number, y: number): Vec2 => ({ x, y });
81
+ const createTexture = (x: number, y: number): Vec2 => {
82
+ // Add input validation and clamping
83
+ const clampedX = typeof x === 'number' && !isNaN(x) ? Math.max(0, Math.min(1, x)) : 0.5;
84
+ const clampedY = typeof y === 'number' && !isNaN(y) ? Math.max(0, Math.min(1, y)) : 0.5;
85
+ return { x: clampedX, y: clampedY };
86
+ };
53
87
 
54
88
  // Validation helpers
55
89
  const validateVec2 = (vec: Vec2): boolean => {
56
- return vec && typeof vec.x === 'number' && typeof vec.y === 'number' &&
57
- !isNaN(vec.x) && !isNaN(vec.y);
90
+ return (
91
+ vec && typeof vec.x === 'number' && typeof vec.y === 'number' && !isNaN(vec.x) && !isNaN(vec.y)
92
+ );
58
93
  };
59
94
 
60
95
  const clampValue = (value: number, min: number, max: number): number => {
96
+ // Add input validation
97
+ if (typeof value !== 'number' || typeof min !== 'number' || typeof max !== 'number') {
98
+ return min;
99
+ }
100
+
101
+ if (isNaN(value)) return min;
102
+ if (isNaN(min)) return 0;
103
+ if (isNaN(max)) return 1;
104
+
61
105
  return Math.max(min, Math.min(max, value));
62
106
  };
63
107
 
64
108
  // Advanced easing functions for Apple-style smooth animations
65
109
  const easeInOutCubic = (t: number): number => {
66
- return t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2;
110
+ // Add input validation
111
+ if (typeof t !== 'number' || isNaN(t)) {
112
+ return 0;
113
+ }
114
+
115
+ const clampedT = Math.max(0, Math.min(1, t));
116
+ return clampedT < 0.5 ? 4 * clampedT * clampedT * clampedT : 1 - Math.pow(-2 * clampedT + 2, 3) / 2;
67
117
  };
68
118
 
69
119
  const easeOutQuart = (t: number): number => {
70
- return 1 - Math.pow(1 - t, 4);
120
+ // Add input validation
121
+ if (typeof t !== 'number' || isNaN(t)) {
122
+ return 0;
123
+ }
124
+
125
+ const clampedT = Math.max(0, Math.min(1, t));
126
+ return 1 - Math.pow(1 - clampedT, 4);
71
127
  };
72
128
 
73
129
  // Perlin-like noise for organic distortion
74
130
  const noise2D = (x: number, y: number): number => {
131
+ // Add input validation
132
+ if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) {
133
+ return 0;
134
+ }
135
+
75
136
  const X = Math.floor(x) & 255;
76
137
  const Y = Math.floor(y) & 255;
77
-
138
+
78
139
  const xf = x - Math.floor(x);
79
140
  const yf = y - Math.floor(y);
80
-
141
+
81
142
  const u = easeInOutCubic(xf);
82
143
  const v = easeInOutCubic(yf);
83
-
144
+
84
145
  // Simple hash-based pseudo-random
85
146
  const hash = (i: number, j: number): number => {
147
+ // Add input validation
148
+ if (typeof i !== 'number' || typeof j !== 'number') {
149
+ return 0;
150
+ }
151
+
86
152
  const n = i + j * 57;
87
- return (Math.sin(n * 12.9898 + 78.233) * 43758.5453) % 1;
153
+ // Use a more stable hash function
154
+ const hashed = Math.sin(n * 12.9898 + 78.233) * 43758.5453;
155
+ return hashed - Math.floor(hashed);
88
156
  };
89
-
157
+
90
158
  const a = hash(X, Y);
91
159
  const b = hash(X + 1, Y);
92
160
  const c = hash(X, Y + 1);
93
161
  const d = hash(X + 1, Y + 1);
94
-
162
+
95
163
  const x1 = a + u * (b - a);
96
164
  const x2 = c + u * (d - c);
97
-
165
+
98
166
  return x1 + v * (x2 - x1);
99
167
  };
100
168
 
101
169
  // Multi-octave noise for complex organic patterns
102
170
  const fbm = (x: number, y: number, octaves: number = 4): number => {
171
+ // Add input validation
172
+ if (typeof x !== 'number' || typeof y !== 'number' || isNaN(x) || isNaN(y)) {
173
+ return 0;
174
+ }
175
+
176
+ // Clamp octaves to prevent performance issues
177
+ const clampedOctaves = Math.max(1, Math.min(8, Math.floor(octaves)));
178
+
103
179
  let value = 0;
104
180
  let amplitude = 0.5;
105
181
  let frequency = 1;
106
-
107
- for (let i = 0; i < octaves; i++) {
182
+
183
+ for (let i = 0; i < clampedOctaves; i++) {
108
184
  value += amplitude * noise2D(x * frequency, y * frequency);
109
185
  frequency *= 2;
110
186
  amplitude *= 0.5;
111
187
  }
112
-
188
+
113
189
  return value;
114
190
  };
115
191
 
116
192
  // Radial distortion for glass-like refraction
117
193
  const calculateRadialDistortion = (x: number, y: number, strength: number): Vec2 => {
118
- const distance = calculateLength(x, y);
119
- const distortion = Math.pow(distance, 2) * strength;
194
+ // Add input validation
195
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof strength !== 'number' ||
196
+ isNaN(x) || isNaN(y) || isNaN(strength)) {
197
+ return { x: 0, y: 0 };
198
+ }
120
199
 
200
+ const distance = calculateLength(x, y);
201
+ const distortion = Math.pow(Math.min(distance, 10), 2) * strength; // Limit distance to prevent extreme values
202
+
121
203
  return {
122
204
  x: x * (1 + distortion),
123
205
  y: y * (1 + distortion),
@@ -126,9 +208,20 @@ const calculateRadialDistortion = (x: number, y: number, strength: number): Vec2
126
208
 
127
209
  // Chromatic aberration calculation
128
210
  const calculateChromaticOffset = (x: number, y: number, intensity: number): Vec2 => {
211
+ // Add input validation
212
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof intensity !== 'number' ||
213
+ isNaN(x) || isNaN(y) || isNaN(intensity)) {
214
+ return { x: 0, y: 0 };
215
+ }
216
+
129
217
  const distance = calculateLength(x, y);
130
- const angle = Math.atan2(y, x);
218
+ // Prevent division by zero and extreme values
219
+ if (distance === 0) {
220
+ return { x: 0, y: 0 };
221
+ }
131
222
 
223
+ const angle = Math.atan2(y, x);
224
+
132
225
  return {
133
226
  x: Math.cos(angle) * distance * intensity,
134
227
  y: Math.sin(angle) * distance * intensity,
@@ -137,38 +230,56 @@ const calculateChromaticOffset = (x: number, y: number, intensity: number): Vec2
137
230
 
138
231
  // Advanced caustic pattern generator for glass refraction
139
232
  const calculateCaustics = (x: number, y: number, time: number, intensity: number = 1): number => {
233
+ // Add input validation
234
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof time !== 'number' ||
235
+ typeof intensity !== 'number' || isNaN(x) || isNaN(y) || isNaN(time) || isNaN(intensity)) {
236
+ return 0.5; // Return middle value on error
237
+ }
238
+
140
239
  const scale = 8;
141
240
  const speed = 2;
142
-
241
+
143
242
  // Multiple caustic layers for realistic light refraction
144
243
  const caustic1 = Math.sin(x * scale + time * speed) * Math.cos(y * scale - time * speed);
145
- const caustic2 = Math.sin((x + 0.5) * scale * 1.3 - time * speed * 0.8) *
146
- Math.cos((y - 0.3) * scale * 1.3 + time * speed * 0.8);
147
- const caustic3 = Math.sin((x - 0.3) * scale * 0.7 + time * speed * 1.2) *
148
- Math.cos((y + 0.4) * scale * 0.7 - time * speed * 1.2);
149
-
244
+ const caustic2 =
245
+ Math.sin((x + 0.5) * scale * 1.3 - time * speed * 0.8) *
246
+ Math.cos((y - 0.3) * scale * 1.3 + time * speed * 0.8);
247
+ const caustic3 =
248
+ Math.sin((x - 0.3) * scale * 0.7 + time * speed * 1.2) *
249
+ Math.cos((y + 0.4) * scale * 0.7 - time * speed * 1.2);
250
+
150
251
  // Combine caustic layers with varying intensities
151
- const combined = (caustic1 * 0.5 + caustic2 * 0.3 + caustic3 * 0.2);
152
-
252
+ const combined = caustic1 * 0.5 + caustic2 * 0.3 + caustic3 * 0.2;
253
+
153
254
  // Apply intensity and normalize to 0-1 range
154
255
  return (combined + 1) * 0.5 * intensity;
155
256
  };
156
257
 
157
258
  // Spectral dispersion for rainbow-like chromatic effects
158
259
  const calculateSpectralDispersion = (
159
- x: number,
160
- y: number,
161
- angle: number,
260
+ x: number,
261
+ y: number,
262
+ angle: number,
162
263
  intensity: number
163
264
  ): { r: Vec2; g: Vec2; b: Vec2 } => {
164
- const distance = calculateLength(x, y);
165
- const dispersionStrength = distance * intensity;
265
+ // Add input validation
266
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof angle !== 'number' ||
267
+ typeof intensity !== 'number' || isNaN(x) || isNaN(y) || isNaN(angle) || isNaN(intensity)) {
268
+ return {
269
+ r: { x: 0, y: 0 },
270
+ g: { x: 0, y: 0 },
271
+ b: { x: 0, y: 0 }
272
+ };
273
+ }
166
274
 
275
+ const distance = calculateLength(x, y);
276
+ const dispersionStrength = Math.min(distance * intensity, 1); // Limit strength to prevent extreme values
277
+
167
278
  // Different wavelengths refract at different angles (like a prism)
168
279
  const redOffset = dispersionStrength * 0.8;
169
280
  const greenOffset = dispersionStrength * 1.0;
170
281
  const blueOffset = dispersionStrength * 1.2;
171
-
282
+
172
283
  return {
173
284
  r: {
174
285
  x: Math.cos(angle) * redOffset,
@@ -187,55 +298,78 @@ const calculateSpectralDispersion = (
187
298
 
188
299
  // Parallax depth offset calculation for multi-layer effects
189
300
  const calculateParallaxOffset = (
190
- x: number,
191
- y: number,
192
- depth: number,
193
- mouseX: number = 0,
301
+ x: number,
302
+ y: number,
303
+ depth: number,
304
+ mouseX: number = 0,
194
305
  mouseY: number = 0
195
306
  ): Vec2 => {
196
- const parallaxStrength = 0.02 * depth;
307
+ // Add input validation
308
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof depth !== 'number' ||
309
+ typeof mouseX !== 'number' || typeof mouseY !== 'number' ||
310
+ isNaN(x) || isNaN(y) || isNaN(depth) || isNaN(mouseX) || isNaN(mouseY)) {
311
+ return { x: 0, y: 0 };
312
+ }
197
313
 
314
+ const parallaxStrength = Math.min(0.02 * depth, 0.1); // Limit strength to prevent extreme values
315
+
198
316
  // Calculate offset based on view angle (simulated by mouse position)
199
317
  const offsetX = (x - mouseX) * parallaxStrength;
200
318
  const offsetY = (y - mouseY) * parallaxStrength;
201
-
319
+
202
320
  return { x: offsetX, y: offsetY };
203
321
  };
204
322
 
205
323
  // Volumetric density for depth perception and scattering
206
- const calculateVolumetricDensity = (
207
- x: number,
208
- y: number,
209
- depth: number,
210
- time: number
211
- ): number => {
212
- const noiseValue = fbm(x * 5 + time * 0.5, y * 5 - time * 0.5, 3);
213
- const depthFalloff = Math.exp(-depth * 2);
324
+ const calculateVolumetricDensity = (x: number, y: number, depth: number, time: number): number => {
325
+ // Add input validation
326
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof depth !== 'number' ||
327
+ typeof time !== 'number' || isNaN(x) || isNaN(y) || isNaN(depth) || isNaN(time)) {
328
+ return 0.5; // Return middle value on error
329
+ }
214
330
 
331
+ const noiseValue = fbm(x * 5 + time * 0.5, y * 5 - time * 0.5, 3);
332
+ const depthFalloff = Math.exp(-Math.max(0, depth) * 2); // Ensure depth is not negative
333
+
215
334
  return noiseValue * depthFalloff * 0.5 + 0.5;
216
335
  };
217
336
 
218
337
  // Advanced turbulence for organic glass distortion
219
338
  const calculateTurbulence = (x: number, y: number, time: number, octaves: number = 5): number => {
339
+ // Add input validation
340
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof time !== 'number' ||
341
+ typeof octaves !== 'number' || isNaN(x) || isNaN(y) || isNaN(time) || isNaN(octaves)) {
342
+ return 0;
343
+ }
344
+
345
+ // Clamp octaves to prevent performance issues
346
+ const clampedOctaves = Math.max(1, Math.min(8, Math.floor(octaves)));
347
+
220
348
  let turbulence = 0;
221
349
  let amplitude = 1;
222
350
  let frequency = 1;
223
-
224
- for (let i = 0; i < octaves; i++) {
351
+
352
+ for (let i = 0; i < clampedOctaves; i++) {
225
353
  const noiseVal = Math.abs(noise2D(x * frequency + time, y * frequency - time));
226
354
  turbulence += noiseVal * amplitude;
227
355
  frequency *= 2;
228
356
  amplitude *= 0.5;
229
357
  }
230
-
358
+
231
359
  return turbulence;
232
360
  };
233
361
 
234
362
  // Micro-surface detail for high-quality glass texture
235
363
  const calculateMicroSurface = (x: number, y: number, time: number): number => {
364
+ // Add input validation
365
+ if (typeof x !== 'number' || typeof y !== 'number' || typeof time !== 'number' ||
366
+ isNaN(x) || isNaN(y) || isNaN(time)) {
367
+ return 0.5; // Return middle value on error
368
+ }
369
+
236
370
  const highFreqNoise = fbm(x * 40 + time * 0.3, y * 40 - time * 0.3, 6);
237
371
  const microDetail = fbm(x * 80, y * 80, 4);
238
-
372
+
239
373
  return (highFreqNoise * 0.7 + microDetail * 0.3) * 0.5;
240
374
  };
241
375
 
@@ -245,65 +379,66 @@ export const fragmentShaders = {
245
379
  if (!validateVec2(uv)) {
246
380
  return { x: 0.5, y: 0.5 };
247
381
  }
248
-
382
+
249
383
  const ix = uv.x - 0.5;
250
384
  const iy = uv.y - 0.5;
251
385
  const time = Date.now() * TIME_SCALE;
252
-
386
+
253
387
  // Enhanced distortion with mouse influence
254
388
  const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
255
389
  const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
256
390
  const mouseDistance = calculateLength(mouseX, mouseY);
257
391
  const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 2, 1));
258
-
392
+
259
393
  // Multi-layered organic distortion using FBM noise
260
394
  const noiseScale = ORGANIC_FLOW_SCALE;
261
- const organicFlow = fbm(
262
- (ix + mouseX * 0.5) * noiseScale + time,
263
- (iy + mouseY * 0.5) * noiseScale + time * 0.7,
264
- 3
265
- ) - 0.5;
266
-
395
+ const organicFlow =
396
+ fbm(
397
+ (ix + mouseX * 0.5) * noiseScale + time,
398
+ (iy + mouseY * 0.5) * noiseScale + time * 0.7,
399
+ 3
400
+ ) - 0.5;
401
+
267
402
  // Enhanced Apple-like liquid distortion with rounded rect SDF
268
403
  const distanceToEdge = roundedRectSDF(ix, iy, 0.4, 0.3, 0.35);
269
404
  const baseDisplacement = smoothStep(0.8, 0, distanceToEdge - 0.05);
270
-
405
+
271
406
  // Radial distortion for glass-like refraction
272
407
  const radialDist = calculateRadialDistortion(ix, iy, RADIAL_DISTORTION_STRENGTH * 0.1);
273
408
  const refractionX = (radialDist.x - ix) * REFRACTION_INTENSITY * baseDisplacement;
274
409
  const refractionY = (radialDist.y - iy) * REFRACTION_INTENSITY * baseDisplacement;
275
-
410
+
276
411
  // Apple-style liquid flow with time-based animation
277
412
  const flowX = Math.sin((ix + mouseX * 2) * 8 + time * 2) * 0.018;
278
413
  const flowY = Math.cos((iy + mouseY * 2) * 8 + time * 1.5) * 0.018;
279
-
414
+
280
415
  // Multi-directional ripples with mouse influence
281
416
  const ripple1 = Math.sin((ix - mouseX) * 12 + (iy - mouseY) * 12 + time * 3) * 0.015;
282
417
  const ripple2 = Math.cos((ix + mouseX) * 10 - (iy - mouseY) * 10 - time * 2) * 0.012;
283
418
  const rippleEffect = (ripple1 + ripple2) * mouseFalloff * mouseDistance;
284
-
419
+
285
420
  // Depth-based layering for premium glass effect
286
421
  const depthLayer1 = Math.sin(ix * 15 + time) * Math.cos(iy * 15 - time) * 0.008;
287
422
  const depthLayer2 = Math.sin(ix * 20 - time * 0.5) * Math.cos(iy * 20 + time * 0.5) * 0.006;
288
423
  const depthEffect = (depthLayer1 + depthLayer2) * baseDisplacement;
289
-
424
+
290
425
  // Combine all distortion effects with fluid viscosity
291
426
  const liquidFlow = (flowX + flowY + organicFlow * 0.025) * FLUID_VISCOSITY;
292
427
  const totalDistortionX = refractionX + liquidFlow + rippleEffect + depthEffect;
293
428
  const totalDistortionY = refractionY + liquidFlow * 0.8 + rippleEffect * 0.9 + depthEffect;
294
-
429
+
295
430
  // Apply chromatic aberration for premium glass look
296
431
  const chromaticOffset = calculateChromaticOffset(ix, iy, CHROMATIC_SPREAD * baseDisplacement);
297
-
432
+
298
433
  const displacement = baseDisplacement * 1.15;
299
434
  const scaled = smoothStep(0, 1, displacement);
300
-
435
+
301
436
  // Final position with all effects combined
302
437
  const finalX = ix + totalDistortionX + chromaticOffset.x * 0.5;
303
438
  const finalY = iy + totalDistortionY + chromaticOffset.y * 0.5;
304
-
439
+
305
440
  return createTexture(
306
- clampValue(finalX * scaled + 0.5, 0, 1),
441
+ clampValue(finalX * scaled + 0.5, 0, 1),
307
442
  clampValue(finalY * scaled + 0.5, 0, 1)
308
443
  );
309
444
  },
@@ -313,42 +448,39 @@ export const fragmentShaders = {
313
448
  if (!validateVec2(uv)) {
314
449
  return { x: 0.5, y: 0.5 };
315
450
  }
316
-
451
+
317
452
  const ix = uv.x - 0.5;
318
453
  const iy = uv.y - 0.5;
319
454
  const time = Date.now() * TIME_SCALE * 0.6;
320
-
455
+
321
456
  const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
322
457
  const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
323
458
  const mouseDistance = calculateLength(mouseX, mouseY);
324
459
  const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 1.5, 1));
325
-
460
+
326
461
  // High-quality organic distortion with multiple octaves
327
462
  const organicX = fbm((ix + mouseX * 0.3) * 10 + time, (iy + mouseY * 0.3) * 10, 5) - 0.5;
328
463
  const organicY = fbm((ix - mouseX * 0.3) * 10, (iy - mouseY * 0.3) * 10 + time * 0.8, 5) - 0.5;
329
-
464
+
330
465
  // Smooth rounded rectangle mask
331
466
  const distanceToEdge = roundedRectSDF(ix, iy, 0.42, 0.32, 0.38);
332
467
  const mask = smoothStep(0.85, -0.1, distanceToEdge);
333
-
468
+
334
469
  // Fluid dynamics simulation
335
470
  const fluidVelocityX = Math.sin(ix * 6 + time * 2) * Math.cos(iy * 4 - time) * 0.025;
336
471
  const fluidVelocityY = Math.cos(ix * 4 - time) * Math.sin(iy * 6 + time * 2) * 0.025;
337
-
472
+
338
473
  // Mouse-driven vortex effect
339
474
  const vortexAngle = Math.atan2(iy - mouseY, ix - mouseX);
340
475
  const vortexStrength = mouseFalloff * mouseDistance * 0.08;
341
476
  const vortexX = Math.cos(vortexAngle + time) * vortexStrength;
342
477
  const vortexY = Math.sin(vortexAngle + time) * vortexStrength;
343
-
478
+
344
479
  // Combine effects with premium smoothing
345
480
  const totalX = ix + (organicX * 0.035 + fluidVelocityX + vortexX) * mask;
346
481
  const totalY = iy + (organicY * 0.035 + fluidVelocityY + vortexY) * mask;
347
-
348
- return createTexture(
349
- clampValue(totalX + 0.5, 0, 1),
350
- clampValue(totalY + 0.5, 0, 1)
351
- );
482
+
483
+ return createTexture(clampValue(totalX + 0.5, 0, 1), clampValue(totalY + 0.5, 0, 1));
352
484
  },
353
485
 
354
486
  // High-end glass with advanced refraction and depth
@@ -356,20 +488,20 @@ export const fragmentShaders = {
356
488
  if (!validateVec2(uv)) {
357
489
  return { x: 0.5, y: 0.5 };
358
490
  }
359
-
491
+
360
492
  const ix = uv.x - 0.5;
361
493
  const iy = uv.y - 0.5;
362
494
  const time = Date.now() * TIME_SCALE * 0.4;
363
-
495
+
364
496
  const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
365
497
  const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
366
498
  const mouseDistance = calculateLength(mouseX, mouseY);
367
-
499
+
368
500
  // Advanced radial distortion with depth
369
501
  const centerDistance = calculateLength(ix, iy);
370
- const refractionStrength = Math.pow(centerDistance, 1.5) * 0.3;
502
+ const refractionStrength = Math.pow(Math.min(centerDistance, 1), 1.5) * 0.3; // Limit centerDistance
371
503
  const refractionAngle = Math.atan2(iy, ix);
372
-
504
+
373
505
  // Multi-layer depth effect
374
506
  let depthX = 0;
375
507
  let depthY = 0;
@@ -377,29 +509,26 @@ export const fragmentShaders = {
377
509
  const layerScale = (layer + 1) * 5;
378
510
  const layerTime = time * (1 + layer * 0.3);
379
511
  const layerStrength = 0.01 / (layer + 1);
380
-
512
+
381
513
  depthX += Math.sin(ix * layerScale + layerTime) * layerStrength;
382
514
  depthY += Math.cos(iy * layerScale - layerTime) * layerStrength;
383
515
  }
384
-
516
+
385
517
  // Glass refraction with mouse influence
386
518
  const refractionX = Math.cos(refractionAngle) * refractionStrength * (1 + mouseDistance * 0.5);
387
519
  const refractionY = Math.sin(refractionAngle) * refractionStrength * (1 + mouseDistance * 0.5);
388
-
520
+
389
521
  // Subtle organic movement
390
522
  const organicNoise = fbm(ix * 8 + time, iy * 8 - time, 2) - 0.5;
391
-
523
+
392
524
  // Edge-aware distortion
393
525
  const distanceToEdge = roundedRectSDF(ix, iy, 0.43, 0.33, 0.36);
394
526
  const edgeMask = smoothStep(0.9, -0.05, distanceToEdge);
395
-
527
+
396
528
  const finalX = ix + (refractionX + depthX + organicNoise * 0.015) * edgeMask;
397
529
  const finalY = iy + (refractionY + depthY + organicNoise * 0.015) * edgeMask;
398
-
399
- return createTexture(
400
- clampValue(finalX + 0.5, 0, 1),
401
- clampValue(finalY + 0.5, 0, 1)
402
- );
530
+
531
+ return createTexture(clampValue(finalX + 0.5, 0, 1), clampValue(finalY + 0.5, 0, 1));
403
532
  },
404
533
 
405
534
  // Metallic liquid effect with shimmer
@@ -407,36 +536,36 @@ export const fragmentShaders = {
407
536
  if (!validateVec2(uv)) {
408
537
  return { x: 0.5, y: 0.5 };
409
538
  }
410
-
539
+
411
540
  const ix = uv.x - 0.5;
412
541
  const iy = uv.y - 0.5;
413
542
  const time = Date.now() * TIME_SCALE * 1.2;
414
-
543
+
415
544
  const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
416
545
  const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
417
-
546
+
418
547
  // Metallic wave patterns
419
548
  const wave1 = Math.sin(ix * 20 + time * 4) * Math.cos(iy * 15 - time * 3) * 0.02;
420
549
  const wave2 = Math.cos(ix * 15 - time * 2) * Math.sin(iy * 20 + time * 5) * 0.015;
421
-
550
+
422
551
  // Shimmer effect
423
552
  const shimmer = fbm(ix * 25 + time * 2, iy * 25 - time * 2, 4) * 0.025;
424
-
553
+
425
554
  // Mouse interaction with metallic flow
426
555
  const flowAngle = Math.atan2(iy - mouseY, ix - mouseX);
427
556
  const flowDistance = calculateLength(ix - mouseX, iy - mouseY);
428
- const flowEffect = Math.sin(flowDistance * 15 - time * 6) * 0.02 * easeOutQuart(1 - Math.min(flowDistance * 2, 1));
429
-
557
+ const flowEffect =
558
+ Math.sin(flowDistance * 15 - time * 6) *
559
+ 0.02 *
560
+ easeOutQuart(1 - Math.min(flowDistance * 2, 1));
561
+
430
562
  const distanceToEdge = roundedRectSDF(ix, iy, 0.41, 0.31, 0.37);
431
563
  const mask = smoothStep(0.88, -0.08, distanceToEdge);
432
-
564
+
433
565
  const totalX = ix + (wave1 + shimmer + Math.cos(flowAngle) * flowEffect) * mask;
434
566
  const totalY = iy + (wave2 + shimmer * 0.8 + Math.sin(flowAngle) * flowEffect) * mask;
435
-
436
- return createTexture(
437
- clampValue(totalX + 0.5, 0, 1),
438
- clampValue(totalY + 0.5, 0, 1)
439
- );
567
+
568
+ return createTexture(clampValue(totalX + 0.5, 0, 1), clampValue(totalY + 0.5, 0, 1));
440
569
  },
441
570
 
442
571
  // basiBasi - Expert Premium Glass Shader
@@ -445,84 +574,87 @@ export const fragmentShaders = {
445
574
  if (!validateVec2(uv)) {
446
575
  return { x: 0.5, y: 0.5 };
447
576
  }
448
-
577
+
449
578
  const ix = uv.x - 0.5;
450
579
  const iy = uv.y - 0.5;
451
580
  const time = Date.now() * TIME_SCALE * 0.5;
452
-
581
+
453
582
  // Mouse interaction setup
454
583
  const mouseX = mousePosition && validateVec2(mousePosition) ? mousePosition.x - 0.5 : 0;
455
584
  const mouseY = mousePosition && validateVec2(mousePosition) ? mousePosition.y - 0.5 : 0;
456
585
  const mouseDistance = calculateLength(mouseX, mouseY);
457
586
  const mouseFalloff = easeOutQuart(1 - Math.min(mouseDistance * 1.2, 1));
458
-
587
+
459
588
  // === CAUSTIC LIGHT PATTERNS ===
460
589
  // Simulate light refraction through glass creating caustic patterns
461
590
  const causticIntensity = calculateCaustics(ix, iy, time, 0.8);
462
591
  const causticDistortion = (causticIntensity - 0.5) * 0.02;
463
-
592
+
464
593
  // === SPECTRAL DISPERSION ===
465
594
  // Rainbow-like chromatic effects from light splitting
466
595
  const refractionAngle = Math.atan2(iy, ix);
467
596
  const spectralDispersion = calculateSpectralDispersion(ix, iy, refractionAngle, 0.025);
468
-
597
+
469
598
  // Average the RGB channels for displacement (simulating prism effect)
470
- const spectralX = (spectralDispersion.r.x + spectralDispersion.g.x + spectralDispersion.b.x) / 3;
471
- const spectralY = (spectralDispersion.r.y + spectralDispersion.g.y + spectralDispersion.b.y) / 3;
472
-
599
+ const spectralX =
600
+ (spectralDispersion.r.x + spectralDispersion.g.x + spectralDispersion.b.x) / 3;
601
+ const spectralY =
602
+ (spectralDispersion.r.y + spectralDispersion.g.y + spectralDispersion.b.y) / 3;
603
+
473
604
  // === MULTI-LAYER PARALLAX DEPTH ===
474
605
  // Create depth perception with 7 layers
475
606
  let parallaxX = 0;
476
607
  let parallaxY = 0;
477
608
  const numParallaxLayers = 7;
478
-
609
+
479
610
  for (let layer = 0; layer < numParallaxLayers; layer++) {
480
611
  const depth = (layer + 1) / numParallaxLayers;
481
612
  const parallaxOffset = calculateParallaxOffset(ix, iy, depth, mouseX, mouseY);
482
-
613
+
483
614
  // Layer-specific distortion
484
- const layerNoise = fbm(
485
- (ix + parallaxOffset.x) * (8 + layer * 2) + time * (0.5 + layer * 0.1),
486
- (iy + parallaxOffset.y) * (8 + layer * 2) - time * (0.5 + layer * 0.1),
487
- 3
488
- ) - 0.5;
489
-
615
+ const layerNoise =
616
+ fbm(
617
+ (ix + parallaxOffset.x) * (8 + layer * 2) + time * (0.5 + layer * 0.1),
618
+ (iy + parallaxOffset.y) * (8 + layer * 2) - time * (0.5 + layer * 0.1),
619
+ 3
620
+ ) - 0.5;
621
+
490
622
  const layerWeight = 1 / (layer + 1);
491
623
  parallaxX += (parallaxOffset.x + layerNoise * 0.01) * layerWeight;
492
624
  parallaxY += (parallaxOffset.y + layerNoise * 0.01) * layerWeight;
493
625
  }
494
-
626
+
495
627
  // Normalize parallax effect
496
628
  parallaxX /= numParallaxLayers;
497
629
  parallaxY /= numParallaxLayers;
498
-
630
+
499
631
  // === VOLUMETRIC SCATTERING ===
500
632
  // Simulate light scattering through glass volume
501
633
  const volumetricDensity = calculateVolumetricDensity(ix, iy, 0.5, time);
502
634
  const scatteringX = Math.cos(refractionAngle) * volumetricDensity * 0.015;
503
635
  const scatteringY = Math.sin(refractionAngle) * volumetricDensity * 0.015;
504
-
636
+
505
637
  // === ADVANCED TURBULENCE ===
506
638
  // High-quality organic distortion
507
639
  const turbulence = calculateTurbulence(ix * 6, iy * 6, time, 6);
508
640
  const turbulenceX = Math.cos(turbulence * Math.PI * 2) * 0.012;
509
641
  const turbulenceY = Math.sin(turbulence * Math.PI * 2) * 0.012;
510
-
642
+
511
643
  // === MICRO-SURFACE DETAIL ===
512
644
  // Fine glass texture details
513
645
  const microSurface = calculateMicroSurface(ix, iy, time);
514
646
  const microDetailX = (microSurface - 0.5) * 0.008;
515
647
  const microDetailY = (microSurface - 0.5) * 0.008;
516
-
648
+
517
649
  // === ADVANCED RADIAL REFRACTION ===
518
650
  // Enhanced glass-like refraction with depth
519
651
  const centerDistance = calculateLength(ix, iy);
520
- const refractionStrength = Math.pow(centerDistance, 1.8) * 0.35;
652
+ const refractionStrength = Math.pow(Math.min(centerDistance, 1), 1.8) * 0.35; // Limit centerDistance
521
653
  const dynamicRefraction = refractionStrength * (1 + mouseFalloff * mouseDistance * 0.8);
522
-
654
+
523
655
  const refractionX = Math.cos(refractionAngle) * dynamicRefraction;
524
656
  const refractionY = Math.sin(refractionAngle) * dynamicRefraction;
525
-
657
+
526
658
  // === MOUSE-DRIVEN VORTEX ===
527
659
  // Interactive swirl effect
528
660
  const vortexAngle = Math.atan2(iy - mouseY, ix - mouseX);
@@ -530,63 +662,64 @@ export const fragmentShaders = {
530
662
  const vortexStrength = mouseFalloff * Math.sin(vortexDistance * 10 - time * 3) * 0.025;
531
663
  const vortexX = Math.cos(vortexAngle + time * 2) * vortexStrength;
532
664
  const vortexY = Math.sin(vortexAngle + time * 2) * vortexStrength;
533
-
665
+
534
666
  // === FLUID DYNAMICS ===
535
667
  // Liquid-like flow patterns
536
- const fluidX = Math.sin(ix * 10 + mouseX * 5 + time * 2.5) * Math.cos(iy * 8 - time * 2) * 0.018;
537
- const fluidY = Math.cos(ix * 8 - time * 2) * Math.sin(iy * 10 + mouseY * 5 + time * 2.5) * 0.018;
538
-
668
+ const fluidX =
669
+ Math.sin(ix * 10 + mouseX * 5 + time * 2.5) * Math.cos(iy * 8 - time * 2) * 0.018;
670
+ const fluidY =
671
+ Math.cos(ix * 8 - time * 2) * Math.sin(iy * 10 + mouseY * 5 + time * 2.5) * 0.018;
672
+
539
673
  // === RIPPLE EFFECTS ===
540
674
  // Multi-directional wave propagation
541
- const ripple1 = Math.sin(centerDistance * 15 - time * 4) * 0.012;
542
- const ripple2 = Math.cos(centerDistance * 20 + time * 3) * 0.008;
675
+ const ripple1 = Math.sin(Math.min(centerDistance, 10) * 15 - time * 4) * 0.012; // Limit centerDistance
676
+ const ripple2 = Math.cos(Math.min(centerDistance, 10) * 20 + time * 3) * 0.008; // Limit centerDistance
543
677
  const rippleEffect = (ripple1 + ripple2) * mouseFalloff;
544
678
  const rippleX = Math.cos(refractionAngle) * rippleEffect;
545
679
  const rippleY = Math.sin(refractionAngle) * rippleEffect;
546
-
680
+
547
681
  // === EDGE-AWARE MASKING ===
548
682
  // Smooth rounded rectangle with premium edge handling
549
683
  const distanceToEdge = roundedRectSDF(ix, iy, 0.44, 0.34, 0.39);
550
684
  const edgeMask = smoothStep(0.92, -0.12, distanceToEdge);
551
685
  const edgeSoftness = smoothStep(0.85, 0.1, distanceToEdge);
552
-
686
+
553
687
  // === COMBINE ALL EFFECTS ===
554
688
  // Layer all distortions with proper weighting
555
- const totalDistortionX = (
556
- refractionX * 1.2 +
557
- spectralX * 0.8 +
558
- parallaxX * 1.5 +
559
- scatteringX * 0.9 +
560
- turbulenceX * 1.0 +
561
- microDetailX * 0.6 +
562
- vortexX * 1.3 +
563
- fluidX * 1.1 +
564
- rippleX * 0.7 +
565
- causticDistortion
566
- ) * edgeMask * edgeSoftness;
567
-
568
- const totalDistortionY = (
569
- refractionY * 1.2 +
570
- spectralY * 0.8 +
571
- parallaxY * 1.5 +
572
- scatteringY * 0.9 +
573
- turbulenceY * 1.0 +
574
- microDetailY * 0.6 +
575
- vortexY * 1.3 +
576
- fluidY * 1.1 +
577
- rippleY * 0.7 +
578
- causticDistortion * 0.8
579
- ) * edgeMask * edgeSoftness;
580
-
689
+ const totalDistortionX =
690
+ (refractionX * 1.2 +
691
+ spectralX * 0.8 +
692
+ parallaxX * 1.5 +
693
+ scatteringX * 0.9 +
694
+ turbulenceX * 1.0 +
695
+ microDetailX * 0.6 +
696
+ vortexX * 1.3 +
697
+ fluidX * 1.1 +
698
+ rippleX * 0.7 +
699
+ causticDistortion) *
700
+ edgeMask *
701
+ edgeSoftness;
702
+
703
+ const totalDistortionY =
704
+ (refractionY * 1.2 +
705
+ spectralY * 0.8 +
706
+ parallaxY * 1.5 +
707
+ scatteringY * 0.9 +
708
+ turbulenceY * 1.0 +
709
+ microDetailY * 0.6 +
710
+ vortexY * 1.3 +
711
+ fluidY * 1.1 +
712
+ rippleY * 0.7 +
713
+ causticDistortion * 0.8) *
714
+ edgeMask *
715
+ edgeSoftness;
716
+
581
717
  // === FINAL POSITION ===
582
718
  // Apply all distortions with smooth falloff
583
719
  const finalX = ix + totalDistortionX * 0.85; // Scale down for subtlety
584
720
  const finalY = iy + totalDistortionY * 0.85;
585
-
586
- return createTexture(
587
- clampValue(finalX + 0.5, 0, 1),
588
- clampValue(finalY + 0.5, 0, 1)
589
- );
721
+
722
+ return createTexture(clampValue(finalX + 0.5, 0, 1), clampValue(finalY + 0.5, 0, 1));
590
723
  },
591
724
  };
592
725
 
@@ -601,10 +734,15 @@ export class ShaderDisplacementGenerator {
601
734
  if (!this.validateOptions(options)) {
602
735
  throw new Error('Invalid shader options provided');
603
736
  }
604
-
737
+
605
738
  this.canvas = document.createElement('canvas');
606
- this.canvas.width = Math.max(1, options.width * this.canvasDPI);
607
- this.canvas.height = Math.max(1, options.height * this.canvasDPI);
739
+ // Enhanced validation for canvas dimensions
740
+ this.canvas.width = Math.max(MIN_CANVAS_DIMENSION,
741
+ Math.min(MAX_CANVAS_DIMENSION,
742
+ Math.round(options.width * this.canvasDPI || DEFAULT_CANVAS_WIDTH)));
743
+ this.canvas.height = Math.max(MIN_CANVAS_DIMENSION,
744
+ Math.min(MAX_CANVAS_DIMENSION,
745
+ Math.round(options.height * this.canvasDPI || DEFAULT_CANVAS_HEIGHT)));
608
746
  this.canvas.style.display = 'none';
609
747
 
610
748
  const context = this.canvas.getContext('2d');
@@ -613,84 +751,114 @@ export class ShaderDisplacementGenerator {
613
751
  }
614
752
  this.context = context;
615
753
  }
616
-
754
+
617
755
  private validateOptions(options: ShaderOptions): boolean {
618
- return options &&
619
- typeof options.width === 'number' && options.width > 0 &&
620
- typeof options.height === 'number' && options.height > 0 &&
621
- typeof options.fragment === 'function';
756
+ try {
757
+ return (
758
+ options &&
759
+ typeof options.width === 'number' &&
760
+ options.width > 0 &&
761
+ options.width <= MAX_CANVAS_DIMENSION &&
762
+ typeof options.height === 'number' &&
763
+ options.height > 0 &&
764
+ options.height <= MAX_CANVAS_DIMENSION &&
765
+ typeof options.fragment === 'function'
766
+ );
767
+ } catch (e) {
768
+ // Graceful error handling
769
+ return false;
770
+ }
622
771
  }
623
772
 
624
773
  updateShader(mousePosition?: Vec2): string {
625
- const w = this.options.width * this.canvasDPI;
626
- const h = this.options.height * this.canvasDPI;
627
-
628
- let maxScale = 0;
629
- const rawValues: number[] = [];
630
-
631
- // Calculate displacement values with enhanced smoothing
632
- for (let y = 0; y < h; y++) {
633
- for (let x = 0; x < w; x++) {
634
- const uv: Vec2 = { x: x / w, y: y / h };
635
-
636
- const pos = this.options.fragment(uv, mousePosition);
637
- let dx = pos.x * w - x;
638
- let dy = pos.y * h - y;
639
-
640
- // Apply edge smoothing for Apple-like effect
641
- const edgeX = Math.min(x / w, (w - x) / w) * 2;
642
- const edgeY = Math.min(y / h, (h - y) / h) * 2;
643
- const edgeFactor = Math.min(edgeX, edgeY);
644
-
645
- dx *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
646
- dy *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
647
-
648
- maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
649
- rawValues.push(dx, dy);
650
- }
651
- }
774
+ try {
775
+ const w = this.options.width * this.canvasDPI;
776
+ const h = this.options.height * this.canvasDPI;
652
777
 
653
- // Improved normalization to prevent artifacts while maintaining intensity
654
- maxScale = Math.max(maxScale, MIN_SCALE);
778
+ let maxScale = 0;
779
+ const rawValues: number[] = [];
655
780
 
656
- // Create ImageData and fill it
657
- const imageData = this.context.createImageData(w, h);
658
- const data = imageData.data;
781
+ // Calculate displacement values with enhanced smoothing
782
+ for (let y = 0; y < h; y++) {
783
+ for (let x = 0; x < w; x++) {
784
+ const uv: Vec2 = { x: x / w, y: y / h };
659
785
 
660
- // Convert to image data with smoother normalization
661
- let rawIndex = 0;
662
- for (let y = 0; y < h; y++) {
663
- for (let x = 0; x < w; x++) {
664
- const dx = rawValues[rawIndex++] || 0;
665
- const dy = rawValues[rawIndex++] || 0;
786
+ const pos = this.options.fragment(uv, mousePosition);
787
+ let dx = pos.x * w - x;
788
+ let dy = pos.y * h - y;
666
789
 
667
- // Smooth the displacement values at edges to prevent hard transitions
668
- const edgeDistance = Math.min(x, y, w - x - 1, h - y - 1);
669
- const edgeFactor = Math.min(1, edgeDistance / EDGE_FADE_PIXELS);
790
+ // Apply edge smoothing for Apple-like effect
791
+ const edgeX = Math.min(x / w, (w - x) / w) * 2;
792
+ const edgeY = Math.min(y / h, (h - y) / h) * 2;
793
+ const edgeFactor = Math.min(edgeX, edgeY);
670
794
 
671
- const smoothedDx = dx * edgeFactor;
672
- const smoothedDy = dy * edgeFactor;
795
+ dx *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
796
+ dy *= smoothStep(0, EDGE_SMOOTHING_FACTOR, edgeFactor);
673
797
 
674
- const r = smoothedDx / maxScale + 0.5;
675
- const g = smoothedDy / maxScale + 0.5;
798
+ maxScale = Math.max(maxScale, Math.abs(dx), Math.abs(dy));
799
+ rawValues.push(dx, dy);
800
+ }
801
+ }
676
802
 
677
- const pixelIndex = (y * w + x) * 4;
678
- data[pixelIndex] = clampValue(r * 255, NORMALIZATION_CLAMP.min, NORMALIZATION_CLAMP.max); // Red channel (X displacement)
679
- data[pixelIndex + 1] = clampValue(g * 255, NORMALIZATION_CLAMP.min, NORMALIZATION_CLAMP.max); // Green channel (Y displacement)
680
- data[pixelIndex + 2] = clampValue(g * 255, NORMALIZATION_CLAMP.min, NORMALIZATION_CLAMP.max); // Blue channel (Y displacement for SVG filter compatibility)
681
- data[pixelIndex + 3] = 255; // Alpha channel
803
+ // Improved normalization to prevent artifacts while maintaining intensity
804
+ maxScale = Math.max(maxScale, MIN_SCALE);
805
+
806
+ // Create ImageData and fill it
807
+ const imageData = this.context.createImageData(w, h);
808
+ const data = imageData.data;
809
+
810
+ // Convert to image data with smoother normalization
811
+ let rawIndex = 0;
812
+ for (let y = 0; y < h; y++) {
813
+ for (let x = 0; x < w; x++) {
814
+ const dx = rawValues[rawIndex++] || 0;
815
+ const dy = rawValues[rawIndex++] || 0;
816
+
817
+ // Smooth the displacement values at edges to prevent hard transitions
818
+ const edgeDistance = Math.min(x, y, w - x - 1, h - y - 1);
819
+ const edgeFactor = Math.min(1, edgeDistance / EDGE_FADE_PIXELS);
820
+
821
+ const smoothedDx = dx * edgeFactor;
822
+ const smoothedDy = dy * edgeFactor;
823
+
824
+ const r = smoothedDx / maxScale + 0.5;
825
+ const g = smoothedDy / maxScale + 0.5;
826
+
827
+ const pixelIndex = (y * w + x) * 4;
828
+ data[pixelIndex] = clampValue(r * 255, NORMALIZATION_CLAMP.min, NORMALIZATION_CLAMP.max); // Red channel (X displacement)
829
+ data[pixelIndex + 1] = clampValue(
830
+ g * 255,
831
+ NORMALIZATION_CLAMP.min,
832
+ NORMALIZATION_CLAMP.max
833
+ ); // Green channel (Y displacement)
834
+ data[pixelIndex + 2] = clampValue(
835
+ g * 255,
836
+ NORMALIZATION_CLAMP.min,
837
+ NORMALIZATION_CLAMP.max
838
+ ); // Blue channel (Y displacement for SVG filter compatibility)
839
+ data[pixelIndex + 3] = 255; // Alpha channel
840
+ }
682
841
  }
683
- }
684
842
 
685
- this.context.putImageData(imageData, 0, 0);
686
- return this.canvas.toDataURL();
843
+ this.context.putImageData(imageData, 0, 0);
844
+ return this.canvas.toDataURL();
845
+ } catch (error) {
846
+ // Graceful fallback on error
847
+ console.warn('ShaderDisplacementGenerator: Error generating shader map, using fallback', error);
848
+ return ''; // Return empty string as fallback
849
+ }
687
850
  }
688
851
 
689
852
  destroy(): void {
690
- this.canvas.remove();
853
+ try {
854
+ this.canvas.remove();
855
+ } catch (e) {
856
+ // Silently handle cleanup errors
857
+ console.warn('ShaderDisplacementGenerator: Error during cleanup', e);
858
+ }
691
859
  }
692
860
 
693
861
  getScale(): number {
694
862
  return this.canvasDPI;
695
863
  }
696
- }
864
+ }