import * as vscode from 'vscode'; import * as os from 'os'; import { getThemeKindName } from './theme'; import type { ConnectionStatus } from './opencode'; export type PanelType = 'chat' | 'agentManager'; export interface WebviewHtmlOptions { webview: vscode.Webview; extensionUri: vscode.Uri; workspaceFolder: string; initialStatus: ConnectionStatus; cliAvailable: boolean; panelType?: PanelType; initialSessionId?: string; viewMode?: 'sidebar' | 'editor'; devServerUrl?: string | null; extensionVersion?: string; } const asCspToken = (value: string | null | undefined): string | null => { if (!value) { return null; } const trimmed = value.trim(); return trimmed.length > 0 ? trimmed : null; }; const toOrigin = (value: string | null | undefined): string | null => { if (!value) { return null; } try { return new URL(value).origin; } catch { return null; } }; const uniqueTokens = (values: Array): string => { return Array.from(new Set(values.map(asCspToken).filter((value): value is string => Boolean(value)))).join(' '); }; export function getWebviewHtml(options: WebviewHtmlOptions): string { const { webview, extensionUri, workspaceFolder, initialStatus, cliAvailable, panelType = 'chat', initialSessionId, viewMode = 'sidebar', devServerUrl, extensionVersion = '', } = options; const scriptPath = vscode.Uri.joinPath(extensionUri, 'dist', 'webview', 'assets', 'index.js'); const scriptUri = webview.asWebviewUri(scriptPath); const normalizedDevServerUrl = asCspToken(devServerUrl)?.replace(/\/$/, '') ?? null; const devServerOrigin = toOrigin(normalizedDevServerUrl); const styleSrc = uniqueTokens([webview.cspSource, "'unsafe-inline'", devServerOrigin]); const scriptSrc = uniqueTokens([webview.cspSource, "'unsafe-inline'", "'unsafe-eval'", devServerOrigin]); const connectSrc = uniqueTokens(['*', 'ws:', 'wss:', 'http:', 'https:', devServerOrigin]); const imgSrc = uniqueTokens([webview.cspSource, 'data:', 'https:', devServerOrigin]); const fontSrc = uniqueTokens([webview.cspSource, 'data:', devServerOrigin]); const themeKind = getThemeKindName(vscode.window.activeColorTheme.kind); // Use VS Code CSS variables for proper theme integration // These variables are automatically provided by VS Code to webviews // // Logo geometry matches OpenChamberLogo.tsx: // edge=48, cos30=0.866, sin30=0.5, centerY=50 // top=(50, 2), left=(8.432, 26), right=(91.568, 26), center=(50, 50) // bottomLeft=(8.432, 74), bottomRight=(91.568, 74), bottom=(50, 98) // topFaceCenterY = (2 + 26 + 50 + 26) / 4 = 26 return ` OpenChamber
${!cliAvailable ? `
OpenCode CLI not found. Please install it first.
` : ''}
`; }