authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-13 19:27:14-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-13 19:27:14-03:00
logdf7ccc8fd01f5d2a1d8ec21bef8f9c59398fe913
tree5d0390ca3b2657daf32be8c6edc89f69bf581865
parent77bca8b2d76a1f42552e5eee7d277fcb1160347a
signature Signed by PGP key B5690EEEBB952194

feat(web): refine compact thread row badges (#11644)


2 files changed, 83 insertions(+), 28 deletions(-)

apps/web/src/components/Sidebar.tsx+79-27
...@@ -1584,7 +1584,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {...@@ -1584,7 +1584,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
1584 useRightPanelStore.getState().open(threadRef, "pull-requests");1584 useRightPanelStore.getState().open(threadRef, "pull-requests");
1585 if (!props.isActive) onThreadActivate(threadRef);1585 if (!props.isActive) onThreadActivate(threadRef);
1586 }, [onThreadActivate, props.isActive, threadRef]);1586 }, [onThreadActivate, props.isActive, threadRef]);
1587 const prBadge =1587 const renderPrBadge = (iconOnly: boolean) =>
1588 prBadgeShape?.kind === "stack" || pr || currentLinkedPr ? (1588 prBadgeShape?.kind === "stack" || pr || currentLinkedPr ? (
1589 <ThreadPullRequestBadgeControl1589 <ThreadPullRequestBadgeControl
1590 variant="underline"1590 variant="underline"
...@@ -1592,10 +1592,12 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {...@@ -1592,10 +1592,12 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
1592 number={pr?.number ?? currentLinkedPr?.number}1592 number={pr?.number ?? currentLinkedPr?.number}
1593 url={pr?.url ?? currentLinkedPr?.url}1593 url={pr?.url ?? currentLinkedPr?.url}
1594 status={prStatus}1594 status={prStatus}
1595 iconOnly={iconOnly}
1595 onOpenStack={handlePrStackClick}1596 onOpenStack={handlePrStackClick}
1596 onOpenPullRequest={handlePrClick}1597 onOpenPullRequest={handlePrClick}
1597 />1598 />
1598 ) : null;1599 ) : null;
1600 const prBadge = renderPrBadge(false);
1599 const terminalStatusIcon = terminalStatus ? (1601 const terminalStatusIcon = terminalStatus ? (
1600 <span1602 <span
1601 role="img"1603 role="img"
...@@ -1687,17 +1689,37 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {...@@ -1687,17 +1689,37 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
1687 />1689 />
1688 }1690 }
1689 >1691 >
1690 {props.project ? (1692 <span className="relative inline-flex size-4 shrink-0 items-center justify-center">
1691 <ProjectFavicon project={props.project} className="size-4 shrink-0" />1693 {props.project ? (
1692 ) : driverKind ? (1694 <ProjectFavicon project={props.project} className="size-4" />
1693 <ProviderInstanceIcon1695 ) : driverKind ? (
1694 driverKind={driverKind}1696 <ProviderInstanceIcon
1695 displayName={providerEntry?.displayName ?? modelInstanceId}1697 driverKind={driverKind}
1696 iconClassName="size-4"1698 displayName={providerEntry?.displayName ?? modelInstanceId}
1697 />1699 iconClassName="size-4"
1698 ) : (1700 />
1699 <SquarePenIcon aria-hidden className="size-4" />1701 ) : (
1700 )}1702 <SquarePenIcon aria-hidden className="size-4" />
1703 )}
1704 {isRemote ? (
1705 <Tooltip>
1706 <TooltipTrigger
1707 render={
1708 <span
1709 role="img"
1710 aria-label={props.environmentLabel ?? "Remote environment"}
1711 className="absolute -left-1 -bottom-1 inline-flex size-3 items-center justify-center rounded-full bg-sidebar text-sidebar-muted-foreground ring-1 ring-sidebar"
1712 />
1713 }
1714 >
1715 <EnvironmentMachineIcon kind={props.environmentMachine} className="size-2.5" />
1716 </TooltipTrigger>
1717 <TooltipPopup side="right">
1718 {props.environmentLabel ?? "Remote environment"}
1719 </TooltipPopup>
1720 </Tooltip>
1721 ) : null}
1722 </span>
1701 {topStatus ? (1723 {topStatus ? (
1702 <span1724 <span
1703 aria-hidden1725 aria-hidden
...@@ -1943,7 +1965,47 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {...@@ -1943,7 +1965,47 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
1943 <div className="flex h-5 w-full min-w-0 items-center gap-1.5">1965 <div className="flex h-5 w-full min-w-0 items-center gap-1.5">
1944 {draftIndicator}1966 {draftIndicator}
1945 {props.project ? (1967 {props.project ? (
1946 <ProjectFavicon project={props.project} className="size-4 shrink-0" />1968 <span className="relative inline-flex size-4 shrink-0 items-center justify-center">
1969 <ProjectFavicon project={props.project} className="size-4" />
1970 {compactRows && isRemote ? (
1971 <Tooltip>
1972 <TooltipTrigger
1973 render={
1974 <span
1975 role="img"
1976 aria-label={props.environmentLabel ?? "Remote environment"}
1977 className="absolute -right-1 -bottom-1 inline-flex size-3 items-center justify-center rounded-full bg-sidebar text-sidebar-muted-foreground ring-1 ring-sidebar"
1978 />
1979 }
1980 >
1981 <EnvironmentMachineIcon
1982 kind={props.environmentMachine}
1983 className="size-2.5"
1984 />
1985 </TooltipTrigger>
1986 <TooltipPopup side="top">
1987 {props.environmentLabel ?? "Remote environment"}
1988 </TooltipPopup>
1989 </Tooltip>
1990 ) : null}
1991 </span>
1992 ) : compactRows && isRemote ? (
1993 <Tooltip>
1994 <TooltipTrigger
1995 render={
1996 <span
1997 role="img"
1998 aria-label={props.environmentLabel ?? "Remote environment"}
1999 className="inline-flex size-4 shrink-0 items-center justify-center text-sidebar-muted-foreground/70"
2000 />
2001 }
2002 >
2003 <EnvironmentMachineIcon kind={props.environmentMachine} className="size-3.5" />
2004 </TooltipTrigger>
2005 <TooltipPopup side="top">
2006 {props.environmentLabel ?? "Remote environment"}
2007 </TooltipPopup>
2008 </Tooltip>
1947 ) : null}2009 ) : null}
1948 {compactRows ? (2010 {compactRows ? (
1949 title2011 title
...@@ -1962,18 +2024,6 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {...@@ -1962,18 +2024,6 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
1962 {pinIndicator}2024 {pinIndicator}
1963 {compactRows ? (2025 {compactRows ? (
1964 <>2026 <>
1965 {isRemote ? (
1966 <span
1967 role="img"
1968 aria-label={props.environmentLabel ?? "Remote environment"}
1969 className="inline-flex shrink-0 text-sidebar-muted-foreground/70"
1970 >
1971 <EnvironmentMachineIcon
1972 kind={props.environmentMachine}
1973 className="size-3.5"
1974 />
1975 </span>
1976 ) : null}
1977 {terminalStatusIcon}2027 {terminalStatusIcon}
1978 {topStatus && CompactStatusIcon ? (2028 {topStatus && CompactStatusIcon ? (
1979 isWokeStatus ? (2029 isWokeStatus ? (
...@@ -2001,7 +2051,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {...@@ -2001,7 +2051,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
2001 </span>2051 </span>
2002 )2052 )
2003 ) : null}2053 ) : null}
2004 {prBadge}2054 {renderPrBadge(true)}
2005 </>2055 </>
2006 ) : null}2056 ) : null}
2007 {/* The visible state owns this slot's width: status at rest,2057 {/* The visible state owns this slot's width: status at rest,
...@@ -2031,7 +2081,9 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {...@@ -2031,7 +2081,9 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
2031 >2081 >
2032 {compactRows ? (2082 {compactRows ? (
2033 status === "working" ? (2083 status === "working" ? (
2034 <WorkingDuration startedAt={resolveWorkingStartedAt(thread)} />2084 <span className={topStatus?.className}>
2085 <WorkingDuration startedAt={resolveWorkingStartedAt(thread)} />
2086 </span>
2035 ) : compactCompletedAt ? (2087 ) : compactCompletedAt ? (
2036 <SidebarCompletedTime completedAt={compactCompletedAt} />2088 <SidebarCompletedTime completedAt={compactCompletedAt} />
2037 ) : (2089 ) : (
apps/web/src/components/ThreadStatusIndicators.tsx+4-1
...@@ -144,6 +144,7 @@ export function ThreadPullRequestBadgeControl({...@@ -144,6 +144,7 @@ export function ThreadPullRequestBadgeControl({
144 number,144 number,
145 url,145 url,
146 status,146 status,
147 iconOnly = false,
147 onOpenStack,148 onOpenStack,
148 onOpenPullRequest,149 onOpenPullRequest,
149}: {150}: {
...@@ -152,6 +153,8 @@ export function ThreadPullRequestBadgeControl({...@@ -152,6 +153,8 @@ export function ThreadPullRequestBadgeControl({
152 number?: number | undefined;153 number?: number | undefined;
153 url?: string | undefined;154 url?: string | undefined;
154 status: PrStatusIndicator | null;155 status: PrStatusIndicator | null;
156 /** Dense rows drop the number/layer count and keep only the state glyph. */
157 iconOnly?: boolean;
155 onOpenStack: () => void;158 onOpenStack: () => void;
156 onOpenPullRequest: (event: MouseEvent<HTMLAnchorElement>) => void;159 onOpenPullRequest: (event: MouseEvent<HTMLAnchorElement>) => void;
157}) {160}) {
...@@ -179,7 +182,7 @@ export function ThreadPullRequestBadgeControl({...@@ -179,7 +182,7 @@ export function ThreadPullRequestBadgeControl({
179 const content = (182 const content = (
180 <>183 <>
181 <ThreadPullRequestBadgeIcon icon={badge?.kind ?? "pull-request"} />184 <ThreadPullRequestBadgeIcon icon={badge?.kind ?? "pull-request"} />
182 {isStack ? badge.layers : linkedCount !== null ? `+${linkedCount}` : number}185 {iconOnly ? null : isStack ? badge.layers : linkedCount !== null ? `+${linkedCount}` : number}
183 </>186 </>
184 );187 );
185 return (188 return (