jupyter-book / mystmd

Command line tools for working with MyST Markdown.
https://mystmd.org/guide
MIT License
201 stars 61 forks source link

How to use myst-parser in browser to generate html from myst markdown string? #824

Closed fhg-isi closed 8 months ago

fhg-isi commented 9 months ago

If I try to use the html example given at

https://github.com/executablebooks/mystmd/tree/main/packages/myst-parser

I get the error Uncaught ReferenceError: unified is not defined

a) Could you please give a full example on how to use myst-parser in the browser to generate html from myst markdown string?

b) Also see https://stackoverflow.com/questions/77707969/how-to-use-myst-parser-for-sharepoint-web-part

welcome[bot] commented 9 months ago

Thanks for opening your first issue here! Engagement like this is essential for open source projects! :hugs:
If you haven't done so already, check out EBP's Code of Conduct. Also, please try to follow the issue template as it helps other community members to contribute more effectively.
If your issue is a feature request, others may react to it, to raise its prominence (see Feature Voting).
Welcome to the EBP community! :tada:

agoose77 commented 9 months ago

Hi @fhg-isi, thanks for opening this issue! It looks like our documentation is slightly out of date.

I'm new to the nitty-gritty of getting this up and running, but did some digging around. I can provide a solution, but do keep an eye on this thread in case one of the team provides a superior alternative.

The error that you're seeing is caused by missing JS dependencies. You need to ensure that your script is being executed in a context where those modules have been loaded. The easiest solution in most modern browsers is to use a <script type="module"> tag to make use of the import statement:

<html>
  <head>
  <link rel="stylesheet" type="text/css" href="myst.css">
  </head>
  <div id="output"></div>
  <script type="module">
    import { unified } from 'https://esm.sh/unified@10.1.2?bundle'
    import { mystParser } from 'https://esm.sh/myst-parser@1.0.21?bundle'
    import { State, transform, mystToHast, formatHtml } from 'https://esm.sh/myst-to-html@1.0.21?bundle'
    import rehypeStringify from 'https://esm.sh/rehype-stringify@9.0.3?bundle'

    const pipe = unified()
      .use(mystParser)
      .use(transform, new State())
      .use(mystToHast)
      .use(formatHtml)
      .use(rehypeStringify);
    const result = pipe.processSync(':::{important}\nHello to the world!\n:::');

    document.getElementById('output').innerHTML = result.value;

  </script>
  </body>
</html>

You can see that this is very close to what the documentation already suggests, but it's a mix of the Node and browser examples. You'll also need a stylesheet, (here, myst.css) of which an example is given in https://github.com/executablebooks/mystmd/blob/690f4695bb172d5a0aa05c0f4600064540528029/packages/myst-parser/myst.css. Note that this is not a published style sheet; we only use it internally for our demos. My thinking is that you'd want to fork this and tweak it. cc @rowanc1

riziles commented 9 months ago

@agoose77 , this is great! Any chance you can add an example with a LaTeX equation?

agoose77 commented 9 months ago

So (again), there might be a better way, but here's a start.

You can load KaTeX and massage the equations to be picked up by KaTeX's auto renderer:

import { unified } from 'unified'
import { mystParser } from 'myst-parser'
import rehypeDocument from 'rehype-document'
import { State, transform, mystToHast, formatHtml } from 'myst-to-html'
import rehypeStringify from 'rehype-stringify'
import mystToMd from 'myst-to-md';
import { selectAll } from 'unist-util-select';

// Prepare math for KaTeX auto renderer by wrapping in `$$`
function mathTransform(tree, file, opts) {
  const inlineNodes = selectAll('inlineMath', tree);
  inlineNodes.forEach((node) => {
    node.value = `$${node.value}$`
  });
  const displayNodes = selectAll('math', tree);
  displayNodes.forEach((node) => {
    node.value = `$$${node.value}$$`
  });
}
const mathPlugin =
  (opts) => (tree, file) => {
    mathTransform(tree, file, opts);
  };

// Create a AST document, or parse using mystmd
const pipe = unified()
  .use(mystParser)
  .use(transform, new State())
  .use(mathPlugin)
  .use(mystToHast)
  .use(formatHtml)
  // Load KaTeX
  .use(rehypeDocument, {
    css: ['myst.css', 'https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css'],
    js: ["https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js", "https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"],
    script: `document.addEventListener("DOMContentLoaded", (event) => {
        renderMathInElement(document.body, {
            delimiters: [{
                    left: "$$",
                    right: "$$",
                    display: true
                },
                {
                    left: "$",
                    right: "$",
                    display: false
                }
            ]
        });
    });`
  })
  .use(rehypeStringify);

// Demo
const result = pipe.processSync(`
:::{important}
Hello to the world!
:::

The math says {math}\`x + y \\frac{x}{y}\`
`);
console.log(result.value);

You don't have to use KaTeX, but I just knew it would be easy to get up and running with it.

riziles commented 9 months ago

Thanks @agoose77 ! This is great! I made some tweaks (see below) to make it a standalone HTML file. FYI, it doesn't look like the table alignment is working, but I believe this is a known issue: https://github.com/executablebooks/mystmd/issues/238

<!DOCTYPE html>
<html>

<head>
  <link rel="stylesheet" type="text/css" href="myst.css">
  <link rel="stylesheet" type="text/css" href='https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css'>
</head>
<div id="output"></div>
<script type="module">
  import { unified } from 'https://esm.sh/unified@10.1.2?bundle';
  import { mystParser } from 'https://esm.sh/myst-parser@1.0.21?bundle';
  import { State, transform, mystToHast, formatHtml } from 'https://esm.sh/myst-to-html@1.0.21?bundle';
  import rehypeStringify from 'https://esm.sh/rehype-stringify@9.0.3?bundle';
  import { selectAll } from 'https://esm.sh/unist-util-select@5.1.0?bundle';
  import renderMathInElement from "https://esm.sh/katex@0.16.9/dist/contrib/auto-render.mjs";

  // Prepare math for KaTeX auto renderer by wrapping in `$$`
  function mathTransform(tree, file, opts) {
    const inlineNodes = selectAll('inlineMath', tree);
    inlineNodes.forEach((node) => {
      node.value = `$${node.value}$`
    });
    const displayNodes = selectAll('math', tree);
    displayNodes.forEach((node) => {
      node.value = `$$${node.value}$$`
    });
  }
  const mathPlugin =
    (opts) => (tree, file) => {
      mathTransform(tree, file, opts);
    };

  // Create a AST document, or parse using mystmd
  const pipe = unified()
    .use(mystParser)
    .use(transform, new State())
    .use(mathPlugin)
    .use(mystToHast)
    .use(formatHtml)
    // Load KaTeX
    .use(rehypeStringify);

  // Demo
  const result = pipe.processSync(`
# Myst Example

:::{important}
Hello to the world!
:::

| left | center | right |
| :--- | :----: | ----: |
| a    | b      | c     |

:::{figure} https://source.unsplash.com/random/500x200/?mountain
:name: my-fig
:align: center

My **bold** mountain 🏔🚠.
:::

The math says $x^2 + \\big( y \\frac{x}{y} \\big)$
`);

  document.getElementById('output').innerHTML = result.value;

  // console.log(result.value);
  document.addEventListener("DOMContentLoaded", function () {
    renderMathInElement(document.body, {
      // customised options
      // • auto-render specific keys, e.g.:
      delimiters: [
        { left: '$$', right: '$$', display: true },
        { left: '$', right: '$', display: false },
        { left: '\\(', right: '\\)', display: false },
        { left: '\\[', right: '\\]', display: true }
      ],
      // • rendering keys, e.g.:
      throwOnError: false
    });
  });

</script>

</body>

</html>
fhg-isi commented 8 months ago

I asked here another follow up question: How to resolve relative links?

https://github.com/orgs/executablebooks/discussions/1125

riziles commented 8 months ago

@fhg-isi , I think for something sophisticated involving multiple markdown documents you may want to use the myst cli's build command to convert all the necessary files to a website as described here: https://mystmd.org/guide/quickstart-myst-websites . My example was more for an edge case where I wanted to dynamically incorporate some simple MyST-flavoured Markdown into a pre-existing webpage.

If you can't use the myst cli for some reason, I'm sure that what you want to do is possible, but I think it's probably more of a unified / remark question than a MyST question (although mine certainly was, too, so please excuse my hypocrisy).

fhg-isi commented 8 months ago

Thank you. Solved it using regular expressions, see https://github.com/orgs/executablebooks/discussions/1125