@mkbabb/glass-ui 3.7.0 → 3.8.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 (213) hide show
  1. package/dist/CardAction-q2qHCJFG.js +157 -0
  2. package/dist/{CommandShortcut-CY0aQTz5.js → CommandShortcut-SzWRhROC.js} +2 -2
  3. package/dist/{ContextMenuSubContent-BeDz7G6f.js → ContextMenuSubContent-Do977Cbf.js} +1 -1
  4. package/dist/{DataTable-gtPOLZvl.js → DataTable-CvlWXz65.js} +3 -3
  5. package/dist/DeckProgress-AVgbIULY.js +23 -0
  6. package/dist/{DialogContent-DG1YMR-P.js → DialogContent-DjZWT3lK.js} +2 -2
  7. package/dist/{DialogFooter-BCIaDP15.js → DialogFooter-Gprw8H_A.js} +1 -1
  8. package/dist/{DiscoGlyph-ZoBYpoxI.js → DiscoGlyph-CG0F410s.js} +1 -1
  9. package/dist/{GlyphFace-Ce_oe3tM.js → GlyphFace-DRjDkB0s.js} +1 -1
  10. package/dist/{HoverPopover-DXpOEw1P.js → HoverPopover-D56O6Ylp.js} +1 -1
  11. package/dist/{IconTooltip-Cu86JR4e.js → IconTooltip-D2T5nYOM.js} +1 -1
  12. package/dist/{MetricBadge-D-bRaQUD.js → MetricBadge-lTc002u2.js} +1 -1
  13. package/dist/{Notification-B5WHP3_1.js → Notification-BxK8LZMw.js} +1 -1
  14. package/dist/{PopoverContent-F1bWnYoh.js → PopoverContent-BId6ggf_.js} +1 -1
  15. package/dist/{Progress-c4vo4ALi.js → Progress-iEuD9pxG.js} +4 -4
  16. package/dist/{ScrollingText-BEp377U5.js → ScrollingText-CVyqxvfv.js} +2 -2
  17. package/dist/{SelectScrollDownButton-DAo5NmY_.js → SelectScrollDownButton-C7TRe-ep.js} +2 -2
  18. package/dist/{ToggleGroupItem-C-AIkBUw.js → ToggleGroupItem-CkXUcBlt.js} +1 -1
  19. package/dist/{UnderlineTabs-D_c1iYo-.js → UnderlineTabs-Uxn-R3MT.js} +2 -2
  20. package/dist/animated-digit.js +2 -2
  21. package/dist/api/index.d.ts +4 -2
  22. package/dist/api.js +1 -1
  23. package/dist/aurora.js +336 -405
  24. package/dist/canvas.d.ts +1 -0
  25. package/dist/canvas.js +3 -0
  26. package/dist/card.js +1 -1
  27. package/dist/carousel.js +105 -104
  28. package/dist/collapsible.js +1 -1
  29. package/dist/{color-DjJCAQ6f.js → color-DMLTunJK.js} +22 -14
  30. package/dist/color.js +2 -2
  31. package/dist/command.js +1 -1
  32. package/dist/compile-BBTRjvQS.js +158 -0
  33. package/dist/components/custom/aurora/composables/atoms.d.ts +90 -25
  34. package/dist/components/custom/aurora/composables/color.d.ts +0 -12
  35. package/dist/components/custom/aurora/composables/uniformBridge.d.ts +34 -23
  36. package/dist/components/custom/aurora/constants/presets.d.ts +10 -7
  37. package/dist/components/custom/aurora/constants/renderMode.d.ts +22 -0
  38. package/dist/components/custom/aurora/constants/shaders/aurora.wgsl.d.ts +1 -1
  39. package/dist/components/custom/aurora/constants/shaders/brush.glsl.d.ts +1 -1
  40. package/dist/components/custom/aurora/constants/shaders/composition.glsl.d.ts +1 -1
  41. package/dist/components/custom/aurora/constants/shaders/mediums.glsl.d.ts +1 -1
  42. package/dist/components/custom/aurora/index.d.ts +3 -3
  43. package/dist/components/custom/constellation/Constellation.vue.d.ts +40 -5
  44. package/dist/components/custom/constellation/constellationField.d.ts +110 -5
  45. package/dist/components/custom/constellation/index.d.ts +2 -2
  46. package/dist/components/custom/dock/GlassDock.vue.d.ts +12 -4
  47. package/dist/components/custom/dock/composables/dockMorphContext.d.ts +57 -0
  48. package/dist/components/custom/dock/composables/index.d.ts +3 -0
  49. package/dist/components/custom/dock/composables/useDockHold.d.ts +61 -0
  50. package/dist/components/custom/dock/composables/useLayerTransition.d.ts +13 -59
  51. package/dist/components/custom/dock/index.d.ts +2 -0
  52. package/dist/components/custom/goo-blob/GooBlob.vue.d.ts +13 -0
  53. package/dist/components/custom/goo-blob/composables/useBlobMood.d.ts +4 -0
  54. package/dist/components/custom/goo-blob/composables/useBlobPointer.d.ts +2 -0
  55. package/dist/components/custom/goo-blob/composables/useBlobSatellites.d.ts +2 -0
  56. package/dist/components/custom/goo-blob/composables/useMetaballRenderer.d.ts +20 -5
  57. package/dist/components/custom/goo-blob/index.d.ts +1 -1
  58. package/dist/components/custom/goo-blob/shaders/sdf-body.glsl.d.ts +1 -1
  59. package/dist/components/custom/goo-blob/shaders/watercolor-edges.glsl.d.ts +2 -2
  60. package/dist/components/custom/goo-blob/types.d.ts +8 -0
  61. package/dist/components/ui/card/Card.vue.d.ts +23 -7
  62. package/dist/components/ui/card/index.d.ts +1 -1
  63. package/dist/components/ui/progress/ProgressDefault.vue.d.ts +14 -4
  64. package/dist/components/ui/slider/Slider.vue.d.ts +3 -1
  65. package/dist/composables/color/index.d.ts +19 -0
  66. package/dist/composables/dom/index.d.ts +1 -1
  67. package/dist/composables/dom/useResolveTokenColor.d.ts +27 -0
  68. package/dist/composables/glass/canvas2d/index.d.ts +2 -1
  69. package/dist/composables/glass/canvas2d/resolveCanvasColor.d.ts +20 -0
  70. package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +15 -1
  71. package/dist/composables/glass/index.d.ts +3 -1
  72. package/dist/composables/glass/useSpecularTracking.d.ts +33 -0
  73. package/dist/composables/glass/webgl/createCanvasLifecycle.d.ts +1 -1
  74. package/dist/composables/glass/webgl/shaders/procedural-color.glsl.d.ts +4 -0
  75. package/dist/composables/glass/webgl/useWebGLCanvas.d.ts +1 -1
  76. package/dist/composables/motion/core/index.d.ts +1 -0
  77. package/dist/configurator.js +1 -1
  78. package/dist/confirm-dialog.js +1 -1
  79. package/dist/constellation.js +144 -75
  80. package/dist/context-menu.js +2 -2
  81. package/dist/controls.js +2 -2
  82. package/dist/dark.js +1 -1
  83. package/dist/data-table.js +1 -1
  84. package/dist/deck-progress.d.ts +1 -0
  85. package/dist/deck-progress.js +2 -0
  86. package/dist/dialog.js +2 -2
  87. package/dist/disco-glyph.js +1 -1
  88. package/dist/dock.js +386 -285
  89. package/dist/{dockContext-DLgrVWsU.js → dockContext-BKeIMmNk.js} +1 -1
  90. package/dist/dom.js +6 -6
  91. package/dist/{dropdown-menu-BWpwutt0.js → dropdown-menu-B_4bWyyG.js} +2 -2
  92. package/dist/dropdown-menu.js +1 -1
  93. package/dist/expandable-container.js +1 -1
  94. package/dist/fonts/README.md +26 -18
  95. package/dist/forms.js +3 -3
  96. package/dist/fourier-field.js +37 -37
  97. package/dist/glass-carousel.js +12 -7
  98. package/dist/glass-panel.js +7 -7
  99. package/dist/glass-ui.css +1 -1
  100. package/dist/glass-ui.js +150 -162
  101. package/dist/glyph-face.js +2 -2
  102. package/dist/goo-blob.js +353 -270
  103. package/dist/header-ribbon.js +1 -1
  104. package/dist/hover-card.js +1 -1
  105. package/dist/hover-popover.js +1 -1
  106. package/dist/icon-tooltip.js +1 -1
  107. package/dist/index.d.ts +1 -0
  108. package/dist/instrument-chassis.js +1 -1
  109. package/dist/instrument-rail.js +1 -1
  110. package/dist/keyboard.js +1 -1
  111. package/dist/{keys-Dyf_uTs5.js → keys-BsYjEFOW.js} +1 -1
  112. package/dist/label.js +1 -1
  113. package/dist/labeled-field.js +6 -6
  114. package/dist/metric-badge.js +1 -1
  115. package/dist/metric-stack.js +1 -1
  116. package/dist/motion-core.js +65 -65
  117. package/dist/motion.js +3 -3
  118. package/dist/notification.js +1 -1
  119. package/dist/paper-backdrop.js +1 -1
  120. package/dist/popover.js +1 -1
  121. package/dist/progress.js +1 -1
  122. package/dist/pulse.js +1 -1
  123. package/dist/reactive.js +2 -2
  124. package/dist/resolveCanvasColor-BBiYfp8g.js +11 -0
  125. package/dist/responsive-tabs.js +2 -2
  126. package/dist/scrolling-text.js +1 -1
  127. package/dist/search.js +7 -7
  128. package/dist/select.js +3 -3
  129. package/dist/separator.js +1 -1
  130. package/dist/{sheet-Cv1uO-Gq.js → sheet-VZEtNFvO.js} +2 -2
  131. package/dist/sheet.js +1 -1
  132. package/dist/slider-D9puf-Ko.js +142 -0
  133. package/dist/slider.js +1 -1
  134. package/dist/sortable-list.js +3 -3
  135. package/dist/styles/components.css +1 -1
  136. package/dist/styles/dock-controls.css +21 -18
  137. package/dist/styles/dock.css +415 -224
  138. package/dist/styles/fonts.css +0 -25
  139. package/dist/styles/glass.css +100 -41
  140. package/dist/styles/theme.css +9 -2
  141. package/dist/styles/tokens.css +139 -32
  142. package/dist/styles/typography.css +52 -69
  143. package/dist/styles/utilities.css +2 -1
  144. package/dist/styles/view-transition.css +12 -40
  145. package/dist/switch.js +1 -1
  146. package/dist/tabs.js +5 -5
  147. package/dist/timeline.js +3 -3
  148. package/dist/toast.js +1 -1
  149. package/dist/toggle-group.js +1 -1
  150. package/dist/tooltip.js +1 -1
  151. package/dist/typewriter.js +1 -1
  152. package/dist/{useAnimatedNumber-C5I3wWd8.js → useAnimatedNumber-C_3wZLx4.js} +1 -1
  153. package/dist/{useCanvas2D-C31yfUGY.js → useCanvas2D-ClrQuZtp.js} +2 -1
  154. package/dist/{useConfiguratorState-B24TcgIn.js → useConfiguratorState-CMff4GzH.js} +3 -3
  155. package/dist/useResolveTokenColor-r9pnNjqK.js +24 -0
  156. package/dist/useSpecularTracking-BVRZsRHA.js +26 -0
  157. package/dist/{useTextHighlight-aMPdEpkq.js → useTextHighlight-9nMW23Qb.js} +1 -1
  158. package/dist/{useTouchGate-YQRwyUGU.js → useTouchGate-WFyJYsth.js} +1 -1
  159. package/dist/vReveal-CqaCz_mC.js +44 -0
  160. package/dist/watercolor-dot.js +2 -2
  161. package/package.json +36 -4
  162. package/src/fonts/README.md +26 -18
  163. package/src/styles/dock-controls.css +21 -18
  164. package/src/styles/dock.css +415 -224
  165. package/src/styles/fonts.css +0 -25
  166. package/src/styles/glass.css +100 -41
  167. package/src/styles/theme.css +9 -2
  168. package/src/styles/tokens.css +139 -32
  169. package/src/styles/typography.css +52 -69
  170. package/src/styles/utilities.css +2 -1
  171. package/src/styles/view-transition.css +12 -40
  172. package/dist/CardAction-D8nSE62a.js +0 -163
  173. package/dist/compile-iM4IUB66.js +0 -158
  174. package/dist/fonts/fraunces/fraunces-latin.woff2 +0 -0
  175. package/dist/slider-BA9wIs5e.js +0 -130
  176. package/dist/useViewTransition-DaN5Zrox.js +0 -21
  177. package/dist/vReveal-BE2TsPoR.js +0 -24
  178. package/src/fonts/fraunces/fraunces-latin.woff2 +0 -0
  179. /package/dist/{CollapsibleContent-D7UNsXQr.js → CollapsibleContent-CP9qF1v4.js} +0 -0
  180. /package/dist/{ContextMenuContent-DGKZX1Du.js → ContextMenuContent-DDa0v27s.js} +0 -0
  181. /package/dist/{HoverCardContent-Bn0UBMY9.js → HoverCardContent-B6CcQ8He.js} +0 -0
  182. /package/dist/{Input-DWQQIOC4.js → Input-DVG_J0ne.js} +0 -0
  183. /package/dist/{InstrumentChassis-BLJBl1BW.js → InstrumentChassis-C2f6cBaQ.js} +0 -0
  184. /package/dist/{InstrumentRail-BElFwKyt.js → InstrumentRail-CUJ05YVG.js} +0 -0
  185. /package/dist/{Label-DBc7bKzZ.js → Label-zRfbQdCn.js} +0 -0
  186. /package/dist/{ModalOverlay-D1VrWyoi.js → ModalOverlay-B1GkAjNb.js} +0 -0
  187. /package/dist/{PaperBackdrop-DCGoGatv.js → PaperBackdrop-CqwjnfLv.js} +0 -0
  188. /package/dist/{SelectGroup-BcaFjvP4.js → SelectGroup-BFiod7X2.js} +0 -0
  189. /package/dist/{SelectSeparator-COFMJGng.js → SelectSeparator-L8pPEn4L.js} +0 -0
  190. /package/dist/{Separator-DZyqsCum.js → Separator-D7sD7RKj.js} +0 -0
  191. /package/dist/{Switch-2rsAtDfA.js → Switch-LDKLmmgx.js} +0 -0
  192. /package/dist/{TabsList-COqIdW7i.js → TabsList-qyHZ2lKY.js} +0 -0
  193. /package/dist/{Toaster-BMaeziJJ.js → Toaster-CabYEEF_.js} +0 -0
  194. /package/dist/{TooltipProvider-CD2IXfRv.js → TooltipProvider-CmZzCuaG.js} +0 -0
  195. /package/dist/{_plugin-vue_export-helper-gLzyemvl.js → _plugin-vue_export-helper-ok8pgSl5.js} +0 -0
  196. /package/dist/composables/{dom → motion}/useTextHighlight.d.ts +0 -0
  197. /package/dist/{constants-CS75AWHG.js → constants-BPOqYJAm.js} +0 -0
  198. /package/dist/{createContext-CvNyBk4o.js → createContext-B44Sah_H.js} +0 -0
  199. /package/dist/{menuItemVariants-B4eFR5Ix.js → menuItemVariants-DKM85SNE.js} +0 -0
  200. /package/dist/{presets-BKVyQfbt.js → presets-D1XPuTj-.js} +0 -0
  201. /package/dist/{prng-wHuHlNHE.js → prng-COGhh02-.js} +0 -0
  202. /package/dist/{useGlassRenderer-HebgSEgN.js → useGlassRenderer-5I0YkI4t.js} +0 -0
  203. /package/dist/{useGlobalDark-yhL4Wp-s.js → useGlobalDark-D3EBWzXK.js} +0 -0
  204. /package/dist/{useIntersectionPause-Cc-H9ees.js → useIntersectionPause-Bj4sPkMh.js} +0 -0
  205. /package/dist/{useInterval-D3USn0yc.js → useInterval-CcLfq2WU.js} +0 -0
  206. /package/dist/{useKeyboardShortcuts-Be3w_Rwk.js → useKeyboardShortcuts-BXWXOZ-9.js} +0 -0
  207. /package/dist/{useResizeObserver-CSyxGxIX.js → useResizeObserver-CWCbk5Px.js} +0 -0
  208. /package/dist/{useScrollProgress-CAH8Ofwt.js → useScrollProgress-C9ysDic5.js} +0 -0
  209. /package/dist/{useSortable-C8ReQ50P.js → useSortable-MGAbCGVJ.js} +0 -0
  210. /package/dist/{useSpringMount-LRZqJTaF.js → useSpringMount-BJSHcLl3.js} +0 -0
  211. /package/dist/{useTimer-DEJonOZa.js → useTimer-3xqf_54b.js} +0 -0
  212. /package/dist/{useUserInvalidAria-CQKjEfwP.js → useUserInvalidAria-DoJKMZ3s.js} +0 -0
  213. /package/dist/{useViewportReady-K96m-Mdv.js → useViewportReady-RIr5Wgkh.js} +0 -0
