Skip to content

Commit 8dfae7d

Browse files
committed
Harden run paths
- Normalized paths instead of using a relative path. - Added `app.py` or first detected `py` file to end of `shiny ...` command
1 parent 03623c3 commit 8dfae7d

File tree

2 files changed

+65
-17
lines changed

2 files changed

+65
-17
lines changed

R/shinylive-quarto-apps-commands.R

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,38 +26,61 @@ create_quarto_shinylive_apps <- function(apps, output_format, output_path) {
2626
#'
2727
#' @export
2828
print.quarto_shinylive_apps <- function(x, ...) {
29+
2930
# Style definitions
3031
path_style <- list(color = "cyan")
3132
cmd_style <- list(color = "yellow")
3233

34+
3335
if (x$output_format == "app-dir") {
3436
# Group apps by engine
3537
r_apps <- which(sapply(x$apps, function(x) x$engine == "r"))
3638
py_apps <- which(sapply(x$apps, function(x) x$engine == "python"))
3739

40+
# Get full normalized path for base directory
41+
full_base_path <- normalizePath(x$output_path, winslash = "/", mustWork = TRUE)
42+
3843
cli::cli_h1("Shinylive Applications")
3944

4045
# Print R apps
4146
if (length(r_apps) > 0) {
42-
cli::cli_h2("R Applications")
47+
cli::cli_h2("Shiny for {.emph R} Applications")
4348
cli::cli_text("Run in {.emph R}:")
4449
for (app_num in r_apps) {
45-
dir_path <- sprintf("%s/app_%d", x$output_path, app_num)
46-
cli::cli_code(sprintf('shiny::runApp("%s")', dir_path))
50+
dir_path <- file.path(full_base_path, sprintf("app_%d", app_num))
51+
cli::cli_code(
52+
sprintf('shiny::runApp("%s")', dir_path),
53+
language = "r"
54+
)
4755
}
4856
}
4957

5058
# Print Python apps
5159
if (length(py_apps) > 0) {
5260
if (length(r_apps) > 0) cli::cli_text() # Add spacing
53-
cli::cli_h2("Python Applications")
61+
cli::cli_h2("Shiny for {.emph Python} Applications")
5462
cli::cli_text("Run in {.emph Terminal}:")
5563
for (app_num in py_apps) {
56-
dir_path <- sprintf("%s/app_%d", x$output_path, app_num)
57-
cli::cli_code(sprintf('shiny run --reload --launch-browser "%s"', dir_path))
64+
app <- x$apps[[app_num]]
65+
dir_path <- file.path(full_base_path, sprintf("app_%d", app_num))
66+
67+
# Find main Python file
68+
file_names <- names(app$files)
69+
main_py_file <- if ("app.py" %in% file_names) {
70+
"app.py"
71+
} else {
72+
# Take first .py file if app.py not found
73+
py_files <- file_names[grepl("\\.py$", file_names)]
74+
if (length(py_files) > 0) py_files[1] else ""
75+
}
76+
77+
app_path <- file.path(dir_path, main_py_file)
78+
cli::cli_code(
79+
sprintf('shiny run --reload --launch-browser "%s"', app_path),
80+
language = "bash"
81+
)
5882
}
5983
}
60-
6184
} else {
6285
# Quarto format
6386
cli::cli_h1("Quarto Document with Shinylive Applications")
@@ -76,20 +99,20 @@ print.quarto_shinylive_apps <- function(x, ...) {
7699
if (needs_cd) {
77100
cli::cli_text()
78101
cli::cli_text("{.strong Step {current_step}:} Change to the document directory:")
79-
cli::cli_code(sprintf('cd "%s"', doc_dir))
102+
cli::cli_code('cd "{doc_dir}"', language = "bash")
80103
current_step <- current_step + 1
81104
}
82105

83106
# Step: Install extension
84107
cli::cli_text()
85108
cli::cli_text("{.strong Step {current_step}:} Install the Shinylive extension:")
86-
cli::cli_code("quarto add quarto-ext/shinylive")
109+
cli::cli_code("quarto add quarto-ext/shinylive", language = "bash")
87110
current_step <- current_step + 1
88111

89112
# Step: Preview document
90113
cli::cli_text()
91114
cli::cli_text("{.strong Step {current_step}:} Preview the document:")
92-
cli::cli_code(sprintf('quarto preview "%s"', doc_name))
115+
cli::cli_code('quarto preview "{doc_name}"', language = "bash")
93116

94117
# Add summary of contained apps
95118
cli::cli_text()
@@ -103,6 +126,5 @@ print.quarto_shinylive_apps <- function(x, ...) {
103126
))
104127
}
105128

106-
# Add invisible return
107129
invisible(x)
108130
}

R/shinylive-standalone-app-commands.R

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,50 @@ create_standalone_shinylive_app <- function(app_data, output_dir, url) {
2626
#'
2727
#' @export
2828
print.standalone_shinylive_app <- function(x, ...) {
29+
30+
2931
# First detect if it's an R or Python app by looking at file extensions
3032
files_list <- if(is.list(x$files[[1]])) x$files else list(x$files)
3133
file_names <- sapply(files_list, function(f) f$name)
3234

35+
# Check if there are R or Python files
3336
is_r_app <- any(grepl("\\.R$", file_names))
3437
is_python_app <- any(grepl("\\.py$", file_names))
3538

39+
# Find main Python file (prefer app.py, but take first .py if not found)
40+
main_py_file <- if (is_python_app) {
41+
app_py <- file_names[file_names == "app.py"]
42+
if (length(app_py) > 0) {
43+
app_py[1]
44+
} else {
45+
py_files <- file_names[grepl("\\.py$", file_names)]
46+
if (length(py_files) > 0) py_files[1] else NULL
47+
}
48+
} else {
49+
NULL
50+
}
51+
52+
# Normalized the output path
53+
full_path <- normalizePath(x$output_dir, winslash = "/", mustWork = TRUE)
54+
3655
cli::cli_h1("Standalone Shinylive Application")
3756

38-
# Show app type and run command
57+
# Show app type and run command with appropriate syntax highlighting
3958
if (is_r_app) {
40-
cli::cli_text("Type: {.emph R Shiny}")
59+
cli::cli_text("Type: {.emph R} Shiny")
4160
cli::cli_text("Run in {.emph R}:")
42-
cli::cli_code(sprintf('shiny::runApp("%s")', x$output_dir))
61+
cli::cli_code(
62+
sprintf('shiny::runApp("%s")', full_path),
63+
language = "r"
64+
)
4365
} else if (is_python_app) {
44-
cli::cli_text("Type: {.emph Python Shiny}")
66+
dir_path <- file.path(full_path, main_py_file)
67+
cli::cli_text("Type: {.emph Python} Shiny")
4568
cli::cli_text("Run in {.emph Terminal}:")
46-
cli::cli_code(sprintf('shiny run --reload --launch-browser "%s"', x$output_dir))
69+
cli::cli_code(
70+
sprintf('shiny run --reload --launch-browser "%s"', dir_path),
71+
language = "bash"
72+
)
4773
}
4874

4975
# Add contents section
@@ -72,7 +98,7 @@ print.standalone_shinylive_app <- function(x, ...) {
7298

7399
# Add location information
74100
cli::cli_text()
75-
cli::cli_text("Location: {.file {x$output_dir}}")
101+
cli::cli_text("Location: {.path {full_path}}")
76102

77103
invisible(x)
78104
}

0 commit comments

Comments
 (0)