diff --git a/packages/components/package.json b/packages/components/package.json index 3983954a..7c64463c 100644 --- a/packages/components/package.json +++ b/packages/components/package.json @@ -18,6 +18,7 @@ "vega": "^5.22.1", "vega-embed": "^6.21.0", "vega-lite": "^5.2.0", + "vscode-uri": "^3.0.3", "yup": "^0.32.11" }, "devDependencies": { diff --git a/packages/vscode-ext/media/previewWebview.js b/packages/vscode-ext/media/previewWebview.js new file mode 100644 index 00000000..6d91b1b9 --- /dev/null +++ b/packages/vscode-ext/media/previewWebview.js @@ -0,0 +1,43 @@ +// based on https://github.com/microsoft/vscode-extension-samples/blob/main/custom-editor-sample/media/catScratch.js +(function () { + console.log("hello world"); + const vscode = acquireVsCodeApi(); + + const container = document.getElementById("root"); + + const root = ReactDOM.createRoot(container); + function updateContent(text) { + root.render( + React.createElement(squiggle_components.SquigglePlayground, { + code: text, + onCodeChange: (code) => { + vscode.postMessage({ type: "edit", text: code }); + }, + showEditor: false, + }) + ); + } + + // Handle messages sent from the extension to the webview + window.addEventListener("message", (event) => { + const message = event.data; // The json data that the extension sent + switch (message.type) { + case "update": + const text = message.text; + + // Update our webview's content + updateContent(text); + + // Then persist state information. + // This state is returned in the call to `vscode.getState` below when a webview is reloaded. + vscode.setState({ text }); + + return; + } + }); + + const state = vscode.getState(); + if (state) { + updateContent(state.text); + } +})(); diff --git a/packages/vscode-ext/media/wysiwyg.js b/packages/vscode-ext/media/wysiwygWebview.js similarity index 100% rename from packages/vscode-ext/media/wysiwyg.js rename to packages/vscode-ext/media/wysiwygWebview.js diff --git a/packages/vscode-ext/package.json b/packages/vscode-ext/package.json index bcd18474..47ac5c1f 100644 --- a/packages/vscode-ext/package.json +++ b/packages/vscode-ext/package.json @@ -18,10 +18,22 @@ "Visualization" ], "activationEvents": [ - "onCustomEditor:squiggle.wysiwyg" + "onCustomEditor:squiggle.wysiwyg", + "onCommand:squiggle.preview" ], "main": "./out/extension.js", "contributes": { + "languages": [ + { + "id": "squiggle", + "extensions": [ + ".squiggle" + ], + "aliases": [ + "Squiggle" + ] + } + ], "customEditors": [ { "viewType": "squiggle.wysiwyg", @@ -31,10 +43,41 @@ "filenamePattern": "*.squiggle" } ], - "priority": "default" + "priority": "option" } ], - "commands": [] + "commands": [ + { + "command": "squiggle.preview", + "title": "Open Preview", + "category": "Squiggle", + "when": "editorLangId == squiggle", + "icon": "$(open-preview)" + } + ], + "menus": { + "editor/title": [ + { + "command": "squiggle.preview", + "when": "editorLangId == squiggle", + "group": "navigation" + } + ], + "commandPalette": [ + { + "command": "squiggle.preview", + "when": "editorLangId == squiggle" + } + ] + }, + "keybindings": [ + { + "command": "squiggle.preview", + "key": "ctrl+k v", + "mac": "cmd+k v", + "when": "editorLangId == squiggle" + } + ] }, "scripts": { "vscode:prepublish": "yarn run compile", diff --git a/packages/vscode-ext/src/squiggleEditor.ts b/packages/vscode-ext/src/editor.ts similarity index 55% rename from packages/vscode-ext/src/squiggleEditor.ts rename to packages/vscode-ext/src/editor.ts index 3e3d9f2c..4410ba26 100644 --- a/packages/vscode-ext/src/squiggleEditor.ts +++ b/packages/vscode-ext/src/editor.ts @@ -1,14 +1,5 @@ import * as vscode from "vscode"; - -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; -} +import { getWebviewContent } from "./utils"; export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider { public static register(context: vscode.ExtensionContext): vscode.Disposable { @@ -26,8 +17,6 @@ export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider { /** * Called when our custom editor is opened. - * - * */ public async resolveCustomTextEditor( document: vscode.TextDocument, @@ -37,7 +26,12 @@ export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider { webviewPanel.webview.options = { enableScripts: true, }; - webviewPanel.webview.html = this.getHtmlForWebview(webviewPanel.webview); + webviewPanel.webview.html = getWebviewContent({ + webview: webviewPanel.webview, + script: "media/wysiwygWebview.js", + title: "Squiggle Editor", + context: this.context, + }); function updateWebview() { webviewPanel.webview.postMessage({ @@ -79,57 +73,6 @@ export class SquiggleEditorProvider implements vscode.CustomTextEditorProvider { updateWebview(); } - /** - * Get the static html used for the editor webviews. - */ - private getHtmlForWebview(webview: vscode.Webview): string { - // Local path to main script run in the webview - - const styleUri = webview.asWebviewUri( - vscode.Uri.joinPath( - this.context.extensionUri, - "media/vendor/components.css" - ) - ); - - const scriptUris = [ - // vendor files are copied over by `yarn run compile` - "media/vendor/react.js", - "media/vendor/react-dom.js", - "media/vendor/components.js", - "media/wysiwyg.js", - ].map((script) => - webview.asWebviewUri( - vscode.Uri.joinPath(this.context.extensionUri, script) - ) - ); - - // Use a nonce to whitelist which scripts can be run - const nonce = getNonce(); - - return /* html */ ` - - -
- - - - - -