student_info() and "reflection_editable"
questions now have a clear Edit/Submit cycle. Submit locks the fields
behind an “Edit” button; Edit reopens them, with the button back to
“Submit” and a note that changes aren’t saved until then. Previously the
fields stayed open after Submit and “Edit” silently resaved, so readers
couldn’t tell whether a change had gone in. The download now reports
only submitted answers, and is blocked until every
student_info() form is submitted. Behaviour
change: a reader who filled in student info without pressing
Submit must now press it before downloading.## Title then
###, is now revealed with the heading.tutorial_options() gains
require_submission, default TRUE: the
“Continue” button at the end of a section is disabled, with a note under
it, until every question() and student_info()
form above it in that section has been submitted. With
type = "reflection" questions, which lock on submit, a
tutorial can show its own answer right after the reader’s without
inviting them to copy it back. Reference material can opt out with
require_submission = FALSE, as hello-learnr2
does. Behaviour change for existing tutorials: readers
must now submit every question in a section before continuing.
New tutorial_options(), for settings that apply to a
whole tutorial page. Its first option, allow_skip, controls
the table of contents: by default (FALSE) a sidebar entry
for a section the reader has not reached yet is dimmed and does nothing
when clicked, becoming a working link only once that section is
unlocked, so the sidebar cannot be used to read the whole tutorial at
once. tutorial_options(allow_skip = TRUE) restores the
previous behaviour, where clicking any entry unlocked every section up
to it and jumped there. Call it once, in an echo: false
chunk; check_tutorial() treats such a chunk as a widget
chunk. Behaviour change for existing tutorials: none of
them opted in, so all of them now lock the sidebar.
The “Start Over” button no longer depends on the
table-of-contents sidebar. A tutorial rendered with
toc: false used to have no Start Over at all, and every
tutorial lost it on a phone-width screen, where Quarto hides the
sidebar. The button now sits at the bottom of the sidebar when one is
showing and otherwise at the top of the tutorial, directly under the
title, moving between the two as the window resizes.
The getting-started and intro-vectors
tutorials are no longer bundled; hello-learnr2 is the one
bundled tutorial, and it gained a “Layout options” section describing
toc, allow_skip and Start Over.
getting-started lives on in ‘primer.tutorials’.
CRAN review: create_tutorial() and
add_live_extension() no longer default dir to
the working directory; dir is required, so neither function
writes anywhere the caller did not name (pass dir = "." for
the old behaviour). available_tutorials() with no
package now finds packages by listing the libraries on
.libPaths() for a tutorials/ directory instead
of calling utils::installed.packages(), which reads several
files per installed package. The package now declares
Depends: R (>= 4.0.0), which
tools::R_user_dir() (the render cache
run_tutorial() writes to) requires.
run_tutorial() now works from GitHub Codespaces (and
any other remote container that forwards ports). Two things were wrong.
The cache root the server serves had no page of its own, so httpuv
answered / with a bare 404 – and / is exactly
what the “Open in Browser” button on VS Code’s and Codespaces’ new-port
notification opens, since it only knows the port, not the tutorial’s
path. The root is now a small page that forwards to the tutorial
launched most recently and lists every other rendered tutorial. And the
address printed and opened was always
http://127.0.0.1:7446/..., which inside a codespace is
reachable only from the container; in a codespace (detected from
CODESPACE_NAME and
GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN) the forwarded
https://<codespace>-7446.app.github.dev/<package>/<name>/
address is used instead. run_tutorial() also honours a
BROWSER environment variable (VS Code sets one in its
terminals that opens pages on the user’s own machine) ahead of R’s
browser option. The render stamp now records the tutorial’s
title, for the root listing.
run_tutorial() now caches renders. A Quarto tutorial
is rendered into output_dir/<package>/<name>/
together with a stamp recording the learnr2 version and a fingerprint of
the installed tutorial files; while both still match, the next launch
serves the cached copy at once instead of spending seconds (or, on a
small cloud machine, half a minute) in Quarto.
refresh = TRUE forces a re-render, which always starts from
a clean directory so stale files cannot linger. New
prerender_tutorials() fills the cache for every installed
Quarto tutorial ahead of time, for container images and other
environment builds.
run_tutorial() always serves on port 7446
(options(learnr2.port = ) overrides) and never falls back
to a random port, and it serves the whole cache root so every tutorial
has its own stable address,
http://127.0.0.1:7446/<package>/<name>/. Saved
answers are keyed by page URL, so this is what makes them findable on
the next launch; it also means one tutorial’s “Start Over” no longer
wipes every tutorial’s progress, and boilerplate questions sharing an id
no longer bleed between tutorials (both of which happened while every
tutorial was served at the same root URL). If a learnr2 server is
already running, a new run_tutorial() just opens the
tutorial there; if the port is held by anything else, it errors instead
of silently serving somewhere the browser has no saved answers.
Breaking for readers mid-tutorial: answers saved under
the old root address are not carried over to the new per-tutorial
address.
available_tutorials() and
run_tutorial() now see a package loaded from its source
tree with pkgload::load_all(), as
devtools::load_all() and devtools::test() do,
reading its tutorials from the source inst/tutorials/.
Previously they resolved only against installed packages, so a content
package’s own devtools::test() found no tutorials (or
silently tested a stale installed copy). Installed packages behave as
before, and ‘pkgload’ is not required.
run_tutorial() now runs classic ‘learnr’ tutorials
too. When the named tutorial’s format is
"rmarkdown" (an .Rmd with
runtime: shiny_prerendered), it is handed to
learnr::run_tutorial() instead of being rendered with
Quarto, so a tool such as the “R Tutorials” VS Code extension can list
and run both kinds of tutorial through learnr2 alone. ‘learnr’ is only
Suggested: any package that bundles classic tutorials already depends on
it, so it is installed whenever such a tutorial is.
open = FALSE is an error for an "rmarkdown"
tutorial, which has no render-only mode.
available_tutorials() gains a
package_dependencies list column, the R packages each
tutorial needs installed locally: character(0) for a
"quarto" tutorial (its exercises run in the browser via
WebR), what learnr::available_tutorials() reports for an
"rmarkdown" one, or NA when ‘learnr’ is not
installed to ask.
New render_tutorials() and
check_tutorial(), learnr2’s counterparts of
‘tutorial.helpers’’ knit_tutorials() and
check_tutorial_defaults(), so a content package can test
its tutorials: render_tutorials() copies each tutorial to a
work directory, adds the ‘quarto-live’ extension, renders it with
Quarto, and errors naming any tutorial that fails;
check_tutorial() runs static checks for the authoring
mistakes a successful render does not catch (missing
#| label:, echo: false,
persist: true, a graded exercise without a
solution: true cell, a {webr} package missing
from webr: packages:, and the standard boilerplate).
run_tutorial() and the package’s own GitHub Pages
publishing now render through render_tutorials(), and
learnr2’s test suite renders every bundled tutorial for real (skipped on
CRAN and where Quarto is not installed).
available_tutorials() gains a path
column, the installed .qmd/.Rmd file, ready to
pass to render_tutorials() and
check_tutorial().
New show_file(), ported from ‘tutorial.helpers’
together with its tests: print all or part of a text file, rows matching
a pattern, its code chunks, or its YAML header. Two changes from the
original: the default (chunk = "auto") now shows the
last code chunk of a file that has chunks – the overwhelmingly
common use – and the whole file otherwise (pass
chunk = "None" or start = 0 for the whole file
of a .qmd; supplying start, end,
or pattern also switches back to rows); and
chunk = "<label>" shows the chunk with that label,
which some existing tutorials already call as if it worked.
question() gains show_text. Set
show_text = FALSE to keep the prompt out of the widget box
when it is already written as ordinary text on the page above it; the
prompt stays in the saved data and is still read by screen readers.question(), quiz(),
student_info(), or download_answers_button()
at the console now opens a browser preview only in an interactive
session; non-interactive prints (scripts, R CMD check) emit
the HTML source instead of launching the system browser.We hope to replace both learnr and tutorial.helpers with learnr2. There is no reason to have two packages, or to not have learnr2 do everything we want a tutorial package to do.
Create interactive R tutorials that run entirely in the browser using ‘Quarto’ and ‘WebR’ via the ‘quarto-live’ extension.