@mkbabb/glass-ui 4.0.0 → 4.0.1

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 (394) hide show
  1. package/README.md +3 -1
  2. package/dist/CardAction-CW7a0m8M.js +253 -0
  3. package/dist/{CommandShortcut-DdtWXxOK.js → CommandShortcut-DvZNQ_HF.js} +4 -4
  4. package/dist/ConfiguratorRow-BR4l-ixA.js +143 -0
  5. package/dist/{ContextMenuContent-ON3x7cuk.js → ContextMenuContent-uWnVxDab.js} +1 -1
  6. package/dist/{ContextMenuSubContent-B0Vs6atR.js → ContextMenuSubContent-xol4sD2q.js} +32 -37
  7. package/dist/{DataTable-C464Zbqd.js → DataTable-CuKwF7wr.js} +4 -4
  8. package/dist/{DialogContent-DDE6pQBU.js → DialogContent-DcKK8k9i.js} +5 -5
  9. package/dist/{DialogFooter-8eZbdmPc.js → DialogFooter-BZNX5aVr.js} +3 -3
  10. package/dist/{FadingScroll-DwNnvKMs.js → FadingScroll-DI6hQhT9.js} +1 -1
  11. package/dist/{HoverCardContent-DkMaQzH5.js → HoverCardContent-b2uaAMRR.js} +1 -1
  12. package/dist/{HoverPopover-Dpzwvc4t.js → HoverPopover-CbqbNwgU.js} +2 -2
  13. package/dist/{IconChip-BDw1j5mu.js → IconChip-BTD-nay5.js} +3 -2
  14. package/dist/{IconTooltip-DSjasZIt.js → IconTooltip-Bl8gFZ8d.js} +1 -1
  15. package/dist/{Input-DVG_J0ne.js → Input-h9v5LeXf.js} +1 -1
  16. package/dist/{MetricBadge-BpC0R_Ec.js → MetricBadge-RHdBP72w.js} +1 -1
  17. package/dist/Notification-VUwexFUf.js +43 -0
  18. package/dist/NumberFieldContent-Cq7EzX-U.js +138 -0
  19. package/dist/{PopoverContent-BTorlp--.js → PopoverContent-DL7E6vJP.js} +4 -4
  20. package/dist/{Progress-hAT4gqGH.js → Progress-DctG61iZ.js} +4 -4
  21. package/dist/{ScrollingText-CUVFTWk2.js → ScrollingText-yOpBzFYq.js} +2 -2
  22. package/dist/{SelectScrollDownButton-C1jb3b3K.js → SelectScrollDownButton-CrA8F0iV.js} +18 -19
  23. package/dist/Toaster-Bw4xR8xV.js +274 -0
  24. package/dist/{ToggleGroupItem-Dzk9hWsF.js → ToggleGroupItem-pEM9vIhs.js} +50 -37
  25. package/dist/{TooltipProvider-B3MkB_8P.js → TooltipProvider-rzvnXz60.js} +1 -1
  26. package/dist/animated-digit.js +2 -2
  27. package/dist/api/index.d.ts +8 -3
  28. package/dist/api/types-extra.d.ts +6 -0
  29. package/dist/aurora.js +885 -242
  30. package/dist/badge.js +1 -1
  31. package/dist/border-progress.d.ts +1 -0
  32. package/dist/border-progress.js +106 -0
  33. package/dist/{button-BNDWhAZb.js → button-DYCUa-tH.js} +71 -26
  34. package/dist/button.js +1 -1
  35. package/dist/canvas.js +2 -2
  36. package/dist/card.js +2 -2
  37. package/dist/carousel.js +93 -110
  38. package/dist/collapsible.js +1 -1
  39. package/dist/command.js +1 -1
  40. package/dist/compile-CQEFRTmx.js +19 -0
  41. package/dist/components/custom/aurora/composables/atoms-fields.d.ts +39 -0
  42. package/dist/components/custom/aurora/composables/auroraFallbackGround.d.ts +108 -0
  43. package/dist/components/custom/aurora/composables/runtime.d.ts +19 -0
  44. package/dist/components/custom/aurora/composables/uniformBridge.d.ts +5 -1
  45. package/dist/components/custom/aurora/composables/uniformBridgeWGPU.d.ts +51 -0
  46. package/dist/components/custom/aurora/composables/wgpuSetup.d.ts +29 -0
  47. package/dist/components/custom/aurora/constants/budget.d.ts +21 -0
  48. package/dist/components/custom/aurora/constants/presets.d.ts +2 -2
  49. package/dist/components/custom/aurora/constants/renderMode.d.ts +63 -12
  50. package/dist/components/custom/aurora/constants/shaders/aurora.wgsl.d.ts +1 -0
  51. package/dist/components/custom/aurora/constants/shaders/oil-modes.glsl.d.ts +1 -0
  52. package/dist/components/custom/aurora/constants/shaders/procedural-color.wgsl.d.ts +4 -0
  53. package/dist/components/custom/aurora/index.d.ts +2 -1
  54. package/dist/components/custom/border-progress/BorderProgress.vue.d.ts +46 -0
  55. package/dist/components/custom/border-progress/composables/useBorderSpectrum.d.ts +22 -0
  56. package/dist/components/custom/border-progress/constants.d.ts +59 -0
  57. package/dist/components/custom/border-progress/index.d.ts +4 -0
  58. package/dist/components/custom/concentric/Concentric.vue.d.ts +39 -0
  59. package/dist/components/custom/concentric/composables/concentricGLSetup.d.ts +11 -0
  60. package/dist/components/custom/concentric/composables/concentricWGPUSetup.d.ts +13 -0
  61. package/dist/components/custom/concentric/composables/ringField.d.ts +44 -0
  62. package/dist/components/custom/concentric/composables/uniformBridgeWGPU.d.ts +13 -0
  63. package/dist/components/custom/concentric/composables/useConcentric.d.ts +35 -0
  64. package/dist/components/custom/concentric/constants.d.ts +43 -0
  65. package/dist/components/custom/concentric/index.d.ts +5 -0
  66. package/dist/components/custom/concentric/shaders/concentric.glsl.d.ts +4 -0
  67. package/dist/components/custom/concentric/shaders/concentric.wgsl.d.ts +1 -0
  68. package/dist/components/custom/dock/DockIconButton.vue.d.ts +13 -4
  69. package/dist/components/custom/dock/DockLayerGroup.vue.d.ts +10 -2
  70. package/dist/components/custom/dock/composables/dockMorphMeasure.d.ts +75 -0
  71. package/dist/components/custom/dock/composables/useLayerTransition.d.ts +13 -0
  72. package/dist/components/custom/dot-flow-field/DotFlowField.vue.d.ts +38 -0
  73. package/dist/components/custom/dot-flow-field/composables/flowField.d.ts +41 -0
  74. package/dist/components/custom/dot-flow-field/composables/uniformBridgeWGPU.d.ts +24 -0
  75. package/dist/components/custom/dot-flow-field/composables/useDotFlowField.d.ts +34 -0
  76. package/dist/components/custom/dot-flow-field/composables/useFlowParticles.d.ts +18 -0
  77. package/dist/components/custom/dot-flow-field/constants.d.ts +48 -0
  78. package/dist/components/custom/dot-flow-field/index.d.ts +5 -0
  79. package/dist/components/custom/dot-flow-field/shaders/flow-field.compute.wgsl.d.ts +1 -0
  80. package/dist/components/custom/dot-flow-field/shaders/flow-field.glsl.d.ts +20 -0
  81. package/dist/components/custom/dot-flow-field/shaders/flow-field.render.wgsl.d.ts +1 -0
  82. package/dist/components/custom/easing/EasingConfigurator.vue.d.ts +33 -0
  83. package/dist/components/custom/easing/EasingPicker.vue.d.ts +49 -0
  84. package/dist/components/custom/easing/composables/useEasingPicker.d.ts +75 -0
  85. package/dist/components/custom/easing/constants.d.ts +28 -0
  86. package/dist/components/custom/easing/index.d.ts +3 -0
  87. package/dist/components/custom/fourier-field/FourierField.vue.d.ts +12 -2
  88. package/dist/components/custom/fourier-field/composables/useFourierField.d.ts +39 -0
  89. package/dist/components/custom/fourier-field/constants.d.ts +7 -0
  90. package/dist/components/custom/glass-panel/GlassPanel.vue.d.ts +1 -1
  91. package/dist/components/custom/goo-blob/GooBlob.vue.d.ts +4 -2
  92. package/dist/components/custom/goo-blob/composables/uniformBridgeWGPU.d.ts +24 -0
  93. package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +18 -11
  94. package/dist/components/custom/goo-blob/composables/wgpuSetup.d.ts +23 -0
  95. package/dist/components/custom/goo-blob/shaders/metaball.wgsl.d.ts +1 -0
  96. package/dist/components/custom/handmark/HandMark.vue.d.ts +1 -1
  97. package/dist/components/custom/icon-chip/types.d.ts +11 -1
  98. package/dist/components/custom/instrument-chassis/InstrumentChassis.vue.d.ts +3 -1
  99. package/dist/components/custom/metric-stack/MetricStack.vue.d.ts +1 -1
  100. package/dist/components/custom/spa-view/SpaView.vue.d.ts +50 -0
  101. package/dist/components/custom/spa-view/index.d.ts +2 -0
  102. package/dist/components/custom/tabs/SegmentedTabs.vue.d.ts +10 -0
  103. package/dist/components/custom/tabs/composables/useTabDragMorph.d.ts +42 -0
  104. package/dist/components/custom/watercolor-dot/WatercolorDot.vue.d.ts +1 -1
  105. package/dist/components/ui/_shared/menuItemVariants.d.ts +1 -1
  106. package/dist/components/ui/badge/index.d.ts +1 -1
  107. package/dist/components/ui/button/Button.vue.d.ts +5 -2
  108. package/dist/components/ui/button/index.d.ts +2 -2
  109. package/dist/components/ui/card/Card.vue.d.ts +34 -4
  110. package/dist/components/ui/card/CardHeader.vue.d.ts +42 -16
  111. package/dist/components/ui/card/ScrollCard.vue.d.ts +33 -0
  112. package/dist/components/ui/card/ScrollCardHeader.vue.d.ts +17 -0
  113. package/dist/components/ui/card/index.d.ts +2 -0
  114. package/dist/components/ui/carousel/CarouselNext.vue.d.ts +2 -2
  115. package/dist/components/ui/carousel/CarouselPrevious.vue.d.ts +2 -2
  116. package/dist/components/ui/carousel/GlassCarouselPager.vue.d.ts +2 -2
  117. package/dist/components/ui/command/Command.vue.d.ts +1 -1
  118. package/dist/components/ui/drawer/Drawer.vue.d.ts +46 -33
  119. package/dist/components/ui/drawer/DrawerContent.vue.d.ts +7 -8
  120. package/dist/components/ui/drawer/DrawerDescription.vue.d.ts +2 -2
  121. package/dist/components/ui/drawer/DrawerOverlay.vue.d.ts +1 -1
  122. package/dist/components/ui/drawer/DrawerTitle.vue.d.ts +2 -2
  123. package/dist/components/ui/drawer/composables/drawerSnapContext.d.ts +26 -0
  124. package/dist/components/ui/drawer/composables/useDrawerSnap.d.ts +17 -0
  125. package/dist/components/ui/drawer/constants.d.ts +45 -0
  126. package/dist/components/ui/drawer/index.d.ts +23 -15
  127. package/dist/components/ui/focus-scope/FocusScope.vue.d.ts +20 -0
  128. package/dist/components/ui/focus-scope/index.d.ts +2 -0
  129. package/dist/components/ui/index.d.ts +1 -0
  130. package/dist/components/ui/progress/ProgressDefault.vue.d.ts +3 -2
  131. package/dist/components/ui/select/SelectContent.vue.d.ts +1 -1
  132. package/dist/components/ui/sheet/index.d.ts +1 -1
  133. package/dist/components/ui/tags-input/TagsInput.vue.d.ts +2 -2
  134. package/dist/components/ui/toast/Toast.vue.d.ts +5 -2
  135. package/dist/components/ui/toast/use-toast.d.ts +14 -0
  136. package/dist/components/ui/toggle/Toggle.vue.d.ts +1 -1
  137. package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +13 -6
  138. package/dist/composables/glass/index.d.ts +5 -2
  139. package/dist/composables/glass/useSpecularPointer.d.ts +33 -0
  140. package/dist/composables/glass/useSpecularTracking.d.ts +20 -0
  141. package/dist/composables/glass/vSpecular.d.ts +2 -0
  142. package/dist/composables/glass/webgl/shaders/flow.glsl.d.ts +1 -0
  143. package/dist/composables/glass/webgl/useWebGLCanvas.d.ts +16 -0
  144. package/dist/composables/glass/webgpu/index.d.ts +2 -0
  145. package/dist/composables/glass/webgpu/useGpuSubstrate.d.ts +54 -0
  146. package/dist/composables/glass/webgpu/useWebGPUCanvas.d.ts +84 -0
  147. package/dist/composables/motion/core/index.d.ts +1 -0
  148. package/dist/composables/motion/index.d.ts +4 -0
  149. package/dist/composables/motion/useDockCtaReceive.d.ts +59 -0
  150. package/dist/composables/motion/useDragMorph.d.ts +77 -0
  151. package/dist/composables/motion/useLiquidFlex.d.ts +11 -0
  152. package/dist/composables/motion/useLiquidPress.d.ts +71 -0
  153. package/dist/composables/motion/useLiquidReveal.d.ts +51 -0
  154. package/dist/composables/motion/useNumericTransition.d.ts +3 -3
  155. package/dist/composables/motion/usePointerVelocityField.d.ts +98 -0
  156. package/dist/concentric.d.ts +1 -0
  157. package/dist/concentric.js +562 -0
  158. package/dist/configurator.js +3 -2
  159. package/dist/confirm-dialog.js +22 -24
  160. package/dist/constellation.js +11 -11
  161. package/dist/context-menu.js +3 -3
  162. package/dist/controls.js +1 -1
  163. package/dist/createCanvasLifecycle-CHD5XrDW.js +82 -0
  164. package/dist/data-table.js +1 -1
  165. package/dist/dialog.js +2 -2
  166. package/dist/dock.js +564 -457
  167. package/dist/{dockContext-Bu1Avy-a.js → dockContext-BNfyPLe9.js} +1 -1
  168. package/dist/dom.js +5 -5
  169. package/dist/dot-flow-field.d.ts +1 -0
  170. package/dist/dot-flow-field.js +604 -0
  171. package/dist/drawer.d.ts +1 -0
  172. package/dist/drawer.js +380 -0
  173. package/dist/{dropdown-menu-BJ7E9js_.js → dropdown-menu-CofSZuk8.js} +14 -15
  174. package/dist/dropdown-menu.js +1 -1
  175. package/dist/easing.d.ts +1 -0
  176. package/dist/easing.js +463 -0
  177. package/dist/expandable-container.js +7 -4
  178. package/dist/fading-scroll.js +1 -1
  179. package/dist/focus-scope.d.ts +1 -0
  180. package/dist/focus-scope.js +23 -0
  181. package/dist/forms.js +7 -7
  182. package/dist/fourier-field.js +128 -116
  183. package/dist/fourier-math.js +1 -1
  184. package/dist/glass-panel.js +2 -2
  185. package/dist/glass-ui.css +1 -1
  186. package/dist/glass-ui.js +149 -341
  187. package/dist/goo-blob.js +830 -224
  188. package/dist/handmark.js +2 -2
  189. package/dist/header-ribbon.js +1 -1
  190. package/dist/hover-card.js +1 -1
  191. package/dist/hover-popover.js +1 -1
  192. package/dist/icon-chip.js +1 -1
  193. package/dist/icon-tooltip.js +1 -1
  194. package/dist/index.d.ts +1 -1
  195. package/dist/instrument-chassis.js +1 -1
  196. package/dist/label.js +1 -1
  197. package/dist/labeled-field.js +6 -6
  198. package/dist/metric-badge.js +1 -1
  199. package/dist/metric-cell.js +1 -1
  200. package/dist/metric-stack.js +4 -8
  201. package/dist/motion-core.js +36 -36
  202. package/dist/motion-curves.js +1 -1
  203. package/dist/motion.js +157 -55
  204. package/dist/notification.js +1 -1
  205. package/dist/number-field.js +1 -1
  206. package/dist/pager-dots.js +1 -1
  207. package/dist/paper-backdrop.js +1 -1
  208. package/dist/popover.js +1 -1
  209. package/dist/{compile-EXXzEr1q.js → procedural-color.glsl-CIFsJhwI.js} +1 -155
  210. package/dist/procedural-color.wgsl-CToRHugY.js +224 -0
  211. package/dist/progress.js +1 -1
  212. package/dist/pulse.js +1 -1
  213. package/dist/reactive.js +2 -2
  214. package/dist/scrolling-text.js +1 -1
  215. package/dist/search.js +9 -11
  216. package/dist/select.js +3 -3
  217. package/dist/separator.js +1 -1
  218. package/dist/{sheet-CquRYOAw.js → sheet-tZMg0E0w.js} +5 -5
  219. package/dist/sheet.js +1 -1
  220. package/dist/{slider-DQ95MET2.js → slider-DVb4HXU-.js} +3 -3
  221. package/dist/slider.js +1 -1
  222. package/dist/sortable-list.js +3 -3
  223. package/dist/spa-view.d.ts +1 -0
  224. package/dist/spa-view.js +46 -0
  225. package/dist/styles/animations.css +33 -9
  226. package/dist/styles/border-progress.css +109 -0
  227. package/dist/styles/cards.css +76 -0
  228. package/dist/styles/components.css +1 -1
  229. package/dist/styles/critical-partition.mjs +140 -0
  230. package/dist/styles/critical.css +12 -0
  231. package/dist/styles/deferred.css +31 -0
  232. package/dist/styles/dock/density.css +45 -0
  233. package/dist/styles/dock/layers.css +54 -30
  234. package/dist/styles/dock/morph.css +124 -7
  235. package/dist/styles/dock/overflow.css +16 -2
  236. package/dist/styles/dock/shell.css +54 -16
  237. package/dist/styles/dock.css +32 -0
  238. package/dist/styles/drawer.css +81 -35
  239. package/dist/styles/glass/deep.css +44 -0
  240. package/dist/styles/glass/ladder.css +87 -31
  241. package/dist/styles/glass/material.css +78 -15
  242. package/dist/styles/glass/reveal.css +136 -0
  243. package/dist/styles/glass/rim.css +100 -0
  244. package/dist/styles/glass/surfaces.css +101 -3
  245. package/dist/styles/glass-refract.css +96 -44
  246. package/dist/styles/glass-specular-track.css +7 -0
  247. package/dist/styles/glass.css +3 -0
  248. package/dist/styles/hover-popover.css +7 -6
  249. package/dist/styles/icon-chip.css +77 -15
  250. package/dist/styles/index.css +21 -9
  251. package/dist/styles/instrument-chassis.css +66 -3
  252. package/dist/styles/scroll-choreography.css +300 -0
  253. package/dist/styles/segmented-tabs.css +31 -0
  254. package/dist/styles/theme/bridges.css +11 -0
  255. package/dist/styles/theme/literals.css +11 -2
  256. package/dist/styles/theme/radius.css +6 -1
  257. package/dist/styles/tokens/dark-arm.css +98 -26
  258. package/dist/styles/tokens/glass-deep.css +75 -0
  259. package/dist/styles/tokens/glass-fx.css +333 -0
  260. package/dist/styles/tokens/glass.css +39 -319
  261. package/dist/styles/tokens/light-dark.css +15 -2
  262. package/dist/styles/tokens/offsets.css +116 -0
  263. package/dist/styles/tokens/on-glass-fg.css +39 -0
  264. package/dist/styles/tokens/property-regs.css +127 -0
  265. package/dist/styles/tokens/scale-paper.css +117 -89
  266. package/dist/styles/tokens/scheme-motion.css +70 -32
  267. package/dist/styles/tokens/scroll-tokens.css +123 -0
  268. package/dist/styles/tokens/{offsets-sizing.css → sizing.css} +37 -100
  269. package/dist/styles/tokens.css +9 -3
  270. package/dist/styles/typography/semantic.css +8 -8
  271. package/dist/styles/typography/utilities.css +22 -7
  272. package/dist/styles/utilities/animate.css +27 -24
  273. package/dist/styles/utilities/base-misc.css +255 -0
  274. package/dist/styles/utilities/base.css +47 -261
  275. package/dist/styles/utilities/btn.css +3 -1
  276. package/dist/styles/utilities/metal.css +189 -0
  277. package/dist/styles/utilities.css +14 -7
  278. package/dist/switch.js +1 -1
  279. package/dist/tabs.js +220 -107
  280. package/dist/timeline.js +2 -2
  281. package/dist/toast.js +1 -1
  282. package/dist/toggle-group.js +1 -1
  283. package/dist/tooltip.js +1 -1
  284. package/dist/typewriter.js +1 -1
  285. package/dist/useCanvas2D-B8w_E3Rr.js +83 -0
  286. package/dist/useConfiguratorState-DNlOh6c8.js +161 -0
  287. package/dist/useDragMorph-DgxnsVJz.js +102 -0
  288. package/dist/useLiquidPress-kfb4KX79.js +39 -0
  289. package/dist/usePointerVelocityField-JbRd5CrM.js +137 -0
  290. package/dist/useSpring-C0jyvEJB.js +40 -0
  291. package/dist/useSpringMount-q1amq1SJ.js +71 -0
  292. package/dist/useSpringPress-BsqPr7FJ.js +34 -0
  293. package/dist/{useTouchGate-28Tk2-t2.js → useTouchGate-CBZTD4SD.js} +1 -1
  294. package/dist/vSpecular-CQFmLNzA.js +68 -0
  295. package/dist/vendor-CKmzio-i.js +226 -0
  296. package/dist/watercolor-dot.js +2 -2
  297. package/package.json +126 -22
  298. package/src/styles/animations.css +33 -9
  299. package/src/styles/border-progress.css +109 -0
  300. package/src/styles/cards.css +76 -0
  301. package/src/styles/critical-partition.mjs +140 -0
  302. package/src/styles/dock/density.css +45 -0
  303. package/src/styles/dock/layers.css +54 -30
  304. package/src/styles/dock/morph.css +124 -7
  305. package/src/styles/dock/overflow.css +16 -2
  306. package/src/styles/dock/shell.css +54 -16
  307. package/src/styles/dock.css +32 -0
  308. package/src/styles/drawer.css +81 -35
  309. package/src/styles/glass/deep.css +44 -0
  310. package/src/styles/glass/ladder.css +87 -31
  311. package/src/styles/glass/material.css +78 -15
  312. package/src/styles/glass/reveal.css +136 -0
  313. package/src/styles/glass/rim.css +100 -0
  314. package/src/styles/glass/surfaces.css +101 -3
  315. package/src/styles/glass-refract.css +96 -44
  316. package/src/styles/glass-specular-track.css +7 -0
  317. package/src/styles/glass.css +3 -0
  318. package/src/styles/hover-popover.css +7 -6
  319. package/src/styles/icon-chip.css +77 -15
  320. package/src/styles/index.css +12 -0
  321. package/src/styles/instrument-chassis.css +66 -3
  322. package/src/styles/scroll-choreography.css +300 -0
  323. package/src/styles/segmented-tabs.css +31 -0
  324. package/src/styles/theme/bridges.css +11 -0
  325. package/src/styles/theme/literals.css +11 -2
  326. package/src/styles/theme/radius.css +6 -1
  327. package/src/styles/tokens/dark-arm.css +98 -26
  328. package/src/styles/tokens/glass-deep.css +75 -0
  329. package/src/styles/tokens/glass-fx.css +333 -0
  330. package/src/styles/tokens/glass.css +39 -319
  331. package/src/styles/tokens/light-dark.css +15 -2
  332. package/src/styles/tokens/offsets.css +116 -0
  333. package/src/styles/tokens/on-glass-fg.css +39 -0
  334. package/src/styles/tokens/property-regs.css +127 -0
  335. package/src/styles/tokens/scale-paper.css +117 -89
  336. package/src/styles/tokens/scheme-motion.css +70 -32
  337. package/src/styles/tokens/scroll-tokens.css +123 -0
  338. package/src/styles/tokens/{offsets-sizing.css → sizing.css} +37 -100
  339. package/src/styles/tokens.css +9 -3
  340. package/src/styles/typography/semantic.css +8 -8
  341. package/src/styles/typography/utilities.css +22 -7
  342. package/src/styles/utilities/animate.css +27 -24
  343. package/src/styles/utilities/base-misc.css +255 -0
  344. package/src/styles/utilities/base.css +47 -261
  345. package/src/styles/utilities/btn.css +3 -1
  346. package/src/styles/utilities/metal.css +189 -0
  347. package/src/styles/utilities.css +14 -7
  348. package/dist/CardAction-XH4YBVEK.js +0 -158
  349. package/dist/Notification-DKjKsQq8.js +0 -100
  350. package/dist/NumberFieldContent-1VtwDSgf.js +0 -145
  351. package/dist/Toaster-Bm_HQSpc.js +0 -263
  352. package/dist/check-njhk-vfu.js +0 -7
  353. package/dist/chevron-down-DKkxb4VV.js +0 -7
  354. package/dist/chevron-right-CPXbqQQz.js +0 -7
  355. package/dist/chevron-up-n_24SqI-.js +0 -7
  356. package/dist/createLucideIcon-DydS2qgk.js +0 -43
  357. package/dist/minimize-2-BV31Ob_h.js +0 -38
  358. package/dist/search-BSGrf2Sk.js +0 -12
  359. package/dist/useCanvas2D-ClrQuZtp.js +0 -96
  360. package/dist/useConfiguratorState-kiIlun8I.js +0 -303
  361. package/dist/useSpecularTracking-Bo5su68i.js +0 -32
  362. package/dist/useSpringMount-CvopqiFr.js +0 -108
  363. package/dist/useViewTransition-COsPjQVZ.js +0 -47
  364. package/dist/x-BUwTKe50.js +0 -10
  365. /package/dist/{CollapsibleContent-C_s6fG7r.js → CollapsibleContent-68dEXIAl.js} +0 -0
  366. /package/dist/{InstrumentChassis-UUJX0-mW.js → InstrumentChassis-Bx0F_q5g.js} +0 -0
  367. /package/dist/{Label-Ck9SWEKM.js → Label-C2ejNWeC.js} +0 -0
  368. /package/dist/{ModalOverlay-CkPRRgl_.js → ModalOverlay-D1VrWyoi.js} +0 -0
  369. /package/dist/{PaperBackdrop-nBKe0vkB.js → PaperBackdrop-Vf3tlpvd.js} +0 -0
  370. /package/dist/{SelectGroup-Dcczq44n.js → SelectGroup-DwwjicPY.js} +0 -0
  371. /package/dist/{SelectSeparator-DK2y1Bun.js → SelectSeparator-DXswVdRz.js} +0 -0
  372. /package/dist/{Separator-BArl4OaB.js → Separator-pKF0oA4M.js} +0 -0
  373. /package/dist/{Switch-Dr--uLGH.js → Switch-CwYNf8bo.js} +0 -0
  374. /package/dist/{_plugin-vue_export-helper-CBt_VvXa.js → _plugin-vue_export-helper-C1je1s0u.js} +0 -0
  375. /package/dist/{badge-UILT_3pZ.js → badge-D9XWW4GT.js} +0 -0
  376. /package/dist/{createContext-B2RaZ-b8.js → createContext-CpBNTRMI.js} +0 -0
  377. /package/dist/{math-DeuJwLFx.js → math-BINYmg5z.js} +0 -0
  378. /package/dist/{menuItemVariants-DWRQXBxJ.js → menuItemVariants-D8VNHq22.js} +0 -0
  379. /package/dist/{prng-Cg22zv8g.js → prng-Za0Rmgag.js} +0 -0
  380. /package/dist/{resolveCanvasColor-BBiYfp8g.js → resolveCanvasColor-CUyHr5Uu.js} +0 -0
  381. /package/dist/{springPresets-WXaLbu8u.js → springPresets-CFQGQfdl.js} +0 -0
  382. /package/dist/{useAnimatedNumber-C_3wZLx4.js → useAnimatedNumber-BfUq2bUM.js} +0 -0
  383. /package/dist/{useInterval-CNdua40W.js → useInterval-CvK1mL92.js} +0 -0
  384. /package/dist/{useLiquidFlex-C12hXwOZ.js → useLiquidFlex-mZPMiNXh.js} +0 -0
  385. /package/dist/{useRAFLoop-CdwJ7284.js → useRAFLoop-UYa4zoiN.js} +0 -0
  386. /package/dist/{useResizeObserver-V6nq5gJb.js → useResizeObserver-Bpoi7tRE.js} +0 -0
  387. /package/dist/{useResolveTokenColor-NUG2zYAW.js → useResolveTokenColor-vAylyR0x.js} +0 -0
  388. /package/dist/{useSortable-BVqlJRBR.js → useSortable-DHjtZSEd.js} +0 -0
  389. /package/dist/{useSurfaceAxis-CMnF2zHb.js → useSurfaceAxis-g0NloYEF.js} +0 -0
  390. /package/dist/{useTextHighlight-NmBM5V_4.js → useTextHighlight-CWEi25hf.js} +0 -0
  391. /package/dist/{useTimer-B_PAasDL.js → useTimer-BHJOKr35.js} +0 -0
  392. /package/dist/{useUserInvalidAria-CDfJuakL.js → useUserInvalidAria-y5namzeE.js} +0 -0
  393. /package/dist/{useViewportReady-CvBcCYDf.js → useViewportReady-anbac3m6.js} +0 -0
  394. /package/dist/{vReveal-D9k0jADx.js → vReveal-Czsb7zQC.js} +0 -0
