@flikk/ui 1.0.0-beta.12 → 1.0.0-beta.13

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/components/ai/StreamingResponse/StreamingResponse.types.d.ts +7 -7
  2. package/dist/components/core/Accordion/Accordion.types.d.ts +1 -7
  3. package/dist/components/core/Accordion/AccordionTrigger.js +4 -6
  4. package/dist/components/core/Alert/Alert.types.d.ts +1 -1
  5. package/dist/components/core/Avatar/Avatar.js +22 -18
  6. package/dist/components/core/Avatar/Avatar.theme.js +1 -1
  7. package/dist/components/core/Avatar/Avatar.types.d.ts +9 -8
  8. package/dist/components/core/AvatarGroup/AvatarGroup.js +2 -2
  9. package/dist/components/core/Badge/Badge.js +7 -5
  10. package/dist/components/core/Badge/Badge.theme.js +11 -4
  11. package/dist/components/core/Badge/Badge.types.d.ts +2 -2
  12. package/dist/components/core/Button/Button.theme.js +2 -2
  13. package/dist/components/core/Calendar/CalendarEvent/CalendarEvent.types.d.ts +5 -6
  14. package/dist/components/core/FileTypeIcon/FileTypeIcon.d.ts +3 -0
  15. package/dist/components/core/FileTypeIcon/FileTypeIcon.js +33 -0
  16. package/dist/components/core/FileTypeIcon/FileTypeIcon.theme.d.ts +2 -0
  17. package/dist/components/core/FileTypeIcon/FileTypeIcon.theme.js +12 -0
  18. package/dist/components/core/FileTypeIcon/FileTypeIcon.types.d.ts +19 -0
  19. package/dist/components/core/FileTypeIcon/fileTypes.d.ts +7 -0
  20. package/dist/components/core/FileTypeIcon/fileTypes.js +30 -0
  21. package/dist/components/core/FileTypeIcon/index.d.ts +2 -0
  22. package/dist/components/core/MenuItem/MenuItem.theme.js +1 -1
  23. package/dist/components/core/Message/Message.js +1 -1
  24. package/dist/components/core/Message/Message.theme.js +2 -2
  25. package/dist/components/core/Message/Message.types.d.ts +6 -6
  26. package/dist/components/core/Message/MessageAvatar.d.ts +2 -2
  27. package/dist/components/core/Message/MessageAvatar.js +3 -3
  28. package/dist/components/core/Popover/PopoverBody.js +19 -8
  29. package/dist/components/core/Tag/Tag.d.ts +1 -1
  30. package/dist/components/core/Tag/Tag.js +2 -2
  31. package/dist/components/core/Tag/Tag.types.d.ts +1 -1
  32. package/dist/components/core/index.d.ts +1 -1
  33. package/dist/components/core/index.js +1 -1
  34. package/dist/components/data-display/Feed/Feed.animations.d.ts +40 -0
  35. package/dist/components/data-display/Feed/Feed.animations.js +26 -0
  36. package/dist/components/data-display/Feed/Feed.d.ts +5 -1
  37. package/dist/components/data-display/Feed/Feed.js +87 -60
  38. package/dist/components/data-display/Feed/Feed.theme.js +11 -13
  39. package/dist/components/data-display/Feed/Feed.types.d.ts +35 -34
  40. package/dist/components/data-display/Feed/index.d.ts +1 -1
  41. package/dist/components/data-display/GanttChart/GanttTaskBar.js +11 -9
  42. package/dist/components/data-display/Timeline/TimelineMarker.js +2 -2
  43. package/dist/components/effects/3d/index.d.ts +1 -1
  44. package/dist/components/effects/AIOrb/AIOrb.d.ts +1 -0
  45. package/dist/components/effects/AIOrb/AIOrb.js +3 -2
  46. package/dist/components/effects/AIOrb/AIOrb.types.d.ts +1 -0
  47. package/dist/components/effects/AIOrb/hooks/index.d.ts +2 -0
  48. package/dist/components/effects/AIOrb/hooks/useOrbState.d.ts +20 -0
  49. package/dist/components/effects/AIOrb/hooks/useOrbState.js +27 -0
  50. package/dist/components/effects/AIOrb/index.d.ts +2 -2
  51. package/dist/components/effects/AIOrb/variants/Aura/Aura.d.ts +3 -0
  52. package/dist/components/effects/AIOrb/variants/Aura/Aura.js +75 -0
  53. package/dist/components/effects/AIOrb/variants/Aura/Aura.shaders.d.ts +28 -0
  54. package/dist/components/effects/AIOrb/variants/Aura/Aura.shaders.js +191 -0
  55. package/dist/components/effects/AIOrb/variants/Aura/Aura.types.d.ts +53 -0
  56. package/dist/components/effects/AIOrb/variants/Aura/ShaderCanvas.d.ts +17 -0
  57. package/dist/components/effects/AIOrb/variants/Aura/ShaderCanvas.js +149 -0
  58. package/dist/components/effects/AIOrb/variants/Aura/index.d.ts +2 -0
  59. package/dist/components/effects/AIOrb/variants/DotSphere/DotSphere.js +38 -7
  60. package/dist/components/effects/AIOrb/variants/DotSphere/DotSphere.types.d.ts +6 -0
  61. package/dist/components/effects/AIOrb/variants/Liquid/Liquid.js +34 -8
  62. package/dist/components/effects/AIOrb/variants/Liquid/Liquid.types.d.ts +6 -0
  63. package/dist/components/effects/AIOrb/variants/index.d.ts +2 -0
  64. package/dist/components/effects/CustomCursor/CustomCursor.d.ts +2 -2
  65. package/dist/components/effects/CustomCursor/CustomCursor.js +50 -47
  66. package/dist/components/effects/GlassSurface/GlassSurface.js +24 -12
  67. package/dist/components/effects/GlassSurface/GlassSurface.types.d.ts +8 -0
  68. package/dist/components/forms/Mention/Mention.js +2 -2
  69. package/dist/index.js +1 -1
  70. package/dist/styles.css +1 -1
  71. package/dist/utils/index.d.ts +1 -0
  72. package/dist/utils/paletteGenerator.d.ts +46 -0
  73. package/package.json +1 -1
  74. package/src/styles/theme.css +2 -6
  75. package/dist/components/core/Loader/Loader.d.ts +0 -24
  76. package/dist/components/core/Loader/Loader.js +0 -57
  77. package/dist/components/core/Loader/Loader.theme.d.ts +0 -27
  78. package/dist/components/core/Loader/Loader.theme.js +0 -60
  79. package/dist/components/core/Loader/Loader.types.d.ts +0 -71
  80. package/dist/components/core/Loader/index.d.ts +0 -2
