diff --git a/confirm/command.go b/confirm/command.go index 3597150..88d7db4 100644 --- a/confirm/command.go +++ b/confirm/command.go @@ -10,6 +10,9 @@ import ( "github.com/charmbracelet/gum/style" ) +// Aborted is the exit code when the user aborts the confirmation. +const Aborted = 130 + // Run provides a shell script interface for prompting a user to confirm an // action with an affirmative or negative answer. func (o Options) Run() error { @@ -31,7 +34,7 @@ func (o Options) Run() error { } if m.(model).aborted { - os.Exit(130) + os.Exit(Aborted) } else if m.(model).confirmation { os.Exit(0) } else { diff --git a/go.sum b/go.sum index bd4f6e5..6e4c40e 100644 --- a/go.sum +++ b/go.sum @@ -18,6 +18,7 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06 github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= github.com/charmbracelet/glamour v0.6.1-0.20230531150759-6d5b52861a9d h1:S4Ejl/M2VrryIgDrDbiuvkwMUDa67/t/H3Wz3i2/vUw= github.com/charmbracelet/glamour v0.6.1-0.20230531150759-6d5b52861a9d/go.mod h1:swCB3CXFsh22H1ESDYdY1tirLiNqCziulDyJ1B6Nt7Q= +github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/lipgloss v0.7.2-0.20230316100548-06dd20ee5707 h1:dXv2HjaDlJZj7wLpTjg1P4B68bdvoXfx7+VXF2/RelY= github.com/charmbracelet/lipgloss v0.7.2-0.20230316100548-06dd20ee5707/go.mod h1:BDceYFEeE5FBoGZeuApZ+V4wSgi8AOIHoryyjYbCTHM= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= @@ -56,6 +57,7 @@ github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= @@ -67,6 +69,7 @@ github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= @@ -79,3 +82,4 @@ golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= diff --git a/pager/pager.go b/pager/pager.go index 5091ea3..49235f1 100644 --- a/pager/pager.go +++ b/pager/pager.go @@ -86,6 +86,7 @@ func (m *model) ProcessText(msg tea.WindowSizeMsg) { func (m model) KeyHandler(key tea.KeyMsg) (model, func() tea.Msg) { var cmd tea.Cmd + const HeightOffset = 2 if m.search.active { switch key.String() { case "enter": @@ -95,7 +96,7 @@ func (m model) KeyHandler(key tea.KeyMsg) (model, func() tea.Msg) { // Trigger a view update to highlight the found matches. m.search.NextMatch(&m) - m.ProcessText(tea.WindowSizeMsg{Height: m.viewport.Height + 2, Width: m.viewport.Width}) + m.ProcessText(tea.WindowSizeMsg{Height: m.viewport.Height + HeightOffset, Width: m.viewport.Width}) } else { m.search.Done() } @@ -114,10 +115,10 @@ func (m model) KeyHandler(key tea.KeyMsg) (model, func() tea.Msg) { m.search.Begin() case "p", "N": m.search.PrevMatch(&m) - m.ProcessText(tea.WindowSizeMsg{Height: m.viewport.Height + 2, Width: m.viewport.Width}) + m.ProcessText(tea.WindowSizeMsg{Height: m.viewport.Height + HeightOffset, Width: m.viewport.Width}) case "n": m.search.NextMatch(&m) - m.ProcessText(tea.WindowSizeMsg{Height: m.viewport.Height + 2, Width: m.viewport.Width}) + m.ProcessText(tea.WindowSizeMsg{Height: m.viewport.Height + HeightOffset, Width: m.viewport.Width}) case "q", "ctrl+c", "esc": return m, tea.Quit } diff --git a/timeout/options.go b/timeout/options.go new file mode 100644 index 0000000..9986835 --- /dev/null +++ b/timeout/options.go @@ -0,0 +1,55 @@ +package timeout + +import ( + "fmt" + "time" + + tea "github.com/charmbracelet/bubbletea" +) + +// Tick interval. +const tickInterval = time.Second + +// TickTimeoutMsg will be dispatched for every tick. +// Containing current timeout value +// and optional parameter to be used when handling the timeout msg. +type TickTimeoutMsg struct { + TimeoutValue time.Duration + Data interface{} +} + +// Init Start Timeout ticker using with timeout in seconds and optional data. +func Init(timeout time.Duration, data interface{}) tea.Cmd { + if timeout > 0 { + return Tick(timeout, data) + } + return nil +} + +// Start ticker. +func Tick(timeoutValue time.Duration, data interface{}) tea.Cmd { + return tea.Tick(tickInterval, func(time.Time) tea.Msg { + // every tick checks if the timeout needs to be decremented + // and send as message + if timeoutValue >= 0 { + timeoutValue -= tickInterval + return TickTimeoutMsg{ + TimeoutValue: timeoutValue, + Data: data, + } + } + return nil + }) +} + +// Str produce Timeout String to be rendered. +func Str(timeout time.Duration) string { + return fmt.Sprintf(" (%d)", max(0, int(timeout.Seconds()))) +} + +func max(a, b int) int { + if a > b { + return a + } + return b +}