3716c8fc6d
TUI fullscreen con 5 vistas: Containers (start/stop/restart/logs), Images (list/remove), Volumes (list/remove), Networks (list/remove), Compose (up/down/logs). Usa DevFactory (tui, shell, core). Navegacion por tabs, filtrado en containers/images, scroll en logs. Incluye operations.db con entities y relations del proyecto.
147 lines
3.1 KiB
Go
147 lines
3.1 KiB
Go
package app
|
|
|
|
import (
|
|
"docker-tui/views"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
"github.com/charmbracelet/lipgloss"
|
|
"github.com/lucasdataproyects/devfactory/tui"
|
|
)
|
|
|
|
type View int
|
|
|
|
const (
|
|
ViewContainers View = iota
|
|
ViewImages
|
|
ViewVolumes
|
|
ViewNetworks
|
|
ViewCompose
|
|
)
|
|
|
|
var tabNames = []string{"Containers", "Images", "Volumes", "Networks", "Compose"}
|
|
|
|
type Model struct {
|
|
tui.BaseModel
|
|
activeTab int
|
|
containers views.ContainersModel
|
|
images views.ImagesModel
|
|
volumes views.VolumesModel
|
|
networks views.NetworksModel
|
|
compose views.ComposeModel
|
|
ready bool
|
|
}
|
|
|
|
func New() Model {
|
|
styles := tui.DefaultStyles()
|
|
return Model{
|
|
BaseModel: tui.NewBaseModel().WithStyles(styles),
|
|
containers: views.NewContainersModel(styles),
|
|
images: views.NewImagesModel(styles),
|
|
volumes: views.NewVolumesModel(styles),
|
|
networks: views.NewNetworksModel(styles),
|
|
compose: views.NewComposeModel(styles),
|
|
}
|
|
}
|
|
|
|
func (m Model) Init() tea.Cmd {
|
|
return m.containers.Init()
|
|
}
|
|
|
|
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case views.KeyQuit:
|
|
return m, tea.Quit
|
|
case views.KeyTab:
|
|
m.activeTab = (m.activeTab + 1) % len(tabNames)
|
|
return m, m.initActiveView()
|
|
case "shift+tab":
|
|
m.activeTab = (m.activeTab - 1 + len(tabNames)) % len(tabNames)
|
|
return m, m.initActiveView()
|
|
}
|
|
case tea.WindowSizeMsg:
|
|
m.HandleWindowSize(msg)
|
|
m.ready = true
|
|
}
|
|
|
|
var cmd tea.Cmd
|
|
switch View(m.activeTab) {
|
|
case ViewContainers:
|
|
m.containers, cmd = m.containers.Update(msg)
|
|
case ViewImages:
|
|
m.images, cmd = m.images.Update(msg)
|
|
case ViewVolumes:
|
|
m.volumes, cmd = m.volumes.Update(msg)
|
|
case ViewNetworks:
|
|
m.networks, cmd = m.networks.Update(msg)
|
|
case ViewCompose:
|
|
m.compose, cmd = m.compose.Update(msg)
|
|
}
|
|
return m, cmd
|
|
}
|
|
|
|
func (m Model) View() string {
|
|
if !m.ready {
|
|
return "Loading..."
|
|
}
|
|
|
|
// Tab bar
|
|
tabs := m.renderTabs()
|
|
|
|
// Active view content
|
|
var content string
|
|
switch View(m.activeTab) {
|
|
case ViewContainers:
|
|
content = m.containers.View()
|
|
case ViewImages:
|
|
content = m.images.View()
|
|
case ViewVolumes:
|
|
content = m.volumes.View()
|
|
case ViewNetworks:
|
|
content = m.networks.View()
|
|
case ViewCompose:
|
|
content = m.compose.View()
|
|
}
|
|
|
|
// Status bar
|
|
status := m.Styles.StatusBar.Render(" Tab: switch view │ Ctrl+C: quit │ Enter: action │ r: refresh")
|
|
|
|
return lipgloss.JoinVertical(lipgloss.Left,
|
|
tabs,
|
|
"",
|
|
content,
|
|
"",
|
|
status,
|
|
)
|
|
}
|
|
|
|
func (m Model) renderTabs() string {
|
|
var tabs []string
|
|
for i, name := range tabNames {
|
|
if i == m.activeTab {
|
|
tabs = append(tabs, m.Styles.Selected.Render(" "+name+" "))
|
|
} else {
|
|
tabs = append(tabs, m.Styles.Muted.Render(" "+name+" "))
|
|
}
|
|
}
|
|
row := lipgloss.JoinHorizontal(lipgloss.Top, tabs...)
|
|
return m.Styles.Header.Render("Docker TUI") + " " + row
|
|
}
|
|
|
|
func (m Model) initActiveView() tea.Cmd {
|
|
switch View(m.activeTab) {
|
|
case ViewContainers:
|
|
return m.containers.Init()
|
|
case ViewImages:
|
|
return m.images.Init()
|
|
case ViewVolumes:
|
|
return m.volumes.Init()
|
|
case ViewNetworks:
|
|
return m.networks.Init()
|
|
case ViewCompose:
|
|
return m.compose.Init()
|
|
}
|
|
return nil
|
|
}
|