From 5b925a3d052f4cf383fef03f2fa78c405bbbba1f Mon Sep 17 00:00:00 2001
From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Date: Thu, 10 Sep 2026 10:48:49 -0700
Subject: [PATCH 1/4] fix(web): mute settled thread row actions
- Dim settled thread content and icons until hover or focus
- Hide pull request badges when the unsettle action is shown
---
apps/web/src/components/Sidebar.tsx | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx
index 72a119cb76ba..502159ce2e45 100644
--- a/apps/web/src/components/Sidebar.tsx
+++ b/apps/web/src/components/Sidebar.tsx
@@ -1387,6 +1387,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
// content; surface is reserved for interaction (hover, multi-select, route).
const rowSurfaceClassName = cn(
"group/sidebar-row relative w-full cursor-pointer overflow-hidden rounded-md text-left outline-none select-none",
+ variantAction === "unsettle" && "[&:not(:hover):not(:focus-within)_*]:text-secondary-label/70",
props.isActive
? "bg-sidebar-row-active text-sidebar-foreground"
: isSelected
@@ -1469,7 +1470,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
: "text-foreground/90",
)
: cn(
- "truncate group-hover/sidebar-row:text-foreground",
+ "truncate group-focus-within/sidebar-row:text-foreground group-hover/sidebar-row:text-foreground",
shouldRecede
? "text-secondary-label/70"
: props.isActive || isWoke
@@ -1597,8 +1598,8 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
{props.project ? : null}
@@ -1617,6 +1618,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
the time/jump label yields to the settle affordance. */}
{prBadge}
{prBadge &&
+ variantAction !== "unsettle" &&
pr &&
(supportsMultiplePullRequests
? visibleThreadPullRequests(thread.pullRequests).length === 0
From 72c10c8a07ff58635358991a32e37e86be7abe6f Mon Sep 17 00:00:00 2001
From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Date: Thu, 10 Sep 2026 10:59:42 -0700
Subject: [PATCH 2/4] fix: simplify multiple pull request badges
---
apps/mobile/src/state/thread-pr-presentation.ts | 10 +++++++---
apps/web/src/components/Sidebar.tsx | 2 +-
apps/web/src/components/ThreadStatusIndicators.tsx | 13 +++++++------
3 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/apps/mobile/src/state/thread-pr-presentation.ts b/apps/mobile/src/state/thread-pr-presentation.ts
index 48fe3abf5f66..fc310d070acc 100644
--- a/apps/mobile/src/state/thread-pr-presentation.ts
+++ b/apps/mobile/src/state/thread-pr-presentation.ts
@@ -65,11 +65,12 @@ export function presentThreadLinkedPullRequests(
const snapshot = link.snapshot;
const state = badge.kind === "stack" ? badge.state : (snapshot?.state ?? null);
const isDraft = snapshot?.isDraft === true && state === "open";
+ const linkedCount = badge.kind === "pull-request" && badge.others > 0 ? badge.others + 1 : null;
const label =
badge.kind === "stack"
? String(badge.layers)
- : badge.others > 0
- ? `+${badge.others}`
+ : linkedCount !== null
+ ? `+${linkedCount}`
: String(link.number);
return {
kind: badge.kind,
@@ -84,7 +85,10 @@ export function presentThreadLinkedPullRequests(
badge.kind === "stack"
? `${badge.layers} pull requests in stack, ${state ?? "status pending"}`
: `#${link.number} pull request ${state === null ? "status pending" : isDraft ? "draft" : state}${badge.others > 0 ? `, ${badge.others} more linked` : ""}`,
- textClassName: state === null || isDraft ? "text-foreground-muted" : PR_STATE_TEXT_CLASS[state],
+ textClassName:
+ linkedCount !== null || state === null || isDraft
+ ? "text-foreground-muted"
+ : PR_STATE_TEXT_CLASS[state],
};
}
diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx
index 502159ce2e45..b1583a346b56 100644
--- a/apps/web/src/components/Sidebar.tsx
+++ b/apps/web/src/components/Sidebar.tsx
@@ -1486,7 +1486,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: {
);
- // Stacks show their layer count; unrelated links show only the remainder count.
+ // Stacks show their layer count; multiple unrelated links show their total count.
// Plain clicks open T3; individual PR links also support opening the host in a new tab.
const prBadgeShape = supportsMultiplePullRequests
? resolveThreadPullRequestBadge(thread.pullRequests)
diff --git a/apps/web/src/components/ThreadStatusIndicators.tsx b/apps/web/src/components/ThreadStatusIndicators.tsx
index 2d724a726506..e948aa4091ee 100644
--- a/apps/web/src/components/ThreadStatusIndicators.tsx
+++ b/apps/web/src/components/ThreadStatusIndicators.tsx
@@ -154,6 +154,7 @@ export function ThreadPullRequestBadgeControl({
onOpenPullRequest: (event: MouseEvent) => void;
}) {
const isStack = badge?.kind === "stack";
+ const linkedCount = badge?.kind === "pull-request" && badge.others > 0 ? badge.others + 1 : null;
if (!isStack && (number === undefined || url === undefined)) return null;
const label = isStack
? `Stack of ${badge.layers} pull requests, ${badge.state}`
@@ -169,16 +170,16 @@ export function ThreadPullRequestBadgeControl({
"text-xs tabular-nums",
variant === "ghost" &&
"font-normal text-xs! active:scale-100 [--control-icon-color:currentColor]",
- isStack ? PR_STATE_COLOR_CLASS[badge.state] : (status?.colorClass ?? "text-muted-foreground"),
+ linkedCount !== null
+ ? "text-secondary-label"
+ : isStack
+ ? PR_STATE_COLOR_CLASS[badge.state]
+ : (status?.colorClass ?? "text-muted-foreground"),
);
const content = (
<>
- {isStack
- ? badge.layers
- : badge?.kind === "pull-request" && badge.others > 0
- ? `+${badge.others}`
- : number}
+ {isStack ? badge.layers : linkedCount !== null ? `+${linkedCount}` : number}
>
);
return (
From 89e916c374048e4fe3c34cd0db6b40ba6d2b22d1 Mon Sep 17 00:00:00 2001
From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Date: Thu, 10 Sep 2026 11:04:02 -0700
Subject: [PATCH 3/4] test(mobile): expect compact linked PR count
---
apps/mobile/src/state/use-thread-pr.test.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/apps/mobile/src/state/use-thread-pr.test.ts b/apps/mobile/src/state/use-thread-pr.test.ts
index 5fdb90e19c65..7659742054b5 100644
--- a/apps/mobile/src/state/use-thread-pr.test.ts
+++ b/apps/mobile/src/state/use-thread-pr.test.ts
@@ -87,7 +87,8 @@ describe("presentThreadLinkedPullRequests", () => {
it("counts unrelated links without labelling them a stack", () => {
expect(presentThreadLinkedPullRequests([linkedPr(1), linkedPr(2)])).toMatchObject({
kind: "pull-request",
- label: "+1",
+ label: "+2",
+ textClassName: "text-foreground-muted",
});
});
From ddedee61c152073efa433577f4725db369bae96b Mon Sep 17 00:00:00 2001
From: Julius Marminge <51714798+juliusmarminge@users.noreply.github.com>
Date: Thu, 10 Sep 2026 11:05:48 -0700
Subject: [PATCH 4/4] fix(mobile): render neutral linked PR counts in both
lists
---
apps/mobile/src/features/threads/thread-list-items.tsx | 4 ++--
apps/mobile/src/features/threads/thread-list-v2-items.tsx | 8 +-------
apps/mobile/src/state/use-thread-pr.test.ts | 1 +
3 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/apps/mobile/src/features/threads/thread-list-items.tsx b/apps/mobile/src/features/threads/thread-list-items.tsx
index 0ec50c674451..a4da32ef7018 100644
--- a/apps/mobile/src/features/threads/thread-list-items.tsx
+++ b/apps/mobile/src/features/threads/thread-list-items.tsx
@@ -44,11 +44,11 @@ export const THREAD_LIST_COMPACT_INSET = HOME_HORIZONTAL_INSET;
const SIDEBAR_ROW_RADIUS = 12;
function pullRequestTintColor(
- pr: Pick,
+ pr: Pick,
colorScheme: "light" | "dark",
) {
const dark = colorScheme === "dark";
- if (pr.state === "open" && pr.isDraft === true) {
+ if (pr.others > 0 || (pr.state === "open" && pr.isDraft === true)) {
return dark ? "#a1a1aa" : "#71717a";
}
switch (pr.state) {
diff --git a/apps/mobile/src/features/threads/thread-list-v2-items.tsx b/apps/mobile/src/features/threads/thread-list-v2-items.tsx
index 67a917c0079c..937ca5f909a8 100644
--- a/apps/mobile/src/features/threads/thread-list-v2-items.tsx
+++ b/apps/mobile/src/features/threads/thread-list-v2-items.tsx
@@ -880,13 +880,7 @@ export const ThreadListV2Row = memo(function ThreadListV2Row(props: {
? materialYouStyleLayoutActive
? "accent-thread-selected-foreground"
: "accent-user-bubble-foreground"
- : pr.kind === "stack" || pr.isDraft || pr.state === null
- ? "accent-foreground-muted"
- : pr.state === "open"
- ? "accent-adaptive-emerald-600-400"
- : pr.state === "merged"
- ? "accent-adaptive-violet-600-400"
- : "accent-foreground-muted"
+ : "accent-foreground-muted"
}
/>
) : null}
diff --git a/apps/mobile/src/state/use-thread-pr.test.ts b/apps/mobile/src/state/use-thread-pr.test.ts
index 7659742054b5..e36bd7d81434 100644
--- a/apps/mobile/src/state/use-thread-pr.test.ts
+++ b/apps/mobile/src/state/use-thread-pr.test.ts
@@ -88,6 +88,7 @@ describe("presentThreadLinkedPullRequests", () => {
expect(presentThreadLinkedPullRequests([linkedPr(1), linkedPr(2)])).toMatchObject({
kind: "pull-request",
label: "+2",
+ others: 1,
textClassName: "text-foreground-muted",
});
});