@bniladridas/cursor 0.1.17 → 0.1.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DESIGN.md ADDED
@@ -0,0 +1,775 @@
1
+ # Cursor TUI Design Specification
2
+
3
+ ## Design Philosophy
4
+
5
+ Cursor is an AI coding agent — the terminal is a **communication tool**, not a code editor. Every pixel of chrome serves one goal: reduce friction between thought and action.
6
+
7
+ **Principles:**
8
+ - **Input-first**: the cursor starts focused in the input bar. Every other UI element is secondary.
9
+ - **Progressive disclosure**: show the last exchange by default. Reveal history, metadata, and controls only when requested.
10
+ - **Zero-config startup**: the first launch asks one question (mode), then drops you into a conversation.
11
+ - **Keyboard-native**: every action has a keybinding. No required mouse interaction.
12
+ - **Responsive by default**: single layout that adapts across 80–200+ columns without breakpoints.
13
+
14
+ ---
15
+
16
+ ## 1. Global Layout Architecture
17
+
18
+ ```
19
+ ┌─ StatusLine ───────────────────────────────────────────── r1
20
+ │ ◉ Mode Model Provider ~/project │
21
+ ├──────────────────────────────────────────────────────────┤
22
+ │ │ r2..N-3
23
+ │ Messages (scrollable, flex-grow) │
24
+ │ │
25
+ │ │
26
+ ├──────────────────────────────────────────────────────────┤
27
+ │ build ⌘P palette :cmd LSP:3 MCP:2 1.2k tokens │ rN-2
28
+ ├─ InputBar ───────────────────────────────────────────────┤ rN-1
29
+ │ > _ │
30
+ └──────────────────────────────────────────────────────────┘ rN
31
+ ```
32
+
33
+ ### Region Definitions
34
+
35
+ | Region | Lines | Role | z-order |
36
+ |--------|-------|------|---------|
37
+ | StatusLine | 1 | Persistent context: mode, model, provider, project | 1 |
38
+ | Messages | 2..N-3 | Primary reading area: scrollable message history | 0 |
39
+ | ContextLine | N-2 | Active agent, token count, keybinding hints | 1 |
40
+ | InputBar | N-1 | Text input, file attachments, mode indicators | 2 |
41
+ | (overlay) | full | Modal dialogs: menus, help, sessions | 3 |
42
+
43
+ ### Z-Order Rule
44
+
45
+ Overlays (z:3) always render **after** the base layout paint. On dismiss, the terminal is restored by re-entering the scroll region and re-drawing the affected lines — no full repaint needed.
46
+
47
+ ### Scroll Region
48
+
49
+ ```term
50
+ \033[2;N-3r # scroll region: lines 2 through N-3
51
+ ```
52
+
53
+ Lines 1 (StatusLine), N-2 (ContextLine), and N-1 (InputBar) are outside the scroll region and never move.
54
+
55
+ ### Terminal Resize
56
+
57
+ On `SIGWINCH`:
58
+ 1. Read new `ws_row` / `ws_col` via `ioctl(TIOCGWINSZ)`
59
+ 2. Recompute scroll region boundaries
60
+ 3. Clear screen with `\033[2J`
61
+ 4. Repaint StatusLine, ContextLine, InputBar
62
+ 5. Repaint visible messages trimmed to new height
63
+
64
+ The message buffer is never reflowed — only the viewport window changes. Messages wider than `ws_col` are soft-wrapped at display time only.
65
+
66
+ ---
67
+
68
+ ## 2. Screen: Chat (Primary)
69
+
70
+ This is where the user spends >95% of their time.
71
+
72
+ ### 2.1 Layout
73
+
74
+ ```
75
+ ┌─ StatusLine ─────────────────────────────────────────────┐
76
+ │ ◉ Offline qwen2.5-coder:1.5b ~/cursor │
77
+ ├──────────────────────────────────────────────────────────┤
78
+ │ │
79
+ │ ┌ You ─── 2:31 PM ──────────────────────────────────┐ │
80
+ │ │ What does this function do? │ │
81
+ │ └────────────────────────────────────────────────────┘ │
82
+ │ │
83
+ │ ┌ Cursor ───────────────────────────────────────────┐ │
84
+ │ │ The function parses a JSON config file at the │ │
85
+ │ │ given path. It reads the file, validates the │ │
86
+ │ │ structure, and returns a Config object. │ │
87
+ │ │ │ │
88
+ │ │ │ path/to/config.json │ │
89
+ │ │ │ FileNotFound → return default config │ │
90
+ │ │ │ Invalid JSON → return default config │ │
91
+ │ │ │ │
92
+ │ │ Would you like me to add logging? │ │
93
+ │ └────────────────────────────────────────────────────┘ │
94
+ │ │
95
+ │ ┌ You ─── 2:32 PM ──────────────────────────────────┐ │
96
+ │ │ Yes, add logging too │ │
97
+ │ └────────────────────────────────────────────────────┘ │
98
+ │ │
99
+ │ ┌ Cursor ───────────────────────────────────────────┐ │
100
+ │ │ [·] Processing... │ │
101
+ │ └────────────────────────────────────────────────────┘ │
102
+ │ │
103
+ ├──────────────────────────────────────────────────────────┤
104
+ │ build ⌘P palette :cmd !shell 842 tokens │
105
+ ├─ InputBar ───────────────────────────────────────────────┤
106
+ │ > _ │
107
+ └──────────────────────────────────────────────────────────┘
108
+ ```
109
+
110
+ ### 2.2 Component Hierarchy
111
+
112
+ ```
113
+ App
114
+ ├── StatusLine
115
+ │ ├── ModeIndicator (◉ Online / ⚡ Offline)
116
+ │ ├── ModelName (claude-sonnet-4 / qwen2.5-coder:1.5b)
117
+ │ ├── ProviderName (Anthropic / Ollama) [online only]
118
+ │ └── ProjectDir (~/src/cursor)
119
+ ├── MessageList (scrollbox, flex-grow)
120
+ │ ├── Message (user)
121
+ │ │ ├── Avatar ("You")
122
+ │ │ ├── Timestamp (2:31 PM) [configurable]
123
+ │ │ └── Body (markdown text)
124
+ │ ├── Message (assistant)
125
+ │ │ ├── Avatar ("Cursor")
126
+ │ │ ├── Timestamp
127
+ │ │ ├── Body
128
+ │ │ ├── CodeBlock (syntax-highlighted, foldable) [0..N]
129
+ │ │ ├── FileAttachment [0..N]
130
+ │ │ └── ToolCall [0..N]
131
+ │ └── LoadingIndicator (spinner + "Processing...")
132
+ ├── ContextLine
133
+ │ ├── AgentName (build / plan)
134
+ │ ├── KeybindingHints (⌘P palette, :cmd, !shell)
135
+ │ ├── LspStatus (LSP:3) [if LSP active]
136
+ │ ├── McpStatus (MCP:2) [if MCP active]
137
+ │ └── TokenCount (842 tokens)
138
+ └── InputBar
139
+ ├── ModePrefix (> for normal, ! for shell, / for cmd)
140
+ ├── TextInput (line-editable, with cursor)
141
+ ├── Placeholder ("Ask anything...")
142
+ ├── FilePill [0..N] (attached file refs)
143
+ └── CharCount (optional, >80% width warning)
144
+ ```
145
+
146
+ ### 2.3 Responsive Behavior
147
+
148
+ | Region | 80 col | 120 col | 160+ col |
149
+ |--------|--------|---------|----------|
150
+ | StatusLine | Mode + Model only | + Project | + Provider |
151
+ | Messages | No timestamps | Timestamps shown | + File tree sidebar (right panel) |
152
+ | Code blocks | No line nums | Line nums | + Fold indicators |
153
+ | ContextLine | palette hint only | + tokens | + LSP/MCP |
154
+ | InputBar | Single line | Single line | Multi-line (3 visible) |
155
+
156
+ **Sidebar (160+):** A right panel appears showing a file tree of the current project. Selecting a file with Tab focuses it; Enter opens in $EDITOR. The sidebar width is fixed at 36 columns.
157
+
158
+ ### 2.4 Keyboard Shortcuts
159
+
160
+ | Key | Context | Action |
161
+ |-----|---------|--------|
162
+ | `Enter` | InputBar | Send message |
163
+ | `Shift+Enter` | InputBar | Insert newline |
164
+ | `Ctrl+Backspace` | InputBar | Delete word backward |
165
+ | `Ctrl+U` | InputBar | Clear line |
166
+ | `Ctrl+L` | InputBar | Clear screen (keep history in buffer) |
167
+ | `Esc` | InputBar | Blur input / cancel |
168
+ | `Tab` | InputBar | Insert 2 spaces / trigger completion |
169
+ | `Ctrl+P` | global | Command palette |
170
+ | `?` / `Ctrl+H` | global | Help overlay |
171
+ | `j` / `↓` | (scroll) | Scroll message list down 1 |
172
+ | `k` / `↑` | (scroll) | Scroll message list up 1 |
173
+ | `Ctrl+D` | (scroll) | Scroll half page down |
174
+ | `Ctrl+U` | (scroll) | Scroll half page up |
175
+ | `g` | (scroll) | Go to top of conversation |
176
+ | `G` | (scroll) | Go to bottom (latest message) |
177
+ | `/` | global | Search messages |
178
+ | `n` / `N` | (search) | Next / previous match |
179
+ | `Ctrl+Z` | global | Undo last response |
180
+ | `Ctrl+S` | global | Save session |
181
+ | `Ctrl+R` | global | Resume session picker |
182
+ | `Ctrl+E` | global | Toggle sidebar (160+ only) |
183
+ | `Ctrl+T` | global | Change theme |
184
+ | `Ctrl+Q` / `Ctrl+C` | global | Quit (with confirm if unsaved) |
185
+
186
+ ### 2.5 State Transitions
187
+
188
+ ```
189
+ [empty] ──type──→ [typing] ──Enter──→ [sending]
190
+ ↑ │
191
+ │ [streaming]
192
+ │ │
193
+ │ ┌─────────────┤
194
+ │ │ │
195
+ │ [complete] [error]
196
+ │ │ │
197
+ └────────── Ctrl+Z ────────┘ │
198
+
199
+ [retry prompt]
200
+
201
+ [sending] → ...
202
+ ```
203
+
204
+ **States detailed:**
205
+
206
+ | State | Appearance | Behavior |
207
+ |-------|-----------|----------|
208
+ | `empty` | Welcome message with starter prompts | Input bar shows placeholder |
209
+ | `typing` | Cursor blinking in input bar | Text accumulates, no send yet |
210
+ | `sending` | "Sending..." with spinner | Input bar disabled, message bubble shows "Sending..." |
211
+ | `streaming` | Partial response visible, animated cursor | Text appends character-by-character |
212
+ | `complete` | Full response rendered | Enable scroll, keyboard nav |
213
+ | `error` | Red banner: "Connection failed. [r] Retry [d] Dismiss" | `r` resends, `d` hides banner |
214
+ | `confirm-quit` | Overlay: "Exit? Unsaved session [s] Save [e] Exit without save [c] Cancel" | Choice-driven transition |
215
+
216
+ ### 2.6 Example Terminal Rendering
217
+
218
+ **80-column terminal:**
219
+
220
+ ```
221
+ ┌─ Cursor ──────────────────────────────────────┐
222
+ │ ⚡ Offline qwen2.5-coder:1.5b │
223
+ ├───────────────────────────────────────────────┤
224
+ │ │
225
+ │ You │
226
+ │ Add error handling to parse_config │
227
+ │ │
228
+ │ ┌ Cursor ─────────────────────────────────┐ │
229
+ │ │ I'll add try/except. Here's the │ │
230
+ │ │ updated function: │ │
231
+ │ │ def parse(path): │ │
232
+ │ │ try: │ │
233
+ │ │ return json.load(open(path)) │ │
234
+ │ │ except: return {} │ │
235
+ │ │ Want me to add logging too? │ │
236
+ │ └─────────────────────────────────────────┘ │
237
+ │ │
238
+ ├───────────────────────────────────────────────┤
239
+ │ build ⌘P :cmd 842 tok │
240
+ ├───────────────────────────────────────────────┤
241
+ │ > _ │
242
+ └───────────────────────────────────────────────┘
243
+ ```
244
+
245
+ **120-column terminal:**
246
+
247
+ ```
248
+ ┌─ StatusLine ─────────────────────────────────────────────────────────────┐
249
+ │ ◉ Online claude-sonnet-4 Anthropic ~/src/cursor │
250
+ ├──────────────────────────────────────────────────────────────────────────┤
251
+ │ │
252
+ │ You · 2:31 PM │
253
+ │ What does this function do? │
254
+ │ │
255
+ │ ┌ Cursor · 4.2s · 328 tok ─────────────────────────────────────────┐ │
256
+ │ │ The function parses a JSON config file at the given path. It │ │
257
+ │ │ reads the file, validates the structure, and returns a Config │ │
258
+ │ │ object. │ │
259
+ │ │ │ │
260
+ │ │ 1 │ def parse_config(path): │ │
261
+ │ │ 2 │ try: │ │
262
+ │ │ 3 │ with open(path) as f: │ │
263
+ │ │ 4 │ return json.load(f) │ │
264
+ │ │ 5 │ except FileNotFoundError: │ │
265
+ │ │ 6 │ return {} │ │
266
+ │ │ 7 │ except json.JSONDecodeError: │ │
267
+ │ │ 8 │ return {} │ │
268
+ │ │ │ │
269
+ │ │ Want me to add logging too? │ │
270
+ │ └───────────────────────────────────────────────────────────────────┘ │
271
+ │ │
272
+ ├──────────────────────────────────────────────────────────────────────────┤
273
+ │ build ⌘P palette :cmd !shell LSP:3 MCP:2 842 tokens │
274
+ ├─ InputBar ───────────────────────────────────────────────────────────────┤
275
+ │ > _ │
276
+ └──────────────────────────────────────────────────────────────────────────┘
277
+ ```
278
+
279
+ ---
280
+
281
+ ## 3. Screen: Startup / Mode Selection
282
+
283
+ Shown once per session on first launch. Never shown again unless `/config` is invoked.
284
+
285
+ ### 3.1 Layout
286
+
287
+ ```
288
+ ┌─ StatusLine ─────────────────────────────────────────────┐
289
+ │ ◉ Cursor v1.0 │
290
+ ├──────────────────────────────────────────────────────────┤
291
+ │ │
292
+ │ Welcome to Cursor │
293
+ │ Your AI coding agent in the terminal │
294
+ │ │
295
+ │ │
296
+ │ ┌────────────────────────────────────────────────────┐ │
297
+ │ │ ◉ Online │ │
298
+ │ │ ○ Offline │ │
299
+ │ │ │ │
300
+ │ │ ↑/↓ navigate · Enter select · q quit │ │
301
+ │ └────────────────────────────────────────────────────┘ │
302
+ │ │
303
+ │ (this area reserved for model picker when │
304
+ │ Online or Offline is selected) │
305
+ │ │
306
+ ├──────────────────────────────────────────────────────────┤
307
+ │ ? Help Ctrl+P palette │
308
+ └──────────────────────────────────────────────────────────┘
309
+ ```
310
+
311
+ ### 3.2 Component Hierarchy
312
+
313
+ ```
314
+ StartupScreen
315
+ ├── WelcomeTitle ("Cursor")
316
+ ├── WelcomeSubtitle ("Your AI coding agent in the terminal")
317
+ ├── SelectionPanel
318
+ │ ├── RadioGroup (Online / Offline)
319
+ │ │ ├── RadioOption (◉ Online)
320
+ │ │ └── RadioOption (○ Offline)
321
+ │ └── KeybindingHint (↑/↓ navigate · Enter select)
322
+ ├── ProviderPanel [visible after Online selected]
323
+ │ └── RadioGroup (Together AI / Cerebras / Fireworks / ...)
324
+ ├── ModelPanel [visible after Offline selected or provider chosen]
325
+ │ └── RadioGroup (dynamic from Ollama / provider defaults)
326
+ └── Footer
327
+ ├── HelpHint (?)
328
+ └── PaletteHint (Ctrl+P)
329
+ ```
330
+
331
+ ### 3.3 State Transitions
332
+
333
+ ```
334
+ start ──→ [mode selection]
335
+
336
+ Online │ Offline
337
+ ▼ ▼
338
+ [provider picker] [model picker (Ollama)]
339
+ │ │
340
+ ▼ ▼
341
+ [model picker] [done]
342
+
343
+
344
+ [done] ──→ transition to Chat screen
345
+ (clear screen, set scroll region)
346
+ ```
347
+
348
+ The "done" transition prints a confirmation line, clears the screen, and enters the Chat screen.
349
+
350
+ ### 3.4 Keyboard Shortcuts
351
+
352
+ | Key | Action |
353
+ |-----|--------|
354
+ | `↑` / `k` | Previous option |
355
+ | `↓` / `j` | Next option |
356
+ | `Enter` | Confirm selection |
357
+ | `Esc` | Back to previous step |
358
+ | `q` | Quit |
359
+
360
+ ### 3.5 Example Rendering
361
+
362
+ ```
363
+ ┌─ StatusLine ─────────────────────────────────────────────┐
364
+ │ ◉ Cursor v1.0 │
365
+ ├──────────────────────────────────────────────────────────┤
366
+ │ │
367
+ │ Welcome to Cursor │
368
+ │ Your AI coding agent in the terminal │
369
+ │ │
370
+ │ │
371
+ │ Mode │
372
+ │ ◉ Offline │
373
+ │ ○ Online │
374
+ │ │
375
+ │ Model │
376
+ │ ○ llama3.2:3b │
377
+ │ ◉ qwen2.5-coder:1.5b │
378
+ │ ○ llama3.2:latest │
379
+ │ ○ llama3.1:latest │
380
+ │ │
381
+ │ ↑/↓ · Enter select Esc back │
382
+ │ │
383
+ ├──────────────────────────────────────────────────────────┤
384
+ │ ? Help ⌘P palette │
385
+ └──────────────────────────────────────────────────────────┘
386
+ ```
387
+
388
+ ---
389
+
390
+ ## 4. Screen: Help Overlay
391
+
392
+ Opened with `?` or `Ctrl+H`. Modal overlay on top of current screen.
393
+
394
+ ### 4.1 Layout
395
+
396
+ ```
397
+ ┌──────────────────────────────────────────────────────────┐
398
+ │ Help ▼ General │
399
+ │ │
400
+ │ General │
401
+ │ ┌────────────────────────────────────────────────────┐ │
402
+ │ │ ? / Ctrl+H Show this help │ │
403
+ │ │ Ctrl+P Command palette │ │
404
+ │ │ Ctrl+Q Quit │ │
405
+ │ │ Ctrl+T Switch theme │ │
406
+ │ └────────────────────────────────────────────────────┘ │
407
+ │ │
408
+ │ Conversation │
409
+ │ ┌────────────────────────────────────────────────────┐ │
410
+ │ │ Enter Send message │ │
411
+ │ │ Shift+Enter New line │ │
412
+ │ │ Ctrl+Z Undo last response │ │
413
+ │ │ Ctrl+L Clear │ │
414
+ │ └────────────────────────────────────────────────────┘ │
415
+ │ │
416
+ │ Navigation │
417
+ │ ┌────────────────────────────────────────────────────┐ │
418
+ │ │ j/↓ / k/↑ Scroll down/up │ │
419
+ │ │ Ctrl+D / Ctrl+U Half page down/up │ │
420
+ │ │ g / G Top / bottom │ │
421
+ │ │ / Search │ │
422
+ │ └────────────────────────────────────────────────────┘ │
423
+ │ │
424
+ │ Sessions │
425
+ │ ┌────────────────────────────────────────────────────┐ │
426
+ │ │ Ctrl+S Save session │ │
427
+ │ │ Ctrl+R Resume session │ │
428
+ │ └────────────────────────────────────────────────────┘ │
429
+ │ │
430
+ │ [1-4] Change tab Esc close │
431
+ └──────────────────────────────────────────────────────────┘
432
+ ```
433
+
434
+ ### 4.2 Behavior
435
+
436
+ - Tab-based: `1` General, `2` Conversation, `3` Navigation, `4` Sessions
437
+ - `Esc` / `?` / `Ctrl+H` dismisses and returns focus to InputBar
438
+ - The overlay is rendered on top of the existing screen without clearing it
439
+ - When dismissed, the screen is redrawn by re-entering the scroll region and repainting
440
+
441
+ ---
442
+
443
+ ## 5. Screen: Session Manager
444
+
445
+ Opened with `Ctrl+S` (save) or `Ctrl+R` (resume).
446
+
447
+ ### 5.1 Layout
448
+
449
+ ```
450
+ ┌─ StatusLine ─────────────────────────────────────────────┐
451
+ │ ◉ Sessions │
452
+ ├──────────────────────────────────────────────────────────┤
453
+ │ │
454
+ │ Sessions │
455
+ │ ┌────────────────────────────────────────────────────┐ │
456
+ │ │ ○ fix-auth-bug 2h ago 12 messages │ │
457
+ │ │ ○ refactor-api yesterday 8 messages │ │
458
+ │ │ ○ add-tests Mar 15 24 messages │ │
459
+ │ │ ○ db-migration Mar 12 6 messages │ │
460
+ │ │ ○ ci-setup Mar 10 15 messages │ │
461
+ │ └────────────────────────────────────────────────────┘ │
462
+ │ │
463
+ │ j/k navigate · Enter resume · d delete · Esc back │
464
+ │ │
465
+ ├──────────────────────────────────────────────────────────┤
466
+ │ │
467
+ │ (InputBar hidden during session manager) │
468
+ │ │
469
+ └──────────────────────────────────────────────────────────┘
470
+ ```
471
+
472
+ ### 5.2 Behavior
473
+
474
+ - Navigating here from `Ctrl+S` auto-focuses a "Save as:" input
475
+ - Navigating here from `Ctrl+R` shows the session list with keyboard navigation
476
+ - `d` deletes the selected session (with confirm)
477
+ - `Enter` resumes the selected session
478
+ - `Esc` returns to Chat screen
479
+
480
+ ---
481
+
482
+ ## 6. Component Library
483
+
484
+ ### 6.1 Message
485
+
486
+ ```
487
+ ┌─ Avatar ─── Timestamp ──────────────────────────────────┐
488
+ │ │
489
+ │ Body text, supporting markdown: │
490
+ │ - **bold**, *italic*, `inline code` │
491
+ │ - Links: [text](url) │
492
+ │ - Lists: ordered and unordered │
493
+ │ │
494
+ │ CodeBlock (if present, see below) │
495
+ │ FileAttachment (if present, see below) │
496
+ │ │
497
+ └──────────────────────────────────────────────────────────┘
498
+ ```
499
+
500
+ **States:**
501
+ - `pending`: dimmed, "Sending..." indicator
502
+ - `streaming`: trailing cursor animation, partial body
503
+ - `complete`: full rendering, all interactions enabled
504
+ - `error`: red left border, error banner at top
505
+
506
+ **Rendering rules:**
507
+ - User messages: no left border (or thin gray)
508
+ - Assistant messages: colored left border (blue for default agent, green for plan agent)
509
+ - Borders are 1 column wide, rendered with box-drawing `│`
510
+
511
+ ### 6.2 CodeBlock
512
+
513
+ ```
514
+ ┌─ language ─── copy ─── 8 lines ─── [−] ─────────────────┐
515
+ │ │
516
+ │ 1 │ def parse_config(path): │
517
+ │ 2 │ try: │
518
+ │ 3 │ with open(path) as f: │
519
+ │ 4 │ return json.load(f) │
520
+ │ 5 │ except FileNotFoundError: │
521
+ │ 6 │ return {} │
522
+ │ 7 │ except json.JSONDecodeError: │
523
+ │ 8 │ return {} │
524
+ │ │
525
+ └──────────────────────────────────────────────────────────┘
526
+ ```
527
+
528
+ **Sub-states:**
529
+ - `collapsed`: shows 3-line preview with `+ 5 lines` expand hint
530
+ - `expanded`: shows full code
531
+ - `copying`: brief "Copied!" flash indicator
532
+
533
+ **Foldable sections:**
534
+ - Long output (>20 lines): collapsed by default
535
+ - Toggle with `[−]` / `[+]` indicator
536
+ - Center line shows `··· 12 lines hidden ···`
537
+
538
+ ### 6.3 Selection Menu (RadioGroup)
539
+
540
+ ```
541
+ ┌─ Mode ───────────────────────────────────────────────────┐
542
+ │ │
543
+ │ ◉ Offline │
544
+ │ ○ Online │
545
+ │ │
546
+ │ ↑/↓ · Enter select │
547
+ └──────────────────────────────────────────────────────────┘
548
+ ```
549
+
550
+ **Properties:**
551
+ - `◉` = selected, `○` = unselected
552
+ - Selected item is bold or highlighted with a distinct color
553
+ - First item selected by default
554
+ - Empty state: "No options available"
555
+ - Loading state: spinner icon for async options (e.g., Ollama model fetch)
556
+
557
+ ### 6.4 StatusLine
558
+
559
+ ```
560
+ ◉ Online claude-sonnet-4 Anthropic ~/src/cursor
561
+ ```
562
+
563
+ Three rendering modes:
564
+ - **Full** (≥120 col): Mode · Model · Provider · Project
565
+ - **Compact** (80–119 col): Mode · Model · Project
566
+ - **Minimal** (<80 col): Mode · Model
567
+
568
+ ### 6.5 InputBar
569
+
570
+ ```
571
+ > Add error handling to the parse_config function_
572
+ ↑ cursor position
573
+ ```
574
+
575
+ **States:**
576
+
577
+ | State | Appearance | Behavior |
578
+ |-------|-----------|----------|
579
+ | `empty` | Placeholder text: "Ask anything..." | Dimmed gray text |
580
+ | `typing` | Cursor blinking, text visible | Normal rendering |
581
+ | `file-attached` | Pill badge: `📄 src/config.py ×` | `×` dismisses attachment |
582
+ | `shell-mode` | Prefix changes to `! ` | Enter executes shell cmd |
583
+ | `command-mode` | Prefix changes to `/ ` | Enter routes to command handler |
584
+ | `disabled` | Dimmed, no cursor | During streaming/loading |
585
+
586
+ **Multi-line input:**
587
+ - `Shift+Enter` inserts newline
588
+ - Input area expands up to 5 lines, then scrolls within input
589
+ - Character count shown in ContextLine when >80% of limit
590
+
591
+ ### 6.6 Loading / Spinner
592
+
593
+ ```
594
+ [·] Processing...
595
+ [o] Processing...
596
+ [O] Processing...
597
+ [o] Processing...
598
+ ```
599
+
600
+ A 4-frame spinner rendered inline. Frames cycle every 150ms. Threaded with `std::atomic<bool>` and a background thread. On completion, the spinner line is replaced with the response text.
601
+
602
+ ### 6.7 Error Banner
603
+
604
+ ```
605
+ ┌─ ⚠ Connection failed ──────────────────────────────────┐
606
+ │ Could not reach api.anthropic.com. Check your network │
607
+ │ and API key. │
608
+ │ │
609
+ │ [r] Retry [d] Dismiss │
610
+ └─────────────────────────────────────────────────────────┘
611
+ ```
612
+
613
+ Rendered as an inline message in the MessageList. Red left border.
614
+
615
+ ---
616
+
617
+ ## 7. Navigation Model
618
+
619
+ ### 7.1 Focus Zones
620
+
621
+ ```
622
+ StatusLine ← always visible, never focusable
623
+ MessageList ← scrollable, focusable (scroll mode)
624
+ ContextLine ← always visible, never focusable
625
+ InputBar ← always focusable (default)
626
+ Overlay ← captures all input when active
627
+ ```
628
+
629
+ The **default focus** is InputBar. When the user presses `j`/`k`, focus shifts to MessageList for scrolling. Pressing `Enter`, `i`, or `Esc` returns focus to InputBar.
630
+
631
+ ### 7.2 Modal vs Modeless
632
+
633
+ | Mode | Trigger | Indicator | Behavior |
634
+ |------|---------|-----------|----------|
635
+ | Input (default) | — | `> ` prefix | Keys type into input |
636
+ | Scroll | `j`/`k`/`g`/`G` | StatusLine shows scroll position | Arrow keys scroll, `i` or `Enter` returns to input |
637
+ | Command | `/ ` prefix at input start | `/ ` prefix | Commands like `/help`, `/save` |
638
+ | Shell | `! ` prefix at input start | `! ` prefix | Enter executes shell command |
639
+ | Menu | From any command | Overlay | Arrow keys navigate, Enter selects |
640
+
641
+ ### 7.3 Command Palette (Ctrl+P)
642
+
643
+ Opens a fuzzy-finder overlay listing all available commands:
644
+
645
+ ```
646
+ > read file
647
+ ──────────────────────────────────────────────────────────
648
+ read:path Read a file
649
+ read:path:start:count Read file range
650
+ search:query Search codebase
651
+ /help Show help
652
+ /save name [tags] Save session
653
+ ```
654
+
655
+ - Type to filter with fuzzy matching
656
+ - `↑`/`↓` to navigate
657
+ - Enter to execute
658
+ - Esc to dismiss
659
+
660
+ ---
661
+
662
+ ## 8. Theme System
663
+
664
+ ### 8.1 Color Tokens
665
+
666
+ ```
667
+ --surface: terminal background (detected)
668
+ --surface-alt: dimmer variant for panels
669
+ --text: terminal foreground
670
+ --text-dim: \033[2m
671
+ --accent: #00aaff (blue, for assistant messages)
672
+ --accent-alt: #00cc66 (green, for plan agent)
673
+ --warning: #ffaa00
674
+ --error: #ff4444
675
+ --success: #00cc66
676
+ --border: dim variant of text
677
+ --selection: inverted text/background
678
+ ```
679
+
680
+ ### 8.2 Auto Theme Detection
681
+
682
+ Read `\033]10;?\007` (foreground) and `\033]11;?\007` (background) terminal queries. If background luminance > 0.5, use dark theme; otherwise light.
683
+
684
+ `Ctrl+T` cycles: auto → dark → light.
685
+
686
+ ### 8.3 Minimum Viable Palette
687
+
688
+ The first version uses **4 ANSI colors only** (plus DIM) to guarantee compatibility across all terminals:
689
+
690
+ | Role | Color |
691
+ |------|-------|
692
+ | Header text | BOLD |
693
+ | Dim text | DIM (default foreground) |
694
+ | Accent | CYAN |
695
+ | Error | RED |
696
+ | Success | GREEN |
697
+ | Warning | YELLOW |
698
+
699
+ This avoids any reliance on 24-bit color or specific terminal capabilities.
700
+
701
+ ---
702
+
703
+ ## 9. State Machine (Full Session)
704
+
705
+ ```
706
+ ┌─────────────────────────┐
707
+ │ LAUNCH │
708
+ │ (check update, etc.) │
709
+ └──────────┬──────────────┘
710
+
711
+
712
+ ┌─────────────────────────┐
713
+ │ STARTUP │
714
+ │ Mode / Model selection │
715
+ └──────────┬──────────────┘
716
+
717
+
718
+ ┌─────────────────────────┐
719
+ ┌─────│ CHAT │◄────┐
720
+ │ │ Conversation loop │ │
721
+ │ └──┬──────────┬───────────┘ │
722
+ │ │ │ │
723
+ │ Ctrl+S Ctrl+R Esc (quit)
724
+ │ │ │ │
725
+ ▼ ▼ ▼ ▼
726
+ ┌──────────┐ ┌────────┐ ┌──────────┐ ┌──────────────┐
727
+ │ HELP │ │ SAVE │ │ RESUME │ │ CONFIRM │
728
+ │ Overlay │ │ Dialog│ │ Picker │ │ Quit? │
729
+ └──────────┘ └────────┘ └──────────┘ └──────┬───────┘
730
+ │ │ │ │
731
+ └─────────────┴──────────┴─────────────────┘
732
+
733
+ s / e / c
734
+
735
+ ┌────────────┴────────────┐
736
+ ▼ ▼
737
+ ┌──────────┐ ┌──────────────┐
738
+ │ Save + │ e │ │ EXIT │
739
+ │ Exit │◄────┘ │ (goodbye) │
740
+ └──────────┘ └──────────────┘
741
+ ```
742
+
743
+ ---
744
+
745
+ ## 10. Implementation Constraints
746
+
747
+ | Constraint | Decision |
748
+ |-----------|----------|
749
+ | No external dependencies | Raw ANSI escape codes + ioctl |
750
+ | Cross-platform | `#ifdef _WIN32` for terminal API |
751
+ | TTY detection | `isatty()` gating for all ANSI output |
752
+ | Terminal resize | `SIGWINCH` handler + repaint |
753
+ | Scroll region | `\033[top;bottomr` for fixed chrome |
754
+ | Input in raw mode | `tcsetattr(ICANON|ECHO)` toggle per key read |
755
+ | Rendering model | Line-based paint, no full-screen buffer |
756
+ | Streaming response | Background thread + atomic done flag + periodic flush |
757
+
758
+ The design deliberately avoids a frame-buffer or cell-grid approach. Each component is stateless and paints by writing lines + cursor-movement sequences to stdout. This keeps the implementation minimal and avoids the complexity of maintaining a diff buffer.
759
+
760
+ ---
761
+
762
+ ## 11. Migration Path (Current → Target)
763
+
764
+ | Step | Change | Effort |
765
+ |------|--------|--------|
766
+ | 1 | Add StatusLine + ContextLine regions (outside scroll) | Small |
767
+ | 2 | Migrate startup menus to modal RadioGroup overlays | Medium |
768
+ | 3 | Add message component with border + avatar rendering | Small |
769
+ | 4 | Implement focus zones (input / scroll) | Medium |
770
+ | 5 | Add code block folding | Small |
771
+ | 6 | Add command palette (Ctrl+P) | Large |
772
+ | 7 | Add session save/resume dialog | Medium |
773
+ | 8 | Add theme detection + Ctrl+T cycling | Small |
774
+ | 9 | Add `SIGWINCH` handler | Small |
775
+ | 10 | Add /search overlay | Medium |