"use client" import React, { useCallback } from "react" import ReactMarkdown, { Components } from "react-markdown" import remarkGfm from "remark-gfm" import katex from "katex" import "katex/dist/katex.min.css" import rehypeRaw from "rehype-raw" import { TableRowCard } from "../cards/table-row-card" import { Accordion } from "./accordion" import Prism from "prismjs" 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" 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", }) }) } interface CustomComponents extends Components { "table-row-card": React.ComponentType<{ node: any children: string }> accordion: React.ComponentType<{ node: any children: string }> "footnote-ref": React.ComponentType<{ identifier: string label: string }> "footnote-definition": React.ComponentType<{ identifier: string label: string children: React.ReactNode }> footnotes: React.ComponentType<{ children: React.ReactNode }> } const generateSectionId = (text: string) => { return text .toLowerCase() .replace(/[']/g, "") .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, "") } export const createMarkdownElement = ( tag: keyof JSX.IntrinsicElements, props: any ) => React.createElement(tag, { ...props, ref: (node: HTMLElement | null) => { if (node && node.textContent) { node.setAttribute( "data-section-id", generateSectionId(node.textContent) ) } }, }) const Table = (props: any) => { return (
{children}
)
}
const REACT_MARKDOWN_CONFIG = (darkMode: boolean): CustomComponents => ({
a: ({ href, children }) => {
if (href?.startsWith("#")) {
return (
{
e.preventDefault()
const targetId = href.slice(1)
const target = document.getElementById(targetId)
if (target) {
scrollToElementWithOffset(target)
}
}}
className="text-anakiwa-500 hover:text-orange duration-200"
>
{children}
)
}
return (
{children}
)
},
h1: ({ ...props }) =>
createMarkdownElement("h1", {
className: "text-primary text-4xl md:text-5xl font-bold",
...props,
}),
h2: ({ ...props }) =>
createMarkdownElement("h2", {
className: "text-primary text-4xl",
...props,
}),
h3: ({ ...props }) =>
createMarkdownElement("h3", {
className: "text-primary text-3xl",
...props,
}),
h4: ({ ...props }) =>
createMarkdownElement("h4", {
className: "text-primary text-xl",
...props,
}),
h5: ({ ...props }) =>
createMarkdownElement("h5", {
className: "text-primary text-lg font-bold",
...props,
}),
h6: ({ ...props }) =>
createMarkdownElement("h6", {
className: "text-primary text-md font-bold",
...props,
}),
code: ({ node, inline, className, children, ...props }) => {
const match = /language-(\w+)/.exec(className || "")
return !inline ? (
{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 (