Skip to content

Commit

Permalink
fix: katex parser
Browse files Browse the repository at this point in the history
Signed-off-by: Innei <[email protected]>
  • Loading branch information
Innei committed Sep 18, 2024
1 parent 2cce561 commit 86c114a
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 89 deletions.
2 changes: 1 addition & 1 deletion src/components/modules/dashboard/writing/EditorLayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export const EditorLayer: FC<{
mainClassName,
)}
>
<div className="flex grow flex-col overflow-auto">{ContentEl}</div>
<div className="flex grow flex-col overflow-hidden">{ContentEl}</div>

{FooterEl}
</main>
Expand Down
36 changes: 13 additions & 23 deletions src/components/modules/dashboard/writing/Writing.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,15 @@ import { callCommand } from '@milkdown/utils'
import { produce } from 'immer'
import { atom, useAtomValue, useSetAtom, useStore } from 'jotai'
import type { FC } from 'react'
import React, { isValidElement, useEffect, useRef } from 'react'
import React, { isValidElement } from 'react'

import { SimpleIconsMermaid } from '~/components/icons/mermaid'
import { useEditorCtx } from '~/components/ui/editor/Milkdown/ctx'
import { excalidrawSchema } from '~/components/ui/editor/Milkdown/plugins/Excalidraw'
import { TextArea } from '~/components/ui/input'
import { useEventCallback } from '~/hooks/common/use-event-callback'
import { clsxm } from '~/lib/helper'
import { jotaiStore } from '~/lib/store'

import type { MilkdownRef } from '../../../ui/editor'
import { MilkdownEditor } from '../../../ui/editor'
import { useBaseWritingContext } from './BaseWritingProvider'
import { TitleInput } from './TitleInput'

Expand Down Expand Up @@ -188,31 +186,23 @@ const Editor = () => {
})
})
const store = useStore()
const handleMarkdownChange = useEventCallback(setText)
const milkdownRef = useRef<MilkdownRef>(null)

useEffect(() => {
jotaiStore.set(milkdownRefAtom, milkdownRef.current)
return () => {
jotaiStore.set(milkdownRefAtom, null)
}
}, [])