@@ -0,0 +1,158 @@
1
+ //#region src/composables/glass/webgl/shaders/procedural-color.glsl.ts
2
+ var e = "\nfloat srgbToLinearCh(float c) {\n return c <= 0.04045 ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4);\n}\nvec3 srgbToLinear(vec3 c) {\n return vec3(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\n\nfloat linearToSrgbCh(float c) {\n return c <= 0.0031308 ? c * 12.92 : 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n}\nvec3 linearToSrgb(vec3 c) {\n return vec3(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}", t = "const mat2 FBM_ROT = mat2(0.8, 0.6, -0.6, 0.8);", n = "\n// value.js LINEAR_SRGB_TO_LMS (constants.ts), row-major; written here as GLSL\n// columns (= the transpose) so mat3 * vec3 evaluates the row-major M·v.\nconst mat3 LINEAR_SRGB_TO_LMS = mat3(\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→OKLab coefficients (gamut.ts lines 295-297 —\n// these differ at the ~1e-9 digit from LMS_TO_OKLAB_MATRIX; use the inline ones to\n// mirror the exact value.js path), row-major → GLSL columns.\nconst mat3 LMS_TO_OKLAB = mat3(\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 (constants.ts), row-major → GLSL columns. Rows:\n// l = [1, 0.3963377774, 0.2158037573], m = [1, -0.1055613458, -0.0638541728],\n// s = [1, -0.0894841775, -1.2914855480].\nconst mat3 OKLAB_TO_LMS = mat3(\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 (constants.ts), row-major → GLSL columns.\nconst mat3 LMS_TO_LINEAR_SRGB = mat3(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\n// Gamma sRGB → raw OKLab (L, a, b). Mirrors value.js srgbToOKLab.\nvec3 srgbToOklab(vec3 c) {\n vec3 lin = srgbToLinear(c);\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n vec3 lmsCbrt = sign(lms) * pow(abs(lms), vec3(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\n\n// Raw OKLab (L, a, b) → linear sRGB. Mirrors value.js oklabToLinearSRGB.\nvec3 oklabToLinearSrgb(vec3 lab) {\n vec3 lms_ = OKLAB_TO_LMS * lab;\n vec3 lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\n\n// OKLab → OKLCh: H in RADIANS. Mirrors value.js rawOklabToOklch (which returns\n// degrees; we stay in radians and only fold to [0, 2pi)).\nvec3 oklabToOklch(vec3 lab) {\n float C = length(lab.yz);\n float H = atan(lab.z, lab.y);\n if (H < 0.0) H += 2.0 * PI;\n return vec3(lab.x, C, H);\n}\n\n// OKLCh (H radians) → OKLab. Mirrors value.js rawOklchToOklab.\nvec3 oklchToOklab(vec3 lch) {\n return vec3(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}", r = "\n// value.js interpolateHue in the NORMALIZED-TURNS domain (h_rad/TAU → turns, the\n// exact .5/+1.0 branch per method, fract() wrap, *TAU back). A radians-native port\n// (PI thresholds / +TAU wrap) diverges 180° at the antipode. methods: 0 shorter,\n// 1 longer, 2 increasing, 3 decreasing.\nfloat interpolateHueTurns(float h0, float h1, float t, int method) {\n float TAU = 2.0 * PI;\n float a = h0 / TAU;\n float b = h1 / TAU;\n float i = b - a;\n if (method == 0) { // shorter\n if (i > 0.5) a += 1.0; else if (i < -0.5) b += 1.0;\n } else if (method == 1) { // longer\n if (i > 0.0 && i < 0.5) a += 1.0; else if (i > -0.5 && i <= 0.0) b += 1.0;\n } else if (method == 2) { // increasing\n if (i < 0.0) b += 1.0;\n } else { // decreasing\n if (i > 0.0) a += 1.0;\n }\n float r = a + t * (b - a);\n r = fract(r); // (r % 1 + 1) % 1 — fract is non-negative in GLSL\n return r * TAU; // back to radians\n}\n\n// Linear-sRGB endpoint pair → OKLab-rectangular interpolation → linear sRGB. Holds\n// chroma across the ramp (no grey midpoint). The default/shorter ramp path.\nvec3 mixPaletteOklab(vec3 linA, vec3 linB, float t) {\n vec3 labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3(1.0 / 3.0)));\n vec3 labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3(1.0 / 3.0)));\n vec3 lab = mix(labA, labB, t);\n return oklabToLinearSrgb(lab);\n}\n\n// Linear-sRGB endpoint pair → OKLCh interpolation along the huePath arc → linear\n// sRGB. L and C lerp; H walks the chosen arc. The deliberate-rainbow path.\nvec3 mixPaletteOklchArc(vec3 linA, vec3 linB, float t, int method) {\n vec3 labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3(1.0 / 3.0)));\n vec3 labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3(1.0 / 3.0)));\n vec3 lchA = oklabToOklch(labA);\n vec3 lchB = oklabToOklch(labB);\n float L = mix(lchA.x, lchB.x, t);\n float C = mix(lchA.y, lchB.y, t);\n float H = interpolateHueTurns(lchA.z, lchB.z, t, method);\n return oklabToLinearSrgb(oklchToOklab(vec3(L, C, H)));\n}\n\n// The ramp dispatcher both samplePalette ports splice. tRaw is the raw inter-stop\n// parameter [0,1]; the smoothstep ease + the huePath branch live HERE so the GLSL\n// and WGSL ramps are character-for-character one source. increasing(2)/decreasing(3)\n// take the OKLCh hue-arc (rainbow sweep); every other method takes the OKLab-\n// rectangular straight line (no hue detour).\nvec3 samplePaletteRamp(vec3 a, vec3 b, float tRaw, int huePath) {\n float 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}", i = "\n// Jarzynski PCG2D — uvec2 → uvec2 permuted-congruential hash (JCGT 2020).\nuvec2 pcg2d(uvec2 v) {\n v = v * 1664525u + 1013904223u;\n v.x += v.y * 1664525u;\n v.y += v.x * 1664525u;\n v = v ^ (v >> 16u);\n v.x += v.y * 1664525u;\n v.y += v.x * 1664525u;\n v = v ^ (v >> 16u);\n return v;\n}\n\n// Float→[0,1) scalar hash: bit-reinterpret the lattice coordinate, pcg2d it, take the\n// high word back to [0,1). Integer-bit — no sin() periodicity, no float-fract lattice.\nfloat pcgHash2(vec2 p) {\n uvec2 u = floatBitsToUint(p);\n return float(pcg2d(u).x) * (1.0 / 4294967296.0);\n}\n\n// Integer-lattice gradient: pcg2d the integer cell, fold the hash to an angle, return\n// the unit gradient vector. The gradient basis (not a scalar value) is what makes the\n// noise zero at the nodes (no value-noise plateaus).\nvec2 pcgGrad2(ivec2 cell) {\n uvec2 h = pcg2d(uvec2(cell));\n float ang = float(h.x) * (1.0 / 4294967296.0) * 6.28318530717958647692;\n return vec2(cos(ang), sin(ang));\n}\n\n// 2D SIMPLEX gradient noise (Gustavson skew). The skew transform F2/G2 maps the square\n// lattice onto a simplex (triangular) grid — no axis-aligned directional bias. Each of\n// the three simplex corners contributes grad·offset shaped by a radial (0.5 - r²)⁴\n// falloff. Output is ~[-1, 1]; callers remap to [0,1] via 0.5 + 0.5*gnoise.\nfloat gnoise(vec2 p) {\n const float F2 = 0.36602540378443864676; // (sqrt(3) - 1) / 2\n const float G2 = 0.21132486540518711775; // (3 - sqrt(3)) / 6\n float s = (p.x + p.y) * F2;\n vec2 i = floor(p + s);\n float tt = (i.x + i.y) * G2;\n vec2 x0 = p - (i - tt); // offset from the simplex origin corner\n ivec2 i1 = (x0.x > x0.y) ? ivec2(1, 0) : ivec2(0, 1);\n vec2 x1 = x0 - vec2(i1) + G2;\n vec2 x2 = x0 - 1.0 + 2.0 * G2;\n ivec2 ii = ivec2(i);\n float n = 0.0;\n float t0 = 0.5 - dot(x0, x0);\n if (t0 > 0.0) { t0 *= t0; n += t0 * t0 * dot(pcgGrad2(ii), x0); }\n float t1 = 0.5 - dot(x1, x1);\n if (t1 > 0.0) { t1 *= t1; n += t1 * t1 * dot(pcgGrad2(ii + i1), x1); }\n float t2 = 0.5 - dot(x2, x2);\n if (t2 > 0.0) { t2 *= t2; n += t2 * t2 * dot(pcgGrad2(ii + ivec2(1, 1)), x2); }\n return 70.0 * n; // normalize to ~[-1, 1]\n}", a = "\nfn srgbToLinearCh(c: f32) -> f32 {\n return select(pow((c + 0.055) / 1.055, 2.4), c / 12.92, c <= 0.04045);\n}\nfn srgbToLinear(c: vec3f) -> vec3f {\n return vec3f(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\nfn linearToSrgbCh(c: f32) -> f32 {\n return select(1.055 * pow(c, 1.0 / 2.4) - 0.055, c * 12.92, c <= 0.0031308);\n}\nfn linearToSrgb(c: vec3f) -> vec3f {\n return vec3f(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}", o = "const FBM_ROT: mat2x2f = mat2x2f(0.8, 0.6, -0.6, 0.8);", s = "\nconst LINEAR_SRGB_TO_LMS: mat3x3f = mat3x3f(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\nconst LMS_TO_OKLAB: mat3x3f = mat3x3f(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\nconst OKLAB_TO_LMS: mat3x3f = mat3x3f(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\nconst LMS_TO_LINEAR_SRGB: mat3x3f = mat3x3f(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn srgbToOklab(c: vec3f) -> vec3f {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n let lmsCbrt = sign(lms) * pow(abs(lms), vec3f(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\nfn oklabToLinearSrgb(lab: vec3f) -> vec3f {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\nfn oklabToOklch(lab: vec3f) -> vec3f {\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 vec3f(lab.x, C, H);\n}\nfn oklchToOklab(lch: vec3f) -> vec3f {\n return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}", c = "\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 let r = fract(a + t * (b - a));\n return r * TAU;\n}\n\nfn mixPaletteOklab(linA: vec3f, linB: vec3f, t: f32) -> vec3f {\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3f(1.0 / 3.0)));\n let lab = mix(labA, labB, vec3f(t));\n return oklabToLinearSrgb(lab);\n}\n\nfn mixPaletteOklchArc(linA: vec3f, linB: vec3f, t: f32, method: i32) -> vec3f {\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3f(1.0 / 3.0)));\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(vec3f(L, C, H)));\n}\n\nfn samplePaletteRamp(a: vec3f, b: vec3f, tRaw: f32, huePath: i32) -> vec3f {\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}", l = "\nfn pcg2d(v0: vec2u) -> vec2u {\n var v = v0 * 1664525u + vec2u(1013904223u);\n v.x = v.x + v.y * 1664525u;\n v.y = v.y + v.x * 1664525u;\n v = v ^ (v >> vec2u(16u));\n v.x = v.x + v.y * 1664525u;\n v.y = v.y + v.x * 1664525u;\n v = v ^ (v >> vec2u(16u));\n return v;\n}\n\nfn pcgHash2(p: vec2f) -> f32 {\n let u = bitcast<vec2u>(p);\n return f32(pcg2d(u).x) * (1.0 / 4294967296.0);\n}\n\nfn pcgGrad2(cell: vec2i) -> vec2f {\n let h = pcg2d(bitcast<vec2u>(cell));\n let ang = f32(h.x) * (1.0 / 4294967296.0) * 6.28318530717958647692;\n return vec2f(cos(ang), sin(ang));\n}\n\nfn gnoise(p: vec2f) -> f32 {\n let F2 = 0.36602540378443864676;\n let G2 = 0.21132486540518711775;\n let s = (p.x + p.y) * F2;\n let i = floor(p + vec2f(s));\n let tt = (i.x + i.y) * G2;\n let x0 = p - (i - vec2f(tt));\n var i1 = vec2i(0, 1);\n if (x0.x > x0.y) { i1 = vec2i(1, 0); }\n let x1 = x0 - vec2f(i1) + vec2f(G2);\n let x2 = x0 - vec2f(1.0) + vec2f(2.0 * G2);\n let ii = vec2i(i);\n var n = 0.0;\n var t0 = 0.5 - dot(x0, x0);\n if (t0 > 0.0) { t0 = t0 * t0; n = n + t0 * t0 * dot(pcgGrad2(ii), x0); }\n var t1 = 0.5 - dot(x1, x1);\n if (t1 > 0.0) { t1 = t1 * t1; n = n + t1 * t1 * dot(pcgGrad2(ii + i1), x1); }\n var t2 = 0.5 - dot(x2, x2);\n if (t2 > 0.0) { t2 = t2 * t2; n = n + t2 * t2 * dot(pcgGrad2(ii + vec2i(1, 1)), x2); }\n return 70.0 * n;\n}";
3
+ function u(e, t, n) {
4
+ return Math.max(t, Math.min(n, e));
5
+ }
6
+ function d() {
7
+ let e = typeof window < "u" && window.devicePixelRatio || 1;
8
+ return Math.min(e, 2);
9
+ }
10
+ //#endregion
11
+ //#region src/composables/glass/webgl/createCanvasLifecycle.ts
12
+ function f(e) {
13
+ let { canvas: t, buildContext: n, resize: r } = e, i = e.respectReducedMotion ?? e.mode !== "capture", a = /* @__PURE__ */ new Set();
14
+ e.mode === "capture" && a.add("manual");
15
+ let o = () => a.size === 0, s = 0, c = typeof performance < "u" ? performance.now() : 0, l = null, u = !1, d = !1, f = i && typeof window < "u" && window.matchMedia ? window.matchMedia("(prefers-reduced-motion: reduce)") : null, p = f?.matches ?? !1;
16
+ function m() {
17
+ if (!o() || !l) return;
18
+ let e = (performance.now() - c) / 1e3, t = l.time ? l.time(e) : e;
19
+ l.frame(t), s = !p && l.shouldContinue() ? requestAnimationFrame(m) : 0;
20
+ }
21
+ function h() {
22
+ u && o() && !s && l && (s = requestAnimationFrame(m));
23
+ }
24
+ function g() {
25
+ let e = f?.matches ?? !1;
26
+ e !== p && (p = e, e || (c = performance.now()), h());
27
+ }
28
+ f?.addEventListener("change", g);
29
+ function _(e = "manual") {
30
+ let t = o();
31
+ a.add(e), t && !o() && (cancelAnimationFrame(s), s = 0);
32
+ }
33
+ function v(e = "manual") {
34
+ let t = !o();
35
+ a.delete(e), t && o() && u && l && (c = performance.now() - 1e3, m());
36
+ }
37
+ let y = typeof document < "u";
38
+ function b() {
39
+ document.hidden ? _("tab-hidden") : v("tab-hidden");
40
+ }
41
+ y && document.addEventListener("visibilitychange", b), y && document.hidden && a.add("tab-hidden");
42
+ let x = null;
43
+ function S(e) {
44
+ e.skipped ? _("off-screen") : v("off-screen");
45
+ }
46
+ function C() {
47
+ if (x) return;
48
+ let e = t.parentElement;
49
+ e && (x = e, e.addEventListener("contentvisibilityautostatechange", S));
50
+ }
51
+ function w() {
52
+ x?.removeEventListener("contentvisibilityautostatechange", S), x = null;
53
+ }
54
+ function T() {
55
+ l = n(), r();
56
+ }
57
+ function E() {
58
+ u || d || (e.bindContextEvents?.(() => {
59
+ d || (T(), o() && h());
60
+ }, () => {
61
+ l = null, cancelAnimationFrame(s), s = 0;
62
+ }), T(), C(), u = !0, c = performance.now(), o() && (s = requestAnimationFrame(m)));
63
+ }
64
+ function D(e) {
65
+ u || E(), l?.frame(e);
66
+ }
67
+ function O() {
68
+ d = !0, cancelAnimationFrame(s), s = 0, y && document.removeEventListener("visibilitychange", b), f?.removeEventListener("change", g), w(), e.unbindContextEvents?.(), l?.teardown?.(), l = null, a.clear();
69
+ }
70
+ return e.mode === "capture" && E(), {
71
+ arm: E,
72
+ suspend: _,
73
+ resume: v,
74
+ wake: h,
75
+ renderAt: D,
76
+ dispose: O,
77
+ get armed() {
78
+ return u;
79
+ },
80
+ get disposed() {
81
+ return d;
82
+ },
83
+ get running() {
84
+ return o();
85
+ },
86
+ get reducedMotion() {
87
+ return p;
88
+ }
89
+ };
90
+ }
91
+ //#endregion
92
+ //#region src/composables/glass/webgl/useWebGLCanvas.ts
93
+ function p(e, t) {
94
+ let { setup: n, contextAttrs: r } = t, i = null, a = null, o = null;
95
+ function s() {
96
+ let t = e.getContext("webgl2", r ?? void 0);
97
+ if (!t) throw Error("[useWebGLCanvas] WebGL2 unavailable");
98
+ return i = t, o = n(i), a || (a = new ResizeObserver(() => o?.resize()), a.observe(e)), {
99
+ frame: o.frame,
100
+ shouldContinue: o.shouldContinue,
101
+ time: o.time,
102
+ teardown: () => {
103
+ o?.teardown?.(), i?.getExtension("WEBGL_lose_context")?.loseContext(), a?.disconnect(), a = null, i = null, o = null;
104
+ }
105
+ };
106
+ }
107
+ let c = null, l = null;
108
+ function u(e) {
109
+ e.preventDefault(), i = null, o = null, l?.();
110
+ }
111
+ let d = f({
112
+ canvas: e,
113
+ mode: t.mode,
114
+ respectReducedMotion: t.respectReducedMotion,
115
+ buildContext: s,
116
+ resize: () => o?.resize(),
117
+ bindContextEvents: (t, n) => {
118
+ c = t, l = n, e.addEventListener("webglcontextlost", u, !1), e.addEventListener("webglcontextrestored", c, !1);
119
+ },
120
+ unbindContextEvents: () => {
121
+ e.removeEventListener("webglcontextlost", u, !1), c && e.removeEventListener("webglcontextrestored", c, !1);
122
+ }
123
+ });
124
+ return {
125
+ arm: d.arm,
126
+ suspend: d.suspend,
127
+ resume: d.resume,
128
+ wake: d.wake,
129
+ renderAt: d.renderAt,
130
+ dispose: d.dispose,
131
+ get gl() {
132
+ return i;
133
+ },
134
+ get reducedMotion() {
135
+ return d.reducedMotion;
136
+ }
137
+ };
138
+ }
139
+ //#endregion
140
+ //#region src/composables/glass/webgl/compile.ts
141
+ function m(e, t, n, r = "[glass-ui:webgl]") {
142
+ let i = e.createShader(t);
143
+ if (e.shaderSource(i, n), e.compileShader(i), !e.getShaderParameter(i, e.COMPILE_STATUS)) {
144
+ let t = e.getShaderInfoLog(i) ?? "unknown";
145
+ throw e.deleteShader(i), Error(`${r} shader compile failed:\n${t}`);
146
+ }
147
+ return i;
148
+ }
149
+ function h(e, t, n, r = "[glass-ui:webgl]") {
150
+ let i = e.createProgram();
151
+ if (e.attachShader(i, t), e.attachShader(i, n), e.linkProgram(i), !e.getProgramParameter(i, e.LINK_STATUS)) {
152
+ let t = e.getProgramInfoLog(i) ?? "unknown";
153
+ throw e.deleteProgram(i), Error(`${r} program link failed:\n${t}`);
154
+ }
155
+ return i;
156
+ }
157
+ //#endregion
158
+ export { u as a, o as c, n as d, s as f, l as g, i as h, f as i, e as l, c as m, h as n, d as o, r as p, p as r, t as s, m as t, a as u };
@@ -1,15 +1,23 @@
1
1
  /**
2
- * AW.W6 — the aurora "atoms of control" door: a PURE, TOTAL mapper from ≤7 intuitive
3
- * atoms to the full `AuroraConfig`. The thin consumer surface over the ~28-field
4
- * author schema (the Stripe/paper.design two-tier model) — nothing is removed from
5
- * `AuroraConfig`; the full schema stays whole as the progressive-disclosure
6
- * "Advanced" escape hatch. `resolveAtoms` is the NEW door, NOT a parallel config path.
2
+ * AX.W10 — the aurora "atoms of control" door: the ONE consumer-facing surface
3
+ * over the ~28-field author schema. `AuroraConfig` is the INTERNAL author schema
4
+ * (the full escape hatch a preset author types against); `resolveAtoms` is the
5
+ * SIMPLIFIED door the live config UI + every consumer drives.
6
+ *
7
+ * The atoms are the user's named control elements (§2.7) — COLOR, ZONES, NOISE,
8
+ * MEDIUM(+texture), MOTION — ≤7 intuitive knobs, each normalized and total
9
+ * (Burley's "Principled" 5-rule discipline: intuitive, few, normalized, robust for
10
+ * EVERY combination). Each atom maps to a co-varying cluster of config fields so one
11
+ * knob moves the entangled axes together (a single COLOR-energy knob co-varies
12
+ * saturation + valueVariance + breath + the warm/cool temperature, because moving any
13
+ * one alone reads as a defect).
7
14
  *
8
15
  * Contract (machine-asserted by proof:aurora-atoms-roundtrip):
9
- * - TOTAL — every atom combination produces a VALID in-range `AuroraConfig`
10
- * respecting every `budget.ts` cap (no NaN, no out-of-range field).
16
+ * - TOTAL — every atom combination (incl. out-of-range/adversarial inputs) produces
17
+ * a VALID in-range `AuroraConfig` respecting every `budget.ts` cap (no NaN, no
18
+ * out-of-range field). Clamp/saturate, never garbage.
11
19
  * - DEFAULT-PRESERVING — `resolveAtoms(DEFAULT_ATOMS)` deep-equals
12
- * `DEFAULT_AURORA_CONFIG` (the wispy-sky default survives the new door).
20
+ * `DEFAULT_AURORA_CONFIG` (the wispy-sky default survives the door).
13
21
  *
14
22
  * Mechanism: clone `DEFAULT_AURORA_CONFIG` and apply ONLY the atoms that are PRESENT
15
23
  * as clamped overrides. An absent atom keeps the default's value, so the empty atom
@@ -17,40 +25,97 @@
17
25
  */
