feat: many features, check TODO.md

This commit is contained in:
2025-07-19 00:45:21 +03:00
parent 3556b06bb9
commit e35126856d
50 changed files with 6996 additions and 674 deletions

173
cli/ui.go Normal file
View File

@@ -0,0 +1,173 @@
package cli
import (
"fmt"
"io"
"os"
"time"
"github.com/fatih/color"
"github.com/schollz/progressbar/v3"
)
// UIManager handles CLI user interface elements.
type UIManager struct {
enableColors bool
enableProgress bool
progressBar *progressbar.ProgressBar
output io.Writer
}
// NewUIManager creates a new UI manager.
func NewUIManager() *UIManager {
return &UIManager{
enableColors: isColorTerminal(),
enableProgress: isInteractiveTerminal(),
output: os.Stderr, // Progress and colors go to stderr
}
}
// SetColorOutput enables or disables colored output.
func (ui *UIManager) SetColorOutput(enabled bool) {
ui.enableColors = enabled
color.NoColor = !enabled
}
// SetProgressOutput enables or disables progress bars.
func (ui *UIManager) SetProgressOutput(enabled bool) {
ui.enableProgress = enabled
}
// StartProgress initializes a progress bar for file processing.
func (ui *UIManager) StartProgress(total int, description string) {
if !ui.enableProgress || total <= 0 {
return
}
ui.progressBar = progressbar.NewOptions(total,
progressbar.OptionSetWriter(ui.output),
progressbar.OptionSetDescription(description),
progressbar.OptionSetTheme(progressbar.Theme{
Saucer: color.GreenString("█"),
SaucerHead: color.GreenString("█"),
SaucerPadding: " ",
BarStart: "[",
BarEnd: "]",
}),
progressbar.OptionShowCount(),
progressbar.OptionShowIts(),
progressbar.OptionSetWidth(40),
progressbar.OptionThrottle(100*time.Millisecond),
progressbar.OptionOnCompletion(func() {
_, _ = fmt.Fprint(ui.output, "\n")
}),
progressbar.OptionSetRenderBlankState(true),
)
}
// UpdateProgress increments the progress bar.
func (ui *UIManager) UpdateProgress(increment int) {
if ui.progressBar != nil {
_ = ui.progressBar.Add(increment)
}
}
// FinishProgress completes the progress bar.
func (ui *UIManager) FinishProgress() {
if ui.progressBar != nil {
_ = ui.progressBar.Finish()
ui.progressBar = nil
}
}
// PrintSuccess prints a success message in green.
func (ui *UIManager) PrintSuccess(format string, args ...interface{}) {
if ui.enableColors {
color.Green("✓ "+format, args...)
} else {
ui.printf("✓ "+format+"\n", args...)
}
}
// PrintError prints an error message in red.
func (ui *UIManager) PrintError(format string, args ...interface{}) {
if ui.enableColors {
color.Red("✗ "+format, args...)
} else {
ui.printf("✗ "+format+"\n", args...)
}
}
// PrintWarning prints a warning message in yellow.
func (ui *UIManager) PrintWarning(format string, args ...interface{}) {
if ui.enableColors {
color.Yellow("⚠ "+format, args...)
} else {
ui.printf("⚠ "+format+"\n", args...)
}
}
// PrintInfo prints an info message in blue.
func (ui *UIManager) PrintInfo(format string, args ...interface{}) {
if ui.enableColors {
color.Blue(" "+format, args...)
} else {
ui.printf(" "+format+"\n", args...)
}
}
// PrintHeader prints a header message in bold.
func (ui *UIManager) PrintHeader(format string, args ...interface{}) {
if ui.enableColors {
_, _ = color.New(color.Bold).Fprintf(ui.output, format+"\n", args...)
} else {
ui.printf(format+"\n", args...)
}
}
// isColorTerminal checks if the terminal supports colors.
func isColorTerminal() bool {
// Check common environment variables
term := os.Getenv("TERM")
if term == "" || term == "dumb" {
return false
}
// Check for CI environments that typically don't support colors
if os.Getenv("CI") != "" {
// GitHub Actions supports colors
if os.Getenv("GITHUB_ACTIONS") == "true" {
return true
}
// Most other CI systems don't
return false
}
// Check if NO_COLOR is set (https://no-color.org/)
if os.Getenv("NO_COLOR") != "" {
return false
}
// Check if FORCE_COLOR is set
if os.Getenv("FORCE_COLOR") != "" {
return true
}
// Default to true for interactive terminals
return isInteractiveTerminal()
}
// isInteractiveTerminal checks if we're running in an interactive terminal.
func isInteractiveTerminal() bool {
// Check if stderr is a terminal (where we output progress/colors)
fileInfo, err := os.Stderr.Stat()
if err != nil {
return false
}
return (fileInfo.Mode() & os.ModeCharDevice) != 0
}
// printf is a helper that ignores printf errors (for UI output).
func (ui *UIManager) printf(format string, args ...interface{}) {
_, _ = fmt.Fprintf(ui.output, format, args...)
}