diff --git a/go.mod b/go.mod index 46a91f9..7306065 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.22.0 require ( github.com/atotto/clipboard v0.1.4 - github.com/charmbracelet/bubbletea v0.26.6 + github.com/charmbracelet/bubbletea v0.27.0 github.com/spf13/cobra v1.8.1 github.com/tobischo/gokeepasslib/v3 v3.6.0 golang.org/x/term v0.23.0 @@ -26,7 +26,7 @@ require ( github.com/tobischo/argon2 v0.1.0 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect golang.org/x/crypto v0.25.0 // indirect - golang.org/x/sync v0.7.0 // indirect - golang.org/x/sys v0.23.0 // indirect + golang.org/x/sync v0.8.0 // indirect + golang.org/x/sys v0.24.0 // indirect golang.org/x/text v0.16.0 // indirect ) diff --git a/go.sum b/go.sum index 531a38c..1397e72 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s= -github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk= +github.com/charmbracelet/bubbletea v0.27.0 h1:Mznj+vvYuYagD9Pn2mY7fuelGvP0HAXtZYGgRBCbHvU= +github.com/charmbracelet/bubbletea v0.27.0/go.mod h1:5MdP9XH6MbQkgGhnlxUqCNmBXf9I74KRQ8HIidRxV1Y= github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM= github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= github.com/charmbracelet/x/input v0.1.3 h1:oy4TMhyGQsYs/WWJwu1ELUMFnjiUAXwtDf048fHbCkg= @@ -49,11 +49,11 @@ golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= +golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM= -golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= +golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= diff --git a/vendor/github.com/charmbracelet/bubbletea/commands.go b/vendor/github.com/charmbracelet/bubbletea/commands.go index 2541bea..bfa3b70 100644 --- a/vendor/github.com/charmbracelet/bubbletea/commands.go +++ b/vendor/github.com/charmbracelet/bubbletea/commands.go @@ -201,3 +201,16 @@ func SetWindowTitle(title string) Cmd { return setWindowTitleMsg(title) } } + +type windowSizeMsg struct{} + +// WindowSize is a command that queries the terminal for its current size. It +// delivers the results to Update via a [WindowSizeMsg]. Keep in mind that +// WindowSizeMsgs will automatically be delivered to Update when the [Program] +// starts and when the window dimensions change so in many cases you will not +// need to explicitly invoke this command. +func WindowSize() Cmd { + return func() Msg { + return windowSizeMsg{} + } +} diff --git a/vendor/github.com/charmbracelet/bubbletea/options.go b/vendor/github.com/charmbracelet/bubbletea/options.go index 8aee6da..a810fe1 100644 --- a/vendor/github.com/charmbracelet/bubbletea/options.go +++ b/vendor/github.com/charmbracelet/bubbletea/options.go @@ -49,6 +49,23 @@ func WithInputTTY() ProgramOption { } } +// WithEnvironment sets the environment variables that the program will use. +// This useful when the program is running in a remote session (e.g. SSH) and +// you want to pass the environment variables from the remote session to the +// program. +// +// Example: +// +// var sess ssh.Session // ssh.Session is a type from the github.com/charmbracelet/ssh package +// pty, _, _ := sess.Pty() +// environ := append(sess.Environ(), "TERM="+pty.Term) +// p := tea.NewProgram(model, tea.WithEnvironment(environ) +func WithEnvironment(env []string) ProgramOption { + return func(p *Program) { + p.environ = env + } +} + // WithoutSignalHandler disables the signal handler that Bubble Tea sets up for // Programs. This is useful if you want to handle signals yourself. func WithoutSignalHandler() ProgramOption { diff --git a/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go b/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go index 1b7f4b9..f81920d 100644 --- a/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go +++ b/vendor/github.com/charmbracelet/bubbletea/standard_renderer.go @@ -108,6 +108,8 @@ func (r *standardRenderer) stop() { defer r.mtx.Unlock() r.execute(ansi.EraseEntireLine) + // Move the cursor back to the beginning of the line + r.execute("\r") if r.useANSICompressor { if w, ok := r.out.(io.WriteCloser); ok { @@ -132,6 +134,8 @@ func (r *standardRenderer) kill() { defer r.mtx.Unlock() r.execute(ansi.EraseEntireLine) + // Move the cursor back to the beginning of the line + r.execute("\r") } // listen waits for ticks on the ticker, or a signal to stop the renderer. diff --git a/vendor/github.com/charmbracelet/bubbletea/tea.go b/vendor/github.com/charmbracelet/bubbletea/tea.go index 7ded48b..62cd641 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tea.go +++ b/vendor/github.com/charmbracelet/bubbletea/tea.go @@ -151,6 +151,9 @@ type Program struct { previousOutputState *term.State renderer renderer + // the environment variables for the program, defaults to os.Environ(). + environ []string + // where to read inputs from, this will usually be os.Stdin. input io.Reader // ttyInput is null if input is not a TTY. @@ -181,6 +184,22 @@ func Quit() Msg { // Quit. type QuitMsg struct{} +// Suspend is a special command that tells the Bubble Tea program to suspend. +func Suspend() Msg { + return SuspendMsg{} +} + +// SuspendMsg signals the program should suspend. +// This usually happens when ctrl+z is pressed on common programs, but since +// bubbletea puts the terminal in raw mode, we need to handle it in a +// per-program basis. +// You can send this message with Suspend. +type SuspendMsg struct{} + +// ResumeMsg can be listen to to do something once a program is resumed back +// from a suspend state. +type ResumeMsg struct{} + // NewProgram creates a new Program. func NewProgram(model Model, opts ...ProgramOption) *Program { p := &Program{ @@ -206,6 +225,11 @@ func NewProgram(model Model, opts ...ProgramOption) *Program { p.output = os.Stdout } + // if no environment was set, set it to os.Environ() + if p.environ == nil { + p.environ = os.Environ() + } + return p } @@ -327,6 +351,11 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { case QuitMsg: return model, nil + case SuspendMsg: + if suspendSupported { + p.suspend() + } + case clearScreenMsg: p.renderer.clearScreen() @@ -401,6 +430,9 @@ func (p *Program) eventLoop(model Model, cmds chan Cmd) (Model, error) { case setWindowTitleMsg: p.SetWindowTitle(string(msg)) + + case windowSizeMsg: + go p.checkResize() } // Process internal messages for the renderer. @@ -550,7 +582,7 @@ func (p *Program) Run() (Model, error) { model, err := p.eventLoop(model, cmds) killed := p.ctx.Err() != nil if killed { - err = ErrProgramKilled + err = fmt.Errorf("%w: %s", ErrProgramKilled, p.ctx.Err()) } else { // Ensure we rendered the final state of the model. p.renderer.write(model.View()) diff --git a/vendor/github.com/charmbracelet/bubbletea/tty.go b/vendor/github.com/charmbracelet/bubbletea/tty.go index 2fdd36b..ed469ad 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty.go @@ -10,6 +10,18 @@ import ( "github.com/muesli/cancelreader" ) +func (p *Program) suspend() { + if err := p.ReleaseTerminal(); err != nil { + // If we can't release input, abort. + return + } + + suspendProcess() + + _ = p.RestoreTerminal() + go p.Send(ResumeMsg{}) +} + func (p *Program) initTerminal() error { if err := p.initInput(); err != nil { return err diff --git a/vendor/github.com/charmbracelet/bubbletea/tty_unix.go b/vendor/github.com/charmbracelet/bubbletea/tty_unix.go index 362e585..5cbb4fe 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty_unix.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty_unix.go @@ -6,6 +6,8 @@ package tea import ( "fmt" "os" + "os/signal" + "syscall" "github.com/charmbracelet/x/term" ) @@ -34,3 +36,14 @@ func openInputTTY() (*os.File, error) { } return f, nil } + +const suspendSupported = true + +// Send SIGTSTP to the entire process group. +func suspendProcess() { + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGCONT) + _ = syscall.Kill(0, syscall.SIGTSTP) + // blocks until a CONT happens... + <-c +} diff --git a/vendor/github.com/charmbracelet/bubbletea/tty_windows.go b/vendor/github.com/charmbracelet/bubbletea/tty_windows.go index de6a82f..a3a2525 100644 --- a/vendor/github.com/charmbracelet/bubbletea/tty_windows.go +++ b/vendor/github.com/charmbracelet/bubbletea/tty_windows.go @@ -62,3 +62,7 @@ func openInputTTY() (*os.File, error) { } return f, nil } + +const suspendSupported = false + +func suspendProcess() {} diff --git a/vendor/golang.org/x/sync/LICENSE b/vendor/golang.org/x/sync/LICENSE index 6a66aea..2a7cf70 100644 --- a/vendor/golang.org/x/sync/LICENSE +++ b/vendor/golang.org/x/sync/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10,7 +10,7 @@ notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - * Neither the name of Google Inc. nor the names of its + * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go index b102b95..7f1961b 100644 --- a/vendor/golang.org/x/sys/unix/ztypes_linux.go +++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go @@ -3807,6 +3807,9 @@ const ( ETHTOOL_MSG_PSE_GET_REPLY = 0x25 ETHTOOL_MSG_RSS_GET_REPLY = 0x26 ETHTOOL_MSG_KERNEL_MAX = 0x2b + ETHTOOL_FLAG_COMPACT_BITSETS = 0x1 + ETHTOOL_FLAG_OMIT_REPLY = 0x2 + ETHTOOL_FLAG_STATS = 0x4 ETHTOOL_A_HEADER_UNSPEC = 0x0 ETHTOOL_A_HEADER_DEV_INDEX = 0x1 ETHTOOL_A_HEADER_DEV_NAME = 0x2 diff --git a/vendor/golang.org/x/sys/windows/types_windows.go b/vendor/golang.org/x/sys/windows/types_windows.go index 4d0c157..3f03b3d 100644 --- a/vendor/golang.org/x/sys/windows/types_windows.go +++ b/vendor/golang.org/x/sys/windows/types_windows.go @@ -2031,6 +2031,50 @@ const ( IF_TYPE_IEEE1394 = 144 ) +// Enum NL_PREFIX_ORIGIN for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_prefix_origin +const ( + IpPrefixOriginOther = 0 + IpPrefixOriginManual = 1 + IpPrefixOriginWellKnown = 2 + IpPrefixOriginDhcp = 3 + IpPrefixOriginRouterAdvertisement = 4 + IpPrefixOriginUnchanged = 1 << 4 +) + +// Enum NL_SUFFIX_ORIGIN for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_suffix_origin +const ( + NlsoOther = 0 + NlsoManual = 1 + NlsoWellKnown = 2 + NlsoDhcp = 3 + NlsoLinkLayerAddress = 4 + NlsoRandom = 5 + IpSuffixOriginOther = 0 + IpSuffixOriginManual = 1 + IpSuffixOriginWellKnown = 2 + IpSuffixOriginDhcp = 3 + IpSuffixOriginLinkLayerAddress = 4 + IpSuffixOriginRandom = 5 + IpSuffixOriginUnchanged = 1 << 4 +) + +// Enum NL_DAD_STATE for [IpAdapterUnicastAddress], see +// https://learn.microsoft.com/en-us/windows/win32/api/nldef/ne-nldef-nl_dad_state +const ( + NldsInvalid = 0 + NldsTentative = 1 + NldsDuplicate = 2 + NldsDeprecated = 3 + NldsPreferred = 4 + IpDadStateInvalid = 0 + IpDadStateTentative = 1 + IpDadStateDuplicate = 2 + IpDadStateDeprecated = 3 + IpDadStatePreferred = 4 +) + type SocketAddress struct { Sockaddr *syscall.RawSockaddrAny SockaddrLength int32 diff --git a/vendor/modules.txt b/vendor/modules.txt index 4192d55..73de5f8 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,7 +1,7 @@ # github.com/atotto/clipboard v0.1.4 ## explicit github.com/atotto/clipboard -# github.com/charmbracelet/bubbletea v0.26.6 +# github.com/charmbracelet/bubbletea v0.27.0 ## explicit; go 1.18 github.com/charmbracelet/bubbletea # github.com/charmbracelet/x/ansi v0.1.4 @@ -62,10 +62,10 @@ golang.org/x/crypto/blake2b golang.org/x/crypto/chacha20 golang.org/x/crypto/internal/alias golang.org/x/crypto/twofish -# golang.org/x/sync v0.7.0 +# golang.org/x/sync v0.8.0 ## explicit; go 1.18 golang.org/x/sync/errgroup -# golang.org/x/sys v0.23.0 +# golang.org/x/sys v0.24.0 ## explicit; go 1.18 golang.org/x/sys/cpu golang.org/x/sys/plan9