Files
claude-task-master/apps/extension/src/services/sidebar-webview-manager.ts
DavidMaliglowka 64302dc191 feat(extension): complete VS Code extension with kanban board interface (#997)
---------
Co-authored-by: DavidMaliglowka <13022280+DavidMaliglowka@users.noreply.github.com>
Co-authored-by: Ralph Khreish <35776126+Crunchyman-ralph@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-08-01 14:04:22 +02:00

91 lines
2.5 KiB
TypeScript

import * as vscode from 'vscode';
import type { TaskMasterApi } from '../utils/task-master-api';
export class SidebarWebviewManager implements vscode.WebviewViewProvider {
private webviewView?: vscode.WebviewView;
private api?: TaskMasterApi;
constructor(private readonly extensionUri: vscode.Uri) {}
setApi(api: TaskMasterApi): void {
this.api = api;
// Update connection status if webview exists
if (this.webviewView) {
this.updateConnectionStatus();
}
}
resolveWebviewView(
webviewView: vscode.WebviewView,
context: vscode.WebviewViewResolveContext,
token: vscode.CancellationToken
): void {
this.webviewView = webviewView;
webviewView.webview.options = {
enableScripts: true,
localResourceRoots: [
vscode.Uri.joinPath(this.extensionUri, 'dist'),
vscode.Uri.joinPath(this.extensionUri, 'assets')
]
};
webviewView.webview.html = this.getHtmlContent(webviewView.webview);
// Handle messages from the webview
webviewView.webview.onDidReceiveMessage((message) => {
if (message.command === 'openBoard') {
vscode.commands.executeCommand('tm.showKanbanBoard');
}
});
// Update connection status on load
this.updateConnectionStatus();
}
updateConnectionStatus(): void {
if (!this.webviewView || !this.api) return;
const status = this.api.getConnectionStatus();
this.webviewView.webview.postMessage({
type: 'connectionStatus',
data: status
});
}
private getHtmlContent(webview: vscode.Webview): string {
const scriptUri = webview.asWebviewUri(
vscode.Uri.joinPath(this.extensionUri, 'dist', 'sidebar.js')
);
const styleUri = webview.asWebviewUri(
vscode.Uri.joinPath(this.extensionUri, 'dist', 'index.css')
);
const nonce = this.getNonce();
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}'; style-src ${webview.cspSource} 'unsafe-inline';">
<link href="${styleUri}" rel="stylesheet">
<title>TaskMaster</title>
</head>
<body>
<div id="root"></div>
<script nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>`;
}
private getNonce(): string {
let text = '';
const possible =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
}