Skip to content

Commit deee5fc

Browse files
committed
feat: add option to delete log files in TUI
Press D to open delete confirmation, Enter to confirm, ESC to cancel. Deletes (truncates) log files for selected streams only.
1 parent 822a545 commit deee5fc

File tree

1 file changed

+78
-3
lines changed

1 file changed

+78
-3
lines changed

internal/tui/tui.go

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ package tui
22

33
import (
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

7275
func 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+
335387
func (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

696748
type 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+
698773
func max(a, b int) int {
699774
if a > b {
700775
return a

0 commit comments

Comments
 (0)