fix(desktop): draw Home in its own shape while the task list loads

Merged
DA
14h ago
+196-11

Summary

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

  • Home no longer goes blank on launch. DesktopHome returned null until the task list was read. Now it draws HomeSkeleton, laid out like Home: a title-sized block for the greeting beside the real New task (the welcome offers it too), then heading-sized blocks over placeholder rows for the Inbox (3 rows) and Recent (6 rows, one for each row Recent lists). The page lands where the blocks stood. The header comment now describes this instead of "draws nothing".
  • The welcome state. The skeleton contains no word that depends on the list: no "Welcome back", no section names. A brand-new account still sees a skeleton for a moment before "Start your first task", but the skeleton never claimed the account had tasks, so nothing misleading flashes. HomeRecent's empty, unread null is covered because the whole page is the skeleton until the list is read.
  • Home's Inbox section drew a 16px Spinner at the section's left edge, outside the rows' inset. It now draws InboxListSkeleton. That component gains two optional props: rows (defaults to today's five) and context, which adds a block before each row's time where Home's rows name what the item is about.

Evidence

  • Platform-independent checks: pnpm check:affected passed. In the sandbox, Electron's binary was fetched first for app-menu.test.ts, and discarded-effect ran with the larger heap.
  • New tests in desktop/desktop-home.test.ts all fail on main:
    • while the task list is unread, Home draws the skeleton: New task, placeholder rows under the Inbox and Recent, no greeting
    • an unread list for an account with no tasks shows neither the welcome nor "Welcome back"
    • once the list lands, the real page replaces the skeleton
    • while what waits is read, Home's Inbox draws three placeholder rows under its name and no ring

Screenshots

While the task list loadsList read, Inbox items still loadingLoaded
Home skeletonHome Inbox skeletonHome loaded

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. (+4 in desktop-home.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. The screenshots below come from the jsdom markup under the built stylesheet in the dark scheme, not from the Mac app.

Made with Luke


Devin Review


Summary by cubic

Home no longer shows a blank window on launch. While the task list is unread, DesktopHome draws a skeleton shaped like the page: a block for the greeting beside the real New task button, then heading blocks over placeholder rows for the Inbox (3 rows) and Recent (6 rows).

  • The skeleton says no word that depends on the list (no "Welcome back", no section names), so a brand-new account's welcome replaces it without flashing anything misleading.
  • Home's Inbox section replaces its lone spinner with InboxListSkeleton, which gains optional rows and context props to match Home's row layout.
  • Four new tests in desktop-home.test.ts cover the skeleton, the unread new-account state, the swap to the real page once the list lands, and the Inbox section's rows.