| ... | @@ -276,6 +276,7 @@ function terminalProcessLabel(count: number): string { | ... | @@ -276,6 +276,7 @@ function terminalProcessLabel(count: number): string { |
| 276 | function SidebarThreadTooltip({ | 276 | function SidebarThreadTooltip({ |
| 277 | thread, | 277 | thread, |
| 278 | projectTitle, | 278 | projectTitle, |
| | 279 | projectDisplayName, |
| 279 | projectCwd, | 280 | projectCwd, |
| 280 | projectFaviconPath, | 281 | projectFaviconPath, |
| 281 | projectIcon, | 282 | projectIcon, |
| ... | @@ -291,6 +292,7 @@ function SidebarThreadTooltip({ | ... | @@ -291,6 +292,7 @@ function SidebarThreadTooltip({ |
| 291 | }: { | 292 | }: { |
| 292 | thread: SidebarThreadSummary; | 293 | thread: SidebarThreadSummary; |
| 293 | projectTitle: string | null; | 294 | projectTitle: string | null; |
| | 295 | projectDisplayName: string | null; |
| 294 | projectCwd: string | null; | 296 | projectCwd: string | null; |
| 295 | projectFaviconPath: string | null; | 297 | projectFaviconPath: string | null; |
| 296 | projectIcon: ProjectIconOverride | null; | 298 | projectIcon: ProjectIconOverride | null; |
| ... | @@ -321,17 +323,17 @@ function SidebarThreadTooltip({ | ... | @@ -321,17 +323,17 @@ function SidebarThreadTooltip({ |
| 321 | {thread.title} | 323 | {thread.title} |
| 322 | </div> | 324 | </div> |
| 323 | <div className="grid gap-1.5 pl-0.5 text-xs text-muted-foreground"> | 325 | <div className="grid gap-1.5 pl-0.5 text-xs text-muted-foreground"> |
| 324 | {projectTitle ? ( | 326 | {projectDisplayName ? ( |
| 325 | <div className="flex min-w-0 items-center gap-2"> | 327 | <div className="flex min-w-0 items-center gap-2"> |
| 326 | <ProjectFavicon | 328 | <ProjectFavicon |
| 327 | environmentId={thread.environmentId} | 329 | environmentId={thread.environmentId} |
| 328 | cwd={projectCwd ?? ""} | 330 | cwd={projectCwd ?? ""} |
| 329 | projectName={projectTitle} | 331 | projectName={projectTitle ?? ""} |
| 330 | faviconPath={projectFaviconPath} | 332 | faviconPath={projectFaviconPath} |
| 331 | projectIcon={projectIcon} | 333 | projectIcon={projectIcon} |
| 332 | className="size-3 shrink-0" | 334 | className="size-3 shrink-0" |
| 333 | /> | 335 | /> |
| 334 | <div className="min-w-0 truncate text-foreground/75">{projectTitle}</div> | 336 | <div className="min-w-0 truncate text-foreground/75">{projectDisplayName}</div> |
| 335 | </div> | 337 | </div> |
| 336 | ) : null} | 338 | ) : null} |
| 337 | {environmentLabel ? ( | 339 | {environmentLabel ? ( |
| ... | @@ -497,6 +499,7 @@ const SidebarDraftRow = memo(function SidebarDraftRow(props: { | ... | @@ -497,6 +499,7 @@ const SidebarDraftRow = memo(function SidebarDraftRow(props: { |
| 497 | session: DraftSessionState; | 499 | session: DraftSessionState; |
| 498 | composer: ComposerThreadDraftState; | 500 | composer: ComposerThreadDraftState; |
| 499 | projectTitle: string | null; | 501 | projectTitle: string | null; |
| | 502 | projectDisplayName: string | null; |
| 500 | projectCwd: string | null; | 503 | projectCwd: string | null; |
| 501 | projectFaviconPath: string | null; | 504 | projectFaviconPath: string | null; |
| 502 | projectIcon: ProjectIconOverride | null; | 505 | projectIcon: ProjectIconOverride | null; |
| ... | @@ -571,7 +574,7 @@ const SidebarDraftRow = memo(function SidebarDraftRow(props: { | ... | @@ -571,7 +574,7 @@ const SidebarDraftRow = memo(function SidebarDraftRow(props: { |
| 571 | className="size-4 shrink-0" | 574 | className="size-4 shrink-0" |
| 572 | /> | 575 | /> |
| 573 | <span className="min-w-0 flex-1 truncate text-xs font-medium text-secondary-label"> | 576 | <span className="min-w-0 flex-1 truncate text-xs font-medium text-secondary-label"> |
| 574 | {props.projectTitle} | 577 | {props.projectDisplayName} |
| 575 | </span> | 578 | </span> |
| 576 | <span className="ml-auto flex h-5 min-w-5 shrink-0 items-center justify-end"> | 579 | <span className="ml-auto flex h-5 min-w-5 shrink-0 items-center justify-end"> |
| 577 | <Tooltip> | 580 | <Tooltip> |
| ... | @@ -609,6 +612,7 @@ interface SidebarDraftRowData { | ... | @@ -609,6 +612,7 @@ interface SidebarDraftRowData { |
| 609 | // subscription + closing divider) so per-keystroke composer updates | 612 | // subscription + closing divider) so per-keystroke composer updates |
| 610 | // re-render only this block, never the whole sidebar. Vanishes at count 0. | 613 | // re-render only this block, never the whole sidebar. Vanishes at count 0. |
| 611 | const SidebarDraftBlock = memo(function SidebarDraftBlock(props: { | 614 | const SidebarDraftBlock = memo(function SidebarDraftBlock(props: { |
| | 615 | projectTitleByKey: ReadonlyMap<string, string>; |
| 612 | projectDisplayNameByKey: ReadonlyMap<string, string>; | 616 | projectDisplayNameByKey: ReadonlyMap<string, string>; |
| 613 | projectCwdByKey: ReadonlyMap<string, string>; | 617 | projectCwdByKey: ReadonlyMap<string, string>; |
| 614 | projectFaviconPathByKey: ReadonlyMap<string, string | null | undefined>; | 618 | projectFaviconPathByKey: ReadonlyMap<string, string | null | undefined>; |
| ... | @@ -706,7 +710,8 @@ const SidebarDraftBlock = memo(function SidebarDraftBlock(props: { | ... | @@ -706,7 +710,8 @@ const SidebarDraftBlock = memo(function SidebarDraftBlock(props: { |
| 706 | draftId={draftId} | 710 | draftId={draftId} |
| 707 | session={session} | 711 | session={session} |
| 708 | composer={composer} | 712 | composer={composer} |
| 709 | projectTitle={props.projectDisplayNameByKey.get(projectKey) ?? null} | 713 | projectTitle={props.projectTitleByKey.get(projectKey) ?? null} |
| | 714 | projectDisplayName={props.projectDisplayNameByKey.get(projectKey) ?? null} |
| 710 | projectCwd={props.projectCwdByKey.get(projectKey) ?? null} | 715 | projectCwd={props.projectCwdByKey.get(projectKey) ?? null} |
| 711 | projectFaviconPath={props.projectFaviconPathByKey.get(projectKey) ?? null} | 716 | projectFaviconPath={props.projectFaviconPathByKey.get(projectKey) ?? null} |
| 712 | projectIcon={props.projectIconByKey.get(projectKey) ?? null} | 717 | projectIcon={props.projectIconByKey.get(projectKey) ?? null} |
| ... | @@ -759,6 +764,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -759,6 +764,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 759 | projectFaviconPath: string | null; | 764 | projectFaviconPath: string | null; |
| 760 | projectIcon: ProjectIconOverride | null; | 765 | projectIcon: ProjectIconOverride | null; |
| 761 | projectTitle: string | null; | 766 | projectTitle: string | null; |
| | 767 | projectDisplayName: string | null; |
| 762 | providerEntryByInstanceId: ReadonlyMap<string, ProviderInstanceEntry>; | 768 | providerEntryByInstanceId: ReadonlyMap<string, ProviderInstanceEntry>; |
| 763 | timestampFormat: TimestampFormat; | 769 | timestampFormat: TimestampFormat; |
| 764 | onThreadClick: (event: ReactMouseEvent, threadRef: ScopedThreadRef) => void; | 770 | onThreadClick: (event: ReactMouseEvent, threadRef: ScopedThreadRef) => void; |
| ... | @@ -994,6 +1000,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -994,6 +1000,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 994 | <SidebarThreadTooltip | 1000 | <SidebarThreadTooltip |
| 995 | thread={thread} | 1001 | thread={thread} |
| 996 | projectTitle={props.projectTitle} | 1002 | projectTitle={props.projectTitle} |
| | 1003 | projectDisplayName={props.projectDisplayName} |
| 997 | projectCwd={props.projectCwd} | 1004 | projectCwd={props.projectCwd} |
| 998 | projectFaviconPath={props.projectFaviconPath} | 1005 | projectFaviconPath={props.projectFaviconPath} |
| 999 | projectIcon={props.projectIcon} | 1006 | projectIcon={props.projectIcon} |
| ... | @@ -1304,7 +1311,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1304,7 +1311,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1304 | <ProjectFavicon | 1311 | <ProjectFavicon |
| 1305 | environmentId={thread.environmentId} | 1312 | environmentId={thread.environmentId} |
| 1306 | cwd={props.projectCwd ?? ""} | 1313 | cwd={props.projectCwd ?? ""} |
| 1307 | projectName={props.projectTitle ?? thread.title} | 1314 | projectName={props.projectTitle ?? ""} |
| 1308 | faviconPath={props.projectFaviconPath} | 1315 | faviconPath={props.projectFaviconPath} |
| 1309 | projectIcon={props.projectIcon} | 1316 | projectIcon={props.projectIcon} |
| 1310 | className="size-4" | 1317 | className="size-4" |
| ... | @@ -1465,14 +1472,14 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { | ... | @@ -1465,14 +1472,14 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: { |
| 1465 | projectIcon={props.projectIcon} | 1472 | projectIcon={props.projectIcon} |
| 1466 | className="size-4 shrink-0" | 1473 | className="size-4 shrink-0" |
| 1467 | /> | 1474 | /> |
| 1468 | {props.projectTitle ? ( | 1475 | {props.projectDisplayName ? ( |
| 1469 | <span | 1476 | <span |
| 1470 | className={cn( | 1477 | className={cn( |
| 1471 | "min-w-0 flex-1 truncate text-secondary-label text-xs", | 1478 | "min-w-0 flex-1 truncate text-secondary-label text-xs", |
| 1472 | shouldRecede ? "font-normal" : "font-medium", | 1479 | shouldRecede ? "font-normal" : "font-medium", |
| 1473 | )} | 1480 | )} |
| 1474 | > | 1481 | > |
| 1475 | {props.projectTitle} | 1482 | {props.projectDisplayName} |
| 1476 | </span> | 1483 | </span> |
| 1477 | ) : ( | 1484 | ) : ( |
| 1478 | <span className="flex-1" /> | 1485 | <span className="flex-1" /> |
| ... | @@ -1669,6 +1676,7 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { | ... | @@ -1669,6 +1676,7 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { |
| 1669 | projectFaviconPath: string | null; | 1676 | projectFaviconPath: string | null; |
| 1670 | projectIcon: ProjectIconOverride | null; | 1677 | projectIcon: ProjectIconOverride | null; |
| 1671 | projectTitle: string | null; | 1678 | projectTitle: string | null; |
| | 1679 | projectDisplayName: string | null; |
| 1672 | environmentLabel: string | null; | 1680 | environmentLabel: string | null; |
| 1673 | environmentMachine: EnvironmentMachineKind; | 1681 | environmentMachine: EnvironmentMachineKind; |
| 1674 | providerEntryByInstanceId: ReadonlyMap<string, ProviderInstanceEntry>; | 1682 | providerEntryByInstanceId: ReadonlyMap<string, ProviderInstanceEntry>; |
| ... | @@ -1735,7 +1743,9 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { | ... | @@ -1735,7 +1743,9 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { |
| 1735 | aria-selected={props.isHighlighted} | 1743 | aria-selected={props.isHighlighted} |
| 1736 | aria-current={props.isRouteActive ? "page" : undefined} | 1744 | aria-current={props.isRouteActive ? "page" : undefined} |
| 1737 | aria-label={ | 1745 | aria-label={ |
| 1738 | props.projectTitle ? `${thread.title}, ${props.projectTitle}` : thread.title | 1746 | props.projectDisplayName |
| | 1747 | ? `${thread.title}, ${props.projectDisplayName}` |
| | 1748 | : thread.title |
| 1739 | } | 1749 | } |
| 1740 | onMouseMove={props.onHighlight} | 1750 | onMouseMove={props.onHighlight} |
| 1741 | onClick={props.onSelect} | 1751 | onClick={props.onSelect} |
| ... | @@ -1751,7 +1761,7 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { | ... | @@ -1751,7 +1761,7 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { |
| 1751 | <ProjectFavicon | 1761 | <ProjectFavicon |
| 1752 | environmentId={thread.environmentId} | 1762 | environmentId={thread.environmentId} |
| 1753 | cwd={props.projectCwd ?? ""} | 1763 | cwd={props.projectCwd ?? ""} |
| 1754 | projectName={props.projectTitle ?? thread.title} | 1764 | projectName={props.projectTitle ?? ""} |
| 1755 | faviconPath={props.projectFaviconPath} | 1765 | faviconPath={props.projectFaviconPath} |
| 1756 | projectIcon={props.projectIcon} | 1766 | projectIcon={props.projectIcon} |
| 1757 | className="size-4 shrink-0" | 1767 | className="size-4 shrink-0" |
| ... | @@ -1764,6 +1774,7 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { | ... | @@ -1764,6 +1774,7 @@ const SidebarSearchResultRow = memo(function SidebarSearchResultRow(props: { |
| 1764 | <SidebarThreadTooltip | 1774 | <SidebarThreadTooltip |
| 1765 | thread={thread} | 1775 | thread={thread} |
| 1766 | projectTitle={props.projectTitle} | 1776 | projectTitle={props.projectTitle} |
| | 1777 | projectDisplayName={props.projectDisplayName} |
| 1767 | projectCwd={props.projectCwd} | 1778 | projectCwd={props.projectCwd} |
| 1768 | projectFaviconPath={props.projectFaviconPath} | 1779 | projectFaviconPath={props.projectFaviconPath} |
| 1769 | projectIcon={props.projectIcon} | 1780 | projectIcon={props.projectIcon} |
| ... | @@ -1995,6 +2006,12 @@ export default function Sidebar() { | ... | @@ -1995,6 +2006,12 @@ export default function Sidebar() { |
| 1995 | ), | 2006 | ), |
| 1996 | [projects], | 2007 | [projects], |
| 1997 | ); | 2008 | ); |
| | 2009 | // Icons use saved titles. Group labels can include a repository owner or a different title. |
| | 2010 | const projectTitleByKey = useMemo( |
| | 2011 | () => |
| | 2012 | new Map(projects.map((project) => [`${project.environmentId}:${project.id}`, project.title])), |
| | 2013 | [projects], |
| | 2014 | ); |
| 1998 | const projectDisplayNameByKey = useMemo( | 2015 | const projectDisplayNameByKey = useMemo( |
| 1999 | () => | 2016 | () => |
| 2000 | new Map( | 2017 | new Map( |
| ... | @@ -3631,7 +3648,7 @@ export default function Sidebar() { | ... | @@ -3631,7 +3648,7 @@ export default function Sidebar() { |
| 3631 | <ProjectFavicon | 3648 | <ProjectFavicon |
| 3632 | environmentId={scopedProjectGroup.environmentId} | 3649 | environmentId={scopedProjectGroup.environmentId} |
| 3633 | cwd={scopedProjectGroup.workspaceRoot} | 3650 | cwd={scopedProjectGroup.workspaceRoot} |
| 3634 | projectName={scopedProjectGroup.displayName} | 3651 | projectName={scopedProjectGroup.title} |
| 3635 | faviconPath={scopedProjectGroup.faviconPath} | 3652 | faviconPath={scopedProjectGroup.faviconPath} |
| 3636 | projectIcon={scopedProjectGroup.projectIcon} | 3653 | projectIcon={scopedProjectGroup.projectIcon} |
| 3637 | className="size-4" | 3654 | className="size-4" |
| ... | @@ -3689,7 +3706,7 @@ export default function Sidebar() { | ... | @@ -3689,7 +3706,7 @@ export default function Sidebar() { |
| 3689 | <ProjectFavicon | 3706 | <ProjectFavicon |
| 3690 | environmentId={project.environmentId} | 3707 | environmentId={project.environmentId} |
| 3691 | cwd={project.workspaceRoot} | 3708 | cwd={project.workspaceRoot} |
| 3692 | projectName={project.displayName} | 3709 | projectName={project.title} |
| 3693 | faviconPath={project.faviconPath} | 3710 | faviconPath={project.faviconPath} |
| 3694 | projectIcon={project.projectIcon} | 3711 | projectIcon={project.projectIcon} |
| 3695 | className="size-4 shrink-0" | 3712 | className="size-4 shrink-0" |
| ... | @@ -3780,6 +3797,10 @@ export default function Sidebar() { | ... | @@ -3780,6 +3797,10 @@ export default function Sidebar() { |
| 3780 | null | 3797 | null |
| 3781 | } | 3798 | } |
| 3782 | projectTitle={ | 3799 | projectTitle={ |
| | 3800 | projectTitleByKey.get(`${thread.environmentId}:${thread.projectId}`) ?? |
| | 3801 | null |
| | 3802 | } |
| | 3803 | projectDisplayName={ |
| 3783 | projectDisplayNameByKey.get( | 3804 | projectDisplayNameByKey.get( |
| 3784 | `${thread.environmentId}:${thread.projectId}`, | 3805 | `${thread.environmentId}:${thread.projectId}`, |
| 3785 | ) ?? null | 3806 | ) ?? null |
| ... | @@ -3902,6 +3923,10 @@ export default function Sidebar() { | ... | @@ -3902,6 +3923,10 @@ export default function Sidebar() { |
| 3902 | null | 3923 | null |
| 3903 | } | 3924 | } |
| 3904 | projectTitle={ | 3925 | projectTitle={ |
| | 3926 | projectTitleByKey.get(`${thread.environmentId}:${thread.projectId}`) ?? |
| | 3927 | null |
| | 3928 | } |
| | 3929 | projectDisplayName={ |
| 3905 | projectDisplayNameByKey.get( | 3930 | projectDisplayNameByKey.get( |
| 3906 | `${thread.environmentId}:${thread.projectId}`, | 3931 | `${thread.environmentId}:${thread.projectId}`, |
| 3907 | ) ?? null | 3932 | ) ?? null |
| ... | @@ -3941,6 +3966,7 @@ export default function Sidebar() { | ... | @@ -3941,6 +3966,7 @@ export default function Sidebar() { |
| 3941 | const items: ReactNode[] = [ | 3966 | const items: ReactNode[] = [ |
| 3942 | <SidebarDraftBlock | 3967 | <SidebarDraftBlock |
| 3943 | key="draft-sessions" | 3968 | key="draft-sessions" |
| | 3969 | projectTitleByKey={projectTitleByKey} |
| 3944 | projectDisplayNameByKey={projectDisplayNameByKey} | 3970 | projectDisplayNameByKey={projectDisplayNameByKey} |
| 3945 | projectCwdByKey={projectCwdByKey} | 3971 | projectCwdByKey={projectCwdByKey} |
| 3946 | projectFaviconPathByKey={projectFaviconPathByKey} | 3972 | projectFaviconPathByKey={projectFaviconPathByKey} |