diff --git a/internal/tui/components/empty_screen.go b/internal/tui/components/empty_screen.go index 75241d2..6476ece 100644 --- a/internal/tui/components/empty_screen.go +++ b/internal/tui/components/empty_screen.go @@ -26,7 +26,7 @@ func NewEmptyScreenComponent(title, description string, width, height int) *Empt Title: title, Description: description, Style: lipgloss.NewStyle().Align(lipgloss.Center).Padding(2), - width: width, + width: width - 6, height: height, } } diff --git a/internal/tui/components/help.go b/internal/tui/components/help.go index 70a3d1f..2672dcf 100644 --- a/internal/tui/components/help.go +++ b/internal/tui/components/help.go @@ -15,6 +15,7 @@ type HelpComponent struct { help help.Model keys KeyMap Style lipgloss.Style + width int } // NewHelpComponent creates a new help component @@ -30,7 +31,16 @@ func NewHelpComponent(title, description string) *HelpComponent { // SetWidth sets the width of the help component func (h *HelpComponent) SetWidth(width int) { - h.help.Width = width + h.width = width + overhead := h.getStyleOverhead() + h.help.Width = width - overhead +} + +// getStyleOverhead calculates the total width overhead from padding and borders +func (h *HelpComponent) getStyleOverhead() int { + hPadding := h.Style.GetHorizontalPadding() + hBorder := h.Style.GetHorizontalBorderSize() + return hPadding + hBorder } // ShowAll toggles the help view between short and full @@ -50,30 +60,167 @@ func (h *HelpComponent) IsShowingAll() bool { // Render returns the formatted help string func (h *HelpComponent) Render() string { - h.help.ShowAll = true - var content strings.Builder // Title titleStyle := lipgloss.NewStyle(). Bold(true). - Foreground(colors.Blue) - content.WriteString(titleStyle.Render(h.Title)) + Foreground(colors.Blue). + Align(lipgloss.Center) + + title := titleStyle.Render(h.Title) + content.WriteString(title) content.WriteString("\n\n") // Description if h.Description != "" { - content.WriteString(h.Description) + descStyle := lipgloss.NewStyle(). + Foreground(colors.LightGray). + Align(lipgloss.Center) + desc := descStyle.Render(h.Description) + content.WriteString(desc) content.WriteString("\n\n") } - // Use bubbles help to render the key bindings - helpView := h.help.View(h.keys) - content.WriteString(helpView) + content.WriteString(h.renderKeyBindings()) + + return h.Style.Render(content.String()) +} + +func (h *HelpComponent) renderKeyBindings() string { + fullHelp := h.keys.FullHelp() + + if len(fullHelp) == 0 { + return "" + } + + // Calculate available content width + availableWidth := h.help.Width + if availableWidth <= 0 { + availableWidth = 80 // default fallback + } + + keyStyle := lipgloss.NewStyle(). + Foreground(colors.Blue). + Bold(true). + Padding(0, 1) + + descStyle := lipgloss.NewStyle(). + Foreground(colors.White) + + sepStyle := lipgloss.NewStyle(). + Foreground(colors.LightGray) + + var sections []string + + sectionTitles := []string{ + "Navigation", + "Pagination", + "View Controls", + "General", + } + + for i, column := range fullHelp { + var rows []string + + if i < len(sectionTitles) { + titleStyle := lipgloss.NewStyle(). + Bold(true). + Foreground(colors.Blue). + Underline(true) + rows = append(rows, titleStyle.Render(sectionTitles[i])) + rows = append(rows, "") + } + + for _, binding := range column { + if !binding.Enabled() { + continue + } + + keyHelp := binding.Help() + keyStr := keyStyle.Render(keyHelp.Key) + descStr := descStyle.Render(keyHelp.Desc) + sep := sepStyle.Render(" • ") + + row := keyStr + sep + descStr + rows = append(rows, row) + } + + section := strings.Join(rows, "\n") + sections = append(sections, section) + } + + // Calculate the width for each section + maxWidth := 0 + for _, section := range sections { + lines := strings.Split(section, "\n") + for _, line := range lines { + width := lipgloss.Width(line) + if width > maxWidth { + maxWidth = width + } + } + } + + // Section padding and spacing constants + const sectionHPadding = 4 // horizontal padding per section (left + right) + const sectionSpacing = 2 // spacing between sections + sectionWidth := maxWidth + sectionHPadding + + // Calculate total width needed for horizontal layout + numSections := len(sections) + totalWidthNeeded := (sectionWidth * numSections) + (sectionSpacing * (numSections - 1)) + + // Decide layout based on available width + var helpContent string + if totalWidthNeeded <= availableWidth && numSections > 1 { + // Horizontal layout - sections side by side + sectionStyle := lipgloss.NewStyle(). + Width(sectionWidth). + Padding(1, 2) + + var styledSections []string + for _, section := range sections { + styledSections = append(styledSections, sectionStyle.Render(section)) + } + + helpContent = lipgloss.JoinHorizontal( + lipgloss.Top, + styledSections..., + ) + } else { + // Vertical layout - sections stacked + // Adjust section width to use more available space + adjustedWidth := availableWidth - sectionHPadding + if adjustedWidth < maxWidth { + adjustedWidth = maxWidth + } + + sectionStyle := lipgloss.NewStyle(). + Width(adjustedWidth). + Padding(1, 2) + + var styledSections []string + for i, section := range sections { + styledSections = append(styledSections, sectionStyle.Render(section)) + // Add spacing between sections except for the last one + if i < len(sections)-1 { + styledSections = append(styledSections, "") + } + } + + helpContent = lipgloss.JoinVertical( + lipgloss.Left, + styledSections..., + ) + } + + // Center the entire help content + centeredStyle := lipgloss.NewStyle(). + Width(availableWidth). + Align(lipgloss.Center) - view := h.Style.Render(content.String()) - h.help.ShowAll = false - return view + return centeredStyle.Render(helpContent) } // GetHelpModel returns the underlying help model for direct manipulation diff --git a/internal/tui/views/trafficwatch/left_pane.go b/internal/tui/views/trafficwatch/left_pane.go index 3d57ccd..a12cbe4 100644 --- a/internal/tui/views/trafficwatch/left_pane.go +++ b/internal/tui/views/trafficwatch/left_pane.go @@ -177,6 +177,11 @@ func (l *LeftPane) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } var cmd tea.Cmd + + if val, ok := msg.(tea.KeyMsg); ok && val.String() == "left" { + return l, nil + } + l.paginator, cmd = l.paginator.Update(msg) return l, cmd } diff --git a/internal/tui/views/trafficwatch/main_view.go b/internal/tui/views/trafficwatch/main_view.go index 94da820..16e9374 100644 --- a/internal/tui/views/trafficwatch/main_view.go +++ b/internal/tui/views/trafficwatch/main_view.go @@ -335,22 +335,19 @@ func (m *MainView) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } // Update focused pane - var cmd tea.Cmd if m.state == StateWithData { m.rightPane.SetFocused(m.focus == "right") - switch m.focus { - case "left": - _, cmd = m.leftPane.Update(msg) - case "right": - _, cmd = m.rightPane.Update(msg) - } + _, cmd := m.leftPane.Update(msg) + cmds = append(cmds, cmd) + _, cmd = m.rightPane.Update(msg) + cmds = append(cmds, cmd) } if m.state == StateLogs { - _, cmd = m.logsView.Update(msg) + _, cmd := m.logsView.Update(msg) + cmds = append(cmds, cmd) } - cmds = append(cmds, cmd) return m, tea.Batch(cmds...) }