package tui import ( "fmt" "strings" "time" "github.com/charmbracelet/lipgloss" "git.unkin.net/unkin/forgebot/pkg/models" ) func renderCard(task models.Task, width int, selected bool) string { color := statusColors[task.Status] style := cardStyle.Width(width - 4) if selected { style = cardSelectedStyle.Width(width - 4).BorderForeground(color) } cmd := lipgloss.NewStyle().Bold(true).Render(task.Command) repo := task.Repository maxRepo := width - 6 if maxRepo > 0 && len(repo) > maxRepo { repo = repo[:maxRepo-1] + "…" } repo = dimStyle.Render(repo) var ref string if task.IssueNumber > 0 { ref = fmt.Sprintf("#%d", task.IssueNumber) } else if task.PRNumber > 0 { ref = fmt.Sprintf("PR#%d", task.PRNumber) } if task.Ref != "" { if ref != "" { ref += " " + task.Ref } else { ref = task.Ref } } ref = dimStyle.Render(ref) elapsed := relativeTime(task.CreatedAt) if task.Status == models.StatusInProgress && task.StartedAt != nil { elapsed = relativeTime(*task.StartedAt) } if (task.Status == models.StatusDone || task.Status == models.StatusWontdo) && task.CompletedAt != nil { elapsed = relativeTime(*task.CompletedAt) } bottomLine := elapsed if task.Author != "" { pad := width - 6 - len(elapsed) - len(task.Author) if pad < 1 { pad = 1 } bottomLine = elapsed + strings.Repeat(" ", pad) + task.Author } bottomLine = dimStyle.Render(bottomLine) content := lipgloss.JoinVertical(lipgloss.Left, cmd, repo, ref, bottomLine) if task.Status == models.StatusWontdo { content = dimStyle.Render(content) } if task.ErrorMessage != "" && task.Status == models.StatusTodo { errHint := task.ErrorMessage if len(errHint) > width-6 { errHint = errHint[:width-7] + "…" } content += "\n" + errStyle.Render(errHint) } return style.Render(content) } func relativeTime(t time.Time) string { d := time.Since(t) switch { case d < time.Minute: return "just now" case d < time.Hour: return fmt.Sprintf("%dm ago", int(d.Minutes())) case d < 24*time.Hour: return fmt.Sprintf("%dh ago", int(d.Hours())) default: return fmt.Sprintf("%dd ago", int(d.Hours()/24)) } }