@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/.github/workflows/release.yml +2 -0
- package/AGENTS.md +66 -36
- package/CMakeLists.txt +54 -10
- package/DESIGN.md +775 -0
- package/README.md +10 -5
- package/cli.js +26 -3
- package/include/agent.h +55 -6
- package/include/services/ai_service.h +3 -0
- package/include/services/git_service.h +4 -0
- package/include/utils/platform.h +2 -2
- package/include/utils/ui.h +19 -0
- package/install.js +1 -0
- package/install.sh +2 -1
- package/package.json +3 -2
- package/release/checksums.txt +4 -4
- package/release/cursor-linux/cursor_v0.1.19_linux_amd64.tar.gz +0 -0
- package/release/cursor-macos/cursor-0.1.19.arm64_sequoia.bottle.tar.gz +0 -0
- package/release/cursor-macos/cursor_v0.1.19_darwin_arm64.tar.gz +0 -0
- package/release/cursor-windows/cursor__windows_amd64.zip +0 -0
- package/src/agent.cpp +1129 -157
- package/src/main.cpp +4 -1
- package/src/services/ai_service.cpp +7 -23
- package/src/services/git_service.cpp +45 -1
- package/src/services/web_service.cpp +7 -3
- package/src/utils/ui.cpp +94 -8
- package/src/utils/version.cpp +48 -15
- package/tests/e2e/run_local_batch.sh +131 -0
- package/tests/main_test.cpp +163 -0
- package/release/cursor-linux/cursor_v0.1.17_linux_amd64.tar.gz +0 -0
- package/release/cursor-macos/cursor-0.1.17.arm64_sequoia.bottle.tar.gz +0 -0
- package/release/cursor-macos/cursor_v0.1.17_darwin_arm64.tar.gz +0 -0
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 |
|