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

Implement lipgloss table and support navigating between stub models #52

Merged
merged 4 commits into from
Sep 9, 2024
Merged
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
7 changes: 4 additions & 3 deletions pkg/app/master/command/images/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/mintoolkit/mint/pkg/app"
"github.com/mintoolkit/mint/pkg/app/master/command"
"github.com/mintoolkit/mint/pkg/app/master/tui"
"github.com/mintoolkit/mint/pkg/app/master/tui/models"
imagesModel "github.com/mintoolkit/mint/pkg/app/master/tui/images"
"github.com/mintoolkit/mint/pkg/app/master/version"
cmd "github.com/mintoolkit/mint/pkg/command"
"github.com/mintoolkit/mint/pkg/crt"
Expand Down Expand Up @@ -129,8 +129,9 @@ func OnCommand(
xc.FailOn(err)

if cparams.TUI {
model := models.InitialImages(images)
tui.RunTUI(model)
standalone := true
model := imagesModel.InitialModel(images, standalone)
tui.RunTUI(model, standalone)
}

if xc.Out.Quiet {
Expand Down
6 changes: 3 additions & 3 deletions pkg/app/master/command/tui/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package tui

import (
tui "github.com/mintoolkit/mint/pkg/app/master/tui"
"github.com/mintoolkit/mint/pkg/app/master/tui/models"
"github.com/mintoolkit/mint/pkg/app/master/tui/home"
cmd "github.com/mintoolkit/mint/pkg/command"
"github.com/urfave/cli/v2"
)
Expand All @@ -18,8 +18,8 @@ var CLI = &cli.Command{
Aliases: []string{Alias},
Usage: Usage,
Action: func(ctx *cli.Context) error {
m := models.InitialModel()
tui.RunTUI(m)
m, _ := home.InitialModel()
tui.RunTUI(m, false)
return nil
},
}
11 changes: 11 additions & 0 deletions pkg/app/master/tui/common/const.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package common

import (
tea "github.com/charmbracelet/bubbletea"
)

var (
// P the current tea program
P *tea.Program
Models []tea.Model
)
11 changes: 11 additions & 0 deletions pkg/app/master/tui/common/styles.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package common

import "github.com/charmbracelet/lipgloss"

var (
Bold = Regular.Bold(true)
Border = Regular.Border(lipgloss.NormalBorder())
Regular = lipgloss.NewStyle()
Padded = Regular.Padding(0, 1)
HelpStyle = Regular.Padding(0, 1).Background(lipgloss.Color("#ffffff")).Foreground(lipgloss.Color("#000000")).Render
)
72 changes: 72 additions & 0 deletions pkg/app/master/tui/home/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package home

import (
"log"

"github.com/charmbracelet/bubbles/key"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/mintoolkit/mint/pkg/app/master/tui/common"
"github.com/mintoolkit/mint/pkg/app/master/tui/keys"
)

type mode int

const (
nav mode = iota
image
debug
)

// Default Model
type Model struct {
mode mode
}

func InitialModel() (tea.Model, tea.Cmd) {
m := &Model{mode: nav}
log.Printf("Welcome model initialized: %v", m)
return m, nil
}

func (m Model) Init() tea.Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}

// Update is called to handle user input and update the model's state.
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch {
case key.Matches(msg, keys.Global.Quit):
return m, tea.Quit // Quit the program.
case key.Matches(msg, keys.Welcome.Images):
m.mode = image
// TODO - Unhardcode the model index
return common.Models[1].Update(nil)

// TODO - support debug model
// case key.Matches(msg, keys.Welcome.Debug):
// m.mode = debug
// // TODO - Unhardcode the model index
// return common.Models[2].Update(nil)
}
}
return m, nil
}

// View returns the view that should be displayed.
func (m Model) View() string {
content := "Dashboard\n Select which view you would like to open\n"

return lipgloss.JoinVertical(lipgloss.Left,
content,
m.help(),
)
}
func (m Model) help() string {
return common.HelpStyle("• i: Open images view • q: quit")
// TODO - support debug view
// return common.HelpStyle("• i: Open images view • d: Open debug view • q: quit")
}
129 changes: 129 additions & 0 deletions pkg/app/master/tui/images/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package images

