diff --git a/src/renderer/src/components/sidebar/SearchBar.tsx b/src/renderer/src/components/sidebar/SearchBar.tsx
index 172606bb987..b49ec748c32 100644
--- a/src/renderer/src/components/sidebar/SearchBar.tsx
+++ b/src/renderer/src/components/sidebar/SearchBar.tsx
@@ -1,76 +1,14 @@
import React, { useCallback } from 'react'
-import { Search, X, Activity, ListFilter, FolderPlus } from 'lucide-react'
+import { Search, X } from 'lucide-react'
import { useAppStore } from '@/store'
import { Input } from '@/components/ui/input'
import { Button } from '@/components/ui/button'
-import {
- DropdownMenu,
- DropdownMenuCheckboxItem,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuLabel,
- DropdownMenuSeparator,
- DropdownMenuTrigger
-} from '@/components/ui/dropdown-menu'
-import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
-import { cn } from '@/lib/utils'
-import RepoDotLabel from '@/components/repo/RepoDotLabel'
const SearchBar = React.memo(function SearchBar() {
const searchQuery = useAppStore((s) => s.searchQuery)
const setSearchQuery = useAppStore((s) => s.setSearchQuery)
- const showActiveOnly = useAppStore((s) => s.showActiveOnly)
- const setShowActiveOnly = useAppStore((s) => s.setShowActiveOnly)
- const filterRepoIds = useAppStore((s) => s.filterRepoIds)
- const setFilterRepoIds = useAppStore((s) => s.setFilterRepoIds)
- const repos = useAppStore((s) => s.repos)
- const addRepo = useAppStore((s) => s.addRepo)
- const selectedRepos = repos.filter((r) => filterRepoIds.includes(r.id))
-
- const handleToggleRepo = useCallback(
- (repoId: string) => {
- setFilterRepoIds(
- filterRepoIds.includes(repoId)
- ? filterRepoIds.filter((id) => id !== repoId)
- : [...filterRepoIds, repoId]
- )
- },
- [filterRepoIds, setFilterRepoIds]
- )
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 (
@@ -83,96 +21,18 @@ const SearchBar = React.memo(function SearchBar() {
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search..."
- className="h-7 pl-7 pr-20 text-[12px] border-none bg-muted/50 shadow-none focus-visible:ring-1 focus-visible:ring-ring/30 placeholder:text-muted-foreground/70"
+ className="h-7 pl-7 pr-8 text-[12px] border-none bg-muted/50 shadow-none focus-visible:ring-1 focus-visible:ring-ring/30 placeholder:text-muted-foreground/70"
/>
-
- {searchQuery && (
-
- )}
-
-
-
-
-
-
-
-
- {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 project
-
- >
- )}
-
-
-
+ {searchQuery && (
+
+ )}
)
diff --git a/src/renderer/src/components/sidebar/SidebarFilter.tsx b/src/renderer/src/components/sidebar/SidebarFilter.tsx
new file mode 100644
index 00000000000..6de1675a495
--- /dev/null
+++ b/src/renderer/src/components/sidebar/SidebarFilter.tsx
@@ -0,0 +1,140 @@
+import React, { useCallback } from 'react'
+import { Activity, ListFilter, FolderPlus, X } from 'lucide-react'
+import { useAppStore } from '@/store'
+import { Button } from '@/components/ui/button'
+import {
+ DropdownMenu,
+ DropdownMenuCheckboxItem,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger
+} from '@/components/ui/dropdown-menu'
+import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip'
+import RepoDotLabel from '@/components/repo/RepoDotLabel'
+
+const SidebarFilter = React.memo(function SidebarFilter() {
+ const showActiveOnly = useAppStore((s) => s.showActiveOnly)
+ const setShowActiveOnly = useAppStore((s) => s.setShowActiveOnly)
+ const filterRepoIds = useAppStore((s) => s.filterRepoIds)
+ const setFilterRepoIds = useAppStore((s) => s.setFilterRepoIds)
+ const repos = useAppStore((s) => s.repos)
+ const addRepo = useAppStore((s) => s.addRepo)
+
+ const handleToggleRepo = useCallback(
+ (repoId: string) => {
+ setFilterRepoIds(
+ filterRepoIds.includes(repoId)
+ ? filterRepoIds.filter((id) => id !== repoId)
+ : [...filterRepoIds, repoId]
+ )
+ },
+ [filterRepoIds, setFilterRepoIds]
+ )
+
+ const handleToggleActive = useCallback(
+ () => setShowActiveOnly(!showActiveOnly),
+ [showActiveOnly, setShowActiveOnly]
+ )
+ const canFilterRepos = repos.length > 1
+ // Why: derive from the current repos list so stale IDs in filterRepoIds
+ // (e.g. lingering after a repo is removed) don't inflate the active-filter
+ // count or falsely signal an applied filter.
+ const selectedRepos = canFilterRepos ? repos.filter((r) => filterRepoIds.includes(r.id)) : []
+ const hasRepoFilter = selectedRepos.length > 0
+ const hasAnyFilter = showActiveOnly || hasRepoFilter
+ const activeFilterCount = (showActiveOnly ? 1 : 0) + selectedRepos.length
+
+ return (
+
+
+
+
+
+
+
+
+ {hasAnyFilter ? 'Edit filters' : 'Filter workspaces'}
+
+
+
+ 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 project
+
+ >
+ )}
+
+
+ )
+})
+
+export default SidebarFilter
diff --git a/src/renderer/src/components/sidebar/SidebarHeader.tsx b/src/renderer/src/components/sidebar/SidebarHeader.tsx
index 3aceafaefe7..3afe12f8309 100644
--- a/src/renderer/src/components/sidebar/SidebarHeader.tsx
+++ b/src/renderer/src/components/sidebar/SidebarHeader.tsx
@@ -16,6 +16,7 @@ import {
DropdownMenuRadioItem
} from '@/components/ui/dropdown-menu'
import type { WorktreeCardProperty } from '../../../../shared/types'
+import SidebarFilter from './SidebarFilter'
const GROUP_BY_OPTIONS = [
{ id: 'none', label: 'All' },
@@ -72,6 +73,7 @@ const SidebarHeader = React.memo(function SidebarHeader() {
Workspaces
+