-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathchat.tsx
More file actions
93 lines (81 loc) · 3.2 KB
/
chat.tsx
File metadata and controls
93 lines (81 loc) · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
'use client';
import { useState, useCallback } from 'react';
import { useClientTransport, useView, useActiveTurns, useAblyMessages } from '@ably/ai-transport/react';
import type { AgentCodecEvent, AgentMessage } from '@ably/ai-transport/anthropic';
import { userMessage } from '../helpers';
import { MessageList } from './message-list';
import { DebugPane } from './debug-pane';
interface ChatProps {
chatId: string;
clientId?: string;
historyLimit?: number;
}
export function Chat({ chatId, clientId, historyLimit }: ChatProps) {
const [input, setInput] = useState('');
const transport = useClientTransport<AgentCodecEvent, AgentMessage>({ channelName: chatId });
const view = useView<AgentCodecEvent, AgentMessage>({ transport, limit: historyLimit ?? 30 });
const activeTurns = useActiveTurns({ transport });
const ablyMessages = useAblyMessages({ transport });
const hasOwnTurns = clientId ? activeTurns.has(clientId) : false;
const handleSubmit = useCallback(() => {
const text = input.trim();
if (!text) return;
setInput('');
view.send([userMessage(text)]);
}, [input, view]);
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit();
}
};
return (
<div className="flex h-dvh">
<div className="flex flex-1 flex-col">
{/* Header */}
<header className="flex items-center gap-2 border-b border-zinc-800 px-4 py-3">
<div className="h-2 w-2 rounded-full bg-emerald-500" />
<h1 className="text-sm font-medium text-zinc-300">Ably AI — Anthropic Agent SDK Demo</h1>
{clientId && <span className="ml-auto text-xs text-zinc-600 font-mono">{clientId}</span>}
</header>
{/* Messages */}
<MessageList view={view} />
{/* Input */}
<div className="border-t border-zinc-800 px-4 py-3">
<div className="flex gap-2">
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={handleKeyDown}
placeholder="Type a message..."
className="flex-1 rounded-md bg-zinc-900 border border-zinc-700 px-3 py-2 text-sm text-zinc-200 placeholder-zinc-600 outline-none focus:border-zinc-500"
autoFocus
/>
{hasOwnTurns && (
<button
type="button"
onClick={() => transport.cancel({ own: true })}
className="rounded-md bg-red-900/60 px-4 py-2 text-sm font-medium text-red-300 hover:bg-red-900/80 transition-colors"
>
Stop
</button>
)}
<button
type="button"
onClick={handleSubmit}
disabled={!input.trim()}
className="rounded-md bg-zinc-700 px-4 py-2 text-sm font-medium text-zinc-200 hover:bg-zinc-600 disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
>
Send
</button>
</div>
</div>
</div>
<DebugPane
messages={view.nodes.map((n) => n.message)}
ablyMessages={ablyMessages}
activeTurns={activeTurns}
/>
</div>
);
}