From f71cd7fa8b604594245d66afde0b859625f71d82 Mon Sep 17 00:00:00 2001 From: James J Balamuta Date: Mon, 25 Mar 2024 08:00:44 -0700 Subject: [PATCH] Switch webR from v0.2.2 to use v0.3.1 (#169) * Switch webR from v0.2.2 to use v0.3.1 * Fully switch to using webR v0.3.x's new `.images` --- _extensions/webr/_extension.yml | 2 +- _extensions/webr/qwebr-compute-engine.js | 77 ++++++++++++------------ _extensions/webr/webr-serviceworker.js | 2 +- _extensions/webr/webr-worker.js | 2 +- _extensions/webr/webr.lua | 2 +- docs/qwebr-release-notes.qmd | 3 +- 6 files changed, 45 insertions(+), 43 deletions(-) diff --git a/_extensions/webr/_extension.yml b/_extensions/webr/_extension.yml index f36e2839..120ca0c7 100644 --- a/_extensions/webr/_extension.yml +++ b/_extensions/webr/_extension.yml @@ -1,7 +1,7 @@ name: webr title: Embedded webr code cells author: James Joseph Balamuta -version: 0.4.1-dev.1 +version: 0.4.1-dev.2 quarto-required: ">=1.2.198" contributes: filters: diff --git a/_extensions/webr/qwebr-compute-engine.js b/_extensions/webr/qwebr-compute-engine.js index 84540b0f..6c995500 100644 --- a/_extensions/webr/qwebr-compute-engine.js +++ b/_extensions/webr/qwebr-compute-engine.js @@ -64,10 +64,8 @@ globalThis.qwebrComputeEngine = async function( // 1. We setup a canvas device to write to by making a namespace call into the {webr} package // 2. We use values inside of the options array to set the figure size. // 3. We capture the output stream information (STDOUT and STERR) - // 4. While parsing the results, we disable image creation. - - // Create a canvas variable for graphics - let canvas = undefined; + // 4. We disable the current device's image creation. + // 5. Piece-wise parse the results into the different output areas // Create a pager variable for help/file contents let pager = []; @@ -92,9 +90,14 @@ globalThis.qwebrComputeEngine = async function( await mainWebR.init(); // Setup a webR canvas by making a namespace call into the {webr} package - await mainWebR.evalRVoid(`webr::canvas(width=${fig_width}, height=${fig_height})`); - - const result = await mainWebRCodeShelter.captureR(codeToRun, { + // Evaluate the R code + // Remove the active canvas silently + const result = await mainWebRCodeShelter.captureR( + `webr::canvas(width=${fig_width}, height=${fig_height}, capture = TRUE) + .webr_cvs_id <- dev.cur() + ${codeToRun} + invisible(dev.off(.webr_cvs_id)) + `, { withAutoprint: true, captureStreams: true, captureConditions: false//, @@ -105,9 +108,6 @@ globalThis.qwebrComputeEngine = async function( // Start attempting to parse the result data processResultOutput:try { - - // Stop creating images - await mainWebR.evalRVoid("dev.off()"); // Avoid running through output processing if (options.results === "hide" || options.output === "false") { @@ -130,34 +130,11 @@ globalThis.qwebrComputeEngine = async function( // Clean the state - // We're now able to process both graphics and pager events. + // We're now able to process pager events. // As a result, we cannot maintain a true 1-to-1 output order // without individually feeding each line const msgs = await mainWebR.flush(); - // Output each image event 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 * fig_width); - canvas.setAttribute("height", 2 * fig_height); - canvas.style.width = options["out-width"] ? options["out-width"] : `${fig_width}px`; - if (options["out-height"]) { - canvas.style.height = options["out-height"]; - } - canvas.style.display = "block"; - canvas.style.margin = "auto"; - } - } - }); - // Use `map` to process the filtered "pager" events asynchronously const pager = await Promise.all( msgs.filter(msg => msg.type === 'pager').map( @@ -185,13 +162,37 @@ globalThis.qwebrComputeEngine = async function( elements.outputCodeDiv.appendChild(pre); - // Place the graphics on the canvas - if (canvas) { + // Determine if we have graphs to display + if (result.images.length > 0) { // Create figure element const figureElement = document.createElement('figure'); - // Append canvas to figure - figureElement.appendChild(canvas); + // Place each rendered graphic onto a canvas element + result.images.forEach((img) => { + // Construct canvas for object + const canvas = document.createElement("canvas"); + + // Set canvas size to image + canvas.width = img.width; + canvas.height = img.height; + + // Apply output truncations + canvas.style.width = options["out-width"] ? options["out-width"] : `${fig_width}px`; + if (options["out-height"]) { + canvas.style.height = options["out-height"]; + } + + // Apply styling + canvas.style.display = "block"; + canvas.style.margin = "auto"; + + // Draw image onto Canvas + const ctx = canvas.getContext("2d"); + ctx.drawImage(img, 0, 0, img.width, img.height); + + // Append canvas to figure output area + figureElement.appendChild(canvas); + }); if (options['fig-cap']) { // Create figcaption element diff --git a/_extensions/webr/webr-serviceworker.js b/_extensions/webr/webr-serviceworker.js index 4022c54a..ab830893 100644 --- a/_extensions/webr/webr-serviceworker.js +++ b/_extensions/webr/webr-serviceworker.js @@ -1 +1 @@ -importScripts('https://webr.r-wasm.org/v0.2.2/webr-serviceworker.js'); +importScripts('https://webr.r-wasm.org/v0.3.1/webr-serviceworker.js'); diff --git a/_extensions/webr/webr-worker.js b/_extensions/webr/webr-worker.js index 8aa663ad..a7b3d5c4 100644 --- a/_extensions/webr/webr-worker.js +++ b/_extensions/webr/webr-worker.js @@ -1 +1 @@ -importScripts('https://webr.r-wasm.org/v0.2.2/webr-worker.js'); +importScripts('https://webr.r-wasm.org/v0.3.1/webr-worker.js'); diff --git a/_extensions/webr/webr.lua b/_extensions/webr/webr.lua index cf8f99d8..bd81f66d 100644 --- a/_extensions/webr/webr.lua +++ b/_extensions/webr/webr.lua @@ -12,7 +12,7 @@ local hasDoneWebRSetup = false -- https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html -- Define a base compatibile version -local baseVersionWebR = "0.2.2" +local baseVersionWebR = "0.3.1" -- Define where WebR can be found local baseUrl = "https://webr.r-wasm.org/v".. baseVersionWebR .."/" diff --git a/docs/qwebr-release-notes.qmd b/docs/qwebr-release-notes.qmd index 0fa7f090..cb01994a 100644 --- a/docs/qwebr-release-notes.qmd +++ b/docs/qwebr-release-notes.qmd @@ -9,10 +9,11 @@ format: --- -# 0.4.x-dev.1: ???????????? (??-??-??) [DEV] +# 0.4.1-dev.2: ???????????? (??-??-??) [DEV] ## Features +- Upgraded the embedded version of webR to v0.3.1. ([#165](https://github.com/coatless/quarto-webr/issues/165)) - `read-only` is a new code cell option that prevents changes to code inside of an `interactive` context. ## Bug fixes