Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ public nonisolated struct SidebarSessionTreeState: Hashable, Sendable {
return false
}

/// Whether a patch from `old` to `new` changes a folded ancestor's roll-up: `new` hides in a fold
/// and its counted state moved. A hidden session's step, title or preview (many a second while
/// it works) changes nothing drawn, so it lays nothing out (ask-268456b5a0).
public func patchChangesRollUp(from old: SidebarWorkspace?, to new: SidebarWorkspace) -> Bool {
guard hidesInFold(new.id) else { return false }
guard let old else { return true }
let lane = states[new.id]
return SidebarAgentState.of(old, lane: lane) != SidebarAgentState.of(new, lane: lane)
}

/// What the order and the Command-numbers need: the parents and the folds.
public var shape: SidebarSessionTreeState { SidebarSessionTreeState(parents: parents, collapsed: collapsed) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ extension SidebarListView {
let shownGroupCard = hoverCard.shownGroupID
for ws in patched {
// The hover cards show no "Now:" step: a working agent's new step (many a second) rebuilds no card.
let cardChanged = workspaces[ws.id].map { var old = $0; old.agentStep = ws.agentStep; return old != ws } ?? true
let previous = workspaces[ws.id]
let cardChanged = previous.map { var old = $0; old.agentStep = ws.agentStep; return old != ws } ?? true
workspaces[ws.id] = ws
for path in model.rowIndex?.paths(of: ws.id) ?? [] {
let section = model.sections[path.section]
Expand All @@ -133,8 +134,9 @@ extension SidebarListView {
}
if cardChanged, shownCard == ws.id { hoverCards.contentChanged(WorkspaceHoverCardController.targetID(ws.id)) }
guard let row = displayed.row(for: .workspace(ws.id)), let section = sections[row.section] else {
// A session a folded parent hides: the parent's roll-up counts it, so the list lays out again.
if SidebarSessionTree.shared.drawn?.hidesInFold(ws.id) == true { heightChanged = true }
// A session a folded parent hides: the parent's roll-up counts its state, so the list lays out
// again when that state moves, not on each step or title it reports.
if SidebarSessionTree.shared.drawn?.patchChangesRollUp(from: previous, to: ws) == true { heightChanged = true }
continue
}
let o = options ?? self.options(includeGap: true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,27 @@ import Testing
#expect(SidebarSessionTreeFeed.badge(asks, answers: [:]) == nil)
}

/// ask-268456b5a0 (Leo 2026-10-28, "loads of stuff in my sidebar that I'm not even looking at"): a session folded
/// under its parent draws only as the parent's roll-up, so its steps and titles lay nothing out; its state does.
@Test func aFoldedSessionsStepLaysNothingOutButItsStateDoes() {
var tree = Self.tree
tree.collapsed = [Self.chief]
let working = SidebarWorkspace(id: Self.lane, title: "lane", agentWorking: true)
var stepped = working
stepped.agentStep = "Reading SidebarRowIndex.swift"
stepped.title = "lane (renamed)"
#expect(!tree.patchChangesRollUp(from: working, to: stepped))
var done = stepped
done.agentWorking = false
#expect(tree.patchChangesRollUp(from: stepped, to: done))
#expect(tree.patchChangesRollUp(from: nil, to: done))
// Shown (no fold over it): its own row patches in place, as before.
#expect(!Self.tree.patchChangesRollUp(from: stepped, to: done))
// A lane's own state decides: the row's step under it changes nothing.
tree.states[Self.lane] = .running
#expect(!tree.patchChangesRollUp(from: working, to: done))
}

@Test func rowsDrawTheLineTheBadgeAndTheKeycaps() throws {
SidebarSessionTree.shared.state = Self.tree
defer { SidebarSessionTree.shared.state = nil }
Expand Down
Loading