Get communication working between extension and webview; add shared data types

This commit is contained in:
Saoud Rizwan
2024-07-07 06:22:00 -04:00
parent 08effc4799
commit 991ea6bd4e
10 changed files with 105 additions and 80 deletions

View File

@@ -37,6 +37,7 @@ export function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand("claude-dev.plusButtonTapped", () => {
const message = "claude-dev.plusButtonTapped!"
vscode.window.showInformationMessage(message)
provider.postMessageToWebview({ type: "action", action: "plusButtonTapped"})
})
)
@@ -44,6 +45,7 @@ export function activate(context: vscode.ExtensionContext) {
vscode.commands.registerCommand("claude-dev.settingsButtonTapped", () => {
const message = "claude-dev.settingsButtonTapped!"
vscode.window.showInformationMessage(message)
provider.postMessageToWebview({ type: "action", action: "settingsButtonTapped"})
})
)

View File

@@ -2,6 +2,8 @@ import { getUri } from "../utilities/getUri"
import { getNonce } from "../utilities/getNonce"
//import * as weather from "weather-js"
import * as vscode from "vscode"
import { ExtensionMessage } from "../shared/ExtensionMessage"
import { WebviewMessage } from "../shared/WebviewMessage"
/*
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
@@ -36,6 +38,11 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
this._setWebviewMessageListener(webviewView.webview)
}
// Send any JSON serializable data to the react app
postMessageToWebview(message: ExtensionMessage) {
this._view?.webview.postMessage(message)
}
/**
* Defines and returns the HTML that should be rendered within the webview panel.
*
@@ -112,14 +119,16 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
* @param context A reference to the extension context
*/
private _setWebviewMessageListener(webview: vscode.Webview) {
webview.onDidReceiveMessage((message: any) => {
const command = message.command
const text = message.text
switch (command) {
case "hello":
webview.onDidReceiveMessage((message: WebviewMessage) => {
switch (message.type) {
case "text":
// Code that should run in response to the hello message command
vscode.window.showInformationMessage(text)
vscode.window.showInformationMessage(message.text!)
// Send a message to our webview.
// You can send any JSON serializable data.
// Could also do this in extension .ts
this.postMessageToWebview({ type: "text", text: `Extension: ${Date.now()}` })
return
// Add more switch case statements here as more webview message commands
// are created within the webview context (i.e. inside media/main.js)

View File

@@ -0,0 +1,8 @@
// type that represents json data that is sent from extension to webview, called ExtensionMessage and has 'type' enum which can be 'plusButtonTapped' or 'settingsButtonTapped' or 'hello'
// webview will hold state
export interface ExtensionMessage {
type: "text" | "action"
text?: string
action?: "plusButtonTapped" | "settingsButtonTapped"
}

View File

@@ -0,0 +1,5 @@
export interface WebviewMessage {
type: "text" | "action"
text?: string
action?: "newTaskButtonTapped" | "yesButtonTapped" | "noButtonTapped" | "executeButtonTapped"
}

View File

@@ -1,42 +1,28 @@
import React, { useState } from "react"
import logo from "./logo.svg"
import React, { useEffect, useState } from "react"
import "./App.css"
import { vscode } from "./utilities/vscode"
import {
VSCodeBadge,
VSCodeButton,
VSCodeCheckbox,
VSCodeDataGrid,
VSCodeDataGridCell,
VSCodeDataGridRow,
VSCodeDivider,
VSCodeDropdown,
VSCodeLink,
VSCodeOption,
VSCodePanels,
VSCodePanelTab,
VSCodePanelView,
VSCodeProgressRing,
VSCodeRadio,
VSCodeRadioGroup,
VSCodeTag,
VSCodeTextArea,
VSCodeTextField,
} from "@vscode/webview-ui-toolkit/react"
import ChatSidebar from "./components/ChatSidebar"
import Demo from "./components/Demo"
import SettingsView from "./components/SettingsView"
import { ExtensionMessage } from "@shared/ExtensionMessage"
const App: React.FC = () => {
const [showSettings, setShowSettings] = useState(true)
const [showSettings, setShowSettings] = useState(false)
const handleHowdyClick = () => {
vscode.postMessage({
command: "hello",
text: "Hey there partner! 🤠",
})
useEffect(() => {
window.addEventListener("message", (e: MessageEvent) => {
const message: ExtensionMessage = e.data
if (message.type === "action") {
switch (message.action!) {
case "settingsButtonTapped":
setShowSettings(true)
break
case "plusButtonTapped":
setShowSettings(false)
break
}
}
})
}, [])
return <>{showSettings ? <SettingsView /> : <ChatSidebar />}</>
}

View File

@@ -2,9 +2,10 @@ import React, { useState, useRef, useEffect, useCallback, KeyboardEvent } from "
import { VSCodeButton, VSCodeTextArea, VSCodeDivider, VSCodeTextField } from "@vscode/webview-ui-toolkit/react"
import { vscode } from "../utilities/vscode"
import DynamicTextArea from "react-textarea-autosize"
import { ExtensionMessage } from "@shared/ExtensionMessage"
interface Message {
id: number
id: string
text: string
sender: "user" | "assistant"
}
@@ -26,17 +27,14 @@ const ChatSidebar = () => {
const handleSendMessage = () => {
if (inputValue.trim()) {
const newMessage: Message = {
id: Date.now(),
id: `${Date.now()}-user`,
text: inputValue.trim(),
sender: "user",
}
setMessages([...messages, newMessage])
setMessages(currentMessages => [...currentMessages, newMessage])
setInputValue("")
// Here you would typically send the message to your extension's backend
vscode.postMessage({
command: "sendMessage",
text: newMessage.text,
})
vscode.postMessage({ type: "text", text: newMessage.text})
}
}
const handleKeyDown = (event: KeyboardEvent<HTMLTextAreaElement>) => {
@@ -52,10 +50,24 @@ const ChatSidebar = () => {
}
}, [])
useEffect(() => {
window.addEventListener("message", (e: MessageEvent) => {
const message: ExtensionMessage = e.data
if (message.type === "text") {
const newMessage: Message = {
id: `${Date.now()}-assistant`,
text: message.text!.trim(),
sender: "assistant",
}
setMessages(currentMessages => [...currentMessages, newMessage])
}
})
}, [])
return (
<div style={{ display: "flex", flexDirection: "column", height: "100vh", backgroundColor: "gray", overflow: "hidden" }}>
<div style={{ display: "flex", flexDirection: "column", height: "100vh", overflow: "hidden" }}>
<div style={{ flexGrow: 1, overflowY: "scroll", scrollbarWidth: "none" }}>
{messages.map((message) => (
{messages.map((message, index) => (
<div
key={message.id}
style={{

View File

@@ -1,5 +1,4 @@
import { vscode } from "../utilities/vscode"
import {
VSCodeBadge,
VSCodeButton,
@@ -23,12 +22,12 @@ import {
} from "@vscode/webview-ui-toolkit/react"
function Demo() {
function handleHowdyClick() {
vscode.postMessage({
command: "hello",
text: "Hey there partner! 🤠",
})
}
// function handleHowdyClick() {
// vscode.postMessage({
// command: "hello",
// text: "Hey there partner! 🤠",
// })
// }
const rowData = [
{
@@ -54,7 +53,7 @@ function Demo() {
return (
<main>
<h1>Hello World!</h1>
<VSCodeButton onClick={handleHowdyClick}>Howdy!</VSCodeButton>
<VSCodeButton>Howdy!</VSCodeButton>
<div className="grid gap-3 p-2 place-items-start">
<VSCodeDataGrid>

View File

@@ -61,13 +61,13 @@ const SettingsView = () => {
color: "var(--vscode-descriptionForeground)",
fontSize: "12px",
lineHeight: "1.5",
fontStyle: "italic"
fontStyle: "italic",
}}>
<p>Made possible by the latest breakthroughs in Claude 3.5 Sonnet's agentic coding capabilities.</p>
<p>
This project was submitted to Anthropic's "Build with Claude June 2024 contest".
This project was submitted to Anthropic's<br/>"Build with Claude June 2024 contest"
<VSCodeLink href="https://github.com/saoudrizwan/claude-dev">
github.com/saoudrizwan/claude-dev
https://github.com/saoudrizwan/claude-dev
</VSCodeLink>
</p>
</div>

View File

@@ -1,3 +1,4 @@
import { WebviewMessage } from "@shared/WebviewMessage"
import type { WebviewApi } from "vscode-webview"
/**
@@ -28,7 +29,7 @@ class VSCodeAPIWrapper {
*
* @param message Abitrary data (must be JSON serializable) to send to the extension context.
*/
public postMessage(message: unknown) {
public postMessage(message: WebviewMessage) {
if (this.vsCodeApi) {
this.vsCodeApi.postMessage(message)
} else {

View File

@@ -14,7 +14,10 @@
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
"jsx": "react-jsx",
"paths": {
"@shared/*": ["../src/shared/*"]
}
},
"include": ["src", "../src/shared"]
}