'use client'
import { trpc } from '~/app/_trpc/Client'
import type { RouterOutputs } from '~/server/routers/_app'
import {
AUTOCOMPLETE_CURSOR_MARKER,
linkSuggestionsToAutocomplete,
type AutocompleteTriggerConfig,
type InternalLinkSuggestion,
type MdeAutocompleteSuggestion,
} from '~/app/_components/Form/Fields/InternalLinkTextarea'
import MdxEditorPreview from './MdxEditorPreview'
function internalLinkSuggestions(params: {
posts?: RouterOutputs['blog']['list'],
projects?: RouterOutputs['projectv2']['listWithStack'],
}): InternalLinkSuggestion[] {
const postLinks = params.posts?.map((post) => ({
label: post.title,
href: `/blog/${post.slug}`,
group: 'Blog',
})) ?? []
const projectLinks = params.projects?.map((project) => ({
label: project.title,
href: `/projects#${project.id}`,
group: 'Project',
})) ?? []
return [...postLinks, ...projectLinks]
}
const mdxAutocompleteSuggestions: MdeAutocompleteSuggestion[] = [
{
label: 'Lead',
value: `\n${AUTOCOMPLETE_CURSOR_MARKER}\n`,
detail: 'Intro paragraph with larger muted text.',
group: 'Component',
trigger: '<',
},
{
label: 'Callout note',
value: `\n${AUTOCOMPLETE_CURSOR_MARKER}\n`,
detail: 'Highlighted note block.',
group: 'Component',
trigger: '<',
},
{
label: 'Callout tip',
value: `\n${AUTOCOMPLETE_CURSOR_MARKER}\n`,
detail: 'Highlighted tip block.',
group: 'Component',
trigger: '<',
},
{
label: 'Callout warning',
value: `\n${AUTOCOMPLETE_CURSOR_MARKER}\n`,
detail: 'Highlighted warning block.',
group: 'Component',
trigger: '<',
},
{
label: 'ButtonLink',
value: `\nView projects\n`,
detail: 'Button-styled internal or external link.',
group: 'Component',
trigger: '<',
},
{
label: 'Figure',
value: ``,
detail: 'Image with optional caption.',
group: 'Component',
trigger: '<',
},
{
label: 'PullQuote',
value: `\n${AUTOCOMPLETE_CURSOR_MARKER}\n`,
detail: 'Large emphasized quote.',
group: 'Component',
trigger: '<',
},
{
label: 'TagList',
value: ``,
detail: 'Inline list of tag badges.',
group: 'Component',
trigger: '<',
},
{
label: 'Badge',
value: `${AUTOCOMPLETE_CURSOR_MARKER}`,
detail: 'Small inline label.',
group: 'Component',
trigger: '<',
},
{
label: 'Image',
value: ``,
detail: 'Markdown image',
group: 'Markdown',
trigger: '!',
},
]
const mdxTriggerConfigs: AutocompleteTriggerConfig[] = [
{
trigger: '[[',
label: 'Internal links',
isQueryValid: (query) => !query.includes(']'),
},
{
trigger: '<',
label: 'MDX components',
isQueryValid: (query) => !/[\s>]/.test(query),
},
{
trigger: '!',
label: 'Markdown',
isQueryValid: (query) => !/[\s\)]/.test(query),
},
]
/**
* Shared props for an MDX-aware `MdeFormField`: internal-link + component
* autocomplete, trigger configs, and a live MDX preview. Used by every admin
* form that edits MDX content (blog, project, cv entry).
*/
export function useMdxEditorFieldProps() {
const posts = trpc.blog.list.useQuery(undefined, { refetchInterval: 5000 })
const projects = trpc.projectv2.listWithStack.useQuery()
const autocompleteSuggestions = [
...linkSuggestionsToAutocomplete(internalLinkSuggestions({ posts: posts.data, projects: projects.data })),
...mdxAutocompleteSuggestions,
]
return {
autocompleteSuggestions,
triggerConfigs: mdxTriggerConfigs,
renderPreview: (source: string) => ,
}
}