Section the "Run in" picker into Active / Settled#212
Conversation
The location picker lists every project with a live workspace — the same set the sidebar draws from. Since the inbox landed, settling a workspace parks its sidebar card without closing anything, so that set only ever grows: the picker and the sidebar drifted into two different models of the same data. The sidebar means "what I'm working on"; the picker meant "everything I've ever opened and not explicitly closed". At a dozen-plus adopted projects that read as a wall of equals — flat, unsorted, uncapped, running off the top of the viewport, with the project you touched a minute ago below one you settled months ago. The picker now speaks the sidebar's own vocabulary, reading the same store so the two can't disagree: - Active — at least one unsettled workspace, most-recently-active first via the inbox activity stamps. Unstamped projects rank last and keep their app-state order, so ordering is strictly better than insertion order rather than dependent on a signal we can't guarantee. - Settled - still open — every workspace settled. Deprioritized, never hidden (starting a thread here is how you resurrect parked work), newest-settled first, collapsed behind "Show N more". The targeted project always shows even from the hidden tail. - Headings and the search field appear only when they earn their space, so a small install keeps the flat list it always had. - The list is capped and scrollable, bounded by the available height so the taller popover can't clip off the top of a short window, and "Open another project..." moved outside the scroll area. Selecting a settled project deliberately does not un-settle it: the inbox already resurfaces a settled workspace the moment its agent goes working, which is exactly what first send does. Un-settling on mere selection would also fire while only browsing locations. Avatar loading is scoped to the rows on screen. It used to fetch three UI-state keys for every known project on every open; the collapsed list now costs a handful, and expanding or searching pays only for what it newly reveals.
Review — Opus 5 (high effort)The sectioning work is good — reading the sidebar's own store so the two surfaces can't disagree is the right call over hiding settled projects or switching to But there's a blocking bug. The search field never gets focus, so typing does nothing
I verified this rather than inferring it, with a throwaway harness ( So: open the picker, type, nothing happens. You have to click the search box first. The test suite can't catch this because it drives the input with Fix: drop And the docstring that led you here is wrong.
There is no such Design conflict with #205#205 adds a different search implementation to this same popover. It matters which lands:
cmdk's Smaller notes
|
|
Review by GPT 5.6 Sol xhigh found a cross-PR lifecycle mismatch with open PR #206.
Please resolve this while rebasing the two PRs: include The standalone focused suites passed 45/45 tests. Pairwise merge-tree checks also show textual conflicts with #205, #206, and #211, so this needs an integration rebase rather than a mechanical merge. |
The location picker's new search field never received focus: the popover's onOpenAutoFocus handler focused the cmdk root div, and since neither shadcn's CommandInput nor cmdk's Command.Input sets autoFocus — and cmdk's root keydown only handles navigation keys — printable keystrokes went nowhere until the user clicked the box. The same handler/CommandInput pairing shipped in the branch picker, the launch model picker, and the multi-provider model picker. Fix it centrally: the open-autofocus handler (now focusCmdkOnOpen) aims at [cmdk-input] when the popover has one and falls back to [cmdk-root] otherwise, so input-less pickers keep their arrow-key navigation. Dropping the handler on input-bearing popovers would not have been enough: in the multi-provider picker the provider rail's buttons precede the input in the DOM, so Radix's first-tabbable default would focus the rail. The handler's docstring claimed the input's own autoFocus made root-focus safe — no such autoFocus exists; the docstring now describes the real contract. Regression tests type with NO prior click on the input, the interaction that masked the bug (userEvent.type clicks first, focusing the input as a side effect). Also stop matching project paths in the location search. Project paths share a long common prefix (/home/…/projects/), so any short query that grazed it subsequence-matched every row at an identical score and the filter read as dead — the same failure that got name-and-path matching reverted in the workspace switcher (#205). The item value keeps the path after a tab separator purely to stay unique across same-basename projects (cmdk keys selection state on value); a custom filter strips it before scoring. partitionProjectScopes now documents that any future parked lifecycle beyond settled must be folded into the Active/Settled partition, or all-parked projects would read as Active here.
Problem
The Thread Scope location picker lists every project that has a live workspace — the same set the sidebar draws from. Since the inbox landed, settling a workspace parks its sidebar card without closing anything, so that set only ever grows.
The picker and the sidebar had drifted into two different models of the same data. The sidebar means "what I'm working on"; the picker meant "everything I've ever opened and not explicitly closed." Auto-settle actively widens that gap, since settling is now the cheap, encouraged gesture and each one leaves a permanent picker entry that appears nowhere in the sidebar.
At a dozen-plus adopted projects that reads as a wall of equals: flat, unsorted, uncapped, running off the top of the viewport, with the project you touched a minute ago sitting below one you settled months ago.
Approach
Rather than hiding settled projects (loses a real use case — starting a thread in a settled project is how you resurrect parked work) or switching to the
recent_projectstable (makes picker and sidebar disagree in a new way), the picker now speaks the sidebar's own vocabulary, reading the same store so the two can't disagree.activitystamps.--radix-popover-content-available-heightso the taller popover can't clip off the top of a short window.Open another project…moved outside the scroll area — the escape hatch must never scroll away.Notes for review
The picker never writes the inbox store. Selecting a settled project deliberately does not un-settle it — the inbox already resurfaces a settled workspace the moment its agent goes
working, which is exactly what first send does. Un-settling on mere selection would also fire while only browsing locations.Ordering degrades honestly.
WorkspaceSnapshotcarries no last-activity timestamp, and the inboxactivitymap is only stamped while the expanded sidebar is mounted. Rather than invent a parallel recency store, unstamped projects rank last and keep their app-state order — strictly better than insertion order, never dependent on a signal we can't guarantee. Hard recency would need backend stamping; that's a separate change.Avatar loading is now scoped to visible rows. It used to fetch 3 UI-state keys for every known project on every open (~57 IPC round trips at 18 projects), most for rows behind the collapsed tail. The effect deliberately does not cancel when the visible set changes mid-flight — that drops a batch and strands rows on default avatars; only a reopen invalidates a result.
Testing
project-scope-list.test.ts), 14 component tests covering sectioning, ordering, collapse, search, laziness, and the no-un-settle contract.tsc --noEmitandvite buildclean.npm run verifycan't complete in a fresh worktree:cargo checkneeds the gitignoredsrc-tauri/binaries/agent-browser-*sidecar. This change touches zero Rust, so the frontend gates were run individually.