Skip to content

Commit

Permalink
Merge pull request #4 from termkit/support-tea-interface-for-instant-…
Browse files Browse the repository at this point in the history
…initialize

Change return type to make possible use as tea.Model
  • Loading branch information
canack authored Sep 1, 2024
2 parents 84cba4f + c77f2b8 commit 99e516d
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 129 deletions.
70 changes: 23 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,27 @@ import (
tea "github.com/charmbracelet/bubbletea"
)

// -----------------------------------------------------------------------------
// Tiny Model
// The Tiny Model is a sub-model for the tabs. It's a simple model that just shows the title of the tab.

// tinyModel is a sub-model for the tabs
type tinyModel struct {
skeleton *skeleton.Skeleton
title string
}

// newTinyModel returns a new tinyModel
func newTinyModel(skeleton *skeleton.Skeleton, title string) *tinyModel {
return &tinyModel{
skeleton: skeleton,
title: title,
}
}

func (m tinyModel) Init() tea.Cmd { return nil }
func (m tinyModel) Init() tea.Cmd {
return nil
}
func (m tinyModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
Expand All @@ -76,74 +84,44 @@ func (m tinyModel) View() string {
}
````

#### 2. Create the Main Model

Next, define the main model that will handle the overall application state and interact with Skeleton:

````go
type mainModel struct {
skeleton *skeleton.Skeleton
}

func (m *mainModel) Init() tea.Cmd {
return tea.Batch(
tea.EnterAltScreen,
tea.SetWindowTitle("Basic Tab Example"),
m.skeleton.Init(), // Initialize the skeleton
)
}

func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
m.skeleton, cmd = m.skeleton.Update(msg)
return m, cmd
}

func (m *mainModel) View() string {
return m.skeleton.View()
}
````

#### 3. Set Up the Application
#### 2. Set Up the Application

Initialize Skeleton, add pages, and configure widgets:

