Skip to content

Commit 8925bb0

Browse files
feat: add UI transitions (#17)
1 parent 86946e5 commit 8925bb0

File tree

7 files changed

+77
-36
lines changed

7 files changed

+77
-36
lines changed

app/globals.css

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,10 +87,6 @@
8787
}
8888

8989
@theme inline {
90-
--radius-sm: calc(var(--radius) - 4px);
91-
--radius-md: calc(var(--radius) - 2px);
92-
--radius-lg: var(--radius);
93-
--radius-xl: calc(var(--radius) + 4px);
9490
--color-background: var(--background);
9591
--color-foreground: var(--foreground);
9692
--color-card: var(--card);
@@ -156,4 +152,27 @@
156152
background-position-x: 300px;
157153
}
158154
}
155+
156+
.appear-from-bottom {
157+
opacity: 1;
158+
transform: translateY(0);
159+
transition-property: transform, opacity;
160+
transition-duration: var(--tw-duration);
161+
transition-timing-function: var(--tw-ease);
162+
163+
@starting-style {
164+
transform: translateY(100%);
165+
}
166+
}
167+
168+
.appear-fade-in {
169+
opacity: 1;
170+
transition-property: opacity;
171+
transition-duration: var(--tw-duration);
172+
transition-timing-function: var(--tw-ease);
173+
174+
@starting-style {
175+
opacity: 0;
176+
}
177+
}
159178
}

