Skip to content

feat(site/src/pages/AgentsPage): swap git panel CTA with diff options - #28049

Open
tracyjohnsonux wants to merge 12 commits into
mainfrom
tj/git-panel-swap-cta
Open

feat(site/src/pages/AgentsPage): swap git panel CTA with diff options#28049
tracyjohnsonux wants to merge 12 commits into
mainfrom
tj/git-panel-swap-cta

Conversation

@tracyjohnsonux

@tracyjohnsonux tracyjohnsonux commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What

Swaps the position of the primary CTA and the diff-style controls in the Git panel, per design:

  • Top toolbar now shows the view-aware primary CTA (View PR / Commit) next to the Refresh button.
  • Per-view sub-header now houses the unified/split diff-style toggle alongside the diff stat badge.

The CTA follows the active view:

  • Remote/PR view → View PR link when a pullRequestUrl is available.
  • Working repo view → Commit button, disabled when the repo has no unified diff.
  • No actionable target (empty state, branch-only without a PR URL) → CTA is hidden and only Refresh remains.

The unified/split segmented control is extracted into a shared DiffStyleToggle so the remote (RemoteDiffPanel) and local (RepoHeader) sub-headers stay in sync.

Screenshots

Before After
image image image
Diff-style toggle + Refresh in row 1; Commit / View PR buried in row 2 Commit / View PR + Refresh in row 1; diff-style toggle in the sub-header

Note there isn't any content size changes, just the screenshots didn't match.

Storybook stories (dark theme) after the change:

  • PullRequestAndWorkingChanges — remote view, View PR in the toolbar, diff toggle in the PR sub-header.
  • WorkingChangesOnly — local view, Commit in the toolbar, diff toggle in the repo header.
  • BranchOnly — pushed branch without a PR, no CTA in the toolbar.
  • DraftPullRequest / MergedPullRequest — CTA and toggle behave the same across PR states.

Notes

  • GitPanel public props are unchanged; the rewire is internal.
  • RemoteDiffPanel gains a required onDiffStyleChange prop (only used by GitPanel).
  • The GitNotActive story previously asserted that the Unified/Split diff buttons render disabled. They now live in the per-view sub-header, which does not render when there is no git context, so the assertions are switched to queryByLabelText(...) === null.
Implementation notes
  • New site/src/pages/AgentsPage/components/DiffViewer/DiffStyleToggle.tsx — shared segmented control with aria-pressed state.
  • GitPanel.tsx gains an internal PrimaryCta that picks the CTA element based on effectiveView and returns null when there is nothing to act on.
  • RepoHeader drops the inline Commit button; RemoteDiffPanel drops the inline View PR anchor.
  • handleDiffStyleChange is forwarded from GitPanel to both content components so state ownership stays at the panel root.

Generated with Coder Agents.

Move the primary CTA (View PR / Commit) into the top toolbar next to
Refresh, and move the unified/split diff-style toggle down into the
per-view sub-header alongside the diff stat badge.

The CTA now follows the active view: a View PR link for the remote/PR
view and a Commit button (disabled when the working repo has no
unified diff) for a working repo view. The diff-style toggle lives
with the diff it configures.

Extract the segmented toggle into a shared DiffStyleToggle component
so the remote and local sub-headers stay in sync.

Generated with Coder Agents.
… link style for View PR

Unify DiffStatBadge and DiffStyleToggle border radius on rounded-md so
the two controls in the sub-header have matching corners.

Restyle the View PR CTA as an offsite link (text-content-link with a
hover underline, no border or filled background) to match how other
external links render across the Agents UI. The Commit CTA on the
working repo view keeps its bordered button treatment because it is a
destructive-adjacent action, not an offsite navigation.

Generated with Coder Agents.
Restore the bordered button treatment for the View PR CTA. The offsite
link style did not read as a clear call to action next to the Refresh
icon, so match the Commit CTA treatment instead.

Generated with Coder Agents.
Drop the divided state pill from the view-switcher trigger. The PR
state (open, draft, merged, closed) is now conveyed by the icon color
alone, so the trigger renders "PR #NNNN" without the redundant "Open"
label and vertical divider. Local repos keep the "Working" prefix
because a bare repo name does not read as a status on its own.

Update PR-state story assertions to match the new trigger content:
identifier is asserted positively and the removed state label is
asserted absent.

Generated with Coder Agents.
…itcher icons

The git view switcher trigger conveys state through icon color alone,
so add role=img aria-label wrappers around each state icon in both the
trigger and the dropdown items.

