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.
|
to remove a shared project scratchpad.
|
||||||
|
|
||||||
### Changed
|
### 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
|
- Grid-mode `get_process_output` now returns whitespace-normalized
|
||||||
text to avoid sending padded terminal rows and repeated blank lines
|
text to avoid sending padded terminal rows and repeated blank lines
|
||||||
over MCP.
|
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 {
|
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 {
|
if text == "" || width <= 0 {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
@@ -525,7 +532,14 @@ func (st *uiState) activeSummaryText(width int) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (st *uiState) activeSummaryRaw() 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 ""
|
return ""
|
||||||
}
|
}
|
||||||
st.settingsMu.Lock()
|
st.settingsMu.Lock()
|
||||||
@@ -534,13 +548,7 @@ func (st *uiState) activeSummaryRaw() string {
|
|||||||
if !enabled {
|
if !enabled {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
st.mu.Lock()
|
sum := st.summaries.Summary(childID)
|
||||||
active := st.activeAgentID
|
|
||||||
st.mu.Unlock()
|
|
||||||
if active == "" {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
sum := st.summaries.Summary(active)
|
|
||||||
text := strings.TrimSpace(sum.Text)
|
text := strings.TrimSpace(sum.Text)
|
||||||
if text == "" {
|
if text == "" {
|
||||||
return ""
|
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) {
|
func TestSummaryManagerArmsOnlyTrackedTopLevelAgents(t *testing.T) {
|
||||||
sess := NewSession(t.TempDir(), "test")
|
sess := NewSession(t.TempDir(), "test")
|
||||||
c := newChildEntry("a1", "agent", KindAgent, []string{"fake"}, nil, "", "", "")
|
c := newChildEntry("a1", "agent", KindAgent, []string{"fake"}, nil, "", "", "")
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ func (st *uiState) drawTabBar() {
|
|||||||
newHintW := utf8.RuneCountInString(newHint) + 2 // " + new " framing
|
newHintW := utf8.RuneCountInString(newHint) + 2 // " + new " framing
|
||||||
|
|
||||||
type tabRect struct {
|
type tabRect struct {
|
||||||
|
childID string
|
||||||
startCol int
|
startCol int
|
||||||
width int
|
width int
|
||||||
label string
|
label string
|
||||||
@@ -66,8 +67,6 @@ func (st *uiState) drawTabBar() {
|
|||||||
glyphStyle string
|
glyphStyle string
|
||||||
active bool
|
active bool
|
||||||
}
|
}
|
||||||
activeTab := -1
|
|
||||||
|
|
||||||
// Reserve space at the right edge for "+ new". If there are too
|
// Reserve space at the right edge for "+ new". If there are too
|
||||||
// many tabs to fit even at minTabWidth, drop tabs from the right
|
// many tabs to fit even at minTabWidth, drop tabs from the right
|
||||||
// until they do. The current focus stays visible.
|
// until they do. The current focus stays visible.
|
||||||
@@ -139,6 +138,7 @@ func (st *uiState) drawTabBar() {
|
|||||||
labelW = utf8.RuneCountInString(label)
|
labelW = utf8.RuneCountInString(label)
|
||||||
}
|
}
|
||||||
tabs = append(tabs, tabRect{
|
tabs = append(tabs, tabRect{
|
||||||
|
childID: c.ID,
|
||||||
startCol: col,
|
startCol: col,
|
||||||
width: w,
|
width: w,
|
||||||
label: label,
|
label: label,
|
||||||
@@ -146,9 +146,6 @@ func (st *uiState) drawTabBar() {
|
|||||||
glyphStyle: glyphStyle,
|
glyphStyle: glyphStyle,
|
||||||
active: active,
|
active: active,
|
||||||
})
|
})
|
||||||
if tabs[len(tabs)-1].active {
|
|
||||||
activeTab = len(tabs) - 1
|
|
||||||
}
|
|
||||||
col += w
|
col += w
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,10 +221,9 @@ func (st *uiState) drawTabBar() {
|
|||||||
hintCol, styleBorder, strings.Repeat("─", newHintW), styleReset)
|
hintCol, styleBorder, strings.Repeat("─", newHintW), styleReset)
|
||||||
}
|
}
|
||||||
|
|
||||||
if activeTab >= 0 {
|
for _, tab := range tabs {
|
||||||
tab := tabs[activeTab]
|
|
||||||
summaryWidth := tab.width - 2
|
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)
|
fmt.Fprintf(&b, "\x1b[2;%dH %s%s%s", tab.startCol, styleDim, summary, styleReset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user