@@ -12,6 +12,7 @@ import (
1212 "strings"
1313 "time"
1414
15+ "github.com/atotto/clipboard"
1516 tea "github.com/charmbracelet/bubbletea"
1617 "github.com/charmbracelet/lipgloss"
1718 "github.com/user/terminal-intelligence/internal/ai"
@@ -45,46 +46,47 @@ import (
4546// - DisplayNotification: Shows fix results with distinct styling
4647// - GetLastAssistantResponse: Retrieves last AI response for insertion
4748type AIChatPane struct {
48- messages []types.ChatMessage // Conversation history
49- inputBuffer string // Current input being typed
50- aiClient ai.AIClient // AI service client
51- model string // AI model to use
52- provider string // "ollama" or "gemini"
53- scrollOffset int // Vertical scroll offset for responses
54- width int // Pane width
55- height int // Pane height
56- focused bool // Whether this pane is focused
57- streaming bool // Whether AI is currently generating
58- copyMode bool // Whether in code block selection mode
59- viewMode bool // Whether viewing a code block
60- codeBlocks []string // Extracted code blocks from responses
61- selectedBlock int // Currently selected code block index
62- lastSelectedCode string // Last selected code block content
63- lastKeyPressed string // Last key pressed (for debugging)
64- activeArea int // 0: Input, 1: Response
65- viewModeScroll int // Scroll offset for code block view mode
66- viewModeScrollX int // Horizontal scroll offset for code block view mode
67- configMode bool // Whether in config editor mode
68- configFields []string // Config field names
69- configValues []string // Config field values
70- selectedField int // Currently selected config field
71- editingField bool // Whether currently editing a field
72- editBuffer string // Buffer for editing field value
73- editCursorPos int // Cursor position within edit buffer
74- terminalMode bool // Whether terminal execution is active
75- cmdRunning bool // Whether the command is currently running
76- terminalOutput []string // Output lines from terminal execution
77- stdinWriter io.WriteCloser // Stdin pipe for sending input to running command
78- terminalInput string // Current input line being typed in terminal mode
79- aiAvailable bool // Whether the AI service is reachable
80- aiChecked bool // Whether the availability check has completed
81- suggestedFile string // Filename suggested by AI for the current code block
82- workingDir string // Directory from which to execute commands
83- codeBlockInfos []dirtracker.CodeBlockInfo // Code blocks with language tags
84- blockDirMappings []string // Effective dir per code block index
85- workspaceRoot string // From AppConfig.WorkspaceDir
86- runningCmd * exec.Cmd // Currently running command process (for kill support)
87- processKilled bool // Whether the process was killed by user (Ctrl+K)
49+ messages []types.ChatMessage // Conversation history
50+ inputBuffer string // Current input being typed
51+ aiClient ai.AIClient // AI service client
52+ model string // AI model to use
53+ provider string // "ollama" or "gemini"
54+ scrollOffset int // Vertical scroll offset for responses
55+ width int // Pane width
56+ height int // Pane height
57+ focused bool // Whether this pane is focused
58+ streaming bool // Whether AI is currently generating
59+ copyMode bool // Whether in code block selection mode
60+ viewMode bool // Whether viewing a code block
61+ codeBlocks []string // Extracted code blocks from responses
62+ selectedBlock int // Currently selected code block index
63+ lastSelectedCode string // Last selected code block content
64+ lastKeyPressed string // Last key pressed (for debugging)
65+ activeArea int // 0: Input, 1: Response
66+ viewModeScroll int // Scroll offset for code block view mode
67+ viewModeScrollX int // Horizontal scroll offset for code block view mode
68+ configMode bool // Whether in config editor mode
69+ configFields []string // Config field names
70+ configValues []string // Config field values
71+ selectedField int // Currently selected config field
72+ editingField bool // Whether currently editing a field
73+ editBuffer string // Buffer for editing field value
74+ editCursorPos int // Cursor position within edit buffer
75+ terminalMode bool // Whether terminal execution is active
76+ cmdRunning bool // Whether the command is currently running
77+ terminalOutput []string // Output lines from terminal execution
78+ stdinWriter io.WriteCloser // Stdin pipe for sending input to running command
79+ terminalInput string // Current input line being typed in terminal mode
80+ aiAvailable bool // Whether the AI service is reachable
81+ aiChecked bool // Whether the availability check has completed
82+ suggestedFile string // Filename suggested by AI for the current code block
83+ workingDir string // Directory from which to execute commands
84+ codeBlockInfos []dirtracker.CodeBlockInfo // Code blocks with language tags
85+ blockDirMappings []string // Effective dir per code block index
86+ workspaceRoot string // From AppConfig.WorkspaceDir
87+ runningCmd * exec.Cmd // Currently running command process (for kill support)
88+ processKilled bool // Whether the process was killed by user (Ctrl+K)
89+ lastKeystrokeTime time.Time // Last keypress timestamp to detect rapid/terminal paste
8890}
8991
9092// AIResponseMsg is sent when AI response chunk is received.
@@ -201,20 +203,21 @@ func NewAIChatPane(client ai.AIClient, model string, provider string, workspaceR
201203 workspaceRoot = cwd
202204 }
203205 return & AIChatPane {
204- messages : []types.ChatMessage {},
205- inputBuffer : "" ,
206- aiClient : client ,
207- model : model ,
208- provider : provider ,
209- scrollOffset : 0 ,
210- width : 0 ,
211- height : 0 ,
212- focused : false ,
213- streaming : false ,
214- activeArea : 0 , // 0: Input, 1: Response
215- terminalOutput : []string {},
216- workingDir : cwd ,
217- workspaceRoot : workspaceRoot ,
206+ messages : []types.ChatMessage {},
207+ inputBuffer : "" ,
208+ aiClient : client ,
209+ model : model ,
210+ provider : provider ,
211+ scrollOffset : 0 ,
212+ width : 0 ,
213+ height : 0 ,
214+ focused : false ,
215+ streaming : false ,
216+ activeArea : 0 , // 0: Input, 1: Response
217+ terminalOutput : []string {},
218+ workingDir : cwd ,
219+ workspaceRoot : workspaceRoot ,
220+ lastKeystrokeTime : time .Now (),
218221 }
219222}
220223
@@ -1159,6 +1162,11 @@ func (a *AIChatPane) handleKeyPress(msg tea.KeyMsg) tea.Cmd {
11591162 return nil
11601163 }
11611164
1165+ // Input area active - handle typing
1166+ now := time .Now ()
1167+ isRapid := now .Sub (a .lastKeystrokeTime ) < 25 * time .Millisecond
1168+ a .lastKeystrokeTime = now
1169+
11621170 // Handle input based on active area
11631171 if a .activeArea == 1 {
11641172 // Response area active - handle scrolling
@@ -1192,7 +1200,21 @@ func (a *AIChatPane) handleKeyPress(msg tea.KeyMsg) tea.Cmd {
11921200 } else {
11931201 // Input area active - handle typing
11941202 switch msg .String () {
1203+ case "ctrl+v" :
1204+ // Paste directly from clipboard into input buffer
1205+ content , err := clipboard .ReadAll ()
1206+ if err == nil {
1207+ // Normalize line endings
1208+ content = strings .ReplaceAll (content , "\r \n " , "\n " )
1209+ a .inputBuffer += content
1210+ }
1211+ return nil
11951212 case "enter" :
1213+ // Treat rapid enters (e.g., from native right-click paste) as newlines
1214+ if msg .Paste || isRapid {
1215+ a .inputBuffer += "\n "
1216+ return nil
1217+ }
11961218 // Send message when Enter is pressed
11971219 if a .inputBuffer != "" {
11981220 message := a .inputBuffer
@@ -1205,9 +1227,18 @@ func (a *AIChatPane) handleKeyPress(msg tea.KeyMsg) tea.Cmd {
12051227 case "backspace" :
12061228 // Delete last character from input buffer
12071229 if len (a .inputBuffer ) > 0 {
1208- a .inputBuffer = a .inputBuffer [:len (a .inputBuffer )- 1 ]
1230+ runes := []rune (a .inputBuffer )
1231+ if len (runes ) > 0 {
1232+ a .inputBuffer = string (runes [:len (runes )- 1 ])
1233+ }
12091234 }
12101235 default :
1236+ if msg .Paste {
1237+ if len (msg .Runes ) > 0 {
1238+ a .inputBuffer += string (msg .Runes )
1239+ }
1240+ return nil
1241+ }
12111242 // Add character to input buffer
12121243 // Check for printable characters to avoid control chars
12131244 if len (msg .String ()) == 1 {
0 commit comments