18
26
  import { type AuroraConfig, type AuroraMedium, type OklchStop } from "../constants/presets";
19
27
  import { type AuroraHarmony } from "./color";
20
- /** The mood/energy poles — one slider fans to the co-varying energy axes. */
21
- export type AuroraMoodAtom = "calm" | "balanced" | "vivid";
22
28
  /** The motion atom — the three motion registers. */
23
29
  export type AuroraMotionAtom = "still" | "breathing" | "drifting";
24
30
  /**
25
- * The Tier-1 atoms of control (≤7). EVERY atom is OPTIONAL — an absent atom keeps the
26
- * wispy-sky default's value for the fields it would drive, so `DEFAULT_ATOMS` (the
27
- * empty set) resolves to `DEFAULT_AURORA_CONFIG`. The full `AuroraConfig` stays whole
28
- * as the progressive-disclosure "Advanced" tier.
31
+ * The ZONES arrangement character — selects the nuclei placement prior. NOT a bare
32
+ * integer count: the same count reads as a different field per arrangement.
33
+ * - `scattered` — radial-ish spread off the centre (loose, atmospheric).
34
+ * - `composed` — the rule-of-thirds prior (the deliberate, balanced field).
35
+ * - `centred` — a tight central cluster (one dominant glow, calm).
36
+ */
37
+ export type AuroraZoneArrangement = "scattered" | "composed" | "centred";
38
+ /** The ZONES atom — a count + an arrangement character (default `composed`). */
39
+ export interface AuroraZones {
40
+ /** 1..MAX_NUCLEI — the number of color zones (nuclei). Clamped. */
41
+ count: number;
42
+ /** The placement prior. Default `composed` (rule-of-thirds). */
43
+ arrangement?: AuroraZoneArrangement;
44
+ }
45
+ /**
46
+ * The MEDIUM atom — the painterly medium + (only for a TEXTURED medium) its dominant
47
+ * texture amount. `amount` is STRUCTURALLY ABSENT for `smooth`: a smooth medium has
48
+ * no dominant texture knob, so the door does not offer/accept one (no silent-inert
49
+ * arm). For a textured medium `amount` (0..1) drives the medium's signature knob.
50
+ */
51
+ export type AuroraMediumAtom = {
52
+ kind: "smooth";
53
+ } | {
54
+ kind: Exclude<AuroraMedium, "smooth">;
55
+ amount?: number;
56
+ };
57
+ /**
58
+ * The interactivity atom — ONLY the wired axes ship. `light` (cursor-as-light) and
59
+ * `scroll` (scroll-coupled palette/breath) are wired downstream
60
+ * (`frameLoop.ts`/`useAurora.ts`); the declared-but-unwired `flow`/`wake` axes are
61
+ * EXCISED from the shipped atom shape (no declared-but-dead axis — excise-or-wire).
62
+ */
63
+ export interface AuroraInteractivityAtom {
64
+ /** cursor drives the impasto light direction (cursor-as-light + idle orbit). */
65
+ light?: boolean;
66
+ /** palette/breath progress couples to scroll (via useScrollProgress). */
67
+ scroll?: boolean;
68
+ }
69
+ /**
70
+ * The Tier-1 atoms of control (≤7) — the user's named control elements. EVERY atom is
71
+ * OPTIONAL; an absent atom keeps the wispy-sky default for the fields it would drive,
72
+ * so `DEFAULT_ATOMS` (the empty set) resolves to `DEFAULT_AURORA_CONFIG`.
29
73
  */
