59 lines
1.6 KiB
TypeScript
59 lines
1.6 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { MDXRemote } from 'next-mdx-remote'
|
|
import { serialize } from 'next-mdx-remote/serialize'
|
|
import type { MDXRemoteSerializeResult } from 'next-mdx-remote'
|
|
import { mdxComponents } from '~/components/mdx-components'
|
|
|
|
export default function MdxEditorPreview(params: { source: string }) {
|
|
const [compiled, setCompiled] = useState<MDXRemoteSerializeResult | null>(null)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
let cancelled = false
|
|
const timeout = window.setTimeout(() => {
|
|
void serialize(params.source, {
|
|
parseFrontmatter: false,
|
|
mdxOptions: {
|
|
remarkPlugins: [],
|
|
rehypePlugins: [],
|
|
},
|
|
})
|
|
.then((result) => {
|
|
if (cancelled) return
|
|
setCompiled(result)
|
|
setError(null)
|
|
})
|
|
.catch((nextError: unknown) => {
|
|
if (cancelled) return
|
|
setCompiled(null)
|
|
setError(nextError instanceof Error ? nextError.message : 'Failed to compile MDX preview')
|
|
})
|
|
}, 200)
|
|
|
|
return () => {
|
|
cancelled = true
|
|
window.clearTimeout(timeout)
|
|
}
|
|
}, [params.source])
|
|
|
|
if (error) {
|
|
return (
|
|
<div className='rounded-md border border-destructive/40 bg-destructive/10 p-4 text-sm text-destructive'>
|
|
{error}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!compiled) {
|
|
return <div className='text-muted-foreground p-4 text-sm'>Rendering preview...</div>
|
|
}
|
|
|
|
return (
|
|
<article className='prose dark:prose-invert max-w-none'>
|
|
<MDXRemote {...compiled} components={mdxComponents} />
|
|
</article>
|
|
)
|
|
}
|