New Tab: steady chats list and composer chips

Merged
TE
23d ago
+561-45

Summary

Leo's New Tab bugs from cosmic-gray: the Chats list and the composer moved and changed after they first painted.

  • One clock for relative times. New Tab and All Chats read ages from a shared minute tick (clock.ts). Before, each page computed Date.now only when its snapshot changed, so two New Tabs showed the same chat as "1m" and "now".
  • One row per lane. A Claude Code transcript row is dropped when an acpmux session reports the same agentSessionId. The acpmux row keeps its name.
  • Failed, not Disconnected. A ready session whose last turn failed reads "Failed". "Disconnected" stays for a lost agent.
  • Still time column. The list's age is a fixed-width, right-aligned column of tabular figures, so the harness icon doesn't move. Rows sit on the title's and composer's edges.
  • Composer chips paint final. A new chat draws its harness's last effort and access chips (chipMemory.ts) instead of grey skeleton pills. The session's values then replace them in place. The model chip names the model the chat will start on.

Testing

Red on base (211ec6af3), green on the fix:

  • test/new-tab-steady-chats.test.tsx: same age across two New Tabs within one minute; a list fixture with both sources dedupes to one named row; Failed vs Disconnected; the age column and edge geometry.
  • test/new-tab-composer-first-frame.test.tsx: no skeleton, and the first frame's chips equal the settled chips.

Also green: 25 related New Tab and composer test files (87 tests), test/ui-rules.test.ts and bun run check.

Not done: the frame-sampled New Tab click capture on a native build. That follows separately.

Changelog

Fixed: New Tab's Chats list and composer no longer shift, duplicate lanes, or show placeholder pills while loading.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Stabilizes the New Tab's Chats list and composer so they no longer shift, duplicate lanes, or show placeholder content after first paint.

  • Relative ages now come from a shared minute clock (clock.ts), so every page shows the same age for the same chat and updates it in the same frame.
  • A Claude Code transcript row is dropped when an acpmux session reports the same agentSessionId; the acpmux row keeps its name.
  • A ready session whose last turn failed reads "Failed"; "Disconnected" stays for a lost agent.
  • The age is a fixed-width, right-aligned column of tabular figures, so the harness icon no longer moves; rows share the title's and composer's edges.
  • A new chat's composer paints its harness's last effort and access chips (via chipMemory.ts) instead of grey skeleton pills, then replaces them with the session's values. The model chip names the model the chat will start on.