diff --git a/pkg/tui/account.go b/pkg/tui/account.go index bd8c4b5..c88c6f0 100644 --- a/pkg/tui/account.go +++ b/pkg/tui/account.go @@ -20,7 +20,7 @@ import ( func (m Model) accountBodyHeight() int { headerHeight := lipgloss.Height(m.BuildHeader()) footerHeight := lipgloss.Height(m.BuildFooter()) - budget := m.heightContainer - headerHeight - footerHeight - 2 + budget := m.heightContainer - headerHeight - footerHeight - 2 - 1 cards := budget / orderCardHeight cards = max(cards, 1) return cards*orderCardHeight - 2 diff --git a/pkg/tui/cart.go b/pkg/tui/cart.go index 15627d1..3d92667 100644 --- a/pkg/tui/cart.go +++ b/pkg/tui/cart.go @@ -14,7 +14,7 @@ func (m Model) updateCartViewport() Model { breadH := lipgloss.Height(m.BuildBreadcrumbs()) footerH := lipgloss.Height(m.BuildFooter()) // 2 = blank gap rows around the cart list - availH := m.heightContainer - headerH - footerH - breadH - 2 + availH := m.heightContainer - headerH - footerH - breadH - 2 - 1 if availH < 1 { availH = 1 } diff --git a/pkg/tui/confirmation.go b/pkg/tui/confirmation.go index e68b2c0..cd527fe 100644 --- a/pkg/tui/confirmation.go +++ b/pkg/tui/confirmation.go @@ -70,7 +70,7 @@ func (m Model) updateConfirmViewport() Model { headerH := lipgloss.Height(m.BuildHeader()) breadH := lipgloss.Height(m.BuildBreadcrumbs()) footerH := lipgloss.Height(m.BuildFooter()) - availH := m.heightContainer - headerH - footerH - breadH + availH := m.heightContainer - headerH - footerH - breadH - 1 if availH < 1 { availH = 1 } diff --git a/pkg/tui/payment_form.go b/pkg/tui/payment_form.go index 8762a78..dfc3990 100644 --- a/pkg/tui/payment_form.go +++ b/pkg/tui/payment_form.go @@ -296,7 +296,7 @@ func (m Model) updatePaymentViewport() Model { headerH := lipgloss.Height(m.BuildHeader()) breadH := lipgloss.Height(m.BuildBreadcrumbs()) footerH := lipgloss.Height(m.BuildFooter()) - availH := m.heightContainer - headerH - footerH - breadH + availH := m.heightContainer - headerH - footerH - breadH - 1 availH = max(availH, 1) if !m.payment.viewportReady { m.payment.viewport = viewport.New(viewport.WithWidth(m.widthContent), viewport.WithHeight(availH)) diff --git a/pkg/tui/shipping_form.go b/pkg/tui/shipping_form.go index 63d997a..9f0020a 100644 --- a/pkg/tui/shipping_form.go +++ b/pkg/tui/shipping_form.go @@ -257,7 +257,7 @@ func (m Model) updateShippingViewport() Model { headerH := lipgloss.Height(m.BuildHeader()) breadH := lipgloss.Height(m.BuildBreadcrumbs()) footerH := lipgloss.Height(m.BuildFooter()) - availH := m.heightContainer - headerH - footerH - breadH + availH := m.heightContainer - headerH - footerH - breadH - 1 if availH < 1 { availH = 1 } diff --git a/pkg/tui/theme/theme.go b/pkg/tui/theme/theme.go index 617dc76..fd93ec5 100644 --- a/pkg/tui/theme/theme.go +++ b/pkg/tui/theme/theme.go @@ -93,4 +93,8 @@ func (t Theme) PanelError() lipgloss.Style { return t.Base().Background(lipgloss.Color("52")).Foreground(lipgloss.Color("196")) } +func (t Theme) PanelNotice() lipgloss.Style { + return t.Base().Background(lipgloss.Color("#009000")).Foreground(lipgloss.Color("#00FF00")) +} + func (t Theme) Form() huh.Theme { return t.form } diff --git a/pkg/tui/views.go b/pkg/tui/views.go index d595e53..9c8e839 100644 --- a/pkg/tui/views.go +++ b/pkg/tui/views.go @@ -67,7 +67,8 @@ func (m Model) renderString() string { marginTop := 1 marginBottom := 1 bufferSpace := 1 - reservedHeight := headerHeight + breadcrumbsHeight + footerHeight + marginTop + marginBottom + bufferSpace + bannerHeight := 1 + reservedHeight := bannerHeight + headerHeight + breadcrumbsHeight + footerHeight + marginBottom + marginTop + bufferSpace availableContentHeight := m.heightContainer - reservedHeight // Ensure minimum content height @@ -81,22 +82,21 @@ func (m Model) renderString() string { if m.Loading { loadingStyle := m.theme.TextLoading().Padding(2, 4) content = loadingStyle.Render("Loading products from API...") - } else if m.error != nil { - errorStyle := m.theme.PanelError().Padding(0, 1).MarginBottom(1) - // soft-wrap long messages instead of clipping. - if m.widthContent > 4 { - errorStyle = errorStyle.Width(m.widthContent - 4) - } - errorBanner := errorStyle.Render(m.error.message) - content = errorBanner + "\n" + m.buildPageContent() - } else if m.notice != nil { - noticeStyle := m.theme.TextSuccess().Border(lipgloss.NormalBorder()).BorderForeground(m.theme.Success()).Padding(0, 1).MarginBottom(1) - noticeBanner := noticeStyle.Render(m.notice.message) - content = noticeBanner + "\n" + m.buildPageContent() } else { content = m.buildPageContent() } + bannerW := lipgloss.Width(header) - 2 + var topBanner string + switch { + case m.error != nil: + topBanner = m.theme.PanelError().Width(bannerW).MaxWidth(bannerW).Render(m.error.message) + case m.notice != nil: + topBanner = m.theme.PanelNotice().Width(bannerW).MaxWidth(bannerW).Render(m.notice.message) + default: + topBanner = lipgloss.NewStyle().Width(m.widthContent).Render("") + } + isViewportPage := m.currentPage == shopPage || m.currentPage == cartPage || m.currentPage == shippingPage || m.currentPage == paymentPage || m.currentPage == confirmPage || m.currentPage == accountPage @@ -104,20 +104,21 @@ func (m Model) renderString() string { var child string if isViewportPage { if breadcrumbs != "" { - child = lipgloss.JoinVertical(lipgloss.Left, header, breadcrumbs, content, footer) + child = lipgloss.JoinVertical(lipgloss.Left, topBanner, header, breadcrumbs, content, footer) } else { - child = lipgloss.JoinVertical(lipgloss.Left, header, content, footer) + child = lipgloss.JoinVertical(lipgloss.Left, topBanner, header, content, footer) } } else if breadcrumbs != "" { contentWithPadding := lipgloss.NewStyle().MarginTop(marginTop).MarginBottom(marginBottom).Height(availableContentHeight).Render(content) - child = lipgloss.JoinVertical(lipgloss.Left, header, breadcrumbs, contentWithPadding, footer) + child = lipgloss.JoinVertical(lipgloss.Left, topBanner, header, breadcrumbs, contentWithPadding, footer) } else { contentWithPadding := lipgloss.NewStyle().MarginTop(marginTop).MarginBottom(marginBottom).Height(availableContentHeight).Render(content) - child = lipgloss.JoinVertical(lipgloss.Left, header, contentWithPadding, footer) + child = lipgloss.JoinVertical(lipgloss.Left, topBanner, header, contentWithPadding, footer) } // Constrain the assembled layout to the container dimensions constrained := lipgloss.NewStyle(). + Width(m.widthContainer). MaxWidth(m.widthContainer). MaxHeight(m.heightContainer). Render(child)