@mkbabb/glass-ui 3.5.0 → 3.6.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 (80) hide show
  1. package/dist/{DataTable-DyKc_26a.js → DataTable-BGVXx0Ww.js} +1 -1
  2. package/dist/{NumberFieldContent-jO-zGOHf.js → NumberFieldContent-Bhhmk95p.js} +1 -1
  3. package/dist/animated-digit.js +1 -1
  4. package/dist/api/index.d.ts +2 -1
  5. package/dist/api.js +1 -1
  6. package/dist/aurora.js +259 -81
  7. package/dist/{button-i8Ek5vvg.js → button-BcMzVrqs.js} +2 -2
  8. package/dist/button.js +1 -1
  9. package/dist/carousel.js +1 -1
  10. package/dist/color.js +1 -1
  11. package/dist/components/custom/aurora/composables/color.d.ts +35 -2
  12. package/dist/components/custom/aurora/composables/glSetup.d.ts +1 -1
  13. package/dist/components/custom/aurora/composables/uniformBridge.d.ts +6 -0
  14. package/dist/components/custom/aurora/constants/presets.d.ts +9 -0
  15. package/dist/components/custom/aurora/constants/shaders/composition.glsl.d.ts +1 -1
  16. package/dist/components/custom/aurora/index.d.ts +3 -2
  17. package/dist/components/custom/constellation/Constellation.vue.d.ts +45 -0
  18. package/dist/components/custom/constellation/constellationField.d.ts +85 -0
  19. package/dist/components/custom/constellation/index.d.ts +3 -0
  20. package/dist/components/custom/dock/GlassDock.vue.d.ts +34 -3
  21. package/dist/composables/glass/canvas2d/index.d.ts +2 -0
  22. package/dist/composables/glass/canvas2d/useCanvas2D.d.ts +48 -0
  23. package/dist/composables/glass/index.d.ts +2 -0
  24. package/dist/composables/motion/core/index.d.ts +1 -0
  25. package/dist/composables/motion/index.d.ts +1 -0
  26. package/dist/composables/motion/useAnimatedNumberMap.d.ts +29 -0
  27. package/dist/composables/motion/useStagger.d.ts +50 -0
  28. package/dist/confirm-dialog.js +1 -1
  29. package/dist/constellation.d.ts +1 -0
  30. package/dist/constellation.js +182 -0
  31. package/dist/controls.js +1 -1
  32. package/dist/dark.js +1 -1
  33. package/dist/data-table.js +1 -1
  34. package/dist/dock.js +121 -102
  35. package/dist/expandable-container.js +1 -1
  36. package/dist/forms.js +1 -1
  37. package/dist/glass-ui.css +1 -1
  38. package/dist/glass-ui.js +79 -78
  39. package/dist/goo-blob.js +3 -3
  40. package/dist/icon-tooltip.js +1 -1
  41. package/dist/keyboard.js +1 -1
  42. package/dist/labeled-field.js +2 -2
  43. package/dist/motion-core.js +90 -49
  44. package/dist/motion.js +31 -21
  45. package/dist/number-field.js +1 -1
  46. package/dist/responsive-tabs.js +1 -1
  47. package/dist/search.js +1 -1
  48. package/dist/sortable-list.js +1 -1
  49. package/dist/styles/dock.css +185 -1
  50. package/dist/styles/glass-refract.css +60 -0
  51. package/dist/styles/glass-specular-track.css +43 -130
  52. package/dist/styles/glass.css +243 -27
  53. package/dist/styles/index.css +1 -0
  54. package/dist/styles/theme.css +6 -0
  55. package/dist/styles/tokens.css +33 -0
  56. package/dist/tabs.js +2 -2
  57. package/dist/{useAnimatedNumber-BF6r64lA.js → useAnimatedNumber-DfVgP4te.js} +1 -1
  58. package/dist/useCanvas2D-BeNJqqIS.js +95 -0
  59. package/dist/watercolor-dot.js +1 -1
  60. package/package.json +21 -1
  61. package/src/styles/dock.css +185 -1
  62. package/src/styles/glass-refract.css +60 -0
  63. package/src/styles/glass-specular-track.css +43 -130
  64. package/src/styles/glass.css +243 -27
  65. package/src/styles/index.css +1 -0
  66. package/src/styles/theme.css +6 -0
  67. package/src/styles/tokens.css +33 -0
  68. /package/dist/{IconTooltip-BTyYn4mr.js → IconTooltip-DZlCkU-0.js} +0 -0
  69. /package/dist/{Input-CU9CNKmo.js → Input-CRJ2owjN.js} +0 -0
  70. /package/dist/{UnderlineTabs-Cq_AD03t.js → UnderlineTabs-CuIK16JI.js} +0 -0
  71. /package/dist/{color-rkK4RMx2.js → color-DS-iLRVM.js} +0 -0
  72. /package/dist/{compile-DVgAxagk.js → compile-C62K_FpC.js} +0 -0
  73. /package/dist/{constants-DsCdlK9I.js → constants-C8NMonuo.js} +0 -0
  74. /package/dist/{presets-1OhFpaIC.js → presets-Dyv9Zl7W.js} +0 -0
  75. /package/dist/{prng-Bz_1Tydc.js → prng-CjVgzjQT.js} +0 -0
  76. /package/dist/{useGlobalDark-CWiaCoEw.js → useGlobalDark-D7R9OypB.js} +0 -0
  77. /package/dist/{useIntersectionPause-DAdVPVp4.js → useIntersectionPause-DsQYGc8V.js} +0 -0
  78. /package/dist/{useKeyboardShortcuts-BQfnAHHW.js → useKeyboardShortcuts-i8tHd5WO.js} +0 -0
  79. /package/dist/{useSortable-DnyGXKKY.js → useSortable-muI29agz.js} +0 -0
  80. /package/dist/{useViewTransition-CJnqg6wl.js → useViewTransition-DI0Rs2rV.js} +0 -0
package/dist/aurora.js CHANGED
@@ -1,12 +1,12 @@
1
1
  import { t as e } from "./_plugin-vue_export-helper-C1je1s0u.js";
2
- import { a as t, i as n, n as r, o as i, r as a, t as o } from "./compile-DVgAxagk.js";
3
- import { n as s, r as c, t as l } from "./presets-1OhFpaIC.js";
4
- import { a as u, r as d, t as f } from "./color-rkK4RMx2.js";
5
- import { t as p } from "./useIntersectionPause-DAdVPVp4.js";
6
- import { computed as m, createElementBlock as h, createElementVNode as g, defineComponent as _, normalizeClass as v, normalizeStyle as y, onBeforeUnmount as b, onMounted as x, openBlock as S, readonly as C, ref as w, unref as ee, watch as T } from "vue";
7
- import { gamutMapOKLab as E, isInSRGBGamut as D, oklabToLinearSRGB as O, rawOklabToOklch as k, rawOklchToOklab as A, srgbToOKLab as te } from "@mkbabb/value.js";
2
+ import { a as t, i as n, n as r, o as i, r as a, s as o, t as s } from "./compile-C62K_FpC.js";
3
+ import { n as c, r as l, t as u } from "./presets-Dyv9Zl7W.js";
4
+ import { a as d, r as f, t as p } from "./color-DS-iLRVM.js";
5
+ import { t as m } from "./useIntersectionPause-DsQYGc8V.js";
6
+ import { computed as h, createElementBlock as g, createElementVNode as _, defineComponent as v, normalizeClass as y, normalizeStyle as b, onBeforeUnmount as x, onMounted as S, openBlock as ee, readonly as te, ref as C, unref as w, watch as T } from "vue";
7
+ import { gamutMapOKLab as E, interpolateHue as D, isInSRGBGamut as O, oklabToLinearSRGB as k, rawOklabToOklch as A, rawOklchToOklab as j, srgbToOKLab as ne } from "@mkbabb/value.js";
8
8
  //#region src/components/custom/aurora/constants/shaders/aurora.vert.ts
