# TUI Layout Redesign — Soft Rounded + Blue Accent

This document has two halves. The **top half** describes what the user sees and experiences — the visual language, the three screens, microfeedback. The **bottom half** is the build plan — phases, files, checklists, code patterns.

---

# DESIGN SPEC

---

## 1. Visual Language

Everything shares one consistent look: a **soft rounded** dark theme with a **blue accent**.

### Surfaces

| Element | Looks like | Feels like |
|---------|------------|------------|
| Background | Deep charcoal `#14161a` | The ground — recedes |
| Panels | Slightly lighter `#181b21`, rounded borders `╭──╮` | Cards floating on the ground |
| Top bar | Darkest `#0f1115` | Anchors the screen |
| Footer bar | Same as top bar | Anchors the bottom |
| Active panel | Blue-tinted border glow | The thing you're working in |

### Light — five levels of contrast

```
#eef1f5  — bright white   → headings, important values
#ced3da  — light gray     → body text, messages
#838b96  — muted gray     → labels, metadata, timestamps
#434a55  — faint          → borders, separators, placeholders
#262b33  — darkest gray   → subtle panel borders
```

### Accent — blue (#4a9eff)

Used sparingly so it means something:
- **Selected item** indicator (`▶`)
- **Active panel** border highlight
- **Interactive hints** (keybind labels)
- **Brand mark** (the `x` in `correx`)
- **Inference events** in the stream
- **Prompt cursor** (`▌`)

Secondary accent is a purple-blue (`#7aa2f7`) used for lifecycle events.

---

## 2. The Three Screens

Every screen shares the same three-row grid. The middle row is a 1.7:1 horizontal split whose content changes by state. The input bar is always inside the left panel's footer.

```
┌──────────────────────────────────────────────────────┐
│ status bar                                            │  ← always visible
├─────────────────────────────────┬─────────────────────┤
│ left panel (1.7)                │ right panel (1.0)   │  ← changes by state
│                                 │                     │
│ ────────────────────            │                     │
│ ❯ input bar (panel footer)     │                     │
├─────────────────────────────────┴─────────────────────┤
│ footer bar — keybind hints · mode                    │  ← always visible
└──────────────────────────────────────────────────────┘
```

### IDLE — choosing what to do

Left column shows the session list (+ workflow picker if no sessions). Right column is a welcome panel.

```
┌──────────────────────────────────────────────────────────────┐
│ ● connected  │  localhost:8080  │  qwen_coder_14b (local)  │
├──────────────────────────┬───────────────────────────────────┤
│ sessions                 │  Correx Agent Harness             │
│                          │                                   │
│ ▶ [01J9CA] ACTIVE   │  Connected · 3 sessions              │
│      cas-segment-store  │  ─────────────────────             │
│   [02KF8B] COMPLETED    │  Select a session from the left    │
│      approval-flow      │  or type a name to start a new    │
│   [03LD7C] FAILED       │  one.                              │
│      refactor-router    │                                   │
│                          │  recent:                           │
│ ──────────────────────  │  cas-segment-store  14:02  ▸       │
│ ▌ Session name…         │  approval-flow      13:45  ✔       │
│ tab filter  ctrl+n new  │  refactor-router    12:30  ✘       │
└──────────────────────────┴───────────────────────────────────┘
│  ⌨i  message  ⌨p  commands  ⌨a  approval  ⌨e  events  ⌨F2  skin  │  Soft · blue
└──────────────────────────────────────────────────────────────────────┘
```

### IN_SESSION — interacting with an agent

Left column is the conversation (messages from user, agent, kernel). Right column is the live event stream. Status bar expands to show session name, branch, model, context meter, state badge.

