@@ -2,6 +2,8 @@ package tui
22
33import (
44 "fmt"
5+ "os"
6+ "path/filepath"
57 "regexp"
68 "strings"
79 "time"
@@ -67,6 +69,7 @@ type Model struct {
6769 detailMode bool // showing detail view of selected entry
6870 reverseOrder bool // show newest logs at top instead of bottom
6971 showStreamList bool // show full streams list overlay
72+ confirmDelete bool // showing delete confirmation
7073}
7174
7275func New (manager * logtail.Manager , cfg * config.Config ) * Model {
@@ -143,18 +146,30 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
143146 m .searchMode = true
144147
145148 case "esc" :
146- if m .detailMode {
149+ if m .confirmDelete {
150+ m .confirmDelete = false
151+ } else if m .detailMode {
147152 m .detailMode = false
148153 m .viewport .SetContent (m .renderTable ())
149154 } else if m .showStreamList {
150155 m .showStreamList = false
151156 }
152157
153158 case "enter" :
154- if len (m .filteredBuffer ) > 0 && m .selectedIdx < len (m .filteredBuffer ) {
159+ if m .confirmDelete {
160+ m .deleteLogFiles ()
161+ m .confirmDelete = false
162+ m .logBuffer = make ([]LogEntry , 0 , 1000 )
163+ m .filteredBuffer = m .logBuffer
164+ m .scrollOffset = 0
165+ m .viewport .SetContent (m .renderTable ())
166+ } else if len (m .filteredBuffer ) > 0 && m .selectedIdx < len (m .filteredBuffer ) {
155167 m .detailMode = ! m .detailMode
156168 }
157169
170+ case "D" :
171+ m .confirmDelete = true
172+
158173 case "up" , "k" :
159174 if m .selectedIdx > 0 {
160175 m .selectedIdx --
@@ -262,6 +277,10 @@ func (m *Model) View() string {
262277 return "Initializing..."
263278 }
264279
280+ if m .confirmDelete {
281+ return m .renderDeleteConfirm ()
282+ }
283+
265284 if m .detailMode && len (m .filteredBuffer ) > 0 && m .selectedIdx < len (m .filteredBuffer ) {
266285 return m .renderDetailView ()
267286 }
@@ -332,6 +351,39 @@ func (m *Model) renderStreamList() string {
332351 )
333352}
334353
354+ func (m * Model ) renderDeleteConfirm () string {
355+ title := titleStyle .Render (" DELETE LOGS " )
356+ header := headerBg .Width (m .width ).Render (title + strings .Repeat (" " , max (0 , m .width - lipgloss .Width (title ))))
357+
358+ var content strings.Builder
359+ content .WriteString ("\n \n " )
360+ content .WriteString (errorColor .Render (" ⚠ WARNING: This will permanently delete log file contents!\n \n " ))
361+ content .WriteString (cyanColor .Render (" The following log files will be cleared:\n \n " ))
362+
363+ for _ , stream := range m .config .Streams {
364+ if m .selectedStreams [stream .Name ] {
365+ content .WriteString (fmt .Sprintf (" • %s (%s)\n " , stream .Name , stream .Path ))
366+ }
367+ }
368+
369+ content .WriteString ("\n " )
370+ content .WriteString (whiteColor .Render (" Press ENTER to confirm, ESC to cancel\n " ))
371+
372+ confirmBox := lipgloss .NewStyle ().
373+ Width (m .width - 4 ).
374+ Height (m .height - 6 ).
375+ Render (content .String ())
376+
377+ footer := helpBar .Render (errorColor .Render ("[ENTER] Delete " ) + grayColor .Render ("[ESC] Cancel" ))
378+
379+ return lipgloss .JoinVertical (
380+ lipgloss .Left ,
381+ header ,
382+ borderStyle .Render (confirmBox ),
383+ footer ,
384+ )
385+ }
386+
335387func (m * Model ) renderDetailView () string {
336388 entry := m .filteredBuffer [m .selectedIdx ]
337389
@@ -572,7 +624,7 @@ func (m *Model) renderFooter() string {
572624 stats := fmt .Sprintf ("Lines: %d | Visible: %d/%d | Scroll: %d" ,
573625 len (m .logBuffer ), len (m .filteredBuffer ), 1000 , m .scrollOffset )
574626
575- controls := grayColor .Render ("[↑/↓]Select [Enter]Detail [/]Search [s]Streams [r]Reverse [c]Clear [p]Pause [q]Quit" )
627+ controls := grayColor .Render ("[↑/↓]Select [Enter]Detail [/]Search [s]Streams [r]Reverse [c]Clear [D]Delete [ p]Pause [q]Quit" )
576628
577629 helpBar2 := helpBar .Render (status + controls )
578630 return helpBar2 + "\n " + helpBar .Render (stats )
@@ -695,6 +747,29 @@ func (m *Model) tick() tea.Cmd {
695747
696748type tickMsg time.Time
697749
750+ func (m * Model ) deleteLogFiles () {
751+ for _ , stream := range m .config .Streams {
752+ if ! m .selectedStreams [stream .Name ] {
753+ continue
754+ }
755+
756+ // Find log files matching the stream patterns
757+ for _ , pattern := range stream .Patterns {
758+ matches , err := filepath .Glob (filepath .Join (stream .Path , pattern ))
759+ if err != nil {
760+ continue
761+ }
762+
763+ for _ , match := range matches {
764+ // Truncate the file (clear contents but keep file)
765+ if err := os .Truncate (match , 0 ); err != nil {
766+ continue
767+ }
768+ }
769+ }
770+ }
771+ }
772+
698773func max (a , b int ) int {
699774 if a > b {
700775 return a
0 commit comments