fix(dashboard): keep the metrics readout's toggle on screen with no frame - #8105
Conversation
…rame The capsule's metrics readout is an inline segment, not a popover: one button both shows the readings and toggles them away. Its open branch pushed a segment only when the system-metrics query had ERRORED, so the third state -- open, no frame, no error -- pushed nothing at all. `sysMetrics` is undefined for the whole of the first fetch AND for the retry window of a failing one, because react-query reports `isError` only once its retries are spent. On a host where /api/system hangs or never produces a frame the readout was therefore logically open with its toggle absent from the DOM, and the click aimed at it landed on the capsule's background: the reported "the cpu, metrics etc. doesn't open". Every open state now pushes a toggle. The pending one carries an em dash per metric rather than a spinner, which is the distinction the sibling usage segment already draws in this same capsule and for the same reason -- a spinner asserts a fetch is about to land, and on a host that never reports metrics that claim never comes true. It reuses the loaded branch's own "no valid reading" glyph, so the two open states differ in opacity rather than in shape. Second half, for the "something left in the background" artifact. The header carries `backdrop-filter`, and the two readout groups inside it carry `container-type: inline-size`, which implies layout containment. When a capsule segment mounted or unmounted inside one, the strip it vacated had to be re-sampled through that blur, and damage from a contained subtree did not always reach a backdrop sharing a layer with the content beneath it -- the old blurred pixels stayed on screen. `.topbar-glass` is now promoted to its own compositing layer, the same remedy #7931 applied to the sandbox-doc frames, and the reason the notification popover portals to <body> to escape this containing block rather than living inside it. Two spellings, because focus mode drives this element's `transform` from an inline style that wins over the rule: `backface-visibility:hidden` promotes on its own and nothing writes it. The two halves meet: the first one also removes the segment MOUNT the second one is sensitive to. The capsule used to gain a button and a divider when the frame landed; it now only re-renders text inside a button that was already there. No rung in the collapse ladder #7851 repaired is touched. The capture script asserts the actions group's content box -- what the container queries measure -- is byte-identical across the absent, pending and loaded scenes, and that nothing outside the capsule moves when the frame arrives. Scope is the two symptoms #7851 did not cover. The artifact half states the mechanism it fixes rather than claiming the whole of the report: a promoted layer and an unpromoted one render the same correct pixels, so no screenshot from this Linux host can distinguish them, and the reporter's macOS reproduction is outstanding. Its contract is pinned from the stylesheet source instead. Closes #7967
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS Root-cause fix — control presence now keyed to the open state, not the fetch state — with precedent-backed compositing remedy and regression pins for both halves. [DESIGN-REVIEWED] 80efedc |
UX Review (Fable 5) — ✅ PASSUX-level review of UX-Verdict: PASS The pending readout reuses the capsule's own dash-and-dim vocabulary, keeps its toggle alive, and every state now names itself to hover and AT. Suggestions
[UX-REVIEWED] 80efedc |
First Principles Review (Fable 5) — ✅ PASSPremise-level review of All checks are done. Every item traces to the reported defect, the conventions hold ( First-Principles-Verdict: PASS Both reported symptoms are fixed at the branch and the element that produced them, and every rider is declared, conventional evidence. What this change shipsIntent: let a user whose host never produces a metrics frame see and close the open readout, and stop the top bar leaving stale blur — a FIX, and the diff does exactly the job the description claims.
Depth checks that hold: the pending-state fix sits at the cause (the branch that pushed nothing), not a guard at the click site; the glass promotion's true cause is a compositor behavior outside this codebase, and the single [FIRST-PRINCIPLES-REVIEWED] 80efedc |
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsThis is a small, well-scoped frontend PR: it adds a pending-state toggle to the metrics readout in The candidate list contains no candidates, so there is nothing to falsify in Step 1. Independently checking the one non-trivial risk (Step 2): the No findings. [OPUS-REVIEWED] 80efedc Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
bolichen97
left a comment
There was a problem hiding this comment.
Approving on the strength of a full readiness audit of every open PR against main, not a
line-by-line reading of this diff — recording that plainly so the next reader knows what this
stamp does and does not cover.
Verified against this exact head SHA:
readiness: passedpresent, andPR Readiness— the one required status context onmain
(rulesetprotected-branches) — issuccesson this head.- No check run on this head is
failure,cancelled,timed_outor still in flight. Skipped
jobs are path-filtered conditionals, none of them required. mergeable: true, and the head is not far enough behindmainfor its green CI to describe a
base that no longer exists.- No surviving reviewer
CHANGES_REQUESTED: any such review is on an older commit and therefore
already dismissed bydismiss_stale_reviews_on_push. - Every issue comment, inline review comment and review thread was read and classified. Nothing
left is an unresolved human change request — the remainder is bot review-lane output, resolved
or outdated threads, explicitly non-blocking suggestions, and author status notes.
Auto-merge (squash) is armed, so this lands once every other ruleset requirement is met.
Problem / Motivation
Two of the three symptoms in #7967 on the macOS desktop app. The CSS half — the
collapse ladder blanking the capsule at narrow widths — was repaired by #7851; these
two were explicitly left standing.
nothing, because the control the click is aimed at is not on screen.
after the capsule's contents change.
Why it matters
The readout is the top bar's only surface for host pressure, and the toggle is its only
control. On a host where
/api/systemis slow or never produces a frame — the reporter'sdiagnostics say
kiro-cli: unavailable— the readout is stuck in a state the user cannotsee or leave: the preference says "show the readings", nothing renders, and there is no
button to press to turn it back off. It survives a reload, because the preference is
persisted.
What changed (motivation → approach → change)
Symptom 1 — the toggle disappears
The metrics readout is an inline capsule segment, not a popover: one button both shows
the readings and toggles them away. Its open branch pushed a segment only when the query
had ERRORED:
sysMetricsis undefined for the whole of the first fetch and for the retry window ofa failing one, because react-query sets
isErroronly once its retries are spent. In thatthird state — open, no frame, no error — the branch pushed nothing, the button left the DOM,
and the click landed on the capsule's background.
Every open state now pushes a toggle. The pending one carries an em dash per metric rather
than a spinner, which is the distinction the sibling usage segment already draws in this
same capsule, for the same reason: a spinner asserts a fetch is about to land, and on a
host that never reports metrics that claim never comes true. It reuses the loaded branch's
own "no valid reading" glyph, so the two open states differ in opacity, not in shape. The
error segment is untouched.
A resolved-but-empty frame is deliberately not one of these states:
{}is truthy, so italready reaches the loaded branch and renders its dashes there. The gap was only ever the
undefined frame.
Symptom 2 — the stale backdrop
.topbar-glasscarriesbackdrop-filter, and the two readout groups inside it carrycontainer-type: inline-size, which implies layout containment. When a capsule segmentmounted or unmounted inside one, the strip it vacated had to be re-sampled through that
blur — and damage from a contained subtree did not always reach a backdrop sharing a layer
with the content beneath it, so the old blurred pixels stayed on screen.
The glass is now promoted to its own compositing layer. That is the same remedy #7931
applied to the sandbox-doc frames, and the reason the notification popover portals to
<body>to escape this containing block rather than living inside it. Two spellings, onpurpose: focus mode drives this element's
transformfrom an inline style that wins overthe rule, so
translateZ(0)is absent for exactly the case that animates hardest;backface-visibility:hiddenpromotes on its own and nothing writes it.The two halves meet
The first fix also removes the segment mount the second one is sensitive to. The capsule
used to gain a button and a divider when the frame landed; it now only re-renders text
inside a button that was already there.
No rung of the ladder #7851 repaired is touched. The capture script asserts the actions
group's content box — what the container queries measure — is identical across the absent,
pending and loaded scenes, and that nothing outside the capsule moves when the frame lands.
Tests
website/src/test/App.topbarMetricsPending.test.tsx(3)api.systemnever resolves, sosysMetricsstays undefined andisErrorstays false. Asserts the toggle exists, reportsaria-pressed="true"(distinguishing it from the closed state's identically-named button),and still closes the readout and persists
mc-topbar-metrics=0. Red before: Unable tofind an accessible element with the role "button" and name
/System metrics/.mid-test and asserts the same pressed control is there before (dashes, no digits) and after
(digits). Rules out a fix that swaps one disappearance for another. Red before.
that already worked. Green before and after.
website/src/test/topbarGlassLayer.test.ts(3) — the promotion is asserted from thestylesheet source, because jsdom does not load
index.cssand a computed-style assertionwould be vacuous (same reason as the existing
topbarBadgeOverhang.test.ts). Pins bothspellings, and pins that containment stayed on the groups the ladder measures. First two
red before.
An earlier draft of the first file asserted a resolved-but-empty frame also lost the toggle.
It passed on base —
{}is truthy — so the premise was wrong and it was replaced by thecontinuity test above rather than kept as a vacuous guard.
Manual verification
website/scripts/capture-topbar-metrics-pending.mjs— new, extending the shared top-barcapture entry with
?metrics=pendingand?metricsfix=offscenes. Run at two widths, oneeither side of the base
@container (max-width:530px)metrics rung. All assertions pass:Local gates:
tsc --noEmitclean ·eslint src/ --max-warnings 597clean (0 on changedfiles) ·
npm run i18n:checkexit 0 (no new keys — the pending state composes fromapp.cpu/app.mem/app.dsk/app.system_metrics/app.click_to_hide) · full vitest28242 passed / 1 failed, the failure being
ArtifactBody.iframeBlob.test.tsx, whichpasses in isolation on this branch and on main and sits in a path this diff cannot reach.
Still outstanding: a macOS reproduction of the artifact. See the scope note below.
Screenshots / video
The metrics segment across the three states, at 1900px where the readings themselves render
(cropped to the right of the header):
Before — open readout, no frame: no segment at all. The waveform toggle is simply gone.
After — open readout, no frame: dimmed
CPU — MEM — DSK —, clickable.Frame landed — same button, real readings. No segment mounted; the text changed.
Full-width headers, and the icon-collapsed rung at 1280px
The 1280px pair is the form the reporter's 243px crop was in: below the 530px group rung the
readings are hidden and the segment is the bare accent glyph — which is where a missing toggle
is least recoverable, because there is no text left to click either.
Full-width 1900px renders: before ·
after ·
loaded
The
12.2万/1万counter and the neighbouring pill render missing-glyph boxes in these shots:the capture entry hardcodes zh-CN strings and this Linux capture host has no CJK font. Not a
product defect and not touched by this diff.
What these shots cannot show: the background artifact. A promoted compositing layer and
an unpromoted one render the same correct pixels — the defect is a stale raster the
compositor fails to invalidate, so no screenshot of a correct render distinguishes the two.
What the shots and the capture assertions do establish is that the promotion changes nothing
about the header's geometry or appearance.
Related Issues
Closes #7967
Pattern harvest
Rule candidate: review-prompt
Pattern: a control that renders only in the states its data has, so the state with no
data has no control — and the surface it toggles cannot be dismissed.
This is the falsy-collapse family the sibling usage segment in this same capsule was already
hardened against (
kiroUsageStatedistinguishesfailed,api-key, warming and loaded, andits comments say why). The generalizable rule is narrower than "handle loading states": when a
control is the ONLY affordance for leaving a persisted open state, its render must be
unconditional on the data, because the failure is not a missing readout but an unreachable
toggle. Worth a review prompt asking, of any conditionally-rendered toggle, what closes this
if the condition is false?
The compositing half is a second instance of #7931's class (
backdrop-filter/container-typecontainment stranding a backdrop) rather than a new pattern, and #7931already filed the frames it did not cover as #8037.
Checklist