Skip to content

Webview

sachin soman edited this page Feb 15, 2021 · 1 revision

Webview

Webview allows us to add what ever content we want in Vscode. Its basically like the webpage we can design everything we want. One thing to note is that a Webview is resource intensive. Therefore use it only if necessary

To learn about webviews the Webview API and github repo is really good and has all the examples we need.

Creating a Panel

We will take the example from vsinder. We will stick this as a new file along with extension.ts and do some modifications which I'll put here.

import * as vscode from 'vscode';
import { getNonce } from "./getNounce";

export class HelloWorldPanel {
    /**
     * Track the currently panel. Only allow a single panel to exist at a time.
     */
    public static currentPanel: HelloWorldPanel | undefined;
    public static readonly viewType = "hello-world";
    private readonly _panel: vscode.WebviewPanel;
    private readonly _extensionUri: vscode.Uri;
    private _disposables: vscode.Disposable[] = [];

    public static createOrShow(extensionUri: vscode.Uri) {
        const column = vscode.window.activeTextEditor
            ? vscode.window.activeTextEditor.viewColumn
            : undefined;

        // If we already have a panel, show it.
        if (HelloWorldPanel.currentPanel) {
            HelloWorldPanel.currentPanel._panel.reveal(column);
            HelloWorldPanel.currentPanel._update();
            return;
        }

        // Otherwise, create a new panel.
        const panel = vscode.window.createWebviewPanel(
            HelloWorldPanel.viewType,
            "Hello,World",
            column || vscode.ViewColumn.One,
            {
                // Enable javascript in the webview
                enableScripts: true,

                // And restrict the webview to only loading content from our extension's `media` directory.
                localResourceRoots: [
                    vscode.Uri.joinPath(extensionUri, "media"),
                    vscode.Uri.joinPath(extensionUri, "out/compiled"),
                ],
            }
        );

        HelloWorldPanel.currentPanel = new HelloWorldPanel(panel, extensionUri);
    }

    public static kill() {
        HelloWorldPanel.currentPanel?.dispose();
        HelloWorldPanel.currentPanel = undefined;
    }

    public static revive(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
        HelloWorldPanel.currentPanel = new HelloWorldPanel(panel, extensionUri);
    }

    private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
        this._panel = panel;
        this._extensionUri = extensionUri;

        // Set the webview's initial html content
        this._update();

        // Listen for when the panel is disposed
        // This happens when the user closes the panel or when the panel is closed programatically
        this._panel.onDidDispose(() => this.dispose(), null, this._disposables);

        // // Handle messages from the webview
        // this._panel.webview.onDidReceiveMessage(
        //   (message) => {
        //     switch (message.command) {
        //       case "alert":
        //         vscode.window.showErrorMessage(message.text);
        //         return;
        //     }
        //   },
        //   null,
        //   this._disposables
        // );
    }

    public dispose() {
        HelloWorldPanel.currentPanel = undefined;

        // Clean up our resources
        this._panel.dispose();

        while (this._disposables.length) {
            const x = this._disposables.pop();
            if (x) {
                x.dispose();
            }
        }
    }

    private async _update() {
        const webview = this._panel.webview;

        this._panel.webview.html = this._getHtmlForWebview(webview);
        webview.onDidReceiveMessage(async (data) => {
            switch (data.type) {
                case "onInfo": {
                    if (!data.value) {
                        return;
                    }
                    vscode.window.showInformationMessage(data.value);
                    break;
                }
                case "onError": {
                    if (!data.value) {
                        return;
                    }
                    vscode.window.showErrorMessage(data.value);
                    break;
                }
                // case "tokens": {
                //     await Util.globalState.update(accessTokenKey, data.accessToken);
                //     await Util.globalState.update(refreshTokenKey, data.refreshToken);
                //     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", "compiled/swiper.js")
        // );

        // // Uri to load styles into webview
        const stylesResetUri = webview.asWebviewUri(vscode.Uri.joinPath(
            this._extensionUri,
            "media",
            "reset.css"
        ));
        const stylesMainUri = webview.asWebviewUri(vscode.Uri.joinPath(
            this._extensionUri,
            "media",
            "vscode.css"
        ));
        // const cssUri = webview.asWebviewUri(
        //     vscode.Uri.joinPath(this._extensionUri, "out", "compiled/swiper.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}">
        </script>
			</head>
            <body>
            <h1> Hello, World </h1>
            <input></input>
            <button>INPUT</button>
			</body>
			</html>`;
    }
}

There are two Vscode css files which the devs want us to use to make oue extensions match the themes of vscode itself. These are two files called 'reset.css' and 'vscode.css'. We can get this from the cat example in github, provided before.

So make a folder called media and store the two files there. Now the files will be called using the two functions described here.

const stylesResetUri = webview.asWebviewUri(vscode.Uri.joinPath(
            this._extensionUri,
            "media",
            "reset.css"
        ));
        
const stylesMainUri = webview.asWebviewUri(vscode.Uri.joinPath(
            this._extensionUri,
            "media",
            "vscode.css"
        ));

Note the code above is in a function ( check the full code given above)

after that we link it like this in HTML

<link href="${stylesResetUri}" rel="stylesheet">
<link href="${stylesMainUri}" rel="stylesheet">

Now we go to extension.ts. And we will create the pane when we invoke the already defined HelloWorld example. By executing the command

HelloWorldPanel.createOrShow(context.extensionUri);

In the function so it looks like this

context.subscriptions.push(
		vscode.commands.registerCommand("todo.askQuestion", async () => {
			HelloWorldPanel.createOrShow(context.extensionUri);
--->		const answer = await vscode.window.showInformationMessage(
				"Hey this is sachin Hows the day lads?",
				"Yay?",
				"Nay?"
			);
			if (answer === "Nay?") {
				vscode.window.showInformationMessage("Sorry to hear that bud :(");
			} else {
				console.log("Good to see you are happy ;)");
			}

		}));

So the function HelloWorldPanel.createOrShow(context.extensionUri); takes in a parameter of type vscode.uri which we pass as extensionUri.

The extensionUri comes from the context from the beginning activate function.

The activate function is called when the extension is first being setup and its where we initialise everything .

Now try running the extension with HelloWorld. We can see the webview Now with input and h1 tag and buttons from the HTML at end.

We can load Js the same way as the css by putting it in a media folder.

Clone this wiki locally