Skip to content
Closed
Show file tree
Hide file tree
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
57 changes: 57 additions & 0 deletions apps/web/src/components/BranchToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ export interface BranchToolbarHandle {
}

interface BranchToolbarProps {
layout?: "composer" | "panel";
panelSection?: "all" | "workspace" | "branch";
forceNewWorktree?: boolean;
ref?: Ref<BranchToolbarHandle>;
environmentId: EnvironmentId;
Expand Down Expand Up @@ -483,6 +485,8 @@ function useLabelsOverflow(element: HTMLDivElement | null): boolean {
}

export const BranchToolbar = memo(function BranchToolbar({
layout = "composer",
panelSection = "all",
forceNewWorktree = false,
ref,
environmentId,
Expand Down Expand Up @@ -602,6 +606,59 @@ export const BranchToolbar = memo(function BranchToolbar({

if (!hasActiveThread || !activeProject) return null;

if (layout === "panel") {
return (
<div className="flex w-full flex-col gap-0.5" data-thread-panel-run-context>
{panelSection !== "branch" && showEnvironmentIndicator && availableEnvironments ? (
<BranchToolbarEnvironmentSelector
displayMode="panel"
autoEnvironmentLabel={autoEnvironmentLabel}
onAutoEnvironment={onAutoEnvironment}
envLocked={envLocked}
environmentId={environmentId}
availableEnvironments={availableEnvironments}
{...(showEnvironmentPicker && onEnvironmentChange ? { onEnvironmentChange } : {})}
/>
) : null}
{panelSection !== "branch" && showGitControls ? (
<BranchToolbarEnvModeSelector
displayMode="panel"
forceNewWorktree={forceNewWorktree}
envLocked={envModeLocked}
effectiveEnvMode={effectiveEnvMode}
activeWorktreePath={activeWorktreePath}
onEnvModeChange={onEnvModeChange}
previousWorktreeLabel={previousWorktreeLabel}
onUsePreviousWorktree={onUsePreviousWorktree}
/>
) : null}
{panelSection !== "workspace" && showGitControls ? (
<BranchToolbarBranchSelector
ref={branchSelectorRef}
displayMode="panel"
className="w-full"
forceNewWorktree={forceNewWorktree}
environmentId={environmentId}
threadId={threadId}
{...(draftId ? { draftId } : {})}
envLocked={envLocked}
{...(forceNewWorktree
? { effectiveEnvModeOverride: "worktree" }
: effectiveEnvModeOverride
? { effectiveEnvModeOverride }
: {})}
{...(activeThreadBranchOverride !== undefined ? { activeThreadBranchOverride } : {})}
{...(onActiveThreadBranchOverrideChange ? { onActiveThreadBranchOverrideChange } : {})}
startFromOrigin={startFromOrigin}
onStartFromOriginChange={onStartFromOriginChange}
{...(onCheckoutPullRequestRequest ? { onCheckoutPullRequestRequest } : {})}
{...(onComposerFocusRequest ? { onComposerFocusRequest } : {})}
/>
) : null}
</div>
);
}

return (
<ComposerSurface.ContextStrip
ref={setStripElement}
Expand Down
56 changes: 45 additions & 11 deletions apps/web/src/components/BranchToolbarBranchSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
import {
THREAD_DETAILS_PANEL_ICON_CLASS,
THREAD_DETAILS_PANEL_SELECT_ROW_CLASS,
THREAD_DETAILS_PANEL_ROW_POPUP_CLASS,
THREAD_DETAILS_PANEL_CHEVRON_CLASS,
} from "./chat/threadDetailsPanelStyles";
import { useSupportsMultiplePullRequests } from "~/hooks/useSupportsMultiplePullRequests";
import { resolveThreadCurrentPullRequestLink } from "@t3tools/shared/threadPullRequests";
import { useRightPanelStore } from "../rightPanelStore";
Expand Down Expand Up @@ -79,6 +85,7 @@ export interface BranchToolbarBranchSelectorHandle {
}

interface BranchToolbarBranchSelectorProps {
displayMode?: "toolbar" | "panel";
forceNewWorktree?: boolean;
ref?: Ref<BranchToolbarBranchSelectorHandle>;
className?: string;
Expand All @@ -100,6 +107,7 @@ function toBranchActionErrorMessage(error: unknown): string {
}

export function BranchToolbarBranchSelector({
displayMode = "toolbar",
forceNewWorktree = false,
ref,
className,
Expand Down Expand Up @@ -785,7 +793,11 @@ export function BranchToolbarBranchSelector({
value={resolvedActiveBranch}
>
<div
className={cn("flex min-w-0 items-center gap-1", className)}
className={cn(
"flex min-w-0 items-center gap-1",
displayMode === "panel" && "w-full",
className,
)}
data-composer-context-control
>
<ThreadPullRequestBadgeControl
Expand All @@ -803,20 +815,33 @@ export function BranchToolbarBranchSelector({
pointer-events-none, so the trigger itself never sees right-clicks
while refs are loading or a branch action is pending. */}
<span
className="flex min-w-0"
className={cn("flex min-w-0", displayMode === "panel" && "flex-1")}
onContextMenu={(event) => handleBranchContextMenu(event, resolvedActiveBranch)}
>
<ComboboxTrigger
render={<Button variant="ghost" size="xs" />}
render={<Button variant="ghost" size={displayMode === "panel" ? "sm" : "xs"} />}
// No press-scale: the popup aligns live to this trigger, so a
// momentary 0.97 shrink would drag the open popup ~3px sideways.
className="min-w-0 max-w-full font-normal text-muted-foreground/70 text-xs! hover:text-foreground/80 active:scale-100"
className={cn(
"min-w-0 max-w-full font-normal text-muted-foreground/70 text-xs! hover:text-foreground/80 active:scale-100",
displayMode === "panel" && THREAD_DETAILS_PANEL_SELECT_ROW_CLASS,
)}
disabled={isInitialBranchesLoadPending || isBranchActionPending}
>
<GitBranchIcon className="size-3 shrink-0 opacity-70" />
<GitBranchIcon
className={
displayMode === "panel"
? THREAD_DETAILS_PANEL_ICON_CLASS
: "size-3 shrink-0 opacity-70"
}
/>
<span
data-composer-label
className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
className={
displayMode === "panel"
? "min-w-0 flex-1 truncate text-left"
: "min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
}
>
<span
data-composer-label-motion
Expand All @@ -825,15 +850,24 @@ export function BranchToolbarBranchSelector({
{triggerLabel}
</span>
</span>
<ChevronDownIcon className="size-3 shrink-0 opacity-50" />
{displayMode === "panel" ? (
<span data-slot="select-icon">
<ChevronDownIcon className={THREAD_DETAILS_PANEL_CHEVRON_CLASS} />
</span>
) : (
<ChevronDownIcon className="size-3 shrink-0 opacity-50" />
)}
</ComboboxTrigger>
</span>
</div>
<ComboboxPopup
align="end"
side="top"
className="flex w-80 flex-col"
{...composerFloatingLayerProps}
align={displayMode === "panel" ? "start" : "end"}
side={displayMode === "panel" ? "bottom" : "top"}
className={cn(
"flex flex-col",
displayMode === "panel" ? THREAD_DETAILS_PANEL_ROW_POPUP_CLASS : "w-80",
)}
{...(displayMode === "toolbar" ? composerFloatingLayerProps : {})}
>
<ComboboxSearchInput
placeholder="Search refs..."
Expand Down
79 changes: 68 additions & 11 deletions apps/web/src/components/BranchToolbarEnvModeSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
import { cn } from "../lib/utils";
import {
THREAD_DETAILS_PANEL_ICON_CLASS,
THREAD_DETAILS_PANEL_LOCKED_ROW_CLASS,
THREAD_DETAILS_PANEL_SELECT_ROW_CLASS,
THREAD_DETAILS_PANEL_ROW_POPUP_CLASS,
} from "./chat/threadDetailsPanelStyles";
import { FolderGit2Icon, FolderGitIcon, FolderIcon, HistoryIcon } from "lucide-react";
import { memo, useMemo } from "react";

Expand All @@ -22,6 +29,7 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
const PREVIOUS_WORKTREE_SELECT_VALUE = "previous-worktree";

interface BranchToolbarEnvModeSelectorProps {
displayMode?: "toolbar" | "panel";
forceNewWorktree?: boolean;
envLocked: boolean;
effectiveEnvMode: EnvMode;
Expand All @@ -32,6 +40,7 @@ interface BranchToolbarEnvModeSelectorProps {
}

export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSelector({
displayMode = "toolbar",
forceNewWorktree = false,
envLocked,
effectiveEnvMode,
Expand All @@ -58,19 +67,38 @@ export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSe
<Tooltip>
<TooltipTrigger
render={<span />}
className="inline-flex h-7 min-w-0 items-center gap-1 border border-transparent px-[calc(--spacing(2)-1px)] font-normal text-muted-foreground/70 text-xs sm:h-6"
className={cn(
"inline-flex h-7 min-w-0 items-center gap-1 border border-transparent px-[calc(--spacing(2)-1px)] font-normal text-muted-foreground/70 text-xs sm:h-6",
displayMode === "panel" && THREAD_DETAILS_PANEL_LOCKED_ROW_CLASS,
)}
data-composer-context-control
>
{forceNewWorktree ? (
<FolderGit2Icon className="size-3 shrink-0" />
<FolderGit2Icon
className={
displayMode === "panel" ? THREAD_DETAILS_PANEL_ICON_CLASS : "size-3 shrink-0"
}
/>
) : activeWorktreePath ? (
<FolderGitIcon className="size-3 shrink-0" />
<FolderGitIcon
className={
displayMode === "panel" ? THREAD_DETAILS_PANEL_ICON_CLASS : "size-3 shrink-0"
}
/>
) : (
<FolderIcon className="size-3 shrink-0" />
<FolderIcon
className={
displayMode === "panel" ? THREAD_DETAILS_PANEL_ICON_CLASS : "size-3 shrink-0"
}
/>
)}
<span
data-composer-label
className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
className={
displayMode === "panel"
? "min-w-0 flex-1 truncate text-left"
: "min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
}
>
<span
data-composer-label-motion
Expand Down Expand Up @@ -110,23 +138,42 @@ export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSe
<SelectTrigger
variant="ghost"
size="xs"
className="min-w-0 shrink font-normal text-xs!"
className={cn(
"min-w-0 shrink font-normal text-xs!",
displayMode === "panel" && THREAD_DETAILS_PANEL_SELECT_ROW_CLASS,
)}
aria-label="Workspace"
data-composer-shortcut="composer.workspace"
data-composer-context-control
/>
}
>
{effectiveEnvMode === "worktree" ? (
<FolderGit2Icon className="size-3" />
<FolderGit2Icon
className={
displayMode === "panel" ? THREAD_DETAILS_PANEL_ICON_CLASS : "size-3 shrink-0"
}
/>
) : activeWorktreePath ? (
<FolderGitIcon className="size-3" />
<FolderGitIcon
className={
displayMode === "panel" ? THREAD_DETAILS_PANEL_ICON_CLASS : "size-3 shrink-0"
}
/>
) : (
<FolderIcon className="size-3" />
<FolderIcon
className={
displayMode === "panel" ? THREAD_DETAILS_PANEL_ICON_CLASS : "size-3 shrink-0"
}
/>
)}
<span
data-composer-label
className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
className={
displayMode === "panel"
? "min-w-0 flex-1 truncate text-left"
: "min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
}
>
<span
data-composer-label-motion
Expand All @@ -135,14 +182,24 @@ export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSe
<SelectValue />
</span>
</span>
{displayMode === "panel" && effectiveEnvMode === "worktree" && !activeWorktreePath ? (
<span className="shrink-0 text-[10px] font-normal text-muted-foreground/70">
Create
</span>
) : null}
</TooltipTrigger>
<TooltipPopup>
{effectiveEnvMode === "worktree"
? resolveEnvModeLabel("worktree")
: resolveCurrentWorkspaceLabel(activeWorktreePath)}
</TooltipPopup>
</Tooltip>
<SelectPopup alignItemWithTrigger={false} {...composerFloatingLayerProps}>
<SelectPopup
alignItemWithTrigger={false}
{...(displayMode === "toolbar"
? composerFloatingLayerProps
: { popupClassName: THREAD_DETAILS_PANEL_ROW_POPUP_CLASS })}
>
<SelectGroup>
<SelectGroupLabel>Workspace</SelectGroupLabel>
<SelectItem value="local">
Expand Down
Loading
Loading