9
- var ne = "#version 300 es\nin vec2 aPos;\nout vec2 vUv;\nvoid main() {\n vUv = aPos * 0.5 + 0.5;\n gl_Position = vec4(aPos, 0.0, 1.0);\n}", re = "// ── Palette LUT ───────────────────────────────────────────────────────────\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 t = smoothstep(0.0, 1.0, t);\n return mix(uPalette[i0], uPalette[i1], t);\n}\n\n// ── Nuclei field ──────────────────────────────────────────────────────────\nvoid nucleiField(vec2 p, float t, out float paletteId, out float valueMod) {\n float accumBias = 0.0;\n float accumValue = 0.0;\n float accumW = 0.0;\n for (int i = 0; i < MAX_NUCLEI; i++) {\n if (i >= uNucleiCount) break;\n vec2 posI = uNucleiPos[i]\n + uNucleiDriftRadius[i] * vec2(\n cos(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i]),\n sin(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i] * 1.13)\n );\n vec2 diff = p - posI;\n // Anisotropic Gaussian: rotate diff into the nucleus's local frame\n // (major axis along uNucleiAngle), then scale the major-axis component by\n // 1/elongation so the squared distance describes an ellipse. Defaults\n // 1.0/0.0 reduce to the isotropic dot(diff, diff).\n float ca = cos(uNucleiAngle[i]);\n float sa = sin(uNucleiAngle[i]);\n vec2 local = vec2( ca * diff.x + sa * diff.y,\n -sa * diff.x + ca * diff.y);\n float along = local.x / max(uNucleiElong[i], 0.01);\n float across = local.y;\n float d2 = along * along + across * across;\n float r = max(uNucleiRadius[i], 0.01);\n float w = exp(-uSoftmaxBeta * d2 / (r * r));\n accumBias += w * uNucleiPaletteBias[i];\n accumValue += w * uNucleiValueBias[i];\n accumW += w;\n }\n paletteId = accumBias / max(accumW, 1e-4);\n valueMod = accumValue / max(accumW, 1e-4);\n\n // Palette drift — slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30–60s hue cycle at the default coefficient);\n // the amplitude lifts the authored coefficient into a 0.03..0.06 paletteId band\n // so the palette visibly travels rather than dithering within one stop.\n float palAmp = clamp(uPaletteDrift * 6.0, 0.0, 0.06);\n paletteId += palAmp * sin(t * uPaletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n}\n", ie = "// ── Flow field ────────────────────────────────────────────────────────────\nvec2 flowField(vec2 p, float t) {\n vec2 dir = vec2(1.0, 0.0);\n if (uFlowPattern == 1) {\n // radial\n dir = normalize(p - uFlowFocal + 1e-4);\n } else if (uFlowPattern == 2) {\n // swirl — tangent to radial\n vec2 rad = normalize(p - uFlowFocal + 1e-4);\n dir = vec2(-rad.y, rad.x);\n } else if (uFlowPattern == 3) {\n // diagonal\n float a = radians(uFlowAngle);\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 4) {\n // multi — curl-noise driven\n float n = fbm(p * 2.0 + t * 0.02);\n float a = n * 6.2831;\n dir = vec2(cos(a), sin(a));\n }\n // curl — perturb by noise. Radial gets much less curl so rays stay clean.\n if (uFlowCurl > 0.0) {\n float n = fbm(p * 3.0) - 0.5;\n float curlAmt = uFlowCurl;\n if (uFlowPattern == 1) curlAmt *= 0.25; // radial: preserve ray clarity\n if (uFlowPattern == 2) curlAmt *= 0.55; // swirl: allow moderate curl\n float a = atan(dir.y, dir.x) + n * 3.14159 * curlAmt;\n dir = vec2(cos(a), sin(a));\n }\n // cursor influence — swirl around the cursor position\n if (uCursorStrength > 0.001) {\n vec2 toCur = uCursor - p;\n float d = length(toCur);\n float r = max(uCursorRadius, 0.01);\n float w = exp(-(d * d) / (r * r * 0.5));\n // swirl tangent\n vec2 tangent = vec2(-toCur.y, toCur.x) / max(d, 1e-4);\n float a0 = atan(dir.y, dir.x);\n float a1 = atan(tangent.y, tangent.x);\n // blend angle\n float da = a1 - a0;\n da = atan(sin(da), cos(da)); // wrap to [-pi, pi]\n float a = a0 + da * w * uCursorStrength;\n dir = vec2(cos(a), sin(a));\n }\n return dir;\n}\n", ae = "// ACES approximation\nvec3 aces(vec3 x) {\n float a = 2.51;\n float b = 0.03;\n float c = 2.43;\n float d = 0.59;\n float e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);\n}\n", oe = "// ── Curved swept brushstroke primitive ────────────────────────────────────\n// A stroke is a curved spine from A to B with quadratic bulge K (sideways offset\n// at midpoint). Width varies along length via a shape profile. Edge is ragged\n// (bristle-modulated), ends are rounded blobs, inside has streak modulation.\n//\n// For pixel p, we invert an approximate projection onto the curved spine:\n// 1) project p onto straight AB axis -> along0, cross0\n// 2) at parameter along0, spine offset = 4·K·along0·(1-along0) perpendicular\n// 3) cross = cross0 - spineOffset, refine along one iteration\n// Coverage blends paint atop col based on per-stroke color sampled from the\n// underlying base at spine midpoint — so overlapping strokes from different\n// palette regions meet at hard, ragged demarcations.\n\nstruct StrokeHit {\n float coverage; // 0..1\n vec3 color;\n float alongT; // 0..1 along spine, for internal modulation\n float crossN; // cross distance / current half-width, signed\n float edgeN; // distance to edge in half-widths (0 = at edge, 1 = at spine)\n};\n\nStrokeHit noHit() { return StrokeHit(0.0, vec3(0.0), 0.0, 0.0, 0.0); }\n\nvec2 rotateDir(vec2 dir, float angle) {\n float ca = cos(angle), sa = sin(angle);\n return vec2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);\n}\n\nvec2 safeDir(vec2 dir) {\n float len = length(dir);\n return len > 1e-4 ? dir / len : vec2(1.0, 0.0);\n}\n\n// Shape profile along the stroke. type:\n// 0 tapered — thin-fat-thin (classic brush)\n// 1 load-drag — fat start, tapers to a point\n// 2 dab — ellipse-like, round center\n// 3 even — near-constant, slight end taper\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 }\n // 0 tapered\n return smoothstep(0.0, 0.22, t) * smoothstep(1.0, 0.78, t);\n}\n\n// A single curved stroke.\n// a, b — endpoints\n// halfW — base half-width (world units)\n// bulge — signed perpendicular midpoint offset, in world units\n// shapeType — 0..3\n// bristleFreq — spatial frequency of edge raggedness\n// bristleAmp — 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed — uniqueness seed for internal streaks\n// colAtMid — pre-sampled color at midpoint\nStrokeHit curvedStroke(vec2 p, vec2 a, vec2 b, float halfW,\n float bulge, int shapeType,\n float bristleFreq, float bristleAmp,\n float streakSeed, vec3 colAtMid) {\n vec2 ab = b - a;\n float L = length(ab);\n if (L < 1e-5) return noHit();\n vec2 tang = ab / L;\n vec2 norm = vec2(-tang.y, tang.x);\n\n vec2 rel = p - a;\n float along0 = dot(rel, tang) / L; // straight projection 0..1 along AB\n float cross0 = dot(rel, norm); // signed cross\n\n // Spine sideways offset due to bulge (parabolic)\n float bend = 4.0 * bulge * along0 * (1.0 - along0);\n float cross1 = cross0 - bend;\n\n // One refinement step: as the spine bends, the closest-point along shifts.\n // Spine tangent differs from straight tangent; first-order correction:\n float dBend = 4.0 * bulge * (1.0 - 2.0 * along0); // d(bend)/d(along)\n float along1 = along0 + (cross1 * dBend) / (L * (1.0 + dBend * dBend / (L * L)));\n\n // Bristle-ragged edge: half-width gets chewed by 1D noise along the spine\n float edgeNoise = fbm(vec2(along1 * bristleFreq, streakSeed * 7.3)) - 0.5;\n float edgeNoise2 = fbm(vec2(along1 * bristleFreq * 2.3, streakSeed * 3.1 + 17.0)) - 0.5;\n float edgeMod = 1.0 - bristleAmp * (0.6 + 0.4 * edgeNoise) * (0.5 + edgeNoise2);\n\n // Width profile along the stroke\n float shape = strokeShape(along1, shapeType);\n float halfWNow = halfW * shape * edgeMod;\n\n // Inside-segment coverage\n float cov = 0.0;\n if (along1 >= 0.0 && along1 <= 1.0 && halfWNow > 1e-6) {\n float cn = abs(cross1) / halfWNow;\n // fwidth-derived AA: half-width from the field's screen-space gradient so the\n // edge holds ~1px regardless of zoom/DPR (mirrors metaball.frag.ts:252-255).\n float aaC = max(fwidth(cn), 1e-6);\n cov = 1.0 - smoothstep(1.0 - aaC, 1.0 + aaC, cn);\n }\n\n // End-cap blobs (rounded tips, not perpendicular cuts)\n float capA = 0.0, capB = 0.0;\n {\n vec2 capCenterA = a + norm * (bulge * 0.0); // at a\n vec2 capCenterB = a + tang * L + norm * bend * 0.0 + norm * 0.0; // at b; bend is 0 at endpoints\n capCenterB = b;\n float endShapeA = strokeShape(0.02, shapeType);\n float endShapeB = strokeShape(0.98, shapeType);\n float rA = halfW * endShapeA * (0.9 + 0.2 * fbm(vec2(streakSeed * 13.0)));\n float rB = halfW * endShapeB * (0.9 + 0.2 * fbm(vec2(streakSeed * 19.0 + 5.0)));\n float dA = length(p - capCenterA);\n float dB = length(p - capCenterB);\n // fwidth-derived AA on the clean Euclidean cap distances (mirrors metaball.frag.ts:252-255).\n float aaA = max(fwidth(dA), 1e-6);\n float aaB = max(fwidth(dB), 1e-6);\n capA = 1.0 - smoothstep(rA - aaA, rA + aaA, dA);\n capB = 1.0 - smoothstep(rB - aaB, rB + aaB, dB);\n // Only apply caps if we're BEYOND the segment; otherwise the segment wins.\n capA *= (along1 < 0.05) ? 1.0 : 0.0;\n capB *= (along1 > 0.95) ? 1.0 : 0.0;\n }\n\n float coverage = max(cov, max(capA, capB));\n if (coverage < 0.002) return noHit();\n\n // Edge distance (for impasto later): 0 at edge, 1 at spine\n float edgeDist = halfWNow > 1e-6 ? clamp(1.0 - abs(cross1) / halfWNow, 0.0, 1.0) : 0.0;\n\n return StrokeHit(\n coverage,\n colAtMid,\n clamp(along1, 0.0, 1.0),\n halfWNow > 1e-6 ? cross1 / halfWNow : 0.0,\n edgeDist\n );\n}\n\n// Composite stroke over col with internal streaking + impasto edge highlight.\n// streakAmp — 0..0.2 how much internal streaks darken/lighten\n// impastoAmp — 0..1 edge catch-light strength\n// hardness — 0..1 how crisp the compositing transition is (1 = razor, 0 = creamy)\nvoid paintOver(inout vec3 col, StrokeHit s, float streakFreq, float streakAmp,\n float impastoAmp, float hardness, float streakSeed) {\n if (s.coverage < 0.002) return;\n float strokeOpacity = clamp(uStrokeAmount, 0.0, 1.0);\n if (strokeOpacity <= 0.001) return;\n vec3 c = s.color;\n\n // Internal streaks — fbm along spine, modulated by cross position. Gives\n // the loaded-brush look: some bristles carry more pigment than others.\n float streakA = fbm(vec2(s.alongT * streakFreq, s.crossN * 2.7 + streakSeed));\n float streakB = fbm(vec2(s.alongT * streakFreq * 0.6 + streakSeed * 3.7, s.crossN * 4.1));\n float streak = 0.6 * (streakA - 0.5) + 0.4 * (streakB - 0.5);\n c *= 1.0 + streak * streakAmp * 2.0;\n\n // Subtle value variance across width (hollow-center catch-light)\n float crossShade = smoothstep(0.0, 0.4, s.edgeN) * (1.0 - smoothstep(0.65, 1.0, s.edgeN));\n c *= 1.0 + crossShade * 0.05;\n\n // Impasto edge highlight — bright rim on one side of the stroke\n float rim = smoothstep(0.85, 1.0, 1.0 - s.edgeN) * step(0.0, s.crossN);\n c += impastoAmp * rim * vec3(0.18, 0.15, 0.11);\n // Shadow on the other side (darker, cooler)\n float shadow = smoothstep(0.85, 1.0, 1.0 - s.edgeN) * step(0.0, -s.crossN);\n c -= impastoAmp * shadow * 0.25 * vec3(0.10, 0.09, 0.07);\n\n float softLimit = mix(0.35, 0.98, hardness);\n float alpha = smoothstep(0.0, 1.0 - softLimit, s.coverage) * strokeOpacity;\n col = mix(col, c, alpha);\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.\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 vec2 cell = floor(p / cellSize);\n\n StrokeHit best = noHit();\n // 3x3 neighborhood\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n vec2 hh = hash22(cc + seed) - 0.5;\n // Density gate — noise threshold; sparse placement.\n float gate = hash21(cc * 1.7 + seed * 0.3);\n if (gate > density) continue;\n\n vec2 center = (cc + 0.5 + hh * jitterAmt) * cellSize;\n\n // Per-stroke direction: consume the layer-provided flow, then add only\n // deterministic local perturbation so alternate stroke layers stay live.\n vec2 f = safeDir(flow);\n float angJ = (hash21(cc + seed + 11.0) - 0.5) * 0.9; // +/- 0.45 rad\n float localCurl = (fbm(center * (2.6 + seed * 0.11) + seed * 1.9) - 0.5) * 0.55 * uFlowCurl;\n vec2 dir = rotateDir(f, angJ + localCurl);\n\n float lenV = cellSize * lenMul * (0.65 + 0.55 * hash21(cc + seed + 23.0));\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", j = "// ── Medium overlays ───────────────────────────────────────────────────────\n\n// A quick re-computation of base color for edge-mask sampling\nvec3 sampleBase(vec2 p, float t) {\n vec2 pw = domainWarp(p, t);\n float id; float vm;\n nucleiField(pw, t, id, vm);\n vec3 c = samplePalette(id);\n c *= 1.0 + uValueVariance * vm;\n return c;\n}\n\nvec3 mediumWatercolor(vec3 col, vec2 p, float t) {\n // Wet-edge cauliflowers via luma-gradient magnitude\n float eps = 0.004;\n vec3 cx1 = sampleBase(p + vec2(eps, 0.0), t);\n vec3 cx2 = sampleBase(p - vec2(eps, 0.0), t);\n vec3 cy1 = sampleBase(p + vec2(0.0, eps), t);\n vec3 cy2 = sampleBase(p - vec2(0.0, eps), t);\n float gx = dot(cx1 - cx2, W_LUMA);\n float gy = dot(cy1 - cy2, W_LUMA);\n float edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n float mask = smoothstep(0.0, 2.5, edge);\n col *= mix(1.0, 0.78, mask * uWetEdge);\n\n // Granulation — pigment settles in paper tooth\n float paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n float pigLoad = 1.0 - dot(col, W_LUMA);\n col *= 1.0 - uGranulation * pigLoad * (paper - 0.5);\n\n // Wash banding — faint horizontal wet gradient\n float band = fbm(vec2(p.x * 1.5, p.y * 0.4));\n col *= 1.0 + 0.04 * (band - 0.5);\n return col;\n}\n\nvec3 mediumPastel(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n vec2 perp = vec2(-flow.y, flow.x);\n float along = dot(p, flow) * uStrokeScale;\n float across = dot(p, perp) * uStrokeScale;\n across += 0.03 * (vnoise(p * 260.0) - 0.5);\n\n float aniso = mix(1.0, 0.18, uStrokeAnisotropy);\n float stroke = fbm(vec2(along * aniso, across));\n col *= mix(1.0, 0.82 + 0.32 * stroke, uStrokeAmount);\n\n // Pastel tooth — tiny high-frequency grain\n float tooth = vnoise(p * 800.0);\n col *= 1.0 - 0.08 * uStrokeAmount * (tooth - 0.5);\n return col;\n}\n", M = "// ── Crayon / oil-pastel — paper tooth × wax pigment (PEER medium) ─────────\n// Crayon is not strokes. It's pigment crumbs dragged across paper tooth.\n// Model: heavy 2D tooth noise at multiple scales, anisotropically stretched\n// along flow direction, multiplied into the base color. Add a slow \"waxy\n// film\" that slightly unifies hues, and occasional darker \"pressed\" spots\n// where the crayon dug in. NO straight segments. Dispatched at main() level as\n// a peer of pastel/watercolor/oil — never a branch inside mediumOil().\nvec3 mediumCrayon(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n float ang = atan(flow.y, flow.x);\n float ca = cos(-ang), sa = sin(-ang);\n vec2 pr = vec2(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n // Anisotropic tooth — squished along flow, coarse cross flow.\n float aniso = mix(0.45, 0.95, uStrokeAnisotropy);\n float scale = max(uStrokeScale * 1.6, 180.0);\n\n float t1 = vnoise(vec2(pr.x * scale * aniso, pr.y * scale));\n float t2 = vnoise(vec2(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + 11.0);\n float t3 = vnoise(vec2(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + 23.0);\n float tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n // Center at 0, amplify\n tooth = (tooth - 0.5) * 1.4;\n\n // Multiplicative darkening where tooth is low (pigment skipped paper valleys)\n float lay = 1.0 + tooth * 0.32 * uStrokeAmount;\n vec3 result = col * lay;\n\n // Occasional pressed-in crumbs — rare darker crumbs\n float crumbs = smoothstep(0.78, 0.95, vnoise(pr * scale * 3.0));\n result *= 1.0 - crumbs * 0.18 * uStrokeAmount;\n\n // Waxy highlight film — slight lightening on tooth peaks\n float waxy = smoothstep(0.55, 0.85, t1);\n result += waxy * 0.04 * vec3(1.0);\n\n // Paper tooth overlay (subtler than oil's canvas)\n float paperTooth = vnoise(p * 340.0) - 0.5;\n result *= 1.0 + paperTooth * 0.14 * uCanvasGrain;\n\n // Broken-color pigment: stable wax/pigment patches, not temporal flicker.\n vec2 pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n float pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + 19.0));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + 17.0),\n hash21(pigmentCell * 2.1 + 31.0),\n 0.45 + 0.55 * pigmentMask\n );\n\n // Crayon is saturation-amplified\n result = saturate3(result, 1.12);\n\n return result;\n}\n\nvec3 mediumOil(vec3 col, vec2 p, float t) {\n // Mode knobs (uStrokeMode) — oil-stroke modes ONLY (crayon is a peer medium,\n // uMedium==4, dispatched at main()):\n // 0 oil — balanced modern-abstract/palette-knife hybrid\n // 1 knife — palette-knife impasto: razor edges, heavy bristle/shadow\n // 3 brushwork — thick bristle brush\n int mode = uStrokeMode;\n\n // Per-mode parameters\n int shapeType = 0; // tapered\n float bristleAmp = 0.25; // 0..0.5\n float streakFreq = 9.0;\n float streakAmp = 0.09;\n float impastoAmp = 0.9;\n float hardness = 0.80; // edge compositing\n float toothScale = 240.0;\n float toothAmp = 0.09;\n float pigmentSat = 1.03;\n float densityBig = 0.65;\n float densityMed = 0.78;\n float densitySml = 0.90;\n\n if (mode == 1) { // palette knife\n shapeType = 3; // flat, even\n bristleAmp = 0.12;\n streakFreq = 4.0; streakAmp = 0.05;\n impastoAmp = 1.6;\n hardness = 0.95;\n toothAmp = 0.04;\n densityBig = 0.80; densityMed = 0.88; densitySml = 0.70;\n } else if (mode == 3) { // thick brushwork\n shapeType = 0; // tapered\n bristleAmp = 0.32;\n streakFreq = 14.0; streakAmp = 0.14;\n impastoAmp = 1.2;\n hardness = 0.85;\n toothAmp = 0.07;\n }\n\n // Scales & multipliers from uniforms\n float baseScale = max(uStrokeScale * 0.006, 0.008);\n // Three layers: big gestural, medium body, small dabs\n float sBig = baseScale * 2.4;\n float sMed = baseScale * 1.1;\n float sSml = baseScale * 0.45;\n\n float lenMulBig = mix(2.2, 3.8, uStrokeAnisotropy);\n float widMulBig = mix(0.55, 0.32, uStrokeAnisotropy);\n float lenMulMed = mix(2.0, 3.4, uStrokeAnisotropy);\n float widMulMed = mix(0.50, 0.30, uStrokeAnisotropy);\n float lenMulSml = mix(1.6, 2.6, uStrokeAnisotropy);\n float widMulSml = mix(0.45, 0.32, uStrokeAnisotropy);\n\n float jitterAmt = 0.75; // large jitter — no grid\n vec2 flow = flowField(p, t);\n\n vec3 result = col;\n\n // Layer 1 — big gestural strokes (sparse, shaping)\n StrokeHit hBig = bestOil(p, sBig, lenMulBig, widMulBig, jitterAmt * 0.55,\n densityBig, shapeType, bristleAmp, flow, t, 1.3);\n paintOver(result, hBig, streakFreq * 0.7, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 1.3);\n\n // Layer 2 — medium body strokes\n StrokeHit hMed = bestOil(p + vec2(11.3, 3.7), sMed, lenMulMed, widMulMed,\n jitterAmt, densityMed, shapeType, bristleAmp, flow, t, 2.7);\n paintOver(result, hMed, streakFreq, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 2.7);\n\n // Layer 3 — small dabs (more frequent, smaller)\n int smlShape = (mode == 1) ? 2 : shapeType; // knife uses dabs for sparkle\n StrokeHit hSml = bestOil(p + vec2(-5.1, 8.4), sSml, lenMulSml, widMulSml,\n jitterAmt * 1.3, densitySml, smlShape,\n bristleAmp * 0.85, flow, t, 4.1);\n paintOver(result, hSml, streakFreq * 1.4, streakAmp * 0.8,\n uImpasto * impastoAmp * 0.65 * uStrokeAmount, hardness, 4.1);\n\n // Layer 4 — fill dabs (very dense, very small) — covers bald spots\n float sFill = baseScale * 0.22;\n float lenMulFill = mix(1.4, 2.0, uStrokeAnisotropy);\n float widMulFill = mix(0.50, 0.38, uStrokeAnisotropy);\n int fillShape = (mode == 1) ? 3 : 2; // knife=even, others=dab (round fills)\n StrokeHit hFill = bestOil(p + vec2(3.9, -6.2), sFill, lenMulFill, widMulFill,\n jitterAmt * 1.5, 0.95, fillShape,\n bristleAmp * 0.6, flow, t, 8.9);\n paintOver(result, hFill, streakFreq * 1.8, streakAmp * 0.6,\n uImpasto * impastoAmp * 0.4 * uStrokeAmount, hardness * 0.9, 8.9);\n\n // Optional crosshatch layer\n if (uStrokeLayers == 2) {\n vec2 flow2 = vec2(-flow.y, flow.x);\n StrokeHit hX = bestOil(p + vec2(7.3, -2.1), sMed, lenMulMed * 0.9, widMulMed,\n jitterAmt, densityMed * 0.7, shapeType, bristleAmp, flow2, t, 6.5);\n paintOver(result, hX, streakFreq, streakAmp * 0.85,\n uImpasto * impastoAmp * 0.55 * uStrokeAmount, hardness, 6.5);\n }\n\n // Canvas tooth — linen weave\n float tooth1 = vnoise(p * toothScale);\n float tooth2 = vnoise(p * toothScale * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * toothAmp * uCanvasGrain;\n\n // Pigment saturation boost\n result = saturate3(result, pigmentSat);\n\n return result;\n}", N = "\n", P = `#version 300 es
9
+ var re = "#version 300 es\nin vec2 aPos;\nout vec2 vUv;\nvoid main() {\n vUv = aPos * 0.5 + 0.5;\n gl_Position = vec4(aPos, 0.0, 1.0);\n}", ie = "// ── Palette LUT (W5 — OKLCh/OKLab interpolation) ──────────────────────────────\n// The endpoints stay CPU-baked to LINEAR sRGB (the Aras precompute pattern); only\n// the BETWEEN-endpoint interpolation moves to perceptual space. OKLab-rectangular\n// for the default ramp (holds chroma, no grey midpoint); the OKLCh hue-arc only on\n// uHuePath increasing/decreasing (deliberate rainbow travel). The prior linear\n// mix() (the muddy-midtone source) is GONE.\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 t = smoothstep(0.0, 1.0, t);\n vec3 a = uPalette[i0];\n vec3 b = uPalette[i1];\n // increasing(2)/decreasing(3) request the OKLCh hue-arc (rainbow sweep); every\n // other method takes the OKLab-rectangular straight line (no hue detour).\n if (uHuePath == 2 || uHuePath == 3) {\n return mixPaletteOklchArc(a, b, t, uHuePath);\n }\n return mixPaletteOklab(a, b, t);\n}\n\n// ── Nuclei field ──────────────────────────────────────────────────────────\nvoid nucleiField(vec2 p, float t, out float paletteId, out float valueMod) {\n float accumBias = 0.0;\n float accumValue = 0.0;\n float accumW = 0.0;\n for (int i = 0; i < MAX_NUCLEI; i++) {\n if (i >= uNucleiCount) break;\n vec2 posI = uNucleiPos[i]\n + uNucleiDriftRadius[i] * vec2(\n cos(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i]),\n sin(t * uNucleiDrift * K_NUCLEI + uNucleiDriftPhase[i] * 1.13)\n );\n vec2 diff = p - posI;\n // Anisotropic Gaussian: rotate diff into the nucleus's local frame\n // (major axis along uNucleiAngle), then scale the major-axis component by\n // 1/elongation so the squared distance describes an ellipse. Defaults\n // 1.0/0.0 reduce to the isotropic dot(diff, diff).\n float ca = cos(uNucleiAngle[i]);\n float sa = sin(uNucleiAngle[i]);\n vec2 local = vec2( ca * diff.x + sa * diff.y,\n -sa * diff.x + ca * diff.y);\n float along = local.x / max(uNucleiElong[i], 0.01);\n float across = local.y;\n float d2 = along * along + across * across;\n float r = max(uNucleiRadius[i], 0.01);\n float w = exp(-uSoftmaxBeta * d2 / (r * r));\n accumBias += w * uNucleiPaletteBias[i];\n accumValue += w * uNucleiValueBias[i];\n accumW += w;\n }\n paletteId = accumBias / max(accumW, 1e-4);\n valueMod = accumValue / max(accumW, 1e-4);\n\n // Palette drift — slow global paletteId breathe between adjacent stops. The\n // rate rides K_PAL (perceptible ~30–60s hue cycle at the default coefficient);\n // the amplitude lifts the authored coefficient into a 0.03..0.06 paletteId band\n // so the palette visibly travels rather than dithering within one stop.\n float palAmp = clamp(uPaletteDrift * 6.0, 0.0, 0.06);\n paletteId += palAmp * sin(t * uPaletteDrift * K_PAL);\n paletteId = clamp(paletteId, 0.0, 1.0);\n}\n", ae = "// ── Flow field ────────────────────────────────────────────────────────────\nvec2 flowField(vec2 p, float t) {\n vec2 dir = vec2(1.0, 0.0);\n if (uFlowPattern == 1) {\n // radial\n dir = normalize(p - uFlowFocal + 1e-4);\n } else if (uFlowPattern == 2) {\n // swirl — tangent to radial\n vec2 rad = normalize(p - uFlowFocal + 1e-4);\n dir = vec2(-rad.y, rad.x);\n } else if (uFlowPattern == 3) {\n // diagonal\n float a = radians(uFlowAngle);\n dir = vec2(cos(a), sin(a));\n } else if (uFlowPattern == 4) {\n // multi — curl-noise driven\n float n = fbm(p * 2.0 + t * 0.02);\n float a = n * 6.2831;\n dir = vec2(cos(a), sin(a));\n }\n // curl — perturb by noise. Radial gets much less curl so rays stay clean.\n if (uFlowCurl > 0.0) {\n float n = fbm(p * 3.0) - 0.5;\n float curlAmt = uFlowCurl;\n if (uFlowPattern == 1) curlAmt *= 0.25; // radial: preserve ray clarity\n if (uFlowPattern == 2) curlAmt *= 0.55; // swirl: allow moderate curl\n float a = atan(dir.y, dir.x) + n * 3.14159 * curlAmt;\n dir = vec2(cos(a), sin(a));\n }\n // cursor influence — swirl around the cursor position\n if (uCursorStrength > 0.001) {\n vec2 toCur = uCursor - p;\n float d = length(toCur);\n float r = max(uCursorRadius, 0.01);\n float w = exp(-(d * d) / (r * r * 0.5));\n // swirl tangent\n vec2 tangent = vec2(-toCur.y, toCur.x) / max(d, 1e-4);\n float a0 = atan(dir.y, dir.x);\n float a1 = atan(tangent.y, tangent.x);\n // blend angle\n float da = a1 - a0;\n da = atan(sin(da), cos(da)); // wrap to [-pi, pi]\n float a = a0 + da * w * uCursorStrength;\n dir = vec2(cos(a), sin(a));\n }\n return dir;\n}\n", oe = "// ACES approximation\nvec3 aces(vec3 x) {\n float a = 2.51;\n float b = 0.03;\n float c = 2.43;\n float d = 0.59;\n float e = 0.14;\n return clamp((x * (a * x + b)) / (x * (c * x + d) + e), 0.0, 1.0);\n}\n", se = "// ── Curved swept brushstroke primitive ────────────────────────────────────\n// A stroke is a curved spine from A to B with quadratic bulge K (sideways offset\n// at midpoint). Width varies along length via a shape profile. Edge is ragged\n// (bristle-modulated), ends are rounded blobs, inside has streak modulation.\n//\n// For pixel p, we invert an approximate projection onto the curved spine:\n// 1) project p onto straight AB axis -> along0, cross0\n// 2) at parameter along0, spine offset = 4·K·along0·(1-along0) perpendicular\n// 3) cross = cross0 - spineOffset, refine along one iteration\n// Coverage blends paint atop col based on per-stroke color sampled from the\n// underlying base at spine midpoint — so overlapping strokes from different\n// palette regions meet at hard, ragged demarcations.\n\nstruct StrokeHit {\n float coverage; // 0..1\n vec3 color;\n float alongT; // 0..1 along spine, for internal modulation\n float crossN; // cross distance / current half-width, signed\n float edgeN; // distance to edge in half-widths (0 = at edge, 1 = at spine)\n};\n\nStrokeHit noHit() { return StrokeHit(0.0, vec3(0.0), 0.0, 0.0, 0.0); }\n\nvec2 rotateDir(vec2 dir, float angle) {\n float ca = cos(angle), sa = sin(angle);\n return vec2(dir.x * ca - dir.y * sa, dir.x * sa + dir.y * ca);\n}\n\nvec2 safeDir(vec2 dir) {\n float len = length(dir);\n return len > 1e-4 ? dir / len : vec2(1.0, 0.0);\n}\n\n// Shape profile along the stroke. type:\n// 0 tapered — thin-fat-thin (classic brush)\n// 1 load-drag — fat start, tapers to a point\n// 2 dab — ellipse-like, round center\n// 3 even — near-constant, slight end taper\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 }\n // 0 tapered\n return smoothstep(0.0, 0.22, t) * smoothstep(1.0, 0.78, t);\n}\n\n// A single curved stroke.\n// a, b — endpoints\n// halfW — base half-width (world units)\n// bulge — signed perpendicular midpoint offset, in world units\n// shapeType — 0..3\n// bristleFreq — spatial frequency of edge raggedness\n// bristleAmp — 0..0.5 fraction of halfW chewed away at edge extrema\n// streakSeed — uniqueness seed for internal streaks\n// colAtMid — pre-sampled color at midpoint\nStrokeHit curvedStroke(vec2 p, vec2 a, vec2 b, float halfW,\n float bulge, int shapeType,\n float bristleFreq, float bristleAmp,\n float streakSeed, vec3 colAtMid) {\n vec2 ab = b - a;\n float L = length(ab);\n if (L < 1e-5) return noHit();\n vec2 tang = ab / L;\n vec2 norm = vec2(-tang.y, tang.x);\n\n vec2 rel = p - a;\n float along0 = dot(rel, tang) / L; // straight projection 0..1 along AB\n float cross0 = dot(rel, norm); // signed cross\n\n // Spine sideways offset due to bulge (parabolic)\n float bend = 4.0 * bulge * along0 * (1.0 - along0);\n float cross1 = cross0 - bend;\n\n // One refinement step: as the spine bends, the closest-point along shifts.\n // Spine tangent differs from straight tangent; first-order correction:\n float dBend = 4.0 * bulge * (1.0 - 2.0 * along0); // d(bend)/d(along)\n float along1 = along0 + (cross1 * dBend) / (L * (1.0 + dBend * dBend / (L * L)));\n\n // Bristle-ragged edge: half-width gets chewed by 1D noise along the spine\n float edgeNoise = fbm(vec2(along1 * bristleFreq, streakSeed * 7.3)) - 0.5;\n float edgeNoise2 = fbm(vec2(along1 * bristleFreq * 2.3, streakSeed * 3.1 + 17.0)) - 0.5;\n float edgeMod = 1.0 - bristleAmp * (0.6 + 0.4 * edgeNoise) * (0.5 + edgeNoise2);\n\n // Width profile along the stroke\n float shape = strokeShape(along1, shapeType);\n float halfWNow = halfW * shape * edgeMod;\n\n // Inside-segment coverage\n float cov = 0.0;\n if (along1 >= 0.0 && along1 <= 1.0 && halfWNow > 1e-6) {\n float cn = abs(cross1) / halfWNow;\n // fwidth-derived AA: half-width from the field's screen-space gradient so the\n // edge holds ~1px regardless of zoom/DPR (mirrors metaball.frag.ts:252-255).\n float aaC = max(fwidth(cn), 1e-6);\n cov = 1.0 - smoothstep(1.0 - aaC, 1.0 + aaC, cn);\n }\n\n // End-cap blobs (rounded tips, not perpendicular cuts)\n float capA = 0.0, capB = 0.0;\n {\n vec2 capCenterA = a + norm * (bulge * 0.0); // at a\n vec2 capCenterB = a + tang * L + norm * bend * 0.0 + norm * 0.0; // at b; bend is 0 at endpoints\n capCenterB = b;\n float endShapeA = strokeShape(0.02, shapeType);\n float endShapeB = strokeShape(0.98, shapeType);\n float rA = halfW * endShapeA * (0.9 + 0.2 * fbm(vec2(streakSeed * 13.0)));\n float rB = halfW * endShapeB * (0.9 + 0.2 * fbm(vec2(streakSeed * 19.0 + 5.0)));\n float dA = length(p - capCenterA);\n float dB = length(p - capCenterB);\n // fwidth-derived AA on the clean Euclidean cap distances (mirrors metaball.frag.ts:252-255).\n float aaA = max(fwidth(dA), 1e-6);\n float aaB = max(fwidth(dB), 1e-6);\n capA = 1.0 - smoothstep(rA - aaA, rA + aaA, dA);\n capB = 1.0 - smoothstep(rB - aaB, rB + aaB, dB);\n // Only apply caps if we're BEYOND the segment; otherwise the segment wins.\n capA *= (along1 < 0.05) ? 1.0 : 0.0;\n capB *= (along1 > 0.95) ? 1.0 : 0.0;\n }\n\n float coverage = max(cov, max(capA, capB));\n if (coverage < 0.002) return noHit();\n\n // Edge distance (for impasto later): 0 at edge, 1 at spine\n float edgeDist = halfWNow > 1e-6 ? clamp(1.0 - abs(cross1) / halfWNow, 0.0, 1.0) : 0.0;\n\n return StrokeHit(\n coverage,\n colAtMid,\n clamp(along1, 0.0, 1.0),\n halfWNow > 1e-6 ? cross1 / halfWNow : 0.0,\n edgeDist\n );\n}\n\n// Composite stroke over col with internal streaking + impasto edge highlight.\n// streakAmp — 0..0.2 how much internal streaks darken/lighten\n// impastoAmp — 0..1 edge catch-light strength\n// hardness — 0..1 how crisp the compositing transition is (1 = razor, 0 = creamy)\nvoid paintOver(inout vec3 col, StrokeHit s, float streakFreq, float streakAmp,\n float impastoAmp, float hardness, float streakSeed) {\n if (s.coverage < 0.002) return;\n float strokeOpacity = clamp(uStrokeAmount, 0.0, 1.0);\n if (strokeOpacity <= 0.001) return;\n vec3 c = s.color;\n\n // Internal streaks — fbm along spine, modulated by cross position. Gives\n // the loaded-brush look: some bristles carry more pigment than others.\n float streakA = fbm(vec2(s.alongT * streakFreq, s.crossN * 2.7 + streakSeed));\n float streakB = fbm(vec2(s.alongT * streakFreq * 0.6 + streakSeed * 3.7, s.crossN * 4.1));\n float streak = 0.6 * (streakA - 0.5) + 0.4 * (streakB - 0.5);\n c *= 1.0 + streak * streakAmp * 2.0;\n\n // Subtle value variance across width (hollow-center catch-light)\n float crossShade = smoothstep(0.0, 0.4, s.edgeN) * (1.0 - smoothstep(0.65, 1.0, s.edgeN));\n c *= 1.0 + crossShade * 0.05;\n\n // Impasto edge highlight — bright rim on one side of the stroke\n float rim = smoothstep(0.85, 1.0, 1.0 - s.edgeN) * step(0.0, s.crossN);\n c += impastoAmp * rim * vec3(0.18, 0.15, 0.11);\n // Shadow on the other side (darker, cooler)\n float shadow = smoothstep(0.85, 1.0, 1.0 - s.edgeN) * step(0.0, -s.crossN);\n c -= impastoAmp * shadow * 0.25 * vec3(0.10, 0.09, 0.07);\n\n float softLimit = mix(0.35, 0.98, hardness);\n float alpha = smoothstep(0.0, 1.0 - softLimit, s.coverage) * strokeOpacity;\n col = mix(col, c, alpha);\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.\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 vec2 cell = floor(p / cellSize);\n\n StrokeHit best = noHit();\n // 3x3 neighborhood\n for (int dy = -1; dy <= 1; dy++) {\n for (int dx = -1; dx <= 1; dx++) {\n vec2 cc = cell + vec2(float(dx), float(dy));\n vec2 hh = hash22(cc + seed) - 0.5;\n // Density gate — noise threshold; sparse placement.\n float gate = hash21(cc * 1.7 + seed * 0.3);\n if (gate > density) continue;\n\n vec2 center = (cc + 0.5 + hh * jitterAmt) * cellSize;\n\n // Per-stroke direction: consume the layer-provided flow, then add only\n // deterministic local perturbation so alternate stroke layers stay live.\n vec2 f = safeDir(flow);\n float angJ = (hash21(cc + seed + 11.0) - 0.5) * 0.9; // +/- 0.45 rad\n float localCurl = (fbm(center * (2.6 + seed * 0.11) + seed * 1.9) - 0.5) * 0.55 * uFlowCurl;\n vec2 dir = rotateDir(f, angJ + localCurl);\n\n float lenV = cellSize * lenMul * (0.65 + 0.55 * hash21(cc + seed + 23.0));\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", ce = "// ── Medium overlays ───────────────────────────────────────────────────────\n\n// A quick re-computation of base color for edge-mask sampling\nvec3 sampleBase(vec2 p, float t) {\n vec2 pw = domainWarp(p, t);\n float id; float vm;\n nucleiField(pw, t, id, vm);\n vec3 c = samplePalette(id);\n c *= 1.0 + uValueVariance * vm;\n return c;\n}\n\nvec3 mediumWatercolor(vec3 col, vec2 p, float t) {\n // Wet-edge cauliflowers via luma-gradient magnitude\n float eps = 0.004;\n vec3 cx1 = sampleBase(p + vec2(eps, 0.0), t);\n vec3 cx2 = sampleBase(p - vec2(eps, 0.0), t);\n vec3 cy1 = sampleBase(p + vec2(0.0, eps), t);\n vec3 cy2 = sampleBase(p - vec2(0.0, eps), t);\n float gx = dot(cx1 - cx2, W_LUMA);\n float gy = dot(cy1 - cy2, W_LUMA);\n float edge = sqrt(gx * gx + gy * gy) / (2.0 * eps);\n float mask = smoothstep(0.0, 2.5, edge);\n col *= mix(1.0, 0.78, mask * uWetEdge);\n\n // Granulation — pigment settles in paper tooth\n float paper = 0.5 * vnoise(p * 160.0) + 0.5 * vnoise(p * 360.0);\n float pigLoad = 1.0 - dot(col, W_LUMA);\n col *= 1.0 - uGranulation * pigLoad * (paper - 0.5);\n\n // Wash banding — faint horizontal wet gradient\n float band = fbm(vec2(p.x * 1.5, p.y * 0.4));\n col *= 1.0 + 0.04 * (band - 0.5);\n return col;\n}\n\nvec3 mediumPastel(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n vec2 perp = vec2(-flow.y, flow.x);\n float along = dot(p, flow) * uStrokeScale;\n float across = dot(p, perp) * uStrokeScale;\n across += 0.03 * (vnoise(p * 260.0) - 0.5);\n\n float aniso = mix(1.0, 0.18, uStrokeAnisotropy);\n float stroke = fbm(vec2(along * aniso, across));\n col *= mix(1.0, 0.82 + 0.32 * stroke, uStrokeAmount);\n\n // Pastel tooth — tiny high-frequency grain\n float tooth = vnoise(p * 800.0);\n col *= 1.0 - 0.08 * uStrokeAmount * (tooth - 0.5);\n return col;\n}\n", M = "// ── Crayon / oil-pastel — paper tooth × wax pigment (PEER medium) ─────────\n// Crayon is not strokes. It's pigment crumbs dragged across paper tooth.\n// Model: heavy 2D tooth noise at multiple scales, anisotropically stretched\n// along flow direction, multiplied into the base color. Add a slow \"waxy\n// film\" that slightly unifies hues, and occasional darker \"pressed\" spots\n// where the crayon dug in. NO straight segments. Dispatched at main() level as\n// a peer of pastel/watercolor/oil — never a branch inside mediumOil().\nvec3 mediumCrayon(vec3 col, vec2 p, float t) {\n vec2 flow = flowField(p, t);\n float ang = atan(flow.y, flow.x);\n float ca = cos(-ang), sa = sin(-ang);\n vec2 pr = vec2(p.x * ca - p.y * sa, p.x * sa + p.y * ca);\n\n // Anisotropic tooth — squished along flow, coarse cross flow.\n float aniso = mix(0.45, 0.95, uStrokeAnisotropy);\n float scale = max(uStrokeScale * 1.6, 180.0);\n\n float t1 = vnoise(vec2(pr.x * scale * aniso, pr.y * scale));\n float t2 = vnoise(vec2(pr.x * scale * aniso * 0.4, pr.y * scale * 0.4) + 11.0);\n float t3 = vnoise(vec2(pr.x * scale * aniso * 2.1, pr.y * scale * 2.1) + 23.0);\n float tooth = 0.55 * t1 + 0.30 * t2 + 0.15 * t3;\n // Center at 0, amplify\n tooth = (tooth - 0.5) * 1.4;\n\n // Multiplicative darkening where tooth is low (pigment skipped paper valleys)\n float lay = 1.0 + tooth * 0.32 * uStrokeAmount;\n vec3 result = col * lay;\n\n // Occasional pressed-in crumbs — rare darker crumbs\n float crumbs = smoothstep(0.78, 0.95, vnoise(pr * scale * 3.0));\n result *= 1.0 - crumbs * 0.18 * uStrokeAmount;\n\n // Waxy highlight film — slight lightening on tooth peaks\n float waxy = smoothstep(0.55, 0.85, t1);\n result += waxy * 0.04 * vec3(1.0);\n\n // Paper tooth overlay (subtler than oil's canvas)\n float paperTooth = vnoise(p * 340.0) - 0.5;\n result *= 1.0 + paperTooth * 0.14 * uCanvasGrain;\n\n // Broken-color pigment: stable wax/pigment patches, not temporal flicker.\n vec2 pigmentCell = floor(pr * max(scale * 0.18, 32.0));\n float pigmentMask = smoothstep(0.28, 0.82, vnoise(pr * scale * 0.21 + 19.0));\n result = brokenColorJitter(\n result,\n hash21(pigmentCell + 17.0),\n hash21(pigmentCell * 2.1 + 31.0),\n 0.45 + 0.55 * pigmentMask\n );\n\n // Crayon is saturation-amplified\n result = saturate3(result, 1.12);\n\n return result;\n}\n\nvec3 mediumOil(vec3 col, vec2 p, float t) {\n // Mode knobs (uStrokeMode) — oil-stroke modes ONLY (crayon is a peer medium,\n // uMedium==4, dispatched at main()):\n // 0 oil — balanced modern-abstract/palette-knife hybrid\n // 1 knife — palette-knife impasto: razor edges, heavy bristle/shadow\n // 3 brushwork — thick bristle brush\n int mode = uStrokeMode;\n\n // Per-mode parameters\n int shapeType = 0; // tapered\n float bristleAmp = 0.25; // 0..0.5\n float streakFreq = 9.0;\n float streakAmp = 0.09;\n float impastoAmp = 0.9;\n float hardness = 0.80; // edge compositing\n float toothScale = 240.0;\n float toothAmp = 0.09;\n float pigmentSat = 1.03;\n float densityBig = 0.65;\n float densityMed = 0.78;\n float densitySml = 0.90;\n\n if (mode == 1) { // palette knife\n shapeType = 3; // flat, even\n bristleAmp = 0.12;\n streakFreq = 4.0; streakAmp = 0.05;\n impastoAmp = 1.6;\n hardness = 0.95;\n toothAmp = 0.04;\n densityBig = 0.80; densityMed = 0.88; densitySml = 0.70;\n } else if (mode == 3) { // thick brushwork\n shapeType = 0; // tapered\n bristleAmp = 0.32;\n streakFreq = 14.0; streakAmp = 0.14;\n impastoAmp = 1.2;\n hardness = 0.85;\n toothAmp = 0.07;\n }\n\n // Scales & multipliers from uniforms\n float baseScale = max(uStrokeScale * 0.006, 0.008);\n // Three layers: big gestural, medium body, small dabs\n float sBig = baseScale * 2.4;\n float sMed = baseScale * 1.1;\n float sSml = baseScale * 0.45;\n\n float lenMulBig = mix(2.2, 3.8, uStrokeAnisotropy);\n float widMulBig = mix(0.55, 0.32, uStrokeAnisotropy);\n float lenMulMed = mix(2.0, 3.4, uStrokeAnisotropy);\n float widMulMed = mix(0.50, 0.30, uStrokeAnisotropy);\n float lenMulSml = mix(1.6, 2.6, uStrokeAnisotropy);\n float widMulSml = mix(0.45, 0.32, uStrokeAnisotropy);\n\n float jitterAmt = 0.75; // large jitter — no grid\n vec2 flow = flowField(p, t);\n\n vec3 result = col;\n\n // Layer 1 — big gestural strokes (sparse, shaping)\n StrokeHit hBig = bestOil(p, sBig, lenMulBig, widMulBig, jitterAmt * 0.55,\n densityBig, shapeType, bristleAmp, flow, t, 1.3);\n paintOver(result, hBig, streakFreq * 0.7, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 1.3);\n\n // Layer 2 — medium body strokes\n StrokeHit hMed = bestOil(p + vec2(11.3, 3.7), sMed, lenMulMed, widMulMed,\n jitterAmt, densityMed, shapeType, bristleAmp, flow, t, 2.7);\n paintOver(result, hMed, streakFreq, streakAmp,\n uImpasto * impastoAmp * uStrokeAmount, hardness, 2.7);\n\n // Layer 3 — small dabs (more frequent, smaller)\n int smlShape = (mode == 1) ? 2 : shapeType; // knife uses dabs for sparkle\n StrokeHit hSml = bestOil(p + vec2(-5.1, 8.4), sSml, lenMulSml, widMulSml,\n jitterAmt * 1.3, densitySml, smlShape,\n bristleAmp * 0.85, flow, t, 4.1);\n paintOver(result, hSml, streakFreq * 1.4, streakAmp * 0.8,\n uImpasto * impastoAmp * 0.65 * uStrokeAmount, hardness, 4.1);\n\n // Layer 4 — fill dabs (very dense, very small) — covers bald spots\n float sFill = baseScale * 0.22;\n float lenMulFill = mix(1.4, 2.0, uStrokeAnisotropy);\n float widMulFill = mix(0.50, 0.38, uStrokeAnisotropy);\n int fillShape = (mode == 1) ? 3 : 2; // knife=even, others=dab (round fills)\n StrokeHit hFill = bestOil(p + vec2(3.9, -6.2), sFill, lenMulFill, widMulFill,\n jitterAmt * 1.5, 0.95, fillShape,\n bristleAmp * 0.6, flow, t, 8.9);\n paintOver(result, hFill, streakFreq * 1.8, streakAmp * 0.6,\n uImpasto * impastoAmp * 0.4 * uStrokeAmount, hardness * 0.9, 8.9);\n\n // Optional crosshatch layer\n if (uStrokeLayers == 2) {\n vec2 flow2 = vec2(-flow.y, flow.x);\n StrokeHit hX = bestOil(p + vec2(7.3, -2.1), sMed, lenMulMed * 0.9, widMulMed,\n jitterAmt, densityMed * 0.7, shapeType, bristleAmp, flow2, t, 6.5);\n paintOver(result, hX, streakFreq, streakAmp * 0.85,\n uImpasto * impastoAmp * 0.55 * uStrokeAmount, hardness, 6.5);\n }\n\n // Canvas tooth — linen weave\n float tooth1 = vnoise(p * toothScale);\n float tooth2 = vnoise(p * toothScale * vec2(0.6, 2.4) + 37.0);\n float tooth = (0.6 * tooth1 + 0.4 * tooth2) - 0.5;\n result *= 1.0 + tooth * toothAmp * uCanvasGrain;\n\n // Pigment saturation boost\n result = saturate3(result, pigmentSat);\n\n return result;\n}", N = "\n", P = `#version 300 es
10
10
  precision highp float;
