Skip to content

Conversation

@ThomasK33
Copy link
Member

Summary

  • Add a Secondary Side Bar view container (muxSecondary) with a mux.chatView webview.
  • Provide a minimal workspace selector + chat UI (streaming text + basic history replay).
  • After mux: Open Workspace opens a new window, auto-reveal the chat view and auto-select the matching workspace.

Testing

  • make static-check
  • cd vscode && bun run compile

📋 Implementation Plan

Plan: VS Code Secondary Sidebar – mux Workspace Selector + Chat

Goal

Add a Secondary Side Bar view for the existing vscode/ extension so you can:

  1. pick a mux workspace, open it in VS Code, and
  2. chat with that mux workspace from inside the Secondary Side Bar.

Key UX requirement: after you run the existing "mux: Open Workspace" command and VS Code opens that workspace, the mux chat view should automatically show the matching chat session (no switching back to the mux desktop app, and ideally no extra clicks).

This leverages VS Code 1.106’s new viewsContainers.secondarySidebar contribution point (release notes: https://code.visualstudio.com/updates/v1_106#_extension-authoring).

Non-goals (initially)

  • Full parity with mux desktop chat UI (attachments, tool-call rendering, plan mode UX, etc.)
  • Offline chat (file-only mode can list workspaces, but chat requires a running mux server)
  • Multi-workspace/agent session management beyond selecting a single workspace at a time

Recommended approach (A): Single webview view with selector + chat

Net new product LoC: ~500–700

One view container in the Secondary Side Bar, containing one webview view:

  • top: connection status + “Refresh”
  • workspace dropdown/list + “Open”
  • chat transcript + input box

Why this is the best “simple” path:

  • Webview is the only practical way to embed a chat transcript + input UI.
  • Keeps extension API surface small (no tree-view provider + separate view coordination).
  • Uses existing mux discovery + API client already in vscode/src/.

Implementation steps

1) Manifest: contribute a container + webview view in the Secondary Side Bar

Update vscode/package.json:

  • Bump engines.vscode to ^1.106.0 (or the minimum version you confirm supports secondarySidebar).
  • Add:
    • contributes.viewsContainers.secondarySidebar: new container, e.g. muxSecondary
    • contributes.views["muxSecondary"]: one type: "webview" view, e.g. mux.chatView
  • Add activation so the extension can auto-reveal chat after opening a workspace:
    • activationEvents: include onStartupFinished (to auto-reveal) and onView:mux.chatView as a safe fallback

2) Extension activation: register a WebviewViewProvider

In vscode/src/extension.ts:

  • Register the provider via vscode.window.registerWebviewViewProvider("mux.chatView", provider, { webviewOptions: { retainContextWhenHidden: true } }).
  • Keep existing commands (mux.openWorkspace, mux.configureConnection) unchanged.
  • Add a small revealChatView() helper that runs workbench.view.extension.<containerId> so the extension can programmatically show the secondary-sidebar view when appropriate.
  • Optionally also expose that helper as a command (e.g. mux.focusChatView) for Command Palette use.

3) Create a small “mux service” layer for the view (reuse existing code)

Add a small module (or refactor existing helpers) so both commands and the sidebar can share logic:

  • getWorkspacesForUi(context)
    • Reuse discoverServerConfig + checkServerReachable + checkAuth
    • Respect mux.connectionMode (auto/server-only/file-only)
    • Return a unified list: WorkspaceWithContext[]
  • getApiClientForUi(context)
    • Cache the ApiClient (per extension host) so the sidebar doesn’t reconnect on every UI event.
  • Defensive programming:
    • assert that required config fields exist.
    • Explicitly handle “file-only” mode: workspace list works, chat controls disabled.

4) Webview UI: minimal HTML/CSS/JS (no framework)

Implement a simple DOM-based UI (inline script is fine to avoid changing build pipeline):

  • Workspace selector:
    • <select> populated with workspaces (label includes project + branch + runtime icon)
    • “Open” button calls extension → openWorkspace(workspaceId)
  • Chat transcript:
    • scrollable message list
    • bottom input (textarea) + “Send” button
    • show “streaming…” indicator when assistant is mid-response
  • Use VS Code theme vars (--vscode-*) and var(--vscode-font-family) for native feel.

5) Webview ↔ extension message protocol

Define explicit message types (keep them JSON-serializable):

Webview → extension

  • ready
  • refreshWorkspaces
  • selectWorkspace { workspaceId }
  • openWorkspace { workspaceId }
  • sendMessage { workspaceId, text }

Extension → webview

  • setSelectedWorkspace { workspaceId: string | null }
  • workspaces { workspaces: UiWorkspace[] }
  • connectionStatus { mode: "api"|"file", baseUrl?: string, error?: string }
  • chatEvent { workspaceId, event: UiChatEvent }
  • chatReset { workspaceId } (when switching workspaces)

Where UiWorkspace is a UI-friendly subset:

  • id, label, description, runtimeType, streaming, plus an openUri string if helpful.

6) Workspace selection: “current VS Code folder” auto-detection

To support the workflow “open workspace in VS Code → chat immediately”:

  • When mux.openWorkspace opens a new window, persist a short-lived "pending auto-select" record in context.globalState (e.g. { workspaceId, expectedWorkspaceUri, createdAt }).
    • Compute expectedWorkspaceUri using the same logic as openWorkspace() (local/worktree file URI vs SSH vscode-remote://...).
  • On activation in the newly opened window (onStartupFinished):
    • read vscode.workspace.workspaceFolders?.[0]?.uri
    • if it matches the pending record’s expectedWorkspaceUri, call revealChatView() and immediately setSelectedWorkspace to the pending workspaceId, then clear the pending record.
  • When the view loads, read vscode.workspace.workspaceFolders?.[0]?.uri.
  • Compute the expected URI for each mux workspace (same logic as openWorkspace) and auto-select the match.
    • For local/worktree: vscode.Uri.file(getWorkspacePath(workspace))
    • For ssh: vscode.Uri.parse("vscode-remote://ssh-remote+${host}${remotePath}")

7) Chat wiring via mux oRPC

Chat requires API mode.

Subscribe (history replay + live events)

  • Spike first: confirm the existing fetch-based oRPC client can consume eventIterator streams from a VS Code extension host (Node fetch streaming). If not, switch the extension client to @orpc/client/websocket against ${baseUrl}/orpc/ws and add a Node WebSocket implementation dependency.
  • Use client.workspace.onChat({ workspaceId }, { signal }).
  • Pump the async iterator in the extension host and forward events to the webview.
  • Maintain an AbortController per selected workspace; abort on:
    • workspace switch
    • view disposal

Send messages

  • client.workspace.sendMessage({ workspaceId, message: text })
  • Handle ResultSchema errors and surface them in the webview.

Keep chat “simple”

  • For initial UI rendering, translate mux events into a minimal set:
    • user/assistant message
    • stream-start, stream-delta, stream-end, stream-error
  • Ignore tool-call / reasoning events at first (or show as a collapsed “activity” line).

8) Failure modes + UX

  • If mux server is unreachable/unauthorized:
    • show a compact in-view banner with the error
    • provide buttons:
      • “Configure Connection” → runs existing mux.configureConnection command
      • “Retry”
  • If connectionMode is file-only:
    • allow workspace list + open
    • disable send input with explanation: “Chat requires mux server”

Validation checklist

  • On VS Code 1.106+, the container renders in the Secondary Side Bar.
  • Workspace list populates in:
    • API mode (server running)
    • file-only mode (server not running)
  • After opening via "mux: Open Workspace", the new window auto-reveals the mux chat view and shows that workspace’s chat session.
  • Selecting a workspace and clicking Open opens the correct folder (local/worktree + ssh).
  • With server running:
    • chat history replays on selection
    • sending a message results in assistant response events
    • switching workspaces cancels the old subscription cleanly

Alternative approach (B): Two views (tree for workspaces + webview for chat)

Net new product LoC: ~700–1000

Use a TreeDataProvider for workspaces (native list + context menu), and a separate webview view for chat.

Why I’m not recommending it first
  • More plumbing: you need to coordinate selection state between the tree view and the chat webview.
  • Still need a webview for the chat transcript + input, so you don’t actually avoid webviews.
  • The extra complexity doesn’t buy much for a “simple chat” MVP.

Future enhancements (after MVP)

  • Render tool calls + bash output events in chat (already available on workspace.onChat).
  • Persist selected workspace per VS Code folder (workspaceState).
  • Add “Resume stream / Interrupt stream” buttons using workspace.resumeStream / workspace.interruptStream.
  • Support images/attachments via sendMessage options (imageParts).

Generated with mux • Model: openai:gpt-5.2 • Thinking: xhigh

@ThomasK33 ThomasK33 force-pushed the vscode-secondary-sidebar branch from cce7fbf to 416120c Compare December 23, 2025 17:25
Change-Id: I8aa7a4f357c9a68b30beeb8d0f9244508c9aa83a
Signed-off-by: Thomas Kosiewski <[email protected]>
@ThomasK33 ThomasK33 force-pushed the vscode-secondary-sidebar branch from 416120c to aee6e67 Compare December 23, 2025 18:35
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