// Copyright © SixtyFPS GmbH // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-Royalty-free-2.0 OR LicenseRef-Slint-Software-3.0 import { Uri } from "vscode"; import * as vscode from "vscode"; import type { BaseLanguageClient } from "vscode-languageclient"; let previewPanel: vscode.WebviewPanel | null = null; let to_lsp_queue: object[] = []; let language_client: BaseLanguageClient | null = null; function use_wasm_preview(): boolean { return vscode.workspace .getConfiguration("slint") .get("preview.providedByEditor", false); } export function panel(): vscode.WebviewPanel | null { return previewPanel; } export function update_configuration() { if (language_client) { send_to_lsp({ PreviewTypeChanged: { is_external: previewPanel !== null || use_wasm_preview(), }, }); } } /// Initialize the callback on the client to make the web preview work export function initClientForPreview( context: vscode.ExtensionContext, client: BaseLanguageClient | null, ) { language_client = client; if (client) { update_configuration(); client.onNotification("slint/lsp_to_preview", async (message: any) => { if ("ShowPreview" in message) { if (open_preview(context)) { return; } } await previewPanel?.webview.postMessage({ command: "slint/lsp_to_preview", params: message, }); }); // Send messages that got queued while LS was down... for (const m of to_lsp_queue) { send_to_lsp(m); } to_lsp_queue = []; } } function send_to_lsp(message: any): boolean { if (language_client) { language_client.sendNotification("slint/preview_to_lsp", message); } else { to_lsp_queue.push(message); } return language_client !== null; } function open_preview(context: vscode.ExtensionContext): boolean { if (previewPanel !== null) { return false; } // Create and show a new webview const panel = vscode.window.createWebviewPanel( "slint-preview", "Slint Preview", vscode.ViewColumn.Beside, { enableScripts: true, retainContextWhenHidden: true }, ); previewPanel = initPreviewPanel(context, panel); return true; } function getPreviewHtml( slint_wasm_preview_url: Uri, default_style: string, ): string { const experimental = typeof process !== "undefined" && process.env.hasOwnProperty("SLINT_ENABLE_EXPERIMENTAL_FEATURES"); const result = ` Slint Preview > `; return result; } export class PreviewSerializer implements vscode.WebviewPanelSerializer { context: vscode.ExtensionContext; constructor(context: vscode.ExtensionContext) { this.context = context; } deserializeWebviewPanel(webviewPanel: vscode.WebviewPanel, _state: any) { previewPanel = initPreviewPanel(this.context, webviewPanel); //// How can we load this state? We can not query the necessary data... return Promise.resolve(); } } function map_url(webview: vscode.Webview, url_: string) { let result: string | undefined; try { const url = Uri.parse(url_, false); if (vscode.workspace.getWorkspaceFolder(url)) { result = previewPanel?.webview.asWebviewUri(url)?.toString(); } } catch (_) { /* nothing to handle */ } webview.postMessage({ command: "map_response", original: url_, mapped: result, }); } function initPreviewPanel( context: vscode.ExtensionContext, panel: vscode.WebviewPanel, ): vscode.WebviewPanel { panel.iconPath = Uri.joinPath(context.extensionUri, "slint-file-icon.svg"); // we will get a preview_ready when the html is loaded and message are ready to be sent panel.webview.onDidReceiveMessage( (message) => { switch (message.command) { case "map_url": map_url(panel.webview, message.url); return; case "preview_ready": send_to_lsp({ RequestState: { unused: true } }); return; case "slint/preview_to_lsp": send_to_lsp(message.params); return; } }, undefined, context.subscriptions, ); const lsp_wasm_url = Uri.joinPath( context.extensionUri, "out/slint_lsp_wasm.js", ); const default_style = vscode.workspace .getConfiguration("slint") .get("preview.style", ""); panel.webview.html = getPreviewHtml( panel.webview.asWebviewUri(lsp_wasm_url), default_style, ); panel.onDidDispose( () => { previewPanel = null; update_configuration(); }, undefined, context.subscriptions, ); return panel; }