posit-dev / r-shinylive

https://posit-dev.github.io/r-shinylive/
Other
159 stars 17 forks source link

MathJax Equations Render Successfully in UI, but Encounter Issues Within Output #49

Closed ydkristanto closed 8 months ago

ydkristanto commented 8 months ago

I've observed a peculiar behavior with MathJax equations in my Shiny app. When I directly include the equations in the UI using the following code:

tags$head(
  tags$script(
    src = "https://polyfill.io/v3/polyfill.min.js?features=es6",
    async = TRUE
  ),
  tags$script(
    src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js",
    async = TRUE,
    id = "MathJax-script"
  )
)

they render successfully. However, when attempting to display the same equations within an output element, such as using renderText or renderUI, issues arise.

I appreciate any insights or guidance in resolving this behavior. Thank you for your assistance.

gadenbuie commented 8 months ago

Hi @ydkristanto! Can you please include a small, complete app that reproduces your issue? Having a full example to run will greatly increase the chances that we'll be able to help you.

ydkristanto commented 8 months ago

At first, I used codes from this example and made a simple Shiny app from it. The resulted app is like this.

library(shiny)

ui <- fluidPage(
  title = 'MathJax Examples',
  withMathJax(),
  helpText('An irrational number \\(\\sqrt{2}\\)
           and a fraction $$1-\\frac{1}{2}$$'),
  helpText('and a fact about \\(\\pi\\):
           $$\\frac2\\pi = \\frac{\\sqrt2}2 \\cdot
           \\frac{\\sqrt{2+\\sqrt2}}2 \\cdot
           \\frac{\\sqrt{2+\\sqrt{2+\\sqrt2}}}2 \\cdots$$'),
  uiOutput('ex1'),
  uiOutput('ex2'),
  uiOutput('ex3'),
  uiOutput('ex4'),
  checkboxInput('ex5_visible', 'Show Example 5', FALSE),
  uiOutput('ex5')
)

server <- function(input, output, session) {
  output$ex1 <- renderUI({
    withMathJax(helpText('Dynamic output 1:  $$\\alpha^2$$'))
  })
  output$ex2 <- renderUI({
    withMathJax(
      helpText('and output 2 $$3^2+4^2=5^2$$'),
      helpText('and output 3 $$\\sin^2(\\theta)+\\cos^2(\\theta)=1$$')
    )
  })
  output$ex3 <- renderUI({
    withMathJax(
      helpText('The busy Cauchy distribution
               $$\\frac{1}{\\pi\\gamma\\,\\left[1 +
               \\left(\\frac{x-x_0}{\\gamma}\\right)^2\\right]}\\!$$'))
  })
  output$ex4 <- renderUI({
    invalidateLater(5000, session)
    x <- round(rcauchy(1), 3)
    withMathJax(sprintf("If \\(X\\) is a Cauchy random variable, then
                        $$P(X \\leq %.03f ) = %.03f$$", x, pcauchy(x)))
  })
  output$ex5 <- renderUI({
    if (!input$ex5_visible) return()
    withMathJax(
      helpText('You do not see me initially: $$e^{i \\pi} + 1 = 0$$')
    )
  })
}

shinyApp(ui, server)

However, all of the math equations are not rendered when the app is deployed with shinylive. Then, I modify the app to be like this.

library(shiny)

ui <- fluidPage(
  title = 'MathJax Examples',
  tags$head(
    tags$script(
      src = "https://polyfill.io/v3/polyfill.min.js?features=es6",
      async = TRUE
    ),
    tags$script(
      src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js",
      async = TRUE,
      id = "MathJax-script"
    )
    ),
  withMathJax(),
  helpText('An irrational number \\(\\sqrt{2}\\)
           and a fraction $$1-\\frac{1}{2}$$'),
  helpText('and a fact about \\(\\pi\\):
           $$\\frac2\\pi = \\frac{\\sqrt2}2 \\cdot
           \\frac{\\sqrt{2+\\sqrt2}}2 \\cdot
           \\frac{\\sqrt{2+\\sqrt{2+\\sqrt2}}}2 \\cdots$$'),
  uiOutput('ex1'),
  uiOutput('ex2'),
  uiOutput('ex3'),
  uiOutput('ex4'),
  checkboxInput('ex5_visible', 'Show Example 5', FALSE),
  uiOutput('ex5')
)

server <- function(input, output, session) {
  output$ex1 <- renderUI({
    withMathJax(helpText('Dynamic output 1:  $$\\alpha^2$$'))
  })
  output$ex2 <- renderUI({
    withMathJax(
      helpText('and output 2 $$3^2+4^2=5^2$$'),
      helpText('and output 3 $$\\sin^2(\\theta)+\\cos^2(\\theta)=1$$')
    )
  })
  output$ex3 <- renderUI({
    withMathJax(
      helpText('The busy Cauchy distribution
               $$\\frac{1}{\\pi\\gamma\\,\\left[1 +
               \\left(\\frac{x-x_0}{\\gamma}\\right)^2\\right]}\\!$$'))
  })
  output$ex4 <- renderUI({
    invalidateLater(5000, session)
    x <- round(rcauchy(1), 3)
    withMathJax(sprintf("If \\(X\\) is a Cauchy random variable, then
                        $$P(X \\leq %.03f ) = %.03f$$", x, pcauchy(x)))
  })
  output$ex5 <- renderUI({
    if (!input$ex5_visible) return()
    withMathJax(
      helpText('You do not see me initially: $$e^{i \\pi} + 1 = 0$$')
    )
  })
}

shinyApp(ui, server)

The equations in the HTML static contents work fine, but not in the dynamic output. Any help will be appreciated.

georgestagg commented 8 months ago

It looks like the default MathJax URL used by Shiny (https://mathjax.rstudio.com/latest/MathJax.js) is not being served with the HTTP header Cross-Origin-Resource-Policy: cross-origin. As such, I think the browser is blocking it from being loaded in the embedded iframe that shows the resulting Shinylive app on the page.

Switching to a URL that serves MathJax with that header should avoid the problem, for example by modifying the start of your app to read:

library(shiny)
options(shiny.mathjax.url = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js")

Example app: https://shinylive.io/r/editor/#code=No...AA

ydkristanto commented 8 months ago

That's great! Thank you, @georgestagg.