11
11
 
12
12
  in vec2 vUv;
@@ -48,6 +48,11 @@ uniform int uNoiseOctaves;
48
48
  // 0 smooth, 1 pastel, 2 watercolor, 3 oil, 4 crayon (peer medium — wax pigment
49
49
  // on paper tooth; NOT an oil-stroke sub-mode, so it dispatches at main() level).
50
50
  uniform int uMedium;
51
+ // Hue-arc method for the OKLCh palette interpolation (W5). Mirrors value.js's
52
+ // HueInterpolationMethod: 0 shorter, 1 longer, 2 increasing, 3 decreasing. Only
53
+ // consulted when the hue-arc path is requested; OKLab-rectangular ramps (the
54
+ // default for adjacent-hue stops) ignore it.
55
+ uniform int uHuePath;
51
56
  uniform int uFlowPattern; // 0 none, 1 radial, 2 swirl, 3 diagonal, 4 multi
52
57
  uniform vec2 uFlowFocal;
53
58
  uniform float uFlowAngle;
@@ -140,6 +145,64 @@ float fbm(vec2 p) {
140
145
  // proof:aurora-space-gamma seam).
141
146
  ${i}
142
147
 
148
+ // W5 — the four Ottosson OKLab/OKLCh matrices + their space fns, spliced from the
149
+ // SAME shared chunk the goo-blob uses (zero new payload, 1e-6-verified). PI is
150
+ // defined first because the chunk's oklabToOklch folds the hue into [0, 2pi).
151
+ const float PI = 3.141592653589793;
152
+ ${o}
153
+
154
+ // ── OKLCh palette interpolation (W5) ─────────────────────────────────────────
155
+ // The muddy-midtone kill: distant-hue stops interpolated by a plain linear mix()
156
+ // desaturate toward grey at the midpoint. The OKLab-rectangular lerp (L,a,b) holds
157
+ // chroma across the ramp; the OKLCh hue-arc form (used only for deliberate rainbow
158
+ // travel via uHuePath increasing/decreasing) sweeps the hue wheel without flipping.
159
+
160
+ // value.js's interpolateHue, transcribed in the NORMALIZED-TURNS domain (h_rad/TAU
161
+ // → turns, the exact .5/+1.0 branch per method, fract() wrap, *TAU back). A
162
+ // radians-native port (PI thresholds / +TAU wrap) diverges 180° at the antipode.
163
+ // methods: 0 shorter, 1 longer, 2 increasing, 3 decreasing.
164
+ float interpolateHueTurns(float h0, float h1, float t, int method) {
165
+ // h0/h1 are radians in [0, 2pi); convert to turns [0,1).
166
+ float TAU = 2.0 * PI;
167
+ float a = h0 / TAU;
168
+ float b = h1 / TAU;
169
+ float i = b - a;
170
+ if (method == 0) { // shorter
171
+ if (i > 0.5) a += 1.0; else if (i < -0.5) b += 1.0;
172
+ } else if (method == 1) { // longer
173
+ if (i > 0.0 && i < 0.5) a += 1.0; else if (i > -0.5 && i <= 0.0) b += 1.0;
174
+ } else if (method == 2) { // increasing
175
+ if (i < 0.0) b += 1.0;
176
+ } else { // decreasing
177
+ if (i > 0.0) a += 1.0;
178
+ }
179
+ float r = a + t * (b - a);
180
+ r = fract(r); // (r % 1 + 1) % 1 — fract is non-negative in GLSL
181
+ return r * TAU; // back to radians
182
+ }
183
+
184
+ // Linear-sRGB endpoint pair → OKLab-rectangular interpolation → linear sRGB. Holds
185
+ // chroma across the ramp (no grey midpoint). Used for the default/shorter ramp path.
186
+ vec3 mixPaletteOklab(vec3 linA, vec3 linB, float t) {
187
+ vec3 labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3(1.0 / 3.0)));
188
+ vec3 labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3(1.0 / 3.0)));
189
+ vec3 lab = mix(labA, labB, t);
190
+ return oklabToLinearSrgb(lab);
191
+ }
192
+
193
+ // Linear-sRGB endpoint pair → OKLCh interpolation along the uHuePath arc → linear
194
+ // sRGB. L and C lerp; H walks the chosen arc. The deliberate-rainbow path.
195
+ vec3 mixPaletteOklchArc(vec3 linA, vec3 linB, float t, int method) {
196
+ vec3 labA = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linA) * pow(abs(LINEAR_SRGB_TO_LMS * linA), vec3(1.0 / 3.0)));
197
+ vec3 labB = LMS_TO_OKLAB * (sign(LINEAR_SRGB_TO_LMS * linB) * pow(abs(LINEAR_SRGB_TO_LMS * linB), vec3(1.0 / 3.0)));
198
+ vec3 lchA = oklabToOklch(labA);
199
+ vec3 lchB = oklabToOklch(labB);
200
+ float L = mix(lchA.x, lchB.x, t);
201
+ float C = mix(lchA.y, lchB.y, t);
202
+ float H = interpolateHueTurns(lchA.z, lchB.z, t, method);
203
+ return oklabToLinearSrgb(oklchToOklab(vec3(L, C, H)));
204
+ }
205
+
143
206
  // Interleaved Gradient Noise (Jimenez) — a 1-LSB triangular dither applied in
