-
Notifications
You must be signed in to change notification settings - Fork 853
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: print unmanaged output above the application (#249)
* merge Adjective-Object/tea_log_renderer into standard renderer * rename queuedMessages -> queuedMessageLines & break apart strings during message processing * delete cursorDownBy * += 1 -> ++ to make the linter happy * add skipLines[] tracking back to standard renderer, and add rename skippedLines local to jumpedLines to clarify they are separate comments * request repaint when a message is recieved * Convert Println and Printf to commands * Add package manager example demonstrating tea.Printf * Use Unix instead of UnixMicro for Go 1.13 support in CI * fix off by one in std renderer * add Printf/Println to tea.go * revert attempt at sequence compression + cursorUpBy Co-authored-by: Maxwell Huang-Hobbs <[email protected]> Co-authored-by: Christian Rocha <[email protected]>
- Loading branch information
1 parent
a2d0ac9
commit ebabec7
Showing
4 changed files
with
289 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
"os" | ||
"strings" | ||
"time" | ||
|
||
"github.com/charmbracelet/bubbles/progress" | ||
"github.com/charmbracelet/bubbles/spinner" | ||
tea "github.com/charmbracelet/bubbletea" | ||
"github.com/charmbracelet/lipgloss" | ||
) | ||
|
||
type model struct { | ||
packages []string | ||
index int | ||
width int | ||
height int | ||
spinner spinner.Model | ||
progress progress.Model | ||
done bool | ||
} | ||
|
||
var ( | ||
currentPkgNameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("211")) | ||
subtleStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("239")) | ||
doneStyle = lipgloss.NewStyle().Margin(1, 2) | ||
checkMark = lipgloss.NewStyle().Foreground(lipgloss.Color("42")).SetString("✓") | ||
) | ||
|
||
func newModel() model { | ||
p := progress.New( | ||
progress.WithDefaultGradient(), | ||
progress.WithWidth(40), | ||
progress.WithoutPercentage(), | ||
) | ||
s := spinner.New() | ||
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("63")) | ||
return model{ | ||
packages: getPackages(), | ||
spinner: s, | ||
progress: p, | ||
} | ||
} | ||
|
||
func (m model) Init() tea.Cmd { | ||
return tea.Batch(downloadAndInstall(m.packages[m.index]), m.spinner.Tick) | ||
} | ||
|
||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { | ||
switch msg := msg.(type) { | ||
case tea.WindowSizeMsg: | ||
m.width, m.height = msg.Width, msg.Height | ||
case tea.KeyMsg: | ||
switch msg.String() { | ||
case "ctrl+c", "esc", "q": | ||
return m, tea.Quit | ||
} | ||
case installedPkgMsg: | ||
if m.index >= len(m.packages)-1 { | ||
// Everything's been installed. We're done! | ||
m.done = true | ||
return m, tea.Quit | ||
} | ||
|
||
// Update progress bar | ||
progressCmd := m.progress.SetPercent(float64(m.index) / float64(len(m.packages)-1)) | ||
|
||
m.index++ | ||
return m, tea.Batch( | ||
progressCmd, | ||
tea.Printf("%s %s", checkMark, m.packages[m.index]), // print success message above our program | ||
downloadAndInstall(m.packages[m.index]), // download the next package | ||
) | ||
case spinner.TickMsg: | ||
var cmd tea.Cmd | ||
m.spinner, cmd = m.spinner.Update(msg) | ||
return m, cmd | ||
case progress.FrameMsg: | ||
newModel, cmd := m.progress.Update(msg) | ||
if newModel, ok := newModel.(progress.Model); ok { | ||
m.progress = newModel | ||
} | ||
return m, cmd | ||
} | ||
return m, nil | ||
} | ||
|
||
func (m model) View() string { | ||
n := len(m.packages) | ||
w := lipgloss.Width(fmt.Sprintf("%d", n)) | ||
|
||
if m.done { | ||
return doneStyle.Render(fmt.Sprintf("Done! Installed %d packages.\n", n)) | ||
} | ||
|
||
pkgCount := fmt.Sprintf(" %*d/%*d", w, m.index, w, n-1) | ||
|
||
spin := m.spinner.View() + " " | ||
prog := m.progress.View() | ||
cellsAvail := max(0, m.width-lipgloss.Width(spin+prog+pkgCount)) | ||
|
||
pkgName := currentPkgNameStyle.Render(m.packages[m.index]) | ||
info := lipgloss.NewStyle().MaxWidth(cellsAvail).Render("Installing " + pkgName) | ||
|
||
cellsRemaining := max(0, m.width-lipgloss.Width(spin+info+prog+pkgCount)) | ||
gap := strings.Repeat(" ", cellsRemaining) | ||
|
||
return spin + info + gap + prog + pkgCount | ||
} | ||
|
||
type installedPkgMsg string | ||
|
||
func downloadAndInstall(pkg string) tea.Cmd { | ||
// This is where you'd do i/o stuff to download and install packages. In | ||
// our case we're just pausing for a moment to simulate the process. | ||
d := time.Millisecond * time.Duration(rand.Intn(500)) | ||
return tea.Tick(d, func(t time.Time) tea.Msg { | ||
return installedPkgMsg(pkg) | ||
}) | ||
} | ||
|
||
func max(a, b int) int { | ||
if a > b { | ||
return a | ||
} | ||
return b | ||
} | ||
|
||
func main() { | ||
rand.Seed(time.Now().Unix()) | ||
|
||
if err := tea.NewProgram(newModel()).Start(); err != nil { | ||
fmt.Println("Error running program:", err) | ||
os.Exit(1) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"math/rand" | ||
) | ||
|
||
var packages = []string{ | ||
"vegeutils", | ||
"libgardening", | ||
"currykit", | ||
"spicerack", | ||
"fullenglish", | ||
"eggy", | ||
"bad-kitty", | ||
"chai", | ||
"hojicha", | ||
"libtacos", | ||
"babys-monads", | ||
"libpurring", | ||
"currywurst-devel", | ||
"xmodmeow", | ||
"licorice-utils", | ||
"cashew-apple", | ||
"rock-lobster", | ||
"standmixer", | ||
"coffee-CUPS", | ||
"libesszet", | ||
"zeichenorientierte-benutzerschnittstellen", | ||
"schnurrkit", | ||
"old-socks-devel", | ||
"jalapeño", | ||
"molasses-utils", | ||
"xkohlrabi", | ||
"party-gherkin", | ||
"snow-peas", | ||
"libyuzu", | ||
} | ||
|
||
func getPackages() []string { | ||
pkgs := packages | ||
copy(pkgs, packages) | ||
|
||
rand.Shuffle(len(pkgs), func(i, j int) { | ||
pkgs[i], pkgs[j] = pkgs[j], pkgs[i] | ||
}) | ||
|
||
for k := range pkgs { | ||
pkgs[k] += fmt.Sprintf("-%d.%d.%d", rand.Intn(10), rand.Intn(10), rand.Intn(10)) | ||
} | ||
return pkgs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters