Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
37df728
feat(site/AgentsPage): consolidate git panel tab strip into a view sw…
tracyjohnsonux Jul 6, 2026
16fb0a7
fix(site/AgentsPage): remove background pill on Working state in git …
tracyjohnsonux Jul 7, 2026
ef3b17b
fix(site/AgentsPage): drop chevron and dropdown behavior in git switc…
tracyjohnsonux Jul 7, 2026
09a5ab4
revert(site/AgentsPage): drop the Git tab label count until multi-PR …
tracyjohnsonux Jul 7, 2026
2402e8d
refactor(site/AgentsPage): hoist ViewItem to module scope and drop du…
tracyjohnsonux Jul 7, 2026
7d4732a
fix(site/AgentsPage): drop background pills on all git switcher states
tracyjohnsonux Jul 7, 2026
b6980ee
feat(site/AgentsPage): indent Working entries under the PR row in git…
tracyjohnsonux Jul 7, 2026
eb9a95e
fix(site/AgentsPage): indent both row and highlight for Working entri…
tracyjohnsonux Jul 7, 2026
ef4489c
fix(site/AgentsPage): drop toolbar border and tighten spacing above m…
tracyjohnsonux Jul 7, 2026
e922332
feat(site/AgentsPage): only show PR title tooltip when the title is t…
tracyjohnsonux Jul 7, 2026
635b74f
fix(site/AgentsPage): trim right padding after chevron in git switche…
tracyjohnsonux Jul 7, 2026
00b404c
fix(site/AgentsPage): drop bottom padding on PR title to sit tighter …
tracyjohnsonux Jul 7, 2026
d90d027
fix(site/AgentsPage): narrow PR title tooltip max width
tracyjohnsonux Jul 7, 2026
4eb29cf
Merge branch 'main' into feat/git-panel-view-dropdown
tracyjohnsonux Jul 7, 2026
0a85386
Merge branch 'main' into feat/git-panel-view-dropdown
tracyjohnsonux Jul 7, 2026
47cd08b
fix(site/src/pages/AgentsPage): address git-panel review feedback
tracyjohnsonux Jul 7, 2026
aa978c1
Merge branch 'main' into feat/git-panel-view-dropdown
tracyjohnsonux Jul 7, 2026
8496d6f
chore(site/src/pages/AgentsPage): tighten git-panel comments
tracyjohnsonux Jul 7, 2026
8eca919
Merge branch 'main' into feat/git-panel-view-dropdown
tracyjohnsonux Jul 13, 2026
0d99913
Merge branch 'main' into feat/git-panel-view-dropdown
tracyjohnsonux Aug 11, 2026
73c961b
fix(site/src/pages/AgentsPage): reattach PR title observer after view…
tracyjohnsonux Aug 11, 2026
95fecf9
refactor(site/src/pages/AgentsPage): drop redundant useMemo calls
tracyjohnsonux Aug 11, 2026
c1f82ff
refactor(site/src/pages/AgentsPage): address review feedback on switc…
tracyjohnsonux Aug 11, 2026
82a59ca
Merge branch 'main' into feat/git-panel-view-dropdown
tracyjohnsonux Aug 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@ import {
CopyIcon,
ExternalLinkIcon,
GitBranchIcon,
GitMergeIcon,
GitPullRequestClosedIcon,
GitPullRequestDraftIcon,
GitPullRequestIcon,
} from "lucide-react";
import { type FC, type RefObject, useEffect, useState } from "react";
import { useQuery } from "react-query";
Expand All @@ -19,7 +15,6 @@ import {
TooltipTrigger,
} from "#/components/Tooltip/Tooltip";
import { useClipboard } from "#/hooks/useClipboard";
import { cn } from "#/utils/cn";
import { parsePullRequestUrl } from "../../utils/pullRequest";
import type { ChatMessageInputRef } from "../AgentChatInput";
import { CommentableDiffViewer } from "../DiffViewer/CommentableDiffViewer";
Expand Down Expand Up @@ -58,45 +53,6 @@ const BranchCopyButton: FC<{ branch: string }> = ({ branch }) => {
);
};

// -------------------------------------------------------------------
// PR state badge
// -------------------------------------------------------------------