```
┌──────────────────────────────────────────────────────────────┐
│ ● connected  ·  cas-segment-store  ·  feat/cas-segments     │
│ qwen_coder_14b (local)     ctx ████████░░ 64%     ▸ active  │
├──────────────────────────┬───────────────────────────────────┤
│ router · conversation    │ event stream               ◌ live │
│                          │                                   │
│ you           14:02      │ 14:11:02 [Lifecycle] stage.enter │
│ Implement CAS storage…   │ 14:11:02 [Context]   pack.synth  │
│                          │ 14:11:03 [Inference] infer.start │
│ correx        14:02      │ 14:11:19 [Tool]      fs.write    │
│ Routed → planning…       │ 14:11:20 [Tool]      fs.write    │
│                          │ 14:11:24 [Domain]    artifact    │
│ agent          14:05     │ 14:11:31 [Tool]      shell.exec  │
│ Plan: each segment is…   │ 14:11:48 [Inference] infer.token │
│                          │ 14:12:02 [Approval]  approval   │
│ correx        14:11      │              raised  T3 network  │
│ Transition planning→impl │                                   │
│                          │                                   │
│ agent          14:12     │                                   │
│ SegmentStore.append()…   │                                   │
│                          │                                   │
│ ─────────────────────── │                                   │
│ ▌ Ask anything…          │                                   │
│ feat/cas-segments · chat │                                   │
└──────────────────────────┴───────────────────────────────────┘
│  ⌨i  message  ⌨p  commands  ⌨a  approval  ⌨e  events  ⌨Tab  panels  ⌨F2  skin  │ Soft · blue
└────────────────────────────────────────────────────────────────────────────────────┘
```

### APPROVAL — gate interrupted

The horizontal split collapses. The full main area shows the approval surface with tier badge, command preview, risk scan, steering input, and decision buttons.

```
┌──────────────────────────────────────────────────────────────┐
│ ● connected  ·  cas-segment-store  ·  feat/cas-segments     │
│ qwen_coder_14b (local)     ctx ████████░░ 64%     ⏸ awaiting│
├──────────────────────────────────────────────────────────────┤
│ ┌─ approval ────────────────────────────────────────────┐   │
│ │  T3  Approval required · external / network            │   │
│ │      Resolve a new dependency over the network         │   │
│ │                                           paused       │   │
│ │                                                       │   │
│ │  $ ./gradlew :segment-store:dependencies               │   │
│ │                                                       │   │
│ │  Adds com.github.luben:zstd-jni:1.5.6-3 for cold-     │   │
│ │  segment compression. Touches Maven Central.           │   │
│ │                                                       │   │
│ │  tool      shell  T3                                  │   │
│ │  reversible  no                                       │   │
│ │  policy     stages.implementation.approvals.tool=req   │   │
│ │                                                       │   │
│ │  SEMANTIC RISK SCAN                                   │   │
│ │  ▲ Network egress to repo1.maven.org                  │   │
│ │  ▲ Mutates gradle.lockfile                            │   │
│ │  ▲ New transitive: org.lz4? (none detected)           │   │
│ │                                                       │   │
│ │  ↳  ok, but lock the version_________________         │   │
│ │                                                       │   │
│ │  [approve a] [auto-approve A] [steer s] [reject r]    │   │
│ └───────────────────────────────────────────────────────┘   │
├──────────────────────────────────────────────────────────────┤
│  ⌨a  approve  ⌨A  auto-session  ⌨s  steer  ⌨r  reject  ⌨esc  later  │ Soft · blue
└──────────────────────────────────────────────────────────────────────────┘
```

### Footer bar — contextual keybinds

| Screen | Hints shown |
|--------|-------------|
| IDLE | `i` message · `p` commands · `a` approval · `e` events · `F2` skin |
| IN_SESSION | above + `Tab` panels · `↑↓` history · `l` back |
| APPROVAL | `a` approve · `A` auto · `s` steer · `r` reject · `esc` later |

Right-aligned mode indicator always reads `Soft · blue`.

---

## 3. Animation & Microfeedback

- **Spinner** — `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏` cycles at ~110ms. Seen next to "live" in event stream header, next to pending events, next to "paused" during approval.
- **Streaming cursor** — block `█` blinks at end of in-progress agent message. 50% opacity blink.
- **Toasts** — brief floating confirmations: "Approved", "Rejected", "Model switched". Appear near bottom center, auto-dismiss ~2.6s.
- **Panel transitions** — active panel border highlight shifts when switching focus with `Tab`.

---

# BUILD PLAN

---

## Phase 1 — Theming System

### File: NEW `components/Theme.kt`

