(graph: WorkflowGraph, loadingOptions?: LoadingOptions)
| 599 | } |
| 600 | |
| 601 | private getHtml(graph: WorkflowGraph, loadingOptions?: LoadingOptions): string { |
| 602 | const webview = this.panel!.webview; |
| 603 | |
| 604 | // Generate nonce for CSP |
| 605 | const nonce = this.getNonce(); |
| 606 | |
| 607 | // Get URIs for static files |
| 608 | const scriptUri = webview.asWebviewUri( |
| 609 | vscode.Uri.joinPath(this.context.extensionUri, 'out', 'webview-client', 'main.js') |
| 610 | ); |
| 611 | const d3Uri = webview.asWebviewUri( |
| 612 | vscode.Uri.joinPath(this.context.extensionUri, 'media', 'webview', 'd3.v7.min.js') |
| 613 | ); |
| 614 | const fontsUri = webview.asWebviewUri( |
| 615 | vscode.Uri.joinPath(this.context.extensionUri, 'media', 'webview', 'fonts') |
| 616 | ); |
| 617 | |
| 618 | // Stringify graph data safely |
| 619 | let graphJson: string; |
| 620 | try { |
| 621 | graphJson = JSON.stringify(graph); |
| 622 | } catch (error) { |
| 623 | console.error('Failed to stringify graph:', error); |
| 624 | graphJson = '{"nodes":[],"edges":[],"llms_detected":[],"workflows":[]}'; |
| 625 | } |
| 626 | |
| 627 | // Read and process CSS with font URI replacement |
| 628 | const cssPath = path.join(this.context.extensionPath, 'media', 'webview', 'styles.css'); |
| 629 | let css = fs.readFileSync(cssPath, 'utf8'); |
| 630 | css = css.replace(/\{\{fontsUri\}\}/g, fontsUri.toString()); |
| 631 | |
| 632 | // Read static HTML template |
| 633 | const htmlPath = path.join(this.context.extensionPath, 'media', 'webview', 'index.html'); |
| 634 | let html = fs.readFileSync(htmlPath, 'utf8'); |
| 635 | |
| 636 | // Replace placeholders |
| 637 | html = html.replace(/\{\{nonce\}\}/g, nonce); |
| 638 | html = html.replace(/\{\{cspSource\}\}/g, webview.cspSource); |
| 639 | html = html.replace(/\{\{d3Uri\}\}/g, d3Uri.toString()); |
| 640 | // Inline the processed CSS (with font URIs resolved) |
| 641 | html = html.replace(/\{\{inlineStyles\}\}/g, css); |
| 642 | |
| 643 | // Replace script tag with graph data injection and bundled script |
| 644 | const loadingState = loadingOptions?.loading ? 'true' : 'false'; |
| 645 | const scriptReplacement = ` |
| 646 | <script nonce="${nonce}"> |
| 647 | window.__GRAPH_DATA__ = ${graphJson}; |
| 648 | window.__LOADING_STATE__ = ${loadingState}; |
| 649 | </script> |
| 650 | <script nonce="${nonce}" src="${scriptUri}"></script> |
| 651 | </body>`; |
| 652 | |
| 653 | html = html.replace('</body>', scriptReplacement); |
| 654 | |
| 655 | return html; |
| 656 | } |
| 657 | |
| 658 | private getNonce(): string { |
no test coverage detected