@once-ui-system/core 1.7.10 → 1.7.12

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 (518) hide show
  1. package/ai/catalog.json +1650 -0
  2. package/ai/catalog.seed.json +411 -0
  3. package/ai/components/Accordion.json +136 -0
  4. package/ai/components/AccordionGroup.json +128 -0
  5. package/ai/components/Animation.json +146 -0
  6. package/ai/components/Arrow.json +128 -0
  7. package/ai/components/AutoScroll.json +130 -0
  8. package/ai/components/Avatar.json +133 -0
  9. package/ai/components/AvatarGroup.json +129 -0
  10. package/ai/components/Background.json +131 -0
  11. package/ai/components/Badge.json +132 -0
  12. package/ai/components/Banner.json +122 -0
  13. package/ai/components/BarChart.json +123 -0
  14. package/ai/components/BlobFx.json +125 -0
  15. package/ai/components/BlockQuote.json +132 -0
  16. package/ai/components/Button.json +26 -0
  17. package/ai/components/Card.json +129 -0
  18. package/ai/components/Carousel.json +134 -0
  19. package/ai/components/CelebrationFx.json +132 -0
  20. package/ai/components/ChartHeader.json +130 -0
  21. package/ai/components/ChartStatus.json +12 -0
  22. package/ai/components/Checkbox.json +13 -0
  23. package/ai/components/Chip.json +132 -0
  24. package/ai/components/CodeBlock.json +122 -0
  25. package/ai/components/ColorInput.json +13 -0
  26. package/ai/components/Column.json +128 -0
  27. package/ai/components/CompareImage.json +126 -0
  28. package/ai/components/ContextMenu.json +22 -0
  29. package/ai/components/CountFx.json +82 -0
  30. package/ai/components/CountdownFx.json +11 -0
  31. package/ai/components/Cursor.json +9 -0
  32. package/ai/components/CursorCard.json +128 -0
  33. package/ai/components/DataTooltip.json +15 -0
  34. package/ai/components/DateInput.json +21 -0
  35. package/ai/components/DatePicker.json +143 -0
  36. package/ai/components/DateRangeInput.json +18 -0
  37. package/ai/components/DateRangePicker.json +129 -0
  38. package/ai/components/Dialog.json +134 -0
  39. package/ai/components/DialogProvider.json +8 -0
  40. package/ai/components/Dropdown.json +128 -0
  41. package/ai/components/DropdownWrapper.json +28 -0
  42. package/ai/components/EmojiPicker.json +129 -0
  43. package/ai/components/EmojiPickerDropdown.json +13 -0
  44. package/ai/components/Fade.json +130 -0
  45. package/ai/components/FadingLettersFx.json +77 -0
  46. package/ai/components/Feedback.json +132 -0
  47. package/ai/components/Flex.json +125 -0
  48. package/ai/components/FlipFx.json +133 -0
  49. package/ai/components/GlitchFx.json +128 -0
  50. package/ai/components/Grid.json +121 -0
  51. package/ai/components/Heading.json +75 -0
  52. package/ai/components/HeadingLink.json +127 -0
  53. package/ai/components/HeadingNav.json +124 -0
  54. package/ai/components/HoloFx.json +128 -0
  55. package/ai/components/Hover.json +130 -0
  56. package/ai/components/HoverCard.json +122 -0
  57. package/ai/components/Icon.json +132 -0
  58. package/ai/components/IconButton.json +22 -0
  59. package/ai/components/InfiniteScroll.json +130 -0
  60. package/ai/components/InlineCode.json +126 -0
  61. package/ai/components/Input.json +24 -0
  62. package/ai/components/InteractiveDetails.json +14 -0
  63. package/ai/components/Kbar.json +10 -0
  64. package/ai/components/Kbd.json +127 -0
  65. package/ai/components/Legend.json +14 -0
  66. package/ai/components/LetterFx.json +14 -0
  67. package/ai/components/Line.json +125 -0
  68. package/ai/components/LineBarChart.json +123 -0
  69. package/ai/components/LineChart.json +123 -0
  70. package/ai/components/LinearGauge.json +130 -0
  71. package/ai/components/LinearGradient.json +15 -0
  72. package/ai/components/List.json +127 -0
  73. package/ai/components/ListItem.json +77 -0
  74. package/ai/components/Logo.json +16 -0
  75. package/ai/components/LogoCloud.json +124 -0
  76. package/ai/components/Mask.json +131 -0
  77. package/ai/components/MasonryGrid.json +132 -0
  78. package/ai/components/MatrixFx.json +135 -0
  79. package/ai/components/Media.json +142 -0
  80. package/ai/components/MediaUpload.json +122 -0
  81. package/ai/components/MegaMenu.json +125 -0
  82. package/ai/components/MobileMegaMenu.json +125 -0
  83. package/ai/components/Modal.json +12 -0
  84. package/ai/components/NavIcon.json +127 -0
  85. package/ai/components/NumberInput.json +16 -0
  86. package/ai/components/OTPInput.json +13 -0
  87. package/ai/components/OgCard.json +135 -0
  88. package/ai/components/Option.json +137 -0
  89. package/ai/components/Particle.json +135 -0
  90. package/ai/components/PasswordInput.json +8 -0
  91. package/ai/components/PieChart.json +123 -0
  92. package/ai/components/ProgressBar.json +131 -0
  93. package/ai/components/Pulse.json +129 -0
  94. package/ai/components/RadialGauge.json +134 -0
  95. package/ai/components/RadialGradient.json +16 -0
  96. package/ai/components/RadioButton.json +17 -0
  97. package/ai/components/RevealFx.json +131 -0
  98. package/ai/components/Row.json +128 -0
  99. package/ai/components/Schema.json +17 -0
  100. package/ai/components/ScrollContainer.json +126 -0
  101. package/ai/components/ScrollToTop.json +124 -0
  102. package/ai/components/Scroller.json +128 -0
  103. package/ai/components/SegmentedControl.json +132 -0
  104. package/ai/components/Select.json +21 -0
  105. package/ai/components/ShineFx.json +80 -0
  106. package/ai/components/Skeleton.json +129 -0
  107. package/ai/components/Slider.json +17 -0
  108. package/ai/components/SmartLink.json +17 -0
  109. package/ai/components/Spinner.json +127 -0
  110. package/ai/components/SplitView.json +129 -0
  111. package/ai/components/StatusIndicator.json +128 -0
  112. package/ai/components/StyleOverlay.json +126 -0
  113. package/ai/components/StylePanel.json +125 -0
  114. package/ai/components/Swiper.json +130 -0
  115. package/ai/components/Switch.json +20 -0
  116. package/ai/components/Table.json +125 -0
  117. package/ai/components/Tag.json +129 -0
  118. package/ai/components/TagInput.json +12 -0
  119. package/ai/components/Text.json +72 -0
  120. package/ai/components/Textarea.json +24 -0
  121. package/ai/components/ThemeSwitcher.json +122 -0
  122. package/ai/components/TiltFx.json +126 -0
  123. package/ai/components/Timeline.json +126 -0
  124. package/ai/components/Toast.json +13 -0
  125. package/ai/components/Toaster.json +14 -0
  126. package/ai/components/ToggleButton.json +24 -0
  127. package/ai/components/Tooltip.json +129 -0
  128. package/ai/components/TypeFx.json +15 -0
  129. package/ai/components/User.json +15 -0
  130. package/ai/components/UserMenu.json +16 -0
  131. package/ai/components/WeatherFx.json +133 -0
  132. package/ai/examples/auth.tsx +105 -0
  133. package/ai/examples/blocks/Background6.tsx +25 -0
  134. package/ai/examples/blocks/Chat1.tsx +70 -0
  135. package/ai/examples/blocks/Dashboard1.tsx +404 -0
  136. package/ai/examples/blocks/Feed3.tsx +151 -0
  137. package/ai/examples/blocks/Footer1.tsx +30 -0
  138. package/ai/examples/blocks/Footer2.tsx +96 -0
  139. package/ai/examples/blocks/Footer3.tsx +127 -0
  140. package/ai/examples/blocks/Footer4.tsx +406 -0
  141. package/ai/examples/blocks/Header1.tsx +370 -0
  142. package/ai/examples/blocks/Header2.tsx +98 -0
  143. package/ai/examples/blocks/Header3.tsx +133 -0
  144. package/ai/examples/blocks/Header4.tsx +123 -0
  145. package/ai/examples/blocks/Productivity1.tsx +318 -0
  146. package/ai/examples/blocks/Roadmap1.tsx +133 -0
  147. package/ai/examples/blocks/Sidebar1.tsx +34 -0
  148. package/ai/examples/blocks/Sidebar2.tsx +17 -0
  149. package/ai/examples/blocks/Sidebar4.tsx +92 -0
  150. package/ai/examples/blocks/Sidebar5.tsx +73 -0
  151. package/ai/examples/blocks/Streaming1.tsx +353 -0
  152. package/ai/examples/blocks/Table1.tsx +303 -0
  153. package/ai/examples/blocks/Waitlist1.tsx +141 -0
  154. package/ai/examples/blocks/content/chat1content.ts +65 -0
  155. package/ai/examples/blocks/content/resources.ts +5 -0
  156. package/ai/examples/blocks/content/roadmap1content.ts +51 -0
  157. package/ai/examples/blocks/manifest.json +191 -0
  158. package/ai/examples/dashboard.tsx +150 -0
  159. package/ai/examples/marketing-hero.tsx +152 -0
  160. package/ai/examples/pricing.tsx +219 -0
  161. package/ai/examples/settings.tsx +140 -0
  162. package/ai/gotchas.json +85 -0
  163. package/ai/manifest.json +21 -0
  164. package/ai/rules.compact.md +73 -0
  165. package/ai/spec.json +2679 -2701
  166. package/ai/tasks/auth.json +23 -0
  167. package/ai/tasks/chat.json +28 -0
  168. package/ai/tasks/dashboard.json +39 -0
  169. package/ai/tasks/index.json +12 -0
  170. package/ai/tasks/marketing-hero.json +38 -0
  171. package/ai/tasks/pricing.json +42 -0
  172. package/ai/tasks/roadmap.json +22 -0
  173. package/ai/tasks/settings.json +34 -0
  174. package/dist/ai/catalog.json +1650 -0
  175. package/dist/ai/catalog.seed.json +411 -0
  176. package/dist/ai/components/Accordion.json +136 -0
  177. package/dist/ai/components/AccordionGroup.json +128 -0
  178. package/dist/ai/components/Animation.json +146 -0
  179. package/dist/ai/components/Arrow.json +128 -0
  180. package/dist/ai/components/AutoScroll.json +130 -0
  181. package/dist/ai/components/Avatar.json +133 -0
  182. package/dist/ai/components/AvatarGroup.json +129 -0
  183. package/dist/ai/components/Background.json +131 -0
  184. package/dist/ai/components/Badge.json +132 -0
  185. package/dist/ai/components/Banner.json +122 -0
  186. package/dist/ai/components/BarChart.json +123 -0
  187. package/dist/ai/components/BlobFx.json +125 -0
  188. package/dist/ai/components/BlockQuote.json +132 -0
  189. package/dist/ai/components/Button.json +26 -0
  190. package/dist/ai/components/Card.json +129 -0
  191. package/dist/ai/components/Carousel.json +134 -0
  192. package/dist/ai/components/CelebrationFx.json +132 -0
  193. package/dist/ai/components/ChartHeader.json +130 -0
  194. package/dist/ai/components/ChartStatus.json +12 -0
  195. package/dist/ai/components/Checkbox.json +13 -0
  196. package/dist/ai/components/Chip.json +132 -0
  197. package/dist/ai/components/CodeBlock.json +122 -0
  198. package/dist/ai/components/ColorInput.json +13 -0
  199. package/dist/ai/components/Column.json +128 -0
  200. package/dist/ai/components/CompareImage.json +126 -0
  201. package/dist/ai/components/ContextMenu.json +22 -0
  202. package/dist/ai/components/CountFx.json +82 -0
  203. package/dist/ai/components/CountdownFx.json +11 -0
  204. package/dist/ai/components/Cursor.json +9 -0
  205. package/dist/ai/components/CursorCard.json +128 -0
  206. package/dist/ai/components/DataTooltip.json +15 -0
  207. package/dist/ai/components/DateInput.json +21 -0
  208. package/dist/ai/components/DatePicker.json +143 -0
  209. package/dist/ai/components/DateRangeInput.json +18 -0
  210. package/dist/ai/components/DateRangePicker.json +129 -0
  211. package/dist/ai/components/Dialog.json +134 -0
  212. package/dist/ai/components/DialogProvider.json +8 -0
  213. package/dist/ai/components/Dropdown.json +128 -0
  214. package/dist/ai/components/DropdownWrapper.json +28 -0
  215. package/dist/ai/components/EmojiPicker.json +129 -0
  216. package/dist/ai/components/EmojiPickerDropdown.json +13 -0
  217. package/dist/ai/components/Fade.json +130 -0
  218. package/dist/ai/components/FadingLettersFx.json +77 -0
  219. package/dist/ai/components/Feedback.json +132 -0
  220. package/dist/ai/components/Flex.json +125 -0
  221. package/dist/ai/components/FlipFx.json +133 -0
  222. package/dist/ai/components/GlitchFx.json +128 -0
  223. package/dist/ai/components/Grid.json +121 -0
  224. package/dist/ai/components/Heading.json +75 -0
  225. package/dist/ai/components/HeadingLink.json +127 -0
  226. package/dist/ai/components/HeadingNav.json +124 -0
  227. package/dist/ai/components/HoloFx.json +128 -0
  228. package/dist/ai/components/Hover.json +130 -0
  229. package/dist/ai/components/HoverCard.json +122 -0
  230. package/dist/ai/components/Icon.json +132 -0
  231. package/dist/ai/components/IconButton.json +22 -0
  232. package/dist/ai/components/InfiniteScroll.json +130 -0
  233. package/dist/ai/components/InlineCode.json +126 -0
  234. package/dist/ai/components/Input.json +24 -0
  235. package/dist/ai/components/InteractiveDetails.json +14 -0
  236. package/dist/ai/components/Kbar.json +10 -0
  237. package/dist/ai/components/Kbd.json +127 -0
  238. package/dist/ai/components/Legend.json +14 -0
  239. package/dist/ai/components/LetterFx.json +14 -0
  240. package/dist/ai/components/Line.json +125 -0
  241. package/dist/ai/components/LineBarChart.json +123 -0
  242. package/dist/ai/components/LineChart.json +123 -0
  243. package/dist/ai/components/LinearGauge.json +130 -0
  244. package/dist/ai/components/LinearGradient.json +15 -0
  245. package/dist/ai/components/List.json +127 -0
  246. package/dist/ai/components/ListItem.json +77 -0
  247. package/dist/ai/components/Logo.json +16 -0
  248. package/dist/ai/components/LogoCloud.json +124 -0
  249. package/dist/ai/components/Mask.json +131 -0
  250. package/dist/ai/components/MasonryGrid.json +132 -0
  251. package/dist/ai/components/MatrixFx.json +135 -0
  252. package/dist/ai/components/Media.json +142 -0
  253. package/dist/ai/components/MediaUpload.json +122 -0
  254. package/dist/ai/components/MegaMenu.json +125 -0
  255. package/dist/ai/components/MobileMegaMenu.json +125 -0
  256. package/dist/ai/components/Modal.json +12 -0
  257. package/dist/ai/components/NavIcon.json +127 -0
  258. package/dist/ai/components/NumberInput.json +16 -0
  259. package/dist/ai/components/OTPInput.json +13 -0
  260. package/dist/ai/components/OgCard.json +135 -0
  261. package/dist/ai/components/Option.json +137 -0
  262. package/dist/ai/components/Particle.json +135 -0
  263. package/dist/ai/components/PasswordInput.json +8 -0
  264. package/dist/ai/components/PieChart.json +123 -0
  265. package/dist/ai/components/ProgressBar.json +131 -0
  266. package/dist/ai/components/Pulse.json +129 -0
  267. package/dist/ai/components/RadialGauge.json +134 -0
  268. package/dist/ai/components/RadialGradient.json +16 -0
  269. package/dist/ai/components/RadioButton.json +17 -0
  270. package/dist/ai/components/RevealFx.json +131 -0
  271. package/dist/ai/components/Row.json +128 -0
  272. package/dist/ai/components/Schema.json +17 -0
  273. package/dist/ai/components/ScrollContainer.json +126 -0
  274. package/dist/ai/components/ScrollToTop.json +124 -0
  275. package/dist/ai/components/Scroller.json +128 -0
  276. package/dist/ai/components/SegmentedControl.json +132 -0
  277. package/dist/ai/components/Select.json +21 -0
  278. package/dist/ai/components/ShineFx.json +80 -0
  279. package/dist/ai/components/Skeleton.json +129 -0
  280. package/dist/ai/components/Slider.json +17 -0
  281. package/dist/ai/components/SmartLink.json +17 -0
  282. package/dist/ai/components/Spinner.json +127 -0
  283. package/dist/ai/components/SplitView.json +129 -0
  284. package/dist/ai/components/StatusIndicator.json +128 -0
  285. package/dist/ai/components/StyleOverlay.json +126 -0
  286. package/dist/ai/components/StylePanel.json +125 -0
  287. package/dist/ai/components/Swiper.json +130 -0
  288. package/dist/ai/components/Switch.json +20 -0
  289. package/dist/ai/components/Table.json +125 -0
  290. package/dist/ai/components/Tag.json +129 -0
  291. package/dist/ai/components/TagInput.json +12 -0
  292. package/dist/ai/components/Text.json +72 -0
  293. package/dist/ai/components/Textarea.json +24 -0
  294. package/dist/ai/components/ThemeSwitcher.json +122 -0
  295. package/dist/ai/components/TiltFx.json +126 -0
  296. package/dist/ai/components/Timeline.json +126 -0
  297. package/dist/ai/components/Toast.json +13 -0
  298. package/dist/ai/components/Toaster.json +14 -0
  299. package/dist/ai/components/ToggleButton.json +24 -0
  300. package/dist/ai/components/Tooltip.json +129 -0
  301. package/dist/ai/components/TypeFx.json +15 -0
  302. package/dist/ai/components/User.json +15 -0
  303. package/dist/ai/components/UserMenu.json +16 -0
  304. package/dist/ai/components/WeatherFx.json +133 -0
  305. package/dist/ai/eval-tasks.md +71 -0
  306. package/dist/ai/examples/blocks/manifest.json +191 -0
  307. package/dist/ai/gotchas.json +85 -0
  308. package/dist/ai/manifest.json +21 -0
  309. package/dist/ai/recipes.md +149 -0
  310. package/dist/ai/rules.compact.md +73 -0
  311. package/dist/ai/rules.md +217 -0
  312. package/dist/ai/spec.json +2680 -0
  313. package/dist/ai/tasks/auth.json +23 -0
  314. package/dist/ai/tasks/chat.json +28 -0
  315. package/dist/ai/tasks/dashboard.json +39 -0
  316. package/dist/ai/tasks/index.json +12 -0
  317. package/dist/ai/tasks/marketing-hero.json +38 -0
  318. package/dist/ai/tasks/pricing.json +42 -0
  319. package/dist/ai/tasks/roadmap.json +22 -0
  320. package/dist/ai/tasks/settings.json +34 -0
  321. package/dist/components/Accordion.d.ts +2 -1
  322. package/dist/components/Accordion.d.ts.map +1 -1
  323. package/dist/components/Accordion.js.map +1 -1
  324. package/dist/components/Accordion.module.scss +1 -0
  325. package/dist/components/AccordionGroup.d.ts +2 -1
  326. package/dist/components/AccordionGroup.d.ts.map +1 -1
  327. package/dist/components/AccordionGroup.js +17 -1
  328. package/dist/components/AccordionGroup.js.map +1 -1
  329. package/dist/components/Avatar.d.ts +2 -1
  330. package/dist/components/Avatar.d.ts.map +1 -1
  331. package/dist/components/Avatar.js.map +1 -1
  332. package/dist/components/AvatarGroup.d.ts +2 -1
  333. package/dist/components/AvatarGroup.d.ts.map +1 -1
  334. package/dist/components/AvatarGroup.js.map +1 -1
  335. package/dist/components/Button.d.ts +7 -5
  336. package/dist/components/Button.d.ts.map +1 -1
  337. package/dist/components/Button.js +3 -3
  338. package/dist/components/Button.js.map +1 -1
  339. package/dist/components/Button.module.scss +233 -111
  340. package/dist/components/Carousel.d.ts.map +1 -1
  341. package/dist/components/Carousel.js +14 -3
  342. package/dist/components/Carousel.js.map +1 -1
  343. package/dist/components/ColorInput.d.ts.map +1 -1
  344. package/dist/components/ColorInput.js +11 -11
  345. package/dist/components/ColorInput.js.map +1 -1
  346. package/dist/components/CompareImage.d.ts.map +1 -1
  347. package/dist/components/CompareImage.js +24 -23
  348. package/dist/components/CompareImage.js.map +1 -1
  349. package/dist/components/CompareImage.module.scss +0 -2
  350. package/dist/components/CursorCard.d.ts.map +1 -1
  351. package/dist/components/CursorCard.js.map +1 -1
  352. package/dist/components/CursorCard.module.scss +1 -1
  353. package/dist/components/DatePicker.d.ts +2 -1
  354. package/dist/components/DatePicker.d.ts.map +1 -1
  355. package/dist/components/DatePicker.js +11 -15
  356. package/dist/components/DatePicker.js.map +1 -1
  357. package/dist/components/DateRangePicker.d.ts +2 -1
  358. package/dist/components/DateRangePicker.d.ts.map +1 -1
  359. package/dist/components/DateRangePicker.js.map +1 -1
  360. package/dist/components/DropdownWrapper.d.ts.map +1 -1
  361. package/dist/components/DropdownWrapper.js +9 -9
  362. package/dist/components/DropdownWrapper.js.map +1 -1
  363. package/dist/components/ElementType.d.ts +5 -6
  364. package/dist/components/ElementType.d.ts.map +1 -1
  365. package/dist/components/ElementType.js.map +1 -1
  366. package/dist/components/EmojiPicker.d.ts +2 -2
  367. package/dist/components/EmojiPickerDropdown.d.ts +2 -2
  368. package/dist/components/Feedback.d.ts.map +1 -1
  369. package/dist/components/Feedback.js +1 -1
  370. package/dist/components/Feedback.js.map +1 -1
  371. package/dist/components/Heading.d.ts +3 -3
  372. package/dist/components/Heading.d.ts.map +1 -1
  373. package/dist/components/Heading.js +6 -3
  374. package/dist/components/Heading.js.map +1 -1
  375. package/dist/components/Icon.d.ts +2 -2
  376. package/dist/components/Icon.d.ts.map +1 -1
  377. package/dist/components/IconButton.d.ts +7 -5
  378. package/dist/components/IconButton.d.ts.map +1 -1
  379. package/dist/components/IconButton.js +2 -2
  380. package/dist/components/IconButton.js.map +1 -1
  381. package/dist/components/Input.d.ts +2 -1
  382. package/dist/components/Input.d.ts.map +1 -1
  383. package/dist/components/Input.js.map +1 -1
  384. package/dist/components/Input.module.scss +1 -196
  385. package/dist/components/Logo.d.ts +2 -1
  386. package/dist/components/Logo.d.ts.map +1 -1
  387. package/dist/components/MatrixFx.d.ts.map +1 -1
  388. package/dist/components/MatrixFx.js +521 -481
  389. package/dist/components/MatrixFx.js.map +1 -1
  390. package/dist/components/Media.d.ts.map +1 -1
  391. package/dist/components/Media.js +5 -1
  392. package/dist/components/Media.js.map +1 -1
  393. package/dist/components/ProgressBar.d.ts +1 -0
  394. package/dist/components/ProgressBar.d.ts.map +1 -1
  395. package/dist/components/ProgressBar.js +9 -3
  396. package/dist/components/ProgressBar.js.map +1 -1
  397. package/dist/components/Pulse.d.ts +1 -0
  398. package/dist/components/Pulse.d.ts.map +1 -1
  399. package/dist/components/Pulse.js +2 -2
  400. package/dist/components/Pulse.js.map +1 -1
  401. package/dist/components/Scroller.js +1 -1
  402. package/dist/components/Scroller.js.map +1 -1
  403. package/dist/components/ServerFlex.d.ts +2 -2
  404. package/dist/components/ServerFlex.d.ts.map +1 -1
  405. package/dist/components/ServerFlex.js +4 -1
  406. package/dist/components/ServerFlex.js.map +1 -1
  407. package/dist/components/ServerGrid.d.ts +2 -2
  408. package/dist/components/ServerGrid.d.ts.map +1 -1
  409. package/dist/components/SmartLink.d.ts +5 -4
  410. package/dist/components/SmartLink.d.ts.map +1 -1
  411. package/dist/components/SmartLink.js.map +1 -1
  412. package/dist/components/Spinner.d.ts +2 -1
  413. package/dist/components/Spinner.d.ts.map +1 -1
  414. package/dist/components/Spinner.js.map +1 -1
  415. package/dist/components/SplitView.d.ts +1 -1
  416. package/dist/components/SplitView.d.ts.map +1 -1
  417. package/dist/components/SplitView.js +1 -1
  418. package/dist/components/SplitView.js.map +1 -1
  419. package/dist/components/StatusIndicator.d.ts +2 -1
  420. package/dist/components/StatusIndicator.d.ts.map +1 -1
  421. package/dist/components/StatusIndicator.js.map +1 -1
  422. package/dist/components/Swiper.d.ts.map +1 -1
  423. package/dist/components/Swiper.js +3 -3
  424. package/dist/components/Swiper.js.map +1 -1
  425. package/dist/components/Tag.d.ts +2 -2
  426. package/dist/components/Tag.d.ts.map +1 -1
  427. package/dist/components/Text.d.ts +3 -3
  428. package/dist/components/Text.d.ts.map +1 -1
  429. package/dist/components/Text.js +6 -3
  430. package/dist/components/Text.js.map +1 -1
  431. package/dist/components/Textarea.d.ts +2 -1
  432. package/dist/components/Textarea.d.ts.map +1 -1
  433. package/dist/components/Textarea.js.map +1 -1
  434. package/dist/components/Toast.d.ts +1 -1
  435. package/dist/components/Toast.d.ts.map +1 -1
  436. package/dist/components/Toast.js +2 -0
  437. package/dist/components/Toast.js.map +1 -1
  438. package/dist/components/Toaster.d.ts +1 -1
  439. package/dist/components/Toaster.d.ts.map +1 -1
  440. package/dist/components/ToggleButton.d.ts +6 -5
  441. package/dist/components/ToggleButton.d.ts.map +1 -1
  442. package/dist/components/ToggleButton.js.map +1 -1
  443. package/dist/components/ToggleButton.module.scss +19 -0
  444. package/dist/components/index.d.ts +0 -3
  445. package/dist/components/index.d.ts.map +1 -1
  446. package/dist/components/index.js +0 -3
  447. package/dist/components/index.js.map +1 -1
  448. package/dist/contexts/ToastProvider.d.ts +1 -1
  449. package/dist/contexts/ToastProvider.d.ts.map +1 -1
  450. package/dist/css/tokens.css +1 -1
  451. package/dist/css/tokens.css.map +1 -1
  452. package/dist/interfaces.d.ts +9 -10
  453. package/dist/interfaces.d.ts.map +1 -1
  454. package/dist/modules/code/CodeBlock.impl.d.ts +2 -0
  455. package/dist/modules/code/CodeBlock.impl.d.ts.map +1 -1
  456. package/dist/modules/code/CodeBlock.impl.js +48 -16
  457. package/dist/modules/code/CodeBlock.impl.js.map +1 -1
  458. package/dist/modules/code/index.d.ts +3 -0
  459. package/dist/modules/code/index.d.ts.map +1 -0
  460. package/dist/modules/code/index.js +2 -0
  461. package/dist/modules/code/index.js.map +1 -0
  462. package/dist/modules/data/BarChart.impl.d.ts +2 -2
  463. package/dist/modules/data/DataTooltip.d.ts +1 -1
  464. package/dist/modules/data/DataTooltip.d.ts.map +1 -1
  465. package/dist/modules/data/DataTooltip.js.map +1 -1
  466. package/dist/modules/data/Legend.d.ts +1 -1
  467. package/dist/modules/data/Legend.d.ts.map +1 -1
  468. package/dist/modules/data/Legend.js.map +1 -1
  469. package/dist/modules/data/LineBarChart.impl.d.ts +2 -2
  470. package/dist/modules/data/LinearGauge.d.ts +1 -2
  471. package/dist/modules/data/LinearGauge.d.ts.map +1 -1
  472. package/dist/modules/data/RadialGauge.d.ts +1 -2
  473. package/dist/modules/data/RadialGauge.d.ts.map +1 -1
  474. package/dist/modules/data/interfaces.d.ts +4 -4
  475. package/dist/modules/index.d.ts +3 -4
  476. package/dist/modules/index.d.ts.map +1 -1
  477. package/dist/modules/index.js +3 -4
  478. package/dist/modules/index.js.map +1 -1
  479. package/dist/modules/media/index.d.ts +3 -0
  480. package/dist/modules/media/index.d.ts.map +1 -0
  481. package/dist/modules/media/index.js +2 -0
  482. package/dist/modules/media/index.js.map +1 -0
  483. package/dist/modules/navigation/HeadingLink.d.ts +1 -2
  484. package/dist/modules/navigation/HeadingLink.d.ts.map +1 -1
  485. package/dist/modules/navigation/HeadingNav.d.ts +2 -2
  486. package/dist/modules/navigation/HeadingNav.d.ts.map +1 -1
  487. package/dist/modules/navigation/HeadingNav.js.map +1 -1
  488. package/dist/modules/navigation/Kbar.d.ts +6 -5
  489. package/dist/modules/navigation/Kbar.d.ts.map +1 -1
  490. package/dist/modules/navigation/Kbar.js +20 -12
  491. package/dist/modules/navigation/Kbar.js.map +1 -1
  492. package/dist/modules/navigation/MobileMegaMenu.d.ts +4 -4
  493. package/dist/modules/navigation/MobileMegaMenu.d.ts.map +1 -1
  494. package/dist/modules/navigation/index.d.ts +2 -2
  495. package/dist/modules/navigation/index.d.ts.map +1 -1
  496. package/dist/modules/navigation/index.js +2 -2
  497. package/dist/modules/navigation/index.js.map +1 -1
  498. package/dist/modules/seo/index.d.ts +5 -0
  499. package/dist/modules/seo/index.d.ts.map +1 -0
  500. package/dist/modules/seo/index.js +3 -0
  501. package/dist/modules/seo/index.js.map +1 -0
  502. package/dist/package.json +13 -2
  503. package/dist/tokens/theme.scss +20 -2
  504. package/dist/types.d.ts +3 -3
  505. package/dist/types.d.ts.map +1 -1
  506. package/dist/types.js +5 -5
  507. package/dist/types.js.map +1 -1
  508. package/dist/utils/dropdownState.d.ts +4 -0
  509. package/dist/utils/dropdownState.d.ts.map +1 -0
  510. package/dist/utils/dropdownState.js +9 -0
  511. package/dist/utils/dropdownState.js.map +1 -0
  512. package/dist/utils/index.d.ts +1 -0
  513. package/dist/utils/index.d.ts.map +1 -1
  514. package/dist/utils/index.js +1 -0
  515. package/dist/utils/index.js.map +1 -1
  516. package/package.json +139 -127
  517. package/LICENSE.md +0 -21
  518. package/next-env.d.ts +0 -5
