Files
fn_registry/fn_operations/docker_tui/views/images.go
T
egutierrez 1e6751d6c0 refactor: navegacion consistente con HandleBack en todas las vistas
Cada vista expone HandleBack() bool que retrocede un nivel interno (logs ->
lista) o indica que esta en estado base. El model principal intercepta q/0/esc
y delega a HandleBack: si retorna true, sale de la TUI; si false, la vista
retrocedio internamente. Elimina manejo duplicado de esc/q/0 en sub-estados.
2026-03-28 04:52:21 +01:00

133 lines
2.9 KiB
Go

package views
import (
"fmt"
tea "github.com/charmbracelet/bubbletea"
"github.com/lucasdataproyects/devfactory/tui"
)
type imagesState int
const (
imagesLoading imagesState = iota
imagesList
imagesAction
)
type imagesLoadedMsg []DockerImage
type imagesActionMsg struct{ output string; err error }
type ImagesModel struct {
state imagesState
list tui.FilteredListModel
spinner tui.SpinnerModel
styles tui.Styles
images []DockerImage
err error
}
func NewImagesModel(styles tui.Styles) ImagesModel {
return ImagesModel{
state: imagesLoading,
list: tui.NewFilteredList(nil, "Filter images..."),
spinner: tui.NewSpinner("Loading images..."),
styles: styles,
}
}
func (m ImagesModel) Init() tea.Cmd {
return tea.Batch(m.spinner.Init(), loadImages)
}
func loadImages() tea.Msg {
images, err := ListImages()
if err != nil {
return imagesLoadedMsg(nil)
}
return imagesLoadedMsg(images)
}
func (m ImagesModel) Update(msg tea.Msg) (ImagesModel, tea.Cmd) {
switch msg := msg.(type) {
case imagesLoadedMsg:
m.images = []DockerImage(msg)
items := make([]tui.ListItem, len(m.images))
for i, img := range m.images {
tag := img.Tag
if tag == "" {
tag = "latest"
}
items[i] = tui.ListItem{
Title: fmt.Sprintf("%s:%s", img.Repository, tag),
Description: fmt.Sprintf("Size: %s — %s", img.Size, img.ID[:12]),
Value: img,
}
}
m.list.SetItems(items)
m.state = imagesList
return m, nil
case imagesActionMsg:
if msg.err != nil {
m.err = msg.err
}
m.state = imagesList
return m, loadImages
case tea.KeyMsg:
if m.state == imagesList {
switch msg.String() {
case "r":
m.state = imagesLoading
return m, tea.Batch(m.spinner.Init(), loadImages)
case "d", "delete":
if item := m.list.SelectedItem(); item != nil {
img := item.Value.(DockerImage)
m.state = imagesAction
return m, func() tea.Msg {
err := RemoveImage(img.ID)
return imagesActionMsg{output: "Removed", err: err}
}
}
}
}
}
var cmd tea.Cmd
switch m.state {
case imagesLoading, imagesAction:
var model tea.Model
model, cmd = m.spinner.Update(msg)
m.spinner = model.(tui.SpinnerModel)
case imagesList:
var model tea.Model
model, cmd = m.list.Update(msg)
m.list = model.(tui.FilteredListModel)
}
return m, cmd
}
// HandleBack retrocede un nivel. Retorna true si ya estaba en estado base.
func (m *ImagesModel) HandleBack() bool {
return true
}
func (m ImagesModel) View() string {
switch m.state {
case imagesLoading, imagesAction:
return m.spinner.View()
case imagesList:
if len(m.images) == 0 {
return m.styles.Muted.Render("No images found. Press 'r' to refresh.")
}
help := m.styles.Muted.Render(" d: remove │ r: refresh │ /: filter")
view := m.list.View() + "\n" + help
if m.err != nil {
view += "\n" + m.styles.Error.Render(fmt.Sprintf(" Error: %v", m.err))
}
return view
}
return ""
}