package ui import ( "strings" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" ) func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // 1. Manejo especial cuando el Registry está caído (StateRegistryDown) if m.state == StateRegistryDown { if keyMsg, ok := msg.(tea.KeyMsg); ok { switch keyMsg.String() { case "enter", "L", "l": m.state = StateLoading m.mode = ModeLocal m.statusMsg = "" return m, fetchLocalImagesCmd(m.client) case "q", "ctrl+c", "CTRL+C": return m, tea.Quit } } return m, nil } switch msg := msg.(type) { case tea.KeyMsg: if m.state == StateNewTag { switch msg.String() { case "esc": m.state = StateMainTable m.textInput.Reset() return m, nil case "enter": inputVal := m.textInput.Value() if inputVal != "" { lastColon := strings.LastIndex(inputVal, ":") var destRepo, destTag string if lastColon != -1 { destRepo = inputVal[:lastColon] destTag = inputVal[lastColon+1:] } else { destRepo = inputVal destTag = "latest" } if destRepo != "" && destTag != "" { selectedRow := m.rows[m.cursor] m.state = StateLoading m.textInput.Reset() if m.mode == ModeLocal { return m, tagLocalImageCmd(m.client, selectedRow.RepoName, selectedRow.Tag, destRepo, destTag) } return m, copyImageCmd(m.client, selectedRow.RepoName, selectedRow.Tag, destRepo, destTag) } } return m, nil } var cmd tea.Cmd m.textInput, cmd = m.textInput.Update(msg) return m, cmd } switch msg.String() { case "ctrl+c", "q": if m.state == StateConfirmDelete { m.state = StateMainTable return m, nil } return m, tea.Quit case "up", "k": if m.cursor > 0 { m.cursor-- } case "down", "j": if m.cursor < len(m.rows)-1 { m.cursor++ } case "r": if m.state == StateMainTable && m.mode != ModeRegistry { m.state = StateLoading m.cursor = 0 m.statusMsg = "" m.mode = ModeRegistry return m, fetchRegistryImagesCmd(m.client) } case "l": if m.state == StateMainTable && m.mode != ModeLocal { m.state = StateLoading m.cursor = 0 m.statusMsg = "" m.mode = ModeLocal return m, fetchLocalImagesCmd(m.client) } case "d": if m.state == StateMainTable && len(m.rows) > 0 { m.state = StateConfirmDelete } case "t": if m.state == StateMainTable && len(m.rows) > 0 { m.state = StateNewTag m.textInput.Focus() return m, textinput.Blink } case "p": if m.state == StateMainTable && m.mode == ModeLocal && len(m.rows) > 0 { selectedRow := m.rows[m.cursor] m.state = StateLoading return m, pushImageCmd(m.client, selectedRow.RepoName, selectedRow.Tag) } case "y": if m.state == StateConfirmDelete { selectedRow := m.rows[m.cursor] m.state = StateLoading if m.mode == ModeLocal { return m, deleteLocalImageCmd(m.client, selectedRow.RepoName, selectedRow.Tag) } return m, deleteTagCmd(m.client, selectedRow.RepoName, selectedRow.Tag) } case "n": if m.state == StateConfirmDelete { m.state = StateMainTable } } case imagesMsg: m.rows = msg.rows if msg.mode == ModeRegistry { m.registryRows = msg.rows } else { m.localRows = msg.rows } m.state = StateMainTable m.cursor = 0 case successMsg: m.statusMsg = string(msg) m.state = StateLoading if m.mode == ModeRegistry { return m, fetchRegistryImagesCmd(m.client) } else { return m, fetchLocalImagesCmd(m.client) } case errorMsg: errVal := error(msg) errMsgStr := errVal.Error() // Comprobamos si el error es de conexión con el registro if strings.Contains(errMsgStr, "connection refused") || strings.Contains(errMsgStr, "dial tcp") || strings.Contains(errMsgStr, "Get \"http://localhost:5000") { m.state = StateRegistryDown m.statusMsg = errMsgStr return m, nil } // Error estándar para otros casos m.err = errVal m.state = StateError return m, nil } return m, nil }