From e3bf19f7a842201f0bd26adcf21356a1851079a7 Mon Sep 17 00:00:00 2001 From: lihong Date: Tue, 10 Nov 2026 22:27:44 +0800 Subject: [PATCH 1/2] fix(sidebar): make child session disclosure visible --- .../sidebar-conversation-card.test.tsx | 6 +- .../sidebar-conversation-card.tsx | 58 +++++++++---------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/src/components/conversations/sidebar-conversation-card.test.tsx b/src/components/conversations/sidebar-conversation-card.test.tsx index f92538e0c57..fd390ad3aeb 100644 --- a/src/components/conversations/sidebar-conversation-card.test.tsx +++ b/src/components/conversations/sidebar-conversation-card.test.tsx @@ -530,13 +530,13 @@ describe("SidebarConversationCard sub-session chevron", () => { expect(queryByLabelText("Expand sub-conversations")).toBeNull() }) - it("overlays the chevron on the rail axis (the agent-icon position)", () => { + it("keeps the chevron on the rail axis", () => { const { getByLabelText } = renderCard(conv(2), { hasChildren: true, expanded: false, }) - // The chevron now sits at the agent-icon's rail-axis x (revealed on hover), - // not in the right-hand time/action slot. + // The chevron stays on the rail axis, separate from the agent icon + // and the right-hand time/action slot. const chevron = getByLabelText("Expand sub-conversations") expect(chevron.style.left).toContain("--conv-rail-axis") }) diff --git a/src/components/conversations/sidebar-conversation-card.tsx b/src/components/conversations/sidebar-conversation-card.tsx index 9e0c4ae0940..cca741c2df8 100644 --- a/src/components/conversations/sidebar-conversation-card.tsx +++ b/src/components/conversations/sidebar-conversation-card.tsx @@ -384,12 +384,13 @@ export const SidebarConversationCard = memo(function SidebarConversationCard({ "rounded-full", "pr-[0.25rem]" )} - // Rail-axis-relative left padding (was a fixed `pl-7`): at depth 0 - // this resolves to 0.875rem + 0.875rem = 1.75rem (= pl-7), so root - // rows are pixel-identical; deeper rows inherit the shifted var. + // Parents reserve room for both the persistent chevron and + // their agent icon. Leaves keep the existing rail spacing. style={{ paddingLeft: - "calc(var(--conv-rail-axis, 0.875rem) + 0.875rem)", + hasChildren && onToggleExpand + ? "calc(var(--conv-rail-axis, 0.875rem) + 1.875rem)" + : "calc(var(--conv-rail-axis, 0.875rem) + 0.875rem)", }} > {/* Ancestor guide rails (depth ≥ 1): keep each parent's vertical @@ -412,18 +413,12 @@ export const SidebarConversationCard = memo(function SidebarConversationCard({ }} />
+ {hasChildren && conversation.child_count > 0 && ( + + {conversation.child_count} + + )} {/* Inline, inside the row's fixed 2rem height: the list's virtualizer and rail geometry assume one line per row. The full set of tags is in the hover bubble. Capped so the @@ -495,13 +502,9 @@ export const SidebarConversationCard = memo(function SidebarConversationCard({ the hover bubble names the original path instead. */} - {/* Expand/collapse affordance for delegation children. It overlays - the agent icon at the rail axis: idle shows the icon, and - hovering (or focusing) the row swaps in this chevron at the very - same spot while the icon fades. A sibling of the row button (HTML - forbids nested buttons) with `stopPropagation` so a toggle never - selects the row; pointer events stay off until revealed so a - click on the icon area still selects the row when not hovering. */} + {/* Keep the child toggle visible at the rail axis, alongside + the agent icon. It is a sibling of the row button so expanding + a subtree never selects the conversation. */} {hasChildren && onToggleExpand && ( - {/* Keep the child toggle visible at the rail axis, alongside - the agent icon. It is a sibling of the row button so expanding + {/* Keep the child toggle visible in the gutter before the + agent icon. It is a sibling of the row button so expanding a subtree never selects the conversation. */} {hasChildren && onToggleExpand && ( )} + {hasChildren && + onToggleExpand && + conversation.child_count > 0 && ( + + )} + {/* Right slot: sizes to its content — the time / status badge normally, the two quick-action buttons (pin, done) on hover — so it never reserves more width than what is actually shown