fixed reasoning

This commit is contained in:
2026-06-10 15:09:36 +03:00
parent e9762d7921
commit 827f9016cd
8 changed files with 220 additions and 14 deletions
+58 -6
View File
@@ -1,4 +1,4 @@
import { FormEvent, useEffect, useRef, useState } from "react";
import { FormEvent, useCallback, useEffect, useRef, useState } from "react";
import ReactMarkdown from "react-markdown";
import { api, ChatMessage, ChatSession } from "../api/client";
import PomodoroWidget from "../components/PomodoroWidget";
@@ -42,7 +42,9 @@ export default function Chat() {
"thinking",
);
const [liveNotices, setLiveNotices] = useState<string[]>([]);
const bottomRef = useRef<HTMLDivElement>(null);
const messagesRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLTextAreaElement>(null);
const scrollRafRef = useRef<number | null>(null);
const { status: pomodoroStatus, refresh: refreshPomodoro } = usePomodoro();
const [lastNotifySeq, setLastNotifySeq] = useState(0);
@@ -69,9 +71,33 @@ export default function Chat() {
}
}, [activeId]);
const scrollToBottom = useCallback((smooth = false) => {
const container = messagesRef.current;
if (!container) return;
container.scrollTo({
top: container.scrollHeight,
behavior: smooth ? "smooth" : "auto",
});
}, []);
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: "smooth" });
}, [messages, streaming, liveNotices, loading]);
if (scrollRafRef.current !== null) {
cancelAnimationFrame(scrollRafRef.current);
}
scrollRafRef.current = requestAnimationFrame(() => {
scrollToBottom(!streaming);
scrollRafRef.current = null;
});
return () => {
if (scrollRafRef.current !== null) {
cancelAnimationFrame(scrollRafRef.current);
}
};
}, [messages, streaming, liveNotices, loading, scrollToBottom]);
const dismissKeyboard = useCallback(() => {
inputRef.current?.blur();
}, []);
const waitingForStream = loading && !streaming;
const pendingLabel =
@@ -119,6 +145,7 @@ export default function Chat() {
const text = input.trim();
setInput("");
dismissKeyboard();
setLoading(true);
setStreaming("");
setPendingPhase("thinking");
@@ -215,7 +242,29 @@ export default function Chat() {
<div className="chat-empty">Создайте новый чат, чтобы начать</div>
) : (
<>
<div className="messages">
<div className="chat-mobile-bar">
<select
className="chat-session-select"
value={activeId}
onChange={(e) => setActiveId(Number(e.target.value))}
aria-label="Выбор чата"
>
{sessions.map((session) => (
<option key={session.id} value={session.id}>
{session.title}
</option>
))}
</select>
<button type="button" className="chat-mobile-new" onClick={handleNewChat}>
+ Новый
</button>
</div>
<div
className="messages"
ref={messagesRef}
onClick={dismissKeyboard}
>
{visibleMessages.map((msg) => (
<div key={msg.id} className={`message message-${msg.role}`}>
<div className="message-role">{roleLabel(msg.role, msg.content)}</div>
@@ -260,15 +309,18 @@ export default function Chat() {
</div>
</div>
)}
<div ref={bottomRef} />
<div className="messages-bottom-anchor" aria-hidden="true" />
</div>
<form className="chat-input" onSubmit={handleSubmit}>
<textarea
ref={inputRef}
value={input}
onChange={(e) => setInput(e.target.value)}
placeholder="Напишите сообщение..."
rows={2}
enterKeyHint="send"
autoComplete="off"
onKeyDown={(e) => {
if (e.key === "Enter" && !e.shiftKey) {
e.preventDefault();