feat(cli): TUI complete overhaul — components, sidebar, search, branding #157

Merged
jason.woltje merged 26 commits from feat/p7-tui-improvements into main 2026-03-15 22:17:20 +00:00
Owner

Complete TUI rewrite with componentized architecture:

  • Components: TopBar (branded mosaic icon), BottomBar (git/token/model info), InputBar, MessageList, Sidebar, SearchBar
  • Hooks: useSocket (typed Socket.IO), useConversations, useSearch, useViewport, useAppMode, useGitInfo
  • Features: conversation sidebar (Ctrl+L), message search with highlighting (Ctrl+K), scrollable history (PgUp/PgDn), keybinding system, thinking level cycling (Ctrl+T)
  • Gateway: token usage, model info, and thinking level events wired through chat gateway
  • Types: typed Socket.IO event maps in @mosaic/types/chat/events
  • Review fixes: wired initialModel/initialProvider through useSocket, added error handling on floating promises, added provider/modelId to ChatMessagePayload type
Complete TUI rewrite with componentized architecture: - **Components**: TopBar (branded mosaic icon), BottomBar (git/token/model info), InputBar, MessageList, Sidebar, SearchBar - **Hooks**: useSocket (typed Socket.IO), useConversations, useSearch, useViewport, useAppMode, useGitInfo - **Features**: conversation sidebar (Ctrl+L), message search with highlighting (Ctrl+K), scrollable history (PgUp/PgDn), keybinding system, thinking level cycling (Ctrl+T) - **Gateway**: token usage, model info, and thinking level events wired through chat gateway - **Types**: typed Socket.IO event maps in @mosaic/types/chat/events - **Review fixes**: wired initialModel/initialProvider through useSocket, added error handling on floating promises, added provider/modelId to ChatMessagePayload type
jason.woltje added 26 commits 2026-03-15 22:17:11 +00:00
- Split monolithic app.tsx into composable components:
  - TopBar: connection indicator (●/○), gateway URL, model name, conversation ID
  - BottomBar: cwd, git branch, token usage
  - MessageList: timestamped messages, tool call indicators, thinking display
  - InputBar: context-aware prompt with streaming/disconnect states
- Extract socket logic into useSocket hook with typed events
- Extract git/cwd info into useGitInfo hook
- Quiet disconnect: single indicator instead of error flood
- Add @mosaic/types dependency for typed Socket.IO events
- Add PRD and task tracking docs

Tasks: TUI-001 through TUI-007 (Wave 1)
- Remove borders from input bar — clean '❯ message mosaic…' prompt
- Two-line footer without borders:
  - Line 1: compact cwd (branch) | Gateway: Connected/Disconnected
  - Line 2: token stats (^in v_out R_cache W_cache $cost ctx%) | (provider) model
- Extend TokenUsage with cacheRead, cacheWrite, cost, contextPercent, contextWindow
- Add providerName to socket hook return
- Reorder layout: top bar → messages → input → footer
- Show '^0  v0  $0.000' instead of '—' when no token data yet
- Always show model slot ('awaiting model' as placeholder)
- Add minHeight={1} to prevent line collapse
- Show 'Mosaic Stack TUI' on left, gateway host on right
- Remove connection status from top bar (lives in footer)
- Remove model/conversation from top bar (lives in footer)
Gateway:
- Emit session:info on session creation with provider, model, thinking level
- Include SessionUsagePayload in agent:end with token stats, cost, context usage
- Handle set:thinking client event to cycle thinking levels
- Respond with updated session:info after thinking level change

Types (@mosaic/types):
- Add SessionUsagePayload (tokens, cost, context) to AgentEndPayload
- Add SessionInfoPayload (provider, model, thinking level, available levels)
- Add SetThinkingPayload and set:thinking to ClientToServerEvents
- Add session:info to ServerToClientEvents

CLI TUI:
- useSocket now tracks tokenUsage, modelName, providerName, thinkingLevel
- Updates from both session:info and agent:end usage payload
- Ctrl+T cycles thinking level via set:thinking socket event
- Footer shows thinking level next to model (e.g. 'claude-opus-4-6 • medium')
- Token stats populate with real ↑in ↓out Rcache Wcache $cost ctx%
- ASCII art mosaic windmill: 4 colored tiles (blue, purple, teal, amber)
  with pink center, matching the Mosaic Stack brand
- 3-line info block (Claude Code style):
  Line 1: 'Mosaic Stack v0.0.0'
  Line 2: model (context) · thinking · agent name
  Line 3: ● host connection status
- Remove bordered box in favor of open layout with icon
- Use half-block chars (▐█ █▌) for outer tiles to reduce width
- Center pink tile fits snugly between the four corners
- Matches the mosaic windmill proportions from the web UI
Brand colors (blue, purple, teal, amber, pink) plus complementary
fills (indigo, sky, rose, green) arranged in a gradient flow:
  blue    indigo  purple
  sky     pink    rose
  amber   green   teal
blue  ··  purple
··  pink  ··
amber ··  teal
Extract gap string to const to prevent prettier from collapsing
the double-space literals between icon tiles.
Add flexGrow={1} to the text column so it fills the full terminal
width. This ensures Ink clears the entire line when content changes
(e.g. 'awaiting model' → 'llama3.2 (8k)'), preventing leftover
characters from previous renders.
Adds a third line to the footer, right-aligned beneath Gateway status:
'session: a1b2c3d4' (first 8 chars) or 'no session' when not connected.
1. (blank) ··· Gateway: Connected
2. ~/path (branch) ··· Session: a1b2c3d4
3. ↑5k ↓72 $0.12 ··· (provider) model • thinking
- Create use-viewport hook tracking scroll offset, viewport size, auto-follow
- Update MessageList to slice visible messages and show scroll indicator
- Wire PgUp/PgDn keybindings in app.tsx
fix(cli): restore componentized app.tsx after rebase, accept initialModel/initialProvider props
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
4259829d65
fix(cli): wire initialModel/initialProvider through useSocket, add error handling
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
28860c9f42
- Pass initialModel/initialProvider from CLI flags into useSocket hook
- Include provider/modelId in socket message emit (restores PR #144 functionality)
- Add provider/modelId optional fields to ChatMessagePayload type
- Add .catch() to floating promises in createConversation/deleteConversation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
jason.woltje merged commit 82c10a7b33 into main 2026-03-15 22:17:20 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#157