Centralize all color and style constants into a single `object Theme` so every widget references the same palette. This eliminates the current pattern of scattered `Style.create().blue()` / `.cyan()` / `.gray()` calls.

```kotlin
object Theme {
    // Surfaces
    val bg: Color          // xterm 233 (#14161a)
    val bgDeep: Color      // xterm 232 (#0f1115)
    val panel: Color       // xterm 234 (#181b21)
    val panel2: Color      // xterm 235 (#1f232b)

    // Text
    val fg: Color          // xterm 252 (#ced3da)
    val fgStrong: Color    // xterm 255 (#eef1f5)
    val dim: Color         // xterm 243 (#838b96)
    val faint: Color       // xterm 238 (#434a55)

    // Accent
    val accent: Color      // #4a9eff (blue)
    val accent2: Color     // #7aa2f7 (purple-blue)

    // Semantic
    val ok: Color          // green
    val warn: Color        // amber
    val bad: Color         // red

    // Border / selection
    val border: Color      // xterm 236 (#262b33)
    val sel: Color         // xterm 235+ (#21262e)

    // Pre-built Style objects
    val dimStyle: Style
    val accentStyle: Style
    val okStyle: Style
    val warnStyle: Style
    val badStyle: Style
    val fgStrongStyle: Style

    // Event category → color
    fun categoryColor(category: String): Color
}
```

### Implementation Checklist

- [ ] Create `Theme.kt` in `components/`
- [ ] Define all Color constants (xterm or rgb depending on Mordant 2.6 API)
- [ ] Define pre-built Style objects for common patterns
- [ ] Define categoryColor() mapping (Lifecycle→accent2, Context→purple, Inference→accent, Tool→ok, Domain→amber, Approval→warn)
- [ ] Replace all hardcoded `.blue()`, `.cyan()`, `.gray()`, `.green()`, `.yellow()`, `.red()` calls across all existing components with Theme.* references

---

## Phase 2 — Layout: Three-Row Grid + Horizontal Split

### File modified: `TuiApp.kt`

**Current layout** (vertical stack for all display states):

```
StatusBar (height=1)
SessionList (height=min(count,7)+2)
[WorkflowList (height=6 if visible)]
[EventHistoryStrip (height=6 if visible)]
RouterPanel (fill)
InputBar (height=4)
```

**Target layout** — 3-row grid for all states:

```
grid rows: [status bar (length=1)] [main area (fill)] [footer bar (length=1)]

main area: horizontal split [left panel (ratio 1.7)] [right panel (ratio 1.0)]
```

Which widgets fill the panels depends on display state:

| State | Left panel (1.7) | Right panel (1.0) |
|-------|------------------|-------------------|
| IDLE | SessionList + WorkflowPicker | WelcomePanel |
| IN_SESSION | RouterPanel | EventStreamPanel |
| APPROVAL | ApprovalSurface (full width, no split) | — |

The input bar moves from being a top-level row **into the left panel's footer** (matching the reference's `Panel.footer` slot). This means each left-panel component is responsible for rendering its own footer containing the input bar.

### Layout code pattern

```kotlin
private fun renderInSessionLayout(frame: Frame, state: TuiState) {
    val area = frame.area()
    val vertRects = Layout.vertical()
        .constraints(
            Constraint.length(STATUS_HEIGHT),
            Constraint.fill(),                     // main area
            Constraint.length(FOOTER_HEIGHT),
        )
        .split(area)

    val mainRects = Layout.horizontal()
        .constraints(Constraint.ratio(1.7f), Constraint.ratio(1.0f))
        .split(vertRects[1])

    frame.renderWidget(statusBarWidget(state), vertRects[0])
    frame.renderWidget(routerPanelWidget(state), mainRects[0])  // InputBar inside
    frame.renderWidget(eventStreamPanelWidget(state), mainRects[1])
    frame.renderWidget(footerBarWidget(state), vertRects[2])
}
```

### InputBar as panel footer

Each left-panel widget (RouterPanel, SessionListPanel) renders its own vertical stack:
- Top: [messages / session rows] (fill)
- Bottom: [InputBar] (fixed height)

This can be done by splitting the panel's allocated rect into two sub-rects, or combining both into a single Paragraph with a separator.

### Implementation Checklist

- [ ] Import `Layout.horizontal()` in `TuiApp.kt`
- [ ] IN_SESSION: 3-row grid with horizontal split (RouterPanel 1.7 + EventStreamPanel 1.0)
- [ ] IDLE: same grid but left = SessionList, right = WelcomePanel
- [ ] APPROVAL: full-width single column in main area
- [ ] Move InputBar from top-level widget to left panel footer
- [ ] Add footer bar rendering as grid row 3

---

## Phase 3 — Status Bar Polish

### File modified: `components/StatusBar.kt`

Upgrade from a simple connection+model line to the full reference `crx-top`:

```
● connected  ·  cas-segment-store  ·  feat/cas-segments  ·  qwen_coder_14b (local)     ctx ████████░░ 64%     ▸ active
```

Segments (separated by `·`):
1. **Connection** — `●` (green) / `○` (red) + connected/disconnected
2. **Session name** — bold (shown in IN_SESSION / APPROVAL)
3. **Branch / workflow ID** — dim
4. **Model** — accent color + provider label
5. **Context meter** — bar with accent fill on faint track + percentage
6. **State badge** — pill: `▸ active` (green) or `⏸ awaiting approval` (amber)
7. **Background badge** — `+N updated` (dim yellow, shown when backgroundUpdateCount > 0)

### Context meter

Requires `contextUsed` and `contextBudget` in TuiState. Render as:

```
ctx [████████░░] 64%
```

Filled portion in accent color, empty portion in faint color.

### Implementation Checklist

- [ ] Add `contextUsed: Int?` and `contextBudget: Int?` to `TuiState`
- [ ] Replace current status bar spans with Theme references
- [ ] Add brand text with accented `x` (`corre` + `x`)
- [ ] Add session name + branch with `·` separators
- [ ] Add model + provider display
- [ ] Implement context meter bar (accent-fill on faint track)
- [ ] Implement state badge pill (warn for awaiting, ok for active)
- [ ] Keep background update badge, style with Theme.dim/yellow

---

## Phase 4 — Event Stream Panel

### File: NEW `components/EventStreamPanel.kt`

The right column in IN_SESSION state. Structured event list replacing the current raw-text `EventHistoryStrip`.

### Event row format

```
14:11:02  [Lifecycle]  stage.enter        implementation (role=coder)
14:11:19  [Tool]       fs.write           SegmentStore.kt (+212)
14:12:02  [Approval]   approval.raised    T3 · gradle network resolve  ⠋[a]
```

Each row:
1. **Timestamp** — dim, fixed-width
2. **Category badge** — colored pill (see categoryColor mapping)
3. **Type** — bold/bright
4. **Detail** — normal text, truncated if long
5. **Pending indicator** — spinner + `[a]` keybind hint for pending approvals

Panel header shows:
```
event stream               ◌ live
```
Or if session has pending approval:
```
event stream               ⠋ paused
```

### Implementation Checklist

- [ ] Create `EventStreamPanel.kt` in `components/`
- [ ] Implement event row rendering with all 5 columns
- [ ] Wire categoryColor() into badge rendering
- [ ] Add pending event indicator (spinner + `[a]` hint)
- [ ] Add "live" / "paused" indicator in panel header
- [ ] Wire into TuiApp.kt as right column in IN_SESSION layout

---

## Phase 5 — Footer Keybind Bar

### File: NEW `components/FooterBar.kt`

Dedicated bottom row showing contextual keybind hints and current mode indicator. Currently these hints are embedded in InputBar row2 — they move here instead.

### Contextual hints

| State | Hints |
|-------|-------|
| IDLE | `i` message · `p` commands · `a` approval · `e` events · `F2` skin |
| IN_SESSION | above + `Tab` panels · `↑↓` history · `l` back |
| APPROVAL | `a` approve · `A` auto · `s` steer · `r` reject · `esc` later |

### Keybind chip pattern

```
 i  message   p  commands   a  approval
```
Key in white-on-blue (accent background, reversed), label in dim text.

### Mode indicator

Right-aligned: `Soft · blue`

### Implementation Checklist

