How the WebView2 Chat Window Integrates with Gateway WebSocket Events in openclaw-windows-node
The WebView2 chat window in openclaw-windows-node synchronizes with gateway events through a shared WebSocket client abstraction, ensuring real-time updates flow to both native and legacy HTML interfaces via the GatewayClientChatBridge.
The openclaw-windows-node repository implements a tray-based chat system that can render conversations either as a native functional surface or inside a legacy WebView2 control. Both modes rely on the same underlying OpenClawGatewayClient to maintain a persistent WebSocket connection, with the GatewayClientChatBridge acting as a decoupling layer that forwards gateway events to whichever UI surface is currently active.
Architecture Overview
The integration follows a bridge pattern that separates connection management from UI rendering. When the user selects "Use standard Gateway Chat interface" in settings, the application switches to WebView2 mode, but the actual WebSocket traffic still flows through the centralized client created by GatewayClientFactory in src/OpenClaw.Connection/GatewayClientFactory.cs.
The GatewayClientChatBridge (implementing IChatGatewayBridge from src/OpenClaw.Tray.WinUI/Chat/IChatGatewayBridge.cs) subscribes to five key gateway events: StatusChanged, SessionsUpdated, ChatMessageReceived, AgentEventReceived, and ModelsListUpdated. This abstraction allows the UI to remain agnostic of transport details and enables unit testing with mock bridges.
The Gateway WebSocket Client
The connection lifecycle begins with GatewayClientFactory, which instantiates OpenClawGatewayClient using a normalized WebSocket URL. The helper class GatewayUrlHelper converts standard HTTP addresses to WebSocket schemes in src/OpenClaw.Connection/GatewayUrlHelper.cs, ensuring the client connects via ws:// or wss:// protocols.
// The factory creates a singleton-style client that both UI modes share
var client = GatewayClientFactory.GetClient(normalizedUrl, token);
This client maintains the persistent connection to the gateway and raises events whenever server-side state changes occur.
The Bridge Abstraction Layer
GatewayClientChatBridge serves as the single source of truth for connection state, exposing properties like CurrentStatus and IsConnected. It forwards WebSocket events to the UI layer without exposing the raw client, as shown in src/OpenClaw.Tray.WinUI/Chat/GatewayClientChatBridge.cs:
_statusChangedHandler = (s, e) =>
{
_currentStatus = e;
StatusChanged?.Invoke(s, e);
if (e == ConnectionStatus.Connected)
{
// proactively request the models list for the UI
_ = _client.RequestModelsListAsync();
}
};
_client.StatusChanged += _statusChangedHandler;
_client.SessionsUpdated += _sessionsUpdatedHandler;
_client.ChatMessageReceived += _chatMessageReceivedHandler;
_client.ModelsListUpdated += _modelsListUpdatedHandler;
When the connection status changes to Connected, the bridge automatically requests the models list (RequestModelsListAsync), ensuring that UI dropdowns populate without requiring manual user interaction.
WebView2 Initialization and Lifecycle
The ChatWindow class in src/OpenClaw.Tray.WinUI/Windows/ChatWindow.xaml.cs hosts the WebView2 control. When operating in legacy mode, it constructs an authenticated chat URL via ChatSurfaceResolver.BuildChatUrl and initializes the WebView2 core through GatewayChatHelper.InitializeWebView2Async:
private async Task InitializeWebViewAsync()
{
// show loading UI …
await GatewayChatHelper.InitializeWebView2Async(WebView);
_webViewInitialized = true;
WebView.CoreWebView2.NavigationCompleted += (s, e) =>
{
LoadingRing.IsActive = false;
if (!e.IsSuccess)
{
// surface error handling based on e.WebErrorStatus …
}
else
{
WebView.Visibility = Visibility.Visible;
RequestChatInputFocus();
}
};
// navigate to the chat page once the token is known
if (!string.IsNullOrEmpty(_chatUrl))
WebView.CoreWebView2.Navigate(_chatUrl);
}
The GatewayChatHelper (located in src/OpenClaw.Tray.WinUI/Helpers/GatewayChatHelper.cs) handles environment setup, authentication header injection, and core WebView2 configuration.
Real-Time Event Synchronization
Events flow from the gateway through the bridge to both UI surfaces simultaneously. While the WebView2 surface only requires the initial HTTP URL to load the chat page, real-time updates (new messages, session changes, model list updates) arrive via the WebSocket connection that the bridge maintains:
Gateway (WebSocket) ──► OpenClawGatewayClient
│ │
│ StatusChanged, etc. │
▼ ▼
GatewayClientChatBridge ──► IChatGatewayBridge
│ │
▼ ▼
ChatWindow (functional UI) ChatWindow (WebView2)
│ │
└─► UI updates (model list, session list, chat messages)
The functional UI consumes the bridge directly via OpenClawChatDataProvider, while the WebView2 surface receives the same data through JavaScript interop or page reloads triggered by the bridge's event handlers.
Dynamic Credential Refresh
When gateway credentials change (e.g., after device pairing), the ChatWindow.RefreshCredentials method rebuilds the authenticated URL and updates the WebView2 navigation without destroying the control instance:
public void RefreshCredentials(string gatewayUrl, string token)
{
_gatewayUrl = gatewayUrl ?? string.Empty;
_token = token ?? string.Empty;
_chatUrl = ChatSurfaceResolver.BuildChatUrl(_gatewayUrl, _token);
Logger.Info($"[ChatWindow] Refreshing to {SafeLogUrl(_chatUrl)}");
if (_webViewMode && _webViewInitialized && WebView?.CoreWebView2 != null)
{
// navigate the already‑running WebView2 to the new, authenticated URL
WebView.CoreWebView2.Navigate(_chatUrl);
}
}
This hot-reload capability ensures the WebView2 chat window always possesses a valid authentication token without requiring an application restart, as implemented in src/OpenClaw.Tray.WinUI/Windows/ChatWindow.xaml.cs.
Summary
- Single WebSocket Client: Both native and WebView2 chat surfaces share one OpenClawGatewayClient instance created by GatewayClientFactory, preventing duplicate connections.
- Bridge Pattern: GatewayClientChatBridge implements IChatGatewayBridge to decouple the WebSocket transport from UI concerns, enabling testability and dual-interface support.
- Automatic Initialization: The WebView2 control initialization in ChatWindow.xaml.cs uses GatewayChatHelper.InitializeWebView2Async to inject authentication headers and handle navigation lifecycle events.
- Proactive State Sync: Upon connection, the bridge automatically requests the models list via
RequestModelsListAsync, ensuring UI dropdowns populate immediately without user action. - Hot Credential Refresh: The
RefreshCredentialsmethod allows runtime URL reconstruction and navigation updates when tokens change, maintaining session continuity.
Frequently Asked Questions
How does the WebView2 chat window receive real-time updates without its own WebSocket connection?
The WebView2 surface loads an initial HTML chat page via HTTP, but the actual real-time data flows through the shared OpenClawGatewayClient WebSocket connection. The GatewayClientChatBridge forwards events like ChatMessageReceived and SessionsUpdated to the ChatWindow, which can then push updates to the WebView2 content through JavaScript interop or trigger page refreshes when necessary.
What happens when the gateway token expires while the WebView2 chat is open?
When credentials refresh (either through automatic re-pairing or manual update), the ChatWindow.RefreshCredentials method constructs a new authenticated URL containing the updated token and calls WebView.CoreWebView2.Navigate(_chatUrl). This navigation occurs without reinitializing the WebView2 environment, ensuring minimal disruption to the user experience.
Why does the application use a bridge pattern instead of connecting the WebView2 directly to the WebSocket?
The GatewayClientChatBridge abstraction (defined in src/OpenClaw.Tray.WinUI/Chat/IChatGatewayBridge.cs) supports both the legacy WebView2 surface and the newer functional UI simultaneously. It encapsulates connection state management (IsConnected, CurrentStatus) and event aggregation, allowing the UI layer to switch between rendering modes without changing how it consumes gateway events. This design also enables unit testing by swapping the real bridge for a mock implementation.
Which component is responsible for converting HTTP URLs to WebSocket URLs?
The GatewayUrlHelper class in src/OpenClaw.Connection/GatewayUrlHelper.cs normalizes gateway addresses, converting http:// or https:// prefixes to ws:// or wss:// respectively. This ensures the OpenClawGatewayClient connects to the correct WebSocket endpoint regardless of how the user configured the gateway URL in settings.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →