'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: `![Image](${AUTOCOMPLETE_CURSOR_MARKER})`, 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) => , } }