mirror of
https://github.com/tw93/Mole.git
synced 2026-03-22 16:45:07 +00:00
Merge pull request #601 from sibisai/fix/status-resize-ghost-lines
fix(status): pad View output to terminal height to prevent ghost lines on resize
This commit is contained in:
@@ -165,6 +165,7 @@ func (m model) View() string {
|
|||||||
|
|
||||||
header, mole := renderHeader(m.metrics, m.errMessage, m.animFrame, termWidth, m.catHidden)
|
header, mole := renderHeader(m.metrics, m.errMessage, m.animFrame, termWidth, m.catHidden)
|
||||||
|
|
||||||
|
var cardContent string
|
||||||
if termWidth <= 80 {
|
if termWidth <= 80 {
|
||||||
cardWidth := termWidth
|
cardWidth := termWidth
|
||||||
if cardWidth > 2 {
|
if cardWidth > 2 {
|
||||||
@@ -179,27 +180,31 @@ func (m model) View() string {
|
|||||||
}
|
}
|
||||||
rendered = append(rendered, renderCard(c, cardWidth, 0))
|
rendered = append(rendered, renderCard(c, cardWidth, 0))
|
||||||
}
|
}
|
||||||
// Combine header, mole, and cards with consistent spacing
|
cardContent = lipgloss.JoinVertical(lipgloss.Left, rendered...)
|
||||||
var content []string
|
} else {
|
||||||
content = append(content, header)
|
cardWidth := max(24, termWidth/2-4)
|
||||||
if mole != "" {
|
cards := buildCards(m.metrics, cardWidth)
|
||||||
content = append(content, mole)
|
cardContent = renderTwoColumns(cards, termWidth)
|
||||||
}
|
|
||||||
content = append(content, lipgloss.JoinVertical(lipgloss.Left, rendered...))
|
|
||||||
return lipgloss.JoinVertical(lipgloss.Left, content...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cardWidth := max(24, termWidth/2-4)
|
|
||||||
cards := buildCards(m.metrics, cardWidth)
|
|
||||||
twoCol := renderTwoColumns(cards, termWidth)
|
|
||||||
// Combine header, mole, and cards with consistent spacing
|
// Combine header, mole, and cards with consistent spacing
|
||||||
var content []string
|
parts := []string{header}
|
||||||
content = append(content, header)
|
|
||||||
if mole != "" {
|
if mole != "" {
|
||||||
content = append(content, mole)
|
parts = append(parts, mole)
|
||||||
}
|
}
|
||||||
content = append(content, twoCol)
|
parts = append(parts, cardContent)
|
||||||
return lipgloss.JoinVertical(lipgloss.Left, content...)
|
output := lipgloss.JoinVertical(lipgloss.Left, parts...)
|
||||||
|
|
||||||
|
// Pad output to exactly fill the terminal height so every frame fully
|
||||||
|
// overwrites the alt screen buffer, preventing ghost lines on resize.
|
||||||
|
if m.height > 0 {
|
||||||
|
contentHeight := lipgloss.Height(output)
|
||||||
|
if contentHeight < m.height {
|
||||||
|
output += strings.Repeat("\n", m.height-contentHeight)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m model) collectCmd() tea.Cmd {
|
func (m model) collectCmd() tea.Cmd {
|
||||||
|
|||||||
@@ -809,16 +809,16 @@ func TestFormatDiskLine(t *testing.T) {
|
|||||||
if expectedLabel == "" {
|
if expectedLabel == "" {
|
||||||
expectedLabel = "DISK"
|
expectedLabel = "DISK"
|
||||||
}
|
}
|
||||||
if !contains(got, expectedLabel) {
|
if !strings.Contains(got, expectedLabel) {
|
||||||
t.Errorf("formatDiskLine(%q, ...) = %q, should contain label %q", tt.label, got, expectedLabel)
|
t.Errorf("formatDiskLine(%q, ...) = %q, should contain label %q", tt.label, got, expectedLabel)
|
||||||
}
|
}
|
||||||
if !contains(got, tt.wantUsed) {
|
if !strings.Contains(got, tt.wantUsed) {
|
||||||
t.Errorf("formatDiskLine(%q, ...) = %q, should contain used value %q", tt.label, got, tt.wantUsed)
|
t.Errorf("formatDiskLine(%q, ...) = %q, should contain used value %q", tt.label, got, tt.wantUsed)
|
||||||
}
|
}
|
||||||
if !contains(got, tt.wantFree) {
|
if !strings.Contains(got, tt.wantFree) {
|
||||||
t.Errorf("formatDiskLine(%q, ...) = %q, should contain free value %q", tt.label, got, tt.wantFree)
|
t.Errorf("formatDiskLine(%q, ...) = %q, should contain free value %q", tt.label, got, tt.wantFree)
|
||||||
}
|
}
|
||||||
if tt.wantNoSubstr != "" && contains(got, tt.wantNoSubstr) {
|
if tt.wantNoSubstr != "" && strings.Contains(got, tt.wantNoSubstr) {
|
||||||
t.Errorf("formatDiskLine(%q, ...) = %q, should not contain %q", tt.label, got, tt.wantNoSubstr)
|
t.Errorf("formatDiskLine(%q, ...) = %q, should not contain %q", tt.label, got, tt.wantNoSubstr)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -1133,6 +1133,37 @@ func TestRenderMemoryCardShowsSwapSizeOnWideWidth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestModelViewPadsToTerminalHeight(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
width int
|
||||||
|
height int
|
||||||
|
}{
|
||||||
|
{"narrow terminal", 60, 40},
|
||||||
|
{"wide terminal", 120, 40},
|
||||||
|
{"tall terminal", 120, 80},
|
||||||
|
{"short terminal", 120, 10},
|
||||||
|
{"zero height", 120, 0},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
m := model{
|
||||||
|
width: tt.width,
|
||||||
|
height: tt.height,
|
||||||
|
ready: true,
|
||||||
|
metrics: MetricsSnapshot{},
|
||||||
|
}
|
||||||
|
|
||||||
|
view := m.View()
|
||||||
|
got := lipgloss.Height(view)
|
||||||
|
if got < tt.height {
|
||||||
|
t.Errorf("View() height = %d, want >= %d (terminal height)", got, tt.height)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestModelViewErrorRendersSingleMole(t *testing.T) {
|
func TestModelViewErrorRendersSingleMole(t *testing.T) {
|
||||||
m := model{
|
m := model{
|
||||||
width: 120,
|
width: 120,
|
||||||
@@ -1169,16 +1200,3 @@ func stripANSI(s string) string {
|
|||||||
}
|
}
|
||||||
return result.String()
|
return result.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(s, substr string) bool {
|
|
||||||
return len(s) >= len(substr) && (s == substr || len(s) > len(substr) && (s[:len(substr)] == substr || s[len(s)-len(substr):] == substr || containsMiddle(s, substr)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func containsMiddle(s, substr string) bool {
|
|
||||||
for i := 0; i <= len(s)-len(substr); i++ {
|
|
||||||
if s[i:i+len(substr)] == substr {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user