package tui import ( "fmt" "strings" "github.com/charmbracelet/lipgloss" "github.com/muyue/muyue/internal/version" ) func (m Model) renderHeader() string { var tabs []string for i, name := range tabNames { icon := tabIcons[i] if tab(i) == m.activeTab { tabStyle := lipgloss.NewStyle(). Background(cyberRed). Foreground(lipgloss.Color("#FFFFFF")). Bold(true). Padding(0, 2) tabs = append(tabs, tabStyle.Render(icon+" "+name)) } else { tabStyle := lipgloss.NewStyle(). Background(bgSurface). Foreground(textDim). Padding(0, 2) tabs = append(tabs, tabStyle.Render(icon+" "+name)) } } tabLine := tabBarStyle.Render(lipgloss.JoinHorizontal(lipgloss.Bottom, tabs...)) timeStr := "" if !m.currentTime.IsZero() { timeStr = m.currentTime.Format("15:04:05") } dateStr := "" if !m.currentTime.IsZero() { dateStr = m.currentTime.Format("02/01/2006") } rightInfo := lipgloss.NewStyle().Background(bgSurface).Padding(0, 1).Render( lipgloss.JoinHorizontal(lipgloss.Center, lipgloss.NewStyle().Foreground(textDim).Render(dateStr+" "), lipgloss.NewStyle().Foreground(cyberRed).Bold(true).Render(timeStr), lipgloss.NewStyle().Foreground(textMuted).Render(" "+getAnimFrame(m.animationFrame)), ), ) statusDots := "" if m.config != nil { hasAI := false for _, p := range m.config.AI.Providers { if p.Active && p.APIKey != "" { hasAI = true break } } if hasAI { statusDots += lipgloss.NewStyle().Foreground(successGreen).Render("●") } else { statusDots += lipgloss.NewStyle().Foreground(errorRed).Render("●") } } else { statusDots += lipgloss.NewStyle().Foreground(warnAmber).Render("●") } statusDots += lipgloss.NewStyle().Foreground(textMuted).Render(" ") if m.mcpConfigured { statusDots += lipgloss.NewStyle().Foreground(successGreen).Render("●") } else { statusDots += lipgloss.NewStyle().Foreground(warnAmber).Render("●") } statusInfo := lipgloss.NewStyle().Background(bgSurface).Padding(0, 1).Render( lipgloss.JoinHorizontal(lipgloss.Center, lipgloss.NewStyle().Foreground(textDim).Render("SYS "), statusDots, ), ) badge := lipgloss.NewStyle().Foreground(cyberRed).Bold(true).Render("MUYUE") versionBadge := lipgloss.NewStyle().Foreground(dimRed).Render("v" + version.Version) logoLine := lipgloss.NewStyle().Background(bgVoid).Padding(0, 1).Render( lipgloss.JoinHorizontal(lipgloss.Center, badge, " ", versionBadge), ) topLine := lipgloss.JoinHorizontal(lipgloss.Bottom, logoLine, strings.Repeat(" ", max(0, m.width-lipgloss.Width(logoLine)-lipgloss.Width(rightInfo)-lipgloss.Width(statusInfo))), statusInfo, rightInfo, ) return lipgloss.JoinVertical(lipgloss.Left, topLine, tabLine) } func (m Model) renderTabMenuOverlay() string { menuStyle := lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(cyberRed). Background(bgCard). Padding(1, 3) tabItemStyle := lipgloss.NewStyle(). Foreground(textDim). Padding(0, 2) tabItemActiveStyle := lipgloss.NewStyle(). Foreground(lipgloss.Color("#FFFFFF")). Background(cyberRed). Bold(true). Padding(0, 2) descs := []string{ "tools, updates & system status", "chat, agents & workflows", "terminal + AI assistant", "profile, API keys & settings", } var items []string for i, name := range tabNames { num := lipgloss.NewStyle().Foreground(dimRed).Render(fmt.Sprintf(" %d.", i+1)) icon := tabIcons[i] + " " if i == m.tabMenuCursor { item := fmt.Sprintf("%s %s%-10s %s", num, icon, name, lipgloss.NewStyle().Foreground(cyberRose).Render(descs[i])) items = append(items, tabItemActiveStyle.Render(">"+item)) } else { item := fmt.Sprintf("%s %s%-10s %s", num, icon, name, lipgloss.NewStyle().Foreground(textMuted).Render(descs[i])) items = append(items, tabItemStyle.Render(" "+item)) } } header := lipgloss.NewStyle().Foreground(cyberRed).Bold(true).Render("SWITCH TAB") content := header + "\n\n" + strings.Join(items, "\n") + "\n\n" + lipgloss.NewStyle().Foreground(textMuted).Render("up/down navigate | enter select | esc cancel") box := menuStyle.Render(content) return lipgloss.Place(m.width, m.height, 0.5, 0.5, box, lipgloss.WithWhitespaceBackground(bgVoid), lipgloss.WithWhitespaceForeground(textMuted), ) } func (m Model) renderQuitOverlay() string { yesStyle := confirmNoStyle noStyle := confirmYesStyle if m.confirmCursor == 0 { yesStyle = confirmYesStyle noStyle = confirmNoStyle } frame := lipgloss.NewStyle().Foreground(cyberRed).Render(getAnimFrame(m.animationFrame)) box := fmt.Sprintf("\n\n %s Quit muyue?\n\n %s %s", frame, yesStyle.Render("[ Yes ]"), noStyle.Render("[ No ]"), ) content := confirmBoxStyle.Render(box) return lipgloss.Place(m.width, m.height, 0.5, 0.5, content, lipgloss.WithWhitespaceBackground(bgVoid), lipgloss.WithWhitespaceForeground(textMuted), ) }