/* Chat mode — real API */ function ChatMode({ onHome, initialStep = 'select', initialProject = null }) { const [step, setStep] = React.useState(initialStep); const [project, setProject] = React.useState(initialProject); const [messages, setMessages] = React.useState([]); const [draft, setDraft] = React.useState(''); const [sending, setSending] = React.useState(false); const [arcProposal, setArcProposal] = React.useState(null); const [finalizing, setFinalizing] = React.useState(false); // Hooks must be declared before any conditional returns (Rules of Hooks) const bodyRef = React.useRef(null); React.useEffect(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [messages]); const startChat = async (proj) => { setProject(proj); setMessages([]); setArcProposal(null); try { const res = await API.chatStart(proj); if (res?.status === 'queued') { setStep('queued'); } else { setStep('chat'); } } catch (e) { if (e.status === 202) { setStep('queued'); } else { alert('Ошибка запуска чата: ' + e.message); } } }; const send = async () => { if (!draft.trim() || sending) return; const text = draft.trim(); setDraft(''); setMessages(m => [...m, { role: 'user', text }]); setSending(true); try { const res = await API.chatMessage(project, text); setMessages(m => [...m, { role: 'agent', text: res.reply }]); if (res.has_arc_proposal) { // fetch updated arcs via project endpoint — they're embedded in the reply text as JSON // parse from reply or mark for finalize setArcProposal('pending'); // signal to user } } catch (e) { setMessages(m => [...m, { role: 'error', text: 'Ошибка: ' + e.message }]); } finally { setSending(false); } }; const finalize = async () => { setFinalizing(true); try { const res = await API.chatFinalize(project); setMessages(m => [...m, { role: 'agent', text: `✓ Сохранено ${res.arcs_saved} арок: ${(res.arc_titles || []).join(', ')}. Агент начнёт планирование.` }]); setArcProposal(null); setStep('done'); } catch (e) { alert('Ошибка финализации: ' + e.message); } finally { setFinalizing(false); } }; const releaseChat = async () => { try { await API.chatRelease(project); } catch {} setStep('select'); setProject(null); setMessages([]); setArcProposal(null); }; if (step === 'select') { return ; } if (step === 'queued') { return setStep('chat')} onBack={() => { releaseChat(); }} />; } if (step === 'done') { return (
Арки сохранены
Планировщик запустит генерацию фаз автоматически. Следи за прогрессом в Inbox.
); } // step === 'chat' const hasProposal = arcProposal === 'pending'; return (
{sending ? 'M1 думает…' : 'M1 active'}} onHome={onHome} />
{messages.length === 0 && (
Опишите проект, что хотите построить. M1 предложит арки.
)}
{messages.map((m, i) => (
{m.text}
{m.role === 'user' ? 'you' : m.role === 'error' ? 'error' : 'M1'}
))} {sending && (
M1 обрабатывает…
)}
{hasProposal && (
)}