Skip to content

feat(web): multi-project tabs + Cmd+K switcher for the web UI #3483

@NilsR0711

Description

@NilsR0711

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:

  1. Project tabs — a persistent tab strip in the sidebar for open projects
  2. Cmd+K project switcher — keyboard-first overlay for switching and opening projects
  3. 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.tsxswitchProject, 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.tsxprojectCwd, 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

  1. User starts gsd --web from any directory (or without a project).
  2. The sidebar shows a tab for the initial project.
  3. Pressing Cmd+K opens the switcher — favorited projects appear at the top,
    user can type to fuzzy-filter by name/path.
  4. Clicking a project switches in the current tab; clicking ↗ or Cmd+clicking
    opens it in a new browser tab.
  5. A new tab appears; the previous project tab stays open and cached.
  6. Switching between tabs is instant (no reload, SSE reconnects in the
    background).
  7. Closing a tab disconnects SSE for that project and removes it from the tab
    strip (unless it's a pinned favorite).
  8. 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

  1. Project tab strip (sidebar) — pure UI, low risk, immediate value
  2. Favorite projects — favorites[] in store + star toggle in projects view
  3. Open in new tab — render project links as <a> tags + ↗ icon
  4. Cmd+K switcher overlay — builds on (1)–(3), adds keyboard-first access

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions