Files
MuyueWorkspace/internal/tui/dashboard.go
Augustin ROUX cb8e3d0d26
Some checks failed
Stable Release / stable (push) Failing after 22s
feat: complete TUI redesign with cyberpunk theme (#1)
- Dark theme with red accents (cyberpunk aesthetic)
- Epuré cyberpunk style: clean dark backgrounds, sharp red highlights
- Full cyberpunk animations: glitch effect, scan line, typewriter
- Mixed Unicode + ASCII icons
- Rounded borders (╭ ╮ ╯ ╰) on cards and panels
- ASCII art block titles (■) with red styling
- Header: MUYUE branding, status indicators, live clock
- Footer: shortcuts, version, update indicator
- Tab transitions: glitch → scan → typewriter sequence
- Extracted header.go, footer.go, animations.go as new files

Controls unchanged: ctrl+t tabs, ctrl+s sidebar, ctrl+a AI panel

file changes:
- styles.go: new color palette (cyberRed, bgVoid, dimRed), block titles
- types.go: added transition state, clock tick, glitch/scan/done messages
- animations.go: new file with glitch, scan, typewriter, hex stream effects
- header.go: new file with logo, tabs, status dots, live clock
- footer.go: new file with shortcuts, version, update indicator
- model.go: integrated transition state machine, clock updates
- dashboard.go, studio.go, terminal.go, config_tab.go: updated icons/styles

Generated with Crush

Assisted-by: GLM-5.1 via Crush <crush@charm.land>

Co-authored-by: Augustin <muyue@legion-muyue.fr>
Reviewed-on: #1
2026-04-20 20:17:06 +00:00

177 lines
5.2 KiB
Go

package tui
import (
"fmt"
"strings"
"github.com/charmbracelet/lipgloss"
)
func (m Model) renderDashboard() string {
colWidth := m.width / 2
if colWidth < 30 {
colWidth = 30
}
var left, right strings.Builder
left.WriteString(renderSectionHeader("SYSTEM", "[*]"))
left.WriteString("\n")
if m.scanResult != nil {
sysInfo := m.scanResult.System.String()
left.WriteString(" ")
left.WriteString(lipgloss.NewStyle().Foreground(textMain).Render(sysInfo))
}
left.WriteString("\n\n")
left.WriteString(renderSectionHeader("INSTALLED TOOLS", "[+]"))
left.WriteString("\n")
if m.scanResult != nil {
installed := 0
total := len(m.scanResult.Tools)
for _, t := range m.scanResult.Tools {
if t.Installed {
installed++
left.WriteString(" ")
left.WriteString(itemOKStyle.Render("[OK] "))
left.WriteString(lipgloss.NewStyle().Foreground(textMain).Render(t.Name))
left.WriteString(lipgloss.NewStyle().Foreground(dimRed).Render(fmt.Sprintf(" %s", extractVersion(t.Version))))
left.WriteString("\n")
} else {
left.WriteString(" ")
left.WriteString(itemMissingStyle.Render("[--] "))
left.WriteString(lipgloss.NewStyle().Foreground(textMuted).Render(t.Name))
left.WriteString(itemPendingStyle.Render(" (missing)"))
left.WriteString("\n")
}
}
barWidth := 20
pct := 0
if total > 0 {
pct = (installed * barWidth) / total
}
bar := lipgloss.NewStyle().Foreground(cyberRed).Bold(true).Render(strings.Repeat("█", pct)) +
lipgloss.NewStyle().Foreground(dimRed).Render(strings.Repeat("░", barWidth-pct))
left.WriteString(fmt.Sprintf("\n %s %d/%d\n", bar, installed, total))
}
left.WriteString("\n")
if m.installing {
left.WriteString(renderSectionHeader("INSTALLING", "[~]"))
left.WriteString("\n")
progBar := m.progressBar.View()
label := ""
if m.installTool != "" {
label = fmt.Sprintf(" %d/%d - %s", m.installCurrent+1, m.installTotal, m.installTool)
} else {
label = fmt.Sprintf(" %d/%d", m.installCurrent, m.installTotal)
}
left.WriteString(fmt.Sprintf(" %s%s\n", progBar, label))
left.WriteString("\n")
}
if len(m.installLog) > 0 {
left.WriteString(renderSectionHeader("INSTALL LOG", "[#]"))
left.WriteString("\n")
for _, l := range m.installLog {
left.WriteString(l + "\n")
}
left.WriteString("\n")
}
right.WriteString(renderSectionHeader("QUICK ACTIONS", "[!]"))
right.WriteString("\n")
actions := []struct {
key string
desc string
color lipgloss.Color
}{
{"i", "Install missing tools", cyberRed},
{"u", "Check for updates", neonRed},
{"s", "Rescan system", cyberPink},
{"l", "Scan LSP servers", cyberRose},
{"m", "Configure MCP servers", brightRed},
}
for _, a := range actions {
right.WriteString(fmt.Sprintf(" %s %s\n",
lipgloss.NewStyle().Foreground(a.color).Bold(true).Render("["+a.key+"]"),
lipgloss.NewStyle().Foreground(textMain).Render(a.desc)))
}
right.WriteString("\n")
right.WriteString(renderSectionHeader("ACTIVE AGENTS", "[*]"))
right.WriteString("\n")
agents := []struct {
name string
}{
{"Crush"},
{"Claude Code"},
}
for _, a := range agents {
right.WriteString(" ")
right.WriteString(lipgloss.NewStyle().Foreground(dimRed).Render(">> "))
right.WriteString(lipgloss.NewStyle().Foreground(textMuted).Render(a.name + " "))
right.WriteString(itemPendingStyle.Render("[stopped]"))
right.WriteString("\n")
}
right.WriteString("\n")
if len(m.updateStatus) > 0 {
right.WriteString(renderSectionHeader("UPDATES", "[^]"))
right.WriteString("\n")
for _, s := range m.updateStatus {
if s.NeedsUpdate {
right.WriteString(" ")
right.WriteString(itemWarnStyle.Render("[!!] "))
right.WriteString(fmt.Sprintf("%s: %s -> %s\n", s.Tool, s.Current, s.Latest))
} else if s.Error == "" {
right.WriteString(" ")
right.WriteString(itemOKStyle.Render("[OK] "))
right.WriteString(fmt.Sprintf("%s: up to date\n", s.Tool))
}
}
right.WriteString("\n")
}
if len(m.lspServers) > 0 {
right.WriteString(renderSectionHeader("LSP SERVERS", "[L]"))
right.WriteString("\n")
lspInstalled := 0
for _, s := range m.lspServers {
if s.Installed {
lspInstalled++
right.WriteString(" ")
right.WriteString(itemOKStyle.Render("[OK] "))
right.WriteString(fmt.Sprintf("%-22s (%s)\n", s.Name, s.Language))
} else {
right.WriteString(" ")
right.WriteString(itemPendingStyle.Render("[ ] "))
right.WriteString(fmt.Sprintf("%-22s (%s)\n", s.Name, s.Language))
}
}
right.WriteString(fmt.Sprintf("\n %d/%d available\n", lspInstalled, len(m.lspServers)))
right.WriteString("\n")
}
mcpStatus := itemPendingStyle.Render("[ ] not configured")
if m.mcpConfigured {
mcpStatus = itemOKStyle.Render("[OK] configured")
}
right.WriteString(fmt.Sprintf(" MCP: %s\n", mcpStatus))
if m.daemon != nil {
daemonStatus := itemPendingStyle.Render("[ ] stopped")
if m.daemon.IsRunning() {
daemonStatus = itemOKStyle.Render("[OK] running")
}
right.WriteString(fmt.Sprintf(" Daemon: %s\n", daemonStatus))
}
leftCol := lipgloss.NewStyle().Width(colWidth).Render(left.String())
rightCol := lipgloss.NewStyle().Width(colWidth).Render(right.String())
return lipgloss.JoinHorizontal(lipgloss.Top, leftCol, rightCol)
}