30
74
  export interface AuroraAtoms {
31
75
  /** A seed color (CSS string or an OklchStop anchor) → drives the derived palette. */
32
76
  seed?: string | OklchStop;
33
77
  /** The hue scheme across the derived ramp. Default `analogous` (when a seed is set). */
34
78
  harmony?: AuroraHarmony;
35
- /** calm ↔ vivid (one knob) — fans to saturation + warpAmount + valueVariance + breath. */
36
- mood?: AuroraMoodAtom;
37
- /** The painterly medium. */
38
- medium?: AuroraMedium;
39
- /** 0..1 — the medium's dominant texture knob (strokeAmount / wetEdge / canvasGrain). */
40
- textureAmount?: number;
79
+ /**
80
+ * 0..1 — the color ENERGY knob. One scalar co-varies the entangled chroma/value
81
+ * cluster: saturation, valueVariance, breath, AND the warm-light/cool-shadow
82
+ * temperature on the derived palette. 0 = calm/muted, 1 = vivid/charged. A
83
+ * continuous curve, not a 3-point named LUT.
84
+ */
85
+ colorEnergy?: number;
86
+ /** The color zones (nuclei) — a count + an arrangement character. */
87
+ zones?: AuroraZones;
88
+ /**
89
+ * 0..1 — the NOISE knob. One scalar fans to the organic-boundary cluster:
90
+ * warpAmount (how much the zones distort), warpScale (the distortion frequency),
91
+ * warpMode (smooth fBm → cellular as it climbs), and noiseOctaves (detail). 0 =
92
+ * clean isotropic zones, 1 = turbulent organic edges.
93
+ */
94
+ noise?: number;
95
+ /** The painterly medium + (textured mediums only) its dominant texture amount. */
96
+ medium?: AuroraMediumAtom;
41
97
  /** still | breathing | drifting — the three motion registers. */
42
98
  motion?: AuroraMotionAtom;
43
- /** 2..6 — the number of color zones (nuclei), auto-arranged on a rule-of-thirds prior. */
44
- zones?: number;
45
- /** The pointer/scroll interactivity axes (default OFF — declared at W6, wired at W8). */
46
- interactivity?: AuroraConfig["interactivity"];
99
+ /** The pointer/scroll interactivity axes (default OFF). Only `light`/`scroll` ship. */
100
+ interactivity?: AuroraInteractivityAtom;
47
101
  }
