From 79e4931b2859c71c5290a0d23c3fa5536bf6e548 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Tue, 28 Oct 2025 12:10:43 +0000 Subject: [PATCH] Precompute message display parts and pause scroll handling --- src/components/message-stream.tsx | 33 +++++++++++++++----------- src/stores/sessions.ts | 39 +++++++++++++++++++++++++++---- src/types/message.ts | 7 ++++++ 3 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/components/message-stream.tsx b/src/components/message-stream.tsx index 0af80ed1..3980df60 100644 --- a/src/components/message-stream.tsx +++ b/src/components/message-stream.tsx @@ -168,16 +168,20 @@ export default function MessageStream(props: MessageStreamProps) { } function handleScroll() { - if (!containerRef) return - - const { scrollTop, scrollHeight, clientHeight } = containerRef - const isAtBottom = scrollHeight - scrollTop - clientHeight < 50 - - setAutoScroll(isAtBottom) - setShowScrollButton(!isAtBottom) + // Scroll handling temporarily disabled during testing + // if (!containerRef) return + // + // const { scrollTop, scrollHeight, clientHeight } = containerRef + // const isAtBottom = scrollHeight - scrollTop - clientHeight < 50 + // + // setAutoScroll(isAtBottom) + // setShowScrollButton(!isAtBottom) } const displayItems = createMemo(() => { + // Ensure memo reacts to preference changes + preferences().showThinkingBlocks + const items: DisplayItem[] = [] let lastAssistantIndex = -1 @@ -197,9 +201,11 @@ export default function MessageStream(props: MessageStreamProps) { break } - const textParts = message.parts.filter((p) => p.type === "text" && !p.synthetic) - const toolParts = message.parts.filter((p) => p.type === "tool") - const reasoningParts = preferences().showThinkingBlocks ? message.parts.filter((p) => p.type === "reasoning") : [] + // Use precomputed displayParts, fallback to empty arrays if not available + const displayParts = message.displayParts || { text: [], tool: [], reasoning: [] } + const textParts = displayParts.text + const toolParts = displayParts.tool + const reasoningParts = displayParts.reasoning const isQueued = message.type === "user" && (lastAssistantIndex === -1 || index > lastAssistantIndex) @@ -229,10 +235,11 @@ export default function MessageStream(props: MessageStreamProps) { const itemsLength = () => displayItems().length createEffect(() => { + // Scroll handling temporarily disabled during testing itemsLength() - if (autoScroll()) { - setTimeout(scrollToBottom, 0) - } + // if (autoScroll()) { + // setTimeout(scrollToBottom, 0) + // } }) return ( diff --git a/src/stores/sessions.ts b/src/stores/sessions.ts index 27336da3..8ddababe 100644 --- a/src/stores/sessions.ts +++ b/src/stores/sessions.ts @@ -1,8 +1,9 @@ import { createSignal } from "solid-js" import type { Session, Agent, Provider } from "../types/session" -import type { Message } from "../types/message" +import type { Message, MessageDisplayParts } from "../types/message" import { instances } from "./instances" import { sseManager } from "../lib/sse-manager" +import { preferences } from "./preferences" interface SessionInfo { tokens: number @@ -81,6 +82,24 @@ function removeSessionIndexes(instanceId: string) { sessionIndexes.delete(instanceId) } +function computeDisplayParts(message: Message, showThinking: boolean): MessageDisplayParts { + const text: any[] = [] + const tool: any[] = [] + const reasoning: any[] = [] + + for (const part of message.parts) { + if (part.type === "text" && !part.synthetic) { + text.push(part) + } else if (part.type === "tool") { + tool.push(part) + } else if (part.type === "reasoning" && showThinking) { + reasoning.push(part) + } + } + + return { text, tool, reasoning } +} + function withSession(instanceId: string, sessionId: string, updater: (session: Session) => void) { const instanceSessions = sessions().get(instanceId) if (!instanceSessions) return @@ -684,7 +703,7 @@ async function loadMessages(instanceId: string, sessionId: string, force = false messagesInfo.set(messageId, info) - return { + const message: Message = { id: messageId, sessionId, type: role === "user" ? "user" : "assistant", @@ -692,6 +711,10 @@ async function loadMessages(instanceId: string, sessionId: string, force = false timestamp: info.time?.created || Date.now(), status: "complete" as const, } + + message.displayParts = computeDisplayParts(message, preferences().showThinkingBlocks) + + return message }) let agentName = "" @@ -794,7 +817,7 @@ function handleMessageUpdate(instanceId: string, event: any): void { if (messageIndex === undefined) { // Create new message - const newMessage = { + const newMessage: Message = { id: part.messageID, sessionId: part.sessionID, type: "assistant" as const, @@ -803,6 +826,8 @@ function handleMessageUpdate(instanceId: string, event: any): void { status: "streaming" as const, } + newMessage.displayParts = computeDisplayParts(newMessage, preferences().showThinkingBlocks) + let insertIndex = session.messages.length for (let i = session.messages.length - 1; i >= 0; i--) { if (session.messages[i].id < newMessage.id) { @@ -854,6 +879,8 @@ function handleMessageUpdate(instanceId: string, event: any): void { message.status = message.status === "sending" ? "streaming" : message.status message.parts = baseParts + message.displayParts = computeDisplayParts(message, preferences().showThinkingBlocks) + // Update message index if ID changed if (oldId !== message.id) { index.messageIndex.delete(oldId) @@ -937,7 +964,7 @@ function handleMessageUpdate(instanceId: string, event: any): void { } } else { // Append new message - const newMessage = { + const newMessage: Message = { id: info.id, sessionId: info.sessionID, type: (info.role === "user" ? "user" : "assistant") as "user" | "assistant", @@ -946,6 +973,8 @@ function handleMessageUpdate(instanceId: string, event: any): void { status: "complete" as const, } + newMessage.displayParts = computeDisplayParts(newMessage, preferences().showThinkingBlocks) + let insertIndex = session.messages.length for (let i = session.messages.length - 1; i >= 0; i--) { if (session.messages[i].id < newMessage.id) { @@ -1083,6 +1112,8 @@ async function sendMessage( status: "sending", } + optimisticMessage.displayParts = computeDisplayParts(optimisticMessage, preferences().showThinkingBlocks) + withSession(instanceId, sessionId, (session) => { session.messages.push(optimisticMessage) const index = getSessionIndex(instanceId, sessionId) diff --git a/src/types/message.ts b/src/types/message.ts index 1be64000..01dbe187 100644 --- a/src/types/message.ts +++ b/src/types/message.ts @@ -1,3 +1,9 @@ +export interface MessageDisplayParts { + text: any[] + tool: any[] + reasoning: any[] +} + export interface Message { id: string sessionId: string @@ -5,4 +11,5 @@ export interface Message { parts: any[] timestamp: number status: "sending" | "sent" | "streaming" | "complete" | "error" + displayParts?: MessageDisplayParts }