Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(v2) Generic model #1298

Open
wants to merge 27 commits into
base: v2-exp
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3d0d20f
chore: update examples
aymanbagabas Nov 25, 2024
49fdcf4
feat: program generics
aymanbagabas Nov 25, 2024
6c36267
fix: use raw output
aymanbagabas Nov 25, 2024
4469b62
fix: initialize Program fields in init method
aymanbagabas Nov 25, 2024
0423caa
refactor!: remove program options
aymanbagabas Jan 27, 2025
6741b5d
docs(examples): update views example to use functional-style program
meowgorithm Jan 27, 2025
6fe44ff
fix: remove stray log call
meowgorithm Jan 27, 2025
28c1ad3
docs(examples): update simple example to use the generic functional s…
meowgorithm Jan 27, 2025
0f5e273
chore: bump colorprofile to v0.2.0
aymanbagabas Jan 27, 2025
ba855fa
fix: unexported context field in Program
aymanbagabas Jan 27, 2025
5fba170
chore: update tests
aymanbagabas Jan 27, 2025
7ad9693
feat: add output tracing
aymanbagabas Jan 27, 2025
38cad19
fix: force input tty for tests
aymanbagabas Jan 27, 2025
2230ed0
fix: use the Env field instead of the environ field
aymanbagabas Jan 27, 2025
4774695
fix: remove Program.Title field
aymanbagabas Jan 27, 2025
bd9f193
fix: lint issues
aymanbagabas Jan 27, 2025
787a9a2
chore: update examples
aymanbagabas Jan 28, 2025
70c8c8e
docs: update Program documentation
aymanbagabas Jan 29, 2025
47c1177
fix: tracing: properly handle TEA_TRACE env var
aymanbagabas Jan 29, 2025
b6c1ba2
feat: use a separate logger for tracing
aymanbagabas Jan 29, 2025
46f89ce
refactor: remove shim compat layer and tea.Model
aymanbagabas Jan 29, 2025
8f62296
fix: tidy and error on missing functions
aymanbagabas Jan 29, 2025
3f4e3fc
chore: update tests
aymanbagabas Jan 29, 2025
423a0e4
fix: use generic type in tty_windows.go and signals_windows.go
aymanbagabas Jan 29, 2025
67f8ede
chore: revert "refactor: remove shim compat layer and tea.Model"
aymanbagabas Jan 29, 2025
ff6833c
chore: update View method signature in tests
aymanbagabas Jan 30, 2025
5d9cb5b
fix: wait for program to initialize before sending messages
aymanbagabas Jan 30, 2025
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: 2 additions & 2 deletions environ.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ type environ []string
// returned will be the empty string.
// This function traverses the environment variables in reverse order, so that
// the last value set for the key is the one returned.
func (p *Program) getenv(key string) (v string) {
return p.environ.Getenv(key)
func (p *Program[T]) getenv(key string) (v string) {
return environ(p.Env).Getenv(key)
}

// Getenv returns the value of the environment variable named by the key. If
Expand Down
6 changes: 3 additions & 3 deletions examples/altscreen-toggle/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ type model struct {
suspending bool
}

func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
return m, nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.ResumeMsg:
m.suspending = false
Expand Down Expand Up @@ -79,7 +79,7 @@ func (m model) View() fmt.Stringer {
}

func main() {
if _, err := tea.NewProgram(model{}).Run(); err != nil {
if err := tea.NewProgram(model{}).Run(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
Expand Down
6 changes: 3 additions & 3 deletions examples/autocomplete/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (

func main() {
p := tea.NewProgram(initialModel())
if _, err := p.Run(); err != nil {
if err := p.Run(); err != nil {
log.Fatal(err)
}
}
Expand Down Expand Up @@ -101,11 +101,11 @@ func initialModel() model {
return model{textInput: ti, help: h, keymap: km}
}

func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
return m, tea.Batch(getRepos, textinput.Blink)
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch msg.String() {
Expand Down
8 changes: 3 additions & 5 deletions examples/capability/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,15 @@ type model struct {
width int
}

var _ tea.Model = model{}

// Init implements tea.Model.
func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
m.input = textinput.New()
m.input.Placeholder = "Enter capability name to request"
return m, m.input.Focus()
}

// Update implements tea.Model.
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.WindowSizeMsg:
Expand Down Expand Up @@ -59,7 +57,7 @@ func (m model) View() fmt.Stringer {
}

func main() {
if _, err := tea.NewProgram(model{}).Run(); err != nil {
if err := tea.NewProgram(model{}).Run(); err != nil {
log.Fatal(err)
}
}
Expand Down
14 changes: 9 additions & 5 deletions examples/cellbuffer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,11 +142,15 @@ type model struct {
xVelocity, yVelocity float64
}

func (m model) Init() (tea.Model, tea.Cmd) {
return m, animate()
func (m model) Init() (model, tea.Cmd) {
return m, tea.Batch(
animate(),
tea.EnableMouseCellMotion,
tea.EnterAltScreen,
)
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
return m, tea.Quit
Expand Down Expand Up @@ -193,8 +197,8 @@ func main() {
spring: harmonica.NewSpring(harmonica.FPS(fps), frequency, damping),
}

p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseCellMotion())
if _, err := p.Run(); err != nil {
p := tea.NewProgram(m)
if err := p.Run(); err != nil {
fmt.Println("Uh oh:", err)
os.Exit(1)
}
Expand Down
6 changes: 3 additions & 3 deletions examples/chat/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const gap = "\n\n"

func main() {
p := tea.NewProgram(initialModel())
if _, err := p.Run(); err != nil {
if err := p.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Oof: %v\n", err)
}
}
Expand Down Expand Up @@ -63,11 +63,11 @@ Type a message and press Enter to send.`)
}
}

func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
return m, textarea.Blink
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.viewport.SetWidth(msg.Width)
Expand Down
11 changes: 5 additions & 6 deletions examples/colorprofile/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,16 @@ var myFancyColor color.Color

type model struct{}

var _ tea.Model = model{}

// Init implements tea.Model.
func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
return m, tea.Batch(
tea.RequestCapability("RGB"),
tea.RequestCapability("Tc"),
)
}

// Update implements tea.Model.
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
return m, tea.Quit
Expand All @@ -47,8 +45,9 @@ func (m model) View() fmt.Stringer {
func main() {
myFancyColor, _ = colorful.Hex("#6b50ff")

p := tea.NewProgram(model{}, tea.WithColorProfile(colorprofile.TrueColor))
if _, err := p.Run(); err != nil {
p := tea.NewProgram(model{})
p.Profile = colorprofile.TrueColor
if err := p.Run(); err != nil {
log.Fatal(err)
}
}
6 changes: 3 additions & 3 deletions examples/composable-views/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,14 @@ func newModel(timeout time.Duration) mainModel {
return m
}

func (m mainModel) Init() (tea.Model, tea.Cmd) {
func (m mainModel) Init() (mainModel, tea.Cmd) {
// start the timer and spinner on program start
timer, cmd := m.timer.Init()
m.timer = timer
return m, tea.Batch(cmd, m.spinner.Tick)
}

func (m mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m mainModel) Update(msg tea.Msg) (mainModel, tea.Cmd) {
var cmd tea.Cmd
var cmds []tea.Cmd
switch msg := msg.(type) {
Expand Down Expand Up @@ -160,7 +160,7 @@ func (m *mainModel) resetSpinner() {
func main() {
p := tea.NewProgram(newModel(defaultTime))

if _, err := p.Run(); err != nil {
if err := p.Run(); err != nil {
log.Fatal(err)
}
}
6 changes: 3 additions & 3 deletions examples/credit-card-form/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
func main() {
p := tea.NewProgram(initialModel())

if _, err := p.Run(); err != nil {
if err := p.Run(); err != nil {
log.Fatal(err)
}
}
Expand Down Expand Up @@ -124,11 +124,11 @@ func initialModel() model {
}
}

func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
return m, textinput.Blink
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
var cmds []tea.Cmd = make([]tea.Cmd, len(m.inputs))

switch msg := msg.(type) {
Expand Down
20 changes: 10 additions & 10 deletions examples/cursor-style/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,35 @@ import (
)

type model struct {
style tea.CursorStyle
shape tea.CursorShape
blink bool
}

func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
m.blink = true
return m, nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch msg.String() {
case "ctrl+q", "q":
return m, tea.Quit
case "h", "left":
if m.style == tea.CursorBlock && m.blink {
if m.shape == tea.CursorBlock && m.blink {
break
}
if m.blink {
m.style--
m.shape--
}
m.blink = !m.blink
case "l", "right":
if m.style == tea.CursorBar && !m.blink {
if m.shape == tea.CursorBar && !m.blink {
break
}
if !m.blink {
m.style++
m.shape++
}
m.blink = !m.blink
}
Expand All @@ -49,7 +49,7 @@ func (m model) View() fmt.Stringer {
"\n\n" +
" <- This is the cursor (a " + m.describeCursor() + ")")
f.Cursor = tea.NewCursor(0, 2)
f.Cursor.Style = m.style
f.Cursor.Shape = m.shape
f.Cursor.Blink = m.blink
return f
}
Expand All @@ -63,7 +63,7 @@ func (m model) describeCursor() string {
adj = "steady"
}

switch m.style {
switch m.shape {
case tea.CursorBlock:
noun = "block"
case tea.CursorUnderline:
Expand All @@ -77,7 +77,7 @@ func (m model) describeCursor() string {

func main() {
p := tea.NewProgram(model{})
if _, err := p.Run(); err != nil {
if err := p.Run(); err != nil {
fmt.Fprintf(os.Stderr, "Error: %v", err)
os.Exit(1)
}
Expand Down
6 changes: 3 additions & 3 deletions examples/debounce/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ type model struct {
tag int
}

func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
return m, nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
// Increment the tag on the model...
Expand Down Expand Up @@ -59,7 +59,7 @@ func (m model) View() fmt.Stringer {
}

func main() {
if _, err := tea.NewProgram(model{}).Run(); err != nil {
if err := tea.NewProgram(model{}).Run(); err != nil {
fmt.Println("uh oh:", err)
os.Exit(1)
}
Expand Down
13 changes: 8 additions & 5 deletions examples/doom-fire/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,14 @@ type model struct {
startTime time.Time
}

func (m model) Init() (tea.Model, tea.Cmd) {
return m, tick
func (m model) Init() (model, tea.Cmd) {
return m, tea.Batch(
tick,
tea.EnterAltScreen,
)
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
if msg.String() == "q" || msg.String() == "ctrl+c" {
Expand Down Expand Up @@ -128,8 +131,8 @@ func initialModel() model {
}

func main() {
p := tea.NewProgram(initialModel(), tea.WithAltScreen())
if _, err := p.Run(); err != nil {
p := tea.NewProgram(initialModel())
if err := p.Run(); err != nil {
fmt.Printf("Error running program: %v", err)
}
}
6 changes: 3 additions & 3 deletions examples/exec/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ type model struct {
err error
}

func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
return m, nil
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch msg.String() {
Expand Down Expand Up @@ -64,7 +64,7 @@ func (m model) View() fmt.Stringer {

func main() {
m := model{}
if _, err := tea.NewProgram(m).Run(); err != nil {
if err := tea.NewProgram(m).Run(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
Expand Down
18 changes: 12 additions & 6 deletions examples/file-picker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ func clearErrorAfter(t time.Duration) tea.Cmd {
})
}

func (m model) Init() (tea.Model, tea.Cmd) {
func (m model) Init() (model, tea.Cmd) {
fp, cmd := m.filepicker.Init()
m.filepicker = fp
return m, cmd
return m, tea.Batch(
tea.EnterAltScreen,
cmd,
)
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
func (m model) Update(msg tea.Msg) (model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyPressMsg:
switch msg.String() {
Expand Down Expand Up @@ -90,7 +93,10 @@ func main() {
m := model{
filepicker: fp,
}
tm, _ := tea.NewProgram(&m, tea.WithAltScreen()).Run()
mm := tm.(model)
fmt.Println("\n You selected: " + m.filepicker.Styles.Selected.Render(mm.selectedFile) + "\n")
tm := tea.NewProgram(&m)
if err := tm.Run(); err != nil {
fmt.Println("Error running program:", err)
os.Exit(1)
}
fmt.Println("\n You selected: " + m.filepicker.Styles.Selected.Render(tm.Model.selectedFile) + "\n")
}
Loading
Loading