fix(chat): respect user scroll and tighten markdown
parent
f7abd99a3e
commit
cb115f1a88
|
|
@ -612,6 +612,7 @@ async function consumeSSE(resp: Response, h: StreamEvents, signal?: AbortSignal)
|
|||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
buf += decoder.decode(value, { stream: true });
|
||||
buf = buf.replace(/\r\n/g, '\n');
|
||||
|
||||
let idx;
|
||||
while ((idx = buf.indexOf('\n\n')) !== -1) {
|
||||
|
|
@ -623,7 +624,11 @@ async function consumeSSE(resp: Response, h: StreamEvents, signal?: AbortSignal)
|
|||
let dataStr = '';
|
||||
for (const line of raw.split('\n')) {
|
||||
if (line.startsWith('event:')) event = line.slice(6).trim();
|
||||
else if (line.startsWith('data:')) dataStr += line.slice(5).trim();
|
||||
else if (line.startsWith('data:')) {
|
||||
let part = line.slice(5);
|
||||
if (part.startsWith(' ')) part = part.slice(1);
|
||||
dataStr += (dataStr ? '\n' : '') + part;
|
||||
}
|
||||
}
|
||||
if (!dataStr) continue;
|
||||
let data: any;
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ export default function ChatPage() {
|
|||
const bodyRef = useRef<HTMLDivElement>(null);
|
||||
const abortRef = useRef<AbortController | null>(null);
|
||||
const creatingSessionRef = useRef(false);
|
||||
const autoScrollRef = useRef(true);
|
||||
|
||||
// URL 参数 ?session=xxx&msg=yyy
|
||||
useEffect(() => {
|
||||
|
|
@ -88,12 +89,25 @@ export default function ChatPage() {
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [searchParams]);
|
||||
|
||||
const scrollBottom = () => {
|
||||
const scrollBottom = (force = false) => {
|
||||
if (!force && !autoScrollRef.current) return;
|
||||
requestAnimationFrame(() => {
|
||||
bodyRef.current?.scrollTo({ top: bodyRef.current.scrollHeight, behavior: 'smooth' });
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const el = bodyRef.current;
|
||||
if (!el) return;
|
||||
const onScroll = () => {
|
||||
const distance = el.scrollHeight - el.scrollTop - el.clientHeight;
|
||||
autoScrollRef.current = distance < 32;
|
||||
};
|
||||
el.addEventListener('scroll', onScroll, { passive: true });
|
||||
onScroll();
|
||||
return () => el.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
|
||||
const loadAgent = async () => {
|
||||
if (!id) {
|
||||
setAgent(null);
|
||||
|
|
@ -198,7 +212,7 @@ export default function ChatPage() {
|
|||
retrieved: [],
|
||||
toolCalls: []
|
||||
});
|
||||
scrollBottom();
|
||||
scrollBottom(true);
|
||||
|
||||
abortRef.current?.abort();
|
||||
const ctrl = new AbortController();
|
||||
|
|
@ -265,7 +279,7 @@ export default function ChatPage() {
|
|||
setSessionRefresh((t) => t + 1);
|
||||
setAttachments([]); // 用完即清
|
||||
setImageUrls([]);
|
||||
scrollBottom();
|
||||
scrollBottom(true);
|
||||
},
|
||||
onAborted: (data) => {
|
||||
// 已停止:保留已生成内容;user 消息也留下(用 tempUser 占位)
|
||||
|
|
@ -322,7 +336,7 @@ export default function ChatPage() {
|
|||
createdAt: Date.now()
|
||||
};
|
||||
setMessages((m) => [...(m || []), tempUser]);
|
||||
scrollBottom();
|
||||
scrollBottom(true);
|
||||
const attText = buildAttachmentsText();
|
||||
const content = attText ? `${text}\n\n${attText}` : text;
|
||||
const model = overrides.model || parseAgentModels(agent?.model)[0] || '';
|
||||
|
|
|
|||
|
|
@ -539,6 +539,30 @@ body {
|
|||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bubble.assistant h1,
|
||||
.bubble.assistant h2,
|
||||
.bubble.assistant h3,
|
||||
.bubble.assistant h4,
|
||||
.bubble.assistant h5,
|
||||
.bubble.assistant h6 {
|
||||
margin: 0.5em 0 0.25em;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.bubble.assistant ol,
|
||||
.bubble.assistant ul {
|
||||
margin: 0.4em 0;
|
||||
padding-left: 1.25em;
|
||||
}
|
||||
|
||||
.bubble.assistant li {
|
||||
margin: 0.15em 0;
|
||||
}
|
||||
|
||||
.bubble.assistant hr {
|
||||
margin: 0.8em 0;
|
||||
}
|
||||
|
||||
.chat-input-wrapper {
|
||||
width: 100%;
|
||||
max-width: 820px;
|
||||
|
|
|
|||
Loading…
Reference in New Issue