Skip to content

Commit

Permalink
fix: avoid conflicts when multiple markdown cards are on the same page
Browse files Browse the repository at this point in the history
  • Loading branch information
marek-mihok committed Nov 14, 2023
1 parent 8e9b726 commit a6ed945
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 22 deletions.
23 changes: 7 additions & 16 deletions py/examples/markdown.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
___
The **quick** __brown__ *fox* **_jumped_ over** the ~~lazy~~ _dog_.
This is <sup>superscript</sup> and this is <sub>subscript</sub>!
Block quote:
> The quick brown fox jumped over the lazy dog.
Expand All @@ -37,12 +39,6 @@
- First nested list item
- Second nested list item
<!-- This content will not appear in the rendered Markdown -->
Ignore \*markdown\* formatting.
This framework is made by [h2o.ai](https://h2o.ai)
Image:
![Monty Python](https://upload.wikimedia.org/wikipedia/en/c/cb/Flyingcircus_2.jpg)
Expand All @@ -61,8 +57,7 @@
Inline code:
Use `git status` to list all new or modified files that haven't yet been committed.
Use `ui.markdown_card` to start creating your own markdown content!
Code block:
Expand All @@ -81,17 +76,13 @@ async def serve(q: Q):
await q.page.save()
```
This is <sup>superscript</sup> and this is <sub>subscript</sub>!
<!-- This content will not appear in the rendered Markdown -->
Linking
--------------------
Ignore \*markdown\* formatting.
Search with [Google][1],
[Yahoo][2] or [MSN][3].
Linking:
[1]: http://google.com/ "Google"
[2]: http://search.yahoo.com/ "Yahoo Search"
[3]: http://search.msn.com/ "MSN Search"
This framework is made by [h2o.ai](https://h2o.ai)
'''

@app('/demo')
Expand Down
14 changes: 8 additions & 6 deletions ui/src/markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ const
}
},
})
const highlightSyntax = async (str: S, language: S, codeBlockId: S) => {
const codeBlock = document.getElementById(codeBlockId)
const highlightSyntax = async (str: S, language: S, codeElementId: S) => {
const codeBlock = document.getElementById(codeElementId)
if (!codeBlock) return ''
if (language) {
try {
Expand Down Expand Up @@ -81,21 +81,23 @@ const highlightSyntax = async (str: S, language: S, codeBlockId: S) => {
return highlightedCode
}

export const Markdown = ({ source, compact = true }: { source: S, compact?: B }) => {
export const Markdown = ({ source, name, compact = true }: { source: S, name: S, compact?: B }) => {
const
prevHighlights = React.useRef<S[]>([]), // Prevent flicker during streaming.
codeBlockIdx = React.useRef(0), // MarkdownIt parses code blocks sequentially, which is a problem for streaming.
markdown = React.useMemo(() => MarkdownIt({
html: true, linkify: true, typographer: true, highlight: (str, lang) => {
const codeBlockId = codeBlockIdx.current.toString()
// Use the unique html element id to avoid conflicts when multiple markdown cards are rendered on the same page.
const codeElementId = `${name}-${codeBlockId}`
if (prevHighlights.current.length === codeBlockIdx.current) prevHighlights.current.push('')

// HACK: MarkdownIt does not support async rules.
// https://github.com/markdown-it/markdown-it/blob/master/docs/development.md#i-need-async-rule-how-to-do-it
setTimeout(async () => prevHighlights.current[+codeBlockId] = await highlightSyntax(str, lang, codeBlockId), 0)
setTimeout(async () => prevHighlights.current[+codeBlockId] = await highlightSyntax(str, lang, codeElementId), 0)

// TODO: Sanitize the HTML.
const ret = `<code id='${codeBlockId}' class="hljs ${css.codeblock}">${prevHighlights.current[codeBlockIdx.current] || str}</code>`
const ret = `<code id='${codeElementId}' class="hljs ${css.codeblock}">${prevHighlights.current[codeBlockIdx.current] || str}</code>`
codeBlockIdx.current++
return ret
}
Expand Down Expand Up @@ -163,7 +165,7 @@ export const
<div data-test={name} className={css.card}>
{title && <div className='wave-s12 wave-w6'>{title}</div>}
<div className={css.body}>
<Markdown source={substitute(state.content, data)} compact={state.compact} />
<Markdown source={substitute(state.content, data)} name={name} compact={state.compact} />
</div>
</div>
)
Expand Down

0 comments on commit a6ed945

Please sign in to comment.