feat(desktop): open Home on New task's composer

Merged
DA
13d ago
+482-98

Summary

PR 8 of the "New task, prompt first" redesign: Home now opens on New task's composer, inline, the way Codex and ChatGPT open on a prompt box. This is question 5 of the approved design, done as the follow-up the developer approved. #3717 added NewTaskComposer.

  • Home (desktop-home.tsx): on main's DetailPage, the greeting is the page title. The composer sits at the top of the content, in .home-composer, in place of the "New task" header action. Inbox and Recent stay below it. The composer starts tasks in the workspace Home is showing.
    • It replaces the button rather than sitting beside it. The card already is New task on this page, so a button next to it would be a second way to do the same thing. ⌘N, the title bar, the sidebar, the command palette and a repository group's "+" still open the dialog. Only Home has the inline card.
    • The press that opened the dialog only picks a repository for the dialog. Home's card starts with no preset, so it never picks that repository up.
    • Signed out, there is no card.
  • Welcome page: an account with no task yet keeps main's centred welcome, with the card under Luke's face and line.
  • One composer for every state: while the task list loads, the page shows the card itself, since the card doesn't depend on the list. Home renders one composer, through a portal, into a box of its own (useComposerSlot). The loading page, the loaded page and the welcome each place that box where it belongs. So nothing typed while the list loads is lost when it lands, including when it lands as a brand-new account's welcome. The caret moves with the words.
  • Focus: the card doesn't set focusedByOwner, so it decides like agent-composer.tsx does. Its words take the caret when the editor mounts, unless a field or editor elsewhere already has it.
  • Keys: use-row-keys.ts already ignores any key not pressed from the window itself or from one of its rows, so ↑, ↓ and Return in the composer belong to the composer. Tests now pin this down. Escape in the card, when nothing in it needs closing, gives up focus. The arrows then reach Home's rows again.
  • Home's card and the dialog's stay in step on the mode. Both can be mounted at once (Home under ⌘N's dialog), so new-task-mode.ts is now one store that every card reads (useSyncExternalStore). Turning Plan on in the dialog turns it on in Home's card too.
  • Model sync dropped: an earlier round kept the cards' model chips in step through a write of the account default. main (#3718) now changes the default only in Settings, and #3717 followed. Each card starts on the default as read, and a pick applies to that card's agent alone. So the sync code and its tests are gone.
  • Draft not kept, by choice: the card's words, files and issue do not survive leaving Home. NewTaskComposer keeps them in its own state. Keeping them would need a composer-drafts.ts place and a controlled draft on the composer; the orchestrator is tracking that as a follow-up.

Screenshots

These come from the real DesktopHome with fake data in headless Chromium, dark theme, not from the packaged app (evidence.sh only runs on macOS).

Home, with tasks:

Home with the composer under the greeting

A new account's welcome:

The welcome with the composer under Luke's line

Evidence

  • pnpm check:affected runs out of memory in this sandbox, so its parts were run one at a time on the head commit:
    • pnpm oxlint and biome on the changed files: passed.
    • tsc --project apps/desktop/tsconfig.json --noEmit: passed.
    • pnpm knip: passed.
    • ./scripts/repository-checks.sh: passed.
    • NODE_OPTIONS=--max-old-space-size=6500 pnpm discarded-effect: passed.
  • pnpm exec vitest run over desktop-home.test.ts, desktop-shell.test.ts and desktop-tasks.test.ts: 78 tests passed. The new welcome-draft test fails without the shared composer box.

Tests

desktop-home.test.ts goes from 23 to 29 tests: one removed and seven added.

  • Removed: "↑ and ↓ pressed on New plan leave the focus on it". The button is gone, and the composer key test below covers the same ground.
  • Added:
    • the composer takes the caret, and ↑/↓/Return typed in it move no row (Return starts its task);
    • it leaves the caret in a field that already has it;
    • Agent mode start (listed workspace, planning: false, agent on the words with the chip's model and the card's idempotency key, no dialog asked for);
    • Plan mode start (Shift+Tab, planning: true, Untitled, the words handed to Luke as the brief, no agent);
    • words typed while the list loads survive it landing, both as the page and as a new account's welcome;
    • with the dialog open over Home, Plan turned on in the dialog is Home's mode once the dialog closes.
  • Changed: the skeleton, greeting and welcome tests now check for the composer instead of the button. The welcome test also starts a task from it. The arrow-key test presses Escape in the composer first. The row-press test no longer clicks the button.
  • desktop-shell.test.ts: the welcome test checks that Home holds the composer, and opens the dialog from the title bar's New task.

Invariants

  • ./scripts/check.sh green. (CI)
  • JSON Schema goldens unchanged. (CI)
  • Every behavior change has, in this PR, the test that fails without it; no change-detector test added.
  • Gateway envelope goldens unchanged. (CI)
  • No new as type assertion.
  • No Effect.runPromise/runSync/runFork outside the runtime edges (the test file's beforeAll lands the editor bundle as the dialog's test and 18 others do).
  • No new setTimeout/setInterval/new Promise/AbortController/fs.watch.
  • No Effect, Stream, or Layer dropped.
  • Test count delta listed above.
  • No hand-rolled file replaced.
  • No CLAUDE.md/AGENTS.md sentence names a changed part.
  • PRIVACY.md unchanged (the composer's box is ph-no-capture, as the dialog's is).
  • No dependency changes.
  • No barrel/door change.

Notes

  • Out of scope, as asked: the composer's own behavior (#3717), the one-call start (PR 7), the brief (PR 5).