commit 623bc860d4893bb43c41135fb64c76e7aa6b662f Author: Brian Buller Date: Wed Jan 21 21:32:44 2026 -0600 Initial Commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ae76122 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4cea6e1 --- /dev/null +++ b/Makefile @@ -0,0 +1,2 @@ +expds: + go build -o build/expds diff --git a/app/app.go b/app/app.go new file mode 100644 index 0000000..6cd6125 --- /dev/null +++ b/app/app.go @@ -0,0 +1,97 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package app + +import ( + "git.bullercodeworks.com/brian/expds/data" + "github.com/gdamore/tcell" + "github.com/spf13/cobra" +) + +type App struct { + repo *data.Repo + style tcell.Style +} + +func NewApp() *App { + a := &App{ + style: tcell.StyleDefault.Foreground(tcell.ColorYellow), + } + err := a.init() + cobra.CheckErr(err) + return a +} + +func (a *App) init() error { + var err error + a.repo, err = data.NewRepo() + return err +} + +func (a *App) Run(args []string) error { + return nil +} + +func (a *App) GetSize() (int, int) { return a.tScreen.Size() } +func (a *App) GetW() int { return a.w } +func (a *App) GetH() int { return a.h } + +func (a *App) SetScreen(scr AppScreen) { + a.screen = scr + a.screen.Init(a) + a.screen.HandleResize(tcell.NewEventResize(a.GetSize())) +} + +func (a *App) GetTScreen() tcell.Screen { return a.tScreen } + +func (a *App) Stop() { a.running = false } +func (a *App) PostNowEvent() error { + ev := new(tcell.EventTime) + ev.SetEventNow() + return a.PostEvent(ev) +} + +func (a *App) PostEvent(ev tcell.Event) error { + if a.tScreen != nil { + return a.tScreen.PostEvent(ev) + } + return nil +} +func (a *App) Sync() { a.tScreen.Sync() } +func (a *App) ClearScreen() { a.tScreen.Clear() } +func (a *App) Exit() { a.running = false } +func (a *App) Cleanup() { a.tScreen.Fini() } + +func (a *App) GetRepo() *data.Repo { return a.repo } + +// Draw a rune to the screen +func (a *DhApp) DrawRune(x, y int, style tcell.Style, r rune) { + a.tScreen.SetContent(x, y, r, nil, style) +} + +// Draw text to the screen +func (a *DhApp) DrawText(x, y int, text string, style tcell.Style) { + h.DrawText(x, y, text, style, a.tScreen) +} + +// Draw a widget to the screen +func (a *DhApp) DrawWidget(w w.Widget) { w.Draw(a.tScreen) } diff --git a/app/app_screen.go b/app/app_screen.go new file mode 100644 index 0000000..2c0ce7a --- /dev/null +++ b/app/app_screen.go @@ -0,0 +1,35 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package app + +import "github.com/gdamore/tcell" + +type AppScreen interface { + Init(*App) + GetName() string + HandleResize(*tcell.EventResize) + HandleKey(*tcell.EventKey) bool + HandleTime(*tcell.EventTime) + Draw() + Log(string, ...any) + Exit() error +} diff --git a/app/screen_home.go b/app/screen_home.go new file mode 100644 index 0000000..d79d52a --- /dev/null +++ b/app/screen_home.go @@ -0,0 +1,113 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package app + +import ( + "git.bullercodeworks.com/brian/expds/data" + wd "git.bullercodeworks.com/brian/expds/widgets" + w "git.bullercodeworks.com/brian/tcell-widgets" + "github.com/gdamore/tcell" +) + +type ScreenHome struct { + a *App + r *data.Repo + w, h int + style tcell.Style + + menuLayout *w.TopMenuLayout + menu *w.Menu + + layout *w.LinearLayout + + pdsListing *wd.SimpleListWithHelp + jsonContent *wd.JsonContent + + alert *w.Alert + alertLayout *w.LinearLayout + + cursor int +} + +func (s *ScreenHome) Init(a *App) { + s.a, s.r = a, a.repo + s.style = a.style + + s.menuLayout = w.NewTopMenuLayout("home.toplayout", s.style) + s.initMenu() + + s.alert = w.NewAlert("home.alert", s.style) + s.alert.SetVisible(false) + s.alertLayout = w.NewLinearLayout("home.alertlayout", s.style) + s.alertLayout.Add(s.alert) + + s.layout = w.NewLinearLayout("home.layout", s.style) + s.layout.SetOrientation(w.LinLayH) + + s.pdsListing = wd.NewSimpleListWithHelp("pdslisting", s.style) + s.pdsListing.SetTitle("No PDS Loaded") + s.pdsListing.SetOnSelect(s.loadSelectedPdsListing) + s.layout.Add(s.pdsListing) + + s.jsonContent = wd.NewJsonContent("jsoncontent", s.style) + s.layout.Add(s.jsonContent) + + s.menuLayout.SetWidget(s.layout) +} + +func (s *ScreenHome) initMenu() { + s.menuLayout.SetActive(true) + menu := s.menuLayout.Menu() + s.menuLayout.AddMenuItems( + menu.CreateMenuItem("File", nil, 'f', + menu.CreateMenuItem("Exit", func() bool { + s.a.Exit() + return true + }, 'x'), + ), + ) +} + +func (s *ScreenHome) GetName() string { return "home" } +func (s *ScreenHome) HandleResize(ev *tcell.EventResize) { + s.w, s.h = ev.Size() + s.menuLayout.HandleResize(ev) +} + +func (s *ScreenHome) HandleKey(ev *tcell.EventKey) bool { + if s.alert.Visible() { + return s.alert.HandleKey(ev) + } + return s.menuLayout.HandleKey(ev) +} + +func (s *ScreenHome) HandleTime(ev *tcell.EventTime) { s.menuLayout.HandleTime(ev) } + +func (s *ScreenHome) Draw() { s.a.DrawWidget(s.menuLayout) } + +func (s *ScreenHome) Exit() error { return nil } + +func (s *ScreenHome) Log(t string, a ...any) {} + +func (s *ScreenHome) loadSelectedPdsListing(idx int, nm string) bool { + return true +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..170447b --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,132 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package cmd + +import ( + "fmt" + "os" + "strings" + + "git.bullercodeworks.com/brian/expds/app" + gap "github.com/muesli/go-app-paths" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +// rootCmd represents the base command when called without any subcommands +var ( + Version = "1.0" + Build = 1 + Name = "expds" + cfgFile string + ConfigDir = "" + program cli.Program + rootCmd = &cobra.Command{ + Use: "expds", + Short: "Utility to edit and view PDS", + RunE: opRun, + } +) + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + err := rootCmd.Execute() + if err != nil { + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initConfig) + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file") +} + +func initConfig() { + viper.SetEnvPrefix(data.EnvPrefix) + viper.AutomaticEnv() + var firstDir string + if cfgFile == "" { + scope := gap.NewScope(gap.User, Name) + dirs, err := scope.ConfigDirs() + if err != nil { + fmt.Println("Can't retrieve default config") + os.Exit(1) + } + firstDir = dirs[0] + for _, v := range dirs { + viper.AddConfigPath(v) + } + viper.SetConfigName(Name) + viper.SetConfigType("yaml") + } + + viper.SetDefault(data.KeyConfigDir, firstDir) + viper.SetDefault(data.KeyDebug, false) + viper.SetConfigFile(cfgFile) + viper.BindEnv(data.KeyDebug) + + configName := fmt.Sprintf("%s.yaml", Name) + var createConfig bool + sep := string(os.PathSeparator) + ConfigDir = fmt.Sprintf("%s%s", firstDir, sep) + configPath := fmt.Sprintf("%s%s", ConfigDir, configName) + if err := viper.ReadInConfig(); err != nil { + createConfig = true + if _, ok := err.(viper.ConfigFileNotFoundError); ok { + fmt.Println("Config file not found.") + } else { + fmt.Println("Config file found, but some other error occurred.") + fmt.Println(err) + } + } + if createConfig { + _, err := os.Stat(firstDir) + if os.IsNotExist(err) { + fmt.Println("Creating Directory:", firstDir) + err := os.Mkdir(firstDir, 0o755) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + } + fmt.Println("Writing Config as:", configPath) + if err = viper.WriteConfigAs(configPath); err != nil { + fmt.Println(err) + os.Exit(1) + } + } + + if viper.GetString(data.KeyUrl) == "" { + url := helpers.CliPromptUser("Server URL", true) + if !strings.HasSuffix(url, "/") { + url = fmt.Sprintf("%s/", url) + } + viper.Set(data.KeyUrl, url) + viper.WriteConfig() + } +} + +func opRun(cmd *cobra.Command, args []string) error { + p := app.NewProgram() + return p.Run(args) +} diff --git a/data/config.go b/data/config.go new file mode 100644 index 0000000..55e6757 --- /dev/null +++ b/data/config.go @@ -0,0 +1,28 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package data + +const ( + EnvPrefix = "EXPDS" + KeyConfigDir = "config" + KeyDebug = "debug" +) diff --git a/data/repo.go b/data/repo.go new file mode 100644 index 0000000..f645317 --- /dev/null +++ b/data/repo.go @@ -0,0 +1,29 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package data + +type Repo struct { +} + +func NewRepo() (*Repo, error) { + return &Repo{}, nil +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..76c78c0 --- /dev/null +++ b/go.mod @@ -0,0 +1,33 @@ +module git.bullercodeworks.com/brian/expds + +go 1.25.1 + +require ( + github.com/gdamore/tcell v1.4.1 + github.com/muesli/go-app-paths v0.2.2 + github.com/spf13/cobra v1.10.2 + github.com/spf13/viper v1.21.0 +) + +require ( + git.bullercodeworks.com/brian/tcell-widgets v0.0.0-20251115204352-197df3e02988 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.1 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/gdamore/encoding v1.0.1 // indirect + github.com/go-viper/mapstructure/v2 v2.5.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/lucasb-eyer/go-colorful v1.3.0 // indirect + github.com/mattn/go-runewidth v0.0.19 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/sagikazarmark/locafero v0.12.0 // indirect + github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect + github.com/spf13/afero v1.15.0 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/sys v0.40.0 // indirect + golang.org/x/text v0.33.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..b535989 --- /dev/null +++ b/go.sum @@ -0,0 +1,88 @@ +git.bullercodeworks.com/brian/tcell-widgets v0.0.0-20251115204352-197df3e02988 h1:O4cDOi2FcMIEKWFQ8KhlIpRfyxAZ2m1o8P6Y5B6uhNg= +git.bullercodeworks.com/brian/tcell-widgets v0.0.0-20251115204352-197df3e02988/go.mod h1:3TlKbuGjY8nrKL5Qcp28h+KnEsXBl3iCwACTy79bdPg= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.1 h1:RjM8gnVbFbgI67SBekIC7ihFpyXwRPYWXn9BZActHbw= +github.com/clipperhouse/uax29/v2 v2.3.1/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/encoding v1.0.1 h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw= +github.com/gdamore/encoding v1.0.1/go.mod h1:0Z0cMFinngz9kS1QfMjCP8TY7em3bZYeeklsSDPivEo= +github.com/gdamore/tcell v1.4.1 h1:6T2+7Zl5U44SU3ensYi/w4SX5hpzbK6NDUDYmgCP3eQ= +github.com/gdamore/tcell v1.4.1/go.mod h1:vxEiSDZdW3L+Uhjii9c3375IlDmR05bzxY404ZVSMo0= +github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= +github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag= +github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +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/muesli/go-app-paths v0.2.2 h1:NqG4EEZwNIhBq/pREgfBmgDmt3h1Smr1MjZiXbpZUnI= +github.com/muesli/go-app-paths v0.2.2/go.mod h1:SxS3Umca63pcFcLtbjVb+J0oD7cl4ixQWoBKhGEtEho= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4= +github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw= +github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cwaq1E1/1lhQhtRK2ts/ZwZEhjcQeJQ1RuC6Q/8U= +github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= +github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9 h1:9exaQaMOCwffKiiiYk6/BndUBv+iRViNW+4lEMi0PvY= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU= +github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190626150813-e07cf5db2756/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE= +golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/main.go b/main.go new file mode 100644 index 0000000..5abc4bd --- /dev/null +++ b/main.go @@ -0,0 +1,28 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package main + +import "git.bullercodeworks.com/brian/expds/cmd" + +func main() { + cmd.Execute() +} diff --git a/widgets/json_content.go b/widgets/json_content.go new file mode 100644 index 0000000..4cbc5da --- /dev/null +++ b/widgets/json_content.go @@ -0,0 +1,118 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package widgets + +import ( + "strings" + + wd "git.bullercodeworks.com/brian/tcell-widgets" + wh "git.bullercodeworks.com/brian/tcell-widgets/helpers" + "github.com/gdamore/tcell" +) + +// TODO: Format contents as json. For now this is a duplice of the Text widget +type JsonContent struct { + id string + text string + contents []string + style tcell.Style + x, y int + w, h int + visible bool + active bool + focusable bool + keyMap *wd.KeyMap + + flags wd.LayoutFlag +} + +var _ wd.Widget = (*JsonContent)(nil) + +func NewJsonContent(id string, style tcell.Style) *JsonContent { + ret := &JsonContent{} + ret.Init(id, style) + return ret +} + +func (w *JsonContent) Init(id string, style tcell.Style) { + w.id = id + w.style = style + w.visible = true + w.focusable = false + w.keyMap = wd.BlankKeyMap() +} + +func (w *JsonContent) Id() string { return w.id } +func (w *JsonContent) HandleResize(ev *tcell.EventResize) { w.w, w.h = ev.Size() } + +func (w *JsonContent) GetKeyMap() *wd.KeyMap { return w.keyMap } +func (w *JsonContent) SetKeyMap(km *wd.KeyMap) { w.keyMap = km } + +func (w *JsonContent) HandleKey(ev *tcell.EventKey) bool { return w.keyMap.Handle(ev) } +func (w *JsonContent) HandleTime(ev *tcell.EventTime) {} +func (w *JsonContent) Draw(screen tcell.Screen) { + if !w.visible { + return + } + y := w.y + for i := range w.contents { + wh.DrawText(w.x, y, w.contents[i], w.style, screen) + y++ + } +} + +func (w *JsonContent) Active() bool { return w.active } +func (w *JsonContent) SetActive(a bool) { w.active = a } +func (w *JsonContent) Visible() bool { return w.visible } +func (w *JsonContent) SetVisible(a bool) { w.visible = a } +func (w *JsonContent) SetX(x int) { w.x = x } +func (w *JsonContent) SetY(y int) { w.y = y } +func (w *JsonContent) GetX() int { return w.x } +func (w *JsonContent) GetY() int { return w.y } +func (w *JsonContent) GetPos() wd.Coord { return wd.Coord{X: w.x, Y: w.y} } +func (w *JsonContent) SetPos(c wd.Coord) { w.x, w.y = c.X, c.Y } +func (w *JsonContent) SetW(x int) { w.w = x } +func (w *JsonContent) SetH(y int) { w.h = y } +func (w *JsonContent) GetW() int { return w.w } +func (w *JsonContent) GetH() int { return w.h } +func (w *JsonContent) WantW() int { return wh.Longest(w.contents) } +func (w *JsonContent) WantH() int { return len(w.contents) } +func (w *JsonContent) SetSize(c wd.Coord) { w.w, w.h = c.X, c.Y } +func (w *JsonContent) Focusable() bool { return w.focusable } +func (w *JsonContent) SetFocusable(b bool) { w.focusable = b } +func (w *JsonContent) MinW() int { return wh.Longest(w.contents) } +func (w *JsonContent) MinH() int { return len(w.contents) } + +func (w *JsonContent) SetJsonContent(txt string) { + w.text = txt + if strings.Contains(w.text, "\n") { + w.contents = strings.Split(w.text, "\n") + } else { + w.contents = []string{w.text} + } +} + +func (w *JsonContent) GetJsonContent() string { return w.text } +func (w *JsonContent) GetContents() []string { return w.contents } + +func (w *JsonContent) AddFlag(f wd.LayoutFlag) { w.flags.Add(f) } +func (w *JsonContent) RemoveFlag(f wd.LayoutFlag) { w.flags.Remove(f) } diff --git a/widgets/list_with_help.go b/widgets/list_with_help.go new file mode 100644 index 0000000..e0e3eed --- /dev/null +++ b/widgets/list_with_help.go @@ -0,0 +1,139 @@ +/* +Copyright © Brian Buller + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ +package widgets + +import ( + "fmt" + + t "git.bullercodeworks.com/brian/tcell-widgets" + h "git.bullercodeworks.com/brian/tcell-widgets/helpers" + "github.com/gdamore/tcell" +) + +type SimpleListWithHelp struct { + id string + style tcell.Style + x, y int + w, h int + visible bool + + list *t.SimpleList + help *t.Text +} + +func NewSimpleListWithHelp(id string, s tcell.Style) *SimpleListWithHelp { + ret := &SimpleListWithHelp{} + ret.Init(id, s) + return ret +} + +func (w *SimpleListWithHelp) Init(id string, s tcell.Style) { + w.id = id + w.style = s + w.visible = true + w.list = t.NewSimpleList(fmt.Sprintf("%s.list", id), s) + w.help = t.NewText(fmt.Sprintf("%s.help", id), s) +} + +func (w *SimpleListWithHelp) Id() string { return w.id } +func (w *SimpleListWithHelp) HandleResize(ev *tcell.EventResize) { + w.w, w.h = ev.Size() + hh := 0 + if w.list.Active() { + hh = w.help.WantH() + } + w.help.HandleResize((t.Coord{X: w.w, Y: hh}).ResizeEvent()) + w.help.SetPos(t.Coord{X: 1, Y: w.h - hh}) + w.list.HandleResize((t.Coord{X: w.w, Y: w.h - hh}).ResizeEvent()) + w.list.SetPos(t.Coord{X: 0, Y: 0}) +} + +func (w *SimpleListWithHelp) GetKeyMap() *t.KeyMap { return w.list.GetKeyMap() } +func (w *SimpleListWithHelp) SetKeyMap(km *t.KeyMap) { w.list.SetKeyMap(km) } + +func (w *SimpleListWithHelp) HandleKey(ev *tcell.EventKey) bool { return w.list.HandleKey(ev) } + +func (w *SimpleListWithHelp) HandleTime(ev *tcell.EventTime) { + w.list.HandleTime(ev) + w.help.HandleTime(ev) +} + +func (w *SimpleListWithHelp) Draw(screen tcell.Screen) { + if !w.visible { + return + } + w.GetPos().DrawOffset(w.list, screen) + w.GetPos().DrawOffset(w.help, screen) +} + +func (w *SimpleListWithHelp) Active() bool { return w.list.Active() } +func (w *SimpleListWithHelp) SetActive(a bool) { + w.list.SetActive(a) + w.help.SetVisible(a) +} +func (w *SimpleListWithHelp) Visible() bool { return w.visible } +func (w *SimpleListWithHelp) SetVisible(a bool) { w.visible = a } +func (w *SimpleListWithHelp) Focusable() bool { return w.list.Focusable() } +func (w *SimpleListWithHelp) SetFocusable(b bool) { w.list.SetFocusable(b) } +func (w *SimpleListWithHelp) SetX(x int) { w.x = x } +func (w *SimpleListWithHelp) SetY(y int) { w.y = y } +func (w *SimpleListWithHelp) GetX() int { return w.x } +func (w *SimpleListWithHelp) GetY() int { return w.y } +func (w *SimpleListWithHelp) GetPos() t.Coord { return t.Coord{X: w.x, Y: w.y} } +func (w *SimpleListWithHelp) SetPos(c t.Coord) { w.x, w.y = c.X, c.Y } +func (w *SimpleListWithHelp) GetW() int { return w.w } +func (w *SimpleListWithHelp) GetH() int { return w.h } +func (w *SimpleListWithHelp) SetW(wd int) { w.w = wd } +func (w *SimpleListWithHelp) SetH(h int) { w.h = h } +func (w *SimpleListWithHelp) SetSize(c t.Coord) { w.w, w.h = c.X, c.Y } +func (w *SimpleListWithHelp) WantW() int { return w.list.WantW() + w.help.WantW() } +func (w *SimpleListWithHelp) WantH() int { return h.Max(w.list.WantH(), w.help.WantH()) } +func (w *SimpleListWithHelp) MinW() int { return h.Max(w.list.MinW(), w.help.MinW()) } +func (w *SimpleListWithHelp) MinH() int { return w.list.MinH() + w.help.MinH() } + +// Help Text Widget +func (w *SimpleListWithHelp) SetHelp(txt string) { w.help.SetText(txt) } +func (w *SimpleListWithHelp) GetHelp() string { return w.help.GetText() } + +// SimpleList Widget +func (w *SimpleListWithHelp) SetCursorWrap(b bool) { w.list.SetCursorWrap(b) } +func (w *SimpleListWithHelp) MoveUp() bool { return w.list.MoveUp() } +func (w *SimpleListWithHelp) MoveDown() bool { return w.list.MoveDown() } +func (w *SimpleListWithHelp) SetTitle(ttl string) { w.list.SetTitle(ttl) } +func (w *SimpleListWithHelp) SetList(l []string) { w.list.SetList(l) } +func (w *SimpleListWithHelp) Clear() { w.list.Clear() } +func (w *SimpleListWithHelp) Add(l string) { w.list.Add(l) } +func (w *SimpleListWithHelp) Remove(l string) { w.list.Remove(l) } +func (w *SimpleListWithHelp) SetBorder(brd []rune) { w.list.SetBorder(brd) } +func (w *SimpleListWithHelp) SetItemStyle(idx int, s tcell.Style) { w.list.SetItemStyle(idx, s) } +func (w *SimpleListWithHelp) SetItem(idx int, txt string) { w.list.SetItem(idx, txt) } +func (w *SimpleListWithHelp) SetSelectedIndex(i int) { w.list.SetSelectedIndex(i) } +func (w *SimpleListWithHelp) SelectedIndex() int { return w.list.SelectedIndex() } +func (w *SimpleListWithHelp) ClearBorder() { w.list.ClearBorder() } +func (w *SimpleListWithHelp) SetOnSelect(s func(int, string) bool) { w.list.SetOnSelect(s) } +func (w *SimpleListWithHelp) SetVimMode(b bool) { w.list.SetVimMode(b) } +func (w *SimpleListWithHelp) SetLogger(l func(string, ...any)) { w.list.SetLogger(l) } +func (w *SimpleListWithHelp) Log(txt string, args ...any) { w.list.Log(txt, args...) } +func (w *SimpleListWithHelp) SetOnChange(c func(idx int, nm string) bool) { w.list.SetOnChange(c) } +func (w *SimpleListWithHelp) GetSelectedItem() string { return w.list.GetSelectedItem() } +func (w *SimpleListWithHelp) GetAllItems() []string { return w.list.GetAllItems() } +func (w *SimpleListWithHelp) GetAllItemStyles() map[int]tcell.Style { return w.list.GetAllItemStyles() }