package/dist/goo-blob.js CHANGED
@@ -1,13 +1,15 @@
1
- import { t as e } from "./_plugin-vue_export-helper-CBt_VvXa.js";
2
- import { t } from "./useResolveTokenColor-NUG2zYAW.js";
3
- import { a as n, c as r, n as i, o as a, r as o, s, t as c } from "./compile-EXXzEr1q.js";
4
- import { s as l, t as u } from "./color-DweYl7pE.js";
5
- import { t as d } from "./useIntersectionPause-Bj4sPkMh.js";
6
- import { n as f, t as p } from "./prng-Cg22zv8g.js";
7
- import { computed as m, createElementBlock as h, createElementVNode as g, defineComponent as _, inject as v, normalizeStyle as y, onScopeDispose as b, onUnmounted as x, openBlock as S, readonly as C, ref as w, useTemplateRef as T, watch as E } from "vue";
8
- import { SpringProgress as D } from "@mkbabb/keyframes.js";
1
+ import { t as e } from "./_plugin-vue_export-helper-C1je1s0u.js";
2
+ import { t } from "./useResolveTokenColor-vAylyR0x.js";
3
+ import { n, r, t as i } from "./procedural-color.glsl-CIFsJhwI.js";
4
+ import { a, n as o, r as s, t as c, u as l } from "./procedural-color.wgsl-CToRHugY.js";
5
+ import { n as u, t as d } from "./compile-CQEFRTmx.js";
6
+ import { s as f, t as p } from "./color-DweYl7pE.js";
7
+ import { t as m } from "./useIntersectionPause-Bj4sPkMh.js";
8
+ import { n as h, t as g } from "./prng-Za0Rmgag.js";
9
+ import { computed as _, createElementBlock as v, createElementVNode as y, defineComponent as b, inject as x, normalizeStyle as S, onScopeDispose as C, onUnmounted as w, openBlock as T, readonly as E, ref as D, useTemplateRef as O, watch as k } from "vue";
10
+ import { SpringProgress as A } from "@mkbabb/keyframes.js";
9
11
  //#region src/components/custom/goo-blob/types.ts
