@mkbabb/glass-ui 6.0.0 → 7.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 (910) hide show
  1. package/README.md +36 -34
  2. package/dist/DropdownMenuTrigger-ClIq8RJE.js +93 -0
  3. package/dist/FeedbackMark-DZq5001h.js +23 -0
  4. package/dist/Input-9BlLluik.js +77 -0
  5. package/dist/{PagerDots-BoTQ7g5l.js → PagerDots-BEq0MgtH.js} +85 -81
  6. package/dist/Surface-kFQeYrYv.js +82 -0
  7. package/dist/accent-tone-solve-Cw7WkRD9.js +14 -0
  8. package/dist/animated-digit.js +5 -5
  9. package/dist/asElement-CRnm6A2v.js +9 -0
  10. package/dist/aurora.js +831 -776
  11. package/dist/axes.js +25 -14
  12. package/dist/badge-u65NClWn.js +62 -0
  13. package/dist/badge.js +1 -1
  14. package/dist/blob-config.js +1 -1
  15. package/dist/blob.js +442 -395
  16. package/dist/button-Bu9F4uU6.js +98 -0
  17. package/dist/button.js +2 -2
  18. package/dist/canvas.js +1 -1
  19. package/dist/card-Bk96VI2R.js +234 -0
  20. package/dist/card.js +2 -2
  21. package/dist/carousel.js +109 -169
  22. package/dist/chip-DFZQr6rV.js +129 -0
  23. package/dist/chip.js +2 -81
  24. package/dist/{class-names-5jotTFzp.js → class-names-Cpy5eaBk.js} +11 -4
  25. package/dist/collapsible-BAP95XTt.js +154 -0
  26. package/dist/collapsible.js +1 -1
  27. package/dist/color.js +46 -50
  28. package/dist/{procedural-color.wgsl-B9mLK7v0.js → color.wgsl-Ct5o9dpf.js} +155 -121
  29. package/dist/command-SFqDQ65h.js +320 -0
  30. package/dist/command.js +1 -1
  31. package/dist/completion-seal.js +54 -54
  32. package/dist/components/_shared/FeedbackMark.vue.d.ts +11 -0
  33. package/dist/components/_shared/axes.d.ts +9 -3
  34. package/dist/components/_shared/class-names.d.ts +9 -8
  35. package/dist/components/_shared/control-size.d.ts +2 -0
  36. package/dist/components/_shared/disclosure-context.d.ts +7 -0
  37. package/dist/components/_shared/disclosure.css +1 -0
  38. package/dist/components/_shared/feedback.d.ts +7 -0
  39. package/dist/components/_shared/field-control.css +1 -0
  40. package/dist/components/_shared/field-surfaces.css +1 -1
  41. package/dist/components/_shared/fieldControl.d.ts +13 -0
  42. package/dist/components/_shared/floating.d.ts +14 -0
  43. package/dist/components/_shared/index.d.ts +2 -6
  44. package/dist/components/_shared/interaction.d.ts +23 -0
  45. package/dist/components/_shared/menu.css +1 -1
  46. package/dist/components/_shared/menuRowClass.d.ts +2 -0
  47. package/dist/components/_shared/primitive.d.ts +19 -0
  48. package/dist/components/_shared/resolveSurfaceClass.d.ts +3 -0
  49. package/dist/components/_shared/selection.d.ts +7 -0
  50. package/dist/components/_shared/useMotionAxis.d.ts +3 -2
  51. package/dist/components/accordion/Accordion.vue.d.ts +41 -16
  52. package/dist/components/accordion/AccordionContent.vue.d.ts +11 -9
  53. package/dist/components/accordion/AccordionItem.vue.d.ts +12 -9
  54. package/dist/components/accordion/AccordionTrigger.vue.d.ts +8 -11
  55. package/dist/components/accordion/index.d.ts +4 -4
  56. package/dist/components/alert/Alert.vue.d.ts +9 -5
  57. package/dist/components/alert/index.d.ts +17 -8
  58. package/dist/components/animated-digit/AnimatedDigit.vue.d.ts +2 -3
  59. package/dist/components/aurora/Aurora.vue.d.ts +37 -42
  60. package/dist/components/aurora/composables/atoms-fields.d.ts +13 -2
  61. package/dist/components/aurora/composables/atoms.d.ts +52 -50
  62. package/dist/components/aurora/composables/auroraFallbackGround.d.ts +13 -13
  63. package/dist/components/aurora/composables/auroraImageSource.d.ts +4 -5
  64. package/dist/components/aurora/composables/color.d.ts +15 -20
  65. package/dist/components/aurora/composables/frameLoop.d.ts +14 -11
  66. package/dist/components/aurora/composables/glSetup.d.ts +19 -2
  67. package/dist/components/aurora/composables/runtime.d.ts +56 -60
  68. package/dist/components/aurora/composables/uniformBridge.d.ts +9 -14
  69. package/dist/components/aurora/composables/uniformBridgeWGPU.d.ts +21 -22
  70. package/dist/components/aurora/composables/uniformBridgeWGPUImage.d.ts +13 -13
  71. package/dist/components/aurora/composables/useAurora.d.ts +19 -30
  72. package/dist/components/aurora/composables/wgpuSetup.d.ts +10 -8
  73. package/dist/components/aurora/constants/budget.d.ts +12 -25
  74. package/dist/components/aurora/constants/presets.d.ts +47 -58
  75. package/dist/components/aurora/constants/renderMode.d.ts +21 -36
  76. package/dist/components/aurora/constants/shaders/aurora-image.wgsl.d.ts +1 -1
  77. package/dist/components/aurora/constants/shaders/aurora-mediums.wgsl.d.ts +1 -1
  78. package/dist/components/aurora/constants/shaders/aurora.wgsl.d.ts +1 -1
  79. package/dist/components/aurora/constants/shaders/brush.glsl.d.ts +1 -1
  80. package/dist/components/aurora/constants/shaders/composition.glsl.d.ts +1 -1
  81. package/dist/components/aurora/constants/shaders/flow.glsl.d.ts +1 -1
  82. package/dist/components/aurora/constants/shaders/metal-medium.glsl.d.ts +2 -2
  83. package/dist/components/aurora/constants/shaders/oil-modes.glsl.d.ts +1 -1
  84. package/dist/components/aurora/constants/shaders/vangogh-medium.glsl.d.ts +1 -1
  85. package/dist/components/aurora/index.d.ts +2 -1
  86. package/dist/components/avatar/Avatar.vue.d.ts +72 -11
  87. package/dist/components/avatar/AvatarFallback.vue.d.ts +1 -2
  88. package/dist/components/avatar/AvatarImage.vue.d.ts +12 -8
  89. package/dist/components/avatar/index.d.ts +3 -9
  90. package/dist/components/avatar/styles.css +1 -0
  91. package/dist/components/badge/Badge.vue.d.ts +2 -9
  92. package/dist/components/badge/index.d.ts +32 -9
  93. package/dist/components/blob/Blob.vue.d.ts +5 -12
  94. package/dist/components/blob/composables/blobSimulation.d.ts +80 -0
  95. package/dist/components/blob/composables/buildMetaballProgram.d.ts +1 -1
  96. package/dist/components/blob/composables/resolveBlobSurface.d.ts +6 -0
  97. package/dist/components/blob/composables/satelliteKinematics.d.ts +3 -3
  98. package/dist/components/blob/composables/uniformBridgeWGPU.d.ts +1 -1
  99. package/dist/components/blob/composables/uploadBlobUniforms.d.ts +4 -31
  100. package/dist/components/blob/composables/useBlobMood.d.ts +5 -5
  101. package/dist/components/blob/composables/useBlobPointer.d.ts +11 -12
  102. package/dist/components/blob/composables/useMetaballRenderer.d.ts +27 -24
  103. package/dist/components/blob/composables/wgpuSetup.d.ts +7 -5
  104. package/dist/components/blob/config.d.ts +1 -1
  105. package/dist/components/blob/index.d.ts +2 -2
  106. package/dist/components/blob/presets.d.ts +2 -2
  107. package/dist/components/blob/shaders/metaball-palette.wgsl.d.ts +1 -1
  108. package/dist/components/blob/shaders/metaball-uniforms.glsl.d.ts +1 -1
  109. package/dist/components/blob/shaders/metaball.wgsl.d.ts +1 -1
  110. package/dist/components/blob/shaders/sdf-body.glsl.d.ts +1 -1
  111. package/dist/components/blob/shaders/watercolor-edges.glsl.d.ts +1 -1
  112. package/dist/components/blob/types.d.ts +28 -60
  113. package/dist/components/button/Button.vue.d.ts +26 -20
  114. package/dist/components/button/index.d.ts +1 -9
  115. package/dist/components/button/styles.css +1 -0
  116. package/dist/components/card/Card.vue.d.ts +20 -155
  117. package/dist/components/card/CardAction.vue.d.ts +0 -7
  118. package/dist/components/card/CardContent.vue.d.ts +2 -2
  119. package/dist/components/card/CardDescription.vue.d.ts +2 -2
  120. package/dist/components/card/CardFooter.vue.d.ts +2 -2
  121. package/dist/components/card/CardHeader.vue.d.ts +3 -42
  122. package/dist/components/card/CardTitle.vue.d.ts +6 -3
  123. package/dist/components/card/card-scroll.css +1 -1
  124. package/dist/components/card/index.d.ts +1 -3
  125. package/dist/components/card/styles.css +1 -1
  126. package/dist/components/carousel/index.d.ts +0 -2
  127. package/dist/components/carousel/interface.d.ts +2 -0
  128. package/dist/components/checkbox/Checkbox.vue.d.ts +17 -9
  129. package/dist/components/checkbox/index.d.ts +1 -1
  130. package/dist/components/checkbox/styles.css +1 -0
  131. package/dist/components/chip/Chip.vue.d.ts +12 -7
  132. package/dist/components/chip/chipVariants.d.ts +20 -43
  133. package/dist/components/chip/index.d.ts +1 -1
  134. package/dist/components/chip/types.d.ts +45 -30
  135. package/dist/components/collapsible/Collapsible.vue.d.ts +22 -9
  136. package/dist/components/collapsible/CollapsibleContent.vue.d.ts +11 -5
  137. package/dist/components/collapsible/CollapsibleTrigger.vue.d.ts +12 -9
  138. package/dist/components/collapsible/index.d.ts +3 -3
  139. package/dist/components/combobox/Combobox.vue.d.ts +10 -20
  140. package/dist/components/combobox/ComboboxAnchor.vue.d.ts +2 -6
  141. package/dist/components/combobox/ComboboxEmpty.vue.d.ts +2 -6
  142. package/dist/components/combobox/ComboboxGroup.vue.d.ts +2 -7
  143. package/dist/components/combobox/ComboboxInput.vue.d.ts +7 -12
  144. package/dist/components/combobox/ComboboxItem.vue.d.ts +7 -11
  145. package/dist/components/combobox/ComboboxItemIndicator.vue.d.ts +2 -6
  146. package/dist/components/combobox/ComboboxList.vue.d.ts +14 -17
  147. package/dist/components/combobox/ComboboxTrigger.vue.d.ts +16 -0
  148. package/dist/components/combobox/index.d.ts +2 -1
  149. package/dist/components/combobox/types.d.ts +87 -0
  150. package/dist/components/command/Command.vue.d.ts +9 -23
  151. package/dist/components/command/CommandDialog.vue.d.ts +4 -9
  152. package/dist/components/command/CommandEmpty.vue.d.ts +2 -6
  153. package/dist/components/command/CommandGroup.vue.d.ts +2 -7
  154. package/dist/components/command/CommandInput.vue.d.ts +6 -7
  155. package/dist/components/command/CommandItem.vue.d.ts +7 -11
  156. package/dist/components/command/CommandList.vue.d.ts +12 -19
  157. package/dist/components/command/CommandSeparator.vue.d.ts +2 -6
  158. package/dist/components/command/CommandShortcut.vue.d.ts +2 -2
  159. package/dist/components/command/dialogContext.d.ts +6 -0
  160. package/dist/components/command/index.d.ts +10 -10
  161. package/dist/components/command/styles.css +1 -0
  162. package/dist/components/command/types.d.ts +27 -0
  163. package/dist/components/completion-seal/CompletionSeal.vue.d.ts +2 -2
  164. package/dist/components/completion-seal/constants.d.ts +2 -2
  165. package/dist/components/configurator/Configurator.vue.d.ts +6 -6
  166. package/dist/components/configurator/ConfiguratorLayer.vue.d.ts +14 -18
  167. package/dist/components/configurator/ConfiguratorRow.vue.d.ts +14 -17
  168. package/dist/components/configurator/size.d.ts +5 -7
  169. package/dist/components/configurator/styles.css +1 -1
  170. package/dist/components/constellation/Constellation.vue.d.ts +1 -1
  171. package/dist/components/constellation/composables/createConstellationField.d.ts +2 -2
  172. package/dist/components/constellation/constants.d.ts +8 -8
  173. package/dist/components/constellation/constellationField.d.ts +5 -6
  174. package/dist/components/constellation/constellationInteraction.d.ts +8 -8
  175. package/dist/components/constellation/constellationTypes.d.ts +53 -54
  176. package/dist/components/constellation/constellationWell.d.ts +2 -2
  177. package/dist/components/dark-mode-toggle/DarkModeToggle.vue.d.ts +12 -0
  178. package/dist/components/dark-mode-toggle/dark-mode-toggle.css +1 -0
  179. package/dist/components/dark-mode-toggle/index.d.ts +2 -0
  180. package/dist/components/data-table/DataTable.vue.d.ts +11 -59
  181. package/dist/components/data-table/composables/useDataTableRowIdentity.d.ts +5 -23
  182. package/dist/components/data-table/index.d.ts +1 -1
  183. package/dist/components/data-table/styles.css +1 -0
  184. package/dist/components/data-table/types.d.ts +10 -12
  185. package/dist/components/deck/index.d.ts +0 -1
  186. package/dist/components/dialog/Dialog.vue.d.ts +8 -3
  187. package/dist/components/dialog/DialogClose.vue.d.ts +3 -1
  188. package/dist/components/dialog/DialogContent.vue.d.ts +27 -90
  189. package/dist/components/dialog/DialogDescription.vue.d.ts +5 -6
  190. package/dist/components/dialog/DialogTitle.vue.d.ts +5 -6
  191. package/dist/components/dialog/DialogTrigger.vue.d.ts +3 -1
  192. package/dist/components/{_shared → dialog}/ModalOverlay.vue.d.ts +18 -16
  193. package/dist/components/dialog/dialogStageContext.d.ts +7 -0
  194. package/dist/components/dialog/index.d.ts +8 -8
  195. package/dist/components/dialog/placement.css +1 -1
  196. package/dist/components/dialog/sheet-motion.d.ts +17 -0
  197. package/dist/components/dock/DockControl.vue.d.ts +9 -12
  198. package/dist/components/dock/DockCrossfade.vue.d.ts +4 -8
  199. package/dist/components/dock/DockLayer.vue.d.ts +2 -3
  200. package/dist/components/dock/DockLayerGroup.vue.d.ts +6 -6
  201. package/dist/components/dock/DockSeparator.vue.d.ts +8 -8
  202. package/dist/components/dock/composables/dockContext.d.ts +4 -14
  203. package/dist/components/dock/composables/dockCrossfadeContext.d.ts +6 -7
  204. package/dist/components/dock/composables/dockMorphMeasure.d.ts +7 -86
  205. package/dist/components/dock/composables/index.d.ts +1 -1
  206. package/dist/components/dock/composables/useDockClickIntegrity.d.ts +1 -1
  207. package/dist/components/dock/composables/useDockHold.d.ts +4 -5
  208. package/dist/components/dock/composables/useDockMorph.d.ts +26 -0
  209. package/dist/components/dock/composables/useDockOverflowFit.d.ts +4 -6
  210. package/dist/components/dock/composables/useDockSearch.d.ts +8 -8
  211. package/dist/components/dock/composables/useDockShellProps.d.ts +46 -43
  212. package/dist/components/dock/composables/useDockSpring.d.ts +7 -11
  213. package/dist/components/dock/composables/useDockState.d.ts +12 -3
  214. package/dist/components/dock/composables/useDockTouchGate.d.ts +14 -23
  215. package/dist/components/dock/constants.d.ts +5 -78
  216. package/dist/components/dock/index.d.ts +2 -7
  217. package/dist/components/dock/styles/controls.css +1 -1
  218. package/dist/components/dock/styles/crossfade.css +1 -1
  219. package/dist/components/dock/styles/density.css +1 -1
  220. package/dist/components/dock/styles/index.css +1 -1
  221. package/dist/components/dock/styles/layer-group.css +1 -1
  222. package/dist/components/dock/styles/layers.css +1 -1
  223. package/dist/components/dock/styles/morph.css +1 -1
  224. package/dist/components/dock/styles/shape.css +1 -1
  225. package/dist/components/dock/styles/shell.css +1 -1
  226. package/dist/components/drawer/Drawer.vue.d.ts +17 -20
  227. package/dist/components/drawer/DrawerContent.vue.d.ts +15 -16
  228. package/dist/components/drawer/DrawerDescription.vue.d.ts +5 -6
  229. package/dist/components/drawer/DrawerOverlay.vue.d.ts +6 -6
  230. package/dist/components/drawer/DrawerTitle.vue.d.ts +5 -6
  231. package/dist/components/drawer/composables/drawerSnapContext.d.ts +7 -1
  232. package/dist/components/drawer/composables/useDrawerSnap.d.ts +3 -1
  233. package/dist/components/drawer/constants.d.ts +7 -25
  234. package/dist/components/drawer/index.d.ts +10 -10
  235. package/dist/components/drawer/styles.css +1 -1
  236. package/dist/components/dropdown-menu/DropdownMenu.vue.d.ts +43 -16
  237. package/dist/components/dropdown-menu/DropdownMenuCheckboxItem.vue.d.ts +22 -13
  238. package/dist/components/dropdown-menu/DropdownMenuContent.vue.d.ts +28 -19
  239. package/dist/components/dropdown-menu/DropdownMenuGroup.vue.d.ts +6 -4
  240. package/dist/components/dropdown-menu/DropdownMenuItem.vue.d.ts +21 -9
  241. package/dist/components/dropdown-menu/DropdownMenuLabel.vue.d.ts +9 -9
  242. package/dist/components/dropdown-menu/DropdownMenuRadioGroup.vue.d.ts +13 -6
  243. package/dist/components/dropdown-menu/DropdownMenuRadioItem.vue.d.ts +18 -11
  244. package/dist/components/dropdown-menu/DropdownMenuSeparator.vue.d.ts +5 -6
  245. package/dist/components/dropdown-menu/DropdownMenuShortcut.vue.d.ts +7 -8
  246. package/dist/components/dropdown-menu/DropdownMenuSub.vue.d.ts +15 -7
  247. package/dist/components/dropdown-menu/DropdownMenuSubContent.vue.d.ts +24 -17
  248. package/dist/components/dropdown-menu/DropdownMenuSubTrigger.vue.d.ts +11 -9
  249. package/dist/components/dropdown-menu/DropdownMenuTrigger.vue.d.ts +15 -8
  250. package/dist/components/dropdown-menu/index.d.ts +15 -15
  251. package/dist/components/dropdown-menu/styles.css +1 -0
  252. package/dist/components/dropdown-menu/useMenuTrigger.d.ts +2 -9
  253. package/dist/components/easing/composables/useEasingPicker.d.ts +5 -8
  254. package/dist/components/easing/constants.d.ts +1 -1
  255. package/dist/components/expandable-container/ExpandableContainer.vue.d.ts +1 -25
  256. package/dist/components/expandable-container/styles.css +1 -0
  257. package/dist/components/fading-scroll/FadingScroll.vue.d.ts +4 -0
  258. package/dist/components/fading-scroll/composables/useFadingScroll.d.ts +4 -0
  259. package/dist/components/fading-scroll/constants.d.ts +1 -2
  260. package/dist/components/fourier-field/FourierField.vue.d.ts +3 -5
  261. package/dist/components/fourier-field/composables/fourierFieldGLSetup.d.ts +1 -1
  262. package/dist/components/fourier-field/composables/fourierFieldWGPUSetup.d.ts +1 -1
  263. package/dist/components/fourier-field/composables/useFourierField.d.ts +4 -4
  264. package/dist/components/fourier-field/constants.d.ts +10 -10
  265. package/dist/components/fourier-field/index.d.ts +3 -3
  266. package/dist/components/fourier-field/presets.d.ts +3 -3
  267. package/dist/components/fourier-field/shaders/fourier-field.glsl.d.ts +2 -2
  268. package/dist/components/fourier-field/shaders/fourier-field.render.wgsl.d.ts +1 -1
  269. package/dist/components/handmark/HandMark.vue.d.ts +1 -1
  270. package/dist/components/handmark/brush.d.ts +1 -1
  271. package/dist/components/handmark/composables/useHandMark.d.ts +4 -4
  272. package/dist/components/handmark/constants.d.ts +10 -10
  273. package/dist/components/handmark/geometry.d.ts +8 -8
  274. package/dist/components/handmark/index.d.ts +7 -8
  275. package/dist/components/handmark/noise.d.ts +3 -3
  276. package/dist/components/handmark/types.d.ts +2 -2
  277. package/dist/components/header-ribbon/HeaderRibbon.vue.d.ts +3 -21
  278. package/dist/components/header-ribbon/styles.css +1 -0
  279. package/dist/components/header-ribbon/types.d.ts +5 -2
  280. package/dist/components/index.d.ts +0 -3
  281. package/dist/components/infinite-scroll/composables/types.d.ts +1 -1
  282. package/dist/components/input/Input.vue.d.ts +10 -35
  283. package/dist/components/input/index.d.ts +2 -1
  284. package/dist/components/input/types.d.ts +22 -0
  285. package/dist/components/instrument-chassis/InstrumentChassis.vue.d.ts +10 -84
  286. package/dist/components/instrument-chassis/index.d.ts +1 -2
  287. package/dist/components/instrument-chassis/styles.css +1 -1
  288. package/dist/components/instrument-chassis/types.d.ts +13 -0
  289. package/dist/components/label/Label.vue.d.ts +9 -14
  290. package/dist/components/label/index.d.ts +1 -1
  291. package/dist/components/labeled-field/LabeledField.vue.d.ts +10 -108
  292. package/dist/components/labeled-field/LabeledInput.vue.d.ts +3 -23
  293. package/dist/components/labeled-field/LabeledSelect.vue.d.ts +12 -18
  294. package/dist/components/labeled-field/LabeledSlider.vue.d.ts +12 -18
  295. package/dist/components/labeled-field/LabeledSwitch.vue.d.ts +14 -17
  296. package/dist/components/labeled-field/index.d.ts +1 -0
  297. package/dist/components/labeled-field/types.d.ts +50 -0
  298. package/dist/components/liquid-grid/composables/liquidGrid.d.ts +5 -5
  299. package/dist/components/liquid-grid/constants.d.ts +6 -7
  300. package/dist/components/liquid-grid/shaders/liquid-grid.glsl.d.ts +1 -1
  301. package/dist/components/liquid-grid/shaders/liquid-grid.wgsl.d.ts +1 -1
  302. package/dist/components/metric/Metric.vue.d.ts +24 -0
  303. package/dist/components/metric/MetricCell.vue.d.ts +26 -0
  304. package/dist/components/metric/MetricRow.vue.d.ts +22 -0
  305. package/dist/components/metric/MetricStack.vue.d.ts +16 -0
  306. package/dist/components/metric/coalesce-metric.d.ts +7 -28
  307. package/dist/components/metric/index.d.ts +5 -0
  308. package/dist/components/metric/styles.css +1 -0
  309. package/dist/components/metric/types.d.ts +32 -0
  310. package/dist/components/number-field/NumberField.vue.d.ts +21 -9
  311. package/dist/components/number-field/NumberFieldContent.vue.d.ts +2 -2
  312. package/dist/components/number-field/NumberFieldDecrement.vue.d.ts +6 -6
  313. package/dist/components/number-field/NumberFieldIncrement.vue.d.ts +6 -6
  314. package/dist/components/number-field/NumberFieldInput.vue.d.ts +1 -6
  315. package/dist/components/number-field/context.d.ts +6 -0
  316. package/dist/components/number-field/index.d.ts +5 -5
  317. package/dist/components/number-field/styles.css +1 -0
  318. package/dist/components/pager-dots/constants.d.ts +1 -2
  319. package/dist/components/pager-dots/index.d.ts +0 -2
  320. package/dist/components/paper-backdrop/PaperBackdrop.vue.d.ts +1 -14
  321. package/dist/components/paper-backdrop/index.d.ts +1 -1
  322. package/dist/components/popover/Popover.vue.d.ts +24 -36
  323. package/dist/components/popover/PopoverContent.vue.d.ts +23 -29
  324. package/dist/components/popover/PopoverTrigger.vue.d.ts +12 -7
  325. package/dist/components/popover/index.d.ts +3 -3
  326. package/dist/components/popover/popoverContext.d.ts +3 -4
  327. package/dist/components/progress/Progress.vue.d.ts +5 -13
  328. package/dist/components/progress/index.d.ts +1 -0
  329. package/dist/components/progress/types.d.ts +19 -0
  330. package/dist/components/pulse/Pulse.vue.d.ts +8 -47
  331. package/dist/components/pulse/index.d.ts +1 -0
  332. package/dist/components/radio-group/RadioGroup.vue.d.ts +27 -15
  333. package/dist/components/radio-group/RadioGroupItem.vue.d.ts +9 -6
  334. package/dist/components/radio-group/index.d.ts +2 -0
  335. package/dist/components/radio-group/styles.css +1 -0
  336. package/dist/components/scroll-progress-rim/styles.css +1 -1
  337. package/dist/components/search/FuzzySearch.vue.d.ts +2 -2
  338. package/dist/components/search/SearchBar.vue.d.ts +2 -2
  339. package/dist/components/search/searchVariants.d.ts +15 -17
  340. package/dist/components/select/Select.vue.d.ts +25 -14
  341. package/dist/components/select/SelectContent.vue.d.ts +26 -29
  342. package/dist/components/select/SelectGroup.vue.d.ts +7 -11
  343. package/dist/components/select/SelectItem.vue.d.ts +24 -13
  344. package/dist/components/select/SelectLabel.vue.d.ts +7 -11
  345. package/dist/components/select/SelectScrollDownButton.vue.d.ts +3 -4
  346. package/dist/components/select/SelectScrollUpButton.vue.d.ts +3 -4
  347. package/dist/components/select/SelectSeparator.vue.d.ts +5 -6
  348. package/dist/components/select/SelectTrigger.vue.d.ts +15 -41
  349. package/dist/components/select/SelectValue.vue.d.ts +13 -7
  350. package/dist/components/select/index.d.ts +16 -8
  351. package/dist/components/separator/Separator.vue.d.ts +11 -6
  352. package/dist/components/separator/index.d.ts +1 -1
  353. package/dist/components/skeleton/Skeleton.vue.d.ts +3 -31
  354. package/dist/components/slider/Slider.vue.d.ts +10 -37
  355. package/dist/components/slider/index.d.ts +1 -45
  356. package/dist/components/slider/types.d.ts +25 -0
  357. package/dist/components/sortable-list/SortableList.vue.d.ts +1 -1
  358. package/dist/components/sortable-list/composables/ghostRenderer.d.ts +1 -1
  359. package/dist/components/sortable-list/composables/touchGate.d.ts +1 -1
  360. package/dist/components/sortable-list/composables/transitionTiming.d.ts +1 -1
  361. package/dist/components/status-dot/StatusDot.vue.d.ts +7 -10
  362. package/dist/components/status-dot/index.d.ts +1 -0
  363. package/dist/components/surface/Surface.vue.d.ts +13 -21
  364. package/dist/components/surface/index.d.ts +2 -2
  365. package/dist/components/switch/Switch.vue.d.ts +20 -11
  366. package/dist/components/switch/index.d.ts +2 -1
  367. package/dist/components/switch/styles.css +1 -0
  368. package/dist/components/table/Table.vue.d.ts +1 -1
  369. package/dist/components/tabs/SegmentedTabs.vue.d.ts +21 -13
  370. package/dist/components/tabs/composables/useTabDragMorph.d.ts +6 -6
  371. package/dist/components/tabs/composables/useTabResponsive.d.ts +5 -6
  372. package/dist/components/tabs/composables/useTabRovingFocus.d.ts +16 -16
  373. package/dist/components/tabs/constants.d.ts +1 -1
  374. package/dist/components/tabs/index.d.ts +1 -1
  375. package/dist/components/tabs/styles/drag.css +1 -1
  376. package/dist/components/tabs/styles/segmented.css +1 -1
  377. package/dist/components/tags-input/TagsInput.vue.d.ts +28 -15
  378. package/dist/components/tags-input/TagsInputInput.vue.d.ts +8 -6
  379. package/dist/components/tags-input/TagsInputItem.vue.d.ts +9 -8
  380. package/dist/components/tags-input/TagsInputItemDelete.vue.d.ts +5 -6
  381. package/dist/components/tags-input/TagsInputItemText.vue.d.ts +5 -6
  382. package/dist/components/tags-input/context.d.ts +5 -0
  383. package/dist/components/tags-input/index.d.ts +5 -5
  384. package/dist/components/tags-input/styles.css +1 -0
  385. package/dist/components/textarea/Textarea.vue.d.ts +10 -55
  386. package/dist/components/textarea/index.d.ts +2 -1
  387. package/dist/components/textarea/types.d.ts +22 -0
  388. package/dist/components/timeline/ContinuousMarkers.vue.d.ts +2 -4
  389. package/dist/components/timeline/ContinuousRail.vue.d.ts +3 -6
  390. package/dist/components/timeline/ContinuousTimeline.vue.d.ts +12 -13
  391. package/dist/components/timeline/GlassTimeline.vue.d.ts +7 -8
  392. package/dist/components/timeline/ScrubberTimeline.vue.d.ts +4 -4
  393. package/dist/components/timeline/SegmentedTimeline.vue.d.ts +2 -3
  394. package/dist/components/timeline/geometry.d.ts +3 -4
  395. package/dist/components/timeline/types.d.ts +3 -3
  396. package/dist/components/toast/Toast.vue.d.ts +37 -79
  397. package/dist/components/toast/ToastAction.vue.d.ts +5 -5
  398. package/dist/components/toast/ToastClose.vue.d.ts +4 -5
  399. package/dist/components/toast/ToastDescription.vue.d.ts +4 -5
  400. package/dist/components/toast/ToastTitle.vue.d.ts +4 -5
  401. package/dist/components/toast/Toaster.vue.d.ts +1 -1
  402. package/dist/components/toast/index.d.ts +8 -9
  403. package/dist/components/toast/use-toast.d.ts +20 -163
  404. package/dist/components/toggle-group/ToggleGroup.vue.d.ts +44 -22
  405. package/dist/components/toggle-group/ToggleGroupItem.vue.d.ts +11 -9
  406. package/dist/components/toggle-group/index.d.ts +3 -2
  407. package/dist/components/toggle-group/styles.css +1 -0
  408. package/dist/components/toggle-group/toggleGroupContext.d.ts +4 -15
  409. package/dist/components/tooltip/Tooltip.vue.d.ts +20 -7
  410. package/dist/components/tooltip/TooltipContent.vue.d.ts +20 -12
  411. package/dist/components/tooltip/TooltipProvider.vue.d.ts +12 -5
  412. package/dist/components/tooltip/TooltipTrigger.vue.d.ts +12 -5
  413. package/dist/components/tooltip/index.d.ts +4 -4
  414. package/dist/components/typewriter/composables/useTypewriter.d.ts +1 -0
  415. package/dist/components/typewriter/utils/timing.d.ts +0 -2
  416. package/dist/components/watercolor-dot/WatercolorDot.vue.d.ts +5 -18
  417. package/dist/components/watercolor-dot/useWatercolorBlob.d.ts +1 -4
  418. package/dist/composables/color/accent-tone-solve.d.ts +2 -5
  419. package/dist/composables/color/index.d.ts +31 -38
  420. package/dist/composables/color/useAccentTone.d.ts +0 -3
  421. package/dist/composables/color/value.d.ts +21 -0
  422. package/dist/composables/context/createContext.d.ts +2 -2
  423. package/dist/composables/dark/index.d.ts +1 -1
  424. package/dist/composables/dark/useGlobalDark.d.ts +7 -6
  425. package/dist/composables/dom/useBreakpoint.d.ts +2 -2
  426. package/dist/composables/dom/useClipboard.d.ts +28 -52
  427. package/dist/composables/dom/useDocumentVisibility.d.ts +3 -1
  428. package/dist/composables/dom/useIdleReady.d.ts +5 -6
  429. package/dist/composables/dom/useTokenColor.d.ts +2 -2
  430. package/dist/composables/dom/useUserInvalidAria.d.ts +2 -3
  431. package/dist/composables/dom/useViewportReady.d.ts +4 -4
  432. package/dist/composables/glass/backdropLuminanceSample.d.ts +7 -6
  433. package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +7 -1
  434. package/dist/composables/glass/textureUpload.d.ts +8 -10
  435. package/dist/composables/glass/useGlassBackdropLuminance.d.ts +11 -19
  436. package/dist/composables/glass/useSpecularPointer.d.ts +6 -6
  437. package/dist/composables/glass/useSpecularTracking.d.ts +12 -15
  438. package/dist/composables/glass/wave/waveField.d.ts +2 -2
  439. package/dist/composables/glass/wave/waveField.glsl.d.ts +1 -1
  440. package/dist/composables/glass/wave/waveField.wgsl.d.ts +1 -1
  441. package/dist/composables/glass/webgl/backingSize.d.ts +5 -8
  442. package/dist/composables/glass/webgl/compile.d.ts +1 -1
  443. package/dist/composables/glass/webgl/createCanvasLifecycle.d.ts +20 -20
  444. package/dist/composables/glass/webgl/useWebGLCanvas.d.ts +16 -28
  445. package/dist/composables/glass/webgl/visibility.d.ts +3 -4
  446. package/dist/composables/glass/webgpu/useGpuSubstrate.d.ts +10 -10
  447. package/dist/composables/glass/webgpu/useWebGPUCanvas.d.ts +1 -1
  448. package/dist/composables/glass/webgpu/webgpuCanvasTypes.d.ts +12 -10
  449. package/dist/composables/glass/webgpu/webgpuDevice.d.ts +15 -16
  450. package/dist/composables/motion/core/asElement.d.ts +2 -0
  451. package/dist/composables/motion/{constants.d.ts → core/constants.d.ts} +2 -3
  452. package/dist/composables/motion/core/index.d.ts +20 -23
  453. package/dist/composables/motion/core/useReducedMotion.d.ts +5 -0
  454. package/dist/composables/motion/core/useViewTransition.d.ts +44 -0
  455. package/dist/composables/motion/index.d.ts +14 -16
  456. package/dist/composables/motion/{useDockCtaReceive.d.ts → morph/useDockCtaReceive.d.ts} +2 -3
  457. package/dist/composables/motion/{useDragMorph.d.ts → morph/useDragMorph.d.ts} +7 -7
  458. package/dist/composables/motion/morph/useElementMorph.d.ts +50 -0
  459. package/dist/composables/motion/{useLeadTrail.d.ts → morph/useLeadTrail.d.ts} +6 -6
  460. package/dist/composables/motion/{useSelectionGroup.d.ts → morph/useSelectionGroup.d.ts} +7 -5
  461. package/dist/composables/motion/{useSelectionIndicator.d.ts → morph/useSelectionIndicator.d.ts} +9 -16
  462. package/dist/composables/motion/{useNumericTransition.d.ts → number/useNumericTransition.d.ts} +2 -2
  463. package/dist/composables/motion/{pointerFieldMappings.d.ts → pointer/pointerFieldMappings.d.ts} +7 -10
  464. package/dist/composables/motion/{usePointerVelocityField.d.ts → pointer/usePointerVelocityField.d.ts} +1 -1
  465. package/dist/composables/motion/{bloomUpField.d.ts → reveal/bloomUpField.d.ts} +1 -1
  466. package/dist/composables/motion/reveal/useBloomUp.d.ts +33 -0
  467. package/dist/composables/motion/{useLiquidReveal.d.ts → reveal/useLiquidReveal.d.ts} +5 -5
  468. package/dist/composables/motion/{useStagger.d.ts → reveal/useStagger.d.ts} +3 -3
  469. package/dist/composables/motion/reveal/useStaggerReveal.d.ts +28 -0
  470. package/dist/composables/motion/{useScrollChrome.d.ts → scroll/useScrollChrome.d.ts} +2 -2
  471. package/dist/composables/motion/{useScrollProgress.d.ts → scroll/useScrollProgress.d.ts} +12 -2
  472. package/dist/composables/motion/{springPresets.d.ts → spring/springPresets.d.ts} +8 -7
  473. package/dist/composables/motion/{springProjection.d.ts → spring/springProjection.d.ts} +3 -0
  474. package/dist/composables/motion/{useLiquidFlex.d.ts → spring/useLiquidFlex.d.ts} +11 -12
  475. package/dist/composables/motion/{useLiquidPress.d.ts → spring/useLiquidPress.d.ts} +3 -3
  476. package/dist/composables/motion/{useSpring.d.ts → spring/useSpring.d.ts} +5 -2
  477. package/dist/composables/motion/{useSpringMount.d.ts → spring/useSpringMount.d.ts} +8 -1
  478. package/dist/composables/sidebar/types.d.ts +3 -3
  479. package/dist/{configurator-CSENXXSy.js → configurator-M5OaIlJd.js} +11 -11
  480. package/dist/configurator.js +1 -1
  481. package/dist/{constants-DRZmNIY-.js → constants-nBz23tJE.js} +1 -1
  482. package/dist/constellation.js +125 -124
  483. package/dist/{createCanvasLifecycle-hsFkQVdl.js → createCanvasLifecycle-DbXRwjU_.js} +87 -82
  484. package/dist/dark-mode-toggle.d.ts +1 -0
  485. package/dist/dark-mode-toggle.js +43 -0
  486. package/dist/dark.js +1 -1
  487. package/dist/data-table-BygKg6ZA.js +491 -0
  488. package/dist/data-table.js +1 -1
  489. package/dist/deck.js +3 -3
  490. package/dist/dialog-TNRDkcE4.js +385 -0
  491. package/dist/dialog.js +1 -1
  492. package/dist/dock.js +523 -940
  493. package/dist/dockContext-BUTlGRNJ.js +13 -0
  494. package/dist/dom.js +52 -114
  495. package/dist/drawer.js +262 -209
  496. package/dist/dropdown-menu-BlbnvMaZ.js +507 -0
  497. package/dist/dropdown-menu.js +2 -2
  498. package/dist/easing.js +294 -278
  499. package/dist/expandable-container.js +107 -93
  500. package/dist/fading-scroll-DhxXIhm2.js +96 -0
  501. package/dist/fading-scroll.js +1 -1
  502. package/dist/field-control.css_vue_type_style_index_0_src_true_lang-CeLay9Tk.js +21 -0
  503. package/dist/floating-dB5TD88k.js +7 -0
  504. package/dist/{flow.glsl-DEGFGE11.js → flow.wgsl-Cd2GUkPx.js} +2 -2
  505. package/dist/fonts/README.md +14 -18
  506. package/dist/forms.js +279 -174
  507. package/dist/fourier-field.js +148 -148
  508. package/dist/glass-ui.css +1 -1
  509. package/dist/glass-ui.js +512 -373
  510. package/dist/handmark.js +4 -4
  511. package/dist/header-ribbon.js +37 -68
  512. package/dist/index.d.ts +9 -18
  513. package/dist/infinite-scroll.js +31 -28
  514. package/dist/instrument-chassis.js +54 -2
  515. package/dist/label-DnMqH7jr.js +55 -0
  516. package/dist/label.js +1 -1
  517. package/dist/labeled-field.js +291 -214
  518. package/dist/liquid-grid.js +141 -151
  519. package/dist/menuRowClass-Nh7CtMON.js +12 -0
  520. package/dist/metric.d.ts +1 -0
  521. package/dist/metric.js +192 -0
  522. package/dist/motion-core.js +73 -165
  523. package/dist/motion.js +104 -161
  524. package/dist/number-field-Boe09SvX.js +154 -0
  525. package/dist/number-field.js +1 -1
  526. package/dist/pager-dots.js +2 -2
  527. package/dist/paper-backdrop.js +16 -24
  528. package/dist/{pointerFieldMappings-CZojkgKj.js → pointerFieldMappings-DP5tla5z.js} +4 -7
  529. package/dist/popover-BQGYXZyO.js +244 -0
  530. package/dist/popover.js +1 -1
  531. package/dist/{presets-CxeNvxuO.js → presets-5myqNv59.js} +1 -1
  532. package/dist/primitive-B2t2jdqM.js +11 -0
  533. package/dist/progress-DClbhRAT.js +88 -0
  534. package/dist/progress.js +1 -1
  535. package/dist/pulse.js +29 -43
  536. package/dist/reactive.js +1 -1
  537. package/dist/resolveCanvasColor-DS_6DBxA.js +82 -0
  538. package/dist/resolveSurfaceClass-DCZ4PZcy.js +6 -0
  539. package/dist/{scrollReader-C2oK9Hb8.js → scrollReader-Brf5cQNo.js} +1 -1
  540. package/dist/search.js +241 -213
  541. package/dist/select-BcBAyLXA.js +336 -0
  542. package/dist/select.js +1 -1
  543. package/dist/selection-DqIULxRG.js +6 -0
  544. package/dist/separator-CgZInrX-.js +75 -0
  545. package/dist/separator.js +1 -1
  546. package/dist/sidebar.js +53 -42
  547. package/dist/slider-DzqeQmMu.js +162 -0
  548. package/dist/slider.js +2 -2
  549. package/dist/sortable-list.js +9 -8
  550. package/dist/springPresets-rCii5KqX.js +52 -0
  551. package/dist/status-dot.js +26 -43
  552. package/dist/styles/accessibility.css +1 -1
  553. package/dist/styles/animations.css +1 -1
  554. package/dist/styles/components.css +1 -1
  555. package/dist/styles/glass/a11y-fallback.css +1 -1
  556. package/dist/styles/glass/accent-tone.css +1 -1
  557. package/dist/styles/glass/control-surfaces.css +1 -1
  558. package/dist/styles/glass/defined.css +1 -1
  559. package/dist/styles/glass/glass-atom.css +1 -1
  560. package/dist/styles/glass/glass-capsule.css +1 -1
  561. package/dist/styles/glass/glass-chip.css +1 -1
  562. package/dist/styles/glass/ladder.css +1 -1
  563. package/dist/styles/glass/liquid-fill.css +1 -1
  564. package/dist/styles/glass/material-roles.css +1 -1
  565. package/dist/styles/glass/material.css +1 -1
  566. package/dist/styles/glass/reveal.css +1 -1
  567. package/dist/styles/glass/squircle.css +1 -1
  568. package/dist/styles/glass/surface-axis.css +1 -1
  569. package/dist/styles/glass/surfaces-pager.css +1 -1
  570. package/dist/styles/glass/surfaces.css +1 -1
  571. package/dist/styles/glass-refract.css +1 -1
  572. package/dist/styles/glass.css +1 -1
  573. package/dist/styles/index.css +1 -1
  574. package/dist/styles/paper.css +1 -1
  575. package/dist/styles/scroll-driven.css +1 -1
  576. package/dist/styles/theme/bridges.css +1 -1
  577. package/dist/styles/theme/radius.css +1 -1
  578. package/dist/styles/tokens/color-radius.css +1 -1
  579. package/dist/styles/tokens/dark-arm.css +1 -1
  580. package/dist/styles/tokens/glass-deep.css +1 -1
  581. package/dist/styles/tokens/glass-fx.css +1 -1
  582. package/dist/styles/tokens/glass.css +1 -1
  583. package/dist/styles/tokens/light-dark.css +1 -1
  584. package/dist/styles/tokens/manifest.d.ts +14 -0
  585. package/dist/styles/tokens/manifest.ts +56 -0
  586. package/dist/styles/tokens/motion-registers.css +1 -1
  587. package/dist/styles/tokens/offsets.css +1 -1
  588. package/dist/styles/tokens/property-regs-specular.css +1 -1
  589. package/dist/styles/tokens/property-regs.css +1 -1
  590. package/dist/styles/tokens/scale-paper.css +1 -1
  591. package/dist/styles/tokens/scheme-motion.css +1 -1
  592. package/dist/styles/tokens/scheme-spring.css +1 -1
  593. package/dist/styles/tokens/scroll-tokens.css +1 -1
  594. package/dist/styles/tokens/shadow.css +1 -1
  595. package/dist/styles/tokens/sizing-config.css +1 -1
  596. package/dist/styles/tokens/sizing.css +1 -1
  597. package/dist/styles/tokens.d.ts +4 -9
  598. package/dist/styles/tokens.ts +5 -9
  599. package/dist/styles/transitions.css +1 -1
  600. package/dist/styles/typography/scale.css +1 -1
  601. package/dist/styles/typography/semantic.css +1 -1
  602. package/dist/styles/typography/utilities.css +1 -1
  603. package/dist/styles/utilities/a11y-overrides.css +1 -1
  604. package/dist/styles/utilities/base-misc.css +1 -1
  605. package/dist/styles/utilities/base.css +1 -1
  606. package/dist/styles/utilities/btn.css +1 -1
  607. package/dist/styles/utilities/components.css +1 -1
  608. package/dist/styles/utilities/metal.css +1 -1
  609. package/dist/styles/utilities/responsive.css +1 -0
  610. package/dist/styles/view-transition.css +1 -1
  611. package/dist/{supportsCssTimeline-Dz-GVxXM.js → supportsCssTimeline-C2zyWpl8.js} +1 -1
  612. package/dist/surface.js +2 -3
  613. package/dist/switch-DZjQuFLa.js +54 -0
  614. package/dist/switch.js +1 -1
  615. package/dist/tabs.js +141 -169
  616. package/dist/timeline.js +86 -87
  617. package/dist/toast-CH5mnsYT.js +267 -0
  618. package/dist/toast.js +1 -1
  619. package/dist/toggle-group-O6jBo7AE.js +150 -0
  620. package/dist/toggle-group.js +1 -1
  621. package/dist/tokens.js +96 -5
  622. package/dist/tooltip-Drx5qnw_.js +148 -0
  623. package/dist/tooltip.js +1 -1
  624. package/dist/typewriter.js +222 -193
  625. package/dist/{useAccentTone-C-p6WdPe.js → useAccentTone-DyInfHXE.js} +2 -2
  626. package/dist/{useAnimatedNumber-BRVVaFFc.js → useAnimatedNumber-WPtMz0AN.js} +2 -2
  627. package/dist/useClipboard-D36OTaeT.js +53 -0
  628. package/dist/useDockCtaReceive-DkrOTM0q.js +146 -0
  629. package/dist/useDocumentVisibility-Dh_TGXsU.js +21 -0
  630. package/dist/useDragMorph-BogSV_nv.js +293 -0
  631. package/dist/{useIntersectionPause-B-dbxl9w.js → useIntersectionPause-B5mAU6XY.js} +15 -15
  632. package/dist/useLeadTrail-AW1-zZcd.js +74 -0
  633. package/dist/{useLiquidFlex-Ch4exNvN.js → useLiquidFlex-TJZmRqyY.js} +1 -1
  634. package/dist/useLiquidPress-BOxuDkKa.js +101 -0
  635. package/dist/useMotionAxis-D-rtIhca.js +19 -0
  636. package/dist/usePointerVelocityField-DsIf7yyq.js +121 -0
  637. package/dist/useRAFLoop-B3YlWK4M.js +98 -0
  638. package/dist/useReducedMotion-vCXA_vyM.js +27 -0
  639. package/dist/useRoutePointer-B8itM7Ka.js +53 -0
  640. package/dist/useScrollProgress-CSFYXIZT.js +43 -0
  641. package/dist/useScrollTrigger-BDdqptlc.js +87 -0
  642. package/dist/useSelectionGroup-B5XplBbo.js +132 -0
  643. package/dist/{useSpring-BBSKFx_0.js → useSpring-9u2_shxV.js} +2 -2
  644. package/dist/useSpringMount-uQ8QL9Ff.js +74 -0
  645. package/dist/useTabRovingFocus-YnWh-Ytr.js +169 -0
  646. package/dist/useTextHighlight-mfwNIC1f.js +112 -0
  647. package/dist/{useTouchGate-Cj5fOkTv.js → useTouchGate-B4mzQcHJ.js} +1 -1
  648. package/dist/vSpecular-DMJo8pfD.js +68 -0
  649. package/dist/value-DMhh2R94.js +33 -0
  650. package/dist/watercolor-dot.js +77 -103
  651. package/package.json +163 -232
  652. package/dist/DropdownMenuTrigger-BzQtd9lt.js +0 -65
  653. package/dist/FadingScroll-C1ZWEjmG.js +0 -83
  654. package/dist/Input-B4WQ_CMc.js +0 -57
  655. package/dist/Surface-BWRfwyn8.js +0 -59
  656. package/dist/accent-tone-solve-Dii9ePhK.js +0 -20
  657. package/dist/badge-C0C-rTBu.js +0 -80
  658. package/dist/button-32mg6xA8.js +0 -194
  659. package/dist/card-16xmzIr3.js +0 -303
  660. package/dist/coalesce-metric-znAcLzkB.js +0 -13
  661. package/dist/collapsible-bPzS1aQk.js +0 -63
  662. package/dist/color-swatch.d.ts +0 -1
  663. package/dist/color-swatch.js +0 -48
  664. package/dist/command-BjPjUU9z.js +0 -307
  665. package/dist/components/_shared/menuItemVariants.d.ts +0 -51
  666. package/dist/components/_shared/useControlSize.d.ts +0 -46
  667. package/dist/components/_shared/useStalePropWarning.d.ts +0 -27
  668. package/dist/components/_shared/useSurfaceAxis.d.ts +0 -64
  669. package/dist/components/card/ScrollCard.vue.d.ts +0 -33
  670. package/dist/components/card/ScrollCardHeader.vue.d.ts +0 -17
  671. package/dist/components/carousel/CarouselNext.vue.d.ts +0 -21
  672. package/dist/components/carousel/CarouselPrevious.vue.d.ts +0 -21
  673. package/dist/components/color-swatch/ColorSwatch.vue.d.ts +0 -22
  674. package/dist/components/color-swatch/index.d.ts +0 -1
  675. package/dist/components/controls/DarkModeToggle.vue.d.ts +0 -32
  676. package/dist/components/controls/dark-mode-toggle.css +0 -1
  677. package/dist/components/controls/index.d.ts +0 -1
  678. package/dist/components/data-table/DataTablePagination.vue.d.ts +0 -13
  679. package/dist/components/dock/DockSection.vue.d.ts +0 -69
  680. package/dist/components/dock/DockStack.vue.d.ts +0 -73
  681. package/dist/components/dock/composables/dockMorphContext.d.ts +0 -91
  682. package/dist/components/dock/composables/useDockFisheye.d.ts +0 -24
  683. package/dist/components/dock/composables/useDockPopover.d.ts +0 -70
  684. package/dist/components/dock/styles/fisheye.css +0 -1
  685. package/dist/components/dock/styles/popover.css +0 -1
  686. package/dist/components/dock/styles/section.css +0 -1
  687. package/dist/components/focus-scope/FocusScope.vue.d.ts +0 -20
  688. package/dist/components/focus-scope/index.d.ts +0 -2
  689. package/dist/components/goo-filter/GooFilter.vue.d.ts +0 -22
  690. package/dist/components/goo-filter/index.d.ts +0 -1
  691. package/dist/components/icon-chip/IconChip.vue.d.ts +0 -4
  692. package/dist/components/icon-chip/index.d.ts +0 -2
  693. package/dist/components/icon-chip/styles.css +0 -1
  694. package/dist/components/icon-chip/types.d.ts +0 -92
  695. package/dist/components/icon-tooltip/IconTooltip.vue.d.ts +0 -16
  696. package/dist/components/icon-tooltip/index.d.ts +0 -1
  697. package/dist/components/instrument-chassis/ChassisDivider.vue.d.ts +0 -30
  698. package/dist/components/metric-badge/MetricBadge.vue.d.ts +0 -40
  699. package/dist/components/metric-badge/index.d.ts +0 -2
  700. package/dist/components/metric-cell/MetricCell.vue.d.ts +0 -108
  701. package/dist/components/metric-cell/index.d.ts +0 -2
  702. package/dist/components/metric-stack/MetricRow.vue.d.ts +0 -120
  703. package/dist/components/metric-stack/MetricStack.vue.d.ts +0 -92
  704. package/dist/components/metric-stack/index.d.ts +0 -4
  705. package/dist/components/notification/Notification.vue.d.ts +0 -16
  706. package/dist/components/notification/index.d.ts +0 -1
  707. package/dist/components/progress/ProgressDefault.vue.d.ts +0 -9
  708. package/dist/components/progress/ProgressGradient.vue.d.ts +0 -26
  709. package/dist/components/progress/ProgressLiquid.vue.d.ts +0 -22
  710. package/dist/components/progress/valueMarks.css +0 -1
  711. package/dist/components/spa-view/SpaView.vue.d.ts +0 -50
  712. package/dist/components/spa-view/index.d.ts +0 -2
  713. package/dist/components/split-chars/SplitChars.vue.d.ts +0 -53
  714. package/dist/components/split-chars/index.d.ts +0 -1
  715. package/dist/components/tabs/composables/useEyeglassLive.d.ts +0 -17
  716. package/dist/components/toggle/Toggle.vue.d.ts +0 -29
  717. package/dist/components/toggle/index.d.ts +0 -26
  718. package/dist/composables/glass/webgl/shaders/glass-refract.glsl.d.ts +0 -36
  719. package/dist/composables/motion/curves.d.ts +0 -48
  720. package/dist/composables/motion/gooBarbellGeometry.d.ts +0 -79
  721. package/dist/composables/motion/morphSignatures.d.ts +0 -88
  722. package/dist/composables/motion/suite.d.ts +0 -2
  723. package/dist/composables/motion/useBloomUp.d.ts +0 -97
  724. package/dist/composables/motion/useCharStagger.d.ts +0 -44
  725. package/dist/composables/motion/useElementMorph.d.ts +0 -107
  726. package/dist/composables/motion/useGooMorph.d.ts +0 -72
  727. package/dist/composables/motion/usePrioritizedTask.d.ts +0 -41
  728. package/dist/composables/motion/useStaggerReveal.d.ts +0 -52
  729. package/dist/composables/motion/useViewTransition.d.ts +0 -81
  730. package/dist/composables/virtual/index.d.ts +0 -4
  731. package/dist/composables/virtual/useVirtualSectionWindow.d.ts +0 -72
  732. package/dist/composables/virtual/useWindowedStore.d.ts +0 -43
  733. package/dist/composables/virtual/virtualSectionLayout.d.ts +0 -103
  734. package/dist/controls.d.ts +0 -1
  735. package/dist/controls.js +0 -83
  736. package/dist/data-table-DveMdgBM.js +0 -629
  737. package/dist/dialog-BzQxKrUr.js +0 -281
  738. package/dist/dockContext-4S9qP0Z4.js +0 -13
  739. package/dist/dropdown-menu-DORIA52F.js +0 -378
  740. package/dist/focus-scope-DUTKveod.js +0 -23
  741. package/dist/focus-scope.d.ts +0 -1
  742. package/dist/focus-scope.js +0 -2
  743. package/dist/icon-chip-BWuxz1-u.js +0 -57
  744. package/dist/icon-chip.d.ts +0 -1
  745. package/dist/icon-chip.js +0 -2
  746. package/dist/icon-tooltip-CO6rw0dZ.js +0 -25
  747. package/dist/icon-tooltip.d.ts +0 -1
  748. package/dist/icon-tooltip.js +0 -2
  749. package/dist/instrument-chassis-Cvstg4_U.js +0 -65
  750. package/dist/label-Cj38tJKG.js +0 -36
  751. package/dist/menuItemVariants-D6AOzzND.js +0 -36
  752. package/dist/metric-badge.d.ts +0 -1
  753. package/dist/metric-badge.js +0 -79
  754. package/dist/metric-cell.d.ts +0 -1
  755. package/dist/metric-cell.js +0 -61
  756. package/dist/metric-stack.d.ts +0 -1
  757. package/dist/metric-stack.js +0 -113
  758. package/dist/motion-curves.d.ts +0 -1
  759. package/dist/motion-curves.js +0 -146
  760. package/dist/notification-DVs7ODMg.js +0 -52
  761. package/dist/notification.d.ts +0 -1
  762. package/dist/notification.js +0 -2
  763. package/dist/number-field-PvkGedHk.js +0 -140
  764. package/dist/popover-WMgyRSyN.js +0 -256
  765. package/dist/progress-C-HHuFZy.js +0 -192
  766. package/dist/resolveCanvasColor-yarP5bdk.js +0 -93
  767. package/dist/select-BIv4R3Ir.js +0 -326
  768. package/dist/separator-CBU94kTM.js +0 -62
  769. package/dist/slider-Cc_qw6GA.js +0 -166
  770. package/dist/spa-view.d.ts +0 -1
  771. package/dist/spa-view.js +0 -46
  772. package/dist/springPresets-h5VKJQqG.js +0 -58
  773. package/dist/styles/glass/progress-rail.css +0 -1
  774. package/dist/switch-Boj4Ucn5.js +0 -49
  775. package/dist/toast-kAbchIN2.js +0 -281
  776. package/dist/toggle-group-BPchFsns.js +0 -178
  777. package/dist/tooltip-BmOQJzh0.js +0 -111
  778. package/dist/useControlSize-CFfOk8I-.js +0 -9
  779. package/dist/useDockCtaReceive-CK-RLASa.js +0 -257
  780. package/dist/useDocumentVisibility-YeBDGqtI.js +0 -17
  781. package/dist/useDragMorph-7ygF9O0m.js +0 -93
  782. package/dist/useGooMorph-D1F6QJlH.js +0 -349
  783. package/dist/useLeadTrail-BYu8dq4G.js +0 -74
  784. package/dist/useLiquidPress-BYcwwGQV.js +0 -68
  785. package/dist/useMotionAxis-DCCXMC-i.js +0 -20
  786. package/dist/usePointerVelocityField-BdvnO0py.js +0 -122
  787. package/dist/useRAFLoop-BtwQ9DyY.js +0 -109
  788. package/dist/useRoutePointer-BDNMFN73.js +0 -52
  789. package/dist/useScrollProgress-DZI4nf4v.js +0 -35
  790. package/dist/useSelectionGroup-LDzBdR1Y.js +0 -215
  791. package/dist/useSpringMount-C6-DBx4e.js +0 -71
  792. package/dist/useSpringPress-BypV_KO-.js +0 -36
  793. package/dist/useSurfaceAxis-DudiZVOR.js +0 -10
  794. package/dist/useTabRovingFocus-DkLYwo4L.js +0 -143
  795. package/dist/vReveal-DZfWl9XI.js +0 -10
  796. package/dist/vSpecular-CQFmLNzA.js +0 -68
  797. package/src/fonts/README.md +0 -124
  798. package/src/fonts/fira-code/OFL.txt +0 -93
  799. package/src/fonts/fira-code/fira-code-latin-ext.woff2 +0 -0
  800. package/src/fonts/fira-code/fira-code-latin.woff2 +0 -0
  801. package/src/fonts/plus-jakarta-sans/OFL.txt +0 -93
  802. package/src/fonts/plus-jakarta-sans/plus-jakarta-sans-latin-ext.woff2 +0 -0
  803. package/src/fonts/plus-jakarta-sans/plus-jakarta-sans-latin.woff2 +0 -0
  804. package/src/styles/accessibility.css +0 -48
  805. package/src/styles/animations.css +0 -468
  806. package/src/styles/draw-in.css +0 -89
  807. package/src/styles/fonts.css +0 -143
  808. package/src/styles/glass/a11y-fallback.css +0 -188
  809. package/src/styles/glass/accent-tone.css +0 -66
  810. package/src/styles/glass/control-surfaces.css +0 -171
  811. package/src/styles/glass/deep.css +0 -114
  812. package/src/styles/glass/defined.css +0 -88
  813. package/src/styles/glass/glass-atom.css +0 -247
  814. package/src/styles/glass/glass-capsule.css +0 -175
  815. package/src/styles/glass/glass-chip.css +0 -211
  816. package/src/styles/glass/grain-overlay.css +0 -79
  817. package/src/styles/glass/ladder-undershadow.css +0 -61
  818. package/src/styles/glass/ladder.css +0 -452
  819. package/src/styles/glass/liquid-enter.css +0 -240
  820. package/src/styles/glass/liquid-fill.css +0 -65
  821. package/src/styles/glass/material-roles.css +0 -18
  822. package/src/styles/glass/material.css +0 -500
  823. package/src/styles/glass/progress-rail.css +0 -78
  824. package/src/styles/glass/reveal.css +0 -333
  825. package/src/styles/glass/rim.css +0 -122
  826. package/src/styles/glass/squircle.css +0 -58
  827. package/src/styles/glass/surface-axis.css +0 -136
  828. package/src/styles/glass/surfaces-pager.css +0 -79
  829. package/src/styles/glass/surfaces.css +0 -434
  830. package/src/styles/glass-refract.css +0 -112
  831. package/src/styles/glass-specular-track.css +0 -74
  832. package/src/styles/glass.css +0 -110
  833. package/src/styles/index.css +0 -288
  834. package/src/styles/paper.css +0 -187
  835. package/src/styles/scroll-choreography.css +0 -294
  836. package/src/styles/scroll-chrome.css +0 -123
  837. package/src/styles/scroll-driven.css +0 -123
  838. package/src/styles/theme/bridges.css +0 -380
  839. package/src/styles/theme/dark.css +0 -7
  840. package/src/styles/theme/literals.css +0 -104
  841. package/src/styles/theme/radius.css +0 -148
  842. package/src/styles/theme.css +0 -33
  843. package/src/styles/tokens/color-radius.css +0 -308
  844. package/src/styles/tokens/dark-arm-glass.css +0 -55
  845. package/src/styles/tokens/dark-arm.css +0 -455
  846. package/src/styles/tokens/glass-deep.css +0 -126
  847. package/src/styles/tokens/glass-fx.css +0 -480
  848. package/src/styles/tokens/glass.css +0 -477
  849. package/src/styles/tokens/light-dark.css +0 -216
  850. package/src/styles/tokens/motion-registers.css +0 -93
  851. package/src/styles/tokens/offsets.css +0 -142
  852. package/src/styles/tokens/on-glass-fg.css +0 -39
  853. package/src/styles/tokens/property-regs-specular.css +0 -160
  854. package/src/styles/tokens/property-regs.css +0 -448
  855. package/src/styles/tokens/scale-paper.css +0 -495
  856. package/src/styles/tokens/scheme-motion.css +0 -425
  857. package/src/styles/tokens/scheme-spring.css +0 -282
  858. package/src/styles/tokens/scroll-tokens.css +0 -137
  859. package/src/styles/tokens/shadow.css +0 -206
  860. package/src/styles/tokens/sizing-config.css +0 -110
  861. package/src/styles/tokens/sizing.css +0 -505
  862. package/src/styles/tokens.css +0 -68
  863. package/src/styles/tokens.ts +0 -58
  864. package/src/styles/transitions.css +0 -228
  865. package/src/styles/typography/scale.css +0 -166
  866. package/src/styles/typography/semantic.css +0 -233
  867. package/src/styles/typography/utilities.css +0 -197
  868. package/src/styles/typography.css +0 -46
  869. package/src/styles/utilities/a11y-overrides.css +0 -184
  870. package/src/styles/utilities/base-misc.css +0 -307
  871. package/src/styles/utilities/base.css +0 -337
  872. package/src/styles/utilities/btn.css +0 -306
  873. package/src/styles/utilities/components.css +0 -337
  874. package/src/styles/utilities/metal.css +0 -272
  875. package/src/styles/utilities.css +0 -24
  876. package/src/styles/view-transition.css +0 -67
  877. package/src/styles/viz-reveal.css +0 -63
  878. /package/dist/{_plugin-vue_export-helper-C1je1s0u.js → _plugin-vue_export-helper-B3ysoDQm.js} +0 -0
  879. /package/dist/{compile-DK3Fri1B.js → compile-D7VYG3gi.js} +0 -0
  880. /package/dist/composables/glass/{webgl/shaders/procedural-color.glsl.d.ts → procedural/color.glsl.d.ts} +0 -0
  881. /package/dist/{components/aurora/constants/shaders/procedural-color.wgsl.d.ts → composables/glass/procedural/color.wgsl.d.ts} +0 -0
  882. /package/dist/composables/motion/{motionTempo.d.ts → core/motionTempo.d.ts} +0 -0
  883. /package/dist/composables/motion/{useIntersectionPause.d.ts → core/useIntersectionPause.d.ts} +0 -0
  884. /package/dist/composables/motion/{useRAFLoop.d.ts → core/useRAFLoop.d.ts} +0 -0
  885. /package/dist/composables/motion/{useYieldToMain.d.ts → core/useYieldToMain.d.ts} +0 -0
  886. /package/dist/composables/motion/{useAnimatedNumber.d.ts → number/useAnimatedNumber.d.ts} +0 -0
  887. /package/dist/composables/motion/{useAnimatedNumberMap.d.ts → number/useAnimatedNumberMap.d.ts} +0 -0
  888. /package/dist/composables/motion/{useCountup.d.ts → number/useCountup.d.ts} +0 -0
  889. /package/dist/composables/motion/{useRoutePointer.d.ts → pointer/useRoutePointer.d.ts} +0 -0
  890. /package/dist/composables/motion/{useTextHighlight.d.ts → reveal/useTextHighlight.d.ts} +0 -0
  891. /package/dist/composables/motion/{vReveal.d.ts → reveal/vReveal.d.ts} +0 -0
  892. /package/dist/composables/motion/{scrollReader.d.ts → scroll/scrollReader.d.ts} +0 -0
  893. /package/dist/composables/motion/{supportsCssTimeline.d.ts → scroll/supportsCssTimeline.d.ts} +0 -0
  894. /package/dist/composables/motion/{useScrollPin.d.ts → scroll/useScrollPin.d.ts} +0 -0
  895. /package/dist/composables/motion/{useScrollScene.d.ts → scroll/useScrollScene.d.ts} +0 -0
  896. /package/dist/composables/motion/{useScrollTrigger.d.ts → scroll/useScrollTrigger.d.ts} +0 -0
  897. /package/dist/composables/motion/{useSpringPress.d.ts → spring/useSpringPress.d.ts} +0 -0
  898. /package/dist/{createContext-FU0n5MGK.js → createContext-BhkE9mLH.js} +0 -0
  899. /package/dist/{dark-DIH-blY1.js → dark-z_P5QwqI.js} +0 -0
  900. /package/dist/{motionTempo-CMS5b0Vu.js → motionTempo-tscp92Nm.js} +0 -0
  901. /package/dist/{prng-Dz2_9RJu.js → prng-2uS6J_A1.js} +0 -0
  902. /package/dist/{rendererStatus-C2-zC2Q-.js → rendererStatus-BxUlBLVy.js} +0 -0
  903. /package/dist/{useDragVelocity-CbMZEBaC.js → useDragVelocity-6ONX0sr0.js} +0 -0
  904. /package/dist/{useFuzzySearch-BOgep7RE.js → useFuzzySearch-CUnZknpm.js} +0 -0
  905. /package/dist/{useResizeObserver-BcUTquV7.js → useResizeObserver-CdUlm2QZ.js} +0 -0
  906. /package/dist/{useResolveTokenColor-C07e-qaT.js → useResolveTokenColor-B2FfJnd9.js} +0 -0
  907. /package/dist/{useTimer-D8koDvJS.js → useTimer-C3yfti4C.js} +0 -0
  908. /package/dist/{useUserInvalidAria-Cr4nR3tu.js → useUserInvalidAria-0GNrddMX.js} +0 -0
  909. /package/dist/{valueDomain-B8L04u3U.js → valueDomain-SbVzoHMH.js} +0 -0
  910. /package/dist/{writeVelocityWeight-D_AcvTsE.js → writeVelocityWeight-DoGOCW3e.js} +0 -0
