@@ -265,47 +265,48 @@ func (app *ChatApplication) renderModelSelection() string {
265265}
266266
267267func (app * ChatApplication ) renderFileSelection () string {
268- allFiles , ok := app .state . Data [ "files" ].([] string )
269- if ! ok || len ( allFiles ) == 0 {
268+ allFiles , searchQuery , selectedIndex := app .getFileSelectionState ( )
269+ if allFiles == nil {
270270 return "📁 No files found in current directory\n \n Press ESC to return to chat"
271271 }
272272
273- searchQuery := ""
274- if query , ok := app .state .Data ["fileSearchQuery" ].(string ); ok {
275- searchQuery = query
276- }
273+ files := app .filterFiles (allFiles , searchQuery )
274+ selectedIndex = app .validateSelectedIndex (files , selectedIndex )
277275
278- var files []string
279- if searchQuery == "" {
280- files = allFiles
281- } else {
282- for _ , file := range allFiles {
283- if strings .Contains (strings .ToLower (file ), strings .ToLower (searchQuery )) {
284- files = append (files , file )
285- }
286- }
287- }
276+ var b strings.Builder
277+ app .renderFileSelectionHeader (& b , files , allFiles , searchQuery )
278+ app .renderFileSearchField (& b , searchQuery )
288279
289- selectedIndex := 0
290- if idx , ok := app .state .Data ["fileSelectedIndex" ].(int ); ok {
291- selectedIndex = idx
280+ if len (files ) == 0 {
281+ return app .renderNoFilesFound (& b , searchQuery )
292282 }
293283
284+ app .renderFileList (& b , files , selectedIndex )
285+ app .renderFileSelectionFooter (& b , files )
286+
287+ return b .String ()
288+ }
289+
290+ func (app * ChatApplication ) validateSelectedIndex (files []string , selectedIndex int ) int {
294291 if selectedIndex >= len (files ) {
295292 selectedIndex = 0
296293 app .state .Data ["fileSelectedIndex" ] = 0
297294 }
295+ return selectedIndex
296+ }
298297
299- var b strings.Builder
300- theme := app .services .GetTheme ()
301-
298+ func (app * ChatApplication ) renderFileSelectionHeader (b * strings.Builder , files , allFiles []string , searchQuery string ) {
302299 if searchQuery != "" {
303300 b .WriteString (fmt .Sprintf ("📁 File Search - %d matches (of %d total files):\n " , len (files ), len (allFiles )))
304301 } else {
305302 b .WriteString (fmt .Sprintf ("📁 Select a file to include in your message (%d files found):\n " , len (files )))
306303 }
307304 b .WriteString (strings .Repeat ("═" , app .state .Width ))
308305 b .WriteString ("\n \n " )
306+ }
307+
308+ func (app * ChatApplication ) renderFileSearchField (b * strings.Builder , searchQuery string ) {
309+ theme := app .services .GetTheme ()
309310
310311 b .WriteString ("🔍 Search: " )
311312 if searchQuery != "" {
@@ -314,23 +315,21 @@ func (app *ChatApplication) renderFileSelection() string {
314315 b .WriteString (fmt .Sprintf ("%stype to filter files...%s│" , theme .GetDimColor (), "\033 [0m" ))
315316 }
316317 b .WriteString ("\n \n " )
318+ }
317319
318- if len (files ) == 0 {
319- b .WriteString (fmt .Sprintf ("%sNo files match '%s'%s\n \n " , theme .GetErrorColor (), searchQuery , "\033 [0m" ))
320- helpText := "Type to search, BACKSPACE to clear search, ESC to cancel"
321- b .WriteString (theme .GetDimColor () + helpText + "\033 [0m" )
322- return b .String ()
323- }
320+ func (app * ChatApplication ) renderNoFilesFound (b * strings.Builder , searchQuery string ) string {
321+ theme := app .services .GetTheme ()
322+
323+ fmt .Fprintf (b , "%sNo files match '%s'%s\n \n " , theme .GetErrorColor (), searchQuery , "\033 [0m" )
324+ helpText := "Type to search, BACKSPACE to clear search, ESC to cancel"
325+ b .WriteString (theme .GetDimColor () + helpText + "\033 [0m" )
326+ return b .String ()
327+ }
324328
329+ func (app * ChatApplication ) renderFileList (b * strings.Builder , files []string , selectedIndex int ) {
330+ theme := app .services .GetTheme ()
325331 maxVisible := 12
326- startIndex := 0
327- if selectedIndex >= maxVisible {
328- startIndex = selectedIndex - maxVisible + 1
329- }
330- endIndex := startIndex + maxVisible
331- if endIndex > len (files ) {
332- endIndex = len (files )
333- }
332+ startIndex , endIndex := app .calculateVisibleRange (len (files ), selectedIndex , maxVisible )
334333
335334 for i := startIndex ; i < endIndex ; i ++ {
336335 file := files [i ]
@@ -340,19 +339,39 @@ func (app *ChatApplication) renderFileSelection() string {
340339 b .WriteString (fmt .Sprintf ("%s %s%s\n " , theme .GetDimColor (), file , "\033 [0m" ))
341340 }
342341 }
342+ }
343+
344+ func (app * ChatApplication ) calculateVisibleRange (totalFiles , selectedIndex , maxVisible int ) (int , int ) {
345+ startIndex := 0
346+ if selectedIndex >= maxVisible {
347+ startIndex = selectedIndex - maxVisible + 1
348+ }
349+ endIndex := startIndex + maxVisible
350+ if endIndex > totalFiles {
351+ endIndex = totalFiles
352+ }
353+ return startIndex , endIndex
354+ }
355+
356+ func (app * ChatApplication ) renderFileSelectionFooter (b * strings.Builder , files []string ) {
357+ theme := app .services .GetTheme ()
358+ maxVisible := 12
343359
344360 b .WriteString ("\n " )
345361
346362 if len (files ) > maxVisible {
363+ selectedIndex := 0
364+ if idx , ok := app .state .Data ["fileSelectedIndex" ].(int ); ok {
365+ selectedIndex = idx
366+ }
367+ startIndex , endIndex := app .calculateVisibleRange (len (files ), selectedIndex , maxVisible )
347368 b .WriteString (fmt .Sprintf ("%sShowing %d-%d of %d matches%s\n " ,
348369 theme .GetDimColor (), startIndex + 1 , endIndex , len (files ), "\033 [0m" ))
349370 b .WriteString ("\n " )
350371 }
351372
352373 helpText := "Type to search, ↑↓ to navigate, ENTER to select, BACKSPACE to clear, ESC to cancel"
353374 b .WriteString (theme .GetDimColor () + helpText + "\033 [0m" )
354-
355- return b .String ()
356375}
357376
358377func (app * ChatApplication ) renderApproval () string {
0 commit comments