package engine import ( "fmt" "strings" "github.com/fatih/color" ) // FormatTaskSummary returns a one-line summary for action feedback. // Example: `3 "Buy groceries" due:tomorrow +errand` func FormatTaskSummary(task *Task, ws *WorkingSet) string { displayID := resolveDisplayID(task, ws) parts := []string{fmt.Sprintf("%d — %q", displayID, task.Description)} if task.Due != nil { parts = append(parts, fmt.Sprintf("due:%s", FormatRelativeDate(*task.Due))) } if task.Project != nil { parts = append(parts, fmt.Sprintf("project:%s", *task.Project)) } if len(task.Tags) > 0 { for _, tag := range task.Tags { parts = append(parts, color.CyanString("+"+tag)) } } return strings.Join(parts, " ") } // FormatTaskConfirmList returns the multi-task confirmation block. // Shows up to 10 tasks, then "...and N more". func FormatTaskConfirmList(action string, tasks []*Task, ws *WorkingSet) string { var b strings.Builder limit := 10 if len(tasks) < limit { limit = len(tasks) } fmt.Fprintf(&b, "About to %s %d task(s):\n", action, len(tasks)) for i := 0; i < limit; i++ { task := tasks[i] displayID := resolveDisplayID(task, ws) line := fmt.Sprintf(" %3d %-40s", displayID, truncate(task.Description, 40)) if task.Due != nil { line += fmt.Sprintf(" due:%-10s", FormatRelativeDate(*task.Due)) } if len(task.Tags) > 0 { tags := make([]string, len(task.Tags)) for j, tag := range task.Tags { tags[j] = "+" + tag } line += " " + strings.Join(tags, " ") } fmt.Fprintln(&b, line) } if len(tasks) > 10 { fmt.Fprintf(&b, " ...and %d more\n", len(tasks)-10) } return b.String() } // FormatAddFeedback returns the detailed post-add feedback block. func FormatAddFeedback(task *Task, displayID int) string { var b strings.Builder fmt.Fprintf(&b, "Created task %d — %q\n", displayID, task.Description) if task.Due != nil { fmt.Fprintf(&b, " Due: %s\n", FormatDateWithRelative(*task.Due)) } if task.Project != nil { fmt.Fprintf(&b, " Project: %s\n", *task.Project) } if task.Priority != PriorityDefault { fmt.Fprintf(&b, " Priority: %s\n", priorityIntToString(task.Priority)) } if task.Scheduled != nil { fmt.Fprintf(&b, " Scheduled: %s\n", FormatDateWithRelative(*task.Scheduled)) } if task.Wait != nil { fmt.Fprintf(&b, " Wait: %s\n", FormatDateWithRelative(*task.Wait)) } if len(task.Tags) > 0 { tags := make([]string, len(task.Tags)) for i, tag := range task.Tags { tags[i] = "+" + tag } fmt.Fprintf(&b, " Tags: %s\n", strings.Join(tags, " ")) } return b.String() } // FormatRecurringAddFeedback returns feedback for a newly created recurring task. func FormatRecurringAddFeedback(instance *Task, displayID int) string { var b strings.Builder fmt.Fprintf(&b, "Created recurring task %d — %q\n", displayID, instance.Description) if instance.RecurrenceDuration != nil { fmt.Fprintf(&b, " Recurrence: %s\n", FormatRecurrenceDuration(*instance.RecurrenceDuration)) } else if instance.ParentUUID != nil { // Instance: get recurrence from parent parent, err := GetTask(*instance.ParentUUID) if err == nil && parent.RecurrenceDuration != nil { fmt.Fprintf(&b, " Recurrence: %s\n", FormatRecurrenceDuration(*parent.RecurrenceDuration)) } } if instance.Due != nil { fmt.Fprintf(&b, " Due: %s\n", FormatDateWithRelative(*instance.Due)) } if len(instance.Tags) > 0 { tags := make([]string, len(instance.Tags)) for i, tag := range instance.Tags { tags[i] = "+" + tag } fmt.Fprintf(&b, " Tags: %s\n", strings.Join(tags, " ")) } return b.String() } // FormatCompletionFeedback returns completion feedback with recurrence info. func FormatCompletionFeedback(task *Task, displayID int, nextInstance *Task) string { var b strings.Builder fmt.Fprintf(&b, "Completed task %d — %q\n", displayID, task.Description) if nextInstance != nil { if nextInstance.Due != nil { fmt.Fprintf(&b, "Next instance created — due: %s\n", FormatDateWithRelative(*nextInstance.Due)) } else { fmt.Fprintf(&b, "Next instance created\n") } } return b.String() } func resolveDisplayID(task *Task, ws *WorkingSet) int { if ws == nil { return 0 } for id, uuid := range ws.byID { if uuid == task.UUID { return id } } return 0 } func truncate(s string, max int) string { if len(s) <= max { return s } return s[:max-1] + "…" }