@@ -11,6 +11,7 @@ import (
1111
1212 "github.com/Ajnasz/go-loggly-cli/search"
1313 "github.com/charmbracelet/bubbles/list"
14+ "github.com/charmbracelet/bubbles/spinner"
1415 "github.com/charmbracelet/bubbles/textinput"
1516 "github.com/charmbracelet/bubbles/viewport"
1617 tea "github.com/charmbracelet/bubbletea"
@@ -146,8 +147,11 @@ type model struct {
146147 valuesList list.Model
147148 resultsList list.Model
148149 detailView viewport.Model
150+ spinner spinner.Model
149151 debugView string
150152
153+ selectedField fieldItem
154+
151155 currentPane pane
152156 width int
153157 height int
@@ -197,7 +201,6 @@ func initialModel(ctx context.Context, account, token string, size int, maxPages
197201 ti .Placeholder = "Enter your Loggly query..."
198202 ti .Focus ()
199203 ti .CharLimit = 500
200- ti .Width = 50
201204 ti .SetValue (query )
202205
203206 fieldsList := list .New ([]list.Item {}, list .NewDefaultDelegate (), 20 , 20 )
@@ -236,6 +239,7 @@ func initialModel(ctx context.Context, account, token string, size int, maxPages
236239 valuesList : valuesList ,
237240 resultsList : resultsList ,
238241 detailView : detailView ,
242+ spinner : spinner .New (),
239243 debugView : "" ,
240244 currentPane : queryPane ,
241245 allFields : make (map [string ]int ),
@@ -246,7 +250,7 @@ func initialModel(ctx context.Context, account, token string, size int, maxPages
246250}
247251
248252func (m model ) Init () tea.Cmd {
249- return textinput .Blink
253+ return tea . Batch ( textinput .Blink , m . spinner . Tick )
250254}
251255
252256func (m model ) Update (msg tea.Msg ) (tea.Model , tea.Cmd ) {
@@ -290,14 +294,22 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
290294 m .loading = true
291295 return m , m .executeQuery ()
292296 }
297+
293298 if m .currentPane == fieldsPane {
294299 cmd := m .selectField ()
295300 return m , cmd
296301 }
302+
297303 if m .currentPane == valuesPane {
298304 m .addValueToQuery ()
305+ if m .loading {
306+ return m , nil
307+ }
308+
309+ m .loading = true
299310 return m , m .executeQuery ()
300311 }
312+
301313 if m .currentPane == resultsPane {
302314 // Show detail view for selected result
303315 if item , ok := m .resultsList .SelectedItem ().(resultItem ); ok {
@@ -316,6 +328,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
316328 }
317329 }
318330
331+ case spinner.TickMsg :
332+ var cmd tea.Cmd
333+ m .spinner , cmd = m .spinner .Update (msg )
334+ return m , cmd
335+
319336 case resultsMsg :
320337 m .loading = false
321338 if msg .err != nil {
@@ -333,8 +350,8 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
333350 }
334351 m .debugView = fmt .Sprintf ("Loaded %d results" , len (msg .results ))
335352 return m , nil
353+
336354 case fieldSelectedMsg :
337- // Field was selected, lists are already updated
338355 return m , nil
339356 }
340357
@@ -377,7 +394,7 @@ func (m *model) updateSizes() {
377394 borderWidth := 6 // 2 chars per pane * 3 panes
378395 rightPaneWidth := m .width - leftPaneWidth - midPaneWidth - borderWidth
379396
380- paneHeight := m .height - 10
397+ paneHeight := m .height - 8
381398
382399 // Set sizes to content area (borders will be added by lipgloss)
383400 m .fieldsList .SetSize (leftPaneWidth , paneHeight - 2 )
@@ -408,7 +425,7 @@ func (m *model) updateFocus() {
408425
409426func (m model ) View () string {
410427 if m .width == 0 {
411- return "Loading..."
428+ return m . spinner . View ()
412429 }
413430
414431 // If showing detail view, render it full screen
@@ -471,19 +488,20 @@ func (m model) View() string {
471488
472489 status := ""
473490 if m .loading {
474- status = " Loading..."
491+ status = m . spinner . View () + " Loading..."
475492 } else if m .err != nil {
476493 status = fmt .Sprintf ("Error: %s" , m .err )
477494 } else if len (m .results ) > 0 {
478495 status = fmt .Sprintf ("%d results" , len (m .results ))
479496 }
480497
481- return lipgloss .JoinVertical (lipgloss .Left ,
498+ status = status + " " + m .debugView
499+
500+ return lipgloss .JoinVertical (
501+ lipgloss .Left ,
482502 querySection ,
483- "" ,
484503 lipgloss .NewStyle ().Foreground (lipgloss .Color ("170" )).Render (fieldTitle ),
485504 panesRow ,
486- "" ,
487505 status ,
488506 // m.debugView,
489507 help ,
@@ -558,8 +576,6 @@ func (m *model) analyzeObject(obj map[string]any, path []string) {
558576}
559577
560578func (m * model ) updateFieldsList () {
561- var items []list.Item
562-
563579 // Get fields at current path level
564580 prefix := ""
565581 if len (m .fieldPath ) > 0 {
@@ -587,6 +603,8 @@ func (m *model) updateFieldsList() {
587603 return fields [i ].count > fields [j ].count
588604 })
589605
606+ var items []list.Item
607+
590608 for _ , f := range fields {
591609 items = append (items , f )
592610 }
@@ -596,6 +614,7 @@ func (m *model) updateFieldsList() {
596614
597615func (m * model ) selectField () tea.Cmd {
598616 if item , ok := m .fieldsList .SelectedItem ().(fieldItem ); ok {
617+ m .selectedField = item
599618 // Check if this field has nested fields
600619 testPath := append (m .fieldPath , item .name )
601620 pathStr := strings .Join (testPath , "." )
@@ -647,6 +666,7 @@ func (m *model) updateResultsView() {
647666 var items []list.Item
648667
649668 for i , result := range m .results {
669+ m .resultsList .SetItems (items )
650670 items = append (items , resultItem {
651671 index : i ,
652672 data : result ,
@@ -660,28 +680,41 @@ func (m *model) updateResultsView() {
660680 }
661681}
662682
683+ func replaceExisitingSearch (query , field , value string ) string {
684+ // Simple replacement logic: look for field:value and replace it
685+ parts := strings .Split (query , " AND " )
686+ for i , part := range parts {
687+ if strings .HasPrefix (part , field + ":" ) {
688+ parts [i ] = fmt .Sprintf ("%s:%s" , field , value )
689+ return strings .Join (parts , " AND " )
690+ }
691+ }
692+ // If not found, append
693+ if query != "" {
694+ return query + " AND " + fmt .Sprintf ("%s:%s" , field , value )
695+ }
696+ return fmt .Sprintf ("%s:%s" , field , value )
697+ }
698+
663699func (m * model ) addValueToQuery () tea.Cmd {
664- if selectedField , ok := m .fieldsList .SelectedItem ().(fieldItem ); ok {
665- if selectedValue , ok := m .valuesList .SelectedItem ().(valueItem ); ok {
666- fieldPath := append (m .fieldPath , selectedField .name )
667- fieldStr := "json." + strings .Join (fieldPath , "." )
668-
669- current := m .queryInput .Value ()
670- if current != "" {
671- current += " AND "
672- }
700+ if m .selectedField .name == "" {
701+ return nil
702+ }
673703
674- // Quote value if it contains spaces
675- value := selectedValue .value
676- if strings .Contains (value , " " ) {
677- value = fmt .Sprintf (`"%s"` , value )
678- }
704+ selectedField := m .selectedField
705+ if selectedValue , ok := m .valuesList .SelectedItem ().(valueItem ); ok {
706+ fieldPath := append (m .fieldPath , selectedField .name )
707+ fieldStr := "json." + strings .Join (fieldPath , "." )
679708
680- m .queryInput .SetValue (current + fmt .Sprintf ("%s:%s" , fieldStr , value ))
681- m .debugView = fmt .Sprintf ("Added to query: %s:%s" , fieldStr , value )
682- }
709+ current := m .queryInput .Value ()
710+ value := selectedValue .value
711+
712+ m .queryInput .SetValue (replaceExisitingSearch (current , fieldStr , value ))
713+ m .debugView = fmt .Sprintf ("Added to query: %s:%s" , fieldStr , value )
714+ return func () tea.Msg { return fieldSelectedMsg {} }
683715 }
684- return func () tea.Msg { return fieldSelectedMsg {} }
716+
717+ return nil
685718}
686719
687720func (m * model ) showDetailView (item resultItem ) {
0 commit comments