Improve message stream auto-scroll during streaming

This commit is contained in:
Shantur Rathore
2025-12-01 19:30:14 +00:00
parent fd23ea54b6
commit e91923ad99
7 changed files with 145 additions and 76 deletions

View File

@@ -618,6 +618,13 @@ function mutateToolPartPermission(
draft.updatedAt = Date.now()
}),
)
// Permission attachment/removal can change the rendered height of the
// message list (e.g., permission blocks or diffs), so bump the
// session revision to ensure auto-scroll reacts.
if (messageRecord.sessionId) {
store.setState("sessionRevisions", messageRecord.sessionId, (value: number = 0) => value + 1)
}
}
function attachPermissionToToolPart(instanceId: string, permission: Permission, active: boolean): void {

View File

@@ -439,10 +439,10 @@ export function createInstanceMessageStore(instanceId: string): InstanceMessageS
bufferPendingPart({ messageId: input.messageId, part: input.part, receivedAt: Date.now() })
return
}
const partId = ensurePartId(input.messageId, input.part, message.partIds.length)
const cloned = clonePart(input.part)
setState(
"messages",
input.messageId,
@@ -463,8 +463,13 @@ export function createInstanceMessageStore(instanceId: string): InstanceMessageS
}
}),
)
// Any part update can change the rendered height of the message
// list, so we treat it as a session revision for scroll purposes.
bumpSessionRevision(message.sessionId)
}
function flushPendingParts(messageId: string) {
const pending = state.pendingParts[messageId]
if (!pending || pending.length === 0) {

View File

@@ -81,20 +81,25 @@ function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | Mes
if (event.type === "message.part.updated") {
const rawPart = event.properties?.part
if (!rawPart) return
const part = normalizeMessagePart(rawPart)
const sessionId = typeof part.sessionID === "string" ? part.sessionID : undefined
const messageId = typeof part.messageID === "string" ? part.messageID : undefined
const messageInfo = (event as any)?.properties?.message as MessageInfo | undefined
const fallbackSessionId = typeof messageInfo?.sessionID === "string" ? messageInfo.sessionID : undefined
const fallbackMessageId = typeof messageInfo?.id === "string" ? messageInfo.id : undefined
const sessionId = typeof part.sessionID === "string" ? part.sessionID : fallbackSessionId
const messageId = typeof part.messageID === "string" ? part.messageID : fallbackMessageId
if (!sessionId || !messageId) return
const session = instanceSessions.get(sessionId)
if (!session) return
const store = messageStoreBus.getOrCreate(instanceId)
const messageInfo = (event as any)?.properties?.message as MessageInfo | undefined
const role: MessageRole = resolveMessageRole(messageInfo)
const createdAt = typeof messageInfo?.time?.created === "number" ? messageInfo.time.created : Date.now()
let record = store.getMessage(messageId)
if (!record) {
const pendingId = findPendingMessageId(store, sessionId, role)
@@ -119,8 +124,9 @@ function handleMessageUpdate(instanceId: string, event: MessageUpdateEvent | Mes
if (messageInfo) {
upsertMessageInfoV2(instanceId, messageInfo, { status: "streaming" })
}
applyPartUpdateV2(instanceId, { ...part, sessionID: sessionId, messageID: messageId })
applyPartUpdateV2(instanceId, part)
updateSessionInfo(instanceId, sessionId)
refreshPermissionsForSession(instanceId, sessionId)