fix(ui,desktop): a failure among rows stands as a row, Retry at its end

Merged
DA
5d ago
+53-35

Follows #3345 for the plan Audit error state UIs and improve them (https://tryluke.dev/stage/plan/02fe53cc-bb22-4861-abf1-995ec3225a3d). The developer said the sidebar's failure still didn't feel aligned.

Summary

In #3345 a failure among rows put the glyph and sentence on one line and wrapped Retry to a second line in narrow places like the sidebar. Retry's own glyph then started a second glyph column, one step in from the alert glyph, so the line read as a staircase.

ErrorState (inline) is now laid out the way a row is:

  • Glyph: the red alert glyph stands where a row's glyph stands.
  • Sentence: stands where a row's words stand, its first line centred in a control's height. A sentence too long for the room wraps in its own column.
  • Retry: stands at the row's end, where a row's own buttons stand. Where the row is 360px or narrower, Retry shows its glyph alone. A container query measures the row's own width. The word "Retry" stays in the DOM, visually hidden, so the button is still named Retry and keyboard and screen-reader use is unchanged.
  • Sidebar: the task list's failure (.sidebar-failure) takes the Tasks head's own end inset. In the sidebar, Retry now lines up with View options, the alert glyph with the nav glyphs, and the sentence with the nav labels. Measured in the harness: the Retry button spans x 217–247, the same as View options; the glyph starts at x 20 and the words at x 43, the same as New task's glyph and label.

Evidence

Rendered from the real renderer components and stylesheet in Chromium, with the task list failing. The font is Linux's.

BeforeAfter
sidebar beforesidebar after
sidebar narrowed to 210px, the sentence wrapping
Home, wide: Retry with its word at the row's end
  • pnpm check:affected: exit 0. Lint passes. Tests pass: ui 53 files / 293 tests, desktop 192 / 1765, web 51 / 197.

Invariants

  • ./scripts/check.sh green. (CI)
  • JSON Schema goldens unchanged.
  • Every behavior change has, in this PR, the test that fails without it. This PR changes layout only; what a reader meets is unchanged and is held by the existing error-state.test.ts and read-failure.test.ts: one alert, its sentence, one button named Retry, and a press that retries. jsdom draws no layout, so no test is added.
  • No new as type assertion.
  • No Effect.run* outside the edges.
  • No new raw async primitives.
  • No discarded Effect.
  • Test count unchanged.
  • No hand-rolled file replaced.
  • No AGENTS.md/CLAUDE.md sentence names a changed part.
  • PRIVACY.md unchanged.
  • Package deps unchanged.
  • Barrel/door rule unaffected.

Notes

  • Blockers or follow-up verification: none. CI is Linux only, so how this looks on the Mac has not been checked by eye.

Made with Luke


Devin Review