diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b0c9f68..a6b2adb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,7 +4,7 @@ jobs: test: strategy: matrix: - go-version: [~1.13, ^1] + go-version: [~1.16, ^1] os: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.os }} env: diff --git a/client/client.go b/client/client.go new file mode 100644 index 0000000..fa7c2cf --- /dev/null +++ b/client/client.go @@ -0,0 +1,221 @@ +package client + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "log" + "sort" + "time" + + "github.com/charmbracelet/charm/kv" + "github.com/dgraph-io/badger/v3" + "github.com/google/uuid" +) + +// Client provides the Glow interface to the Charm Cloud +type Client struct { + kv *kv.KV +} + +var stashPrefix = []byte("stash_") + +// ErrorPageOutOfBounds is an error for an invalid page number. +var ErrorPageOutOfBounds = errors.New("page must be a value of 1 or greater") + +// MarkdownsByCreatedAtDesc sorts markdown documents by date in descending +// order. It implements sort.Interface for []Markdown based on the CreatedAt +// field. +type MarkdownsByCreatedAtDesc []*Markdown + +// Sort implementation for MarkdownByCreatedAt. +func (m MarkdownsByCreatedAtDesc) Len() int { return len(m) } +func (m MarkdownsByCreatedAtDesc) Swap(i, j int) { m[i], m[j] = m[j], m[i] } +func (m MarkdownsByCreatedAtDesc) Less(i, j int) bool { return m[i].CreatedAt.After(m[j].CreatedAt) } + +// Markdown is the struct that contains the markdown and note data. If +// EncryptKeyID is not blank, the content should be assumed to be encrypted. +// Once decrypted, that field will be blanked. +type Markdown struct { + ID string `json:"id"` + Note string `json:"note"` + Body string `json:"body,omitempty"` + CreatedAt time.Time `json:"created_at"` +} + +// NewClient creates a new Client with the default settings +func NewClient() (*Client, error) { + kv, err := kv.OpenWithDefaults("charm.sh.glow") + if err != nil { + return nil, err + } + err = kv.Sync() + if err != nil { + return nil, err + } + return &Client{kv: kv}, nil +} + +// GetNews returns the Glow paginated news results. +func (cc *Client) GetNews(page int) ([]*Markdown, error) { + if page < 1 { + return nil, ErrorPageOutOfBounds + } + var news []*Markdown + // err := cc.makeAPIRequest("GET", fmt.Sprintf("news?page=%d", page), nil, &news) + // if err != nil { + // return nil, err + // } + return news, nil +} + +// GetNewsMarkdown returns the Markdown struct for the given news markdown ID. +func (cc *Client) GetNewsMarkdown(markdownID string) (*Markdown, error) { + var md Markdown + // err := cc.makeAPIRequest("GET", fmt.Sprintf("news/%d", markdownID), nil, &md) + // if err != nil { + // return nil, err + // } + return &md, nil +} + +// GetStash returns the paginated user stash for the authenticated Charm user. +func (cc *Client) GetStash(page int) ([]*Markdown, error) { + if page < 1 { + return nil, ErrorPageOutOfBounds + } + limit := 50 + startOffset := (page * limit) - limit + endOffset := page * limit + var stash MarkdownsByCreatedAtDesc + err := cc.kv.View(func(txn *badger.Txn) error { + opt := badger.DefaultIteratorOptions + opt.Prefix = stashPrefix + it := txn.NewIterator(opt) + defer it.Close() + for it.Seek(stashPrefix); it.ValidForPrefix(stashPrefix); it.Next() { + item := it.Item() + err := item.Value(func(v []byte) error { + md := &Markdown{} + err := json.Unmarshal(v, md) + if err != nil { + return err + } + stash = append(stash, md) + return nil + }) + if err != nil { + return err + } + if len(stash) > endOffset { + break + } + } + return nil + }) + if err != nil { + return nil, err + } + sort.Sort(stash) + if startOffset >= len(stash) { + return []*Markdown{}, nil + } + if endOffset > len(stash) { + return stash[startOffset:], nil + } + return stash[startOffset:endOffset], nil +} + +// GetStashMarkdown returns the Markdown struct for the given stash markdown ID. +func (cc *Client) GetStashMarkdown(markdownID string) (*Markdown, error) { + var md Markdown + d, err := cc.kv.Get([]byte(markdownID)) + if err != nil { + return nil, err + } + err = json.Unmarshal(d, &md) + if err != nil { + return nil, err + } + return &md, nil +} + +// StashMarkdown encrypts and stashes a new markdown file with note. +func (cc *Client) StashMarkdown(note string, body string) (*Markdown, error) { + gid := uuid.New().String() + md := &Markdown{Note: note, Body: body, ID: gid, CreatedAt: time.Now()} + err := cc.saveMarkdown(md) + if err != nil { + return nil, err + } + return md, nil +} + +// DeleteMarkdown deletes the stash markdown for the given ID. +func (cc *Client) DeleteMarkdown(markdownID string) error { + txn, err := cc.kv.NewTransaction(true) + if err != nil { + return err + } + mid, sid := markdownKeys(markdownID) + err = txn.Delete(mid) + if err != nil { + return err + } + err = txn.Delete(sid) + if err != nil { + return err + } + return cc.kv.Commit(txn, func(err error) { + if err != nil { + log.Printf("Badger commit error: %s", err) + } + }) +} + +// SetMarkdownNote updates the note for a given stash markdown ID. +func (cc *Client) SetMarkdownNote(markdownID string, note string) error { + md, err := cc.GetStashMarkdown(markdownID) + if err != nil { + return err + } + md.Note = note + return cc.saveMarkdown(md) +} + +func (cc *Client) saveMarkdown(md *Markdown) error { + mid, sid := markdownKeys(md.ID) + txn, err := cc.kv.NewTransaction(true) + if err != nil { + return err + } + buf := bytes.NewBuffer(nil) + err = json.NewEncoder(buf).Encode(md) + if err != nil { + return err + } + err = txn.Set(mid, buf.Bytes()) + if err != nil { + return err + } + buf = bytes.NewBuffer(nil) + smd := &Markdown{ID: md.ID, Note: md.Note, CreatedAt: md.CreatedAt} + err = json.NewEncoder(buf).Encode(smd) + if err != nil { + return err + } + err = txn.Set(sid, buf.Bytes()) + if err != nil { + return err + } + return cc.kv.Commit(txn, func(err error) { + if err != nil { + log.Printf("Badger commit error: %s", err) + } + }) +} + +func markdownKeys(markdownID string) ([]byte, []byte) { + return []byte(markdownID), []byte(fmt.Sprintf("%s%s", stashPrefix, markdownID)) +} diff --git a/config_cmd.go b/config_cmd.go index a90b903..28286d1 100644 --- a/config_cmd.go +++ b/config_cmd.go @@ -7,7 +7,6 @@ import ( "os/exec" "path" - "github.com/charmbracelet/charm/ui/common" gap "github.com/muesli/go-app-paths" "github.com/spf13/cobra" ) @@ -27,8 +26,8 @@ var configCmd = &cobra.Command{ Use: "config", Hidden: false, Short: "Edit the glow config file", - Long: formatBlock(fmt.Sprintf("\n%s the glow config file. We’ll use EDITOR to determine which editor to use. If the config file doesn't exist, it will be created.", common.Keyword("Edit"))), - Example: formatBlock("glow config\nglow config --config path/to/config.yml"), + Long: paragraph(fmt.Sprintf("\n%s the glow config file. We’ll use EDITOR to determine which editor to use. If the config file doesn't exist, it will be created.", keyword("Edit"))), + Example: paragraph("glow config\nglow config --config path/to/config.yml"), Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { editor := os.Getenv("EDITOR") diff --git a/formatting.go b/formatting.go deleted file mode 100644 index 6e3dffa..0000000 --- a/formatting.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "github.com/muesli/reflow/indent" - "github.com/muesli/reflow/wordwrap" -) - -const ( - wrapAt = 78 - indentAmount = 2 -) - -func formatBlock(s string) string { - return indent.String(wordwrap.String(s, wrapAt-indentAmount), indentAmount) -} diff --git a/go.mod b/go.mod index 1a1c576..7a8dff8 100644 --- a/go.mod +++ b/go.mod @@ -1,29 +1,28 @@ module github.com/charmbracelet/glow -go 1.13 +go 1.16 require ( - github.com/charmbracelet/bubbles v0.7.6 - github.com/charmbracelet/bubbletea v0.13.2 - github.com/charmbracelet/charm v0.8.6 + github.com/charmbracelet/bubbles v0.10.3 + github.com/charmbracelet/bubbletea v0.20.0 + github.com/charmbracelet/charm v0.9.1 github.com/charmbracelet/glamour v0.2.1-0.20210402234443-abe9cda419ba + github.com/charmbracelet/lipgloss v0.5.0 + github.com/dgraph-io/badger/v3 v3.2011.1 github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac - github.com/google/uuid v1.1.2 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect - github.com/mattn/go-runewidth v0.0.12 + github.com/google/uuid v1.1.2 + github.com/mattn/go-runewidth v0.0.13 github.com/meowgorithm/babyenv v1.3.1 github.com/mitchellh/go-homedir v1.1.0 github.com/muesli/gitcha v0.2.0 github.com/muesli/go-app-paths v0.2.1 - github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 - github.com/muesli/termenv v0.8.1 + github.com/muesli/reflow v0.3.0 + github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 github.com/sahilm/fuzzy v0.1.0 github.com/segmentio/ksuid v1.0.3 github.com/spf13/cobra v1.4.0 github.com/spf13/viper v1.4.0 - golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect - golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc // indirect - golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 - golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf - golang.org/x/text v0.3.2 + golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c + golang.org/x/term v0.0.0-20210422114643-f5beecf764ed + golang.org/x/text v0.3.3 ) diff --git a/go.sum b/go.sum index b2779fd..1240542 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,14 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM= +github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38 h1:smF2tmSOzy2Mm+0dGI2AIUHY+w0BUc+4tn40djz7+6U= github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI= github.com/alecthomas/chroma v0.8.2 h1:x3zkuE2lUk/RIekyAJ3XRqSCP4zwWDfcw/YJCuCAACg= @@ -13,91 +20,198 @@ github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897 h1:p9Sln00KOTlrYkx github.com/alecthomas/repr v0.0.0-20180818092828-117648cd9897/go.mod h1:xTS7Pm1pD1mvyM075QCDSRqH6qRLXylzS24ZTpRiSzQ= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/atotto/clipboard v0.1.2 h1:YZCtFu5Ie8qX2VmVTBnrqLSiU9XOWwqNRmdT3gIQzbY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= +github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/auth0/go-jwt-middleware v1.0.0 h1:76t55qLQu3xjMFbkirbSCA8ZPcO1ny+20Uq1wkSTRDE= +github.com/auth0/go-jwt-middleware v1.0.0/go.mod h1:nX2S0GmCyl087kdNSSItfOvMYokq5PSTG1yGIP5Le4U= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/calmh/randomart v1.1.0 h1:evl+iwc10LXtHdMZhzLxmsCQVmWnkXs44SbC6Uk0Il8= github.com/calmh/randomart v1.1.0/go.mod h1:DQUbPVyP+7PAs21w/AnfMKG5NioxS3TbZ2F9MSK/jFM= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/charmbracelet/bubbles v0.7.5/go.mod h1:IRTORFvhEI6OUH7WhN2Ks8Z8miNGimk1BE6cmHijOkM= -github.com/charmbracelet/bubbles v0.7.6 h1:SCAp4ZEUf2tBNEsufo+Xxxu2dvbFhYSDPrX45toQZrM= -github.com/charmbracelet/bubbles v0.7.6/go.mod h1:0D4XRYK0tjo8JMvflz1obpVcOikNZSG46SFauoZj22s= -github.com/charmbracelet/bubbletea v0.12.2/go.mod h1:3gZkYELUOiEUOp0bTInkxguucy/xRbGSOcbMs1geLxg= -github.com/charmbracelet/bubbletea v0.13.2 h1:fSOx3q0/VbA3ChWeiNcUsNeNysD9FFWD1tZypShBuCQ= -github.com/charmbracelet/bubbletea v0.13.2/go.mod h1:okqaA5VF0aSpEZ2HB+L/cxVw2HthIDZ1dmWoRZs8/4g= -github.com/charmbracelet/charm v0.8.6 h1:/U6rxGj4J6zZ1Ex8+wTr4hNMr4ESBzNZbC1UyrJPVbg= -github.com/charmbracelet/charm v0.8.6/go.mod h1:8dE3uX+TYSpa7Q6e/CmjN6WSd7koSAKNQTGWugFREx4= +github.com/charmbracelet/bubbles v0.8.0/go.mod h1:5WX1sSSjNCgCrzvRMN/z23HxvWaa+AI16Ch0KPZPeDs= +github.com/charmbracelet/bubbles v0.10.3 h1:fKarbRaObLn/DCsZO4Y3vKCwRUzynQD9L+gGev1E/ho= +github.com/charmbracelet/bubbles v0.10.3/go.mod h1:jOA+DUF1rjZm7gZHcNyIVW+YrBPALKfpGVdJu8UiJsA= +github.com/charmbracelet/bubbletea v0.13.1/go.mod h1:tp9tr9Dadh0PLhgiwchE5zZJXm5543JYjHG9oY+5qSg= +github.com/charmbracelet/bubbletea v0.19.0/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA= +github.com/charmbracelet/bubbletea v0.19.3/go.mod h1:VuXF2pToRxDUHcBUcPmCRUHRvFATM4Ckb/ql1rBl3KA= +github.com/charmbracelet/bubbletea v0.20.0 h1:/b8LEPgCbNr7WWZ2LuE/BV1/r4t5PyYJtDb+J3vpwxc= +github.com/charmbracelet/bubbletea v0.20.0/go.mod h1:zpkze1Rioo4rJELjRyGlm9T2YNou1Fm4LIJQSa5QMEM= +github.com/charmbracelet/charm v0.9.1 h1:lcBUL8OruDLuP56erZ1iD1viJ41H7TyyHeIOIZ4T0v4= +github.com/charmbracelet/charm v0.9.1/go.mod h1:0EnHP/Gh/m7gah8+f0f9WL0oSS9yOu0ZMTXNdzx919k= github.com/charmbracelet/glamour v0.2.1-0.20210402234443-abe9cda419ba h1:smKYYwwVPZyMK2LCirIi2WY25tZZW0IU7GYe1ASGCe4= github.com/charmbracelet/glamour v0.2.1-0.20210402234443-abe9cda419ba/go.mod h1:nHP5wEbsv2eOJ7XfiScQV3p5dpZSM051R0VkxnOIPgg= +github.com/charmbracelet/harmonica v0.1.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= +github.com/charmbracelet/keygen v0.1.2 h1:Gr/gdIOjDIxCTRVXpwa9tsXPoJPS2eGNehPoMnZLvTQ= +github.com/charmbracelet/keygen v0.1.2/go.mod h1:kFQ3Cvop12fXWX1K29vxDxV9x8ujG4wBSXq//GySSSk= +github.com/charmbracelet/lipgloss v0.1.1/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg= +github.com/charmbracelet/lipgloss v0.1.2/go.mod h1:5D8zradw52m7QmxRF6QgwbwJi9je84g8MkWiGN07uKg= +github.com/charmbracelet/lipgloss v0.4.0/go.mod h1:vmdkHvce7UzX6xkyf4cca8WlwdQ5RQr8fzta+xl7BOM= +github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= +github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= +github.com/charmbracelet/wish v0.1.1 h1:BLsUBlHzIxw5ebzmBzxUUMfakdteew6gQOhudhsLKpM= +github.com/charmbracelet/wish v0.1.1/go.mod h1:tD+sb5aS1SSX0t7hIZXXUonv2YbnFNCnU6qfOolKKUE= github.com/chris-ramon/douceur v0.2.0 h1:IDMEdxlEUUBYBKE4z/mJnFyVXox+MjuEVDJNN27glkU= github.com/chris-ramon/douceur v0.2.0/go.mod h1:wDW5xjJdeoMm1mRt4sD4c/LbF/mWdEpRXQKjTR8nIBE= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/containerd/console v1.0.1 h1:u7SFAJyRqWcG6ogaMAx3KjSTy1e3hT9QxqX7Jco7dRc= +github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ= github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/badger/v3 v3.2011.1 h1:Hmyof0WMEF/QtutX5SQHzIMnJQxb/IrSzhjckV2SD6g= +github.com/dgraph-io/badger/v3 v3.2011.1/go.mod h1:0rLLrQpKVQAL0or/lBLMQznhr6dWWX7h5AKnmnqx268= +github.com/dgraph-io/ristretto v0.0.4-0.20210122082011-bb5d392ed82d h1:eQYOG6A4td1tht0NdJB9Ls6DsXRGb2Ft6X9REU/MbbE= +github.com/dgraph-io/ristretto v0.0.4-0.20210122082011-bb5d392ed82d/go.mod h1:tv2ec8nA7vRpSYX7/MbP52ihrUMXIHit54CQMq8npXQ= github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac h1:opbrjaN/L8gg6Xh5D04Tem+8xVcz6ajZlGCs49mQgyg= github.com/dustin/go-humanize v1.0.1-0.20200219035652-afde56e7acac/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gliderlabs/ssh v0.3.3 h1:mBQ8NiOgDkINJrZtoizkC3nDNYgSaWtxyem6S2XHBtA= +github.com/gliderlabs/ssh v0.3.3/go.mod h1:ZSS+CUoKHDrqVakTfTWUlKSr9MtMFkC4UvtQKD7O914= +github.com/go-delve/delve v1.5.0/go.mod h1:c6b3a1Gry6x8a4LGCe/CWzrocrfaHvkUxCj3k4bvSUQ= +github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E= +github.com/go-git/go-billy/v5 v5.2.0/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0= +github.com/go-git/go-git-fixtures/v4 v4.2.1/go.mod h1:K8zd3kDUAykwTdDCr+I0per6Y6vMiRR/nnVTBtavnB0= +github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti4ihgckDc= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/flatbuffers v1.12.0 h1:/PtAHvnBY4Kqnx/xCQ3OIV9uYcSFGScBsWI3Oogeh6w= +github.com/google/flatbuffers v1.12.0/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3 h1:x95R7cp+rSeeqAMI2knLtQ0DKlaBhv2NrtrOvafPHRo= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-dap v0.2.0/go.mod h1:5q8aYQFnHOAZEMP+6vmq25HKYAEwE+LF5yh7JKrrhSQ= github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= +github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc= +github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jacobsa/crypto v0.0.0-20190317225127-9f44e2d11115 h1:YuDUUFNM21CAbyPOpOP8BicaTD/0klJEKt5p8yuw+uY= +github.com/jacobsa/crypto v0.0.0-20190317225127-9f44e2d11115/go.mod h1:LadVJg0XuawGk+8L1rYnIED8451UyNxEMdTWCEt5kmU= +github.com/jacobsa/oglematchers v0.0.0-20150720000706-141901ea67cd h1:9GCSedGjMcLZCrusBZuo4tyKLpKUPenUUqi34AkuFmA= +github.com/jacobsa/oglematchers v0.0.0-20150720000706-141901ea67cd/go.mod h1:TlmyIZDpGmwRoTWiakdr+HA1Tukze6C6XbRVidYq02M= +github.com/jacobsa/oglemock v0.0.0-20150831005832-e94d794d06ff h1:2xRHTvkpJ5zJmglXLRqHiZQNjUoOkhUyhTAhEQvPAWw= +github.com/jacobsa/oglemock v0.0.0-20150831005832-e94d794d06ff/go.mod h1:gJWba/XXGl0UoOmBQKRWCJdHrr3nE0T65t6ioaj3mLI= +github.com/jacobsa/ogletest v0.0.0-20170503003838-80d50a735a11 h1:BMb8s3ENQLt5ulwVIHVDWFHp8eIXmbfSExkvdn9qMXI= +github.com/jacobsa/ogletest v0.0.0-20170503003838-80d50a735a11/go.mod h1:+DBdDyfoO2McrOyDemRBq0q9CMEByef7sYl7JH5Q3BI= +github.com/jacobsa/reqtrace v0.0.0-20150505043853-245c9e0234cb h1:uSWBjJdMf47kQlXMwWEfmc864bA1wAC+Kl3ApryuG9Y= +github.com/jacobsa/reqtrace v0.0.0-20150505043853-245c9e0234cb/go.mod h1:ivcmUvxXWjb27NsPEaiYK7AidlZXS7oQ5PowUS9z3I4= +github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= +github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= @@ -105,18 +219,28 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69 github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mattn/go-isatty v0.0.13/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14-0.20210829144114-504425e14f74/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg= +github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/meowgorithm/babyenv v1.3.0/go.mod h1:lwNX+J6AGBFqNrMZ2PTLkM6SO+W4X8DOg9zBDO4j3Ig= github.com/meowgorithm/babyenv v1.3.1 h1:18ZEYIgbzoFQfRLF9+lxjRfk/ui6w8U0FWl07CgWvvc= github.com/meowgorithm/babyenv v1.3.1/go.mod h1:lwNX+J6AGBFqNrMZ2PTLkM6SO+W4X8DOg9zBDO4j3Ig= +github.com/meowgorithm/babylogger v1.2.0 h1:lV48OR+bMVR4qVIaUrcTcITGhfV+C/gKnTYCUR++APU= +github.com/meowgorithm/babylogger v1.2.0/go.mod h1:Kmw1fbhkP4sLJmhiGIpThiG+guQAQ8dQ3GnLa+8Fjf0= github.com/microcosm-cc/bluemonday v1.0.4 h1:p0L+CTpo/PLFdkoPcJemLXG+fpMD7pYOoDEq1axMbGg= github.com/microcosm-cc/bluemonday v1.0.4/go.mod h1:8iwZnFn2CDDNZ0r6UXhF4xawGvzaqzCRa1n3/lO3W2w= github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a h1:eU8j/ClY2Ty3qdHnn0TyW3ivFoPC/0F1gQZz8yTxbbE= @@ -125,26 +249,35 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mmcloughlin/avo v0.0.0-20201105074841-5d2f697d268f/go.mod h1:6aKT4zZIrpGqB3RpFU14ByCSSyKY6LfJz4J/JJChHfI= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= +github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/gitcha v0.2.0 h1:+wOgT2dI9s2Tznj1t1rb/qkK5e0cb6qD8c4IX2TR/YY= github.com/muesli/gitcha v0.2.0/go.mod h1:Ri8m9TZS4+ORG4JVmVKUQcWZuxDvUW3UKxMdQfzG2zI= github.com/muesli/go-app-paths v0.2.1 h1:Qi+2igkDX2aPqyRddp7P0sMQIBwBqhkfQfNcjdGjL6Y= github.com/muesli/go-app-paths v0.2.1/go.mod h1:SxS3Umca63pcFcLtbjVb+J0oD7cl4ixQWoBKhGEtEho= -github.com/muesli/reflow v0.1.0/go.mod h1:I9bWAt7QTg/que/qmUCJBGlj7wEq8OAFBjPNjc6xK4I= github.com/muesli/reflow v0.2.0/go.mod h1:qT22vjVmM9MIUeLgsVYe/Ye7eZlbv9dZjL3dVhUqLX8= -github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68 h1:y1p/ycavWjGT9FnmSjdbWUlLGvcxrY0Rw3ATltrxOhk= github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= +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/sasquatch v0.0.0-20200811221207-66979d92330a h1:Hw/15RYEOUD6T9UCRkUmNBa33kJkH33Fui6hE4sRLKU= github.com/muesli/sasquatch v0.0.0-20200811221207-66979d92330a/go.mod h1:+XG0ne5zXWBTSbbe7Z3/RWxaT8PZY6zaZ1dX6KjprYY= github.com/muesli/termenv v0.7.2/go.mod h1:ct2L5N2lmix82RaY3bMWwVu/jUFc9Ule0KGDCiKYPh8= -github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc= -github.com/muesli/termenv v0.8.1 h1:9q230czSP3DHVpkaPDXGp0TOfAwyjyYwXlUCQxQSaBk= github.com/muesli/termenv v0.8.1/go.mod h1:kzt/D/4a88RoheZmwfqorY3A+tnsSMA9HJC/fQSFKo0= +github.com/muesli/termenv v0.9.0/go.mod h1:R/LzAKf+suGs4IsO95y7+7DpFHO0KABgnZqtlyx2mBw= +github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739 h1:QANkGiGr39l1EESqrE0gZw0/AJNYzIvoGLhIoVYtluI= +github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= +github.com/muesli/toktok v0.0.0-20201007181047-c74187025f3f h1:CsWXx5ejjjogr8EwcrzAG9p+oqbLV/Yoh7ugE4gEVlc= +github.com/muesli/toktok v0.0.0-20201007181047-c74187025f3f/go.mod h1:FmV+MTLqgtVrOUfvwWxUZXyoiCGzYt8siG4BqrY4Mb8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -155,15 +288,19 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk= +github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 h1:G04eS0JkAIVZfaJLjla9dNxkJCPiKIGZlw9AfOhzOD0= @@ -172,109 +309,239 @@ github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/segmentio/ksuid v1.0.3 h1:FoResxvleQwYiPAVKe1tMUlEirodZqlqglIuFsdDntY= github.com/segmentio/ksuid v1.0.3/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0= +github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= github.com/spf13/cobra v1.4.0 h1:y+wJpx64xcgO1V+RcnwW0LEHxTKRi2ZDPSBjWnrg88Q= github.com/spf13/cobra v1.4.0/go.mod h1:Wo4iy3BUC+X2Fybo0PDqwJIv3dNRiZLHQymsfxlB84g= github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/twitchyliquid64/golang-asm v0.15.0/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/urfave/negroni v1.0.0 h1:kIimOitoypq34K7TG7DUaJ9kq/N4Ofuwi1sjz0KipXc= +github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4= +github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/xrash/smetrics v0.0.0-20200730060457-89a2a8a1fb0b h1:tnWgqoOBmInkt5pbLjagwNVjjT4RdJhFHzL1ebCSRh8= +github.com/xrash/smetrics v0.0.0-20200730060457-89a2a8a1fb0b/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.1 h1:eVwehsLsZlCJCwXyGLgg+Q4iFWE/eTIMG0e8waCmm/I= github.com/yuin/goldmark v1.3.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.starlark.net v0.0.0-20190702223751-32f345186213/go.mod h1:c1/X6cHgvdXj6pUlmWKMkuqRnW4K8x2vwt6JAaaircg= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +goji.io v2.0.2+incompatible h1:uIssv/elbKRLznFUy3Xj4+2Mz/qKhek/9aZQDUMae7c= +goji.io v2.0.2+incompatible/go.mod h1:sbqFwrtqZACxLBTQcdgVjFh54yGVCvwq8+w49MVMMIk= +golang.org/x/arch v0.0.0-20190927153633-4e8777c89be4/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= -golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= +golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc h1:zK/HqS5bZxDptfPJNq8v7vJfXtkU7r9TLIoSr1bXaP4= -golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= +golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201020230747-6e5568b54d1a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78 h1:nVuTkr9L6Bq62qpUqKo/RnZCFfzDBL0bYo6w9OJUqZY= -golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/sys v0.0.0-20201126233918-771906719818/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210422114643-f5beecf764ed h1:Ei4bQjjpYUsS4efOUz+5Nz++IVkHk87n2zBA0NxBWc0= +golang.org/x/term v0.0.0-20210422114643-f5beecf764ed/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20201105001634-bc3cf281b174/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 h1:M8tBwCtWD/cZV9DZpFYRUgaymAYAr+aIUTWzDaM3uPs= +golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +modernc.org/cc/v3 v3.32.4/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= +modernc.org/cc/v3 v3.33.5 h1:gfsIOmcv80EelyQyOHn/Xhlzex8xunhQxWiJRMYmPrI= +modernc.org/cc/v3 v3.33.5/go.mod h1:0R6jl1aZlIl2avnYfbfHBS1QB6/f+16mihBObaBC878= +modernc.org/ccgo/v3 v3.9.2/go.mod h1:gnJpy6NIVqkETT+L5zPsQFj7L2kkhfPMzOghRNv/CFo= +modernc.org/ccgo/v3 v3.9.4 h1:mt2+HyTZKxva27O6T4C9//0xiNQ/MornL3i8itM5cCs= +modernc.org/ccgo/v3 v3.9.4/go.mod h1:19XAY9uOrYnDhOgfHwCABasBvK69jgC4I8+rizbk3Bc= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.7.13-0.20210308123627-12f642a52bb8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/libc v1.9.5 h1:zv111ldxmP7DJ5mOIqzRbza7ZDl3kh4ncKfASB2jIYY= +modernc.org/libc v1.9.5/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w= +modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/mathutil v1.2.2 h1:+yFk8hBprV+4c0U9GjFtL+dV3N8hOJ8JCituQcMShFY= +modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= +modernc.org/memory v1.0.4 h1:utMBrFcpnQDdNsmM6asmyH/FM9TqLPS7XF7otpJmrwM= +modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc= +modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A= +modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.10.8 h1:tZzV+/FwlSBddiJAHLR+qxsw2nx7jpLMKOCVu6NTjxI= +modernc.org/sqlite v1.10.8/go.mod h1:k45BYY2DU82vbS/dJ24OzHCtjPeMEcZ1DV2POiE8nRs= +modernc.org/strutil v1.1.0 h1:+1/yCzZxY2pZwwrsbH+4T7BQMoLQ9QiBshRC9eicYsc= +modernc.org/strutil v1.1.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs= +modernc.org/tcl v1.5.2 h1:sYNjGr4zK6cDH74USl8wVJRrvDX6UOLpG0j4lFvR0W0= +modernc.org/tcl v1.5.2/go.mod h1:pmJYOLgpiys3oI4AeAafkcUfE+TKKilminxNyU/+Zlo= +modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk= +modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.0.1-0.20210308123920-1f282aa71362/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= +modernc.org/z v1.0.1 h1:WyIDpEpAIx4Hel6q/Pcgj/VhaQV5XPJ2I6ryIYbjnpc= +modernc.org/z v1.0.1/go.mod h1:8/SRk5C/HgiQWCgXdfpb+1RvhORdkz5sw72d3jjtyqA= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go index 47424e4..5d60435 100644 --- a/main.go +++ b/main.go @@ -12,17 +12,16 @@ import ( "path/filepath" "strings" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/charm/cmd" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/glow/ui" + "github.com/charmbracelet/glow/utils" "github.com/meowgorithm/babyenv" gap "github.com/muesli/go-app-paths" "github.com/spf13/cobra" "github.com/spf13/viper" "golang.org/x/term" - - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/charm/ui/common" - "github.com/charmbracelet/glamour" - "github.com/charmbracelet/glow/ui" - "github.com/charmbracelet/glow/utils" ) var ( @@ -41,7 +40,7 @@ var ( rootCmd = &cobra.Command{ Use: "glow [SOURCE|DIR]", Short: "Render markdown on the CLI, with pizzazz!", - Long: formatBlock(fmt.Sprintf("\nRender markdown on the CLI, %s!", common.Keyword("with pizzazz"))), + Long: paragraph(fmt.Sprintf("\nRender markdown on the CLI, %s!", keyword("with pizzazz"))), SilenceErrors: false, SilenceUsage: false, TraverseChildren: true, @@ -334,6 +333,7 @@ func runTUI(workingDirectory string, stashedOnly bool) error { cfg.ShowAllFiles = showAllFiles cfg.GlamourMaxWidth = width cfg.GlamourStyle = style + cfg.EnableMouse = mouse if stashedOnly { cfg.DocumentTypes.Add(ui.StashedDoc, ui.NewsDoc) @@ -342,14 +342,7 @@ func runTUI(workingDirectory string, stashedOnly bool) error { } // Run Bubble Tea program - p := ui.NewProgram(cfg) - p.EnterAltScreen() - defer p.ExitAltScreen() - if mouse { - p.EnableMouseCellMotion() - defer p.DisableMouseCellMotion() - } - if err := p.Start(); err != nil { + if err := ui.NewProgram(cfg).Start(); err != nil { return err } @@ -400,6 +393,9 @@ func init() { stashCmd.PersistentFlags().StringVarP(&memo, "memo", "m", "", "memo/note for stashing") rootCmd.AddCommand(stashCmd) + rootCmd.AddCommand(cmd.LinkCmd("glow")) + rootCmd.AddCommand(cmd.KeysCmd) + rootCmd.AddCommand(cmd.KeySyncCmd) rootCmd.AddCommand(configCmd) } diff --git a/stash_cmd.go b/stash_cmd.go index e6b3ea4..4961c1e 100644 --- a/stash_cmd.go +++ b/stash_cmd.go @@ -3,26 +3,26 @@ package main import ( "fmt" "io/ioutil" - "log" "os" "path" "strings" - "github.com/charmbracelet/charm" - "github.com/charmbracelet/charm/ui/common" - "github.com/muesli/termenv" + charm "github.com/charmbracelet/charm/proto" + "github.com/charmbracelet/glow/client" + "github.com/charmbracelet/lipgloss" "github.com/spf13/cobra" ) var ( memo string + dot = lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")).Render("•") stashCmd = &cobra.Command{ Use: "stash [SOURCE]", Hidden: false, Short: "Stash a markdown", - Long: formatBlock(fmt.Sprintf("\nDo %s stuff. Run with no arguments to browse your stash or pass a path to a markdown file to stash it.", common.Keyword("stash"))), - Example: formatBlock("glow stash\nglow stash README.md\nglow stash -m \"secret notes\" path/to/notes.md"), + Long: paragraph(fmt.Sprintf("\nDo %s stuff. Run with no arguments to browse your stash or pass a path to a markdown file to stash it.", keyword("stash"))), + Example: paragraph("glow stash\nglow stash README.md\nglow stash -m \"secret notes\" path/to/notes.md"), Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { initConfig() @@ -53,27 +53,16 @@ var ( return fmt.Errorf("error stashing markdown") } - dot := termenv.String("•").Foreground(common.Green.Color()).String() fmt.Println(dot + " Stashed!") return nil }, } ) -func getCharmConfig() *charm.Config { - cfg, err := charm.ConfigFromEnv() - if err != nil { - log.Fatal(err) - } - - return cfg -} - -func initCharmClient() *charm.Client { - cfg := getCharmConfig() - cc, err := charm.NewClient(cfg) +func initCharmClient() *client.Client { + cc, err := client.NewClient() if err == charm.ErrMissingSSHAuth { - fmt.Println(formatBlock("We had some trouble authenticating via SSH. If this continues to happen the Charm tool may be able to help you. More info at https://github.com/charmbracelet/charm.")) + fmt.Println(paragraph("We had some trouble authenticating via SSH. If this continues to happen the Charm tool may be able to help you. More info at https://github.com/charmbracelet/charm.")) os.Exit(1) } else if err != nil { fmt.Println(err) diff --git a/style.go b/style.go new file mode 100644 index 0000000..7737dd2 --- /dev/null +++ b/style.go @@ -0,0 +1,16 @@ +package main + +import ( + . "github.com/charmbracelet/lipgloss" +) + +var ( + keyword = NewStyle(). + Foreground(AdaptiveColor{Light: "#04B575", Dark: "#04B575"}). + Render + + paragraph = NewStyle(). + Width(78). + Padding(0, 0, 0, 2). + Render +) diff --git a/ui/config.go b/ui/config.go index 67d94d6..44b70fe 100644 --- a/ui/config.go +++ b/ui/config.go @@ -3,10 +3,12 @@ package ui // Config contains TUI-specific configuration. type Config struct { ShowAllFiles bool + CharmHost string `env:"CHARM_HOST" default:"api.charm.sh"` Gopath string `env:"GOPATH"` HomeDir string `env:"HOME"` GlamourMaxWidth uint GlamourStyle string + EnableMouse bool // Which directory should we start from? WorkingDirectory string diff --git a/ui/markdown.go b/ui/markdown.go index f6f8f2c..6511c99 100644 --- a/ui/markdown.go +++ b/ui/markdown.go @@ -8,7 +8,7 @@ import ( "time" "unicode" - "github.com/charmbracelet/charm" + "github.com/charmbracelet/glow/client" "github.com/dustin/go-humanize" "github.com/segmentio/ksuid" "golang.org/x/text/runes" @@ -46,7 +46,7 @@ type markdown struct { // field is ephemeral, and should only be referenced during filtering. filterValue string - charm.Markdown + client.Markdown } func (m *markdown) generateIDs() { @@ -155,7 +155,7 @@ func normalize(in string) (string, error) { // wrapMarkdowns wraps a *charm.Markdown with a *markdown in order to add some // extra metadata. -func wrapMarkdowns(t DocType, md []*charm.Markdown) (m []*markdown) { +func wrapMarkdowns(t DocType, md []*client.Markdown) (m []*markdown) { for _, v := range md { m = append(m, &markdown{ docType: t, diff --git a/ui/pager.go b/ui/pager.go index 7f43019..29ebac6 100644 --- a/ui/pager.go +++ b/ui/pager.go @@ -11,13 +11,12 @@ import ( "github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/charm" - lib "github.com/charmbracelet/charm/ui/common" "github.com/charmbracelet/glamour" + "github.com/charmbracelet/glow/client" + "github.com/charmbracelet/lipgloss" runewidth "github.com/mattn/go-runewidth" "github.com/muesli/reflow/ansi" "github.com/muesli/reflow/truncate" - te "github.com/muesli/termenv" ) const statusBarHeight = 1 @@ -25,31 +24,82 @@ const statusBarHeight = 1 var ( pagerHelpHeight int - mintGreen = lib.NewColorPair("#89F0CB", "#89F0CB") - darkGreen = lib.NewColorPair("#1C8760", "#1C8760") + mintGreen = lipgloss.AdaptiveColor{Light: "#89F0CB", Dark: "#89F0CB"} + darkGreen = lipgloss.AdaptiveColor{Light: "#1C8760", Dark: "#1C8760"} - noteHeading = te.String(" Set Memo "). - Foreground(lib.Cream.Color()). - Background(lib.Green.Color()). - String() + noteHeading = lipgloss.NewStyle(). + Foreground(cream). + Background(green). + Padding(0, 1). + Render("Set Memo") - statusBarNoteFg = lib.NewColorPair("#7D7D7D", "#656565") - statusBarBg = lib.NewColorPair("#242424", "#E6E6E6") + statusBarNoteFg = lipgloss.AdaptiveColor{Light: "#656565", Dark: "#7D7D7D"} + statusBarBg = lipgloss.AdaptiveColor{Light: "#E6E6E6", Dark: "#242424"} - // Styling funcs. - statusBarScrollPosStyle = newStyle(lib.NewColorPair("#5A5A5A", "#949494"), statusBarBg, false) - statusBarNoteStyle = newStyle(statusBarNoteFg, statusBarBg, false) - statusBarHelpStyle = newStyle(statusBarNoteFg, lib.NewColorPair("#323232", "#DCDCDC"), false) - statusBarStashDotStyle = newStyle(lib.Green, statusBarBg, false) - statusBarMessageStyle = newStyle(mintGreen, darkGreen, false) - statusBarMessageStashIconStyle = newStyle(mintGreen, darkGreen, false) - statusBarMessageScrollPosStyle = newStyle(mintGreen, darkGreen, false) - statusBarMessageHelpStyle = newStyle(lib.NewColorPair("#B6FFE4", "#B6FFE4"), lib.Green, false) - helpViewStyle = newStyle(statusBarNoteFg, lib.NewColorPair("#1B1B1B", "#f2f2f2"), false) + statusBarScrollPosStyle = lipgloss.NewStyle(). + Foreground(lipgloss.AdaptiveColor{Light: "#949494", Dark: "#5A5A5A"}). + Background(statusBarBg). + Render + + statusBarNoteStyle = lipgloss.NewStyle(). + Foreground(statusBarNoteFg). + Background(statusBarBg). + Render + + statusBarHelpStyle = lipgloss.NewStyle(). + Foreground(statusBarNoteFg). + Background(lipgloss.AdaptiveColor{Light: "#DCDCDC", Dark: "#323232"}). + Render + + statusBarStashDotStyle = lipgloss.NewStyle(). + Foreground(green). + Background(statusBarBg). + Render + + statusBarMessageStyle = lipgloss.NewStyle(). + Foreground(mintGreen). + Background(darkGreen). + Render + + statusBarMessageStashIconStyle = lipgloss.NewStyle(). + Foreground(mintGreen). + Background(darkGreen). + Render + + statusBarMessageScrollPosStyle = lipgloss.NewStyle(). + Foreground(mintGreen). + Background(darkGreen). + Render + + statusBarMessageHelpStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#B6FFE4")). + Background(green). + Render + + helpViewStyle = lipgloss.NewStyle(). + Foreground(statusBarNoteFg). + Background(lipgloss.AdaptiveColor{Light: "#f2f2f2", Dark: "#1B1B1B"}). + Render + + spinnerStyle = lipgloss.NewStyle(). + Foreground(statusBarNoteFg). + Background(statusBarBg) + + pagerNoteInputPromptStyle = lipgloss.NewStyle(). + Foreground(darkGray). + Background(yellowGreen). + Padding(0, 1) + + pagerNoteInputStyle = lipgloss.NewStyle(). + Foreground(darkGray). + Background(yellowGreen) + + pagerNoteInputCursorStyle = lipgloss.NewStyle(). + Foreground(fuschia) ) type contentRenderedMsg string -type noteSavedMsg *charm.Markdown +type noteSavedMsg *client.Markdown type pagerState int @@ -83,28 +133,22 @@ type pagerModel struct { func newPagerModel(common *commonModel) pagerModel { // Init viewport - vp := viewport.Model{} + vp := viewport.New(0, 0) vp.YPosition = 0 vp.HighPerformanceRendering = config.HighPerformancePager // Text input for notes/memos - ti := textinput.NewModel() - ti.Prompt = te.String(" > "). - Foreground(lib.Color(darkGray)). - Background(lib.YellowGreen.Color()). - String() - ti.TextColor = darkGray - ti.BackgroundColor = lib.YellowGreen.String() - ti.CursorColor = lib.Fuschia.String() + ti := textinput.New() + ti.Prompt = " > " + ti.PromptStyle = pagerNoteInputPromptStyle + ti.TextStyle = pagerNoteInputStyle + ti.CursorStyle = pagerNoteInputCursorStyle ti.CharLimit = noteCharacterLimit ti.Focus() // Text input for search - sp := spinner.NewModel() - sp.ForegroundColor = statusBarNoteFg.String() - sp.BackgroundColor = statusBarBg.String() - sp.HideFor = time.Millisecond * 50 - sp.MinimumLifetime = time.Millisecond * 180 + sp := spinner.New() + sp.Style = spinnerStyle return pagerModel{ common: common, @@ -251,11 +295,10 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { // Stash a local document if m.state != pagerStateStashing && stashableDocTypes.Contains(md.docType) { m.state = pagerStateStashing - m.spinner.Start() cmds = append( cmds, - stashDocument(m.common.cc, md), - spinner.Tick, + stashDocument(m.common.cc, m.common.cwd, md), + m.spinner.Tick, ) } case "?": @@ -270,8 +313,8 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { if m.state == pagerStateStashing || m.spinner.Visible() { // If we're still stashing, or if the spinner still needs to // finish, spin it along. - newSpinnerModel, cmd := m.spinner.Update(msg) - m.spinner = newSpinnerModel + var cmd tea.Cmd + m.spinner, cmd = m.spinner.Update(msg) cmds = append(cmds, cmd) } else if m.state == pagerStateStashSuccess && !m.spinner.Visible() { // If the spinner's finished and we haven't told the user the @@ -385,9 +428,7 @@ func (m pagerModel) statusBarView(b *strings.Builder) { // Status indicator; spinner or stash dot var statusIndicator string if m.state == pagerStateStashing || m.state == pagerStateStashSuccess { - if m.spinner.Visible() { - statusIndicator = statusBarNoteStyle(" ") + m.spinner.View() - } + statusIndicator = statusBarNoteStyle(" ") + m.spinner.View() } else if isStashed && showStatusMessage { statusIndicator = statusBarMessageStashIconStyle(" " + pagerStashIcon) } else if isStashed { diff --git a/ui/stash.go b/ui/stash.go index 6e85d58..0adce4c 100644 --- a/ui/stash.go +++ b/ui/stash.go @@ -13,11 +13,10 @@ import ( "github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/charm" - lib "github.com/charmbracelet/charm/ui/common" + "github.com/charmbracelet/glow/client" + "github.com/charmbracelet/lipgloss" "github.com/muesli/reflow/ansi" "github.com/muesli/reflow/truncate" - te "github.com/muesli/termenv" "github.com/sahilm/fuzzy" ) @@ -30,26 +29,39 @@ const ( ) var ( - stashedStatusMessage = statusMessage{normalStatusMessage, "Stashed!"} + stashingStatusMessage = statusMessage{normalStatusMessage, "Stashing..."} alreadyStashedStatusMessage = statusMessage{subtleStatusMessage, "Already stashed"} ) var ( - stashTextInputPromptStyle styleFunc = newFgStyle(lib.YellowGreen) - dividerDot string = darkGrayFg(" • ") - dividerBar string = darkGrayFg(" │ ") - offlineHeaderNote string = darkGrayFg("(Offline)") + dividerDot = darkGrayFg(" • ") + dividerBar = darkGrayFg(" │ ") + offlineHeaderNote = darkGrayFg("(Offline)") + + logoStyle = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#ECFD65")). + Background(fuschia). + Bold(true) + + stashSpinnerStyle = lipgloss.NewStyle(). + Foreground(gray) + stashInputPromptStyle = lipgloss.NewStyle(). + Foreground(yellowGreen). + MarginRight(1) + stashInputCursorStyle = lipgloss.NewStyle(). + Foreground(fuschia). + MarginRight(1) ) // MSG -type deletedStashedItemMsg int +type deletedStashedItemMsg string type filteredMarkdownMsg []*markdown type fetchedMarkdownMsg *markdown type markdownFetchFailedMsg struct { err error - id int + id string note string } @@ -241,6 +253,14 @@ func (m stashModel) online() bool { return !m.localOnly() && m.common.authStatus == authOK } +// Whether or not the spinner should be spinning. +func (m stashModel) shouldSpin() bool { + loading := !m.loadingDone() + stashing := m.common.isStashing() + openingDocument := m.viewState == stashStateLoadingDocument + return loading || stashing || openingDocument +} + func (m *stashModel) setSize(width, height int) { m.common.width = width m.common.height = height @@ -432,7 +452,7 @@ func (m *stashModel) openMarkdown(md *markdown) tea.Cmd { cmd = loadRemoteMarkdown(m.common.cc, md) } - return tea.Batch(cmd, spinner.Tick) + return tea.Batch(cmd, m.spinner.Tick) } func (m *stashModel) newStatusMessage(sm statusMessage) tea.Cmd { @@ -497,22 +517,21 @@ func (m *stashModel) moveCursorDown() { // INIT func newStashModel(common *commonModel) stashModel { - sp := spinner.NewModel() + sp := spinner.New() sp.Spinner = spinner.Line - sp.ForegroundColor = lib.SpinnerColor.String() - sp.HideFor = time.Millisecond * 100 - sp.MinimumLifetime = time.Millisecond * 180 - sp.Start() + sp.Style = stashSpinnerStyle - ni := textinput.NewModel() - ni.Prompt = stashTextInputPromptStyle("Memo: ") - ni.CursorColor = lib.Fuschia.String() + ni := textinput.New() + ni.Prompt = "Memo:" + ni.PromptStyle = stashInputPromptStyle + ni.CursorStyle = stashInputCursorStyle ni.CharLimit = noteCharacterLimit ni.Focus() - si := textinput.NewModel() - si.Prompt = stashTextInputPromptStyle("Find: ") - si.CursorColor = lib.Fuschia.String() + si := textinput.New() + si.Prompt = "Find:" + si.PromptStyle = stashInputPromptStyle + si.CursorStyle = stashInputCursorStyle si.CharLimit = noteCharacterLimit si.Focus() @@ -633,14 +652,9 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { return m, nil case spinner.TickMsg: - loading := !m.loadingDone() - stashing := m.common.isStashing() - openingDocument := m.viewState == stashStateLoadingDocument - spinnerVisible := m.spinner.Visible() - - if loading || stashing || openingDocument || spinnerVisible { - newSpinnerModel, cmd := m.spinner.Update(msg) - m.spinner = newSpinnerModel + if m.shouldSpin() { + var cmd tea.Cmd + m.spinner, cmd = m.spinner.Update(msg) cmds = append(cmds, cmd) } @@ -653,10 +667,9 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { } } - // Note: mechanical stuff related to stash success is handled in the parent - // update function. case stashSuccessMsg: - m.spinner.Finish() + // No-op: mechanical stuff related to stash success is handled in the + // parent update function. // Note: mechanical stuff related to stash failure is handled in the parent // update function. @@ -834,14 +847,13 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { break } - // Checks passed; perform the stash. Note that we optimistically - // show the status message. + // Checks passed; perform the stash. m.common.filesStashed[md.stashID] = struct{}{} m.common.filesStashing[md.stashID] = struct{}{} m.common.latestFileStashed = md.stashID cmds = append(cmds, - stashDocument(m.common.cc, *md), - m.newStatusMessage(stashedStatusMessage), + stashDocument(m.common.cc, m.common.cwd, *md), + m.newStatusMessage(stashingStatusMessage), ) // If we're stashing a filtered item, optimistically convert the @@ -856,12 +868,9 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { // The spinner subtly shows the stash state in a non-optimistic // fashion, namely because it was originally implemented this way. - // If this stash succeeds quickly enough, the spinner won't run - // at all. - if m.loadingDone() && !m.spinner.Visible() { - m.spinner.Start() - cmds = append(cmds, spinner.Tick) - } + // Ideally, if this stash succeeds quickly enough, the spinner + // wouldn't run at all. + cmds = append(cmds, m.spinner.Tick) // Prompt for deletion case "x": @@ -1089,7 +1098,7 @@ func (m *stashModel) handleNoteInput(msg tea.Msg) tea.Cmd { // If the user is issuing a rename on a newly stashed item in a // filtered listing, there's a small chance the user could try and // set a note before the stash is complete. - if md.ID == 0 { + if md.ID == "" { if debug { log.Printf("user attempted to rename, but markdown ID is 0: %v", md) } @@ -1132,7 +1141,7 @@ func (m stashModel) view() string { case stashStateReady: loadingIndicator := " " - if !m.loadingDone() || m.spinner.Visible() { + if m.shouldSpin() { loadingIndicator = m.spinner.View() } @@ -1195,7 +1204,7 @@ func (m stashModel) view() string { // pointers in our model should be refactored away. var p paginator.Model = *(m.paginator()) p.Type = paginator.Arabic - pagination = lib.Subtle(p.View()) + pagination = paginationStyle.Render(p.View()) } // We could also look at m.stashFullyLoaded and add an indicator @@ -1218,11 +1227,7 @@ func (m stashModel) view() string { } func glowLogoView(text string) string { - return te.String(text). - Bold(). - Foreground(glowLogoTextColor). - Background(lib.Fuschia.Color()). - String() + return logoStyle.Render(text) } func (m stashModel) headerView() string { @@ -1258,9 +1263,9 @@ func (m stashModel) headerView() string { } if m.stashedOnly() { - return lib.Subtle("Can’t load stash") + maybeOffline + return subtleStyle.Render("Can’t load stash") + maybeOffline } - return lib.Subtle("No markdown files found") + maybeOffline + return subtleStyle.Render("No markdown files found") + maybeOffline } // Tabs @@ -1288,9 +1293,9 @@ func (m stashModel) headerView() string { } if m.sectionIndex == i && len(m.sections) > 1 { - s = selectedTabColor(s) + s = selectedTabStyle.Render(s) } else { - s = tabColor(s) + s = tabStyle.Render(s) } sections = append(sections, s) } @@ -1374,12 +1379,12 @@ func (m stashModel) populatedView() string { // COMMANDS // loadRemoteMarkdown is a command for loading markdown from the server. -func loadRemoteMarkdown(cc *charm.Client, md *markdown) tea.Cmd { +func loadRemoteMarkdown(cc *client.Client, md *markdown) tea.Cmd { return func() tea.Msg { newMD, err := fetchMarkdown(cc, md.ID, md.docType) if err != nil { if debug { - log.Printf("error loading %s markdown (ID %d, Note: '%s'): %v", md.docType, md.ID, md.Note, err) + log.Printf("error loading %s markdown (ID %s, Note: '%s'): %v", md.docType, md.ID, md.Note, err) } return markdownFetchFailedMsg{ err: err, @@ -1413,7 +1418,7 @@ func loadLocalMarkdown(md *markdown) tea.Cmd { } } -func deleteStashedItem(cc *charm.Client, id int) tea.Cmd { +func deleteStashedItem(cc *client.Client, id string) tea.Cmd { return func() tea.Msg { err := cc.DeleteMarkdown(id) if err != nil { @@ -1454,8 +1459,8 @@ func filterMarkdowns(m stashModel) tea.Cmd { // ETC // fetchMarkdown performs the actual I/O for loading markdown from the sever. -func fetchMarkdown(cc *charm.Client, id int, t DocType) (*markdown, error) { - var md *charm.Markdown +func fetchMarkdown(cc *client.Client, id string, t DocType) (*markdown, error) { + var md *client.Markdown var err error switch t { diff --git a/ui/stashhelp.go b/ui/stashhelp.go index fea1fdf..a53dfca 100644 --- a/ui/stashhelp.go +++ b/ui/stashhelp.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - lib "github.com/charmbracelet/charm/ui/common" "github.com/muesli/reflow/ansi" ) @@ -195,7 +194,7 @@ func (m stashModel) miniHelpView(entries ...string) string { } var ( - truncationChar = lib.Subtle("…") + truncationChar = subtleStyle.Render("…") truncationWidth = ansi.PrintableRuneWidth(truncationChar) ) diff --git a/ui/stashitem.go b/ui/stashitem.go index 7c5005e..0206364 100644 --- a/ui/stashitem.go +++ b/ui/stashitem.go @@ -5,10 +5,9 @@ import ( "log" "strings" - lib "github.com/charmbracelet/charm/ui/common" + "github.com/charmbracelet/lipgloss" "github.com/muesli/reflow/ansi" "github.com/muesli/reflow/truncate" - "github.com/muesli/termenv" "github.com/sahilm/fuzzy" ) @@ -67,7 +66,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { date = dullYellowFg(date) default: if m.common.latestFileStashed == md.stashID && - m.statusMessage == stashedStatusMessage { + m.statusMessage == stashingStatusMessage { gutter = greenFg(verticalLine) icon = dimGreenFg(icon) title = greenFg(title) @@ -77,8 +76,8 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { icon = dullFuchsiaFg(icon) if m.currentSection().key == filterSection && m.filterState == filterApplied || singleFilteredItem { - s := termenv.Style{}.Foreground(lib.Fuschia.Color()) - title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline()) + s := lipgloss.NewStyle().Foreground(fuschia) + title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) } else { title = fuchsiaFg(title) } @@ -91,7 +90,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { gutter = " " if m.common.latestFileStashed == md.stashID && - m.statusMessage == stashedStatusMessage { + m.statusMessage == stashingStatusMessage { icon = dimGreenFg(icon) title = greenFg(title) date = semiDimGreenFg(date) @@ -100,8 +99,8 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { title = dimIndigoFg(title) date = dimSubtleIndigoFg(date) } else { - s := termenv.Style{}.Foreground(lib.Indigo.Color()) - title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline()) + s := lipgloss.NewStyle().Foreground(indigo) + title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) date = subtleIndigoFg(date) } } else if isFiltering && m.filterInput.Value() == "" { @@ -117,8 +116,8 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { if title == noMemoTitle { title = brightGrayFg(title) } else { - s := termenv.Style{}.Foreground(lib.NewColorPair("#dddddd", "#1a1a1a").Color()) - title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline()) + s := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"}) + title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) } date = brightGrayFg(date) } @@ -128,7 +127,7 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { fmt.Fprintf(b, "%s %s", gutter, date) } -func styleFilteredText(haystack, needles string, defaultStyle, matchedStyle termenv.Style) string { +func styleFilteredText(haystack, needles string, defaultStyle, matchedStyle lipgloss.Style) string { b := strings.Builder{} normalizedHay, err := normalize(haystack) @@ -138,7 +137,7 @@ func styleFilteredText(haystack, needles string, defaultStyle, matchedStyle term matches := fuzzy.Find(needles, []string{normalizedHay}) if len(matches) == 0 { - return defaultStyle.Styled(haystack) + return defaultStyle.Render(haystack) } m := matches[0] // only one match exists @@ -146,12 +145,12 @@ func styleFilteredText(haystack, needles string, defaultStyle, matchedStyle term styled := false for _, mi := range m.MatchedIndexes { if i == mi { - b.WriteString(matchedStyle.Styled(string(rune))) + b.WriteString(matchedStyle.Render(string(rune))) styled = true } } if !styled { - b.WriteString(defaultStyle.Styled(string(rune))) + b.WriteString(defaultStyle.Render(string(rune))) } } diff --git a/ui/styles.go b/ui/styles.go index ed527f4..fe7c0ff 100644 --- a/ui/styles.go +++ b/ui/styles.go @@ -1,63 +1,85 @@ package ui import ( - lib "github.com/charmbracelet/charm/ui/common" - te "github.com/muesli/termenv" + . "github.com/charmbracelet/lipgloss" ) -type styleFunc func(string) string +// Colors. +var ( + normal = AdaptiveColor{Light: "#1A1A1A", Dark: "#dddddd"} + normalDim = AdaptiveColor{Light: "#A49FA5", Dark: "#777777"} + gray = AdaptiveColor{Light: "#909090", Dark: "#626262"} + midGray = AdaptiveColor{Light: "#B2B2B2", Dark: "#4A4A4A"} + darkGray = AdaptiveColor{Light: "#DDDADA", Dark: "#3C3C3C"} + brightGray = AdaptiveColor{Light: "#847A85", Dark: "#979797"} + dimBrightGray = AdaptiveColor{Light: "#C2B8C2", Dark: "#4D4D4D"} + indigo = AdaptiveColor{Light: "#5A56E0", Dark: "#7571F9"} + dimIndigo = AdaptiveColor{Light: "#9498FF", Dark: "#494690"} + subtleIndigo = AdaptiveColor{Light: "#7D79F6", Dark: "#514DC1"} + dimSubtleIndigo = AdaptiveColor{Light: "#BBBDFF", Dark: "#383584"} + cream = AdaptiveColor{Light: "#FFFDF5", Dark: "#FFFDF5"} + yellowGreen = AdaptiveColor{Light: "#04B575", Dark: "#ECFD65"} + dullYellowGreen = AdaptiveColor{Light: "#6BCB94", Dark: "#9BA92F"} + fuschia = AdaptiveColor{Light: "#EE6FF8", Dark: "#EE6FF8"} + dimFuchsia = AdaptiveColor{Light: "#F1A8FF", Dark: "#99519E"} + dullFuchsia = AdaptiveColor{Dark: "#AD58B4", Light: "#F793FF"} + dimDullFuchsia = AdaptiveColor{Light: "#F6C9FF", Dark: "#6B3A6F"} + green = Color("#04B575") + red = AdaptiveColor{Light: "#FF4672", Dark: "#ED567A"} + faintRed = AdaptiveColor{Light: "#FF6F91", Dark: "#C74665"} -const ( - darkGray = "#333333" + semiDimGreen = AdaptiveColor{Light: "#35D79C", Dark: "#036B46"} + dimGreen = AdaptiveColor{Light: "#72D2B0", Dark: "#0B5137"} +) + +// Ulimately, we'll transition to named styles. +var ( + normalFg = NewStyle().Foreground(normal).Render + dimNormalFg = NewStyle().Foreground(normalDim).Render + + brightGrayFg = NewStyle().Foreground(brightGray).Render + dimBrightGrayFg = NewStyle().Foreground(dimBrightGray).Render + + grayFg = NewStyle().Foreground(gray).Render + midGrayFg = NewStyle().Foreground(midGray).Render + darkGrayFg = NewStyle().Foreground(darkGray).Render + + greenFg = NewStyle().Foreground(green).Render + semiDimGreenFg = NewStyle().Foreground(semiDimGreen).Render + dimGreenFg = NewStyle().Foreground(dimGreen).Render + + fuchsiaFg = NewStyle().Foreground(fuschia).Render + dimFuchsiaFg = NewStyle().Foreground(dimFuchsia).Render + + dullFuchsiaFg = NewStyle().Foreground(dullFuchsia).Render + dimDullFuchsiaFg = NewStyle().Foreground(dimDullFuchsia).Render + + indigoFg = NewStyle().Foreground(fuschia).Render + dimIndigoFg = NewStyle().Foreground(dimIndigo).Render + + subtleIndigoFg = NewStyle().Foreground(subtleIndigo).Render + dimSubtleIndigoFg = NewStyle().Foreground(dimSubtleIndigo).Render + + yellowFg = NewStyle().Foreground(yellowGreen).Render // renders light green on light backgrounds + dullYellowFg = NewStyle().Foreground(dullYellowGreen).Render // renders light green on light backgrounds + redFg = NewStyle().Foreground(red).Render + faintRedFg = NewStyle().Foreground(faintRed).Render ) var ( - normalFg = newFgStyle(lib.NewColorPair("#dddddd", "#1a1a1a")) - dimNormalFg = newFgStyle(lib.NewColorPair("#777777", "#A49FA5")) + tabStyle = NewStyle(). + Foreground(AdaptiveColor{Light: "#909090", Dark: "#626262"}) - brightGrayFg = newFgStyle(lib.NewColorPair("#979797", "#847A85")) - dimBrightGrayFg = newFgStyle(lib.NewColorPair("#4D4D4D", "#C2B8C2")) + selectedTabStyle = NewStyle(). + Foreground(AdaptiveColor{Light: "#333333", Dark: "#979797"}) - grayFg = newFgStyle(lib.NewColorPair("#626262", "#909090")) - midGrayFg = newFgStyle(lib.NewColorPair("#4A4A4A", "#B2B2B2")) - darkGrayFg = newFgStyle(lib.NewColorPair("#3C3C3C", "#DDDADA")) + errorTitleStyle = NewStyle(). + Foreground(cream). + Background(red). + Padding(0, 1) - greenFg = newFgStyle(lib.NewColorPair("#04B575", "#04B575")) - semiDimGreenFg = newFgStyle(lib.NewColorPair("#036B46", "#35D79C")) - dimGreenFg = newFgStyle(lib.NewColorPair("#0B5137", "#72D2B0")) + subtleStyle = NewStyle(). + Foreground(AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"}) - fuchsiaFg = newFgStyle(lib.Fuschia) - dimFuchsiaFg = newFgStyle(lib.NewColorPair("#99519E", "#F1A8FF")) - - dullFuchsiaFg = newFgStyle(lib.NewColorPair("#AD58B4", "#F793FF")) - dimDullFuchsiaFg = newFgStyle(lib.NewColorPair("#6B3A6F", "#F6C9FF")) - - indigoFg = newFgStyle(lib.Indigo) - dimIndigoFg = newFgStyle(lib.NewColorPair("#494690", "#9498FF")) - - subtleIndigoFg = newFgStyle(lib.NewColorPair("#514DC1", "#7D79F6")) - dimSubtleIndigoFg = newFgStyle(lib.NewColorPair("#383584", "#BBBDFF")) - - yellowFg = newFgStyle(lib.YellowGreen) // renders light green on light backgrounds - dullYellowFg = newFgStyle(lib.NewColorPair("#9BA92F", "#6BCB94")) // renders light green on light backgrounds - redFg = newFgStyle(lib.Red) - faintRedFg = newFgStyle(lib.FaintRed) - - // Ultimately, we should transition to named styles - tabColor = newFgStyle(lib.NewColorPair("#626262", "#909090")) - selectedTabColor = newFgStyle(lib.NewColorPair("#979797", "#332F33")) + paginationStyle = subtleStyle.Copy() ) - -// Returns a termenv style with foreground and background options. -func newStyle(fg, bg lib.ColorPair, bold bool) func(string) string { - s := te.Style{}.Foreground(fg.Color()).Background(bg.Color()) - if bold { - s = s.Bold() - } - return s.Styled -} - -// Returns a new termenv style with background options only. -func newFgStyle(c lib.ColorPair) styleFunc { - return te.Style{}.Foreground(c.Color()).Styled -} diff --git a/ui/ui.go b/ui/ui.go index 5a15f7c..26a699c 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -10,12 +10,10 @@ import ( "strings" "time" - "github.com/charmbracelet/bubbles/spinner" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/charm" - "github.com/charmbracelet/charm/keygen" - "github.com/charmbracelet/charm/ui/common" - lib "github.com/charmbracelet/charm/ui/common" + charm "github.com/charmbracelet/charm/proto" + "github.com/charmbracelet/charm/ui/keygen" + "github.com/charmbracelet/glow/client" "github.com/charmbracelet/glow/utils" "github.com/muesli/gitcha" te "github.com/muesli/termenv" @@ -29,8 +27,7 @@ const ( ) var ( - config Config - glowLogoTextColor = lib.Color("#ECFD65") + config Config markdownExtensions = []string{ "*.md", "*.mdown", "*.mkdn", "*.mkd", "*.markdown", @@ -53,26 +50,30 @@ func NewProgram(cfg Config) *tea.Program { debug = true } config = cfg - return tea.NewProgram(newModel(cfg)) + + opts := []tea.ProgramOption{tea.WithAltScreen()} + if cfg.EnableMouse { + opts = append(opts, tea.WithMouseCellMotion()) + } + + return tea.NewProgram(newModel(cfg), opts...) } type errMsg struct{ err error } func (e errMsg) Error() string { return e.err.Error() } -type newCharmClientMsg *charm.Client +type newCharmClientMsg *client.Client type sshAuthErrMsg struct{} -type keygenFailedMsg struct{ err error } -type keygenSuccessMsg struct{} type initLocalFileSearchMsg struct { cwd string ch chan gitcha.SearchResult } type foundLocalFileMsg gitcha.SearchResult type localFileSearchFinished struct{} -type gotStashMsg []*charm.Markdown +type gotStashMsg []*client.Markdown type stashLoadErrMsg struct{ err error } -type gotNewsMsg []*charm.Markdown +type gotNewsMsg []*client.Markdown type statusMessageTimeoutMsg applicationContext type newsLoadErrMsg struct{ err error } type stashSuccessMsg markdown @@ -132,7 +133,7 @@ const ( // Common stuff we'll need to access in all models. type commonModel struct { cfg Config - cc *charm.Client + cc *client.Client cwd string authStatus authStatus width int @@ -182,8 +183,8 @@ func (m *model) unloadDocument() []tea.Cmd { batch = append(batch, tea.ClearScrollArea) } - if !m.stash.loadingDone() { - batch = append(batch, spinner.Tick) + if !m.stash.shouldSpin() { + batch = append(batch, m.stash.spinner.Tick) } return batch } @@ -224,7 +225,7 @@ func (m model) Init() tea.Cmd { if d.Contains(StashedDoc) || d.Contains(NewsDoc) { cmds = append(cmds, newCharmClient, - spinner.Tick, + m.stash.spinner.Tick, ) } @@ -306,7 +307,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case sshAuthErrMsg: if m.keygenState != keygenFinished { // if we haven't run the keygen yet, do that m.keygenState = keygenRunning - cmds = append(cmds, generateSSHKeys) + cmds = append(cmds, keygen.GenerateKeys(m.common.cfg.CharmHost)) } else { // The keygen ran but things still didn't work and we can't auth m.common.authStatus = authFailed @@ -319,7 +320,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.stash.loaded.Add(StashedDoc, NewsDoc) } - case keygenFailedMsg: + case keygen.FailedMsg: // Keygen failed. That sucks. m.common.authStatus = authFailed m.stash.err = errors.New("could not authenticate; could not generate SSH keys") @@ -332,7 +333,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // Even though it failed, news/stash loading is finished m.stash.loaded.Add(StashedDoc, NewsDoc) - case keygenSuccessMsg: + case keygen.SuccessMsg: // The keygen's done, so let's try initializing the charm client again m.keygenState = keygenFinished cmds = append(cmds, newCharmClient) @@ -455,12 +456,9 @@ func errorView(err error, fatal bool) string { exitMsg += "return" } s := fmt.Sprintf("%s\n\n%v\n\n%s", - te.String(" ERROR "). - Foreground(lib.Cream.Color()). - Background(lib.Red.Color()). - String(), + errorTitleStyle.Render("ERROR"), err, - common.Subtle(exitMsg), + subtleStyle.Render(exitMsg), ) return "\n" + indent(s, 3) } @@ -530,12 +528,7 @@ func findNextLocalFile(m model) tea.Cmd { } func newCharmClient() tea.Msg { - cfg, err := charm.ConfigFromEnv() - if err != nil { - return errMsg{err} - } - - cc, err := charm.NewClient(cfg) + cc, err := client.NewClient() if err == charm.ErrMissingSSHAuth { if debug { log.Println("missing SSH auth:", err) @@ -601,24 +594,7 @@ func loadNews(m stashModel) tea.Cmd { } } -func generateSSHKeys() tea.Msg { - if debug { - log.Println("running keygen...") - } - _, err := keygen.NewSSHKeyPair(nil) - if err != nil { - if debug { - log.Println("keygen failed:", err) - } - return keygenFailedMsg{err} - } - if debug { - log.Println("keys generated succcessfully") - } - return keygenSuccessMsg{} -} - -func saveDocumentNote(cc *charm.Client, id int, note string) tea.Cmd { +func saveDocumentNote(cc *client.Client, id string, note string) tea.Cmd { if cc == nil { return func() tea.Msg { err := errors.New("can't set note; no charm client") @@ -635,11 +611,11 @@ func saveDocumentNote(cc *charm.Client, id int, note string) tea.Cmd { } return errMsg{err} } - return noteSavedMsg(&charm.Markdown{ID: id, Note: note}) + return noteSavedMsg(&client.Markdown{ID: id, Note: note}) } } -func stashDocument(cc *charm.Client, md markdown) tea.Cmd { +func stashDocument(cc *client.Client, cwd string, md markdown) tea.Cmd { return func() tea.Msg { if cc == nil { err := errors.New("can't stash; no charm client") @@ -684,7 +660,8 @@ func stashDocument(cc *charm.Client, md markdown) tea.Cmd { } } - newMd, err := cc.StashMarkdown(md.Note, md.Body) + memo := stripAbsolutePath(md.Note, cwd) + newMd, err := cc.StashMarkdown(memo, md.Body) if err != nil { if debug { log.Println("error stashing document:", err) @@ -719,7 +696,7 @@ func localFileToMarkdown(cwd string, res gitcha.SearchResult) *markdown { md := &markdown{ docType: LocalDoc, localPath: res.Path, - Markdown: charm.Markdown{ + Markdown: client.Markdown{ Note: stripAbsolutePath(res.Path, cwd), CreatedAt: res.Info.ModTime(), },