Skip to content

Commit 21ebe9e

Browse files
cleanup component ui
1 parent df90867 commit 21ebe9e

File tree

3 files changed

+373
-128
lines changed

3 files changed

+373
-128
lines changed

app/ui/_components.tsx

Lines changed: 91 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -245,24 +245,6 @@ export const COMPONENTS = {
245245
return (
246246
<Container componentName="AudioVisualizer">
247247
<div className="flex items-center gap-2">
248-
<div className="flex-1">
249-
<label className="font-mono text-xs uppercase" htmlFor="state">
250-
State
251-
</label>
252-
<Select value={state} onValueChange={(value) => setState(value as AgentState)}>
253-
<SelectTrigger id="state" className="w-full">
254-
<SelectValue placeholder="Select a state" />
255-
</SelectTrigger>
256-
<SelectContent>
257-
{states.map((state) => (
258-
<SelectItem key={state} value={state}>
259-
{state}
260-
</SelectItem>
261-
))}
262-
</SelectContent>
263-
</Select>
264-
</div>
265-
266248
<div className="flex-1">
267249
<label className="font-mono text-xs uppercase" htmlFor="size">
268250
Size
@@ -313,16 +295,34 @@ export const COMPONENTS = {
313295
className="mx-auto"
314296
/>
315297
</div>
316-
<div className="text-center">Original BarVisualizer</div>
317-
<div className="border-border grid place-items-center rounded-xl border p-4 py-8">
318-
<BarVisualizer
319-
size={size as audioBarVisualizerVariantsSizeType}
320-
state={state}
321-
audioTrack={micTrackRef!}
322-
barCount={parseInt(barCount) || undefined}
323-
className="mx-auto"
324-
/>
325-
</div>
298+
<details>
299+
<summary className="text-muted-foreground font-mono text-xs uppercase">
300+
<span className="inline-block cursor-pointer p-1">Original BarVisualizer</span>
301+
</summary>
302+
<div className="border-border grid place-items-center rounded-xl border p-4 py-8">
303+
<BarVisualizer
304+
size={size as audioBarVisualizerVariantsSizeType}
305+
state={state}
306+
audioTrack={micTrackRef!}
307+
barCount={parseInt(barCount) || undefined}
308+
className="mx-auto"
309+
/>
310+
</div>
311+
</details>
312+
</div>
313+
314+
<div className="flex flex-wrap gap-4">
315+
{states.map((stateType) => (
316+
<Button
317+
key={stateType}
318+
size="sm"
319+
variant={state === stateType ? 'primary' : 'default'}
320+
onClick={() => setState(stateType)}
321+
className={'flex-1'}
322+
>
323+
{stateType}
324+
</Button>
325+
))}
326326
</div>
327327
</Container>
328328
);
@@ -363,24 +363,6 @@ export const COMPONENTS = {
363363
return (
364364
<Container componentName="AudioVisualizer">
365365
<div className="flex items-center gap-2">
366-
<div className="flex-1">
367-
<label className="font-mono text-xs uppercase" htmlFor="state">
368-
State
369-
</label>
370-
<Select value={state} onValueChange={(value) => setState(value as AgentState)}>
371-
<SelectTrigger id="state" className="w-full">
372-
<SelectValue placeholder="Select a state" />
373-
</SelectTrigger>
374-
<SelectContent>
375-
{states.map((state) => (
376-
<SelectItem key={state} value={state}>
377-
{state}
378-
</SelectItem>
379-
))}
380-
</SelectContent>
381-
</Select>
382-
</div>
383-
384366
<div className="flex-1">
385367
<label className="font-mono text-xs uppercase" htmlFor="size">
386368
Size
@@ -432,6 +414,20 @@ export const COMPONENTS = {
432414
/>
433415
</div>
434416
</div>
417+
418+
<div className="flex flex-wrap gap-4">
419+
{states.map((stateType) => (
420+
<Button
421+
key={stateType}
422+
size="sm"
423+
variant={state === stateType ? 'primary' : 'default'}
424+
onClick={() => setState(stateType)}
425+
className={'flex-1'}
426+
>
427+
{stateType}
428+
</Button>
429+
))}
430+
</div>
435431
</Container>
436432
);
437433
},
@@ -476,24 +472,6 @@ export const COMPONENTS = {
476472
return (
477473
<Container componentName="AudioVisualizer">
478474
<div className="flex items-center gap-2">
479-
<div className="flex-1">
480-
<label className="font-mono text-xs uppercase" htmlFor="state">
481-
State
482-
</label>
483-
<Select value={state} onValueChange={(value) => setState(value as AgentState)}>
484-
<SelectTrigger id="state" className="w-full">
485-
<SelectValue placeholder="Select a state" />
486-
</SelectTrigger>
487-
<SelectContent>
488-
{states.map((state) => (
489-
<SelectItem key={state} value={state}>
490-
{state}
491-
</SelectItem>
492-
))}
493-
</SelectContent>
494-
</Select>
495-
</div>
496-
497475
<div className="flex-1">
498476
<label className="font-mono text-xs uppercase" htmlFor="rowCount">
499477
Row count
@@ -560,19 +538,34 @@ export const COMPONENTS = {
560538
options={demoOptions}
561539
/>
562540
</div>
563-
<div className="border-border bg-muted overflow-x-auto rounded-xl border p-8">
564-
<pre className="text-muted-foreground text-sm">
565-
<code>{JSON.stringify(demoOptions, null, 2)}</code>
566-
</pre>
541+
542+
<div className="flex flex-wrap gap-4">
543+
{states.map((stateType) => (
544+
<Button
545+
key={stateType}
546+
size="sm"
547+
variant={state === stateType ? 'primary' : 'default'}
548+
onClick={() => setState(stateType)}
549+
className={'flex-1'}
550+
>
551+
{stateType}
552+
</Button>
553+
))}
554+
</div>
555+
556+
<div>
557+
<StoryTitle>Demo options</StoryTitle>
558+
<div className="border-border bg-muted overflow-x-auto rounded-xl border p-8">
559+
<pre className="text-muted-foreground text-sm">
560+
<code>{JSON.stringify(demoOptions, null, 2)}</code>
561+
</pre>
562+
</div>
567563
</div>
568564
</Container>
569565
);
570566
},
571567

572568
AudioShaderVisualizer: () => {
573-
const { startSession, endSession } = useSession();
574-
const { localParticipant } = useLocalParticipant();
575-
576569
// shape
577570
const [shape, setShape] = useState(1.0);
578571
// color scale
@@ -593,20 +586,18 @@ export const COMPONENTS = {
593586
const [size, setSize] = useState<audioShaderVisualizerVariantsSizeType>('lg');
594587
const [state, setState] = useState<AgentState>(states[0]);
595588

596-
const {
597-
// state,
598-
audioTrack,
599-
} = useVoiceAssistant();
600-
601-
useEffect(() => {
602-
if (state === 'speaking') {
603-
startSession();
604-
localParticipant.setMicrophoneEnabled(true, undefined);
605-
} else {
606-
endSession();
607-
localParticipant.setMicrophoneEnabled(false, undefined);
608-
}
609-
}, [startSession, endSession, state, localParticipant]);
589+
const { microphoneTrack, localParticipant } = useLocalParticipant();
590+
const micTrackRef = useMemo<TrackReferenceOrPlaceholder | undefined>(() => {
591+
return state === 'speaking'
592+
? ({
593+
participant: localParticipant,
594+
source: Track.Source.Microphone,
595+
publication: microphoneTrack,
596+
} as TrackReference)
597+
: undefined;
598+
}, [state, localParticipant, microphoneTrack]);
599+
600+
useMicrophone();
610601

611602
const fields = [
612603
['color position', colorPosition, setColorPosition, 0, 1, 0.01],
@@ -615,28 +606,7 @@ export const COMPONENTS = {
615606

616607
return (
617608
<Container componentName="AudioShaderVisualizer">
618-
<StartAudio label="Start Audio" />
619-
<RoomAudioRenderer />
620-
621609
<div className="flex gap-4">
622-
<div className="flex-1">
623-
<label className="font-mono text-xs uppercase" htmlFor="state">
624-
State
625-
</label>
626-
<Select value={state} onValueChange={(value) => setState(value as AgentState)}>
627-
<SelectTrigger id="state" className="w-full">
628-
<SelectValue placeholder="Select a state" />
629-
</SelectTrigger>
630-
<SelectContent>
631-
{states.map((state) => (
632-
<SelectItem key={state} value={state}>
633-
{state}
634-
</SelectItem>
635-
))}
636-
</SelectContent>
637-
</Select>
638-
</div>
639-
640610
<div className="flex-1">
641611
<label className="font-mono text-xs uppercase" htmlFor="size">
642612
Size
@@ -681,11 +651,25 @@ export const COMPONENTS = {
681651
shape={shape}
682652
colorScale={colorScale}
683653
colorPosition={colorPosition}
684-
audioTrack={audioTrack as TrackReferenceOrPlaceholder}
654+
audioTrack={micTrackRef!}
685655
className="mx-auto bg-black"
686656
/>
687657
</div>
688658

659+
<div className="flex flex-wrap gap-4">
660+
{states.map((stateType) => (
661+
<Button
662+
key={stateType}
663+
size="sm"
664+
variant={state === stateType ? 'primary' : 'default'}
665+
onClick={() => setState(stateType)}
666+
className={'flex-1'}
667+
>
668+
{stateType}
669+
</Button>
670+
))}
671+
</div>
672+
689673
<div className="grid grid-cols-2 gap-4">
690674
{fields.map(([name, value, setValue, min = 0.1, max = 10, step = 0.1]) => {
691675
return (

components/livekit/audio-visualizer/audio-shader-visualizer/audio-shader-visualizer.tsx

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,21 @@ import {
1717
// useMultibandTrackVolume,
1818
useTrackVolume,
1919
} from '@livekit/components-react';
20-
import { AuroraShaders, type AuroraShadersProps } from '@/components/ui/shadcn-io/aurora-shaders';
2120
import { cn } from '@/lib/utils';
21+
import { AuroraShaders, type AuroraShadersProps } from './aurora-shaders';
2222

2323
const DEFAULT_SPEED = 10;
2424
const DEFAULT_AMPLITUDE = 2;
2525
const DEFAULT_FREQUENCY = 0.5;
2626
const DEFAULT_SCALE = 0.2;
2727
const DEFAULT_BRIGHTNESS = 1.5;
2828
const DEFAULT_TRANSITION: ValueAnimationTransition = { duration: 0.5, ease: 'easeOut' };
29+
const DEFAULT_PULSE_TRANSITION: ValueAnimationTransition = {
30+
duration: 0.5,
31+
ease: 'easeOut',
32+
repeat: Infinity,
33+
repeatType: 'mirror',
34+
};
2935

3036
function useAnimatedValue<T>(initialValue: T) {
3137
const [value, setValue] = useState(initialValue);
@@ -40,7 +46,7 @@ function useAnimatedValue<T>(initialValue: T) {
4046
[motionValue]
4147
);
4248

43-
return { value, controls: controlsRef, animate: animateFn };
49+
return { value, motionValue, controls: controlsRef, animate: animateFn };
4450
}
4551

4652
export const audioShaderVisualizerVariants = cva(['aspect-square'], {
@@ -75,9 +81,13 @@ export function AudioShaderVisualizer({
7581
AuroraShadersProps &
7682
VariantProps<typeof audioShaderVisualizerVariants>) {
7783
const [speed, setSpeed] = useState(DEFAULT_SPEED);
84+
const {
85+
value: scale,
86+
animate: animateScale,
87+
motionValue: scaleMotionValue,
88+
} = useAnimatedValue(DEFAULT_SCALE);
7889
const { value: amplitude, animate: animateAmplitude } = useAnimatedValue(DEFAULT_AMPLITUDE);
7990
const { value: frequency, animate: animateFrequency } = useAnimatedValue(DEFAULT_FREQUENCY);
80-
const { value: scale, animate: animateScale } = useAnimatedValue(DEFAULT_SCALE);
8191
const { value: brightness, animate: animateBrightness } = useAnimatedValue(DEFAULT_BRIGHTNESS);
8292

8393
const volume = useTrackVolume(audioTrack as TrackReference, {
@@ -100,36 +110,29 @@ export function AudioShaderVisualizer({
100110
animateScale(0.35, DEFAULT_TRANSITION);
101111
animateAmplitude(1, DEFAULT_TRANSITION);
102112
animateFrequency(0.7, DEFAULT_TRANSITION);
103-
animateBrightness(2.0, DEFAULT_TRANSITION);
113+
// animateBrightness(2.0, DEFAULT_TRANSITION);
114+
animateBrightness([1.5, 2.0], DEFAULT_PULSE_TRANSITION);
104115
return;
105116
case 'initializing':
106117
setSpeed(30);
107118
animateScale(0.3, DEFAULT_TRANSITION);
108119
animateAmplitude(0.5, DEFAULT_TRANSITION);
109120
animateFrequency(1, DEFAULT_TRANSITION);
110-
animateBrightness([0.5, 2.5], {
111-
duration: 0.2,
112-
repeat: Infinity,
113-
repeatType: 'mirror',
114-
});
121+
animateBrightness([0.5, 2.5], DEFAULT_PULSE_TRANSITION);
115122
return;
116123
case 'thinking':
117124
setSpeed(30);
118-
animateScale([0.15, 0.13], {
119-
duration: 0.3,
120-
repeat: Infinity,
121-
repeatType: 'mirror',
122-
});
125+
animateScale(0.1, DEFAULT_TRANSITION);
123126
animateAmplitude(1.0, DEFAULT_TRANSITION);
124127
animateFrequency(3.0, DEFAULT_TRANSITION);
125-
animateBrightness([1.5, 2.5], {
126-
duration: 0.3,
127-
repeat: Infinity,
128-
repeatType: 'mirror',
129-
});
128+
animateBrightness([1.0, 2.0], DEFAULT_PULSE_TRANSITION);
130129
return;
131130
case 'speaking':
132131
setSpeed(50);
132+
animateScale(0.3, DEFAULT_TRANSITION);
133+
animateAmplitude(1.0, DEFAULT_TRANSITION);
134+
animateFrequency(0.7, DEFAULT_TRANSITION);
135+
animateBrightness(1.5, DEFAULT_TRANSITION);
133136
return;
134137
}
135138
}, [
@@ -143,13 +146,21 @@ export function AudioShaderVisualizer({
143146
]);
144147

145148
useEffect(() => {
146-
if (state === 'speaking' && volume > 0) {
149+
if (state === 'speaking' && volume > 0 && !scaleMotionValue.isAnimating()) {
147150
animateScale(0.3 - 0.1 * volume, { duration: 0 });
148151
animateAmplitude(1.0 + 0.2 * volume, { duration: 0 });
149152
animateFrequency(0.7 - 0.3 * volume, { duration: 0 });
150153
animateBrightness(1.5 + 1.0 * volume, { duration: 0 });
151154
}
152-
}, [state, volume, animateScale, animateAmplitude, animateFrequency, animateBrightness]);
155+
}, [
156+
state,
157+
volume,
158+
scaleMotionValue,
159+
animateScale,
160+
animateAmplitude,
161+
animateFrequency,
162+
animateBrightness,
163+
]);
153164

154165
return (
155166
<AuroraShaders

0 commit comments

Comments
 (0)