import (
"time"

"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/lipgloss"
"github.com/charmbracelet/lipgloss/table"
"github.com/dustin/go-humanize"
"github.com/mintoolkit/mint/pkg/app/master/tui/common"
"github.com/mintoolkit/mint/pkg/app/master/tui/home"
"github.com/mintoolkit/mint/pkg/app/master/tui/keys"
"github.com/mintoolkit/mint/pkg/crt"
"github.com/mintoolkit/mint/pkg/crt/docker/dockerutil"

tea "github.com/charmbracelet/bubbletea"
)

// Model represents the state of the TUI.
type Model struct {
table table.Table
width int
height int
standalone bool
}

// Styles - move to `common`
const (
gray = lipgloss.Color("#737373")
lightGray = lipgloss.Color("#d3d3d3")
white = lipgloss.Color("#ffffff")
)

var (
// HeaderStyle is the lipgloss style used for the table headers.
HeaderStyle = lipgloss.NewStyle().Foreground(white).Bold(true).Align(lipgloss.Center)
// CellStyle is the base lipgloss style used for the table rows.
CellStyle = lipgloss.NewStyle().Padding(0, 1).Width(14)
// OddRowStyle is the lipgloss style used for odd-numbered table rows.
OddRowStyle = CellStyle.Foreground(gray)
// EvenRowStyle is the lipgloss style used for even-numbered table rows.
EvenRowStyle = CellStyle.Foreground(lightGray)
// BorderStyle is the lipgloss style used for the table border.
BorderStyle = lipgloss.NewStyle().Foreground(white)
)

// End styles

// InitialModel returns the initial state of the model.
func InitialModel(images map[string]crt.BasicImageInfo, standalone bool) *Model {
m := &Model{
width: 20,
height: 15,
standalone: standalone,
}
var rows [][]string
for k, v := range images {
imageRow := []string{k, dockerutil.CleanImageID(v.ID)[:12], humanize.Time(time.Unix(v.Created, 0)), humanize.Bytes(uint64(v.Size))}
rows = append(rows, imageRow)
}

t := table.New().
Border(lipgloss.NormalBorder()).
BorderStyle(BorderStyle).
StyleFunc(func(row, col int) lipgloss.Style {
var style lipgloss.Style

switch {
case row == 0:
return HeaderStyle
case row%2 == 0:
style = EvenRowStyle
default:
style = OddRowStyle
}

return style
}).
Headers("Name", "ID", "Created", "Size").
Rows(rows...)

m.table = *t
return m
}

func (m Model) Init() tea.Cmd {
// Just return `nil`, which means "no I/O right now, please."
return nil
}

// Update is called to handle user input and update the model's state.
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.table.Width(msg.Width)
m.table.Height(msg.Height)

case tea.KeyMsg:
switch {
case key.Matches(msg, keys.Global.Quit):
return m, tea.Quit
case key.Matches(msg, keys.Global.Back):
return home.InitialModel()
}
}
return m, nil
}

// View returns the view that should be displayed.
func (m Model) View() string {
var components []string

content := m.table.String()

components = append(components, content)

components = append(components, m.help())

return lipgloss.JoinVertical(lipgloss.Left,
components...,
)
}

func (m Model) help() string {
if m.standalone {
return common.HelpStyle("• q: quit")
}
return common.HelpStyle("• esc: back • q: quit")
}
43 changes: 43 additions & 0 deletions pkg/app/master/tui/keys/keys.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package keys

import (
"github.com/charmbracelet/bubbles/key"
)

type global struct {
Filter key.Binding
Quit key.Binding
Help key.Binding
Back key.Binding
}

var Global = global{
Filter: key.NewBinding(
key.WithKeys("/"),
key.WithHelp(`/`, "filter"),
),
Quit: key.NewBinding(
key.WithKeys("ctrl+c", "q"),
key.WithHelp("ctrl+c/q", "quit"),
),
Back: key.NewBinding(
key.WithKeys("esc"),
key.WithHelp("esc", "back"),
),
}

type welcome struct {
Images key.Binding
Debug key.Binding
}

var Welcome = welcome{
Images: key.NewBinding(
key.WithKeys("i"),
key.WithHelp("i", "Open images view"),
),
Debug: key.NewBinding(
key.WithKeys("d"),
key.WithHelp("d", "Open debug view"),
),
}
Loading
Loading