144
207
  // DISPLAY space AFTER the OETF, so 8-bit mid-tone banding on the soft gradient is
145
208
  // quantization-dithered at the value being quantized (post-transfer, not linear).
@@ -207,10 +270,10 @@ vec2 domainWarp(vec2 p, float t) {
207
270
  return warped;
208
271
  }
209
272
 
210
- ` + re + N + ie + N + "// ── Color utils ───────────────────────────────────────────────────────────\nconst vec3 W_LUMA = vec3(0.2126, 0.7152, 0.0722);\n\nvec3 hueShift(vec3 c, float degrees) {\n float a = radians(degrees);\n float co = cos(a), si = sin(a);\n mat3 m = mat3(\n 0.299 + 0.701 * co + 0.168 * si,\n 0.587 - 0.587 * co + 0.330 * si,\n 0.114 - 0.114 * co - 0.497 * si,\n\n 0.299 - 0.299 * co - 0.328 * si,\n 0.587 + 0.413 * co + 0.035 * si,\n 0.114 - 0.114 * co + 0.292 * si,\n\n 0.299 - 0.300 * co + 1.250 * si,\n 0.587 - 0.588 * co - 1.050 * si,\n 0.114 + 0.886 * co - 0.203 * si\n );\n return m * c;\n}\n\nvec3 brokenColorJitter(vec3 c, float hueSeed, float valueSeed, float strength) {\n float amt = clamp(uBrokenColor * strength, 0.0, 1.0);\n if (amt <= 0.001) return c;\n float hueDeg = (hueSeed - 0.5) * 32.0 * amt;\n float valueMul = 1.0 + (valueSeed - 0.5) * 0.28 * amt;\n return max(hueShift(c, hueDeg) * valueMul, vec3(0.0));\n}\n\nvec3 saturate3(vec3 c, float amt) {\n float l = dot(c, W_LUMA);\n return mix(vec3(l), c, amt);\n}\n\n" + ae + N + j + N + oe + N + M + N + "\n// ── Main ──────────────────────────────────────────────────────────────────\nvoid main() {\n // Normalized 0..1 coordinates for nuclei, domain warp, and medium sampling.\n vec2 uv = vUv;\n vec2 pN = uv;\n\n float t = uTime;\n\n // Warp in pN space\n vec2 p_warp = domainWarp(pN, t);\n\n // Composition\n float paletteId; float valueMod;\n nucleiField(p_warp, t, paletteId, valueMod);\n vec3 col = samplePalette(paletteId);\n col *= 1.0 + uValueVariance * valueMod;\n\n // Breath — slow global luminance wobble\n float breath = sin(t * 6.2831 / max(uBreathPeriod, 1.0));\n col *= 1.0 + uBreathDepth * breath * 0.5;\n\n // Medium — crayon (4) is a peer dispatched here, not a mediumOil sub-mode.\n if (uMedium == 1) col = mediumPastel(col, pN, t);\n else if (uMedium == 2) col = mediumWatercolor(col, pN, t);\n else if (uMedium == 3) col = mediumOil(col, pN, t);\n else if (uMedium == 4) col = mediumCrayon(col, pN, t);\n\n // Saturation trim\n col = saturate3(col, uSaturation);\n\n // Tonemap + film grain\n col = aces(col);\n float grain = hash21(gl_FragCoord.xy + t * 17.0);\n col += (grain - 0.5) * uPaperGrain;\n\n col = clamp(col * 0.985 + 0.008, 0.0, 1.0);\n\n // MANDATORY OETF — closes the seam (mirrors metaball.frag.ts:278). The whole\n // pipeline above (linear palette LUT, ACES tonemap, grain) runs in LINEAR; without\n // this transfer the aurora ships ~2.2× too dark (linear 0.5 → ~0.215, not ~0.735).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF — breaks 8-bit\n // mid-tone banding on the soft gradient. Texture-free (the canonical Jimenez IGN).\n col += (1.0 / 255.0) * (ign(gl_FragCoord.xy) - 0.5);\n\n // Premultiply operates on DISPLAY-space color (matches the blob: OETF, then premul).\n fragColor = vec4(col * uAlpha, uAlpha);\n}\n", F = "[Aurora]", I = /* @__PURE__ */ "uTime.uPalette.uStopCount.uNucleiCount.uNucleiPos.uNucleiRadius.uNucleiPaletteBias.uNucleiValueBias.uNucleiDriftRadius.uNucleiDriftPhase.uNucleiElong.uNucleiAngle.uSoftmaxBeta.uValueVariance.uWarpAmount.uWarpScale.uWarpDrift.uWarpMode.uNoiseOctaves.uMedium.uFlowPattern.uFlowFocal.uFlowAngle.uFlowCurl.uCursor.uCursorStrength.uCursorRadius.uStrokeAmount.uStrokeScale.uStrokeAnisotropy.uStrokeLayers.uStrokeMode.uWetEdge.uGranulation.uImpasto.uBrokenColor.uCanvasGrain.uNucleiDrift.uPaletteDrift.uBreathDepth.uBreathPeriod.uSaturation.uPaperGrain.uAlpha".split(".");
273
+ ` + ie + N + ae + N + "// ── Color utils (OKLCh — W5; the sRGB YIQ hueShift matrix is DELETED) ──────────\nconst vec3 W_LUMA = vec3(0.2126, 0.7152, 0.0722);\n\n// Linear sRGB → OKLab (the cbrt-LMS path; the goo-blob's srgbToOklab takes gamma,\n// here the color field is already linear so we skip the OETF inverse).\nvec3 linOklab(vec3 lin) {\n vec3 lms = LINEAR_SRGB_TO_LMS * lin;\n return LMS_TO_OKLAB * (sign(lms) * pow(abs(lms), vec3(1.0 / 3.0)));\n}\n\n// Broken color (W5) — hue + chroma jitter at fixed PERCEPTUAL lightness, in OKLCh.\n// Broken color is hue variation at constant VALUE, which only OKLCh makes true (the\n// prior YIQ-style sRGB rotation muddied value). This is the seam AW.W4's van-Gogh /\n// oil-pastel per-stroke pigment jitter consumes (the pigment-variation read).\nvec3 brokenColorJitter(vec3 c, float hueSeed, float valueSeed, float strength) {\n float amt = clamp(uBrokenColor * strength, 0.0, 1.0);\n if (amt <= 0.001) return c;\n vec3 lch = oklabToOklch(linOklab(c));\n // ±~16° hue swing + ±~12% chroma swing; L held so value reads constant.\n lch.z += (hueSeed - 0.5) * (32.0 * PI / 180.0) * amt;\n lch.y = max(lch.y * (1.0 + (valueSeed - 0.5) * 0.28 * amt), 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n}\n\n// Saturation (W5) — chroma scale at fixed L/H in OKLCh (the perceptual saturation,\n// replacing the luma-mix sRGB form). amt=1 is identity; amt<1 desaturates toward\n// the achromatic axis with NO lightness shift (the muddy-luma-mix grey is gone).\nvec3 saturate3(vec3 c, float amt) {\n vec3 lch = oklabToOklch(linOklab(c));\n lch.y = max(lch.y * amt, 0.0);\n return max(oklabToLinearSrgb(oklchToOklab(lch)), vec3(0.0));\n}\n\n" + oe + N + ce + N + se + N + M + N + "\n// ── Main ──────────────────────────────────────────────────────────────────\nvoid main() {\n // Normalized 0..1 coordinates for nuclei, domain warp, and medium sampling.\n vec2 uv = vUv;\n vec2 pN = uv;\n\n float t = uTime;\n\n // Warp in pN space\n vec2 p_warp = domainWarp(pN, t);\n\n // Composition\n float paletteId; float valueMod;\n nucleiField(p_warp, t, paletteId, valueMod);\n vec3 col = samplePalette(paletteId);\n col *= 1.0 + uValueVariance * valueMod;\n\n // Breath — slow global luminance wobble\n float breath = sin(t * 6.2831 / max(uBreathPeriod, 1.0));\n col *= 1.0 + uBreathDepth * breath * 0.5;\n\n // Medium — crayon (4) is a peer dispatched here, not a mediumOil sub-mode.\n if (uMedium == 1) col = mediumPastel(col, pN, t);\n else if (uMedium == 2) col = mediumWatercolor(col, pN, t);\n else if (uMedium == 3) col = mediumOil(col, pN, t);\n else if (uMedium == 4) col = mediumCrayon(col, pN, t);\n\n // Saturation trim\n col = saturate3(col, uSaturation);\n\n // Tonemap + film grain\n col = aces(col);\n float grain = hash21(gl_FragCoord.xy + t * 17.0);\n col += (grain - 0.5) * uPaperGrain;\n\n col = clamp(col * 0.985 + 0.008, 0.0, 1.0);\n\n // MANDATORY OETF — closes the seam (mirrors metaball.frag.ts:278). The whole\n // pipeline above (linear palette LUT, ACES tonemap, grain) runs in LINEAR; without\n // this transfer the aurora ships ~2.2× too dark (linear 0.5 → ~0.215, not ~0.735).\n col = linearToSrgb(col);\n\n // 1-LSB triangular IGN dither in DISPLAY space, AFTER the OETF — breaks 8-bit\n // mid-tone banding on the soft gradient. Texture-free (the canonical Jimenez IGN).\n col += (1.0 / 255.0) * (ign(gl_FragCoord.xy) - 0.5);\n\n // Premultiply operates on DISPLAY-space color (matches the blob: OETF, then premul).\n fragColor = vec4(col * uAlpha, uAlpha);\n}\n", F = "[Aurora]", I = /* @__PURE__ */ "uTime.uPalette.uStopCount.uNucleiCount.uNucleiPos.uNucleiRadius.uNucleiPaletteBias.uNucleiValueBias.uNucleiDriftRadius.uNucleiDriftPhase.uNucleiElong.uNucleiAngle.uSoftmaxBeta.uValueVariance.uWarpAmount.uWarpScale.uWarpDrift.uWarpMode.uNoiseOctaves.uMedium.uHuePath.uFlowPattern.uFlowFocal.uFlowAngle.uFlowCurl.uCursor.uCursorStrength.uCursorRadius.uStrokeAmount.uStrokeScale.uStrokeAnisotropy.uStrokeLayers.uStrokeMode.uWetEdge.uGranulation.uImpasto.uBrokenColor.uCanvasGrain.uNucleiDrift.uPaletteDrift.uBreathDepth.uBreathPeriod.uSaturation.uPaperGrain.uAlpha".split(".");
211
274
  function L(e, t, n) {
212
- let i = o(e, e.VERTEX_SHADER, t, F), a = o(e, e.FRAGMENT_SHADER, n, F), s = r(e, i, a, F);
213
- e.useProgram(s);
275
+ let i = s(e, e.VERTEX_SHADER, t, F), a = s(e, e.FRAGMENT_SHADER, n, F), o = r(e, i, a, F);
276
+ e.useProgram(o);
214
277
  let c = e.createVertexArray();
215
278
  e.bindVertexArray(c);
216
279
  let l = e.createBuffer();
@@ -222,12 +285,12 @@ function L(e, t, n) {
222
285
  -1,
223
286
  3
224
287
  ]), e.STATIC_DRAW);
