11import {
2- CancellationToken ,
3- commands ,
4- Disposable ,
5- TextEditor ,
6- Uri ,
7- WebviewView ,
8- WebviewViewProvider ,
9- WebviewViewResolveContext ,
10- window ,
11- } from "vscode"
12- import { isProduction } from "../is_dev"
2+ CancellationToken ,
3+ commands ,
4+ Disposable ,
5+ TextEditor ,
6+ Uri ,
7+ WebviewView ,
8+ WebviewViewProvider ,
9+ WebviewViewResolveContext ,
10+ window ,
11+ workspace ,
12+ } from "vscode" ;
13+ import { isProduction } from "../is_dev" ;
14+ import { type CallbackEvent } from "@bus/callbacks" ;
15+ import { getWorkspaceFolders } from "../common/vscodeapi" ;
1316
14- export class LineagePanel implements WebviewViewProvider , Disposable {
15- public static readonly viewType = "sqlmesh.lineage"
16-
17- private panel : WebviewView | undefined
18- private getServerUrl : ( ) => string
19- private _extensionUri : Uri
20-
21- public constructor (
22- extensionUri : Uri ,
23- getServerUrl : ( ) => string
24- ) {
25- this . _extensionUri = extensionUri
26- this . getServerUrl = getServerUrl
27- window . onDidChangeActiveTextEditor ( ( event : TextEditor | undefined ) => {
28- if ( this . panel ) {
29- this . panel . webview . html = this . getHtml ( )
30- }
31- } )
32- }
17+ export class LineagePanel implements WebviewViewProvider , Disposable {
18+ public static readonly viewType = "sqlmesh.lineage" ;
3319
34- private getPanel ( ) {
35- return this . panel
36- }
20+ private panel : WebviewView | undefined ;
21+ private getServerUrl : ( ) => string ;
22+ private _extensionUri : Uri ;
3723
38- public resolveWebviewView (
39- webviewView : WebviewView ,
40- _context : WebviewViewResolveContext ,
41- _token : CancellationToken ,
42- ) {
43- if ( this . panel ) {
44- webviewView = this . panel
45- }
46- this . panel = webviewView
24+ public constructor ( extensionUri : Uri , getServerUrl : ( ) => string ) {
25+ this . _extensionUri = extensionUri ;
26+ this . getServerUrl = getServerUrl ;
27+ window . onDidChangeActiveTextEditor ( ( event : TextEditor | undefined ) => {
28+ if ( this . panel ) {
29+ const { externalUrl, externalAuthority } =
30+ this . externalUrlAndAutority ( ) ;
31+ this . panel . webview . html = this . getHtml ( externalUrl , externalAuthority ) ;
32+ }
33+ } ) ;
34+ }
35+
36+ private getPanel ( ) {
37+ return this . panel ;
38+ }
39+
40+ public resolveWebviewView (
41+ webviewView : WebviewView ,
42+ _context : WebviewViewResolveContext ,
43+ _token : CancellationToken
44+ ) {
45+ if ( this . panel ) {
46+ webviewView = this . panel ;
47+ }
48+ this . panel = webviewView ;
4749
48- webviewView . webview . options = {
49- // Allow scripts in the webview
50- enableScripts : true ,
51- localResourceRoots : [
52- this . _extensionUri
53- ]
54- }
50+ webviewView . webview . options = {
51+ // Allow scripts in the webview
52+ enableScripts : true ,
53+ localResourceRoots : [ this . _extensionUri ] ,
54+ } ;
5555
5656 // Set content options for external URL access
57- const externalUrl = this . getServerUrl ( ) ;
58- const externalAuthority = new URL ( externalUrl ) . origin ;
59-
60- webviewView . webview . html = this . getHtml ( )
61- }
57+ // Set up message listener for events from the iframe
58+ webviewView . webview . onDidReceiveMessage (
59+ async ( message ) => {
60+ console . log ( "message received" , message ) ;
61+ if ( message && message . key ) {
62+ if ( message . key === "vscode_callback" ) {
63+ const payload : CallbackEvent = message . payload ;
64+ // Handle callback events from the iframe
65+ switch ( payload . key ) {
66+ case "openFile" :
67+ console . log ( "opening file " , payload . payload . path ) ;
68+ const workspaceFolders = getWorkspaceFolders ( ) ;
69+ if ( workspaceFolders . length != 1 ) {
70+ throw new Error ( "Only one workspace folder is supported" ) ;
71+ }
72+ const workspaceFolder = workspaceFolders [ 0 ] ;
73+ const fullPath = Uri . joinPath (
74+ workspaceFolder . uri ,
75+ payload . payload . path
76+ ) ;
77+ console . log ( "fullPath" , fullPath ) ;
78+ const document = await workspace . openTextDocument ( fullPath ) ;
79+ await window . showTextDocument ( document ) ;
80+ break ;
81+ case "formatProject" :
82+ console . log ( "formatProject" , message . payload ) ;
83+ break ;
84+ default :
85+ console . log ( `Unhandled message type in key: ${ message . key } ` ) ;
86+ }
87+ } else {
88+ console . log ( "Unhandled message type: " , message ) ;
89+ }
90+ }
91+ } ,
92+ undefined ,
93+ [ ]
94+ ) ;
95+ const { externalUrl, externalAuthority } = this . externalUrlAndAutority ( ) ;
96+ webviewView . webview . html = this . getHtml ( externalUrl , externalAuthority ) ;
97+ }
6298
63- getHtml ( ) {
64-
65- const isProd = isProduction ( )
66-
67- const externalUrl = ! isProd ? "http://localhost:5173/lineage" : this . getServerUrl ( ) + "/lineage"
99+ externalUrlAndAutority ( ) : { externalUrl : string ; externalAuthority : string } {
100+ const isProd = isProduction ( ) ;
101+ const externalUrl = ! isProd
102+ ? "http://localhost:5173/lineage"
103+ : this . getServerUrl ( ) + "/lineage" ;
68104 const externalAuthority = new URL ( externalUrl ) . origin ;
69-
105+ return { externalUrl, externalAuthority } ;
106+ }
107+
108+ getHtml ( externalUrl : string , externalAuthority : string ) {
70109 // The CSP is too restrictive - it only allows frame-src but no other resources
71110 // Adding connect-src for API calls, img-src for images, and style-src for CSS
72111 return `
73112<!DOCTYPE html>
74113<html>
75114<head>
115+ <script>
116+ // Listen for messages from the iframe and forward them to the extension
117+ window.addEventListener('message', (event) => {
118+ // Forward messages from the iframe to the extension
119+ const message = event.data;
120+ if (message && message.key) {
121+ // Post the message to the extension host
122+ vscode.postMessage(message);
123+ }
124+ });
125+
126+ // Define a function to handle events from the extension and send them to the iframe
127+ const vscode = acquireVsCodeApi();
128+ window.addEventListener('message', (event) => {
129+ // Check if the message is from the extension
130+ if (event.source === window && event.data) {
131+ // Forward the message to the iframe
132+ const iframe = document.querySelector('iframe');
133+ if (iframe && iframe.contentWindow) {
134+ iframe.contentWindow.postMessage(event.data, '*');
135+ }
136+ }
137+ });
138+ </script>
76139 <meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src ${ externalAuthority } ; connect-src ${ externalAuthority } ; img-src ${ externalAuthority } data:; script-src 'unsafe-inline' ${ externalAuthority } ; style-src 'unsafe-inline' ${ externalAuthority } ;">
77140</head>
78141<body>
79142 <iframe src="${ externalUrl } " style="width:100%; height:100vh;" frameborder="0" allow="clipboard-read; clipboard-write"></iframe>
80143</body>
81- </html> `
144+ </html> ` ;
82145 }
83146
84-
85- dispose ( ) {
86- // WebviewView doesn't have a dispose method
87- // We can clear references
88- this . panel = undefined ;
89- }
90- }
147+ dispose ( ) {
148+ // WebviewView doesn't have a dispose method
149+ // We can clear references
150+ this . panel = undefined ;
151+ }
152+ }
0 commit comments