Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ import { toast } from "@superset/ui/sonner";
import { useCallback, useMemo, useState } from "react";
import { electronTrpc } from "renderer/lib/electron-trpc";
import { useChangesStore } from "renderer/stores/changes";
import type {
ChangeCategory,
ChangedFile,
GitChangesStatus,
} from "shared/changes-types";
import type { ChangedFile, GitChangesStatus } from "shared/changes-types";
import { useScrollContext } from "../../context";
import { sortFiles } from "../../utils";
import { VirtualizedFileList } from "../VirtualizedFileList";
Expand All @@ -32,15 +28,9 @@ export function InfiniteScrollView({
hideUnchangedRegions,
toggleHideUnchangedRegions,
fileListViewMode,
expandedSections: expandedCategories,
toggleSection: toggleCategory,
} = useChangesStore();
const [expandedCategories, setExpandedCategories] = useState<
Record<ChangeCategory, boolean>
>({
"against-base": true,
committed: true,
staged: true,
unstaged: true,
});
const [collapsedFiles, setCollapsedFiles] = useState<Set<string>>(new Set());

const totals = useMemo(() => {
Expand Down Expand Up @@ -76,13 +66,6 @@ export function InfiniteScrollView({
};
}, [status]);

const toggleCategory = useCallback((category: ChangeCategory) => {
setExpandedCategories((prev) => ({
...prev,
[category]: !prev[category],
}));
}, []);

const toggleFile = useCallback((key: string) => {
setCollapsedFiles((prev) => {
const next = new Set(prev);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export function CategoryHeader({
<button
type="button"
onClick={onToggle}
className="flex items-center gap-2 px-4 py-2 w-full text-left hover:bg-muted transition-colors sticky top-0 z-20 border-b border-border"
className="flex items-center gap-2 px-4 py-2 w-full text-left hover:bg-muted transition-colors sticky top-0 z-20 border-b border-r border-border"
>
{isExpanded ? (
<LuChevronDown className="size-4 text-muted-foreground" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export function DiffToolbar({
onToggleHideUnchangedRegions,
}: DiffToolbarProps) {
return (
<div className="flex items-center gap-3 px-3 py-1.5 border-b border-border bg-background sticky top-0 z-30">
<div className="flex items-center gap-3 px-3 py-1.5 border-b border-r border-border bg-background sticky top-0 z-30">
<div className="flex items-center gap-3 text-xs text-muted-foreground flex-1">
<span>
{viewedCount}/{totalFiles} viewed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@superset/ui/dropdown-menu";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@superset/ui/select";
import { Tooltip, TooltipContent, TooltipTrigger } from "@superset/ui/tooltip";
import { useEffect, useRef, useState } from "react";
import { HiArrowPath } from "react-icons/hi2";
import { LuExpand, LuLoaderCircle, LuShrink, LuX } from "react-icons/lu";
import { HiArrowPath, HiCheck } from "react-icons/hi2";
import {
LuExpand,
LuGitBranch,
LuLoaderCircle,
LuShrink,
LuX,
} from "react-icons/lu";
import { VscGitStash, VscGitStashApply } from "react-icons/vsc";
import { HotkeyTooltipContent } from "renderer/components/HotkeyTooltipContent";
import { electronTrpc } from "renderer/lib/electron-trpc";
Expand All @@ -39,8 +39,6 @@ interface ChangesHeaderProps {
isStashPending: boolean;
}

const TOOLTIP_CLOSE_DELAY = 100;

function BaseBranchSelector({ worktreePath }: { worktreePath: string }) {
const { baseBranch, setBaseBranch } = useChangesStore();
const { data: branchData, isLoading } =
Expand All @@ -49,81 +47,63 @@ function BaseBranchSelector({ worktreePath }: { worktreePath: string }) {
{ enabled: !!worktreePath },
);

const [tooltipOpen, setTooltipOpen] = useState(false);
const closeTimeoutRef = useRef<NodeJS.Timeout | null>(null);

const handlePointerEnter = () => {
if (closeTimeoutRef.current) {
clearTimeout(closeTimeoutRef.current);
closeTimeoutRef.current = null;
}
setTooltipOpen(true);
};

const handlePointerLeave = () => {
closeTimeoutRef.current = setTimeout(() => {
setTooltipOpen(false);
}, TOOLTIP_CLOSE_DELAY);
};

useEffect(() => {
return () => {
if (closeTimeoutRef.current) {
clearTimeout(closeTimeoutRef.current);
}
};
}, []);

const effectiveBaseBranch = baseBranch ?? branchData?.defaultBranch ?? "main";
const sortedBranches = [...(branchData?.remote ?? [])].sort((a, b) => {
if (a === branchData?.defaultBranch) return -1;
if (b === branchData?.defaultBranch) return 1;
return a.localeCompare(b);
});

const handleChange = (value: string) => {
if (value === branchData?.defaultBranch && baseBranch === null) return;
setBaseBranch(value);
const handleBranchSelect = (branch: string) => {
if (branch === branchData?.defaultBranch && baseBranch === null) return;
setBaseBranch(branch);
};

if (isLoading || !branchData) {
return (
<span className="px-1.5 py-0.5 rounded bg-muted/50 text-foreground text-[10px] font-medium truncate">
{effectiveBaseBranch}
</span>
);
}

return (
<Tooltip open={tooltipOpen}>
<Select value={effectiveBaseBranch} onValueChange={handleChange}>
<DropdownMenu>
<Tooltip>
<TooltipTrigger asChild>
<SelectTrigger
size="sm"
className="h-5 px-1.5 py-0 text-[10px] font-medium border-none bg-muted/50 hover:bg-muted text-foreground min-w-0 w-auto gap-0.5 rounded"
onPointerEnter={handlePointerEnter}
onPointerLeave={handlePointerLeave}
>
<SelectValue />
</SelectTrigger>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-6 p-0"
disabled={isLoading}
>
<LuGitBranch className="size-3.5" />
</Button>
Comment on lines +57 to +74
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.

⚠️ Potential issue | 🟡 Minor

Add an accessible label to the icon-only base-branch button.

Icon-only controls need an aria-label for screen readers.

🔧 Suggested fix
 						<Button
 							variant="ghost"
 							size="icon"
 							className="size-6 p-0"
 							disabled={isLoading}
+							aria-label="Change base branch"
 						>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const handleBranchSelect = (branch: string) => {
if (branch === branchData?.defaultBranch && baseBranch === null) return;
setBaseBranch(branch);
};
if (isLoading || !branchData) {
return (
<span className="px-1.5 py-0.5 rounded bg-muted/50 text-foreground text-[10px] font-medium truncate">
{effectiveBaseBranch}
</span>
);
}
return (
<Tooltip open={tooltipOpen}>
<Select value={effectiveBaseBranch} onValueChange={handleChange}>
<DropdownMenu>
<Tooltip>
<TooltipTrigger asChild>
<SelectTrigger
size="sm"
className="h-5 px-1.5 py-0 text-[10px] font-medium border-none bg-muted/50 hover:bg-muted text-foreground min-w-0 w-auto gap-0.5 rounded"
onPointerEnter={handlePointerEnter}
onPointerLeave={handlePointerLeave}
>
<SelectValue />
</SelectTrigger>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-6 p-0"
disabled={isLoading}
>
<LuGitBranch className="size-3.5" />
</Button>
const handleBranchSelect = (branch: string) => {
if (branch === branchData?.defaultBranch && baseBranch === null) return;
setBaseBranch(branch);
};
return (
<DropdownMenu>
<Tooltip>
<TooltipTrigger asChild>
<DropdownMenuTrigger asChild>
<Button
variant="ghost"
size="icon"
className="size-6 p-0"
disabled={isLoading}
aria-label="Change base branch"
>
<LuGitBranch className="size-3.5" />
</Button>
🤖 Prompt for AI Agents
In
`@apps/desktop/src/renderer/screens/main/components/WorkspaceView/Sidebar/ChangesView/components/ChangesHeader/ChangesHeader.tsx`
around lines 57 - 74, The base-branch Button (the icon-only branch selector
inside ChangesHeader using DropdownMenuTrigger/DropdownMenu and the Button
component) lacks an accessible label; add an aria-label (e.g., "Select base
branch" or similar descriptive text) to that Button element so screen readers
can announce its purpose, and ensure the Tooltip content still matches the
aria-label for consistency; update the JSX for the Button used in ChangesHeader
(the Button with variant="ghost" size="icon" and <LuGitBranch />) to include the
aria-label prop.

</DropdownMenuTrigger>
</TooltipTrigger>
<SelectContent align="start">
{sortedBranches
.filter((branch) => branch)
.map((branch) => (
<SelectItem key={branch} value={branch} className="text-xs">
<TooltipContent side="bottom" showArrow={false}>
Change base branch
</TooltipContent>
</Tooltip>
<DropdownMenuContent align="start" className="w-56">
<DropdownMenuLabel className="text-xs text-muted-foreground font-normal">
Current base branch
</DropdownMenuLabel>
<DropdownMenuSeparator />
{sortedBranches
.filter((branch) => branch)
.map((branch) => (
<DropdownMenuItem
key={branch}
onClick={() => handleBranchSelect(branch)}
className="flex items-center justify-between text-xs"
>
<span className="truncate">
{branch}
{branch === branchData.defaultBranch && (
{branch === branchData?.defaultBranch && (
<span className="ml-1 text-muted-foreground">(default)</span>
)}
</SelectItem>
))}
</SelectContent>
</Select>
<TooltipContent side="bottom" showArrow={false}>
Change base branch
</TooltipContent>
</Tooltip>
</span>
{branch === effectiveBaseBranch && (
<HiCheck className="size-3.5 shrink-0 text-primary" />
)}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
);
}

Expand Down Expand Up @@ -263,69 +243,58 @@ export function ChangesHeader({
};

return (
<div className="flex flex-col">
<div className="flex items-center gap-1.5 px-2 py-1.5">
<span className="text-[10px] text-muted-foreground shrink-0">
Base:
</span>
<BaseBranchSelector worktreePath={worktreePath} />
<div className="flex-1" />
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={handleExpandToggle}
className="size-6 p-0"
>
{isExpanded ? (
<LuShrink className="size-3.5" />
) : (
<LuExpand className="size-3.5" />
)}
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" showArrow={false}>
<HotkeyTooltipContent
label={isExpanded ? "Collapse sidebar" : "Expand sidebar"}
hotkeyId="TOGGLE_EXPAND_SIDEBAR"
/>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={toggleSidebar}
className="size-6 p-0"
>
<LuX className="size-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" showArrow={false}>
<HotkeyTooltipContent
label="Close Changes Sidebar"
hotkeyId="TOGGLE_SIDEBAR"
/>
</TooltipContent>
</Tooltip>
</div>

<div className="flex items-center gap-0.5 px-2 pb-1.5">
<StashDropdown
onStash={onStash}
onStashIncludeUntracked={onStashIncludeUntracked}
onStashPop={onStashPop}
isPending={isStashPending}
/>
<ViewModeToggle
viewMode={viewMode}
onViewModeChange={onViewModeChange}
/>
<RefreshButton onRefresh={onRefresh} />
<PRStatusLink workspaceId={workspaceId} />
</div>
<div className="flex items-center gap-0.5 px-2 py-1.5">
<BaseBranchSelector worktreePath={worktreePath} />
<StashDropdown
onStash={onStash}
onStashIncludeUntracked={onStashIncludeUntracked}
onStashPop={onStashPop}
isPending={isStashPending}
/>
<ViewModeToggle viewMode={viewMode} onViewModeChange={onViewModeChange} />
<RefreshButton onRefresh={onRefresh} />
<PRStatusLink workspaceId={workspaceId} />
<div className="flex-1" />
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={handleExpandToggle}
className="size-6 p-0"
>
{isExpanded ? (
<LuShrink className="size-3.5" />
) : (
<LuExpand className="size-3.5" />
)}
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" showArrow={false}>
<HotkeyTooltipContent
label={isExpanded ? "Collapse sidebar" : "Expand sidebar"}
hotkeyId="TOGGLE_EXPAND_SIDEBAR"
/>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={toggleSidebar}
className="size-6 p-0"
>
<LuX className="size-3.5" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom" showArrow={false}>
<HotkeyTooltipContent
label="Close Changes Sidebar"
hotkeyId="TOGGLE_SIDEBAR"
/>
</TooltipContent>
</Tooltip>
</div>
);
}
Loading