Skip to content

Commit 7c605ee

Browse files
authored
Set webR options from within Quarto Document YAML (#13)
* Add old code cell type to check against * Add more documentation to the README file * Bump embedded workers to the 0.1.1 version * Re-write the initialization portion of WebR to allow for user-specified variables in the quarto document's meta field * Update the template file with new YAML options * Bump the extension * Fix code cell specification * Better description * Remove log diagnostics
1 parent 288faab commit 7c605ee

8 files changed

+434
-86
lines changed

README.md

+56-12
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ filters:
3232
- webr
3333
```
3434
35-
Then, place the code for `webr` in a code block marked with `{webr}`
35+
Then, place the R code for `webR` in a code block marked with `{webr-r}`
3636

3737
````markdown
3838
---
@@ -52,33 +52,75 @@ summary(fit)
5252
````
5353

5454

55-
When `quarto render` or `quarto preview` is called, the filter will execute under the `jupyter` compute engine if `engine: knitr` is not specified.
55+
When `quarto render` or `quarto preview` is called, the filter will execute under `engine: knitr`.
5656
During the execution, the filter adds two files to the working directory: `webr-worker.js` and `webr-serviceworker.js`. These files allow for the
57-
webR session to be started and must be present with the rendered output.
57+
`webR` session to be started and must be present with the rendered output.
58+
59+
**Note:** If `engine: knitr` is not specified, then the `jupyter` compute engine will be used by default.
5860

5961
### Packages
6062

61-
By default, the `quarto-webr` extension avoids loading or requesting additional packages. Additional packages can be added by including:
63+
By default, the `quarto-webr` extension avoids loading or requesting additional packages. Additional packages can be added
64+
when the document is first opened or on per-code cell basis. You can view what packages are available by either executing
65+
the following R code (either with WebR or just R):
6266

6367
```r
64-
webr::install("package")
68+
available.packages(repos="https://repo.r-wasm.org/", type="source")
6569
```
6670

67-
For example, to install `ggplot2`, you would need to use:
71+
Or, by navigating to the WebR repository:
6872

69-
```r
70-
webr::install("ggplot2")
73+
<https://github.com/r-wasm/webr-repo/blob/main/repo-packages>
74+
75+
76+
#### Install on document open
77+
78+
Add to the document header YAML the `packages` key under `webr` with each package listed using an array, e.g.
79+
80+
```yaml
81+
---
82+
webr:
83+
packages: ['ggplot2', 'dplyr']
84+
---
7185
```
7286

73-
You can view what packages are available by either executing the following R code (either with WebR or just R):
87+
#### Install on an as needed basis
88+
89+
Packages may also be installed inside of a code cell through the built-in [`webr::install()` function](https://docs.r-wasm.org/webr/latest/packages.html#example-installing-the-matrix-package). For example, to install `ggplot2`, you would need to use:
7490

7591
```r
76-
available.packages(repos="https://repo.r-wasm.org/", type="source")
92+
webr::install("ggplot2")
7793
```
7894

79-
Or, by navigating to the WebR repository:
95+
### Customizing webR from the Quarto Extension
8096

81-
<https://github.com/r-wasm/webr-repo/blob/main/repo-packages>
97+
The `quarto-webr` extension supports specifying the following `WebROptions` options:
98+
99+
- `home-dir`: The WebAssembly user’s home directory and initial working directory ([`Documentation`](https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html#homedir)). Default: `'/home/web_user'`.
100+
- `base-url`: The base URL used for downloading R WebAssembly binaries. ([`Documentation`](https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html#baseurl)). Default: `'https://webr.r-wasm.org/[version]/'`.
101+
- `service-worker-url`: The base URL from where to load JavaScript worker scripts when loading webR with the ServiceWorker communication channel mode ([`Documentation`](https://docs.r-wasm.org/webr/latest/api/js/interfaces/WebR.WebROptions.html#serviceworkerurl)). Default: `''`.
102+
103+
The extension also has native options for:
104+
105+
- `show-startup-message`: Display in the document header the state of WebR initialization. Default: `true`
106+
- `show-header-message`: Display in the document header whether COOP and COEP headers are in use for faster page loads. Default: `false`
107+
108+
For these options to be active, they must be placed underneath the `webr` entry in the documentation header, e.g.
109+
110+
```markdown
111+
---
112+
title: WebR in Quarto HTML Documents
113+
format: html
114+
engine: knitr
115+
webr:
116+
show-startup-message: false
117+
show-header-message: false
118+
home-dir: '/home/r-user/'
119+
packages: ['ggplot2', 'dplyr']
120+
filters:
121+
- webr
122+
---
123+
```
82124

83125
## Known Hiccups
84126

@@ -96,6 +138,8 @@ If `webr-worker.js` or `webr-serviceworker.js` are not found when the document l
96138
└── webr-worker.js
97139
```
98140

141+
Still having trouble? Try specifying where the worker files are located using the `service-worker-url` option in the document's YAML header.
142+
99143
### Directly accessing rendered HTML
100144

101145
When using `quarto preview` or `quarto render`, the rendered HTML document is being shown by mimicking a server running under `https://localhost/`. Usually, everything works in this context assuming the above directory structure is followed. However, if you **directly** open the rendered HTML document, e.g. `demo-quarto-web.html`, inside of a Web Browser, then the required WebR components cannot be loaded for security reasons. You can read a bit more about the problem in this [StackOverflow answer](https://stackoverflow.com/questions/6811398/html5-web-workers-work-in-firefox-4-but-not-in-chrome-12-0-742-122/6823683#6823683).

_extensions/webr/_extension.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: webr
22
title: Embedded webr code cells
33
author: James Joseph Balamuta
4-
version: 0.0.4
4+
version: 0.1.0
55
quarto-required: ">=1.2.198"
66
contributes:
77
filters:

_extensions/webr/template.qmd

+10-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
title: "WebR-enabled code cell"
33
format: html
44
engine: knitr
5+
#webr:
6+
# show-startup-message: false # Display status of webR initialization
7+
# show-header-message: false # Check to see if COOP&COEP headers are set for speed.
8+
# packages: ['ggplot2', 'dplyr'] # Pre-install dependencies
9+
# home-dir: "/home/rstudio" # Customize where the working directory is
10+
# base-url: '' # Base URL used for downloading R WebAssembly binaries
11+
# service-worker-url: '' # URL from where to load JavaScript worker scripts when loading webR with the ServiceWorker communication channel.
512
filters:
613
- webr
714
---
@@ -10,15 +17,15 @@ filters:
1017

1118
This is a webr-enabled code cell in a Quarto HTML document.
1219

13-
```{webr}
20+
```{webr-r}
1421
1 + 1
1522
```
1623

17-
```{webr}
24+
```{webr-r}
1825
fit = lm(mpg ~ am, data = mtcars)
1926
summary(fit)
2027
```
2128

22-
```{webr}
29+
```{webr-r}
2330
plot(pressure)
2431
```

_extensions/webr/webr-init.html

+84-4
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,97 @@
1313
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/6.65.7/mode/r/r.js"></script>
1414
<script type="module">
1515

16+
// Start a timer
17+
const initializeWebRTimerStart = performance.now();
1618

17-
import { WebR } from "https://webr.r-wasm.org/v0.1.0/webr.mjs";
18-
19+
// Determine if we need to install R packages
20+
var installRPackagesList = [{{INSTALLRPACKAGESLIST}}];
21+
// Check to see if we have an empty array, if we do set to skip the installation.
22+
var setupRPackages = !(installRPackagesList.indexOf("") !== -1);
23+
24+
// Display a startup message?
25+
var showStartupMessage = {{SHOWSTARTUPMESSAGE}};
26+
var showHeaderMessage = {{SHOWHEADERMESSAGE}};
27+
if (showStartupMessage) {
28+
29+
// Create the outermost div element
30+
var quartoTitleMeta = document.createElement("div");
31+
quartoTitleMeta.classList.add("quarto-title-meta");
32+
33+
// Create the first inner div element
34+
var firstInnerDiv = document.createElement("div");
35+
36+
// Create the second inner div element with "WebR Status" heading
37+
// and contents
38+
var secondInnerDiv = document.createElement("div");
39+
secondInnerDiv.classList.add("quarto-title-meta-heading");
40+
secondInnerDiv.innerText = "WebR Status";
41+
42+
// Add another inner div
43+
var secondInnerDivContents = document.createElement("div");
44+
secondInnerDivContents.classList.add("quarto-title-meta-contents");
45+
46+
// Describe the WebR state
47+
var startupMessageWebR = document.createElement("p");
48+
startupMessageWebR.innerText = "🟡 Loading...";
49+
startupMessageWebR.setAttribute("id", "startup");
50+
51+
// Put everything together
52+
secondInnerDivContents.appendChild(startupMessageWebR);
53+
54+
// Add a status indicator for COOP and COEP Headers
55+
if (showHeaderMessage) {
56+
var crossOriginMessage = document.createElement("p");
57+
crossOriginMessage.innerText = `${crossOriginIsolated ? '🟢' : '🟡'} COOP & COEP Headers`;
58+
crossOriginMessage.setAttribute("id", "coop-coep-header");
59+
secondInnerDivContents.appendChild(crossOriginMessage);
60+
}
61+
62+
firstInnerDiv.appendChild(secondInnerDiv);
63+
firstInnerDiv.appendChild(secondInnerDivContents);
64+
quartoTitleMeta.appendChild(firstInnerDiv);
65+
66+
// Add new element as last child in header element
67+
var header = document.getElementsByTagName("header")[0];
68+
header.appendChild(quartoTitleMeta);
69+
}
70+
71+
// Retrieve the webr.mjs
72+
import { WebR } from "https://webr.r-wasm.org/v0.1.1/webr.mjs";
1973

20-
// Maybe return the `/` if needed in SW_URL?
21-
globalThis.webR = new WebR();
74+
// Populate WebR options with defaults or new values based on
75+
// webr meta
76+
globalThis.webR = new WebR({
77+
"baseURL": "{{BASEURL}}",
78+
"serviceWorkerUrl": "{{SERVICEWORKERURL}}",
79+
"homedir": "{{HOMEDIR}}"
80+
});
2281

82+
// Initialization WebR
2383
await globalThis.webR.init();
84+
85+
// Setup a shelter
2486
globalThis.webRCodeShelter = await new globalThis.webR.Shelter();
87+
88+
// Installing Packages
89+
if (showStartupMessage && setupRPackages) {
90+
// If initialized, but we have packages to install switch status
91+
startupMessageWebR.innerText = "🟡 Installing package dependencies..."
92+
// Install packages
93+
await globalThis.webR.installPackages(installRPackagesList)
94+
}
95+
96+
// Switch to allowing code to be executed
2597
document.querySelectorAll(".btn-webr").forEach((btn) => {
2698
btn.innerText = "Run code";
2799
btn.disabled = false;
28100
});
101+
102+
// Stop timer
103+
const initializeWebRTimerEnd = performance.now();
104+
105+
if (showStartupMessage) {
106+
// If initialized, switch to a green light
107+
startupMessageWebR.innerText = "🟢 Ready!"
108+
}
29109
</script>
+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
importScripts('https://webr.r-wasm.org/v0.1.0/webr-serviceworker.js');
1+
importScripts('https://webr.r-wasm.org/v0.1.1/webr-serviceworker.js');

_extensions/webr/webr-worker.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
importScripts('https://webr.r-wasm.org/v0.1.0/webr-worker.js');
1+
importScripts('https://webr.r-wasm.org/v0.1.1/webr-worker.js');

0 commit comments

Comments
 (0)