"use client"
import katex from "katex"
import React, { useCallback } from "react"
import ReactMarkdown, { Components } from "react-markdown"
import remarkGfm from "remark-gfm"
import "katex/dist/katex.min.css"
import { TableRowCard } from "../cards/table-row-card"
import { Accordion } from "./accordion"
import Prism from "prismjs"
import rehypeRaw from "rehype-raw"
import "prismjs/themes/prism-tomorrow.css"
import "prismjs/components/prism-javascript"
import "prismjs/components/prism-typescript"
import "prismjs/components/prism-jsx"
import "prismjs/components/prism-tsx"
import "prismjs/components/prism-json"
import "prismjs/components/prism-bash"
import "prismjs/components/prism-css"
import "prismjs/components/prism-markdown"
import "prismjs/components/prism-yaml"
import "prismjs/components/prism-python"
import "prismjs/components/prism-rust"
import "prismjs/components/prism-solidity"
import { AppLink } from "../app-link"
import { Icons } from "../icons"
const SCROLL_OFFSET = 150
const scrollToElementWithOffset = (target: HTMLElement) => {
const rect = target.getBoundingClientRect()
const targetPosition = rect.top + window.pageYOffset - SCROLL_OFFSET
// Set margin before scrolling
target.style.scrollMarginTop = `${SCROLL_OFFSET}px`
requestAnimationFrame(() => {
window.scrollTo({
top: targetPosition,
behavior: "smooth",
})
})
}
// Helper function to convert double dollar math blocks to a custom markdown syntax
const preprocessMathBlocks = (content: string): string => {
// Replace $$...$$ with a custom markdown syntax that won't interfere with footnotes
return content.replace(/\$\$([\s\S]*?)\$\$/g, (match, mathContent) => {
const encodedMath = Buffer.from(mathContent.trim()).toString("base64")
return `\n\n
{children}
)
}
const HeadingLink = ({
level,
children,
}: {
level: number
children: React.ReactNode
}) => {
const [copied, setCopied] = React.useState(false)
const [showLink, setShowLink] = React.useState(false)
const id = React.useMemo(() => {
if (typeof children === "string") {
return generateSectionId(children)
}
const text = React.Children.toArray(children)
.map((child) => {
if (typeof child === "string") return child
if (
React.isValidElement(child) &&
typeof child.props?.children === "string"
) {
return child.props.children
}
return ""
})
.join("")
return generateSectionId(text)
}, [children])
const copyToClipboard = React.useCallback(() => {
const url = new URL(window.location.href)
url.hash = id
navigator.clipboard.writeText(url.toString())
setCopied(true)
setTimeout(() => setCopied(false), 2000)
}, [id])
const headingClasses = React.useMemo(() => {
switch (level) {
case 1:
return "text-4xl md:text-5xl font-bold"
case 2:
return "text-4xl"
case 3:
return "text-3xl"
case 4:
return "text-xl"
case 5:
return "text-lg font-bold"
default:
return "text-md font-bold"
}
}, [level])
const Component = React.createElement(
`h${level}` as keyof JSX.IntrinsicElements,
{
id,
className: `group relative flex items-center gap-2 text-primary ${headingClasses}`,
onMouseEnter: () => setShowLink(true),
onMouseLeave: () => setShowLink(false),
},
<>
{children}
>
)
return Component
}
const REACT_MARKDOWN_CONFIG = (darkMode: boolean): CustomComponents => ({
a: ({ href, children, ...props }) => {
if (href?.startsWith("#")) {
return (
{children}
)
},
p: ({ node, children }: { node: any; children: React.ReactNode }) => {
const childArray = React.Children.toArray(children)
const isOnlyLink =
childArray.length === 1 &&
React.isValidElement(childArray[0]) &&
childArray[0].type === "a"
if (isOnlyLink) {
return <>{children}>
}
// For other paragraphs, continue with normal processing
const text = childArray
.map((child) => {
if (typeof child === "string") return child
if (React.isValidElement(child) && child.props?.children) {
return child.props.children
}
return ""
})
.join("")
let isMathOnly = false
if (text.trim().startsWith("$$") && text.trim().endsWith("$$")) {
const innerContent = text.trim().slice(2, -2)
if (!innerContent.includes("$$")) {
isMathOnly = true
}
}
if (containsMath(text)) {
return (
{children}
) }, // Handle math in list items li: ({ node, children, ...props }) => { const text = React.Children.toArray(children) .map((child) => { if (typeof child === "string") return child // @ts-expect-error - children props vary if (child?.props?.children) return child.props.children return "" }) .join("") if (containsMath(text)) { return ({children}), "footnote-ref": ({ identifier, label }) => { const handleClick = useCallback( (e: React.MouseEvent) => { e.preventDefault() e.stopPropagation() const target = document.getElementById(`fn-${identifier}`) if (target) { scrollToElementWithOffset(target) } }, [identifier] ) React.useEffect(() => { console.log("Footnote ref mounted:", identifier) }, [identifier]) return ( ) }, "footnote-definition": ({ identifier, label, children }) => { const handleBackClick = useCallback( (e: React.MouseEvent) => { e.preventDefault() e.stopPropagation() const target = document.getElementById(`fnref-${identifier}`) if (target) { scrollToElementWithOffset(target) } }, [identifier] ) React.useEffect(() => { console.log("Footnote definition mounted:", identifier) }, [identifier]) return (