Precompute message display parts and pause scroll handling

This commit is contained in:
Shantur Rathore
2025-10-28 12:10:43 +00:00
parent 3a15ba7f76
commit 79e4931b28
3 changed files with 62 additions and 17 deletions

View File

@@ -168,16 +168,20 @@ export default function MessageStream(props: MessageStreamProps) {
} }
function handleScroll() { function handleScroll() {
if (!containerRef) return // Scroll handling temporarily disabled during testing
// if (!containerRef) return
const { scrollTop, scrollHeight, clientHeight } = containerRef //
const isAtBottom = scrollHeight - scrollTop - clientHeight < 50 // const { scrollTop, scrollHeight, clientHeight } = containerRef
// const isAtBottom = scrollHeight - scrollTop - clientHeight < 50
setAutoScroll(isAtBottom) //
setShowScrollButton(!isAtBottom) // setAutoScroll(isAtBottom)
// setShowScrollButton(!isAtBottom)
} }
const displayItems = createMemo(() => { const displayItems = createMemo(() => {
// Ensure memo reacts to preference changes
preferences().showThinkingBlocks
const items: DisplayItem[] = [] const items: DisplayItem[] = []
let lastAssistantIndex = -1 let lastAssistantIndex = -1
@@ -197,9 +201,11 @@ export default function MessageStream(props: MessageStreamProps) {
break break
} }
const textParts = message.parts.filter((p) => p.type === "text" && !p.synthetic) // Use precomputed displayParts, fallback to empty arrays if not available
const toolParts = message.parts.filter((p) => p.type === "tool") const displayParts = message.displayParts || { text: [], tool: [], reasoning: [] }
const reasoningParts = preferences().showThinkingBlocks ? message.parts.filter((p) => p.type === "reasoning") : [] const textParts = displayParts.text
const toolParts = displayParts.tool
const reasoningParts = displayParts.reasoning
const isQueued = message.type === "user" && (lastAssistantIndex === -1 || index > lastAssistantIndex) const isQueued = message.type === "user" && (lastAssistantIndex === -1 || index > lastAssistantIndex)
@@ -229,10 +235,11 @@ export default function MessageStream(props: MessageStreamProps) {
const itemsLength = () => displayItems().length const itemsLength = () => displayItems().length
createEffect(() => { createEffect(() => {
// Scroll handling temporarily disabled during testing
itemsLength() itemsLength()
if (autoScroll()) { // if (autoScroll()) {
setTimeout(scrollToBottom, 0) // setTimeout(scrollToBottom, 0)
} // }
}) })
return ( return (

View File

@@ -1,8 +1,9 @@
import { createSignal } from "solid-js" import { createSignal } from "solid-js"
import type { Session, Agent, Provider } from "../types/session" 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 { instances } from "./instances"
import { sseManager } from "../lib/sse-manager" import { sseManager } from "../lib/sse-manager"
import { preferences } from "./preferences"
interface SessionInfo { interface SessionInfo {
tokens: number tokens: number
@@ -81,6 +82,24 @@ function removeSessionIndexes(instanceId: string) {
sessionIndexes.delete(instanceId) 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) { function withSession(instanceId: string, sessionId: string, updater: (session: Session) => void) {
const instanceSessions = sessions().get(instanceId) const instanceSessions = sessions().get(instanceId)
if (!instanceSessions) return if (!instanceSessions) return
@@ -684,7 +703,7 @@ async function loadMessages(instanceId: string, sessionId: string, force = false
messagesInfo.set(messageId, info) messagesInfo.set(messageId, info)
return { const message: Message = {
id: messageId, id: messageId,
sessionId, sessionId,
type: role === "user" ? "user" : "assistant", type: role === "user" ? "user" : "assistant",
@@ -692,6 +711,10 @@ async function loadMessages(instanceId: string, sessionId: string, force = false
timestamp: info.time?.created || Date.now(), timestamp: info.time?.created || Date.now(),
status: "complete" as const, status: "complete" as const,
} }
message.displayParts = computeDisplayParts(message, preferences().showThinkingBlocks)
return message
}) })
let agentName = "" let agentName = ""
@@ -794,7 +817,7 @@ function handleMessageUpdate(instanceId: string, event: any): void {
if (messageIndex === undefined) { if (messageIndex === undefined) {
// Create new message // Create new message
const newMessage = { const newMessage: Message = {
id: part.messageID, id: part.messageID,
sessionId: part.sessionID, sessionId: part.sessionID,
type: "assistant" as const, type: "assistant" as const,
@@ -803,6 +826,8 @@ function handleMessageUpdate(instanceId: string, event: any): void {
status: "streaming" as const, status: "streaming" as const,
} }
newMessage.displayParts = computeDisplayParts(newMessage, preferences().showThinkingBlocks)
let insertIndex = session.messages.length let insertIndex = session.messages.length
for (let i = session.messages.length - 1; i >= 0; i--) { for (let i = session.messages.length - 1; i >= 0; i--) {
if (session.messages[i].id < newMessage.id) { 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.status = message.status === "sending" ? "streaming" : message.status
message.parts = baseParts message.parts = baseParts
message.displayParts = computeDisplayParts(message, preferences().showThinkingBlocks)
// Update message index if ID changed // Update message index if ID changed
if (oldId !== message.id) { if (oldId !== message.id) {
index.messageIndex.delete(oldId) index.messageIndex.delete(oldId)
@@ -937,7 +964,7 @@ function handleMessageUpdate(instanceId: string, event: any): void {
} }
} else { } else {
// Append new message // Append new message
const newMessage = { const newMessage: Message = {
id: info.id, id: info.id,
sessionId: info.sessionID, sessionId: info.sessionID,
type: (info.role === "user" ? "user" : "assistant") as "user" | "assistant", type: (info.role === "user" ? "user" : "assistant") as "user" | "assistant",
@@ -946,6 +973,8 @@ function handleMessageUpdate(instanceId: string, event: any): void {
status: "complete" as const, status: "complete" as const,
} }
newMessage.displayParts = computeDisplayParts(newMessage, preferences().showThinkingBlocks)
let insertIndex = session.messages.length let insertIndex = session.messages.length
for (let i = session.messages.length - 1; i >= 0; i--) { for (let i = session.messages.length - 1; i >= 0; i--) {
if (session.messages[i].id < newMessage.id) { if (session.messages[i].id < newMessage.id) {
@@ -1083,6 +1112,8 @@ async function sendMessage(
status: "sending", status: "sending",
} }
optimisticMessage.displayParts = computeDisplayParts(optimisticMessage, preferences().showThinkingBlocks)
withSession(instanceId, sessionId, (session) => { withSession(instanceId, sessionId, (session) => {
session.messages.push(optimisticMessage) session.messages.push(optimisticMessage)
const index = getSessionIndex(instanceId, sessionId) const index = getSessionIndex(instanceId, sessionId)

View File

@@ -1,3 +1,9 @@
export interface MessageDisplayParts {
text: any[]
tool: any[]
reasoning: any[]
}
export interface Message { export interface Message {
id: string id: string
sessionId: string sessionId: string
@@ -5,4 +11,5 @@ export interface Message {
parts: any[] parts: any[]
timestamp: number timestamp: number
status: "sending" | "sent" | "streaming" | "complete" | "error" status: "sending" | "sent" | "streaming" | "complete" | "error"
displayParts?: MessageDisplayParts
} }