Skip to content

Commit 86c114a

Browse files
committed
fix: katex parser
Signed-off-by: Innei <[email protected]>
1 parent 2cce561 commit 86c114a

File tree

6 files changed

+65
-89
lines changed

6 files changed

+65
-89
lines changed

src/components/modules/dashboard/writing/EditorLayer.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export const EditorLayer: FC<{
3838
mainClassName,
3939
)}
4040
>
41-
<div className="flex grow flex-col overflow-auto">{ContentEl}</div>
41+
<div className="flex grow flex-col overflow-hidden">{ContentEl}</div>
4242

4343
{FooterEl}
4444
</main>

src/components/modules/dashboard/writing/Writing.tsx

+13-23
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,15 @@ import { callCommand } from '@milkdown/utils'
1111
import { produce } from 'immer'
1212
import { atom, useAtomValue, useSetAtom, useStore } from 'jotai'
1313
import type { FC } from 'react'
14-
import React, { isValidElement, useEffect, useRef } from 'react'
14+
import React, { isValidElement } from 'react'
1515

1616
import { SimpleIconsMermaid } from '~/components/icons/mermaid'
1717
import { useEditorCtx } from '~/components/ui/editor/Milkdown/ctx'
1818
import { excalidrawSchema } from '~/components/ui/editor/Milkdown/plugins/Excalidraw'
19+
import { TextArea } from '~/components/ui/input'
1920
import { useEventCallback } from '~/hooks/common/use-event-callback'
20-
import { clsxm } from '~/lib/helper'
21-
import { jotaiStore } from '~/lib/store'
2221

2322
import type { MilkdownRef } from '../../../ui/editor'
24-
import { MilkdownEditor } from '../../../ui/editor'
2523
import { useBaseWritingContext } from './BaseWritingProvider'
2624
import { TitleInput } from './TitleInput'
2725

@@ -188,31 +186,23 @@ const Editor = () => {
188186
})
189187
})
190188
const store = useStore()
191-
const handleMarkdownChange = useEventCallback(setText)
192-
const milkdownRef = useRef<MilkdownRef>(null)
193-
194-
useEffect(() => {
195-
jotaiStore.set(milkdownRefAtom, milkdownRef.current)
196-
return () => {
197-
jotaiStore.set(milkdownRefAtom, null)
198-
}
199-
}, [])
200189

201190
return (
202191
<>
203-
<MenuBar />
204-
<div
205-
className={clsxm(
206-
'relative h-0 grow overflow-auto rounded-xl border p-3 duration-200 focus-within:border-accent',
207-
'border-zinc-200 bg-white placeholder:text-slate-500 focus-visible:border-accent dark:border-neutral-800 dark:bg-zinc-900',
208-
)}
209-
>
210-
<MilkdownEditor
192+
{/* <MenuBar /> */}
193+
194+
{/* <MilkdownEditor
211195
ref={milkdownRef}
212196
onMarkdownChange={handleMarkdownChange}
213197
initialMarkdown={store.get(ctxAtom).text}
214-
/>
215-
</div>
198+
/> */}
199+
<TextArea
200+
className="bg-base-100"
201+
defaultValue={store.get(ctxAtom).text}
202+
onChange={(e) => {
203+
setText(e.target.value)
204+
}}
205+
/>
216206
</>
217207
)
218208
}

src/components/modules/toc/TocTree.tsx

