From 5b5865d64dfb469de16b5d18a485656ca49d83da Mon Sep 17 00:00:00 2001 From: Ernest Date: Sat, 13 Apr 2024 22:31:32 +0300 Subject: [PATCH] UI (#18) * feat: add lipgloss and huh - use lipgloss to render the tables - huh to handle the inputs, interactivity basically * chore: update readme --- README.md | 9 +++- cmd/delete.go | 32 +++++++++--- cmd/list.go | 11 ++-- cmd/pwd.go | 19 +++---- cmd/root.go | 10 ++-- cmd/search.go | 91 ++++++++++++++++++++++++++------- go.mod | 23 +++++++-- go.sum | 52 ++++++++++++++++--- internal/model/model_handler.go | 12 ++--- 9 files changed, 198 insertions(+), 61 deletions(-) diff --git a/README.md b/README.md index abc07e8..4385a57 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,10 @@ [![Go Reference](https://pkg.go.dev/badge/github.com/musaubrian/tg.svg)](https://pkg.go.dev/github.com/musaubrian/tg) [![tests](https://github.com/musaubrian/tg/actions/workflows/test.yml/badge.svg)](https://github.com/musaubrian/tg/actions/workflows/test.yml) -> A cli tool to help manage your passwords + +A command line password manager. + + Thats it. ## Installation @@ -23,4 +26,8 @@ cd tg go build . ``` +or get one of the releases [archives](https://github.com/musaubrian/tg/releases) + Contributions are welcomed + +Powered by [huh](https://github.com/charmbracelet/huh) and [lipgloss](https://github.com/charmbracelet/lipgloss) diff --git a/cmd/delete.go b/cmd/delete.go index bd6c60b..0eff789 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -5,6 +5,7 @@ import ( "log" "os" + "github.com/charmbracelet/huh" "github.com/musaubrian/tg/internal/model" "github.com/spf13/cobra" ) @@ -25,7 +26,22 @@ var deleteSiteCmd = &cobra.Command{ Use: "site", Short: "Remove a record using its site name", Run: func(cmd *cobra.Command, args []string) { + var confirm bool + siteName := model.GetInput("SiteName") + + title := fmt.Sprintf("This will delete all records related to site [%s]\n\nDo you want to continue?", siteName) + + err := huh.NewConfirm().Title(title).Affirmative("Yes").Negative("No!!").Value(&confirm).Run() + if err != nil { + log.Fatal(err) + } + + if !confirm { + fmt.Println("Stopping process") + os.Exit(1) + } + model.DeleteRecord(siteName, model.SiteName) }, } @@ -39,19 +55,21 @@ CAUTION!! If multiple sites have the same username, they will all be deleted `, Run: func(cmd *cobra.Command, args []string) { + var confirm bool + userName := model.GetInput("UserName") - fmt.Printf("This will delete all records with the username [%s]\n", userName) - agree := model.GetInput("Continue y/N") + title := fmt.Sprintf("This will delete all records related to the user [%s]\n\nDo you want to continue?", userName) - if len(agree) < 1 || agree == "n" { + err := huh.NewConfirm().Title(title).Affirmative("Yes").Negative("No!!").Value(&confirm).Run() + if err != nil { + log.Fatal(err) + } + if !confirm { fmt.Println("Stopping process") os.Exit(1) - } else if agree == "y" { - model.DeleteRecord(userName, model.Username) - } else { - log.Fatal("Unknown option") } + model.DeleteRecord(userName, model.Username) }, } diff --git a/cmd/list.go b/cmd/list.go index 7eae023..2179c94 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -12,7 +12,7 @@ import ( var listCmd = &cobra.Command{ Use: "list", Short: "List all records in the database", - Long: `Lists all the records(sitename, username, password) available + Long: `Lists all the records(sitename, username, password) available in the database`, Aliases: []string{"l"}, Run: func(cmd *cobra.Command, args []string) { @@ -22,11 +22,12 @@ var listCmd = &cobra.Command{ } sites := model.ListAll() if pretty { - t.AddHeader("\n#", "USERNAME", "SITE_NAME", "PASSWORD") - for i, site := range sites { - t.AddLine(i+1, site.UserName, site.Name, site.Password) + t := t.Headers("USERNAME", "SITE_NAME", "PASSWORD") + + for _, site := range sites { + t.Row(site.UserName, site.Name, site.Password) } - t.Print() + fmt.Println(t) } else { for _, site := range sites { fmt.Println("\nSiteName:", site.Name) diff --git a/cmd/pwd.go b/cmd/pwd.go index 8dbb399..b8fbcf8 100644 --- a/cmd/pwd.go +++ b/cmd/pwd.go @@ -19,16 +19,17 @@ parse 'c' as argument to copy it without displaying it `, Run: func(cmd *cobra.Command, args []string) { pwd := utils.GeneratePassword() - if len(args) < 1 { - fmt.Println(pwd) - } else { - if args[0] == "c" { - utils.CopyToClipboard(pwd) - fmt.Println("Copied to clipboard") - } else { - log.Fatalf("Run `%s pwd -h` to see how to use this command", rootCmd.Use) - } + + copyPwd, err := rootCmd.Flags().GetBool("copy") + if err != nil { + log.Fatal(err) + } + if copyPwd { + utils.CopyToClipboard(pwd) + fmt.Println("Copied to clipboard") + return } + fmt.Println(pwd) }, } diff --git a/cmd/root.go b/cmd/root.go index fd63b79..d1faa23 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -5,13 +5,16 @@ import ( "os" "path" - "github.com/cheynewallace/tabby" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/lipgloss/table" "github.com/musaubrian/tg/internal/model" "github.com/musaubrian/tg/internal/utils" "github.com/spf13/cobra" ) -var t = tabby.New() +var t = table.New().StyleFunc(func(row, col int) lipgloss.Style { + return lipgloss.NewStyle().Width(26) +}) // rootCmd represents the base command when called without any subcommands var rootCmd = &cobra.Command{ @@ -54,5 +57,6 @@ func init() { // Cobra also supports local flags, which will only run // when this action is called directly. // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") - rootCmd.PersistentFlags().BoolP("pretty", "p", false, "List results in a nice table format") + rootCmd.PersistentFlags().BoolP("pretty", "p", true, "List results in a nice table format") + rootCmd.PersistentFlags().BoolP("copy", "c", false, "Copy password to clipboard") } diff --git a/cmd/search.go b/cmd/search.go index ae46d9d..992de21 100644 --- a/cmd/search.go +++ b/cmd/search.go @@ -4,6 +4,7 @@ import ( "fmt" "log" + "github.com/charmbracelet/huh" "github.com/musaubrian/tg/internal/model" "github.com/musaubrian/tg/internal/utils" "github.com/spf13/cobra" @@ -24,9 +25,11 @@ var searchByUserName = &cobra.Command{ Example: ` tg search user some_username -tinygo search user some_username -p +tg search user some_username -c `, Run: func(cmd *cobra.Command, args []string) { + var selectedFromMultiple string + if len(args) < 1 { log.Fatal("You did not parse value to search for") } @@ -34,22 +37,37 @@ tinygo search user some_username -p if err != nil { log.Fatal(err) } + copyPwd, err := rootCmd.Flags().GetBool("copy") + if err != nil { + log.Fatal(err) + } results, err := model.SearchRecords(args[0], model.Username) if err != nil { log.Fatal(err) } - if len(results) == 1 { - utils.CopyToClipboard(results[0].Password) - fmt.Printf("Copied [%s's] password for [%s] to clipboard\n", results[0].UserName, results[0].Name) - return + if copyPwd { + if len(results) == 1 { + utils.CopyToClipboard(results[0].Password) + fmt.Printf("Copied [%s's] password for [%s] to clipboard\n", results[0].UserName, results[0].Name) + return + } else { + huh.NewSelect[string]().Title("Multiple values returned, pick from the site you want").Options( + generateHuhOpts(results, "user")..., + ).Value(&selectedFromMultiple).Run() + + utils.CopyToClipboard(selectedFromMultiple) + fmt.Println("Copied password to clipboard") + return + } } + if pretty { - t.AddHeader("\n#", "USER_NAME", "SITE_NAME", "PASSWORD") - for i, v := range results { - t.AddLine(i+1, v.UserName, v.Name, v.Password) + t := t.Headers("USERNAME", "SITE_NAME", "PASSWORD") + for _, site := range results { + t.Row(site.UserName, site.Name, site.Password) } - t.Print() + fmt.Println(t) } else { for _, site := range results { fmt.Println("\nSiteName:", site.Name) @@ -66,9 +84,11 @@ var searchbySiteName = &cobra.Command{ Short: "Search for site by sitename", Example: ` tinygo search site some_sitename -tinygo search site some_sitename -p +tinygo search site some_sitename -c `, Run: func(cmd *cobra.Command, args []string) { + var selectedFromMultiple string + if len(args) < 1 { log.Fatal("You did not parse value to search for") } @@ -80,17 +100,33 @@ tinygo search site some_sitename -p if err != nil { log.Fatal(err) } - if len(results) == 1 { - utils.CopyToClipboard(results[0].Password) - fmt.Printf("Copied [%s's] password for [%s] to clipboard\n", results[0].UserName, results[0].Name) - return + copyPwd, err := rootCmd.Flags().GetBool("copy") + if err != nil { + log.Fatal(err) + } + + if copyPwd { + if len(results) == 1 { + utils.CopyToClipboard(results[0].Password) + fmt.Printf("Copied [%s's] password for [%s] to clipboard\n", results[0].UserName, results[0].Name) + return + } else { + huh.NewSelect[string]().Title("Multiple values returned, pick the user you want").Options( + generateHuhOpts(results, "site")..., + ).Value(&selectedFromMultiple).Run() + + utils.CopyToClipboard(selectedFromMultiple) + fmt.Println("Copied password to clipboard") + return + } } + if pretty { - t.AddHeader("\n#", "USER_NAME", "SITE_NAME", "PASSWORD") - for i, v := range results { - t.AddLine(i+1, v.UserName, v.Name, v.Password) + t := t.Headers("USERNAME", "SITE_NAME", "PASSWORD") + for _, site := range results { + t.Row(site.UserName, site.Name, site.Password) } - t.Print() + fmt.Println(t) } else { for _, site := range results { fmt.Println("\nSiteName:", site.Name) @@ -117,3 +153,22 @@ func init() { // is called directly, e.g.: // searchCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } + +func generateHuhOpts(results []model.Site, searchParam string) []huh.Option[string] { + var opts []huh.Option[string] + for _, v := range results { + if searchParam == "site" { + opts = append(opts, huh.Option[string]{ + Key: v.UserName, + Value: v.Password, + }) + } else { + opts = append(opts, huh.Option[string]{ + Key: v.Name, + Value: v.Password, + }) + } + } + + return opts +} diff --git a/go.mod b/go.mod index eb08390..4ed4fe7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,8 @@ go 1.19 require ( github.com/atotto/clipboard v0.1.4 - github.com/cheynewallace/tabby v1.1.1 + github.com/charmbracelet/huh v0.3.0 + github.com/charmbracelet/lipgloss v0.10.0 github.com/fatih/color v1.14.1 github.com/spf13/cobra v1.6.1 gorm.io/driver/sqlite v1.4.4 @@ -12,12 +13,28 @@ require ( ) require ( + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/catppuccin/go v0.2.0 // indirect + github.com/charmbracelet/bubbles v0.17.2-0.20240108170749-ec883029c8e6 // indirect + github.com/charmbracelet/bubbletea v0.25.0 // indirect + github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.17 // 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.15 // indirect github.com/mattn/go-sqlite3 v1.14.15 // 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.2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/spf13/pflag v1.0.5 // indirect - golang.org/x/sys v0.3.0 // indirect + golang.org/x/sync v0.4.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect ) diff --git a/go.sum b/go.sum index ce7ac6a..2e2e499 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,20 @@ +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/cheynewallace/tabby v1.1.1 h1:JvUR8waht4Y0S3JF17G6Vhyt+FRhnqVCkk8l4YrOU54= -github.com/cheynewallace/tabby v1.1.1/go.mod h1:Pba/6cUL8uYqvOc9RkyvFbHGrQ9wShyrn6/S/1OYVys= +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/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= +github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= +github.com/charmbracelet/bubbles v0.17.2-0.20240108170749-ec883029c8e6 h1:6nVCV8pqGaeyxetur3gpX3AAaiyKgzjIoCPV3NXKZBE= +github.com/charmbracelet/bubbles v0.17.2-0.20240108170749-ec883029c8e6/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o= +github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= +github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= +github.com/charmbracelet/huh v0.3.0 h1:CxPplWkgW2yUTDDG0Z4S5HH8SJOosWHd4LxCvi0XsKE= +github.com/charmbracelet/huh v0.3.0/go.mod h1:fujUdKX8tC45CCSaRQdw789O6uaCRwx8l2NDyKfC4jA= +github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s= +github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE= +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/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w= github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg= @@ -12,21 +25,48 @@ github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkr github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +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-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 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= +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.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +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/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI= +github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= +github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= +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.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= +golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= -golang.org/x/sys v0.3.0/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.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/sqlite v1.4.4 h1:gIufGoR0dQzjkyqDyYSCvsYR6fba1Gw5YKDqKeChxFc= diff --git a/internal/model/model_handler.go b/internal/model/model_handler.go index 73fb249..a1537f4 100644 --- a/internal/model/model_handler.go +++ b/internal/model/model_handler.go @@ -1,13 +1,11 @@ package model import ( - "bufio" "errors" "fmt" - "log" - "os" "strings" + "github.com/charmbracelet/huh" "github.com/fatih/color" "gorm.io/gorm" ) @@ -28,13 +26,9 @@ var ( // Get input from the user func GetInput(prompt string) string { - reader := bufio.NewReader(os.Stdin) - fmt.Printf("$ %s: ", prompt) + var result string - result, err := reader.ReadString('\n') - if err != nil { - log.Fatal("Could not read input: ", err) - } + huh.NewInput().Title(prompt).Value(&result).Run() result = strings.TrimSuffix(result, "\n") return result