const PullRequestStateBadge: FC<{
state?: string;
draft?: boolean;
}> = ({ state, draft }) => {
let Icon = GitPullRequestIcon;
let label = "Open";
let colorClasses = "bg-surface-git-added text-git-added-bright";

if (state === "merged") {
Icon = GitMergeIcon;
label = "Merged";
colorClasses = "bg-surface-git-merged text-git-merged-bright";
} else if (state === "closed") {
Icon = GitPullRequestClosedIcon;
label = "Closed";
colorClasses = "bg-surface-git-deleted text-git-deleted-bright";
} else if (draft) {
Icon = GitPullRequestDraftIcon;
label = "Draft";
colorClasses = "text-content-secondary";
}

return (
<span
className={cn(
"inline-flex shrink-0 items-center gap-1 rounded-sm border border-solid border-border-default px-2 text-[13px] font-medium leading-5",
colorClasses,
)}
>
<Icon className="size-3" />
{label}
</span>
);
};

// -------------------------------------------------------------------
// Main component
// -------------------------------------------------------------------
Expand Down Expand Up @@ -152,8 +108,6 @@ export const RemoteDiffPanel: FC<RemoteDiffPanelProps> = ({
// ---------------------------------------------------------------
const pullRequestUrl = diffStatus?.url;
const parsedPr = pullRequestUrl ? parsePullRequestUrl(pullRequestUrl) : null;
const prState = diffStatus?.pull_request_state;
const prDraft = diffStatus?.pull_request_draft;
const baseBranch = diffStatus?.base_branch;
const headBranch = diffStatus?.head_branch;

Expand Down Expand Up @@ -189,7 +143,6 @@ export const RemoteDiffPanel: FC<RemoteDiffPanelProps> = ({
)}
</div>
<div className="ml-auto flex shrink-0 items-center gap-1.5">
<PullRequestStateBadge state={prState} draft={prDraft} />
{diffStatus?.additions || diffStatus?.deletions ? (
<DiffStatBadge
additions={diffStatus.additions}
Expand Down
127 changes: 105 additions & 22 deletions site/src/pages/AgentsPage/components/GitPanel/GitPanel.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,82 @@ export const PullRequestAndWorkingChanges: Story = {
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
// The branch row exposes a button that copies the PR head
// branch name. The aria-label embeds the branch so a single
// query is enough to assert both presence and target.
// The aria-label embeds the head branch, so a single query
// asserts both presence and target.
await expect(
canvas.getByLabelText("Copy branch name: feat/add-mcp-config"),
).toBeVisible();

const switcher = canvas.getByTestId("git-panel-view-switcher");
await expect(switcher).toHaveTextContent("Open");
await expect(switcher).toHaveTextContent("PR #23020");

const title = canvas.getByTestId("git-panel-pr-title");
await expect(title).toHaveTextContent(
"feat(agents): add MCP server configuration to agents",
);
},
};

/**
* Opens the dropdown, asserts the PR + working repos appear, then
* clicks a working entry to verify the view swap.
*/
export const ViewSwitcherOpen: Story = {
args: {
prTab: { prNumber: 23020, chatId: "test-chat" },
remoteDiffStats: makePrStatus({
pull_request_title: "feat: multi-repo workspace support",
head_branch: "feat/multi-repo",
}),
repositories: new Map([
["/home/coder/coder", makeRepo()],
[
"/home/coder/other-project",
makeRepo({
repo_root: "/home/coder/other-project",
branch: "main",
remote_origin: "https://github.com/coder/other-project.git",
unified_diff: secondRepoDiff,
}),
],
]),
},
beforeEach: () => {
spyOn(API.experimental, "getChatDiffContents").mockResolvedValue({
...defaultDiffContents,
diff: sampleDiff,
});
},
play: async ({ canvasElement }) => {
Comment thread
tracyjohnsonux marked this conversation as resolved.
const canvas = within(canvasElement);
const switcher = canvas.getByTestId("git-panel-view-switcher");
await userEvent.click(switcher);

// The Radix menu portals to document.body, so query the full
// document instead of the story canvas.
const menu = await waitFor(() => {
const el = document.querySelector("[role='menu']");
if (!el) throw new Error("menu not found");
return el as HTMLElement;
});
await expect(menu).toHaveTextContent("PR #23020");
await expect(menu).toHaveTextContent("Working");
await expect(menu).toHaveTextContent("coder");
await expect(menu).toHaveTextContent("other-project");

// Selecting a menu item swaps the active view and the trigger
// identifier reflects the new selection.
const otherProjectItem = within(menu).getByText("other-project");
await userEvent.click(otherProjectItem);
await waitFor(() => {
expect(canvas.getByTestId("git-panel-view-switcher")).toHaveTextContent(
"other-project",
);
});
await expect(
canvas.getByTestId("git-panel-view-switcher"),
).toHaveTextContent("Working");
},
};

Expand All @@ -172,6 +242,11 @@ export const DraftPullRequest: Story = {
diff: sampleDiff,
});
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const switcher = canvas.getByTestId("git-panel-view-switcher");
await expect(switcher).toHaveTextContent("Draft");
},
};

