fix: ensure header visibility with explicit newline and nil checks

This commit is contained in:
大森 2026-05-10 15:00:41 +08:00
parent 8619a7f35c
commit 9fec5df6f7
2 changed files with 69 additions and 47 deletions

View File

@ -212,17 +212,18 @@ func (m *Model) updateLayout() {
rightWidth := 42 rightWidth := 42
leftWidth := m.width - rightWidth - 3 leftWidth := m.width - rightWidth - 3
headerHeight := 3
mainHeight := m.height - headerHeight - 1 headerHeight := 2
inputHeight := 5 mainHeight := m.height - headerHeight
chatHeight := mainHeight - inputHeight if mainHeight < 10 {
if chatHeight < 10 { mainHeight = 10
chatHeight = 10 }
chatHeight := mainHeight - 9
if chatHeight < 5 {
chatHeight = 5
} }
m.textarea.SetWidth(leftWidth) m.textarea.SetWidth(leftWidth)
m.viewport.Width = leftWidth
m.viewport.Height = chatHeight
if !m.ready { if !m.ready {
m.viewport = viewport.New(leftWidth, chatHeight) m.viewport = viewport.New(leftWidth, chatHeight)
@ -265,33 +266,18 @@ func (m Model) View() string {
return "Initializing..." return "Initializing..."
} }
rightWidth := 42 rightWidth := 40
leftWidth := m.width - rightWidth - 3 leftWidth := m.width - rightWidth - 2
header := m.renderHeader() header := m.renderHeader()
headerHeight := lipgloss.Height(header)
mainHeight := m.height - headerHeight
inputHeight := 5
chatHeight := mainHeight - inputHeight
if chatHeight < 10 {
chatHeight = 10
}
chatBox := boxStyle.Width(leftWidth).Height(chatHeight).Render(m.viewport.View()) chatBox := boxStyle.Width(leftWidth).Height(m.height - 8).Render(m.viewport.View())
inputBox := boxStyle.Width(leftWidth).Render(m.textarea.View()) inputBox := boxStyle.Width(leftWidth).Render(m.textarea.View())
leftPanel := lipgloss.JoinVertical(lipgloss.Left, chatBox, inputBox) leftPanel := lipgloss.JoinVertical(lipgloss.Left, chatBox, inputBox)
stats := m.renderStats() stats := m.renderStats()
agents := m.renderAgents() agents := m.renderAgents()
empty := boxStyle.Width(rightWidth - 2).Height(m.height - lipgloss.Height(stats) - lipgloss.Height(agents) - 5).Render("")
statsHeight := lipgloss.Height(stats)
agentsHeight := lipgloss.Height(agents)
remainingHeight := mainHeight - statsHeight - agentsHeight - 2
if remainingHeight < 3 {
remainingHeight = 3
}
empty := boxStyle.Width(rightWidth - 4).Height(remainingHeight).Render("")
rightPanel := lipgloss.JoinVertical(lipgloss.Left, stats, agents, empty) rightPanel := lipgloss.JoinVertical(lipgloss.Left, stats, agents, empty)
if m.loading { if m.loading {
@ -299,19 +285,21 @@ func (m Model) View() string {
} }
body := lipgloss.JoinHorizontal(lipgloss.Top, leftPanel, rightPanel) body := lipgloss.JoinHorizontal(lipgloss.Top, leftPanel, rightPanel)
return lipgloss.JoinVertical(lipgloss.Left, header, body) return header + "\n" + body
} }
func (m Model) renderHeader() string { func (m Model) renderHeader() string {
version := "v0.1.0" version := "v0.1.0"
title := titleStyle.Render("orca.agent ") + mutedStyle.Render(version) title := "orca.agent " + version
line := strings.Repeat("─", m.width-2)
return lipgloss.NewStyle(). return lipgloss.NewStyle().
Width(m.width). Width(m.width).
Padding(0, 1). Padding(0, 1).
BorderBottom(true). Bold(true).
BorderStyle(lipgloss.NormalBorder()). Foreground(lipgloss.Color(colors.primary)).
BorderForeground(lipgloss.Color(colors.border)). Render(title) + "\n" + lipgloss.NewStyle().
Render(title) Foreground(lipgloss.Color(colors.border)).
Render(line)
} }
func (m Model) renderStats() string { func (m Model) renderStats() string {
@ -319,16 +307,22 @@ func (m Model) renderStats() string {
b.WriteString(titleStyle.Render("Statistics") + "\n\n") b.WriteString(titleStyle.Render("Statistics") + "\n\n")
tools := 0 tools := 0
if tm := m.kernel.ToolManager(); tm != nil { if m.kernel != nil {
tools = tm.Count() if tm := m.kernel.ToolManager(); tm != nil {
tools = tm.Count()
}
} }
skills := 0 skills := 0
if sm := m.kernel.SkillManager(); sm != nil { if m.kernel != nil {
skills = len(sm.ListSkills()) if sm := m.kernel.SkillManager(); sm != nil {
skills = len(sm.ListSkills())
}
} }
agents := 0 agents := 0
if as := m.kernel.ActorSystem(); as != nil { if m.kernel != nil {
agents = as.AgentCount() if as := m.kernel.ActorSystem(); as != nil {
agents = as.AgentCount()
}
} }
b.WriteString(statLabelStyle.Render("Tools: ")) b.WriteString(statLabelStyle.Render("Tools: "))
@ -345,15 +339,17 @@ func (m Model) renderAgents() string {
var b strings.Builder var b strings.Builder
b.WriteString(titleStyle.Render("Active Agents") + "\n\n") b.WriteString(titleStyle.Render("Active Agents") + "\n\n")
if as := m.kernel.ActorSystem(); as != nil { if m.kernel != nil {
for _, info := range as.AgentInfos() { if as := m.kernel.ActorSystem(); as != nil {
status := "idle" for _, info := range as.AgentInfos() {
style := idleAgentStyle status := "idle"
if info.Status == actor.StatusProcessing || m.agentRuns[info.ID] { style := idleAgentStyle
status = "running" if info.Status == actor.StatusProcessing || m.agentRuns[info.ID] {
style = activeAgentStyle status = "running"
style = activeAgentStyle
}
b.WriteString(fmt.Sprintf("• %s: %s\n", info.ID, style.Render(status)))
} }
b.WriteString(fmt.Sprintf("• %s: %s\n", info.ID, style.Render(status)))
} }
} }

View File

@ -1,8 +1,11 @@
package tui package tui
import ( import (
"strings"
"testing" "testing"
"time" "time"
"github.com/charmbracelet/bubbles/textarea"
) )
func TestEventWriter(t *testing.T) { func TestEventWriter(t *testing.T) {
@ -74,3 +77,26 @@ func TestModelFormatMessage(t *testing.T) {
t.Error("system message should not be empty") t.Error("system message should not be empty")
} }
} }
func TestViewContainsHeader(t *testing.T) {
ta := textarea.New()
ta.ShowLineNumbers = false
ta.KeyMap.InsertNewline.SetEnabled(false)
m := Model{
width: 120,
height: 40,
textarea: ta,
messages: []ChatMessage{},
events: make(chan Event, 10),
agentRuns: make(map[string]bool),
}
m.updateLayout()
view := m.View()
if view == "Initializing..." {
t.Fatal("view should not be initializing")
}
if !strings.Contains(view, "orca.agent") {
t.Errorf("view should contain 'orca.agent', got:\n%s", view[:min(200, len(view))])
}
}