import React from 'react'; import ReactMarkdown, { type Components, type ExtraProps, } from 'react-markdown'; import remarkGfm from 'remark-gfm'; import { markdownHeadings, markdownTables } from '@/lib/markdown'; import { Button } from './ui/button'; type TableProps = React.ComponentProps<'table'> & ExtraProps; // The shared table frame, with the paragraph's bottom margin so the next // block does not butt against it. const SharedTable = markdownTables.table as React.ComponentType; const TableFrame = (props: TableProps) => (
); const components: Components = { ...markdownHeadings, ...markdownTables, table: TableFrame, p: ({ children }) =>

{children}

, ul: ({ children }) => ( ), ol: ({ children }) => (
    {children}
), a: ({ children, href }) => ( ), // Fenced and indented code keep their lines and scroll sideways in a // bordered box; the inline pill below applies to inline code only. pre: ({ children }) => (
      {children}
    
), code: ({ children }) => ( {children} ), mark: ({ children }) => ( {children} ), }; type MdNode = { type: string; value?: string; children?: MdNode[]; data?: { hName?: string }; }; /** * A remark plugin that wraps every case-insensitive, literal match of `term` * in text (never in code) in a ``. */ function remarkHighlight(term: string) { const needle = term.toLowerCase(); const split = (value: string): MdNode[] => { const out: MdNode[] = []; const lower = value.toLowerCase(); let from = 0; let at = lower.indexOf(needle); while (at !== -1) { if (at > from) out.push({ type: 'text', value: value.slice(from, at) }); const hit = value.slice(at, at + needle.length); out.push({ type: 'highlight', data: { hName: 'mark' }, children: [{ type: 'text', value: hit }], }); from = at + needle.length; at = lower.indexOf(needle, from); } if (from < value.length) out.push({ type: 'text', value: value.slice(from) }); return out; }; const walk = (node: MdNode) => { if (!node.children) return; node.children = node.children.flatMap((child) => { if (child.type === 'text' && child.value) return split(child.value); walk(child); return [child]; }); }; return () => (tree: MdNode) => walk(tree); } /** * Markdown as a source view renders it: a wiki page, an open chunk, the edit * drawer's preview. The chat answer's headings and tables, lists outside the * text, links as `link inline`. `highlight` marks an entity's name (the graph's * chunk drawer). */ export default function SourceMarkdown({ content, highlight, }: { content: string; highlight?: string; }) { const term = highlight?.trim(); return (
{content}
); }