components/livekit/agent-audio-tile.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export function AgentAudioTile({ className }: AgentAudioTileProps) {
1919
state={agent.state}
2020
barCount={5}
2121
options={{ minHeight: 5 }}
22-
className={cn('flex aspect-video h-[126px] items-center justify-center gap-4', className)}
22+
className={cn('flex h-[200px] items-center justify-center gap-4', className)}
2323
>
2424
<span
2525
className={cn([

components/livekit/agent-control-bar/agent-control-bar.tsx

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,31 @@ export function AgentControlBar({
6363
return (
6464
<div
6565
aria-label="Voice assistant controls"
66-
className={cn('bg-background flex flex-col rounded-md border p-3 shadow-md', className)}
66+
className={cn(
67+
'bg-background flex flex-col rounded-md border p-3 drop-shadow-xl/3',
68+
className
69+
)}
6770
{...props}
6871
>
69-
{chatOpen && (
70-
<>
71-
<div className={`flex w-full overflow-hidden`}>
72-
<ChatInput onSend={handleSendMessage} disabled={isSendingMessage} className="w-full" />
73-
</div>
74-
<hr className="my-3" />
75-
</>
76-
)}
72+
<div
73+
inert={!chatOpen}
74+
className={cn(
75+
'overflow-hidden transition-[height] duration-300 ease-out',
76+
chatOpen ? 'h-[57px]' : 'h-0'
77+
)}
78+
>
79+
<div className="flex h-8 w-full">
80+
<ChatInput onSend={handleSendMessage} disabled={isSendingMessage} className="w-full" />
81+
</div>
82+
<hr className="my-3" />
83+
</div>
84+
7785
<div className="flex flex-row justify-between gap-1">
7886
<div className="flex w-full gap-1">
7987
{visibleControls.microphone && (
8088
<div className="flex items-center gap-0">
8189
<TrackToggle
82-
className="peer relative w-auto rounded-r-none border-r-0"
90+
className="peer relative w-auto md:rounded-r-none md:border-r-0"
8391
source={Track.Source.Microphone}
8492
>
8593
<BarVisualizer
@@ -92,7 +100,7 @@ export function AgentControlBar({
92100
</BarVisualizer>
93101
</TrackToggle>
94102
<DeviceSelect
95-
className="peer-data-[state=on]:border-button-foreground rounded-l-none border-l-0"
103+
className="peer-data-[state=on]:border-button-foreground hidden rounded-l-none border-l-0 md:block"
96104
size="sm"
97105
kind="audioinput"
98106
onActiveDeviceChange={handleDeviceChange}
@@ -104,11 +112,11 @@ export function AgentControlBar({
104112
{visibleControls.camera && (
105113
<div className="flex items-center gap-0">
106114
<TrackToggle
107-
className="peer relative w-auto rounded-r-none border-r-0"
115+
className="peer relative w-auto md:rounded-r-none md:border-r-0"
108116
source={Track.Source.Camera}
109117
/>
110118
<DeviceSelect
111-
className="peer-data-[state=on]:border-button-foreground rounded-l-none border-l-0"
119+
className="peer-data-[state=on]:border-button-foreground hidden rounded-l-none border-l-0 md:block"
112120
size="sm"
113121
kind="videoinput"
114122
onActiveDeviceChange={handleDeviceChange}
@@ -138,7 +146,8 @@ export function AgentControlBar({
138146
{visibleControls.leave && (
139147
<Button variant="destructive" onClick={handleDisconnect} className="font-mono">
140148
<PhoneDisconnectIcon weight="bold" />
141-
END CALL
149+
<span className="hidden md:inline">END CALL</span>
150+
<span className="inline md:hidden">END</span>
142151
</Button>
143152
)}
144153
</div>

components/livekit/chat/chat-input.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ export function ChatInput({ onSend, className, disabled, ...props }: ChatInputPr
1919
inputRef.current?.focus();
2020
};
2121

22+
const isDisabled = disabled || message.trim().length === 0;
23+
2224
return (
2325
<form
2426
{...props}
@@ -37,8 +39,8 @@ export function ChatInput({ onSend, className, disabled, ...props }: ChatInputPr
3739
<Button
3840
type="submit"
3941
size="sm"
40-
variant="secondary"
41-
disabled={message.trim().length === 0 || disabled}
42+
variant={isDisabled ? 'secondary' : 'default'}
43+
disabled={isDisabled}
4244
className="font-mono"
4345
>
4446
SEND

components/session-view.tsx

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,14 @@ export default function SessionView() {
2323
return (
2424
<main>
2525
{chatOpen && (
26-
<ChatMessageView className="mx-auto min-h-svh w-full max-w-2xl pt-56 pb-48">
26+
<ChatMessageView className="appear-fade-in mx-auto min-h-svh w-full max-w-2xl px-3 pt-56 pb-48 delay-300 duration-1000 ease-out md:px-0">
2727
<div className="space-y-3 whitespace-pre-wrap">
2828
{messages.map((message: ReceivedChatMessage) => (
29-
<ChatEntry key={message.id} entry={message} />
29+
<ChatEntry
30+
key={message.id}
31+
entry={message}
32+
className="appear-fade-in duration-2000 ease-out"
33+
/>
3034
))}
3135
</div>
3236
</ChatMessageView>
@@ -39,8 +43,8 @@ export default function SessionView() {
3943

4044
<div
4145
className={cn(
42-
'fixed left-1/2 z-50 rounded-xl border border-transparent p-8 transition-[left,top,transform,shadow,border] duration-500 ease-out',
43-
chatOpen ? 'shadow-3xl border-border top-24 shadow-md' : 'top-1/2'
46+
'bg-background fixed left-1/2 z-50 rounded-2xl border border-transparent p-8 transition-[left,top,transform,shadow,border] duration-500 ease-out',
47+
chatOpen ? 'border-border top-24 drop-shadow-2xl/3' : 'top-1/2'
4448
)}
4549
style={{
4650
transform: chatOpen
@@ -51,13 +55,20 @@ export default function SessionView() {
5155
<AgentAudioTile />
5256
</div>
5357

54-
<div className="bg-background fixed right-0 bottom-0 left-0 z-50 px-12 pb-12">
55-
<div className="mx-auto w-full max-w-2xl">
56-
{messages.length === 0 && (
57-
<p className="animate-text-gradient from-foreground/20 via-foreground to-foreground/20 mx-auto mb-6 bg-gradient-to-r bg-clip-text text-sm font-semibold text-transparent">
58+
<div className="bg-background appear-from-bottom fixed right-0 bottom-0 left-0 z-50 px-3 pb-3 delay-300 duration-300 ease-out md:px-12 md:pb-12">
59+
<div className="relative mx-auto w-full max-w-2xl">
60+
<div
61+
aria-hidden={messages.length > 0}
62+
className={cn(
63+
'appear-fade-in absolute inset-x-0 -top-12 delay-600 duration-2000 ease-in',
64+
messages.length === 0 ? 'opacity-100' : '!opacity-0'
65+
)}
66+
>
67+
<p className="animate-text-gradient from-foreground/20 via-foreground to-foreground/20 mx-auto bg-gradient-to-r bg-clip-text text-center text-sm font-semibold text-transparent">
5868
Agent is listening, ask it a question
5969
</p>
60-
)}
70+
</div>
71+
6172
<AgentControlBar onChatOpenChange={setChatOpen} onSendMessage={handleSendMessage} />
6273
</div>
6374
{/* skrim */}

components/ui/button.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { cn } from '@/lib/utils';
55

66
const buttonVariants = cva(
77
[
8-
'inline-flex items-center justify-center gap-2 shrink-0 rounded-md text-sm font-medium whitespace-nowrap cursor-pointer outline-none transition-all',
8+
'inline-flex items-center justify-center gap-2 shrink-0 rounded-md text-sm font-medium whitespace-nowrap cursor-pointer outline-none transition-colors duration-300',
99
'focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]',
1010
'disabled:pointer-events-none disabled:opacity-50',
1111
'aria-invalid:ring-destructive/20 aria-invalid:border-destructive dark:aria-invalid:ring-destructive/40 ',
@@ -14,18 +14,18 @@ const buttonVariants = cva(
1414
{
1515
variants: {
1616
variant: {
17-
default: 'bg-primary text-primary-foreground shadow-xs hover:bg-primary/90',
17+
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
1818
destructive: [
19-
'bg-destructive border border-destructive-border text-destructive-foreground shadow-xs',
19+
'bg-destructive border border-destructive-border text-destructive-foreground',
2020
'hover:bg-background focus-visible:ring-destructive-foreground/20',
2121
'dark:focus-visible:ring-destructive-foreground/40',
2222
],
2323
outline: [
24-
'border bg-background shadow-xs',
24+
'border bg-background',
2525
'hover:bg-accent hover:text-accent-foreground',
2626
'dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
2727
],
28-
secondary: 'bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80',
28+
secondary: 'bg-secondary text-secondary-foregroun hover:bg-secondary/80',
2929
ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
3030
link: 'text-primary underline-offset-4 hover:underline',
3131
},

components/ui/select.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ function SelectContent({
6767
data-slot="select-content"
6868
className={cn(
6969
[
70-
'bg-popover text-popover-foreground relative z-50 overflow-x-hidden overflow-y-auto rounded-md border shadow-md',
70+
'bg-popover text-popover-foreground relative z-50 overflow-x-hidden overflow-y-auto rounded-md border drop-shadow-xl/5',
7171
'max-h-(--radix-select-content-available-height) min-w-[8rem]',
7272
'origin-(--radix-select-content-transform-origin)',
7373
'data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95',

0 commit comments

Comments
 (0)