summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoradamdottv <[email protected]>2025-06-19 10:45:10 -0500
committeradamdottv <[email protected]>2025-06-19 10:45:10 -0500
commitf8a7cd372d1cbf9ddc82c447d14c3d4fef9daf8f (patch)
tree3eb04846ee29d9c946072d1a7a239ea7e91a50d7
parentf48eac638d27bb81ba2f6681622755130b5a1261 (diff)
downloadopencode-f8a7cd372d1cbf9ddc82c447d14c3d4fef9daf8f.tar.gz
opencode-f8a7cd372d1cbf9ddc82c447d14c3d4fef9daf8f.zip
fix(tui): toast placement and overlay rendering
-rw-r--r--packages/tui/internal/components/toast/toast.go129
1 files changed, 78 insertions, 51 deletions
diff --git a/packages/tui/internal/components/toast/toast.go b/packages/tui/internal/components/toast/toast.go
index 9c13f4d9c..6612b8e52 100644
--- a/packages/tui/internal/components/toast/toast.go
+++ b/packages/tui/internal/components/toast/toast.go
@@ -86,77 +86,104 @@ func (tm *ToastManager) Update(msg tea.Msg) (*ToastManager, tea.Cmd) {
return tm, nil
}
+// renderSingleToast renders a single toast notification
+func (tm *ToastManager) renderSingleToast(toast Toast) string {
+ t := theme.CurrentTheme()
+
+ baseStyle := styles.BaseStyle().
+ Background(t.BackgroundElement()).
+ Foreground(t.Text()).
+ Padding(1, 2).
+ BorderStyle(lipgloss.ThickBorder()).
+ BorderBackground(t.Background()).
+ BorderForeground(toast.Color).
+ BorderLeft(true).
+ BorderRight(true)
+
+ maxWidth := max(40, layout.Current.Viewport.Width/3)
+ contentMaxWidth := max(maxWidth-6, 20)
+
+ // Build content with wrapping
+ var content strings.Builder
+ if toast.Title != nil {
+ titleStyle := lipgloss.NewStyle().
+ Foreground(toast.Color).
+ Bold(true)
+ content.WriteString(titleStyle.Render(*toast.Title))
+ content.WriteString("\n")
+ }
+
+ // Wrap message text
+ messageStyle := lipgloss.NewStyle()
+ contentWidth := lipgloss.Width(toast.Message)
+ if contentWidth > contentMaxWidth {
+ messageStyle = messageStyle.Width(contentMaxWidth)
+ }
+ content.WriteString(messageStyle.Render(toast.Message))
+
+ // Render toast with max width
+ return baseStyle.MaxWidth(maxWidth).Render(content.String())
+}
+
// View renders all active toasts
func (tm *ToastManager) View() string {
if len(tm.toasts) == 0 {
return ""
}
- t := theme.CurrentTheme()
-
var toastViews []string
for _, toast := range tm.toasts {
- baseStyle := styles.BaseStyle().
- Background(t.BackgroundElement()).
- Foreground(t.Text()).
- Padding(1, 2).
- BorderStyle(lipgloss.ThickBorder()).
- BorderBackground(t.Background()).
- BorderForeground(toast.Color).
- BorderLeft(true).
- BorderRight(true)
-
- maxWidth := max(40, layout.Current.Viewport.Width/3)
- contentMaxWidth := max(maxWidth-6, 20)
-
- // Build content with wrapping
- var content strings.Builder
- if toast.Title != nil {
- titleStyle := lipgloss.NewStyle().
- Foreground(toast.Color).
- Bold(true)
- content.WriteString(titleStyle.Render(*toast.Title))
- content.WriteString("\n")
- }
-
- // Wrap message text
- messageStyle := lipgloss.NewStyle().Width(contentMaxWidth)
- content.WriteString(messageStyle.Render(toast.Message))
-
- // Render toast with max width
- toastView := baseStyle.MaxWidth(maxWidth).Render(content.String())
- toastViews = append(toastViews, toastView)
+ toastView := tm.renderSingleToast(toast)
+ toastViews = append(toastViews, toastView+"\n")
}
- // Stack toasts vertically with small gap
- return strings.Join(toastViews, "\n\n")
+ t := theme.CurrentTheme()
+ content := lipgloss.JoinVertical(lipgloss.Right, toastViews...)
+ return lipgloss.NewStyle().Background(t.Background()).Render(content)
}
// RenderOverlay renders the toasts as an overlay on the given background
func (tm *ToastManager) RenderOverlay(background string) string {
- toastView := tm.View()
- if toastView == "" {
+ if len(tm.toasts) == 0 {
return background
}
- // Calculate position (bottom right with padding)
bgWidth := lipgloss.Width(background)
bgHeight := lipgloss.Height(background)
- toastWidth := lipgloss.Width(toastView)
- toastHeight := lipgloss.Height(toastView)
-
- // Position with 2 character padding from edges
- x := bgWidth - toastWidth - 2
- y := bgHeight - toastHeight - 2
-
- // Ensure we don't go negative
- if x < 0 {
- x = 0
- }
- if y < 0 {
- y = 0
+ result := background
+
+ // Start from top with 2 character padding
+ currentY := 2
+
+ // Render each toast individually
+ for _, toast := range tm.toasts {
+ // Render individual toast
+ toastView := tm.renderSingleToast(toast)
+ toastWidth := lipgloss.Width(toastView)
+ toastHeight := lipgloss.Height(toastView)
+
+ // Position at top-right with 2 character padding from right edge
+ x := bgWidth - toastWidth - 2
+
+ // Ensure we don't go negative
+ if x < 0 {
+ x = 0
+ }
+
+ // Check if toast fits vertically
+ if currentY + toastHeight > bgHeight - 2 {
+ // No more room for toasts
+ break
+ }
+
+ // Place this toast
+ result = layout.PlaceOverlay(x, currentY, toastView, result)
+
+ // Move down for next toast (add 1 for spacing between toasts)
+ currentY += toastHeight + 1
}
- return layout.PlaceOverlay(x, y, toastView, background)
+
+ return result
}
type ToastOptions struct {