Add vscode-webview-ui-toolkit and follow tutorial to get started

This commit is contained in:
Saoud Rizwan
2024-07-06 00:40:50 -04:00
parent 094524625b
commit 0ede211d4f
11 changed files with 378 additions and 65 deletions

112
src/HelloWorldPanel.ts Normal file
View File

@@ -0,0 +1,112 @@
/*
Example of vscode-webview-ui-toolkit
https://github.com/microsoft/vscode-webview-ui-toolkit/blob/main/docs/getting-started.md
https://github.com/microsoft/vscode-webview-ui-toolkit/blob/main/docs/components.md
*/
import * as vscode from "vscode"
import { getUri } from "./utilities/getUri"
import { getNonce } from "./utilities/getNonce"
export class HelloWorldPanel {
/*
- public can be access outside of class
- private can only be accessed by class itself (_ is a convention not required)
- readonly means var can only be set during declaration or in constructor
- static means var is shared among all instances of class
*/
public static currentPanel: HelloWorldPanel | undefined
private readonly panel: vscode.WebviewPanel
private disposables: vscode.Disposable[] = []
private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri) {
this.panel = panel
// the method can be triggered when the webview panel is closed
this.panel.onDidDispose(() => this.dispose(), null, this.disposables)
this.panel.webview.html = this.getWebviewContent(this.panel.webview, extensionUri)
this.setWebviewMessageListener(this.panel.webview);
}
// This will be responsible for rendering the current webview panel if it exists or creating and displaying a new webview panel.
public static render(extensionUri: vscode.Uri) {
if (HelloWorldPanel.currentPanel) {
HelloWorldPanel.currentPanel.panel.reveal(vscode.ViewColumn.One)
} else {
const panel = vscode.window.createWebviewPanel("helloworld", "Hello World", vscode.ViewColumn.One, {
// Enable javascript in the webview
enableScripts: true,
// Restrict the webview to only load resources from the `out` directory
localResourceRoots: [vscode.Uri.joinPath(extensionUri, "dist")],
})
HelloWorldPanel.currentPanel = new HelloWorldPanel(panel, extensionUri)
}
}
// webview resources are cleaned up when the webview panel is closed by the user or closed programmatically.
public dispose() {
HelloWorldPanel.currentPanel = undefined
this.panel.dispose()
while (this.disposables.length) {
const disposable = this.disposables.pop()
if (disposable) {
disposable.dispose()
}
}
}
// where the UI of the extension will be defined. This is also where references to CSS and JavaScript files are created and inserted into the webview HTML.
private getWebviewContent(webview: vscode.Webview, extensionUri: vscode.Uri) {
const webviewUri = getUri(webview, extensionUri, ["dist", "webview.js"])
/*
content security policy of your webview to only allow scripts that have a specific nonce
create a content security policy meta tag so that only loading scripts with a nonce is allowed
As your extension grows you will likely want to add custom styles, fonts, and/or images to your webview. If you do, you will need to update the content security policy meta tag to explicity allow for these resources. E.g.
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; font-src ${webview.cspSource}; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}';">
in meta tag we add nonce attribute: A cryptographic nonce (only used once) to allow scripts. The server must generate a unique nonce value each time it transmits a policy. It is critical to provide a nonce that cannot be guessed as bypassing a resource's policy is otherwise trivial.
*/
const nonce = getNonce()
return /*html*/ `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'nonce-${nonce}';">
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
<vscode-button id="howdy">Howdy!</vscode-button>
<script type="module" nonce="${nonce}" src="${webviewUri}"></script>
</body>
</html>
`
}
// responsible for setting up an event listener that listens for messages passed from the webview context and executes code based on the received message.
private setWebviewMessageListener(webview: vscode.Webview) {
webview.onDidReceiveMessage(
(message: any) => {
const command = message.command
const text = message.text
switch (command) {
case "hello":
vscode.window.showInformationMessage(text)
return
}
},
undefined,
this.disposables
)
}
}

View File

@@ -1,25 +1,31 @@
// 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 * as vscode from "vscode"
import { HelloWorldPanel } from "./HelloWorldPanel"
// 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) {
// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
console.log('Congratulations, your extension "claude-dev" is now active!');
console.log('Congratulations, your extension "claude-dev" is now active!')
// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
const disposable = vscode.commands.registerCommand('claude-dev.helloWorld', () => {
// The code you place here will be executed every time your command is executed
// Display a message box to the user
vscode.window.showInformationMessage('Hello World from claude-dev!');
});
// const disposable = vscode.commands.registerCommand("claude-dev.helloWorld", () => {
// // The code you place here will be executed every time your command is executed
// // Display a message box to the user
// vscode.window.showInformationMessage("Hello World from claude-dev!")
// })
context.subscriptions.push(disposable);
// context.subscriptions.push(disposable)
const helloCommand = vscode.commands.registerCommand("claude-dev.helloWorld", () => {
HelloWorldPanel.render(context.extensionUri)
})
context.subscriptions.push(helloCommand)
}
// This method is called when your extension is deactivated

View File

@@ -0,0 +1,8 @@
export 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
}

5
src/utilities/getUri.ts Normal file
View File

@@ -0,0 +1,5 @@
import { Uri, Webview } from "vscode"
export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) {
return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList))
}

25
src/webview/main.ts Normal file
View File

@@ -0,0 +1,25 @@
import { provideVSCodeDesignSystem, vsCodeButton, vsCodeCheckbox } from "@vscode/webview-ui-toolkit"
// const toolkit = require("@vscode/webview-ui-toolkit")
// /*
// You must register the components you want to use
// */
provideVSCodeDesignSystem().register(vsCodeButton(), vsCodeCheckbox())
const vscode = acquireVsCodeApi();
window.addEventListener("load", main);
function main() {
// To get improved type annotations/IntelliSense the associated class for
// a given toolkit component can be imported and used to type cast a reference
// to the element (i.e. the `as Button` syntax)
const howdyButton = document.getElementById("howdy") as Button;
howdyButton?.addEventListener("click", handleHowdyClick);
}
function handleHowdyClick() {
vscode.postMessage({
command: "hello",
text: "Hey there partner! 🤠",
});
}