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 @@ -24,6 +24,7 @@ import {
chatRowClass,
chatRowHoverClass,
chatRowIndicatorClass,
chatRowRailClass,
chatRowShellClass,
inlineRowTriggerClass,
normalizeCallTitleSeparator,
Expand Down Expand Up @@ -147,7 +148,7 @@ export const ToolCallGroup = memo(function ToolCallGroup({
</Disclosure.Trigger>
</Disclosure.Heading>
<Disclosure.Content>
<Disclosure.Body className="ml-3 border-l border-dashed border-[color:var(--border)] pb-0 pl-3 pt-0">
<Disclosure.Body className={`ml-3 ${chatRowRailClass} pb-0 pl-3 pt-0`}>
{hasOverflowRows && !sameFileEditSummary ? (
<div className="mb-0.5 flex justify-start">
<button
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ export const chatRowHoverClass = "transition-colors hover:[--muted:var(--foregro
// (`pt-1` dense, `pt-2.5` roomier).
export const chatRowBodyClass = "mt-1 border-t border-[var(--hairline)] px-2";

// Dashed left rail marking a group's nested children as belonging to it (the
// chat tool-call group body, and the sidebar's worktree/thread groups). Only
// the rail treatment lives here — callers add their own indent (`ml-*`/`pl-*`)
// so a dense list can hug tighter than the roomier chat body.
export const chatRowRailClass = "border-l border-dashed border-[color:var(--border)]";

// Disclosure trigger for a dense inline row inside a tool-call group
// (ToolCallInline, ReasoningInline): icon + title + trailing meta hugging
// their content, with the shared hover treatment.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { openNewThread, openNewThreadSideBySide } from "@/renderer/actions/threa
import { useSidebarUiStore, useThreadListLimit } from "@/renderer/state/sidebarUiStore";
import { useThreadLiveWorkflowStore } from "@/renderer/state/threadLiveWorkflowStore";
import { SidebarButton } from "@/renderer/components/common/SidebarButton";
import { chatRowRailClass } from "@/renderer/components/thread/ChatPane/parts/items/chatRow";
import { NewThreadButton } from "./NewThreadButton";
import { buildSidebarProjectRows, type SidebarRow } from "./sidebarProjectRows";
import type { ThreadSortMode } from "./sortMode";
Expand Down Expand Up @@ -95,7 +96,7 @@ function SidebarThreadRow(props: {
const { t } = useLingui();

if (row.kind === "thread") {
return (
const item = (
<SortableThreadItem
thread={row.thread}
threadIndex={row.threadIndex}
Expand All @@ -110,6 +111,14 @@ function SidebarThreadRow(props: {
{...(row.sortDisabled !== undefined ? { sortDisabled: row.sortDisabled } : {})}
/>
);
// Group children hang off the same dashed rail as the chat tool-call group
// (shared recipe). `ml-3.5` drops the rail down the centerline of the group
// header's icon; no left padding keeps the child hugging the rail so the
// nesting reads without a wide indent.
if (row.inGroup) {
return <div className={`ml-3.5 ${chatRowRailClass}`}>{item}</div>;
}
return item;
}

return (
Expand All @@ -129,12 +138,10 @@ function SidebarThreadRow(props: {
editingThreadId={editingThreadId}
setEditingThreadId={setEditingThreadId}
/>
) : row.kind === "section-label" ? (
) : (
<div className="px-1.5 pb-0.5 pt-2 text-[10px] font-medium uppercase tracking-wider text-muted">
{t(row.label)}
</div>
) : (
<div aria-hidden className="mx-1.5 my-1 h-px bg-[var(--hairline)]" />
)}
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export function SidebarThreadGroup(props: {
}
}}
>
<div className="group flex w-full items-center gap-1 rounded px-1.5 py-1">
<div className="group flex w-full items-center gap-1 rounded px-2 py-1">
<button
type="button"
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export type SidebarRow =
showWorktreeBadge: boolean;
showWorktreeFilesButton?: boolean;
sortDisabled?: boolean;
/** Child of a worktree/thread group — rendered against a left rail. */
inGroup?: boolean;
}
| {
kind: "worktree-group";
Expand All @@ -35,7 +37,6 @@ export type SidebarRow =
key: string;
entry: Extract<ThreadListEntry, { kind: "thread-group" }>;
}
| { kind: "divider"; key: string }
| { kind: "section-label"; key: string; label: MessageDescriptor }
| { kind: "see-more"; key: string; hiddenCount: number };

Expand Down Expand Up @@ -139,6 +140,7 @@ function pushEntryRows(
group: `wt:${entry.group.worktreePath}`,
showWorktreeBadge: false,
showWorktreeFilesButton: false,
inGroup: true,
});
});
}
Expand All @@ -161,6 +163,7 @@ function pushEntryRows(
group: `group:${groupKey}`,
showWorktreeBadge: !!thread.worktreePath,
sortDisabled: input.dndDisabled,
inGroup: true,
});
});
}
Expand Down Expand Up @@ -237,13 +240,6 @@ export function buildSidebarProjectRows(input: {
isCollapsed,
nextUngroupedIndex,
});
const isLast = i === list.length - 1;
if (isLast) return;
if (entry.kind === "worktree-group" && !isCollapsed(entry.group.worktreePath)) {
rows.push({ kind: "divider", key: `wt-divider:${entry.group.worktreePath}` });
} else if (entry.kind === "thread-group" && !isCollapsed(`group:${entry.group.groupId}`)) {
rows.push({ kind: "divider", key: `group-divider:${entry.group.groupId}` });
}
});
};

Expand Down