+9-11
Original file line numberDiff line numberDiff line change
@@ -145,17 +145,15 @@ export const TocTree: Component<
145145
className={clsx('scrollbar-none overflow-auto', scrollClassname)}
146146
ref={scrollContainerRef}
147147
>
148-
{toc?.map((heading) => {
149-
return (
150-
<MemoedItem
151-
heading={heading}
152-
isActive={heading.anchorId === activeId}
153-
key={heading.title}
154-
rootDepth={rootDepth}
155-
onClick={handleScrollTo}
156-
/>
157-
)
158-
})}
148+
{toc?.map((heading) => (
149+
<MemoedItem
150+
heading={heading}
151+
isActive={heading.anchorId === activeId}
152+
key={`${heading.title}-${heading.index}`}
153+
rootDepth={rootDepth}
154+
onClick={handleScrollTo}
155+
/>
156+
))}
159157
</ul>
160158
{accessoryElement && (
161159
<li className="shrink-0">

src/components/ui/katex/index.tsx

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import katex from 'katex'
2+
import type { FC } from 'react'
3+
import { useMemo } from 'react'
4+
5+
type KateXProps = {
6+
children: string
7+
mode?: string // If `display` the math will be rendered in display mode. Otherwise the math will be rendered in inline mode.
8+
}
9+
10+
export const KateX: FC<KateXProps> = (props) => {
11+
const { children, mode } = props
12+
13+
const displayMode = mode === 'display'
14+
15+
const throwOnError = false // render unsupported commands as text instead of throwing a `ParseError`
16+
17+
return (
18+
<span
19+
dangerouslySetInnerHTML={useMemo(
20+
() => ({
21+
__html: katex.renderToString(children, {
22+
displayMode,
23+
throwOnError,
24+
}),
25+
}),
26+
[children, displayMode, throwOnError],
27+
)}
28+
/>
29+
)
30+
}

src/components/ui/markdown/Markdown.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -275,7 +275,7 @@ export const Markdown: FC<MdProps & MarkdownToJSX.Options & PropsWithChildren> =
275275
<Tag key={state?.key} start={node.start}>
276276
{node.items.map((item: any, i: number) => {
277277
let className = ''
278-
if (item[0]?.type == 'gfmTask') {
278+
if (item[0]?.type === 'gfmTask') {
279279
className = 'list-none flex items-center'
280280
}
281281

src/components/ui/markdown/parsers/katex.tsx

+11-53
Original file line numberDiff line numberDiff line change
@@ -2,77 +2,35 @@
22

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

5-
import { useIsomorphicLayoutEffect } from 'foxact/use-isomorphic-layout-effect'
65
import type { MarkdownToJSX } from 'markdown-to-jsx'
76
import { blockRegex, Priority, simpleInlineRegex } from 'markdown-to-jsx'
8-
import type { FC } from 'react'
9-
import { useState } from 'react'
7+
8+
import { KateX } from '../../katex'
109

1110
// $ c = \pm\sqrt{a^2 + b^2} $
1211
export const KateXRule: MarkdownToJSX.Rule = {
13-
match: simpleInlineRegex(
14-
/^(?!\\)\$\s*((?:\[(?:[^$]|(?=\\)\$)*?\]|<(?:[^$]|(?=\\)\$)*?>(?:(?:[^$]|(?=\\)\$)*?<(?:[^$]|(?=\\)\$)*?>)?|`(?:[^$]|(?=\\)\$)*?`|[^$]|(?=\\)\$)*?)\s*(?!\\)\$/,
15-
),
16-
order: Priority.MED,
12+
match: (source) => {
13+
return simpleInlineRegex(
14+
/^(?!\\)\$\s*((?:\[(?:[^$]|(?=\\)\$)*?\]|<(?:[^$]|(?=\\)\$)*?>(?:(?:[^$]|(?=\\)\$)*?<(?:[^$]|(?=\\)\$)*?>)?|`(?:[^$]|(?=\\)\$)*?`|[^$]|(?=\\)\$)*?)\s*(?!\\)\$/,
15+
)(source, { inline: true })
16+
},
17+
order: Priority.LOW,
1718
parse(capture) {
1819
return {
1920
type: 'kateX',
2021
katex: capture[1],
2122
}
2223
},
2324
react(node, output, state) {
24-
return <LateX key={state?.key}>{node.katex}</LateX>
25+
return <KateX key={state?.key}>{node.katex}</KateX>
2526
},
2627
}
27-
28-
type LateXProps = {
29-
children: string
30-
mode?: string // If `display` the math will be rendered in display mode. Otherwise the math will be rendered in inline mode.
31-
}
32-
33-
const LateX: FC<LateXProps> = (props) => {
34-
const { children, mode } = props
35-
36-
const [html, setHtml] = useState('')
37-
38-
const displayMode = mode === 'display'
39-
40-
const throwOnError = false // render unsupported commands as text instead of throwing a `ParseError`
41-
42-
useIsomorphicLayoutEffect(() => {
43-
let isMounted = true
44-
import('katex').then((katex) => {
45-
if (!isMounted) return
46-
// biome-ignore lint/correctness/noUnsafeOptionalChaining: <explanation>
47-
const html = (katex?.default?.renderToString || katex?.renderToString)(
48-
children,
49-
{
50-
displayMode,
51-
throwOnError,
52-
},
53-
)
54-
55-
setHtml(html)
56-
})
57-
return () => {
58-
isMounted = false
59-
}
60-
}, [])
61-
62-
return (
63-
<span
64-
dangerouslySetInnerHTML={{ __html: html }}
65-
className="katex-container"
66-
/>
67-
)
68-
}
69-
7028
export const KateXBlockRule: MarkdownToJSX.Rule = {
7129
match: blockRegex(
7230
new RegExp(`^\\s*\\$\\$ *(?<content>[\\s\\S]+?)\\s*\\$\\$ *(?:\n *)+\n?`),
7331
),
7432

75-
order: Priority.LOW,
33+
order: Priority.MED,
7634
parse(capture) {
7735
return {
7836
type: 'kateXBlock',
@@ -82,7 +40,7 @@ export const KateXBlockRule: MarkdownToJSX.Rule = {
8240
react(node, _, state?) {
8341
return (
8442
<div className="scrollbar-none overflow-auto" key={state?.key}>
85-
<LateX mode="display">{node.groups.content}</LateX>
43+
<KateX mode="display">{node.groups.content}</KateX>
8644
</div>
8745
)
8846
},

0 commit comments

Comments
 (0)