Skip to content

Commit 8f51907

Browse files
authored
Adds wrapping (w) ability in detailed log viewer (#67)
1 parent fb82825 commit 8f51907

File tree

4 files changed

+92
-7
lines changed

4 files changed

+92
-7
lines changed

internal/tui/modals.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,12 @@ func (m *DashboardModel) renderModalStatusBar() string {
301301
if m.aiClient != nil {
302302
statusItems = append(statusItems, "i: AI Analysis")
303303
}
304+
// Add wrapping toggle for log details modal
305+
if m.attributeWrappingEnabled {
306+
statusItems = append(statusItems, "w: Disable wrapping")
307+
} else {
308+
statusItems = append(statusItems, "w: Enable wrapping")
309+
}
304310
statusItems = append(statusItems, "↑↓/Wheel: Scroll", "PgUp/PgDn: Page")
305311
}
306312
} else {

internal/tui/model.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ type DashboardModel struct {
111111
logAutoScroll bool // Auto-scroll to latest logs in log viewer
112112
instructionsScrollOffset int // Scroll position for instructions/filter status screen
113113

114+
// Modal display options
115+
attributeWrappingEnabled bool // Whether to wrap attribute values instead of truncating them
116+
114117
// Update interval management
115118
availableIntervals []time.Duration
116119
currentIntervalIdx int
@@ -283,6 +286,7 @@ func NewDashboardModel(maxLogBuffer int, updateInterval time.Duration, aiModel s
283286
logAutoScroll: true, // Start with auto-scroll enabled
284287
showColumns: true, // Show Host/Service columns by default
285288
instructionsScrollOffset: 0, // Start at top of instructions
289+
attributeWrappingEnabled: false, // Default to truncating (not wrapping)
286290
// Initialize statistics tracking
287291
statsStartTime: time.Now(),
288292
statsTotalBytes: 0,

internal/tui/navigation.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -861,6 +861,16 @@ func (m *DashboardModel) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
861861
}
862862
return m, nil
863863
}
864+
case "w":
865+
// Toggle attribute wrapping - only when not in chat mode
866+
if !m.chatActive {
867+
m.attributeWrappingEnabled = !m.attributeWrappingEnabled
868+
// Refresh the modal content with new wrapping setting
869+
if m.currentLogEntry != nil {
870+
m.modalContent = m.formatLogDetails(*m.currentLogEntry, 60)
871+
}
872+
return m, nil
873+
}
864874
case "escape", "esc": // escape to close modal (only if not in chat mode)
865875
m.showModal = false
866876
m.modalContent = ""
@@ -899,6 +909,14 @@ func (m *DashboardModel) handleKeyPress(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
899909
case "pgdown":
900910
m.infoViewport.HalfPageDown()
901911
return m, nil
912+
case "w":
913+
// Toggle attribute wrapping
914+
m.attributeWrappingEnabled = !m.attributeWrappingEnabled
915+
// Refresh the modal content with new wrapping setting
916+
if m.currentLogEntry != nil {
917+
m.modalContent = m.formatLogDetails(*m.currentLogEntry, 60)
918+
}
919+
return m, nil
902920
case "escape", "esc":
903921
m.showModal = false
904922
m.modalContent = ""

internal/tui/tables.go

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,25 +44,45 @@ func (m *DashboardModel) formatAttributesTable(attributes map[string]string, max
4444

4545
// Create table rows
4646
rows := []table.Row{}
47+
totalRows := 0
4748
for _, key := range keys {
4849
value := attributes[key]
49-
// Truncate long values to fit
50-
if len(value) > valueWidth-3 {
51-
value = value[:valueWidth-3] + "..."
52-
}
53-
// Truncate long keys to fit
50+
51+
// Always truncate long keys to fit (keys are less important to see in full)
5452
displayKey := key
5553
if len(displayKey) > keyWidth-3 {
5654
displayKey = displayKey[:keyWidth-3] + "..."
5755
}
58-
rows = append(rows, table.Row{displayKey, value})
56+
57+
// Handle value display based on wrapping setting
58+
if m.attributeWrappingEnabled && len(value) > valueWidth {
59+
// Wrap long values across multiple rows
60+
wrappedLines := wrapText(value, valueWidth)
61+
for i, line := range wrappedLines {
62+
if i == 0 {
63+
// First line shows the key
64+
rows = append(rows, table.Row{displayKey, line})
65+
} else {
66+
// Subsequent lines have empty key column
67+
rows = append(rows, table.Row{"", line})
68+
}
69+
totalRows++
70+
}
71+
} else {
72+
// Truncate long values to fit (default behavior)
73+
if len(value) > valueWidth-3 {
74+
value = value[:valueWidth-3] + "..."
75+
}
76+
rows = append(rows, table.Row{displayKey, value})
77+
totalRows++
78+
}
5979
}
6080

6181
// Create and configure table
6282
t := table.New(
6383
table.WithColumns(columns),
6484
table.WithRows(rows),
65-
table.WithHeight(len(rows)+2), // +2 for header and padding
85+
table.WithHeight(totalRows+2), // +2 for header and padding
6686
table.WithWidth(maxWidth),
6787
table.WithFocused(false), // Disable focus to prevent selection
6888
)
@@ -155,6 +175,43 @@ func (m *DashboardModel) formatLogDetails(entry LogEntry, maxWidth int) string {
155175
return details.String()
156176
}
157177

178+
// wrapText wraps text to fit within the specified width
179+
func wrapText(text string, width int) []string {
180+
if len(text) <= width {
181+
return []string{text}
182+
}
183+
184+
var lines []string
185+
for len(text) > width {
186+
// Find the best break point (prefer spaces)
187+
breakPoint := width
188+
189+
// Look for a space near the end to break on word boundary
190+
for i := width - 1; i > width/2 && i < len(text); i-- {
191+
if text[i] == ' ' {
192+
breakPoint = i
193+
break
194+
}
195+
}
196+
197+
// Add the line (excluding the space if we broke on one)
198+
if breakPoint < len(text) && text[breakPoint] == ' ' {
199+
lines = append(lines, text[:breakPoint])
200+
text = text[breakPoint+1:] // Skip the space
201+
} else {
202+
lines = append(lines, text[:breakPoint])
203+
text = text[breakPoint:]
204+
}
205+
}
206+
207+
// Add the remaining text
208+
if len(text) > 0 {
209+
lines = append(lines, text)
210+
}
211+
212+
return lines
213+
}
214+
158215
// formatAttributeValuesModal formats the attribute values modal showing individual values and their counts with full width layout
159216
func (m *DashboardModel) formatAttributeValuesModal(entry *memory.AttributeStatsEntry, maxWidth int) string {
160217
var modal strings.Builder

0 commit comments

Comments
 (0)