Saoud Rizwan пре 1 година
родитељ
комит
fd750864db
8 измењених фајлова са 372 додато и 16 уклоњено
  1. 37 3
      package.json
  2. BIN
      src/assets/icon.png
  3. 34 3
      src/extension.ts
  4. 78 0
      src/providers/SidebarProvider.ts
  5. 8 0
      src/utilities/getNonce.ts
  6. 11 0
      src/utilities/getUri.ts
  7. 159 10
      src/webview/main.ts
  8. 45 0
      src/webview/styles.css

+ 37 - 3
package.json

@@ -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


+ 34 - 3
src/extension.ts

@@ -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

+ 78 - 0
src/providers/SidebarProvider.ts

@@ -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 won’t send you spam.</p>
+                <p class="text">Unsubscribe at any time.</p>
+                
+            </div>
+      </div>
+			</section>
+			<!--<script nonce="${nonce}" src="${scriptUri}"></script>-->
+      </body>
+
+			</html>`
+	}
+}

+ 8 - 0
src/utilities/getNonce.ts

@@ -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"

+ 11 - 0
src/utilities/getUri.ts

@@ -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))
 }

+ 159 - 10
src/webview/main.ts

@@ -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())
 
+/*
+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 - 0
src/webview/styles.css

@@ -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;
+}