fix(desktop): an empty HUD thread shouldn't paint a blank panel

A fresh thread has nothing to show, but the HUD showed a slab of frosted
glass above the bar anyway. Vibrancy is the window's whole content view, so
it frosts the full rectangle — fine while the band always filled the window,
wrong the moment there is no transcript to fill it. It stays off until there
is something to back.

The sheet had a 12px floor for the same reason: the breathing room above the
first row was added in CSS, so a zero-row transcript still measured 12. It is
folded into the measured height now and only applies when there are rows.
This commit is contained in:
Brooklyn Nicholson
2026-08-08 12:31:52 -05:00
parent 0c2cdccccc
commit 843c3abcc7
2 changed files with 14 additions and 10 deletions
+13 -9
View File
@@ -9,7 +9,7 @@ import { useI18n } from '@/i18n'
import { chatMessageText } from '@/lib/chat-messages'
import { closeHud } from '@/store/hud'
import { $activeSessionAwaitingInput } from '@/store/prompts'
import { $busy, $messages } from '@/store/session'
import { $busy, $messages, $messagesEmpty } from '@/store/session'
import { WiredPane } from '../contrib/wiring'
import { titlebarButtonClass } from '../shell/titlebar'
@@ -41,8 +41,8 @@ const HUD_DIM_MS = Math.round(HUD_FADE_MS * 1.5)
const HUD_COLLAPSE_MS = Math.round(HUD_FADE_MS * 0.66)
/** Breathing room the sheet keeps above the first row, so the fade has
* somewhere to land. Published to CSS, and used here to work out how much of
* the window the HUD actually occupies. */
* somewhere to land. Folded into the measured height rather than added in CSS,
* so an empty transcript measures a true zero instead of a 12px strip. */
const HUD_SHEET_OVERHANG_PX = 12
/** Composer on top, transcript always hanging below it — Spotlight's shape,
@@ -156,6 +156,10 @@ export function HudShell() {
const { t } = useI18n()
const [recent, holdBand] = useRecentActivity()
const held = useHudHeld()
// Nothing to back yet. Vibrancy is the window's whole content view, so on a
// fresh thread it frosts the empty space above the bar into a blank slab —
// the transcript's backing showing up before the transcript does.
const empty = useStore($messagesEmpty)
// Main holds the session id on this window's behalf, so leaving HUD mode can
// hand the app window back whatever conversation ended up here.
@@ -264,9 +268,10 @@ export function HudShell() {
const span =
!rows?.length || !box
? 0
: root.dataset.hudEdge === 'top'
? rows[rows.length - 1].getBoundingClientRect().bottom - box.top
: box.bottom - rows[0].getBoundingClientRect().top
: HUD_SHEET_OVERHANG_PX +
(root.dataset.hudEdge === 'top'
? rows[rows.length - 1].getBoundingClientRect().bottom - box.top
: box.bottom - rows[0].getBoundingClientRect().top)
root.style.setProperty('--hud-band-height', `${Math.max(0, Math.round(span))}px`)
@@ -297,7 +302,7 @@ export function HudShell() {
}
}, [])
useHudGlass(rootRef, recent || held)
useHudGlass(rootRef, (recent || held) && !empty)
useHudClickThrough(rootRef)
// Force the HOST layers transparent. index.html's pre-paint script writes an
@@ -332,8 +337,7 @@ export function HudShell() {
'--hud-fade': `${HUD_FADE_MS}ms`,
'--hud-collapse': `${HUD_COLLAPSE_MS}ms`,
'--hud-dim': `${HUD_DIM_MS}ms`,
'--hud-reveal': `${HUD_REVEAL_MS}ms`,
'--hud-sheet-overhang': `${HUD_SHEET_OVERHANG_PX}px`
'--hud-reveal': `${HUD_REVEAL_MS}ms`
} as CSSProperties
}
>
+1 -1
View File
@@ -2393,7 +2393,7 @@ button[data-slot='aui_msg-reactions'] svg {
The height below still tracks the transcript; only the offset animates on
exit, and it finishes ahead of the fade so the panel has left while the last
of the text is still going. */
height: min(100%, calc(var(--hud-band-height, 0px) + var(--hud-sheet-overhang, 0.75rem)));
height: min(100%, var(--hud-band-height, 0px));
translate: 0 var(--hud-exit-shift, 2.5rem);
opacity: 0;
transition: