fix: enable text selection, dashboard multi-column layout
All checks were successful
CI / build (push) Successful in 1m44s
All checks were successful
CI / build (push) Successful in 1m44s
- Remove WithMouseCellMotion to allow text selection in terminal - Dashboard now uses 2-column layout (left: system/tools, right: actions/status) - More compact dashboard with better space usage 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush <crush@charm.land>
This commit is contained in:
@@ -108,7 +108,7 @@ func runTUI() {
|
||||
result := scanner.ScanSystem()
|
||||
|
||||
model := tui.NewModel(cfg, result)
|
||||
p := tea.NewProgram(model, tea.WithAltScreen(), tea.WithMouseCellMotion())
|
||||
p := tea.NewProgram(model, tea.WithAltScreen())
|
||||
|
||||
if _, err := p.Run(); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
|
||||
@@ -1173,32 +1173,37 @@ func (m Model) renderTermInput() string {
|
||||
}
|
||||
|
||||
func (m Model) renderDashboard() string {
|
||||
var b strings.Builder
|
||||
colWidth := m.width / 2
|
||||
if colWidth < 30 {
|
||||
colWidth = 30
|
||||
}
|
||||
|
||||
b.WriteString(sectionStyle.Render("System"))
|
||||
b.WriteString("\n")
|
||||
var left, right strings.Builder
|
||||
|
||||
left.WriteString(sectionStyle.Render("System"))
|
||||
left.WriteString("\n")
|
||||
if m.scanResult != nil {
|
||||
sysInfo := m.scanResult.System.String()
|
||||
b.WriteString(" ")
|
||||
b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("#E0E0E0")).Render(sysInfo))
|
||||
left.WriteString(" ")
|
||||
left.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("#E0E0E0")).Render(sysInfo))
|
||||
}
|
||||
b.WriteString("\n\n")
|
||||
left.WriteString("\n\n")
|
||||
|
||||
b.WriteString(sectionStyle.Render("Tools"))
|
||||
b.WriteString("\n")
|
||||
left.WriteString(sectionStyle.Render("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++
|
||||
b.WriteString(" ")
|
||||
b.WriteString(itemOKStyle.Render(" "))
|
||||
b.WriteString(fmt.Sprintf(" %-12s %s\n", t.Name, extractVersion(t.Version)))
|
||||
left.WriteString(" ")
|
||||
left.WriteString(itemOKStyle.Render(" "))
|
||||
left.WriteString(fmt.Sprintf(" %-12s %s\n", t.Name, extractVersion(t.Version)))
|
||||
} else {
|
||||
b.WriteString(" ")
|
||||
b.WriteString(itemMissingStyle.Render(" "))
|
||||
b.WriteString(fmt.Sprintf(" %-12s %s\n", t.Name, itemPendingStyle.Render("(not installed)")))
|
||||
left.WriteString(" ")
|
||||
left.WriteString(itemMissingStyle.Render(" "))
|
||||
left.WriteString(fmt.Sprintf(" %-12s %s\n", t.Name, itemPendingStyle.Render("(not installed)")))
|
||||
}
|
||||
}
|
||||
barWidth := 20
|
||||
@@ -1208,30 +1213,13 @@ func (m Model) renderDashboard() string {
|
||||
}
|
||||
bar := lipgloss.NewStyle().Foreground(successColor).Render(strings.Repeat("█", pct)) +
|
||||
lipgloss.NewStyle().Foreground(dimColor).Render(strings.Repeat("░", barWidth-pct))
|
||||
b.WriteString(fmt.Sprintf("\n %s %d/%d tools installed\n", bar, installed, total))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
|
||||
if len(m.updateStatus) > 0 {
|
||||
b.WriteString(sectionStyle.Render("Updates"))
|
||||
b.WriteString("\n")
|
||||
for _, s := range m.updateStatus {
|
||||
if s.NeedsUpdate {
|
||||
b.WriteString(" ")
|
||||
b.WriteString(itemWarnStyle.Render(" "))
|
||||
b.WriteString(fmt.Sprintf(" %s: %s -> %s\n", s.Tool, s.Current, s.Latest))
|
||||
} else if s.Error == "" {
|
||||
b.WriteString(" ")
|
||||
b.WriteString(itemOKStyle.Render(" "))
|
||||
b.WriteString(fmt.Sprintf(" %s: up to date\n", s.Tool))
|
||||
}
|
||||
}
|
||||
b.WriteString("\n")
|
||||
left.WriteString(fmt.Sprintf("\n %s %d/%d\n", bar, installed, total))
|
||||
}
|
||||
left.WriteString("\n")
|
||||
|
||||
if m.installing {
|
||||
b.WriteString(sectionStyle.Render("Installing..."))
|
||||
b.WriteString("\n")
|
||||
left.WriteString(sectionStyle.Render("Installing..."))
|
||||
left.WriteString("\n")
|
||||
progBar := m.progressBar.View()
|
||||
label := ""
|
||||
if m.installTool != "" {
|
||||
@@ -1239,19 +1227,21 @@ func (m Model) renderDashboard() string {
|
||||
} else {
|
||||
label = fmt.Sprintf(" %d/%d", m.installCurrent, m.installTotal)
|
||||
}
|
||||
b.WriteString(fmt.Sprintf(" %s%s\n", progBar, label))
|
||||
b.WriteString("\n")
|
||||
left.WriteString(fmt.Sprintf(" %s%s\n", progBar, label))
|
||||
left.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(m.installLog) > 0 {
|
||||
b.WriteString(sectionStyle.Render("Install Log"))
|
||||
b.WriteString("\n")
|
||||
left.WriteString(sectionStyle.Render("Install Log"))
|
||||
left.WriteString("\n")
|
||||
for _, l := range m.installLog {
|
||||
b.WriteString(l + "\n")
|
||||
left.WriteString(l + "\n")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
left.WriteString("\n")
|
||||
}
|
||||
|
||||
right.WriteString(sectionStyle.Render("Quick Actions"))
|
||||
right.WriteString("\n")
|
||||
actions := []struct {
|
||||
key string
|
||||
desc string
|
||||
@@ -1262,68 +1252,77 @@ func (m Model) renderDashboard() string {
|
||||
{"l", "Scan LSP servers"},
|
||||
{"m", "Configure MCP servers"},
|
||||
}
|
||||
b.WriteString(sectionStyle.Render("Quick Actions"))
|
||||
b.WriteString("\n")
|
||||
for _, a := range actions {
|
||||
b.WriteString(fmt.Sprintf(" %s %s\n",
|
||||
right.WriteString(fmt.Sprintf(" %s %s\n",
|
||||
lipgloss.NewStyle().Foreground(baseColor).Bold(true).Render("["+a.key+"]"),
|
||||
a.desc))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
right.WriteString("\n")
|
||||
|
||||
if len(m.updateStatus) > 0 {
|
||||
right.WriteString(sectionStyle.Render("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(" "))
|
||||
right.WriteString(fmt.Sprintf(" %s: up to date\n", s.Tool))
|
||||
}
|
||||
}
|
||||
right.WriteString("\n")
|
||||
}
|
||||
|
||||
if len(m.lspServers) > 0 {
|
||||
b.WriteString(sectionStyle.Render("LSP Servers"))
|
||||
b.WriteString("\n")
|
||||
right.WriteString(sectionStyle.Render("LSP Servers"))
|
||||
right.WriteString("\n")
|
||||
lspInstalled := 0
|
||||
for _, s := range m.lspServers {
|
||||
if s.Installed {
|
||||
lspInstalled++
|
||||
b.WriteString(" ")
|
||||
b.WriteString(itemOKStyle.Render(" "))
|
||||
b.WriteString(fmt.Sprintf(" %-28s (%s)\n", s.Name, s.Language))
|
||||
right.WriteString(" ")
|
||||
right.WriteString(itemOKStyle.Render(" "))
|
||||
right.WriteString(fmt.Sprintf(" %-22s (%s)\n", s.Name, s.Language))
|
||||
} else {
|
||||
b.WriteString(" ")
|
||||
b.WriteString(itemPendingStyle.Render(" "))
|
||||
b.WriteString(fmt.Sprintf(" %-28s (%s)\n", s.Name, s.Language))
|
||||
right.WriteString(" ")
|
||||
right.WriteString(itemPendingStyle.Render(" "))
|
||||
right.WriteString(fmt.Sprintf(" %-22s (%s)\n", s.Name, s.Language))
|
||||
}
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("\n Installed: %d/%d\n", lspInstalled, len(m.lspServers)))
|
||||
b.WriteString("\n")
|
||||
right.WriteString(fmt.Sprintf("\n Installed: %d/%d\n", lspInstalled, len(m.lspServers)))
|
||||
right.WriteString("\n")
|
||||
}
|
||||
|
||||
if m.daemon != nil {
|
||||
b.WriteString(sectionStyle.Render("Update Daemon"))
|
||||
b.WriteString("\n")
|
||||
right.WriteString(sectionStyle.Render("Daemon"))
|
||||
right.WriteString("\n")
|
||||
if m.daemon.IsRunning() {
|
||||
b.WriteString(" ")
|
||||
b.WriteString(itemOKStyle.Render("running"))
|
||||
right.WriteString(" ")
|
||||
right.WriteString(itemOKStyle.Render("running"))
|
||||
lastCheck := m.daemon.LastCheck()
|
||||
if !lastCheck.IsZero() {
|
||||
b.WriteString(fmt.Sprintf(" last check: %s", lastCheck.Format("15:04:05")))
|
||||
right.WriteString(fmt.Sprintf(" last: %s", lastCheck.Format("15:04:05")))
|
||||
}
|
||||
} else {
|
||||
b.WriteString(" ")
|
||||
b.WriteString(itemPendingStyle.Render("stopped"))
|
||||
right.WriteString(" ")
|
||||
right.WriteString(itemPendingStyle.Render("stopped"))
|
||||
}
|
||||
b.WriteString("\n")
|
||||
|
||||
logs := m.daemon.Logs()
|
||||
if len(logs) > 3 {
|
||||
logs = logs[len(logs)-3:]
|
||||
}
|
||||
for _, l := range logs {
|
||||
b.WriteString(" " + itemPendingStyle.Render(l) + "\n")
|
||||
}
|
||||
b.WriteString("\n")
|
||||
right.WriteString("\n\n")
|
||||
}
|
||||
|
||||
mcpStatus := itemPendingStyle.Render("not configured")
|
||||
if m.mcpConfigured {
|
||||
mcpStatus = itemOKStyle.Render("configured")
|
||||
}
|
||||
b.WriteString(fmt.Sprintf("MCP Servers: %s\n", mcpStatus))
|
||||
right.WriteString(fmt.Sprintf("MCP: %s\n", mcpStatus))
|
||||
|
||||
return b.String()
|
||||
leftCol := lipgloss.NewStyle().Width(colWidth).Render(left.String())
|
||||
rightCol := lipgloss.NewStyle().Width(colWidth).Render(right.String())
|
||||
|
||||
return lipgloss.JoinHorizontal(lipgloss.Top, leftCol, rightCol)
|
||||
}
|
||||
|
||||
func (m Model) renderChat() string {
|
||||
|
||||
Reference in New Issue
Block a user