diff --git a/cmd/delete.go b/cmd/delete.go index 65393cd..411e17e 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -120,17 +120,17 @@ var deleteCmd = &cobra.Command{ fmt.Println() ui.PrintWarning(ui.EmojiWarning, "You are about to delete this entry:") fmt.Println() - ui.PrintInfo(4, "ID", fmt.Sprintf("%d", selectedEntry.ID)) - ui.PrintInfo(4, "Project", selectedEntry.ProjectName) - ui.PrintInfo(4, "Start", selectedEntry.StartTime.Format("Jan 2, 2006 at 3:04 PM")) + ui.PrintInfo(4, ui.Bold("ID"), fmt.Sprintf("%d", selectedEntry.ID)) + ui.PrintInfo(4, ui.Bold("Project"), selectedEntry.ProjectName) + ui.PrintInfo(4, ui.Bold("Start"), selectedEntry.StartTime.Format("Jan 2, 2006 at 3:04 PM")) if selectedEntry.EndTime != nil { - ui.PrintInfo(4, "End", selectedEntry.EndTime.Format("Jan 2, 2006 at 3:04 PM")) - ui.PrintInfo(4, "Duration", ui.FormatDuration(selectedEntry.Duration())) + ui.PrintInfo(4, ui.Bold("End"), selectedEntry.EndTime.Format("Jan 2, 2006 at 3:04 PM")) + ui.PrintInfo(4, ui.Bold("Duration"), ui.FormatDuration(selectedEntry.Duration())) } else { - ui.PrintInfo(4, "Status", ui.Warning("Running")) + ui.PrintInfo(4, ui.Bold("Status"), ui.Warning("Running")) } if selectedEntry.Description != "" { - ui.PrintInfo(4, "Description", selectedEntry.Description) + ui.PrintInfo(4, ui.Bold("Description"), selectedEntry.Description) } fmt.Println() diff --git a/cmd/edit.go b/cmd/edit.go index fb1b7bd..75fef93 100644 --- a/cmd/edit.go +++ b/cmd/edit.go @@ -251,7 +251,7 @@ var editCmd = &cobra.Command{ // Show confirmation with diff fmt.Println() - ui.PrintInfo(0, "Changes to entry", fmt.Sprintf("#%d", selectedEntry.ID)) + ui.PrintInfo(0, ui.Bold("Changes to entry"), fmt.Sprintf("#%d", selectedEntry.ID)) fmt.Println() hasChanges := false @@ -260,19 +260,19 @@ var editCmd = &cobra.Command{ hasChanges = true oldStr := selectedEntry.StartTime.Format("01-02-2006 3:04 PM") newStr := editedEntry.StartTime.Format("01-02-2006 3:04 PM") - fmt.Printf(" Start time: %s → %s\n", ui.Muted(oldStr), newStr) + fmt.Printf(" %s %s → %s\n", ui.Bold("Start time:"), ui.Muted(oldStr), newStr) } if !selectedEntry.EndTime.Equal(*editedEntry.EndTime) { hasChanges = true oldStr := selectedEntry.EndTime.Format("01-02-2006 3:04 PM") newStr := editedEntry.EndTime.Format("01-02-2006 3:04 PM") - fmt.Printf(" End time: %s → %s\n", ui.Muted(oldStr), newStr) + fmt.Printf(" %s %s → %s\n", ui.Bold("End time:"), ui.Muted(oldStr), newStr) } if selectedEntry.Description != editedEntry.Description { hasChanges = true - fmt.Printf(" Description: %s → %s\n", ui.Muted(fmt.Sprintf("%q", selectedEntry.Description)), fmt.Sprintf("%q", editedEntry.Description)) + fmt.Printf(" %s %s → %s\n", ui.Bold("Description:"), ui.Muted(fmt.Sprintf("%q", selectedEntry.Description)), fmt.Sprintf("%q", editedEntry.Description)) } if !hasChanges { diff --git a/cmd/export.go b/cmd/export.go index 0500898..6915492 100644 --- a/cmd/export.go +++ b/cmd/export.go @@ -101,7 +101,7 @@ var exportCmd = &cobra.Command{ os.Exit(1) } - ui.PrintSuccess(ui.EmojiExport, fmt.Sprintf("Exported %d entries to %s", len(entries), filename)) + ui.PrintSuccess(ui.EmojiExport, fmt.Sprintf("Exported %s to %s", ui.Bold(fmt.Sprintf("%d entries", len(entries))), ui.Bold(filename))) ui.NewlineBelow() }, diff --git a/cmd/init.go b/cmd/init.go index f1901b3..0864290 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -107,12 +107,12 @@ var initCmd = &cobra.Command{ } fmt.Println() - ui.PrintSuccess(ui.EmojiSuccess, fmt.Sprintf("Created .tmporc for project '%s'", name)) + ui.PrintSuccess(ui.EmojiSuccess, fmt.Sprintf("Created .tmporc for project %s", ui.Bold(name))) if hourlyRate > 0 { - ui.PrintInfo(4, "Hourly Rate", fmt.Sprintf("$%.2f", hourlyRate)) + ui.PrintInfo(4, ui.Bold("Hourly Rate"), fmt.Sprintf("$%.2f", hourlyRate)) } if description != "" { - ui.PrintInfo(4, "Description", description) + ui.PrintInfo(4, ui.Bold("Description"), description) } fmt.Println() diff --git a/cmd/log.go b/cmd/log.go index 801cdf9..7ee735a 100644 --- a/cmd/log.go +++ b/cmd/log.go @@ -79,7 +79,7 @@ var logCmd = &cobra.Command{ fmt.Println() } - fmt.Println(ui.Muted(fmt.Sprintf("─── %s ───", entryDate))) + fmt.Println(ui.Bold(ui.Muted(fmt.Sprintf("─── %s ───", entryDate)))) currentDate = entryDate } @@ -93,7 +93,7 @@ var logCmd = &cobra.Command{ timeRange += ui.Warning("(running)") + " " } - fmt.Printf(" %s %-20s %s\n", timeRange, entry.ProjectName, ui.FormatDuration(duration)) + fmt.Printf(" %s %s %s\n", timeRange, ui.Bold(fmt.Sprintf("%-20s", entry.ProjectName)), ui.FormatDuration(duration)) if entry.Description != "" { fmt.Printf(" %s %s\n", ui.Muted("└─"), entry.Description) } @@ -101,7 +101,7 @@ var logCmd = &cobra.Command{ fmt.Println() ui.PrintSeparator() - fmt.Printf("%s %s\n", ui.Info("Total Time:"), ui.FormatDuration(totalDuration)) + fmt.Printf("%s %s\n", ui.BoldInfo("Total Time:"), ui.Bold(ui.FormatDuration(totalDuration))) ui.NewlineBelow() }, diff --git a/cmd/manual.go b/cmd/manual.go index f7a2e4c..40587f1 100644 --- a/cmd/manual.go +++ b/cmd/manual.go @@ -156,19 +156,19 @@ var manualCmd = &cobra.Command{ duration := entry.Duration() fmt.Println() - ui.PrintSuccess(ui.EmojiSuccess, fmt.Sprintf("Created manual entry for '%s'", entry.ProjectName)) - ui.PrintInfo(4, "Start", startTime.Format("Jan 2, 2006 at 3:04 PM")) - ui.PrintInfo(4, "End", endTime.Format("Jan 2, 2006 at 3:04 PM")) - ui.PrintInfo(4, "Duration", ui.FormatDuration(duration)) + ui.PrintSuccess(ui.EmojiSuccess, fmt.Sprintf("Created manual entry for %s", ui.Bold(entry.ProjectName))) + ui.PrintInfo(4, ui.Bold("Start"), startTime.Format("Jan 2, 2006 at 3:04 PM")) + ui.PrintInfo(4, ui.Bold("End"), endTime.Format("Jan 2, 2006 at 3:04 PM")) + ui.PrintInfo(4, ui.Bold("Duration"), ui.FormatDuration(duration)) if entry.Description != "" { - ui.PrintInfo(4, "Description", entry.Description) + ui.PrintInfo(4, ui.Bold("Description"), entry.Description) } if entry.HourlyRate != nil { earnings := duration.Hours() * *entry.HourlyRate - fmt.Printf(" %s %s\n", ui.Info("Hourly Rate:"), fmt.Sprintf("$%.2f", *entry.HourlyRate)) - fmt.Printf(" %s %s\n", ui.Info("Earnings:"), fmt.Sprintf("$%.2f", earnings)) + fmt.Printf(" %s %s\n", ui.BoldInfo("Hourly Rate:"), fmt.Sprintf("$%.2f", *entry.HourlyRate)) + fmt.Printf(" %s %s\n", ui.BoldInfo("Earnings:"), fmt.Sprintf("$%.2f", earnings)) } ui.NewlineBelow() diff --git a/cmd/start.go b/cmd/start.go index e34a9e8..b641c7c 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -62,7 +62,7 @@ var startCmd = &cobra.Command{ os.Exit(1) } - ui.PrintSuccess(ui.EmojiStart, fmt.Sprintf("Started tracking time for '%s'", entry.ProjectName)) + ui.PrintSuccess(ui.EmojiStart, fmt.Sprintf("Started tracking time for %s", ui.Bold(entry.ProjectName))) if cfg, _, err := config.FindAndLoad(); err == nil && cfg != nil { ui.PrintMuted(4, "└─ Config Source: .tmporc") diff --git a/cmd/stats.go b/cmd/stats.go index f5cd1e5..5dee16e 100644 --- a/cmd/stats.go +++ b/cmd/stats.go @@ -110,20 +110,20 @@ func ShowPeriodStats(entries []*storage.TimeEntry, periodName string) { } } - ui.PrintSuccess(ui.EmojiStats, fmt.Sprintf("Stats for %s", periodName)) + ui.PrintSuccess(ui.EmojiStats, fmt.Sprintf("Stats for %s", ui.Bold(periodName))) fmt.Println() - ui.PrintInfo(4, "Total Time", fmt.Sprintf("%s (%.2f hours)", ui.FormatDuration(totalDuration), totalDuration.Hours())) - ui.PrintInfo(4, "Total Entries", fmt.Sprintf("%d", len(entries))) + ui.PrintInfo(4, ui.Bold("Total Time"), fmt.Sprintf("%s (%.2f hours)", ui.FormatDuration(totalDuration), totalDuration.Hours())) + ui.PrintInfo(4, ui.Bold("Total Entries"), fmt.Sprintf("%d", len(entries))) if hasAnyEarnings { - ui.PrintInfo(4, "Earnings", fmt.Sprintf("$%.2f", totalEarnings)) + ui.PrintInfo(4, ui.Bold("Earnings"), fmt.Sprintf("$%.2f", totalEarnings)) } fmt.Println() - ui.PrintInfo(4, "By Project", "") + ui.PrintInfo(4, ui.Bold("By Project"), "") for project, duration := range projectStats { percentage := (duration.Seconds() / totalDuration.Seconds()) * 100 - fmt.Printf(" %-20s %s (%.1f%%)\n", project, ui.FormatDuration(duration), percentage) + fmt.Printf(" %s %s (%.1f%%)\n", ui.Bold(fmt.Sprintf("%-20s", project)), ui.FormatDuration(duration), percentage) if earnings, ok := projectEarnings[project]; ok && earnings > 0 { fmt.Printf(" %s %s\n", ui.Muted("└─ Earnings:"), fmt.Sprintf("$%.2f", earnings)) @@ -176,20 +176,20 @@ func ShowAllTimeStats(entries []*storage.TimeEntry, db *storage.Database) { projects, _ := db.GetAllProjects() - ui.PrintSuccess(ui.EmojiStats, "All-Time Statistics") - ui.PrintInfo(4, "Total Time", fmt.Sprintf("%s (%.2f hours)", ui.FormatDuration(totalDuration), totalDuration.Hours())) - ui.PrintInfo(4, "Total Entries", fmt.Sprintf("%d", len(entries))) - ui.PrintInfo(4, "Projects Tracked", fmt.Sprintf("%d", len(projects))) + ui.PrintSuccess(ui.EmojiStats, ui.Bold("All-Time Statistics")) + ui.PrintInfo(4, ui.Bold("Total Time"), fmt.Sprintf("%s (%.2f hours)", ui.FormatDuration(totalDuration), totalDuration.Hours())) + ui.PrintInfo(4, ui.Bold("Total Entries"), fmt.Sprintf("%d", len(entries))) + ui.PrintInfo(4, ui.Bold("Projects Tracked"), fmt.Sprintf("%d", len(projects))) if hasAnyEarnings { - ui.PrintInfo(4, "Earnings", fmt.Sprintf("$%.2f", totalEarnings)) + ui.PrintInfo(4, ui.Bold("Earnings"), fmt.Sprintf("$%.2f", totalEarnings)) } fmt.Println() - ui.PrintInfo(4, "By Project", "") + ui.PrintInfo(4, ui.Bold("By Project"), "") for project, duration := range projectStats { percentage := (duration.Seconds() / totalDuration.Seconds()) * 100 - fmt.Printf(" %-20s %s (%.1f%%)\n", project, ui.FormatDuration(duration), percentage) + fmt.Printf(" %s %s (%.1f%%)\n", ui.Bold(fmt.Sprintf("%-20s", project)), ui.FormatDuration(duration), percentage) if earnings, ok := projectEarnings[project]; ok && earnings > 0 { fmt.Printf(" %s %s\n", ui.Muted("└─ Earnings:"), fmt.Sprintf("$%.2f", earnings)) diff --git a/cmd/status.go b/cmd/status.go index 3b84d7a..8363438 100644 --- a/cmd/status.go +++ b/cmd/status.go @@ -42,12 +42,12 @@ var statusCmd = &cobra.Command{ duration := time.Since(running.StartTime) - ui.PrintSuccess(ui.EmojiStatus, fmt.Sprintf("Currently tracking: %s", running.ProjectName)) - ui.PrintInfo(4, "Started", running.StartTime.Format("3:04 PM")) - ui.PrintInfo(4, "Duration", ui.FormatDuration(duration)) + ui.PrintSuccess(ui.EmojiStatus, fmt.Sprintf("Currently tracking: %s", ui.Bold(running.ProjectName))) + ui.PrintInfo(4, ui.Bold("Started"), running.StartTime.Format("3:04 PM")) + ui.PrintInfo(4, ui.Bold("Duration"), ui.FormatDuration(duration)) if running.Description != "" { - ui.PrintInfo(4, "Description", running.Description) + ui.PrintInfo(4, ui.Bold("Description"), running.Description) } ui.NewlineBelow() diff --git a/cmd/stop.go b/cmd/stop.go index 020aacf..dc6cb40 100644 --- a/cmd/stop.go +++ b/cmd/stop.go @@ -45,8 +45,8 @@ var stopCmd = &cobra.Command{ duration := time.Since(running.StartTime) - ui.PrintSuccess(ui.EmojiStop, fmt.Sprintf("Stopped tracking '%s'", running.ProjectName)) - ui.PrintInfo(4, "Total Duration", ui.FormatDuration(duration)) + ui.PrintSuccess(ui.EmojiStop, fmt.Sprintf("Stopped tracking %s", ui.Bold(running.ProjectName))) + ui.PrintInfo(4, ui.Bold("Total Duration"), ui.FormatDuration(duration)) ui.NewlineBelow() }, diff --git a/internal/ui/ui.go b/internal/ui/ui.go index 00ca277..36fe5b9 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -23,6 +23,11 @@ const ( FormatDim = "\033[2m" FormatItalic = "\033[3m" FormatUnderline = "\033[4m" + + // Specific reset codes (don't reset colors) + ResetBoldDim = "\033[22m" // Reset bold and dim + ResetItalic = "\033[23m" // Reset italic + ResetUnderline = "\033[24m" // Reset underline ) // Emoji Constants @@ -68,22 +73,22 @@ func Muted(message string) string { // Bold text formatting functions that return formatted string func Bold(message string) string { - return FormatBold + message + ColorReset + return FormatBold + message + ResetBoldDim } // Dim text formatting functions that return formatted string func Dim(message string) string { - return FormatDim + message + ColorReset + return FormatDim + message + ResetBoldDim } // Italic text formatting functions that return formatted string func Italic(message string) string { - return FormatItalic + message + ColorReset + return FormatItalic + message + ResetItalic } // Underline text formatting functions that return formatted string func Underline(message string) string { - return FormatUnderline + message + ColorReset + return FormatUnderline + message + ResetUnderline } // Bold success combined formatting functions for common use cases diff --git a/internal/ui/ui_test.go b/internal/ui/ui_test.go index 49444f2..9a6b8c8 100644 --- a/internal/ui/ui_test.go +++ b/internal/ui/ui_test.go @@ -49,28 +49,28 @@ func TestFormattingFunctions(t *testing.T) { result := Bold("test") assert.Contains(t, result, FormatBold) assert.Contains(t, result, "test") - assert.Contains(t, result, ColorReset) + assert.Contains(t, result, ResetBoldDim) }) t.Run("Dim adds dim formatting", func(t *testing.T) { result := Dim("test") assert.Contains(t, result, FormatDim) assert.Contains(t, result, "test") - assert.Contains(t, result, ColorReset) + assert.Contains(t, result, ResetBoldDim) }) t.Run("Italic adds italic formatting", func(t *testing.T) { result := Italic("test") assert.Contains(t, result, FormatItalic) assert.Contains(t, result, "test") - assert.Contains(t, result, ColorReset) + assert.Contains(t, result, ResetItalic) }) t.Run("Underline adds underline formatting", func(t *testing.T) { result := Underline("test") assert.Contains(t, result, FormatUnderline) assert.Contains(t, result, "test") - assert.Contains(t, result, ColorReset) + assert.Contains(t, result, ResetUnderline) }) } @@ -180,6 +180,9 @@ func TestConstants(t *testing.T) { assert.NotEmpty(t, FormatDim) assert.NotEmpty(t, FormatItalic) assert.NotEmpty(t, FormatUnderline) + assert.NotEmpty(t, ResetBoldDim) + assert.NotEmpty(t, ResetItalic) + assert.NotEmpty(t, ResetUnderline) }) t.Run("Emoji constants are defined", func(t *testing.T) {