````go
// -----------------------------------------------------------------------------
// Main Program
func main() {
skel := skeleton.NewSkeleton()
s := skeleton.NewSkeleton()

// Add tabs (pages)
skel.AddPage("first", "First Tab", newTinyModel(skel, "First"))
skel.AddPage("second", "Second Tab", newTinyModel(skel, "Second"))
skel.AddPage("third", "Third Tab", newTinyModel(skel, "Third"))
s.AddPage("first", "First Tab", newTinyModel(s, "First"))
s.AddPage("second", "Second Tab", newTinyModel(s, "Second"))
s.AddPage("third", "Third Tab", newTinyModel(s, "Third"))

// Set up key bindings ( Optional | Defaults: ctrl+left, ctrl+right )
//To switch next page
skel.KeyMap.SwitchTabRight = key.NewBinding(
s.KeyMap.SwitchTabRight = key.NewBinding(
key.WithKeys("shift+right"))

// To switch previous page
skel.KeyMap.SwitchTabLeft = key.NewBinding(
s.KeyMap.SwitchTabLeft = key.NewBinding(
key.WithKeys("shift+left"))

// Add a widget to entire screen
skel.AddWidget("battery", "Battery %92")
skel.AddWidget("time", time.Now().Format("15:04:05"))
s.AddWidget("battery", "Battery %92")
s.AddWidget("time", time.Now().Format("15:04:05"))

// Update the time widget every second
go func() {
time.Sleep(time.Second)
for {
skel.UpdateWidgetValue("time", time.Now().Format("15:04:05"))
s.UpdateWidgetValue("time", time.Now().Format("15:04:05"))
time.Sleep(time.Second)
}
}()

model := &mainModel{
skeleton: skel,
}

p := tea.NewProgram(model)
p := tea.NewProgram(s)
if err := p.Start(); err != nil {
panic(err)
}
Expand All @@ -154,9 +132,7 @@ func main() {

1. **Model Definition**: `tinyModel` represents the content of each tab. It uses the Skeleton instance to query terminal dimensions and display information.

2. **Main Model**: `mainModel` integrates Skeleton and handles application state updates and rendering.

3. **Application Setup**: The `main` function initializes Skeleton, adds pages, and sets up widgets. The time widget updates every second to reflect the current time.
2. **Application Setup**: The `main` function initializes Skeleton, adds pages, and sets up widgets. The time widget updates every second to reflect the current time.

## Skeleton in the Wild
Some programs that use Skeleton in production:
Expand Down
26 changes: 1 addition & 25 deletions examples/file-reader/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,6 @@ import (
"github.com/termkit/skeleton"
)

type mainModel struct {
skeleton *skeleton.Skeleton
}

func (m *mainModel) Init() tea.Cmd {
return tea.Batch(
tea.EnterAltScreen,
tea.SetWindowTitle("File Reader"),
m.skeleton.Init(),
)
}

func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
m.skeleton, cmd = m.skeleton.Update(msg)
return m, cmd
}

func (m *mainModel) View() string {
return m.skeleton.View()
}

func main() {
s := skeleton.NewSkeleton()
s.SetPagePosition(lipgloss.Left)
Expand All @@ -37,9 +15,7 @@ func main() {

s.AddPage("explorer", "Explorer", newExplorer(s))

m := &mainModel{skeleton: s}

if err := tea.NewProgram(m).Start(); err != nil {
if err := tea.NewProgram(s).Start(); err != nil {
panic(err)
}
}
56 changes: 15 additions & 41 deletions examples/fundamental-skeleton/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ func newTinyModel(skeleton *skeleton.Skeleton, title string) *tinyModel {
}
}

func (m tinyModel) Init() tea.Cmd { return nil }
func (m tinyModel) Init() tea.Cmd {
return nil
}
func (m tinyModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
Expand All @@ -40,60 +42,32 @@ func (m tinyModel) View() string {
}

// -----------------------------------------------------------------------------
// Main Model
// The Main Model is the main model for the program. It contains the skeleton and the tab models.

type mainModel struct {
skeleton *skeleton.Skeleton
}

func (m *mainModel) Init() tea.Cmd {
return tea.Batch(
tea.EnterAltScreen,
tea.SetWindowTitle("Basic Tab Example"),
m.skeleton.Init(),
)
}

func (m *mainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
m.skeleton, cmd = m.skeleton.Update(msg)
return m, cmd
}

func (m *mainModel) View() string {
return m.skeleton.View()
}

// Main Program
func main() {
skel := skeleton.NewSkeleton()
s := skeleton.NewSkeleton()

// Add tabs (pages)
skel.AddPage("first", "First Tab", newTinyModel(skel, "First"))
skel.AddPage("second", "Second Tab", newTinyModel(skel, "Second"))
skel.AddPage("third", "Third Tab", newTinyModel(skel, "Third"))
s.AddPage("first", "First Tab", newTinyModel(s, "First"))
s.AddPage("second", "Second Tab", newTinyModel(s, "Second"))
s.AddPage("third", "Third Tab", newTinyModel(s, "Third"))

// Add a widget to entire screen
// Add a widget to entire screen ( Optional )
// Battery level is hardcoded. You can use a library to get the battery level of your system.
skel.AddWidget("battery", "Battery %92") // Add a widget to entire screen
s.AddWidget("battery", "Battery %92") // Add a widget to entire screen

// Add current time
skel.AddWidget("time", time.Now().Format("15:04:05"))
// Add current time ( Optional )
s.AddWidget("time", time.Now().Format("15:04:05"))

// Update the time widget every second
// Update the time widget every second ( Optional )
go func() {
time.Sleep(time.Second)
for {
skel.UpdateWidgetValue("time", time.Now().Format("15:04:05"))
s.UpdateWidgetValue("time", time.Now().Format("15:04:05"))
time.Sleep(time.Second)
}
}()

model := &mainModel{
skeleton: skel,
}

p := tea.NewProgram(model)
p := tea.NewProgram(s)
if err := p.Start(); err != nil {
panic(err)
}
Expand Down
25 changes: 9 additions & 16 deletions skeleton.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,21 +354,6 @@ func (s *Skeleton) GetActivePage() string {
return s.header.headers[s.currentTab].key
}

func (s *Skeleton) Init() tea.Cmd {
if len(s.pages) == 0 {
panic("skeleton: no pages added, please add at least one page")
}

inits := make([]tea.Cmd, 3) // 3 for (self, header, Value)

// and init self, header and Value
inits[0] = s.Listen()
inits[1] = s.header.Init()
inits[2] = s.widget.Init()

return tea.Batch(inits...)
}

// IAMActivePage is a message to trigger the update of the active page.
type IAMActivePage struct{}

Expand All @@ -379,7 +364,15 @@ func (s *Skeleton) IAMActivePageCmd() tea.Cmd {
}
}

func (s *Skeleton) Update(msg tea.Msg) (*Skeleton, tea.Cmd) {
func (s *Skeleton) Init() tea.Cmd {
if len(s.pages) == 0 {
panic("skeleton: no pages added, please add at least one page")
}

return tea.Batch(s.Listen(), s.header.Init(), s.widget.Init())
}

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

Expand Down

0 comments on commit 99e516d

Please sign in to comment.