Protocol for Message Passing Between Webview and VS Code Extension Host in Secure Design
The Secure Design extension uses a simple JSON-based command protocol where the webview calls vscode.postMessage() to send commands to the extension host, which processes requests via onDidReceiveMessage and responds through webview.postMessage().
The hbmartin/secure-design repository implements a bidirectional communication layer between its React-based webview UI (rendering the Chat sidebar and Canvas) and the TypeScript extension host backend. This protocol enables the UI to request file operations, trigger LLM prompts, and synchronize state while keeping the webview sandboxed according to VS Code security architecture.
How the Message Protocol Works
Webview to Extension Host (Outbound)
The webview obtains a reference to the VS Code API by calling const vscode = acquireVsCodeApi(); at initialization. When the UI requires backend action—such as loading design files or submitting a prompt—it constructs a plain JavaScript object containing a command property and posts it via vscode.postMessage(message).
In src/webview/components/CanvasView.tsx, eight distinct postMessage calls dispatch commands including:
loadDesignFiles– Requests the host to read workspace design files and stream them backselectDesign– Notifies the host that the user selected a specific design element via{ command: 'selectDesign', designId: string }clearContext– Signals the host to reset stored LLM contextprompt– Transmits user-typed text to the LLM service via{ command: 'prompt', text: string }setTheme– Persists theme configuration with{ command: 'setTheme', theme: ThemeData }
Extension Host to Webview (Inbound)
The extension host creates a WebviewPanel in src/SuperdesignCanvasPanel.ts and registers an inbound message handler:
this._panel.webview.onDidReceiveMessage(async message => {
switch (message.command) {
case 'loadDesignFiles':
const designs = await this._designService.getAllDesigns();
this._panel.webview.postMessage({
type: 'designData',
designs,
});
break;
// … additional command handlers …
}
});
After processing, the host responds using this._panel.webview.postMessage(response), where the response object contains a type field identifying the payload category.
Complete Message Flow
- User interaction triggers a React component to build a command object and call
vscode.postMessage. - Extension host receives it via
onDidReceiveMessage, switches onmessage.command, and executes the requested work (file I/O, LLM calls). - Host responds with
webview.postMessagecontaining a typed payload. - Webview receives the response via
window.addEventListener('message', …)insrc/webview/index.tsxand updates React state accordingly.
Key Commands and Message Types
| Direction | Identifier | Payload Structure | Purpose |
|---|---|---|---|
| Webview → Host | loadDesignFiles |
{ command: 'loadDesignFiles' } |
Request workspace design file enumeration |
| Webview → Host | selectDesign |
{ command: 'selectDesign', designId: string } |
Notify selection of specific design element |
| Webview → Host | clearContext |
{ command: 'clearContext' } |
Reset LLM conversation context |
| Webview → Host | prompt |
{ command: 'prompt', text: string } |
Submit user prompt to LLM service |
| Webview → Host | setTheme |
{ command: 'setTheme', theme: ThemeData } |
Persist theme configuration |
| Host → Webview | designData |
{ type: 'designData', designs: DesignFile[] } |
Deliver design file list to render |
| Host → Webview | status |
{ type: 'status', message: string } |
Display temporary info banner |
| Host → Webview | error |
{ type: 'error', error: string } |
Propagate exception for user feedback |
| Host → Webview | contextUpdate |
{ type: 'contextUpdate', context: string } |
Synchronize LLM context after tool execution |
Implementation Examples
Posting a Message from the Webview
In src/webview/components/CanvasView.tsx, the UI requests design files:
const vscode = acquireVsCodeApi();
const loadMessage = { command: 'loadDesignFiles' };
vscode.postMessage(loadMessage);
Handling Messages in the Extension Host
From src/SuperdesignCanvasPanel.ts, the host processes the request and replies:
this._panel.webview.onDidReceiveMessage(async message => {
switch (message.command) {
case 'loadDesignFiles':
const designs = await this._designService.getAllDesigns();
this._panel.webview.postMessage({
type: 'designData',
designs,
});
break;
// … other commands …
}
});
Receiving Responses in the Webview
In src/webview/index.tsx, the React application listens for host responses:
useEffect(() => {
const listener = (event: MessageEvent) => {
const msg = event.data;
if (msg.type === 'designData') {
setDesigns(msg.designs);
} else if (msg.type === 'error') {
showError(msg.error);
}
// … handle other types …
};
window.addEventListener('message', listener);
return () => window.removeEventListener('message', listener);
}, []);
Source Files and Architecture
| File | Role | GitHub Link |
|---|---|---|
src/webview/components/CanvasView.tsx |
React component dispatching commands via vscode.postMessage and rendering design data. |
https://github.com/hbmartin/secure-design/blob/main/src/webview/components/CanvasView.tsx |
src/SuperdesignCanvasPanel.ts |
Extension host class creating the WebviewPanel, registering onDidReceiveMessage, and posting responses. |
https://github.com/hbmartin/secure-design/blob/main/src/SuperdesignCanvasPanel.ts |
src/webview/index.tsx |
Webview entry point setting up the global message event listener to bridge VS Code API events into React state. |
https://github.com/hbmartin/secure-design/blob/main/src/webview/index.tsx |
src/webview/components/DesignPanel.tsx |
Secondary UI component also utilizing vscode.postMessage for design‑specific interactions. |
https://github.com/hbmartin/secure-design/blob/main/src/webview/components/DesignPanel.tsx |
Summary
- The Secure Design extension implements a JSON command protocol for webview-extension host communication.
- Outbound messages use
acquireVsCodeApi().postMessage()with acommandproperty to trigger host actions likeloadDesignFilesorprompt. - Inbound messages are captured by
onDidReceiveMessageinSuperdesignCanvasPanel.ts, processed, and returned viawebview.postMessage()with atypefield such asdesignDataorerror. - The webview listens for responses via
window.addEventListener('message', …)insrc/webview/index.tsxto update React application state.
Frequently Asked Questions
How does the webview acquire the VS Code API?
The webview calls const vscode = acquireVsCodeApi(); at initialization. This function is injected into the webview global scope by VS Code and provides the postMessage method used to transmit commands to the extension host.
What happens if a message command is not recognized by the extension host?
The extension host’s onDidReceiveMessage handler in SuperdesignCanvasPanel.ts uses a switch statement on message.command. If the command does not match any defined case, the message is silently ignored; no error response is automatically generated unless explicitly implemented in a default case.
Can the protocol handle binary data or file transfers?
The current protocol transmits plain JSON objects. Binary data or large file contents are not sent as raw binary; instead, the extension host reads files using Node.js APIs and transmits the serialized content (e.g., base64 strings or JSON‑serializable objects) within the payload or designs fields of the message structure.
How are errors propagated back to the webview UI?
When the extension host encounters an exception during command processing, it catches the error and responds with a message object containing type: 'error' and an error string property. The webview’s global message listener in src/webview/index.tsx detects this type and routes it to the error display logic, presenting feedback to the user without reloading the webview.
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 →