diff options
-rw-r--r-- | flags.go | 6 | ||||
-rw-r--r-- | internal/switcher/switcher.go | 21 | ||||
-rw-r--r-- | internal/userinput/userinput.go | 78 | ||||
-rw-r--r-- | main.go | 36 |
4 files changed, 136 insertions, 5 deletions
@@ -13,4 +13,10 @@ var AppFlags []cli.Flag = []cli.Flag{ Usage: "Log file", Value: "/dev/null", // Don't log by default }, + &cli.StringFlag{ + Name: "color", + Aliases: []string{"c"}, + Usage: "Initial color", + Value: "", + }, } diff --git a/internal/switcher/switcher.go b/internal/switcher/switcher.go index fce242c..ec5c47a 100644 --- a/internal/switcher/switcher.go +++ b/internal/switcher/switcher.go @@ -51,8 +51,29 @@ func (m *Model) Prev() int { return m.active } +func (m *Model) SetActive(i int) { + m.active = m.fixSel(i) +} + +func (m *Model) UpdatePicker(i int, c colors.ColorSpace) { + m.pickers[i].SetColor(c) +} + +func (m *Model) NewNotice(msg string) tea.Cmd { + return m.notices.New(msg) +} + func (m Model) Init() tea.Cmd { cmds := []tea.Cmd{} + + // Make a backup of notices received before the program starts + // then reinitialize them with a proper expiration time. + noticeBackup := m.notices.Notices + m.notices = notices.New() + for _, v := range noticeBackup { + cmds = append(cmds, m.NewNotice(v)) + } + for _, p := range m.pickers { cmds = append(cmds, p.Init()) } diff --git a/internal/userinput/userinput.go b/internal/userinput/userinput.go new file mode 100644 index 0000000..8f1ecdc --- /dev/null +++ b/internal/userinput/userinput.go @@ -0,0 +1,78 @@ +package userinput + +import ( + "errors" + "fmt" + "strings" + + "github.com/ChausseBenjamin/termpicker/internal/colors" +) + +var ( + errUnknownColorFormat = errors.New("Unrecognized color format") + errHexParsing = errors.New("Failed to parse hex color") + errRGBParsing = errors.New("Failed to parse RGB color") + errHSLParsing = errors.New("Failed to parse HSL color") + errCMYKParsing = errors.New("Failed to parse CMYK color") +) + +func sanitize(s string) string { + s = strings.ReplaceAll(s, "\"", "") + s = strings.ReplaceAll(s, "%", "") + s = strings.ReplaceAll(s, "°", "") + s = strings.TrimSpace(s) + s = strings.ToLower(s) + return s +} + +func ParseColor(s string) (colors.ColorSpace, error) { + s = sanitize(s) + switch { + case strings.Contains(s, "#"): + return parseHex(s) + case strings.Contains(s, "rgb"): + return parseRGB(s) + case strings.Contains(s, "hsl"): + return parseHSL(s) + case strings.Contains(s, "cmyk"): + return parseCMYK(s) + default: + return nil, errUnknownColorFormat + } +} + +func parseRGB(s string) (colors.ColorSpace, error) { + var r, g, b int + _, err := fmt.Sscanf(s, "rgb(%d,%d,%d)", &r, &g, &b) + if err != nil { + return nil, errors.Join(errRGBParsing, err) + } + return colors.RGB{R: r, G: g, B: b}, nil +} + +func parseHex(s string) (colors.ColorSpace, error) { + var r, g, b int + _, err := fmt.Sscanf(s, "#%02x%02x%02x", &r, &g, &b) + if err != nil { + return nil, errors.Join(errHexParsing, err) + } + return colors.RGB{R: r, G: g, B: b}, nil +} + +func parseCMYK(s string) (colors.ColorSpace, error) { + var c, m, y, k int + _, err := fmt.Sscanf(s, "cmyk(%d,%d,%d,%d)", &c, &m, &y, &k) + if err != nil { + return nil, errors.Join(errCMYKParsing, err) + } + return colors.CMYK{C: c, M: m, Y: y, K: k}, nil +} + +func parseHSL(str string) (colors.ColorSpace, error) { + var h, s, l int + _, err := fmt.Sscanf(str, "hsl(%d,%d,%d)", &h, &s, &l) + if err != nil { + return nil, errors.Join(errHSLParsing, err) + } + return colors.HSL{H: h, S: s, L: l}, nil +} @@ -4,8 +4,10 @@ import ( "log/slog" "os" + "github.com/ChausseBenjamin/termpicker/internal/colors" "github.com/ChausseBenjamin/termpicker/internal/picker" "github.com/ChausseBenjamin/termpicker/internal/switcher" + "github.com/ChausseBenjamin/termpicker/internal/userinput" "github.com/ChausseBenjamin/termpicker/internal/util" tea "github.com/charmbracelet/bubbletea" "github.com/urfave/cli/v2" @@ -23,10 +25,34 @@ func AppAction(ctx *cli.Context) error { slog.SetDefault(slog.New(handler)) slog.Info("Starting Termpicker") - rgb := picker.RGB() - cmyk := picker.CMYK() - hsl := picker.HSL() - sw := switcher.New([]picker.Model{*rgb, *cmyk, *hsl}) + + sw := switcher.New([]picker.Model{ + *picker.RGB(), + *picker.CMYK(), + *picker.HSL(), + }) + + if colorStr := ctx.String("color"); colorStr != "" { + color, err := userinput.ParseColor(colorStr) + if err != nil { + slog.Error("Failed to parse color", util.ErrKey, err) + sw.NewNotice(err.Error()) + } else { + pc := color.ToPrecise() + switch color.(type) { + case colors.RGB: + sw.UpdatePicker(0, pc) + sw.SetActive(0) + case colors.CMYK: + sw.UpdatePicker(1, pc) + sw.SetActive(1) + case colors.HSL: + sw.UpdatePicker(2, pc) + sw.SetActive(2) + } + } + } + p := tea.NewProgram(sw) if _, err := p.Run(); err != nil { return err @@ -36,7 +62,7 @@ func AppAction(ctx *cli.Context) error { func main() { app := &cli.App{ - Name: "TermPicker", + Name: "Termpicker", Usage: "A terminal-based color picker", Action: AppAction, Flags: AppFlags, |