fix(desktop): show placeholder rows while the Inbox is first read
Merged
DA
2d ago+82+82+01234567890123456789-6-6-0123456789
Summary
- Implements the task plan Fix Inbox loading state. While the Inbox read its items, it showed only a small spinner in the top-left corner of the list.
- The list now shows skeleton rows in the shape of an item row: a circle for the agent's tile, a line for the title, and a short block for the time. They sit inside
@luke/ui'sSkeletonRegion, which tells screen readers "Reading your inbox…". When the items arrive, they take the rows' place. The sidebar's task list already loads the same way. InboxListSkeletonlives intasks/inbox-list.tsxand is built fromListRowandSkeleton, so it needs no new CSS.desktop-inbox.tsx'sListNotenow uses it instead of theSpinner.- The pane next to the list stays empty while items load, as before. Home's, a task's, and a person's Inbox spinners are unchanged and outside this PR's scope.
Evidence
- Platform-independent checks:
pnpm check:affected: typecheck and lint passed. Two failures came from the sandbox, not the change:discarded-effectran out of heap, andapp-menu.test.tscouldn't find Electron's binary. I reran both on their own after raising the heap and fetching Electron, and they passed, as didpnpm knip. The other 211 desktop test files passed. - New test in
desktop-inbox.test.ts: while the plans' reads are held, the list shows a busy status, "Reading your inbox…", with skeleton rows, no pressable rows, and no spinner. When the reads land, the items are listed. It fails onmain. - Screenshots: I rendered the Inbox's DOM from the test against the built
styles.cssin dark mode (in the comment below).
Invariants
-
./scripts/check.shgreen. (CI) - JSON Schema goldens unchanged (
LUKE_UPDATE_FIXTURESnot 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
astype assertion outside the allowlisted files. (CI via anti-slop) - No
Effect.runPromise/runSync/runForkoutside the runtime edges and the shimstools/anti-slop/effect-edges.jsonrecords. (CI viaanti-slop/no-run-promise-outside-edges) - No new
setTimeout/setInterval/new Promise/AbortController/fs.watchoutside the filestools/anti-slop/effect-edges.jsonrecords. (CI viaanti-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 test in
desktop-inbox.test.ts) - 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.mdsentence naming a changed part is edited; root pair stays byte-identical. (CI for pair existence; manual for wording) -
PRIVACY.mdunchanged, or the change is a product decision called out in the PR body. - Package
package.jsondeps match what the sources import by bare specifier;effectviacatalog:. (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 renderernode:grep plus a new grep for those specifiers in renderer bundles)
Notes
- Blockers or follow-up verification: None. CI runs only on Linux, so the change has not been checked in the Mac app itself.
Made with Luke
Summary by cubic
Replaces the small spinner in the Inbox list's corner with placeholder skeleton rows while the Inbox first reads its items.
- Placeholder rows match an item row's shape: a circle for the agent's tile, a line for the title, and a short block for the time, so items land where the blocks stood.
- The rows sit in
@luke/ui'sSkeletonRegion, which announces "Reading your inbox…" to screen readers. - The new
InboxListSkeletonis built from existingListRowandSkeletoncomponents, so no new CSS is needed. - Adds a test in
desktop-inbox.test.tsthat fails onmain.