diff --git a/src/renderer/src/components/sidebar/GroupControls.tsx b/src/renderer/src/components/sidebar/GroupControls.tsx deleted file mode 100644 index 90e58a1e844..00000000000 --- a/src/renderer/src/components/sidebar/GroupControls.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import React from 'react' -import { useAppStore } from '@/store' -import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' - -const GroupControls = React.memo(function GroupControls() { - const groupBy = useAppStore((s) => s.groupBy) - const setGroupBy = useAppStore((s) => s.setGroupBy) - - return ( -
- { - if (v) { - setGroupBy(v as typeof groupBy) - } - }} - variant="outline" - size="sm" - className="h-6 flex-1 justify-start" - > - - All - - - PR Status - - - Repo - - -
- ) -}) - -export default GroupControls diff --git a/src/renderer/src/components/sidebar/SearchBar.tsx b/src/renderer/src/components/sidebar/SearchBar.tsx index 23d30e2b1b2..4ba4757da60 100644 --- a/src/renderer/src/components/sidebar/SearchBar.tsx +++ b/src/renderer/src/components/sidebar/SearchBar.tsx @@ -1,5 +1,5 @@ import React, { useCallback } from 'react' -import { Search, X, Activity, FolderTree, FolderPlus } from 'lucide-react' +import { Search, X, Activity, ListFilter, FolderPlus } from 'lucide-react' import { useAppStore } from '@/store' import { Input } from '@/components/ui/input' import { Button } from '@/components/ui/button' @@ -8,6 +8,7 @@ import { DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuItem, + DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from '@/components/ui/dropdown-menu' @@ -37,30 +38,39 @@ const SearchBar = React.memo(function SearchBar() { [filterRepoIds, setFilterRepoIds] ) - const repoTriggerLabel = - selectedRepos.length === 0 ? ( - - - All - - ) : selectedRepos.length === 1 ? ( - - ) : ( - - - {selectedRepos.length} repos - - ) - const handleClear = useCallback(() => setSearchQuery(''), [setSearchQuery]) const handleToggleActive = useCallback( () => setShowActiveOnly(!showActiveOnly), [showActiveOnly, setShowActiveOnly] ) + const canFilterRepos = repos.length > 1 + const hasRepoFilter = canFilterRepos && selectedRepos.length > 0 + const hasAnyFilter = showActiveOnly || hasRepoFilter + const activeFilterCount = (showActiveOnly ? 1 : 0) + (hasRepoFilter ? selectedRepos.length : 0) + + const filterSummary = (() => { + if (!hasAnyFilter) { + return null + } + if (showActiveOnly && !hasRepoFilter) { + return ( + + + Active + + ) + } + if (!showActiveOnly && hasRepoFilter && selectedRepos.length === 1) { + return ( + + ) + } + return {activeFilterCount} filters + })() return (
@@ -78,71 +88,87 @@ const SearchBar = React.memo(function SearchBar() { )} - - - - - - {showActiveOnly ? 'Show all' : 'Active only'} - - - {repos.length > 1 && ( - - - - - - { - setFilterRepoIds([]) - }} - > - All repos - - {repos.map((r) => ( - handleToggleRepo(r.id)} - onSelect={(event) => event.preventDefault()} + + + + + + + + + {hasAnyFilter ? 'Edit filters' : 'Filter worktrees'} + + + + Status + event.preventDefault()} + > + + Active only + + {canFilterRepos && ( + <> + + Repositories + {repos.map((r) => ( + handleToggleRepo(r.id)} + onSelect={(event) => event.preventDefault()} + > + + + ))} + + )} + {hasAnyFilter && ( + <> + + { + setShowActiveOnly(false) + setFilterRepoIds([]) + }} + > + + Clear filters + + + )} + {canFilterRepos && ( + <> + + { + addRepo() + }} + > + + Add repo + + + )} + +
diff --git a/src/renderer/src/components/sidebar/SidebarHeader.tsx b/src/renderer/src/components/sidebar/SidebarHeader.tsx index fb71576f3a5..e8f00eec774 100644 --- a/src/renderer/src/components/sidebar/SidebarHeader.tsx +++ b/src/renderer/src/components/sidebar/SidebarHeader.tsx @@ -3,6 +3,7 @@ import { Plus, SlidersHorizontal } from 'lucide-react' import { useAppStore } from '@/store' import { Button } from '@/components/ui/button' import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip' +import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group' import { isGitRepoKind } from '../../../../shared/repo-kind' import { DropdownMenu, @@ -16,6 +17,12 @@ import { } from '@/components/ui/dropdown-menu' import type { WorktreeCardProperty } from '../../../../shared/types' +const GROUP_BY_OPTIONS = [ + { id: 'none', label: 'All' }, + { id: 'pr-status', label: 'PR Status' }, + { id: 'repo', label: 'Repo' } +] as const + const PROPERTY_OPTIONS: { id: WorktreeCardProperty; label: string }[] = [ { id: 'status', label: 'Terminal status' }, { id: 'unread', label: 'Unread indicator' }, @@ -44,6 +51,8 @@ const SidebarHeader = React.memo(function SidebarHeader() { const toggleWorktreeCardProperty = useAppStore((s) => s.toggleWorktreeCardProperty) const sortBy = useAppStore((s) => s.sortBy) const setSortBy = useAppStore((s) => s.setSortBy) + const groupBy = useAppStore((s) => s.groupBy) + const setGroupBy = useAppStore((s) => s.setGroupBy) return (
@@ -69,7 +78,34 @@ const SidebarHeader = React.memo(function SidebarHeader() { View options - + + Group by +
+ { + if (v) { + setGroupBy(v as typeof groupBy) + } + }} + variant="outline" + size="sm" + className="h-6 w-full justify-start" + > + {GROUP_BY_OPTIONS.map((opt) => ( + + {opt.label} + + ))} + +
+ + Sort by - {/* Virtualized scrollable list */}