Implement core App component and state management #8309

Closed
opened 2026-02-16 18:09:39 -05:00 by yindo · 1 comment
Owner

Originally created by @randomm on GitHub (Feb 2, 2026).

Originally assigned to: @thdxr on GitHub.

Parent Epic

Part of #11762 (Ink-based TUI Rewrite)

Objective

Create the main App component with centralized state management using useReducer.

Architecture

<App>
├── <Static items={completedMessages}>
├── <LiveSection>
├── <StatusBar>
└── <InputLine>

State Interface

interface AppState {
  messages: Message[]           // Completed messages
  streaming: {
    text: string                // Current streaming text
    tools: Map<string, Tool>    // Active tools
    tasks: Map<string, Task>    // Active tasks with child status
  }
  session: {
    id: string | null
    agent: string
    model: string | null
  }
  ui: {
    mode: 'input' | 'select' | 'navigation'
    selectOptions?: SelectOption[]
  }
}

Tasks

  • Create src/cli/ink/App.tsx with main component structure
  • Create src/cli/ink/state/types.ts with TypeScript interfaces
  • Create src/cli/ink/state/reducer.ts with action handlers:
    • SET_SESSION - Update current session
    • STREAM_TEXT - Append streaming text
    • TOOL_START - Add tool to active set
    • TOOL_END - Move tool to completed, remove from active
    • TASK_START - Add task to active set
    • TASK_UPDATE_CHILD - Update child tool status
    • TASK_END - Move task to completed
    • MESSAGE_COMPLETE - Finalize message, move to Static
    • SET_UI_MODE - Switch between input/select/navigation
  • Create src/cli/ink/state/context.tsx for state provider
  • Wire up basic render test

Acceptance Criteria

  • App component renders without errors
  • State updates via dispatch work correctly
  • TypeScript types are comprehensive
  • No runtime errors on state transitions

Estimated Effort

1.5 days

Dependencies

  • #11763 (Setup Ink dependencies)
Originally created by @randomm on GitHub (Feb 2, 2026). Originally assigned to: @thdxr on GitHub. ## Parent Epic Part of #11762 (Ink-based TUI Rewrite) ## Objective Create the main App component with centralized state management using useReducer. ## Architecture ``` <App> ├── <Static items={completedMessages}> ├── <LiveSection> ├── <StatusBar> └── <InputLine> ``` ## State Interface ```typescript interface AppState { messages: Message[] // Completed messages streaming: { text: string // Current streaming text tools: Map<string, Tool> // Active tools tasks: Map<string, Task> // Active tasks with child status } session: { id: string | null agent: string model: string | null } ui: { mode: 'input' | 'select' | 'navigation' selectOptions?: SelectOption[] } } ``` ## Tasks - [ ] Create `src/cli/ink/App.tsx` with main component structure - [ ] Create `src/cli/ink/state/types.ts` with TypeScript interfaces - [ ] Create `src/cli/ink/state/reducer.ts` with action handlers: - `SET_SESSION` - Update current session - `STREAM_TEXT` - Append streaming text - `TOOL_START` - Add tool to active set - `TOOL_END` - Move tool to completed, remove from active - `TASK_START` - Add task to active set - `TASK_UPDATE_CHILD` - Update child tool status - `TASK_END` - Move task to completed - `MESSAGE_COMPLETE` - Finalize message, move to Static - `SET_UI_MODE` - Switch between input/select/navigation - [ ] Create `src/cli/ink/state/context.tsx` for state provider - [ ] Wire up basic render test ## Acceptance Criteria - [ ] App component renders without errors - [ ] State updates via dispatch work correctly - [ ] TypeScript types are comprehensive - [ ] No runtime errors on state transitions ## Estimated Effort 1.5 days ## Dependencies - #11763 (Setup Ink dependencies)
yindo closed this issue 2026-02-16 18:09:39 -05:00
Author
Owner

@randomm commented on GitHub (Feb 2, 2026):

Research Findings

Ink App Patterns (from Context7 + production analysis)

  • Structure: Single <App /> component rendered with render(), orchestrates layout and state
  • State: useReducer for complex transitions, useState for simple state
  • Static: <Static items={array}> renders completed content above dynamic - only new items trigger re-renders
  • Testing: ink-testing-library with render() and lastFrame() assertions

Existing Lite TUI Analysis

  • Files: 6 files (~450 lines total)
  • SDK Events: MessageV2.Event.PartUpdated for streaming
  • Component Mapping:
    • LineEditorTextInput
    • Spinnerink-spinner
    • Raw ANSI writes → Ink render tree
  • State: Minimal - async generator yielding ChatChunk types

Implementation Notes

  • Lite TUI uses buffering approach (no pausedForProse hack found in current code)
  • Ink's React reconciler will handle concurrent updates properly
  • Keep UI layer thin - direct model-tool communication pattern
@randomm commented on GitHub (Feb 2, 2026): ## Research Findings ### Ink App Patterns (from Context7 + production analysis) - **Structure**: Single `<App />` component rendered with `render()`, orchestrates layout and state - **State**: `useReducer` for complex transitions, `useState` for simple state - **Static**: `<Static items={array}>` renders completed content above dynamic - only new items trigger re-renders - **Testing**: `ink-testing-library` with `render()` and `lastFrame()` assertions ### Existing Lite TUI Analysis - **Files**: 6 files (~450 lines total) - **SDK Events**: `MessageV2.Event.PartUpdated` for streaming - **Component Mapping**: - `LineEditor` → `TextInput` - `Spinner` → `ink-spinner` - Raw ANSI writes → Ink render tree - **State**: Minimal - async generator yielding `ChatChunk` types ### Implementation Notes - Lite TUI uses buffering approach (no `pausedForProse` hack found in current code) - Ink's React reconciler will handle concurrent updates properly - Keep UI layer thin - direct model-tool communication pattern
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#8309