@mkbabb/glass-ui 6.0.0 → 8.0.0

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 (1192) hide show
  1. package/README.md +40 -37
  2. package/dist/DialogContent-4hzLiaCQ.js +152 -0
  3. package/dist/DotRing-Blh4Mc3Q.js +27 -0
  4. package/dist/DropdownMenuTrigger-8UZsBtZ8.js +93 -0
  5. package/dist/Surface-i1tWC-Ga.js +48 -0
  6. package/dist/accent-tone-solve-Cw7WkRD9.js +14 -0
  7. package/dist/asElement-CRnm6A2v.js +9 -0
  8. package/dist/aurora.d.ts +1 -1
  9. package/dist/aurora.js +832 -767
  10. package/dist/axes.d.ts +1 -1
  11. package/dist/axes.js +16 -26
  12. package/dist/badge-owUOaaba.js +63 -0
  13. package/dist/badge.d.ts +1 -1
  14. package/dist/badge.js +2 -2
  15. package/dist/blob-config.d.ts +1 -1
  16. package/dist/blob-config.js +1 -1
  17. package/dist/blob.d.ts +1 -1
  18. package/dist/blob.js +442 -395
  19. package/dist/button-yvGPb0Bx.js +110 -0
  20. package/dist/button.d.ts +1 -1
  21. package/dist/button.js +2 -2
  22. package/dist/canvas.d.ts +1 -1
  23. package/dist/canvas.js +2 -2
  24. package/dist/card-3HFFpC7_.js +190 -0
  25. package/dist/card.d.ts +1 -1
  26. package/dist/card.js +2 -2
  27. package/dist/carousel.d.ts +1 -1
  28. package/dist/carousel.js +191 -239
  29. package/dist/checkbox-BnXuGgJ3.js +64 -0
  30. package/dist/checkbox.d.ts +1 -0
  31. package/dist/checkbox.js +2 -0
  32. package/dist/chip.d.ts +1 -1
  33. package/dist/chip.js +101 -53
  34. package/dist/{class-names-5jotTFzp.js → class-names-q-UGOkHH.js} +26 -5
  35. package/dist/collapsible-B7O6m-Ch.js +154 -0
  36. package/dist/collapsible.d.ts +1 -1
  37. package/dist/collapsible.js +1 -1
  38. package/dist/color.d.ts +1 -1
  39. package/dist/color.js +46 -50
  40. package/dist/{procedural-color.wgsl-B9mLK7v0.js → color.wgsl-B0N3mKMb.js} +200 -165
  41. package/dist/command-BuBnYpxF.js +340 -0
  42. package/dist/command.d.ts +1 -1
  43. package/dist/command.js +1 -1
  44. package/dist/component-styles.css +4 -0
  45. package/dist/components/_shared/axes.d.ts +13 -4
  46. package/dist/components/_shared/class-names.d.ts +10 -9
  47. package/dist/components/_shared/control.d.ts +11 -0
  48. package/dist/components/_shared/disclosure/disclosure-context.d.ts +7 -0
  49. package/dist/components/_shared/feedback/DotRing.vue.d.ts +3 -0
  50. package/dist/components/_shared/feedback/dot-ring.css +1 -0
  51. package/dist/components/_shared/feedback/feedback-tone.css +1 -0
  52. package/dist/components/_shared/field/control.d.ts +13 -0
  53. package/dist/components/_shared/focus.d.ts +11 -0
  54. package/dist/components/_shared/index.d.ts +2 -6
  55. package/dist/components/_shared/interaction.d.ts +23 -0
  56. package/dist/components/_shared/menu/menu.css +1 -0
  57. package/dist/components/_shared/menu/rowClass.d.ts +2 -0
  58. package/dist/components/_shared/overlay/content.d.ts +40 -0
  59. package/dist/components/_shared/overlay/index.d.ts +12 -0
  60. package/dist/components/{dock/composables → _shared/overlay}/isTeleportedTarget.d.ts +5 -0
  61. package/dist/components/_shared/overlay/participation.d.ts +66 -0
  62. package/dist/components/_shared/overlay/placement.d.ts +22 -0
  63. package/dist/components/_shared/primitive.d.ts +19 -0
  64. package/dist/components/_shared/selection.d.ts +46 -0
  65. package/dist/components/_shared/surface/resolve.d.ts +17 -0
  66. package/dist/components/_shared/useMotionAxis.d.ts +3 -8
  67. package/dist/components/accordion/Accordion.vue.d.ts +41 -16
  68. package/dist/components/accordion/AccordionContent.vue.d.ts +11 -9
  69. package/dist/components/accordion/AccordionItem.vue.d.ts +12 -9
  70. package/dist/components/accordion/AccordionTrigger.vue.d.ts +8 -11
  71. package/dist/components/accordion/index.d.ts +4 -4
  72. package/dist/components/alert/Alert.vue.d.ts +9 -5
  73. package/dist/components/alert/AlertDescription.vue.d.ts +2 -2
  74. package/dist/components/alert/AlertTitle.vue.d.ts +2 -2
  75. package/dist/components/alert/index.d.ts +17 -8
  76. package/dist/components/aurora/Aurora.vue.d.ts +41 -46
  77. package/dist/components/aurora/composables/atoms-fields.d.ts +13 -2
  78. package/dist/components/aurora/composables/atoms.d.ts +54 -52
  79. package/dist/components/aurora/composables/auroraFallbackGround.d.ts +14 -14
  80. package/dist/components/aurora/composables/auroraImageSource.d.ts +6 -7
  81. package/dist/components/aurora/composables/color.d.ts +18 -24
  82. package/dist/components/aurora/composables/frameLoop.d.ts +17 -14
  83. package/dist/components/aurora/composables/glSetup.d.ts +19 -7
  84. package/dist/components/aurora/composables/runtime.d.ts +58 -62
  85. package/dist/{composables/glass → components/aurora/composables}/textureUpload.d.ts +8 -23
  86. package/dist/components/aurora/composables/uniformBridge.d.ts +11 -33
  87. package/dist/components/aurora/composables/uniformBridgeWGPU.d.ts +23 -24
  88. package/dist/components/aurora/composables/uniformBridgeWGPUImage.d.ts +15 -15
  89. package/dist/components/aurora/composables/useAurora.d.ts +23 -34
  90. package/dist/components/aurora/composables/useCursorInteraction.d.ts +2 -2
  91. package/dist/components/aurora/composables/wgpuSetup.d.ts +14 -12
  92. package/dist/components/aurora/constants/budget.d.ts +5 -54
  93. package/dist/components/aurora/constants/presets.d.ts +49 -60
  94. package/dist/components/aurora/constants/renderMode.d.ts +21 -36
  95. package/dist/components/aurora/constants/shaders/aurora-image.frag.d.ts +0 -2
  96. package/dist/components/aurora/constants/shaders/aurora-image.wgsl.d.ts +1 -3
  97. package/dist/components/aurora/constants/shaders/aurora-mediums.wgsl.d.ts +1 -1
  98. package/dist/components/aurora/constants/shaders/aurora.wgsl.d.ts +1 -1
  99. package/dist/components/aurora/constants/shaders/brush.glsl.d.ts +1 -1
  100. package/dist/components/aurora/constants/shaders/composition.glsl.d.ts +1 -1
  101. package/dist/components/aurora/constants/shaders/flow.glsl.d.ts +1 -1
  102. package/dist/components/aurora/constants/shaders/metal-medium.glsl.d.ts +2 -2
  103. package/dist/components/aurora/constants/shaders/oil-modes.glsl.d.ts +1 -1
  104. package/dist/components/aurora/constants/shaders/vangogh-medium.glsl.d.ts +1 -1
  105. package/dist/components/aurora/index.d.ts +15 -13
  106. package/dist/components/avatar/Avatar.vue.d.ts +72 -11
  107. package/dist/components/avatar/AvatarFallback.vue.d.ts +1 -2
  108. package/dist/components/avatar/AvatarImage.vue.d.ts +7 -8
  109. package/dist/components/avatar/index.d.ts +3 -9
  110. package/dist/components/badge/Badge.vue.d.ts +3 -17
  111. package/dist/components/badge/index.d.ts +33 -9
  112. package/dist/components/blob/Blob.vue.d.ts +8 -15
  113. package/dist/components/blob/composables/blobSimulation.d.ts +80 -0
  114. package/dist/components/blob/composables/buildMetaballProgram.d.ts +1 -1
  115. package/dist/components/blob/composables/resolveBlobSurface.d.ts +6 -0
  116. package/dist/components/blob/composables/satelliteKinematics.d.ts +4 -4
  117. package/dist/components/blob/composables/uniformBridgeWGPU.d.ts +4 -4
  118. package/dist/components/blob/composables/uploadBlobUniforms.d.ts +7 -34
  119. package/dist/components/blob/composables/useBlobMood.d.ts +6 -6
  120. package/dist/components/blob/composables/useBlobPointer.d.ts +11 -15
  121. package/dist/components/blob/composables/useBlobSatellites.d.ts +1 -1
  122. package/dist/components/blob/composables/useMetaballRenderer.d.ts +32 -29
  123. package/dist/components/blob/composables/wgpuSetup.d.ts +10 -8
  124. package/dist/components/blob/config.d.ts +3 -3
  125. package/dist/components/blob/constants.d.ts +1 -3
  126. package/dist/components/blob/index.d.ts +8 -8
  127. package/dist/components/blob/presets.d.ts +3 -3
  128. package/dist/components/blob/shaders/metaball-palette.wgsl.d.ts +1 -1
  129. package/dist/components/blob/shaders/metaball-uniforms.glsl.d.ts +1 -1
  130. package/dist/components/blob/shaders/metaball.wgsl.d.ts +1 -1
  131. package/dist/components/blob/shaders/sdf-body.glsl.d.ts +1 -1
  132. package/dist/components/blob/shaders/watercolor-edges.glsl.d.ts +1 -1
  133. package/dist/components/blob/types.d.ts +28 -60
  134. package/dist/components/button/Button.vue.d.ts +32 -20
  135. package/dist/components/button/index.d.ts +1 -9
  136. package/dist/components/button/styles.css +1 -0
  137. package/dist/components/card/Card.vue.d.ts +24 -162
  138. package/dist/components/card/CardContent.vue.d.ts +2 -2
  139. package/dist/components/card/CardDescription.vue.d.ts +2 -2
  140. package/dist/components/card/CardFooter.vue.d.ts +2 -2
  141. package/dist/components/card/CardHeader.vue.d.ts +8 -40
  142. package/dist/components/card/CardTitle.vue.d.ts +6 -3
  143. package/dist/components/card/index.d.ts +6 -9
  144. package/dist/components/card/scroll.css +1 -0
  145. package/dist/components/card/styles.css +1 -1
  146. package/dist/components/carousel/Carousel.vue.d.ts +14 -19
  147. package/dist/components/carousel/CarouselContent.vue.d.ts +2 -12
  148. package/dist/components/carousel/CarouselItem.vue.d.ts +14 -3
  149. package/dist/components/carousel/CarouselPager.vue.d.ts +2 -2
  150. package/dist/components/carousel/index.d.ts +7 -8
  151. package/dist/components/carousel/projection.d.ts +23 -0
  152. package/dist/components/carousel/styles.css +1 -0
  153. package/dist/components/carousel/types.d.ts +47 -0
  154. package/dist/components/carousel/useCarousel.d.ts +4 -21
  155. package/dist/components/checkbox/Checkbox.vue.d.ts +17 -9
  156. package/dist/components/checkbox/index.d.ts +1 -1
  157. package/dist/components/chip/Chip.vue.d.ts +12 -7
  158. package/dist/components/chip/accent-tone.css +1 -0
  159. package/dist/components/chip/chipVariants.d.ts +20 -43
  160. package/dist/components/chip/index.d.ts +3 -3
  161. package/dist/components/chip/types.d.ts +46 -31
  162. package/dist/components/collapsible/Collapsible.vue.d.ts +22 -9
  163. package/dist/components/collapsible/CollapsibleContent.vue.d.ts +11 -5
  164. package/dist/components/collapsible/CollapsibleTrigger.vue.d.ts +12 -9
  165. package/dist/components/collapsible/index.d.ts +3 -3
  166. package/dist/components/command/Command.vue.d.ts +11 -23
  167. package/dist/components/command/CommandDialog.vue.d.ts +10 -11
  168. package/dist/components/command/CommandEmpty.vue.d.ts +2 -6
  169. package/dist/components/command/CommandGroup.vue.d.ts +2 -7
  170. package/dist/components/command/CommandInput.vue.d.ts +6 -7
  171. package/dist/components/command/CommandItem.vue.d.ts +7 -11
  172. package/dist/components/command/CommandList.vue.d.ts +12 -19
  173. package/dist/components/command/CommandSeparator.vue.d.ts +2 -6
  174. package/dist/components/command/CommandShortcut.vue.d.ts +2 -2
  175. package/dist/components/command/index.d.ts +11 -10
  176. package/dist/components/command/types.d.ts +32 -0
  177. package/dist/components/configurator/Configurator.vue.d.ts +7 -7
  178. package/dist/components/configurator/ConfiguratorLayer.vue.d.ts +14 -18
  179. package/dist/components/configurator/ConfiguratorRow.vue.d.ts +15 -18
  180. package/dist/components/configurator/index.d.ts +6 -6
  181. package/dist/components/configurator/size.d.ts +5 -7
  182. package/dist/components/configurator/styles.css +1 -1
  183. package/dist/components/configurator/useConfiguratorState.d.ts +1 -1
  184. package/dist/components/constellation/Constellation.vue.d.ts +4 -6
  185. package/dist/components/constellation/composables/createConstellationField.d.ts +3 -3
  186. package/dist/components/constellation/composables/useConstellation.d.ts +2 -2
  187. package/dist/components/constellation/constants.d.ts +9 -9
  188. package/dist/components/constellation/constellationField.d.ts +8 -10
  189. package/dist/components/constellation/constellationInteraction.d.ts +9 -13
  190. package/dist/components/constellation/constellationRender.d.ts +1 -3
  191. package/dist/components/constellation/constellationTypes.d.ts +51 -63
  192. package/dist/components/constellation/constellationWell.d.ts +3 -3
  193. package/dist/components/constellation/index.d.ts +8 -6
  194. package/dist/components/dark-mode-toggle/DarkModeToggle.vue.d.ts +12 -0
  195. package/dist/components/dark-mode-toggle/dark-mode-toggle.css +1 -0
  196. package/dist/components/dark-mode-toggle/index.d.ts +2 -0
  197. package/dist/components/data-table/DataTable.vue.d.ts +11 -59
  198. package/dist/components/data-table/composables/useDataTableResponsive.d.ts +1 -1
  199. package/dist/components/data-table/composables/useDataTableRowIdentity.d.ts +5 -23
  200. package/dist/components/data-table/index.d.ts +2 -2
  201. package/dist/components/data-table/types.d.ts +10 -12
  202. package/dist/components/{card/CardAction.vue.d.ts → deck/DeckSlide.vue.d.ts} +12 -8
  203. package/dist/components/deck/DeckStage.vue.d.ts +36 -0
  204. package/dist/components/deck/composables/useDeck.d.ts +35 -10
  205. package/dist/components/deck/composables/useDeckCapture.d.ts +26 -0
  206. package/dist/components/deck/composables/useDeckHashSync.d.ts +8 -0
  207. package/dist/components/deck/composables/useDeckKeyboard.d.ts +16 -8
  208. package/dist/components/deck/composables/useDeckSnap.d.ts +20 -0
  209. package/dist/components/deck/composables/useDeckSwipe.d.ts +13 -0
  210. package/dist/components/deck/composables/useEdgeZones.d.ts +14 -0
  211. package/dist/components/deck/constants.d.ts +0 -3
  212. package/dist/components/deck/index.d.ts +22 -7
  213. package/dist/components/deck/slideContext.d.ts +21 -0
  214. package/dist/components/deck/styles/capture.css +1 -0
  215. package/dist/components/deck/styles/index.css +1 -0
  216. package/dist/components/deck/styles/reveal.css +1 -0
  217. package/dist/components/deck/styles/stage.css +1 -0
  218. package/dist/components/deck/styles/turn.css +1 -0
  219. package/dist/components/deck/types.d.ts +26 -0
  220. package/dist/components/deck/window.d.ts +27 -0
  221. package/dist/components/dialog/Dialog.vue.d.ts +21 -3
  222. package/dist/components/dialog/DialogContent.vue.d.ts +32 -99
  223. package/dist/components/dialog/DialogDescription.vue.d.ts +5 -6
  224. package/dist/components/dialog/DialogFooter.vue.d.ts +2 -2
  225. package/dist/components/dialog/DialogHeader.vue.d.ts +2 -2
  226. package/dist/components/dialog/DialogTitle.vue.d.ts +5 -6
  227. package/dist/components/dialog/ModalOverlay.vue.d.ts +66 -0
  228. package/dist/components/dialog/index.d.ts +7 -8
  229. package/dist/components/dialog/styles.css +1 -0
  230. package/dist/components/dock/DockControl.vue.d.ts +15 -14
  231. package/dist/components/dock/DockCrossfade.vue.d.ts +6 -10
  232. package/dist/components/dock/DockLayer.vue.d.ts +3 -4
  233. package/dist/components/dock/DockLayerGroup.vue.d.ts +18 -18
  234. package/dist/components/dock/DockSeparator.vue.d.ts +25 -20
  235. package/dist/components/dock/GlassDock.vue.d.ts +4 -4
  236. package/dist/components/dock/composables/dockContext.d.ts +33 -19
  237. package/dist/components/dock/composables/dockCrossfadeContext.d.ts +10 -11
  238. package/dist/components/dock/composables/dockMorphMeasure.d.ts +7 -87
  239. package/dist/components/dock/composables/dockSwitcherContext.d.ts +29 -0
  240. package/dist/components/dock/composables/index.d.ts +10 -10
  241. package/dist/components/dock/composables/useDockClickIntegrity.d.ts +1 -1
  242. package/dist/components/dock/composables/useDockHold.d.ts +24 -10
  243. package/dist/components/dock/composables/useDockMorph.d.ts +26 -0
  244. package/dist/components/dock/composables/useDockOverflowFit.d.ts +4 -6
  245. package/dist/components/dock/composables/useDockSearch.d.ts +12 -12
  246. package/dist/components/dock/composables/useDockShellProps.d.ts +44 -65
  247. package/dist/components/dock/composables/useDockSpring.d.ts +7 -11
  248. package/dist/components/dock/composables/useDockState.d.ts +28 -6
  249. package/dist/components/dock/composables/useDockTouchGate.d.ts +14 -23
  250. package/dist/components/dock/constants.d.ts +21 -80
  251. package/dist/components/dock/index.d.ts +15 -20
  252. package/dist/components/dock/styles/adaptive-legibility.css +1 -1
  253. package/dist/components/dock/styles/controls/icon-button.css +1 -1
  254. package/dist/components/dock/styles/controls/tab-button.css +1 -1
  255. package/dist/components/dock/styles/controls/touch-floor.css +1 -1
  256. package/dist/components/dock/styles/controls/triggers.css +1 -1
  257. package/dist/components/dock/styles/controls.css +1 -1
  258. package/dist/components/dock/styles/crossfade.css +1 -1
  259. package/dist/components/dock/styles/cta-seat.css +1 -1
  260. package/dist/components/dock/styles/density.css +1 -1
  261. package/dist/components/dock/styles/dock.css +1 -1
  262. package/dist/components/dock/styles/index.css +1 -1
  263. package/dist/components/dock/styles/layer-group.css +1 -1
  264. package/dist/components/dock/styles/layers.css +1 -1
  265. package/dist/components/dock/styles/morph.css +1 -1
  266. package/dist/components/dock/styles/overflow.css +1 -1
  267. package/dist/components/dock/styles/search.css +1 -1
  268. package/dist/components/dock/styles/shape.css +1 -1
  269. package/dist/components/dock/styles/shell-regions.css +1 -1
  270. package/dist/components/dock/styles/shell.css +1 -1
  271. package/dist/components/easing/EasingCurve.vue.d.ts +51 -0
  272. package/dist/components/easing/EasingPicker.vue.d.ts +16 -32
  273. package/dist/components/easing/constants.d.ts +16 -14
  274. package/dist/components/easing/index.d.ts +5 -3
  275. package/dist/components/easing/{composables/useEasingPicker.d.ts → usePicker.d.ts} +31 -30
  276. package/dist/components/expandable-container/ExpandableContainer.vue.d.ts +1 -25
  277. package/dist/components/expandable-container/index.d.ts +1 -1
  278. package/dist/components/fading-scroll/FadingScroll.vue.d.ts +4 -0
  279. package/dist/components/fading-scroll/composables/useFadingScroll.d.ts +4 -0
  280. package/dist/components/fading-scroll/constants.d.ts +2 -10
  281. package/dist/components/fading-scroll/index.d.ts +2 -2
  282. package/dist/components/fourier-field/FourierField.vue.d.ts +9 -13
  283. package/dist/components/fourier-field/composables/fourierFieldGLSetup.d.ts +5 -5
  284. package/dist/components/fourier-field/composables/fourierFieldWGPUSetup.d.ts +5 -5
  285. package/dist/components/fourier-field/composables/uniformBridgeWGPU.d.ts +2 -4
  286. package/dist/components/fourier-field/composables/useFourierField.d.ts +8 -8
  287. package/dist/components/fourier-field/constants.d.ts +11 -11
  288. package/dist/components/fourier-field/index.d.ts +10 -12
  289. package/dist/components/fourier-field/shaders/fourier-field.glsl.d.ts +2 -2
  290. package/dist/components/fourier-field/shaders/fourier-field.render.wgsl.d.ts +1 -1
  291. package/dist/components/handmark/HandMark.vue.d.ts +6 -6
  292. package/dist/components/handmark/brush.d.ts +1 -1
  293. package/dist/components/handmark/composables/useHandMark.d.ts +8 -8
  294. package/dist/components/handmark/constants.d.ts +10 -10
  295. package/dist/components/handmark/geometry.d.ts +9 -12
  296. package/dist/components/handmark/index.d.ts +17 -16
  297. package/dist/components/handmark/ink.d.ts +1 -1
  298. package/dist/components/handmark/noise.d.ts +3 -3
  299. package/dist/components/handmark/texture.d.ts +2 -2
  300. package/dist/components/handmark/types.d.ts +3 -3
  301. package/dist/components/index.d.ts +30 -35
  302. package/dist/components/infinite-scroll/InfiniteScroll.vue.d.ts +3 -3
  303. package/dist/components/infinite-scroll/composables/index.d.ts +2 -2
  304. package/dist/components/infinite-scroll/composables/types.d.ts +1 -1
  305. package/dist/components/infinite-scroll/composables/useInfiniteScroll.d.ts +1 -1
  306. package/dist/components/infinite-scroll/index.d.ts +2 -2
  307. package/dist/components/input/Input.vue.d.ts +15 -35
  308. package/dist/components/input/index.d.ts +3 -1
  309. package/dist/components/input/types.d.ts +62 -0
  310. package/dist/components/label/Label.vue.d.ts +9 -14
  311. package/dist/components/label/index.d.ts +1 -1
  312. package/dist/components/labeled-field/LabeledField.vue.d.ts +10 -108
  313. package/dist/components/labeled-field/LabeledInput.vue.d.ts +3 -23
  314. package/dist/components/labeled-field/LabeledSlider.vue.d.ts +12 -18
  315. package/dist/components/labeled-field/LabeledSwitch.vue.d.ts +14 -17
  316. package/dist/components/labeled-field/index.d.ts +5 -5
  317. package/dist/components/labeled-field/types.d.ts +41 -0
  318. package/dist/components/menu/DropdownMenu.vue.d.ts +51 -0
  319. package/dist/components/menu/DropdownMenuCheckboxItem.vue.d.ts +33 -0
  320. package/dist/components/menu/DropdownMenuContent.vue.d.ts +42 -0
  321. package/dist/components/{dropdown-menu → menu}/DropdownMenuGroup.vue.d.ts +6 -4
  322. package/dist/components/menu/DropdownMenuItem.vue.d.ts +31 -0
  323. package/dist/components/menu/DropdownMenuLabel.vue.d.ts +19 -0
  324. package/dist/components/{dropdown-menu → menu}/DropdownMenuRadioGroup.vue.d.ts +13 -6
  325. package/dist/components/menu/DropdownMenuRadioItem.vue.d.ts +29 -0
  326. package/dist/components/menu/DropdownMenuSeparator.vue.d.ts +7 -0
  327. package/dist/components/menu/DropdownMenuShortcut.vue.d.ts +16 -0
  328. package/dist/components/{dropdown-menu → menu}/DropdownMenuSub.vue.d.ts +15 -7
  329. package/dist/components/menu/DropdownMenuSubContent.vue.d.ts +41 -0
  330. package/dist/components/menu/DropdownMenuSubTrigger.vue.d.ts +20 -0
  331. package/dist/components/menu/DropdownMenuTrigger.vue.d.ts +27 -0
  332. package/dist/components/menu/context.d.ts +7 -0
  333. package/dist/components/menu/index.d.ts +15 -0
  334. package/dist/components/metric/Metric.vue.d.ts +26 -0
  335. package/dist/components/metric/MetricRow.vue.d.ts +14 -0
  336. package/dist/components/metric/MetricStack.vue.d.ts +16 -0
  337. package/dist/components/metric/coalesce-metric.d.ts +31 -24
  338. package/dist/components/metric/index.d.ts +6 -0
  339. package/dist/components/metric/styles.css +1 -0
  340. package/dist/components/metric/types.d.ts +44 -0
  341. package/dist/components/music-staff/MusicStaff.vue.d.ts +22 -0
  342. package/dist/components/music-staff/glyphs.d.ts +74 -0
  343. package/dist/components/music-staff/index.d.ts +4 -0
  344. package/dist/components/music-staff/midiAdapter.d.ts +22 -0
  345. package/dist/components/music-staff/staffGeometry.d.ts +216 -0
  346. package/dist/components/music-staff/styles.css +1 -0
  347. package/dist/components/number-field/NumberField.vue.d.ts +27 -10
  348. package/dist/components/number-field/NumberFieldInput.vue.d.ts +1 -6
  349. package/dist/components/number-field/NumberFieldStep.vue.d.ts +31 -0
  350. package/dist/components/number-field/context.d.ts +10 -0
  351. package/dist/components/number-field/index.d.ts +3 -5
  352. package/dist/components/pager-dots/PagerDots.vue.d.ts +22 -10
  353. package/dist/components/pager-dots/index.d.ts +3 -4
  354. package/dist/components/pager-dots/worm.d.ts +42 -0
  355. package/dist/components/popover/Popover.vue.d.ts +45 -35
  356. package/dist/components/popover/PopoverContent.vue.d.ts +27 -30
  357. package/dist/components/popover/PopoverTrigger.vue.d.ts +12 -7
  358. package/dist/components/popover/index.d.ts +3 -3
  359. package/dist/components/popover/popoverContext.d.ts +13 -4
  360. package/dist/components/progress/Progress.vue.d.ts +6 -14
  361. package/dist/components/progress/index.d.ts +2 -1
  362. package/dist/components/progress/types.d.ts +29 -0
  363. package/dist/components/radio-group/RadioGroup.vue.d.ts +25 -15
  364. package/dist/components/radio-group/RadioGroupItem.vue.d.ts +10 -6
  365. package/dist/components/radio-group/index.d.ts +4 -2
  366. package/dist/components/scroll-progress-rim/ScrollProgressRim.vue.d.ts +2 -14
  367. package/dist/components/scroll-progress-rim/index.d.ts +2 -3
  368. package/dist/components/scroll-progress-rim/styles.css +1 -1
  369. package/dist/components/scroll-progress-rim/types.d.ts +20 -2
  370. package/dist/components/search/SearchBar.vue.d.ts +4 -4
  371. package/dist/components/search/composables/fuzzySearchIndex.d.ts +1 -1
  372. package/dist/components/search/composables/index.d.ts +5 -5
  373. package/dist/components/search/composables/useFuzzySearch.d.ts +1 -1
  374. package/dist/components/search/index.d.ts +8 -9
  375. package/dist/components/search/searchVariants.d.ts +15 -18
  376. package/dist/components/select/Select.vue.d.ts +25 -14
  377. package/dist/components/select/SelectContent.vue.d.ts +24 -32
  378. package/dist/components/select/SelectGroup.vue.d.ts +7 -11
  379. package/dist/components/select/SelectItem.vue.d.ts +24 -13
  380. package/dist/components/select/SelectLabel.vue.d.ts +7 -11
  381. package/dist/components/select/SelectScrollButton.vue.d.ts +18 -0
  382. package/dist/components/select/SelectSeparator.vue.d.ts +5 -6
  383. package/dist/components/select/SelectTrigger.vue.d.ts +8 -41
  384. package/dist/components/select/SelectValue.vue.d.ts +11 -7
  385. package/dist/components/select/index.d.ts +18 -8
  386. package/dist/components/separator/Separator.vue.d.ts +11 -6
  387. package/dist/components/separator/index.d.ts +1 -1
  388. package/dist/components/sheet/SheetContent.vue.d.ts +60 -0
  389. package/dist/components/sheet/detents/projection.d.ts +42 -0
  390. package/dist/components/sheet/detents/use.d.ts +27 -0
  391. package/dist/components/sheet/index.d.ts +4 -0
  392. package/dist/components/sheet/motion.d.ts +29 -0
  393. package/dist/components/sheet/styles.css +1 -0
  394. package/dist/components/skeleton/Skeleton.vue.d.ts +3 -31
  395. package/dist/components/skeleton/index.d.ts +1 -1
  396. package/dist/components/slider/Slider.vue.d.ts +10 -38
  397. package/dist/components/slider/index.d.ts +2 -46
  398. package/dist/components/slider/styles.css +1 -0
  399. package/dist/components/slider/types.d.ts +34 -0
  400. package/dist/components/sortable-list/SortableItem.vue.d.ts +1 -1
  401. package/dist/components/sortable-list/SortableList.vue.d.ts +2 -3
  402. package/dist/components/sortable-list/context.d.ts +1 -1
  403. package/dist/components/sortable-list/drag.d.ts +52 -0
  404. package/dist/components/sortable-list/ghost.d.ts +40 -0
  405. package/dist/components/sortable-list/index.d.ts +4 -4
  406. package/dist/components/sortable-list/motion.d.ts +75 -0
  407. package/dist/components/sortable-list/resolve.d.ts +56 -0
  408. package/dist/components/sortable-list/{composables/types.d.ts → types.d.ts} +14 -7
  409. package/dist/components/sortable-list/{composables/useSortable.d.ts → useSortable.d.ts} +2 -4
  410. package/dist/components/status-dot/StatusDot.vue.d.ts +14 -10
  411. package/dist/components/status-dot/index.d.ts +3 -1
  412. package/dist/components/status-dot/states.d.ts +4 -0
  413. package/dist/components/surface/Surface.vue.d.ts +33 -24
  414. package/dist/components/surface/index.d.ts +2 -2
  415. package/dist/components/switch/Switch.vue.d.ts +15 -11
  416. package/dist/components/switch/index.d.ts +2 -1
  417. package/dist/components/table/Table.vue.d.ts +1 -1
  418. package/dist/components/table/index.d.ts +8 -8
  419. package/dist/components/tabs/SegmentedTabs.vue.d.ts +28 -24
  420. package/dist/components/tabs/composables/useTabDragMorph.d.ts +7 -7
  421. package/dist/components/tabs/composables/useTabResponsive.d.ts +6 -7
  422. package/dist/components/tabs/composables/useTabRovingFocus.d.ts +31 -23
  423. package/dist/components/tabs/index.d.ts +2 -2
  424. package/dist/components/tabs/styles/drag.css +1 -1
  425. package/dist/components/tabs/styles/segmented.css +1 -1
  426. package/dist/components/textarea/Textarea.vue.d.ts +14 -54
  427. package/dist/components/textarea/index.d.ts +2 -1
  428. package/dist/components/textarea/types.d.ts +45 -0
  429. package/dist/components/timeline/Timeline.vue.d.ts +41 -0
  430. package/dist/components/timeline/geometry.d.ts +53 -79
  431. package/dist/components/timeline/index.d.ts +2 -2
  432. package/dist/components/timeline/types.d.ts +42 -42
  433. package/dist/components/toast/Toast.vue.d.ts +39 -81
  434. package/dist/components/toast/ToastAction.vue.d.ts +5 -5
  435. package/dist/components/toast/ToastClose.vue.d.ts +4 -5
  436. package/dist/components/toast/ToastDescription.vue.d.ts +4 -5
  437. package/dist/components/toast/ToastTitle.vue.d.ts +4 -5
  438. package/dist/components/toast/Toaster.vue.d.ts +1 -1
  439. package/dist/components/toast/index.d.ts +8 -9
  440. package/dist/components/toast/use-toast.d.ts +27 -163
  441. package/dist/components/toggle-group/ToggleGroup.vue.d.ts +25 -14
  442. package/dist/components/toggle-group/ToggleGroupItem.vue.d.ts +14 -12
  443. package/dist/components/toggle-group/context.d.ts +28 -0
  444. package/dist/components/toggle-group/index.d.ts +2 -2
  445. package/dist/components/tooltip/Tooltip.vue.d.ts +20 -7
  446. package/dist/components/tooltip/TooltipContent.vue.d.ts +16 -13
  447. package/dist/components/tooltip/TooltipProvider.vue.d.ts +12 -5
  448. package/dist/components/tooltip/TooltipTrigger.vue.d.ts +12 -5
  449. package/dist/components/tooltip/index.d.ts +4 -4
  450. package/dist/components/typewriter/TypewriterText.vue.d.ts +12 -32
  451. package/dist/components/typewriter/composables/useTypewriter.d.ts +2 -1
  452. package/dist/components/typewriter/index.d.ts +7 -7
  453. package/dist/components/typewriter/types.d.ts +1 -1
  454. package/dist/components/typewriter/utils/timing.d.ts +1 -3
  455. package/dist/components/typewriter/utils/typoStateMachine.d.ts +1 -1
  456. package/dist/composables/color/accent-tone-solve.d.ts +3 -6
  457. package/dist/composables/color/index.d.ts +36 -43
  458. package/dist/composables/color/useAccentTone.d.ts +0 -3
  459. package/dist/composables/color/value.d.ts +21 -0
  460. package/dist/composables/context/createContext.d.ts +2 -2
  461. package/dist/composables/context/index.d.ts +1 -1
  462. package/dist/composables/dark/index.d.ts +5 -5
  463. package/dist/composables/dark/useGlobalDark.d.ts +7 -6
  464. package/dist/composables/dom/index.d.ts +10 -10
  465. package/dist/composables/dom/useBreakpoint.d.ts +2 -2
  466. package/dist/composables/dom/useClipboard.d.ts +28 -52
  467. package/dist/composables/dom/useDocumentVisibility.d.ts +3 -1
  468. package/dist/composables/dom/useDragVelocity.d.ts +7 -0
  469. package/dist/composables/dom/useIdleReady.d.ts +5 -6
  470. package/dist/composables/dom/useTokenColor.d.ts +2 -2
  471. package/dist/composables/dom/useUserInvalidAria.d.ts +2 -3
  472. package/dist/composables/dom/useViewportReady.d.ts +4 -4
  473. package/dist/composables/glass/backdropLuminanceSample.d.ts +23 -24
  474. package/dist/composables/glass/backdropSampleMath.d.ts +10 -0
  475. package/dist/composables/glass/canvas2d/index.d.ts +3 -3
  476. package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +8 -10
  477. package/dist/composables/glass/index.d.ts +5 -7
  478. package/dist/composables/glass/useGlassBackdropLuminance.d.ts +13 -22
  479. package/dist/composables/glass/useSpecularTracking.d.ts +13 -16
  480. package/dist/composables/glass/webgl/backingSize.d.ts +5 -8
  481. package/dist/composables/glass/webgl/compile.d.ts +1 -1
  482. package/dist/composables/glass/webgl/createCanvasLifecycle.d.ts +25 -34
  483. package/dist/composables/glass/webgl/useWebGLCanvas.d.ts +24 -39
  484. package/dist/composables/glass/webgl/visibility.d.ts +4 -5
  485. package/dist/composables/glass/webgpu/useGpuSubstrate.d.ts +20 -23
  486. package/dist/composables/glass/webgpu/useWebGPUCanvas.d.ts +4 -5
  487. package/dist/composables/glass/webgpu/webgpuCanvasTypes.d.ts +19 -19
  488. package/dist/composables/glass/webgpu/webgpuDevice.d.ts +8 -26
  489. package/dist/composables/keyboard/index.d.ts +1 -1
  490. package/dist/composables/motion/core/asElement.d.ts +2 -0
  491. package/dist/composables/motion/core/constants.d.ts +25 -0
  492. package/dist/composables/motion/core/index.d.ts +21 -24
  493. package/dist/composables/motion/{motionTempo.d.ts → core/motionTempo.d.ts} +0 -2
  494. package/dist/composables/motion/core/useReducedMotion.d.ts +5 -0
  495. package/dist/composables/motion/core/useViewTransition.d.ts +44 -0
  496. package/dist/composables/motion/dissolve/dissolveGrammar.d.ts +88 -0
  497. package/dist/composables/motion/engage/engageEnvelopes.d.ts +45 -0
  498. package/dist/composables/motion/engage/engageLadder.d.ts +86 -0
  499. package/dist/composables/motion/index.d.ts +14 -16
  500. package/dist/composables/motion/morph/eyeglass.d.ts +141 -0
  501. package/dist/composables/motion/{useDockCtaReceive.d.ts → morph/useDockCtaReceive.d.ts} +8 -9
  502. package/dist/composables/motion/{useDragMorph.d.ts → morph/useDragMorph.d.ts} +7 -7
  503. package/dist/composables/motion/morph/useElementMorph.d.ts +50 -0
  504. package/dist/composables/motion/morph/useLeadTrail.d.ts +71 -0
  505. package/dist/composables/motion/morph/useSelectionGroup.d.ts +123 -0
  506. package/dist/composables/motion/morph/useSelectionIndicator.d.ts +113 -0
  507. package/dist/composables/motion/{useAnimatedNumberMap.d.ts → number/useAnimatedNumberMap.d.ts} +1 -1
  508. package/dist/composables/motion/{pointerFieldMappings.d.ts → pointer/pointerFieldMappings.d.ts} +8 -11
  509. package/dist/composables/motion/{usePointerVelocityField.d.ts → pointer/usePointerVelocityField.d.ts} +1 -1
  510. package/dist/composables/motion/{useRoutePointer.d.ts → pointer/useRoutePointer.d.ts} +1 -1
  511. package/dist/composables/motion/{useLiquidReveal.d.ts → reveal/useLiquidReveal.d.ts} +11 -10
  512. package/dist/composables/motion/{useStagger.d.ts → reveal/useStagger.d.ts} +3 -3
  513. package/dist/composables/motion/route/routeGrammar.d.ts +60 -0
  514. package/dist/composables/motion/route/useRouteTransition.d.ts +45 -0
  515. package/dist/composables/motion/{useScrollChrome.d.ts → scroll/useScrollChrome.d.ts} +2 -2
  516. package/dist/composables/motion/{useScrollProgress.d.ts → scroll/useScrollProgress.d.ts} +9 -6
  517. package/dist/composables/motion/{useScrollScene.d.ts → scroll/useScrollScene.d.ts} +5 -4
  518. package/dist/composables/motion/spring/springPresets.d.ts +32 -0
  519. package/dist/composables/motion/{springProjection.d.ts → spring/springProjection.d.ts} +6 -4
  520. package/dist/composables/motion/{useLiquidFlex.d.ts → spring/useLiquidFlex.d.ts} +11 -12
  521. package/dist/composables/motion/{useLiquidPress.d.ts → spring/useLiquidPress.d.ts} +5 -5
  522. package/dist/composables/motion/{useSpring.d.ts → spring/useSpring.d.ts} +5 -2
  523. package/dist/composables/motion/{useSpringMount.d.ts → spring/useSpringMount.d.ts} +14 -4
  524. package/dist/composables/motion/{useSpringPress.d.ts → spring/useSpringPress.d.ts} +6 -6
  525. package/dist/composables/reactive/index.d.ts +2 -2
  526. package/dist/composables/sidebar/index.d.ts +10 -10
  527. package/dist/composables/sidebar/types.d.ts +3 -3
  528. package/dist/composables/sidebar/useClickDelegate.d.ts +1 -1
  529. package/dist/composables/sidebar/useLazyLoader.d.ts +1 -1
  530. package/dist/composables/sidebar/useScrollTo.d.ts +1 -1
  531. package/dist/composables/sidebar/useScrollTracker.d.ts +1 -1
  532. package/dist/composables/sidebar/useSidebarState.d.ts +1 -1
  533. package/dist/composables/sidebar/useTreeIndex.d.ts +1 -1
  534. package/dist/{configurator-CSENXXSy.js → configurator-BoeScnDL.js} +12 -12
  535. package/dist/configurator.d.ts +1 -1
  536. package/dist/configurator.js +1 -1
  537. package/dist/{constants-DRZmNIY-.js → constants-Dk96HZHu.js} +3 -3
  538. package/dist/constellation.d.ts +1 -1
  539. package/dist/constellation.js +198 -196
  540. package/dist/content-CWO-XL7o.js +18 -0
  541. package/dist/control-Da4XeT43.js +11 -0
  542. package/dist/control.css_vue_type_style_index_0_src_true_lang-BIdufaDG.js +21 -0
  543. package/dist/{createCanvasLifecycle-hsFkQVdl.js → createCanvasLifecycle-hJHobVTn.js} +84 -82
  544. package/dist/dark-mode-toggle.d.ts +1 -0
  545. package/dist/dark-mode-toggle.js +43 -0
  546. package/dist/dark.d.ts +1 -1
  547. package/dist/dark.js +1 -1
  548. package/dist/data-table-_RrOEdiw.js +493 -0
  549. package/dist/data-table.d.ts +1 -1
  550. package/dist/data-table.js +1 -1
  551. package/dist/deck-Ci6YjB5e.js +453 -0
  552. package/dist/deck.d.ts +1 -1
  553. package/dist/deck.js +2 -94
  554. package/dist/dialog-CbRnTRtg.js +78 -0
  555. package/dist/dialog.d.ts +1 -1
  556. package/dist/dialog.js +3 -2
  557. package/dist/dock.d.ts +1 -1
  558. package/dist/dock.js +654 -1044
  559. package/dist/dom.d.ts +1 -1
  560. package/dist/dom.js +65 -105
  561. package/dist/easing.d.ts +1 -1
  562. package/dist/easing.js +709 -522
  563. package/dist/expandable-container.d.ts +1 -1
  564. package/dist/expandable-container.js +109 -93
  565. package/dist/fading-scroll-CTeW3KNS.js +96 -0
  566. package/dist/fading-scroll.d.ts +1 -1
  567. package/dist/fading-scroll.js +1 -1
  568. package/dist/focus-Duf7EpUu.js +13 -0
  569. package/dist/fourier-field.d.ts +1 -1
  570. package/dist/fourier-field.js +172 -174
  571. package/dist/fourier-math.d.ts +1 -1
  572. package/dist/glass-ui.css +1 -1
  573. package/dist/glass-ui.js +295 -505
  574. package/dist/handmark.d.ts +1 -1
  575. package/dist/handmark.js +4 -4
  576. package/dist/index.d.ts +41 -51
  577. package/dist/infinite-scroll.d.ts +1 -1
  578. package/dist/infinite-scroll.js +59 -43
  579. package/dist/input-Bls2A4Tt.js +54 -0
  580. package/dist/input.d.ts +1 -0
  581. package/dist/input.js +2 -0
  582. package/dist/keyboard.d.ts +1 -1
  583. package/dist/label-BQEZhnK_.js +53 -0
  584. package/dist/label.d.ts +1 -1
  585. package/dist/label.js +1 -1
  586. package/dist/labeled-field.d.ts +1 -1
  587. package/dist/labeled-field.js +232 -239
  588. package/dist/menu-wLu1XGKn.js +501 -0
  589. package/dist/menu.d.ts +1 -0
  590. package/dist/{dropdown-menu.js → menu.js} +2 -2
  591. package/dist/metric.d.ts +1 -0
  592. package/dist/metric.js +139 -0
  593. package/dist/motion-D4uYE0UM.js +61 -0
  594. package/dist/motion-core.d.ts +1 -1
  595. package/dist/motion-core.js +206 -165
  596. package/dist/motion.d.ts +1 -1
  597. package/dist/motion.js +164 -239
  598. package/dist/music-staff.d.ts +1 -0
  599. package/dist/music-staff.js +915 -0
  600. package/dist/number-field-NFAwQ0HK.js +111 -0
  601. package/dist/number-field.d.ts +1 -1
  602. package/dist/number-field.js +2 -2
  603. package/dist/pager-dots.d.ts +1 -1
  604. package/dist/pager-dots.js +362 -2
  605. package/dist/participation-B0hdsIRo.js +42 -0
  606. package/dist/{pointerFieldMappings-CZojkgKj.js → pointerFieldMappings-DP5tla5z.js} +4 -7
  607. package/dist/popover-DBMKTPkL.js +181 -0
  608. package/dist/popover.d.ts +1 -1
  609. package/dist/popover.js +1 -1
  610. package/dist/{presets-CxeNvxuO.js → presets-5myqNv59.js} +1 -1
  611. package/dist/primitive-B2t2jdqM.js +11 -0
  612. package/dist/progress-DF8dWZIX.js +74 -0
  613. package/dist/progress.d.ts +1 -1
  614. package/dist/progress.js +1 -1
  615. package/dist/radio-group-Ci8o-pDx.js +85 -0
  616. package/dist/radio-group.d.ts +1 -0
  617. package/dist/radio-group.js +2 -0
  618. package/dist/reactive.d.ts +1 -1
  619. package/dist/reactive.js +1 -1
  620. package/dist/resolve-DdrNvBby.js +6 -0
  621. package/dist/resolveCanvasColor-6MCrswTB.js +81 -0
  622. package/dist/rowClass-DYOXwxhO.js +10 -0
  623. package/dist/scroll-progress-rim.d.ts +1 -1
  624. package/dist/scroll-progress-rim.js +48 -42
  625. package/dist/{scrollReader-C2oK9Hb8.js → scrollReader-Brf5cQNo.js} +1 -1
  626. package/dist/search.d.ts +1 -1
  627. package/dist/search.js +33 -296
  628. package/dist/select-CDgwGH_P.js +290 -0
  629. package/dist/select.d.ts +1 -1
  630. package/dist/select.js +2 -2
  631. package/dist/selection-DqIULxRG.js +6 -0
  632. package/dist/separator-CN11t406.js +73 -0
  633. package/dist/separator.d.ts +1 -1
  634. package/dist/separator.js +1 -1
  635. package/dist/sheet-BEFvnO_u.js +340 -0
  636. package/dist/sheet.d.ts +1 -0
  637. package/dist/sheet.js +3 -0
  638. package/dist/sidebar.d.ts +1 -1
  639. package/dist/sidebar.js +53 -42
  640. package/dist/slider-gsc8jDIo.js +227 -0
  641. package/dist/slider.d.ts +1 -1
  642. package/dist/slider.js +2 -2
  643. package/dist/sortable-list.d.ts +1 -1
  644. package/dist/sortable-list.js +412 -286
  645. package/dist/springPresets-Cbe0Voqg.js +52 -0
  646. package/dist/status-dot-BpnqOP5N.js +56 -0
  647. package/dist/status-dot.d.ts +1 -1
  648. package/dist/status-dot.js +2 -59
  649. package/dist/styles/accessibility.css +1 -1
  650. package/dist/styles/animations.css +1 -1
  651. package/dist/styles/components.css +1 -1
  652. package/dist/styles/fonts.css +1 -1
  653. package/dist/styles/glass/a11y-fallback.css +1 -1
  654. package/dist/styles/glass/control-bit.css +1 -0
  655. package/dist/styles/glass/control-edge.css +1 -0
  656. package/dist/styles/glass/control-surfaces.css +1 -1
  657. package/dist/styles/glass/deep.css +1 -1
  658. package/dist/styles/glass/defined.css +1 -1
  659. package/dist/styles/glass/dissolve.css +1 -0
  660. package/dist/styles/glass/focus-veil.css +1 -0
  661. package/dist/styles/glass/glass-atom.css +1 -1
  662. package/dist/styles/glass/glass-capsule.css +1 -1
  663. package/dist/styles/glass/glass-chip.css +1 -1
  664. package/dist/styles/glass/grasp.css +1 -0
  665. package/dist/styles/glass/ladder-undershadow.css +1 -1
  666. package/dist/styles/glass/ladder.css +1 -1
  667. package/dist/styles/glass/liquid-enter.css +1 -1
  668. package/dist/styles/glass/liquid-fill.css +1 -1
  669. package/dist/styles/glass/mark.css +1 -0
  670. package/dist/styles/glass/material.css +1 -1
  671. package/dist/styles/glass/overlay-plate.css +1 -0
  672. package/dist/styles/glass/reveal.css +1 -1
  673. package/dist/styles/glass/rim.css +1 -1
  674. package/dist/styles/glass/squircle.css +1 -1
  675. package/dist/styles/glass/surface-axis.css +1 -1
  676. package/dist/styles/glass/surfaces-pager.css +1 -1
  677. package/dist/styles/glass/surfaces.css +1 -1
  678. package/dist/styles/glass/track-flow.css +1 -0
  679. package/dist/styles/glass/value-marks.css +1 -0
  680. package/dist/styles/glass/veil.css +1 -0
  681. package/dist/styles/glass-specular-track.css +1 -1
  682. package/dist/styles/glass.css +1 -1
  683. package/dist/styles/index.css +1 -1
  684. package/dist/styles/paper.css +1 -1
  685. package/dist/styles/scroll-choreography.css +1 -1
  686. package/dist/styles/scroll-chrome.css +1 -1
  687. package/dist/styles/scroll-driven.css +1 -1
  688. package/dist/styles/theme/bridges.css +1 -1
  689. package/dist/styles/theme/dark.css +1 -1
  690. package/dist/styles/theme/literals.css +1 -1
  691. package/dist/styles/theme/radius.css +1 -1
  692. package/dist/styles/theme.css +1 -1
  693. package/dist/styles/tokens/color-radius.css +1 -1
  694. package/dist/styles/tokens/dark-arm-glass.css +1 -1
  695. package/dist/styles/tokens/dark-arm.css +1 -1
  696. package/dist/styles/tokens/glass-deep.css +1 -1
  697. package/dist/styles/tokens/glass-fx.css +1 -1
  698. package/dist/styles/tokens/glass.css +1 -1
  699. package/dist/styles/tokens/light-dark.css +1 -1
  700. package/dist/styles/tokens/manifest.d.ts +14 -0
  701. package/dist/styles/tokens/motion-registers.css +1 -1
  702. package/dist/styles/tokens/offsets.css +1 -1
  703. package/dist/styles/tokens/on-glass-fg.css +1 -1
  704. package/dist/styles/tokens/property-regs-specular.css +1 -1
  705. package/dist/styles/tokens/property-regs.css +1 -1
  706. package/dist/styles/tokens/scale-paper.css +1 -1
  707. package/dist/styles/tokens/scheme-motion.css +1 -1
  708. package/dist/styles/tokens/scheme-spring.css +1 -1
  709. package/dist/styles/tokens/scroll-tokens.css +1 -1
  710. package/dist/styles/tokens/shadow.css +1 -1
  711. package/dist/styles/tokens/sizing-config.css +1 -1
  712. package/dist/styles/tokens/sizing.css +1 -1
  713. package/dist/styles/tokens.css +1 -1
  714. package/dist/styles/tokens.d.ts +5 -10
  715. package/dist/styles/track-well.css +1 -0
  716. package/dist/styles/transitions.css +1 -1
  717. package/dist/styles/typography/scale.css +1 -1
  718. package/dist/styles/typography/semantic.css +1 -1
  719. package/dist/styles/typography/utilities.css +1 -1
  720. package/dist/styles/typography.css +1 -1
  721. package/dist/styles/utilities/a11y-overrides.css +1 -1
  722. package/dist/styles/utilities/base-misc.css +1 -1
  723. package/dist/styles/utilities/base.css +1 -1
  724. package/dist/styles/utilities/btn.css +1 -1
  725. package/dist/styles/utilities/components.css +1 -1
  726. package/dist/styles/utilities/metal.css +1 -1
  727. package/dist/styles/utilities/responsive.css +1 -0
  728. package/dist/styles/utilities.css +1 -1
  729. package/dist/styles/view-transition.css +1 -1
  730. package/dist/styles/viz-reveal.css +1 -1
  731. package/dist/{supportsCssTimeline-Dz-GVxXM.js → supportsCssTimeline-Di9gzleG.js} +6 -5
  732. package/dist/surface.d.ts +1 -1
  733. package/dist/surface.js +2 -3
  734. package/dist/switch-DxZzDrmV.js +49 -0
  735. package/dist/switch.d.ts +1 -1
  736. package/dist/switch.js +1 -1
  737. package/dist/tabs-CYscnq5H.js +268 -0
  738. package/dist/tabs.d.ts +1 -1
  739. package/dist/tabs.js +2 -339
  740. package/dist/textarea.d.ts +1 -0
  741. package/dist/textarea.js +58 -0
  742. package/dist/timeline.d.ts +1 -1
  743. package/dist/timeline.js +241 -532
  744. package/dist/toast-9c6Kp58L.js +269 -0
  745. package/dist/toast.d.ts +1 -1
  746. package/dist/toast.js +1 -1
  747. package/dist/toggle-group-11aPGMfE.js +125 -0
  748. package/dist/toggle-group.d.ts +1 -1
  749. package/dist/toggle-group.js +1 -1
  750. package/dist/tokens.d.ts +1 -1
  751. package/dist/tokens.js +98 -5
  752. package/dist/tooltip-DqKMZ4dn.js +142 -0
  753. package/dist/tooltip.d.ts +1 -1
  754. package/dist/tooltip.js +1 -1
  755. package/dist/typewriter.d.ts +1 -1
  756. package/dist/typewriter.js +215 -208
  757. package/dist/useAccentTone-DFAh1osB.js +47 -0
  758. package/dist/useClipboard-D36OTaeT.js +53 -0
  759. package/dist/useDockCtaReceive-BSSgD6mu.js +146 -0
  760. package/dist/useDragMorph-B363RUbF.js +293 -0
  761. package/dist/{useDragVelocity-CbMZEBaC.js → useDragVelocity-C5BkKiNf.js} +28 -24
  762. package/dist/useIntersectionPause-CzKR3EyP.js +57 -0
  763. package/dist/useLeadTrail-DQWdpFN1.js +83 -0
  764. package/dist/{useLiquidFlex-Ch4exNvN.js → useLiquidFlex-TJZmRqyY.js} +1 -1
  765. package/dist/useLiquidPress-h1scrPtP.js +101 -0
  766. package/dist/useMotionAxis-D-rtIhca.js +19 -0
  767. package/dist/usePointerVelocityField-DsIf7yyq.js +121 -0
  768. package/dist/useReducedMotion-vCXA_vyM.js +27 -0
  769. package/dist/{useTouchGate-Cj5fOkTv.js → useResolveTokenColor-CspQv_ik.js} +25 -2
  770. package/dist/useRoutePointer-B8itM7Ka.js +53 -0
  771. package/dist/useScrollChrome-BJbWV32F.js +162 -0
  772. package/dist/useScrollProgress-Bq-KPaKM.js +41 -0
  773. package/dist/useScrollTrigger-Abe5dEeO.js +86 -0
  774. package/dist/useSelectionGroup-zsEwitF6.js +291 -0
  775. package/dist/{useSpring-BBSKFx_0.js → useSpring-9u2_shxV.js} +2 -2
  776. package/dist/useSpringMount-OA-0mTNv.js +73 -0
  777. package/dist/vReveal-B5WYGofz.js +44 -0
  778. package/dist/vSpecular-DMJo8pfD.js +68 -0
  779. package/dist/value-DMhh2R94.js +33 -0
  780. package/dist/{valueDomain-B8L04u3U.js → valueDomain-DXRzfc8l.js} +1 -1
  781. package/package.json +183 -291
  782. package/dist/DropdownMenuTrigger-BzQtd9lt.js +0 -65
  783. package/dist/FadingScroll-C1ZWEjmG.js +0 -83
  784. package/dist/Input-B4WQ_CMc.js +0 -57
  785. package/dist/PagerDots-BoTQ7g5l.js +0 -307
  786. package/dist/Surface-BWRfwyn8.js +0 -59
  787. package/dist/accent-tone-solve-Dii9ePhK.js +0 -20
  788. package/dist/animated-digit.d.ts +0 -1
  789. package/dist/animated-digit.js +0 -44
  790. package/dist/badge-C0C-rTBu.js +0 -80
  791. package/dist/button-32mg6xA8.js +0 -194
  792. package/dist/card-16xmzIr3.js +0 -303
  793. package/dist/coalesce-metric-znAcLzkB.js +0 -13
  794. package/dist/collapsible-bPzS1aQk.js +0 -63
  795. package/dist/color-swatch.d.ts +0 -1
  796. package/dist/color-swatch.js +0 -48
  797. package/dist/command-BjPjUU9z.js +0 -307
  798. package/dist/completion-seal.d.ts +0 -1
  799. package/dist/completion-seal.js +0 -132
  800. package/dist/components/_shared/ModalOverlay.vue.d.ts +0 -43
  801. package/dist/components/_shared/feedback-tone.css +0 -1
  802. package/dist/components/_shared/field-surfaces.css +0 -1
  803. package/dist/components/_shared/menu.css +0 -1
  804. package/dist/components/_shared/menuItemVariants.d.ts +0 -51
  805. package/dist/components/_shared/useControlSize.d.ts +0 -46
  806. package/dist/components/_shared/useStalePropWarning.d.ts +0 -27
  807. package/dist/components/_shared/useSurfaceAxis.d.ts +0 -64
  808. package/dist/components/animated-digit/AnimatedDigit.vue.d.ts +0 -56
  809. package/dist/components/animated-digit/index.d.ts +0 -2
  810. package/dist/components/card/ScrollCard.vue.d.ts +0 -33
  811. package/dist/components/card/ScrollCardHeader.vue.d.ts +0 -17
  812. package/dist/components/card/card-scroll.css +0 -1
  813. package/dist/components/carousel/CarouselNext.vue.d.ts +0 -21
  814. package/dist/components/carousel/CarouselPrevious.vue.d.ts +0 -21
  815. package/dist/components/carousel/interface.d.ts +0 -20
  816. package/dist/components/color-swatch/ColorSwatch.vue.d.ts +0 -22
  817. package/dist/components/color-swatch/index.d.ts +0 -1
  818. package/dist/components/combobox/Combobox.vue.d.ts +0 -32
  819. package/dist/components/combobox/ComboboxAnchor.vue.d.ts +0 -18
  820. package/dist/components/combobox/ComboboxEmpty.vue.d.ts +0 -18
  821. package/dist/components/combobox/ComboboxGroup.vue.d.ts +0 -19
  822. package/dist/components/combobox/ComboboxInput.vue.d.ts +0 -23
  823. package/dist/components/combobox/ComboboxItem.vue.d.ts +0 -22
  824. package/dist/components/combobox/ComboboxItemIndicator.vue.d.ts +0 -18
  825. package/dist/components/combobox/ComboboxList.vue.d.ts +0 -32
  826. package/dist/components/combobox/index.d.ts +0 -9
  827. package/dist/components/completion-seal/CompletionSeal.vue.d.ts +0 -28
  828. package/dist/components/completion-seal/composables/useCompletionSeal.d.ts +0 -37
  829. package/dist/components/completion-seal/constants.d.ts +0 -66
  830. package/dist/components/completion-seal/index.d.ts +0 -4
  831. package/dist/components/completion-seal/styles.css +0 -1
  832. package/dist/components/controls/DarkModeToggle.vue.d.ts +0 -32
  833. package/dist/components/controls/dark-mode-toggle.css +0 -1
  834. package/dist/components/controls/index.d.ts +0 -1
  835. package/dist/components/data-table/DataTablePagination.vue.d.ts +0 -13
  836. package/dist/components/deck/DeckPager.vue.d.ts +0 -29
  837. package/dist/components/dialog/DialogClose.vue.d.ts +0 -14
  838. package/dist/components/dialog/DialogTrigger.vue.d.ts +0 -14
  839. package/dist/components/dialog/placement.css +0 -1
  840. package/dist/components/dock/DockSection.vue.d.ts +0 -69
  841. package/dist/components/dock/DockStack.vue.d.ts +0 -73
  842. package/dist/components/dock/composables/dockMorphContext.d.ts +0 -91
  843. package/dist/components/dock/composables/useDockFisheye.d.ts +0 -24
  844. package/dist/components/dock/composables/useDockPopover.d.ts +0 -70
  845. package/dist/components/dock/styles/fisheye.css +0 -1
  846. package/dist/components/dock/styles/popover.css +0 -1
  847. package/dist/components/dock/styles/section.css +0 -1
  848. package/dist/components/drawer/Drawer.vue.d.ts +0 -85
  849. package/dist/components/drawer/DrawerContent.vue.d.ts +0 -40
  850. package/dist/components/drawer/DrawerDescription.vue.d.ts +0 -18
  851. package/dist/components/drawer/DrawerFooter.vue.d.ts +0 -17
  852. package/dist/components/drawer/DrawerHeader.vue.d.ts +0 -17
  853. package/dist/components/drawer/DrawerOverlay.vue.d.ts +0 -8
  854. package/dist/components/drawer/DrawerTitle.vue.d.ts +0 -18
  855. package/dist/components/drawer/composables/drawerSnapContext.d.ts +0 -28
  856. package/dist/components/drawer/composables/useDrawerSnap.d.ts +0 -24
  857. package/dist/components/drawer/constants.d.ts +0 -50
  858. package/dist/components/drawer/index.d.ts +0 -33
  859. package/dist/components/drawer/styles.css +0 -1
  860. package/dist/components/dropdown-menu/DropdownMenu.vue.d.ts +0 -24
  861. package/dist/components/dropdown-menu/DropdownMenuCheckboxItem.vue.d.ts +0 -24
  862. package/dist/components/dropdown-menu/DropdownMenuContent.vue.d.ts +0 -36
  863. package/dist/components/dropdown-menu/DropdownMenuItem.vue.d.ts +0 -19
  864. package/dist/components/dropdown-menu/DropdownMenuLabel.vue.d.ts +0 -19
  865. package/dist/components/dropdown-menu/DropdownMenuRadioItem.vue.d.ts +0 -22
  866. package/dist/components/dropdown-menu/DropdownMenuSeparator.vue.d.ts +0 -8
  867. package/dist/components/dropdown-menu/DropdownMenuShortcut.vue.d.ts +0 -17
  868. package/dist/components/dropdown-menu/DropdownMenuSubContent.vue.d.ts +0 -34
  869. package/dist/components/dropdown-menu/DropdownMenuSubTrigger.vue.d.ts +0 -18
  870. package/dist/components/dropdown-menu/DropdownMenuTrigger.vue.d.ts +0 -20
  871. package/dist/components/dropdown-menu/index.d.ts +0 -15
  872. package/dist/components/dropdown-menu/useMenuTrigger.d.ts +0 -14
  873. package/dist/components/easing/EasingConfigurator.vue.d.ts +0 -33
  874. package/dist/components/focus-scope/FocusScope.vue.d.ts +0 -20
  875. package/dist/components/focus-scope/index.d.ts +0 -2
  876. package/dist/components/fourier-field/presets.d.ts +0 -54
  877. package/dist/components/goo-filter/GooFilter.vue.d.ts +0 -22
  878. package/dist/components/goo-filter/index.d.ts +0 -1
  879. package/dist/components/header-ribbon/HeaderRibbon.vue.d.ts +0 -31
  880. package/dist/components/header-ribbon/index.d.ts +0 -2
  881. package/dist/components/header-ribbon/types.d.ts +0 -7
  882. package/dist/components/icon-chip/IconChip.vue.d.ts +0 -4
  883. package/dist/components/icon-chip/index.d.ts +0 -2
  884. package/dist/components/icon-chip/styles.css +0 -1
  885. package/dist/components/icon-chip/types.d.ts +0 -92
  886. package/dist/components/icon-tooltip/IconTooltip.vue.d.ts +0 -16
  887. package/dist/components/icon-tooltip/index.d.ts +0 -1
  888. package/dist/components/instrument-chassis/ChassisDivider.vue.d.ts +0 -30
  889. package/dist/components/instrument-chassis/InstrumentChassis.vue.d.ts +0 -97
  890. package/dist/components/instrument-chassis/index.d.ts +0 -3
  891. package/dist/components/instrument-chassis/styles.css +0 -1
  892. package/dist/components/labeled-field/LabeledSelect.vue.d.ts +0 -26
  893. package/dist/components/liquid-grid/LiquidGrid.vue.d.ts +0 -46
  894. package/dist/components/liquid-grid/composables/liquidGrid.d.ts +0 -113
  895. package/dist/components/liquid-grid/composables/liquidGridGLSetup.d.ts +0 -18
  896. package/dist/components/liquid-grid/composables/liquidGridWGPUSetup.d.ts +0 -21
  897. package/dist/components/liquid-grid/composables/uniformBridgeWGPU.d.ts +0 -16
  898. package/dist/components/liquid-grid/composables/useLiquidGrid.d.ts +0 -32
  899. package/dist/components/liquid-grid/constants.d.ts +0 -93
  900. package/dist/components/liquid-grid/index.d.ts +0 -5
  901. package/dist/components/liquid-grid/shaders/liquid-grid.glsl.d.ts +0 -4
  902. package/dist/components/liquid-grid/shaders/liquid-grid.wgsl.d.ts +0 -1
  903. package/dist/components/metric-badge/MetricBadge.vue.d.ts +0 -40
  904. package/dist/components/metric-badge/index.d.ts +0 -2
  905. package/dist/components/metric-cell/MetricCell.vue.d.ts +0 -108
  906. package/dist/components/metric-cell/index.d.ts +0 -2
  907. package/dist/components/metric-stack/MetricRow.vue.d.ts +0 -120
  908. package/dist/components/metric-stack/MetricStack.vue.d.ts +0 -92
  909. package/dist/components/metric-stack/index.d.ts +0 -4
  910. package/dist/components/notification/Notification.vue.d.ts +0 -16
  911. package/dist/components/notification/index.d.ts +0 -1
  912. package/dist/components/number-field/NumberFieldContent.vue.d.ts +0 -17
  913. package/dist/components/number-field/NumberFieldDecrement.vue.d.ts +0 -18
  914. package/dist/components/number-field/NumberFieldIncrement.vue.d.ts +0 -18
  915. package/dist/components/pager-dots/composables/usePagerWorm.d.ts +0 -26
  916. package/dist/components/pager-dots/constants.d.ts +0 -15
  917. package/dist/components/pager-dots/pagerWindow.d.ts +0 -9
  918. package/dist/components/paper-backdrop/PaperBackdrop.vue.d.ts +0 -21
  919. package/dist/components/paper-backdrop/index.d.ts +0 -2
  920. package/dist/components/progress/ProgressDefault.vue.d.ts +0 -9
  921. package/dist/components/progress/ProgressGradient.vue.d.ts +0 -26
  922. package/dist/components/progress/ProgressLiquid.vue.d.ts +0 -22
  923. package/dist/components/progress/valueMarks.css +0 -1
  924. package/dist/components/pulse/Pulse.vue.d.ts +0 -53
  925. package/dist/components/pulse/index.d.ts +0 -1
  926. package/dist/components/search/FuzzySearch.vue.d.ts +0 -24
  927. package/dist/components/select/SelectScrollDownButton.vue.d.ts +0 -18
  928. package/dist/components/select/SelectScrollUpButton.vue.d.ts +0 -18
  929. package/dist/components/sortable-list/composables/dragController.d.ts +0 -29
  930. package/dist/components/sortable-list/composables/dropResolver.d.ts +0 -34
  931. package/dist/components/sortable-list/composables/ghostRenderer.d.ts +0 -44
  932. package/dist/components/sortable-list/composables/index.d.ts +0 -1
  933. package/dist/components/sortable-list/composables/touchGate.d.ts +0 -26
  934. package/dist/components/sortable-list/composables/transitionTiming.d.ts +0 -50
  935. package/dist/components/spa-view/SpaView.vue.d.ts +0 -50
  936. package/dist/components/spa-view/index.d.ts +0 -2
  937. package/dist/components/split-chars/SplitChars.vue.d.ts +0 -53
  938. package/dist/components/split-chars/index.d.ts +0 -1
  939. package/dist/components/tabs/composables/useEyeglassLive.d.ts +0 -17
  940. package/dist/components/tabs/constants.d.ts +0 -1
  941. package/dist/components/tags-input/TagsInput.vue.d.ts +0 -28
  942. package/dist/components/tags-input/TagsInputInput.vue.d.ts +0 -8
  943. package/dist/components/tags-input/TagsInputItem.vue.d.ts +0 -18
  944. package/dist/components/tags-input/TagsInputItemDelete.vue.d.ts +0 -18
  945. package/dist/components/tags-input/TagsInputItemText.vue.d.ts +0 -8
  946. package/dist/components/tags-input/index.d.ts +0 -5
  947. package/dist/components/timeline/ContinuousMarkers.vue.d.ts +0 -49
  948. package/dist/components/timeline/ContinuousRail.vue.d.ts +0 -26
  949. package/dist/components/timeline/ContinuousTimeline.vue.d.ts +0 -118
  950. package/dist/components/timeline/GlassTimeline.vue.d.ts +0 -114
  951. package/dist/components/timeline/ScrubberTimeline.vue.d.ts +0 -45
  952. package/dist/components/timeline/SegmentedTimeline.vue.d.ts +0 -46
  953. package/dist/components/toggle/Toggle.vue.d.ts +0 -29
  954. package/dist/components/toggle/index.d.ts +0 -26
  955. package/dist/components/toggle-group/toggleGroupContext.d.ts +0 -20
  956. package/dist/components/typewriter/composables/index.d.ts +0 -1
  957. package/dist/components/watercolor-dot/WatercolorDot.vue.d.ts +0 -74
  958. package/dist/components/watercolor-dot/index.d.ts +0 -3
  959. package/dist/components/watercolor-dot/prng.d.ts +0 -5
  960. package/dist/components/watercolor-dot/useWatercolorBlob.d.ts +0 -31
  961. package/dist/composables/glass/ambientHueHistogram.d.ts +0 -27
  962. package/dist/composables/glass/useSpecularPointer.d.ts +0 -52
  963. package/dist/composables/glass/wave/index.d.ts +0 -4
  964. package/dist/composables/glass/wave/waveField.d.ts +0 -136
  965. package/dist/composables/glass/wave/waveField.glsl.d.ts +0 -1
  966. package/dist/composables/glass/wave/waveField.wgsl.d.ts +0 -1
  967. package/dist/composables/glass/webgl/shaders/glass-refract.glsl.d.ts +0 -36
  968. package/dist/composables/glass/webgpu/index.d.ts +0 -3
  969. package/dist/composables/index.d.ts +0 -9
  970. package/dist/composables/motion/bloomUpField.d.ts +0 -29
  971. package/dist/composables/motion/constants.d.ts +0 -19
  972. package/dist/composables/motion/curves.d.ts +0 -48
  973. package/dist/composables/motion/gooBarbellGeometry.d.ts +0 -79
  974. package/dist/composables/motion/morphSignatures.d.ts +0 -88
  975. package/dist/composables/motion/springPresets.d.ts +0 -45
  976. package/dist/composables/motion/suite.d.ts +0 -2
  977. package/dist/composables/motion/useBloomUp.d.ts +0 -97
  978. package/dist/composables/motion/useCharStagger.d.ts +0 -44
  979. package/dist/composables/motion/useCountup.d.ts +0 -30
  980. package/dist/composables/motion/useElementMorph.d.ts +0 -107
  981. package/dist/composables/motion/useGooMorph.d.ts +0 -72
  982. package/dist/composables/motion/useLeadTrail.d.ts +0 -69
  983. package/dist/composables/motion/useNumericTransition.d.ts +0 -56
  984. package/dist/composables/motion/usePrioritizedTask.d.ts +0 -41
  985. package/dist/composables/motion/useScrollPin.d.ts +0 -49
  986. package/dist/composables/motion/useSelectionGroup.d.ts +0 -83
  987. package/dist/composables/motion/useSelectionIndicator.d.ts +0 -90
  988. package/dist/composables/motion/useStaggerReveal.d.ts +0 -52
  989. package/dist/composables/motion/useTextHighlight.d.ts +0 -41
  990. package/dist/composables/motion/useViewTransition.d.ts +0 -81
  991. package/dist/composables/virtual/index.d.ts +0 -4
  992. package/dist/composables/virtual/useVirtualSectionWindow.d.ts +0 -72
  993. package/dist/composables/virtual/useWindowedStore.d.ts +0 -43
  994. package/dist/composables/virtual/virtualSectionLayout.d.ts +0 -103
  995. package/dist/controls.d.ts +0 -1
  996. package/dist/controls.js +0 -83
  997. package/dist/data-table-DveMdgBM.js +0 -629
  998. package/dist/dialog-BzQxKrUr.js +0 -281
  999. package/dist/dockContext-4S9qP0Z4.js +0 -13
  1000. package/dist/drawer.d.ts +0 -1
  1001. package/dist/drawer.js +0 -443
  1002. package/dist/dropdown-menu-DORIA52F.js +0 -378
  1003. package/dist/dropdown-menu.d.ts +0 -1
  1004. package/dist/flow.glsl-DEGFGE11.js +0 -4
  1005. package/dist/focus-scope-DUTKveod.js +0 -23
  1006. package/dist/focus-scope.d.ts +0 -1
  1007. package/dist/focus-scope.js +0 -2
  1008. package/dist/fonts/README.md +0 -124
  1009. package/dist/fonts/fira-code/OFL.txt +0 -93
  1010. package/dist/fonts/plus-jakarta-sans/OFL.txt +0 -93
  1011. package/dist/forms.d.ts +0 -5
  1012. package/dist/forms.js +0 -298
  1013. package/dist/header-ribbon.d.ts +0 -1
  1014. package/dist/header-ribbon.js +0 -74
  1015. package/dist/icon-chip-BWuxz1-u.js +0 -57
  1016. package/dist/icon-chip.d.ts +0 -1
  1017. package/dist/icon-chip.js +0 -2
  1018. package/dist/icon-tooltip-CO6rw0dZ.js +0 -25
  1019. package/dist/icon-tooltip.d.ts +0 -1
  1020. package/dist/icon-tooltip.js +0 -2
  1021. package/dist/instrument-chassis-Cvstg4_U.js +0 -65
  1022. package/dist/instrument-chassis.d.ts +0 -1
  1023. package/dist/instrument-chassis.js +0 -2
  1024. package/dist/label-Cj38tJKG.js +0 -36
  1025. package/dist/liquid-grid.d.ts +0 -1
  1026. package/dist/liquid-grid.js +0 -1075
  1027. package/dist/menuItemVariants-D6AOzzND.js +0 -36
  1028. package/dist/metric-badge.d.ts +0 -1
  1029. package/dist/metric-badge.js +0 -79
  1030. package/dist/metric-cell.d.ts +0 -1
  1031. package/dist/metric-cell.js +0 -61
  1032. package/dist/metric-stack.d.ts +0 -1
  1033. package/dist/metric-stack.js +0 -113
  1034. package/dist/motion-curves.d.ts +0 -1
  1035. package/dist/motion-curves.js +0 -146
  1036. package/dist/notification-DVs7ODMg.js +0 -52
  1037. package/dist/notification.d.ts +0 -1
  1038. package/dist/notification.js +0 -2
  1039. package/dist/number-field-PvkGedHk.js +0 -140
  1040. package/dist/paper-backdrop.d.ts +0 -1
  1041. package/dist/paper-backdrop.js +0 -31
  1042. package/dist/popover-WMgyRSyN.js +0 -256
  1043. package/dist/progress-C-HHuFZy.js +0 -192
  1044. package/dist/pulse.d.ts +0 -1
  1045. package/dist/pulse.js +0 -56
  1046. package/dist/resolveCanvasColor-yarP5bdk.js +0 -93
  1047. package/dist/select-BIv4R3Ir.js +0 -326
  1048. package/dist/separator-CBU94kTM.js +0 -62
  1049. package/dist/slider-Cc_qw6GA.js +0 -166
  1050. package/dist/spa-view.d.ts +0 -1
  1051. package/dist/spa-view.js +0 -46
  1052. package/dist/springPresets-h5VKJQqG.js +0 -58
  1053. package/dist/styles/draw-in.css +0 -1
  1054. package/dist/styles/glass/accent-tone.css +0 -1
  1055. package/dist/styles/glass/grain-overlay.css +0 -1
  1056. package/dist/styles/glass/material-roles.css +0 -1
  1057. package/dist/styles/glass/progress-rail.css +0 -1
  1058. package/dist/styles/glass-refract.css +0 -1
  1059. package/dist/styles/tokens.ts +0 -58
  1060. package/dist/switch-Boj4Ucn5.js +0 -49
  1061. package/dist/toast-kAbchIN2.js +0 -281
  1062. package/dist/toggle-group-BPchFsns.js +0 -178
  1063. package/dist/tooltip-BmOQJzh0.js +0 -111
  1064. package/dist/useAccentTone-C-p6WdPe.js +0 -23
  1065. package/dist/useAnimatedNumber-BRVVaFFc.js +0 -40
  1066. package/dist/useControlSize-CFfOk8I-.js +0 -9
  1067. package/dist/useDockCtaReceive-CK-RLASa.js +0 -257
  1068. package/dist/useDocumentVisibility-YeBDGqtI.js +0 -17
  1069. package/dist/useDragMorph-7ygF9O0m.js +0 -93
  1070. package/dist/useGooMorph-D1F6QJlH.js +0 -349
  1071. package/dist/useIntersectionPause-B-dbxl9w.js +0 -39
  1072. package/dist/useLeadTrail-BYu8dq4G.js +0 -74
  1073. package/dist/useLiquidPress-BYcwwGQV.js +0 -68
  1074. package/dist/useMotionAxis-DCCXMC-i.js +0 -20
  1075. package/dist/usePointerVelocityField-BdvnO0py.js +0 -122
  1076. package/dist/useRAFLoop-BtwQ9DyY.js +0 -109
  1077. package/dist/useResolveTokenColor-C07e-qaT.js +0 -24
  1078. package/dist/useRoutePointer-BDNMFN73.js +0 -52
  1079. package/dist/useScrollProgress-DZI4nf4v.js +0 -35
  1080. package/dist/useSelectionGroup-LDzBdR1Y.js +0 -215
  1081. package/dist/useSpringMount-C6-DBx4e.js +0 -71
  1082. package/dist/useSpringPress-BypV_KO-.js +0 -36
  1083. package/dist/useSurfaceAxis-DudiZVOR.js +0 -10
  1084. package/dist/useTabRovingFocus-DkLYwo4L.js +0 -143
  1085. package/dist/useUserInvalidAria-Cr4nR3tu.js +0 -25
  1086. package/dist/vReveal-DZfWl9XI.js +0 -10
  1087. package/dist/vSpecular-CQFmLNzA.js +0 -68
  1088. package/dist/watercolor-dot.d.ts +0 -1
  1089. package/dist/watercolor-dot.js +0 -175
  1090. package/src/fonts/README.md +0 -124
  1091. package/src/fonts/fira-code/OFL.txt +0 -93
  1092. package/src/fonts/fira-code/fira-code-latin-ext.woff2 +0 -0
  1093. package/src/fonts/fira-code/fira-code-latin.woff2 +0 -0
  1094. package/src/fonts/plus-jakarta-sans/OFL.txt +0 -93
  1095. package/src/fonts/plus-jakarta-sans/plus-jakarta-sans-latin-ext.woff2 +0 -0
  1096. package/src/fonts/plus-jakarta-sans/plus-jakarta-sans-latin.woff2 +0 -0
  1097. package/src/styles/accessibility.css +0 -48
  1098. package/src/styles/animations.css +0 -468
  1099. package/src/styles/draw-in.css +0 -89
  1100. package/src/styles/fonts.css +0 -143
  1101. package/src/styles/glass/a11y-fallback.css +0 -188
  1102. package/src/styles/glass/accent-tone.css +0 -66
  1103. package/src/styles/glass/control-surfaces.css +0 -171
  1104. package/src/styles/glass/deep.css +0 -114
  1105. package/src/styles/glass/defined.css +0 -88
  1106. package/src/styles/glass/glass-atom.css +0 -247
  1107. package/src/styles/glass/glass-capsule.css +0 -175
  1108. package/src/styles/glass/glass-chip.css +0 -211
  1109. package/src/styles/glass/grain-overlay.css +0 -79
  1110. package/src/styles/glass/ladder-undershadow.css +0 -61
  1111. package/src/styles/glass/ladder.css +0 -452
  1112. package/src/styles/glass/liquid-enter.css +0 -240
  1113. package/src/styles/glass/liquid-fill.css +0 -65
  1114. package/src/styles/glass/material-roles.css +0 -18
  1115. package/src/styles/glass/material.css +0 -500
  1116. package/src/styles/glass/progress-rail.css +0 -78
  1117. package/src/styles/glass/reveal.css +0 -333
  1118. package/src/styles/glass/rim.css +0 -122
  1119. package/src/styles/glass/squircle.css +0 -58
  1120. package/src/styles/glass/surface-axis.css +0 -136
  1121. package/src/styles/glass/surfaces-pager.css +0 -79
  1122. package/src/styles/glass/surfaces.css +0 -434
  1123. package/src/styles/glass-refract.css +0 -112
  1124. package/src/styles/glass-specular-track.css +0 -74
  1125. package/src/styles/glass.css +0 -110
  1126. package/src/styles/index.css +0 -288
  1127. package/src/styles/paper.css +0 -187
  1128. package/src/styles/scroll-choreography.css +0 -294
  1129. package/src/styles/scroll-chrome.css +0 -123
  1130. package/src/styles/scroll-driven.css +0 -123
  1131. package/src/styles/theme/bridges.css +0 -380
  1132. package/src/styles/theme/dark.css +0 -7
  1133. package/src/styles/theme/literals.css +0 -104
  1134. package/src/styles/theme/radius.css +0 -148
  1135. package/src/styles/theme.css +0 -33
  1136. package/src/styles/tokens/color-radius.css +0 -308
  1137. package/src/styles/tokens/dark-arm-glass.css +0 -55
  1138. package/src/styles/tokens/dark-arm.css +0 -455
  1139. package/src/styles/tokens/glass-deep.css +0 -126
  1140. package/src/styles/tokens/glass-fx.css +0 -480
  1141. package/src/styles/tokens/glass.css +0 -477
  1142. package/src/styles/tokens/light-dark.css +0 -216
  1143. package/src/styles/tokens/motion-registers.css +0 -93
  1144. package/src/styles/tokens/offsets.css +0 -142
  1145. package/src/styles/tokens/on-glass-fg.css +0 -39
  1146. package/src/styles/tokens/property-regs-specular.css +0 -160
  1147. package/src/styles/tokens/property-regs.css +0 -448
  1148. package/src/styles/tokens/scale-paper.css +0 -495
  1149. package/src/styles/tokens/scheme-motion.css +0 -425
  1150. package/src/styles/tokens/scheme-spring.css +0 -282
  1151. package/src/styles/tokens/scroll-tokens.css +0 -137
  1152. package/src/styles/tokens/shadow.css +0 -206
  1153. package/src/styles/tokens/sizing-config.css +0 -110
  1154. package/src/styles/tokens/sizing.css +0 -505
  1155. package/src/styles/tokens.css +0 -68
  1156. package/src/styles/tokens.ts +0 -58
  1157. package/src/styles/transitions.css +0 -228
  1158. package/src/styles/typography/scale.css +0 -166
  1159. package/src/styles/typography/semantic.css +0 -233
  1160. package/src/styles/typography/utilities.css +0 -197
  1161. package/src/styles/typography.css +0 -46
  1162. package/src/styles/utilities/a11y-overrides.css +0 -184
  1163. package/src/styles/utilities/base-misc.css +0 -307
  1164. package/src/styles/utilities/base.css +0 -337
  1165. package/src/styles/utilities/btn.css +0 -306
  1166. package/src/styles/utilities/components.css +0 -337
  1167. package/src/styles/utilities/metal.css +0 -272
  1168. package/src/styles/utilities.css +0 -24
  1169. package/src/styles/view-transition.css +0 -67
  1170. package/src/styles/viz-reveal.css +0 -63
  1171. /package/dist/{_plugin-vue_export-helper-C1je1s0u.js → _plugin-vue_export-helper-B3ysoDQm.js} +0 -0
  1172. /package/dist/{compile-DK3Fri1B.js → compile-D7VYG3gi.js} +0 -0
  1173. /package/dist/components/_shared/{valueDomain.d.ts → field/valueDomain.d.ts} +0 -0
  1174. /package/dist/composables/glass/{webgl/shaders/procedural-color.glsl.d.ts → procedural/color.glsl.d.ts} +0 -0
  1175. /package/dist/{components/aurora/constants/shaders/procedural-color.wgsl.d.ts → composables/glass/procedural/color.wgsl.d.ts} +0 -0
  1176. /package/dist/composables/motion/{useIntersectionPause.d.ts → core/useIntersectionPause.d.ts} +0 -0
  1177. /package/dist/composables/motion/{useRAFLoop.d.ts → core/useRAFLoop.d.ts} +0 -0
  1178. /package/dist/composables/motion/{useYieldToMain.d.ts → core/useYieldToMain.d.ts} +0 -0
  1179. /package/dist/composables/motion/{useAnimatedNumber.d.ts → number/useAnimatedNumber.d.ts} +0 -0
  1180. /package/dist/composables/motion/{vReveal.d.ts → reveal/vReveal.d.ts} +0 -0
  1181. /package/dist/composables/motion/{scrollReader.d.ts → scroll/scrollReader.d.ts} +0 -0
  1182. /package/dist/composables/motion/{supportsCssTimeline.d.ts → scroll/supportsCssTimeline.d.ts} +0 -0
  1183. /package/dist/composables/motion/{useScrollTrigger.d.ts → scroll/useScrollTrigger.d.ts} +0 -0
  1184. /package/dist/{createContext-FU0n5MGK.js → createContext-BhkE9mLH.js} +0 -0
  1185. /package/dist/{dark-DIH-blY1.js → dark-z_P5QwqI.js} +0 -0
  1186. /package/dist/{motionTempo-CMS5b0Vu.js → motionTempo-tscp92Nm.js} +0 -0
  1187. /package/dist/{prng-Dz2_9RJu.js → prng-2uS6J_A1.js} +0 -0
  1188. /package/dist/{rendererStatus-C2-zC2Q-.js → rendererStatus-BxUlBLVy.js} +0 -0
  1189. /package/dist/{useFuzzySearch-BOgep7RE.js → useFuzzySearch-CUnZknpm.js} +0 -0
  1190. /package/dist/{useResizeObserver-BcUTquV7.js → useResizeObserver-CdUlm2QZ.js} +0 -0
  1191. /package/dist/{useTimer-D8koDvJS.js → useTimer-C3yfti4C.js} +0 -0
  1192. /package/dist/{writeVelocityWeight-D_AcvTsE.js → writeVelocityWeight-DoGOCW3e.js} +0 -0
package/dist/aurora.js CHANGED
@@ -1,23 +1,130 @@
1
- import { t as e } from "./_plugin-vue_export-helper-C1je1s0u.js";
2
- import { t } from "./usePointerVelocityField-BdvnO0py.js";
3
- import { d as n, s as r } from "./pointerFieldMappings-CZojkgKj.js";
4
- import { t as i } from "./useScrollProgress-DZI4nf4v.js";
5
- import { t as a } from "./useIntersectionPause-B-dbxl9w.js";
6
- import { cssToOklch as o, deriveHue as s, gamutMapStop as c, oklchStopToHex as l, oklchToLinear as u, warmCatchLight as d } from "./color.js";
7
- import { a as f, c as p, d as m, f as h, i as g, m as _, n as v, o as y, p as b, r as x, s as S, t as C, u as ee } from "./procedural-color.wgsl-B9mLK7v0.js";
8
- import { t as te } from "./flow.glsl-DEGFGE11.js";
9
- import { a as ne, n as w, o as re } from "./rendererStatus-C2-zC2Q-.js";
10
- import { n as ie, t as ae } from "./compile-DK3Fri1B.js";
11
- import { computed as T, createElementBlock as oe, createElementVNode as se, defineComponent as ce, getCurrentInstance as le, normalizeClass as ue, normalizeStyle as de, onBeforeUnmount as E, onMounted as D, openBlock as fe, readonly as O, ref as pe, shallowRef as me, unref as he, watch as k } from "vue";
12
- import { interpolateHue as ge, oklabToLinearSRGB as _e, rawOklabToOklch as ve, rawOklchToOklab as ye, srgbToOKLab as be } from "@mkbabb/value.js";
1
+ import { t as e } from "./_plugin-vue_export-helper-B3ysoDQm.js";
2
+ import { t } from "./usePointerVelocityField-DsIf7yyq.js";
3
+ import { d as n, s as r } from "./pointerFieldMappings-DP5tla5z.js";
4
+ import { t as i } from "./useScrollProgress-Bq-KPaKM.js";
5
+ import { t as a } from "./useIntersectionPause-CzKR3EyP.js";
6
+ import { t as o } from "./value-DMhh2R94.js";
7
+ import { cssToOklch as s, deriveHue as c, gamutMapStop as l, oklchStopToHex as u, oklchToLinear as d, warmCatchLight as f } from "./color.js";
8
+ import { a as p, c as m, d as h, f as g, i as _, l as v, n as y, o as b, p as x, r as S, s as C, t as w, u as T } from "./color.wgsl-B0N3mKMb.js";
9
+ import { a as E, n as D, o as ee } from "./rendererStatus-BxUlBLVy.js";
10
+ import { n as te, t as O } from "./compile-D7VYG3gi.js";
11
+ import { computed as k, createElementBlock as ne, createElementVNode as A, defineComponent as re, getCurrentInstance as ie, normalizeClass as ae, normalizeStyle as oe, onBeforeUnmount as se, onMounted as ce, openBlock as le, readonly as ue, ref as de, shallowRef as fe, unref as j, watch as M } from "vue";
12
+ import { interpolateHue as pe } from "@mkbabb/value.js/color";
13
+ //#region src/components/aurora/composables/textureUpload.ts
14
+ var me = {
15
+ premultiplyAlpha: "none",
16
+ colorSpaceConversion: "none"
17
+ };
18
+ function he(e) {
19
+ return createImageBitmap(e, me);
20
+ }
21
+ async function ge(e) {
22
+ return typeof e == "string" ? he(await (await fetch(e)).blob()) : e instanceof Blob ? he(e) : e;
23
+ }
24
+ function N() {
25
+ let e = new ImageData(1, 1);
26
+ return e.data.set([
27
+ 128,
28
+ 128,
29
+ 128,
30
+ 255
31
+ ]), e;
32
+ }
33
+ function _e(e) {
34
+ return e instanceof HTMLImageElement ? {
35
+ width: e.naturalWidth,
36
+ height: e.naturalHeight
37
+ } : {
38
+ width: e.width,
39
+ height: e.height
40
+ };
41
+ }
42
+ function ve(e, t, n = {}) {
43
+ let { linearFilter: r = !0, clampToEdge: i = !0 } = n, a = e.createTexture();
44
+ e.bindTexture(e.TEXTURE_2D, a), e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !1), e.pixelStorei(e.UNPACK_COLORSPACE_CONVERSION_WEBGL, e.NONE), e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL, !1), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA, e.RGBA, e.UNSIGNED_BYTE, t);
45
+ let o = r ? e.LINEAR : e.NEAREST;
46
+ e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, o), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, o);
47
+ let s = i ? e.CLAMP_TO_EDGE : e.REPEAT;
48
+ return e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, s), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, s), e.pixelStorei(e.UNPACK_COLORSPACE_CONVERSION_WEBGL, e.BROWSER_DEFAULT_WEBGL), a;
49
+ }
50
+ function P(e, t) {
51
+ let { width: n, height: r } = _e(t), i = e.createTexture({
52
+ label: "[texture-upload] image",
53
+ size: [
54
+ n,
55
+ r,
56
+ 1
57
+ ],
58
+ format: "rgba8unorm",
59
+ usage: 22
60
+ });
61
+ return e.queue.copyExternalImageToTexture({
62
+ source: t,
63
+ flipY: !1
64
+ }, {
65
+ texture: i,
66
+ premultipliedAlpha: !1
67
+ }, [
68
+ n,
69
+ r,
70
+ 1
71
+ ]), {
72
+ texture: i,
73
+ width: n,
74
+ height: r
75
+ };
76
+ }
77
+ //#endregion
78
+ //#region src/components/aurora/composables/auroraImageSource.ts
79
+ function ye(e) {
80
+ let t = null, n = null, r;
81
+ return {
82
+ getDecodedImage: () => t,
83
+ setImageUploader(e) {
84
+ n = e, e && t && e(t);
85
+ },
86
+ ensureDecoded(i) {
87
+ i.source !== "image" || i.src == null || i.src !== r && (r = i.src, ge(i.src).then((r) => {
88
+ t = r, n?.(r), e.wake();
89
+ }).catch((t) => {
90
+ e.onError?.(t instanceof Error ? t : Error(String(t)));
91
+ }));
92
+ }
93
+ };
94
+ }
95
+ function be(e, t, n) {
96
+ let r = null;
97
+ e.activeTexture(e.TEXTURE0);
98
+ let i = (n) => {
99
+ e.useProgram(t), e.activeTexture(e.TEXTURE0), r && e.deleteTexture(r), r = ve(e, n);
100
+ };
101
+ return i(n.getDecodedImage() ?? N()), n.setImageUploader(i), () => {
102
+ r && e.deleteTexture(r), n.setImageUploader(null);
103
+ };
104
+ }
105
+ //#endregion
106
+ //#region src/components/aurora/constants/renderMode.ts
107
+ function F() {
108
+ let e;
109
+ try {
110
+ e = h();
111
+ } catch {
112
+ return !1;
113
+ }
114
+ return e === null ? !1 : e.includes("swiftshader") || e.includes("llvmpipe") || e.includes("software") || e.includes("basic render") || e.includes("microsoft basic");
115
+ }
116
+ function I(e, t = {}) {
117
+ return e !== "css" && !t.forceWebGLUnderSoftwareRaster && F() ? "css" : e === "auto" ? "webgl" : e;
118
+ }
119
+ //#endregion
13
120
  //#region src/components/aurora/constants/shaders/aurora.vert.ts
14
- var xe = "#version 300 es\nin vec2 aPos;\nout vec2 vUv;\nvoid main() {\n vUv = aPos * 0.5 + 0.5;\n gl_Position = vec4(aPos, 0.0, 1.0);\n}", Se = "// ── Palette LUT (W5 — OKLCh/OKLab interpolation) ──────────────────────────────\n// The endpoints stay CPU-baked to LINEAR sRGB (the Aras precompute pattern); only\n// the BETWEEN-endpoint interpolation runs in perceptual space. AX.W11 — the ramp\n// itself (the smoothstep ease + the OKLab-rectangular-vs-OKLCh-hue-arc dispatch on\n// uHuePath) is the shared samplePaletteRamp from procedural-color.glsl, the SAME\n// source the WebGPU twin splices; samplePalette here only selects the bracketing\n// stop pair + the raw inter-stop t and hands them to the shared ramp.\nvec3 samplePalette(float id) {\n id = clamp(id, 0.0, 1.0);\n float scaled = id * float(uStopCount - 1);\n int i0 = int(floor(scaled));\n int i1 = min(i0 + 1, uStopCount - 1);\n float t = fract(scaled);\n return samplePaletteRamp(uPalette[i0], uPalette[i1], t, uHuePath);\n}\n\n// ── Nuclei field ──────────────────────────────────────────────────────────\nvoid nucleiField(vec2 p, float t, out float paletteId, out float valueMod) {\n float accumBias = 0.0;\n float accumValue = 0.0;\n float accumW = 0.0;\n for (int i = 0; i < MAX_NUCLEI; i++) {\n if (i >= uNucleiCount) break;\n vec2 posI = uNucleiPos[i]\n + uNucleiDriftRadius[i] * vec2(\n cos(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i]),\n sin(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i] * 1.13)\n );\n vec2 diff = p - posI;\n // Anisotropic Gaussian: rotate diff into the nucleus's local frame\n // (major axis along uNucleiAngle), then scale the major-axis component by\n // 1/elongation so the squared distance describes an ellipse. Defaults\n // 1.0/0.0 reduce to the isotropic dot(diff, diff).\n float ca = cos(uNucleiAngle[i]);\n float sa = sin(uNucleiAngle[i]);\n vec2 local = vec2( ca * diff.x + sa * diff.y,\n -sa * diff.x + ca * diff.y);\n float along = local.x / max(uNucleiElong[i], 0.01);\n float across = local.y;\n float d2 = along * along + across * across;\n float r = max(uNucleiRadius[i], 0.01);\n float w = exp(-uSoftmaxBeta * d2 / (r * r));\n accumBias += w * uNucleiPaletteBias[i];\n accumValue += w * uNucleiValueBias[i];\n accumW += w;\n }\n paletteId = accumBias / max(accumW, 1e-4);\n valueMod = accumValue / max(accumW, 1e-4);\n\n // Palette drift — slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30–60s hue cycle at the default coefficient);\n // the amplitude lifts the authored coefficient into a 0.03..0.06 paletteId band\n // so the palette visibly travels rather than dithering within one stop.\n float palAmp = clamp(uPaletteDrift * 6.0, 0.0, 0.06);\n paletteId += palAmp * sin(t * uPaletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n}\n", Ce = "// ── Flow field ────────────────────────────────────────────────────────────\n// Structure-tensor prototype (body defined post-sampleBase in the mediums block).\n// Returns vec4(dir.x, dir.y, coherence, packedGrad) — the minor-eigenvector tangent + A + the\n// metal-gradient .w lane (BG.W-AUR-METAL-FINISH widened vec3→vec4). The forward decl MUST match\n// the vec4 definition or WebKit's compiler rejects the mismatched signature (L1b).\nvec4 structureTensorField(vec2 p, float t, vec2 fallbackDir);\n\nvec2 flowField(vec2 p, float t) {\n vec2 dir = vec2(1.0, 0.0);\n if (uFlowPattern == 1) {\n // radial\n dir = normalize(p - uFlowFocal + 1e-4);\n } else if (uFlowPattern == 2) {\n // swirl — tangent to radial\n vec2 rad = normalize(p - uFlowFocal + 1e-4);\n dir = vec2(-rad.y, rad.x);\n } else if (uFlowPattern == 3) {\n // diagonal\n float a = radians(uFlowAngle);\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 4) {\n // multi — curl-noise driven\n float n = fbm(p * 2.0 + t * 0.02);\n float a = n * 6.2831;\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 5) {\n // tensor / ETF — the color field's OWN edge-tangent flow (AW.W4.1). The\n // diagonal angle seeds the smooth fallback the coherence-weighted blend\n // relaxes toward in low-structure zones. Returns directly (no curl/cursor\n // post-mix — the tensor field already carries the structure orientation;\n // the cursor warp still drives the underlying color field in domainWarp).\n float a = radians(uFlowAngle);\n vec2 fallback = vec2(cos(a), sin(a));\n return structureTensorField(p, t, fallback).xy;\n }\n // curl — perturb by noise. Radial gets much less curl so rays stay clean.\n if (uFlowCurl > 0.0) {\n float n = fbm(p * 3.0) - 0.5;\n float curlAmt = uFlowCurl;\n if (uFlowPattern == 1) curlAmt *= 0.25; // radial: preserve ray clarity\n if (uFlowPattern == 2) curlAmt *= 0.55; // swirl: allow moderate curl\n float a = atan(dir.y, dir.x) + n * 3.14159 * curlAmt;\n dir = vec2(cos(a), sin(a));\n }\n // cursor influence — swirl around the cursor position\n if (uCursorStrength > 0.001) {\n vec2 toCur = uCursor - p;\n float d = length(toCur);\n float r = max(uCursorRadius, 0.01);\n float w = exp(-(d * d) / (r * r * 0.5));\n // swirl tangent\n vec2 tangent = vec2(-toCur.y, toCur.x) / max(d, 1e-4);\n float a0 = atan(dir.y, dir.x);\n float a1 = atan(tangent.y, tangent.x);\n // blend angle\n float da = a1 - a0;\n da = atan(sin(da), cos(da)); // wrap to [-pi, pi]\n float a = a0 + da * w * uCursorStrength;\n dir = vec2(cos(a), sin(a));\n }\n // AW.W8.1 — velocity-reactive swirl-BURST: a fast flick (uCursorBurst, decaying\n // over ~1s) injects a transient flow bias along the pointer velocity, distinct from\n // the steady attraction above (the stateless swirl is SUPERSEDED by this form). The\n // burst is gated by the master tempo scalar on the CPU side (it decays to 0 under\n // PRM/pause), so this term cannot leak motion under reduced-motion.\n if (uCursorBurst > 0.001) {\n vec2 toCur = uCursor - p;\n float d = length(toCur);\n float r = max(uCursorRadius, 0.01);\n float w = exp(-(d * d) / (r * r * 0.7));\n // Bias the flow toward the velocity direction, scaled by the decaying burst.\n vec2 velDir = uCursorVelocity / max(length(uCursorVelocity), 1e-4);\n float a0 = atan(dir.y, dir.x);\n float a1 = atan(velDir.y, velDir.x);\n float da = atan(sin(a1 - a0), cos(a1 - a0));\n float a = a0 + da * w * uCursorBurst * 0.8;\n dir = vec2(cos(a), sin(a));\n }\n return dir;\n}\n", we = "// Khronos PBR-Neutral tonemap (hue + saturation preserving)\nvec3 aces(vec3 color) {\n const float startCompression = 0.8 - 0.04;\n const float desaturation = 0.15;\n float x = min(color.r, min(color.g, color.b));\n float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;\n color -= offset;\n float peak = max(color.r, max(color.g, color.b));\n if (peak < startCompression) return clamp(color, 0.0, 1.0);\n float d = 1.0 - startCompression;\n float newPeak = 1.0 - d * d / (peak + d - startCompression);\n color *= newPeak / peak;\n float g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);\n return clamp(mix(color, newPeak * vec3(1.0), g), 0.0, 1.0);\n}\n", Te = "// ── Curved swept brushstroke primitive ────────────────────────────────────\n// A stroke is a curved spine from A to B with quadratic bulge K (sideways offset\n// at midpoint). Width varies along length via a shape profile. Edge is ragged\n// (bristle-modulated), ends are rounded blobs, inside has streak modulation.\n//\n// For pixel p, we invert an approximate projection onto the curved spine:\n// 1) project p onto straight AB axis -> along0, cross0\n// 2) at parameter along0, spine offset = 4·K·along0·(1-along0) perpendicular\n// 3) cross = cross0 - spineOffset, refine along one iteration\n// Coverage blends paint atop col based on per-stroke color sampled from the\n// underlying base at spine midpoint — so overlapping strokes from different\n// palette regions meet at hard, ragged demarcations.\n\nstruct StrokeHit {\n float coverage; // 0..1\n vec3 color;\n float alongT; // 0..1 along spine, for internal modulation\n float crossN; // cross distance / current half-width, signed\n float edgeN; // distance to edge in half-widths (0 = at edge, 1 = at spine)\n};\n\nStrokeHit noHit() { return StrokeHit(0.0, vec3(0.0), 0.0, 0.0, 0.0); }\n\nvec2 rotateDir(vec2 dir, float angle) {\n float ca = cos(angle), sa = sin(angle);\n return vec2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);\n}\n\nvec2 safeDir(vec2 dir) {\n float len = length(dir);\n return len > 1e-4 ? dir / len : vec2(1.0, 0.0);\n}\n\n// Shape profile along the stroke. type:\n// 0 tapered — thin-fat-thin (classic brush)\n// 1 load-drag — fat start, tapers to a point\n// 2 dab — ellipse-like, round center\n// 3 even — near-constant, slight end taper\n// 4 comma — van-Gogh atomic dab: ASYMMETRIC taper, fat near one end and\n// drawn to a fine point at the other (the divisionist crescent\n// mark). The width peak sits off-centre (~0.32) so the two ends\n// taper at DIFFERENT rates — a loaded round head + a dragged tail.\nfloat strokeShape(float t, int type) {\n t = clamp(t, 0.0, 1.0);\n if (type == 1) {\n // loaded at t=0, tapering\n return pow(1.0 - t, 0.55) * smoothstep(0.0, 0.08, t);\n } else if (type == 2) {\n // dab / blob\n float d = t - 0.5;\n return exp(-d * d * 12.0);\n } else if (type == 3) {\n // mostly even, slight end softening\n return smoothstep(0.0, 0.08, t) * smoothstep(1.0, 0.92, t) * 0.95 + 0.05;\n } else if (type == 4) {\n // comma / crescent — width peaks at ~0.32 then drags to a fine tail. The two\n // sides of the peak fall at different exponents (a fat round head, a long thin\n // tail), so the mark reads as a directional van-Gogh comma, not a symmetric oval.\n float peak = 0.32;\n float head = smoothstep(0.0, peak, t); // rounded loaded head\n float tail = pow(clamp((1.0 - t) / (1.0 - peak), 0.0, 1.0), 1.6); // dragged thin tail\n return clamp(min(head, tail) * 1.04, 0.0, 1.0);\n }\n // 0 tapered\n return smoothstep(0.0, 0.22, t) * smoothstep(1.0, 0.78, t);\n}\n\n// A single curved stroke.\n// a, b — endpoints\n// halfW — base half-width (world units)\n// bulge — signed perpendicular midpoint offset, in world units\n// shapeType — 0..3\n// bristleFreq — spatial frequency of edge raggedness\n// bristleAmp — 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed — uniqueness seed for internal streaks\n// colAtMid — pre-sampled color at midpoint\nStrokeHit curvedStroke(vec2 p, vec2 a, vec2 b, float halfW,\n float bulge, int shapeType,\n float bristleFreq, float bristleAmp,\n float streakSeed, vec3 colAtMid) {\n vec2 ab = b - a;\n float L = length(ab);\n if (L < 1e-5) return noHit();\n vec2 tang = ab / L;\n vec2 norm = vec2(-tang.y, tang.x);\n\n vec2 rel = p - a;\n float along0 = dot(rel, tang) / L; // straight projection 0..1 along AB\n float cross0 = dot(rel, norm); // signed cross\n\n // Spine sideways offset due to bulge (parabolic)\n float bend = 4.0 * bulge * along0 * (1.0 - along0);\n float cross1 = cross0 - bend;\n\n // One refinement step: as the spine bends, the closest-point along shifts.\n // Spine tangent differs from straight tangent; first-order correction:\n float dBend = 4.0 * bulge * (1.0 - 2.0 * along0); // d(bend)/d(along)\n float along1 = along0 + (cross1 * dBend) / (L * (1.0 + dBend * dBend / (L * L)));\n\n // Bristle-ragged edge: half-width gets chewed by 1D noise along the spine\n float edgeNoise = fbm(vec2(along1 * bristleFreq, streakSeed * 7.3)) - 0.5;\n float edgeNoise2 = fbm(vec2(along1 * bristleFreq * 2.3, streakSeed * 3.1 + 17.0)) - 0.5;\n float edgeMod = 1.0 - bristleAmp * (0.6 + 0.4 * edgeNoise) * (0.5 + edgeNoise2);\n\n // Width profile along the stroke\n float shape = strokeShape(along1, shapeType);\n float halfWNow = halfW * shape * edgeMod;\n\n // Inside-segment coverage\n float cov = 0.0;\n if (along1 >= 0.0 && along1 <= 1.0 && halfWNow > 1e-6) {\n float cn = abs(cross1) / halfWNow;\n // fwidth-derived AA: half-width from the field's screen-space gradient so the\n // edge holds ~1px regardless of zoom/DPR (mirrors metaball.frag.ts:252-255).\n float aaC = max(fwidth(cn), 1e-6);\n cov = 1.0 - smoothstep(1.0 - aaC, 1.0 + aaC, cn);\n }\n\n // End-cap blobs (rounded tips, not perpendicular cuts)\n float capA = 0.0, capB = 0.0;\n {\n vec2 capCenterA = a + norm * (bulge * 0.0); // at a\n vec2 capCenterB = a + tang * L + norm * bend * 0.0 + norm * 0.0; // at b; bend is 0 at endpoints\n capCenterB = b;\n float endShapeA = strokeShape(0.02, shapeType);\n float endShapeB = strokeShape(0.98, shapeType);\n float rA = halfW * endShapeA * (0.9 + 0.2 * fbm(vec2(streakSeed * 13.0)));\n float rB = halfW * endShapeB * (0.9 + 0.2 * fbm(vec2(streakSeed * 19.0 + 5.0)));\n float dA = length(p - capCenterA);\n float dB = length(p - capCenterB);\n // fwidth-derived AA on the clean Euclidean cap distances (mirrors metaball.frag.ts:252-255).\n float aaA = max(fwidth(dA), 1e-6);\n float aaB = max(fwidth(dB), 1e-6);\n capA = 1.0 - smoothstep(rA - aaA, rA + aaA, dA);\n capB = 1.0 - smoothstep(rB - aaB, rB + aaB, dB);\n // Only apply caps if we're BEYOND the segment; otherwise the segment wins.\n capA *= (along1 < 0.05) ? 1.0 : 0.0;\n capB *= (along1 > 0.95) ? 1.0 : 0.0;\n }\n\n float coverage = max(cov, max(capA, capB));\n if (coverage < 0.002) return noHit();\n\n // Edge distance (for impasto later): 0 at edge, 1 at spine\n float edgeDist = halfWNow > 1e-6 ? clamp(1.0 - abs(cross1) / halfWNow, 0.0, 1.0) : 0.0;\n\n return StrokeHit(\n coverage,\n colAtMid,\n clamp(along1, 0.0, 1.0),\n halfWNow > 1e-6 ? cross1 / halfWNow : 0.0,\n edgeDist\n );\n}\n\n// The painterly STROKE mediums (those that deposit via paintStrokeLayers/paintOver):\n// oil (3), van-Gogh (5), oil-pastel (6). For these the stroke OVER-composite runs in\n// OKLab so overlapping complementaries mix through a chromatic path, not the linear-\n// RGB grey mud; the smooth/atmospheric pole never reaches paintOver so it pays nothing.\nbool isPainterlyStroke() {\n return uMedium == 3 || uMedium == 5 || uMedium == 6;\n}\n\n// OKLab OVER-composite: lerp L,a,b of the over-color toward the under-color (the\n// Ottosson matrices spliced from procedural-color.glsl.ts — no new color math). Two\n// complementary-hued strokes overlapping transition through OKLab, where the midpoint\n// holds chroma, instead of the linear mix() that desaturates toward grey (slice 8 F2).\nvec3 paintOverOklab(vec3 under, vec3 over, float alpha) {\n vec3 labU = linOklab(under);\n vec3 labO = linOklab(over);\n return max(oklabToLinearSrgb(mix(labU, labO, alpha)), vec3(0.0));\n}\n\n// Composite stroke over col with internal streaking + ACCUMULATE paint HEIGHT.\n// streakAmp — 0..0.2 how much internal streaks darken/lighten\n// impastoAmp — 0..1 paint-thickness contribution (the height the relight reads)\n// hardness — 0..1 how crisp the compositing transition is (1 = razor, 0 = creamy)\n//\n// AW.W4.2 — the fixed-RGB edge rim (the phantom upper-left light) is RETIRED. Each\n// stroke now deposits PAINT HEIGHT into the height accumulator (coverage x per-stroke\n// thickness, perturbed by the bristle/streak fbm for ridges/grooves); mediumOil\n// derives a normal from the accumulated height gradient (dFdx/dFdy) and relights it\n// once with the movable uLightDir source (relightImpasto), in linear before aces().\n//\n// AX.W13 — the internal streak now perturbs HUE + CHROMA in OKLCh (not value-only),\n// so a single stroke carries a small hue gradient (broken color at the ATOM level —\n// adjacent strokes shimmer like real impasto, not flat stamped swatches; slice 8 F5);\n// and the OVER-composite runs in OKLab on the painterly stroke mediums so overlapping\n// complementaries mix as pigment, not the linear-mix grey (slice 8 F2).\n// impastoFloor — the per-stroke height-crown floor: oil's 0.4+0.6*edgeN reads\n// floor=0.4 (the crown tapers to 40% at the stroke edge); the van-Gogh\n// profile passes floor=1.0 so each atomic dab catches a FULL-height\n// glint (scope item 1d). The crown magnitude is floor + (1-floor)*edgeN.\nvoid paintOver(inout vec3 col, inout float height, StrokeHit s,\n float streakFreq, float streakAmp,\n float impastoAmp, float hardness, float streakSeed,\n float impastoFloor) {\n if (s.coverage < 0.002) return;\n float strokeOpacity = clamp(uStrokeAmount, 0.0, 1.0);\n if (strokeOpacity <= 0.001) return;\n vec3 c = s.color;\n\n // Internal streaks — fbm along spine, modulated by cross position. Gives\n // the loaded-brush look: some bristles carry more pigment than others.\n float streakA = fbm(vec2(s.alongT * streakFreq, s.crossN * 2.7 + streakSeed));\n float streakB = fbm(vec2(s.alongT * streakFreq * 0.6 + streakSeed * 3.7, s.crossN * 4.1));\n float streak = 0.6 * (streakA - 0.5) + 0.4 * (streakB - 0.5);\n\n // ── WITHIN-stroke OKLCh broken color (AX.W13, slice 8 F5). The streak fBm perturbs\n // the stroke's HUE + CHROMA along alongT/crossN, seeded per-stroke, so a single\n // stroke reads a small hue gradient (the atom-level broken-color shimmer). Value is\n // carried by the same streak so the loaded-brush light/dark survives. Gated to the\n // painterly stroke mediums + uBrokenColor (the smooth pole keeps the cheap value-only\n // streak). A second decorrelated fBm drives the hue axis so hue ≠ a scaled value.\n float hueStreak = fbm(vec2(s.alongT * streakFreq * 0.45 + streakSeed * 5.3,\n s.crossN * 1.9 + streakSeed * 1.7)) - 0.5;\n if (isPainterlyStroke() && uBrokenColor > 0.001) {\n vec3 lch = oklabToOklch(linOklab(c));\n // ±~10° within-stroke hue swing + ±~9% chroma, scaled by uBrokenColor. Smaller\n // than the per-stroke cell jitter (brokenColorJitter) — this is the intra-stroke\n // gradient layered on top of the per-cell pigment patch.\n lch.z += hueStreak * (20.0 * PI / 180.0) * uBrokenColor;\n lch.y = max(lch.y * (1.0 + (streak) * 0.18 * uBrokenColor), 0.0);\n c = max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n }\n // Value streak (the loaded-brush light/dark across the bristles) — kept for every\n // medium that reaches paintOver (the smooth pole never does). The along-spine value\n // modulation is the loaded-brush light/dark that gives each stroke its OWN directional\n // luminance band — the structure-tensor signal a low-contrast color field (a uniform-hue\n // oil field) has no other source for (the §4.2 anisotropy lift on the flat-palette case).\n c *= 1.0 + streak * streakAmp * 3.2;\n\n // Cross-stroke ridge/valley shading — a catch-light along the spine centre, darkening to\n // the stroke edges. This is the strongest DIRECTIONAL luminance cue: it carves a bright\n // ridge down the stroke's length and dark grooves between adjacent strokes, so a field of\n // parallel strokes reads as parallel light/dark bands (the high-A van-Gogh signature) even\n // where the underlying colour is flat. s.edgeN is 0 at the edge, 1 at the spine.\n float ridgeShade = smoothstep(0.0, 0.55, s.edgeN);\n c *= 1.0 + (ridgeShade - 0.5) * 0.30;\n\n float softLimit = mix(0.35, 0.98, hardness);\n float alpha = smoothstep(0.0, 1.0 - softLimit, s.coverage) * strokeOpacity;\n // OKLab OVER-composite on the painterly stroke mediums (overlap mixes as pigment,\n // not grey); the smooth pole never reaches paintOver, so it keeps the cheap path.\n col = isPainterlyStroke() ? paintOverOklab(col, c, alpha) : mix(col, c, alpha);\n\n // ── Accumulate paint HEIGHT (AW.W4.2). Thickness peaks at the stroke spine and\n // falls toward the impastoFloor at the edge (edgeN: 0=edge, 1=spine), perturbed by\n // the bristle streak so ridges/grooves form. The relight reads the gradient of this\n // field. The van-Gogh profile's floor=1.0 gives every atomic dab a full-height crown.\n float ridge = 0.7 + 0.3 * (streak + 0.5);\n float crown = impastoFloor + (1.0 - impastoFloor) * s.edgeN;\n height += s.coverage * impastoAmp * crown * ridge * alpha;\n}\n\n// AW.W4.2 — relight the accumulated paint height. Derive a normal from the height\n// gradient (dFdx/dFdy — derivatives are in-pattern; fwidth is already used for AA),\n// then apply diffuse + Blinn specular from the movable uLightDir/uLightColor source.\n// LINEAR-space lighting BEFORE aces() (the tonemap/OETF stay locked); thin strokes\n// inherit a low shininess, thick impasto raises it (high-height ridges catch a sharp\n// glint). canvasBase is the tooth height so the canvas weave also catches the light.\nvec3 relightImpasto(vec3 col, float height, float canvasBase) {\n float h = height + canvasBase;\n // Height-gradient normal. heightScale trades relief depth for surface flatness.\n float heightScale = 6.0;\n vec3 N = normalize(vec3(-dFdx(h) * 40.0, -dFdy(h) * 40.0, 1.0 / heightScale));\n vec3 L = normalize(uLightDir);\n vec3 V = vec3(0.0, 0.0, 1.0); // view straight on\n vec3 H = normalize(L + V);\n float diff = max(dot(N, L), 0.0);\n // Thick impasto → high shininess (sharp glint); thin paint → broad soft sheen.\n float shininess = mix(8.0, 64.0, clamp(height * 2.0, 0.0, 1.0));\n float spec = pow(max(dot(N, H), 0.0), shininess);\n // Modulate the lit terms by how much paint sits here (no relief on bare ground).\n float relief = clamp(h * 1.5, 0.0, 1.0);\n vec3 lit = col * (1.0 + uImpasto * relief * (diff - 0.5) * 0.5 * uLightColor);\n // The specular glint is gated to ACCUMULATED-thick paint (height²) — a per-fine-stroke\n // sharp glint on every thin fill dab read as bright isotropic SPECKLE that buried the\n // directional stroke bands (the §4.2 anisotropy / §4.3 slope sink on the low-contrast\n // oil field). Only where real impasto has built up does the raked glint fire, so the\n // highlight tracks the macro ridges, not the fine speckle.\n float specGate = clamp(height, 0.0, 1.0);\n lit += uImpasto * relief * spec * specGate * 0.5 * uLightColor;\n return lit;\n}\n\n// Best-of-9-neighbor placement: sample 3x3 surrounding cells and take the\n// stroke that covers this pixel most. Breaks the grid by per-cell jitter and\n// sparse density via noise thresholding.\n// energyGrade — the SBR energy-grade magnitude (0 = none, the oil/oil-pastel\n// baseline; 1 = the full van-Gogh Starry-Night cascade — long\n// confident strokes in the lights/coherent zones, short dabs in\n// the darks/flat zones). The van-Gogh profile carries 1.0 (the\n// energy grade is a PROFILE field, not a buried uMedium==5 branch).\nStrokeHit bestOil(vec2 p, float cellSize, float lenMul, float halfWMul,\n float jitterAmt, float density, int shapeType,\n float bristleAmp, vec2 flow, float t, float seed,\n float energyGrade) {\n vec2 cell = floor(p / cellSize);\n\n StrokeHit best = noHit();\n // 3x3 neighborhood\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n vec2 hh = hash22(cc + seed) - 0.5;\n // Density gate — noise threshold; sparse placement.\n float gate = hash21(cc * 1.7 + seed * 0.3);\n if (gate > density) continue;\n\n vec2 center = (cc + 0.5 + hh * jitterAmt) * cellSize;\n\n // Per-stroke direction source — the AW.W4.1 strokeOrient switch:\n // uStrokeOrient==0 (flow) — the layer-provided hand-authored flowField.\n // uStrokeOrient==1 (tensor) — the structure-tensor minor eigenvector at the\n // cell center (the color field's edge-tangent),\n // so strokes hug the color zones (van Gogh).\n // coh carries the per-cell coherence A for the W4.3 energy grading (long\n // confident strokes where structure is coherent, stubby dabs in flat zones).\n vec2 f;\n float coh = 1.0;\n if (uStrokeOrient == 1) {\n // vec4 (the metal-finish widen) — .xy tangent + .z coherence; the .w gradient lane\n // is unused here. A vec3 local would be a type-mismatch WebKit rejects (L1c).\n vec4 tf = structureTensorField(center, t, safeDir(flow));\n f = safeDir(tf.xy);\n coh = tf.z;\n } else {\n f = safeDir(flow);\n }\n // Per-stroke deterministic local perturbation so alternate stroke layers stay\n // live. Tensor strokes get FAR less angular jitter (the field already orients them\n // — over-jittering the tensor strokes scatters their directions and the rendered\n // field reads isotropic, the §4.2 anisotropy floor). The flow-oriented (non-tensor)\n // strokes keep the full jitter (they have no per-cell orientation source).\n float jScale = (uStrokeOrient == 1) ? 0.15 : 1.0;\n float angJ = (hash21(cc + seed + 11.0) - 0.5) * 0.9 * jScale; // +/- 0.45/0.07 rad\n float curlScale = (uStrokeOrient == 1) ? 0.30 : 1.0;\n float localCurl = (fbm(center * (2.6 + seed * 0.11) + seed * 1.9) - 0.5) * 0.55 * uFlowCurl * curlScale;\n vec2 dir = rotateDir(f, angJ + localCurl);\n\n // AX.W13 — the SBR energy grade is a PROFILE field (energyGrade), not a buried\n // uMedium==5 branch. The van-Gogh profile passes energyGrade=1.0 (the full\n // Starry-Night cascade); oil/oil-pastel pass 0.0 (uniform length). The grade\n // modulates stroke length by local luminance (bright → long confident strokes,\n // dark → short dabs, the Kolmogorov/Batchelor congruence) AND by coherence\n // (coherent zones → long, flat → stubby), lerped in by energyGrade so a profile\n // can dial the cascade from off (0) to full (1).\n float energy = 1.0;\n if (energyGrade > 0.001) {\n float luma = clamp(dot(sampleBase(center, t), W_LUMA), 0.0, 1.0);\n float graded = mix(0.5, 1.5, luma) * mix(0.6, 1.0, coh);\n energy = mix(1.0, graded, energyGrade);\n }\n\n float lenV = cellSize * lenMul * (0.65 + 0.55 * hash21(cc + seed + 23.0)) * energy;\n float halfW = cellSize * halfWMul * (0.70 + 0.55 * hash21(cc + seed + 41.0));\n float bulge = (hash21(cc + seed + 53.0) - 0.5) * lenV * 0.35;\n\n vec2 a = center - dir * (lenV * 0.5);\n vec2 b = center + dir * (lenV * 0.5);\n\n vec3 colMid = brokenColorJitter(\n sampleBase(center, t),\n hash21(cc + seed + 89.0),\n hash21(cc * 2.3 + seed + 97.0),\n 1.0\n );\n\n StrokeHit h = curvedStroke(p, a, b, halfW, bulge, shapeType,\n 7.0, bristleAmp,\n hash21(cc + seed + 67.0), colMid);\n if (h.coverage > best.coverage) best = h;\n }\n }\n return best;\n}\n", Ee = "// ── Van-Gogh — DEDICATED atomic-dab body (W-AUR-VANGOGH-REBUILD) ─────────────\n// REBUILT FROM FIRST PRINCIPLES. The prior path routed van-Gogh through the shared\n// oil paintStrokeMedium cascade — four DENSE layers of LONG-THIN tensor-oriented\n// strokes (lenMul up to 5.2, widMul 0.22) hugging a continuous coherent field. Packed\n// that tight, thin strokes that follow continuous iso-bands MERGE into smooth flowing\n// filaments — the \"marbled flow-bands\" read (a spirograph/fingerprint, not impasto),\n// AND the per-cell 8-tap structure tensor × 4 layers × 9 cells = ~324 sampleBase/pixel\n// made it run at ~4fps. Van Gogh's sky is NOT continuous flow — it is DISCRETE, SHORT,\n// LOADED comma dabs, separated by visible ground, queued into swirl ROWS.\n//\n// The rebuild is a purpose-built two-layer DAB pass, disjoint from the oil cascade:\n// • orientation from the analytic SWIRL flowField (van-Gogh skies ARE swirl rows) +\n// per-cell angular jitter — NOT the expensive per-cell tensor (the lag source, and\n// the marble source: a jittered discrete dab can NOT merge into a continuous\n// filament the way a tensor-locked thin stroke does).\n// • SHORT comma dabs (len/wid ≈ 2.6 : 1 — a loaded crescent mark, not a 10:1\n// filament), so each reads as a SEPARABLE atom.\n// • SPARSE placement with visible inter-dab ground (density ≈ 0.42), over a darkened\n// swirl underpainting (the Starry-Night visible ground the bright dabs read over).\n// • a single sampleBase per cell + OKLCh broken-color jitter (pigment shimmer);\n// full-height impasto crown per dab (each catches its own raked glint).\n// This is ~2 layers × 9 cells × ~1 sampleBase ≈ 20/pixel (a ~16× cost cut, ~60fps), and\n// jittered short dabs CANNOT marble.\n\n// One atomic comma dab — a CLEAN analytic crescent SDF (NOT the oil curvedStroke).\n// The oil curvedStroke's spine-projection + rounded end-caps + fwidth-AA + bristle\n// edge each leave a thin sub-pixel skirt, and a field of those skirts over open ground\n// abuts into a faint network of SEAM LINES. A van-Gogh dab needs none of that\n// machinery — it is a short oriented capsule (rounded line segment) whose half-width\n// tapers along its length (the loaded-head → dragged-tail comma). The capsule SDF is\n// derivative-free (smoothstep AA against the analytic distance), so no fwidth skirt, no\n// best-of-9 seam. Returns a StrokeHit (coverage/color/alongT/edgeN) for paintOver.\n// cellSize — dab cell pitch (UV units); lenMul/widMul — dab aspect off the cell;\n// density — placement gate (sparse → visible gaps); seed — layer decorrelation.\nStrokeHit vangoghDab(vec2 p, float cellSize, float lenMul, float widMul,\n float density, float jitterAmt, float angJitter,\n vec2 flow, float t, float seed) {\n vec2 cell = floor(p / cellSize);\n StrokeHit best = noHit();\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n // Sparse density gate — leaves visible canvas ground between dabs (atomicity).\n if (hash21(cc * 1.7 + seed * 0.3) > density) continue;\n vec2 jit = (hash22(cc + seed) - 0.5) * jitterAmt;\n vec2 center = (cc + 0.5 + jit) * cellSize;\n\n // Orientation: the analytic swirl flow at the cell center + per-cell angular\n // jitter (so adjacent dabs FAN — the divisionist scatter that keeps the dabs from\n // locking into perfectly-concentric rows) + a flow-noise curl wobble.\n float ang = (hash21(cc + seed + 11.0) - 0.5) * angJitter;\n float curl = (fbm(center * 3.1 + seed * 1.9) - 0.5) * (0.18 + 0.3 * uFlowCurl);\n vec2 dir = rotateDir(safeDir(flow), ang + curl);\n\n // SHORT comma — len ≈ the cell pitch (a contained dab, not a tiling filament).\n float len = cellSize * lenMul * (0.82 + 0.30 * hash21(cc + seed + 23.0));\n float halfW = max(cellSize * widMul * (0.86 + 0.28 * hash21(cc + seed + 41.0)), 0.010);\n\n // Local dab frame: along = projection onto the dab axis (centred 0), perp = cross.\n vec2 rel = p - center;\n float along = dot(rel, dir);\n float perp = dot(rel, vec2(-dir.y, dir.x));\n // The comma TAPER: half-width peaks ~1/3 from the loaded head and drags to a fine\n // tail (the asymmetric crescent). u in [0,1] along the dab from head to tail.\n float halfLen = len * 0.5;\n float u = clamp((along + halfLen) / max(len, 1e-5), 0.0, 1.0);\n float head = smoothstep(0.0, 0.30, u); // rounded loaded head\n float tail = pow(clamp((1.0 - u) / 0.70, 0.0, 1.0), 1.3); // dragged thin tail\n float prof = clamp(min(head, tail) * 1.05, 0.0, 1.0);\n float wNow = halfW * prof;\n // A gentle comma CURVE — bow the spine sideways so the mark reads as a crescent.\n float bowAmt = (hash21(cc + seed + 53.0) - 0.5) * halfW * 1.4;\n float spine = bowAmt * sin(u * PI); // 0 at ends, max mid\n float d = abs(perp - spine);\n // Derivative-free AA: a fixed fraction of the dab width (no fwidth skirt). Coverage\n // is 1 in the dab body, smoothly 0 at the edge; outside the [head,tail] span wNow→0\n // so the dab self-terminates with no end-cap blob.\n float aa = max(halfW * 0.45, 0.002);\n float cov = (wNow > 1e-5) ? (1.0 - smoothstep(wNow - aa, wNow + aa, d)) : 0.0;\n if (cov < 0.004) continue;\n\n // One sampleBase per cell + OKLCh broken-color jitter (pigment patch shimmer). The\n // dab pigment is chroma-boosted (saturate3 1.22) AND value-lifted (×1.14) at the\n // SOURCE — the loaded brush mark deposits BRIGHTER + more saturated pigment than the\n // base field, so the loaded-mark pixels lift BOTH the §4.1 colourfulness AND the field\n // mean-luma. The higher mean raises the gap-fraction threshold (meanLum·0.6) so the\n // moderate-depth inter-dab ground falls below it (the gap-fraction atomicity read is\n // DECOUPLED from the ground floor depth — the ground can stay only moderately dark, so\n // the §4.3 slope is NOT over-steepened, while the bright dabs manufacture the gap). The\n // dark ground is untouched, so this lifts C + gap WITHOUT a band-conflicting deep ground.\n vec3 colMid = brokenColorJitter(\n saturate3(sampleBase(center, t), 1.22),\n hash21(cc + seed + 89.0),\n hash21(cc * 2.3 + seed + 97.0),\n 1.0\n );\n\n // edgeN: 1 at the spine, 0 at the edge (the impasto crown reads it).\n float edgeN = (wNow > 1e-5) ? clamp(1.0 - d / wNow, 0.0, 1.0) : 0.0;\n if (cov > best.coverage) {\n best = StrokeHit(cov, colMid, u, (wNow > 1e-5 ? (perp - spine) / wNow : 0.0), edgeN);\n }\n }\n }\n return best;\n}\n\nvec3 mediumVangogh(vec3 col, vec2 p, float t) {\n vec3 result = col;\n float height = 0.0;\n\n // The swirl flow basis (van-Gogh sky = swirl rows). Computed ONCE per pixel.\n vec2 flow = flowField(p, t);\n\n // Dab cell pitch in UV [0,1]. CHUNKY, SEPARABLE marks — the big layer is ~1/14 of\n // the frame so it reads as a distinct loaded brush dab (not a fine speckle, not a\n // screen-spanning filament). uStrokeScale (authored ~110) modulates the pitch\n // INVERSELY (higher = finer dabs) around the ~0.07 chunky default; clamped so the\n // dab never collapses to noise or balloons past one screen. uStrokeAnisotropy\n // stretches the comma aspect (longer, more directional marks).\n float pitch = clamp(7.0 / max(uStrokeScale, 20.0), 0.044, 0.12);\n // The dab LENGTH ≈ the cell pitch (lenAniso ~1.2-1.7) so a dab sits WITHIN its cell\n // and leaves visible ground around it — a longer dab (lenMul > 2) spans neighbour\n // cells and tiles the field into continuous coverage (no gaps, the gap-fraction floor\n // miss + the marble). Narrow width keeps it a directional crescent, not a wide shard.\n // lenAniso/widAniso — the comma aspect. The W-AUR-VANGOGH-CONFIG reconcile lengthened\n // the comma (1.25-1.75 → 1.5-2.0) and THINNED it (0.40-0.30 → 0.32-0.24): the rebuilt\n // dabs measured §4.2 structure-tensor anisotropy A=0.699, BELOW the reference band\n // floor 0.732 (the dabs read slightly too short-fat → locally isotropic, dragging mean\n // A down). A LONGER, THINNER comma hugs the swirl-flow tangent more strongly so the\n // rendered field reads coherent the way the tensor metric measures it (A lift), while\n // the thinner width LEAVES MORE inter-dab ground (the gap-fraction atomicity read is\n // preserved, not closed). The length stays ≈ the cell pitch so each dab remains a\n // CONTAINED separable mark, not a tiling filament.\n float lenAniso = mix(2.1, 2.7, uStrokeAnisotropy); // comma length / cell pitch (a contained dab)\n float widAniso = mix(0.23, 0.16, uStrokeAnisotropy); // comma width / cell pitch (a thin loaded crescent)\n\n // Layer 1 — the big swirl-row dabs: the loaded gestural marks, SPARSE so the dark\n // underpainting shows BETWEEN them (the atomicity read — visible inter-dab ground).\n // hardness 0.74 — the loaded brush deposits with a SOFT shoulder, so adjacent dabs\n // feather instead of razor-clipping into hard polygon shards. jitterAmt 0.85 fully\n // scatters the cell centres so the placement GRID never reads as a periodic seam.\n // angJitter EASED 0.55 → 0.38 (the reconcile A lift): a tighter angular spread keeps\n // adjacent dabs more flow-aligned (coherent) while the per-cell curl wobble still\n // breaks the perfectly-concentric-rows read (the dabs FAN gently, not lock to a grid).\n // angJitter eased 0.38 → 0.26 + hardness 0.74 → 0.84 (the §4.2 A lift): a tighter angular\n // spread keeps adjacent dabs strongly flow-aligned (the structure-tensor coherence) and a\n // sharper edge gives each dab a STRONGER directional gradient (a soft dab's faint edge\n // reads weakly anisotropic). The per-cell curl wobble still breaks the perfectly-\n // concentric-rows read, so the dabs FAN gently rather than locking to a grid.\n StrokeHit dBig = vangoghDab(p, pitch, lenAniso, widAniso,\n 0.30, 0.50, 0.08, flow, t, 1.3);\n // streakFreq raised 14 → 34 (the §4.3 slope fix): a high-frequency streak runs ALONG the\n // dab spine, injecting fine-scale luma energy that flattens the too-steep β WITHOUT\n // adding new isotropic marks (the along-spine streak stays directional, so the §4.2\n // anisotropy the long-thin dab carries is preserved — the band-conflict resolution).\n paintOver(result, height, dBig, 48.0, 0.34,\n uImpasto * 0.75 * uStrokeAmount, 0.84, 1.3, 1.0 /* full crown */);\n\n // Layer 2 — two-thirds-pitch accent dabs (the divisionist sparkle between the big\n // marks — denser placement but still contained dabs, so dark canvas survives between).\n // angJitter eased 0.5 → 0.34 (same A-lift reasoning — the accent dabs queue along the\n // flow rather than scattering isotropically); hardness 0.78 → 0.86 sharpens the gradient.\n StrokeHit dSml = vangoghDab(p + vec2(-5.1, 8.4), pitch * 0.62, lenAniso * 0.95,\n widAniso * 0.95, 0.36, 0.60, 0.20, flow, t, 4.1);\n paintOver(result, height, dSml, 42.0, 0.22,\n uImpasto * 0.55 * uStrokeAmount, 0.86, 4.1, 1.0);\n\n // Layer 3 — the FINE divisionist flecks (W-AUR-VANGOGH-CONFIG reconcile). The two-layer\n // field read as a few BIG cobalt dabs over a smooth empty teal ground (the side-by-side\n // against starry-night-crop.png: the painting is DENSE small varied flecks, not sparse\n // large marks). The flecks are LONG·THIN directional commas hugging the swirl tangent\n // (the A-preserving fleck — short fat flecks read isotropic and drag §4.2 down). Sparse\n // density 0.30 keeps the inter-fleck ground open (the gap-fraction atomicity read).\n // The flecks are FINE (≈0.42× pitch) directional commas, tightly flow-aligned (angJitter\n // 0.22) and SPARSE (0.30) — the divisionist sparkle between the loaded marks that keeps\n // the inter-dab ground open (atomicity) while adding the small-mark mid-band energy. Thin\n // + flow-aligned so the §4.2 anisotropy the long dabs carry survives the added small marks.\n StrokeHit dFleck = vangoghDab(p + vec2(8.7, -3.2), pitch * 0.42, lenAniso * 1.0,\n widAniso * 0.68, 0.30, 0.85, 0.22, flow, t, 7.7);\n paintOver(result, height, dFleck, 30.0, 0.16,\n uImpasto * 0.40 * uStrokeAmount, 0.80, 7.7, 1.0);\n\n // ── Flow-aligned GROUND swirl-GROOVING (W-AUR-VANGOGH-CONFIG reconcile) ──\n // The bare ground between the dabs was a SMOOTH low-frequency teal gradient — the §4.3\n // power-spectrum slope ran too STEEP (β≈−2.0: too much low-frequency nuclei-field energy\n // vs the high band). Van Gogh's actual sky has the swirl BRUSHWORK everywhere, so groove\n // the OPEN GROUND with a flow-aligned ripple. It is SUBTRACTIVE-biased (it only darkens,\n // never brightens the ground), so it DEEPENS the inter-dab valleys (MORE gap-fraction —\n // atomicity preserved/strengthened) while injecting the across-flow mid-band luma energy\n // that flattens β. Gated to the open ground (low height) via the (1-height) gate so it\n // never lifts/erodes the loaded dab bodies (the dab read is untouched). Across the swirl\n // FLOW axis so the grooves run WITH the sky's brushwork direction.\n // The ground swirl-combing — a FIXED-global-angle directional grain on the open ground.\n // A flow-FOLLOWING comb varies its direction with the swirl across the readback window,\n // so its blurred structure tensor averages toward isotropic (no §4.2 lift). A comb at one\n // GLOBAL angle (van-Gogh's dominant diagonal stroke grain) is COHERENT over the whole\n // window ⇒ every open-ground pixel reads the SAME direction (the §4.2 mean-anisotropy lift\n // the swirl comb could not reach). Two resolved bands (≈22 + ≈44 across the global axis ⇒\n // ~21px + ~11px at 464px, both in the §4.3 fit window) also feed the β flatten. Gated to\n // the open ground (low height) so the loaded dabs are untouched; SUBTRACTIVE-biased so the\n // inter-dab valleys deepen (gap-fraction preserved, not filled).\n vec2 gdir = normalize(vec2(0.82, 0.57)); // the dominant global stroke grain axis\n float gacross = dot(p, vec2(-gdir.y, gdir.x));\n float groundGate = 1.0 - smoothstep(0.05, 0.28, height);\n float groove = sin(gacross * 22.0) + 0.5 * sin(gacross * 44.0 + 1.3);\n result *= 1.0 - 0.16 * groundGate * (0.5 - 0.5 * groove);\n\n // Canvas tooth (fine linen weave — base relief the raking light catches).\n float tooth1 = vnoise(p * 220.0);\n float tooth2 = vnoise(p * 220.0 * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * 0.06 * uCanvasGrain;\n\n // Relight the accumulated impasto height (raking light catches each dab's crown —\n // the visible impasto ridge that makes a dab read as a RAISED loaded mark).\n float canvasBase = tooth * 0.06 * 0.5;\n result = relightImpasto(result, height, canvasBase);\n\n // The visible Starry-Night GROUND — the open canvas between dabs (low accumulated\n // height) darkens toward the deep underpainting, so the bright impasto dabs read as\n // SEPARABLE atomic marks over a darker field (the dab↔ground contrast the user's\n // \"reads as strokes\" bar needs, AND the painterly-statistics gap-fraction floor — the\n // dark inter-dab ground must register as a measurable fraction of the field). 0.30\n // floor over a WIDER smoothstep (0..0.40): deep enough that the open gaps read as the\n // dark Starry-Night sky between marks (clearing the 4% gap floor) while the gradual\n // ramp keeps the gaps reading as flowing underpainting, not the hard black ink LINES a\n // narrow near-black step (0.26 / 0.0..0.32) produced.\n // The smoothstep FLOOR starts at 0.10 (not 0): a dab's faint AA coverage TAIL deposits\n // a sliver of height, and where many tails align along the swirl flow those slivers\n // accumulate into a faint continuous height ridge that read as thin curved LINES in the\n // ground. Holding the ground full-dark until a real dab CORE (height > 0.10) lifts out\n // keeps the open field clean — only the loaded dab bodies emerge, the tails stay ground.\n result *= mix(0.115, 1.0, smoothstep(0.085, 0.31, height));\n\n // The van-Gogh chroma — a light OKLCh punch. EASED to 0.92 (off 1.04) in the\n // W-AUR-VANGOGH-CONFIG reconcile: the rebuilt discrete-dab register's per-dab OKLCh\n // broken-color jitter (the ±16° hue + ±chroma swing per cell) lifts the field's\n // colourfulness on its own, so the prior 1.04 punch on TOP overshot the §4.1\n // reference band (rendered C=100.5 vs the painting's measured 70.67 ± 15/+25 →\n // ceiling 95.67 — neon, not Starry Night's rich-but-not-garish blue/gold). The 0.92\n // demote pulls the rendered colourfulness back IN-band while the broken-color jitter\n // keeps the per-dab pigment variation (the discrete-stroke read is untouched — this\n // is a chroma scale, not a dab-geometry change). Band-anchored on the real painting\n // (proof:aurora-arresting), NOT a widened band.\n result = saturate3(result, 1.0);\n return result;\n}\n\n", De = "StrokeProfile profileFor(int medium, int mode) {\n // The oil baseline (mode 0) — balanced modern-abstract/palette-knife hybrid.\n StrokeProfile prof = StrokeProfile(\n 0, // shapeType — tapered\n 0.25, // bristleAmp\n 9.0, // streakFreq\n 0.09, // streakAmp\n 0.9, // impastoAmp\n 0.80, // hardness\n 340.0, // toothScale — W-AUR-VANGOGH-CONFIG reconcile: FINER 240 → 340. Oil's §4.3 β sat\n // right AT the −1.45 band ceiling (±0.02 frame variance straddling\n // it, surfaced once the vangogh bands went green and the spec reached\n // the oil β assert). MEASURED on the live GPU: ADDING fine high-\n // frequency tooth energy steepens oil's power-spectrum slope (more\n // negative — coarsening it the other way pushed β SHALLOWER to\n // −1.33). A finer, stronger tooth moves β to a safe distance inside\n // the −5/3 band [−1.85, −1.45]. Oil-only (oil-pastel sets 280).\n 0.14, // toothAmp — raised 0.09 → 0.14 (same oil-β steepen: more fine-grain high band).\n 1.03, // pigmentSat\n 0.65, // densityBig\n 0.78, // densityMed\n 0.90, // densitySml\n 0.0, // energyGrade — oil is uniform-length (no Starry-Night cascade)\n 0.4, // impastoFloor — oil's 0.4+0.6·edgeN crown falloff\n 0.92, // densityFill — oil fills DENSELY with the directional low-relief fill so the\n // flat-colour regions between the macro strokes are TEXTURED with\n // flow-aligned fine strokes (the §4.2 anisotropy needs every pixel\n // to carry directional gradient; a flat untextured region reads as\n // an isotropic/degenerate tensor and drags mean A down). The fill\n // is now directional + low-streak, so dense coverage textures\n // WITHOUT the prior round-dab speckle.\n 1.0 // groundFloor — oil keeps the full base (no ground darken; fills everywhere)\n );\n\n // W-AUR-VANGOGH-REBUILD — the van-Gogh medium NO LONGER routes through this oil\n // StrokeProfile cascade. It is a DEDICATED atomic-dab body (mediumVangogh / vangoghDab\n // below) — the prior profile-driven cascade (four dense layers of long-thin\n // tensor-oriented strokes) merged into the \"marbled flow-bands\" read and ran at ~4fps.\n // profileFor now serves ONLY the oil + oil-pastel stroke mediums; the MEDIUM_VANGOGH\n // case is removed (no caller). MEDIUM_VANGOGH stays #define'd (the main() dispatch +\n // isPainterlyStroke still key off uMedium==5).\n\n // ── Oil-pastel — broad smeared directional strokes deposited via the brush engine\n // (slice 8 F1). Creamy soft edges (low hardness), heavy build-up, and a chroma punch.\n // Distinct from the dry-crayon tooth-multiply (mediumCrayon): oil-pastel LAYS strokes.\n if (medium == MEDIUM_OILPASTEL) {\n prof.shapeType = 0; // tapered directional smear — a round dab reads locally\n // isotropic (steep β / low A); the creamy oil-pastel mark\n // is a broad DIRECTIONAL smear, which carries the §4.2\n // coherence + adds the directional mid-band the §4.3 slope\n // needs (the round-dab profile rolled β off to −2.8)\n prof.bristleAmp = 0.10; // smooth creamy edges (waxy pastel, not bristle)\n prof.streakFreq = 18.0; // finer internal smear — adds the mid/high-band the smooth\n // round-dab profile lacked (the β-too-steep fix); the\n // directional along-spine streak flattens the §4.3 slope\n // without crossing the strokes\n prof.streakAmp = 0.20;\n prof.impastoAmp = 0.7; // build-up, but flatter than oil impasto\n prof.hardness = 0.42; // CREAMY — soft compositing, strokes blend on overlap\n prof.toothScale = 280.0;\n prof.toothAmp = 0.12; // the pastel tooth reads stronger\n prof.pigmentSat = 0.80; // the waxy chroma — eased off 1.16 so the rendered C clears\n // the §4.1 band ceiling with margin while the K-M subtractive\n // overlap path (paintOverOklab) keeps the chroma OFF the grey floor\n prof.hardness = 0.58; // creamy but the strokes still REGISTER — a too-soft blend\n // (0.42) smeared the marks into the smooth colour zones and\n // the field rolled off too steep (β≈−2.9, a near-gradient);\n // a crisper deposit keeps the mid/high-band the −5/3 slope needs\n prof.densityBig = 0.62; // broad directional coverage — but NOT over-dense: too many\n // overlapping creamy strokes point in slightly-varied\n // directions and the local §4.2 coherence DROPS (over-dense\n // measured A=0.57 < the 0.62-density A=0.63). The mid-density\n // flow-aligned deposit is the coherence sweet spot.\n prof.densityMed = 0.78;\n prof.densitySml = 0.90;\n prof.energyGrade = 0.80; // a strong Starry-Night length cascade — varies stroke length\n // by luma/coherence so the dab-size energy spreads across\n // scales (the −5/3 cascade the uniform-length 0.0 grade lacked,\n // the §4.3 β-too-steep fix). Not the full van-Gogh 1.0 (oil-\n // pastel keeps its creamy broad character).\n prof.impastoFloor = 0.55; // a softer crown than van-Gogh's full-height glint\n return prof;\n }\n\n // ── Oil sub-modes (mode dispatch on the oil medium).\n if (mode == 1) { // palette knife — razor edges, heavy impasto\n prof.shapeType = 3; // flat, even\n prof.bristleAmp = 0.12;\n prof.streakFreq = 4.0; prof.streakAmp = 0.05;\n prof.impastoAmp = 1.6;\n prof.hardness = 0.95;\n prof.toothAmp = 0.04;\n prof.densityBig = 0.80; prof.densityMed = 0.88; prof.densitySml = 0.70;\n } else if (mode == 3) { // thick brushwork — heavy bristle brush\n prof.shapeType = 0; // tapered\n prof.bristleAmp = 0.32;\n prof.streakFreq = 14.0; prof.streakAmp = 0.14;\n prof.impastoAmp = 1.2;\n prof.hardness = 0.85;\n prof.toothAmp = 0.07;\n }\n return prof;\n}\n", Oe = "\n// ── Gradient pack (BG.W-AUR-METAL-FINISH) ──────────────────────────────────\n// structureTensorField re-plumbs its already-computed luma gradient (Gx,Gy) out through\n// the vec4 return's .w lane so the metal medium pays ZERO extra taps (a second Sobel is\n// 8 sampleBase() taps). 12 bits per bounded component ([-4,4]) into one f32, exact under\n// highp; the .xy/.z crayon/kuwahara callers are transparent to the widen.\nfloat packGrad(float gx, float gy) {\n float qx = floor(clamp(gx * 0.125 + 0.5, 0.0, 1.0) * 4095.0);\n float qy = floor(clamp(gy * 0.125 + 0.5, 0.0, 1.0) * 4095.0);\n return qx + qy * 4096.0;\n}\nvec2 unpackGrad(float packed) {\n float qy = floor(packed * (1.0 / 4096.0));\n float qx = packed - qy * 4096.0;\n return (vec2(qx, qy) * (1.0 / 4095.0) - 0.5) * 8.0;\n}\n", ke = "#define MEDIUM_METAL 8\n#define MEDIUM_METALGRAD 9\n\n// ── Metal — the two-term anisotropic BRDF over the field's own relief (uMedium==8/9,\n// BG.W-AUR-METAL-FINISH) ─────────────────────────────────────────────────────────\n// Metal is a MUTUALLY-EXCLUSIVE medium (NOT a finish axis) — the field re-lights as\n// warm folded metal: the luma HEIGHT field (the packed structure-tensor gradient →\n// the surface normal) catches an anisotropic Blinn streak (brushed metal, the streak\n// runs ALONG the coherent edge-tangent) plus a sharp crest, both raked by a cursor-\n// synthesized light that reads on BOTH backends (uCursor is in-struct — a phantom\n// uLightDir read would be flat on the WGSL primary). The catch is the ACHROMATIC-WARM\n// anchor (AURORA_CATCH_LIGHT_ANCHOR ≈ [1.0,0.97,0.90], never a cold chrome-blue). The\n// body keeps the field HUE (technicolor valley base); the height rides the relief.\n// ZERO new taps — structureTensorField ALREADY computed the gradient (unpacked from .w).\nconst vec3 METAL_CATCH_WARM = vec3(1.0, 0.97, 0.90); // AURORA_CATCH_LIGHT_ANCHOR\nconst float METAL_HEIGHT_SCALE = 2.2; // gradient → normal tilt (× uMetalHeightScale)\nconst float METAL_LIGHT_Z = 0.55; // synth-light z (toward viewer)\nconst vec2 METAL_IDLE_RAKE = vec2(0.32, 0.34); // upper-right idle rake bias\nconst float METAL_SHININESS_ANISO = 6.0; // anisotropic streak exponent (brushed)\nconst float METAL_SHININESS_CREST = 40.0; // crest specular exponent (sharp)\nconst float METAL_COHERENCE_FLOOR = 0.22; // streak gates on edge coherence A\nconst float METAL_BODY_FLOOR = 0.42; // diffuse valley floor (dark side reads)\nconst float METAL_GRADIENT_FLATTEN = 0.55; // metal-gradient pre-flatten toward luma\nconst float METAL_SPARKLE_DENSITY = 240.0; // metal-gradient per-cell flake grid\nconst float METAL_SPARKLE_THRESH = 0.72; // only some cells flake\nconst float METAL_SPARKLE_AMP = 0.6; // flake glint amplitude\n\n// The shared metal BRDF core — takes the ALREADY-computed structureTensorField vec4 so\n// the caller pays ZERO extra taps (metal-gradient reuses the same stf). WebKit-fenced:\n// every pow base is clamped to [0,1], the exponents are compile-time constants.\nvec3 metalShade(vec3 baseCol, vec2 p, vec4 stf) {\n vec2 tangent = stf.xy;\n float A = stf.z;\n vec2 grad = unpackGrad(stf.w);\n // Height-field normal: N = normalize(vec3(grad * scale, 1)). The luma relief tilts\n // the surface; the flat field reads near vec3(0,0,1) (facing the viewer).\n vec3 N = normalize(vec3(grad * (METAL_HEIGHT_SCALE * uMetalHeightScale), 1.0));\n vec3 V = vec3(0.0, 0.0, 1.0);\n // Cursor-synthesized rake light (in-struct on BOTH backends — never uLightDir). The\n // idle bias keeps a still field lit from the upper-right; a moving cursor rakes it.\n vec3 L = normalize(vec3((uCursor - p) + METAL_IDLE_RAKE, METAL_LIGHT_Z));\n vec3 H = normalize(L + V);\n // Anisotropic streak — brushed metal: the highlight runs ALONG the edge-tangent, so\n // it is a function of the H-component PERPENDICULAR to the tangent (sinTH).\n float tH = dot(normalize(tangent), H.xy);\n float sinTH = sqrt(clamp(1.0 - tH * tH, 0.0, 1.0));\n float streak = pow(clamp(sinTH, 0.0, 1.0), METAL_SHININESS_ANISO);\n float crest = pow(clamp(dot(N, H), 0.0, 1.0), METAL_SHININESS_CREST);\n float spec = streak * crest;\n // Coherence gate — the anisotropic streak reads only where the field has a real edge\n // (a flat zone has no brush direction; A≈0 → no streak).\n spec *= smoothstep(0.0, METAL_COHERENCE_FLOOR, A);\n // Technicolor valley base — the body keeps the FIELD hue; the diffuse term rides the\n // height relief so the ridges brighten and the pits fall toward the floor.\n float diff = clamp(dot(N, L), 0.0, 1.0);\n vec3 body = baseCol * (METAL_BODY_FLOOR + (1.0 - METAL_BODY_FLOOR) * diff);\n // The catch is the achromatic-warm anchor, scaled by the polish knob.\n return body + METAL_CATCH_WARM * (spec * clamp(uMetalPolish, 0.0, 4.0));\n}\n\nvec3 mediumMetal(vec3 col, vec2 p, float t) {\n vec4 stf = structureTensorField(p, t, flowField(p, t));\n return metalShade(col, p, stf);\n}\n\n// metal-gradient (uMedium==9) — the SAME BRDF over a pre-flattened/gradient base + a\n// twinkle-in-place flake sparkle (position FIXED per cell, PHASE-animated, gated on\n// facing — flake glint, never a boil).\nvec3 mediumMetalGradient(vec3 col, vec2 p, float t) {\n vec4 stf = structureTensorField(p, t, flowField(p, t));\n // Pre-flatten toward luma (the smoother gradient-metallic base) — same BRDF, calmer body.\n // NB: flat is a GLSL reserved interpolation qualifier — WebKit's compiler REJECTS it as an\n // identifier (ANGLE tolerates it); named flatCol so the WebKit aurora shader compiles (L1a).\n vec3 flatCol = mix(col, vec3(dot(col, W_LUMA)), METAL_GRADIENT_FLATTEN);\n vec3 metal = metalShade(flatCol, p, stf);\n // The height-field normal facing (recomputed from the same packed gradient — ZERO taps).\n vec2 grad = unpackGrad(stf.w);\n float facing = normalize(vec3(grad * (METAL_HEIGHT_SCALE * uMetalHeightScale), 1.0)).z;\n // Twinkle-in-place: a fixed per-cell flake seed, phase-animated glint gated on facing.\n vec2 cell = floor(p * METAL_SPARKLE_DENSITY);\n float seed = hash21(cell);\n float tw = pow(clamp(sin(t + seed * 6.2831853), 0.0, 1.0), 40.0);\n float flakeGate = step(METAL_SPARKLE_THRESH, seed);\n metal += METAL_CATCH_WARM * (tw * flakeGate * clamp(facing, 0.0, 1.0) * METAL_SPARKLE_AMP);\n return metal;\n}\n", Ae = "// ── Medium overlays ───────────────────────────────────────────────────────\n\n// A quick re-computation of base color for edge-mask sampling\nvec3 sampleBase(vec2 p, float t) {\n vec2 pw = domainWarp(p, t);\n float id; float vm;\n nucleiField(pw, t, id, vm);\n vec3 c = samplePalette(id);\n c *= 1.0 + uValueVariance * vm;\n return c;\n}\n" + Oe + "\n// ── Structure-tensor / edge-tangent-flow (AW.W4.1) ─────────────────────────\n// The keystone of the painterly engine: derive stroke orientation from the COLOR\n// FIELD's OWN structure, not a hand-authored flow pattern. A 3x3 Sobel over\n// luma(sampleBase) yields the gradient (Gx,Gy); the 2x2 structure tensor\n// J = [[Gx², GxGy],[GxGy, Gy²]] eigen-decomposes closed-form into the MINOR\n// eigenvector (the edge-TANGENT — least color change, the stroke direction) + the\n// coherence A = (λ1-λ2)/(λ1+λ2); flat zones relax toward fallbackDir by (1-A). The\n// single-pass small-tap WebGL2 form. Returns vec4(tangent.xy, A, packGrad(Gx,Gy)) —\n// the .w lane carries the metal medium's gradient (BG.W-AUR-METAL-FINISH; the .xy/.z\n// callers are byte-unchanged).\nvec4 structureTensorField(vec2 p, float t, vec2 fallbackDir) {\n float e = 0.0035; // small-tap neighborhood (edge-mask scale)\n // 3x3 luma samples.\n float l00 = dot(sampleBase(p + vec2(-e, -e), t), W_LUMA);\n float l10 = dot(sampleBase(p + vec2( 0.0, -e), t), W_LUMA);\n float l20 = dot(sampleBase(p + vec2( e, -e), t), W_LUMA);\n float l01 = dot(sampleBase(p + vec2(-e, 0.0), t), W_LUMA);\n float l21 = dot(sampleBase(p + vec2( e, 0.0), t), W_LUMA);\n float l02 = dot(sampleBase(p + vec2(-e, e), t), W_LUMA);\n float l12 = dot(sampleBase(p + vec2( 0.0, e), t), W_LUMA);\n float l22 = dot(sampleBase(p + vec2( e, e), t), W_LUMA);\n // Sobel derivatives.\n float Gx = (l20 + 2.0 * l21 + l22) - (l00 + 2.0 * l01 + l02);\n float Gy = (l02 + 2.0 * l12 + l22) - (l00 + 2.0 * l10 + l20);\n // Structure tensor components.\n float Jxx = Gx * Gx;\n float Jyy = Gy * Gy;\n float Jxy = Gx * Gy;\n // Closed-form 2x2 eigenvalues: λ = 0.5(tr ± sqrt((Jxx-Jyy)² + 4Jxy²)).\n float tr = Jxx + Jyy;\n float disc = sqrt(max((Jxx - Jyy) * (Jxx - Jyy) + 4.0 * Jxy * Jxy, 0.0));\n float lambdaMaj = 0.5 * (tr + disc);\n float lambdaMin = 0.5 * (tr - disc);\n // The MAJOR eigenvector points along the gradient (the edge NORMAL); the MINOR\n // is its perpendicular — the edge TANGENT (the stroke direction). The principal\n // angle is θ = 0.5·atan2(2·Jxy, Jxx-Jyy); this form is robust at Jxy≈0 (where the\n // (λ-Jyy, Jxy) eigenvector formula collapses to a zero vector). The tangent is at\n // θ + 90°. Guard the fully-isotropic (no-gradient) degenerate case → fallback dir.\n vec2 tangent;\n if (disc < 1e-7) {\n tangent = fallbackDir;\n } else {\n float theta = 0.5 * atan(2.0 * Jxy, Jxx - Jyy); // major (gradient) angle\n tangent = vec2(-sin(theta), cos(theta)); // perpendicular = tangent\n }\n // Coherence A in [0,1] — high where one eigenvalue dominates (a strong edge),\n // ~0 in flat zones. Relax toward the smooth fallback by (1 - A).\n float A = (lambdaMaj - lambdaMin) / (lambdaMaj + lambdaMin + 1e-6);\n A = clamp(A, 0.0, 1.0);\n // Coherence-weighted blend: full tensor in coherent zones, smooth in flat ones.\n // Resolve the 180° sign ambiguity toward the fallback so the blend is continuous.\n if (dot(tangent, fallbackDir) < 0.0) tangent = -tangent;\n // The blend weight is a low-power curve (A^0.35) so even MODERATE-coherence zones queue\n // strongly along the tangent (a flowing van-Gogh band reads coherent the way the\n // structure-tensor metric measures it; a linear A under-orients the mid-band and the\n // rendered field reads too isotropic — the §4.2 anisotropy lift). The flat-zone (A≈0)\n // relax to the fallback survives (0^0.35=0), so tensor noise never reads as jitter.\n float blendW = pow(A, 0.28);\n vec2 dir = normalize(mix(fallbackDir, tangent, blendW) + 1e-6);\n return vec4(dir, A, packGrad(Gx, Gy));\n}\n\nvec3 mediumWatercolor(vec3 col, vec2 p, float t) {\n // Wet-edge cauliflowers via luma-gradient magnitude\n float eps = 0.004;\n vec3 cx1 = sampleBase(p + vec2(eps, 0.0), t);\n vec3 cx2 = sampleBase(p - vec2(eps, 0.0), t);\n vec3 cy1 = sampleBase(p + vec2(0.0, eps), t);\n vec3 cy2 = sampleBase(p - vec2(0.0, eps), t);\n float gx = dot(cx1 - cx2, W_LUMA);\n float gy = dot(cy1 - cy2, W_LUMA);\n float edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n float mask = smoothstep(0.0, 2.5, edge);\n col *= mix(1.0, 0.78, mask * uWetEdge);\n\n // Granulation — pigment settles in paper tooth\n float paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n float pigLoad = 1.0 - dot(col, W_LUMA);\n col *= 1.0 - uGranulation * pigLoad * (paper - 0.5);\n\n // Wash banding — faint horizontal wet gradient\n float band = fbm(vec2(p.x * 1.5, p.y * 0.4));\n col *= 1.0 + 0.04 * (band - 0.5);\n return col;\n}\n\nvec3 mediumPastel(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n vec2 perp = vec2(-flow.y, flow.x);\n float along = dot(p, flow) * uStrokeScale;\n float across = dot(p, perp) * uStrokeScale;\n across += 0.03 * (vnoise(p * 260.0) - 0.5);\n\n float aniso = mix(1.0, 0.18, uStrokeAnisotropy);\n float stroke = fbm(vec2(along * aniso, across));\n col *= mix(1.0, 0.82 + 0.32 * stroke, uStrokeAmount);\n\n // Pastel tooth — tiny high-frequency grain\n float tooth = vnoise(p * 800.0);\n col *= 1.0 - 0.08 * uStrokeAmount * (tooth - 0.5);\n return col;\n}\n", je = "// ── Medium id constants (mirror uniformBridge.ts MEDIUM_ID — the uMedium ladder) ──\n// The StrokeProfile selector (AX.W12 + AX.W13) dispatches on the medium id: oil,\n// van-Gogh, and oil-pastel each AUTHOR a first-class profile in profileFor.\n#define MEDIUM_OIL 3\n#define MEDIUM_CRAYON 4\n#define MEDIUM_VANGOGH 5\n#define MEDIUM_OILPASTEL 6\n\n// ── Crayon — DRY wax pigment on paper tooth (first-class medium, uMedium==4) ──\n// Crayon is not strokes. It's dry pigment crumbs dragged across paper tooth — the\n// DRY tooth-multiply model: anisotropic tooth noise multiplied into the base, a hard\n// SCUMBLE that lets the lower color show through the broken upper layer, and stable\n// OKLCh broken-color pigment patches. NO sheen, NO burnish film — that waxy gloss is\n// the OIL-PASTEL deposition's signature (mediumOilPastel), distinct from dry crayon\n// (AX.W13 slice 8 F1 — the split). Crayon shares the SUBSTRATE (the structure-tensor\n// orientation, the tooth noise, OKLCh brokenColorJitter), not the dispatch body.\nvec3 mediumCrayon(vec3 col, vec2 p, float t) {\n // Orient the deposition along the structure-tensor edge-tangent (the bridge forces\n // tensor for crayon, so read the tensor field directly).\n vec2 flow = structureTensorField(p, t, flowField(p, t)).xy;\n float ang = atan(flow.y, flow.x);\n float ca = cos(-ang), sa = sin(-ang);\n vec2 pr = vec2(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n float aniso = mix(0.45, 0.95, uStrokeAnisotropy);\n float scale = max(uStrokeScale * 1.6, 180.0);\n\n // ── Paper TOOTH height field — the relief the dry pigment rides. Anisotropic\n // (squished along flow, coarse cross-flow). Peaks = ridges, valleys = paper pits.\n float t1 = vnoise(vec2(pr.x * scale * aniso, pr.y * scale));\n float t2 = vnoise(vec2(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + 11.0);\n float t3 = vnoise(vec2(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + 23.0);\n float tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n // ── DRY TOOTH-MULTIPLY — the crayon multiplies the tooth relief into the base\n // color (dark in the pits, bright on the ridges). The pressure (uStrokeAmount) sets\n // how hard the multiply bites. This is the DRY model — no pigment build-up, no sheen.\n float pressure = clamp(uStrokeAmount, 0.0, 1.0);\n float bite = mix(0.18, 0.46, pressure); // multiply strength\n vec3 result = col * (1.0 - bite * (0.5 - tooth));\n\n // ── HARD SCUMBLE — a broken upper layer that lets the lower color show through the\n // gaps. A coarse mask gates a HARD (high-contrast) dragged pass so the dry crayon\n // reads as broken pigment crumbs, not a smooth wash. Drag follows the tensor flow.\n float scumbleMask = smoothstep(0.42, 0.78, vnoise(vec2(pr.x * scale * 0.55, pr.y * scale * 0.22) + 7.0));\n float scumbleCoverage = scumbleMask * pressure * 0.7; // hard broken upper layer\n vec3 paper = col * mix(0.80, 0.94, pressure); // the paper showing through\n result = mix(paper, result, scumbleCoverage);\n\n // ── Broken-color pigment: stable wax/pigment patches in OKLCh (W5), not flicker.\n vec2 pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n float pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + 19.0));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + 17.0),\n hash21(pigmentCell * 2.1 + 31.0),\n 0.45 + 0.55 * pigmentMask\n );\n\n // Crayon is saturation-amplified (the wax chroma punch — but DRY, no sheen).\n result = saturate3(result, 1.08);\n\n return result;\n}\n\n// ── StrokeProfile — the oil-stroke parameter vector (AX.W12, slice 8 F6) ──────\n// The SBR \"stroke = parameter vector\" canon (facet 11): the per-mode if-ladder knobs\n// become struct FIELDS — logic-as-DATA, not an imperative branch. profileFor(medium,\n// mode) populates the profile for a (medium, mode) pair; paintStrokeLayers(profile) runs\n// the four-layer bestOil/paintOver cascade off it. A new medium (the W13 van-Gogh /\n// oil-pastel profiles) AUTHORS a profile entry — it never edits a monolith.\nstruct StrokeProfile {\n int shapeType; // 0 tapered, 1 load-drag, 2 dab, 3 even, 4 comma/crescent\n float bristleAmp; // edge raggedness 0..0.5\n float streakFreq; // internal-streak spatial frequency\n float streakAmp; // internal-streak amplitude\n float impastoAmp; // paint-thickness contribution (the relight reads the height)\n float hardness; // edge-compositing crispness 0..1\n float toothScale; // canvas-tooth (linen weave) spatial frequency\n float toothAmp; // canvas-tooth amplitude\n float pigmentSat; // pigment saturation boost (OKLCh chroma scale)\n float densityBig; // layer-1 (big gestural) placement density gate\n float densityMed; // layer-2 (medium body) placement density gate\n float densitySml; // layer-3 (small dabs) placement density gate\n float energyGrade; // AX.W13 — SBR energy-grade magnitude (0 off; 1 the van-Gogh\n // Starry-Night length cascade). The energy grade is a PROFILE\n // field, NOT a buried uMedium==5 bestOil branch (slice 8 F0).\n float impastoFloor;// AX.W13 — the per-stroke height-crown floor (0.4 oil falloff;\n // 1.0 van-Gogh FULL-height crown so each dab catches its glint).\n float densityFill; // AX.W13 — layer-4 fill-dab density. Oil/oil-pastel fill the\n // bald spots (0.95, full coverage); van-Gogh sets it LOW so the\n // sparse atomic dabs keep their visible inter-stroke ground gaps\n // (the atomicity read — a 0.95 fill would close every gap to a smear).\n float groundFloor; // AX.W13 — the bare-ground darken floor. The gaps between strokes\n // (low accumulated height) multiply toward this floor, so van-Gogh's\n // sparse atomic dabs read over a visibly darker underpainting (the\n // Starry-Night visible ground). Oil/oil-pastel keep 1.0 (no darken).\n};\n\n// The (medium, mode) -> StrokeProfile selector. The if-ladder's knobs are the profile's\n// fields; a new medium adds a CASE here (never an edit to a dispatch body). mode is the\n// uStrokeMode oil-stroke sub-mode (0 oil, 1 palette-knife, 3 brushwork). crayon\n// (uMedium==4) is a PEER tooth-multiply medium that never reaches the stroke cascade.\n// The painterly STROKE mediums are oil (MEDIUM_OIL), van-Gogh (MEDIUM_VANGOGH) and\n// oil-pastel (MEDIUM_OILPASTEL) — each AUTHORS its own profile entry below.\n" + De + "\n// The single parameterized four-layer stroke cascade (AX.W12). The four hand-unrolled\n// bestOil/paintOver invocations collapse into ONE body driven by the profile + the\n// uniform-derived per-layer scale/anisotropy multipliers (which are mode-INVARIANT — the\n// per-layer offsets, seeds, and len/wid muls are fixed across modes, so they stay here as\n// the cascade's structure; only the profile's knobs differentiate the medium). The\n// mode passes through for the two mode-special-cased shapes (knife layer-3 dabs +\n// layer-4 fill shape), which are placement details of the cascade, not profile knobs.\nvoid paintStrokeLayers(inout vec3 col, inout float height, StrokeProfile prof,\n int mode, vec2 p, float t) {\n // Scales & multipliers from uniforms (mode-invariant cascade structure).\n float baseScale = max(uStrokeScale * 0.006, 0.008);\n // Three primary layers: big gestural, medium body, small dabs.\n // W-AUR-STUDIO D5 — the −5/3 radii respacing was MEASURED + REVERTED (recorded, not\n // silently struck). The φ-adjacent geometric candidate (2.4 / 1.45 / 0.87, a fixed\n // ≈1.66× step) moved oil-pastel β −2.534 → −2.413 — TOWARD the −5/3 band but NOT into\n // it (floor −1.85), so the keep-iff-into-band gate failed. The single-pass WebGL2 path\n // is the true limiter (HC-aurora §5): the oil-pastel β + the oil/oil-pastel anisotropy\n // residual stays the oil/oil-pastel stroke-cascade ceiling. BB.W-AUR-KUWAHARA DECIDED the\n // booking: the SOFT anisotropic-Kuwahara finish ships as the OPT-IN mediumKuwahara() body\n // (uMedium==7) the consumer selects — NOT a re-tune of these oil/oil-pastel knobs. The\n // hand-set 2.4 / 1.1 / 0.45 stays — it is van-Gogh's landed-band spacing.\n float sBig = baseScale * 2.4;\n float sMed = baseScale * 1.1;\n float sSml = baseScale * 0.45;\n\n // Strokes are elongated HARD (long·thin) so the rendered field reads strongly\n // directional — the §4.2 structure-tensor anisotropy lift. A short fat dab is locally\n // isotropic and drags the measured A down; a long thin stroke that hugs the tensor\n // tangent is what gives Starry Night its A≈0.83. The width muls are pulled tighter to\n // keep the long·thin aspect.\n float lenMulBig = mix(3.0, 5.2, uStrokeAnisotropy);\n float widMulBig = mix(0.42, 0.22, uStrokeAnisotropy);\n float lenMulMed = mix(2.8, 4.8, uStrokeAnisotropy);\n float widMulMed = mix(0.40, 0.21, uStrokeAnisotropy);\n float lenMulSml = mix(2.2, 3.6, uStrokeAnisotropy);\n float widMulSml = mix(0.36, 0.23, uStrokeAnisotropy);\n\n float jitterAmt = 0.75; // large jitter — no grid\n vec2 flow = flowField(p, t);\n\n // Layer 1 — big gestural strokes (sparse, shaping)\n StrokeHit hBig = bestOil(p, sBig, lenMulBig, widMulBig, jitterAmt * 0.55,\n prof.densityBig, prof.shapeType, prof.bristleAmp, flow, t, 1.3,\n prof.energyGrade);\n paintOver(col, height, hBig, prof.streakFreq * 0.7, prof.streakAmp,\n uImpasto * prof.impastoAmp * uStrokeAmount, prof.hardness, 1.3, prof.impastoFloor);\n\n // Layer 2 — medium body strokes\n StrokeHit hMed = bestOil(p + vec2(11.3, 3.7), sMed, lenMulMed, widMulMed,\n jitterAmt, prof.densityMed, prof.shapeType, prof.bristleAmp, flow, t, 2.7,\n prof.energyGrade);\n paintOver(col, height, hMed, prof.streakFreq, prof.streakAmp,\n uImpasto * prof.impastoAmp * uStrokeAmount, prof.hardness, 2.7, prof.impastoFloor);\n\n // Layer 3 — small strokes (more frequent, smaller). Even the knife layer-3 stays the\n // medium's DIRECTIONAL shape, not a round dab — the round-dab sparkle was a fine-scale\n // isotropic speckle that dragged the §4.2 anisotropy down + rolled the §4.3 slope shallow.\n int smlShape = prof.shapeType;\n StrokeHit hSml = bestOil(p + vec2(-5.1, 8.4), sSml, lenMulSml, widMulSml,\n jitterAmt * 0.9, prof.densitySml, smlShape,\n prof.bristleAmp * 0.85, flow, t, 4.1, prof.energyGrade);\n paintOver(col, height, hSml, prof.streakFreq * 1.4, prof.streakAmp * 0.6,\n uImpasto * prof.impastoAmp * 0.45 * uStrokeAmount, prof.hardness, 4.1, prof.impastoFloor);\n\n // Layer 4 — fill strokes (dense, fine) — cover bald spots. The density is\n // PROFILE-driven (prof.densityFill): oil/oil-pastel fill to full coverage; van-Gogh\n // keeps it sparse so the bare-ground gaps between its atomic dabs survive. The fill is\n // ELONGATED + flow-oriented (NOT a round dab) — a dense round-dab fill is locally\n // isotropic and was the dominant §4.2 anisotropy sink; an oriented fine stroke keeps the\n // fill directional so the whole field reads coherent.\n float sFill = baseScale * 0.22;\n float lenMulFill = mix(2.0, 3.2, uStrokeAnisotropy);\n float widMulFill = mix(0.34, 0.24, uStrokeAnisotropy);\n int fillShape = (mode == 1) ? 3 : prof.shapeType; // knife=even; others inherit the medium's directional shape\n StrokeHit hFill = bestOil(p + vec2(3.9, -6.2), sFill, lenMulFill, widMulFill,\n jitterAmt * 0.8, prof.densityFill, fillShape,\n prof.bristleAmp * 0.6, flow, t, 8.9, prof.energyGrade);\n // The fill is a low-relief, low-streak base coat — it covers bald spots WITHOUT adding\n // fine bright speckle (a high-impasto/high-streak fill caught a per-dab glint that read\n // as isotropic noise over the directional macro strokes — the §4.2/§4.3 sink). Its\n // positional jitter is tamed too so the fill strokes stay flow-aligned, not scattered.\n paintOver(col, height, hFill, prof.streakFreq * 1.2, prof.streakAmp * 0.35,\n uImpasto * prof.impastoAmp * 0.18 * uStrokeAmount, prof.hardness * 0.9, 8.9, prof.impastoFloor);\n\n // Optional crosshatch layer\n if (uStrokeLayers == 2) {\n vec2 flow2 = vec2(-flow.y, flow.x);\n StrokeHit hX = bestOil(p + vec2(7.3, -2.1), sMed, lenMulMed * 0.9, widMulMed,\n jitterAmt, prof.densityMed * 0.7, prof.shapeType, prof.bristleAmp, flow2, t, 6.5,\n prof.energyGrade);\n paintOver(col, height, hX, prof.streakFreq, prof.streakAmp * 0.85,\n uImpasto * prof.impastoAmp * 0.55 * uStrokeAmount, prof.hardness, 6.5, prof.impastoFloor);\n }\n}\n\n// The shared stroke-medium SUBSTRATE finish (AX.W12 + AX.W13): paint the four-layer\n// cascade off the profile, then canvas-tooth + relight + saturation. The three painterly\n// STROKE mediums (oil / van-Gogh / oil-pastel) each AUTHOR their own profile and call\n// this — the substrate is shared, the PROFILE (not a dispatch-body fork) differentiates\n// the medium (slice 8 F6). mode passes through for the cascade's mode-special-cased shapes.\nvec3 paintStrokeMedium(vec3 col, vec2 p, float t, StrokeProfile prof, int mode) {\n vec3 result = col;\n // AW.W4.2 — accumulated paint HEIGHT across the stroke layers. The relight reads\n // its gradient for the normal; the canvas tooth seeds the base relief.\n float height = 0.0;\n paintStrokeLayers(result, height, prof, mode, p, t);\n\n // Canvas tooth — linen weave\n float tooth1 = vnoise(p * prof.toothScale);\n float tooth2 = vnoise(p * prof.toothScale * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * prof.toothAmp * uCanvasGrain;\n\n // AW.W4.2 — relight the accumulated paint height with the movable uLightDir\n // source (diffuse + Blinn specular, in LINEAR before aces()). The canvas tooth\n // is the base relief term so the weave also catches the raking light.\n float canvasBase = tooth * prof.toothAmp * 0.5;\n result = relightImpasto(result, height, canvasBase);\n\n // AX.W13 — the visible ground. The bare-ground gaps (low accumulated stroke height)\n // multiply toward prof.groundFloor, so van-Gogh's sparse atomic dabs read as separable\n // marks over a darker underpainting (oil/oil-pastel keep groundFloor=1.0 → no-op). The\n // smoothstep keeps the impasto dabs (high height) at full value; only the open ground\n // between them darkens.\n result *= mix(prof.groundFloor, 1.0, smoothstep(0.0, 0.38, height));\n\n // Pigment saturation boost\n result = saturate3(result, prof.pigmentSat);\n\n return result;\n}\n\nvec3 mediumOil(vec3 col, vec2 p, float t) {\n // The oil medium is a thin body: fetch the oil profile (mode-dispatched), paint via\n // the shared stroke substrate. uStrokeMode: 0 oil (gestural), 1 knife, 3 brushwork.\n int mode = uStrokeMode;\n StrokeProfile prof = profileFor(MEDIUM_OIL, mode);\n return paintStrokeMedium(col, p, t, prof, mode);\n}\n\n#define MEDIUM_KUWAHARA 7\n\n// ── Kuwahara — the anisotropic generalized Kuwahara painterly finish (uMedium==7,\n// BB.W-AUR-KUWAHARA) ───────────────────────────────────────────────────────────────\n// The SOTA edge-preserving painterly smoothing: the generalized/anisotropic Kuwahara of\n// Kyprianidis 2010, the SOFT polynomial-weighted variant (NOT the pre-2010 hard argmin,\n// which BANDS the flat field into an 8-spoke pinwheel — the §4.2 anti-regression). Aurora\n// is a PROCEDURAL field (no input texture), so the operator runs over sampleBase() directly:\n// an elliptical kernel oriented along the structure tensor (elongated along the edge-tangent,\n// narrowed by the anisotropy), divided into 8 overlapping sectors via smooth weights; the\n// output is the variance-weighted blend of the sector means (1/(1+var^q) — low-variance\n// sectors dominate, the SOFT criterion → flat oil-paint patches with crisp zone boundaries).\n// Single-pass small-radius WebGL2 form: 4 rings × 8 angular taps = 32 procedural samples\n// (cost-bounded; the offscreen-park / PRM freeze the substrate owns is untouched).\nvec3 mediumKuwahara(vec3 col, vec2 p, float t) {\n // Orient the kernel along the structure-tensor edge-tangent + read the coherence A\n // (the field widened vec3→vec4 for metal's gradient; kuwahara reads only .xy/.z).\n vec4 stf = structureTensorField(p, t, flowField(p, t));\n vec2 tangent = stf.xy;\n float A = stf.z;\n // The ellipse axes: major along the tangent, minor across. Anisotropy squeezes the\n // minor axis (coherent zones → long-thin patch along the colour band; flat zones →\n // near-circular so the operator degrades gracefully to the isotropic Kuwahara). The\n // base radius rides uStrokeScale so the painterly patch size tracks the studio knob.\n float radius = clamp(0.010 + uStrokeScale * 0.00018, 0.006, 0.024);\n float aniso = mix(1.0, 0.34, clamp(A, 0.0, 1.0)); // minor/major axis ratio\n vec2 ax = tangent; // major-axis unit vector (edge-tangent)\n vec2 ay = vec2(-tangent.y, tangent.x); // minor-axis unit vector (edge-normal)\n\n // 8 sectors, soft polynomial weights. Each sector i centers on angle i·45°; a sample's\n // sector weight is a smooth gaussian-of-angular-distance so sectors OVERLAP (no hard\n // argmin → no pinwheel banding). The center sample seeds all sectors equally.\n const int SECTORS = 8;\n vec3 m[8]; // per-sector weighted colour sum\n vec3 s[8]; // per-sector weighted colour² sum (for variance)\n float w[8]; // per-sector weight sum\n for (int i = 0; i < SECTORS; i++) { m[i] = vec3(0.0); s[i] = vec3(0.0); w[i] = 0.0; }\n\n // 4 rings × 8 angular taps over the elliptical disc. Each tap: sample the procedural\n // field, then deposit into the sectors by the soft angular weight × the radial falloff.\n const int RINGS = 4;\n const int TAPS = 8;\n for (int r = 1; r <= RINGS; r++) {\n float rr = float(r) / float(RINGS); // 0.25 .. 1.0\n float radial = 1.0 - 0.6 * rr; // inner taps weigh more (gaussian-ish)\n for (int a = 0; a < TAPS; a++) {\n float ang = 6.2831853 * float(a) / float(TAPS);\n // Elliptical offset: major axis full radius, minor axis squeezed by aniso.\n vec2 dirE = ax * cos(ang) + ay * (sin(ang) * aniso);\n vec2 off = dirE * (radius * rr);\n vec3 c = sampleBase(p + off, t);\n // The sample's angular bin (its sector) — soft, so it spills into neighbours.\n for (int k = 0; k < SECTORS; k++) {\n float sectorAng = 6.2831853 * float(k) / float(SECTORS);\n // Smallest signed angular distance to the sector center.\n float d = ang - sectorAng;\n d = atan(sin(d), cos(d)); // wrap to [-π, π]\n float sw = exp(-d * d * 2.2) * radial; // gaussian angular weight × radial\n m[k] += c * sw;\n s[k] += c * c * sw;\n w[k] += sw;\n }\n }\n }\n\n // Per-sector mean + scalar luma-variance, then the SOFT variance-weighted blend.\n vec3 accum = vec3(0.0);\n float accumW = 0.0;\n for (int k = 0; k < SECTORS; k++) {\n float wk = max(w[k], 1e-4);\n vec3 mean = m[k] / wk;\n vec3 var3 = max(s[k] / wk - mean * mean, vec3(0.0));\n float variance = dot(var3, W_LUMA); // perceptual scalar variance\n // SOFT criterion: low-variance sectors (flat, inside a colour zone) dominate; a\n // high-variance sector (straddling an edge) is suppressed. q controls the sharpness;\n // a finite q (not argmin) keeps the blend continuous → no pinwheel spoke.\n float sectorW = 1.0 / (1.0 + pow(variance * 320.0, 4.0));\n accum += mean * sectorW;\n accumW += sectorW;\n }\n vec3 result = accum / max(accumW, 1e-4);\n\n // The center sample anchors the result in near-flat zones (where every sector reads the\n // same colour) so the operator is a no-op there — the smoothing only BITES at edges.\n vec3 center = sampleBase(p, t);\n result = mix(center, result, clamp(0.35 + 0.65 * A, 0.0, 1.0));\n\n // A faint canvas tooth + pigment-saturation lift so the flattened patches read as\n // oil-paint, not a posterized blur (the painterly identity the smoothing alone lacks).\n float tooth = vnoise(p * 300.0) - 0.5;\n result *= 1.0 + tooth * 0.06 * uCanvasGrain;\n result = saturate3(result, 1.04);\n\n // Blend toward the incoming col by uStrokeAmount so the studio amount knob controls\n // the finish strength (uStrokeAmount=0 → the raw field passes through; default → full).\n return mix(col, result, clamp(uStrokeAmount, 0.0, 1.0));\n}\n\n" + ke + Ee + "// AX.W13 — the oil-pastel medium (uMedium==6) is a DISTINCT stroke-deposition body,\n// split out of the dry-crayon tooth-multiply (mediumCrayon) (slice 8 F1). It DEPOSITS\n// broad smeared directional strokes via the same brush engine with a creamy soft\n// hardness, heavy pigment build-up where strokes overlap, and a chroma punch — the\n// stroke-deposition model that gives oil-pastel its depth and less-uniform read.\n// Shares the SUBSTRATE (the stroke cascade + tooth + relight) with oil/van-Gogh, NOT\n// the dispatch body of dry crayon. The bridge forces tensor orientation.\nvec3 mediumOilPastel(vec3 col, vec2 p, float t) {\n StrokeProfile prof = profileFor(MEDIUM_OILPASTEL, 0);\n return paintStrokeMedium(col, p, t, prof, 0);\n}", A = "\n", Me = `#version 300 es
121
+ var xe = "#version 300 es\nin vec2 aPos;\nout vec2 vUv;\nvoid main() {\n vUv = aPos * 0.5 + 0.5;\n gl_Position = vec4(aPos, 0.0, 1.0);\n}", Se = "\nconst float CURL_EPS = 0.012;\n\n// Scalar fbm potential psi — the host's noise basis, wrapped so the curl operator is\n// basis-agnostic. A host carrying its own fbm defines this as: return fbm(p);\nfloat potentialFBM(vec2 p);\n\n// ∇×ψ in 2D — the divergence-free flow direction. Central finite differences of the\n// scalar potential give the partials; the cross-pairing (∂y, −∂x) is the 2D curl.\nvec2 curlFBM(vec2 p) {\n float dx = potentialFBM(p + vec2(CURL_EPS, 0.0)) - potentialFBM(p - vec2(CURL_EPS, 0.0));\n float dy = potentialFBM(p + vec2(0.0, CURL_EPS)) - potentialFBM(p - vec2(0.0, CURL_EPS));\n // ψ-gradient is (dx, dy)/(2·eps); the curl rotates it 90°: (∂ψ/∂y, −∂ψ/∂x).\n vec2 g = vec2(dx, dy) / (2.0 * CURL_EPS);\n return vec2(g.y, -g.x);\n}\n", Ce = "// ── Palette LUT (OKLCh/OKLab interpolation) ──────────────────────────────\n// The endpoints stay CPU-baked to LINEAR sRGB (the Aras precompute pattern); only\n// the between-endpoint interpolation runs in perceptual space. The ramp\n// itself (the smoothstep ease + the OKLab-rectangular-vs-OKLCh-hue-arc dispatch on\n// uHuePath) is the shared samplePaletteRamp from procedural-color.glsl, the SAME\n// source the WebGPU twin splices; samplePalette here only selects the bracketing\n// stop pair + the raw inter-stop t and hands them to the shared ramp.\nvec3 samplePalette(float id) {\n id = clamp(id, 0.0, 1.0);\n float scaled = id * float(uStopCount - 1);\n int i0 = int(floor(scaled));\n int i1 = min(i0 + 1, uStopCount - 1);\n float t = fract(scaled);\n return samplePaletteRamp(uPalette[i0], uPalette[i1], t, uHuePath);\n}\n\n// ── Nuclei field ──────────────────────────────────────────────────────────\nvoid nucleiField(vec2 p, float t, out float paletteId, out float valueMod) {\n float accumBias = 0.0;\n float accumValue = 0.0;\n float accumW = 0.0;\n for (int i = 0; i < MAX_NUCLEI; i++) {\n if (i >= uNucleiCount) break;\n vec2 posI = uNucleiPos[i]\n + uNucleiDriftRadius[i] * vec2(\n cos(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i]),\n sin(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i] * 1.13)\n );\n vec2 diff = p - posI;\n // Anisotropic Gaussian: rotate diff into the nucleus's local frame\n // (major axis along uNucleiAngle), then scale the major-axis component by\n // 1/elongation so the squared distance describes an ellipse. Defaults\n // 1.0/0.0 reduce to the isotropic dot(diff, diff).\n float ca = cos(uNucleiAngle[i]);\n float sa = sin(uNucleiAngle[i]);\n vec2 local = vec2( ca * diff.x + sa * diff.y,\n -sa * diff.x + ca * diff.y);\n float along = local.x / max(uNucleiElong[i], 0.01);\n float across = local.y;\n float d2 = along * along + across * across;\n float r = max(uNucleiRadius[i], 0.01);\n float w = exp(-uSoftmaxBeta * d2 / (r * r));\n accumBias += w * uNucleiPaletteBias[i];\n accumValue += w * uNucleiValueBias[i];\n accumW += w;\n }\n paletteId = accumBias / max(accumW, 1e-4);\n valueMod = accumValue / max(accumW, 1e-4);\n\n // Palette drift — slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30–60s hue cycle at the default coefficient);\n // the amplitude lifts the authored coefficient into a 0.03..0.06 paletteId band\n // so the palette visibly travels rather than dithering within one stop.\n float palAmp = clamp(uPaletteDrift * 6.0, 0.0, 0.06);\n paletteId += palAmp * sin(t * uPaletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n}\n", we = "// ── Flow field ────────────────────────────────────────────────────────────\n// Structure-tensor prototype (body defined post-sampleBase in the mediums block).\n// Returns vec4(dir.x, dir.y, coherence, packedGrad) — the minor-eigenvector tangent + A + the\n// metal-gradient .w lane in the widened vec3→vec4 return. The forward declaration must match\n// the vec4 definition or WebKit's compiler rejects the mismatched signature (L1b).\nvec4 structureTensorField(vec2 p, float t, vec2 fallbackDir);\n\nvec2 flowField(vec2 p, float t) {\n vec2 dir = vec2(1.0, 0.0);\n if (uFlowPattern == 1) {\n // radial\n dir = normalize(p - uFlowFocal + 1e-4);\n } else if (uFlowPattern == 2) {\n // swirl — tangent to radial\n vec2 rad = normalize(p - uFlowFocal + 1e-4);\n dir = vec2(-rad.y, rad.x);\n } else if (uFlowPattern == 3) {\n // diagonal\n float a = radians(uFlowAngle);\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 4) {\n // multi — curl-noise driven\n float n = fbm(p * 2.0 + t * 0.02);\n float a = n * 6.2831;\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 5) {\n // Tensor / ETF: the color field's own edge-tangent flow. The\n // diagonal angle seeds the smooth fallback the coherence-weighted blend\n // relaxes toward in low-structure zones. Returns directly (no curl/cursor\n // post-mix — the tensor field already carries the structure orientation;\n // the cursor warp still drives the underlying color field in domainWarp).\n float a = radians(uFlowAngle);\n vec2 fallback = vec2(cos(a), sin(a));\n return structureTensorField(p, t, fallback).xy;\n }\n // curl — perturb by noise. Radial gets much less curl so rays stay clean.\n if (uFlowCurl > 0.0) {\n float n = fbm(p * 3.0) - 0.5;\n float curlAmt = uFlowCurl;\n if (uFlowPattern == 1) curlAmt *= 0.25; // radial: preserve ray clarity\n if (uFlowPattern == 2) curlAmt *= 0.55; // swirl: allow moderate curl\n float a = atan(dir.y, dir.x) + n * 3.14159 * curlAmt;\n dir = vec2(cos(a), sin(a));\n }\n // cursor influence — swirl around the cursor position\n if (uCursorStrength > 0.001) {\n vec2 toCur = uCursor - p;\n float d = length(toCur);\n float r = max(uCursorRadius, 0.01);\n float w = exp(-(d * d) / (r * r * 0.5));\n // swirl tangent\n vec2 tangent = vec2(-toCur.y, toCur.x) / max(d, 1e-4);\n float a0 = atan(dir.y, dir.x);\n float a1 = atan(tangent.y, tangent.x);\n // blend angle\n float da = a1 - a0;\n da = atan(sin(da), cos(da)); // wrap to [-pi, pi]\n float a = a0 + da * w * uCursorStrength;\n dir = vec2(cos(a), sin(a));\n }\n return dir;\n}\n", Te = "// Khronos PBR-Neutral tonemap (hue + saturation preserving)\nvec3 aces(vec3 color) {\n const float startCompression = 0.8 - 0.04;\n const float desaturation = 0.15;\n float x = min(color.r, min(color.g, color.b));\n float offset = x < 0.08 ? x - 6.25 * x * x : 0.04;\n color -= offset;\n float peak = max(color.r, max(color.g, color.b));\n if (peak < startCompression) return clamp(color, 0.0, 1.0);\n float d = 1.0 - startCompression;\n float newPeak = 1.0 - d * d / (peak + d - startCompression);\n color *= newPeak / peak;\n float g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);\n return clamp(mix(color, newPeak * vec3(1.0), g), 0.0, 1.0);\n}\n", Ee = "// ── Curved swept brushstroke primitive ────────────────────────────────────\n// A stroke is a curved spine from A to B with quadratic bulge K (sideways offset\n// at midpoint). Width varies along length via a shape profile. Edge is ragged\n// (bristle-modulated), ends are rounded blobs, inside has streak modulation.\n//\n// For pixel p, we invert an approximate projection onto the curved spine:\n// 1) project p onto straight AB axis -> along0, cross0\n// 2) at parameter along0, spine offset = 4·K·along0·(1-along0) perpendicular\n// 3) cross = cross0 - spineOffset, refine along one iteration\n// Coverage blends paint atop col based on per-stroke color sampled from the\n// underlying base at spine midpoint — so overlapping strokes from different\n// palette regions meet at hard, ragged demarcations.\n\nstruct StrokeHit {\n float coverage; // 0..1\n vec3 color;\n float alongT; // 0..1 along spine, for internal modulation\n float crossN; // cross distance / current half-width, signed\n float edgeN; // distance to edge in half-widths (0 = at edge, 1 = at spine)\n};\n\nStrokeHit noHit() { return StrokeHit(0.0, vec3(0.0), 0.0, 0.0, 0.0); }\n\nvec2 rotateDir(vec2 dir, float angle) {\n float ca = cos(angle), sa = sin(angle);\n return vec2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);\n}\n\nvec2 safeDir(vec2 dir) {\n float len = length(dir);\n return len > 1e-4 ? dir / len : vec2(1.0, 0.0);\n}\n\n// Shape profile along the stroke. type:\n// 0 tapered — thin-fat-thin (classic brush)\n// 1 load-drag — fat start, tapers to a point\n// 2 dab — ellipse-like, round center\n// 3 even — near-constant, slight end taper\n// 4 comma — van-Gogh atomic dab: ASYMMETRIC taper, fat near one end and\n// drawn to a fine point at the other (the divisionist crescent\n// mark). The width peak sits off-centre (~0.32) so the two ends\n// taper at DIFFERENT rates — a loaded round head + a dragged tail.\nfloat strokeShape(float t, int type) {\n t = clamp(t, 0.0, 1.0);\n if (type == 1) {\n // loaded at t=0, tapering\n return pow(1.0 - t, 0.55) * smoothstep(0.0, 0.08, t);\n } else if (type == 2) {\n // dab / blob\n float d = t - 0.5;\n return exp(-d * d * 12.0);\n } else if (type == 3) {\n // mostly even, slight end softening\n return smoothstep(0.0, 0.08, t) * smoothstep(1.0, 0.92, t) * 0.95 + 0.05;\n } else if (type == 4) {\n // comma / crescent — width peaks at ~0.32 then drags to a fine tail. The two\n // sides of the peak fall at different exponents (a fat round head, a long thin\n // tail), so the mark reads as a directional van-Gogh comma, not a symmetric oval.\n float peak = 0.32;\n float head = smoothstep(0.0, peak, t); // rounded loaded head\n float tail = pow(clamp((1.0 - t) / (1.0 - peak), 0.0, 1.0), 1.6); // dragged thin tail\n return clamp(min(head, tail) * 1.04, 0.0, 1.0);\n }\n // 0 tapered\n return smoothstep(0.0, 0.22, t) * smoothstep(1.0, 0.78, t);\n}\n\n// A single curved stroke.\n// a, b — endpoints\n// halfW — base half-width (world units)\n// bulge — signed perpendicular midpoint offset, in world units\n// shapeType — 0..3\n// bristleFreq — spatial frequency of edge raggedness\n// bristleAmp — 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed — uniqueness seed for internal streaks\n// colAtMid — pre-sampled color at midpoint\nStrokeHit curvedStroke(vec2 p, vec2 a, vec2 b, float halfW,\n float bulge, int shapeType,\n float bristleFreq, float bristleAmp,\n float streakSeed, vec3 colAtMid) {\n vec2 ab = b - a;\n float L = length(ab);\n if (L < 1e-5) return noHit();\n vec2 tang = ab / L;\n vec2 norm = vec2(-tang.y, tang.x);\n\n vec2 rel = p - a;\n float along0 = dot(rel, tang) / L; // straight projection 0..1 along AB\n float cross0 = dot(rel, norm); // signed cross\n\n // Spine sideways offset due to bulge (parabolic)\n float bend = 4.0 * bulge * along0 * (1.0 - along0);\n float cross1 = cross0 - bend;\n\n // One refinement step: as the spine bends, the closest-point along shifts.\n // Spine tangent differs from straight tangent; first-order correction:\n float dBend = 4.0 * bulge * (1.0 - 2.0 * along0); // d(bend)/d(along)\n float along1 = along0 + (cross1 * dBend) / (L * (1.0 + dBend * dBend / (L * L)));\n\n // Bristle-ragged edge: half-width gets chewed by 1D noise along the spine\n float edgeNoise = fbm(vec2(along1 * bristleFreq, streakSeed * 7.3)) - 0.5;\n float edgeNoise2 = fbm(vec2(along1 * bristleFreq * 2.3, streakSeed * 3.1 + 17.0)) - 0.5;\n float edgeMod = 1.0 - bristleAmp * (0.6 + 0.4 * edgeNoise) * (0.5 + edgeNoise2);\n\n // Width profile along the stroke\n float shape = strokeShape(along1, shapeType);\n float halfWNow = halfW * shape * edgeMod;\n\n // Inside-segment coverage\n float cov = 0.0;\n if (along1 >= 0.0 && along1 <= 1.0 && halfWNow > 1e-6) {\n float cn = abs(cross1) / halfWNow;\n // fwidth-derived AA: half-width from the field's screen-space gradient so the\n // edge holds ~1px regardless of zoom/DPR (mirrors metaball.frag.ts:252-255).\n float aaC = max(fwidth(cn), 1e-6);\n cov = 1.0 - smoothstep(1.0 - aaC, 1.0 + aaC, cn);\n }\n\n // End-cap blobs (rounded tips, not perpendicular cuts)\n float capA = 0.0, capB = 0.0;\n {\n vec2 capCenterA = a + norm * (bulge * 0.0); // at a\n vec2 capCenterB = a + tang * L + norm * bend * 0.0 + norm * 0.0; // at b; bend is 0 at endpoints\n capCenterB = b;\n float endShapeA = strokeShape(0.02, shapeType);\n float endShapeB = strokeShape(0.98, shapeType);\n float rA = halfW * endShapeA * (0.9 + 0.2 * fbm(vec2(streakSeed * 13.0)));\n float rB = halfW * endShapeB * (0.9 + 0.2 * fbm(vec2(streakSeed * 19.0 + 5.0)));\n float dA = length(p - capCenterA);\n float dB = length(p - capCenterB);\n // fwidth-derived AA on the clean Euclidean cap distances (mirrors metaball.frag.ts:252-255).\n float aaA = max(fwidth(dA), 1e-6);\n float aaB = max(fwidth(dB), 1e-6);\n capA = 1.0 - smoothstep(rA - aaA, rA + aaA, dA);\n capB = 1.0 - smoothstep(rB - aaB, rB + aaB, dB);\n // Only apply caps if we're BEYOND the segment; otherwise the segment wins.\n capA *= (along1 < 0.05) ? 1.0 : 0.0;\n capB *= (along1 > 0.95) ? 1.0 : 0.0;\n }\n\n float coverage = max(cov, max(capA, capB));\n if (coverage < 0.002) return noHit();\n\n // Edge distance (for impasto later): 0 at edge, 1 at spine\n float edgeDist = halfWNow > 1e-6 ? clamp(1.0 - abs(cross1) / halfWNow, 0.0, 1.0) : 0.0;\n\n return StrokeHit(\n coverage,\n colAtMid,\n clamp(along1, 0.0, 1.0),\n halfWNow > 1e-6 ? cross1 / halfWNow : 0.0,\n edgeDist\n );\n}\n\n// The painterly STROKE mediums (those that deposit via paintStrokeLayers/paintOver):\n// oil (3), van-Gogh (5), oil-pastel (6). For these the stroke OVER-composite runs in\n// OKLab so overlapping complementaries mix through a chromatic path, not the linear-\n// RGB grey mud; the smooth/atmospheric pole never reaches paintOver so it pays nothing.\nbool isPainterlyStroke() {\n return uMedium == 3 || uMedium == 5 || uMedium == 6;\n}\n\n// OKLab OVER-composite: lerp L,a,b of the over-color toward the under-color (the\n// Ottosson matrices spliced from procedural-color.glsl.ts — no new color math). Two\n// complementary-hued strokes overlapping transition through OKLab, where the midpoint\n// holds chroma, instead of the linear mix() that desaturates toward grey.\nvec3 paintOverOklab(vec3 under, vec3 over, float alpha) {\n vec3 labU = linOklab(under);\n vec3 labO = linOklab(over);\n return max(oklabToLinearSrgb(mix(labU, labO, alpha)), vec3(0.0));\n}\n\n// Composite stroke over col with internal streaking + ACCUMULATE paint HEIGHT.\n// streakAmp — 0..0.2 how much internal streaks darken/lighten\n// impastoAmp — 0..1 paint-thickness contribution (the height the relight reads)\n// hardness — 0..1 how crisp the compositing transition is (1 = razor, 0 = creamy)\n//\n// The fixed-RGB edge rim (the phantom upper-left light) is retired. Each\n// stroke now deposits PAINT HEIGHT into the height accumulator (coverage x per-stroke\n// thickness, perturbed by the bristle/streak fbm for ridges/grooves); mediumOil\n// derives a normal from the accumulated height gradient (dFdx/dFdy) and relights it\n// once with the movable uLightDir source (relightImpasto), in linear before aces().\n//\n// The internal streak perturbs hue and chroma in OKLCh, not value only,\n// so a single stroke carries a small hue gradient (broken color at the ATOM level —\n// adjacent strokes shimmer like real impasto, not flat stamped swatches);\n// and the OVER-composite runs in OKLab on the painterly stroke mediums so overlapping\n// complementaries mix as pigment, not the linear-mix grey.\n// impastoFloor — the per-stroke height-crown floor: oil's 0.4+0.6*edgeN reads\n// floor=0.4 (the crown tapers to 40% at the stroke edge); the van-Gogh\n// profile passes floor=1.0 so each atomic dab catches a FULL-height\n// glint (scope item 1d). The crown magnitude is floor + (1-floor)*edgeN.\nvoid paintOver(inout vec3 col, inout float height, StrokeHit s,\n float streakFreq, float streakAmp,\n float impastoAmp, float hardness, float streakSeed,\n float impastoFloor) {\n if (s.coverage < 0.002) return;\n float strokeOpacity = clamp(uStrokeAmount, 0.0, 1.0);\n if (strokeOpacity <= 0.001) return;\n vec3 c = s.color;\n\n // Internal streaks — fbm along spine, modulated by cross position. Gives\n // the loaded-brush look: some bristles carry more pigment than others.\n float streakA = fbm(vec2(s.alongT * streakFreq, s.crossN * 2.7 + streakSeed));\n float streakB = fbm(vec2(s.alongT * streakFreq * 0.6 + streakSeed * 3.7, s.crossN * 4.1));\n float streak = 0.6 * (streakA - 0.5) + 0.4 * (streakB - 0.5);\n\n // ── Within-stroke OKLCh broken color. The streak fBm perturbs\n // the stroke's HUE + CHROMA along alongT/crossN, seeded per-stroke, so a single\n // stroke reads a small hue gradient (the atom-level broken-color shimmer). Value is\n // carried by the same streak so the loaded-brush light/dark survives. Gated to the\n // painterly stroke mediums + uBrokenColor (the smooth pole keeps the cheap value-only\n // streak). A second decorrelated fBm drives the hue axis so hue ≠ a scaled value.\n float hueStreak = fbm(vec2(s.alongT * streakFreq * 0.45 + streakSeed * 5.3,\n s.crossN * 1.9 + streakSeed * 1.7)) - 0.5;\n if (isPainterlyStroke() && uBrokenColor > 0.001) {\n vec3 lch = oklabToOklch(linOklab(c));\n // ±~10° within-stroke hue swing + ±~9% chroma, scaled by uBrokenColor. Smaller\n // than the per-stroke cell jitter (brokenColorJitter) — this is the intra-stroke\n // gradient layered on top of the per-cell pigment patch.\n lch.z += hueStreak * (20.0 * PI / 180.0) * uBrokenColor;\n lch.y = max(lch.y * (1.0 + (streak) * 0.18 * uBrokenColor), 0.0);\n c = max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n }\n // Value streak (the loaded-brush light/dark across the bristles) — kept for every\n // medium that reaches paintOver (the smooth pole never does). The along-spine value\n // modulation is the loaded-brush light/dark that gives each stroke its OWN directional\n // luminance band — the structure-tensor signal a low-contrast color field (a uniform-hue\n // oil field) has no other source for (the §4.2 anisotropy lift on the flat-palette case).\n c *= 1.0 + streak * streakAmp * 3.2;\n\n // Cross-stroke ridge/valley shading — a catch-light along the spine centre, darkening to\n // the stroke edges. This is the strongest DIRECTIONAL luminance cue: it carves a bright\n // ridge down the stroke's length and dark grooves between adjacent strokes, so a field of\n // parallel strokes reads as parallel light/dark bands (the high-A van-Gogh signature) even\n // where the underlying colour is flat. s.edgeN is 0 at the edge, 1 at the spine.\n float ridgeShade = smoothstep(0.0, 0.55, s.edgeN);\n c *= 1.0 + (ridgeShade - 0.5) * 0.30;\n\n float softLimit = mix(0.35, 0.98, hardness);\n float alpha = smoothstep(0.0, 1.0 - softLimit, s.coverage) * strokeOpacity;\n // OKLab OVER-composite on the painterly stroke mediums (overlap mixes as pigment,\n // not grey); the smooth pole never reaches paintOver, so it keeps the cheap path.\n col = isPainterlyStroke() ? paintOverOklab(col, c, alpha) : mix(col, c, alpha);\n\n // ── Accumulate paint height. Thickness peaks at the stroke spine and\n // falls toward the impastoFloor at the edge (edgeN: 0=edge, 1=spine), perturbed by\n // the bristle streak so ridges/grooves form. The relight reads the gradient of this\n // field. The van-Gogh profile's floor=1.0 gives every atomic dab a full-height crown.\n float ridge = 0.7 + 0.3 * (streak + 0.5);\n float crown = impastoFloor + (1.0 - impastoFloor) * s.edgeN;\n height += s.coverage * impastoAmp * crown * ridge * alpha;\n}\n\n// Relight the accumulated paint height. Derive a normal from the height\n// gradient (dFdx/dFdy — derivatives are in-pattern; fwidth is already used for AA),\n// then apply diffuse + Blinn specular from the movable uLightDir/uLightColor source.\n// LINEAR-space lighting BEFORE aces() (the tonemap/OETF stay locked); thin strokes\n// inherit a low shininess, thick impasto raises it (high-height ridges catch a sharp\n// glint). canvasBase is the tooth height so the canvas weave also catches the light.\nvec3 relightImpasto(vec3 col, float height, float canvasBase) {\n float h = height + canvasBase;\n // Height-gradient normal. heightScale trades relief depth for surface flatness.\n float heightScale = 6.0;\n vec3 N = normalize(vec3(-dFdx(h) * 40.0, -dFdy(h) * 40.0, 1.0 / heightScale));\n vec3 L = normalize(uLightDir);\n vec3 V = vec3(0.0, 0.0, 1.0); // view straight on\n vec3 H = normalize(L + V);\n float diff = max(dot(N, L), 0.0);\n // Thick impasto → high shininess (sharp glint); thin paint → broad soft sheen.\n float shininess = mix(8.0, 64.0, clamp(height * 2.0, 0.0, 1.0));\n float spec = pow(max(dot(N, H), 0.0), shininess);\n // Modulate the lit terms by how much paint sits here (no relief on bare ground).\n float relief = clamp(h * 1.5, 0.0, 1.0);\n vec3 lit = col * (1.0 + uImpasto * relief * (diff - 0.5) * 0.5 * uLightColor);\n // The specular glint is gated to ACCUMULATED-thick paint (height²) — a per-fine-stroke\n // sharp glint on every thin fill dab read as bright isotropic SPECKLE that buried the\n // directional stroke bands (the §4.2 anisotropy / §4.3 slope sink on the low-contrast\n // oil field). Only where real impasto has built up does the raked glint fire, so the\n // highlight tracks the macro ridges, not the fine speckle.\n float specGate = clamp(height, 0.0, 1.0);\n lit += uImpasto * relief * spec * specGate * 0.5 * uLightColor;\n return lit;\n}\n\n// Best-of-9-neighbor placement: sample 3x3 surrounding cells and take the\n// stroke that covers this pixel most. Breaks the grid by per-cell jitter and\n// sparse density via noise thresholding.\n// energyGrade — the SBR energy-grade magnitude (0 = none, the oil/oil-pastel\n// baseline; 1 = the full van-Gogh Starry-Night cascade — long\n// confident strokes in the lights/coherent zones, short dabs in\n// the darks/flat zones). The van-Gogh profile carries 1.0 (the\n// energy grade is a PROFILE field, not a buried uMedium==5 branch).\nStrokeHit bestOil(vec2 p, float cellSize, float lenMul, float halfWMul,\n float jitterAmt, float density, int shapeType,\n float bristleAmp, vec2 flow, float t, float seed,\n float energyGrade) {\n vec2 cell = floor(p / cellSize);\n\n StrokeHit best = noHit();\n // 3x3 neighborhood\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n vec2 hh = hash22(cc + seed) - 0.5;\n // Density gate — noise threshold; sparse placement.\n float gate = hash21(cc * 1.7 + seed * 0.3);\n if (gate > density) continue;\n\n vec2 center = (cc + 0.5 + hh * jitterAmt) * cellSize;\n\n // Per-stroke direction source from the strokeOrient switch:\n // uStrokeOrient==0 (flow) — the layer-provided hand-authored flowField.\n // uStrokeOrient==1 (tensor) — the structure-tensor minor eigenvector at the\n // cell center (the color field's edge-tangent),\n // so strokes hug the color zones (van Gogh).\n // coh carries the per-cell coherence A for the energy grading (long\n // confident strokes where structure is coherent, stubby dabs in flat zones).\n vec2 f;\n float coh = 1.0;\n if (uStrokeOrient == 1) {\n // vec4 (the metal-finish widen) — .xy tangent + .z coherence; the .w gradient lane\n // is unused here. A vec3 local would be a type-mismatch WebKit rejects (L1c).\n vec4 tf = structureTensorField(center, t, safeDir(flow));\n f = safeDir(tf.xy);\n coh = tf.z;\n } else {\n f = safeDir(flow);\n }\n // Per-stroke deterministic local perturbation so alternate stroke layers stay\n // live. Tensor strokes get FAR less angular jitter (the field already orients them\n // — over-jittering the tensor strokes scatters their directions and the rendered\n // field reads isotropic, the §4.2 anisotropy floor). The flow-oriented (non-tensor)\n // strokes keep the full jitter (they have no per-cell orientation source).\n float jScale = (uStrokeOrient == 1) ? 0.15 : 1.0;\n float angJ = (hash21(cc + seed + 11.0) - 0.5) * 0.9 * jScale; // +/- 0.45/0.07 rad\n float curlScale = (uStrokeOrient == 1) ? 0.30 : 1.0;\n float localCurl = (fbm(center * (2.6 + seed * 0.11) + seed * 1.9) - 0.5) * 0.55 * uFlowCurl * curlScale;\n vec2 dir = rotateDir(f, angJ + localCurl);\n\n // The SBR energy grade is a profile field (energyGrade), not a buried\n // uMedium==5 branch. The van-Gogh profile passes energyGrade=1.0 (the full\n // Starry-Night cascade); oil/oil-pastel pass 0.0 (uniform length). The grade\n // modulates stroke length by local luminance (bright → long confident strokes,\n // dark → short dabs, the Kolmogorov/Batchelor congruence) AND by coherence\n // (coherent zones → long, flat → stubby), lerped in by energyGrade so a profile\n // can dial the cascade from off (0) to full (1).\n float energy = 1.0;\n if (energyGrade > 0.001) {\n float luma = clamp(dot(sampleBase(center, t), W_LUMA), 0.0, 1.0);\n float graded = mix(0.5, 1.5, luma) * mix(0.6, 1.0, coh);\n energy = mix(1.0, graded, energyGrade);\n }\n\n float lenV = cellSize * lenMul * (0.65 + 0.55 * hash21(cc + seed + 23.0)) * energy;\n float halfW = cellSize * halfWMul * (0.70 + 0.55 * hash21(cc + seed + 41.0));\n float bulge = (hash21(cc + seed + 53.0) - 0.5) * lenV * 0.35;\n\n vec2 a = center - dir * (lenV * 0.5);\n vec2 b = center + dir * (lenV * 0.5);\n\n vec3 colMid = brokenColorJitter(\n sampleBase(center, t),\n hash21(cc + seed + 89.0),\n hash21(cc * 2.3 + seed + 97.0),\n 1.0\n );\n\n StrokeHit h = curvedStroke(p, a, b, halfW, bulge, shapeType,\n 7.0, bristleAmp,\n hash21(cc + seed + 67.0), colMid);\n if (h.coverage > best.coverage) best = h;\n }\n }\n return best;\n}\n", De = "// ── Van Gogh atomic-dab body ─────────────────────────────────────────────\n// Van-Gogh does NOT route through the shared\n// oil paintStrokeMedium cascade — four DENSE layers of LONG-THIN tensor-oriented\n// strokes (lenMul up to 5.2, widMul 0.22) hugging a continuous coherent field. Packed\n// that tight, thin strokes that follow continuous iso-bands MERGE into smooth flowing\n// filaments — the \"marbled flow-bands\" read (a spirograph/fingerprint, not impasto),\n// AND the per-cell 8-tap structure tensor × 4 layers × 9 cells = ~324 sampleBase/pixel\n// runs at ~4fps. Van Gogh's sky is NOT continuous flow — it is DISCRETE, SHORT,\n// LOADED comma dabs, separated by visible ground, queued into swirl ROWS.\n//\n// This is a purpose-built two-layer DAB pass, disjoint from the oil cascade:\n// • orientation from the analytic SWIRL flowField (van-Gogh skies ARE swirl rows) +\n// per-cell angular jitter — NOT the expensive per-cell tensor (the lag source, and\n// the marble source: a jittered discrete dab can NOT merge into a continuous\n// filament the way a tensor-locked thin stroke does).\n// • SHORT comma dabs (len/wid ≈ 2.6 : 1 — a loaded crescent mark, not a 10:1\n// filament), so each reads as a SEPARABLE atom.\n// • SPARSE placement with visible inter-dab ground (density ≈ 0.42), over a darkened\n// swirl underpainting (the Starry-Night visible ground the bright dabs read over).\n// • a single sampleBase per cell + OKLCh broken-color jitter (pigment shimmer);\n// full-height impasto crown per dab (each catches its own raked glint).\n// This is ~2 layers × 9 cells × ~1 sampleBase ≈ 20/pixel (a ~16× cost cut, ~60fps), and\n// jittered short dabs CANNOT marble.\n\n// One atomic comma dab — a CLEAN analytic crescent SDF (NOT the oil curvedStroke).\n// The oil curvedStroke's spine-projection + rounded end-caps + fwidth-AA + bristle\n// edge each leave a thin sub-pixel skirt, and a field of those skirts over open ground\n// abuts into a faint network of SEAM LINES. A van-Gogh dab needs none of that\n// machinery — it is a short oriented capsule (rounded line segment) whose half-width\n// tapers along its length (the loaded-head → dragged-tail comma). The capsule SDF is\n// derivative-free (smoothstep AA against the analytic distance), so no fwidth skirt, no\n// best-of-9 seam. Returns a StrokeHit (coverage/color/alongT/edgeN) for paintOver.\n// cellSize — dab cell pitch (UV units); lenMul/widMul — dab aspect off the cell;\n// density — placement gate (sparse → visible gaps); seed — layer decorrelation.\nStrokeHit vangoghDab(vec2 p, float cellSize, float lenMul, float widMul,\n float density, float jitterAmt, float angJitter,\n vec2 flow, float t, float seed) {\n vec2 cell = floor(p / cellSize);\n StrokeHit best = noHit();\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n // Sparse density gate — leaves visible canvas ground between dabs (atomicity).\n if (hash21(cc * 1.7 + seed * 0.3) > density) continue;\n vec2 jit = (hash22(cc + seed) - 0.5) * jitterAmt;\n vec2 center = (cc + 0.5 + jit) * cellSize;\n\n // Orientation: the analytic swirl flow at the cell center + per-cell angular\n // jitter (so adjacent dabs FAN — the divisionist scatter that keeps the dabs from\n // locking into perfectly-concentric rows) + a flow-noise curl wobble.\n float ang = (hash21(cc + seed + 11.0) - 0.5) * angJitter;\n float curl = (fbm(center * 3.1 + seed * 1.9) - 0.5) * (0.18 + 0.3 * uFlowCurl);\n vec2 dir = rotateDir(safeDir(flow), ang + curl);\n\n // SHORT comma — len ≈ the cell pitch (a contained dab, not a tiling filament).\n float len = cellSize * lenMul * (0.82 + 0.30 * hash21(cc + seed + 23.0));\n float halfW = max(cellSize * widMul * (0.86 + 0.28 * hash21(cc + seed + 41.0)), 0.010);\n\n // Local dab frame: along = projection onto the dab axis (centred 0), perp = cross.\n vec2 rel = p - center;\n float along = dot(rel, dir);\n float perp = dot(rel, vec2(-dir.y, dir.x));\n // The comma TAPER: half-width peaks ~1/3 from the loaded head and drags to a fine\n // tail (the asymmetric crescent). u in [0,1] along the dab from head to tail.\n float halfLen = len * 0.5;\n float u = clamp((along + halfLen) / max(len, 1e-5), 0.0, 1.0);\n float head = smoothstep(0.0, 0.30, u); // rounded loaded head\n float tail = pow(clamp((1.0 - u) / 0.70, 0.0, 1.0), 1.3); // dragged thin tail\n float prof = clamp(min(head, tail) * 1.05, 0.0, 1.0);\n float wNow = halfW * prof;\n // A gentle comma CURVE — bow the spine sideways so the mark reads as a crescent.\n float bowAmt = (hash21(cc + seed + 53.0) - 0.5) * halfW * 1.4;\n float spine = bowAmt * sin(u * PI); // 0 at ends, max mid\n float d = abs(perp - spine);\n // Derivative-free AA: a fixed fraction of the dab width (no fwidth skirt). Coverage\n // is 1 in the dab body, smoothly 0 at the edge; outside the [head,tail] span wNow→0\n // so the dab self-terminates with no end-cap blob.\n float aa = max(halfW * 0.45, 0.002);\n float cov = (wNow > 1e-5) ? (1.0 - smoothstep(wNow - aa, wNow + aa, d)) : 0.0;\n if (cov < 0.004) continue;\n\n // One sampleBase per cell + OKLCh broken-color jitter (pigment patch shimmer). The\n // dab pigment is chroma-boosted (saturate3 1.22) AND value-lifted (×1.14) at the\n // SOURCE — the loaded brush mark deposits BRIGHTER + more saturated pigment than the\n // base field, so the loaded-mark pixels lift BOTH the §4.1 colourfulness AND the field\n // mean-luma. The higher mean raises the gap-fraction threshold (meanLum·0.6) so the\n // moderate-depth inter-dab ground falls below it (the gap-fraction atomicity read is\n // DECOUPLED from the ground floor depth — the ground can stay only moderately dark, so\n // the §4.3 slope is NOT over-steepened, while the bright dabs manufacture the gap). The\n // dark ground is untouched, so this lifts C + gap WITHOUT a band-conflicting deep ground.\n vec3 colMid = brokenColorJitter(\n saturate3(sampleBase(center, t), 1.22),\n hash21(cc + seed + 89.0),\n hash21(cc * 2.3 + seed + 97.0),\n 1.0\n );\n\n // edgeN: 1 at the spine, 0 at the edge (the impasto crown reads it).\n float edgeN = (wNow > 1e-5) ? clamp(1.0 - d / wNow, 0.0, 1.0) : 0.0;\n if (cov > best.coverage) {\n best = StrokeHit(cov, colMid, u, (wNow > 1e-5 ? (perp - spine) / wNow : 0.0), edgeN);\n }\n }\n }\n return best;\n}\n\nvec3 mediumVangogh(vec3 col, vec2 p, float t) {\n vec3 result = col;\n float height = 0.0;\n\n // The swirl flow basis (van-Gogh sky = swirl rows). Computed ONCE per pixel.\n vec2 flow = flowField(p, t);\n\n // Dab cell pitch in UV [0,1]. CHUNKY, SEPARABLE marks — the big layer is ~1/14 of\n // the frame so it reads as a distinct loaded brush dab (not a fine speckle, not a\n // screen-spanning filament). uStrokeScale (authored ~110) modulates the pitch\n // INVERSELY (higher = finer dabs) around the ~0.07 chunky default; clamped so the\n // dab never collapses to noise or balloons past one screen. uStrokeAnisotropy\n // stretches the comma aspect (longer, more directional marks).\n float pitch = clamp(7.0 / max(uStrokeScale, 20.0), 0.044, 0.12);\n // The dab LENGTH ≈ the cell pitch (lenAniso ~1.2-1.7) so a dab sits WITHIN its cell\n // and leaves visible ground around it — a longer dab (lenMul > 2) spans neighbour\n // cells and tiles the field into continuous coverage (no gaps, the gap-fraction floor\n // miss + the marble). Narrow width keeps it a directional crescent, not a wide shard.\n // lenAniso/widAniso — the comma aspect. The current profile lengthens\n // the comma (1.25-1.75 → 1.5-2.0) and THINNED it (0.40-0.30 → 0.32-0.24): the rebuilt\n // dabs measured §4.2 structure-tensor anisotropy A=0.699, BELOW the reference band\n // floor 0.732 (the dabs read slightly too short-fat → locally isotropic, dragging mean\n // A down). A LONGER, THINNER comma hugs the swirl-flow tangent more strongly so the\n // rendered field reads coherent the way the tensor metric measures it (A lift), while\n // the thinner width LEAVES MORE inter-dab ground (the gap-fraction atomicity read is\n // preserved, not closed). The length stays ≈ the cell pitch so each dab remains a\n // CONTAINED separable mark, not a tiling filament.\n float lenAniso = mix(2.1, 2.7, uStrokeAnisotropy); // comma length / cell pitch (a contained dab)\n float widAniso = mix(0.23, 0.16, uStrokeAnisotropy); // comma width / cell pitch (a thin loaded crescent)\n\n // Layer 1 — the big swirl-row dabs: the loaded gestural marks, SPARSE so the dark\n // underpainting shows BETWEEN them (the atomicity read — visible inter-dab ground).\n // hardness 0.74 — the loaded brush deposits with a SOFT shoulder, so adjacent dabs\n // feather instead of razor-clipping into hard polygon shards. jitterAmt 0.85 fully\n // scatters the cell centres so the placement GRID never reads as a periodic seam.\n // angJitter EASED 0.55 → 0.38 (the reconcile A lift): a tighter angular spread keeps\n // adjacent dabs more flow-aligned (coherent) while the per-cell curl wobble still\n // breaks the perfectly-concentric-rows read (the dabs FAN gently, not lock to a grid).\n // angJitter eased 0.38 → 0.26 + hardness 0.74 → 0.84 (the §4.2 A lift): a tighter angular\n // spread keeps adjacent dabs strongly flow-aligned (the structure-tensor coherence) and a\n // sharper edge gives each dab a STRONGER directional gradient (a soft dab's faint edge\n // reads weakly anisotropic). The per-cell curl wobble still breaks the perfectly-\n // concentric-rows read, so the dabs FAN gently rather than locking to a grid.\n StrokeHit dBig = vangoghDab(p, pitch, lenAniso, widAniso,\n 0.30, 0.50, 0.08, flow, t, 1.3);\n // streakFreq raised 14 → 34 (the §4.3 slope fix): a high-frequency streak runs ALONG the\n // dab spine, injecting fine-scale luma energy that flattens the too-steep β WITHOUT\n // adding new isotropic marks (the along-spine streak stays directional, so the §4.2\n // anisotropy the long-thin dab carries is preserved — the band-conflict resolution).\n paintOver(result, height, dBig, 48.0, 0.34,\n uImpasto * 0.75 * uStrokeAmount, 0.84, 1.3, 1.0 /* full crown */);\n\n // Layer 2 — two-thirds-pitch accent dabs (the divisionist sparkle between the big\n // marks — denser placement but still contained dabs, so dark canvas survives between).\n // angJitter eased 0.5 → 0.34 (same A-lift reasoning — the accent dabs queue along the\n // flow rather than scattering isotropically); hardness 0.78 → 0.86 sharpens the gradient.\n StrokeHit dSml = vangoghDab(p + vec2(-5.1, 8.4), pitch * 0.62, lenAniso * 0.95,\n widAniso * 0.95, 0.36, 0.60, 0.20, flow, t, 4.1);\n paintOver(result, height, dSml, 42.0, 0.22,\n uImpasto * 0.55 * uStrokeAmount, 0.86, 4.1, 1.0);\n\n // Layer 3 — fine divisionist flecks. The two-layer\n // field read as a few BIG cobalt dabs over a smooth empty teal ground (the side-by-side\n // against starry-night-crop.png: the painting is DENSE small varied flecks, not sparse\n // large marks). The flecks are LONG·THIN directional commas hugging the swirl tangent\n // (the A-preserving fleck — short fat flecks read isotropic and drag §4.2 down). Sparse\n // density 0.30 keeps the inter-fleck ground open (the gap-fraction atomicity read).\n // The flecks are FINE (≈0.42× pitch) directional commas, tightly flow-aligned (angJitter\n // 0.22) and SPARSE (0.30) — the divisionist sparkle between the loaded marks that keeps\n // the inter-dab ground open (atomicity) while adding the small-mark mid-band energy. Thin\n // + flow-aligned so the §4.2 anisotropy the long dabs carry survives the added small marks.\n StrokeHit dFleck = vangoghDab(p + vec2(8.7, -3.2), pitch * 0.42, lenAniso * 1.0,\n widAniso * 0.68, 0.30, 0.85, 0.22, flow, t, 7.7);\n paintOver(result, height, dFleck, 30.0, 0.16,\n uImpasto * 0.40 * uStrokeAmount, 0.80, 7.7, 1.0);\n\n // ── Flow-aligned ground swirl grooving ─────────────────────────\n // The bare ground between the dabs was a SMOOTH low-frequency teal gradient — the §4.3\n // power-spectrum slope ran too STEEP (β≈−2.0: too much low-frequency nuclei-field energy\n // vs the high band). Van Gogh's actual sky has the swirl BRUSHWORK everywhere, so groove\n // the OPEN GROUND with a flow-aligned ripple. It is SUBTRACTIVE-biased (it only darkens,\n // never brightens the ground), so it DEEPENS the inter-dab valleys (MORE gap-fraction —\n // atomicity preserved/strengthened) while injecting the across-flow mid-band luma energy\n // that flattens β. Gated to the open ground (low height) via the (1-height) gate so it\n // never lifts/erodes the loaded dab bodies (the dab read is untouched). Across the swirl\n // FLOW axis so the grooves run WITH the sky's brushwork direction.\n // The ground swirl-combing — a FIXED-global-angle directional grain on the open ground.\n // A flow-FOLLOWING comb varies its direction with the swirl across the readback window,\n // so its blurred structure tensor averages toward isotropic (no §4.2 lift). A comb at one\n // GLOBAL angle (van-Gogh's dominant diagonal stroke grain) is COHERENT over the whole\n // window ⇒ every open-ground pixel reads the SAME direction (the §4.2 mean-anisotropy lift\n // the swirl comb could not reach). Two resolved bands (≈22 + ≈44 across the global axis ⇒\n // ~21px + ~11px at 464px, both in the §4.3 fit window) also feed the β flatten. Gated to\n // the open ground (low height) so the loaded dabs are untouched; SUBTRACTIVE-biased so the\n // inter-dab valleys deepen (gap-fraction preserved, not filled).\n vec2 gdir = normalize(vec2(0.82, 0.57)); // the dominant global stroke grain axis\n float gacross = dot(p, vec2(-gdir.y, gdir.x));\n float groundGate = 1.0 - smoothstep(0.05, 0.28, height);\n float groove = sin(gacross * 22.0) + 0.5 * sin(gacross * 44.0 + 1.3);\n result *= 1.0 - 0.16 * groundGate * (0.5 - 0.5 * groove);\n\n // Canvas tooth (fine linen weave — base relief the raking light catches).\n float tooth1 = vnoise(p * 220.0);\n float tooth2 = vnoise(p * 220.0 * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * 0.06 * uCanvasGrain;\n\n // Relight the accumulated impasto height (raking light catches each dab's crown —\n // the visible impasto ridge that makes a dab read as a RAISED loaded mark).\n float canvasBase = tooth * 0.06 * 0.5;\n result = relightImpasto(result, height, canvasBase);\n\n // The visible Starry-Night GROUND — the open canvas between dabs (low accumulated\n // height) darkens toward the deep underpainting, so the bright impasto dabs read as\n // SEPARABLE atomic marks over a darker field (the dab↔ground contrast the user's\n // \"reads as strokes\" bar needs, AND the painterly-statistics gap-fraction floor — the\n // dark inter-dab ground must register as a measurable fraction of the field). 0.30\n // floor over a WIDER smoothstep (0..0.40): deep enough that the open gaps read as the\n // dark Starry-Night sky between marks (clearing the 4% gap floor) while the gradual\n // ramp keeps the gaps reading as flowing underpainting, not the hard black ink LINES a\n // narrow near-black step (0.26 / 0.0..0.32) produced.\n // The smoothstep FLOOR starts at 0.10 (not 0): a dab's faint AA coverage TAIL deposits\n // a sliver of height, and where many tails align along the swirl flow those slivers\n // accumulate into a faint continuous height ridge that read as thin curved LINES in the\n // ground. Holding the ground full-dark until a real dab CORE (height > 0.10) lifts out\n // keeps the open field clean — only the loaded dab bodies emerge, the tails stay ground.\n result *= mix(0.115, 1.0, smoothstep(0.085, 0.31, height));\n\n // The van-Gogh chroma — a light OKLCh punch at 0.92 in the\n // The discrete-dab profile's per-dab OKLCh\n // broken-color jitter (the ±16° hue + ±chroma swing per cell) lifts the field's\n // colourfulness on its own, so a 1.04 punch on TOP overshoots the\n // reference band (rendered C=100.5 vs the painting's measured 70.67 ± 15/+25 →\n // ceiling 95.67 — neon, not Starry Night's rich-but-not-garish blue/gold). The 0.92\n // demote pulls the rendered colourfulness back IN-band while the broken-color jitter\n // keeps the per-dab pigment variation (the discrete-stroke read is untouched — this\n // is a chroma scale, not a dab-geometry change). Band-anchored on the real painting\n // not a widened band.\n result = saturate3(result, 1.0);\n return result;\n}\n\n", Oe = "StrokeProfile profileFor(int medium, int mode) {\n // The oil baseline (mode 0) — balanced modern-abstract/palette-knife hybrid.\n StrokeProfile prof = StrokeProfile(\n 0, // shapeType — tapered\n 0.25, // bristleAmp\n 9.0, // streakFreq\n 0.09, // streakAmp\n 0.9, // impastoAmp\n 0.80, // hardness\n 340.0, // toothScale — finer 240 → 340. Oil's β sat\n // right AT the −1.45 band ceiling (±0.02 frame variance straddling\n // it, surfaced once the vangogh bands went green and the spec reached\n // the oil β assert). MEASURED on the live GPU: ADDING fine high-\n // frequency tooth energy steepens oil's power-spectrum slope (more\n // negative — coarsening it the other way pushed β SHALLOWER to\n // −1.33). A finer, stronger tooth moves β to a safe distance inside\n // the −5/3 band [−1.85, −1.45]. Oil-only (oil-pastel sets 280).\n 0.14, // toothAmp — raised 0.09 → 0.14 (same oil-β steepen: more fine-grain high band).\n 1.03, // pigmentSat\n 0.65, // densityBig\n 0.78, // densityMed\n 0.90, // densitySml\n 0.0, // energyGrade — oil is uniform-length (no Starry-Night cascade)\n 0.4, // impastoFloor — oil's 0.4+0.6·edgeN crown falloff\n 0.92, // densityFill — oil fills DENSELY with the directional low-relief fill so the\n // flat-colour regions between the macro strokes are TEXTURED with\n // flow-aligned fine strokes (the §4.2 anisotropy needs every pixel\n // to carry directional gradient; a flat untextured region reads as\n // an isotropic/degenerate tensor and drags mean A down). The fill\n // is now directional + low-streak, so dense coverage textures\n // WITHOUT the round-dab speckle.\n 1.0 // groundFloor — oil keeps the full base (no ground darken; fills everywhere)\n );\n\n // The Van Gogh medium does NOT route through this oil\n // StrokeProfile cascade. It is a DEDICATED atomic-dab body (mediumVangogh / vangoghDab\n // below) — a profile-driven cascade (four dense layers of long-thin\n // tensor-oriented strokes) merges into the \"marbled flow-bands\" read and runs at ~4fps.\n // profileFor now serves ONLY the oil + oil-pastel stroke mediums; the MEDIUM_VANGOGH\n // case is removed (no caller). MEDIUM_VANGOGH stays #define'd (the main() dispatch +\n // isPainterlyStroke still key off uMedium==5).\n\n // ── Oil-pastel — broad smeared directional strokes deposited via the brush engine\n // Creamy soft edges (low hardness), heavy build-up, and a chroma punch.\n // Distinct from the dry-crayon tooth-multiply (mediumCrayon): oil-pastel LAYS strokes.\n if (medium == MEDIUM_OILPASTEL) {\n prof.shapeType = 0; // tapered directional smear — a round dab reads locally\n // isotropic (steep β / low A); the creamy oil-pastel mark\n // is a broad DIRECTIONAL smear, which carries the §4.2\n // coherence + adds the directional mid-band the §4.3 slope\n // needs (the round-dab profile rolled β off to −2.8)\n prof.bristleAmp = 0.10; // smooth creamy edges (waxy pastel, not bristle)\n prof.streakFreq = 18.0; // finer internal smear — adds the mid/high-band the smooth\n // round-dab profile lacked (the β-too-steep fix); the\n // directional along-spine streak flattens the §4.3 slope\n // without crossing the strokes\n prof.streakAmp = 0.20;\n prof.impastoAmp = 0.7; // build-up, but flatter than oil impasto\n prof.hardness = 0.42; // CREAMY — soft compositing, strokes blend on overlap\n prof.toothScale = 280.0;\n prof.toothAmp = 0.12; // the pastel tooth reads stronger\n prof.pigmentSat = 0.80; // the waxy chroma — kept low so the rendered C clears\n // the §4.1 band ceiling with margin while the K-M subtractive\n // overlap path (paintOverOklab) keeps the chroma OFF the grey floor\n prof.hardness = 0.58; // creamy but the strokes still REGISTER — a too-soft blend\n // (0.42) smeared the marks into the smooth colour zones and\n // the field rolled off too steep (β≈−2.9, a near-gradient);\n // a crisper deposit keeps the mid/high-band the −5/3 slope needs\n prof.densityBig = 0.62; // broad directional coverage — but NOT over-dense: too many\n // overlapping creamy strokes point in slightly-varied\n // directions and the local §4.2 coherence DROPS (over-dense\n // measured A=0.57 < the 0.62-density A=0.63). The mid-density\n // flow-aligned deposit is the coherence sweet spot.\n prof.densityMed = 0.78;\n prof.densitySml = 0.90;\n prof.energyGrade = 0.80; // a strong Starry-Night length cascade — varies stroke length\n // by luma/coherence so the dab-size energy spreads across\n // scales (the −5/3 cascade the uniform-length 0.0 grade lacked,\n // the §4.3 β-too-steep fix). Not the full van-Gogh 1.0 (oil-\n // pastel keeps its creamy broad character).\n prof.impastoFloor = 0.55; // a softer crown than van-Gogh's full-height glint\n return prof;\n }\n\n // ── Oil sub-modes (mode dispatch on the oil medium).\n if (mode == 1) { // palette knife — razor edges, heavy impasto\n prof.shapeType = 3; // flat, even\n prof.bristleAmp = 0.12;\n prof.streakFreq = 4.0; prof.streakAmp = 0.05;\n prof.impastoAmp = 1.6;\n prof.hardness = 0.95;\n prof.toothAmp = 0.04;\n prof.densityBig = 0.80; prof.densityMed = 0.88; prof.densitySml = 0.70;\n } else if (mode == 3) { // thick brushwork — heavy bristle brush\n prof.shapeType = 0; // tapered\n prof.bristleAmp = 0.32;\n prof.streakFreq = 14.0; prof.streakAmp = 0.14;\n prof.impastoAmp = 1.2;\n prof.hardness = 0.85;\n prof.toothAmp = 0.07;\n }\n return prof;\n}\n", ke = "\n// ── Gradient pack ──────────────────────────────────\n// structureTensorField re-plumbs its already-computed luma gradient (Gx,Gy) out through\n// the vec4 return value's .w lane so the metal medium pays ZERO extra taps (a second Sobel is\n// 8 sampleBase() taps). 12 bits per bounded component ([-4,4]) into one f32, exact under\n// highp; the .xy/.z crayon/kuwahara callers are transparent to the widen.\nfloat packGrad(float gx, float gy) {\n float qx = floor(clamp(gx * 0.125 + 0.5, 0.0, 1.0) * 4095.0);\n float qy = floor(clamp(gy * 0.125 + 0.5, 0.0, 1.0) * 4095.0);\n return qx + qy * 4096.0;\n}\nvec2 unpackGrad(float packed) {\n float qy = floor(packed * (1.0 / 4096.0));\n float qx = packed - qy * 4096.0;\n return (vec2(qx, qy) * (1.0 / 4095.0) - 0.5) * 8.0;\n}\n", Ae = "#define MEDIUM_METAL 8\n#define MEDIUM_METALGRAD 9\n\n// ── Metal — two-term anisotropic BRDF over the field relief (uMedium==8/9) ──\n// Metal is a MUTUALLY-EXCLUSIVE medium (NOT a finish axis) — the field re-lights as\n// warm folded metal: the luma HEIGHT field (the packed structure-tensor gradient →\n// the surface normal) catches an anisotropic Blinn streak (brushed metal, the streak\n// runs ALONG the coherent edge-tangent) plus a sharp crest, both raked by a cursor-\n// synthesized light that reads on BOTH backends (uCursor is in-struct — a phantom\n// uLightDir read would be flat on the WGSL primary). The catch is the ACHROMATIC-WARM\n// anchor (AURORA_CATCH_LIGHT_ANCHOR ≈ [1.0,0.97,0.90], never a cold chrome-blue). The\n// body keeps the field HUE (technicolor valley base); the height rides the relief.\n// ZERO new taps — structureTensorField already computed the gradient (unpacked from .w).\nconst vec3 METAL_CATCH_WARM = vec3(1.0, 0.97, 0.90); // AURORA_CATCH_LIGHT_ANCHOR\nconst float METAL_HEIGHT_SCALE = 2.2; // gradient → normal tilt (× uMetalHeightScale)\nconst float METAL_LIGHT_Z = 0.55; // synth-light z (toward viewer)\nconst vec2 METAL_IDLE_RAKE = vec2(0.32, 0.34); // upper-right idle rake bias\nconst float METAL_SHININESS_ANISO = 6.0; // anisotropic streak exponent (brushed)\nconst float METAL_SHININESS_CREST = 40.0; // crest specular exponent (sharp)\nconst float METAL_COHERENCE_FLOOR = 0.22; // streak gates on edge coherence A\nconst float METAL_BODY_FLOOR = 0.42; // diffuse valley floor (dark side reads)\nconst float METAL_GRADIENT_FLATTEN = 0.55; // metal-gradient pre-flatten toward luma\nconst float METAL_SPARKLE_DENSITY = 240.0; // metal-gradient per-cell flake grid\nconst float METAL_SPARKLE_THRESH = 0.72; // only some cells flake\nconst float METAL_SPARKLE_AMP = 0.6; // flake glint amplitude\n\n// The shared metal BRDF core — takes the ALREADY-computed structureTensorField vec4 so\n// the caller pays ZERO extra taps (metal-gradient reuses the same stf). WebKit-fenced:\n// every pow base is clamped to [0,1], the exponents are compile-time constants.\nvec3 metalShade(vec3 baseCol, vec2 p, vec4 stf) {\n vec2 tangent = stf.xy;\n float A = stf.z;\n vec2 grad = unpackGrad(stf.w);\n // Height-field normal: N = normalize(vec3(grad * scale, 1)). The luma relief tilts\n // the surface; the flat field reads near vec3(0,0,1) (facing the viewer).\n vec3 N = normalize(vec3(grad * (METAL_HEIGHT_SCALE * uMetalHeightScale), 1.0));\n vec3 V = vec3(0.0, 0.0, 1.0);\n // Cursor-synthesized rake light (in-struct on BOTH backends — never uLightDir). The\n // idle bias keeps a still field lit from the upper-right; a moving cursor rakes it.\n vec3 L = normalize(vec3((uCursor - p) + METAL_IDLE_RAKE, METAL_LIGHT_Z));\n vec3 H = normalize(L + V);\n // Anisotropic streak — brushed metal: the highlight runs ALONG the edge-tangent, so\n // it is a function of the H-component PERPENDICULAR to the tangent (sinTH).\n float tH = dot(normalize(tangent), H.xy);\n float sinTH = sqrt(clamp(1.0 - tH * tH, 0.0, 1.0));\n float streak = pow(clamp(sinTH, 0.0, 1.0), METAL_SHININESS_ANISO);\n float crest = pow(clamp(dot(N, H), 0.0, 1.0), METAL_SHININESS_CREST);\n float spec = streak * crest;\n // Coherence gate — the anisotropic streak reads only where the field has a real edge\n // (a flat zone has no brush direction; A≈0 → no streak).\n spec *= smoothstep(0.0, METAL_COHERENCE_FLOOR, A);\n // Technicolor valley base — the body keeps the FIELD hue; the diffuse term rides the\n // height relief so the ridges brighten and the pits fall toward the floor.\n float diff = clamp(dot(N, L), 0.0, 1.0);\n vec3 body = baseCol * (METAL_BODY_FLOOR + (1.0 - METAL_BODY_FLOOR) * diff);\n // The catch is the achromatic-warm anchor, scaled by the polish knob.\n return body + METAL_CATCH_WARM * (spec * clamp(uMetalPolish, 0.0, 4.0));\n}\n\nvec3 mediumMetal(vec3 col, vec2 p, float t) {\n vec4 stf = structureTensorField(p, t, flowField(p, t));\n return metalShade(col, p, stf);\n}\n\n// metal-gradient (uMedium==9) — the SAME BRDF over a pre-flattened/gradient base + a\n// twinkle-in-place flake sparkle (position FIXED per cell, PHASE-animated, gated on\n// facing — flake glint, never a boil).\nvec3 mediumMetalGradient(vec3 col, vec2 p, float t) {\n vec4 stf = structureTensorField(p, t, flowField(p, t));\n // Pre-flatten toward luma (the smoother gradient-metallic base) — same BRDF, calmer body.\n // NB: flat is a GLSL reserved interpolation qualifier — WebKit's compiler REJECTS it as an\n // identifier (ANGLE tolerates it); named flatCol so the WebKit aurora shader compiles (L1a).\n vec3 flatCol = mix(col, vec3(dot(col, W_LUMA)), METAL_GRADIENT_FLATTEN);\n vec3 metal = metalShade(flatCol, p, stf);\n // The height-field normal facing (recomputed from the same packed gradient — ZERO taps).\n vec2 grad = unpackGrad(stf.w);\n float facing = normalize(vec3(grad * (METAL_HEIGHT_SCALE * uMetalHeightScale), 1.0)).z;\n // Twinkle-in-place: a fixed per-cell flake seed, phase-animated glint gated on facing.\n vec2 cell = floor(p * METAL_SPARKLE_DENSITY);\n float seed = hash21(cell);\n float tw = pow(clamp(sin(t + seed * 6.2831853), 0.0, 1.0), 40.0);\n float flakeGate = step(METAL_SPARKLE_THRESH, seed);\n metal += METAL_CATCH_WARM * (tw * flakeGate * clamp(facing, 0.0, 1.0) * METAL_SPARKLE_AMP);\n return metal;\n}\n", je = "// ── Medium overlays ───────────────────────────────────────────────────────\n\n// A quick re-computation of base color for edge-mask sampling\nvec3 sampleBase(vec2 p, float t) {\n vec2 pw = domainWarp(p, t);\n float id; float vm;\n nucleiField(pw, t, id, vm);\n vec3 c = samplePalette(id);\n c *= 1.0 + uValueVariance * vm;\n return c;\n}\n" + ke + "\n// ── Structure tensor / edge-tangent flow ───────────────────────────────────\n// The keystone of the painterly engine: derive stroke orientation from the COLOR\n// FIELD's OWN structure, not a hand-authored flow pattern. A 3x3 Sobel over\n// luma(sampleBase) yields the gradient (Gx,Gy); the 2x2 structure tensor\n// J = [[Gx², GxGy],[GxGy, Gy²]] eigen-decomposes closed-form into the MINOR\n// eigenvector (the edge-TANGENT — least color change, the stroke direction) + the\n// coherence A = (λ1-λ2)/(λ1+λ2); flat zones relax toward fallbackDir by (1-A). The\n// single-pass small-tap WebGL2 form. Returns vec4(tangent.xy, A, packGrad(Gx,Gy)) —\n// The .w lane carries the metal medium's gradient; the .xy/.z callers are unchanged.\nvec4 structureTensorField(vec2 p, float t, vec2 fallbackDir) {\n float e = 0.0035; // small-tap neighborhood (edge-mask scale)\n // 3x3 luma samples.\n float l00 = dot(sampleBase(p + vec2(-e, -e), t), W_LUMA);\n float l10 = dot(sampleBase(p + vec2( 0.0, -e), t), W_LUMA);\n float l20 = dot(sampleBase(p + vec2( e, -e), t), W_LUMA);\n float l01 = dot(sampleBase(p + vec2(-e, 0.0), t), W_LUMA);\n float l21 = dot(sampleBase(p + vec2( e, 0.0), t), W_LUMA);\n float l02 = dot(sampleBase(p + vec2(-e, e), t), W_LUMA);\n float l12 = dot(sampleBase(p + vec2( 0.0, e), t), W_LUMA);\n float l22 = dot(sampleBase(p + vec2( e, e), t), W_LUMA);\n // Sobel derivatives.\n float Gx = (l20 + 2.0 * l21 + l22) - (l00 + 2.0 * l01 + l02);\n float Gy = (l02 + 2.0 * l12 + l22) - (l00 + 2.0 * l10 + l20);\n // Structure tensor components.\n float Jxx = Gx * Gx;\n float Jyy = Gy * Gy;\n float Jxy = Gx * Gy;\n // Closed-form 2x2 eigenvalues: λ = 0.5(tr ± sqrt((Jxx-Jyy)² + 4Jxy²)).\n float tr = Jxx + Jyy;\n float disc = sqrt(max((Jxx - Jyy) * (Jxx - Jyy) + 4.0 * Jxy * Jxy, 0.0));\n float lambdaMaj = 0.5 * (tr + disc);\n float lambdaMin = 0.5 * (tr - disc);\n // The MAJOR eigenvector points along the gradient (the edge NORMAL); the MINOR\n // is its perpendicular — the edge TANGENT (the stroke direction). The principal\n // angle is θ = 0.5·atan2(2·Jxy, Jxx-Jyy); this form is robust at Jxy≈0 (where the\n // (λ-Jyy, Jxy) eigenvector formula collapses to a zero vector). The tangent is at\n // θ + 90°. Guard the fully-isotropic (no-gradient) degenerate case → fallback dir.\n vec2 tangent;\n if (disc < 1e-7) {\n tangent = fallbackDir;\n } else {\n float theta = 0.5 * atan(2.0 * Jxy, Jxx - Jyy); // major (gradient) angle\n tangent = vec2(-sin(theta), cos(theta)); // perpendicular = tangent\n }\n // Coherence A in [0,1] — high where one eigenvalue dominates (a strong edge),\n // ~0 in flat zones. Relax toward the smooth fallback by (1 - A).\n float A = (lambdaMaj - lambdaMin) / (lambdaMaj + lambdaMin + 1e-6);\n A = clamp(A, 0.0, 1.0);\n // Coherence-weighted blend: full tensor in coherent zones, smooth in flat ones.\n // Resolve the 180° sign ambiguity toward the fallback so the blend is continuous.\n if (dot(tangent, fallbackDir) < 0.0) tangent = -tangent;\n // The blend weight is a low-power curve (A^0.35) so even MODERATE-coherence zones queue\n // strongly along the tangent (a flowing van-Gogh band reads coherent the way the\n // structure-tensor metric measures it; a linear A under-orients the mid-band and the\n // rendered field reads too isotropic — the §4.2 anisotropy lift). The flat-zone (A≈0)\n // relax to the fallback survives (0^0.35=0), so tensor noise never reads as jitter.\n float blendW = pow(A, 0.28);\n vec2 dir = normalize(mix(fallbackDir, tangent, blendW) + 1e-6);\n return vec4(dir, A, packGrad(Gx, Gy));\n}\n\nvec3 mediumWatercolor(vec3 col, vec2 p, float t) {\n // Wet-edge cauliflowers via luma-gradient magnitude\n float eps = 0.004;\n vec3 cx1 = sampleBase(p + vec2(eps, 0.0), t);\n vec3 cx2 = sampleBase(p - vec2(eps, 0.0), t);\n vec3 cy1 = sampleBase(p + vec2(0.0, eps), t);\n vec3 cy2 = sampleBase(p - vec2(0.0, eps), t);\n float gx = dot(cx1 - cx2, W_LUMA);\n float gy = dot(cy1 - cy2, W_LUMA);\n float edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n float mask = smoothstep(0.0, 2.5, edge);\n col *= mix(1.0, 0.78, mask * uWetEdge);\n\n // Granulation — pigment settles in paper tooth\n float paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n float pigLoad = 1.0 - dot(col, W_LUMA);\n col *= 1.0 - uGranulation * pigLoad * (paper - 0.5);\n\n // Wash banding — faint horizontal wet gradient\n float band = fbm(vec2(p.x * 1.5, p.y * 0.4));\n col *= 1.0 + 0.04 * (band - 0.5);\n return col;\n}\n\nvec3 mediumPastel(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n vec2 perp = vec2(-flow.y, flow.x);\n float along = dot(p, flow) * uStrokeScale;\n float across = dot(p, perp) * uStrokeScale;\n across += 0.03 * (vnoise(p * 260.0) - 0.5);\n\n float aniso = mix(1.0, 0.18, uStrokeAnisotropy);\n float stroke = fbm(vec2(along * aniso, across));\n col *= mix(1.0, 0.82 + 0.32 * stroke, uStrokeAmount);\n\n // Pastel tooth — tiny high-frequency grain\n float tooth = vnoise(p * 800.0);\n col *= 1.0 - 0.08 * uStrokeAmount * (tooth - 0.5);\n return col;\n}\n", Me = "// ── Medium id constants (mirror uniformBridge.ts MEDIUM_ID — the uMedium ladder) ──\n// The StrokeProfile selector dispatches on the medium id: oil,\n// van-Gogh, and oil-pastel each AUTHOR a first-class profile in profileFor.\n#define MEDIUM_OIL 3\n#define MEDIUM_CRAYON 4\n#define MEDIUM_VANGOGH 5\n#define MEDIUM_OILPASTEL 6\n\n// ── Crayon — DRY wax pigment on paper tooth (first-class medium, uMedium==4) ──\n// Crayon is not strokes. It's dry pigment crumbs dragged across paper tooth — the\n// DRY tooth-multiply model: anisotropic tooth noise multiplied into the base, a hard\n// SCUMBLE that lets the lower color show through the broken upper layer, and stable\n// OKLCh broken-color pigment patches. NO sheen, NO burnish film — that waxy gloss is\n// the OIL-PASTEL deposition's signature (mediumOilPastel), distinct from dry crayon\n// split. Crayon shares the substrate (the structure-tensor\n// orientation, the tooth noise, OKLCh brokenColorJitter), not the dispatch body.\nvec3 mediumCrayon(vec3 col, vec2 p, float t) {\n // Orient the deposition along the structure-tensor edge-tangent (the bridge forces\n // tensor for crayon, so read the tensor field directly).\n vec2 flow = structureTensorField(p, t, flowField(p, t)).xy;\n float ang = atan(flow.y, flow.x);\n float ca = cos(-ang), sa = sin(-ang);\n vec2 pr = vec2(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n float aniso = mix(0.45, 0.95, uStrokeAnisotropy);\n float scale = max(uStrokeScale * 1.6, 180.0);\n\n // ── Paper TOOTH height field — the relief the dry pigment rides. Anisotropic\n // (squished along flow, coarse cross-flow). Peaks = ridges, valleys = paper pits.\n float t1 = vnoise(vec2(pr.x * scale * aniso, pr.y * scale));\n float t2 = vnoise(vec2(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + 11.0);\n float t3 = vnoise(vec2(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + 23.0);\n float tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n // ── DRY TOOTH-MULTIPLY — the crayon multiplies the tooth relief into the base\n // color (dark in the pits, bright on the ridges). The pressure (uStrokeAmount) sets\n // how hard the multiply bites. This is the DRY model — no pigment build-up, no sheen.\n float pressure = clamp(uStrokeAmount, 0.0, 1.0);\n float bite = mix(0.18, 0.46, pressure); // multiply strength\n vec3 result = col * (1.0 - bite * (0.5 - tooth));\n\n // ── HARD SCUMBLE — a broken upper layer that lets the lower color show through the\n // gaps. A coarse mask gates a HARD (high-contrast) dragged pass so the dry crayon\n // reads as broken pigment crumbs, not a smooth wash. Drag follows the tensor flow.\n float scumbleMask = smoothstep(0.42, 0.78, vnoise(vec2(pr.x * scale * 0.55, pr.y * scale * 0.22) + 7.0));\n float scumbleCoverage = scumbleMask * pressure * 0.7; // hard broken upper layer\n vec3 paper = col * mix(0.80, 0.94, pressure); // the paper showing through\n result = mix(paper, result, scumbleCoverage);\n\n // ── Broken-color pigment: stable wax/pigment patches in OKLCh, not flicker.\n vec2 pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n float pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + 19.0));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + 17.0),\n hash21(pigmentCell * 2.1 + 31.0),\n 0.45 + 0.55 * pigmentMask\n );\n\n // Crayon is saturation-amplified (the wax chroma punch — but DRY, no sheen).\n result = saturate3(result, 1.08);\n\n return result;\n}\n\n// ── StrokeProfile — the oil-stroke parameter vector ────────────────────────\n// The SBR \"stroke = parameter vector\" canon (facet 11): the per-mode if-ladder knobs\n// become struct FIELDS — logic-as-DATA, not an imperative branch. profileFor(medium,\n// mode) populates the profile for a (medium, mode) pair; paintStrokeLayers(profile) runs\n// the four-layer bestOil/paintOver cascade off it. A new medium, such as van-Gogh or\n// oil-pastel, authors a profile entry; it never edits a monolith.\nstruct StrokeProfile {\n int shapeType; // 0 tapered, 1 load-drag, 2 dab, 3 even, 4 comma/crescent\n float bristleAmp; // edge raggedness 0..0.5\n float streakFreq; // internal-streak spatial frequency\n float streakAmp; // internal-streak amplitude\n float impastoAmp; // paint-thickness contribution (the relight reads the height)\n float hardness; // edge-compositing crispness 0..1\n float toothScale; // canvas-tooth (linen weave) spatial frequency\n float toothAmp; // canvas-tooth amplitude\n float pigmentSat; // pigment saturation boost (OKLCh chroma scale)\n float densityBig; // layer-1 (big gestural) placement density gate\n float densityMed; // layer-2 (medium body) placement density gate\n float densitySml; // layer-3 (small dabs) placement density gate\n float energyGrade; // SBR energy-grade magnitude (0 off; 1 the van-Gogh\n // Starry-Night length cascade). The energy grade is a PROFILE\n // field, NOT a buried uMedium==5 bestOil branch.\n float impastoFloor;// Per-stroke height-crown floor (0.4 oil falloff;\n // 1.0 van-Gogh FULL-height crown so each dab catches its glint).\n float densityFill; // Layer-4 fill-dab density. Oil/oil-pastel fill the\n // bald spots (0.95, full coverage); van-Gogh sets it LOW so the\n // sparse atomic dabs keep their visible inter-stroke ground gaps\n // (the atomicity read — a 0.95 fill would close every gap to a smear).\n float groundFloor; // Bare-ground darken floor. The gaps between strokes\n // (low accumulated height) multiply toward this floor, so van-Gogh's\n // sparse atomic dabs read over a visibly darker underpainting (the\n // Starry-Night visible ground). Oil/oil-pastel keep 1.0 (no darken).\n};\n\n// The (medium, mode) -> StrokeProfile selector. The if-ladder's knobs are the profile's\n// fields; a new medium adds a CASE here (never an edit to a dispatch body). mode is the\n// uStrokeMode oil-stroke sub-mode (0 oil, 1 palette-knife, 3 brushwork). crayon\n// (uMedium==4) is a PEER tooth-multiply medium that never reaches the stroke cascade.\n// The painterly STROKE mediums are oil (MEDIUM_OIL), van-Gogh (MEDIUM_VANGOGH) and\n// oil-pastel (MEDIUM_OILPASTEL) — each AUTHORS its own profile entry below.\n" + Oe + "\n// The single parameterized four-layer stroke cascade. The four hand-unrolled\n// bestOil/paintOver invocations collapse into ONE body driven by the profile + the\n// uniform-derived per-layer scale/anisotropy multipliers (which are mode-INVARIANT — the\n// per-layer offsets, seeds, and len/wid muls are fixed across modes, so they stay here as\n// the cascade's structure; only the profile's knobs differentiate the medium). The\n// mode passes through for the two mode-special-cased shapes (knife layer-3 dabs +\n// layer-4 fill shape), which are placement details of the cascade, not profile knobs.\nvoid paintStrokeLayers(inout vec3 col, inout float height, StrokeProfile prof,\n int mode, vec2 p, float t) {\n // Scales & multipliers from uniforms (mode-invariant cascade structure).\n float baseScale = max(uStrokeScale * 0.006, 0.008);\n // Three primary layers: big gestural, medium body, small dabs.\n // The −5/3 radii respacing was measured and reverted, not\n // silently struck). The φ-adjacent geometric candidate (2.4, 1.45, 0.87, a fixed\n // ≈1.66× step) moved oil-pastel β −2.534 → −2.413 — TOWARD the −5/3 band but NOT into\n // it (floor −1.85), so the keep-iff-into-band gate failed. The single-pass WebGL2 path\n // is the true limiter (HC-aurora §5): the oil-pastel β + the oil/oil-pastel anisotropy\n // residual stays the oil/oil-pastel stroke-cascade ceiling. The soft\n // anisotropic-Kuwahara finish therefore remains the opt-in mediumKuwahara() body\n // (uMedium==7) the consumer selects — NOT a re-tune of these oil/oil-pastel knobs. The\n // hand-set 2.4, 1.1, 0.45 stays — it is van-Gogh's landed-band spacing.\n float sBig = baseScale * 2.4;\n float sMed = baseScale * 1.1;\n float sSml = baseScale * 0.45;\n\n // Strokes are elongated HARD (long·thin) so the rendered field reads strongly\n // directional — the §4.2 structure-tensor anisotropy lift. A short fat dab is locally\n // isotropic and drags the measured A down; a long thin stroke that hugs the tensor\n // tangent is what gives Starry Night its A≈0.83. The width muls are pulled tighter to\n // keep the long·thin aspect.\n float lenMulBig = mix(3.0, 5.2, uStrokeAnisotropy);\n float widMulBig = mix(0.42, 0.22, uStrokeAnisotropy);\n float lenMulMed = mix(2.8, 4.8, uStrokeAnisotropy);\n float widMulMed = mix(0.40, 0.21, uStrokeAnisotropy);\n float lenMulSml = mix(2.2, 3.6, uStrokeAnisotropy);\n float widMulSml = mix(0.36, 0.23, uStrokeAnisotropy);\n\n float jitterAmt = 0.75; // large jitter — no grid\n vec2 flow = flowField(p, t);\n\n // Layer 1 — big gestural strokes (sparse, shaping)\n StrokeHit hBig = bestOil(p, sBig, lenMulBig, widMulBig, jitterAmt * 0.55,\n prof.densityBig, prof.shapeType, prof.bristleAmp, flow, t, 1.3,\n prof.energyGrade);\n paintOver(col, height, hBig, prof.streakFreq * 0.7, prof.streakAmp,\n uImpasto * prof.impastoAmp * uStrokeAmount, prof.hardness, 1.3, prof.impastoFloor);\n\n // Layer 2 — medium body strokes\n StrokeHit hMed = bestOil(p + vec2(11.3, 3.7), sMed, lenMulMed, widMulMed,\n jitterAmt, prof.densityMed, prof.shapeType, prof.bristleAmp, flow, t, 2.7,\n prof.energyGrade);\n paintOver(col, height, hMed, prof.streakFreq, prof.streakAmp,\n uImpasto * prof.impastoAmp * uStrokeAmount, prof.hardness, 2.7, prof.impastoFloor);\n\n // Layer 3 — small strokes (more frequent, smaller). Even the knife layer-3 stays the\n // medium's DIRECTIONAL shape, not a round dab — the round-dab sparkle was a fine-scale\n // isotropic speckle that dragged the §4.2 anisotropy down + rolled the §4.3 slope shallow.\n int smlShape = prof.shapeType;\n StrokeHit hSml = bestOil(p + vec2(-5.1, 8.4), sSml, lenMulSml, widMulSml,\n jitterAmt * 0.9, prof.densitySml, smlShape,\n prof.bristleAmp * 0.85, flow, t, 4.1, prof.energyGrade);\n paintOver(col, height, hSml, prof.streakFreq * 1.4, prof.streakAmp * 0.6,\n uImpasto * prof.impastoAmp * 0.45 * uStrokeAmount, prof.hardness, 4.1, prof.impastoFloor);\n\n // Layer 4 — fill strokes (dense, fine) — cover bald spots. The density is\n // PROFILE-driven (prof.densityFill): oil/oil-pastel fill to full coverage; van-Gogh\n // keeps it sparse so the bare-ground gaps between its atomic dabs survive. The fill is\n // ELONGATED + flow-oriented (NOT a round dab) — a dense round-dab fill is locally\n // isotropic and was the dominant §4.2 anisotropy sink; an oriented fine stroke keeps the\n // fill directional so the whole field reads coherent.\n float sFill = baseScale * 0.22;\n float lenMulFill = mix(2.0, 3.2, uStrokeAnisotropy);\n float widMulFill = mix(0.34, 0.24, uStrokeAnisotropy);\n int fillShape = (mode == 1) ? 3 : prof.shapeType; // knife=even; others inherit the medium's directional shape\n StrokeHit hFill = bestOil(p + vec2(3.9, -6.2), sFill, lenMulFill, widMulFill,\n jitterAmt * 0.8, prof.densityFill, fillShape,\n prof.bristleAmp * 0.6, flow, t, 8.9, prof.energyGrade);\n // The fill is a low-relief, low-streak base coat — it covers bald spots WITHOUT adding\n // fine bright speckle (a high-impasto/high-streak fill caught a per-dab glint that read\n // as isotropic noise over the directional macro strokes — the §4.2/§4.3 sink). Its\n // positional jitter is tamed too so the fill strokes stay flow-aligned, not scattered.\n paintOver(col, height, hFill, prof.streakFreq * 1.2, prof.streakAmp * 0.35,\n uImpasto * prof.impastoAmp * 0.18 * uStrokeAmount, prof.hardness * 0.9, 8.9, prof.impastoFloor);\n\n // Optional crosshatch layer\n if (uStrokeLayers == 2) {\n vec2 flow2 = vec2(-flow.y, flow.x);\n StrokeHit hX = bestOil(p + vec2(7.3, -2.1), sMed, lenMulMed * 0.9, widMulMed,\n jitterAmt, prof.densityMed * 0.7, prof.shapeType, prof.bristleAmp, flow2, t, 6.5,\n prof.energyGrade);\n paintOver(col, height, hX, prof.streakFreq, prof.streakAmp * 0.85,\n uImpasto * prof.impastoAmp * 0.55 * uStrokeAmount, prof.hardness, 6.5, prof.impastoFloor);\n }\n}\n\n// The shared stroke-medium substrate finish paints the four-layer\n// cascade off the profile, then canvas-tooth + relight + saturation. The three painterly\n// STROKE mediums (oil, van-Gogh, oil-pastel) each AUTHOR their own profile and call\n// this — the substrate is shared, the PROFILE (not a dispatch-body fork) differentiates\n// the medium. mode passes through for the cascade's mode-special-cased shapes.\nvec3 paintStrokeMedium(vec3 col, vec2 p, float t, StrokeProfile prof, int mode) {\n vec3 result = col;\n // Accumulated paint height across the stroke layers. The relight reads\n // its gradient for the normal; the canvas tooth seeds the base relief.\n float height = 0.0;\n paintStrokeLayers(result, height, prof, mode, p, t);\n\n // Canvas tooth — linen weave\n float tooth1 = vnoise(p * prof.toothScale);\n float tooth2 = vnoise(p * prof.toothScale * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * prof.toothAmp * uCanvasGrain;\n\n // Relight the accumulated paint height with the movable uLightDir\n // source (diffuse + Blinn specular, in LINEAR before aces()). The canvas tooth\n // is the base relief term so the weave also catches the raking light.\n float canvasBase = tooth * prof.toothAmp * 0.5;\n result = relightImpasto(result, height, canvasBase);\n\n // Preserve visible ground. The bare-ground gaps (low accumulated stroke height)\n // multiply toward prof.groundFloor, so van-Gogh's sparse atomic dabs read as separable\n // marks over a darker underpainting (oil/oil-pastel keep groundFloor=1.0 → no-op). The\n // smoothstep keeps the impasto dabs (high height) at full value; only the open ground\n // between them darkens.\n result *= mix(prof.groundFloor, 1.0, smoothstep(0.0, 0.38, height));\n\n // Pigment saturation boost\n result = saturate3(result, prof.pigmentSat);\n\n return result;\n}\n\nvec3 mediumOil(vec3 col, vec2 p, float t) {\n // The oil medium is a thin body: fetch the oil profile (mode-dispatched), paint via\n // the shared stroke substrate. uStrokeMode: 0 oil (gestural), 1 knife, 3 brushwork.\n int mode = uStrokeMode;\n StrokeProfile prof = profileFor(MEDIUM_OIL, mode);\n return paintStrokeMedium(col, p, t, prof, mode);\n}\n\n#define MEDIUM_KUWAHARA 7\n\n// ── Kuwahara — anisotropic generalized painterly finish (uMedium==7) ───────\n// The SOTA edge-preserving painterly smoothing: the generalized/anisotropic Kuwahara of\n// Kyprianidis 2010, the SOFT polynomial-weighted variant (NOT the pre-2010 hard argmin,\n// which BANDS the flat field into an 8-spoke pinwheel — the §4.2 anti-regression). Aurora\n// is a PROCEDURAL field (no input texture), so the operator runs over sampleBase() directly:\n// an elliptical kernel oriented along the structure tensor (elongated along the edge-tangent,\n// narrowed by the anisotropy), divided into 8 overlapping sectors via smooth weights; the\n// output is the variance-weighted blend of the sector means (1/(1+var^q) — low-variance\n// sectors dominate, the SOFT criterion → flat oil-paint patches with crisp zone boundaries).\n// Single-pass small-radius WebGL2 form: 4 rings × 8 angular taps = 32 procedural samples\n// (cost-bounded; the offscreen-park, PRM freeze the substrate owns is untouched).\nvec3 mediumKuwahara(vec3 col, vec2 p, float t) {\n // Orient the kernel along the structure-tensor edge-tangent + read the coherence A\n // (the field widened vec3→vec4 for metal's gradient; Kuwahara reads only .xy/.z).\n vec4 stf = structureTensorField(p, t, flowField(p, t));\n vec2 tangent = stf.xy;\n float A = stf.z;\n // The ellipse axes: major along the tangent, minor across. Anisotropy squeezes the\n // minor axis (coherent zones → long-thin patch along the colour band; flat zones →\n // near-circular so the operator degrades gracefully to the isotropic Kuwahara). The\n // base radius rides uStrokeScale so the painterly patch size tracks the studio knob.\n float radius = clamp(0.010 + uStrokeScale * 0.00018, 0.006, 0.024);\n float aniso = mix(1.0, 0.34, clamp(A, 0.0, 1.0)); // minor/major axis ratio\n vec2 ax = tangent; // major-axis unit vector (edge-tangent)\n vec2 ay = vec2(-tangent.y, tangent.x); // minor-axis unit vector (edge-normal)\n\n // 8 sectors, soft polynomial weights. Each sector i centers on angle i·45°; a sample's\n // sector weight is a smooth gaussian-of-angular-distance so sectors OVERLAP (no hard\n // argmin → no pinwheel banding). The center sample seeds all sectors equally.\n const int SECTORS = 8;\n vec3 m[8]; // per-sector weighted colour sum\n vec3 s[8]; // per-sector weighted colour² sum (for variance)\n float w[8]; // per-sector weight sum\n for (int i = 0; i < SECTORS; i++) { m[i] = vec3(0.0); s[i] = vec3(0.0); w[i] = 0.0; }\n\n // 4 rings × 8 angular taps over the elliptical disc. Each tap: sample the procedural\n // field, then deposit into the sectors by the soft angular weight × the radial falloff.\n const int RINGS = 4;\n const int TAPS = 8;\n for (int r = 1; r <= RINGS; r++) {\n float rr = float(r) / float(RINGS); // 0.25 .. 1.0\n float radial = 1.0 - 0.6 * rr; // inner taps weigh more (gaussian-ish)\n for (int a = 0; a < TAPS; a++) {\n float ang = 6.2831853 * float(a) / float(TAPS);\n // Elliptical offset: major axis full radius, minor axis squeezed by aniso.\n vec2 dirE = ax * cos(ang) + ay * (sin(ang) * aniso);\n vec2 off = dirE * (radius * rr);\n vec3 c = sampleBase(p + off, t);\n // The sample's angular bin (its sector) — soft, so it spills into neighbours.\n for (int k = 0; k < SECTORS; k++) {\n float sectorAng = 6.2831853 * float(k) / float(SECTORS);\n // Smallest signed angular distance to the sector center.\n float d = ang - sectorAng;\n d = atan(sin(d), cos(d)); // wrap to [-π, π]\n float sw = exp(-d * d * 2.2) * radial; // gaussian angular weight × radial\n m[k] += c * sw;\n s[k] += c * c * sw;\n w[k] += sw;\n }\n }\n }\n\n // Per-sector mean + scalar luma-variance, then the SOFT variance-weighted blend.\n vec3 accum = vec3(0.0);\n float accumW = 0.0;\n for (int k = 0; k < SECTORS; k++) {\n float wk = max(w[k], 1e-4);\n vec3 mean = m[k] / wk;\n vec3 var3 = max(s[k] / wk - mean * mean, vec3(0.0));\n float variance = dot(var3, W_LUMA); // perceptual scalar variance\n // SOFT criterion: low-variance sectors (flat, inside a colour zone) dominate; a\n // high-variance sector (straddling an edge) is suppressed. q controls the sharpness;\n // a finite q (not argmin) keeps the blend continuous → no pinwheel spoke.\n float sectorW = 1.0 / (1.0 + pow(variance * 320.0, 4.0));\n accum += mean * sectorW;\n accumW += sectorW;\n }\n vec3 result = accum / max(accumW, 1e-4);\n\n // The center sample anchors the result in near-flat zones (where every sector reads the\n // same colour) so the operator is a no-op there — the smoothing only BITES at edges.\n vec3 center = sampleBase(p, t);\n result = mix(center, result, clamp(0.35 + 0.65 * A, 0.0, 1.0));\n\n // A faint canvas tooth + pigment-saturation lift so the flattened patches read as\n // oil-paint, not a posterized blur (the painterly identity the smoothing alone lacks).\n float tooth = vnoise(p * 300.0) - 0.5;\n result *= 1.0 + tooth * 0.06 * uCanvasGrain;\n result = saturate3(result, 1.04);\n\n // Blend toward the incoming col by uStrokeAmount so the studio amount knob controls\n // the finish strength (uStrokeAmount=0 → the raw field passes through; default → full).\n return mix(col, result, clamp(uStrokeAmount, 0.0, 1.0));\n}\n\n" + Ae + De + "// The oil-pastel medium (uMedium==6) is a distinct stroke-deposition body,\n// separate from the dry-crayon tooth-multiply (mediumCrayon). It DEPOSITS\n// broad smeared directional strokes via the same brush engine with a creamy soft\n// hardness, heavy pigment build-up where strokes overlap, and a chroma punch — the\n// stroke-deposition model that gives oil-pastel its depth and less-uniform read.\n// Shares the SUBSTRATE (the stroke cascade + tooth + relight) with oil/van-Gogh, NOT\n// the dispatch body of dry crayon. The bridge forces tensor orientation.\nvec3 mediumOilPastel(vec3 col, vec2 p, float t) {\n StrokeProfile prof = profileFor(MEDIUM_OILPASTEL, 0);\n return paintStrokeMedium(col, p, t, prof, 0);\n}", L = "\n", Ne = `#version 300 es
15
122
  precision highp float;
16
123
 
17
124
  in vec2 vUv;
18
125
  out vec4 fragColor;
19
126
 
20
- #define MAX_NUCLEI 6
127
+ #define MAX_NUCLEI 8
21
128
  #define MAX_STOPS 8
22
129
 
23
130
  // ── Uniforms ───────────────────────────────────────────────────────────────
@@ -36,7 +143,7 @@ uniform float uNucleiValueBias[MAX_NUCLEI];
36
143
  uniform float uNucleiDriftRadius[MAX_NUCLEI];
37
144
  uniform float uNucleiDriftPhase[MAX_NUCLEI];
38
145
  // Anisotropy: per-nucleus elongation (1.0 = isotropic) and major-axis angle (radians).
39
- // Defaults (1.0 / 0.0) reduce to the original circular Gaussian.
146
+ // Defaults (1.0, 0.0) reduce to the original circular Gaussian.
40
147
  uniform float uNucleiElong[MAX_NUCLEI];
41
148
  uniform float uNucleiAngle[MAX_NUCLEI];
42
149
  uniform float uSoftmaxBeta;
@@ -46,37 +153,31 @@ uniform float uValueVariance;
46
153
  uniform float uWarpAmount;
47
154
  uniform float uWarpScale;
48
155
  uniform float uWarpDrift;
49
- uniform int uWarpMode; // 0=fbm 1=cellular 2=hybrid 3=curl (BB.B1 — opt-in Bridson flow warp)
156
+ uniform int uWarpMode; // 0=fbm 1=cellular 2=hybrid 3=curl (opt-in Bridson flow warp)
50
157
  uniform int uNoiseOctaves;
51
158
 
52
159
  // Medium
53
160
  // 0 smooth, 1 pastel, 2 watercolor, 3 oil, 4 crayon (peer medium — wax pigment
54
161
  // on paper tooth; NOT an oil-stroke sub-mode, so it dispatches at main() level).
55
162
  uniform int uMedium;
56
- // Hue-arc method for the OKLCh palette interpolation (W5). Mirrors value.js's
163
+ // Hue-arc method for OKLCh palette interpolation. Mirrors value.js's
57
164
  // HueInterpolationMethod: 0 shorter, 1 longer, 2 increasing, 3 decreasing. Only
58
165
  // consulted when the hue-arc path is requested; OKLab-rectangular ramps (the
59
166
  // default for adjacent-hue stops) ignore it.
60
167
  uniform int uHuePath;
61
- uniform int uFlowPattern; // 0 none, 1 radial, 2 swirl, 3 diagonal, 4 multi, 5 tensor/ETF (W4.1)
168
+ uniform int uFlowPattern; // 0 none, 1 radial, 2 swirl, 3 diagonal, 4 multi, 5 tensor/ETF
62
169
  uniform vec2 uFlowFocal;
63
170
  uniform float uFlowAngle;
64
171
  uniform float uFlowCurl;
65
172
  uniform vec2 uCursor; // in 0..1 screen space (matches pN)
66
173
  uniform float uCursorStrength; // 0..1 attraction amount
67
174
  uniform float uCursorRadius; // radius of influence (0.05..0.5)
68
- // AW.W8.1 — the velocity-reactive flow: a fast flick injects a transient swirl-burst
69
- // (uCursorBurst, decaying over ~1s) along the pointer velocity (uCursorVelocity),
70
- // distinct from the steady uCursorStrength attraction. Both are PRM-gated (the cursor
71
- // write-path early-outs on reduced-motion, and the master tempo scalar zeroes them).
72
- uniform vec2 uCursorVelocity; // smoothed pointer velocity (normalized-screen/move)
73
- uniform float uCursorBurst; // 0..1 transient flick burst
74
175
  uniform float uStrokeAmount;
75
176
  uniform float uStrokeScale;
76
177
  uniform float uStrokeAnisotropy;
77
178
  uniform int uStrokeLayers; // 1 or 2 (crosshatch)
78
179
  uniform int uStrokeMode; // 0 oil (modern gestural), 1 palette-knife, 3 modern-chunky (crayon is uMedium==4, a peer medium)
79
- // AW.W4.1 — the stroke-orientation source: 0 flow (hand-authored flowField), 1
180
+ // Stroke-orientation source: 0 flow (hand-authored flowField), 1
80
181
  // tensor (the structure-tensor minor eigenvector — the color field's edge-tangent).
81
182
  uniform int uStrokeOrient;
82
183
  uniform float uWetEdge;
@@ -85,17 +186,16 @@ uniform float uImpasto;
85
186
  uniform float uBrokenColor;
86
187
  uniform float uCanvasGrain;
87
188
 
88
- // BG.W-AUR-METAL-FINISH — the metal-medium knobs (uMedium==8/9). uMetalPolish scales
189
+ // Metal-medium knobs (uMedium==8/9). uMetalPolish scales
89
190
  // the specular catch intensity; uMetalHeightScale scales the luma-relief → normal tilt.
90
191
  // On the WGSL primary these ride the free cursor.z/.w pad lanes (uniformBridgeWGPU); the
91
- // .frag carries them as their own uniforms. Written 0 on a non-metal config → no-op.
192
+ // The fragment shader carries them as uniforms. Non-metal configs write 0.
92
193
  uniform float uMetalPolish;
93
194
  uniform float uMetalHeightScale;
94
195
 
95
- // AW.W4.2 — the impasto relight axis. A movable directional source lighting the
196
+ // The impasto relight axis is a movable directional source lighting the
96
197
  // accumulated paint-height field (diffuse + Blinn specular, in LINEAR before aces()).
97
- // AW.W8 drives uLightDir from the cursor (cursor-as-light). Default = upper-left so
98
- // the still default reads identically to the prior fixed rim.
198
+ // The cursor drives uLightDir; the upper-left default supplies the still pose.
99
199
  uniform vec3 uLightDir; // unit direction (x,y in screen space, z toward viewer)
100
200
  uniform vec3 uLightColor; // warm-white tint (linear)
101
201
 
@@ -110,7 +210,7 @@ uniform float uSaturation;
110
210
  uniform float uPaperGrain;
111
211
  uniform float uAlpha;
112
212
 
113
- // BD.W-AUR-VIVIDNESS — the §3 chroma-floor strength (0 = off / pre-floor identity,
213
+ // Chroma-floor strength (0 = off,
114
214
  // 1 = the vivid default). The shader lifts any pale zone's OKLab chroma toward
115
215
  // uVividness * VIVID_TARGET, hue-preserving, warm-anchored below the near-gray epsilon.
116
216
  uniform float uVividness;
@@ -120,7 +220,7 @@ uniform float uVividness;
120
220
  // human-friendly 0..~0.05 band — that scale is what the config schema and every
121
221
  // demo preset are tuned against. But that band, multiplied straight into uTime
122
222
  // (seconds), yields rad/sec rates so small the field reads visually static (one
123
- // nuclei orbit took ~10 min at the old default). These K_* constants decouple
223
+ // nuclei orbit would otherwise take minutes). These K_* constants decouple
124
224
  // the AUTHORING scale from the RAD/SEC scale: each time term wraps its coefficient
125
225
  // in the matching K_, lifting the same authored value to a perceptible period
126
226
  // without touching any preset. Tuned so the field reads SLOWLY ALIVE — drift over
@@ -155,10 +255,10 @@ float vnoise(vec2 p) {
155
255
  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
156
256
  }
157
257
 
158
- // The rotated-octave FBM rotation constant — spliced from the shared chunk (AV.W2
159
- // — the one FBM_ROT). The loop below stays aurora-local (2.02 lacunarity +
258
+ // The rotated-octave FBM rotation constant is spliced from the shared chunk
259
+ // as the sole FBM_ROT. The loop below stays aurora-local (2.02 lacunarity +
160
260
  // uniform-driven octaves, per §3a — only the rotation constant converges).
161
- ${ee}
261
+ ${p}
162
262
 
163
263
  float fbm(vec2 p) {
164
264
  float v = 0.0;
@@ -172,52 +272,51 @@ float fbm(vec2 p) {
172
272
  return v;
173
273
  }
174
274
 
175
- // BB.B1 — the shared curl-noise flow chunk (curlFBM), spliced from the single source
176
- // procedural surfaces share (the AV.W2 shared-chunk precedent, applied to FLOW). The
275
+ // The shared curl-noise flow chunk (curlFBM) is spliced from the single source
276
+ // procedural surfaces share (the shared-chunk precedent, applied to FLOW). The
177
277
  // chunk owns ONLY the basis-agnostic curl operator; the host owns the noise basis —
178
278
  // aurora wraps its OWN fbm as the scalar potential (so curl rides the same 2.02-
179
279
  // lacunarity loop the rest of the warp uses). The curl operator is consumed ONLY on
180
280
  // the OPT-IN warpMode == "curl" branch (uWarpMode == 3) — the default warp paths are
181
281
  // byte-untouched (no curl call), so every existing aurora gate stays byte-equivalent.
182
- ${te}
282
+ ${Se}
183
283
 
184
284
  // The scalar fbm potential ψ for curlFBM — aurora's own fbm. The splice above
185
285
  // forward-declares potentialFBM; this defines it (ES 3.00 allows the prototype).
186
286
  float potentialFBM(vec2 p) { return fbm(p); }
187
287
 
188
- // AX.W12 — the painterly-medium organic noise basis (Jarzynski PCG2D integer-bit hash
288
+ // The painterly-medium organic noise basis (Jarzynski PCG2D integer-bit hash
189
289
  // + 2D simplex gradient noise), SPLICED from the shared procedural-color chunk (the
190
290
  // single hash source).
191
291
  // The painterly mediums (mediumOil's tooth/granulation) opt into pcgHash2/gnoise for
192
292
  // organic paper/pigment grain; the smooth/atmospheric pole KEEPS its cheap value-noise
193
- // fbm above (the cost-tiering is preserved per AX.W12 §3a — this is an ADDITIVE basis,
293
+ // fbm above (the cost tiering is preserved: this is an additive basis,
194
294
  // not a blanket upgrade). The aurora-local hash21/vnoise/fbm loop stays (legitimately
195
295
  // divergent per the chunk's §3a).
196
- ${_}
296
+ ${v}
197
297
 
198
- // The sRGB OETF (linearToSrgb) — spliced from the shared procedural-color chunk
199
- // (AV.W2 — the single OETF source; the AV.W1 local copy is deleted here). Aurora's
200
- // linear pipeline closes the seam with this transfer before fragColor (the
201
- // proof:aurora-space-gamma seam).
202
- ${m}
298
+ // The sRGB OETF (linearToSrgb) is spliced from the shared procedural-color chunk,
299
+ // the sole OETF source; the local copy is deleted here. Aurora's linear pipeline
300
+ // closes the color-space seam with this transfer before fragColor.
301
+ ${b}
203
302
 
204
- // W5 — the four Ottosson OKLab/OKLCh matrices + their space fns, spliced from the
303
+ // The four Ottosson OKLab/OKLCh matrices + their space fns, spliced from the
205
304
  // SAME shared chunk the goo-blob uses (zero new payload, 1e-6-verified). PI is
206
305
  // defined first because the chunk's oklabToOklch folds the hue into [0, 2pi).
207
306
  const float PI = 3.141592653589793;
208
- ${h}
307
+ ${C}
209
308
 
210
- // ── OKLCh palette ramp (W5 interp · AX.W11 single-sourced twin) ───────────────
309
+ // ── OKLCh palette ramp (single-sourced twin) ───────────────────────────────
211
310
  // The muddy-midtone kill: distant-hue stops interpolated by a plain linear mix()
212
311
  // desaturate toward grey at the midpoint. The OKLab-rectangular lerp (L,a,b) holds
213
312
  // chroma across the ramp; the OKLCh hue-arc form (deliberate rainbow travel via
214
313
  // uHuePath increasing/decreasing) sweeps the hue wheel without flipping.
215
314
  //
216
- // AX.W11 — the ramp (interpolateHueTurns + mixPaletteOklab + mixPaletteOklchArc +
315
+ // The ramp (interpolateHueTurns + mixPaletteOklab + mixPaletteOklchArc +
217
316
  // the smoothstep-eased samplePaletteRamp dispatcher) is SPLICED from the shared
218
317
  // procedural-color chunk (the single ramp source). composition.glsl.ts's
219
318
  // samplePalette calls samplePaletteRamp.
220
- ${b}
319
+ ${m}
221
320
 
222
321
  // Interleaved Gradient Noise (Jimenez) — a 1-LSB triangular dither applied in
223
322
  // DISPLAY space AFTER the OETF, so 8-bit mid-tone banding on the soft gradient is
@@ -226,7 +325,7 @@ float ign(vec2 p) {
226
325
  return fract(52.9829189 * fract(dot(p, vec2(0.06711056, 0.00583715))));
227
326
  }
228
327
 
229
- // Cellular / Worley f1
328
+ // Cellular, Worley f1
230
329
  float cellular(vec2 p) {
231
330
  vec2 i = floor(p);
232
331
  vec2 f = fract(p);
@@ -262,7 +361,7 @@ vec2 domainWarp(vec2 p, float t) {
262
361
  float c2 = cellular(p * uWarpScale * 1.2 + vec2(11.0, 7.0));
263
362
  warp = mix(r, vec2(c1, c2), 0.5);
264
363
  } else if (uWarpMode == 3) {
265
- // BB.B1 — curl (Bridson flow warp). The divergence-free curl of an fbm potential
364
+ // Curl (Bridson flow warp). The divergence-free curl of an fbm potential
266
365
  // advects the field along a source-free swirl — folds + stretches like fluid
267
366
  // advection, never the source-y bulge a raw fbm gradient produces. The potential
268
367
  // scrolls on the same warp-drift clock; curlFBM is the shared chunk's operator.
@@ -293,13 +392,13 @@ vec2 domainWarp(vec2 p, float t) {
293
392
  return warped;
294
393
  }
295
394
 
296
- ` + Se + A + Ce + A + "// ── Color utils (OKLCh — W5; the sRGB YIQ hueShift matrix is DELETED) ──────────\nconst vec3 W_LUMA = vec3(0.2126, 0.7152, 0.0722);\n\n// Linear sRGB → OKLab (the cbrt-LMS path; the goo-blob's srgbToOklab takes gamma,\n// here the color field is already linear so we skip the OETF inverse).\nvec3 linOklab(vec3 lin) {\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * (sign(lms) * pow(abs(lms), vec3(1.0 / 3.0)));\n}\n\n// Broken color (W5) — hue + chroma jitter at fixed PERCEPTUAL lightness, in OKLCh.\n// Broken color is hue variation at constant VALUE, which only OKLCh makes true (the\n// prior YIQ-style sRGB rotation muddied value). This is the seam AW.W4's van-Gogh /\n// oil-pastel per-stroke pigment jitter consumes (the pigment-variation read).\nvec3 brokenColorJitter(vec3 c, float hueSeed, float valueSeed, float strength) {\n float amt = clamp(uBrokenColor * strength, 0.0, 1.0);\n if (amt <= 0.001) return c;\n vec3 lch = oklabToOklch(linOklab(c));\n // ±~16° hue swing + ±~12% chroma swing; L held so value reads constant.\n lch.z += (hueSeed - 0.5) * (32.0 * PI / 180.0) * amt;\n lch.y = max(lch.y * (1.0 + (valueSeed - 0.5) * 0.28 * amt), 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n}\n\n// Saturation (W5) — chroma scale at fixed L/H in OKLCh (the perceptual saturation,\n// replacing the luma-mix sRGB form). amt=1 is identity; amt<1 desaturates toward\n// the achromatic axis with NO lightness shift (the muddy-luma-mix grey is gone).\nvec3 saturate3(vec3 c, float amt) {\n vec3 lch = oklabToOklch(linOklab(c));\n lch.y = max(lch.y * amt, 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n}\n\n// BD.W-AUR-VIVIDNESS — the §3 chroma FLOOR. Operate in OKLab: lightness + hue are\n// untouched, chroma is lifted toward the floor. A pale zone BLOOMS to transmission-fit;\n// a vivid zone (already above the floor) is byte-untouched. The near-gray hue guard is\n// STRUCTURAL: below VIVID_EPS the (a,b) direction is precision noise, so the lift\n// synthesizes along the WARM anchor (amber, both components positive) — NEVER the noisy\n// near-zero vector, NEVER a cold/teal hue. uVividness:0 returns c identically (the gated\n// byte-identity opt-out). Mode factor: a dark route lifts the floor a touch (a dim field\n// needs more chroma to read vivid through glass) — derived from the field's own L.\nconst float VIVID_TARGET = 0.115;\nconst float VIVID_EPS = 0.012;\n// The warm amber anchor in OKLab (a,b): OKLCh hue 70°, unit chroma. cos/sin(70°) — both\n// POSITIVE (warm yellow-orange). The near-gray fallback direction, the warm-floor law.\nconst vec2 VIVID_WARM_ANCHOR = vec2(0.34202, 0.93969);\nvec3 vividnessFloor(vec3 c) {\n if (uVividness <= 0.0001) return c;\n vec3 lab = linOklab(c);\n float C = length(lab.yz);\n // Mode-aware: dim fields (low L) get a slightly higher target so they read vivid\n // through glass. mix factor is gentle (1.0 .. 1.18) so light routes are unaffected.\n float modeLift = mix(1.18, 1.0, clamp(lab.x * 1.4, 0.0, 1.0));\n float Cmin = uVividness * VIVID_TARGET * modeLift;\n vec2 hueDir = (C > VIVID_EPS) ? lab.yz / C : VIVID_WARM_ANCHOR;\n lab.yz = hueDir * max(C, Cmin);\n return max(oklabToLinearSrgb(lab), vec3(0.0));\n}\n\n" + we + A + Ae + A + Te + A + je + A + "\n// ── Main ──────────────────────────────────────────────────────────────────\nvoid main() {\n // Normalized 0..1 coordinates for nuclei, domain warp, and medium sampling.\n vec2 uv = vUv;\n vec2 pN = uv;\n\n float t = uTime;\n\n // Warp in pN space\n vec2 p_warp = domainWarp(pN, t);\n\n // Composition\n float paletteId; float valueMod;\n nucleiField(p_warp, t, paletteId, valueMod);\n vec3 col = samplePalette(paletteId);\n col *= 1.0 + uValueVariance * valueMod;\n\n // Breath — slow global luminance wobble\n float breath = sin(t * 6.2831 / max(uBreathPeriod, 1.0));\n col *= 1.0 + uBreathDepth * breath * 0.5;\n\n // Medium dispatch (AX.W13 — each is a FIRST-CLASS body, no shared dispatch):\n // 1 pastel · 2 watercolor · 3 oil · 4 crayon (DRY tooth-multiply) ·\n // 5 van-Gogh (atomic comma/crescent dabs) · 6 oil-pastel (stroke deposition) ·\n // 7 kuwahara (anisotropic-Kuwahara edge-preserving painterly finish — BB.W-AUR-KUWAHARA).\n // van-Gogh is no longer a mediumOil passthrough; oil-pastel and crayon no longer\n // share a body — they share the SUBSTRATE (the tooth/placement helpers), not the\n // dispatch body (slice 8 F0/F1). Kuwahara is OPT-IN (default-off — no preset selects\n // it unless a config explicitly sets medium:\"kuwahara\"); the smooth default + the\n // van-Gogh HERO + the existing painterly bodies are byte-unchanged by its addition.\n if (uMedium == 1) col = mediumPastel(col, pN, t);\n else if (uMedium == 2) col = mediumWatercolor(col, pN, t);\n else if (uMedium == 3) col = mediumOil(col, pN, t);\n else if (uMedium == 4) col = mediumCrayon(col, pN, t);\n else if (uMedium == 5) col = mediumVangogh(col, pN, t);\n else if (uMedium == 6) col = mediumOilPastel(col, pN, t);\n else if (uMedium == 7) col = mediumKuwahara(col, pN, t);\n // BG.W-AUR-METAL-FINISH — the mutually-exclusive metal mediums (opt-in, uMedium==8/9;\n // the smooth default + every existing medium are byte-unchanged by these arms).\n else if (uMedium == 8) col = mediumMetal(col, pN, t);\n else if (uMedium == 9) col = mediumMetalGradient(col, pN, t);\n\n // Saturation trim\n col = saturate3(col, uSaturation);\n\n // BD.W-AUR-VIVIDNESS — the §3 chroma floor (warm-anchored, hue-preserving). Lifts a\n // pale zone toward the transmissive floor so warm glass over the field reads\n // transmissive-not-gray; a vivid zone is untouched; uVividness:0 is a no-op.\n col = vividnessFloor(col);\n\n // Tonemap + film grain\n col = aces(col);\n float grain = hash21(gl_FragCoord.xy + t * 17.0);\n col += (grain - 0.5) * uPaperGrain;\n\n col = clamp(col * 0.985 + 0.008, 0.0, 1.0);\n\n // MANDATORY OETF — closes the seam (mirrors metaball.frag.ts:278). The whole\n // pipeline above (linear palette LUT, ACES tonemap, grain) runs in LINEAR; without\n // this transfer the aurora ships ~2.2× too dark (linear 0.5 → ~0.215, not ~0.735).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF — breaks 8-bit\n // mid-tone banding on the soft gradient. Texture-free (the canonical Jimenez IGN).\n col += (1.0 / 255.0) * (ign(gl_FragCoord.xy) - 0.5);\n\n // Premultiply operates on DISPLAY-space color (matches the blob: OETF, then premul).\n fragColor = vec4(col * uAlpha, uAlpha);\n}\n", Ne = `#version 300 es
395
+ ` + Ce + L + we + "\n// ── Color utils (OKLCh; the sRGB YIQ hueShift matrix is DELETED) ──────────\nconst vec3 W_LUMA = vec3(0.2126, 0.7152, 0.0722);\n\n// Linear sRGB → OKLab (the cbrt-LMS path; the goo-blob's srgbToOklab takes gamma,\n// here the color field is already linear so we skip the OETF inverse).\nvec3 linOklab(vec3 lin) {\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * (sign(lms) * pow(abs(lms), vec3(1.0 / 3.0)));\n}\n\n// Broken color — hue + chroma jitter at fixed PERCEPTUAL lightness, in OKLCh.\n// Broken color is hue variation at constant VALUE, which only OKLCh makes true (the\n// prior YIQ-style sRGB rotation muddied value). Van-Gogh and oil-pastel consume\n// this seam for per-stroke pigment jitter.\nvec3 brokenColorJitter(vec3 c, float hueSeed, float valueSeed, float strength) {\n float amt = clamp(uBrokenColor * strength, 0.0, 1.0);\n if (amt <= 0.001) return c;\n vec3 lch = oklabToOklch(linOklab(c));\n // ±~16° hue swing + ±~12% chroma swing; L held so value reads constant.\n lch.z += (hueSeed - 0.5) * (32.0 * PI / 180.0) * amt;\n lch.y = max(lch.y * (1.0 + (valueSeed - 0.5) * 0.28 * amt), 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n}\n\n// Saturation — chroma scale at fixed L/H in OKLCh (the perceptual saturation,\n// replacing the luma-mix sRGB form). amt=1 is identity; amt<1 desaturates toward\n// the achromatic axis with NO lightness shift (the muddy-luma-mix grey is gone).\nvec3 saturate3(vec3 c, float amt) {\n vec3 lch = oklabToOklch(linOklab(c));\n lch.y = max(lch.y * amt, 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n}\n\n// The chroma floor operates in OKLab: lightness + hue are\n// untouched, chroma is lifted toward the floor. A pale zone BLOOMS to transmission-fit;\n// a vivid zone (already above the floor) is byte-untouched. The near-gray hue guard is\n// STRUCTURAL: below VIVID_EPS the (a,b) direction is precision noise, so the lift\n// synthesizes along the WARM anchor (amber, both components positive) — NEVER the noisy\n// near-zero vector, NEVER a cold/teal hue. uVividness:0 returns c identically (the gated\n// byte-identity opt-out). Mode factor: a dark route lifts the floor a touch (a dim field\n// needs more chroma to read vivid through glass) — derived from the field's own L.\nconst float VIVID_TARGET = 0.115;\nconst float VIVID_EPS = 0.012;\n// The warm amber anchor in OKLab (a,b): OKLCh hue 70°, unit chroma. cos/sin(70°) — both\n// POSITIVE (warm yellow-orange). The near-gray fallback direction, the warm-floor law.\nconst vec2 VIVID_WARM_ANCHOR = vec2(0.34202, 0.93969);\nvec3 vividnessFloor(vec3 c) {\n if (uVividness <= 0.0001) return c;\n vec3 lab = linOklab(c);\n float C = length(lab.yz);\n // Mode-aware: dim fields (low L) get a slightly higher target so they read vivid\n // through glass. The mix factor is gentle (1.0–1.18) so light routes are unaffected.\n float modeLift = mix(1.18, 1.0, clamp(lab.x * 1.4, 0.0, 1.0));\n float Cmin = uVividness * VIVID_TARGET * modeLift;\n vec2 hueDir = (C > VIVID_EPS) ? lab.yz / C : VIVID_WARM_ANCHOR;\n lab.yz = hueDir * max(C, Cmin);\n return max(oklabToLinearSrgb(lab), vec3(0.0));\n}\n\n" + Te + L + je + L + Ee + L + Me + "\n\n// ── Main ──────────────────────────────────────────────────────────────────\nvoid main() {\n // Normalized 0..1 coordinates for nuclei, domain warp, and medium sampling.\n vec2 uv = vUv;\n vec2 pN = uv;\n\n float t = uTime;\n\n // Warp in pN space\n vec2 p_warp = domainWarp(pN, t);\n\n // Composition\n float paletteId; float valueMod;\n nucleiField(p_warp, t, paletteId, valueMod);\n vec3 col = samplePalette(paletteId);\n col *= 1.0 + uValueVariance * valueMod;\n\n // Breath — slow global luminance wobble\n float breath = sin(t * 6.2831 / max(uBreathPeriod, 1.0));\n col *= 1.0 + uBreathDepth * breath * 0.5;\n\n // Medium dispatch: each is a first-class body with no shared dispatch.\n // 1 pastel · 2 watercolor · 3 oil · 4 crayon (DRY tooth-multiply) ·\n // 5 van-Gogh (atomic comma/crescent dabs) · 6 oil-pastel (stroke deposition) ·\n // 7 kuwahara (anisotropic-Kuwahara edge-preserving painterly finish).\n // van-Gogh is no longer a mediumOil passthrough; oil-pastel and crayon no longer\n // share a body — they share the SUBSTRATE (the tooth/placement helpers), not the\n // dispatch body. Kuwahara is OPT-IN (default-off — no preset selects\n // it unless a config explicitly sets medium:\"kuwahara\"); the smooth default + the\n // van-Gogh HERO + the existing painterly bodies are byte-unchanged by its addition.\n if (uMedium == 1) col = mediumPastel(col, pN, t);\n else if (uMedium == 2) col = mediumWatercolor(col, pN, t);\n else if (uMedium == 3) col = mediumOil(col, pN, t);\n else if (uMedium == 4) col = mediumCrayon(col, pN, t);\n else if (uMedium == 5) col = mediumVangogh(col, pN, t);\n else if (uMedium == 6) col = mediumOilPastel(col, pN, t);\n else if (uMedium == 7) col = mediumKuwahara(col, pN, t);\n // The mutually exclusive metal mediums are opt-in at uMedium==8/9;\n // the smooth default + every existing medium are byte-unchanged by these arms).\n else if (uMedium == 8) col = mediumMetal(col, pN, t);\n else if (uMedium == 9) col = mediumMetalGradient(col, pN, t);\n\n // Cursor-local luminance lean. Position, radius, and the CPU-folded strength are the\n // same four packed values on WebGL2 and WebGPU; absent interactivity writes strength 0.\n vec2 cursorDelta = pN - uCursor;\n float cursorRadius = max(uCursorRadius, 0.01);\n float cursorLean = exp(-dot(cursorDelta, cursorDelta) / (cursorRadius * cursorRadius * 0.5));\n col *= 1.0 + 0.12 * cursorLean * uCursorStrength;\n\n // Saturation trim\n col = saturate3(col, uSaturation);\n\n // The chroma floor is warm-anchored and hue-preserving. It lifts a\n // pale zone toward the transmissive floor so warm glass over the field reads\n // transmissive-not-gray; a vivid zone is untouched; uVividness:0 is a no-op.\n col = vividnessFloor(col);\n\n // Tonemap + film grain\n col = aces(col);\n float grain = hash21(gl_FragCoord.xy + t * 17.0);\n col += (grain - 0.5) * uPaperGrain;\n\n col = clamp(col * 0.985 + 0.008, 0.0, 1.0);\n\n // MANDATORY OETF — closes the seam (mirrors metaball.frag.ts:278). The whole\n // pipeline above (linear palette LUT, ACES tonemap, grain) runs in LINEAR; without\n // this transfer the aurora ships ~2.2× too dark (linear 0.5 → ~0.215, not ~0.735).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF — breaks 8-bit\n // mid-tone banding on the soft gradient. Texture-free (the canonical Jimenez IGN).\n col += (1.0 / 255.0) * (ign(gl_FragCoord.xy) - 0.5);\n\n // Premultiply operates on DISPLAY-space color (matches the blob: OETF, then premul).\n fragColor = vec4(col * uAlpha, uAlpha);\n}\n", Pe = `#version 300 es
297
396
  precision highp float;
298
397
 
299
398
  in vec2 vUv;
300
399
  out vec4 fragColor;
301
400
 
302
- #define MAX_NUCLEI 6
401
+ #define MAX_NUCLEI 8
303
402
  #define BLUR_RINGS 3
304
403
  #define BLUR_SECTORS 8
305
404
  const float PI = 3.141592653589793;
@@ -358,7 +457,7 @@ float vnoise(vec2 p) {
358
457
  float d = hash21(i + vec2(1.0, 1.0));
359
458
  return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
360
459
  }
361
- ${ee}
460
+ ${p}
362
461
  float fbm(vec2 p) {
363
462
  float v = 0.0;
364
463
  float a = 0.5;
@@ -375,8 +474,8 @@ float ign(vec2 p) {
375
474
  return fract(52.9829189 * fract(dot(p, vec2(0.06711056, 0.00583715))));
376
475
  }
377
476
 
378
- ${m}
379
- ${h}
477
+ ${b}
478
+ ${C}
380
479
 
381
480
  // linear-sRGB → raw OKLab (the vividnessFloor helper — mirrors aurora.frag's linOklab).
382
481
  vec3 linOklab(vec3 lin) {
@@ -512,6 +611,11 @@ void main() {
512
611
  float radius = mix(uBlurMin, uBlurMax, zone);
513
612
  vec3 col = zoneBlur(uv, radius);
514
613
 
614
+ vec2 cursorDelta = uv - uCursor;
615
+ float cursorRadius = max(uCursorRadius, 0.01);
616
+ float cursorLean = exp(-dot(cursorDelta, cursorDelta) / (cursorRadius * cursorRadius * 0.5));
617
+ col *= 1.0 + 0.12 * cursorLean * uCursorStrength;
618
+
515
619
  // Saturation trim + the source-agnostic vividness floor (a washed photo blooms).
516
620
  col = saturate3(col, uSaturation);
517
621
  col = vividnessFloor(col);
@@ -529,147 +633,59 @@ void main() {
529
633
  // Premultiply on display-space color (matches the palette path: OETF, then premul).
530
634
  fragColor = vec4(col * uAlpha, uAlpha);
531
635
  }
532
- `, Pe = {
533
- premultiplyAlpha: "none",
534
- colorSpaceConversion: "none"
535
- };
536
- function Fe(e) {
537
- return createImageBitmap(e, Pe);
538
- }
539
- async function Ie(e) {
540
- return typeof e == "string" ? Fe(await (await fetch(e)).blob()) : e instanceof Blob ? Fe(e) : e;
541
- }
542
- function Le() {
543
- let e = new ImageData(1, 1);
544
- return e.data.set([
545
- 128,
546
- 128,
547
- 128,
548
- 255
549
- ]), e;
550
- }
551
- function Re(e) {
552
- return e instanceof HTMLImageElement ? {
553
- width: e.naturalWidth,
554
- height: e.naturalHeight
555
- } : {
556
- width: e.width,
557
- height: e.height
636
+ `, R = .001, Fe = 1e-4;
637
+ function z(e, t, i) {
638
+ let a = n(e);
639
+ if (i > 0) {
640
+ let t = e.acceleration.value, n = e.velocity.value, r = -(t.x * n.x + t.y * n.y);
641
+ r > 0 && (a.burst = Math.min(1, a.burst + r * 3));
642
+ }
643
+ let o = r(a, t);
644
+ return {
645
+ x: o.cx,
646
+ y: o.cy,
647
+ strength: o.strength,
648
+ radius: o.radius
558
649
  };
559
650
  }
560
- function ze(e, t, n = {}) {
561
- let { linearFilter: r = !0, clampToEdge: i = !0 } = n, a = e.createTexture();
562
- e.bindTexture(e.TEXTURE_2D, a), e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL, !1), e.pixelStorei(e.UNPACK_COLORSPACE_CONVERSION_WEBGL, e.NONE), e.pixelStorei(e.UNPACK_FLIP_Y_WEBGL, !1), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA, e.RGBA, e.UNSIGNED_BYTE, t);
563
- let o = r ? e.LINEAR : e.NEAREST;
564
- e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, o), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, o);
565
- let s = i ? e.CLAMP_TO_EDGE : e.REPEAT;
566
- return e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, s), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, s), e.pixelStorei(e.UNPACK_COLORSPACE_CONVERSION_WEBGL, e.BROWSER_DEFAULT_WEBGL), a;
651
+ function B(e) {
652
+ let t = e.attractorVelocity.value;
653
+ return e.engagement.value > R || e.speed.value > R || e.burst.value > R || Math.hypot(t.x, t.y) > Fe;
567
654
  }
568
- var Be = 2;
569
- function Ve(e, t) {
570
- let { width: n, height: r } = Re(t), i = e.createTexture({
571
- label: "[texture-upload] image",
572
- size: [
573
- n,
574
- r,
575
- 1
576
- ],
577
- format: "rgba8unorm",
578
- usage: Be | 20
579
- });
580
- return e.queue.copyExternalImageToTexture({
581
- source: t,
582
- flipY: !1
583
- }, {
584
- texture: i,
585
- premultipliedAlpha: !1
586
- }, [
587
- n,
588
- r,
589
- 1
590
- ]), {
591
- texture: i,
592
- width: n,
593
- height: r
655
+ function Ie(e) {
656
+ let { gl: t, prog: n, uniforms: r, pointerField: i, getCursorScalars: a, getConfig: o, getReducedMotion: s, getRenderTime: c } = e, l = (e) => 1 - e, u = null, d = {
657
+ x: .5,
658
+ y: .5,
659
+ strength: 0,
660
+ radius: .25
594
661
  };
595
- }
596
- //#endregion
597
- //#region src/components/aurora/composables/auroraImageSource.ts
598
- function He(e) {
599
- let t = null, n = null, r;
600
- return {
601
- getDecodedImage: () => t,
602
- setImageUploader(e) {
603
- n = e, e && t && e(t);
604
- },
605
- ensureDecoded(i) {
606
- i.source !== "image" || i.src == null || i.src !== r && (r = i.src, Ie(i.src).then((r) => {
607
- t = r, n?.(r), e.wake();
608
- }).catch((t) => {
609
- e.onError?.(t instanceof Error ? t : Error(String(t)));
610
- }));
662
+ function f() {
663
+ return +!s();
664
+ }
665
+ function p(e) {
666
+ let i = c(e);
667
+ if (t.useProgram(n), t.uniform2f(r.uCursor, d.x, l(d.y)), t.uniform1f(r.uCursorStrength, d.strength), t.uniform1f(r.uCursorRadius, d.radius), t.uniform1f(r.uTime, i), o().interactivity?.light) {
668
+ let e = f(), n = i * .25, a = Math.cos(n) * .5, o = Math.sin(n) * .5, s = (d.x - .5) * 2, c = (.5 - d.y) * 2, l = d.strength * e;
669
+ a = a * (1 - l) + s * l, o = o * (1 - l) + c * l, t.uniform3f(r.uLightDir, a * e + (1 - e) * -.5, o * e + (1 - e) * .6, .62);
611
670
  }
612
- };
613
- }
614
- function Ue(e, t, n) {
615
- let r = null;
616
- e.activeTexture(e.TEXTURE0);
617
- let i = (n) => {
618
- e.useProgram(t), e.activeTexture(e.TEXTURE0), r && e.deleteTexture(r), r = ze(e, n);
619
- };
620
- return i(n.getDecodedImage() ?? Le()), n.setImageUploader(i), () => {
621
- r && e.deleteTexture(r), n.setImageUploader(null);
622
- };
623
- }
624
- //#endregion
625
- //#region src/components/aurora/constants/renderMode.ts
626
- function j() {
627
- let e;
628
- try {
629
- e = y();
630
- } catch {
631
- return !1;
671
+ t.clear(t.COLOR_BUFFER_BIT), t.drawArrays(t.TRIANGLES, 0, 3);
672
+ }
673
+ function m() {
674
+ if (s()) return !1;
675
+ let e = o();
676
+ return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0 || B(i);
632
677
  }
633
- return e === null ? !1 : e.includes("swiftshader") || e.includes("llvmpipe") || e.includes("software") || e.includes("basic render") || e.includes("microsoft basic");
634
- }
635
- function M(e, t = {}) {
636
- return e !== "css" && !t.forceWebGLUnderSoftwareRaster && j() ? "css" : e === "auto" ? "webgl" : e;
637
- }
638
- //#endregion
639
- //#region src/components/aurora/composables/glSetup.ts
640
- var N = "[Aurora]", We = /* @__PURE__ */ "uTime.uPalette.uStopCount.uNucleiCount.uNucleiPos.uNucleiRadius.uNucleiPaletteBias.uNucleiValueBias.uNucleiDriftRadius.uNucleiDriftPhase.uNucleiElong.uNucleiAngle.uSoftmaxBeta.uValueVariance.uWarpAmount.uWarpScale.uWarpDrift.uWarpMode.uNoiseOctaves.uMedium.uHuePath.uFlowPattern.uFlowFocal.uFlowAngle.uFlowCurl.uCursor.uCursorStrength.uCursorRadius.uCursorVelocity.uCursorBurst.uStrokeAmount.uStrokeScale.uStrokeAnisotropy.uStrokeLayers.uStrokeMode.uStrokeOrient.uLightDir.uLightColor.uWetEdge.uGranulation.uImpasto.uBrokenColor.uCanvasGrain.uMetalPolish.uMetalHeightScale.uNucleiDrift.uPaletteDrift.uBreathDepth.uBreathPeriod.uSaturation.uPaperGrain.uAlpha.uVividness.uImage.uBlurMin.uBlurMax.uImageAspect".split(".");
641
- function Ge(e, t, n) {
642
- let r = ae(e, e.VERTEX_SHADER, t, N), i = ae(e, e.FRAGMENT_SHADER, n, N), a = ie(e, r, i, N);
643
- e.useProgram(a);
644
- let o = e.createVertexArray();
645
- e.bindVertexArray(o);
646
- let s = e.createBuffer();
647
- e.bindBuffer(e.ARRAY_BUFFER, s), e.bufferData(e.ARRAY_BUFFER, new Float32Array([
648
- -1,
649
- -1,
650
- 3,
651
- -1,
652
- -1,
653
- 3
654
- ]), e.STATIC_DRAW);
655
- let c = e.getAttribLocation(a, "aPos");
656
- e.enableVertexAttribArray(c), e.vertexAttribPointer(c, 2, e.FLOAT, !1, 0, 0);
657
- let l = {};
658
- for (let t of We) l[t] = e.getUniformLocation(a, t);
659
678
  return {
660
- program: a,
661
- vs: r,
662
- fs: i,
663
- uniforms: l,
664
- geometry: {
665
- vao: o,
666
- buf: s
667
- }
679
+ frame: (e) => {
680
+ let t = f(), n = u === null ? 0 : Math.max(0, (e - u) * 1e3);
681
+ u = e, i.tick(t === 0 ? 0 : n), d = z(i, a(), t), p(e);
682
+ },
683
+ needsAnimation: m
668
684
  };
669
685
  }
670
686
  //#endregion
671
687
  //#region src/components/aurora/constants/presets.ts
672
- var Ke = 6, qe = 8, Je = 1, Ye = .115, Xe = .004, Ze = .06, P = {
688
+ var Le = 8, Re = 8, ze = 1, Be = .115, Ve = .004, He = .06, V = {
673
689
  palette: [
674
690
  {
675
691
  L: .64,
@@ -737,7 +753,7 @@ var Ke = 6, qe = 8, Je = 1, Ye = .115, Xe = .004, Ze = .06, P = {
737
753
  paperGrain: .008,
738
754
  alpha: 1,
739
755
  vividness: 1
740
- }, Qe = {
756
+ }, Ue = {
741
757
  medium: "crayon",
742
758
  strokeOrient: "tensor",
743
759
  granulation: .3,
@@ -753,64 +769,63 @@ var Ke = 6, qe = 8, Je = 1, Ye = .115, Xe = .004, Ze = .06, P = {
753
769
  };
754
770
  //#endregion
755
771
  //#region src/components/aurora/composables/color.ts
756
- function F(e, t = 8, n) {
772
+ function H(e, t = 8, n) {
757
773
  let r = n ?? new Float32Array(t * 3), i = Math.min(e.length, t);
758
774
  for (let t = 0; t < i; t++) {
759
- let [n, i, a] = u(e[t]);
775
+ let [n, i, a] = d(e[t]);
760
776
  r[t * 3 + 0] = n, r[t * 3 + 1] = i, r[t * 3 + 2] = a;
761
777
  }
762
778
  for (let e = i; e < t; e++) r[e * 3 + 0] = 0, r[e * 3 + 1] = 0, r[e * 3 + 2] = 0;
763
779
  return r;
764
780
  }
765
- function $e(e) {
781
+ function We(e) {
766
782
  if (e.length === 0) return "transparent";
767
- if (e.length === 1) return l(e[0]);
783
+ if (e.length === 1) return u(e[0]);
768
784
  let t = e.length;
769
785
  return `linear-gradient(135deg, ${e.map((e, n) => {
770
786
  let r = Math.round(n / (t - 1) * 100);
771
- return `${l(e)} ${r}%`;
787
+ return `${u(e)} ${r}%`;
772
788
  }).join(", ")})`;
773
789
  }
774
- function et(e) {
775
- let t = e.replace("#", ""), n = parseInt(t.slice(0, 2), 16), r = parseInt(t.slice(2, 4), 16), i = parseInt(t.slice(4, 6), 16), [a, o, s] = be(n / 255, r / 255, i / 255), [c, l, u] = ve(a, o, s);
776
- return {
777
- L: c,
778
- C: l,
779
- h: u
780
- };
790
+ function Ge(e) {
791
+ return s(e);
781
792
  }
782
- var tt = [.35, .95], nt = [.18, .42];
783
- function rt(e, t) {
784
- return e ? [Math.max(0, Math.min(1, e[0])), Math.max(0, Math.min(1, e[1]))] : t === "dark" ? nt : tt;
793
+ var Ke = [.35, .95], qe = [.18, .42];
794
+ function Je(e, t) {
795
+ return e ? [Math.max(0, Math.min(1, e[0])), Math.max(0, Math.min(1, e[1]))] : t === "dark" ? qe : Ke;
785
796
  }
786
- function I(e, t = {}) {
787
- let n = typeof e == "string" ? o(e) : e, { stopCount: r = 4, harmony: i = "analogous", lightnessSpread: a = .32, chromaFalloff: l = .85, hueSpread: u = 28, lightnessEasing: d = "linear", chromaEasing: f = "bell", temperatureShift: p = 0, avoidHues: m, scheme: h, lBand: g } = t, _ = Math.max(2, Math.min(8, Math.round(r))), [v, y] = rt(g, h), b = n.L - a / 2, x = n.L + a / 2;
797
+ function Ye(e, t = {}) {
798
+ let n = typeof e == "string" ? s(e) : e, { stopCount: r = 4, harmony: i = "analogous", lightnessSpread: a = .32, chromaFalloff: o = .85, hueSpread: u = 28, lightnessEasing: d = "linear", chromaEasing: f = "bell", temperatureShift: p = 0, avoidHues: m, scheme: h, lBand: g } = t, _ = Math.max(2, Math.min(8, Math.round(r))), [v, y] = Je(g, h), b = n.L - a / 2, x = n.L + a / 2;
788
799
  b < v && (x += v - b, b = v), x > y && (b -= x - y, x = y), b = Math.max(v, b), x = Math.min(y, x);
789
800
  let S = [], C = -Infinity;
790
801
  for (let e = 0; e < _; e++) {
791
- let t = _ === 1 ? 0 : e / (_ - 1), r = b + (x - b) * L(t, d), a = n.C * l, o;
792
- if (f === "bell") o = a + (n.C - a) * at(t);
802
+ let t = _ === 1 ? 0 : e / (_ - 1), r = b + (x - b) * Ze(t, d), a = n.C * o, s;
803
+ if (f === "bell") s = a + (n.C - a) * Qe(t);
793
804
  else {
794
- let e = f === "sine" ? L(t, "sine") : t;
795
- o = n.C - (n.C - a) * e;
805
+ let e = f === "sine" ? Ze(t, "sine") : t;
806
+ s = n.C - (n.C - a) * e;
796
807
  }
797
- let h = s(n.h, i, u, t);
798
- p > 0 && (h = lt(h, t, p)), m && m.length > 0 && (h = dt(h, m));
799
- let g = it(c({
808
+ let h = c(n.h, i, u, t);
809
+ p > 0 && (h = nt(h, t, p)), m && m.length > 0 && (h = at(h, m));
810
+ let g = Xe(l({
800
811
  L: r,
801
- C: o,
812
+ C: s,
802
813
  h
803
814
  }), C);
804
815
  C = g.L, S.push(g);
805
816
  }
806
817
  return S;
807
818
  }
808
- function it(e, t) {
819
+ function Xe(e, t) {
809
820
  if (e.L >= t) return e;
810
821
  let { h: n } = e, r = t, i = e.C;
811
822
  for (let e = 0; e < 48; e++) {
812
- let [e, t, a] = ye(r, i, n), [o, s, c] = _e(e, t, a);
813
- if (Math.max(o, s, c) <= 1) break;
823
+ let [e, t, a] = d({
824
+ L: r,
825
+ C: i,
826
+ h: n
827
+ });
828
+ if (Math.max(e, t, a) <= 1) break;
814
829
  i *= .99;
815
830
  }
816
831
  return {
@@ -819,36 +834,36 @@ function it(e, t) {
819
834
  h: n
820
835
  };
821
836
  }
822
- function L(e, t) {
837
+ function Ze(e, t) {
823
838
  return t === "sine" ? .5 - .5 * Math.cos(Math.PI * e) : e;
824
839
  }
825
- function at(e) {
840
+ function Qe(e) {
826
841
  return Math.sin(Math.PI * e);
827
842
  }
828
- var ot = 70, st = 250, ct = 22;
829
- function lt(e, t, n) {
843
+ var $e = 70, et = 250, tt = 22;
844
+ function nt(e, t, n) {
830
845
  let r = (t - .5) * 2;
831
846
  if (r === 0 || n === 0) return e;
832
- let i = (ge(e, r >= 0 ? ot : st, 1, "shorter") - e + 540) % 360 - 180;
833
- return ((e + Math.sign(i) * Math.min(Math.abs(r) * n * ct, Math.abs(i))) % 360 + 360) % 360;
847
+ let i = (o("applyTemperature", pe(e, r >= 0 ? $e : et, 1, "shorter")) - e + 540) % 360 - 180;
848
+ return ((e + Math.sign(i) * Math.min(Math.abs(r) * n * tt, Math.abs(i))) % 360 + 360) % 360;
834
849
  }
835
- var R = (e) => (e % 360 + 360) % 360;
836
- function ut(e, t, n) {
837
- let r = R(e), i = R(t), a = R(n);
850
+ var U = (e) => (e % 360 + 360) % 360;
851
+ function rt(e, t, n) {
852
+ let r = U(e), i = U(t), a = U(n);
838
853
  return i <= a ? r >= i && r <= a : r >= i || r <= a;
839
854
  }
840
- function z(e, t) {
841
- let n = Math.abs(R(e) - R(t)) % 360;
855
+ function it(e, t) {
856
+ let n = Math.abs(U(e) - U(t)) % 360;
842
857
  return n > 180 ? 360 - n : n;
843
858
  }
844
- function dt(e, t) {
845
- let n = .5, r = R(e);
859
+ function at(e, t) {
860
+ let n = .5, r = U(e);
846
861
  for (let e = 0; e < t.length + 2; e++) {
847
862
  let e = !1;
848
863
  for (let [i, a] of t) {
849
- if (!ut(r, i, a)) continue;
850
- let t = R(i - n), o = R(a + n);
851
- r = z(r, t) <= z(r, o) ? t : o, e = !0;
864
+ if (!rt(r, i, a)) continue;
865
+ let t = U(i - n), o = U(a + n);
866
+ r = it(r, t) <= it(r, o) ? t : o, e = !0;
852
867
  }
853
868
  if (!e) return r;
854
869
  }
@@ -856,15 +871,12 @@ function dt(e, t) {
856
871
  }
857
872
  //#endregion
858
873
  //#region src/components/aurora/composables/uniformBridge.ts
859
- var ft = {
874
+ var ot = {
860
875
  x: .5,
861
876
  y: .5,
862
877
  strength: 0,
863
- radius: .25,
864
- velX: 0,
865
- velY: 0,
866
- burst: 0
867
- }, B = {
878
+ radius: .25
879
+ }, st = {
868
880
  smooth: 0,
869
881
  pastel: 1,
870
882
  watercolor: 2,
@@ -875,144 +887,146 @@ var ft = {
875
887
  kuwahara: 7,
876
888
  metal: 8,
877
889
  "metal-gradient": 9
878
- }, V = {
890
+ }, ct = {
879
891
  shorter: 0,
880
892
  longer: 1,
881
893
  increasing: 2,
882
894
  decreasing: 3
883
- }, pt = {
895
+ }, lt = {
884
896
  none: 0,
885
897
  radial: 1,
886
898
  swirl: 2,
887
899
  diagonal: 3,
888
900
  multi: 4,
889
901
  tensor: 5
890
- }, H = {
902
+ }, ut = {
891
903
  flow: 0,
892
904
  tensor: 1
893
- }, mt = {
905
+ }, dt = {
894
906
  fbm: 0,
895
907
  cellular: 1,
896
908
  hybrid: 2,
897
909
  curl: 3
898
- }, ht = {
910
+ }, ft = {
899
911
  oil: 0,
900
912
  knife: 1,
901
913
  chunky: 3
902
- }, U = {
914
+ }, W = {
903
915
  L: .985,
904
916
  C: .0125,
905
917
  h: 77.5
906
918
  };
919
+ function pt(e) {
920
+ return e === void 0 ? f(W.L, W.C, W.h) : Array.isArray(e) ? e : f(e.L, e.C, e.h);
921
+ }
922
+ function mt(e) {
923
+ return st[e.medium];
924
+ }
925
+ function ht(e) {
926
+ return ft[e.strokeMode];
927
+ }
907
928
  function gt(e) {
908
- return e === void 0 ? d(U.L, U.C, U.h) : Array.isArray(e) ? e : d(e.L, e.C, e.h);
909
- }
910
- function _t(e) {
911
- return B[e.medium];
912
- }
913
- function vt(e) {
914
- return ht[e.strokeMode];
915
- }
916
- function yt(e) {
917
- return e.medium === "vangogh" || e.medium === "oil-pastel" || e.medium === "crayon" || e.medium === "kuwahara" ? H.tensor : H[e.strokeOrient ?? "flow"];
918
- }
919
- function bt(e, t, n) {
920
- let r = (e) => 1 - e, i = {
921
- palette: new Float32Array(24),
922
- pos: new Float32Array(12),
923
- rad: new Float32Array(6),
924
- pb: new Float32Array(6),
925
- vb: new Float32Array(6),
926
- dr: new Float32Array(6),
927
- dp: new Float32Array(6),
928
- elong: new Float32Array(6),
929
- angle: new Float32Array(6)
929
+ return e.medium === "vangogh" || e.medium === "oil-pastel" || e.medium === "crayon" || e.medium === "kuwahara" ? ut.tensor : ut[e.strokeOrient ?? "flow"];
930
+ }
931
+ function _t(e, t, n, r = !1) {
932
+ let i = (e) => 1 - e, a = {
933
+ palette: /* @__PURE__ */ new Float32Array(24),
934
+ pos: /* @__PURE__ */ new Float32Array(16),
935
+ rad: /* @__PURE__ */ new Float32Array(8),
936
+ pb: /* @__PURE__ */ new Float32Array(8),
937
+ vb: /* @__PURE__ */ new Float32Array(8),
938
+ dr: /* @__PURE__ */ new Float32Array(8),
939
+ dp: /* @__PURE__ */ new Float32Array(8),
940
+ elong: /* @__PURE__ */ new Float32Array(8),
941
+ angle: /* @__PURE__ */ new Float32Array(8)
930
942
  };
931
- return function(a) {
932
- e.useProgram(t), F(a.palette, 8, i.palette), e.uniform3fv(n.uPalette, i.palette), e.uniform1i(n.uStopCount, Math.min(a.palette.length, 8));
933
- let o = Math.min(a.nuclei.length, 6);
934
- e.uniform1i(n.uNucleiCount, o);
935
- for (let e = 0; e < o; e++) {
936
- let t = a.nuclei[e];
937
- i.pos[e * 2 + 0] = t.x, i.pos[e * 2 + 1] = r(t.y), i.rad[e] = t.radius, i.pb[e] = t.paletteBias, i.vb[e] = t.valueBias, i.dr[e] = t.driftRadius, i.dp[e] = t.driftPhase, i.elong[e] = t.elongation ?? 1, i.angle[e] = -(t.angle ?? 0) * Math.PI / 180;
943
+ return function(o) {
944
+ e.useProgram(t), H(o.palette, 8, a.palette), e.uniform3fv(n.uPalette, a.palette), e.uniform1i(n.uStopCount, Math.min(o.palette.length, 8));
945
+ let s = Math.min(o.nuclei.length, 8);
946
+ e.uniform1i(n.uNucleiCount, s);
947
+ for (let e = 0; e < s; e++) {
948
+ let t = o.nuclei[e];
949
+ a.pos[e * 2 + 0] = t.x, a.pos[e * 2 + 1] = i(t.y), a.rad[e] = t.radius, a.pb[e] = t.paletteBias, a.vb[e] = t.valueBias, a.dr[e] = t.driftRadius, a.dp[e] = t.driftPhase, a.elong[e] = t.elongation ?? 1, a.angle[e] = -(t.angle ?? 0) * Math.PI / 180;
938
950
  }
939
- for (let e = o; e < 6; e++) i.pos[e * 2 + 0] = 0, i.pos[e * 2 + 1] = 0, i.rad[e] = 0, i.pb[e] = 0, i.vb[e] = 0, i.dr[e] = 0, i.dp[e] = 0, i.elong[e] = 1, i.angle[e] = 0;
940
- e.uniform2fv(n.uNucleiPos, i.pos), e.uniform1fv(n.uNucleiRadius, i.rad), e.uniform1fv(n.uNucleiPaletteBias, i.pb), e.uniform1fv(n.uNucleiValueBias, i.vb), e.uniform1fv(n.uNucleiDriftRadius, i.dr), e.uniform1fv(n.uNucleiDriftPhase, i.dp), e.uniform1fv(n.uNucleiElong, i.elong), e.uniform1fv(n.uNucleiAngle, i.angle), e.uniform1f(n.uSoftmaxBeta, a.softmaxBeta), e.uniform1f(n.uValueVariance, a.valueVariance), e.uniform1f(n.uWarpAmount, a.warpAmount), e.uniform1f(n.uWarpScale, a.warpScale), e.uniform1f(n.uWarpDrift, a.warpDrift), e.uniform1i(n.uWarpMode, mt[a.warpMode]), e.uniform1i(n.uNoiseOctaves, a.noiseOctaves), e.uniform1i(n.uMedium, _t(a)), e.uniform1i(n.uHuePath, V[a.huePath ?? "shorter"]), e.uniform1i(n.uFlowPattern, pt[a.flow.pattern]), e.uniform2f(n.uFlowFocal, a.flow.focalX, r(a.flow.focalY)), e.uniform1f(n.uFlowAngle, a.flow.angle), e.uniform1f(n.uFlowCurl, a.flow.curl);
941
- let s = ft;
942
- e.uniform2f(n.uCursor, s.x, r(s.y)), e.uniform1f(n.uCursorStrength, s.strength), e.uniform1f(n.uCursorRadius, s.radius), e.uniform2f(n.uCursorVelocity, s.velX, -s.velY), e.uniform1f(n.uCursorBurst, s.burst), e.uniform1f(n.uStrokeAmount, a.strokeAmount), e.uniform1f(n.uStrokeScale, a.strokeScale), e.uniform1f(n.uStrokeAnisotropy, a.strokeAnisotropy), e.uniform1i(n.uStrokeLayers, a.strokeLayers), e.uniform1i(n.uStrokeMode, vt(a)), e.uniform1i(n.uStrokeOrient, yt(a));
951
+ for (let e = s; e < 8; e++) a.pos[e * 2 + 0] = 0, a.pos[e * 2 + 1] = 0, a.rad[e] = 0, a.pb[e] = 0, a.vb[e] = 0, a.dr[e] = 0, a.dp[e] = 0, a.elong[e] = 1, a.angle[e] = 0;
952
+ e.uniform2fv(n.uNucleiPos, a.pos), e.uniform1fv(n.uNucleiRadius, a.rad), e.uniform1fv(n.uNucleiPaletteBias, a.pb), e.uniform1fv(n.uNucleiValueBias, a.vb), e.uniform1fv(n.uNucleiDriftRadius, a.dr), e.uniform1fv(n.uNucleiDriftPhase, a.dp), e.uniform1fv(n.uNucleiElong, a.elong), e.uniform1fv(n.uNucleiAngle, a.angle), e.uniform1f(n.uSoftmaxBeta, o.softmaxBeta), e.uniform1f(n.uValueVariance, o.valueVariance), e.uniform1f(n.uWarpAmount, o.warpAmount), e.uniform1f(n.uWarpScale, o.warpScale), e.uniform1f(n.uWarpDrift, o.warpDrift), e.uniform1i(n.uWarpMode, dt[o.warpMode]), e.uniform1i(n.uNoiseOctaves, o.noiseOctaves), e.uniform1i(n.uMedium, mt(o)), e.uniform1i(n.uHuePath, ct[o.huePath ?? "shorter"]), e.uniform1i(n.uFlowPattern, lt[o.flow.pattern]), e.uniform2f(n.uFlowFocal, o.flow.focalX, i(o.flow.focalY)), e.uniform1f(n.uFlowAngle, o.flow.angle), e.uniform1f(n.uFlowCurl, o.flow.curl);
953
+ let c = ot;
954
+ e.uniform2f(n.uCursor, c.x, i(c.y)), e.uniform1f(n.uCursorStrength, c.strength), e.uniform1f(n.uCursorRadius, c.radius), e.uniform1f(n.uStrokeAmount, o.strokeAmount), e.uniform1f(n.uStrokeScale, o.strokeScale), e.uniform1f(n.uStrokeAnisotropy, o.strokeAnisotropy), e.uniform1i(n.uStrokeLayers, o.strokeLayers), e.uniform1i(n.uStrokeMode, ht(o)), e.uniform1i(n.uStrokeOrient, gt(o));
943
955
  {
944
- let t = a.lightDir ?? [
956
+ let t = o.lightDir ?? [
945
957
  -.5,
946
958
  .6,
947
959
  .62
948
- ], r = gt(a.lightColor);
960
+ ], r = pt(o.lightColor);
949
961
  e.uniform3f(n.uLightDir, t[0], t[1], t[2]), e.uniform3f(n.uLightColor, r[0], r[1], r[2]);
950
962
  }
951
- e.uniform1f(n.uWetEdge, a.wetEdge), e.uniform1f(n.uGranulation, a.granulation), e.uniform1f(n.uImpasto, a.impasto), e.uniform1f(n.uBrokenColor, a.brokenColor), e.uniform1f(n.uCanvasGrain, a.canvasGrain), e.uniform1f(n.uMetalPolish, a.metalPolish ?? 1), e.uniform1f(n.uMetalHeightScale, a.metalHeightScale ?? 1), e.uniform1f(n.uNucleiDrift, a.nucleiDrift), e.uniform1f(n.uPaletteDrift, a.paletteDrift), e.uniform1f(n.uBreathDepth, a.breathDepth), e.uniform1f(n.uBreathPeriod, a.breathPeriod), e.uniform1f(n.uSaturation, a.saturation), e.uniform1f(n.uPaperGrain, a.paperGrain), e.uniform1f(n.uAlpha, a.alpha), e.uniform1f(n.uVividness, a.vividness ?? 1), e.uniform1i(n.uImage, 0), e.uniform1f(n.uBlurMin, a.imageBlur?.min ?? .004), e.uniform1f(n.uBlurMax, a.imageBlur?.max ?? .06);
952
- let c = e.drawingBufferHeight > 0 ? e.drawingBufferWidth / e.drawingBufferHeight : 1;
953
- e.uniform1f(n.uImageAspect, c);
963
+ e.uniform1f(n.uWetEdge, o.wetEdge), e.uniform1f(n.uGranulation, o.granulation), e.uniform1f(n.uImpasto, o.impasto), e.uniform1f(n.uBrokenColor, o.brokenColor), e.uniform1f(n.uCanvasGrain, o.canvasGrain), e.uniform1f(n.uMetalPolish, o.metalPolish ?? 1), e.uniform1f(n.uMetalHeightScale, o.metalHeightScale ?? 1), e.uniform1f(n.uNucleiDrift, o.nucleiDrift), e.uniform1f(n.uPaletteDrift, o.paletteDrift), e.uniform1f(n.uBreathDepth, o.breathDepth), e.uniform1f(n.uBreathPeriod, o.breathPeriod), e.uniform1f(n.uSaturation, o.saturation), e.uniform1f(n.uPaperGrain, o.paperGrain), e.uniform1f(n.uAlpha, r ? 1 : o.alpha), e.uniform1f(n.uVividness, o.vividness ?? 1), e.uniform1i(n.uImage, 0), e.uniform1f(n.uBlurMin, o.imageBlur?.min ?? .004), e.uniform1f(n.uBlurMax, o.imageBlur?.max ?? .06);
964
+ let l = e.drawingBufferHeight > 0 ? e.drawingBufferWidth / e.drawingBufferHeight : 1;
965
+ e.uniform1f(n.uImageAspect, l);
954
966
  };
955
967
  }
956
968
  //#endregion
957
- //#region src/components/aurora/composables/frameLoop.ts
958
- var W = .001, xt = 1e-4;
959
- function G(e, t, i) {
960
- let a = r(n(e), t), o = a.burst;
961
- if (i > 0) {
962
- let t = e.acceleration.value, n = e.velocity.value, r = -(t.x * n.x + t.y * n.y);
963
- r > 0 && (o = Math.min(1, o + r * 3));
964
- }
969
+ //#region src/components/aurora/composables/glSetup.ts
970
+ var G = "[Aurora]", vt = /* @__PURE__ */ "uTime.uPalette.uStopCount.uNucleiCount.uNucleiPos.uNucleiRadius.uNucleiPaletteBias.uNucleiValueBias.uNucleiDriftRadius.uNucleiDriftPhase.uNucleiElong.uNucleiAngle.uSoftmaxBeta.uValueVariance.uWarpAmount.uWarpScale.uWarpDrift.uWarpMode.uNoiseOctaves.uMedium.uHuePath.uFlowPattern.uFlowFocal.uFlowAngle.uFlowCurl.uCursor.uCursorStrength.uCursorRadius.uStrokeAmount.uStrokeScale.uStrokeAnisotropy.uStrokeLayers.uStrokeMode.uStrokeOrient.uLightDir.uLightColor.uWetEdge.uGranulation.uImpasto.uBrokenColor.uCanvasGrain.uMetalPolish.uMetalHeightScale.uNucleiDrift.uPaletteDrift.uBreathDepth.uBreathPeriod.uSaturation.uPaperGrain.uAlpha.uVividness.uImage.uBlurMin.uBlurMax.uImageAspect".split(".");
971
+ function yt(e, t, n) {
972
+ let r = O(e, e.VERTEX_SHADER, t, G), i = O(e, e.FRAGMENT_SHADER, n, G), a = te(e, r, i, G);
973
+ e.useProgram(a);
974
+ let o = e.createVertexArray();
975
+ e.bindVertexArray(o);
976
+ let s = e.createBuffer();
977
+ e.bindBuffer(e.ARRAY_BUFFER, s), e.bufferData(e.ARRAY_BUFFER, new Float32Array([
978
+ -1,
979
+ -1,
980
+ 3,
981
+ -1,
982
+ -1,
983
+ 3
984
+ ]), e.STATIC_DRAW);
985
+ let c = e.getAttribLocation(a, "aPos");
986
+ e.enableVertexAttribArray(c), e.vertexAttribPointer(c, 2, e.FLOAT, !1, 0, 0);
987
+ let l = {};
988
+ for (let t of vt) l[t] = e.getUniformLocation(a, t);
965
989
  return {
966
- x: a.cx,
967
- y: a.cy,
968
- strength: a.strength,
969
- radius: a.radius,
970
- velX: a.velX,
971
- velY: a.velY,
972
- burst: o
990
+ program: a,
991
+ vs: r,
992
+ fs: i,
993
+ uniforms: l,
994
+ geometry: {
995
+ vao: o,
996
+ buf: s
997
+ }
973
998
  };
974
999
  }
975
- function K(e) {
976
- let t = e.attractorVelocity.value;
977
- return e.engagement.value > W || e.speed.value > W || e.burst.value > W || Math.hypot(t.x, t.y) > xt;
978
- }
979
- function St(e) {
980
- let { gl: t, prog: n, uniforms: r, pointerField: i, getCursorScalars: a, getConfig: o, getReducedMotion: s } = e, c = (e) => 1 - e, l = null, u = {
981
- x: .5,
982
- y: .5,
983
- strength: 0,
984
- radius: .25,
985
- velX: 0,
986
- velY: 0,
987
- burst: 0
988
- };
989
- function d() {
990
- return +!s();
991
- }
992
- function f(e) {
993
- if (t.useProgram(n), t.uniform2f(r.uCursor, u.x, c(u.y)), t.uniform1f(r.uCursorStrength, u.strength), t.uniform1f(r.uCursorRadius, u.radius), t.uniform2f(r.uCursorVelocity, u.velX, -u.velY), t.uniform1f(r.uCursorBurst, u.burst), t.uniform1f(r.uTime, e), o().interactivity?.light) {
994
- let n = d(), i = e * .25, a = Math.cos(i) * .5, o = Math.sin(i) * .5, s = (u.x - .5) * 2, c = (.5 - u.y) * 2, l = u.strength * n;
995
- a = a * (1 - l) + s * l, o = o * (1 - l) + c * l, t.uniform3f(r.uLightDir, a * n + (1 - n) * -.5, o * n + (1 - n) * .6, .62);
996
- }
997
- t.clear(t.COLOR_BUFFER_BIT), t.drawArrays(t.TRIANGLES, 0, 3);
998
- }
999
- function p() {
1000
- if (s()) return !1;
1001
- let e = o();
1002
- return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0 ? !0 : K(i);
1003
- }
1004
- return {
1005
- frame: (e) => {
1006
- let t = d(), n = l === null ? 0 : Math.max(0, (e - l) * 1e3);
1007
- l = e, i.tick(t === 0 ? 0 : n), u = G(i, a(), t), f(e);
1008
- },
1009
- needsAnimation: p
1000
+ function bt(e) {
1001
+ return (t) => {
1002
+ let n = e.getConfig().source === "image", { program: r, vs: i, fs: a, uniforms: o, geometry: s } = yt(t, xe, n ? Pe : Ne), c = n ? be(t, r, e.imageCoordinator) : null, l = (e) => {
1003
+ t.viewport(0, 0, e.w, e.h), t.useProgram(r);
1004
+ }, u = _t(t, r, o, e.opaquePresentation), d = (e) => u(e);
1005
+ e.registerConfigUploader(d), d(e.getConfig());
1006
+ let f = Ie({
1007
+ gl: t,
1008
+ prog: r,
1009
+ uniforms: o,
1010
+ pointerField: e.pointerField,
1011
+ getCursorScalars: e.getCursorScalars,
1012
+ getConfig: e.getConfig,
1013
+ getReducedMotion: e.getReducedMotion,
1014
+ getRenderTime: e.getRenderTime
1015
+ });
1016
+ return t.clearColor(0, 0, 0, +!!e.opaquePresentation), t.disable(t.DEPTH_TEST), e.opaquePresentation ? t.disable(t.BLEND) : (t.enable(t.BLEND), t.blendFunc(t.ONE, t.ONE_MINUS_SRC_ALPHA)), {
1017
+ frame: f.frame,
1018
+ shouldContinue: f.needsAnimation,
1019
+ resize: l,
1020
+ teardown: () => {
1021
+ e.registerConfigUploader(null), t.deleteProgram(r), t.deleteShader(i), t.deleteShader(a), t.deleteBuffer(s.buf), t.deleteVertexArray(s.vao), c?.();
1022
+ }
1023
+ };
1010
1024
  };
1011
1025
  }
1012
1026
  //#endregion
1013
1027
  //#region src/components/aurora/constants/shaders/aurora.wgsl.ts
1014
- var Ct = `
1015
- const MAX_NUCLEI: i32 = 6;
1028
+ var xt = `
1029
+ const MAX_NUCLEI: i32 = 8;
1016
1030
  const MAX_STOPS: i32 = 8;
1017
1031
  const PI: f32 = 3.141592653589793;
1018
1032
 
@@ -1029,10 +1043,10 @@ struct Uniforms {
1029
1043
  // scalars2: (uAlpha, uNucleiDrift, uPaletteDrift, uBreathDepth)
1030
1044
  scalars2: vec4<f32>,
1031
1045
  // scalars3: (uBreathPeriod, uCursorStrength, uCursorRadius, uVividness)
1032
- // BD.W-AUR-VIVIDNESS — the .w pad lane now carries the §3 chroma-floor strength.
1046
+ // The .w pad lane now carries the chroma-floor strength.
1033
1047
  scalars3: vec4<f32>,
1034
1048
  // cursor: (uCursor.x, uCursor.y, uMetalPolish, uMetalHeightScale)
1035
- // BG.W-AUR-METAL-FINISH — the two free pad lanes carry the metal-medium knobs (the
1049
+ // The two free pad lanes carry the metal-medium knobs (the
1036
1050
  // metal light is cursor-synthesized so it reads in-struct on the WGSL primary too).
1037
1051
  cursor: vec4<f32>,
1038
1052
  // ints: (uStopCount, uNucleiCount, uWarpMode, uNoiseOctaves)
@@ -1040,10 +1054,10 @@ struct Uniforms {
1040
1054
  // ints1: (uMedium, uHuePath, _, _)
1041
1055
  ints1: vec4<i32>,
1042
1056
  palette: array<vec4<f32>, 8>,
1043
- nuc0: array<vec4<f32>, 6>,
1044
- nuc1: array<vec4<f32>, 6>,
1045
- nuc2: array<vec4<f32>, 6>,
1046
- // BC.W-VIZ-AURORA (T4) — the painterly-medium scalar lanes. APPENDED after the
1057
+ nuc0: array<vec4<f32>, 8>,
1058
+ nuc1: array<vec4<f32>, 8>,
1059
+ nuc2: array<vec4<f32>, 8>,
1060
+ // The painterly-medium scalar lanes are appended after the
1047
1061
  // arrays so EVERY existing offset (and the smooth-default parity capture) is
1048
1062
  // byte-identical (these lanes are written 0 on a smooth config). scalars4:
1049
1063
  // (uStrokeAmount, uStrokeScale, uStrokeAnisotropy, uCanvasGrain). scalars5:
@@ -1062,7 +1076,7 @@ const K_NUCLEI: f32 = 14.0;
1062
1076
  const K_PAL: f32 = 24.0;
1063
1077
  const K_WARP: f32 = 5.0;
1064
1078
 
1065
- // ── Noise (aurora-local hash21/vnoise/fbm — legitimately divergent per AV.W2 §3a) ──
1079
+ // ── Noise (Aurora-local hash21/vnoise/fbm, deliberately distinct) ──────────
1066
1080
  fn hash21(p0: vec2<f32>) -> f32 {
1067
1081
  var p = fract(p0 * vec2<f32>(123.34, 456.21));
1068
1082
  p = p + dot(p, p + 45.32);
@@ -1086,7 +1100,7 @@ fn vnoise(p: vec2<f32>) -> f32 {
1086
1100
  return mix(mix(a, b, uu.x), mix(c, d, uu.x), uu.y);
1087
1101
  }
1088
1102
 
1089
- ${C}
1103
+ ${w}
1090
1104
 
1091
1105
  fn fbm(p0: vec2<f32>) -> f32 {
1092
1106
  var v = 0.0;
@@ -1102,16 +1116,31 @@ fn fbm(p0: vec2<f32>) -> f32 {
1102
1116
  return v;
1103
1117
  }
1104
1118
 
1105
- ${v}
1106
- ${x}
1107
- ${g}
1119
+ fn potentialFBM(p: vec2f) -> f32 { return fbm(p); }
1120
+
1121
+ const CURL_EPS: f32 = 0.012;
1122
+
1123
+ // ∇×ψ in 2D — the divergence-free flow direction. Central finite differences of the
1124
+ // scalar potential give the partials; the cross-pairing (∂y, −∂x) is the 2D curl.
1125
+ fn curlFBM(p: vec2f) -> vec2f {
1126
+ let dx = potentialFBM(p + vec2f(CURL_EPS, 0.0)) - potentialFBM(p - vec2f(CURL_EPS, 0.0));
1127
+ let dy = potentialFBM(p + vec2f(0.0, CURL_EPS)) - potentialFBM(p - vec2f(0.0, CURL_EPS));
1128
+ // ψ-gradient is (dx, dy)/(2·eps); the curl rotates it 90°: (∂ψ/∂y, −∂ψ/∂x).
1129
+ let g = vec2f(dx, dy) / (2.0 * CURL_EPS);
1130
+ return vec2f(g.y, -g.x);
1131
+ }
1132
+
1133
+
1134
+ ${y}
1135
+ ${S}
1136
+ ${_}
1108
1137
 
1109
1138
  // Interleaved Gradient Noise (Jimenez) — display-space 1-LSB dither.
1110
1139
  fn ign(p: vec2<f32>) -> f32 {
1111
1140
  return fract(52.9829189 * fract(dot(p, vec2<f32>(0.06711056, 0.00583715))));
1112
1141
  }
1113
1142
 
1114
- // Cellular / Worley f1.
1143
+ // Cellular, Worley f1.
1115
1144
  fn cellular(p: vec2<f32>) -> f32 {
1116
1145
  let i = floor(p);
1117
1146
  let f = fract(p);
@@ -1127,7 +1156,7 @@ fn cellular(p: vec2<f32>) -> f32 {
1127
1156
  return sqrt(m);
1128
1157
  }
1129
1158
 
1130
- // ── Warp (Quilez double warp + the three warp modes + the cursor swirl) ──
1159
+ // ── Warp (Quilez double warp + the four warp modes + the cursor swirl) ──
1131
1160
  fn domainWarp(p: vec2<f32>, t: f32) -> vec2<f32> {
1132
1161
  let warpScale = u.scalars1.x;
1133
1162
  let warpDrift = u.scalars1.y;
@@ -1153,6 +1182,9 @@ fn domainWarp(p: vec2<f32>, t: f32) -> vec2<f32> {
1153
1182
  let c1 = cellular(p * warpScale * 1.2);
1154
1183
  let c2 = cellular(p * warpScale * 1.2 + vec2<f32>(11.0, 7.0));
1155
1184
  warp = mix(r, vec2<f32>(c1, c2), 0.5);
1185
+ } else if (warpMode == 3) {
1186
+ let fp = p * warpScale + vec2<f32>(t * warpDrift * K_WARP);
1187
+ warp = curlFBM(fp);
1156
1188
  }
1157
1189
  var warped = p + warpAmount * warp;
1158
1190
 
@@ -1241,7 +1273,7 @@ fn saturate3(c: vec3<f32>, amt: f32) -> vec3<f32> {
1241
1273
  return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3<f32>(0.0));
1242
1274
  }
1243
1275
 
1244
- // BD.W-AUR-VIVIDNESS — the §3 chroma FLOOR, the WGSL twin of the GLSL vividnessFloor.
1276
+ // The chroma floor, the WGSL twin of the GLSL vividnessFloor.
1245
1277
  // OKLab chroma lifted toward the floor, hue + L preserved; below VIVID_EPS the (a,b)
1246
1278
  // direction is precision noise so the lift synthesizes along the WARM anchor (amber,
1247
1279
  // both components positive) — never teal/navy. vividness (scalars3.w) == 0 is a no-op
@@ -1280,7 +1312,7 @@ fn aces(color0: vec3<f32>) -> vec3<f32> {
1280
1312
  return clamp(mix(color, newPeak * vec3<f32>(1.0), g), vec3<f32>(0.0), vec3<f32>(1.0));
1281
1313
  }
1282
1314
 
1283
- // ── BC.W-VIZ-AURORA (T4) — the painterly-medium bodies (the keystone WGSL port) ──
1315
+ // ── Painterly-medium bodies (the keystone WGSL port) ───────────────────────
1284
1316
  // Spliced HERE, after every helper the mediums consume is defined (domainWarp,
1285
1317
  // nucleiField, samplePalette, linOklab, saturate3, oklabToOklch/oklchToOklab,
1286
1318
  // hash21/vnoise/fbm) and before the vertex/fragment stages. applyMedium(col,p,t)
@@ -1298,17 +1330,17 @@ fn mGranulation() -> f32 { return u.scalars5.y; }
1298
1330
  fn mBrokenColor() -> f32 { return u.scalars5.z; }
1299
1331
  fn mKuwaharaQ() -> f32 { return u.scalars5.w; }
1300
1332
  fn mKuwaharaRadius() -> f32 { return u.kuwahara.x; }
1301
- // BG.W-AUR-METAL-FINISH — the metal-medium knobs ride the FREE cursor.z/.w pad lanes
1333
+ // The metal-medium knobs ride the free cursor.z/.w pad lanes
1302
1334
  // (uniformBridgeWGPU packs them; written 0 on a non-metal config → no-op). These read
1303
1335
  // the IN-STRUCT cursor — the metal light is cursor-synthesized so it crosses to WGSL
1304
- // (a phantom uLightDir read would be flat on the primary — uLightDir is .frag-only).
1336
+ // (a phantom uLightDir read would be flat on the primary; uLightDir is fragment-only).
1305
1337
  fn mMetalPolish() -> f32 { return u.cursor.z; }
1306
1338
  fn mMetalHeightScale() -> f32 { return u.cursor.w; }
1307
1339
 
1308
- // ── Gradient pack (BG.W-AUR-METAL-FINISH; the WGSL twin) ────────────────────
1340
+ // ── Gradient pack (the WGSL twin) ──────────────────────────────────────────
1309
1341
  // structureTensorField re-plumbs its already-computed luma gradient (Gx,Gy) out
1310
- // through the return's .w lane so the metal medium pays ZERO extra taps. 12 bits per
1311
- // bounded component ([-4,4]) into one f32; the crayon/kuwahara callers read only .xy/.z.
1342
+ // through the return value's .w lane so the metal medium pays ZERO extra taps. 12 bits per
1343
+ // bounded component ([-4,4]) fit into one f32; the crayon/kuwahara callers read only .xy/.z.
1312
1344
  fn packGrad(gx: f32, gy: f32) -> f32 {
1313
1345
  let qx = floor(clamp(gx * 0.125 + 0.5, 0.0, 1.0) * 4095.0);
1314
1346
  let qy = floor(clamp(gy * 0.125 + 0.5, 0.0, 1.0) * 4095.0);
@@ -1340,10 +1372,10 @@ fn brokenColorJitter(c: vec3<f32>, hueSeed: f32, valueSeed: f32, strength: f32)
1340
1372
  return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3<f32>(0.0));
1341
1373
  }
1342
1374
 
1343
- // ── Structure-tensor / edge-tangent-flow (AW.W4.1; the WGSL twin) ───────────
1375
+ // ── Structure tensor / edge-tangent flow (the WGSL twin) ───────────────────
1344
1376
  // Returns vec4(tangent.x, tangent.y, A, packGrad(Gx,Gy)) — the minor-eigenvector
1345
- // edge-tangent + coherence + the metal medium's packed gradient (BG.W-AUR-METAL-FINISH;
1346
- // the .xy/.z crayon/kuwahara callers are byte-unchanged).
1377
+ // edge-tangent + coherence + the metal medium's packed gradient. The .xy/.z
1378
+ // crayon/kuwahara callers are byte-unchanged.
1347
1379
  fn structureTensorField(p: vec2<f32>, t: f32, fallbackDir: vec2<f32>) -> vec4<f32> {
1348
1380
  let e = 0.0035;
1349
1381
  let l00 = dot(sampleBase(p + vec2<f32>(-e, -e), t), W_LUMA);
@@ -1630,18 +1662,18 @@ fn mediumMetalGradient(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {
1630
1662
  // (pastel/watercolor/crayon) + kuwahara + metal port their own bodies; the
1631
1663
  // oil/vangogh/oil-pastel STROKE cascade renders the anisotropic-Kuwahara PAINTERLY
1632
1664
  // finish here (a real oil-paint read, never the smooth core — the "NO FALLBACKS on
1633
- // Safari" mandate; the full per-dab Starry-Night stroke WGSL is the booked
1634
- // W-AURORA-WGPU-MEDIUMS-STROKES tail).
1665
+ // Safari" mandate; the full per-dab Starry-Night stroke WGSL remains a separate
1666
+ // full-fidelity port).
1635
1667
  fn applyMedium(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {
1636
1668
  let medium = u.ints1.x;
1637
1669
  if (medium == 1) { return mediumPastel(col, p, t); }
1638
1670
  if (medium == 2) { return mediumWatercolor(col, p, t); }
1639
1671
  if (medium == 4) { return mediumCrayon(col, p, t); }
1640
- // BG.W-AUR-METAL-FINISH — metal DUAL-PORTS (a .frag-only metal is invisible on the
1641
- // WGSL primary of both browsers); 8 metal, 9 metal-gradient.
1672
+ // Metal uses both renderer paths; a fragment-only implementation is invisible on the
1673
+ // WGSL primary. IDs 8 and 9 select metal and metal-gradient respectively.
1642
1674
  if (medium == 8) { return mediumMetal(col, p, t); }
1643
1675
  if (medium == 9) { return mediumMetalGradient(col, p, t); }
1644
- // oil(3) / vangogh(5) / oil-pastel(6) / kuwahara(7) all render the painterly
1676
+ // oil(3), vangogh(5), oil-pastel(6), kuwahara(7) all render the painterly
1645
1677
  // anisotropic-Kuwahara finish on the WGSL primary (the WebGL2 fallback carries the
1646
1678
  // full per-dab stroke cascade for oil/vangogh/oil-pastel).
1647
1679
  if (medium == 3 || medium == 5 || medium == 6 || medium == 7) {
@@ -1695,17 +1727,23 @@ fn fs_main(in: VSOut) -> @location(0) vec4<f32> {
1695
1727
  let breath = sin(t * 6.2831 / max(breathPeriod, 1.0));
1696
1728
  col = col * (1.0 + breathDepth * breath * 0.5);
1697
1729
 
1698
- // BC.W-VIZ-AURORA (T4) — the painterly-medium dispatch. uMedium 0 (smooth) is a
1699
- // no-op pass-through so the DEFAULT smooth config is byte-identical to the prior
1700
- // WGSL primary (the parity capture); uMedium 1-7 render the ported painterly
1730
+ // Painterly-medium dispatch. uMedium 0 (smooth) is a
1731
+ // no-op pass-through so the DEFAULT smooth config renders unchanged
1732
+ // (the parity capture); uMedium 1-7 render the painterly
1701
1733
  // bodies (pastel/watercolor/crayon/kuwahara) or the anisotropic-Kuwahara finish
1702
1734
  // (oil/vangogh/oil-pastel) — never a silent smooth degrade on Safari 26.
1703
1735
  col = applyMedium(col, pN, t);
1704
1736
 
1737
+ // Cursor-local luminance lean, byte-parallel with the GLSL path.
1738
+ let cursorDelta = pN - u.cursor.xy;
1739
+ let cursorRadius = max(u.scalars3.z, 0.01);
1740
+ let cursorLean = exp(-dot(cursorDelta, cursorDelta) / (cursorRadius * cursorRadius * 0.5));
1741
+ col = col * (1.0 + 0.12 * cursorLean * u.scalars3.y);
1742
+
1705
1743
  // Saturation trim.
1706
1744
  col = saturate3(col, saturation);
1707
1745
 
1708
- // BD.W-AUR-VIVIDNESS — the §3 chroma floor (warm-anchored, hue-preserving). The WGSL
1746
+ // The chroma floor is warm-anchored and hue-preserving. The WGSL
1709
1747
  // twin of the GLSL call; vividness:0 is a no-op so the smooth/pale default stays gated.
1710
1748
  col = vividnessFloor(col, vividness);
1711
1749
 
@@ -1727,14 +1765,14 @@ fn fs_main(in: VSOut) -> @location(0) vec4<f32> {
1727
1765
  // Premultiply on DISPLAY-space color (matches the WebGL2 path: OETF, then premul).
1728
1766
  return vec4<f32>(col * alpha, alpha);
1729
1767
  }
1730
- `, wt = `
1731
- const MAX_NUCLEI: i32 = 6;
1768
+ `, St = `
1769
+ const MAX_NUCLEI: i32 = 8;
1732
1770
  const PI: f32 = 3.141592653589793;
1733
1771
  const BLUR_RINGS: i32 = 3;
1734
1772
  const BLUR_SECTORS: i32 = 8;
1735
1773
 
1736
1774
  // The image program's OWN uniform struct (packed vec4 lanes — trivial std140/WGSL
1737
- // alignment). DISTINCT from the palette program's 576-byte struct (a separate program).
1775
+ // alignment). DISTINCT from the palette program's 672-byte struct (a separate program).
1738
1776
  struct ImageUniforms {
1739
1777
  // scalars0: (uTime, uWarpAmount, uWarpScale, uWarpDrift)
1740
1778
  scalars0: vec4<f32>,
@@ -1749,8 +1787,8 @@ struct ImageUniforms {
1749
1787
  // ints: (uNucleiCount, uNoiseOctaves, _, _)
1750
1788
  ints: vec4<i32>,
1751
1789
  // nuc0[i] = (pos.x, pos.y, radius, valueBias); nuc1[i] = (driftRadius, driftPhase, elong, angle)
1752
- nuc0: array<vec4<f32>, 6>,
1753
- nuc1: array<vec4<f32>, 6>,
1790
+ nuc0: array<vec4<f32>, 8>,
1791
+ nuc1: array<vec4<f32>, 8>,
1754
1792
  };
1755
1793
 
1756
1794
  @group(0) @binding(0) var<uniform> u: ImageUniforms;
@@ -1776,7 +1814,7 @@ fn vnoise(p: vec2<f32>) -> f32 {
1776
1814
  let d = hash21(i + vec2<f32>(1.0, 1.0));
1777
1815
  return mix(mix(a, b, uu.x), mix(c, d, uu.x), uu.y);
1778
1816
  }
1779
- ${C}
1817
+ ${w}
1780
1818
  fn fbm(p0: vec2<f32>) -> f32 {
1781
1819
  var v = 0.0;
1782
1820
  var a = 0.5;
@@ -1794,8 +1832,8 @@ fn ign(p: vec2<f32>) -> f32 {
1794
1832
  return fract(52.9829189 * fract(dot(p, vec2<f32>(0.06711056, 0.00583715))));
1795
1833
  }
1796
1834
 
1797
- ${v}
1798
- ${x}
1835
+ ${y}
1836
+ ${S}
1799
1837
 
1800
1838
  fn linOklab(lin: vec3<f32>) -> vec3<f32> {
1801
1839
  let lms = LINEAR_SRGB_TO_LMS * lin;
@@ -1957,6 +1995,11 @@ fn fs_main(in: VSOut) -> @location(0) vec4<f32> {
1957
1995
  let radius = mix(blurMin, blurMax, zone);
1958
1996
  var col = zoneBlur(uv, radius);
1959
1997
 
1998
+ let cursorDelta = uv - u.cursor.xy;
1999
+ let cursorRadius = max(u.scalars1.w, 0.01);
2000
+ let cursorLean = exp(-dot(cursorDelta, cursorDelta) / (cursorRadius * cursorRadius * 0.5));
2001
+ col = col * (1.0 + 0.12 * cursorLean * u.scalars1.z);
2002
+
1960
2003
  col = saturate3(col, saturation);
1961
2004
  col = vividnessFloor(col, vividness);
1962
2005
 
@@ -1971,7 +2014,7 @@ fn fs_main(in: VSOut) -> @location(0) vec4<f32> {
1971
2014
 
1972
2015
  return vec4<f32>(col * alpha, alpha);
1973
2016
  }
1974
- `, q = {
2017
+ `, K = {
1975
2018
  scalars0: 0,
1976
2019
  scalars1: 4,
1977
2020
  scalars2: 8,
@@ -1981,40 +2024,40 @@ fn fs_main(in: VSOut) -> @location(0) vec4<f32> {
1981
2024
  ints1: 24,
1982
2025
  palette: 28,
1983
2026
  nuc0: 60,
1984
- nuc1: 84,
1985
- nuc2: 108,
1986
- scalars4: 132,
1987
- scalars5: 136,
1988
- kuwahara: 140
1989
- }, Tt = .01, Et = 8, Dt = 4, Ot = (e) => 1 - e;
1990
- function kt() {
1991
- let e = /* @__PURE__ */ new ArrayBuffer(576);
2027
+ nuc1: 92,
2028
+ nuc2: 124,
2029
+ scalars4: 156,
2030
+ scalars5: 160,
2031
+ kuwahara: 164
2032
+ }, Ct = .01, wt = 8, Tt = 4, Et = (e) => 1 - e;
2033
+ function Dt() {
2034
+ let e = /* @__PURE__ */ new ArrayBuffer(672);
1992
2035
  return {
1993
2036
  buffer: e,
1994
2037
  f32: new Float32Array(e),
1995
2038
  i32: new Int32Array(e),
1996
- paletteScratch: new Float32Array(24)
2039
+ paletteScratch: /* @__PURE__ */ new Float32Array(24)
1997
2040
  };
1998
2041
  }
1999
- function At(e, t, n, r) {
2000
- let { f32: i, i32: a, paletteScratch: o } = e;
2001
- i[q.scalars0 + 0] = r, i[q.scalars0 + 1] = t.softmaxBeta, i[q.scalars0 + 2] = t.valueVariance, i[q.scalars0 + 3] = t.warpAmount, i[q.scalars1 + 0] = t.warpScale, i[q.scalars1 + 1] = t.warpDrift, i[q.scalars1 + 2] = t.saturation, i[q.scalars1 + 3] = t.paperGrain, i[q.scalars2 + 0] = t.alpha, i[q.scalars2 + 1] = t.nucleiDrift, i[q.scalars2 + 2] = t.paletteDrift, i[q.scalars2 + 3] = t.breathDepth, i[q.scalars3 + 0] = t.breathPeriod, i[q.scalars3 + 1] = n.strength, i[q.scalars3 + 2] = n.radius, i[q.scalars3 + 3] = t.vividness ?? 1, i[q.cursor + 0] = n.x, i[q.cursor + 1] = Ot(n.y), i[q.cursor + 2] = t.metalPolish ?? 1, i[q.cursor + 3] = t.metalHeightScale ?? 1, a[q.ints0 + 0] = Math.min(t.palette.length, 8);
2002
- let s = Math.min(t.nuclei.length, 6);
2003
- a[q.ints0 + 1] = s, a[q.ints0 + 2] = mt[t.warpMode], a[q.ints0 + 3] = t.noiseOctaves, a[q.ints1 + 0] = B[t.medium], a[q.ints1 + 1] = V[t.huePath ?? "shorter"], a[q.ints1 + 2] = 0, a[q.ints1 + 3] = 0, F(t.palette, 8, o);
2042
+ function Ot(e, t, n, r, i = t.alpha) {
2043
+ let { f32: a, i32: o, paletteScratch: s } = e;
2044
+ a[K.scalars0 + 0] = r, a[K.scalars0 + 1] = t.softmaxBeta, a[K.scalars0 + 2] = t.valueVariance, a[K.scalars0 + 3] = t.warpAmount, a[K.scalars1 + 0] = t.warpScale, a[K.scalars1 + 1] = t.warpDrift, a[K.scalars1 + 2] = t.saturation, a[K.scalars1 + 3] = t.paperGrain, a[K.scalars2 + 0] = i, a[K.scalars2 + 1] = t.nucleiDrift, a[K.scalars2 + 2] = t.paletteDrift, a[K.scalars2 + 3] = t.breathDepth, a[K.scalars3 + 0] = t.breathPeriod, a[K.scalars3 + 1] = n.strength, a[K.scalars3 + 2] = n.radius, a[K.scalars3 + 3] = t.vividness ?? 1, a[K.cursor + 0] = n.x, a[K.cursor + 1] = Et(n.y), a[K.cursor + 2] = t.metalPolish ?? 1, a[K.cursor + 3] = t.metalHeightScale ?? 1, o[K.ints0 + 0] = Math.min(t.palette.length, 8);
2045
+ let c = Math.min(t.nuclei.length, 8);
2046
+ o[K.ints0 + 1] = c, o[K.ints0 + 2] = dt[t.warpMode], o[K.ints0 + 3] = t.noiseOctaves, o[K.ints1 + 0] = st[t.medium], o[K.ints1 + 1] = ct[t.huePath ?? "shorter"], o[K.ints1 + 2] = 0, o[K.ints1 + 3] = 0, H(t.palette, 8, s);
2004
2047
  for (let e = 0; e < 8; e++) {
2005
- let t = q.palette + e * 4;
2006
- i[t + 0] = o[e * 3 + 0], i[t + 1] = o[e * 3 + 1], i[t + 2] = o[e * 3 + 2], i[t + 3] = 0;
2048
+ let t = K.palette + e * 4;
2049
+ a[t + 0] = s[e * 3 + 0], a[t + 1] = s[e * 3 + 1], a[t + 2] = s[e * 3 + 2], a[t + 3] = 0;
2007
2050
  }
2008
- for (let e = 0; e < 6; e++) {
2009
- let n = q.nuc0 + e * 4, r = q.nuc1 + e * 4, a = q.nuc2 + e * 4;
2010
- if (e < s) {
2051
+ for (let e = 0; e < 8; e++) {
2052
+ let n = K.nuc0 + e * 4, r = K.nuc1 + e * 4, i = K.nuc2 + e * 4;
2053
+ if (e < c) {
2011
2054
  let o = t.nuclei[e];
2012
- i[n + 0] = o.x, i[n + 1] = Ot(o.y), i[n + 2] = o.radius, i[n + 3] = o.paletteBias, i[r + 0] = o.valueBias, i[r + 1] = o.driftRadius, i[r + 2] = o.driftPhase, i[r + 3] = o.elongation ?? 1, i[a + 0] = -(o.angle ?? 0) * Math.PI / 180, i[a + 1] = 0, i[a + 2] = 0, i[a + 3] = 0;
2013
- } else i[n + 0] = 0, i[n + 1] = 0, i[n + 2] = 0, i[n + 3] = 0, i[r + 0] = 0, i[r + 1] = 0, i[r + 2] = 0, i[r + 3] = 1, i[a + 0] = 0, i[a + 1] = 0, i[a + 2] = 0, i[a + 3] = 0;
2055
+ a[n + 0] = o.x, a[n + 1] = Et(o.y), a[n + 2] = o.radius, a[n + 3] = o.paletteBias, a[r + 0] = o.valueBias, a[r + 1] = o.driftRadius, a[r + 2] = o.driftPhase, a[r + 3] = o.elongation ?? 1, a[i + 0] = -(o.angle ?? 0) * Math.PI / 180, a[i + 1] = 0, a[i + 2] = 0, a[i + 3] = 0;
2056
+ } else a[n + 0] = 0, a[n + 1] = 0, a[n + 2] = 0, a[n + 3] = 0, a[r + 0] = 0, a[r + 1] = 0, a[r + 2] = 0, a[r + 3] = 1, a[i + 0] = 0, a[i + 1] = 0, a[i + 2] = 0, a[i + 3] = 0;
2014
2057
  }
2015
- return i[q.scalars4 + 0] = t.strokeAmount, i[q.scalars4 + 1] = t.strokeScale, i[q.scalars4 + 2] = t.strokeAnisotropy, i[q.scalars4 + 3] = t.canvasGrain, i[q.scalars5 + 0] = t.wetEdge, i[q.scalars5 + 1] = t.granulation, i[q.scalars5 + 2] = t.brokenColor, i[q.scalars5 + 3] = t.kuwaharaQ ?? Dt, i[q.kuwahara + 0] = t.kuwaharaRadius ?? Tt, i[q.kuwahara + 1] = Et, i[q.kuwahara + 2] = 0, i[q.kuwahara + 3] = 0, e;
2058
+ return a[K.scalars4 + 0] = t.strokeAmount, a[K.scalars4 + 1] = t.strokeScale, a[K.scalars4 + 2] = t.strokeAnisotropy, a[K.scalars4 + 3] = t.canvasGrain, a[K.scalars5 + 0] = t.wetEdge, a[K.scalars5 + 1] = t.granulation, a[K.scalars5 + 2] = t.brokenColor, a[K.scalars5 + 3] = t.kuwaharaQ ?? Tt, a[K.kuwahara + 0] = t.kuwaharaRadius ?? Ct, a[K.kuwahara + 1] = wt, a[K.kuwahara + 2] = 0, a[K.kuwahara + 3] = 0, e;
2016
2059
  }
2017
- var J = {
2060
+ var q = {
2018
2061
  scalars0: 0,
2019
2062
  scalars1: 4,
2020
2063
  scalars2: 8,
@@ -2022,154 +2065,155 @@ var J = {
2022
2065
  cursor: 16,
2023
2066
  ints: 20,
2024
2067
  nuc0: 24,
2025
- nuc1: 48
2026
- }, jt = (e) => 1 - e;
2027
- function Mt() {
2028
- let e = /* @__PURE__ */ new ArrayBuffer(288);
2068
+ nuc1: 56
2069
+ }, kt = (e) => 1 - e;
2070
+ function At() {
2071
+ let e = /* @__PURE__ */ new ArrayBuffer(352);
2029
2072
  return {
2030
2073
  buffer: e,
2031
2074
  f32: new Float32Array(e),
2032
2075
  i32: new Int32Array(e)
2033
2076
  };
2034
2077
  }
2035
- function Nt(e, t, n, r, i) {
2036
- let { f32: a, i32: o } = e;
2037
- a[J.scalars0 + 0] = r, a[J.scalars0 + 1] = t.warpAmount, a[J.scalars0 + 2] = t.warpScale, a[J.scalars0 + 3] = t.warpDrift, a[J.scalars1 + 0] = t.softmaxBeta, a[J.scalars1 + 1] = t.nucleiDrift, a[J.scalars1 + 2] = n.strength, a[J.scalars1 + 3] = n.radius, a[J.scalars2 + 0] = t.saturation, a[J.scalars2 + 1] = t.paperGrain, a[J.scalars2 + 2] = t.alpha, a[J.scalars2 + 3] = t.vividness ?? 1, a[J.image + 0] = t.imageBlur?.min ?? .004, a[J.image + 1] = t.imageBlur?.max ?? .06, a[J.image + 2] = i, a[J.image + 3] = 0, a[J.cursor + 0] = n.x, a[J.cursor + 1] = jt(n.y), a[J.cursor + 2] = 0, a[J.cursor + 3] = 0;
2038
- let s = Math.min(t.nuclei.length, 6);
2039
- o[J.ints + 0] = s, o[J.ints + 1] = t.noiseOctaves, o[J.ints + 2] = 0, o[J.ints + 3] = 0;
2040
- for (let e = 0; e < 6; e++) {
2041
- let n = J.nuc0 + e * 4, r = J.nuc1 + e * 4;
2042
- if (e < s) {
2078
+ function jt(e, t, n, r, i, a = t.alpha) {
2079
+ let { f32: o, i32: s } = e;
2080
+ o[q.scalars0 + 0] = r, o[q.scalars0 + 1] = t.warpAmount, o[q.scalars0 + 2] = t.warpScale, o[q.scalars0 + 3] = t.warpDrift, o[q.scalars1 + 0] = t.softmaxBeta, o[q.scalars1 + 1] = t.nucleiDrift, o[q.scalars1 + 2] = n.strength, o[q.scalars1 + 3] = n.radius, o[q.scalars2 + 0] = t.saturation, o[q.scalars2 + 1] = t.paperGrain, o[q.scalars2 + 2] = a, o[q.scalars2 + 3] = t.vividness ?? 1, o[q.image + 0] = t.imageBlur?.min ?? .004, o[q.image + 1] = t.imageBlur?.max ?? .06, o[q.image + 2] = i, o[q.image + 3] = 0, o[q.cursor + 0] = n.x, o[q.cursor + 1] = kt(n.y), o[q.cursor + 2] = 0, o[q.cursor + 3] = 0;
2081
+ let c = Math.min(t.nuclei.length, 8);
2082
+ s[q.ints + 0] = c, s[q.ints + 1] = t.noiseOctaves, s[q.ints + 2] = 0, s[q.ints + 3] = 0;
2083
+ for (let e = 0; e < 8; e++) {
2084
+ let n = q.nuc0 + e * 4, r = q.nuc1 + e * 4;
2085
+ if (e < c) {
2043
2086
  let i = t.nuclei[e];
2044
- a[n + 0] = i.x, a[n + 1] = jt(i.y), a[n + 2] = i.radius, a[n + 3] = i.valueBias, a[r + 0] = i.driftRadius, a[r + 1] = i.driftPhase, a[r + 2] = i.elongation ?? 1, a[r + 3] = -(i.angle ?? 0) * Math.PI / 180;
2045
- } else a[n + 0] = 0, a[n + 1] = 0, a[n + 2] = 0, a[n + 3] = 0, a[r + 0] = 0, a[r + 1] = 0, a[r + 2] = 1, a[r + 3] = 0;
2087
+ o[n + 0] = i.x, o[n + 1] = kt(i.y), o[n + 2] = i.radius, o[n + 3] = i.valueBias, o[r + 0] = i.driftRadius, o[r + 1] = i.driftPhase, o[r + 2] = i.elongation ?? 1, o[r + 3] = -(i.angle ?? 0) * Math.PI / 180;
2088
+ } else o[n + 0] = 0, o[n + 1] = 0, o[n + 2] = 0, o[n + 3] = 0, o[r + 0] = 0, o[r + 1] = 0, o[r + 2] = 1, o[r + 3] = 0;
2046
2089
  }
2047
2090
  return e;
2048
2091
  }
2049
2092
  //#endregion
2050
2093
  //#region src/components/aurora/composables/wgpuSetup.ts
2051
- var Pt = 64, Ft = 8, Y = 2;
2052
- function It(e) {
2053
- let { canvas: t, getCursorScalars: n, getConfig: r, getReducedMotion: i, pointerField: a, getDecodedImage: o, registerImageUploader: s } = e, c = null;
2054
- return function(e, l, u) {
2055
- if (r().source === "image") return Lt(e, l, u, {
2094
+ var J = 2, Mt = {
2095
+ color: {
2096
+ srcFactor: "one",
2097
+ dstFactor: "one-minus-src-alpha",
2098
+ operation: "add"
2099
+ },
2100
+ alpha: {
2101
+ srcFactor: "one",
2102
+ dstFactor: "one-minus-src-alpha",
2103
+ operation: "add"
2104
+ }
2105
+ };
2106
+ function Nt(e) {
2107
+ let { canvas: t, opaquePresentation: n, getCursorScalars: r, getConfig: i, getReducedMotion: a, getRenderTime: o, pointerField: s, getDecodedImage: c, registerImageUploader: l } = e, u = null;
2108
+ return function(e, d, f) {
2109
+ if (i().source === "image") return Pt(e, d, f, {
2056
2110
  canvas: t,
2057
- getCursorScalars: n,
2058
- getConfig: r,
2059
- getReducedMotion: i,
2060
- pointerField: a,
2061
- getDecodedImage: o,
2062
- registerImageUploader: s
2111
+ opaquePresentation: n,
2112
+ getCursorScalars: r,
2113
+ getConfig: i,
2114
+ getReducedMotion: a,
2115
+ getRenderTime: o,
2116
+ pointerField: s,
2117
+ getDecodedImage: c,
2118
+ registerImageUploader: l
2063
2119
  });
2064
- let d = e.createShaderModule({
2120
+ let p = e.createShaderModule({
2065
2121
  label: "[Aurora] aurora.wgsl",
2066
- code: Ct
2067
- }), f = e.createBuffer({
2122
+ code: xt
2123
+ }), m = e.createBuffer({
2068
2124
  label: "[Aurora] uniforms",
2069
- size: 576,
2070
- usage: Pt | Ft
2071
- }), p = e.createBindGroupLayout({
2125
+ size: 672,
2126
+ usage: 72
2127
+ }), h = e.createBindGroupLayout({
2072
2128
  label: "[Aurora] bind-group-0",
2073
2129
  entries: [{
2074
2130
  binding: 0,
2075
- visibility: Y,
2131
+ visibility: J,
2076
2132
  buffer: { type: "uniform" }
2077
2133
  }]
2078
- }), m = e.createRenderPipeline({
2134
+ }), g = e.createRenderPipeline({
2079
2135
  label: "[Aurora] pipeline",
2080
- layout: e.createPipelineLayout({ bindGroupLayouts: [p] }),
2136
+ layout: e.createPipelineLayout({ bindGroupLayouts: [h] }),
2081
2137
  vertex: {
2082
- module: d,
2138
+ module: p,
2083
2139
  entryPoint: "vs_main"
2084
2140
  },
2085
2141
  fragment: {
2086
- module: d,
2142
+ module: p,
2087
2143
  entryPoint: "fs_main",
2088
2144
  targets: [{
2089
- format: u,
2090
- blend: {
2091
- color: {
2092
- srcFactor: "one",
2093
- dstFactor: "one-minus-src-alpha",
2094
- operation: "add"
2095
- },
2096
- alpha: {
2097
- srcFactor: "one",
2098
- dstFactor: "one-minus-src-alpha",
2099
- operation: "add"
2100
- }
2101
- }
2145
+ format: f,
2146
+ blend: n ? void 0 : Mt
2102
2147
  }]
2103
2148
  },
2104
2149
  primitive: { topology: "triangle-list" }
2105
- }), h = e.createBindGroup({
2150
+ }), _ = e.createBindGroup({
2106
2151
  label: "[Aurora] bind-group",
2107
- layout: p,
2152
+ layout: h,
2108
2153
  entries: [{
2109
2154
  binding: 0,
2110
- resource: { buffer: f }
2155
+ resource: { buffer: m }
2111
2156
  }]
2112
- }), g = kt();
2113
- function _(e) {}
2114
- function v(t) {
2115
- let o = +!i(), s = c === null ? 0 : Math.max(0, (t - c) * 1e3);
2116
- c = t, a.tick(o === 0 ? 0 : s);
2117
- let u = G(a, n(), o);
2118
- At(g, r(), u, t), e.queue.writeBuffer(f, 0, g.buffer);
2119
- let d = l.getCurrentTexture().createView(), p = e.createCommandEncoder({ label: "[Aurora] frame" }), _ = p.beginRenderPass({ colorAttachments: [{
2120
- view: d,
2157
+ }), v = Dt();
2158
+ function y(e) {}
2159
+ function b(t) {
2160
+ let c = +!a(), l = u === null ? 0 : Math.max(0, (t - u) * 1e3);
2161
+ u = t, s.tick(c === 0 ? 0 : l);
2162
+ let f = z(s, r(), c), p = i();
2163
+ Ot(v, p, f, o(t), n ? 1 : p.alpha), e.queue.writeBuffer(m, 0, v.buffer);
2164
+ let h = d.getCurrentTexture().createView(), y = e.createCommandEncoder({ label: "[Aurora] frame" }), b = y.beginRenderPass({ colorAttachments: [{
2165
+ view: h,
2121
2166
  clearValue: {
2122
2167
  r: 0,
2123
2168
  g: 0,
2124
2169
  b: 0,
2125
- a: 0
2170
+ a: +!!n
2126
2171
  },
2127
2172
  loadOp: "clear",
2128
2173
  storeOp: "store"
2129
2174
  }] });
2130
- _.setPipeline(m), _.setBindGroup(0, h), _.draw(3, 1, 0, 0), _.end(), e.queue.submit([p.finish()]);
2175
+ b.setPipeline(g), b.setBindGroup(0, _), b.draw(3, 1, 0, 0), b.end(), e.queue.submit([y.finish()]);
2131
2176
  }
2132
- function y() {
2133
- if (i()) return !1;
2134
- let e = r();
2135
- return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0 ? !0 : K(a);
2177
+ function x() {
2178
+ if (a()) return !1;
2179
+ let e = i();
2180
+ return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0 || B(s);
2136
2181
  }
2137
2182
  return {
2138
- frame: v,
2139
- shouldContinue: y,
2140
- resize: _,
2141
- time: (e) => i() ? 3.7 : e,
2183
+ frame: b,
2184
+ shouldContinue: x,
2185
+ resize: y,
2142
2186
  teardown: () => {
2143
- f.destroy();
2187
+ m.destroy();
2144
2188
  }
2145
2189
  };
2146
2190
  };
2147
2191
  }
2148
- function Lt(e, t, n, r) {
2149
- let { canvas: i, getCursorScalars: a, getConfig: o, getReducedMotion: s, pointerField: c, getDecodedImage: l, registerImageUploader: u } = r, d = null, f = e.createShaderModule({
2192
+ function Pt(e, t, n, r) {
2193
+ let { canvas: i, opaquePresentation: a, getCursorScalars: o, getConfig: s, getReducedMotion: c, getRenderTime: l, pointerField: u, getDecodedImage: d, registerImageUploader: f } = r, p = null, m = e.createShaderModule({
2150
2194
  label: "[Aurora] aurora-image.wgsl",
2151
- code: wt
2152
- }), p = e.createBuffer({
2195
+ code: St
2196
+ }), h = e.createBuffer({
2153
2197
  label: "[Aurora] image uniforms",
2154
- size: 288,
2155
- usage: Pt | Ft
2156
- }), m = e.createSampler({
2198
+ size: 352,
2199
+ usage: 72
2200
+ }), g = e.createSampler({
2157
2201
  label: "[Aurora] image sampler",
2158
2202
  magFilter: "linear",
2159
2203
  minFilter: "linear",
2160
2204
  addressModeU: "clamp-to-edge",
2161
2205
  addressModeV: "clamp-to-edge"
2162
- }), h = e.createBindGroupLayout({
2206
+ }), _ = e.createBindGroupLayout({
2163
2207
  label: "[Aurora] image bind-group-0",
2164
2208
  entries: [
2165
2209
  {
2166
2210
  binding: 0,
2167
- visibility: Y,
2211
+ visibility: J,
2168
2212
  buffer: { type: "uniform" }
2169
2213
  },
2170
2214
  {
2171
2215
  binding: 1,
2172
- visibility: Y,
2216
+ visibility: J,
2173
2217
  texture: {
2174
2218
  sampleType: "float",
2175
2219
  viewDimension: "2d"
@@ -2177,222 +2221,231 @@ function Lt(e, t, n, r) {
2177
2221
  },
2178
2222
  {
2179
2223
  binding: 2,
2180
- visibility: Y,
2224
+ visibility: J,
2181
2225
  sampler: { type: "filtering" }
2182
2226
  }
2183
2227
  ]
2184
- }), g = e.createRenderPipeline({
2228
+ }), v = e.createRenderPipeline({
2185
2229
  label: "[Aurora] image pipeline",
2186
- layout: e.createPipelineLayout({ bindGroupLayouts: [h] }),
2230
+ layout: e.createPipelineLayout({ bindGroupLayouts: [_] }),
2187
2231
  vertex: {
2188
- module: f,
2232
+ module: m,
2189
2233
  entryPoint: "vs_main"
2190
2234
  },
2191
2235
  fragment: {
2192
- module: f,
2236
+ module: m,
2193
2237
  entryPoint: "fs_main",
2194
2238
  targets: [{
2195
2239
  format: n,
2196
- blend: {
2197
- color: {
2198
- srcFactor: "one",
2199
- dstFactor: "one-minus-src-alpha",
2200
- operation: "add"
2201
- },
2202
- alpha: {
2203
- srcFactor: "one",
2204
- dstFactor: "one-minus-src-alpha",
2205
- operation: "add"
2206
- }
2207
- }
2240
+ blend: a ? void 0 : Mt
2208
2241
  }]
2209
2242
  },
2210
2243
  primitive: { topology: "triangle-list" }
2211
- }), _ = Ve(e, l() ?? Le()).texture;
2212
- function v() {
2244
+ }), y = P(e, d() ?? N()).texture;
2245
+ function b() {
2213
2246
  return e.createBindGroup({
2214
2247
  label: "[Aurora] image bind-group",
2215
- layout: h,
2248
+ layout: _,
2216
2249
  entries: [
2217
2250
  {
2218
2251
  binding: 0,
2219
- resource: { buffer: p }
2252
+ resource: { buffer: h }
2220
2253
  },
2221
2254
  {
2222
2255
  binding: 1,
2223
- resource: _.createView()
2256
+ resource: y.createView()
2224
2257
  },
2225
2258
  {
2226
2259
  binding: 2,
2227
- resource: m
2260
+ resource: g
2228
2261
  }
2229
2262
  ]
2230
2263
  });
2231
2264
  }
2232
- let y = v();
2233
- u((t) => {
2234
- _.destroy(), _ = Ve(e, t).texture, y = v();
2265
+ let x = b();
2266
+ f((t) => {
2267
+ y.destroy(), y = P(e, t).texture, x = b();
2235
2268
  });
2236
- let b = Mt();
2237
- function x(e) {}
2238
- function S(n) {
2239
- let r = +!s(), l = d === null ? 0 : Math.max(0, (n - d) * 1e3);
2240
- d = n, c.tick(r === 0 ? 0 : l);
2241
- let u = G(c, a(), r), f = i.height > 0 ? i.width / i.height : 1;
2242
- Nt(b, o(), u, n, f), e.queue.writeBuffer(p, 0, b.buffer);
2243
- let m = t.getCurrentTexture().createView(), h = e.createCommandEncoder({ label: "[Aurora] image frame" }), _ = h.beginRenderPass({ colorAttachments: [{
2244
- view: m,
2269
+ let S = At();
2270
+ function C(e) {}
2271
+ function w(n) {
2272
+ let r = +!c(), d = p === null ? 0 : Math.max(0, (n - p) * 1e3);
2273
+ p = n, u.tick(r === 0 ? 0 : d);
2274
+ let f = z(u, o(), r), m = i.height > 0 ? i.width / i.height : 1, g = s();
2275
+ jt(S, g, f, l(n), m, a ? 1 : g.alpha), e.queue.writeBuffer(h, 0, S.buffer);
2276
+ let _ = t.getCurrentTexture().createView(), y = e.createCommandEncoder({ label: "[Aurora] image frame" }), b = y.beginRenderPass({ colorAttachments: [{
2277
+ view: _,
2245
2278
  clearValue: {
2246
2279
  r: 0,
2247
2280
  g: 0,
2248
2281
  b: 0,
2249
- a: 0
2282
+ a: +!!a
2250
2283
  },
2251
2284
  loadOp: "clear",
2252
2285
  storeOp: "store"
2253
2286
  }] });
2254
- _.setPipeline(g), _.setBindGroup(0, y), _.draw(3, 1, 0, 0), _.end(), e.queue.submit([h.finish()]);
2287
+ b.setPipeline(v), b.setBindGroup(0, x), b.draw(3, 1, 0, 0), b.end(), e.queue.submit([y.finish()]);
2255
2288
  }
2256
- function C() {
2257
- if (s()) return !1;
2258
- let e = o();
2259
- return e.nucleiDrift !== 0 || e.warpDrift !== 0 || e.paletteDrift !== 0 ? !0 : K(c);
2289
+ function T() {
2290
+ if (c()) return !1;
2291
+ let e = s();
2292
+ return e.nucleiDrift !== 0 || e.warpDrift !== 0 || e.paletteDrift !== 0 || B(u);
2260
2293
  }
2261
2294
  return {
2262
- frame: S,
2263
- shouldContinue: C,
2264
- resize: x,
2265
- time: (e) => s() ? 3.7 : e,
2295
+ frame: w,
2296
+ shouldContinue: T,
2297
+ resize: C,
2266
2298
  teardown: () => {
2267
- u(null), _.destroy(), p.destroy();
2299
+ f(null), y.destroy(), h.destroy();
2268
2300
  }
2269
2301
  };
2270
2302
  }
2271
2303
  //#endregion
2272
2304
  //#region src/components/aurora/composables/runtime.ts
2273
- function Rt(e) {
2274
- return e.mode === "capture" ? !0 : e.initStrategy === "eager";
2305
+ function Ft(e) {
2306
+ return e.mode === "capture" || e.initStrategy === "eager";
2275
2307
  }
2276
- function zt(e) {
2308
+ function It(e) {
2277
2309
  return typeof e.preserveDrawingBuffer == "boolean" ? e.preserveDrawingBuffer : e.mode === "capture";
2278
2310
  }
2279
- function Bt(e, n, r = {}) {
2280
- let i = zt(r), a = !r.forceWebGLUnderSoftwareRaster && j(), o = n, s = t({
2311
+ function Lt(e = "live") {
2312
+ let t = e !== "capture";
2313
+ return {
2314
+ opaque: t,
2315
+ alphaMode: t ? "opaque" : "premultiplied",
2316
+ contextAlpha: !t
2317
+ };
2318
+ }
2319
+ var Rt = (e) => Math.min(1, Math.max(0, e));
2320
+ function Y(e) {
2321
+ return e.interactivity?.swirl === !0 || e.medium !== "smooth" && e.interactivity?.light === !0;
2322
+ }
2323
+ var zt = 3.7;
2324
+ function Bt(e, t, n, r) {
2325
+ return r ? zt : e.interactivity?.scroll === !0 ? t + Math.min(1, Math.max(0, n)) * e.breathPeriod : t;
2326
+ }
2327
+ function Vt(e, n, r = {}) {
2328
+ let i = It(r), a = Lt(r.mode), o = e.style.opacity, s = (t) => {
2329
+ a.opaque && (e.style.opacity = String(Rt(t)));
2330
+ };
2331
+ s(n.alpha);
2332
+ let c = !r.forceWebGLUnderSoftwareRaster && F(), l = n, u = 0, d = t({
2281
2333
  mass: 1,
2282
2334
  damping: .9,
2283
2335
  attractorResponse: .28,
2284
2336
  leadGain: .08,
2285
2337
  halfLifeMs: 110
2286
- }), c = .8, l = .25, u = () => ({
2287
- strength: c,
2288
- radius: l
2289
- }), d = null, m = He({
2290
- wake: () => h.wake(),
2338
+ }), f = .8, p = .25, m = () => {
2339
+ let e = l.interactivity, t = Y(l);
2340
+ return {
2341
+ strength: t ? f : 0,
2342
+ radius: p,
2343
+ amplitude: t ? Math.min(1, Math.max(0, e?.amplitude ?? .5)) : 0
2344
+ };
2345
+ }, h = null, g = ye({
2346
+ wake: () => b.wake(),
2291
2347
  onError: r.onInitError
2292
- }), h = a ? {
2293
- arm: () => {},
2348
+ }), _ = c ? /* @__PURE__ */ Error("[Aurora] GPU animation is unavailable under software rasterization") : null, v = !1;
2349
+ function y() {
2350
+ !_ || v || (v = !0, r.onInitError?.(_));
2351
+ }
2352
+ let b = c ? {
2353
+ backend: "webgl2",
2354
+ arm: y,
2355
+ armAsync: () => (y(), Promise.reject(_)),
2356
+ presize: () => {},
2294
2357
  suspend: () => {},
2295
2358
  resume: () => {},
2296
2359
  wake: () => {},
2297
2360
  renderAt: () => {},
2298
2361
  dispose: () => {},
2299
2362
  reducedMotion: !1
2300
- } : f(e, {
2363
+ } : T(e, {
2301
2364
  mode: r.mode === "capture" ? "capture" : "live",
2302
- dprPolicy: p,
2365
+ alphaMode: a.alphaMode,
2366
+ dprPolicy: x,
2303
2367
  revealBloom: r.revealBloom ?? !1,
2304
2368
  contextAttrs: {
2305
2369
  antialias: !1,
2306
- alpha: !0,
2307
- premultipliedAlpha: !0,
2370
+ alpha: a.contextAlpha,
2371
+ premultipliedAlpha: a.contextAlpha,
2308
2372
  preserveDrawingBuffer: i
2309
2373
  },
2310
- setupWGPU: It({
2374
+ setupWGPU: Nt({
2311
2375
  canvas: e,
2312
- getCursorScalars: u,
2313
- getConfig: () => o,
2314
- getReducedMotion: () => h.reducedMotion,
2315
- pointerField: s,
2316
- getDecodedImage: () => m.getDecodedImage(),
2317
- registerImageUploader: (e) => m.setImageUploader(e)
2376
+ opaquePresentation: a.opaque,
2377
+ getCursorScalars: m,
2378
+ getConfig: () => l,
2379
+ getReducedMotion: () => b.reducedMotion,
2380
+ getRenderTime: (e) => Bt(l, e, u, b.reducedMotion),
2381
+ pointerField: d,
2382
+ getDecodedImage: () => g.getDecodedImage(),
2383
+ registerImageUploader: (e) => g.setImageUploader(e)
2318
2384
  }),
2319
- setupGL: (t) => {
2320
- let n = o.source === "image", { program: r, vs: i, fs: a, uniforms: c, geometry: l } = Ge(t, xe, n ? Ne : Me), f = n ? Ue(t, r, m) : null;
2321
- function p(n) {
2322
- t.viewport(0, 0, n?.w ?? e.width, n?.h ?? e.height), t.useProgram(r);
2385
+ setupGL: bt({
2386
+ opaquePresentation: a.opaque,
2387
+ getConfig: () => l,
2388
+ getReducedMotion: () => b.reducedMotion,
2389
+ getRenderTime: (e) => Bt(l, e, u, b.reducedMotion),
2390
+ getCursorScalars: m,
2391
+ pointerField: d,
2392
+ imageCoordinator: g,
2393
+ registerConfigUploader: (e) => {
2394
+ h = e;
2323
2395
  }
2324
- let g = bt(t, r, c), _ = St({
2325
- gl: t,
2326
- prog: r,
2327
- uniforms: c,
2328
- pointerField: s,
2329
- getCursorScalars: u,
2330
- getConfig: () => o,
2331
- getReducedMotion: () => h.reducedMotion
2332
- });
2333
- return t.clearColor(0, 0, 0, 0), t.disable(t.DEPTH_TEST), t.enable(t.BLEND), t.blendFunc(t.ONE, t.ONE_MINUS_SRC_ALPHA), d = (e) => {
2334
- o = e, g(e);
2335
- }, d(o), {
2336
- frame: _.frame,
2337
- shouldContinue: _.needsAnimation,
2338
- resize: p,
2339
- time: (e) => h.reducedMotion ? 3.7 : e,
2340
- teardown: () => {
2341
- t.deleteProgram(r), t.deleteShader(i), t.deleteShader(a), t.deleteBuffer(l.buf), t.deleteVertexArray(l.vao), f?.();
2342
- }
2343
- };
2344
- },
2396
+ }),
2345
2397
  onInitError: r.onInitError ? (e) => r.onInitError?.(e instanceof Error ? e : Error(String(e))) : void 0,
2346
2398
  onStatus: r.onRendererStatus
2347
2399
  });
2348
- a && r.onRendererStatus?.(w());
2349
- function g(e, t, n = .8) {
2350
- c = n, s.setActive(!0), s.setPointer(e, t), h.wake();
2400
+ _ && r.onRendererStatus?.(ee("webgl2", "Software renderer", _));
2401
+ function S(e, t, n = .8) {
2402
+ Y(l) && (f = n, d.setActive(!0), d.setPointer(e, t), b.wake());
2351
2403
  }
2352
- function _() {
2353
- s.setActive(!1), h.wake();
2404
+ function C() {
2405
+ d.setActive(!1), b.wake();
2354
2406
  }
2355
- function v(e) {
2356
- l = e, h.wake();
2407
+ function w(e) {
2408
+ p = e, b.wake();
2357
2409
  }
2358
- function y(e) {
2359
- h.wake();
2410
+ function E(e) {
2411
+ let t = Math.min(1, Math.max(0, e));
2412
+ t !== u && (u = t, l.interactivity?.scroll === !0 && !b.reducedMotion && b.wake());
2360
2413
  }
2361
- function b() {
2362
- h.armAsync ? h.armAsync().catch((e) => {
2363
- r.onInitError?.(e instanceof Error ? e : Error(String(e)));
2364
- }) : h.arm();
2414
+ function D() {
2415
+ b.armAsync().catch(() => void 0);
2365
2416
  }
2366
- function x() {
2367
- return h.armAsync ? h.armAsync() : (h.arm(), Promise.resolve());
2417
+ function te() {
2418
+ return b.armAsync();
2368
2419
  }
2369
- return m.ensureDecoded(o), r.mode !== "capture" && Rt(r) && b(), {
2370
- arm: () => b(),
2371
- armAsync: () => x(),
2420
+ return g.ensureDecoded(l), r.mode !== "capture" && Ft(r) && D(), {
2421
+ arm: () => D(),
2422
+ armAsync: () => te(),
2372
2423
  update: (e) => {
2373
- o = e, d?.(e), m.ensureDecoded(e), h.wake();
2424
+ l = e, s(e.alpha), Y(e) || d.setActive(!1), h?.(e), g.ensureDecoded(e), b.wake();
2374
2425
  },
2375
- setCursor: g,
2376
- clearCursor: _,
2377
- setCursorRadius: v,
2378
- setReducedMotion: y,
2426
+ setCursor: S,
2427
+ clearCursor: C,
2428
+ setCursorRadius: w,
2429
+ setScrollProgress: E,
2379
2430
  get reducedMotion() {
2380
- return h.reducedMotion;
2431
+ return b.reducedMotion;
2381
2432
  },
2382
- pause: (e = "manual") => h.suspend(e),
2383
- resume: (e = "manual") => h.resume(e),
2384
- renderAt: (e) => h.renderAt(e),
2385
- dispose: () => h.dispose()
2433
+ pause: (e = "manual") => b.suspend(e),
2434
+ resume: (e = "manual") => b.resume(e),
2435
+ renderAt: (e) => b.renderAt(e),
2436
+ dispose: () => {
2437
+ b.dispose(), a.opaque && (e.style.opacity = o);
2438
+ }
2386
2439
  };
2387
2440
  }
2388
2441
  //#endregion
2389
2442
  //#region src/components/aurora/composables/configSource.ts
2390
- function Vt(e) {
2443
+ function Ht(e) {
2391
2444
  return typeof e == "function" ? e : e && typeof e == "object" && "value" in e ? () => e.value : () => e;
2392
2445
  }
2393
2446
  //#endregion
2394
2447
  //#region src/components/aurora/composables/useAurora.ts
2395
- function Ht(e) {
2448
+ function Ut(e) {
2396
2449
  if (typeof window > "u") return () => {};
2397
2450
  if (typeof window.requestIdleCallback == "function") {
2398
2451
  let t = window.requestIdleCallback(() => e(), { timeout: 2e3 });
@@ -2409,63 +2462,61 @@ function Ht(e) {
2409
2462
  r = !0, cancelAnimationFrame(t), n !== void 0 && clearTimeout(n);
2410
2463
  };
2411
2464
  }
2412
- function Ut(e, t, n = {}, r = {}) {
2413
- let o = Vt(t), s = r.renderMode === "css", c = null, l = null, u = null, d = null, f = null, p = null, m = null, h = !1, g = pe(!1), _ = me(s ? w() : ne("webgl2")), v = n.mode === "capture" || n.initStrategy === "eager";
2414
- function y() {
2415
- m && c && c.setReducedMotion(m.matches);
2416
- }
2417
- function b(e) {
2465
+ function Wt(e, t, n = {}, r = {}) {
2466
+ let o = Ht(t), s = r.renderMode === "css", c = null, l = null, u = null, d = null, f = null, p = null, m = !1, h = de(!1), g = fe(s ? D() : E("webgl2")), _ = i({
2467
+ target: e,
2468
+ trackExit: !0,
2469
+ enabled: k(() => !s && o().interactivity?.scroll === !0)
2470
+ });
2471
+ d = M(_, (e) => c?.setScrollProgress(e), { flush: "sync" });
2472
+ let v = n.mode === "capture" || n.initStrategy === "eager";
2473
+ function y(e) {
2418
2474
  let t = e instanceof Error ? e : Error(String(e));
2419
- _.value = re(_.value.engine, _.value.adapter, t), n.onInitError?.(t);
2475
+ g.value = ee(g.value.engine, g.value.adapter, t), n.onInitError?.(t);
2420
2476
  }
2421
- function x() {
2422
- if (!(h || !c)) {
2423
- h = !0;
2424
- try {
2425
- c.arm();
2426
- } catch (e) {
2427
- b(e);
2428
- return;
2429
- }
2430
- g.value = !0, c.update(o()), typeof window.matchMedia == "function" && (m = window.matchMedia("(prefers-reduced-motion: reduce)"), m.addEventListener("change", y)), l = k(o, (e) => c?.update(e), { deep: !0 }), o().interactivity?.scroll && e.value && (d = k(i({
2431
- target: e,
2432
- trackExit: !0
2433
- }), (e) => {
2434
- !c || c.reducedMotion || c.setCursorRadius(.2 + e * .25);
2435
- }));
2477
+ async function b() {
2478
+ if (m || !c) return;
2479
+ m = !0;
2480
+ let e = c;
2481
+ try {
2482
+ await e.armAsync();
2483
+ } catch (e) {
2484
+ g.value.phase !== "error" && y(e);
2485
+ return;
2436
2486
  }
2487
+ c !== e || g.value.engine === "css" || (h.value = !0, e.update(o()), e.setScrollProgress(_.value), l = M(o, (t) => e.update(t), { deep: !0 }));
2437
2488
  }
2438
- return D(() => {
2489
+ return ce(() => {
2439
2490
  let t = e.value;
2440
2491
  if (!t || s) return;
2441
2492
  try {
2442
- c = Bt(t, o(), {
2493
+ c = Vt(t, o(), {
2443
2494
  initStrategy: "deferred",
2444
2495
  ...n,
2445
2496
  onRendererStatus: (e) => {
2446
- _.value = e, n.onRendererStatus?.(e);
2497
+ g.value = e, h.value = e.phase === "ready", n.onRendererStatus?.(e);
2447
2498
  }
2448
2499
  });
2449
2500
  } catch (e) {
2450
- b(e);
2501
+ y(e);
2451
2502
  return;
2452
2503
  }
2453
2504
  if (v) {
2454
- x();
2505
+ b();
2455
2506
  return;
2456
2507
  }
2457
2508
  p = a(e, {
2458
- pause: () => c?.pause("off-screen"),
2459
- resume: () => c?.resume("off-screen")
2509
+ pause: () => c?.pause("off-screen-io"),
2510
+ resume: () => c?.resume("off-screen-io")
2460
2511
  }, { pauseWhenHidden: !1 });
2461
2512
  let r = p.isIntersecting;
2462
- u = k(r, (e) => {
2463
- !e || h || f || (f = Ht(() => {
2464
- f = null, r.value && x();
2513
+ u = M(r, (e) => {
2514
+ !e || m || f || (f = Ut(() => {
2515
+ f = null, r.value && b();
2465
2516
  }));
2466
2517
  }, { immediate: !0 });
2467
- }), E(() => {
2468
- f?.(), u?.(), l?.(), d?.(), p?.dispose(), m?.removeEventListener("change", y), c?.dispose(), c = null;
2518
+ }), se(() => {
2519
+ f?.(), u?.(), l?.(), d?.(), p?.dispose(), c?.dispose(), c = null;
2469
2520
  }), {
2470
2521
  setCursor: (e, t, n) => c?.setCursor(e, t, n),
2471
2522
  clearCursor: () => c?.clearCursor(),
@@ -2473,8 +2524,8 @@ function Ut(e, t, n = {}, r = {}) {
2473
2524
  renderAt: (e) => c?.renderAt(e),
2474
2525
  pause: () => c?.pause(),
2475
2526
  resume: () => c?.resume(),
2476
- isArmed: O(g),
2477
- rendererStatus: O(_)
2527
+ isArmed: ue(h),
2528
+ rendererStatus: ue(g)
2478
2529
  };
2479
2530
  }
2480
2531
  //#endregion
@@ -2483,7 +2534,7 @@ function X(e) {
2483
2534
  let t = Math.max(0, Math.min(1, e));
2484
2535
  return t <= .0031308 ? t * 12.92 : 1.055 * t ** (1 / 2.4) - .055;
2485
2536
  }
2486
- function Wt([e, t, n]) {
2537
+ function Gt([e, t, n]) {
2487
2538
  let r = .76, i = Math.min(e, Math.min(t, n)), a = i < .08 ? i - 6.25 * i * i : .04, o = e - a, s = t - a, c = n - a, l = Math.max(o, Math.max(s, c)), u = (e) => Math.max(0, Math.min(1, e));
2488
2539
  if (l < r) return [
2489
2540
  u(o),
@@ -2499,10 +2550,10 @@ function Wt([e, t, n]) {
2499
2550
  u(c + (f - c) * m)
2500
2551
  ];
2501
2552
  }
2502
- function Gt([e, t, n]) {
2553
+ function Kt([e, t, n]) {
2503
2554
  return .2126 * e + .7152 * t + .0722 * n;
2504
2555
  }
2505
- function Kt(e, t) {
2556
+ function qt(e, t) {
2506
2557
  let n = e.length;
2507
2558
  if (n === 0) return [
2508
2559
  0,
@@ -2517,7 +2568,7 @@ function Kt(e, t) {
2517
2568
  s[2] + (c[2] - s[2]) * o
2518
2569
  ];
2519
2570
  }
2520
- function qt(e, t, n, r) {
2571
+ function Jt(e, t, n, r) {
2521
2572
  let i = 0, a = 0, o = 0;
2522
2573
  for (let s of e) {
2523
2574
  let e = n - s.x, c = r - s.y, l = (s.angle ?? 0) * Math.PI / 180, u = Math.cos(l), d = Math.sin(l), f = u * e + d * c, p = -d * e + u * c, m = f / Math.max(s.elongation ?? 1, .01), h = p, g = m * m + h * h, _ = Math.max(s.radius, .01), v = Math.exp(-t * g / (_ * _));
@@ -2529,13 +2580,13 @@ function qt(e, t, n, r) {
2529
2580
  valueMod: a / s
2530
2581
  };
2531
2582
  }
2532
- function Jt(e, t, n) {
2533
- let r = e.palette.map((e) => u(e)), { paletteId: i, valueMod: a } = qt(e.nuclei, e.softmaxBeta, t, n), o = Kt(r, i), s = 1 + e.valueVariance * a, c = [
2583
+ function Z(e, t, n) {
2584
+ let r = e.palette.map((e) => d(e)), { paletteId: i, valueMod: a } = Jt(e.nuclei, e.softmaxBeta, t, n), o = qt(r, i), s = 1 + e.valueVariance * a, c = [
2534
2585
  o[0] * s,
2535
2586
  o[1] * s,
2536
2587
  o[2] * s
2537
2588
  ];
2538
- return c = Wt(c), c = [
2589
+ return c = Gt(c), c = [
2539
2590
  Math.max(0, Math.min(1, c[0] * .985 + .008)),
2540
2591
  Math.max(0, Math.min(1, c[1] * .985 + .008)),
2541
2592
  Math.max(0, Math.min(1, c[2] * .985 + .008))
@@ -2550,7 +2601,7 @@ function Yt([e, t, n]) {
2550
2601
  return `rgb(${r(e)}, ${r(t)}, ${r(n)})`;
2551
2602
  }
2552
2603
  function Xt(e) {
2553
- return Gt(e.map((e) => {
2604
+ return Kt(e.map((e) => {
2554
2605
  let t = Math.max(0, Math.min(1, e));
2555
2606
  return t <= .04045 ? t / 12.92 : ((t + .055) / 1.055) ** 2.4;
2556
2607
  }));
@@ -2568,7 +2619,7 @@ function Zt(e, t = {}) {
2568
2619
  0
2569
2620
  ];
2570
2621
  for (let t = 0; t < n; t++) for (let s = 0; s < n; s++) {
2571
- let c = (s + .5) / n, l = (t + .5) / n, u = Jt(e, c, l);
2622
+ let c = (s + .5) / n, l = (t + .5) / n, u = Z(e, c, l);
2572
2623
  r.push({
2573
2624
  x: c,
2574
2625
  y: l,
@@ -2638,10 +2689,10 @@ function Qt(e, t) {
2638
2689
  }
2639
2690
  //#endregion
2640
2691
  //#region src/components/aurora/Aurora.vue?vue&type=script&setup=true&lang.ts
2641
- var $t = ["data-aurora-substrate"], en = /* @__PURE__ */ e(/* @__PURE__ */ ce({
2692
+ var $t = ["data-aurora-substrate"], en = /*#__PURE__*/ e(/* @__PURE__ */ re({
2642
2693
  __name: "Aurora",
2643
2694
  props: {
2644
- config: { default: () => P },
2695
+ config: { default: () => V },
2645
2696
  runtimeOptions: {},
2646
2697
  onInitError: {},
2647
2698
  renderMode: { default: "auto" },
@@ -2649,15 +2700,15 @@ var $t = ["data-aurora-substrate"], en = /* @__PURE__ */ e(/* @__PURE__ */ ce({
2649
2700
  },
2650
2701
  emits: ["rendererStatus"],
2651
2702
  setup(e, { expose: t, emit: n }) {
2652
- let r = n, i = e, a = M(i.renderMode, { forceWebGLUnderSoftwareRaster: i.runtimeOptions?.forceWebGLUnderSoftwareRaster ?? !1 }), o = T(() => Math.max(0, Math.min(1, i.opacityCeiling))), s = pe(null), c = le(), l = c?.appContext.config.errorHandler, u = l ? (e) => l(e, c?.proxy ?? null, "Aurora initialization") : void 0, d = Ut(s, () => i.config, {
2703
+ let r = n, i = e, a = I(i.renderMode, { forceWebGLUnderSoftwareRaster: i.runtimeOptions?.forceWebGLUnderSoftwareRaster ?? !1 }), o = k(() => Math.max(0, Math.min(1, i.opacityCeiling))), s = de(null), c = ie(), l = c?.appContext.config.errorHandler, u = l ? (e) => l(e, c?.proxy ?? null, "Aurora initialization") : void 0, d = Wt(s, () => i.config, {
2653
2704
  ...i.runtimeOptions ?? {},
2654
2705
  onInitError: i.onInitError ?? i.runtimeOptions?.onInitError ?? u
2655
2706
  }, { renderMode: a });
2656
- k(d.rendererStatus, (e) => r("rendererStatus", e), {
2707
+ M(d.rendererStatus, (e) => r("rendererStatus", e), {
2657
2708
  immediate: !0,
2658
2709
  flush: "sync"
2659
2710
  });
2660
- let f = T(() => Zt(i.config)), p = T(() => f.value.backgroundImage), m = T(() => f.value.backgroundColor);
2711
+ let f = k(() => Zt(i.config)), p = k(() => f.value.backgroundImage), m = k(() => f.value.backgroundColor);
2661
2712
  return t({
2662
2713
  config: i.config,
2663
2714
  canvasRef: s,
@@ -2669,29 +2720,29 @@ var $t = ["data-aurora-substrate"], en = /* @__PURE__ */ e(/* @__PURE__ */ ce({
2669
2720
  resume: d.resume,
2670
2721
  isArmed: d.isArmed,
2671
2722
  rendererStatus: d.rendererStatus
2672
- }), (e, t) => (fe(), oe("div", {
2723
+ }), (e, t) => (le(), ne("div", {
2673
2724
  class: "aurora-root block h-full w-full overflow-hidden",
2674
- "data-aurora-substrate": he(a),
2675
- style: de({ "--aurora-opacity-ceiling": o.value })
2676
- }, [se("div", {
2677
- class: "aurora-placeholder h-full w-full",
2725
+ "data-aurora-substrate": j(a),
2726
+ style: oe({ opacity: o.value })
2727
+ }, [A("div", {
2728
+ class: "aurora-placeholder h-full min-h-0 w-full",
2678
2729
  "aria-hidden": "true",
2679
- style: de({
2730
+ style: oe({
2680
2731
  backgroundImage: p.value,
2681
2732
  backgroundColor: m.value
2682
2733
  })
2683
- }, null, 4), se("canvas", {
2734
+ }, null, 4), A("div", { class: ae(["aurora-canvas-layer h-full min-h-0 w-full", { "aurora-canvas-layer--armed": j(d).isArmed.value }]) }, [A("canvas", {
2684
2735
  ref_key: "canvasRef",
2685
2736
  ref: s,
2686
2737
  "aria-hidden": "true",
2687
- class: ue(["aurora-canvas block h-full w-full", { "aurora-canvas--armed": he(d).isArmed.value }])
2688
- }, null, 2)], 12, $t));
2738
+ class: ae(["aurora-canvas block h-full w-full", { "aurora-canvas--armed": j(d).isArmed.value }])
2739
+ }, null, 2)], 2)], 12, $t));
2689
2740
  }
2690
- }), [["__scopeId", "data-v-7453c302"]]);
2741
+ }), [["__scopeId", "data-v-aebed9bd"]]);
2691
2742
  //#endregion
2692
2743
  //#region src/components/aurora/composables/useCursorInteraction.ts
2693
2744
  function tn(e, t, n) {
2694
- let r = Vt(t), i = n.hitFraction ?? .35, a = null, o = null;
2745
+ let r = Ht(t), i = n.hitFraction ?? .35, a = null, o = null;
2695
2746
  function s(e, t) {
2696
2747
  let n = t.getBoundingClientRect();
2697
2748
  return {
@@ -2749,7 +2800,7 @@ function tn(e, t, n) {
2749
2800
  if (!n) return;
2750
2801
  let { x: i, y: c } = s(t, n), u = l(i, c);
2751
2802
  if (t.altKey && u === null) {
2752
- if (r().nuclei.length >= 6) return;
2803
+ if (r().nuclei.length >= 8) return;
2753
2804
  r().nuclei.push(f(i, c)), t.preventDefault();
2754
2805
  return;
2755
2806
  }
@@ -2778,20 +2829,20 @@ function tn(e, t, n) {
2778
2829
  }, a = n.getBoundingClientRect(), o = l((i.clientX - a.left) / Math.max(a.width, 1), (i.clientY - a.top) / Math.max(a.height, 1));
2779
2830
  o !== null && r().nuclei.length > 1 && (r().nuclei.splice(o, 1), t.preventDefault());
2780
2831
  }
2781
- return D(() => {
2832
+ return ce(() => {
2782
2833
  let t = e.value;
2783
2834
  t && (t.addEventListener("pointermove", p), t.addEventListener("pointerleave", m), t.addEventListener("pointerdown", h), t.addEventListener("pointerup", g), t.addEventListener("pointercancel", g), t.addEventListener("contextmenu", _));
2784
- }), E(() => {
2835
+ }), se(() => {
2785
2836
  let t = e.value;
2786
2837
  t && (t.removeEventListener("pointermove", p), t.removeEventListener("pointerleave", m), t.removeEventListener("pointerdown", h), t.removeEventListener("pointerup", g), t.removeEventListener("pointercancel", g), t.removeEventListener("contextmenu", _));
2787
2838
  }), { isDragging: () => a };
2788
2839
  }
2789
2840
  //#endregion
2790
2841
  //#region src/components/aurora/composables/atoms-fields.ts
2791
- function Z(e, t, n) {
2842
+ function Q(e, t, n) {
2792
2843
  return e + (t - e) * n;
2793
2844
  }
2794
- var Q = {
2845
+ var $ = {
2795
2846
  saturation: {
2796
2847
  calm: .85,
2797
2848
  vivid: 1.2
@@ -2831,9 +2882,69 @@ var Q = {
2831
2882
  warpDrift: .008,
2832
2883
  breathDepth: .05
2833
2884
  }
2834
- };
2835
- function rn(e, t, n) {
2836
- let r = S(n, 0, 1);
2885
+ }, rn = .61803398875;
2886
+ function an(e, t = "composed") {
2887
+ let n = Math.max(1, Math.min(8, Math.round(e))), r = [
2888
+ [.33, .33],
2889
+ [.67, .67],
2890
+ [.67, .33],
2891
+ [.33, .67],
2892
+ [.5, .33],
2893
+ [.5, .67],
2894
+ [.33, .5],
2895
+ [.67, .5]
2896
+ ], i = (e) => {
2897
+ if (t === "scattered") {
2898
+ let t = e * rn * Math.PI * 2, r = .12 + .3 * Math.sqrt((e + .5) / n);
2899
+ return [g(.5 + r * Math.cos(t), .1, .9), g(.5 + r * Math.sin(t), .1, .9)];
2900
+ }
2901
+ if (t === "centred") {
2902
+ if (n === 1) return [.5, .5];
2903
+ let t = e / n * Math.PI * 2;
2904
+ return [.5 + .12 * Math.cos(t), .5 + .12 * Math.sin(t)];
2905
+ }
2906
+ return r[e % r.length];
2907
+ };
2908
+ return Array.from({ length: n }, (e, t) => {
2909
+ let [r, a] = i(t);
2910
+ return {
2911
+ x: r,
2912
+ y: a,
2913
+ radius: .5,
2914
+ paletteBias: n === 1 ? 0 : t / (n - 1),
2915
+ valueBias: (t % 2 == 0 ? 1 : -1) * .05,
2916
+ driftRadius: .045,
2917
+ driftPhase: t * 2.4 % (Math.PI * 2)
2918
+ };
2919
+ });
2920
+ }
2921
+ var on = .03;
2922
+ function sn(e, t, n) {
2923
+ let r = e.length;
2924
+ if (r === 0 || t <= 0) return;
2925
+ let i = e.reduce((e, t) => e + t.C, 0) / r, a = Math.min(.22, i * 1.5), o = (e, t, n) => l({
2926
+ L: e,
2927
+ C: t,
2928
+ h: n
2929
+ }).C;
2930
+ if (e.forEach((e, n) => {
2931
+ let r = n % 2 == 0 ? a : on;
2932
+ e.C = o(e.L, g(Q(e.C, r, t), 0, .4), e.h);
2933
+ }), !n) return;
2934
+ let s = 0;
2935
+ for (let t = 1; t < r; t++) e[t].L < e[s].L && (s = t);
2936
+ let c = e[s];
2937
+ c.C = o(c.L, on, c.h);
2938
+ }
2939
+ function cn(e) {
2940
+ return e < .4 ? "fbm" : e < .75 ? "hybrid" : "cellular";
2941
+ }
2942
+ function ln(e, t) {
2943
+ let n = g(t, 0, 1);
2944
+ e.warpAmount = g(Q(.2, .6, n), 0, .6), e.warpScale = g(Q(1, 2.6, n), .5, 3), e.warpMode = cn(n), e.noiseOctaves = n < .5 ? 3 : n < .85 ? 4 : 5;
2945
+ }
2946
+ function un(e, t, n) {
2947
+ let r = g(n, 0, 1);
2837
2948
  switch (t) {
2838
2949
  case "watercolor":
2839
2950
  e.wetEdge = r, e.granulation = r * .8;
@@ -2856,138 +2967,92 @@ function rn(e, t, n) {
2856
2967
  break;
2857
2968
  }
2858
2969
  }
2859
- function an(e, t) {
2970
+ function dn(e, t) {
2860
2971
  switch (t) {
2861
- case "watercolor": return S(e.wetEdge, 0, 1);
2972
+ case "watercolor": return g(e.wetEdge, 0, 1);
2862
2973
  case "oil":
2863
2974
  case "vangogh":
2864
2975
  case "pastel":
2865
2976
  case "oil-pastel":
2866
2977
  case "crayon":
2867
- case "kuwahara": return S(e.strokeAmount, 0, 1);
2978
+ case "kuwahara": return g(e.strokeAmount, 0, 1);
2868
2979
  case "metal":
2869
- case "metal-gradient": return S((e.metalPolish ?? 1) / 4, 0, 1);
2980
+ case "metal-gradient": return g((e.metalPolish ?? 1) / 4, 0, 1);
2870
2981
  }
2871
2982
  }
2872
- function on(e) {
2983
+ function fn(e) {
2873
2984
  return e.nucleiDrift + e.paletteDrift + e.warpDrift > .001 ? "drifting" : e.breathDepth > .001 ? "breathing" : "still";
2874
2985
  }
2875
- function sn(e, t, n) {
2876
- return n === t ? 0 : S((e - t) / (n - t), 0, 1);
2986
+ function pn(e, t, n) {
2987
+ return n === t ? 0 : g((e - t) / (n - t), 0, 1);
2877
2988
  }
2878
2989
  //#endregion
2879
2990
  //#region src/components/aurora/composables/atoms.ts
2880
- var cn = {}, ln = .61803398875;
2881
- function un(e, t = "composed") {
2882
- let n = Math.max(1, Math.min(6, Math.round(e))), r = [
2883
- [.33, .33],
2884
- [.67, .67],
2885
- [.67, .33],
2886
- [.33, .67],
2887
- [.5, .5],
2888
- [.5, .33]
2889
- ], i = (e) => {
2890
- switch (t) {
2891
- case "scattered": {
2892
- let t = e * ln * Math.PI * 2, r = .12 + .3 * Math.sqrt((e + .5) / n);
2893
- return [S(.5 + r * Math.cos(t), .1, .9), S(.5 + r * Math.sin(t), .1, .9)];
2894
- }
2895
- case "centred": {
2896
- if (n === 1) return [.5, .5];
2897
- let t = e / n * Math.PI * 2, r = .12;
2898
- return [.5 + r * Math.cos(t), .5 + r * Math.sin(t)];
2899
- }
2900
- default: return r[e % r.length];
2901
- }
2902
- };
2903
- return Array.from({ length: n }, (e, t) => {
2904
- let [r, a] = i(t);
2905
- return {
2906
- x: r,
2907
- y: a,
2908
- radius: .5,
2909
- paletteBias: n === 1 ? 0 : t / (n - 1),
2910
- valueBias: (t % 2 == 0 ? 1 : -1) * .05,
2911
- driftRadius: .045,
2912
- driftPhase: t * 2.4 % (Math.PI * 2)
2913
- };
2914
- });
2915
- }
2916
- var $ = .03;
2917
- function dn(e, t, n) {
2918
- let r = e.length;
2919
- if (r === 0 || t <= 0) return;
2920
- let i = e.reduce((e, t) => e + t.C, 0) / r, a = Math.min(.22, i * 1.5), o = (e, t, n) => c({
2921
- L: e,
2922
- C: t,
2923
- h: n
2924
- }).C;
2925
- if (e.forEach((e, n) => {
2926
- let r = n % 2 == 0 ? a : $, i = S(Z(e.C, r, t), 0, .4);
2927
- e.C = o(e.L, i, e.h);
2928
- }), n) {
2929
- let t = 0;
2930
- for (let n = 1; n < r; n++) e[n].L < e[t].L && (t = n);
2931
- let n = e[t];
2932
- n.C = o(n.L, $, n.h);
2933
- }
2934
- }
2935
- function fn(e) {
2936
- return e < .4 ? "fbm" : e < .75 ? "hybrid" : "cellular";
2937
- }
2938
- function pn(e, t) {
2939
- let n = S(t, 0, 1);
2940
- e.warpAmount = S(Z(.2, .6, n), 0, .6), e.warpScale = S(Z(1, 2.6, n), .5, 3), e.warpMode = fn(n), e.noiseOctaves = n < .5 ? 3 : n < .85 ? 4 : 5;
2941
- }
2942
- function mn(e = cn, t = P) {
2991
+ var mn = {};
2992
+ function hn(e = mn, t = V) {
2943
2993
  let n = {
2944
2994
  ...t,
2945
2995
  palette: t.palette.map((e) => ({ ...e })),
2946
2996
  nuclei: t.nuclei.map((e) => ({ ...e })),
2947
2997
  flow: { ...t.flow }
2948
- }, r = e.colorEnergy === void 0 ? void 0 : S(e.colorEnergy, 0, 1);
2998
+ }, r = e.colorEnergy === void 0 ? void 0 : g(e.colorEnergy, 0, 1);
2949
2999
  if (e.seed !== void 0) {
2950
- let t = e.hueSpread === void 0 ? r === void 0 ? void 0 : Z(Q.hueSpread.calm, Q.hueSpread.vivid, r) : S(e.hueSpread, 0, 180);
2951
- n.palette = I(e.seed, {
3000
+ let t = e.hueSpread === void 0 ? r === void 0 ? void 0 : Q($.hueSpread.calm, $.hueSpread.vivid, r) : g(e.hueSpread, 0, 180);
3001
+ n.palette = Ye(e.seed, {
2952
3002
  harmony: e.harmony ?? "analogous",
2953
3003
  stopCount: 4,
2954
- temperatureShift: r === void 0 ? 0 : Z(Q.temperatureShift.calm, Q.temperatureShift.vivid, r),
3004
+ temperatureShift: r === void 0 ? 0 : Q($.temperatureShift.calm, $.temperatureShift.vivid, r),
2955
3005
  ...t === void 0 ? {} : { hueSpread: t },
2956
3006
  ...e.lightnessScheme === void 0 ? {} : { scheme: e.lightnessScheme },
2957
3007
  ...e.lBand === void 0 ? {} : { lBand: e.lBand }
2958
- }), e.chromaVariance !== void 0 && e.chromaVariance > 0 && dn(n.palette, S(e.chromaVariance, 0, 1), e.chromaCounterpoint ?? !1);
3008
+ }), e.chromaVariance !== void 0 && e.chromaVariance > 0 && sn(n.palette, g(e.chromaVariance, 0, 1), e.chromaCounterpoint ?? !1);
2959
3009
  }
2960
- if (r !== void 0 && (n.saturation = S(Z(Q.saturation.calm, Q.saturation.vivid, r), .6, 1.3), n.valueVariance = S(Z(Q.valueVariance.calm, Q.valueVariance.vivid, r), 0, .3), n.breathDepth = S(Z(Q.breathDepth.calm, Q.breathDepth.vivid, r), 0, .15)), e.zones !== void 0 && (n.nuclei = un(e.zones.count, e.zones.arrangement ?? "composed")), e.noise !== void 0 && pn(n, e.noise), e.medium !== void 0 && (n.medium = e.medium.kind, (e.medium.kind === "vangogh" || e.medium.kind === "oil-pastel") && (n.strokeOrient = "tensor"), e.medium.kind !== "smooth" && e.medium.amount !== void 0 && rn(n, e.medium.kind, e.medium.amount)), e.motion !== void 0) {
3010
+ if (r !== void 0 && (n.saturation = g(Q($.saturation.calm, $.saturation.vivid, r), .6, 1.3), n.valueVariance = g(Q($.valueVariance.calm, $.valueVariance.vivid, r), 0, .3), n.breathDepth = g(Q($.breathDepth.calm, $.breathDepth.vivid, r), 0, .15)), e.zones !== void 0 && (n.nuclei = an(e.zones.count, e.zones.arrangement ?? "composed")), e.noise !== void 0 && ln(n, e.noise), e.medium !== void 0 && (n.medium = e.medium.kind, (e.medium.kind === "vangogh" || e.medium.kind === "oil-pastel") && (n.strokeOrient = "tensor"), e.medium.kind !== "smooth" && e.medium.amount !== void 0 && un(n, e.medium.kind, e.medium.amount)), e.motion !== void 0) {
2961
3011
  let t = nn[e.motion];
2962
- n.nucleiDrift = S(t.nucleiDrift, 0, .05), n.paletteDrift = S(t.paletteDrift, 0, .04), n.warpDrift = S(t.warpDrift, 0, .015), n.breathDepth = S(e.motion === "still" ? 0 : Math.max(n.breathDepth, t.breathDepth), 0, .15);
3012
+ n.nucleiDrift = g(t.nucleiDrift, 0, .05), n.paletteDrift = g(t.paletteDrift, 0, .04), n.warpDrift = g(t.warpDrift, 0, .015), n.breathDepth = g(e.motion === "still" ? 0 : Math.max(n.breathDepth, t.breathDepth), 0, .15);
2963
3013
  }
2964
3014
  if (e.interactivity !== void 0) {
2965
- let t = e.interactivity;
3015
+ let t = e.interactivity, r = e.medium?.kind !== void 0 && e.medium.kind !== "smooth" ? e.interactivity.light : void 0;
2966
3016
  n.interactivity = {
2967
- ...t,
3017
+ ...r === void 0 ? {} : { light: r },
3018
+ ...t.scroll === void 0 ? {} : { scroll: t.scroll },
2968
3019
  swirl: t.swirl ?? !0,
2969
- amplitude: t.amplitude === void 0 ? .5 : S(t.amplitude, 0, 1)
3020
+ amplitude: t.amplitude === void 0 ? .5 : g(t.amplitude, 0, 1)
2970
3021
  };
2971
3022
  }
2972
3023
  return n;
2973
3024
  }
2974
- function hn(e) {
2975
- let t = e.medium, n = t === "smooth" ? { kind: t } : {
2976
- kind: t,
2977
- amount: an(e, t)
2978
- };
2979
- return {
2980
- seed: e.palette.length > 0 ? l(e.palette[0]) : "#3a7bd5",
3025
+ function gn(e) {
3026
+ let t = e.medium, n = e.interactivity, r = n && (n.scroll !== void 0 || n.swirl !== void 0 || n.amplitude !== void 0) ? {
3027
+ ...n.scroll === void 0 ? {} : { scroll: n.scroll },
3028
+ ...n.swirl === void 0 ? {} : { swirl: n.swirl },
3029
+ ...n.amplitude === void 0 ? {} : { amplitude: n.amplitude }
3030
+ } : void 0, i = {
3031
+ seed: e.palette.length > 0 ? u(e.palette[0]) : "#3a7bd5",
2981
3032
  harmony: "analogous",
2982
- colorEnergy: sn(e.saturation, Q.saturation.calm, Q.saturation.vivid),
3033
+ colorEnergy: pn(e.saturation, $.saturation.calm, $.saturation.vivid),
2983
3034
  zones: {
2984
- count: Math.max(1, Math.min(6, e.nuclei.length)),
3035
+ count: Math.max(1, Math.min(8, e.nuclei.length)),
2985
3036
  arrangement: "composed"
2986
3037
  },
2987
- noise: sn(e.warpAmount, .2, .6),
2988
- medium: n,
2989
- motion: on(e)
3038
+ noise: pn(e.warpAmount, .2, .6),
3039
+ motion: fn(e)
3040
+ };
3041
+ return t === "smooth" ? {
3042
+ ...i,
3043
+ medium: { kind: t },
3044
+ ...r ? { interactivity: r } : {}
3045
+ } : {
3046
+ ...i,
3047
+ medium: {
3048
+ kind: t,
3049
+ amount: dn(e, t)
3050
+ },
3051
+ ...r || n?.light !== void 0 ? { interactivity: {
3052
+ ...r,
3053
+ ...n?.light === void 0 ? {} : { light: n.light }
3054
+ } } : {}
2990
3055
  };
2991
3056
  }
2992
3057
  //#endregion
2993
- export { en as Aurora, cn as DEFAULT_ATOMS, P as DEFAULT_AURORA_CONFIG, Je as DEFAULT_VIVIDNESS, Ze as IMAGE_BLUR_MAX_DEFAULT, Xe as IMAGE_BLUR_MIN_DEFAULT, Ke as MAX_NUCLEI, qe as MAX_STOPS, Qe as PAPER_WASH_GROUND, Ye as VIVID_TARGET, Zt as auroraFallbackGround, hn as configToAtoms, Bt as createAurora, o as cssToOklch, I as deriveAurora, F as flattenPalette, et as hexToOklchStop, j as isSoftwareWebGLRenderer, qt as nucleiFieldStatic, un as nucleiPrior, l as oklchStopToHex, u as oklchToLinear, $e as paletteToCssGradient, Gt as relativeLuminance, mn as resolveAtoms, M as resolveRenderMode, Jt as sampleAuroraField, Ut as useAurora, tn as useCursorInteraction };
3058
+ export { en as Aurora, mn as DEFAULT_ATOMS, V as DEFAULT_AURORA_CONFIG, ze as DEFAULT_VIVIDNESS, He as IMAGE_BLUR_MAX_DEFAULT, Ve as IMAGE_BLUR_MIN_DEFAULT, Le as MAX_NUCLEI, Re as MAX_STOPS, Ue as PAPER_WASH_GROUND, Be as VIVID_TARGET, Zt as auroraFallbackGround, gn as configToAtoms, Vt as createAurora, s as cssToOklch, Ye as deriveAurora, H as flattenPalette, Ge as hexToOklchStop, F as isSoftwareWebGLRenderer, Jt as nucleiFieldStatic, an as nucleiPrior, u as oklchStopToHex, d as oklchToLinear, We as paletteToCssGradient, Kt as relativeLuminance, hn as resolveAtoms, I as resolveRenderMode, Z as sampleAuroraField, Wt as useAurora, tn as useCursorInteraction };