Skip to content

Commit 849e5c5

Browse files
fix(tui): stabilize themed panel rendering
1 parent be8896c commit 849e5c5

3 files changed

Lines changed: 57 additions & 23 deletions

File tree

go/internal/tui/app.go

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ const minHeight = 12
850850

851851
func (a App) View() string {
852852
if a.width == 0 {
853-
return "Loading..."
853+
return appStyle.Width(max(1, a.width)).Height(max(1, a.height)).Render("Loading...")
854854
}
855855

856856
if a.width < minWidth || a.height < minHeight {
@@ -889,12 +889,12 @@ func (a App) View() string {
889889
view := strings.Join(parts, "\n")
890890

891891
if a.showHelp {
892-
return renderHelpOverlay(a.width, a.height)
892+
return appStyle.Width(a.width).Height(a.height).Render(renderHelpOverlay(a.width, a.height))
893893
}
894894
if a.confirmMsg != "" {
895-
return renderConfirmModal(a.confirmMsg, a.width, a.height)
895+
return appStyle.Width(a.width).Height(a.height).Render(renderConfirmModal(a.confirmMsg, a.width, a.height))
896896
}
897-
return view
897+
return appStyle.Width(a.width).Height(a.height).Render(view)
898898
}
899899

900900
func (a App) renderSessionsBody(filtered []model.SessionInfo, bodyHeight int) string {
@@ -936,7 +936,7 @@ func (a App) renderSessionsBody(filtered []model.SessionInfo, bodyHeight int) st
936936
s := filtered[a.selectedIdx]
937937
selected = &s
938938
}
939-
detailContent := renderDetail(selected, bodyHeight, a.insertMode)
939+
detailContent := renderDetail(selected, rightWidth-2, bodyHeight, a.insertMode)
940940
rightPanel := panelBorder.Width(rightWidth).Height(bodyHeight).Render(detailContent)
941941

942942
return lipgloss.JoinHorizontal(lipgloss.Top, leftPanel, rightPanel)
@@ -970,6 +970,18 @@ func (a *App) todoSectionHeight(totalHeight int, focused bool) int {
970970

971971
func (a *App) renderTodoPanel(width, height int, focused bool) string {
972972
var lines []string
973+
panelLine := func(content string) string {
974+
return lipgloss.NewStyle().
975+
Background(tokenBgSurface).
976+
Width(width).
977+
Render(content)
978+
}
979+
panelSelectedLine := func(content string) string {
980+
return lipgloss.NewStyle().
981+
Background(tokenBgSelected).
982+
Width(width).
983+
Render(content)
984+
}
973985

974986
count := len(a.todos)
975987
headerText := fmt.Sprintf("Todos (%d)", count)
@@ -983,16 +995,16 @@ func (a *App) renderTodoPanel(width, height int, focused bool) string {
983995
if focused {
984996
hdrStyle = hdrStyle.Foreground(tokenFgAccent)
985997
}
986-
lines = append(lines, hdrStyle.Render(headerText))
998+
lines = append(lines, panelLine(hdrStyle.Render(headerText)))
987999

9881000
if !focused {
9891001
// In sessions tab: show hint to switch to todos tab
990-
lines = append(lines, lipgloss.NewStyle().Foreground(tokenFgMuted).Render(" 2:manage todos"))
1002+
lines = append(lines, panelLine(lipgloss.NewStyle().Foreground(tokenFgMuted).Render(" 2:manage todos")))
9911003
}
9921004

9931005
if len(a.todos) == 0 && !a.todoInsert {
994-
lines = append(lines, lipgloss.NewStyle().Foreground(tokenFgMuted).Italic(true).
995-
Render(" (empty)"))
1006+
lines = append(lines, panelLine(lipgloss.NewStyle().Foreground(tokenFgMuted).Italic(true).
1007+
Render(" (empty)")))
9961008
}
9971009

9981010
maxItems := height - 2
@@ -1045,7 +1057,12 @@ func (a *App) renderTodoPanel(width, height int, focused bool) string {
10451057
if selected {
10461058
prefix = lipgloss.NewStyle().Foreground(tokenFgAccent).Render("▎ ")
10471059
}
1048-
lines = append(lines, prefix+lipgloss.NewStyle().Foreground(tokenFgMuted).Render(bullet)+" "+style.Render(text))
1060+
line := prefix + lipgloss.NewStyle().Foreground(tokenFgMuted).Render(bullet) + " " + style.Render(text)
1061+
if selected {
1062+
lines = append(lines, panelSelectedLine(line))
1063+
} else {
1064+
lines = append(lines, panelLine(line))
1065+
}
10491066
}
10501067

10511068
if a.todoInsert {
@@ -1054,10 +1071,10 @@ func (a *App) renderTodoPanel(width, height int, focused bool) string {
10541071
if len(input) > width-6 {
10551072
input = input[len(input)-width+6:]
10561073
}
1057-
lines = append(lines, cursor+lipgloss.NewStyle().Foreground(tokenFgDefault).Render(input+"█"))
1074+
lines = append(lines, panelLine(cursor+lipgloss.NewStyle().Foreground(tokenFgDefault).Render(input+"█")))
10581075
} else if focused && len(lines) < height {
1059-
lines = append(lines,
1060-
lipgloss.NewStyle().Foreground(tokenFgMuted).Render(" i:add ⏎:done d:del"))
1076+
lines = append(lines, panelLine(
1077+
lipgloss.NewStyle().Foreground(tokenFgMuted).Render(" i:add ⏎:done d:del")))
10611078
}
10621079

10631080
return strings.Join(lines, "\n")

go/internal/tui/styles.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ var (
129129
tokenFgAccent lipgloss.Color
130130
tokenBgSelected lipgloss.Color
131131
tokenBgAttention lipgloss.Color
132+
tokenBgSurface lipgloss.Color
132133
tokenBgHeader lipgloss.Color
133134
tokenBgTabBar lipgloss.Color
134135
tokenFgTooSmall lipgloss.Color
@@ -144,6 +145,7 @@ var (
144145
dividerStyle lipgloss.Style
145146

146147
gradientColors []string
148+
appStyle lipgloss.Style
147149
)
148150

149151
func init() {
@@ -216,6 +218,7 @@ func applyTheme(theme themeDefinition) {
216218
tokenFgAccent = colorCyan
217219
tokenBgSelected = theme.selectedBg
218220
tokenBgAttention = theme.attentionBg
221+
tokenBgSurface = colorSurface
219222
tokenBgHeader = theme.headerBg
220223
tokenBgTabBar = theme.tabBarBg
221224
tokenFgTooSmall = theme.tooSmall
@@ -243,6 +246,9 @@ func applyTheme(theme themeDefinition) {
243246
Background(colorSurface)
244247
dividerStyle = lipgloss.NewStyle().Foreground(colorBorder)
245248
gradientColors = theme.contextGradient
249+
appStyle = lipgloss.NewStyle().
250+
Foreground(colorText).
251+
Background(colorBg)
246252
}
247253

248254
var workingSpinner = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}

go/internal/tui/views.go

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ func renderTooSmall(w, h int) string {
2121
w, h, minWidth, minHeight,
2222
)
2323
return lipgloss.NewStyle().
24+
Background(colorBg).
2425
Foreground(tokenFgTooSmall).
2526
Padding(1, 2).
2627
Render(msg)
@@ -178,7 +179,10 @@ func renderSessionTableHeader(width, sortMode int) string {
178179
dim.Width(nameWidth).Render(nameLabel) +
179180
dim.Width(3).Render(" ST")
180181

181-
return row
182+
return lipgloss.NewStyle().
183+
Background(tokenBgSurface).
184+
Width(width).
185+
Render(row)
182186
}
183187

184188
func renderSessionTableRow(s model.SessionInfo, selected, blinkOn bool, spinnerIdx, width int) string {
@@ -232,7 +236,10 @@ func renderSessionTableRow(s model.SessionInfo, selected, blinkOn bool, spinnerI
232236
Width(width).
233237
Render(row)
234238
}
235-
return lipgloss.NewStyle().Width(width).Render(row)
239+
return lipgloss.NewStyle().
240+
Background(tokenBgSurface).
241+
Width(width).
242+
Render(row)
236243
}
237244

238245
func typeTag(t model.ProcessType) string {
@@ -317,7 +324,10 @@ func renderOutputTab(a App, width, height int) string {
317324

318325
// ── Detail panel ────────────────────────────────────────────────────
319326

320-
func renderDetail(s *model.SessionInfo, height int, insertMode bool) string {
327+
func renderDetail(s *model.SessionInfo, width, height int, insertMode bool) string {
328+
if width < 20 {
329+
width = 20
330+
}
321331
if s == nil {
322332
return panelHeadingStyle.Render("Overview") + "\n" +
323333
lipgloss.NewStyle().Foreground(tokenFgMuted).
@@ -353,7 +363,7 @@ func renderDetail(s *model.SessionInfo, height int, insertMode bool) string {
353363
lipgloss.NewStyle().Foreground(colorMagenta).Render("⎇ "+sanitizeField(s.GitBranch)))
354364
}
355365

356-
lines = append(lines, "", divider(40))
366+
lines = append(lines, "", divider(min(width, 40)))
357367

358368
if s.ContextPct > 0 || s.ContextTokens > 0 {
359369
lines = append(lines, "")
@@ -368,7 +378,7 @@ func renderDetail(s *model.SessionInfo, height int, insertMode bool) string {
368378
if s.LastActivity != "" {
369379
activity := sanitizeField(s.LastActivity)
370380
if len(activity) > 80 {
371-
activity = activity[:80] + "…"
381+
activity = activity[:min(width, 80)] + "…"
372382
}
373383
lines = append(lines, "",
374384
lipgloss.NewStyle().Foreground(tokenFgMuted).Italic(true).
@@ -378,7 +388,7 @@ func renderDetail(s *model.SessionInfo, height int, insertMode bool) string {
378388
if s.TaskSummary != "" {
379389
task := sanitizeField(s.TaskSummary)
380390
if len(task) > 76 {
381-
task = task[:76] + "…"
391+
task = task[:min(width, 76)] + "…"
382392
}
383393
lines = append(lines, "",
384394
lipgloss.NewStyle().Foreground(tokenFgAccent).Bold(true).Render("Task: ")+
@@ -387,7 +397,7 @@ func renderDetail(s *model.SessionInfo, height int, insertMode bool) string {
387397

388398
// Pane preview
389399
if s.PaneContent != "" {
390-
lines = append(lines, "", divider(40))
400+
lines = append(lines, "", divider(min(width, 40)))
391401

392402
paneLines := strings.Split(s.PaneContent, "\n")
393403
for len(paneLines) > 0 && strings.TrimSpace(paneLines[len(paneLines)-1]) == "" {
@@ -407,11 +417,12 @@ func renderDetail(s *model.SessionInfo, height int, insertMode bool) string {
407417
preview := paneLines[start:]
408418

409419
var colored []string
420+
previewWidth := max(16, width-2)
410421
for _, pl := range preview {
411422
pl = strings.ReplaceAll(pl, "\r", "")
412423
pl = strings.ReplaceAll(pl, "\t", " ")
413-
if runes := []rune(pl); len(runes) > 76 {
414-
pl = string(runes[:76])
424+
if runes := []rune(pl); len(runes) > previewWidth {
425+
pl = string(runes[:previewWidth])
415426
}
416427
colored = append(colored, colorPreviewLine(pl))
417428
}
@@ -425,7 +436,7 @@ func renderDetail(s *model.SessionInfo, height int, insertMode bool) string {
425436
boxStyle = boxStyle.BorderForeground(colorYellow)
426437
boxLabel = lipgloss.NewStyle().Foreground(colorYellow).Bold(true).Render(" INSERT ")
427438
}
428-
box := boxStyle.Width(78).Height(previewHeight).
439+
box := boxStyle.Width(previewWidth).Height(previewHeight).
429440
Render(strings.Join(colored, "\n"))
430441

431442
lines = append(lines, boxLabel, box)

0 commit comments

Comments
 (0)