diff --git a/_extensions/coatless/webr/_extension.yml b/_extensions/coatless/webr/_extension.yml index 11784ec..172077e 100644 --- a/_extensions/coatless/webr/_extension.yml +++ b/_extensions/coatless/webr/_extension.yml @@ -1,7 +1,7 @@ name: webr title: Embedded webr code cells author: James Joseph Balamuta -version: 0.1.0 +version: 0.3.6 quarto-required: ">=1.2.198" contributes: filters: diff --git a/_extensions/coatless/webr/monaco-editor-init.html b/_extensions/coatless/webr/monaco-editor-init.html new file mode 100644 index 0000000..a56edb0 --- /dev/null +++ b/_extensions/coatless/webr/monaco-editor-init.html @@ -0,0 +1,10 @@ + + \ No newline at end of file diff --git a/_extensions/coatless/webr/webr-editor.html b/_extensions/coatless/webr/webr-context-interactive.html similarity index 57% rename from _extensions/coatless/webr/webr-editor.html rename to _extensions/coatless/webr/webr-context-interactive.html index a518c19..eeb5994 100644 --- a/_extensions/coatless/webr/webr-editor.html +++ b/_extensions/coatless/webr/webr-context-interactive.html @@ -30,20 +30,6 @@ hideCursorInOverviewRuler: true // Remove cursor indictor in right hand side scroll bar }); - // Add a keydown event listener for Shift+Enter using the addCommand method - editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, function () { - // Code to run when Shift+Enter is pressed - executeCode(editor.getValue()); - }); - - // Add a keydown event listener for Ctrl+Enter to run selected code - editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, function () { - // Get the selected text from the editor - const selectedText = editor.getModel().getValueInRange(editor.getSelection()); - // Code to run when Ctrl+Enter is pressed (run selected code) - executeCode(selectedText); - }); - // Dynamically modify the height of the editor window if new lines are added. let ignoreEvent = false; const updateHeight = () => { @@ -61,6 +47,65 @@ } }; + // Helper function to check if selected text is empty + function isEmptyCodeText(selectedCodeText) { + return (selectedCodeText === null || selectedCodeText === undefined || selectedCodeText === ""); + } + + // Registry of keyboard shortcuts that should be re-added to each editor window + // when focus changes. + const addWebRKeyboardShortCutCommands = () => { + // Add a keydown event listener for Shift+Enter to run all code in cell + editor.addCommand(monaco.KeyMod.Shift | monaco.KeyCode.Enter, () => { + + // Retrieve all text inside the editor + executeCode(editor.getValue()); + }); + + // Add a keydown event listener for CMD/Ctrl+Enter to run selected code + editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter, () => { + + // Get the selected text from the editor + const selectedText = editor.getModel().getValueInRange(editor.getSelection()); + // Check if no code is selected + if (isEmptyCodeText(selectedText)) { + // Obtain the current cursor position + let currentPosition = editor.getPosition(); + // Retrieve the current line content + let currentLine = editor.getModel().getLineContent(currentPosition.lineNumber); + + // Propose a new position to move the cursor to + let newPosition = new monaco.Position(currentPosition.lineNumber + 1, 1); + + // Check if the new position is beyond the last line of the editor + if (newPosition.lineNumber > editor.getModel().getLineCount()) { + // Add a new line at the end of the editor + editor.executeEdits("addNewLine", [{ + range: new monaco.Range(newPosition.lineNumber, 1, newPosition.lineNumber, 1), + text: "\n", + forceMoveMarkers: true, + }]); + } + + // Run the entire line of code. + executeCode(currentLine); + + // Move cursor to new position + editor.setPosition(newPosition); + } else { + // Code to run when Ctrl+Enter is pressed with selected code + executeCode(selectedText); + } + }); + } + + // Register an on focus event handler for when a code cell is selected to update + // what keyboard shortcut commands should work. + // This is a workaround to fix a regression that happened with multiple + // editor windows since Monaco 0.32.0 + // https://github.com/microsoft/monaco-editor/issues/2947 + editor.onDidFocusEditorText(addWebRKeyboardShortCutCommands); + // Register an on change event for when new code is added to the editor window editor.onDidContentSizeChange(updateHeight); @@ -79,15 +124,15 @@ // Initialize webR await globalThis.webR.init(); - // Setup a webR canvas - await webR.evalRVoid("canvas(width={{WIDTH}}, height={{HEIGHT}})"); + // Setup a webR canvas by making a namespace call into the {webr} package + await webR.evalRVoid("webr::canvas(width={{WIDTH}}, height={{HEIGHT}})"); // Capture output data from evaluating the code const result = await webRCodeShelter.captureR(codeToRun, { withAutoprint: true, captureStreams: true, - captureConditions: false, - env: webR.objs.emptyEnv, + captureConditions: false//, + // env: webR.objs.emptyEnv, // maintain a global environment for webR v0.2.0 }); // Start attempting to parse the result data @@ -106,8 +151,13 @@ // Output each image stored msgs.forEach(msg => { - if (msg.type === "canvasExec") { - if (!canvas) { + // Determine if old canvas can be used or a new canvas is required. + if (msg.type === 'canvas'){ + // Add image to the current canvas + if (msg.data.event === 'canvasImage') { + canvas.getContext('2d').drawImage(msg.data.image, 0, 0); + } else if (msg.data.event === 'canvasNewPage') { + // Generate a new canvas element canvas = document.createElement("canvas"); canvas.setAttribute("width", 2 * {{WIDTH}}); canvas.setAttribute("height", 2 * {{HEIGHT}}); @@ -115,7 +165,6 @@ canvas.style.display = "block"; canvas.style.margin = "auto"; } - Function(`this.getContext("2d").${msg.data}`).bind(canvas)(); } }); diff --git a/_extensions/coatless/webr/webr-context-output.html b/_extensions/coatless/webr/webr-context-output.html new file mode 100644 index 0000000..8246489 --- /dev/null +++ b/_extensions/coatless/webr/webr-context-output.html @@ -0,0 +1,90 @@ +