mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
feat(web): add LaTeX math formula rendering with KaTeX (#436)
* feat(web): add LaTeX math formula rendering with KaTeX Add remark-math + rehype-katex to the markdown rendering pipeline so inline ($...$) and display ($$...$$) math formulas are rendered as proper KaTeX output in chat messages and tool results. Closes #237 * fix(web): disable single-dollar math parsing and add KaTeX to reasoning - Set singleDollarTextMath: false to prevent $HOME, $PATH etc from being misinterpreted as math formulas. Only $$...$$ (display) is parsed; inline math requires explicit \(...\) or $$...$$. - Add rehypePlugins to the reasoning renderer so math formulas render consistently across chat, tool results, and reasoning blocks. * refactor(web): use satisfies for type-safe plugin exports Replace any[] with satisfies NonNullable<MarkdownTextPrimitiveProps[...]> to preserve type safety on the shared plugin lists without needing eslint suppressions. * fix(web): enable single-dollar inline math syntax Re-enable $...$ parsing (remark-math default) so inline formulas like $E=mc^2$ render correctly. Shell variables like $HOME typically appear inside code spans/blocks which remark-math does not parse, so false positives are minimal in practice.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import type { MarkdownTextPrimitiveProps } from '@assistant-ui/react-markdown'
|
||||
import { MarkdownTextPrimitive } from '@assistant-ui/react-markdown'
|
||||
import { TextMessagePartProvider } from '@assistant-ui/react'
|
||||
import { MARKDOWN_PLUGINS, defaultComponents } from '@/components/assistant-ui/markdown-text'
|
||||
import { MARKDOWN_PLUGINS, MARKDOWN_REHYPE_PLUGINS, defaultComponents } from '@/components/assistant-ui/markdown-text'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
interface MarkdownRendererProps {
|
||||
@@ -18,6 +18,7 @@ function MarkdownContent(props: MarkdownRendererProps) {
|
||||
<TextMessagePartProvider text={props.content}>
|
||||
<MarkdownTextPrimitive
|
||||
remarkPlugins={MARKDOWN_PLUGINS}
|
||||
rehypePlugins={MARKDOWN_REHYPE_PLUGINS}
|
||||
components={mergedComponents}
|
||||
className={cn('aui-md min-w-0 max-w-full break-words text-base')}
|
||||
/>
|
||||
|
||||
@@ -6,13 +6,18 @@ import {
|
||||
type CodeHeaderProps,
|
||||
} from '@assistant-ui/react-markdown'
|
||||
import remarkGfm from 'remark-gfm'
|
||||
import remarkMath from 'remark-math'
|
||||
import rehypeKatex from 'rehype-katex'
|
||||
import remarkDisableIndentedCode from '@/lib/remark-disable-indented-code'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { SyntaxHighlighter } from '@/components/assistant-ui/shiki-highlighter'
|
||||
import { useCopyToClipboard } from '@/hooks/useCopyToClipboard'
|
||||
import { CopyIcon, CheckIcon } from '@/components/icons'
|
||||
|
||||
export const MARKDOWN_PLUGINS = [remarkGfm, remarkDisableIndentedCode]
|
||||
import type { MarkdownTextPrimitiveProps } from '@assistant-ui/react-markdown'
|
||||
|
||||
export const MARKDOWN_PLUGINS = [remarkGfm, remarkMath, remarkDisableIndentedCode] satisfies NonNullable<MarkdownTextPrimitiveProps['remarkPlugins']>
|
||||
export const MARKDOWN_REHYPE_PLUGINS = [rehypeKatex] satisfies NonNullable<MarkdownTextPrimitiveProps['rehypePlugins']>
|
||||
|
||||
function CodeHeader(props: CodeHeaderProps) {
|
||||
const { copied, copy } = useCopyToClipboard()
|
||||
@@ -225,6 +230,7 @@ export function MarkdownText() {
|
||||
return (
|
||||
<MarkdownTextPrimitive
|
||||
remarkPlugins={MARKDOWN_PLUGINS}
|
||||
rehypePlugins={MARKDOWN_REHYPE_PLUGINS}
|
||||
components={defaultComponents}
|
||||
className={cn('aui-md min-w-0 max-w-full break-words text-base')}
|
||||
/>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useState, useEffect, type FC, type PropsWithChildren } from 'react'
|
||||
import { useMessage } from '@assistant-ui/react'
|
||||
import { MarkdownTextPrimitive } from '@assistant-ui/react-markdown'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { defaultComponents, MARKDOWN_PLUGINS } from '@/components/assistant-ui/markdown-text'
|
||||
import { defaultComponents, MARKDOWN_PLUGINS, MARKDOWN_REHYPE_PLUGINS } from '@/components/assistant-ui/markdown-text'
|
||||
|
||||
function ChevronIcon(props: { className?: string; open?: boolean }) {
|
||||
return (
|
||||
@@ -40,6 +40,7 @@ export const Reasoning: FC = () => {
|
||||
return (
|
||||
<MarkdownTextPrimitive
|
||||
remarkPlugins={MARKDOWN_PLUGINS}
|
||||
rehypePlugins={MARKDOWN_REHYPE_PLUGINS}
|
||||
components={defaultComponents}
|
||||
className={cn('aui-reasoning-content min-w-0 max-w-full break-words text-sm text-[var(--app-hint)]')}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user