Labels follow a consistent pattern so screen readers announce the state
clearly:

- Pull request status: Open | Draft | Merged | Closed
- Git view: Branch
- Git view: Working (<repo>)

Generated with Coder Agents.
…utton feel from single-item trigger

Split the git view switcher trigger into two shapes:

- Single item: no border, background, or padding. Just the state icon
  and identifier as inline text. The icon still carries an aria-label
  so the state is announced to assistive tech.
- Multiple items: restore the original bordered dropdown pill with the
  state on the left (border-r divider) and the identifier on the right,
  ending in a chevron.

Update the affected story assertions to match: dropdown stories assert
the state label appears, single-item stories assert the state aria-label
is present via getByRole("img").

Generated with Coder Agents.
…m dropdown trigger

Trim the multi-item view-switcher trigger so only the state icon,
identifier, and chevron remain. The bordered pill (button chrome) and
chevron stay because they signal the dropdown affordance. Local repos
keep the "Working" prefix because the identifier alone (a repo name)
does not read as a status.

Add an aria-label on the state icon so screen readers still announce
PR status now that the visible label is gone.

Generated with Coder Agents.
Bump the dropdown chevron from size-3 at opacity-70 to size-3.5 with
text-content-secondary so the affordance stays visible against the
pill background.

Generated with Coder Agents.
…git switcher

The refactored dropdown trigger dropped the surrounding text-content-primary
context, which the chevron relied on via opacity-70. Add that class to the
trigger wrapper and put the chevron back to its original size and opacity.

Generated with Coder Agents.
…witcher trigger

Cut the trigger's horizontal padding from px-2 to px-1 so the pill hugs
its contents.

Generated with Coder Agents.
Drop unused disabled/disabledTitle props on DiffStyleToggle (no caller
passes them), tighten repetitive story comments, and collapse the
stale iconLabel JSDoc that predated the dropdown trigger reusing it.

Generated with Coder Agents.
@tracyjohnsonux

tracyjohnsonux commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

I do have a storybook for this. I wasn't expecting it to be such a big PR. Guess there are a lot of stories to update. https://6006--dev--gitpanel-cta-swap--tracy--apps.dev.coder.com/?path=/story/pages-agentspage-gitpanel--closed-pull-request&globals=theme:dark (normal admin login)

Comment on lines +103 to +105
/** Accessible label for the state icon. Screen readers announce this
* because the state is conveyed by icon color alone on the trigger. */
iconLabel: string;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/** Accessible label for the state icon. Screen readers announce this
* because the state is conveyed by icon color alone on the trigger. */
iconLabel: string;
iconLabel: string;

Comment on lines +467 to +468
// Single-item trigger: flat text, no button chrome. Icon aria-label
// keeps the state readable when there is no dropdown context.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Single-item trigger: flat text, no button chrome. Icon aria-label
// keeps the state readable when there is no dropdown context.

Comment on lines +489 to +492
// Dropdown trigger: bordered pill with the state icon, identifier,
// and chevron. The PR state label (Open/Draft/Merged/Closed) is
// dropped because the icon color already conveys it; local repos
// keep the "Working" prefix so the identifier reads as a status.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Dropdown trigger: bordered pill with the state icon, identifier,
// and chevron. The PR state label (Open/Draft/Merged/Closed) is
// dropped because the icon color already conveys it; local repos
// keep the "Working" prefix so the identifier reads as a status.

Comment on lines 570 to +579
};

// ---------------------------------------------------------------
// Primary CTA (View PR / Commit) shown next to Refresh in the toolbar.
// The action follows the active view: a link to the PR on the remote
// view, or a Commit button on a working repo. Renders nothing when
// there is no actionable target.
// ---------------------------------------------------------------

const PrimaryCta: FC<{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
};
// ---------------------------------------------------------------
// Primary CTA (View PR / Commit) shown next to Refresh in the toolbar.
// The action follows the active view: a link to the PR on the remote
// view, or a Commit button on a working repo. Renders nothing when
// there is no actionable target.
// ---------------------------------------------------------------
const PrimaryCta: FC<{
};
const PrimaryCta: FC<{

Comment on lines +586 to +587
const ctaClassName =
"inline-flex h-6 shrink-0 items-center gap-1 rounded-md border border-solid border-border-default bg-surface-primary px-2 text-[13px] font-medium leading-none text-content-primary no-underline transition-colors hover:bg-surface-secondary disabled:pointer-events-none disabled:opacity-50";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inline this definition of class at use. If we're worried about duplicating the class name, figure out a better component design to avoid that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants