From 7e11894743faf826c809d11c355523e1342b9e5d Mon Sep 17 00:00:00 2001 From: Ere Mid Date: Mon, 16 Jan 2023 22:09:32 +0100 Subject: [PATCH] feat: new kubectl get view --- cmd/root.go | 3 +- config/config.go | 3 +- go.mod | 1 + go.sum | 13 +-- main.go | 15 ++- tools/tools.go | 30 ++++-- tui/keymap.go | 15 ++- tui/pages/examples/examples.go | 186 ++++++++++++++++++++++++++++++--- tui/pages/header/header.go | 37 +++++-- tui/theme.go | 10 ++ 10 files changed, 263 insertions(+), 50 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 4a3c0fd..001ba54 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -33,9 +33,10 @@ var ( ) // Execute executes the root command. -func Execute() { +func Execute(version string) { rootCmd.PersistentFlags().StringVarP(&c.Path, "path", "p", ".", "your provider path") rootCmd.AddCommand(listTestedCmd) + c.Version = version err := rootCmd.Execute() if err != nil { os.Exit(1) diff --git a/config/config.go b/config/config.go index a879153..4784946 100644 --- a/config/config.go +++ b/config/config.go @@ -3,5 +3,6 @@ package config // Provider is the configuration provider type Provider struct { - Path string + Path string + Version string } diff --git a/go.mod b/go.mod index c0f9728..e8abe10 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52 v1.2.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/containerd/console v1.0.3 // indirect github.com/dlclark/regexp2 v1.8.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect diff --git a/go.sum b/go.sum index d5bb184..9cdbfc8 100644 --- a/go.sum +++ b/go.sum @@ -2,7 +2,6 @@ github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbf github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTxab9L4E= github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= @@ -15,6 +14,7 @@ github.com/charmbracelet/bubbletea v0.23.1 h1:CYdteX1wCiCzKNUlwm25ZHBIc1GXlYFyUI github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= +github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= @@ -27,7 +27,6 @@ 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/dietsche/rfsnotify v0.0.0-20200716145600-b37be6e4177f h1:b3QvpXLSx1U13VM79rSkA+6Xv4lmT/urEMzA36Yma0U= github.com/dietsche/rfsnotify v0.0.0-20200716145600-b37be6e4177f/go.mod h1:ztitxkMUaBsHRey1tS5xFCd4gm/zAQwA9yfCP5y4cAA= -github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dlclark/regexp2 v1.8.0 h1:rJD5HeGIT/2b5CDk63FVCwZA3qgYElfg+oQK7uH5pfE= github.com/dlclark/regexp2 v1.8.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= @@ -35,7 +34,6 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4 github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= @@ -44,7 +42,6 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -58,7 +55,6 @@ github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWV github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg= github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a h1:jlDOeO5TU0pYlbc/y6PFguab5IjANI0Knrpg3u/ton4= github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= @@ -77,7 +73,6 @@ github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6 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.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -92,7 +87,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.5.3 h1:3HUJmBFbQW9fhQOzMgseU134xfi6hU+mjWywx5Ty+/M= github.com/yuin/goldmark v1.5.3/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -100,7 +94,6 @@ github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18W github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= golang.org/x/exp v0.0.0-20230116083435-1de6713980de h1:DBWn//IJw30uYCgERoxCg84hWtA97F4wMiKOIh00Uf0= golang.org/x/exp v0.0.0-20230116083435-1de6713980de/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= -golang.org/x/net v0.0.0-20221002022538-bcab6841153b h1:6e93nYa3hNqAvLr0pD4PN1fFS+gKzp2zAXqrnTCstqU= golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw= golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= @@ -112,15 +105,11 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg= golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k= golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= diff --git a/main.go b/main.go index c6428e7..9329a32 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,19 @@ // Package main provides the entry point for the application package main -import "github.com/FrangipaneTeam/bean/cmd" +import ( + _ "embed" + "strings" + + "github.com/FrangipaneTeam/bean/cmd" +) + +var ( + //go:embed version.txt + version string +) func main() { - cmd.Execute() + version = strings.TrimSuffix(version, "\n") + cmd.Execute(version) } diff --git a/tools/tools.go b/tools/tools.go index 6e85752..81b7f7c 100644 --- a/tools/tools.go +++ b/tools/tools.go @@ -24,8 +24,11 @@ import ( // KubectlResult is the result of a kubectl command type KubectlResult struct { - Verb string - Out string + Verb string + Out string + CmdID string + Index int + Item *tui.Example } // Markdown is a struct that holds the content of a markdown file @@ -37,12 +40,24 @@ type Markdown struct { type ErrorMsg struct { Reason string Cause error + CmdID string + Index int + Item *tui.Example } // Kubectl runs a kubectl command -func Kubectl(verb string, file string) tea.Cmd { +func Kubectl(verb string, file string, cmdID string) tea.Cmd { return func() tea.Msg { - args := []string{verb, "-f", file} + if verb == "" || file == "" { + return nil + } + + var args []string + if verb == "managed" { + args = []string{"get", "managed"} + } else { + args = []string{verb, "-f", file} + } cmd := exec.Command("kubectl", args...) var stdout, stderr bytes.Buffer @@ -53,11 +68,14 @@ func Kubectl(verb string, file string) tea.Cmd { return ErrorMsg{ Reason: fmt.Sprintf("command kubectl %s failed", strings.Join(args, " ")), Cause: errors.New(stderr.String()), + CmdID: cmdID, } } + return KubectlResult{ - Out: stdout.String(), - Verb: verb, + Out: stdout.String(), + Verb: verb, + CmdID: cmdID, } } } diff --git a/tui/keymap.go b/tui/keymap.go index afde1d1..f24378a 100644 --- a/tui/keymap.go +++ b/tui/keymap.go @@ -145,10 +145,23 @@ func (m *ListKeyMap) YamlHelp() { {m.UpDown, m.LeftRight, m.Back}, {m.ForwardSlash, m.Enter}, {m.Help, m.Quit}, - {m.Apply, m.Delete, m.Print, m.Get, m.ShowDependanciesFiles}, + {m.Apply, m.Delete, m.Print, m.Get}, + {m.ShowDependanciesFiles}, } } +// GetHelp sets the keymap for the get view +func (m *ListKeyMap) GetHelp() { + m.ActiveShortHelp = []key.Binding{ + m.Apply, + m.Delete, + m.Back, + m.Quit, + } + + m.ActiveFullHelp = [][]key.Binding{} +} + // YamlActionHelp sets the keymap for the yaml action view func (m *ListKeyMap) YamlActionHelp() { diff --git a/tui/pages/examples/examples.go b/tui/pages/examples/examples.go index db1848b..bd85323 100644 --- a/tui/pages/examples/examples.go +++ b/tui/pages/examples/examples.go @@ -3,7 +3,9 @@ package examples import ( "fmt" + "math/rand" "strings" + "time" "github.com/FrangipaneTeam/bean/config" "github.com/FrangipaneTeam/bean/tools" @@ -15,6 +17,7 @@ import ( "github.com/FrangipaneTeam/bean/tui/pages/md" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/list" + "github.com/charmbracelet/bubbles/progress" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) @@ -27,6 +30,26 @@ const ( pK8S = "k8s" ) +var ( + letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + k8sCmdList map[string]*k8sCmd +) + +func randSeq(n int) string { + b := make([]rune, n) + for i := range b { + b[i] = letters[rand.Intn(len(letters))] + } + return string(b) +} + +type k8sCmd struct { + done bool + canceled bool + verb string + cmdOutput string +} + type model struct { exampleList map[string][]list.Item currentList list.Model @@ -48,9 +71,18 @@ type model struct { config config.Provider - k8sOutput string + k8sCurrentFiles string + k8sCurrentIDView string + k8sProgressMsg string + k8sCurrentKind string + progressK8SGet progress.Model + tickRunning bool + + previousItemPostion int } +type tickK8SGet time.Time + // New returns a new model of the examples page. // nolint: golint // model not used outside of this package func New(e tui.LoadedExamples, width, height int, c config.Provider) model { @@ -81,17 +113,22 @@ func New(e tui.LoadedExamples, width, height int, c config.Provider) model { list.Title = "Choose an example" list.DisableQuitKeybindings() list.SetShowHelp(false) + // list.StatusMessageLifetime = 5 + list.SetStatusBarItemName("example", "examples") // list.Help = help.Model{}` header := header.New( - "Bean", + "Bean "+c.Version, "A FrangipaneTeam bin", width, int(float64(height)*0.2), c, ) + footer := footer.New(width, int(float64(width)*0.2), listKeys) + k8sCmdList = make(map[string]*k8sCmd) + return model{ exampleList: e.Examples, currentList: list, @@ -108,6 +145,11 @@ func New(e tui.LoadedExamples, width, height int, c config.Provider) model { config: c, showDependenciesFiles: true, + progressK8SGet: progress.New( + progress.WithSolidFill("#CBEDD5"), + progress.WithoutPercentage(), + progress.WithWidth(10), + ), } } @@ -130,6 +172,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { cmd tea.Cmd cmds []tea.Cmd ) + switch msg := msg.(type) { // Is it a key press? @@ -145,31 +188,40 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case key.Matches(msg, m.keys.Back): m.errorRaised = false + m.tickRunning = false + m.k8sCurrentFiles = "" + + // back to the list, cancel all k8sCmd + for _, v := range k8sCmdList { + v.canceled = true + } + + cmd = m.progressK8SGet.SetPercent(0) + cmds = append(cmds, cmd) switch m.viewName { - // case "root": - // m.keys.RootHelp() - // return m, nil case pRessources: m.viewName = pRoot m.keys.RootHelp() cmd = m.currentList.NewStatusMessage("back to home !") - m.currentList.ResetSelected() + // m.currentList.ResetSelected() cmds = append(cmds, cmd) m, cmd = m.showExamples() case pViewPort: m.viewName = pRoot m.keys.RootHelp() case pPrintActions, pK8S: + m.keys.Get.SetEnabled(true) m.viewName = pRessources m.keys.YamlHelp() cmd = m.currentList.NewStatusMessage("back to " + m.listName) - m.currentList.ResetSelected() + // m.currentList.ResetSelected() cmds = append(cmds, cmd) m, cmd = m.showYaml(m.listName) } cmds = append(cmds, cmd) + return m, tea.Batch(cmds...) case key.Matches(msg, m.keys.Enter): if m.viewName != pRoot { @@ -178,6 +230,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { title := m.currentList.SelectedItem().(*tui.Example).Title() m, cmd = m.showYaml(title) m.viewName = pRessources + m.previousItemPostion = m.currentList.Index() m.keys.YamlHelp() return m, cmd @@ -221,7 +274,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd case key.Matches(msg, m.keys.Get), key.Matches(msg, m.keys.Apply), key.Matches(msg, m.keys.Delete): - if m.viewName == pRessources { + if m.viewName == pRessources || m.viewName == pK8S { + if m.viewName != pK8S { + m.keys.Get.SetEnabled(true) + } + file := m.currentList.SelectedItem().(*tui.Example).Title() extra := m.currentList.SelectedItem().(*tui.Example).HaveExtraFile() secret := m.currentList.SelectedItem().(*tui.Example).HaveSecretFile() @@ -239,19 +296,36 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { file += fmt.Sprintf(",%s", d) } + cmdID := randSeq(5) + k8sCmdList[cmdID] = &k8sCmd{ + done: false, + } + verb := "unknown" switch { case key.Matches(msg, m.keys.Get): m.viewName = pK8S - m.keys.OnlyBackQuit() - verb = "get" + m.k8sCurrentIDView = cmdID + verb = "managed" + m.keys.GetHelp() + m.keys.Get.SetEnabled(false) + k8sCmdList[cmdID].verb = "get" + m.k8sCurrentKind = m.currentList.SelectedItem().(*tui.Example).Description() case key.Matches(msg, m.keys.Apply): + m.k8sProgressMsg = "apply sent !" verb = "apply" + k8sCmdList[cmdID].verb = "apply" + case key.Matches(msg, m.keys.Delete): + m.k8sProgressMsg = "delete sent !" verb = "delete" + k8sCmdList[cmdID].verb = "delete" } - cmd = tools.Kubectl(verb, file) + + m.k8sCurrentFiles = file + m.header.Notification = fmt.Sprintf("k %s @ %s", verb, time.Now().Format("15:04:05")) + cmd = tools.Kubectl(verb, file, cmdID) return m, cmd } @@ -294,6 +368,9 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.errorPanel = m.errorPanel.RaiseError(msg.Reason, msg.Cause) m.errorRaised = true m.keys.ErrorHelp() + if msg.CmdID != "" { + m.currentList.SetItem(msg.Index, msg.Item) + } return m, cmd case tui.ListTestedDone: @@ -301,15 +378,61 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd case tools.KubectlResult: + k8sCmd := k8sCmdList[msg.CmdID] + + k8sCmd.cmdOutput = msg.Out + + if k8sCmd.canceled { + // m.currentList.SetItem(listIndex, listMsg) + delete(k8sCmdList, msg.CmdID) + return m, nil + } + k8sCmdList[msg.CmdID].done = true + switch msg.Verb { case "apply", "delete": - cmd := m.currentList.NewStatusMessage(fmt.Sprintf("kubectl %s ok", msg.Verb)) + m.k8sProgressMsg = "" + m.header.Notification = fmt.Sprintf("k %s @ %s ✓", msg.Verb, time.Now().Format("15:04:05")) + // cmd := m.currentList.NewStatusMessage(fmt.Sprintf("kubectl %s ok", msg.Verb)) return m, cmd - case "get": + case "managed": m.viewName = pK8S - m.k8sOutput = msg.Out + m.k8sCurrentIDView = msg.CmdID + m.k8sProgressMsg = "" + m.header.Notification = fmt.Sprintf("k %s @ %s ✓", msg.Verb, time.Now().Format("15:04:05")) + if !m.tickRunning { + m.tickRunning = true + cmd = m.tickCmd() + } + return m, cmd + } + + case tickK8SGet: + if m.tickRunning { + var kCmd tea.Cmd + if m.progressK8SGet.Percent() == 1.0 { + m.progressK8SGet.SetPercent(0) + + cmdID := randSeq(5) + kCmd = tools.Kubectl("managed", m.k8sCurrentFiles, cmdID) + k8sCmdList[cmdID] = &k8sCmd{ + verb: "managed", + } + // m.k8sOutput = "" + } + + // Note that you can also use progress.Model.SetPercent to set the + // percentage value explicitly, too. + cmd := m.progressK8SGet.IncrPercent(0.1) + return m, tea.Batch(m.tickCmd(), cmd, kCmd) } + + // FrameMsg is sent when the progress bar wants to animate itself + case progress.FrameMsg: + progressModel, cmd := m.progressK8SGet.Update(msg) + m.progressK8SGet = progressModel.(progress.Model) + return m, cmd } // Return the updated model to the Bubble Tea runtime for processing. @@ -363,11 +486,29 @@ func (m model) View() string { ) case pK8S: - get := lipgloss.NewStyle().Height(m.currentList.Height()).Render(m.k8sOutput) + cmd := k8sCmdList[m.k8sCurrentIDView] + getOutput := "loading..." + reloadOutput := "" + // w := lipgloss.Width + + h := "Using ressource : " + m.k8sCurrentKind + h = lipgloss.NewStyle().Background(tui.RedColour).Margin(0, 0, 1, 0).Render(h) + + if cmd.done { + reloadOutput = fmt.Sprintf("%s reloading... %s", m.progressK8SGet.View(), m.k8sProgressMsg) + reloadOutput = lipgloss.NewStyle().MaxWidth(m.width).Margin(1, 0, 1, 0).Render(reloadOutput) + getOutput = lipgloss.NewStyle().MaxWidth(m.width).Border(lipgloss.RoundedBorder()).Render(cmd.cmdOutput) + } + ui := lipgloss.JoinVertical(lipgloss.Center, h, getOutput, reloadOutput) + dialog := lipgloss.Place(m.width, m.currentList.Height(), + lipgloss.Center, lipgloss.Center, + lipgloss.NewStyle().Render(ui), + ) + view = lipgloss.JoinVertical( - lipgloss.Left, + lipgloss.Center, m.header.View(), - get, + dialog, m.footer.View(), ) @@ -436,6 +577,8 @@ func (m model) showExamples() (model, tea.Cmd) { m.currentList.Title = "Choose an example" m.listName = "-" + m.currentList.Select(m.previousItemPostion) + return m, cmd } @@ -450,3 +593,12 @@ func (m model) showYaml(title string) (model, tea.Cmd) { } return m, cmd } + +func (m model) tickCmd() tea.Cmd { + return tea.Tick(time.Second*1, func(t time.Time) tea.Msg { + if !m.tickRunning { + return nil + } + return tickK8SGet(t) + }) +} diff --git a/tui/pages/header/header.go b/tui/pages/header/header.go index 00885fe..bc5a1dd 100644 --- a/tui/pages/header/header.go +++ b/tui/pages/header/header.go @@ -3,6 +3,7 @@ package header import ( "fmt" + "strings" "time" "github.com/FrangipaneTeam/bean/config" @@ -26,6 +27,7 @@ type Model struct { notifyCrds chan pages.NotifyActivity notifyExamples chan pages.NotifyActivity Width, Height int + Notification string // errorPanel errorpanel.Model // errorRaised bool config config.Provider @@ -129,15 +131,10 @@ func (m Model) View() string { tui.FeintTextStyle.Padding(0, 0, 0, 2).Render(m.Description), ) - var err string - // if m.errorRaised { - // err = "\n\n" + m.errorPanel.View() - // } - header := "" // nolint: gocritic // TODO: try to change the logic to avoid this if !m.crdRecentActivity && !m.examplesRecentActivity { - header = fmt.Sprintf("%s Watching CRD files", m.spinner.View()) + header = fmt.Sprintf("%s Watch for new crd/examples files", m.spinner.View()) } else if m.crdRecentActivity { c := lipgloss.NewStyle().Foreground(tui.SpinnerColour).Render("→") header = fmt.Sprintf("%s New CRD files %s %s %ds ago", m.spinner.View(), c, "Updated", m.hideNotify) @@ -145,11 +142,31 @@ func (m Model) View() string { c := lipgloss.NewStyle().Foreground(tui.SpinnerColour).Render("→") header = fmt.Sprintf("%s New examples %s %s %ds ago", m.spinner.View(), c, "Updated", m.hideNotify) } + + notification := strings.Builder{} + + if m.Notification != "" { + t := strings.Trim(m.Notification, "\n") + fmt.Fprintf(¬ification, "• %s", t) + } + + header = lipgloss.JoinHorizontal( + lipgloss.Top, + lipgloss.NewStyle().Width(m.Width/2).Align(lipgloss.Left).Render(header), + tui.NotificationStyle. + Width(m.Width/2). + Align(lipgloss.Right). + Render(notification.String()), + ) + banner := lipgloss.JoinVertical( - lipgloss.Left, - lipgloss.NewStyle().MarginBottom(1).Render(nameVersion), - lipgloss.NewStyle().Render(header), - err, + lipgloss.Center, + lipgloss.NewStyle(). + MarginBottom(1). + Width(m.Width-4). + Align(lipgloss.Center). + Render(nameVersion), + header, ) border := tui.BorderBottom.Width(m.Width).Render(banner) diff --git a/tui/theme.go b/tui/theme.go index 777fe86..380e490 100644 --- a/tui/theme.go +++ b/tui/theme.go @@ -48,4 +48,14 @@ var ( BorderTop = lipgloss.NewStyle(). Border(lipgloss.NormalBorder(), true, false, false, false). BorderForeground(BorderColour) + + // StatusBarStyle = lipgloss.NewStyle(). + // Foreground(lipgloss.AdaptiveColor{Light: "#343433", Dark: "#C1C6B2"}). + // Background(lipgloss.AdaptiveColor{Light: "#D9DCCF", Dark: "#353533"}) + NotificationStyle = lipgloss.NewStyle(). + // Inherit(StatusBarStyle). + Foreground(lipgloss.Color(RedColour)) + // Background(lipgloss.Color("#FF5F87")) + // Padding(0, 1). + // MarginRight(1) )