Implement an AI-Enhanced MDX Editor for Direct GitHub Markdown Editing: Part 1

In this article, we will build an AI-enhanced MDX editor that allows direct editing of markdown files on GitHub, streamlining your workflow and eliminating the need for multiple tools.

Let's Build an AI-Enhanced MDX Editor for GitHub!

Hello there! Are you tired of manually editing markdown files in your IDE? Well, fret not! We're about to create a markdown editor that lets you edit directly on GitHub. Sounds cool, right?

Why Do This?

The goal is simple: minimize the hassle and extend our current workflow. Instead of juggling multiple tools, we'll integrate everything into our existing application. No need for separate services or databases—just pure markdown magic.

How It Works

Our editor will let you edit markdown files straight from your GitHub repo and push the changes. It's as straightforward as that!

Here's the core code snippet to get us started:

import {ForwardRefEditor as Editor} from './forwardRefEditor'
import '@mdxeditor/editor/style.css'

const getArticleBySlug = async (slug: string) => {
  'use server'
  const owner = '<user>';
  const repo = '<repo_name>';
  const path = `path_to_your_articles/${slug}.mdx`;
  const ref = 'main'; // branch
  const rawArticleLocation = `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${ref}`;

  const res = await fetch(rawArticleLocation, {
    headers: {
      'Accept': 'application/vnd.github+json',
      'Authorization': `token ${process.env.GITHUB_TOKEN}`,
      'X-Github-Api-Version': "2022-11-28"
    },
  });
  const jsonResponse = await res.json();
  const content = Buffer.from(jsonResponse.content, 'base64').toString();
  return content;
}

interface PageProps {
  params: { slug: string };
  searchParams: { [key: string]: string | string[] | undefined };
}

const EditArticlePage = async (props: PageProps) => {
  const slug = props.params.slug;
  const article = await getArticleBySlug(slug)
  return <Editor markdown={article} />;
};

export default EditArticlePage;

Additional Components

But wait, there's more! We need a couple of additional components to make this work. One of them is our InitializedMDXEditor, heavily inspired by mdxeditor tutorials.

Here's the code for that:

'use client'
import type { ForwardedRef } from 'react'
import {
  headingsPlugin,
  listsPlugin,
  quotePlugin,
  thematicBreakPlugin,
  toolbarPlugin,
  MDXEditor,
  type MDXEditorMethods,
  type MDXEditorProps,
  UndoRedo,
  BoldItalicUnderlineToggles,
  markdownShortcutPlugin,
  CreateLink,
  BlockTypeSelect,
  CodeToggle,
  Separator,
  Button,
  frontmatterPlugin,
  InsertFrontmatter,
  diffSourcePlugin,
  DiffSourceToggleWrapper,
  codeBlockPlugin,
  codeMirrorPlugin
} from '@mdxeditor/editor'

// Only import this to the next file
export default function InitializedMDXEditor({
  editorRef,
  ...props
}: { editorRef: ForwardedRef<MDXEditorMethods> | null } & MDXEditorProps) {
  return (
    <MDXEditor
      plugins={[
        headingsPlugin(),
        listsPlugin(),
        quotePlugin(),
        thematicBreakPlugin(),
        diffSourcePlugin({
          diffMarkdown: props.markdown,
          viewMode: 'rich-text',
          // readOnlyDiff: true
        }),
        frontmatterPlugin(),
        toolbarPlugin({
          toolbarContents: () => (
            <>
              {' '}
              <DiffSourceToggleWrapper >
                <InsertFrontmatter />
                <Separator />
                <UndoRedo />
                <BoldItalicUnderlineToggles />
                <CreateLink />
                <BlockTypeSelect />
                <CodeToggle />
                <Separator />
                <Button>EditorAI</Button>
                <Button>Save</Button>
              </DiffSourceToggleWrapper>
            </>
          ),
        }),
        codeBlockPlugin({ defaultCodeBlockLanguage: 'js' }),
        codeMirrorPlugin({
          codeBlockLanguages: { js: 'JavaScript', ts: 'Typescript', tsx: 'React TSX', json: 'JSON', diff: 'Diff', typescript: 'Typescript' },
        }),
        markdownShortcutPlugin(),
      ]}
      {...props}
      ref={editorRef}
    />
  )
}

The Dynamic Import

And, of course, we need a dynamic import component. Here's how we do it:

'use client'
import { forwardRef } from 'react';
import {
  type MDXEditorMethods, type MDXEditorProps
} from '@mdxeditor/editor'
import dynamic from 'next/dynamic';

const Editor = dynamic(() => import('./InitializedEditor'), { ssr: false })

export const ForwardRefEditor = forwardRef<MDXEditorMethods, MDXEditorProps>((props, ref) => <Editor {...props} editorRef={ref} />);

ForwardRefEditor.displayName = 'ForwardRefEditor';

Wrapping Up

So there you have it! We've laid the groundwork for our AI-enhanced MDX editor. Stay tuned for part 2, where we'll dive into integrating AI to improve content quality. For those who can't wait, check out our article on Automate Content Quality with AI: Implement a Pre-Commit Hook in TypeScript with GPT-4.

Happy coding!

← Back to posts

We use cookies to improve your experience and analyze our traffic. By using our site, you consent to our use of cookies. You can manage your preferences below: