mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 20:31:37 -05:00
Implement bidirectional extension webview messaging system; extension holds claude messages state to keep webview stateless
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { Uri, Webview } from "vscode"
|
||||
//import * as weather from "weather-js"
|
||||
import * as vscode from "vscode"
|
||||
import { ExtensionMessage } from "../shared/ExtensionMessage"
|
||||
import { ClaudeMessage, ExtensionMessage } from "../shared/ExtensionMessage"
|
||||
import { WebviewMessage } from "../shared/WebviewMessage"
|
||||
import { ClaudeDev } from "../ClaudeDev"
|
||||
|
||||
/*
|
||||
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
|
||||
@@ -12,11 +13,13 @@ https://github.com/KumarVariable/vscode-extension-sidebar-html/blob/master/src/c
|
||||
|
||||
type ExtensionSecretKey = "apiKey"
|
||||
type ExtensionGlobalStateKey = "didOpenOnce" | "maxRequestsPerTask"
|
||||
type ExtensionWorkspaceStateKey = "claudeMessages" | "apiConversationHistory"
|
||||
|
||||
export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
public static readonly viewType = "claude-dev.SidebarProvider"
|
||||
|
||||
private _view?: vscode.WebviewView
|
||||
private view?: vscode.WebviewView
|
||||
private claudeDev?: ClaudeDev
|
||||
|
||||
constructor(private readonly context: vscode.ExtensionContext) {}
|
||||
|
||||
@@ -25,7 +28,7 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
context: vscode.WebviewViewResolveContext<unknown>,
|
||||
token: vscode.CancellationToken
|
||||
): void | Thenable<void> {
|
||||
this._view = webviewView
|
||||
this.view = webviewView
|
||||
|
||||
webviewView.webview.options = {
|
||||
// Allow scripts in the webview
|
||||
@@ -36,12 +39,22 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
|
||||
// Sets up an event listener to listen for messages passed from the webview view context
|
||||
// and executes code based on the message that is recieved
|
||||
this._setWebviewMessageListener(webviewView.webview)
|
||||
this.setWebviewMessageListener(webviewView.webview)
|
||||
}
|
||||
|
||||
async tryToInitClaudeDevWithTask(task: string) {
|
||||
const [apiKey, maxRequestsPerTask] = await Promise.all([
|
||||
this.getSecret("apiKey") as Promise<string | undefined>,
|
||||
this.getGlobalState("maxRequestsPerTask") as Promise<number | undefined>,
|
||||
])
|
||||
if (this.view && apiKey) {
|
||||
this.claudeDev = new ClaudeDev(this, task, apiKey, maxRequestsPerTask)
|
||||
}
|
||||
}
|
||||
|
||||
// Send any JSON serializable data to the react app
|
||||
postMessageToWebview(message: ExtensionMessage) {
|
||||
this._view?.webview.postMessage(message)
|
||||
async postMessageToWebview(message: ExtensionMessage) {
|
||||
await this.view?.webview.postMessage(message)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -131,24 +144,27 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
* @param webview A reference to the extension webview
|
||||
* @param context A reference to the extension context
|
||||
*/
|
||||
private _setWebviewMessageListener(webview: vscode.Webview) {
|
||||
private setWebviewMessageListener(webview: vscode.Webview) {
|
||||
webview.onDidReceiveMessage(async (message: WebviewMessage) => {
|
||||
switch (message.type) {
|
||||
case "webviewDidLaunch":
|
||||
await this.updateGlobalState("didOpenOnce", true)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "text":
|
||||
case "newTask":
|
||||
// Code that should run in response to the hello message command
|
||||
vscode.window.showInformationMessage(message.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()}` })
|
||||
//this.postMessageToWebview({ type: "text", text: `Extension: ${Date.now()}` })
|
||||
// initializing new instance of ClaudeDev will make sure that any agentically running promises in old instance don't affect our new task. this essentially creates a fresh slate for the new task
|
||||
await this.tryToInitClaudeDevWithTask(message.text!)
|
||||
break
|
||||
case "apiKey":
|
||||
await this.storeSecret("apiKey", message.text!)
|
||||
this.claudeDev?.updateApiKey(message.text!)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "maxRequestsPerTask":
|
||||
@@ -160,25 +176,65 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
}
|
||||
}
|
||||
await this.updateGlobalState("maxRequestsPerTask", result)
|
||||
this.claudeDev?.updateMaxRequestsPerTask(result)
|
||||
await this.postStateToWebview()
|
||||
break
|
||||
case "askResponse":
|
||||
this.claudeDev?.handleWebviewAskResponse(message.askResponse!, message.text)
|
||||
// Add more switch case statements here as more webview message commands
|
||||
// are created within the webview context (i.e. inside media/main.js)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private async postStateToWebview() {
|
||||
const [didOpenOnce, apiKey, maxRequestsPerTask] = await Promise.all([
|
||||
async postStateToWebview() {
|
||||
const [didOpenOnce, apiKey, maxRequestsPerTask, claudeMessages] = await Promise.all([
|
||||
this.getGlobalState("didOpenOnce") as Promise<boolean | undefined>,
|
||||
this.getSecret("apiKey") as Promise<string | undefined>,
|
||||
this.getGlobalState("maxRequestsPerTask") as Promise<number | undefined>,
|
||||
this.getClaudeMessages(),
|
||||
])
|
||||
this.postMessageToWebview({
|
||||
type: "state",
|
||||
state: { didOpenOnce: !!didOpenOnce, apiKey: apiKey, maxRequestsPerTask: maxRequestsPerTask },
|
||||
state: { didOpenOnce: !!didOpenOnce, apiKey, maxRequestsPerTask, claudeMessages },
|
||||
})
|
||||
}
|
||||
|
||||
// client messages
|
||||
|
||||
async getClaudeMessages(): Promise<ClaudeMessage[]> {
|
||||
const messages = (await this.getWorkspaceState("claudeMessages")) as ClaudeMessage[]
|
||||
return messages || []
|
||||
}
|
||||
|
||||
async setClaudeMessages(messages: ClaudeMessage[] | undefined) {
|
||||
await this.updateWorkspaceState("claudeMessages", messages)
|
||||
}
|
||||
|
||||
async addClaudeMessage(message: ClaudeMessage): Promise<ClaudeMessage[]> {
|
||||
const messages = await this.getClaudeMessages()
|
||||
messages.push(message)
|
||||
await this.setClaudeMessages(messages)
|
||||
return messages
|
||||
}
|
||||
|
||||
// api conversation history
|
||||
|
||||
async getApiConversationHistory(): Promise<ClaudeMessage[]> {
|
||||
const messages = (await this.getWorkspaceState("apiConversationHistory")) as ClaudeMessage[]
|
||||
return messages || []
|
||||
}
|
||||
|
||||
async setApiConversationHistory(messages: ClaudeMessage[] | undefined) {
|
||||
await this.updateWorkspaceState("apiConversationHistory", messages)
|
||||
}
|
||||
|
||||
async addMessageToApiConversationHistory(message: ClaudeMessage): Promise<ClaudeMessage[]> {
|
||||
const messages = await this.getClaudeMessages()
|
||||
messages.push(message)
|
||||
await this.setClaudeMessages(messages)
|
||||
return messages
|
||||
}
|
||||
|
||||
/*
|
||||
Storage
|
||||
@@ -186,6 +242,8 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
https://www.eliostruyf.com/devhack-code-extension-storage-options/
|
||||
*/
|
||||
|
||||
// global
|
||||
|
||||
private async updateGlobalState(key: ExtensionGlobalStateKey, value: any) {
|
||||
await this.context.globalState.update(key, value)
|
||||
}
|
||||
@@ -194,6 +252,18 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||
return await this.context.globalState.get(key)
|
||||
}
|
||||
|
||||
// workspace
|
||||
|
||||
private async updateWorkspaceState(key: ExtensionWorkspaceStateKey, value: any) {
|
||||
await this.context.workspaceState.update(key, value)
|
||||
}
|
||||
|
||||
private async getWorkspaceState(key: ExtensionWorkspaceStateKey) {
|
||||
return await this.context.workspaceState.get(key)
|
||||
}
|
||||
|
||||
// secrets
|
||||
|
||||
private async storeSecret(key: ExtensionSecretKey, value: any) {
|
||||
await this.context.secrets.store(key, value)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user