225
- let u = e.getAttribLocation(s, "aPos");
288
+ let u = e.getAttribLocation(o, "aPos");
226
289
  e.enableVertexAttribArray(u), e.vertexAttribPointer(u, 2, e.FLOAT, !1, 0, 0);
227
290
  let d = {};
228
- for (let t of I) d[t] = e.getUniformLocation(s, t);
291
+ for (let t of I) d[t] = e.getUniformLocation(o, t);
229
292
  return {
230
- program: s,
293
+ program: o,
231
294
  vs: i,
232
295
  fs: a,
233
296
  uniforms: d,
@@ -242,7 +305,7 @@ function L(e, t, n) {
242
305
  function R(e, t = 8, n) {
243
306
  let r = n ?? new Float32Array(t * 3), i = Math.min(e.length, t);
244
307
  for (let t = 0; t < i; t++) {
245
- let [n, i, a] = u(e[t]);
308
+ let [n, i, a] = d(e[t]);
246
309
  r[t * 3 + 0] = n, r[t * 3 + 1] = i, r[t * 3 + 2] = a;
247
310
  }
248
311
  for (let e = i; e < t; e++) r[e * 3 + 0] = 0, r[e * 3 + 1] = 0, r[e * 3 + 2] = 0;
@@ -250,50 +313,74 @@ function R(e, t = 8, n) {
250
313
  }
251
314
  function z(e) {
252
315
  if (e.length === 0) return "transparent";
253
- if (e.length === 1) return d(e[0]);
316
+ if (e.length === 1) return f(e[0]);
254
317
  let t = e.length;
255
318
  return `linear-gradient(135deg, ${e.map((e, n) => {
256
319
  let r = Math.round(n / (t - 1) * 100);
257
- return `${d(e)} ${r}%`;
320
+ return `${f(e)} ${r}%`;
258
321
  }).join(", ")})`;
259
322
  }
260
323
  function B(e) {
261
- let t = e.replace("#", ""), n = parseInt(t.slice(0, 2), 16), r = parseInt(t.slice(2, 4), 16), i = parseInt(t.slice(4, 6), 16), [a, o, s] = te(n / 255, r / 255, i / 255), [c, l, u] = k(a, o, s);
324
+ let t = e.replace("#", ""), n = parseInt(t.slice(0, 2), 16), r = parseInt(t.slice(2, 4), 16), i = parseInt(t.slice(4, 6), 16), [a, o, s] = ne(n / 255, r / 255, i / 255), [c, l, u] = A(a, o, s);
262
325
  return {
263
326
  L: c,
264
327
  C: l,
265
328
  h: u
266
329
  };
267
330
  }
268
- var V = [.35, .95];
269
- function H(e, t = {}) {
270
- let n = typeof e == "string" ? f(e) : e, { stopCount: r = 4, harmony: i = "analogous", lightnessSpread: a = .32, chromaFalloff: o = .85, hueSpread: s = 28 } = t, c = Math.max(2, Math.min(8, Math.round(r))), [l, u] = V, d = n.L - a / 2, p = n.L + a / 2;
271
- d < l && (p += l - d, d = l), p > u && (d -= p - u, p = u), d = Math.max(l, d), p = Math.min(u, p);
272
- let m = [];
273
- for (let e = 0; e < c; e++) {
274
- let t = c === 1 ? 0 : e / (c - 1), r = d + (p - d) * t, a = n.C * (1 - (1 - o) * t), l = U(n.h, i, s, t);
275
- m.push(W({
331
+ var V = {
332
+ complementary: "longer",
333
+ "split-complementary": "longer",
334
+ analogous: "shorter",
335
+ monochrome: "shorter",
336
+ triad: "increasing",
337
+ tetradic: "increasing"
338
+ }, H = [.35, .95];
339
+ function U(e, t = {}) {
340
+ let n = typeof e == "string" ? p(e) : e, { stopCount: r = 4, harmony: i = "analogous", lightnessSpread: a = .32, chromaFalloff: o = .85, hueSpread: s = 28, lightnessEasing: c = "linear", chromaEasing: l = "bell", temperatureShift: u = 0 } = t, d = Math.max(2, Math.min(8, Math.round(r))), [f, m] = H, h = n.L - a / 2, g = n.L + a / 2;
341
+ h < f && (g += f - h, h = f), g > m && (h -= g - m, g = m), h = Math.max(f, h), g = Math.min(m, g);
342
+ let _ = [];
343
+ for (let e = 0; e < d; e++) {
344
+ let t = d === 1 ? 0 : e / (d - 1), r = h + (g - h) * W(t, c), a = n.C * o, f;
345
+ if (l === "bell") f = a + (n.C - a) * G(t);
346
+ else {
347
+ let e = l === "sine" ? W(t, "sine") : t;
348
+ f = n.C - (n.C - a) * e;
349
+ }
350
+ let p = ue(n.h, i, s, t);
351
+ u > 0 && (p = le(p, t, u)), _.push(de({
276
352
  L: r,
277
- C: a,
278
- h: l
353
+ C: f,
354
+ h: p
279
355
  }));
280
356
  }
281
- return m;
357
+ return _;
358
+ }
359
+ function W(e, t) {
360
+ return t === "sine" ? .5 - .5 * Math.cos(Math.PI * e) : e;
361
+ }
362
+ function G(e) {
363
+ return Math.sin(Math.PI * e);
282
364
  }
283
- function U(e, t, n, r) {
284
- let i = (e) => (e % 360 + 360) % 360;
365
+ function le(e, t, n) {
366
+ return ((e) => (e % 360 + 360) % 360)(e + (t - .5) * 2 * n * 22);
367
+ }
368
+ function ue(e, t, n, r) {
369
+ let i = (e) => (e % 360 + 360) % 360, a = V[t], o = (e, t) => i(D(i(e) / 360, i(t) / 360, r, a) * 360);
285
370
  switch (t) {
286
371
  case "monochrome": return i(e);
287
- case "complementary": return i(e + 180 * r);
288
- case "triad": return i(e + 240 * r);
289
- default: return i(e + n * (r - .5) * 2);
372
+ case "complementary": return o(e, e + 180);
373
+ case "split-complementary": return o(e + 150, e + 210);
374
+ case "triad": return o(e, e + 240);
375
+ case "tetradic": return o(e, e + 270);
376
+ default: return o(e - n, e + n);
290
377
  }
291
378
  }
292
- function W(e) {
293
- let [t, n, r] = A(e.L, e.C, e.h), [i, a, o] = E(t, n, r), [s, c, l] = k(i, a, o), u = c;
379
+ function de(e) {
380
+ let [t, n, r] = j(e.L, e.C, e.h), [i, a, o] = E(t, n, r), [s, c, l] = A(i, a, o), u = c;
294
381
  for (let e = 0; e < 6; e++) {
295
- let [e, t, n] = A(s, u, l), [r, i, a] = O(e, t, n);
296
- if (D(r, i, a)) break;
382
+ let [e, t, n] = j(s, u, l), [r, i, a] = k(e, t, n);
383
+ if (O(r, i, a)) break;
297
384
  u *= .999;
298
385
  }
299
386
  return {
@@ -302,21 +389,112 @@ function W(e) {
302
389
  h: l
303
390
  };
304
391
  }
392
+ function fe(e) {
393
+ let t = [.33, .67], n = [
394
+ [t[0], t[0]],
395
+ [t[1], t[1]],
396
+ [t[1], t[0]],
397
+ [t[0], t[1]],
398
+ [.5, .5],
399
+ [.5, t[0]]
400
+ ], r = Math.max(1, Math.min(n.length, e));
401
+ return Array.from({ length: r }, (e, t) => {
402
+ let [i, a] = n[t];
403
+ return {
404
+ x: i,
405
+ y: a,
406
+ radius: .5,
407
+ paletteBias: r === 1 ? 0 : t / (r - 1),
408
+ valueBias: (t % 2 == 0 ? 1 : -1) * .05,
409
+ driftRadius: .045,
410
+ driftPhase: t * 2.4 % (Math.PI * 2)
411
+ };
412
+ });
413
+ }
414
+ var pe = {
415
+ atmospheric: {
416
+ harmony: "analogous",
417
+ medium: "smooth",
418
+ temperatureShift: .3,
419
+ chromaEasing: "bell",
420
+ saturation: .95,
421
+ warpAmount: .55,
422
+ valueVariance: .08,
423
+ breathDepth: .06,
424
+ zones: 2
425
+ },
426
+ painterly: {
427
+ harmony: "split-complementary",
428
+ medium: "oil",
429
+ temperatureShift: .6,
430
+ chromaEasing: "bell",
431
+ saturation: 1.05,
432
+ warpAmount: .45,
433
+ valueVariance: .12,
434
+ breathDepth: .04,
435
+ zones: 3
436
+ },
437
+ vivid: {
438
+ harmony: "triad",
439
+ medium: "watercolor",
440
+ temperatureShift: .2,
441
+ chromaEasing: "linear",
442
+ saturation: 1.2,
443
+ warpAmount: .5,
444
+ valueVariance: .14,
445
+ breathDepth: .05,
446
+ zones: 3
447
+ },
448
+ muted: {
449
+ harmony: "monochrome",
450
+ medium: "pastel",
451
+ temperatureShift: .15,
452
+ chromaEasing: "bell",
453
+ saturation: .78,
454
+ warpAmount: .4,
455
+ valueVariance: .06,
456
+ breathDepth: .03,
457
+ zones: 2
458
+ }
459
+ };
460
+ function me(e, t = "atmospheric") {
461
+ let n = pe[t], r = U(e, {
462
+ harmony: n.harmony,
463
+ temperatureShift: n.temperatureShift,
464
+ chromaEasing: n.chromaEasing,
465
+ stopCount: 4
466
+ });
467
+ return {
468
+ ...u,
469
+ palette: r,
470
+ nuclei: fe(n.zones),
471
+ medium: n.medium,
472
+ saturation: n.saturation,
473
+ warpAmount: n.warpAmount,
474
+ valueVariance: n.valueVariance,
475
+ breathDepth: n.breathDepth
476
+ };
477
+ }
305
478
  //#endregion
306
479
  //#region src/components/custom/aurora/composables/uniformBridge.ts
307
- var G = {
480
+ var K = {
308
481
  smooth: 0,
309
482
  pastel: 1,
310
483
  watercolor: 2,
311
484
  oil: 3,
312
485
  crayon: 4
313
- }, K = {
486
+ }, he = {
487
+ shorter: 0,
488
+ longer: 1,
489
+ increasing: 2,
490
+ decreasing: 3
491
+ }, ge = {
314
492
  none: 0,
315
493
  radial: 1,
316
494
  swirl: 2,
317
495
  diagonal: 3,
318
496
  multi: 4
319
- }, se = {
497
+ }, _e = {
320
498
  fbm: 0,
321
499
  cellular: 1,
322
500
  hybrid: 2
@@ -325,13 +503,13 @@ var G = {
325
503
  knife: 1,
326
504
  chunky: 3
327
505
  };
328
- function ce(e) {
329
- return e.medium === "oil" && e.strokeMode === "crayon" ? G.crayon : G[e.medium];
506
+ function ve(e) {
507
+ return e.medium === "oil" && e.strokeMode === "crayon" ? K.crayon : K[e.medium];
330
508
  }
331
- function le(e) {
509
+ function ye(e) {
332
510
  return e.strokeMode === "crayon" ? q.oil : q[e.strokeMode];
333
511
  }
334
- function ue(e, t, n, r) {
512
+ function be(e, t, n, r) {
335
513
  let i = (e) => 1 - e, a = {
336
514
  palette: new Float32Array(24),
337
515
  pos: new Float32Array(12),
@@ -352,13 +530,13 @@ function ue(e, t, n, r) {
352
530
  a.pos[e * 2 + 0] = t.x, a.pos[e * 2 + 1] = i(t.y), a.rad[e] = t.radius, a.pb[e] = t.paletteBias, a.vb[e] = t.valueBias, a.dr[e] = t.driftRadius, a.dp[e] = t.driftPhase, a.elong[e] = t.elongation ?? 1, a.angle[e] = -(t.angle ?? 0) * Math.PI / 180;
353
531
  }
354
532
  for (let e = s; e < 6; e++) a.pos[e * 2 + 0] = 0, a.pos[e * 2 + 1] = 0, a.rad[e] = 0, a.pb[e] = 0, a.vb[e] = 0, a.dr[e] = 0, a.dp[e] = 0, a.elong[e] = 1, a.angle[e] = 0;
355
- e.uniform2fv(n.uNucleiPos, a.pos), e.uniform1fv(n.uNucleiRadius, a.rad), e.uniform1fv(n.uNucleiPaletteBias, a.pb), e.uniform1fv(n.uNucleiValueBias, a.vb), e.uniform1fv(n.uNucleiDriftRadius, a.dr), e.uniform1fv(n.uNucleiDriftPhase, a.dp), e.uniform1fv(n.uNucleiElong, a.elong), e.uniform1fv(n.uNucleiAngle, a.angle), e.uniform1f(n.uSoftmaxBeta, o.softmaxBeta), e.uniform1f(n.uValueVariance, o.valueVariance), e.uniform1f(n.uWarpAmount, o.warpAmount), e.uniform1f(n.uWarpScale, o.warpScale), e.uniform1f(n.uWarpDrift, o.warpDrift), e.uniform1i(n.uWarpMode, se[o.warpMode]), e.uniform1i(n.uNoiseOctaves, o.noiseOctaves), e.uniform1i(n.uMedium, ce(o)), e.uniform1i(n.uFlowPattern, K[o.flow.pattern]), e.uniform2f(n.uFlowFocal, o.flow.focalX, i(o.flow.focalY)), e.uniform1f(n.uFlowAngle, o.flow.angle), e.uniform1f(n.uFlowCurl, o.flow.curl), e.uniform2f(n.uCursor, r.x, i(r.y)), e.uniform1f(n.uCursorStrength, r.strength), e.uniform1f(n.uCursorRadius, r.radius), e.uniform1f(n.uStrokeAmount, o.strokeAmount), e.uniform1f(n.uStrokeScale, o.strokeScale), e.uniform1f(n.uStrokeAnisotropy, o.strokeAnisotropy), e.uniform1i(n.uStrokeLayers, o.strokeLayers), e.uniform1i(n.uStrokeMode, le(o)), e.uniform1f(n.uWetEdge, o.wetEdge), e.uniform1f(n.uGranulation, o.granulation), e.uniform1f(n.uImpasto, o.impasto), e.uniform1f(n.uBrokenColor, o.brokenColor), e.uniform1f(n.uCanvasGrain, o.canvasGrain), e.uniform1f(n.uNucleiDrift, o.nucleiDrift), e.uniform1f(n.uPaletteDrift, o.paletteDrift), e.uniform1f(n.uBreathDepth, o.breathDepth), e.uniform1f(n.uBreathPeriod, o.breathPeriod), e.uniform1f(n.uSaturation, o.saturation), e.uniform1f(n.uPaperGrain, o.paperGrain), e.uniform1f(n.uAlpha, o.alpha);
533
+ e.uniform2fv(n.uNucleiPos, a.pos), e.uniform1fv(n.uNucleiRadius, a.rad), e.uniform1fv(n.uNucleiPaletteBias, a.pb), e.uniform1fv(n.uNucleiValueBias, a.vb), e.uniform1fv(n.uNucleiDriftRadius, a.dr), e.uniform1fv(n.uNucleiDriftPhase, a.dp), e.uniform1fv(n.uNucleiElong, a.elong), e.uniform1fv(n.uNucleiAngle, a.angle), e.uniform1f(n.uSoftmaxBeta, o.softmaxBeta), e.uniform1f(n.uValueVariance, o.valueVariance), e.uniform1f(n.uWarpAmount, o.warpAmount), e.uniform1f(n.uWarpScale, o.warpScale), e.uniform1f(n.uWarpDrift, o.warpDrift), e.uniform1i(n.uWarpMode, _e[o.warpMode]), e.uniform1i(n.uNoiseOctaves, o.noiseOctaves), e.uniform1i(n.uMedium, ve(o)), e.uniform1i(n.uHuePath, he[o.huePath ?? "shorter"]), e.uniform1i(n.uFlowPattern, ge[o.flow.pattern]), e.uniform2f(n.uFlowFocal, o.flow.focalX, i(o.flow.focalY)), e.uniform1f(n.uFlowAngle, o.flow.angle), e.uniform1f(n.uFlowCurl, o.flow.curl), e.uniform2f(n.uCursor, r.x, i(r.y)), e.uniform1f(n.uCursorStrength, r.strength), e.uniform1f(n.uCursorRadius, r.radius), e.uniform1f(n.uStrokeAmount, o.strokeAmount), e.uniform1f(n.uStrokeScale, o.strokeScale), e.uniform1f(n.uStrokeAnisotropy, o.strokeAnisotropy), e.uniform1i(n.uStrokeLayers, o.strokeLayers), e.uniform1i(n.uStrokeMode, ye(o)), e.uniform1f(n.uWetEdge, o.wetEdge), e.uniform1f(n.uGranulation, o.granulation), e.uniform1f(n.uImpasto, o.impasto), e.uniform1f(n.uBrokenColor, o.brokenColor), e.uniform1f(n.uCanvasGrain, o.canvasGrain), e.uniform1f(n.uNucleiDrift, o.nucleiDrift), e.uniform1f(n.uPaletteDrift, o.paletteDrift), e.uniform1f(n.uBreathDepth, o.breathDepth), e.uniform1f(n.uBreathPeriod, o.breathPeriod), e.uniform1f(n.uSaturation, o.saturation), e.uniform1f(n.uPaperGrain, o.paperGrain), e.uniform1f(n.uAlpha, o.alpha);
356
534
  };
357
535
  }
358
536
  //#endregion
359
537
  //#region src/components/custom/aurora/composables/cursorModel.ts
360
- var J = .22, de = .18, fe = .992;
361
- function pe() {
538
+ var J = .22, xe = .18, Y = .992;
539
+ function Se() {
362
540
  return {
363
541
  x: .5,
364
542
  y: .5,
@@ -369,15 +547,15 @@ function pe() {
369
547
  radius: .25
370
548
  };
371
549
  }
372
- function me(e) {
373
- e.x += (e.targetX - e.x) * J, e.y += (e.targetY - e.y) * J, e.strength += (e.targetStrength - e.strength) * de, e.targetStrength *= fe;
550
+ function Ce(e) {
551
+ e.x += (e.targetX - e.x) * J, e.y += (e.targetY - e.y) * J, e.strength += (e.targetStrength - e.strength) * xe, e.targetStrength *= Y;
374
552
  }
375
- function he(e) {
553
+ function we(e) {
376
554
  return e.targetStrength > .001 || e.strength > .001 || Math.abs(e.x - e.targetX) > .001 || Math.abs(e.y - e.targetY) > .001;
377
555
  }
378
556
  //#endregion
379
557
  //#region src/components/custom/aurora/composables/frameLoop.ts
380
- function ge(e) {
558
+ function Te(e) {
381
559
  let { gl: t, prog: n, uniforms: r, cursor: i, getConfig: a, getReducedMotion: o } = e, s = (e) => 1 - e;
382
560
  function c(e) {
383
561
  t.useProgram(n), t.uniform2f(r.uCursor, i.x, s(i.y)), t.uniform1f(r.uCursorStrength, i.strength), t.uniform1f(r.uCursorRadius, i.radius), t.uniform1f(r.uTime, e), t.clear(t.COLOR_BUFFER_BIT), t.drawArrays(t.TRIANGLES, 0, 3);
@@ -385,25 +563,25 @@ function ge(e) {
385
563
  function l() {
386
564
  if (o()) return !1;
387
565
  let e = a();
388
- return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0 ? !0 : he(i);
566
+ return e.nucleiDrift !== 0 || e.paletteDrift !== 0 || e.breathDepth !== 0 || e.warpDrift !== 0 ? !0 : we(i);
389
567
  }
390
568
  return {
391
569
  frame: (e) => {
392
- me(i), c(e);
570
+ Ce(i), c(e);
393
571
  },
394
572
  needsAnimation: l
395
573
  };
396
574
  }
397
575
  //#endregion
398
576
  //#region src/components/custom/aurora/composables/runtime.ts
399
- function _e(e) {
577
+ function Ee(e) {
400
578
  return e.mode === "capture" ? !0 : e.initStrategy === "eager";
401
579
  }
402
- function Y(e) {
580
+ function De(e) {
403
581
  return typeof e.preserveDrawingBuffer == "boolean" ? e.preserveDrawingBuffer : e.mode === "capture";
404
582
  }
405
583
  function X(e, t, r = {}) {
406
- let i = Y(r), o = t, s = pe(), c = null, l = a(e, {
584
+ let i = De(r), o = t, s = Se(), c = null, l = a(e, {
407
585
  mode: r.mode === "capture" ? "capture" : "live",
408
586
  contextAttrs: {
409
587
  antialias: !1,
@@ -412,12 +590,12 @@ function X(e, t, r = {}) {
412
590
  preserveDrawingBuffer: i
413
591
  },
414
592
  setup: (t) => {
415
- let { program: r, vs: i, fs: a, uniforms: u, geometry: d } = L(t, ne, P);
593
+ let { program: r, vs: i, fs: a, uniforms: u, geometry: d } = L(t, re, P);
416
594
  function f() {
417
595
  let i = n(), a = e.clientWidth || e.parentElement?.clientWidth || 1, o = e.clientHeight || e.parentElement?.clientHeight || 1, s = Math.max(1, Math.floor(a * i)), c = Math.max(1, Math.floor(o * i));
418
596
  e.width = s, e.height = c, t.viewport(0, 0, s, c), t.useProgram(r);
419
597
  }
420
- let p = ue(t, r, u, s), m = ge({
598
+ let p = be(t, r, u, s), m = Te({
421
599
  gl: t,
422
600
  prog: r,
423
601
  uniforms: u,
@@ -452,7 +630,7 @@ function X(e, t, r = {}) {
452
630
  function p(e) {
453
631
  l.wake();
454
632
  }
455
- return r.mode !== "capture" && _e(r) && l.arm(), {
633
+ return r.mode !== "capture" && Ee(r) && l.arm(), {
456
634
  arm: () => l.arm(),
457
635
  update: (e) => {
458
636
  o = e, c?.(e), l.wake();
@@ -474,7 +652,7 @@ function Z(e) {
474
652
  }
475
653
  //#endregion
476
654
  //#region src/components/custom/aurora/composables/useAurora.ts
477
- function ve(e) {
655
+ function Oe(e) {
478
656
  if (typeof window > "u") return () => {};
479
657
  if (typeof window.requestIdleCallback == "function") {
480
658
  let t = window.requestIdleCallback(() => e(), { timeout: 2e3 });
@@ -492,7 +670,7 @@ function ve(e) {
492
670
  };
493
671
  }
494
672
  function Q(e, t, n = {}, r = {}) {
495
- let i = Z(t), a = r.renderMode === "css", o = null, s = null, c = null, l = null, u = null, d = null, f = !1, m = w(!1), h = n.mode === "capture" || n.initStrategy === "eager";
673
+ let i = Z(t), a = r.renderMode === "css", o = null, s = null, c = null, l = null, u = null, d = null, f = !1, p = C(!1), h = n.mode === "capture" || n.initStrategy === "eager";
496
674
  function g() {
497
675
  d && o && o.setReducedMotion(d.matches);
498
676
  }
@@ -513,10 +691,10 @@ function Q(e, t, n = {}, r = {}) {
513
691
  _(e);
514
692
  return;
515
693
  }
516
- m.value = !0, d = window.matchMedia("(prefers-reduced-motion: reduce)"), d.addEventListener("change", g), s = T(i, (e) => o?.update(e), { deep: !0 });
694
+ p.value = !0, d = window.matchMedia("(prefers-reduced-motion: reduce)"), d.addEventListener("change", g), s = T(i, (e) => o?.update(e), { deep: !0 });
517
695
  }
518
696
  }
519
- return x(() => {
697
+ return S(() => {
520
698
  let t = e.value;
521
699
  if (!t || a) return;
522
700
  try {
@@ -532,17 +710,17 @@ function Q(e, t, n = {}, r = {}) {
532
710
  v();
533
711
  return;
534
712
  }
535
- u = p(e, {
713
+ u = m(e, {
536
714
  pause: () => o?.pause("off-screen"),
537
715
  resume: () => o?.resume("off-screen")
538
716
  }, { pauseWhenHidden: !1 });
539
717
  let r = u.isIntersecting;
540
718
  c = T(r, (e) => {
541
- !e || f || l || (l = ve(() => {
719
+ !e || f || l || (l = Oe(() => {
542
720
  l = null, r.value && v();
543
721
  }));
544
722
  }, { immediate: !0 });
545
- }), b(() => {
723
+ }), x(() => {
546
724
  l?.(), c?.(), s?.(), u?.dispose(), d?.removeEventListener("change", g), o?.dispose(), o = null;
547
725
  }), {
548
726
  setCursor: (e, t, n) => o?.setCursor(e, t, n),
@@ -551,7 +729,7 @@ function Q(e, t, n = {}, r = {}) {
551
729
  renderAt: (e) => o?.renderAt(e),
552
730
  pause: () => o?.pause(),
553
731
  resume: () => o?.resume(),
554
- isArmed: C(m)
732
+ isArmed: te(p)
555
733
  };
556
734
  }
557
735
  //#endregion
@@ -564,20 +742,20 @@ function $(e) {
564
742
  }
565
743
  //#endregion
566
744
  //#region src/components/custom/aurora/Aurora.vue
567
- var ye = /* @__PURE__ */ e(/* @__PURE__ */ _({
745
+ var ke = /* @__PURE__ */ e(/* @__PURE__ */ v({
568
746
  __name: "Aurora",
569
747
  props: {
570
- config: { default: () => l },
748
+ config: { default: () => u },
571
749
  runtimeOptions: {},
572
750
  onInitError: {},
573
751
  renderMode: { default: "auto" },
574
752
  opacityCeiling: { default: 1 }
575
753
  },
576
754
  setup(e, { expose: t }) {
577
- let n = e, r = $(n.renderMode), i = m(() => Math.max(0, Math.min(1, n.opacityCeiling))), a = w(null), o = Q(a, () => n.config, m(() => ({
755
+ let n = e, r = $(n.renderMode), i = h(() => Math.max(0, Math.min(1, n.opacityCeiling))), a = C(null), o = Q(a, () => n.config, h(() => ({
578
756
  ...n.runtimeOptions ?? {},
579
757
  ...n.onInitError ? { onInitError: n.onInitError } : {}
580
- })).value, { renderMode: r }), s = m(() => z(n.config.palette));
758
+ })).value, { renderMode: r }), s = h(() => z(n.config.palette));
581
759
  return t({
582
760
  config: n.config,
583
761
  canvasRef: a,
@@ -588,24 +766,24 @@ var ye = /* @__PURE__ */ e(/* @__PURE__ */ _({
588
766
  pause: o.pause,
589
767
  resume: o.resume,
590
768
  isArmed: o.isArmed
591
- }), (e, t) => (S(), h("div", {
769
+ }), (e, t) => (ee(), g("div", {
592
770
  class: "aurora-root block h-full w-full overflow-hidden",
593
- style: y({ "--aurora-opacity-ceiling": i.value })
594
- }, [g("div", {
771
+ style: b({ "--aurora-opacity-ceiling": i.value })
772
+ }, [_("div", {
595
773
  class: "aurora-placeholder h-full w-full",
596
774
  "aria-hidden": "true",
597
- style: y({ backgroundImage: s.value })
598
- }, null, 4), g("canvas", {
775
+ style: b({ backgroundImage: s.value })
776
+ }, null, 4), _("canvas", {
599
777
  ref_key: "canvasRef",
600
778
  ref: a,
601
779
  "aria-hidden": "true",
602
- class: v(["aurora-canvas block h-full w-full", { "aurora-canvas--armed": ee(o).isArmed.value }])
780
+ class: y(["aurora-canvas block h-full w-full", { "aurora-canvas--armed": w(o).isArmed.value }])
603
781
  }, null, 2)], 4));
604
782
  }
605
783
  }), [["__scopeId", "data-v-c5277232"]]);
606
784
  //#endregion
607
785
  //#region src/components/custom/aurora/composables/useCursorInteraction.ts
608
- function be(e, t, n) {
786
+ function Ae(e, t, n) {
609
787
  let r = Z(t), i = n.hitFraction ?? .35, a = null, o = null;
610
788
  function s(e, t) {
611
789
  let n = t.getBoundingClientRect();
@@ -693,13 +871,13 @@ function be(e, t, n) {
693
871
  }, a = n.getBoundingClientRect(), o = l((i.clientX - a.left) / Math.max(a.width, 1), (i.clientY - a.top) / Math.max(a.height, 1));
694
872
  o !== null && r().nuclei.length > 1 && (r().nuclei.splice(o, 1), t.preventDefault());
695
873
  }
696
- return x(() => {
874
+ return S(() => {
697
875
  let t = e.value;
698
876
  t && (t.addEventListener("pointermove", p), t.addEventListener("pointerleave", m), t.addEventListener("pointerdown", h), t.addEventListener("pointerup", g), t.addEventListener("pointercancel", g), t.addEventListener("contextmenu", _));
699
- }), b(() => {
877
+ }), x(() => {
700
878
  let t = e.value;
701
879
  t && (t.removeEventListener("pointermove", p), t.removeEventListener("pointerleave", m), t.removeEventListener("pointerdown", h), t.removeEventListener("pointerup", g), t.removeEventListener("pointercancel", g), t.removeEventListener("contextmenu", _));
702
880
  }), { isDragging: () => a };
703
881
  }
704
882
  //#endregion
705
- export { ye as Aurora, l as DEFAULT_AURORA_CONFIG, s as MAX_NUCLEI, c as MAX_STOPS, X as createAurora, f as cssToOklch, H as deriveAurora, R as flattenPalette, B as hexToOklchStop, d as oklchStopToHex, u as oklchToLinear, z as paletteToCssGradient, $ as resolveRenderMode, Q as useAurora, be as useCursorInteraction };
883
+ export { ke as Aurora, u as DEFAULT_AURORA_CONFIG, c as MAX_NUCLEI, l as MAX_STOPS, X as createAurora, p as cssToOklch, U as deriveAurora, me as deriveScene, R as flattenPalette, B as hexToOklchStop, f as oklchStopToHex, d as oklchToLinear, z as paletteToCssGradient, $ as resolveRenderMode, Q as useAurora, Ae as useCursorInteraction };