Details
",
+ "Details
",
code_html,
" "
)
}
sprintf(
- "%s
",
+ "%s
",
paste(c(code_html, plot_html), collapse = "\n")
)
}
diff --git a/pkg-r/inst/www/commons-chat/commons-chat.css b/pkg-r/inst/www/commons-chat/commons-chat.css
index 14af90dd..bf0fc1a3 100644
--- a/pkg-r/inst/www/commons-chat/commons-chat.css
+++ b/pkg-r/inst/www/commons-chat/commons-chat.css
@@ -556,14 +556,14 @@ shiny-chat-container
white-space: normal;
}
-/* ---- run_r display ---------------------------------------------------- */
+/* ---- run_r and run_python display -------------------------------------- */
-.commons-run-r-display {
+.commons-run-display {
display: grid;
gap: 0.6rem;
}
-.commons-run-r-details > summary {
+.commons-run-details > summary {
align-items: center;
color: inherit;
cursor: pointer;
@@ -574,11 +574,11 @@ shiny-chat-container
width: fit-content;
}
-.commons-run-r-details > summary::-webkit-details-marker {
+.commons-run-details > summary::-webkit-details-marker {
display: none;
}
-.commons-run-r-details > summary::before {
+.commons-run-details > summary::before {
border-bottom: 1px solid currentColor;
border-right: 1px solid currentColor;
content: "";
@@ -590,92 +590,98 @@ shiny-chat-container
width: 0.45em;
}
-.commons-run-r-details[open] > summary::before {
+.commons-run-details[open] > summary::before {
transform: rotate(45deg);
}
-.commons-run-r-details > summary:focus-visible {
+.commons-run-details > summary:focus-visible {
border-radius: var(--bs-border-radius-sm, 0.25rem);
outline: 2px solid var(--bs-primary, #007bc2);
outline-offset: 2px;
}
-.commons-run-r-details[open] > .commons-run-r-code {
- animation: commons-run-r-details-reveal 0.2s ease-out;
+.commons-run-details[open] > .commons-run-code {
+ animation: commons-run-details-reveal 0.2s ease-out;
}
-@keyframes commons-run-r-details-reveal {
+@keyframes commons-run-details-reveal {
from {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
- .commons-run-r-details > summary::before {
+ .commons-run-details > summary::before {
transition: none;
}
- .commons-run-r-details[open] > .commons-run-r-code {
+ .commons-run-details[open] > .commons-run-code {
animation: none;
}
}
-.commons-run-r-display .commons-run-r-code {
+.commons-run-display .commons-run-code {
margin: 0;
overflow-x: auto;
white-space: pre;
}
-.commons-run-r-code .hl.com {
+.commons-run-code .hl.com {
color: #a0a1a7;
font-style: italic;
}
-.commons-run-r-code .hl.kwa {
+.commons-run-code .hl.kwa {
color: #a626a4;
}
-.commons-run-r-code .hl.kwc,
-.commons-run-r-code .hl.num {
+.commons-run-code .hl.kwc,
+.commons-run-code .hl.num {
color: #986801;
}
-.commons-run-r-code .hl.kwd {
+.commons-run-code .hl.kwd {
color: #4078f2;
}
-.commons-run-r-code .hl.sng {
+.commons-run-code .hl.sng {
color: #50a14f;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.com {
+[data-bs-theme="dark"] .commons-run-code .hl.com {
color: #5c6370;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.kwa {
+[data-bs-theme="dark"] .commons-run-code .hl.kwa {
color: #c678dd;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.kwc,
-[data-bs-theme="dark"] .commons-run-r-code .hl.num {
+[data-bs-theme="dark"] .commons-run-code .hl.kwc,
+[data-bs-theme="dark"] .commons-run-code .hl.num {
color: #d19a66;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.kwd {
+[data-bs-theme="dark"] .commons-run-code .hl.kwd {
color: #61aeee;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.sng {
+[data-bs-theme="dark"] .commons-run-code .hl.sng {
color: #98c379;
}
-.commons-run-r-details > .commons-run-r-code {
+.commons-run-details > .commons-run-code {
margin-top: 0.4rem;
}
-.commons-run-r-plot {
+.commons-run-plot {
border: 1px solid var(--bs-border-color, #dee2e6);
border-radius: 0.5rem;
height: auto;
max-width: min(100%, 34rem);
}
+
+/* shinychat dims finished tool cards, which would fade a plot meant for the
+ user. Opacity on the card cannot be undone on the image inside it. */
+shiny-chat-container .shiny-tool-card:has(.commons-run-plot):not([fullscreen]) {
+ opacity: 1;
+}
diff --git a/pkg-r/src/sandbox.c b/pkg-r/src/sandbox.c
index 56f563eb..82552330 100644
--- a/pkg-r/src/sandbox.c
+++ b/pkg-r/src/sandbox.c
@@ -914,7 +914,11 @@ SEXP c_sandbox_engage(SEXP read_roots, SEXP rw_roots, SEXP memory_limit,
off = append_subpaths(profile, size, off, rw_roots);
off = append_str(profile, size, off,
")\n"
- "(allow file-read-metadata)\n");
+ "(allow file-read-metadata)\n"
+ /* A new process reads the root directory while it starts, and aborts if
+ * it cannot. This exposes the names at the top level of the disk, not the
+ * contents of anything under them. */
+ "(allow file-read-data (literal \"/\"))\n");
char *err = NULL;
if (sandbox_init(profile, 0, &err) != 0) {
diff --git a/pkg-r/tests/testthat/test-run-r.R b/pkg-r/tests/testthat/test-run-r.R
index f2640821..e084edba 100644
--- a/pkg-r/tests/testthat/test-run-r.R
+++ b/pkg-r/tests/testthat/test-run-r.R
@@ -31,7 +31,7 @@ test_that("run_r executes code against stored handles", {
expect_false(res@extra$display$open)
expect_match(
res@extra$display$html,
- '',
+ '',
fixed = TRUE
)
expect_match(
@@ -145,8 +145,8 @@ test_that("run_r returns plots as images and opens the display", {
"data:image/png;base64,",
fixed = TRUE
)
- expect_match(res@extra$display$html, "commons-run-r-details")
- expect_match(res@extra$display$html, "commons-run-r-code", fixed = TRUE)
+ expect_match(res@extra$display$html, "commons-run-details")
+ expect_match(res@extra$display$html, "commons-run-code", fixed = TRUE)
expect_match(res@extra$display$html, "Details
", fixed = TRUE)
})
@@ -165,7 +165,7 @@ test_that("run_r collapses code and output above plots", {
expect_match(res@value[[1]]@text, "private warning")
expect_match(
res@extra$display$html,
- 'Details
',
+ 'Details
',
fixed = TRUE
)
expect_match(res@extra$display$html, "#> private text", fixed = TRUE)
@@ -177,7 +177,7 @@ test_that("run_r collapses code and output above plots", {
fixed = TRUE
)
expect_lt(
- as.integer(regexpr("commons-run-r-details", res@extra$display$html)),
+ as.integer(regexpr("commons-run-details", res@extra$display$html)),
as.integer(regexpr(
"data:image/png;base64,",
res@extra$display$html,
@@ -219,7 +219,7 @@ test_that("run_r surfaces errors from model code without failing the tool", {
expect_match(res@value, "Error: boom")
expect_false(res@extra$display$open)
expect_match(res@extra$display$html, "#> boom", fixed = TRUE)
- expect_match(res@extra$display$html, "commons-run-r-code", fixed = TRUE)
+ expect_match(res@extra$display$html, "commons-run-code", fixed = TRUE)
expect_no_match(res@extra$display$html, "1',
fixed = TRUE
)
- expect_match(res@extra$display$html, "commons-run-r-code", fixed = TRUE)
+ expect_match(res@extra$display$html, "commons-run-code", fixed = TRUE)
expect_no_match(res@extra$display$html, "#>", fixed = TRUE)
expect_no_match(res@extra$display$html, " summary {
+.commons-run-details > summary {
align-items: center;
color: inherit;
cursor: pointer;
@@ -574,11 +574,11 @@ shiny-chat-container
width: fit-content;
}
-.commons-run-r-details > summary::-webkit-details-marker {
+.commons-run-details > summary::-webkit-details-marker {
display: none;
}
-.commons-run-r-details > summary::before {
+.commons-run-details > summary::before {
border-bottom: 1px solid currentColor;
border-right: 1px solid currentColor;
content: "";
@@ -590,92 +590,98 @@ shiny-chat-container
width: 0.45em;
}
-.commons-run-r-details[open] > summary::before {
+.commons-run-details[open] > summary::before {
transform: rotate(45deg);
}
-.commons-run-r-details > summary:focus-visible {
+.commons-run-details > summary:focus-visible {
border-radius: var(--bs-border-radius-sm, 0.25rem);
outline: 2px solid var(--bs-primary, #007bc2);
outline-offset: 2px;
}
-.commons-run-r-details[open] > .commons-run-r-code {
- animation: commons-run-r-details-reveal 0.2s ease-out;
+.commons-run-details[open] > .commons-run-code {
+ animation: commons-run-details-reveal 0.2s ease-out;
}
-@keyframes commons-run-r-details-reveal {
+@keyframes commons-run-details-reveal {
from {
opacity: 0;
}
}
@media (prefers-reduced-motion: reduce) {
- .commons-run-r-details > summary::before {
+ .commons-run-details > summary::before {
transition: none;
}
- .commons-run-r-details[open] > .commons-run-r-code {
+ .commons-run-details[open] > .commons-run-code {
animation: none;
}
}
-.commons-run-r-display .commons-run-r-code {
+.commons-run-display .commons-run-code {
margin: 0;
overflow-x: auto;
white-space: pre;
}
-.commons-run-r-code .hl.com {
+.commons-run-code .hl.com {
color: #a0a1a7;
font-style: italic;
}
-.commons-run-r-code .hl.kwa {
+.commons-run-code .hl.kwa {
color: #a626a4;
}
-.commons-run-r-code .hl.kwc,
-.commons-run-r-code .hl.num {
+.commons-run-code .hl.kwc,
+.commons-run-code .hl.num {
color: #986801;
}
-.commons-run-r-code .hl.kwd {
+.commons-run-code .hl.kwd {
color: #4078f2;
}
-.commons-run-r-code .hl.sng {
+.commons-run-code .hl.sng {
color: #50a14f;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.com {
+[data-bs-theme="dark"] .commons-run-code .hl.com {
color: #5c6370;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.kwa {
+[data-bs-theme="dark"] .commons-run-code .hl.kwa {
color: #c678dd;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.kwc,
-[data-bs-theme="dark"] .commons-run-r-code .hl.num {
+[data-bs-theme="dark"] .commons-run-code .hl.kwc,
+[data-bs-theme="dark"] .commons-run-code .hl.num {
color: #d19a66;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.kwd {
+[data-bs-theme="dark"] .commons-run-code .hl.kwd {
color: #61aeee;
}
-[data-bs-theme="dark"] .commons-run-r-code .hl.sng {
+[data-bs-theme="dark"] .commons-run-code .hl.sng {
color: #98c379;
}
-.commons-run-r-details > .commons-run-r-code {
+.commons-run-details > .commons-run-code {
margin-top: 0.4rem;
}
-.commons-run-r-plot {
+.commons-run-plot {
border: 1px solid var(--bs-border-color, #dee2e6);
border-radius: 0.5rem;
height: auto;
max-width: min(100%, 34rem);
}
+
+/* shinychat dims finished tool cards, which would fade a plot meant for the
+ user. Opacity on the card cannot be undone on the image inside it. */
+shiny-chat-container .shiny-tool-card:has(.commons-run-plot):not([fullscreen]) {
+ opacity: 1;
+}