UNPKG

equicord-companion

Version:

Equicord Companion is a vscode extension to test Equicord patches & webpack finds right from the comfort of your IDE

198 lines (167 loc) 8.24 kB
/** * Manages react webview panels */ import { format } from "@modules/format"; import { mkStringUri } from "@modules/util"; import { handleExtractPayload, sendAndGetData } from "@server"; import { EvaledPatch, ReporterData, WebviewMessage } from "@type/reporter"; import { extensionPath, extensionUri } from "./extension"; import { commands, Disposable, Uri, ViewColumn, WebviewPanel, window } from "vscode"; type Patch = { patch: EvaledPatch; }; type PluginName = { pluginName: string; }; type Diff = { oldModule: string; newModule: string; }; // TODO: add persistant state export class ReporterPanel { /** * Track the currently panel. Only allow a single panel to exist at a time. */ public static currentPanel: ReporterPanel | undefined; private static readonly viewType = "equicordReporter"; private readonly _panel: WebviewPanel; private readonly _extensionUri: Uri; private readonly _extensionPath: string; private _disposables: Disposable[] = []; public static createOrShow(data: ReporterData) { const column = window.activeTextEditor ? window.activeTextEditor.viewColumn : undefined; // If we already have a panel, show it. // Otherwise, create a new panel. // dont create another panel with the same data if (ReporterPanel.currentPanel && !data) { ReporterPanel.currentPanel._panel.reveal(column); } else { ReporterPanel.currentPanel = new ReporterPanel(extensionUri, column || ViewColumn.One, data); } } private constructor(extensionUri: Uri, column: ViewColumn, data: ReporterData) { this._extensionUri = extensionUri; this._extensionPath = extensionPath; // Create and show a new webview panel this._panel = window.createWebviewPanel(ReporterPanel.viewType, "Equicord Reporter", column, { // Enable javascript in the webview enableScripts: true, // And restric the webview to only loading content from our extension's `media` directory. localResourceRoots: [Uri.joinPath(extensionUri, "dist/webview")], }); // Set the webview's initial html content this._panel.webview.html = this._getHtmlForWebview(data); // Listen for when the panel is disposed // This happens when the user closes the panel or when the panel is closed programatically this._panel.onDidDispose(() => this.dispose(), null, this._disposables); // Handle messages from the webview this._panel.webview.onDidReceiveMessage(async (message: WebviewMessage) => { try { switch (message.type) { case "disable": { const { pluginName, enabled }: PluginName & { enabled: boolean; } = message.data; // DISABLE PLUGIN await sendAndGetData({ type: "disable", data: { pluginName, enabled, }, }); break; } case "jumpToPatch": { const { patch }: Patch & PluginName = message.data; // any attempt to get this to open without user interaction is a complete shitshow // just use the builtin fuzzy finder and the patch find // while there might be more than one find, the user can deal with that commands.executeCommand("workbench.action.quickOpen", `%${patch.find}`); break; } case "extract": { const { patch }: Patch = message.data; if (Number.isNaN(Number(patch.id))) { window.showErrorMessage("Module ID is not a number"); return; } try { const r = await sendAndGetData<"extract">({ type: "extract", data: { extractType: "id", idOrSearch: Number(patch.id), usePatched: null, }, }); handleExtractPayload(r); } catch (e) { window.showErrorMessage(String(e)); } break; } case "diff": { const { oldModule, newModule, id }: Diff & EvaledPatch = message.data; // we cant format code with syntax errors let sourceUri, patchedUri; try { sourceUri = mkStringUri(await format(oldModule)); patchedUri = mkStringUri(await format(newModule)); } catch (error) { window.showErrorMessage(`Failed to format code, probably a syntax error, ${error}`); sourceUri = mkStringUri(oldModule); patchedUri = mkStringUri(newModule); } commands.executeCommand("vscode.diff", sourceUri, patchedUri, `Patch Diff: ${id}`); break; } default: { window.showErrorMessage(`Unknown message type from webview, got : ${message.type}`); break; } } } catch (error) { window.showErrorMessage(String(error)); } }, null, this._disposables); } public dispose() { ReporterPanel.currentPanel = undefined; // Clean up our resources this._panel.dispose(); while (this._disposables.length) { const x = this._disposables.pop(); if (x) { x.dispose(); } } } private _getHtmlForWebview(data: ReporterData) { const scriptPathOnDisk = Uri.joinPath(this._extensionUri, "dist/webview/index.js"); const scriptUri = this._panel.webview.asWebviewUri(scriptPathOnDisk); const stylePathOnDisk = Uri.joinPath(this._extensionUri, "dist/webview/index.css"); const styleUri = this._panel.webview.asWebviewUri(stylePathOnDisk); // Use a nonce to whitelist which scripts can be run const nonce = getNonce(); return `<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"> <meta name="theme-color" content="#000000"> <title>React App</title> <script nonce="${nonce}">window.reporterData = ${JSON.stringify(data)}</script> <link rel="stylesheet" type="text/css" href="${styleUri}"> <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${this._panel.webview.cspSource} https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src ${this._panel.webview.cspSource} https:; script-src 'nonce-${nonce}';"> <base href="${this._panel.webview.asWebviewUri(Uri.joinPath(this._extensionUri, "dist/webview"))}/"> </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> <div id="root"></div> <script defer nonce="${nonce}" src="${scriptUri}"></script> </body> </html>`; } } function getNonce() { let text = ""; const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; for (let i = 0; i < 32; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }