-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from coatless-quarto-webr/upgrade-quarto-webr-e…
…xtension-0-3-6 Upgrade quarto-webr from 0.3.0 to 0.3.6
- Loading branch information
Showing
14 changed files
with
513 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<script src="https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/vs/loader.js"></script> | ||
<script type="module" id="webr-monaco-editor-init"> | ||
|
||
// Configure the Monaco Editor's loader | ||
require.config({ | ||
paths: { | ||
'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.43.0/min/vs' | ||
} | ||
}); | ||
</script> |
204 changes: 204 additions & 0 deletions
204
_extensions/coatless/webr/webr-context-interactive.html
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,204 @@ | ||
<button class="btn btn-default btn-webr" disabled type="button" id="webr-run-button-{{WEBRCOUNTER}}">Loading | ||
webR...</button> | ||
<div id="webr-editor-{{WEBRCOUNTER}}"></div> | ||
<div id="webr-code-output-{{WEBRCOUNTER}}" aria-live="assertive"> | ||
<pre style="visibility: hidden"></pre> | ||
</div> | ||
<script type="module"> | ||
// Retrieve webR code cell information | ||
const runButton = document.getElementById("webr-run-button-{{WEBRCOUNTER}}"); | ||
const outputDiv = document.getElementById("webr-code-output-{{WEBRCOUNTER}}"); | ||
const editorDiv = document.getElementById("webr-editor-{{WEBRCOUNTER}}"); | ||
|
||
// Add a light grey outline around the code editor | ||
editorDiv.style.border = "1px solid #eee"; | ||
|
||
// Load the Monaco Editor and create an instance | ||
let editor; | ||
require(['vs/editor/editor.main'], function () { | ||
editor = monaco.editor.create(editorDiv, { | ||
value: `{{WEBRCODE}}`, | ||
language: 'r', | ||
theme: 'vs-light', | ||
automaticLayout: true, // TODO: Could be problematic for slide decks | ||
scrollBeyondLastLine: false, | ||
minimap: { | ||
enabled: false | ||
}, | ||
fontSize: '17.5rem', // Bootstrap is 1 rem | ||
renderLineHighlight: "none", // Disable current line highlighting | ||
hideCursorInOverviewRuler: true // Remove cursor indictor in right hand side scroll bar | ||
}); | ||
|
||
// Dynamically modify the height of the editor window if new lines are added. | ||
let ignoreEvent = false; | ||
const updateHeight = () => { | ||
const contentHeight = editor.getContentHeight(); | ||
// We're avoiding a width change | ||
//editorDiv.style.width = `${width}px`; | ||
editorDiv.style.height = `${contentHeight}px`; | ||
try { | ||
ignoreEvent = true; | ||
|
||
// The key to resizing is this call | ||
editor.layout(); | ||
} finally { | ||
ignoreEvent = false; | ||
} | ||
}; | ||
|
||
// 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); | ||
|
||
// Manually re-update height to account for the content we inserted into the call | ||
updateHeight(); | ||
}); | ||
|
||
// Function to execute the code (accepts code as an argument) | ||
async function executeCode(codeToRun) { | ||
// Disable run button for code cell active | ||
runButton.disabled = true; | ||
|
||
// Create a canvas variable for graphics | ||
let canvas = undefined; | ||
|
||
// Initialize webR | ||
await globalThis.webR.init(); | ||
|
||
// 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, // maintain a global environment for webR v0.2.0 | ||
}); | ||
|
||
// Start attempting to parse the result data | ||
try { | ||
|
||
// Stop creating images | ||
await webR.evalRVoid("dev.off()"); | ||
|
||
// Merge output streams of STDOUT and STDErr (messages and errors are combined.) | ||
const out = result.output.filter( | ||
evt => evt.type == "stdout" || evt.type == "stderr" | ||
).map((evt) => evt.data).join("\n"); | ||
|
||
// Clean the state | ||
const msgs = await webR.flush(); | ||
|
||
// Output each image stored | ||
msgs.forEach(msg => { | ||
// 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}}); | ||
canvas.style.width = "700px"; | ||
canvas.style.display = "block"; | ||
canvas.style.margin = "auto"; | ||
} | ||
} | ||
}); | ||
|
||
// Nullify the outputDiv of content | ||
outputDiv.innerHTML = ""; | ||
|
||
// Design an output object for messages | ||
const pre = document.createElement("pre"); | ||
if (/\S/.test(out)) { | ||
// Display results as text | ||
const code = document.createElement("code"); | ||
code.innerText = out; | ||
pre.appendChild(code); | ||
} else { | ||
// If nothing is present, hide the element. | ||
pre.style.visibility = "hidden"; | ||
} | ||
outputDiv.appendChild(pre); | ||
|
||
// Place the graphics on the canvas | ||
if (canvas) { | ||
const p = document.createElement("p"); | ||
p.appendChild(canvas); | ||
outputDiv.appendChild(p); | ||
} | ||
} finally { | ||
// Clean up the remaining code | ||
webRCodeShelter.purge(); | ||
runButton.disabled = false; | ||
} | ||
} | ||
|
||
// Add a click event listener to the run button | ||
runButton.onclick = function () { | ||
executeCode(editor.getValue()); | ||
}; | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
<div id="webr-code-output-{{WEBRCOUNTER}}" aria-live="assertive"> | ||
<pre style="visibility: hidden"></pre> | ||
</div> | ||
<script type="module"> | ||
// Retrieve webR code cell information | ||
const outputDiv = document.getElementById("webr-code-output-{{WEBRCOUNTER}}"); | ||
|
||
// Function to execute the code (accepts code as an argument) | ||
async function executeOutputOnlyCode(codeToRun) { | ||
// Create a canvas variable for graphics | ||
let canvas = undefined; | ||
|
||
// Initialize webR | ||
await globalThis.webR.init(); | ||
|
||
// 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, // maintain a global environment for webR v0.2.0 | ||
}); | ||
|
||
// Start attempting to parse the result data | ||
try { | ||
|
||
// Stop creating images | ||
await webR.evalRVoid("dev.off()"); | ||
|
||
// Merge output streams of STDOUT and STDErr (messages and errors are combined.) | ||
const out = result.output.filter( | ||
evt => evt.type == "stdout" || evt.type == "stderr" | ||
).map((evt) => evt.data).join("\n"); | ||
|
||
// Clean the state | ||
const msgs = await webR.flush(); | ||
|
||
// Output each image stored | ||
msgs.forEach(msg => { | ||
// 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}}); | ||
canvas.style.width = "700px"; | ||
canvas.style.display = "block"; | ||
canvas.style.margin = "auto"; | ||
} | ||
} | ||
}); | ||
|
||
// Nullify the outputDiv of content | ||
outputDiv.innerHTML = ""; | ||
|
||
// Design an output object for messages | ||
const pre = document.createElement("pre"); | ||
if (/\S/.test(out)) { | ||
// Display results as text | ||
const code = document.createElement("code"); | ||
code.innerText = out; | ||
pre.appendChild(code); | ||
} else { | ||
// If nothing is present, hide the element. | ||
pre.style.visibility = "hidden"; | ||
} | ||
outputDiv.appendChild(pre); | ||
|
||
// Place the graphics on the canvas | ||
if (canvas) { | ||
const p = document.createElement("p"); | ||
p.appendChild(canvas); | ||
outputDiv.appendChild(p); | ||
} | ||
} finally { | ||
// Clean up the remaining code | ||
webRCodeShelter.purge(); | ||
} | ||
} | ||
|
||
// Run the code | ||
executeOutputOnlyCode(`{{WEBRCODE}}`) | ||
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<script type="module"> | ||
// Initialization WebR | ||
await globalThis.webR.init(); | ||
|
||
// Run R code without focusing on storing data. | ||
await globalThis.webR.evalRVoid(` | ||
{{WEBRCODE}} | ||
`) | ||
</script> |
Oops, something went wrong.