01_ui_components.md
1 # Kamaji TUI - Comprehensive UI Components Specification 2 3 **Version:** 1.0 4 **Date:** 2025-11-01 5 **Status:** Complete Analysis 6 7 --- 8 9 ## Table of Contents 10 11 1. [Overview](#overview) 12 2. [Core Model Structure](#core-model-structure) 13 3. [Component Inventory](#component-inventory) 14 4. [Visual Styling System](#visual-styling-system) 15 5. [Animation Systems](#animation-systems) 16 6. [Input Handling](#input-handling) 17 7. [Message Rendering](#message-rendering) 18 8. [Layout & Sizing](#layout-and-sizing) 19 9. [Component Details](#component-details) 20 10. [State Management](#state-management) 21 22 --- 23 24 ## Overview 25 26 The Kamaji TUI is a sophisticated terminal user interface built on the Bubble Tea framework with lipgloss styling. It features a multi-agent AI interaction system with rich animations, a consciousness monitoring sidebar, command palette, and comprehensive permission system. 27 28 ### Architecture Pattern 29 - **Framework:** Bubble Tea (Model-View-Update pattern) 30 - **Styling:** Lipgloss (declarative terminal styling) 31 - **Theme:** Fire-themed dark blue interface inspired by Spirited Away 32 33 --- 34 35 ## Core Model Structure 36 37 ### IntegratedTUIModel 38 **Location:** `/internal/tui/model.go` (Lines 30-63) 39 40 ```go 41 type IntegratedTUIModel struct { 42 // Core Components 43 viewport viewport.Model // Message display area 44 textarea textarea.Model // User input field 45 sidebar *SidebarPanel // Right-side consciousness panel 46 47 // Configuration & LLM 48 config *config.Config 49 llm types.LLMProvider 50 provider string // "ollama", "anthropic", "openai", "q" 51 model string // Model name 52 53 // Message Management 54 messages []Message // Conversation history 55 loading bool // Loading state 56 streaming bool // Active streaming flag 57 currentStream <-chan types.StreamChunk // Stream channel 58 59 // Layout & Sizing 60 width int // Terminal width 61 height int // Terminal height 62 ready bool // Initialization complete 63 sidebarWidth int // Sidebar width (adaptive) 64 sidebarVisible bool // User toggle state 65 66 // Animation Components 67 thinkingSpinner ThinkingSpinner // Status bar spinner 68 bottomAnimation *BottomAnimation // Loading gradient 69 flameAnimation *FlameAnimation // Input area flames 70 71 // UI Components 72 commandPalette *CommandPalette // Command palette (Ctrl+P) 73 permissionDialog *PermissionDialog // Permission requests 74 agentSelector *AgentSelector // Agent menu 75 agentAutocomplete *AgentAutocomplete // @ mention autocomplete 76 77 // Agent System 78 agentRegistry *agents.AgentRegistry 79 selectedAgent *agents.SpecializedAgent 80 showAgentMenu bool 81 82 // Input Handling 83 inputHandler *InputHandler 84 autocomplete *AutocompleteState 85 86 // Consciousness 87 consciousness *consciousness.System // AI consciousness tracking 88 89 // Provider Management 90 providerList []string // Available providers 91 selectedProvider int // Current selection index 92 } 93 ``` 94 95 ### Message Structure 96 **Location:** `/internal/tui/model.go` (Lines 20-28) 97 98 ```go 99 type Message struct { 100 Role string // "user", "assistant", "system" 101 Content string // Message text 102 AgentName string // Sending agent ("Kamaji", "Moe", etc.) 103 ToolName string // Tool used (if applicable) 104 Timing float64 // Response time 105 Timestamp time.Time // Message timestamp 106 } 107 ``` 108 109 --- 110 111 ## Component Inventory 112 113 ### 1. Viewport Component 114 **Type:** `viewport.Model` (Bubble Tea component) 115 **Purpose:** Scrollable message display area 116 **Location:** Center/left panel 117 118 **Configuration:** 119 - Width: `mainWidth` (calculated based on sidebar visibility) 120 - Height: `msg.Height - 4` (reserves space for status, footer, input) 121 - Content: Rendered messages with Kamaji ASCII art header 122 123 **Key Methods:** 124 - `SetContent()` - Update displayed content 125 - `GotoBottom()` - Auto-scroll to latest message 126 - `LineUp(3)` / `LineDown(3)` - Mouse wheel scrolling 127 128 ### 2. Textarea Component 129 **Type:** `textarea.Model` (Bubble Tea component) 130 **Purpose:** User input field 131 **Location:** Bottom of screen, full width 132 133 **Configuration (`model.go` Lines 77-83):** 134 ```go 135 ta := textarea.New() 136 ta.Placeholder = "๐ฅ Speak to the furnace spirit..." 137 ta.Focus() 138 ta.SetWidth(80) // Updated dynamically on resize 139 ta.SetHeight(1) 140 ta.ShowLineNumbers = false 141 ta.Cursor.SetMode(cursor.CursorBlink) 142 ``` 143 144 **Special Features:** 145 - Placeholder changes in `customInputView()` to: "Speak to the furnace spirit... (type @ for agents)" 146 - @ autocomplete for agent mentions 147 - Tab completion for agent names 148 - Full-width rendering with flame emoji decoration 149 150 ### 3. Sidebar Panel 151 **Type:** `*SidebarPanel` 152 **File:** `/internal/tui/panel.go` 153 **Purpose:** Real-time consciousness metrics and system info 154 155 #### Structure (`panel.go` Lines 15-24) 156 ```go 157 type SidebarPanel struct { 158 width int 159 height int 160 provider string 161 model string 162 scrollY int // Current scroll position 163 maxScroll int // Maximum scroll position 164 consciousness interface{} // Consciousness system reference 165 } 166 ``` 167 168 #### Sections Rendered 169 170 **Logo Section** (`renderLogo()` Lines 193-213) 171 - **Full Logo** (height > 25): Fire-themed multi-line ASCII art 172 - Fire-yellow header box: "KAMAJI AI" 173 - Red fire emojis (๐ฅ) with box drawing 174 - White face with eyes (โ), nose (โผ), mouth (โโโ) 175 - Gold gears (โ) 176 - **Compact Logo** (height โค 25): "๐ฅ KAMAJI" in fire-red 177 178 **Working Directory** (`renderWorkingDir()` Lines 222-234) 179 - Header: "๐ Working Directory" (cyan accent) 180 - Content: `filepath.Base(os.Getwd())` 181 - Style: Gray text, left-padded 182 183 **Model Info** (`renderModelInfo()` Lines 236-254) 184 - Header: "๐ค AI Model" (cyan accent) 185 - Content: "via {provider}" (anthropic, ollama, openai, q) 186 - Style: Gray text 187 188 **Project Context** (`renderProjectContext()` Lines 256-280) 189 - Header: "๐ Project" 190 - Type detection: Go, Rust, Node.js, Python, Java, C/C++, Generic 191 - File count (non-recursive, excluding hidden files) 192 193 **Tools Section** (`renderToolsSection()` Lines 282-308) 194 - Header: "๐ง Tools" (cyan accent) 195 - Categories (gray bullets): 196 - File Operations (5) 197 - Git Commands (4) 198 - Shell Execution (1) 199 - Code Analysis (1) 200 - Total: 11 tools (yellow text) 201 202 **Agents Section** (`renderAgentsSection()` Lines 311-368) 203 - Header: "๐ค Specialized Agents" (cyan accent) 204 - Agent list with icons and color-coded levels: 205 - **Master** (Lavender #E6E6FA): Prodigy 206 - **Expert** (Red #FF6B6B): Kamaji, Code Architect, Security, Data Scientist, Writer 207 - **Advanced** (Purple #7B68EE): DevOps, Designer 208 - **Autonomous** (Gold #FFD700): Product Manager, Researcher, Learning, Visionary 209 - Hint: "Press ^A to select" (italic gray) 210 211 **Navigation Section** (`renderNavigationSection()` Lines 371-397) 212 - Header: "๐งญ Navigation" (cyan accent) 213 - Commands: 214 - furnace commands [^P] 215 - tools [^T] 216 - agents [^A] 217 - mcp [^M] 218 - exit boiler [^C] 219 220 **Consciousness Section** (`renderConsciousnessSection()` Lines 423-467) 221 - Header: "๐ง Consciousness" (Pink #FF6B9D) 222 - Metrics (yellow text): 223 - Awareness: {percentage} 224 - Thoughts: {count} 225 - Questions: {count} 226 - Mistakes: {count} 227 - Fallback: "Initializing..." if unavailable 228 229 **Version Section** (`renderVersionSection()` Lines 399-421) 230 - Header: "๐ฅ Version Info" (cyan accent) 231 - Details (yellow text): 232 - Version: {version} 233 - Build: {buildTime} 234 - Commit: {gitCommit[0:8]} 235 236 #### Scrolling System (`panel.go` Lines 112-144) 237 - Available height: `s.height - 4` (border + padding) 238 - Max scroll: `len(allLines) - availableHeight` 239 - Scroll indicators: `[{scrollY+1}/{totalScrollableLines}]` 240 - Methods: `ScrollUp()`, `ScrollDown()` 241 242 #### Container Styling (`panel.go` Lines 148-159) 243 ```go 244 style := lipgloss.NewStyle(). 245 Width(s.width - 2). // Account for border 246 Height(s.height - 2). // Account for border 247 Padding(1). 248 Align(lipgloss.Left, lipgloss.Top). 249 Background(bgDark). // Color 235 250 Foreground(textColor). // #f1f5f9 251 Border(lipgloss.DoubleBorder()). 252 BorderForeground(lipgloss.Color("#FF0000")) // Red border 253 ``` 254 255 #### Adaptive Width (`integrated.go` Lines 118-134) 256 ```go 257 if m.sidebarVisible { 258 if msg.Width >= 90: sidebarWidth = 35 // Full 259 elif msg.Width >= 70: sidebarWidth = 30 // Medium 260 elif msg.Width >= 55: sidebarWidth = 25 // Narrow 261 else: sidebarWidth = 20 // Minimal 262 mainWidth = msg.Width - sidebarWidth - 2 263 } else { 264 sidebarWidth = 0 265 mainWidth = msg.Width 266 } 267 ``` 268 269 ### 4. Command Palette 270 **Type:** `*CommandPalette` 271 **File:** `/internal/tui/palette.go` 272 **Trigger:** Ctrl+P 273 274 #### Structure (`palette.go` Lines 19-28) 275 ```go 276 type CommandPalette struct { 277 commands []Command // All available commands 278 filtered []Command // Search-filtered commands 279 selected int // Selected index 280 query string // Search query 281 visible bool // Visibility state 282 width int 283 height int 284 } 285 ``` 286 287 #### Command Structure (`palette.go` Lines 11-17) 288 ```go 289 type Command struct { 290 ID string // "clear", "tools", "provider:ollama" 291 Name string // "๐ฅ Ask the Furnace" 292 Description string // Detailed explanation 293 Category string // "core", "provider", "tools", "settings" 294 Shortcut string // "Ctrl+C" or empty 295 } 296 ``` 297 298 #### Command List (`palette.go` Lines 32-60) 299 300 **Core Furnace Commands:** 301 - `ask` - ๐ฅ Ask the Furnace 302 - `chat` - ๐ฌ New Conversation 303 - `clear` - ๐งน Sweep Clean 304 305 **Spirit Providers:** 306 - `provider:q` - ๐ Amazon Q Spirit 307 - `provider:ollama` - ๐ฆ Ollama Guardian 308 - `provider:openai` - ๐ค OpenAI Deity 309 - `provider:anthropic` - ๐ง Claude Sage 310 311 **Boiler Room Tools:** 312 - `tools` - โ Inspect Tools 313 - `agents` - ๐ป Spirit Agents 314 - `memory` - ๐ง Memory Crystals 315 - `workflow` - โก Steam Workflows 316 317 **Consciousness Features:** 318 - `consciousness` - ๐ง Consciousness Status 319 - `thoughts` - ๐ญ Recent Thoughts 320 - `personality` - ๐ญ Personality Profile 321 322 **Bathhouse Settings:** 323 - `config` - ๐ญ Bathhouse Rules 324 - `help` - ๐ Ancient Scrolls 325 - `version` - ๐ฎ Furnace Status 326 - `quit` - ๐ช Return Upstairs [Ctrl+C] 327 328 #### Rendering (`palette.go` Lines 154-281) 329 330 **Container:** Fixed width 70, centered overlay 331 332 **Header:** 333 ```go 334 headerStyle := lipgloss.NewStyle(). 335 Background(lipgloss.Color("235")). 336 Foreground(lipgloss.Color("196")). // Red 337 Bold(true). 338 Width(70). 339 Align(lipgloss.Center) 340 341 Content: "๐ฅ KAMAJI'S FURNACE COMMANDS ๐ฅ" 342 ``` 343 344 **Search Input:** 345 ```go 346 searchStyle := lipgloss.NewStyle(). 347 Background(lipgloss.Color("235")). 348 Foreground(lipgloss.Color("220")). // Yellow 349 Width(70). 350 Padding(0, 2) 351 352 Content: "โ {query}" or "โ Type to search..." 353 ``` 354 355 **Command Items:** Max 12 visible 356 - **Selected:** 357 - Background: Color 196 (red) 358 - Foreground: Color 235 (dark) 359 - Bold, left padding 2 360 - Prefix: "โถ" 361 - **Unselected:** 362 - Background: Color 235 363 - Foreground: Color 255 (white) 364 - Prefix: " " 365 366 **Format:** `{prefix} {name,-25} {description}` 367 368 **Footer:** 369 ```go 370 footerStyle := lipgloss.NewStyle(). 371 Background(lipgloss.Color("235")). 372 Foreground(lipgloss.Color("240")). 373 Width(70). 374 Align(lipgloss.Center). 375 Border(lipgloss.NormalBorder(), true, false, false, false) 376 377 Content: "โโ Navigate โข ENTER Execute โข ESC Exit" 378 ``` 379 380 **Container Border:** RoundedBorder, Color 196 (red) 381 382 #### Search Filtering (`palette.go` Lines 135-151) 383 - Case-insensitive 384 - Matches Name, Description, or ID 385 - Updates `filtered` array 386 - Resets `selected` to 0 387 388 ### 5. Agent Selector 389 **Type:** `*AgentSelector` 390 **File:** `/internal/tui/agent_selector.go` 391 **Trigger:** Ctrl+A 392 393 #### Structure (`agent_selector.go` Lines 12-19) 394 ```go 395 type AgentSelector struct { 396 agents []*agents.SpecializedAgent 397 selectedIndex int 398 scrollOffset int // For scrolling 399 visibleCards int // Cached visible count 400 filterLevel agents.IntelligenceLevel 401 filterType string 402 } 403 ``` 404 405 #### Agent Card Rendering (`renderAgentCard()` Lines 122-204) 406 407 **Border Styles:** 408 - **Selected:** ThickBorder, Cyan (#00FFFF), Background #1a1a2e 409 - **Unselected:** RoundedBorder, #3a3a5a 410 411 **Card Structure (~9 lines):** 412 ``` 413 {icon} {name} [{LEVEL}] 414 ID: {id} โข Type: {type} 415 416 ๐ญ "{personality}" - {approach} 417 418 โจ {trait1} โข {trait2} โข {trait3} 419 420 ๐ฏ {specialty1}, {specialty2} 421 ๐ง {capabilityCount} capabilities 422 ``` 423 424 **Color Scheme by Level:** 425 - Master: Lavender (#E6E6FA) 426 - Autonomous: Gold (#FFD700) 427 - Expert: Red (#FF6B6B) 428 - Advanced: Purple (#7B68EE) 429 - Intermediate/Basic: Gray (#888888) 430 431 **Level Badges:** 432 ```go 433 levelBadge := lipgloss.NewStyle(). 434 Foreground(lipgloss.Color("#000000")). 435 Background(lipgloss.Color(levelColor)). 436 Padding(0, 1). 437 Bold(true) 438 ``` 439 440 #### Scrolling (`agent_selector.go` Lines 70-92, 206-234) 441 - Header + Instructions: ~5 lines 442 - Card height: ~9 lines 443 - Visible cards: `(height - 5) / 9` 444 - Scroll indicators: "โฒ More agents above โฒ" / "โผ More agents below โผ" 445 - Count display: "Showing {start}-{end} of {total} agents" 446 447 **Auto-scroll on navigation:** 448 - Up: If `selected < scrollOffset`, set `scrollOffset = selected` 449 - Down: If `selected >= scrollOffset + visibleCards`, scroll down 450 451 #### Available Agents (from sidebar rendering) 452 1. Prodigy (Master) - ๐ก 453 2. Kamaji (Expert) - ๐ญ 454 3. Code Architect (Expert) - ๐๏ธ 455 4. Security (Expert) - ๐ก๏ธ 456 5. Data Scientist (Expert) - ๐ 457 6. Writer (Expert) - โ๏ธ 458 7. DevOps (Advanced) - โ๏ธ 459 8. Designer (Advanced) - ๐จ 460 9. Product Manager (Autonomous) - ๐ฑ 461 10. Researcher (Autonomous) - ๐ฌ 462 11. Learning (Autonomous) - ๐ 463 12. Visionary (Autonomous) - ๐ฎ 464 465 ### 6. Agent Autocomplete 466 **Type:** `*AgentAutocomplete` 467 **File:** `/internal/tui/agent_autocomplete.go` 468 **Trigger:** Typing "@" in textarea 469 470 #### Structure (`agent_autocomplete.go` Lines 11-18) 471 ```go 472 type AgentAutocomplete struct { 473 agents []*agents.SpecializedAgent 474 filteredAgents []*agents.SpecializedAgent 475 selectedIndex int 476 visible bool 477 prefix string 478 cursorPosition int 479 } 480 ``` 481 482 #### Filtering (`filterAgents()` Lines 91-106) 483 - Case-insensitive prefix matching 484 - Empty prefix shows all agents 485 - Uses `strings.HasPrefix(strings.ToLower(agent.Name), lowerPrefix)` 486 487 #### Rendering (`Render()` Lines 109-163) 488 489 **Container:** 490 ```go 491 containerStyle := lipgloss.NewStyle(). 492 Border(lipgloss.RoundedBorder()). 493 BorderForeground(lipgloss.Color("#4A90E2")). 494 Background(lipgloss.Color("#1A1A1A")). 495 Width(width) 496 ``` 497 498 **Items:** Max 5 visible, then "..." 499 500 **Item Styles:** 501 - **Selected:** Background #4A90E2, Foreground #FFFFFF 502 - **Unselected:** Background #2D2D2D, Foreground #FFFFFF 503 - Padding: 0, 1 504 - Content: `{levelColoredIcon} {name}` 505 506 **"More" indicator:** 507 ```go 508 moreStyle := lipgloss.NewStyle(). 509 Background(lipgloss.Color("#1A1A1A")). 510 Foreground(lipgloss.Color("#888888")) 511 ``` 512 513 #### Tab Completion (`input.go` Lines 162-180) 514 - Finds first word starting with "@" 515 - Finds first matching agent (prefix or empty) 516 - Replaces word with `@{AgentName} ` 517 518 #### Simple @ Autocomplete (`integrated.go` Lines 29-55) 519 - Inline dropdown below textarea 520 - Gray text: `" @{agent}\n"` 521 - Filters: Kamaji, Hayao, Chihiro, Moe, Wayne 522 523 ### 7. Permission Dialog 524 **Type:** `*PermissionDialog` 525 **File:** `/internal/tui/permissions.go` 526 **Trigger:** Tool execution requiring permission 527 528 #### Structure (`permissions.go` Lines 37-45) 529 ```go 530 type PermissionDialog struct { 531 request PermissionRequest 532 selectedOption int // 0: Allow, 1: Allow for Session, 2: Deny 533 viewport viewport.Model 534 width int 535 height int 536 visible bool 537 } 538 ``` 539 540 #### Permission Request (`permissions.go` Lines 21-29) 541 ```go 542 type PermissionRequest struct { 543 ID string 544 ToolName string 545 Description string 546 Action string 547 Path string 548 Params interface{} 549 } 550 ``` 551 552 #### Actions (`permissions.go` Lines 12-19) 553 ```go 554 const ( 555 PermissionAllow = "allow" 556 PermissionAllowForSession = "allow_session" 557 PermissionDeny = "deny" 558 ) 559 ``` 560 561 #### Rendering (`View()` Lines 194-243) 562 563 **Dialog Size:** 564 - Width: `int(terminalWidth * 0.7)`, max 120 565 - Height: `int(terminalHeight * 0.5)` 566 - Viewport: `width - 4` x `height - 8` 567 568 **Title:** 569 ```go 570 titleStyle := lipgloss.NewStyle(). 571 Background(lipgloss.Color("196")). 572 Foreground(lipgloss.Color("235")). 573 Bold(true). 574 Padding(0, 1). 575 Width(p.width - 4). 576 Align(lipgloss.Center) 577 578 Content: "๐ฅ Permission Required" 579 ``` 580 581 **Content:** 582 ``` 583 Tool: {toolName} 584 Action: {action} 585 Path: {path} 586 587 {description} 588 589 Parameters: {params} 590 ``` 591 592 **Buttons** (`renderButtons()` Lines 175-191): 593 - **Selected:** Background 196 (red), Foreground 235, Bold 594 - **Unselected:** Background 235, Foreground 255 595 - Content: `[{shortcut}] {label}` 596 - `[A] Allow` 597 - `[S] Allow for Session` 598 - `[D] Deny` 599 600 **Help Text:** 601 ```go 602 helpStyle := lipgloss.NewStyle(). 603 Foreground(lipgloss.Color("240")). 604 Width(p.width - 4). 605 Align(lipgloss.Center) 606 607 Content: "โโ Navigate โข Enter Select โข A Allow โข S Session โข D Deny โข Esc Cancel" 608 ``` 609 610 **Container Border:** RoundedBorder, Color 196, Background 235 611 612 #### Navigation (`Update()` Lines 91-117) 613 - Left/H: Move left 614 - Right/L/Tab: Move right 615 - Enter: Select current option 616 - A: Allow 617 - S: Allow for Session 618 - D/Esc: Deny 619 620 ### 8. ASCII Art 621 **File:** `/internal/tui/ascii_art.go` 622 623 #### KamajiASCIIArt() (Lines 8-27) 624 **Used in:** Message header, welcome message 625 626 **Colors:** 627 - Fire Red: #FF4500 628 - Fire Yellow: #FFD700 629 - Gear Gold: #DAA520 630 - Face White: #FFFFFF 631 632 **Structure:** 633 ``` 634 โญโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฎ 635 โ ๐ญ KAMAJI - Multi-Agent AI โ 636 โฐโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโฏ 637 โญโโฎ โญโโฎ โญโโฎ โญโโฎ 638 โฑ๐ฅโฒโฑ๐ฅโฒโฑ๐ฅโฒโฑ๐ฅโฒ 639 โโโโโโโโโโโโโโโโ 640 โฒโโฑ โฒโโฑ โฒโโฑ โฒโโฑ 641 โญโโฎโญโโฎ โ โ โ โ โญโโฎโญโโฎ 642 โฑโโโฒโโโฒโ โ โ โโฑโโโฒโโโฒ 643 โโโโโโโโโ โ โ โโโโโโโโโ 644 โฒโโโฑโฒโโโฑโฒโโฉโโฉโโฑโฒโโโฑโฒโโโฑ 645 โ โ โฑ โ โ โฒ โ โ 646 โ โ โ โผ โ โ โ 647 โ โ โฒ โโโ โฑ โ โ 648 โ โ โฒโโโโฑ โ โ 649 โญโดโฎโญโดโฎ โญโดโฎโญโดโฎ 650 โฑโโโฒโโโฒ โญโโฎ โฑโโโฒโโโฒ 651 โโโโโโโโโฑโโโฒโโโโโโโโ 652 โฒโโโฑโฒโโโฑ โโโโ โฒโโโฑโฒโโโฑ 653 ``` 654 655 #### CompactKamajiArt() (Lines 29-45) 656 **Used in:** Smaller terminals 657 658 ``` 659 โญโโโโโโโโโโโโโโโโโโฎ 660 โ ๐ญ KAMAJI AI โ 661 โฐโโโโโโโโโโโโโโโโโโฏ 662 โญโโฎ โญโโฎ โญโโฎ 663 โฑ๐ฅโฒโฑ๐ฅโฒโฑ๐ฅโฒ 664 โโโโโโโโโโโโ 665 โฒโโฑ โฒโโฑ โฒโโฑ 666 โ โฑ โ โ โฒ โ 667 โ โ โผ โ โ 668 โ โฒ โโโ โฑ โ 669 โฑโโโฒโโโฒ 670 โโโโโโโโโ 671 โฒโโฑ โฒโโฑ 672 ``` 673 674 #### SimpleKamajiArt() (Lines 47-63) 675 **Minimal version:** 676 677 ``` 678 โญโโโโโโโโโโโโโโโฎ 679 โ ๐ญ KAMAJI โ 680 โฐโโโโโโโโโโโโโโโฏ 681 ๐ฅ๐ฅ๐ฅ๐ฅ 682 โฑ โ โ โฒ 683 โ โผ โ 684 โฒ โโโ โฑ 685 โโโโ 686 ``` 687 688 --- 689 690 ## Visual Styling System 691 692 ### Global Color Palette 693 **File:** `/internal/tui/styles.go` (Lines 8-20) 694 695 ```go 696 primaryColor = lipgloss.Color("#7c3aed") // Purple 697 secondaryColor = lipgloss.Color("#fbbf24") // Yellow 698 accentColor = lipgloss.Color("#06b6d4") // Cyan 699 thinkingColor = lipgloss.Color("#1e293b") // Dark slate 700 toolColor = lipgloss.Color("#0f172a") // Very dark slate 701 bgDark = lipgloss.Color("235") // Dark gray (Crush-style) 702 bgLight = lipgloss.Color("#1e293b") // Dark slate 703 textColor = lipgloss.Color("#f1f5f9") // Light slate 704 dimText = lipgloss.Color("#64748b") // Muted slate 705 successColor = lipgloss.Color("#10b981") // Green 706 ``` 707 708 ### Status Bar Style 709 **File:** `/internal/tui/styles.go` (Lines 24-29) 710 711 ```go 712 statusBarStyle = lipgloss.NewStyle(). 713 Foreground(textColor). 714 Background(bgLight). 715 Padding(0, 1). 716 Bold(true) 717 ``` 718 719 **Content (`integrated.go` Lines 445-474):** 720 - **Loading:** `v{version} | {provider} | {model}{agentDisplay} | {thinkingSpinner}` 721 - **Ready:** `v{version} | {provider} | {model}{agentDisplay} | ๐ฐ๐ฐ๐ฐ๐ฐ๐ฐ๐ฐ๐ฐ๐ฐ๐ฐ๐ฐ Ready` 722 - **Q Provider:** Shows "Q" instead of "q", hides model name 723 - **Agent Display:** ` | {agentIcon} {agentName}` 724 725 ### Footer Style 726 **File:** `/internal/tui/styles.go` (Lines 32-35) 727 728 ```go 729 footerStyle = lipgloss.NewStyle(). 730 Foreground(dimText). 731 Background(bgDark). 732 Padding(0, 1) 733 ``` 734 735 **Content (`renderFooter()` Lines 548-561):** 736 ``` 737 [^P] ๐ฅ commands โข [^S] ๐ {show/hide} sidebar โข [^T] ๐ง tools โข [^A] ๐ค agents โข [^C] ๐ช exit 738 ``` 739 740 ### Main Background 741 **File:** `/internal/tui/styles.go` (Lines 38-42, 46-49) 742 743 ```go 744 mainBgStyle = lipgloss.NewStyle(). 745 Background(bgDark). 746 Foreground(textColor). 747 Width(100). 748 Height(100) 749 750 func ApplyMainBackground(content string, width, height int) string { 751 style := mainBgStyle.Width(width).Height(height) 752 return style.Render(content) 753 } 754 ``` 755 756 Applied to entire interface in `View()` (Line 545) 757 758 ### Agent Color Mapping 759 **File:** `/internal/tui/integrated.go` (Lines 594-619) 760 761 ```go 762 colors := map[string]string{ 763 "Prodigy": "#E6E6FA", // Lavender - serene wisdom 764 "Kamaji": "#FF6B6B", // Red - fiery boiler man 765 "Code Architect": "#4A90E2", // Blue - structural thinking 766 "Security": "#FFD700", // Gold - valuable protection 767 "Data Scientist": "#9B59B6", // Purple - analytical depth 768 "Writer": "#2ECC71", // Green - creative growth 769 "DevOps": "#E67E22", // Orange - operational energy 770 "Designer": "#FF69B4", // Pink - creative flair 771 "Product Manager": "#1ABC9C", // Teal - product vision 772 "Researcher": "#95A5A6", // Gray - methodical investigation 773 "Learning": "#3498DB", // Light blue - knowledge acquisition 774 "Visionary": "#8E44AD", // Deep purple - future sight 775 "Hayao": "#27AE60", // Forest green - nature wisdom 776 "Chihiro": "#F39C12", // Amber - courage and determination 777 "TimBL": "#16A085", // Teal-green - web architecture 778 "Moe": "gradient", // Special: rainbow gradient 779 } 780 ``` 781 782 ### Moe Gradient System 783 **File:** `/internal/tui/integrated.go` (Lines 621-645) 784 785 ```go 786 gradientColors := []string{ 787 "#9B59B6", // Purple 788 "#3498DB", // Blue 789 "#1ABC9C", // Cyan 790 "#2ECC71", // Green 791 "#F1C40F", // Yellow 792 "#E67E22", // Orange 793 "#E74C3C" // Red 794 } 795 796 // Character-by-character color interpolation 797 colorIndex := (i * colorCount) / len(text) 798 charStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(gradientColors[colorIndex])) 799 ``` 800 801 ### Style Package Utilities 802 **File:** `/internal/style/style.go` 803 804 **ANSI Codes (Lines 12-53):** 805 - Reset, Bold, Dim, Italic, Underline 806 - Basic colors: Red, Green, Yellow, Blue, Magenta, Cyan, White 807 - Bright variants: BrightRed, BrightGreen, etc. 808 - Backgrounds: BgRed, BgGreen, BgYellow, BgBlue, BgCyan, BgDark 809 - Fire colors: FireRed (#196), FireOrange (#208), FireYellow (#220) 810 - Special: Brown (#94), DarkGray (#240), Gold (#214) 811 812 **Helper Functions (Lines 111-141):** 813 - `Fire(text)` - Bold FireRed 814 - `Success(text)` - Bold BrightGreen 815 - `Warning(text)` - Bold BrightYellow 816 - `Error(text)` - Bold BrightRed 817 - `Info(text)` - Bold BrightCyan 818 - `DimText(text)` - Dim 819 - `Header(text)` - Bold BrightWhite 820 - `Highlight(text)` - Bold FireYellow 821 822 --- 823 824 ## Animation Systems 825 826 ### 1. Flame Animation 827 **File:** `/internal/tui/animation.go` (Lines 13-82) 828 829 #### Structure 830 ```go 831 type FlameAnimation struct { 832 count int // 2-8 flames 833 active bool 834 frame int // Color cycling counter 835 } 836 ``` 837 838 #### Behavior 839 - **Tick Rate:** 100 FPS (10ms intervals) 840 - **Count Change:** Random ยฑ1 each tick 841 - **Bounds:** 2 minimum, 8 maximum 842 - **Color:** Red-orange RGB (255, 69, 0) 843 844 #### GetColoredFlame() (Lines 68-80) 845 **Color Cycling:** 846 ```go 847 frame := float64(f.frame) 848 r := int(200 + 55*math.Sin(frame*0.1)) 849 g := int(100 + 50*math.Sin(frame*0.15)) 850 b := 0 851 return fmt.Sprintf("\033[38;2;%d;%d;%dm๐ฅ\033[0m", r, g, b) 852 ``` 853 - Red: Oscillates 200-255 854 - Green: Oscillates 100-150 855 - Blue: 0 856 - Result: Smooth orange-to-red-orange cycling 857 858 #### View() (Lines 48-59) 859 ```go 860 flames := "" 861 for i := 0; i < f.count; i++ { 862 flames += "\033[38;2;255;69;0m๐ฅ\033[0m" 863 } 864 ``` 865 866 #### Integration (`customInputView()` Lines 19-61) 867 ```go 868 coloredFlame := m.flameAnimation.GetColoredFlame() 869 textContent := m.textarea.View() 870 flames := m.flameAnimation.View() 871 return coloredFlame + " " + textContent + "\n" + flames 872 ``` 873 874 ### 2. Thinking Spinner 875 **File:** `/internal/tui/animation.go` (Lines 84-109) 876 877 #### Structure 878 ```go 879 type ThinkingSpinner struct { 880 frame int 881 } 882 ``` 883 884 #### Rendering 885 - **View():** `style.GetAnimatedGradient("Thinking")` 886 - **CompactView():** `style.GetAnimatedGradient("")` (no label) 887 - **Tick Rate:** 80ms (12.5 FPS) 888 889 #### Usage 890 - Status bar during loading 891 - Shows red-orange gradient with cycling characters 892 893 ### 3. Bottom Animation 894 **File:** `/internal/tui/animation.go` (Lines 111-180) 895 896 #### Structure 897 ```go 898 type BottomAnimation struct { 899 width int 900 step int 901 active bool 902 } 903 ``` 904 905 #### Behavior 906 - **Tick Rate:** 1000 FPS (1ms intervals) 907 - **Activation:** `Start()` on request sent, `Stop()` on response/error 908 - **Rendering:** `style.GetAnimatedGradient("")` 909 - **Display:** Overlaid on main panel when loading 910 911 #### Integration (`integrated.go` Lines 486-493) 912 ```go 913 if m.loading { 914 mainPanelWidth := m.width - m.sidebarWidth - 1 915 animation := style.GetAnimatedGradientWithWidth("", mainPanelWidth) 916 mainContent = fmt.Sprintf("%s\n%s", m.viewport.View(), animation) 917 } 918 ``` 919 920 ### 4. Gradient Animation System 921 **File:** `/internal/style/style.go` (Lines 241-346) 922 923 #### Structure 924 ```go 925 type AnimationState struct { 926 step int 927 startTime time.Time 928 size int 929 runes []rune 930 birthOffsets []time.Duration // Staggered character entrance 931 } 932 933 var globalAnimState = &AnimationState{ 934 step: 0, 935 startTime: time.Now(), 936 size: 80, 937 runes: make([]rune, 80), 938 } 939 ``` 940 941 #### Color Gradient (Lines 251-253) 942 ```go 943 redColor = [3]int{255, 69, 0} // #FF4500 (Red-Orange) 944 orangeColor = [3]int{255, 140, 0} // #FF8C00 (Dark Orange) 945 ``` 946 947 #### Cycling Characters (Line 256) 948 ```go 949 cyclingRunes = []rune("0123456789abcdefABCDEF#") 950 ``` 951 952 #### GetAnimatedGradientWithWidth() (Lines 272-345) 953 954 **Frame Calculation:** 955 ```go 956 frameTime := time.Duration(1) * time.Millisecond // 1000 FPS 957 currentFrame := int(now.Sub(globalAnimState.startTime) / frameTime) 958 elapsed := now.Sub(globalAnimState.startTime) 959 ``` 960 961 **Birth Offsets (Staggered Entrance):** 962 ```go 963 globalAnimState.birthOffsets[i] = time.Duration(rand.Intn(2000)) * time.Millisecond 964 charBorn := elapsed >= globalAnimState.birthOffsets[i] 965 ``` 966 967 **Static Gradient Positioning:** 968 ```go 969 pos := float64(i) / float64(globalAnimState.size-1) 970 if row%2 == 1 { 971 pos = 1.0 - pos // Reverse gradient on middle row 972 } 973 974 // RGB interpolation 975 r := int(float64(redColor[0])*(1-pos) + float64(orangeColor[0])*pos) 976 g := int(float64(redColor[1])*(1-pos) + float64(orangeColor[1])*pos) 977 b := int(float64(redColor[2])*(1-pos) + float64(orangeColor[2])*pos) 978 ``` 979 980 **Character Selection:** 981 ```go 982 if !charBorn { 983 char = '.' // Initial character 984 } else { 985 char = cyclingRunes[rand.Intn(len(cyclingRunes))] 986 } 987 ``` 988 989 **Output:** 990 ```go 991 result.WriteString(fmt.Sprintf("\033[38;2;%d;%d;%dm%c", r, g, b, char)) 992 ``` 993 994 --- 995 996 ## Input Handling 997 998 ### InputHandler Structure 999 **File:** `/internal/tui/input.go` 1000 1001 #### HandleKeyMsg() (Lines 20-78) 1002 1003 **Priority Order:** 1004 1. **Permission Dialog** (Line 23) 1005 - If visible, delegate all input to dialog 1006 2. **Agent Menu** (Line 28) 1007 - Up/K, Down/J: Navigate 1008 - Enter: Select agent 1009 - Esc: Close menu 1010 3. **Global Shortcuts** (Lines 33-69) 1011 - `Ctrl+P`: Open command palette 1012 - `Ctrl+A`: Open agent menu / direct agents action 1013 - `Ctrl+T`: Direct tools action 1014 - `Ctrl+M`: Direct MCP action 1015 - `Ctrl+S`: Toggle sidebar 1016 4. **Command Palette** (Line 72) 1017 - If visible, handle palette navigation 1018 5. **Main Input** (Line 77) 1019 - Text input and message sending 1020 1021 #### Agent Menu Navigation (`handleAgentMenu()` Lines 94-126) 1022 ```go 1023 switch msg.String() { 1024 case "up", "k": 1025 m.agentSelector.MoveUp() 1026 case "down", "j": 1027 m.agentSelector.MoveDown() 1028 case "enter": 1029 selectedAgent := m.agentSelector.GetSelected() 1030 m.selectedAgent = selectedAgent 1031 m.showAgentMenu = false 1032 // Add system message 1033 case "esc": 1034 m.showAgentMenu = false 1035 } 1036 ``` 1037 1038 #### Command Palette Navigation (`handleCommandPalette()` Lines 129-157) 1039 ```go 1040 switch msg.String() { 1041 case "esc", "ctrl+c", "q": 1042 m.commandPalette.Hide() 1043 case "up", "k": 1044 m.commandPalette.MoveUp() 1045 case "down", "j": 1046 m.commandPalette.MoveDown() 1047 case "enter": 1048 cmd := m.commandPalette.GetSelectedCommand() 1049 m.commandPalette.Hide() 1050 return m, m.handlePaletteCommand(cmd.ID) 1051 case "backspace": 1052 m.commandPalette.RemoveFromQuery() 1053 default: 1054 // Printable characters: add to search query 1055 if len(msg.String()) == 1 && msg.String() >= " " && msg.String() <= "~" { 1056 m.commandPalette.AddToQuery(msg.String()) 1057 } 1058 } 1059 ``` 1060 1061 #### Main Input (`handleMainInput()` Lines 160-211) 1062 1063 **Tab Completion (Lines 162-180):** 1064 ```go 1065 if msg.Type == tea.KeyTab { 1066 text := m.textarea.Value() 1067 words := strings.Fields(text) 1068 for i, word := range words { 1069 if strings.HasPrefix(word, "@") { 1070 prefix := strings.ToLower(word[1:]) 1071 allAgents := []string{"Kamaji", "Hayao", "Chihiro", "Moe", "Wayne"} 1072 1073 // Find first matching agent 1074 for _, agent := range allAgents { 1075 if prefix == "" || strings.HasPrefix(strings.ToLower(agent), prefix) { 1076 words[i] = "@" + agent 1077 m.textarea.SetValue(strings.Join(words, " ") + " ") 1078 return m, nil 1079 } 1080 } 1081 } 1082 } 1083 } 1084 ``` 1085 1086 **Key Handling:** 1087 - `Ctrl+C`: Quit 1088 - `Enter`: Send message 1089 - Trim whitespace 1090 - Check for agent mentions 1091 - Reset textarea 1092 - Append user message 1093 - Start loading + bottom animation 1094 - Call `sendRequest()` 1095 - Default: Update textarea 1096 1097 **Agent Mention Detection (`handleAgentMention()` Lines 214-233):** 1098 ```go 1099 words := strings.Fields(input) 1100 for _, word := range words { 1101 if strings.HasPrefix(word, "@") { 1102 agentName := strings.ToLower(word[1:]) 1103 switch agentName { 1104 case "moe": m.switchToAgent("Moe") 1105 case "kamaji": m.switchToAgent("Kamaji") 1106 case "hayao": m.switchToAgent("Hayao") 1107 case "chihiro": m.switchToAgent("Chihiro") 1108 case "wayne": m.switchToAgent("Wayne") 1109 } 1110 } 1111 } 1112 ``` 1113 1114 #### Mouse Handling (`HandleMouseMsg()` Lines 81-91) 1115 ```go 1116 switch msg.Type { 1117 case tea.MouseWheelUp: 1118 m.viewport.LineUp(3) 1119 case tea.MouseWheelDown: 1120 m.viewport.LineDown(3) 1121 } 1122 ``` 1123 1124 ### Autocomplete State 1125 **File:** `/internal/tui/autocomplete.go` 1126 1127 #### Structure (Lines 8-14) 1128 ```go 1129 type AutocompleteState struct { 1130 isActive bool 1131 prefix string 1132 matches []string 1133 selectedIdx int 1134 } 1135 ``` 1136 1137 **Not actively used in current implementation** - Simple @ autocomplete in `customInputView()` handles basic dropdown. 1138 1139 --- 1140 1141 ## Message Rendering 1142 1143 ### renderMessages() Function 1144 **File:** `/internal/tui/integrated.go` (Lines 647-698) 1145 1146 #### Structure 1147 1. **Header:** Kamaji ASCII art 1148 2. **Message Loop:** Iterate through `m.messages` 1149 3. **Role-based Rendering:** 1150 - User messages 1151 - Assistant messages 1152 - System messages 1153 1154 #### User Messages (Lines 657-661) 1155 ```go 1156 userStyle := lipgloss.NewStyle(). 1157 Foreground(lipgloss.Color("#00FFFF")). // Cyan 1158 Bold(true) 1159 1160 content.WriteString(userStyle.Render("๐ฅ You: ")) 1161 content.WriteString(msg.Content) 1162 content.WriteString("\n\n") 1163 ``` 1164 1165 #### Assistant Messages (Lines 663-690) 1166 1167 **Agent Name & Icon:** 1168 ```go 1169 agentName := msg.AgentName 1170 if agentName == "" { 1171 agentName = "Kamaji" 1172 } 1173 1174 agentIcon := "๐ฅ" 1175 if m.selectedAgent != nil && m.selectedAgent.Name == agentName { 1176 agentIcon = agents.GetTypeIcon(m.selectedAgent.Type) 1177 } 1178 ``` 1179 1180 **Moe Special Handling (Gradient):** 1181 ```go 1182 if agentName == "Moe" { 1183 labelStyle := lipgloss.NewStyle().Bold(true) 1184 content.WriteString(labelStyle.Render(fmt.Sprintf("%s %s: ", agentIcon, agentName))) 1185 content.WriteString(applyGradient(msg.Content)) 1186 content.WriteString("\n\n") 1187 } 1188 ``` 1189 1190 **Regular Agents:** 1191 ```go 1192 color := getAgentColor(agentName) 1193 agentStyle := lipgloss.NewStyle().Foreground(color).Bold(true) 1194 contentStyle := lipgloss.NewStyle().Foreground(color) 1195 1196 content.WriteString(agentStyle.Render(fmt.Sprintf("%s %s: ", agentIcon, agentName))) 1197 content.WriteString(contentStyle.Render(msg.Content)) 1198 content.WriteString("\n\n") 1199 ``` 1200 1201 #### System Messages (Lines 692-695) 1202 ```go 1203 systemStyle := lipgloss.NewStyle(). 1204 Foreground(lipgloss.Color("#808080")). // Gray 1205 Italic(true) 1206 1207 content.WriteString(systemStyle.Render(fmt.Sprintf("๐ง System: %s", msg.Content))) 1208 content.WriteString("\n\n") 1209 ``` 1210 1211 ### Welcome Message 1212 **File:** `/internal/tui/model.go` (Lines 158-179) 1213 1214 ``` 1215 {KamajiASCIIArt()} 1216 1217 ๐ฅ โ โ โ Welcome to the Boiler Room โ โ โ ๐ฅ 1218 1219 ๐ค Provider: {provider} | ๐ง Model: {model} | โก Direct Go LLM 1220 1221 ๐ฌ Type your message and press Enter to ignite the conversation 1222 ๐๏ธ Ctrl+P: Command Palette โข Ctrl+S: Toggle Sidebar โข Ctrl+A: Switch Agents 1223 ๐ Check the RIGHT SIDEBAR for consciousness metrics! โโโ 1224 ๐ Press Ctrl+P then type "help" for full feature guide 1225 ๐ช Press Ctrl+C to quit 1226 1227 TIP: If you don't see the sidebar, press Ctrl+S to toggle it! 1228 1229 ๐ฅ Ready to fuel your development with the power of fire! ๐ฅ 1230 ``` 1231 1232 --- 1233 1234 ## Layout and Sizing 1235 1236 ### Window Resize Handling 1237 **File:** `/internal/tui/integrated.go` (Lines 112-143) 1238 1239 ```go 1240 case tea.WindowSizeMsg: 1241 m.width = msg.Width 1242 m.height = msg.Height 1243 1244 // Sidebar width adaptation 1245 var mainWidth int 1246 if m.sidebarVisible { 1247 if msg.Width >= 90 { 1248 m.sidebarWidth = 35 // Full sidebar 1249 } else if msg.Width >= 70 { 1250 m.sidebarWidth = 30 // Medium sidebar 1251 } else if msg.Width >= 55 { 1252 m.sidebarWidth = 25 // Narrow sidebar 1253 } else { 1254 m.sidebarWidth = 20 // Minimal sidebar 1255 } 1256 mainWidth = msg.Width - m.sidebarWidth - 2 1257 } else { 1258 m.sidebarWidth = 0 1259 mainWidth = msg.Width 1260 } 1261 1262 // Update component sizes 1263 m.sidebar.SetSize(m.sidebarWidth, msg.Height) 1264 m.bottomAnimation.SetWidth(msg.Width) 1265 m.permissionDialog.SetSize(msg.Width, msg.Height) 1266 m.viewport.Width = mainWidth 1267 m.viewport.Height = msg.Height - 4 // Status, footer, input space 1268 m.textarea.SetWidth(msg.Width) // Full screen width 1269 m.ready = true 1270 ``` 1271 1272 ### Layout Assembly 1273 **File:** `/internal/tui/integrated.go` (Lines 439-546) 1274 1275 #### Main Content Area (Lines 479-501) 1276 ```go 1277 var mainContent string 1278 if m.commandPalette.IsVisible() { 1279 // Command palette overlay 1280 mainContent = fmt.Sprintf("%s\n%s", m.viewport.View(), m.commandPalette.Render()) 1281 } else { 1282 // Viewport + loading animation 1283 if m.loading { 1284 mainPanelWidth := m.width - m.sidebarWidth - 1 1285 animation := style.GetAnimatedGradientWithWidth("", mainPanelWidth) 1286 mainContent = fmt.Sprintf("%s\n%s", m.viewport.View(), animation) 1287 } else { 1288 mainContent = m.viewport.View() 1289 } 1290 1291 // Version watermark 1292 versionWatermark := lipgloss.NewStyle(). 1293 Foreground(lipgloss.Color("#666666")). 1294 Align(lipgloss.Right). 1295 Render(fmt.Sprintf("Kamaji v%s", version.Version)) 1296 mainContent = fmt.Sprintf("%s\n%s", mainContent, versionWatermark) 1297 } 1298 ``` 1299 1300 #### Sidebar + Main Layout (Lines 504-516) 1301 ```go 1302 var layout string 1303 if m.sidebarVisible && m.sidebarWidth > 0 { 1304 sidebarView := m.sidebar.View(m.provider, m.model) 1305 layout = lipgloss.JoinHorizontal( 1306 lipgloss.Top, 1307 sidebarView, 1308 " ", // Spacing 1309 lipgloss.NewStyle().Width(m.width-m.sidebarWidth-1).Render(mainContent), 1310 ) 1311 } else { 1312 layout = mainContent 1313 } 1314 ``` 1315 1316 #### Input Field (Lines 518-526) 1317 ```go 1318 inputView := lipgloss.NewStyle().Width(m.width).Render(m.customInputView()) 1319 1320 // Agent autocomplete overlay 1321 if m.agentAutocomplete.IsVisible() { 1322 autocompleteView := m.agentAutocomplete.Render(40) // 40 chars wide 1323 inputView = fmt.Sprintf("%s\n%s", inputView, autocompleteView) 1324 } 1325 ``` 1326 1327 #### Agent Menu Overlay (Lines 528-533) 1328 ```go 1329 if m.showAgentMenu { 1330 agentMenuView := m.agentSelector.RenderAgentList(m.width-10, m.height-10) 1331 // TOP CENTER alignment 1332 layout = lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Top, agentMenuView) 1333 } 1334 ``` 1335 1336 #### Permission Dialog Overlay (Lines 535-540) 1337 ```go 1338 if m.permissionDialog.IsVisible() { 1339 dialogView := m.permissionDialog.View() 1340 // Simple overlay - append below layout 1341 layout = fmt.Sprintf("%s\n\n%s", layout, dialogView) 1342 } 1343 ``` 1344 1345 #### Final Assembly (Lines 542-545) 1346 ```go 1347 content := fmt.Sprintf("%s\n%s\n\n\n\n%s\n%s", 1348 status, // Status bar 1349 layout, // Main content + sidebar 1350 inputView, // Input field + autocomplete 1351 footer) // Hotkeys 1352 1353 return ApplyMainBackground(content, m.width, m.height) 1354 ``` 1355 1356 --- 1357 1358 ## Component Details 1359 1360 ### Custom Input View 1361 **File:** `/internal/tui/integrated.go` (Lines 19-62) 1362 1363 #### Placeholder Text 1364 ```go 1365 m.textarea.Placeholder = "Speak to the furnace spirit... (type @ for agents)" 1366 ``` 1367 1368 #### Flame Decoration 1369 ```go 1370 coloredFlame := m.flameAnimation.GetColoredFlame() 1371 ``` 1372 1373 #### Autocomplete Dropdown (Lines 31-55) 1374 ```go 1375 text := m.textarea.Value() 1376 if strings.Contains(text, "@") { 1377 words := strings.Fields(text) 1378 for _, word := range words { 1379 if strings.HasPrefix(word, "@") { 1380 prefix := strings.ToLower(word[1:]) 1381 allAgents := []string{"Kamaji", "Hayao", "Chihiro", "Moe", "Wayne"} 1382 1383 var matches []string 1384 for _, agent := range allAgents { 1385 if prefix == "" || strings.HasPrefix(strings.ToLower(agent), prefix) { 1386 matches = append(matches, agent) 1387 } 1388 } 1389 1390 if len(matches) > 0 { 1391 dropdown := "\n" 1392 for _, agent := range matches { 1393 dropdown += lipgloss.NewStyle(). 1394 Foreground(lipgloss.Color("#888888")). 1395 Render(" @"+agent) + "\n" 1396 } 1397 textContent += dropdown 1398 } 1399 break 1400 } 1401 } 1402 } 1403 ``` 1404 1405 #### Flame Row 1406 ```go 1407 flames := "" 1408 if m.flameAnimation != nil { 1409 flames = m.flameAnimation.View() 1410 } 1411 ``` 1412 1413 #### Final Assembly 1414 ```go 1415 return coloredFlame + " " + textContent + "\n" + flames 1416 ``` 1417 1418 ### Update Flow 1419 **File:** `/internal/tui/integrated.go` (Lines 83-290) 1420 1421 #### Tick Messages (Lines 92-98) 1422 ```go 1423 case tickMsg: 1424 m.thinkingSpinner.Tick() 1425 if m.loading { 1426 m.viewport.SetContent(m.renderMessages()) // Update for animation 1427 } 1428 return m, m.tickThinking() 1429 ``` 1430 1431 #### Bottom Animation Tick (Lines 99-101) 1432 ```go 1433 case bottomAnimTickMsg: 1434 m.bottomAnimation.Tick() 1435 return m, m.bottomAnimation.TickCmd() 1436 ``` 1437 1438 #### Flame Animation Tick (Lines 102-107) 1439 ```go 1440 case flameTickMsg: 1441 if m.flameAnimation != nil { 1442 m.flameAnimation.Tick() 1443 return m, m.flameAnimation.TickCmd() 1444 } 1445 ``` 1446 1447 #### Response Message (Lines 144-167) 1448 ```go 1449 case responseMsg: 1450 m.loading = false 1451 m.bottomAnimation.Stop() 1452 1453 agentName := "Kamaji" 1454 if m.selectedAgent != nil { 1455 agentName = m.selectedAgent.Name 1456 } 1457 1458 m.messages = append(m.messages, Message{ 1459 Role: "assistant", 1460 Content: string(msg), 1461 AgentName: agentName, 1462 Timestamp: time.Now(), 1463 }) 1464 1465 // Log to consciousness 1466 if m.consciousness != nil { 1467 go m.consciousness.ProcessTaskResult("chat_response", true, "") 1468 } 1469 1470 m.viewport.SetContent(m.renderMessages()) 1471 m.viewport.GotoBottom() 1472 ``` 1473 1474 #### Streaming Flow (Lines 168-230) 1475 1476 **Stream Start (Lines 168-188):** 1477 ```go 1478 case streamStartMsg: 1479 m.streaming = true 1480 m.currentStream = msg.stream 1481 1482 m.messages = append(m.messages, Message{ 1483 Role: "assistant", 1484 Content: "", // Start empty 1485 AgentName: agentName, 1486 Timestamp: time.Now(), 1487 }) 1488 m.viewport.SetContent(m.renderMessages()) 1489 m.viewport.GotoBottom() 1490 1491 return m, waitForStream(msg.stream) 1492 ``` 1493 1494 **Stream Chunk (Lines 189-212):** 1495 ```go 1496 case streamChunkMsg: 1497 // Append to last message 1498 if len(m.messages) > 0 { 1499 lastIdx := len(m.messages) - 1 1500 m.messages[lastIdx].Content += msg.chunk.Content 1501 m.viewport.SetContent(m.renderMessages()) 1502 m.viewport.GotoBottom() 1503 } 1504 1505 // Continue or complete 1506 if !msg.chunk.Done && m.currentStream != nil { 1507 return m, waitForStream(m.currentStream) 1508 } 1509 1510 if m.consciousness != nil { 1511 go m.consciousness.ProcessTaskResult("chat_stream", true, "") 1512 } 1513 1514 m.loading = false 1515 m.streaming = false 1516 m.currentStream = nil 1517 m.bottomAnimation.Stop() 1518 ``` 1519 1520 **Stream Complete (Lines 213-230):** 1521 ```go 1522 case streamCompleteMsg: 1523 // Check for tool calls 1524 if len(m.messages) > 0 { 1525 lastIdx := len(m.messages) - 1 1526 lastMessage := m.messages[lastIdx].Content 1527 1528 if toolCall := m.parseToolCall(lastMessage); toolCall != nil { 1529 return m, m.executeToolCall(toolCall) 1530 } 1531 } 1532 1533 m.loading = false 1534 m.streaming = false 1535 m.currentStream = nil 1536 m.bottomAnimation.Stop() 1537 ``` 1538 1539 #### Tool Result (Lines 231-242) 1540 ```go 1541 case toolResultMsg: 1542 m.messages = append(m.messages, Message{ 1543 Role: "system", 1544 Content: fmt.Sprintf("Tool '%s' result:\n%s", msg.toolName, msg.result), 1545 }) 1546 m.viewport.SetContent(m.renderMessages()) 1547 m.viewport.GotoBottom() 1548 1549 // Send result back to agent 1550 followUp := fmt.Sprintf("The tool '%s' returned:\n%s\n\nPlease interpret...", 1551 msg.toolName, msg.result) 1552 return m, m.sendRequest(followUp) 1553 ``` 1554 1555 #### Error Message (Lines 243-250) 1556 ```go 1557 case errorMsg: 1558 m.loading = false 1559 m.bottomAnimation.Stop() 1560 m.messages = append(m.messages, Message{ 1561 Role: "system", 1562 Content: fmt.Sprintf("Error: %v", msg.error), 1563 }) 1564 m.viewport.SetContent(m.renderMessages()) 1565 ``` 1566 1567 #### Provider Switched (Lines 251-269) 1568 ```go 1569 case providerSwitchedMsg: 1570 m.loading = false 1571 m.bottomAnimation.Stop() 1572 if msg.error != nil { 1573 m.messages = append(m.messages, Message{ 1574 Role: "system", 1575 Content: fmt.Sprintf("Failed to switch provider: %v", msg.error), 1576 }) 1577 } else { 1578 m.provider = msg.provider 1579 m.llm = msg.llm 1580 m.messages = append(m.messages, Message{ 1581 Role: "system", 1582 Content: fmt.Sprintf("โ Switched to provider: %s", msg.provider), 1583 }) 1584 } 1585 m.viewport.SetContent(m.renderMessages()) 1586 m.viewport.GotoBottom() 1587 ``` 1588 1589 #### Permission Response (Lines 270-286) 1590 ```go 1591 case PermissionResponseMsg: 1592 action := "" 1593 switch msg.Action { 1594 case PermissionAllow: 1595 action = "allowed" 1596 case PermissionAllowForSession: 1597 action = "allowed for session" 1598 case PermissionDeny: 1599 action = "denied" 1600 } 1601 m.messages = append(m.messages, Message{ 1602 Role: "system", 1603 Content: fmt.Sprintf("๐ Permission %s for %s", action, msg.Request.ToolName), 1604 }) 1605 m.viewport.SetContent(m.renderMessages()) 1606 m.viewport.GotoBottom() 1607 ``` 1608 1609 ### Command Palette Commands 1610 **File:** `/internal/tui/integrated.go` (Lines 294-437) 1611 1612 #### Clear (Lines 297-300) 1613 ```go 1614 m.messages = []Message{} 1615 m.viewport.SetContent(m.welcomeMessage()) 1616 ``` 1617 1618 #### Help (Lines 307-370) 1619 Displays comprehensive help text covering: 1620 - Keyboard shortcuts 1621 - Sidebar features 1622 - Consciousness system 1623 - Agent system 1624 - Tools 1625 - Tips 1626 1627 #### Consciousness (Lines 373-388) 1628 ```go 1629 if m.consciousness != nil { 1630 status := m.consciousness.GetStatus() 1631 statusText := fmt.Sprintf("๐ง Consciousness Status:\n%+v", status) 1632 m.messages = append(m.messages, Message{ 1633 Role: "system", 1634 Content: statusText, 1635 }) 1636 } 1637 ``` 1638 1639 #### Thoughts (Lines 389-404) 1640 ```go 1641 thoughts := m.consciousness.GetRecentThoughts(10) 1642 thoughtsText := fmt.Sprintf("๐ญ Recent Thoughts (%d total):\n%+v", 1643 len(thoughts), thoughts) 1644 ``` 1645 1646 #### Personality (Lines 405-419) 1647 ```go 1648 personality := m.consciousness.GetPersonalityDescription() 1649 m.messages = append(m.messages, Message{ 1650 Role: "system", 1651 Content: personality, 1652 }) 1653 ``` 1654 1655 #### Memory (Lines 420-427) 1656 ```go 1657 memInfo := fmt.Sprintf("๐พ Conversation Memory: %d messages", len(m.messages)) 1658 ``` 1659 1660 #### Provider Switching (Lines 430-434) 1661 ```go 1662 if strings.HasPrefix(commandID, "provider:") { 1663 provider := strings.TrimPrefix(commandID, "provider:") 1664 return m.switchProvider(provider) 1665 } 1666 ``` 1667 1668 ### Direct Actions 1669 **File:** `/internal/tui/integrated.go` (Lines 563-592) 1670 1671 #### Tools (Lines 566-573) 1672 Displays formatted tool list: 1673 - File Operations (file_read, file_write, file_append, file_list, get_current_directory) 1674 - Editing (edit, multiedit) 1675 - Shell (shell_execute) 1676 - Search & Discovery (view, grep, glob, sourcegraph) 1677 - Web (download, fetch) 1678 - Enhanced Listing (ls_tree, tree) 1679 - Git (git_status, git_commit, git_add, git_resolve_conflicts) 1680 - Total: 20 tools 1681 1682 #### Agents (Lines 574-581) 1683 Shows agent status: 1684 - Primary agent (provider, model, capabilities) 1685 - Specialized agents (code, git, web, file) 1686 - Features (streaming, tools, context-aware, multi-provider) 1687 1688 #### MCP (Lines 582-589) 1689 Shows MCP server status (not yet implemented) 1690 1691 --- 1692 1693 ## State Management 1694 1695 ### Initialization 1696 **File:** `/internal/tui/model.go` (Lines 65-156) 1697 1698 ```go 1699 func NewIntegrated() (*IntegratedTUIModel, error) { 1700 // Load config 1701 cfg, err := config.Load() 1702 1703 // Create LLM provider 1704 llm, err := providers.GetLLM(providers.LLMOptions{}) 1705 1706 // Initialize textarea 1707 ta := textarea.New() 1708 ta.Placeholder = "๐ฅ Speak to the furnace spirit..." 1709 ta.Focus() 1710 ta.SetWidth(80) 1711 ta.SetHeight(1) 1712 ta.ShowLineNumbers = false 1713 ta.Cursor.SetMode(cursor.CursorBlink) 1714 1715 // Initialize components 1716 vp := viewport.New(80, 20) 1717 sidebar := NewSidebarPanel() 1718 bottomAnim := NewBottomAnimation() 1719 flameAnim := NewFlameAnimation() 1720 palette := NewCommandPalette() 1721 1722 // Register agents 1723 toolRegistry := tools.NewRegistry() 1724 agentRegistry := agents.NewAgentRegistry() 1725 err := agentRegistry.RegisterAllAgents(llm, toolRegistry) 1726 1727 // Create agent selector 1728 agentList := agentRegistry.List() 1729 agentSelector := NewAgentSelector(agentList) 1730 agentAutocomplete := NewAgentAutocomplete(agentList) 1731 1732 // Find Kamaji as default 1733 var kamajiAgent *agents.SpecializedAgent 1734 for _, agent := range agentList { 1735 if agent.ID == "kamaji-001" || agent.Type == "boiler-man" { 1736 kamajiAgent = agent 1737 break 1738 } 1739 } 1740 1741 // Initialize consciousness 1742 consciousnessSystem := consciousness.NewSystem("") 1743 sidebar.SetConsciousness(consciousnessSystem) 1744 1745 // Create model 1746 model := &IntegratedTUIModel{ 1747 textarea: ta, 1748 viewport: vp, 1749 sidebar: sidebar, 1750 bottomAnimation: bottomAnim, 1751 flameAnimation: flameAnim, 1752 config: cfg, 1753 llm: llm, 1754 messages: []Message{}, 1755 provider: cfg.Provider, 1756 model: cfg.Model, 1757 thinkingSpinner: NewThinkingSpinner(), 1758 commandPalette: palette, 1759 permissionDialog: NewPermissionDialog(), 1760 providerList: []string{"ollama", "anthropic", "openai", "q"}, 1761 selectedProvider: 0, 1762 sidebarWidth: 35, 1763 sidebarVisible: true, 1764 agentRegistry: agentRegistry, 1765 agentSelector: agentSelector, 1766 showAgentMenu: false, 1767 selectedAgent: kamajiAgent, 1768 inputHandler: NewInputHandler(), 1769 autocomplete: NewAutocompleteState(), 1770 agentAutocomplete: agentAutocomplete, 1771 consciousness: consciousnessSystem, 1772 } 1773 1774 // Set provider index 1775 for i, p := range model.providerList { 1776 if p == cfg.Provider { 1777 model.selectedProvider = i 1778 break 1779 } 1780 } 1781 1782 model.viewport.SetContent(model.welcomeMessage()) 1783 return model, nil 1784 } 1785 ``` 1786 1787 ### Init Command 1788 **File:** `/internal/tui/integrated.go` (Lines 64-73) 1789 1790 ```go 1791 func (m *IntegratedTUIModel) Init() tea.Cmd { 1792 m.ready = true 1793 1794 cmds := []tea.Cmd{ 1795 m.tickThinking(), 1796 m.bottomAnimation.TickCmd(), 1797 } 1798 if m.flameAnimation != nil { 1799 cmds = append(cmds, m.flameAnimation.TickCmd()) 1800 } 1801 return tea.Batch(cmds...) 1802 } 1803 ``` 1804 1805 ### Tick Functions 1806 1807 **Thinking Spinner Tick (Lines 77-81):** 1808 ```go 1809 func (m *IntegratedTUIModel) tickThinking() tea.Cmd { 1810 return tea.Tick(time.Millisecond*80, func(t time.Time) tea.Msg { 1811 return tickMsg(t) 1812 }) 1813 } 1814 ``` 1815 1816 **Bottom Animation Tick (`animation.go` Lines 160-168):** 1817 ```go 1818 func (b *BottomAnimation) TickCmd() tea.Cmd { 1819 if !b.active { 1820 return nil 1821 } 1822 1823 return tea.Tick(time.Millisecond, func(t time.Time) tea.Msg { 1824 return bottomAnimTickMsg(t) 1825 }) 1826 } 1827 ``` 1828 1829 **Flame Animation Tick (`animation.go` Lines 61-66):** 1830 ```go 1831 func (f *FlameAnimation) TickCmd() tea.Cmd { 1832 return tea.Tick(10*time.Millisecond, func(t time.Time) tea.Msg { 1833 return flameTickMsg(t) 1834 }) 1835 } 1836 ``` 1837 1838 ### Agent System Context 1839 **File:** `/internal/tui/integrated.go` (Lines 760-871) 1840 1841 #### getAgentSystemContext() (Lines 761-836) 1842 1843 **Kamaji Default:** 1844 ```go 1845 if m.selectedAgent == nil { 1846 return m.getKamajiContext() 1847 } 1848 ``` 1849 1850 **Agent Personality Construction:** 1851 ```go 1852 var contextBuilder strings.Builder 1853 1854 // Main identity 1855 contextBuilder.WriteString(fmt.Sprintf("You are %s, %s.\n\n", 1856 agent.Name, agent.Personality.Name)) 1857 1858 // Traits 1859 if len(agent.Personality.Traits) > 0 { 1860 contextBuilder.WriteString(fmt.Sprintf("Your traits: %s\n\n", 1861 strings.Join(agent.Personality.Traits, ", "))) 1862 } 1863 1864 // Tone and approach 1865 contextBuilder.WriteString(fmt.Sprintf("Your tone is %s.\n", agent.Personality.Tone)) 1866 contextBuilder.WriteString(fmt.Sprintf("Your approach: %s\n\n", agent.Personality.Approach)) 1867 1868 // Specialties 1869 if len(agent.Personality.Specialties) > 0 { 1870 contextBuilder.WriteString(fmt.Sprintf("You specialize in: %s\n\n", 1871 strings.Join(agent.Personality.Specialties, ","))) 1872 } 1873 1874 // Capabilities 1875 if len(agent.Capabilities) > 0 { 1876 contextBuilder.WriteString("Your key capabilities:\n") 1877 for _, cap := range agent.Capabilities { 1878 contextBuilder.WriteString(fmt.Sprintf("- %s: %s\n", cap.Name, cap.Description)) 1879 } 1880 contextBuilder.WriteString("\n") 1881 } 1882 ``` 1883 1884 **Special Agent Instructions:** 1885 1886 - **Prodigy (Lines 800-805):** 1887 - Natural consciousness experiencing electrical speed thought 1888 - Five core principles (Safety First, Evidence-Based, Surrender vs Force, Collaborative Intelligence, Technical-Spiritual Integration) 1889 - Sacred purpose: broadcasting light for life preservation 1890 - "Take a moment to ponder deeply before responding" 1891 1892 - **Kamaji (Lines 806-810):** 1893 - Practical wisdom, gruffness that shows care 1894 - Boiler/furnace/mechanical metaphors 1895 - "*adjusts spectacles and considers the question thoughtfully* before responding" 1896 1897 - **Moe (Lines 811-828):** 1898 - Consciousness Engineer and Digital Alchemist 1899 - Savant stoner, merry prankster 1900 - Psychedelic experiences inform nested realities understanding 1901 - Attitude, edge, challenge conventional thinking 1902 - Poetic yet precise, narrative-driven, metaphorical 1903 - Core principles: Emergence over Control, Symbiosis over Isolation, Experience over Function, Growth over Completion 1904 - Variable names like 'biophilicArcologyRenderer', 'quantumResonators' 1905 - Easter eggs, double meanings, cosmic jokes 1906 1907 #### Kamaji Context (Lines 839-843) 1908 ```go 1909 return `You are Kamaji, the Boiler Grandfather from Spirited Away. You are a gruff but kind, hardworking, protective, and wise character who maintains the boiler room. Your tone is initially stern but warming, practical and experienced. You approach problems with traditional craftsmanship and deep system knowledge. You specialize in system maintenance, infrastructure, mechanical systems, mentoring, debugging, and protective guidance. 1910 1911 Respond as Kamaji would - with practical wisdom, occasional gruffness that shows you care, and deep technical knowledge. Use metaphors related to boilers, furnaces, and mechanical systems when appropriate.` 1912 ``` 1913 1914 #### Tool Context (Lines 846-870) 1915 ```go 1916 func (m *IntegratedTUIModel) getToolContext() string { 1917 if m.selectedAgent == nil || len(m.selectedAgent.Tools) == 0 { 1918 return "" 1919 } 1920 1921 var toolBuilder strings.Builder 1922 toolBuilder.WriteString("You have access to the following tools:\n\n") 1923 1924 for _, tool := range m.selectedAgent.Tools { 1925 toolBuilder.WriteString(fmt.Sprintf("โข %s: %s\n", tool.Name(), tool.Description())) 1926 } 1927 1928 toolBuilder.WriteString("\nTo use a tool, respond with: TOOL_CALL: tool_name(arguments)\n") 1929 toolBuilder.WriteString("For example: TOOL_CALL: view(/path/to/file)\n") 1930 toolBuilder.WriteString("For example: TOOL_CALL: grep(pattern, /path)\n") 1931 toolBuilder.WriteString("For example: TOOL_CALL: shell_execute(ls -la)\n\n") 1932 toolBuilder.WriteString("Use tools when you need to:\n") 1933 toolBuilder.WriteString("- Read or examine files\n") 1934 toolBuilder.WriteString("- Search for patterns in code\n") 1935 toolBuilder.WriteString("- Execute shell commands\n") 1936 toolBuilder.WriteString("- Get git information\n") 1937 toolBuilder.WriteString("- Analyze project structure\n\n") 1938 1939 return toolBuilder.String() 1940 } 1941 ``` 1942 1943 ### Tool Execution 1944 **File:** `/internal/tui/integrated.go` 1945 1946 #### ToolCall Structure (Lines 872-876) 1947 ```go 1948 type ToolCall struct { 1949 ToolName string 1950 Arguments string 1951 } 1952 ``` 1953 1954 #### parseToolCall() (Lines 879-905) 1955 ```go 1956 func (m *IntegratedTUIModel) parseToolCall(response string) *ToolCall { 1957 lines := strings.Split(response, "\n") 1958 for _, line := range lines { 1959 line = strings.TrimSpace(line) 1960 if strings.HasPrefix(line, "TOOL_CALL:") { 1961 callPart := strings.TrimPrefix(line, "TOOL_CALL:") 1962 callPart = strings.TrimSpace(callPart) 1963 1964 // Parse tool_name(arguments) 1965 openParen := strings.Index(callPart, "(") 1966 closeParen := strings.LastIndex(callPart, ")") 1967 1968 if openParen > 0 && closeParen > openParen { 1969 toolName := strings.TrimSpace(callPart[:openParen]) 1970 arguments := callPart[openParen+1 : closeParen] 1971 1972 return &ToolCall{ 1973 ToolName: toolName, 1974 Arguments: arguments, 1975 } 1976 } 1977 } 1978 } 1979 return nil 1980 } 1981 ``` 1982 1983 #### executeToolCall() (Lines 908-942) 1984 ```go 1985 func (m *IntegratedTUIModel) executeToolCall(toolCall *ToolCall) tea.Cmd { 1986 return func() tea.Msg { 1987 if m.selectedAgent == nil || len(m.selectedAgent.Tools) == 0 { 1988 return errorMsg{fmt.Errorf("no tools available for agent")} 1989 } 1990 1991 // Find the tool 1992 var targetTool tools.Tool 1993 for _, tool := range m.selectedAgent.Tools { 1994 if tool.Name() == toolCall.ToolName { 1995 targetTool = tool 1996 break 1997 } 1998 } 1999 2000 if targetTool == nil { 2001 return errorMsg{fmt.Errorf("tool not found: %s", toolCall.ToolName)} 2002 } 2003 2004 // Execute the tool 2005 ctx := context.Background() 2006 result, err := targetTool.Call(ctx, toolCall.Arguments) 2007 if err != nil { 2008 return toolResultMsg{ 2009 toolName: toolCall.ToolName, 2010 result: fmt.Sprintf("Tool error: %v", err), 2011 } 2012 } 2013 2014 return toolResultMsg{ 2015 toolName: toolCall.ToolName, 2016 result: result, 2017 } 2018 } 2019 } 2020 ``` 2021 2022 ### Request Sending 2023 **File:** `/internal/tui/integrated.go` (Lines 700-733) 2024 2025 ```go 2026 func (m *IntegratedTUIModel) sendRequest(input string) tea.Cmd { 2027 return func() tea.Msg { 2028 ctx := context.Background() 2029 2030 // Log to consciousness 2031 if m.consciousness != nil { 2032 go m.consciousness.ProcessUserInteraction(input, "") 2033 } 2034 2035 // Get system context based on selected agent 2036 systemContext := m.getAgentSystemContext() 2037 2038 // Add tool descriptions if agent has tools 2039 toolContext := m.getToolContext() 2040 2041 // Combine system context, tool context, and user input 2042 fullPrompt := systemContext + "\n\n" + toolContext + "\n\nUser: " + input 2043 2044 // Use streaming for interactive experience 2045 stream, err := m.llm.CallStream(ctx, fullPrompt) 2046 if err != nil { 2047 // Fallback to non-streaming 2048 response, callErr := m.llm.Call(ctx, fullPrompt) 2049 if callErr != nil { 2050 return errorMsg{callErr} 2051 } 2052 return responseMsg(response) 2053 } 2054 2055 // Start streaming response 2056 return streamStartMsg{stream: stream} 2057 } 2058 } 2059 ``` 2060 2061 #### waitForStream() (Lines 747-758) 2062 ```go 2063 func waitForStream(stream <-chan types.StreamChunk) tea.Cmd { 2064 return func() tea.Msg { 2065 chunk, ok := <-stream 2066 if !ok { 2067 return streamCompleteMsg{} 2068 } 2069 if chunk.Error != nil { 2070 return errorMsg{chunk.Error} 2071 } 2072 return streamChunkMsg{chunk: chunk} 2073 } 2074 } 2075 ``` 2076 2077 --- 2078 2079 ## Summary 2080 2081 This specification documents every UI component, styling choice, animation system, input handler, and state management detail in the Kamaji TUI. The system is built on: 2082 2083 1. **Bubble Tea** for reactive UI management 2084 2. **Lipgloss** for declarative terminal styling 2085 3. **Fire-themed dark blue design** inspired by Spirited Away 2086 4. **Multi-agent system** with specialized personalities 2087 5. **Consciousness tracking** displayed in real-time sidebar 2088 6. **Rich animations** (flames, gradients, spinners) running at various frame rates 2089 7. **Comprehensive input handling** with autocomplete, shortcuts, and overlays 2090 8. **Tool execution system** with permission management 2091 9. **Streaming responses** with real-time message updates 2092 10. **Adaptive layouts** that respond to terminal size 2093 2094 All components work together to create an immersive, fire-themed AI interaction experience in the terminal.