Version final

This commit is contained in:
luisgulo committed 2026-10-31 18:32:28 +01:00
1 parent 2d4b09b9ea
commit 74b7a0c448
13 files changed
+1103 -7

No files matched your search

+198
View File
@@ -0,0 +1,198 @@
package ui
import (
"fmt"
"github.com/charmbracelet/lipgloss"
)
var (
titleStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#FFFFFF")).
Background(lipgloss.Color("#900C3F")).
Padding(0, 1)
headerBgStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#FFFFFF")).
Background(lipgloss.Color("#1F618D"))
localHeaderBgStyle = lipgloss.NewStyle().
Bold(true).
Foreground(lipgloss.Color("#FFFFFF")).
Background(lipgloss.Color("#B7950B"))
selectedRowStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Background(lipgloss.Color("#2980B9")).
Bold(true)
normalProjectStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#F4D03F")).
Bold(true)
normalImageStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#D5D8DC"))
fullNameStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#00FFFF")).
Bold(true)
footerKeyStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#000000")).
Background(lipgloss.Color("#F4D03F")).
Bold(true)
footerDescStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Background(lipgloss.Color("#2C3E50"))
errorBoxStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#FF5F56")).
Bold(true)
successStyle = lipgloss.NewStyle().
Foreground(lipgloss.Color("#2ECC71")).
Bold(true)
)
func (m Model) View() string {
if m.state == StateRegistryDown {
return m.viewRegistryDown()
}
switch m.state {
case StateLoading:
modeText := "del Registro Docker"
if m.mode == ModeLocal {
modeText = "Locales de Docker"
}
return fmt.Sprintf("\n Cargando imágenes %s...\n", modeText)
case StateError:
return fmt.Sprintf("\n %s\n\n [Presiona 'q' para salir / 'r' o 'l' para cambiar de modo]\n", errorBoxStyle.Render("Error: "+m.err.Error()))
case StateMainTable:
s := ""
modeStr := "REGISTRY"
styleHeader := headerBgStyle
if m.mode == ModeLocal {
modeStr = "LOCAL"
styleHeader = localHeaderBgStyle
}
titleBar := fmt.Sprintf(" REGISTRY-TUI v%s [%s] ", m.version, modeStr)
s += styleHeader.Width(70).Render(titleBar) + "\n"
s += "┌───────────────────────┬──────────────────────────────────────────┐\n"
s += fmt.Sprintf("│ %-21s │ %-40s │\n", "PROYECTO / RUTA", "IMAGEN : VERSION")
s += "├───────────────────────┼──────────────────────────────────────────┤\n"
if len(m.rows) == 0 {
msgEmpty := "No hay imágenes en el registro"
if m.mode == ModeLocal {
msgEmpty = "No se encontraron imágenes locales"
}
s += fmt.Sprintf("│ %-21s │ %-40s │\n", "", msgEmpty)
s += "└───────────────────────┴──────────────────────────────────────────┘\n"
} else {
for i, row := range m.rows {
displayProj := row.Project
if displayProj == "(raíz)" {
displayProj = ""
}
imageVersion := fmt.Sprintf("%s:%s", row.ImageName, row.Tag)
isSelected := (m.cursor == i)
var lineContent string
if isSelected {
projFormatted := fmt.Sprintf(" %-21s ", displayProj)
imgFormatted := fmt.Sprintf(" %-40s ", imageVersion)
renderedProj := selectedRowStyle.Render(projFormatted)
renderedImg := selectedRowStyle.Render(imgFormatted)
lineContent = fmt.Sprintf("│%s│%s│", renderedProj, renderedImg)
} else {
projFormatted := normalProjectStyle.Render(fmt.Sprintf("%-21s", displayProj))
imgFormatted := normalImageStyle.Render(fmt.Sprintf("%-40s", imageVersion))
lineContent = fmt.Sprintf("│ %s │ %s │", projFormatted, imgFormatted)
}
s += lineContent + "\n"
if i < len(m.rows)-1 {
s += "├───────────────────────┼──────────────────────────────────────────┤\n"
}
}
s += "└───────────────────────┴──────────────────────────────────────────┘\n"
}
// Ruta completa seleccionada
if len(m.rows) > 0 {
selected := m.rows[m.cursor]
fullRef := fmt.Sprintf("%s:%s", selected.RepoName, selected.Tag)
s += fmt.Sprintf(" Ruta completa: %s\n", fullNameStyle.Render(fullRef))
} else {
s += "\n"
}
total := len(m.rows)
statusInfo := fmt.Sprintf(" Total: %d imágenes (%s)", total, modeStr)
if m.statusMsg != "" {
statusInfo = " " + successStyle.Render("✔ "+m.statusMsg)
}
s += fmt.Sprintf("%-70s\n\n", statusInfo)
// Barra de botones inferior estilo MC uniforme para ambos modos
btnR := footerKeyStyle.Render(" R ") + footerDescStyle.Render(" Registry ")
btnL := footerKeyStyle.Render(" L ") + footerDescStyle.Render(" Local ")
btnT := footerKeyStyle.Render(" t ") + footerDescStyle.Render(" Tagear ")
btnD := footerKeyStyle.Render(" d ") + footerDescStyle.Render(" Borrar ")
btnP := footerKeyStyle.Render(" p ") + footerDescStyle.Render(" Push ")
btnQ := footerKeyStyle.Render(" q ") + footerDescStyle.Render(" Salir ")
if m.mode == ModeLocal {
s += fmt.Sprintf(" %s %s %s %s %s %s\n", btnR, btnL, btnP, btnT, btnD, btnQ)
} else {
s += fmt.Sprintf(" %s %s %s %s %s\n", btnR, btnL, btnT, btnD, btnQ)
}
return s
case StateConfirmDelete:
selectedRow := m.rows[m.cursor]
s := headerBgStyle.Width(50).Render(" Confirmar Borrado ") + "\n\n"
s += fmt.Sprintf(" ¿Estás seguro de que deseas borrar la imagen:\n -> %s:%s?\n\n", selectedRow.RepoName, selectedRow.Tag)
if m.mode == ModeLocal {
s += errorBoxStyle.Render(" ¡Esta acción eliminará la imagen de tu sistema local!\n\n")
} else {
s += errorBoxStyle.Render(" ¡Esta acción eliminará el manifest permanentemente del registro!\n\n")
}
s += footerKeyStyle.Render(" y ") + footerDescStyle.Render(" Sí, borrar ") + " " + footerKeyStyle.Render(" n ") + footerDescStyle.Render(" Cancelar ")
return s
case StateNewTag:
selectedRow := m.rows[m.cursor]
s := headerBgStyle.Width(50).Render(" Reestructurar / Tagear Imagen ") + "\n\n"
s += fmt.Sprintf(" Origen: %s:%s\n\n", selectedRow.RepoName, selectedRow.Tag)
s += " Introduce la ruta completa de destino\n (ej: proyecto-web/alpine:v1.0):\n\n"
s += " " + m.textInput.View() + "\n\n"
s += footerKeyStyle.Render(" Enter ") + footerDescStyle.Render(" Confirmar ") + " " + footerKeyStyle.Render(" Esc ") + footerDescStyle.Render(" Cancelar ")
return s
}
return ""
}
func (m Model) viewRegistryDown() string {
s := titleStyle.Render(" ⚠️ ADVERTENCIA DE CONEXIÓN - REGISTRY-TUI ") + "\n\n"
s += fmt.Sprintf(" No se puede conectar al Registry local:\n %s\n\n", m.statusMsg)
s += " ─────────────────────────────────────────────────────────\n"
s += " [ENTER / L] Cambiar directamente al modo LOCAL\n"
s += " [ q ] Salir de la aplicación\n"
return lipgloss.NewStyle().Margin(2, 4).Render(s)
}