@@ -58,368 +58,216 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
58
58
  const ctx = canvas.getContext("2d", { willReadFrequently: true });
59
59
  if (!ctx)
60
60
  return;
61
- // Set canvas size
61
+ // Set canvas size — skip until the container has layout (0×0 crashes getImageData)
62
62
  let canvasWidth = 0;
63
63
  let canvasHeight = 0;
64
- const updateSize = () => {
64
+ let initialized = false;
65
+ let disposed = false;
66
+ const applySize = () => {
65
67
  const rect = container.getBoundingClientRect();
68
+ if (rect.width <= 0 || rect.height <= 0) {
69
+ return false;
70
+ }
66
71
  canvasWidth = rect.width;
67
72
  canvasHeight = rect.height;
68
- canvas.width = rect.width * 2; // 2x for retina
69
- canvas.height = rect.height * 2;
73
+ canvas.width = Math.floor(rect.width * 2);
74
+ canvas.height = Math.floor(rect.height * 2);
70
75
  canvas.style.width = `${rect.width}px`;
71
76
  canvas.style.height = `${rect.height}px`;
72
- ctx.scale(2, 2); // Scale for retina
77
+ ctx.setTransform(1, 0, 0, 1, 0, 0);
78
+ ctx.scale(2, 2);
79
+ return true;
73
80
  };
74
- updateSize();
75
- window.addEventListener("resize", updateSize);
76
- // Parse colors - convert token names to CSS variables
77
- const parsedColors = colors.map((color) => {
78
- // Get computed value from CSS variable
79
- const computedColor = getComputedStyle(container).getPropertyValue(`--${color}`);
80
- return computedColor || color;
81
- });
82
- // Create dot grid with padding to prevent edge gaps during displacement
83
- const totalSize = size + spacing;
84
- const maxDisplacement = (bulge?.intensity ?? 10) * 2; // Account for max possible displacement
85
- const paddedWidth = canvasWidth + maxDisplacement * 2;
86
- const paddedHeight = canvasHeight + maxDisplacement * 2;
87
- const cols = Math.ceil(paddedWidth / totalSize);
88
- const rows = Math.ceil(paddedHeight / totalSize);
89
- // Only create new dots if grid doesn't exist or dimensions/size changed
90
- let dots = dotsRef.current;
91
- let maxDistance = 0;
92
- if (dots.length === 0 || dots[0]?.gridSize !== totalSize || dots[0]?.canvasW !== canvasWidth || dots[0]?.canvasH !== canvasHeight) {
93
- // Create new dot grid
94
- dots = [];
95
- for (let row = 0; row < rows; row++) {
96
- for (let col = 0; col < cols; col++) {
97
- const x = col * totalSize + size / 2 - maxDisplacement;
98
- const y = row * totalSize + size / 2 - maxDisplacement;
99
- // Calculate distance from reveal origin
100
- let distanceFromOrigin = 0;
101
- const centerX = canvasWidth / 2;
102
- const centerY = canvasHeight / 2;
103
- switch (revealFrom) {
104
- case "center":
105
- const dx = x - centerX;
106
- const dy = y - centerY;
107
- distanceFromOrigin = Math.sqrt(dx * dx + dy * dy);
108
- break;
109
- case "top":
110
- distanceFromOrigin = y;
111
- break;
112
- case "bottom":
113
- distanceFromOrigin = canvasHeight - y;
114
- break;
115
- case "left":
116
- distanceFromOrigin = x;
117
- break;
118
- case "right":
119
- distanceFromOrigin = canvasWidth - x;
120
- break;
121
- }
122
- dots.push({
123
- x,
124
- y,
125
- gridX: col,
126
- gridY: row,
127
- color: parsedColors[Math.floor(Math.random() * parsedColors.length)],
128
- baseOpacity: 0.3 + Math.random() * 0.7,
129
- distanceFromOrigin,
130
- randomOffset: Math.random() * 0.3,
131
- flickerPhase: Math.random() * Math.PI * 2,
132
- flickerSpeed: 0.8 + Math.random() * 0.4,
133
- gridSize: totalSize,
134
- canvasW: canvasWidth,
135
- canvasH: canvasHeight,
136
- });
137
- }
138
- }
139
- // Find max distance for normalization
140
- maxDistance = Math.max(...dots.map((d) => d.distanceFromOrigin));
141
- dotsRef.current = dots;
142
- }
143
- else {
144
- // Update colors on existing dots
145
- dots.forEach((dot) => {
146
- dot.color = parsedColors[Math.floor(Math.random() * parsedColors.length)];
147
- });
148
- maxDistance = Math.max(...dots.map((d) => d.distanceFromOrigin));
149
- }
150
- // Animation loop
151
- const startTime = Date.now();
152
- const frameInterval = 1000 / fps; // Target ms per frame
153
- // Bulge configuration
154
- const bulgeEnabled = !!bulge;
155
- const bulgeType = bulge?.type ?? "ripple"; // Default: ripple
156
- const bulgeDuration = bulge?.duration ?? 3; // Default: 3 seconds per wave
157
- const bulgeIntensity = bulge?.intensity ?? 10; // Default: 10px displacement
158
- const bulgeRepeat = bulge?.repeat ?? true; // Default: true
159
- const bulgeDelay = bulge?.delay ?? 0; // Default: 0ms
160
- // Calculate center point for circular wave
161
- const centerX = canvasWidth / 2;
162
- const centerY = canvasHeight / 2;
163
- const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);
164
- // Cache for static rendering (when animation completes)
165
- let cachedImageData = null;
166
- // When reduced motion is active, force static rendering after first frame
167
- if (!shouldAnimate) {
168
- isStaticRef.current = true;
169
- mountAnimationCompleteRef.current = true;
170
- }
171
- const animate = (currentTime = 0) => {
172
- // Skip rendering if not visible in viewport
173
- if (!isVisibleRef.current) {
174
- animationRef.current = requestAnimationFrame(animate);
81
+ const handleResize = () => {
82
+ const prevW = canvasWidth;
83
+ const prevH = canvasHeight;
84
+ if (!applySize())
175
85
  return;
86
+ if (initialized && (prevW !== canvasWidth || prevH !== canvasHeight)) {
87
+ dotsRef.current = [];
88
+ isStaticRef.current = false;
176
89
  }
177
- // FPS throttling
178
- if (currentTime - lastFrameTimeRef.current < frameInterval) {
179
- animationRef.current = requestAnimationFrame(animate);
90
+ };
91
+ window.addEventListener("resize", handleResize);
92
+ const setup = () => {
93
+ if (disposed || initialized)
180
94
  return;
181
- }
182
- lastFrameTimeRef.current = currentTime;
183
- // If static and we have cached image, just render that and stop animating
184
- if (isStaticRef.current && cachedImageData) {
185
- ctx.putImageData(cachedImageData, 0, 0);
186
- // Don't request another frame - animation is complete
95
+ if (!applySize())
187
96
  return;
188
- }
189
- ctx.clearRect(0, 0, canvasWidth, canvasHeight);
190
- const time = (Date.now() - startTime) / 1000; // Time in seconds
191
- // Calculate circular wave radius (travels from center to edge)
192
- let waveProgress = 0;
193
- let showBulge = false;
194
- let bulgeFadeOut = 1; // Multiplier for fading out bulge effect (1 = full effect, 0 = no effect)
195
- if (bulgeEnabled) {
196
- const bulgeElapsed = (Date.now() - bulgeStartTimeRef.current) / 1000;
197
- const delaySeconds = bulgeDelay / 1000;
198
- const totalCycleDuration = bulgeDuration + delaySeconds;
199
- const adjustedTime = bulgeElapsed - delaySeconds;
200
- const fadeStartPercent = 0.6; // Start fading at 60% of wave duration
201
- if (adjustedTime >= 0) {
202
- showBulge = true;
203
- if (bulgeRepeat) {
204
- // Continuous repeating wave
205
- const cycleTime = adjustedTime % totalCycleDuration;
206
- waveProgress = cycleTime < bulgeDuration ? cycleTime / bulgeDuration : 0;
207
- showBulge = cycleTime < bulgeDuration; // Only show during active wave, not delay
208
- }
209
- else {
210
- // Single wave with opacity fade during last 40%
211
- if (adjustedTime <= bulgeDuration) {
212
- waveProgress = adjustedTime / bulgeDuration;
213
- // Start fading opacity at 60% of duration
214
- if (waveProgress >= fadeStartPercent) {
215
- const fadeProgress = (waveProgress - fadeStartPercent) / (1 - fadeStartPercent);
216
- bulgeFadeOut = 1 - fadeProgress; // Fade from 1 to 0
97
+ initialized = true;
98
+ // Parse colors - convert token names to CSS variables
99
+ const parsedColors = colors.map((color) => {
100
+ const computedColor = getComputedStyle(container).getPropertyValue(`--${color}`);
101
+ return computedColor || color;
102
+ });
103
+ // Create dot grid with padding to prevent edge gaps during displacement
104
+ const totalSize = size + spacing;
105
+ const maxDisplacement = (bulge?.intensity ?? 10) * 2;
106
+ const paddedWidth = canvasWidth + maxDisplacement * 2;
107
+ const paddedHeight = canvasHeight + maxDisplacement * 2;
108
+ const cols = Math.ceil(paddedWidth / totalSize);
109
+ const rows = Math.ceil(paddedHeight / totalSize);
110
+ let dots = dotsRef.current;
111
+ let maxDistance = 0;
112
+ if (dots.length === 0 || dots[0]?.gridSize !== totalSize || dots[0]?.canvasW !== canvasWidth || dots[0]?.canvasH !== canvasHeight) {
113
+ dots = [];
114
+ for (let row = 0; row < rows; row++) {
115
+ for (let col = 0; col < cols; col++) {
116
+ const x = col * totalSize + size / 2 - maxDisplacement;
117
+ const y = row * totalSize + size / 2 - maxDisplacement;
118
+ let distanceFromOrigin = 0;
119
+ const centerX = canvasWidth / 2;
120
+ const centerY = canvasHeight / 2;
121
+ switch (revealFrom) {
122
+ case "center": {
123
+ const dx = x - centerX;
124
+ const dy = y - centerY;
125
+ distanceFromOrigin = Math.sqrt(dx * dx + dy * dy);
126
+ break;
217
127
  }
128
+ case "top":
129
+ distanceFromOrigin = y;
130
+ break;
131
+ case "bottom":
132
+ distanceFromOrigin = canvasHeight - y;
133
+ break;
134
+ case "left":
135
+ distanceFromOrigin = x;
136
+ break;
137
+ case "right":
138
+ distanceFromOrigin = canvasWidth - x;
139
+ break;
218
140
  }
219
- else {
220
- waveProgress = 0;
221
- showBulge = false;
222
- }
141
+ dots.push({
142
+ x,
143
+ y,
144
+ gridX: col,
145
+ gridY: row,
146
+ color: parsedColors[Math.floor(Math.random() * parsedColors.length)],
147
+ baseOpacity: 0.3 + Math.random() * 0.7,
148
+ distanceFromOrigin,
149
+ randomOffset: Math.random() * 0.3,
150
+ flickerPhase: Math.random() * Math.PI * 2,
151
+ flickerSpeed: 0.8 + Math.random() * 0.4,
152
+ gridSize: totalSize,
153
+ canvasW: canvasWidth,
154
+ canvasH: canvasHeight,
155
+ });
223
156
  }
224
157
  }
158
+ maxDistance = dots.length > 0 ? Math.max(...dots.map((d) => d.distanceFromOrigin)) : 0;
159
+ dotsRef.current = dots;
225
160
  }
226
- const waveRadius = waveProgress * maxRadius * 1.5; // Travel beyond edges for smooth cycle
227
- // For instant trigger, show all dots immediately at full opacity
228
- if (trigger === "instant") {
229
- // Check if we're in a static state (no flicker, no active bulge)
230
- const isCurrentlyStatic = !flicker && (!bulgeEnabled || !showBulge);
231
- if (isCurrentlyStatic && isStaticRef.current && cachedImageData) {
232
- // Use cached render
233
- ctx.putImageData(cachedImageData, 0, 0);
161
+ else {
162
+ dots.forEach((dot) => {
163
+ dot.color = parsedColors[Math.floor(Math.random() * parsedColors.length)];
164
+ });
165
+ maxDistance = dots.length > 0 ? Math.max(...dots.map((d) => d.distanceFromOrigin)) : 0;
166
+ }
167
+ const startTime = Date.now();
168
+ const frameInterval = 1000 / fps;
169
+ const bulgeEnabled = !!bulge;
170
+ const bulgeType = bulge?.type ?? "ripple";
171
+ const bulgeDuration = bulge?.duration ?? 3;
172
+ const bulgeIntensity = bulge?.intensity ?? 10;
173
+ const bulgeRepeat = bulge?.repeat ?? true;
174
+ const bulgeDelay = bulge?.delay ?? 0;
175
+ const centerX = canvasWidth / 2;
176
+ const centerY = canvasHeight / 2;
177
+ const maxRadius = Math.sqrt(centerX * centerX + centerY * centerY);
178
+ let cachedImageData = null;
179
+ const cacheStaticFrame = () => {
180
+ if (canvas.width <= 0 || canvas.height <= 0)
181
+ return;
182
+ try {
183
+ cachedImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
234
184
  }
235
- else {
236
- // Render dots
237
- dots.forEach((dot) => {
238
- ctx.fillStyle = dot.color;
239
- let opacity = dot.baseOpacity;
240
- // Apply flicker effect if enabled
241
- if (flicker) {
242
- const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
243
- const flickerMultiplier = 0.6 + (flickerValue * 0.4); // Oscillate between 0.6 and 1.0
244
- opacity *= flickerMultiplier;
245
- }
246
- // Calculate bulge displacement
247
- let offsetX = 0;
248
- let offsetY = 0;
249
- let sizeMultiplier = 1;
250
- let bulgeOpacity = 1;
251
- if (bulgeEnabled && showBulge) {
252
- if (bulgeType === "ripple") {
253
- // Ripple: circular wave from center
254
- const dx = dot.x - centerX;
255
- const dy = dot.y - centerY;
256
- const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
257
- const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
258
- const waveWidth = maxRadius * 0.15;
259
- const distanceNorm = distanceToWave / waveWidth;
260
- const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
261
- const angle = Math.atan2(dy, dx);
262
- const displacementAmount = waveFactor * bulgeIntensity * bulgeFadeOut;
263
- offsetX = Math.cos(angle) * displacementAmount;
264
- offsetY = Math.sin(angle) * displacementAmount;
265
- sizeMultiplier = 1 + waveFactor * 0.8 * bulgeFadeOut;
266
- const waveOpacity = 0.3 + waveFactor * 0.7;
267
- bulgeOpacity = 1 + (waveOpacity - 1) * bulgeFadeOut;
268
- }
269
- else if (bulgeType === "wave") {
270
- // Wave: Organic S-curve from bottom-left to top-right with rotation
271
- const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
272
- // Start wave off-screen before bottom-left, end off-screen after top-right
273
- const wavePosOnDiagonal = (waveProgress * diagonalLength * 1.4) - (diagonalLength * 0.2);
274
- // Calculate distance along the diagonal (from bottom-left to top-right)
275
- const normalizedX = dot.x / canvasWidth;
276
- const normalizedY = 1 - (dot.y / canvasHeight);
277
- const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
278
- // Distance from wave front
279
- const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
280
- const waveWidth = diagonalLength * 0.25;
281
- const distanceNorm = distanceToWaveFront / waveWidth;
282
- // Smooth wave factor with wider influence
283
- const waveFactor = Math.exp(-distanceNorm * distanceNorm * 2.5);
284
- // Create rotating S-curve with multiple frequency components
285
- const perpendicularOffset = normalizedY - normalizedX;
286
- // Primary S-curve that rotates over time
287
- const rotationPhase = waveProgress * Math.PI * 3; // Rotates 1.5 times during animation
288
- const primaryFreq = 3; // Number of S-curves along the wave
289
- const sCurvePrimary = Math.sin(perpendicularOffset * Math.PI * primaryFreq + rotationPhase);
290
- // Secondary curve for complexity (higher frequency, lower amplitude)
291
- const secondaryFreq = 7;
292
- const sCurveSecondary = Math.sin(perpendicularOffset * Math.PI * secondaryFreq - rotationPhase * 1.5) * 0.4;
293
- // Combine curves for organic motion
294
- const sCurveFactor = sCurvePrimary + sCurveSecondary;
295
- // Wave straightens at the edges (distanceNorm affects curve strength)
296
- const curveStrength = 1 - Math.abs(distanceNorm) * 0.5;
297
- const modulatedCurve = sCurveFactor * curveStrength;
298
- // Apply displacement
299
- const baseDisplacement = waveFactor * bulgeIntensity * bulgeFadeOut;
300
- const diagonalAngle = Math.PI / 4;
301
- // Perpendicular displacement creates the S-curve
302
- const perpAngle = diagonalAngle + Math.PI / 2;
303
- offsetX = Math.cos(diagonalAngle) * baseDisplacement + Math.cos(perpAngle) * modulatedCurve * baseDisplacement * 0.8;
304
- offsetY = -Math.sin(diagonalAngle) * baseDisplacement - Math.sin(perpAngle) * modulatedCurve * baseDisplacement * 0.8;
305
- sizeMultiplier = 1 + waveFactor * 0.5 * bulgeFadeOut;
306
- const waveOpacity = 0.4 + waveFactor * 0.6;
307
- bulgeOpacity = 1 + (waveOpacity - 1) * bulgeFadeOut;
308
- }
309
- }
310
- ctx.globalAlpha = opacity * bulgeOpacity;
311
- const adjustedSize = size * sizeMultiplier;
312
- const sizeOffset = (adjustedSize - size) / 2;
313
- ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
314
- });
315
- // Cache if now static
316
- if (isCurrentlyStatic) {
317
- cachedImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
318
- isStaticRef.current = true;
319
- }
185
+ catch {
186
+ cachedImageData = null;
320
187
  }
321
- ctx.globalAlpha = 1;
322
- // Only continue animating if there's something dynamic (flicker or bulge)
323
- if (!isCurrentlyStatic) {
188
+ };
189
+ // When reduced motion is active, force static rendering after first frame
190
+ if (!shouldAnimate) {
191
+ isStaticRef.current = true;
192
+ mountAnimationCompleteRef.current = true;
193
+ }
194
+ const animate = (currentTime = 0) => {
195
+ if (canvasWidth <= 0 || canvasHeight <= 0) {
324
196
  animationRef.current = requestAnimationFrame(animate);
197
+ return;
325
198
  }
326
- else {
327
- // Clear the ref so the animation can be restarted
328
- animationRef.current = undefined;
199
+ // Skip rendering if not visible in viewport
200
+ if (!isVisibleRef.current) {
201
+ animationRef.current = requestAnimationFrame(animate);
202
+ return;
329
203
  }
330
- return;
331
- }
332
- // For mount trigger - progressive reveal on load, then static
333
- if (trigger === "mount") {
334
- if (!mountAnimationCompleteRef.current) {
335
- // Revealing animation with explosive easing
336
- const now = Date.now();
337
- const elapsed = (now - revealStartTimeRef.current) / 1000;
338
- // Cubic easing: starts very slow, then explodes
339
- const revealProgress = Math.pow(elapsed, 3) * speed * 3;
340
- // Check if animation is complete (max offset is ~1.3)
341
- if (revealProgress >= 2.0) {
342
- mountAnimationCompleteRef.current = true;
343
- }
344
- dots.forEach((dot) => {
345
- const normalizedDistance = dot.distanceFromOrigin / maxDistance;
346
- const introOffset = normalizedDistance * 0.8 + dot.randomOffset * 0.5;
347
- let opacity = 0;
348
- if (revealProgress > introOffset) {
349
- const fadeIn = (revealProgress - introOffset) * 8;
350
- opacity = Math.min(1, fadeIn * fadeIn) * dot.baseOpacity;
351
- // Apply flicker effect if enabled
352
- if (flicker) {
353
- const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
354
- const flickerMultiplier = 0.6 + (flickerValue * 0.4);
355
- opacity *= flickerMultiplier;
356
- }
204
+ // FPS throttling
205
+ if (currentTime - lastFrameTimeRef.current < frameInterval) {
206
+ animationRef.current = requestAnimationFrame(animate);
207
+ return;
208
+ }
209
+ lastFrameTimeRef.current = currentTime;
210
+ // If static and we have cached image, just render that and stop animating
211
+ if (isStaticRef.current && cachedImageData) {
212
+ ctx.putImageData(cachedImageData, 0, 0);
213
+ // Don't request another frame - animation is complete
214
+ return;
215
+ }
216
+ ctx.clearRect(0, 0, canvasWidth, canvasHeight);
217
+ const time = (Date.now() - startTime) / 1000; // Time in seconds
218
+ // Calculate circular wave radius (travels from center to edge)
219
+ let waveProgress = 0;
220
+ let showBulge = false;
221
+ let bulgeFadeOut = 1; // Multiplier for fading out bulge effect (1 = full effect, 0 = no effect)
222
+ if (bulgeEnabled) {
223
+ const bulgeElapsed = (Date.now() - bulgeStartTimeRef.current) / 1000;
224
+ const delaySeconds = bulgeDelay / 1000;
225
+ const totalCycleDuration = bulgeDuration + delaySeconds;
226
+ const adjustedTime = bulgeElapsed - delaySeconds;
227
+ const fadeStartPercent = 0.6; // Start fading at 60% of wave duration
228
+ if (adjustedTime >= 0) {
229
+ showBulge = true;
230
+ if (bulgeRepeat) {
231
+ // Continuous repeating wave
232
+ const cycleTime = adjustedTime % totalCycleDuration;
233
+ waveProgress = cycleTime < bulgeDuration ? cycleTime / bulgeDuration : 0;
234
+ showBulge = cycleTime < bulgeDuration; // Only show during active wave, not delay
357
235
  }
358
- if (opacity > 0) {
359
- // Calculate bulge displacement
360
- let offsetX = 0;
361
- let offsetY = 0;
362
- let sizeMultiplier = 1;
363
- let bulgeOpacity = 1;
364
- if (bulgeEnabled) {
365
- if (bulgeType === "ripple") {
366
- const dx = dot.x - centerX;
367
- const dy = dot.y - centerY;
368
- const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
369
- const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
370
- const waveWidth = maxRadius * 0.15;
371
- const distanceNorm = distanceToWave / waveWidth;
372
- const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
373
- const angle = Math.atan2(dy, dx);
374
- const displacementAmount = waveFactor * bulgeIntensity;
375
- offsetX = Math.cos(angle) * displacementAmount;
376
- offsetY = Math.sin(angle) * displacementAmount;
377
- sizeMultiplier = 1 + waveFactor * 0.8;
378
- bulgeOpacity = 0.3 + waveFactor * 0.7;
379
- }
380
- else if (bulgeType === "wave") {
381
- const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
382
- const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
383
- const normalizedX = dot.x / canvasWidth;
384
- const normalizedY = 1 - (dot.y / canvasHeight);
385
- const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
386
- const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
387
- const waveWidth = diagonalLength * 0.2;
388
- const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
389
- const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
390
- const perpendicularOffset = normalizedY - normalizedX;
391
- const sCurvePhase = perpendicularOffset * Math.PI * 2;
392
- const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
393
- const baseDisplacement = waveFactor * bulgeIntensity;
394
- const diagonalAngle = Math.PI / 4;
395
- offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
396
- offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
397
- sizeMultiplier = 1 + waveFactor * 0.6;
398
- bulgeOpacity = 0.4 + waveFactor * 0.6;
236
+ else {
237
+ // Single wave with opacity fade during last 40%
238
+ if (adjustedTime <= bulgeDuration) {
239
+ waveProgress = adjustedTime / bulgeDuration;
240
+ // Start fading opacity at 60% of duration
241
+ if (waveProgress >= fadeStartPercent) {
242
+ const fadeProgress = (waveProgress - fadeStartPercent) / (1 - fadeStartPercent);
243
+ bulgeFadeOut = 1 - fadeProgress; // Fade from 1 to 0
399
244
  }
400
245
  }
401
- ctx.fillStyle = dot.color;
402
- ctx.globalAlpha = opacity * bulgeOpacity;
403
- const adjustedSize = size * sizeMultiplier;
404
- const sizeOffset = (adjustedSize - size) / 2;
405
- ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
246
+ else {
247
+ waveProgress = 0;
248
+ showBulge = false;
249
+ }
406
250
  }
407
- });
251
+ }
408
252
  }
409
- else {
410
- // Animation complete - show all dots with optional flicker
253
+ const waveRadius = waveProgress * maxRadius * 1.5; // Travel beyond edges for smooth cycle
254
+ // For instant trigger, show all dots immediately at full opacity
255
+ if (trigger === "instant") {
256
+ // Check if we're in a static state (no flicker, no active bulge)
411
257
  const isCurrentlyStatic = !flicker && (!bulgeEnabled || !showBulge);
412
258
  if (isCurrentlyStatic && isStaticRef.current && cachedImageData) {
413
- // Use cached render for completed static state
259
+ // Use cached render
414
260
  ctx.putImageData(cachedImageData, 0, 0);
415
261
  }
416
262
  else {
263
+ // Render dots
417
264
  dots.forEach((dot) => {
418
265
  ctx.fillStyle = dot.color;
419
266
  let opacity = dot.baseOpacity;
267
+ // Apply flicker effect if enabled
420
268
  if (flicker) {
421
269
  const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
422
- const flickerMultiplier = 0.6 + (flickerValue * 0.4);
270
+ const flickerMultiplier = 0.6 + (flickerValue * 0.4); // Oscillate between 0.6 and 1.0
423
271
  opacity *= flickerMultiplier;
424
272
  }
425
273
  // Calculate bulge displacement
@@ -427,8 +275,9 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
427
275
  let offsetY = 0;
428
276
  let sizeMultiplier = 1;
429
277
  let bulgeOpacity = 1;
430
- if (bulgeEnabled) {
278
+ if (bulgeEnabled && showBulge) {
431
279
  if (bulgeType === "ripple") {
280
+ // Ripple: circular wave from center
432
281
  const dx = dot.x - centerX;
433
282
  const dy = dot.y - centerY;
434
283
  const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
@@ -437,31 +286,52 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
437
286
  const distanceNorm = distanceToWave / waveWidth;
438
287
  const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
439
288
  const angle = Math.atan2(dy, dx);
440
- const displacementAmount = waveFactor * bulgeIntensity;
289
+ const displacementAmount = waveFactor * bulgeIntensity * bulgeFadeOut;
441
290
  offsetX = Math.cos(angle) * displacementAmount;
442
291
  offsetY = Math.sin(angle) * displacementAmount;
443
- sizeMultiplier = 1 + waveFactor * 0.8;
444
- bulgeOpacity = 0.3 + waveFactor * 0.7;
292
+ sizeMultiplier = 1 + waveFactor * 0.8 * bulgeFadeOut;
293
+ const waveOpacity = 0.3 + waveFactor * 0.7;
294
+ bulgeOpacity = 1 + (waveOpacity - 1) * bulgeFadeOut;
445
295
  }
446
296
  else if (bulgeType === "wave") {
297
+ // Wave: Organic S-curve from bottom-left to top-right with rotation
447
298
  const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
448
- const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
299
+ // Start wave off-screen before bottom-left, end off-screen after top-right
300
+ const wavePosOnDiagonal = (waveProgress * diagonalLength * 1.4) - (diagonalLength * 0.2);
301
+ // Calculate distance along the diagonal (from bottom-left to top-right)
449
302
  const normalizedX = dot.x / canvasWidth;
450
303
  const normalizedY = 1 - (dot.y / canvasHeight);
451
304
  const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
305
+ // Distance from wave front
452
306
  const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
453
- const waveWidth = diagonalLength * 0.2;
454
- const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
455
- const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
307
+ const waveWidth = diagonalLength * 0.25;
308
+ const distanceNorm = distanceToWaveFront / waveWidth;
309
+ // Smooth wave factor with wider influence
310
+ const waveFactor = Math.exp(-distanceNorm * distanceNorm * 2.5);
311
+ // Create rotating S-curve with multiple frequency components
456
312
  const perpendicularOffset = normalizedY - normalizedX;
457
- const sCurvePhase = perpendicularOffset * Math.PI * 2;
458
- const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
459
- const baseDisplacement = waveFactor * bulgeIntensity;
313
+ // Primary S-curve that rotates over time
314
+ const rotationPhase = waveProgress * Math.PI * 3; // Rotates 1.5 times during animation
315
+ const primaryFreq = 3; // Number of S-curves along the wave
316
+ const sCurvePrimary = Math.sin(perpendicularOffset * Math.PI * primaryFreq + rotationPhase);
317
+ // Secondary curve for complexity (higher frequency, lower amplitude)
318
+ const secondaryFreq = 7;
319
+ const sCurveSecondary = Math.sin(perpendicularOffset * Math.PI * secondaryFreq - rotationPhase * 1.5) * 0.4;
320
+ // Combine curves for organic motion
321
+ const sCurveFactor = sCurvePrimary + sCurveSecondary;
322
+ // Wave straightens at the edges (distanceNorm affects curve strength)
323
+ const curveStrength = 1 - Math.abs(distanceNorm) * 0.5;
324
+ const modulatedCurve = sCurveFactor * curveStrength;
325
+ // Apply displacement
326
+ const baseDisplacement = waveFactor * bulgeIntensity * bulgeFadeOut;
460
327
  const diagonalAngle = Math.PI / 4;
461
- offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
462
- offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
463
- sizeMultiplier = 1 + waveFactor * 0.6;
464
- bulgeOpacity = 0.4 + waveFactor * 0.6;
328
+ // Perpendicular displacement creates the S-curve
329
+ const perpAngle = diagonalAngle + Math.PI / 2;
330
+ offsetX = Math.cos(diagonalAngle) * baseDisplacement + Math.cos(perpAngle) * modulatedCurve * baseDisplacement * 0.8;
331
+ offsetY = -Math.sin(diagonalAngle) * baseDisplacement - Math.sin(perpAngle) * modulatedCurve * baseDisplacement * 0.8;
332
+ sizeMultiplier = 1 + waveFactor * 0.5 * bulgeFadeOut;
333
+ const waveOpacity = 0.4 + waveFactor * 0.6;
334
+ bulgeOpacity = 1 + (waveOpacity - 1) * bulgeFadeOut;
465
335
  }
466
336
  }
467
337
  ctx.globalAlpha = opacity * bulgeOpacity;
@@ -471,133 +341,121 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
471
341
  });
472
342
  // Cache if now static
473
343
  if (isCurrentlyStatic) {
474
- cachedImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
344
+ cacheStaticFrame();
475
345
  isStaticRef.current = true;
476
346
  }
477
347
  }
348
+ ctx.globalAlpha = 1;
349
+ // Only continue animating if there's something dynamic (flicker or bulge)
350
+ if (!isCurrentlyStatic) {
351
+ animationRef.current = requestAnimationFrame(animate);
352
+ }
353
+ else {
354
+ // Clear the ref so the animation can be restarted
355
+ animationRef.current = undefined;
356
+ }
357
+ return;
478
358
  }
479
- ctx.globalAlpha = 1;
480
- // Only continue animating if mount animation is incomplete or there's something dynamic
481
- const isCurrentlyStatic = mountAnimationCompleteRef.current && !flicker && (!bulgeEnabled || !showBulge);
482
- if (!isCurrentlyStatic) {
483
- animationRef.current = requestAnimationFrame(animate);
484
- }
485
- else {
486
- // Clear the ref so the animation can be restarted
487
- animationRef.current = undefined;
488
- }
489
- return;
490
- }
491
- // For hover, click, and manual triggers with animation
492
- if (trigger === "hover" || trigger === "click" || trigger === "manual") {
493
- if (isHoveredRef.current) {
494
- // Revealing animation with explosive easing
495
- const now = Date.now();
496
- const elapsed = (now - revealStartTimeRef.current) / 1000;
497
- // Cubic easing: starts very slow, then explodes
498
- const revealProgress = Math.pow(elapsed, 3) * speed * 3;
499
- // Cap progress to prevent infinite growth (max offset is ~1.3)
500
- const cappedProgress = Math.min(revealProgress, 2.0);
501
- // Track maximum progress for reverse animation
502
- maxRevealProgressRef.current = cappedProgress;
503
- dots.forEach((dot) => {
504
- const normalizedDistance = dot.distanceFromOrigin / maxDistance;
505
- const introOffset = normalizedDistance * 0.8 + dot.randomOffset * 0.5; // Much lower threshold
506
- let opacity = 0;
507
- if (cappedProgress > introOffset) {
508
- // Explosive opacity increase
509
- const fadeIn = (cappedProgress - introOffset) * 8; // Faster fade-in
510
- opacity = Math.min(1, fadeIn * fadeIn) * dot.baseOpacity;
511
- // Apply flicker effect if enabled
512
- if (flicker) {
513
- const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
514
- const flickerMultiplier = 0.6 + (flickerValue * 0.4); // Oscillate between 0.6 and 1.0
515
- opacity *= flickerMultiplier;
516
- }
359
+ // For mount trigger - progressive reveal on load, then static
360
+ if (trigger === "mount") {
361
+ if (!mountAnimationCompleteRef.current) {
362
+ // Revealing animation with explosive easing
363
+ const now = Date.now();
364
+ const elapsed = (now - revealStartTimeRef.current) / 1000;
365
+ // Cubic easing: starts very slow, then explodes
366
+ const revealProgress = Math.pow(elapsed, 3) * speed * 3;
367
+ // Check if animation is complete (max offset is ~1.3)
368
+ if (revealProgress >= 2.0) {
369
+ mountAnimationCompleteRef.current = true;
517
370
  }
518
- if (opacity > 0) {
519
- // Calculate bulge displacement
520
- let offsetX = 0;
521
- let offsetY = 0;
522
- let sizeMultiplier = 1;
523
- let bulgeOpacity = 1;
524
- if (bulgeEnabled) {
525
- if (bulgeType === "ripple") {
526
- const dx = dot.x - centerX;
527
- const dy = dot.y - centerY;
528
- const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
529
- const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
530
- const waveWidth = maxRadius * 0.15;
531
- const distanceNorm = distanceToWave / waveWidth;
532
- const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
533
- const angle = Math.atan2(dy, dx);
534
- const displacementAmount = waveFactor * bulgeIntensity;
535
- offsetX = Math.cos(angle) * displacementAmount;
536
- offsetY = Math.sin(angle) * displacementAmount;
537
- sizeMultiplier = 1 + waveFactor * 0.8;
538
- bulgeOpacity = 0.3 + waveFactor * 0.7;
371
+ dots.forEach((dot) => {
372
+ const normalizedDistance = dot.distanceFromOrigin / maxDistance;
373
+ const introOffset = normalizedDistance * 0.8 + dot.randomOffset * 0.5;
374
+ let opacity = 0;
375
+ if (revealProgress > introOffset) {
376
+ const fadeIn = (revealProgress - introOffset) * 8;
377
+ opacity = Math.min(1, fadeIn * fadeIn) * dot.baseOpacity;
378
+ // Apply flicker effect if enabled
379
+ if (flicker) {
380
+ const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
381
+ const flickerMultiplier = 0.6 + (flickerValue * 0.4);
382
+ opacity *= flickerMultiplier;
539
383
  }
540
- else if (bulgeType === "wave") {
541
- const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
542
- const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
543
- const normalizedX = dot.x / canvasWidth;
544
- const normalizedY = 1 - (dot.y / canvasHeight);
545
- const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
546
- const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
547
- const waveWidth = diagonalLength * 0.2;
548
- const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
549
- const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
550
- const perpendicularOffset = normalizedY - normalizedX;
551
- const sCurvePhase = perpendicularOffset * Math.PI * 2;
552
- const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
553
- const baseDisplacement = waveFactor * bulgeIntensity;
554
- const diagonalAngle = Math.PI / 4;
555
- offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
556
- offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
557
- sizeMultiplier = 1 + waveFactor * 0.6;
558
- bulgeOpacity = 0.4 + waveFactor * 0.6;
384
+ }
385
+ if (opacity > 0) {
386
+ // Calculate bulge displacement
387
+ let offsetX = 0;
388
+ let offsetY = 0;
389
+ let sizeMultiplier = 1;
390
+ let bulgeOpacity = 1;
391
+ if (bulgeEnabled) {
392
+ if (bulgeType === "ripple") {
393
+ const dx = dot.x - centerX;
394
+ const dy = dot.y - centerY;
395
+ const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
396
+ const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
397
+ const waveWidth = maxRadius * 0.15;
398
+ const distanceNorm = distanceToWave / waveWidth;
399
+ const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
400
+ const angle = Math.atan2(dy, dx);
401
+ const displacementAmount = waveFactor * bulgeIntensity;
402
+ offsetX = Math.cos(angle) * displacementAmount;
403
+ offsetY = Math.sin(angle) * displacementAmount;
404
+ sizeMultiplier = 1 + waveFactor * 0.8;
405
+ bulgeOpacity = 0.3 + waveFactor * 0.7;
406
+ }
407
+ else if (bulgeType === "wave") {
408
+ const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
409
+ const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
410
+ const normalizedX = dot.x / canvasWidth;
411
+ const normalizedY = 1 - (dot.y / canvasHeight);
412
+ const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
413
+ const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
414
+ const waveWidth = diagonalLength * 0.2;
415
+ const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
416
+ const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
417
+ const perpendicularOffset = normalizedY - normalizedX;
418
+ const sCurvePhase = perpendicularOffset * Math.PI * 2;
419
+ const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
420
+ const baseDisplacement = waveFactor * bulgeIntensity;
421
+ const diagonalAngle = Math.PI / 4;
422
+ offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
423
+ offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
424
+ sizeMultiplier = 1 + waveFactor * 0.6;
425
+ bulgeOpacity = 0.4 + waveFactor * 0.6;
426
+ }
559
427
  }
428
+ ctx.fillStyle = dot.color;
429
+ ctx.globalAlpha = opacity * bulgeOpacity;
430
+ const adjustedSize = size * sizeMultiplier;
431
+ const sizeOffset = (adjustedSize - size) / 2;
432
+ ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
560
433
  }
561
- ctx.fillStyle = dot.color;
562
- ctx.globalAlpha = opacity * bulgeOpacity;
563
- const adjustedSize = size * sizeMultiplier;
564
- const sizeOffset = (adjustedSize - size) / 2;
565
- ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
434
+ });
435
+ }
436
+ else {
437
+ // Animation complete - show all dots with optional flicker
438
+ const isCurrentlyStatic = !flicker && (!bulgeEnabled || !showBulge);
439
+ if (isCurrentlyStatic && isStaticRef.current && cachedImageData) {
440
+ // Use cached render for completed static state
441
+ ctx.putImageData(cachedImageData, 0, 0);
566
442
  }
567
- });
568
- }
569
- else {
570
- // Reverse animation when hover ends - only run if there's something to hide
571
- if (hideStartProgressRef.current > 0) {
572
- const elapsed = (Date.now() - hideStartTimeRef.current) / 1000;
573
- // Use quadratic easing for faster hide (power of 2)
574
- const hideSpeed = speed * 6; // Faster to handle max progress of 2.0
575
- const hideProgress = Math.pow(elapsed, 2) * hideSpeed;
576
- // Reverse from the progress we had when hide started
577
- const reverseProgress = Math.max(0, hideStartProgressRef.current - hideProgress);
578
- if (reverseProgress > 0.01) { // Small threshold to ensure completion
579
- // Still have dots to hide
443
+ else {
580
444
  dots.forEach((dot) => {
581
- const normalizedDistance = dot.distanceFromOrigin / maxDistance;
582
- const introOffset = normalizedDistance * 0.8 + dot.randomOffset * 0.5;
583
- let opacity = 0;
584
- if (reverseProgress > introOffset) {
585
- const fadeIn = (reverseProgress - introOffset) * 8;
586
- opacity = Math.min(1, fadeIn * fadeIn) * dot.baseOpacity;
587
- // Apply flicker effect if enabled
588
- if (flicker) {
589
- const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
590
- const flickerMultiplier = 0.6 + (flickerValue * 0.4);
591
- opacity *= flickerMultiplier;
592
- }
445
+ ctx.fillStyle = dot.color;
446
+ let opacity = dot.baseOpacity;
447
+ if (flicker) {
448
+ const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
449
+ const flickerMultiplier = 0.6 + (flickerValue * 0.4);
450
+ opacity *= flickerMultiplier;
593
451
  }
594
- if (opacity > 0) {
595
- // Calculate bulge displacement
596
- let offsetX = 0;
597
- let offsetY = 0;
598
- let sizeMultiplier = 1;
599
- let bulgeOpacity = 1;
600
- if (bulgeEnabled) {
452
+ // Calculate bulge displacement
453
+ let offsetX = 0;
454
+ let offsetY = 0;
455
+ let sizeMultiplier = 1;
456
+ let bulgeOpacity = 1;
457
+ if (bulgeEnabled) {
458
+ if (bulgeType === "ripple") {
601
459
  const dx = dot.x - centerX;
602
460
  const dy = dot.y - centerY;
603
461
  const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
@@ -607,48 +465,230 @@ const MatrixFx = React.forwardRef(({ speed = 1, colors = ["brand-solid-medium"],
607
465
  const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
608
466
  const angle = Math.atan2(dy, dx);
609
467
  const displacementAmount = waveFactor * bulgeIntensity;
610
- offsetX = Math.cos(angle) * displacementAmount * 0.3;
611
- offsetY = Math.sin(angle) * displacementAmount * 0.3 - waveFactor * bulgeIntensity;
468
+ offsetX = Math.cos(angle) * displacementAmount;
469
+ offsetY = Math.sin(angle) * displacementAmount;
612
470
  sizeMultiplier = 1 + waveFactor * 0.8;
613
471
  bulgeOpacity = 0.3 + waveFactor * 0.7;
614
472
  }
615
- ctx.fillStyle = dot.color;
616
- ctx.globalAlpha = opacity * bulgeOpacity;
617
- const adjustedSize = size * sizeMultiplier;
618
- const sizeOffset = (adjustedSize - size) / 2;
619
- ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
473
+ else if (bulgeType === "wave") {
474
+ const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
475
+ const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
476
+ const normalizedX = dot.x / canvasWidth;
477
+ const normalizedY = 1 - (dot.y / canvasHeight);
478
+ const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
479
+ const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
480
+ const waveWidth = diagonalLength * 0.2;
481
+ const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
482
+ const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
483
+ const perpendicularOffset = normalizedY - normalizedX;
484
+ const sCurvePhase = perpendicularOffset * Math.PI * 2;
485
+ const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
486
+ const baseDisplacement = waveFactor * bulgeIntensity;
487
+ const diagonalAngle = Math.PI / 4;
488
+ offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
489
+ offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
490
+ sizeMultiplier = 1 + waveFactor * 0.6;
491
+ bulgeOpacity = 0.4 + waveFactor * 0.6;
492
+ }
620
493
  }
494
+ ctx.globalAlpha = opacity * bulgeOpacity;
495
+ const adjustedSize = size * sizeMultiplier;
496
+ const sizeOffset = (adjustedSize - size) / 2;
497
+ ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
621
498
  });
499
+ // Cache if now static
500
+ if (isCurrentlyStatic) {
501
+ cacheStaticFrame();
502
+ isStaticRef.current = true;
503
+ }
622
504
  }
623
- else {
624
- // Hide animation complete, reset progress and clear canvas
625
- hideStartProgressRef.current = 0;
505
+ }
506
+ ctx.globalAlpha = 1;
507
+ // Only continue animating if mount animation is incomplete or there's something dynamic
508
+ const isCurrentlyStatic = mountAnimationCompleteRef.current && !flicker && (!bulgeEnabled || !showBulge);
509
+ if (!isCurrentlyStatic) {
510
+ animationRef.current = requestAnimationFrame(animate);
511
+ }
512
+ else {
513
+ // Clear the ref so the animation can be restarted
514
+ animationRef.current = undefined;
515
+ }
516
+ return;
517
+ }
518
+ // For hover, click, and manual triggers with animation
519
+ if (trigger === "hover" || trigger === "click" || trigger === "manual") {
520
+ if (isHoveredRef.current) {
521
+ // Revealing animation with explosive easing
522
+ const now = Date.now();
523
+ const elapsed = (now - revealStartTimeRef.current) / 1000;
524
+ // Cubic easing: starts very slow, then explodes
525
+ const revealProgress = Math.pow(elapsed, 3) * speed * 3;
526
+ // Cap progress to prevent infinite growth (max offset is ~1.3)
527
+ const cappedProgress = Math.min(revealProgress, 2.0);
528
+ // Track maximum progress for reverse animation
529
+ maxRevealProgressRef.current = cappedProgress;
530
+ dots.forEach((dot) => {
531
+ const normalizedDistance = dot.distanceFromOrigin / maxDistance;
532
+ const introOffset = normalizedDistance * 0.8 + dot.randomOffset * 0.5; // Much lower threshold
533
+ let opacity = 0;
534
+ if (cappedProgress > introOffset) {
535
+ // Explosive opacity increase
536
+ const fadeIn = (cappedProgress - introOffset) * 8; // Faster fade-in
537
+ opacity = Math.min(1, fadeIn * fadeIn) * dot.baseOpacity;
538
+ // Apply flicker effect if enabled
539
+ if (flicker) {
540
+ const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
541
+ const flickerMultiplier = 0.6 + (flickerValue * 0.4); // Oscillate between 0.6 and 1.0
542
+ opacity *= flickerMultiplier;
543
+ }
544
+ }
545
+ if (opacity > 0) {
546
+ // Calculate bulge displacement
547
+ let offsetX = 0;
548
+ let offsetY = 0;
549
+ let sizeMultiplier = 1;
550
+ let bulgeOpacity = 1;
551
+ if (bulgeEnabled) {
552
+ if (bulgeType === "ripple") {
553
+ const dx = dot.x - centerX;
554
+ const dy = dot.y - centerY;
555
+ const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
556
+ const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
557
+ const waveWidth = maxRadius * 0.15;
558
+ const distanceNorm = distanceToWave / waveWidth;
559
+ const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
560
+ const angle = Math.atan2(dy, dx);
561
+ const displacementAmount = waveFactor * bulgeIntensity;
562
+ offsetX = Math.cos(angle) * displacementAmount;
563
+ offsetY = Math.sin(angle) * displacementAmount;
564
+ sizeMultiplier = 1 + waveFactor * 0.8;
565
+ bulgeOpacity = 0.3 + waveFactor * 0.7;
566
+ }
567
+ else if (bulgeType === "wave") {
568
+ const diagonalLength = Math.sqrt(canvasWidth * canvasWidth + canvasHeight * canvasHeight);
569
+ const wavePosOnDiagonal = waveProgress * diagonalLength * 1.2;
570
+ const normalizedX = dot.x / canvasWidth;
571
+ const normalizedY = 1 - (dot.y / canvasHeight);
572
+ const dotDiagonalPos = (normalizedX + normalizedY) / 2 * diagonalLength;
573
+ const distanceToWaveFront = dotDiagonalPos - wavePosOnDiagonal;
574
+ const waveWidth = diagonalLength * 0.2;
575
+ const distanceNorm = Math.abs(distanceToWaveFront) / waveWidth;
576
+ const waveFactor = Math.exp(-distanceNorm * distanceNorm * 3);
577
+ const perpendicularOffset = normalizedY - normalizedX;
578
+ const sCurvePhase = perpendicularOffset * Math.PI * 2;
579
+ const sCurveFactor = Math.sin(sCurvePhase + waveProgress * Math.PI * 2);
580
+ const baseDisplacement = waveFactor * bulgeIntensity;
581
+ const diagonalAngle = Math.PI / 4;
582
+ offsetX = Math.cos(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
583
+ offsetY = -Math.sin(diagonalAngle) * baseDisplacement + sCurveFactor * baseDisplacement * 0.5;
584
+ sizeMultiplier = 1 + waveFactor * 0.6;
585
+ bulgeOpacity = 0.4 + waveFactor * 0.6;
586
+ }
587
+ }
588
+ ctx.fillStyle = dot.color;
589
+ ctx.globalAlpha = opacity * bulgeOpacity;
590
+ const adjustedSize = size * sizeMultiplier;
591
+ const sizeOffset = (adjustedSize - size) / 2;
592
+ ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
593
+ }
594
+ });
595
+ }
596
+ else {
597
+ // Reverse animation when hover ends - only run if there's something to hide
598
+ if (hideStartProgressRef.current > 0) {
599
+ const elapsed = (Date.now() - hideStartTimeRef.current) / 1000;
600
+ // Use quadratic easing for faster hide (power of 2)
601
+ const hideSpeed = speed * 6; // Faster to handle max progress of 2.0
602
+ const hideProgress = Math.pow(elapsed, 2) * hideSpeed;
603
+ // Reverse from the progress we had when hide started
604
+ const reverseProgress = Math.max(0, hideStartProgressRef.current - hideProgress);
605
+ if (reverseProgress > 0.01) { // Small threshold to ensure completion
606
+ // Still have dots to hide
607
+ dots.forEach((dot) => {
608
+ const normalizedDistance = dot.distanceFromOrigin / maxDistance;
609
+ const introOffset = normalizedDistance * 0.8 + dot.randomOffset * 0.5;
610
+ let opacity = 0;
611
+ if (reverseProgress > introOffset) {
612
+ const fadeIn = (reverseProgress - introOffset) * 8;
613
+ opacity = Math.min(1, fadeIn * fadeIn) * dot.baseOpacity;
614
+ // Apply flicker effect if enabled
615
+ if (flicker) {
616
+ const flickerValue = Math.sin(time * dot.flickerSpeed * 3 + dot.flickerPhase);
617
+ const flickerMultiplier = 0.6 + (flickerValue * 0.4);
618
+ opacity *= flickerMultiplier;
619
+ }
620
+ }
621
+ if (opacity > 0) {
622
+ // Calculate bulge displacement
623
+ let offsetX = 0;
624
+ let offsetY = 0;
625
+ let sizeMultiplier = 1;
626
+ let bulgeOpacity = 1;
627
+ if (bulgeEnabled) {
628
+ const dx = dot.x - centerX;
629
+ const dy = dot.y - centerY;
630
+ const distanceFromCenter = Math.sqrt(dx * dx + dy * dy);
631
+ const distanceToWave = Math.abs(distanceFromCenter - waveRadius);
632
+ const waveWidth = maxRadius * 0.15;
633
+ const distanceNorm = distanceToWave / waveWidth;
634
+ const waveFactor = Math.exp(-distanceNorm * distanceNorm * 4);
635
+ const angle = Math.atan2(dy, dx);
636
+ const displacementAmount = waveFactor * bulgeIntensity;
637
+ offsetX = Math.cos(angle) * displacementAmount * 0.3;
638
+ offsetY = Math.sin(angle) * displacementAmount * 0.3 - waveFactor * bulgeIntensity;
639
+ sizeMultiplier = 1 + waveFactor * 0.8;
640
+ bulgeOpacity = 0.3 + waveFactor * 0.7;
641
+ }
642
+ ctx.fillStyle = dot.color;
643
+ ctx.globalAlpha = opacity * bulgeOpacity;
644
+ const adjustedSize = size * sizeMultiplier;
645
+ const sizeOffset = (adjustedSize - size) / 2;
646
+ ctx.fillRect(dot.x + offsetX - sizeOffset, dot.y + offsetY - sizeOffset, adjustedSize, adjustedSize);
647
+ }
648
+ });
649
+ }
650
+ else {
651
+ // Hide animation complete, reset progress and clear canvas
652
+ hideStartProgressRef.current = 0;
653
+ }
626
654
  }
627
655
  }
628
656
  }
629
- }
630
- ctx.globalAlpha = 1;
631
- // Only continue animating if there's active animation or dynamic content
632
- const hasActiveAnimation = isHoveredRef.current || hideStartProgressRef.current > 0;
633
- const hasDynamicContent = flicker || (bulgeEnabled && showBulge);
634
- if (hasActiveAnimation || hasDynamicContent) {
635
- animationRef.current = requestAnimationFrame(animate);
657
+ ctx.globalAlpha = 1;
658
+ // Only continue animating if there's active animation or dynamic content
659
+ const hasActiveAnimation = isHoveredRef.current || hideStartProgressRef.current > 0;
660
+ const hasDynamicContent = flicker || (bulgeEnabled && showBulge);
661
+ if (hasActiveAnimation || hasDynamicContent) {
662
+ animationRef.current = requestAnimationFrame(animate);
663
+ }
664
+ else {
665
+ // Clear the ref so the animation can be restarted
666
+ animationRef.current = undefined;
667
+ }
668
+ };
669
+ // Store animate function in ref so it can be restarted
670
+ animateFnRef.current = animate;
671
+ animate();
672
+ };
673
+ setup();
674
+ const resizeObserver = new ResizeObserver(() => {
675
+ if (!initialized) {
676
+ setup();
636
677
  }
637
678
  else {
638
- // Clear the ref so the animation can be restarted
639
- animationRef.current = undefined;
679
+ handleResize();
640
680
  }
641
- };
642
- // Store animate function in ref so it can be restarted
643
- animateFnRef.current = animate;
644
- animate();
681
+ });
682
+ resizeObserver.observe(container);
645
683
  return () => {
646
- window.removeEventListener("resize", updateSize);
684
+ disposed = true;
685
+ resizeObserver.disconnect();
686
+ window.removeEventListener("resize", handleResize);
647
687
  if (animationRef.current) {
648
688
  cancelAnimationFrame(animationRef.current);
649
689
  }
650
690
  };
651
- }, [colors, size, spacing, speed, revealFrom, trigger, flicker, bulge, fps]);
691
+ }, [colors, size, spacing, speed, revealFrom, trigger, flicker, bulge, fps, shouldAnimate]);
652
692
  // Manual trigger control via `active` prop
653
693
  useEffect(() => {
654
694
  if (trigger !== "manual")