Skip to content

Commit 37ca8cc

Browse files
committed
merge: sync palette rows with current redesign base
2 parents 706a988 + 04f69aa commit 37ca8cc

5 files changed

Lines changed: 77 additions & 27 deletions

File tree

‎apps/web/src/components/ThreadStatusIndicators.linkedPr.browser.test.tsx‎

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { expect, it, vi } from "vite-plus/test";
44
import { render } from "../../test/render";
55
import { AppAtomRegistryProvider } from "../rpc/atomRegistry";
66
import { makeThreadFixture } from "../test-fixtures";
7-
import { ThreadRowLeadingStatus } from "./ThreadStatusIndicators";
7+
import { ThreadPullRequestsMiniList, ThreadRowLeadingStatus } from "./ThreadStatusIndicators";
88

99
const environmentId = EnvironmentId.make("environment-test");
1010

@@ -64,3 +64,23 @@ it("updates palette PR status from linked snapshots without a legacy link or bra
6464
await expect.element(screen.getByLabelText("PR #7 - Draft: Linked change")).toBeVisible();
6565
await expect.element(screen.getByLabelText("PR #7, status pending")).not.toBeInTheDocument();
6666
});
67+
68+
it.each([1, 6, 1125])("caps a %i-link hover preview at five PR rows", async (count) => {
69+
const links: ThreadPullRequestLink[] = Array.from({ length: count }, (_, index) => ({
70+
host: "github.com",
71+
repository: "example/repo",
72+
number: index + 1,
73+
url: `https://github.com/example/repo/pull/${index + 1}`,
74+
source: "manual",
75+
linkedAt: "2026-01-01T00:00:00Z",
76+
stack: null,
77+
snapshot: null,
78+
}));
79+
await using screen = await render(<ThreadPullRequestsMiniList pullRequests={links} />);
80+
expect(screen.getByRole("listitem").all()).toHaveLength(Math.min(count, 5) + (count > 5 ? 1 : 0));
81+
await expect.element(screen.getByText("#1", { exact: true })).toBeVisible();
82+
if (count > 5) {
83+
await expect.element(screen.getByText(`${count - 5} more`, { exact: true })).toBeVisible();
84+
await expect.element(screen.getByText("#6", { exact: true })).not.toBeInTheDocument();
85+
}
86+
});

‎apps/web/src/components/ThreadStatusIndicators.tsx‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { pipe } from "effect/Function";
21
import {
32
scopeProjectRef,
43
scopedThreadKey,
@@ -356,18 +355,19 @@ function sidebarPullRequestToneClassName(toneClassName: string): string {
356355
}
357356
}
358357

358+
const THREAD_PULL_REQUEST_PREVIEW_LIMIT = 5;
359+
359360
export function ThreadPullRequestsMiniList({
360361
pullRequests,
361362
onOpenPullRequest,
362363
}: {
363364
pullRequests: ReadonlyArray<ThreadPullRequestLink>;
364365
onOpenPullRequest?: (event: MouseEvent<HTMLAnchorElement>, url: string) => void;
365366
}) {
366-
const lines = pipe(
367-
visibleThreadPullRequests(pullRequests),
368-
resolveThreadPullRequestChains,
369-
pullRequestListLines,
370-
);
367+
const chains = resolveThreadPullRequestChains(pullRequests);
368+
const lines = pullRequestListLines(chains, THREAD_PULL_REQUEST_PREVIEW_LIMIT);
369+
const hiddenCount =
370+
chains.reduce((count, chain) => count + chain.layers.length, 0) - lines.length;
371371
if (lines.length === 0) return null;
372372
return (
373373
// Interactive rows carry their own hover padding; static rows need the wider gap.
@@ -394,6 +394,7 @@ export function ThreadPullRequestsMiniList({
394394
</ThreadPullRequestMiniListItem>
395395
);
396396
})}
397+
{hiddenCount > 0 ? <li className="text-muted-foreground">{hiddenCount} more</li> : null}
397398
</ul>
398399
);
399400
}

‎apps/web/src/components/chat/ThreadDetailsPrRows.tsx‎

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { pipe } from "effect/Function";
21
import { ThreadDetailsControl } from "./ThreadDetailsControl";
32
import type { EnvironmentId, ScopedThreadRef, ThreadPullRequestLink } from "@t3tools/contracts";
43
import {
@@ -92,13 +91,15 @@ export function ThreadDetailsPrRows({
9291
},
9392
});
9493
const currentRow = <ThreadDetailsPrRow {...row} onStopWatching={stopWatching(currentLink)} />;
95-
const rest =
96-
currentLink === null
97-
? []
98-
: pipe(visibleThreadPullRequests(links), resolveThreadPullRequestChains, pullRequestListLines)
99-
.map((line) => line.link)
100-
.filter((link) => threadPullRequestKeyOf(link) !== threadPullRequestKeyOf(currentLink));
101-
if (rest.length === 0) return currentRow;
94+
const visibleLinks = visibleThreadPullRequests(links);
95+
if (currentLink === null || visibleLinks.length <= 1) return currentRow;
96+
const restCount = visibleLinks.length - 1;
97+
const currentKey = threadPullRequestKeyOf(currentLink);
98+
const rest = expanded
99+
? pullRequestListLines(resolveThreadPullRequestChains(visibleLinks))
100+
.map((line) => line.link)
101+
.filter((link) => threadPullRequestKeyOf(link) !== currentKey)
102+
: [];
102103

