diff --git a/cmd/root.go b/cmd/root.go index cb3d947a..9f5b1443 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -343,7 +343,7 @@ func bindFlags(cmd *cobra.Command, nameToArg map[string]arg) { func mainEntrypoint(cmd *cobra.Command, _ []string) { dev.Debug("~STARTING UP~") rootOpts := getRootOpts(cmd) - initialModel, options := setup(cmd, rootOpts, "") + initialModel, options := setup(cmd, rootOpts, "", nil) program := tea.NewProgram(initialModel, options...) if _, err := program.Run(); err != nil { diff --git a/cmd/serve.go b/cmd/serve.go index 846062c9..826310d9 100644 --- a/cmd/serve.go +++ b/cmd/serve.go @@ -6,12 +6,13 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/ssh" "github.com/charmbracelet/wish" + "github.com/charmbracelet/wish/activeterm" bm "github.com/charmbracelet/wish/bubbletea" "github.com/spf13/cobra" "log" + "net" "os" "os/signal" - "strconv" "strings" "syscall" "time" @@ -58,9 +59,9 @@ var ( ) func serveEntrypoint(cmd *cobra.Command, args []string) { + var err error host := cmd.Flags().Lookup("host").Value.String() portStr := cmd.Flags().Lookup("port").Value.String() - port, err := strconv.Atoi(portStr) if err != nil { fmt.Println(fmt.Errorf("could not convert %s to integer", portStr)) os.Exit(1) @@ -68,7 +69,12 @@ func serveEntrypoint(cmd *cobra.Command, args []string) { hostKeyPath := cmd.Flags().Lookup("host-key-path").Value.String() hostKeyPEM := cmd.Flags().Lookup("host-key-pem").Value.String() - options := []ssh.Option{wish.WithAddress(fmt.Sprintf("%s:%d", host, port))} + options := []ssh.Option{wish.WithAddress(net.JoinHostPort(host, portStr))} + + // Allocate a pty. + // This creates a pseudoconsole on windows, compatibility is limited in that case + options = append(options, ssh.AllocatePty()) + if hostKeyPath != "" { options = append(options, wish.WithHostKeyPath(hostKeyPath)) } @@ -77,6 +83,8 @@ func serveEntrypoint(cmd *cobra.Command, args []string) { } middleware := wish.WithMiddleware( bm.Middleware(generateTeaHandler(cmd)), + // ensure the user has requested a tty + activeterm.Middleware(), customLoggingMiddleware(), ) options = append(options, middleware) @@ -88,7 +96,7 @@ func serveEntrypoint(cmd *cobra.Command, args []string) { done := make(chan os.Signal, 1) signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - log.Printf("Starting SSH server on %s:%d", host, port) + log.Printf("Starting SSH server on %s:%s", host, portStr) go func() { if err = s.ListenAndServe(); err != nil { log.Fatalln(err) @@ -112,6 +120,6 @@ func generateTeaHandler(cmd *cobra.Command) func(ssh.Session) (tea.Model, []tea. if sshCommands := s.Command(); len(sshCommands) == 1 { overrideToken = strings.TrimSpace(sshCommands[0]) } - return setup(cmd, changedOpts, overrideToken) + return setup(cmd, changedOpts, overrideToken, s) } } diff --git a/cmd/util.go b/cmd/util.go index 06f1bb7a..1a07bd98 100644 --- a/cmd/util.go +++ b/cmd/util.go @@ -377,7 +377,7 @@ func getRootOpts(cmd *cobra.Command) []string { return opts } -func setup(cmd *cobra.Command, rootOpts []string, overrideToken string) (app.Model, []tea.ProgramOption) { - initialModel := app.InitialModel(getConfig(cmd, rootOpts, overrideToken)) +func setup(cmd *cobra.Command, rootOpts []string, overrideToken string, session ssh.Session) (app.Model, []tea.ProgramOption) { + initialModel := app.InitialModel(getConfig(cmd, rootOpts, overrideToken), session) return initialModel, []tea.ProgramOption{tea.WithAltScreen()} } diff --git a/go.mod b/go.mod index fe1fbfd5..96bc9570 100644 --- a/go.mod +++ b/go.mod @@ -6,10 +6,10 @@ require ( github.com/atotto/clipboard v0.1.4 github.com/carlmjohnson/versioninfo v0.22.4 github.com/charmbracelet/bubbles v0.16.1 - github.com/charmbracelet/bubbletea v0.24.2 - github.com/charmbracelet/lipgloss v0.7.1 - github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 - github.com/charmbracelet/wish v1.1.1 + github.com/charmbracelet/bubbletea v0.26.1 + github.com/charmbracelet/lipgloss v0.10.0 + github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 + github.com/charmbracelet/wish v1.4.0 github.com/hashicorp/nomad/api v0.0.0-20230619092614-e29ad68c588d github.com/itchyny/gojq v0.12.13 github.com/moby/term v0.5.0 @@ -17,17 +17,20 @@ require ( github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.16.0 - golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 + golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f ) require ( github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/caarlos0/sshmarshal v0.1.0 // indirect - github.com/charmbracelet/keygen v0.4.2 // indirect - github.com/charmbracelet/log v0.2.2 // indirect - github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect + github.com/charmbracelet/keygen v0.5.0 // indirect + github.com/charmbracelet/log v0.4.0 // indirect + github.com/charmbracelet/x/errors v0.0.0-20240425164147-ba2a9512b05f // indirect + github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f // indirect + github.com/containerd/console v1.0.4 // indirect + github.com/creack/pty v1.1.21 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect @@ -41,26 +44,27 @@ require ( github.com/itchyny/timefmt-go v0.1.5 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.15.1 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/rivo/uniseg v0.4.4 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect - golang.org/x/crypto v0.10.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.9.0 // indirect - golang.org/x/term v0.9.0 // indirect - golang.org/x/text v0.10.0 // indirect + github.com/u-root/u-root v0.11.0 // indirect + golang.org/x/crypto v0.22.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.19.0 // indirect + golang.org/x/term v0.19.0 // indirect + golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 7778432c..7e538536 100644 --- a/go.sum +++ b/go.sum @@ -46,25 +46,41 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= -github.com/caarlos0/sshmarshal v0.1.0 h1:zTCZrDORFfWh526Tsb7vCm3+Yg/SfW/Ub8aQDeosk0I= -github.com/caarlos0/sshmarshal v0.1.0/go.mod h1:7Pd/0mmq9x/JCzKauogNjSQEhivBclCQHfr9dlpDIyA= github.com/carlmjohnson/versioninfo v0.22.4 h1:AucUHDSKmk6j7Yx3dECGUxaowGHOAN0Zx5/EBtsXn4Y= github.com/carlmjohnson/versioninfo v0.22.4/go.mod h1:QT9mph3wcVfISUKd0i9sZfVrPviHuSF+cUtLjm2WSf8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY= github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc= -github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06RaW2cx/SY= -github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= -github.com/charmbracelet/keygen v0.4.2 h1:TNHua2MlXc6W1dQB2iW4msSZGKlb8RtxtmYDWUs4iRw= -github.com/charmbracelet/keygen v0.4.2/go.mod h1:4e4FT3HSdLU/u83RfJWvzJIaVb8aX4MxtDlfXwpDJaI= -github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= -github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= -github.com/charmbracelet/log v0.2.2 h1:CaXgos+ikGn5tcws5Cw3paQuk9e/8bIwuYGhnkqQFjo= -github.com/charmbracelet/log v0.2.2/go.mod h1:Zs11hKpb8l+UyX4y1srwZIGW+MPCXJHIty3MB9l/sno= -github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103 h1:wpHMERIN0pQZE635jWwT1dISgfjbpUcEma+fbPKSMCU= -github.com/charmbracelet/ssh v0.0.0-20221117183211-483d43d97103/go.mod h1:0Vm2/8yBljiLDnGJHU8ehswfawrEybGk33j5ssqKQVM= -github.com/charmbracelet/wish v1.1.1 h1:KdICASKd2oh2JPvk1Z4CJtAi97cFErXF7NKienPICO4= -github.com/charmbracelet/wish v1.1.1/go.mod h1:xh4KZpSULw+Xqb9bcbhw92QAinVB75CVLWrFuyY6IVs= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/bubbletea v0.26.1 h1:xujcQeF73rh4jwu3+zhfQsvV18x+7zIjlw7/CYbzGJ0= +github.com/charmbracelet/bubbletea v0.26.1/go.mod h1:FzKr7sKoO8iFVcdIBM9J0sJOcQv5nDQaYwsee3kpbgo= +github.com/charmbracelet/keygen v0.5.0 h1:XY0fsoYiCSM9axkrU+2ziE6u6YjJulo/b9Dghnw6MZc= +github.com/charmbracelet/keygen v0.5.0/go.mod h1:DfvCgLHxZ9rJxdK0DGw3C/LkV4SgdGbnliHcObV3L+8= +github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= +github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= +github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= +github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw= +github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g= +github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= +github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= +github.com/charmbracelet/ssh v0.0.0-20240118173142-6d7cf11c8371 h1:lgr2JbKDeq13Ar9gwmwELlJAF6R13pP3Kp37C9KLVM8= +github.com/charmbracelet/ssh v0.0.0-20240118173142-6d7cf11c8371/go.mod h1:l/6/exIt0QMHEW5IiqoY9HyPlSuXAm6xkngFIJgiEYI= +github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917 h1:NZKjJ7d/pzk/AfcJYEzmF8M48JlIrrY00RR5JdDc3io= +github.com/charmbracelet/ssh v0.0.0-20240401141849-854cddfa2917/go.mod h1:8/Ve8iGRRIGFM1kepYfRF2pEOF5Y3TEZYoJaA54228U= +github.com/charmbracelet/wish v1.2.1-0.20240122121046-e5d20f514768 h1:ic6QyQ7bBjmYbp8gr185DrWlbfIAfbJphHmoDBiKU3w= +github.com/charmbracelet/wish v1.2.1-0.20240122121046-e5d20f514768/go.mod h1:77Y9SA53dIQhxkb1sXc1Tea9dO3S/qH2I9jtRqXi8w0= +github.com/charmbracelet/wish v1.4.0 h1:pL1uVP/YuYgJheHEj98teZ/n6pMYnmlZq/fcHvomrfc= +github.com/charmbracelet/wish v1.4.0/go.mod h1:ew4/MjJVfW/akEO9KmrQHQv1F7bQRGscRMrA+KtovTk= +github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651 h1:3RXpZWGWTOeVXCTv0Dnzxdv/MhNUkBfEcbaTY0zrTQI= +github.com/charmbracelet/x/errors v0.0.0-20240117030013-d31dba354651/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= +github.com/charmbracelet/x/errors v0.0.0-20240425164147-ba2a9512b05f h1:pNRvHGEEjmP2E2MRxE4eCc2LiEa71dLzSlKDwUwkd1Q= +github.com/charmbracelet/x/errors v0.0.0-20240425164147-ba2a9512b05f/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0= +github.com/charmbracelet/x/exp/term v0.0.0-20240117031359-6e25c76a1efe h1:HeRgHWxOTu7l73rKsa5BRAeaUenmNyomiPCUHXv/y14= +github.com/charmbracelet/x/exp/term v0.0.0-20240117031359-6e25c76a1efe/go.mod h1:kOOxxyxgAFQVcR5yQJWTuLjzt5dR2pcgwy3WaLEudjE= +github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f h1:1BXkZqDueTOBECyDoFGRi0xMYgjJ6vvoPIkWyKOwzTc= +github.com/charmbracelet/x/exp/term v0.0.0-20240425164147-ba2a9512b05f/go.mod h1:yQqGHmheaQfkqiJWjklPHVAq1dKbk8uGbcoS/lcKCJ0= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -74,9 +90,11 @@ github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnht github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro= +github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= +github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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= @@ -88,6 +106,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +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/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= @@ -135,8 +155,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -199,12 +219,14 @@ github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0V github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= @@ -219,8 +241,8 @@ 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/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= -github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= +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/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= @@ -234,6 +256,8 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -261,10 +285,15 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/u-root/gobusybox/src v0.0.0-20221229083637-46b2883a7f90 h1:zTk5683I9K62wtZ6eUa6vu6IWwVHXPnoKK5n2unAwv0= +github.com/u-root/gobusybox/src v0.0.0-20221229083637-46b2883a7f90/go.mod h1:lYt+LVfZBBwDZ3+PHk4k/c/TnKOkjJXiJO73E32Mmpc= +github.com/u-root/u-root v0.11.0 h1:6gCZLOeRyevw7gbTwMj3fKxnr9+yHFlgF3N7udUVNO8= +github.com/u-root/u-root v0.11.0/go.mod h1:DBkDtiZyONk9hzVEdB/PWI9B4TxDkElWlVTHseglrZY= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -282,9 +311,10 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -295,8 +325,10 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= -golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= +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/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= +golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -371,8 +403,10 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -409,16 +443,19 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= -golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -427,8 +464,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/internal/tui/components/app/app.go b/internal/tui/components/app/app.go index e28ba476..a173e98c 100644 --- a/internal/tui/components/app/app.go +++ b/internal/tui/components/app/app.go @@ -2,6 +2,9 @@ package app import ( "fmt" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/wish" + "github.com/charmbracelet/wish/bubbletea" "github.com/robinovitch61/wander/internal/fileio" "os" "os/exec" @@ -12,6 +15,7 @@ import ( "github.com/charmbracelet/bubbles/key" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/ssh" "github.com/hashicorp/nomad/api" "github.com/itchyny/gojq" "github.com/robinovitch61/wander/internal/dev" @@ -68,6 +72,11 @@ type Model struct { config Config client api.Client + session ssh.Session + + renderer *lipgloss.Renderer + styles style.Styles + header header.Model compact bool currentPage nomad.Page @@ -107,17 +116,26 @@ func getFirstPage(c Config) nomad.Page { return firstPage } -func InitialModel(c Config) Model { +func InitialModel(c Config, session ssh.Session) Model { firstPage := getFirstPage(c) + var renderer *lipgloss.Renderer + if session != nil { + renderer = bubbletea.MakeRenderer(session) + } + styles := style.NewStyles(renderer) initialHeader := header.New( constants.LogoString, c.LogoColor, c.URL, c.Version, - nomad.GetPageKeyHelp(firstPage, false, false, false, nomad.StdOut, false, !c.StartAllTasksView), + nomad.GetPageKeyHelp(firstPage, false, false, false, nomad.StdOut, false, !c.StartAllTasksView, styles), + styles, ) return Model{ config: c, + session: session, + renderer: renderer, + styles: styles, header: initialHeader, currentPage: firstPage, updateID: nextUpdateID(), @@ -302,8 +320,15 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { c.Env = os.Environ() stdoutProxy := &nomad.StdoutProxy{} - c.Stdout = stdoutProxy m.getCurrentPageModel().SetDoesNeedNewInput() + if m.session != nil { + wc := wish.Command(m.session, c.Path, c.Args[1:]...) + wc.SetStdout(stdoutProxy) + return m, tea.Exec(wc, func(err error) tea.Msg { + return nomad.ExecCompleteMsg{Output: string(stdoutProxy.SavedOutput)} + }) + } + c.Stdout = stdoutProxy return m, tea.ExecProcess(c, func(err error) tea.Msg { return nomad.ExecCompleteMsg{Output: string(stdoutProxy.SavedOutput)} }) @@ -311,12 +336,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case fileio.SaveCompleteMessage: toastMsg := msg.SuccessMessage - toastStyle := style.SuccessToast + toastStyle := m.styles.SuccessToast if msg.Err != "" { toastMsg = fmt.Sprintf("Error: %s", msg.Err) - toastStyle = style.ErrorToast + toastStyle = m.styles.ErrorToast } - newToast := toast.New(toastMsg) + newToast := toast.New(toastMsg, m.styles.SuccessToast) m.getCurrentPageModel().SetToast(newToast, toastStyle) cmds = append(cmds, tea.Tick(newToast.Timeout, func(t time.Time) tea.Msg { return toast.TimeoutMsg{ID: newToast.ID} })) @@ -325,16 +350,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { "%s completed successfully", nomad.GetAllocAdminText(m.adminAction, msg.TaskName, msg.AllocName, msg.AllocID), ) - toastStyle := style.SuccessToast + toastStyle := m.styles.SuccessToast if msg.Err != nil { toastMsg = fmt.Sprintf( "%s failed with error: %s", nomad.GetAllocAdminText(m.adminAction, msg.TaskName, msg.AllocName, msg.AllocID), msg.Err.Error(), ) - toastStyle = style.ErrorToast + toastStyle = m.styles.ErrorToast } - newToast := toast.New(toastMsg) + newToast := toast.New(toastMsg, m.styles.SuccessToast) m.getCurrentPageModel().SetToast(newToast, toastStyle) cmds = append(cmds, tea.Tick(newToast.Timeout, func(t time.Time) tea.Msg { return toast.TimeoutMsg{ID: newToast.ID} })) @@ -343,16 +368,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { "%s completed successfully", nomad.GetJobAdminText(m.adminAction, msg.JobID), ) - toastStyle := style.SuccessToast + toastStyle := m.styles.SuccessToast if msg.Err != nil { toastMsg = fmt.Sprintf( "%s failed with error: %s", nomad.GetJobAdminText(m.adminAction, msg.JobID), msg.Err.Error(), ) - toastStyle = style.ErrorToast + toastStyle = m.styles.ErrorToast } - newToast := toast.New(toastMsg) + newToast := toast.New(toastMsg, m.styles.SuccessToast) m.getCurrentPageModel().SetToast(newToast, toastStyle) cmds = append(cmds, tea.Tick(newToast.Timeout, func(t time.Time) tea.Msg { return toast.TimeoutMsg{ID: newToast.ID} })) } @@ -389,9 +414,9 @@ func (m *Model) initialize() error { firstPage := getFirstPage(m.config) m.pageModels = make(map[nomad.Page]*page.Model) - for k, pageConfig := range nomad.GetAllPageConfigs(m.width, m.getPageHeight(), m.config.CompactTables) { + for k, pageConfig := range nomad.GetAllPageConfigs(m.width, m.getPageHeight(), m.config.CompactTables, m.styles) { startFiltering := m.config.StartFiltering && k == firstPage - p := page.New(pageConfig, m.config.CopySavePath, startFiltering, m.config.FilterWithContext) + p := page.New(pageConfig, m.config.CopySavePath, startFiltering, m.config.FilterWithContext, m.styles) m.pageModels[k] = &p } @@ -652,7 +677,7 @@ func (m *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd { case key.Matches(msg, keymap.KeyMap.StdOut): if !m.currentPageLoading() && m.logType != nomad.StdOut { m.logType = nomad.StdOut - m.getCurrentPageModel().SetViewportStyle(style.ViewportHeaderStyle, style.StdOut) + m.getCurrentPageModel().SetViewportStyle(m.styles.ViewportHeaderStyle, m.styles.StdOut) m.getCurrentPageModel().SetLoading(true) return m.getCurrentPageCmd() } @@ -660,8 +685,8 @@ func (m *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd { case key.Matches(msg, keymap.KeyMap.StdErr): if !m.currentPageLoading() && m.logType != nomad.StdErr { m.logType = nomad.StdErr - stdErrHeaderStyle := style.ViewportHeaderStyle.Copy().Inherit(style.StdErr) - m.getCurrentPageModel().SetViewportStyle(stdErrHeaderStyle, style.StdErr) + stdErrHeaderStyle := m.styles.ViewportHeaderStyle.Copy().Inherit(m.styles.StdErr) + m.getCurrentPageModel().SetViewportStyle(stdErrHeaderStyle, m.styles.StdErr) m.getCurrentPageModel().SetLoading(true) return m.getCurrentPageCmd() } @@ -688,7 +713,16 @@ func (m *Model) getCurrentPageModel() *page.Model { } func (m *Model) updateKeyHelp() { - newKeyHelp := nomad.GetPageKeyHelp(m.currentPage, m.currentPageFilterFocused(), m.currentPageFilterApplied(), m.currentPageViewportSaving(), m.logType, m.compact, m.inJobsMode) + newKeyHelp := nomad.GetPageKeyHelp( + m.currentPage, + m.currentPageFilterFocused(), + m.currentPageFilterApplied(), + m.currentPageViewportSaving(), + m.logType, + m.compact, + m.inJobsMode, + m.styles, + ) m.header.SetKeyHelp(newKeyHelp) } @@ -751,7 +785,7 @@ func (m Model) getCurrentPageCmd() tea.Cmd { case nomad.LoglinePage: return nomad.PrettifyLine(m.logline, nomad.LoglinePage) case nomad.StatsPage: - return nomad.FetchStats(m.client, m.alloc.ID, m.alloc.Name) + return nomad.FetchStats(m.client, m.alloc.ID, m.alloc.Name, m.styles) case nomad.AllocAdminPage: return func() tea.Msg { // this does no async work, just constructs the task admin menu @@ -848,5 +882,14 @@ func (m Model) currentPageViewportSaving() bool { } func (m Model) getFilterPrefix(page nomad.Page) string { - return page.GetFilterPrefix(m.config.Namespace, m.jobID, m.taskName, m.alloc.Name, m.alloc.ID, m.config.Event.Topics, m.config.Event.Namespace) + return page.GetFilterPrefix( + m.config.Namespace, + m.jobID, + m.taskName, + m.alloc.Name, + m.alloc.ID, + m.config.Event.Topics, + m.config.Event.Namespace, + m.styles, + ) } diff --git a/internal/tui/components/filter/filter.go b/internal/tui/components/filter/filter.go index d8694c11..82b70e96 100644 --- a/internal/tui/components/filter/filter.go +++ b/internal/tui/components/filter/filter.go @@ -20,9 +20,10 @@ type Model struct { textinput textinput.Model compact bool suffix string + styles style.Styles } -func New(prefix string) Model { +func New(prefix string, styles style.Styles) Model { ti := textinput.New() ti.Prompt = "" ti.Cursor.SetMode(cursor.CursorHide) @@ -30,6 +31,7 @@ func New(prefix string) Model { prefix: prefix, keyMap: keyMap, textinput: ti, + styles: styles, } } @@ -42,9 +44,9 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { func (m Model) View() string { if m.textinput.Focused() { - m.textinput.TextStyle = style.FilterEditing - m.textinput.PromptStyle = style.FilterEditing - m.textinput.Cursor.TextStyle = style.FilterEditing + m.textinput.TextStyle = m.styles.FilterEditing + m.textinput.PromptStyle = m.styles.FilterEditing + m.textinput.Cursor.TextStyle = m.styles.FilterEditing if len(m.textinput.Value()) > 0 { // editing existing filter m.textinput.Prompt = "filter: " @@ -59,14 +61,14 @@ func (m Model) View() string { if len(m.textinput.Value()) > 0 { // filter applied, not editing m.textinput.Prompt = "filter: " - m.textinput.Cursor.TextStyle = style.FilterApplied - m.textinput.PromptStyle = style.FilterApplied - m.textinput.TextStyle = style.FilterApplied + m.textinput.Cursor.TextStyle = m.styles.FilterApplied + m.textinput.PromptStyle = m.styles.FilterApplied + m.textinput.TextStyle = m.styles.FilterApplied } else { // no filter, not editing m.textinput.Prompt = "" - m.textinput.PromptStyle = style.Regular - m.textinput.TextStyle = style.Regular + m.textinput.PromptStyle = m.styles.Regular + m.textinput.TextStyle = m.styles.Regular m.textinput.SetValue("'/' to filter") m.SetSuffix("") } @@ -75,7 +77,7 @@ func (m Model) View() string { filterString := m.textinput.View() filterStringStyle := m.textinput.TextStyle.Copy().MarginLeft(1).PaddingLeft(1).PaddingRight(0) - filterPrefixStyle := style.FilterPrefix.Copy() + filterPrefixStyle := m.styles.FilterPrefix.Copy() if m.compact { filterPrefixStyle = filterPrefixStyle.UnsetBorderStyle().Padding(0).MarginLeft(0).MarginRight(1) } diff --git a/internal/tui/components/header/header.go b/internal/tui/components/header/header.go index 9db4825f..fcea09f3 100644 --- a/internal/tui/components/header/header.go +++ b/internal/tui/components/header/header.go @@ -8,30 +8,31 @@ import ( type Model struct { logo, logoColor, nomadUrl, version, keyHelp string compact bool + styles style.Styles } -func New(logo string, logoColor string, nomadUrl, version, keyHelp string) (m Model) { - return Model{logo: logo, logoColor: logoColor, nomadUrl: nomadUrl, version: version, keyHelp: keyHelp} +func New(logo string, logoColor string, nomadUrl, version, keyHelp string, styles style.Styles) (m Model) { + return Model{logo: logo, logoColor: logoColor, nomadUrl: nomadUrl, version: version, keyHelp: keyHelp, styles: styles} } func (m Model) View() string { - logoStyle := style.Logo.Copy() + logoStyle := m.styles.Logo.Copy() if m.logoColor != "" { logoStyle.Foreground(lipgloss.Color(m.logoColor)) } - clusterUrl := style.ClusterUrl.Render(m.nomadUrl) + clusterUrl := m.styles.ClusterUrl.Render(m.nomadUrl) if m.compact { return lipgloss.JoinHorizontal( lipgloss.Center, logoStyle.Padding(0).Margin(0).Render("WANDER"), - style.KeyHelp.Render(m.keyHelp), - style.Regular.Copy().Padding(0, 2, 0, 0).Render(m.version), + m.styles.KeyHelp.Render(m.keyHelp), + m.styles.Regular.Copy().Padding(0, 2, 0, 0).Render(m.version), clusterUrl, ) } logo := logoStyle.Render(m.logo) - left := style.Header.Render(lipgloss.JoinVertical(lipgloss.Center, logo, m.version, clusterUrl)) - styledKeyHelp := style.KeyHelp.Render(m.keyHelp) + left := m.styles.Header.Render(lipgloss.JoinVertical(lipgloss.Center, logo, m.version, clusterUrl)) + styledKeyHelp := m.styles.KeyHelp.Render(m.keyHelp) return lipgloss.JoinHorizontal(lipgloss.Center, left, styledKeyHelp) } diff --git a/internal/tui/components/page/page.go b/internal/tui/components/page/page.go index c7265941..024fd989 100644 --- a/internal/tui/components/page/page.go +++ b/internal/tui/components/page/page.go @@ -3,6 +3,7 @@ package page import ( "fmt" "github.com/robinovitch61/wander/internal/fileio" + "github.com/robinovitch61/wander/internal/tui/style" "strings" "github.com/atotto/clipboard" @@ -50,12 +51,12 @@ type Model struct { FilterWithContext bool } -func New(c Config, copySavePath, startFiltering, filterWithContext bool) Model { - pageFilter := filter.New("") +func New(c Config, copySavePath, startFiltering, filterWithContext bool, styles style.Styles) Model { + pageFilter := filter.New("", styles) if startFiltering { pageFilter.Focus() } - pageViewport := viewport.New(c.Width, c.Height-pageFilter.ViewHeight(), c.CompactTableContent) + pageViewport := viewport.New(c.Width, c.Height-pageFilter.ViewHeight(), c.CompactTableContent, styles) pageViewport.SetSelectionEnabled(c.SelectionEnabled) pageViewport.SetWrapText(c.WrapText) pageViewport.ConditionalStyle = c.ViewportConditionalStyle diff --git a/internal/tui/components/toast/toast.go b/internal/tui/components/toast/toast.go index e1148078..d9789af1 100644 --- a/internal/tui/components/toast/toast.go +++ b/internal/tui/components/toast/toast.go @@ -6,7 +6,6 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/robinovitch61/wander/internal/dev" "github.com/robinovitch61/wander/internal/tui/constants" - "github.com/robinovitch61/wander/internal/tui/style" "sync" "time" ) @@ -24,13 +23,13 @@ type Model struct { MessageStyle lipgloss.Style } -func New(message string) Model { +func New(message string, successStyle lipgloss.Style) Model { return Model{ ID: nextID(), message: message, Timeout: constants.ToastDuration, Visible: true, - MessageStyle: style.SuccessToast, + MessageStyle: successStyle, } } diff --git a/internal/tui/components/viewport/viewport.go b/internal/tui/components/viewport/viewport.go index 84083958..846efd5e 100644 --- a/internal/tui/components/viewport/viewport.go +++ b/internal/tui/components/viewport/viewport.go @@ -72,16 +72,18 @@ type Model struct { SpecialHighlightStyle lipgloss.Style ContentStyle lipgloss.Style FooterStyle lipgloss.Style + PseudoPromptStyle lipgloss.Style + ViewportStyle lipgloss.Style // ConditionalStyle styles lines containing key with corresponding style in value ConditionalStyle map[string]lipgloss.Style } -func New(width, height int, compactTableContent bool) (m Model) { +func New(width, height int, compactTableContent bool, styles style.Styles) (m Model) { m.saveDialog = textinput.New() m.saveDialog.Prompt = "> " - m.saveDialog.PromptStyle = style.SaveDialogPromptStyle - m.saveDialog.PlaceholderStyle = style.SaveDialogPlaceholderStyle - m.saveDialog.TextStyle = style.SaveDialogTextStyle + m.saveDialog.PromptStyle = styles.SaveDialogPromptStyle + m.saveDialog.PlaceholderStyle = styles.SaveDialogPlaceholderStyle + m.saveDialog.TextStyle = styles.SaveDialogTextStyle m.setWidthAndHeight(width, height) @@ -94,11 +96,13 @@ func New(width, height int, compactTableContent bool) (m Model) { m.SpecialContentIdx = -1 - m.HeaderStyle = style.ViewportHeaderStyle - m.SelectedContentStyle = style.ViewportSelectedRowStyle - m.HighlightStyle = style.ViewportHighlightStyle - m.SpecialHighlightStyle = style.ViewportSpecialHighlightStyle - m.FooterStyle = style.ViewportFooterStyle + m.HeaderStyle = styles.ViewportHeaderStyle + m.SelectedContentStyle = styles.ViewportSelectedRowStyle + m.HighlightStyle = styles.ViewportHighlightStyle + m.SpecialHighlightStyle = styles.ViewportSpecialHighlightStyle + m.FooterStyle = styles.ViewportFooterStyle + m.PseudoPromptStyle = styles.PseudoPrompt + m.ViewportStyle = styles.Viewport return m } @@ -307,7 +311,7 @@ func (m Model) View() string { } if m.showPrompt { - viewString = strings.TrimRight(viewString, "\n") + style.PseudoPrompt.Render(" ") + "\n" + viewString = strings.TrimRight(viewString, "\n") + m.PseudoPromptStyle.Render(" ") + "\n" } if footerHeight > 0 { @@ -316,7 +320,7 @@ func (m Model) View() string { viewString += strings.Repeat("\n", padCount) viewString += footerString } - renderedViewString := style.Viewport.Width(m.width).Height(m.height).Render(viewString) + renderedViewString := m.ViewportStyle.Width(m.width).Height(m.height).Render(viewString) if m.toast.Visible { lines := strings.Split(renderedViewString, "\n") diff --git a/internal/tui/constants/constants.go b/internal/tui/constants/constants.go index 73b2ad06..db278f5a 100644 --- a/internal/tui/constants/constants.go +++ b/internal/tui/constants/constants.go @@ -1,8 +1,6 @@ package constants import ( - "github.com/charmbracelet/lipgloss" - "github.com/robinovitch61/wander/internal/tui/style" "strings" "time" ) @@ -20,13 +18,6 @@ const TableSeparator = "|【=◈︿◈=】|" const TablePadding = " " -var JobsTableStatusStyles = map[string]lipgloss.Style{ - TablePadding + "pending" + TablePadding: style.JobRowPending, - TablePadding + "dead" + TablePadding: style.JobRowDead, -} - -var TasksTableStatusStyles = JobsTableStatusStyles - const DefaultPageInput = "/bin/sh" // DefaultEventJQQuery is a single line as this shows up verbatim in `wander --help` diff --git a/internal/tui/nomad/pages.go b/internal/tui/nomad/pages.go index 99636688..0e77f833 100644 --- a/internal/tui/nomad/pages.go +++ b/internal/tui/nomad/pages.go @@ -7,7 +7,6 @@ import ( "github.com/hashicorp/nomad/api" "github.com/robinovitch61/wander/internal/tui/components/page" "github.com/robinovitch61/wander/internal/tui/components/viewport" - "github.com/robinovitch61/wander/internal/tui/constants" "github.com/robinovitch61/wander/internal/tui/formatter" "github.com/robinovitch61/wander/internal/tui/keymap" "github.com/robinovitch61/wander/internal/tui/style" @@ -42,21 +41,21 @@ const ( JobAdminConfirmPage ) -func GetAllPageConfigs(width, height int, compactTables bool) map[Page]page.Config { +func GetAllPageConfigs(width, height int, compactTables bool, styles style.Styles) map[Page]page.Config { return map[Page]page.Config{ JobsPage: { Width: width, Height: height, LoadingString: JobsPage.LoadingString(), SelectionEnabled: true, WrapText: false, RequestInput: false, CompactTableContent: compactTables, - ViewportConditionalStyle: constants.JobsTableStatusStyles, + ViewportConditionalStyle: style.GetTableStyles(styles), }, AllTasksPage: { Width: width, Height: height, LoadingString: AllTasksPage.LoadingString(), SelectionEnabled: true, WrapText: false, RequestInput: false, CompactTableContent: compactTables, - ViewportConditionalStyle: constants.TasksTableStatusStyles, + ViewportConditionalStyle: style.GetTableStyles(styles), }, JobSpecPage: { Width: width, Height: height, @@ -104,7 +103,7 @@ func GetAllPageConfigs(width, height int, compactTables bool) map[Page]page.Conf LoadingString: JobTasksPage.LoadingString(), SelectionEnabled: true, WrapText: false, RequestInput: false, CompactTableContent: compactTables, - ViewportConditionalStyle: constants.TasksTableStatusStyles, + ViewportConditionalStyle: style.GetTableStyles(styles), }, ExecPage: { Width: width, Height: height, @@ -371,65 +370,65 @@ func (p Page) Backward(inJobsMode bool) Page { return p } -func allocEventFilterPrefix(allocName, allocID string) string { - return fmt.Sprintf("%s %s", style.Bold.Render(allocName), formatter.ShortAllocID(allocID)) +func allocEventFilterPrefix(allocName, allocID string, styles style.Styles) string { + return fmt.Sprintf("%s %s", styles.Bold.Render(allocName), formatter.ShortAllocID(allocID)) } -func taskFilterPrefix(taskName, allocName string) string { - return fmt.Sprintf("%s in %s", style.Bold.Render(taskName), allocName) +func taskFilterPrefix(taskName, allocName string, styles style.Styles) string { + return fmt.Sprintf("%s in %s", styles.Bold.Render(taskName), allocName) } -func namespaceFilterPrefix(namespace string) string { +func namespaceFilterPrefix(namespace string, styles style.Styles) string { if namespace == "*" { return "All Namespaces" } - return fmt.Sprintf("Namespace %s", style.Bold.Render(namespace)) + return fmt.Sprintf("Namespace %s", styles.Bold.Render(namespace)) } -func (p Page) GetFilterPrefix(namespace, jobID, taskName, allocName, allocID string, eventTopics Topics, eventNamespace string) string { +func (p Page) GetFilterPrefix(namespace, jobID, taskName, allocName, allocID string, eventTopics Topics, eventNamespace string, styles style.Styles) string { switch p { case JobsPage: - return fmt.Sprintf("Jobs in %s", namespaceFilterPrefix(namespace)) + return fmt.Sprintf("Jobs in %s", namespaceFilterPrefix(namespace, styles)) case AllTasksPage: - return fmt.Sprintf("All Tasks in %s", namespaceFilterPrefix(namespace)) + return fmt.Sprintf("All Tasks in %s", namespaceFilterPrefix(namespace, styles)) case JobSpecPage: - return fmt.Sprintf("Spec for Job %s", style.Bold.Render(jobID)) + return fmt.Sprintf("Spec for Job %s", styles.Bold.Render(jobID)) case JobEventsPage: - return fmt.Sprintf("Events for Job %s (%s)", style.Bold.Render(jobID), getTopicNames(eventTopics)) + return fmt.Sprintf("Events for Job %s (%s)", styles.Bold.Render(jobID), getTopicNames(eventTopics)) case JobEventPage: - return fmt.Sprintf("Event for Job %s", style.Bold.Render(jobID)) + return fmt.Sprintf("Event for Job %s", styles.Bold.Render(jobID)) case JobMetaPage: return fmt.Sprintf("Meta for Job %s", jobID) case AllocEventsPage: - return fmt.Sprintf("Events for Allocation %s", allocEventFilterPrefix(allocName, allocID)) + return fmt.Sprintf("Events for Allocation %s", allocEventFilterPrefix(allocName, allocID, styles)) case AllocEventPage: - return fmt.Sprintf("Event for Allocation %s", allocEventFilterPrefix(allocName, allocID)) + return fmt.Sprintf("Event for Allocation %s", allocEventFilterPrefix(allocName, allocID, styles)) case AllEventsPage: return fmt.Sprintf("All Events in Namespace %s (%s)", eventNamespace, formatEventTopics(eventTopics)) case AllEventPage: return "Event" case JobTasksPage: - return fmt.Sprintf("Tasks for Job %s", style.Bold.Render(jobID)) + return fmt.Sprintf("Tasks for Job %s", styles.Bold.Render(jobID)) case ExecPage: - return fmt.Sprintf("Exec for Task %s", taskFilterPrefix(taskName, allocName)) + return fmt.Sprintf("Exec for Task %s", taskFilterPrefix(taskName, allocName, styles)) case ExecCompletePage: - return fmt.Sprintf("Exec Complete for Task %s", taskFilterPrefix(taskName, allocName)) + return fmt.Sprintf("Exec Complete for Task %s", taskFilterPrefix(taskName, allocName, styles)) case AllocSpecPage: - return fmt.Sprintf("Spec for Allocation %s %s", style.Bold.Render(allocName), formatter.ShortAllocID(allocID)) + return fmt.Sprintf("Spec for Allocation %s %s", styles.Bold.Render(allocName), formatter.ShortAllocID(allocID)) case LogsPage: - return fmt.Sprintf("Logs for Task %s", taskFilterPrefix(taskName, allocName)) + return fmt.Sprintf("Logs for Task %s", taskFilterPrefix(taskName, allocName, styles)) case LoglinePage: - return fmt.Sprintf("Log Line for Task %s", taskFilterPrefix(taskName, allocName)) + return fmt.Sprintf("Log Line for Task %s", taskFilterPrefix(taskName, allocName, styles)) case StatsPage: return fmt.Sprintf("Stats for Allocation %s", allocName) case AllocAdminPage: - return fmt.Sprintf("Admin Actions for Allocation %s %s", style.Bold.Render(allocName), formatter.ShortAllocID(allocID)) + return fmt.Sprintf("Admin Actions for Allocation %s %s", styles.Bold.Render(allocName), formatter.ShortAllocID(allocID)) case AllocAdminConfirmPage: - return fmt.Sprintf("Confirm Admin Action for Allocation %s %s", style.Bold.Render(allocName), formatter.ShortAllocID(allocID)) + return fmt.Sprintf("Confirm Admin Action for Allocation %s %s", styles.Bold.Render(allocName), formatter.ShortAllocID(allocID)) case JobAdminPage: - return fmt.Sprintf("Admin Actions for Job %s", style.Bold.Render(jobID)) + return fmt.Sprintf("Admin Actions for Job %s", styles.Bold.Render(jobID)) case JobAdminConfirmPage: - return fmt.Sprintf("Confirm Admin Action for Job %s", style.Bold.Render(jobID)) + return fmt.Sprintf("Confirm Admin Action for Job %s", styles.Bold.Render(jobID)) default: panic("page not found") } @@ -467,10 +466,10 @@ func UpdatePageDataWithDelay(id int, p Page, d time.Duration) tea.Cmd { return nil } -func getShortHelp(bindings []key.Binding) string { +func getShortHelp(bindings []key.Binding, styles style.Styles) string { var output string for _, km := range bindings { - output += style.KeyHelpKey.Render(km.Help().Key) + " " + style.KeyHelpDescription.Render(km.Help().Desc) + " " + output += styles.KeyHelpKey.Render(km.Help().Key) + " " + styles.KeyHelpDescription.Render(km.Help().Desc) + " " } output = strings.TrimSpace(output) return output @@ -485,10 +484,11 @@ func GetPageKeyHelp( filterFocused, filterApplied, saving bool, logType LogType, compact, inJobsMode bool, + styles style.Styles, ) string { if compact { changeKeyHelp(&keymap.KeyMap.Compact, "expand header") - return getShortHelp([]key.Binding{keymap.KeyMap.Compact}) + return getShortHelp([]key.Binding{keymap.KeyMap.Compact}, styles) } else { changeKeyHelp(&keymap.KeyMap.Compact, "compact") } @@ -564,26 +564,26 @@ func GetPageKeyHelp( if currentPage == ExecPage { changeKeyHelp(&keymap.KeyMap.Forward, "run command") secondRow = append(fourthRow, keymap.KeyMap.Forward) - return getShortHelp(firstRow) + "\n" + getShortHelp(secondRow) + return getShortHelp(firstRow, styles) + "\n" + getShortHelp(secondRow, styles) } if saving { changeKeyHelp(&keymap.KeyMap.Forward, "confirm save") changeKeyHelp(&keymap.KeyMap.Back, "cancel save") secondRow = []key.Binding{keymap.KeyMap.Back, keymap.KeyMap.Forward} - return getShortHelp(firstRow) + "\n" + getShortHelp(secondRow) + return getShortHelp(firstRow, styles) + "\n" + getShortHelp(secondRow, styles) } if filterFocused { changeKeyHelp(&keymap.KeyMap.Forward, "apply filter") changeKeyHelp(&keymap.KeyMap.Back, "cancel filter") secondRow = []key.Binding{keymap.KeyMap.Back, keymap.KeyMap.Forward} - return getShortHelp(firstRow) + "\n" + getShortHelp(secondRow) + return getShortHelp(firstRow, styles) + "\n" + getShortHelp(secondRow, styles) } var final string for _, row := range [][]key.Binding{firstRow, secondRow, thirdRow, fourthRow} { - final += getShortHelp(row) + "\n" + final += getShortHelp(row, styles) + "\n" } return strings.TrimRight(final, "\n") diff --git a/internal/tui/nomad/stats.go b/internal/tui/nomad/stats.go index 762fc80b..20baf842 100644 --- a/internal/tui/nomad/stats.go +++ b/internal/tui/nomad/stats.go @@ -10,7 +10,7 @@ import ( "github.com/robinovitch61/wander/internal/tui/style" ) -func FetchStats(client api.Client, allocID, allocName string) tea.Cmd { +func FetchStats(client api.Client, allocID, allocName string, styles style.Styles) tea.Cmd { return func() tea.Msg { alloc, _, err := client.Allocations().Info(allocID, nil) if err != nil { @@ -47,9 +47,9 @@ func FetchStats(client api.Client, allocID, allocName string) tea.Cmd { memMiB := float64(allocMemory.Usage) / 1024 / 1024 givenMemMiB := *allocatedMemoryMB perc := memMiB / float64(givenMemMiB) * 100 - stylePercent := style.Regular + stylePercent := styles.Regular if perc > 100 { - stylePercent = style.StatBad + stylePercent = styles.StatBad } percStr := stylePercent.Render(fmt.Sprintf("%.1f%%", perc)) allocMemoryTableVal = fmt.Sprintf("%.0f/%d MiB (%s)", memMiB, givenMemMiB, percStr) @@ -59,9 +59,9 @@ func FetchStats(client api.Client, allocID, allocName string) tea.Cmd { cpuMhz := int(allocCpu.TotalTicks) givenCpuMhz := *allocatedCpuMhz perc := float64(cpuMhz) / float64(givenCpuMhz) * 100 - stylePercent := style.Regular + stylePercent := styles.Regular if perc > 100 { - stylePercent = style.StatBad + stylePercent = styles.StatBad } percStr := stylePercent.Render(fmt.Sprintf("%.1f%%", perc)) allocCpuTableVal = fmt.Sprintf("%d/%d MHz (%s)", cpuMhz, givenCpuMhz, percStr) @@ -87,9 +87,9 @@ func FetchStats(client api.Client, allocID, allocName string) tea.Cmd { memMiB := float64(taskMemory.Usage) / 1024 / 1024 givenMemMiB := *taskGivenResources.MemoryMB perc := memMiB / float64(givenMemMiB) * 100 - stylePercent := style.Regular + stylePercent := styles.Regular if perc > 100 { - stylePercent = style.StatBad + stylePercent = styles.StatBad } percStr := stylePercent.Render(fmt.Sprintf("%.1f%%", perc)) taskMemoryTableVal = fmt.Sprintf("%.0f/%d MiB (%s)", memMiB, givenMemMiB, percStr) @@ -99,9 +99,9 @@ func FetchStats(client api.Client, allocID, allocName string) tea.Cmd { cpuMhz := int(taskCpu.TotalTicks) givenCpuMhz := *taskGivenResources.CPU perc := float64(cpuMhz) / float64(givenCpuMhz) * 100 - stylePercent := style.Regular + stylePercent := styles.Regular if perc > 100 { - stylePercent = style.StatBad + stylePercent = styles.StatBad } percStr := stylePercent.Render(fmt.Sprintf("%.1f%%", perc)) taskCpuTableVal = fmt.Sprintf("%d/%d MHz (%s)", cpuMhz, givenCpuMhz, percStr) diff --git a/internal/tui/style/style.go b/internal/tui/style/style.go index dbafb792..7970d426 100644 --- a/internal/tui/style/style.go +++ b/internal/tui/style/style.go @@ -1,6 +1,9 @@ package style -import "github.com/charmbracelet/lipgloss" +import ( + "github.com/charmbracelet/lipgloss" + "github.com/robinovitch61/wander/internal/tui/constants" +) const ( black = lipgloss.Color("#000000") @@ -14,33 +17,102 @@ const ( yellow = lipgloss.Color("#DBBD70") ) -var ( - Regular = lipgloss.NewStyle() - Bold = Regular.Copy().Bold(true) - Logo = Regular.Copy().Padding(0, 0).Foreground(yellow) - ClusterUrl = Bold.Copy() - KeyHelp = Regular.Copy().Padding(0, 1) - KeyHelpKey = Regular.Copy().Foreground(blue).Bold(true) - KeyHelpDescription = Regular.Copy() - Header = Regular.Copy().Padding(0, 1).Border(lipgloss.RoundedBorder(), true) - FilterPrefix = Regular.Copy().Padding(0, 3).Border(lipgloss.NormalBorder(), true) - FilterEditing = Regular.Copy().Foreground(black).Background(blue) - FilterApplied = Regular.Copy().Foreground(black).Background(greenblue) - JobRowPending = Regular.Copy().Foreground(yellow) - JobRowDead = Regular.Copy().Foreground(red) - StatBad = Regular.Copy().Foreground(black).Background(red) - PseudoPrompt = Regular.Copy().Background(blue) - Viewport = Regular.Copy() - ViewportHeaderStyle = Bold.Copy() - ViewportSelectedRowStyle = Regular.Copy().Foreground(black).Background(blue) - ViewportHighlightStyle = Regular.Copy().Foreground(black).Background(pink) - ViewportSpecialHighlightStyle = Regular.Copy().Foreground(black).Background(yellow) - ViewportFooterStyle = Regular.Copy().Foreground(grey) - SaveDialogPromptStyle = Regular.Copy().Background(darkred).Foreground(black) - SaveDialogPlaceholderStyle = Regular.Copy().Background(darkred).Foreground(black) - SaveDialogTextStyle = Regular.Copy().Background(darkred).Foreground(black) - StdOut = Regular.Copy().UnsetForeground() - StdErr = Regular.Copy().Foreground(red) - SuccessToast = Bold.Copy().PaddingLeft(1).Foreground(black).Background(darkgreen) - ErrorToast = Bold.Copy().PaddingLeft(1).Foreground(black).Background(darkred) -) +type Styles struct { + Regular lipgloss.Style + Bold lipgloss.Style + Logo lipgloss.Style + ClusterUrl lipgloss.Style + KeyHelp lipgloss.Style + KeyHelpKey lipgloss.Style + KeyHelpDescription lipgloss.Style + Header lipgloss.Style + FilterPrefix lipgloss.Style + FilterEditing lipgloss.Style + FilterApplied lipgloss.Style + JobRowPending lipgloss.Style + JobRowDead lipgloss.Style + StatBad lipgloss.Style + PseudoPrompt lipgloss.Style + Viewport lipgloss.Style + ViewportHeaderStyle lipgloss.Style + ViewportSelectedRowStyle lipgloss.Style + ViewportHighlightStyle lipgloss.Style + ViewportSpecialHighlightStyle lipgloss.Style + ViewportFooterStyle lipgloss.Style + SaveDialogPromptStyle lipgloss.Style + SaveDialogPlaceholderStyle lipgloss.Style + SaveDialogTextStyle lipgloss.Style + StdOut lipgloss.Style + StdErr lipgloss.Style + SuccessToast lipgloss.Style + ErrorToast lipgloss.Style +} + +func NewStyles(renderer *lipgloss.Renderer) Styles { + var regular = renderer.NewStyle() + var bold = regular.Copy().Bold(true) + var logo = regular.Copy().Padding(0, 0).Foreground(yellow) + var clusterUrl = bold.Copy() + var keyHelp = regular.Copy().Padding(0, 1) + var keyHelpKey = regular.Copy().Foreground(blue).Bold(true) + var keyHelpDescription = regular.Copy() + var header = regular.Copy().Padding(0, 1).Border(lipgloss.RoundedBorder(), true) + var filterPrefix = regular.Copy().Padding(0, 3).Border(lipgloss.NormalBorder(), true) + var filterEditing = regular.Copy().Foreground(black).Background(blue) + var filterApplied = regular.Copy().Foreground(black).Background(greenblue) + var jobRowPending = regular.Copy().Foreground(yellow) + var jobRowDead = regular.Copy().Foreground(red) + var statBad = regular.Copy().Foreground(black).Background(red) + var pseudoPrompt = regular.Copy().Background(blue) + var viewport = regular.Copy() + var viewportHeaderStyle = bold.Copy() + var viewportSelectedRowStyle = regular.Copy().Foreground(black).Background(blue) + var viewportHighlightStyle = regular.Copy().Foreground(black).Background(pink) + var viewportSpecialHighlightStyle = regular.Copy().Foreground(black).Background(yellow) + var viewportFooterStyle = regular.Copy().Foreground(grey) + var saveDialogPromptStyle = regular.Copy().Background(darkred).Foreground(black) + var saveDialogPlaceholderStyle = regular.Copy().Background(darkred).Foreground(black) + var saveDialogTextStyle = regular.Copy().Background(darkred).Foreground(black) + var stdOut = regular.Copy().UnsetForeground() + var stdErr = regular.Copy().Foreground(red) + var successToast = bold.Copy().PaddingLeft(1).Foreground(black).Background(darkgreen) + var errorToast = bold.Copy().PaddingLeft(1).Foreground(black).Background(darkred) + + return Styles{ + Regular: regular, + Bold: bold, + Logo: logo, + ClusterUrl: clusterUrl, + KeyHelp: keyHelp, + KeyHelpKey: keyHelpKey, + KeyHelpDescription: keyHelpDescription, + Header: header, + FilterPrefix: filterPrefix, + FilterEditing: filterEditing, + FilterApplied: filterApplied, + JobRowPending: jobRowPending, + JobRowDead: jobRowDead, + StatBad: statBad, + PseudoPrompt: pseudoPrompt, + Viewport: viewport, + ViewportHeaderStyle: viewportHeaderStyle, + ViewportSelectedRowStyle: viewportSelectedRowStyle, + ViewportHighlightStyle: viewportHighlightStyle, + ViewportSpecialHighlightStyle: viewportSpecialHighlightStyle, + ViewportFooterStyle: viewportFooterStyle, + SaveDialogPromptStyle: saveDialogPromptStyle, + SaveDialogPlaceholderStyle: saveDialogPlaceholderStyle, + SaveDialogTextStyle: saveDialogTextStyle, + StdOut: stdOut, + StdErr: stdErr, + SuccessToast: successToast, + ErrorToast: errorToast, + } +} + +func GetTableStyles(s Styles) map[string]lipgloss.Style { + return map[string]lipgloss.Style{ + constants.TablePadding + "pending" + constants.TablePadding: s.JobRowPending, + constants.TablePadding + "dead" + constants.TablePadding: s.JobRowDead, + } +}