diff --git a/apps/web/src/components/SidebarV2.tsx b/apps/web/src/components/SidebarV2.tsx index e6ec1eca9b6..a0787e3a496 100644 --- a/apps/web/src/components/SidebarV2.tsx +++ b/apps/web/src/components/SidebarV2.tsx @@ -356,6 +356,41 @@ function SnoozePopoverButton(props: { ); } +/** + * Compact "project · server" line for cross-project / multi-env lists. + * Mirrors classic recency subtitles without fighting V2 card chrome. + */ +function ProjectServerContextLine(props: { + readonly projectTitle: string | null; + readonly environmentLabel: string | null; + readonly showProject: boolean; + readonly showEnvironment: boolean; + readonly isRemote: boolean; + readonly className?: string; +}) { + const showProject = props.showProject && Boolean(props.projectTitle); + const showEnvironment = props.showEnvironment && Boolean(props.environmentLabel); + if (!showProject && !showEnvironment) return null; + return ( + + {showProject ? {props.projectTitle} : null} + {showProject && showEnvironment ? ( + + · + + ) : null} + {showEnvironment ? ( + + {props.isRemote ? ( + + ) : null} + {props.environmentLabel} + + ) : null} + + ); +} + const SidebarV2Row = memo(function SidebarV2Row(props: { thread: SidebarThreadSummary; variant: "card" | "slim"; @@ -378,6 +413,16 @@ const SidebarV2Row = memo(function SidebarV2Row(props: { environmentLabel: string | null; projectCwd: string | null; projectTitle: string | null; + /** + * When true (All projects scope), surface project name on rows so + * cross-project lists stay attributable. Scoped lists omit it. + */ + showCrossProjectContext: boolean; + /** + * When true (multiple environments connected), surface server label so + * same-named projects on different hosts stay distinct. + */ + showEnvironmentContext: boolean; providerEntryByInstanceId: ReadonlyMap; onThreadClick: (event: ReactMouseEvent, threadRef: ScopedThreadRef) => void; onThreadActivate: (threadRef: ScopedThreadRef) => void; @@ -528,6 +573,12 @@ const SidebarV2Row = memo(function SidebarV2Row(props: { const isRemote = props.currentEnvironmentId !== null && thread.environmentId !== props.currentEnvironmentId; + // Project label when scanning all projects; env when multi-host or remote + // even if scoped (same project name on two machines). + const showProjectContext = props.showCrossProjectContext && Boolean(props.projectTitle); + const showEnvironmentContext = + Boolean(props.environmentLabel) && + (props.showEnvironmentContext || props.showCrossProjectContext || isRemote); const detailsTooltip = ( - {title} +
+ {title} + {showSlimContext ? ( + + ) : null} +
{/* The PR badge stays outside the hover-fading slot: it must remain visible AND clickable while the row is hovered. Only the time/jump label yields to the settle affordance. */} @@ -870,7 +943,21 @@ const SidebarV2Row = memo(function SidebarV2Row(props: { cwd={props.projectCwd ?? ""} className="size-4 shrink-0" /> - {props.projectTitle ? ( + {showProjectContext || showEnvironmentContext ? ( + + ) : props.projectTitle ? ( + // Scoped to one project: keep a light project label for + // continuity without forcing server noise. 1} providerEntryByInstanceId={providerEntryByInstanceId} onThreadClick={handleThreadClick} onThreadActivate={navigateToThread}