Add sidebar code

This commit is contained in:
Saoud Rizwan 2024-07-06 01:31:16 -04:00
parent 0ede211d4f
commit fd750864db
8 changed files with 372 additions and 16 deletions

View File

@ -12,12 +12,46 @@
"activationEvents": [],
"main": "./dist/extension.js",
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "custom-activitybar",
"title": "VSCode Extension",
"icon": "assets/logo_bito.svg"
}
]
},
"views": {
"custom-activitybar": [
{
"type": "webview",
"id": "vscodeSidebar.openview",
"name": "View",
"contextualTitle": "View"
}
]
},
"commands": [
{
"command": "claude-dev.helloWorld",
"title": "Hello World"
"command": "vscodeSidebar.openview",
"title": "Sidebar View"
},
{
"command": "vscodeSidebar.menu.view",
"category": "vscode-extension-sidebar-html",
"title": "Sample WebView in VS Code Sidebar",
"icon": "$(clear-all)"
}
]
],
"menus": {
"view/title": [
{
"command": "vscodeSidebar.menu.view",
"group": "navigation",
"when": "view == vscodeSidebar.openview"
}
]
}
},
"scripts": {
"vscode:prepublish": "npm run package",

BIN
src/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -2,6 +2,16 @@
// Import the module and reference it with the alias vscode in your code below
import * as vscode from "vscode"
import { HelloWorldPanel } from "./HelloWorldPanel"
import { SidebarProvider } from "./providers/SidebarProvider"
/*
Built using https://github.com/microsoft/vscode-webview-ui-toolkit
Inspired by
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/default/weather-webview
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/tree/main/frameworks/hello-world-react-cra
*/
// This method is called when your extension is activated
// Your extension is activated the very first time the command is executed
@ -21,11 +31,32 @@ export function activate(context: vscode.ExtensionContext) {
// context.subscriptions.push(disposable)
const helloCommand = vscode.commands.registerCommand("claude-dev.helloWorld", () => {
HelloWorldPanel.render(context.extensionUri)
// const helloCommand = vscode.commands.registerCommand("claude-dev.helloWorld", () => {
// HelloWorldPanel.render(context.extensionUri)
// })
// context.subscriptions.push(helloCommand)
const provider = new SidebarProvider(context.extensionUri)
context.subscriptions.push(vscode.window.registerWebviewViewProvider(SidebarProvider.viewType, provider))
context.subscriptions.push(
vscode.commands.registerCommand("vscodeSidebar.menu.view", () => {
const message = "Menu/Title of extension is clicked !"
vscode.window.showInformationMessage(message)
})
)
// Command has been defined in the package.json file
// Provide the implementation of the command with registerCommand
// CommandId parameter must match the command field in package.json
let openWebView = vscode.commands.registerCommand("vscodeSidebar.openview", () => {
// Display a message box to the user
vscode.window.showInformationMessage('Command " Sidebar View [vscodeSidebar.openview] " called.')
})
context.subscriptions.push(helloCommand)
context.subscriptions.push(openWebView)
}
// This method is called when your extension is deactivated

View File

@ -0,0 +1,78 @@
import { getUri } from "../utilities/getUri"
import { getNonce } from "../utilities/getNonce"
//import * as weather from "weather-js"
import * as vscode from "vscode"
export class SidebarProvider implements vscode.WebviewViewProvider {
public static readonly viewType = "vscodeSidebar.openview"
private _view?: vscode.WebviewView
constructor(private readonly _extensionUri: vscode.Uri) {}
resolveWebviewView(
webviewView: vscode.WebviewView,
context: vscode.WebviewViewResolveContext<unknown>,
token: vscode.CancellationToken
): void | Thenable<void> {
this._view = webviewView
webviewView.webview.options = {
// Allow scripts in the webview
enableScripts: true,
localResourceRoots: [this._extensionUri],
}
webviewView.webview.html = this.getHtmlContent(webviewView.webview)
}
private getHtmlContent(webview: vscode.Webview): string {
// Get the local path to main script run in the webview,
// then convert it to a uri we can use in the webview.
const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "main.js"))
const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "reset.css"))
const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "vscode.css"))
// Same for stylesheet
const stylesheetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, "assets", "main.css"))
// Use a nonce to only allow a specific script to be run.
const nonce = getNonce()
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource}; script-src 'nonce-${nonce}';">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="${styleResetUri}" rel="stylesheet">
<link href="${styleVSCodeUri}" rel="stylesheet">
<link href="${stylesheetUri}" rel="stylesheet">
</head>
<body>
<section class="wrapper">
<div class="container">
<div class="content">
<h2 class="subtitle">Subscribe today</h2>
<input type="text" class="mail" placeholder="Your email address" name="mail" required>
<button class="add-color-button">Subscribe</button>
<p class="text">We wont send you spam.</p>
<p class="text">Unsubscribe at any time.</p>
</div>
</div>
</section>
<!--<script nonce="${nonce}" src="${scriptUri}"></script>-->
</body>
</html>`
}
}

View File

@ -1,3 +1,11 @@
/**
* A helper function that returns a unique alphanumeric identifier called a nonce.
*
* @remarks This function is primarily used to help enforce content security
* policies for resources/scripts being executed in a webview context.
*
* @returns A nonce
*/
export function getNonce() {
let text = ""
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"

View File

@ -1,5 +1,16 @@
import { Uri, Webview } from "vscode"
/**
* A helper function which will get the webview URI of a given file or resource.
*
* @remarks This URI can be used within a webview's HTML as a link to the
* given file/resource.
*
* @param webview A reference to the extension webview
* @param extensionUri The URI of the directory containing the extension
* @param pathList An array of strings representing the path to a file/resource
* @returns A URI pointing to the file/resource
*/
export function getUri(webview: Webview, extensionUri: Uri, pathList: string[]) {
return webview.asWebviewUri(Uri.joinPath(extensionUri, ...pathList))
}

View File

@ -1,14 +1,8 @@
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
@ -16,10 +10,165 @@ function main() {
const howdyButton = document.getElementById("howdy") as Button;
howdyButton?.addEventListener("click", handleHowdyClick);
}
function handleHowdyClick() {
vscode.postMessage({
command: "hello",
text: "Hey there partner! 🤠",
});
}
}
*/
import {
provideVSCodeDesignSystem,
Button,
Dropdown,
ProgressRing,
TextField,
vsCodeButton,
vsCodeDropdown,
vsCodeOption,
vsCodeTextField,
vsCodeProgressRing,
} from "@vscode/webview-ui-toolkit";
// In order to use the Webview UI Toolkit web components they
// must be registered with the browser (i.e. webview) using the
// syntax below.
provideVSCodeDesignSystem().register(
vsCodeButton(),
vsCodeDropdown(),
vsCodeOption(),
vsCodeProgressRing(),
vsCodeTextField()
);
// Get access to the VS Code API from within the webview context
const vscode = acquireVsCodeApi();
// Just like a regular webpage we need to wait for the webview
// DOM to load before we can reference any of the HTML elements
// or toolkit components
window.addEventListener("load", main);
// Main function that gets executed once the webview DOM loads
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 checkWeatherButton = document.getElementById("check-weather-button") as Button;
checkWeatherButton.addEventListener("click", checkWeather);
setVSCodeMessageListener();
}
function checkWeather() {
const location = document.getElementById("location") as TextField;
const unit = document.getElementById("unit") as Dropdown;
// Passes a message back to the extension context with the location that
// should be searched for and the degree unit (F or C) that should be returned
vscode.postMessage({
command: "weather",
location: location.value,
unit: unit.value,
});
displayLoadingState();
}
// Sets up an event listener to listen for messages passed from the extension context
// and executes code based on the message that is recieved
function setVSCodeMessageListener() {
window.addEventListener("message", (event) => {
const command = event.data.command;
// switch (command) {
// case "weather":
// const weatherData = JSON.parse(event.data.payload);
// displayWeatherData(weatherData);
// break;
// case "error":
// displayError(event.data.message);
// break;
// }
});
}
function displayLoadingState() {
const loading = document.getElementById("loading") as ProgressRing;
const icon = document.getElementById("icon");
const summary = document.getElementById("summary");
if (loading && icon && summary) {
loading.classList.remove("hidden");
icon.classList.add("hidden");
summary.textContent = "Getting weather...";
}
}
// function displayWeatherData(weatherData) {
// const loading = document.getElementById("loading") as ProgressRing;
// const icon = document.getElementById("icon");
// const summary = document.getElementById("summary");
// if (loading && icon && summary) {
// loading.classList.add("hidden");
// icon.classList.remove("hidden");
// icon.textContent = getWeatherIcon(weatherData);
// summary.textContent = getWeatherSummary(weatherData);
// }
// }
// function displayError(errorMsg) {
// const loading = document.getElementById("loading") as ProgressRing;
// const icon = document.getElementById("icon");
// const summary = document.getElementById("summary");
// if (loading && icon && summary) {
// loading.classList.add("hidden");
// icon.classList.add("hidden");
// summary.textContent = errorMsg;
// }
// }
// function getWeatherSummary(weatherData) {
// const skyText = weatherData.current.skytext;
// const temperature = weatherData.current.temperature;
// const degreeType = weatherData.location.degreetype;
// return `${skyText}, ${temperature}${degreeType}`;
// }
// function getWeatherIcon(weatherData) {
// const skyText = weatherData.current.skytext.toLowerCase();
// let icon = "";
// switch (skyText) {
// case "sunny":
// icon = "☀️";
// break;
// case "mostly sunny":
// icon = "🌤";
// break;
// case "partly sunny":
// icon = "🌥";
// break;
// case "clear":
// icon = "☀️";
// break;
// case "fair":
// icon = "🌥";
// break;
// case "mostly cloudy":
// icon = "☁️";
// break;
// case "cloudy":
// icon = "☁️";
// break;
// case "rain showers":
// icon = "🌦";
// break;
// default:
// icon = "✨";
// }
// return icon;
// }

45
src/webview/styles.css Normal file
View File

@ -0,0 +1,45 @@
h1 {
font-size: 1.5em;
}
#search-container {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
}
#location {
width: 100%;
margin-top: 0.5rem;
}
#unit {
min-width: 30px;
width: 100%;
margin-top: 0.5rem;
}
#check-weather-button {
margin-top: 0.5rem;
}
#results-container {
display: flex;
align-items: center;
justify-content: space-around;
background-color: var(--vscode-input-background);
padding: 1rem;
margin: 1rem 0;
border-radius: 2px;
}
#icon {
font-size: 3em;
padding: 0;
margin: 0;
}
.hidden {
display: none;
}