feat(code): project pickers on quill autocomplete#2103
feat(code): project pickers on quill autocomplete#2103adamleithp wants to merge 1 commit intoux/file-picker-quillfrom
Conversation
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
apps/code/src/renderer/features/sidebar/components/ProjectSwitcher.tsx:286-291
Query not reset when a project is selected via click or Enter. `handleProjectSelect` calls `setDialogOpen(false)` directly on the parent state, bypassing `handleOpenChange`, so `setQuery("")` is never reached. Compare with `FilePicker.handleSelect` which explicitly calls `handleOpenChange(false)`. The test plan says "Reopening the dialog clears the previous query" — that only holds for Escape/overlay closes today, not for project selection.
```suggestion
const handleSelect = (id: string | null) => {
if (id === null) return;
const projectId = Number(id);
if (Number.isNaN(projectId)) return;
handleProjectSelect(projectId);
setQuery("");
};
```
### Issue 2 of 2
apps/code/src/renderer/features/onboarding/components/ProjectSelect.tsx:40-46
Same issue as `ProjectSwitcher`: when a project is selected, `setOpen(false)` changes the controlled `open` state directly, so Radix UI's Popover does not fire `onOpenChange` and `setQuery("")` is never reached. If the user re-opens the picker after a selection, the previous search string is still visible. Routing through `handleOpenChange` ensures the query is always cleared on any close path.
```suggestion
const handleSelect = (id: string | null) => {
if (id === null) return;
const next = Number(id);
if (Number.isNaN(next)) return;
onProjectChange(next);
handleOpenChange(false);
};
```
Reviews (1): Last reviewed commit: "feat(code): project pickers on quill aut..." | Re-trigger Greptile |
| const handleSelect = (id: string | null) => { | ||
| if (id === null) return; | ||
| const projectId = Number(id); | ||
| if (Number.isNaN(projectId)) return; | ||
| handleProjectSelect(projectId); | ||
| }; |
There was a problem hiding this comment.
Query not reset when a project is selected via click or Enter.
handleProjectSelect calls setDialogOpen(false) directly on the parent state, bypassing handleOpenChange, so setQuery("") is never reached. Compare with FilePicker.handleSelect which explicitly calls handleOpenChange(false). The test plan says "Reopening the dialog clears the previous query" — that only holds for Escape/overlay closes today, not for project selection.
| const handleSelect = (id: string | null) => { | |
| if (id === null) return; | |
| const projectId = Number(id); | |
| if (Number.isNaN(projectId)) return; | |
| handleProjectSelect(projectId); | |
| }; | |
| const handleSelect = (id: string | null) => { | |
| if (id === null) return; | |
| const projectId = Number(id); | |
| if (Number.isNaN(projectId)) return; | |
| handleProjectSelect(projectId); | |
| setQuery(""); | |
| }; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/renderer/features/sidebar/components/ProjectSwitcher.tsx
Line: 286-291
Comment:
Query not reset when a project is selected via click or Enter. `handleProjectSelect` calls `setDialogOpen(false)` directly on the parent state, bypassing `handleOpenChange`, so `setQuery("")` is never reached. Compare with `FilePicker.handleSelect` which explicitly calls `handleOpenChange(false)`. The test plan says "Reopening the dialog clears the previous query" — that only holds for Escape/overlay closes today, not for project selection.
```suggestion
const handleSelect = (id: string | null) => {
if (id === null) return;
const projectId = Number(id);
if (Number.isNaN(projectId)) return;
handleProjectSelect(projectId);
setQuery("");
};
```
How can I resolve this? If you propose a fix, please make it concise.| const handleSelect = (id: string | null) => { | ||
| if (id === null) return; | ||
| const next = Number(id); | ||
| if (Number.isNaN(next)) return; | ||
| onProjectChange(next); | ||
| setOpen(false); | ||
| }; |
There was a problem hiding this comment.
Same issue as
ProjectSwitcher: when a project is selected, setOpen(false) changes the controlled open state directly, so Radix UI's Popover does not fire onOpenChange and setQuery("") is never reached. If the user re-opens the picker after a selection, the previous search string is still visible. Routing through handleOpenChange ensures the query is always cleared on any close path.
| const handleSelect = (id: string | null) => { | |
| if (id === null) return; | |
| const next = Number(id); | |
| if (Number.isNaN(next)) return; | |
| onProjectChange(next); | |
| setOpen(false); | |
| }; | |
| const handleSelect = (id: string | null) => { | |
| if (id === null) return; | |
| const next = Number(id); | |
| if (Number.isNaN(next)) return; | |
| onProjectChange(next); | |
| handleOpenChange(false); | |
| }; |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/renderer/features/onboarding/components/ProjectSelect.tsx
Line: 40-46
Comment:
Same issue as `ProjectSwitcher`: when a project is selected, `setOpen(false)` changes the controlled `open` state directly, so Radix UI's Popover does not fire `onOpenChange` and `setQuery("")` is never reached. If the user re-opens the picker after a selection, the previous search string is still visible. Routing through `handleOpenChange` ensures the query is always cleared on any close path.
```suggestion
const handleSelect = (id: string | null) => {
if (id === null) return;
const next = Number(id);
if (Number.isNaN(next)) return;
onProjectChange(next);
handleOpenChange(false);
};
```
How can I resolve this? If you propose a fix, please make it concise.
Summary
ProjectSwitcher(sidebar "Change project" dialog) andProjectSelect(onboarding inline picker) fromcmdk(via theCommand.tsxwrapper) to quillAutocomplete*.ProjectSwitchernow uses quillDialog+DialogContent(matchesCommandMenu/FilePicker); orgs render as labeled groups when there are 2+ orgs, otherwise unlabeled.ProjectSelectkeeps the RadixPopoverhost (anchored to the inline "change" trigger), Autocomplete is rendered inline inside.inline + defaultOpen + autoHighlight="always"pattern, customfilterfor case-insensitive name matching,AutocompleteStatusempty content,CommandKeyHintsfooter on the dialog variant.Command.tsxwrapper +Command.css, plus the cmdk-targetedProjectSwitcher.css/ProjectSelect.css.Stacked on #2101 (
ux/file-picker-quill) which is stacked on #2100 (ux/cmd-menu). Merge order: #2100 → #2101 → this.What's left on cmdk
components/ui/combobox/Combobox.tsx+useComboboxFilter.ts(standalone reusable Combobox)features/folder-picker/components/GitHubRepoPicker.tsx(only importsdefaultFilterfrom cmdk)Both can move to a follow-up PR (likely against quill's
Combobox*namespace, separate fromAutocomplete*).Test plan
ProjectSwitcher(sidebar bottom-left → "Change project")No projects match "xyz"shows when filter is empty.ProjectSelect(onboarding "change" link next to project name)onProjectChangefires with the picked id.disabledprop dims the trigger and prevents clicks.Regressions / general
pnpm --filter code typecheckclean.