fix(desktop): polish compaction indicator and preserve scrollback

Show a shimmering "Summarizing thread" label during auto-compaction, skip
the post-turn hydrate when compaction fired so the live transcript does not
collapse to the stored summary-only session.
This commit is contained in:
Brooklyn Nicholson
2026-06-14 02:48:48 -05:00
parent 715b691723
commit 1eb13744b4
4 changed files with 60 additions and 47 deletions
@@ -96,7 +96,7 @@ import { extractPreviewTargets } from '@/lib/preview-targets'
import { useEnterAnimation } from '@/lib/use-enter-animation'
import { cn } from '@/lib/utils'
import { playSpeechText, stopVoicePlayback } from '@/lib/voice-playback'
import { $compactionStatus } from '@/store/compaction'
import { $compactionActive } from '@/store/compaction'
import type { ComposerAttachment } from '@/store/composer'
import { notifyError } from '@/store/notifications'
import { $connection } from '@/store/session'
@@ -337,15 +337,25 @@ const StatusRow: FC<{ children: ReactNode; label: string } & React.ComponentProp
</div>
)
// Fixed label while auto-compaction runs — decoupled from backend status text.
const COMPACTION_LABEL = 'Summarizing thread'
const CompactionHint: FC = () => (
<span className="shimmer min-w-0 truncate text-muted-foreground/55">{COMPACTION_LABEL}</span>
)
const ResponseLoadingIndicator: FC = () => {
const { t } = useI18n()
const elapsed = useElapsedSeconds()
const compaction = useStore($compactionStatus)
const compacting = useStore($compactionActive)
return (
<StatusRow data-slot="aui_response-loading" label={compaction ?? t.assistant.thread.loadingResponse}>
<StatusRow
data-slot="aui_response-loading"
label={compacting ? COMPACTION_LABEL : t.assistant.thread.loadingResponse}
>
<span aria-hidden="true" className="dither inline-block size-3 rounded-[2px] text-midground/80 animate-pulse" />
{compaction && <span className="min-w-0 truncate">{compaction}</span>}
{compacting && <CompactionHint />}
<ActivityTimerText seconds={elapsed} />
</StatusRow>
)
@@ -380,7 +390,7 @@ const StreamStallIndicator: FC = () => {
})
const [stalled, setStalled] = useState(false)
const compaction = useStore($compactionStatus)
const compacting = useStore($compactionActive)
useEffect(() => {
setStalled(false)
@@ -389,8 +399,7 @@ const StreamStallIndicator: FC = () => {
return () => window.clearTimeout(id)
}, [activity])
// Compaction surfaces immediately; an ordinary stall waits out STREAM_STALL_S.
const active = stalled || Boolean(compaction)
const active = stalled || compacting
const elapsed = useElapsedSeconds(active)
if (!active) {
@@ -398,9 +407,13 @@ const StreamStallIndicator: FC = () => {
}
return (
<StatusRow className="mt-1.5" data-slot="aui_stream-stall" label={compaction ?? 'Hermes is thinking'}>
<StatusRow
className="mt-1.5"
data-slot="aui_stream-stall"
label={compacting ? COMPACTION_LABEL : 'Hermes is thinking'}
>
<span aria-hidden="true" className="dither inline-block size-3 rounded-[2px] text-midground/80 animate-pulse" />
{compaction && <span className="min-w-0 truncate">{compaction}</span>}
{compacting && <CompactionHint />}
<ActivityTimerText seconds={elapsed} />
</StatusRow>
)