Skip to content
Open
Changes from all commits
Commits
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
84 changes: 75 additions & 9 deletions apps/web/src/components/BranchToolbarBranchSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { EnvironmentId, VcsRef, ThreadId } from "@t3tools/contracts";
import { LegendList, type LegendListRef } from "@legendapp/list/react";
import { ChevronDownIcon, GitBranchIcon, SearchIcon } from "lucide-react";
import {
type MouseEvent,
useCallback,
useDeferredValue,
useEffect,
Expand All @@ -16,6 +17,7 @@ import {

import { useComposerDraftStore, type DraftId } from "../composerDraftStore";
import { readEnvironmentApi } from "../environmentApi";
import { readLocalApi } from "../localApi";
import { useVcsStatus } from "../lib/vcsStatusState";
import { useVcsRefs, vcsRefManager } from "../lib/vcsRefState";
import { newCommandId } from "../lib/utils";
Expand All @@ -32,6 +34,11 @@ import {
resolveEffectiveEnvMode,
shouldIncludeBranchPickerItem,
} from "./BranchToolbar.logic";
import {
ChangeRequestStatusIcon,
prStatusIndicator,
resolveThreadPr,
} from "./ThreadStatusIndicators";
import { Button } from "./ui/button";
import {
Combobox,
Expand All @@ -44,6 +51,7 @@ import {
ComboboxTrigger,
} from "./ui/combobox";
import { stackedThreadToast, toastManager } from "./ui/toast";
import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";

interface BranchToolbarBranchSelectorProps {
className?: string;
Expand Down Expand Up @@ -514,6 +522,41 @@ export function BranchToolbarBranchSelector({
resolvedActiveBranch,
});

// PR pill shown next to the branch selector when the active branch has one.
const branchPr = resolveThreadPr(resolvedActiveBranch, branchStatusQuery.data ?? null);
const branchPrStatus = prStatusIndicator(
branchPr,
branchStatusQuery.data?.sourceControlProvider,
);
// Action-oriented tooltip (the pill opens the PR), distinct from the sidebar's
// state-description tooltip.
const branchPrTooltip = branchPr
? `Open ${sourceControlPresentation.terminology.singular} #${branchPr.number} (${branchPr.state}) in browser`
: "";
const openPrLink = useCallback((event: MouseEvent<HTMLElement>, prUrl: string) => {
event.preventDefault();
event.stopPropagation();

const api = readLocalApi();
if (!api) {
toastManager.add({
type: "error",
title: "Link opening is unavailable.",
});
return;
}

void api.shell.openExternal(prUrl).catch((error) => {
toastManager.add(
stackedThreadToast({
type: "error",
title: "Unable to open pull request link",
description: error instanceof Error ? error.message : "An error occurred.",
}),
);
});
}, []);

function renderPickerItem(itemValue: string, index: number) {
if (checkoutPullRequestItemValue && itemValue === checkoutPullRequestItemValue) {
return (
Expand Down Expand Up @@ -610,15 +653,38 @@ export function BranchToolbarBranchSelector({
open={isBranchMenuOpen}
value={resolvedActiveBranch}
>
<ComboboxTrigger
render={<Button variant="ghost" size="xs" />}
className={cn("min-w-0 text-muted-foreground/70 hover:text-foreground/80", className)}
disabled={isInitialBranchesLoadPending || isBranchActionPending}
>
<GitBranchIcon className="size-3 shrink-0 opacity-70" />
<span className="min-w-0 max-w-[240px] truncate">{triggerLabel}</span>
<ChevronDownIcon className="size-3 shrink-0 opacity-50" />
</ComboboxTrigger>
<div className={cn("flex min-w-0 items-center gap-1", className)}>
{branchPr && branchPrStatus ? (
<Tooltip>
<TooltipTrigger
render={
<button
type="button"
aria-label={branchPrTooltip}
onClick={(event) => openPrLink(event, branchPrStatus.url)}
className={cn(
"inline-flex shrink-0 items-center gap-0.5 rounded px-1 py-0.5 text-[11px] font-medium tabular-nums transition-colors hover:bg-muted/60",
branchPrStatus.colorClass,
)}
/>
}
>
<ChangeRequestStatusIcon className="size-3" />
<span>#{branchPr.number}</span>
</TooltipTrigger>
<TooltipPopup side="top">{branchPrTooltip}</TooltipPopup>
</Tooltip>
) : null}
<ComboboxTrigger
render={<Button variant="ghost" size="xs" />}
className="min-w-0 text-muted-foreground/70 hover:text-foreground/80"
disabled={isInitialBranchesLoadPending || isBranchActionPending}
>
<GitBranchIcon className="size-3 shrink-0 opacity-70" />
<span className="min-w-0 max-w-[240px] truncate">{triggerLabel}</span>
<ChevronDownIcon className="size-3 shrink-0 opacity-50" />
</ComboboxTrigger>
</div>
<ComboboxPopup align="end" side="top" className="flex w-80 flex-col">
<div className="shrink-0 px-3 pt-2.5">
<div className="relative -translate-y-px border-b border-border/70 pb-1.5 transition-colors focus-within:border-ring">
Expand Down
Loading