feat: modularize TUI dashboard components into individual view files #363
feat: modularize TUI dashboard components into individual view files #363SuperCoolPencil wants to merge 4 commits intomainfrom
Conversation
…and implement network activity graph and download list UI
…lue rendering and remove redundant whitespace
| func (m *RootModel) renderHeaderBox(width, height int) string { | ||
| contentWidth := width - 2 | ||
| contentHeight := height - 2 | ||
|
|
||
| if contentWidth < 0 { | ||
| contentWidth = 0 | ||
| } | ||
| if contentHeight < 1 { | ||
| contentHeight = 1 | ||
| } | ||
|
|
||
| logoText := ` | ||
| _______ ___________ ____ | ||
| / ___/ / / / ___/ __ '/ _ \ | ||
| (__ ) /_/ / / / /_/ / __/ | ||
| /____/\__,_/_/ \__, /\___/ | ||
| /____/ ` | ||
|
|
||
| // Server info part | ||
| greenDot := lipgloss.NewStyle().Foreground(colors.StateDownloading).Render("●") | ||
| host := m.ServerHost | ||
| if host == "" { | ||
| host = "127.0.0.1" | ||
| } | ||
| serverAddr := fmt.Sprintf("%s:%d", host, m.ServerPort) | ||
|
|
||
| var statusLine string | ||
| if m.IsRemote { | ||
| statusLine = lipgloss.NewStyle().Foreground(colors.NeonCyan).Bold(true).Render(" Connected to " + serverAddr) | ||
| } else { | ||
| statusLine = lipgloss.NewStyle().Foreground(colors.NeonCyan).Bold(true).Render(" Serving at " + serverAddr) | ||
| } | ||
|
|
||
| serverPortContent := lipgloss.NewStyle(). | ||
| Width(contentWidth). | ||
| Align(lipgloss.Center). | ||
| Render(greenDot + statusLine) | ||
|
|
||
| var innerContent string | ||
| // If the height is too short for both logo and server text, just return server text centered vertically | ||
| if contentHeight < 7 { | ||
| innerContent = lipgloss.Place(contentWidth, contentHeight, lipgloss.Center, lipgloss.Center, serverPortContent) | ||
| } else { | ||
| var logoContent string | ||
| if m.logoCache != "" { | ||
| logoContent = m.logoCache | ||
| } else { | ||
| gradientLogo := ApplyGradient(logoText, colors.NeonPink, colors.NeonPurple) | ||
| m.logoCache = lipgloss.NewStyle().Render(gradientLogo) | ||
| logoContent = m.logoCache | ||
| } | ||
|
|
||
| logoBoxHeight := contentHeight - 1 // 1 line for the server text at the bottom | ||
| if logoBoxHeight < 1 { | ||
| logoBoxHeight = 1 | ||
| } | ||
|
|
||
| logoBox := lipgloss.Place(contentWidth, logoBoxHeight, lipgloss.Center, lipgloss.Center, logoContent) | ||
| innerContent = lipgloss.JoinVertical(lipgloss.Left, logoBox, serverPortContent) | ||
| } | ||
|
|
||
| return renderBtopBox("", PaneTitleStyle.Render(" Server "), innerContent, width, height, colors.Gray) | ||
| } |
There was a problem hiding this comment.
MinLogoWidth guard dropped — logo renders at previously-suppressed widths
The original code had hideLogo := leftWidth < MinLogoWidth (threshold: 60) and skipped the ASCII art at narrow left-column widths. renderHeaderBox removes this guard entirely and only falls back on contentHeight < 7. At leftWidth between 35–59, the logo gradient will now be rendered inside a narrower-than-expected area, potentially wrapping or overflowing.
Consider restoring the width check:
if contentHeight < 7 || width < MinLogoWidth {
innerContent = lipgloss.Place(contentWidth, contentHeight, lipgloss.Center, lipgloss.Center, serverPortContent)
} else {
// render logo...
}Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/tui/view_dashboard_header.go
Line: 11-73
Comment:
**`MinLogoWidth` guard dropped — logo renders at previously-suppressed widths**
The original code had `hideLogo := leftWidth < MinLogoWidth` (threshold: 60) and skipped the ASCII art at narrow left-column widths. `renderHeaderBox` removes this guard entirely and only falls back on `contentHeight < 7`. At `leftWidth` between 35–59, the logo gradient will now be rendered inside a narrower-than-expected area, potentially wrapping or overflowing.
Consider restoring the width check:
```go
if contentHeight < 7 || width < MinLogoWidth {
innerContent = lipgloss.Place(contentWidth, contentHeight, lipgloss.Center, lipgloss.Center, serverPortContent)
} else {
// render logo...
}
```
How can I resolve this? If you propose a fix, please make it concise.|
|
||
| if hideGraphStats { | ||
| // No stats box — graph gets almost full width | ||
| graphAreaWidth, axisWidth := GetGraphAreaDimensions(contentWidth+BoxStyle.GetHorizontalFrameSize()*2, true) |
There was a problem hiding this comment.
GetGraphAreaDimensions receives width + 2 instead of width
The original code passed rightWidth directly:
// original
graphAreaWidth, axisWidth := GetGraphAreaDimensions(rightWidth, true)The new code passes contentWidth + BoxStyle.GetHorizontalFrameSize()*2:
// new: contentWidth = width-2, GetHorizontalFrameSize()*2 = 4 → passes width+2
graphAreaWidth, axisWidth := GetGraphAreaDimensions(contentWidth+BoxStyle.GetHorizontalFrameSize()*2, true)This inflates the argument by 2 units compared to the original, widening the computed graph area and potentially overflowing the box at tight widths. Passing width directly, as the original did, is more correct here.
Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/tui/view_dashboard_graph.go
Line: 124
Comment:
**`GetGraphAreaDimensions` receives `width + 2` instead of `width`**
The original code passed `rightWidth` directly:
```go
// original
graphAreaWidth, axisWidth := GetGraphAreaDimensions(rightWidth, true)
```
The new code passes `contentWidth + BoxStyle.GetHorizontalFrameSize()*2`:
```go
// new: contentWidth = width-2, GetHorizontalFrameSize()*2 = 4 → passes width+2
graphAreaWidth, axisWidth := GetGraphAreaDimensions(contentWidth+BoxStyle.GetHorizontalFrameSize()*2, true)
```
This inflates the argument by 2 units compared to the original, widening the computed graph area and potentially overflowing the box at tight widths. Passing `width` directly, as the original did, is more correct here.
How can I resolve this? If you propose a fix, please make it concise.| logWidth := leftWidth - logoWidth - BoxStyle.GetHorizontalFrameSize() | ||
|
|
||
| if logoWidth < 4 { | ||
| logoWidth = 4 // Minimum for server box content | ||
| logoWidth = 4 | ||
| } | ||
| if logWidth < 4 { | ||
| logWidth = 4 // Minimum for viewport | ||
| logWidth = 4 | ||
| } |
There was a problem hiding this comment.
Trailing whitespace on several lines
The refactoring stripped meaningful inline comments (e.g. // Rest for log box, // Minimum for server box content, // Minimum for viewport) but left behind trailing spaces on the same lines.
| logWidth := leftWidth - logoWidth - BoxStyle.GetHorizontalFrameSize() | |
| if logoWidth < 4 { | |
| logoWidth = 4 // Minimum for server box content | |
| logoWidth = 4 | |
| } | |
| if logWidth < 4 { | |
| logWidth = 4 // Minimum for viewport | |
| logWidth = 4 | |
| } | |
| logWidth := leftWidth - logoWidth - BoxStyle.GetHorizontalFrameSize() // Rest for log box | |
| if logoWidth < 4 { | |
| logoWidth = 4 // Minimum for server box content | |
| } | |
| if logWidth < 4 { | |
| logWidth = 4 // Minimum for viewport | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: internal/tui/view.go
Line: 446-453
Comment:
**Trailing whitespace on several lines**
The refactoring stripped meaningful inline comments (e.g. `// Rest for log box`, `// Minimum for server box content`, `// Minimum for viewport`) but left behind trailing spaces on the same lines.
```suggestion
logWidth := leftWidth - logoWidth - BoxStyle.GetHorizontalFrameSize() // Rest for log box
if logoWidth < 4 {
logoWidth = 4 // Minimum for server box content
}
if logWidth < 4 {
logWidth = 4 // Minimum for viewport
}
```
How can I resolve this? If you propose a fix, please make it concise.
Greptile Summary
This PR modularizes the monolithic
view.goTUI dashboard into individual files per pane (view_dashboard_header.go,view_dashboard_list.go,view_dashboard_graph.go,view_dashboard_log.go,view_dashboard_details.go,view_dashboard_chunkmap.go), and changesPaneTitleStylefromNeonCyantoLightGray.The structural split is clean and improves navigability, but the refactoring introduced a subtle layout inconsistency:
renderFocusedDetailsis now called twice per frame with different content widths, causing the pre-measureddetailHeightused for layout to potentially diverge from the actual rendered height.Confidence Score: 4/5
Safe to merge after addressing the double-computation width mismatch in the detail pane; all other issues are minor visual regressions
One P1 defect: renderFocusedDetails is called twice per frame with a 2-unit content-width discrepancy between the height-measurement pass (rightWidth-4) and the rendering pass (rightWidth-2). This can produce a layout height that doesn't match the rendered content, misaligning the right column at certain terminal widths. Two P2 issues (GetGraphAreaDimensions inflation by +2, missing MinLogoWidth guard) are behavioural regressions worth fixing but won't break the primary path for most users.
internal/tui/view.go and internal/tui/view_dashboard_details.go (double computation / width mismatch), internal/tui/view_dashboard_graph.go (GetGraphAreaDimensions argument)
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD V["RootModel.View()"] V --> HD["renderHeaderBox()\nview_dashboard_header.go"] V --> LB["renderLogBox()\nview_dashboard_log.go"] V --> GB["renderGraphBox()\nview_dashboard_graph.go"] V --> DL["renderDownloadsBox()\nview_dashboard_list.go"] V --> DB["renderDetailsBox()\nview_dashboard_details.go"] V --> CB["renderChunkMapBox()\nview_dashboard_chunkmap.go"] V -->|"pre-measures detailHeight\nwith rightWidth-4"| RFD1["renderFocusedDetails()\nwidth = rightWidth-4"] DB -->|"re-renders with\nrightWidth-2"| RFD2["renderFocusedDetails()\nwidth = rightWidth-2"] RFD1 -.->|"height mismatch\npossible"| RFD2 style RFD1 fill:#f9a,stroke:#f00 style RFD2 fill:#f9a,stroke:#f00 style DB fill:#fdd,stroke:#f00Comments Outside Diff (1)
internal/tui/view.go, line 326-342 (link)renderFocusedDetailsis called here to measuredetailHeightfor layout, usingdetailWidth = rightWidth - PaneStyle.GetHorizontalFrameSize(). BecauseDefaultPaddingX = 1,PaneStyle.GetHorizontalFrameSize() = 4, so the measurement width isrightWidth - 4.renderDetailsBoxthen callsrenderFocusedDetailsagain internally withcontentWidth = width - 2 = rightWidth - 2— 2 units wider. InsiderenderFocusedDetails, the actual inner content width isw - 4, so:contentWidth = rightWidth - 8contentWidth = rightWidth - 6If any text (filename, path, ID, stats) wraps at one width but not the other, the measured
detailHeightdiverges from the actual rendered height, mis-sizing the right column's layout. The original code computed this once with a consistent width.The simplest fix is to pass the already-computed
detailContentintorenderDetailsBoxinstead of recomputing:Prompt To Fix With AI
Prompt To Fix All With AI
Reviews (1): Last reviewed commit: "refactor: replace if-else block with swi..." | Re-trigger Greptile