├── LICENSE ├── README.md ├── _examples ├── kitt.go └── simple.go └── progress └── indicator.go /LICENSE: -------------------------------------------------------------------------------- 1 | Kitt 2 | The Masterminds 3 | Copyright (C) 2016, Matt Butcher and Matt Farina 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kitt: Make CLI Pretty 2 | 3 | Kitt is a small Go library for improving CLI UIs. 4 | 5 | It currently provides the following: 6 | 7 | - A configurable progress indicator. 8 | 9 | ## Usage 10 | 11 | Here's an example progress indicator. 12 | 13 | ```go 14 | package main 15 | 16 | import ( 17 | "time" 18 | 19 | "github.com/Masterminds/kitt/progress" 20 | ) 21 | 22 | func main() { 23 | p := progress.NewIndicator() 24 | p.Start("Starting") 25 | 26 | time.Sleep(2 * time.Second) 27 | p.Message("Still going") 28 | 29 | time.Sleep(2 * time.Second) 30 | p.Done("Done") 31 | 32 | time.Sleep(20 * time.Millisecond) 33 | println("The end") 34 | } 35 | ``` 36 | 37 | There are all kinds of things you can customize, including the graph 38 | used for the meter itself, as well as the timing. 39 | 40 | For more, see the `_examples` directory. 41 | -------------------------------------------------------------------------------- /_examples/kitt.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "github.com/Masterminds/kitt/progress" 8 | ) 9 | 10 | func main() { 11 | pi := &progress.Indicator{ 12 | Interval: 200 * time.Millisecond, 13 | Frames: []string{ 14 | "[* ]", 15 | "[.* ]", 16 | "[ .* ]", 17 | "[ .* ]", 18 | "[ .* ]", 19 | "[ .* ]", 20 | "[ *]", 21 | "[ *.]", 22 | "[ *. ]", 23 | "[ *. ]", 24 | "[ *. ]", 25 | "[ *. ]", 26 | }, 27 | Writer: os.Stdout, 28 | } 29 | 30 | pi.Start("Hello") 31 | time.Sleep(7 * time.Second) 32 | pi.Message("Bye") 33 | time.Sleep(4 * time.Second) 34 | pi.Done("Done") 35 | time.Sleep(40 * time.Millisecond) 36 | } 37 | -------------------------------------------------------------------------------- /_examples/simple.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/Masterminds/kitt/progress" 7 | ) 8 | 9 | func main() { 10 | p := progress.NewIndicator() 11 | p.Start("Starting") 12 | 13 | time.Sleep(2 * time.Second) 14 | p.Message("Still going") 15 | 16 | time.Sleep(2 * time.Second) 17 | p.Done("Done") 18 | 19 | time.Sleep(20 * time.Millisecond) 20 | println("The end") 21 | } 22 | -------------------------------------------------------------------------------- /progress/indicator.go: -------------------------------------------------------------------------------- 1 | package progress 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type Indicator struct { 12 | Interval time.Duration 13 | Frames []string 14 | Writer io.Writer 15 | 16 | initialMessage string 17 | done chan string 18 | msg chan string 19 | } 20 | 21 | func NewIndicator() *Indicator { 22 | return &Indicator{ 23 | Interval: time.Second, 24 | Writer: os.Stdout, 25 | Frames: []string{ 26 | ". ", 27 | ".. ", 28 | "...", 29 | }, 30 | } 31 | } 32 | 33 | func (i *Indicator) Start(message string) { 34 | i.done = make(chan string) 35 | i.msg = make(chan string, 100) 36 | go i.run() 37 | i.msg <- message 38 | } 39 | 40 | func (i *Indicator) Message(message string) { 41 | i.msg <- message 42 | } 43 | 44 | func (i *Indicator) Done(message string) { 45 | i.done <- message 46 | } 47 | 48 | func (i *Indicator) run() { 49 | tt := time.NewTicker(i.Interval) 50 | fr := 0 51 | mm := i.initialMessage 52 | maxlen := len(mm) 53 | 54 | for { 55 | select { 56 | case m := <-i.msg: 57 | mm = m 58 | case <-tt.C: 59 | pad := maxlen - len(mm) 60 | if pad < 0 { 61 | maxlen = len(mm) 62 | } else if pad > 0 { 63 | mm += strings.Repeat(" ", pad) 64 | } 65 | fmt.Fprintf(i.Writer, "%s %s\r", i.Frames[fr], mm) 66 | fr++ 67 | if fr >= len(i.Frames) { 68 | fr = 0 69 | } 70 | case mm := <-i.done: 71 | if pad := maxlen + len(i.Frames[fr]) + 2 - len(mm); pad > 0 { 72 | mm += strings.Repeat(" ", pad) 73 | } 74 | fmt.Println(mm) 75 | tt.Stop() 76 | close(i.msg) 77 | close(i.done) 78 | return 79 | } 80 | } 81 | } 82 | --------------------------------------------------------------------------------