Skip to content

Instantly share code, notes, and snippets.

@AWolf81
Last active September 26, 2022 21:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save AWolf81/c0fef6435487c989cd84811dd7973643 to your computer and use it in GitHub Desktop.
Save AWolf81/c0fef6435487c989cd84811dd7973643 to your computer and use it in GitHub Desktop.
VS Code Webview example (Provider with postMessage)
//media\main.js
//@ts-check
// This script will be run within the webview itself
// It cannot access the main VS Code APIs directly.
(function () {
const handleExtensionMessages = (event) => {
const { message, type } = event.data;
switch (type) {
case 'greeting':
console.log("received", message);
break;
}
};
window.addEventListener("message", handleExtensionMessages);
})();
html {
box-sizing: border-box;
font-size: 13px;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body,
h1,
h2,
h3,
h4,
h5,
h6,
p,
ol,
ul {
margin: 0;
padding: 0;
font-weight: normal;
}
img {
max-width: 100%;
height: auto;
}
:root {
--container-paddding: 20px;
--input-padding-vertical: 6px;
--input-padding-horizontal: 4px;
--input-margin-vertical: 4px;
--input-margin-horizontal: 0;
}
body {
padding: 0 var(--container-paddding);
color: var(--vscode-foreground);
font-size: var(--vscode-font-size);
font-weight: var(--vscode-font-weight);
font-family: var(--vscode-font-family);
background-color: var(--vscode-editor-background);
}
ol,
ul {
padding-left: var(--container-paddding);
}
body > *,
form > * {
margin-block-start: var(--input-margin-vertical);
margin-block-end: var(--input-margin-vertical);
}
*:focus {
outline-color: var(--vscode-focusBorder) !important;
}
a {
color: var(--vscode-textLink-foreground);
}
a:hover,
a:active {
color: var(--vscode-textLink-activeForeground);
}
code {
font-size: var(--vscode-editor-font-size);
font-family: var(--vscode-editor-font-family);
}
button {
border: none;
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
width: 100%;
text-align: center;
outline: 1px solid transparent;
outline-offset: 2px !important;
color: var(--vscode-button-foreground);
background: var(--vscode-button-background);
}
button:hover {
cursor: pointer;
background: var(--vscode-button-hoverBackground);
}
button:focus {
outline-color: var(--vscode-focusBorder);
}
button.secondary {
color: var(--vscode-button-secondaryForeground);
background: var(--vscode-button-secondaryBackground);
}
button.secondary:hover {
background: var(--vscode-button-secondaryHoverBackground);
}
input:not([type='checkbox']),
textarea {
display: block;
width: 100%;
border: none;
font-family: var(--vscode-font-family);
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
color: var(--vscode-input-foreground);
outline-color: var(--vscode-input-border);
background-color: var(--vscode-input-background);
}
input::placeholder,
textarea::placeholder {
color: var(--vscode-input-placeholderForeground);
}
{
"name": "webview-provider-example",
"displayName": "webview-provider-example",
"description": "",
"version": "0.0.1",
"engines": {
"vscode": "^1.71.0"
},
"categories": [
"Other"
],
"activationEvents": [
"onView:myExtension.controlsView",
"onCommand:myExtension.sayHello"
],
"main": "./out/extension.js",
"contributes": {
"views": {
"explorer": [
{
"type": "webview",
"id": "myExtension.controlsView",
"name": "MyExtension"
}
]
},
"commands": [
{
"command": "myExtension.sayHello",
"category": "myExtension",
"title": "SayHello"
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile && npm run lint",
"lint": "eslint src --ext ts",
"test": "node ./out/test/runTest.js"
},
"devDependencies": {
"@types/vscode": "^1.71.0",
"@types/glob": "^7.2.0",
"@types/mocha": "^9.1.1",
"@types/node": "16.x",
"@typescript-eslint/eslint-plugin": "^5.31.0",
"@typescript-eslint/parser": "^5.31.0",
"eslint": "^8.20.0",
"glob": "^8.0.3",
"mocha": "^10.0.0",
"typescript": "^4.7.4",
"@vscode/test-electron": "^2.1.5"
}
}
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import { WebViewProvider } from './WebViewProvider';
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
const provider = new WebViewProvider(context.extensionUri);
context.subscriptions.push(
vscode.commands.registerCommand('myExtension.sayHello', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
// vscode.window.showInformationMessage(output);
provider.postMessageToWebview({
type: 'greeting',
message: 'HelloWorld',
});
}),
);
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(
WebViewProvider.viewType,
provider,
),
);
}
// this method is called when your extension is deactivated
export function deactivate() {}
import * as vscode from 'vscode';
function getNonce() {
let text = "";
const possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
export class WebViewProvider
implements vscode.WebviewViewProvider
{
public static readonly viewType = 'myExtension.controlsView';
private _view?: vscode.WebviewView;
constructor(private readonly _extensionUri: vscode.Uri) {
}
public postMessageToWebview(message: any) {
this._view?.webview.postMessage(message);
}
public resolveWebviewView(
webviewView: vscode.WebviewView,
context: vscode.WebviewViewResolveContext,
_token: vscode.CancellationToken,
) {
this._view = webviewView; // needed so we can use it in postMessageToWebview later
webviewView.webview.options = {
// Allow scripts in the webview
enableScripts: true,
localResourceRoots: [this._extensionUri],
};
webviewView.webview.html = this._getHtmlForWebview(
webviewView.webview,
);
webviewView.webview.onDidReceiveMessage((data) => {
switch (data.type) {
// other message types ...
case 'onYourEvent': {
console.log(data.value); // see below webview to extension communication snippet
break;
}
case 'onInfo': {
if (!data.value) {
return;
}
vscode.window.showInformationMessage(data.value);
break;
}
case 'onError': {
if (!data.value) {
return;
}
vscode.window.showErrorMessage(data.value);
break;
}
}
});
}
private _getHtmlForWebview(webview: vscode.Webview) {
// // And the uri we use to load this script in the webview
// const scriptUri = webview.asWebviewUri(
// vscode.Uri.joinPath(
// this._extensionUri,
// 'out',
// 'svelte-app/bundle.js',
// ),
// );
const scriptUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "media", "main.js")
);
// Local path to css styles
const styleResetPath = vscode.Uri.joinPath(
this._extensionUri,
'media',
'reset.css',
);
const stylesPathMainPath = vscode.Uri.joinPath(
this._extensionUri,
'media',
'vscode.css',
);
// Uri to load styles into webview
const stylesResetUri = webview.asWebviewUri(styleResetPath);
const stylesMainUri = webview.asWebviewUri(stylesPathMainPath);
// const cssUri = webview.asWebviewUri(
// vscode.Uri.joinPath(
// this._extensionUri,
// 'out/svelte-app',
// 'bundle.css',
// ),
// vscode.Uri.joinPath(this._extensionUri, "media", "main.css")
// );
// Use a nonce to only allow specific scripts to be run
const nonce = getNonce();
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--
Use a content security policy to only allow loading images from https or from our extension directory,
and only allow scripts that have a specific nonce.
-->
<meta http-equiv="Content-Security-Policy" content="img-src https: data:; style-src 'unsafe-inline' ${webview.cspSource}; script-src 'nonce-${nonce}';">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="${stylesResetUri}" rel="stylesheet">
<link href="${stylesMainUri}" rel="stylesheet">
<script nonce="${nonce}">
const tsvscode = acquireVsCodeApi();
</script>
</head>
<body>
<h1>My Extension</h1>
</body>
<script nonce="${nonce}" src="${scriptUri}"></script>
</html>`;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment