02_input_interactions.md
1 # Kamaji TUI - Input Handling & User Interactions Specification 2 3 **Version:** 1.0 4 **Last Updated:** 2025-11-01 5 **Status:** Complete Documentation 6 7 --- 8 9 ## Table of Contents 10 11 1. [Overview](#overview) 12 2. [Input Architecture](#input-architecture) 13 3. [Priority Hierarchy](#priority-hierarchy) 14 4. [Keyboard Interactions](#keyboard-interactions) 15 5. [Mouse Interactions](#mouse-interactions) 16 6. [Mode-Specific Behaviors](#mode-specific-behaviors) 17 7. [Text Input & Processing](#text-input--processing) 18 8. [Autocomplete System](#autocomplete-system) 19 9. [State Transitions](#state-transitions) 20 10. [Command Processing](#command-processing) 21 11. [Agent Mentions & Switching](#agent-mentions--switching) 22 12. [Input Validation](#input-validation) 23 24 --- 25 26 ## Overview 27 28 The Kamaji TUI implements a layered input handling system with clear priority hierarchies and mode-based routing. All keyboard and mouse input flows through the `InputHandler` component, which dispatches events based on the current UI state. 29 30 ### Core Components 31 32 - **InputHandler** (`input.go`): Central dispatcher for all input events 33 - **IntegratedTUIModel.Update()** (`integrated.go`): Main event loop handler 34 - **CommandPalette** (`palette.go`): Command search and execution overlay 35 - **PermissionDialog** (`permissions.go`): Tool permission request overlay 36 - **AgentSelector** (`agent_selector.go`): Agent selection menu 37 - **AutocompleteState** (`autocomplete.go`): @ mention autocomplete system 38 39 --- 40 41 ## Input Architecture 42 43 ### Event Flow 44 45 ``` 46 tea.Msg 47 ↓ 48 IntegratedTUIModel.Update() 49 ↓ 50 ├─ tea.KeyMsg → InputHandler.HandleKeyMsg() 51 ├─ tea.MouseMsg → InputHandler.HandleMouseMsg() 52 ├─ tea.WindowSizeMsg → [resize handling] 53 ├─ responseMsg → [LLM response handling] 54 ├─ streamStartMsg → [streaming start] 55 ├─ streamChunkMsg → [streaming chunk] 56 ├─ PermissionResponseMsg → [permission handling] 57 └─ [other custom messages] 58 ``` 59 60 ### Input Handler Routing 61 62 `HandleKeyMsg()` routes inputs based on priority: 63 64 1. **Permission Dialog** (highest priority) 65 2. **Agent Menu** (navigation overlay) 66 3. **Command Palette** (command search) 67 4. **Global Shortcuts** (Ctrl+P, Ctrl+A, Ctrl+S, Ctrl+T, Ctrl+M) 68 5. **Main Input** (chat interface) 69 70 --- 71 72 ## Priority Hierarchy 73 74 ### Input Priority Levels (Highest to Lowest) 75 76 1. **Permission Dialog Visible** 77 - Captures ALL input when visible 78 - Keys: left, right, h, l, tab, enter, a, s, d, esc 79 - Blocks all other input processing 80 81 2. **Agent Menu Open** (`m.showAgentMenu == true`) 82 - Captures: up, down, k, j, enter, esc 83 - Prevents main input and global shortcuts 84 85 3. **Command Palette Open** (`m.commandPalette.IsVisible()`) 86 - Captures: esc, ctrl+c, q, up, down, k, j, enter, backspace, printable chars 87 - Prevents main input (but NOT permission dialog) 88 89 4. **Global Shortcuts** (when palette closed and not loading) 90 - Ctrl+P, Ctrl+A, Ctrl+S, Ctrl+T, Ctrl+M 91 - Active in main mode only 92 93 5. **Main Input** 94 - Tab (autocomplete), Ctrl+C (quit), Enter (send), regular typing 95 - Only active when no overlays are open 96 97 --- 98 99 ## Keyboard Interactions 100 101 ### Global Hotkeys (Main Mode Only) 102 103 | Key | Action | Condition | File | Line | 104 |-----|--------|-----------|------|------| 105 | `Ctrl+P` | Open command palette | `!m.commandPalette.IsVisible() && !m.loading` | `input.go` | 33 | 106 | `Ctrl+A` | Open agent menu | `!m.commandPalette.IsVisible() && !m.loading` | `input.go` | 38 | 107 | `Ctrl+T` | Show tools list | `!m.commandPalette.IsVisible() && !m.loading` | `input.go` | 45 | 108 | `Ctrl+M` | Show MCP info | `!m.commandPalette.IsVisible() && !m.loading` | `input.go` | 51 | 109 | `Ctrl+S` | Toggle sidebar visibility | `!m.commandPalette.IsVisible() && !m.loading` | `input.go` | 54-68 | 110 | `Ctrl+C` | Quit application | Main input mode | `input.go` | 183-184 | 111 112 **Note:** Ctrl+A appears twice in code (lines 38 and 48) - second instance is unreachable due to first check. 113 114 ### Command Palette Mode 115 116 | Key | Action | Effect | File | Line | 117 |-----|--------|--------|------|------| 118 | `esc` | Close palette | Hides palette, returns to main | `input.go` | 131-133 | 119 | `ctrl+c` | Close palette | Same as esc | `input.go` | 131-133 | 120 | `q` | Close palette | Same as esc | `input.go` | 131-133 | 121 | `up` | Move selection up | Cycles through filtered commands | `input.go` | 134-137 | 122 | `k` | Move selection up | Vim-style navigation | `input.go` | 134-137 | 123 | `down` | Move selection down | Cycles through filtered commands | `input.go` | 138-141 | 124 | `j` | Move selection down | Vim-style navigation | `input.go` | 138-141 | 125 | `enter` | Execute command | Executes selected command, closes palette | `input.go` | 140-146 | 126 | `backspace` | Remove character from search | Updates filter | `input.go` | 147-150 | 127 | `[printable]` | Add to search query | Filters commands (chars `' '` to `'~'`) | `input.go` | 152-154 | 128 129 **Palette Navigation Logic:** 130 - `MoveUp()`: `selected = (selected - 1 + len(filtered)) % len(filtered)` (wraps around) 131 - `MoveDown()`: `selected = (selected + 1) % len(filtered)` (wraps around) 132 133 ### Agent Menu Mode 134 135 | Key | Action | Effect | File | Line | 136 |-----|--------|--------|------|------| 137 | `up` | Move selection up | Selects previous agent in list | `input.go` | 96-98 | 138 | `k` | Move selection up | Vim-style navigation | `input.go` | 96-98 | 139 | `down` | Move selection down | Selects next agent in list | `input.go` | 99-101 | 140 | `j` | Move selection down | Vim-style navigation | `input.go` | 99-101 | 141 | `enter` | Select agent | Activates agent, shows system message, closes menu | `input.go` | 102-120 | 142 | `esc` | Close menu | Returns to main input | `input.go` | 121-123 | 143 144 **Agent Selection Logic:** 145 - Scrolling viewport implemented in `AgentSelector` with `scrollOffset` 146 - Visible cards calculated based on terminal height: `visibleCards = (height - 5) / 9` 147 - Auto-scrolls when selection moves outside visible area 148 - Resets to top (`selectedIndex = 0`, `scrollOffset = 0`) when opened 149 150 ### Permission Dialog Mode 151 152 | Key | Action | Effect | File | Line | 153 |-----|--------|--------|------|------| 154 | `left` | Move selection left | Previous option (wraps) | `permissions.go` | 99-100 | 155 | `h` | Move selection left | Vim-style | `permissions.go` | 99-100 | 156 | `right` | Move selection right | Next option (wraps) | `permissions.go` | 101-102 | 157 | `l` | Move selection right | Vim-style | `permissions.go` | 101-102 | 158 | `tab` | Move selection right | Same as right | `permissions.go` | 101-102 | 159 | `enter` | Confirm selection | Executes selected option, closes dialog | `permissions.go` | 103-104 | 160 | `a` / `A` | Allow | Directly selects "Allow" | `permissions.go` | 105-106 | 161 | `s` / `S` | Allow for session | Directly selects "Allow for Session" | `permissions.go` | 107-108 | 162 | `d` / `D` | Deny | Directly selects "Deny" | `permissions.go` | 109-110 | 163 | `esc` | Deny | Same as deny | `permissions.go` | 109-110 | 164 165 **Options:** 166 - 0: Allow (permanently) 167 - 1: Allow for Session (until app closes) 168 - 2: Deny 169 170 ### Main Input Mode 171 172 | Key | Action | Effect | File | Line | 173 |-----|--------|--------|------|------| 174 | `Tab` | Autocomplete agent | Completes `@agent` mentions | `input.go` | 162-180 | 175 | `Ctrl+C` | Quit application | Exits TUI | `input.go` | 183-184 | 176 | `Enter` | Send message | Processes input, sends to LLM | `input.go` | 185-204 | 177 | `[any]` | Type character | Updates textarea | `input.go` | 207-209 | 178 179 **Textarea Keybindings:** 180 The textarea component has its own internal keybindings (from charmbracelet/bubbles): 181 - Arrow keys: Navigate cursor 182 - Home/End: Beginning/end of line 183 - Backspace/Delete: Character deletion 184 - Ctrl+K: Delete to end of line 185 - Ctrl+U: Delete entire line 186 187 --- 188 189 ## Mouse Interactions 190 191 ### Mouse Event Handling 192 193 | Event | Action | Effect | File | Line | 194 |-------|--------|--------|------|------| 195 | `MouseWheelUp` | Scroll viewport up | Scrolls chat history up 3 lines | `input.go` | 83-85 | 196 | `MouseWheelDown` | Scroll viewport down | Scrolls chat history down 3 lines | `input.go` | 86-88 | 197 198 **Note:** No click interactions implemented. Only scroll wheel is handled. 199 200 --- 201 202 ## Mode-Specific Behaviors 203 204 ### 1. Permission Dialog Mode 205 206 **Entry:** When `m.permissionDialog.Show(request)` is called 207 **Exit:** After user responds (Allow/Session/Deny) 208 **Priority:** HIGHEST - blocks all other input 209 210 **State:** 211 - `visible`: boolean flag 212 - `selectedOption`: 0-2 (Allow/Session/Deny) 213 - `request`: PermissionRequest object 214 215 **Behavior:** 216 - Captures all keyboard input 217 - Horizontal navigation (left/right/h/l/tab) 218 - Direct hotkey selection (a/s/d) 219 - Enter confirms, esc denies 220 - Sends `PermissionResponseMsg` on exit 221 222 ### 2. Agent Menu Mode 223 224 **Entry:** `Ctrl+A` pressed in main mode 225 **Exit:** Agent selected (Enter) or cancelled (Esc) 226 **State Flag:** `m.showAgentMenu` 227 228 **State:** 229 - `selectedIndex`: Currently highlighted agent 230 - `scrollOffset`: First visible agent index 231 - `visibleCards`: Number of agents fitting on screen 232 233 **Behavior:** 234 - Vertical navigation (up/down/j/k) 235 - Auto-scrolling with viewport management 236 - Resets to top on open (`agentSelector.Reset()`) 237 - On selection: 238 - Sets `m.selectedAgent` 239 - Adds system message with agent info 240 - Updates viewport and scrolls to bottom 241 - Sets `m.showAgentMenu = false` 242 243 ### 3. Command Palette Mode 244 245 **Entry:** `Ctrl+P` pressed in main mode 246 **Exit:** Command executed (Enter) or cancelled (Esc/Ctrl+C/q) 247 **State Method:** `m.commandPalette.IsVisible()` 248 249 **State:** 250 - `query`: Search string 251 - `filtered`: Commands matching query 252 - `selected`: Index in filtered list 253 - `visible`: Display flag 254 255 **Behavior:** 256 - Real-time search filtering 257 - Vertical navigation (up/down/j/k) 258 - Backspace removes from query 259 - Printable chars add to query 260 - Filter updates trigger `selected = 0` reset 261 - Enter executes `handlePaletteCommand(cmd.ID)` 262 263 **Commands Available:** 264 | ID | Name | Category | Description | 265 |----|------|----------|-------------| 266 | `clear` | Sweep Clean | core | Clear messages | 267 | `tools` | Inspect Tools | tools | Show tools list | 268 | `agents` | Spirit Agents | tools | Show agents info | 269 | `mcp` | MCP Servers | tools | Show MCP status | 270 | `help` | Ancient Scrolls | settings | Show help text | 271 | `consciousness` | Consciousness Status | consciousness | View consciousness metrics | 272 | `thoughts` | Recent Thoughts | consciousness | Show thought stream | 273 | `personality` | Personality Profile | consciousness | View trait evolution | 274 | `memory` | Memory Crystals | tools | View message count | 275 | `quit` | Return Upstairs | settings | Exit app | 276 | `provider:*` | Provider switch | provider | Switch LLM provider | 277 278 ### 4. Main Input Mode 279 280 **Entry:** Default mode when no overlays active 281 **Exit:** When overlay opened 282 **Condition:** `!m.commandPalette.IsVisible() && !m.showAgentMenu && !m.permissionDialog.IsVisible()` 283 284 **State:** 285 - `m.textarea`: Bubble tea textarea component 286 - `m.loading`: Prevents input when waiting for response 287 - `m.messages`: Message history 288 289 **Behavior:** 290 - Tab autocomplete for `@agent` mentions 291 - Enter sends message (if non-empty) 292 - Ctrl+C quits 293 - All other keys update textarea 294 - Message sending triggers: 295 - `handleAgentMention()` - checks for `@agent` 296 - Adds user message to history 297 - Clears textarea 298 - Updates viewport to bottom 299 - Sets `m.loading = true` 300 - Starts bottom animation 301 - Returns `sendRequest(input)` command 302 303 --- 304 305 ## Text Input & Processing 306 307 ### Input Field Behavior 308 309 **Component:** `github.com/charmbracelet/bubbles/textarea` 310 311 **Placeholder Text:** 312 `"Speak to the furnace spirit... (type @ for agents)"` 313 314 **Display Logic:** 315 - When empty: Shows colored flame animation 316 - When typing: Shows textarea with text 317 - `@ mentions`: Shows autocomplete dropdown 318 319 **Message Validation:** 320 ```go 321 input := strings.TrimSpace(m.textarea.Value()) 322 if input == "" { 323 return m, nil // Reject empty messages 324 } 325 ``` 326 327 **Processing Flow:** 328 1. User types in textarea 329 2. Tab triggers autocomplete (if `@` present) 330 3. Enter sends message: 331 - Trim whitespace 332 - Check for empty (reject if empty) 333 - Process `@agent` mentions 334 - Reset textarea 335 - Add to message history 336 - Update viewport 337 - Set loading state 338 - Send LLM request 339 340 ### Message Sending 341 342 **Trigger:** Enter key in main mode 343 **File:** `input.go` lines 185-204 344 **File:** `integrated.go` lines 700-733 345 346 **Steps:** 347 1. Trim input: `strings.TrimSpace(m.textarea.Value())` 348 2. Validate non-empty 349 3. Check for agent mentions: `handleAgentMention(m, input)` 350 4. Reset textarea: `m.textarea.Reset()` 351 5. Append user message with timestamp 352 6. Update viewport: `m.viewport.SetContent(m.renderMessages())` 353 7. Scroll to bottom: `m.viewport.GotoBottom()` 354 8. Set loading: `m.loading = true` 355 9. Start animation: `m.bottomAnimation.Start()` 356 10. Return send command: `m.sendRequest(input)` 357 358 **LLM Request:** 359 - Builds system context from selected agent 360 - Adds tool descriptions 361 - Combines: `systemContext + toolContext + "User: " + input` 362 - Attempts streaming: `m.llm.CallStream(ctx, fullPrompt)` 363 - Falls back to regular call if streaming fails 364 - Returns `streamStartMsg` or `responseMsg` 365 366 --- 367 368 ## Autocomplete System 369 370 ### @ Mention Autocomplete 371 372 **Component:** `AutocompleteState` (`autocomplete.go`) 373 **Trigger:** Typing `@` followed by characters 374 **Completion Key:** `Tab` 375 376 ### Autocomplete Logic 377 378 **Activation:** 379 ```go 380 if strings.HasPrefix(word, "@") { 381 prefix := strings.ToLower(word[1:]) 382 allAgents := []string{"Kamaji", "Hayao", "Chihiro", "Moe", "Wayne"} 383 384 // Find first matching agent 385 for _, agent := range allAgents { 386 if prefix == "" || strings.HasPrefix(strings.ToLower(agent), prefix) { 387 words[i] = "@" + agent 388 m.textarea.SetValue(strings.Join(words, " ") + " ") 389 return m, nil 390 } 391 } 392 } 393 ``` 394 395 **Available Agents:** 396 - Kamaji (default) 397 - Hayao 398 - Chihiro 399 - Moe 400 - Wayne 401 402 **Matching:** 403 - Case-insensitive prefix matching 404 - Empty prefix matches first agent (Kamaji) 405 - Partial typing filters: `@m` → Moe, `@mo` → Moe, `@h` → Hayao 406 407 **Display:** 408 1. **Inline Completion:** Grey text showing remainder of match 409 ``` 410 @mo[e] ← grey "e" 411 ``` 412 413 2. **Dropdown:** List of all matches (if >1) 414 ``` 415 @Kamaji 416 @Hayao 417 @Chihiro 418 @Moe ← highlighted 419 @Wayne 420 ``` 421 422 **Completion Behavior:** 423 - Tab replaces word with full agent name 424 - Adds trailing space after completion 425 - Sets textarea value directly 426 - Example: `"Tell @m"` + Tab → `"Tell @Moe "` 427 428 ### AutocompleteState Methods 429 430 | Method | Purpose | File | Line | 431 |--------|---------|------|------| 432 | `Update(text, agents)` | Updates matches based on current text | `autocomplete.go` | 22-51 | 433 | `GetInlineCompletion()` | Returns grey completion text | `autocomplete.go` | 54-66 | 434 | `GetDropdown()` | Returns dropdown list view | `autocomplete.go` | 69-84 | 435 | `Complete(originalText)` | Returns text with completion applied | `autocomplete.go` | 87-100 | 436 | `MoveUp()` | Previous match in dropdown | `autocomplete.go` | 103-110 | 437 | `MoveDown()` | Next match in dropdown | `autocomplete.go` | 113-120 | 438 439 **Note:** Dropdown navigation (MoveUp/MoveDown) exists but is NOT connected to keyboard input in main code. 440 441 --- 442 443 ## State Transitions 444 445 ### State Diagram 446 447 ``` 448 ┌──────────────┐ 449 │ Main Input │ ◄─────────────────────────────┐ 450 └───┬──────────┘ │ 451 │ │ 452 │ Ctrl+P │ esc/enter 453 ├─────────────►┌──────────────────┐ │ 454 │ │ Command Palette ├─────────┤ 455 │ └──────────────────┘ │ 456 │ │ 457 │ Ctrl+A │ 458 ├─────────────►┌──────────────────┐ │ 459 │ │ Agent Menu ├─────────┤ 460 │ └──────────────────┘ │ 461 │ │ 462 │ Tool Request │ 463 └─────────────►┌──────────────────┐ │ 464 │ Permission Dialog├─────────┘ 465 └──────────────────┘ 466 (highest priority) 467 ``` 468 469 ### State Variables 470 471 | Variable | Type | Purpose | File | 472 |----------|------|---------|------| 473 | `m.showAgentMenu` | bool | Agent menu visible | `integrated.go` | 474 | `m.commandPalette.visible` | bool | Palette visible | `palette.go` | 475 | `m.permissionDialog.visible` | bool | Dialog visible | `permissions.go` | 476 | `m.loading` | bool | Waiting for LLM | `integrated.go` | 477 | `m.streaming` | bool | LLM streaming active | `integrated.go` | 478 | `m.sidebarVisible` | bool | Sidebar shown | `integrated.go` | 479 480 ### Loading State 481 482 **Purpose:** Prevent input during LLM processing 483 **Set:** On message send 484 **Cleared:** On response complete, stream complete, or error 485 486 **Effect:** 487 - Disables global shortcuts (Ctrl+P, Ctrl+A, etc.) 488 - Shows thinking spinner in status bar 489 - Shows bottom loading animation 490 - Main input still accepts typing (not blocked) 491 492 --- 493 494 ## Command Processing 495 496 ### Command Palette Commands 497 498 **Handler:** `handlePaletteCommand(commandID string)` (`integrated.go` line 295) 499 500 #### Core Commands 501 502 | ID | Action | Implementation | 503 |----|--------|----------------| 504 | `clear` | Clear messages | `m.messages = []Message{}`, shows welcome | 505 | `tools` | Show tools | Calls `handleDirectAction("tools")` | 506 | `agents` | Show agents | Calls `handleDirectAction("agents")` | 507 | `mcp` | Show MCP | Calls `handleDirectAction("mcp")` | 508 | `help` | Show help | Appends multi-line help text to messages | 509 | `quit` | Exit | Returns `tea.Quit` | 510 511 #### Consciousness Commands 512 513 | ID | Action | Implementation | 514 |----|--------|----------------| 515 | `consciousness` | Status | Gets `m.consciousness.GetStatus()`, shows in message | 516 | `thoughts` | Recent thoughts | Gets `GetRecentThoughts(10)`, shows in message | 517 | `personality` | Personality | Gets `GetPersonalityDescription()`, shows in message | 518 | `memory` | Memory info | Shows message count: `len(m.messages)` | 519 520 #### Provider Commands 521 522 **Pattern:** `provider:*` 523 524 | ID | Provider | Implementation | 525 |----|----------|----------------| 526 | `provider:q` | Amazon Q | Calls `switchProvider("q")` | 527 | `provider:ollama` | Ollama | Calls `switchProvider("ollama")` | 528 | `provider:openai` | OpenAI | Calls `switchProvider("openai")` | 529 | `provider:anthropic` | Anthropic | Calls `switchProvider("anthropic")` | 530 531 **Switch Logic:** 532 ```go 533 if strings.HasPrefix(commandID, "provider:") { 534 provider := strings.TrimPrefix(commandID, "provider:") 535 return m.switchProvider(provider) 536 } 537 ``` 538 539 ### Direct Actions (Hotkeys) 540 541 **Handler:** `handleDirectAction(action string)` (`integrated.go` line 564) 542 543 **Actions:** 544 545 1. **"tools"** - Shows formatted tools list: 546 - File Operations (5) 547 - Editing (2) 548 - Shell (1) 549 - Search & Discovery (4) 550 - Web (2) 551 - Enhanced Listing (2) 552 - Git (4) 553 - Total: 20 tools 554 555 2. **"agents"** - Shows agent status: 556 - Primary agent info (provider, model) 557 - Specialized agents list 558 - Agent features 559 - All agents ready message 560 561 3. **"mcp"** - Shows MCP status: 562 - Status: Not yet implemented 563 - Coming soon list 564 - Planned features 565 566 **All actions:** Append system message and update viewport. 567 568 --- 569 570 ## Agent Mentions & Switching 571 572 ### Mention Detection 573 574 **Handler:** `handleAgentMention(m, input)` (`input.go` line 214) 575 **Trigger:** Before sending message 576 **Pattern:** `@agentname` anywhere in input 577 578 **Implementation:** 579 ```go 580 words := strings.Fields(input) 581 for _, word := range words { 582 if strings.HasPrefix(word, "@") { 583 agentName := strings.ToLower(word[1:]) 584 switch agentName { 585 case "moe": 586 m.switchToAgent("Moe") 587 case "kamaji": 588 m.switchToAgent("Kamaji") 589 case "hayao": 590 m.switchToAgent("Hayao") 591 case "chihiro": 592 m.switchToAgent("Chihiro") 593 case "wayne": 594 m.switchToAgent("Wayne") 595 } 596 } 597 } 598 ``` 599 600 **Behavior:** 601 - Case-insensitive matching 602 - Processes ALL mentions in input 603 - Last mention wins if multiple 604 - Silent switch (no feedback to user) 605 - Only processes hardcoded list 606 607 ### Agent Switching 608 609 **Method:** `switchToAgent(name string)` 610 **Effect:** 611 - Looks up agent in `m.availableAgents` 612 - Sets `m.selectedAgent` 613 - Updates system prompt for LLM 614 - Next message uses new agent's personality 615 616 **Agent Context:** 617 Built in `getAgentSystemContext()` (`integrated.go` line 761): 618 - Agent name and personality name 619 - Traits list 620 - Tone and approach 621 - Specialties 622 - Capabilities summary 623 - Special instructions per agent (Prodigy, Kamaji, Moe) 624 625 --- 626 627 ## Input Validation 628 629 ### Message Validation 630 631 **Location:** `input.go` line 186-189 632 633 ```go 634 input := strings.TrimSpace(m.textarea.Value()) 635 if input == "" { 636 return m, nil // Reject and do nothing 637 } 638 ``` 639 640 **Rules:** 641 - Trim leading/trailing whitespace 642 - Reject if empty after trim 643 - No length limits enforced 644 - No character filtering 645 646 ### Search Query Validation (Palette) 647 648 **Location:** `input.go` line 152 649 650 ```go 651 if len(msg.String()) == 1 && msg.String() >= " " && msg.String() <= "~" { 652 m.commandPalette.AddToQuery(msg.String()) 653 } 654 ``` 655 656 **Rules:** 657 - Only single printable ASCII characters 658 - Range: space (0x20) to tilde (0x7E) 659 - Rejects control chars, unicode, multi-byte 660 661 ### Command ID Validation 662 663 **Location:** `integrated.go` line 430-434 664 665 ```go 666 if strings.HasPrefix(commandID, "provider:") { 667 provider := strings.TrimPrefix(commandID, "provider:") 668 return m.switchProvider(provider) 669 } 670 ``` 671 672 **Pattern Matching:** 673 - Provider commands: `provider:*` 674 - Other commands: direct ID match 675 - No input sanitization on IDs 676 677 --- 678 679 ## Additional Technical Details 680 681 ### Viewport Scrolling 682 683 **Component:** `github.com/charmbracelet/bubbles/viewport` 684 685 **Auto-scroll Triggers:** 686 - Message received: `m.viewport.GotoBottom()` 687 - User message sent: `m.viewport.GotoBottom()` 688 - Command executed: `m.viewport.GotoBottom()` 689 690 **Manual Scroll:** 691 - Mouse wheel up: `viewport.LineUp(3)` 692 - Mouse wheel down: `viewport.LineDown(3)` 693 - No keyboard scroll controls 694 695 ### Sidebar Scroll (Panel) 696 697 **Component:** `SidebarPanel` (`panel.go`) 698 699 **Scroll State:** 700 - `scrollY`: Current scroll position 701 - `maxScroll`: Maximum scroll allowed 702 703 **Methods:** 704 - `ScrollUp()`: Decrements `scrollY` if > 0 705 - `ScrollDown()`: Increments `scrollY` if < `maxScroll` 706 707 **Note:** Scroll methods exist but are NOT connected to input handlers (no keybindings). 708 709 ### Window Resize Handling 710 711 **Event:** `tea.WindowSizeMsg` 712 **Handler:** `integrated.go` line 112-143 713 714 **Updates:** 715 - `m.width`, `m.height` 716 - Sidebar width (adaptive: 20-35 based on terminal width) 717 - Main content width 718 - Sidebar size: `m.sidebar.SetSize()` 719 - Bottom animation width 720 - Permission dialog size 721 - Viewport dimensions 722 - Textarea width 723 724 **Sidebar Width Logic:** 725 ```go 726 if msg.Width >= 90 { m.sidebarWidth = 35 } 727 else if msg.Width >= 70 { m.sidebarWidth = 30 } 728 else if msg.Width >= 55 { m.sidebarWidth = 25 } 729 else { m.sidebarWidth = 20 } 730 ``` 731 732 ### Animation Handling 733 734 **Tick Messages:** 735 - `tickMsg`: Thinking spinner (80ms interval) 736 - `bottomAnimTickMsg`: Bottom loading animation 737 - `flameTickMsg`: Flame animation 738 739 **None affect input handling** - animations are visual only. 740 741 --- 742 743 ## Summary of All Input Bindings 744 745 ### Complete Key Binding Table 746 747 | Mode | Key(s) | Action | Priority | 748 |------|--------|--------|----------| 749 | **ANY** | (varies) | Permission dialog | 1 (HIGHEST) | 750 | **Agent Menu** | up, k | Previous agent | 2 | 751 | **Agent Menu** | down, j | Next agent | 2 | 752 | **Agent Menu** | enter | Select agent | 2 | 753 | **Agent Menu** | esc | Cancel | 2 | 754 | **Palette** | esc, ctrl+c, q | Close palette | 3 | 755 | **Palette** | up, k | Previous command | 3 | 756 | **Palette** | down, j | Next command | 3 | 757 | **Palette** | enter | Execute command | 3 | 758 | **Palette** | backspace | Delete search char | 3 | 759 | **Palette** | [printable] | Add to search | 3 | 760 | **Permission** | left, h | Previous option | 1 | 761 | **Permission** | right, l, tab | Next option | 1 | 762 | **Permission** | enter | Confirm selection | 1 | 763 | **Permission** | a, A | Allow directly | 1 | 764 | **Permission** | s, S | Allow for session | 1 | 765 | **Permission** | d, D, esc | Deny | 1 | 766 | **Main** | ctrl+p | Open palette | 4 | 767 | **Main** | ctrl+a | Open agent menu | 4 | 768 | **Main** | ctrl+t | Show tools | 4 | 769 | **Main** | ctrl+m | Show MCP | 4 | 770 | **Main** | ctrl+s | Toggle sidebar | 4 | 771 | **Main** | tab | Autocomplete @agent | 5 | 772 | **Main** | ctrl+c | Quit | 5 | 773 | **Main** | enter | Send message | 5 | 774 | **Main** | (any) | Type in textarea | 5 | 775 | **Viewport** | Mouse Wheel Up | Scroll up 3 lines | N/A | 776 | **Viewport** | Mouse Wheel Down | Scroll down 3 lines | N/A | 777 778 --- 779 780 ## File Reference Index 781 782 | File | Purpose | Key Components | 783 |------|---------|----------------| 784 | `input.go` | Central input dispatcher | `InputHandler`, `HandleKeyMsg`, `HandleMouseMsg` | 785 | `integrated.go` | Main event loop | `Update()`, message handlers, command execution | 786 | `palette.go` | Command search | `CommandPalette`, command list, filtering | 787 | `permissions.go` | Permission requests | `PermissionDialog`, option selection | 788 | `agent_selector.go` | Agent menu | `AgentSelector`, card rendering, scrolling | 789 | `autocomplete.go` | @ mention completion | `AutocompleteState`, matching logic | 790 | `panel.go` | Sidebar panel | `SidebarPanel` (scroll methods unused) | 791 792 --- 793 794 ## Known Limitations & Notes 795 796 1. **Duplicate Ctrl+A Binding:** Lines 38 and 48 in `input.go` both check for Ctrl+A. Second check is unreachable. 797 798 2. **Autocomplete Dropdown Navigation:** `MoveUp()`/`MoveDown()` exist but are not wired to keyboard input. Only Tab completion works. 799 800 3. **Sidebar Scroll:** `ScrollUp()`/`ScrollDown()` methods exist but have no keybindings. 801 802 4. **Loading State Input:** Global shortcuts disabled during loading, but textarea still accepts typing. 803 804 5. **Agent List Hardcoded:** Available agents for @ completion are hardcoded, not dynamic from `m.availableAgents`. 805 806 6. **No Click Support:** Mouse handling only supports scroll wheel, no click events. 807 808 7. **No Multi-line Input:** Textarea is single-height, no Shift+Enter for newlines. 809 810 8. **Command Palette Filter:** Case-insensitive search across name, description, and ID. 811 812 9. **Permission Dialog:** Always appears as overlay, no queue system for multiple requests. 813 814 10. **Viewport Auto-scroll:** No way to disable auto-scroll to bottom on new messages. 815 816 --- 817 818 **End of Specification**