10
- var O = {
12
+ var j = {
11
13
  geometry: {
12
14
  canvasSize: 200,
13
15
  bodyRadius: .22,
@@ -71,7 +73,7 @@ var O = {
71
73
  },
72
74
  quality: "full",
73
75
  tempo: 1
74
- }, k = Symbol("blobConfig"), A = 1 / 1.6, j = {
76
+ }, M = Symbol("blobConfig"), N = 1 / 1.6, P = {
75
77
  idle: {
76
78
  valence: 0,
77
79
  arousal: .35
@@ -92,50 +94,50 @@ var O = {
92
94
  valence: .7,
93
95
  arousal: 1
94
96
  }
95
- }, M = (e, t, n) => e + (t - e) * n;
96
- function N({ valence: e, arousal: t }) {
97
+ }, F = (e, t, n) => e + (t - e) * n;
98
+ function I({ valence: e, arousal: t }) {
97
99
  return {
98
- orbitSpeedScale: M(.4, 2.2, t),
99
- wobbleScale: M(.5, 2, t),
100
- pulseFreq: M(.15, 1.5, t),
101
- pulseAmp: M(.008, .05, t),
102
- noiseAmp: M(.015, .045, t),
103
- hueRange: M(3, 25, t * .6 + (e * .5 + .5) * .4),
104
- satShift: M(-.05, .1, e * .5 + .5),
105
- brightnessShift: M(-.03, .08, e * .5 + .5),
106
- smoothK: M(.85, 1.35, t),
107
- pointerAttraction: M(-.2, .6, e * .5 + .5) * (.7 + .15 * t),
108
- mergeRate: M(2, .3, t),
109
- iridScale: M(.55, 1.35, t)
100
+ orbitSpeedScale: F(.4, 2.2, t),
101
+ wobbleScale: F(.5, 2, t),
102
+ pulseFreq: F(.15, 1.5, t),
103
+ pulseAmp: F(.008, .05, t),
104
+ noiseAmp: F(.015, .045, t),
105
+ hueRange: F(3, 25, t * .6 + (e * .5 + .5) * .4),
106
+ satShift: F(-.05, .1, e * .5 + .5),
107
+ brightnessShift: F(-.03, .08, e * .5 + .5),
108
+ smoothK: F(.85, 1.35, t),
109
+ pointerAttraction: F(-.2, .6, e * .5 + .5) * (.7 + .15 * t),
110
+ mergeRate: F(2, .3, t),
111
+ iridScale: F(.55, 1.35, t)
110
112
  };
111
113
  }
112
- var P = {
113
- idle: N(j.idle),
114
- happy: N(j.happy),
115
- curious: N(j.curious),
116
- sleepy: N(j.sleepy),
117
- excited: N(j.excited)
118
- }, F = {
114
+ var L = {
115
+ idle: I(P.idle),
116
+ happy: I(P.happy),
117
+ curious: I(P.curious),
118
+ sleepy: I(P.sleepy),
119
+ excited: I(P.excited)
120
+ }, R = {
119
121
  idle: 1500,
120
122
  happy: 1200,
121
123
  curious: 800,
122
124
  sleepy: 2500,
123
125
  excited: 600
124
- }, I = 6e3, L = .35, R = .85, z = .2, B = .015, V = .02, H = .01, U = .015, W = .75, ee = 2e3, te = /* @__PURE__ */ "uResolution.uTime.uBaseColor.uPointer.uPointerActive.uPointerAttraction.uPointerStrength.uBodyRadius.uPulsePhase.uPulseAmp.uNoiseAmp.uNoiseFreq.uNoiseSpeed.uWarpAmp.uSmoothK.uMerge.uMaxReach.uLit.uRimColor.uLightDir.uSpecStrength.uSpecShininess.uRimPower.uRimStrength.uIridescence.uIridHue.uIridSpeed.uSssScale.uSssPower.uCoreGlow.uHueRange.uSatShift.uBrightnessShift.uColorNoiseFreq.uColorNoiseSpeed.uStopCount.uSatCount.uTrailCount.uVelocity.uStretch".split(".");
126
+ }, z = 6e3, B = .35, V = .85, H = .2, U = .015, W = .02, ee = .01, te = .015, G = .75, ne = 2e3, re = /* @__PURE__ */ "uResolution.uTime.uBaseColor.uPointer.uPointerActive.uPointerAttraction.uPointerStrength.uBodyRadius.uPulsePhase.uPulseAmp.uNoiseAmp.uNoiseFreq.uNoiseSpeed.uWarpAmp.uSmoothK.uMerge.uMaxReach.uLit.uRimColor.uLightDir.uSpecStrength.uSpecShininess.uRimPower.uRimStrength.uIridescence.uIridHue.uIridSpeed.uSssScale.uSssPower.uCoreGlow.uHueRange.uSatShift.uBrightnessShift.uColorNoiseFreq.uColorNoiseSpeed.uStopCount.uSatCount.uTrailCount.uVelocity.uStretch".split(".");
125
127
  //#endregion
126
128
  //#region src/components/custom/goo-blob/composables/easing.ts
127
- function ne(e) {
129
+ function ie(e) {
128
130
  return e < .5 ? 2 * e * e : 1 - 2 * (1 - e) * (1 - e);
129
131
  }
130
- function re(e) {
132
+ function ae(e) {
131
133
  return e * e;
132
134
  }
133
- function ie(e) {
135
+ function oe(e) {
134
136
  return 1 - (1 - e) * (1 - e);
135
137
  }
136
138
  //#endregion
137
139
  //#region src/components/custom/goo-blob/composables/useBlobMood.ts
138
- function ae(e, t, n) {
140
+ function se(e, t, n) {
139
141
  let r = (e, t) => e + (t - e) * n;
140
142
  return {
141
143
  orbitSpeedScale: r(e.orbitSpeedScale, t.orbitSpeedScale),
@@ -152,10 +154,10 @@ function ae(e, t, n) {
152
154
  iridScale: r(e.iridScale, t.iridScale)
153
155
  };
154
156
  }
155
- function G() {
156
- let e = w("idle"), t = w({ ...P.idle }), n = { ...P.idle }, r = { ...P.idle }, i = 0, a = 0, o = !1, s = 0, c = !1, l = !1, u = 0;
157
+ function ce() {
158
+ let e = D("idle"), t = D({ ...L.idle }), n = { ...L.idle }, r = { ...L.idle }, i = 0, a = 0, o = !1, s = 0, c = !1, l = !1, u = 0;
157
159
  function d(s, l = {}) {
158
- (l.source ?? "manual") === "manual" && (c = !0), !(s === e.value && !o) && (e.value = s, n = { ...t.value }, r = P[s], a = F[s], i = 0, o = !0);
160
+ (l.source ?? "manual") === "manual" && (c = !0), !(s === e.value && !o) && (e.value = s, n = { ...t.value }, r = L[s], a = R[s], i = 0, o = !0);
159
161
  }
160
162
  function f(e) {
161
163
  u = e.idleMs;
@@ -172,17 +174,17 @@ function G() {
172
174
  return o || s > 0 || c && e.value !== "idle" ? !1 : !(!c && e.value !== "sleepy" && u < 6e3);
173
175
  }
174
176
  function m() {
175
- return o || s > 0 || c || e.value === "sleepy" ? Infinity : Math.max(0, I - u);
177
+ return o || s > 0 || c || e.value === "sleepy" ? Infinity : Math.max(0, z - u);
176
178
  }
177
179
  function h(e) {
178
180
  if (s > 0 && (s = Math.max(0, s - e)), !o) return;
179
181
  i += e;
180
- let c = Math.min(i / a, 1), l = ne(c);
181
- t.value = ae(n, r, l), c >= 1 && (o = !1);
182
+ let c = Math.min(i / a, 1), l = ie(c);
183
+ t.value = se(n, r, l), c >= 1 && (o = !1);
182
184
  }
183
185
  return {
184
- currentMood: C(e),
185
- params: C(t),
186
+ currentMood: E(e),
187
+ params: E(t),
186
188
  setMood: d,
187
189
  update: f,
188
190
  tick: h,
@@ -192,17 +194,17 @@ function G() {
192
194
  }
193
195
  //#endregion
194
196
  //#region src/components/custom/goo-blob/composables/useBlobPointer.ts
195
- function K(e) {
196
- let t = w({
197
+ function le(e) {
198
+ let t = D({
197
199
  x: 0,
198
200
  y: 0
199
- }), n = w({
201
+ }), n = D({
200
202
  x: 0,
201
203
  y: 0
202
- }), r = w(!1), i = w(0), a = 0, o = 0, s = {
204
+ }), r = D(!1), i = D(0), a = 0, o = 0, s = {
203
205
  response: .18,
204
206
  dampingFraction: 1
205
- }, c = new D(s), l = new D(s), u = Array.from({ length: 15 }, () => ({
207
+ }, c = new A(s), l = new A(s), u = Array.from({ length: 15 }, () => ({
206
208
  x: 0,
207
209
  y: 0,
208
210
  radius: 0
@@ -217,14 +219,14 @@ function K(e) {
217
219
  r.value = !1;
218
220
  }
219
221
  let _ = null;
220
- E(e, (e) => {
222
+ k(e, (e) => {
221
223
  _ &&= (_(), null), e && (e.addEventListener("pointermove", h), e.addEventListener("pointerleave", g), _ = () => {
222
224
  e.removeEventListener("pointermove", h), e.removeEventListener("pointerleave", g);
223
225
  });
224
- }, { immediate: !0 }), x(() => _?.());
226
+ }, { immediate: !0 }), w(() => _?.());
225
227
  function v(e = 16) {
226
228
  let s = Math.min(e, 50), h = s / 1e3;
227
- r.value ? O = 0 : O += Math.min(e, 50);
229
+ r.value ? C = 0 : C += Math.min(e, 50);
228
230
  let g = r.value ? a : 0, _ = r.value ? o : 0;
229
231
  if (c.target = g, l.target = _, c.tickDt(s), l.tickDt(s), t.value = {
230
232
  x: c.value,
@@ -233,7 +235,7 @@ function K(e) {
233
235
  x: c.velocity,
234
236
  y: l.velocity
235
237
  }, p !== 0 || m !== 0) {
236
- let e = -324 * p - 2 * L * 18 * m;
238
+ let e = -324 * p - 2 * B * 18 * m;
237
239
  m += e * h, p += m * h, p < -.9 && (p = -.9), Math.abs(p) < 1e-4 && Math.abs(m) < 1e-4 && (p = 0, m = 0);
238
240
  }
239
241
  i.value = p;
@@ -256,15 +258,15 @@ function K(e) {
256
258
  };
257
259
  }
258
260
  let b = !1;
259
- function S(e) {
261
+ function x(e) {
260
262
  m += e * 18, b = !0;
261
263
  }
262
- function T() {
264
+ function S() {
263
265
  let e = b;
264
266
  return b = !1, e;
265
267
  }
266
- let O = 0;
267
- function k() {
268
+ let C = 0;
269
+ function T() {
268
270
  c.reset(0, 0), l.reset(0, 0), c.target = 0, l.target = 0, t.value = {
269
271
  x: 0,
270
272
  y: 0
@@ -273,36 +275,36 @@ function K(e) {
273
275
  y: 0
274
276
  }, p = 0, m = 0, i.value = 0, f = 0;
275
277
  }
276
- function A() {
278
+ function O() {
277
279
  return !(r.value || Math.hypot(c.velocity, l.velocity) > .001 || Math.abs(c.value) > .001 || Math.abs(l.value) > .001 || f > 0 || p !== 0 || m !== 0);
278
280
  }
279
281
  return {
280
- pointer: C(t),
281
- velocity: C(n),
282
- active: C(r),
283
- pulse: C(i),
284
- idleMs: () => O,
282
+ pointer: E(t),
283
+ velocity: E(n),
284
+ active: E(r),
285
+ pulse: E(i),
286
+ idleMs: () => C,
285
287
  tick: v,
286
288
  trailSources: y,
287
- click: S,
288
- consumeClick: T,
289
- rest: k,
290
- isAtRest: A
289
+ click: x,
290
+ consumeClick: S,
291
+ rest: T,
292
+ isAtRest: O
291
293
  };
292
294
  }
293
295
  //#endregion
294
296
  //#region src/components/custom/goo-blob/composables/useBlobSatellites.ts
295
- function q(e, t, n) {
297
+ function K(e, t, n) {
296
298
  return t + e() * (n - t);
297
299
  }
298
- function J(e) {
300
+ function q(e) {
299
301
  return e < 0 ? 0 : e > 1 ? 1 : e;
300
302
  }
301
- function Y(e, t, n) {
303
+ function J(e, t, n) {
302
304
  return e + (t - e) * n;
303
305
  }
304
- function oe(e, t, n, r, i) {
305
- let a = t / 4 * Math.PI * 2 + (e() - .5) * Math.PI, o = n * (R + e() * z), s = r * (.3 + e() * .7);
306
+ function ue(e, t, n, r, i) {
307
+ let a = t / 4 * Math.PI * 2 + (e() - .5) * Math.PI, o = n * (V + e() * H), s = r * (.3 + e() * .7);
306
308
  return {
307
309
  phase: "orbiting",
308
310
  phaseStart: i,
@@ -312,9 +314,9 @@ function oe(e, t, n, r, i) {
312
314
  phaseOffset: a,
313
315
  baseRadiusX: o * (1 - s),
314
316
  baseRadiusY: o * (1 + s),
315
- wobbleAmp1: B + e() * V,
317
+ wobbleAmp1: U + e() * W,
316
318
  wobbleFreq1: .08 + e() * .22,
317
- wobbleAmp2: H + e() * U,
319
+ wobbleAmp2: ee + e() * te,
318
320
  wobbleFreq2: .18 + e() * .32,
319
321
  pertXAmp: .03 + e() * .05,
320
322
  pertXFreq: .06 + e() * .18,
@@ -328,23 +330,23 @@ function oe(e, t, n, r, i) {
328
330
  endY: 0
329
331
  };
330
332
  }
331
- function X(e, t, n, r) {
333
+ function Y(e, t, n, r) {
332
334
  e.phase = t, e.phaseStart = n, e.phaseDuration = r;
333
335
  }
334
- function Z(e, t, n, r) {
336
+ function X(e, t, n, r) {
335
337
  let i = (t - e.timeOrigin) / 1e3, a = e.angularSpeed * n * i + e.phaseOffset, o = r * (e.wobbleAmp1 * Math.sin(e.wobbleFreq1 * i) + e.wobbleAmp2 * Math.sin(e.wobbleFreq2 * i + 1.3));
336
338
  return {
337
339
  x: (e.baseRadiusX + o) * Math.cos(a) + e.pertXAmp * Math.sin(e.pertXFreq * i + e.pertXPhase),
338
340
  y: (e.baseRadiusY + o) * Math.sin(a) + e.pertYAmp * Math.cos(e.pertYFreq * i + e.pertYPhase)
339
341
  };
340
342
  }
341
- function se(e, t, n, r, i) {
343
+ function de(e, t, n, r, i) {
342
344
  e.timeOrigin = i, e.angularSpeed = .08 + t() * .16, e.phaseOffset = t() * Math.PI * 2;
343
- let a = n * (R + t() * z), o = r * (.3 + t() * .7);
344
- e.baseRadiusX = a * (1 - o), e.baseRadiusY = a * (1 + o), e.wobbleAmp1 = B + t() * V, e.wobbleFreq1 = .08 + t() * .22, e.wobbleAmp2 = H + t() * U, e.wobbleFreq2 = .18 + t() * .32, e.pertXAmp = .03 + t() * .05, e.pertXFreq = .06 + t() * .18, e.pertXPhase = t() * Math.PI * 2, e.pertYAmp = .03 + t() * .05, e.pertYFreq = .07 + t() * .17, e.pertYPhase = t() * Math.PI * 2;
345
+ let a = n * (V + t() * H), o = r * (.3 + t() * .7);
346
+ e.baseRadiusX = a * (1 - o), e.baseRadiusY = a * (1 + o), e.wobbleAmp1 = U + t() * W, e.wobbleFreq1 = .08 + t() * .22, e.wobbleAmp2 = ee + t() * te, e.wobbleFreq2 = .18 + t() * .32, e.pertXAmp = .03 + t() * .05, e.pertXFreq = .06 + t() * .18, e.pertXPhase = t() * Math.PI * 2, e.pertYAmp = .03 + t() * .05, e.pertYFreq = .07 + t() * .17, e.pertYPhase = t() * Math.PI * 2;
345
347
  }
346
- function ce(e, t) {
347
- let n = f(p(t + "goo")), r = [], i = [], a = [], o = -Infinity, s = e.geometry.orbitRadius;
348
+ function fe(e, t) {
349
+ let n = h(g(t + "goo")), r = [], i = [], a = [], o = -Infinity, s = e.geometry.orbitRadius;
348
350
  function c() {
349
351
  let t = e.geometry.orbitRadius;
350
352
  if (t !== s && s > 0) {
@@ -355,7 +357,7 @@ function ce(e, t) {
355
357
  }
356
358
  function l(t) {
357
359
  let o = e.geometry.satelliteCount;
358
- for (; r.length < o;) r.push(oe(n, r.length, e.geometry.orbitRadius, e.geometry.eccentricity, t)), i.push({
360
+ for (; r.length < o;) r.push(ue(n, r.length, e.geometry.orbitRadius, e.geometry.eccentricity, t)), i.push({
359
361
  x: 0,
360
362
  y: 0,
361
363
  radius: e.geometry.satelliteRadius,
@@ -371,62 +373,62 @@ function ce(e, t) {
371
373
  l(t), c();
372
374
  let u = r.length, d = s.mergeRate, f = e.geometry.satelliteRadius, p = e.satellites.mergeDuration, m = e.satellites.emergeDuration, h = e.satellites.orbitDuration, g = e.satellites.absorbedDuration;
373
375
  for (let c = 0; c < u; c++) {
374
- let l = r[c], u = t - l.phaseStart, _ = J(l.phaseDuration > 0 ? u / l.phaseDuration : 1);
376
+ let l = r[c], u = t - l.phaseStart, _ = q(l.phaseDuration > 0 ? u / l.phaseDuration : 1);
375
377
  switch (l.phase) {
376
378
  case "orbiting":
377
- if (_ >= 1) if (t - o < 3e3 * d) l.phaseStart = t, l.phaseDuration = q(n, h[0], h[1]);
379
+ if (_ >= 1) if (t - o < 3e3 * d) l.phaseStart = t, l.phaseDuration = K(n, h[0], h[1]);
378
380
  else {
379
- let e = Z(l, t, s.orbitSpeedScale, s.wobbleScale);
381
+ let e = X(l, t, s.orbitSpeedScale, s.wobbleScale);
380
382
  l.startX = e.x, l.startY = e.y;
381
383
  let n = Math.hypot(e.x, e.y), r = n > .01 ? .08 / n : 0;
382
- l.endX = e.x * r, l.endY = e.y * r, X(l, "merging", t, p), o = t;
384
+ l.endX = e.x * r, l.endY = e.y * r, Y(l, "merging", t, p), o = t;
383
385
  }
384
386
  break;
385
387
  case "merging":
386
- _ >= 1 && X(l, "absorbed", t, q(n, g[0], g[1]));
388
+ _ >= 1 && Y(l, "absorbed", t, K(n, g[0], g[1]));
387
389
  break;
388
390
  case "absorbed":
389
391
  if (_ >= 1) {
390
- se(l, n, e.geometry.orbitRadius, e.geometry.eccentricity, t);
391
- let r = Z(l, t + 3e3, s.orbitSpeedScale, s.wobbleScale);
392
+ de(l, n, e.geometry.orbitRadius, e.geometry.eccentricity, t);
393
+ let r = X(l, t + 3e3, s.orbitSpeedScale, s.wobbleScale);
392
394
  l.endX = r.x, l.endY = r.y;
393
395
  let i = Math.hypot(r.x, r.y), a = i > .01 ? .08 / i : 0;
394
- l.startX = r.x * a, l.startY = r.y * a, X(l, "emerging", t, m);
396
+ l.startX = r.x * a, l.startY = r.y * a, Y(l, "emerging", t, m);
395
397
  }
396
398
  break;
397
399
  case "emerging":
398
400
  if (_ >= 1) {
399
401
  let e = a[c];
400
- e.x = l.endX, e.y = l.endY, e.start = t, l.timeOrigin = t, l.phaseOffset = Math.atan2(l.endY, l.endX), X(l, "orbiting", t, q(n, h[0], h[1]));
402
+ e.x = l.endX, e.y = l.endY, e.start = t, l.timeOrigin = t, l.phaseOffset = Math.atan2(l.endY, l.endX), Y(l, "orbiting", t, K(n, h[0], h[1]));
401
403
  }
402
404
  break;
403
405
  }
404
406
  let v, y, b, x;
405
407
  switch (l.phase) {
406
408
  case "orbiting": {
407
- let e = Z(l, t, s.orbitSpeedScale, s.wobbleScale), n = a[c], r = t - n.start;
409
+ let e = X(l, t, s.orbitSpeedScale, s.wobbleScale), n = a[c], r = t - n.start;
408
410
  if (n.start > 0 && r < 2e3) {
409
- let t = J(r / ee), i = t * t * (3 - 2 * t);
410
- v = Y(n.x, e.x, i), y = Y(n.y, e.y, i);
411
+ let t = q(r / ne), i = t * t * (3 - 2 * t);
412
+ v = J(n.x, e.x, i), y = J(n.y, e.y, i);
411
413
  } else v = e.x, y = e.y;
412
- b = W, x = 1;
414
+ b = G, x = 1;
413
415
  break;
414
416
  }
415
417
  case "merging": {
416
- let e = re(J((t - l.phaseStart) / l.phaseDuration));
417
- v = Y(l.startX, l.endX, e), y = Y(l.startY, l.endY, e);
418
- let n = J((t - l.phaseStart) / l.phaseDuration);
419
- b = n > .85 ? Y(W, 0, (n - .85) / .15) : W, x = Y(1, .65, e);
418
+ let e = ae(q((t - l.phaseStart) / l.phaseDuration));
419
+ v = J(l.startX, l.endX, e), y = J(l.startY, l.endY, e);
420
+ let n = q((t - l.phaseStart) / l.phaseDuration);
421
+ b = n > .85 ? J(G, 0, (n - .85) / .15) : G, x = J(1, .65, e);
420
422
  break;
421
423
  }
422
424
  case "absorbed":
423
425
  v = 0, y = 0, b = 0, x = .5;
424
426
  break;
425
427
  case "emerging": {
426
- let e = ie(J((t - l.phaseStart) / l.phaseDuration));
427
- v = Y(l.startX, l.endX, e), y = Y(l.startY, l.endY, e);
428
- let n = J((t - l.phaseStart) / l.phaseDuration);
429
- b = n < .1 ? Y(0, W, n / .1) : W, x = Y(.65, 1, e);
428
+ let e = oe(q((t - l.phaseStart) / l.phaseDuration));
429
+ v = J(l.startX, l.endX, e), y = J(l.startY, l.endY, e);
430
+ let n = q((t - l.phaseStart) / l.phaseDuration);
431
+ b = n < .1 ? J(0, G, n / .1) : G, x = J(.65, 1, e);
430
432
  break;
431
433
  }
432
434
  }
@@ -437,14 +439,14 @@ function ce(e, t) {
437
439
  function d() {
438
440
  for (let e of r) e.phaseOffset += (n() - .5) * .6, e.pertXPhase += n() * Math.PI, e.pertYPhase += n() * Math.PI;
439
441
  }
440
- function m(e) {
441
- n = f(p(e + "goo"));
442
+ function f(e) {
443
+ n = h(g(e + "goo"));
442
444
  }
443
- function h() {
445
+ function p() {
444
446
  for (let e of r) if (e.phase !== "orbiting") return !1;
445
447
  return !0;
446
448
  }
447
- function g(e) {
449
+ function m(e) {
448
450
  let t = Infinity;
449
451
  for (let e of r) {
450
452
  let n = e.phaseStart + e.phaseDuration;
@@ -456,14 +458,14 @@ function ce(e, t) {
456
458
  sources: i,
457
459
  tick: u,
458
460
  nudge: d,
459
- reseed: m,
460
- isQuiescent: h,
461
- nextEventMs: g
461
+ reseed: f,
462
+ isQuiescent: p,
463
+ nextEventMs: m
462
464
  };
463
465
  }
464
466
  //#endregion
465
467
  //#region src/components/custom/goo-blob/shaders/metaball.vert.ts
466
- var le = "#version 300 es\n\nin vec2 aPosition;\nout vec2 vUv;\n\nvoid main() {\n vUv = 0.5 * (aPosition + 1.0);\n gl_Position = vec4(aPosition, 0.0, 1.0);\n}", ue = "// --- SDF ---\n\nfloat sdCircle(vec2 p, vec2 center, float radius) {\n return length(p - center) - radius;\n}\n\n// AX.W15 — the VALUE+GRADIENT circle: returns vec3(d, dd/dx, dd/dy). The circle\n// gradient is the UNIT radial direction (p - center)/length(p - center) (the\n// eikonal property holds EXACTLY for a circle), guarded at the degenerate centre.\n// This is the keystone of the analytic-gradient smin: carrying the gradient\n// through the scene field DELETES the per-pixel 4-tap normal (4 full sceneDist\n// evals, each running 3-octave FBM + the sat/trail loops).\nvec3 sdgCircle(vec2 p, vec2 center, float radius) {\n vec2 d = p - center;\n float len = length(d);\n vec2 grad = len > 1e-6 ? d / len : vec2(0.0, 1.0);\n return vec3(len - radius, grad);\n}\n\n// IQ 2024 quadratic-polynomial smin, NORMALIZED so k is a blend band in distance\n// units. The k *= 4.0 pre-scale makes the h*h*k*0.25 poly's effective blend width\n// equal to k (the un-normalized form's width was 0.25*k).\nfloat sminQuadratic(float a, float b, float k) {\n k *= 4.0;\n float h = max(k - abs(a - b), 0.0) / k;\n return min(a, b) - h * h * k * 0.25;\n}\n\n// IQ circular smin — a quarter-circle fillet at the seam (rounder than the\n// quadratic). The k *= 1.0/(1.0-sqrt(0.5)) factor normalizes k to the same\n// blend-band units so the merge axis swaps shape without re-tuning uSmoothK.\nfloat sminCircular(float a, float b, float k) {\n k *= 1.0 / (1.0 - sqrt(0.5));\n float h = max(k - abs(a - b), 0.0) / k;\n return min(a, b) - k * 0.5 * (1.0 + h - sqrt(1.0 - h * (h - 2.0)));\n}\n\n// Config-gated merge dispatch — uMerge 0 = quadratic, 1 = circular.\nfloat smin(float a, float b, float k) {\n if (uMerge > 0.5) return sminCircular(a, b, k);\n return sminQuadratic(a, b, k);\n}\n\n// AX.W15 — the VALUE+GRADIENT smin. a/b are vec3(dist, grad.xy); the blended\n// gradient is mix(a.yz, b.yz, w) with the interpolation weight w the distance\n// blend uses (IQ's vec2-smin insight). This propagates the analytic gradient\n// through the merge so the surface normal reads the field gradient DIRECTLY — no\n// finite-difference tap. The blend weight matches each variant.\nvec3 sminQuadraticG(vec3 a, vec3 b, float k) {\n k *= 4.0;\n float h = max(k - abs(a.x - b.x), 0.0) / k;\n float m = h * h * k * 0.25;\n // h-weight that selects which surface dominates (0 → a, 1 → b).\n float w = (a.x < b.x) ? 0.5 * h * h : 1.0 - 0.5 * h * h;\n float dist = min(a.x, b.x) - m;\n vec2 grad = mix(a.yz, b.yz, w);\n return vec3(dist, grad);\n}\n\nvec3 sminCircularG(vec3 a, vec3 b, float k) {\n k *= 1.0 / (1.0 - sqrt(0.5));\n float h = max(k - abs(a.x - b.x), 0.0) / k;\n float m = k * 0.5 * (1.0 + h - sqrt(1.0 - h * (h - 2.0)));\n float w = (a.x < b.x) ? 0.5 * h : 1.0 - 0.5 * h;\n float dist = min(a.x, b.x) - m;\n vec2 grad = mix(a.yz, b.yz, w);\n return vec3(dist, grad);\n}\n\nvec3 sminG(vec3 a, vec3 b, float k) {\n if (uMerge > 0.5) return sminCircularG(a, b, k);\n return sminQuadraticG(a, b, k);\n}", de = "// --- Noise ---\n\nfloat hash21(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * 0.1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\n// IQ analytic-derivative gradient noise — value in .x, ANALYTIC gradient\n// (d/dx, d/dy) in .yz, from one eval. The quintic fade has a continuous 2nd\n// derivative so the gradient is smooth (a cubic smoothstep fade would crease it).\n// (Inigo Quilez, https://iquilezles.org/articles/gradientnoise/.)\nvec3 noised(vec2 p) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n\n // Quintic fade + its derivative.\n vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);\n vec2 du = 30.0 * f * f * (f * (f - 2.0) + 1.0);\n\n // Per-corner gradient vectors from the hash (centred to [-1, 1]).\n vec2 ga = -1.0 + 2.0 * vec2(hash21(i + vec2(0.0, 0.0)), hash21(i + vec2(7.3, 0.0)));\n vec2 gb = -1.0 + 2.0 * vec2(hash21(i + vec2(1.0, 0.0)), hash21(i + vec2(8.3, 0.0)));\n vec2 gc = -1.0 + 2.0 * vec2(hash21(i + vec2(0.0, 1.0)), hash21(i + vec2(7.3, 1.0)));\n vec2 gd = -1.0 + 2.0 * vec2(hash21(i + vec2(1.0, 1.0)), hash21(i + vec2(8.3, 1.0)));\n\n float va = dot(ga, f - vec2(0.0, 0.0));\n float vb = dot(gb, f - vec2(1.0, 0.0));\n float vc = dot(gc, f - vec2(0.0, 1.0));\n float vd = dot(gd, f - vec2(1.0, 1.0));\n\n // Value, remapped to [0,1] so it drops in for the prior valueNoise.\n float n = va + u.x * (vb - va) + u.y * (vc - va) + u.x * u.y * (va - vb - vc + vd);\n float value = 0.5 + 0.5 * n;\n\n // Analytic gradient (chain rule through the bilinear blend + the fade).\n vec2 grad =\n ga + u.x * (gb - ga) + u.y * (gc - ga) + u.x * u.y * (ga - gb - gc + gd) +\n du * (vec2(u.y, u.x) * (va - vb - vc + vd) + vec2(vb, vc) - va);\n return vec3(value, 0.5 * grad);\n}\n\n// Rotated-octave FBM over the analytic gradient noise: a fixed ~0.5 rad 2x2\n// rotation between octaves breaks the axis-aligned banding. The rotation CONSTANT\n// is spliced from the shared chunk (AV.W2 — the one FBM_ROT); the loops below\n// stay blob-local (param octaves + the LIQUID lacunarity/persistence, per §3a).", fe = 1.8, pe = .42, me = `float fbm(vec2 p, int octaves) {
468
+ var pe = "#version 300 es\n\nin vec2 aPosition;\nout vec2 vUv;\n\nvoid main() {\n vUv = 0.5 * (aPosition + 1.0);\n gl_Position = vec4(aPosition, 0.0, 1.0);\n}", me = "// --- SDF ---\n\nfloat sdCircle(vec2 p, vec2 center, float radius) {\n return length(p - center) - radius;\n}\n\n// AX.W15 — the VALUE+GRADIENT circle: returns vec3(d, dd/dx, dd/dy). The circle\n// gradient is the UNIT radial direction (p - center)/length(p - center) (the\n// eikonal property holds EXACTLY for a circle), guarded at the degenerate centre.\n// This is the keystone of the analytic-gradient smin: carrying the gradient\n// through the scene field DELETES the per-pixel 4-tap normal (4 full sceneDist\n// evals, each running 3-octave FBM + the sat/trail loops).\nvec3 sdgCircle(vec2 p, vec2 center, float radius) {\n vec2 d = p - center;\n float len = length(d);\n vec2 grad = len > 1e-6 ? d / len : vec2(0.0, 1.0);\n return vec3(len - radius, grad);\n}\n\n// IQ 2024 quadratic-polynomial smin, NORMALIZED so k is a blend band in distance\n// units. The k *= 4.0 pre-scale makes the h*h*k*0.25 poly's effective blend width\n// equal to k (the un-normalized form's width was 0.25*k).\nfloat sminQuadratic(float a, float b, float k) {\n k *= 4.0;\n float h = max(k - abs(a - b), 0.0) / k;\n return min(a, b) - h * h * k * 0.25;\n}\n\n// IQ circular smin — a quarter-circle fillet at the seam (rounder than the\n// quadratic). The k *= 1.0/(1.0-sqrt(0.5)) factor normalizes k to the same\n// blend-band units so the merge axis swaps shape without re-tuning uSmoothK.\nfloat sminCircular(float a, float b, float k) {\n k *= 1.0 / (1.0 - sqrt(0.5));\n float h = max(k - abs(a - b), 0.0) / k;\n return min(a, b) - k * 0.5 * (1.0 + h - sqrt(1.0 - h * (h - 2.0)));\n}\n\n// Config-gated merge dispatch — uMerge 0 = quadratic, 1 = circular.\nfloat smin(float a, float b, float k) {\n if (uMerge > 0.5) return sminCircular(a, b, k);\n return sminQuadratic(a, b, k);\n}\n\n// AX.W15 — the VALUE+GRADIENT smin. a/b are vec3(dist, grad.xy); the blended\n// gradient is mix(a.yz, b.yz, w) with the interpolation weight w the distance\n// blend uses (IQ's vec2-smin insight). This propagates the analytic gradient\n// through the merge so the surface normal reads the field gradient DIRECTLY — no\n// finite-difference tap. The blend weight matches each variant.\nvec3 sminQuadraticG(vec3 a, vec3 b, float k) {\n k *= 4.0;\n float h = max(k - abs(a.x - b.x), 0.0) / k;\n float m = h * h * k * 0.25;\n // h-weight that selects which surface dominates (0 → a, 1 → b).\n float w = (a.x < b.x) ? 0.5 * h * h : 1.0 - 0.5 * h * h;\n float dist = min(a.x, b.x) - m;\n vec2 grad = mix(a.yz, b.yz, w);\n return vec3(dist, grad);\n}\n\nvec3 sminCircularG(vec3 a, vec3 b, float k) {\n k *= 1.0 / (1.0 - sqrt(0.5));\n float h = max(k - abs(a.x - b.x), 0.0) / k;\n float m = k * 0.5 * (1.0 + h - sqrt(1.0 - h * (h - 2.0)));\n float w = (a.x < b.x) ? 0.5 * h : 1.0 - 0.5 * h;\n float dist = min(a.x, b.x) - m;\n vec2 grad = mix(a.yz, b.yz, w);\n return vec3(dist, grad);\n}\n\nvec3 sminG(vec3 a, vec3 b, float k) {\n if (uMerge > 0.5) return sminCircularG(a, b, k);\n return sminQuadraticG(a, b, k);\n}", he = "// --- Noise ---\n\nfloat hash21(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * 0.1031);\n p3 += dot(p3, p3.yzx + 33.33);\n return fract((p3.x + p3.y) * p3.z);\n}\n\n// IQ analytic-derivative gradient noise — value in .x, ANALYTIC gradient\n// (d/dx, d/dy) in .yz, from one eval. The quintic fade has a continuous 2nd\n// derivative so the gradient is smooth (a cubic smoothstep fade would crease it).\n// (Inigo Quilez, https://iquilezles.org/articles/gradientnoise/.)\nvec3 noised(vec2 p) {\n vec2 i = floor(p);\n vec2 f = fract(p);\n\n // Quintic fade + its derivative.\n vec2 u = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);\n vec2 du = 30.0 * f * f * (f * (f - 2.0) + 1.0);\n\n // Per-corner gradient vectors from the hash (centred to [-1, 1]).\n vec2 ga = -1.0 + 2.0 * vec2(hash21(i + vec2(0.0, 0.0)), hash21(i + vec2(7.3, 0.0)));\n vec2 gb = -1.0 + 2.0 * vec2(hash21(i + vec2(1.0, 0.0)), hash21(i + vec2(8.3, 0.0)));\n vec2 gc = -1.0 + 2.0 * vec2(hash21(i + vec2(0.0, 1.0)), hash21(i + vec2(7.3, 1.0)));\n vec2 gd = -1.0 + 2.0 * vec2(hash21(i + vec2(1.0, 1.0)), hash21(i + vec2(8.3, 1.0)));\n\n float va = dot(ga, f - vec2(0.0, 0.0));\n float vb = dot(gb, f - vec2(1.0, 0.0));\n float vc = dot(gc, f - vec2(0.0, 1.0));\n float vd = dot(gd, f - vec2(1.0, 1.0));\n\n // Value, remapped to [0,1] so it drops in for the prior valueNoise.\n float n = va + u.x * (vb - va) + u.y * (vc - va) + u.x * u.y * (va - vb - vc + vd);\n float value = 0.5 + 0.5 * n;\n\n // Analytic gradient (chain rule through the bilinear blend + the fade).\n vec2 grad =\n ga + u.x * (gb - ga) + u.y * (gc - ga) + u.x * u.y * (ga - gb - gc + gd) +\n du * (vec2(u.y, u.x) * (va - vb - vc + vd) + vec2(vb, vc) - va);\n return vec3(value, 0.5 * grad);\n}\n\n// Rotated-octave FBM over the analytic gradient noise: a fixed ~0.5 rad 2x2\n// rotation between octaves breaks the axis-aligned banding. The rotation CONSTANT\n// is spliced from the shared chunk (AV.W2 — the one FBM_ROT); the loops below\n// stay blob-local (param octaves + the LIQUID lacunarity/persistence, per §3a).", ge = 1.8, _e = .42, ve = `float fbm(vec2 p, int octaves) {
467
469
  float value = 0.0;
468
470
  float amp = 0.5;
469
471
  float freq = 1.0;
@@ -471,8 +473,8 @@ var le = "#version 300 es\n\nin vec2 aPosition;\nout vec2 vUv;\n\nvoid main() {\
471
473
  if (i >= octaves) break;
472
474
  value += amp * noised(p * freq).x;
473
475
  p = FBM_ROT * p;
474
- freq *= ${fe.toFixed(1)};
475
- amp *= ${pe.toFixed(2)};
476
+ freq *= ${ge.toFixed(1)};
477
+ amp *= ${_e.toFixed(2)};
476
478
  }
477
479
  return value;
478
480
  }
@@ -499,8 +501,8 @@ vec3 fbmG(vec2 p, int octaves) {
499
501
  // d(value)/dp = amp · freq · (R^i)^T · noised.yz (the rotated chain rule).
500
502
  grad += amp * freq * (transpose(rotAccum) * n.yz);
501
503
  pp = FBM_ROT * pp;
502
- freq *= ${fe.toFixed(1)};
503
- amp *= ${pe.toFixed(2)};
504
+ freq *= ${ge.toFixed(1)};
505
+ amp *= ${_e.toFixed(2)};
504
506
  rotAccum = FBM_ROT * rotAccum;
505
507
  }
506
508
  return vec3(value, grad);
@@ -515,9 +517,9 @@ vec3 fbmWarpedG(vec2 p, int octaves, float warpAmp) {
515
517
  vec2 q = vec2(fbm(p + vec2(0.0, 0.0), octaves), fbm(p + vec2(5.2, 1.3), octaves));
516
518
  return fbmG(p + warpAmp * (q - 0.5), octaves);
517
519
  }
518
- `, he = "// Edit #4's gamut step — hue-preserving inward chroma clamp. If the linear result\n// is out of [0,1], bisect chroma toward 0 (L + h fixed) until in gamut. Preserves\n// hue exactly (only C shrinks). 16 steps resolve C to < 2^-16 of its span.\nbool inGamut(vec3 lin) {\n return all(greaterThanEqual(lin, vec3(0.0))) && all(lessThanEqual(lin, vec3(1.0)));\n}\nvec3 gamutClampOklch(vec3 lch) {\n vec3 lin = oklabToLinearSrgb(oklchToOklab(lch));\n if (inGamut(lin)) return lch;\n float lo = 0.0;\n float hi = lch.y;\n for (int i = 0; i < 16; i++) {\n float mid = 0.5 * (lo + hi);\n vec3 test = vec3(lch.x, mid, lch.z);\n if (inGamut(oklabToLinearSrgb(oklchToOklab(test)))) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n return vec3(lch.x, lo, lch.z);\n}", ge = "#version 300 es\nprecision highp float;\n\nin vec2 vUv;\nout vec4 fragColor;\n\nuniform vec2 uResolution;\nuniform float uTime;\nuniform vec3 uBaseColor;\n\n// Multi-stop palette (W11.b) — 2-4 in-family OKLCh stops (uploaded GAMMA sRGB, like\n// uBaseColor). uStopCount <= 1 falls back to uBaseColor (the single-color default,\n// zero regression). MAX_BLOB_STOPS is a compile-time #define.\n#define MAX_BLOB_STOPS 4\nuniform int uStopCount;\nuniform vec3 uPalette[MAX_BLOB_STOPS];\n\n// Pointer\nuniform vec2 uPointer;\nuniform float uPointerActive;\nuniform float uPointerAttraction;\nuniform float uPointerStrength;\n\n// Main body\nuniform float uBodyRadius;\n\n// Pulsation\nuniform float uPulsePhase;\nuniform float uPulseAmp;\n\n// Surface noise\nuniform float uNoiseAmp;\nuniform float uNoiseFreq;\nuniform float uNoiseSpeed;\n\n// Gooey\nuniform float uSmoothK;\nuniform float uMerge; // 0 = quadratic smin, 1 = circular smin (rounder menisci)\n\n// AX.W16 (arm 5) — the PRE-FBM bounding-discard radius (UV space). main() early-outs\n// to a transparent write for any fragment OUTSIDE this radius BEFORE the two 3-octave\n// FBM calls + the OKLCh round-trip. The renderer uploads it PADDED by every\n// outward-expanding term (body + eccentric orbit + sat radius + smin band + FBM edge\n// amplitude + click-pulse + a lean/squash safety pad) so it NEVER clips the wet\n// meniscus (IQ: inflate the bound to match any outward op).\nuniform float uMaxReach;\n\n// Membrane — domain-warp strength on the FBM edge displacement (0 = plain fbm).\nuniform float uWarpAmp;\n\n// Lit glass surface (W9.b) — gated behind uLit so the flat fill stays default.\nuniform float uLit; // 0 = flat fill (default), 1 = lit droplet\nuniform vec3 uRimColor; // Fresnel rim tint (GAMMA sRGB, via the /color leaf)\nuniform vec3 uLightDir; // normalized light direction (x, y, z)\nuniform float uSpecStrength; // Blinn-Phong specular weight\nuniform float uSpecShininess; // specular exponent (16-64, tight glint)\nuniform float uRimPower; // Fresnel/Schlick exponent (~2-3)\nuniform float uRimStrength; // Fresnel rim weight\n\n// Iridescence + fake subsurface (W11.a) — translucent-gel read.\nuniform float uIridescence; // warm-pearl rim sheen weight (0 = off)\nuniform float uIridHue; // base hue (radians) the cosine palette biases toward (warm)\nuniform float uIridSpeed; // animated-thickness scroll for the iridescent t\nuniform float uSssScale; // fast-SSS back-light weight\nuniform float uSssPower; // fast-SSS exponent\nuniform float uCoreGlow; // thickness-driven inner-luminosity lift\n\n// Color perturbation (perceptually uniform — OKLCh L/C/h)\nuniform float uHueRange; // degrees of hue swing (converted to radians)\nuniform float uSatShift; // OKLCh chroma swing\nuniform float uBrightnessShift; // OKLCh lightness bias\nuniform float uColorNoiseFreq;\nuniform float uColorNoiseSpeed;\n\n// Satellites (max 4)\n#define MAX_SATS 4\nuniform int uSatCount;\nuniform vec2 uSatPos[MAX_SATS];\nuniform float uSatRadius[MAX_SATS];\nuniform float uSatOpacity[MAX_SATS];\n\n// Pointer trail (W10) — a decaying-radius pseudopod toward the cursor. uTrail is a\n// COMPILE-TIME-SIZED array (GLSL ES 3.00 forbids a uniform-sized array) with a\n// DYNAMIC break on uTrailCount, mirroring the satellite loop. Later samples paint\n// smaller metaballs (r *= 1 - i/N) so the trail tapers into a tail.\n#define TRAIL_N 15\nuniform int uTrailCount;\nuniform vec2 uTrailPos[TRAIL_N];\nuniform float uTrailRadius[TRAIL_N];\n\n// Velocity-driven volume-preserving squash-and-stretch (W10).\nuniform vec2 uVelocity; // smoothed pointer velocity (motion direction + speed)\nuniform float uStretch; // squash-stretch magnitude (0 = off)\n\nconst float PI = 3.141592653589793;\n", Q = "\n", _e = ge + Q + de + Q + `${a}
520
+ `, ye = "// Edit #4's gamut step — hue-preserving inward chroma clamp. If the linear result\n// is out of [0,1], bisect chroma toward 0 (L + h fixed) until in gamut. Preserves\n// hue exactly (only C shrinks). 16 steps resolve C to < 2^-16 of its span.\nbool inGamut(vec3 lin) {\n return all(greaterThanEqual(lin, vec3(0.0))) && all(lessThanEqual(lin, vec3(1.0)));\n}\nvec3 gamutClampOklch(vec3 lch) {\n vec3 lin = oklabToLinearSrgb(oklchToOklab(lch));\n if (inGamut(lin)) return lch;\n float lo = 0.0;\n float hi = lch.y;\n for (int i = 0; i < 16; i++) {\n float mid = 0.5 * (lo + hi);\n vec3 test = vec3(lch.x, mid, lch.z);\n if (inGamut(oklabToLinearSrgb(oklchToOklab(test)))) {\n lo = mid;\n } else {\n hi = mid;\n }\n }\n return vec3(lch.x, lo, lch.z);\n}", be = "#version 300 es\nprecision highp float;\n\nin vec2 vUv;\nout vec4 fragColor;\n\nuniform vec2 uResolution;\nuniform float uTime;\nuniform vec3 uBaseColor;\n\n// Multi-stop palette (W11.b) — 2-4 in-family OKLCh stops (uploaded GAMMA sRGB, like\n// uBaseColor). uStopCount <= 1 falls back to uBaseColor (the single-color default,\n// zero regression). MAX_BLOB_STOPS is a compile-time #define.\n#define MAX_BLOB_STOPS 4\nuniform int uStopCount;\nuniform vec3 uPalette[MAX_BLOB_STOPS];\n\n// Pointer\nuniform vec2 uPointer;\nuniform float uPointerActive;\nuniform float uPointerAttraction;\nuniform float uPointerStrength;\n\n// Main body\nuniform float uBodyRadius;\n\n// Pulsation\nuniform float uPulsePhase;\nuniform float uPulseAmp;\n\n// Surface noise\nuniform float uNoiseAmp;\nuniform float uNoiseFreq;\nuniform float uNoiseSpeed;\n\n// Gooey\nuniform float uSmoothK;\nuniform float uMerge; // 0 = quadratic smin, 1 = circular smin (rounder menisci)\n\n// AX.W16 (arm 5) — the PRE-FBM bounding-discard radius (UV space). main() early-outs\n// to a transparent write for any fragment OUTSIDE this radius BEFORE the two 3-octave\n// FBM calls + the OKLCh round-trip. The renderer uploads it PADDED by every\n// outward-expanding term (body + eccentric orbit + sat radius + smin band + FBM edge\n// amplitude + click-pulse + a lean/squash safety pad) so it NEVER clips the wet\n// meniscus (IQ: inflate the bound to match any outward op).\nuniform float uMaxReach;\n\n// Membrane — domain-warp strength on the FBM edge displacement (0 = plain fbm).\nuniform float uWarpAmp;\n\n// Lit glass surface (W9.b) — gated behind uLit so the flat fill stays default.\nuniform float uLit; // 0 = flat fill (default), 1 = lit droplet\nuniform vec3 uRimColor; // Fresnel rim tint (GAMMA sRGB, via the /color leaf)\nuniform vec3 uLightDir; // normalized light direction (x, y, z)\nuniform float uSpecStrength; // Blinn-Phong specular weight\nuniform float uSpecShininess; // specular exponent (16-64, tight glint)\nuniform float uRimPower; // Fresnel/Schlick exponent (~2-3)\nuniform float uRimStrength; // Fresnel rim weight\n\n// Iridescence + fake subsurface (W11.a) — translucent-gel read.\nuniform float uIridescence; // warm-pearl rim sheen weight (0 = off)\nuniform float uIridHue; // base hue (radians) the cosine palette biases toward (warm)\nuniform float uIridSpeed; // animated-thickness scroll for the iridescent t\nuniform float uSssScale; // fast-SSS back-light weight\nuniform float uSssPower; // fast-SSS exponent\nuniform float uCoreGlow; // thickness-driven inner-luminosity lift\n\n// Color perturbation (perceptually uniform — OKLCh L/C/h)\nuniform float uHueRange; // degrees of hue swing (converted to radians)\nuniform float uSatShift; // OKLCh chroma swing\nuniform float uBrightnessShift; // OKLCh lightness bias\nuniform float uColorNoiseFreq;\nuniform float uColorNoiseSpeed;\n\n// Satellites (max 4)\n#define MAX_SATS 4\nuniform int uSatCount;\nuniform vec2 uSatPos[MAX_SATS];\nuniform float uSatRadius[MAX_SATS];\nuniform float uSatOpacity[MAX_SATS];\n\n// Pointer trail (W10) — a decaying-radius pseudopod toward the cursor. uTrail is a\n// COMPILE-TIME-SIZED array (GLSL ES 3.00 forbids a uniform-sized array) with a\n// DYNAMIC break on uTrailCount, mirroring the satellite loop. Later samples paint\n// smaller metaballs (r *= 1 - i/N) so the trail tapers into a tail.\n#define TRAIL_N 15\nuniform int uTrailCount;\nuniform vec2 uTrailPos[TRAIL_N];\nuniform float uTrailRadius[TRAIL_N];\n\n// Velocity-driven volume-preserving squash-and-stretch (W10).\nuniform vec2 uVelocity; // smoothed pointer velocity (motion direction + speed)\nuniform float uStretch; // squash-stretch magnitude (0 = off)\n\nconst float PI = 3.141592653589793;\n", Z = "\n", xe = be + Z + he + Z + `${i}
519
521
 
520
- ` + me + Q + ue + Q + `
522
+ ` + ve + Z + me + Z + `
521
523
  // --- Color (Ottosson OKLab/OKLCh — value.js EXACT constants, transposed) ---
522
524
  //
523
525
  // The OETF (srgbToLinear/linearToSrgb) + the four Ottosson matrices + their space
@@ -526,13 +528,13 @@ vec3 fbmWarpedG(vec2 p, int octaves, float warpAmp) {
526
528
  // ONCE so the OETF can never diverge from aurora's; the line-for-line TS port
527
529
  // (__tests__/metaball-color.glsl-port.ts) mirrors that chunk. The gamut-clamp
528
530
  // below stays blob-local (aurora has no in-shader OKLCh path).
529
- ${s}
531
+ ${n}
530
532
  ${r}
531
533
 
532
- ` + he + Q + "\n// AX.W15 — de-synced breath. The body throb was a single sin(uPulsePhase) — a\n// mechanical pulse that re-syncs every cycle. Three DETUNED sines at IRRATIONAL\n// frequency ratios (1, 0.13/0.09 ≈ 1.444, 0.31) never re-phase, so the membrane\n// breathes like a living creature (an asymmetric slower-exhale calm band) rather\n// than a metronome. Normalized so the composite stays in ~[-1, 1].\nfloat breath(float phase) {\n return (sin(phase)\n + 0.5 * sin(phase * 1.4444 + 1.7)\n + 0.28 * sin(phase * 0.31 + 4.1)) / 1.78;\n}\n\n// AX.W15 — the composite SDF field WITH its ANALYTIC GRADIENT, returned as\n// vec3(dist, ∂d/∂x, ∂d/∂y). The domain-warped body membrane smin-merged with the\n// satellites + trail; the gradient propagates through sminG's mix(a.yz, b.yz, h)\n// so the surface normal reads the field gradient DIRECTLY (the 4-tap finite\n// difference is DELETED — its 4 full evals per lit pixel are gone).\n//\n// The ANISOTROPIC SQUASH (W10 volume-preserving stretch) is a linear map\n// bodyUv = D·R·uv (R rotates world→(motion-axis, perp); D = diag(1/sa, sa)). The\n// body field's gradient is computed in bodyUv space, so it MUST be transformed back\n// to uv space by M^T = R^T·D (the inverse-transpose handling the SOTA-deepening [2]\n// warns the 4-tap did implicitly by sampling in screen space).\nvec3 sceneDistG(vec2 uv) {\n // De-synced pulsing body radius.\n float bodyR = uBodyRadius + breath(uPulsePhase) * uPulseAmp;\n\n // Velocity-driven VOLUME-PRESERVING squash-and-stretch (W10), SATURATED (AX.W46\n // D5). sa is the stretch along motion, EXACTLY 1/sa perpendicular (area-preserving).\n // The basis (ax, perp) is captured so the gradient can be transformed back below.\n //\n // The W10 form sa = 1 + speed*uStretch rode an UNBOUNDED critically-damped spring\n // velocity (O(5-8)/s on a fast flick) -> sa ~ 1.78-2.25x elongation, a violent\n // taffy-pull. A tanh saturation sa = 1 + tanh(speed*k)*uStretch caps the elongation\n // at 1 + uStretch (~1.5x at the default stretch 0.5) no matter how fast the flick —\n // the fastest flick reads as a lively bounce, never a rubber-band snap (the standard\n // volume-preserving squash-and-stretch restraint). k = 1.6 keeps the tanh in its\n // near-linear regime for a slow drag (the gentle squash still reads) and saturates\n // only the fast flick.\n vec2 bodyUv = uv;\n vec2 ax = vec2(1.0, 0.0);\n vec2 perp = vec2(0.0, 1.0);\n float sa = 1.0;\n float speed = length(uVelocity);\n bool squashed = uStretch > 0.0 && speed > 1e-4;\n if (squashed) {\n ax = uVelocity / speed;\n perp = vec2(-ax.y, ax.x);\n sa = 1.0 + tanh(speed * 1.6) * uStretch;\n bodyUv = vec2(dot(uv, ax) / sa, dot(uv, perp) * sa);\n }\n\n // Domain-warped FBM displacement WITH its analytic gradient (the warp-Jacobian\n // approximation per [2]). disp = (noiseVal - 0.5) · uNoiseAmp; its gradient in\n // bodyUv space is uNoiseAmp · uNoiseFreq · fbmGrad (the uNoiseFreq chain factor\n // from the bodyUv·uNoiseFreq argument).\n vec3 fbmv = fbmWarpedG(bodyUv * uNoiseFreq + uTime * uNoiseSpeed, 3, uWarpAmp);\n float bodyDisplacement = (fbmv.x - 0.5) * uNoiseAmp;\n vec2 dispGradBody = uNoiseAmp * uNoiseFreq * fbmv.yz;\n\n // Body circle WITH gradient (in bodyUv space). The displaced radius subtracts\n // the displacement, so the body gradient is circleGrad - dispGrad (sign per\n // [2]: d = circleDist - displacement ⇒ grad = circleGrad - dispGrad).\n vec3 bodyG = sdgCircle(bodyUv, vec2(0.0), bodyR + bodyDisplacement);\n vec2 bodyGradBody = bodyG.yz - dispGradBody;\n\n // Transform the body gradient back to uv space: g_uv = R^T · D · g_body, i.e.\n // scale by D = diag(1/sa, sa) in the (ax, perp) frame then reconstruct in world.\n vec2 bodyGradUv;\n if (squashed) {\n vec2 scaled = vec2(bodyGradBody.x / sa, bodyGradBody.y * sa);\n bodyGradUv = scaled.x * ax + scaled.y * perp;\n } else {\n bodyGradUv = bodyGradBody;\n }\n vec3 d = vec3(bodyG.x, bodyGradUv);\n\n // Satellites — smin-merged into the body (uMerge selects quadratic/circular).\n for (int i = 0; i < MAX_SATS; i++) {\n if (i >= uSatCount) break;\n vec3 satG = sdgCircle(uv, uSatPos[i], uSatRadius[i]);\n satG.x += (1.0 - uSatOpacity[i]) * 0.3;\n d = sminG(d, satG, uSmoothK);\n }\n\n // Pointer trail — a decaying-radius pseudopod reaching toward the cursor,\n // smin-merged so it stretches an elastic limb and snaps back.\n for (int i = 0; i < TRAIL_N; i++) {\n if (i >= uTrailCount) break;\n vec3 trailG = sdgCircle(uv, uTrailPos[i], uTrailRadius[i]);\n d = sminG(d, trailG, uSmoothK);\n }\n return d;\n}\n\n// AX.W15 — the analytic surface normal. The field gradient grad2d arrives\n// DIRECTLY from sceneDistG (no 4-tap). The smin field is sub-unit (the CD family\n// |grad| ≤ 1) so the gradient is NOT unit-length — normalize() before the dome\n// lift (the unit contract is on the FINAL lifted normal N, not the raw field\n// gradient). The Z dome: 'interior' is the normalized depth inward from the rim\n// (-d/bodyR); z = sqrt(1 - (1 - interior)^2) is the unit half-sphere profile (flat\n// centre, steep rim). N tilts outward along the field gradient near the rim and\n// points at the viewer deep inside.\nvec3 surfaceNormalFromGrad(vec2 grad2d, float d, float bodyR) {\n vec2 g = normalize(grad2d + vec2(1e-6));\n float interior = clamp(-d / max(bodyR, 1e-4), 0.0, 1.0);\n float z = sqrt(max(0.0, 1.0 - (1.0 - interior) * (1.0 - interior)));\n return normalize(vec3(g * (1.0 - z), z) + vec3(0.0, 0.0, 1e-6));\n}\n\n// W11.b — sample the multi-stop palette at t in [0,1], interpolating adjacent\n// stops in OKLab with a MIDPOINT CHROMA-BUMP (a linear OKLab mix of a vivid pair\n// dips chroma through grey; the bump lifts it back). Falls back to uBaseColor when\n// uStopCount <= 1. Returns an OKLCh stop [L, C, h(rad)].\nvec3 samplePaletteOklch(float t) {\n if (uStopCount <= 1) return oklabToOklch(srgbToOklab(uBaseColor));\n float n = float(uStopCount);\n float ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n int i0 = int(floor(ft));\n int i1 = min(i0 + 1, uStopCount - 1);\n float f = ft - float(i0);\n // Fetch adjacent stops (uPalette is gamma sRGB) → OKLab.\n vec3 labA = srgbToOklab(uPalette[i0]);\n vec3 labB = srgbToOklab(uPalette[i1]);\n vec3 lab = mix(labA, labB, f);\n vec3 lch = oklabToOklch(lab);\n // Midpoint chroma-bump: a bell (peaks at f=0.5) lifts C off the grey midpoint.\n lch.y += 0.03 * sin(PI * f);\n return lch;\n}\n\nvoid main() {\n vec2 uv = vUv - 0.5;\n\n // AX.W16 (arm 5) — PRE-FBM bounding early-out. The oversized canvas (1.6x the\n // wrapper) runs the full fragment ALU — two 3-octave FBM evals + the OKLCh\n // round-trip + the lit/iridescence/SSS block — on a large transparent border\n // otherwise (~60% of the canvas is outside the droplet reach). Any fragment beyond\n // uMaxReach (the PADDED worst-case painted reach, uploaded so it never clips the\n // wet meniscus) writes transparent and returns BEFORE any of that work. This is a\n // transparent WRITE, NOT a GLSL discard: the pass is a pure premultiplied blend\n // with no depth, and discard disables the tiled-renderer fast path on mobile GPUs.\n // No fwidth is computed before the return, so there is no\n // derivative-in-non-uniform-control-flow hazard (the kept pixels compute their own\n // fwidth normally below). The pad covers the pointer-lean uv shift, so testing the\n // pre-deform uv is safe.\n if (dot(uv, uv) > uMaxReach * uMaxReach) {\n fragColor = vec4(0.0);\n return;\n }\n\n // Pointer deformation — honor the SIGN of uPointerAttraction (W10): a positive\n // attraction leans the body IN toward the cursor, a negative shies it AWAY. The\n // signed influence flows straight into the UV shift (no hardcoded repulsion).\n //\n // AY.W-BLOB-CONFIG D2 — the SIGN was INVERTED in the rendered result. The\n // sample-shift sign here was uv -= dir*influence; the live readback (an attraction\n // sweep over a HELD pointer) measured the body shifting MORE toward the cursor as\n // attraction went MORE NEGATIVE (a=-1 -> +0.081, a=0 -> +0.063, a=+1 -> +0.042) —\n // exactly inverted: a shy-away -1 LUNGED at the cursor, a lean-in +1 pulled back.\n // pointerDir = uPointer - uv is the toward-pointer direction in SAMPLE space; to\n // MOVE the rendered body TOWARD the pointer the sample must shift ALONG pointerDir\n // (sample the field from where the pointer is), so the sign is uv += dir*influence.\n // A positive attraction now shifts the body toward the cursor (leans in), a negative\n // shifts it away (shies) — the sign the comment + the demo slider always promised,\n // now true in the render.\n //\n // AX.W46 D5 — the falloff radius NARROWS 0.65 → 0.5 (the calm-lean reconciliation).\n // The W15 REDRESS widened it to 0.65 to clear a synthetic floor; the live π-lane\n // read the result as a LUNGE. The drama lives in the STRENGTH (pointerStrength,\n // dropped to 0.18 in types.ts), NOT the falloff — the falloff's job is purely to keep\n // the lean COHERENT across the creature so the body, satellites and trail all tilt\n // toward the cursor as ONE. At 0.5 the body (sitting ≈0.2 uv from the cursor on a\n // hover-flick) is still well inside the cutoff, so the whole creature tilts as one —\n // a gentle, coherent \"it notices you\" lean rather than the body lunging a body-width.\n if (uPointerActive > 0.5) {\n vec2 pointerDir = uPointer - uv;\n float pointerDist = length(pointerDir);\n float influence = smoothstep(0.5, 0.0, pointerDist) * uPointerAttraction * uPointerStrength;\n uv += normalize(pointerDir + 1e-6) * influence;\n }\n\n // The de-synced pulsing body radius — also reused below for the inner-glow scale.\n float bodyR = uBodyRadius + breath(uPulsePhase) * uPulseAmp;\n\n // Composite domain-warped membrane field WITH its analytic gradient (AX.W15).\n vec3 scene = sceneDistG(uv);\n float d = scene.x;\n vec2 fieldGrad = scene.yz;\n\n // Edit #1 — fwidth-based anti-aliased edge: derive the smoothstep half-width\n // from the SDF screen-space gradient so the edge stays ~1px regardless of zoom.\n float aa = max(fwidth(d), 1e-6);\n float alpha = 1.0 - smoothstep(-aa, aa, d);\n\n if (alpha < 0.001) {\n fragColor = vec4(0.0);\n return;\n }\n\n // Edit #4 — perceptually-uniform OKLCh variation. Gamma base → linear → OKLab\n // → OKLCh; perturb L/C/h; OKLCh → OKLab → linear; hue-preserving gamut clamp.\n // The base is the multi-stop palette sample (W11.b) — distributed across the\n // body/satellites by the color noise field — or uBaseColor when single-stop.\n float colorNoise = fbm(uv * uColorNoiseFreq + uTime * uColorNoiseSpeed, 3);\n vec3 oklch = samplePaletteOklch(colorNoise);\n\n oklch.z += (colorNoise - 0.5) * uHueRange * (PI / 180.0); // hue swing, radians\n oklch.y = max(oklch.y + (colorNoise - 0.5) * uSatShift, 0.0); // chroma swing\n oklch.x = clamp(oklch.x + uBrightnessShift, 0.0, 1.0); // lightness bias\n\n // Surface normal — from the ANALYTIC field gradient (AX.W15: the 4-tap is\n // deleted). Computed ONCE here and reused by the iridescence (W11.a), the\n // fake-SSS (W11.a), and the lit glass block (W9.b).\n vec3 N = surfaceNormalFromGrad(fieldGrad, d, bodyR);\n vec3 V = vec3(0.0, 0.0, 1.0);\n float thickness = clamp(-d / max(bodyR, 1e-4), 0.0, 1.0); // 0 at rim, ~1 deep in\n float fres = pow(1.0 - max(dot(N, V), 0.0), 2.5); // rim-weighted angle\n\n // ── W11.a iridescence — warm-biased IQ cosine palette driving OKLCh HUE ──\n //\n // A thin-film-like sheen on the RIM (fres-weighted), NOT the cold-blue default:\n // an IQ cosine palette (a + b*cos(2pi(c*t + d))) maps the Fresnel/edge angle +\n // the FBM color field + an animated thickness onto a HUE OFFSET biased to the\n // warm arc (uIridHue), with the chroma clamped to a warm-pearl band so the rim\n // reads cream/gold (congruent with the warm-cream-glass identity). Mixed into\n // OKLCh BEFORE the gamut clamp. Default LOW.\n if (uIridescence > 0.0) {\n float t = fres + 0.3 * colorNoise + uTime * uIridSpeed;\n // Cosine palette in HUE space, centred on the warm uIridHue (~0.18 turn of\n // warm sweep so the sheen shimmers gold→peach, never swinging to cold blue).\n float iridHue = uIridHue + 0.18 * PI * cos(2.0 * PI * t);\n float w = fres * uIridescence; // rim-weighted blend\n oklch.z = mix(oklch.z, iridHue, w);\n // Warm-pearl chroma cap: lift chroma toward a pale-pearl band, clamped.\n oklch.y = min(oklch.y + 0.04 * w, oklch.y + 0.08);\n oklch.x = min(oklch.x + 0.05 * w, 1.0); // a touch brighter at the sheen\n }\n\n // ── W11.a fake subsurface translucency — thickness inner-glow + back-light ──\n //\n // A bright translucent CORE fading to a light-leaking EDGE (the -d ramp) plus\n // the fast-SSS back-light (light wrapping through the thin rim). Both lift OKLCh\n // L and warm the hue, consuming the W9 normal. In OKLCh before the gamut clamp.\n if (uCoreGlow > 0.0 || uSssScale > 0.0) {\n // Beer-Lambert inner luminosity (AX.W15): a SATURATING 1 - exp(-k·thickness)\n // curve — a flat thick core with a fast warm rim falloff — NOT the linear\n // coreGlow·thickness ramp (which over-brightens deep interiors). k ≈ 3 reads\n // as glass depth.\n oklch.x = min(oklch.x + uCoreGlow * (1.0 - exp(-3.0 * thickness)), 1.0);\n // Fast-SSS back-light: light wrapping through the thin (low-thickness) rim.\n vec3 L = normalize(uLightDir + vec3(1e-6));\n float back = pow(clamp(dot(V, -(L + N * thickness)), 0.0, 1.0), uSssPower);\n float sss = back * uSssScale * (1.0 - thickness);\n oklch.x = min(oklch.x + sss, 1.0);\n // Warm the THIN leaking rim only (scale by 1 - thickness): the SSS hue-warm\n // shift rides the rim, not the core (AX.W15 — only the thin rim warms).\n oklch.z += sss * 0.1 * (1.0 - thickness);\n }\n\n oklch = gamutClampOklch(oklch);\n\n vec3 lin = oklabToLinearSrgb(oklchToOklab(oklch));\n\n // ── W9.b lit glass surface — Blinn-Phong glint + Fresnel rim, in LINEAR ──\n //\n // The lit terms enter lin in LINEAR space BEFORE the OETF (linearToSrgb)\n // and BEFORE the * alpha premultiply — a post-OETF apply double-gammas the\n // highlight and fringes the premultiplied edge (the named A5/A2 trap). The\n // prior flat inner-glow lift is SUBSUMED by the Fresnel rim (the curvature\n // read now comes from the surface normal, not a flat smoothstep). Gated behind\n // uLit so the flat fill stays the default (zero regression).\n if (uLit > 0.5) {\n // N, V, thickness are already computed in main() (reused by iridescence/SSS).\n vec3 L = normalize(uLightDir + vec3(1e-6)); // light direction\n vec3 H = normalize(L + V); // Blinn-Phong half-vector\n\n // Warm-cream specular glint — a near-white OKLCh TINT (L~0.97, C~0.03,\n // hue~85°) routed through the SAME spliced OKLCh matrices, NOT hardcoded\n // sRGB white (pure white reads cheap-CG). Linearized for the linear add.\n vec3 warmCream = oklabToLinearSrgb(oklchToOklab(vec3(0.97, 0.03, radians(85.0))));\n\n // ENERGY-CONSERVING Blinn-Phong (AX.W15 [9]): the (shininess+2)/8 factor\n // DECOUPLES shininess from strength (without it, raising shininess dims the\n // glint, so the two knobs fight). SPECULAR ANTIALIASING: the FBM membrane\n // makes the normal vary fast, so a tight glint (16-64) STROBES on small\n // dock-grid blobs. Widen the effective lobe where the normal varies\n // (Toksvig-style fwidth clamp) — drop shininess, NOT raise it. nVar reads\n // the screen-space normal derivative; a high-variance pixel softens the\n // exponent so the glint stays stable.\n float nVar = length(fwidth(N));\n float shininess = uSpecShininess / (1.0 + 24.0 * nVar);\n float energyNorm = (shininess + 2.0) / 8.0;\n float spec = pow(max(dot(N, H), 0.0), shininess) * uSpecStrength * energyNorm;\n\n // Fresnel/Schlick rim — fed uRimColor (the --foreground warm rim via the\n // /color leaf). FOREGROUND-AWARE MIN-CONTRAST GUARD (AX.W15 [10]):\n // a var(--primary) blob in dark mode resolves a rim near the BODY color, so\n // the rim washes out (no contrast). When the rim's luminance sits too close\n // to the body's, the guard CHROMA-REDUCES and L-LIFTS the rim stop (a\n // perceptual move, NOT a re-tint) so the curved rim always reads. Computed in\n // OKLCh so the lift is perceptual.\n vec3 rimOkl = oklabToOklch(srgbToOklab(uRimColor));\n float bodyL = oklch.x; // the post-perturb body lightness\n float dL = abs(rimOkl.x - bodyL);\n // If the rim is within 0.22 L of the body, push it AWAY (lift if the body is\n // dark, the dark-mode primary case; the chroma-reduce keeps it neutral-warm).\n float lack = clamp((0.22 - dL) / 0.22, 0.0, 1.0);\n rimOkl.x = clamp(mix(rimOkl.x, (bodyL < 0.5 ? 0.92 : 0.18), lack), 0.0, 1.0);\n rimOkl.y *= mix(1.0, 0.5, lack); // chroma-reduce toward a pale pearl\n vec3 rimLin = oklabToLinearSrgb(oklchToOklab(rimOkl));\n\n float rimFres = pow(1.0 - max(dot(N, V), 0.0), uRimPower);\n float rim = rimFres * uRimStrength * (1.0 - 0.6 * thickness);\n\n // Combine the two warm highlights and add in LINEAR. max(spec, rim*scale)\n // keeps the glint from stacking on the rim into a blown hotspot.\n //\n // AX.W46 D4 — CLAMP the linear highlight below unity before the add (the\n // belt-and-braces guard the re-derived specStrength makes rarely-binding but\n // never lets a worst-case normal blow to a hard white spot). The energy-\n // conserving Blinn-Phong CAN still spike on a grazing normal where the Toksvig\n // widen has not kicked in; capping the highlight magnitude at 0.85 (a contained\n // warm gleam, sub-unity) means the OETF clamp below can never crush a\n // pure-white pixel over the dome. The cap is on the highlight ALONE (not the\n // composited lin), so the body color underneath is untouched — it caps only\n // the additive catch-light's energy.\n vec3 highlight = max(warmCream * spec, rimLin * rim);\n highlight = min(highlight, vec3(0.85));\n lin += highlight;\n }\n\n vec3 rgb = clamp(linearToSrgb(lin), 0.0, 1.0); // MANDATORY OETF — closes the seam\n\n // IGN dither (AX.W15 [2][9]) — the low-chroma warm-cream dome BANDS on 8-bit\n // panels (visible Mach steps in the smooth L roll). Interleaved-gradient noise at\n // 1/255, applied AFTER linearToSrgb and BEFORE the *alpha premultiply (aurora\n // ships the same splice). A triangular ±0.5 LSB dither decorrelates the steps.\n float ign = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));\n rgb = clamp(rgb + (ign - 0.5) / 255.0, 0.0, 1.0);\n\n // Edit #8 — premultiply AFTER the OETF: straight-alpha gamma → premultiplied.\n fragColor = vec4(rgb * alpha, alpha);\n}";
534
+ ` + ye + Z + "\n// AX.W15 — de-synced breath. The body throb was a single sin(uPulsePhase) — a\n// mechanical pulse that re-syncs every cycle. Three DETUNED sines at IRRATIONAL\n// frequency ratios (1, 0.13/0.09 ≈ 1.444, 0.31) never re-phase, so the membrane\n// breathes like a living creature (an asymmetric slower-exhale calm band) rather\n// than a metronome. Normalized so the composite stays in ~[-1, 1].\nfloat breath(float phase) {\n return (sin(phase)\n + 0.5 * sin(phase * 1.4444 + 1.7)\n + 0.28 * sin(phase * 0.31 + 4.1)) / 1.78;\n}\n\n// AX.W15 — the composite SDF field WITH its ANALYTIC GRADIENT, returned as\n// vec3(dist, ∂d/∂x, ∂d/∂y). The domain-warped body membrane smin-merged with the\n// satellites + trail; the gradient propagates through sminG's mix(a.yz, b.yz, h)\n// so the surface normal reads the field gradient DIRECTLY (the 4-tap finite\n// difference is DELETED — its 4 full evals per lit pixel are gone).\n//\n// The ANISOTROPIC SQUASH (W10 volume-preserving stretch) is a linear map\n// bodyUv = D·R·uv (R rotates world→(motion-axis, perp); D = diag(1/sa, sa)). The\n// body field's gradient is computed in bodyUv space, so it MUST be transformed back\n// to uv space by M^T = R^T·D (the inverse-transpose handling the SOTA-deepening [2]\n// warns the 4-tap did implicitly by sampling in screen space).\nvec3 sceneDistG(vec2 uv) {\n // De-synced pulsing body radius.\n float bodyR = uBodyRadius + breath(uPulsePhase) * uPulseAmp;\n\n // Velocity-driven VOLUME-PRESERVING squash-and-stretch (W10), SATURATED (AX.W46\n // D5). sa is the stretch along motion, EXACTLY 1/sa perpendicular (area-preserving).\n // The basis (ax, perp) is captured so the gradient can be transformed back below.\n //\n // The W10 form sa = 1 + speed*uStretch rode an UNBOUNDED critically-damped spring\n // velocity (O(5-8)/s on a fast flick) -> sa ~ 1.78-2.25x elongation, a violent\n // taffy-pull. A tanh saturation sa = 1 + tanh(speed*k)*uStretch caps the elongation\n // at 1 + uStretch (~1.5x at the default stretch 0.5) no matter how fast the flick —\n // the fastest flick reads as a lively bounce, never a rubber-band snap (the standard\n // volume-preserving squash-and-stretch restraint). k = 1.6 keeps the tanh in its\n // near-linear regime for a slow drag (the gentle squash still reads) and saturates\n // only the fast flick.\n vec2 bodyUv = uv;\n vec2 ax = vec2(1.0, 0.0);\n vec2 perp = vec2(0.0, 1.0);\n float sa = 1.0;\n float speed = length(uVelocity);\n bool squashed = uStretch > 0.0 && speed > 1e-4;\n if (squashed) {\n ax = uVelocity / speed;\n perp = vec2(-ax.y, ax.x);\n sa = 1.0 + tanh(speed * 1.6) * uStretch;\n bodyUv = vec2(dot(uv, ax) / sa, dot(uv, perp) * sa);\n }\n\n // Domain-warped FBM displacement WITH its analytic gradient (the warp-Jacobian\n // approximation per [2]). disp = (noiseVal - 0.5) · uNoiseAmp; its gradient in\n // bodyUv space is uNoiseAmp · uNoiseFreq · fbmGrad (the uNoiseFreq chain factor\n // from the bodyUv·uNoiseFreq argument).\n vec3 fbmv = fbmWarpedG(bodyUv * uNoiseFreq + uTime * uNoiseSpeed, 3, uWarpAmp);\n float bodyDisplacement = (fbmv.x - 0.5) * uNoiseAmp;\n vec2 dispGradBody = uNoiseAmp * uNoiseFreq * fbmv.yz;\n\n // Body circle WITH gradient (in bodyUv space). The displaced radius subtracts\n // the displacement, so the body gradient is circleGrad - dispGrad (sign per\n // [2]: d = circleDist - displacement ⇒ grad = circleGrad - dispGrad).\n vec3 bodyG = sdgCircle(bodyUv, vec2(0.0), bodyR + bodyDisplacement);\n vec2 bodyGradBody = bodyG.yz - dispGradBody;\n\n // Transform the body gradient back to uv space: g_uv = R^T · D · g_body, i.e.\n // scale by D = diag(1/sa, sa) in the (ax, perp) frame then reconstruct in world.\n vec2 bodyGradUv;\n if (squashed) {\n vec2 scaled = vec2(bodyGradBody.x / sa, bodyGradBody.y * sa);\n bodyGradUv = scaled.x * ax + scaled.y * perp;\n } else {\n bodyGradUv = bodyGradBody;\n }\n vec3 d = vec3(bodyG.x, bodyGradUv);\n\n // Satellites — smin-merged into the body (uMerge selects quadratic/circular).\n for (int i = 0; i < MAX_SATS; i++) {\n if (i >= uSatCount) break;\n vec3 satG = sdgCircle(uv, uSatPos[i], uSatRadius[i]);\n satG.x += (1.0 - uSatOpacity[i]) * 0.3;\n d = sminG(d, satG, uSmoothK);\n }\n\n // Pointer trail — a decaying-radius pseudopod reaching toward the cursor,\n // smin-merged so it stretches an elastic limb and snaps back.\n for (int i = 0; i < TRAIL_N; i++) {\n if (i >= uTrailCount) break;\n vec3 trailG = sdgCircle(uv, uTrailPos[i], uTrailRadius[i]);\n d = sminG(d, trailG, uSmoothK);\n }\n return d;\n}\n\n// AX.W15 — the analytic surface normal. The field gradient grad2d arrives\n// DIRECTLY from sceneDistG (no 4-tap). The smin field is sub-unit (the CD family\n// |grad| ≤ 1) so the gradient is NOT unit-length — normalize() before the dome\n// lift (the unit contract is on the FINAL lifted normal N, not the raw field\n// gradient). The Z dome: 'interior' is the normalized depth inward from the rim\n// (-d/bodyR); z = sqrt(1 - (1 - interior)^2) is the unit half-sphere profile (flat\n// centre, steep rim). N tilts outward along the field gradient near the rim and\n// points at the viewer deep inside.\nvec3 surfaceNormalFromGrad(vec2 grad2d, float d, float bodyR) {\n vec2 g = normalize(grad2d + vec2(1e-6));\n float interior = clamp(-d / max(bodyR, 1e-4), 0.0, 1.0);\n float z = sqrt(max(0.0, 1.0 - (1.0 - interior) * (1.0 - interior)));\n return normalize(vec3(g * (1.0 - z), z) + vec3(0.0, 0.0, 1e-6));\n}\n\n// W11.b — sample the multi-stop palette at t in [0,1], interpolating adjacent\n// stops in OKLab with a MIDPOINT CHROMA-BUMP (a linear OKLab mix of a vivid pair\n// dips chroma through grey; the bump lifts it back). Falls back to uBaseColor when\n// uStopCount <= 1. Returns an OKLCh stop [L, C, h(rad)].\nvec3 samplePaletteOklch(float t) {\n if (uStopCount <= 1) return oklabToOklch(srgbToOklab(uBaseColor));\n float n = float(uStopCount);\n float ft = clamp(t, 0.0, 1.0) * (n - 1.0);\n int i0 = int(floor(ft));\n int i1 = min(i0 + 1, uStopCount - 1);\n float f = ft - float(i0);\n // Fetch adjacent stops (uPalette is gamma sRGB) → OKLab.\n vec3 labA = srgbToOklab(uPalette[i0]);\n vec3 labB = srgbToOklab(uPalette[i1]);\n vec3 lab = mix(labA, labB, f);\n vec3 lch = oklabToOklch(lab);\n // Midpoint chroma-bump: a bell (peaks at f=0.5) lifts C off the grey midpoint.\n lch.y += 0.03 * sin(PI * f);\n return lch;\n}\n\nvoid main() {\n vec2 uv = vUv - 0.5;\n\n // AX.W16 (arm 5) — PRE-FBM bounding early-out. The oversized canvas (1.6x the\n // wrapper) runs the full fragment ALU — two 3-octave FBM evals + the OKLCh\n // round-trip + the lit/iridescence/SSS block — on a large transparent border\n // otherwise (~60% of the canvas is outside the droplet reach). Any fragment beyond\n // uMaxReach (the PADDED worst-case painted reach, uploaded so it never clips the\n // wet meniscus) writes transparent and returns BEFORE any of that work. This is a\n // transparent WRITE, NOT a GLSL discard: the pass is a pure premultiplied blend\n // with no depth, and discard disables the tiled-renderer fast path on mobile GPUs.\n // No fwidth is computed before the return, so there is no\n // derivative-in-non-uniform-control-flow hazard (the kept pixels compute their own\n // fwidth normally below). The pad covers the pointer-lean uv shift, so testing the\n // pre-deform uv is safe.\n if (dot(uv, uv) > uMaxReach * uMaxReach) {\n fragColor = vec4(0.0);\n return;\n }\n\n // Pointer deformation — honor the SIGN of uPointerAttraction (W10): a positive\n // attraction leans the body IN toward the cursor, a negative shies it AWAY. The\n // signed influence flows straight into the UV shift (no hardcoded repulsion).\n //\n // AY.W-BLOB-CONFIG D2 — the SIGN was INVERTED in the rendered result. The\n // sample-shift sign here was uv -= dir*influence; the live readback (an attraction\n // sweep over a HELD pointer) measured the body shifting MORE toward the cursor as\n // attraction went MORE NEGATIVE (a=-1 -> +0.081, a=0 -> +0.063, a=+1 -> +0.042) —\n // exactly inverted: a shy-away -1 LUNGED at the cursor, a lean-in +1 pulled back.\n // pointerDir = uPointer - uv is the toward-pointer direction in SAMPLE space; to\n // MOVE the rendered body TOWARD the pointer the sample must shift ALONG pointerDir\n // (sample the field from where the pointer is), so the sign is uv += dir*influence.\n // A positive attraction now shifts the body toward the cursor (leans in), a negative\n // shifts it away (shies) — the sign the comment + the demo slider always promised,\n // now true in the render.\n //\n // AX.W46 D5 — the falloff radius NARROWS 0.65 → 0.5 (the calm-lean reconciliation).\n // The W15 REDRESS widened it to 0.65 to clear a synthetic floor; the live π-lane\n // read the result as a LUNGE. The drama lives in the STRENGTH (pointerStrength,\n // dropped to 0.18 in types.ts), NOT the falloff — the falloff's job is purely to keep\n // the lean COHERENT across the creature so the body, satellites and trail all tilt\n // toward the cursor as ONE. At 0.5 the body (sitting ≈0.2 uv from the cursor on a\n // hover-flick) is still well inside the cutoff, so the whole creature tilts as one —\n // a gentle, coherent \"it notices you\" lean rather than the body lunging a body-width.\n if (uPointerActive > 0.5) {\n vec2 pointerDir = uPointer - uv;\n float pointerDist = length(pointerDir);\n float influence = smoothstep(0.5, 0.0, pointerDist) * uPointerAttraction * uPointerStrength;\n uv += normalize(pointerDir + 1e-6) * influence;\n }\n\n // The de-synced pulsing body radius — also reused below for the inner-glow scale.\n float bodyR = uBodyRadius + breath(uPulsePhase) * uPulseAmp;\n\n // Composite domain-warped membrane field WITH its analytic gradient (AX.W15).\n vec3 scene = sceneDistG(uv);\n float d = scene.x;\n vec2 fieldGrad = scene.yz;\n\n // Edit #1 — fwidth-based anti-aliased edge: derive the smoothstep half-width\n // from the SDF screen-space gradient so the edge stays ~1px regardless of zoom.\n float aa = max(fwidth(d), 1e-6);\n float alpha = 1.0 - smoothstep(-aa, aa, d);\n\n if (alpha < 0.001) {\n fragColor = vec4(0.0);\n return;\n }\n\n // Edit #4 — perceptually-uniform OKLCh variation. Gamma base → linear → OKLab\n // → OKLCh; perturb L/C/h; OKLCh → OKLab → linear; hue-preserving gamut clamp.\n // The base is the multi-stop palette sample (W11.b) — distributed across the\n // body/satellites by the color noise field — or uBaseColor when single-stop.\n float colorNoise = fbm(uv * uColorNoiseFreq + uTime * uColorNoiseSpeed, 3);\n vec3 oklch = samplePaletteOklch(colorNoise);\n\n oklch.z += (colorNoise - 0.5) * uHueRange * (PI / 180.0); // hue swing, radians\n oklch.y = max(oklch.y + (colorNoise - 0.5) * uSatShift, 0.0); // chroma swing\n oklch.x = clamp(oklch.x + uBrightnessShift, 0.0, 1.0); // lightness bias\n\n // Surface normal — from the ANALYTIC field gradient (AX.W15: the 4-tap is\n // deleted). Computed ONCE here and reused by the iridescence (W11.a), the\n // fake-SSS (W11.a), and the lit glass block (W9.b).\n vec3 N = surfaceNormalFromGrad(fieldGrad, d, bodyR);\n vec3 V = vec3(0.0, 0.0, 1.0);\n float thickness = clamp(-d / max(bodyR, 1e-4), 0.0, 1.0); // 0 at rim, ~1 deep in\n float fres = pow(1.0 - max(dot(N, V), 0.0), 2.5); // rim-weighted angle\n\n // ── W11.a iridescence — warm-biased IQ cosine palette driving OKLCh HUE ──\n //\n // A thin-film-like sheen on the RIM (fres-weighted), NOT the cold-blue default:\n // an IQ cosine palette (a + b*cos(2pi(c*t + d))) maps the Fresnel/edge angle +\n // the FBM color field + an animated thickness onto a HUE OFFSET biased to the\n // warm arc (uIridHue), with the chroma clamped to a warm-pearl band so the rim\n // reads cream/gold (congruent with the warm-cream-glass identity). Mixed into\n // OKLCh BEFORE the gamut clamp. Default LOW.\n if (uIridescence > 0.0) {\n float t = fres + 0.3 * colorNoise + uTime * uIridSpeed;\n // Cosine palette in HUE space, centred on the warm uIridHue (~0.18 turn of\n // warm sweep so the sheen shimmers gold→peach, never swinging to cold blue).\n float iridHue = uIridHue + 0.18 * PI * cos(2.0 * PI * t);\n float w = fres * uIridescence; // rim-weighted blend\n oklch.z = mix(oklch.z, iridHue, w);\n // Warm-pearl chroma cap: lift chroma toward a pale-pearl band, clamped.\n oklch.y = min(oklch.y + 0.04 * w, oklch.y + 0.08);\n oklch.x = min(oklch.x + 0.05 * w, 1.0); // a touch brighter at the sheen\n }\n\n // ── W11.a fake subsurface translucency — thickness inner-glow + back-light ──\n //\n // A bright translucent CORE fading to a light-leaking EDGE (the -d ramp) plus\n // the fast-SSS back-light (light wrapping through the thin rim). Both lift OKLCh\n // L and warm the hue, consuming the W9 normal. In OKLCh before the gamut clamp.\n if (uCoreGlow > 0.0 || uSssScale > 0.0) {\n // Beer-Lambert inner luminosity (AX.W15): a SATURATING 1 - exp(-k·thickness)\n // curve — a flat thick core with a fast warm rim falloff — NOT the linear\n // coreGlow·thickness ramp (which over-brightens deep interiors). k ≈ 3 reads\n // as glass depth.\n oklch.x = min(oklch.x + uCoreGlow * (1.0 - exp(-3.0 * thickness)), 1.0);\n // Fast-SSS back-light: light wrapping through the thin (low-thickness) rim.\n vec3 L = normalize(uLightDir + vec3(1e-6));\n float back = pow(clamp(dot(V, -(L + N * thickness)), 0.0, 1.0), uSssPower);\n float sss = back * uSssScale * (1.0 - thickness);\n oklch.x = min(oklch.x + sss, 1.0);\n // Warm the THIN leaking rim only (scale by 1 - thickness): the SSS hue-warm\n // shift rides the rim, not the core (AX.W15 — only the thin rim warms).\n oklch.z += sss * 0.1 * (1.0 - thickness);\n }\n\n oklch = gamutClampOklch(oklch);\n\n vec3 lin = oklabToLinearSrgb(oklchToOklab(oklch));\n\n // ── W9.b lit glass surface — Blinn-Phong glint + Fresnel rim, in LINEAR ──\n //\n // The lit terms enter lin in LINEAR space BEFORE the OETF (linearToSrgb)\n // and BEFORE the * alpha premultiply — a post-OETF apply double-gammas the\n // highlight and fringes the premultiplied edge (the named A5/A2 trap). The\n // prior flat inner-glow lift is SUBSUMED by the Fresnel rim (the curvature\n // read now comes from the surface normal, not a flat smoothstep). Gated behind\n // uLit so the flat fill stays the default (zero regression).\n if (uLit > 0.5) {\n // N, V, thickness are already computed in main() (reused by iridescence/SSS).\n vec3 L = normalize(uLightDir + vec3(1e-6)); // light direction\n vec3 H = normalize(L + V); // Blinn-Phong half-vector\n\n // Warm-cream specular glint — a near-white OKLCh TINT (L~0.97, C~0.03,\n // hue~85°) routed through the SAME spliced OKLCh matrices, NOT hardcoded\n // sRGB white (pure white reads cheap-CG). Linearized for the linear add.\n vec3 warmCream = oklabToLinearSrgb(oklchToOklab(vec3(0.97, 0.03, radians(85.0))));\n\n // ENERGY-CONSERVING Blinn-Phong (AX.W15 [9]): the (shininess+2)/8 factor\n // DECOUPLES shininess from strength (without it, raising shininess dims the\n // glint, so the two knobs fight). SPECULAR ANTIALIASING: the FBM membrane\n // makes the normal vary fast, so a tight glint (16-64) STROBES on small\n // dock-grid blobs. Widen the effective lobe where the normal varies\n // (Toksvig-style fwidth clamp) — drop shininess, NOT raise it. nVar reads\n // the screen-space normal derivative; a high-variance pixel softens the\n // exponent so the glint stays stable.\n float nVar = length(fwidth(N));\n float shininess = uSpecShininess / (1.0 + 24.0 * nVar);\n float energyNorm = (shininess + 2.0) / 8.0;\n float spec = pow(max(dot(N, H), 0.0), shininess) * uSpecStrength * energyNorm;\n\n // Fresnel/Schlick rim — fed uRimColor (the --foreground warm rim via the\n // /color leaf). FOREGROUND-AWARE MIN-CONTRAST GUARD (AX.W15 [10]):\n // a var(--primary) blob in dark mode resolves a rim near the BODY color, so\n // the rim washes out (no contrast). When the rim's luminance sits too close\n // to the body's, the guard CHROMA-REDUCES and L-LIFTS the rim stop (a\n // perceptual move, NOT a re-tint) so the curved rim always reads. Computed in\n // OKLCh so the lift is perceptual.\n vec3 rimOkl = oklabToOklch(srgbToOklab(uRimColor));\n float bodyL = oklch.x; // the post-perturb body lightness\n float dL = abs(rimOkl.x - bodyL);\n // If the rim is within 0.22 L of the body, push it AWAY (lift if the body is\n // dark, the dark-mode primary case; the chroma-reduce keeps it neutral-warm).\n float lack = clamp((0.22 - dL) / 0.22, 0.0, 1.0);\n rimOkl.x = clamp(mix(rimOkl.x, (bodyL < 0.5 ? 0.92 : 0.18), lack), 0.0, 1.0);\n rimOkl.y *= mix(1.0, 0.5, lack); // chroma-reduce toward a pale pearl\n vec3 rimLin = oklabToLinearSrgb(oklchToOklab(rimOkl));\n\n float rimFres = pow(1.0 - max(dot(N, V), 0.0), uRimPower);\n float rim = rimFres * uRimStrength * (1.0 - 0.6 * thickness);\n\n // Combine the two warm highlights and add in LINEAR. max(spec, rim*scale)\n // keeps the glint from stacking on the rim into a blown hotspot.\n //\n // AX.W46 D4 — CLAMP the linear highlight below unity before the add (the\n // belt-and-braces guard the re-derived specStrength makes rarely-binding but\n // never lets a worst-case normal blow to a hard white spot). The energy-\n // conserving Blinn-Phong CAN still spike on a grazing normal where the Toksvig\n // widen has not kicked in; capping the highlight magnitude at 0.85 (a contained\n // warm gleam, sub-unity) means the OETF clamp below can never crush a\n // pure-white pixel over the dome. The cap is on the highlight ALONE (not the\n // composited lin), so the body color underneath is untouched — it caps only\n // the additive catch-light's energy.\n vec3 highlight = max(warmCream * spec, rimLin * rim);\n highlight = min(highlight, vec3(0.85));\n lin += highlight;\n }\n\n vec3 rgb = clamp(linearToSrgb(lin), 0.0, 1.0); // MANDATORY OETF — closes the seam\n\n // IGN dither (AX.W15 [2][9]) — the low-chroma warm-cream dome BANDS on 8-bit\n // panels (visible Mach steps in the smooth L roll). Interleaved-gradient noise at\n // 1/255, applied AFTER linearToSrgb and BEFORE the *alpha premultiply (aurora\n // ships the same splice). A triangular ±0.5 LSB dither decorrelates the steps.\n float ign = fract(52.9829189 * fract(dot(gl_FragCoord.xy, vec2(0.06711056, 0.00583715))));\n rgb = clamp(rgb + (ign - 0.5) / 255.0, 0.0, 1.0);\n\n // Edit #8 — premultiply AFTER the OETF: straight-alpha gamma → premultiplied.\n fragColor = vec4(rgb * alpha, alpha);\n}";
533
535
  //#endregion
534
536
  //#region src/components/custom/goo-blob/composables/uploadBlobUniforms.ts
535
- function ve(e, t, n, r, i, a, o, s, c) {
537
+ function Se(e, t, n, r, i, a, o, s, c) {
536
538
  let { U: l, satPosLocs: u, satRadLocs: d, satOpLocs: f, trailPosLocs: p, trailRadLocs: m, paletteLocs: h } = r, { params: g, rgb: _, simTimeMs: v, timeSec: y, resolveColor: b, rimColor: x, paletteStops: S } = c, { geometry: C, membrane: w, color: T, surface: E, interaction: D } = a;
537
539
  e.useProgram(t), e.bindVertexArray(n), e.uniform2f(l.uResolution, i.width, i.height), e.uniform1f(l.uTime, v / 1e3), e.uniform3f(l.uBaseColor, _[0], _[1], _[2]);
538
540
  let O = S, k = Math.min(O.length, 4);
@@ -544,22 +546,22 @@ function ve(e, t, n, r, i, a, o, s, c) {
544
546
  e.uniform3f(n, t[0], t[1], t[2]);
545
547
  } else e.uniform3f(n, _[0], _[1], _[2]);
546
548
  }
547
- let j = o.pointer.value, M = D.pointerAttraction + g.pointerAttraction;
548
- e.uniform2f(l.uPointer, j.x * .5 * A, j.y * .5 * A), e.uniform1f(l.uPointerActive, +!!o.active.value), e.uniform1f(l.uPointerAttraction, M), e.uniform1f(l.uPointerStrength, D.pointerStrength * A);
549
- let N = Math.max(0, Math.min(1, M)), P = o.velocity.value;
550
- e.uniform2f(l.uVelocity, P.x * .5 * A, P.y * .5 * A), e.uniform1f(l.uStretch, D.stretch);
551
- let F = o.trailSources(C.satelliteRadius * .7 * N);
549
+ let A = o.pointer.value, j = D.pointerAttraction + g.pointerAttraction;
550
+ e.uniform2f(l.uPointer, A.x * .5 * N, A.y * .5 * N), e.uniform1f(l.uPointerActive, +!!o.active.value), e.uniform1f(l.uPointerAttraction, j), e.uniform1f(l.uPointerStrength, D.pointerStrength * N);
551
+ let M = Math.max(0, Math.min(1, j)), P = o.velocity.value;
552
+ e.uniform2f(l.uVelocity, P.x * .5 * N, P.y * .5 * N), e.uniform1f(l.uStretch, D.stretch);
553
+ let F = o.trailSources(C.satelliteRadius * .7 * M);
552
554
  e.uniform1i(l.uTrailCount, F.count);
553
555
  for (let t = 0; t < 15; t++) {
554
556
  let n = p[t] ?? null, r = m[t] ?? null, i = F.sources[t];
555
- i ? (e.uniform2f(n, i.x * .5 * A, i.y * .5 * A), e.uniform1f(r, i.radius * A)) : (e.uniform2f(n, 0, 0), e.uniform1f(r, 0));
557
+ i ? (e.uniform2f(n, i.x * .5 * N, i.y * .5 * N), e.uniform1f(r, i.radius * N)) : (e.uniform2f(n, 0, 0), e.uniform1f(r, 0));
556
558
  }
557
- e.uniform1f(l.uBodyRadius, C.bodyRadius * A), e.uniform1f(l.uPulsePhase, y * w.pulseFreq * g.pulseFreq * Math.PI * 2);
559
+ e.uniform1f(l.uBodyRadius, C.bodyRadius * N), e.uniform1f(l.uPulsePhase, y * w.pulseFreq * g.pulseFreq * Math.PI * 2);
558
560
  let I = o.pulse.value * D.clickImpulse;
559
- e.uniform1f(l.uPulseAmp, (w.pulseAmp * g.pulseAmp / .015 + I) * A), e.uniform1f(l.uNoiseAmp, w.noiseAmp * g.noiseAmp / .025 * A), e.uniform1f(l.uNoiseFreq, w.noiseFreq), e.uniform1f(l.uNoiseSpeed, w.noiseSpeed), e.uniform1f(l.uWarpAmp, w.warpAmp);
561
+ e.uniform1f(l.uPulseAmp, (w.pulseAmp * g.pulseAmp / .015 + I) * N), e.uniform1f(l.uNoiseAmp, w.noiseAmp * g.noiseAmp / .025 * N), e.uniform1f(l.uNoiseFreq, w.noiseFreq), e.uniform1f(l.uNoiseSpeed, w.noiseSpeed), e.uniform1f(l.uWarpAmp, w.warpAmp);
560
562
  let L = C.orbitRadius * 1.2 * (1 + C.eccentricity), R = w.smoothK * g.smoothK, z = Math.max(0, L - C.satelliteRadius - C.bodyRadius), B = R > 0 ? Math.min(1.25, 1 + z / R) : 1;
561
- e.uniform1f(l.uSmoothK, R * B * A), e.uniform1f(l.uMerge, +(w.merge === "circular"));
562
- let V = L + C.satelliteRadius, H = (Math.max(C.bodyRadius, V) + R * B + w.noiseAmp * g.noiseAmp / .025 + Math.abs(o.pulse.value) * D.clickImpulse) * A + .1;
563
+ e.uniform1f(l.uSmoothK, R * B * N), e.uniform1f(l.uMerge, +(w.merge === "circular"));
564
+ let V = L + C.satelliteRadius, H = (Math.max(C.bodyRadius, V) + R * B + w.noiseAmp * g.noiseAmp / .025 + Math.abs(o.pulse.value) * D.clickImpulse) * N + .1;
563
565
  e.uniform1f(l.uMaxReach, H), e.uniform1f(l.uHueRange, T.hueRange + g.hueRange), e.uniform1f(l.uSatShift, T.satShift + g.satShift), e.uniform1f(l.uBrightnessShift, T.brightnessShift + g.brightnessShift), e.uniform1f(l.uColorNoiseFreq, T.colorNoiseFreq), e.uniform1f(l.uColorNoiseSpeed, T.colorNoiseSpeed), e.uniform1f(l.uLit, +!!E.lit);
564
566
  let U = b(x);
565
567
  e.uniform3f(l.uRimColor, U[0], U[1], U[2]), e.uniform3f(l.uLightDir, E.lightDir[0], E.lightDir[1], E.lightDir[2]), e.uniform1f(l.uSpecStrength, E.specStrength), e.uniform1f(l.uSpecShininess, E.specShininess), e.uniform1f(l.uRimPower, E.rimPower), e.uniform1f(l.uRimStrength, E.rimStrength), e.uniform1f(l.uIridescence, E.iridescence * g.iridScale), e.uniform1f(l.uIridHue, E.iridHue * (Math.PI / 180)), e.uniform1f(l.uIridSpeed, E.iridSpeed), e.uniform1f(l.uSssScale, E.sssScale * g.iridScale), e.uniform1f(l.uSssPower, E.sssPower), e.uniform1f(l.uCoreGlow, E.coreGlow);
@@ -567,20 +569,20 @@ function ve(e, t, n, r, i, a, o, s, c) {
567
569
  e.uniform1i(l.uSatCount, W.length);
568
570
  for (let t = 0; t < 4; t++) {
569
571
  let n = u[t] ?? null, r = d[t] ?? null, i = f[t] ?? null, a = W[t];
570
- a ? (e.uniform2f(n, a.x * A, a.y * A), e.uniform1f(r, a.radius * A), e.uniform1f(i, a.opacity)) : (e.uniform2f(n, 0, 0), e.uniform1f(r, 0), e.uniform1f(i, 0));
572
+ a ? (e.uniform2f(n, a.x * N, a.y * N), e.uniform1f(r, a.radius * N), e.uniform1f(i, a.opacity)) : (e.uniform2f(n, 0, 0), e.uniform1f(r, 0), e.uniform1f(i, 0));
571
573
  }
572
574
  e.clearColor(0, 0, 0, 0), e.clear(e.COLOR_BUFFER_BIT), e.drawArrays(e.TRIANGLES, 0, 6), e.bindVertexArray(null);
573
575
  }
574
576
  //#endregion
575
577
  //#region src/components/custom/goo-blob/composables/buildMetaballProgram.ts
576
- var $ = "[GooBlob]";
577
- function ye(e) {
578
- let t = c(e, e.VERTEX_SHADER, le, $), n = c(e, e.FRAGMENT_SHADER, _e, $), r = i(e, t, n, $);
578
+ var Q = "[GooBlob]";
579
+ function Ce(e) {
580
+ let t = d(e, e.VERTEX_SHADER, pe, Q), n = d(e, e.FRAGMENT_SHADER, xe, Q), r = u(e, t, n, Q);
579
581
  e.useProgram(r);
580
- let a = e.createVertexArray();
581
- e.bindVertexArray(a);
582
- let o = e.createBuffer();
583
- e.bindBuffer(e.ARRAY_BUFFER, o), e.bufferData(e.ARRAY_BUFFER, new Float32Array([
582
+ let i = e.createVertexArray();
583
+ e.bindVertexArray(i);
584
+ let a = e.createBuffer();
585
+ e.bindBuffer(e.ARRAY_BUFFER, a), e.bufferData(e.ARRAY_BUFFER, new Float32Array([
584
586
  -1,
585
587
  -1,
586
588
  1,
@@ -594,12 +596,12 @@ function ye(e) {
594
596
  1,
595
597
  1
596
598
  ]), e.STATIC_DRAW);
597
- let s = e.getAttribLocation(r, "aPosition");
598
- e.enableVertexAttribArray(s), e.vertexAttribPointer(s, 2, e.FLOAT, !1, 0, 0);
599
- let l = {};
600
- for (let t of te) l[t] = e.getUniformLocation(r, t);
601
- let u = [], d = [], f = [];
602
- for (let t = 0; t < 4; t++) u.push(e.getUniformLocation(r, `uSatPos[${t}]`)), d.push(e.getUniformLocation(r, `uSatRadius[${t}]`)), f.push(e.getUniformLocation(r, `uSatOpacity[${t}]`));
599
+ let o = e.getAttribLocation(r, "aPosition");
600
+ e.enableVertexAttribArray(o), e.vertexAttribPointer(o, 2, e.FLOAT, !1, 0, 0);
601
+ let s = {};
602
+ for (let t of re) s[t] = e.getUniformLocation(r, t);
603
+ let c = [], l = [], f = [];
604
+ for (let t = 0; t < 4; t++) c.push(e.getUniformLocation(r, `uSatPos[${t}]`)), l.push(e.getUniformLocation(r, `uSatRadius[${t}]`)), f.push(e.getUniformLocation(r, `uSatOpacity[${t}]`));
603
605
  let p = [], m = [];
604
606
  for (let t = 0; t < 15; t++) p.push(e.getUniformLocation(r, `uTrailPos[${t}]`)), m.push(e.getUniformLocation(r, `uTrailRadius[${t}]`));
605
607
  let h = [];
@@ -608,12 +610,12 @@ function ye(e) {
608
610
  prog: r,
609
611
  vs: t,
610
612
  fs: n,
611
- vao: a,
612
- buf: o,
613
+ vao: i,
614
+ buf: a,
613
615
  locs: {
614
- U: l,
615
- satPosLocs: u,
616
- satRadLocs: d,
616
+ U: s,
617
+ satPosLocs: c,
618
+ satRadLocs: l,
617
619
  satOpLocs: f,
618
620
  trailPosLocs: p,
619
621
  trailRadLocs: m,
@@ -622,86 +624,690 @@ function ye(e) {
622
624
  };
623
625
  }
624
626
  //#endregion
627
+ //#region src/components/custom/goo-blob/shaders/metaball.wgsl.ts
628
+ var we = `
629
+ const MAX_SATS: i32 = 4;
630
+ const TRAIL_N: i32 = 15;
631
+ const MAX_BLOB_STOPS: i32 = 4;
632
+ const PI: f32 = 3.141592653589793;
633
+ const FBM_LACUNARITY: f32 = 1.8;
634
+ const FBM_GAIN: f32 = 0.42;
635
+
636
+ // ── Uniforms (the typed-struct source-of-truth — see uniformBridgeWGPU.ts) ──
637
+ // Scalars packed into vec4 lanes; per-satellite / per-trail / per-palette rows packed
638
+ // into a vec4 so the array stride is the natural 16 bytes (no std140 stride trap — the
639
+ // parity-blowout suspect the wave's §Triumvirate names).
640
+ struct Uniforms {
641
+ // s0: (uTime, uBodyRadius, uPulsePhase, uPulseAmp)
642
+ s0: vec4<f32>,
643
+ // s1: (uNoiseAmp, uNoiseFreq, uNoiseSpeed, uWarpAmp)
644
+ s1: vec4<f32>,
645
+ // s2: (uSmoothK, uMerge, uMaxReach, uLit)
646
+ s2: vec4<f32>,
647
+ // s3: (uSpecStrength, uSpecShininess, uRimPower, uRimStrength)
648
+ s3: vec4<f32>,
649
+ // s4: (uIridescence, uIridHue, uIridSpeed, uSssScale)
650
+ s4: vec4<f32>,
651
+ // s5: (uSssPower, uCoreGlow, uHueRange, uSatShift)
652
+ s5: vec4<f32>,
653
+ // s6: (uBrightnessShift, uColorNoiseFreq, uColorNoiseSpeed, uStretch)
654
+ s6: vec4<f32>,
655
+ // s7: (uPointerActive, uPointerAttraction, uPointerStrength, _pad)
656
+ s7: vec4<f32>,
657
+ // ptr: (uPointer.x, uPointer.y, uVelocity.x, uVelocity.y)
658
+ ptr: vec4<f32>,
659
+ // base: (uBaseColor.rgb, _pad)
660
+ base: vec4<f32>,
661
+ // rim: (uRimColor.rgb, _pad)
662
+ rim: vec4<f32>,
663
+ // light: (uLightDir.xyz, _pad)
664
+ light: vec4<f32>,
665
+ // res: (uResolution.x, uResolution.y, _pad, _pad)
666
+ res: vec4<f32>,
667
+ // ints: (uStopCount, uSatCount, uTrailCount, _pad)
668
+ ints: vec4<i32>,
669
+ palette: array<vec4<f32>, 4>,
670
+ satPos: array<vec4<f32>, 4>, // (pos.x, pos.y, radius, opacity)
671
+ trailPos: array<vec4<f32>, 15>, // (pos.x, pos.y, radius, _pad)
672
+ };
673
+
674
+ @group(0) @binding(0) var<uniform> u: Uniforms;
675
+
676
+ // ── Noise (blob-LOCAL 3D-p3 hash + IQ analytic-derivative gradient noise) ──
677
+ fn hash21(p: vec2<f32>) -> f32 {
678
+ var p3 = fract(vec3<f32>(p.x, p.y, p.x) * 0.1031);
679
+ p3 = p3 + dot(p3, p3.yzx + 33.33);
680
+ return fract((p3.x + p3.y) * p3.z);
681
+ }
682
+
683
+ // IQ analytic-derivative gradient noise — value in .x, ANALYTIC gradient (d/dx, d/dy) in
684
+ // .yz, from one eval. The quintic fade has a continuous 2nd derivative.
685
+ fn noised(p: vec2<f32>) -> vec3<f32> {
686
+ let i = floor(p);
687
+ let f = fract(p);
688
+ let uu = f * f * f * (f * (f * 6.0 - 15.0) + 10.0);
689
+ let du = 30.0 * f * f * (f * (f - 2.0) + 1.0);
690
+
691
+ let ga = -1.0 + 2.0 * vec2<f32>(hash21(i + vec2<f32>(0.0, 0.0)), hash21(i + vec2<f32>(7.3, 0.0)));
692
+ let gb = -1.0 + 2.0 * vec2<f32>(hash21(i + vec2<f32>(1.0, 0.0)), hash21(i + vec2<f32>(8.3, 0.0)));
693
+ let gc = -1.0 + 2.0 * vec2<f32>(hash21(i + vec2<f32>(0.0, 1.0)), hash21(i + vec2<f32>(7.3, 1.0)));
694
+ let gd = -1.0 + 2.0 * vec2<f32>(hash21(i + vec2<f32>(1.0, 1.0)), hash21(i + vec2<f32>(8.3, 1.0)));
695
+
696
+ let va = dot(ga, f - vec2<f32>(0.0, 0.0));
697
+ let vb = dot(gb, f - vec2<f32>(1.0, 0.0));
698
+ let vc = dot(gc, f - vec2<f32>(0.0, 1.0));
699
+ let vd = dot(gd, f - vec2<f32>(1.0, 1.0));
700
+
701
+ let n = va + uu.x * (vb - va) + uu.y * (vc - va) + uu.x * uu.y * (va - vb - vc + vd);
702
+ let value = 0.5 + 0.5 * n;
703
+
704
+ let grad =
705
+ ga + uu.x * (gb - ga) + uu.y * (gc - ga) + uu.x * uu.y * (ga - gb - gc + gd) +
706
+ du * (vec2<f32>(uu.y, uu.x) * (va - vb - vc + vd) + vec2<f32>(vb, vc) - va);
707
+ return vec3<f32>(value, 0.5 * grad);
708
+ }
709
+
710
+ ${c}
711
+
712
+ fn fbm(p0: vec2<f32>, octaves: i32) -> f32 {
713
+ var value = 0.0;
714
+ var amp = 0.5;
715
+ var freq = 1.0;
716
+ var p = p0;
717
+ for (var i = 0; i < 4; i = i + 1) {
718
+ if (i >= octaves) { break; }
719
+ value = value + amp * noised(p * freq).x;
720
+ p = FBM_ROT * p;
721
+ freq = freq * FBM_LACUNARITY;
722
+ amp = amp * FBM_GAIN;
723
+ }
724
+ return value;
725
+ }
726
+
727
+ // FBM with ANALYTIC GRADIENT — value in .x, ∂/∂x, ∂/∂y in .yz. The gradient accumulates
728
+ // noised()'s analytic gradient per octave, chain-ruled through the per-octave frequency
729
+ // scale AND the FBM_ROT rotation (the keystone feed for the analytic surface normal).
730
+ fn fbmG(p: vec2<f32>, octaves: i32) -> vec3<f32> {
731
+ var value = 0.0;
732
+ var grad = vec2<f32>(0.0, 0.0);
733
+ var amp = 0.5;
734
+ var freq = 1.0;
735
+ var rotAccum = mat2x2<f32>(1.0, 0.0, 0.0, 1.0);
736
+ var pp = p;
737
+ for (var i = 0; i < 4; i = i + 1) {
738
+ if (i >= octaves) { break; }
739
+ let n = noised(pp * freq);
740
+ value = value + amp * n.x;
741
+ grad = grad + amp * freq * (transpose(rotAccum) * n.yz);
742
+ pp = FBM_ROT * pp;
743
+ freq = freq * FBM_LACUNARITY;
744
+ amp = amp * FBM_GAIN;
745
+ rotAccum = FBM_ROT * rotAccum;
746
+ }
747
+ return vec3<f32>(value, grad);
748
+ }
749
+
750
+ // One domain-warp pass with the ANALYTIC GRADIENT carried (warp-Jacobian approximation).
751
+ fn fbmWarpedG(p: vec2<f32>, octaves: i32, warpAmp: f32) -> vec3<f32> {
752
+ if (warpAmp <= 0.0) { return fbmG(p, octaves); }
753
+ let q = vec2<f32>(fbm(p + vec2<f32>(0.0, 0.0), octaves),
754
+ fbm(p + vec2<f32>(5.2, 1.3), octaves));
755
+ return fbmG(p + warpAmp * (q - 0.5), octaves);
756
+ }
757
+
758
+ // ── SDF body (value+gradient circle + the IQ normalized smin, value+gradient form) ──
759
+ fn sdgCircle(p: vec2<f32>, center: vec2<f32>, radius: f32) -> vec3<f32> {
760
+ let d = p - center;
761
+ let len = length(d);
762
+ var grad = vec2<f32>(0.0, 1.0);
763
+ if (len > 1e-6) { grad = d / len; }
764
+ return vec3<f32>(len - radius, grad);
765
+ }
766
+
767
+ fn sminQuadraticG(a: vec3<f32>, b: vec3<f32>, k0: f32) -> vec3<f32> {
768
+ let k = k0 * 4.0;
769
+ let h = max(k - abs(a.x - b.x), 0.0) / k;
770
+ let m = h * h * k * 0.25;
771
+ var w = 1.0 - 0.5 * h * h;
772
+ if (a.x < b.x) { w = 0.5 * h * h; }
773
+ let dist = min(a.x, b.x) - m;
774
+ let grad = mix(a.yz, b.yz, w);
775
+ return vec3<f32>(dist, grad);
776
+ }
777
+
778
+ fn sminCircularG(a: vec3<f32>, b: vec3<f32>, k0: f32) -> vec3<f32> {
779
+ let k = k0 * (1.0 / (1.0 - sqrt(0.5)));
780
+ let h = max(k - abs(a.x - b.x), 0.0) / k;
781
+ let m = k * 0.5 * (1.0 + h - sqrt(1.0 - h * (h - 2.0)));
782
+ var w = 1.0 - 0.5 * h;
783
+ if (a.x < b.x) { w = 0.5 * h; }
784
+ let dist = min(a.x, b.x) - m;
785
+ let grad = mix(a.yz, b.yz, w);
786
+ return vec3<f32>(dist, grad);
787
+ }
788
+
789
+ fn sminG(a: vec3<f32>, b: vec3<f32>, k: f32) -> vec3<f32> {
790
+ if (u.s2.y > 0.5) { return sminCircularG(a, b, k); }
791
+ return sminQuadraticG(a, b, k);
792
+ }
793
+
794
+ ${o}
795
+ ${s}
796
+
797
+ // ── OKLCh gamut clamp (hue-preserving inward chroma clamp — blob-local) ──
798
+ fn inGamut(lin: vec3<f32>) -> bool {
799
+ return all(lin >= vec3<f32>(0.0)) && all(lin <= vec3<f32>(1.0));
800
+ }
801
+ fn gamutClampOklch(lch: vec3<f32>) -> vec3<f32> {
802
+ let lin = oklabToLinearSrgb(oklchToOklab(lch));
803
+ if (inGamut(lin)) { return lch; }
804
+ var lo = 0.0;
805
+ var hi = lch.y;
806
+ for (var i = 0; i < 16; i = i + 1) {
807
+ let mid = 0.5 * (lo + hi);
808
+ let test = vec3<f32>(lch.x, mid, lch.z);
809
+ if (inGamut(oklabToLinearSrgb(oklchToOklab(test)))) { lo = mid; } else { hi = mid; }
810
+ }
811
+ return vec3<f32>(lch.x, lo, lch.z);
812
+ }
813
+
814
+ // AX.W15 — de-synced breath (three DETUNED sines at irrational ratios).
815
+ fn breath(phase: f32) -> f32 {
816
+ return (sin(phase) + 0.5 * sin(phase * 1.4444 + 1.7) + 0.28 * sin(phase * 0.31 + 4.1)) / 1.78;
817
+ }
818
+
819
+ // AX.W15 — the composite SDF field WITH its ANALYTIC GRADIENT vec3(dist, ∂/∂x, ∂/∂y).
820
+ fn sceneDistG(uv: vec2<f32>) -> vec3<f32> {
821
+ let uBodyRadius = u.s0.y;
822
+ let uPulsePhase = u.s0.z;
823
+ let uPulseAmp = u.s0.w;
824
+ let uNoiseAmp = u.s1.x;
825
+ let uNoiseFreq = u.s1.y;
826
+ let uNoiseSpeed = u.s1.z;
827
+ let uWarpAmp = u.s1.w;
828
+ let uSmoothK = u.s2.x;
829
+ let uTime = u.s0.x;
830
+ let uVelocity = u.ptr.zw;
831
+ let uStretch = u.s6.w;
832
+ let uSatCount = u.ints.y;
833
+ let uTrailCount = u.ints.z;
834
+
835
+ let bodyR = uBodyRadius + breath(uPulsePhase) * uPulseAmp;
836
+
837
+ // Velocity-driven VOLUME-PRESERVING squash-and-stretch (tanh-saturated).
838
+ var bodyUv = uv;
839
+ var ax = vec2<f32>(1.0, 0.0);
840
+ var perp = vec2<f32>(0.0, 1.0);
841
+ var sa = 1.0;
842
+ let speed = length(uVelocity);
843
+ let squashed = uStretch > 0.0 && speed > 1e-4;
844
+ if (squashed) {
845
+ ax = uVelocity / speed;
846
+ perp = vec2<f32>(-ax.y, ax.x);
847
+ sa = 1.0 + tanh(speed * 1.6) * uStretch;
848
+ bodyUv = vec2<f32>(dot(uv, ax) / sa, dot(uv, perp) * sa);
849
+ }
850
+
851
+ // Domain-warped FBM displacement WITH its analytic gradient.
852
+ let fbmv = fbmWarpedG(bodyUv * uNoiseFreq + uTime * uNoiseSpeed, 3, uWarpAmp);
853
+ let bodyDisplacement = (fbmv.x - 0.5) * uNoiseAmp;
854
+ let dispGradBody = uNoiseAmp * uNoiseFreq * fbmv.yz;
855
+
856
+ let bodyG = sdgCircle(bodyUv, vec2<f32>(0.0), bodyR + bodyDisplacement);
857
+ let bodyGradBody = bodyG.yz - dispGradBody;
858
+
859
+ var bodyGradUv: vec2<f32>;
860
+ if (squashed) {
861
+ let scaled = vec2<f32>(bodyGradBody.x / sa, bodyGradBody.y * sa);
862
+ bodyGradUv = scaled.x * ax + scaled.y * perp;
863
+ } else {
864
+ bodyGradUv = bodyGradBody;
865
+ }
866
+ var d = vec3<f32>(bodyG.x, bodyGradUv);
867
+
868
+ // Satellites — smin-merged into the body.
869
+ for (var i = 0; i < MAX_SATS; i = i + 1) {
870
+ if (i >= uSatCount) { break; }
871
+ let s = u.satPos[i];
872
+ var satG = sdgCircle(uv, s.xy, s.z);
873
+ satG.x = satG.x + (1.0 - s.w) * 0.3;
874
+ d = sminG(d, satG, uSmoothK);
875
+ }
876
+
877
+ // Pointer trail — a decaying-radius pseudopod.
878
+ for (var i = 0; i < TRAIL_N; i = i + 1) {
879
+ if (i >= uTrailCount) { break; }
880
+ let tp = u.trailPos[i];
881
+ let trailG = sdgCircle(uv, tp.xy, tp.z);
882
+ d = sminG(d, trailG, uSmoothK);
883
+ }
884
+ return d;
885
+ }
886
+
887
+ // AX.W15 — the analytic surface normal from the field gradient.
888
+ fn surfaceNormalFromGrad(grad2d: vec2<f32>, d: f32, bodyR: f32) -> vec3<f32> {
889
+ let g = normalize(grad2d + vec2<f32>(1e-6));
890
+ let interior = clamp(-d / max(bodyR, 1e-4), 0.0, 1.0);
891
+ let z = sqrt(max(0.0, 1.0 - (1.0 - interior) * (1.0 - interior)));
892
+ return normalize(vec3<f32>(g * (1.0 - z), z) + vec3<f32>(0.0, 0.0, 1e-6));
893
+ }
894
+
895
+ // W11.b — sample the multi-stop palette at t, OKLab mix + midpoint chroma-bump. Returns
896
+ // an OKLCh stop [L, C, h(rad)]. Falls back to uBaseColor when uStopCount <= 1.
897
+ fn samplePaletteOklch(t: f32) -> vec3<f32> {
898
+ let uStopCount = u.ints.x;
899
+ if (uStopCount <= 1) { return oklabToOklch(srgbToOklab(u.base.rgb)); }
900
+ let n = f32(uStopCount);
901
+ let ft = clamp(t, 0.0, 1.0) * (n - 1.0);
902
+ let i0 = i32(floor(ft));
903
+ let i1 = min(i0 + 1, uStopCount - 1);
904
+ let f = ft - f32(i0);
905
+ let labA = srgbToOklab(u.palette[i0].rgb);
906
+ let labB = srgbToOklab(u.palette[i1].rgb);
907
+ let lab = mix(labA, labB, f);
908
+ var lch = oklabToOklch(lab);
909
+ lch.y = lch.y + 0.03 * sin(PI * f);
910
+ return lch;
911
+ }
912
+
913
+ // ── Full-screen triangle vertex stage (the pilot idiom — no vertex buffer) ──
914
+ struct VSOut {
915
+ @builtin(position) pos: vec4<f32>,
916
+ @location(0) uv: vec2<f32>,
917
+ };
918
+
919
+ @vertex
920
+ fn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {
921
+ var p = vec2<f32>(-1.0, -1.0);
922
+ if (vi == 1u) { p = vec2<f32>(3.0, -1.0); }
923
+ else if (vi == 2u) { p = vec2<f32>(-1.0, 3.0); }
924
+ var out: VSOut;
925
+ out.pos = vec4<f32>(p, 0.0, 1.0);
926
+ // vUv = aPos * 0.5 + 0.5 (the metaball.vert convention).
927
+ out.uv = p * 0.5 + 0.5;
928
+ return out;
929
+ }
930
+
931
+ // ── Fragment stage (the main() transcription) ──
932
+ @fragment
933
+ fn fs_main(in: VSOut) -> @location(0) vec4<f32> {
934
+ let uTime = u.s0.x;
935
+ let uBodyRadius = u.s0.y;
936
+ let uPulsePhase = u.s0.z;
937
+ let uPulseAmp = u.s0.w;
938
+ let uMaxReach = u.s2.z;
939
+ let uLit = u.s2.w;
940
+ let uPointerActive = u.s7.x;
941
+ let uPointerAttraction = u.s7.y;
942
+ let uPointerStrength = u.s7.z;
943
+ let uPointer = u.ptr.xy;
944
+ let uHueRange = u.s5.z;
945
+ let uSatShift = u.s5.w;
946
+ let uBrightnessShift = u.s6.x;
947
+ let uColorNoiseFreq = u.s6.y;
948
+ let uColorNoiseSpeed = u.s6.z;
949
+ let uIridescence = u.s4.x;
950
+ let uIridHue = u.s4.y;
951
+ let uIridSpeed = u.s4.z;
952
+ let uSssScale = u.s4.w;
953
+ let uSssPower = u.s5.x;
954
+ let uCoreGlow = u.s5.y;
955
+ let uSpecStrength = u.s3.x;
956
+ let uSpecShininess = u.s3.y;
957
+ let uRimPower = u.s3.z;
958
+ let uRimStrength = u.s3.w;
959
+ let uLightDir = u.light.xyz;
960
+ let uRimColor = u.rim.rgb;
961
+
962
+ var uv = in.uv - 0.5;
963
+
964
+ // PRE-FBM bounding early-out — transparent WRITE (not a discard).
965
+ if (dot(uv, uv) > uMaxReach * uMaxReach) {
966
+ return vec4<f32>(0.0);
967
+ }
968
+
969
+ // Pointer deformation — sign per AY.W-BLOB-CONFIG D2.
970
+ if (uPointerActive > 0.5) {
971
+ let pointerDir = uPointer - uv;
972
+ let pointerDist = length(pointerDir);
973
+ let influence = smoothstep(0.5, 0.0, pointerDist) * uPointerAttraction * uPointerStrength;
974
+ uv = uv + normalize(pointerDir + vec2<f32>(1e-6)) * influence;
975
+ }
976
+
977
+ let bodyR = uBodyRadius + breath(uPulsePhase) * uPulseAmp;
978
+
979
+ // Composite domain-warped membrane field WITH its analytic gradient.
980
+ let scene = sceneDistG(uv);
981
+ let d = scene.x;
982
+ let fieldGrad = scene.yz;
983
+
984
+ // ── fwidth SITE #1 (metaball.frag.ts line 266) — AA-edge half-width from the SDF
985
+ // screen-space gradient. The WGSL fragment-stage fwidth() transcription. ──
986
+ let aa = max(fwidth(d), 1e-6);
987
+ let alpha = 1.0 - smoothstep(-aa, aa, d);
988
+
989
+ if (alpha < 0.001) {
990
+ return vec4<f32>(0.0);
991
+ }
992
+
993
+ // Edit #4 — perceptually-uniform OKLCh variation.
994
+ let colorNoise = fbm(uv * uColorNoiseFreq + uTime * uColorNoiseSpeed, 3);
995
+ var oklch = samplePaletteOklch(colorNoise);
996
+
997
+ oklch.z = oklch.z + (colorNoise - 0.5) * uHueRange * (PI / 180.0);
998
+ oklch.y = max(oklch.y + (colorNoise - 0.5) * uSatShift, 0.0);
999
+ oklch.x = clamp(oklch.x + uBrightnessShift, 0.0, 1.0);
1000
+
1001
+ let N = surfaceNormalFromGrad(fieldGrad, d, bodyR);
1002
+ let V = vec3<f32>(0.0, 0.0, 1.0);
1003
+ let thickness = clamp(-d / max(bodyR, 1e-4), 0.0, 1.0);
1004
+ let fres = pow(1.0 - max(dot(N, V), 0.0), 2.5);
1005
+
1006
+ // ── W11.a iridescence — warm-biased IQ cosine palette driving OKLCh HUE ──
1007
+ if (uIridescence > 0.0) {
1008
+ let t = fres + 0.3 * colorNoise + uTime * uIridSpeed;
1009
+ let iridHue = uIridHue + 0.18 * PI * cos(2.0 * PI * t);
1010
+ let w = fres * uIridescence;
1011
+ oklch.z = mix(oklch.z, iridHue, w);
1012
+ oklch.y = min(oklch.y + 0.04 * w, oklch.y + 0.08);
1013
+ oklch.x = min(oklch.x + 0.05 * w, 1.0);
1014
+ }
1015
+
1016
+ // ── W11.a fake subsurface translucency — thickness inner-glow + back-light ──
1017
+ if (uCoreGlow > 0.0 || uSssScale > 0.0) {
1018
+ oklch.x = min(oklch.x + uCoreGlow * (1.0 - exp(-3.0 * thickness)), 1.0);
1019
+ let L = normalize(uLightDir + vec3<f32>(1e-6));
1020
+ let back = pow(clamp(dot(V, -(L + N * thickness)), 0.0, 1.0), uSssPower);
1021
+ let sss = back * uSssScale * (1.0 - thickness);
1022
+ oklch.x = min(oklch.x + sss, 1.0);
1023
+ oklch.z = oklch.z + sss * 0.1 * (1.0 - thickness);
1024
+ }
1025
+
1026
+ oklch = gamutClampOklch(oklch);
1027
+
1028
+ var lin = oklabToLinearSrgb(oklchToOklab(oklch));
1029
+
1030
+ // ── W9.b lit glass surface — Blinn-Phong glint + Fresnel rim, in LINEAR ──
1031
+ if (uLit > 0.5) {
1032
+ let L = normalize(uLightDir + vec3<f32>(1e-6));
1033
+ let H = normalize(L + V);
1034
+
1035
+ let warmCream = oklabToLinearSrgb(oklchToOklab(vec3<f32>(0.97, 0.03, radians(85.0))));
1036
+
1037
+ // ── fwidth SITE #2 (metaball.frag.ts line 364) — Toksvig normal-variance
1038
+ // specular clamp. The WGSL fragment-stage fwidth() of the surface normal. ──
1039
+ let nVar = length(fwidth(N));
1040
+ let shininess = uSpecShininess / (1.0 + 24.0 * nVar);
1041
+ let energyNorm = (shininess + 2.0) / 8.0;
1042
+ let spec = pow(max(dot(N, H), 0.0), shininess) * uSpecStrength * energyNorm;
1043
+
1044
+ var rimOkl = oklabToOklch(srgbToOklab(uRimColor));
1045
+ let bodyL = oklch.x;
1046
+ let dL = abs(rimOkl.x - bodyL);
1047
+ let lack = clamp((0.22 - dL) / 0.22, 0.0, 1.0);
1048
+ var target = 0.18;
1049
+ if (bodyL < 0.5) { target = 0.92; }
1050
+ rimOkl.x = clamp(mix(rimOkl.x, target, lack), 0.0, 1.0);
1051
+ rimOkl.y = rimOkl.y * mix(1.0, 0.5, lack);
1052
+ let rimLin = oklabToLinearSrgb(oklchToOklab(rimOkl));
1053
+
1054
+ let rimFres = pow(1.0 - max(dot(N, V), 0.0), uRimPower);
1055
+ let rim = rimFres * uRimStrength * (1.0 - 0.6 * thickness);
1056
+
1057
+ var highlight = max(warmCream * spec, rimLin * rim);
1058
+ highlight = min(highlight, vec3<f32>(0.85));
1059
+ lin = lin + highlight;
1060
+ }
1061
+
1062
+ var rgb = clamp(linearToSrgb(lin), vec3<f32>(0.0), vec3<f32>(1.0));
1063
+
1064
+ // IGN dither — 1-LSB triangular dither in DISPLAY space, AFTER linearToSrgb.
1065
+ let fragCoord = in.pos.xy;
1066
+ let ign = fract(52.9829189 * fract(dot(fragCoord, vec2<f32>(0.06711056, 0.00583715))));
1067
+ rgb = clamp(rgb + (ign - 0.5) / 255.0, vec3<f32>(0.0), vec3<f32>(1.0));
1068
+
1069
+ // premultiply AFTER the OETF: straight-alpha gamma → premultiplied.
1070
+ return vec4<f32>(rgb * alpha, alpha);
1071
+ }
1072
+ `, Te = 592, $ = {
1073
+ s0: 0,
1074
+ s1: 4,
1075
+ s2: 8,
1076
+ s3: 12,
1077
+ s4: 16,
1078
+ s5: 20,
1079
+ s6: 24,
1080
+ s7: 28,
1081
+ ptr: 32,
1082
+ base: 36,
1083
+ rim: 40,
1084
+ light: 44,
1085
+ res: 48,
1086
+ ints: 52,
1087
+ palette: 56,
1088
+ satPos: 72,
1089
+ trailPos: 88
1090
+ };
1091
+ function Ee() {
1092
+ let e = new ArrayBuffer(Te);
1093
+ return {
1094
+ buffer: e,
1095
+ f32: new Float32Array(e),
1096
+ i32: new Int32Array(e)
1097
+ };
1098
+ }
1099
+ function De(e, t, n, r, i, a) {
1100
+ let { f32: o, i32: s } = e, { params: c, rgb: l, simTimeMs: u, timeSec: d, resolveColor: f, rimColor: p, paletteStops: m } = a, { geometry: h, membrane: g, color: _, surface: v, interaction: y } = n, b = r.pointer.value, x = y.pointerAttraction + c.pointerAttraction, S = Math.max(0, Math.min(1, x)), C = r.velocity.value, w = r.pulse.value * y.clickImpulse, T = (g.pulseAmp * c.pulseAmp / .015 + w) * N, E = d * g.pulseFreq * c.pulseFreq * Math.PI * 2, D = g.noiseAmp * c.noiseAmp / .025 * N, O = h.orbitRadius * 1.2 * (1 + h.eccentricity), k = g.smoothK * c.smoothK, A = Math.max(0, O - h.satelliteRadius - h.bodyRadius), j = k > 0 ? Math.min(1.25, 1 + A / k) : 1, M = k * j * N, P = O + h.satelliteRadius, F = (Math.max(h.bodyRadius, P) + k * j + g.noiseAmp * c.noiseAmp / .025 + Math.abs(r.pulse.value) * y.clickImpulse) * N + .1;
1101
+ o[$.s0 + 0] = u / 1e3, o[$.s0 + 1] = h.bodyRadius * N, o[$.s0 + 2] = E, o[$.s0 + 3] = T, o[$.s1 + 0] = D, o[$.s1 + 1] = g.noiseFreq, o[$.s1 + 2] = g.noiseSpeed, o[$.s1 + 3] = g.warpAmp, o[$.s2 + 0] = M, o[$.s2 + 1] = +(g.merge === "circular"), o[$.s2 + 2] = F, o[$.s2 + 3] = +!!v.lit, o[$.s3 + 0] = v.specStrength, o[$.s3 + 1] = v.specShininess, o[$.s3 + 2] = v.rimPower, o[$.s3 + 3] = v.rimStrength, o[$.s4 + 0] = v.iridescence * c.iridScale, o[$.s4 + 1] = v.iridHue * (Math.PI / 180), o[$.s4 + 2] = v.iridSpeed, o[$.s4 + 3] = v.sssScale * c.iridScale, o[$.s5 + 0] = v.sssPower, o[$.s5 + 1] = v.coreGlow, o[$.s5 + 2] = _.hueRange + c.hueRange, o[$.s5 + 3] = _.satShift + c.satShift, o[$.s6 + 0] = _.brightnessShift + c.brightnessShift, o[$.s6 + 1] = _.colorNoiseFreq, o[$.s6 + 2] = _.colorNoiseSpeed, o[$.s6 + 3] = y.stretch, o[$.s7 + 0] = +!!r.active.value, o[$.s7 + 1] = x, o[$.s7 + 2] = y.pointerStrength * N, o[$.s7 + 3] = 0, o[$.ptr + 0] = b.x * .5 * N, o[$.ptr + 1] = b.y * .5 * N, o[$.ptr + 2] = C.x * .5 * N, o[$.ptr + 3] = C.y * .5 * N, o[$.base + 0] = l[0], o[$.base + 1] = l[1], o[$.base + 2] = l[2], o[$.base + 3] = 0;
1102
+ let I = f(p);
1103
+ o[$.rim + 0] = I[0], o[$.rim + 1] = I[1], o[$.rim + 2] = I[2], o[$.rim + 3] = 0, o[$.light + 0] = v.lightDir[0], o[$.light + 1] = v.lightDir[1], o[$.light + 2] = v.lightDir[2], o[$.light + 3] = 0, o[$.res + 0] = t.width, o[$.res + 1] = t.height, o[$.res + 2] = 0, o[$.res + 3] = 0;
1104
+ let L = m, R = Math.min(L.length, 4);
1105
+ for (let e = 0; e < 4; e++) {
1106
+ let t = $.palette + e * 4, n = L[e];
1107
+ if (n) {
1108
+ let e = f(n);
1109
+ o[t + 0] = e[0], o[t + 1] = e[1], o[t + 2] = e[2];
1110
+ } else o[t + 0] = l[0], o[t + 1] = l[1], o[t + 2] = l[2];
1111
+ o[t + 3] = 0;
1112
+ }
1113
+ let z = i.sources;
1114
+ for (let e = 0; e < 4; e++) {
1115
+ let t = $.satPos + e * 4, n = z[e];
1116
+ n ? (o[t + 0] = n.x * N, o[t + 1] = n.y * N, o[t + 2] = n.radius * N, o[t + 3] = n.opacity) : (o[t + 0] = 0, o[t + 1] = 0, o[t + 2] = 0, o[t + 3] = 0);
1117
+ }
1118
+ let B = r.trailSources(h.satelliteRadius * .7 * S);
1119
+ for (let e = 0; e < 15; e++) {
1120
+ let t = $.trailPos + e * 4, n = B.sources[e];
1121
+ n ? (o[t + 0] = n.x * .5 * N, o[t + 1] = n.y * .5 * N, o[t + 2] = n.radius * N) : (o[t + 0] = 0, o[t + 1] = 0, o[t + 2] = 0), o[t + 3] = 0;
1122
+ }
1123
+ return s[$.ints + 0] = R, s[$.ints + 1] = z.length, s[$.ints + 2] = B.count, s[$.ints + 3] = 0, e;
1124
+ }
1125
+ //#endregion
1126
+ //#region src/components/custom/goo-blob/composables/wgpuSetup.ts
1127
+ var Oe = 64, ke = 8, Ae = 2;
1128
+ function je(e) {
1129
+ let { canvas: t, config: n, pointer: r, satellites: i, resolveFrame: a, shouldContinue: o } = e;
1130
+ return function(e, s, c) {
1131
+ let u = e.createShaderModule({
1132
+ label: "[GooBlob] metaball.wgsl",
1133
+ code: we
1134
+ }), d = e.createBuffer({
1135
+ label: "[GooBlob] uniforms",
1136
+ size: Te,
1137
+ usage: Oe | ke
1138
+ }), f = e.createBindGroupLayout({
1139
+ label: "[GooBlob] bind-group-0",
1140
+ entries: [{
1141
+ binding: 0,
1142
+ visibility: Ae,
1143
+ buffer: { type: "uniform" }
1144
+ }]
1145
+ }), p = e.createRenderPipeline({
1146
+ label: "[GooBlob] pipeline",
1147
+ layout: e.createPipelineLayout({ bindGroupLayouts: [f] }),
1148
+ vertex: {
1149
+ module: u,
1150
+ entryPoint: "vs_main"
1151
+ },
1152
+ fragment: {
1153
+ module: u,
1154
+ entryPoint: "fs_main",
1155
+ targets: [{
1156
+ format: c,
1157
+ blend: {
1158
+ color: {
1159
+ srcFactor: "one",
1160
+ dstFactor: "one-minus-src-alpha",
1161
+ operation: "add"
1162
+ },
1163
+ alpha: {
1164
+ srcFactor: "one",
1165
+ dstFactor: "one-minus-src-alpha",
1166
+ operation: "add"
1167
+ }
1168
+ }
1169
+ }]
1170
+ },
1171
+ primitive: { topology: "triangle-list" }
1172
+ }), m = e.createBindGroup({
1173
+ label: "[GooBlob] bind-group",
1174
+ layout: f,
1175
+ entries: [{
1176
+ binding: 0,
1177
+ resource: { buffer: d }
1178
+ }]
1179
+ }), h = Ee();
1180
+ function g() {
1181
+ let e = l(), r = n.quality === "half" ? .5 : 1, i = t.clientWidth || n.geometry.canvasSize, a = t.clientHeight || n.geometry.canvasSize, o = Math.max(1, Math.round(i * e * r)), s = Math.max(1, Math.round(a * e * r));
1182
+ (t.width !== o || t.height !== s) && (t.width = o, t.height = s);
1183
+ }
1184
+ function _(o) {
1185
+ let c = a(o);
1186
+ if (!c) return;
1187
+ De(h, t, n, r, i, c), e.queue.writeBuffer(d, 0, h.buffer);
1188
+ let l = s.getCurrentTexture().createView(), u = e.createCommandEncoder({ label: "[GooBlob] frame" }), f = u.beginRenderPass({ colorAttachments: [{
1189
+ view: l,
1190
+ clearValue: {
1191
+ r: 0,
1192
+ g: 0,
1193
+ b: 0,
1194
+ a: 0
1195
+ },
1196
+ loadOp: "clear",
1197
+ storeOp: "store"
1198
+ }] });
1199
+ f.setPipeline(p), f.setBindGroup(0, m), f.draw(3, 1, 0, 0), f.end(), e.queue.submit([u.finish()]);
1200
+ }
1201
+ return {
1202
+ frame: _,
1203
+ shouldContinue: o,
1204
+ resize: g,
1205
+ teardown: () => {
1206
+ d.destroy();
1207
+ }
1208
+ };
1209
+ };
1210
+ }
1211
+ //#endregion
625
1212
  //#region src/components/custom/goo-blob/composables/useMetaballRenderer.ts
626
- function be(e) {
627
- let { canvasRef: t, color: r, rimColor: i, paletteStops: a, mood: s, pointer: c, satellites: f, config: p } = e, m = 0, h = 0, g = /* @__PURE__ */ new Map();
1213
+ function Me(e) {
1214
+ let { canvasRef: t, color: n, rimColor: r, paletteStops: i, mood: o, pointer: s, satellites: c, config: u } = e, d = 0, h = 0, g = /* @__PURE__ */ new Map();
628
1215
  function _(e) {
629
1216
  let t = g.get(e);
630
1217
  if (t) return t;
631
- let n = l(u(e));
1218
+ let n = f(p(e));
632
1219
  return g.size > 256 && g.clear(), g.set(e, n), n;
633
1220
  }
634
1221
  let v = null, y = !1, b = null;
635
- function S() {
1222
+ function x() {
636
1223
  b !== null && (clearTimeout(b), b = null);
637
1224
  }
638
- function C(e) {
639
- if (S(), typeof setTimeout > "u") return;
1225
+ function S(e) {
1226
+ if (x(), typeof setTimeout > "u") return;
640
1227
  let t = Math.min(Math.max(e - h, 16), 3e4);
641
1228
  b = setTimeout(() => {
642
1229
  b = null, v?.wake();
643
1230
  }, t);
644
1231
  }
645
- d(t, {
1232
+ m(t, {
646
1233
  pause: () => v?.suspend("off-screen-io"),
647
1234
  resume: () => v?.resume("off-screen-io")
648
1235
  }, {
649
1236
  rootMargin: "200px",
650
1237
  pauseWhenHidden: !1
651
1238
  });
652
- function w(e) {
653
- v = o(e, {
1239
+ function C(e) {
1240
+ let t = d ? (e - d) * 1e3 : 16;
1241
+ d = e;
1242
+ let a = Math.max(0, Math.min(t, 50)), l = v?.reducedMotion ?? !1, f = (l || y ? 0 : u.tempo) * a;
1243
+ return h += f, l || o.update({
1244
+ pointerActive: s.active.value,
1245
+ clicked: s.consumeClick(),
1246
+ idleMs: s.idleMs()
1247
+ }), o.tick(f), l ? s.rest() : s.tick(f), c.tick(h, o.params.value), {
1248
+ params: o.params.value,
1249
+ rgb: _(n.value),
1250
+ simTimeMs: h,
1251
+ timeSec: e,
1252
+ resolveColor: _,
1253
+ rimColor: r.value,
1254
+ paletteStops: i.value
1255
+ };
1256
+ }
1257
+ function T() {
1258
+ if (y) return !1;
1259
+ if (!o.isSettled() || !s.isAtRest() || !c.isQuiescent()) return x(), !0;
1260
+ let e = c.nextEventMs(h), t = o.nextAutoMoodMs(), n = Number.isFinite(t) ? h + t : Infinity;
1261
+ return S(Math.min(e, n)), !1;
1262
+ }
1263
+ function E(e) {
1264
+ let t = l(), n = u.quality === "half" ? .5 : 1, r = e.clientWidth || u.geometry.canvasSize, i = e.clientHeight || u.geometry.canvasSize, a = Math.max(1, Math.round(r * t * n)), o = Math.max(1, Math.round(i * t * n));
1265
+ return (e.width !== a || e.height !== o) && (e.width = a, e.height = o), {
1266
+ w: a,
1267
+ h: o
1268
+ };
1269
+ }
1270
+ function D(e) {
1271
+ v = a(e, {
654
1272
  contextAttrs: {
655
1273
  alpha: !0,
656
1274
  premultipliedAlpha: !0,
657
1275
  antialias: !1,
658
1276
  preserveDrawingBuffer: !1
659
1277
  },
660
- setup: (t) => {
661
- let { prog: o, vs: l, fs: u, vao: d, buf: g, locs: b } = ye(t);
662
- function x() {
663
- let r = n(), i = p.quality === "half" ? .5 : 1, a = e.clientWidth || p.geometry.canvasSize, o = e.clientHeight || p.geometry.canvasSize, s = Math.max(1, Math.round(a * r * i)), c = Math.max(1, Math.round(o * r * i));
664
- (e.width !== s || e.height !== c) && (e.width = s, e.height = c), t.viewport(0, 0, s, c);
665
- }
666
- function w(n) {
667
- let l = m ? (n - m) * 1e3 : 16;
668
- m = n;
669
- let u = Math.max(0, Math.min(l, 50)), g = v?.reducedMotion ?? !1, x = (g || y ? 0 : p.tempo) * u;
670
- h += x, g || s.update({
671
- pointerActive: c.active.value,
672
- clicked: c.consumeClick(),
673
- idleMs: c.idleMs()
674
- }), s.tick(x), g ? c.rest() : c.tick(x), f.tick(h, s.params.value), ve(t, o, d, b, e, p, c, f, {
675
- params: s.params.value,
676
- rgb: _(r.value),
677
- simTimeMs: h,
678
- timeSec: n,
679
- resolveColor: _,
680
- rimColor: i.value,
681
- paletteStops: a.value
682
- });
1278
+ setupWGPU: je({
1279
+ canvas: e,
1280
+ config: u,
1281
+ pointer: s,
1282
+ satellites: c,
1283
+ resolveFrame: C,
1284
+ shouldContinue: T,
1285
+ getReducedMotion: () => v?.reducedMotion ?? !1
1286
+ }),
1287
+ setupGL: (t) => {
1288
+ let { prog: n, vs: r, fs: i, vao: a, buf: o, locs: l } = Ce(t);
1289
+ function d() {
1290
+ let { w: n, h: r } = E(e);
1291
+ t.viewport(0, 0, n, r);
683
1292
  }
684
- function T() {
685
- if (y) return !1;
686
- if (!s.isSettled() || !c.isAtRest() || !f.isQuiescent()) return S(), !0;
687
- let e = f.nextEventMs(h), t = s.nextAutoMoodMs(), n = Number.isFinite(t) ? h + t : Infinity;
688
- return C(Math.min(e, n)), !1;
1293
+ function f(r) {
1294
+ Se(t, n, a, l, e, u, s, c, C(r));
689
1295
  }
690
1296
  return {
691
- frame: w,
1297
+ frame: f,
692
1298
  shouldContinue: T,
693
- resize: x,
1299
+ resize: d,
694
1300
  teardown: () => {
695
- t.deleteProgram(o), t.deleteShader(l), t.deleteShader(u), t.deleteBuffer(g), t.deleteVertexArray(d);
1301
+ t.deleteProgram(n), t.deleteShader(r), t.deleteShader(i), t.deleteBuffer(o), t.deleteVertexArray(a);
696
1302
  }
697
1303
  };
698
1304
  }
699
- }), v.arm();
1305
+ }), v.armAsync();
700
1306
  }
701
- return E(t, (e) => {
702
- e && !v && w(e);
703
- }, { immediate: !0 }), E(r, () => v?.wake()), E(a, () => v?.wake(), { deep: !0 }), x(() => {
704
- S(), v?.dispose(), v = null;
1307
+ return k(t, (e) => {
1308
+ e && !v && D(e);
1309
+ }, { immediate: !0 }), k(n, () => v?.wake()), k(i, () => v?.wake(), { deep: !0 }), w(() => {
1310
+ x(), v?.dispose(), v = null;
705
1311
  }), {
706
1312
  pause: () => {
707
1313
  y = !0, v?.suspend("manual");
@@ -714,7 +1320,7 @@ function be(e) {
714
1320
  }
715
1321
  //#endregion
716
1322
  //#region src/components/custom/goo-blob/GooBlob.vue
717
- var xe = /* @__PURE__ */ e(/* @__PURE__ */ _({
1323
+ var Ne = /* @__PURE__ */ e(/* @__PURE__ */ b({
718
1324
  __name: "GooBlob",
719
1325
  props: {
720
1326
  color: {},
@@ -727,42 +1333,42 @@ var xe = /* @__PURE__ */ e(/* @__PURE__ */ _({
727
1333
  },
728
1334
  emits: ["click", "update:paused"],
729
1335
  setup(e, { expose: n, emit: r }) {
730
- let i = r, a = v(k, null), o = e.config ?? a;
1336
+ let i = r, a = x(M, null), o = e.config ?? a;
731
1337
  if (!o) throw Error("[glass-ui] GooBlob: no blob config. Pass an explicit `config` prop (e.g. BLOB_CONFIG_DEFAULTS) or provide(BLOB_CONFIG_KEY, cfg) from an ancestor. There is no silent defaults synthesis.");
732
- let s = () => e.config ?? a ?? o, c = T("canvasRef"), l = T("wrapperRef"), u = G(), d = K(l), f = ce(o, e.color + e.seed), p = t(), _ = w(e.color), x = w(o.surface.rimColor), C = w([...o.color.paletteStops]);
733
- function D() {
1338
+ let s = () => e.config ?? a ?? o, c = O("canvasRef"), l = O("wrapperRef"), u = ce(), d = le(l), f = fe(o, e.color + e.seed), p = t(), m = D(e.color), h = D(o.surface.rimColor), g = D([...o.color.paletteStops]);
1339
+ function b() {
734
1340
  let t = l.value, n = s();
735
- p.invalidate(), _.value = p.resolve(e.color, t), x.value = p.resolve(n.surface.rimColor, t), C.value = n.color.paletteStops.map((e) => p.resolve(e, t));
1341
+ p.invalidate(), m.value = p.resolve(e.color, t), h.value = p.resolve(n.surface.rimColor, t), g.value = n.color.paletteStops.map((e) => p.resolve(e, t));
736
1342
  }
737
- let O = be({
1343
+ let w = Me({
738
1344
  canvasRef: c,
739
- color: _,
740
- rimColor: x,
741
- paletteStops: C,
1345
+ color: m,
1346
+ rimColor: h,
1347
+ paletteStops: g,
742
1348
  mood: u,
743
1349
  pointer: d,
744
1350
  satellites: f,
745
1351
  config: o
746
1352
  });
747
- E(l, D, { immediate: !0 });
748
- let A = null;
749
- typeof document < "u" && (A = new MutationObserver(D), A.observe(document.documentElement, {
1353
+ k(l, b, { immediate: !0 });
1354
+ let E = null;
1355
+ typeof document < "u" && (E = new MutationObserver(b), E.observe(document.documentElement, {
750
1356
  attributes: !0,
751
1357
  attributeFilter: ["class"]
752
- })), E(m(() => e.color), (t) => {
753
- D(), f.reseed(t + e.seed);
754
- }), E(() => {
1358
+ })), k(_(() => e.color), (t) => {
1359
+ b(), f.reseed(t + e.seed);
1360
+ }), k(() => {
755
1361
  let e = s();
756
1362
  return [e.color.paletteStops.join("|"), e.surface.rimColor];
757
- }, () => D(), { deep: !0 }), E(() => d.active.value, (e) => {
758
- e && O.wake();
759
- }), E(() => e.paused, (e) => e ? O.pause() : O.resume(), { immediate: !0 }), b(() => {
760
- A?.disconnect(), A = null;
1363
+ }, () => b(), { deep: !0 }), k(() => d.active.value, (e) => {
1364
+ e && w.wake();
1365
+ }), k(() => e.paused, (e) => e ? w.pause() : w.resume(), { immediate: !0 }), C(() => {
1366
+ E?.disconnect(), E = null;
761
1367
  });
762
- function j() {
1368
+ function A() {
763
1369
  f.nudge();
764
1370
  }
765
- function M(e) {
1371
+ function j(e) {
766
1372
  u.setMood(e, { source: "manual" });
767
1373
  }
768
1374
  function N() {
@@ -772,19 +1378,19 @@ var xe = /* @__PURE__ */ e(/* @__PURE__ */ _({
772
1378
  N(), i("click");
773
1379
  }
774
1380
  return n({
775
- nudge: j,
776
- setMood: M,
1381
+ nudge: A,
1382
+ setMood: j,
777
1383
  pulse: N,
778
1384
  currentMood: u.currentMood,
779
- pause: O.pause,
780
- resume: O.resume
781
- }), (t, n) => (S(), h("div", {
1385
+ pause: w.pause,
1386
+ resume: w.resume
1387
+ }), (t, n) => (T(), v("div", {
782
1388
  ref_key: "wrapperRef",
783
1389
  ref: l,
784
1390
  class: "goo-blob-wrapper",
785
- style: y({ "--blob-color": e.color }),
1391
+ style: S({ "--blob-color": e.color }),
786
1392
  onClick: P
787
- }, [g("canvas", {
1393
+ }, [y("canvas", {
788
1394
  ref_key: "canvasRef",
789
1395
  ref: c,
790
1396
  class: "goo-blob-canvas",
@@ -792,6 +1398,6 @@ var xe = /* @__PURE__ */ e(/* @__PURE__ */ _({
792
1398
  "data-testid": "goo-blob-canvas"
793
1399
  }, null, 512)], 4));
794
1400
  }
795
- }), [["__scopeId", "data-v-4f2010d6"]]);
1401
+ }), [["__scopeId", "data-v-46b07dc3"]]);
796
1402
  //#endregion
797
- export { O as BLOB_CONFIG_DEFAULTS, k as BLOB_CONFIG_KEY, xe as GooBlob, G as useBlobMood, K as useBlobPointer, ce as useBlobSatellites, be as useMetaballRenderer };
1403
+ export { j as BLOB_CONFIG_DEFAULTS, M as BLOB_CONFIG_KEY, Ne as GooBlob, ce as useBlobMood, le as useBlobPointer, fe as useBlobSatellites, Me as useMetaballRenderer };