Skip to content

Commit d9f23e8

Browse files
authored
refactor(sidebar): align grouped threads with shared dashed rails (#292)
1 parent a003d43 commit d9f23e8

5 files changed

Lines changed: 24 additions & 14 deletions

File tree

src/renderer/components/thread/ChatPane/parts/items/ToolCallGroup.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
chatRowClass,
2525
chatRowHoverClass,
2626
chatRowIndicatorClass,
27+
chatRowRailClass,
2728
chatRowShellClass,
2829
inlineRowTriggerClass,
2930
normalizeCallTitleSeparator,
@@ -147,7 +148,7 @@ export const ToolCallGroup = memo(function ToolCallGroup({
147148
</Disclosure.Trigger>
148149
</Disclosure.Heading>
149150
<Disclosure.Content>
150-
<Disclosure.Body className="ml-3 border-l border-dashed border-[color:var(--border)] pb-0 pl-3 pt-0">
151+
<Disclosure.Body className={`ml-3 ${chatRowRailClass} pb-0 pl-3 pt-0`}>
151152
{hasOverflowRows && !sameFileEditSummary ? (
152153
<div className="mb-0.5 flex justify-start">
153154
<button

src/renderer/components/thread/ChatPane/parts/items/chatRow.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ export const chatRowHoverClass = "transition-colors hover:[--muted:var(--foregro
3333
// (`pt-1` dense, `pt-2.5` roomier).
3434
export const chatRowBodyClass = "mt-1 border-t border-[var(--hairline)] px-2";
3535

36+
// Dashed left rail marking a group's nested children as belonging to it (the
37+
// chat tool-call group body, and the sidebar's worktree/thread groups). Only
38+
// the rail treatment lives here — callers add their own indent (`ml-*`/`pl-*`)
39+
// so a dense list can hug tighter than the roomier chat body.
40+
export const chatRowRailClass = "border-l border-dashed border-[color:var(--border)]";
41+
3642
// Disclosure trigger for a dense inline row inside a tool-call group
3743
// (ToolCallInline, ReasoningInline): icon + title + trailing meta hugging
3844
// their content, with the shared hover treatment.

src/renderer/views/MainView/parts/Sidebar/parts/SidebarProjectThreadList.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { openNewThread, openNewThreadSideBySide } from "@/renderer/actions/threa
1212
import { useSidebarUiStore, useThreadListLimit } from "@/renderer/state/sidebarUiStore";
1313
import { useThreadLiveWorkflowStore } from "@/renderer/state/threadLiveWorkflowStore";
1414
import { SidebarButton } from "@/renderer/components/common/SidebarButton";
15+
import { chatRowRailClass } from "@/renderer/components/thread/ChatPane/parts/items/chatRow";
1516
import { NewThreadButton } from "./NewThreadButton";
1617
import { buildSidebarProjectRows, type SidebarRow } from "./sidebarProjectRows";
1718
import type { ThreadSortMode } from "./sortMode";
@@ -95,7 +96,7 @@ function SidebarThreadRow(props: {
9596
const { t } = useLingui();
9697

9798
if (row.kind === "thread") {
98-
return (
99+
const item = (
99100
<SortableThreadItem
100101
thread={row.thread}
101102
threadIndex={row.threadIndex}
@@ -110,6 +111,14 @@ function SidebarThreadRow(props: {
110111
{...(row.sortDisabled !== undefined ? { sortDisabled: row.sortDisabled } : {})}
111112
/>
112113
);
114+
// Group children hang off the same dashed rail as the chat tool-call group
115+
// (shared recipe). `ml-3.5` drops the rail down the centerline of the group
116+
// header's icon; no left padding keeps the child hugging the rail so the
117+
// nesting reads without a wide indent.
118+
if (row.inGroup) {
119+
return <div className={`ml-3.5 ${chatRowRailClass}`}>{item}</div>;
120+
}
121+
return item;
113122
}
114123

115124
return (
@@ -129,12 +138,10 @@ function SidebarThreadRow(props: {
129138
editingThreadId={editingThreadId}
130139
setEditingThreadId={setEditingThreadId}
131140
/>
132-
) : row.kind === "section-label" ? (
141+
) : (
133142
<div className="px-1.5 pb-0.5 pt-2 text-[10px] font-medium uppercase tracking-wider text-muted">
134143
{t(row.label)}
135144
</div>
136-
) : (
137-
<div aria-hidden className="mx-1.5 my-1 h-px bg-[var(--hairline)]" />
138145
)}
139146
</div>
140147
);

src/renderer/views/MainView/parts/Sidebar/parts/SidebarThreadGroup.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ export function SidebarThreadGroup(props: {
104104
}
105105
}}
106106
>
107-
<div className="group flex w-full items-center gap-1 rounded px-1.5 py-1">
107+
<div className="group flex w-full items-center gap-1 rounded px-2 py-1">
108108
<button
109109
type="button"
110110
className="flex min-w-0 flex-1 items-center gap-1.5 text-left text-xs font-medium text-muted transition-colors hover:text-foreground"

src/renderer/views/MainView/parts/Sidebar/parts/sidebarProjectRows.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ export type SidebarRow =
2121
showWorktreeBadge: boolean;
2222
showWorktreeFilesButton?: boolean;
2323
sortDisabled?: boolean;
24+
/** Child of a worktree/thread group — rendered against a left rail. */
25+
inGroup?: boolean;
2426
}
2527
| {
2628
kind: "worktree-group";
@@ -35,7 +37,6 @@ export type SidebarRow =
3537
key: string;
3638
entry: Extract<ThreadListEntry, { kind: "thread-group" }>;
3739
}
38-
| { kind: "divider"; key: string }
3940
| { kind: "section-label"; key: string; label: MessageDescriptor }
4041
| { kind: "see-more"; key: string; hiddenCount: number };
4142

@@ -139,6 +140,7 @@ function pushEntryRows(
139140
group: `wt:${entry.group.worktreePath}`,
140141
showWorktreeBadge: false,
141142
showWorktreeFilesButton: false,
143+
inGroup: true,
142144
});
143145
});
144146
}
@@ -161,6 +163,7 @@ function pushEntryRows(
161163
group: `group:${groupKey}`,
162164
showWorktreeBadge: !!thread.worktreePath,
163165
sortDisabled: input.dndDisabled,
166+
inGroup: true,
164167
});
165168
});
166169
}
@@ -237,13 +240,6 @@ export function buildSidebarProjectRows(input: {
237240
isCollapsed,
238241
nextUngroupedIndex,
239242
});
240-
const isLast = i === list.length - 1;
241-
if (isLast) return;
242-
if (entry.kind === "worktree-group" && !isCollapsed(entry.group.worktreePath)) {
243-
rows.push({ kind: "divider", key: `wt-divider:${entry.group.worktreePath}` });
244-
} else if (entry.kind === "thread-group" && !isCollapsed(`group:${entry.group.groupId}`)) {
245-
rows.push({ kind: "divider", key: `group-divider:${entry.group.groupId}` });
246-
}
247243
});
248244
};
249245

0 commit comments

Comments
 (0)