- [ ] Create `FooterBar.kt` in `components/`
- [ ] Implement contextual hint logic based on display state / input mode / overlay
- [ ] Add mode indicator (right-aligned)
- [ ] Wire into TuiApp.kt as `Constraint.length(1)` in grid row 3
- [ ] Remove all key hint spans from InputBar row2 (keep only session name, chat mode)

---

## Phase 6 — Welcome Panel (IDLE Right Column)

### File: NEW `components/WelcomePanel.kt`

The right column in IDLE state. Shows contextual info when no session is active:

- Brand header (`Correx Agent Harness`) with accented `x`
- Brief contextual prompt ("select a session or type a name")
- Recent activity list (completed/active sessions with timestamps + status glyphs)
- Connection status hint if disconnected

### Implementation Checklist

- [ ] Create `WelcomePanel.kt` in `components/`
- [ ] Show brand header with accented `x`
- [ ] Show contextual prompt text
- [ ] Show recent sessions (if available) or empty-state message
- [ ] Use Theme colors throughout

---

## Phase 7 — Font Size 14-15

Terminal font size is emulator-controlled, not app-controlled. What we can do:

1. Review all MAX constants for readability at 14pt:
   - `NAME_MAX = 15` → may need adjustment
   - `DETAIL_MAX = 60` → review
   - `DIFF_LINE_MAX_LENGTH = 100` → may need decrease
   - `MAX_VISIBLE = 7` → may need decrease (fewer rows fit)

2. Add `Theme.fontSize: Int = 14` as documentation / scaling factor for layout constants.

3. If Mordant `Style.Builder` exposes size (needs verification), apply it.

### Implementation Checklist

- [ ] Add `Theme.fontSize` constant
- [ ] Review and adjust truncation/visibility constants
- [ ] Verify row estimates fit within target terminal sizes

---

## Phase 8 — Approval Surface Polish

### File modified: `components/ApprovalSurface.kt`

Already the most polished component, needs theme integration:

- Replace `Color.YELLOW` border with `Theme.warn`
- Replace hardcoded `.green()`, `.red()`, `.yellow()`, `.gray()` with Theme.*
- Tier badge → `Theme.pill()` pattern
- Risk scan section → `▲` prefix in amber (matching reference `.risk` class)
- Key hint bar → `Theme.dimStyle` with framed key labels

### Implementation Checklist

- [ ] Replace all hardcoded color calls with Theme references
- [ ] Replace `Color.YELLOW` border with `Theme.warn`
- [ ] Align risk section formatting with reference

---

## Phase 9 — Session List & Workflow List Polish

### Files: `SessionList.kt`, `WorkflowListPanel.kt`

Color substitution — these already use ROUNDED borders:

- `▶` prefix → `Theme.accent` (was `.blue()`)
- Names → `Theme.fgStrong` (was `.cyan()`)
- Status labels → `Theme.ok`, `Theme.warn`, `Theme.bad`
- Overflow / empty text → `Theme.dim`
- Borders → `Theme.border`

### Implementation Checklist

- [ ] Replace all hardcoded color calls with Theme references in SessionList.kt
- [ ] Replace all hardcoded color calls with Theme references in WorkflowListPanel.kt

---

# APPENDICES

---

## A. Color Reference

### Palette

| Token | Hex | ANSI | Use |
|-------|-----|------|-----|
| `bg` | `#14161a` | xterm 233 | Screen background |
| `bgDeep` | `#0f1115` | xterm 232 | Top/footer bar |
| `panel` | `#181b21` | xterm 234 | Panel surface |
| `panel2` | `#1f232b` | xterm 235 | Secondary surface |
| `fg` | `#ced3da` | xterm 252 | Body text |
| `fgStrong` | `#eef1f5` | xterm 255 | Headings, emphasis |
| `dim` | `#838b96` | xterm 243 | Muted text |
| `faint` | `#434a55` | xterm 238 | Borders, placeholders |
| `border` | `#262b33` | xterm 236 | Panel borders |
| `sel` | `#21262e` | xterm 235+ | Selection highlight |
| `accent` | `#4a9eff` | rgb(74,158,255) | Blue accent |
| `accent2` | `#7aa2f7` | rgb(122,162,247) | Purple-blue secondary |
| `ok` | `#7fd88f` | rgb(127,216,143) | Success |
| `warn` | `#e8c06a` | rgb(232,192,106) | Warning |
| `bad` | `#e87f7f` | rgb(232,127,127) | Error |

### Event category → color

| Category | Color | Hex |
|----------|-------|-----|
| Lifecycle | accent2 | `#7aa2f7` |
| Context | purple | `#c98fd9` |
| Inference | accent | `#4a9eff` |
| Tool | ok | `#7fd88f` |
| Domain | amber | `#e8b765` |
| Approval | warn | `#e8c06a` |

---

## B. Keybind Reference

| Key | Context | Action |
|-----|---------|--------|
| `↑↓` | Session list | Navigate sessions |
| `↑↓` | Input mode (IN_SESSION) | History navigation |
| `Enter` | Session list | Enter selected session |
| `Enter` | Input bar | Submit message / start session |
| `Tab` | Any | Toggle filter mode / focus panel |
| `i` | No overlay | Focus input bar |
| `p`, `/`, `:` | No overlay | Open command palette |
| `a` | No overlay | Open pending approval |
| `e` | No overlay | Open event inspector overlay |
| `c` | No overlay | Open context pack inspector |
| `g` | No overlay | Open stage graph |
| `m` | No overlay | Open model registry |
| `t` | No overlay | Open tool palette |
| `a` | Approval | Approve |
| `A` | Approval | Auto-approve session |
| `s` | Approval | Steer + approve |
| `r` | Approval | Reject |
| `ctrl+a` | Approval | Approve (alt) |
| `ctrl+r` | Approval | Reject (alt) |
| `ctrl+e` | Any | Toggle event strip |
| `ctrl+n` | IDLE | Start new session |
| `ctrl+l` | IN_SESSION | Return to session list |
| `ctrl+q` | Any | Quit |
| `ctrl+x` | Approval | Toggle diff expand |
| `F2` | Any | Cycle chrome direction |
| `F3` | Any | Cycle accent color |
| `Escape` | Overlay | Close overlay |
| `Escape` | Input focus | Blur input |

---

## C. State Changes

### TuiState additions

```kotlin
data class TuiState(
    // ... existing fields ...
    val contextUsed: Int? = null,
    val contextBudget: Int? = null,
    val eventStreamExpanded: Boolean = false,
)
```

`contextUsed` / `contextBudget` feed the context meter. Populated from `ServerMessage` snapshot data during snapshot phase.

---

## D. Implementation Order

| Phase | Files | Dependencies | Effort |
|-------|-------|-------------|--------|
| **1. Theme** | `Theme.kt` (new) | None | Medium |
| **2. Layout** | `TuiApp.kt`, left panel components | Phase 1 | Large |
| **3. Status bar** | `StatusBar.kt`, `TuiState.kt` | Phase 1 | Medium |
| **4. Event stream** | `EventStreamPanel.kt` (new) | Phase 1, Phase 2 | Medium |
| **5. Footer bar** | `FooterBar.kt` (new), `InputBar.kt` | Phase 1, Phase 2 | Small |
| **6. Welcome panel** | `WelcomePanel.kt` (new) | Phase 1, Phase 2 | Small |
| **7. Font** | `Theme.kt` (amend), constants review | Phase 1 | Small |
| **8. Approval** | `ApprovalSurface.kt` | Phase 1 | Small |
| **9. List polish** | `SessionList.kt`, `WorkflowListPanel.kt` | Phase 1 | Small |

---

## E. Constraints & Risks

1. **Mordant Color API**: `Color.rgb(r, g, b)` depends on Mordant 2.6.0. If unavailable, fall back to `Color.xterm(n)` for closest ANSI color. Verify during Phase 1.
2. **Horizontal split**: `Layout.horizontal()` is part of Tambouille 0.3.0 (currently used in build.gradle). If unavailable, simulate with two-column Paragraph or verify API existence before Phase 2.
3. **Context meter**: Requires upstream data (context used/budget) that may not be in current server protocol. May need to stub until data arrives.
4. **Interactive event clicks**: Tambouille supports widget click callbacks. Event row selection can be added if interactivity is needed.
