package tui import ( "fmt" "strings" ) // View is PURE: Model → string. No side effects. func View(model Model) string { switch model.Screen { case ScreenMain: return viewMain(model) case ScreenAgentList: return viewAgentList(model) case ScreenAgentActions: return viewAgentActions(model) case ScreenLogs: return viewLogs(model) case ScreenServer: return viewServer(model) default: return "" } } func viewMain(m Model) string { var b strings.Builder b.WriteString("\n Bot Server Dashboard\n") b.WriteString(" " + strings.Repeat("─", 36) + "\n") // Summary running, stopped, disabled := countStatuses(m.Agents) total := len(m.Agents) if total > 0 { b.WriteString(fmt.Sprintf(" %d agents (%d running, %d stopped, %d disabled)\n\n", total, running, stopped, disabled)) } else { b.WriteString(" Loading...\n\n") } // Menu for i, opt := range MainMenuOptions() { cursor := " " if i == m.Cursor { cursor = "> " } b.WriteString(fmt.Sprintf(" %s%-16s %s\n", cursor, opt.Label, opt.Desc)) } b.WriteString("\n ↑↓ navegar enter seleccionar q salir\n") return b.String() } func viewAgentList(m Model) string { var b strings.Builder b.WriteString("\n Agents\n") b.WriteString(" " + strings.Repeat("─", 60) + "\n") if len(m.Agents) == 0 { b.WriteString(" No agents found.\n") } for i, a := range m.Agents { cursor := " " if i == m.Cursor { cursor = "> " } icon := "○" status := "stopped" if !a.Enabled { icon = " " status = "disabled" } else if a.Running { icon = "●" status = fmt.Sprintf("running PID %d", a.PID) } b.WriteString(fmt.Sprintf(" %s%s %-20s %-8s %s\n", cursor, icon, a.ID, a.Version, status)) if a.Instances > 1 { b.WriteString(fmt.Sprintf(" ⚠ WARNING: %d instances running!\n", a.Instances)) } } if m.StatusMsg != "" { b.WriteString("\n " + m.StatusMsg + "\n") } b.WriteString("\n ↑↓ navegar enter acciones 0 volver\n") return b.String() } func viewAgentActions(m Model) string { var b strings.Builder if m.Selected == nil { return " No agent selected.\n" } a := m.Selected icon := "○ stopped" if a.Running { icon = fmt.Sprintf("● running PID %d", a.PID) } b.WriteString(fmt.Sprintf("\n %s %s\n", a.ID, icon)) b.WriteString(" " + strings.Repeat("─", 44) + "\n") // Stats line if running if a.Running && (a.Memory != "" || a.CPU != "") { parts := []string{} if a.Uptime != "" { parts = append(parts, "uptime: "+a.Uptime) } if a.Memory != "" { parts = append(parts, "mem: "+a.Memory) } if a.CPU != "" { parts = append(parts, "cpu: "+a.CPU) } if a.LogSize != "" { parts = append(parts, "log: "+a.LogSize) } b.WriteString(" " + strings.Join(parts, " ") + "\n") } b.WriteString("\n") opts := AgentActionOptions(a.Running) for i, opt := range opts { cursor := " " if i == m.Cursor { cursor = "> " } b.WriteString(fmt.Sprintf(" %s%-16s %s\n", cursor, opt.Label, opt.Desc)) } if m.StatusMsg != "" { b.WriteString("\n " + m.StatusMsg + "\n") } b.WriteString("\n ↑↓ navegar enter ejecutar 0 volver\n") return b.String() } func viewLogs(m Model) string { var b strings.Builder agentID := "?" if m.Selected != nil { agentID = m.Selected.ID } b.WriteString(fmt.Sprintf("\n %s — Logs\n", agentID)) b.WriteString(" " + strings.Repeat("─", 60) + "\n") if len(m.LogLines) == 0 { b.WriteString(" (no log data)\n") } else { visible := visibleLogLines(m) end := m.LogScroll + visible if end > len(m.LogLines) { end = len(m.LogLines) } start := m.LogScroll if start >= len(m.LogLines) { start = max(0, len(m.LogLines)-1) } for _, line := range m.LogLines[start:end] { // Truncate long lines if len(line) > m.WindowWidth-4 && m.WindowWidth > 10 { line = line[:m.WindowWidth-7] + "..." } b.WriteString(" " + line + "\n") } } b.WriteString("\n ↑↓ scroll r recargar 0 volver\n") return b.String() } func viewServer(m Model) string { var b strings.Builder b.WriteString("\n Server Management\n") b.WriteString(" " + strings.Repeat("─", 44) + "\n") // Summary running, stopped, disabled := countStatuses(m.Agents) total := len(m.Agents) if total > 0 { b.WriteString(fmt.Sprintf(" %d agents: %d running, %d stopped, %d disabled\n", total, running, stopped, disabled)) } else { b.WriteString(" Loading...\n") } // Agent status list (compact) if total > 0 { b.WriteString("\n") for _, a := range m.Agents { icon := "○" if !a.Enabled { icon = " " } else if a.Running { icon = "●" } b.WriteString(fmt.Sprintf(" %s %s\n", icon, a.ID)) } } b.WriteString("\n") // Action menu for i, opt := range ServerMenuOptions() { cursor := " " if i == m.Cursor { cursor = "> " } b.WriteString(fmt.Sprintf(" %s%-16s %s\n", cursor, opt.Label, opt.Desc)) } if m.StatusMsg != "" { b.WriteString("\n " + m.StatusMsg + "\n") } b.WriteString("\n ↑↓ navegar enter ejecutar 0 volver\n") return b.String() } func countStatuses(agents []AgentView) (running, stopped, disabled int) { for _, a := range agents { switch { case !a.Enabled: disabled++ case a.Running: running++ default: stopped++ } } return }