Closed yonicd closed 3 years ago
It's possible that this is related to #3181. In that case, the reactiveVals weren't actually still around; they just had entries in the reactlog that weren't scoped to the session, so they remained in the "global" part of the reactlog even after a session ended.
Are you sure this is related to the specific browser, or do the extra entries just accumulate when you open/reload the app in more browser windows?
I am running the app in clean sessions each time.
I initially saw this using {whereami} reactive counter that got hit inconsistently which is basically reflecting the same issue through a different logging scheme.
Here is the output from that attempt: https://gist.github.com/yonicd/024baffb3117d8270b95232f49b334f8
{reactor} is creating independent sessions of webdriver/processx and running unit tests then unlinking the side effects.
You can see the same behavior is happening in that output which is independent of possible reactlog activity.
I see the exact same reactlog graph in each of the three cases when I do the following, making sure to restart R between each run:
install.packages('shiny')
install.packages('reactlog')
remotes::install_github('yonicd/puzzlemath@17def58')
### Pane Viewer ----
### RESTART R HERE ###
library(reactlog)
library(puzzlemath)
library(shiny)
reactlog::reactlog_enable()
options(shiny.launch.browser = .rs.invokeShinyPaneViewer)
shiny::reactlogReset()
shinyApp(
ui = puzzlemath:::app_ui,
server = puzzlemath:::app_server
)
reactlogShow()
### Window Viewer ----
### RESTART R HERE ###
library(reactlog)
library(puzzlemath)
library(shiny)
reactlog::reactlog_enable()
options(shiny.launch.browser = .rs.invokeShinyWindowViewer)
shiny::reactlogReset()
shinyApp(
ui = puzzlemath:::app_ui,
server = puzzlemath:::app_server
)
reactlogShow()
# External browser ----------
### RESTART R HERE ###
library(reactlog)
library(puzzlemath)
library(shiny)
reactlog::reactlog_enable()
options(shiny.launch.browser = .rs.invokeShinyWindowExternal)
shiny::reactlogReset()
shinyApp(
ui = puzzlemath:::app_ui,
server = puzzlemath:::app_server
)
reactlogShow()
i tried that again restarting each time.
no duplicate elements:
duplicate elements:
What is the internal browser of the RS desktop IDE on the osx?
I also similar behavior on GHA workflows and couldnt understand why my reactivity counts were different when running reactivity tests.
I see the same thing in Firefox that I did in Chrome.
This is with the app running in rstudio.cloud.
The RStudio IDE on Mac is based on Chromium, I believe.
It's not clear to me how you're using firefox to visit the app, but I suspect that it's hitting the app twice. You could add some code to your server function to write a timestamp to a file every time the server function is executed; then you can see how many times the app is being hit. You can also use options(shiny.trace=TRUE)
to see the network traffic.
I’ll try that, thanks for digging into this.
not sure what i am seeing here, you probably know better to spot what is happening.
It seems like in firefox the action button game
was hit after the question was created and put in ques
and in chrome this isnt happening.
query in terminal the default browser
yonis@Yonis-MacBook-Pro ~ % x=~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist; \
plutil -convert xml1 $x; \
grep 'https' -b3 $x | awk 'NR==2 {split($2, arr, "[><]"); print arr[3]}'; \
plutil -convert binary1 $x
com.google.chrome
Restarting R session...
> options(shiny.trace=TRUE)
> options(shiny.launch.browser = .rs.invokeShinyWindowExternal)
> shiny::reactlogReset()
> shiny::shinyApp(
+ ui = puzzlemath:::app_ui,
+ server = puzzlemath:::app_server
+ )
Loading required package: shiny
Listening on http://127.0.0.1:7183
SEND {"config":{"workerId":"","sessionId":"9a71839f391a1fead621fbad4db7dad9","user":null}}
RECV {"method":"init","data":{"signs":["+","-","*","/"],"game:shiny.action":0,"draw:shiny.action":0,"range":[1,10],"n":25,"ans":"",".clientdata_output_plot1-plot_width":930,".clientdata_output_plot1-plot_height":500,".clientdata_output_plot1-plot_bg":"rgb(255, 255, 255)",".clientdata_output_plot1-plot_fg":"rgb(51, 51, 51)",".clientdata_output_plot1-plot_accent":"rgb(51, 122, 183)",".clientdata_output_plot1-plot_font":{"families":["Helvetica Neue","Helvetica","Arial","sans-serif"],"size":"14px"},".clientdata_output_ques_hidden":false,".clientdata_output_plot1-plot_hidden":false,".clientdata_output_tbl_hidden":true,".clientdata_pixelratio":2,".clientdata_url_protocol":"http:",".clientdata_url_hostname":"127.0.0.1",".clientdata_url_port":"7183",".clientdata_url_pathname":"/",".clientdata_url_search":"",".clientdata_url_hash_initial":"",".clientdata_url_hash":"",".clientdata_singletons":"603e796bcfc2ab3685167d58c426f64c15a95192",".clientdata_allowDataUriScheme":true}}
SEND {"busy":"busy"}
SEND {"custom":{"shinyjs-show":{"id":"draw","anim":false,"animType":"slide","time":0.5,"selector":null}}}
SEND {"custom":{"shinyjs-click":{"id":"game"}}}
SEND {"custom":{"shinyjs-click":{"id":"game"}}}
SEND {"custom":{"shinyjs-runjs":{"code":"document.getElementById('anspanel').style.borderColor = 'grey'"}}}
SEND {"custom":{"shinyjs-runjs":{"code":"document.getElementById('anspanel').style.borderWidth = '1px'"}}}
SEND {"recalculating":{"name":"plot1-plot","status":"recalculating"}}
SEND {"custom":{"shinyjs-logjs":{"text":"plot"}}}
── Running `shiny::renderPlot`(...) at mod_plot.R#31 (1) ─────────────────────────────
SEND {"recalculating":{"name":"plot1-plot","status":"recalculated"}}
SEND {"busy":"idle"}
SEND {"errors":{},"values":{"plot1-plot":{"src":"data:image/png;[base64 data]","width":930,"height":500,"coordmap":{"panels":[{"panel":1,"row":1,"col":1,"panel_vars":{},"log":{"x":null,"y":null},"domain":{"left":-123.6,"right":1995.6,"bottom":3.8,"top":2178.2},"mapping":{"x":"xx","y":"yy"},"range":{"left":16.4383561643836,"right":1849.04109589041,"bottom":983.561643835616,"top":10.958904109589}}],"dims":{"width":1860,"height":1000}}}},"inputMessages":[]}
RECV {"method":"update","data":{"game:shiny.action":2,"draw:shiny.action":2}}
SEND {"busy":"busy"}
SEND {"custom":{"shinyjs-show":{"id":"draw","anim":false,"animType":"slide","time":0.5,"selector":null}}}
── Running observeEventHandler(...) at app_server.R#36 (1) ───────────────────────────
SEND {"recalculating":{"name":"ques","status":"recalculating"}}
SEND {"recalculating":{"name":"ques","status":"recalculated"}}
SEND {"busy":"idle"}
SEND {"errors":{},"values":{"ques":"8 * 6 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
yonis@Yonis-MacBook-Pro ~ % x=~/Library/Preferences/com.apple.LaunchServices/com.apple.launchservices.secure.plist; \
plutil -convert xml1 $x; \
grep 'https' -b3 $x | awk 'NR==2 {split($2, arr, "[><]"); print arr[3]}'; \
plutil -convert binary1 $x
org.mozilla.firefox
Restarting R session...
> library(puzzlemath)
> options(shiny.trace=TRUE)
> options(shiny.launch.browser = .rs.invokeShinyWindowExternal)
> shiny::reactlogReset()
> shiny::shinyApp(
+ ui = puzzlemath:::app_ui,
+ server = puzzlemath:::app_server
+ )
Listening on http://127.0.0.1:7620
SEND {"config":{"workerId":"","sessionId":"3100565c0a54718fd6eecbddb09d9153","user":null}}
RECV {"method":"init","data":{"signs":["+","-","*","/"],"game:shiny.action":0,"draw:shiny.action":0,"range":[1,10],"n":25,"ans":"",".clientdata_output_plot1-plot_width":930,".clientdata_output_plot1-plot_height":500,".clientdata_output_plot1-plot_bg":"rgb(255, 255, 255)",".clientdata_output_plot1-plot_fg":"rgb(51, 51, 51)",".clientdata_output_plot1-plot_accent":"rgb(51, 122, 183)",".clientdata_output_plot1-plot_font":{"families":["Helvetica Neue","Helvetica","Arial","sans-serif"],"size":"14px"},".clientdata_output_ques_hidden":false,".clientdata_output_plot1-plot_hidden":false,".clientdata_output_tbl_hidden":true,".clientdata_pixelratio":2,".clientdata_url_protocol":"http:",".clientdata_url_hostname":"127.0.0.1",".clientdata_url_port":"7620",".clientdata_url_pathname":"/",".clientdata_url_search":"",".clientdata_url_hash_initial":"",".clientdata_url_hash":"",".clientdata_singletons":"603e796bcfc2ab3685167d58c426f64c15a95192",".clientdata_allowDataUriScheme":true}}
SEND {"busy":"busy"}
SEND {"custom":{"shinyjs-show":{"id":"draw","anim":false,"animType":"slide","time":0.5,"selector":null}}}
SEND {"custom":{"shinyjs-click":{"id":"game"}}}
SEND {"custom":{"shinyjs-click":{"id":"game"}}}
SEND {"custom":{"shinyjs-runjs":{"code":"document.getElementById('anspanel').style.borderColor = 'grey'"}}}
SEND {"custom":{"shinyjs-runjs":{"code":"document.getElementById('anspanel').style.borderWidth = '1px'"}}}
SEND {"recalculating":{"name":"plot1-plot","status":"recalculating"}}
SEND {"custom":{"shinyjs-logjs":{"text":"plot"}}}
── Running `shiny::renderPlot`(...) at mod_plot.R#31 (1) ─────────────────────────────
SEND {"recalculating":{"name":"plot1-plot","status":"recalculated"}}
SEND {"busy":"idle"}
SEND {"errors":{},"values":{"plot1-plot":{"src":"data:image/png;[base64 data]","width":930,"height":500,"coordmap":{"panels":[{"panel":1,"row":1,"col":1,"panel_vars":{},"log":{"x":null,"y":null},"domain":{"left":-164.4,"right":2180.4,"bottom":-160.6,"top":2174.6},"mapping":{"x":"xx","y":"yy"},"range":{"left":16.4383561643836,"right":1849.04109589041,"bottom":983.561643835616,"top":10.958904109589}}],"dims":{"width":1860,"height":1000}}}},"inputMessages":[]}
RECV {"method":"update","data":{"game:shiny.action":1,"draw:shiny.action":1}}
SEND {"busy":"busy"}
SEND {"custom":{"shinyjs-show":{"id":"draw","anim":false,"animType":"slide","time":0.5,"selector":null}}}
── Running observeEventHandler(...) at app_server.R#36 (1) ───────────────────────────
SEND {"recalculating":{"name":"ques","status":"recalculating"}}
SEND {"recalculating":{"name":"ques","status":"recalculated"}}
SEND {"busy":"idle"}
SEND {"errors":{},"values":{"ques":"8 * 5 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
RECV {"method":"update","data":{"game:shiny.action":2,"draw:shiny.action":2}}
SEND {"busy":"busy"}
SEND {"custom":{"shinyjs-show":{"id":"draw","anim":false,"animType":"slide","time":0.5,"selector":null}}}
── Running observeEventHandler(...) at app_server.R#36 (2) ───────────────────────────
SEND {"progress":{"type":"binding","message":{"id":"ques"}}}
SEND {"recalculating":{"name":"ques","status":"recalculating"}}
SEND {"recalculating":{"name":"ques","status":"recalculated"}}
SEND {"busy":"idle"}
SEND {"errors":{},"values":{"ques":"10 - 4 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
I also added
app_server <- function( input, output, session ) {
cat(Sys.time(),file = 'timestamp.txt',append = TRUE,sep = '\n')
...
}
and got back only one hit per run
1608782552 # chrome
1608782612 # firefox
Here is the GHA test of the same setup (using reactor), showing this isnt constrained to the local machine that I am using.
testthat setup: https://github.com/yonicd/puzzlemath/blob/main/tests/testthat/reactor-initial.R gha workflow: https://github.com/yonicd/puzzlemath/blob/main/.github/workflows/R-reactor.yml gha output: https://github.com/yonicd/puzzlemath/runs/1603764213?check_suite_focus=true#step:7:33
these unit test are checking the same thing as above, how many times have {whereami} calls been hit after initial invalidation. Which in this case is the same reactive chunk that contains output$ques. This is consistent with what reactlog and shiny.trace are tracking.
At this point, I suggest making a truly minimal reproducible example -- without shinyjs, without reactor, etc. Given what we know at this point, it could be due to shinyjs, or perhaps even the order that JS files are loaded, so it would be best to remove as many variables as possible.
Will do. I’m wo a computer for the week, so I’ll pick this up after the new year. Happy Holidays!
So i removed shinyjs and am using native session$sendCustomMessages
with message handlers defined in inst/app/script.js
here is a reprex
using RSelenium
and processx
, which is consistent with the GHA tests that are run too.
I think the difference is coming from these lines, where if i turn off one of the customMessages
then the initialization is the same.
#remotes::install_github('yonicd/puzzlemath@rm_shinyjs')
testdir <- file.path(tempdir(),'test')
dir.create(testdir,showWarnings = FALSE)
process_args <- c(
"options('shiny.port'= 26877, shiny.host='127.0.0.1', shiny.trace = TRUE)",
"library(puzzlemath)",
"packageVersion('puzzlemath')",
"puzzlemath::run_app()"
)
test_process <- processx::process$new(
command = normalizePath(file.path(Sys.getenv("R_HOME"),'R')),
args = c("-e", paste0(process_args,collapse = ';')),
stderr = file.path(testdir,'err.txt'),
stdout = file.path(testdir,'out.txt')
)
gecko_driver <- RSelenium::rsDriver(
browser = "firefox",
verbose = FALSE,
port = httpuv::randomPort(),
extraCapabilities = list(
"moz:firefoxOptions" = list(
args = c("--headless",'--memory 1024mb')
)
)
)
gecko_driver$client$navigate('http://127.0.0.1:26877')
Sys.sleep(5)
cat(readLines(file.path(testdir,'out.txt')),sep = '\n')
#>
#> R version 3.6.3 (2020-02-29) -- "Holding the Windsock"
#> Copyright (C) 2020 The R Foundation for Statistical Computing
#> Platform: x86_64-apple-darwin15.6.0 (64-bit)
#>
#> R is free software and comes with ABSOLUTELY NO WARRANTY.
#> You are welcome to redistribute it under certain conditions.
#> Type 'license()' or 'licence()' for distribution details.
#>
#> Natural language support but running in an English locale
#>
#> R is a collaborative project with many contributors.
#> Type 'contributors()' for more information and
#> 'citation()' on how to cite R or R packages in publications.
#>
#> Type 'demo()' for some demos, 'help()' for on-line help, or
#> 'help.start()' for an HTML browser interface to help.
#> Type 'q()' to quit R.
#>
#> > options('shiny.port'= 26877, shiny.host='127.0.0.1', shiny.trace = TRUE);library(puzzlemath);packageVersion('puzzlemath');puzzlemath::run_app()
#> [1] ‘0.0.4’
#> ── Running `shiny::renderPlot`(...) at mod_plot.R#31 (1) ───────────────────────
#> ── Running observeEventHandler(...) at app_server.R#35 (1) ─────────────────────
#> ── Running observeEventHandler(...) at app_server.R#35 (2) ─────────────────────
cat(readLines(file.path(testdir,'err.txt')),sep = '\n')
#> Loading required package: shiny
#>
#> Listening on http://127.0.0.1:26877
#> SEND {"config":{"workerId":"","sessionId":"a16b03167f173f2a8495cb5afc2592a7","user":null}}
#> RECV {"method":"init","data":{"signs":["+","-","*","/"],"game:shiny.action":0,"draw:shiny.action":0,"range":[1,10],"n":25,"ans":"",".clientdata_output_plot1-plot_width":881,".clientdata_output_plot1-plot_height":500,".clientdata_output_plot1-plot_bg":"rgb(255, 255, 255)",".clientdata_output_plot1-plot_fg":"rgb(51, 51, 51)",".clientdata_output_plot1-plot_accent":"rgb(51, 122, 183)",".clientdata_output_plot1-plot_font":{"families":["Helvetica Neue","Helvetica","Arial","sans-serif"],"size":"14px"},".clientdata_output_ques_hidden":false,".clientdata_output_plot1-plot_hidden":false,".clientdata_output_tbl_hidden":true,".clientdata_pixelratio":1,".clientdata_url_protocol":"http:",".clientdata_url_hostname":"127.0.0.1",".clientdata_url_port":"26877",".clientdata_url_pathname":"/",".clientdata_url_search":"",".clientdata_url_hash_initial":"",".clientdata_url_hash":"",".clientdata_singletons":"",".clientdata_allowDataUriScheme":true}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"custom":{"clickon":"#game"}}
#> SEND {"custom":{"clickon":"#game"}}
#> SEND {"custom":{"eval":"document.getElementById('anspanel').style.borderColor = 'grey'"}}
#> SEND {"custom":{"eval":"document.getElementById('anspanel').style.borderWidth = '1px'"}}
#> SEND {"recalculating":{"name":"plot1-plot","status":"recalculating"}}
#> SEND {"recalculating":{"name":"plot1-plot","status":"recalculated"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{"plot1-plot":{"src":"data:image/png;[base64 data]","width":881,"height":500,"coordmap":{"panels":[{"panel":1,"row":1,"col":1,"panel_vars":{},"log":{"x":null,"y":null},"domain":{"left":-143.2,"right":2127.2,"bottom":-136.9,"top":2129.9},"mapping":{"x":"xx","y":"yy"},"range":{"left":8.21917808219178,"right":875.520547945206,"bottom":491.780821917808,"top":5.47945205479452}}],"dims":{"width":881,"height":500}}}},"inputMessages":[]}
#> RECV {"method":"update","data":{"game:shiny.action":1,"draw:shiny.action":1}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculating"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculated"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{"ques":"9 + 7 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
#> RECV {"method":"update","data":{"game:shiny.action":2,"draw:shiny.action":2}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"progress":{"type":"binding","message":{"id":"ques"}}}
#> SEND {"recalculating":{"name":"ques","status":"recalculating"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculated"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{"ques":"8 / 1 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
gecko_driver$server$stop()
#> [1] TRUE
test_process$kill()
#> [1] TRUE
test_process <- processx::process$new(
command = normalizePath(file.path(Sys.getenv("R_HOME"),'R')),
args = c("-e", paste0(process_args,collapse = ';')),
stderr = file.path(testdir,'err.txt'),
stdout = file.path(testdir,'out.txt')
)
chrome_driver <- RSelenium::rsDriver(
browser = "chrome",
verbose = FALSE,
chromever = "87.0.4280.88",
port = httpuv::randomPort(),
extraCapabilities = list(
chromeOptions = list(
args = c("--headless","--disable-gpu")
)
)
)
chrome_driver$client$navigate('http://127.0.0.1:26877')
Sys.sleep(5)
cat(readLines(file.path(testdir,'out.txt')),sep = '\n')
#>
#> R version 3.6.3 (2020-02-29) -- "Holding the Windsock"
#> Copyright (C) 2020 The R Foundation for Statistical Computing
#> Platform: x86_64-apple-darwin15.6.0 (64-bit)
#>
#> R is free software and comes with ABSOLUTELY NO WARRANTY.
#> You are welcome to redistribute it under certain conditions.
#> Type 'license()' or 'licence()' for distribution details.
#>
#> Natural language support but running in an English locale
#>
#> R is a collaborative project with many contributors.
#> Type 'contributors()' for more information and
#> 'citation()' on how to cite R or R packages in publications.
#>
#> Type 'demo()' for some demos, 'help()' for on-line help, or
#> 'help.start()' for an HTML browser interface to help.
#> Type 'q()' to quit R.
#>
#> > options('shiny.port'= 26877, shiny.host='127.0.0.1', shiny.trace = TRUE);library(puzzlemath);packageVersion('puzzlemath');puzzlemath::run_app()
#> [1] ‘0.0.4’
#> ── Running `shiny::renderPlot`(...) at mod_plot.R#31 (1) ───────────────────────
#> ── Running observeEventHandler(...) at app_server.R#35 (1) ─────────────────────
cat(readLines(file.path(testdir,'err.txt')),sep = '\n')
#> Loading required package: shiny
#>
#> Listening on http://127.0.0.1:26877
#> SEND {"config":{"workerId":"","sessionId":"ab0780efa6048fba4d4b6d8227e6a319","user":null}}
#> RECV {"method":"init","data":{"signs":["+","-","*","/"],"game:shiny.action":0,"draw:shiny.action":0,"range":[1,10],"n":25,"ans":"",".clientdata_output_plot1-plot_width":503,".clientdata_output_plot1-plot_height":500,".clientdata_output_plot1-plot_bg":"rgb(255, 255, 255)",".clientdata_output_plot1-plot_fg":"rgb(51, 51, 51)",".clientdata_output_plot1-plot_accent":"rgb(51, 122, 183)",".clientdata_output_plot1-plot_font":{"families":["Helvetica Neue","Helvetica","Arial","sans-serif"],"size":"14px"},".clientdata_output_ques_hidden":false,".clientdata_output_plot1-plot_hidden":false,".clientdata_output_tbl_hidden":true,".clientdata_pixelratio":2,".clientdata_url_protocol":"http:",".clientdata_url_hostname":"127.0.0.1",".clientdata_url_port":"26877",".clientdata_url_pathname":"/",".clientdata_url_search":"",".clientdata_url_hash_initial":"",".clientdata_url_hash":"",".clientdata_singletons":"",".clientdata_allowDataUriScheme":true}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"custom":{"clickon":"#game"}}
#> SEND {"custom":{"clickon":"#game"}}
#> SEND {"custom":{"eval":"document.getElementById('anspanel').style.borderColor = 'grey'"}}
#> SEND {"custom":{"eval":"document.getElementById('anspanel').style.borderWidth = '1px'"}}
#> SEND {"recalculating":{"name":"plot1-plot","status":"recalculating"}}
#> SEND {"recalculating":{"name":"plot1-plot","status":"recalculated"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{"plot1-plot":{"src":"data:image/png;[base64 data]","width":503,"height":500,"coordmap":{"panels":[{"panel":1,"row":1,"col":1,"panel_vars":{},"log":{"x":null,"y":null},"domain":{"left":-137.4,"right":1967.4,"bottom":104.1,"top":2166.9},"mapping":{"x":"xx","y":"yy"},"range":{"left":16.4383561643836,"right":995.041095890411,"bottom":983.561643835616,"top":10.958904109589}}],"dims":{"width":1006,"height":1000}}}},"inputMessages":[]}
#> RECV {"method":"update","data":{"game:shiny.action":2,"draw:shiny.action":2}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculating"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculated"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{"ques":"9 * 2 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
chrome_driver$server$stop()
#> [1] TRUE
test_process$kill()
#> [1] TRUE
library(puzzlemath)
details::details(sessioninfo::session_info(),summary = 'sessioninfo')
Created on 2021-01-01 by the reprex package (v0.3.0)
i trimmed out a lot of the uneccessary parts of the app and moved the app into a more standard global/server/ui format to make it simpler to reprex on your side.
files are in inst/app
#remotes::install_github('yonicd/puzzlemath@rm_shinyjs')
testdir <- file.path(tempdir(),'test')
dir.create(testdir,showWarnings = FALSE)
process_args <- c(
"options('shiny.port'= 26877, shiny.host='127.0.0.1', shiny.trace = TRUE)",
"library(puzzlemath)",
"packageVersion('puzzlemath')",
"shiny::runApp(system.file('app',package = 'puzzlemath'))"
)
test_process <- processx::process$new(
command = normalizePath(file.path(Sys.getenv("R_HOME"),'R')),
args = c("-e", paste0(process_args,collapse = ';')),
stderr = file.path(testdir,'err.txt'),
stdout = file.path(testdir,'out.txt')
)
gecko_driver <- RSelenium::rsDriver(
browser = "firefox",
verbose = FALSE,
port = httpuv::randomPort(),
extraCapabilities = list(
"moz:firefoxOptions" = list(
args = c("--headless",'--memory 1024mb')
)
)
)
gecko_driver$client$navigate('http://127.0.0.1:26877')
Sys.sleep(5)
cat(readLines(file.path(testdir,'out.txt')),sep = '\n')
#>
#> R version 3.6.3 (2020-02-29) -- "Holding the Windsock"
#> Copyright (C) 2020 The R Foundation for Statistical Computing
#> Platform: x86_64-apple-darwin15.6.0 (64-bit)
#>
#> R is free software and comes with ABSOLUTELY NO WARRANTY.
#> You are welcome to redistribute it under certain conditions.
#> Type 'license()' or 'licence()' for distribution details.
#>
#> Natural language support but running in an English locale
#>
#> R is a collaborative project with many contributors.
#> Type 'contributors()' for more information and
#> 'citation()' on how to cite R or R packages in publications.
#>
#> Type 'demo()' for some demos, 'help()' for on-line help, or
#> 'help.start()' for an HTML browser interface to help.
#> Type 'q()' to quit R.
#>
#> > options('shiny.port'= 26877, shiny.host='127.0.0.1', shiny.trace = TRUE);library(puzzlemath);packageVersion('puzzlemath');shiny::runApp(system.file('app',package = 'puzzlemath'))
#> [1] ‘0.0.4’
#> ── Running observeEventHandler(...) at server.R#25 (1) ─────────────────────────
#> ── Running observeEventHandler(...) at server.R#25 (2) ─────────────────────────
cat(readLines(file.path(testdir,'err.txt')),sep = '\n')
#> Loading required package: shiny
#>
#> Listening on http://127.0.0.1:26877
#> SEND {"config":{"workerId":"","sessionId":"5a01640cafb0baa09f86a6aea2684ffb","user":null}}
#> RECV {"method":"init","data":{"game:shiny.action":0,"draw:shiny.action":0,"signs":["+","-","*","/"],"range":[1,10],"n":25,"ans":"",".clientdata_output_ques_hidden":false,".clientdata_pixelratio":1,".clientdata_url_protocol":"http:",".clientdata_url_hostname":"127.0.0.1",".clientdata_url_port":"26877",".clientdata_url_pathname":"/",".clientdata_url_search":"",".clientdata_url_hash_initial":"",".clientdata_url_hash":"",".clientdata_singletons":"",".clientdata_allowDataUriScheme":true}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"custom":{"clickon":"#game"}}
#> SEND {"custom":{"clickon":"#game"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{},"inputMessages":[]}
#> RECV {"method":"update","data":{"game:shiny.action":1,"draw:shiny.action":1}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculating"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculated"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{"ques":"8 + 6 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
#> RECV {"method":"update","data":{"game:shiny.action":2,"draw:shiny.action":2}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"progress":{"type":"binding","message":{"id":"ques"}}}
#> SEND {"recalculating":{"name":"ques","status":"recalculating"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculated"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{"ques":"10 / 2 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
gecko_driver$server$stop()
#> [1] TRUE
test_process$kill()
#> [1] TRUE
test_process <- processx::process$new(
command = normalizePath(file.path(Sys.getenv("R_HOME"),'R')),
args = c("-e", paste0(process_args,collapse = ';')),
stderr = file.path(testdir,'err.txt'),
stdout = file.path(testdir,'out.txt')
)
chrome_driver <- RSelenium::rsDriver(
browser = "chrome",
verbose = FALSE,
chromever = "87.0.4280.88",
port = httpuv::randomPort(),
extraCapabilities = list(
chromeOptions = list(
args = c("--headless","--disable-gpu")
)
)
)
chrome_driver$client$navigate('http://127.0.0.1:26877')
Sys.sleep(5)
cat(readLines(file.path(testdir,'out.txt')),sep = '\n')
#>
#> R version 3.6.3 (2020-02-29) -- "Holding the Windsock"
#> Copyright (C) 2020 The R Foundation for Statistical Computing
#> Platform: x86_64-apple-darwin15.6.0 (64-bit)
#>
#> R is free software and comes with ABSOLUTELY NO WARRANTY.
#> You are welcome to redistribute it under certain conditions.
#> Type 'license()' or 'licence()' for distribution details.
#>
#> Natural language support but running in an English locale
#>
#> R is a collaborative project with many contributors.
#> Type 'contributors()' for more information and
#> 'citation()' on how to cite R or R packages in publications.
#>
#> Type 'demo()' for some demos, 'help()' for on-line help, or
#> 'help.start()' for an HTML browser interface to help.
#> Type 'q()' to quit R.
#>
#> > options('shiny.port'= 26877, shiny.host='127.0.0.1', shiny.trace = TRUE);library(puzzlemath);packageVersion('puzzlemath');shiny::runApp(system.file('app',package = 'puzzlemath'))
#> [1] ‘0.0.4’
#> ── Running observeEventHandler(...) at server.R#25 (1) ─────────────────────────
cat(readLines(file.path(testdir,'err.txt')),sep = '\n')
#> Loading required package: shiny
#>
#> Listening on http://127.0.0.1:26877
#> SEND {"config":{"workerId":"","sessionId":"390366c4c826735831fc9a64ce069ab2","user":null}}
#> RECV {"method":"init","data":{"game:shiny.action":0,"draw:shiny.action":0,"signs":["+","-","*","/"],"range":[1,10],"n":25,"ans":"",".clientdata_output_ques_hidden":false,".clientdata_pixelratio":2,".clientdata_url_protocol":"http:",".clientdata_url_hostname":"127.0.0.1",".clientdata_url_port":"26877",".clientdata_url_pathname":"/",".clientdata_url_search":"",".clientdata_url_hash_initial":"",".clientdata_url_hash":"",".clientdata_singletons":"",".clientdata_allowDataUriScheme":true}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"custom":{"clickon":"#game"}}
#> SEND {"custom":{"clickon":"#game"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{},"inputMessages":[]}
#> RECV {"method":"update","data":{"game:shiny.action":2,"draw:shiny.action":2}}
#> SEND {"busy":"busy"}
#> SEND {"custom":{"showid":"draw"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculating"}}
#> SEND {"recalculating":{"name":"ques","status":"recalculated"}}
#> SEND {"busy":"idle"}
#> SEND {"errors":{},"values":{"ques":"8 * 3 ?"},"inputMessages":[{"id":"ans","message":{"value":""}}]}
chrome_driver$server$stop()
#> [1] TRUE
test_process$kill()
#> [1] TRUE
details::details(sessioninfo::session_info(),summary = 'sessioninfo')
Created on 2021-01-04 by the reprex package (v0.3.0)
Can you provide a simple version of the app that can just be copied and pasted? And preferably without RSelenium involved as well.
options(shiny.trace = TRUE,shiny.launch.browser = .rs.invokeShinyWindowExternal)
shiny::runApp('inst/app')
That's better, but it should still be possible to remove significant amounts of code, and that will really help to zero in on the problem.
For example, it seems unlikely that the r2df()
stuff is related to the reactivity issue, so it's best to take it out. And if it is related to the reactivity issue, that's important and interesting. I'd suggest removing bits of code until there's the minimum code that will reproduce the problem. See https://github.com/rstudio/shiny/wiki/Creating-a-Reproducible-Example for more.
You can create an app that has the reactlog stuff shown along with the app. See here for an example: https://github.com/rstudio/shinycoreci-apps/blob/c8a1bd4a318bd2a503f02a03abfc91bab5913335/apps/192-reactlog-hello/app.R
Additionally, you can inline the JS code with something like this in the UI:
tags$script(HTML("
$( document ).ready(function() {
.....
});"
))
Ideally, someone else will be able to copy your code and paste it into a new R session to see the issue.
I think i narrowed down where the problem is shiny::renderText
, but i'm not sure why it is making a difference in gecko vs everything else.
options(shiny.trace=TRUE)
library(shiny)
library(reactlog)
reactlog_enable()
shiny::reactlogReset()
this <- new.env()
app_ui <- shiny::fluidPage(
tags$script(HTML("
$( document ).ready(function() {
Shiny.addCustomMessageHandler('clickon', function(what) {
$(what).click();
});
});"
)),
shiny::sidebarLayout(
shiny::sidebarPanel(
shiny::checkboxGroupInput(
inputId = "signs",
label = "Choose Signs",
inline = TRUE,
choices = c('+','-','*','/'),
selected = c('+','-','*','/')
),
shiny::sliderInput(inputId = 'range', label = 'Number Range',
min = 1, max = 100, value = c(1,10)
),
shiny::sliderInput(inputId = 'n', label = 'Number of Questions',
min = 4, max = 50,step = 1, value = 25
),
shiny::wellPanel(
shiny::actionButton(
inputId = 'game',
label = 'New Game',
onclick = "document.getElementById('draw').click()"
),
shiny::actionButton('draw','New Question')
),
shiny::verbatimTextOutput('ques'),
shiny::wellPanel(
id = 'anspanel',
shiny::textInput(
inputId = 'ans',
label = NULL,
value = '',
placeholder = 'answer')
)),
shiny::mainPanel(
reactlog_module_ui()
)
)
)
app_server <- function( input, output, session ) {
shiny::observeEvent( input$range, {
session$sendCustomMessage('clickon','#game')
})
shiny::observeEvent( input$signs, {
session$sendCustomMessage('clickon','#game')
})
reactlog_module_server()
shiny::observeEvent(c(input$range,input$n,input$game,input$signs),{
shiny::req(input$range)
shiny::req(input$n)
shiny::req(input$signs)
this$df <- data.frame(
x = sample(input$range[1]:input$range[2],input$n,replace = TRUE),
y = sample(input$range[1]:input$range[2],input$n,replace = TRUE),
sign = sample(input$signs,1),
a = 1,
stringsAsFactors = FALSE
)
})
shiny::observeEvent( input$draw , {
idx <- which(this$df$a==1)
if(length(idx)==1){
s <- idx
}else{
s <- sample(idx,1)
}
this$qp <- this$df[s,]
shiny::updateTextInput(session,'ans',value = '')
############################################
# Commenting this out removes the behavior #
############################################
output$ques <- shiny::renderText({
sprintf('%s %s %s ?',this$qp$x,this$qp$sign,this$qp$y)
})
############################################
# Commenting this out removes the behavior #
############################################
})
}
shiny::shinyApp(
ui = app_ui,
server = app_server,
options = list(launch.browser = shiny::browserViewer())
)
Thanks, that version of the code is much easier to digest. Here's a version of it with even more stuff removed that does the same thing:
library(shiny)
library(reactlog)
options(shiny.trace=F)
reactlog_enable()
reactlogReset()
ui <- fluidPage(
tags$script(HTML("
$( document ).ready(function() {
Shiny.addCustomMessageHandler('clickon', function(what) {
console.log('customMessageHandler for clickon')
$(what).click();
});
});"
)),
sidebarLayout(
sidebarPanel(
textInput("signs", "Signs:", value = "x"),
textInput('range', 'Range:', value = "x"),
actionButton('draw','Draw:'),
verbatimTextOutput('ques')
),
mainPanel(
reactlog_module_ui()
)
)
)
server <- function( input, output, session ) {
reactlog_module_server()
message("=============================")
observeEvent( input$range, {
message("==> input$range")
session$sendCustomMessage('clickon','#draw')
})
observeEvent( input$signs, {
message("==> input$signs")
session$sendCustomMessage('clickon','#draw')
})
observeEvent( input$draw , {
message("==> input$draw: ", input$draw)
output$ques <- renderText({
sprintf('output$ques text here')
})
})
}
shinyApp(ui, server, options = list(launch.browser = F, port = 5000))
Notably, in Chrome most of the time I see one instance of output$ques
in the reactlog, but sometimes I see two instances of it. In Firefox, it looks like there are always two instances.
Here's a screenshot in Chrome with one output$ques
:
And another screenshot with two instances:
But you can see the same thing without the reactlog. Each time the observeEvent
expression is executed, it assigns a new object with output$ques <- renderText(...)
. In general, you want to avoid re-assigning something to an output unless there's a specific reason to do it. Typically, you do the assignment to output$ques
outside of the observer.
Anyway, that part can be removed, and you can just look at the messages printed to the console. Sometimes I get this:
=============================
==> input$range
==> input$signs
==> input$draw: 2
And sometimes this:
=============================
==> input$range
==> input$signs
==> input$draw: 1
==> input$draw: 2
So the output$ques
thing is sort of tangential to the actual issue. The real issue is that the observeEvent
sometimes runs once, and sometimes twice.
The reason it's happening has to do with the way that sendCustomMessage
works. For most Shiny output things, like render
functions, all them execute, and then Shiny batches up the updates and sends them all in one message to the client.
sendCustomMessage
is a bit different: the message is sent immediately, because we didn't want to impose any constraints on the timing of messages sent to the client.
The code always triggers two clicks, one from observeEvent(input$range)
, and one from observeEvent(input$signs)
. Because there are two observeEvent
s that call sendCustomMessage()
, two separate messages are sent. Sometimes the browser receives and processes them very close in time, so that they occur in a single "tick" of the JS event loop. In that case, the browser just sends the value after two clicks, 2
, to the server. When that happens, the observeEvent(input$draw, ...)
executes just once.
However, if the browser is able to processes each message quickly enough that they occur in separate ticks of the JS event loop, then the browser first sends 1
to the server, and then 2
. And when that happens, the observeEvent(input$draw, ...)
executes twice.
The differences between Chrome and Firefox in your case are probably because Firefox is handling the messages more quickly. If you want to avoid this behavior, I suggest adding an observer that listens to both input$signs
and input$range
either directly, or indirectly.
thanks for all the help and the thorough explanation!
btw the inline messages you put in are exactly what {whereami} does.
In whereami's case it also logs the hits for you and keeps a record of what line was triggered in the source file.
For shiny it also reports which reactive chunk you are tracking.
I am running an app that is showing different
reactlog
behavior depending on the browser/viewer I am using, should the behavior be browser agnostic?The app repository is here: https://github.com/yonicd/puzzlemath
reactlog jsons: reactlogs.zip
I am running reactlog only until the app in invalidated at startup:
From the logs you can see that shiny is creating duplicates of elements in the window viewer and the windowexternal compared to the paneviewer which is then creating additional reactivity that shouldnt be happening.
here are screenshots of reactlog
System details
Browser Version:
session info
```r ─ Session info ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── setting value version R version 3.6.3 (2020-02-29) os macOS Catalina 10.15.7 system x86_64, darwin15.6.0 ui RStudio language (EN) collate en_US.UTF-8 ctype en_US.UTF-8 tz America/New_York date 2020-12-23 ─ Packages ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── package * version date lib source askpass 1.1 2019-01-13 [1] CRAN (R 3.6.0) assertthat 0.2.1 2019-03-21 [1] CRAN (R 3.6.0) attempt 0.3.1 2020-05-03 [1] CRAN (R 3.6.2) binman 0.1.1 2018-07-18 [1] CRAN (R 3.6.0) bitops 1.0-6 2013-08-17 [1] CRAN (R 3.6.0) caTools 1.18.0 2020-01-17 [1] CRAN (R 3.6.0) cli 2.2.0 2020-11-20 [1] CRAN (R 3.6.2) clipr 0.7.1 2020-10-08 [1] CRAN (R 3.6.2) codetools 0.2-16 2018-12-24 [1] CRAN (R 3.6.3) colorspace 1.4-1 2019-03-18 [1] CRAN (R 3.6.0) config 0.3 2018-03-27 [1] CRAN (R 3.6.0) crayon 1.3.4 2017-09-16 [1] CRAN (R 3.6.0) curl 4.3 2019-12-02 [1] CRAN (R 3.6.0) deldir 0.2-3 2020-11-09 [1] CRAN (R 3.6.2) desc 1.2.0.9000 2020-09-28 [1] Github (r-lib/desc@c175259) details 0.2.1 2020-01-12 [1] local digest 0.6.27 2020-10-24 [1] CRAN (R 3.6.2) dockerfiler 0.1.3 2019-03-19 [1] CRAN (R 3.6.0) dplyr 1.0.2 2020-08-18 [1] CRAN (R 3.6.2) ellipsis 0.3.1 2020-05-15 [1] CRAN (R 3.6.2) evaluate 0.14 2019-05-28 [1] CRAN (R 3.6.0) fansi 0.4.1 2020-01-08 [1] CRAN (R 3.6.0) farver 2.0.3 2020-01-16 [1] CRAN (R 3.6.0) fastmap 1.0.1 2019-10-08 [1] CRAN (R 3.6.0) fs 1.5.0 2020-07-31 [1] CRAN (R 3.6.2) generics 0.0.2 2018-11-29 [1] CRAN (R 3.6.0) ggplot2 3.3.2 2020-06-19 [1] CRAN (R 3.6.2) ggvoronoi 0.8.3 2019-02-19 [1] CRAN (R 3.6.0) glue 1.4.2 2020-08-27 [1] CRAN (R 3.6.2) golem 0.3.0 2020-12-06 [1] local gridExtra 2.3 2017-09-09 [1] CRAN (R 3.6.0) gtable 0.3.0 2019-03-25 [1] CRAN (R 3.6.0) htmltools 0.5.0 2020-06-16 [1] CRAN (R 3.6.2) httpuv 1.5.4 2020-06-06 [1] CRAN (R 3.6.2) httr 1.4.2 2020-07-20 [1] CRAN (R 3.6.2) jsonlite 1.7.2 2020-12-09 [1] CRAN (R 3.6.2) knitr 1.30 2020-09-22 [1] CRAN (R 3.6.2) labeling 0.3 2014-08-23 [1] CRAN (R 3.6.0) later 1.1.0.9000 2020-12-12 [1] Github (r-lib/later@ff8c451) lattice 0.20-38 2018-11-04 [1] CRAN (R 3.6.3) lifecycle 0.2.0 2020-03-06 [1] CRAN (R 3.6.0) magick 2.2 2019-08-26 [1] CRAN (R 3.6.0) magrittr 2.0.1 2020-11-17 [1] CRAN (R 3.6.2) mime 0.9 2020-02-04 [1] CRAN (R 3.6.0) munsell 0.5.0 2018-06-12 [1] CRAN (R 3.6.0) openssl 1.4.3 2020-09-18 [1] CRAN (R 3.6.2) pagedown 0.9 2020-03-18 [1] CRAN (R 3.6.0) pillar 1.4.7 2020-11-20 [1] CRAN (R 3.6.2) pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 3.6.0) pkgload 1.1.0 2020-05-29 [1] CRAN (R 3.6.2) png 0.1-7 2013-12-03 [1] CRAN (R 3.6.0) processx 3.4.5 2020-11-30 [1] CRAN (R 3.6.2) promises 1.1.1.9001 2020-11-25 [1] Github (rstudio/promises@bbadb3d) ps 1.5.0 2020-12-05 [1] CRAN (R 3.6.2) purrr 0.3.4 2020-04-17 [1] CRAN (R 3.6.2) puzzlemath * 0.0.3 2020-12-22 [1] local R6 2.5.0 2020-10-28 [1] CRAN (R 3.6.2) raster 3.4-5 2020-11-14 [1] CRAN (R 3.6.2) Rcpp 1.0.5 2020-07-06 [1] CRAN (R 3.6.2) reactlog 1.1.0 2020-09-12 [1] CRAN (R 3.6.2) reactor 0.2.1 2020-12-22 [1] local remotes 2.2.0 2020-07-21 [1] CRAN (R 3.6.2) rgeos 0.5-3 2020-05-08 [1] CRAN (R 3.6.2) rlang 0.4.9 2020-11-26 [1] CRAN (R 3.6.2) rmarkdown 2.5 2020-10-21 [1] CRAN (R 3.6.3) roxygen2 7.1.1 2020-06-27 [1] CRAN (R 3.6.2) rprojroot 2.0.2 2020-11-25 [1] Github (r-lib/rprojroot@5bafca9) rsconnect 0.8.16 2019-12-13 [1] CRAN (R 3.6.2) RSelenium 1.7.7 2020-02-03 [1] CRAN (R 3.6.1) rstudioapi 0.13 2020-11-12 [1] CRAN (R 3.6.2) scales 1.1.0 2019-11-18 [1] CRAN (R 3.6.0) semver 0.2.0 2017-01-06 [1] CRAN (R 3.6.0) sessioninfo 1.1.1 2018-11-05 [1] CRAN (R 3.6.0) shiny * 1.5.0 2020-06-23 [1] CRAN (R 3.6.2) shinyjs 1.0 2018-01-08 [1] CRAN (R 3.6.0) shinyWidgets 0.5.0 2019-11-18 [1] CRAN (R 3.6.0) sp 1.4-4 2020-10-07 [1] CRAN (R 3.6.2) stringi 1.5.3 2020-09-09 [1] CRAN (R 3.6.2) stringr 1.4.0 2019-02-10 [1] CRAN (R 3.6.0) testthat 3.0.0 2020-10-31 [1] CRAN (R 3.6.2) tibble 3.0.4 2020-10-12 [1] CRAN (R 3.6.2) tidyselect 1.1.0 2020-05-11 [1] CRAN (R 3.6.2) usethis 1.9.0.9000 2020-11-25 [1] Github (r-lib/usethis@2a7ee3b) vctrs 0.3.5 2020-11-17 [1] CRAN (R 3.6.2) viridis 0.5.1 2018-03-29 [1] CRAN (R 3.6.0) viridisLite 0.3.0 2018-02-01 [1] CRAN (R 3.6.0) wdman 0.2.5 2020-01-31 [1] CRAN (R 3.6.0) whereami 0.1.9 2020-09-24 [1] local withr 2.3.0 2020-09-22 [1] CRAN (R 3.6.2) xfun 0.19 2020-10-30 [1] CRAN (R 3.6.2) XML 3.98-1.20 2019-06-06 [1] CRAN (R 3.6.0) xml2 1.3.2 2020-04-23 [1] CRAN (R 3.6.2) xtable 1.8-4 2019-04-21 [1] CRAN (R 3.6.0) yaml 2.2.1 2020-02-01 [1] CRAN (R 3.6.0) [1] /Library/Frameworks/R.framework/Versions/3.6/Resources/library ```Example application or steps to reproduce the problem
```R install.packages('reactlog') remotes::install_github('yonicd/puzzlemath@17def58') library(reactlog) library(puzzlemath) library(shiny) reactlog::reactlog_enable() ### Pane Viewer ---- options(shiny.launch.browser = .rs.invokeShinyPaneViewer) shiny::reactlogReset() # the following is akin to running puzzlemath::run_app() shinyApp( ui = puzzlemath:::app_ui, server = puzzlemath:::app_server ) # save to disk cat(jsonlite::serializeJSON(shiny::reactlog()), file = "~/shinytest/puzzlemath_paneviewer.json",sep='\n') ### Window Viewer ---- options(shiny.launch.browser = .rs.invokeShinyWindowViewer) shiny::reactlogReset() shinyApp( ui = puzzlemath:::app_ui, server = puzzlemath:::app_server ) # save to disk cat(jsonlite::serializeJSON(shiny::reactlog()), file = "~/shinytest/puzzlemath_windowviewer.json",sep='\n') options(shiny.launch.browser = .rs.invokeShinyWindowExternal) shiny::reactlogReset() shinyApp( ui = puzzlemath:::app_ui, server = puzzlemath:::app_server ) # save to disk cat(jsonlite::serializeJSON(shiny::reactlog()), file = "~/shinytest/puzzlemath_windowexternal.json",sep='\n') ``` ### Describe the problem in detail