fix(desktop): show placeholder rows while the Inbox is first read

Merged
DA
2d ago
+82-6

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's SkeletonRegion, 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.
  • InboxListSkeleton lives in tasks/inbox-list.tsx and is built from ListRow and Skeleton, so it needs no new CSS. desktop-inbox.tsx's ListNote now uses it instead of the Spinner.
  • 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-effect ran out of heap, and app-menu.test.ts couldn't find Electron's binary. I reran both on their own after raising the heap and fetching Electron, and they passed, as did pnpm 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 on main.
  • Screenshots: I rendered the Inbox's DOM from the test against the built styles.css in dark mode (in the comment below).

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 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.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. CI runs only on Linux, so the change has not been checked in the Mac app itself.

Made with Luke


Devin Review


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's SkeletonRegion, which announces "Reading your inbox…" to screen readers.
  • The new InboxListSkeleton is built from existing ListRow and Skeleton components, so no new CSS is needed.
  • Adds a test in desktop-inbox.test.ts that fails on main.