diff --git a/apps/agent/entrypoints/options/App.tsx b/apps/agent/entrypoints/options/App.tsx index aba8ee18..c0647505 100644 --- a/apps/agent/entrypoints/options/App.tsx +++ b/apps/agent/entrypoints/options/App.tsx @@ -3,7 +3,7 @@ import { HashRouter, Navigate, Route, Routes } from 'react-router' import { AISettingsPage } from './ai-settings/AISettingsPage' import { ConnectMCP } from './connect-mcp/ConnectMCP' import { CustomizationPage } from './customization/CustomizationPage' -import { JTBDAgentPage } from './jtbd-agent/jtbd-agent-page' +import { SurveyPage } from './jtbd-agent' import { DashboardLayout } from './layout/DashboardLayout' import { LlmHubPage } from './llm-hub/LlmHubPage' import { MCPSettingsPage } from './mcp-settings/MCPSettingsPage' @@ -36,7 +36,7 @@ export const App: FC = () => { element={} /> } /> - } /> + } /> diff --git a/apps/agent/entrypoints/options/jtbd-agent/jtbd-agent-chat.tsx b/apps/agent/entrypoints/options/jtbd-agent/chat.tsx similarity index 50% rename from apps/agent/entrypoints/options/jtbd-agent/jtbd-agent-chat.tsx rename to apps/agent/entrypoints/options/jtbd-agent/chat.tsx index b095bdf8..57e5aee2 100644 --- a/apps/agent/entrypoints/options/jtbd-agent/jtbd-agent-chat.tsx +++ b/apps/agent/entrypoints/options/jtbd-agent/chat.tsx @@ -4,7 +4,9 @@ import { MessageResponse } from '@/components/ai-elements/message' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { cn } from '@/lib/utils' -import type { Message } from './use-jtbd-agent-chat' +import type { Message } from './use-chat' +import { useVoiceInput } from './use-voice-input' +import { VoiceInputButton } from './voice-input-button' interface Props { messages: Message[] @@ -39,7 +41,29 @@ const MessageBubble: FC<{ message: Message }> = ({ message }) => { ) } -export const JTBDAgentChat: FC = ({ +const WAVEFORM_BARS = [0, 1, 2, 3, 4] as const + +const WaveformIndicator: FC<{ level: number }> = ({ level }) => { + return ( +
+ {WAVEFORM_BARS.map((barIndex) => { + const barLevel = Math.max( + 0.2, + Math.sin((barIndex / WAVEFORM_BARS.length) * Math.PI) * (level / 100), + ) + return ( +
+ ) + })} +
+ ) +} + +export const Chat: FC = ({ messages, isStreaming, onSendMessage, @@ -48,15 +72,28 @@ export const JTBDAgentChat: FC = ({ const [input, setInput] = useState('') const messagesEndRef = useRef(null) + const voice = useVoiceInput() + const messagesLength = messages.length // biome-ignore lint/correctness/useExhaustiveDependencies: intentionally scroll on message count change useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }) }, [messagesLength]) + // Insert transcript into input when transcription completes + useEffect(() => { + if (voice.transcript && !voice.isTranscribing) { + setInput((prev) => { + const separator = prev.trim() ? ' ' : '' + return prev + separator + voice.transcript + }) + voice.clearTranscript() + } + }, [voice.transcript, voice.isTranscribing, voice.clearTranscript]) + const handleSubmit = (e: FormEvent) => { e.preventDefault() - if (!input.trim() || isStreaming) return + if (!input.trim() || isStreaming || voice.isRecording) return onSendMessage(input.trim()) setInput('') } @@ -68,6 +105,9 @@ export const JTBDAgentChat: FC = ({ } } + const isInputDisabled = + isStreaming || voice.isRecording || voice.isTranscribing + return (
@@ -78,16 +118,43 @@ export const JTBDAgentChat: FC = ({
+ {voice.error && ( +
{voice.error}
+ )} +
-