feat: add TranscriptChatModal and TranscriptChatButton components

This commit is contained in:
Igor Loskutov
2026-01-12 19:58:32 -05:00
parent 544793a24f
commit 39e0b89e67
3 changed files with 113 additions and 9 deletions

View File

@@ -1,14 +1,23 @@
{ {
"assignee": null, "assignee": "igor.loskutoff@gmail.com",
"claim_note": "", "claim_note": "",
"claimed_at": null, "claimed_at": "2026-01-13T00:52:01.366013Z",
"created_at": "2026-01-12T22:41:17.835044Z", "created_at": "2026-01-12T22:41:17.835044Z",
"depends_on": [], "depends_on": [],
"epic": "fn-1", "epic": "fn-1",
"evidence": {
"commits": [
"d5a77087594b3b54150e78466132f2dfa001901b"
],
"prs": [],
"tests": [
"pnpm tsc --noEmit"
]
},
"id": "fn-1.6", "id": "fn-1.6",
"priority": null, "priority": null,
"spec_path": ".flow/tasks/fn-1.6.md", "spec_path": ".flow/tasks/fn-1.6.md",
"status": "todo", "status": "done",
"title": "Chat dialog component", "title": "Chat dialog component",
"updated_at": "2026-01-12T22:41:17.835218Z" "updated_at": "2026-01-13T00:58:52.502248Z"
} }

View File

@@ -7,9 +7,11 @@ TBD
- [ ] TBD - [ ] TBD
## Done summary ## Done summary
TBD - Created TranscriptChatModal component with Dialog UI
- Added TranscriptChatButton floating action button
- Implemented message display with streaming indicator
- Added input field with Enter key support
## Evidence ## Evidence
- Commits: - Commits: d5a77087594b3b54150e78466132f2dfa001901b
- Tests: - Tests: pnpm tsc --noEmit
- PRs: - PRs:

View File

@@ -0,0 +1,93 @@
"use client";
import { useState } from "react";
import { Box, Dialog, Input, IconButton } from "@chakra-ui/react";
import { MessageCircle } from "lucide-react";
import type { Message } from "./useTranscriptChat";
interface TranscriptChatModalProps {
open: boolean;
onClose: () => void;
messages: Message[];
sendMessage: (text: string) => void;
isStreaming: boolean;
currentStreamingText: string;
}
export function TranscriptChatModal({
open,
onClose,
messages,
sendMessage,
isStreaming,
currentStreamingText,
}: TranscriptChatModalProps) {
const [input, setInput] = useState("");
const handleSend = () => {
if (!input.trim()) return;
sendMessage(input);
setInput("");
};
return (
<Dialog.Root open={open} onOpenChange={(e) => !e.open && onClose()}>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content maxW="500px" h="600px">
<Dialog.Header>Transcript Chat</Dialog.Header>
<Dialog.Body overflowY="auto">
{messages.map((msg) => (
<Box
key={msg.id}
p={3}
mb={2}
bg={msg.role === "user" ? "blue.50" : "gray.50"}
borderRadius="md"
>
{msg.text}
</Box>
))}
{isStreaming && (
<Box p={3} bg="gray.50" borderRadius="md">
{currentStreamingText}
<Box as="span" className="animate-pulse">
</Box>
</Box>
)}
</Dialog.Body>
<Dialog.Footer>
<Input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === "Enter" && handleSend()}
placeholder="Ask about transcript..."
disabled={isStreaming}
/>
</Dialog.Footer>
</Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>
);
}
export function TranscriptChatButton({ onClick }: { onClick: () => void }) {
return (
<IconButton
position="fixed"
bottom="24px"
right="24px"
onClick={onClick}
size="lg"
colorPalette="blue"
borderRadius="full"
aria-label="Open chat"
>
<MessageCircle />
</IconButton>
);
}