diff --git a/frontend/src/components/transcript-debug/ChatContainer.vue b/frontend/src/components/transcript-debug/ChatContainer.vue index 1c59c43..c7f5504 100644 --- a/frontend/src/components/transcript-debug/ChatContainer.vue +++ b/frontend/src/components/transcript-debug/ChatContainer.vue @@ -380,26 +380,34 @@ defineExpose({ selectMode, toggleSelectMode, allCollapsed, collapseAllExceptLast // These skip the bounce animation and get a smooth transition instead const resolvedUuids = ref(new Set()) +// Force scroll to the absolute bottom of the container +function scrollToBottom() { + if (!scrollContainer.value) return + const el = scrollContainer.value + el.scrollTop = el.scrollHeight + // Second pass: content-visibility: auto can cause reflow after initial paint, + // so re-scroll after the browser finishes layout + requestAnimationFrame(() => { + el.scrollTop = el.scrollHeight + }) +} + // Scroll to bottom when a new transcript is loaded watch( () => props.conversation.sessionId, async () => { await nextTick() - if (scrollContainer.value) { - scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight - } + scrollToBottom() }, { immediate: true } ) -// Auto-scroll to bottom when messages change +// Auto-scroll to bottom when messages change (every WS update) watch( () => props.conversation.messages.length, async () => { await nextTick() - if (scrollContainer.value) { - scrollContainer.value.scrollTop = scrollContainer.value.scrollHeight - } + scrollToBottom() } ) @@ -674,7 +682,6 @@ function formatDuration(start: string, end: string): string { /> props.terminalReady === false) // explicitly false, not null +// terminalReady: null = no terminal, false = starting, true = ready const noTerminal = computed(() => props.terminalReady === null) -const canSend = computed(() => props.terminalReady === true && !props.processing) +const canSend = computed(() => props.terminalReady === true) const isDisabled = computed(() => !input.value.trim() || !canSend.value) function handleSend() { @@ -66,24 +64,12 @@ watch(() => props.voiceTranscript, (newText) => {