import { useState } from 'react' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { Textarea } from '@/components/ui/textarea' import { cn } from '@/lib/utils' import './markdown.css' interface MarkdownEditorProps { value: string onChange?: (value: string) => void rows?: number /** Monospace editing font — use for raw .md files (AGENTS.md / SKILL.md). */ mono?: boolean /** Split a leading YAML frontmatter block and show it above the rendered body in the preview. */ frontmatter?: boolean placeholder?: string id?: string /** Which tab to open on first render. Defaults to Preview when read-only (no onChange), else Edit. */ defaultTab?: Tab /** Extra classes for the textarea (edit tab). */ className?: string } type Tab = 'edit' | 'preview' /** Strips a leading `---\n…\n---` frontmatter block so the preview can render the body as prose. */ function splitFrontmatter(src: string): { fm: string | null; body: string } { const match = src.match(/^---\n([\s\S]*?)\n---\n?/) return match ? { fm: match[1], body: src.slice(match[0].length) } : { fm: null, body: src } } /** * A markdown field with Edit | Preview tabs. Edit is the familiar textarea; Preview renders * GitHub-flavored markdown (react-markdown + remark-gfm, no raw HTML — retrieved/authored content is * data, not markup). Used wherever the app authors markdown: AGENTS.md, SKILL.md, agent persona, and * the review artifact. */ export function MarkdownEditor({ value, onChange, rows = 8, mono = false, frontmatter = false, placeholder, id, defaultTab, className, }: MarkdownEditorProps) { const [tab, setTab] = useState(defaultTab ?? (onChange ? 'edit' : 'preview')) const { fm, body } = frontmatter ? splitFrontmatter(value) : { fm: null, body: value } const hasContent = (frontmatter ? body : value).trim().length > 0 return (
{(['edit', 'preview'] as Tab[]).map((t) => ( ))}
{tab === 'edit' ? (