Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ export function IssueLinkCommand({
<TooltipContent side="bottom">{tooltipLabel}</TooltipContent>
</Tooltip>
<PopoverContent
className="w-80 p-0"
className="w-[440px] p-0"
align="start"
side="bottom"
onWheel={(event) => event.stopPropagation()}
Expand All @@ -179,7 +179,7 @@ export function IssueLinkCommand({
Show closed
</label>
</div>
<CommandList className="max-h-[280px]">
<CommandList className="max-h-[420px]">
{filteredTasks.length === 0 && (
<CommandEmpty>
{showClosed ? "No issues found." : "No open issues found."}
Expand Down Expand Up @@ -211,25 +211,35 @@ export function IssueLinkCommand({
task.externalUrl ?? undefined,
)
}
className="group"
className="group items-start gap-3 rounded-md px-2.5 py-2"
>
{status ? (
<StatusIcon
type={status.type}
color={status.color}
progress={status.progressPercent ?? undefined}
/>
) : (
<span className="size-3.5 shrink-0 rounded-full border border-muted-foreground/40" />
)}
<span className="max-w-24 shrink-0 truncate font-mono text-xs text-muted-foreground">
{task.slug}
<span className="mt-0.5 flex size-4 shrink-0 items-center justify-center">
{status ? (
<StatusIcon
type={status.type}
color={status.color}
progress={status.progressPercent ?? undefined}
/>
) : (
<span className="size-3.5 rounded-full border border-muted-foreground/40" />
)}
</span>
<span className="min-w-0 flex-1 truncate text-xs">
{task.title}
</span>
<span className="shrink-0 hidden text-xs text-muted-foreground group-data-[selected=true]:inline">
Link ↵
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
<span className="truncate text-sm leading-snug">
{task.title}
</span>
<span className="flex items-center gap-1.5 text-[11px] text-muted-foreground">
<span className="font-mono">{task.slug}</span>
{status ? (
<>
<span aria-hidden>·</span>
<span className="capitalize">{status.type}</span>
</>
) : null}
</span>
</div>
<span className="ml-2 hidden shrink-0 self-center text-[11px] text-muted-foreground group-data-[selected=true]:inline">
</span>
</CommandItem>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,17 @@ import {
} from "@superset/ui/command";
import { Popover, PopoverContent, PopoverTrigger } from "@superset/ui/popover";
import { Tabs, TabsList, TabsTrigger } from "@superset/ui/tabs";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@superset/ui/tooltip";
import { useEffect, useRef, useState } from "react";
import { GoGitBranch } from "react-icons/go";
import { GoGitBranch, GoGlobe } from "react-icons/go";
import { HiCheck, HiChevronUpDown } from "react-icons/hi2";
import { LuFolderOpen } from "react-icons/lu";
import { PLATFORM } from "renderer/hotkeys";
import { formatRelativeTime } from "renderer/lib/formatRelativeTime";
import type { BranchFilter, BranchRow } from "../../../hooks/useBranchContext";
import { FormPickerTrigger } from "../FormPickerTrigger";

const MOD_KEY = PLATFORM === "mac" ? "⌘" : "Ctrl";

interface CompareBaseBranchPickerProps {
effectiveCompareBaseBranch: string | null;
defaultBranch: string | null | undefined;
Expand All @@ -37,13 +35,9 @@ interface CompareBaseBranchPickerProps {
branchName: string,
source: "local" | "remote-tracking",
) => void;
onCheckoutBranch: (branchName: string) => void;
onOpenExisting: (branchName: string) => void;
// Authoritative (cloud-synced) answer to "does a workspace row exist for
// this branch on this host?". Computed from the v2Workspaces collection
// so it stays in sync with soft-deletes. Trumps any server-side
// `hasWorkspace` snapshot, which can be stale after deletion.
hasWorkspaceForBranch: (branchName: string) => boolean;
// Server's workspaces.create resolves between open-tracked, adopt-foreign-
// worktree, and fresh-create — the picker doesn't decide.
onOpenWorkspace: (branchName: string) => void;
}

export function CompareBaseBranchPicker({
Expand All @@ -60,11 +54,11 @@ export function CompareBaseBranchPicker({
hasNextPage,
onLoadMore,
onSelectCompareBaseBranch,
onCheckoutBranch,
onOpenExisting,
hasWorkspaceForBranch,
onOpenWorkspace,
}: CompareBaseBranchPickerProps) {
const [open, setOpen] = useState(false);
// Mirror cmdk's selected row so Mod+Enter can resolve it without DOM lookup.
const [selectedValue, setSelectedValue] = useState("");

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.

P2: Reset or validate selectedValue before handling Mod+Enter; otherwise the shortcut can open a stale branch selection.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/desktop/src/renderer/routes/_authenticated/components/DashboardNewWorkspaceModal/components/DashboardNewWorkspaceForm/PromptGroup/components/CompareBaseBranchPicker/CompareBaseBranchPicker.tsx, line 64:

<comment>Reset or validate `selectedValue` before handling Mod+Enter; otherwise the shortcut can open a stale branch selection.</comment>

<file context>
@@ -55,6 +58,10 @@ export function CompareBaseBranchPicker({
+	// Mirror cmdk's selected-row value so a Mod+Enter keydown can resolve it
+	// to the branch name without traversing DOM. Without this, keyboard users
+	// have no path to `onOpenWorkspace`.
+	const [selectedValue, setSelectedValue] = useState("");
 	const sentinelRef = useRef<HTMLDivElement | null>(null);
 
</file context>

Tip: Review your code locally with the cubic CLI to iterate faster.

const sentinelRef = useRef<HTMLDivElement | null>(null);

useEffect(() => {
Expand Down Expand Up @@ -113,20 +107,39 @@ export function CompareBaseBranchPicker({
<GoGitBranch className="size-3 shrink-0" />
{isBranchesLoading && branches.length === 0 ? (
<span className="h-2.5 w-14 rounded-sm bg-muted-foreground/15 animate-pulse" />
) : (
) : effectiveCompareBaseBranch ? (
<span className="font-mono truncate">
{effectiveCompareBaseBranch || "..."}
{effectiveCompareBaseBranch}
</span>
) : (
<span className="truncate text-muted-foreground/80">
Select base branch…
</span>
)}
<HiChevronUpDown className="size-3 shrink-0" />
</FormPickerTrigger>
</PopoverTrigger>
<PopoverContent
className="w-96 p-0"
className="w-[440px] p-0"
align="start"
onWheel={(event) => event.stopPropagation()}
>
<Command shouldFilter={false}>
<Command
shouldFilter={false}
value={selectedValue}
onValueChange={setSelectedValue}
onKeyDown={(e) => {
// cmdk leaves focus on the input, so the per-row button is
// pointer-only — Mod+Enter is the keyboard path to it.
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) {
if (!selectedValue) return;
e.preventDefault();
e.stopPropagation();
onOpenWorkspace(selectedValue);
setOpen(false);
}
}}
>
<CommandInput
placeholder="Search branches..."
value={branchSearch}
Expand All @@ -146,134 +159,96 @@ export function CompareBaseBranchPicker({
</TabsTrigger>
</TabsList>
</Tabs>
<TooltipProvider delayDuration={300}>
<CommandList className="max-h-[400px]">
{!isBranchesLoading && branches.length === 0 && (
<CommandEmpty>No branches found</CommandEmpty>
)}
{branches.map((branch) => {
const isRemoteOnly = branch.isRemote && !branch.isLocal;
const isWorktree = Boolean(branch.worktreePath);
return (
<CommandItem
key={branch.name}
value={branch.name}
onSelect={() => {
// Carry the row's locality through so the server doesn't
// re-resolve and risk picking a stale cached remote ref.
onSelectCompareBaseBranch(
branch.name,
branch.isLocal ? "local" : "remote-tracking",
);
setOpen(false);
}}
className="group h-11 flex items-center justify-between gap-3 px-3"
>
<span className="flex items-center gap-2.5 truncate flex-1 min-w-0">
<GoGitBranch className="size-3.5 shrink-0 text-muted-foreground" />
<span className="truncate font-mono text-xs">
{branch.name}
</span>
<span className="flex items-center gap-1.5 shrink-0">
{branch.name === defaultBranch && (
<span className="text-[10px] text-muted-foreground bg-muted px-1.5 py-0.5 rounded">
default
</span>
)}
{isRemoteOnly && (
<span className="text-[10px] text-muted-foreground/60 bg-muted/60 px-1.5 py-0.5 rounded">
remote
</span>
)}
{isWorktree && (
<span className="text-[10px] text-primary/80 bg-primary/10 px-1.5 py-0.5 rounded">
worktree
</span>
)}
</span>
<CommandList className="max-h-[420px]">
{!isBranchesLoading && branches.length === 0 && (
<CommandEmpty>No branches found</CommandEmpty>
)}
{branches.map((branch) => {
const isRemoteOnly = branch.isRemote && !branch.isLocal;
const isWorktree = Boolean(branch.worktreePath);
return (
<CommandItem
key={branch.name}
value={branch.name}
onSelect={() => {
// Carry the row's locality through so the server doesn't
// re-resolve and risk picking a stale cached remote ref.
onSelectCompareBaseBranch(
branch.name,
branch.isLocal ? "local" : "remote-tracking",
);
setOpen(false);
}}
className="group items-start gap-3 rounded-md px-2.5 py-2"
>
{isWorktree ? (
<LuFolderOpen className="mt-0.5 size-4 shrink-0 text-primary/80" />
) : isRemoteOnly ? (
<GoGlobe className="mt-0.5 size-4 shrink-0 text-muted-foreground/60" />
) : (
<GoGitBranch className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
)}
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
<span className="truncate text-sm leading-snug">
{branch.name}
</span>
<span className="flex items-center gap-2 shrink-0">
<span className="flex items-center gap-1.5 text-[11px] text-muted-foreground">
{branch.lastCommitDate > 0 && (
<span className="text-[11px] text-muted-foreground/70 group-hover:hidden">
<span>
{formatRelativeTime(branch.lastCommitDate * 1000)}
</span>
)}
{isWorktree ? (
(() => {
// Authoritative check against the cloud-synced
// collection — a `server hasWorkspace:true` row
// may be stale after a delete.
const hasWorkspace = hasWorkspaceForBranch(
branch.name,
);
return (
<button
type="button"
className="hidden group-hover:inline-flex group-focus-within:inline-flex items-center rounded-sm bg-primary/10 hover:bg-primary/20 px-2 py-0.5 text-[11px] text-primary font-medium"
onClick={(e) => {
e.stopPropagation();
if (hasWorkspace) {
onOpenExisting(branch.name);
} else {
onCheckoutBranch(branch.name);
}
}}
>
{hasWorkspace ? "Open" : "Create"}
</button>
);
})()
) : branch.isCheckedOut ? (
<Tooltip>
<TooltipTrigger asChild>
{/*
Use aria-disabled, NOT the native `disabled` attribute.
Native disabled buttons don't fire pointer events, so the
Tooltip never sees hover/focus and never opens — defeating
the purpose of explaining why the button is unavailable.
*/}
<button
type="button"
aria-disabled="true"
className="hidden group-hover:inline-flex group-focus-within:inline-flex items-center rounded-sm bg-muted px-2 py-0.5 text-[11px] text-muted-foreground/70 cursor-not-allowed"
onClick={(e) => e.stopPropagation()}
>
Check out
</button>
</TooltipTrigger>
<TooltipContent side="left">
Already checked out in another worktree
</TooltipContent>
</Tooltip>
) : (
<button
type="button"
className="hidden group-hover:inline-flex group-focus-within:inline-flex items-center rounded-sm bg-primary/10 hover:bg-primary/20 px-2 py-0.5 text-[11px] text-primary font-medium"
onClick={(e) => {
e.stopPropagation();
onCheckoutBranch(branch.name);
}}
>
Check out
</button>
{branch.name === defaultBranch && (
<>
<span aria-hidden>·</span>
<span>default</span>
</>
)}
{effectiveCompareBaseBranch === branch.name && (
<HiCheck className="size-4 text-primary" />
{isRemoteOnly && (
<>
<span aria-hidden>·</span>
<span>remote</span>
</>
)}
{isWorktree && (
<>
<span aria-hidden>·</span>
<span className="text-primary/80">worktree</span>
</>
)}
</span>
</CommandItem>
);
})}
{hasNextPage && (
<div
ref={sentinelRef}
className="py-2 text-center text-[11px] text-muted-foreground/60"
>
{isFetchingNextPage ? "Loading more..." : ""}
</div>
)}
</CommandList>
</TooltipProvider>
</div>
<span className="ml-2 flex shrink-0 items-center gap-1.5 self-center">
<button
type="button"
className="hidden items-center rounded-sm bg-primary/10 px-2 py-0.5 text-[11px] font-medium text-primary hover:bg-primary/20 group-hover:inline-flex group-data-[selected=true]:inline-flex"
onClick={(e) => {
e.stopPropagation();
onOpenWorkspace(branch.name);
setOpen(false);
}}
>
Open workspace
<span className="ml-1.5 text-[10px] opacity-70">
{MOD_KEY}↵
</span>
</button>
{effectiveCompareBaseBranch === branch.name && (
<HiCheck className="size-4 text-primary group-hover:hidden group-data-[selected=true]:hidden" />
)}
</span>
</CommandItem>
);
})}
{hasNextPage && (
<div
ref={sentinelRef}
className="py-2 text-center text-[11px] text-muted-foreground/60"
>
{isFetchingNextPage ? "Loading more..." : ""}
</div>
)}
</CommandList>
</Command>
</PopoverContent>
</Popover>
Expand Down
Loading
Loading