diff --git a/packages/editor/README.md b/packages/editor/README.md deleted file mode 100644 index aa673e39f..000000000 --- a/packages/editor/README.md +++ /dev/null @@ -1,139 +0,0 @@ - - This is the core engine that supports Rich Text Editing at Plane -

Plane's Editor

-
- -

- An open-source Notion-style WYSIWYG editor with AI-powered autocompletions. -

- -

- Hacker News - - License - - Novel.sh's GitHub repo -

- -

- Introduction · - Installation · - Deploy Your Own · - Setting Up Locally · - Tech Stack · - Contributing · - License -

-
- -## Introduction - -[Novel](https://novel.sh/) is a Notion-style WYSIWYG editor with AI-powered autocompletions. - -https://github.com/steven-tey/novel/assets/28986134/2099877f-4f2b-4b1c-8782-5d803d63be5c - -
- -## Installation - -To use Novel in a project, you can run the following command to install the `novel` [NPM package](https://www.npmjs.com/package/novel): - -``` -npm i novel -``` - -Then, you can use it in your code like this: - -```jsx -import { Editor } from "novel"; - -export default function App() { - return ; -} -``` - -The `Editor` is a React component that takes in the following props: - -| Prop | Type | Description | Default | -| ------------------- | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------- | -| `completionApi` | `string` | The API route to use for the OpenAI completion API. | `/api/generate` | -| `className` | `string` | Editor container classname. | `"relative min-h-[500px] w-full max-w-screen-lg border-stone-200 bg-white sm:mb-[calc(20vh)] sm:rounded-lg sm:border sm:shadow-lg"` | -| `defaultValue` | `JSONContent` or `string` | The default value to use for the editor. | [`defaultEditorContent`](https://github.com/steven-tey/novel/blob/main/packages/core/src/ui/editor/default-content.tsx) | -| `extensions` | `Extension[]` | A list of extensions to use for the editor, in addition to the [default Novel extensions](https://github.com/steven-tey/novel/blob/main/packages/core/src/ui/editor/extensions/index.tsx). | `[]` | -| `editorProps` | `EditorProps` | Props to pass to the underlying Tiptap editor, in addition to the [default Novel editor props](https://github.com/steven-tey/novel/blob/main/packages/core/src/ui/editor/props.ts). | `{}` | -| `onUpdate` | `(editor?: Editor) => void` | A callback function that is called whenever the editor is updated. | `() => {}` | -| `onDebouncedUpdate` | `(editor?: Editor) => void` | A callback function that is called whenever the editor is updated, but only after the defined debounce duration. | `() => {}` | -| `debounceDuration` | `number` | The duration (in milliseconds) to debounce the `onDebouncedUpdate` callback. | `750` | -| `storageKey` | `string` | The key to use for storing the editor's value in local storage. | `novel__content` | - -> **Note**: Make sure to define an API endpoint that matches the `completionApi` prop (default is `/api/generate`). This is needed for the AI autocompletions to work. Here's an example: https://github.com/steven-tey/novel/blob/main/apps/web/app/api/generate/route.ts - -Here's an example application: https://github.com/steven-tey/novella - -## Deploy Your Own - -You can deploy your own version of Novel to Vercel with one click: - -[![Deploy with Vercel](https://vercel.com/button)](https://stey.me/novel-deploy) - -## Setting Up Locally - -To set up Novel locally, you'll need to clone the repository and set up the following environment variables: - -- `OPENAI_API_KEY` – your OpenAI API key (you can get one [here](https://platform.openai.com/account/api-keys)) -- `BLOB_READ_WRITE_TOKEN` – your Vercel Blob read/write token (currently [still in beta](https://vercel.com/docs/storage/vercel-blob/quickstart#quickstart), but feel free to [sign up on this form](https://vercel.fyi/blob-beta) for access) - -If you've deployed this to Vercel, you can also use [`vc env pull`](https://vercel.com/docs/cli/env#exporting-development-environment-variables) to pull the environment variables from your Vercel project. - -To run the app locally, you can run the following commands: - -``` -pnpm i -pnpm build -pnpm dev -``` - -## Cross-framework support - -While Novel is built for React, we also have a few community-maintained packages for non-React frameworks: - -- Svelte: https://novel.sh/svelte -- Vue: https://novel.sh/vue - -## VSCode Extension - -Thanks to @bennykok, Novel also has a VSCode Extension: https://novel.sh/vscode - -https://github.com/steven-tey/novel/assets/28986134/58ebf7e3-cdb3-43df-878b-119e304f7373 - -## Tech Stack - -Novel is built on the following stack: - -- [Next.js](https://nextjs.org/) – framework -- [Tiptap](https://tiptap.dev/) – text editor -- [OpenAI](https://openai.com/) - AI completions -- [Vercel AI SDK](https://sdk.vercel.ai/docs) – AI library -- [Vercel](https://vercel.com) – deployments -- [TailwindCSS](https://tailwindcss.com/) – styles -- [Cal Sans](https://github.com/calcom/font) – font - -## Contributing - -Here's how you can contribute: - -- [Open an issue](https://github.com/steven-tey/novel/issues) if you believe you've encountered a bug. -- Make a [pull request](https://github.com/steven-tey/novel/pull) to add new features/make quality-of-life improvements/fix bugs. - - - - - -## Repo Activity - -![Novel.sh repo activity – generated by Axiom](https://repobeats.axiom.co/api/embed/2ebdaa143b0ad6e7c2ee23151da7b37f67da0b36.svg) - -## License - -Licensed under the [Apache-2.0 license](https://github.com/steven-tey/novel/blob/main/LICENSE.md). - diff --git a/packages/editor/package.json b/packages/editor/package.json index e9e7cd079..b27a020ac 100644 --- a/packages/editor/package.json +++ b/packages/editor/package.json @@ -1,5 +1,5 @@ { - "name": "plane-editor", + "name": "@plane/editor", "version": "0.0.1", "description": "Rich Text Editor that powers Plane", "main": "./dist/index.js", @@ -27,15 +27,11 @@ "dependencies": { "@blueprintjs/popover2": "^2.0.10", "@tiptap/core": "^2.1.7", - "@tiptap/extension-blockquote": "^2.1.10", - "@tiptap/extension-bullet-list": "^2.1.10", "@tiptap/extension-code-block-lowlight": "^2.0.4", "@tiptap/extension-highlight": "^2.1.7", "@tiptap/extension-horizontal-rule": "^2.1.7", "@tiptap/extension-image": "^2.1.7", "@tiptap/extension-link": "^2.1.7", - "@tiptap/extension-list-item": "^2.1.10", - "@tiptap/extension-ordered-list": "^2.1.10", "@tiptap/extension-placeholder": "2.0.3", "@tiptap/extension-table": "^2.1.6", "@tiptap/extension-table-cell": "^2.1.6", @@ -63,7 +59,6 @@ "react": "18.2.0", "react-dom": "18.2.0", "react-markdown": "^8.0.7", - "sonner": "^0.7.0", "tailwind-merge": "^1.14.0", "tippy.js": "^6.3.7", "tiptap-markdown": "^0.8.2", diff --git a/packages/editor/src/types/delete-file.ts b/packages/editor/src/types/delete-image.ts similarity index 100% rename from packages/editor/src/types/delete-file.ts rename to packages/editor/src/types/delete-image.ts diff --git a/packages/editor/src/types/upload-file.ts b/packages/editor/src/types/upload-image.ts similarity index 71% rename from packages/editor/src/types/upload-file.ts rename to packages/editor/src/types/upload-image.ts index bb2003b0c..e9d35ad48 100644 --- a/packages/editor/src/types/upload-file.ts +++ b/packages/editor/src/types/upload-image.ts @@ -1 +1 @@ -export type UploadImage = (workspaceSlug: string, formData: FormData) => Promise; +export type UploadImage = (workspaceSlug: string, formData: FormData) => Promise; diff --git a/packages/editor/src/ui/editor/extensions/image/updated-image.tsx b/packages/editor/src/ui/editor/extensions/image/updated-image.tsx index 0162fc15a..2ba977f57 100644 --- a/packages/editor/src/ui/editor/extensions/image/updated-image.tsx +++ b/packages/editor/src/ui/editor/extensions/image/updated-image.tsx @@ -1,9 +1,9 @@ import Image from "@tiptap/extension-image"; import TrackImageDeletionPlugin from "@/ui/editor/plugins/delete-image"; import UploadImagesPlugin from "@/ui/editor/plugins/upload-image"; -import { DeleteFileFunction } from "@/types/delete-file"; +import { DeleteImage } from "@/types/delete-image"; -const UpdatedImage = (deleteImage: DeleteFileFunction) => Image.extend({ +const UpdatedImage = (deleteImage: DeleteImage) => Image.extend({ addProseMirrorPlugins() { return [UploadImagesPlugin(), TrackImageDeletionPlugin(deleteImage)]; }, diff --git a/packages/editor/src/ui/editor/extensions/index.tsx b/packages/editor/src/ui/editor/extensions/index.tsx index 303a46c06..5fd7f9ad1 100644 --- a/packages/editor/src/ui/editor/extensions/index.tsx +++ b/packages/editor/src/ui/editor/extensions/index.tsx @@ -12,9 +12,6 @@ import Highlight from "@tiptap/extension-highlight"; import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight"; import { InputRule } from "@tiptap/core"; import Gapcursor from "@tiptap/extension-gapcursor"; -import OrderedList from "@tiptap/extension-ordered-list"; -import ListItem from "@tiptap/extension-list-item"; -import BulletList from "@tiptap/extension-bullet-list"; import { Table } from "@/ui/editor/extensions/table/table"; import { TableHeader } from "@/ui/editor/extensions/table/table-header"; import { TableRow } from "@tiptap/extension-table-row"; @@ -23,8 +20,8 @@ import { CustomTableCell } from "@/ui/editor/extensions/table/table-cell"; import UpdatedImage from "@/ui/editor/extensions/image/updated-image"; import SlashCommand from "@/ui/editor/extensions/slash-command"; -import { DeleteFileFunction } from "@/types/delete-file"; -import { UploadFileFunction } from "@/types/upload-file"; +import { DeleteImage } from "@/types/delete-image"; +import { UploadImage } from "@/types/upload-image"; import isValidHttpUrl from "@/ui/editor/menus/bubble-menu/utils" @@ -36,8 +33,8 @@ lowlight.registerLanguage("ts", ts); export const TiptapExtensions = ( workspaceSlug: string, - uploadFile: UploadFileFunction, - deleteFile: DeleteFileFunction, + uploadFile: UploadImage, + deleteFile: DeleteImage, setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void ) => [ StarterKit.configure({ diff --git a/packages/editor/src/ui/editor/extensions/slash-command.tsx b/packages/editor/src/ui/editor/extensions/slash-command.tsx index 76cbda8b9..4fba7af5f 100644 --- a/packages/editor/src/ui/editor/extensions/slash-command.tsx +++ b/packages/editor/src/ui/editor/extensions/slash-command.tsx @@ -19,7 +19,7 @@ import { } from "lucide-react"; import { startImageUpload } from "@/ui/editor/plugins/upload-image"; import { cn } from "@/lib/utils"; -import { UploadImage } from "@/types/upload-file"; +import { UploadImage } from "@/types/upload-image"; interface CommandItemProps { title: string; diff --git a/packages/editor/src/ui/editor/index.tsx b/packages/editor/src/ui/editor/index.tsx index d534d57eb..e296d400a 100644 --- a/packages/editor/src/ui/editor/index.tsx +++ b/packages/editor/src/ui/editor/index.tsx @@ -8,8 +8,8 @@ import { TiptapExtensions } from '@/ui/editor/extensions'; import { EditorBubbleMenu } from '@/ui/editor/menus/bubble-menu'; import { ImageResizer } from '@/ui/editor/extensions/image/image-resize'; import { TiptapEditorProps } from '@/ui/editor/props'; -import { UploadImage } from '@/types/upload-file'; -import { DeleteImage } from '@/types/delete-file'; +import { UploadImage } from '@/types/upload-image'; +import { DeleteImage } from '@/types/delete-image'; export interface ITipTapRichTextEditor { value: string; diff --git a/packages/editor/src/ui/editor/plugins/delete-image.tsx b/packages/editor/src/ui/editor/plugins/delete-image.tsx index a240cb259..9204481a8 100644 --- a/packages/editor/src/ui/editor/plugins/delete-image.tsx +++ b/packages/editor/src/ui/editor/plugins/delete-image.tsx @@ -1,6 +1,6 @@ import { EditorState, Plugin, PluginKey, Transaction } from "@tiptap/pm/state"; import { Node as ProseMirrorNode } from "@tiptap/pm/model"; -import { DeleteFileFunction } from "@/types/delete-file"; +import { DeleteImage } from "@/types/delete-image"; const deleteKey = new PluginKey("delete-image"); const IMAGE_NODE_TYPE = "image"; @@ -12,7 +12,7 @@ interface ImageNode extends ProseMirrorNode { }; } -const TrackImageDeletionPlugin = (deleteImage: DeleteFileFunction): Plugin => +const TrackImageDeletionPlugin = (deleteImage: DeleteImage): Plugin => new Plugin({ key: deleteKey, appendTransaction: (transactions: readonly Transaction[], oldState: EditorState, newState: EditorState) => { @@ -55,7 +55,7 @@ const TrackImageDeletionPlugin = (deleteImage: DeleteFileFunction): Plugin => export default TrackImageDeletionPlugin; -async function onNodeDeleted(src: string, deleteImage: DeleteFileFunction): Promise { +async function onNodeDeleted(src: string, deleteImage: DeleteImage): Promise { try { const assetUrlWithWorkspaceId = new URL(src).pathname.substring(1); const resStatus = await deleteImage(assetUrlWithWorkspaceId); diff --git a/packages/editor/src/ui/editor/plugins/upload-image.tsx b/packages/editor/src/ui/editor/plugins/upload-image.tsx index 99a963fd2..4c3bbf9a8 100644 --- a/packages/editor/src/ui/editor/plugins/upload-image.tsx +++ b/packages/editor/src/ui/editor/plugins/upload-image.tsx @@ -1,4 +1,4 @@ -import { UploadFileFunction, UploadImage } from "@/types/upload-file"; +import { UploadImage } from "@/types/upload-image"; import { EditorState, Plugin, PluginKey } from "@tiptap/pm/state"; import { Decoration, DecorationSet, EditorView } from "@tiptap/pm/view"; @@ -102,7 +102,7 @@ export async function startImageUpload( } const UploadImageHandler = (file: File, workspaceSlug: string, - uploadFile: UploadFileFunction + uploadFile: UploadImage ): Promise => { if (!workspaceSlug) { return Promise.reject("Workspace slug is missing"); diff --git a/packages/editor/src/ui/editor/props.tsx b/packages/editor/src/ui/editor/props.tsx index 3de3294bf..ff5b2f11b 100644 --- a/packages/editor/src/ui/editor/props.tsx +++ b/packages/editor/src/ui/editor/props.tsx @@ -1,7 +1,7 @@ import { EditorProps } from "@tiptap/pm/view"; import { findTableAncestor } from "@/ui/editor/menus/table-menu"; import { startImageUpload } from "@/ui/editor/plugins/upload-image"; -import { UploadImage } from "@/types/upload-file"; +import { UploadImage } from "@/types/upload-image"; export function TiptapEditorProps( workspaceSlug: string, diff --git a/web/package.json b/web/package.json index eddd4a06d..deae3980f 100644 --- a/web/package.json +++ b/web/package.json @@ -80,7 +80,7 @@ "tlds": "^1.238.0", "use-debounce": "^9.0.4", "uuid": "^9.0.0", - "plane-editor": "*" + "@plane/editor": "*" }, "devDependencies": { "@types/js-cookie": "^3.0.2", @@ -101,8 +101,5 @@ "tsconfig": "*", "tailwind-config": "*", "typescript": "4.7.4" - }, - "resolutions": { - "prosemirror-model": "1.18.1" } } diff --git a/yarn.lock b/yarn.lock index 0449ae903..c958eb62b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7190,7 +7190,14 @@ prosemirror-menu@^1.2.1: prosemirror-history "^1.0.0" prosemirror-state "^1.0.0" -prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.19.0, prosemirror-model@^1.8.1: +prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.18.1, prosemirror-model@^1.8.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.18.1.tgz#1d5d6b6de7b983ee67a479dc607165fdef3935bd" + integrity sha512-IxSVBKAEMjD7s3n8cgtwMlxAXZrC7Mlag7zYsAKDndAqnDScvSmp/UdnRTV/B33lTCVU3CCm7dyAn/rVVD0mcw== + dependencies: + orderedmap "^2.0.0" + +prosemirror-model@^1.19.0: version "1.19.3" resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.19.3.tgz#f0d55285487fefd962d0ac695f716f4ec6705006" integrity sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ== @@ -7951,11 +7958,6 @@ sonner@^0.6.2: resolved "https://registry.yarnpkg.com/sonner/-/sonner-0.6.2.tgz#d87420e80d8b25b6d2bd6aabcc28465f03962bdc" integrity sha512-bh4FWhYoNN481ZIW94W4e0kSLBTMGislYg2YXvDS1px1AJJz4erQe9jHV8s5pS1VMVDgfh3CslNSFLaU6Ldrnw== -sonner@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/sonner/-/sonner-0.7.1.tgz#46441caa4e94a0491fe8e7ce56887d097f90c4df" - integrity sha512-awbVcBwV0xV5TN7kJEZv6Sx7Fi9JBL26vTn0FnBcf6YsyWKnyvz16I/jE6omCtBjd0/YXsPHJ//VuA5fvD2vEg== - source-list-map@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"