@@ -1 +1 @@
1
- export declare const AURORA_WGSL = "\nconst MAX_NUCLEI: i32 = 6;\nconst MAX_STOPS: i32 = 8;\nconst PI: f32 = 3.141592653589793;\n\n// \u2500\u2500 Uniforms (the typed-struct source-of-truth \u2014 see aurora.wgsl.uniforms.ts) \u2500\u2500\n// Scalars are packed into vec4 lanes to keep the std140/WGSL 16-byte struct alignment\n// trivial. nuc0[i] = (pos.x, pos.y, radius, paletteBias); nuc1[i] = (valueBias,\n// driftRadius, driftPhase, elong); nuc2[i] = (angle, _, _, _). palette[i].rgb is the\n// CPU-baked linear-sRGB stop.\nstruct Uniforms {\n // scalars0: (uTime, uSoftmaxBeta, uValueVariance, uWarpAmount)\n scalars0: vec4<f32>,\n // scalars1: (uWarpScale, uWarpDrift, uSaturation, uPaperGrain)\n scalars1: vec4<f32>,\n // scalars2: (uAlpha, uNucleiDrift, uPaletteDrift, uBreathDepth)\n scalars2: vec4<f32>,\n // scalars3: (uBreathPeriod, uCursorStrength, uCursorRadius, uVividness)\n // BD.W-AUR-VIVIDNESS \u2014 the .w pad lane now carries the \u00A73 chroma-floor strength.\n scalars3: vec4<f32>,\n // cursor: (uCursor.x, uCursor.y, uMetalPolish, uMetalHeightScale)\n // BG.W-AUR-METAL-FINISH \u2014 the two free pad lanes carry the metal-medium knobs (the\n // metal light is cursor-synthesized so it reads in-struct on the WGSL primary too).\n cursor: vec4<f32>,\n // ints: (uStopCount, uNucleiCount, uWarpMode, uNoiseOctaves)\n ints0: vec4<i32>,\n // ints1: (uMedium, uHuePath, _, _)\n ints1: vec4<i32>,\n palette: array<vec4<f32>, 8>,\n nuc0: array<vec4<f32>, 6>,\n nuc1: array<vec4<f32>, 6>,\n nuc2: array<vec4<f32>, 6>,\n // BC.W-VIZ-AURORA (T4) \u2014 the painterly-medium scalar lanes. APPENDED after the\n // arrays so EVERY existing offset (and the smooth-default parity capture) is\n // byte-identical (these lanes are written 0 on a smooth config). scalars4:\n // (uStrokeAmount, uStrokeScale, uStrokeAnisotropy, uCanvasGrain). scalars5:\n // (uWetEdge, uGranulation, uBrokenColor, uKuwaharaQ). kuwahara: (uKuwaharaRadius,\n // uKuwaharaSectors, _, _) \u2014 the Kuwahara radius/sectors/q in lockstep with\n // packAuroraWGPUUniforms (the typed-struct discipline).\n scalars4: vec4<f32>,\n scalars5: vec4<f32>,\n kuwahara: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u: Uniforms;\n\n// \u2500\u2500 Authoring-scale \u2192 rad/sec lifts (byte-identical to aurora.frag.ts) \u2500\u2500\nconst K_NUCLEI: f32 = 14.0;\nconst K_PAL: f32 = 24.0;\nconst K_WARP: f32 = 5.0;\n\n// \u2500\u2500 Noise (aurora-local hash21/vnoise/fbm \u2014 legitimately divergent per AV.W2 \u00A73a) \u2500\u2500\nfn hash21(p0: vec2<f32>) -> f32 {\n var p = fract(p0 * vec2<f32>(123.34, 456.21));\n p = p + dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\n\nfn hash22(p0: vec2<f32>) -> vec2<f32> {\n let p = vec2<f32>(dot(p0, vec2<f32>(127.1, 311.7)),\n dot(p0, vec2<f32>(269.5, 183.3)));\n return fract(sin(p) * 43758.5453);\n}\n\nfn vnoise(p: vec2<f32>) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let uu = f * f * (3.0 - 2.0 * f);\n let a = hash21(i);\n let b = hash21(i + vec2<f32>(1.0, 0.0));\n let c = hash21(i + vec2<f32>(0.0, 1.0));\n let d = hash21(i + vec2<f32>(1.0, 1.0));\n return mix(mix(a, b, uu.x), mix(c, d, uu.x), uu.y);\n}\n\nconst FBM_ROT: mat2x2<f32> = mat2x2<f32>(0.8, 0.6, -0.6, 0.8);\n\nfn fbm(p0: vec2<f32>) -> f32 {\n var v = 0.0;\n var a = 0.5;\n var p = p0;\n let octaves = u.ints0.w;\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= octaves) { break; }\n v = v + a * vnoise(p);\n p = FBM_ROT * p * 2.02;\n a = a * 0.5;\n }\n return v;\n}\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n if (c <= 0.04045) { return c / 12.92; }\n return pow((c + 0.055) / 1.055, 2.4);\n}\nfn srgbToLinear(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfn linearToSrgbCh(c: f32) -> f32 {\n if (c <= 0.0031308) { return c * 12.92; }\n return 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nfn linearToSrgb(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS, row-major \u2192 WGSL columns (= the transpose).\nconst LINEAR_SRGB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients, row-major \u2192 WGSL columns.\nconst LMS_TO_OKLAB: mat3x3<f32> = mat3x3<f32>(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF, row-major \u2192 WGSL columns.\nconst OKLAB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB, row-major \u2192 WGSL columns.\nconst LMS_TO_LINEAR_SRGB: mat3x3<f32> = mat3x3<f32>(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn cbrt3(v: vec3<f32>) -> vec3<f32> {\n return sign(v) * pow(abs(v), vec3<f32>(1.0 / 3.0));\n}\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nfn srgbToOklab(c: vec3<f32>) -> vec3<f32> {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nfn oklabToLinearSrgb(lab: vec3<f32>) -> vec3<f32> {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS, folded to [0, 2pi). Mirrors value.js rawOklabToOklch.\nfn oklabToOklch(lab: vec3<f32>) -> vec3<f32> {\n let C = length(lab.yz);\n var H = atan2(lab.z, lab.y);\n if (H < 0.0) { H = H + 2.0 * PI; }\n return vec3<f32>(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nfn oklchToOklab(lch: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\n// value.js interpolateHue in the NORMALIZED-TURNS domain. methods: 0 shorter,\n// 1 longer, 2 increasing, 3 decreasing.\nfn interpolateHueTurns(h0: f32, h1: f32, t: f32, method: i32) -> f32 {\n let TAU = 2.0 * PI;\n var a = h0 / TAU;\n var b = h1 / TAU;\n let i = b - a;\n if (method == 0) { // shorter\n if (i > 0.5) { a = a + 1.0; } else if (i < -0.5) { b = b + 1.0; }\n } else if (method == 1) { // longer\n if (i > 0.0 && i < 0.5) { a = a + 1.0; } else if (i > -0.5 && i <= 0.0) { b = b + 1.0; }\n } else if (method == 2) { // increasing\n if (i < 0.0) { b = b + 1.0; }\n } else { // decreasing\n if (i > 0.0) { a = a + 1.0; }\n }\n var r = a + t * (b - a);\n r = fract(r); // fract is non-negative in WGSL\n return r * TAU; // back to radians\n}\n\n// Linear-sRGB endpoint pair \u2192 OKLab-rectangular interpolation \u2192 linear sRGB.\nfn mixPaletteOklab(linA: vec3<f32>, linB: vec3<f32>, t: f32) -> vec3<f32> {\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linA);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linB);\n let lab = mix(labA, labB, t);\n return oklabToLinearSrgb(lab);\n}\n\n// Linear-sRGB endpoint pair \u2192 OKLCh hue-arc interpolation \u2192 linear sRGB.\nfn mixPaletteOklchArc(linA: vec3<f32>, linB: vec3<f32>, t: f32, method: i32) -> vec3<f32> {\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linA);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linB);\n let lchA = oklabToOklch(labA);\n let lchB = oklabToOklch(labB);\n let L = mix(lchA.x, lchB.x, t);\n let C = mix(lchA.y, lchB.y, t);\n let H = interpolateHueTurns(lchA.z, lchB.z, t, method);\n return oklabToLinearSrgb(oklchToOklab(vec3<f32>(L, C, H)));\n}\n\n// The ramp dispatcher (the samplePalette splices it). tRaw is the raw inter-stop\n// parameter [0,1]; the smoothstep ease + the huePath branch live here so the ramp is\n// one source. increasing(2)/decreasing(3) take the OKLCh hue-arc; every other method\n// takes the OKLab-rectangular straight line.\nfn samplePaletteRamp(a: vec3<f32>, b: vec3<f32>, tRaw: f32, huePath: i32) -> vec3<f32> {\n let t = smoothstep(0.0, 1.0, tRaw);\n if (huePath == 2 || huePath == 3) {\n return mixPaletteOklchArc(a, b, t, huePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// Interleaved Gradient Noise (Jimenez) \u2014 display-space 1-LSB dither.\nfn ign(p: vec2<f32>) -> f32 {\n return fract(52.9829189 * fract(dot(p, vec2<f32>(0.06711056, 0.00583715))));\n}\n\n// Cellular / Worley f1.\nfn cellular(p: vec2<f32>) -> f32 {\n let i = floor(p);\n let f = fract(p);\n var m = 1e9;\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let g = vec2<f32>(f32(x), f32(y));\n let o = hash22(i + g);\n let r = g + o - f;\n m = min(m, dot(r, r));\n }\n }\n return sqrt(m);\n}\n\n// \u2500\u2500 Warp (Quilez double warp + the three warp modes + the cursor swirl) \u2500\u2500\nfn domainWarp(p: vec2<f32>, t: f32) -> vec2<f32> {\n let warpScale = u.scalars1.x;\n let warpDrift = u.scalars1.y;\n let warpAmount = u.scalars0.w;\n let warpMode = u.ints0.z;\n let cursorStrength = u.scalars3.y;\n let cursorRadius = u.scalars3.z;\n let cursor = u.cursor.xy;\n\n let q = vec2<f32>(fbm(p * warpScale + vec2<f32>(0.0, 0.0) + t * warpDrift * K_WARP),\n fbm(p * warpScale + vec2<f32>(5.2, 1.3) + t * warpDrift * K_WARP));\n let r = vec2<f32>(fbm(p * warpScale + 4.0 * q + vec2<f32>(1.7, 9.2)),\n fbm(p * warpScale + 4.0 * q + vec2<f32>(8.3, 2.8)));\n\n var warp = r;\n if (warpMode == 1) {\n // cellular \u2014 chunky territories\n let c1 = cellular(p * warpScale * 1.5 + vec2<f32>(t * warpDrift * K_WARP * 2.0, 0.0));\n let c2 = cellular(p * warpScale * 1.5 + vec2<f32>(11.0, 7.0 + t * warpDrift * K_WARP * 2.0));\n warp = vec2<f32>(c1, c2);\n } else if (warpMode == 2) {\n // hybrid \u2014 fbm + cellular averaged\n let c1 = cellular(p * warpScale * 1.2);\n let c2 = cellular(p * warpScale * 1.2 + vec2<f32>(11.0, 7.0));\n warp = mix(r, vec2<f32>(c1, c2), 0.5);\n }\n var warped = p + warpAmount * warp;\n\n // Cursor swirl \u2014 rotate p around the cursor with radial falloff.\n if (cursorStrength > 0.001) {\n let toP = p - cursor;\n let d = length(toP);\n let rr = max(cursorRadius, 0.01);\n let w = exp(-(d * d) / (rr * rr * 0.45));\n let ang = w * cursorStrength * 2.1;\n let ca = cos(ang);\n let sa = sin(ang);\n var rotated = vec2<f32>(ca * toP.x - sa * toP.y, sa * toP.x + ca * toP.y) + cursor;\n let pinch = w * cursorStrength * 0.08;\n rotated = mix(rotated, cursor, pinch);\n warped = mix(warped, rotated + warpAmount * warp * 0.7, w * cursorStrength);\n }\n return warped;\n}\n\n// \u2500\u2500 Palette LUT \u2500\u2500\nfn samplePalette(id0: f32) -> vec3<f32> {\n let id = clamp(id0, 0.0, 1.0);\n let stopCount = u.ints0.x;\n let scaled = id * f32(stopCount - 1);\n let i0 = i32(floor(scaled));\n let i1 = min(i0 + 1, stopCount - 1);\n let t = fract(scaled);\n return samplePaletteRamp(u.palette[i0].rgb, u.palette[i1].rgb, t, u.ints1.y);\n}\n\n// \u2500\u2500 Nuclei field (anisotropic Gaussian softmax + drift + palette drift) \u2500\u2500\nstruct NucleiResult { paletteId: f32, valueMod: f32 };\n\nfn nucleiField(p: vec2<f32>, t: f32) -> NucleiResult {\n let nucleiCount = u.ints0.y;\n let nucleiDrift = u.scalars2.y;\n let paletteDrift = u.scalars2.z;\n let softmaxBeta = u.scalars0.y;\n\n var accumBias = 0.0;\n var accumValue = 0.0;\n var accumW = 0.0;\n for (var i = 0; i < MAX_NUCLEI; i = i + 1) {\n if (i >= nucleiCount) { break; }\n let n0 = u.nuc0[i]; // pos.x, pos.y, radius, paletteBias\n let n1 = u.nuc1[i]; // valueBias, driftRadius, driftPhase, elong\n let angle = u.nuc2[i].x;\n let driftRadius = n1.y;\n let driftPhase = n1.z;\n let posI = n0.xy + driftRadius * vec2<f32>(\n cos(t * nucleiDrift * K_NUCLEI + driftPhase),\n sin(t * nucleiDrift * K_NUCLEI + driftPhase * 1.13)\n );\n let diff = p - posI;\n let ca = cos(angle);\n let sa = sin(angle);\n let local = vec2<f32>(ca * diff.x + sa * diff.y, -sa * diff.x + ca * diff.y);\n let along = local.x / max(n1.w, 0.01);\n let across = local.y;\n let d2 = along * along + across * across;\n let r = max(n0.z, 0.01);\n let w = exp(-softmaxBeta * d2 / (r * r));\n accumBias = accumBias + w * n0.w;\n accumValue = accumValue + w * n1.x;\n accumW = accumW + w;\n }\n var paletteId = accumBias / max(accumW, 1e-4);\n let valueMod = accumValue / max(accumW, 1e-4);\n\n let palAmp = clamp(paletteDrift * 6.0, 0.0, 0.06);\n paletteId = paletteId + palAmp * sin(t * paletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n return NucleiResult(paletteId, valueMod);\n}\n\n// \u2500\u2500 Color utils (OKLCh saturation) \u2500\u2500\nfn linOklab(lin: vec3<f32>) -> vec3<f32> {\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\nfn saturate3(c: vec3<f32>, amt: f32) -> vec3<f32> {\n var lch = oklabToOklch(linOklab(c));\n lch.y = max(lch.y * amt, 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3<f32>(0.0));\n}\n\n// BD.W-AUR-VIVIDNESS \u2014 the \u00A73 chroma FLOOR, the WGSL twin of the GLSL vividnessFloor.\n// OKLab chroma lifted toward the floor, hue + L preserved; below VIVID_EPS the (a,b)\n// direction is precision noise so the lift synthesizes along the WARM anchor (amber,\n// both components positive) \u2014 never teal/navy. vividness (scalars3.w) == 0 is a no-op\n// (the byte-identity opt-out). Mode factor lifts a dim field a touch (read through glass).\nconst VIVID_TARGET: f32 = 0.115;\nconst VIVID_EPS: f32 = 0.012;\nconst VIVID_WARM_ANCHOR: vec2<f32> = vec2<f32>(0.34202, 0.93969);\nfn vividnessFloor(c: vec3<f32>, vividness: f32) -> vec3<f32> {\n if (vividness <= 0.0001) { return c; }\n var lab = linOklab(c);\n let C = length(lab.yz);\n let modeLift = mix(1.18, 1.0, clamp(lab.x * 1.4, 0.0, 1.0));\n let Cmin = vividness * VIVID_TARGET * modeLift;\n var hueDir = VIVID_WARM_ANCHOR;\n if (C > VIVID_EPS) { hueDir = lab.yz / C; }\n let yz = hueDir * max(C, Cmin);\n lab = vec3<f32>(lab.x, yz.x, yz.y);\n return max(oklabToLinearSrgb(lab), vec3<f32>(0.0));\n}\n\n// \u2500\u2500 PBR-Neutral tonemap (named aces() in the GLSL \u2014 the tonemap slot) \u2500\u2500\nfn aces(color0: vec3<f32>) -> vec3<f32> {\n let startCompression = 0.8 - 0.04;\n let desaturation = 0.15;\n var color = color0;\n let x = min(color.r, min(color.g, color.b));\n var offset = 0.04;\n if (x < 0.08) { offset = x - 6.25 * x * x; }\n color = color - offset;\n let peak = max(color.r, max(color.g, color.b));\n if (peak < startCompression) { return clamp(color, vec3<f32>(0.0), vec3<f32>(1.0)); }\n let d = 1.0 - startCompression;\n let newPeak = 1.0 - d * d / (peak + d - startCompression);\n color = color * (newPeak / peak);\n let g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);\n return clamp(mix(color, newPeak * vec3<f32>(1.0), g), vec3<f32>(0.0), vec3<f32>(1.0));\n}\n\n// \u2500\u2500 BC.W-VIZ-AURORA (T4) \u2014 the painterly-medium bodies (the keystone WGSL port) \u2500\u2500\n// Spliced HERE, after every helper the mediums consume is defined (domainWarp,\n// nucleiField, samplePalette, linOklab, saturate3, oklabToOklch/oklchToOklab,\n// hash21/vnoise/fbm) and before the vertex/fragment stages. applyMedium(col,p,t)\n// is the dispatch the fragment stage calls.\n\n// \u2500\u2500 Painterly luma + medium scalar accessors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst W_LUMA: vec3<f32> = vec3<f32>(0.2126, 0.7152, 0.0722);\n\nfn mStrokeAmount() -> f32 { return u.scalars4.x; }\nfn mStrokeScale() -> f32 { return u.scalars4.y; }\nfn mStrokeAnisotropy() -> f32 { return u.scalars4.z; }\nfn mCanvasGrain() -> f32 { return u.scalars4.w; }\nfn mWetEdge() -> f32 { return u.scalars5.x; }\nfn mGranulation() -> f32 { return u.scalars5.y; }\nfn mBrokenColor() -> f32 { return u.scalars5.z; }\nfn mKuwaharaQ() -> f32 { return u.scalars5.w; }\nfn mKuwaharaRadius() -> f32 { return u.kuwahara.x; }\n// BG.W-AUR-METAL-FINISH \u2014 the metal-medium knobs ride the FREE cursor.z/.w pad lanes\n// (uniformBridgeWGPU packs them; written 0 on a non-metal config \u2192 no-op). These read\n// the IN-STRUCT cursor \u2014 the metal light is cursor-synthesized so it crosses to WGSL\n// (a phantom uLightDir read would be flat on the primary \u2014 uLightDir is .frag-only).\nfn mMetalPolish() -> f32 { return u.cursor.z; }\nfn mMetalHeightScale() -> f32 { return u.cursor.w; }\n\n// \u2500\u2500 Gradient pack (BG.W-AUR-METAL-FINISH; the WGSL twin) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// structureTensorField re-plumbs its already-computed luma gradient (Gx,Gy) out\n// through the return's .w lane so the metal medium pays ZERO extra taps. 12 bits per\n// bounded component ([-4,4]) into one f32; the crayon/kuwahara callers read only .xy/.z.\nfn packGrad(gx: f32, gy: f32) -> f32 {\n let qx = floor(clamp(gx * 0.125 + 0.5, 0.0, 1.0) * 4095.0);\n let qy = floor(clamp(gy * 0.125 + 0.5, 0.0, 1.0) * 4095.0);\n return qx + qy * 4096.0;\n}\nfn unpackGrad(packed: f32) -> vec2<f32> {\n let qy = floor(packed * (1.0 / 4096.0));\n let qx = packed - qy * 4096.0;\n return (vec2<f32>(qx, qy) * (1.0 / 4095.0) - 0.5) * 8.0;\n}\n\n// \u2500\u2500 Base color re-sample (the edge-mask operator the mediums Sobel/sector-sample) \u2500\u2500\n// The WGSL twin of mediums.glsl.ts sampleBase: warp -> nuclei field -> palette -> value.\nfn sampleBase(p: vec2<f32>, t: f32) -> vec3<f32> {\n let pw = domainWarp(p, t);\n let nf = nucleiField(pw, t);\n var c = samplePalette(nf.paletteId);\n c = c * (1.0 + u.scalars0.z * nf.valueMod);\n return c;\n}\n\n// \u2500\u2500 Broken color (hue + chroma jitter at constant OKLCh L) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn brokenColorJitter(c: vec3<f32>, hueSeed: f32, valueSeed: f32, strength: f32) -> vec3<f32> {\n let amt = clamp(mBrokenColor() * strength, 0.0, 1.0);\n if (amt <= 0.001) { return c; }\n var lch = oklabToOklch(linOklab(c));\n lch.z = 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<f32>(0.0));\n}\n\n// \u2500\u2500 Structure-tensor / edge-tangent-flow (AW.W4.1; the WGSL twin) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Returns vec4(tangent.x, tangent.y, A, packGrad(Gx,Gy)) \u2014 the minor-eigenvector\n// edge-tangent + coherence + the metal medium's packed gradient (BG.W-AUR-METAL-FINISH;\n// the .xy/.z crayon/kuwahara callers are byte-unchanged).\nfn structureTensorField(p: vec2<f32>, t: f32, fallbackDir: vec2<f32>) -> vec4<f32> {\n let e = 0.0035;\n let l00 = dot(sampleBase(p + vec2<f32>(-e, -e), t), W_LUMA);\n let l10 = dot(sampleBase(p + vec2<f32>(0.0, -e), t), W_LUMA);\n let l20 = dot(sampleBase(p + vec2<f32>( e, -e), t), W_LUMA);\n let l01 = dot(sampleBase(p + vec2<f32>(-e, 0.0), t), W_LUMA);\n let l21 = dot(sampleBase(p + vec2<f32>( e, 0.0), t), W_LUMA);\n let l02 = dot(sampleBase(p + vec2<f32>(-e, e), t), W_LUMA);\n let l12 = dot(sampleBase(p + vec2<f32>(0.0, e), t), W_LUMA);\n let l22 = dot(sampleBase(p + vec2<f32>( e, e), t), W_LUMA);\n let Gx = (l20 + 2.0 * l21 + l22) - (l00 + 2.0 * l01 + l02);\n let Gy = (l02 + 2.0 * l12 + l22) - (l00 + 2.0 * l10 + l20);\n let Jxx = Gx * Gx;\n let Jyy = Gy * Gy;\n let Jxy = Gx * Gy;\n let tr = Jxx + Jyy;\n let disc = sqrt(max((Jxx - Jyy) * (Jxx - Jyy) + 4.0 * Jxy * Jxy, 0.0));\n let lambdaMaj = 0.5 * (tr + disc);\n let lambdaMin = 0.5 * (tr - disc);\n var tangent: vec2<f32>;\n if (disc < 1e-7) {\n tangent = fallbackDir;\n } else {\n let theta = 0.5 * atan2(2.0 * Jxy, Jxx - Jyy);\n tangent = vec2<f32>(-sin(theta), cos(theta));\n }\n var A = (lambdaMaj - lambdaMin) / (lambdaMaj + lambdaMin + 1e-6);\n A = clamp(A, 0.0, 1.0);\n if (dot(tangent, fallbackDir) < 0.0) { tangent = -tangent; }\n let blendW = pow(A, 0.28);\n let dir = normalize(mix(fallbackDir, tangent, blendW) + vec2<f32>(1e-6, 1e-6));\n return vec4<f32>(dir, A, packGrad(Gx, Gy));\n}\n\n// \u2500\u2500 Flow field (the WGSL twin \u2014 the simple flow seed the medium fallback dirs use) \u2500\u2500\nfn flowField(p: vec2<f32>, t: f32) -> vec2<f32> {\n // Curl-noise-driven swirl (the medium-default dir; the full radial/swirl/diagonal\n // dispatch rides uniforms the WGSL struct does not carry \u2014 the procedural mediums\n // use this multi/curl seed as the fallback the structure tensor relaxes toward).\n let n = fbm(p * 2.0 + vec2<f32>(t * 0.02, t * 0.02));\n let a = n * 6.2831853;\n var dir = vec2<f32>(cos(a), sin(a));\n // Cursor swirl bias (matches the GLSL flow cursor term \u2014 the steady attraction).\n let cursorStrength = u.scalars3.y;\n let cursorRadius = u.scalars3.z;\n let cursor = u.cursor.xy;\n if (cursorStrength > 0.001) {\n let toCur = cursor - p;\n let d = length(toCur);\n let r = max(cursorRadius, 0.01);\n let w = exp(-(d * d) / (r * r * 0.5));\n let tangent = vec2<f32>(-toCur.y, toCur.x) / max(d, 1e-4);\n let a0 = atan2(dir.y, dir.x);\n let a1 = atan2(tangent.y, tangent.x);\n var da = a1 - a0;\n da = atan2(sin(da), cos(da));\n let aa = a0 + da * w * cursorStrength;\n dir = vec2<f32>(cos(aa), sin(aa));\n }\n return dir;\n}\n\n// \u2500\u2500 Watercolor \u2014 wet-edge cauliflowers + granulation + wash banding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn mediumWatercolor(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n var col = col0;\n let eps = 0.004;\n let cx1 = sampleBase(p + vec2<f32>(eps, 0.0), t);\n let cx2 = sampleBase(p - vec2<f32>(eps, 0.0), t);\n let cy1 = sampleBase(p + vec2<f32>(0.0, eps), t);\n let cy2 = sampleBase(p - vec2<f32>(0.0, eps), t);\n let gx = dot(cx1 - cx2, W_LUMA);\n let gy = dot(cy1 - cy2, W_LUMA);\n let edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n let mask = smoothstep(0.0, 2.5, edge);\n col = col * mix(1.0, 0.78, mask * mWetEdge());\n\n let paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n let pigLoad = 1.0 - dot(col, W_LUMA);\n col = col * (1.0 - mGranulation() * pigLoad * (paper - 0.5));\n\n let band = fbm(vec2<f32>(p.x * 1.5, p.y * 0.4));\n col = col * (1.0 + 0.04 * (band - 0.5));\n return col;\n}\n\n// \u2500\u2500 Pastel \u2014 anisotropic stroke noise along the flow + tooth grain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn mediumPastel(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n var col = col0;\n let flow = flowField(p, t);\n let perp = vec2<f32>(-flow.y, flow.x);\n let strokeScale = mStrokeScale();\n let along = dot(p, flow) * strokeScale;\n var across = dot(p, perp) * strokeScale;\n across = across + 0.03 * (vnoise(p * 260.0) - 0.5);\n\n let aniso = mix(1.0, 0.18, mStrokeAnisotropy());\n let stroke = fbm(vec2<f32>(along * aniso, across));\n col = col * mix(1.0, 0.82 + 0.32 * stroke, mStrokeAmount());\n\n let tooth = vnoise(p * 800.0);\n col = col * (1.0 - 0.08 * mStrokeAmount() * (tooth - 0.5));\n return col;\n}\n\n// \u2500\u2500 Crayon \u2014 DRY wax pigment on paper tooth (the tooth-multiply + scumble) \u2500\u2500\nfn mediumCrayon(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let flow = structureTensorField(p, t, flowField(p, t)).xy;\n let ang = atan2(flow.y, flow.x);\n let ca = cos(-ang);\n let sa = sin(-ang);\n let pr = vec2<f32>(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n let aniso = mix(0.45, 0.95, mStrokeAnisotropy());\n let scale = max(mStrokeScale() * 1.6, 180.0);\n\n let t1 = vnoise(vec2<f32>(pr.x * scale * aniso, pr.y * scale));\n let t2 = vnoise(vec2<f32>(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + vec2<f32>(11.0, 11.0));\n let t3 = vnoise(vec2<f32>(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + vec2<f32>(23.0, 23.0));\n let tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n let pressure = clamp(mStrokeAmount(), 0.0, 1.0);\n let bite = mix(0.18, 0.46, pressure);\n var result = col0 * (1.0 - bite * (0.5 - tooth));\n\n let scumbleMask = smoothstep(0.42, 0.78, vnoise(vec2<f32>(pr.x * scale * 0.55, pr.y * scale * 0.22) + vec2<f32>(7.0, 7.0)));\n let scumbleCoverage = scumbleMask * pressure * 0.7;\n let paper = col0 * mix(0.80, 0.94, pressure);\n result = mix(paper, result, scumbleCoverage);\n\n let pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n let pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + vec2<f32>(19.0, 19.0)));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + vec2<f32>(17.0, 17.0)),\n hash21(pigmentCell * 2.1 + vec2<f32>(31.0, 31.0)),\n 0.45 + 0.55 * pigmentMask\n );\n\n result = saturate3(result, 1.08);\n return result;\n}\n\n// \u2500\u2500 Kuwahara \u2014 the SOFT anisotropic generalized Kuwahara painterly finish \u2500\u2500\u2500\u2500\n// THE KEYSTONE (Kyprianidis 2010). Aurora is a PROCEDURAL field (no input texture) so the\n// operator runs over sampleBase directly: an elliptical kernel oriented along the structure\n// tensor (elongated along the edge-tangent, narrowed across by the anisotropy), 8 OVERLAPPING\n// sectors via SMOOTH gaussian-angular weights, blended by 1/(1+var^q) (the SOFT criterion \u2192\n// no 8-spoke pinwheel by construction). 4 rings \u00D7 8 angular taps = 32 procedural samples.\nfn mediumKuwahara(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let stf = structureTensorField(p, t, flowField(p, t));\n let tangent = stf.xy;\n let A = stf.z;\n // The studio knob drives the patch radius; clamp into the procedural patch band.\n let radius = clamp(mKuwaharaRadius() + mStrokeScale() * 0.00018, 0.006, 0.024);\n let aniso = mix(1.0, 0.34, clamp(A, 0.0, 1.0));\n let ax = tangent;\n let ay = vec2<f32>(-tangent.y, tangent.x);\n\n // Per-sector weighted sums (8 sectors). WGSL has no runtime-indexed local arrays of\n // vectors as cleanly as GLSL, so unroll the sector accumulators explicitly.\n var mSum: array<vec3<f32>, 8>;\n var sSum: array<vec3<f32>, 8>;\n var wSum: array<f32, 8>;\n for (var i = 0; i < 8; i = i + 1) {\n mSum[i] = vec3<f32>(0.0);\n sSum[i] = vec3<f32>(0.0);\n wSum[i] = 0.0;\n }\n\n let RINGS = 4;\n let TAPS = 8;\n for (var r = 1; r <= RINGS; r = r + 1) {\n let rr = f32(r) / f32(RINGS);\n let radial = 1.0 - 0.6 * rr;\n for (var a = 0; a < TAPS; a = a + 1) {\n let ang = 6.2831853 * f32(a) / f32(TAPS);\n let dirE = ax * cos(ang) + ay * (sin(ang) * aniso);\n let off = dirE * (radius * rr);\n let c = sampleBase(p + off, t);\n for (var k = 0; k < 8; k = k + 1) {\n let sectorAng = 6.2831853 * f32(k) / 8.0;\n var d = ang - sectorAng;\n d = atan2(sin(d), cos(d));\n let sw = exp(-d * d * 2.2) * radial;\n mSum[k] = mSum[k] + c * sw;\n sSum[k] = sSum[k] + c * c * sw;\n wSum[k] = wSum[k] + sw;\n }\n }\n }\n\n let q = max(mKuwaharaQ(), 1.0);\n var accum = vec3<f32>(0.0);\n var accumW = 0.0;\n for (var k = 0; k < 8; k = k + 1) {\n let wk = max(wSum[k], 1e-4);\n let mean = mSum[k] / wk;\n let var3 = max(sSum[k] / wk - mean * mean, vec3<f32>(0.0));\n let variance = dot(var3, W_LUMA);\n let sectorW = 1.0 / (1.0 + pow(variance * 320.0, q));\n accum = accum + mean * sectorW;\n accumW = accumW + sectorW;\n }\n var result = accum / max(accumW, 1e-4);\n\n // Anchor in near-flat zones so the smoothing only bites at edges.\n let center = sampleBase(p, t);\n result = mix(center, result, clamp(0.35 + 0.65 * A, 0.0, 1.0));\n\n // Faint canvas tooth + pigment-saturation so the flattened patches read as oil paint.\n let tooth = vnoise(p * 300.0) - 0.5;\n result = result * (1.0 + tooth * 0.06 * mCanvasGrain());\n result = saturate3(result, 1.04);\n\n return mix(col0, result, clamp(mStrokeAmount(), 0.0, 1.0));\n}\n\n// \u2500\u2500 Metal \u2014 the two-term anisotropic BRDF (uMedium==8/9; the WGSL twin) \u2500\u2500\u2500\u2500\u2500\u2500\n// The field re-lights as warm folded metal: the luma HEIGHT field (the packed\n// structure-tensor gradient \u2192 the surface normal) catches an anisotropic Blinn streak\n// (brushed metal, running ALONG the coherent edge-tangent) plus a sharp crest, both\n// raked by the IN-STRUCT cursor-synth light (crosses to WGSL \u2014 never uLightDir). The\n// catch is the achromatic-warm anchor (never a cold chrome-blue). ZERO extra taps \u2014\n// the gradient is unpacked from the tensor's .w lane the caller already computed.\nconst METAL_CATCH_WARM: vec3<f32> = vec3<f32>(1.0, 0.97, 0.90);\nconst METAL_HEIGHT_SCALE: f32 = 2.2;\nconst METAL_LIGHT_Z: f32 = 0.55;\nconst METAL_IDLE_RAKE: vec2<f32> = vec2<f32>(0.32, 0.34);\nconst METAL_SHININESS_ANISO: f32 = 6.0;\nconst METAL_SHININESS_CREST: f32 = 40.0;\nconst METAL_COHERENCE_FLOOR: f32 = 0.22;\nconst METAL_BODY_FLOOR: f32 = 0.42;\nconst METAL_GRADIENT_FLATTEN: f32 = 0.55;\nconst METAL_SPARKLE_DENSITY: f32 = 240.0;\nconst METAL_SPARKLE_THRESH: f32 = 0.72;\nconst METAL_SPARKLE_AMP: f32 = 0.6;\n\nfn metalShade(baseCol: vec3<f32>, p: vec2<f32>, stf: vec4<f32>) -> vec3<f32> {\n let tangent = stf.xy;\n let A = stf.z;\n let grad = unpackGrad(stf.w);\n let N = normalize(vec3<f32>(grad * (METAL_HEIGHT_SCALE * mMetalHeightScale()), 1.0));\n let V = vec3<f32>(0.0, 0.0, 1.0);\n // Cursor-synth rake light (u.cursor is in-struct \u2014 crosses to WGSL).\n let L = normalize(vec3<f32>((u.cursor.xy - p) + METAL_IDLE_RAKE, METAL_LIGHT_Z));\n let H = normalize(L + V);\n // Anisotropic streak \u2014 brushed metal along the edge-tangent.\n let tH = dot(normalize(tangent), H.xy);\n let sinTH = sqrt(clamp(1.0 - tH * tH, 0.0, 1.0));\n let streak = pow(clamp(sinTH, 0.0, 1.0), METAL_SHININESS_ANISO);\n let crest = pow(clamp(dot(N, H), 0.0, 1.0), METAL_SHININESS_CREST);\n var spec = streak * crest;\n spec = spec * smoothstep(0.0, METAL_COHERENCE_FLOOR, A);\n // Technicolor valley base \u2014 the body keeps the FIELD hue; luma rides the relief.\n let diff = clamp(dot(N, L), 0.0, 1.0);\n let body = baseCol * (METAL_BODY_FLOOR + (1.0 - METAL_BODY_FLOOR) * diff);\n return body + METAL_CATCH_WARM * (spec * clamp(mMetalPolish(), 0.0, 4.0));\n}\n\nfn mediumMetal(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let stf = structureTensorField(p, t, flowField(p, t));\n return metalShade(col, p, stf);\n}\n\n// metal-gradient (uMedium==9) \u2014 the SAME BRDF over a pre-flattened base + the\n// twinkle-in-place flake sparkle (position FIXED per cell, phase-animated, facing-gated).\nfn mediumMetalGradient(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let stf = structureTensorField(p, t, flowField(p, t));\n let flat = mix(col, vec3<f32>(dot(col, W_LUMA)), METAL_GRADIENT_FLATTEN);\n var metal = metalShade(flat, p, stf);\n let grad = unpackGrad(stf.w);\n let facing = normalize(vec3<f32>(grad * (METAL_HEIGHT_SCALE * mMetalHeightScale()), 1.0)).z;\n let cell = floor(p * METAL_SPARKLE_DENSITY);\n let seed = hash21(cell);\n let tw = pow(clamp(sin(t + seed * 6.2831853), 0.0, 1.0), 40.0);\n let flakeGate = step(METAL_SPARKLE_THRESH, seed);\n metal = metal + METAL_CATCH_WARM * (tw * flakeGate * clamp(facing, 0.0, 1.0) * METAL_SPARKLE_AMP);\n return metal;\n}\n\n// \u2500\u2500 Medium dispatch (the WGSL twin of the GLSL main() if-ladder) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// uMedium: 0 smooth \u00B7 1 pastel \u00B7 2 watercolor \u00B7 3 oil \u00B7 4 crayon \u00B7 5 vangogh \u00B7\n// 6 oil-pastel \u00B7 7 kuwahara \u00B7 8 metal \u00B7 9 metal-gradient. The cheap peer mediums\n// (pastel/watercolor/crayon) + kuwahara + metal port their own bodies; the\n// oil/vangogh/oil-pastel STROKE cascade renders the anisotropic-Kuwahara PAINTERLY\n// finish here (a real oil-paint read, never the smooth core \u2014 the \"NO FALLBACKS on\n// Safari\" mandate; the full per-dab Starry-Night stroke WGSL is the booked\n// W-AURORA-WGPU-MEDIUMS-STROKES tail).\nfn applyMedium(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let medium = u.ints1.x;\n if (medium == 1) { return mediumPastel(col, p, t); }\n if (medium == 2) { return mediumWatercolor(col, p, t); }\n if (medium == 4) { return mediumCrayon(col, p, t); }\n // BG.W-AUR-METAL-FINISH \u2014 metal DUAL-PORTS (a .frag-only metal is invisible on the\n // WGSL primary of both browsers); 8 metal, 9 metal-gradient.\n if (medium == 8) { return mediumMetal(col, p, t); }\n if (medium == 9) { return mediumMetalGradient(col, p, t); }\n // oil(3) / vangogh(5) / oil-pastel(6) / kuwahara(7) all render the painterly\n // anisotropic-Kuwahara finish on the WGSL primary (the WebGL2 fallback carries the\n // full per-dab stroke cascade for oil/vangogh/oil-pastel).\n if (medium == 3 || medium == 5 || medium == 6 || medium == 7) {\n return mediumKuwahara(col, p, t);\n }\n return col;\n}\n\n\n// \u2500\u2500 Full-screen triangle vertex stage (the pilot idiom \u2014 no vertex buffer) \u2500\u2500\nstruct VSOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // NDC corners of the covering triangle (-1,-1), (3,-1), (-1,3).\n var p = vec2<f32>(-1.0, -1.0);\n if (vi == 1u) { p = vec2<f32>(3.0, -1.0); }\n else if (vi == 2u) { p = vec2<f32>(-1.0, 3.0); }\n var out: VSOut;\n out.pos = vec4<f32>(p, 0.0, 1.0);\n // vUv = aPos * 0.5 + 0.5 (the aurora.vert convention).\n out.uv = p * 0.5 + 0.5;\n return out;\n}\n\n// \u2500\u2500 Fragment stage (the main() transcription) \u2500\u2500\n@fragment\nfn fs_main(in: VSOut) -> @location(0) vec4<f32> {\n let t = u.scalars0.x;\n let valueVariance = u.scalars0.z;\n let breathDepth = u.scalars2.w;\n let breathPeriod = u.scalars3.x;\n let saturation = u.scalars1.z;\n let paperGrain = u.scalars1.w;\n let alpha = u.scalars2.x;\n let vividness = u.scalars3.w;\n\n let uv = in.uv;\n let pN = uv;\n\n let p_warp = domainWarp(pN, t);\n\n let nf = nucleiField(p_warp, t);\n var col = samplePalette(nf.paletteId);\n col = col * (1.0 + valueVariance * nf.valueMod);\n\n // Breath \u2014 slow global luminance wobble.\n let breath = sin(t * 6.2831 / max(breathPeriod, 1.0));\n col = col * (1.0 + breathDepth * breath * 0.5);\n\n // BC.W-VIZ-AURORA (T4) \u2014 the painterly-medium dispatch. uMedium 0 (smooth) is a\n // no-op pass-through so the DEFAULT smooth config is byte-identical to the prior\n // WGSL primary (the parity capture); uMedium 1-7 render the ported painterly\n // bodies (pastel/watercolor/crayon/kuwahara) or the anisotropic-Kuwahara finish\n // (oil/vangogh/oil-pastel) \u2014 never a silent smooth degrade on Safari 26.\n col = applyMedium(col, pN, t);\n\n // Saturation trim.\n col = saturate3(col, saturation);\n\n // BD.W-AUR-VIVIDNESS \u2014 the \u00A73 chroma floor (warm-anchored, hue-preserving). The WGSL\n // twin of the GLSL call; vividness:0 is a no-op so the smooth/pale default stays gated.\n col = vividnessFloor(col, vividness);\n\n // Tonemap + film grain.\n col = aces(col);\n // gl_FragCoord.xy \u2192 the device-pixel position (the WGSL @builtin(position)).\n let fragCoord = in.pos.xy;\n let grain = hash21(fragCoord + t * 17.0);\n col = col + (grain - 0.5) * paperGrain;\n\n col = clamp(col * 0.985 + 0.008, vec3<f32>(0.0), vec3<f32>(1.0));\n\n // MANDATORY OETF \u2014 closes the seam (the whole pipeline above runs in LINEAR).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF.\n col = col + (1.0 / 255.0) * (ign(fragCoord) - 0.5);\n\n // Premultiply on DISPLAY-space color (matches the WebGL2 path: OETF, then premul).\n return vec4<f32>(col * alpha, alpha);\n}\n";
1
+ export declare const AURORA_WGSL = "\nconst MAX_NUCLEI: i32 = 8;\nconst MAX_STOPS: i32 = 8;\nconst PI: f32 = 3.141592653589793;\n\n// \u2500\u2500 Uniforms (the typed-struct source-of-truth \u2014 see aurora.wgsl.uniforms.ts) \u2500\u2500\n// Scalars are packed into vec4 lanes to keep the std140/WGSL 16-byte struct alignment\n// trivial. nuc0[i] = (pos.x, pos.y, radius, paletteBias); nuc1[i] = (valueBias,\n// driftRadius, driftPhase, elong); nuc2[i] = (angle, _, _, _). palette[i].rgb is the\n// CPU-baked linear-sRGB stop.\nstruct Uniforms {\n // scalars0: (uTime, uSoftmaxBeta, uValueVariance, uWarpAmount)\n scalars0: vec4<f32>,\n // scalars1: (uWarpScale, uWarpDrift, uSaturation, uPaperGrain)\n scalars1: vec4<f32>,\n // scalars2: (uAlpha, uNucleiDrift, uPaletteDrift, uBreathDepth)\n scalars2: vec4<f32>,\n // scalars3: (uBreathPeriod, uCursorStrength, uCursorRadius, uVividness)\n // The .w pad lane now carries the chroma-floor strength.\n scalars3: vec4<f32>,\n // cursor: (uCursor.x, uCursor.y, uMetalPolish, uMetalHeightScale)\n // The two free pad lanes carry the metal-medium knobs (the\n // metal light is cursor-synthesized so it reads in-struct on the WGSL primary too).\n cursor: vec4<f32>,\n // ints: (uStopCount, uNucleiCount, uWarpMode, uNoiseOctaves)\n ints0: vec4<i32>,\n // ints1: (uMedium, uHuePath, _, _)\n ints1: vec4<i32>,\n palette: array<vec4<f32>, 8>,\n nuc0: array<vec4<f32>, 8>,\n nuc1: array<vec4<f32>, 8>,\n nuc2: array<vec4<f32>, 8>,\n // The painterly-medium scalar lanes are appended after the\n // arrays so EVERY existing offset (and the smooth-default parity capture) is\n // byte-identical (these lanes are written 0 on a smooth config). scalars4:\n // (uStrokeAmount, uStrokeScale, uStrokeAnisotropy, uCanvasGrain). scalars5:\n // (uWetEdge, uGranulation, uBrokenColor, uKuwaharaQ). kuwahara: (uKuwaharaRadius,\n // uKuwaharaSectors, _, _) \u2014 the Kuwahara radius/sectors/q in lockstep with\n // packAuroraWGPUUniforms (the typed-struct discipline).\n scalars4: vec4<f32>,\n scalars5: vec4<f32>,\n kuwahara: vec4<f32>,\n};\n\n@group(0) @binding(0) var<uniform> u: Uniforms;\n\n// \u2500\u2500 Authoring-scale \u2192 rad/sec lifts (byte-identical to aurora.frag.ts) \u2500\u2500\nconst K_NUCLEI: f32 = 14.0;\nconst K_PAL: f32 = 24.0;\nconst K_WARP: f32 = 5.0;\n\n// \u2500\u2500 Noise (Aurora-local hash21/vnoise/fbm, deliberately distinct) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn hash21(p0: vec2<f32>) -> f32 {\n var p = fract(p0 * vec2<f32>(123.34, 456.21));\n p = p + dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\n\nfn hash22(p0: vec2<f32>) -> vec2<f32> {\n let p = vec2<f32>(dot(p0, vec2<f32>(127.1, 311.7)),\n dot(p0, vec2<f32>(269.5, 183.3)));\n return fract(sin(p) * 43758.5453);\n}\n\nfn vnoise(p: vec2<f32>) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let uu = f * f * (3.0 - 2.0 * f);\n let a = hash21(i);\n let b = hash21(i + vec2<f32>(1.0, 0.0));\n let c = hash21(i + vec2<f32>(0.0, 1.0));\n let d = hash21(i + vec2<f32>(1.0, 1.0));\n return mix(mix(a, b, uu.x), mix(c, d, uu.x), uu.y);\n}\n\nconst FBM_ROT: mat2x2<f32> = mat2x2<f32>(0.8, 0.6, -0.6, 0.8);\n\nfn fbm(p0: vec2<f32>) -> f32 {\n var v = 0.0;\n var a = 0.5;\n var p = p0;\n let octaves = u.ints0.w;\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= octaves) { break; }\n v = v + a * vnoise(p);\n p = FBM_ROT * p * 2.02;\n a = a * 0.5;\n }\n return v;\n}\n\nfn potentialFBM(p: vec2f) -> f32 { return fbm(p); }\n\nconst CURL_EPS: f32 = 0.012;\n\n// \u2207\u00D7\u03C8 in 2D \u2014 the divergence-free flow direction. Central finite differences of the\n// scalar potential give the partials; the cross-pairing (\u2202y, \u2212\u2202x) is the 2D curl.\nfn curlFBM(p: vec2f) -> vec2f {\n let dx = potentialFBM(p + vec2f(CURL_EPS, 0.0)) - potentialFBM(p - vec2f(CURL_EPS, 0.0));\n let dy = potentialFBM(p + vec2f(0.0, CURL_EPS)) - potentialFBM(p - vec2f(0.0, CURL_EPS));\n // \u03C8-gradient is (dx, dy)/(2\u00B7eps); the curl rotates it 90\u00B0: (\u2202\u03C8/\u2202y, \u2212\u2202\u03C8/\u2202x).\n let g = vec2f(dx, dy) / (2.0 * CURL_EPS);\n return vec2f(g.y, -g.x);\n}\n\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n if (c <= 0.04045) { return c / 12.92; }\n return pow((c + 0.055) / 1.055, 2.4);\n}\nfn srgbToLinear(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfn linearToSrgbCh(c: f32) -> f32 {\n if (c <= 0.0031308) { return c * 12.92; }\n return 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nfn linearToSrgb(c: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\n\n// value.js LINEAR_SRGB_TO_LMS, row-major \u2192 WGSL columns (= the transpose).\nconst LINEAR_SRGB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\n\n// value.js srgbToOKLab's INLINE LMS\u2192OKLab coefficients, row-major \u2192 WGSL columns.\nconst LMS_TO_OKLAB: mat3x3<f32> = mat3x3<f32>(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\n\n// value.js OKLAB_TO_LMS_COEFF, row-major \u2192 WGSL columns.\nconst OKLAB_TO_LMS: mat3x3<f32> = mat3x3<f32>(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\n\n// value.js LMS_TO_LINEAR_SRGB, row-major \u2192 WGSL columns.\nconst LMS_TO_LINEAR_SRGB: mat3x3<f32> = mat3x3<f32>(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn cbrt3(v: vec3<f32>) -> vec3<f32> {\n return sign(v) * pow(abs(v), vec3<f32>(1.0 / 3.0));\n}\n\n// Gamma sRGB \u2192 raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nfn srgbToOklab(c: vec3<f32>) -> vec3<f32> {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\n// Raw OKLab (L, a, b) \u2192 linear sRGB. Mirrors value.js oklabToLinearSRGB.\nfn oklabToLinearSrgb(lab: vec3<f32>) -> vec3<f32> {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab \u2192 OKLCh: H in RADIANS, folded to [0, 2pi). Mirrors value.js rawOklabToOklch.\nfn oklabToOklch(lab: vec3<f32>) -> vec3<f32> {\n let C = length(lab.yz);\n var H = atan2(lab.z, lab.y);\n if (H < 0.0) { H = H + 2.0 * PI; }\n return vec3<f32>(lab.x, C, H);\n}\n\n// OKLCh (H radians) \u2192 OKLab. Mirrors value.js rawOklchToOklab.\nfn oklchToOklab(lch: vec3<f32>) -> vec3<f32> {\n return vec3<f32>(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\n// value.js interpolateHue in the NORMALIZED-TURNS domain. methods: 0 shorter,\n// 1 longer, 2 increasing, 3 decreasing.\nfn interpolateHueTurns(h0: f32, h1: f32, t: f32, method: i32) -> f32 {\n let TAU = 2.0 * PI;\n var a = h0 / TAU;\n var b = h1 / TAU;\n let i = b - a;\n if (method == 0) { // shorter\n if (i > 0.5) { a = a + 1.0; } else if (i < -0.5) { b = b + 1.0; }\n } else if (method == 1) { // longer\n if (i > 0.0 && i < 0.5) { a = a + 1.0; } else if (i > -0.5 && i <= 0.0) { b = b + 1.0; }\n } else if (method == 2) { // increasing\n if (i < 0.0) { b = b + 1.0; }\n } else { // decreasing\n if (i > 0.0) { a = a + 1.0; }\n }\n var r = a + t * (b - a);\n r = fract(r); // fract is non-negative in WGSL\n return r * TAU; // back to radians\n}\n\n// Linear-sRGB endpoint pair \u2192 OKLab-rectangular interpolation \u2192 linear sRGB.\nfn mixPaletteOklab(linA: vec3<f32>, linB: vec3<f32>, t: f32) -> vec3<f32> {\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linA);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linB);\n let lab = mix(labA, labB, t);\n return oklabToLinearSrgb(lab);\n}\n\n// Linear-sRGB endpoint pair \u2192 OKLCh hue-arc interpolation \u2192 linear sRGB.\nfn mixPaletteOklchArc(linA: vec3<f32>, linB: vec3<f32>, t: f32, method: i32) -> vec3<f32> {\n let labA = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linA);\n let labB = LMS_TO_OKLAB * cbrt3(LINEAR_SRGB_TO_LMS * linB);\n let lchA = oklabToOklch(labA);\n let lchB = oklabToOklch(labB);\n let L = mix(lchA.x, lchB.x, t);\n let C = mix(lchA.y, lchB.y, t);\n let H = interpolateHueTurns(lchA.z, lchB.z, t, method);\n return oklabToLinearSrgb(oklchToOklab(vec3<f32>(L, C, H)));\n}\n\n// The ramp dispatcher (the samplePalette splices it). tRaw is the raw inter-stop\n// parameter [0,1]; the smoothstep ease + the huePath branch live here so the ramp is\n// one source. increasing(2)/decreasing(3) take the OKLCh hue-arc; every other method\n// takes the OKLab-rectangular straight line.\nfn samplePaletteRamp(a: vec3<f32>, b: vec3<f32>, tRaw: f32, huePath: i32) -> vec3<f32> {\n let t = smoothstep(0.0, 1.0, tRaw);\n if (huePath == 2 || huePath == 3) {\n return mixPaletteOklchArc(a, b, t, huePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// Interleaved Gradient Noise (Jimenez) \u2014 display-space 1-LSB dither.\nfn ign(p: vec2<f32>) -> f32 {\n return fract(52.9829189 * fract(dot(p, vec2<f32>(0.06711056, 0.00583715))));\n}\n\n// Cellular, Worley f1.\nfn cellular(p: vec2<f32>) -> f32 {\n let i = floor(p);\n let f = fract(p);\n var m = 1e9;\n for (var y = -1; y <= 1; y = y + 1) {\n for (var x = -1; x <= 1; x = x + 1) {\n let g = vec2<f32>(f32(x), f32(y));\n let o = hash22(i + g);\n let r = g + o - f;\n m = min(m, dot(r, r));\n }\n }\n return sqrt(m);\n}\n\n// \u2500\u2500 Warp (Quilez double warp + the four warp modes + the cursor swirl) \u2500\u2500\nfn domainWarp(p: vec2<f32>, t: f32) -> vec2<f32> {\n let warpScale = u.scalars1.x;\n let warpDrift = u.scalars1.y;\n let warpAmount = u.scalars0.w;\n let warpMode = u.ints0.z;\n let cursorStrength = u.scalars3.y;\n let cursorRadius = u.scalars3.z;\n let cursor = u.cursor.xy;\n\n let q = vec2<f32>(fbm(p * warpScale + vec2<f32>(0.0, 0.0) + t * warpDrift * K_WARP),\n fbm(p * warpScale + vec2<f32>(5.2, 1.3) + t * warpDrift * K_WARP));\n let r = vec2<f32>(fbm(p * warpScale + 4.0 * q + vec2<f32>(1.7, 9.2)),\n fbm(p * warpScale + 4.0 * q + vec2<f32>(8.3, 2.8)));\n\n var warp = r;\n if (warpMode == 1) {\n // cellular \u2014 chunky territories\n let c1 = cellular(p * warpScale * 1.5 + vec2<f32>(t * warpDrift * K_WARP * 2.0, 0.0));\n let c2 = cellular(p * warpScale * 1.5 + vec2<f32>(11.0, 7.0 + t * warpDrift * K_WARP * 2.0));\n warp = vec2<f32>(c1, c2);\n } else if (warpMode == 2) {\n // hybrid \u2014 fbm + cellular averaged\n let c1 = cellular(p * warpScale * 1.2);\n let c2 = cellular(p * warpScale * 1.2 + vec2<f32>(11.0, 7.0));\n warp = mix(r, vec2<f32>(c1, c2), 0.5);\n } else if (warpMode == 3) {\n let fp = p * warpScale + vec2<f32>(t * warpDrift * K_WARP);\n warp = curlFBM(fp);\n }\n var warped = p + warpAmount * warp;\n\n // Cursor swirl \u2014 rotate p around the cursor with radial falloff.\n if (cursorStrength > 0.001) {\n let toP = p - cursor;\n let d = length(toP);\n let rr = max(cursorRadius, 0.01);\n let w = exp(-(d * d) / (rr * rr * 0.45));\n let ang = w * cursorStrength * 2.1;\n let ca = cos(ang);\n let sa = sin(ang);\n var rotated = vec2<f32>(ca * toP.x - sa * toP.y, sa * toP.x + ca * toP.y) + cursor;\n let pinch = w * cursorStrength * 0.08;\n rotated = mix(rotated, cursor, pinch);\n warped = mix(warped, rotated + warpAmount * warp * 0.7, w * cursorStrength);\n }\n return warped;\n}\n\n// \u2500\u2500 Palette LUT \u2500\u2500\nfn samplePalette(id0: f32) -> vec3<f32> {\n let id = clamp(id0, 0.0, 1.0);\n let stopCount = u.ints0.x;\n let scaled = id * f32(stopCount - 1);\n let i0 = i32(floor(scaled));\n let i1 = min(i0 + 1, stopCount - 1);\n let t = fract(scaled);\n return samplePaletteRamp(u.palette[i0].rgb, u.palette[i1].rgb, t, u.ints1.y);\n}\n\n// \u2500\u2500 Nuclei field (anisotropic Gaussian softmax + drift + palette drift) \u2500\u2500\nstruct NucleiResult { paletteId: f32, valueMod: f32 };\n\nfn nucleiField(p: vec2<f32>, t: f32) -> NucleiResult {\n let nucleiCount = u.ints0.y;\n let nucleiDrift = u.scalars2.y;\n let paletteDrift = u.scalars2.z;\n let softmaxBeta = u.scalars0.y;\n\n var accumBias = 0.0;\n var accumValue = 0.0;\n var accumW = 0.0;\n for (var i = 0; i < MAX_NUCLEI; i = i + 1) {\n if (i >= nucleiCount) { break; }\n let n0 = u.nuc0[i]; // pos.x, pos.y, radius, paletteBias\n let n1 = u.nuc1[i]; // valueBias, driftRadius, driftPhase, elong\n let angle = u.nuc2[i].x;\n let driftRadius = n1.y;\n let driftPhase = n1.z;\n let posI = n0.xy + driftRadius * vec2<f32>(\n cos(t * nucleiDrift * K_NUCLEI + driftPhase),\n sin(t * nucleiDrift * K_NUCLEI + driftPhase * 1.13)\n );\n let diff = p - posI;\n let ca = cos(angle);\n let sa = sin(angle);\n let local = vec2<f32>(ca * diff.x + sa * diff.y, -sa * diff.x + ca * diff.y);\n let along = local.x / max(n1.w, 0.01);\n let across = local.y;\n let d2 = along * along + across * across;\n let r = max(n0.z, 0.01);\n let w = exp(-softmaxBeta * d2 / (r * r));\n accumBias = accumBias + w * n0.w;\n accumValue = accumValue + w * n1.x;\n accumW = accumW + w;\n }\n var paletteId = accumBias / max(accumW, 1e-4);\n let valueMod = accumValue / max(accumW, 1e-4);\n\n let palAmp = clamp(paletteDrift * 6.0, 0.0, 0.06);\n paletteId = paletteId + palAmp * sin(t * paletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n return NucleiResult(paletteId, valueMod);\n}\n\n// \u2500\u2500 Color utils (OKLCh saturation) \u2500\u2500\nfn linOklab(lin: vec3<f32>) -> vec3<f32> {\n let lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * cbrt3(lms);\n}\n\nfn saturate3(c: vec3<f32>, amt: f32) -> vec3<f32> {\n var lch = oklabToOklch(linOklab(c));\n lch.y = max(lch.y * amt, 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3<f32>(0.0));\n}\n\n// The chroma floor, the WGSL twin of the GLSL vividnessFloor.\n// OKLab chroma lifted toward the floor, hue + L preserved; below VIVID_EPS the (a,b)\n// direction is precision noise so the lift synthesizes along the WARM anchor (amber,\n// both components positive) \u2014 never teal/navy. vividness (scalars3.w) == 0 is a no-op\n// (the byte-identity opt-out). Mode factor lifts a dim field a touch (read through glass).\nconst VIVID_TARGET: f32 = 0.115;\nconst VIVID_EPS: f32 = 0.012;\nconst VIVID_WARM_ANCHOR: vec2<f32> = vec2<f32>(0.34202, 0.93969);\nfn vividnessFloor(c: vec3<f32>, vividness: f32) -> vec3<f32> {\n if (vividness <= 0.0001) { return c; }\n var lab = linOklab(c);\n let C = length(lab.yz);\n let modeLift = mix(1.18, 1.0, clamp(lab.x * 1.4, 0.0, 1.0));\n let Cmin = vividness * VIVID_TARGET * modeLift;\n var hueDir = VIVID_WARM_ANCHOR;\n if (C > VIVID_EPS) { hueDir = lab.yz / C; }\n let yz = hueDir * max(C, Cmin);\n lab = vec3<f32>(lab.x, yz.x, yz.y);\n return max(oklabToLinearSrgb(lab), vec3<f32>(0.0));\n}\n\n// \u2500\u2500 PBR-Neutral tonemap (named aces() in the GLSL \u2014 the tonemap slot) \u2500\u2500\nfn aces(color0: vec3<f32>) -> vec3<f32> {\n let startCompression = 0.8 - 0.04;\n let desaturation = 0.15;\n var color = color0;\n let x = min(color.r, min(color.g, color.b));\n var offset = 0.04;\n if (x < 0.08) { offset = x - 6.25 * x * x; }\n color = color - offset;\n let peak = max(color.r, max(color.g, color.b));\n if (peak < startCompression) { return clamp(color, vec3<f32>(0.0), vec3<f32>(1.0)); }\n let d = 1.0 - startCompression;\n let newPeak = 1.0 - d * d / (peak + d - startCompression);\n color = color * (newPeak / peak);\n let g = 1.0 - 1.0 / (desaturation * (peak - newPeak) + 1.0);\n return clamp(mix(color, newPeak * vec3<f32>(1.0), g), vec3<f32>(0.0), vec3<f32>(1.0));\n}\n\n// \u2500\u2500 Painterly-medium bodies (the keystone WGSL port) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Spliced HERE, after every helper the mediums consume is defined (domainWarp,\n// nucleiField, samplePalette, linOklab, saturate3, oklabToOklch/oklchToOklab,\n// hash21/vnoise/fbm) and before the vertex/fragment stages. applyMedium(col,p,t)\n// is the dispatch the fragment stage calls.\n\n// \u2500\u2500 Painterly luma + medium scalar accessors \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nconst W_LUMA: vec3<f32> = vec3<f32>(0.2126, 0.7152, 0.0722);\n\nfn mStrokeAmount() -> f32 { return u.scalars4.x; }\nfn mStrokeScale() -> f32 { return u.scalars4.y; }\nfn mStrokeAnisotropy() -> f32 { return u.scalars4.z; }\nfn mCanvasGrain() -> f32 { return u.scalars4.w; }\nfn mWetEdge() -> f32 { return u.scalars5.x; }\nfn mGranulation() -> f32 { return u.scalars5.y; }\nfn mBrokenColor() -> f32 { return u.scalars5.z; }\nfn mKuwaharaQ() -> f32 { return u.scalars5.w; }\nfn mKuwaharaRadius() -> f32 { return u.kuwahara.x; }\n// The metal-medium knobs ride the free cursor.z/.w pad lanes\n// (uniformBridgeWGPU packs them; written 0 on a non-metal config \u2192 no-op). These read\n// the IN-STRUCT cursor \u2014 the metal light is cursor-synthesized so it crosses to WGSL\n// (a phantom uLightDir read would be flat on the primary; uLightDir is fragment-only).\nfn mMetalPolish() -> f32 { return u.cursor.z; }\nfn mMetalHeightScale() -> f32 { return u.cursor.w; }\n\n// \u2500\u2500 Gradient pack (the WGSL twin) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// structureTensorField re-plumbs its already-computed luma gradient (Gx,Gy) out\n// through the return value's .w lane so the metal medium pays ZERO extra taps. 12 bits per\n// bounded component ([-4,4]) fit into one f32; the crayon/kuwahara callers read only .xy/.z.\nfn packGrad(gx: f32, gy: f32) -> f32 {\n let qx = floor(clamp(gx * 0.125 + 0.5, 0.0, 1.0) * 4095.0);\n let qy = floor(clamp(gy * 0.125 + 0.5, 0.0, 1.0) * 4095.0);\n return qx + qy * 4096.0;\n}\nfn unpackGrad(packed: f32) -> vec2<f32> {\n let qy = floor(packed * (1.0 / 4096.0));\n let qx = packed - qy * 4096.0;\n return (vec2<f32>(qx, qy) * (1.0 / 4095.0) - 0.5) * 8.0;\n}\n\n// \u2500\u2500 Base color re-sample (the edge-mask operator the mediums Sobel/sector-sample) \u2500\u2500\n// The WGSL twin of mediums.glsl.ts sampleBase: warp -> nuclei field -> palette -> value.\nfn sampleBase(p: vec2<f32>, t: f32) -> vec3<f32> {\n let pw = domainWarp(p, t);\n let nf = nucleiField(pw, t);\n var c = samplePalette(nf.paletteId);\n c = c * (1.0 + u.scalars0.z * nf.valueMod);\n return c;\n}\n\n// \u2500\u2500 Broken color (hue + chroma jitter at constant OKLCh L) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn brokenColorJitter(c: vec3<f32>, hueSeed: f32, valueSeed: f32, strength: f32) -> vec3<f32> {\n let amt = clamp(mBrokenColor() * strength, 0.0, 1.0);\n if (amt <= 0.001) { return c; }\n var lch = oklabToOklch(linOklab(c));\n lch.z = 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<f32>(0.0));\n}\n\n// \u2500\u2500 Structure tensor / edge-tangent flow (the WGSL twin) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Returns vec4(tangent.x, tangent.y, A, packGrad(Gx,Gy)) \u2014 the minor-eigenvector\n// edge-tangent + coherence + the metal medium's packed gradient. The .xy/.z\n// crayon/kuwahara callers are byte-unchanged.\nfn structureTensorField(p: vec2<f32>, t: f32, fallbackDir: vec2<f32>) -> vec4<f32> {\n let e = 0.0035;\n let l00 = dot(sampleBase(p + vec2<f32>(-e, -e), t), W_LUMA);\n let l10 = dot(sampleBase(p + vec2<f32>(0.0, -e), t), W_LUMA);\n let l20 = dot(sampleBase(p + vec2<f32>( e, -e), t), W_LUMA);\n let l01 = dot(sampleBase(p + vec2<f32>(-e, 0.0), t), W_LUMA);\n let l21 = dot(sampleBase(p + vec2<f32>( e, 0.0), t), W_LUMA);\n let l02 = dot(sampleBase(p + vec2<f32>(-e, e), t), W_LUMA);\n let l12 = dot(sampleBase(p + vec2<f32>(0.0, e), t), W_LUMA);\n let l22 = dot(sampleBase(p + vec2<f32>( e, e), t), W_LUMA);\n let Gx = (l20 + 2.0 * l21 + l22) - (l00 + 2.0 * l01 + l02);\n let Gy = (l02 + 2.0 * l12 + l22) - (l00 + 2.0 * l10 + l20);\n let Jxx = Gx * Gx;\n let Jyy = Gy * Gy;\n let Jxy = Gx * Gy;\n let tr = Jxx + Jyy;\n let disc = sqrt(max((Jxx - Jyy) * (Jxx - Jyy) + 4.0 * Jxy * Jxy, 0.0));\n let lambdaMaj = 0.5 * (tr + disc);\n let lambdaMin = 0.5 * (tr - disc);\n var tangent: vec2<f32>;\n if (disc < 1e-7) {\n tangent = fallbackDir;\n } else {\n let theta = 0.5 * atan2(2.0 * Jxy, Jxx - Jyy);\n tangent = vec2<f32>(-sin(theta), cos(theta));\n }\n var A = (lambdaMaj - lambdaMin) / (lambdaMaj + lambdaMin + 1e-6);\n A = clamp(A, 0.0, 1.0);\n if (dot(tangent, fallbackDir) < 0.0) { tangent = -tangent; }\n let blendW = pow(A, 0.28);\n let dir = normalize(mix(fallbackDir, tangent, blendW) + vec2<f32>(1e-6, 1e-6));\n return vec4<f32>(dir, A, packGrad(Gx, Gy));\n}\n\n// \u2500\u2500 Flow field (the WGSL twin \u2014 the simple flow seed the medium fallback dirs use) \u2500\u2500\nfn flowField(p: vec2<f32>, t: f32) -> vec2<f32> {\n // Curl-noise-driven swirl (the medium-default dir; the full radial/swirl/diagonal\n // dispatch rides uniforms the WGSL struct does not carry \u2014 the procedural mediums\n // use this multi/curl seed as the fallback the structure tensor relaxes toward).\n let n = fbm(p * 2.0 + vec2<f32>(t * 0.02, t * 0.02));\n let a = n * 6.2831853;\n var dir = vec2<f32>(cos(a), sin(a));\n // Cursor swirl bias (matches the GLSL flow cursor term \u2014 the steady attraction).\n let cursorStrength = u.scalars3.y;\n let cursorRadius = u.scalars3.z;\n let cursor = u.cursor.xy;\n if (cursorStrength > 0.001) {\n let toCur = cursor - p;\n let d = length(toCur);\n let r = max(cursorRadius, 0.01);\n let w = exp(-(d * d) / (r * r * 0.5));\n let tangent = vec2<f32>(-toCur.y, toCur.x) / max(d, 1e-4);\n let a0 = atan2(dir.y, dir.x);\n let a1 = atan2(tangent.y, tangent.x);\n var da = a1 - a0;\n da = atan2(sin(da), cos(da));\n let aa = a0 + da * w * cursorStrength;\n dir = vec2<f32>(cos(aa), sin(aa));\n }\n return dir;\n}\n\n// \u2500\u2500 Watercolor \u2014 wet-edge cauliflowers + granulation + wash banding \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn mediumWatercolor(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n var col = col0;\n let eps = 0.004;\n let cx1 = sampleBase(p + vec2<f32>(eps, 0.0), t);\n let cx2 = sampleBase(p - vec2<f32>(eps, 0.0), t);\n let cy1 = sampleBase(p + vec2<f32>(0.0, eps), t);\n let cy2 = sampleBase(p - vec2<f32>(0.0, eps), t);\n let gx = dot(cx1 - cx2, W_LUMA);\n let gy = dot(cy1 - cy2, W_LUMA);\n let edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n let mask = smoothstep(0.0, 2.5, edge);\n col = col * mix(1.0, 0.78, mask * mWetEdge());\n\n let paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n let pigLoad = 1.0 - dot(col, W_LUMA);\n col = col * (1.0 - mGranulation() * pigLoad * (paper - 0.5));\n\n let band = fbm(vec2<f32>(p.x * 1.5, p.y * 0.4));\n col = col * (1.0 + 0.04 * (band - 0.5));\n return col;\n}\n\n// \u2500\u2500 Pastel \u2014 anisotropic stroke noise along the flow + tooth grain \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfn mediumPastel(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n var col = col0;\n let flow = flowField(p, t);\n let perp = vec2<f32>(-flow.y, flow.x);\n let strokeScale = mStrokeScale();\n let along = dot(p, flow) * strokeScale;\n var across = dot(p, perp) * strokeScale;\n across = across + 0.03 * (vnoise(p * 260.0) - 0.5);\n\n let aniso = mix(1.0, 0.18, mStrokeAnisotropy());\n let stroke = fbm(vec2<f32>(along * aniso, across));\n col = col * mix(1.0, 0.82 + 0.32 * stroke, mStrokeAmount());\n\n let tooth = vnoise(p * 800.0);\n col = col * (1.0 - 0.08 * mStrokeAmount() * (tooth - 0.5));\n return col;\n}\n\n// \u2500\u2500 Crayon \u2014 DRY wax pigment on paper tooth (the tooth-multiply + scumble) \u2500\u2500\nfn mediumCrayon(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let flow = structureTensorField(p, t, flowField(p, t)).xy;\n let ang = atan2(flow.y, flow.x);\n let ca = cos(-ang);\n let sa = sin(-ang);\n let pr = vec2<f32>(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n let aniso = mix(0.45, 0.95, mStrokeAnisotropy());\n let scale = max(mStrokeScale() * 1.6, 180.0);\n\n let t1 = vnoise(vec2<f32>(pr.x * scale * aniso, pr.y * scale));\n let t2 = vnoise(vec2<f32>(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + vec2<f32>(11.0, 11.0));\n let t3 = vnoise(vec2<f32>(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + vec2<f32>(23.0, 23.0));\n let tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n\n let pressure = clamp(mStrokeAmount(), 0.0, 1.0);\n let bite = mix(0.18, 0.46, pressure);\n var result = col0 * (1.0 - bite * (0.5 - tooth));\n\n let scumbleMask = smoothstep(0.42, 0.78, vnoise(vec2<f32>(pr.x * scale * 0.55, pr.y * scale * 0.22) + vec2<f32>(7.0, 7.0)));\n let scumbleCoverage = scumbleMask * pressure * 0.7;\n let paper = col0 * mix(0.80, 0.94, pressure);\n result = mix(paper, result, scumbleCoverage);\n\n let pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n let pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + vec2<f32>(19.0, 19.0)));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + vec2<f32>(17.0, 17.0)),\n hash21(pigmentCell * 2.1 + vec2<f32>(31.0, 31.0)),\n 0.45 + 0.55 * pigmentMask\n );\n\n result = saturate3(result, 1.08);\n return result;\n}\n\n// \u2500\u2500 Kuwahara \u2014 the SOFT anisotropic generalized Kuwahara painterly finish \u2500\u2500\u2500\u2500\n// THE KEYSTONE (Kyprianidis 2010). Aurora is a PROCEDURAL field (no input texture) so the\n// operator runs over sampleBase directly: an elliptical kernel oriented along the structure\n// tensor (elongated along the edge-tangent, narrowed across by the anisotropy), 8 OVERLAPPING\n// sectors via SMOOTH gaussian-angular weights, blended by 1/(1+var^q) (the SOFT criterion \u2192\n// no 8-spoke pinwheel by construction). 4 rings \u00D7 8 angular taps = 32 procedural samples.\nfn mediumKuwahara(col0: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let stf = structureTensorField(p, t, flowField(p, t));\n let tangent = stf.xy;\n let A = stf.z;\n // The studio knob drives the patch radius; clamp into the procedural patch band.\n let radius = clamp(mKuwaharaRadius() + mStrokeScale() * 0.00018, 0.006, 0.024);\n let aniso = mix(1.0, 0.34, clamp(A, 0.0, 1.0));\n let ax = tangent;\n let ay = vec2<f32>(-tangent.y, tangent.x);\n\n // Per-sector weighted sums (8 sectors). WGSL has no runtime-indexed local arrays of\n // vectors as cleanly as GLSL, so unroll the sector accumulators explicitly.\n var mSum: array<vec3<f32>, 8>;\n var sSum: array<vec3<f32>, 8>;\n var wSum: array<f32, 8>;\n for (var i = 0; i < 8; i = i + 1) {\n mSum[i] = vec3<f32>(0.0);\n sSum[i] = vec3<f32>(0.0);\n wSum[i] = 0.0;\n }\n\n let RINGS = 4;\n let TAPS = 8;\n for (var r = 1; r <= RINGS; r = r + 1) {\n let rr = f32(r) / f32(RINGS);\n let radial = 1.0 - 0.6 * rr;\n for (var a = 0; a < TAPS; a = a + 1) {\n let ang = 6.2831853 * f32(a) / f32(TAPS);\n let dirE = ax * cos(ang) + ay * (sin(ang) * aniso);\n let off = dirE * (radius * rr);\n let c = sampleBase(p + off, t);\n for (var k = 0; k < 8; k = k + 1) {\n let sectorAng = 6.2831853 * f32(k) / 8.0;\n var d = ang - sectorAng;\n d = atan2(sin(d), cos(d));\n let sw = exp(-d * d * 2.2) * radial;\n mSum[k] = mSum[k] + c * sw;\n sSum[k] = sSum[k] + c * c * sw;\n wSum[k] = wSum[k] + sw;\n }\n }\n }\n\n let q = max(mKuwaharaQ(), 1.0);\n var accum = vec3<f32>(0.0);\n var accumW = 0.0;\n for (var k = 0; k < 8; k = k + 1) {\n let wk = max(wSum[k], 1e-4);\n let mean = mSum[k] / wk;\n let var3 = max(sSum[k] / wk - mean * mean, vec3<f32>(0.0));\n let variance = dot(var3, W_LUMA);\n let sectorW = 1.0 / (1.0 + pow(variance * 320.0, q));\n accum = accum + mean * sectorW;\n accumW = accumW + sectorW;\n }\n var result = accum / max(accumW, 1e-4);\n\n // Anchor in near-flat zones so the smoothing only bites at edges.\n let center = sampleBase(p, t);\n result = mix(center, result, clamp(0.35 + 0.65 * A, 0.0, 1.0));\n\n // Faint canvas tooth + pigment-saturation so the flattened patches read as oil paint.\n let tooth = vnoise(p * 300.0) - 0.5;\n result = result * (1.0 + tooth * 0.06 * mCanvasGrain());\n result = saturate3(result, 1.04);\n\n return mix(col0, result, clamp(mStrokeAmount(), 0.0, 1.0));\n}\n\n// \u2500\u2500 Metal \u2014 the two-term anisotropic BRDF (uMedium==8/9; the WGSL twin) \u2500\u2500\u2500\u2500\u2500\u2500\n// The field re-lights as warm folded metal: the luma HEIGHT field (the packed\n// structure-tensor gradient \u2192 the surface normal) catches an anisotropic Blinn streak\n// (brushed metal, running ALONG the coherent edge-tangent) plus a sharp crest, both\n// raked by the IN-STRUCT cursor-synth light (crosses to WGSL \u2014 never uLightDir). The\n// catch is the achromatic-warm anchor (never a cold chrome-blue). ZERO extra taps \u2014\n// the gradient is unpacked from the tensor's .w lane the caller already computed.\nconst METAL_CATCH_WARM: vec3<f32> = vec3<f32>(1.0, 0.97, 0.90);\nconst METAL_HEIGHT_SCALE: f32 = 2.2;\nconst METAL_LIGHT_Z: f32 = 0.55;\nconst METAL_IDLE_RAKE: vec2<f32> = vec2<f32>(0.32, 0.34);\nconst METAL_SHININESS_ANISO: f32 = 6.0;\nconst METAL_SHININESS_CREST: f32 = 40.0;\nconst METAL_COHERENCE_FLOOR: f32 = 0.22;\nconst METAL_BODY_FLOOR: f32 = 0.42;\nconst METAL_GRADIENT_FLATTEN: f32 = 0.55;\nconst METAL_SPARKLE_DENSITY: f32 = 240.0;\nconst METAL_SPARKLE_THRESH: f32 = 0.72;\nconst METAL_SPARKLE_AMP: f32 = 0.6;\n\nfn metalShade(baseCol: vec3<f32>, p: vec2<f32>, stf: vec4<f32>) -> vec3<f32> {\n let tangent = stf.xy;\n let A = stf.z;\n let grad = unpackGrad(stf.w);\n let N = normalize(vec3<f32>(grad * (METAL_HEIGHT_SCALE * mMetalHeightScale()), 1.0));\n let V = vec3<f32>(0.0, 0.0, 1.0);\n // Cursor-synth rake light (u.cursor is in-struct \u2014 crosses to WGSL).\n let L = normalize(vec3<f32>((u.cursor.xy - p) + METAL_IDLE_RAKE, METAL_LIGHT_Z));\n let H = normalize(L + V);\n // Anisotropic streak \u2014 brushed metal along the edge-tangent.\n let tH = dot(normalize(tangent), H.xy);\n let sinTH = sqrt(clamp(1.0 - tH * tH, 0.0, 1.0));\n let streak = pow(clamp(sinTH, 0.0, 1.0), METAL_SHININESS_ANISO);\n let crest = pow(clamp(dot(N, H), 0.0, 1.0), METAL_SHININESS_CREST);\n var spec = streak * crest;\n spec = spec * smoothstep(0.0, METAL_COHERENCE_FLOOR, A);\n // Technicolor valley base \u2014 the body keeps the FIELD hue; luma rides the relief.\n let diff = clamp(dot(N, L), 0.0, 1.0);\n let body = baseCol * (METAL_BODY_FLOOR + (1.0 - METAL_BODY_FLOOR) * diff);\n return body + METAL_CATCH_WARM * (spec * clamp(mMetalPolish(), 0.0, 4.0));\n}\n\nfn mediumMetal(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let stf = structureTensorField(p, t, flowField(p, t));\n return metalShade(col, p, stf);\n}\n\n// metal-gradient (uMedium==9) \u2014 the SAME BRDF over a pre-flattened base + the\n// twinkle-in-place flake sparkle (position FIXED per cell, phase-animated, facing-gated).\nfn mediumMetalGradient(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let stf = structureTensorField(p, t, flowField(p, t));\n let flat = mix(col, vec3<f32>(dot(col, W_LUMA)), METAL_GRADIENT_FLATTEN);\n var metal = metalShade(flat, p, stf);\n let grad = unpackGrad(stf.w);\n let facing = normalize(vec3<f32>(grad * (METAL_HEIGHT_SCALE * mMetalHeightScale()), 1.0)).z;\n let cell = floor(p * METAL_SPARKLE_DENSITY);\n let seed = hash21(cell);\n let tw = pow(clamp(sin(t + seed * 6.2831853), 0.0, 1.0), 40.0);\n let flakeGate = step(METAL_SPARKLE_THRESH, seed);\n metal = metal + METAL_CATCH_WARM * (tw * flakeGate * clamp(facing, 0.0, 1.0) * METAL_SPARKLE_AMP);\n return metal;\n}\n\n// \u2500\u2500 Medium dispatch (the WGSL twin of the GLSL main() if-ladder) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// uMedium: 0 smooth \u00B7 1 pastel \u00B7 2 watercolor \u00B7 3 oil \u00B7 4 crayon \u00B7 5 vangogh \u00B7\n// 6 oil-pastel \u00B7 7 kuwahara \u00B7 8 metal \u00B7 9 metal-gradient. The cheap peer mediums\n// (pastel/watercolor/crayon) + kuwahara + metal port their own bodies; the\n// oil/vangogh/oil-pastel STROKE cascade renders the anisotropic-Kuwahara PAINTERLY\n// finish here (a real oil-paint read, never the smooth core \u2014 the \"NO FALLBACKS on\n// Safari\" mandate; the full per-dab Starry-Night stroke WGSL remains a separate\n// full-fidelity port).\nfn applyMedium(col: vec3<f32>, p: vec2<f32>, t: f32) -> vec3<f32> {\n let medium = u.ints1.x;\n if (medium == 1) { return mediumPastel(col, p, t); }\n if (medium == 2) { return mediumWatercolor(col, p, t); }\n if (medium == 4) { return mediumCrayon(col, p, t); }\n // Metal uses both renderer paths; a fragment-only implementation is invisible on the\n // WGSL primary. IDs 8 and 9 select metal and metal-gradient respectively.\n if (medium == 8) { return mediumMetal(col, p, t); }\n if (medium == 9) { return mediumMetalGradient(col, p, t); }\n // oil(3), vangogh(5), oil-pastel(6), kuwahara(7) all render the painterly\n // anisotropic-Kuwahara finish on the WGSL primary (the WebGL2 fallback carries the\n // full per-dab stroke cascade for oil/vangogh/oil-pastel).\n if (medium == 3 || medium == 5 || medium == 6 || medium == 7) {\n return mediumKuwahara(col, p, t);\n }\n return col;\n}\n\n\n// \u2500\u2500 Full-screen triangle vertex stage (the pilot idiom \u2014 no vertex buffer) \u2500\u2500\nstruct VSOut {\n @builtin(position) pos: vec4<f32>,\n @location(0) uv: vec2<f32>,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // NDC corners of the covering triangle (-1,-1), (3,-1), (-1,3).\n var p = vec2<f32>(-1.0, -1.0);\n if (vi == 1u) { p = vec2<f32>(3.0, -1.0); }\n else if (vi == 2u) { p = vec2<f32>(-1.0, 3.0); }\n var out: VSOut;\n out.pos = vec4<f32>(p, 0.0, 1.0);\n // vUv = aPos * 0.5 + 0.5 (the aurora.vert convention).\n out.uv = p * 0.5 + 0.5;\n return out;\n}\n\n// \u2500\u2500 Fragment stage (the main() transcription) \u2500\u2500\n@fragment\nfn fs_main(in: VSOut) -> @location(0) vec4<f32> {\n let t = u.scalars0.x;\n let valueVariance = u.scalars0.z;\n let breathDepth = u.scalars2.w;\n let breathPeriod = u.scalars3.x;\n let saturation = u.scalars1.z;\n let paperGrain = u.scalars1.w;\n let alpha = u.scalars2.x;\n let vividness = u.scalars3.w;\n\n let uv = in.uv;\n let pN = uv;\n\n let p_warp = domainWarp(pN, t);\n\n let nf = nucleiField(p_warp, t);\n var col = samplePalette(nf.paletteId);\n col = col * (1.0 + valueVariance * nf.valueMod);\n\n // Breath \u2014 slow global luminance wobble.\n let breath = sin(t * 6.2831 / max(breathPeriod, 1.0));\n col = col * (1.0 + breathDepth * breath * 0.5);\n\n // Painterly-medium dispatch. uMedium 0 (smooth) is a\n // no-op pass-through so the DEFAULT smooth config renders unchanged\n // (the parity capture); uMedium 1-7 render the painterly\n // bodies (pastel/watercolor/crayon/kuwahara) or the anisotropic-Kuwahara finish\n // (oil/vangogh/oil-pastel) \u2014 never a silent smooth degrade on Safari 26.\n col = applyMedium(col, pN, t);\n\n // Cursor-local luminance lean, byte-parallel with the GLSL path.\n let cursorDelta = pN - u.cursor.xy;\n let cursorRadius = max(u.scalars3.z, 0.01);\n let cursorLean = exp(-dot(cursorDelta, cursorDelta) / (cursorRadius * cursorRadius * 0.5));\n col = col * (1.0 + 0.12 * cursorLean * u.scalars3.y);\n\n // Saturation trim.\n col = saturate3(col, saturation);\n\n // The chroma floor is warm-anchored and hue-preserving. The WGSL\n // twin of the GLSL call; vividness:0 is a no-op so the smooth/pale default stays gated.\n col = vividnessFloor(col, vividness);\n\n // Tonemap + film grain.\n col = aces(col);\n // gl_FragCoord.xy \u2192 the device-pixel position (the WGSL @builtin(position)).\n let fragCoord = in.pos.xy;\n let grain = hash21(fragCoord + t * 17.0);\n col = col + (grain - 0.5) * paperGrain;\n\n col = clamp(col * 0.985 + 0.008, vec3<f32>(0.0), vec3<f32>(1.0));\n\n // MANDATORY OETF \u2014 closes the seam (the whole pipeline above runs in LINEAR).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF.\n col = col + (1.0 / 255.0) * (ign(fragCoord) - 0.5);\n\n // Premultiply on DISPLAY-space color (matches the WebGL2 path: OETF, then premul).\n return vec4<f32>(col * alpha, alpha);\n}\n";
@@ -1 +1 @@
1
- export declare const AURORA_BRUSH_GLSL = "// \u2500\u2500 Curved swept brushstroke primitive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\u00B7K\u00B7along0\u00B7(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 \u2014 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 \u2014 thin-fat-thin (classic brush)\n// 1 load-drag \u2014 fat start, tapers to a point\n// 2 dab \u2014 ellipse-like, round center\n// 3 even \u2014 near-constant, slight end taper\n// 4 comma \u2014 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 \u2014 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 \u2014 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 \u2014 endpoints\n// halfW \u2014 base half-width (world units)\n// bulge \u2014 signed perpendicular midpoint offset, in world units\n// shapeType \u2014 0..3\n// bristleFreq \u2014 spatial frequency of edge raggedness\n// bristleAmp \u2014 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed \u2014 uniqueness seed for internal streaks\n// colAtMid \u2014 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 \u2014 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 \u2014 0..0.2 how much internal streaks darken/lighten\n// impastoAmp \u2014 0..1 paint-thickness contribution (the height the relight reads)\n// hardness \u2014 0..1 how crisp the compositing transition is (1 = razor, 0 = creamy)\n//\n// AW.W4.2 \u2014 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 \u2014 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 \u2014\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 \u2014 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 \u2014 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 // \u2500\u2500 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 \u2260 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 // \u00B1~10\u00B0 within-stroke hue swing + \u00B1~9% chroma, scaled by uBrokenColor. Smaller\n // than the per-stroke cell jitter (brokenColorJitter) \u2014 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) \u2014 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 \u2014 the structure-tensor signal a low-contrast color field (a uniform-hue\n // oil field) has no other source for (the \u00A74.2 anisotropy lift on the flat-palette case).\n c *= 1.0 + streak * streakAmp * 3.2;\n\n // Cross-stroke ridge/valley shading \u2014 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 // \u2500\u2500 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 \u2014 relight the accumulated paint height. Derive a normal from the height\n// gradient (dFdx/dFdy \u2014 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 \u2192 high shininess (sharp glint); thin paint \u2192 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\u00B2) \u2014 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 \u00A74.2 anisotropy / \u00A74.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 \u2014 the SBR energy-grade magnitude (0 = none, the oil/oil-pastel\n// baseline; 1 = the full van-Gogh Starry-Night cascade \u2014 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 \u2014 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 \u2014 the AW.W4.1 strokeOrient switch:\n // uStrokeOrient==0 (flow) \u2014 the layer-provided hand-authored flowField.\n // uStrokeOrient==1 (tensor) \u2014 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) \u2014 .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 // \u2014 over-jittering the tensor strokes scatters their directions and the rendered\n // field reads isotropic, the \u00A74.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 \u2014 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 \u2192 long confident strokes,\n // dark \u2192 short dabs, the Kolmogorov/Batchelor congruence) AND by coherence\n // (coherent zones \u2192 long, flat \u2192 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";
1
+ export declare const AURORA_BRUSH_GLSL = "// \u2500\u2500 Curved swept brushstroke primitive \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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\u00B7K\u00B7along0\u00B7(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 \u2014 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 \u2014 thin-fat-thin (classic brush)\n// 1 load-drag \u2014 fat start, tapers to a point\n// 2 dab \u2014 ellipse-like, round center\n// 3 even \u2014 near-constant, slight end taper\n// 4 comma \u2014 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 \u2014 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 \u2014 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 \u2014 endpoints\n// halfW \u2014 base half-width (world units)\n// bulge \u2014 signed perpendicular midpoint offset, in world units\n// shapeType \u2014 0..3\n// bristleFreq \u2014 spatial frequency of edge raggedness\n// bristleAmp \u2014 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed \u2014 uniqueness seed for internal streaks\n// colAtMid \u2014 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 \u2014 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 \u2014 0..0.2 how much internal streaks darken/lighten\n// impastoAmp \u2014 0..1 paint-thickness contribution (the height the relight reads)\n// hardness \u2014 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 \u2014\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 \u2014 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 \u2014 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 // \u2500\u2500 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 \u2260 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 // \u00B1~10\u00B0 within-stroke hue swing + \u00B1~9% chroma, scaled by uBrokenColor. Smaller\n // than the per-stroke cell jitter (brokenColorJitter) \u2014 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) \u2014 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 \u2014 the structure-tensor signal a low-contrast color field (a uniform-hue\n // oil field) has no other source for (the \u00A74.2 anisotropy lift on the flat-palette case).\n c *= 1.0 + streak * streakAmp * 3.2;\n\n // Cross-stroke ridge/valley shading \u2014 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 // \u2500\u2500 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 \u2014 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 \u2192 high shininess (sharp glint); thin paint \u2192 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\u00B2) \u2014 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 \u00A74.2 anisotropy / \u00A74.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 \u2014 the SBR energy-grade magnitude (0 = none, the oil/oil-pastel\n// baseline; 1 = the full van-Gogh Starry-Night cascade \u2014 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 \u2014 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) \u2014 the layer-provided hand-authored flowField.\n // uStrokeOrient==1 (tensor) \u2014 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) \u2014 .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 // \u2014 over-jittering the tensor strokes scatters their directions and the rendered\n // field reads isotropic, the \u00A74.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 \u2192 long confident strokes,\n // dark \u2192 short dabs, the Kolmogorov/Batchelor congruence) AND by coherence\n // (coherent zones \u2192 long, flat \u2192 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";
@@ -1 +1 @@
1
- export declare const AURORA_COMPOSITION_GLSL = "// \u2500\u2500 Palette LUT (W5 \u2014 OKLCh/OKLab interpolation) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 \u2014 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// \u2500\u2500 Nuclei field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 \u2014 slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30\u201360s 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";
1
+ export declare const AURORA_COMPOSITION_GLSL = "// \u2500\u2500 Palette LUT (OKLCh/OKLab interpolation) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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// \u2500\u2500 Nuclei field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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 \u2014 slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30\u201360s 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";
@@ -1 +1 @@
1
- export declare const AURORA_FLOW_GLSL = "// \u2500\u2500 Flow field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Structure-tensor prototype (body defined post-sampleBase in the mediums block).\n// Returns vec4(dir.x, dir.y, coherence, packedGrad) \u2014 the minor-eigenvector tangent + A + the\n// metal-gradient .w lane (BG.W-AUR-METAL-FINISH widened vec3\u2192vec4). 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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";
1
+ export declare const AURORA_FLOW_GLSL = "// \u2500\u2500 Flow field \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Structure-tensor prototype (body defined post-sampleBase in the mediums block).\n// Returns vec4(dir.x, dir.y, coherence, packedGrad) \u2014 the minor-eigenvector tangent + A + the\n// metal-gradient .w lane in the widened vec3\u2192vec4 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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";
@@ -1,2 +1,2 @@
1
- export declare const AURORA_METAL_PACK_GLSL = "\n// \u2500\u2500 Gradient pack (BG.W-AUR-METAL-FINISH) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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";
2
- export declare const AURORA_METAL_MEDIUM_GLSL = "#define MEDIUM_METAL 8\n#define MEDIUM_METALGRAD 9\n\n// \u2500\u2500 Metal \u2014 the two-term anisotropic BRDF over the field's own relief (uMedium==8/9,\n// BG.W-AUR-METAL-FINISH) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Metal is a MUTUALLY-EXCLUSIVE medium (NOT a finish axis) \u2014 the field re-lights as\n// warm folded metal: the luma HEIGHT field (the packed structure-tensor gradient \u2192\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 \u2014 a phantom\n// uLightDir read would be flat on the WGSL primary). The catch is the ACHROMATIC-WARM\n// anchor (AURORA_CATCH_LIGHT_ANCHOR \u2248 [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 \u2014 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 \u2192 normal tilt (\u00D7 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 \u2014 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 \u2014 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 \u2014 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 \u2014 the anisotropic streak reads only where the field has a real edge\n // (a flat zone has no brush direction; A\u22480 \u2192 no streak).\n spec *= smoothstep(0.0, METAL_COHERENCE_FLOOR, A);\n // Technicolor valley base \u2014 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) \u2014 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 \u2014 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) \u2014 same BRDF, calmer body.\n // NB: flat is a GLSL reserved interpolation qualifier \u2014 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 \u2014 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";
1
+ export declare const AURORA_METAL_PACK_GLSL = "\n// \u2500\u2500 Gradient pack \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\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";
2
+ export declare const AURORA_METAL_MEDIUM_GLSL = "#define MEDIUM_METAL 8\n#define MEDIUM_METALGRAD 9\n\n// \u2500\u2500 Metal \u2014 two-term anisotropic BRDF over the field relief (uMedium==8/9) \u2500\u2500\n// Metal is a MUTUALLY-EXCLUSIVE medium (NOT a finish axis) \u2014 the field re-lights as\n// warm folded metal: the luma HEIGHT field (the packed structure-tensor gradient \u2192\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 \u2014 a phantom\n// uLightDir read would be flat on the WGSL primary). The catch is the ACHROMATIC-WARM\n// anchor (AURORA_CATCH_LIGHT_ANCHOR \u2248 [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 \u2014 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 \u2192 normal tilt (\u00D7 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 \u2014 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 \u2014 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 \u2014 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 \u2014 the anisotropic streak reads only where the field has a real edge\n // (a flat zone has no brush direction; A\u22480 \u2192 no streak).\n spec *= smoothstep(0.0, METAL_COHERENCE_FLOOR, A);\n // Technicolor valley base \u2014 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) \u2014 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 \u2014 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) \u2014 same BRDF, calmer body.\n // NB: flat is a GLSL reserved interpolation qualifier \u2014 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 \u2014 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";
@@ -1 +1 @@
1
- export declare const AURORA_OIL_MODES_GLSL = "StrokeProfile profileFor(int medium, int mode) {\n // The oil baseline (mode 0) \u2014 balanced modern-abstract/palette-knife hybrid.\n StrokeProfile prof = StrokeProfile(\n 0, // shapeType \u2014 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 \u2014 W-AUR-VANGOGH-CONFIG reconcile: FINER 240 \u2192 340. Oil's \u00A74.3 \u03B2 sat\n // right AT the \u22121.45 band ceiling (\u00B10.02 frame variance straddling\n // it, surfaced once the vangogh bands went green and the spec reached\n // the oil \u03B2 assert). MEASURED on the live GPU: ADDING fine high-\n // frequency tooth energy steepens oil's power-spectrum slope (more\n // negative \u2014 coarsening it the other way pushed \u03B2 SHALLOWER to\n // \u22121.33). A finer, stronger tooth moves \u03B2 to a safe distance inside\n // the \u22125/3 band [\u22121.85, \u22121.45]. Oil-only (oil-pastel sets 280).\n 0.14, // toothAmp \u2014 raised 0.09 \u2192 0.14 (same oil-\u03B2 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 \u2014 oil is uniform-length (no Starry-Night cascade)\n 0.4, // impastoFloor \u2014 oil's 0.4+0.6\u00B7edgeN crown falloff\n 0.92, // densityFill \u2014 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 \u00A74.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 \u2014 oil keeps the full base (no ground darken; fills everywhere)\n );\n\n // W-AUR-VANGOGH-REBUILD \u2014 the van-Gogh medium NO LONGER routes through this oil\n // StrokeProfile cascade. It is a DEDICATED atomic-dab body (mediumVangogh / vangoghDab\n // below) \u2014 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 // \u2500\u2500 Oil-pastel \u2014 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 \u2014 a round dab reads locally\n // isotropic (steep \u03B2 / low A); the creamy oil-pastel mark\n // is a broad DIRECTIONAL smear, which carries the \u00A74.2\n // coherence + adds the directional mid-band the \u00A74.3 slope\n // needs (the round-dab profile rolled \u03B2 off to \u22122.8)\n prof.bristleAmp = 0.10; // smooth creamy edges (waxy pastel, not bristle)\n prof.streakFreq = 18.0; // finer internal smear \u2014 adds the mid/high-band the smooth\n // round-dab profile lacked (the \u03B2-too-steep fix); the\n // directional along-spine streak flattens the \u00A74.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 \u2014 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 \u2014 eased off 1.16 so the rendered C clears\n // the \u00A74.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 \u2014 a too-soft blend\n // (0.42) smeared the marks into the smooth colour zones and\n // the field rolled off too steep (\u03B2\u2248\u22122.9, a near-gradient);\n // a crisper deposit keeps the mid/high-band the \u22125/3 slope needs\n prof.densityBig = 0.62; // broad directional coverage \u2014 but NOT over-dense: too many\n // overlapping creamy strokes point in slightly-varied\n // directions and the local \u00A74.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 \u2014 varies stroke length\n // by luma/coherence so the dab-size energy spreads across\n // scales (the \u22125/3 cascade the uniform-length 0.0 grade lacked,\n // the \u00A74.3 \u03B2-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 // \u2500\u2500 Oil sub-modes (mode dispatch on the oil medium).\n if (mode == 1) { // palette knife \u2014 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 \u2014 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";
1
+ export declare const AURORA_OIL_MODES_GLSL = "StrokeProfile profileFor(int medium, int mode) {\n // The oil baseline (mode 0) \u2014 balanced modern-abstract/palette-knife hybrid.\n StrokeProfile prof = StrokeProfile(\n 0, // shapeType \u2014 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 \u2014 finer 240 \u2192 340. Oil's \u03B2 sat\n // right AT the \u22121.45 band ceiling (\u00B10.02 frame variance straddling\n // it, surfaced once the vangogh bands went green and the spec reached\n // the oil \u03B2 assert). MEASURED on the live GPU: ADDING fine high-\n // frequency tooth energy steepens oil's power-spectrum slope (more\n // negative \u2014 coarsening it the other way pushed \u03B2 SHALLOWER to\n // \u22121.33). A finer, stronger tooth moves \u03B2 to a safe distance inside\n // the \u22125/3 band [\u22121.85, \u22121.45]. Oil-only (oil-pastel sets 280).\n 0.14, // toothAmp \u2014 raised 0.09 \u2192 0.14 (same oil-\u03B2 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 \u2014 oil is uniform-length (no Starry-Night cascade)\n 0.4, // impastoFloor \u2014 oil's 0.4+0.6\u00B7edgeN crown falloff\n 0.92, // densityFill \u2014 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 \u00A74.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 \u2014 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) \u2014 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 // \u2500\u2500 Oil-pastel \u2014 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 \u2014 a round dab reads locally\n // isotropic (steep \u03B2 / low A); the creamy oil-pastel mark\n // is a broad DIRECTIONAL smear, which carries the \u00A74.2\n // coherence + adds the directional mid-band the \u00A74.3 slope\n // needs (the round-dab profile rolled \u03B2 off to \u22122.8)\n prof.bristleAmp = 0.10; // smooth creamy edges (waxy pastel, not bristle)\n prof.streakFreq = 18.0; // finer internal smear \u2014 adds the mid/high-band the smooth\n // round-dab profile lacked (the \u03B2-too-steep fix); the\n // directional along-spine streak flattens the \u00A74.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 \u2014 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 \u2014 kept low so the rendered C clears\n // the \u00A74.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 \u2014 a too-soft blend\n // (0.42) smeared the marks into the smooth colour zones and\n // the field rolled off too steep (\u03B2\u2248\u22122.9, a near-gradient);\n // a crisper deposit keeps the mid/high-band the \u22125/3 slope needs\n prof.densityBig = 0.62; // broad directional coverage \u2014 but NOT over-dense: too many\n // overlapping creamy strokes point in slightly-varied\n // directions and the local \u00A74.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 \u2014 varies stroke length\n // by luma/coherence so the dab-size energy spreads across\n // scales (the \u22125/3 cascade the uniform-length 0.0 grade lacked,\n // the \u00A74.3 \u03B2-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 // \u2500\u2500 Oil sub-modes (mode dispatch on the oil medium).\n if (mode == 1) { // palette knife \u2014 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 \u2014 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";
@@ -1 +1 @@
1
- export declare const AURORA_VANGOGH_MEDIUM_GLSL = "// \u2500\u2500 Van-Gogh \u2014 DEDICATED atomic-dab body (W-AUR-VANGOGH-REBUILD) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// REBUILT FROM FIRST PRINCIPLES. The prior path routed van-Gogh through the shared\n// oil paintStrokeMedium cascade \u2014 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 \u2014 the \"marbled flow-bands\" read (a spirograph/fingerprint, not impasto),\n// AND the per-cell 8-tap structure tensor \u00D7 4 layers \u00D7 9 cells = ~324 sampleBase/pixel\n// made it run at ~4fps. Van Gogh's sky is NOT continuous flow \u2014 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// \u2022 orientation from the analytic SWIRL flowField (van-Gogh skies ARE swirl rows) +\n// per-cell angular jitter \u2014 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// \u2022 SHORT comma dabs (len/wid \u2248 2.6 : 1 \u2014 a loaded crescent mark, not a 10:1\n// filament), so each reads as a SEPARABLE atom.\n// \u2022 SPARSE placement with visible inter-dab ground (density \u2248 0.42), over a darkened\n// swirl underpainting (the Starry-Night visible ground the bright dabs read over).\n// \u2022 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 \u00D7 9 cells \u00D7 ~1 sampleBase \u2248 20/pixel (a ~16\u00D7 cost cut, ~60fps), and\n// jittered short dabs CANNOT marble.\n\n// One atomic comma dab \u2014 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 \u2014 it is a short oriented capsule (rounded line segment) whose half-width\n// tapers along its length (the loaded-head \u2192 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 \u2014 dab cell pitch (UV units); lenMul/widMul \u2014 dab aspect off the cell;\n// density \u2014 placement gate (sparse \u2192 visible gaps); seed \u2014 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 \u2014 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 \u2014 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 \u2014 len \u2248 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 \u2014 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\u21920\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 (\u00D71.14) at the\n // SOURCE \u2014 the loaded brush mark deposits BRIGHTER + more saturated pigment than the\n // base field, so the loaded-mark pixels lift BOTH the \u00A74.1 colourfulness AND the field\n // mean-luma. The higher mean raises the gap-fraction threshold (meanLum\u00B70.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 \u2014 the ground can stay only moderately dark, so\n // the \u00A74.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 \u2014 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 \u2248 the cell pitch (lenAniso ~1.2-1.7) so a dab sits WITHIN its cell\n // and leaves visible ground around it \u2014 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 \u2014 the comma aspect. The W-AUR-VANGOGH-CONFIG reconcile lengthened\n // the comma (1.25-1.75 \u2192 1.5-2.0) and THINNED it (0.40-0.30 \u2192 0.32-0.24): the rebuilt\n // dabs measured \u00A74.2 structure-tensor anisotropy A=0.699, BELOW the reference band\n // floor 0.732 (the dabs read slightly too short-fat \u2192 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 \u2248 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 \u2014 the big swirl-row dabs: the loaded gestural marks, SPARSE so the dark\n // underpainting shows BETWEEN them (the atomicity read \u2014 visible inter-dab ground).\n // hardness 0.74 \u2014 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 \u2192 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 \u2192 0.26 + hardness 0.74 \u2192 0.84 (the \u00A74.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 \u2192 34 (the \u00A74.3 slope fix): a high-frequency streak runs ALONG the\n // dab spine, injecting fine-scale luma energy that flattens the too-steep \u03B2 WITHOUT\n // adding new isotropic marks (the along-spine streak stays directional, so the \u00A74.2\n // anisotropy the long-thin dab carries is preserved \u2014 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 \u2014 two-thirds-pitch accent dabs (the divisionist sparkle between the big\n // marks \u2014 denser placement but still contained dabs, so dark canvas survives between).\n // angJitter eased 0.5 \u2192 0.34 (same A-lift reasoning \u2014 the accent dabs queue along the\n // flow rather than scattering isotropically); hardness 0.78 \u2192 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 \u2014 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\u00B7THIN directional commas hugging the swirl tangent\n // (the A-preserving fleck \u2014 short fat flecks read isotropic and drag \u00A74.2 down). Sparse\n // density 0.30 keeps the inter-fleck ground open (the gap-fraction atomicity read).\n // The flecks are FINE (\u22480.42\u00D7 pitch) directional commas, tightly flow-aligned (angJitter\n // 0.22) and SPARSE (0.30) \u2014 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 \u00A74.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 // \u2500\u2500 Flow-aligned GROUND swirl-GROOVING (W-AUR-VANGOGH-CONFIG reconcile) \u2500\u2500\n // The bare ground between the dabs was a SMOOTH low-frequency teal gradient \u2014 the \u00A74.3\n // power-spectrum slope ran too STEEP (\u03B2\u2248\u22122.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 \u2014\n // atomicity preserved/strengthened) while injecting the across-flow mid-band luma energy\n // that flattens \u03B2. 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 \u2014 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 \u00A74.2 lift). A comb at one\n // GLOBAL angle (van-Gogh's dominant diagonal stroke grain) is COHERENT over the whole\n // window \u21D2 every open-ground pixel reads the SAME direction (the \u00A74.2 mean-anisotropy lift\n // the swirl comb could not reach). Two resolved bands (\u224822 + \u224844 across the global axis \u21D2\n // ~21px + ~11px at 464px, both in the \u00A74.3 fit window) also feed the \u03B2 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 \u2014 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 \u2014\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 \u2014 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\u2194ground contrast the user's\n // \"reads as strokes\" bar needs, AND the painterly-statistics gap-fraction floor \u2014 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 \u2014 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 \u2014 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 \u00B116\u00B0 hue + \u00B1chroma swing per cell) lifts the field's\n // colourfulness on its own, so the prior 1.04 punch on TOP overshot the \u00A74.1\n // reference band (rendered C=100.5 vs the painting's measured 70.67 \u00B1 15/+25 \u2192\n // ceiling 95.67 \u2014 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 \u2014 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";
1
+ export declare const AURORA_VANGOGH_MEDIUM_GLSL = "// \u2500\u2500 Van Gogh atomic-dab body \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Van-Gogh does NOT route through the shared\n// oil paintStrokeMedium cascade \u2014 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 \u2014 the \"marbled flow-bands\" read (a spirograph/fingerprint, not impasto),\n// AND the per-cell 8-tap structure tensor \u00D7 4 layers \u00D7 9 cells = ~324 sampleBase/pixel\n// runs at ~4fps. Van Gogh's sky is NOT continuous flow \u2014 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// \u2022 orientation from the analytic SWIRL flowField (van-Gogh skies ARE swirl rows) +\n// per-cell angular jitter \u2014 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// \u2022 SHORT comma dabs (len/wid \u2248 2.6 : 1 \u2014 a loaded crescent mark, not a 10:1\n// filament), so each reads as a SEPARABLE atom.\n// \u2022 SPARSE placement with visible inter-dab ground (density \u2248 0.42), over a darkened\n// swirl underpainting (the Starry-Night visible ground the bright dabs read over).\n// \u2022 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 \u00D7 9 cells \u00D7 ~1 sampleBase \u2248 20/pixel (a ~16\u00D7 cost cut, ~60fps), and\n// jittered short dabs CANNOT marble.\n\n// One atomic comma dab \u2014 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 \u2014 it is a short oriented capsule (rounded line segment) whose half-width\n// tapers along its length (the loaded-head \u2192 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 \u2014 dab cell pitch (UV units); lenMul/widMul \u2014 dab aspect off the cell;\n// density \u2014 placement gate (sparse \u2192 visible gaps); seed \u2014 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 \u2014 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 \u2014 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 \u2014 len \u2248 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 \u2014 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\u21920\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 (\u00D71.14) at the\n // SOURCE \u2014 the loaded brush mark deposits BRIGHTER + more saturated pigment than the\n // base field, so the loaded-mark pixels lift BOTH the \u00A74.1 colourfulness AND the field\n // mean-luma. The higher mean raises the gap-fraction threshold (meanLum\u00B70.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 \u2014 the ground can stay only moderately dark, so\n // the \u00A74.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 \u2014 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 \u2248 the cell pitch (lenAniso ~1.2-1.7) so a dab sits WITHIN its cell\n // and leaves visible ground around it \u2014 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 \u2014 the comma aspect. The current profile lengthens\n // the comma (1.25-1.75 \u2192 1.5-2.0) and THINNED it (0.40-0.30 \u2192 0.32-0.24): the rebuilt\n // dabs measured \u00A74.2 structure-tensor anisotropy A=0.699, BELOW the reference band\n // floor 0.732 (the dabs read slightly too short-fat \u2192 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 \u2248 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 \u2014 the big swirl-row dabs: the loaded gestural marks, SPARSE so the dark\n // underpainting shows BETWEEN them (the atomicity read \u2014 visible inter-dab ground).\n // hardness 0.74 \u2014 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 \u2192 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 \u2192 0.26 + hardness 0.74 \u2192 0.84 (the \u00A74.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 \u2192 34 (the \u00A74.3 slope fix): a high-frequency streak runs ALONG the\n // dab spine, injecting fine-scale luma energy that flattens the too-steep \u03B2 WITHOUT\n // adding new isotropic marks (the along-spine streak stays directional, so the \u00A74.2\n // anisotropy the long-thin dab carries is preserved \u2014 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 \u2014 two-thirds-pitch accent dabs (the divisionist sparkle between the big\n // marks \u2014 denser placement but still contained dabs, so dark canvas survives between).\n // angJitter eased 0.5 \u2192 0.34 (same A-lift reasoning \u2014 the accent dabs queue along the\n // flow rather than scattering isotropically); hardness 0.78 \u2192 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 \u2014 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\u00B7THIN directional commas hugging the swirl tangent\n // (the A-preserving fleck \u2014 short fat flecks read isotropic and drag \u00A74.2 down). Sparse\n // density 0.30 keeps the inter-fleck ground open (the gap-fraction atomicity read).\n // The flecks are FINE (\u22480.42\u00D7 pitch) directional commas, tightly flow-aligned (angJitter\n // 0.22) and SPARSE (0.30) \u2014 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 \u00A74.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 // \u2500\u2500 Flow-aligned ground swirl grooving \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // The bare ground between the dabs was a SMOOTH low-frequency teal gradient \u2014 the \u00A74.3\n // power-spectrum slope ran too STEEP (\u03B2\u2248\u22122.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 \u2014\n // atomicity preserved/strengthened) while injecting the across-flow mid-band luma energy\n // that flattens \u03B2. 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 \u2014 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 \u00A74.2 lift). A comb at one\n // GLOBAL angle (van-Gogh's dominant diagonal stroke grain) is COHERENT over the whole\n // window \u21D2 every open-ground pixel reads the SAME direction (the \u00A74.2 mean-anisotropy lift\n // the swirl comb could not reach). Two resolved bands (\u224822 + \u224844 across the global axis \u21D2\n // ~21px + ~11px at 464px, both in the \u00A74.3 fit window) also feed the \u03B2 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 \u2014 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 \u2014\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 \u2014 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\u2194ground contrast the user's\n // \"reads as strokes\" bar needs, AND the painterly-statistics gap-fraction floor \u2014 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 \u2014 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 \u2014 a light OKLCh punch at 0.92 in the\n // The discrete-dab profile's per-dab OKLCh\n // broken-color jitter (the \u00B116\u00B0 hue + \u00B1chroma 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 \u00B1 15/+25 \u2192\n // ceiling 95.67 \u2014 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 \u2014 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";
@@ -6,7 +6,8 @@ export { useCursorInteraction } from "./composables/useCursorInteraction";
6
6
  export { createAurora } from "./composables/runtime";
