Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# quarto (development version)

- `quarto_preview()` now explicitly returns the preview server URL (invisibly) and documents this behavior. This enables programmatic workflows such as taking screenshots with **webshot2** or passing the URL to other automation tools (thanks, @cwickham, #233).

- Added NA value detection in YAML processing to prevent silent failures when passing R's `NA` values to Quarto CLI. Functions `as_yaml()` and `write_yaml()` now validate for NA values and provide clear error messages with actionable suggestions. This addresses issues where R's `NA` values get converted to YAML strings (like `.na.real`) that Quarto doesn't recognize as missing values, because they are not supported in YAML 1.2 spec. This is to help users handle missing data appropriately before passing to Quarto (#168).

- Added `add_spin_preamble()` function to add YAML preambles to R scripts for use with Quarto Script rendering support. The function automatically detects existing preambles and provides flexible customization options through `title` and `preamble` parameters (#164).
Expand Down
14 changes: 9 additions & 5 deletions R/daemon.R
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@

# port and host
args <- c(args, "--port", port)
if (!identical(host, "127.0.0.1")) {
if (identical(host, "127.0.0.1")) {
host <- "localhost" # use localhost for consistency

Check warning on line 66 in R/daemon.R

View check run for this annotation

Codecov / codecov/patch

R/daemon.R#L65-L66

Added lines #L65 - L66 were not covered by tests
}
if (!identical(host, "localhost")) {

Check warning on line 68 in R/daemon.R

View check run for this annotation

Codecov / codecov/patch

R/daemon.R#L68

Added line #L68 was not covered by tests
args <- c(args, "--host", host)
}

Expand Down Expand Up @@ -148,12 +151,14 @@
}
poll_process()

serve_url <- quarto[[url_key]] %||% sprintf("http://%s:%i", host, port)

Check warning on line 154 in R/daemon.R

View check run for this annotation

Codecov / codecov/patch

R/daemon.R#L154

Added line #L154 was not covered by tests

# indicate server is running
if (isFALSE(quiet)) {
cli::cli
cli::cli_inform(c(
"",
i = "Stop the preview with {.code quarto::quarto_{command}_stop()}"
# "i" = "Preview server running at {.url {serve_url}}",
">" = "Stop the preview with {.code quarto_{command}_stop()}"

Check warning on line 161 in R/daemon.R

View check run for this annotation

Codecov / codecov/patch

R/daemon.R#L161

Added line #L161 was not covered by tests
))
}

Expand All @@ -166,11 +171,10 @@
utils::browseURL
)
}
serve_url <- quarto[[url_key]] %||% paste0("http://localhost:", port)
browse(serve_url)
}

invisible()
invisible(serve_url)

Check warning on line 177 in R/daemon.R

View check run for this annotation

Codecov / codecov/patch

R/daemon.R#L177

Added line #L177 was not covered by tests
}

stop_serve_daemon <- function(command) {
Expand Down
16 changes: 15 additions & 1 deletion R/preview.R
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
#' @param quiet Suppress warning and other messages, from R and also Quarto CLI
#' (i.e `--quiet` is passed as command line)
#'
#' @return The URL of the preview server (invisibly). This can be used to
#' programmatically access the server location, for example to take screenshots
#' with webshot2 or pass to other automation tools.
#'
#' @importFrom processx process
#' @importFrom rstudioapi isAvailable
#' @importFrom rstudioapi viewer
Expand All @@ -42,6 +46,16 @@
#' # (rather than RStudio Viewer)
#' quarto_preview("myproj", open = utils::browseURL)
#'
#' # Capture the preview URL for programmatic use
#' preview_url <- quarto_preview("document.qmd", browse = FALSE)
#' cat("Preview available at:", preview_url, "\n")
#'
#' # Take a screenshot of the preview using webshot2
#' if (require(webshot2)) {
#' preview_url <- quarto_preview("document.qmd", browse = FALSE)
#' webshot2::webshot(preview_url, "preview.png")
#' }
#'
#' # Stop any running quarto preview
#' quarto_preview_stop()
#' }
Expand Down Expand Up @@ -71,7 +85,7 @@ quarto_preview <- function(
args <- c("--no-navigate")
}

# serve
# serve (return serve_url)
run_serve_daemon(
"preview",
file,
Expand Down
15 changes: 15 additions & 0 deletions man/quarto_preview.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

44 changes: 44 additions & 0 deletions tests/testthat/test-preview.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
test_that("quarto_preview default functionality", {
skip_if_no_quarto()
skip_on_cran()

tmp_dir <- withr::local_tempdir()
withr::local_dir(tmp_dir)
xfun::write_utf8(c("---", "title: Test", "---", "", "# Hello"), "test.qmd")

expect_no_error({
url <- withr::with_dir(tmp_dir, {
quarto_preview("test.qmd", browse = FALSE, quiet = TRUE)
})
})

# Always clean up
withr::defer(quarto_preview_stop())

if (exists("url")) {
expect_true(grepl("^https?://", url))
}
})

test_that("quarto_preview can change port", {
skip_if_no_quarto()
skip_on_cran()

tmp_dir <- withr::local_tempdir()
withr::local_dir(tmp_dir)
xfun::write_utf8(c("---", "title: Test", "---", "", "# Hello"), "test.qmd")

expect_no_error({
url <- withr::with_dir(tmp_dir, {
quarto_preview("test.qmd", port = 8888, browse = FALSE, quiet = TRUE)
})
})

# Always clean up
withr::defer(quarto_preview_stop())

if (exists("url")) {
expect_true(grepl("^https?://", url))
expect_true(grepl(":8888", url))
}
})