103104
return (
104105
<>
@@ -120,6 +121,7 @@ export function ThreadDetailsPrRows({
120121
<ThreadDetailsControl
121122
variant="ghost"
122123
size="sm"
124+
aria-expanded={expanded}
123125
onClick={() => setExpanded(!expanded)}
124126
part="row"
125127
tone="muted"
@@ -131,7 +133,7 @@ export function ThreadDetailsPrRows({
131133
// Chevrons, not plus and minus: the plus already means "add" in this card.
132134
icon={expanded ? ChevronUp : ChevronDown}
133135
/>
134-
{expanded ? "Show less" : `Show ${rest.length} more`}
136+
{expanded ? "Show less" : `Show ${restCount} more`}
135137
</ThreadDetailsControl>
136138
</>
137139
);

‎packages/client-runtime/src/threadPullRequests.test.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { beforeEach, describe, expect, it } from "vite-plus/test";
77

88
import {
99
legacyThreadPullRequestKey,
10+
pullRequestListLines,
1011
threadPullRequestSearchTerms,
1112
resolveThreadCurrentPullRequest,
1213
resolveThreadPullRequestChains,
@@ -493,3 +494,23 @@ it("searches the legacy projection when old environments decode to an empty link
493494
threadPullRequestSearchTerms({ pullRequests: [link(34)], linkedPullRequest }),
494495
).not.toContain("#12");
495496
});
497+
498+
it("limits preview rows without changing chain order, depth, or stack counts", () => {
499+
const layers = Array.from({ length: 1125 }, (_, index) =>
500+
link(index + 1, { linkedAt: "2026-01-01T00:00:00Z" }),
501+
);
502+
const chains = [
503+
{ kind: "derived" as const, layers, depths: layers.map((_, index) => index) },
504+
{
505+
kind: "derived" as const,
506+
layers: [link(2000, { linkedAt: "2026-01-02T00:00:00Z" })],
507+
depths: [0],
508+
},
509+
];
510+
const full = pullRequestListLines(chains);
511+
expect(full).toHaveLength(1126);
512+
expect(full[0]!.link.number).toBe(2000);
513+
expect(pullRequestListLines(chains, 5)).toEqual(full.slice(0, 5));
514+
expect(pullRequestListLines(chains, 5)[1]!.stack?.size).toBe(1125);
515+
expect(pullRequestListLines(chains, 0)).toEqual([]);
516+
});

‎packages/source-control-core/src/shared/threadPullRequests.ts‎

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -366,22 +366,28 @@ function chainKeyOf(chain: ThreadPullRequestChain): string {
366366

367367
export function pullRequestListLines(
368368
chains: ReadonlyArray<ThreadPullRequestChain>,
369+
limit = Infinity,
369370
): ReadonlyArray<PullRequestListLine> {
370371
const ordered = [...chains].sort(
371372
(left, right) =>
372373
Math.max(...right.layers.map(latestUpdatedAt)) -
373374
Math.max(...left.layers.map(latestUpdatedAt)),
374375
);
375-
return ordered.flatMap((chain) => {
376+
const lines: PullRequestListLine[] = [];
377+
for (const chain of ordered) {
378+
if (lines.length >= limit) break;
376379
const chainKey = chainKeyOf(chain);
377-
return chain.layers.map((link, index) => ({
378-
link,
379-
depth: chain.depths[index]!,
380-
chainKey,
381-
stack:
382-
index === 0 && chain.layers.length > 1
383-
? { kind: chain.kind, size: chain.layers.length }
384-
: null,
385-
}));
386-
});
380+
for (let index = 0; index < chain.layers.length && lines.length < limit; index += 1) {
381+
lines.push({
382+
link: chain.layers[index]!,
383+
depth: chain.depths[index]!,
384+
chainKey,
385+
stack:
386+
index === 0 && chain.layers.length > 1
387+
? { kind: chain.kind, size: chain.layers.length }
388+
: null,
389+
});
390+
}
391+
}
392+
return lines;
387393
}

0 commit comments

Comments
 (0)