()
| 18 | import { useChatList } from '@/hooks/useChatList'; |
| 19 | import { useChatStream } from '@/hooks/useChatStream'; |
| 20 | import { CodeEngine } from './code-engine/code-engine'; |
| 21 | import { useProjectStatusMonitor } from '@/hooks/useProjectStatusMonitor'; |
| 22 | import { useIsMobile } from '@/hooks/useIsMobile'; |
| 23 | import { cn } from '@/lib/utils'; |
| 24 | import { extractQuestions } from '@/components/chat/question-card'; |
| 25 | import { useAuthContext } from '@/providers/AuthProvider'; |
| 26 | |
| 27 | export default function Chat() { |
| 28 | // Initialize state, refs, and custom hooks |
| 29 | const { isAuthorized } = useAuthContext(); |
| 30 | const urlParams = new URLSearchParams(window.location.search); |
| 31 | const [chatId, setChatId] = useState(''); |
| 32 | const [messages, setMessages] = useState([]); |
| 33 | const [input, setInput] = useState(''); |
| 34 | const formRef = useRef<HTMLFormElement>(null); |
| 35 | const { models } = useModels(); |
| 36 | // useModels resolves asynchronously, so seeding state from models[0] at mount |
| 37 | // pins whatever the fallback was. Adopt the first real model once it lands; |
| 38 | // until then send '' and let the backend apply its configured default. |
| 39 | const [selectedModel, setSelectedModel] = useState(''); |
| 40 | useEffect(() => { |
| 41 | if (!selectedModel && models.length > 0) setSelectedModel(models[0]); |
| 42 | }, [models, selectedModel]); |
| 43 | const { refetchChats } = useChatList(); |
| 44 | // A 18/82 horizontal split has no meaning on a phone: one pane at a time, |
| 45 | // with a slim switcher on top. |
| 46 | const isMobile = useIsMobile(); |
| 47 | const [mobilePane, setMobilePane] = useState<'chat' | 'preview'>('chat'); |
| 48 | |
| 49 | // Project status monitoring for the current chat |
| 50 | const { isReady, projectId, chatModel, error } = |
| 51 | useProjectStatusMonitor(chatId); |
| 52 | |
| 53 | // The chat remembers which model it was created with; prefer that over the |
| 54 | // head of the configured list, so the first turn runs on what was picked. |
| 55 | useEffect(() => { |
| 56 | if (chatModel) setSelectedModel(chatModel); |
| 57 | }, [chatModel]); |
| 58 | |
| 59 | // Switching mid-chat sticks: the choice is written back to the chat so a |
| 60 | // reload does not silently revert to the creation-time model. |
| 61 | const [updateChatModel] = useMutation(UPDATE_CHAT_MODEL); |
| 62 | const changeModel = useCallback( |
| 63 | (model: string) => { |
| 64 | setSelectedModel(model); |
| 65 | if (chatId) { |
| 66 | updateChatModel({ variables: { chatId, model } }).catch(() => |
| 67 | toast.error('Could not save the model choice') |
| 68 | ); |
| 69 | } |
| 70 | }, |
| 71 | [chatId, updateChatModel] |
| 72 | ); |
| 73 | |
| 74 | // Apollo query to fetch chat history |
| 75 | useQuery(GET_CHAT_HISTORY, { |
| 76 | variables: { chatId }, |
| 77 | skip: !isAuthorized || !chatId, |
nothing calls this directly
no test coverage detected