From 25773fd693f2b7e6144243a36479f987e58057e3 Mon Sep 17 00:00:00 2001 From: TheIcarusWings <10465470+TheIcarusWings@users.noreply.github.com> Date: Fri, 12 Jun 2026 13:21:59 +0000 Subject: [PATCH] feat(sidebar): show worktree indicator on session rows Sessions running in a git worktree show a FolderGit2 icon in the trailing status cluster, with the worktree folder and branch in an accessible tooltip label. Add focused render coverage for visible and absent worktree paths. Co-authored-by: codex --- apps/web/src/components/Sidebar.tsx | 2 + .../ThreadStatusIndicators.test.tsx | 39 +++++++++++++++++++ .../src/components/ThreadStatusIndicators.tsx | 37 +++++++++++++++++- 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/components/ThreadStatusIndicators.test.tsx diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index 1b46b0f1d04..77d0ffda1ab 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -17,6 +17,7 @@ import { resolveThreadPr, terminalStatusFromRunningIds, ThreadStatusLabel, + ThreadWorktreeIndicator, } from "./ThreadStatusIndicators"; import { ProjectFavicon } from "./ProjectFavicon"; import { useAtomValue } from "@effect/atom-react"; @@ -741,6 +742,7 @@ export const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThr )} + {terminalStatus && ( { + it("renders the worktree folder and branch in an accessible label", () => { + const markup = renderToStaticMarkup( + , + ); + + expect(markup).toContain('role="img"'); + expect(markup).toContain( + 'aria-label="Worktree: sidebar-indicator (feature/sidebar-indicator)"', + ); + expect(markup).toContain('data-testid="thread-worktree-thread-1"'); + }); + + it.each([null, "", " "])("renders nothing for an absent worktree path", (worktreePath) => { + const markup = renderToStaticMarkup( + , + ); + + expect(markup).toBe(""); + }); +}); diff --git a/apps/web/src/components/ThreadStatusIndicators.tsx b/apps/web/src/components/ThreadStatusIndicators.tsx index 8eac1fa412a..3e85920d190 100644 --- a/apps/web/src/components/ThreadStatusIndicators.tsx +++ b/apps/web/src/components/ThreadStatusIndicators.tsx @@ -4,7 +4,7 @@ import { scopeThreadRef, } from "@t3tools/client-runtime/environment"; import type { VcsStatusResult } from "@t3tools/contracts"; -import { CloudIcon, GitPullRequestIcon, TerminalIcon } from "lucide-react"; +import { CloudIcon, FolderGit2Icon, GitPullRequestIcon, TerminalIcon } from "lucide-react"; import { useMemo } from "react"; import { useEnvironment, usePrimaryEnvironmentId } from "../state/environments"; import { useProject } from "../state/entities"; @@ -15,6 +15,7 @@ import { useUiStateStore } from "../uiStateStore"; import { resolveChangeRequestPresentation } from "../sourceControlPresentation"; import { resolveThreadStatusPill, type ThreadStatusPill } from "./Sidebar.logic"; import type { SidebarThreadSummary } from "../types"; +import { formatWorktreePathForDisplay } from "../worktreeCleanup"; import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; export interface PrStatusIndicator { @@ -94,6 +95,40 @@ export function terminalStatusFromRunningIds( }; } +export function ThreadWorktreeIndicator({ + thread, +}: { + thread: Pick; +}) { + const worktreePath = thread.worktreePath?.trim(); + if (!worktreePath) { + return null; + } + + const displayPath = formatWorktreePathForDisplay(worktreePath); + const tooltip = thread.branch + ? `Worktree: ${displayPath} (${thread.branch})` + : `Worktree: ${displayPath}`; + + return ( + + + } + > + + + {tooltip} + + ); +} + export function ThreadStatusLabel({ status, compact = false,