mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-21 12:51:17 -05:00
Clean up disposables when panel is closed
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
import { Uri, Webview } from "vscode"
|
import { Uri, Webview } from "vscode"
|
||||||
//import * as weather from "weather-js"
|
//import * as weather from "weather-js"
|
||||||
import * as vscode from "vscode"
|
import * as vscode from "vscode"
|
||||||
|
import { ClaudeDev } from "../ClaudeDev"
|
||||||
import { ClaudeMessage, ExtensionMessage } from "../shared/ExtensionMessage"
|
import { ClaudeMessage, ExtensionMessage } from "../shared/ExtensionMessage"
|
||||||
import { WebviewMessage } from "../shared/WebviewMessage"
|
import { WebviewMessage } from "../shared/WebviewMessage"
|
||||||
import { ClaudeDev } from "../ClaudeDev"
|
|
||||||
import { WebviewPanel, WebviewView } from "vscode"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
|
https://github.com/microsoft/vscode-webview-ui-toolkit-samples/blob/main/default/weather-webview/src/providers/WeatherViewProvider.ts
|
||||||
@@ -18,16 +17,38 @@ type ExtensionWorkspaceStateKey = "claudeMessages"
|
|||||||
|
|
||||||
export class SidebarProvider implements vscode.WebviewViewProvider {
|
export class SidebarProvider implements vscode.WebviewViewProvider {
|
||||||
public static readonly viewType = "claude-dev.SidebarProvider"
|
public static readonly viewType = "claude-dev.SidebarProvider"
|
||||||
|
private disposables: vscode.Disposable[] = []
|
||||||
private view?: vscode.WebviewView | vscode.WebviewPanel
|
private view?: vscode.WebviewView | vscode.WebviewPanel
|
||||||
private claudeDev?: ClaudeDev
|
private claudeDev?: ClaudeDev
|
||||||
private claudeMessagesCache: ClaudeMessage[] = []
|
private claudeMessagesCache: ClaudeMessage[] = []
|
||||||
|
|
||||||
constructor(private readonly context: vscode.ExtensionContext) {}
|
constructor(private readonly context: vscode.ExtensionContext) {}
|
||||||
|
|
||||||
|
/*
|
||||||
|
VSCode extensions use the disposable pattern to clean up resources when the sidebar/editor tab is closed by the user or system. This applies to event listening, commands, interacting with the UI, etc.
|
||||||
|
- https://vscode-docs.readthedocs.io/en/stable/extensions/patterns-and-principles/
|
||||||
|
- https://github.com/microsoft/vscode-extension-samples/blob/main/webview-sample/src/extension.ts
|
||||||
|
*/
|
||||||
|
async dispose() {
|
||||||
|
console.log("Disposing provider...")
|
||||||
|
await this.clearTask() // clears claudeDev and claudeMesssagesCache
|
||||||
|
console.log("Cleared task")
|
||||||
|
if (this.view && "dispose" in this.view) {
|
||||||
|
this.view.dispose()
|
||||||
|
console.log("Disposed webview")
|
||||||
|
}
|
||||||
|
while (this.disposables.length) {
|
||||||
|
const x = this.disposables.pop()
|
||||||
|
if (x) {
|
||||||
|
x.dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log("Disposed disposables")
|
||||||
|
}
|
||||||
|
|
||||||
resolveWebviewView(
|
resolveWebviewView(
|
||||||
webviewView: vscode.WebviewView | vscode.WebviewPanel
|
webviewView: vscode.WebviewView | vscode.WebviewPanel
|
||||||
//context: vscode.WebviewViewResolveContext<unknown>,
|
//context: vscode.WebviewViewResolveContext<unknown>, used to recreate a deallocated webview, but we don't need this since we use retainContextWhenHidden
|
||||||
//token: vscode.CancellationToken
|
//token: vscode.CancellationToken
|
||||||
): void | Thenable<void> {
|
): void | Thenable<void> {
|
||||||
this.view = webviewView
|
this.view = webviewView
|
||||||
@@ -50,26 +71,50 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
|||||||
// https://github.com/microsoft/vscode-discussions/discussions/840
|
// https://github.com/microsoft/vscode-discussions/discussions/840
|
||||||
if ("onDidChangeViewState" in webviewView) {
|
if ("onDidChangeViewState" in webviewView) {
|
||||||
// WebviewView and WebviewPanel have all the same properties except for this visibility listener
|
// WebviewView and WebviewPanel have all the same properties except for this visibility listener
|
||||||
webviewView.onDidChangeViewState(() => {
|
// panel
|
||||||
if (this.view?.visible) {
|
webviewView.onDidChangeViewState(
|
||||||
this.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
|
() => {
|
||||||
}
|
if (this.view?.visible) {
|
||||||
})
|
this.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
this.disposables
|
||||||
|
)
|
||||||
} else if ("onDidChangeVisibility" in webviewView) {
|
} else if ("onDidChangeVisibility" in webviewView) {
|
||||||
webviewView.onDidChangeVisibility(() => {
|
// sidebar
|
||||||
if (this.view?.visible) {
|
webviewView.onDidChangeVisibility(
|
||||||
this.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
|
() => {
|
||||||
}
|
if (this.view?.visible) {
|
||||||
})
|
this.postMessageToWebview({ type: "action", action: "didBecomeVisible" })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
this.disposables
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Listen for when the view is disposed
|
||||||
|
// This happens when the user closes the view or when the view is closed programmatically
|
||||||
|
webviewView.onDidDispose(
|
||||||
|
async () => {
|
||||||
|
await this.dispose()
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
this.disposables
|
||||||
|
)
|
||||||
|
|
||||||
// Listen for when color changes
|
// Listen for when color changes
|
||||||
vscode.workspace.onDidChangeConfiguration((e) => {
|
vscode.workspace.onDidChangeConfiguration(
|
||||||
if (e.affectsConfiguration("workbench.colorTheme")) {
|
(e) => {
|
||||||
// Sends latest theme name to webview
|
if (e.affectsConfiguration("workbench.colorTheme")) {
|
||||||
this.postStateToWebview()
|
// Sends latest theme name to webview
|
||||||
}
|
this.postStateToWebview()
|
||||||
})
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
this.disposables
|
||||||
|
)
|
||||||
|
|
||||||
// if the extension is starting a new session, clear previous task state
|
// if the extension is starting a new session, clear previous task state
|
||||||
this.clearTask()
|
this.clearTask()
|
||||||
@@ -178,55 +223,58 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
|
|||||||
* executes code based on the message that is recieved.
|
* executes code based on the message that is recieved.
|
||||||
*
|
*
|
||||||
* @param webview A reference to the extension webview
|
* @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) => {
|
webview.onDidReceiveMessage(
|
||||||
switch (message.type) {
|
async (message: WebviewMessage) => {
|
||||||
case "webviewDidLaunch":
|
switch (message.type) {
|
||||||
await this.updateGlobalState("didOpenOnce", true)
|
case "webviewDidLaunch":
|
||||||
await this.postStateToWebview()
|
await this.updateGlobalState("didOpenOnce", true)
|
||||||
break
|
await this.postStateToWebview()
|
||||||
case "newTask":
|
break
|
||||||
// Code that should run in response to the hello message command
|
case "newTask":
|
||||||
//vscode.window.showInformationMessage(message.text!)
|
// Code that should run in response to the hello message command
|
||||||
|
//vscode.window.showInformationMessage(message.text!)
|
||||||
|
|
||||||
// Send a message to our webview.
|
// Send a message to our webview.
|
||||||
// You can send any JSON serializable data.
|
// You can send any JSON serializable data.
|
||||||
// Could also do this in extension .ts
|
// 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
|
// 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!)
|
await this.tryToInitClaudeDevWithTask(message.text!)
|
||||||
break
|
break
|
||||||
case "apiKey":
|
case "apiKey":
|
||||||
await this.storeSecret("apiKey", message.text!)
|
await this.storeSecret("apiKey", message.text!)
|
||||||
this.claudeDev?.updateApiKey(message.text!)
|
this.claudeDev?.updateApiKey(message.text!)
|
||||||
await this.postStateToWebview()
|
await this.postStateToWebview()
|
||||||
break
|
break
|
||||||
case "maxRequestsPerTask":
|
case "maxRequestsPerTask":
|
||||||
let result: number | undefined = undefined
|
let result: number | undefined = undefined
|
||||||
if (message.text && message.text.trim()) {
|
if (message.text && message.text.trim()) {
|
||||||
const num = Number(message.text)
|
const num = Number(message.text)
|
||||||
if (!isNaN(num)) {
|
if (!isNaN(num)) {
|
||||||
result = num
|
result = num
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
await this.updateGlobalState("maxRequestsPerTask", result)
|
||||||
await this.updateGlobalState("maxRequestsPerTask", result)
|
this.claudeDev?.updateMaxRequestsPerTask(result)
|
||||||
this.claudeDev?.updateMaxRequestsPerTask(result)
|
await this.postStateToWebview()
|
||||||
await this.postStateToWebview()
|
break
|
||||||
break
|
case "askResponse":
|
||||||
case "askResponse":
|
this.claudeDev?.handleWebviewAskResponse(message.askResponse!, message.text)
|
||||||
this.claudeDev?.handleWebviewAskResponse(message.askResponse!, message.text)
|
break
|
||||||
break
|
case "clearTask":
|
||||||
case "clearTask":
|
// newTask will start a new task with a given task text, while clear task resets the current session and allows for a new task to be started
|
||||||
// newTask will start a new task with a given task text, while clear task resets the current session and allows for a new task to be started
|
await this.clearTask()
|
||||||
await this.clearTask()
|
await this.postStateToWebview()
|
||||||
await this.postStateToWebview()
|
break
|
||||||
break
|
// Add more switch case statements here as more webview message commands
|
||||||
// Add more switch case statements here as more webview message commands
|
// are created within the webview context (i.e. inside media/main.js)
|
||||||
// are created within the webview context (i.e. inside media/main.js)
|
}
|
||||||
}
|
},
|
||||||
})
|
null,
|
||||||
|
this.disposables
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async postStateToWebview() {
|
async postStateToWebview() {
|
||||||
|
|||||||
Reference in New Issue
Block a user