fix(desktop): draw the Issues board and an issue's page in blocks while they read

Merged
DA
4d ago
+194-14

Summary

PR 7 of 9 in the loading-states rollout (Issues). Context: the Loading states audit and the plan Fix Inbox loading state, "Loading states rollout".

  • Issues board while the list loads. The board is the default layout, and it drew nothing under the toolbar until the list answered, on purpose: CollectionPage's loading rows are shaped like a list, not a board. It now draws IssueBoardSkeleton (issue-board.tsx), one column per status in the board's own .issue-board / .issue-column box. Each column has a head block where the glyph and name go, and a few cards, each with an identifier-and-time line over a title line. The whole thing sits in a SkeletonRegion that says "Reading issues". The list layout keeps CollectionPage's loading rows. I updated the listState comment to match.
  • One issue's page before it loads. If you open an issue from a link or a notification, or before the list holds it, the page used to show only its breadcrumbs until useIssue answered. DetailFrame now draws IssueDetailSkeleton (issue-detail.tsx) in the issue's own .desktop-document / .plan-body: a title block in the heading's type, a byline block, two chip-sized control blocks, and three description lines, said as "Reading the issue". The failure states are unchanged.
  • Drawn with @luke/ui's Skeleton, SKELETON_SHAPE, and SkeletonRegion. styles/issues.css uses only tokens and adds a few rules so the reading region fills the page's height, as the board does.

Evidence

  • Platform-independent checks: pnpm check:affected passed: typecheck; lint, knip, discarded-effect; and test (desktop 213 files, 1969 tests).
  • New and changed tests in desktop/issues-page.test.ts fail without the change:
    • the board, while it reads, draws a column of blocks for every status, said as reading, and no issue cards. This replaces the old assertion that it drew nothing.
    • an issue's page opened from a link before its read answers draws blocks in the issue's own document, said as reading, until the title lands.
  • Screenshots below are in the dark scheme, built from the renderer's stylesheet over the jsdom suite's markup.

Invariants

  • ./scripts/check.sh green. (CI)
  • JSON Schema goldens unchanged (LUKE_UPDATE_FIXTURES not run, or diff explained line by line). (CI)
  • Every behavior change has, in this PR, the test that fails without it; no change-detector test added. (manual; root AGENTS.md "Testing")
  • Gateway envelope goldens unchanged. (CI)
  • No new as type assertion outside the allowlisted files. (CI via anti-slop)
  • No Effect.runPromise/runSync/runFork outside the runtime edges and the shims tools/anti-slop/effect-edges.json records. (CI via anti-slop/no-run-promise-outside-edges)
  • No new setTimeout/setInterval/new Promise/AbortController/fs.watch outside the files tools/anti-slop/effect-edges.json records. (CI via anti-slop/no-raw-async-primitives)
  • No Effect, Stream, or Layer written as a statement and dropped; a description a caller walks past runs nothing. (CI via pnpm discarded-effect)
  • Test count equals the pre-PR count or the delta is listed. (+1: the issue page's reading test; the board's reading test was rewritten in place)
  • Each hand-rolled file the PR replaces is deleted, or the part that remains is a permanent adaptor named in root AGENTS.md's "The permanent adaptors" with the reason it stays.
  • Every CLAUDE.md/AGENTS.md sentence naming a changed part is edited; root pair stays byte-identical. (CI for pair existence; manual for wording)
  • PRIVACY.md unchanged, or the change is a product decision called out in the PR body.
  • Package package.json deps match what the sources import by bare specifier; effect via catalog:. (CI)
  • Barrel/door rule: no Node-reaching Effect module (@effect/platform-node, effect/sql) behind a barrel the renderer or a web function opens. (CI: existing renderer node: grep plus a new grep for those specifiers in renderer bundles)

Notes

  • Blockers or follow-up verification: None. Screenshots follow in a comment.

Made with Luke


Devin Review


Summary by cubic

Adds loading skeletons so the Issues board and an individual issue's page show placeholders shaped like their content while data is read, instead of drawing nothing.

  • The board (default layout) now draws one placeholder column per status with head and card blocks, labeled "Reading issues"; the list layout keeps its existing loading rows.
  • An issue's page opened from a link or notification now draws placeholder title, byline, chips, and description lines in the issue's own document, labeled "Reading the issue"; failure states are unchanged.
  • New and updated tests in desktop/issues-page.test.ts cover both reading states.