Work through TODO fixes #8
@@ -11,6 +11,8 @@ loosely follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
to remove a shared project scratchpad.
|
||||
|
||||
### Changed
|
||||
- The tab bar now shows each visible agent tab's own summary instead
|
||||
of only rendering the focused tab's summary.
|
||||
- Grid-mode `get_process_output` now returns whitespace-normalized
|
||||
text to avoid sending padded terminal rows and repeated blank lines
|
||||
over MCP.
|
||||
|
||||
1
TODO.md
1
TODO.md
@@ -1 +0,0 @@
|
||||
- [ ] The per-tab agent summary text should display below the tab always, not just when the tab is focused.
|
||||
|
||||
@@ -514,7 +514,14 @@ func (st *uiState) dbgf(format string, args ...any) {
|
||||
}
|
||||
|
||||
func (st *uiState) activeSummaryText(width int) string {
|
||||
text := st.activeSummaryRaw()
|
||||
st.mu.Lock()
|
||||
active := st.activeAgentID
|
||||
st.mu.Unlock()
|
||||
return st.summaryTextFor(active, width)
|
||||
}
|
||||
|
||||
func (st *uiState) summaryTextFor(childID string, width int) string {
|
||||
text := st.summaryRawFor(childID)
|
||||
if text == "" || width <= 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -525,7 +532,14 @@ func (st *uiState) activeSummaryText(width int) string {
|
||||
}
|
||||
|
||||
func (st *uiState) activeSummaryRaw() string {
|
||||
if st.summaries == nil {
|
||||
st.mu.Lock()
|
||||
active := st.activeAgentID
|
||||
st.mu.Unlock()
|
||||
return st.summaryRawFor(active)
|
||||
}
|
||||
|
||||
func (st *uiState) summaryRawFor(childID string) string {
|
||||
if st.summaries == nil || childID == "" {
|
||||
return ""
|
||||
}
|
||||
st.settingsMu.Lock()
|
||||
@@ -534,13 +548,7 @@ func (st *uiState) activeSummaryRaw() string {
|
||||
if !enabled {
|
||||
return ""
|
||||
}
|
||||
st.mu.Lock()
|
||||
active := st.activeAgentID
|
||||
st.mu.Unlock()
|
||||
if active == "" {
|
||||
return ""
|
||||
}
|
||||
sum := st.summaries.Summary(active)
|
||||
sum := st.summaries.Summary(childID)
|
||||
text := strings.TrimSpace(sum.Text)
|
||||
if text == "" {
|
||||
return ""
|
||||
|
||||
@@ -52,6 +52,41 @@ func TestWrapSidebarSummaryKeepsWordBoundaries(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryTextForSelectsChildAndClips(t *testing.T) {
|
||||
sess := NewSession(t.TempDir(), "test")
|
||||
cfg := defaultSettings()
|
||||
st := &uiState{
|
||||
sess: sess,
|
||||
settings: cfg,
|
||||
summaries: newSummaryManager(sess, t.TempDir(), preset.Set{}, func() autoSummarySettings {
|
||||
return cfg.AutoSummary.clone()
|
||||
}, nil, nil),
|
||||
}
|
||||
st.summaries.mu.Lock()
|
||||
st.summaries.entries["a1"] = &summaryEntry{state: summaryState{Text: " alpha summary "}}
|
||||
st.summaries.entries["a2"] = &summaryEntry{state: summaryState{Text: "beta summary"}}
|
||||
st.summaries.entries["empty"] = &summaryEntry{state: summaryState{Text: " "}}
|
||||
st.summaries.entries["long"] = &summaryEntry{state: summaryState{Text: "abcdefghijklmnopqrstuvwxyz"}}
|
||||
st.summaries.mu.Unlock()
|
||||
|
||||
if got := st.summaryTextFor("a2", 20); got != "beta summary" {
|
||||
t.Fatalf("summaryTextFor(a2) = %q, want beta summary", got)
|
||||
}
|
||||
if got := st.summaryTextFor("empty", 20); got != "" {
|
||||
t.Fatalf("summaryTextFor(empty) = %q, want empty", got)
|
||||
}
|
||||
if got := st.summaryTextFor("long", 8); got != "abcdefg…" {
|
||||
t.Fatalf("summaryTextFor(long) = %q, want abcdefg…", got)
|
||||
}
|
||||
|
||||
st.settingsMu.Lock()
|
||||
st.settings.AutoSummary.Enabled = false
|
||||
st.settingsMu.Unlock()
|
||||
if got := st.summaryTextFor("a1", 20); got != "" {
|
||||
t.Fatalf("summaryTextFor disabled = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSummaryManagerArmsOnlyTrackedTopLevelAgents(t *testing.T) {
|
||||
sess := NewSession(t.TempDir(), "test")
|
||||
c := newChildEntry("a1", "agent", KindAgent, []string{"fake"}, nil, "", "", "")
|
||||
|
||||
@@ -59,6 +59,7 @@ func (st *uiState) drawTabBar() {
|
||||
newHintW := utf8.RuneCountInString(newHint) + 2 // " + new " framing
|
||||
|
||||
type tabRect struct {
|
||||
childID string
|
||||
startCol int
|
||||
width int
|
||||
label string
|
||||
@@ -66,8 +67,6 @@ func (st *uiState) drawTabBar() {
|
||||
glyphStyle string
|
||||
active bool
|
||||
}
|
||||
activeTab := -1
|
||||
|
||||
// Reserve space at the right edge for "+ new". If there are too
|
||||
// many tabs to fit even at minTabWidth, drop tabs from the right
|
||||
// until they do. The current focus stays visible.
|
||||
@@ -139,6 +138,7 @@ func (st *uiState) drawTabBar() {
|
||||
labelW = utf8.RuneCountInString(label)
|
||||
}
|
||||
tabs = append(tabs, tabRect{
|
||||
childID: c.ID,
|
||||
startCol: col,
|
||||
width: w,
|
||||
label: label,
|
||||
@@ -146,9 +146,6 @@ func (st *uiState) drawTabBar() {
|
||||
glyphStyle: glyphStyle,
|
||||
active: active,
|
||||
})
|
||||
if tabs[len(tabs)-1].active {
|
||||
activeTab = len(tabs) - 1
|
||||
}
|
||||
col += w
|
||||
}
|
||||
}
|
||||
@@ -224,10 +221,9 @@ func (st *uiState) drawTabBar() {
|
||||
hintCol, styleBorder, strings.Repeat("─", newHintW), styleReset)
|
||||
}
|
||||
|
||||
if activeTab >= 0 {
|
||||
tab := tabs[activeTab]
|
||||
for _, tab := range tabs {
|
||||
summaryWidth := tab.width - 2
|
||||
if summary := st.activeSummaryText(summaryWidth); summary != "" {
|
||||
if summary := st.summaryTextFor(tab.childID, summaryWidth); summary != "" {
|
||||
fmt.Fprintf(&b, "\x1b[2;%dH %s%s%s", tab.startCol, styleDim, summary, styleReset)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user