48
102
  /**
49
103
  * The default atoms — the EMPTY set. `resolveAtoms(DEFAULT_ATOMS)` deep-equals the
50
104
  * wispy-sky `DEFAULT_AURORA_CONFIG` (machine-asserted). The user explicitly asked the
51
- * default to be preserved; the empty-atom pass-through is how the new door honors it.
105
+ * default to be preserved; the empty-atom pass-through is how the door honors it.
52
106
  */
53
107
  export declare const DEFAULT_ATOMS: AuroraAtoms;
108
+ /**
109
+ * The deterministic nuclei prior for `count` zones under an `arrangement` character.
110
+ * ONE home for the rule-of-thirds prior (the AW duplicated prior pair is gone). Total:
111
+ * clamps `count` to `[1, MAX_NUCLEI]`; every arrangement yields a valid in-range nuclei
112
+ * array.
113
+ *
114
+ * - `composed` — the rule-of-thirds anchor table (the deliberate balanced field).
115
+ * - `scattered` — a golden-angle (φ⁻¹) low-discrepancy spread off-centre (loose).
116
+ * - `centred` — a tight cluster around the centre (one dominant glow).
117
+ */
118
+ export declare function nucleiPrior(count: number, arrangement?: AuroraZoneArrangement): AuroraConfig["nuclei"];
54
119
  /**
55
120
  * Expand the ≤7 Tier-1 atoms into a full, valid, in-range `AuroraConfig`. A PURE,
56
121
  * TOTAL function: clones the wispy-sky default and applies ONLY the present atoms as
@@ -1,4 +1,3 @@
1
- import { type AuroraConfig } from "../constants/presets";
2
1
  import { type ColorHarmony, type OklchStop } from "../../../../composables/color";
3
2
  export { cssToOklch, oklchStopToHex, oklchToLinear, } from "../../../../composables/color";
4
3
  export type { OklchStop } from "../../../../composables/color";
@@ -79,14 +78,3 @@ export interface DeriveAuroraOptions {
79
78
  * @param options ramp shape — stop count, harmony, L/C/h spreads.
80
79
  */
81
80
  export declare function deriveAurora(seed: string | OklchStop, options?: DeriveAuroraOptions): OklchStop[];
82
- /** Atmospheric mood words for {@link deriveScene}. */
83
- export type AuroraMood = "atmospheric" | "painterly" | "vivid" | "muted";
84
- /**
85
- * Seed ONE color + a mood word into a WHOLE `AuroraConfig` — palette (via
86
- * `deriveAurora`), nuclei layout (a rule-of-thirds/golden prior), medium, and the
87
- * motion/output knobs the mood implies. A SUPERSET front door over `deriveAurora`:
88
- * a consumer hands a seed + `"painterly"` and gets a coherent backdrop, not just a
89
- * palette. The base is the wispy-sky `DEFAULT_AURORA_CONFIG` (every field the mood
90
- * does not touch stays the library default).
91
- */
92
- export declare function deriveScene(seed: string | OklchStop, mood?: AuroraMood): AuroraConfig;
@@ -6,15 +6,16 @@
6
6
  * NEW union member is a COMPILE error until it gets a slot — no silent stale-Record
7
7
  * gap where a fresh enum value uploads `undefined`.
8
8
  *
9
- * Crayon is a PEER medium (`uMedium == 4`), NOT a stroke mode: the shader hoists it
10
- * out of `mediumOil()` (wax-on-tooth, not oil strokes). So `MEDIUM_ID` carries the
11
- * peer `crayon: 4` slot and `STROKE_MODE_ID` no longer maps `crayon` — a
12
- * `strokeMode === "crayon"` config resolves to the crayon peer medium via
13
- * `resolveMediumId` and uploads a benign `uStrokeMode` (oil).
9
+ * Crayon is a FIRST-CLASS medium (`uMedium == 4`, AX.W13), NOT a stroke mode: the
10
+ * shader has a dedicated `mediumCrayon()` body (DRY wax-on-tooth). `MEDIUM_ID` carries
11
+ * the `crayon: 4` slot and `StrokeMode` no longer has a `crayon` member — the legacy
12
+ * `oil` + `strokeMode:"crayon"` peer-route is REMOVED (clean break); a crayon surface
13
+ * selects `medium:"crayon"`.
14
14
  *
15
15
  * The pre-allocated upload buffers live in the bridge: a slider drag refills them
16
16
  * in place rather than allocating ~8 Float32Arrays per frame.
17
17
  */
18
+ import type { OklchStop } from "../../../../composables/color";
18
19
  import type { UniformLocations } from "./glSetup";
19
20
  import type { CursorState } from "./cursorModel";
