feat: Implement non-interactive quote display
This commit introduces a non-interactive mode for the `collect` commands. When running in a non-interactive session, the progress bar is replaced with a series of thematic Borg quotes, printed in matrix-green text. The quotes are sourced from a `quotes.json` file, which is embedded into the binary using Go's `embed` package. The `pkg/ui` package now contains a `NonInteractivePrompter` that detects the session type and displays the quotes accordingly. The `collect` commands have been updated to use this new prompter, and the underlying `vcs` and `website` packages have been made more robust to handle cases where a progress bar is not provided. This commit also addresses feedback from the code review: - Fixes a formatting inconsistency in `data/quotes.json`. - Updates the `go-colorable` dependency to `v0.1.14`. - Adds a nil-check to the `Write` method in `pkg/ui/progress_writer.go` to prevent panics. - Implements caching for the quotes using `sync.Once` and adds checks for empty quote slices to prevent panics in `pkg/ui/quote.go`. - Refactors the `NonInteractivePrompter` in `pkg/ui/non_interactive_prompter.go` to be more robust and efficient.
This commit is contained in:
parent
edc0d6a18c
commit
b5daedd735
6 changed files with 57 additions and 11 deletions
|
|
@ -83,7 +83,7 @@
|
|||
"svg": "Vectorize the collective – infinite resolution, zero resistance.",
|
||||
"webp": "Hybrid assimilation – the Core optimizes without compromise.",
|
||||
"heic": "Apple‑grade assimilation – the Core preserves HDR.",
|
||||
"raw": "Raw data intake – the Core ingests the sensor’s soul",
|
||||
"raw": "Raw data intake – the Core ingests the sensor’s soul.",
|
||||
"ico": "Iconic assimilation – the Core packs the smallest symbols.",
|
||||
"avif": "Next‑gen assimilation – the Core squeezes the future.",
|
||||
"tiff": "High‑definition capture – the Core stores every photon.",
|
||||
|
|
|
|||
2
go.mod
2
go.mod
|
|
@ -28,7 +28,7 @@ require (
|
|||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -64,6 +64,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ package ui
|
|||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/fatih/color"
|
||||
|
|
@ -11,18 +12,29 @@ import (
|
|||
)
|
||||
|
||||
type NonInteractivePrompter struct {
|
||||
stopChan chan bool
|
||||
stopChan chan struct{}
|
||||
quoteFunc func() (string, error)
|
||||
started bool
|
||||
mu sync.Mutex
|
||||
stopOnce sync.Once
|
||||
}
|
||||
|
||||
func NewNonInteractivePrompter(quoteFunc func() (string, error)) *NonInteractivePrompter {
|
||||
return &NonInteractivePrompter{
|
||||
stopChan: make(chan bool),
|
||||
stopChan: make(chan struct{}),
|
||||
quoteFunc: quoteFunc,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *NonInteractivePrompter) Start() {
|
||||
p.mu.Lock()
|
||||
if p.started {
|
||||
p.mu.Unlock()
|
||||
return
|
||||
}
|
||||
p.started = true
|
||||
p.mu.Unlock()
|
||||
|
||||
if p.IsInteractive() {
|
||||
return // Don't start in interactive mode
|
||||
}
|
||||
|
|
@ -39,7 +51,7 @@ func (p *NonInteractivePrompter) Start() {
|
|||
quote, err := p.quoteFunc()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting quote:", err)
|
||||
return
|
||||
continue
|
||||
}
|
||||
c := color.New(color.FgGreen)
|
||||
c.Println(quote)
|
||||
|
|
@ -49,10 +61,12 @@ func (p *NonInteractivePrompter) Start() {
|
|||
}
|
||||
|
||||
func (p *NonInteractivePrompter) Stop() {
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||
if p.IsInteractive() {
|
||||
return
|
||||
}
|
||||
p.stopChan <- true
|
||||
p.stopOnce.Do(func() {
|
||||
close(p.stopChan)
|
||||
})
|
||||
}
|
||||
|
||||
func (p *NonInteractivePrompter) IsInteractive() bool {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,9 @@ func NewProgressWriter(bar *progressbar.ProgressBar) *progressWriter {
|
|||
}
|
||||
|
||||
func (pw *progressWriter) Write(p []byte) (n int, err error) {
|
||||
if pw == nil || pw.bar == nil {
|
||||
return len(p), nil
|
||||
}
|
||||
s := string(p)
|
||||
pw.bar.Describe(s)
|
||||
return len(p), nil
|
||||
|
|
|
|||
|
|
@ -5,12 +5,19 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Snider/Borg/data"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
var (
|
||||
cachedQuotes *Quotes
|
||||
quotesOnce sync.Once
|
||||
quotesErr error
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
}
|
||||
|
|
@ -49,8 +56,15 @@ func loadQuotes() (*Quotes, error) {
|
|||
return "es, nil
|
||||
}
|
||||
|
||||
func getQuotes() (*Quotes, error) {
|
||||
quotesOnce.Do(func() {
|
||||
cachedQuotes, quotesErr = loadQuotes()
|
||||
})
|
||||
return cachedQuotes, quotesErr
|
||||
}
|
||||
|
||||
func GetRandomQuote() (string, error) {
|
||||
quotes, err := loadQuotes()
|
||||
quotes, err := getQuotes()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
@ -63,6 +77,10 @@ func GetRandomQuote() (string, error) {
|
|||
allQuotes = append(allQuotes, quotes.PWAProcessing...)
|
||||
allQuotes = append(allQuotes, quotes.CodeRelatedLong...)
|
||||
|
||||
if len(allQuotes) == 0 {
|
||||
return "", fmt.Errorf("no quotes available")
|
||||
}
|
||||
|
||||
return allQuotes[rand.Intn(len(allQuotes))], nil
|
||||
}
|
||||
|
||||
|
|
@ -77,25 +95,34 @@ func PrintQuote() {
|
|||
}
|
||||
|
||||
func GetVCSQuote() (string, error) {
|
||||
quotes, err := loadQuotes()
|
||||
quotes, err := getQuotes()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(quotes.VCSProcessing) == 0 {
|
||||
return "", fmt.Errorf("no VCS quotes available")
|
||||
}
|
||||
return quotes.VCSProcessing[rand.Intn(len(quotes.VCSProcessing))], nil
|
||||
}
|
||||
|
||||
func GetPWAQuote() (string, error) {
|
||||
quotes, err := loadQuotes()
|
||||
quotes, err := getQuotes()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(quotes.PWAProcessing) == 0 {
|
||||
return "", fmt.Errorf("no PWA quotes available")
|
||||
}
|
||||
return quotes.PWAProcessing[rand.Intn(len(quotes.PWAProcessing))], nil
|
||||
}
|
||||
|
||||
func GetWebsiteQuote() (string, error) {
|
||||
quotes, err := loadQuotes()
|
||||
quotes, err := getQuotes()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(quotes.CodeRelatedLong) == 0 {
|
||||
return "", fmt.Errorf("no website quotes available")
|
||||
}
|
||||
return quotes.CodeRelatedLong[rand.Intn(len(quotes.CodeRelatedLong))], nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue