AI Text Editor Plugin for the Next.js Supabase Saas Starter Kit
This plugin adds an AI WYSIWYG Editor to your Next.js Supabase SaaS application.
This plugin adds an AI Editor component Next.js application. You can import this component anywhere in your application.
It is built with Lexical, a text editor framework for React, by Meta.
This plugin is currently experimental and not yet officially released.
Using the Plugin
Installation
To install the plugin, you can use git subtrees from your original repository:
git subtree add --prefix plugins/text-editor git@github.com:makerkit/next-supabase-saas-kit-plugins.git text-editor --squash
After running this command, you will have the plugin in your repository at plugins/text-editor
. Once pulled, you can apply any customization you need.
Using the CLI
If you're using the CLI, you can run the following command to install the plugin:
npx @makerkit/cli@latest plugins install
Follow the instructions to install the plugin.
Add the plugin as a workspace in your package.json
You can do so by adding the following to your package.json
file:
{ "workspaces": [ "plugins/text-editor" ]}
Add it next to the other workspaces in your package.json
file.
Add the paths alias to the TypeScript configuration
To make sure that the TypeScript compiler can find the plugin, you will need to add the following paths alias to your tsconfig.json
file, in addition to the other paths aliases that you may have.
If not yet present, add the following to your tsconfig.json
file:
{ "compilerOptions": { "paths": { "~/plugins/*": [ "./plugins/*" ] } }}
You only need to add this once, even if you have multiple plugins.
Installing dependencies
To install the dependencies, you can run the following command:
npm i
NPM will install the dependencies in the plugins/text-editor
folder as an NPM workspace.
Add the required API handlers
You will need to add the following API handlers to your application:
api/ai/autocomplete/route.ts
api/ai/edit/route.ts
Edit Route
To add the edit route, create a file at src/app/api/ai/edit/route.ts
with the following content:
import { createAIEditRouteHandler } from '~/plugins/text-editor/lib/route-handler';export const runtime = `edge`;export const POST = createAIEditRouteHandler;
Autocomplete Route
To add the autocomplete route, create a file at src/app/api/ai/autocomplete/route.ts
with the following content:
import { createAIAutocompleteRouteHandler } from '~/plugins/text-editor/lib/route-handler';export const runtime = `edge`;export const POST = createAIAutocompleteRouteHandler;
Style
Import the CSS file at plugins/text-editor/editor.css
into your global CSS file at src/app/styles/globals.css
:
@import "../../plugins/text-editor/editor.css";
Using the plugin
To use the plugin, you can import the component from the plugin folder.
NB: the example below uses rxjs
, which is a dependency not included in the kit. I do recommend using it, but you can use any other library to handle debouncing and other logic that you may need if you want to use autosaving for your application.
'use client';import { useCallback, useEffect, useMemo, useRef } from 'react';import { toast } from 'sonner';import { debounceTime, distinctUntilChanged, Subject, switchMap, tap,} from 'rxjs';import Editor from '~/plugins/text-editor/components/Editor';export default function EditorContainer() { const subject$ = useMemo(() => new Subject(), []); const currentToastId = useRef<string | number>(); const onChange = useCallback( (content: string) => { subject$.next(content); }, [subject$], ); useEffect(() => { const subscription = subject$ .pipe( debounceTime(2000), distinctUntilChanged(), switchMap(() => { if (currentToastId.current) { toast.dismiss(currentToastId.current); } currentToastId.current = toast.loading('Saving...', { id: currentToastId.current, }); return new Promise((resolve) => { setTimeout(resolve, 2000); }); }), tap(() => { toast.success('Saved!', { id: currentToastId.current, }); }), ) .subscribe(() => { currentToastId.current = undefined; }); return () => { subscription.unsubscribe(); }; }, [subject$]); return ( <> <Editor className={'h-[80vh] w-[100vh]'} content={getInitialContent()} onChange={onChange} /> </> );}function getInitialContent() { return `## Introducing Makerkit's AI EditorThis plugin is powered by OpenAI's GPT-3 and Lexical's Editor, and helps you add an AI-powered editor to your SaaS, in just a few lines of code.Install it using the CLI:\`\`\`npx @makerkit/cli plugins install\`\`\`Available for *Pro* and *Teams* customers **for free**. `.trim();}
Now, import the component EditorContainer
anywhere in your application:
import { Toaster } from 'sonner';import dynamic from 'next/dynamic';import PageLoadingIndicator from '~/core/ui/PageLoadingIndicator';import { withI18n } from '~/i18n/with-i18n';import I18nProvider from '~/i18n/I18nProvider';import getLanguageCookie from '~/i18n/get-language-cookie';const EditorContainer = dynamic( () => { return import('~/app/editor/EditorContainer'); }, { ssr: false, loading: () => ( <PageLoadingIndicator fullPage={false}>Loading...</PageLoadingIndicator> ), },);function EditorPage() { return ( <I18nProvider lang={getLanguageCookie()}> <div className={ 'w-screen h-screen flex justify-center items-center bg-gray-50' + ' dark:bg-dark-900 flex flex-col space-y-4' } > <Toaster /> <EditorContainer /> </div> </I18nProvider> );}export default withI18n(EditorPage);