package tui import ( "fmt" "strings" "bee/audit/internal/platform" tea "github.com/charmbracelet/bubbletea" ) func (m model) View() string { if m.busy { title := "bee" if m.busyTitle != "" { title = m.busyTitle } return fmt.Sprintf("%s\n\nWorking...\n\n[ctrl+c] quit\n", title) } switch m.screen { case screenMain: return renderMainMenu("bee", m.banner, "Select action", m.mainMenu, m.cursor) case screenNetwork: return renderMenu("Network", "Select action", m.networkMenu, m.cursor) case screenServices: return renderMenu("Services", "Select service", m.services, m.cursor) case screenServiceAction: items := make([]string, len(m.serviceMenu)) copy(items, m.serviceMenu) return renderMenu("Service: "+m.selectedService, "Select action", items, m.cursor) case screenAcceptance: return renderMenu("Acceptance tests", "Select action", []string{"Run NVIDIA command pack", "Run memory test", "Run storage diagnostic pack", "Back"}, m.cursor) case screenExportTargets: title := "Export audit" if m.pendingAction == actionExportBundle { title = "Export support bundle" } return renderMenu(title, "Select removable filesystem", renderTargetItems(m.targets), m.cursor) case screenInterfacePick: return renderMenu("Interfaces", "Select interface", renderInterfaceItems(m.interfaces), m.cursor) case screenStaticForm: return renderForm("Static IPv4: "+m.selectedIface, m.formFields, m.formIndex) case screenConfirm: title, body := m.confirmBody() return renderConfirm(title, body, m.cursor) case screenOutput: return fmt.Sprintf("%s\n\n%s\n\n[enter/esc] back [ctrl+c] quit\n", m.title, strings.TrimSpace(m.body)) default: return "bee\n" } } func (m model) confirmBody() (string, string) { switch m.pendingAction { case actionExportAudit: if m.selectedTarget == nil { return "Export audit", "No target selected" } return "Export audit", fmt.Sprintf("Copy latest audit JSON to %s?", m.selectedTarget.Device) case actionExportBundle: if m.selectedTarget == nil { return "Export support bundle", "No target selected" } return "Export support bundle", fmt.Sprintf("Copy support bundle archive to %s?", m.selectedTarget.Device) case actionRunNvidiaSAT: return "NVIDIA SAT", "Run NVIDIA acceptance command pack?" case actionRunMemorySAT: return "Memory SAT", "Run runtime memory test with memtester?" case actionRunStorageSAT: return "Storage SAT", "Run storage diagnostic pack and start short self-tests where supported?" default: return "Confirm", "Proceed?" } } func renderTargetItems(targets []platform.RemovableTarget) []string { items := make([]string, 0, len(targets)) for _, target := range targets { desc := fmt.Sprintf("%s [%s %s]", target.Device, target.FSType, target.Size) if target.Label != "" { desc += " label=" + target.Label } if target.Mountpoint != "" { desc += " mounted=" + target.Mountpoint } items = append(items, desc) } return items } func renderInterfaceItems(interfaces []platform.InterfaceInfo) []string { items := make([]string, 0, len(interfaces)) for _, iface := range interfaces { label := iface.Name if len(iface.IPv4) > 0 { label += " [" + strings.Join(iface.IPv4, ", ") + "]" } items = append(items, label) } return items } func renderMenu(title, subtitle string, items []string, cursor int) string { var body strings.Builder fmt.Fprintf(&body, "%s\n\n%s\n\n", title, subtitle) if len(items) == 0 { body.WriteString("(no items)\n") } else { for i, item := range items { prefix := " " if i == cursor { prefix = "> " } fmt.Fprintf(&body, "%s%s\n", prefix, item) } } body.WriteString("\n[↑/↓] move [enter] select [esc] back [ctrl+c] quit\n") return body.String() } func renderMainMenu(title, banner, subtitle string, items []string, cursor int) string { var body strings.Builder fmt.Fprintf(&body, "%s\n\n", title) if banner != "" { body.WriteString(strings.TrimSpace(banner)) body.WriteString("\n\n") } body.WriteString(subtitle) body.WriteString("\n\n") if len(items) == 0 { body.WriteString("(no items)\n") } else { for i, item := range items { prefix := " " if i == cursor { prefix = "> " } fmt.Fprintf(&body, "%s%s\n", prefix, item) } } body.WriteString("\n[↑/↓] move [enter] select [esc] back [ctrl+c] quit\n") return body.String() } func renderForm(title string, fields []formField, idx int) string { var body strings.Builder fmt.Fprintf(&body, "%s\n\n", title) for i, field := range fields { prefix := " " if i == idx { prefix = "> " } fmt.Fprintf(&body, "%s%s: %s\n", prefix, field.Label, field.Value) } body.WriteString("\n[tab/↑/↓] move [enter] next/submit [backspace] delete [esc] cancel\n") return body.String() } func renderConfirm(title, body string, cursor int) string { options := []string{"Confirm", "Cancel"} var out strings.Builder fmt.Fprintf(&out, "%s\n\n%s\n\n", title, body) for i, option := range options { prefix := " " if i == cursor { prefix = "> " } fmt.Fprintf(&out, "%s%s\n", prefix, option) } out.WriteString("\n[←/→/↑/↓] move [enter] select [esc] cancel\n") return out.String() } func resultCmd(title, body string, err error, back screen) tea.Cmd { return func() tea.Msg { return resultMsg{title: title, body: body, err: err, back: back} } }