فهرست منبع

fix(tui): toast placement and overlay rendering

adamdottv 8 ماه پیش
والد
کامیت
f8a7cd372d
1فایلهای تغییر یافته به همراه78 افزوده شده و 51 حذف شده
  1. 78 51
      packages/tui/internal/components/toast/toast.go

+ 78 - 51
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 {