Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,4 @@ jobs:
run: go mod download

- name: Run tests
run: go test


run: go test -race
6 changes: 3 additions & 3 deletions examples/custom/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func main() {
// sleep for 3 seconds then return nil
Task: func(t *taskin.Task) error {
for i := 0; i < 2; i++ {
t.Title = fmt.Sprintf("Task 1 - [%d/%d]", i+1, 2)
t.SetTitle(fmt.Sprintf("Task 1 - [%d/%d]", i+1, 2))
time.Sleep(1 * time.Second)
}
return nil
Expand All @@ -28,7 +28,7 @@ func main() {
Task: func(t *taskin.Task) error {
for i := 0; i < 5; i++ {
t.Progress(i+1, 5)
t.Title = fmt.Sprintf("Task 2 - [%d/%d]", i+1, 5)
t.SetTitle(fmt.Sprintf("Task 2 - [%d/%d]", i+1, 5))
time.Sleep(1 * time.Second)
}
return nil
Expand All @@ -39,7 +39,7 @@ func main() {
// sleep for 3 seconds then return nil
Task: func(t *taskin.Task) error {
for i := 0; i < 2; i++ {
t.Title = fmt.Sprintf("Task 3 - [%d/%d]", i+1, 2)
t.SetTitle(fmt.Sprintf("Task 3 - [%d/%d]", i+1, 2))
time.Sleep(1 * time.Second)
}
return nil
Expand Down
6 changes: 3 additions & 3 deletions examples/disable-ui/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func main() {
Title: "Task with UI disabled",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Task with UI disabled: [%d/3] processing", i+1)
t.SetTitle(fmt.Sprintf("Task with UI disabled: [%d/3] processing", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand All @@ -26,7 +26,7 @@ func main() {
Title: "Child task 1",
Task: func(t *taskin.Task) error {
for i := 0; i < 2; i++ {
t.Title = fmt.Sprintf("Child task 1: [%d/2] working", i+1)
t.SetTitle(fmt.Sprintf("Child task 1: [%d/2] working", i+1))
time.Sleep(300 * time.Millisecond)
}
return nil
Expand All @@ -36,7 +36,7 @@ func main() {
Title: "Child task 2",
Task: func(t *taskin.Task) error {
for i := 0; i < 2; i++ {
t.Title = fmt.Sprintf("Child task 2: [%d/2] working", i+1)
t.SetTitle(fmt.Sprintf("Child task 2: [%d/2] working", i+1))
time.Sleep(300 * time.Millisecond)
}
return nil
Expand Down
4 changes: 2 additions & 2 deletions examples/error/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func main() {
Title: "Task 1",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Task 1: [%d/3] seconds have passed", i+1)
t.SetTitle(fmt.Sprintf("Task 1: [%d/3] seconds have passed", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand All @@ -29,7 +29,7 @@ func main() {
Title: "Task 3",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Task 3: [%d/3] seconds have passed", i+1)
t.SetTitle(fmt.Sprintf("Task 3: [%d/3] seconds have passed", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand Down
12 changes: 6 additions & 6 deletions examples/multi/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func main() {
Title: "Mow the lawn",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Mow the lawn: [%d/3] passes", i+1)
t.SetTitle(fmt.Sprintf("Mow the lawn: [%d/3] passes", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand All @@ -26,7 +26,7 @@ func main() {
Title: "Pluck the silkies [0/3]",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Pluck the silkies [%d/3]", i+1)
t.SetTitle(fmt.Sprintf("Pluck the silkies [%d/3]", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand All @@ -40,7 +40,7 @@ func main() {
Title: "[0/3] Pluck the Polish",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("[%d/3] Pluck the Polish", i+1)
t.SetTitle(fmt.Sprintf("[%d/3] Pluck the Polish", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand All @@ -50,7 +50,7 @@ func main() {
Title: "[0/3] Pluck the Marans",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("[%d/3] Pluck the Marans", i+1)
t.SetTitle(fmt.Sprintf("[%d/3] Pluck the Marans", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand All @@ -63,7 +63,7 @@ func main() {
Title: "[0/3] Pluck the leghorns",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("[%d/3] Pluck the Leghorns", i+1)
t.SetTitle(fmt.Sprintf("[%d/3] Pluck the Leghorns", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand All @@ -76,7 +76,7 @@ func main() {
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Progress(i+1, 3)
t.Title = fmt.Sprintf("Paint the house: [%d/3] walls painted", i+1)
t.SetTitle(fmt.Sprintf("Paint the house: [%d/3] walls painted", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand Down
2 changes: 1 addition & 1 deletion examples/progress/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func main() {
Task: func(t *taskin.Task) error {
for i := 0; i < 5; i++ {
t.Progress(i+1, 5)
t.Title = fmt.Sprintf("Progress [%d/%d]", i+1, 5)
t.SetTitle(fmt.Sprintf("Progress [%d/%d]", i+1, 5))
time.Sleep(1 * time.Second)
}
return nil
Expand Down
4 changes: 2 additions & 2 deletions examples/simple/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ func main() {
Title: "Task 1",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Task 1: [%d/3] seconds have passed", i+1)
t.SetTitle(fmt.Sprintf("Task 1: [%d/3] seconds have passed", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand All @@ -23,7 +23,7 @@ func main() {
Title: "Task 2",
Task: func(t *taskin.Task) error {
for i := 0; i < 3; i++ {
t.Title = fmt.Sprintf("Task 2: [%d/3] seconds have passed", i+1)
t.SetTitle(fmt.Sprintf("Task 2: [%d/3] seconds have passed", i+1))
time.Sleep(500 * time.Millisecond)
}
return nil
Expand Down
21 changes: 21 additions & 0 deletions models.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,32 @@ package taskin
import (
"github.com/charmbracelet/bubbles/progress"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
)

type TerminateWithError struct {
Error error
}

type taskStartedMsg struct {
Path []int
}

type taskUpdatedMsg struct {
Path []int
Task Task
}

type taskCompletedMsg struct {
Path []int
Task Task
}

type taskFailedMsg struct {
Path []int
Task Task
}

type TaskState int

const (
Expand Down Expand Up @@ -50,4 +70,5 @@ type Model struct {
HideView bool
Shutdown bool
ShutdownError error
taskMessages chan tea.Msg
}
78 changes: 63 additions & 15 deletions mvc.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,34 @@ func (m *Model) Init() tea.Cmd {
}
}
}
if m.taskMessages != nil {
if len(m.Runners) == 0 {
return tea.Quit
}
cmds = append(cmds, runTasksCmd(cloneRunners(m.Runners), m.taskMessages), waitForTaskMessage(m.taskMessages))
}
return tea.Batch(cmds...)
}

func waitForTaskMessage(messages <-chan tea.Msg) tea.Cmd {
return func() tea.Msg {
return <-messages
}
}

func (m *Model) waitForTaskMessage() tea.Cmd {
if m.taskMessages == nil {
return nil
}
return waitForTaskMessage(m.taskMessages)
}

func (m *Model) SetShutdown(err error) {
m.Shutdown = true
m.ShutdownError = err
}

func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmds []tea.Cmd

if m.Shutdown && m.ShutdownError != nil {
return m, tea.Quit
}
Expand All @@ -38,9 +55,36 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case TerminateWithError:
m.SetShutdown(msg.Error)
return m, tea.Quit
case taskStartedMsg:
if runner := m.runnerAtPath(msg.Path); runner != nil {
runner.State = Running
}
return m, m.waitForTaskMessage()
case taskUpdatedMsg:
if runner := m.runnerAtPath(msg.Path); runner != nil {
runner.Task = msg.Task
}
return m, m.waitForTaskMessage()
case taskCompletedMsg:
if runner := m.runnerAtPath(msg.Path); runner != nil {
runner.Task = msg.Task
runner.State = Completed
}
allDone, anyFailed := m.checkTasksState()
if allDone && !anyFailed {
return m, tea.Quit
}
return m, m.waitForTaskMessage()
case taskFailedMsg:
if runner := m.runnerAtPath(msg.Path); runner != nil {
runner.Task = msg.Task
runner.State = Failed
}
return m, m.waitForTaskMessage()

case spinner.TickMsg:
// Helper function to update spinners recursively
var cmds []tea.Cmd

var updateSpinners func(runner *Runner) []tea.Cmd
updateSpinners = func(runner *Runner) []tea.Cmd {
var spinnerCmds []tea.Cmd
Expand All @@ -53,28 +97,19 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}

// Recursively update all children's spinners
for i := range runner.Children {
spinnerCmds = append(spinnerCmds, updateSpinners(&runner.Children[i])...)
}

return spinnerCmds
}

allDone := true
for i := range m.Runners {
cmds = append(cmds, updateSpinners(&m.Runners[i])...)

if m.Runners[i].State == Failed {
return m, tea.Quit
}

if m.Runners[i].State != Completed && m.Runners[i].State != Failed {
allDone = false
}
}

if allDone {
allDone, anyFailed := m.checkTasksState()
if allDone && !anyFailed {
return m, tea.Quit
}

Expand All @@ -84,6 +119,20 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}

func (m *Model) runnerAtPath(path []int) *Runner {
if len(path) == 0 || path[0] < 0 || path[0] >= len(m.Runners) {
return nil
}
runner := &m.Runners[path[0]]
for _, index := range path[1:] {
if index < 0 || index >= len(runner.Children) {
return nil
}
runner = &runner.Children[index]
}
return runner
}

func (m *Model) checkTasksState() (allDone, anyFailed bool) {
allDone = true
for _, runner := range m.Runners {
Expand Down Expand Up @@ -148,7 +197,6 @@ func renderTask(runner Runner, indent string) string {
view = indent + lipgloss.NewStyle().Render(status) + "\n"
}

// Recursively render children
if len(runner.Children) > 0 && (runner.State == Running || IsCI() || runner.Config.DisableUI) {
for _, child := range runner.Children {
view += renderTask(child, indent+" ")
Expand Down
12 changes: 6 additions & 6 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,21 @@ https://github.com/fumeapp/taskin/blob/3cd766c21e5eaba5edb33f38d3781d6cf814f9f9/
The `*taskin.Task` struct passeed into your task has some useful properties that you can use to customize the task view.

### Change the title
Already demonstrated in most of the examples, you can change `t.Title` at any time
Already demonstrated in most of the examples, you can change the title with `t.SetTitle("New title")`.

### Hide a view
Sometimes you might need to temporarily hide you task view in order to prompt a user for input.
You can do this by toggling the task.HideView boolean.
You can do this by calling `t.SetHideView(true)` and showing it again with `t.SetHideView(false)`.

```go
Task: func(T *taskin.Task ) error {
t.HideView = true
t.SetHideView(true)
if err := PromptForInput(); err != nil {
t.HideView = false
t.SetHideView(false)
return err
}
t.HideView = false
t.Title = "Input received"
t.SetHideView(false)
t.SetTitle("Input received")
return nil
}

Expand Down
Loading
Loading