Skip to content

Session sidebar: ⌘K search with wildcard/regex, and per-repo subtabs - #24

Merged
jbachorik merged 1 commit into
mainfrom
feat/session_impr
Aug 26, 2026
Merged

Session sidebar: ⌘K search with wildcard/regex, and per-repo subtabs#24
jbachorik merged 1 commit into
mainfrom
feat/session_impr

Conversation

@jbachorik

Copy link
Copy Markdown
Contributor

The sidebar listed every repository as one flat TreeView. With more than a handful of repos that became unreadable, and the global filter shortcut (⌘F) collided with the conventional in-terminal find binding. This change restructures the sidebar around per-repository subtabs and upgrades the search shortcut and matcher.

Session search: ⌘K + wildcard/regex

The global "focus the sidebar filter" shortcut moves from ⌘F to ⌘K (ShortcutsOverlay relabelled "Session search"), freeing ⌘F for a future terminal-find binding.

The filter's text matcher is extracted into SidebarQuery, which adds two shapes on top of the historical case-insensitive substring:

  • Glob*/?, other regex metacharacters escaped.
  • Regex/body/ or re:body, compiled CASE_INSENSITIVE|DOTALL with find().

An unparseable regex body falls back to a literal substring match of the body, never throwing — a typo in the filter must not blank the sidebar. RepositorySidebar compiles the matcher once at the top of rebuildTree; matchesRepo/matchesNode/matchesPullRequest and the PR-group narrower all consult it instead of a lowercased substring.

Per-repo subtabs instead of a flat tree

The flat all-repos TreeView is replaced by a wrapping FlowPane strip of per-repository ToggleButtons above the TreeView, which now shows only the selected repo's subtree. One repo is visible at a time; the subtab titles wrap inside a bounded width (long repo names take as many rows as they need) and the strip itself wraps across rows as the sidebar narrows.

A subtab is shown iff the repo name matches the text filter OR any of its session/worktree rows do — the same filter the old flat treeview used, now deciding subtab visibility instead of expand-and-keep. Within a shown repo, rows filter exactly as before (chips AND text, facets OR within an axis).

The repo pane keeps everything the expandable row carried (sessions, unopened worktrees, stale/locked buckets, the PULL REQUESTS group, the ⟳ rescan and the + new-session button); the repo row is now an always-expanded pane header (caret and click-to-collapse removed, a force-re-expand guard defeats keyboard ←). Per-repo collapse state (collapsed/collapsedBeforeFilter/filterChangedSinceLastRebuild) is gone — there is nothing to collapse.

Selecting a subtab replaces the old "repo-row expand" as the lazy PR-scan trigger, and refreshWorktrees's collapsed-skip became a not-selected-skip (isSelectedRepo): a repo whose worktree list changes while nobody is looking at it is marked stale and self-heals the moment its subtab is reselected. Switching the active session to another repo auto-selects that repo's subtab so its subtree is the one showing when the row is revealed.

Verification

  • ./gradlew :app:test --tests "app.drydock.ui.*" — all green, including the rewritten dedup FX test.
  • The dedup FX test's "collapsed self-heals on expand" case is rewritten to the subtab model ("deselected self-heals on reselect"), driving the worktree rescan through the workspace seam (refreshWorktreesFor) since the ⟳ button lives on the repo row a deselected repo does not show.
  • Not covered: manual visual verification of the subtab strip layout (wrapping, multi-row titles) against a real multi-repo workspace — the FX tests exercise the behaviour, not the CSS layout.

Out of scope

5 unrelated git worktree add timeouts in GitStatusServiceTest/WorkspaceMcpSessionContextTest reproduce on clean main (verified by stashing this branch); they are caused by the global core.hooksPath secrets-scanner hook under parallel load, not by these changes.

The sidebar listed every repository as one flat TreeView. With more than
a handful of repos that became unreadable, and the global filter shortcut
(⌘F) collided with the conventional in-terminal find binding.

Search. The global "focus the sidebar filter" shortcut moves from ⌘F to
⌘K (ShortcutsOverlay relabelled "Session search"), freeing ⌘F for a
future terminal-find binding. The filter's text matcher is extracted into
SidebarQuery, which adds two shapes on top of the historical
case-insensitive substring: glob (`*`/`?`, other regex metacharacters
escaped) and regex (`/body/` or `re:body`, compiled
CASE_INSENSITIVE|DOTALL with find()). An unparseable regex body falls back
to a literal substring match of the body, never throwing -- a typo in the
filter must not blank the sidebar. RepositorySidebar compiles the matcher
once at the top of rebuildTree; matchesRepo/matchesNode/matchesPullRequest
and the PR-group narrower all consult it instead of a lowercased substring.

Subtabs. The flat all-repos TreeView is replaced by a wrapping FlowPane
strip of per-repository ToggleButtons above the TreeView, which now shows
only the selected repo's subtree. One repo is visible at a time; the
subtab titles wrap inside a bounded width (long repo names take as many
rows as they need) and the strip itself wraps across rows as the sidebar
narrows. A subtab is shown iff the repo name matches the text filter OR
any of its session/worktree rows do -- the same filter the old flat
treeview used, now deciding subtab visibility instead of expand-and-keep.
Within a shown repo, rows filter exactly as before (chips AND text, facets
OR within an axis).

The repo pane keeps everything the expandable row carried (sessions,
unopened worktrees, stale/locked buckets, the PULL REQUESTS group, the ⟳
rescan and the + new-session button); the repo row is now an
always-expanded pane header (caret and click-to-collapse removed, a
force-re-expand guard defeats keyboard ←). Per-repo collapse state
(collapsed/collapsedBeforeFilter/filterChangedSinceLastRebuild) is gone --
there is nothing to collapse. Selecting a subtab replaces the old
"repo-row expand" as the lazy PR-scan trigger, and refreshWorktrees's
collapsed-skip became a not-selected-skip (isSelectedRepo), so a repo
whose worktree list changes while nobody is looking at it is marked stale
and self-heals the moment its subtab is reselected. Switching the active
session to another repo auto-selects that repo's subtab so its subtree is
the one showing when the row is revealed.

The dedup FX test's "collapsed self-heals on expand" case is rewritten to
the subtab model ("deselected self-heals on reselect"), driving the
worktree rescan through the workspace seam (refreshWorktreesFor) since the
⟳ button lives on the repo row a deselected repo does not show.

Co-Authored-By: Claude <noreply@anthropic.com>
@jbachorik
jbachorik marked this pull request as ready for review August 26, 2026 14:02
@jbachorik
jbachorik merged commit 7d1d963 into main Aug 26, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant