261 lines
6.1 KiB
Go
261 lines
6.1 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.WindowSizeMsg:
|
|
m.width = msg.Width
|
|
return m, nil
|
|
case tea.KeyMsg:
|
|
if m.busy {
|
|
if msg.String() == "ctrl+c" {
|
|
return m, tea.Quit
|
|
}
|
|
return m, nil
|
|
}
|
|
next, cmd := m.updateKey(msg)
|
|
nextModel := next.(model)
|
|
if shouldRefreshSnapshot(m, nextModel) {
|
|
return nextModel, tea.Batch(cmd, nextModel.refreshSnapshotCmd())
|
|
}
|
|
return nextModel, cmd
|
|
case satProgressMsg:
|
|
if m.busy && m.progressPrefix != "" {
|
|
if len(msg.lines) > 0 {
|
|
m.progressLines = msg.lines
|
|
}
|
|
return m, pollSATProgress(m.progressPrefix, m.progressSince)
|
|
}
|
|
return m, nil
|
|
case snapshotMsg:
|
|
m.banner = msg.banner
|
|
m.panel = msg.panel
|
|
return m, nil
|
|
case resultMsg:
|
|
m.busy = false
|
|
m.busyTitle = ""
|
|
m.progressLines = nil
|
|
m.progressPrefix = ""
|
|
m.title = msg.title
|
|
if msg.err != nil {
|
|
body := strings.TrimSpace(msg.body)
|
|
if body == "" {
|
|
m.body = fmt.Sprintf("ERROR: %v", msg.err)
|
|
} else {
|
|
m.body = fmt.Sprintf("%s\n\nERROR: %v", body, msg.err)
|
|
}
|
|
} else {
|
|
m.body = msg.body
|
|
}
|
|
m.pendingAction = actionNone
|
|
if msg.back != "" {
|
|
m.prevScreen = msg.back
|
|
} else {
|
|
m.prevScreen = m.screen
|
|
}
|
|
m.screen = screenOutput
|
|
m.cursor = 0
|
|
return m, m.refreshSnapshotCmd()
|
|
case servicesMsg:
|
|
m.busy = false
|
|
m.busyTitle = ""
|
|
if msg.err != nil {
|
|
m.title = "Services"
|
|
m.body = msg.err.Error()
|
|
m.prevScreen = screenSettings
|
|
m.screen = screenOutput
|
|
return m, m.refreshSnapshotCmd()
|
|
}
|
|
m.services = msg.services
|
|
m.screen = screenServices
|
|
m.cursor = 0
|
|
return m, m.refreshSnapshotCmd()
|
|
case interfacesMsg:
|
|
m.busy = false
|
|
m.busyTitle = ""
|
|
if msg.err != nil {
|
|
m.title = "interfaces"
|
|
m.body = msg.err.Error()
|
|
m.prevScreen = screenNetwork
|
|
m.screen = screenOutput
|
|
return m, m.refreshSnapshotCmd()
|
|
}
|
|
m.interfaces = msg.ifaces
|
|
m.screen = screenInterfacePick
|
|
m.cursor = 0
|
|
return m, m.refreshSnapshotCmd()
|
|
case exportTargetsMsg:
|
|
m.busy = false
|
|
m.busyTitle = ""
|
|
if msg.err != nil {
|
|
m.title = "export"
|
|
m.body = msg.err.Error()
|
|
m.prevScreen = screenMain
|
|
m.screen = screenOutput
|
|
return m, m.refreshSnapshotCmd()
|
|
}
|
|
m.targets = msg.targets
|
|
m.screen = screenExportTargets
|
|
m.cursor = 0
|
|
return m, m.refreshSnapshotCmd()
|
|
case nvidiaGPUsMsg:
|
|
return m.handleNvidiaGPUsMsg(msg)
|
|
case nvtopClosedMsg:
|
|
return m, nil
|
|
case nvidiaSATDoneMsg:
|
|
if m.nvidiaSATAborted {
|
|
return m, nil
|
|
}
|
|
if m.nvidiaSATCancel != nil {
|
|
m.nvidiaSATCancel()
|
|
m.nvidiaSATCancel = nil
|
|
}
|
|
m.prevScreen = screenHealthCheck
|
|
m.screen = screenOutput
|
|
m.title = msg.title
|
|
if msg.err != nil {
|
|
body := strings.TrimSpace(msg.body)
|
|
if body == "" {
|
|
m.body = fmt.Sprintf("ERROR: %v", msg.err)
|
|
} else {
|
|
m.body = fmt.Sprintf("%s\n\nERROR: %v", body, msg.err)
|
|
}
|
|
} else {
|
|
m.body = msg.body
|
|
}
|
|
return m, m.refreshSnapshotCmd()
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m model) updateKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|
switch m.screen {
|
|
case screenMain:
|
|
return m.updateMain(msg)
|
|
case screenHealthCheck:
|
|
return m.updateHealthCheck(msg)
|
|
case screenSettings:
|
|
return m.updateMenu(msg, len(m.settingsMenu), m.handleSettingsMenu)
|
|
case screenNetwork:
|
|
return m.updateMenu(msg, len(m.networkMenu), m.handleNetworkMenu)
|
|
case screenServices:
|
|
return m.updateMenu(msg, len(m.services), m.handleServicesMenu)
|
|
case screenServiceAction:
|
|
return m.updateMenu(msg, len(m.serviceMenu), m.handleServiceActionMenu)
|
|
case screenNvidiaSATSetup:
|
|
return m.updateNvidiaSATSetup(msg)
|
|
case screenNvidiaSATRunning:
|
|
return m.updateNvidiaSATRunning(msg)
|
|
case screenExportTargets:
|
|
return m.updateMenu(msg, len(m.targets), m.handleExportTargetsMenu)
|
|
case screenInterfacePick:
|
|
return m.updateMenu(msg, len(m.interfaces), m.handleInterfacePickMenu)
|
|
case screenOutput:
|
|
switch msg.String() {
|
|
case "esc", "enter", "q":
|
|
m.screen = m.prevScreen
|
|
m.body = ""
|
|
m.title = ""
|
|
m.pendingAction = actionNone
|
|
return m, nil
|
|
case "ctrl+c":
|
|
return m, tea.Quit
|
|
}
|
|
case screenStaticForm:
|
|
return m.updateStaticForm(msg)
|
|
case screenConfirm:
|
|
return m.updateConfirm(msg)
|
|
}
|
|
if msg.String() == "ctrl+c" {
|
|
return m, tea.Quit
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// updateMain handles keys on the main (two-column) screen.
|
|
func (m model) updateMain(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|
if m.panelFocus {
|
|
return m.updateMainPanel(msg)
|
|
}
|
|
// Switch focus to right panel.
|
|
if (msg.String() == "tab" || msg.String() == "right" || msg.String() == "l") && len(m.panel.Rows) > 0 {
|
|
m.panelFocus = true
|
|
return m, nil
|
|
}
|
|
return m.updateMenu(msg, len(m.mainMenu), m.handleMainMenu)
|
|
}
|
|
|
|
// updateMainPanel handles keys when right panel has focus.
|
|
func (m model) updateMainPanel(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
|
|
switch msg.String() {
|
|
case "up", "k":
|
|
if m.panelCursor > 0 {
|
|
m.panelCursor--
|
|
}
|
|
case "down", "j":
|
|
if m.panelCursor < len(m.panel.Rows)-1 {
|
|
m.panelCursor++
|
|
}
|
|
case "enter":
|
|
if m.panelCursor < len(m.panel.Rows) {
|
|
key := m.panel.Rows[m.panelCursor].Key
|
|
m.busy = true
|
|
m.busyTitle = key
|
|
return m, func() tea.Msg {
|
|
r := m.app.ComponentDetailResult(key)
|
|
return resultMsg{title: r.Title, body: r.Body, back: screenMain}
|
|
}
|
|
}
|
|
case "tab", "left", "h", "esc":
|
|
m.panelFocus = false
|
|
case "q", "ctrl+c":
|
|
return m, tea.Quit
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
func (m model) updateMenu(msg tea.KeyMsg, size int, onEnter func() (tea.Model, tea.Cmd)) (tea.Model, tea.Cmd) {
|
|
if size == 0 {
|
|
size = 1
|
|
}
|
|
switch msg.String() {
|
|
case "up", "k":
|
|
if m.cursor > 0 {
|
|
m.cursor--
|
|
}
|
|
case "down", "j":
|
|
if m.cursor < size-1 {
|
|
m.cursor++
|
|
}
|
|
case "enter":
|
|
return onEnter()
|
|
case "esc":
|
|
switch m.screen {
|
|
case screenNetwork, screenServices:
|
|
m.screen = screenSettings
|
|
m.cursor = 0
|
|
case screenSettings:
|
|
m.screen = screenMain
|
|
m.cursor = 0
|
|
case screenServiceAction:
|
|
m.screen = screenServices
|
|
m.cursor = 0
|
|
case screenExportTargets:
|
|
m.screen = screenMain
|
|
m.cursor = 0
|
|
case screenInterfacePick:
|
|
m.screen = screenNetwork
|
|
m.cursor = 0
|
|
}
|
|
case "q", "ctrl+c":
|
|
return m, tea.Quit
|
|
}
|
|
return m, nil
|
|
}
|