Skip to content

feat: Embedded Copilot CLI tool window#7

Merged
sailro merged 7 commits intosailro:mainfrom
bommerts:feature/embedded-terminal-window
Apr 12, 2026
Merged

feat: Embedded Copilot CLI tool window#7
sailro merged 7 commits intosailro:mainfrom
bommerts:feature/embedded-terminal-window

Conversation

@bommerts
Copy link
Copy Markdown
Contributor

Summary

Adds a dockable tool window (Tools → Copilot CLI Window) that hosts the Copilot CLI inside Visual Studio using WebView2 + xterm.js + Windows ConPTY.

Closes #6

What's new

  • ConPTY integration — the CLI runs in a real pseudo-console, so ANSI colors, interactive prompts, and TTY detection all work correctly
  • WebView2 + xterm.js — terminal rendering with full escape code support, resize handling, and theme-matched dark background
  • Session lifecycle — process survives tool window hide/show; restarts on solution switch; cleans up on dispose
  • Backward compatible — the existing Launch Copilot CLI (external terminal) is unchanged; the MCP bridge and /ide\ connection are unaffected

New files

File Purpose
\ConPty.cs\ P/Invoke wrapper for Windows ConPTY APIs
\TerminalProcess.cs\ Manages CLI process via ConPTY with async I/O
\TerminalSessionService.cs\ Package-level session lifecycle singleton
\TerminalToolWindow.cs\ VS ToolWindowPane shell
\TerminalToolWindowControl.cs\ WPF + WebView2 + xterm.js bridge
\Resources/Terminal/*\ HTML, JS, and bundled xterm.js assets

Modified files

  • \CopilotCliIdePackage.cs\ / .vsct\ — new menu command + tool window registration
  • \VsServices.cs\ — exposes TerminalSessionService singleton
  • \CopilotCliIde.csproj\ / \Directory.Packages.props\ — WebView2 NuGet + resource bundling

Testing

  • MSBuild build succeeds (0 errors, 0 warnings)
  • All 284 existing tests pass
  • Format check passes on all new/modified files
  • Manually tested in VS 2026 Enterprise — terminal renders correctly, /ide connects normally

Screenshot

Will add screenshot after review feedback


This is a draft — happy to adjust the approach based on your feedback!

bommerts and others added 6 commits April 11, 2026 09:50
Add a dockable tool window (Tools > Copilot CLI Window) that hosts the
Copilot CLI inside Visual Studio using WebView2 + xterm.js + ConPTY.

New files:
- ConPty.cs: P/Invoke wrapper for Windows pseudo-console APIs
- TerminalProcess.cs: manages CLI process via ConPTY with async I/O
- TerminalSessionService.cs: package-level session lifecycle manager
- TerminalToolWindow.cs: VS ToolWindowPane shell
- TerminalToolWindowControl.cs: WPF control with WebView2 + xterm.js
- Resources/Terminal/: HTML, JS, and bundled xterm.js assets

Modified files:
- CopilotCliIdePackage.cs: wire up tool window, session service, command
- CopilotCliIdePackage.vsct: add Copilot CLI Window menu command
- VsServices.cs: expose TerminalSessionService singleton
- CopilotCliIde.csproj: add WebView2 NuGet, WPF refs, terminal resources
- Directory.Packages.props: add WebView2 version

The existing 'Launch Copilot CLI' external terminal remains available.
The /ide bridge connection is completely unaffected.

Refs: sailro#6

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Defer ConPTY process start until xterm.js reports actual dimensions
  via the first resize message (fixes text overlap/misalignment)
- Pass cols/rows from xterm.js through to ConPty.Create instead of
  hardcoded 120x40
- Use TryGetWebMessageAsString instead of WebMessageAsJson to correctly
  handle JS postMessage(JSON.stringify(...)) string messages
- Add detailed diagnostic logging to WebView2 initialization

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Accumulate pipe reads in a StringBuilder and flush every 16ms (~60fps)
instead of firing a separate SwitchToMainThreadAsync + PostWebMessageAsJson
for each read. This eliminates keystroke lag caused by echo latency.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Override PreProcessMessage in TerminalToolWindow to prevent VS from
intercepting arrow keys, Tab, Escape, Enter, Backspace, Delete, Home,
End, PgUp, and PgDn — letting them reach WebView2 and xterm.js so
interactive prompts (e.g., multi-choice selection) work correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove GotFocus handler that caused infinite focus loop
  (_webView.Focus() triggered bubbling GotFocus, which called
  _webView.Focus() again — locking the UI thread)
- Keep IsVisibleChanged handler for auto-focus when window appears
- Make WebView2 init lazy (placeholder TextBlock in constructor,
  WebView2 created in DeferredInitialize at ApplicationIdle priority)
- Add Transient=true to ProvideToolWindow to avoid forced auto-restore
- Add dispose/initializing guards to prevent races during startup
- Replace JoinableTaskFactory.RunAsync with Dispatcher.BeginInvoke for
  terminal output dispatch (lighter UI thread marshaling)
- Move JSON serialization off UI thread for output messages
- Remove temporary diagnostic file logging (DiagLog)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Replace GotFocus with PreviewMouseDown for focus handling.
  GotFocus + WebView2 causes infinite loops because:
  (a) IsKeyboardFocusWithin is always False for hosted Chromium HWNDs
  (b) term.focus() triggers focus-in escape sequences back to C#
- PreviewMouseDown fires only on real user clicks — no loop possible.
  Calls both _webView.Focus() (WPF) and term.focus() (Chromium) to
  resync focus state after VS layout transitions.
- Re-attach to session service in OnLoaded when WebView2 is already
  initialized (handles Unloaded/Loaded cycle during debug layout switch).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bommerts bommerts marked this pull request as ready for review April 11, 2026 16:45
@sailro
Copy link
Copy Markdown
Owner

sailro commented Apr 12, 2026

Hey thank you @bommerts for this nice contribution !. I added two comments after quick testing. But yes this is definitively useful. Thanks !

…orkspaceFolder

- Make GetWorkspaceFolder() internal static on CopilotCliIdePackage
  as single source of truth (removes duplicate from TerminalToolWindowControl)
- RestartSession() accepts optional workingDirectory parameter
- OnSolutionOpened passes fresh workspace folder to RestartSession
- StopSession clears _workingDirectory to prevent stale path reuse

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bommerts
Copy link
Copy Markdown
Contributor Author

Hey thank you @bommerts for this nice contribution !. I added two comments after quick testing. But yes this is definitively useful. Thanks !

Appreciate it! I absolutely love using GitHub Copilot CLI + the VS ide + what you have done with this extension. I hope this helps.

@sailro sailro merged commit 403ffa3 into sailro:main Apr 12, 2026
1 check passed
sailro added a commit that referenced this pull request Apr 12, 2026
Add embedded terminal subsystem to README usage section, populate CHANGELOG
[Unreleased] with full feature breakdown, and add comprehensive terminal
architecture section to copilot-instructions.md covering ConPTY, WebView2,
xterm.js lifecycle, and threading patterns.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.

Feature Request: Embedded Copilot CLI Tool Window

2 participants