return (
<>
<MenuBar />
<div
className={clsxm(
'relative h-0 grow overflow-auto rounded-xl border p-3 duration-200 focus-within:border-accent',
'border-zinc-200 bg-white placeholder:text-slate-500 focus-visible:border-accent dark:border-neutral-800 dark:bg-zinc-900',
)}
>
<MilkdownEditor
{/* <MenuBar /> */}

{/* <MilkdownEditor
ref={milkdownRef}
onMarkdownChange={handleMarkdownChange}
initialMarkdown={store.get(ctxAtom).text}
/>
</div>
/> */}
<TextArea
className="bg-base-100"
defaultValue={store.get(ctxAtom).text}
onChange={(e) => {
setText(e.target.value)
}}
/>
</>
)
}
Expand Down
20 changes: 9 additions & 11 deletions src/components/modules/toc/TocTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,17 +145,15 @@ export const TocTree: Component<
className={clsx('scrollbar-none overflow-auto', scrollClassname)}
ref={scrollContainerRef}
>
{toc?.map((heading) => {
return (
<MemoedItem
heading={heading}
isActive={heading.anchorId === activeId}
key={heading.title}
rootDepth={rootDepth}
onClick={handleScrollTo}
/>
)
})}
{toc?.map((heading) => (
<MemoedItem
heading={heading}
isActive={heading.anchorId === activeId}
key={`${heading.title}-${heading.index}`}
rootDepth={rootDepth}
onClick={handleScrollTo}
/>
))}
</ul>
{accessoryElement && (
<li className="shrink-0">
Expand Down
30 changes: 30 additions & 0 deletions src/components/ui/katex/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import katex from 'katex'
import type { FC } from 'react'
import { useMemo } from 'react'

type KateXProps = {
children: string
mode?: string // If `display` the math will be rendered in display mode. Otherwise the math will be rendered in inline mode.
}

export const KateX: FC<KateXProps> = (props) => {
const { children, mode } = props

const displayMode = mode === 'display'

const throwOnError = false // render unsupported commands as text instead of throwing a `ParseError`

return (
<span
dangerouslySetInnerHTML={useMemo(
() => ({
__html: katex.renderToString(children, {
displayMode,
throwOnError,
}),
}),
[children, displayMode, throwOnError],
)}
/>
)
}
2 changes: 1 addition & 1 deletion src/components/ui/markdown/Markdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ export const Markdown: FC<MdProps & MarkdownToJSX.Options & PropsWithChildren> =
<Tag key={state?.key} start={node.start}>
{node.items.map((item: any, i: number) => {
let className = ''
if (item[0]?.type == 'gfmTask') {
if (item[0]?.type === 'gfmTask') {
className = 'list-none flex items-center'
}

Expand Down
64 changes: 11 additions & 53 deletions src/components/ui/markdown/parsers/katex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,77 +2,35 @@

import 'katex/dist/katex.min.css'

import { useIsomorphicLayoutEffect } from 'foxact/use-isomorphic-layout-effect'
import type { MarkdownToJSX } from 'markdown-to-jsx'
import { blockRegex, Priority, simpleInlineRegex } from 'markdown-to-jsx'
import type { FC } from 'react'
import { useState } from 'react'

import { KateX } from '../../katex'

// $ c = \pm\sqrt{a^2 + b^2} $
export const KateXRule: MarkdownToJSX.Rule = {
match: simpleInlineRegex(
/^(?!\\)\$\s*((?:\[(?:[^$]|(?=\\)\$)*?\]|<(?:[^$]|(?=\\)\$)*?>(?:(?:[^$]|(?=\\)\$)*?<(?:[^$]|(?=\\)\$)*?>)?|`(?:[^$]|(?=\\)\$)*?`|[^$]|(?=\\)\$)*?)\s*(?!\\)\$/,
),
order: Priority.MED,
match: (source) => {
return simpleInlineRegex(
/^(?!\\)\$\s*((?:\[(?:[^$]|(?=\\)\$)*?\]|<(?:[^$]|(?=\\)\$)*?>(?:(?:[^$]|(?=\\)\$)*?<(?:[^$]|(?=\\)\$)*?>)?|`(?:[^$]|(?=\\)\$)*?`|[^$]|(?=\\)\$)*?)\s*(?!\\)\$/,
)(source, { inline: true })
},
order: Priority.LOW,
parse(capture) {
return {
type: 'kateX',
katex: capture[1],
}
},
react(node, output, state) {
return <LateX key={state?.key}>{node.katex}</LateX>
return <KateX key={state?.key}>{node.katex}</KateX>
},
}

type LateXProps = {
children: string
mode?: string // If `display` the math will be rendered in display mode. Otherwise the math will be rendered in inline mode.
}

const LateX: FC<LateXProps> = (props) => {
const { children, mode } = props

const [html, setHtml] = useState('')

const displayMode = mode === 'display'

const throwOnError = false // render unsupported commands as text instead of throwing a `ParseError`

useIsomorphicLayoutEffect(() => {
let isMounted = true
import('katex').then((katex) => {
if (!isMounted) return
// biome-ignore lint/correctness/noUnsafeOptionalChaining: <explanation>
const html = (katex?.default?.renderToString || katex?.renderToString)(
children,
{
displayMode,
throwOnError,
},
)

setHtml(html)
})
return () => {
isMounted = false
}
}, [])

return (
<span
dangerouslySetInnerHTML={{ __html: html }}
className="katex-container"
/>
)
}

export const KateXBlockRule: MarkdownToJSX.Rule = {
match: blockRegex(
new RegExp(`^\\s*\\$\\$ *(?<content>[\\s\\S]+?)\\s*\\$\\$ *(?:\n *)+\n?`),
),

order: Priority.LOW,
order: Priority.MED,
parse(capture) {
return {
type: 'kateXBlock',
Expand All @@ -82,7 +40,7 @@ export const KateXBlockRule: MarkdownToJSX.Rule = {
react(node, _, state?) {
return (
<div className="scrollbar-none overflow-auto" key={state?.key}>
<LateX mode="display">{node.groups.content}</LateX>
<KateX mode="display">{node.groups.content}</KateX>
</div>
)
},
Expand Down

0 comments on commit 86c114a

Please sign in to comment.