@@ -28,9 +28,9 @@ const TimelineMarker = React__default.forwardRef(({ icon, className, ...props },
28
28
  const variants = shouldReduceMotion ? undefined : timelineMarkerVariants;
29
29
  // Filter out conflicting motion props
30
30
  const { onDrag, onDragEnd, onDragStart, onAnimationStart, onAnimationEnd, ...safeProps } = props;
31
- return (jsxs("div", { className: cn("relative flex flex-col items-center", isHorizontal && "flex-row"), children: [jsx(motion.div, { ref: ref, className: cn(theme.markerStyle, theme.markerStatuses[status], className), "data-status": status, "aria-label": `${status} step`, initial: variants ? "hidden" : undefined, animate: variants ? "visible" : undefined, variants: variants, ...safeProps, children: renderIcon() }), !isLast && !isHorizontal && (jsx("div", { className: cn("w-0.5 flex-1 min-h-4", status === "completed"
31
+ return (jsxs("div", { className: cn("relative flex flex-col items-center", isHorizontal && "w-full"), children: [jsx(motion.div, { ref: ref, className: cn(theme.markerStyle, theme.markerStatuses[status], className), "data-status": status, "aria-label": `${status} step`, initial: variants ? "hidden" : undefined, animate: variants ? "visible" : undefined, variants: variants, ...safeProps, children: renderIcon() }), !isLast && !isHorizontal && (jsx("div", { className: cn("w-0.5 flex-1 min-h-4", status === "completed"
32
32
  ? "bg-[var(--color-success)]"
33
- : "bg-[var(--color-border)]"), "aria-hidden": "true" })), !isLast && isHorizontal && (jsx("div", { className: cn("h-0.5 flex-1 min-w-4", status === "completed"
33
+ : "bg-[var(--color-border)]"), "aria-hidden": "true" })), !isLast && isHorizontal && (jsx("div", { className: cn("absolute top-1/2 -translate-y-1/2 left-1/2 h-0.5 w-full", status === "completed"
34
34
  ? "bg-[var(--color-success)]"
35
35
  : "bg-[var(--color-border)]"), "aria-hidden": "true" }))] }));
36
36
  });
@@ -1,5 +1,5 @@
1
1
  export { AIOrb } from "../AIOrb";
2
- export type { AIOrbProps, DotSphereProps, LiquidProps } from "../AIOrb";
2
+ export type { AIOrbProps, OrbState, DotSphereProps, LiquidProps, AuraProps } from "../AIOrb";
3
3
  export { Particles } from "../Particles";
4
4
  export type { ParticlesProps } from "../Particles";
5
5
  export { SpotlightBorder } from "../SpotlightBorder";
@@ -3,4 +3,5 @@ import type { AIOrbProps } from "./AIOrb.types";
3
3
  export declare const AIOrb: React.FC<AIOrbProps> & {
4
4
  DotSphere: React.FC<import("./variants").DotSphereProps>;
5
5
  Liquid: React.FC<import("./variants").LiquidProps>;
6
+ Aura: React.FC<import("./variants").AuraProps>;
6
7
  };
@@ -2,6 +2,7 @@ import { jsx } from 'react/jsx-runtime';
2
2
  import { cn } from '../../../utils/cn.js';
3
3
  import { DotSphere } from './variants/DotSphere/DotSphere.js';
4
4
  import { Liquid } from './variants/Liquid/Liquid.js';
5
+ import { Aura } from './variants/Aura/Aura.js';
5
6
 
6
7
  const AIOrbRoot = ({ size, backgroundColor = "#000", className, style, children, ...props }) => {
7
8
  const containerStyle = {
@@ -9,8 +10,8 @@ const AIOrbRoot = ({ size, backgroundColor = "#000", className, style, children,
9
10
  ...(size ? { width: size, height: size } : {}),
10
11
  ...style,
11
12
  };
12
- return (jsx("div", { className: cn("relative overflow-hidden", !size && "w-full h-full", className), style: containerStyle, ...props, children: children }));
13
+ return (jsx("div", { className: cn("relative overflow-visible", !size && "w-full h-full", className), style: containerStyle, ...props, children: children }));
13
14
  };
14
- const AIOrb = Object.assign(AIOrbRoot, { DotSphere, Liquid });
15
+ const AIOrb = Object.assign(AIOrbRoot, { DotSphere, Liquid, Aura });
15
16
 
16
17
  export { AIOrb };
@@ -1,4 +1,5 @@
1
1
  import React from "react";
2
+ export type OrbState = "idle" | "listening" | "thinking" | "speaking";
2
3
  export interface AIOrbProps extends Omit<React.HTMLAttributes<HTMLDivElement>, "color"> {
3
4
  /**
4
5
  * Fixed size in pixels. If omitted the orb fills its parent.
@@ -0,0 +1,2 @@
1
+ export { useOrbState } from "./useOrbState";
2
+ export type { StateParamConfig } from "./useOrbState";
@@ -0,0 +1,20 @@
1
+ import type { OrbState } from "../AIOrb.types";
2
+ /**
3
+ * State-to-parameter configuration. Maps each OrbState to a set of numeric
4
+ * target values that will be smoothly interpolated.
5
+ */
6
+ export type StateParamConfig<T extends Record<string, number>> = Record<OrbState, T>;
7
+ /**
8
+ * Generic ref-based interpolation hook for orb state-driven animation.
9
+ *
10
+ * - When `state` is defined, target values come from the config map.
11
+ * - When `state` is undefined, target values come from the `propValues` fallback
12
+ * (backwards-compatible — orb behaves exactly as if no state was set).
13
+ * - Returns a mutable `valuesRef` read inside animation loops, and a `tick(dt)`
14
+ * function that advances the interpolation by `dt` seconds.
15
+ * - Zero React re-renders — all mutation is ref-based.
16
+ */
17
+ export declare function useOrbState<T extends Record<string, number>>(state: OrbState | undefined, config: StateParamConfig<T>, propValues: T, damping?: number): {
18
+ readonly valuesRef: import("react").RefObject<T>;
19
+ readonly tick: (dt: number) => void;
20
+ };
@@ -0,0 +1,27 @@
1
+ import { useRef, useCallback } from 'react';
2
+
3
+ /**
4
+ * Generic ref-based interpolation hook for orb state-driven animation.
5
+ *
6
+ * - When `state` is defined, target values come from the config map.
7
+ * - When `state` is undefined, target values come from the `propValues` fallback
8
+ * (backwards-compatible — orb behaves exactly as if no state was set).
9
+ * - Returns a mutable `valuesRef` read inside animation loops, and a `tick(dt)`
10
+ * function that advances the interpolation by `dt` seconds.
11
+ * - Zero React re-renders — all mutation is ref-based.
12
+ */
13
+ function useOrbState(state, config, propValues, damping = 4) {
14
+ const valuesRef = useRef({ ...propValues });
15
+ const tick = useCallback((dt) => {
16
+ const target = state ? config[state] : propValues;
17
+ const current = valuesRef.current;
18
+ const factor = 1 - Math.exp(-damping * dt);
19
+ for (const key in target) {
20
+ current[key] = (current[key] +
21
+ (target[key] - current[key]) * factor);
22
+ }
23
+ }, [state, config, propValues, damping]);
24
+ return { valuesRef, tick };
25
+ }
26
+
27
+ export { useOrbState };
@@ -1,3 +1,3 @@
1
1
  export { AIOrb } from "./AIOrb";
2
- export type { AIOrbProps } from "./AIOrb.types";
3
- export type { DotSphereProps, LiquidProps } from "./variants";
2
+ export type { AIOrbProps, OrbState } from "./AIOrb.types";
3
+ export type { DotSphereProps, LiquidProps, AuraProps } from "./variants";
@@ -0,0 +1,3 @@
1
+ import React from "react";
2
+ import type { AuraProps } from "./Aura.types";
3
+ export declare const Aura: React.FC<AuraProps>;
@@ -0,0 +1,75 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useMemo } from 'react';
3
+ import { useOrbState } from '../../hooks/useOrbState.js';
4
+ import { ShaderCanvas } from './ShaderCanvas.js';
5
+ import { auraFragmentShader } from './Aura.shaders.js';
6
+
7
+ const STATE_CONFIG = {
8
+ idle: { speed: 10, amplitude: 1.2, frequency: 0.4, brightness: 1.0, scale: 0.2 },
9
+ listening: { speed: 20, amplitude: 1.0, frequency: 0.7, brightness: 1.75, scale: 0.3 },
10
+ thinking: { speed: 30, amplitude: 0.5, frequency: 1.0, brightness: 1.5, scale: 0.3 },
11
+ speaking: { speed: 70, amplitude: 0.75, frequency: 1.25, brightness: 1.5, scale: 0.3 },
12
+ };
13
+ // ── Hex → RGB helper ─────────────────────────────────────────────────────────
14
+ function hexToRgb(hex) {
15
+ const h = hex.replace("#", "");
16
+ const num = parseInt(h.length === 3 ? h.split("").map((c) => c + c).join("") : h, 16);
17
+ return [(num >> 16) / 255, ((num >> 8) & 0xff) / 255, (num & 0xff) / 255];
18
+ }
19
+ // ── Aura component ───────────────────────────────────────────────────────────
20
+ const Aura = ({ color = "#1FD5F9", colorShift = 0.3, speed = 10, amplitude = 2.0, frequency = 0.5, scale = 0.2, blur = 1.0, brightness = 1.5, themeMode = "dark", state, }) => {
21
+ const rgb = useMemo(() => hexToRgb(color), [color]);
22
+ const propValues = useMemo(() => ({ speed, amplitude, frequency, brightness, scale }), [speed, amplitude, frequency, brightness, scale]);
23
+ const { valuesRef, tick } = useOrbState(state, STATE_CONFIG, propValues);
24
+ // Stable uniform object — mutated in-place each frame via the getter ref
25
+ const uniforms = useMemo(() => ({
26
+ u_color: rgb,
27
+ u_colorShift: colorShift,
28
+ u_speed: speed,
29
+ u_amplitude: amplitude,
30
+ u_frequency: frequency,
31
+ u_scale: scale,
32
+ u_blur: blur,
33
+ u_brightness: brightness,
34
+ u_bloom: 0.4,
35
+ u_spacing: 0.5,
36
+ u_variance: 0.1,
37
+ u_smoothing: 1.0,
38
+ u_lightMode: themeMode === "light" ? 1.0 : 0.0,
39
+ }),
40
+ // eslint-disable-next-line react-hooks/exhaustive-deps
41
+ []);
42
+ // Sync non-interpolated props every render
43
+ uniforms.u_color = rgb;
44
+ uniforms.u_colorShift = colorShift;
45
+ uniforms.u_blur = blur;
46
+ uniforms.u_lightMode = themeMode === "light" ? 1.0 : 0.0;
47
+ // Custom ref whose getter ticks interpolation + writes interpolated values
48
+ // into the uniform object right before ShaderCanvas reads it each frame.
49
+ const tickingRef = useMemo(() => {
50
+ let lastT = 0;
51
+ return {
52
+ get current() {
53
+ const now = performance.now() / 1000;
54
+ if (lastT > 0) {
55
+ tick(Math.min(now - lastT, 0.1));
56
+ }
57
+ lastT = now;
58
+ const v = valuesRef.current;
59
+ uniforms.u_speed = v.speed;
60
+ uniforms.u_amplitude = v.amplitude;
61
+ uniforms.u_frequency = v.frequency;
62
+ uniforms.u_brightness = v.brightness;
63
+ uniforms.u_scale = v.scale;
64
+ return uniforms;
65
+ },
66
+ set current(_v) {
67
+ // no-op — ShaderCanvas never writes to this ref
68
+ },
69
+ };
70
+ // eslint-disable-next-line react-hooks/exhaustive-deps
71
+ }, [tick]);
72
+ return (jsx("div", { className: "absolute inset-0", children: jsx(ShaderCanvas, { fragmentShader: auraFragmentShader, uniformsRef: tickingRef }) }));
73
+ };
74
+
75
+ export { Aura };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Aura fragment shader — faithful port of LiveKit's AgentAudioVisualizerAura.
3
+ *
4
+ * Uses 36 iterations of turbulence-warped SDF evaluation to produce a
5
+ * luminous ring through accumulation. The ring shape emerges naturally
6
+ * from the turbulence + circle SDF interaction — not from drawing a
7
+ * ring directly.
8
+ *
9
+ * Uniforms (all from Aura component):
10
+ * u_color vec3 Base RGB colour (0-1)
11
+ * u_colorShift float HSV hue shift range across iterations (0-1)
12
+ * u_speed float Animation speed multiplier
13
+ * u_amplitude float Turbulence displacement strength
14
+ * u_frequency float Wave complexity (remapped to 2.0-15.0)
15
+ * u_scale float Circle SDF radius
16
+ * u_blur float Edge softness
17
+ * u_brightness float Overall brightness/opacity
18
+ * u_bloom float Glow intensity (dark mode only)
19
+ * u_spacing float Iteration spacing (remapped to 1.0-TAU)
20
+ * u_variance float Amplitude variance per turbulence layer
21
+ * u_smoothing float Dynamic blur smoothing factor
22
+ * u_lightMode float 0.0 = dark mode, 1.0 = light mode
23
+ *
24
+ * Built-in from ShaderCanvas:
25
+ * iTime float Elapsed seconds
26
+ * iResolution vec2 Viewport pixel dimensions
27
+ */
28
+ export declare const auraFragmentShader = "#version 300 es\nprecision highp float;\n\nuniform float iTime;\nuniform vec2 iResolution;\n\nuniform vec3 u_color;\nuniform float u_colorShift;\nuniform float u_speed;\nuniform float u_amplitude;\nuniform float u_frequency;\nuniform float u_scale;\nuniform float u_blur;\nuniform float u_brightness;\nuniform float u_bloom;\nuniform float u_spacing;\nuniform float u_variance;\nuniform float u_smoothing;\nuniform float u_lightMode;\n\nout vec4 fragColor;\n\n/* \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst float TAU = 6.283185;\n\n/* \u2500\u2500 Utilities \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nvec2 randFibo(vec2 p) {\n p = fract(p * vec2(443.897, 441.423));\n p += dot(p, p.yx + 19.19);\n return fract((p.xx + p.yx) * p.xy);\n}\n\nvec3 Tonemap(vec3 x) {\n x *= 4.0;\n return x / (1.0 + x);\n}\n\nfloat luma(vec3 color) {\n return dot(color, vec3(0.299, 0.587, 0.114));\n}\n\nvec3 rgb2hsv(vec3 c) {\n vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);\n vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));\n vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));\n float d = q.x - min(q.w, q.y);\n float e = 1.0e-10;\n return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);\n}\n\nvec3 hsv2rgb(vec3 c) {\n vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);\n vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);\n return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);\n}\n\n/* \u2500\u2500 SDF \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nfloat sdCircle(vec2 st, float r) {\n return length(st) - r;\n}\n\n/* \u2500\u2500 Turbulence \u2014 4 layers of sinusoidal displacement with rotation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nvec2 turb(vec2 pos, float t, float it) {\n mat2 rotation = mat2(0.6, -0.25, 0.25, 0.9);\n mat2 layerRotation = mat2(0.6, -0.8, 0.8, 0.6);\n\n float frequency = mix(2.0, 15.0, u_frequency);\n float amplitude = u_amplitude;\n float frequencyGrowth = 1.4;\n float animTime = t * 0.1 * u_speed;\n\n const int LAYERS = 4;\n for (int i = 0; i < LAYERS; i++) {\n vec2 rotatedPos = pos * rotation;\n vec2 wave = sin(frequency * rotatedPos + float(i) * animTime + it);\n\n pos += (amplitude / frequency) * rotation[0] * wave;\n\n rotation *= layerRotation;\n amplitude *= mix(1.0, max(wave.x, wave.y), u_variance);\n frequency *= frequencyGrowth;\n }\n\n return pos;\n}\n\n/* \u2500\u2500 Main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\nconst float ITERATIONS = 36.0;\n\nvoid main() {\n vec2 uv = gl_FragCoord.xy / iResolution.xy;\n\n vec3 pp = vec3(0.0);\n vec3 bloom = vec3(0.0);\n float t = iTime * 0.5;\n vec2 pos = uv - 0.5;\n\n vec2 prevPos = turb(pos, t, 0.0 - 1.0 / ITERATIONS);\n float spacing = mix(1.0, TAU, u_spacing);\n\n for (float i = 1.0; i < ITERATIONS + 1.0; i++) {\n float iter = i / ITERATIONS;\n vec2 st = turb(pos, t, iter * spacing);\n float d = abs(sdCircle(st, u_scale));\n float pd = distance(st, prevPos);\n prevPos = st;\n float dynamicBlur = exp2(pd * 2.0 * 1.4426950408889634) - 1.0;\n float ds = smoothstep(0.0, u_blur * 0.05 + max(dynamicBlur * u_smoothing, 0.001), d);\n\n vec3 col = u_color;\n if (u_colorShift > 0.01) {\n vec3 hsv = rgb2hsv(col);\n hsv.x = fract(hsv.x + (1.0 - iter) * u_colorShift * 0.3);\n col = hsv2rgb(hsv);\n }\n\n float invd = 1.0 / max(d + dynamicBlur, 0.001);\n pp += (ds - 1.0) * col;\n bloom += clamp(invd, 0.0, 250.0) * col;\n }\n\n pp *= 1.0 / ITERATIONS;\n\n vec3 color;\n\n // Dark mode (default)\n if (u_lightMode < 0.5) {\n bloom = bloom / (bloom + 2e4);\n color = (-pp + bloom * 3.0 * u_bloom) * 1.2;\n color += (randFibo(gl_FragCoord.xy).x - 0.5) / 255.0;\n color = Tonemap(color);\n float alpha = luma(color) * u_brightness;\n fragColor = vec4(color * u_brightness, alpha);\n }\n // Light mode\n else {\n color = -pp;\n color += (randFibo(gl_FragCoord.xy).x - 0.5) / 255.0;\n\n float brightness = length(color);\n vec3 direction = brightness > 0.0 ? color / brightness : color;\n\n float factor = 2.0;\n float mappedBrightness = (brightness * factor) / (1.0 + brightness * factor);\n color = direction * mappedBrightness;\n\n float gray = dot(color, vec3(0.2, 0.5, 0.1));\n float saturationBoost = 3.0;\n color = mix(vec3(gray), color, saturationBoost);\n\n color = clamp(color, 0.0, 1.0);\n\n float alpha = mappedBrightness * clamp(u_brightness, 1.0, 2.0);\n fragColor = vec4(color, alpha);\n }\n}\n";
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Aura fragment shader — faithful port of LiveKit's AgentAudioVisualizerAura.
3
+ *
4
+ * Uses 36 iterations of turbulence-warped SDF evaluation to produce a
5
+ * luminous ring through accumulation. The ring shape emerges naturally
6
+ * from the turbulence + circle SDF interaction — not from drawing a
7
+ * ring directly.
8
+ *
9
+ * Uniforms (all from Aura component):
10
+ * u_color vec3 Base RGB colour (0-1)
11
+ * u_colorShift float HSV hue shift range across iterations (0-1)
12
+ * u_speed float Animation speed multiplier
13
+ * u_amplitude float Turbulence displacement strength
14
+ * u_frequency float Wave complexity (remapped to 2.0-15.0)
15
+ * u_scale float Circle SDF radius
16
+ * u_blur float Edge softness
17
+ * u_brightness float Overall brightness/opacity
18
+ * u_bloom float Glow intensity (dark mode only)
19
+ * u_spacing float Iteration spacing (remapped to 1.0-TAU)
20
+ * u_variance float Amplitude variance per turbulence layer
21
+ * u_smoothing float Dynamic blur smoothing factor
22
+ * u_lightMode float 0.0 = dark mode, 1.0 = light mode
23
+ *
24
+ * Built-in from ShaderCanvas:
25
+ * iTime float Elapsed seconds
26
+ * iResolution vec2 Viewport pixel dimensions
27
+ */
28
+ const auraFragmentShader = `#version 300 es
29
+ precision highp float;
30
+
31
+ uniform float iTime;
32
+ uniform vec2 iResolution;
33
+
34
+ uniform vec3 u_color;
35
+ uniform float u_colorShift;
36
+ uniform float u_speed;
37
+ uniform float u_amplitude;
38
+ uniform float u_frequency;
39
+ uniform float u_scale;
40
+ uniform float u_blur;
41
+ uniform float u_brightness;
42
+ uniform float u_bloom;
43
+ uniform float u_spacing;
44
+ uniform float u_variance;
45
+ uniform float u_smoothing;
46
+ uniform float u_lightMode;
47
+
48
+ out vec4 fragColor;
49
+
50
+ /* ── Constants ─────────────────────────────────────────────────────────────── */
51
+
52
+ const float TAU = 6.283185;
53
+
54
+ /* ── Utilities ─────────────────────────────────────────────────────────────── */
55
+
56
+ vec2 randFibo(vec2 p) {
57
+ p = fract(p * vec2(443.897, 441.423));
58
+ p += dot(p, p.yx + 19.19);
59
+ return fract((p.xx + p.yx) * p.xy);
60
+ }
61
+
62
+ vec3 Tonemap(vec3 x) {
63
+ x *= 4.0;
64
+ return x / (1.0 + x);
65
+ }
66
+
67
+ float luma(vec3 color) {
68
+ return dot(color, vec3(0.299, 0.587, 0.114));
69
+ }
70
+
71
+ vec3 rgb2hsv(vec3 c) {
72
+ vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
73
+ vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
74
+ vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
75
+ float d = q.x - min(q.w, q.y);
76
+ float e = 1.0e-10;
77
+ return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
78
+ }
79
+
80
+ vec3 hsv2rgb(vec3 c) {
81
+ vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
82
+ vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
83
+ return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
84
+ }
85
+
86
+ /* ── SDF ───────────────────────────────────────────────────────────────────── */
87
+
88
+ float sdCircle(vec2 st, float r) {
89
+ return length(st) - r;
90
+ }
91
+
92
+ /* ── Turbulence — 4 layers of sinusoidal displacement with rotation ────────── */
93
+
94
+ vec2 turb(vec2 pos, float t, float it) {
95
+ mat2 rotation = mat2(0.6, -0.25, 0.25, 0.9);
96
+ mat2 layerRotation = mat2(0.6, -0.8, 0.8, 0.6);
97
+
98
+ float frequency = mix(2.0, 15.0, u_frequency);
99
+ float amplitude = u_amplitude;
100
+ float frequencyGrowth = 1.4;
101
+ float animTime = t * 0.1 * u_speed;
102
+
103
+ const int LAYERS = 4;
104
+ for (int i = 0; i < LAYERS; i++) {
105
+ vec2 rotatedPos = pos * rotation;
106
+ vec2 wave = sin(frequency * rotatedPos + float(i) * animTime + it);
107
+
108
+ pos += (amplitude / frequency) * rotation[0] * wave;
109
+
110
+ rotation *= layerRotation;
111
+ amplitude *= mix(1.0, max(wave.x, wave.y), u_variance);
112
+ frequency *= frequencyGrowth;
113
+ }
114
+
115
+ return pos;
116
+ }
117
+
118
+ /* ── Main ──────────────────────────────────────────────────────────────────── */
119
+
120
+ const float ITERATIONS = 36.0;
121
+
122
+ void main() {
123
+ vec2 uv = gl_FragCoord.xy / iResolution.xy;
124
+
125
+ vec3 pp = vec3(0.0);
126
+ vec3 bloom = vec3(0.0);
127
+ float t = iTime * 0.5;
128
+ vec2 pos = uv - 0.5;
129
+
130
+ vec2 prevPos = turb(pos, t, 0.0 - 1.0 / ITERATIONS);
131
+ float spacing = mix(1.0, TAU, u_spacing);
132
+
133
+ for (float i = 1.0; i < ITERATIONS + 1.0; i++) {
134
+ float iter = i / ITERATIONS;
135
+ vec2 st = turb(pos, t, iter * spacing);
136
+ float d = abs(sdCircle(st, u_scale));
137
+ float pd = distance(st, prevPos);
138
+ prevPos = st;
139
+ float dynamicBlur = exp2(pd * 2.0 * 1.4426950408889634) - 1.0;
140
+ float ds = smoothstep(0.0, u_blur * 0.05 + max(dynamicBlur * u_smoothing, 0.001), d);
141
+
142
+ vec3 col = u_color;
143
+ if (u_colorShift > 0.01) {
144
+ vec3 hsv = rgb2hsv(col);
145
+ hsv.x = fract(hsv.x + (1.0 - iter) * u_colorShift * 0.3);
146
+ col = hsv2rgb(hsv);
147
+ }
148
+
149
+ float invd = 1.0 / max(d + dynamicBlur, 0.001);
150
+ pp += (ds - 1.0) * col;
151
+ bloom += clamp(invd, 0.0, 250.0) * col;
152
+ }
153
+
154
+ pp *= 1.0 / ITERATIONS;
155
+
156
+ vec3 color;
157
+
158
+ // Dark mode (default)
159
+ if (u_lightMode < 0.5) {
160
+ bloom = bloom / (bloom + 2e4);
161
+ color = (-pp + bloom * 3.0 * u_bloom) * 1.2;
162
+ color += (randFibo(gl_FragCoord.xy).x - 0.5) / 255.0;
163
+ color = Tonemap(color);
164
+ float alpha = luma(color) * u_brightness;
165
+ fragColor = vec4(color * u_brightness, alpha);
166
+ }
167
+ // Light mode
168
+ else {
169
+ color = -pp;
170
+ color += (randFibo(gl_FragCoord.xy).x - 0.5) / 255.0;
171
+
172
+ float brightness = length(color);
173
+ vec3 direction = brightness > 0.0 ? color / brightness : color;
174
+
175
+ float factor = 2.0;
176
+ float mappedBrightness = (brightness * factor) / (1.0 + brightness * factor);
177
+ color = direction * mappedBrightness;
178
+
179
+ float gray = dot(color, vec3(0.2, 0.5, 0.1));
180
+ float saturationBoost = 3.0;
181
+ color = mix(vec3(gray), color, saturationBoost);
182
+
183
+ color = clamp(color, 0.0, 1.0);
184
+
185
+ float alpha = mappedBrightness * clamp(u_brightness, 1.0, 2.0);
186
+ fragColor = vec4(color, alpha);
187
+ }
188
+ }
189
+ `;
190
+
191
+ export { auraFragmentShader };
@@ -0,0 +1,53 @@
1
+ import type { OrbState } from "../../AIOrb.types";
2
+ export interface AuraProps {
3
+ /**
4
+ * Base colour as a hex string.
5
+ * @default '#1FD5F9'
6
+ */
7
+ color?: string;
8
+ /**
9
+ * HSV hue shift for iridescence (0-1).
10
+ * @default 0.3
11
+ */
12
+ colorShift?: number;
13
+ /**
14
+ * Animation speed multiplier.
15
+ * @default 10
16
+ */
17
+ speed?: number;
18
+ /**
19
+ * Wave displacement strength.
20
+ * @default 2.0
21
+ */
22
+ amplitude?: number;
23
+ /**
24
+ * Wave complexity.
25
+ * @default 0.5
26
+ */
27
+ frequency?: number;
28
+ /**
29
+ * Circle SDF radius in normalized coordinates.
30
+ * @default 0.2
31
+ */
32
+ scale?: number;
33
+ /**
34
+ * Edge softness.
35
+ * @default 1.0
36
+ */
37
+ blur?: number;
38
+ /**
39
+ * Overall brightness/opacity.
40
+ * @default 1.5
41
+ */
42
+ brightness?: number;
43
+ /**
44
+ * Rendering mode — affects colour inversion for light backgrounds.
45
+ * @default 'dark'
46
+ */
47
+ themeMode?: "dark" | "light";
48
+ /**
49
+ * State-driven animation. When set, overrides speed/amplitude/frequency/
50
+ * brightness/scale with smooth interpolation towards state targets.
51
+ */
52
+ state?: OrbState;
53
+ }
@@ -0,0 +1,17 @@
1
+ import React from "react";
2
+ export interface ShaderCanvasProps {
3
+ fragmentShader: string;
4
+ uniformsRef: React.MutableRefObject<Record<string, number | number[]>>;
5
+ maxDpr?: number;
6
+ className?: string;
7
+ style?: React.CSSProperties;
8
+ }
9
+ /**
10
+ * Minimal raw WebGL2 canvas renderer for fullscreen fragment shaders.
11
+ * - Built-in uniforms: iTime (float), iResolution (vec2)
12
+ * - Custom uniforms read from `uniformsRef` each frame (no stale closures)
13
+ * - ResizeObserver for responsive canvas sizing
14
+ * - rAF animation loop with cleanup on unmount
15
+ * - Graceful degradation: renders nothing if WebGL2 unavailable
16
+ */
17
+ export declare const ShaderCanvas: React.FC<ShaderCanvasProps>;
@@ -0,0 +1,149 @@
1
+ import { jsx } from 'react/jsx-runtime';
2
+ import { useRef, useEffect } from 'react';
3
+
4
+ const VERTEX_SHADER = `#version 300 es
5
+ in vec2 a_position;
6
+ void main() {
7
+ gl_Position = vec4(a_position, 0.0, 1.0);
8
+ }`;
9
+ /**
10
+ * Minimal raw WebGL2 canvas renderer for fullscreen fragment shaders.
11
+ * - Built-in uniforms: iTime (float), iResolution (vec2)
12
+ * - Custom uniforms read from `uniformsRef` each frame (no stale closures)
13
+ * - ResizeObserver for responsive canvas sizing
14
+ * - rAF animation loop with cleanup on unmount
15
+ * - Graceful degradation: renders nothing if WebGL2 unavailable
16
+ */
17
+ const ShaderCanvas = ({ fragmentShader, uniformsRef, maxDpr = 2, className, style, }) => {
18
+ const canvasRef = useRef(null);
19
+ useEffect(() => {
20
+ const canvas = canvasRef.current;
21
+ if (!canvas)
22
+ return;
23
+ const gl = canvas.getContext("webgl2", {
24
+ alpha: true,
25
+ premultipliedAlpha: false,
26
+ antialias: false,
27
+ });
28
+ if (!gl)
29
+ return;
30
+ // ── Compile shaders ────────────────────────────────────────────────
31
+ const compileShader = (type, src) => {
32
+ const shader = gl.createShader(type);
33
+ if (!shader)
34
+ return null;
35
+ gl.shaderSource(shader, src);
36
+ gl.compileShader(shader);
37
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
38
+ if (process.env.NODE_ENV !== "production") {
39
+ console.error("[ShaderCanvas]", gl.getShaderInfoLog(shader));
40
+ }
41
+ gl.deleteShader(shader);
42
+ return null;
43
+ }
44
+ return shader;
45
+ };
46
+ const vs = compileShader(gl.VERTEX_SHADER, VERTEX_SHADER);
47
+ const fs = compileShader(gl.FRAGMENT_SHADER, fragmentShader);
48
+ if (!vs || !fs)
49
+ return;
50
+ const program = gl.createProgram();
51
+ gl.attachShader(program, vs);
52
+ gl.attachShader(program, fs);
53
+ gl.linkProgram(program);
54
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
55
+ if (process.env.NODE_ENV !== "production") {
56
+ console.error("[ShaderCanvas]", gl.getProgramInfoLog(program));
57
+ }
58
+ return;
59
+ }
60
+ gl.useProgram(program);
61
+ // ── Fullscreen quad ────────────────────────────────────────────────
62
+ const positions = new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]);
63
+ const vao = gl.createVertexArray();
64
+ gl.bindVertexArray(vao);
65
+ const buf = gl.createBuffer();
66
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
67
+ gl.bufferData(gl.ARRAY_BUFFER, positions, gl.STATIC_DRAW);
68
+ const aPos = gl.getAttribLocation(program, "a_position");
69
+ gl.enableVertexAttribArray(aPos);
70
+ gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
71
+ // ── Uniform locations ──────────────────────────────────────────────
72
+ const uTime = gl.getUniformLocation(program, "iTime");
73
+ const uRes = gl.getUniformLocation(program, "iResolution");
74
+ // Cache custom uniform locations
75
+ const customLocs = new Map();
76
+ const getCustomLoc = (name) => {
77
+ var _a;
78
+ if (!customLocs.has(name)) {
79
+ customLocs.set(name, gl.getUniformLocation(program, name));
80
+ }
81
+ return (_a = customLocs.get(name)) !== null && _a !== void 0 ? _a : null;
82
+ };
83
+ // ── Resize handling ────────────────────────────────────────────────
84
+ const dpr = Math.min(window.devicePixelRatio || 1, maxDpr);
85
+ let width = 0;
86
+ let height = 0;
87
+ const resize = () => {
88
+ const rect = canvas.getBoundingClientRect();
89
+ const w = Math.round(rect.width * dpr);
90
+ const h = Math.round(rect.height * dpr);
91
+ if (w !== width || h !== height) {
92
+ width = w;
93
+ height = h;
94
+ canvas.width = w;
95
+ canvas.height = h;
96
+ gl.viewport(0, 0, w, h);
97
+ }
98
+ };
99
+ const observer = new ResizeObserver(resize);
100
+ observer.observe(canvas);
101
+ resize();
102
+ // ── Animation loop ─────────────────────────────────────────────────
103
+ gl.enable(gl.BLEND);
104
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
105
+ let rafId = 0;
106
+ const startTime = performance.now();
107
+ const frame = () => {
108
+ rafId = requestAnimationFrame(frame);
109
+ const t = (performance.now() - startTime) / 1000;
110
+ gl.uniform1f(uTime, t);
111
+ gl.uniform2f(uRes, width, height);
112
+ // Set custom uniforms from ref
113
+ const uniforms = uniformsRef.current;
114
+ for (const name in uniforms) {
115
+ const loc = getCustomLoc(name);
116
+ if (!loc)
117
+ continue;
118
+ const val = uniforms[name];
119
+ if (typeof val === "number") {
120
+ gl.uniform1f(loc, val);
121
+ }
122
+ else if (Array.isArray(val)) {
123
+ if (val.length === 2)
124
+ gl.uniform2f(loc, val[0], val[1]);
125
+ else if (val.length === 3)
126
+ gl.uniform3f(loc, val[0], val[1], val[2]);
127
+ else if (val.length === 4)
128
+ gl.uniform4f(loc, val[0], val[1], val[2], val[3]);
129
+ }
130
+ }
131
+ gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
132
+ };
133
+ rafId = requestAnimationFrame(frame);
134
+ return () => {
135
+ cancelAnimationFrame(rafId);
136
+ observer.disconnect();
137
+ gl.deleteProgram(program);
138
+ gl.deleteShader(vs);
139
+ gl.deleteShader(fs);
140
+ gl.deleteBuffer(buf);
141
+ gl.deleteVertexArray(vao);
142
+ };
143
+ // Fragment shader is static — only set up once
144
+ // eslint-disable-next-line react-hooks/exhaustive-deps
145
+ }, []);
146
+ return (jsx("canvas", { ref: canvasRef, className: className, style: { display: "block", width: "100%", height: "100%", ...style } }));
147
+ };
148
+
149
+ export { ShaderCanvas };
@@ -0,0 +1,2 @@
1
+ export { Aura } from "./Aura";
2
+ export type { AuraProps } from "./Aura.types";