/** Merged PR. */
Expand All @@ -194,6 +269,11 @@ export const MergedPullRequest: Story = {
diff: sampleDiff,
});
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const switcher = canvas.getByTestId("git-panel-view-switcher");
await expect(switcher).toHaveTextContent("Merged");
},
};

/** Closed PR. */
Expand All @@ -216,6 +296,11 @@ export const ClosedPullRequest: Story = {
diff: sampleDiff,
});
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
const switcher = canvas.getByTestId("git-panel-view-switcher");
await expect(switcher).toHaveTextContent("Closed");
},
};

/** Branch pushed but no PR opened yet. */
Expand Down Expand Up @@ -367,10 +452,8 @@ export const LargeDiff: Story = {
};

/**
* Regression: when a repo was dirty during this session and then went
* clean (empty unified_diff), the tab must remain visible. Before the
* ever-dirty fix, the tab vanished the moment the diff became empty,
* which is what users saw as "diff disappears between edit_files".
* Regression: a repo that was dirty earlier in the session must
* keep its switcher entry even after its unified_diff empties.
*/
export const EverDirtyRepoGoneClean: Story = {
args: {
Expand All @@ -380,24 +463,24 @@ export const EverDirtyRepoGoneClean: Story = {
everDirty: new Set(["/home/coder/coder"]),
},
play: async ({ canvasElement }) => {
// The repo tab is still present (identified by the 'Working'
// prefix used by GitPanel's tab-strip button) even though the
// current diff is empty, because it was dirty earlier in the
// session.
const tabs = Array.from(canvasElement.querySelectorAll("button")).filter(
(b) => (b.textContent ?? "").startsWith("Working"),
// Before the ever-dirty fix, the entry vanished the moment the
// diff emptied and users saw the diff "disappear between
// edit_files". The entry must persist here.
const switcher = canvasElement.querySelector(
"[data-testid='git-panel-view-switcher']",
);
expect(tabs).toHaveLength(1);
expect(switcher).not.toBeNull();
expect(switcher?.textContent ?? "").toContain("Working");

// The content pane shows the diff viewer's empty-diff state.
// The content pane falls through to the diff viewer's empty state.
expect(canvasElement.textContent ?? "").toContain("No file changes");
},
};

/**
* Baseline: a repo reported clean from the start (never dirty in
* this session) has no tab. Ensures the ever-dirty fix did not
* regress the "nothing to show" case.
* this session) has no switcher entry. Ensures the ever-dirty fix
* did not regress the "nothing to show" case.
*/
export const CleanRepoFromStart: Story = {
args: {
Expand All @@ -407,11 +490,11 @@ export const CleanRepoFromStart: Story = {
everDirty: new Set(),
},
play: async ({ canvasElement }) => {
// No local repo tab should appear in the tab strip. The
// 'Working' prefix is GitPanel's tab-strip label contract.
const tabs = Array.from(canvasElement.querySelectorAll("button")).filter(
(b) => (b.textContent ?? "").startsWith("Working"),
const switcher = canvasElement.querySelector(
Comment thread
tracyjohnsonux marked this conversation as resolved.
"[data-testid='git-panel-view-switcher']",
);
expect(tabs).toHaveLength(0);
expect(switcher).not.toBeNull();
expect(switcher?.textContent ?? "").toContain("No changes");
expect(switcher?.textContent ?? "").not.toContain("Working");
},
};
Loading
Loading