Summary
The web UI currently supports only one active project at a time. Switching
between projects requires manually editing the URL (?project=) or opening
multiple terminals. The underlying infrastructure (ProjectStoreManager,
projectBridgeRegistry, per-project SSE lifecycle) already supports multiple
projects in parallel — what is missing is a visible, ergonomic navigation layer
on top of it.
This issue proposes three complementary additions:
- Project tabs — a persistent tab strip in the sidebar for open projects
- Cmd+K project switcher — keyboard-first overlay for switching and opening projects
- Favorite projects — pin frequently used projects for instant access
Problem
Starting gsd --web from a project directory locks the UI to that directory.
Working across multiple repos (e.g. a frontend and its API backend) requires:
- Manually constructing
?project=/path/to/other in the address bar
- Or running two separate
gsd --web instances on different ports
Neither is discoverable or convenient. Users who work across multiple repos
simultaneously have no ergonomic way to do so from a single web UI session.
The infrastructure is already capable — ProjectStoreManager caches stores per
project and re-attaches SSE on switch (reconnectSSE), and projectBridgeRegistry
runs independent BridgeService instances per project directory. The gap is
purely in the UI layer.
Proposal
1 — Project tab strip (sidebar)
Add a compact tab strip at the top of the sidebar listing all currently open
projects:
[ proj-a × ] [ proj-b × ] [ + ]
- Each tab shows the directory basename
- The active tab is visually highlighted
× removes the project from ProjectStoreManager (disconnects SSE)
+ opens the existing project picker / discovery view
- Clicking a tab calls
manager.switchProject(path) — already implemented
This is the lowest-effort path because ProjectStoreManager and
switchProject already exist and work correctly.
Relevant files:
web/lib/project-store-manager.tsx — switchProject, store lifecycle
web/components/gsd/projects-view.tsx — existing project picker UI
web/lib/gsd-workspace-store.tsx — SSE connect/disconnect lifecycle
2 — Cmd+K project switcher overlay
A keyboard-driven command palette that opens on Cmd+K / Ctrl+K:
┌──────────────────────────────────────────────┐
│ 🔍 Switch project… │
├──────────────────────────────────────────────┤
│ ★ proj-a ~/code/proj-a (active) ↗ │
│ ★ proj-b ~/code/proj-b ↗ │
│ ──────────────────────────────────────────── │
│ proj-c ~/code/proj-c ↗ │
│ proj-d ~/code/proj-d ↗ │
│ ──────────────────────────────────────────── │
│ Open folder… │
└──────────────────────────────────────────────┘
- Fuzzy-filters projects by name/path
- Favorited projects pinned at the top (★), then recently visited, then discovered
- Open in new browser tab (↗) icon on each project row — opens
?project=/path in a new tab for side-by-side work across browser tabs
- "Open folder…" triggers the OS folder picker (
<input type="file" webkitdirectory>)
or falls back to a text input for manual path entry
- Uses the existing
project-discovery-service results for suggestions
Relevant files:
src/web/project-discovery-service.ts — project scanning + metadata
web/lib/gsd-workspace-store.tsx — projectCwd, active project state
3 — Favorite projects
Allow users to pin projects as favorites so they always appear at the top of
the switcher and can optionally be pinned as persistent tabs:
- Star toggle (★) in the Projects view and Cmd+K switcher to favorite/unfavorite
- Open in new tab (↗) icon next to each project — renders as an
<a href="?project=...">
so Cmd+Click and right-click → "Open in new tab" also work natively
- Sorted by favorites first in Cmd+K: ★ Favorites → Recent → Discovered
- Pinned tabs (optional): favorited projects can remain as permanent tabs in
the tab strip, similar to pinned browser tabs — they don't show × and survive
session restarts
- Persisted via a
favorites: string[] array in the workspace store
(localStorage-backed), so favorites survive across sessions
This is low-effort (a single array + a toggle icon) but significantly improves
the experience for users who regularly work across the same 2–3 repos.
Open in new browser tab
Project links throughout the UI (Projects view, Cmd+K switcher, tab strip)
should be rendered as real <a href="?project=/path/to/repo"> links rather
than click handlers. This enables:
- ↗ icon — explicit "open in new tab" action on each project row
- Cmd+Click / Ctrl+Click — browser-native "open in new tab" on any project link
- Right-click → "Open in new tab" — works automatically with real links
- Multiple browser tabs — each tab runs its own project independently,
sharing the same gsd --web server instance
No server changes needed — the ?project= parameter is already supported and
each browser tab gets its own ProjectStoreManager instance.
Expected behaviour after implementation
- User starts
gsd --web from any directory (or without a project).
- The sidebar shows a tab for the initial project.
- Pressing
Cmd+K opens the switcher — favorited projects appear at the top,
user can type to fuzzy-filter by name/path.
- Clicking a project switches in the current tab; clicking ↗ or Cmd+clicking
opens it in a new browser tab.
- A new tab appears; the previous project tab stays open and cached.
- Switching between tabs is instant (no reload, SSE reconnects in the
background).
- Closing a tab disconnects SSE for that project and removes it from the tab
strip (unless it's a pinned favorite).
- Clicking the ★ icon on a project adds it to favorites — it stays pinned
across sessions.
Benefits
- No more URL engineering to switch projects
- Both projects remain warm in memory — instant context switch
- Multiple browser tabs for true side-by-side work across repos
- Agents can run in parallel in separate projects (server already supports this)
- Discoverable from the UI — no docs needed
- Favorite projects are always one keystroke away
- Zero changes to the server or bridge layer required
Scope / non-goals
- Not a split-view / side-by-side layout (separate, larger effort)
- Not a change to SSE architecture — only one project streams at a time;
the rest remain cached
- Not a replacement for the existing project picker — tabs, Cmd+K, and
favorites complement it
- Default behavior (single project on launch) is unchanged
Suggested implementation order
- Project tab strip (sidebar) — pure UI, low risk, immediate value
- Favorite projects —
favorites[] in store + star toggle in projects view
- Open in new tab — render project links as
<a> tags + ↗ icon
- Cmd+K switcher overlay — builds on (1)–(3), adds keyboard-first access
Summary
The web UI currently supports only one active project at a time. Switching
between projects requires manually editing the URL (
?project=) or openingmultiple terminals. The underlying infrastructure (
ProjectStoreManager,projectBridgeRegistry, per-project SSE lifecycle) already supports multipleprojects in parallel — what is missing is a visible, ergonomic navigation layer
on top of it.
This issue proposes three complementary additions:
Problem
Starting
gsd --webfrom a project directory locks the UI to that directory.Working across multiple repos (e.g. a frontend and its API backend) requires:
?project=/path/to/otherin the address bargsd --webinstances on different portsNeither is discoverable or convenient. Users who work across multiple repos
simultaneously have no ergonomic way to do so from a single web UI session.
The infrastructure is already capable —
ProjectStoreManagercaches stores perproject and re-attaches SSE on switch (
reconnectSSE), andprojectBridgeRegistryruns independent
BridgeServiceinstances per project directory. The gap ispurely in the UI layer.
Proposal
1 — Project tab strip (sidebar)
Add a compact tab strip at the top of the sidebar listing all currently open
projects:
×removes the project fromProjectStoreManager(disconnects SSE)+opens the existing project picker / discovery viewmanager.switchProject(path)— already implementedThis is the lowest-effort path because
ProjectStoreManagerandswitchProjectalready exist and work correctly.Relevant files:
web/lib/project-store-manager.tsx—switchProject, store lifecycleweb/components/gsd/projects-view.tsx— existing project picker UIweb/lib/gsd-workspace-store.tsx— SSE connect/disconnect lifecycle2 — Cmd+K project switcher overlay
A keyboard-driven command palette that opens on
Cmd+K/Ctrl+K:?project=/pathin a new tab for side-by-side work across browser tabs<input type="file" webkitdirectory>)or falls back to a text input for manual path entry
project-discovery-serviceresults for suggestionsRelevant files:
src/web/project-discovery-service.ts— project scanning + metadataweb/lib/gsd-workspace-store.tsx—projectCwd, active project state3 — Favorite projects
Allow users to pin projects as favorites so they always appear at the top of
the switcher and can optionally be pinned as persistent tabs:
<a href="?project=...">so Cmd+Click and right-click → "Open in new tab" also work natively
the tab strip, similar to pinned browser tabs — they don't show
×and survivesession restarts
favorites: string[]array in the workspace store(localStorage-backed), so favorites survive across sessions
This is low-effort (a single array + a toggle icon) but significantly improves
the experience for users who regularly work across the same 2–3 repos.
Open in new browser tab
Project links throughout the UI (Projects view, Cmd+K switcher, tab strip)
should be rendered as real
<a href="?project=/path/to/repo">links ratherthan click handlers. This enables:
sharing the same
gsd --webserver instanceNo server changes needed — the
?project=parameter is already supported andeach browser tab gets its own
ProjectStoreManagerinstance.Expected behaviour after implementation
gsd --webfrom any directory (or without a project).Cmd+Kopens the switcher — favorited projects appear at the top,user can type to fuzzy-filter by name/path.
opens it in a new browser tab.
background).
strip (unless it's a pinned favorite).
across sessions.
Benefits
Scope / non-goals
the rest remain cached
favorites complement it
Suggested implementation order
favorites[]in store + star toggle in projects view<a>tags + ↗ icon