20
21
  import { type AuroraConfig } from "../constants/presets";
@@ -55,35 +56,45 @@ export declare const STROKE_MODE_ID: {
55
56
  readonly knife: 1;
56
57
  readonly chunky: 3;
57
58
  };
59
+ export declare const AURORA_CATCH_LIGHT_ANCHOR: OklchStop;
58
60
  /**
59
- * The effective `uMedium` int for a config. The only non-identity case: a
60
- * `medium: "oil"` + `strokeMode: "crayon"` config selects the crayon PEER
61
- * (`uMedium == 4`) rather than oil — behavior-preserving with the pre-hoist
62
- * `mediumOil()` `mode == 2` branch (same pixel output, dispatched one level up).
61
+ * Resolve a config's `lightColor` to a LINEAR-light `[r,g,b]` triple. Accepts either
62
+ * a raw linear triple (the array form, passed through) OR an `{L,C,h}` OKLCh anchor
63
+ * (derived through the shared `warmCatchLight` helper). Omitted = the canonical aurora
64
+ * warm-white from `AURORA_CATCH_LIGHT_ANCHOR`.
65
+ */
66
+ export declare function resolveLightColor(lightColor: [number, number, number] | OklchStop | undefined): [number, number, number];
67
+ /**
68
+ * The effective `uMedium` int for a config. AX.W13 — a pure identity map over the
69
+ * medium union (the `oil` + `strokeMode:"crayon"` peer-route is REMOVED; crayon is its
70
+ * own first-class `medium:"crayon"`). `medium` is total over `MEDIUM_ID` (the
71
+ * `satisfies` guarantees it), so this never resolves `undefined`.
63
72
  */
64
73
  export declare function resolveMediumId(cfg: AuroraConfig): number;
65
74
  /**
66
- * The `uStrokeMode` int for a config. Crayon is not an oil stroke (it routes to
67
- * the crayon peer medium where `uStrokeMode` is unread), so it uploads the benign
68
- * oil default; the other modes map directly.
75
+ * The `uStrokeMode` int for a config. AX.W13 — `strokeMode` is total over
76
+ * `STROKE_MODE_ID` (no `crayon` special-case), so this is a direct map.
69
77
  */
70
78
  export declare function resolveStrokeModeId(cfg: AuroraConfig): number;
71
79
  /**
72
- * AW.W4.1/W4.3/W4.4 — the `uStrokeOrient` int for a config. The painterly mediums
73
- * (`vangogh`, `oil-pastel`) FORCE the structure-tensor orientation regardless of
74
- * the config's `strokeOrient` field — their brushwork must hug the color zones (the
75
- * congruent-to-real-Van-Gogh contract). Every other medium honors the config's
76
- * `strokeOrient` (default `"flow"` — the pre-W4 path renders identically).
80
+ * AX.W13 — the `uStrokeOrient` int for a config. The painterly mediums (`vangogh`,
81
+ * `oil-pastel`, `crayon`) FORCE the structure-tensor orientation regardless of the
82
+ * config's `strokeOrient` field — their marks must hug the color zones (the
83
+ * congruent-to-real-works contract). Every other medium honors the config's
84
+ * `strokeOrient` (default `"flow"`).
77
85
  */
78
86
  export declare function resolveStrokeOrientId(cfg: AuroraConfig): number;
79
- export declare const WGPU_UNIFORM_FLOATS: number;
87
+ export declare const WGPU_UNIFORM_FLOATS = 20;
88
+ export declare const WGPU_FIELD_FLOATS: number;
80
89
  /**
81
- * Pack an `AuroraConfig` + cursor into the std140 WGSL Uniforms layout (the WebGPU
82
- * write path). Fills `out` in place (the caller owns one reused buffer so a slider
83
- * drag does not churn the GC). The WebGL2 fallback path is unaffected (it keeps its
84
- * `gl.uniform*` calls). The float layout MUST mirror aurora.wgsl.ts `struct Uniforms`.
90
+ * Pack an `AuroraConfig` into the SPLIT WebGPU buffers (AX.W07). `uniformOut` takes the
91
+ * 16 constant-indexed scalars (the f32-packed counts the shader i32()-casts); `fieldOut`
92
+ * takes the palette + nuclei vec4 arrays for the storage `Field` buffer. Both are filled
93
+ * in place (the caller owns the reused buffers so a slider drag does not churn the GC).
94
+ * The WebGL2 fallback path is unaffected (it keeps its `gl.uniform*` calls). The float
95
+ * layouts MUST mirror aurora.wgsl.ts `struct Uniforms` + `struct Field`.
85
96
  */
86
- export declare function packGPUUniforms(cfg: AuroraConfig, timeSec: number, out: Float32Array): Float32Array;
97
+ export declare function packGPUUniforms(cfg: AuroraConfig, timeSec: number, uniformOut: Float32Array, fieldOut: Float32Array): void;
87
98
  /**
88
99
  * Build the per-config uniform uploader bound to a program's uniform location
89
100
  * cache + cursor state. Returns `uploadConfig` — call it to push a fresh
@@ -39,9 +39,9 @@ export interface AuroraNucleus {
39
39
  */
40
40
  angle?: number;
41
41
  }
42
- export type AuroraMedium = "smooth" | "pastel" | "watercolor" | "oil" | "vangogh" | "oil-pastel";
43
- /** Applied only when `medium === "oil"`. Routes inside the shader. */
44
- export type StrokeMode = "oil" | "knife" | "crayon" | "chunky";
42
+ export type AuroraMedium = "smooth" | "pastel" | "watercolor" | "oil" | "crayon" | "vangogh" | "oil-pastel";
43
+ /** Oil-stroke sub-mode. Applied only when `medium === "oil"`. Routes inside the shader. */
44
+ export type StrokeMode = "oil" | "knife" | "chunky";
45
45
  export type FlowPattern = "none" | "radial" | "swirl" | "diagonal" | "multi" | "tensor";
46
46
  /**
47
47
  * AW.W4.1 — how a stroke's orientation is sourced. `flow` keeps the hand-authored
@@ -124,11 +124,14 @@ export interface AuroraConfig {
124
124
  */
125
125
  lightDir?: [number, number, number];
126
126
  /**
127
- * AW.W4.2 — the relight tint (linear-light RGB). Optional; omitted = a warm
128
- * white (the canonical impasto catch-light). The diffuse + Blinn-specular terms
129
- * modulate by this.
127
+ * AW.W4.2 / AX.W11 — the relight tint (the impasto catch-light). Optional;
128
+ * omitted = the canonical warm-white the shared `warmCatchLight` OKLCh helper
129
+ * derives (the `(0.985, 0.0125, 77.5°)` anchor — perceptually the prior eyeballed
130
+ * warm-white, now on the OKLCh core). Author it either as a raw LINEAR-light
131
+ * `[r,g,b]` triple OR as an `{L,C,h}` OKLCh anchor (the bridge derives the linear
132
+ * triple via `warmCatchLight`). The diffuse + Blinn-specular terms modulate by it.
130
133
  */
131
- lightColor?: [number, number, number];
134
+ lightColor?: [number, number, number] | OklchStop;
132
135
  nucleiDrift: number;
133
136
  paletteDrift: number;
134
137
  breathDepth: number;
@@ -13,6 +13,28 @@ export type AuroraRenderMode = "webgl" | "css" | "auto";
13
13
  * placeholder floor.
14
14
  */
15
15
  export type AuroraSubstrate = "webgpu" | "webgl" | "css";
