From 7d2afe1ca176d0eb76f65523ed7ebcbca702d0c1 Mon Sep 17 00:00:00 2001 From: Benjamin Chausse Date: Thu, 16 Jan 2025 00:20:12 -0500 Subject: Central UI (#26) * migrate to urfave/cli V3 * Remove slider background * Force the use of TrueColor everywhere * All ui info in one location * Fix BinaryFill Example * Make unfocused tabs more faint * Fix color input width * Ctrl-c overrides color input and quits termpicker * docs: remove redundant comment * fix: refactor picker View() --- flags.go | 2 +- go.mod | 5 +- go.sum | 18 ++++--- internal/picker/defaults.go | 37 +++++++------ internal/picker/picker.go | 33 ++++++------ internal/progress/progress.go | 21 +++++--- internal/quit/quit.go | 7 ++- internal/slider/slider.go | 16 +++--- internal/switcher/switcher.go | 83 ++++++++++++++-------------- internal/ui/misc.go | 16 ++++++ internal/ui/style.go | 123 ++++++++++++++++++++++++++++++++++++++++++ main.go | 37 ++++++------- 12 files changed, 273 insertions(+), 125 deletions(-) create mode 100644 internal/ui/misc.go create mode 100644 internal/ui/style.go diff --git a/flags.go b/flags.go index 2eb5294..a7eb37f 100644 --- a/flags.go +++ b/flags.go @@ -1,6 +1,6 @@ package main -import "github.com/urfave/cli/v2" +import "github.com/urfave/cli/v3" const ( flagLogfile = "logfile" diff --git a/go.mod b/go.mod index 42ba5fd..5de072d 100644 --- a/go.mod +++ b/go.mod @@ -12,13 +12,12 @@ require ( github.com/hashicorp/go-uuid v1.0.3 github.com/lucasb-eyer/go-colorful v1.2.0 github.com/muesli/termenv v0.15.2 - github.com/urfave/cli/v2 v2.27.5 + github.com/urfave/cli/v3 v3.0.0-beta1 ) require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect @@ -26,8 +25,6 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect golang.org/x/sync v0.9.0 // indirect golang.org/x/sys v0.27.0 // indirect golang.org/x/text v0.8.0 // indirect diff --git a/go.sum b/go.sum index 7237336..5db4ae7 100644 --- a/go.sum +++ b/go.sum @@ -14,8 +14,8 @@ github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSe github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= @@ -34,15 +34,15 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= -github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg= +github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -51,3 +51,5 @@ golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/picker/defaults.go b/internal/picker/defaults.go index a7a0d89..5500465 100644 --- a/internal/picker/defaults.go +++ b/internal/picker/defaults.go @@ -1,31 +1,34 @@ package picker import ( - "github.com/ChausseBenjamin/termpicker/internal/progress" "github.com/ChausseBenjamin/termpicker/internal/slider" + "github.com/ChausseBenjamin/termpicker/internal/ui" ) func RGB() *Model { - r := slider.New('R', 255, progress.WithGradient("#660000", "#ff0000")) - g := slider.New('G', 255, progress.WithGradient("#006600", "#00ff00")) - b := slider.New('B', 255, progress.WithGradient("#000066", "#0000ff")) - rgb := New([]slider.Model{r, g, b}, "RGB") - return rgb + return New( + []slider.Model{ + slider.New('R', 255, ui.Style().Sliders.R...), + slider.New('G', 255, ui.Style().Sliders.G...), + slider.New('B', 255, ui.Style().Sliders.B...), + }, "RGB") } func CMYK() *Model { - c := slider.New('C', 100, progress.WithGradient("#006666", "#00ffff")) - m := slider.New('M', 100, progress.WithGradient("#660066", "#ff00ff")) - y := slider.New('Y', 100, progress.WithGradient("#666600", "#ffff00")) - k := slider.New('K', 100, progress.WithSolidFill("#000000")) - cmyk := New([]slider.Model{c, m, y, k}, "CMYK") - return cmyk + return New( + []slider.Model{ + slider.New('C', 100, ui.Style().Sliders.C...), + slider.New('M', 100, ui.Style().Sliders.M...), + slider.New('Y', 100, ui.Style().Sliders.Y...), + slider.New('K', 100, ui.Style().Sliders.K...), + }, "CMYK") } func HSL() *Model { - h := slider.New('H', 360, progress.WithDefaultGradient()) - s := slider.New('S', 100, progress.WithGradient("#a68e59", "#ffae00")) - l := slider.New('L', 100, progress.WithGradient("#222222", "#ffffff")) - hsl := New([]slider.Model{h, s, l}, "HSL") - return hsl + return New( + []slider.Model{ + slider.New('H', 360, ui.Style().Sliders.H...), + slider.New('S', 100, ui.Style().Sliders.S...), + slider.New('L', 100, ui.Style().Sliders.L...), + }, "HSL") } diff --git a/internal/picker/picker.go b/internal/picker/picker.go index 5a7493d..1c39aaa 100644 --- a/internal/picker/picker.go +++ b/internal/picker/picker.go @@ -2,17 +2,15 @@ package picker import ( "fmt" + "strings" "github.com/ChausseBenjamin/termpicker/internal/colors" "github.com/ChausseBenjamin/termpicker/internal/slider" + "github.com/ChausseBenjamin/termpicker/internal/ui" "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" ) -const ( - activeRune = '>' -) - type Model struct { title string active int @@ -111,21 +109,24 @@ func (m Model) Init() tea.Cmd { return tea.Batch(cmds...) } -func (m Model) View() string { - var s string +func ViewSlider(active bool, s slider.Model) string { + if active { + return fmt.Sprintf("%s %s", + ui.Style().PickerCursor.Render(ui.PickerSelRune), + s.View(), + ) + } + return fmt.Sprintf(" %s", + s.View(), + ) +} - carriageReturn := "" +func (m Model) View() string { + sliderList := make([]string, len(m.sliders)) for i, slider := range m.sliders { - if i > 0 { - carriageReturn = "\n" - } - if i == m.active { - s += fmt.Sprintf("%v%c %s", carriageReturn, activeRune, slider.View()) - } else { - s += fmt.Sprintf("%v %s", carriageReturn, slider.View()) - } + sliderList[i] = ViewSlider(i == m.active, slider) } - return s + return strings.Join(sliderList, "\n") } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { diff --git a/internal/progress/progress.go b/internal/progress/progress.go index 1e00836..c6caade 100644 --- a/internal/progress/progress.go +++ b/internal/progress/progress.go @@ -107,6 +107,18 @@ func WithFillCharacters(steps []FillStep) Option { } } +// WithBinaryFill results in a less granular but possible more widely compatible +// progress bar as only two characters are used to represent completion of a +// single block (full/complete and empty/incomplete). +func WithBinaryFill() Option { + return func(m *Model) { + m.FillSteps = []FillStep{ + {' ', 0.0}, + {'█', 1.0}, + } + } +} + // WithoutPercentage hides the numeric percentage. func WithoutPercentage() Option { return func(m *Model) { @@ -165,8 +177,6 @@ type Model struct { // "Filled" sections of the progress bar. FullColor string - EmptyColor string - // Settings for rendering the numeric percentage. ShowPercentage bool PercentFormat string // a fmt string for a float @@ -200,7 +210,6 @@ func New(opts ...Option) Model { Width: defaultWidth, FillSteps: defaultFillSteps(), FullColor: "#7571F9", - EmptyColor: "#606060", ShowPercentage: true, PercentFormat: " %3.0f%%", colorProfile: termenv.ColorProfile(), @@ -332,17 +341,13 @@ func (m Model) barView(b *strings.Builder, percent float64, textWidth int) { b.WriteString( termenv.String(string(step.rune)). Foreground(m.color(color)). - Background(m.color(m.EmptyColor)). String(), ) } else { // Empty cell emptyStep := m.FillSteps[0] b.WriteString( - termenv.String(string(emptyStep.rune)). - Foreground(m.color(m.EmptyColor)). - Background(m.color(m.EmptyColor)). - String(), + termenv.String(string(emptyStep.rune)).String(), ) } } diff --git a/internal/quit/quit.go b/internal/quit/quit.go index ac11abc..98c40a7 100644 --- a/internal/quit/quit.go +++ b/internal/quit/quit.go @@ -1,6 +1,9 @@ package quit -import tea "github.com/charmbracelet/bubbletea" +import ( + "github.com/ChausseBenjamin/termpicker/internal/ui" + tea "github.com/charmbracelet/bubbletea" +) const byeMsg = "Goodbye!\n" @@ -10,4 +13,4 @@ func (m Model) Init() tea.Cmd { return nil } func (m Model) Update(tea.Msg) (tea.Model, tea.Cmd) { return m, nil } -func (m Model) View() string { return byeMsg } +func (m Model) View() string { return ui.Style().Quit.Render(byeMsg) } diff --git a/internal/slider/slider.go b/internal/slider/slider.go index 2d47249..dbf4093 100644 --- a/internal/slider/slider.go +++ b/internal/slider/slider.go @@ -2,8 +2,10 @@ package slider import ( "fmt" + "strings" "github.com/ChausseBenjamin/termpicker/internal/progress" + "github.com/ChausseBenjamin/termpicker/internal/ui" "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" ) @@ -18,10 +20,8 @@ type Model struct { func New(label byte, maxVal int, opts ...progress.Option) Model { slider := Model{ - label: label, - progress: progress.New( - progress.WithoutPercentage(), - ), + label: label, + progress: progress.New(), max: maxVal, current: maxVal / 2, mappings: newKeybinds(), @@ -32,7 +32,7 @@ func New(label byte, maxVal int, opts ...progress.Option) Model { return slider } -func (m Model) Title() string { return fmt.Sprintf("%c", m.label) } +func (m Model) Title() string { return fmt.Sprintf("%c:", m.label) } func (m Model) Init() tea.Cmd { // Triggering a frame message Update here will force the progress bar to @@ -79,5 +79,9 @@ func (m Model) ViewValue(current int) string { } func (m Model) View() string { - return fmt.Sprintf("%v: %v %v", m.Title(), m.progress.View(), m.ViewValue(m.current)) + return strings.Join([]string{ + ui.Style().SliderLabel.Render(m.Title()), + m.progress.View(), + ui.Style().SliderVal.Render(m.ViewValue(m.current)), + }, " ") } diff --git a/internal/switcher/switcher.go b/internal/switcher/switcher.go index f368224..8de31c6 100644 --- a/internal/switcher/switcher.go +++ b/internal/switcher/switcher.go @@ -10,6 +10,7 @@ import ( "github.com/ChausseBenjamin/termpicker/internal/picker" "github.com/ChausseBenjamin/termpicker/internal/preview" "github.com/ChausseBenjamin/termpicker/internal/quit" + "github.com/ChausseBenjamin/termpicker/internal/ui" "github.com/charmbracelet/bubbles/help" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" @@ -39,12 +40,19 @@ func New() Model { *picker.CMYK(), *picker.HSL(), } + + input := textinput.New() + input.PromptStyle = ui.Style().InputPrompt + input.TextStyle = ui.Style().InputText + input.Prompt = ui.PromptPrefix + input.Placeholder = ui.PromptPlaceholder + return Model{ active: 0, pickers: pickers, preview: *preview.New(colors.Hex(pickers[0].GetColor())), help: help.New(), - input: textinput.New(), + input: input, notices: notices.New(), fullHelp: false, } @@ -93,68 +101,59 @@ func (m Model) Init() tea.Cmd { } func (m Model) View() string { - norm := lipgloss.NewStyle().Faint(true) - bright := lipgloss.NewStyle().Faint(false) - - delims := [3]string{"[ ", " | ", "]"} - for i, d := range delims { - delims[i] = bright.Render(d) - } - - var sections []string + tabs := make([]string, len(m.pickers)) for i, p := range m.pickers { if i == m.active { - sections = append( - sections, - bright. - Underline(true). - Bold(true). - Render(p.Title()), - ) + tabs[i] = ui.Style().TabSel.Render(p.Title()) } else { - sections = append(sections, norm.Render(p.Title())) + tabs[i] = ui.Style().TabNorm.Render(p.Title()) } } - tabs := "[ " + strings.Join(sections, " | ") + " ]" + tabStr := strings.Join([]string{ + ui.Style().TabGeom.Render(ui.TabSepLeft), + strings.Join(tabs, ui.Style().TabGeom.Render(ui.TabSepMid)), + ui.Style().TabGeom.Render(ui.TabSepRight), + }, " ") - pickerView := m.pickers[m.active].View() - boxStyle := lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), true, true, false, true) - w := lipgloss.Width(pickerView) - pickerView = boxStyle.Render(pickerView) + pickerStr := m.pickers[m.active].View() + w := lipgloss.Width(pickerStr) m.preview.SetWidth(w) - boxStyle = boxStyle.Border(lipgloss.RoundedBorder(), false, true, false, true) - previewStr := boxStyle.Render(m.preview.View()) + previewStr := m.preview.View() m.help.Styles.ShortKey.Width(w) - boxStyle = boxStyle.Border(lipgloss.RoundedBorder(), false, true, true, true).Width(w) - var helpstr string + var helpStr string + m.help.Width = w if m.fullHelp { - helpstr = m.help.FullHelpView(m.AllKeys()) + helpStr = m.help.FullHelpView(m.AllKeys()) } else { // This is a hack since the current view has too many keys // and the horizontal "ShortHelpView" gets too wide. // "FullHelpView" seperates keys by columns (and we only show the first). - // helpstr = m.help.FullHelpView([][]key.Binding{m.AllKeys()[0]}) - helpstr = m.help.FullHelpView(shortKeys()) + // helpStr = m.help.FullHelpView([][]key.Binding{m.AllKeys()[0]}) + helpStr = m.help.FullHelpView(shortKeys()) } - helpstr = boxStyle.Render(helpstr) - inputStr := "" + var inputStr string if m.input.Focused() { - boxStyle = boxStyle.Border(lipgloss.RoundedBorder(), true, true, true, true).Width(w) - inputStr = boxStyle.Render(m.input.View()) + m.input.Width = w - lipgloss.Width(ui.PromptPrefix) - 1 + inputStr = ui.Style().Boxed.Render(m.input.View()) } - return fmt.Sprintf("%s\n%s\n%s\n%v\n%v\n%v", - tabs, - pickerView, + mainArea := ui.Style().Boxed.Render(strings.Join([]string{ + pickerStr, previewStr, - helpstr, - inputStr, - m.notices.View(), - ) + helpStr, + }, "\n")) + + return strings.Join( + []string{ + tabStr, + mainArea, + inputStr, + m.notices.View(), + }, "\n") } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { @@ -169,7 +168,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: - if m.input.Focused() { + if m.input.Focused() && msg.String() != "ctrl+c" { keys.esc.SetEnabled(true) keys.confirm.SetEnabled(true) if key.Matches(msg, keys.esc) { diff --git a/internal/ui/misc.go b/internal/ui/misc.go new file mode 100644 index 0000000..33092b4 --- /dev/null +++ b/internal/ui/misc.go @@ -0,0 +1,16 @@ +package ui + +import ( + lg "github.com/charmbracelet/lipgloss" + "github.com/muesli/termenv" +) + +func ColorProfile() termenv.Profile { + return termenv.TrueColor +} + +func init() { + r := lg.DefaultRenderer() + r.SetColorProfile(ColorProfile()) + lg.SetDefaultRenderer(r) +} diff --git a/internal/ui/style.go b/internal/ui/style.go new file mode 100644 index 0000000..75d2f70 --- /dev/null +++ b/internal/ui/style.go @@ -0,0 +1,123 @@ +package ui + +import ( + "github.com/ChausseBenjamin/termpicker/internal/progress" + lg "github.com/charmbracelet/lipgloss" +) + +const ( + textSel = "#F2F1F0" + textNorm = "#A7AFB1" + textFaint = "#6F797B" + geomFg = "#ACB3B5" + + TabSepLeft = "[" + TabSepMid = " | " + TabSepRight = "]" + + PickerSelRune = ">" + + PromptPrefix = "> " + PromptPlaceholder = "Enter a color (ex: #b7416e)" + + SliderMinWidth = 22 // 1 ASCII change every 2.05 deg. avg + SliderMaxWidth = 90 // 2 ASCII change per deg. + +) + +type sliderOpts struct { + R, G, B []progress.Option + C, M, Y, K []progress.Option + H, S, L []progress.Option +} + +type StyleSheet struct { + TabSel lg.Style + TabNorm lg.Style + TabGeom lg.Style + SliderVal lg.Style + SliderLabel lg.Style + PickerCursor lg.Style + Preview lg.Style + InputPrompt lg.Style + InputText lg.Style + Notice lg.Style + Quit lg.Style + Boxed lg.Style + Sliders sliderOpts +} + +var style StyleSheet + +func Style() StyleSheet { + return style +} + +func init() { + baseStyle := lg.NewStyle(). + Foreground(lg.Color(textNorm)) + + baseSliderOpts := []progress.Option{ + progress.WithColorProfile(ColorProfile()), + progress.WithoutPercentage(), + // progress.WithBinaryFill(), // uncomment for legacy look + } + + style = StyleSheet{ + TabSel: baseStyle.Inherit(lg.NewStyle(). + Foreground(lg.Color(textSel)). + Underline(true). + Bold(true)), + + TabNorm: baseStyle.Inherit(lg.NewStyle(). + Foreground(lg.Color(textFaint)). + Underline(false). + Faint(true). + Bold(false)), + + TabGeom: baseStyle.Inherit(lg.NewStyle(). + Foreground(lg.Color(geomFg))), + + SliderVal: baseStyle, + + SliderLabel: baseStyle, + + PickerCursor: baseStyle.Inherit(lg.NewStyle(). + Bold(true)), + + Preview: baseStyle, + + InputPrompt: baseStyle.Inherit(lg.NewStyle(). + Bold(true)), + + InputText: baseStyle, + + Notice: baseStyle.Inherit(lg.NewStyle(). + Bold(true)), + + Quit: baseStyle.Inherit(lg.NewStyle(). + Foreground(lg.Color(textSel)). + Bold(true)), + + Boxed: baseStyle.Inherit(lg.NewStyle(). + Border(lg.RoundedBorder())), + + Sliders: sliderOpts{ + // RGB + R: append(baseSliderOpts, progress.WithGradient("#660000", "#ff0000")), + G: append(baseSliderOpts, progress.WithGradient("#006600", "#00ff00")), + B: append(baseSliderOpts, progress.WithGradient("#000066", "#0000ff")), + + // CMYK + C: append(baseSliderOpts, progress.WithGradient("#006666", "#00ffff")), + M: append(baseSliderOpts, progress.WithGradient("#660066", "#ff00ff")), + Y: append(baseSliderOpts, progress.WithGradient("#666600", "#ffff00")), + K: append(baseSliderOpts, progress.WithSolidFill("#000000")), + + // HSL + H: append(baseSliderOpts, progress.WithDefaultGradient()), + S: append(baseSliderOpts, progress.WithGradient("#a68e59", "#ffae00")), + L: append(baseSliderOpts, progress.WithGradient("#222222", "#ffffff")), + }, + } +} diff --git a/main.go b/main.go index 5511ac6..f8dacf8 100644 --- a/main.go +++ b/main.go @@ -1,31 +1,29 @@ package main import ( + "context" "log/slog" "os" - "time" "github.com/ChausseBenjamin/termpicker/internal/logging" "github.com/ChausseBenjamin/termpicker/internal/switcher" "github.com/ChausseBenjamin/termpicker/internal/util" tea "github.com/charmbracelet/bubbletea" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v3" ) -var ( // Set by the build system - version = "compiled" - date = "" -) +// Set by the build system +var version = "compiled" -func AppAction(ctx *cli.Context) error { - logfile := logging.Setup(ctx.String("logfile")) +func AppAction(ctx context.Context, cmd *cli.Command) error { + logfile := logging.Setup(cmd.String("logfile")) defer logfile.Close() slog.Info("Starting Termpicker") sw := switcher.New() - if colorStr := ctx.String("color"); colorStr != "" { + if colorStr := cmd.String("color"); colorStr != "" { sw.NewNotice(sw.SetColorFromText(colorStr)) } @@ -37,19 +35,16 @@ func AppAction(ctx *cli.Context) error { } func main() { - compileDate, _ := time.Parse(time.RFC3339, date) - app := &cli.App{ - Name: "Termpicker", - Usage: "A terminal-based color picker", - Action: AppAction, - Authors: []*cli.Author{ - {Name: "Benjamin Chausse", Email: "benjamin@chausse.xyz"}, - }, - Version: version, - Flags: AppFlags, - Compiled: compileDate, + app := &cli.Command{ + Name: "Termpicker", + Usage: "A terminal-based color picker", + Action: AppAction, + Authors: []any{"Benjamin Chausse "}, + Version: version, + Flags: AppFlags, + EnableShellCompletion: true, } - if err := app.Run(os.Args); err != nil { + if err := app.Run(context.Background(), os.Args); err != nil { slog.Error("Program crashed", util.ErrKey, err.Error()) os.Exit(1) } -- cgit v1.2.3