Skip to content

Multi-server support for the desktop app - #605

Merged
SawyerHood merged 29 commits into
mainfrom
bb/configurable-host-url-selection-thr_era2syy2kz
Jul 10, 2026
Merged

SawyerHood merged 29 commits into
mainfrom
bb/configurable-host-url-selection-thr_era2syy2kz

Conversation

@SawyerHood

Copy link
Copy Markdown
Collaborator

Summary

Lets the bb desktop app manage and switch between multiple bb servers.

  • Server registry owned by the Electron shell (userData/servers.json): the built-in local server, manually added URLs, and BB Connect servers, with per-window active server and probe-gated add (typed duplicate/incompatible/unreachable failures).
  • Server rail: a React tile column inside the sidebar (shown with 2+ servers) that switches the window between servers; collapses with the sidebar. Window ▸ Server native menu (⌃⌘1–9) works on any loaded page, including a dead server's error page.
  • Settings → Servers (desktop-only, feature-detected): grouped server list with live status, add-by-URL with probe feedback, rename/remove for manual entries, auto-connect and show-connect-servers toggles.
  • Auto-connect is now a setting: when off, the shell no longer silently attaches to an already-running local server and shows an info view instead.
  • BB Connect pre-seeding: new GET /api/connect/servers on the connect worker (machine/server-credential or owner-session auth, heartbeat-based liveness, gate-intercepted before tunnel routing), listAccountServers plugin RPC + bb connect servers CLI, and a 10-minute shell sync that upserts connect:<handle> entries with self-handle dedup.
  • Per-server tile customization: icon (curated 108-icon Hugeicons registry, searchable picker) + color (favicon palette), stored as a local overlay that survives connect sync; works for builtin/manual/connect entries alike.
  • bbDesktop.servers bridge contract in @bb/desktop-contract with zod-validated, sender-gated IPC; version-skew-safe in both directions (feature-detected surface, renderer-validated style strings).

A native (WebContentsView) rail was built, evaluated, and deliberately replaced by the SPA rail; the spike findings live in the history (W2/W5/W7 merges).

Testing

  • pnpm exec turbo run typecheck (repo-wide, 42 tasks) and typecheck test on @bb/desktop, @bb/desktop-contract, @bb/app, @bb/connect, bb-plugin-connect — 1,500+ tests green.
  • Live QA against the real Electron dev shell via CDP: add/switch/remove servers, add-failure reasons, rail render + active states + sidebar collapse, auto-connect-off boot gate, settings pickers, icon/color persistence across restarts.
  • Known gaps: connect pre-seeding needs the apps/connect worker deployed; bb connect servers e2e against prod getbb.app untested until then.

🤖 Generated with Claude Code

SawyerHood and others added 29 commits July 10, 2026 09:57
Expose GET /api/connect/servers on the connect gate so the desktop app and
paired plugins can list every bb on the authenticated account, with a plugin
RPC/CLI passthrough (bb connect servers) and live status from last_seen_at.
Introduce optional bbDesktop.servers types and schemas so the shell and SPA can negotiate multi-server list/add/remove/rename/setActive/auto-connect with version-skew safety.
Persist servers.json with a non-removable builtin entry, probe-gate manual adds, and track per-window active server inheritance without requiring Electron.
Expose servers over IPC/preload, switch per-window origins from the Window ▸ Server menu, keep the local runtime pinned for remote loads, and gate silent local attach behind autoConnectToLocalServer.
…sthrough)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ency.

Return a typed duplicate failure for registry collisions, reject malformed add payloads, restrict servers IPC to application windows, serve list() from cache with background status refresh, and disallow renaming the builtin entry.
…tching, auto-connect gate

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Register a desktop-only Servers nav bucket gated on window.bbDesktop.servers,
with list/add/remove/rename, status badges, auto-connect toggle, and live
onChange updates over the existing bridge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sync paired bbs from the local connect plugin RPC into registry entries
(source "connect"), skip the local selfHandle twin, persist for offline
boot, and gate list/menu visibility with showConnectServers. listAccountServers
now includes public URLs for each handle.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
When the registry has ≥2 servers, application windows host a 52px left rail
and the SPA as sibling WebContentsViews so switching works on any loaded
page. Classic single-webContents layout stays the default and safety fallback
for single-server windows; crossing the threshold recreates open windows.

Absorbs the W2 spike findings without shipping the throwaway probes: SPA-
targeted IPC/devtools/context menus, browser-tab bounds offset by rail width,
traffic lights at {x:70,y:18} via setWindowButtonPosition, and re-apply on
leave-full-screen. Theme bridge accepts legacy light|dark or resolved
{mode,canvas,ink} for live rail recoloring.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

# Conflicts:
#	apps/desktop/src/main.ts
…Settings > Servers

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
An unbound Electron native method throws 'Object has been destroyed' when
invoked off the instance, which blanked every window when the registry
crossed the two-server threshold. Also forward BB_DESKTOP_ELECTRON_ARGS in
the dev launcher for CDP-driven QA.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rail widens 52 → 68px so the ~52px macOS light cluster fits inside its top
drag strip (x=10), with a dividing line at the SPA's 48px chrome-row height.
The window-state push gains optional windowButtonsInRail so the SPA drops its
traffic-light reserve while the rail owns the corner; older shells omit the
field and keep the classic reserve.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Rail background now uses the sidebar's exact canvas/ink mix so rail + sidebar
read as one panel; tiles go quiet (transparent until hover, soft accent fill
when active) and status dots only appear for offline/incompatible servers, in
muted ink instead of hardcoded chromatic literals.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The theme push now carries the computed --sidebar value; the rail paints it
verbatim instead of re-deriving the mix shell-side, which drifted from the
actual sidebar surface. Falls back to the default formula for older SPAs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The server rail will be rendered by the React SPA instead. Delete the
Electron rail WebContentsView machinery, layout-mode window recreation,
and rail-only theme/window-state contract fields so the shell is classic
single-webContents again.
Design pivot from the native WebContentsView rail: the rail is now a React
column inside the sidebar panel below the chrome row — it inherits theme
tokens, collapses with the sidebar, and needs no traffic-light geometry.
Shown only when the desktop registry has 2+ servers; the Window > Server
menu remains the works-on-any-page fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rail column was starving sidebar content (group labels truncated).
AppLayout now offsets --sidebar-width by SERVER_RAIL_WIDTH_PX while the rail
is visible, including during resize drags; browser-tab views resync bounds
when the rail toggles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Electron's window.confirm swallowed the click; bb has no blocking-confirm
pattern anywhere, the button is hover-revealed, and re-adding is one URL
paste.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Persist a local tileStyles overlay keyed by server id so styles survive
connect sync and apply to builtin, manual, and connect rows. Surface
icon/color on list entries, plumb setTileStyle through IPC, and let the
rail and settings picker render Hugeicons + favicon palette colors with
renderer-side fallbacks for version skew.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The picker previously offered the app's UI icon vocabulary (arrows, chrome
glyphs) which is meaningless for telling machines apart. New registry: 108
identity-flavored Hugeicons (devices, places, nature, creatures, transport,
objects) with human-searchable names, tree-shaken via named imports and
separate from shared-ui's ICON_MAP. 'Zap' kept for saved styles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…url-selection-thr_era2syy2kz

# Conflicts:
#	apps/app/src/components/settings/settings-nav.tsx
@SawyerHood
SawyerHood merged commit db79339 into main Jul 10, 2026
10 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