Skip to content

Commit 6f4fbae

Browse files
create agent-chat-input
1 parent a9a15c3 commit 6f4fbae

File tree

3 files changed

+106
-107
lines changed

3 files changed

+106
-107
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { useEffect, useRef, useState } from 'react';
2+
import { PaperPlaneRightIcon, SpinnerIcon } from '@phosphor-icons/react/dist/ssr';
3+
import { Button } from '@/components/ui/button';
4+
5+
interface AgentChatInputProps {
6+
chatOpen: boolean;
7+
isAgentAvailable?: boolean;
8+
onSend?: (message: string) => void;
9+
}
10+
11+
export function AgentChatInput({
12+
chatOpen,
13+
isAgentAvailable = false,
14+
onSend = async () => {},
15+
}: AgentChatInputProps) {
16+
const inputRef = useRef<HTMLTextAreaElement>(null);
17+
const [isSending, setIsSending] = useState(false);
18+
const [message, setMessage] = useState<string>('');
19+
20+
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
21+
e.preventDefault();
22+
23+
try {
24+
setIsSending(true);
25+
await onSend(message);
26+
setMessage('');
27+
} catch (error) {
28+
console.error(error);
29+
} finally {
30+
setIsSending(false);
31+
}
32+
};
33+
34+
const isDisabled = isSending || !isAgentAvailable || message.trim().length === 0;
35+
36+
useEffect(() => {
37+
if (chatOpen && isAgentAvailable) return;
38+
// when not disabled refocus on input
39+
inputRef.current?.focus();
40+
}, [chatOpen, isAgentAvailable]);
41+
42+
return (
43+
<form
44+
onSubmit={handleSubmit}
45+
className="mb-3 flex grow items-end gap-2 rounded-md pl-1 text-sm"
46+
>
47+
<textarea
48+
autoFocus
49+
ref={inputRef}
50+
value={message}
51+
disabled={!chatOpen}
52+
placeholder="Type something..."
53+
onChange={(e) => setMessage(e.target.value)}
54+
className="field-sizing-content max-h-16 min-h-8 flex-1 py-2 [scrollbar-width:thin] focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
55+
/>
56+
<Button
57+
size="icon"
58+
type="submit"
59+
disabled={isDisabled}
60+
variant={isDisabled ? 'secondary' : 'default'}
61+
title={isSending ? 'Sending...' : 'Send'}
62+
className="self-end rounded-full disabled:cursor-not-allowed"
63+
>
64+
{isSending ? (
65+
<SpinnerIcon className="animate-spin" weight="bold" />
66+
) : (
67+
<PaperPlaneRightIcon weight="bold" />
68+
)}
69+
</Button>
70+
</form>
71+
);
72+
}

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

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,38 @@
22

33
import { type HTMLAttributes, useCallback, useState } from 'react';
44
import { Track } from 'livekit-client';
5+
import { motion } from 'motion/react';
56
import { useChat, useRemoteParticipants } from '@livekit/components-react';
67
import { ChatTextIcon, PhoneDisconnectIcon } from '@phosphor-icons/react/dist/ssr';
78
import { AgentTrackControl } from '@/components/livekit/agent-track-control';
89
import { AgentTrackToggle, toggleVariants } from '@/components/livekit/agent-track-toggle';
910
import { Button } from '@/components/ui/button';
1011
import { Toggle } from '@/components/ui/toggle';
1112
import { cn } from '@/lib/utils';
12-
import { ChatInput } from './chat-input';
13+
import { AgentChatInput } from './agent-chat-input';
1314
import { UseInputControlsProps, useInputControls } from './hooks/use-input-controls';
1415
import { usePublishPermissions } from './hooks/use-publish-permissions';
1516

17+
const MOTION_PROPS = {
18+
variants: {
19+
hidden: {
20+
height: 0,
21+
opacity: 0,
22+
marginBottom: 0,
23+
},
24+
visible: {
25+
height: 'auto',
26+
opacity: 1,
27+
marginBottom: 12,
28+
},
29+
},
30+
initial: 'hidden',
31+
transition: {
32+
duration: 0.3,
33+
ease: 'easeOut',
34+
},
35+
};
36+
1637
export interface ControlBarControls {
1738
leave?: boolean;
1839
camera?: boolean;
@@ -89,11 +110,18 @@ export function AgentControlBar({
89110
>
90111
{/* Chat Input */}
91112
{visibleControls.chat && (
92-
<ChatInput
93-
chatOpen={chatOpen}
94-
isAgentAvailable={isAgentAvailable}
95-
onSend={handleSendMessage}
96-
/>
113+
<motion.div
114+
{...MOTION_PROPS}
115+
inert={!chatOpen}
116+
animate={chatOpen ? 'visible' : 'hidden'}
117+
className="border-input/50 flex w-full items-start overflow-hidden border-b"
118+
>
119+
<AgentChatInput
120+
chatOpen={chatOpen}
121+
isAgentAvailable={isAgentAvailable}
122+
onSend={handleSendMessage}
123+
/>
124+
</motion.div>
97125
)}
98126

99127
<div className="flex gap-1">

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

Lines changed: 0 additions & 101 deletions
This file was deleted.

0 commit comments

Comments
 (0)