7
7
  export type { AuroraRuntimeMode, AuroraRuntimeOptions } from "./composables/runtime";
8
8
  export { DEFAULT_AURORA_CONFIG, PAPER_WASH_GROUND, DEFAULT_VIVIDNESS, VIVID_TARGET, IMAGE_BLUR_MIN_DEFAULT, IMAGE_BLUR_MAX_DEFAULT, MAX_NUCLEI, MAX_STOPS, type AuroraConfig, type AuroraCursorApi, type AuroraFlow, type AuroraInstance, type AuroraInteractivity, type AuroraMedium, type AuroraNucleus, type AuroraSource, type AuroraImageSource, type AuroraImageBlur, type FlowPattern, type OklchStop, type StrokeMode, type StrokeOrient, type WarpMode, } from "./constants/presets";
9
- export { resolveAtoms, configToAtoms, nucleiPrior, DEFAULT_ATOMS, type AuroraAtoms, type AuroraMotionAtom, type AuroraZones, type AuroraZoneArrangement, type AuroraMediumAtom, type AuroraInteractivityAtom, } from "./composables/atoms";
9
+ export { resolveAtoms, configToAtoms, DEFAULT_ATOMS, type AuroraAtoms, type AuroraZones, type AuroraMediumAtom, type AuroraInteractivityAtom, type AuroraFieldInteractivityAtom, type AuroraSmoothInteractivityAtom, type AuroraPainterlyInteractivityAtom, } from "./composables/atoms";
10
+ export { nucleiPrior, type AuroraMotionAtom, type AuroraZoneArrangement, } from "./composables/atoms-fields";
10
11
  export { cssToOklch, deriveAurora, flattenPalette, hexToOklchStop, oklchStopToHex, oklchToLinear, paletteToCssGradient, } from "./composables/color";
11
12
  export { auroraFallbackGround, sampleAuroraField, nucleiFieldStatic, relativeLuminance, type AuroraFallbackGround, type AuroraFallbackGroundOptions, type AuroraGroundMetrics, } from "./composables/auroraFallbackGround";
12
13
  export type { AuroraHarmony, DeriveAuroraOptions, DeriveEasing, } from "./composables/color";