Open coatless opened 11 months ago
Note: This isn't quite the same as support for the
## file:
directive (not yet implemented for R) that allows for breaking up a code cell inside of Quarto.
Hi @coatless, I'm having a hard time parsing this sentence. As far as I can tell, there are two things you might mean by ## file:
directive:
shinylive-r
or shinylive-python
chunk, which marks the start of a new file with ## file: <file_name>
.#| file:
for including an external file.I think you might mean the first option, which is implemented for shinylive-r
chunks, but you might mean the second, which is not implemented for the shinylive extension in general. After that, I'm not sure which Quarto feature "allows for breaking up a code cell"...?
That said, it would be helpful for the shinylive extension to allow users to pull in the chunk contents from a file, e.g. using a file
chunk option, or via a directory, possibly using app_dir
as the chunk option name.
👋 @gadenbuie
Sorry for the confusion.
The ask here is to be able to take any external R Shiny App (.app
/ui.R
+ server.R
) and incorporate it into the shinylive-r
code cell. As it stands, the current approach is to keep a standalone test file for R and, then, copy + paste it verbatim into a {shinylive-r}
cell.
The knitr option of #| file:
is one way to approach solving that issue. The other is parsing a shinyAppDir()
function call in an shinylive-r
cell. In the final code cell, I was suggesting an easier parse approach for the directory specification since that does not require executing code in the cell to unfold the app via shinyAppDir()
.
Regarding the ## file:
attribute, I mentioned that to emphasize that I wasn't looking for an in-script option for embedding the script (e.g. no ## file: ui.R
and ## file: server.R
.
Though, for r-shinylive
the ## file:
is not fully implemented. Consider the following modifications from the py-shinylive index.qmd
example:
---
title: "Embed image file attribute shinylive-r"
format:
html:
resources:
- shinylive-sw.js
filters:
- shinylive
---
```{shinylive-r}
#| standalone: true
#| components: [editor, viewer]
#| layout: vertical
#| viewerHeight: 400
## file: app.R
library(shiny)
ui <- fluidPage(
titlePanel("Hello Shiny!")
)
server <- function(input, output) {
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)
## file: www/logo.png
## type: binary
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
<img width="1370" alt="Editor view showing app instantiation fails to create R object from image data" src="https://github.com/quarto-ext/shinylive/assets/833642/0af85699-1b63-4659-86f5-48bcc9864f96">
<img width="1377" alt="Preview of app data stream" src="https://github.com/quarto-ext/shinylive/assets/833642/9d14a7d5-cf0d-4ddf-933d-03f65d19d4cc">
@coatless thanks for the explanation -- I would expect binary embedded files to work, so I've move that part of the discussion to a new issue: #40.
I certainly agree that it'd be useful to be able to embed an entire app via it's directory and my personal preference is for a chunk option.
By the way, I recently added a command to the CLI in the Python py-shinylive
package to create shinylive URLs from local files or to decode shinylive URLs. Given a directory structure like
.
├── app.R
└── images
├── basic.txt
└── logo.png
and after installing shinylive with pip install shinylive
, you could run
shinylive url encode app.R images/*
#> https://shinylive.io/r/editor/#code=NobwRAdghgtgpmAXAAjFADugd...
to create the shinylive URL for that app bundle. You can pipe this output back into shinylive url decode
to decode the app, which is helpfully printed using the quarto chunk syntax:
shinylive url encode app.R images/* | shinylive url decode
## file: app.R
library(shiny)
ui <- fluidPage(
titlePanel("Hello Shiny!")
)
server <- function(input, output) {
}
# Create Shiny app ----
shinyApp(ui = ui, server = server)
## file: images/basic.txt
Just plain text
## file: images/logo.png
## type: binary
iVBORw0KGgoAAAANSUhEUgAAACgAAAAuCAYAAABap1twAAAABGdB...
Obviously this isn't as convenient as an actual chunk option, but it might help you out until are able to develop the new feature.
By the way, for text app files, Quarto's include
directive works really well:
Instead of this (which doesn't work)
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
shiny::shinyAppDir(
"path/to/shiny/app/"
)
You can use this
````markdown
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
{{< include "path/to/shiny/app/app.R" >}}
Or to rename the file
````markdown
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
## file: app.R
{{< include "path/to/shiny/app/app-example.R" >}}
I just created a new Quarto extension that provides a base64 shortcode to fill in the last missing gap here: adding base64 encoded files to Shinylive.
Using that extension, you can now do this:
```{shinylive-r}
#| standalone: true
#| viewerHeight: 600
## file: app.R
{{< include "path/to/shiny/app/app-example.R" >}}
## file: www/photo.png
## type: binary
{{< base64 "path/to/shiny/app/www/photo.png" >}}
For more complicated apps, I think we'd like to lean on `shinylive::export()` or `shinylive.export()` so that we can coordinate where the assets live. Maybe we could add a shortcode `{{< shinylive-app-r "app/dir" ... >}}`.
With
rmarkdown
, the app source can be outside of the code cell using: