fix(desktop): hide dismissed projects from ⌘K too

Remove-from-sidebar only filtered the overview; the command palette still
listed every auto project. Share one filter so both surfaces stay in sync.
This commit is contained in:
Brooklyn Nicholson 2026-07-30 07:09:40 -05:00
parent 14db1a99e2
commit ce6ecf306e
4 changed files with 49 additions and 6 deletions

View file

@ -46,6 +46,7 @@ import {
$sidebarSessionOrderManual,
$sidebarWorkspaceOrderIds,
$sidebarWorkspaceParentOrderIds,
filterVisibleProjects,
pinSession,
SESSION_SEARCH_FOCUS_EVENT,
setPinnedSessionOrder,
@ -609,11 +610,8 @@ export function ChatSidebar({
return []
}
const dismissed = new Set(dismissedAutoProjects)
const sorted = sortProjectsForOverview(
projectTree
.filter(node => !(node.isAuto && dismissed.has(node.id)))
filterVisibleProjects(projectTree, dismissedAutoProjects)
.map(project =>
excludeProjectSessions(
{

View file

@ -68,6 +68,7 @@ import {
setCommandPaletteOpen
} from '@/store/command-palette'
import { $bindings } from '@/store/keybinds'
import { $dismissedAutoProjectIds, filterVisibleProjects } from '@/store/layout'
import { openPetGenerate } from '@/store/pet-generate'
import { $projectTree, goToProject, openFolderAsProject, requestStartWorkSession } from '@/store/projects'
import { $connection } from '@/store/session'
@ -519,6 +520,7 @@ function CommandPaletteBody({ onExited }: { onExited: () => void }) {
const bindings = useStore($bindings)
const worktrees = useStore($repoWorktrees)
const projectTree = useStore($projectTree)
const dismissedAutoProjects = useStore($dismissedAutoProjectIds)
const navigate = useNavigate()
const { availableThemes, mode, resolvedMode, setMode, setTheme, themeName } = useTheme()
const [search, setSearch] = useState('')
@ -719,7 +721,7 @@ function CommandPaletteBody({ onExited }: { onExited: () => void }) {
label: cc.openFolder,
run: () => void openFolderAsProject()
},
...projectTree.map(project => ({
...filterVisibleProjects(projectTree, dismissedAutoProjects).map(project => ({
comboHint: 'mod+enter',
icon: codiconIcon(project.icon || (project.isNoProject ? 'home' : 'folder-library')),
id: `project-${project.id}`,
@ -940,7 +942,7 @@ function CommandPaletteBody({ onExited }: { onExited: () => void }) {
// live state through `detail()`, so the groups must rebuild after a select
// that kept the palette open — eslint only sees an unused dep.
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [contributedItems, go, projectTree, selectTick, settingsSectionLabel, t, updateVersionLabel])
}, [contributedItems, dismissedAutoProjects, go, projectTree, selectTick, settingsSectionLabel, t, updateVersionLabel])
// The long, granular lists (settings fields, API keys, MCP servers, archived
// chats) only surface once the user types — otherwise they'd bury the

View file

@ -0,0 +1,29 @@
import { describe, expect, it } from 'vitest'
import { filterVisibleProjects } from './layout'
const auto = (id: string) => ({ id, isAuto: true as const })
const explicit = (id: string) => ({ id, isAuto: false as const })
const home = { id: '__home__' }
describe('filterVisibleProjects', () => {
it('drops dismissed autos and keeps explicit + home + undismissed', () => {
const tree = [explicit('p_shop'), auto('/www/otl-theme'), auto('/www/keep'), home]
expect(filterVisibleProjects(tree, ['/www/otl-theme', '/www/other']).map(p => p.id)).toEqual([
'p_shop',
'/www/keep',
'__home__'
])
})
it('ignores dismiss ids that hit an explicit project', () => {
// List is auto-only by construction; still don't let a stale id hide a real row.
const tree = [explicit('p_real'), auto('/www/gone')]
expect(filterVisibleProjects(tree, ['p_real', '/www/gone']).map(p => p.id)).toEqual(['p_real'])
})
it('passes the list through when nothing is dismissed', () => {
const tree = [auto('/www/a'), explicit('p_b')]
expect(filterVisibleProjects(tree, [])).toBe(tree)
})
})

View file

@ -221,6 +221,20 @@ export function dismissAutoProject(id: string): void {
}
}
// Auto projects dismissed from the overview stay out of every surface that
// lists projects (sidebar + ⌘K). Explicit rows never match.
export function filterVisibleProjects<T extends { id: string; isAuto?: boolean }>(
projects: readonly T[],
dismissedIds: readonly string[] = $dismissedAutoProjectIds.get()
): T[] {
if (!dismissedIds.length) {
return projects as T[]
}
const dismissed = new Set(dismissedIds)
return projects.filter(project => !(project.isAuto && dismissed.has(project.id)))
}
// Hide a worktree row after it's been removed via git.
export function dismissWorktree(id: string): void {
const current = $dismissedWorktreeIds.get()