diff --git a/DESCRIPTION b/DESCRIPTION
index 20db12b..657d0bd 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -17,7 +17,10 @@ Imports:
roxygen2
Suggests:
rstudioapi,
- testthat (>= 3.0.0)
+ testthat (>= 3.0.0),
+ jsonlite,
+ base64enc,
+ desc
Encoding: UTF-8
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.3.2
diff --git a/NAMESPACE b/NAMESPACE
index 47fb5c7..c7cf901 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -1,5 +1,8 @@
# Generated by roxygen2: do not edit by hand
S3method(roxygen2::roxy_tag_parse,roxy_tag_examplesTempdir)
+S3method(roxygen2::roxy_tag_parse,roxy_tag_examplesWebR)
S3method(roxygen2::roxy_tag_rd,roxy_tag_examplesTempdir)
+S3method(roxygen2::roxy_tag_rd,roxy_tag_examplesWebR)
export(insert_examplesTempdir_template)
+export(insert_examplesWebR_template)
diff --git a/R/examplesWebR.R b/R/examplesWebR.R
new file mode 100644
index 0000000..e0dfc64
--- /dev/null
+++ b/R/examplesWebR.R
@@ -0,0 +1,791 @@
+#' Custom `@examplesWebR` tag.
+#'
+#' When roxygen2 processes your documentation, the `@examplesWebR` tag generates
+#' both an example section and webR integration. Specifically, the
+#' example code is split into a regular `@examples` section and a WebR section.
+#' The webR section contains a clickable link or embedding an interactive iframe
+#' that interacts with the webR REPL by automatically encoding the code
+#' for sharing.
+#'
+#' @section Implementation:
+#'
+#' When the `@examplesWebR` tag is processed, it creates:
+#'
+#' 1. Regular `@examples` section with the code
+#' 2. A `"WebR"` section containing format-appropriate webR integration link
+#'
+#' The webR URL format is:
+#'
+#' ```
+#' https://webr.r-wasm.org/{version}/?mode={mode}&channel={channel}#code={encoded_code}&ju{a}
+#' ```
+#'
+#' where the code is JSON-encoded and uncompressed.
+#'
+#' @section Generated Structure:
+#'
+#' The generated RD file will have the structure:
+#'
+#' ```
+#' \section{WebR}{
+#' \ifelse{html}{
+#' \out{
+#' \U0001F310 View in webR REPL
+#' }
+#' }{
+#' \ifelse{latex}{
+#' \url{webR_URL}
+#' }{
+#' Interactive webR content not available for this output format.
+#' }
+#' }
+#' }
+#' \examples{
+#' # Your R code here
+#' plot(1:5, 1:5)
+#' }
+#' ```
+#'
+#' where `webR_URL` is the link to the webR REPL with the encoded code.
+#'
+#' @section Output Format Support:
+#'
+#' The webR integration adapts to different documentation formats:
+#'
+#' - **HTML**: Interactive buttons/iframes with full webR functionality
+#' - **LaTeX/PDF**: Plain URL link to webR session
+#' - **Other formats**: Informational message about limited support
+#'
+#' @section Parameters:
+#'
+#' The tag supports optional parameters (these override global `DESCRIPTION` config):
+#'
+#' - `@examplesWebR`: Default behavior (link in WebR section)
+#' - `@examplesWebR embed`: Embed iframe instead of link
+#' - `@examplesWebR embed=false` - Explicitly disable embedding (override global config)
+#' - `@examplesWebR version=v0.5.4`: Specify webR version greater than or equal to 0.5.4 (default: `latest`)
+#' - `@examplesWebR height=500`: Set iframe height in pixels (default: `400`)
+#' - `@examplesWebR autorun` - Enable autorun on webR session open
+#' - `@examplesWebR autorun=false` - Explicitly disable autorun (override global config)
+#' - `@examplesWebR mode=editor-plot` - Configure embedded webR interface (editor, plot, terminal, files) (default: `"editor-plot-terminal"`)
+#' - `@examplesWebR channel=Automatic` - Set webR communication channel (default: `"Automatic`)
+#'
+#' **Version Requirements**: The version parameter must be either `"latest"` or a
+#' version string `v0.5.4` or higher. Earlier versions are not supported
+#' as the embedding feature is new.
+#'
+#' **Mode Options**: Valid mode components are `editor`, `plot`, `terminal`, and `files`.
+#' Combine multiple components with hyphens (e.g., `editor-plot-terminal`).
+#'
+#' @section Global Configuration:
+#'
+#' You can set global defaults for tags in your package's `DESCRIPTION` file:
+#'
+#' ```
+#' Config/rocleteer/webr-embed: false
+#' Config/rocleteer/webr-height: 500
+#' Config/rocleteer/webr-version: v0.5.4
+#' Config/rocleteer/webr-autorun: true
+#' Config/rocleteer/webr-mode: editor-plot
+#' Config/rocleteer/webr-channel: Automatic
+#' ```
+#'
+#' @section Package Configuration:
+#'
+#' To use the `@examplesWebR` tag in your package, add the required dependencies:
+#'
+#' In your `DESCRIPTION` file:
+#'
+#' ```
+#' Suggests:
+#' rocleteer
+#' Remotes: coatless-rpkg/rocleteer
+#'
+#' Roxygen: list(..., packages = c("rocleteer"))
+#' ```
+#'
+#' You will also need to specify a location where we can obtain the webR
+#' compiled package in `DESCRIPTION`. This will usually be a GitHub Pages URL
+#' or an R-universe URL. The recommended way is to use the
+#' `Config/rocleteer/webr-repo` field in your `DESCRIPTION` file:
+#'
+#' ```
+#' Config/rocleteer/webr-repo: https://user.github.io/pkgname/
+#' ```
+#'
+#' Alternatively, you can use the `URL` field in your `DESCRIPTION` file to
+#' specify the repository URL. The tag will attempt to auto-detect the
+#' repository URL from the `URL` field if the `Config/rocleteer/webr-repo` field
+#' is not set. The tag will look for:
+#'
+#' ```
+#' URL: https://user.github.io/pkgname/, https://github.com/user/pkgname
+#' ```
+#'
+#' or
+#'
+#' ```
+#' URL: https://username.r-universe.dev
+#' ```
+#'
+#' If we cannot find a suitable repository URL, the tag will throw an error
+#' during processing.
+#'
+#' The generated webR examples will use this URL to install the package
+#' and load it in the webR REPL. The installation code will look like this:
+#'
+#' ```r
+#' # Install and load webR Package
+#' install.packages("pkgname", repos = "https://user.github.io/pkgname/")
+#' library("pkgname")
+#'
+#' # Example code:
+#' your_function()
+#' ```
+#'
+#' @name tag-examplesWebR
+#'
+#' @usage
+#' #' @examplesWebR
+#' #' # Your example code that will be available in webR
+#' #'
+#' #' @examplesWebR embed
+#' #' # Your example code with embedded webR iframe
+#' #'
+#' #' @examplesWebR embed version=v0.5.4 height=400
+#' #' # Your example code with embedded webR iframe with height 400 set to version 0.5.4
+#' #'
+#' #' @examplesWebR embed=false autorun mode=editor-plot
+#' #' # Your example code with custom configuration overriding global settings
+#' #'
+#' @examples
+#' # A function with webR integration
+#' #
+#' #' @title Plot Simple Data
+#' #' @description
+#' #' This function creates a simple plot
+#' #'
+#' #' @param x Numeric vector for x-axis
+#' #' @param y Numeric vector for y-axis
+#' #'
+#' #' @return
+#' #' A plot object
+#' #'
+#' #' @examplesWebR
+#' #' # Create some data
+#' #' x <- 1:10
+#' #' y <- x^2
+#' #'
+#' #' # Create a plot
+#' #' plot(x, y, type = "b", main = "Simple Quadratic")
+#' #'
+#' #' # Add a line
+#' #' lines(x, x^1.5, col = "red")
+NULL
+
+# Internal functions to handle the `@examplesWebR` tag ----
+
+#' Read webR configuration from DESCRIPTION file
+#'
+#' Extract webR-related configuration from the package DESCRIPTION file
+#'
+#' @param base_path Base path to find DESCRIPTION file
+#'
+#' @return
+#' List of configuration options
+#'
+#' @noRd
+read_webr_config <- function(base_path) {
+ # Check for required packages
+ if (!requireNamespace("desc", quietly = TRUE)) {
+ stop("Package 'desc' is required for @examplesWebR tag DESCRIPTION integration")
+ }
+
+ # Find DESCRIPTION file
+ desc_path <- file.path(base_path, "DESCRIPTION")
+ if (!file.exists(desc_path)) {
+ warning("DESCRIPTION file not found, using default webR configuration")
+ return(list())
+ }
+
+ # Read DESCRIPTION
+ d <- desc::desc(desc_path)
+
+ # Extract webR configuration
+ config <- list()
+
+ # Global webR options
+ config$embed <- d$get_field("Config/rocleteer/webr-embed", default = "false")
+ config$embed <- tolower(config$embed) %in% c("true", "yes", "1")
+
+ config$height <- as.numeric(d$get_field("Config/rocleteer/webr-height", default = "300"))
+ config$version <- d$get_field("Config/rocleteer/webr-version", default = "latest")
+ config$autorun <- d$get_field("Config/rocleteer/webr-autorun", default = "false")
+ config$autorun <- tolower(config$autorun) %in% c("true", "yes", "1")
+
+ # Package info for installation
+ config$package_name <- d$get_field("Package", default = "")
+
+ # Repository detection
+ config$webr_repo <- detect_webr_repo(d)
+
+ return(config)
+}
+
+#' Detect webR repository from DESCRIPTION
+#'
+#' Find the appropriate repository URL for webR package installation
+#'
+#' @param desc_obj A desc object from the desc package
+#'
+#' @return
+#' Repository URL string, or stops with error if not found
+#'
+#' @noRd
+detect_webr_repo <- function(desc_obj) {
+ # Check for dedicated webR repo field first
+ webr_repo <- desc_obj$get_field("Config/rocleteer/webr-repo", default = NA)
+ if (!is.na(webr_repo) && nzchar(webr_repo)) {
+ return(webr_repo)
+ }
+
+ # Try to detect from URL field
+ url <- desc_obj$get_field("URL", default = NA)
+ if (!is.na(url) && nzchar(url)) {
+ # Handle multiple URLs (comma or whitespace separated)
+ urls <- strsplit(url, "[,\\s]+")[[1]]
+ urls <- trimws(urls)
+
+ for (u in urls) {
+ # Check for GitHub Pages pattern: https://user.github.io/pkgname/
+ if (grepl("https://[^/]+\\.github\\.io/[^/]+/?$", u)) {
+ return(u)
+ }
+ # Check for R-universe pattern: https://username.r-universe.dev
+ if (grepl("https://[^/]+\\.r-universe\\.dev/?$", u)) {
+ return(u)
+ }
+ }
+ }
+
+ # If no suitable repo found, throw an error
+ stop(
+ "No suitable webR repository found. Please add one of:\n",
+ "1. Config/Needs/WebRRepo: https://user.github.io/pkgname/\n",
+ "2. URL field with GitHub Pages (https://user.github.io/pkgname/) or R-universe (https://user.r-universe.dev) pattern"
+ )
+}
+
+
+#' Generate package installation code for webR
+#'
+#' Create R code to install and load the package in webR
+#'
+#' @param package_name Name of the package
+#' @param repo_url Repository URL
+#'
+#' @return
+#' R code string for package installation
+#'
+#' @noRd
+generate_package_install_code <- function(package_name, repo_url) {
+ if (!nzchar(package_name) || is.na(repo_url)) {
+ return("")
+ }
+
+ install_code <- paste0(
+ "# Install and load webR Package\n",
+ 'webr::install(\n "', package_name, '",\n repos = "', repo_url, '",\n mount = FALSE)\n',
+ 'library("', package_name, '")\n',
+ '\n',
+ "# Example code:\n"
+ )
+
+ return(install_code)
+}
+
+#' Encode R code for webR REPL sharing
+#'
+#' This function encodes R code in the format expected by webR REPL
+#' for URL sharing. It matches the exact encoding used by webR:
+#' JSON format + no compression + base64 + proper flags
+#'
+#' @param code Character string containing R code
+#' @param filename Optional filename (default: "example.R")
+#' @param autorun Whether to enable autorun (adds 'a' flag)
+#'
+#' @return
+#' Base64 encoded string suitable for webR URLs with proper flags
+#'
+#' @noRd
+encode_webr_code <- function(code, filename = "example.R", autorun = FALSE) {
+ # Check for required packages
+ if (!requireNamespace("jsonlite", quietly = TRUE)) {
+ stop("Package 'jsonlite' is required for @examplesWebR tag")
+ }
+ if (!requireNamespace("base64enc", quietly = TRUE)) {
+ stop("Package 'base64enc' is required for @examplesWebR tag")
+ }
+
+ # Create the share item structure exactly as webR expects
+ share_item <- list(
+ list(
+ name = filename,
+ path = paste0("/", filename),
+ text = code
+ )
+ )
+
+ # Convert to JSON to avoid a dependency on RcppMsgPack
+ # Note: webR supports JSON with the 'j' flag
+ json_str <- jsonlite::toJSON(share_item, auto_unbox = TRUE, pretty = FALSE)
+
+ # webR expects UTF-8 bytes
+ json_bytes <- charToRaw(json_str)
+
+ # TODO: Use uncompressed for better compatibility with the webR URL
+
+ # Base64 encode (matching webR's base64 encoding)
+ base64_str <- base64enc::base64encode(json_bytes)
+
+ # URL encode for safe inclusion in URLs
+ encoded <- utils::URLencode(base64_str, reserved = TRUE)
+
+ # Escape percent signs...
+ escape_comment <- function(x) {
+ gsub("%", "\\%", x, fixed = TRUE)
+ }
+ # ...to avoid issues with webR REPL parsing
+ encoded <- escape_comment(encoded)
+
+ # Add flags to encoding: 'jua' = JSON format + uncompressed + autorun
+ flags <- if (autorun) "jua" else "ju"
+ encoded_with_flags <- paste0(encoded, "&", flags)
+
+ return(encoded_with_flags)
+}
+
+#' Validate webR version
+#'
+#' Check if the provided webR version is valid (latest or v0.5.4+)
+#'
+#' @param version_str Version string to validate
+#'
+#' @return
+#' TRUE if valid, FALSE otherwise
+#'
+#' @section Interal Examples:
+#'
+#' ```r
+#' # Valid versions:
+#' validate_webr_version("latest") # TRUE
+#' validate_webr_version("v0.5.4") # TRUE
+#' validate_webr_version("v0.6.0") # TRUE
+#' validate_webr_version("v1.0.0") # TRUE
+#'
+#' # Invalid versions:
+#' validate_webr_version("v0.5.3") # FALSE (too old)
+#' validate_webr_version("v0.3.0") # FALSE (too old)
+#' validate_webr_version("0.6.0") # FALSE (missing 'v')
+#' validate_webr_version("invalid") # FALSE (not a version)
+#' ```
+#'
+#' @noRd
+validate_webr_version <- function(version_str) {
+ # "latest" is always valid
+ if (version_str == "latest") {
+ return(TRUE)
+ }
+
+ # Check if version starts with 'v' and has a valid format
+ if (!grepl("^v\\d+\\.\\d+\\.\\d+", version_str)) {
+ return(FALSE)
+ }
+
+ # Extract numeric version (remove 'v' prefix)
+ numeric_part <- sub("^v", "", version_str)
+
+ # Try to parse as version and compare with minimum
+ tryCatch({
+ provided_version <- numeric_version(numeric_part)
+ min_version <- numeric_version("0.5.4")
+
+ return(provided_version >= min_version)
+ }, error = function(e) {
+ # If version parsing fails, it's invalid
+ return(FALSE)
+ })
+}
+
+
+#' Validate webR mode
+#'
+#' Check if the provided webR mode contains only valid options
+#'
+#' @param mode_str Mode string to validate (e.g., "editor-plot-terminal")
+#'
+#' @return
+#' TRUE if valid, FALSE otherwise
+#'
+#' @noRd
+validate_webr_mode <- function(mode_str) {
+ if (!nzchar(mode_str)) {
+ return(TRUE) # Empty mode is valid (use webR default)
+ }
+
+ valid_modes <- c("editor", "plot", "terminal", "files")
+ provided_modes <- strsplit(mode_str, "-")[[1]]
+
+ # Check if all provided modes are valid
+ all(provided_modes %in% valid_modes)
+}
+
+#' Parse webR tag parameters
+#'
+#' Extract parameters from the webR tag line and merge with global config
+#'
+#' @param tag_line First line of the tag
+#' @param global_config Global configuration from DESCRIPTION file
+#'
+#' @return
+#' List of parameters
+#'
+#' @noRd
+parse_webr_params <- function(tag_line, global_config = list()) {
+ # Start with global config defaults
+ params <- list(
+ embed = global_config$embed %||% TRUE,
+ version = global_config$version %||% "latest",
+ height = global_config$height %||% 300,
+ autorun = global_config$autorun %||% FALSE,
+ mode = global_config$mode %||% "editor-plot-terminal",
+ channel = global_config$channel %||% ""
+ )
+
+ # Parse local parameters from tag line (these override global config)
+
+ # Handle embed (including explicit false)
+ if (grepl("embed=false", tag_line, ignore.case = TRUE)) {
+ params$embed <- FALSE
+ } else if (grepl("embed", tag_line, ignore.case = TRUE)) {
+ params$embed <- TRUE
+ }
+
+ # Handle autorun (including explicit false)
+ if (grepl("autorun=false", tag_line, ignore.case = TRUE)) {
+ params$autorun <- FALSE
+ } else if (grepl("autorun", tag_line, ignore.case = TRUE)) {
+ params$autorun <- TRUE
+ }
+
+ # Extract version if specified
+ version_match <- regmatches(tag_line, regexpr("version=\\S+", tag_line))
+ if (length(version_match) > 0) {
+ version_str <- sub("version=", "", version_match)
+
+ # Validate version
+ if (!validate_webr_version(version_str)) {
+ stop("Invalid webR version '", version_str, "'. Must be 'latest' or v0.5.4 or higher (e.g., v0.5.4, v0.6.0)")
+ }
+
+ params$version <- version_str
+ }
+
+ # Extract height if specified
+ height_match <- regmatches(tag_line, regexpr("height=\\d+", tag_line))
+ if (length(height_match) > 0) {
+ params$height <- as.numeric(sub("height=", "", height_match))
+ }
+
+ # Extract mode if specified
+ mode_match <- regmatches(tag_line, regexpr("mode=[\\w\\-]+", tag_line))
+ if (length(mode_match) > 0) {
+ mode_str <- sub("mode=", "", mode_match)
+
+ # Validate mode
+ if (!validate_webr_mode(mode_str)) {
+ stop("Invalid webR mode '", mode_str, "'. Valid options are: editor, plot, terminal, files (separated by hyphens)")
+ }
+
+ params$mode <- mode_str
+ }
+
+ # Extract channel if specified
+ channel_match <- regmatches(tag_line, regexpr("channel=\\S+", tag_line))
+ if (length(channel_match) > 0) {
+ params$channel <- sub("channel=", "", channel_match)
+ }
+
+ return(params)
+}
+
+#' Generate webR warning HTML
+#'
+#' Create standardized warning message for webR examples
+#'
+#' @return
+#' HTML string with warning message and contact information
+#'
+#' @noRd
+webr_experimental_warning <- function() {
+ paste0(
+ '
',
+ '\U0001F9EA Experimental: Interactive webR examples are a new feature. ',
+ 'Loading may take a moment, and the package version might differ from this documentation.',
+ '
'
+ )
+}
+
+#' Generate webR REPL link
+#'
+#' Create a webR URL with version, mode, channel, and encoded code
+#'
+#' @param encoded_code Base64 encoded code with flags
+#' @param version webR version (e.g. "latest", "v0.5.4"). Default: `"latest"`
+#' @param mode webR interface mode (e.g., "editor-plot-terminal-files"). Default: `""`
+#' @param channel webR channel (e.g., "Automatic"). Default: `""`
+#'
+#' @return
+#' A URL string for the webR REPL
+#'
+#' @keywords internal
+webr_repl_href <- function(encoded_code, version = "latest", mode = "", channel = "") {
+ base_url <- paste0("https://webr.r-wasm.org/", version,"/")
+
+ # Build query parameters
+ query_params <- c()
+
+ if (nzchar(mode)) {
+ query_params <- c(query_params, paste0("mode=", mode))
+ }
+
+ if (nzchar(channel)) {
+ query_params <- c(query_params, paste0("channel=", channel))
+ }
+
+ # Construct URL
+ if (length(query_params) > 0) {
+ url <- paste0(base_url, "?", paste(query_params, collapse = "&"), "#code=", encoded_code)
+ } else {
+ url <- paste0(base_url, "#code=", encoded_code)
+ }
+
+ return(url)
+
+}
+
+#' Generate webR link HTML
+#'
+#' Create HTML for webR REPL link
+#'
+#' @param encoded_code Base64 encoded code
+#' @param version webR version
+#' @param mode webR interface mode
+#' @param channel webR channel
+#'
+#' @return
+#' HTML string for link
+#'
+#' @noRd
+webr_repl_link <- function(encoded_code, version = "latest", mode = "", channel = "") {
+ url <- webr_repl_href(encoded_code, version, mode, channel)
+ html <- paste0(
+ '
',
+
+ # JavaScript for iframe management
+ ''
+ )
+
+ return(html)
+}
+
+#' Parse the `@examplesWebR` tag
+#'
+#' This function handles the new `@examplesWebR` tag, which automatically
+#' creates both regular examples and webR REPL integration.
+#'
+#' @param x A roxygen2 tag
+#'
+#' @return
+#' A parsed roxygen2 tag that contains examples plus webR content
+#'
+#' @noRd
+#' @exportS3Method roxygen2::roxy_tag_parse roxy_tag_examplesWebR
+roxy_tag_parse.roxy_tag_examplesWebR <- function(x) {
+ # Split the raw text into lines
+ lines <- strsplit(x$raw, "\r?\n")[[1]]
+
+ # Read global config from DESCRIPTION (we'll need base_path in roxy_tag_rd)
+ # For now, store the tag parameters for later processing
+ x$tag_line <- lines[1]
+
+ # Extract the actual code (skip first line if it contains only the tag)
+ code_lines <- if (grepl("^\\s*$", lines[2])) lines[-c(1, 2)] else lines[-1]
+ code <- paste(code_lines, collapse = "\n")
+
+ # Process as examples tag first
+ x$raw <- paste(code_lines, collapse = "\n")
+ x$tag <- "examples"
+ results <- roxygen2::tag_examples(x)
+
+ # Store the original code and tag line for later processing
+ results$original_code <- code
+ results$tag_line <- x$tag_line
+
+ return(results)
+}
+
+#' @noRd
+#' @exportS3Method roxygen2::roxy_tag_rd roxy_tag_examplesWebR
+roxy_tag_rd.roxy_tag_examplesWebR <- function(x, base_path, env) {
+
+ # If no original code, just return examples
+ if (is.null(x$original_code) || !nzchar(trimws(x$original_code))) {
+ return(roxygen2::rd_section("examples", x$val))
+ }
+
+ global_config <- list()
+
+ # Read global configuration from DESCRIPTION
+ tryCatch({
+ global_config <- read_webr_config(base_path)
+ }, error = function(e) {
+ warning("Failed to read webR config from DESCRIPTION:\n", e$message)
+ })
+
+ # Parse parameters, merging tag-level with global config
+ params <- parse_webr_params(x$tag_line %||% "", global_config)
+
+ # Generate package installation code if we have repo info
+ install_code <- ""
+ if (!is.null(global_config$webr_repo) && !is.null(global_config$package_name)) {
+ tryCatch({
+ install_code <- generate_package_install_code(global_config$package_name, global_config$webr_repo)
+ }, error = function(e) {
+ warning("Failed to generate package installation code: ", e$message)
+ })
+ }
+
+ # Combine installation code with original example code
+ full_code <- paste0(install_code, x$original_code)
+
+ # Encode the code for webR
+ webr_data <- NULL
+ tryCatch({
+ encoded_code <- encode_webr_code(full_code, autorun = params$autorun)
+
+ # Generate the webR URL for all formats
+ webr_url <- paste0("https://webr.r-wasm.org/", params$version, "/#code=", encoded_code)
+
+ # Generate webR integration HTML for HTML format
+ webr_html <- if (params$embed) {
+ webr_repl_iframe(encoded_code, params$version, params$height, params$mode, params$channel)
+ } else {
+ webr_repl_link(encoded_code, params$version, params$mode, params$channel)
+ }
+
+ # Create content that adapts to different output formats
+ webr_content <- paste0(
+ "\\ifelse{html}{\\out{\n",
+ webr_html,
+ "\n}}{\\ifelse{latex}{\\url{", webr_url, "}}{Interactive webR content not available for this output format.}}"
+ )
+
+ # Create custom WebR section
+ webr_section <- roxygen2::rd_section("section", list(title = "WebR", content = webr_content))
+
+ # Return both examples and WebR sections
+ return(list(
+ roxygen2::rd_section("examples", x$val),
+ webr_section
+ ))
+
+ }, error = function(e) {
+ warning("Failed to create webR integration: ", e$message)
+ return(roxygen2::rd_section("examples", x$val))
+ })
+}
diff --git a/R/rstudio-addins.R b/R/rstudio-addins.R
index e971dd3..e613b87 100644
--- a/R/rstudio-addins.R
+++ b/R/rstudio-addins.R
@@ -17,3 +17,24 @@ insert_examplesTempdir_template <- function() {
rstudioapi::insertText(template)
}
+
+#' Insert a webR examples template
+#'
+#' This function inserts a template for the `@examplesWebR` tag at the cursor position.
+#' It's designed to be used as an RStudio addin.
+#'
+#' @export
+insert_examplesWebR_template <- function() {
+ if (!requireNamespace("rstudioapi", quietly = TRUE) || !rstudioapi::isAvailable()) {
+ stop("This function must be used within RStudio")
+ }
+
+ template <- paste(
+ "#' @examplesWebR",
+ "#' # Your interactive R code here",
+ "#' plot(1:10, 1:10)",
+ sep = "\n"
+ )
+
+ rstudioapi::insertText(template)
+}
diff --git a/README.Rmd b/README.Rmd
index 30a98c6..0b56c21 100644
--- a/README.Rmd
+++ b/README.Rmd
@@ -118,6 +118,166 @@ example_function <- function() {
>
> This roclet is inspired by [an old post of mine](https://blog.thecoatlessprofessor.com/programming/r/hiding-tempdir-and-tempfile-statements-in-r-documentation/) that I initially shared in 2018 covering this pattern.
+### `@examplesWebR`
+
+The `@examplesWebR` tag creates interactive examples that can be run directly in
+the browser using [webR](https://docs.r-wasm.org/webr/latest/). This makes your
+package documentation more engaging and allows users to experiment with examples
+without installing R locally.
+
+For this to work with developmental versions of your package, you will need to
+either have an account with [r-universe.dev](https://r-universe.dev/) or
+use the following `pkgdown` + `rwasm` build action:
+
+
+
+For a fast setup, please use:
+
+```r
+usethis::use_github_action(repos = "https://github.com/r-wasm/actions/blob/main/examples/rwasm-binary-and-pkgdown-site.yml")
+```
+
+> [!IMPORTANT]
+>
+> Please make sure to delete your old `pkgdown.yml` file.
+
+## Requirements
+
+For `@examplesWebR` functionality, your package's `DESCRIPTION` file must
+have:
+
+```
+Suggests:
+ rocleteer
+Remotes: coatless-rpkg/rocleteer
+
+Roxygen: list(..., packages = c("rocleteer"))
+
+Config/rocleteer/webr-repo: https://user.github.io/pkgname/
+```
+
+For the package to be usable in webR examples, you must
+specify a webR-compatible repository in your `DESCRIPTION` file. This
+repository can be generated by [`r-universe`](https://r-universe.dev/) or
+by using the above GitHub Action to build and serve a webR R binary alongside
+your pkgdown site.
+
+By default, the `@examplesWebR` tag will look for the following:
+
+- `Config/rocleteer/webr-repo: https://user.r-universe.dev/` (recommended)
+- `Config/rocleteer/webr-repo: https://user.github.io/pkgname/`
+- `URL` field containing GitHub Pages or R-universe patterns (shown above)
+
+If none of these are found, the tag will produce a warning during processing
+and will not generate the webR section in the Rd file.
+
+#### WebR Version Support
+
+Only webR versions v0.5.4 and higher are supported.
+The tag will validate the version parameter and produce an error if an
+unsupported version is specified.
+
+#### Generated Structure
+
+When you use `@examplesWebR`, it generates:
+
+1. **Examples Section**: Contains your R code as normal examples
+2. **WebR Section**: Contains the webR integration (link or iframe)
+
+That is, from:
+
+```r
+#' @examplesWebR
+#' # Create some data
+#' x <- 1:10
+#' y <- x^2
+#'
+#' # Create a plot
+#' plot(x, y, type = "b", main = "Interactive Example")
+```
+
+it generates:
+
+```rd
+\section{WebR}{
+ \ifelse{html}{
+ \out{
+
+
+ }
+ }{
+ \ifelse{latex}{
+ \url{webR_URL}
+ }{
+ Interactive webR content not available for this output format.
+ }
+ }
+}
+\examples{
+# Create some data
+x <- 1:10
+y <- x^2
+
+# Create a plot
+plot(x, y, type = "b", main = "Interactive Example")
+}
+```
+
+This creates:
+
+- Regular examples with your R code
+- A "WebR" section with a "Try it in your browser" and "Open in Tab" buttons
+ in HTML documentation or a URL in LaTeX documentation.
+- The button opens either an embedded webR session or
+ a new tab with the code in an interactive webR REPL.
+
+> [!NOTE]
+>
+> The `@examplesWebR` tag uses a simplified encoding scheme compatible with webR.
+
+#### Standalone Mode
+
+To avoid embedding the webR REPL, you can use the `@examplesWebR embed=false` tag.
+This will generate a link to the webR REPL without embedding it directly in the documentation.
+
+```r
+#' @examplesWebR embed
+#' # This code will be available in a new web browser tab with the webR REPL
+#' library(ggplot2)
+#' ggplot(mtcars, aes(mpg, wt)) +
+#' geom_point() +
+#' theme_minimal()
+```
+
+#### Additional Options
+
+You can customize the `@examplesWebR` tag with additional options:
+
+- `autorun`: Embed an iframe instead of showing a link (default: `"false"`)
+- `embed`: Embed an iframe instead of showing a link (default: `"true"`)
+- `height=N`: Set iframe height in pixels (default: `400`)
+- `version=X`: Specify webR version (default: `"latest"`)
+- `mode=X-Y`: Configure embedded webR interface (editor, plot, terminal, files) (default: `"editor-plot-terminal"`)
+- `channel=X`: Set webR communication channel (default: `"Automatic`)
+
+For example, to embed with a specific height and version:
+
+```r
+#' @examplesWebR autorun height=500 version=v0.5.4
+#' # Custom height iframe with specific webR version that autoruns code
+#' summary(cars)
+#' plot(cars)
+```
+
+> [!NOTE]
+>
+> I've been on a quest to make R package documentation more interactive and
+> engaging, and this tag is a step towards that goal. It first started as
+> a way to [build and serve a webR R binary alongside pkgdown sites](https://github.com/r-wasm/actions/issues/15) and, then,
+> moved to [`altdocs` with the `quarto-webr` Quarto Extension](https://github.com/coatless-r-n-d/quarto-webr-in-altdoc)...
+> And now, we finally have a way to do this with roxygen2 and pkgdown!
+
+
## License
AGPL (>=3)
diff --git a/README.md b/README.md
index ef4817e..7c689f9 100644
--- a/README.md
+++ b/README.md
@@ -110,6 +110,173 @@ example_function <- function() {
> mine](https://blog.thecoatlessprofessor.com/programming/r/hiding-tempdir-and-tempfile-statements-in-r-documentation/)
> that I initially shared in 2018 covering this pattern.
+### `@examplesWebR`
+
+The `@examplesWebR` tag creates interactive examples that can be run
+directly in the browser using
+[webR](https://docs.r-wasm.org/webr/latest/). This makes your package
+documentation more engaging and allows users to experiment with examples
+without installing R locally.
+
+For this to work with developmental versions of your package, you will
+need to either have an account with
+[r-universe.dev](https://r-universe.dev/) or use the following
+`pkgdown` + `rwasm` build action:
+
+
+
+For a fast setup, please use:
+
+``` r
+usethis::use_github_action(repos = "https://github.com/r-wasm/actions/blob/main/examples/rwasm-binary-and-pkgdown-site.yml")
+```
+
+> \[!IMPORTANT\]
+>
+> Please make sure to delete your old `pkgdown.yml` file.
+
+## Requirements
+
+For `@examplesWebR` functionality, your package’s `DESCRIPTION` file
+must have:
+
+ Suggests:
+ rocleteer
+ Remotes: coatless-rpkg/rocleteer
+
+ Roxygen: list(..., packages = c("rocleteer"))
+
+ Config/rocleteer/webr-repo: https://user.github.io/pkgname/
+
+For the package to be usable in webR examples, you must specify a
+webR-compatible repository in your `DESCRIPTION` file. This repository
+can be generated by [`r-universe`](https://r-universe.dev/) or by using
+the above GitHub Action to build and serve a webR R binary alongside
+your pkgdown site.
+
+By default, the `@examplesWebR` tag will look for the following:
+
+- `Config/rocleteer/webr-repo: https://user.r-universe.dev/`
+ (recommended)
+- `Config/rocleteer/webr-repo: https://user.github.io/pkgname/`
+- `URL` field containing GitHub Pages or R-universe patterns (shown
+ above)
+
+If none of these are found, the tag will produce a warning during
+processing and will not generate the webR section in the Rd file.
+
+#### WebR Version Support
+
+Only webR versions v0.5.4 and higher are supported. The tag will
+validate the version parameter and produce an error if an unsupported
+version is specified.
+
+#### Generated Structure
+
+When you use `@examplesWebR`, it generates:
+
+1. **Examples Section**: Contains your R code as normal examples
+2. **WebR Section**: Contains the webR integration (link or iframe)
+
+That is, from:
+
+``` r
+#' @examplesWebR
+#' # Create some data
+#' x <- 1:10
+#' y <- x^2
+#'
+#' # Create a plot
+#' plot(x, y, type = "b", main = "Interactive Example")
+```
+
+it generates:
+
+``` rd
+\section{WebR}{
+ \ifelse{html}{
+ \out{
+
+
+ }
+ }{
+ \ifelse{latex}{
+ \url{webR_URL}
+ }{
+ Interactive webR content not available for this output format.
+ }
+ }
+}
+\examples{
+# Create some data
+x <- 1:10
+y <- x^2
+
+# Create a plot
+plot(x, y, type = "b", main = "Interactive Example")
+}
+```
+
+This creates:
+
+- Regular examples with your R code
+- A “WebR” section with a “Try it in your browser” and “Open in Tab”
+ buttons in HTML documentation or a URL in LaTeX documentation.
+- The button opens either an embedded webR session or a new tab with the
+ code in an interactive webR REPL.
+
+> \[!NOTE\]
+>
+> The `@examplesWebR` tag uses a simplified encoding scheme compatible
+> with webR.
+
+#### Standalone Mode
+
+To avoid embedding the webR REPL, you can use the
+`@examplesWebR embed=false` tag. This will generate a link to the webR
+REPL without embedding it directly in the documentation.
+
+``` r
+#' @examplesWebR embed
+#' # This code will be available in a new web browser tab with the webR REPL
+#' library(ggplot2)
+#' ggplot(mtcars, aes(mpg, wt)) +
+#' geom_point() +
+#' theme_minimal()
+```
+
+#### Additional Options
+
+You can customize the `@examplesWebR` tag with additional options:
+
+- `autorun`: Embed an iframe instead of showing a link (default:
+ `"false"`)
+- `embed`: Embed an iframe instead of showing a link (default: `"true"`)
+- `height=N`: Set iframe height in pixels (default: `400`)
+- `version=X`: Specify webR version (default: `"latest"`)
+- `mode=X-Y`: Configure embedded webR interface (editor, plot, terminal,
+ files) (default: `"editor-plot-terminal"`)
+- `channel=X`: Set webR communication channel (default: `"Automatic`)
+
+For example, to embed with a specific height and version:
+
+``` r
+#' @examplesWebR autorun height=500 version=v0.5.4
+#' # Custom height iframe with specific webR version that autoruns code
+#' summary(cars)
+#' plot(cars)
+```
+
+> \[!NOTE\]
+>
+> I’ve been on a quest to make R package documentation more interactive
+> and engaging, and this tag is a step towards that goal. It first
+> started as a way to [build and serve a webR R binary alongside pkgdown
+> sites](https://github.com/r-wasm/actions/issues/15) and, then, moved
+> to [`altdocs` with the `quarto-webr` Quarto
+> Extension](https://github.com/coatless-r-n-d/quarto-webr-in-altdoc)…
+> And now, we finally have a way to do this with roxygen2 and pkgdown!
+
## License
AGPL (\>=3)
diff --git a/inst/rstudio/addins.dcf b/inst/rstudio/addins.dcf
index d4d63f7..4600c05 100644
--- a/inst/rstudio/addins.dcf
+++ b/inst/rstudio/addins.dcf
@@ -2,3 +2,8 @@ Name: Insert examplesTempdir
Description: Insert a template for examples that run in a temporary working directory
Binding: insert_examplesTempdir_template
Interactive: false
+
+Name: Insert examplesWebR
+Description: Insert a template for examples that can be run in webR REPL
+Binding: insert_examplesWebR_template
+Interactive: false
\ No newline at end of file
diff --git a/man/insert_examplesWebR_template.Rd b/man/insert_examplesWebR_template.Rd
new file mode 100644
index 0000000..86e69f8
--- /dev/null
+++ b/man/insert_examplesWebR_template.Rd
@@ -0,0 +1,12 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/rstudio-addins.R
+\name{insert_examplesWebR_template}
+\alias{insert_examplesWebR_template}
+\title{Insert a webR examples template}
+\usage{
+insert_examplesWebR_template()
+}
+\description{
+This function inserts a template for the \verb{@examplesWebR} tag at the cursor position.
+It's designed to be used as an RStudio addin.
+}
diff --git a/man/tag-examplesWebR.Rd b/man/tag-examplesWebR.Rd
new file mode 100644
index 0000000..90bf5ce
--- /dev/null
+++ b/man/tag-examplesWebR.Rd
@@ -0,0 +1,198 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/examplesWebR.R
+\name{tag-examplesWebR}
+\alias{tag-examplesWebR}
+\title{Custom \verb{@examplesWebR} tag.}
+\usage{
+#' @examplesWebR
+#' # Your example code that will be available in webR
+#'
+#' @examplesWebR embed
+#' # Your example code with embedded webR iframe
+#'
+#' @examplesWebR embed version=v0.5.4 height=400
+#' # Your example code with embedded webR iframe with height 400 set to version 0.5.4
+#'
+#' @examplesWebR embed=false autorun mode=editor-plot
+#' # Your example code with custom configuration overriding global settings
+#'
+}
+\description{
+When roxygen2 processes your documentation, the \verb{@examplesWebR} tag generates
+both an example section and webR integration. Specifically, the
+example code is split into a regular \verb{@examples} section and a WebR section.
+The webR section contains a clickable link or embedding an interactive iframe
+that interacts with the webR REPL by automatically encoding the code
+for sharing.
+}
+\section{Implementation}{
+
+
+When the \verb{@examplesWebR} tag is processed, it creates:
+\enumerate{
+\item Regular \verb{@examples} section with the code
+\item A \code{"WebR"} section containing format-appropriate webR integration link
+}
+
+The webR URL format is:
+
+\if{html}{\out{
}}
+
+where the code is JSON-encoded and uncompressed.
+}
+
+\section{Generated Structure}{
+
+
+The generated RD file will have the structure:
+
+\if{html}{\out{
}}\preformatted{\\section\{WebR\}\{
+ \ifelse{html}{
+ \out{
+ \U0001F310 View in webR REPL
+ }
+ }{
+ \ifelse{latex}{
+ \url{webR_URL}
+ }{
+ Interactive webR content not available for this output format.
+ }
+ }
+\}
+\\examples\{
+# Your R code here
+plot(1:5, 1:5)
+\}
+}\if{html}{\out{
}}
+
+where \code{webR_URL} is the link to the webR REPL with the encoded code.
+}
+
+\section{Output Format Support}{
+
+
+The webR integration adapts to different documentation formats:
+\itemize{
+\item \strong{HTML}: Interactive buttons/iframes with full webR functionality
+\item \strong{LaTeX/PDF}: Plain URL link to webR session
+\item \strong{Other formats}: Informational message about limited support
+}
+}
+
+\section{Parameters}{
+
+
+The tag supports optional parameters (these override global \code{DESCRIPTION} config):
+\itemize{
+\item \verb{@examplesWebR}: Default behavior (link in WebR section)
+\item \verb{@examplesWebR embed}: Embed iframe instead of link
+\itemize{
+\item \verb{@examplesWebR embed=false} - Explicitly disable embedding (override global config)
+}
+\item \verb{@examplesWebR version=v0.5.4}: Specify webR version greater than or equal to 0.5.4 (default: \code{latest})
+\item \verb{@examplesWebR height=500}: Set iframe height in pixels (default: \code{400})
+\item \verb{@examplesWebR autorun} - Enable autorun on webR session open
+\itemize{
+\item \verb{@examplesWebR autorun=false} - Explicitly disable autorun (override global config)
+}
+\item \verb{@examplesWebR mode=editor-plot} - Configure embedded webR interface (editor, plot, terminal, files) (default: \code{"editor-plot-terminal"})
+\item \verb{@examplesWebR channel=Automatic} - Set webR communication channel (default: \verb{"Automatic})
+}
+
+\strong{Version Requirements}: The version parameter must be either \code{"latest"} or a
+version string \code{v0.5.4} or higher. Earlier versions are not supported
+as the embedding feature is new.
+
+\strong{Mode Options}: Valid mode components are \code{editor}, \code{plot}, \code{terminal}, and \code{files}.
+Combine multiple components with hyphens (e.g., \code{editor-plot-terminal}).
+}
+
+\section{Global Configuration}{
+
+
+You can set global defaults for tags in your package's \code{DESCRIPTION} file:
+
+\if{html}{\out{
}}
+}
+
+\section{Package Configuration}{
+
+
+To use the \verb{@examplesWebR} tag in your package, add the required dependencies:
+
+In your \code{DESCRIPTION} file:
+
+\if{html}{\out{
}}
+
+You will also need to specify a location where we can obtain the webR
+compiled package in \code{DESCRIPTION}. This will usually be a GitHub Pages URL
+or an R-universe URL. The recommended way is to use the
+\code{Config/rocleteer/webr-repo} field in your \code{DESCRIPTION} file:
+
+\if{html}{\out{
}}
+
+Alternatively, you can use the \code{URL} field in your \code{DESCRIPTION} file to
+specify the repository URL. The tag will attempt to auto-detect the
+repository URL from the \code{URL} field if the \code{Config/rocleteer/webr-repo} field
+is not set. The tag will look for:
+
+\if{html}{\out{
}}
+
+If we cannot find a suitable repository URL, the tag will throw an error
+during processing.
+
+The generated webR examples will use this URL to install the package
+and load it in the webR REPL. The installation code will look like this:
+
+\if{html}{\out{
}}\preformatted{# Install and load webR Package
+install.packages("pkgname", repos = "https://user.github.io/pkgname/")
+library("pkgname")
+
+# Example code:
+your_function()
+}\if{html}{\out{
}}
+}
+
+\examples{
+# A function with webR integration
+#
+#' @title Plot Simple Data
+#' @description
+#' This function creates a simple plot
+#'
+#' @param x Numeric vector for x-axis
+#' @param y Numeric vector for y-axis
+#'
+#' @return
+#' A plot object
+#'
+#' @examplesWebR
+#' # Create some data
+#' x <- 1:10
+#' y <- x^2
+#'
+#' # Create a plot
+#' plot(x, y, type = "b", main = "Simple Quadratic")
+#'
+#' # Add a line
+#' lines(x, x^1.5, col = "red")
+}
diff --git a/man/webr_repl_href.Rd b/man/webr_repl_href.Rd
new file mode 100644
index 0000000..4f805d3
--- /dev/null
+++ b/man/webr_repl_href.Rd
@@ -0,0 +1,24 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/examplesWebR.R
+\name{webr_repl_href}
+\alias{webr_repl_href}
+\title{Generate webR REPL link}
+\usage{
+webr_repl_href(encoded_code, version = "latest", mode = "", channel = "")
+}
+\arguments{
+\item{encoded_code}{Base64 encoded code with flags}
+
+\item{version}{webR version (e.g. "latest", "v0.5.4"). Default: \code{"latest"}}
+
+\item{mode}{webR interface mode (e.g., "editor-plot-terminal-files"). Default: \code{""}}
+
+\item{channel}{webR channel (e.g., "Automatic"). Default: \code{""}}
+}
+\value{
+A URL string for the webR REPL
+}
+\description{
+Create a webR URL with version, mode, channel, and encoded code
+}
+\keyword{internal}