// src/web/pages/Chat.tsx
import { useMemo } from "react";
import {
useAgentChat, ChatMessageComponent as ChatMessage,
ChatInput, ToolCallsPanel, MessageType,
} from "@synthetiq/app-framework/web";
import type { ChatMessageData as ChatMessageType } from "@synthetiq/app-framework/web";
export function ChatPage() {
const {
sessions, currentSession, messages,
isProcessing, processingStage, isLoading,
sendMessage, cancelRequest, selectSession,
} = useAgentChat({});
const groupedMessages = useMemo(() => {
type MessageGroup =
| { type: "user"; message: ChatMessageType }
| { type: "tools"; messages: ChatMessageType[] }
| { type: "assistant"; message: ChatMessageType };
const groups: MessageGroup[] = [];
let currentToolGroup: ChatMessageType[] = [];
for (const msg of messages) {
if (msg.type === MessageType.USER_MESSAGE) {
if (currentToolGroup.length > 0) {
groups.push({ type: "tools", messages: currentToolGroup });
currentToolGroup = [];
}
groups.push({ type: "user", message: msg });
} else if (msg.type === MessageType.ASSISTANT_MESSAGE) {
if (currentToolGroup.length > 0) {
groups.push({ type: "tools", messages: currentToolGroup });
currentToolGroup = [];
}
groups.push({ type: "assistant", message: msg });
} else {
currentToolGroup.push(msg);
}
}
if (currentToolGroup.length > 0) {
groups.push({ type: "tools", messages: currentToolGroup });
}
return groups;
}, [messages]);
const handleSend = async (content: string) => {
if (currentSession) {
await sendMessage(content, currentSession.id);
} else {
await sendMessage(content, crypto.randomUUID(), true);
}
};
return (
<div className="flex flex-col h-full">
<div className="flex items-center gap-2 p-2 border-b">
<select
value={currentSession?.id || ""}
onChange={(e) => e.target.value && selectSession(e.target.value)}
>
<option value="" disabled>Select chat...</option>
{sessions.map((s) => <option key={s.id} value={s.id}>{s.name}</option>)}
</select>
<button onClick={() => selectSession("")}>New Chat</button>
</div>
<div className="flex-1 overflow-y-auto p-4 pb-24 space-y-4">
{groupedMessages.map((group, i) => {
const isLastGroup = i === groupedMessages.length - 1;
if (group.type === "user") {
return (
<div key={`user-${i}`}>
<ChatMessage message={group.message} />
{isProcessing && isLastGroup && (
<ToolCallsPanel messages={[]} isStreaming={true} processingStage={processingStage} />
)}
</div>
);
}
if (group.type === "tools") {
return (
<ToolCallsPanel
key={`tools-${i}`}
messages={group.messages}
isStreaming={isProcessing && isLastGroup}
processingStage={isLastGroup ? processingStage : null}
/>
);
}
if (group.type === "assistant") {
return <ChatMessage key={`assistant-${i}`} message={group.message} />;
}
return null;
})}
</div>
<ChatInput
onSend={handleSend}
disabled={isProcessing || isLoading}
isProcessing={isProcessing}
onCancel={cancelRequest}
/>
</div>
);
}