-
Notifications
You must be signed in to change notification settings - Fork 40
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Convert to quarto #149
Convert to quarto #149
Conversation
Used `Find in Files...` and replace in RStudio
To include an empty line after the YAML block, the following modified version of `knitr::convert_chunk_header()` was used: ```r function ( input, output = NULL, type = c("multiline", "wrap", "yaml"), width = 0.9 * getOption("width")) { text = xfun::read_utf8(input) ext = xfun::file_ext(input) if (missing(type) && ext == "qmd") type = "yaml" type = match.arg(type) pattern = knitr:::detect_pattern(text, ext) if (pattern == "brew") return() markdown_mode = pattern == "md" chunk_begin = knitr:::all_patterns[[pattern]]$chunk.begin nb_added = 0L new_text = text for (i in grep(chunk_begin, text)) { indent = knitr:::get_chunk_indent(text[i]) header = knitr:::extract_params_src(chunk_begin, text[i]) engine = if (markdown_mode) knitr:::get_chunk_engine(header) else "r" params = if (markdown_mode) knitr:::get_chunk_params(header) else header if (params == "") next params2 = knitr:::clean_empty_params(params) params2 = trimws(knitr:::clean_empty_params(params2)) opt_chars = xfun:::get_option_comment(engine) prefix = paste0(indent, opt_chars$start) new_text[i + nb_added] = gsub(params, "", text[i], fixed = TRUE) if (type == "wrap") { params3 = strwrap(params2, width, prefix = prefix) } else if (type == "multiline") { res = xfun::csv_options(params2) params3 = sprintf("%s = %s,", names(res), deparsed_string(res)) last = length(params3) params3[last] = gsub(",$", "", params3[last]) params3 = if (width <= 0) paste0(prefix, params3) else { strwrap(params3, width, prefix = prefix) } } else { params3 = xfun::csv_options(params2) params3 = lapply(params3, function(x) { if (is.symbol(x) || is.language(x)) { x = deparse(x, 500L) attr(x, "tag") = "!expr" } x }) params3 = knitr:::dash_names(params3) params3 = strsplit(yaml::as.yaml(params3, handlers = list(logical = function(x) { x = tolower(x) class(x) = "verbatim" x }, numeric = function(x) { if (length(x) != 1) return(x) x2 = as.integer(x) if (x2 == x) x2 else x }), line.sep = "\n"), "\n")[[1]] params3 = paste0(prefix, params3) } if (nzchar(opt_chars$end)) params3 = paste0(params3, opt_chars$end) new_text = append(new_text, c(params3, ""), after = i + nb_added) nb_added = nb_added + length(params3) + 1 } if (is.null(output)) return(new_text) if (is.function(output)) output = output(input) xfun::write_utf8(new_text, output) invisible(output) } ```
The following code was run locally on macOS: - Replace underscores with dashes ```shell find . -type f -name "*.qmd" | while read file do if grep -q '^#|' "$file"; then sed -i '' '/^#| label: /s/_/-/g' "$file" fi done ``` - Replace spaces with dashes ```shell find . -type f -name "*.qmd" | while read file do if grep -q '^#| label: .* ' "$file"; then sed -i '' '/^#| label: /s/ /-/g' "$file" sed -i '' 's/^#|-label:-/#| label: /' "$file" fi done ``` - Convert to lower case ```shell for file in *.qmd; do awk '{if ($0 ~ /^#\| label: /) {print tolower($0)} else {print $0}}' "$file" > temp.txt && mv temp.txt "$file" done ```
The following code was run locally on macOS: ```shell sed -i '' 's/^```{yaml}/```yaml/' 29-quarto_formats.qmd ```
The following code was run locally on macOS: ```shell find . -type f -name "*.qmd" | while read file do sed -i '' -E 's/`r knitr::include_url\("([^"]+)"\)`/{{< video \1 >}}/g' "$file" done ```
The following code was run locally on macOS: ```shell find . -type f -name "*.qmd" | while read file do sed -i '' -E 's/<details>/::: {.callout-tip collapse="true"}/; s/[[:space:]]*<summary>[[:space:]]*Meeting chat log[[:space:]]*<\/summary>/## Meeting chat log/g; s/<\/details>/:::/g' "$file" done ```
The following code was run locally on macOS to add shortcodes: ```shell for file in *.qmd; do filename="${file%.qmd}" awk -v filename="$filename" ' BEGIN { title_found = 0 } { if (title_found == 0 && /^# /) { title_found = 1 print $0 print "" print "::: {.content-visible when-profile=\"slides\"}" print "{{< revealjs \"slides/" filename ".html\" >}}" print ":::" } else { print $0 } }' "$file" > "${file}.tmp" mv "${file}.tmp" "$file" done ```
Level 1 headings cause vertical navigation in Quarto revealjs slides. Using YAML works both for book and slides format. When using YAML, it is not possible to keep a book chapter unnumbered like with `# <title> {.unnumbered}`, so the level 1 headings of unnumbered chapters `index.qmd` and `00-introduction.qmd` must stay the same. The following code was run locally on macOS and changes to `index.qmd` and `00-introduction.qmd` were manually discarded: ```shell find . -type f -name "*.qmd" | while read -r file; do heading=$(awk 'NR==1{print; exit}' "$file" | sed 's/^# //') title_block="---\ntitle: \"${heading}\"\n---" sed -i "1s|^#.*|$title_block|" "$file" done ```
Only level 1 headings can be unnumbered in books but these cause the same file to be rendered as revealjs slides with vertical navigation. The `.content-visible` class is used to generate poject-specific headings, i.e., unnumbered chapters for book or a level 2 heading for slides.
Thank you for this effort! Unfortunately, I already have an in-progress PR to implement a much simpler system, like I used in https://dslc.io/wapir. I have to manually add chapters to the In general, I highly recommend opening an issue in a repo before taking on something this large! That gives the maintainer a chance to let you know if your project is likely to be helpful. Thanks again for putting in this work! I'll check out your process to see if there's anything to borrow for the approach I'm taking! |
Hi,
Summary
Conversion of {bookdown} to Quarto book and slides (hopefully can be used as a template for other book clubs).
Depends on workflows provided in PR r4ds/r4dsactions#5.
Details
gitbook allows
split_by: section
to split each chapter into separate pages using sections (defined by level 2 headings), in essence creating almost the equivalent of presentation slides. Unfortunately, the Quarto book does not have a similar option (see this discussion).After some experimentation, I decided it would be best to render the Quarto project once as a book and once as slides using Quarto project profiles and embed the slides as profile content only in the book using shortcodes using the embedio extension.
In general, I tried to use bash commands on macOS and a modified version of
knitr::convert_chunk_header()
(both mainly generated with GPT-4o) to automate some of the necessary text replacements. For details on the clean-up tasks, see the different commits.Caveats
Currently, the "Render project" button in the build pane of RStudio will only render the book. To render everything, render first the book and then the slides (rendering the book second would overwrite the slides output):
Render time is also pretty long because of a code chunk in
21-databases.qmd
and because everything will be computed twice. I decided against keeping computations using the Quarto project optionfreeze
because its behaviour can be unpredictable for people not having experience with it.Commits
Convert to Quarto file names
Convert to Quarto book & remove orphan files
Define parts in project file
Format learning objectives as heading level 2
Used
Find in Files...
and replace in RStudioConvert code chunk options to YAML syntax
To include an empty line after the YAML block, the following modified
version of
knitr::convert_chunk_header()
was used:Control heading numbering depth in project file
Clean up code chunk labels
The following code was run locally on macOS:
Replace underscores with dashes
The following code was run locally on macOS:
The following code was run locally on macOS:
The following code was run locally on macOS:
Set up Quarto profiles for book and slides
Embed slides in book using embedio extension
The following code was run locally on macOS to add shortcodes:
title
in YAMLLevel 1 headings cause vertical navigation in Quarto revealjs slides.
Using YAML works both for book and slides format. When using YAML, it
is not possible to keep a book chapter unnumbered like with
# <title> {.unnumbered}
, so the level 1 headings of unnumberedchapters
index.qmd
and00-introduction.qmd
must stay the same.The following code was run locally on macOS and changes to
index.qmd
and
00-introduction.qmd
were manually discarded:Use profile content to generate correct heading
Only level 1 headings can be unnumbered in books but these cause the
same file to be rendered as revealjs slides with vertical navigation.
The
.content-visible
class is used to generate poject-specificheadings, i.e., unnumbered chapters for book or a level 2 heading for
slides.
Add Quarto project file
Rename workflow
Use new Quarto-specific reusuable workflows
Update dependencies
Include link to Quarto book