├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── cmd └── root.go ├── go.mod ├── go.sum ├── main.go ├── pkg ├── flags │ └── flags.go ├── model │ └── model.go ├── theme │ ├── load.go │ └── theme.go ├── typer │ └── typer.go └── utility │ └── utility.go └── schema.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Version [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Dependency directories (remove the comment below to include it) 15 | # vendor/ 16 | 17 | # Other 18 | *.txt -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Maas Lalani 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 all 13 | 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 THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL := run 2 | 3 | # enables things like `make run -- -l 3 --min-word-length 5` 4 | ifeq (run, $(firstword $(MAKECMDGOALS))) 5 | runargs := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS)) 6 | $(eval $(runargs):;@true) 7 | endif 8 | 9 | .PHONY: run 10 | run: 11 | go run main.go $(runargs) 12 | 13 | build: 14 | go build main.go -o typer 15 | 16 | install: 17 | go install 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Typer 2 | 3 | Typing test in your terminal 4 | 5 | ![Typer Banner](../assets/banner.png) 6 | 7 | ### Installation 8 | 9 | To just install `typer` simply use this command: 10 | ``` 11 | go install github.com/maaslalani/typer@latest 12 | ``` 13 | 14 | ### Usage 15 | To begin a typing test simply type `typer`. This will generate random words for you to type and show you your WPM score. 16 | ``` 17 | typer 18 | ``` 19 | 20 | To change the length of the typing test, use the `--length` flag. 21 | ``` 22 | typer -l 20 23 | ``` 24 | 25 | To set min word length, you can use `--min-word-length` flag. 26 | ``` 27 | typer --min-word-length 5 28 | ``` 29 | There is no maximum value, but anything below 1 will count as no min length. 30 | 31 | You can use Monkeytype as a source of words, just pass `-m, --monkeytype` flag, 32 | by default it'll use `english` dictionary, you can change that by adding `--monkeytype-language string` flag. 33 | ``` 34 | typer -m --monkeytype-language code_go 35 | ``` 36 | see: [monkeytype/languages/_list.json](https://github.com/monkeytypegame/monkeytype/blob/master/frontend/static/languages/_list.json) 37 | 38 | If you want to provide your own text, you can pass in a file name with the `--file` flag. The typing test will use the contents of the specified file. 39 | ``` 40 | typer -f filename.txt 41 | ``` 42 | 43 | You can also pipe data by `stdin`. 44 | ``` 45 | echo 'Text from stdin!' | typer 46 | ``` 47 | 48 | ### Themes 49 | 50 | There is basic theme support, theme should be saved in config file (default `$HOME/.typer.yaml`) and should look similar to this default theme: 51 | 52 | ```yaml 53 | theme: 54 | #file: /an/absoulute/path/to/the/theme.yaml # if set, it will ignore everything below 55 | bar: 56 | color: '#4776E6' # basic color of the progressbar 57 | #gradient: '#ff0000' # if passed, will generate a gradient from previous color to this one 58 | graph: 59 | # see: https://pkg.go.dev/github.com/guptarohit/asciigraph#AnsiColor 60 | # see: https://github.com/guptarohit/asciigraph/blob/master/color.go#L152-L292 61 | color: blue # does not use rgb but rather ANSI codes 62 | height: 3 # height of the graph 63 | text: 64 | error: # color when misspelled 65 | background: '#f33' 66 | foreground: '#fff' 67 | typed: # color when character have been typed 68 | foreground: '#fff' 69 | #background: '#000' # optional, default theme does not add background 70 | untyped: # color when still haven't been typed 71 | foreground: '#555' 72 | #background: '#000' # optional, default theme does not add background 73 | 74 | ``` 75 | 76 | ### Demo 77 | ![typer](../assets/typer.png?raw=true) 78 | -------------------------------------------------------------------------------- /cmd/root.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/maaslalani/typer/pkg/flags" 8 | "github.com/maaslalani/typer/pkg/typer" 9 | homedir "github.com/mitchellh/go-homedir" 10 | "github.com/spf13/cobra" 11 | "github.com/spf13/viper" 12 | ) 13 | 14 | var ( 15 | cfgFile string 16 | length int 17 | minWordLength int 18 | filePath string 19 | monkeytypeLanguage string 20 | ) 21 | 22 | var rootCmd = &cobra.Command{ 23 | Use: "typer", 24 | Short: "Terminal typing test", 25 | Long: `Measure your typing speed without ever leaving your terminal.`, 26 | Run: func(cmd *cobra.Command, _ []string) { 27 | c, err := cmd.Flags().GetBool("capital") 28 | if err != nil { 29 | fmt.Println("Error: Something went wrong with the capital flag.", err) 30 | } 31 | 32 | p, err := cmd.Flags().GetBool("punctuation") 33 | if err != nil { 34 | fmt.Println("Error: Something went wrong with the punctuation flag.", err) 35 | } 36 | 37 | m, err := cmd.Flags().GetBool("monkeytype") 38 | if err != nil { 39 | fmt.Println("Error: Something went wrong with monkeytype flag", err) 40 | } 41 | 42 | flagStruct := flags.Flags{ 43 | Length: length, 44 | MinWordLength: minWordLength, 45 | Capital: c, 46 | Punctuation: p, 47 | } 48 | 49 | stat, err := os.Stdin.Stat() 50 | if err != nil { 51 | fmt.Print(err) 52 | os.Exit(1) 53 | } 54 | 55 | switch true { 56 | case (stat.Mode() & os.ModeCharDevice) == 0: 57 | err = typer.FromStdin(length, &flagStruct) 58 | break 59 | case m: 60 | err = typer.FromMonkeytype(monkeytypeLanguage, &flagStruct) 61 | break 62 | case filePath != "": 63 | err = typer.FromFile(filePath, &flagStruct) 64 | break 65 | default: 66 | err = typer.FromRandom(length, &flagStruct) 67 | } 68 | 69 | if err != nil { 70 | fmt.Println("Error:", err) 71 | os.Exit(1) 72 | } 73 | }, 74 | } 75 | 76 | func Execute() { 77 | cobra.CheckErr(rootCmd.Execute()) 78 | } 79 | 80 | func init() { 81 | cobra.OnInitialize(initConfig) 82 | 83 | rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.typer.yaml)") 84 | rootCmd.PersistentFlags().StringVar(&monkeytypeLanguage, "monkeytype-language", "english", "monkeytype language") 85 | rootCmd.PersistentFlags().StringVarP(&filePath, "file", "f", "", "path to input file") 86 | 87 | rootCmd.PersistentFlags().IntVarP(&length, "length", "l", flags.DefaultLength, "set max text length") 88 | rootCmd.PersistentFlags().IntVar(&minWordLength, "min-word-length", -1, "set min word length") 89 | 90 | rootCmd.PersistentFlags().BoolP("capital", "c", false, "true to include capital letters") 91 | rootCmd.PersistentFlags().BoolP("punctuation", "p", false, "true to include punctuation") 92 | rootCmd.PersistentFlags().BoolP("monkeytype", "m", false, "true to use monkeytype as a source") 93 | 94 | if length > flags.MaxLength { 95 | fmt.Println("Error: Max length value exceeded. Restoring to max length value.") 96 | length = flags.MaxLength 97 | } 98 | 99 | if length < 0 { 100 | fmt.Println("Error: Length cannot be negative. Using default length.") 101 | length = flags.MaxLength 102 | } 103 | } 104 | 105 | func initConfig() { 106 | if cfgFile != "" { 107 | viper.SetConfigFile(cfgFile) 108 | } else { 109 | home, err := homedir.Dir() 110 | cobra.CheckErr(err) 111 | viper.AddConfigPath(home) 112 | viper.SetConfigName(".typer") 113 | } 114 | 115 | viper.AutomaticEnv() 116 | 117 | err := viper.ReadInConfig() 118 | if err == nil { 119 | fmt.Fprintln(os.Stderr, "Using config file:", viper.ConfigFileUsed()) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/maaslalani/typer 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/charmbracelet/bubbles v0.7.7 7 | github.com/charmbracelet/bubbletea v0.13.2 8 | github.com/guptarohit/asciigraph v0.5.5 9 | github.com/mitchellh/go-homedir v1.1.0 10 | github.com/mitchellh/go-wordwrap v1.0.1 11 | github.com/muesli/termenv v0.8.1 12 | github.com/spf13/afero v1.2.2 // indirect 13 | github.com/spf13/cobra v1.1.3 14 | github.com/spf13/jwalterweatherman v1.1.0 // indirect 15 | github.com/spf13/viper v1.7.1 16 | github.com/stretchr/testify v1.4.0 // indirect 17 | github.com/tyler-smith/go-bip39 v1.1.0 18 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 7 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 8 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 9 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 10 | cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= 11 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 12 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 13 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 14 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= 15 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 16 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 17 | github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= 18 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 19 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 20 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 21 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 22 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 23 | github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= 24 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 25 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 26 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 27 | github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= 28 | github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= 29 | github.com/charmbracelet/bubbles v0.7.7 h1:eVsAYiAJskifXaqHpPzbOZS1srMaTnuiTazC6uukBbA= 30 | github.com/charmbracelet/bubbles v0.7.7/go.mod h1:5WX1sSSjNCgCrzvRMN/z23HxvWaa+AI16Ch0KPZPeDs= 31 | github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg= 32 | github.com/charmbracelet/bubbletea v0.13.2 h1:fSOx3q0/VbA3ChWeiNcUsNeNysD9FFWD1tZypShBuCQ= 33 | github.com/charmbracelet/bubbletea v0.13.2/go.mod h1:okqaA5VF0aSpEZ2HB+L/cxVw2HthIDZ1dmWoRZs8/4g= 34 | github.com/charmbracelet/lipgloss v0.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg= 35 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 36 | github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc= 37 | github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= 38 | github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= 39 | github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= 40 | github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= 41 | github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= 42 | github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= 43 | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 44 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 45 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 46 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 47 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 48 | github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= 49 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 50 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= 51 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 52 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 53 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 54 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 55 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 56 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 57 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 58 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 59 | github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= 60 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 61 | github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 62 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 63 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 64 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 65 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 66 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 67 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 68 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 69 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 70 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 71 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 72 | github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= 73 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 74 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 75 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 76 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 77 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 78 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 79 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 80 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 81 | github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 82 | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= 83 | github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= 84 | github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= 85 | github.com/guptarohit/asciigraph v0.5.5 h1:ccFnUF8xYIOUPPY3tmdvRyHqmn1MYI9iv1pLKX+/ZkQ= 86 | github.com/guptarohit/asciigraph v0.5.5/go.mod h1:dYl5wwK4gNsnFf9Zp+l06rFiDZ5YtXM6x7SRWZ3KGag= 87 | github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= 88 | github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= 89 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 90 | github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 91 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 92 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 93 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 94 | github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= 95 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 96 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 97 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 98 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 99 | github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= 100 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 101 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 102 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 103 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 104 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 105 | github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= 106 | github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= 107 | github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= 108 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= 109 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= 110 | github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= 111 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 112 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 113 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 114 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 115 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 116 | github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= 117 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 118 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 119 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 120 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 121 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 122 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 123 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 124 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 125 | github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 126 | github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= 127 | github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= 128 | github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= 129 | github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= 130 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 131 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 132 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 133 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 134 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 135 | github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 136 | github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= 137 | github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= 138 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 139 | github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= 140 | github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= 141 | github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 142 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 143 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 144 | github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= 145 | github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= 146 | github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= 147 | github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= 148 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 149 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 150 | github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= 151 | github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 152 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 153 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 154 | github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9FnmSjdbWUlLGvcxrY0Rw3ATltrxOhk= 155 | github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= 156 | github.com/muesli/termenv v0.7.2/go.mod h1:ct2L5N2lmix82RaY3bMWwVu/jUFc9Ule0KGDCiKYPh8= 157 | github.com/muesli/termenv v0.8.1 h1:9q230czSP3DHVpkaPDXGp0TOfAwyjyYwXlUCQxQSaBk= 158 | github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0= 159 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 160 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 161 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 162 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= 163 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= 164 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 165 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 166 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 167 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 168 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 169 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 170 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 171 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 172 | github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= 173 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 174 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 175 | github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 176 | github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 177 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 178 | github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 179 | github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 180 | github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 181 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 182 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 183 | github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= 184 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 185 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 186 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 187 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 188 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 189 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 190 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 191 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 192 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 193 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 194 | github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= 195 | github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= 196 | github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= 197 | github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= 198 | github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= 199 | github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= 200 | github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= 201 | github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= 202 | github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= 203 | github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= 204 | github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= 205 | github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= 206 | github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 207 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 208 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 209 | github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 210 | github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk= 211 | github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 212 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 213 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 214 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 215 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 216 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 217 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 218 | github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= 219 | github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 220 | github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= 221 | github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= 222 | github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= 223 | github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 224 | go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= 225 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 226 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 227 | go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= 228 | go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= 229 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= 230 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 231 | golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 232 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 233 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 234 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 235 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 236 | golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 237 | golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897 h1:pLI5jrR7OSLijeIDcmRxNmw2api+jEfxLoykJVice/E= 238 | golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 239 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 240 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 241 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 242 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 243 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 244 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 245 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 246 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 247 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 248 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 249 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 250 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 251 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 252 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 253 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 254 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 255 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 256 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 257 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 258 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 259 | golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 260 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 261 | golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 262 | golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 263 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 264 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 265 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 266 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 267 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 268 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 269 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 270 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 271 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 272 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 273 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 274 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 275 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 276 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 277 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 278 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 279 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 280 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 281 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 282 | golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 283 | golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 284 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 285 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 286 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 287 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 288 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 289 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 290 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 291 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 292 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 293 | golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 294 | golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 295 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k= 296 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 297 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 298 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 299 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 300 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 301 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 302 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 303 | golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 304 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 305 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 306 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 307 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 308 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 309 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 310 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 311 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 312 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 313 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 314 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 315 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 316 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 317 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 318 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 319 | golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 320 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 321 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 322 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 323 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 324 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 325 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 326 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 327 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 328 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 329 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 330 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 331 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 332 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 333 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 334 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 335 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 336 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 337 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 338 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 339 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 340 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 341 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 342 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 343 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 344 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 345 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= 346 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 347 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 348 | gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= 349 | gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= 350 | gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= 351 | gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= 352 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 353 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 354 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 355 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 356 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 357 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 358 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 359 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 360 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 361 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 362 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "github.com/maaslalani/typer/cmd" 4 | 5 | func main() { 6 | cmd.Execute() 7 | } 8 | -------------------------------------------------------------------------------- /pkg/flags/flags.go: -------------------------------------------------------------------------------- 1 | package flags 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | util "github.com/maaslalani/typer/pkg/utility" 8 | ) 9 | 10 | const ( 11 | DefaultLength = 20 12 | MaxLength = 500 13 | ) 14 | 15 | type Flags struct { 16 | Length int 17 | MinWordLength int 18 | Capital bool 19 | Punctuation bool 20 | } 21 | 22 | // formatText applies formatting based on flags 23 | func (f *Flags) FormatText(s string) (string, error) { 24 | var err error 25 | s, err = util.AdjustWhitespace(s) 26 | if err != nil { 27 | return "", err 28 | } 29 | 30 | if !f.Punctuation { 31 | s, err = util.RemoveNonAlpha(s) 32 | if err != nil { 33 | return "", err 34 | } 35 | } 36 | 37 | if f.MinWordLength > 1 { 38 | s, err = util.MinWordLength(s, f.MinWordLength) 39 | if err != nil { 40 | return "", err 41 | } 42 | } 43 | 44 | if f.Length <= 0 { 45 | f.Length = DefaultLength 46 | } else if f.Length > MaxLength { 47 | f.Length = MaxLength 48 | } 49 | s = util.AdjustLength(s, f.Length) 50 | 51 | if !f.Capital { 52 | s = strings.ToLower(s) 53 | } 54 | 55 | if strings.Trim(s, " \n") == "" { 56 | return s, fmt.Errorf("word list is empty") 57 | } 58 | 59 | return s, nil 60 | } 61 | -------------------------------------------------------------------------------- /pkg/model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | "github.com/charmbracelet/bubbles/progress" 8 | tea "github.com/charmbracelet/bubbletea" 9 | "github.com/guptarohit/asciigraph" 10 | "github.com/maaslalani/typer/pkg/theme" 11 | ) 12 | 13 | const ( 14 | width = 60. 15 | 16 | // charsPerWord is the average characters per word used by most typing tests 17 | // to calculate your WPM score. 18 | charsPerWord = 5. 19 | ) 20 | 21 | var ( 22 | wpms []float64 23 | ) 24 | 25 | type Model struct { 26 | // Percent is a value from 0 to 1 that represents the current completion of the typing test 27 | Percent float64 28 | Progress *progress.Model 29 | // Text is the randomly generated text for the user to type 30 | Text []rune 31 | // Typed is the text that the user has typed so far 32 | Typed []rune 33 | // Start and end are the start and end time of the typing test 34 | Start time.Time 35 | // Mistakes is the number of characters that were mistyped by the user 36 | Mistakes int 37 | // Score is the user's score calculated by correct characters typed 38 | Score float64 39 | // Theme is the current color theme 40 | Theme *theme.Theme 41 | } 42 | 43 | // Init inits the bubbletea model for use 44 | func (m Model) Init() tea.Cmd { 45 | return nil 46 | } 47 | 48 | func (m Model) updateProgress() (tea.Model, tea.Cmd) { 49 | m.Percent = float64(len(m.Typed)) / float64(len(m.Text)) 50 | if m.Percent >= 1.0 { 51 | return m, tea.Quit 52 | } 53 | return m, nil 54 | } 55 | 56 | // Update updates the bubbletea model by handling the progress bar update 57 | // and adding typed characters to the state if they are valid typing characters 58 | func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { 59 | switch msg := msg.(type) { 60 | case tea.KeyMsg: 61 | // Start counting time only after the first keystroke 62 | if m.Start.IsZero() { 63 | m.Start = time.Now() 64 | } 65 | 66 | // User wants to cancel the typing test 67 | if msg.Type == tea.KeyCtrlC { 68 | return m, tea.Quit 69 | } 70 | 71 | // Deleting characters 72 | if msg.Type == tea.KeyBackspace && len(m.Typed) > 0 { 73 | m.Typed = m.Typed[:len(m.Typed)-1] 74 | return m.updateProgress() 75 | } 76 | 77 | // Ensure we are adding characters only that we want the user to be able to type 78 | if msg.Type != tea.KeyRunes { 79 | return m, nil 80 | } 81 | 82 | char := msg.Runes[0] 83 | next := rune(m.Text[len(m.Typed)]) 84 | 85 | // To properly account for line wrapping we need to always insert a new line 86 | // Where the next line starts to not break the user interface, even if the user types a random character 87 | if next == '\n' { 88 | m.Typed = append(m.Typed, next) 89 | 90 | // Since we need to perform a line break 91 | // if the user types a space we should simply ignore it. 92 | if char == ' ' { 93 | return m, nil 94 | } 95 | } 96 | 97 | m.Typed = append(m.Typed, msg.Runes...) 98 | 99 | if char == next { 100 | m.Score += 1. 101 | } 102 | 103 | return m.updateProgress() 104 | case tea.WindowSizeMsg: 105 | m.Progress.Width = msg.Width - 4 106 | if m.Progress.Width > width { 107 | m.Progress.Width = width 108 | } 109 | return m, nil 110 | 111 | default: 112 | return m, nil 113 | } 114 | } 115 | 116 | // View shows the current state of the typing test. 117 | // It displays a progress bar for the progression of the typing test, 118 | // the typed characters (with errors displayed in red) and remaining 119 | // characters to be typed in a faint display 120 | func (m Model) View() string { 121 | remaining := m.Text[len(m.Typed):] 122 | 123 | var typed string 124 | for i, c := range m.Typed { 125 | if c == rune(m.Text[i]) { 126 | typed += m.Theme.StringColor(m.Theme.Text.Typed, string(c)).String() 127 | } else { 128 | typed += m.Theme.StringColor(m.Theme.Text.Error, string(m.Text[i])).String() 129 | } 130 | } 131 | 132 | s := fmt.Sprintf( 133 | "\n %s\n\n%s", 134 | m.Progress.View(m.Percent), 135 | typed, 136 | ) 137 | if len(remaining) > 0 { 138 | s += m.Theme.StringColor(m.Theme.Text.Untyped, string(remaining[:1])).Underline().Faint().String() 139 | s += m.Theme.StringColor(m.Theme.Text.Untyped, string(remaining[1:])).Faint().String() 140 | } 141 | 142 | var wpm float64 143 | // Start counting wpm after at least two characters are typed 144 | if len(m.Typed) > 1 { 145 | wpm = (m.Score / charsPerWord) / (time.Since(m.Start).Minutes()) 146 | } 147 | 148 | if len(m.Typed) > charsPerWord { 149 | wpms = append(wpms, wpm) 150 | } 151 | 152 | wpmsCount := wpms 153 | if len(wpmsCount) <= 0 { 154 | wpmsCount = []float64{0} 155 | } 156 | 157 | graph := asciigraph.Plot( 158 | wpmsCount, 159 | asciigraph.Height(m.Theme.Graph.Height), 160 | asciigraph.Width(m.Progress.Width-5), 161 | asciigraph.Precision(2), 162 | asciigraph.SeriesColors(m.Theme.GraphColor()), 163 | ) 164 | s += fmt.Sprintf("\n\n%s\n\nWPM: %.2f\n", graph, wpm) 165 | return s 166 | } 167 | -------------------------------------------------------------------------------- /pkg/theme/load.go: -------------------------------------------------------------------------------- 1 | package theme 2 | 3 | import ( 4 | "github.com/spf13/viper" 5 | ) 6 | 7 | func LoadViper(v *viper.Viper, first bool) (*Theme, error) { 8 | theme := DefaultTheme() 9 | v.UnmarshalKey("theme", theme) 10 | if !first || theme.File == "" { 11 | return theme, nil 12 | } 13 | 14 | v = viper.New() 15 | v.SetConfigFile(theme.File) 16 | err := v.ReadInConfig() 17 | if err != nil { 18 | return theme, err 19 | } 20 | return LoadViper(v, false) 21 | } 22 | -------------------------------------------------------------------------------- /pkg/theme/theme.go: -------------------------------------------------------------------------------- 1 | package theme 2 | 3 | import ( 4 | "github.com/charmbracelet/bubbles/progress" 5 | "github.com/guptarohit/asciigraph" 6 | "github.com/muesli/termenv" 7 | ) 8 | 9 | func DefaultTheme() *Theme { 10 | return &Theme{ 11 | Text: Text{ 12 | Untyped: RGBColor{ 13 | Foreground: "#555", 14 | Background: "", 15 | }, 16 | Typed: RGBColor{ 17 | Foreground: "#fff", 18 | Background: "", 19 | }, 20 | Error: RGBColor{ 21 | Foreground: "#fff", 22 | Background: "#f33", 23 | }, 24 | }, 25 | Bar: Bar{ 26 | Color: "#4776E6", 27 | Gradient: "", 28 | }, 29 | Graph: Graph{ 30 | Color: "blue", 31 | Height: 3, 32 | }, 33 | } 34 | } 35 | 36 | type Theme struct { 37 | File string `json:"file" toml:"file" yaml:"file"` 38 | Text Text `json:"text" toml:"text" yaml:"text"` 39 | Bar Bar `json:"bar" toml:"bar" yaml:"bar"` 40 | Graph Graph `json:"graph" toml:"graph" yaml:"graph"` 41 | } 42 | 43 | type Text struct { 44 | Untyped RGBColor `json:"untyped" toml:"untyped" yaml:"untyped"` 45 | Typed RGBColor `json:"typed" toml:"typed" yaml:"typed"` 46 | Error RGBColor `json:"error" toml:"error" yaml:"error"` 47 | ErrorForeground RGBColor `json:"error_foreground" toml:"error_foreground" yaml:"error_foreground"` 48 | } 49 | 50 | type RGBColor struct { 51 | Foreground string `json:"foreground" toml:"foreground" yaml:"foreground"` 52 | Background string `json:"background" toml:"background" yaml:"background"` 53 | } 54 | 55 | type Bar struct { 56 | Color string `json:"color" toml:"color" yaml:"color"` 57 | Gradient string `json:"gradient" toml:"gradient" yaml:"gradient"` 58 | } 59 | 60 | type Graph struct { 61 | Color string `json:"color" toml:"color" yaml:"color"` 62 | Height int `json:"height" toml:"height" yaml:"height"` 63 | } 64 | 65 | func (t Theme) StringColor(rgbColors RGBColor, input string) termenv.Style { 66 | profile := termenv.ColorProfile() 67 | output := termenv.String(input) 68 | 69 | if rgbColors.Background != "" { 70 | output = output.Background(profile.Color(rgbColors.Background)) 71 | } 72 | 73 | if rgbColors.Foreground != "" { 74 | output = output.Foreground(profile.Color(rgbColors.Foreground)) 75 | } 76 | 77 | return output 78 | } 79 | 80 | func (t Theme) BarColor() progress.Option { 81 | if t.Bar.Color == "" { 82 | return progress.WithDefaultGradient() 83 | } 84 | 85 | if t.Bar.Gradient == "" { 86 | return progress.WithSolidFill(t.Bar.Color) 87 | } 88 | 89 | return progress.WithGradient(t.Bar.Color, t.Bar.Gradient) 90 | } 91 | 92 | func (t Theme) GraphColor() asciigraph.AnsiColor { 93 | graphColor := asciigraph.Blue 94 | if color, ok := asciigraph.ColorNames[t.Graph.Color]; ok { 95 | graphColor = color 96 | } 97 | return graphColor 98 | } 99 | -------------------------------------------------------------------------------- /pkg/typer/typer.go: -------------------------------------------------------------------------------- 1 | package typer 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "math/rand" 9 | "net/http" 10 | "os" 11 | "strings" 12 | "time" 13 | 14 | "github.com/charmbracelet/bubbles/progress" 15 | tea "github.com/charmbracelet/bubbletea" 16 | "github.com/maaslalani/typer/pkg/flags" 17 | "github.com/maaslalani/typer/pkg/model" 18 | "github.com/maaslalani/typer/pkg/theme" 19 | util "github.com/maaslalani/typer/pkg/utility" 20 | wrap "github.com/mitchellh/go-wordwrap" 21 | "github.com/spf13/viper" 22 | ) 23 | 24 | const ( 25 | words = 15 26 | defaultWidth = 60 27 | ) 28 | 29 | // FromStdin takes input text from stdin 30 | func FromStdin(n int, flagStruct *flags.Flags) error { 31 | var stdin []byte 32 | scanner := bufio.NewScanner(os.Stdin) 33 | for scanner.Scan() { 34 | stdin = append(stdin, scanner.Bytes()...) 35 | } 36 | 37 | if err := scanner.Err(); err != nil { 38 | return err 39 | } 40 | 41 | text := string(stdin) 42 | text, err := flagStruct.FormatText(text) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | return run(text) 48 | } 49 | 50 | // FromRandom runs the app with text generated by RandomWords 51 | func FromRandom(n int, flagStruct *flags.Flags) error { 52 | text := util.RandomWords(n) 53 | 54 | text, err := flagStruct.FormatText(text) 55 | if err != nil { 56 | return err 57 | } 58 | 59 | return run(text) 60 | } 61 | 62 | // FromFile runs the app with text extracted from a file 63 | func FromFile(path string, flagStruct *flags.Flags) error { 64 | text, err := util.ReadFile(path) 65 | if err != nil { 66 | return err 67 | } 68 | 69 | text, err = flagStruct.FormatText(text) 70 | if err != nil { 71 | return err 72 | } 73 | 74 | return run(text) 75 | } 76 | 77 | func FromMonkeytype(language string, flagStruct *flags.Flags) error { 78 | if language == "" { 79 | language = "english" 80 | } 81 | 82 | resp, err := http.Get(fmt.Sprintf("https://raw.githubusercontent.com/monkeytypegame/monkeytype/master/frontend/static/languages/%s.json", language)) 83 | if err != nil { 84 | return err 85 | } 86 | defer resp.Body.Close() 87 | 88 | if resp.StatusCode != 200 { 89 | return fmt.Errorf("error while fetching language (code %d)", resp.StatusCode) 90 | } 91 | 92 | bodyBytes, err := io.ReadAll(resp.Body) 93 | 94 | if err != nil { 95 | return err 96 | } 97 | 98 | words := struct { 99 | Words []string `json:"words"` 100 | }{} 101 | if err := json.Unmarshal(bodyBytes, &words); err != nil { 102 | return err 103 | } 104 | 105 | seed := rand.NewSource(time.Now().Unix() + int64(len(words.Words))) 106 | r := rand.New(seed) 107 | r.Shuffle(len(words.Words), func(i, j int) { 108 | words.Words[i], words.Words[j] = words.Words[j], words.Words[i] 109 | }) 110 | 111 | formatted, err := flagStruct.FormatText(strings.Join(words.Words, "\n")) 112 | if err != nil { 113 | return err 114 | } 115 | 116 | return run(formatted) 117 | } 118 | 119 | // run is responsible for running the GUI 120 | func run(text string) error { 121 | currentTheme, err := theme.LoadViper(viper.GetViper(), true) 122 | if err != nil { 123 | return err 124 | } 125 | 126 | bar, err := progress.NewModel(currentTheme.BarColor()) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | program := tea.NewProgram(model.Model{ 132 | Progress: bar, 133 | Text: []rune(wrap.WrapString(text, defaultWidth)), 134 | Theme: currentTheme, 135 | }) 136 | 137 | return program.Start() 138 | } 139 | -------------------------------------------------------------------------------- /pkg/utility/utility.go: -------------------------------------------------------------------------------- 1 | package utility 2 | 3 | import ( 4 | "math/rand" 5 | "os" 6 | "regexp" 7 | "strings" 8 | "time" 9 | 10 | "github.com/tyler-smith/go-bip39/wordlists" 11 | ) 12 | 13 | // ReadFile returns the file contents as a string 14 | func ReadFile(path string) (string, error) { 15 | contents, err := os.ReadFile(path) 16 | return string(contents), err 17 | } 18 | 19 | // RandomWords generates a strings with the specified number of random words using wordlist 20 | func RandomWords(n int) string { 21 | seed := rand.NewSource(time.Now().UnixNano()) 22 | r := rand.New(seed) 23 | words := wordlists.English 24 | l := len(words) 25 | 26 | var s string 27 | for i := 0; i < n; i++ { 28 | s += words[r.Intn(l)] + " " 29 | } 30 | return s 31 | } 32 | 33 | // AdjustLength shortens a string if it's word count is greater than n 34 | func AdjustLength(s string, n int) string { 35 | words := strings.Fields(s) 36 | if len(words) > n { 37 | words = words[:n] 38 | } 39 | s = strings.Join(words, " ") 40 | 41 | return s 42 | } 43 | 44 | // AdjustWhitespace replaces every group of whitespace characters with a single space charracter 45 | func AdjustWhitespace(s string) (string, error) { 46 | reg, err := regexp.Compile(`\s+`) 47 | if err != nil { 48 | return "", err 49 | } 50 | 51 | s = reg.ReplaceAllString(s, " ") 52 | 53 | if s[len(s)-1] == ' ' { 54 | s = s[:len(s)-1] 55 | } 56 | return s, nil 57 | } 58 | 59 | // RemoveNonAlpha removes all non-alphanumeric characters exept whitespace 60 | func RemoveNonAlpha(s string) (string, error) { 61 | reg, err := regexp.Compile(`[^\p{L}\p{N} ]+`) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | s = reg.ReplaceAllString(s, "") 67 | return s, nil 68 | } 69 | 70 | // Remove words of minimum length 71 | func MinWordLength(s string, l int) (string, error) { 72 | words := strings.Fields(s) 73 | for i, word := range words { 74 | if len([]rune(word)) < l { 75 | words[i] = "" 76 | } 77 | } 78 | return strings.Join(words, " "), nil 79 | } 80 | -------------------------------------------------------------------------------- /schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "definitions": { 4 | "color": { 5 | "type": "string", 6 | "minLength": 1, 7 | "pattern": "^#([0-9A-F]{3}|[0-9A-F]{6})$", 8 | "examples": [ 9 | "#000000", 10 | "#FF0000", 11 | "#00FF00", 12 | "#0000FF", 13 | "#FFFF00", 14 | "#FF00FF", 15 | "#00FFFF", 16 | "#FFFFFF", 17 | "#000", 18 | "#F00", 19 | "#0F0", 20 | "#00F", 21 | "#FF0", 22 | "#F0F", 23 | "#0FF", 24 | "#FFF" 25 | ] 26 | }, 27 | "foreground-property": { 28 | "title": "foreground", 29 | "description": "A foreground color\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 30 | "$ref": "#/definitions/color" 31 | }, 32 | "background-property": { 33 | "title": "background", 34 | "description": "A background color\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 35 | "$ref": "#/definitions/color" 36 | } 37 | }, 38 | "title": "typer settings", 39 | "description": "typer settings\nhttps://github.com/maaslalani/typer", 40 | "type": "object", 41 | "properties": { 42 | "theme": { 43 | "title": "theme", 44 | "description": "Theme settings\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 45 | "type": "object", 46 | "properties": { 47 | "file": { 48 | "title": "file", 49 | "description": "An absolute path to theme to override settings below\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 50 | "type": "string", 51 | "minLength": 1, 52 | "pattern": "[^ ]", 53 | "examples": [ 54 | "/an/absoulute/path/to/the/theme.yaml" 55 | ] 56 | }, 57 | "bar": { 58 | "title": "bar", 59 | "description": "Bar settings\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 60 | "type": "object", 61 | "properties": { 62 | "color": { 63 | "title": "color", 64 | "description": "A color\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 65 | "$ref": "#/definitions/color" 66 | }, 67 | "gradient": { 68 | "title": "gradient", 69 | "description": "A second gradient color\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 70 | "$ref": "#/definitions/color" 71 | } 72 | }, 73 | "dependencies": { 74 | "gradient": [ 75 | "color" 76 | ] 77 | }, 78 | "minProperties": 1, 79 | "additionalProperties": false 80 | }, 81 | "graph": { 82 | "title": "graph", 83 | "description": "Graphics settings\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 84 | "type": "object", 85 | "properties": { 86 | "color": { 87 | "title": "color", 88 | "description": "A color\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 89 | "type": "string", 90 | "enum": [ 91 | "default", 92 | "aliceblue", 93 | "antiquewhite", 94 | "aqua", 95 | "aquamarine", 96 | "azure", 97 | "beige", 98 | "bisque", 99 | "black", 100 | "blanchedalmond", 101 | "blue", 102 | "blueviolet", 103 | "brown", 104 | "burlywood", 105 | "cadetblue", 106 | "chartreuse", 107 | "chocolate", 108 | "coral", 109 | "cornflowerblue", 110 | "cornsilk", 111 | "crimson", 112 | "cyan", 113 | "darkblue", 114 | "darkcyan", 115 | "darkgoldenrod", 116 | "darkgray", 117 | "darkgreen", 118 | "darkkhaki", 119 | "darkmagenta", 120 | "darkolivegreen", 121 | "darkorange", 122 | "darkorchid", 123 | "darkred", 124 | "darksalmon", 125 | "darkseagreen", 126 | "darkslateblue", 127 | "darkslategray", 128 | "darkturquoise", 129 | "darkviolet", 130 | "deeppink", 131 | "deepskyblue", 132 | "dimgray", 133 | "dodgerblue", 134 | "firebrick", 135 | "floralwhite", 136 | "forestgreen", 137 | "fuchsia", 138 | "gainsboro", 139 | "ghostwhite", 140 | "gold", 141 | "goldenrod", 142 | "gray", 143 | "green", 144 | "greenyellow", 145 | "honeydew", 146 | "hotpink", 147 | "indianred", 148 | "indigo", 149 | "ivory", 150 | "khaki", 151 | "lavender", 152 | "lavenderblush", 153 | "lawngreen", 154 | "lemonchiffon", 155 | "lightblue", 156 | "lightcoral", 157 | "lightcyan", 158 | "lightgoldenrodyellow", 159 | "lightgray", 160 | "lightgreen", 161 | "lightpink", 162 | "lightsalmon", 163 | "lightseagreen", 164 | "lightskyblue", 165 | "lightslategray", 166 | "lightsteelblue", 167 | "lightyellow", 168 | "lime", 169 | "limegreen", 170 | "linen", 171 | "magenta", 172 | "maroon", 173 | "mediumaquamarine", 174 | "mediumblue", 175 | "mediumorchid", 176 | "mediumpurple", 177 | "mediumseagreen", 178 | "mediumslateblue", 179 | "mediumspringgreen", 180 | "mediumturquoise", 181 | "mediumvioletred", 182 | "midnightblue", 183 | "mintcream", 184 | "mistyrose", 185 | "moccasin", 186 | "navajowhite", 187 | "navy", 188 | "oldlace", 189 | "olive", 190 | "olivedrab", 191 | "orange", 192 | "orangered", 193 | "orchid", 194 | "palegoldenrod", 195 | "palegreen", 196 | "paleturquoise", 197 | "palevioletred", 198 | "papayawhip", 199 | "peachpuff", 200 | "peru", 201 | "pink", 202 | "plum", 203 | "powderblue", 204 | "purple", 205 | "red", 206 | "rosybrown", 207 | "royalblue", 208 | "saddlebrown", 209 | "salmon", 210 | "sandybrown", 211 | "seagreen", 212 | "seashell", 213 | "sienna", 214 | "silver", 215 | "skyblue", 216 | "slateblue", 217 | "slategray", 218 | "snow", 219 | "springgreen", 220 | "steelblue", 221 | "tan", 222 | "teal", 223 | "thistle", 224 | "tomato", 225 | "turquoise", 226 | "violet", 227 | "wheat", 228 | "white", 229 | "whitesmoke", 230 | "yellow", 231 | "yellowgreen" 232 | ] 233 | }, 234 | "height": { 235 | "title": "height", 236 | "description": "A height\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 237 | "type": "integer", 238 | "minimum": 0 239 | } 240 | }, 241 | "minProperties": 1, 242 | "additionalProperties": false 243 | }, 244 | "text": { 245 | "title": "text", 246 | "description": "Text settings\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 247 | "type": "object", 248 | "properties": { 249 | "error": { 250 | "title": "error", 251 | "description": "Color settings for misspellings\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 252 | "type": "object", 253 | "properties": { 254 | "foreground": { 255 | "$ref": "#/definitions/foreground-property" 256 | }, 257 | "background": { 258 | "$ref": "#/definitions/background-property" 259 | } 260 | }, 261 | "minProperties": 1, 262 | "additionalProperties": false 263 | }, 264 | "typed": { 265 | "title": "error", 266 | "description": "Color settings for typed characters\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 267 | "type": "object", 268 | "properties": { 269 | "foreground": { 270 | "$ref": "#/definitions/foreground-property" 271 | }, 272 | "background": { 273 | "$ref": "#/definitions/background-property" 274 | } 275 | }, 276 | "minProperties": 1, 277 | "additionalProperties": false 278 | }, 279 | "untyped": { 280 | "title": "error", 281 | "description": "Color settings for not typed characters\nhttps://github.com/maaslalani/typer?tab=readme-ov-file#themes", 282 | "type": "object", 283 | "properties": { 284 | "foreground": { 285 | "$ref": "#/definitions/foreground-property" 286 | }, 287 | "background": { 288 | "$ref": "#/definitions/background-property" 289 | } 290 | }, 291 | "minProperties": 1, 292 | "additionalProperties": false 293 | } 294 | }, 295 | "minProperties": 1, 296 | "additionalProperties": false 297 | } 298 | }, 299 | "minProperties": 1, 300 | "additionalProperties": false 301 | } 302 | }, 303 | "minProperties": 1, 304 | "additionalProperties": false 305 | } 306 | --------------------------------------------------------------------------------