16
+ /**
17
+ * AX.W07 — the WebGPU-parity lever (internal, NOT a consumer prop — consumers should
18
+ * not know the WGSL twin exists). The shared W07↔W14 switch:
19
+ *
20
+ * - W07 sets it `false`. The WGSL twin's two device-proven black-canvas defects (the
21
+ * int-in-float counts + the `var<uniform>` dynamic-index Metal miscompile) are FIXED
22
+ * at their root in this wave, but the twin is STILL reduced-parity by design even
23
+ * unblocked (isotropic-only nuclei, fbm-only warp, NO flow/cursor/lighting/mediums/
24
+ * strokes/grain, a straight-OKLab palette vs the GLSL OKLCh hue-arc). So a
25
+ * WebGPU-capable machine is served the TESTED universal WebGL2 path (DESIGN.md
26
+ * invariant 8 — the correct single-pass renderer) rather than the reduced-parity twin:
27
+ * `resolveRenderModeAsync` returns `{ substrate: "webgl", device: null }` while this
28
+ * is `false`. This is a knowingly-DEGRADED-by-design phased outcome (SPEC.md
29
+ * §DEGRADED).
30
+ * - W14 (band C · AURORA) owns the flip — and ONLY for the OPT-IN Kuwahara painterly
31
+ * finish over a parity-floor field, never to auto-default a capable machine (the
32
+ * §4 note 14 DELETE-the-auto-default disposition). W14 is the NAMED restoration wave.
33
+ *
34
+ * The gate forces the WebGPU path internally regardless of this lever (it tests the
35
+ * SHADER, not the default routing); only the LIVE default routing reads it.
36
+ */
37
+ export declare const WEBGPU_PARITY = false;
16
38
  /**
17
39
  * Resolve `"auto"` to a concrete substrate per device tier. `"webgl"` and
18
40
  * `"css"` pass through unchanged.
@@ -1,7 +1,7 @@
1
1
  declare const WGSL_MAX_NUCLEI = 6;
2
2
  declare const WGSL_MAX_STOPS = 8;
3
3
  export declare const AURORA_VERTEX_WGSL = "\nstruct VSOut {\n @builtin(position) pos: vec4f,\n @location(0) uv: vec2f,\n};\n\n@vertex\nfn vs_main(@builtin(vertex_index) vi: u32) -> VSOut {\n // Full-screen triangle (matches the WebGL2 [-1,-1, 3,-1, -1,3] quad).\n var p = array<vec2f, 3>(vec2f(-1.0, -1.0), vec2f(3.0, -1.0), vec2f(-1.0, 3.0));\n var out: VSOut;\n let xy = p[vi];\n out.pos = vec4f(xy, 0.0, 1.0);\n out.uv = 0.5 * (xy + vec2f(1.0));\n return out;\n}";
4
- export declare const AURORA_FRAGMENT_WGSL = "\nconst PI: f32 = 3.141592653589793;\nconst W_LUMA: vec3f = vec3f(0.2126, 0.7152, 0.0722);\nconst MAX_NUCLEI: u32 = 6u;\nconst MAX_STOPS: u32 = 8u;\n\n// The std140 uniform struct \u2014 the WebGPU write path packs an AuroraConfig here.\nstruct Uniforms {\n time: f32,\n stopCount: i32,\n nucleiCount: i32,\n softmaxBeta: f32,\n valueVariance: f32,\n warpAmount: f32,\n warpScale: f32,\n warpDrift: f32,\n warpMode: i32,\n noiseOctaves: i32,\n medium: i32,\n breathDepth: f32,\n breathPeriod: f32,\n saturation: f32,\n paperGrain: f32,\n alpha: f32,\n palette: array<vec4f, 8>, // .xyz = linear-sRGB stop\n nucleiPos: array<vec4f, 6>, // .xy pos, .z radius, .w paletteBias\n nucleiMod: array<vec4f, 6>, // .x valueBias, .y elong, .z angle, .w driftRadius\n};\n\n@group(0) @binding(0) var<uniform> U: Uniforms;\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n return select(pow((c + 0.055) / 1.055, 2.4), c / 12.92, c <= 0.04045);\n}\nfn srgbToLinear(c: vec3f) -> vec3f {\n return vec3f(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\nfn linearToSrgbCh(c: f32) -> f32 {\n return select(1.055 * pow(c, 1.0 / 2.4) - 0.055, c * 12.92, c <= 0.0031308);\n}\nfn linearToSrgb(c: vec3f) -> vec3f {\n return vec3f(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\nconst FBM_ROT: mat2x2f = mat2x2f(0.8, 0.6, -0.6, 0.8);\n\nconst LINEAR_SRGB_TO_LMS: mat3x3f = mat3x3f(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\nconst LMS_TO_OKLAB: mat3x3f = mat3x3f(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\nconst OKLAB_TO_LMS: mat3x3f = mat3x3f(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\nconst LMS_TO_LINEAR_SRGB: mat3x3f = mat3x3f(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn srgbToOklab(c: vec3f) -> vec3f {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n let lmsCbrt = sign(lms) * pow(abs(lms), vec3f(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\nfn oklabToLinearSrgb(lab: vec3f) -> vec3f {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\nfn oklabToOklch(lab: vec3f) -> vec3f {\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 vec3f(lab.x, C, H);\n}\nfn oklchToOklab(lch: vec3f) -> vec3f {\n return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\nfn hash21(p0: vec2f) -> f32 {\n var p = fract(p0 * vec2f(123.34, 456.21));\n p = p + dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\nfn vnoise(p: vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = hash21(i);\n let b = hash21(i + vec2f(1.0, 0.0));\n let c = hash21(i + vec2f(0.0, 1.0));\n let d = hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\nfn fbm(p0: vec2f) -> f32 {\n var v = 0.0;\n var a = 0.5;\n var p = p0;\n for (var i = 0; i < 5; i = i + 1) {\n if (i >= U.noiseOctaves) { 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 samplePalette(id: f32) -> vec3f {\n // Nearest-pair OKLab interpolation across the stops (mirrors the GLSL default ramp).\n let n = U.stopCount;\n if (n <= 1) { return U.palette[0].xyz; }\n let t = clamp(id, 0.0, 1.0) * f32(n - 1);\n let i0 = i32(floor(t));\n let i1 = min(i0 + 1, n - 1);\n let f = t - f32(i0);\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * U.palette[i0].xyz) * pow(abs(LINEAR_SRGB_TO_LMS * U.palette[i0].xyz), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * U.palette[i1].xyz) * pow(abs(LINEAR_SRGB_TO_LMS * U.palette[i1].xyz), vec3f(1.0 / 3.0)));\n return oklabToLinearSrgb(mix(labA, labB, f));\n}\n\nfn domainWarp(p: vec2f, t: f32) -> vec2f {\n let drift = t * U.warpDrift * 5.0;\n let q = vec2f(\n fbm(p * U.warpScale + vec2f(drift, 0.0)),\n fbm(p * U.warpScale + vec2f(5.2, 1.3 + drift))\n );\n let r = vec2f(\n fbm(p * U.warpScale + 4.0 * q + vec2f(1.7, 9.2)),\n fbm(p * U.warpScale + 4.0 * q + vec2f(8.3, 2.8))\n );\n return p + U.warpAmount * r;\n}\n\nfn nucleiField(p: vec2f, t: f32) -> vec2f {\n // Returns (paletteId, valueMod) \u2014 softmax over the nuclei (isotropic; the WebGPU\n // twin keeps the parity-floor isotropic Gaussian).\n var accumId = 0.0;\n var accumVal = 0.0;\n var w = 0.0;\n let n = U.nucleiCount;\n for (var i = 0; i < 6; i = i + 1) {\n if (i >= n) { break; }\n let pos = U.nucleiPos[i].xy;\n let rad = max(U.nucleiPos[i].z, 0.01);\n let d2 = dot(p - pos, p - pos);\n let wi = exp((-U.softmaxBeta * d2) / (rad * rad));\n accumId = accumId + wi * U.nucleiPos[i].w;\n accumVal = accumVal + wi * U.nucleiMod[i].x;\n w = w + wi;\n }\n if (w < 1e-4) { return vec2f(0.5, 0.0); }\n return vec2f(accumId / w, accumVal / w);\n}\n\n// ACES (Narkowicz) \u2014 matches the GLSL tonemap.\nfn aces(x: vec3f) -> vec3f {\n let a = 2.51; let b = 0.03; let c = 2.43; let d = 0.59; let e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3f(0.0), vec3f(1.0));\n}\n\n@fragment\nfn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n let t = U.time;\n let pw = domainWarp(uv, t);\n let nf = nucleiField(pw, t);\n var col = samplePalette(nf.x);\n col = col * (1.0 + U.valueVariance * nf.y);\n\n // Breath.\n let breath = sin(t * 6.2831 / max(U.breathPeriod, 1.0));\n col = col * (1.0 + U.breathDepth * breath * 0.5);\n\n // (The WebGPU smooth pole; the painterly mediums run the W7c multi-pass passes.)\n col = aces(col);\n col = linearToSrgb(col); // the mandatory OETF (linear \u2192 display).\n return vec4f(col * U.alpha, U.alpha); // premultiply AFTER the OETF (parity).\n}";
4
+ export declare const AURORA_FRAGMENT_WGSL = "\nconst PI: f32 = 3.141592653589793;\nconst W_LUMA: vec3f = vec3f(0.2126, 0.7152, 0.0722);\nconst MAX_NUCLEI: u32 = 6u;\nconst MAX_STOPS: u32 = 8u;\n\n// The uniform struct \u2014 ONLY constant-indexed scalars (safe in the uniform address\n// space). The five count/enum fields are f32 (AX.W07 1a): the WebGPU write path packs\n// them as floats and the shader i32-casts at each use, so WGSL never reads a float\n// bit-pattern as a raw i32. The dynamically-indexed arrays moved OUT to struct Field\n// below (AX.W07 1b).\nstruct Uniforms {\n time: f32,\n stopCount: f32,\n nucleiCount: f32,\n softmaxBeta: f32,\n valueVariance: f32,\n warpAmount: f32,\n warpScale: f32,\n warpDrift: f32,\n warpMode: f32,\n noiseOctaves: f32,\n medium: f32,\n breathDepth: f32,\n breathPeriod: f32,\n saturation: f32,\n paperGrain: f32,\n alpha: f32,\n // AX.W11 \u2014 the huePath enum (f32-packed, i32()-cast in samplePalette) carries the\n // shared ramp's OKLab-rectangular-vs-OKLCh-hue-arc dispatch into the WebGPU twin so\n // a huePath:'increasing' rainbow config arcs identically to the WebGL2 oracle. The\n // CPU pad keeps the buffer vec4-aligned (WGPU_UNIFORM_FLOATS rounds to 20).\n huePath: f32,\n};\n\n// The dynamically-indexed arrays \u2014 bound var<storage, read> (AX.W07 1b). A runtime\n// palette/nuclei index into a var<uniform> array<vec4f> returns [0,0,0,0] on Metal\n// (MSL forbids dynamic indexing of constant); storage is always-legal. The vec4 stride\n// is std140-identical to the uniform layout, so the move is byte-exact.\nstruct Field {\n palette: array<vec4f, 8>, // .xyz = linear-sRGB stop\n nucleiPos: array<vec4f, 6>, // .xy pos, .z radius, .w paletteBias\n nucleiMod: array<vec4f, 6>, // .x valueBias, .y elong, .z angle, .w driftRadius\n};\n\n@group(0) @binding(0) var<uniform> U: Uniforms;\n@group(0) @binding(1) var<storage, read> F: Field;\n\n\nfn srgbToLinearCh(c: f32) -> f32 {\n return select(pow((c + 0.055) / 1.055, 2.4), c / 12.92, c <= 0.04045);\n}\nfn srgbToLinear(c: vec3f) -> vec3f {\n return vec3f(srgbToLinearCh(c.r), srgbToLinearCh(c.g), srgbToLinearCh(c.b));\n}\nfn linearToSrgbCh(c: f32) -> f32 {\n return select(1.055 * pow(c, 1.0 / 2.4) - 0.055, c * 12.92, c <= 0.0031308);\n}\nfn linearToSrgb(c: vec3f) -> vec3f {\n return vec3f(linearToSrgbCh(c.r), linearToSrgbCh(c.g), linearToSrgbCh(c.b));\n}\nconst FBM_ROT: mat2x2f = mat2x2f(0.8, 0.6, -0.6, 0.8);\n\nconst LINEAR_SRGB_TO_LMS: mat3x3f = mat3x3f(\n 0.4122214708, 0.2119034982, 0.0883024619,\n 0.5363325363, 0.6806995451, 0.2817188376,\n 0.0514459929, 0.1073969566, 0.6299787005\n);\nconst LMS_TO_OKLAB: mat3x3f = mat3x3f(\n 0.2104542553, 1.9779984951, 0.0259040371,\n 0.7936177850, -2.4285922050, 0.7827717662,\n -0.0040720468, 0.4505937099, -0.8086757660\n);\nconst OKLAB_TO_LMS: mat3x3f = mat3x3f(\n 1.0, 1.0, 1.0,\n 0.3963377774, -0.1055613458, -0.0894841775,\n 0.2158037573, -0.0638541728, -1.2914855480\n);\nconst LMS_TO_LINEAR_SRGB: mat3x3f = mat3x3f(\n 4.0767416621, -1.2684380046, -0.0041960863,\n -3.3077115913, 2.6097574011, -0.7034186147,\n 0.2309699292, -0.3413193965, 1.7076147010\n);\n\nfn srgbToOklab(c: vec3f) -> vec3f {\n let lin = srgbToLinear(c);\n let lms = LINEAR_SRGB_TO_LMS * lin;\n let lmsCbrt = sign(lms) * pow(abs(lms), vec3f(1.0 / 3.0));\n return LMS_TO_OKLAB * lmsCbrt;\n}\nfn oklabToLinearSrgb(lab: vec3f) -> vec3f {\n let lms_ = OKLAB_TO_LMS * lab;\n let lms = lms_ * lms_ * lms_;\n return LMS_TO_LINEAR_SRGB * lms;\n}\nfn oklabToOklch(lab: vec3f) -> vec3f {\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 vec3f(lab.x, C, H);\n}\nfn oklchToOklab(lch: vec3f) -> vec3f {\n return vec3f(lch.x, lch.y * cos(lch.z), lch.y * sin(lch.z));\n}\n\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 let r = fract(a + t * (b - a));\n return r * TAU;\n}\n\nfn mixPaletteOklab(linA: vec3f, linB: vec3f, t: f32) -> vec3f {\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3f(1.0 / 3.0)));\n let lab = mix(labA, labB, vec3f(t));\n return oklabToLinearSrgb(lab);\n}\n\nfn mixPaletteOklchArc(linA: vec3f, linB: vec3f, t: f32, method: i32) -> vec3f {\n let labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3f(1.0 / 3.0)));\n let labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3f(1.0 / 3.0)));\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(vec3f(L, C, H)));\n}\n\nfn samplePaletteRamp(a: vec3f, b: vec3f, tRaw: f32, huePath: i32) -> vec3f {\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// AX.W12 \u2014 the painterly-medium organic noise basis (PCG2D integer-bit hash + 2D\n// simplex gradient noise), SPLICED from the shared procedural-color chunk \u2014 the EXACT\n// twin of the GLSL leaf aurora.frag.ts splices, so the hash can NEVER drift between\n// backends (proof:aurora-noise-hash-equivalence locks the twins to 1e-6). The WebGPU\n// single-pass fs_main below stays the smooth pole (it runs no medium body \u2014 the\n// painterly mediums are the W7c/W14 multi-pass), so the leaf is single-sourced HERE for\n// the twin-equivalence contract; the W14 painterly passes consume it.\n\nfn pcg2d(v0: vec2u) -> vec2u {\n var v = v0 * 1664525u + vec2u(1013904223u);\n v.x = v.x + v.y * 1664525u;\n v.y = v.y + v.x * 1664525u;\n v = v ^ (v >> vec2u(16u));\n v.x = v.x + v.y * 1664525u;\n v.y = v.y + v.x * 1664525u;\n v = v ^ (v >> vec2u(16u));\n return v;\n}\n\nfn pcgHash2(p: vec2f) -> f32 {\n let u = bitcast<vec2u>(p);\n return f32(pcg2d(u).x) * (1.0 / 4294967296.0);\n}\n\nfn pcgGrad2(cell: vec2i) -> vec2f {\n let h = pcg2d(bitcast<vec2u>(cell));\n let ang = f32(h.x) * (1.0 / 4294967296.0) * 6.28318530717958647692;\n return vec2f(cos(ang), sin(ang));\n}\n\nfn gnoise(p: vec2f) -> f32 {\n let F2 = 0.36602540378443864676;\n let G2 = 0.21132486540518711775;\n let s = (p.x + p.y) * F2;\n let i = floor(p + vec2f(s));\n let tt = (i.x + i.y) * G2;\n let x0 = p - (i - vec2f(tt));\n var i1 = vec2i(0, 1);\n if (x0.x > x0.y) { i1 = vec2i(1, 0); }\n let x1 = x0 - vec2f(i1) + vec2f(G2);\n let x2 = x0 - vec2f(1.0) + vec2f(2.0 * G2);\n let ii = vec2i(i);\n var n = 0.0;\n var t0 = 0.5 - dot(x0, x0);\n if (t0 > 0.0) { t0 = t0 * t0; n = n + t0 * t0 * dot(pcgGrad2(ii), x0); }\n var t1 = 0.5 - dot(x1, x1);\n if (t1 > 0.0) { t1 = t1 * t1; n = n + t1 * t1 * dot(pcgGrad2(ii + i1), x1); }\n var t2 = 0.5 - dot(x2, x2);\n if (t2 > 0.0) { t2 = t2 * t2; n = n + t2 * t2 * dot(pcgGrad2(ii + vec2i(1, 1)), x2); }\n return 70.0 * n;\n}\n\nfn hash21(p0: vec2f) -> f32 {\n var p = fract(p0 * vec2f(123.34, 456.21));\n p = p + dot(p, p + 45.32);\n return fract(p.x * p.y);\n}\nfn vnoise(p: vec2f) -> f32 {\n let i = floor(p);\n let f = fract(p);\n let u = f * f * (3.0 - 2.0 * f);\n let a = hash21(i);\n let b = hash21(i + vec2f(1.0, 0.0));\n let c = hash21(i + vec2f(0.0, 1.0));\n let d = hash21(i + vec2f(1.0, 1.0));\n return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);\n}\nfn fbm(p0: vec2f) -> f32 {\n var v = 0.0;\n var a = 0.5;\n var p = p0;\n let octaves = i32(U.noiseOctaves); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\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 samplePalette(id: f32) -> vec3f {\n // AX.W11 \u2014 splice the SHARED samplePaletteRamp (the smoothstep ease + the OKLab-\n // rectangular-vs-OKLCh-hue-arc huePath dispatch), the EXACT twin the GLSL oracle\n // splices. The body here only selects the bracketing stop pair + the raw inter-stop\n // t (the W07 storage-Field dynamic index), then hands them to the shared ramp.\n let n = i32(U.stopCount); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\n if (n <= 1) { return F.palette[0].xyz; }\n let t = clamp(id, 0.0, 1.0) * f32(n - 1);\n let i0 = i32(floor(t));\n let i1 = min(i0 + 1, n - 1);\n let f = t - f32(i0);\n // AX.W07 1b \u2014 read the palette from the storage Field (dynamic i0/i1 index);\n // AX.W11 \u2014 i32()-cast the f32-packed huePath enum (parity with the count casts).\n return samplePaletteRamp(F.palette[i0].xyz, F.palette[i1].xyz, f, i32(U.huePath));\n}\n\nfn domainWarp(p: vec2f, t: f32) -> vec2f {\n let drift = t * U.warpDrift * 5.0;\n let q = vec2f(\n fbm(p * U.warpScale + vec2f(drift, 0.0)),\n fbm(p * U.warpScale + vec2f(5.2, 1.3 + drift))\n );\n let r = vec2f(\n fbm(p * U.warpScale + 4.0 * q + vec2f(1.7, 9.2)),\n fbm(p * U.warpScale + 4.0 * q + vec2f(8.3, 2.8))\n );\n return p + U.warpAmount * r;\n}\n\nfn nucleiField(p: vec2f, t: f32) -> vec2f {\n // Returns (paletteId, valueMod) \u2014 softmax over the nuclei (isotropic; the WebGPU\n // twin keeps the parity-floor isotropic Gaussian).\n var accumId = 0.0;\n var accumVal = 0.0;\n var w = 0.0;\n let n = i32(U.nucleiCount); // AX.W07 1a \u2014 i32()-cast the f32-packed count.\n for (var i = 0; i < 6; i = i + 1) {\n if (i >= n) { break; }\n // AX.W07 1b \u2014 read the nuclei from the storage Field (dynamic i index).\n let pos = F.nucleiPos[i].xy;\n let rad = max(F.nucleiPos[i].z, 0.01);\n let d2 = dot(p - pos, p - pos);\n let wi = exp((-U.softmaxBeta * d2) / (rad * rad));\n accumId = accumId + wi * F.nucleiPos[i].w;\n accumVal = accumVal + wi * F.nucleiMod[i].x;\n w = w + wi;\n }\n if (w < 1e-4) { return vec2f(0.5, 0.0); }\n return vec2f(accumId / w, accumVal / w);\n}\n\n// ACES (Narkowicz) \u2014 matches the GLSL tonemap.\nfn aces(x: vec3f) -> vec3f {\n let a = 2.51; let b = 0.03; let c = 2.43; let d = 0.59; let e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), vec3f(0.0), vec3f(1.0));\n}\n\n@fragment\nfn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {\n let t = U.time;\n let pw = domainWarp(uv, t);\n let nf = nucleiField(pw, t);\n var col = samplePalette(nf.x);\n col = col * (1.0 + U.valueVariance * nf.y);\n\n // Breath.\n let breath = sin(t * 6.2831 / max(U.breathPeriod, 1.0));\n col = col * (1.0 + U.breathDepth * breath * 0.5);\n\n // (The WebGPU smooth pole; the painterly mediums run the W7c multi-pass passes.)\n col = aces(col);\n col = linearToSrgb(col); // the mandatory OETF (linear \u2192 display).\n return vec4f(col * U.alpha, U.alpha); // premultiply AFTER the OETF (parity).\n}";
5
5
  /** The assembled WGSL module string (vertex + fragment) for createGPUCanvas. */
6
6
  export declare const AURORA_WGSL: string;
7
7
  export { WGSL_MAX_NUCLEI, WGSL_MAX_STOPS };