Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion frontend/src/components/chat/TaskInputPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useApiConfigStore } from '@/stores/apiConfigStore';
import { useChatStore } from '@/stores/chatStore';
import { AnimatePresence, motion } from 'framer-motion';
import { Paperclip, PauseCircle, Send, X, Upload } from 'lucide-react';
import React, { useRef, useState, useCallback } from 'react';
import React, { useRef, useState, useCallback, useEffect } from 'react';

export const TaskInputPanel: React.FC = () => {
const [message, setMessage] = useState('');
Expand All @@ -14,15 +14,28 @@ export const TaskInputPanel: React.FC = () => {
const [isDragging, setIsDragging] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const dropZoneRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);

const { sendMessage, stopChat, isLoading, isStreaming } = useChat();
const { geminiApiKey, backendHasApiKey } = useApiConfigStore();
const wasStopped = useChatStore((state) => state.wasStopped);
const draftMessage = useChatStore((state) => state.draftMessage);
const setDraftMessage = useChatStore((state) => state.setDraftMessage);

const isProcessing = (isStreaming || isLoading) && !wasStopped;
const hasContent = message.trim() || images.length > 0;
const canSend = hasContent && !isProcessing;

// Sync draft message from store to local state
useEffect(() => {
if (draftMessage) {
setMessage(draftMessage);
setDraftMessage(''); // Clear the draft in the store
// Focus the input
inputRef.current?.focus();
}
}, [draftMessage, setDraftMessage]);

// Process files (from input or drag & drop)
const processFiles = useCallback((files: FileList | File[]) => {
Array.from(files).forEach(file => {
Expand Down Expand Up @@ -251,6 +264,7 @@ export const TaskInputPanel: React.FC = () => {
)}
>
<input
ref={inputRef}
type="text"
value={message}
onChange={(e) => setMessage(e.target.value)}
Expand Down
9 changes: 8 additions & 1 deletion frontend/src/components/conversation/ConversationPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,19 @@ export const ConversationPanel: React.FC = () => {
};

const EmptyState: React.FC = () => {
const setDraftMessage = useChatStore((s) => s.setDraftMessage);

const suggestions = [
{ icon: Stethoscope, text: 'Analyze medical imaging' },
{ icon: FileSearch, text: 'Search clinical literature' },
{ icon: Brain, text: 'Draft clinical reports' },
{ icon: Globe, text: 'Find treatment guidelines' },
];

const handleSuggestionClick = (text: string) => {
setDraftMessage(text);
};

return (
<div className="flex flex-col items-center justify-center min-h-[60vh] text-center select-none">
{/* Logo */}
Expand Down Expand Up @@ -92,12 +98,13 @@ const EmptyState: React.FC = () => {
{suggestions.map((suggestion) => (
<button
key={suggestion.text}
onClick={() => handleSuggestionClick(suggestion.text)}
className={cn(
"flex items-center gap-2 px-4 py-2.5 rounded-xl",
"bg-card border border-card-border",
"text-sm text-foreground-secondary",
"hover:border-accent hover:text-accent hover:bg-accent-light/50",
"transition-all duration-200 cursor-default"
"transition-all duration-200 cursor-pointer"
)}
>
<suggestion.icon className="w-4 h-4" />
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/stores/chatStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ interface ChatState {
streamingContent: string
isSSEConnected: boolean
wasStopped: boolean
draftMessage: string

// Actions
addMessage: (message: ChatMessage) => void
Expand All @@ -31,6 +32,7 @@ interface ChatState {
setSSEConnected: (isConnected: boolean) => void
setWasStopped: (wasStopped: boolean) => void
clearMessages: () => void
setDraftMessage: (message: string) => void
reset: () => void
}

Expand All @@ -45,6 +47,7 @@ const initialState = {
streamingContent: '',
isSSEConnected: false,
wasStopped: false,
draftMessage: '',
}

export const useChatStore = create<ChatState>()(
Expand Down Expand Up @@ -124,6 +127,11 @@ export const useChatStore = create<ChatState>()(
state.messages = []
}),

setDraftMessage: (message) =>
set((state) => {
state.draftMessage = message
}),

reset: () =>
set((state) => {
Object.assign(state, initialState)
Expand Down