From 775cc9ef115f2882e1089d03322d3be12e2d8802 Mon Sep 17 00:00:00 2001 From: wizardchen Date: Tue, 23 Jun 2026 20:09:01 +0800 Subject: [PATCH] fix(frontend): auto-scroll capped thinking block during streaming Keep scrolling the thinking detail panel to the bottom while reasoning content streams, not only before the first chunk arrives. --- .../RagPipelineProgress.style.test.mjs | 6 ++++ .../chat/components/RagPipelineProgress.vue | 36 +++++++++++++------ 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/frontend/src/views/chat/components/RagPipelineProgress.style.test.mjs b/frontend/src/views/chat/components/RagPipelineProgress.style.test.mjs index 112b6e570..7fc80f5ab 100644 --- a/frontend/src/views/chat/components/RagPipelineProgress.style.test.mjs +++ b/frontend/src/views/chat/components/RagPipelineProgress.style.test.mjs @@ -82,3 +82,9 @@ test('collapsed summary uses the same title-to-answer spacing as agent', () => { assert.match(source, /\.tree-container \{\s*margin: 0 0 16px;/) assert.match(source, /\.rag-pipeline-progress \{[\s\S]*margin: 0;/) }) + +test('rag pipeline auto-scrolls capped thinking detail while streaming', () => { + assert.match(source, /isThinkingStreaming/) + assert.match(source, /scrollThinkingDetailToBottom/) + assert.match(source, /watch\(thinkingContent[\s\S]*scrollThinkingDetailToBottom/) +}) diff --git a/frontend/src/views/chat/components/RagPipelineProgress.vue b/frontend/src/views/chat/components/RagPipelineProgress.vue index 2b031e1d6..5a2d1b3a8 100644 --- a/frontend/src/views/chat/components/RagPipelineProgress.vue +++ b/frontend/src/views/chat/components/RagPipelineProgress.vue @@ -348,6 +348,14 @@ const thinkingPending = computed( !props.session?.is_completed, ) +const isThinkingStreaming = computed( + () => + showThinkingStep.value && + thinkingExpanded.value && + !hasAnswer.value && + !props.session?.is_completed, +) + const visible = computed( () => steps.value.length > 0 || showPrePipelineWait.value || showThinkingStep.value, ) @@ -427,6 +435,16 @@ function toggleThinking() { thinkingExpanded.value = !thinkingExpanded.value } +function scrollThinkingDetailToBottom() { + nextTick(() => { + if (!rootElement.value) return + rootElement.value.querySelectorAll('.thinking-detail-content').forEach((el) => { + const htmlEl = el as HTMLElement + htmlEl.scrollTop = htmlEl.scrollHeight + }) + }) +} + watch(thinkingPending, (pending) => { if (pending) { thinkingExpanded.value = true @@ -440,17 +458,13 @@ watch(hasAnswer, (answered) => { }) watch(thinkingContent, () => { - if (!thinkingPending.value) return - nextTick(() => { - if (!rootElement.value) return - const els = rootElement.value.querySelectorAll('.thinking-detail-content') - els.forEach((el) => { - const htmlEl = el as HTMLElement - if (htmlEl.scrollHeight > htmlEl.clientHeight) { - htmlEl.scrollTop = htmlEl.scrollHeight - } - }) - }) + if (!isThinkingStreaming.value) return + scrollThinkingDetailToBottom() +}) + +watch(thinkingExpanded, (expanded) => { + if (!expanded || !isThinkingStreaming.value) return + scrollThinkingDetailToBottom() })